feat(physics): Phase 2 — BuildingPhysics + CheckBuildingTransit
Closes the outdoor→indoor entry path. New BuildingPhysics type holds the per-SortCell BldPortal list + building world transform; PhysicsDataCache caches it (CacheBuilding + GetBuilding); CellTransit.CheckBuildingTransit tests each portal's destination cell via PointInsideCellBsp. PhysicsEngine.ResolveCellId's outdoor branch now hooks CheckBuildingTransit after the terrain-grid lookup: if the matched landcell has a cached building stab, check whether the sphere has crossed into one of its interior EnvCells before returning. GameWindow at landblock-load time iterates LandBlockInfo.Buildings and caches each via PhysicsDataCache.CacheBuilding. The landcell-id derivation uses retail's row-major cell-index formula (gridX * 8 + gridY + 1). Polish items from Subagent B/C reviews folded in: - visited HashSet in FindCellList's BFS (avoids O(N^2) re-enqueue) - ResolveCellId_NoDataCache_ReturnsFallback test (closes coverage gap) - DataCache-asymmetry comment in PhysicsEngine.ResolveCellId - Replaced misleading FindCellList outdoor-branch TODO with explicit note that ResolveCellId bypasses this branch — wired in ResolveCellId directly. - Removed unused 'using DatReaderWriter.Types;' from CellTransit.cs - 2 new CellTransitFindCellListTests integration tests - 1 new CellTransitCheckBuildingTransitTests test (null-CellBSP guard case; happy path deferred to visual verification). 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:
parent
aad697602e
commit
069534a372
8 changed files with 301 additions and 7 deletions
|
|
@ -258,7 +258,8 @@ public sealed class PhysicsEngine
|
|||
|
||||
if (fallbackLow >= 0x0100u)
|
||||
{
|
||||
// Indoor seed: use portal-graph traversal.
|
||||
// Indoor branch needs DataCache to look up cells; outdoor uses
|
||||
// _landblocks (no DataCache dependency).
|
||||
if (DataCache is null) return fallbackCellId;
|
||||
return CellTransit.FindCellList(DataCache, worldPos, sphereRadius, fallbackCellId);
|
||||
}
|
||||
|
|
@ -274,7 +275,29 @@ public sealed class PhysicsEngine
|
|||
if (localX >= 0f && localX < 192f && localY >= 0f && localY < 192f)
|
||||
{
|
||||
uint lowCellId = lb.Terrain.ComputeOutdoorCellId(localX, localY);
|
||||
return (kvp.Key & 0xFFFF0000u) | lowCellId;
|
||||
uint outdoorCellId = (kvp.Key & 0xFFFF0000u) | lowCellId;
|
||||
|
||||
// Outdoor→indoor entry: if this landcell has a cached building,
|
||||
// check whether the sphere has crossed into one of its interior
|
||||
// EnvCells via the building's portals.
|
||||
if (DataCache is not null)
|
||||
{
|
||||
var building = DataCache.GetBuilding(outdoorCellId);
|
||||
if (building is not null)
|
||||
{
|
||||
var candidates = new System.Collections.Generic.HashSet<uint>();
|
||||
CellTransit.CheckBuildingTransit(
|
||||
DataCache, building, worldPos, sphereRadius, candidates);
|
||||
if (candidates.Count > 0)
|
||||
{
|
||||
// First candidate wins — building portal containment is
|
||||
// mutually exclusive in retail (one interior cell per portal).
|
||||
foreach (var c in candidates) return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outdoorCellId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue