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; } }