using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Simulation
{
///
/// Periodic-AoE state on a player-cast zone ghost (Vortex / LightZone) — the LANTERN Aoe/zone archetype.
/// The zone is a server-spawned INTERPOLATED ownerless ghost; position replicates via the stock LocalTransform
/// variant. 07-21 G6 (design review wf_98bf1268): THREE fields are now [GhostField] so the client can
/// draw an HONEST fill telegraph (ZoneTelegraphSystem): (G3 ownership tint),
/// (rim = the TRUE folded damage radius) and (fill arrival = the
/// damage moment — server mutations propagate to all clients on an ownerless ghost). Damage schedule/amount
/// stay server-only. ZonePulseSystem borrows the GeyserEruptSystem skeleton (the INVERTED
/// invalid-tick guard so a born-0 tick never storm-fires + the = now + period reschedule) but is
/// ENEMY-ONLY (no friendly fire), stamps =
/// (kills credit the caster, feeding KillRewardSystem — NOT the -1 environment convention), and has NO region
/// gate (a no-world gym). DestroyEntity when elapses.
///
public struct ZoneEffect : IComponentData
{
/// Pulse cadence in ticks — single source for ZonePulseSystem (damage) and ZoneTelegraphSystem
/// (the client fill window). Hoisted 07-21 G6 (was ZonePulseSystem.k_PulsePeriodTicks).
public const uint PulsePeriodTicks = 30;
/// NetworkId of the casting player (damage/kill attribution + the client's mine-vs-ally tint).
[GhostField] public int CasterNetworkId;
/// Planar (XZ) damage radius, world units. Replicated so the drawn rim is the TRUE folded radius.
[GhostField(Quantization = 100)] public float Radius;
/// Raw next-pulse tick (NonZero). 0 = unscheduled → lazy-stamped born-correct (GeyserErupt H2
/// rule, never storm-fires). Replicated for the client fill: ride the ABSOLUTE tick + a value-latch + a
/// was-counting-down arm-guard (the Geyser ★ rule — never edge-detect the re-stamp).
[GhostField] public uint NextTick;
/// Damage dealt to each living enemy in radius per pulse. Server-only.
public float DamagePerPulse;
/// Raw tick the zone despawns (NonZero). Active while .IsNewerThan(serverTick). Server-only.
public uint ExpireTick;
/// See . bit0 = Vortex (also pull enemies toward the zone center each pulse).
public byte Flags;
}
/// Bit masks for .
public static class ZoneEffectFlag
{
public const byte Vortex = 1;
}
///
/// SERVER-ONLY decoy-wisp state (the Aoe/spawn Spark) — a server-spawned INTERPOLATED ownerless ghost that
/// draws Husk aggro. EnemyAISystem gathers DecoyTag entities into its target set so Husks path to and
/// strike the decoy (soaking hits via its Health/DamageEvent buffer); it despawns on Health <= 0 or
/// when elapses (both handled where EnemyAISystem already iterates decoys). Position
/// replicates via the stock LocalTransform variant — NOT a [GhostField].
///
public struct DecoyTag : IComponentData
{
/// Raw tick the decoy despawns (NonZero). 0 = no timeout; active while .IsNewerThan(serverTick).
public uint ExpireTick;
}
}