docs(physics): Phase L.2 movement collision conformance plan
Formalize Phase L.2 as the active holistic movement/collision program, align the roadmap and architecture docs, file tactical physics follow-ups, and refresh collision memory away from rewrite-from-zero guidance. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
e44d24cec6
commit
d4c3f947d2
6 changed files with 689 additions and 242 deletions
208
docs/plans/2026-04-29-movement-collision-conformance.md
Normal file
208
docs/plans/2026-04-29-movement-collision-conformance.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# Phase L.2 - Movement & Collision Conformance
|
||||
|
||||
**Status:** ACTIVE planning document, created 2026-04-29.
|
||||
**Roadmap owner:** Phase L.2 in `docs/plans/2026-04-11-roadmap.md`.
|
||||
**Scope:** player movement prediction, retail collision/transition behavior,
|
||||
building boundaries, edge and wall sliding, cell ownership, outbound movement
|
||||
packets, and server-correction diagnostics.
|
||||
|
||||
## Purpose
|
||||
|
||||
Phase B.3 shipped the first usable physics foundation: terrain contact,
|
||||
basic resolver behavior, streaming-populated collision inputs, and enough
|
||||
movement wire support to walk on ACE. That was not the complete retail
|
||||
collision system.
|
||||
|
||||
Phase L.2 is the conformance program that turns that foundation into a
|
||||
retail-faithful movement stack. It is the single organizing bucket for work
|
||||
that otherwise looks scattered across B.3 physics, L.1 animation/motion, and
|
||||
G.3 dungeon/portal space.
|
||||
|
||||
The active movement spine is:
|
||||
|
||||
```text
|
||||
input + motion command
|
||||
-> local body prediction / root-motion source
|
||||
-> PhysicsEngine.ResolveWithTransition
|
||||
-> TransitionTypes + BSPQuery + ShadowObjectRegistry contact/cell result
|
||||
-> MoveToState / AutonomousPosition outbound packets
|
||||
-> server echo or correction diagnostics
|
||||
```
|
||||
|
||||
Live ACE accepting a position, or the absence of visible rubber-banding, is
|
||||
not proof of retail collision parity. ACE can tolerate coarse or locally
|
||||
invalid fine-grained movement. L.2 therefore requires retail-decomp evidence,
|
||||
synthetic conformance tests, real-DAT fixtures, and live retail-observer checks.
|
||||
|
||||
## Current Foundation
|
||||
|
||||
Already active in acdream:
|
||||
|
||||
- `PhysicsEngine.ResolveWithTransition` is the local player collision path.
|
||||
- `BSPQuery` contains a partial retail-style BSP dispatcher and step/contact
|
||||
logic.
|
||||
- `TransitionTypes` carries `SpherePath`, `CollisionInfo`, `ObjectInfo`,
|
||||
transition validation, step-up/down, and partial slide behavior.
|
||||
- `PhysicsDataCache` loads GfxObj, Setup, and CellStruct physics data from DATs.
|
||||
- `ShadowObjectRegistry` gives the resolver a broadphase over nearby world
|
||||
objects.
|
||||
- `TerrainSurface` uses triangle-aware terrain sampling rather than the older
|
||||
bilinear placeholder.
|
||||
|
||||
Known incomplete areas:
|
||||
|
||||
- Full `CELLARRAY` ownership and `CObjCell::find_cell_list` / adjacent-cell
|
||||
checks are not ported.
|
||||
- `cell_bsp` / `CellBSP` is not fully represented as a first-class runtime
|
||||
owner.
|
||||
- Building entry/exit and indoor/outdoor portal transit are not solved by the
|
||||
normal walking path.
|
||||
- Retail `edge_slide`, `cliff_slide`, and `precipice_slide` behavior is
|
||||
incomplete; failed edge/step-down cases often hard-block instead of sliding.
|
||||
- `NegPolyHit` handling is a stub relative to the retail transition dispatch.
|
||||
- Live entities collapse to a simplified cylinder shape; exact retail
|
||||
sphere/cylsphere and object-shape behavior is not yet matched.
|
||||
- Outbound contact/cell fields can be too optimistic, so server agreement does
|
||||
not necessarily mean local conformance.
|
||||
|
||||
## Lane Model
|
||||
|
||||
L.2 uses five working lanes. The roadmap breaks them into six sub-lanes because
|
||||
real-DAT and live verification spans every lane.
|
||||
|
||||
| Lane | Owns | Roadmap slice |
|
||||
|---|---|---|
|
||||
| Diagnostics | Truth probes, dump flags, server-correction logging, retail observer harness | L.2a, L.2f |
|
||||
| Transition parity | `FindTransitionalPosition`, step-up/down, edge-slide, cliff-slide, precipice-slide, `NegPolyHit` dispatch | L.2c |
|
||||
| Geometry fidelity | `CSphere`, `CCylSphere`, object shape extraction, building object collision, walkable polygon context | L.2d |
|
||||
| Cell/building ownership | outdoor cell seams, low-cell id updates, `CELLARRAY`, `cell_bsp`, building entry/exit | L.2e |
|
||||
| Movement/network authority | contact byte, full cell id, MoveToState / AutonomousPosition cadence, root motion vs velocity prediction, correction response | L.2b, L.2f |
|
||||
|
||||
## Roadmap Slices
|
||||
|
||||
### L.2a - Truth & Diagnostics
|
||||
|
||||
Goal: make every bad movement outcome explainable.
|
||||
|
||||
- Add targeted diagnostics for local placement, contact plane, object hit,
|
||||
water, cell id, outbound packet fields, server echo, and correction delta.
|
||||
- Keep diagnostics opt-in via env vars and devtools panels.
|
||||
- Record enough data for side-by-side retail-observer runs without drowning
|
||||
normal logs.
|
||||
- Build real-DAT fixture capture for known walls, building ledges, rooftops,
|
||||
slopes, landblock seams, and dungeon entrances.
|
||||
|
||||
### L.2b - Movement Wire / Contact Authority
|
||||
|
||||
Goal: stop sending movement packets that claim more certainty than the local
|
||||
resolver has earned.
|
||||
|
||||
- Fix outbound contact state so `AutonomousPosition` and `MoveToState` do not
|
||||
always claim grounded contact.
|
||||
- Track local result cell id and outbound full cell id separately from the last
|
||||
server placement until correction proves they agree.
|
||||
- Reconcile packet cadence with retail/holtburger references.
|
||||
- Wire routine server correction handling and diagnostics, not only portal
|
||||
reseating.
|
||||
|
||||
### L.2c - Transition Parity: Edge / Slide / Neg-Poly
|
||||
|
||||
Goal: match retail movement at walls, roof edges, step boundaries, and
|
||||
precipices.
|
||||
|
||||
- Port and test `edge_slide`, `cliff_slide`, `precipice_slide`, and
|
||||
`step_up_slide` behavior from named retail.
|
||||
- Preserve walkable polygon context needed for precipice/edge decisions.
|
||||
- Replace `NegPolyHit` stub behavior with the retail dispatch path.
|
||||
- Confirm the user-visible rule: walk-only motion is blocked by step,
|
||||
edge, walkable, and collision rules; jumping clears `OnWalkable` and only
|
||||
succeeds when the airborne path actually clears geometry.
|
||||
|
||||
### L.2d - Shape Fidelity: Sphere / CylSphere / Building Objects
|
||||
|
||||
Goal: object collisions use retail shape semantics, not one simplified
|
||||
fallback.
|
||||
|
||||
- Finish `CSphere` / `CCylSphere` parity for static and live objects.
|
||||
- Stop treating all live entities as one root-centered cylinder.
|
||||
- Preserve enough building identity to model `CBuildingObj` collision and
|
||||
`bldg_check` behavior.
|
||||
- Audit `Setup.Radius` and cylinder fallback behavior against retail before
|
||||
relying on them for conformance.
|
||||
|
||||
### L.2e - Cell Ownership: Outdoor Seams, CELLARRAY, cell_bsp
|
||||
|
||||
Goal: the resolver knows which cell owns the movement and which adjacent cells
|
||||
must be checked.
|
||||
|
||||
- Update low outdoor cell id across 24m cell boundaries and landblock seams.
|
||||
- Port the retail adjacent-cell search: `find_cell_list`, `check_other_cells`,
|
||||
and `adjust_check_pos`.
|
||||
- Promote `cell_bsp` / `CellBSP` from partial data to active runtime owner.
|
||||
- Hand G.3 a trustworthy building/portal boundary so dungeon streaming is not
|
||||
asked to solve collision ownership after the fact.
|
||||
|
||||
### L.2f - Real-DAT and Live Retail-Observer Conformance
|
||||
|
||||
Goal: prove the stack against real terrain/building/cell data and what a retail
|
||||
client sees when observing acdream.
|
||||
|
||||
- Add real-DAT fixtures for representative movement cases.
|
||||
- Use retail client observer runs to verify motion packets, animation/movement
|
||||
coupling, and server-visible placement.
|
||||
- Treat ACE acceptance as a coarse compatibility check only.
|
||||
- Require conformance notes in tests or research docs for every AC-specific
|
||||
algorithm ported under L.2.
|
||||
|
||||
## Named Retail Anchors
|
||||
|
||||
Primary source: `docs/research/named-retail/acclient_2013_pseudo_c.txt`.
|
||||
Struct source: `docs/research/named-retail/acclient.h`.
|
||||
Address lookup: `docs/research/named-retail/symbols.json`.
|
||||
|
||||
Use these names before falling back to older `docs/research/decompiled/`
|
||||
chunks:
|
||||
|
||||
- `CTransition::find_transitional_position` - `0x0050BDF0`
|
||||
- `CTransition::transitional_insert` - `0x0050B6F0`
|
||||
- `CTransition::step_up` - `0x0050B610`
|
||||
- `CTransition::step_down` - `0x0050B2A0`
|
||||
- `CTransition::edge_slide` - `0x0050B3D0`
|
||||
- `CTransition::cliff_slide` - `0x0050A6D0`
|
||||
- `SPHEREPATH::step_up_slide` - `0x0050C3B0`
|
||||
- `SPHEREPATH::precipice_slide` - `0x0050CC80`
|
||||
- `SPHEREPATH::adjust_check_pos` - `0x0050CC00`
|
||||
- `CTransition::adjust_offset` - `0x0050A370`
|
||||
- `CTransition::check_other_cells` - `0x0050AE50`
|
||||
- `CPhysicsObj::is_valid_walkable` - `0x0050F530`
|
||||
- `CObjCell::find_cell_list` - `0x0052B4E0`
|
||||
- `CBuildingObj::find_building_collisions`
|
||||
- `CCellStruct::point_in_cell`
|
||||
- `CCellStruct::sphere_intersects_cell`
|
||||
- `CCellStruct::box_intersects_cell`
|
||||
- `CCylSphere::intersects_sphere`
|
||||
- `CSphere::intersects_sphere`
|
||||
- `CSphere::slide_sphere`
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Land L.2a diagnostics first. Do not make another physics change blind.
|
||||
2. Fix L.2b packet/contact truth so logs and server echoes describe reality.
|
||||
3. Port L.2c transition parity in narrow slices with named-retail citations and
|
||||
conformance tests.
|
||||
4. Improve L.2d shape fidelity where transition parity depends on object
|
||||
contact semantics.
|
||||
5. Land L.2e cell/building ownership before G.3 dungeon/portal work relies on
|
||||
indoor/outdoor walking.
|
||||
6. Promote each synthetic case to L.2f real-DAT and live observer coverage.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- A developer can name the active movement path and the current incomplete
|
||||
pieces without reading old chat logs.
|
||||
- `dotnet build` and `dotnet test` stay green for each implementation slice.
|
||||
- Every AC-specific port cites named retail decomp or a documented fallback.
|
||||
- Real-DAT fixtures cover buildings, walls, roof edges, outdoor seams, and at
|
||||
least one dungeon/building entrance path before L.2 is marked shipped.
|
||||
- Retail observer view and acdream local view both agree on contact, position,
|
||||
and movement state for the representative cases.
|
||||
Loading…
Add table
Add a link
Reference in a new issue