Files
healer-man/src/scene/sceneLifecycle.ts
T
2026-08-30 14:16:56 -04:00

56 lines
2.2 KiB
TypeScript

import type { DungeonId } from "../game/dungeonRegistry";
import type { Frameloop } from "@react-three/fiber";
import type { GameplayFrameRateLimit } from "../game/frameRateSettings";
/**
* Encounter transitions must never remount the Canvas: R3F intentionally loses
* the old WebGL context during Canvas disposal. Only explicit graphics recovery
* is allowed to replace the renderer.
*/
export function rendererCanvasKey(canvasRevision: number): string {
return `renderer:${Math.max(0, Math.trunc(canvasRevision))}`;
}
/** The Rapier world and all encounter actors are rebuilt for every session. */
export function encounterWorldKey(dungeonId: DungeonId, sessionRevision: number): string {
return `${dungeonId}:encounter:${Math.max(0, Math.trunc(sessionRevision))}`;
}
/**
* Readiness belongs to one encounter world. Comparing revisions avoids a
* child collision-ready effect being overwritten by a later parent reset in
* the same React effect flush, and prevents stale worlds from unblocking the
* current session.
*/
export function encounterWorldIsReady(
readySessionRevision: number | null,
sessionRevision: number,
): boolean {
return readySessionRevision !== null
&& readySessionRevision === Math.max(0, Math.trunc(sessionRevision));
}
/**
* A paused local world only needs a frame when React or R3F invalidates it.
* Loading, recovery, and shared-online worlds keep the continuous loop so
* asynchronous scene setup and authoritative remote simulation can progress.
*/
export function sceneFrameLoop(options: {
readonly worldReady: boolean;
readonly graphicsRecovering: boolean;
readonly sharedOnline: boolean;
readonly paused: boolean;
readonly mapOpen: boolean;
readonly companionOpen: boolean;
readonly documentVisible: boolean;
readonly frameRateLimit: GameplayFrameRateLimit;
}): Frameloop {
if (!options.documentVisible && !options.sharedOnline) return "demand";
const locallyPaused = options.worldReady
&& !options.graphicsRecovering
&& !options.sharedOnline
&& (options.paused || options.mapOpen || options.companionOpen);
if (locallyPaused) return "demand";
return options.frameRateLimit === "display" ? "always" : "never";
}