chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
656
Assets/Scripts/CashSystem/UI/MatchResultUI.cs
Normal file
656
Assets/Scripts/CashSystem/UI/MatchResultUI.cs
Normal file
@@ -0,0 +1,656 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// MatchResultUI — Unified VICTORY / DEFEAT end-of-match panel.
|
||||
/// Works for both Cash System (team 3v3) and Practice (1v1) modes.
|
||||
///
|
||||
/// Stats auto-populate based on mode:
|
||||
/// Cash System → Kills, Cash Deposited, Cash Stolen, Damage
|
||||
/// Practice → Kills, Deaths, Assists, Damage
|
||||
///
|
||||
/// Hierarchy (from scene):
|
||||
/// Stats (root, this script)
|
||||
/// ├─ continue (Button)
|
||||
/// ├─ restart (Button)
|
||||
/// ├─ Title — result header "VICTORY" / "DEFEAT"
|
||||
/// ├─ Title(1) — score text "24 VICTORY 20"
|
||||
/// ├─ Playerstatsui
|
||||
/// │ ├─ PlayerName
|
||||
/// │ ├─ kills
|
||||
/// │ ├─ deaths
|
||||
/// │ ├─ assists
|
||||
/// │ └─ damage
|
||||
/// ├─ Enemystatsui
|
||||
/// │ ├─ EnemyName
|
||||
/// │ ├─ kills
|
||||
/// │ ├─ deaths
|
||||
/// │ ├─ assists
|
||||
/// │ └─ damage
|
||||
/// └─ Victory tab
|
||||
/// </summary>
|
||||
public class MatchResultUI : MonoBehaviour
|
||||
{
|
||||
[Header("Panel Root")]
|
||||
[Tooltip("The root panel that gets shown/hidden. If null, uses this.gameObject.")]
|
||||
[SerializeField] private GameObject rootPanel;
|
||||
|
||||
[Header("Header")]
|
||||
[SerializeField] private TextMeshProUGUI resultHeaderText; // "VICTORY" / "DEFEAT"
|
||||
[SerializeField] private TextMeshProUGUI scoreText; // "24 VICTORY 20" style
|
||||
|
||||
[Header("Team Names")]
|
||||
[SerializeField] private TextMeshProUGUI playerTeamLabel; // e.g. "CHEETAH WIN!"
|
||||
[SerializeField] private TextMeshProUGUI enemyTeamLabel; // e.g. "RABBIT LOSE!"
|
||||
|
||||
[Header("Player Stats Texts")]
|
||||
[Tooltip("Player/team name text inside Playerstatsui")]
|
||||
[SerializeField] private TextMeshProUGUI playerNameText;
|
||||
[Tooltip("Stat row 1 — Kills (both modes)")]
|
||||
[SerializeField] private TextMeshProUGUI playerKillsText;
|
||||
[Tooltip("Stat row 2 — Deaths (practice) / Cash Deposited (cash system)")]
|
||||
[SerializeField] private TextMeshProUGUI playerDeathsText;
|
||||
[Tooltip("Stat row 3 — Assists (practice) / Cash Stolen (cash system)")]
|
||||
[SerializeField] private TextMeshProUGUI playerAssistsText;
|
||||
[Tooltip("Stat row 4 — Damage (both modes)")]
|
||||
[SerializeField] private TextMeshProUGUI playerDamageText;
|
||||
|
||||
[Header("Enemy Stats Texts")]
|
||||
[Tooltip("Enemy/team name text inside Enemystatsui")]
|
||||
[SerializeField] private TextMeshProUGUI enemyNameText;
|
||||
[SerializeField] private TextMeshProUGUI enemyKillsText;
|
||||
[SerializeField] private TextMeshProUGUI enemyDeathsText;
|
||||
[SerializeField] private TextMeshProUGUI enemyAssistsText;
|
||||
[SerializeField] private TextMeshProUGUI enemyDamageText;
|
||||
|
||||
[Header("Buttons")]
|
||||
[SerializeField] private Button continueButton;
|
||||
[SerializeField] private Button restartButton;
|
||||
|
||||
[Header("Colors")]
|
||||
[SerializeField] private Color victoryColor = new Color(1f, 0.84f, 0f); // Gold
|
||||
[SerializeField] private Color defeatColor = new Color(0.96f, 0.26f, 0.21f); // Red
|
||||
[SerializeField] private Color panelBgColor = new Color(0.08f, 0.12f, 0.18f, 0.92f);
|
||||
|
||||
[Header("Audio")]
|
||||
[SerializeField] private AudioClip victorySFX;
|
||||
[SerializeField] private AudioClip defeatSFX;
|
||||
[SerializeField] private AudioClip buttonClickSFX;
|
||||
[SerializeField] private float sfxVolume = 1f;
|
||||
|
||||
[Header("Animation")]
|
||||
[SerializeField] private float revealDuration = 0.6f;
|
||||
|
||||
// runtime
|
||||
private AudioSource _audio;
|
||||
private CanvasGroup _canvasGroup;
|
||||
private float _savedFixedDeltaTime;
|
||||
|
||||
private static MatchResultUI _instance;
|
||||
public static MatchResultUI Instance => _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a MatchResultUI instance exists in the scene.
|
||||
/// Finds inactive instances first, or creates a minimal one with buttons on the existing Canvas.
|
||||
/// Call from CashSystemManager.Start() so end-of-match always shows a panel.
|
||||
/// </summary>
|
||||
public static void EnsureExists()
|
||||
{
|
||||
if (_instance != null) return;
|
||||
|
||||
// 1. Try to find an inactive instance already in the scene
|
||||
MatchResultUI[] all = Resources.FindObjectsOfTypeAll<MatchResultUI>();
|
||||
foreach (var m in all)
|
||||
{
|
||||
if (m.gameObject.scene.name == null) continue;
|
||||
// Activate parents top-down first, then the object itself
|
||||
List<GameObject> chain = new List<GameObject>();
|
||||
Transform t = m.transform;
|
||||
while (t != null)
|
||||
{
|
||||
if (!t.gameObject.activeSelf)
|
||||
chain.Add(t.gameObject);
|
||||
t = t.parent;
|
||||
}
|
||||
for (int i = chain.Count - 1; i >= 0; i--)
|
||||
chain[i].SetActive(true);
|
||||
|
||||
if (_instance != null)
|
||||
{
|
||||
DevLog.Log("[MatchResultUI] Found existing inactive panel, activated it");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Create a minimal panel on an existing Canvas
|
||||
Canvas canvas = Object.FindObjectOfType<Canvas>();
|
||||
if (canvas == null)
|
||||
{
|
||||
GameObject canvasObj = new GameObject("UICanvas");
|
||||
canvas = canvasObj.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 100;
|
||||
canvasObj.AddComponent<UnityEngine.UI.CanvasScaler>();
|
||||
canvasObj.AddComponent<UnityEngine.UI.GraphicRaycaster>();
|
||||
}
|
||||
|
||||
GameObject panelRoot = new GameObject("MatchResultUI_Auto");
|
||||
panelRoot.transform.SetParent(canvas.transform, false);
|
||||
|
||||
RectTransform rt = panelRoot.AddComponent<RectTransform>();
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
|
||||
// Background
|
||||
Image bg = panelRoot.AddComponent<Image>();
|
||||
bg.color = new Color(0.08f, 0.12f, 0.18f, 0.92f);
|
||||
|
||||
// Result header
|
||||
GameObject headerObj = new GameObject("ResultHeader");
|
||||
headerObj.transform.SetParent(panelRoot.transform, false);
|
||||
RectTransform hrt = headerObj.AddComponent<RectTransform>();
|
||||
hrt.anchorMin = new Vector2(0.2f, 0.7f);
|
||||
hrt.anchorMax = new Vector2(0.8f, 0.9f);
|
||||
hrt.offsetMin = Vector2.zero;
|
||||
hrt.offsetMax = Vector2.zero;
|
||||
TextMeshProUGUI headerTxt = headerObj.AddComponent<TextMeshProUGUI>();
|
||||
headerTxt.text = "MATCH RESULT";
|
||||
headerTxt.fontSize = 80;
|
||||
headerTxt.alignment = TextAlignmentOptions.Center;
|
||||
headerTxt.color = Color.white;
|
||||
|
||||
// Score text
|
||||
GameObject scoreObj = new GameObject("ScoreText");
|
||||
scoreObj.transform.SetParent(panelRoot.transform, false);
|
||||
RectTransform srt = scoreObj.AddComponent<RectTransform>();
|
||||
srt.anchorMin = new Vector2(0.2f, 0.55f);
|
||||
srt.anchorMax = new Vector2(0.8f, 0.7f);
|
||||
srt.offsetMin = Vector2.zero;
|
||||
srt.offsetMax = Vector2.zero;
|
||||
TextMeshProUGUI scoreTxt = scoreObj.AddComponent<TextMeshProUGUI>();
|
||||
scoreTxt.text = "";
|
||||
scoreTxt.fontSize = 48;
|
||||
scoreTxt.alignment = TextAlignmentOptions.Center;
|
||||
scoreTxt.color = Color.white;
|
||||
|
||||
// Continue button
|
||||
Button continueBtn = CreateAutoButton(panelRoot.transform, "Continue",
|
||||
new Vector2(0.25f, 0.1f), new Vector2(0.48f, 0.22f), new Color(0.2f, 0.7f, 0.3f));
|
||||
|
||||
// Restart button
|
||||
Button restartBtn = CreateAutoButton(panelRoot.transform, "Restart",
|
||||
new Vector2(0.52f, 0.1f), new Vector2(0.75f, 0.22f), new Color(0.8f, 0.4f, 0.1f));
|
||||
|
||||
MatchResultUI ui = panelRoot.AddComponent<MatchResultUI>();
|
||||
ui.resultHeaderText = headerTxt;
|
||||
ui.scoreText = scoreTxt;
|
||||
ui.continueButton = continueBtn;
|
||||
ui.restartButton = restartBtn;
|
||||
|
||||
DevLog.Log("[MatchResultUI] Auto-created minimal result panel on Canvas");
|
||||
}
|
||||
|
||||
private static Button CreateAutoButton(Transform parent, string label,
|
||||
Vector2 anchorMin, Vector2 anchorMax, Color bgColor)
|
||||
{
|
||||
GameObject btnObj = new GameObject(label + "Button");
|
||||
btnObj.transform.SetParent(parent, false);
|
||||
RectTransform brt = btnObj.AddComponent<RectTransform>();
|
||||
brt.anchorMin = anchorMin;
|
||||
brt.anchorMax = anchorMax;
|
||||
brt.offsetMin = Vector2.zero;
|
||||
brt.offsetMax = Vector2.zero;
|
||||
Image btnBg = btnObj.AddComponent<Image>();
|
||||
btnBg.color = bgColor;
|
||||
Button btn = btnObj.AddComponent<Button>();
|
||||
|
||||
GameObject btnLabelObj = new GameObject("Label");
|
||||
btnLabelObj.transform.SetParent(btnObj.transform, false);
|
||||
RectTransform lrt = btnLabelObj.AddComponent<RectTransform>();
|
||||
lrt.anchorMin = Vector2.zero;
|
||||
lrt.anchorMax = Vector2.one;
|
||||
lrt.offsetMin = Vector2.zero;
|
||||
lrt.offsetMax = Vector2.zero;
|
||||
TextMeshProUGUI txt = btnLabelObj.AddComponent<TextMeshProUGUI>();
|
||||
txt.text = label;
|
||||
txt.fontSize = 42;
|
||||
txt.alignment = TextAlignmentOptions.Center;
|
||||
txt.color = Color.white;
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
// ─── Lifecycle ───────────────────────────────────────────
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_instance = this;
|
||||
if (rootPanel == null) rootPanel = gameObject;
|
||||
|
||||
_audio = GetComponent<AudioSource>();
|
||||
if (_audio == null) _audio = gameObject.AddComponent<AudioSource>();
|
||||
_audio.playOnAwake = false;
|
||||
_audio.spatialBlend = 0f;
|
||||
|
||||
_canvasGroup = rootPanel.GetComponent<CanvasGroup>();
|
||||
if (_canvasGroup == null) _canvasGroup = rootPanel.AddComponent<CanvasGroup>();
|
||||
|
||||
// Ensure MatchStatTracker exists to record stats from match start
|
||||
if (MatchStatTracker.Instance == null)
|
||||
{
|
||||
GameObject trackerObj = new GameObject("MatchStatTracker");
|
||||
trackerObj.AddComponent<MatchStatTracker>();
|
||||
}
|
||||
|
||||
// IMPORTANT: Do NOT use SetActive(false) — that disables the MonoBehaviour
|
||||
// and unsubscribes from GameEvents.OnMatchResult.
|
||||
// Instead hide via CanvasGroup so the script stays alive.
|
||||
HideImmediate();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
GameEvents.OnMatchResult += OnMatchResult;
|
||||
|
||||
// Wire button listeners
|
||||
WireButtonListeners();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GameEvents.OnMatchResult -= OnMatchResult;
|
||||
|
||||
if (continueButton != null) continueButton.onClick.RemoveListener(OnContinueClicked);
|
||||
if (restartButton != null) restartButton.onClick.RemoveListener(OnRestartClicked);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Late-wire buttons — handles auto-created panels where buttons
|
||||
/// are assigned after Awake/OnEnable.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
WireButtonListeners();
|
||||
}
|
||||
|
||||
private void WireButtonListeners()
|
||||
{
|
||||
if (continueButton != null)
|
||||
{
|
||||
continueButton.onClick.RemoveListener(OnContinueClicked);
|
||||
continueButton.onClick.AddListener(OnContinueClicked);
|
||||
}
|
||||
if (restartButton != null)
|
||||
{
|
||||
restartButton.onClick.RemoveListener(OnRestartClicked);
|
||||
restartButton.onClick.AddListener(OnRestartClicked);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Event ───────────────────────────────────────────────
|
||||
|
||||
private void OnMatchResult(bool playerWon)
|
||||
{
|
||||
Show(playerWon);
|
||||
}
|
||||
|
||||
// ─── Public API ──────────────────────────────────────────
|
||||
|
||||
/// <summary>Show the result screen. Called automatically via GameEvents or manually.</summary>
|
||||
public void Show(bool playerWon)
|
||||
{
|
||||
DevLog.Log($"[MatchResultUI] Show(playerWon={playerWon})");
|
||||
rootPanel.SetActive(true);
|
||||
// Don't set alpha here — RevealRoutine fades from 0 to 1
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.interactable = false;
|
||||
|
||||
// Pause game so combat freezes behind the result panel
|
||||
_savedFixedDeltaTime = Time.fixedDeltaTime;
|
||||
Time.timeScale = 0f;
|
||||
// NOTE: Do NOT set fixedDeltaTime to 0 — timeScale=0 already pauses FixedUpdate.
|
||||
// Setting fixedDeltaTime=0 caused an infinite loop / hard freeze.
|
||||
|
||||
bool isCashSystem = SelectionOptions.Instance != null && SelectionOptions.Instance.isCashSystemSelected;
|
||||
|
||||
// ── Header ──
|
||||
if (resultHeaderText != null)
|
||||
{
|
||||
resultHeaderText.text = playerWon ? "VICTORY" : "DEFEAT";
|
||||
resultHeaderText.color = playerWon ? victoryColor : defeatColor;
|
||||
}
|
||||
|
||||
// ── Scores ──
|
||||
if (isCashSystem)
|
||||
PopulateCashSystemScores(playerWon);
|
||||
else
|
||||
PopulatePracticeScores(playerWon);
|
||||
|
||||
// ── Team labels ──
|
||||
PopulateTeamLabels(playerWon, isCashSystem);
|
||||
|
||||
// ── Stats ──
|
||||
if (isCashSystem)
|
||||
PopulateCashSystemStats();
|
||||
else
|
||||
PopulatePracticeStats();
|
||||
|
||||
// ── Sound ──
|
||||
AudioClip clip = playerWon ? victorySFX : defeatSFX;
|
||||
if (clip != null) _audio.PlayOneShot(clip, sfxVolume);
|
||||
|
||||
// ── Animate ──
|
||||
StartCoroutine(RevealRoutine());
|
||||
}
|
||||
|
||||
// ─── Cash System Stats ───────────────────────────────────
|
||||
|
||||
private void PopulateCashSystemScores(bool playerWon)
|
||||
{
|
||||
CashSystemManager mgr = CashSystemManager.Instance;
|
||||
float playerScore = 0f, enemyScore = 0f;
|
||||
if (mgr != null)
|
||||
{
|
||||
var pv = mgr.GetVault("Player");
|
||||
var ev = mgr.GetVault("Enemy");
|
||||
if (pv != null) playerScore = pv.StoredCash;
|
||||
if (ev != null) enemyScore = ev.StoredCash;
|
||||
}
|
||||
|
||||
if (scoreText != null)
|
||||
{
|
||||
string resultWord = playerWon ? "VICTORY" : "DEFEAT";
|
||||
scoreText.text = $"{playerScore:F0} {resultWord} {enemyScore:F0}";
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateCashSystemStats()
|
||||
{
|
||||
CashSystemManager mgr = CashSystemManager.Instance;
|
||||
if (mgr == null) { ClearStats(true); return; }
|
||||
|
||||
MatchStatTracker tracker = MatchStatTracker.Instance;
|
||||
|
||||
var playerChars = mgr.GetTeamCharacters("Player");
|
||||
var enemyChars = mgr.GetTeamCharacters("Enemy");
|
||||
|
||||
// Get vault totals for the score header
|
||||
var pv = mgr.GetVault("Player");
|
||||
var ev = mgr.GetVault("Enemy");
|
||||
float playerVaultTotal = pv != null ? pv.StoredCash : 0f;
|
||||
float enemyVaultTotal = ev != null ? ev.StoredCash : 0f;
|
||||
|
||||
int pKills = 0;
|
||||
float pDeposited = 0f, pStolen = 0f, pCollected = 0f;
|
||||
int eKills = 0;
|
||||
float eDeposited = 0f, eStolen = 0f, eCollected = 0f;
|
||||
|
||||
if (tracker != null)
|
||||
{
|
||||
foreach (var s in tracker.GetTeamStats(playerChars))
|
||||
{
|
||||
pKills += s.kills;
|
||||
pDeposited += s.cashDeposited;
|
||||
pStolen += s.cashStolen;
|
||||
pCollected += s.cashCollected;
|
||||
}
|
||||
foreach (var s in tracker.GetTeamStats(enemyChars))
|
||||
{
|
||||
eKills += s.kills;
|
||||
eDeposited += s.cashDeposited;
|
||||
eStolen += s.cashStolen;
|
||||
eCollected += s.cashCollected;
|
||||
}
|
||||
}
|
||||
|
||||
// Player side — Cash System stats (4 text slots used smartly for cash data)
|
||||
SetStatText(playerNameText, "PLAYER TEAM");
|
||||
SetStatText(playerKillsText, $"Vault Total: ${playerVaultTotal:F0}");
|
||||
SetStatText(playerDeathsText, $"Cash Deposited: ${pDeposited:F0}");
|
||||
SetStatText(playerAssistsText, $"Cash Stolen: ${pStolen:F0}");
|
||||
SetStatText(playerDamageText, $"Eliminations: {pKills}");
|
||||
|
||||
// Enemy side
|
||||
SetStatText(enemyNameText, "ENEMY TEAM");
|
||||
SetStatText(enemyKillsText, $"Vault Total: ${enemyVaultTotal:F0}");
|
||||
SetStatText(enemyDeathsText, $"Cash Deposited: ${eDeposited:F0}");
|
||||
SetStatText(enemyAssistsText, $"Cash Stolen: ${eStolen:F0}");
|
||||
SetStatText(enemyDamageText, $"Eliminations: {eKills}");
|
||||
}
|
||||
|
||||
// ─── Practice / 1v1 Stats ────────────────────────────────
|
||||
|
||||
private void PopulatePracticeScores(bool playerWon)
|
||||
{
|
||||
if (scoreText != null)
|
||||
{
|
||||
scoreText.text = playerWon ? "VICTORY" : "DEFEAT";
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulatePracticeStats()
|
||||
{
|
||||
MatchStatTracker tracker = MatchStatTracker.Instance;
|
||||
|
||||
// In practice mode: find Player and Enemy tagged objects
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
GameObject enemyObj = GameObject.FindGameObjectWithTag("Enemy");
|
||||
|
||||
int pKills = 0, pDeaths = 0, pAssists = 0;
|
||||
float pDamage = 0f;
|
||||
int eKills = 0, eDeaths = 0, eAssists = 0;
|
||||
float eDamage = 0f;
|
||||
|
||||
if (tracker != null)
|
||||
{
|
||||
if (playerObj != null)
|
||||
{
|
||||
var s = tracker.GetStats(playerObj);
|
||||
pKills = s.kills;
|
||||
pDeaths = s.deaths;
|
||||
pAssists = s.assists;
|
||||
pDamage = s.damageDealt;
|
||||
}
|
||||
if (enemyObj != null)
|
||||
{
|
||||
var s = tracker.GetStats(enemyObj);
|
||||
eKills = s.kills;
|
||||
eDeaths = s.deaths;
|
||||
eAssists = s.assists;
|
||||
eDamage = s.damageDealt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback from HealthNew
|
||||
if (playerObj != null) FallbackSingle(playerObj, out pKills, out pDeaths, out pDamage);
|
||||
if (enemyObj != null) FallbackSingle(enemyObj, out eKills, out eDeaths, out eDamage);
|
||||
}
|
||||
|
||||
// Get actual character display names for practice mode
|
||||
string playerName = GetCharacterDisplayName(playerObj, "PLAYER");
|
||||
string enemyName = GetCharacterDisplayName(enemyObj, "ENEMY");
|
||||
|
||||
// Player side — practice labels (K/D/A + Damage)
|
||||
SetStatText(playerNameText, playerName);
|
||||
SetStatText(playerKillsText, $"Kills: {pKills}");
|
||||
SetStatText(playerDeathsText, $"Deaths: {pDeaths}");
|
||||
SetStatText(playerAssistsText, $"Assists: {pAssists}");
|
||||
SetStatText(playerDamageText, $"Damage: {pDamage:F0}");
|
||||
|
||||
// Enemy side
|
||||
SetStatText(enemyNameText, enemyName);
|
||||
SetStatText(enemyKillsText, $"Kills: {eKills}");
|
||||
SetStatText(enemyDeathsText, $"Deaths: {eDeaths}");
|
||||
SetStatText(enemyAssistsText, $"Assists: {eAssists}");
|
||||
SetStatText(enemyDamageText, $"Damage: {eDamage:F0}");
|
||||
}
|
||||
|
||||
// ─── Common Helpers ──────────────────────────────────────
|
||||
|
||||
private void PopulateTeamLabels(bool playerWon, bool isCashSystem)
|
||||
{
|
||||
if (isCashSystem)
|
||||
{
|
||||
if (playerTeamLabel != null)
|
||||
playerTeamLabel.text = playerWon ? "PLAYER TEAM WIN!" : "PLAYER TEAM LOSE!";
|
||||
if (enemyTeamLabel != null)
|
||||
enemyTeamLabel.text = playerWon ? "ENEMY TEAM LOSE!" : "ENEMY TEAM WIN!";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Practice mode — use actual character names
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
GameObject enemyObj = GameObject.FindGameObjectWithTag("Enemy");
|
||||
string pName = GetCharacterDisplayName(playerObj, "YOU");
|
||||
string eName = GetCharacterDisplayName(enemyObj, "ENEMY");
|
||||
|
||||
if (playerTeamLabel != null)
|
||||
playerTeamLabel.text = playerWon ? $"{pName} WIN!" : $"{pName} LOSE!";
|
||||
if (enemyTeamLabel != null)
|
||||
enemyTeamLabel.text = playerWon ? $"{eName} LOSE!" : $"{eName} WIN!";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the display name for a character GameObject (e.g. "WINDHAM").</summary>
|
||||
private string GetCharacterDisplayName(GameObject character, string fallback)
|
||||
{
|
||||
if (character == null) return fallback;
|
||||
if (CharacterPrefabManager.Instance != null)
|
||||
{
|
||||
string name = CharacterPrefabManager.Instance.GetCharacterDisplayName(character);
|
||||
if (!string.IsNullOrEmpty(name)) return name.ToUpper();
|
||||
}
|
||||
return character.name.Replace("(Clone)", "").Trim().ToUpper();
|
||||
}
|
||||
|
||||
private void SetStatText(TextMeshProUGUI tmp, string value)
|
||||
{
|
||||
if (tmp != null) tmp.text = value;
|
||||
}
|
||||
|
||||
private void ClearStats(bool isCashSystem = false)
|
||||
{
|
||||
if (isCashSystem)
|
||||
{
|
||||
SetStatText(playerNameText, "PLAYER TEAM");
|
||||
SetStatText(playerKillsText, "Vault Total: $0");
|
||||
SetStatText(playerDeathsText, "Cash Deposited: $0");
|
||||
SetStatText(playerAssistsText, "Cash Stolen: $0");
|
||||
SetStatText(playerDamageText, "Eliminations: 0");
|
||||
SetStatText(enemyNameText, "ENEMY TEAM");
|
||||
SetStatText(enemyKillsText, "Vault Total: $0");
|
||||
SetStatText(enemyDeathsText, "Cash Deposited: $0");
|
||||
SetStatText(enemyAssistsText, "Cash Stolen: $0");
|
||||
SetStatText(enemyDamageText, "Eliminations: 0");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStatText(playerNameText, "PLAYER");
|
||||
SetStatText(playerKillsText, "Kills: 0");
|
||||
SetStatText(playerDeathsText, "Deaths: 0");
|
||||
SetStatText(playerAssistsText, "Assists: 0");
|
||||
SetStatText(playerDamageText, "Damage: 0");
|
||||
SetStatText(enemyNameText, "ENEMY");
|
||||
SetStatText(enemyKillsText, "Kills: 0");
|
||||
SetStatText(enemyDeathsText, "Deaths: 0");
|
||||
SetStatText(enemyAssistsText, "Assists: 0");
|
||||
SetStatText(enemyDamageText, "Damage: 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fallback stats from HealthNew for a team list.</summary>
|
||||
private void FallbackStats(List<GameObject> team, out int kills, out int deaths, out float damage)
|
||||
{
|
||||
kills = 0; deaths = 0; damage = 0f;
|
||||
if (team == null) return;
|
||||
foreach (var c in team)
|
||||
{
|
||||
if (c == null) continue;
|
||||
var h = c.GetComponent<HealthNew>();
|
||||
if (h == null) continue;
|
||||
if (h.isDead) deaths++;
|
||||
damage += Mathf.Max(0, h.maxHealth - h.currentHealth);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fallback stats from HealthNew for a single character.</summary>
|
||||
private void FallbackSingle(GameObject character, out int kills, out int deaths, out float damage)
|
||||
{
|
||||
kills = 0; deaths = 0; damage = 0f;
|
||||
if (character == null) return;
|
||||
var h = character.GetComponent<HealthNew>();
|
||||
if (h == null) return;
|
||||
if (h.isDead) deaths = 1;
|
||||
damage = Mathf.Max(0, h.maxHealth - h.currentHealth);
|
||||
}
|
||||
|
||||
/// <summary>Hide panel via CanvasGroup — keeps MonoBehaviour alive.</summary>
|
||||
private void HideImmediate()
|
||||
{
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.interactable = false;
|
||||
}
|
||||
|
||||
// ─── Animation ───────────────────────────────────────────
|
||||
|
||||
private IEnumerator RevealRoutine()
|
||||
{
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
_canvasGroup.interactable = true;
|
||||
float elapsed = 0f;
|
||||
while (elapsed < revealDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
_canvasGroup.alpha = Mathf.Clamp01(elapsed / revealDuration);
|
||||
yield return null;
|
||||
}
|
||||
_canvasGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
// ─── Button Callbacks ────────────────────────────────────
|
||||
|
||||
private void OnContinueClicked()
|
||||
{
|
||||
PlayButtonClick();
|
||||
|
||||
// Restore time before scene transition
|
||||
Time.timeScale = 1f;
|
||||
Time.fixedDeltaTime = _savedFixedDeltaTime;
|
||||
GameEvents.ClearAll();
|
||||
|
||||
GameStatsManager statsManager = FindObjectOfType<GameStatsManager>();
|
||||
if (statsManager != null)
|
||||
statsManager.match_end();
|
||||
else
|
||||
UnityEngine.SceneManagement.SceneManager.LoadScene("MenuScene");
|
||||
}
|
||||
|
||||
private void OnRestartClicked()
|
||||
{
|
||||
PlayButtonClick();
|
||||
|
||||
// Restore time before scene transition
|
||||
Time.timeScale = 1f;
|
||||
Time.fixedDeltaTime = _savedFixedDeltaTime;
|
||||
GameEvents.ClearAll();
|
||||
|
||||
// Mode flags are preserved on SelectionOptions (DontDestroyOnLoad)
|
||||
UnityEngine.SceneManagement.SceneManager.LoadScene(
|
||||
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
|
||||
}
|
||||
|
||||
private void PlayButtonClick()
|
||||
{
|
||||
if (buttonClickSFX != null && _audio != null)
|
||||
_audio.PlayOneShot(buttonClickSFX, sfxVolume);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user