UN-2 RESOLVED: GetMaxSpeed x4 is byte-verified retail; doc-comment was the misread
The register's UN-2 row recorded a contradiction: the GetMaxSpeed XML doc claimed the bare run rate was retail-correct (~5.9 m/s catch-up, calling the xRunAnimSpeed multiply a misread), while the implementation multiplied by RunAnimSpeed citing ACE. Settled against the binary, not the pseudo-C: - BN pseudo-C (acclient_2013_pseudo_c.txt:305127) renders get_max_speed as void with a bare `this->my_run_rate;` because it DROPS x87 instructions. - Disassembling the PDB-matched v11.4186 binary at VA 0x00527cb0: all THREE return paths end `fld <rate>; fmul dword ptr [0x007C8918]; ret`, and the .rdata dword at 0x007C8918 is 4.0f. Sibling get_adjusted_max_speed (0x00527d00) carries the same trailing fmul. Verifier committed at tools/verify_un2_fmul.py (PE parse + byte decode, rerunnable). - Retail paths: weenie null -> 1.0 x4; InqRunRate ok -> queried x4; InqRunRate failed -> my_run_rate x4. ACE MotionInterp.cs:665-676 matches. Changes: - Doc-comment rewritten: the implementation is retail-correct; the catch-up speed 2 x get_max_speed ~= 23.5 m/s at run 200 IS retail. The 1-Hz remote-blip symptom the old comment attributed to this multiply is therefore UNEXPLAINED by it (if it recurs: #41 family, not this). - Weenie-null path aligned to retail's LITERAL 1.0 default (was MyRunRate). - Tests re-pinned to the three retail paths (the old NoWeenie test pinned the non-retail fallback). - Register: UN-2 row deleted per the retire rule (6 -> 5 UN rows); shortlist renumbered. This is the 2nd confirmed instance of the BN x87-dropout artifact class (memory: feedback_bn_decomp_field_names) deciding a register row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f6a30f4aae
commit
0cb97aa594
4 changed files with 114 additions and 66 deletions
|
|
@ -845,15 +845,14 @@ public sealed class MotionInterpreterTests
|
|||
[InlineData(MotionCommand.RunForward)]
|
||||
public void GetMaxSpeed_IgnoresForwardCommand_AlwaysReturnsRunRate(uint command)
|
||||
{
|
||||
// GetMaxSpeed is the InterpolationManager.AdjustOffset catch-up speed — it deliberately
|
||||
// returns RunAnimSpeed × run-rate REGARDLESS of the current ForwardCommand (see GetMaxSpeed's
|
||||
// doc comment: the bare run rate × RunAnimSpeed, ACE MotionInterp.cs:670-678, retail-verified
|
||||
// — the slow catch-up is intentional, it fixed the 1-Hz remote-blip). It does NOT branch
|
||||
// per-command. These previously asserted a REMOVED command-branching design (WalkForward →
|
||||
// WalkAnimSpeed, WalkBackward → ×0.65, Idle → 0); that contract no longer exists, so they are
|
||||
// consolidated here to PIN the no-branch contract across commands (Phase W green-tests triage).
|
||||
var interp = MakeInterp();
|
||||
interp.MyRunRate = 1.75f;
|
||||
// GetMaxSpeed is the InterpolationManager.AdjustOffset catch-up speed — it
|
||||
// returns RunAnimSpeed × run-rate REGARDLESS of the current ForwardCommand
|
||||
// (retail 0x00527cb0 never reads interpreted_state; UN-2 byte verification
|
||||
// 2026-06-12, tools/verify_un2_fmul.py). These previously asserted a REMOVED
|
||||
// command-branching design (WalkForward → WalkAnimSpeed, WalkBackward →
|
||||
// ×0.65, Idle → 0); they PIN the no-branch contract across commands.
|
||||
var weenie = new FakeWeenie { RunRate = 1.75f };
|
||||
var interp = MakeInterp(weenie: weenie);
|
||||
interp.InterpretedState.ForwardCommand = command;
|
||||
|
||||
float speed = interp.GetMaxSpeed();
|
||||
|
|
@ -862,17 +861,33 @@ public sealed class MotionInterpreterTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMaxSpeed_RunForward_NoWeenie_FallsBackToMyRunRate()
|
||||
public void GetMaxSpeed_NoWeenie_ReturnsLiteralOneTimesRunAnimSpeed()
|
||||
{
|
||||
// WeenieObj is null (MakeInterp with no weenie argument); MyRunRate
|
||||
// is set explicitly. GetMaxSpeed must use MyRunRate as the run-rate
|
||||
// source when InqRunRate is unavailable.
|
||||
// Retail 0x00527cb0 weenie_obj == null path: fld 1.0 (.rdata 0x007928B0),
|
||||
// fmul 4.0 (.rdata 0x007C8918) — the LITERAL 1.0, NOT my_run_rate (UN-2
|
||||
// byte verification 2026-06-12). MyRunRate is set to a different value to
|
||||
// prove it is not consulted on this path.
|
||||
var interp = MakeInterp();
|
||||
interp.MyRunRate = 1.75f;
|
||||
interp.InterpretedState.ForwardCommand = MotionCommand.RunForward;
|
||||
|
||||
float speed = interp.GetMaxSpeed();
|
||||
|
||||
Assert.Equal(MotionInterpreter.RunAnimSpeed * 1.0f, speed, precision: 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMaxSpeed_InqRunRateFails_FallsBackToMyRunRate()
|
||||
{
|
||||
// Retail 0x00527cb0 InqRunRate-failure path: fld [esi+0x7c] (my_run_rate),
|
||||
// fmul 4.0. The InqRunRate out-value is discarded on failure.
|
||||
var weenie = new FakeWeenie { RunRate = 9.9f, InqRunRateResult = false };
|
||||
var interp = MakeInterp(weenie: weenie);
|
||||
interp.MyRunRate = 1.75f;
|
||||
interp.InterpretedState.ForwardCommand = MotionCommand.RunForward;
|
||||
|
||||
float speed = interp.GetMaxSpeed();
|
||||
|
||||
Assert.Equal(MotionInterpreter.RunAnimSpeed * 1.75f, speed, precision: 4);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue