namespace AcDream.App.Streaming;
/// Result of the per-frame dungeon streaming-gate decision.
/// Passed to — collapse
/// streaming to the single dungeon landblock.
/// When non-null, override the streaming observer to this
/// landblock key (the cell id's high 16 bits, 0xXXYY). Null leaves the caller's observer as-is.
public readonly record struct DungeonGateResult(bool InsideDungeon, uint? ObserverLandblockKey);
///
/// AP-36: the dungeon streaming gate (#133 FPS). When the player stands in a SEALED
/// EnvCell (an indoor cell that doesn't see outside), streaming collapses to the single
/// dungeon landblock — AC dungeons have no adjacent landblocks, so the normal 25×25
/// window would pull in ~129 unrelated ocean-grid dungeons. The trigger is the player's
/// CURRENT cell (CellGraph.CurrCell, set the moment the player is placed), and the
/// observer is pinned to that cell's OWN landblock (the cell id high 16 bits) because a
/// dungeon's EnvCells sit at arbitrary ocean-grid world coords with negative local offsets.
///
/// Extracted from GameWindow.OnUpdate as a pure function so the
/// teleport-hold rule (below) is unit-testable without the GL/dat/network stack.
///
public static class DungeonStreamingGate
{
///
/// Decide the streaming gate from the player's current cell.
///
/// True while a teleport arrival is held (the controller is in
/// PortalSpace): the player is NOT yet placed, so is the frozen
/// SOURCE cell, not where the player is going.
/// CurrCell is EnvCell && !SeenOutside.
/// The current cell id (0xXXYYNNNN).
public static DungeonGateResult Compute(
bool isTeleportHold, bool currCellIsSealedDungeon, uint currCellId)
{
// #145/#138: during a teleport hold the player is NOT yet placed, so CurrCell is
// the frozen SOURCE cell — where the player IS, not where they're going. Streaming
// must follow the DESTINATION, which the PortalSpace observer pin already does, so
// the source-cell gate is suppressed. Otherwise a teleport OUT of a dungeon keeps
// streaming collapsed on the source dungeon (CurrCell still sealed) → the outdoor
// destination never hydrates → the TAS transit holds 600 frames → force-snap to
// ocean. A teleport INTO a dungeon is handled explicitly upstream by
// StreamingController.PreCollapseToDungeon (and the controller's _collapsed latch
// holds it through the hold), so suppressing the gate here doesn't regress it.
if (isTeleportHold)
return new DungeonGateResult(false, null);
if (currCellIsSealedDungeon)
return new DungeonGateResult(true, currCellId >> 16);
return new DungeonGateResult(false, null);
}
}