diff --git a/Assets/Scripts/Components/Controller/FreeCamController.cs b/Assets/Scripts/Components/Controller/FreeCamController.cs index 37e1a69..cdcef79 100644 --- a/Assets/Scripts/Components/Controller/FreeCamController.cs +++ b/Assets/Scripts/Components/Controller/FreeCamController.cs @@ -6,7 +6,7 @@ using UnityEngine.InputSystem; /// /// Free-flying spectator camera on its own dedicated : WASD flight relative -/// to the view, Q/E (or Space/Left Ctrl) for down/up, mouse look. A +/// to the view, Space/Left Ctrl for up/down, mouse look. A /// capsule makes it slide along world geometry instead of clipping. Driven by /// via ; while inactive this component does nothing. /// @@ -170,14 +170,15 @@ public class FreeCamController : MonoBehaviour if (kb.sKey.isPressed) dir -= transform.forward; if (kb.dKey.isPressed) dir += transform.right; if (kb.aKey.isPressed) dir -= transform.right; - if (kb.eKey.isPressed || kb.spaceKey.isPressed) dir += Vector3.up; - if (kb.qKey.isPressed || kb.leftCtrlKey.isPressed) dir -= Vector3.up; + // Q/E intentionally unbound: E is the taunt key and must work in free cam. + if (kb.spaceKey.isPressed) dir += Vector3.up; + if (kb.leftCtrlKey.isPressed) dir -= Vector3.up; } #else dir += transform.forward * Input.GetAxisRaw("Vertical"); dir += transform.right * Input.GetAxisRaw("Horizontal"); - if (Input.GetKey(KeyCode.E) || Input.GetKey(KeyCode.Space)) dir += Vector3.up; - if (Input.GetKey(KeyCode.Q) || Input.GetKey(KeyCode.LeftControl)) dir -= Vector3.up; + if (Input.GetKey(KeyCode.Space)) dir += Vector3.up; + if (Input.GetKey(KeyCode.LeftControl)) dir -= Vector3.up; #endif return dir; } diff --git a/Assets/Scripts/Components/UI/OverviewMenu.cs b/Assets/Scripts/Components/UI/OverviewMenu.cs index 734313a..782b299 100644 --- a/Assets/Scripts/Components/UI/OverviewMenu.cs +++ b/Assets/Scripts/Components/UI/OverviewMenu.cs @@ -6,7 +6,8 @@ using UnityEngine.InputSystem; #endif /// -/// The Esc overview menu (the "Overview UI" canvas in Main). Owns ALL Escape handling: Esc toggles +/// The Esc overview menu (the "Overview UI" canvas in Main). Owns ALL Escape handling (Tab acts as an +/// alternative to Esc): Esc toggles /// this panel as a modal pause overlay (gameplay suspended via ); /// while the session browser or match results overlay is open instead, Esc closes that, and while paint /// mode is active Esc exits to the camera mode (Player Control, or First Person for the hunter). Buttons @@ -41,6 +42,9 @@ public class OverviewMenu : MonoBehaviour [SerializeField] private Button paintButton; [SerializeField] private Button firstPersonButton; [SerializeField] private Button freeCamButton; + [Tooltip("Triggers the hider's manual radar ping (same as the E key) and closes the menu. Only shown " + + "for a hider during Seek.")] + [SerializeField] private Button tauntButton; private bool _open; @@ -60,6 +64,7 @@ public class OverviewMenu : MonoBehaviour if (paintButton) paintButton.onClick.AddListener(OnPaint); if (firstPersonButton) firstPersonButton.onClick.AddListener(OnFirstPerson); if (freeCamButton) freeCamButton.onClick.AddListener(OnFreeCam); + if (tauntButton) tauntButton.onClick.AddListener(OnTaunt); } private void OnDisable() @@ -70,6 +75,7 @@ public class OverviewMenu : MonoBehaviour if (paintButton) paintButton.onClick.RemoveListener(OnPaint); if (firstPersonButton) firstPersonButton.onClick.RemoveListener(OnFirstPerson); if (freeCamButton) freeCamButton.onClick.RemoveListener(OnFreeCam); + if (tauntButton) tauntButton.onClick.RemoveListener(OnTaunt); } private void Update() @@ -147,6 +153,12 @@ public class OverviewMenu : MonoBehaviour gameMode.SetPaused(false); } + private void OnTaunt() + { + Close(resume: true); + if (MatchManager.Instance) MatchManager.Instance.RequestManualPing(); + } + private void OnPlayerControl() => SwitchMode(); private void OnPaint() => SwitchMode(); private void OnFirstPerson() => SwitchMode(); @@ -174,6 +186,12 @@ public class OverviewMenu : MonoBehaviour if (matchResultButton) matchResultButton.gameObject.SetActive(matchResultScreen && matchResultScreen.HasResults); + // Taunt/ping only does something for a hider during Seek (the server re-gates found hiders) - + // hide it otherwise, same pattern as the role-scoped mode buttons. + if (tauntButton) + tauntButton.gameObject.SetActive(gameMode.LocalPlayerIsHider && MatchManager.Instance && + MatchManager.Instance.LocalSubPhase == MatchManager.MatchSubPhase.Seek); + SetModeButton(playerControlButton, modes, active); SetModeButton(paintButton, modes, active); SetModeButton(firstPersonButton, modes, active); @@ -191,13 +209,14 @@ public class OverviewMenu : MonoBehaviour button.interactable = !(active is T); } - // NOT TypingGuard-gated (GameplayKeyPressed): Esc must work even while a text field has focus. + // Esc is NOT TypingGuard-gated: it must work even while a text field has focus. + // Tab (alternative binding) IS gated so it stays usable for typing/field navigation. private static bool EscapePressed() { #if ENABLE_INPUT_SYSTEM - return InputUtil.KeyPressedThisFrame(Key.Escape); + return InputUtil.KeyPressedThisFrame(Key.Escape) || InputUtil.GameplayKeyPressed(Key.Tab); #else - return InputUtil.KeyPressedThisFrame(KeyCode.Escape); + return InputUtil.KeyPressedThisFrame(KeyCode.Escape) || InputUtil.GameplayKeyPressed(KeyCode.Tab); #endif } } diff --git a/Assets/Scripts/GameMode/GameModeManager.cs b/Assets/Scripts/GameMode/GameModeManager.cs index 56bca5b..83a2364 100644 --- a/Assets/Scripts/GameMode/GameModeManager.cs +++ b/Assets/Scripts/GameMode/GameModeManager.cs @@ -98,6 +98,9 @@ public class GameModeManager : MonoBehaviour /// True while gameplay is suspended for a modal overlay (see ). public bool IsPaused => _paused; + /// True while the bound local avatar is a hider (drives the overview menu's taunt button). + public bool LocalPlayerIsHider => _localRole == PlayerRole.Hider; + /// The active mode, or - while paused - the mode that will resume. Null in the no-player state. public GameMode EffectiveMode => ActiveMode ?? ((_resumeIndex >= 0 && _resumeIndex < _modes.Count) ? _modes[_resumeIndex] : null); @@ -413,9 +416,9 @@ public class GameModeManager : MonoBehaviour } // A hider manually pings itself onto every hunter's radar (same blip as the periodic sweep), on demand. - // Excluded in Free Cam whose fly controls already use this key; the phase/role gate lives in MatchManager - // (RequestManualPing no-ops outside Seek / for a found hider), so a stray press is harmless. - if (_localRole == PlayerRole.Hider && !(ActiveMode is FreeCamMode) && + // Works in every mode incl. Free Cam (its fly controls no longer use Q/E); the phase/role gate lives in + // MatchManager (RequestManualPing no-ops outside Seek / for a found hider), so a stray press is harmless. + if (_localRole == PlayerRole.Hider && InputUtil.GameplayKeyPressed(pingKey) && MatchManager.Instance) MatchManager.Instance.RequestManualPing(); diff --git a/Assets/Textures/Icons/Icon_firstperson_paint.png b/Assets/Textures/Icons/Icon_firstperson_paint.png new file mode 100644 index 0000000..53e3ad4 Binary files /dev/null and b/Assets/Textures/Icons/Icon_firstperson_paint.png differ diff --git a/Assets/Textures/Icons/Icon_firstperson_paint.png.meta b/Assets/Textures/Icons/Icon_firstperson_paint.png.meta new file mode 100644 index 0000000..91e7b7e --- /dev/null +++ b/Assets/Textures/Icons/Icon_firstperson_paint.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: e1ee44a22edb1ce47930b5ad143075a4 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 0 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Textures/Icons/Icon_taunt.png b/Assets/Textures/Icons/Icon_taunt.png new file mode 100644 index 0000000..cc2a5b0 Binary files /dev/null and b/Assets/Textures/Icons/Icon_taunt.png differ diff --git a/Assets/Textures/Icons/Icon_taunt.png.meta b/Assets/Textures/Icons/Icon_taunt.png.meta new file mode 100644 index 0000000..fc794e1 --- /dev/null +++ b/Assets/Textures/Icons/Icon_taunt.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: f7a95cb24662ee042ae5bba5593ca55a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 0 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: