// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs using UnityEngine; using TD.Gameplay.Waves; namespace TD.Gameplay.EnemyUpgrades { /// /// Base class for one card in the post-wave enemy-buff vote. Players collectively choose one /// of these to permanently attach to the wave they just cleared; it takes effect the next time /// that wave comes round in the cycle. /// /// /// Mirrors DraftOption deliberately. Same shape — abstract SO, one asset /// per card, concrete subclasses carry the payload — so the two authoring surfaces read the /// same way. The difference is who it targets: a draft option applies to a player, an enemy /// upgrade applies to a wave slot. /// /// Identity. Options live in and cross the network /// as their pool index, the same stable-within-a-match id pattern used by the tower catalog /// and draft pool. /// /// Recording is not this type's job. WaveVote records the winning option id /// onto the slot via RunState.ServerAddUpgrade; is only /// for cards that need a side effect beyond being recorded (the vote-meta cards). /// Ability cards need nothing here — the spawn path reads the slot's recorded set. /// public abstract class EnemyUpgradeOption : ScriptableObject { [Header("Presentation")] [Tooltip("Name shown on the vote card.")] public string DisplayName; [Tooltip("Short description shown on the vote card. Say what it does to the enemies, in " + "the players' terms — this is the only thing they get to judge the vote on.")] [TextArea(2, 4)] public string Description; [Tooltip("OPTIONAL override for the vote card icon. Leave empty to inherit the icon of " + "whatever this option grants.")] public Sprite Icon; /// /// Icon actually shown on the vote card. Subclasses override to fall back to the icon of /// the thing they grant; the base has nothing to inherit from, so it returns /// (which may be null). /// public virtual Sprite ResolveIcon() => Icon; /// /// Server-only: may this option be OFFERED for right now? Default /// true; override for cards with prerequisites or ones that would be a no-op. /// /// /// Callers already skip options the slot has taken before, so implementations don't need /// to re-check that. /// public virtual bool IsValidForSlot(RunState run, int slot) => true; /// /// Server-only: side effect to run after this option has been recorded onto /// . Default no-op — most cards do their work at enemy-spawn time /// by virtue of being on the slot, not at vote-resolution time. /// public virtual void ServerOnApplied(RunState run, int slot) { } } }