import { useDungeonSimulationStore } from "../game/dungeonSimulationStore"; import { dungeonRouteDefinition } from "../game/dungeonRoutes"; import type { DungeonSimulationSpeed } from "../game/dungeonSimulationTypes"; import { finalizeDungeonSimulation } from "../scene/DungeonSimulationSceneBridge"; import { useShellStore } from "../app/shellStore"; import { classById } from "../app/characterCatalog"; import { PARTY_ROLE_LABELS, usePartyStore } from "../game/partyStore"; const SPEEDS: readonly DungeonSimulationSpeed[] = [0.5, 1, 2, 4]; function formatTime(elapsedMs: number): string { const seconds = Math.floor(elapsedMs / 1_000); return `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}.${Math.floor((elapsedMs % 1_000) / 100)}`; } function downloadReport(): void { const simulation = useDungeonSimulationStore.getState(); const report = simulation.replay?.report; if (!report) return; const url = URL.createObjectURL(new Blob([JSON.stringify(report, null, 2)], { type: "application/json" })); const anchor = document.createElement("a"); anchor.href = url; anchor.download = `${report.dungeonId}-${report.seed}-SimulationReportV1.json`; anchor.click(); URL.revokeObjectURL(url); } export function DungeonSimulationHud() { const simulation = useDungeonSimulationStore(); const partyMembers = usePartyStore((state) => state.members); const route = simulation.dungeonId ? dungeonRouteDefinition(simulation.dungeonId, simulation.routeId ?? "primary") : null; const leg = route?.legs.find((candidate) => candidate.objectiveId === simulation.objectiveId) ?? null; const replayDuration = simulation.replay?.durationMs ?? 0; const fallbackMember = partyMembers.find((member) => member.health > 0) ?? partyMembers[0] ?? null; const spectatedMember = partyMembers.find((member) => member.id === simulation.spectatedMemberId) ?? fallbackMember; const returnToGmTools = () => { useDungeonSimulationStore.getState().leave(); useShellStore.getState().openGmAdmin(); }; return (