using Unity.Entities; using Unity.NetCode; namespace ProjectM.Simulation { /// /// MC-4 — predicted, owner-replicated melee combo state on the player (the PRIMARY offense verb). UNLIKE the dash /// (DashState is non-replicated because a dash is a STATELESS-per-tick decision), the combo is /// PATH-DEPENDENT — which chain link you are on depends on the sequence + timing of prior presses — and the bounded /// input command buffer cannot reconstruct it across a reconnect / long rollback (MC-4 review PRED-1 / ROLLBACK-1). /// So the minimal anchor is replicated as owner-predicted [GhostField]s: a rollback restores the /// authoritative combo position, then MeleeComboSystem re-simulates forward with ABSOLUTE-WRITE windows (never an /// in-place prev+1 of a non-restored field — the DashSystem idempotency idiom). The derived chain deadline /// (LockUntilTick + grace) is NOT stored. All ticks routed through TickUtil.NonZero; compared via /// only. Baked all-zero (idle). This is the player ghost's only net-new replicated melee /// state (one re-bake; in-family with DashCooldown/AbilityCooldown). /// public struct MeleeCombo : IComponentData { /// Current/last swing index: 0 = idle, 1..N = chain link. Owner-predicted so a rollback restores the /// authoritative combo position (the only legitimately path-dependent value). [GhostField] public byte Step; /// Raw ServerTick the current swing started (NonZero). Inclusive lower bound of the movement-commit /// window; also the juice edge signal and the (instant) damage frame. [GhostField] public uint SwingStartTick; /// Raw ServerTick the swing's movement-commit / recovery lock ends (NonZero). Gates the next press /// (locked while now < this) and anchors the chain window [LockUntilTick, LockUntilTick + grace). [GhostField] public uint LockUntilTick; /// 07-20 (guidelines G7 input buffer): raw ServerTick of an Attack press that landed during the /// swing lock's LAST MeleeBufferTicks (NonZero; 0 = empty). Consumed (zeroed) when it starts the chained /// swing; validity is anchored to the UNLOCK edge, not the press tick, so server tick-batching cannot /// drop an edge press (design review wf_000bc247 C1). Predicted + rollback-restored like its siblings. [GhostField] public uint BufferedAttackTick; } /// 07-20 (guidelines G2.1 damage-at-contact): SERVER-ONLY scheduled cleave — the AttackWindup /// schedule-and-consume idiom, entity-local (design review wf_000bc247 C11: no container, no disposal, no /// staleness window). Stamped when a swing starts (ResolveTick = start + MeleeTiming.ContactTicks), fired on /// a wrap-safe elapsed compare (tick-batch-proof), CONSUMED by zeroing. A still-armed pending is FLUSHED /// (fired early) before a chained swing overwrites it, so no knob combination can lose a cleave (C0/C12). /// Not replicated; the client's contact cues are presentation-side (CombatFeedbackSystem). Baked zeroed. public struct MeleeCleavePending : IComponentData { /// NonZero tick the armed swing's blade lands; 0 = nothing pending. public uint ResolveTick; /// Combo step of the ARMED swing (finisher scaling resolved against the live combo length). public byte Step; } }