Updates to spells, HUD, Gold, and Camera Controls. New sound effects!

This commit is contained in:
Matt F 2026-07-18 18:12:56 -07:00
parent 689fe7be88
commit a33d951abd
27 changed files with 578 additions and 68 deletions

View file

@ -116,13 +116,17 @@ namespace TD.UI
private bool draftSubscribed;
private PlayerDraft subscribedDraft;
// Spell hotbar (bottom-ui Section 6). Frame is display:none while the local player
// Spell hotbar (bottom-ui Section 6). Frame is visibility:hidden (but still occupies
// its reserved width, so the centered command bar never shifts) while the local player
// has no granted spells; rebuilt on grant (append-only, so this is rare). Cooldown
// state changes continuously and has no change event, so it's polled every Update.
private VisualElement spellHotbarFrame;
private VisualElement spellHotbar;
private bool spellLoadoutSubscribed;
private PlayerSpellLoadout subscribedSpellLoadout;
// The loadout the hotbar is currently showing — the SELECTED builder's, or null when
// no builder is selected (hotbar hidden). Cooldown polling reads this.
private PlayerSpellLoadout displayedSpellLoadout;
private readonly List<SpellSlotUi> spellSlotUis = new List<SpellSlotUi>();
private readonly struct SpellSlotUi
@ -419,9 +423,10 @@ namespace TD.UI
title.style.marginBottom = 6;
card.Add(title);
if (option.Icon != null)
var iconSprite = option.ResolveIcon();
if (iconSprite != null)
{
var img = new Image { sprite = option.Icon };
var img = new Image { sprite = iconSprite };
img.style.width = 48;
img.style.height = 48;
img.style.marginBottom = 6;
@ -475,23 +480,41 @@ namespace TD.UI
// ----- Spell hotbar -------------------------------------------------
// Rebuilds the hotbar cells from the local player's current loadout. Called on
// OnLoadoutChanged (a new spell granted) — spells are append-only, so this is rare,
// not a per-frame concern.
// Rebuilds the hotbar cells for the SELECTED builder's loadout. Called on selection
// change and on OnLoadoutChanged (a new spell granted). Spells are append-only, so
// this is rare, not a per-frame concern.
//
// The hotbar follows selection: it shows the selected builder's spells and is removed
// entirely when no builder is selected (spells belong to the builder). While a builder
// IS selected, the frame keeps its reserved width (visibility toggle, not display) so
// granting a spell mid-selection doesn't reflow/shift the centered command bar.
private void RebuildSpellHotbar()
{
if (spellHotbar == null) return;
spellHotbar.Clear();
spellSlotUis.Clear();
var loadout = PlayerSpellLoadout.Local;
int slotCount = loadout?.SlotCount ?? 0;
var loadout = GetSelectedBuilderLoadout();
displayedSpellLoadout = loadout;
if (spellHotbarFrame != null)
spellHotbarFrame.style.display = slotCount > 0 ? DisplayStyle.Flex : DisplayStyle.None;
{
if (loadout == null)
{
// No builder selected → remove the slot (the command grid is hidden too).
spellHotbarFrame.style.display = DisplayStyle.None;
return;
}
// Builder selected → keep the reserved slot in layout; toggle only visibility so
// a mid-selection spell grant doesn't jump the command bar.
spellHotbarFrame.style.display = DisplayStyle.Flex;
spellHotbarFrame.style.visibility =
loadout.SlotCount > 0 ? Visibility.Visible : Visibility.Hidden;
}
if (loadout == null) return;
int slotCount = loadout.SlotCount;
var layout = SpellHotkeys.Layout;
for (int i = 0; i < slotCount; i++)
{
@ -531,6 +554,17 @@ namespace TD.UI
}
}
// The loadout the spell hotbar should display: the currently-selected builder's
// (resolved via its owner, so it shows whichever builder is selected — normally the
// local player's), or null when the selection isn't a builder → hotbar hidden.
private PlayerSpellLoadout GetSelectedBuilderLoadout()
{
var selected = SelectionState.Instance?.SelectedObject;
if (selected is Builder builder)
return PlayerSpellLoadout.GetForClient(builder.OwnerClientId);
return null;
}
// Per-frame: dims each slot while on cooldown and shows the remaining whole seconds.
// Cooldown has no change event (it advances continuously with server time), so this
// has to be polled — cheap at hotbar scale (at most MaxSpellSlots cells).
@ -538,7 +572,7 @@ namespace TD.UI
{
if (spellSlotUis.Count == 0) return;
var loadout = PlayerSpellLoadout.Local;
var loadout = displayedSpellLoadout;
if (loadout == null) return;
for (int i = 0; i < spellSlotUis.Count; i++)
@ -1403,6 +1437,10 @@ namespace TD.UI
// hides via PopulateGridForSelection when there are no actions.
PopulateInfoPanel(selection);
PopulateGridForSelection(selection);
// Section 6 (spell hotbar) follows selection: show the selected builder's spells,
// hide entirely when the selection isn't a builder.
RebuildSpellHotbar();
}
/// <summary>