// Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
namespace TD.Gameplay
{
///
/// Per-player set of unlocked tower upgrade nodes — the tower types this player is
/// allowed to convert an existing tower into. Lives on the Player prefab alongside
/// .
///
///
/// Distinct from the deck, deliberately. is what you
/// can build from scratch; this is what you can upgrade into. They have to be
/// separate sets because the 2.0 model is two-step: a draft pick unlocks a node, and gold spent
/// on an already-placed tower converts it. Unlocking an upgrade must not make it directly
/// buildable, or the gold step — the entire cost of the upgrade — is bypassed.
///
/// The tree lives in the data. Edges are TowerDefinition.UpgradePaths; this
/// component only records which nodes a player has earned the right to use. Whether a
/// particular tower can take a particular upgrade is a question for the tree (is it a child of
/// what's placed?) crossed with this set (has the player unlocked it?).
///
/// Append-only within a match, mirroring the deck. Reset happens by re-initializing at
/// match start, so Retry / return-to-lobby cycles start clean.
///
public class PlayerTowerUpgrades : NetworkBehaviour
{
// ----- Static registry (mirrors PlayerTowerDeck / PlayerGoldManager) -----
private static readonly Dictionary s_byClientId
= new Dictionary();
public static PlayerTowerUpgrades GetForClient(ulong clientId)
{
s_byClientId.TryGetValue(clientId, out var upgrades);
return upgrades;
}
public static PlayerTowerUpgrades Local
{
get
{
var nm = NetworkManager.Singleton;
if (nm == null || !nm.IsClient) return null;
return GetForClient(nm.LocalClientId);
}
}
// ----- Networked state --------------------------------------------
// TowerTypeIds (catalog indices) this player may upgrade into.
private NetworkList unlockedNodes;
/// Fired on every peer when the unlocked set changes. The HUD subscribes so a
/// selected tower's upgrade buttons appear the moment a draft grants them.
public event System.Action OnUpgradesChanged;
private void Awake()
{
unlockedNodes = new NetworkList();
}
public override void OnNetworkSpawn()
{
s_byClientId[OwnerClientId] = this;
unlockedNodes.OnListChanged += HandleListChanged;
}
public override void OnNetworkDespawn()
{
unlockedNodes.OnListChanged -= HandleListChanged;
if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
s_byClientId.Remove(OwnerClientId);
}
private void HandleListChanged(NetworkListEvent _) => OnUpgradesChanged?.Invoke();
// ----- Read API ---------------------------------------------------
public int Count => unlockedNodes.Count;
public int GetTypeIdAt(int index) => unlockedNodes[index];
/// True if this player has unlocked the given upgrade node.
public bool Contains(int towerTypeId)
{
for (int i = 0; i < unlockedNodes.Count; i++)
if (unlockedNodes[i] == towerTypeId) return true;
return false;
}
// ----- Server API -------------------------------------------------
/// Server-only: clear the unlocked set. Called at match start.
public void ServerInitialize()
{
if (!IsServer) return;
unlockedNodes.Clear();
}
///
/// Server-only: unlock an upgrade node. Returns false if already unlocked (so a draft
/// option can report a wasted pick rather than silently succeeding).
///
public bool ServerUnlock(int towerTypeId)
{
if (!IsServer) return false;
if (towerTypeId < 0 || Contains(towerTypeId)) return false;
unlockedNodes.Add(towerTypeId);
return true;
}
}
}