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>
All seven review lenses pass. CanJump's polarity is upgraded from
plausibility to proof: raw bytes of 0x00591b50 show fld load / fcomp
[0x007c5e24 = 2.0f] / test ah,5 / jp -> return 0, i.e. return 1 iff
load < 2.0 with unordered refusing - exactly the shipped code, NaN edge
included. UN-8 deleted. CACQualities::JumpStaminaCost's pk flag decoded
for P3: PlayerKillerStatus in {4,0x40} AND PropertyFloat 0x91 + 20 s >=
now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ports the retail CACQualities/EncumbranceSystem/MovementSystem chain
(named-retail decomp pc 256393/412901-414050/416169-416320/695958+) so
PlayerWeenie's run rate, jump height, jump permission, and jump stamina
cost are real functions of burden, current stamina, and vitae/skill
enchantments instead of stubs.
Core:
- New EncumbranceSystem.cs (delegates to the already-verified
BurdenMath formulas — one source of truth for the burden HUD and
movement physics) and MovementSystem.cs (GetRunRate/GetJumpHeight/
JumpStaminaCost/GetJumpPower, decomp-cited; ACE cross-referenced
where BN dropped the general-case arithmetic entirely).
- PlayerWeenie rewritten as the CACQualities-shaped composition:
CanJump gates on burden (<2.0 load, UN-8 — x87 polarity resolved by
plausibility, Ghidra MCP unavailable this slice), JumpStaminaCost
returns the real ceil((load+0.5)*power*8+2) cost and always affords
it (matches decomp — retail's own function never refuses; "weak"
jump comes entirely from the stamina==0 skill-zeroing gate inside
InqRunRate/InqJumpVelocity, not a hard refusal), SetStamina wires a
null="unknown, don't gate" sentinel preserving every pre-P1 test.
- EnchantmentMath.GetMod gained an optional StatModType flag filter
(GetSkillMod convenience wrapper) so the SAME vitae/family-stacking
machinery already used for vital-max buffs now also answers "what's
the vitae+skill-enchantment-adjusted Run/Jump skill" — reusing the
M3 active-enchantment state, not a new engine.
Runtime:
- RuntimeCharacterState now stores the pre-EnchantSkill base run/jump
skill and recomputes the adjusted value (vitae first, then matching
Skill-flagged buffs, floor 0.5, truncate) on every base push AND on
every Spellbook.EnchantmentsChanged notification — a vitae change
alone moves the produced rate without a fresh PlayerDescription.
- RuntimeMovementSkillState extended with Burden/CurrentStamina
(RuntimeMovementSkillProjection.ApplyTo pushes both through the
existing seam); LiveSessionEventRouter recomputes burden from the
same Strength+aug-property+EncumbranceVal inputs the burden HUD
already assembles (reacting to the same ClientObjectTable events)
and pushes current stamina from LocalPlayerState vital updates.
- Wires the previously dead-lettered ReportExhaustion() R3-W4 seam:
LiveSessionRuntimeFactory's OnMovementStatsUpdated callback re-
applies the current snapshot to the live controller and forces an
immediate movement re-evaluation on any skill/burden/stamina change.
Register: retires TS-5 (CanJump/JumpStaminaCost stubs) and AP-25 (no
vitae in pushed skill). Adds AP-127 (two minor unmodeled retail bonus
properties + the stamina-buff-adjusts-local-copy nuance, deliberately
out of the bounded "run/jump query path only" scope) and UN-8 (the
CanJump x87 polarity call, flagged for a future Ghidra MCP
confirmation pass). Extends TS-23 (PlayerKillerStatus not parsed) to
cover JumpStaminaCost's new pk parameter, hardcoded false pending P3.
Full pseudocode + retail citations + the vitae/skill-level finding in
docs/research/2026-07-30-stat-coupled-movement-pseudocode.md.
Release suite: Core.Tests 3977/2 skips, Runtime.Tests 425/0 skips,
App.Tests 3968/3 skips — all green. (One pre-existing, unrelated Debug-
only flake in LandblockBuildOriginTests reproduces on the pre-P1
baseline and passes in Release; not touched here.)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>