Files
healer-man/src/game/dungeonSession.ts
T
2026-09-01 14:35:56 -04:00

101 lines
3.5 KiB
TypeScript

import { useCombatStore } from "./combatStore";
import { releaseDungeonGltfCache } from "./dungeonAssetCache";
import { clearDungeonRuntimeCaches } from "./dungeonRuntimeReset";
import { clearMobRuntimeRegistry } from "./mobRuntimeRegistry";
import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry";
import { resetPartyRuntimeClock } from "./partyRuntime";
import { usePartyStore } from "./partyStore";
import { useGameStore, type GameMode } from "./store";
import { dungeonCanEnter, dungeonDefinitionById, type DungeonId } from "./dungeonRegistry";
import type { WorldSpawn } from "./dungeonTypes";
import { useManastormAdminStore } from "./manastormAdminStore";
import type { GmReviewTarget } from "./gmContentReview";
export interface DungeonSessionOptions {
readonly gameMode?: GameMode;
readonly spawn?: WorldSpawn;
readonly gmTestMode?: boolean;
readonly gmReviewTarget?: GmReviewTarget | null;
/** Used by in-game encounter transitions so character progression and inventory survive. */
readonly preserveCharacter?: boolean;
}
/**
* The single dungeon-switch boundary. It deliberately clears renderer-facing
* registries before publishing the new active id, preventing stale positions,
* targets, routes, or prepared animation templates from crossing instances.
*/
export function activateDungeonSession(
dungeonId: string,
difficultyId?: string,
options: DungeonSessionOptions = {},
): boolean {
const definition = dungeonDefinitionById(dungeonId);
if (!definition || !dungeonCanEnter(dungeonId)) {
return false;
}
const previousDefinition = dungeonDefinitionById(useGameStore.getState().activeDungeonId);
if (previousDefinition) releaseDungeonGltfCache(previousDefinition);
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
clearDungeonRuntimeCaches();
resetPartyRuntimeClock();
if (options.preserveCharacter) {
useCombatStore.getState().resetEncounter();
const combat = useCombatStore.getState();
useCombatStore.setState({
mobs: {},
selectedTargetId: null,
activeCast: null,
effects: [],
comboPoints: 0,
feedback: null,
});
usePartyStore.getState().resetCombat(combat.level, combat.maxHealth);
} else {
useCombatStore.setState({
mobs: {},
selectedTargetId: null,
activeCast: null,
effects: [],
comboPoints: 0,
feedback: null,
});
usePartyStore.setState({
seed: null,
members: [],
command: "defend",
selectedMemberId: null,
selfSelected: false,
routeBlocked: false,
activeMobId: null,
objectiveBossId: null,
memberTargetIds: {},
});
}
useGameStore.getState().activateDungeon(dungeonId as DungeonId, difficultyId, {
gameMode: options.gameMode,
spawn: options.spawn ?? useManastormAdminStore.getState().config.dungeonSpawns[dungeonId],
gmTestMode: options.gmTestMode,
gmReviewTarget: options.gmReviewTarget,
});
useCombatStore.getState().setPlayerPosition(useGameStore.getState().playerPosition);
return true;
}
/**
* Rebuilds the current encounter after an asset/render failure without
* discarding the selected character, party composition, or run progression.
*/
export function restartActiveDungeonSession(): boolean {
const game = useGameStore.getState();
return activateDungeonSession(game.activeDungeonId, game.activeDifficultyId, {
gameMode: game.gameMode,
spawn: game.activeSpawn,
gmTestMode: game.gmTestMode,
gmReviewTarget: game.activeGmReviewTarget,
preserveCharacter: true,
});
}