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:
Erik 2026-04-29 21:28:56 +02:00
parent e44d24cec6
commit d4c3f947d2
6 changed files with 689 additions and 242 deletions

View file

@ -177,6 +177,141 @@ missing is the plugin-API surface.
--- ---
## #30 — AutonomousPosition contact byte is too often grounded
**Status:** OPEN
**Severity:** HIGH
**Filed:** 2026-04-29
**Component:** physics / net / movement
**Description:** Outbound movement can claim grounded contact even when the
local resolver result is uncertain or airborne. `AutonomousPosition.Build`
defaults `lastContact` to 1, and the app path needs an audit to ensure
`ResolveResult.IsOnGround` is what reaches the wire.
**Root cause / status:** Tracked under Phase L.2b. This can make ACE accept
movement that is not actually retail-valid and can hide edge/step-down bugs.
**Files:** `src/AcDream.Core.Net/Messages/AutonomousPosition.cs`,
`src/AcDream.Core.Net/Messages/MoveToState.cs`,
`src/AcDream.App/Input/PlayerMovementController.cs`.
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
**Acceptance:** Ground contact byte is derived from the current resolved
movement result for both autonomous heartbeat and movement-state sends. Tests
cover grounded, airborne, and failed-transition cases.
---
## #31 — Low outdoor cell id can go stale after transition movement
**Status:** OPEN
**Severity:** HIGH
**Filed:** 2026-04-29
**Component:** physics / cells / movement
**Description:** Local movement can cross 24m outdoor cell boundaries while
the low cell id used for outbound full cell id remains stale. This can combine
correct landblock high bits with the wrong outdoor-cell low byte.
**Root cause / status:** Tracked under Phase L.2e. `CELLARRAY`,
`CObjCell::find_cell_list`, adjacent-cell checks, and low-cell ownership are
not fully ported.
**Files:** `src/AcDream.Core/Physics/PhysicsEngine.cs`,
`src/AcDream.Core/Physics/TransitionTypes.cs`,
`src/AcDream.App/Input/PlayerMovementController.cs`.
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
**Acceptance:** Crossing a 24m outdoor-cell seam updates the local resolved
cell id and the outbound full cell id. Tests cover intra-landblock seams and
landblock-edge seams.
---
## #32 — Retail edge-slide / cliff-slide / precipice-slide incomplete
**Status:** OPEN
**Severity:** HIGH
**Filed:** 2026-04-29
**Component:** physics / collision
**Description:** When walking along walls, roof edges, cliff edges, or failed
step-down boundaries, retail often slides along the boundary. acdream still
hard-blocks or accepts too much in several of these cases.
**Root cause / status:** Tracked under Phase L.2c. Named retail anchors include
`CTransition::edge_slide`, `CTransition::cliff_slide`,
`SPHEREPATH::precipice_slide`, and `SPHEREPATH::step_up_slide`.
**Files:** `src/AcDream.Core/Physics/TransitionTypes.cs`,
`src/AcDream.Core/Physics/BSPQuery.cs`,
`tests/AcDream.Core.Tests/`.
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
**Acceptance:** Synthetic and real-DAT tests cover wall-slide, roof-edge slide,
cliff/precipice slide, failed step-up/step-down, and the jump-clears-edge case.
---
## #33 — Live entity collision shape collapses to one cylinder
**Status:** OPEN
**Severity:** MEDIUM
**Filed:** 2026-04-29
**Component:** physics / entities
**Description:** Live world entities do not yet use exact retail
`CSphere` / `CCylSphere` shape semantics. Several paths collapse the entity to
a simplified root-centered cylinder or fallback radius, which is not enough for
retail object and creature collision parity.
**Root cause / status:** Tracked under Phase L.2d. Requires auditing object
shape extraction, `Setup.Radius` fallback, building object identity, and live
entity broadphase records against named retail.
**Files:** `src/AcDream.Core/Physics/CollisionPrimitives.cs`,
`src/AcDream.Core/Physics/ShadowObjectRegistry.cs`,
`src/AcDream.Core/Physics/PhysicsDataCache.cs`.
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
**Acceptance:** Live object collision uses the appropriate retail sphere or
cylsphere data where available. Tests prove at least one multi-shape object and
one live creature case no longer use the single-cylinder fallback.
---
## #34 — Missing routine local/server correction diagnostic
**Status:** OPEN
**Severity:** MEDIUM
**Filed:** 2026-04-29
**Component:** physics / net / diagnostics
**Description:** The client needs an opt-in diagnostic that logs local predicted
position/contact/cell, outbound movement fields, server `UpdatePosition` echo,
and correction delta. Current correction visibility is too focused on portal
arrival and not enough on normal walking.
**Root cause / status:** Tracked under Phase L.2a/L.2b. Without this probe,
ACE's tolerance can hide local collision divergence.
**Files:** `src/AcDream.Core.Net/WorldSession.cs`,
`src/AcDream.App/Input/PlayerMovementController.cs`,
`src/AcDream.App/Rendering/GameWindow.cs`.
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
**Acceptance:** With the diagnostic enabled, a walking session logs local
resolved placement, outbound cell/contact fields, server echo placement, and
correction delta in a grep-friendly format.
---
## #2 — Lightning visual mismatch (sky PES path disproved) ## #2 — Lightning visual mismatch (sky PES path disproved)

View file

@ -100,67 +100,129 @@ crib-sheet version.
--- ---
## Project Structure (target) ## Project Structure (current + target)
``` ```
src/ src/
AcDream.Core/ Layer 2-4: no GL, no Silk.NET, pure logic AcDream.Core/ Layer 2-4: no GL, no Silk.NET, pure logic
Physics/ Physics/
PhysicsBody.cs ← ported from decompiled (done) PhysicsBody.cs -> body state / integration foundation (done)
CollisionPrimitives.cs ← ported from decompiled (done) CollisionPrimitives.cs -> retail primitive helpers (partial, active)
MotionInterpreter.cs ← ported from decompiled (done) MotionInterpreter.cs -> motion state machine (done, still L.1 polish)
AnimationSequencer.cs ← ported from decompiled (done) AnimationSequencer.cs -> animation playback + root-motion data (done, L.1 active)
CellBsp.cs ← TODO: port from decompiled TerrainSurface.cs -> triangle-aware terrain contact (done)
Transition.cs ← TODO: port from decompiled BSPQuery.cs -> partial retail BSP dispatcher (active in L.2)
TerrainSurface.cs ← verified against ACME (done) TransitionTypes.cs -> SpherePath / CollisionInfo / transition helpers (active in L.2)
PhysicsDataCache.cs -> GfxObj / Setup / CellStruct collision data (done, active)
ShadowObjectRegistry.cs -> broadphase for nearby physics objects (active)
PhysicsEngine.cs -> ResolveWithTransition active player path
CellBsp.cs -> not a first-class runtime owner yet (L.2e)
World/ World/
GameEntity.cs ← TODO: unified entity (replaces scattered state) GameEntity.cs -> target unified entity, not current reality
WorldState.cs ← TODO: owns all entities WorldState.cs -> target entity owner
CellTracker.cs ← TODO: per-entity cell management CellTracker.cs -> target per-entity cell management
SceneryGenerator.cs verified against decompiled (done) SceneryGenerator.cs -> verified against decompiled (done)
LandblockLoader.cs done LandblockLoader.cs -> done
Terrain/ Terrain/
LandblockMesh.cs verified against ACME (done) LandblockMesh.cs -> verified against ACME (done)
TerrainBlending.cs verified against ACME (done) TerrainBlending.cs -> verified against ACME (done)
Meshing/ Meshing/
GfxObjMesh.cs cross-checked against ACME (done) GfxObjMesh.cs -> cross-checked against ACME (done)
SetupMesh.cs cross-checked (done) SetupMesh.cs -> cross-checked (done)
Textures/ Textures/
SurfaceDecoder.cs done SurfaceDecoder.cs -> done
Dat/ Dat/
MotionResolver.cs ← done (move here from Meshing/) MotionResolver.cs -> done (target move from Meshing/)
AcDream.Core.Net/ Layer 2: networking AcDream.Core.Net/ Layer 2: networking
WorldSession.cs done (wire-compatible with ACE) WorldSession.cs -> done (wire-compatible with ACE)
NetClient.cs done NetClient.cs -> done
Messages/ done (CreateObject, MoveToState, etc.) Messages/ -> done (CreateObject, MoveToState, etc.)
AcDream.Plugin.Abstractions/ Layer 5: plugin interfaces AcDream.Plugin.Abstractions/ Layer 5: plugin interfaces
IAcDreamPlugin.cs done IAcDreamPlugin.cs -> done
IPluginHost.cs done IPluginHost.cs -> done
IGameState.cs done IGameState.cs -> done
IEvents.cs done IEvents.cs -> done
AcDream.App/ Layer 1 + Layer 4 wiring AcDream.App/ Layer 1 + Layer 4 wiring
Rendering/ Rendering/
GameWindow.cs ← TODO: thin down to GL calls only GameWindow.cs -> still owns too much runtime wiring
TerrainRenderer.cs done TerrainRenderer.cs -> done
StaticMeshRenderer.cs done StaticMeshRenderer.cs -> done
TextureCache.cs done TextureCache.cs -> done
ChaseCamera.cs done ChaseCamera.cs -> done
FlyCamera.cs done FlyCamera.cs -> done
Streaming/ Streaming/
StreamingController.cs done StreamingController.cs -> done
GpuWorldState.cs done GpuWorldState.cs -> done
Input/ Input/
PlayerMovementController.cs ← done (uses ported physics) PlayerMovementController.cs -> active movement driver
Plugins/ Plugins/
AppPluginHost.cs done AppPluginHost.cs -> done
``` ```
--- ---
## GameEntity: The Unified Entity (TODO — the big refactor) ## Movement And Collision Architecture
Phase L.2 is the current organizing program for physics, collision,
boundaries, buildings, sliding, cell ownership, movement packets, and server
authority. Detailed plan: `docs/plans/2026-04-29-movement-collision-conformance.md`.
The active player movement spine is:
```text
InputDispatcher / PlayerMovementController
-> MotionInterpreter + local body prediction
-> PhysicsEngine.ResolveWithTransition
-> TransitionTypes + BSPQuery + ShadowObjectRegistry
-> ResolveResult contact/cell state
-> MoveToState / AutonomousPosition outbound messages
-> WorldSession server echo or correction handling
```
What exists and is active:
- `PhysicsEngine.ResolveWithTransition` is the path used for local player
collision resolution.
- `BSPQuery` contains the partial retail-style BSP collision dispatcher used by
the transition path.
- `TransitionTypes` carries `SpherePath`, `CollisionInfo`, `ObjectInfo`,
transition validation, step-up/down, contact-plane handling, and partial
slide behavior.
- `PhysicsDataCache` loads GfxObj, Setup, and CellStruct physics data from DATs.
- `ShadowObjectRegistry` gives movement a broadphase over nearby objects and
buildings.
- `TerrainSurface` uses triangle-aware terrain contact; older "bilinear terrain
Z" descriptions are historical B.3 language, not current architecture.
What remains incomplete:
- `CELLARRAY`, `CObjCell::find_cell_list`, adjacent-cell checks, and low outdoor
cell id updates across 24m seams.
- `cell_bsp` / `CellBSP` as the authoritative runtime owner for indoor and
building collision.
- Building portal transit and normal walking through building entry/exit
boundaries.
- Full retail `edge_slide`, `cliff_slide`, `precipice_slide`, and `NegPolyHit`
dispatch behavior.
- Exact `CSphere` / `CCylSphere` object-shape parity, especially for live
entities that currently collapse to a simplified cylinder fallback.
- Routine local/server correction diagnostics. ACE accepting a position is a
compatibility signal, not proof of fine retail collision parity.
Ownership by phase:
- B.3 is shipped MVP history: first resolver foundation and tests.
- L.1 owns animation/motion parity, including root-motion coupling.
- L.2 owns the movement/collision conformance stack listed above.
- G.3 owns dungeon streaming and portal-space delivery after L.2e gives it
trustworthy cell/building boundaries.
---
## GameEntity: The Unified Entity (target refactor)
Currently, entity state is scattered across: Currently, entity state is scattered across:
- `WorldEntity` (position, rotation, mesh refs) - `WorldEntity` (position, rotation, mesh refs)
@ -198,21 +260,21 @@ public sealed class GameEntity
{ {
Motion.ApplyCurrentMovement(); // set velocity from motion state Motion.ApplyCurrentMovement(); // set velocity from motion state
Physics.UpdateObject(dt); // integrate position Physics.UpdateObject(dt); // integrate position
// TODO: Transition.FindValidPosition // collision resolve PhysicsEngine.ResolveWithTransition(); // current L.2 collision spine
Cell.UpdateCell(Physics.Position); // check cell transitions Cell.UpdateCell(Physics.Position); // target: retail cell ownership
Animation.Advance(dt); // advance animation frames Animation.Advance(dt); // advance animation frames
RebuildMeshRefs(); // compute per-part transforms RebuildMeshRefs(); // compute per-part transforms
} }
} }
``` ```
Every entity in the world — player, NPC, monster, lifestone, door, chest — Target state: every entity in the world — player, NPC, monster, lifestone,
is a `GameEntity`. The renderer iterates them and draws. The plugin API door, chest — becomes a `GameEntity`. The renderer iterates them and draws.
exposes them as `WorldEntitySnapshot`. GameWindow becomes thin. The plugin API exposes them as `WorldEntitySnapshot`. GameWindow becomes thin.
--- ---
## Per-Frame Update Order (matches retail) ## Per-Frame Update Order (current runtime)
``` ```
1. Network tick 1. Network tick
@ -224,15 +286,16 @@ exposes them as `WorldEntitySnapshot`. GameWindow becomes thin.
create terrain + scenery GameEntities create terrain + scenery GameEntities
3. Input tick (player mode only) 3. Input tick (player mode only)
└── Read WASD/mouse → MotionInterpreter.DoMotion → └── InputDispatcher scopes → PlayerMovementController →
MotionInterpreter/body prediction → ResolveWithTransition →
send MoveToState/AutonomousPosition to server send MoveToState/AutonomousPosition to server
4. Entity tick (ALL entities, 30Hz fixed step) 4. Entity / animation tick
└── For each GameEntity: entity.Update(dt) └── Current code still has scattered world/entity state. L.1 owns
This runs: motion → physics → collision → cell → animation animation parity; L.2 owns movement/collision conformance.
5. Render tick 5. Render tick
└── For each GameEntity: read MeshRefs, draw └── Read current entity mesh refs, draw
TerrainRenderer.Draw, StaticMeshRenderer.Draw TerrainRenderer.Draw, StaticMeshRenderer.Draw
(frustum cull, translucency pass, etc.) (frustum cull, translucency pass, etc.)
@ -248,89 +311,26 @@ exposes them as `WorldEntitySnapshot`. GameWindow becomes thin.
--- ---
## Execution Plan: How to Get There ## Roadmap Model
### Phase R1: GameEntity Refactor (the foundation) The old R1-R8 architecture sequence was a useful early refactor sketch, but it
**Goal:** Replace the scattered entity state with unified GameEntity. is no longer the execution plan. The strategic source of truth is now
`docs/plans/2026-04-11-roadmap.md`, with per-phase details in `docs/plans/`
and `docs/superpowers/specs/`.
1. Create `GameEntity` class in `AcDream.Core/World/` Current movement/collision ownership:
2. Move `AnimatedEntity` fields into `GameEntity.Animation`
3. Move `WorldEntity` fields into `GameEntity.Physics` + position
4. Move `_entitiesByServerGuid` into `WorldState`
5. Move animation tick from `GameWindow.TickAnimations` into `GameEntity.Update`
6. GameWindow.OnRender reads `GameEntity.MeshRefs` instead of `WorldEntity.MeshRefs`
**Test:** Everything looks the same as before. No visual change. - **B.3** is shipped MVP history: first collision resolver foundation.
- **L.1** owns animation/motion parity, including root-motion coupling.
- **L.2** owns movement and collision conformance:
`docs/plans/2026-04-29-movement-collision-conformance.md`.
- **G.3** owns dungeon streaming and portal-space delivery after L.2e lands
trustworthy `cell_bsp`, `CELLARRAY`, adjacent-cell checks, and building
entry/exit boundaries.
### Phase R2: Thin GameWindow The GameEntity / thin GameWindow refactor remains a valid target architecture,
**Goal:** GameWindow does only GL calls + input dispatch. but it is not a prerequisite for L.2. Do not resurrect old R1-R8 phase numbers
for new work; add or update roadmap phases instead.
1. Extract entity creation from `OnLiveEntitySpawned` into `WorldState.SpawnEntity`
2. Extract motion updates from `OnLiveMotionUpdated` into `WorldState.UpdateMotion`
3. Extract player movement from the giant OnUpdate block into `PlayerController`
4. GameWindow.OnUpdate calls: network.Tick → streaming.Tick → input.Tick → worldState.Tick → render
**Test:** Everything works the same. GameWindow.cs drops from 2000+ to ~500 lines.
### Phase R3: CellBSP + Wall Collision
**Goal:** Entities can't walk through walls.
1. Port CellBSP from decompiled code (sphere_intersects_cell)
2. Port Transition.FindValidPosition (swept sphere collision)
3. Wire into GameEntity.Update between physics and cell tracking
4. Indoor transitions become correct (wall stops you, doorway lets you through)
**Test:** Walk into building wall → stopped. Walk through doorway → enter.
### Phase R4: Complete Animation State Machine
**Goal:** Every animation works for every entity type.
1. Port full MotionInterp.PerformMovement from decompiled (all 5 movement types)
2. Port Links table resolution for smooth transitions
3. Port idle modifiers (fidgets)
4. Jump animation (wire jump motion command through the pipeline)
**Test:** All entity types animate correctly. Transitions are smooth.
### Phase R5: Lighting from Retail
**Goal:** Sun, ambient, per-vertex lighting match retail.
1. Port AdjustPlanes (FUN_00532440) — face normals + per-vertex lighting
2. Extract global lighting constants from decompiled DAT addresses
3. Replace hardcoded shader constants with ported values
**Test:** Side-by-side with retail client shows matching lighting.
### Phase R6: Server Compliance
**Goal:** ACE accepts all movement, no rubber-banding.
1. Server-authoritative Z (trust server position, local is cosmetic)
2. Proper MoveToState with full RawMotionState packing
3. Keepalive ping (5s idle)
4. Graceful session management
**Test:** Walk around, other clients see smooth movement. No ACE errors.
### Phase R7: Interaction
**Goal:** Click NPCs, open doors, pick up items, chat.
1. Use/UseWithTarget game actions
2. Door open animation (server sends UpdateMotion → animate)
3. Chat send/receive
4. Basic inventory (pickup/drop)
**Test:** Open a door, talk to an NPC, send a chat message.
### Phase R8: Plugin API Completion
**Goal:** Plugins can observe and control everything.
1. IGameState exposes all GameEntity fields
2. IEvents fires for all world changes
3. IActions covers: Move, Cast, Use, Say, Pickup, Drop
4. IPacketPipeline hooks all 4 stages
5. Lua macro engine (MoonSharp) ships as a built-in plugin
**Test:** A Lua script auto-loots gems. A C# plugin displays an overlay.
--- ---
@ -339,11 +339,12 @@ exposes them as `WorldEntitySnapshot`. GameWindow becomes thin.
``` ```
For every AC-specific behavior: For every AC-specific behavior:
1. DECOMPILE → Find the function in docs/research/decompiled/ 0. GREP NAMED → Search docs/research/named-retail/ by class::method
2. CROSS-CHECK → Verify against ACE + ACME + holtburger 1. FALLBACK → Use older docs/research/decompiled/ chunks only if needed
2. CROSS-CHECK → Verify against ACE + ACME + holtburger where relevant
3. PSEUDOCODE → Translate to readable pseudocode 3. PSEUDOCODE → Translate to readable pseudocode
4. PORT → Faithful C# translation 4. PORT → Faithful C# translation
5. TEST → Conformance test against decompiled golden values 5. TEST → Conformance test against retail/decomp golden values
6. INTEGRATE → Surgical wiring into the existing system 6. INTEGRATE → Surgical wiring into the existing system
7. VERIFY → Visual + functional test 7. VERIFY → Visual + functional test
``` ```
@ -359,9 +360,9 @@ For acdream-specific code (renderer, plugin API, streaming):
| Domain | Primary Oracle | Secondary | | Domain | Primary Oracle | Secondary |
|--------|---------------|-----------| |--------|---------------|-----------|
| Physics/collision | Decompiled acclient.exe | ACE Physics/ | | Physics/collision | `docs/research/named-retail/` | ACE Physics/ + older decompiled chunks |
| Animation | Decompiled + ACE Animation/ | — | | Animation | `docs/research/named-retail/` + ACE Animation/ | — |
| Terrain | ACME ClientReference.cs | Decompiled | | Terrain | ACME ClientReference.cs | named retail / older decompiled chunks |
| Rendering | WorldBuilder (Silk.NET) | ACViewer | | Rendering | WorldBuilder (Silk.NET) | ACViewer |
| Protocol | holtburger | AC2D | | Protocol | holtburger | AC2D |
| Server behavior | ACE | — | | Server behavior | ACE | — |

View file

@ -1,6 +1,6 @@
# acdream — strategic roadmap # 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. **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.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.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 ✓ | | 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 ✓ | | 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 ✓ | | 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 ✓ | | 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:** **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.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.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.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). - **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.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. - **✓ 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. **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`. **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:** **Sub-pieces:**
- **L.1a — Audit & inventory.** Map retail named-decomp evidence, ACE - **L.1a — Audit & inventory.** Map retail named-decomp evidence, ACE
cross-references, existing acdream hook points, and current gaps for each 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) ### Phase J — Long-tail (deferred / low-priority)
Not detailed here; each gets its own brainstorm when it becomes relevant. 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** ✓ | | 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) | | 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) | | 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 talk to NPCs | **Phase H.3** (emote scripts + dialogs) |
| Can't open a door | **Phase F** (object-use action) | | Can't open a door | **Phase F** (object-use action) |
| Portals render as a rotating black disk | **Phase E.3** (particle system) | | 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** ✓ | | Combat doesn't show in the chat log | **I.7 FIXED** ✓ |
| Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) | | Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) |
| No sound | **Phase E.2** | | 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 fight monsters | **Phase F.3** (combat math + damage) |
| Can't cast spells | **Phase F.4** | | Can't cast spells | **Phase F.4** |
| No inventory panel | **Phase F.2 + F.5** | | No inventory panel | **Phase F.2 + F.5** |

View 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.

View file

@ -1,131 +1,128 @@
# Collision System Port Status and Plan # Collision System Port - Status and Plan
## Current State (2026-04-14) ## Current State (2026-04-29)
The collision system has been patched multiple times but does NOT match The collision system is no longer a pure placeholder and should not be treated
retail. The user has explicitly requested a **full faithful port** of as "delete everything and start over." A partial retail transition port exists:
the retail collision system — no shortcuts, no simplifications.
## What Went Wrong - `PhysicsEngine.ResolveWithTransition` is the active player movement resolver.
- `BSPQuery` contains a partial retail-style BSP dispatcher.
- `TransitionTypes` carries the active `SpherePath`, `CollisionInfo`,
transition, step, contact, and partial slide logic.
- `PhysicsDataCache` loads GfxObj, Setup, and CellStruct physics data.
- `ShadowObjectRegistry` gives the resolver a broadphase over nearby objects.
- `TerrainSurface` uses triangle-aware terrain contact.
Instead of porting the decompiled code line-by-line (as CLAUDE.md This foundation is useful, but it is not complete retail collision parity.
mandates), I wrote simplified approximations: The project now tracks the remaining work as Phase L.2 - Movement & Collision
- Static overlap instead of swept-sphere FindTimeOfCollision Conformance:
- Custom FindObjCollisions instead of porting Sphere.IntersectsSphere
- Custom BSP query instead of porting BSPTree.find_collisions dispatcher
- Ad-hoc push-out instead of proper SlideSphere crease-projection
- Incremental patches that don't address root architectural issues
Each patch fixed one symptom but introduced new edge cases. The result - Plan: `docs/plans/2026-04-29-movement-collision-conformance.md`
is a patchwork that handles ~60-70% of cases but fails on the rest. - Roadmap owner: `docs/plans/2026-04-11-roadmap.md`
- Tactical follow-ups: `docs/ISSUES.md` #30-#34
## What Must Happen Next ## Durable Lesson
**Delete the existing collision code and start fresh.** Port from ACE's Do not guess at AC physics, movement packets, terrain/cell ownership, or
complete C# implementation, cross-referencing the decompiled code for collision constants. The previous patchwork failures came from simplified
ground truth. ACE has the ENTIRE system already in C#: approximations:
### Files to port from ACE (in order): - static overlap instead of swept-sphere transition behavior
- custom object collision instead of retail `CSphere` / `CCylSphere`
- incomplete BSP dispatch
- ad-hoc push-out instead of retail slide / edge / precipice handling
- server "no rubber-band" treated as proof of local collision correctness
1. **Sphere.cs**`IntersectsSphere` (FUN_005387c0), `SlideSphere` (both variants), `StepSphereUp`, `StepSphereDown`, `LandOnSphere`, `CollideWithPoint`, `CollidesWithSphere` The named retail decomp is now the primary source. Search
`docs/research/named-retail/acclient_2013_pseudo_c.txt` by `class::method`
before using older decompiled chunks or reference repos.
2. **BSPTree.cs**`find_collisions` (6-path dispatcher) ## Active Approach
3. **BSPNode.cs**`sphere_intersects_poly` (tree traversal with movement), `find_walkable`, `hits_walkable`, `sphere_intersects_solid` Continue by conformance lanes rather than rewriting blindly:
4. **BSPLeaf.cs** — leaf-level polygon tests 1. **Truth & diagnostics (L.2a).** Add local placement/contact/cell,
object-hit, outbound-packet, server echo, and correction-delta probes.
2. **Movement wire/contact authority (L.2b).** Fix contact byte and full-cell
truth before using ACE acceptance as evidence.
3. **Transition parity (L.2c).** Port edge-slide, cliff-slide,
precipice-slide, step-up/down slide, and `NegPolyHit` dispatch.
4. **Shape fidelity (L.2d).** Finish `CSphere` / `CCylSphere` semantics,
live-entity shapes, and building object identity.
5. **Cell ownership (L.2e).** Port `CELLARRAY`, `find_cell_list`,
`check_other_cells`, `adjust_check_pos`, low-cell updates, and `cell_bsp`.
6. **Real-DAT and live observer conformance (L.2f).** Promote every synthetic
case to real-world fixtures and retail-observer checks.
5. **Polygon.cs**`pos_hits_sphere`, `adjust_sphere_to_plane`, `check_walkable` ## What To Preserve
6. **Transition.cs**`FindTransitionalPosition`, `TransitionalInsert`, `StepUp`, `StepDown`, `ValidateTransition`, `AdjustOffset` - `CollisionPrimitives.cs` low-level helpers, while auditing remaining shape
gaps against named retail.
- `PhysicsDataCache.cs` DAT-backed collision data loading.
- `ShadowObjectRegistry.cs` broadphase concept.
- `TransitionTypes.cs` data structures and partial transition port.
- `BSPQuery.cs` partial dispatcher as the current porting surface.
- `PhysicsBody.cs`, `MotionInterpreter.cs`, and `PlayerWeenie.cs` foundations.
7. **SpherePath.cs**`SetCheckPos`, `AddOffsetToCheckPos`, `CacheLocalSpaceSphere`, `SetCollide`, `SetWalkable`, `SetNegPolyHit` ## Known Gaps
8. **CollisionInfo.cs**`SetContactPlane`, `SetSlidingNormal`, `SetCollisionNormal` - Full `CELLARRAY` and adjacent-cell ownership are missing.
- `cell_bsp` is not yet a first-class runtime owner.
- Building portal transit and building entry/exit collision are incomplete.
- `edge_slide`, `cliff_slide`, `precipice_slide`, and `NegPolyHit` behavior are
incomplete.
- Live entity shape fidelity is simplified.
- Outbound movement contact/cell fields can be overconfident.
- Routine local/server correction diagnostics are missing.
9. **ObjectInfo.cs**`ValidateWalkable` ## Retail Anchors
10. **LandCell.cs**`FindEnvCollisions` (outdoor terrain) Primary:
11. **EnvCell.cs**`FindEnvCollisions` (indoor BSP) - `docs/research/named-retail/acclient_2013_pseudo_c.txt`
- `docs/research/named-retail/acclient.h`
- `docs/research/named-retail/symbols.json`
12. **ObjCell.cs**`FindObjCollisions`, `find_cell_list` Key names:
### ACE source locations: - `CTransition::find_transitional_position`
- `references/ACE/Source/ACE.Server/Physics/Sphere.cs` - `CTransition::transitional_insert`
- `references/ACE/Source/ACE.Server/Physics/BSP/BSPTree.cs` - `CTransition::step_up`
- `references/ACE/Source/ACE.Server/Physics/BSP/BSPNode.cs` - `CTransition::step_down`
- `references/ACE/Source/ACE.Server/Physics/BSP/BSPLeaf.cs` - `CTransition::edge_slide`
- `references/ACE/Source/ACE.Server/Physics/Polygon.cs` - `CTransition::cliff_slide`
- `references/ACE/Source/ACE.Server/Physics/Transition.cs` - `SPHEREPATH::step_up_slide`
- `references/ACE/Source/ACE.Server/Physics/SpherePath.cs` - `SPHEREPATH::precipice_slide`
- `references/ACE/Source/ACE.Server/Physics/Collision/CollisionInfo.cs` - `SPHEREPATH::adjust_check_pos`
- `references/ACE/Source/ACE.Server/Physics/Collision/ObjectInfo.cs` - `CTransition::check_other_cells`
- `CObjCell::find_cell_list`
- `CPhysicsObj::is_valid_walkable`
- `CBuildingObj::find_building_collisions`
- `CCellStruct::sphere_intersects_cell`
- `CCylSphere::intersects_sphere`
- `CSphere::intersects_sphere`
- `CSphere::slide_sphere`
### Decompiled ground truth (named-retail is now primary, 2026-04-25): Older fallback:
- **`docs/research/named-retail/acclient_2013_pseudo_c.txt`** — grep for
`BSPTree::`, `BSPNode::`, `BSPLeaf::`, `CPolygon::`, `CCylSphere::`,
`Transition::`, `CPhysicsObj::`, `SpherePath::` to find named bodies.
- **`docs/research/named-retail/acclient.h`** — verbatim retail struct
layouts for the BSP / Sphere / Transition types.
- **`docs/research/named-retail/symbols.json`** — name↔address lookup.
- `docs/research/decompiled/chunk_00530000.c` — older Ghidra fallback for
BSP / Polygon / Sphere collision (FUN_xxx names).
- `docs/research/decompiled/chunk_00500000.c` — older Ghidra fallback for
PhysicsObj / transition callers.
- `docs/research/acclient_function_map.md` — hand-curated cross-port index
(ACE / ACME mappings + struct-offset notes).
### Pseudocode (already written): - `docs/research/decompiled/chunk_00530000.c`
- `docs/research/transition_pseudocode.md` — full system documented - `docs/research/decompiled/chunk_00500000.c`
- `docs/research/acclient_function_map.md`
## What to Keep Reference aids:
- `CollisionPrimitives.cs` — 9 low-level functions already faithfully ported from decompiled code. These are CORRECT and match retail. - `references/ACE/Source/ACE.Server/Physics/`
- `PhysicsDataCache.cs` — GfxObj/Setup/CellStruct physics data loading from dats. Correct. - `references/holtburger/` for movement wire behavior
- `ShadowObjectRegistry.cs` — cell-based spatial index. Correct concept, may need refinement. - `references/AC2D/` for the older client-side movement packet reference
- `TransitionTypes.cs` data structures — SpherePath, CollisionInfo, ObjectInfo, PhysicsGlobals. Mostly correct, may need field additions.
- `PhysicsBody.cs` — Euler integration. Correct.
- `MotionInterpreter.cs` — Motion state machine. Correct.
- `PlayerWeenie.cs` — Run/Jump formulas. Correct.
## What to Replace ## Mandatory Workflow
- `BSPQuery.cs` — replace with faithful port of BSPTree/BSPNode/BSPLeaf For every AC-specific function:
- `TransitionTypes.cs` Transition methods — replace FindTransitionalPosition, TransitionalInsert, FindEnvCollisions, FindObjCollisions, SlideSphere, AdjustOffset with faithful ports
- `PhysicsEngine.ResolveWithTransition` — may need restructuring
## Approach (MANDATORY — per CLAUDE.md) 1. Grep named retail first.
2. Cross-reference ACE / holtburger / AC2D where relevant.
For EVERY function: 3. Write readable pseudocode before porting.
4. Port faithfully; do not simplify.
1. **GREP NAMED FIRST, then DECOMPILE FALLBACK.** Search the named 5. Add conformance tests.
retail decomp first: `grep -n "ClassName::Method" docs/research/named-retail/acclient_2013_pseudo_c.txt`. 6. Integrate surgically into the active L.2 lane.
For struct layouts: `grep -n "^struct ClassName" docs/research/named-retail/acclient.h`. 7. Verify with synthetic tests, real-DAT fixtures, and live observer evidence.
Only if the named pseudo-C lacks a function (rare), fall back to the
older `docs/research/decompiled/` chunks via the function map at
`docs/research/acclient_function_map.md`.
2. **CROSS-REFERENCE ACE.** Read ACE's C# port of the same function.
ACE provides naming and structure. Note any differences.
3. **WRITE PSEUDOCODE.** Translate the decompiled C into readable
pseudocode BEFORE porting to C#. Add to
`docs/research/collision_port_pseudocode.md`.
4. **PORT FAITHFULLY.** Translate pseudocode to C# line-by-line.
Same variable names, same control flow, same boundary conditions.
Do NOT "improve" or "simplify" the algorithm.
5. **VERIFY.** When ACE and the decompiled code disagree, the
decompiled code wins. Document the difference.
### Execution order:
1. Sphere collision (Sphere.cs) — FUN_005387c0 and sub-functions
2. BSP tree (BSPTree/Node/Leaf) — find_collisions dispatcher
3. Polygon tests (Polygon.cs) — pos_hits_sphere, adjust_sphere_to_plane
4. Transition orchestrator (Transition.cs) — FindTransitionalPosition
5. Cell collision (LandCell/EnvCell/ObjCell) — FindEnvCollisions, FindObjCollisions
6. Wire into PhysicsEngine.ResolveWithTransition
7. Test: terrain → indoor walls → objects → step-up → every object type

View file

@ -0,0 +1,49 @@
# Movement & Collision Conformance Crib
## Phase
Active phase: **L.2 - Movement & Collision Conformance**.
Plan: `docs/plans/2026-04-29-movement-collision-conformance.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
```text
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.