import { dungeonById } from "../game/dungeonCatalog"; import { activateDungeonSession } from "../game/dungeonSession"; import { useGameStore } from "../game/store"; import { classCanFillPartyRole, defaultPartyRoleForClass, PARTY_ROLE_LABELS, type PartyRole, } from "../game/partyRoles"; import { useShellStore } from "./shellStore"; import { registerDungeonShellRuntime, type DungeonEntrySettings } from "./dungeonShellRuntimeBridge"; import { touchCharacter } from "./accountRepository"; import { specializationById } from "../game/specializationCatalog"; import { dungeonContentIsUsable } from "../content/contentManager"; export function installDungeonShellRuntime(): void { registerDungeonShellRuntime({ enterDungeon }); } function enterDungeon(dungeonId: string, requestedRole?: PartyRole, settings?: DungeonEntrySettings): boolean { const dungeon = dungeonById(dungeonId); if (!dungeon || !dungeon.available) { useShellStore.setState({ notice: "That dungeon is not available yet." }); return false; } if (!dungeonContentIsUsable(dungeonId)) { useShellStore.setState({ notice: "Install this expedition from Downloads before entering." }); return false; } const { session, selectedCharacterId, characters } = useShellStore.getState(); if (!session || !selectedCharacterId) { useShellStore.setState({ notice: "Select or create a character first." }); return false; } const activeCharacter = touchCharacter(session.ownerId, selectedCharacterId) ?? characters.find((character) => character.id === selectedCharacterId) ?? null; if (!activeCharacter) { useShellStore.setState({ notice: "That character is no longer available." }); return false; } const savedSpecialization = specializationById(activeCharacter.specializationId); if (activeCharacter.categoryId !== "rom" && !savedSpecialization) { useShellStore.setState({ phase: "class-hall", notice: "Choose a specialization before entering activities." }); return false; } const playerRole = savedSpecialization?.role ?? requestedRole ?? defaultPartyRoleForClass(activeCharacter.classId); if (savedSpecialization && requestedRole && requestedRole !== savedSpecialization.role) { useShellStore.setState({ notice: `${savedSpecialization.displayName} is locked to ${PARTY_ROLE_LABELS[savedSpecialization.role]}.` }); return false; } if (!classCanFillPartyRole(activeCharacter.classId, playerRole)) { useShellStore.setState({ notice: `${activeCharacter.name}'s class cannot enter as ${PARTY_ROLE_LABELS[playerRole]}.`, }); return false; } const checkpoint = activeCharacter.dungeonRunCheckpoint; const resume = !settings?.freshRun && checkpoint?.dungeonId === dungeonId && checkpoint.run?.finishedAt == null && (settings?.difficultyId === undefined || checkpoint.difficultyId === settings.difficultyId) && (settings?.bossDifficulty === undefined || checkpoint.run?.bossDifficulty === settings.bossDifficulty) ? activeCharacter.dungeonRunCheckpoint : null; if (!activateDungeonSession(dungeonId, settings?.difficultyId ?? resume?.difficultyId, { bossDifficulty: settings?.bossDifficulty ?? resume?.run?.bossDifficulty, spawn: resume?.spawn, encounterSnapshots: resume?.encounters, runProgress: resume?.run, legacyResume: Boolean(resume && resume.run?.bossDifficulty === undefined), runId: settings?.runId, })) { useShellStore.setState({ notice: dungeon.availabilityReason ?? "That dungeon failed its latest validation gate.", }); return false; } if (settings?.raceStartBlocked) useGameStore.setState({ raceStartBlocked: true }); useGameStore.getState().setCompanionOpen(false); useShellStore.setState({ phase: "game", activeCharacter, activeDungeonId: dungeon.id, activeDungeonRole: playerRole, notice: "", }); return true; }