using Unity.Entities; using Unity.Mathematics; using Unity.NetCode; namespace ProjectM.Simulation { /// /// MC-1 — server-only Charger lunge state (a KnockbackState SHAPE-twin). Component PRESENCE is the Charger /// discriminator (no enum / brain byte — honours the Burst cross-assembly-enum rule; EnemyAISystem is Bursted): /// a Husk variant baked with LungeState is driven by the Charger branch, every other Husk by the Grunt branch /// (which excludes these via .WithNone<LungeState>()). On a wind-up commit the Charger LOCKS /// toward the target and travels at until — dealing /// contact damage if it connects, or staggering into a punish window if it whiffs (wall-stop or overshoot). /// NOT a [GhostField] (the lunged position replicates via the stock LocalTransform variant, like /// KnockbackState). All ticks via TickUtil.NonZero; compared with only. /// public struct LungeState : IComponentData { /// Fixed planar lunge heading, locked at commit (world XZ -> float2 x,y). public float2 Dir; /// Lunge speed (world units/s); only meaningful while is active. public float Speed; /// Raw tick the lunge ends (NonZero). 0 = not lunging. Active while .IsNewerThan(serverTick). public uint UntilTick; /// Raw tick the whiff-stagger punish window ends (NonZero; set at BOTH whiff sites). 0 = not /// staggered — or already punished: HealthApplyDamageSystem zeroes it when the first player-sourced hit /// lands so a window counts ONCE in DevTelemetry.ChargerWhiffPunishesLanded. The attack lockout itself /// rides EnemyAttackCooldown.NextAttackTick; this field only scores the punish. public uint StaggerUntilTick; } /// /// REPLICATED enableable MID-LUNGE flag on a Charger (Slice 1, Feature D). ENABLED for exactly the ticks a /// Charger is committed to its locked-direction lunge ( active), DISABLED /// otherwise. The ONLY replicated Charger surface beyond the stock LocalTransform — a [GhostEnabledBit], /// NOT a [GhostField], because the client needs only on/off: the lunge HEADING is already carried by the /// replicated LocalTransform.Rotation (EnemyAISystem writes LookRotationSafe(lungeDir) each lunge tick), so the /// client indicator derives direction via AnimParamMath.PlanarForward like the danger cone already does. Fixes /// the cue VANISHING at commit (AttackWindup zeroes on commit, so a windup-gated cone disappears exactly when /// the danger is realest): this bit STAYS on through the committed travel. Server-derived once per tick from /// LungeState.UntilTick in EnemyAISystem (the sole LungeState writer); BAKE DISABLED (a Charger spawns /// not-lunging) + visit via .WithPresent<IsLunging>() to write the bit while disabled (the Dead idiom). /// [GhostEnabledBit] public struct IsLunging : IComponentData, IEnableableComponent { } }