B button launches upgrade menu
This commit is contained in:
parent
79cd331141
commit
3ada934e41
1 changed files with 89 additions and 56 deletions
|
|
@ -25,53 +25,56 @@ namespace TD.UI
|
||||||
|
|
||||||
// ----- Inspector --------------------------------------------------
|
// ----- Inspector --------------------------------------------------
|
||||||
|
|
||||||
[Header("Scene References")]
|
[Header("Scene References")] [Tooltip("The local client's TowerPlacementController.")] [SerializeField]
|
||||||
[Tooltip("The local client's TowerPlacementController.")]
|
private TowerPlacementController placementController;
|
||||||
[SerializeField] private TowerPlacementController placementController;
|
|
||||||
|
|
||||||
[Tooltip("The local client's TowerPaintController (drives the Paint tab + paint cursor).")]
|
[Tooltip("The local client's TowerPaintController (drives the Paint tab + paint cursor).")]
|
||||||
[SerializeField] private TowerPaintController paintController;
|
[SerializeField] private TowerPaintController paintController;
|
||||||
|
|
||||||
[Tooltip("The TowerPlacementManager NetworkObject in the scene.")]
|
[Tooltip("The TowerPlacementManager NetworkObject in the scene.")] [SerializeField]
|
||||||
[SerializeField] private TowerPlacementManager placementManager;
|
private TowerPlacementManager placementManager;
|
||||||
|
|
||||||
[Tooltip("The local client's CameraController. Used by the minimap for click-to-jump " +
|
[Tooltip("The local client's CameraController. Used by the minimap for click-to-jump " +
|
||||||
"and drag-to-pan.")]
|
"and drag-to-pan.")]
|
||||||
[SerializeField] private CameraController cameraController;
|
[SerializeField]
|
||||||
|
private CameraController cameraController;
|
||||||
|
|
||||||
[Header("Settings")]
|
[Header("Settings")] [SerializeField] private float rejectionMessageDuration = 2.5f;
|
||||||
[SerializeField] private float rejectionMessageDuration = 2.5f;
|
|
||||||
|
|
||||||
[Tooltip("Maximum visible height of the chat feed in pixels. Content past this " +
|
[Tooltip("Maximum visible height of the chat feed in pixels. Content past this " +
|
||||||
"height is clipped — older messages scroll off the top of the visible area " +
|
"height is clipped — older messages scroll off the top of the visible area " +
|
||||||
"but stay in history (scroll up while chat is open to view).")]
|
"but stay in history (scroll up while chat is open to view).")]
|
||||||
[SerializeField] private float chatMaxHeight = 280f;
|
[SerializeField]
|
||||||
|
private float chatMaxHeight = 280f;
|
||||||
|
|
||||||
[Tooltip("Maximum messages kept in chat history. Defaults to effectively unlimited " +
|
[Tooltip("Maximum messages kept in chat history. Defaults to effectively unlimited " +
|
||||||
"(int.MaxValue) — every message sent during a match stays scrollable. " +
|
"(int.MaxValue) — every message sent during a match stays scrollable. " +
|
||||||
"Lower the value if a long match ever shows DOM perf issues; this field " +
|
"Lower the value if a long match ever shows DOM perf issues; this field " +
|
||||||
"is the safety valve, not a normal-play limit.")]
|
"is the safety valve, not a normal-play limit.")]
|
||||||
[SerializeField] private int chatMaxMessages = int.MaxValue;
|
[SerializeField]
|
||||||
|
private int chatMaxMessages = int.MaxValue;
|
||||||
|
|
||||||
[Tooltip("Color used for SYSTEM chat messages (e.g. 'Life Lost', income changes).")]
|
[Tooltip("Color used for SYSTEM chat messages (e.g. 'Life Lost', income changes).")] [SerializeField]
|
||||||
[SerializeField] private Color chatSystemColor = new Color(1f, 0.7f, 0.2f);
|
private Color chatSystemColor = new Color(1f, 0.7f, 0.2f);
|
||||||
|
|
||||||
[Tooltip("Color used for PLAYER chat message bodies. Sender prefix uses the player's slot color.")]
|
[Tooltip("Color used for PLAYER chat message bodies. Sender prefix uses the player's slot color.")]
|
||||||
[SerializeField] private Color chatPlayerColor = new Color(0.92f, 0.92f, 0.92f);
|
[SerializeField]
|
||||||
|
private Color chatPlayerColor = new Color(0.92f, 0.92f, 0.92f);
|
||||||
|
|
||||||
// ----- Cached UI element references -------------------------------
|
// ----- Cached UI element references -------------------------------
|
||||||
|
|
||||||
private Label goldLabel;
|
private Label goldLabel;
|
||||||
private Label waveLabel;
|
private Label waveLabel;
|
||||||
private Label livesLabel;
|
private Label livesLabel;
|
||||||
private Label nextWaveLabel; // prep countdown ("next: 0:12")
|
private Label nextWaveLabel; // prep countdown ("next: 0:12")
|
||||||
private Label leakedLabel; // local player's origin-leak count ("leaked: 3")
|
private Label leakedLabel; // local player's origin-leak count ("leaked: 3")
|
||||||
private Label incomeLabel; // top-bar per-wave gold-earned counter ("+150 g/wave")
|
private Label incomeLabel; // top-bar per-wave gold-earned counter ("+150 g/wave")
|
||||||
private VisualElement playerListContainer; // right-panel scoreboard rows
|
private VisualElement playerListContainer; // right-panel scoreboard rows
|
||||||
private Label portraitName;
|
private Label portraitName;
|
||||||
private Label levelLabel;
|
private Label levelLabel;
|
||||||
private VisualElement statLines;
|
private VisualElement statLines;
|
||||||
private VisualElement commandGrid;
|
private VisualElement commandGrid;
|
||||||
|
|
||||||
private VisualElement actionFrame; // hidden via display:none when no actions are available
|
private VisualElement actionFrame; // hidden via display:none when no actions are available
|
||||||
private VisualElement commandTabs; // Build/Paint tab row — shown only for a Builder selection
|
private VisualElement commandTabs; // Build/Paint tab row — shown only for a Builder selection
|
||||||
private Button tabBuild;
|
private Button tabBuild;
|
||||||
|
|
@ -79,6 +82,7 @@ namespace TD.UI
|
||||||
private VisualElement buildProgressContainer; // info-panel sub-view, shown for BuildSiteVisual selections
|
private VisualElement buildProgressContainer; // info-panel sub-view, shown for BuildSiteVisual selections
|
||||||
private VisualElement buildProgressFill; // width driven each frame from progress
|
private VisualElement buildProgressFill; // width driven each frame from progress
|
||||||
private Label buildProgressPercent;
|
private Label buildProgressPercent;
|
||||||
|
|
||||||
private Label ttTitle;
|
private Label ttTitle;
|
||||||
private Label ttDesc;
|
private Label ttDesc;
|
||||||
private Label ttStats;
|
private Label ttStats;
|
||||||
|
|
@ -91,7 +95,7 @@ namespace TD.UI
|
||||||
// without rebuilding the elements.
|
// without rebuilding the elements.
|
||||||
private VisualElement enemyHealthBar;
|
private VisualElement enemyHealthBar;
|
||||||
private VisualElement enemyHealthFill;
|
private VisualElement enemyHealthFill;
|
||||||
private Label enemyHealthText;
|
private Label enemyHealthText;
|
||||||
|
|
||||||
// Match-end overlay — built once on Start and toggled on Phase changes.
|
// Match-end overlay — built once on Start and toggled on Phase changes.
|
||||||
private VisualElement matchEndOverlay;
|
private VisualElement matchEndOverlay;
|
||||||
|
|
@ -99,15 +103,15 @@ namespace TD.UI
|
||||||
// Buff menu overlay — toggled by the B key via ToggleBuffMenu().
|
// Buff menu overlay — toggled by the B key via ToggleBuffMenu().
|
||||||
private VisualElement buffMenuOverlay;
|
private VisualElement buffMenuOverlay;
|
||||||
private VisualElement buffMenuContent;
|
private VisualElement buffMenuContent;
|
||||||
private Label matchEndTitle;
|
private Label matchEndTitle;
|
||||||
|
|
||||||
// Chat panel (bottom-left, above portrait) — programmatic. The container
|
// Chat panel (bottom-left, above portrait) — programmatic. The container
|
||||||
// holds both the scrollable feed and the input. Highlight + scroll
|
// holds both the scrollable feed and the input. Highlight + scroll
|
||||||
// interactivity are toggled on the container when typing.
|
// interactivity are toggled on the container when typing.
|
||||||
private VisualElement chatContainer;
|
private VisualElement chatContainer;
|
||||||
private ScrollView chatFeed;
|
private ScrollView chatFeed;
|
||||||
private TextField chatInput;
|
private TextField chatInput;
|
||||||
private bool chatInputOpen;
|
private bool chatInputOpen;
|
||||||
|
|
||||||
// Frame on which the chat input was opened or closed. Enter on that frame
|
// Frame on which the chat input was opened or closed. Enter on that frame
|
||||||
// and the next one is ignored to prevent the open/close-triggering keypress
|
// and the next one is ignored to prevent the open/close-triggering keypress
|
||||||
|
|
@ -129,12 +133,12 @@ namespace TD.UI
|
||||||
private CommandTab activeTab = CommandTab.Build;
|
private CommandTab activeTab = CommandTab.Build;
|
||||||
|
|
||||||
private Coroutine rejectionFadeCoroutine;
|
private Coroutine rejectionFadeCoroutine;
|
||||||
private bool placementManagerReady; // true once TowerPlacementManager.Instance is non-null
|
private bool placementManagerReady; // true once TowerPlacementManager.Instance is non-null
|
||||||
private bool uiInitialized;
|
private bool uiInitialized;
|
||||||
private bool selectionSubscribed; // true once we've successfully hooked SelectionState.OnSelectionChanged
|
private bool selectionSubscribed; // true once we've successfully hooked SelectionState.OnSelectionChanged
|
||||||
private bool matchStateSubscribed; // true once OnPhaseChanged is hooked
|
private bool matchStateSubscribed; // true once OnPhaseChanged is hooked
|
||||||
private MinimapView minimapView;
|
private MinimapView minimapView;
|
||||||
private IPanel myPanel; // tracked separately so OnDestroy only clears the static if it still points at us
|
private IPanel myPanel; // tracked separately so OnDestroy only clears the static if it still points at us
|
||||||
|
|
||||||
// ----- Hotkeys ----------------------------------------------------
|
// ----- Hotkeys ----------------------------------------------------
|
||||||
//
|
//
|
||||||
|
|
@ -156,10 +160,15 @@ namespace TD.UI
|
||||||
private readonly struct HotkeyBinding
|
private readonly struct HotkeyBinding
|
||||||
{
|
{
|
||||||
public readonly Key Key;
|
public readonly Key Key;
|
||||||
public readonly VisualElement Button; // for enabledSelf gating
|
public readonly VisualElement Button; // for enabledSelf gating
|
||||||
public readonly System.Action Action;
|
public readonly System.Action Action;
|
||||||
|
|
||||||
public HotkeyBinding(Key k, VisualElement b, System.Action a)
|
public HotkeyBinding(Key k, VisualElement b, System.Action a)
|
||||||
{ Key = k; Button = b; Action = a; }
|
{
|
||||||
|
Key = k;
|
||||||
|
Button = b;
|
||||||
|
Action = a;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Static hit-test probe --------------------------------------
|
// ----- Static hit-test probe --------------------------------------
|
||||||
|
|
@ -255,6 +264,7 @@ namespace TD.UI
|
||||||
|
|
||||||
// Cache element references — log a warning for any that are missing
|
// Cache element references — log a warning for any that are missing
|
||||||
// so UXML/USS mismatches surface immediately.
|
// so UXML/USS mismatches surface immediately.
|
||||||
|
|
||||||
goldLabel = Require<Label>(root, "gold-label");
|
goldLabel = Require<Label>(root, "gold-label");
|
||||||
waveLabel = Require<Label>(root, "wave-label");
|
waveLabel = Require<Label>(root, "wave-label");
|
||||||
livesLabel = Require<Label>(root, "lives-label");
|
livesLabel = Require<Label>(root, "lives-label");
|
||||||
|
|
@ -273,14 +283,15 @@ namespace TD.UI
|
||||||
|
|
||||||
if (tabBuild != null) tabBuild.clicked += () => SwitchTab(CommandTab.Build);
|
if (tabBuild != null) tabBuild.clicked += () => SwitchTab(CommandTab.Build);
|
||||||
if (tabPaint != null) tabPaint.clicked += () => SwitchTab(CommandTab.Paint);
|
if (tabPaint != null) tabPaint.clicked += () => SwitchTab(CommandTab.Paint);
|
||||||
|
|
||||||
buildProgressContainer = Require<VisualElement>(root, "build-progress");
|
buildProgressContainer = Require<VisualElement>(root, "build-progress");
|
||||||
buildProgressFill = Require<VisualElement>(root, "build-progress-fill");
|
buildProgressFill = Require<VisualElement>(root, "build-progress-fill");
|
||||||
buildProgressPercent = Require<Label>(root, "build-progress-percent");
|
buildProgressPercent = Require<Label>(root, "build-progress-percent");
|
||||||
ttTitle = Require<Label>(root, "tt-title");
|
ttTitle = Require<Label>(root, "tt-title");
|
||||||
ttDesc = Require<Label>(root, "tt-desc");
|
ttDesc = Require<Label>(root, "tt-desc");
|
||||||
ttStats = Require<Label>(root, "tt-stats");
|
ttStats = Require<Label>(root, "tt-stats");
|
||||||
ttCost = Require<Label>(root, "tt-cost");
|
ttCost = Require<Label>(root, "tt-cost");
|
||||||
rejectionLabel = Require<Label>(root, "rejection-label");
|
rejectionLabel = Require<Label>(root, "rejection-label");
|
||||||
|
|
||||||
// Map area and its transparent ancestors must not consume pointer
|
// Map area and its transparent ancestors must not consume pointer
|
||||||
// events so clicks reach the 3D scene underneath. The bottom-ui is now
|
// events so clicks reach the 3D scene underneath. The bottom-ui is now
|
||||||
|
|
@ -340,8 +351,8 @@ namespace TD.UI
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
TowerPlacementController.OnRejectionMessageReady += ShowRejectionMessage;
|
TowerPlacementController.OnRejectionMessageReady += ShowRejectionMessage;
|
||||||
WaveManager.OnLifeLost += HandleLifeLost;
|
WaveManager.OnLifeLost += HandleLifeLost;
|
||||||
ChatService.OnMessageReceived += HandleChatMessage;
|
ChatService.OnMessageReceived += HandleChatMessage;
|
||||||
// Try to subscribe now; if SelectionState.Awake hasn't run yet (Unity does
|
// Try to subscribe now; if SelectionState.Awake hasn't run yet (Unity does
|
||||||
// not guarantee Awake/OnEnable ordering across objects), Start will retry.
|
// not guarantee Awake/OnEnable ordering across objects), Start will retry.
|
||||||
TrySubscribeSelection();
|
TrySubscribeSelection();
|
||||||
|
|
@ -350,13 +361,14 @@ namespace TD.UI
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
TowerPlacementController.OnRejectionMessageReady -= ShowRejectionMessage;
|
TowerPlacementController.OnRejectionMessageReady -= ShowRejectionMessage;
|
||||||
WaveManager.OnLifeLost -= HandleLifeLost;
|
WaveManager.OnLifeLost -= HandleLifeLost;
|
||||||
ChatService.OnMessageReceived -= HandleChatMessage;
|
ChatService.OnMessageReceived -= HandleChatMessage;
|
||||||
if (selectionSubscribed && SelectionState.Instance != null)
|
if (selectionSubscribed && SelectionState.Instance != null)
|
||||||
{
|
{
|
||||||
SelectionState.Instance.OnSelectionChanged -= HandleSelectionChanged;
|
SelectionState.Instance.OnSelectionChanged -= HandleSelectionChanged;
|
||||||
selectionSubscribed = false;
|
selectionSubscribed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchStateSubscribed && MatchState.Instance != null)
|
if (matchStateSubscribed && MatchState.Instance != null)
|
||||||
{
|
{
|
||||||
MatchState.Instance.OnPhaseChanged -= HandlePhaseChanged;
|
MatchState.Instance.OnPhaseChanged -= HandlePhaseChanged;
|
||||||
|
|
@ -421,6 +433,7 @@ namespace TD.UI
|
||||||
buildProgressFill.style.width =
|
buildProgressFill.style.width =
|
||||||
new StyleLength(new Length(progress * 100f, LengthUnit.Percent));
|
new StyleLength(new Length(progress * 100f, LengthUnit.Percent));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildProgressPercent != null)
|
if (buildProgressPercent != null)
|
||||||
{
|
{
|
||||||
buildProgressPercent.text = $"{Mathf.RoundToInt(progress * 100f)}%";
|
buildProgressPercent.text = $"{Mathf.RoundToInt(progress * 100f)}%";
|
||||||
|
|
@ -608,8 +621,8 @@ namespace TD.UI
|
||||||
nameLabel.style.color = PlayerColors.Get(pms.Slot);
|
nameLabel.style.color = PlayerColors.Get(pms.Slot);
|
||||||
nameLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
nameLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||||
nameLabel.style.fontSize = 10;
|
nameLabel.style.fontSize = 10;
|
||||||
nameLabel.style.whiteSpace = WhiteSpace.NoWrap; // keep name on one line
|
nameLabel.style.whiteSpace = WhiteSpace.NoWrap; // keep name on one line
|
||||||
nameLabel.style.flexGrow = 1; // takes all leftover width
|
nameLabel.style.flexGrow = 1; // takes all leftover width
|
||||||
nameLabel.style.flexShrink = 1;
|
nameLabel.style.flexShrink = 1;
|
||||||
nameLabel.style.overflow = Overflow.Hidden;
|
nameLabel.style.overflow = Overflow.Hidden;
|
||||||
nameLabel.style.textOverflow = TextOverflow.Ellipsis;
|
nameLabel.style.textOverflow = TextOverflow.Ellipsis;
|
||||||
|
|
@ -653,6 +666,7 @@ namespace TD.UI
|
||||||
// gated by the lobby), but the values do.
|
// gated by the lobby), but the values do.
|
||||||
private static readonly System.Text.StringBuilder s_scoreboardSigBuf =
|
private static readonly System.Text.StringBuilder s_scoreboardSigBuf =
|
||||||
new System.Text.StringBuilder(64);
|
new System.Text.StringBuilder(64);
|
||||||
|
|
||||||
private string ComputeScoreboardSignature(System.Collections.Generic.List<PlayerMatchState> players)
|
private string ComputeScoreboardSignature(System.Collections.Generic.List<PlayerMatchState> players)
|
||||||
{
|
{
|
||||||
s_scoreboardSigBuf.Clear();
|
s_scoreboardSigBuf.Clear();
|
||||||
|
|
@ -662,12 +676,14 @@ namespace TD.UI
|
||||||
var gm = PlayerGoldManager.GetForClient(pms.OwnerClientId);
|
var gm = PlayerGoldManager.GetForClient(pms.OwnerClientId);
|
||||||
int gold = gm != null ? gm.CurrentGold : 0;
|
int gold = gm != null ? gm.CurrentGold : 0;
|
||||||
int leaks = (wm != null && pms.Slot != PlayerSlot.None)
|
int leaks = (wm != null && pms.Slot != PlayerSlot.None)
|
||||||
? wm.GetZoneLeakCount(pms.Slot) : 0;
|
? wm.GetZoneLeakCount(pms.Slot)
|
||||||
|
: 0;
|
||||||
s_scoreboardSigBuf.Append((int)pms.Slot).Append(':')
|
s_scoreboardSigBuf.Append((int)pms.Slot).Append(':')
|
||||||
.Append(pms.DisplayName ?? string.Empty).Append(':')
|
.Append(pms.DisplayName ?? string.Empty).Append(':')
|
||||||
.Append(gold).Append(':')
|
.Append(gold).Append(':')
|
||||||
.Append(leaks).Append(';');
|
.Append(leaks).Append(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_scoreboardSigBuf.ToString();
|
return s_scoreboardSigBuf.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,7 +691,7 @@ namespace TD.UI
|
||||||
|
|
||||||
private const int GRID_COLS = 5;
|
private const int GRID_COLS = 5;
|
||||||
private const int GRID_ROWS = 3;
|
private const int GRID_ROWS = 3;
|
||||||
private const int GRID_MAX = GRID_COLS * GRID_ROWS;
|
private const int GRID_MAX = GRID_COLS * GRID_ROWS;
|
||||||
|
|
||||||
// First-time check that TowerPlacementManager exists. Once ready, populates the
|
// First-time check that TowerPlacementManager exists. Once ready, populates the
|
||||||
// grid for the current selection. Subsequent populates flow through
|
// grid for the current selection. Subsequent populates flow through
|
||||||
|
|
@ -685,7 +701,7 @@ namespace TD.UI
|
||||||
if (placementManager == null)
|
if (placementManager == null)
|
||||||
placementManager = TowerPlacementManager.Instance;
|
placementManager = TowerPlacementManager.Instance;
|
||||||
|
|
||||||
if (placementManager == null) return; // not spawned yet — retry next frame
|
if (placementManager == null) return; // not spawned yet — retry next frame
|
||||||
|
|
||||||
placementManagerReady = true;
|
placementManagerReady = true;
|
||||||
PopulateGridForSelection(SelectionState.Instance?.SelectedObject);
|
PopulateGridForSelection(SelectionState.Instance?.SelectedObject);
|
||||||
|
|
@ -700,7 +716,7 @@ namespace TD.UI
|
||||||
private void PopulateGridForSelection(ISelectable selection)
|
private void PopulateGridForSelection(ISelectable selection)
|
||||||
{
|
{
|
||||||
if (commandGrid == null) return;
|
if (commandGrid == null) return;
|
||||||
if (!placementManagerReady) return; // deferred to TryReadyPlacementManager
|
if (!placementManagerReady) return; // deferred to TryReadyPlacementManager
|
||||||
|
|
||||||
// Selection changed — invalidate the previous frame's hotkey bindings
|
// Selection changed — invalidate the previous frame's hotkey bindings
|
||||||
// before creating new ones. Without this, stale buttons from a previous
|
// before creating new ones. Without this, stale buttons from a previous
|
||||||
|
|
@ -710,8 +726,8 @@ namespace TD.UI
|
||||||
// Decide whether any actions exist for this selection. The action frame
|
// Decide whether any actions exist for this selection. The action frame
|
||||||
// is hidden entirely when there are none — matches the WC3-style UX.
|
// is hidden entirely when there are none — matches the WC3-style UX.
|
||||||
bool hasActions = selection is Builder
|
bool hasActions = selection is Builder
|
||||||
|| selection is TowerInstance
|
|| selection is TowerInstance
|
||||||
|| selection is BuildSiteVisual;
|
|| selection is BuildSiteVisual;
|
||||||
if (actionFrame != null)
|
if (actionFrame != null)
|
||||||
actionFrame.style.display = hasActions ? DisplayStyle.Flex : DisplayStyle.None;
|
actionFrame.style.display = hasActions ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
|
||||||
|
|
@ -729,7 +745,7 @@ namespace TD.UI
|
||||||
RefreshTabActiveState();
|
RefreshTabActiveState();
|
||||||
|
|
||||||
commandGrid.Clear();
|
commandGrid.Clear();
|
||||||
if (!hasActions) return; // grid stays empty; frame is hidden anyway
|
if (!hasActions) return; // grid stays empty; frame is hidden anyway
|
||||||
|
|
||||||
// Build the 15-cell action layout for the active selection kind.
|
// Build the 15-cell action layout for the active selection kind.
|
||||||
var cells = new VisualElement[GRID_MAX];
|
var cells = new VisualElement[GRID_MAX];
|
||||||
|
|
@ -754,12 +770,14 @@ namespace TD.UI
|
||||||
cells[i] = CreateTowerButton(def, typeId, HotkeyLayout[i]);
|
cells[i] = CreateTowerButton(def, typeId, HotkeyLayout[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
cells[GRID_MAX - 1] = CreateBuffMenuButton(HotkeyLayout[GRID_MAX - 1]);
|
||||||
}
|
}
|
||||||
else if (selection is TowerInstance tower)
|
else if (selection is TowerInstance tower)
|
||||||
{
|
{
|
||||||
// WC3 layout convention: primary action top-left (Q), sell bottom-right (B).
|
// WC3 layout convention: primary action top-left (Q), sell bottom-right (B).
|
||||||
cells[0] = CreateUpgradeButton(tower, HotkeyLayout[0]);
|
cells[0] = CreateUpgradeButton(tower, HotkeyLayout[0]);
|
||||||
cells[GRID_MAX - 1] = CreateSellButton(tower, HotkeyLayout[GRID_MAX - 1]);
|
cells[GRID_MAX - 1] = CreateSellButton(tower, HotkeyLayout[GRID_MAX - 1]);
|
||||||
}
|
}
|
||||||
else if (selection is BuildSiteVisual bsv)
|
else if (selection is BuildSiteVisual bsv)
|
||||||
|
|
@ -908,6 +926,7 @@ namespace TD.UI
|
||||||
costLabel.pickingMode = PickingMode.Ignore;
|
costLabel.pickingMode = PickingMode.Ignore;
|
||||||
btn.Add(costLabel);
|
btn.Add(costLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -917,9 +936,12 @@ namespace TD.UI
|
||||||
private VisualElement CreateUpgradeButton(TowerInstance tower, Key hotkey)
|
private VisualElement CreateUpgradeButton(TowerInstance tower, Key hotkey)
|
||||||
{
|
{
|
||||||
var btn = CreateActionButton(
|
var btn = CreateActionButton(
|
||||||
costText: "", // tier cost unknown until upgrade system lands
|
costText: "", // tier cost unknown until upgrade system lands
|
||||||
hotkey: hotkey,
|
hotkey: hotkey,
|
||||||
onClick: () => { /* TODO: upgrade flow */ });
|
onClick: () =>
|
||||||
|
{
|
||||||
|
/* TODO: upgrade flow */
|
||||||
|
});
|
||||||
btn.SetEnabled(false);
|
btn.SetEnabled(false);
|
||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
@ -931,12 +953,23 @@ namespace TD.UI
|
||||||
: 0;
|
: 0;
|
||||||
var btn = CreateActionButton(
|
var btn = CreateActionButton(
|
||||||
costText: sellValue > 0 ? $"+{sellValue}g" : "",
|
costText: sellValue > 0 ? $"+{sellValue}g" : "",
|
||||||
hotkey: hotkey,
|
hotkey: hotkey,
|
||||||
onClick: () => { /* TODO: sell flow */ });
|
onClick: () =>
|
||||||
|
{
|
||||||
|
/* TODO: sell flow */
|
||||||
|
});
|
||||||
btn.SetEnabled(false);
|
btn.SetEnabled(false);
|
||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VisualElement CreateBuffMenuButton(Key hotkey)
|
||||||
|
{
|
||||||
|
return CreateActionButton(
|
||||||
|
costText: "Buffs",
|
||||||
|
hotkey: hotkey,
|
||||||
|
onClick: () => ToggleBuffMenu());
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel action for an in-progress build. Fires the owner-only RPC; the
|
// Cancel action for an in-progress build. Fires the owner-only RPC; the
|
||||||
// server cancels the matching job (or, for shelved sites, refunds + despawns
|
// server cancels the matching job (or, for shelved sites, refunds + despawns
|
||||||
// directly), full gold is refunded, the BuildSiteVisual is despawned, and
|
// directly), full gold is refunded, the BuildSiteVisual is despawned, and
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue