acdream/docs/research/2026-07-31-269-slope-stop-capture.md

4 KiB
Raw Blame History

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.