chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
663
Assets/Scripts/CashSystem/CashSystemUI.cs
Normal file
663
Assets/Scripts/CashSystem/CashSystemUI.cs
Normal file
@@ -0,0 +1,663 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// CashSystemUI — Event-driven HUD for Cash System mode.
|
||||
/// Subscribes to GameEvents; never polls via FindObjectOfType.
|
||||
/// Includes: timer, vault bars, carried cash, kill feed, combo popup, vault attack warning, countdown.
|
||||
/// </summary>
|
||||
public class CashSystemUI : MonoBehaviour
|
||||
{
|
||||
[Header("Timer Display")]
|
||||
[SerializeField] private TextMeshProUGUI timerText;
|
||||
|
||||
[Header("Vault Displays")]
|
||||
[SerializeField] private TextMeshProUGUI playerVaultText;
|
||||
[SerializeField] private TextMeshProUGUI enemyVaultText;
|
||||
[SerializeField] private Slider playerVaultBar;
|
||||
[SerializeField] private Slider enemyVaultBar;
|
||||
|
||||
[Header("Carried Cash Display")]
|
||||
[SerializeField] private TextMeshProUGUI carriedCashText;
|
||||
[SerializeField] private GameObject carriedCashPanel;
|
||||
|
||||
[Header("Character Switch Indicator")]
|
||||
[SerializeField] private TextMeshProUGUI currentCharacterText;
|
||||
[SerializeField] private Image[] teamCharacterIcons;
|
||||
|
||||
[Header("Steal Progress")]
|
||||
[SerializeField] private GameObject stealProgressPanel;
|
||||
[SerializeField] private Slider stealProgressBar;
|
||||
[SerializeField] private TextMeshProUGUI stealProgressText;
|
||||
|
||||
[Header("Steal Dial (optional)")]
|
||||
[Tooltip("Optional sprite to use for the steal progress radial dial. If left empty the code will try to copy the import UI's dial sprite at runtime.")]
|
||||
[SerializeField] private Sprite stealDialSprite;
|
||||
[Header("Countdown Display")]
|
||||
[SerializeField] private TextMeshProUGUI countdownText;
|
||||
[SerializeField] private GameObject countdownPanel;
|
||||
|
||||
[Header("Kill Feed")]
|
||||
[SerializeField] private TextMeshProUGUI killFeedText;
|
||||
[SerializeField] private int maxFeedEntries = 5;
|
||||
|
||||
[Header("Combo Popup")]
|
||||
[SerializeField] private TextMeshProUGUI comboText;
|
||||
[SerializeField] private GameObject comboPanel;
|
||||
|
||||
[Header("Popup Message")]
|
||||
[SerializeField] private TextMeshProUGUI popupText;
|
||||
[SerializeField] private GameObject popupPanel;
|
||||
|
||||
[Header("Pickup Screen Effect")]
|
||||
[Tooltip("Full-screen flash image (stretch to fill, starts transparent)")]
|
||||
[SerializeField] private Image pickupFlashImage;
|
||||
[Tooltip("Color of the screen flash when picking up cash")]
|
||||
[SerializeField] private Color pickupFlashColor = new Color(1f, 0.84f, 0f, 0.35f); // Gold
|
||||
[Tooltip("How long the flash lasts")]
|
||||
[SerializeField] private float pickupFlashDuration = 0.3f;
|
||||
[Tooltip("Text popup showing collected amount (e.g. '+$100')")]
|
||||
[SerializeField] private TextMeshProUGUI pickupAmountText;
|
||||
[SerializeField] private GameObject pickupAmountPanel;
|
||||
|
||||
[Header("Win Target")]
|
||||
[SerializeField] private float targetCash = 2000f;
|
||||
|
||||
// ─── Runtime ─────────────────────────────────────────────
|
||||
private readonly List<string> _feedEntries = new List<string>();
|
||||
private Coroutine _comboFade;
|
||||
private Coroutine _popupFade;
|
||||
private Coroutine _pickupFlashCoroutine;
|
||||
private float _originalTimerFontSize = -1f;
|
||||
private bool _dirtyVault;
|
||||
|
||||
// Cached score values (set by events, read in LateUpdate for dirty-flag refresh)
|
||||
private float _playerScore, _enemyScore;
|
||||
|
||||
// Cached carrier reference — invalidated on character switch
|
||||
private CashCarrier _cachedCarrier;
|
||||
private GameObject _cachedCarrierCharacter;
|
||||
private bool _stealWasActive;
|
||||
|
||||
// CanvasGroup for intro delay — hidden until intro panel finishes
|
||||
private CanvasGroup _canvasGroup;
|
||||
|
||||
// ─── Lifecycle ───────────────────────────────────────────
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (SelectionOptions.Instance == null || !SelectionOptions.Instance.isCashSystemSelected)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up CanvasGroup so we can hide during intro without disabling the script
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (_canvasGroup == null) _canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
|
||||
// Hide the HUD during the intro panel — CashSystemManager will show it after intro completes
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.interactable = false;
|
||||
|
||||
if (playerVaultBar != null) playerVaultBar.maxValue = targetCash;
|
||||
if (enemyVaultBar != null) enemyVaultBar.maxValue = targetCash;
|
||||
if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
|
||||
ConfigureStealDial();
|
||||
if (comboPanel != null) comboPanel.SetActive(false);
|
||||
if (popupPanel != null) popupPanel.SetActive(false);
|
||||
|
||||
// Explicitly hide carried cash panel and clear text on start
|
||||
HideCarried();
|
||||
if (carriedCashText != null) carriedCashText.text = "";
|
||||
}
|
||||
|
||||
/// <summary>Show the HUD (called by CashSystemManager after intro completes).</summary>
|
||||
public void ShowHUD()
|
||||
{
|
||||
if (_canvasGroup == null) return;
|
||||
_canvasGroup.alpha = 1f;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
_canvasGroup.interactable = true;
|
||||
Debug.Log("[CashSystemUI] HUD shown");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the steal progress dial to match the import UI's dial if possible.
|
||||
/// Tries in this order:
|
||||
/// 1) If `stealDialSprite` is assigned in inspector, use that.
|
||||
/// 2) If an ImportUIPanel exists in the scene (created by CashBoxImportController), copy its ProgressDial sprite.
|
||||
/// 3) If we have a Slider with a fill Rect, apply the sprite as a radial Image.
|
||||
/// </summary>
|
||||
private void ConfigureStealDial()
|
||||
{
|
||||
if (stealProgressBar == null) return;
|
||||
|
||||
Sprite spriteToUse = stealDialSprite;
|
||||
|
||||
// Try to find the runtime ImportUIPanel created by CashBoxImportController
|
||||
if (spriteToUse == null)
|
||||
{
|
||||
var importPanel = GameObject.Find("ImportUIPanel");
|
||||
if (importPanel != null)
|
||||
{
|
||||
var dial = importPanel.transform.Find("ProgressDial");
|
||||
if (dial != null)
|
||||
{
|
||||
var img = dial.GetComponent<Image>();
|
||||
if (img != null) spriteToUse = img.sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spriteToUse == null) return;
|
||||
|
||||
// Apply sprite to the Slider's fill image if available
|
||||
var fillRect = stealProgressBar.fillRect;
|
||||
if (fillRect != null)
|
||||
{
|
||||
var fillImg = fillRect.GetComponent<Image>();
|
||||
if (fillImg != null)
|
||||
{
|
||||
fillImg.sprite = spriteToUse;
|
||||
fillImg.type = Image.Type.Filled;
|
||||
fillImg.fillMethod = Image.FillMethod.Radial360;
|
||||
fillImg.fillOrigin = (int)Image.Origin360.Top;
|
||||
fillImg.fillClockwise = true;
|
||||
fillImg.preserveAspect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// COPY ADDITIONAL VISUAL SETTINGS FROM IMPORT UI
|
||||
var importPanelObj = GameObject.Find("ImportUIPanel");
|
||||
if (importPanelObj != null)
|
||||
{
|
||||
// Copy panel background color if stealProgressPanel has an Image
|
||||
var importImage = importPanelObj.GetComponent<Image>();
|
||||
if (importImage != null && stealProgressPanel != null)
|
||||
{
|
||||
var stealPanelImg = stealProgressPanel.GetComponent<Image>();
|
||||
if (stealPanelImg != null)
|
||||
{
|
||||
stealPanelImg.color = importImage.color;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy font/size/color from Import's AmountText to our stealProgressText
|
||||
var amountTf = importPanelObj.transform.Find("AmountText");
|
||||
if (amountTf != null && stealProgressText != null)
|
||||
{
|
||||
var importAmountTmp = amountTf.GetComponent<TextMeshProUGUI>();
|
||||
if (importAmountTmp != null)
|
||||
{
|
||||
// Copy common TMP settings
|
||||
stealProgressText.font = importAmountTmp.font;
|
||||
stealProgressText.fontSize = importAmountTmp.fontSize;
|
||||
stealProgressText.color = importAmountTmp.color;
|
||||
stealProgressText.fontStyle = importAmountTmp.fontStyle;
|
||||
stealProgressText.enableAutoSizing = importAmountTmp.enableAutoSizing;
|
||||
stealProgressText.outlineWidth = importAmountTmp.outlineWidth;
|
||||
stealProgressText.outlineColor = importAmountTmp.outlineColor;
|
||||
stealProgressText.alignment = importAmountTmp.alignment;
|
||||
}
|
||||
}
|
||||
|
||||
// Also copy countdown/status text style if present (optional)
|
||||
var countdownTf = importPanelObj.transform.Find("CountdownText");
|
||||
if (countdownTf != null && timerText != null)
|
||||
{
|
||||
var importCountdownTmp = countdownTf.GetComponent<TextMeshProUGUI>();
|
||||
if (importCountdownTmp != null)
|
||||
{
|
||||
timerText.font = importCountdownTmp.font;
|
||||
timerText.fontSize = importCountdownTmp.fontSize;
|
||||
timerText.color = importCountdownTmp.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
GameEvents.OnTimerUpdated += OnTimerUpdated;
|
||||
GameEvents.OnScoreChanged += OnScoreChanged;
|
||||
GameEvents.OnBagPickedUp += OnBagPickedUp;
|
||||
GameEvents.OnCashDeposited += OnCashDeposited;
|
||||
GameEvents.OnKillFeedEntry += OnKillFeedEntry;
|
||||
GameEvents.OnComboMultiplier += OnComboMultiplier;
|
||||
GameEvents.OnVaultUnderAttack += OnVaultUnderAttack;
|
||||
GameEvents.OnVaultDefended += OnVaultDefended;
|
||||
GameEvents.OnCountdownTick += OnCountdownTick;
|
||||
GameEvents.OnPopupMessage += OnPopupMessage;
|
||||
GameEvents.OnCharacterSwitched += OnCharacterSwitched;
|
||||
GameEvents.OnCarrierKilled += OnCarrierKilled;
|
||||
GameEvents.OnMatchEnd += OnMatchEnd;
|
||||
GameEvents.OnBagDropped += OnBagDropped;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GameEvents.OnTimerUpdated -= OnTimerUpdated;
|
||||
GameEvents.OnScoreChanged -= OnScoreChanged;
|
||||
GameEvents.OnBagPickedUp -= OnBagPickedUp;
|
||||
GameEvents.OnCashDeposited -= OnCashDeposited;
|
||||
GameEvents.OnKillFeedEntry -= OnKillFeedEntry;
|
||||
GameEvents.OnComboMultiplier -= OnComboMultiplier;
|
||||
GameEvents.OnVaultUnderAttack -= OnVaultUnderAttack;
|
||||
GameEvents.OnVaultDefended -= OnVaultDefended;
|
||||
GameEvents.OnCountdownTick -= OnCountdownTick;
|
||||
GameEvents.OnPopupMessage -= OnPopupMessage;
|
||||
GameEvents.OnCharacterSwitched -= OnCharacterSwitched;
|
||||
GameEvents.OnCarrierKilled -= OnCarrierKilled;
|
||||
GameEvents.OnMatchEnd -= OnMatchEnd;
|
||||
GameEvents.OnBagDropped -= OnBagDropped;
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// Dirty-flag: only refresh vault bars when data changed
|
||||
if (_dirtyVault)
|
||||
{
|
||||
_dirtyVault = false;
|
||||
RefreshVaultBars();
|
||||
}
|
||||
|
||||
// Steal progress: check every frame only while active, otherwise throttle to ~6Hz
|
||||
if (_stealWasActive || Time.frameCount % 10 == 0)
|
||||
{
|
||||
UpdateStealProgress();
|
||||
}
|
||||
|
||||
// Carried cash: event-driven via OnBagPickedUp/OnBagDropped/OnCashDeposited/OnCarrierKilled
|
||||
// Only poll at ~10Hz as a safety net for edge cases
|
||||
if (Time.frameCount % 6 == 0)
|
||||
{
|
||||
RefreshCarriedCash();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Event Handlers ──────────────────────────────────────
|
||||
|
||||
private void OnTimerUpdated(float remaining)
|
||||
{
|
||||
if (timerText == null) return;
|
||||
int m = Mathf.FloorToInt(remaining / 60);
|
||||
int s = Mathf.FloorToInt(remaining % 60);
|
||||
timerText.text = $"{m:00}:{s:00}";
|
||||
timerText.color = remaining <= 30f
|
||||
? Color.Lerp(Color.red, Color.white, Mathf.PingPong(Time.time * 2, 1))
|
||||
: Color.white;
|
||||
}
|
||||
|
||||
private void OnScoreChanged(string teamTag, float total)
|
||||
{
|
||||
if (teamTag == "Player") _playerScore = total;
|
||||
else _enemyScore = total;
|
||||
_dirtyVault = true;
|
||||
}
|
||||
|
||||
private void OnBagPickedUp(CashBag bag, CashCarrier carrier)
|
||||
{
|
||||
// Update carrier display immediately
|
||||
RefreshCarriedCash();
|
||||
|
||||
// Only show screen effects for the player-controlled character
|
||||
CashSystemManager mgr = CashSystemManager.Instance;
|
||||
if (mgr == null) return;
|
||||
GameObject controlled = mgr.CurrentControlledCharacter;
|
||||
if (controlled == null || carrier == null) return;
|
||||
if (carrier.gameObject != controlled) return;
|
||||
|
||||
// Screen flash effect
|
||||
ShowPickupFlash();
|
||||
|
||||
// Floating "+$X" amount text
|
||||
float amount = bag != null ? bag.CashValue : 0f;
|
||||
ShowPickupAmount(amount);
|
||||
}
|
||||
|
||||
private void OnBagDropped(CashBag bag, Vector3 pos)
|
||||
{
|
||||
RefreshCarriedCash();
|
||||
}
|
||||
|
||||
private void OnCashDeposited(string teamTag, float value, int combo)
|
||||
{
|
||||
_dirtyVault = true;
|
||||
RefreshCarriedCash();
|
||||
}
|
||||
|
||||
private void OnKillFeedEntry(string killer, string action, string victim)
|
||||
{
|
||||
string entry = $"<b>{killer}</b> {action} <b>{victim}</b>";
|
||||
_feedEntries.Add(entry);
|
||||
if (_feedEntries.Count > maxFeedEntries)
|
||||
_feedEntries.RemoveAt(0);
|
||||
|
||||
if (killFeedText != null)
|
||||
killFeedText.text = string.Join("\n", _feedEntries);
|
||||
}
|
||||
|
||||
private void OnComboMultiplier(int multiplier)
|
||||
{
|
||||
if (comboText == null) return;
|
||||
comboText.text = $"x{multiplier} COMBO!";
|
||||
if (comboPanel != null) comboPanel.SetActive(true);
|
||||
if (_comboFade != null) StopCoroutine(_comboFade);
|
||||
_comboFade = StartCoroutine(FadePanel(comboPanel, 2f));
|
||||
}
|
||||
|
||||
private void OnVaultUnderAttack(TeamVault vault)
|
||||
{
|
||||
if (vault.TeamTag != "Player") return;
|
||||
if (stealProgressPanel != null) stealProgressPanel.SetActive(true);
|
||||
if (stealProgressText != null) stealProgressText.text = "VAULT UNDER ATTACK!";
|
||||
}
|
||||
|
||||
private void OnVaultDefended(TeamVault vault)
|
||||
{
|
||||
if (vault.TeamTag != "Player") return;
|
||||
if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnCountdownTick(string text)
|
||||
{
|
||||
ShowCountdown(text);
|
||||
}
|
||||
|
||||
private void OnPopupMessage(string message, float duration)
|
||||
{
|
||||
if (popupText != null) popupText.text = message;
|
||||
if (popupPanel != null) popupPanel.SetActive(true);
|
||||
if (_popupFade != null) StopCoroutine(_popupFade);
|
||||
_popupFade = StartCoroutine(FadePanel(popupPanel, duration));
|
||||
}
|
||||
|
||||
private void OnCharacterSwitched(GameObject oldChar, GameObject newChar)
|
||||
{
|
||||
if (newChar == null) return;
|
||||
if (currentCharacterText != null)
|
||||
currentCharacterText.text = newChar.name;
|
||||
|
||||
// Highlight icon
|
||||
if (teamCharacterIcons != null)
|
||||
{
|
||||
CashSystemManager mgr = CashSystemManager.Instance;
|
||||
if (mgr != null)
|
||||
{
|
||||
var team = mgr.GetTeamCharacters("Player");
|
||||
int idx = team.IndexOf(newChar);
|
||||
for (int i = 0; i < teamCharacterIcons.Length; i++)
|
||||
if (teamCharacterIcons[i] != null)
|
||||
teamCharacterIcons[i].color = i == idx ? Color.white : new Color(1, 1, 1, 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCarrierKilled(CashCarrier carrier)
|
||||
{
|
||||
RefreshCarriedCash();
|
||||
}
|
||||
|
||||
private void OnMatchEnd(string winnerTag)
|
||||
{
|
||||
// Could show final score overlay
|
||||
}
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────
|
||||
|
||||
private void RefreshVaultBars()
|
||||
{
|
||||
if (playerVaultText != null) playerVaultText.text = $"${_playerScore:F0}";
|
||||
if (enemyVaultText != null) enemyVaultText.text = $"${_enemyScore:F0}";
|
||||
if (playerVaultBar != null) playerVaultBar.value = _playerScore;
|
||||
if (enemyVaultBar != null) enemyVaultBar.value = _enemyScore;
|
||||
}
|
||||
|
||||
private void RefreshCarriedCash()
|
||||
{
|
||||
CashSystemManager mgr = CashSystemManager.Instance;
|
||||
if (mgr == null) { HideCarried(); return; }
|
||||
GameObject cur = mgr.CurrentControlledCharacter;
|
||||
if (cur == null) { HideCarried(); return; }
|
||||
|
||||
// Cache CashCarrier — only re-fetch when controlled character changes
|
||||
if (_cachedCarrierCharacter != cur)
|
||||
{
|
||||
_cachedCarrierCharacter = cur;
|
||||
_cachedCarrier = cur.GetComponent<CashCarrier>();
|
||||
}
|
||||
|
||||
if (_cachedCarrier == null || _cachedCarrier.CarriedCash < 0.01f) { HideCarried(); return; }
|
||||
|
||||
if (carriedCashText != null) carriedCashText.text = $"Carrying: ${_cachedCarrier.CarriedCash:F0}";
|
||||
if (carriedCashPanel != null) carriedCashPanel.SetActive(true);
|
||||
}
|
||||
|
||||
private void HideCarried()
|
||||
{
|
||||
if (carriedCashPanel != null) carriedCashPanel.SetActive(false);
|
||||
}
|
||||
|
||||
private void UpdateStealProgress()
|
||||
{
|
||||
// Check player vault steal progress (uses EntityRegistry now)
|
||||
var vaults = EntityRegistry<TeamVault>.GetAll();
|
||||
TeamVault playerVault = null;
|
||||
for (int i = 0; i < vaults.Count; i++)
|
||||
if (vaults[i] != null && vaults[i].TeamTag == "Player") { playerVault = vaults[i]; break; }
|
||||
|
||||
if (playerVault != null && playerVault.IsBeingStolen)
|
||||
{
|
||||
_stealWasActive = true;
|
||||
if (stealProgressPanel != null) stealProgressPanel.SetActive(true);
|
||||
if (stealProgressBar != null) stealProgressBar.value = playerVault.StealProgress;
|
||||
}
|
||||
else
|
||||
{
|
||||
_stealWasActive = false;
|
||||
if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FadePanel(GameObject panel, float duration)
|
||||
{
|
||||
yield return new WaitForSeconds(duration);
|
||||
if (panel != null) panel.SetActive(false);
|
||||
}
|
||||
|
||||
// ─── Pickup Screen Effects ───────────────────────────────
|
||||
|
||||
private void ShowPickupFlash()
|
||||
{
|
||||
if (_pickupFlashCoroutine != null) StopCoroutine(_pickupFlashCoroutine);
|
||||
|
||||
// If no flash image assigned, create one at runtime
|
||||
if (pickupFlashImage == null)
|
||||
{
|
||||
CreatePickupFlashImage();
|
||||
}
|
||||
|
||||
if (pickupFlashImage != null)
|
||||
{
|
||||
_pickupFlashCoroutine = StartCoroutine(PickupFlashRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePickupFlashImage()
|
||||
{
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas == null) canvas = FindObjectOfType<Canvas>();
|
||||
if (canvas == null) return;
|
||||
|
||||
GameObject flashObj = new GameObject("PickupFlash");
|
||||
flashObj.transform.SetParent(canvas.transform, false);
|
||||
|
||||
RectTransform rt = flashObj.AddComponent<RectTransform>();
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
|
||||
pickupFlashImage = flashObj.AddComponent<Image>();
|
||||
pickupFlashImage.color = Color.clear;
|
||||
pickupFlashImage.raycastTarget = false;
|
||||
|
||||
// Make sure it renders on top
|
||||
flashObj.transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
private IEnumerator PickupFlashRoutine()
|
||||
{
|
||||
pickupFlashImage.color = pickupFlashColor;
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < pickupFlashDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / pickupFlashDuration;
|
||||
pickupFlashImage.color = Color.Lerp(pickupFlashColor, Color.clear, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
pickupFlashImage.color = Color.clear;
|
||||
}
|
||||
|
||||
private void ShowPickupAmount(float amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
// If no pickup amount panel, create one dynamically
|
||||
if (pickupAmountText == null)
|
||||
{
|
||||
CreatePickupAmountText();
|
||||
}
|
||||
|
||||
if (pickupAmountText != null)
|
||||
{
|
||||
pickupAmountText.text = $"+${amount:F0}";
|
||||
if (pickupAmountPanel != null) pickupAmountPanel.SetActive(true);
|
||||
|
||||
// Auto-hide after a short duration
|
||||
StartCoroutine(FadePickupAmount());
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePickupAmountText()
|
||||
{
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas == null) canvas = FindObjectOfType<Canvas>();
|
||||
if (canvas == null) return;
|
||||
|
||||
pickupAmountPanel = new GameObject("PickupAmountPanel");
|
||||
pickupAmountPanel.transform.SetParent(canvas.transform, false);
|
||||
|
||||
RectTransform panelRect = pickupAmountPanel.AddComponent<RectTransform>();
|
||||
panelRect.anchorMin = new Vector2(0.5f, 0.6f);
|
||||
panelRect.anchorMax = new Vector2(0.5f, 0.6f);
|
||||
panelRect.sizeDelta = new Vector2(700f, 160f);
|
||||
panelRect.anchoredPosition = Vector2.zero;
|
||||
|
||||
GameObject textObj = new GameObject("PickupAmountText");
|
||||
textObj.transform.SetParent(pickupAmountPanel.transform, false);
|
||||
|
||||
RectTransform textRect = textObj.AddComponent<RectTransform>();
|
||||
textRect.anchorMin = Vector2.zero;
|
||||
textRect.anchorMax = Vector2.one;
|
||||
textRect.offsetMin = Vector2.zero;
|
||||
textRect.offsetMax = Vector2.zero;
|
||||
|
||||
pickupAmountText = textObj.AddComponent<TextMeshProUGUI>();
|
||||
pickupAmountText.fontSize = 96;
|
||||
pickupAmountText.alignment = TextAlignmentOptions.Center;
|
||||
pickupAmountText.color = new Color(1f, 0.92f, 0.23f); // Bright gold
|
||||
pickupAmountText.fontStyle = FontStyles.Bold;
|
||||
pickupAmountText.enableAutoSizing = false;
|
||||
|
||||
// Add outline for readability
|
||||
pickupAmountText.outlineWidth = 0.35f;
|
||||
pickupAmountText.outlineColor = new Color32(0, 0, 0, 200);
|
||||
|
||||
pickupAmountPanel.SetActive(false);
|
||||
}
|
||||
|
||||
private IEnumerator FadePickupAmount()
|
||||
{
|
||||
float displayDuration = 1.2f;
|
||||
float fadeDuration = 0.4f;
|
||||
|
||||
if (pickupAmountText == null) yield break;
|
||||
|
||||
// Float upward animation
|
||||
RectTransform rt = pickupAmountPanel.GetComponent<RectTransform>();
|
||||
Vector2 startPos = new Vector2(0f, 0f);
|
||||
Vector2 endPos = new Vector2(0f, 50f);
|
||||
|
||||
Color startColor = pickupAmountText.color;
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < displayDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / displayDuration;
|
||||
|
||||
// Float upward
|
||||
if (rt != null)
|
||||
rt.anchoredPosition = Vector2.Lerp(startPos, endPos, t);
|
||||
|
||||
// Fade out in last portion
|
||||
if (t > 0.6f)
|
||||
{
|
||||
float fadeT = (t - 0.6f) / 0.4f;
|
||||
pickupAmountText.color = new Color(startColor.r, startColor.g, startColor.b, 1f - fadeT);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Reset
|
||||
pickupAmountText.color = startColor;
|
||||
if (rt != null) rt.anchoredPosition = startPos;
|
||||
if (pickupAmountPanel != null) pickupAmountPanel.SetActive(false);
|
||||
}
|
||||
|
||||
// ─── Legacy API (called from other scripts) ──────────────
|
||||
|
||||
public void UpdateTimerDisplay(float timeRemaining) => OnTimerUpdated(timeRemaining);
|
||||
public void UpdateVaultDisplay(TeamVault vault)
|
||||
{
|
||||
if (vault == null) return;
|
||||
if (vault.TeamTag == "Player") _playerScore = vault.StoredCash;
|
||||
else _enemyScore = vault.StoredCash;
|
||||
_dirtyVault = true;
|
||||
}
|
||||
public void UpdateCarriedCashDisplay(CashCarrier carrier) => RefreshCarriedCash();
|
||||
public void UpdateCurrentCharacter(string characterName, int index)
|
||||
{
|
||||
if (currentCharacterText != null) currentCharacterText.text = characterName;
|
||||
}
|
||||
|
||||
public void ShowCountdown(string text)
|
||||
{
|
||||
if (countdownText != null)
|
||||
countdownText.text = text;
|
||||
if (countdownPanel != null)
|
||||
countdownPanel.SetActive(!string.IsNullOrEmpty(text));
|
||||
|
||||
// Fallback: use timer text for countdown
|
||||
if (countdownText == null && timerText != null && !string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (_originalTimerFontSize < 0) _originalTimerFontSize = timerText.fontSize;
|
||||
timerText.text = text;
|
||||
timerText.fontSize = 72;
|
||||
}
|
||||
else if (countdownText == null && timerText != null && string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (_originalTimerFontSize > 0) timerText.fontSize = _originalTimerFontSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user