feat(combat): port retail basic combat bar

Mount authored gmCombatUI, share one press/hold/release request state machine across DAT buttons and keybindings, and recover the exact 1.0s/0.8s power timing from matching retail x86. The same timer fixes jump charge, while ready-stance, response queueing, auto-repeat, layout binding, migration, and conformance coverage keep behavior architectural rather than panel-local.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 14:03:28 +02:00
parent 9958458318
commit 2215c76c7e
22 changed files with 1265 additions and 46 deletions

View file

@ -165,15 +165,14 @@ public sealed class PlayerMovementController
// Jump charge state.
private bool _jumpCharging;
private float _jumpExtent;
// K-fix6 (2026-04-26): retail's PowerBar charge constant for jump is
// not legible in the named decomp (the divisor was clobbered in
// GetPowerBarLevel's FPU stack reordering at FUN_0056ade0). 2.0/s
// (full charge in 0.5s) feels matches retail muscle memory better
// than the previous 1.0/s — a tap gives a noticeable hop, half-hold
// a meaningful jump, full-hold the maximum extent. The vertical
// velocity formula itself (height × 19.6 → vz) is unchanged and
// matches retail byte-for-byte; only the time-to-fill is faster.
private const float JumpChargeRate = 2.0f;
// Matching v11.4186 x86 resolves GetPowerBarLevel's collapsed x87
// operands: ATTACK_POWERUP_TIME=1.0 s, DUAL_WIELD_POWERUP_TIME=0.8 s.
// Jump uses the same shared powerbar function, so its normal fill rate is
// 1 extent/s and DualWieldCombat is 1/0.8 = 1.25 extent/s.
private const float JumpChargeRate =
1f / (float)AcDream.Core.Combat.CombatInputPlanner.AttackPowerUpSeconds;
private const float DualWieldJumpChargeRate =
1f / (float)AcDream.Core.Combat.CombatInputPlanner.DualWieldPowerUpSeconds;
// Airborne → grounded transition detection. Flipped on every frame where
// the body transitions from airborne to on-walkable; used by the GameWindow
@ -780,7 +779,11 @@ public sealed class PlayerMovementController
// this line despite the W3 port.
_motion.ChargeJump();
}
_jumpExtent = MathF.Min(_jumpExtent + dt * JumpChargeRate, 1.0f);
float chargeRate = _motion.InterpretedState.CurrentStyle
== AcDream.Core.Combat.CombatInputPlanner.DualWieldCombatStyle
? DualWieldJumpChargeRate
: JumpChargeRate;
_jumpExtent = MathF.Min(_jumpExtent + dt * chargeRate, 1.0f);
}
else if (_jumpCharging)
{