UnityTowerDefense/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs
Matt F 4892d7253d 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>
2026-07-30 17:45:11 -07:00

53 lines
2.4 KiB
C#

// Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs
using UnityEngine;
using TD.Core;
namespace TD.Gameplay.EnemyAbilities
{
/// <summary>
/// An enemy that reaches the defense point doesn't just cost a life — it robs the player whose
/// maze it escaped.
/// </summary>
/// <remarks>
/// <b>Charged to the origin zone, not the whole team.</b> Lives are a shared pool, so a leak
/// already punishes everyone equally; billing the gold to whoever let it through is the part
/// that makes this card bite differently from "enemies cost 2 lives". It also keeps the blame
/// legible — the player who leaked is the player who pays.
///
/// <para>Deducting more gold than a player has simply empties them; there is no debt. Going
/// negative would silently disable building until they clawed back to zero, which reads as a
/// broken HUD rather than a penalty.</para>
/// </remarks>
[CreateAssetMenu(fileName = "GoldTheftAbility", menuName = "TD/Enemy Abilities/Gold Theft")]
public class GoldTheftAbilityDefinition : EnemyAbilityDefinition
{
public override EnemyAbilityKind Kind => EnemyAbilityKind.GoldTheft;
[Header("Theft")]
[Tooltip("Flat gold taken from the leaking player, on top of the normal life cost.")]
[Min(0)]
public int GoldStolen = 15;
public override void ServerOnReachedGoal(EnemyAbility instance, PlayerSlot originZone)
{
if (GoldStolen <= 0 || originZone == PlayerSlot.None) return;
var pms = PlayerMatchState.GetForSlot(originZone);
if (pms == null) return;
var gold = PlayerGoldManager.GetForClient(pms.OwnerClientId);
if (gold == null) return;
// Clamp to what they actually hold — a leak shouldn't be able to put a player in debt.
int taken = Mathf.Min(GoldStolen, gold.CurrentGold);
if (taken <= 0) return;
gold.DeductGold(taken);
// Surface it in-world on every peer so the loss isn't just a number quietly ticking
// down in the corner. Routed through WaveManager because the popup has to reach
// clients and a ScriptableObject has no NetworkBehaviour to send from.
WaveManager.Instance?.ServerBroadcastGoldLoss(instance.transform.position, taken);
}
}
}