acdream/docs/research/2026-07-26-prepared-indoor-transit-regression.md

3.6 KiB

Prepared indoor transit regression

Date: 2026-07-26 Scope: Slice-I prepared collision consumer correction Behavioral boundary: No collision formula, portal rule, or renderer behavior changed. This restores the existing retail traversal over the production flat representation.

Symptom and connected evidence

After the Slice-I parsed-graph removal, entering houses or dungeons could:

  • collide with empty space at a doorway or room boundary;
  • retain the wrong indoor cell on stairs or upper floors;
  • expose the world background through the interior portal view.

The connected capture at .test-out/indoor-visibility-20260726-220007/stdout.log placed the player in Holtburg cottage vestibule 0xA9B40150, with the chase-camera eye outside the doorway in outdoor landcell 0xA9B40029. The original DAT graph advanced the camera transition from 0xA9B40150 to 0xA9B40029. The prepared production path retained 0xA9B40150, causing collision and portal rendering to share the same wrong viewer-cell root.

Retail oracle

Named retail:

  • CEnvCell::find_transit_cells @ 0x0052C820
  • CObjCell::find_cell_list
  • CEnvCell::find_visible_child_cell

Consolidated pseudocode: acclient_indoor_transitions_pseudocode.md.

For each CCellPortal, retail reads that portal's polygon plane from the owning CCellStruct. An exterior portal admits outdoor cells when any path sphere straddles the plane. An interior portal tests the loaded neighbor's cell-containment BSP, using the plane only as an unloaded-cell hint.

Root cause

The prepared package was complete:

  • FlatEnvCellTopology.Portals[i].PolygonIndex retained the direct portal polygon index;
  • FlatCellStructureCollisionAsset.PortalPolygons retained the exact plane;
  • prepared containment and physics BSPs matched the original DAT graph.

CellTransit.FindTransitCellsSphere, however, still began with:

if currentCell.PortalPolygons is null: return

PortalPolygons is the old parsed-DAT dictionary and is intentionally null in production after Slice I7. Therefore every prepared indoor cell skipped its entire portal loop. The I4/I5 differential referee covered BSP queries and complete resolver results, but did not exercise portal-plane lookup after the parsed graph had been removed, allowing the representation-boundary defect to escape.

Correction

CellTransit now resolves the exact same portal plane from:

  1. the parsed dictionary in explicit graph-oracle fixtures; or
  2. the prepared topology's direct polygon index and flat polygon table in production.

Prepared topology/list mismatches and invalid polygon indices fail loudly as corrupt publication rather than silently disabling indoor traversal. Portal iteration order, comparisons, sphere transforms, neighbor containment, and outside admission remain unchanged.

Automated evidence

  • Synthetic prepared exterior-portal test with no parsed polygon dictionary.
  • Synthetic prepared loaded-neighbor test with no parsed containment tree.
  • Exact connected Holtburg cottage camera path: graph and prepared paths both end in 0xA9B40029 at identical position.
  • Installed-DAT graph/flat coverage expanded across the Holtburg cottage and inn vestibules, connectors, stairwell, upper floor, and inn shell.
  • Physics-focused Release tests: 1,778 passed / 1 skipped.
  • Interaction/projection focused App tests: 127 passed.
  • Client-object ordering tests: 82 passed.
  • Complete Release build: passed with zero warnings.

No divergence-register row is required: the correction removes an accidental production divergence and introduces no replacement behavior.