Melee feel overhaul: SwordCombat anims at heavy cadence, salvage axe, damage-at-contact + buffer + dash law (guidelines forks 1-6)
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>
This commit is contained in:
@@ -35,6 +35,15 @@ namespace ProjectM.Simulation
|
||||
const float DefaultSharpness = CharacterComponent.DefaultGroundedSharpness; // CharacterComponent.GetDefault() base
|
||||
const float SimTickRate = 60f;
|
||||
|
||||
// 07-20 G7 dash law (design review wf_000bc247 C4): a LOOKUP, not a query add -- melee-less entities
|
||||
// (dash test harnesses) must keep dashing; only entities that carry MeleeCombo get the commitment rule.
|
||||
ComponentLookup<MeleeCombo> m_MeleeLookup;
|
||||
|
||||
public void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_MeleeLookup = state.GetComponentLookup<MeleeCombo>(isReadOnly: true);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
@@ -50,19 +59,31 @@ namespace ProjectM.Simulation
|
||||
float dashSharpness = t.DashSharpness;
|
||||
// 07-15 underwater feel: the restore target honors the MoveSharpness dev-override (0 = authored const).
|
||||
float baseSharpness = t.MoveSharpness > 0f ? t.MoveSharpness : DefaultSharpness;
|
||||
float contactKnob = math.max(0f, t.MeleeContactTicks); // 07-20 G7: pre-contact swing = dash refused
|
||||
m_MeleeLookup.Update(ref state);
|
||||
|
||||
|
||||
foreach (var (ds, cd, control, character, input, facing) in
|
||||
foreach (var (ds, cd, control, character, input, facing, entity) in
|
||||
SystemAPI.Query<RefRW<DashState>, RefRW<DashCooldown>, RefRW<CharacterControl>,
|
||||
RefRW<CharacterComponent>, RefRO<PlayerInput>, RefRO<PlayerFacing>>()
|
||||
.WithAll<Simulate>().WithDisabled<Dead>())
|
||||
.WithAll<Simulate>().WithDisabled<Dead>().WithEntityAccess())
|
||||
{
|
||||
// --- START (idempotent: fresh press + cooldown ready + not already mid-dash) ---
|
||||
bool ready = cd.ValueRO.NextTick == 0u
|
||||
|| !new NetworkTick(cd.ValueRO.NextTick).IsNewerThan(serverTick);
|
||||
bool inWindow = ds.ValueRO.RecoverUntilTick != 0u
|
||||
&& new NetworkTick(ds.ValueRO.RecoverUntilTick).IsNewerThan(serverTick);
|
||||
if (input.ValueRO.Dash.IsSet && ready && !inWindow)
|
||||
// 07-20 G7 (the Bathynaut COMMITS): a dash start is refused while a swing is PRE-CONTACT -- the
|
||||
// blade hasn't landed; post-contact recovery dash-cancel stays allowed. Same wrap-safe window
|
||||
// family as the anim pulse (TickWindowMath.SwingActive over the per-step contact ticks);
|
||||
// contact knob 0 -> zero-length window -> no refusal (legacy). Deterministic both worlds.
|
||||
bool preContactCommit = false;
|
||||
if (m_MeleeLookup.HasComponent(entity))
|
||||
{
|
||||
var mcRO = m_MeleeLookup[entity];
|
||||
preContactCommit = TickWindowMath.SwingActive(mcRO, serverTick, MeleeTiming.ContactTicks(mcRO.Step, contactKnob));
|
||||
}
|
||||
if (input.ValueRO.Dash.IsSet && ready && !inWindow && !preContactCommit)
|
||||
{
|
||||
// C1 + 07-15 fork: dash toward MOVEMENT input when moving (a panic 'dash away' still works); else
|
||||
// toward the CURSOR aim (stationary aim-and-dash keeps today's KBM feel under move-facing —
|
||||
|
||||
Reference in New Issue
Block a user