feat(physics): Phase 2 — wire CellBSP + Portals into CellPhysics

Adds PortalInfo struct and extends CellPhysics with CellBSP (third BSP
for point-in-cell tests, typed CellBSPTree from DatReaderWriter),
Portals (from envCell.CellPortals), PortalPolygons (resolved
cellStruct.Polygons — portals reference visible polys, not
PhysicsPolygons), and VisibleCellIds (populated for future use;
envCell.VisibleCells is List<UInt16>, not Dictionary).

Deletes CellPhysics.LocalAabbMin/Max and PhysicsDataCache.TryFindContainingCell
— Phase D's AABB shortcut is gone. CacheCellStruct's AABB compute
removed; the [cell-cache] diagnostic updated with portal/visible counts
instead.

CacheCellStruct signature gains an EnvCell parameter (one call site in
GameWindow.cs:5384 updated). ResolveOutdoorCellId drops the
TryFindContainingCell call; portal-graph CellTransit replaces it next.

ResolveOutdoorCellIdTests object initializers had the deleted AABB
properties stripped temporarily so the build stays green; the file gets
replaced wholesale in the next commit (CellTransit integration). Those
2 AABB-containment tests continue to fail (they were pre-broken on this
branch); no new failures introduced.

Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md
Plan: docs/superpowers/plans/2026-05-19-indoor-portal-cell-tracking.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-19 16:52:20 +02:00
parent b282c69f28
commit 1969c55823
7 changed files with 215 additions and 113 deletions

View file

@ -230,18 +230,15 @@ public sealed class PhysicsEngine
}
/// <summary>
/// Resolve a position's CellId. Tries indoor EnvCell containment first
/// (via <see cref="PhysicsDataCache.TryFindContainingCell"/>); falls back
/// to outdoor terrain landcell resolution.
/// Resolve a position's CellId. Falls back to outdoor terrain landcell
/// resolution or trusts an already-indoor fallbackCellId.
///
/// <para>
/// Indoor walking Phase D (2026-05-19) extended this to fix #84 + #85:
/// previously the function only resolved outdoor cells, so a player
/// geometrically inside an EnvCell stayed in outdoor-landcell range and
/// the indoor cell-BSP collision branch never fired. The indoor
/// containment check promotes the player's CellId to the matched
/// EnvCell, which lets <see cref="Transition.FindEnvCollisions"/>'s
/// indoor branch (gated on cellLow &gt;= 0x0100) take effect.
/// Phase D (2026-05-19) previously used an AABB containment check
/// (<c>TryFindContainingCell</c>) to promote the player into an indoor
/// EnvCell. Phase 2 (2026-05-19) removes that AABB shortcut; the
/// portal-graph <c>CellTransit</c> traversal (next subagent) replaces it
/// with retail-faithful BSP point-in-cell tests.
/// </para>
///
/// <para>
@ -256,12 +253,6 @@ public sealed class PhysicsEngine
if (fallbackCellId == 0)
return 0;
// Phase D: indoor-cell-containment check. If the player's worldPos
// is geometrically inside a cached EnvCell, return that cell's full
// id — overrides any prior outdoor CellId the caller passed in.
if (DataCache is not null && DataCache.TryFindContainingCell(worldPos, out var indoorId))
return indoorId;
// Pre-existing: if the caller already passes an indoor CellId AND
// the player isn't in any cached EnvCell, trust the caller. This
// preserves behaviour for indoor cells whose physics hasn't been