acdream/memory/project_movement_collision_conformance.md

5.7 KiB

Movement & Collision Conformance Crib

Phase

Active phase: Campaign P — Physics Retail-Feel Parity (the L.2/R6 foundation is shipped).

Plan: docs/plans/2026-07-29-physics-parity-campaign.md.

Roadmap: docs/plans/2026-04-11-roadmap.md.

One-Sentence Framing

B.3 shipped the MVP resolver foundation; L.2 is the holistic conformance program for physics, collision, buildings, edge/wall sliding, cell ownership, movement packets, and server correction.

Active Movement Spine

InputDispatcher / PlayerMovementController
  -> MotionInterpreter + local body prediction
  -> PhysicsEngine.ResolveWithTransition
  -> TransitionTypes + BSPQuery + ShadowObjectRegistry
  -> ResolveResult contact/cell state
  -> MoveToState / AutonomousPosition outbound messages
  -> WorldSession server echo or correction diagnostics

Lane Ownership

  • L.2a: truth probes, diagnostics, fixture capture.
  • L.2b: movement wire/contact truth, cell id on packets, correction handling.
  • L.2c: transition parity, edge-slide, cliff-slide, precipice-slide, NegPolyHit.
  • L.2d: CSphere / CCylSphere, live entity shapes, building object identity.
  • L.2e: outdoor seams, CELLARRAY, find_cell_list, adjacent-cell checks, cell_bsp, building entry/exit boundaries.
  • L.2f: real-DAT fixtures and live retail-observer conformance.

Non-Negotiables

  • Grep named retail before changing AC-specific physics or movement behavior.
  • Do not treat ACE accepting a position as proof of retail collision parity.
  • Do not reintroduce rewrite-from-zero collision guidance. Continue the partial retail port by L.2 lanes.
  • G.3 dungeon/portal delivery waits on L.2e for trustworthy cell/building ownership.
  • L.1 animation work must coordinate with L.2 when root motion or observer movement changes the predicted body path.

Shipped Slices

  • 2026-04-29: L.2a/L.2b first diagnostic slice. ACDREAM_DUMP_MOVE_TRUTH=1 logs move-truth OUT for outbound MoveToState / AutonomousPosition and move-truth ECHO for player UpdatePosition echoes, including local/server delta. GameWindow now passes explicit grounded/airborne contact bytes from MovementResult.IsOnGround to both movement packet builders.
  • 2026-04-29: L.2e first cell-ownership fix. ResolveWithTransition refreshes outdoor cell ownership from world position during the sphere sweep, so 24m outdoor seams update low cell ids and full-cell callers crossing landblock seams get the destination landblock prefix plus the correct outdoor low cell.
  • 2026-04-30: L.2c edge-slide plumbing. User live-tested wall-adjacent slide as acceptable. Local player and remote dead-reckoning now pass retail-default ObjectInfoState.EdgeSlide; ACDREAM_DUMP_EDGE_SLIDE=1 logs failed step-down edge cases and now reports whether walkable polygon context is present before cliff/precipice handling.
  • 2026-04-30: L.2c precipice-slide context. Named retail SPHEREPATH::precipice_slide and ACE Polygon.find_crossed_edge are now captured in docs/research/2026-04-30-precipice-slide-pseudocode.md. Terrain supplies exact walkable triangle vertices, BSP step-down/find-walkable stores world-space walkable vertices for static object tops, and failed step-down edge cases run the retail back-probe before precipice slide. cliff_slide has a first port, but NegPolyHit, CELLARRAY, full cell_bsp, and real-DAT building portal conformance remain open L.2 work.
  • 2026-04-30: Edge-slide retry-loop lesson. SPHEREPATH::precipice_slide usually returns Slid after applying the tangent offset. That result must be handled inside TransitionalInsert like wall slide (continue and re-test the adjusted CheckPos), not returned to ValidateTransition; the outer validator treats non-OK as a collision and restores CurPos, making edges feel like hard stops even when the tangent was computed.
  • 2026-07-31: Campaign P #269 slope-stop parity. A 2,184-quantum live capture proved the 5% landing reflect and calc_friction were correct, but ValidateTransition restored LastKnownContactPlane without retail's preceding OBJECTINFO::kill_velocity. Retail consumes that plane only on a non-OK recovery and overwrites last-known validity from the final contact plane. Both rules are now ported; focused/full tests and the user's repeated slope-jump gate pass. See docs/research/2026-07-31-269-slope-stop-capture.md.
  • 2026-07-31: #271 stair-side uphill reversal. Never promote ACDream's retained LastWalkable history into retail's current SPHEREPATH::walkable slot inside edge_slide. When current walkable is null, retail CTransition::edge_slide @ 0x0050B3D0 must back-probe at the current sphere center and either use that newly discovered polygon or collide. The stale substitution selected a preceding stair tread, reversed the uphill tangent, and dropped the player rapidly down the stairs. The exact live frame is pinned in Issue185OutdoorStairsSeamReplayTests; Core passes 4,108 / 2 skips and the complete Release solution passes 10,062 / 5 skips. The user accepted repeated uphill stair-side runs on 2026-07-31. See docs/research/2026-07-31-271-stair-side-slide-capture.md.
  • 2026-07-31: #272 burden invalidation. Retail InqLoad @ 0x0058F130 obtains Strength through InqAttribute @ 0x00591A00, so carry capacity always uses the enchantment-adjusted value. Runtime movement, the indicator bar, and the inventory meter must all consume LocalPlayerState.GetEffectiveAttribute(Strength) and react to the same Spellbook.EnchantmentsChanged event. That one event covers spell add, remove, expiration, dispel, and death purge. Never refresh this through a UI-only workaround or wait for a base-attribute packet. The connected buff/death gate was user-accepted on 2026-07-31.