UnityTowerDefense/Assets/_Project/Scripts/Gameplay/MatchState.cs

138 lines
5.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Assets/_Project/Scripts/Gameplay/MatchState.cs
using Unity.Netcode;
using UnityEngine;
using TD.Core;
namespace TD.Gameplay
{
/// <summary>
/// Singleton NetworkBehaviour that tracks global match data.
/// Lives on a persistent scene object.
///
/// All state is server-authoritative (Server-write NetworkVariables).
/// Clients react to OnValueChanged events on individual fields.
/// Phase transitions (Lobby → CountDown → Playing → Victory/Defeat)
/// and wave/lives logic are wired in Phases 1.41.6 and 1.8.
/// </summary>
public class MatchState : NetworkBehaviour
{
// --- Singleton ---------------------------------------------------
public static MatchState Instance { get; private set; }
// --- Networked state ---------------------------------------------
private readonly NetworkVariable<MatchPhase> phase = new NetworkVariable<MatchPhase>(
value: MatchPhase.Playing,
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server
);
/// <summary>Current match phase. Authoritative on the server, replicated to all clients.</summary>
public MatchPhase Phase => phase.Value;
// STUBBED — written by the wave system (Phase 1.5/1.6). Zero until waves begin.
[Tooltip("STUBBED — not written until the wave system lands (Phase 1.5/1.6).")]
private readonly NetworkVariable<int> currentWave = new NetworkVariable<int>(
value: 0,
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server
);
/// <summary>STUBBED. Current wave index. Zero until the wave system (Phase 1.5/1.6) writes it.</summary>
public int CurrentWave => currentWave.Value;
// STUBBED — written by the combat/leak system (Phase 1.4). Zero until then.
[Tooltip("STUBBED — not written until the combat/leak system lands (Phase 1.4).")]
private readonly NetworkVariable<int> lives = new NetworkVariable<int>(
value: 0,
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server
);
/// <summary>STUBBED. Shared lives pool. Zero until the combat/leak system (Phase 1.4) writes it.</summary>
public int Lives => lives.Value;
// STUBBED — driven by the race-pick UI (Phase 1.8). Zero when no pick is in progress.
[Tooltip("STUBBED — not driven until the race-pick system lands (Phase 1.8).")]
private readonly NetworkVariable<float> racePickTimer = new NetworkVariable<float>(
value: 0f,
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server
);
/// <summary>STUBBED. Seconds remaining in the race-pick countdown. Zero when no pick is in progress.</summary>
public float RacePickTimer => racePickTimer.Value;
/// <summary>
/// Fired on every peer (server and clients) when the phase changes.
/// Subscribe to react to phase transitions without polling.
/// </summary>
public event System.Action<MatchPhase, MatchPhase> OnPhaseChanged;
// --- Lifecycle ---------------------------------------------------
public override void OnNetworkSpawn()
{
if (Instance != null && Instance != this)
{
Debug.LogError("[MatchState] Duplicate MatchState detected — only one may exist per scene. " +
"Despawning the duplicate.");
NetworkObject.Despawn();
return;
}
Instance = this;
phase.OnValueChanged += HandlePhaseChanged;
Debug.Log($"[MatchState] Spawned. Phase={phase.Value}");
}
public override void OnNetworkDespawn()
{
phase.OnValueChanged -= HandlePhaseChanged;
if (Instance == this) Instance = null;
}
// --- Server API --------------------------------------------------
/// <summary>
/// Transitions to a new phase. Server-only; no-op on clients with a log warning.
/// </summary>
public void SetPhase(MatchPhase next)
{
if (!IsServer) { Debug.LogWarning("[MatchState] SetPhase called on a client — ignored."); return; }
if (phase.Value == next) return;
phase.Value = next;
}
/// <summary>STUBBED. Sets the current wave index. Called by the wave system (Phase 1.5/1.6).</summary>
public void SetCurrentWave(int wave)
{
if (!IsServer) { Debug.LogWarning("[MatchState] SetCurrentWave called on a client — ignored."); return; }
currentWave.Value = wave;
}
/// <summary>STUBBED. Sets the shared lives pool. Called by the combat/leak system (Phase 1.4).</summary>
public void SetLives(int value)
{
if (!IsServer) { Debug.LogWarning("[MatchState] SetLives called on a client — ignored."); return; }
lives.Value = Mathf.Max(0, value);
}
/// <summary>STUBBED. Sets the race-pick countdown timer. Called by the race-pick system (Phase 1.8).</summary>
public void SetRacePickTimer(float seconds)
{
if (!IsServer) { Debug.LogWarning("[MatchState] SetRacePickTimer called on a client — ignored."); return; }
racePickTimer.Value = Mathf.Max(0f, seconds);
}
// --- Handlers ----------------------------------------------------
private void HandlePhaseChanged(MatchPhase previous, MatchPhase current)
{
Debug.Log($"[MatchState] Phase: {previous} → {current}");
OnPhaseChanged?.Invoke(previous, current);
}
}
}