diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 9b67b51..c47b778 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -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) diff --git a/docs/architecture/acdream-architecture.md b/docs/architecture/acdream-architecture.md index 56b888d..5f134da 100644 --- a/docs/architecture/acdream-architecture.md +++ b/docs/architecture/acdream-architecture.md @@ -100,67 +100,129 @@ crib-sheet version. --- -## Project Structure (target) +## Project Structure (current + target) ``` src/ AcDream.Core/ Layer 2-4: no GL, no Silk.NET, pure logic Physics/ - PhysicsBody.cs ← ported from decompiled (done) - CollisionPrimitives.cs ← ported from decompiled (done) - MotionInterpreter.cs ← ported from decompiled (done) - AnimationSequencer.cs ← ported from decompiled (done) - CellBsp.cs ← TODO: port from decompiled - Transition.cs ← TODO: port from decompiled - TerrainSurface.cs ← verified against ACME (done) + PhysicsBody.cs -> body state / integration foundation (done) + CollisionPrimitives.cs -> retail primitive helpers (partial, active) + MotionInterpreter.cs -> motion state machine (done, still L.1 polish) + AnimationSequencer.cs -> animation playback + root-motion data (done, L.1 active) + TerrainSurface.cs -> triangle-aware terrain contact (done) + BSPQuery.cs -> partial retail BSP dispatcher (active in L.2) + 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/ - GameEntity.cs ← TODO: unified entity (replaces scattered state) - WorldState.cs ← TODO: owns all entities - CellTracker.cs ← TODO: per-entity cell management - SceneryGenerator.cs ← verified against decompiled (done) - LandblockLoader.cs ← done + GameEntity.cs -> target unified entity, not current reality + WorldState.cs -> target entity owner + CellTracker.cs -> target per-entity cell management + SceneryGenerator.cs -> verified against decompiled (done) + LandblockLoader.cs -> done Terrain/ - LandblockMesh.cs ← verified against ACME (done) - TerrainBlending.cs ← verified against ACME (done) + LandblockMesh.cs -> verified against ACME (done) + TerrainBlending.cs -> verified against ACME (done) Meshing/ - GfxObjMesh.cs ← cross-checked against ACME (done) - SetupMesh.cs ← cross-checked (done) + GfxObjMesh.cs -> cross-checked against ACME (done) + SetupMesh.cs -> cross-checked (done) Textures/ - SurfaceDecoder.cs ← done + SurfaceDecoder.cs -> done Dat/ - MotionResolver.cs ← done (move here from Meshing/) + MotionResolver.cs -> done (target move from Meshing/) AcDream.Core.Net/ Layer 2: networking - WorldSession.cs ← done (wire-compatible with ACE) - NetClient.cs ← done - Messages/ ← done (CreateObject, MoveToState, etc.) + WorldSession.cs -> done (wire-compatible with ACE) + NetClient.cs -> done + Messages/ -> done (CreateObject, MoveToState, etc.) AcDream.Plugin.Abstractions/ Layer 5: plugin interfaces - IAcDreamPlugin.cs ← done - IPluginHost.cs ← done - IGameState.cs ← done - IEvents.cs ← done + IAcDreamPlugin.cs -> done + IPluginHost.cs -> done + IGameState.cs -> done + IEvents.cs -> done AcDream.App/ Layer 1 + Layer 4 wiring Rendering/ - GameWindow.cs ← TODO: thin down to GL calls only - TerrainRenderer.cs ← done - StaticMeshRenderer.cs ← done - TextureCache.cs ← done - ChaseCamera.cs ← done - FlyCamera.cs ← done + GameWindow.cs -> still owns too much runtime wiring + TerrainRenderer.cs -> done + StaticMeshRenderer.cs -> done + TextureCache.cs -> done + ChaseCamera.cs -> done + FlyCamera.cs -> done Streaming/ - StreamingController.cs ← done - GpuWorldState.cs ← done + StreamingController.cs -> done + GpuWorldState.cs -> done Input/ - PlayerMovementController.cs ← done (uses ported physics) + PlayerMovementController.cs -> active movement driver 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: - `WorldEntity` (position, rotation, mesh refs) @@ -198,21 +260,21 @@ public sealed class GameEntity { Motion.ApplyCurrentMovement(); // set velocity from motion state Physics.UpdateObject(dt); // integrate position - // TODO: Transition.FindValidPosition // collision resolve - Cell.UpdateCell(Physics.Position); // check cell transitions + PhysicsEngine.ResolveWithTransition(); // current L.2 collision spine + Cell.UpdateCell(Physics.Position); // target: retail cell ownership Animation.Advance(dt); // advance animation frames RebuildMeshRefs(); // compute per-part transforms } } ``` -Every entity in the world — player, NPC, monster, lifestone, door, chest — -is a `GameEntity`. The renderer iterates them and draws. The plugin API -exposes them as `WorldEntitySnapshot`. GameWindow becomes thin. +Target state: every entity in the world — player, NPC, monster, lifestone, +door, chest — becomes a `GameEntity`. The renderer iterates them and draws. +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 @@ -224,15 +286,16 @@ exposes them as `WorldEntitySnapshot`. GameWindow becomes thin. create terrain + scenery GameEntities 3. Input tick (player mode only) - └── Read WASD/mouse → MotionInterpreter.DoMotion → + └── InputDispatcher scopes → PlayerMovementController → + MotionInterpreter/body prediction → ResolveWithTransition → send MoveToState/AutonomousPosition to server -4. Entity tick (ALL entities, 30Hz fixed step) - └── For each GameEntity: entity.Update(dt) - This runs: motion → physics → collision → cell → animation +4. Entity / animation tick + └── Current code still has scattered world/entity state. L.1 owns + animation parity; L.2 owns movement/collision conformance. 5. Render tick - └── For each GameEntity: read MeshRefs, draw + └── Read current entity mesh refs, draw TerrainRenderer.Draw, StaticMeshRenderer.Draw (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) -**Goal:** Replace the scattered entity state with unified GameEntity. +The old R1-R8 architecture sequence was a useful early refactor sketch, but it +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/` -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` +Current movement/collision ownership: -**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 -**Goal:** GameWindow does only GL calls + input dispatch. - -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. +The GameEntity / thin GameWindow refactor remains a valid target architecture, +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. --- @@ -339,11 +339,12 @@ exposes them as `WorldEntitySnapshot`. GameWindow becomes thin. ``` For every AC-specific behavior: -1. DECOMPILE → Find the function in docs/research/decompiled/ -2. CROSS-CHECK → Verify against ACE + ACME + holtburger +0. GREP NAMED → Search docs/research/named-retail/ by class::method +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 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 7. VERIFY → Visual + functional test ``` @@ -359,9 +360,9 @@ For acdream-specific code (renderer, plugin API, streaming): | Domain | Primary Oracle | Secondary | |--------|---------------|-----------| -| Physics/collision | Decompiled acclient.exe | ACE Physics/ | -| Animation | Decompiled + ACE Animation/ | — | -| Terrain | ACME ClientReference.cs | Decompiled | +| Physics/collision | `docs/research/named-retail/` | ACE Physics/ + older decompiled chunks | +| Animation | `docs/research/named-retail/` + ACE Animation/ | — | +| Terrain | ACME ClientReference.cs | named retail / older decompiled chunks | | Rendering | WorldBuilder (Silk.NET) | ACViewer | | Protocol | holtburger | AC2D | | Server behavior | ACE | — | diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md index c4cb6ee..a75fd3f 100644 --- a/docs/plans/2026-04-11-roadmap.md +++ b/docs/plans/2026-04-11-roadmap.md @@ -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** | diff --git a/docs/plans/2026-04-29-movement-collision-conformance.md b/docs/plans/2026-04-29-movement-collision-conformance.md new file mode 100644 index 0000000..f31acca --- /dev/null +++ b/docs/plans/2026-04-29-movement-collision-conformance.md @@ -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. diff --git a/memory/project_collision_port.md b/memory/project_collision_port.md index dc1fcd1..1866b88 100644 --- a/memory/project_collision_port.md +++ b/memory/project_collision_port.md @@ -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 -retail. The user has explicitly requested a **full faithful port** of -the retail collision system — no shortcuts, no simplifications. +The collision system is no longer a pure placeholder and should not be treated +as "delete everything and start over." A partial retail transition port exists: -## 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 -mandates), I wrote simplified approximations: -- Static overlap instead of swept-sphere FindTimeOfCollision -- 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 +This foundation is useful, but it is not complete retail collision parity. +The project now tracks the remaining work as Phase L.2 - Movement & Collision +Conformance: -Each patch fixed one symptom but introduced new edge cases. The result -is a patchwork that handles ~60-70% of cases but fails on the rest. +- Plan: `docs/plans/2026-04-29-movement-collision-conformance.md` +- 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 -complete C# implementation, cross-referencing the decompiled code for -ground truth. ACE has the ENTIRE system already in C#: +Do not guess at AC physics, movement packets, terrain/cell ownership, or +collision constants. The previous patchwork failures came from simplified +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: -- `references/ACE/Source/ACE.Server/Physics/Sphere.cs` -- `references/ACE/Source/ACE.Server/Physics/BSP/BSPTree.cs` -- `references/ACE/Source/ACE.Server/Physics/BSP/BSPNode.cs` -- `references/ACE/Source/ACE.Server/Physics/BSP/BSPLeaf.cs` -- `references/ACE/Source/ACE.Server/Physics/Polygon.cs` -- `references/ACE/Source/ACE.Server/Physics/Transition.cs` -- `references/ACE/Source/ACE.Server/Physics/SpherePath.cs` -- `references/ACE/Source/ACE.Server/Physics/Collision/CollisionInfo.cs` -- `references/ACE/Source/ACE.Server/Physics/Collision/ObjectInfo.cs` +- `CTransition::find_transitional_position` +- `CTransition::transitional_insert` +- `CTransition::step_up` +- `CTransition::step_down` +- `CTransition::edge_slide` +- `CTransition::cliff_slide` +- `SPHEREPATH::step_up_slide` +- `SPHEREPATH::precipice_slide` +- `SPHEREPATH::adjust_check_pos` +- `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): -- **`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). +Older fallback: -### Pseudocode (already written): -- `docs/research/transition_pseudocode.md` — full system documented +- `docs/research/decompiled/chunk_00530000.c` +- `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. -- `PhysicsDataCache.cs` — GfxObj/Setup/CellStruct physics data loading from dats. Correct. -- `ShadowObjectRegistry.cs` — cell-based spatial index. Correct concept, may need refinement. -- `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. +- `references/ACE/Source/ACE.Server/Physics/` +- `references/holtburger/` for movement wire behavior +- `references/AC2D/` for the older client-side movement packet reference -## What to Replace +## Mandatory Workflow -- `BSPQuery.cs` — replace with faithful port of BSPTree/BSPNode/BSPLeaf -- `TransitionTypes.cs` Transition methods — replace FindTransitionalPosition, TransitionalInsert, FindEnvCollisions, FindObjCollisions, SlideSphere, AdjustOffset with faithful ports -- `PhysicsEngine.ResolveWithTransition` — may need restructuring +For every AC-specific function: -## Approach (MANDATORY — per CLAUDE.md) - -For EVERY function: - -1. **GREP NAMED FIRST, then DECOMPILE FALLBACK.** Search the named - retail decomp first: `grep -n "ClassName::Method" docs/research/named-retail/acclient_2013_pseudo_c.txt`. - For struct layouts: `grep -n "^struct ClassName" docs/research/named-retail/acclient.h`. - 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 +1. Grep named retail first. +2. Cross-reference ACE / holtburger / AC2D where relevant. +3. Write readable pseudocode before porting. +4. Port faithfully; do not simplify. +5. Add conformance tests. +6. Integrate surgically into the active L.2 lane. +7. Verify with synthetic tests, real-DAT fixtures, and live observer evidence. diff --git a/memory/project_movement_collision_conformance.md b/memory/project_movement_collision_conformance.md new file mode 100644 index 0000000..f835b07 --- /dev/null +++ b/memory/project_movement_collision_conformance.md @@ -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.