using Unity.Mathematics;
namespace ProjectM.Simulation
{
/// 07-20 melee feel forks (guidelines G2.1): the per-step CONTACT tick — how many ticks after
/// SwingStartTick the blade actually lands. The server cleave resolves THEN (not at swing start), so the
/// damage cone is a true record of the visible hit. Fractions derive from the AC_PlayerTopDown clip contact
/// seconds at the wired state speeds (PlayerRigTools swaps table: A hit@0.267s, B hit@0.167s, C hit@0.333s
/// @60Hz): step2 = x0.625 (10t at the default), step3+ (finisher) = x1.25 (20t). Knob = TuningConfig
/// .MeleeContactTicks (default 16); 0 = IMMEDIATE (legacy same-tick resolve; the mechanics tests pin 0).
/// COUPLING (documented, not clamped): keep contact(step3) < CastFacingTicks (26) and < MeleeRecoverTicks —
/// the cast-turn/anim window and chain cadence assume the hit lands inside both. The cleave itself is safe at
/// ANY knob value (MeleeComboSystem flushes a still-armed pending cleave before overwriting the swing).
public static class MeleeTiming
{
/// Server tick-batching slack: the buffered-attack validity window past the unlock edge must
/// cover Netcode's default MaxSimulationStepBatchSize (4) or an edge press drops under load (design
/// review wf_000bc247 C1).
public const uint BatchSlackTicks = 4;
public static uint ContactTicks(byte step, float contactKnob)
{
if (contactKnob <= 0f) return 0u; // 0 = immediate (legacy same-tick resolve)
float f = step == 2 ? 0.625f : step >= 3 ? 1.25f : 1f;
return (uint)math.max(0f, math.round(contactKnob * f));
}
}
}