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
|
|
@ -1,6 +1,6 @@
|
|||
# acdream — strategic roadmap
|
||||
|
||||
**Status:** Living document. Updated 2026-04-11 after Phase 6, 7.1, 9.1, 9.2 landed.
|
||||
**Status:** Living document. Updated 2026-04-29 for Phase L.2 movement/collision conformance planning.
|
||||
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
|
||||
|
||||
---
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
| A.1 | Streaming landblock loader — runtime-configurable visible window (default 5×5, `ACDREAM_STREAM_RADIUS`), camera-centered offline / player-centered live, hysteresis-based unloads, pending-spawn list for late CreateObject events | Live ✓ |
|
||||
| A.2 | Frustum culling — per-landblock AABB test (Gribb-Hartmann), terrain + static-mesh renderers skip culled landblocks, perf overlay in window title | Visual ✓ |
|
||||
| A.3 | Background net receive thread — dedicated daemon thread buffers UDP into Channel, render thread drains | Visual ✓ |
|
||||
| B.3 | Physics collision engine — TerrainSurface (heightmap Z), CellSurface (indoor floor polygon projection), PhysicsEngine (resolver with step-height + cell transitions). Populated from streaming pipeline. | Tests ✓ |
|
||||
| B.3 | Physics MVP resolver foundation — terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. Not the complete retail collision system. | Tests ✓ |
|
||||
| B.2 | Player movement mode — Tab-toggled WASD ground walking, walk/run/idle animations, third-person chase camera, MoveToState + AutonomousPosition outbound, portal entry. Outdoor-only MVP. | Live ✓ |
|
||||
| D.1 | 2D ortho overlay + font rendering (StbTrueTypeSharp atlas + TextRenderer + DebugOverlay) | Visual ✓ |
|
||||
| E.1 | Motion-hook expansion — AnimationSequencer fires all 27 hook types per crossed frame; PosFrames root motion + vel/omega exposure; IAnimationHookSink + AnimationHookRouter fan-out | Tests ✓ |
|
||||
|
|
@ -94,7 +94,7 @@ Plus polish that doesn't get its own phase number:
|
|||
**Sub-pieces:**
|
||||
- **✓ SHIPPED — B.1 — Outbound ack pump.** Shipped as Phase 4.9 — per-packet ACK_SEQUENCE, not periodic. Server no longer drops idle clients.
|
||||
- **✓ SHIPPED — B.2 — Player movement mode.** Tab-toggled WASD ground walking with collision-resolved outdoor terrain, walk/run/idle/turn-right animations, third-person chase camera, outbound MoveToState (0xF61C) + AutonomousPosition (0xF753) server messages, portal entry works. Outdoor→indoor transition disabled for MVP (CellSurface floor polygons too aggressive without portal-based detection). Minor polish remaining: strafe animation, turn-left animation. Spec: `docs/superpowers/specs/2026-04-12-player-movement-design.md`.
|
||||
- **✓ SHIPPED — B.3 — Physics collision engine.** TerrainSurface (heightmap bilinear Z), CellSurface (indoor floor polygon projection via barycentric interpolation), PhysicsEngine (top-level resolver with step-height enforcement, outdoor↔indoor cell transitions, gravity reporting). Populated from streaming pipeline. 16 unit tests with fake data. Spec: `docs/superpowers/specs/2026-04-12-physics-collision-engine-design.md`.
|
||||
- **✓ SHIPPED — B.3 — Physics MVP resolver foundation.** Terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. This shipped enough foundation for outdoor walking and early portal experiments, but it is not the complete retail collision system. Current conformance work lives under **Phase L.2 — Movement & Collision Conformance**. Spec history: `docs/superpowers/specs/2026-04-12-physics-collision-engine-design.md`.
|
||||
- **B.4 — `Use` / `UseWithTarget` / `PickUp`.** Outbound interaction messages. Drives opening doors, looting, talking to vendors.
|
||||
- **B.5 — Chat.** `SendTell`, `SendChat` outbound + receive/display inbound (display side depends on Phase D.1).
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ Research: R9 + R12 + R13.
|
|||
|
||||
- **✓ SHIPPED — G.1 — Sky + weather + day-night.** Deterministic client-side from Portal Year time. Sky dome geometry + keyframe gradients + rain/snow particles. See `r12-weather-daynight.md`. Full data + visual stack shipped: Region dat loader, keyframe interp, WeatherSystem with 5-kind PDF + transitions + storm flashes, WorldSession→WorldTimeService sync via ConnectRequest+TimeSync, SkyRenderer with sky-object arcs + UV scroll, rain/snow billboard renderer, F7/F10 debug cycle keys.
|
||||
- **✓ SHIPPED — G.2 — Dynamic lighting.** 8-light D3D-style fixed pipeline. Hard-cutoff at Range, no attenuation inside. Cell ambient. Shader UBO per frame. See `r13-dynamic-lighting.md`. SceneLightingUbo std140 at binding=1 feeds terrain + mesh + mesh_instanced + sky shaders. LightingHookSink auto-registers Setup.Lights at entity stream-in, flips IsLit on SetLightHook, unregisters on landblock unload.
|
||||
- **G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with `LoginComplete` re-send, "pink bubble" loading state. See `r09-dungeon-portal-space.md`.
|
||||
- **G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with `LoginComplete` re-send, "pink bubble" loading state. **Blocked on L.2e** for trustworthy `cell_bsp`, indoor/outdoor portal transit, adjacent-cell ownership, and building entry/exit collision boundaries. See `r09-dungeon-portal-space.md`.
|
||||
|
||||
**Acceptance:** walk outside at dusk, see the sky gradient + sun moving; enter a torch-lit dungeon via portal; leave back to daylight.
|
||||
|
||||
|
|
@ -318,6 +318,11 @@ queues, speed scaling, and PosFrame root motion.
|
|||
|
||||
**Plan of record:** `docs/plans/animation-system-audit.md`.
|
||||
|
||||
**Coupling to L.2:** L.1 owns animation/motion parity. L.2 owns collision,
|
||||
contact truth, movement packets, and server-visible placement. They meet where
|
||||
root motion or observer movement changes the predicted body path; any such
|
||||
change must keep both phase plans in sync.
|
||||
|
||||
**Sub-pieces:**
|
||||
- **L.1a — Audit & inventory.** Map retail named-decomp evidence, ACE
|
||||
cross-references, existing acdream hook points, and current gaps for each
|
||||
|
|
@ -349,6 +354,55 @@ queues, speed scaling, and PosFrame root motion.
|
|||
|
||||
---
|
||||
|
||||
### Phase L.2 — Movement & Collision Conformance
|
||||
|
||||
**Status:** ACTIVE.
|
||||
|
||||
**Goal:** make acdream's movement and collision behavior retail-faithful across
|
||||
terrain, buildings, walls, roof edges, cell seams, portal boundaries, outbound
|
||||
movement packets, and server correction. This is the holistic bucket for the
|
||||
work previously scattered across B.3 physics follow-ups, L.1 motion coupling,
|
||||
and G.3 dungeon/portal ownership.
|
||||
|
||||
**Plan of record:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
|
||||
|
||||
**Current foundation:** `PhysicsEngine.ResolveWithTransition`,
|
||||
`BSPQuery`, `TransitionTypes`, `PhysicsDataCache`, and
|
||||
`ShadowObjectRegistry` exist and are active. They are partial retail ports and
|
||||
diagnostic scaffolding, not yet the final collision system.
|
||||
|
||||
**Sub-lanes:**
|
||||
- **L.2a — Truth & diagnostics.** Local placement/contact/cell logs, object-hit
|
||||
probes, correction-delta diagnostics, retail-observer capture workflow, and
|
||||
real-DAT fixture capture.
|
||||
- **L.2b — Movement wire/contact authority.** Fix outbound contact truth,
|
||||
full-cell id handling, packet cadence, and routine server correction handling.
|
||||
- **L.2c — Transition parity: edge/slide/neg-poly.** Port and test retail
|
||||
`edge_slide`, `cliff_slide`, `precipice_slide`, step-up/down slide, and
|
||||
`NegPolyHit` dispatch behavior.
|
||||
- **L.2d — Shape fidelity: sphere/cylsphere/building objects.** Finish
|
||||
`CSphere` / `CCylSphere` parity, live-entity object shapes, building object
|
||||
collision identity, and `Setup.Radius` fallback audit.
|
||||
- **L.2e — Cell ownership: outdoor seams, `CELLARRAY`, `cell_bsp`.** Update
|
||||
low outdoor cell id across 24m seams, port adjacent-cell checks, activate
|
||||
`cell_bsp`, and hand G.3 a trustworthy building/portal boundary model.
|
||||
- **L.2f — Real-DAT and live retail-observer conformance.** Promote synthetic
|
||||
tests to real-world fixtures and verify local acdream view plus retail
|
||||
observer view. ACE accepting a position is a compatibility check, not proof
|
||||
of fine-grained retail collision parity.
|
||||
|
||||
**Acceptance:**
|
||||
- A developer can trace the active movement path: input/motion -> body
|
||||
prediction -> `ResolveWithTransition` -> contact/cell result -> outbound
|
||||
packets -> server echo/correction.
|
||||
- Buildings, edge-slide, wall-slide, cell seams, packet authority, and dungeon
|
||||
portal ownership each have an L.2 lane.
|
||||
- Every AC-specific algorithm port cites named retail decomp, or a documented
|
||||
fallback when named retail lacks the body.
|
||||
- `dotnet build` and `dotnet test` are green for each implementation slice.
|
||||
|
||||
---
|
||||
|
||||
### Phase J — Long-tail (deferred / low-priority)
|
||||
|
||||
Not detailed here; each gets its own brainstorm when it becomes relevant.
|
||||
|
|
@ -431,7 +485,10 @@ port in any phase — no separate listing here.
|
|||
| Holtburg sign half-buried | **5 FIXED** ✓ |
|
||||
| Can't walk past the loaded 3×3 window | **A.1 FIXED** ✓ (5×5 default, `ACDREAM_STREAM_RADIUS` to tune) |
|
||||
| Frame hitch crossing landblock boundary | **Phase A.3** (synchronous loader for now; async returns when DatCollection is thread-safe) |
|
||||
| Walking around doesn't move me on the server | **Phase B.3 FIXED** ✓ |
|
||||
| Walking around doesn't move me on the server | **Phase B.2/B.3 FIXED** ✓ for coarse server movement; fine retail collision parity is **Phase L.2** |
|
||||
| Sliding along buildings / walls feels wrong | **Phase L.2c + L.2d** |
|
||||
| Roof edge / cliff / precipice blocks or slides wrong | **Phase L.2c** |
|
||||
| Crossing outdoor cell seams reports the wrong cell | **Phase L.2e** |
|
||||
| Can't talk to NPCs | **Phase H.3** (emote scripts + dialogs) |
|
||||
| Can't open a door | **Phase F** (object-use action) |
|
||||
| Portals render as a rotating black disk | **Phase E.3** (particle system) |
|
||||
|
|
@ -444,7 +501,7 @@ port in any phase — no separate listing here.
|
|||
| Combat doesn't show in the chat log | **I.7 FIXED** ✓ |
|
||||
| Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) |
|
||||
| No sound | **Phase E.2** |
|
||||
| Dungeons / foundry interior missing | **Phase G.3** |
|
||||
| Dungeons / foundry interior missing | **Phase G.3** after **L.2e** cell/building ownership |
|
||||
| Can't fight monsters | **Phase F.3** (combat math + damage) |
|
||||
| Can't cast spells | **Phase F.4** |
|
||||
| No inventory panel | **Phase F.2 + F.5** |
|
||||
|
|
|
|||
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