diff --git a/src/AcDream.App/Rendering/CellVisibility.cs b/src/AcDream.App/Rendering/CellVisibility.cs
index 074aaa4..84638cf 100644
--- a/src/AcDream.App/Rendering/CellVisibility.cs
+++ b/src/AcDream.App/Rendering/CellVisibility.cs
@@ -83,6 +83,25 @@ public sealed class LoadedCell
/// and route IndoorPass cell scoping.
///
public uint? BuildingId { get; internal set; }
+
+ ///
+ /// Phase U.4c: the stab_list PVS as full (landblock-prefixed) cell ids — retail
+ /// CEnvCell.stab_list (acclient.h ~30925), the stable set of cells potentially
+ /// visible from this cell, precomputed by the AC content tools. Refreshed only at
+ /// hydration (= retail's per-cell-entry grab_visible_cells, decomp:311878).
+ /// PortalVisibilityBuilder grounds set membership in it so a brittle per-frame
+ /// portal-side test can't drop a potentially-visible cell from the visible set.
+ /// Empty when the dat carried no stab list (degenerate / old cell).
+ ///
+ public IReadOnlyList VisibleCells = System.Array.Empty();
+
+ ///
+ /// Phase U.4c: retail CEnvCell.seen_outside (acclient.h ~30925) — this cell sees
+ /// the exterior (an exit portal is reachable from it). Retail gates the landscape
+ /// data + draw decision on the camera cell's value (RenderNormalMode decomp:92649,
+ /// grab_visible_cells decomp:311878). The stable anchor for the terrain-draw test.
+ ///
+ public bool SeenOutside;
}
///
diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs
index 2a8947f..cf84efe 100644
--- a/src/AcDream.App/Rendering/GameWindow.cs
+++ b/src/AcDream.App/Rendering/GameWindow.cs
@@ -5693,6 +5693,16 @@ public sealed class GameWindow : IDisposable
portalPolygons.Add(polyVerts);
}
+ // Phase U.4c: surface the stable PVS + seen-outside flag onto the render cell.
+ // Both come straight off the dat EnvCell — no new parsing (PhysicsDataCache
+ // already reads VisibleCells the same way; A8CellAudit reads the flag).
+ uint lbPrefix = envCellId & 0xFFFF0000u;
+ var visibleCells = new List();
+ if (envCell.VisibleCells is not null)
+ foreach (var lowId in envCell.VisibleCells)
+ visibleCells.Add(lbPrefix | lowId);
+ bool seenOutside = envCell.Flags.HasFlag(DatReaderWriter.Enums.EnvCellFlags.SeenOutside);
+
var loaded = new LoadedCell
{
CellId = envCellId,
@@ -5704,6 +5714,8 @@ public sealed class GameWindow : IDisposable
Portals = portals,
ClipPlanes = clipPlanes,
PortalPolygons = portalPolygons, // Phase A8
+ VisibleCells = visibleCells, // Phase U.4c
+ SeenOutside = seenOutside, // Phase U.4c
};
_pendingCells.Add(loaded);
}