7b65912f30
07-18/19/20 arc: LightCombo01/HeavyCombo01 clips onto Swing1-3/Slam at ~natural speed (26-tick window, 30-tick recover, DPS-held damage retune), Menacing01 combat idle (InCombat), SM_Wep_Axe_Large_01 rigid-skinned to Hand_R at x1.25, LANTERN FX retone + blade-smear ribbon. Forks 1-6 per Combat_Attack_Feel_Guidelines (design review wf_000bc247): cleave resolves at the CONTACT tick (MeleeCleavePending schedule-and-consume, knob 31, 0=legacy; connect cues moved to contact), MeleeRange 2.2 + reach-only finisher mult 1.25 (knob 30), BufferedAttackTick GhostField input buffer (knob 29, unlock-edge validity), dash refused pre-contact (lookup-based). Tests: MeleeComboTests pin legacy knobs; +3 fork tests (414 green); live server proof: HP drop exactly at swing+16 under tick batching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
53 lines
3.6 KiB
C#
53 lines
3.6 KiB
C#
using Unity.Entities;
|
|
using Unity.NetCode;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// 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 <see cref="Step"/> 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 <c>[GhostField]</c>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 <c>TickUtil.NonZero</c>; compared via
|
|
/// <see cref="NetworkTick"/> 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).
|
|
/// </summary>
|
|
public struct MeleeCombo : IComponentData
|
|
{
|
|
/// <summary>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).</summary>
|
|
[GhostField] public byte Step;
|
|
|
|
/// <summary>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.</summary>
|
|
[GhostField] public uint SwingStartTick;
|
|
|
|
/// <summary>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).</summary>
|
|
[GhostField] public uint LockUntilTick;
|
|
|
|
/// <summary>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.</summary>
|
|
[GhostField] public uint BufferedAttackTick;
|
|
}
|
|
|
|
/// <summary>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.</summary>
|
|
public struct MeleeCleavePending : IComponentData
|
|
{
|
|
/// <summary>NonZero tick the armed swing's blade lands; 0 = nothing pending.</summary>
|
|
public uint ResolveTick;
|
|
/// <summary>Combo step of the ARMED swing (finisher scaling resolved against the live combo length).</summary>
|
|
public byte Step;
|
|
}
|
|
}
|