# Cylinder fallback dedupe (Phase A1) — implementation plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Skip the mesh-AABB-fallback cylinder registration for landblock stabs (`entity.Id & 0xFF000000u == 0xC0000000u`). Eliminates "thin air" collisions inside cottages and around building exteriors. **Architecture:** One-line bool definition near existing `_isOutdoorMesh` declaration + one additional clause in the existing `if` gate at the fallback's entry. No new files, no math changes, no new tests beyond visual verification. **Tech Stack:** C# .NET 10. Single source-file change. **Spec:** [`docs/superpowers/specs/2026-05-21-cylinder-fallback-dedup-design.md`](../specs/2026-05-21-cylinder-fallback-dedup-design.md) --- ## File Structure | File | Action | Responsibility | |---|---|---| | `src/AcDream.App/Rendering/GameWindow.cs` | Modify | Add `_isLandblockStab` flag + AND it into the mesh-AABB-fallback gate. ~3 lines added. | No new tests. The fix is verified by a live capture (no [entity-source] mesh-aabb-fallback emissions for 0xC0xxxxxx entities) + visual verification (no thin-air collisions inside cottages). --- ## Task 1: Gate mesh-AABB-fallback on "not a landblock stab" **Files:** - Modify: `src/AcDream.App/Rendering/GameWindow.cs` - **Insertion site 1:** immediately after the existing `_isOutdoorMesh` / `_isScenery` declarations at lines 5826-5829. - **Insertion site 2:** add a clause to the existing `if` at line 6050-6052 (the gate around the mesh-AABB-fallback path). - [ ] **Step 1: Add `_isLandblockStab` flag near `_isOutdoorMesh`** Find this code at lines 5825-5830 in `GameWindow.cs`: ```csharp uint _srcPrefix = entity.SourceGfxObjOrSetupId & 0xFF000000u; bool _isOutdoorMesh = ((entity.Id & 0x80000000u) != 0) // scenery || ((entity.Id < 0x40000000u) // stab && (_srcPrefix == 0x01000000u || _srcPrefix == 0x02000000u)); bool _isScenery = _isOutdoorMesh; if (_isScenery) scTried++; ``` Insert a new line immediately after `bool _isScenery = _isOutdoorMesh;` (but before the `if (_isScenery) scTried++;`), so the block becomes: ```csharp uint _srcPrefix = entity.SourceGfxObjOrSetupId & 0xFF000000u; bool _isOutdoorMesh = ((entity.Id & 0x80000000u) != 0) // scenery || ((entity.Id < 0x40000000u) // stab && (_srcPrefix == 0x01000000u || _srcPrefix == 0x02000000u)); bool _isScenery = _isOutdoorMesh; // ISSUES #83 / Phase A1 (2026-05-21): landblock stabs // (LandBlockInfo.Objects + Buildings) use entity.Id with the // 0xC0XXYY00+n layout per LandblockLoader.cs:55. Their BSP // collision covers the whole structure; the mesh-AABB-fallback // path below is for canopy-only-BSP procedural scenery // (0x80XXYY00+n) and produces a redundant 1.5m-clamped // invisible disc at the stab's mesh origin — the user-reported // "thin air" collision inside cottages. Gate the fallback to // exclude stabs. Spec: // docs/superpowers/specs/2026-05-21-cylinder-fallback-dedup-design.md. bool _isLandblockStab = (entity.Id & 0xFF000000u) == 0xC0000000u; if (_isScenery) scTried++; ``` - [ ] **Step 2: AND `!_isLandblockStab` into the existing fallback gate** Find this code at lines 6050-6052: ```csharp if (!isPhantomSetup && (_isOutdoorMesh || (entityBsp == 0 && entityCyl == 0)) && entity.MeshRefs.Count > 0) ``` Modify it to: ```csharp if (!isPhantomSetup && !_isLandblockStab && (_isOutdoorMesh || (entityBsp == 0 && entityCyl == 0)) && entity.MeshRefs.Count > 0) ``` (One new line, `&& !_isLandblockStab`, inserted as the second clause.) - [ ] **Step 3: Build the project** Run: `dotnet build` Expected: PASS — no compile errors. No warnings other than the pre-existing baseline. - [ ] **Step 4: Run the full Core test suite** Run: `dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj` Expected: PASS — same 8 pre-existing failures as baseline, no new failures, no new passes. - [ ] **Step 5: Commit** ```bash git add src/AcDream.App/Rendering/GameWindow.cs git commit -m "$(cat <<'EOF' fix(physics): skip mesh-AABB-fallback cylinder for landblock stabs ISSUES #83 Phase A1. Landblock stabs (entity.Id 0xC0XXYY00+n per LandblockLoader.cs:55) were being registered with TWO collision shadows: the correct per-part BSP at `entity.Id*256 + partIdx`, AND a redundant mesh-AABB-fallback cylinder at `entity.Id`. The fallback clamped to 1.5m radius, centered at the building's mesh origin, producing user-reported "thin air" collisions inside cottages and within 2m of building exteriors. The fallback was originally designed for canopy-only-BSP procedural scenery (0x80XXYY00+n) — trees whose BSP covers the canopy but not the trunk. Landblock stabs have full BSP coverage and don't need it. Probe evidence (launch-thinair capture): - 0xC0A9B479 cylinder fallback (Holtburg cottage): 104 hits in a short capture session, all inside the cottage main room (cell=0xA9B4013F), ~2m from the building's mesh origin. - 0xA9B47900 BSP (the actual cottage walls): 52 legitimate hits. Fix: one new bool _isLandblockStab + one clause in the existing mesh-AABB-fallback gate. Spec: docs/superpowers/specs/2026-05-21-cylinder-fallback-dedup-design.md. Co-Authored-By: Claude Opus 4.7 (1M context) EOF )" ``` --- ## Verification (manual, post-commit) After the commit lands, re-run the thin-air capture: ```powershell $env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call" $env:ACDREAM_LIVE = "1" $env:ACDREAM_TEST_HOST = "127.0.0.1" $env:ACDREAM_TEST_PORT = "9000" $env:ACDREAM_TEST_USER = "testaccount" $env:ACDREAM_TEST_PASS = "testpassword" $env:ACDREAM_DEVTOOLS = "1" $env:ACDREAM_PROBE_BUILDING = "1" $env:ACDREAM_PROBE_RESOLVE = "1" dotnet build -c Debug dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch-a1-verify.log" ``` User walks inside a Holtburg cottage and around outside. Expected: 1. **Visual:** can walk freely inside the cottage room — no invisible wall in the middle of the room. 2. **Log:** convert to UTF-8 and check: ```powershell Get-Content launch-a1-verify.log -Encoding Unicode | Out-File launch-a1-verify.utf8.log -Encoding utf8 Get-Content launch-a1-verify.utf8.log | Where-Object { $_ -match '\[entity-source\].*note=mesh-aabb-fallback.*entityId=0xC0' } | Measure-Object | Select-Object -ExpandProperty Count ``` Expected: **0** (no 0xC0 entities now register a mesh-AABB-fallback). Also expected: no `[resolve]` hits attribute to `0xC0XXYY79`-style cylinder IDs. --- ## Self-review checklist - [x] **Spec coverage**: spec components 1+2 (the bool + the gate clause) both implemented in Task 1 steps 1+2. - [x] **No placeholders**: every step has the exact line numbers and code blocks. - [x] **Type consistency**: `_isLandblockStab` is `bool`, referenced in the gate as `!_isLandblockStab`. Consistent. - [x] **Atomic commit**: one commit, ~3 lines added, single file. - [x] **Acceptance**: visual verification (no thin-air collision) + grep verification (no 0xC0 fallback registrations). - [x] **No new tests**: justified — the fix is in `GameWindow.cs` (app layer), which doesn't have natural unit tests, and the behavior is verified by live capture per the spec.