using Unity.Entities; using Unity.NetCode; namespace ProjectM.Simulation { /// /// Replicated Husk attack-telegraph signal. While is non-zero the Husk is /// "winding up" to strike; EnemyAISystem sets it ~ before the strike /// lands, and the strike fires when the tick elapses. This is a [GhostField] (the only replicated Husk field /// beyond the stock LocalTransform) so the CLIENT can play a ~0.3s pre-strike cue — the client has none of the /// server-only timing inputs (EnemyStats / EnemyAttackCooldown), so the wind-up MUST be replicated. A uint tick /// (not a [GhostEnabledBit]) so the cue can ramp/countdown and survive a missed snapshot (absolute, not an edge). /// public struct AttackWindup : IComponentData { /// Server tick the wind-up completes + the strike lands (0 = not winding up; scheduled via TickUtil.NonZero). [GhostField] public uint WindUpUntilTick; } /// Client-safe BAKED telegraph metadata (NOT a [GhostField] — baked values are identical on /// both worlds, so the client reads it directly to size the danger-cone ramp + pick the Charger look). /// WindupTicks = the variant's wind-up lead in ticks (the client danger-ramp denominator: Grunt = /// Tuning.AttackWindupTicks, Charger = ChargerWindupTicks); IsCharger distinguishes the variant since /// LungeState is server-only. Both stored as byte (no enum on a client-read path — honours the Burst /// byte-not-enum convention); never changes at runtime, so a missed snapshot is irrelevant. Baked by /// EnemyAuthoring.EnemyBaker (the SOLE writer — reads sibling ChargerAuthoring to set the Charger values). public struct EnemyTelegraph : IComponentData { /// Per-variant wind-up DURATION in ticks (the client danger-ramp denominator). public byte WindupTicks; /// Enemy kind for the client telegraph look: 0=Grunt, 1=Charger, 2=Spitter, 3=Swarmer (the /// ZoneEnemyMath.Kind* bytes). Baked per variant by EnemyBaker (the SOLE writer); never a [GhostField]. public byte Kind; } }