Compare commits
No commits in common. "04d7b42e60f093ae6830562f26b6edefab395d49" and "e25a691ad99afdb7a5e4dddd770bbf2cb0180419" have entirely different histories.
04d7b42e60
...
e25a691ad9
7 changed files with 3 additions and 485 deletions
|
|
@ -18,7 +18,6 @@ GameObject:
|
||||||
- component: {fileID: 2806524246861401801}
|
- component: {fileID: 2806524246861401801}
|
||||||
- component: {fileID: 5683786710272852339}
|
- component: {fileID: 5683786710272852339}
|
||||||
- component: {fileID: 6856236869205671849}
|
- component: {fileID: 6856236869205671849}
|
||||||
- component: {fileID: -4438158474568445526}
|
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Player
|
m_Name: Player
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
|
|
@ -174,16 +173,3 @@ MonoBehaviour:
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout
|
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout
|
||||||
ShowTopMostFoldoutHeaderGroup: 1
|
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
|
|
||||||
|
|
|
||||||
|
|
@ -13757,75 +13757,6 @@ Mesh:
|
||||||
- serializedVersion: 1
|
- serializedVersion: 1
|
||||||
m_IndexStart: 0
|
m_IndexStart: 0
|
||||||
m_IndexCount: 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
|
--- !u!1 &955672358
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
@ -29187,4 +29118,3 @@ SceneRoots:
|
||||||
- {fileID: 914832726}
|
- {fileID: 914832726}
|
||||||
- {fileID: 993870445}
|
- {fileID: 993870445}
|
||||||
- {fileID: 759470735}
|
- {fileID: 759470735}
|
||||||
- {fileID: 954434161}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Debug-only network relay: exposes one RPC that force-applies a <see cref="DraftOption"/>
|
|
||||||
/// by its <see cref="DraftPool"/> index, bypassing <c>PlayerDraft</c>'s "must currently be
|
|
||||||
/// offered" check.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 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.
|
|
||||||
/// </remarks>
|
|
||||||
public class DebugCommandRelay : NetworkBehaviour
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<ulong, DebugCommandRelay> s_byClientId
|
|
||||||
= new Dictionary<ulong, DebugCommandRelay>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Owning client: force-apply the DraftOption at <paramref name="optionId"/> (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.
|
|
||||||
/// </summary>
|
|
||||||
[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}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 65825b3189ae873948659b37b219f5e6
|
|
||||||
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Standalone debug console overlay: press the toggle key to open a text
|
|
||||||
/// input, type a command, Enter to submit, Escape to cancel.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Deliberately decoupled from <see cref="HUDController"/> — own
|
|
||||||
/// GameObject, own UIDocument/PanelSettings — so it can be dropped out of
|
|
||||||
/// a build entirely by not including that GameObject, the same way
|
|
||||||
/// <see cref="DevWaveControls"/> is kept separate from shipping systems.
|
|
||||||
///
|
|
||||||
/// Supports one command so far: "get <type> <name>" grants a
|
|
||||||
/// <see cref="TD.Gameplay.Draft.DraftOption"/> to the local player via
|
|
||||||
/// <see cref="DebugCommandRelay"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[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<UIDocument>();
|
|
||||||
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<FocusInEvent>(_ => HUDController.IsTextInputActive = true);
|
|
||||||
commandInput.RegisterCallback<FocusOutEvent>(_ => 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 <type> <name>" 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<string, Type> DraftTypeAliases =
|
|
||||||
new Dictionary<string, Type>(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 <type> <name>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string nameToken = string.Join(" ", tokens, 2, tokens.Length - 2);
|
|
||||||
ExecuteGet(tokens[1], nameToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// "get <type> <name>": grants any DraftOption of the given type whose DisplayName
|
|
||||||
// matches <name>, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 7c6a808e8291552d0b64e36a210879c4
|
|
||||||
|
|
@ -157,19 +157,8 @@ namespace TD.UI
|
||||||
|
|
||||||
// Set true whenever the chat input or any other text field on the HUD has
|
// 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
|
// keyboard focus. Camera, builder input, and hotkey handlers all gate on
|
||||||
// this to keep typing from driving gameplay. Setter is internal (not
|
// this to keep typing from driving gameplay.
|
||||||
// private) so other standalone text-input surfaces — e.g. TD.Dev.DebugConsole,
|
public static bool IsTextInputActive { get; private set; }
|
||||||
// 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;
|
|
||||||
|
|
||||||
// ----- State ------------------------------------------------------
|
// ----- State ------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -2028,7 +2017,7 @@ namespace TD.UI
|
||||||
|
|
||||||
if (!chatInputOpen)
|
if (!chatInputOpen)
|
||||||
{
|
{
|
||||||
if (enterDown && Time.frameCount > SuppressChatOpenUntilFrame) OpenChatInput();
|
if (enterDown) OpenChatInput();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue