diff --git a/Assets/_Project/Prefabs/Player/Player.prefab b/Assets/_Project/Prefabs/Player/Player.prefab index 6e06dfd..08614f3 100644 --- a/Assets/_Project/Prefabs/Player/Player.prefab +++ b/Assets/_Project/Prefabs/Player/Player.prefab @@ -18,7 +18,6 @@ GameObject: - component: {fileID: 2806524246861401801} - component: {fileID: 5683786710272852339} - component: {fileID: 6856236869205671849} - - component: {fileID: -4438158474568445526} m_Layer: 0 m_Name: Player m_TagString: Untagged @@ -174,16 +173,3 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout ShowTopMostFoldoutHeaderGroup: 1 ---- !u!114 &-4438158474568445526 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3493329038866903420} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 65825b3189ae873948659b37b219f5e6, type: 3} - m_Name: - m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DebugCommandRelay - ShowTopMostFoldoutHeaderGroup: 1 diff --git a/Assets/_Project/Scenes/Levels/9Player.unity b/Assets/_Project/Scenes/Levels/9Player.unity index 8c21309..0fcac59 100644 --- a/Assets/_Project/Scenes/Levels/9Player.unity +++ b/Assets/_Project/Scenes/Levels/9Player.unity @@ -13757,75 +13757,6 @@ Mesh: - serializedVersion: 1 m_IndexStart: 0 m_IndexCount: 0 ---- !u!1 &954434158 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 954434161} - - component: {fileID: 954434160} - - component: {fileID: 954434159} - m_Layer: 0 - m_Name: DebugConsole - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &954434159 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 954434158} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 7c6a808e8291552d0b64e36a210879c4, type: 3} - m_Name: - m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DebugConsole - toggleKey: 4 ---- !u!114 &954434160 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 954434158} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} - m_Name: - m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument - m_PanelSettings: {fileID: 11400000, guid: 6aa0af71585acea4db4995c3931dc946, type: 2} - m_ParentUI: {fileID: 0} - sourceAsset: {fileID: 0} - m_SortingOrder: 1 - m_Position: 0 - m_WorldSpaceSizeMode: 1 - m_WorldSpaceWidth: 1920 - m_WorldSpaceHeight: 1080 - m_PivotReferenceSize: 0 - m_Pivot: 0 - m_WorldSpaceCollider: {fileID: 0} ---- !u!4 &954434161 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 954434158} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -52.51635, y: 0, z: -27.6311} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &955672358 GameObject: m_ObjectHideFlags: 0 @@ -29187,4 +29118,3 @@ SceneRoots: - {fileID: 914832726} - {fileID: 993870445} - {fileID: 759470735} - - {fileID: 954434161} diff --git a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs b/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs deleted file mode 100644 index 3af3076..0000000 --- a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Assets/_Project/Scripts/Dev/DebugCommandRelay.cs -using System.Collections.Generic; -using Unity.Netcode; -using UnityEngine; -using TD.Gameplay.Draft; - -namespace TD.Dev -{ - /// - /// Debug-only network relay: exposes one RPC that force-applies a - /// by its index, bypassing PlayerDraft's "must currently be - /// offered" check. - /// - /// - /// Lives entirely separate from PlayerDraft/DraftOption/DraftPool — none of those files - /// know this component exists. Removing this component (and TD.Dev.DebugConsole, which - /// calls it) leaves the shipping draft system completely untouched. - /// - /// Added to the Player prefab alongside PlayerDraft/PlayerGoldManager/etc. so it spawns - /// with a per-player OwnerClientId. Static per-client registry mirrors PlayerDraft's - /// GetForClient/Local pattern. - /// - public class DebugCommandRelay : NetworkBehaviour - { - private static readonly Dictionary s_byClientId - = new Dictionary(); - - public static DebugCommandRelay GetForClient(ulong clientId) - { - s_byClientId.TryGetValue(clientId, out var relay); - return relay; - } - - public static DebugCommandRelay Local - { - get - { - var nm = NetworkManager.Singleton; - if (nm == null || !nm.IsClient) return null; - return GetForClient(nm.LocalClientId); - } - } - - public override void OnNetworkSpawn() - { - s_byClientId[OwnerClientId] = this; - } - - public override void OnNetworkDespawn() - { - if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this) - s_byClientId.Remove(OwnerClientId); - } - - /// - /// Owning client: force-apply the DraftOption at (its - /// DraftPool index) immediately, skipping the normal "must be currently offered" gate. - /// Debug/testing only — any connected client can grant themselves anything via this - /// RPC, same tradeoff DevWaveControls already accepts for its hotkeys. - /// - [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)] - public void DebugGrantDraftOptionRpc(int optionId) - { - var option = DraftPool.Instance != null ? DraftPool.Instance.Get(optionId) : null; - if (option == null) - { - Debug.LogWarning($"[DebugCommandRelay] Grant requested for unknown option id {optionId}."); - return; - } - - if (option.ServerApply(OwnerClientId)) - Debug.Log($"[DebugCommandRelay] Granted '{option.DisplayName}' to client {OwnerClientId}."); - else - Debug.LogWarning($"[DebugCommandRelay] '{option.DisplayName}' could not be applied " + - $"to client {OwnerClientId}."); - } - } -} diff --git a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta b/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta deleted file mode 100644 index d7a2598..0000000 --- a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 65825b3189ae873948659b37b219f5e6 \ No newline at end of file diff --git a/Assets/_Project/Scripts/Dev/DebugConsole.cs b/Assets/_Project/Scripts/Dev/DebugConsole.cs deleted file mode 100644 index 869be74..0000000 --- a/Assets/_Project/Scripts/Dev/DebugConsole.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Assets/_Project/Scripts/Dev/DebugConsole.cs -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using UnityEngine; -using UnityEngine.InputSystem; -using UnityEngine.UIElements; -using TD.UI; -using TD.Gameplay.Draft; - -namespace TD.Dev -{ - /// - /// Standalone debug console overlay: press the toggle key to open a text - /// input, type a command, Enter to submit, Escape to cancel. - /// - /// - /// Deliberately decoupled from — own - /// GameObject, own UIDocument/PanelSettings — so it can be dropped out of - /// a build entirely by not including that GameObject, the same way - /// is kept separate from shipping systems. - /// - /// Supports one command so far: "get <type> <name>" grants a - /// to the local player via - /// . - /// - [RequireComponent(typeof(UIDocument))] - public class DebugConsole : MonoBehaviour - { - public static DebugConsole Instance { get; private set; } - - [Tooltip("Keyboard shortcut that opens and closes the console. Uses the new Input " + - "System (UnityEngine.InputSystem.Key). Backquote is the physical ` / ~ key.")] - [SerializeField] private Key toggleKey = Key.Backquote; - - private VisualElement consoleContainer; - private TextField commandInput; - private bool consoleOpen; - - // Frame on which the console was opened or closed. The toggle key and Enter - // are ignored on that frame and the next so the keypress that opened/closed - // the console doesn't also get typed into the field or immediately submit it. - // Mirrors HUDController's chatToggleSuppressFrame. - private int toggleSuppressFrame = -1; - - private void Awake() - { - if (Instance != null && Instance != this) - { - Debug.LogError("[DebugConsole] Duplicate instance detected. Destroying the new one."); - Destroy(gameObject); - return; - } - Instance = this; - } - - private void Start() - { - var doc = GetComponent(); - if (doc == null) - { - Debug.LogError("[DebugConsole] No UIDocument component found."); - return; - } - - var root = doc.rootVisualElement; - if (root == null) - { - Debug.LogError("[DebugConsole] rootVisualElement is null. " + - "Check that Panel Settings is assigned."); - return; - } - - BuildConsoleUi(root); - } - - private void BuildConsoleUi(VisualElement uiRoot) - { - // Top-anchored bar, closed by default. Distinct position from the HUD's - // bottom-left chat panel so the two never overlap. - consoleContainer = new VisualElement(); - consoleContainer.pickingMode = PickingMode.Ignore; - consoleContainer.style.position = Position.Absolute; - consoleContainer.style.left = 0; - consoleContainer.style.right = 0; - consoleContainer.style.top = 0; - consoleContainer.style.paddingTop = 8; - consoleContainer.style.paddingBottom = 8; - consoleContainer.style.paddingLeft = 12; - consoleContainer.style.paddingRight = 12; - consoleContainer.style.backgroundColor = new Color(0f, 0f, 0f, 0.75f); - consoleContainer.style.display = DisplayStyle.None; - - commandInput = new TextField(); - commandInput.style.minHeight = 28; - commandInput.style.width = Length.Percent(100); - commandInput.maxLength = 200; - commandInput.isDelayed = false; - StyleConsoleInput(commandInput); - - // Focus tracking — contributes to the same flag HUDController's chat - // input sets, so camera/builder/spell input all stay gated correctly - // regardless of which text field currently has focus. - commandInput.RegisterCallback(_ => HUDController.IsTextInputActive = true); - commandInput.RegisterCallback(_ => HUDController.IsTextInputActive = false); - - consoleContainer.Add(commandInput); - uiRoot.Add(consoleContainer); - } - - // Mirrors HUDController.StyleChatInputDark — dark background, white text, - // a thin border, and inner padding so ascenders/descenders don't clip. - // Green border (vs. chat's neutral gray) as a quick visual "this is the - // debug console, not chat" cue. - private static void StyleConsoleInput(TextField field) - { - var darkBg = new Color(0.05f, 0.05f, 0.05f, 0.95f); - var borderClr = new Color(0.35f, 0.75f, 0.35f); - - field.style.backgroundColor = darkBg; - field.style.color = Color.white; - field.style.borderTopWidth = field.style.borderBottomWidth = - field.style.borderLeftWidth = field.style.borderRightWidth = 1; - field.style.borderTopColor = field.style.borderBottomColor = - field.style.borderLeftColor = field.style.borderRightColor = borderClr; - - var inner = field.Q("unity-text-input"); - if (inner != null) - { - inner.style.backgroundColor = darkBg; - inner.style.color = Color.white; - inner.style.paddingTop = 4; - inner.style.paddingBottom = 4; - inner.style.paddingLeft = 6; - inner.style.paddingRight = 6; - } - } - - private void Update() - { - var kb = Keyboard.current; - if (kb == null) return; // no keyboard connected (e.g. headless server) - - if (Time.frameCount <= toggleSuppressFrame) return; - - if (toggleKey != Key.None && kb[toggleKey].wasPressedThisFrame) - { - if (consoleOpen) CloseConsole(); - else OpenConsole(); - return; - } - - if (!consoleOpen) return; - - bool enterDown = kb.enterKey.wasPressedThisFrame || kb.numpadEnterKey.wasPressedThisFrame; - bool escDown = kb.escapeKey.wasPressedThisFrame; - - if (enterDown) SubmitCommand(); - else if (escDown) CloseConsole(); - } - - private void OpenConsole() - { - if (commandInput == null) return; - consoleContainer.style.display = DisplayStyle.Flex; - consoleContainer.pickingMode = PickingMode.Position; - commandInput.SetValueWithoutNotify(string.Empty); - - // Suppress the toggle key and Enter for this frame and the next so the - // keypress that opened the console doesn't also get typed into the field. - // Focus is deferred a frame for the same reason — UI Toolkit would - // otherwise route the open keypress to the freshly-focused TextField. - toggleSuppressFrame = Time.frameCount + 1; - consoleOpen = true; - StartCoroutine(FocusNextFrame()); - } - - private IEnumerator FocusNextFrame() - { - yield return null; - if (consoleOpen && commandInput != null) - { - commandInput.Focus(); - commandInput.SelectAll(); - } - } - - private void CloseConsole() - { - if (commandInput != null) - { - commandInput.SetValueWithoutNotify(string.Empty); - commandInput.Blur(); - } - if (consoleContainer != null) - { - consoleContainer.style.display = DisplayStyle.None; - consoleContainer.pickingMode = PickingMode.Ignore; - } - - consoleOpen = false; - toggleSuppressFrame = Time.frameCount + 1; - - // The Enter that submitted/cancelled this console also gets polled by - // HUDController's chat this same frame — suppress its "Enter opens chat" - // branch so closing the debug console doesn't pop chat open too. - HUDController.SuppressChatOpenUntilFrame = Time.frameCount + 1; - } - - private void SubmitCommand() - { - string text = commandInput?.value ?? string.Empty; - CloseConsole(); - - if (string.IsNullOrWhiteSpace(text)) return; - ExecuteCommand(text.Trim()); - } - - // Maps the type token in "get " to the DraftOption subclass it - // should match against. Add an entry here whenever a new DraftOption subclass - // should be gettable from the console. - private static readonly Dictionary DraftTypeAliases = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "spell", typeof(BuilderSpellDraftOption) }, - { "tower", typeof(NewTowerDraftOption) }, - { "buff", typeof(BuilderEffectDraftOption) }, - { "effect", typeof(BuilderEffectDraftOption) }, - }; - - private void ExecuteCommand(string text) - { - string[] tokens = text.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); - if (tokens.Length < 3 || !string.Equals(tokens[0], "get", StringComparison.OrdinalIgnoreCase)) - { - Debug.Log($"[DebugConsole] Unrecognized command: \"{text}\". Try: get "); - return; - } - - string nameToken = string.Join(" ", tokens, 2, tokens.Length - 2); - ExecuteGet(tokens[1], nameToken); - } - - // "get ": grants any DraftOption of the given type whose DisplayName - // matches , via the local player's DebugCommandRelay. Reuses DraftOption's - // existing type-agnostic ServerApply dispatch instead of hardcoding a grant path - // per option type. - private void ExecuteGet(string typeToken, string nameToken) - { - if (!DraftTypeAliases.TryGetValue(typeToken, out var optionType)) - { - Debug.LogWarning($"[DebugConsole] Unknown draft option type \"{typeToken}\". " + - $"Valid types: {string.Join(", ", DraftTypeAliases.Keys)}"); - return; - } - - var pool = DraftPool.Instance; - if (pool == null) - { - Debug.LogWarning("[DebugConsole] No DraftPool in this scene."); - return; - } - - string normalizedTarget = Normalize(nameToken); - for (int i = 0; i < pool.Count; i++) - { - var option = pool.Get(i); - if (option == null || !optionType.IsInstanceOfType(option)) continue; - if (Normalize(option.DisplayName) != normalizedTarget) continue; - - var relay = DebugCommandRelay.Local; - if (relay == null) - { - Debug.LogWarning("[DebugConsole] No local DebugCommandRelay found — " + - "is it on the Player prefab?"); - return; - } - - relay.DebugGrantDraftOptionRpc(i); - return; - } - - Debug.LogWarning($"[DebugConsole] No {typeToken} option named \"{nameToken}\" found."); - } - - // DisplayName is player-facing draft card text (its spacing/casing is chosen for - // presentation, not for typing), so command matching strips everything but letters - // and digits before comparing — "Slow Area", "slow_area", and "SlowArea" all match - // the same option. - private static string Normalize(string s) - { - if (string.IsNullOrEmpty(s)) return string.Empty; - var sb = new StringBuilder(s.Length); - foreach (char c in s) - if (char.IsLetterOrDigit(c)) sb.Append(char.ToLowerInvariant(c)); - return sb.ToString(); - } - - private void OnDestroy() - { - if (Instance == this) Instance = null; - } - } -} diff --git a/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta b/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta deleted file mode 100644 index c9a3292..0000000 --- a/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7c6a808e8291552d0b64e36a210879c4 \ No newline at end of file diff --git a/Assets/_Project/Scripts/UI/HUDController.cs b/Assets/_Project/Scripts/UI/HUDController.cs index badd2b8..03cb3b0 100644 --- a/Assets/_Project/Scripts/UI/HUDController.cs +++ b/Assets/_Project/Scripts/UI/HUDController.cs @@ -157,19 +157,8 @@ namespace TD.UI // Set true whenever the chat input or any other text field on the HUD has // keyboard focus. Camera, builder input, and hotkey handlers all gate on - // this to keep typing from driving gameplay. Setter is internal (not - // private) so other standalone text-input surfaces — e.g. TD.Dev.DebugConsole, - // which is not part of HUDController — can contribute to the same flag - // instead of every gameplay script needing to know about every input widget. - public static bool IsTextInputActive { get; internal set; } - - // Frame until which chat's "Enter opens chat" behavior is suppressed. Other - // standalone text-input surfaces (e.g. TD.Dev.DebugConsole) set this when their - // own Enter-driven submit/close already consumed the keypress, so the same - // Enter press doesn't also get interpreted as "open chat" this frame. Without - // this, submitting a debug console command with Enter would immediately pop - // chat open too, since both systems poll the raw key independently. - internal static int SuppressChatOpenUntilFrame = -1; + // this to keep typing from driving gameplay. + public static bool IsTextInputActive { get; private set; } // ----- State ------------------------------------------------------ @@ -2028,7 +2017,7 @@ namespace TD.UI if (!chatInputOpen) { - if (enterDown && Time.frameCount > SuppressChatOpenUntilFrame) OpenChatInput(); + if (enterDown) OpenChatInput(); return; }