fix(movement): invalidate burden on enchantment changes

This commit is contained in:
Erik 2026-07-31 10:16:27 +02:00
parent 2b9dfec9d7
commit d6e8b60303
13 changed files with 231 additions and 25 deletions

View file

@ -98,6 +98,43 @@ Copy this block when adding a new issue:
---
## #272 — Strength enchantments do not invalidate burden
**Status:** DONE — 2026-07-31 (implementation, automated gates, and user live
buff/death gate)
**Severity:** HIGH (movement state and retained HUD disagree with retail)
**Filed:** 2026-07-31
**Component:** player qualities / enchantments / burden
**Symptom:** while overburdened, casting a Strength spell did not reduce the
burden state until the base Strength attribute changed. Dying purged the
Strength spell but did not restore the overburdened state.
**Root cause:** `LiveSessionEventRouter.RecomputeBurden` read raw
`AttributeValue.Current` and subscribed only to base Strength/object-table
changes. The indicator bar and inventory meter had the same raw-Strength
composition, and the inventory meter did not observe enchantment changes.
Retail `CACQualities::InqLoad @ 0x0058F130` calls
`CACQualities::InqAttribute @ 0x00591A00`, which applies
`CACQualities::EnchantAttribute @ 0x00594570`; every load query therefore uses
effective Strength.
**Fix:** all three consumers now read
`LocalPlayerState.GetEffectiveAttribute(Strength)`. The Runtime burden owner,
indicator bar, and inventory meter subscribe to the canonical
`Spellbook.EnchantmentsChanged` edge, covering add, remove, expiration,
dispel, and death purge through one path. Regression tests pin both
buff-to-unburdened and purge-to-overburdened transitions without any base
attribute update.
**Acceptance:** overload a character, cast a Strength spell, and observe the
burden icon/meter plus movement update immediately. Die while the spell is
active and observe the spell purge restore the overburdened icon/meter and
movement immediately. **Passed live 2026-07-31:** the user confirmed the
burden state now updates correctly.
---
## #271 — Stair-side collision reverses uphill movement and rapidly slides the player down
**Status:** DONE — 2026-07-31 (implementation + user live gate)

View file

@ -53,7 +53,11 @@ TS-8 are retired by focused and end-to-end packet tests. #269's
capture-driven slope-slide residual is also closed and user-accepted:
`CTransition::validate_transition` now performs retail's non-OK-only
remembered-plane restore with the preceding `OBJECTINFO::kill_velocity`.
Campaign P now continues with the unfinished live matrix rows.
The matrix then exposed #272: burden was invalidated by base Strength but not
by Strength enchantment add/purge. Runtime movement plus both retained burden
surfaces now use effective Strength and the canonical enchantment-change edge;
automated gates pass and the connected buff/death gate was user-accepted on
2026-07-31. Campaign P then continues with the unfinished live matrix rows.
---

View file

@ -400,6 +400,14 @@ root-caused, retail-ported, and user-accepted in the same session:
and the complete Release suite passes 10,062 tests / 5 skips. The user
accepted repeated uphill runs while pressing into the stair sides. Evidence:
`docs/research/2026-07-31-271-stair-side-slide-capture.md`.
- **#272 complete and user-accepted 2026-07-31** —
`CACQualities::InqLoad` consumes enchantment-adjusted Strength through
`InqAttribute`, but Runtime movement and both retained burden displays read
raw Strength and did not share the enchantment invalidation edge. They now
consume `GetEffectiveAttribute(Strength)` and
`Spellbook.EnchantmentsChanged`, so buff, dispel, expiration, and death
purge recompute the same burden state immediately. Focused and full
Runtime/App tests pass.
Matrix rows accepted so far: speed parity, roof slide, downhill bounce,
flat pop, uphill landing, and #269's slope-stop feel

View file

@ -54,11 +54,16 @@ InqLoad(this, &loadOut):
return 1 // always succeeds for CACQualities (has vtable)
```
This EXACTLY matches acdream's existing `IndicatorBarController.UpdateBurden()`
/ `InventoryController.RefreshBurden()` pattern (Strength attribute + prop
0xE6 aug + prop 5 EncumbranceVal, falling back to `SumCarriedBurden` when the
wire value is absent) — already ported, already correct, already tested via
the UI. **`AcDream.Core.Items.BurdenMath`
The property/capacity shape matches acdream's
`IndicatorBarController.UpdateBurden()` /
`InventoryController.RefreshBurden()` pattern (Strength attribute + prop 0xE6
aug + prop 5 EncumbranceVal, falling back to `SumCarriedBurden` when the wire
value is absent). A 2026-07-31 connected gate exposed one omitted retail
detail: `InqAttribute` returns the enchantment-adjusted attribute, while all
three acdream burden consumers still read raw `AttributeValue.Current`.
Issue #272 corrects them to `LocalPlayerState.GetEffectiveAttribute(Strength)`
and invalidates burden on the canonical `Spellbook.EnchantmentsChanged` edge.
**`AcDream.Core.Items.BurdenMath`
(`EncumbranceCapacity`/`LoadRatio`/`LoadModifier`) is the SAME formulas at
the SAME addresses.** P1's `EncumbranceSystem` (Physics-namespaced, for
citation clarity next to `MovementSystem`) delegates to `BurdenMath` rather
@ -317,8 +322,10 @@ Runtime (AcDream.Runtime, presentation-free):
- onSkillsUpdated callback -> character.Character.UpdateMovementSkillBase(...)
- NEW: inventory.Objects.{ObjectAdded,ObjectUpdated,ObjectRemoved,ObjectMoved,
ContainerContentsReplaced,Cleared} + LocalPlayer.AttributeChanged(Strength)
+ Spellbook.EnchantmentsChanged
-> recompute burden (Strength + prop 0xE6 aug + prop 5 EncumbranceVal,
SAME shape as IndicatorBarController.UpdateBurden/InventoryController.RefreshBurden)
using effective/enchantment-adjusted Strength
-> character.Character.MovementSkills.UpdateBurden(ratio)
- NEW: character.Character.LocalPlayer.Changed(VitalKind.Stamina)
-> character.Character.MovementSkills.UpdateStamina(current)