First pass at full refactor to 2.0 design
Restructures the game around the cyclical run loop from Game Design Doc V2: 5 waves = a cycle, 3 cycles = a phase, each phase ends in a boss. - New TD.Gameplay.Waves: WaveGroup / PhaseDefinition / RunDefinition author the run as draggable weighted pools; RunState owns phase/cycle position, the drawn wave slots, and the per-slot enemy buff sets. WaveManager's flat wave array is gone -- it now only runs the encounter RunState points at. - New TD.Gameplay.EnemyUpgrades: the post-wave enemy-buff vote, with public live-replicated ballots so the HUD can show who voted for what. - Inter-wave flow is now strictly sequential: draft -> vote -> build, each stage ending early once every player has acted. - Enemy abilities inverted from per-instance random rolls to deterministic, stacking per-wave-slot sets. Six cards ship: Split (reworked), Flight, Blink, No Bounty, Gold Theft, Double Up. - Tower upgrades are a two-step tree: a draft pick unlocks a node, gold converts an already-placed tower in place. - Boss encounters flag their enemies and drive a boss HP bar. - Player cap reduced to 3 via MatchRules.MaxPlayers. - GoldConfig is now keyed by global encounter number rather than wave index. Compiles clean; NOT yet verified in-engine. Editor wiring still required -- see Docs/2.0_Setup_Checklist.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7e5c3a8279
commit
4892d7253d
64 changed files with 4023 additions and 344 deletions
116
Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
Normal file
116
Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Per-player set of unlocked tower <b>upgrade nodes</b> — the tower types this player is
|
||||
/// allowed to convert an existing tower into. Lives on the Player prefab alongside
|
||||
/// <see cref="PlayerTowerDeck"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Distinct from the deck, deliberately.</b> <see cref="PlayerTowerDeck"/> is what you
|
||||
/// can <i>build from scratch</i>; this is what you can <i>upgrade into</i>. 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.</para>
|
||||
///
|
||||
/// <para><b>The tree lives in the data.</b> Edges are <c>TowerDefinition.UpgradePaths</c>; 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?).</para>
|
||||
///
|
||||
/// <para>Append-only within a match, mirroring the deck. Reset happens by re-initializing at
|
||||
/// match start, so Retry / return-to-lobby cycles start clean.</para>
|
||||
/// </remarks>
|
||||
public class PlayerTowerUpgrades : NetworkBehaviour
|
||||
{
|
||||
// ----- Static registry (mirrors PlayerTowerDeck / PlayerGoldManager) -----
|
||||
|
||||
private static readonly Dictionary<ulong, PlayerTowerUpgrades> s_byClientId
|
||||
= new Dictionary<ulong, PlayerTowerUpgrades>();
|
||||
|
||||
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<int> unlockedNodes;
|
||||
|
||||
/// <summary>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.</summary>
|
||||
public event System.Action OnUpgradesChanged;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
unlockedNodes = new NetworkList<int>();
|
||||
}
|
||||
|
||||
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<int> _) => OnUpgradesChanged?.Invoke();
|
||||
|
||||
// ----- Read API ---------------------------------------------------
|
||||
|
||||
public int Count => unlockedNodes.Count;
|
||||
|
||||
public int GetTypeIdAt(int index) => unlockedNodes[index];
|
||||
|
||||
/// <summary>True if this player has unlocked the given upgrade node.</summary>
|
||||
public bool Contains(int towerTypeId)
|
||||
{
|
||||
for (int i = 0; i < unlockedNodes.Count; i++)
|
||||
if (unlockedNodes[i] == towerTypeId) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----- Server API -------------------------------------------------
|
||||
|
||||
/// <summary>Server-only: clear the unlocked set. Called at match start.</summary>
|
||||
public void ServerInitialize()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
unlockedNodes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-only: unlock an upgrade node. Returns false if already unlocked (so a draft
|
||||
/// option can report a wasted pick rather than silently succeeding).
|
||||
/// </summary>
|
||||
public bool ServerUnlock(int towerTypeId)
|
||||
{
|
||||
if (!IsServer) return false;
|
||||
if (towerTypeId < 0 || Contains(towerTypeId)) return false;
|
||||
|
||||
unlockedNodes.Add(towerTypeId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue