(Feat) Add build cost
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Ashwild.Building;
|
||||
using Ashwild.Interaction;
|
||||
using Ashwild.Player;
|
||||
|
||||
@@ -39,6 +41,18 @@ namespace Ashwild.UI
|
||||
/// </summary>
|
||||
[SerializeField] private TMP_Text promptLabel;
|
||||
|
||||
[Header("Build Cost")]
|
||||
/// <summary>
|
||||
/// Container the per-resource cost entries are parented under while a build is being placed.
|
||||
/// Its own layout (e.g. a HorizontalLayoutGroup) arranges them.
|
||||
/// </summary>
|
||||
[SerializeField] private RectTransform costHolder;
|
||||
|
||||
/// <summary>
|
||||
/// Prefab instantiated once per cost line (icon + amount) into the holder. Pooled and reused.
|
||||
/// </summary>
|
||||
[SerializeField] private BuildCostEntryUI costEntryPrefab;
|
||||
|
||||
[Header("States")]
|
||||
/// <summary>
|
||||
/// Look used when nothing is hovered (the plain dot).
|
||||
@@ -101,6 +115,18 @@ namespace Ashwild.UI
|
||||
/// </summary>
|
||||
private string currentPrompt;
|
||||
|
||||
/// <summary>
|
||||
/// True while a build's cost is displayed: the prompt label is suppressed and the holder takes
|
||||
/// over, so a hovered interactable's prompt never fights the cost readout mid-placement.
|
||||
/// </summary>
|
||||
private bool isShowingCost;
|
||||
|
||||
/// <summary>
|
||||
/// The instantiated cost entries, pooled and reused across placements — activated up to the
|
||||
/// current cost's line count and hidden beyond it.
|
||||
/// </summary>
|
||||
private readonly List<BuildCostEntryUI> costEntries = new List<BuildCostEntryUI>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
@@ -126,6 +152,7 @@ namespace Ashwild.UI
|
||||
private void OnEnable()
|
||||
{
|
||||
PlayerEvents.InteractableHoverChanged += HandleHoverChanged;
|
||||
PlayerEvents.BuildCostChanged += HandleBuildCostChanged;
|
||||
ApplyState(idleState, isHover: false, animated: false);
|
||||
SetLabel(null, animated: false);
|
||||
}
|
||||
@@ -136,6 +163,7 @@ namespace Ashwild.UI
|
||||
private void OnDisable()
|
||||
{
|
||||
PlayerEvents.InteractableHoverChanged -= HandleHoverChanged;
|
||||
PlayerEvents.BuildCostChanged -= HandleBuildCostChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -151,7 +179,7 @@ namespace Ashwild.UI
|
||||
if (prompt != currentPrompt)
|
||||
{
|
||||
currentPrompt = prompt;
|
||||
SetLabel(prompt, animated: true);
|
||||
if (!isShowingCost) SetLabel(prompt, animated: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +208,24 @@ namespace Ashwild.UI
|
||||
currentPrompt = hovering ? target.InteractionPrompt : null;
|
||||
|
||||
ApplyState(hovering ? hoverState : idleState, hovering, animated: true);
|
||||
SetLabel(currentPrompt, animated: true);
|
||||
if (!isShowingCost) SetLabel(currentPrompt, animated: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the active build's cost (hiding the prompt label and filling the holder) while placing,
|
||||
/// or clears it and restores the normal prompt when the list is null/empty (placement ended).
|
||||
/// </summary>
|
||||
private void HandleBuildCostChanged(IReadOnlyList<BuildCostView> cost)
|
||||
{
|
||||
if (cost == null || cost.Count == 0)
|
||||
{
|
||||
ClearCost();
|
||||
return;
|
||||
}
|
||||
|
||||
isShowingCost = true;
|
||||
SetLabel(null, animated: true);
|
||||
PopulateCost(cost);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -243,6 +288,55 @@ namespace Ashwild.UI
|
||||
a => promptLabel.alpha = a, target, labelFadeDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the holder with one entry per cost line, reusing pooled entries and only instantiating
|
||||
/// when the current build needs more lines than any previous one. Entries beyond this build's
|
||||
/// count are hidden. Logs and no-ops when the holder or prefab reference is missing.
|
||||
/// </summary>
|
||||
private void PopulateCost(IReadOnlyList<BuildCostView> cost)
|
||||
{
|
||||
if (costHolder == null || costEntryPrefab == null)
|
||||
{
|
||||
Debug.LogError("[CrosshairManager] Cost holder or entry prefab not assigned — cannot show the build cost.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cost.Count; i++)
|
||||
{
|
||||
BuildCostEntryUI entry;
|
||||
if (i < costEntries.Count)
|
||||
{
|
||||
entry = costEntries[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = Instantiate(costEntryPrefab, costHolder);
|
||||
costEntries.Add(entry);
|
||||
}
|
||||
|
||||
entry.gameObject.SetActive(true);
|
||||
entry.Set(cost[i].Icon, cost[i].Owned, cost[i].Amount, cost[i].Affordable);
|
||||
}
|
||||
|
||||
for (int i = cost.Count; i < costEntries.Count; i++)
|
||||
if (costEntries[i] != null) costEntries[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides every cost entry and restores the prompt for whatever is currently hovered. No-op when
|
||||
/// no cost is being shown.
|
||||
/// </summary>
|
||||
private void ClearCost()
|
||||
{
|
||||
if (!isShowingCost) return;
|
||||
isShowingCost = false;
|
||||
|
||||
for (int i = 0; i < costEntries.Count; i++)
|
||||
if (costEntries[i] != null) costEntries[i].gameObject.SetActive(false);
|
||||
|
||||
SetLabel(currentPrompt, animated: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills every cached tween so none survive a disable/destroy.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user