fix #266: retail run-rate 800 branch is exact-equality sentinel, not a cap
Raw byte decode of MovementSystem::GetRunRate (0x006b0950, PDB-paired
binary): fild skill; fcom [800f]; fnstsw; test ah, 0x44; jp general —
the C2/C3 parity idiom whose 18/4 fall-through executes ONLY at
skill == 800 exactly. ACE read this as >= 800 ('max run speed?') and
Campaign P P1 inherited that misread when BN dropped the arithmetic,
flat-lining every maxed character at 4.5 (retail-true ~3.70, +21%) and
erasing the vitae differential (both 10200 and 15225 sat above 800).
The [stat-chain] live capture proved the enchant chain correct end to
end (vitae 0.67 -> eff run 10200 -> controller), isolating the formula.
General path byte-verified: (loadMod*(skill/(skill+200)*11)+4)/scaling/4.
InqMaxRunRate's skill=9999 probe gets ~3.6961, not 4.5.
Golden tests pin the 799/800/801 straddle and the maxed-skill vitae
differential; pseudocode doc §6 carries the decode plus a do-not-
reimport-ACE warning. Complete Release suite: 10,025 passed / 5 skips.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
bd3ade625f
commit
61e959169b
5 changed files with 85 additions and 28 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -11,10 +12,29 @@ namespace AcDream.Core.Tests.Physics;
|
|||
public class MovementSystemTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetRunRate_Skill800Cap_Returns4Point5()
|
||||
public void GetRunRate_Skill800Sentinel_IsExactEqualityOnly()
|
||||
{
|
||||
// Retail 0x006b0950 byte decode (#266): fcom [800f]; test ah, 0x44;
|
||||
// jp — the 18/4 return executes ONLY when skill == 800 exactly.
|
||||
// 799 and 801 take the general formula on either side of it.
|
||||
Assert.Equal(4.5f, MovementSystem.GetRunRate(0f, 800), precision: 5);
|
||||
Assert.Equal(4.5f, MovementSystem.GetRunRate(0f, 999999), precision: 5);
|
||||
Assert.Equal(3.1994f, MovementSystem.GetRunRate(0f, 799), precision: 3);
|
||||
Assert.Equal(3.2005f, MovementSystem.GetRunRate(0f, 801), precision: 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRunRate_MaxedSkills_UseGeneralFormula_VitaeDifferentiates()
|
||||
{
|
||||
// #266 regression: +Acdream at 33% vitae (eff 10200) vs +Je at 5%
|
||||
// vitae (eff 14463) — retail runs them within ~0.4% of each other
|
||||
// (~3.70 vs ~3.71), NOT at a flat 4.5. The old ">= 800 cap"
|
||||
// misread made both saturate identically and erased vitae entirely.
|
||||
// Tolerance asserts (not precision:) — 3.7125 straddles a 3-decimal
|
||||
// rounding boundary, the same xunit footgun as the AP-7 case.
|
||||
Assert.True(MathF.Abs(MovementSystem.GetRunRate(0f, 10200) - 3.6971f) < 1e-3f);
|
||||
Assert.True(MathF.Abs(MovementSystem.GetRunRate(0f, 14463) - 3.7125f) < 1e-3f);
|
||||
// InqMaxRunRate's skill=9999 probe: general formula, not 4.5.
|
||||
Assert.True(MathF.Abs(MovementSystem.GetRunRate(0f, 9999) - 3.6961f) < 1e-3f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue