// Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs
using UnityEngine;
using TD.Core;
namespace TD.Gameplay.EnemyAbilities
{
///
/// Base class for one enemy ability (e.g. "split into smaller enemies on death"). Abilities
/// are attached to a wave slot by the post-wave player vote, and every enemy that wave
/// spawns from then on carries the whole accumulated set.
///
///
/// Assignment is deterministic, not random. This system originally rolled an
/// ability per individual enemy against a no-ability weight. Under the 2.0 design the players
/// choose it, it applies to every enemy in the wave, and it persists for the rest of the phase
/// — so the roll is gone and WaveManager reads the slot's set instead. What survived is
/// the shape: one asset per kind, server-only hooks.
///
/// Abilities stack. A wave can collect several across a phase's cycles, so an
/// enemy holds a list and every hook below runs once per ability. Implementations must not
/// assume they are the only ability on the enemy.
///
/// Server-only hooks. All hooks run on the server only —
/// gates every call on IsServer. They default to no-ops so a new card only overrides the
/// one or two it actually needs.
///
/// No per-enemy state on the asset. A single asset is shared by every enemy
/// carrying the ability, across every wave and match. Anything per-instance belongs on the
/// component or the enemy itself.
///
public abstract class EnemyAbilityDefinition : ScriptableObject
{
/// Which enemy ability this asset's data belongs to.
public abstract EnemyAbilityKind Kind { get; }
[Header("Presentation")]
[Tooltip("Name shown in debug logs and the enemy-info panel.")]
public string DisplayName;
[Tooltip("Short description shown in the enemy-info panel.")]
[TextArea(2, 4)]
public string Description;
// ----- Spawn-time stat modification --------------------------------
///
/// Server-only: alter what this enemy spawns as, before it is built. Runs for every enemy
/// of the buffed wave. Default no-op.
///
///
/// This is the hook for cards that change what an enemy fundamentally is — flight,
/// health, speed, size. It must run before the enemy exists, because spawn position
/// (flyers are raised) and EnemyHealth/EnemyMovement initialization all read
/// these values once and keep them.
///
/// When several abilities stack, they see each other's edits in application order,
/// so multiplicative modifiers compose naturally. Prefer multiplying over assigning for
/// anything numeric, or the last card to run silently wins.
///
public virtual void ServerModifySpawn(ref EnemySpawnContext context) { }
// ----- Lifetime hooks ----------------------------------------------
/// Server-only: called once, right after the ability set is assigned (before the
/// enemy's NetworkObject is spawned to clients). Default no-op.
/// This ability's slot on — the
/// same index receives, so per-enemy timers can be seeded here.
public virtual void ServerOnSpawn(EnemyAbility instance, int abilityIndex) { }
///
/// Server-only: called every frame this ability is active on a live enemy. Default no-op.
///
/// This ability's slot on . Pass it
/// to to reach a per-enemy float this ability owns.
///
/// The asset holds no per-enemy state. One
/// instance is shared by every enemy carrying the ability, so a cooldown stored in a field
/// here would be shared by the entire wave — every enemy would blink in lockstep, or worse,
/// race each other's writes. gives each enemy its own
/// slot by reference, which is enough for the timer-driven abilities and costs no
/// allocation.
///
public virtual void ServerTick(EnemyAbility instance, int abilityIndex, float dt) { }
/// Server-only: called the instant the enemy's HP reaches zero, before the
/// death animation/despawn sequence plays. Default no-op.
public virtual void ServerOnDeath(EnemyAbility instance, EnemyHealth health) { }
// ----- Economy hooks -----------------------------------------------
///
/// Server-only: adjust the gold a player earns for killing this enemy. Returns the reward
/// to pass on; default is unchanged.
///
///
/// Chained across a stacked ability set, each ability receiving the previous one's result.
/// Callers clamp the final value at zero, so returning a negative is safe but pointless.
///
public virtual int ServerModifyKillReward(EnemyAbility instance, int reward) => reward;
///
/// Server-only: called when this enemy reaches the defense point, after lives have been
/// deducted. The hook for leak-punishing cards that cost more than lives. Default no-op.
///
public virtual void ServerOnReachedGoal(EnemyAbility instance, PlayerSlot originZone) { }
}
}