fix(physics): port retail slope landing stop

This commit is contained in:
Erik 2026-07-31 09:10:53 +02:00
parent 1d8371dbe5
commit 5a0f9868a6
13 changed files with 870 additions and 103 deletions

View file

@ -142,7 +142,7 @@ remain in place (ride ACDREAM_DUMP_MOTION=1) until the user gate passes.
## #269 — Slope-stop slide runs too far (post-bounce-rework residual)
**Status:** OPEN (user live gate 2026-07-30 — "almost pass with merits")
**Status:** DONE — 2026-07-31 (implementation + user live gate)
**Severity:** LOW-MEDIUM (feel residual; bounce family otherwise accepted)
**Component:** physics / landing slide decay
@ -162,14 +162,24 @@ cos(10°) with base 0.2); the jump chain end-to-end (`GetJumpHeight`
Sledding auto-toggle (P2 finding re-confirmed; no `state |= 0x800000`
writer exists).
**Next step (behavior question, not code):** live A/B per the cdb
workflow — breakpoints on `calc_friction`/`handle_all_collisions` dumping
velocity per tick while the retail client lands + slides on a slope, vs an
`ACDREAM_CAPTURE_RESOLVE` capture of the same maneuver on the same slope.
Compare the decay curves; the first diverging tick names the mechanism.
Candidates the trace discriminates: hop-chain cadence (friction skips
airborne micro-hop ticks), gravity-tangential rebuild timing, quantum
alignment.
**Resolution (2026-07-31):** a 2,184-quantum live capture isolated the
first divergence. The landing tick produced retail's correct 5% reflect,
but the following quanta repeatedly restored
`LastKnownContactPlane` while retaining the reflected velocity. The body
therefore remained Contact + OnWalkable with `v·n > 0.25`, where retail
`calc_friction` intentionally does no work, and slid at full speed.
Named-retail `CTransition::validate_transition @ 0x0050AA70` revealed the
omission: its non-OK remembered-plane recovery calls
`OBJECTINFO::kill_velocity @ 0x0050CFE0` *before* the proximity test and
plane restore (`0x0050AAED0x0050AB42`). It also consumes the remembered
plane only in that non-OK branch and overwrites last-known validity from
the final contact plane at `0x0050ACFF`. ACDream now follows that exact
ordering. Focused collision-recovery and clean-advance pins pass; full
Core (4,107/2 skips) and Runtime (439/0) suites pass; the user repeated the
slope-jump test and accepted the result (“Perfect! Works great!”).
Capture and decode:
`docs/research/2026-07-31-269-slope-stop-capture.md`.
---

View file

@ -46,12 +46,14 @@ a ledger pass. Goal: zero physics TS rows, no unargued feel-affecting AP
rows, one final batched connected visual matrix. Sonnet implements, Opus
reviews. The plan is
[`2026-07-29-physics-parity-campaign.md`](2026-07-29-physics-parity-campaign.md).
The 2026-07-31 #268 stat-chain package is implemented pending its live visual
gate: panel and Runtime movement share retail's complete augmentation ordering,
The 2026-07-31 #268 stat-chain package is implemented and user-accepted:
panel and Runtime movement share retail's complete augmentation ordering,
the authored per-fragment vitae/buff/debuff colors are live, and AP-127 plus
TS-8 are retired by focused and end-to-end packet tests. The remaining
implementation target is #269's capture-driven slope-slide feel residual,
followed by the unfinished live matrix rows.
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.
---

View file

@ -380,13 +380,19 @@ root-caused, retail-ported, and user-accepted in the same session:
authored 0x1B palette exactly: #7FFFFF vitae, #00FF00 buff, #FF0000
debuff. TS-8 is also retired: a real live 0x02C2 payload carries its full
StatMod through dispatch and changes the effective skill immediately.
- **#269 filed** — slope-stop slide sometimes runs long. `calc_friction`
(0x0050ee70) and the complete jump chain (`GetJumpHeight` 0x006b09b0,
`InqJumpVelocity` vz=sqrt(h·19.6), 1.0 s/0.8 s powerbar charge) are
byte-verified identical — the user's jump-height hypothesis is
refuted; next step is the live cdb A/B decay-curve trace.
- **#269 closed 2026-07-31** — the live 2,184-quantum trace proved the
landing reflect and friction math were correct. ACDream omitted retail's
`OBJECTINFO::kill_velocity` before restoring a remembered contact plane
in `CTransition::validate_transition @ 0x0050AA70`, retaining full
downhill velocity while repeatedly re-grounding the mover. The exact
non-OK-only restore/kill order and final last-known validity overwrite
are now ported, focused/full gates pass, and the user accepted repeated
slope jumps. Evidence:
`docs/research/2026-07-31-269-slope-stop-capture.md`.
Matrix rows accepted so far: speed parity, roof slide, downhill bounce,
flat pop, uphill landing (rows 3/4/5-partial/12-partial). Remaining
rows: 1, 2, 6, 7, 8, 9, 10, 11 plus #269's slide feel. Suite at this
checkpoint: 10,031 passed / 5 skips / 0 failures.
flat pop, uphill landing, and #269's slope-stop feel
(rows 3/4/5-partial/12-partial). Remaining rows: 1, 2, 6, 7, 8, 9, 10,
and 11. The #269 checkpoint passes 4,107 Core tests / 2 skips and 439
Runtime tests / 0 skips; the complete Release suite passes 10,061 tests /
5 skips / 0 failures.

View file

@ -0,0 +1,104 @@
# Issue #269 — slope-stop capture and retail correction
**Date:** 2026-07-31
**Status:** implemented; user live gate passed
**Scope:** landing-bounce follow-up, `CTransition::validate_transition`
## Symptom
After the retail 5%-elasticity landing reflection was restored for #265,
the character could retain too much downhill speed after landing on a
walkable slope. The user described the residual as “slides too far on
landing.”
## ACDream live capture
`ACDREAM_CAPTURE_PLAYER_QUANTA=<jsonl-path>` records the local player's
complete admitted object quantum without changing simulation order:
1. quantum start;
2. root/PositionManager composition;
3. pre- and post-`UpdatePhysicsInternal`;
4. transition result;
5. final collision-response commit.
The accepted repro contained 2,184 quanta. The clearest landing was:
| Quantum | Event | Velocity |
|---|---|---|
| 1740 | final airborne quantum | `(-12.316, 8.187, -26.266)` |
| 1741 | slope collision, normal `(-0.236, 0.236, 0.943)` | |
| 1741 post-response | correct 5% reflect | `(-17.391, 13.262, -6.576)` |
| 17421758 | still Contact + OnWalkable, no new collision normal | velocity unchanged |
| 1759+ | contact relationship changes | friction finally begins decaying |
The reflected velocity had `dot(v, normal) = +1.0252`: it pointed away
from the slope. Retail `calc_friction` correctly skips while this value is
at least `0.25`, so friction was not the defect. ACDream was repeatedly
restoring the remembered slope plane and re-grounding the body without
performing retail's accompanying velocity stop.
## Retail oracle
Named-retail:
- `CPhysicsObj::check_contact` `0x0050F5B0`
- `CPhysicsObj::get_object_info` `0x00511CC0`
- `CTransition::validate_transition` `0x0050AA70`
- `OBJECTINFO::kill_velocity` `0x0050CFE0`
The exact `validate_transition` order at
`0x0050AAED0x0050AB42` is:
1. enter only for a non-OK collision/adjusted/slid result;
2. if `last_known_contact_plane_valid`, call
`OBJECTINFO::kill_velocity`;
3. test the current sphere center against the remembered plane using
`radius + 0.0002`;
4. restore the contact plane only when still within that distance;
5. later, at `0x0050ACFF`, overwrite last-known validity with final
contact-plane validity.
`OBJECTINFO::kill_velocity` calls
`CPhysicsObj::set_velocity({0,0,0}, 0)`. ACDream had ported the proximity
test and plane restore but omitted this call. It also allowed the
last-known plane to re-ground clean accepted moves, although retail only
consumes it in the non-OK recovery branch.
## Correction
`Transition.ValidateTransition` now:
- calls `ObjectInfo.StopVelocity()` before the remembered-plane
proximity/restore test on a non-OK recovery;
- performs that restore only in the retail branch;
- overwrites last-known validity from final contact validity, so a clean
move away cannot be re-grounded from stale memory.
The existing `PhysicsEngine.ResolveWithTransition` consumption of
`VelocityKilled` applies the zero to the canonical `PhysicsBody` before
the collision-response tail. The initial 5% landing reflection remains;
only a following collision recovery performs the retail stop.
## Gates
- New focused pins:
- collision recovery with a remembered plane kills velocity;
- clean advance with a remembered plane neither kills nor re-grounds.
- Full `AcDream.Core.Tests`: 4,107 passed / 2 skipped.
- Full `AcDream.Runtime.Tests`: 439 passed.
- `AcDream.App` Release build: 0 warnings / 0 errors.
- Complete Release suite: 10,061 passed / 5 skipped / 0 failed.
- User live gate: **PASS** — repeated slope jumps now settle correctly
(“Perfect! Works great!”).
## Diagnostic tools retained
- `tools/analyze_269_slope_stop_capture.py`
- `tools/cdb/run-issue269-slope-stop.ps1`
- `tools/cdb/issue269-slope-stop.cdb`
The cdb runner refuses to attach unless the live retail executable matches
the Sept 2013 named PDB. The locally installed 2015 retail executable does
not match; the static named-retail decode above is therefore the retail
oracle used for this correction.