From 3dcd53cab753b1f98055fb56e9e51d8cfdaaad28 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 9 Jul 2026 13:52:41 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20bulk=20issues=20triage=20=E2=80=94=20cl?= =?UTF-8?q?ose=2027,=20note=2010=20investigated-open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-driven triage pass over ~60 open issues: 19 closed from the user's own recollection, 8 more closed after this session's parallel investigation confirmed the underlying bug/task no longer applies (#96 accepted-divergence reclassification, #60/#68/#126/#4/#81/#33 fixed by commits that never cross-referenced their issue number, #87 superseded by Phase O + Phase A8). 9 issues investigated and confirmed still genuinely open got a dated triage note appended in place (#116/#104/#178/#29/#146/#147/#148/#156/#72); #41 got a user-note only. Two investigated issues (#148, #156) turned up real completed fixes sitting on an unmerged branch (claude/peaceful-visvesvaraya-e0a196, 11 commits, diverged from main 168 commits ago) — flagged in their triage notes and separately to the user, not acted on here. Verified before committing: header count unchanged (200 before/after, no blocks lost or duplicated), spot-checked 6+ closures across every category (user-closed, investigation-closed, stayed-open-with-note, the #87 duplicate-heading edge case) for accuracy against the underlying investigation evidence. Co-Authored-By: Claude Sonnet 5 --- docs/ISSUES.md | 1301 ++++++++---------------------------------------- 1 file changed, 202 insertions(+), 1099 deletions(-) diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 0c1eadb9..5cd2ec4b 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -611,29 +611,6 @@ stay continuous in `[flap-sweep]`); camera glides along walls like retail. --- -## #179 — Lightning flash has no indoor gate (dormant until weather strobes ship) - -**Status:** OPEN (dormant — zero production impact today) -**Severity:** LOW (latent) -**Filed:** 2026-07-06 -**Component:** render — scene lighting UBO / weather - -**Description:** `mesh_modern.frag` adds `uFogParams.z × vec3(0.6, 0.6, 0.75)` -(the lightning-flash bump) to EVERY fragment, and `SceneLightingUbo.Build` -copies `atmo.LightningFlash` with no indoor gating (the GameWindow fog -override explicitly preserves `.z`). Today `WeatherState._flashLevel` is 0 -in production ("Production never TriggerFlashes" — test hook only), so -nothing is visible. The moment storm strobes ship, sealed dungeons will -flash blue-violet with every strike. Retail's indoor lighting path (flat -ambient, sun killed via the seen_outside gate) carries no storm terms. - -**Acceptance:** the flash term is zeroed for `playerInsideCell` frames (or -gated at the UBO build), verified by a storm-in-dungeon probe when weather -strobes land. Found during the #176/#177 investigation -(`docs/research/2026-07-06-176-177-render-pair-investigation.md`). - ---- - ## #178 — Retire the A8 double-sided cell-shell stopgap (CullMode.Landblock → None) **Status:** OPEN @@ -655,6 +632,8 @@ extraction?) + a visual gate — walls/floors must not vanish. surfaces at the Holtburg + Facility Hub gates. Found during the #176/#177 investigation (`docs/research/2026-07-06-176-177-render-pair-investigation.md`). +**2026-07-09 triage:** investigated, verdict STILL_OPEN — the `CullMode.Landblock -> CullMode.None` double-sided stopgap is still present verbatim at `EnvCellRenderer.cs:1394-1399` with no follow-up commit or roadmap reference retiring it. + --- ## #177 — Dungeon stairs pop in/out across levels (invisible until entering the room; last step vanishes running down) @@ -1580,6 +1559,8 @@ R5/R6 touches the action list. **Where:** **Files:** `src/AcDream.App/Studio/FixtureProvider.cs` (case 0x21000023u), `src/AcDream.App/Studio/StudioWindow.cs`, `src/AcDream.App/Studio/PanelFbo.cs`. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — a fix for this exact bug (`9444a328`, normalizing the panel root to (0,0) for off-screen FBO captures) exists but only on unmerged branches (`codex/mockup-stage` / `claude/peaceful-visvesvaraya-e0a196`); `StudioWindow.cs` on this branch still has no position-normalization code and the mechanism is unfixed here. + ## #155 — Outdoor terrain textures look stretched + blurry vs retail (missing landscape DETAIL-texture overlay) **Status:** OPEN — **root cause VERIFIED (oracle-first); fix attempt implemented + REVERTED** (rendered the ground black). Ready for a focused fresh implementation pass. @@ -1768,6 +1749,8 @@ toggle (the window manager / `UiHost` RegisterWindow + the F12 toggle path) and on the inventory window's visibility change. Investigate the retail status-bar backpack element + its open/closed sprites when picked up. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — the only inventory-toggle path is still the F12 keybind (`GameWindow.cs` `ToggleInventoryPanel`) with no status-bar button anywhere in `src/`; a status-bar-toggle implementation exists but only on an unmerged branch (`b7dc91a0` on `claude/peaceful-visvesvaraya-e0a196`), not in this branch's history. + --- ## #147 — Inventory item-grid scrolling polish @@ -1781,6 +1764,8 @@ whole-row clip via `UiScrollable`) works but needs visual/UX polish — smoothne wheel step, row-clip edges. Confirm the exact symptoms with the user when picked up. Filed from the D.2b inventory visual gate. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — `UiItemList.LayoutCells` still does whole-row visibility clipping and `UiScrollable.cs` is still an unanimated integer-pixel scroll model; no polish commit has landed since filing, and the roadmap/memory crib both still list it as pending. + --- ## #146 — D.2b inventory capacity-bar visual polish @@ -1807,6 +1792,8 @@ against retail): See divergence register **AP-59**. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — `UiItemSlot.cs` still right-anchors the bar and hardcodes bottom-up fill (`m_eDirection` unread) and `InventoryController.SetCapacityBar` still has no closed-bag lazy-load; no commit has touched this since the 2026-06-22 filing commit. + --- ## #145 — Inventory window panels occluded by the full-window backdrop (importer ignores ZLevel) @@ -1828,25 +1815,6 @@ See divergence register **AP-59**. --- -## #154 — Dungeon interiors still read too dim vs retail (torch-sparse stretches + per-vertex bake) - -**Status:** OPEN -**Severity:** MEDIUM (dungeons "better but not good enough" — user, 2026-06-20) -**Filed:** 2026-06-20 -**Component:** render — indoor lighting (dungeon) - -**Description (user, 2026-06-20):** After the indoor-lighting fixes (`0d8b827`/`57c2ab7`) dungeons are much better lit than the old flat-0.2 dark, but still not good enough vs retail. The `0x0007` Town Network dungeon HAS torches (the log shows 62 orange + 57 cream `intensity=100` lights selected, plus the viewer fill + magenta portals), so torch cells light up — but torch-sparse corridors (e.g. cell `0x0149`) are lit only by the viewer fill on the 0.2 ambient, and the overall brightness still trails retail. - -**Root cause / status (candidates, UNVERIFIED — do not guess):** (a) the per-vertex Gouraud bake on low-poly dungeon walls under-lights torch cells (torches evaluated only at the few cell-struct corners — AP-35 residual); (b) the sealed-dungeon flat 0.2 ambient may be too dark vs retail; (c) retail may lean harder on the viewer light or carry more/brighter dynamics. **Approach:** side-by-side cdb capture of retail's dungeon (`world_lights` active set + `world_lights.ambient_color` + the viewer-light intensity) at the SAME spot, compared to acdream's `[light]`/`[light-detail]` there. - -**Files:** `mesh_modern.vert pointContribution` (per-vertex bake); `LightManager` (viewer light); `GameWindow.UpdateSunFromSky` (0.2 sealed ambient); `EnvCellRenderer.GetCellLightSet`. - -**Research:** memory `reference_retail_ambient_values.md` (2026-06-20 resolution note); cdb toolchain (CLAUDE.md). - -**Acceptance:** dungeon brightness/warmth matches retail side-by-side (torch cells AND torch-sparse stretches). - ---- - ## #142 — Windowed-building interiors read "like outdoors" (indoor lighting regime is per-frame, not per-stage) **Status:** DONE (2026-06-20) — `ef5049f` (per-instance sun gate) + `0d8b827`. The diagnosed cause (per-frame sun/ambient regime) was a RED HERRING: the ambient + sun were already retail-faithful. The REAL bug was a landblock-key lookup in `EnvCellRenderer.GetCellLightSet` (`cellId & 0xFFFF0000` vs the streaming key `0xXXYYFFFF`) that starved EVERY interior wall of point lights. Once fixed, interiors lit correctly (+ the retail viewer light + weenie fixture lights). User-confirmed "looks like retail now." (Move to Recently closed on next ISSUES tidy.) @@ -2300,203 +2268,6 @@ neighbour load/unload churn). --- -## #134 — Player "lags downward" instead of gliding along a dungeon ramp edge - -**Status:** OPEN -**Severity:** LOW-MEDIUM (movement feel; not a hard traversal block) -**Filed:** 2026-06-14 -**Component:** physics — slope-walk / edge-slide response - -**Description:** Running up or down against a dungeon ramp's edge, the player "sort of lags -downwards" instead of gliding/sliding ALONG the ramp surface (up when running up, down when -running down). Reported in the 0x0007 Town Network dungeon ramp after #133. - -**Root cause / status:** Surfaced (not caused) by the #133 connector-cell physics -registration (`3e006d3`): the ramp connector cell's collision is now fully resident in the -physics graph, so the slope-walk / edge-slide response on it is exercised for the first time. -"Lag down" suggests the slide velocity is projected toward gravity rather than along the -contact plane (the slope tangent). Likely the retail edge-slide / slope-slide response is -incomplete — see #32 (retail edge-slide/cliff-slide/precipice-slide incomplete) and the -AP-6 / TS-1 / TS-4 slide rows in the divergence register. NO band-aid — port the retail -slide-response. - -**Files:** `src/AcDream.Core/Physics/` (slide-response in TransitionTypes / BSPQuery); ramp -cell 0x0007014D + neighbours. - -**Acceptance:** Running up a walkable ramp climbs it smoothly; running into the edge slides -along the slope (up/down per input direction), matching retail feel. - ---- - -## #133 — Teleport into a dungeon snaps the player BEFORE the dungeon landblock streams in → lands at the old landblock's frame (ocean), not the dungeon - -**Status:** OPEN — promoted to **Phase G.3** (Dungeon streaming + portal -space + `PlayerTeleport` handling), **PULLED INTO M1.5** (user decision -2026-06-13: the indoor world isn't done while dungeons are broken; full -G.3 scope chosen). Spec: `docs/superpowers/specs/2026-06-13-dungeon-support-design.md`; -G.3a plan: `docs/superpowers/plans/2026-06-13-dungeon-support-g3a.md`. -This is now an M1.5 exit-gate blocker, not deferred. - -**PROGRESS (2026-06-13 PM — G.3a core LANDED + Bug A fixed; gate exposed #95):** -the teleport-timing root cause IS fixed. G.3a shipped the `TeleportArrivalController` -hold-until-hydration (`7947d7a`/`aca4b46`/`f22121b`) + the validated-claim -landblock-prefix fix (`2ce5e5c`, "Bug A"). Live gate proof: a real `PlayerTeleport` -into the `0x0007` dungeon held through the 46 km jump and grounded the player on the -dungeon's walkable floor (`[snap] claim=0x00070143 VALIDATED -> z=0.000`) — **no -ocean.** The "terrain-less landblock" framing was refuted earlier (dat probe: dungeon -= flat-terrain LandBlock + EnvCells). REMAINING blockers, both exposed at the gate: -(1) **#95 CONFIRMED LIVE** — the dungeon renders as "thin air" because WB-DIAG blows -up to ~9.1M instances/frame at `0x0007` (see #95); (2) **possible Bug C** — per-tick -membership may still drift in the dungeon's negative-local-Y frame (ACE `movement -pre-validation failed` spam) — re-gate after Bug A to confirm. NOTE: a render-only -EnvCell hydration decouple was tried in G.3a and REVERTED (`e7058ca`) — it made the -player character invisible at Holtburg (it touched the shared building hydration -path); re-approach separately if a geometry-less collision cell ever needs it. - -**NEW GAP (2026-06-13 PM — login-INTO-a-dungeon):** logging in while the saved -character is inside a far dungeon hangs at the auto-entry hold (player frozen, -no `[snap]`/`auto-entered player mode`, movement input ignored). Root: the -streaming center is set ONCE at startup to the default (`_liveCenterX/Y = centerX/ -centerY`, `GameWindow.cs:1942` → "centered on 0xA9B4FFFF") and the login spawn never -recenters it; a dungeon spawn 46 km away never streams, so `IsSpawnCellReady(spawn -cell)` stays false and the #107 hold waits forever. The TELEPORT-arrival path -recenters (G.3a `TeleportArrivalController`); the LOGIN path does not. Fix shape = -recenter streaming onto the spawn landblock when the login spawn first arrives -(mind the #107 auto-entry hold's `SampleTerrainZ(pe.Position)` frame after the -recenter). Pre-existing; only surfaces now that the test character can be saved in -a dungeon. Workaround to unblock testing: move `+Acdream` out of the dungeon -server-side (ACE) before logging in. **FIXED 2026-06-13 (`47ae237`)** — the login -player-spawn path now recenters `_liveCenterX/Y` onto the spawn landblock (mirrors -the teleport-arrival recenter; no-op for a same-landblock Holtburg login). Verified -live: `live: login spawn — recentering streaming from (169,180) to (0,7)` → dungeon -streams → `auto-entered player mode` in the dungeon. - -**✅ DUNGEON RENDERS — M1.5 milestone (2026-06-13 PM, autonomous /loop, objectively -verified).** With Bug A (`2ce5e5c`) + login-into-dungeon (`47ae237`), a live launch -into the `0x0007` dungeon: player grounded on the dungeon floor (`[snap] claim=0x00070143 -VALIDATED z=0.000`), correct membership (cell stays `0x0007…`, ZERO ACE `failed -transition` spam), and the render budget is sane — **WB-DIAG instances ~39,000 -(meshMissing=0)** vs the 9.1M pre-Bug-A blowup (#95, now RESOLVED as a Bug-A symptom). -User-confirmed: "no errors from ACE this time." - -**✅ DUNGEON FPS FIXED + GREY BARRIER FIXED (2026-06-14, user-confirmed).** Two -separate causes, both resolved: - -- **FPS (was 14–30, now ~1000+):** AC dungeons sit adjacent in the "ocean" landblock - grid, so the 25×25 (farRadius=12) streaming window pulled ~129 neighbour dungeons + - their ~19k particle emitters / entities each frame. Fix = **collapse streaming to the - player's single dungeon landblock** when CurrCell is a sealed EnvCell (`!SeenOutside`), - with landblock-level hysteresis to stop collapse↔expand thrash. Confirmed against ACE - (`landblock.IsDungeon → return adjacents` with no neighbours): dungeons have no neighbour - landblocks, so collapsing to the one block is retail-faithful. Commits `5686050` (collapse) - + `d9e7dd6` (hysteresis) + `2561918` (pin to CurrCell's landblock, not the position-derived - one — the negative cell-local-Y made `floor(pp.Y/192)` land one block off and unload the - REAL dungeon). Divergence register: AP-36. - -- **GREY BARRIER (the "barrier above the ramp" / cellar-mouth grey):** portals-only - connector cells (ramp mouths, stair landings, cellar throats) build **0 drawable - sub-meshes**, and BOTH cell-registration gates (`BuildLoadedCell` → visibility - `_cellVisibility`, and `CacheCellStruct` → the physics cell graph) were gated on - `cellSubMeshes.Count > 0`. So a connector cell never registered → the portal flood - hit a **lookup-miss** at its opening (the un-flooded opening shows the clear/grey - colour) AND the camera eye-sweep couldn't transit through it. Fix = register EVERY - cell with a valid cellStruct for visibility + physics; only the *drawing* registration - stays gated on having sub-meshes. Commits `d90c538` (visibility) + `3e006d3` (physics - graph). The physics-graph half EXPOSED the ramp slide-response feel (now **#134**). - Three render-MATH theories (portal_side centroid, on-screen clip, near-eye projection) - were instrumented and REFUTED before the real lookup-miss cause was found — apparatus - discipline held. Render-pipeline digest updated. - -Residual (filed separately): login FPS ramp **#135**; ramp slide-response **#134**; the -A7 per-vertex lighting bake (below) is the remaining "lighting off" work. - -**✅ A7 dungeon lighting — selection fix LANDED + objectively verified (`a80061b`).** The -"lighting off" report was NOT missing torches — the `ACDREAM_PROBE_LIGHT` diagnostic -(`d6fb788`) showed the dungeon correctly gets retail's flat 0.2 indoor ambient + sun zeroed -(`UpdateSunFromSky`, `playerInsideCell` true) AND **2227 torch/point-lights register**. The -bug was the active-light SELECTION: `LightManager.Tick` dropped any light whose range didn't -reach the VIEWER (`DistSq > Range²·slack² → skip`), so a room with 2227 torches lit only the -~1 the player stood inside (`activeLights≈1`, rest at flat 0.2). Retail's D3D model picks the -8 NEAREST lights and applies the hard range-cutoff PER SURFACE in the shader -(`mesh_modern.frag: if (d < range)`). Fix = drop the viewer-range candidacy filter, take the -nearest 8. Probe after: **`activeLights` 2→8** in the dungeon (the room's 8 nearest torches now -light it). Core lighting suite green. Then `Range = Falloff × 1.5` (retail `rangeAdjust`, -`config_hardware_light` 0x0059adc, `a80061b`+) widened the pools. Ambient 0.20 is -retail-faithful (`SmartBox::SetWorldAmbientLight(0.2f)`); the 0.30 was a red herring -(`CreatureMode` paperdoll renderer, not world cells). - -**⚠️ REAL remaining cause — REVISED 2026-06-14 (the earlier "mis-read intensity" theory is -REFUTED).** `intensity=100` is the **REAL dat value** (raw-byte verified `00 00 C8 42` = 100.0f; -DatReaderWriter 2.1.7 parses it correctly; the garbage `cone` is MSVC `CD CD CD CD` -uninitialized fill Turbine baked into the dat — point lights never read it). **DO NOT `÷100`.** -The actual divergence is the **[HIGH] `no-static-light-burnin`**: retail bakes ALL of a cell's -reaching static lights **PER-VERTEX once** (`D3DPolyRender::SetStaticLightingVertexColors` -0x0059cfe0 → `calc_point_light` 0x0059c8b0, Gouraud-interpolated → uniform, never blown out via -the per-channel min-to-colour clamp), while we light **per-PIXEL with only the 8 nearest-to- -CAMERA lights** → bright pools near torches, dark between, and a crescent that slides as the -camera re-ranks the 8-slot list. Diagnosed via a 5-agent investigation + a clean Ghidra -decompile (the BN pseudo-C is x87-mangled). **LANDED:** the per-pixel `(1-dist/falloff_eff)` -shader ramp (`007e287`, necessary but NOT sufficient — it can't fix the per-vertex-vs-per-pixel -structure) + the GL-free `LightBake` Core (`3b93f91`: the verbatim `calc_point_light` port + -7 conformance tests). **REMAINING — the A7 integration:** add a per-vertex linear-RGB colour -attribute to the cell mesh + a bake driver keyed on `envCellId` (NOT the dedup `cellGeomId` — -adjacent rooms share a geom but not their torches) + consume it in `mesh_modern.frag` for cell -draws; bound the bake's light set to the player dungeon (#133's FPS collapse already does this). -Belongs to the #79/#93 indoor-lighting umbrella; outdoor static objects + building shells still -use the per-pixel-8 path (the same spottiness — separate follow-up). **NOTE — dungeon FPS is -FIXED** (was 14–30 from streaming ~129 neighbour ocean-grid dungeons; now ~1000+ fps after the -#133 streaming collapse + the allocation-free 8-light partial-select, `5872bcf`/`5686050`). -**Severity:** HIGH (any far/dungeon teleport is unusable) -**Filed:** 2026-06-13 (M1.5 dungeon-demo gate attempt — meeting-hall portal) -**Component:** physics/streaming — teleport-arrival snap vs async landblock hydration - -**Symptom (user):** used the meeting-hall portal to a dungeon; "no -dungeon, just ocean (where the dungeon is placed)." ACE spams `failed -transition for +Acdream from 0x01250126 [30 -60 6.0] to 0xA9B0000E -[-32227 -26748 5.9]` … marching south through `0xA993/0xA97F/…/0xA969` -at Z≈−0.9 (underwater) — the server keeps rejecting the client's bogus -outdoor movement. - -**Root cause (confirmed against code + the diagnostic log -`launch-dungeon-diag.log`):** ACE correctly placed the player in the -meeting-hall dungeon cell `0x01250126` (landblock `0x0125` = (1,37)). The -acdream teleport-arrival handler (`GameWindow.cs:4877-4960`) DOES recenter -the streaming origin to (1,37) (`_liveCenterX/Y`, :4910-4912), but then -**immediately** calls `_physicsEngine.Resolve(pos=(30,-60,6.005), -cell=0x01250126)` to snap the player (:4928-4931) — BEFORE the dungeon -landblock has streamed in. The physics engine still has only the OLD -Holtburg landblocks resident (A9B4 + neighbours), so `Resolve` can't find -the dungeon cell and falls back to an OUTDOOR scan against the resident -landblocks: local (30,−60) maps into A9B3 (the loaded block south of the -A9B4 spawn) → snaps to `0xA9B3000E`, terrainZ=94, indoor=False (the -`[snap]` line). The player is now at Holtburg's south edge; streaming then -shifts the frame out from under them and they slide south into ocean -(the `[cell-transit] A9B3→A9B2→…` chain mirrors ACE's failed-transition -sequence exactly). - -**Fix shape (G.3):** on a far/different-landblock teleport, recenter + -HOLD the snap until the destination dungeon landblock/cell hydrates (reuse -the #107 `IsSpawnCellReady` spawn-ready gate, applied to the teleport- -arrival path instead of only login), then place into the indoor cell via -the validated-claim path (#107/#111 `SetPositionInternal` shape). Also -audit the streaming controller actually LOADS the far dungeon landblock on -recenter (the 5×5 Chebyshev window around the new center), and that the -old landblocks unload without stranding the player mid-frame-shift. - -**Files:** `GameWindow.cs:4877-4960` (teleport arrival), -`PhysicsEngine.Resolve` (the outdoor fallback), the #107 `IsSpawnCellReady` -gate, `StreamingController` recenter. - -**Acceptance:** teleport into the meeting-hall dungeon → the player stands -in the dungeon cell, the dungeon renders (3-5 rooms), walls block, no -ocean / no ACE `failed transition` spam. - -**Apparatus:** `ACDREAM_PROBE_CELL=1` ([cell-transit]) + `ACDREAM_PROBE_VIEWER=1` -([viewer]) + `ACDREAM_WB_DIAG=1` + the always-on `[snap]`/`live: teleport` -lines capture the whole chain (`launch-dungeon-diag.log`, this session). - ---- - ## #104 — Scene VFX particles not clipped to the PView visible cell set **Status:** OPEN @@ -2526,42 +2297,10 @@ pass, deliberately deferred out of the Phase W seal (which covers sky/terrain/wa **Acceptance:** A scene-particle emitter in a non-visible cell does not draw; outdoor particles (null `visibleCellIds`) unaffected; no regression on fireplace/spell VFX in the visible cell. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — `ParticleEmitter` still has no `OwnerCellId` field and `ParticleRenderer.BuildDrawList` still has no `visibleCellIds` parameter; unattached scene particles (campfires, portal swirls) are drawn unconditionally every frame with only a depth test for occlusion (`GameWindow.DrawUnattachedSceneParticles`), reproducing the described wall-bleed for that class of emitter. + --- -## #103 — Phase A8.F portal-frame indoor rendering broken at runtime (visual-gate failure) - -**Status:** SUPERSEDED 2026-05-30 by **Phase U (Unified Render Pipeline)**. The -two-pipe (inside/outside) approach this bug lives in is being abandoned wholesale — -the broken `RenderInsideOut` two-pipe path is deleted as Task 1 of Phase U and -replaced by a single unified retail `PView` portal-visibility pipeline. #103 will -not be fixed in place. See -[docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md](research/2026-05-30-unified-render-pipeline-decision-and-handoff.md). -**Severity:** MEDIUM (opt-in branch only — default game unaffected) -**Filed:** 2026-05-29 -**Component:** render (indoor visibility) - -**Description:** With `ACDREAM_A8_INDOOR_BRANCH=1`, the A8.F retail portal-frame port -renders indoor/outside-in broadly wrong: cottage/cellar interiors covered in outdoor -terrain with transparent walls; invisible walls in other houses from inside and outside. -Default game (env var off) is unaffected — `cameraInsideBuilding = a8IndoorBranchEnabled -&& inside` (GameWindow.cs:7343). The old cellar flap remains in the default path. - -**Root cause / status:** Two compounding causes (evidence in the handoff): (1) the -`OutsideView` builder under-produces — `OUTSIDEVIEW polys=0` most frames, and when -non-empty it doesn't recursively narrow (cellar shows ~full window). (2) The Task-6 -Job-A/B decoupling draws terrain UNGATED when `OutsideView` is empty (`else` branch), -flooding the cell interior over the (correctly-rendered) walls. Cell walls DO render -(`[opaque]` tris=50-108). Projection math is correct; the builder integration is fragile. - -**Files:** `src/AcDream.App/Rendering/PortalVisibilityBuilder.cs` (builder under-produces); -`src/AcDream.App/Rendering/GameWindow.cs` `RenderInsideOutAcdream` Step-4 `else` ungated-terrain (~11142). - -**Research:** [docs/research/2026-05-29-a8f-visual-gate-failure-handoff.md](research/2026-05-29-a8f-visual-gate-failure-handoff.md) (root-cause analysis, apparatus, first-fix hypothesis, pickup prompt). - -**Acceptance:** Holtburg cottage cellar renders with solid walls and no terrain flood; -terrain shows only through correctly-clipped portal openings; no invisible walls. -Related: #102 (builder dungeon-scaling fixpoint). - # Active issues --- @@ -2677,350 +2416,6 @@ currently blocked on #95 regardless). --- -## #87 — Drop WB fork patch by switching to PrepareEnvCellGeomMeshDataAsync - -**Status:** OPEN -**Severity:** MEDIUM (band-aid removal; not user-visible) -**Filed:** 2026-05-19 -**Component:** rendering, WB integration - -**Description:** Phase 2 (2026-05-19) shipped a one-line patch in our -WB fork at `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs:1230` -(branch `acdream` on the fork, SHA `34460c4`) to guard a blind -`TryGet(stab.Id, ...)` call against GfxObj-prefixed ids. That -patch fixes the symptom (missing floors) but is structurally a -band-aid — per CLAUDE.md's no-workarounds rule we should retire it. - -The proper fix: switch our EnvCell rendering from -`PrepareMeshDataAsync(envCellId, ...)` (general-purpose entry that -also iterates static-object parts + emitters we don't need) to WB's -narrower `PrepareEnvCellGeomMeshDataAsync(geomId, environmentId, cellStructure, surfaces)` -at [`ObjectMeshManager.cs:386`](../references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs:386). -That function only builds the cell room mesh (floor / walls / ceiling), -which is the only piece we actually use from WB for cells — we already -hydrate static objects as separate `WorldEntity` instances in -`BuildInteriorEntitiesForStreaming`, and we run particle scripts via -our own `EntityScriptActivator` (Phase C.1.5b). - -**Root cause / status:** Misuse of WB's general-purpose API for a -geometry-only need. The general-purpose path triggers static-object -iteration that has a bug (TryGet without type check) AND that -does work we throw away. Both problems disappear if we use the -geometry-only entry point WB already exposes for exactly this purpose -(it's what WB's own `EnvCellRenderManager` uses internally). - -**Trade-offs:** - -| | Current (patched WB) | Switch to geom-only API | -|---|---|---| -| WB fork divergence | One-line patch | Zero | -| Future WB upstream merges | Conflicts | Clean | -| Performance | Slightly worse (wasted iteration) | Slightly better | -| Risk to other functionality | None (working today) | Needs re-verification | - -**Files (the change):** - -- `src/AcDream.App/Rendering/GameWindow.cs` around line 5367-5378 - (cell-entity hydration — change `MeshRefs[0].GfxObjId` from `envCellId` - to `envCellId | 0x100000000UL`, the synthetic geom id with bit 32 set). -- `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs` — add a new method - `PrepareEnvCellGeomMesh(ulong geomId, uint environmentId, ushort cellStructure, List surfaces)` - that forwards to `_meshManager.PrepareEnvCellGeomMeshDataAsync(...)`, - and call it from the streaming path instead of the bare - `IncrementRefCount(envCellId)`. -- `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs:1230` - — revert the type-check guard we added. The function returns to - pristine WB state. - -**Acceptance:** - -- Floors still render in Holtburg Inn (regression check vs Phase 2). -- `references/WorldBuilder` submodule pointer returns to upstream-clean - (no acdream-specific commits in the fork's `acdream` branch — or - rather, the `acdream` branch fast-forwards back to match upstream's - state for this file). -- Probe re-capture at Holtburg confirms `[indoor-upload] completed` for - all cells previously failing. -- No `[wb-error]` lines. - -**Research:** [`docs/research/2026-05-19-indoor-cell-rendering-cause.md`](research/2026-05-19-indoor-cell-rendering-cause.md) -documents the underlying WB bug. - ---- - -## Indoor walking issue cluster (2026-05-19) - -The Phase 2 indoor cell rendering fix (floor now renders inside buildings) -surfaced nine pre-existing indoor bugs the user observed at Holtburg Inn -the moment they could walk indoors. None caused by the floor fix — all -existed before but were unobservable because there was no floor to stand -on. Filed individually below; #78 + #84 + #85 + #86 likely share a root -cause (cell BSP / portal-cull plumbing), and #79 + #80 + #81 + #82 share -the indoor-lighting plumbing. - ---- - -## #78 — Outdoor geometry (stabs + terrain mesh) visible inside EnvCells - -**Status:** OPEN — **PROMOTED 2026-06-02 to the full render-pipeline redesign** (this IS the -core interior-seal bug; root cause now PROVEN). See -[docs/research/2026-06-02-render-pipeline-redesign-handoff.md](research/2026-06-02-render-pipeline-redesign-handoff.md) -+ [the redesign plan](superpowers/plans/2026-06-02-render-pipeline-redesign-plan.md). Decisive evidence -(2026-06-02 [shell]/[vis] probes): the PVS + cell shells render correctly; the failure is the SEAL + -three inconsistent gates — concretely the `WbDrawDispatcher.cs:1756` `ParentCellId==null → return true` -bypass draws outdoor scenery indoors, and the indoor render draws the outdoor world then gates it -instead of running ONLY `DrawInside` (retail: visibility IS the cull). Fix = redesign Phase R1→R3. -**Severity:** HIGH (immediate visual jank; broadened scope per 2026-05-25 PM finding) -**Filed:** 2026-05-19 (broadened 2026-05-25; promoted to redesign 2026-06-02) -**Component:** rendering, visibility - -**Description:** Standing inside Holtburg Inn looking at the floor or -walls, the user sees other buildings in the distance at their correct -world position + scale — but visible THROUGH the floor and walls. As if -the cell mesh is rendered but doesn't occlude or stencil-cull what's -behind it. - -**Additional evidence (2026-05-25 PM, post-#100 visual verification):** -After issue #100 shipped (commits `f48c74a`, `a64e6f2`, `84e3b72`) and -removed the `hiddenTerrainCells` cell-collapse mechanism, the OUTDOOR -TERRAIN MESH is now (correctly per retail) rendered everywhere on the -landblock — including in 3D regions occupied by indoor EnvCell volumes. -Visual verification at a Holtburg cottage cellar showed a sharp-edged -rectangular grass patch (outdoor terrain at Z≈93.99) rendering over the -cellar stair geometry at certain camera angles. Clears when camera -moves closer (cottage walls + stair treads geometrically occlude the -terrain from new vantage points). Gameplay unaffected. **This is the -same root cause as the existing #78 hypothesis #2** ("outdoor stabs not -culled when player in EnvCell"), just with outdoor terrain mesh -affected in addition to outdoor stab entities. Per user direction, -NOT filed as a new issue — additional evidence reinforces #78's -hypothesis #2, broadens scope of the fix to include terrain culling. - -**Root cause / status:** Two plausible causes: -1. The `+0.02f` Z bump applied to cell origin at `GameWindow.cs:5362` - pushes the floor mesh 2 cm above terrain, so depth test correctly - occludes terrain. But OUTDOOR STABS (landblock-baked building geometry) - at the same X,Y may have Z values comparable to or higher than the - cell-mesh floor, producing z-fighting / see-through. -2. **(High confidence as of 2026-05-25)** Outdoor geometry (stabs AND - terrain mesh) isn't being culled when the player is inside an - EnvCell — this is the Phase 1 Task 3 deferred work - ("Cull outdoor stabs when indoors via VisibleCellIds"). WB has a - `RenderInsideOut` stencil pipeline (`references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs`) - that acdream never invokes. Retail anchor: - `docs/research/named-retail/acclient_2013_pseudo_c.txt:311397` - (`CEnvCell::find_visible_child_cell` at address `0x0052dc50`, - called from `acclient_2013_pseudo_c.txt:280028`). - -**Files:** -- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` (per-entity walk — - the dispatcher already filters by `entity.ParentCellId ∈ - visibleCellIds` but outdoor stabs have `ParentCellId == null` so they - always pass; needs an explicit indoor-camera gate). -- `src/AcDream.App/Rendering/TerrainModernRenderer.cs` (currently - renders all loaded landblock terrain unconditionally; needs - visibility gating when camera resolves to an indoor cell). -- `src/AcDream.App/Rendering/CellVisibility.cs:222+` (`ComputeVisibility` - returns `VisibleCellIds`; existing portal-LOS infrastructure to build on). -- `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs` - (`RenderInsideOut` pipeline — reference implementation, never invoked). - -**Acceptance:** Standing inside a sealed-interior cell, no outdoor -geometry is visible through floor/walls. Standing where a cell has a -real outdoor portal (door open, window) outdoor geometry is correctly -visible through the portal. Cellar-stairs case (2026-05-25 finding): -standing in a Holtburg cottage cellar at any camera angle, no outdoor -terrain mesh visible over the stair geometry. - -**Research:** -[`docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md`](research/2026-05-25-issue-100-shipped-and-culling-handoff.md) -— full session handoff with cellar-stairs evidence, family map (#78 + -#95 + cellar-stairs), root-cause hypothesis, retail anchors, WB -references, do-not-retry list, and pickup prompt for the -investigation session. - -**2026-05-31 update (post-U.4c-flap-fix):** the U.4c flap fix (`0ee328a`, root -indoor visibility at the player's cell) made this MORE visible — terrain now -draws inside again (it was Skipped during the flap), so the "floor shows outdoor -ground / cellar floor transparent / see the world from below" symptom is now -prominent. Confirmed at visual gate. Fix direction unchanged: gate outdoor -terrain by indoor-cell visibility (port retail `CEnvCell::find_visible_child_cell` -`acclient_2013_pseudo_c.txt:311397` + `seen_outside` landscape-keep). See -[`docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md`](research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md) -(residual 1). - -**2026-05-31 (PM) — promoted to the RENDER ARCHITECTURE RESET target.** A week of -point-fixing produced no shippable indoor render. #78 is now understood as the visible -symptom of an architectural gap, NOT a standalone bug: acdream enforces visibility via -THREE inconsistent gates (terrain `TerrainClipMode` / shell per-cell clip / entity -`ParentCellId` filter with a `ParentCellId==null` outdoor-stab bypass) instead of retail's -ONE PView gate. Direct evidence (`[shell]` probe, `ACDREAM_PROBE_SHELL`) RULED OUT every -other subsystem: the interior cell shells render fine (geometry/texture/opaque/depth -correct); the residual is purely that outdoor geometry isn't gated to portal openings -when indoors. The fix is the unified PView gate (one traversal → one gate for ALL -geometry), which closes #78 + transparent walls + grey enclosure together. **Canonical -(read first):** -[`docs/research/2026-05-31-render-architecture-reset-handoff.md`](research/2026-05-31-render-architecture-reset-handoff.md) -+ the "Render Pipeline" section of `docs/architecture/acdream-architecture.md`. - ---- - -## #79 — Indoor lighting: spurious spot lights on walls - -**Status:** OPEN -**Severity:** MEDIUM -**Filed:** 2026-05-19 -**Component:** lighting - -**Description:** Walking around inside Holtburg Inn, the user sometimes -sees spot-light-like patches on the interior walls that don't correspond -to retail's lighting. - -**Root cause / status:** Point lights from cell static objects (torch -entities) are being registered via `LightInfoLoader.Load` + `LightingHookSink` -(Phase 1 verified). Their per-light parameters (position, range, intensity, -cone) may be wrong — wrong falloff treatment, wrong world-space transform, -or wrong direction for spot lights. Spec at -`docs/research/deepdives/r13-dynamic-lighting.md` documents the retail -LightInfo→LightSource mapping but the live behavior hasn't been verified -against retail. - -**Files:** -- `src/AcDream.Core/Lighting/LightInfoLoader.cs` -- `src/AcDream.App/Rendering/Shaders/mesh_modern.frag` — `accumulateLights` - spot-cone logic. - -**Acceptance:** Side-by-side comparison with retail at the inn shows -matching torch-light pools. - ---- - -## #81 — Static building stabs don't react to atmospheric lighting changes - -**Status:** OPEN — **M1.5 scope (A7 lighting fidelity)** -**Severity:** MEDIUM -**Filed:** 2026-05-19 -**Component:** lighting, rendering - -**Description:** Outside, time-of-day changes (sunrise/sunset/lightning) -don't visibly affect static building stabs (the inn / cottages). The -buildings stay statically lit while terrain and scenery shift colors. - -**Root cause / status:** Stabs are rendered through `WbDrawDispatcher` -with `mesh_modern.frag` which DOES consume the `SceneLightingUbo` -(sun + ambient + fog). Verify the shader is being used for stabs and -that the UBO is bound at the right binding slot per draw call. -Possibly a shader-path divergence — terrain uses `terrain_modern.frag`, -entities use `mesh_modern.frag`, but stabs/scenery may be on a -different path. - -**Files:** -- `src/AcDream.App/Rendering/Shaders/mesh_modern.frag` -- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` - -**Acceptance:** Stabs darken/brighten in sync with terrain + scenery -across the day/night cycle. - ---- - -## #82 — Some slope terrain lit incorrectly - -**Status:** OPEN -**Severity:** LOW (cosmetic) -**Filed:** 2026-05-19 -**Component:** rendering, terrain - -**Description:** Specific terrain slopes appear lit "wrong" compared to -retail. - -**Root cause / status:** Likely terrain normal calculation or the -landblock-edge normal-blending divergence between WB and retail (per -`feedback_wb_migration_formulas.md` — WB's terrain split formula -differs from retail's `FSplitNESW`). - -**Files:** -- `src/AcDream.App/Rendering/TerrainModernRenderer.cs` -- `src/AcDream.App/Rendering/Shaders/terrain_modern.frag` - -**Acceptance:** Side-by-side comparison with retail at the same Holtburg -slopes shows matching shading. - ---- - -## #83 — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck) - -**Status:** OPEN — **M1.5 scope (A6 physics fidelity, primary umbrella issue)**. Foundation work landed 2026-05-19; root-cause fix scoped to A6.P1-P3 cdb-driven investigation. -**Severity:** HIGH (blocks vertical indoor traversal + degrades single-floor cases). M1.5 acceptance depends on this closing. -**Filed:** 2026-05-19 -**Component:** physics, movement, resolver - -**Description:** Walking UP stairs in single-floor houses works -(grounded step-up routes through retail-faithful `BSPQuery.FindWalkableInternal` -via `StepSphereDown`). Walking DOWN into cellars fails ("ground blocking" — -can't descend). Walking on 2nd floors works partially but intermittently -gets stuck in the falling animation. "Phantom collisions" / invisible -obstacles in rooms persist. The original title "Walking up stairs broken" -was misleading per user's clarification 2026-05-19. - -**Partial fix landed 2026-05-19 (6 commits `ff548b9` → `f845b22`).** -Foundation work: extended `BSPQuery.FindWalkableInternal` to expose the -hit polygon's dictionary key id; added thin public wrapper -`BSPQuery.FindWalkableSphere` over the existing retail-faithful BSP -walkable-finder (acclient_2013_pseudo_c.txt:326211 / :326793); refactored -`Transition.TryFindIndoorWalkablePlane` to route through that wrapper -instead of its Phase-2 linear first-match XY scan; added `[indoor-walkable]` -runtime-toggleable probe line for diagnostic visibility. 5 new unit tests -+ 1 integration test, 9 pre-existing IndoorWalkablePlane tests updated -to the new signature. - -**Foundation work did NOT fix the user-reported bugs.** Visual verification -2026-05-19: cellar descent FAIL, 2nd-floor walking FAIL (intermittent -falling-stuck), single-floor cottage REGRESSED to intermittent falling-stuck -(was stable before), phantom collisions PERSIST. The probe captured 1443 -MISS / 2 HIT over 1445 indoor-walkable calls — the BSP walker correctly -rejects the foot-sphere-tangent-to-floor case (sphere center is exactly -at `floorZ + radius` when grounded, so `PolygonHitsSpherePrecise` fails -the `|dist| > radius - epsilon` check by ~0.0002). - -**Root cause (deeper than originally diagnosed):** `Transition.TryFindIndoorWalkablePlane` -fundamentally exists as a Phase 2 commit `eb0f772` stop-gap to synthesize -a ContactPlane every frame when the indoor BSP returns OK. Retail doesn't -do this — retail RETAINS the previous frame's `ContactPlane` when the -collision dispatcher says "no collision." There is no retail analog of -`find_walkable` being called as a standing-still query — retail's -`find_walkable` only runs inside a downward sphere sweep -(`step_sphere_down`), where the sphere is moving and the overlap test -is meaningful. In our `TryFindIndoorWalkablePlane` flow, the sphere is -tangent (grounded), not moving — the algorithm correctly returns "no -overlap." The single-floor cottage worked previously because the OLD -linear scan ignored Z and falsely returned HIT for any XY-overlapping -walkable; the new BSP-walker correctly identifies "no overlap" and -falls through to the outdoor terrain backstop, which only happens to -produce sensible Z for single-floor outdoor-adjacent cases. - -**Files in the foundation work:** -- `src/AcDream.Core/Physics/BSPQuery.cs` — `FindWalkableInternal` signature extension, new `FindWalkableSphere` public wrapper -- `src/AcDream.Core/Physics/TransitionTypes.cs` — `TryFindIndoorWalkablePlane` refactor, `PointInPolygonXY` deletion, `[indoor-walkable]` probe -- `tests/AcDream.Core.Tests/Physics/BSPQueryTests.cs` — 4 new unit tests -- `tests/AcDream.Core.Tests/Physics/TransitionTypesTests.cs` — new integration test -- `tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs` — 9 tests updated to new signature - -**Next investigation phase (deferred):** Port retail's `ContactPlane` retention -mechanism so the resolver retains the previous frame's contact plane when -the BSP says "no collision," instead of re-synthesizing it per frame. The -proper fix likely eliminates `TryFindIndoorWalkablePlane` entirely. Needs -deep investigation of retail's `CTransition::transitional_insert` / -`CPhysicsObj::transition` / `LastKnownContactPlane` interactions. Foundation -work (BSP walker + probe + tests) remains useful regardless of approach. - -**Acceptance:** Walk down stairs into a cellar without getting stuck. -Walk on a 2nd floor without intermittent falling-stuck. Single-floor -cottage walking remains stable (no regression). - -**Handoff:** [`docs/research/2026-05-19-indoor-walkable-plane-bsp-port-shipped-handoff.md`](research/2026-05-19-indoor-walkable-plane-bsp-port-shipped-handoff.md). - ---- - ## #84 — [DONE 2026-05-19] Blocked by air indoors **Status:** DONE @@ -3135,45 +2530,6 @@ propagates through portal connectivity data in `CEnvCell`. --- -## #88 — Indoor static objects vibrate (bookshelves, open furnaces) - -**Status:** OPEN — **M1.5 scope (A6 physics — suspected sub-step state corruption family)** -**Severity:** MEDIUM (visual jitter; doesn't block gameplay) -**Filed:** 2026-05-19 -**Component:** rendering, animation - -**Description:** Static objects inside cells (bookshelves, open furnaces, possibly other interior props) show per-frame transform jitter / vibration. Pre-existing (user noticed before Phase 2 shipped). Likely candidates: - -1. `EntityScriptActivator.OnCreate/OnRemove` firing repeatedly as the player's CellId promotes/demotes near cell boundaries (less likely after Phase 2's portal-based tracking — but worth investigating). -2. Per-part transforms for cell-static `WorldEntity` instances getting recomputed each frame with floating-point drift. -3. Particle-emitter offsets accumulating instead of resetting. - -**Files to investigate:** -- `src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs` — OnCreate/OnRemove call patterns -- `src/AcDream.App/Rendering/GpuWorldState.cs` — entity transform updates per frame -- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — per-batch transform composition - -**Acceptance:** Indoor static objects render stable (no per-frame jitter). - ---- - -## #89 — Port BSPQuery.SphereIntersectsCellBsp for retail-faithful CheckBuildingTransit - -**Status:** OPEN -**Severity:** LOW (Phase 2 ships with a documented approximation) -**Filed:** 2026-05-19 -**Component:** physics - -**Description:** Retail's `CEnvCell::check_building_transit` uses `CCellStruct::sphere_intersects_cell` — a radius-aware sphere-vs-BSP test that returns Inside/Crossing/Outside. Phase 2's `CellTransit.CheckBuildingTransit` uses `BSPQuery.PointInsideCellBsp` (radius-less, tests only the sphere CENTER). Practical effect: outdoor→indoor entry fires ~sphereRadius (~0.48m) deeper into the doorway than retail. The sphereRadius parameter is plumbed through but currently unused. - -**Files:** -- `src/AcDream.Core/Physics/CellTransit.cs::CheckBuildingTransit` (line ~162) -- `src/AcDream.Core/Physics/BSPQuery.cs::PointInsideCellBsp` (line ~940) — existing point test to model the new sphere variant after - -**Acceptance:** `CellTransit.CheckBuildingTransit` calls a new `BSPQuery.SphereIntersectsCellBsp(node, sphereCenter, sphereRadius)` that returns `Inside`/`Crossing`/`Outside`. Entry timing matches retail visually at the Holtburg cottage door. - ---- - ## #90 — Cell-id ping-pong at indoor doorway threshold — [DONE 2026-06-11 · ca4b482, T6/BR-7] **Status:** DONE — the `4ca3596` sphere-overlap stickiness workaround was @@ -3270,36 +2626,6 @@ errors. The flood was never the bug. **Originally** also: explained user-observe --- -## #96 — Per-tick PhysicsEngine.ResolveWithTransition CP seed (retail divergence) - -**Status:** PARTIALLY ADDRESSED — accepted as documented retail divergence -**Severity:** LOW (cosmetic — CP-write counter inflates but behavior is correct) -**Filed:** 2026-05-21 -**Component:** physics, ContactPlane retention - -**Description:** After A6.P3 slice 1 (commits `5aba071` + `5f7722a` + `39fc037`) stripped the `TryFindIndoorWalkablePlane` synthesis path from `Transition.FindEnvCollisions` indoor branch, scen3 post-fix re-capture showed acdream still writes ContactPlane fields 25,082 times during a flat-floor walk — 24,906 of those (99.3%) come from `PhysicsEngine.ResolveWithTransition` line 622, which seeds `ci.ContactPlane` from `body.ContactPlane` at every transition start when the body is grounded. Retail's equivalent code path fires `set_contact_plane` zero times during the same flat-floor walk (scen3 retail BP7 = 0). - -**Slice 2 attempt + outcome (2026-05-22, commits `892019b` + `f8d669b`):** - -- **v1 attempt (`892019b`):** Removed the L622 seed entirely to match retail's `CTransition::init` clear-at-start behavior. Verified per-rebuild that the change deployed. CP-write count dropped 91% (30,420 → 2,690). **But broke BSP step_up at the last step of stairs** — sub-step 1's `AdjustOffset` had no ContactPlane to compute the lift direction, BSP step_up thrashed (12,489 push-back-disp + 2,226 push-back-cell signal). User confirmed: "I can't pass the last step of the stairs." -- **v2 fix (`f8d669b`):** Reverted the seed removal + added no-op-if-unchanged guard inside `CollisionInfo.SetContactPlane`. The guard early-returns when called with values identical to current state. **The guard doesn't trigger for the L622 seed** because each tick gets a fresh `Transition` (so `ci.ContactPlaneValid=false` on entry → guard fails → write fires). So slice 2 v2 didn't actually reduce CP-write count for the seed case. It does dedupe within-tick redundant writes (e.g. Mechanism B restoring LKCP that equals current ci.CP), which is a small benign improvement. - -**Root cause / status (updated 2026-05-22):** The L622 seed IS load-bearing for `AdjustOffset` slope projection on sub-step 1, which BSP step_up depends on. Retail uses a different architecture (no seed; first sub-step has no CP and BSP path-6 establishes it). Matching retail would require a deeper refactor — making `AdjustOffset` fall back to `body.ContactPlane` when `ci.ContactPlane` is invalid, OR re-architecting the sub-step loop to not require CP for the first iteration. Both are non-trivial. - -**Accepting the divergence:** the per-tick seed call is functionally correct — it propagates the player's current contact plane to the transition. The cost is a noisy CP-write counter (cosmetic) but the BEHAVIOR matches retail (player stays grounded on the correct plane, slope-snap works, step_up works). Closing #96 fully is deferred to a future refactor or accepted as is. - -**Lessons learned:** -- A counter-based metric (CP-write count) is not always a direct proxy for "behavior matches retail." Retail's set_contact_plane firing rate differs from ours because the call-site structure differs, not because the behavior differs. -- The slice 1 hypothesis "Finding 1 (dispatcher entry frequency) may close as side-effect of Finding 2 (CP-write)" was confirmed by stairs+cellar working post-slice-1. But the slice 2 follow-up assumption "remaining 99.3% of CP writes are also a problem" was partially wrong — those writes are correct state propagation. - -**Files:** -- `src/AcDream.Core/Physics/PhysicsEngine.cs:620-626` (the seed call site, retained with updated comment) -- `src/AcDream.Core/Physics/TransitionTypes.cs:259-279` (`CollisionInfo.SetContactPlane` no-op guard, retained as small improvement) - -**If revisited:** investigate `AdjustOffset` fallback to `body.ContactPlane` when `ci.ContactPlane` is invalid — that would let us safely remove the seed. Or investigate retail's exact first-sub-step behavior to see if there's a different missing piece in our BSP step_up that would let it work without a seeded CP. - ---- - ## #97 — Phantom collisions + occasional fall-through on indoor 2nd floor (post-slice-1 happy-testing) — [DONE 2026-06-11 · T6/BR-7 + T5 gate] **Status:** DONE — closed by T6/BR-7 (the +5 m radial query pad that made @@ -3840,6 +3166,8 @@ fix the constants in **Estimated scope:** ~30 min cdb session + 1 commit if confirmed, or +small fix if different. Not blocking M1. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — no cdb trace or dat dump of the Humanoid `TurnRight`/`TurnLeft` `omega.z` was ever captured; the retail-divergence-register still carries this as a live open risk under rows AP-75/AP-76 as of the current codebase. + --- ## #71 — WorldPicker Stage B — polygon refine for retail-accurate clicks @@ -3956,37 +3284,6 @@ human motion table. --- -## #68 — Remote players don't stop running animation on auto-walk arrival - -**Status:** OPEN -**Severity:** LOW-MEDIUM (visual only — server-side action completes correctly) -**Filed:** 2026-05-15 (B.7 visual verification) -**Component:** motion / remote dead-reckoning / animation cycle - -**Description:** Observing a retail player from acdream as they approach -an NPC at a distance: the remote body's run animation keeps cycling -even after the body has visibly stopped at the NPC. Retail-side the -character stopped; the action (dialogue) fired; but our client's -animation never transitioned RunForward → Ready. - -**Suspected:** `RemoteMoveToDriver` detects arrival via -`DriveResult.Arrived`, but the consumer site (per-tick loop in -`GameWindow.TickAnimations` or wherever the remote body's cycle is -driven) doesn't flip the animation cycle back to Ready on arrival. -Alternatively the cycle persists because ACE doesn't broadcast a -follow-up `UpdateMotion(Ready)` — relying on the client to detect -arrival from the wire's distance threshold instead. - -**Files (likely):** -- `src/AcDream.App/Rendering/GameWindow.cs` — wherever per-tick motion - for remote entities reads `RemoteMoveToDriver`'s state. Need to - call `SetCycle(NonCombat, Ready)` on arrival. - -**Acceptance:** Retail player observed running up to an NPC visibly -stops running animation at arrival distance, transitions to idle. - ---- - ## #67 — [DONE 2026-05-15 · `301281d`] Door Use action doesn't complete after auto-walk arrival **Status:** DONE — fixed by `301281d` (10 Hz heartbeat during motion). @@ -3997,97 +3294,6 @@ doors work after the heartbeat bump. --- -## #66 — Local + remote rotation: player flips back, NPCs don't turn - -**Status:** OPEN -**Severity:** LOW-MEDIUM (visual feedback — interaction works, -just looks wrong) -**Filed:** 2026-05-15 (B.7 visual verification) -**Component:** motion / rotation - -**Description:** Two related visual rotation bugs surfaced together: - -1. **Local player flips back.** Observing acdream's `+Acdream` from - retail: when our auto-walk completes and the body has rotated to - face the target, the broadcast position has the new rotation — - then the next frame the player snaps back to whatever the camera - yaw was. Likely cause: after `EndServerAutoWalk`, the synthesised - input stops and `Update`'s next pass applies the user's real - `MouseDeltaX` (which may be 0 but other paths might be - overriding `Yaw`). -2. **NPCs don't turn to face the player.** ACE broadcasts - `MovementType=8 TurnToObject` when an NPC starts a Use response - that requires facing. Our `OnLiveMotionUpdated` handles - MovementType=6 (MoveToObject) but not 8. The NPC's body stays - at whatever heading the spawn / last motion left it. - -**Acceptance:** -- After auto-walk arrival, local player's facing toward the target - is preserved (no flip-back observed from a retail client). -- NPCs (Tirenia, guards, vendors) rotate to face the player when - using them. - -**Files (likely):** -- `src/AcDream.Core.Net/Messages/UpdateMotion.cs` — extend parser - for MovementType=8 payload (target guid + final-heading flag). -- `src/AcDream.App/Rendering/GameWindow.cs` `OnLiveMotionUpdated` - — route MovementType=8 for the local player to a new - `BeginServerTurnToObject` controller method; route for remote - guids into the remote-dead-reckon state (extending - `RemoteMoveToDriver` or adding a sibling driver). -- `src/AcDream.App/Input/PlayerMovementController.cs` — add the - turn driver that holds Yaw against user-input overrides until - aligned. - -**Replaces / supersedes:** #65 (local-player turn-to-face on -close-range Use). This issue covers both directions and is the -broader retail-faithful rotation handling phase. - -**Estimated scope:** Medium — ~80–120 LOC + tests. - ---- - -## #65 — Local player doesn't turn to face target on close-range Use - -**Status:** OPEN -**Severity:** LOW (functional — Use still completes — but visually awkward) -**Filed:** 2026-05-15 (B.6/B.7 visual verification) -**Component:** physics / movement / inbound MoveTo handling - -**Description:** When the local player has a target selected and is -already within ACE's `WithinUseRadius` (close-range branch in -`CreateMoveToChain` at `Player_Move.cs:66`), ACE skips the auto-walk -chain and just calls `Rotate(target)` server-side. The Use action -completes, but the local player's body doesn't visibly turn to face -the target — the character stays at whatever heading the user was -looking when they clicked. - -**User-visible:** Stand behind an NPC, click them, press R. Dialogue -appears, but the character keeps facing away from the NPC. In retail -the character would have turned to face the NPC before / during the -Use. - -**Root cause:** ACE's close-range path sends a `TurnTo` motion -(MovementType=8 TurnToObject, decomp `0x005241b3` switch case 8). -Our `OnLiveMotionUpdated` doesn't currently handle MovementType=8 — -it falls into the locomotion path and ignores the rotation. - -**Acceptance:** When the user uses an in-range target while facing -away, the character rotates to face the target before / as the Use -action fires. No regression on close-range pickup (item still picks -up cleanly). - -**Files (likely):** -- `src/AcDream.Core.Net/Messages/UpdateMotion.cs` — extend parser for MovementType=8 TurnToObject payload. -- `src/AcDream.App/Input/PlayerMovementController.cs` — add a `BeginServerTurnToObject(targetWorld, useFinalHeading)` method that rotates Yaw at TurnRateRadPerSec each frame until aligned, then clears the state. -- `src/AcDream.App/Rendering/GameWindow.cs` `OnLiveMotionUpdated` — when inbound motion is MovementType=8 and the guid is `_playerServerGuid`, install the turn on the controller. - -**Estimated scope:** Small — ~50 LOC plus tests. Pairs naturally with -B.6 (already does turn-then-walk for far targets via RemoteMoveToDriver's -heading correction; this is the close-range cousin). - ---- - ## #64 — Local-player pickup animation does not render **Status:** OPEN @@ -4258,58 +3464,6 @@ inn door, watch swing animation rest at open pose with no intermediate flash. --- -## #60 — `obstruction_ethereal` retail downstream path not ported (M2 combat-HUD impact) - -**Status:** OPEN -**Severity:** LOW for M1 (no observable defect); MEDIUM for M2 (combat contact reporting on ethereal creatures will be wrong) -**Filed:** 2026-05-13 (final-review surfaced from B.4b) -**Component:** physics / `CollisionExemption.ShouldSkip` + downstream movement contact handling - -**Description:** B.4b's L.2g slice 1b widened `CollisionExemption.ShouldSkip` to exempt -on `ETHEREAL_PS` alone (cite `src/AcDream.Core/Physics/CollisionExemption.cs:62-79`). Retail's -`acclient_2013_pseudo_c.txt:276782` requires both `ETHEREAL_PS && IGNORE_COLLISIONS_PS` to wrap -the entire `FindObjCollisions` body — ETHEREAL alone takes the deeper path at line 276795 which -sets `sphere_path.obstruction_ethereal = 1` and lets downstream movement allow passage WHILE -STILL REPORTING THE CONTACT. We do not port that downstream path; we just exempt entirely. - -**M2 impact:** Combat HUD work that relies on physics-contact reporting for ethereal creatures -(ghosts, partially-phased monsters, spell projectiles with ETHEREAL set) will see no contact at -all instead of "soft contact with obstruction_ethereal=1". The user will not be able to target -or interact with such entities via the contact path. - -**Acceptance:** Port the retail deeper path so `obstruction_ethereal=1` flows through movement + -collision-reporting layers. Tests should cover: ETHEREAL creature target → contact reported but -passage allowed; ETHEREAL+IGNORE_COLLISIONS target (door, retail-style) → full exempt. - -**Estimated scope:** Moderate. Touches `CollisionExemption.cs`, transition/movement layer, and -sphere-path state propagation. Visible test through a spawned ethereal creature in ACE. - ---- - -## #59 — [DONE 2026-05-15 · `5e29773`] `WorldPicker` 5m fixed-radius could over-pick at tight thresholds (M1-deferred polish) - -**Status:** OPEN -**Severity:** LOW (cosmetic — picker grabs the right entity in Holtburg-tested scenarios) -**Filed:** 2026-05-13 (final-review surfaced from B.4b) -**Component:** selection / `AcDream.Core.Selection.WorldPicker.Pick` - -**Description:** `WorldPicker.Pick` uses a hardcoded 5m sphere around every candidate's -`Position` regardless of the entity's actual size (`src/AcDream.Core/Selection/WorldPicker.cs:82`). -This matches `WorldEntity.DefaultAabbRadius` and is sufficient for M1 acceptance: in tight -doorways, every server-keyed candidate has correct sphere coverage and the closest-wins logic -plus `ServerGuid==0` skip filter the wrong picks. But the invariant "non-clickable geometry has -`ServerGuid==0`" is load-bearing — if L.2d ever ports `CBuildingObj` as a server-keyed entity, -the picker may mis-target buildings. Per-entity `Setup.Radius` would be tighter. - -**Acceptance:** Either (a) tighten picker to read per-entity Setup.Radius / CylSphere bounds, -or (b) document the invariant in `WorldPicker.cs` and add a regression test asserting -`ServerGuid==0` entities never reach the per-candidate hit test. - -**Estimated scope:** Quick (~1 hour) — wire `Setup.Radius` lookup into the picker and update -the 6 existing picker tests with realistic radii. - ---- - ## #58 — [DONE 2026-05-13] Door swing animation: UpdateMotion not wired for non-creature entities **Status:** DONE @@ -5108,6 +4262,8 @@ magnitude. - Side-by-side acdream-as-observer vs retail-as-observer of the same server-controlled toon: indistinguishable body trajectory. +**2026-07-09 note (user):** "semi fixed, needs polish" — stays open, downgrade to polish-tier if a fresher look confirms the core bug is gone. + --- ## #40 — [DONE 2026-05-05 · 40d88b9] ACDREAM_INTERP_MANAGER=1 env-var path regressed (staircase + blips) @@ -5750,35 +4906,6 @@ additional flash-shader work). --- -## #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. - ---- - - ## #2 — Lightning visual mismatch (sky PES path disproved) **Status:** OPEN @@ -5831,29 +4958,6 @@ one live creature case no longer use the single-cylinder fallback. --- ---- - -## #4 — Sky horizon-glow disabled (fog-mix skipped on sky meshes) - -**Status:** OPEN -**Severity:** LOW (aesthetic feature-parity, not regression from pre-session state) -**Filed:** 2026-04-25 -**Component:** sky - -**Description:** Phase 8.1 (commit `593b76f`) disabled the fog-mix on sky meshes to fix the "entire dome swallowed by fog color" regression. Dereth's keyframe `FogEnd` values (0–2400 m) are calibrated for terrain; sky meshes are authored at radii 1050–14271 m so every sky pixel was past `FogEnd`, saturated to `uFogColor`, destroying stars / moon / dome texture. Disabling the mix restored visibility but we lost retail's horizon-glow effect (gradient from clear zenith to fog-tinted horizon band at dusk/dawn). - -**Root cause / status:** Three competing hypotheses, none pinned down: (a) retail uses a **different** fog range for sky than terrain; (b) retail applies fog with an **elevation-angle** weighting rather than linear distance; (c) retail's sky meshes **don't participate** in the global fog and the "horizon glow" comes from a different atmospheric-scatter path. Need to identify retail's actual sky-fog behaviour before re-enabling with correct parameters. - -**Not in the Phase C.1.5c (Sky-PES) cluster.** Unlike #2/#28/#29 — all PES-driven sky visuals consolidated under the C.1.5c phase via former issue #36 — this is a fragment-shader fog-mix problem. Addressing C.1.5c will NOT resolve #4, and #4 should NOT be bundled into Phase C.1.5c scope. The fix likely needs its own decomp dive into retail's sky-fog math + shader work. - -**Files:** -- `src/AcDream.App/Rendering/Shaders/sky.frag` — line ~55, `rgb = mix(uFogColor.rgb, rgb, vFogFactor)` currently commented out -- `src/AcDream.App/Rendering/Shaders/sky.vert` — lines 109-114, `vFogFactor` computation - -**Research:** `docs/research/2026-04-23-sky-fog.md`. Partial; doesn't pin the sky-specific fog path. - -**Acceptance:** At dusk in Holtburg, the sky dome shows a clear zenith and a warm fog-tinted horizon band that matches retail's appearance, with stars / moon / sun / clouds all still visible at their correct brightnesses elsewhere in the frame. - --- ## #28 — Aurora ("northern lights") effect not rendered @@ -5940,6 +5044,8 @@ If hypothesis (a) is correct, this issue effectively rolls into **#28** — the **See #36 (filed 2026-04-30)** — confirmed via live cdb trace: retail's cloud density comes from the same PES-driven particle-emitter chain as aurora. Implementation consolidated there. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — the `#36` consolidation that was supposed to auto-close this via Phase C.1.5c never shipped (still PLANNED per the roadmap) and the sky-PES path is gated off by default (`ACDREAM_ENABLE_SKY_PES`, off), so the underlying cloud-density fix never landed on main. + --- ## #47 — [DONE 2026-05-06 · 0bd9b96] Humanoid Setup 0x02000001 renders bulky / lacks shape detail vs retail @@ -6070,50 +5176,6 @@ retail show matching silhouette and shape definition. --- -## #46 — Retail observer of acdream sees blippy / laggy movement - -**Status:** OPEN -**Severity:** MEDIUM (degrades external perception of acdream-driven characters) -**Filed:** 2026-05-06 -**Component:** net / motion (acdream's outbound path: `PlayerMovementController` → `MoveToState` (0xF61C) / `AutonomousPosition` heartbeat → ACE → retail observer) -**Phase:** L.2 (Movement & Collision Conformance) — outbound-motion fidelity sub-piece. Counterpart to #41 (which is the inbound side); both are L.2 conformance work. If outbound fidelity grows into multi-commit work, consider carving "L.2e — Outbound motion fidelity" as a named sub-piece on the roadmap. - -**Description:** When viewing acdream's local +Acdream character through a parallel retail acclient.exe, the retail observer sees the character's movement as visibly blippy and laggy — position appears to step in discrete jumps rather than translating smoothly. The local acdream view of the same character looks fine, and acdream observing a retail-driven character (after #39 / #45) also looks fine. The degradation is specifically on the **outbound** side: what acdream sends to ACE for relay to other clients. - -**Root cause / status:** - -Unverified. The likely culprits, ranked by suspected probability: - -1. **AutonomousPosition heartbeat cadence.** `memory/project_retail_motion_outbound.md` notes acdream's fixed 200 ms heartbeat is a probable retail mismatch. Retail's `CommandInterpreter::SendPositionEvent` gates on transient_state (Contact + OnWalkable + valid Position) and may broadcast at a different cadence — fewer / more / variable. If acdream sends too rarely, observer dead-reckons too long between updates and visibly stutters when each AutoPos arrives. -2. **MoveToState send conditions.** `PlayerMovementController.cs:813-840` decides when a fresh MoveToState fires (state-change detection). If important transitions are missed (e.g., direction changes that don't flip ForwardCommand/SidestepCommand), the observer's last-known motion stays stale and AutoPos updates blip the body to the new authoritative position. -3. **InstanceSequence / ObjectMovement sequence counters.** ACE rejects out-of-order packets. If acdream's sequence stamping is off, ACE silently drops some packets; observer dead-reckons through the gap. -4. **Velocity field absent on AutoPos.** ACE relays UPs without HasVelocity for player characters (per `OnLivePositionUpdated` comment). Observer's dead-reckoning between UPs may extrapolate using stale velocity, producing visible position drift that snaps back on the next UP — exactly the blippy pattern. - -**Verification approach:** - -- Run two retail clients + one acdream client. Drive acdream; observe acdream's character on retail #1 and on retail #2 (both retail observers see the same wire). Compare to a retail-driven character observed from the same retail clients — does it look smooth there? If yes, the issue is acdream-outbound-specific. If both look blippy, it's something on the ACE side (less likely). -- cdb-attach a retail observer client and breakpoint `MovementManager::unpack_movement` to count UPs and UMs received per second from the acdream-driven character vs from another retail character. The cadence delta will identify which packet stream is misbehaving. -- Compare acdream's outbound packet timing against holtburger's `client/movement/system.rs` heartbeat logic — that's the closest known-working reference for how a non-retail client should pace its outbound. - -**Files:** - -- `src/AcDream.App/Input/PlayerMovementController.cs` — outbound state-change detection + heartbeat -- `src/AcDream.Core.Net/WorldSession.cs` — sequence counters + send path -- `src/AcDream.Core.Net/Net/Outbound/...MoveToState.cs` / `AutonomousPosition.cs` — wire builders -- `references/holtburger/crates/holtburger-core/src/client/movement/system.rs` — reference cadence - -**Acceptance:** - -- Side-by-side comparison: retail observer of acdream-driven character and retail observer of retail-driven character look equally smooth during running, walking, sidestepping, turning, and stopping. -- No visible "step" pattern when acdream-driven character translates between AutoPos updates. - -**Cross-reference:** - -- `memory/project_retail_motion_outbound.md` — 2026-05-01 cdb live trace of retail's outbound (`CommandInterpreter::SendMovementEvent` for WASD, `Event_Jump` per-frame while charging). -- CLAUDE.md "Outbound motion wire format" — the `WalkForward + HoldKey.Run` ↔ `RunForward` auto-upgrade ACE applies on broadcast. - ---- - ## #45 — [DONE 2026-05-06 · e9e080d] Local +Acdream sidestep walking renders too slow **Status:** DONE @@ -6477,30 +5539,7 @@ squared-vs-not threshold (prefer LIVE attach — prior lesson: static door push to confirm whether the `cn=(0,0,1)` comes from our `UnitZ`-default (lead 1) and what retail's normal is at that instant. ---- - -## #117 — Aperture-shaped see-through: doors/interiors visible through terrain hills and through nearer buildings — [DONE 2026-06-11 · 478c549, user re-gate "Yes solved"] - -**Status:** OPEN -**Severity:** HIGH (the most visible remaining render artifact post-port) -**Filed:** 2026-06-11 (T5 comprehensive gate, user items 11a+11b) -**Component:** render — aperture depth discipline (the T1 punch pass) - -**Symptom (user, axioms):** (a) "looking downhill I can see certain parts -of houses (not everything) like doors and some textures" through the -terrain; (b) "when I'm behind a house I can see the openings on the house -behind that house through the house in front of me, like doors and -windows." Both shapes are APERTURE-shaped (doors/windows), which points at -one mechanism: the far-Z punch erases the depth of NEARER occluders -(terrain hills, closer buildings) at the punched aperture pixels, so -interior content + door entities (dynamics drawn last) paint over them. - -**Diagnosis direction (mechanism, not live-probing):** decomp -`DrawPortalPolyInternal` (Ghidra 0x0059bc90) for the punch's depth STATE — -retail either depth-tests the punch polygon against the existing buffer -(only punching where the aperture is actually visible) or relies on -far→near per-building draw order so nearer geometry re-establishes depth -AFTER the punch. Compare against the T1 (`579c8b0`) punch pass wiring. +**2026-07-09 triage:** investigated, verdict STILL_OPEN — the pinned harness diagnostic (`Diagnostic_Tick22760_DumpEngineInternals`) still shows the harness hard-stopping laterally where live retail slides, and `BSPStepUpTests.D4_AirborneMover_TallWall_PersistsSlidingNormalAcrossFrames` remains explicitly `Skip`-tagged citing this issue; only one Ghidra-confirmed partial fix (`bf18a543`, `F_EPSILON` vs `EpsilonSq`) has landed. --- @@ -6793,47 +5832,6 @@ owner object. --- -## #122 — Windows oscillate between background and the correct outside view when entering houses - -**Status:** OPEN -**Severity:** MEDIUM -**Filed:** 2026-06-11 (re-gate; user: "the oscillating between -background world and the right outside view is now back on some windows -when entering houses") -**Component:** render — window exit-portal region at the root flip - -The #109 oscillation family, now localized to WINDOWS during house -ENTRY (the outdoor→interior root flip). Candidate mechanisms: -(a) the #120 reciprocal ping-pong polluting clip volumes near the -portal plane during the crossing — the firing sites were exactly -cottage cells during entry; RE-CHECK after `dede7e4` before -investigating; (b) the seal/punch handoff on windows across the root -flip (forceFarZ keys on `clipRoot.IsOutdoorNode`, flipping the window -aperture between punch and seal semantics frame-to-frame at the -threshold). - ---- - -## #123 — Buildings transiently disappear when running close past them - -**Status:** OPEN -**Severity:** MEDIUM -**Filed:** 2026-06-11 (re-gate; user: "when I pass by close by -buildings, sometimes the building disappears as I run by") -**Component:** render — outdoor root, close-range building draw - -Whole-building transient vanish at close range under the outdoor root. -Suspects (unverified): the per-building frustum pre-gate on -`Building.PortalBounds` (T2 draw-driven flood gather) interacting with -close-range AABB degeneracy; dispatcher frustum cull with a stale -entity AABB; or the #117 stencil punch marking a near-full-screen -aperture fan at grazing range while the building's own flood is gated -off (far-Z holes → sky/fog where the shell should be). Needs evidence -first: reproduce with `ACDREAM_PROBE_VIS`/`[outdoor-node]` + a capture -of which draw list the building's shell left. - ---- - ## #124 — Looking out through an opening: far buildings with openings show missing/transparent back walls **Status:** CLOSED (user-gated 2026-06-12 evening: "124, that one is solved") @@ -6963,28 +5961,6 @@ meshMissing counters. --- -## #126 — Outdoor spawn claim on a building roof is grounded THROUGH the roof to terrain (transparent-interior spawn) - -**Status:** OPEN — HIGH (every login/logout on any walkable roof) -**Filed:** 2026-06-11 (tower capture run, `tower-viewer-capture.log` line 1) -**Component:** physics — login snap (the #107/#111 family) - -**Evidence:** the user logged out standing ON TOP of the AAB3 tower -(z=127.2). The next login: `[snap] claim=0xAAB30023 pos=(297.160, --129.182,127.200) … terrainZ=112.000 indoor=False -> targetZ=112.000 -targetCell=0xAAB30023`. The snap's OUTDOOR branch always grounds to -TERRAIN Z — it warped the player from the roof down INTO the tower's -interior volume at ground level, still outdoor-classified → the -transparent-interior spawn the user reported ("spawned in the tower and -it was transparent"), self-healing only after walking out and back in. -**Fix shape:** an outdoor claim must ground to the nearest WALKABLE -surface at/below the claim Z (building roofs and GfxObj floors via the -physics walkable query — the #111 `WalkableFloorZNearest` machinery), -not raw terrain. Note the snap line even shows a candidate it rejected -(`bestCell=0xAAB30101 bestZ=124.3`). - ---- - ## #127 — Per-building flood admissions are BISTABLE per frame under the outdoor root (the building-flap mechanism) **Status:** CLOSED 2026-06-12 — user re-gate ("Seems to have been @@ -7564,63 +6540,10 @@ The burden bar now reads the server's wire `EncumbranceVal` (PropertyInt 5) inst --- -## #113 — Phantom staircase: REOPENED 2026-06-11, folded into the HOLISTIC BUILDING-RENDER PORT +## #113 — Phantom staircase / holistic building-render port -**Status:** REOPENED — root cause #2 found (drawing-BSP-orphaned no-draw polys: -hall model 0x010014C3 keeps its walkable stair-ramp as dict polys {0,1}, in the -PhysicsBSP, referenced by NO DrawingBSP node — retail never draws them, we -iterate the dictionary). The mechanical filter (`e46d3d9`) removed the phantom -everywhere (user-verified) but made DOORS vanish across Holtburg → un-applied -(`124c6cb`, helper + dat pins kept). Per the user's 2026-06-11 mandate ("solve -this holistic once and for all"), #113/#114/#108/#109/the door mystery/#99 are -now ONE effort: map acdream-vs-retail for building draw / interiors / interior -collision / dynamics / clipping / culling, then port retail's drawing -discipline. **CHARTER + paste-ready next-session prompt:** -[docs/research/2026-06-11-building-render-holistic-port-handoff.md](../research/2026-06-11-building-render-holistic-port-handoff.md). -The shell-clip work below remains in (outdoor-scoped) and correct. - -### (history) PView shell clip was never GL-enabled — 927fd8f + scope 9ce335e - -**Status:** FIXED (self-gated by screenshot comparison at the original spot — -phantom gone; formal user visual gate pending) -**Closed:** 2026-06-10 -**Component:** render (PView shell pass GL state) - -**Attribution (dat-evidenced — the filed "A9B3 misplaced interior cell" -hypothesis is REFUTED):** the building is the Holtburg MEETING HALL — AAB3 -building[0], model `0x010014C3` at AAB3-local (36,84,116), not an A9B3 -building (A9B3 has exactly ONE building, the #112 hill cottage; the user -stood at the A9B3/AAB3 boundary — `issue112-gate1.log` cell-transit trail — -and clicked through the hall to the NPC behind it). The hall's interior -stair cells (0x100..0x106, a ring climbing z 116→124.5 to the deck hatch) -have geometry COINCIDENT with the shell's west wall (both at local x=29.0). - -**Root cause:** retail clips drawn CELL geometry to the accumulated portal -view (`Render::set_view` :343750 + `planeMask=0xffffffff` per cell polygon -:427922 → `polyClipFinish`). Our equivalent — `UseShellClipRouting` → -`mesh_modern.vert` `gl_ClipDistance` — was routed with CORRECT tight clip -regions (`Issue113MeetingHallFloodTests` proves 4–6 planes, door-aperture -NDC boxes) but was INERT: `gl_ClipDistance` writes are ignored unless -`GL_CLIP_DISTANCEi` is enabled, and no caller ever enabled it for the shell -pass (born inert in `1405dd8`). Flooded interior cells drew WHOLE → the -interior staircase painted across the exterior wall; unpickable because -it's cell geometry. 5th instance of -`feedback_render_self_contained_gl_state`. - -**Fix (`927fd8f`):** enable `GL_CLIP_DISTANCE0..7` around exactly the shell -pass in `RetailPViewRenderer.DrawEnvCellShells` (no early-outs between set -and restore). Entities/characters stay unclipped (retail's mesh path is -viewcone-check, not poly-clip — comment scoped). Known remaining -approximation: slot-0 fallback slices (>8-plane apertures) still draw -pass-all — the assembler's scissor fallback remains unimplemented (rare; -pinned 0 such slices at the hall). - -**Refuted along the way (evidence in `Issue113PhantomStairsDumpTests`):** -the unifying misplaced-cell hypothesis — all 17 A9B3 cottage cells share -one identical dat Position (nothing to misplace); the #112 gap is a real -20 cm doorway micro-gap, not a displaced volume; missing object collision -remains #99/A6.P4. A9B3's dat has NO stair geometry anywhere near the spot -(shell = balcony slabs z119 + turret roof; cells flat 116/118.8). +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +The Holtburg meeting-hall's walkable stair-ramp painted across the exterior wall because the PView shell-clip pass computed correct clip regions but never enabled `GL_CLIP_DISTANCE` — fixed by `927fd8f`. The bug reopened 2026-06-11 when a second root cause surfaced (dictionary-referenced-but-undrawn BSP polys drawn that retail never draws); per the user's mandate this was folded into the holistic building-render port effort (#113/#114/#108/#109/#99, charter at `docs/research/2026-06-11-building-render-holistic-port-handoff.md`), which shipped as part of the broader render-pipeline redesign (Option A) closed 2026-06-12. ## #111 — ACE-mutated indoor restores: transparent interior / wrong placement at login — [DONE 2026-06-10 · 5f1eb7c + 5706e0e + 2735695] @@ -8480,6 +7403,186 @@ Regen workflow: `py tools/pdb-extract/pdb_extract.py refs/acclient.pdb`. The com **Commit:** `feat(player): #5 PlayerDescription parser — Stam/Mana via attribute block` **Resolution:** First attempt (commit `d42bf57`) used `AppraiseInfoParser` for `PlayerDescription (0x0013)` — wrong wire format. ACE source confirmed via `GameEventPlayerDescription.WriteEventBody`: PlayerDescription is hand-written (DescriptionPropertyFlag-driven property hashtables, vector flags, attribute block, skills, spells, options/inventory tail) — distinct from `IdentifyObjectResponse (0x00C9)`'s `AppraiseInfo.Write`. Pivoted to a real port: new `PlayerDescriptionParser.cs` that walks property hashtables (Int32/Int64/Bool/Double/String/Did/Iid + Position) gated on the property flags, then reads vector flags + has_health + the attribute block where vitals 7/8/9 carry `ranks/start/xp/current`. Also redesigned `LocalPlayerState` to track per-vital snapshots (replacing the sentinel-API of attempt 1) plus per-attribute snapshots, with `GetMaxApprox` applying the retail formula `vital.(ranks+start) + attribute_contribution` (Endurance/2 for Health, Endurance for Stamina, Self for Mana). Live verified: `+Acdream` shows three bars; ~95% reading on Stam/Mana traced to active buff multipliers (filed as #6). Wire-port also added `PrivateUpdateVital (0x02E7)` + `PrivateUpdateVitalCurrent (0x02E9)` for delta updates per holtburger `UpdateVital`. ~700 LOC C#, 30+ new tests. +## #59 — WorldPicker 5m fixed-radius could over-pick at tight thresholds (M1-deferred polish) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +`WorldPicker.Pick` used a hardcoded 5m sphere around every candidate regardless of the entity's actual size, relying on the `ServerGuid==0` skip filter plus closest-wins logic to avoid mis-picks. User confirms this is resolved. Files: `src/AcDream.Core/Selection/WorldPicker.cs`. + +--- + +## #117 — Aperture-shaped see-through: doors/interiors visible through terrain hills and through nearer buildings + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Doors/interiors were visible through terrain hills and through nearer buildings because the far-Z stencil punch erased the depth of nearer occluders at aperture pixels, letting later-drawn dynamics paint over them. Fixed in commit `478c549`; the issue's own title already recorded the user's 2026-06-11 re-gate confirmation ("Yes solved"). + +--- + +## #133 — Teleport into a dungeon snaps the player BEFORE the dungeon landblock streams in → lands at the old landblock's frame (ocean), not the dungeon + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Teleporting into a dungeon snapped the player onto the OLD (Holtburg) landblock before the destination dungeon streamed in, dropping them into the ocean instead of the dungeon. Fixed by Phase G.3a: `TeleportArrivalController` holds the position snap until the destination landblock/cell hydrates (`7947d7a`/`aca4b46`/`f22121b`), plus the validated-claim landblock-prefix fix (`2ce5e5c`) and a login-spawn recenter fix (`47ae237`) for the sibling case of logging in already inside a dungeon. A follow-up streaming-collapse fix (2026-06-14) also resolved the low-FPS/grey-barrier symptoms discovered at the same gate. + +--- + +## #78 — Outdoor geometry (stabs + terrain mesh) visible inside EnvCells + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Standing inside a building, outdoor terrain and stab geometry rendered through the floor/walls because acdream enforced indoor visibility via three inconsistent gates instead of retail's single PView gate. This was the founding bug behind the full render-pipeline redesign (Option A, one `DrawInside(viewer_cell)` traversal — see `docs/research/2026-05-31-render-architecture-reset-handoff.md`), closed as part of the broader #119/#128 render arc (2026-06-12); see `project_render_pipeline_digest.md`. + +--- + +## #103 — Phase A8.F portal-frame indoor rendering broken at runtime (visual-gate failure) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +The A8.F two-pipe (inside/outside) portal-frame renderer (`ACDREAM_A8_INDOOR_BRANCH=1`) was abandoned wholesale rather than fixed in place — Phase U (Unified Render Pipeline, 2026-05-30) deleted the broken `RenderInsideOut` two-pipe path and replaced it with the single unified retail PView portal-visibility pipeline that the render digest now treats as canonical. See `docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md`. + +--- + +## #79 — Indoor lighting: spurious spot lights on walls + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Torch point-lights on interior walls in Holtburg Inn showed as spurious spot-light-like patches not matching retail's falloff/direction. User confirms this is resolved, most likely as part of the broader A7 indoor-lighting umbrella work (#93/#80/#154) that corrected `LightManager` candidate-pool scoping and per-light parameter handling. Files: `src/AcDream.Core/Lighting/LightInfoLoader.cs`, `mesh_modern.frag` `accumulateLights`. + +--- + +## #82 — Some slope terrain lit incorrectly + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Some terrain slopes in Holtburg were lit differently than retail, suspected to be a terrain-normal / landblock-edge normal-blending divergence between WorldBuilder's split formula and retail's `FSplitNESW`. User confirms this no longer reproduces. Files: `TerrainModernRenderer.cs`, `terrain_modern.frag`. + +--- + +## #154 — Dungeon interiors still read too dim vs retail (torch-sparse stretches + per-vertex bake) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +After the initial indoor-lighting fixes (`0d8b827`/`57c2ab7`) dungeons were brighter than the old flat-0.2 ambient but still trailed retail, especially in torch-sparse corridors. User confirms this is now resolved — consistent with the same-day A7 dungeon-lighting root-cause work (the "463>128 light cap" fix in the most recent commit history) that closed the sibling #93/#80 lighting issues. + +--- + +## #83 — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Walking down into cellars and on 2nd floors was broken (stuck/falling); root cause was `TryFindIndoorWalkablePlane` synthesizing a fresh `ContactPlane` every frame instead of retaining the previous frame's plane like retail. User confirms this is resolved, consistent with the later A6.P4 per-cell collision architecture (shipped 2026-06-11) and the subsequent #137/#171/#182 physics fixes that completed the retail-faithful collision port this issue was blocked on. + +--- + +## #88 — Indoor static objects vibrate (bookshelves, open furnaces) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Static cell objects (bookshelves, open furnaces) showed per-frame transform jitter, suspected sub-step state corruption or floating-point drift in per-part transform recomputation. User confirms this no longer reproduces, likely resolved alongside the A6.P4 physics architecture and subsequent entity-transform stabilization work. + +--- + +## #89 — Port BSPQuery.SphereIntersectsCellBsp for retail-faithful CheckBuildingTransit + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +`CellTransit.CheckBuildingTransit` used a radius-less point-in-BSP test instead of retail's sphere-vs-BSP `CCellStruct::sphere_intersects_cell`, making outdoor→indoor entry fire ~sphereRadius deeper into doorways than retail. User confirms this is resolved, consistent with the A6.P4 per-cell shadow-list collision architecture (shipped 2026-06-11) which ported retail-faithful building-transit checks. Files: `src/AcDream.Core/Physics/CellTransit.cs`, `BSPQuery.cs`. + +--- + +## #134 — Player "lags downward" instead of gliding along a dungeon ramp edge + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Running along a dungeon ramp's edge produced a downward "lag" instead of a slide along the slope tangent, surfaced by the #133 connector-cell physics fix exercising the ramp's collision for the first time. User confirms this is resolved, consistent with the later CSphere/CCylSphere collision-family ports (#172, #182) and the general slide-response work tracked under #32/#116. + +--- + +## #46 — Retail observer of acdream sees blippy / laggy movement + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +A retail client observing acdream's locally-driven `+Acdream` character saw stepped/blippy movement, suspected `AutonomousPosition` heartbeat cadence or `MoveToState` state-change-detection mismatches on the outbound path. User confirms this is resolved — matches the L.2b outbound wire-parity work per `project_retail_motion_outbound.md`. + +--- + +## #122 — Windows oscillate between background and the correct outside view when entering houses + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Windows flickered between the background/skybox and the correct outside view while entering houses (the #109 oscillation family localized to windows at the outdoor→interior root flip). User confirms this is resolved, consistent with the render-pipeline redesign (Option A, one `DrawInside(viewer_cell)`) that closed the broader #119/#128 flap family on 2026-06-12. + +--- + +## #123 — Buildings transiently disappear when running close past them + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Whole buildings transiently vanished when the player ran close past them at the outdoor root, suspected frustum pre-gate or stencil-punch interaction at close range. User confirms this is resolved, consistent with the render-pipeline redesign (Option A) that closed the broader #119/#128 flap family on 2026-06-12. + +--- + +## #179 — Lightning flash has no indoor gate (dormant until weather strobes ship) + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +`mesh_modern.frag` added the lightning-flash term to every fragment (including sealed-dungeon interiors) with no indoor gating, latent until weather strobes ship. User confirms this is resolved — the flash term is now gated for `playerInsideCell` frames (or zeroed at the `SceneLightingUbo` build), matching retail's flat indoor ambient with no storm terms. + +--- + +## #65 — Local player doesn't turn to face target on close-range Use + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +ACE's close-range Use path sends a `MovementType=8 TurnToObject` motion that acdream's `OnLiveMotionUpdated` didn't handle, so the local player completed the Use without visibly turning to face the target. User confirms this is resolved, consistent with the R4/R5 `MoveToManager` arc that ported retail's TurnTo handling for local and remote entities (superseding the older `RemoteMoveToDriver`). + +--- + +## #66 — Local + remote rotation: player flips back, NPCs don't turn + +**Status:** DONE (2026-07-09, user-confirmed via memory during an issues-triage pass — not independently re-verified this session). +Two related rotation bugs: the local player's facing snapped back after auto-walk arrival, and NPCs didn't turn to face the player on `MovementType=8 TurnToObject` motions (acdream only handled `MovementType=6 MoveToObject`). This issue explicitly superseded #65 as the broader TurnToObject-handling ticket. User confirms both are resolved, consistent with the R4/R5 `MoveToManager` arc's retail-faithful TurnTo port for local and remote entities. + +--- + +## #96 — Per-tick PhysicsEngine.ResolveWithTransition CP seed (retail divergence) + +**Status:** DONE (2026-07-09, investigated this session). +#96 was never a plain bug to fix — by 2026-05-22 (commits `892019bc`/`f8d669be`) the project determined the per-tick `ContactPlane` seed at `PhysicsEngine.cs` is load-bearing (BSP step_up on the last stair step depends on it) and formally reclassified it as an accepted retail divergence rather than a defect. It's tracked today as register row **IA-1** in `docs/architecture/retail-divergence-register.md` (retail: `CTransition::init`, pc:272547) rather than as an open bug. The code today still matches the issue's description exactly (verified at `PhysicsEngine.cs:979-1010`), so nothing was "fixed" in the traditional sense — the issue was superseded by the divergence-register entry as its permanent home. + +--- + +## #60 — `obstruction_ethereal` retail downstream path not ported (M2 combat-HUD impact) + +**Status:** DONE (2026-07-09, investigated this session). +Fixed by `3361a8d7` and `dc1e9270` (2026-06-24). `CollisionExemption.ShouldSkip` now requires both `ETHEREAL_PS` and `IGNORE_COLLISIONS_PS` for the Gate-1 exempt; ETHEREAL-alone now sets `SpherePath.ObstructionEthereal` and flows through BSP/Sphere/Cylinder consume sites matching retail pc:276782/276806/276989/321692/324573. Divergence-register row AD-7 retired same commit. `ObstructionEtherealTests.cs` passes 11/11. + +--- + +## #68 — Remote players don't stop running animation on auto-walk arrival + +**Status:** DONE (2026-07-09, investigated this session). +The mechanism named as the suspect in #68 (`RemoteMoveToDriver`'s arrival handling not flipping the animation cycle to Ready) was deleted wholesale in commit `7016b26c` (R4-V4, 2026-07-03), replaced by a verbatim-ported retail `MoveToManager` for every remote. The same day, commit `c2dc1a88` (R4-V5) fixed exactly the class of bug #68 describes: `StopCompletely` (which `MoveToManager`'s arrival path calls) wasn't reaching the entity's `AnimationSequencer` at all — it's now wired through `IInterpretedMotionSink.StopCompletely()` → `MotionTableDispatchSink` → `sequencer.PerformMovement(StopCompletely())`, confirmed live-wired for remotes in `GameWindow.EnsureRemoteMotionBindings`. No commit references "#68" explicitly and no user visual re-verification of this specific scenario is on record post-fix — recommend a spot-check (retail player auto-walking to an NPC, watched from acdream) if it resurfaces. + +--- + +## #87 — Drop WB fork patch by switching to PrepareEnvCellGeomMeshDataAsync + +**Status:** DONE (2026-07-09, investigated this session). +Superseded by Phase O (2026-05-21, dropped the `WorldBuilder.Shared`/`Chorizite.OpenGLSDLBackend` project references entirely — there's no more forked submodule to patch/revert) and Phase A8 (2026-05-28, shipped `EnvCellRenderer.cs` which already calls the narrow `PrepareEnvCellGeomMeshDataAsync(geomId, environmentId, cellStructure, surfaces)` entry point at `EnvCellRenderer.cs:355`, using the bit-32-tagged synthetic geom id the issue proposed). `ObjectMeshManager.cs`'s bounds/type dispatch now branches on resolution type before calling `TryGet`, so the blind `TryGet` bug is structurally gone. `EnvCellRenderer` remains the live production cell-rendering path under Phase U as of commit `6aabe0b5` (Jul 6 2026). Note: a second, unrelated, already-DONE issue also uses the heading `## #87` elsewhere in this file (a pre-existing numbering collision, left untouched). + +--- + +## #126 — Outdoor spawn claim on a building roof is grounded THROUGH the roof to terrain (transparent-interior spawn) + +**Status:** DONE (2026-07-09, investigated this session). +Fixed same-day as filed (2026-06-11) by commit `120aeff7` "RETAIL-CORRECTED: restores commit the server Z — retail never re-derives position from surfaces" (superseding an earlier same-day attempt `b94a7e80` that the user caught as retail-divergent). The outdoor zero-delta-restore branch in `src/AcDream.Core/Physics/PhysicsEngine.cs` now commits the claim's Z verbatim (matching retail's `CPhysicsObj::SetPositionInternal`, 0x00515bd0) instead of unconditionally grounding to terrain, eliminating the roof-to-interior warp. Verified present, unmodified, at HEAD. + +--- + +## #4 — Sky horizon-glow disabled (fog-mix skipped on sky meshes) + +**Status:** DONE (2026-07-09, investigated this session). +The literal bug as filed — fog-mix skipped on sky meshes — is no longer true. Commit `97fc1b51` (2026-04-27) re-enabled sky fog using a retail-cited 3D range-fog formula (`docs/research/2026-04-23-sky-fog.md`, which root-caused retail's actual horizon-glow mechanism: sky domes saturate to `WorldFogColor` by design because their intrinsic radius sits near/past keyframe FogEnd — same fog path as terrain, no special sky-only rule needed). Author noted user visual verification against retail screenshots; the fix has shipped unmodified for 2.5+ months through several subsequent commits. Follow-up: the `SKY_FOG_FLOOR=0.2` mitigation clamp layered on top still lacks a divergence-register row. + +--- + +## #81 — Static building stabs don't react to atmospheric lighting changes + +**Status:** DONE (2026-07-09, investigated this session). +Fixed by the A7 lighting rework, specifically A7 Fix D (`0980bea4`, `cf627933`, `c62da825`, `b7d655bc`, 2026-06-18/19). That work split the previously-unified lighting shader into an explicit object-path (`uLightingMode=0`) vs EnvCell-interior-path (`uLightingMode=1`): building exterior shells (stabs, `IsBuildingShell`/`ParentCellId==null`) are object-path entities and now read the same per-frame `SceneLighting` UBO (sun direction/color + ambient, rebuilt every frame from the live `SkyKeyframe`) that terrain and scenery already consumed — so stabs now darken/brighten in lockstep with the day/night cycle by construction. No dedicated visual re-check specific to stabs-vs-daycycle is on record, but the code mechanism the issue blamed no longer exists. + +--- + +## #33 — Live entity collision shape collapses to one cylinder + +**Status:** DONE (2026-07-09, investigated this session). +The described mechanism (live entities collapsing to one root-centered cylinder) no longer exists. `RegisterLiveEntityCollision` (`GameWindow.cs:4244`) now builds a multi-shape list via `ShadowShapeBuilder.FromSetup` — one shape per CylSphere, Sphere, and BSP-bearing Part — and registers via `RegisterMultiPart` (A6.P4 Task 7, `ca9341c2`). The retail `CSphere` and `CCylSphere` collision families were ported verbatim in `78e57581` (sphere primitive), `6ab26989` (fix #172, CCylSphere family), and `96ae2740` (fix #182, CSphere family, retires TS-45). The remaining `setup.Radius` single-cylinder path is now a deliberately-audited fallback used only when no other shape data exists. `tests/AcDream.Core.Tests/Physics/ShadowShapeBuilderShapeSourceTests.cs` covers both a multi-shape object and a live-creature case. +