Commit graph

5 commits

Author SHA1 Message Date
Erik
0a7ce8fd58 Revert "fix(physics): remove per-frame indoor walkable-plane synthesis"
This reverts commit 9f874f4650.
2026-05-20 09:17:24 +02:00
Erik
9f874f4650 fix(physics): remove per-frame indoor walkable-plane synthesis
The indoor branch of FindEnvCollisions called Transition.TryFindIndoorWalkablePlane
every frame to re-synthesize the ContactPlane after BSP returned OK.
The synthesis routed through BSPQuery.FindWalkableSphere ->
walkable_hits_sphere, which correctly rejects tangent contact via
|dist| > radius - epsilon. For a grounded player standing on or
brushing a floor, the foot sphere is tangent: 99.87% MISS rate per
the 2026-05-20 [cp-write] probe (3150 MISS / 3154 calls). Each MISS
fell through to outdoor terrain backstop, writing a ContactPlane
that's below the indoor floor by ~0.02m (the render Z-bump),
marking the player airborne and triggering the falling-animation
stuck symptom user-reported on 2nd-floor walks.

Fix: delete the synthesis + outdoor-fallthrough from the indoor OK
path. ContactPlane is retained from the prior tick's seed
(PhysicsEngine.ResolveWithTransition:583, init_contact_plane
equivalent) or refreshed by BSP Path 3 (step_sphere_down) / Path 4
(land-on-surface) during the same tick. Matches retail's
BSPTREE::find_collisions OK path (acclient_2013_pseudo_c.txt:323938).

Also deletes:
- Transition.TryFindIndoorWalkablePlane (~104 lines incl. doc-comment)
- INDOOR_WALKABLE_PROBE_DISTANCE constant
- [indoor-walkable] probe log line
- IndoorWalkablePlaneTests.cs (8 tests, the helper's coverage)
- TransitionTypesTests.cs (1 test, also tested the helper)

Net: -491 lines. BSPQuery.FindWalkableSphere + its 5 unit tests
retained as the underlying retail-faithful walkable-finder API
(reachable for spawn-placement / teleport-verification / future
debug needs; its doc-comment is updated to reflect the change).

Closes Bug A in the indoor ContactPlane retention phase.
Spec: docs/superpowers/specs/2026-05-20-indoor-walkable-synthesis-removal-design.md.
Plan: docs/superpowers/plans/2026-05-20-indoor-walkable-synthesis-removal.md.
Predecessor: de8ffde (Bug B, BSP world-origin fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:11:04 +02:00
Erik
7c516edd7b fix(physics): document adjustedCenter discard + restore wall-poly test
Code review feedback on Task 3 commit 91b29d1:

- TryFindIndoorWalkablePlane: comment explaining why FindWalkableSphere's
  adjustedCenter out param is intentionally discarded (ValidateWalkable
  recomputes contact geometry from plane + foot position, consistent
  with the outdoor terrain path).
- IndoorWalkablePlaneTests: new TryFindIndoorWalkablePlane_WallPolyInBsp_ReturnsFalse
  restores integration-level coverage that the renamed NoBsp_ReturnsFalse
  lost. Verifies WalkableAllowance gate rejects a wall polygon in the
  cell BSP. Steep-poly rejection is also covered at the BSPQuery layer
  by FindWalkableSphere_SteepPoly_RejectedByWalkableAllowance.

No behavior change. Build clean; all related tests pass; same 8
pre-existing failures.

Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:58:53 +02:00
Erik
91b29d1a89 fix(physics): route indoor walkable-plane synthesis through retail BSP walker
TryFindIndoorWalkablePlane (Phase 2 commit eb0f772) used a linear
first-match XY scan of cellPhysics.Resolved with no Z-proximity test.
For any cell with two walkable polys overlapping in XY at different Z
(cellars, 2nd floors, balconies, stairs spanning floors), it returned
whichever polygon came first in dictionary order — typically the upper
floor when descending, causing the player to be reported below the
synthesized plane → ValidateWalkable fails → falling-stuck. Symptoms
reported by user 2026-05-19: cannot descend into cellar; cannot walk
on 2nd floor; "invisible obstacles at certain spots" (suspected
cascade from wrong-Z ContactPlane misrouting the resolver state).

Fix: route through BSPQuery.FindWalkableSphere (added previous commit),
which wraps the existing retail-faithful FindWalkableInternal
(BSPNODE::find_walkable + BSPLEAF::find_walkable port). Adds a
sphereRadius parameter to TryFindIndoorWalkablePlane so the foot
sphere is built with the actual entity radius rather than a guess.
WalkableAllowance is save/restored via try/finally so the slope
threshold used by walkable_hits_sphere doesn't leak back to the
resolver. Method becomes an instance method (was static) to access
this.SpherePath.

Deletes the now-dead PointInPolygonXY helper.

Updates IndoorWalkablePlaneTests.cs: all TryFindIndoorWalkablePlane
test fixtures now include a PhysicsBSPTree leaf node (required by
the new routing path), calls pass sphereRadius, and the PointInPolygonXY
tests are removed (method deleted). Adds TransitionTypesTests.cs with
an integration test covering two-overlapping-floors selection AND
WalkableAllowance preservation.

Closes (pending visual verification): ISSUES #83.
Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:47:49 +02:00
Erik
eb0f772f0f fix(physics): Phase 2 — synthesize indoor walkable plane from cell floor
When the indoor cell-BSP query returns OK (no wall collision), the player
is standing on a floor poly inside the cell. Previously the code fell
through to outdoor terrain (SampleTerrainWalkable + ValidateWalkable),
which used the OUTDOOR terrain plane — below the indoor floor due to the
+0.02f Z-bump applied for render z-fight prevention. ValidateWalkable
saw the player 0.5m above the outdoor plane → marked them as airborne
→ walkable=False → falling animation, never recovers.

Adds TryFindIndoorWalkablePlane (internal static for testability): scans
the cell's resolved physics polys for a walkable floor poly (normal.Z >=
0.6664, walkable-slope threshold matching retail) under the player's XY,
transforms its plane + vertices to world space via WorldTransform, and
calls ValidateWalkable with the indoor plane. Adds PointInPolygonXY
(ray-casting even-odd rule, ignores Z). Both are wired just after the
BSP OK branch in FindEnvCollisions; outdoor terrain remains a defensive
backstop if no floor poly is found under the player indoors (rare).

Matches retail's CEnvCell::find_env_collisions behavior: no fall-through
to terrain when the cell BSP successfully completes a query.

Evidence: launch-phase2-verify5.log captured 12,141 walkable=False
events during an indoor session where the player never managed to walk
back outdoor through a door — they got stuck against the indoor wall
and the resolver never re-established a walkable contact plane.

Adds 13 unit tests in IndoorWalkablePlaneTests.cs covering:
- player over floor poly (returns true, plane normal up, plane at correct Z)
- player outside poly XY (returns false)
- no walkable polys (returns false)
- empty Resolved dict (returns false)
- cell with world translation (plane + vertices in world space)
- PointInPolygonXY cases (centre, near corner, on boundary, outside, Z ignored)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:13:13 +02:00