Files
healer-man/src/game/DungeonRunBridge.tsx
T
2026-09-13 11:07:56 -04:00

67 lines
3.4 KiB
TypeScript

import { useEffect } from "react";
import { useShellStore } from "../app/shellStore";
import { useOnlineSessionStore } from "../app/onlineSessionStore";
import { useOnlineGroupStore } from "../app/onlineGroupStore";
import { activeDungeonCompletedObjectiveIds, subscribeDungeonEncounterSnapshots } from "./dungeonEncounterRuntime";
import { useCombatStore } from "./combatStore";
import { usePartyStore } from "./partyStore";
import { useGameStore } from "./store";
import { useDungeonRunStore } from "./dungeonRunStore";
import type { DungeonRunMember } from "./dungeonRunTypes";
function currentRunParty(): readonly DungeonRunMember[] {
const shell = useShellStore.getState();
const character = shell.activeCharacter;
if (!character) return [];
const members: DungeonRunMember[] = [{ name: character.name, classId: character.classId,
level: useCombatStore.getState().level, role: shell.activeDungeonRole, npc: false,
...(character.secondaryClassId ? { secondaryClassId: character.secondaryClassId } : {}) }];
const online = useOnlineSessionStore.getState().snapshot;
for (const player of online?.players ?? []) {
if (player.accountId === shell.session?.ownerId || !player.character) continue;
members.push({ name: player.character.name, classId: player.character.classId,
level: player.character.level, role: player.role ?? "damage", npc: false });
}
for (const member of usePartyStore.getState().members) {
members.push({ name: member.name, classId: member.classId, level: member.level, role: member.role, npc: true });
}
return members.slice(0, 5);
}
/** Wall-clock run timing survives wipes, pauses, checkpoints, and renderer retries. */
export function DungeonRunBridge() {
useEffect(() => {
let announcedId: string | null = null;
const advance = () => {
const game = useGameStore.getState();
const shell = useShellStore.getState();
if (shell.phase !== "game" || game.gameMode !== "dungeon" || game.gmTestMode
|| !["ready", "proxy"].includes(game.assetStatus)) return;
const run = useDungeonRunStore.getState();
if (run.progress?.dungeonId !== game.activeDungeonId) return;
const party = currentRunParty();
const activity = useOnlineGroupStore.getState().activeActivity;
if (activity?.type === "dungeon" && activity.contentId === game.activeDungeonId
&& run.progress.finishedAt === null && run.progress.id !== activity.id) {
useDungeonRunStore.setState({ progress: { ...run.progress, id: activity.id,
startedAt: activity.startedAt, party } });
}
run.start(Date.now(), party);
run.advance(activeDungeonCompletedObjectiveIds(game.activeDungeonId),
Object.values(useCombatStore.getState().mobs).filter((mob) => mob.dead && !mob.despawned).map((mob) => mob.id),
Date.now(), party);
const progress = useDungeonRunStore.getState().progress;
if (progress?.finishedAt !== null && progress && announcedId !== progress.id) {
announcedId = progress.id;
useGameStore.getState().openOverlay("dungeon-results");
}
};
const unsubscribeEncounters = subscribeDungeonEncounterSnapshots(advance);
const unsubscribeGame = useGameStore.subscribe(advance);
const interval = setInterval(advance, 250);
advance();
return () => { unsubscribeEncounters(); unsubscribeGame(); clearInterval(interval); };
}, []);
return null;
}