docs: indoor walkable-plane BSP port partial-ship handoff
Foundation work (6 commits ff548b9..f845b22) landed but visual
verification 2026-05-19 FAILED to fix the user-reported indoor bugs.
Documenting the deeper diagnosis + the next phase target without
reverting the foundation work.
What landed (kept):
- BSPQuery.FindWalkableInternal gained ref ushort hitPolyId (Task 1).
- New public BSPQuery.FindWalkableSphere wrapper over the existing
retail-faithful walkable finder (Task 2).
- Transition.TryFindIndoorWalkablePlane refactored through it,
PointInPolygonXY deleted (Task 3).
- [indoor-walkable] runtime-toggleable probe (Task 4).
- 5 new tests + 9 updated existing tests, all green; build clean.
What didn't fix: cellar descent FAIL, 2nd-floor walking FAIL
(intermittent falling-stuck), single-floor cottage REGRESSION (was
stable, now intermittent falling-stuck), phantom collisions PERSIST.
Probe evidence: 1443 MISS / 2 HIT over 1445 calls. Smoking gun:
foot-sphere-tangent-to-floor case fails PolygonHitsSpherePrecise's
|dist| > radius - epsilon check by ~0.0002. The BSP walker is
correct; the caller (TryFindIndoorWalkablePlane) is misusing it.
Root cause (deeper than originally diagnosed): TryFindIndoorWalkablePlane
exists only as a Phase 2 commit eb0f772 stop-gap. Retail doesn't
synthesize a ContactPlane per frame — retail RETAINS the previous
frame's plane when the BSP says no collision. Retail's find_walkable
only runs inside step_sphere_down (a sweep), never as a standing-still
query.
Next phase target: port retail's ContactPlane retention so the
resolver retains state across frames. Likely eliminates the per-frame
TryFindIndoorWalkablePlane call entirely. Foundation work (BSP walker
+ probe + tests) remains useful regardless.
ISSUES #83 remains OPEN with the deeper diagnosis.
Roadmap header updated to reflect partial-ship status.
Handoff at docs/research/2026-05-19-indoor-walkable-plane-bsp-port-shipped-handoff.md.
Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md
Plan: docs/superpowers/plans/2026-05-19-indoor-walkable-plane-bsp-port.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f845b2241a
commit
c6b3fd6ebf
3 changed files with 253 additions and 18 deletions
|
|
@ -282,29 +282,77 @@ slopes shows matching shading.
|
|||
|
||||
---
|
||||
|
||||
## #83 — Walking up stairs broken
|
||||
## #83 — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck)
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** HIGH (blocks vertical indoor traversal)
|
||||
**Status:** OPEN — foundation work landed 2026-05-19, root-cause fix deferred to a follow-up investigation phase
|
||||
**Severity:** HIGH (blocks vertical indoor traversal + degrades single-floor cases)
|
||||
**Filed:** 2026-05-19
|
||||
**Component:** physics, movement
|
||||
**Component:** physics, movement, resolver
|
||||
|
||||
**Description:** When the player tries to walk up stairs inside a
|
||||
building, movement is broken — gets stuck, gets bounced, or fails to
|
||||
ascend.
|
||||
**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.
|
||||
|
||||
**Root cause / status:** The retail physics has explicit step-up logic
|
||||
(`CPhysicsObj::step_up` etc.) ported into `PhysicsEngine` for outdoor
|
||||
terrain ramps. For indoor stairs (EnvCell CellStruct geometry composed
|
||||
of polygons), the step-up resolver may not be examining cell BSP
|
||||
correctly, OR cell BSP and cell mesh disagree on stair Z values.
|
||||
**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.
|
||||
|
||||
**Files:**
|
||||
- `src/AcDream.Core/Physics/PhysicsEngine.cs`
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` (cell BSP query path).
|
||||
**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).
|
||||
|
||||
**Acceptance:** Walking forward at the base of an inn stairwell ascends
|
||||
to the second floor without getting stuck.
|
||||
**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).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# acdream — strategic roadmap
|
||||
|
||||
**Status:** Living document. Updated 2026-05-19. **Between phases.** **Since the last header update:** Indoor cell rendering Phase 1 (diagnostics) + Phase 2 (fix) shipped — root cause was a one-line WB bug at `ObjectMeshManager.cs:1223` (blind `TryGet<Setup>` on GfxObj-prefixed stab ids threw `ArgumentOutOfRangeException` which WB's outer catch silently swallowed, causing 26/123 Holtburg cells to fail upload). Identified via diagnostic chain (5 `[indoor-*]` probes + a `ContinueWith` exception surfacer + a `ConsoleErrorLogger` injected into WB), fixed with a Setup-prefix guard. User visually confirmed floors render. Surfaced 9 pre-existing indoor bugs filed in `docs/ISSUES.md`. **Earlier:** C.1.5b shipped (issue #56 per-part transforms for multi-emitter PES + `EntityScriptActivator` extended to dat-hydrated EnvCell statics & exterior stabs — portal swirl, inn fireplace flames, cottage chimney smoke, spell-cast particles all match retail). post-A.5 polish completed (#52 lifestone, #54 JobKind, #53 Tier 1 cache); N.6 slice 1 shipped (gpu_us fix + radius=12 perf baseline, conclusion CPU dominates GPU 30–50×); C.1.5a shipped (portal PES wiring; surfaced #56 → resolved in C.1.5b).
|
||||
**Status:** Living document. Updated 2026-05-19. **Between phases.** **Since the last header update:** Indoor walkable-plane BSP port FOUNDATION shipped (6 commits, `ff548b9` → `f845b22`) but visual verification failed — cellar descent, 2nd-floor walking, single-floor cottage regressions all confirm the shipped fix doesn't address the user-reported indoor bugs. Root cause now diagnosed as deeper than originally thought: `TryFindIndoorWalkablePlane` exists as a Phase 2 stop-gap that retail doesn't have an analog for. Retail retains ContactPlane state across frames; we re-synthesize per frame. Foundation work (BSP walker + probe + tests) remains useful; next phase needs to port retail's ContactPlane retention mechanism and likely eliminate `TryFindIndoorWalkablePlane` entirely. 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). ISSUES #83 remains OPEN with deeper diagnosis. **Earlier:** Indoor cell rendering Phase 1 (diagnostics) + Phase 2 (fix) shipped — root cause was a one-line WB bug at `ObjectMeshManager.cs:1223` (blind `TryGet<Setup>` on GfxObj-prefixed stab ids threw `ArgumentOutOfRangeException` which WB's outer catch silently swallowed, causing 26/123 Holtburg cells to fail upload). Identified via diagnostic chain (5 `[indoor-*]` probes + a `ContinueWith` exception surfacer + a `ConsoleErrorLogger` injected into WB), fixed with a Setup-prefix guard. User visually confirmed floors render. Surfaced 9 pre-existing indoor bugs filed in `docs/ISSUES.md`. **Earlier:** C.1.5b shipped (issue #56 per-part transforms for multi-emitter PES + `EntityScriptActivator` extended to dat-hydrated EnvCell statics & exterior stabs — portal swirl, inn fireplace flames, cottage chimney smoke, spell-cast particles all match retail). post-A.5 polish completed (#52 lifestone, #54 JobKind, #53 Tier 1 cache); N.6 slice 1 shipped (gpu_us fix + radius=12 perf baseline, conclusion CPU dominates GPU 30–50×); C.1.5a shipped (portal PES wiring; surfaced #56 → resolved in C.1.5b).
|
||||
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
# Indoor walkable-plane BSP port — partial-ship handoff (2026-05-19)
|
||||
|
||||
**Outcome:** Foundation shipped (6 commits). Visual verification FAILED. User-reported bugs (cellar descent, 2nd-floor walking, phantom collisions) remain unresolved. Root cause now diagnosed deeper than originally thought; next phase needs to port retail's `ContactPlane` retention mechanism. Foundation work (BSP walker + probe + tests) is useful regardless of the next approach.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
I diagnosed the wrong root cause initially. I assumed `TryFindIndoorWalkablePlane`'s linear first-match XY scan picking the wrong polygon was the bug, and built a retail-faithful BSP-walker replacement (`BSPQuery.FindWalkableSphere` wrapper over the existing `FindWalkableInternal` port of `BSPNODE::find_walkable` + `BSPLEAF::find_walkable`). The BSP walker is correct, but it returns MISS for the standing-grounded case (foot sphere tangent to floor → `PolygonHitsSpherePrecise` correctly rejects tangent contact by ~0.0002 epsilon).
|
||||
|
||||
The actual root cause: **`TryFindIndoorWalkablePlane` shouldn't exist at all**. It was added 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 reports no collision. There is no retail analog of `find_walkable` as a standing-still query. `find_walkable` only runs inside a downward sphere sweep (`step_sphere_down`), where the sphere is moving and the overlap test is meaningful.
|
||||
|
||||
---
|
||||
|
||||
## What shipped (foundation)
|
||||
|
||||
6 commits, `ff548b9` → `f845b22`. `dotnet build -c Debug` clean; 8 pre-existing test failures unchanged baseline; 5 new tests + 9 updated existing tests all pass.
|
||||
|
||||
| # | SHA | Subject |
|
||||
|---|---|---|
|
||||
| 1 | `ff548b9` | `refactor(physics): expose hitPolyId from FindWalkableInternal` |
|
||||
| 2 | `7f55e14` | `feat(physics): add BSPQuery.FindWalkableSphere wrapper` (+ 4 unit tests) |
|
||||
| 3 | `86ecdf9` | `fix(physics): tighten FindWalkableSphere test assertions + header` (code review fix) |
|
||||
| 4 | `91b29d1` | `fix(physics): route indoor walkable-plane synthesis through retail BSP walker` |
|
||||
| 5 | `7c516ed` | `fix(physics): document adjustedCenter discard + restore wall-poly test` (code review fix) |
|
||||
| 6 | `f845b22` | `feat(physics): add [indoor-walkable] probe line` |
|
||||
|
||||
**Files touched:**
|
||||
- `src/AcDream.Core/Physics/BSPQuery.cs` — `FindWalkableInternal` gained `ref ushort hitPolyId`; new public `FindWalkableSphere` wrapper.
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` — `TryFindIndoorWalkablePlane` refactored from `static` linear scan to instance method routing through `FindWalkableSphere` with `WalkableAllowance` save/restore. `PointInPolygonXY` deleted. `[indoor-walkable]` probe added at the `FindEnvCollisions` callsite.
|
||||
- `tests/AcDream.Core.Tests/Physics/BSPQueryTests.cs` — 4 new `FindWalkableSphere` unit tests.
|
||||
- `tests/AcDream.Core.Tests/Physics/TransitionTypesTests.cs` — new file, integration test for two-overlapping-floors + WalkableAllowance preservation.
|
||||
- `tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs` — 9 tests updated to new instance-method + sphereRadius signature with BSP fixtures; 2 `PointInPolygonXY` tests deleted; 1 new wall-poly integration test.
|
||||
|
||||
---
|
||||
|
||||
## Visual verification — FAIL (user-driven, 2026-05-19)
|
||||
|
||||
Launch flags: `ACDREAM_DEVTOOLS=1`, `ACDREAM_PROBE_INDOOR_BSP=1`. Log: `launch-walkable-fix-6.log` (latest run).
|
||||
|
||||
User report verbatim:
|
||||
> Cant walk down to the cellar. Looks like ground is blocking.
|
||||
> I get stuck sometimes in a falling animation at random places.
|
||||
> When I walk up on second floors. I get stuck sometimes on random places in falling animation.
|
||||
> Lightning is still broken.
|
||||
> Get phantom collison in rooms.
|
||||
> NO change
|
||||
|
||||
Result against acceptance scenarios:
|
||||
|
||||
| Scenario | Pre-ship | Post-ship | Outcome |
|
||||
|---|---|---|---|
|
||||
| Cellar descent | "ground blocking" | "ground blocking" | **FAIL** — no change |
|
||||
| 2nd-floor walking | "snaps back / invisible obstacles" | "intermittent falling-stuck" | **FAIL** — different symptom, still broken |
|
||||
| Single-floor cottage walking | stable | "intermittent falling-stuck at random spots" | **REGRESSION** — degraded from stable to unstable |
|
||||
| Phantom collisions in rooms | present | present | **PERSIST** |
|
||||
| Indoor lightning (#79/#80/#81/#82) | broken | broken | unchanged (out of scope for this phase) |
|
||||
|
||||
---
|
||||
|
||||
## Probe evidence (from launch 1)
|
||||
|
||||
`[indoor-walkable]` probe captured 1445 calls in a Holtburg-area session. **1443 MISS / 2 HIT.**
|
||||
|
||||
Sample HIT line:
|
||||
```
|
||||
[indoor-walkable] cell=0xA9B40150 wpos=(132.258,16.524,94.480) probe=0.50 result=HIT poly=0x0000 wn=(0.000,0.000,1.000) wD=-94.020 dz=+0.46
|
||||
```
|
||||
|
||||
Sample MISS line:
|
||||
```
|
||||
[indoor-walkable] cell=0xA9B40150 wpos=(132.258,16.524,94.500) probe=0.50 result=MISS
|
||||
```
|
||||
|
||||
The 20mm Z oscillation between `94.480` (HIT) and `94.500` (MISS) is the smoking gun:
|
||||
- World physics floor (after +0.02f cell-origin Z-bump in `PhysicsDataCache.CacheCellStruct`) is at `Z=94.020`.
|
||||
- When foot center is at `Z=94.500` (= floor + radius), distance to plane = `0.48` = sphere radius. `PolygonHitsSpherePrecise` checks `|dist| > radius - epsilon` (line 117 of BSPQuery.cs). `0.48 > 0.4798` → **rejected by ~0.0002**.
|
||||
- When foot center is at `Z=94.480` (= floor + 0.46), distance = `0.46 < 0.4798` → accepted, HIT.
|
||||
- The resolver oscillates between these two positions as the indoor walkable plane and the outdoor terrain backstop alternate as the contact source.
|
||||
|
||||
---
|
||||
|
||||
## Why the fix doesn't work — deeper diagnosis
|
||||
|
||||
`TryFindIndoorWalkablePlane` exists only as a Phase 2 stop-gap (commit `eb0f772`). It was added because the indoor BSP collision branch in `FindEnvCollisions` returns OK when the player is grounded standing still, but the resolver then needed a `ContactPlane` to feed `ValidateWalkable`. Without a synthesized indoor plane, the code fell through to outdoor terrain backstop, which is BELOW the indoor floor by `+0.02f`, marking the player as floating → falling-stuck. The Phase 2 fix synthesized a plane from `cellPhysics.Resolved` via a linear XY scan.
|
||||
|
||||
My Task 3 refactor swapped that linear scan for the retail-faithful BSP walker (`BSPQuery.FindWalkableInternal`). The BSP walker is correct — it implements `BSPNODE::find_walkable` + `BSPLEAF::find_walkable` faithfully. But in retail, this function is called from `BSPTREE::step_sphere_down` inside a movement sweep, where the sphere is moving downward. `walkable_hits_sphere` requires the sphere to overlap the plane (`|dist| < radius - eps`), which is satisfied during the sweep because the moving sphere penetrates the plane mid-sweep. In our standing-grounded use case, the sphere is tangent (foot resting on floor), not penetrating → no overlap → no walkable found → MISS.
|
||||
|
||||
**Retail's actual flow for the standing-grounded case:**
|
||||
|
||||
1. Player at rest on floor. ContactPlane retained from previous frame.
|
||||
2. Frame tick. Gravity + movement applied.
|
||||
3. `CTransition::transitional_insert` runs.
|
||||
4. `find_collisions` Path 5 (Contact branch): `sphere_intersects_poly` test.
|
||||
- If the sphere penetrates the floor (gravity moved it slightly down), `step_sphere_up` runs → `step_down` → `step_sphere_down` → `find_walkable` → finds the floor → `adjust_sphere_to_plane` snaps it up to tangent → ContactPlane updated.
|
||||
- If the sphere does NOT penetrate (still tangent from last frame), Path 5 returns OK. **ContactPlane is NOT recomputed — it's retained from last frame.**
|
||||
5. Player walks horizontally. Same as above — ContactPlane persists.
|
||||
|
||||
Our acdream code:
|
||||
- Per-frame `FindEnvCollisions` calls indoor BSP `FindCollisions`.
|
||||
- Indoor BSP returns OK (no collision).
|
||||
- We call `TryFindIndoorWalkablePlane` to RECOMPUTE the ContactPlane from scratch. This is the WRONG behavior — retail doesn't recompute.
|
||||
- The recomputation fails (BSP walker can't handle tangent sphere) or succeeds with a slightly-off plane (linear scan returning the wrong polygon's Z).
|
||||
- Either way: the ContactPlane is unstable frame-to-frame → resolver state oscillates → player gets stuck in falling animation.
|
||||
|
||||
---
|
||||
|
||||
## Recommended next phase: ContactPlane retention
|
||||
|
||||
Port retail's `ContactPlane` retention so the resolver retains the previous frame's plane when the BSP says "no collision," instead of re-synthesizing every frame.
|
||||
|
||||
**Investigation targets (retail decomp):**
|
||||
- `CTransition::transitional_insert` (acclient_2013_pseudo_c.txt:273137) — the main per-frame resolver entry. Note line 273165: `if (edi != OK_TS) this->sphere_path.neg_poly_hit = 0;` — only mutates state on non-OK results.
|
||||
- `CPhysicsObj::transition` family — where `LastKnownContactPlane` is read/written.
|
||||
- Search the decomp for `last_known_contact_plane` and `contact_plane_valid` to map the full lifecycle.
|
||||
- `CTransition::check_walkable` (referenced at line 273202) — possibly involved in walkable persistence.
|
||||
|
||||
**Likely shape of the fix:**
|
||||
- In `Transition.FindEnvCollisions` (TransitionTypes.cs:1262), when indoor BSP returns OK, DO NOT call `TryFindIndoorWalkablePlane`. Instead, retain the existing `CollisionInfo.ContactPlane` (which was set by the previous frame's step-up or step-down).
|
||||
- Only update the ContactPlane when an actual collision/step event occurs (Path 4 land, Path 5 step-up-success, Path 3 step-down-success).
|
||||
- Outdoor terrain backstop remains for the outdoor case but is gated on `!IsIndoor(cellId)`.
|
||||
|
||||
**Foundation work to keep:**
|
||||
- `BSPQuery.FindWalkableSphere` wrapper — useful for any future "find a walkable plane indoors" query (e.g., spawn-placement, teleport-target verification).
|
||||
- `FindWalkableInternal`'s `hitPolyId` ref param — same.
|
||||
- `[indoor-walkable]` probe — keep, but expect it to fire less often once retention is in place (only when the sphere is actually penetrating).
|
||||
- All 5 new tests + 9 updated tests — they verify the BSP walker's correctness, which is unchanged in the next phase.
|
||||
|
||||
**Foundation work to delete (or refactor):**
|
||||
- `Transition.TryFindIndoorWalkablePlane` — likely deleted entirely, OR kept as an out-of-band synthesis path for edge cases (initial spawn, cell-id promotion mid-frame) but no longer called per-frame from `FindEnvCollisions`.
|
||||
- `INDOOR_WALKABLE_PROBE_DISTANCE` constant — deleted with `TryFindIndoorWalkablePlane`, or kept for the out-of-band use case.
|
||||
|
||||
---
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **Do not** add a sphere-offset hack to make `PolygonHitsSpherePrecise` accept tangent contact. That mis-aligns acdream's overlap semantics with retail's. The right answer is to not call `find_walkable` in the standing-still case at all.
|
||||
- **Do not** revert the 6 foundation commits. They are correct retail-faithful ports; the BSP walker is needed for legitimate use cases (just not the one we wired it to).
|
||||
- **Do not** widen the +0.02f Z-bump or try to compensate for it in the resolver. The bump is a render concern; it should remain transparent to physics. The bug is in the per-frame ContactPlane recompute, not the bump itself.
|
||||
|
||||
---
|
||||
|
||||
## Quick reference for the next-session implementer
|
||||
|
||||
**Spec to read first (this phase's, for context — but don't re-execute it):**
|
||||
- `docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md` (committed `165f67a`)
|
||||
- `docs/superpowers/plans/2026-05-19-indoor-walkable-plane-bsp-port.md` (committed `e62d076`)
|
||||
|
||||
**Code anchors:**
|
||||
- [`src/AcDream.Core/Physics/TransitionTypes.cs:1262`](../../src/AcDream.Core/Physics/TransitionTypes.cs#L1262) — `FindEnvCollisions` indoor branch.
|
||||
- [`src/AcDream.Core/Physics/TransitionTypes.cs:1192`](../../src/AcDream.Core/Physics/TransitionTypes.cs#L1192) — `TryFindIndoorWalkablePlane` (the thing to likely delete in the next phase).
|
||||
- [`src/AcDream.Core/Physics/CollisionInfo`](../../src/AcDream.Core/Physics/) — search for `ContactPlane` write sites to map who currently sets it.
|
||||
- [`src/AcDream.Core/Physics/SpherePath`](../../src/AcDream.Core/Physics/) — `LastKnownContactPlane`-style fields if any exist.
|
||||
|
||||
**Retail decomp anchors:**
|
||||
- `docs/research/named-retail/acclient_2013_pseudo_c.txt:273099` — `CTransition::step_up`.
|
||||
- `docs/research/named-retail/acclient_2013_pseudo_c.txt:273137` — `CTransition::transitional_insert`.
|
||||
- `docs/research/named-retail/acclient_2013_pseudo_c.txt:323565` — `BSPTREE::step_sphere_up`.
|
||||
- `docs/research/named-retail/acclient_2013_pseudo_c.txt:326793` — `BSPLEAF::find_walkable` (already ported, behavior verified).
|
||||
|
||||
**Visual verification scenarios (re-use for the next phase):**
|
||||
1. Cellar descent (the primary failing scenario)
|
||||
2. 2nd-floor walking
|
||||
3. Single-floor cottage (regression check — must NOT degrade)
|
||||
4. Phantom collisions (cascade check — if root cause is fixed, these should improve)
|
||||
|
||||
**Launch command:**
|
||||
```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_INDOOR_BSP = "1"
|
||||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch-next-phase.log"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session lessons (for future Claude)
|
||||
|
||||
1. **Brainstorm a hypothesis-test before a full spec.** I diagnosed the wrong root cause and built 6 commits on it. A small spike (add the probe FIRST, capture a log, look at it before designing the fix) would have surfaced the 99.9% MISS rate immediately and pointed at the deeper issue.
|
||||
2. **Tangent contact is the dominant grounded case.** Any test fixture designed to exercise `walkable_hits_sphere` MUST include the tangent case (`dist == radius`), not just penetrating cases. My unit tests used Z=0.4 with radius=0.48 (overlap = 0.4 < 0.4798, passes easily) — comfortable but unrepresentative.
|
||||
3. **`find_walkable` is a sweep query, not a query.** It's only meaningful when called from `step_sphere_down`. Any caller using it as "stand here, find my floor" is misusing the algorithm. Retail doesn't have such a caller because retail retains ContactPlane across frames.
|
||||
4. **The +0.02f cell-origin Z-bump is a render artifact bleeding into physics.** It creates a 20mm offset between visual and physics floors. This is fine when the resolver retains state but breaks when the resolver re-computes every frame. The bump is not the root cause but it amplifies the oscillation symptom.
|
||||
Loading…
Add table
Add a link
Reference in a new issue