diff --git a/CLAUDE.md b/CLAUDE.md index f883018..f88e77f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -526,8 +526,31 @@ acdream's plan lives in two files committed to the repo: acceptance criteria. Do not drift from the spec without explicit user approval. -**Currently between phases.** Phase C.1.5b just shipped; next phase is -the user's call from the candidate list below. +**Currently in Phase L.2 (Movement & Collision Conformance).** L.2a slices +1+2+3 shipped 2026-05-12 (this evening); the natural next step is the +L.2d slice 1 brainstorm / design spec. Cold-start prompt for the next +session: [`docs/research/2026-05-12-l2d-next-session-prompt.md`](docs/research/2026-05-12-l2d-next-session-prompt.md). +Full handoff: [`docs/research/2026-05-12-l2a-shipped-l2d-handoff.md`](docs/research/2026-05-12-l2a-shipped-l2d-handoff.md). + +**Phase L.2a (Truth & Diagnostics) slices 1-3 shipped 2026-05-12.** +Three commits land the L.2 "make every bad movement outcome explainable" +diagnostic foundation. Slice 1 (`ebef820`) adds runtime-toggleable +`ACDREAM_PROBE_RESOLVE` (one `[resolve]` line per +`PhysicsEngine.ResolveWithTransition` call) + `ACDREAM_PROBE_CELL` (one +`[cell-transit]` line per `PlayerMovementController.CellId` change), +both backed by a new `AcDream.Core.Physics.PhysicsDiagnostics` static +class and mirrored as DebugPanel checkboxes. Slice 2 (`e0c08bc`) extends +the `[resolve]` line with `obj=0x...` attribution. Slice 3 (`a068292`) +populates the previously-stub `CollisionInfo.CollideObjectGuids` / +`LastCollidedObjectGuid` (declared in `TransitionTypes.cs` but never +written anywhere) at the per-object iteration in `FindObjCollisions`, +so the slice-2 promise is now actually delivered. Visual-verified at +the Holtburg Town doorway: probes captured 140 wall hits attributed to +`obj=0xA9B47900` (landblock-baked static = the building itself, +**NOT** a door entity), confirming L.2d sub-direction as **port +`CBuildingObj` collision + per-cell walkability** rather than door- +state-toggle. Plus a definitive L.2e finding: player `CellId` tracked +as bare low byte (`0x00000029`) with no landblock prefix. **Phase C.1.5b (per-part PES transforms + dat-hydrated entity DefaultScript) shipped 2026-05-12.** Closes issue #56. `SetupPartTransforms.Compute(setup)` @@ -585,6 +608,13 @@ together comprise the streaming + rendering perf foundation for the project. **Next phase candidates (in rough preference order):** +- **L.2d slice 1 brainstorm + spec** (`docs/research/2026-05-12-l2d-next-session-prompt.md`). + Direct continuation of tonight's L.2a evidence: port `CBuildingObj` collision + + per-cell walkability so doorway gaps are walkable. Unblocks "walk into a + building" + sets up G.3 dungeon streaming. **Note:** triage the 8 pre-existing + test failures first (none introduced by L.2a slices — verified by stash + rerun + — but most touch movement/physics code L.2d will evolve). See the handoff doc's + "Open concerns" section. - **Triage the chronic open-issue list** in `docs/ISSUES.md` — #2 (lightning), #4 (sky horizon-glow), #28 (aurora), #29 (cloud thinness), #37 (humanoid coat), #50 (stray tree), #41 (remote-motion blips) have been open since @@ -744,6 +774,18 @@ via `PlayerMovementController.ApplyServerRunRate`) or from diagnostics (`[UM_RAW]`, `[SCFAST]`, `[SCFULL]`, `[SETCYCLE]`, `[FWD_WIRE]`, `[OMEGA_DIAG]`, `[SEQSTATE]`, `[PARTSDIAG]`, `[VEL_DIAG]`, `[UPCYCLE]`). Heavy. +- `ACDREAM_PROBE_RESOLVE=1` — L.2a slice 1+2+3 (2026-05-12). One + `[resolve]` line per `PhysicsEngine.ResolveWithTransition` call: + input + target + output position/cell, ok-vs-partial, grounded-in, + contact-plane status, wall normal if hit, **responsible entity + guid** (post-slice-3 attribution plumbing), env flag, walkable + polygon valid. Heavy (~30 Hz × every entity). Runtime-toggleable + via the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`. +- `ACDREAM_PROBE_CELL=1` — L.2a slice 1 (2026-05-12). One + `[cell-transit]` line per `PlayerMovementController.CellId` + change: old → new cell, world position, reason tag + (`resolver` / `teleport`). Low volume — only fires on actual cell + crossings. Runtime-toggleable via the same DebugPanel section. - *(retired 2026-05-05 by L.3 M2/M3)* `ACDREAM_INTERP_MANAGER` was an env-var gate on an experimental per-tick remote motion path. L.3 M2 (commit 40d88b9) replaced both gates (`OnLivePositionUpdated` + diff --git a/docs/plans/2026-04-29-movement-collision-conformance.md b/docs/plans/2026-04-29-movement-collision-conformance.md index cdabd45..dfe8057 100644 --- a/docs/plans/2026-04-29-movement-collision-conformance.md +++ b/docs/plans/2026-04-29-movement-collision-conformance.md @@ -169,6 +169,23 @@ fallback. - Audit `Setup.Radius` and cylinder fallback behavior against retail before relying on them for conformance. +Current sub-direction (2026-05-12, evidence-driven by L.2a slice 2 + 3): +The "I can't walk through doorways" symptom at Holtburg is **NOT a door- +state-toggle issue**. The `[resolve]` probe captured 140 hit=yes lines +at the doorway with `obj=0xA9B47900` (126 hits) — a landblock-baked +static in the `0xLLLLxxxx` range, i.e. the **building itself**, not a +door entity (no `0xCC0Cxxxx`-range hits). The building's baked collision +mesh is treated as one solid block; the doorway gap that's visible in +the rendered mesh isn't represented in the collision data we consume. + +L.2d slice 1's scope is therefore the `CBuildingObj` + per-cell +walkability port (interpretation 2 of the handoff). The named retail +anchors `CCellStruct::point_in_cell`, `CCellStruct::sphere_intersects_cell`, +`CCellStruct::box_intersects_cell`, `CBuildingObj::find_building_collisions` +are the entry points. Spec to be written at +`docs/superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md`. +Handoff: [docs/research/2026-05-12-l2a-shipped-l2d-handoff.md](../research/2026-05-12-l2a-shipped-l2d-handoff.md). + ### L.2e - Cell Ownership: Outdoor Seams, CELLARRAY, cell_bsp Goal: the resolver knows which cell owns the movement and which adjacent cells diff --git a/docs/research/2026-05-12-l2a-shipped-l2d-handoff.md b/docs/research/2026-05-12-l2a-shipped-l2d-handoff.md new file mode 100644 index 0000000..9ecfcaa --- /dev/null +++ b/docs/research/2026-05-12-l2a-shipped-l2d-handoff.md @@ -0,0 +1,176 @@ +# L.2a shipped — L.2d direction confirmed — Cold-Start Handoff + +**Created:** 2026-05-12 evening, immediately after the L.2a-slice-1/2/3 work landed and visual-verified. +**Audience:** the next agent picking up Phase L.2 (Movement & Collision Conformance). +**Purpose:** give you everything you need to start L.2d brainstorming cold, without spelunking through this session's transcript. + +--- + +## TL;DR + +Phase L.2a (Truth & Diagnostics) shipped three slices tonight. They surfaced **three concrete L.2 findings** with reproducible evidence — converting "we should look at this someday" theories into "here is the entity id, here is the wall normal, here is the cell id." With those findings in hand, the next concrete physics work in the L.2 roadmap is **L.2d slice 1 — port `CBuildingObj` collision so doorway gaps are walkable.** Brainstorm + spec, then port. + +**Three slices shipped to `claude/intelligent-poitras-b2c4f9`:** + +| Commit | What | Why | +|---|---|---| +| [`ebef820`](.) | L.2a slice 1: `[resolve]` + `[cell-transit]` probes + DebugPanel mirror | Foundation for every later L.2 change to be evidence-driven | +| [`e0c08bc`](.) | L.2a slice 2: surface hit object guid in `[resolve]` line | Tell us WHICH entity is the wall, not just the wall normal | +| [`a068292`](.) | L.2a slice 3: populate the previously-stub `CollisionInfo.CollideObjectGuids` / `LastCollidedObjectGuid` | Slice 2 found these fields were declared but never written — fixed the structural gap | + +--- + +## Three findings from the L.2a probes + +All produced by walking around Holtburg + pushing W into a Town doorway with `ACDREAM_PROBE_RESOLVE=1 ACDREAM_PROBE_CELL=1`. + +### Finding 1 — L.2e cell-id format gap (DEFINITIVE) + +The player's tracked `CellId` is being recorded as a **bare low byte** (`0x00000029`), with no landblock prefix. AC cell ids are normally `0xLLLLCCCC` — landblock id (4 hex digits) + cell-within-landblock (4 hex digits, `0x0001-0x00FF` outdoor or `0x0100+` indoor). + +Evidence from a tonight log: + +``` +[cell-transit] 0x00000001 -> 0x00000029 pos=(132.585,21.015,94.000) reason=resolver +``` + +NPCs in the same area show MIXED forms in their resolve lines: +- `cell=0xA9B3000E` ← full landblock-prefixed (correct) +- `cell=0x00000032` ← bare low byte (matches the bug shape) + +Likely source: `ResolveOutdoorCellId(...)` at [src/AcDream.Core/Physics/PhysicsEngine.cs:687](src/AcDream.Core/Physics/PhysicsEngine.cs:687) — that's the function that ResolveWithTransition routes the output cell id through before returning. Worth grepping for its body. + +This is the L.2e blocker per the plan-of-record: +> *"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`."* + +### Finding 2 — L.2c wall-slide is working + +The transition layer at this spot does the retail-faithful thing: + +``` +[resolve] ent=0x000F4240 in=(132.067,17.567,94.000) cell=0x00000029 + tgt=(132.239,17.172,94.000) + out=(131.938,17.567,94.000) cell=0x00000029 + ok=True groundedIn=True cp=valid hit=yes n=(0.00,1.00,0.00) + obj=0xA9B47900 walkable=True +``` + +- Wall normal `(0, 1, 0)` — vertical wall facing +Y, captured correctly. +- `out` shows the position clamped along the wall: X slid back from 132.067 → 131.938, Y preserved. +- `ok=True` — resolver completed normally (no `ok=False` anywhere in the trace, 0/140). + +**No L.2c work needed at this site.** Edge-slide / wall-slide port from earlier (per the plan-of-record's L.2c "Current shipped slice" note) is doing its job here. + +### Finding 3 — L.2d sub-direction = CBuildingObj port (NOT door-toggle) + +All 140 hit=yes lines in the doorway-push test came back with the **same dominant `obj=` attribution**: + +| obj | hits | range | what it is | +|---|---|---|---| +| **`0xA9B47900`** | **126** | `0xLLLLxxxx` (landblock-baked static) | The Holtburg building itself — its baked collision mesh | +| `0x000F4245` | 14 | `0x000Fxxxx` (local-spawn entity) | An NPC standing near the doorway | + +`0xA9B4` matches the Holtburg landblock prefix we logged at startup (`loading world view centered on 0xA9B4FFFF`). The `0x7900` low bytes is its landblock-local entity id. **It's the building's baked collision shape — not a door entity, not a creature.** + +**Implication:** the "doorway is blocked" symptom is NOT a door-collision-not-toggled bug (which would have shown a door-range entity id, typically `0xCC0Cxxxx`). It's a **building-mesh fidelity issue**: the building's baked collision data we're loading represents the building as a solid block with no walkable opening where the visual doorway is. + +Two non-mutually-exclusive interpretations: +1. **Collision-mesh extraction is wrong** — we load building geometry but don't respect the BSP nodes that encode doorway openings. +2. **`CBuildingObj` + per-cell walkability is not ported** — retail uses a per-cell `CObjCell` structure that maps "this interior cell is reachable" / "this exterior cell connects to those interior cells." Without that, we treat the building as one opaque collision volume. + +The plan-of-record's L.2d goal: +> *"Preserve enough building identity to model `CBuildingObj` collision and `bldg_check` behavior."* + +points at interpretation 2 as the canonical fix. + +--- + +## What this session deliberately did NOT do + +- **Other L.2a slices** (contact-plane probe, ShadowObject hit log, water probe, real-DAT fixture-capture pipeline). Slice 1 + 2 + 3 cover the most-load-bearing case (resolver outcomes + cell transits + entity attribution). The remaining diagnostics serve future L.2 work and can ship opportunistically. +- **L.2d implementation or brainstorm.** Deliberately parked for a fresh session with this evidence as cold-start context. +- **L.2e implementation.** The cell-id format finding is filed but not investigated. +- **Pre-existing test failures.** 8 tests fail at the branch base (none from these slices — verified by stash + rerun on every test cycle). Not from this slice. See "Open concerns" below. + +--- + +## Branch state at handoff + +- Branch: `claude/intelligent-poitras-b2c4f9` +- Three slice commits ahead of `eab347d` (the C.1.5b merge into main), plus a docs commit that adds this handoff + the next-session prompt + plan-of-record / CLAUDE.md updates. +- Tonight's last code commit was `a068292` (L.2a slice 3); docs commit follows. +- Worktree clean post-docs-commit; merge to main is the user's planned next operation. + +## What's now in the diagnostic surface + +Live env vars (both can be flipped at runtime via the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`): + +- **`ACDREAM_PROBE_RESOLVE=1`** — one `[resolve]` line per `PhysicsEngine.ResolveWithTransition` call: + ``` + [resolve] ent=0xEEEEEEEE in=(x,y,z) cell=0xCCCCCCCC tgt=(x,y,z) out=(x,y,z) cell=0xCCCCCCCC ok=Y/N groundedIn=Y/N cp=valid|lastKnown|none hit=yes n=(nx,ny,nz) obj=0xOOOOOOOO env nObj=N walkable=Y/N + ``` + Heavy: fires for every entity's resolve per physics tick. +- **`ACDREAM_PROBE_CELL=1`** — one `[cell-transit]` line per `PlayerMovementController.CellId` change: + ``` + [cell-transit] 0xOLD -> 0xNEW pos=(x,y,z) reason=resolver|teleport + ``` + Low volume — only fires on actual cell crossings. + +Both backed by `AcDream.Core.Physics.PhysicsDiagnostics` static class (initial from env var, set/get from anywhere at runtime). + +## Files changed in this session + +``` +src/AcDream.Core/Physics/PhysicsDiagnostics.cs (new) +src/AcDream.Core/Physics/PhysicsEngine.cs (modified — probe emission) +src/AcDream.Core/Physics/TransitionTypes.cs (modified — entity attribution plumbing) +src/AcDream.App/Input/PlayerMovementController.cs (modified — UpdateCellId chokepoint) +src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs (modified — Probe* forwarder props) +src/AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs (modified — two new checkboxes) +docs/plans/2026-04-29-movement-collision-conformance.md (modified — shipped-slice note + L.2d sub-direction) +``` + +## Open concerns flagged but NOT addressed in this session + +- **8 pre-existing test failures** on the branch base, verified by stash+rerun: `MotionInterpreterTests.GetMaxSpeed_*` (3), `PositionManagerTests.ComputeOffset_BothActive_Combined`, `PlayerMovementControllerTests.Update_ForwardInput_MovesInFacingDirection`, `DispatcherToMovementIntegrationTests.Dispatcher_W_held_produces_forward_motion`, `BSPStepUpTests.{D4_AirborneMover_TallWall_PersistsSlidingNormalAcrossFrames,C3_Path6_AirborneMoverHitsSteepSlope_SetsCollide}`. Most touch movement/physics code we're about to evolve in L.2b/L.2c/L.2d — **triage before further L.2 work** is recommended. +- **Player entity id quirk.** Local player physics entity id observed as `0x000F4240` in the resolve probe, not the server guid `0x5000000A`. This is presumably the dat/local-spawn entity id — fine for diagnostic, worth keeping in mind for any future "is this the player?" check. + +## Cold-start checklist for L.2d brainstorm + +1. Read this handoff. +2. Read [docs/plans/2026-04-29-movement-collision-conformance.md](docs/plans/2026-04-29-movement-collision-conformance.md) — focus on L.2d section. +3. Read the L.2d named-retail anchors: + - `CCellStruct::point_in_cell`, `CCellStruct::sphere_intersects_cell`, `CCellStruct::box_intersects_cell` + - `CBuildingObj::find_building_collisions` + - `CObjCell::find_cell_list` (already shared with L.2e) + Grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` by `class::method`. +4. Read [src/AcDream.Core/Physics/TransitionTypes.cs:1386](src/AcDream.Core/Physics/TransitionTypes.cs:1386) — current `FindObjCollisions` loop, where building objects currently route through generic BSP/Cylinder paths. +5. Read [src/AcDream.Core/Physics/PhysicsDataCache.cs](src/AcDream.Core/Physics/PhysicsDataCache.cs) — how we currently load BSP / GfxObj data; figure out if building-specific data (interior cells, `CBuildingObj`) is loaded but not consumed. +6. Cross-reference WorldBuilder (`references/WorldBuilder/`) for any building-cell handling already present. +7. Brainstorm the slice (`superpowers:brainstorming` if useful) — scope, named-retail anchors, conformance tests, real-DAT fixtures. +8. Write a spec at `docs/superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md`. +9. Implement in slices with conformance citations in each commit. + +## Reproducing the doorway evidence + +In case you want to re-capture the trace: + +```powershell +# In the project worktree +$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call" +$env:ACDREAM_LIVE = "1" +$env:ACDREAM_TEST_HOST = "127.0.0.1" +$env:ACDREAM_TEST_PORT = "9000" +$env:ACDREAM_TEST_USER = "testaccount" +$env:ACDREAM_TEST_PASS = "testpassword" +$env:ACDREAM_DEVTOOLS = "1" +$env:ACDREAM_PROBE_CELL = "1" +$env:ACDREAM_PROBE_RESOLVE = "1" +dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch.log" +``` + +Walk acdream up to a Holtburg building doorway. Hold W into it for ~2 seconds. Close. Grep `launch.log` for: +- `cell-transit` — cell tracking +- `\[resolve\].*hit=yes` — wall hits with object attribution + +Wall entity should appear as `obj=0xA9B47XXX` for the same Holtburg building, OR a different `0xA9Bxxxxx` for other buildings in the area. diff --git a/docs/research/2026-05-12-l2d-next-session-prompt.md b/docs/research/2026-05-12-l2d-next-session-prompt.md new file mode 100644 index 0000000..bcb7395 --- /dev/null +++ b/docs/research/2026-05-12-l2d-next-session-prompt.md @@ -0,0 +1,51 @@ +# Copy-paste prompt — next session for L.2d brainstorm + +**This file is meant to be pasted verbatim into a new Claude Code session.** It assumes the next session starts on a freshly-merged `main` with the L.2a-slice-1/2/3 work already landed. + +--- + +## Prompt to paste + +> You are picking up Phase L.2d (Movement & Collision Conformance — Shape Fidelity: Sphere / CylSphere / Building Objects) for the acdream project. +> +> The previous session shipped L.2a-slice-1/2/3 (resolver + cell-transit probes + entity attribution plumbing) and used the probes to settle the L.2d sub-direction call: **the wall blocking us at building doorways is a landblock-baked static (`0xA9B47900` for the Holtburg test building), NOT a door entity.** The fix is to port `CBuildingObj` + per-cell walkability so the building's baked collision mesh has walkable openings where doorways are. Door-state-toggle is NOT the issue. +> +> Before writing any code: +> +> 1. **Read the handoff:** `docs/research/2026-05-12-l2a-shipped-l2d-handoff.md` — full context, evidence, file pointers. +> 2. **Read the plan-of-record:** `docs/plans/2026-04-29-movement-collision-conformance.md` — focus on L.2d, and notice that L.2c already shipped most of its work + L.2a is now ~75% covered. +> 3. **Read the named-retail anchors** (grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` by `class::method`): +> - `CCellStruct::point_in_cell` +> - `CCellStruct::sphere_intersects_cell` +> - `CCellStruct::box_intersects_cell` +> - `CBuildingObj::find_building_collisions` +> - `CObjCell::find_cell_list` +> 4. **Read current code:** +> - `src/AcDream.Core/Physics/TransitionTypes.cs:1386` — `FindObjCollisions` (where building objects currently flow through generic BSP path). +> - `src/AcDream.Core/Physics/PhysicsDataCache.cs` — what building-specific data we already load vs ignore. +> 5. **Cross-reference WorldBuilder** at `references/WorldBuilder/` for any building-cell handling we can crib. +> +> Your deliverable for this session: +> +> 1. A brainstorm using `superpowers:brainstorming` if scope is unclear, then +> 2. A design spec at `docs/superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md` covering: +> - Named-retail anchors with line numbers from the PDB pseudo-C +> - Component breakdown (CObjCell port, CBuildingObj port, integration with FindObjCollisions) +> - Conformance test plan (synthetic + real-DAT fixtures at known Holtburg buildings) +> - Slice plan (3-5 commits, each conformance-cited) +> - Acceptance criteria +> 3. After spec approval, implement slice 1. +> +> **Before implementation,** verify the L.2a probes still work — relaunch with `ACDREAM_PROBE_RESOLVE=1 ACDREAM_PROBE_CELL=1 ACDREAM_DEVTOOLS=1`, walk up to the Holtburg test doorway, confirm `[resolve]` lines still show `obj=0xA9B4xxxx` for the wall hits. (Reproduction recipe in the handoff doc's last section.) +> +> Side note: **8 pre-existing test failures** exist on main (verified by stash+rerun in the prior session, none from L.2a slice work). Most touch movement/physics code we're about to evolve. **Triage them before sinking deep L.2d effort** — a recent baseline regression in this area could waste hours of L.2d work. + +--- + +## Reading order if you only have 10 minutes + +1. `docs/research/2026-05-12-l2a-shipped-l2d-handoff.md` — TL;DR + Three findings sections (5 min). +2. `docs/plans/2026-04-29-movement-collision-conformance.md` §L.2d (2 min). +3. `src/AcDream.Core/Physics/TransitionTypes.cs:1386-1543` — current `FindObjCollisions` body (3 min). + +From there, decide whether to brainstorm or jump straight to the spec.