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:
@@ -46,6 +46,14 @@ namespace ProjectM.Simulation
|
||||
public float MeleeFinisherMult;
|
||||
public float MeleeComboLength;
|
||||
|
||||
// 07-20 melee feel forks (guidelines G2/G7, design review wf_000bc247):
|
||||
/// <summary>Finisher REACH multiplier (reach honesty G2.2) — MeleeFinisherMult keeps damage/recover/knockback only.</summary>
|
||||
public float MeleeFinisherRangeMult;
|
||||
/// <summary>Attack input-buffer window (ticks before unlock a press is remembered). 0 = off.</summary>
|
||||
public float MeleeBufferTicks;
|
||||
/// <summary>Base contact delay (ticks from swing start to the blade landing; per-step via MeleeTiming). 0 = IMMEDIATE (legacy same-tick cleave).</summary>
|
||||
public float MeleeContactTicks;
|
||||
|
||||
// EB-1 fortress aggro: a <1 multiplier on a Husk's SQUARED distance to a structure (so structures are
|
||||
// preferred targets); a closer player 'in the way' still wins. Read server-side by EnemyAISystem.
|
||||
public float StructureAggroWeight;
|
||||
@@ -76,15 +84,18 @@ namespace ProjectM.Simulation
|
||||
ChargerLungeDurationTicks = 18f,
|
||||
ChargerWhiffStaggerTicks = 36f,
|
||||
GruntWindupTicks = Tuning.AttackWindupTicks, // canonical Grunt-windup source (TelegraphTests couples to it)
|
||||
MeleeDamage = 18f,
|
||||
MeleeRange = 2.6f,
|
||||
MeleeDamage = 30f, // 07-19 heavy-weapon retime: bigger hit, slower cadence (DPS ~held: 18/0.27s -> 30/0.50s)
|
||||
MeleeRange = 2.2f, // 07-20 G2.2 reach honesty: axe tip ~1.8m + a 0.4m player-favoring margin (was 2.6)
|
||||
MeleeConeHalfAngleRad = 0.9f, // ~51.5 deg half-angle (~103 deg cleave arc)
|
||||
MeleeRecoverTicks = 16f, // light-swing lock/recovery (~0.27s)
|
||||
MeleeChainGraceTicks = 18f, // window after the lock to chain the next hit (~0.3s)
|
||||
MeleeRecoverTicks = 30f, // heavy-swing lock/recovery (~0.50s; 07-19 weighty-melee retime)
|
||||
MeleeChainGraceTicks = 24f, // window after the lock to chain the next hit (~0.4s)
|
||||
MeleeSwingMoveScale = 0.35f, // movement-commit while swinging (0..1)
|
||||
MeleeKnockbackSpeed = 6f,
|
||||
MeleeFinisherMult = 1.8f, // finisher (last hit) scales dmg/range/recover/knockback
|
||||
MeleeFinisherMult = 1.5f, // finisher (last hit) scales dmg/range/recover/knockback (07-19: 1.8 -> 1.5, recover was ballooning at the heavy cadence)
|
||||
MeleeComboLength = 3f, // light, light, finisher
|
||||
MeleeFinisherRangeMult = 1.25f, // 07-20 G2.2: finisher REACH (damage/recover/knockback keep MeleeFinisherMult)
|
||||
MeleeBufferTicks = 8f, // 07-20 G7: attack press remembered in the last ~0.13s of a lock (0 = off)
|
||||
MeleeContactTicks = 16f, // 07-20 G2.1: blade lands ~0.27s after swing start (per-step via MeleeTiming; 0 = legacy immediate)
|
||||
StructureAggroWeight = 0.7f, // EB-1: <1 prefers structures (fortress aggro); live-tunable
|
||||
StaggerKnockbackSpeed = 7f, // B2 poise: kb.Speed >= this interrupts windups/lunges; below = nudge only
|
||||
SeparationMaxSpeed = 3f, // B1: max separation push (units/s) so soft-collision can't fling
|
||||
@@ -116,6 +127,9 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.CastTurnRateDeg:
|
||||
case TuningKnob.MoveSharpness:
|
||||
case TuningKnob.SeparationMaxSpeed:
|
||||
case TuningKnob.MeleeFinisherRangeMult:
|
||||
case TuningKnob.MeleeBufferTicks: // 07-20: 0 = buffer OFF sentinel (must survive ClampKnob — review C10)
|
||||
case TuningKnob.MeleeContactTicks: // 07-20: 0 = IMMEDIATE sentinel (legacy same-tick cleave)
|
||||
return math.max(0f, value);
|
||||
// tick knobs: >= 1 (a 0 tick count is degenerate; a 0 i-frame window divides-by-zero in DashSystem).
|
||||
// FinalSiegeMultiplier also lands here on purpose — a final siege should never be < 1x a normal one.
|
||||
@@ -155,6 +169,9 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.TurnRateDeg: c.TurnRateDeg = value; break;
|
||||
case TuningKnob.CastTurnRateDeg: c.CastTurnRateDeg = value; break;
|
||||
case TuningKnob.MoveSharpness: c.MoveSharpness = value; break;
|
||||
case TuningKnob.MeleeBufferTicks: c.MeleeBufferTicks = value; break;
|
||||
case TuningKnob.MeleeFinisherRangeMult: c.MeleeFinisherRangeMult = value; break;
|
||||
case TuningKnob.MeleeContactTicks: c.MeleeContactTicks = value; break;
|
||||
|
||||
// unknown index -> no-op (matches the no-default switch convention in DebugCommandReceiveSystem)
|
||||
}
|
||||
@@ -190,6 +207,9 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.TurnRateDeg: return c.TurnRateDeg;
|
||||
case TuningKnob.CastTurnRateDeg: return c.CastTurnRateDeg;
|
||||
case TuningKnob.MoveSharpness: return c.MoveSharpness;
|
||||
case TuningKnob.MeleeBufferTicks: return c.MeleeBufferTicks;
|
||||
case TuningKnob.MeleeFinisherRangeMult: return c.MeleeFinisherRangeMult;
|
||||
case TuningKnob.MeleeContactTicks: return c.MeleeContactTicks;
|
||||
|
||||
default: return 0f;
|
||||
}
|
||||
@@ -223,6 +243,9 @@ namespace ProjectM.Simulation
|
||||
TurnRateDeg = c.TurnRateDeg,
|
||||
CastTurnRateDeg = c.CastTurnRateDeg,
|
||||
MoveSharpness = c.MoveSharpness,
|
||||
MeleeBufferTicks = c.MeleeBufferTicks,
|
||||
MeleeFinisherRangeMult = c.MeleeFinisherRangeMult,
|
||||
MeleeContactTicks = c.MeleeContactTicks,
|
||||
|
||||
};
|
||||
|
||||
@@ -254,6 +277,9 @@ namespace ProjectM.Simulation
|
||||
TurnRateDeg = r.TurnRateDeg,
|
||||
CastTurnRateDeg = r.CastTurnRateDeg,
|
||||
MoveSharpness = r.MoveSharpness,
|
||||
MeleeBufferTicks = r.MeleeBufferTicks,
|
||||
MeleeFinisherRangeMult = r.MeleeFinisherRangeMult,
|
||||
MeleeContactTicks = r.MeleeContactTicks,
|
||||
|
||||
};
|
||||
}
|
||||
@@ -289,9 +315,12 @@ namespace ProjectM.Simulation
|
||||
public const byte TurnRateDeg = 26;
|
||||
public const byte CastTurnRateDeg = 27;
|
||||
public const byte MoveSharpness = 28;
|
||||
public const byte MeleeBufferTicks = 29; // 07-20 G7 (0 = off sentinel)
|
||||
public const byte MeleeFinisherRangeMult = 30; // 07-20 G2.2
|
||||
public const byte MeleeContactTicks = 31; // 07-20 G2.1 (0 = immediate sentinel)
|
||||
|
||||
/// <summary>Knob count (overlay iteration bound).</summary>
|
||||
public const byte Count = 29;
|
||||
public const byte Count = 32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -327,6 +356,9 @@ namespace ProjectM.Simulation
|
||||
public float TurnRateDeg;
|
||||
public float CastTurnRateDeg;
|
||||
public float MoveSharpness;
|
||||
public float MeleeBufferTicks; // 07-20 melee feel forks (dev-protocol bump: rebuild both peers)
|
||||
public float MeleeFinisherRangeMult;
|
||||
public float MeleeContactTicks;
|
||||
}
|
||||
|
||||
// NOTE: appending fields = a DEV-PROTOCOL BUMP (RpcCollection hash) — rebuild both peers together.
|
||||
|
||||
Reference in New Issue
Block a user