168 lines
8.5 KiB
TypeScript
168 lines
8.5 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useShellStore } from "./app/shellStore";
|
|
import "./ui/uiLayout.css";
|
|
import { DualDisplayFrame } from "./components/DualDisplayFrame";
|
|
import { installInput, releasePointerLock } from "./game/inputManager";
|
|
import { CombatBridge } from "./game/CombatBridge";
|
|
import { connectDpsMeter } from "./game/dpsMeterStore";
|
|
import { useCombatStore } from "./game/combatStore";
|
|
import { releaseDungeonGltfCache } from "./game/dungeonAssetCache";
|
|
import { clearDungeonRuntimeCaches } from "./game/dungeonRuntimeReset";
|
|
import { bossTestingSourceDungeonId, clearBossTestingDungeons, requireDungeonDefinition } from "./game/dungeonRegistry";
|
|
import { BossTestingBridge } from "./game/BossTestingBridge";
|
|
import { PartyBridge } from "./game/PartyBridge";
|
|
import { usePartyStore } from "./game/partyStore";
|
|
import { useGameStore } from "./game/store";
|
|
import { GameScene } from "./scene/GameScene";
|
|
import { LocalCompanionPanel } from "./ui/CompanionPanel";
|
|
import { Hud } from "./ui/Hud";
|
|
import { GameplayPanels } from "./ui/GameplayPanels";
|
|
import { MapPanel } from "./ui/MapPanel";
|
|
import { PauseMenu } from "./ui/PauseMenu";
|
|
import { SceneErrorBoundary } from "./ui/SceneErrorBoundary";
|
|
import { OnlineSessionBridge } from "./game/OnlineSessionBridge";
|
|
import { DungeonRunCheckpointBridge } from "./game/DungeonRunCheckpointBridge";
|
|
import { DungeonRunBridge } from "./game/DungeonRunBridge";
|
|
import { DungeonRaceBridge } from "./game/DungeonRaceBridge";
|
|
import { DungeonRaceHud } from "./ui/DungeonRaceHud";
|
|
import { DungeonRunResults } from "./ui/DungeonRunResults";
|
|
import { DungeonEncounterEffectBridge } from "./game/DungeonEncounterEffectBridge";
|
|
import { interactWithActiveDungeonObject } from "./game/dungeonWorldInteraction";
|
|
import { UiLayoutEditor } from "./ui/UiLayoutEditor";
|
|
import { activeUiLayout, currentUiDevice } from "./game/uiLayoutStore";
|
|
import { useUiLayoutStore } from "./game/uiLayoutStore";
|
|
import { profileInputBindings } from "./game/keybindPreferences";
|
|
import { KeybindsPanel } from "./ui/KeybindsPanel";
|
|
import { lootShelvedBoss, releaseShelvedGameRuntime, ShelvedGameBridges } from "@shelvedGameRuntime";
|
|
import { useDungeonSimulationStore } from "./game/dungeonSimulationStore";
|
|
import { DungeonSimulationHud } from "./ui/DungeonSimulationHud";
|
|
import { DungeonBossIntro } from "./ui/DungeonBossIntro";
|
|
import { useDungeonRouteStore } from "./game/dungeonRouteStore";
|
|
import { installCombatClock } from "./game/combatClock";
|
|
import { dungeonSimulationNow } from "./game/dungeonSimulationStore";
|
|
import { useDungeonRaceStore } from "./app/dungeonRaceStore";
|
|
import { BiteSizedEditor } from "./ui/BiteSizedEditor";
|
|
import { useBiteSizedLayoutsStore } from "./game/biteSizedLayoutsStore";
|
|
|
|
function InputBridge() {
|
|
const overlay = useGameStore((state) => state.overlay);
|
|
const companionOpen = useGameStore((state) => state.companionOpen);
|
|
|
|
useEffect(() => installInput({
|
|
desktopActionBarsActive: () => currentUiDevice() === "pc" && activeUiLayout().wowBars,
|
|
inputSuspended: () => useDungeonSimulationStore.getState().sessionMode !== "play"
|
|
|| useBiteSizedLayoutsStore.getState().panelFocused
|
|
|| useGameStore.getState().dungeonIntroActive
|
|
|| useGameStore.getState().raceStartBlocked
|
|
|| ["ui-layout", "keybinds", "dungeon-results", "boss-test-review", "report-issue"].includes(useGameStore.getState().overlay ?? ""),
|
|
getBindings: (device) => {
|
|
const preferences = useUiLayoutStore.getState().preferences;
|
|
const bindings = profileInputBindings(preferences, device);
|
|
for (let b = activeUiLayout().barCount; b < 5; b++) for (let s = 0; s < 12; s++) bindings[`bar:${b}:${s}`] = null;
|
|
return bindings;
|
|
},
|
|
castDesktopSlot: (bar, slot) => {
|
|
const game = useGameStore.getState(); const button = activeUiLayout().bars[bar]?.slots[slot];
|
|
if (button?.abilityId) useCombatStore.getState().castAbility(button.abilityId, game.playerPosition, Date.now());
|
|
},
|
|
togglePanel: (panel) => useGameStore.getState().toggleOverlay(panel),
|
|
togglePause: () => useGameStore.getState().togglePause(),
|
|
toggleMap: () => useGameStore.getState().toggleMap(),
|
|
toggleInventory: () => useGameStore.getState().toggleOverlay("inventory"),
|
|
toggleSpellbook: () => useGameStore.getState().toggleOverlay("spellbook"),
|
|
toggleTalents: () => useGameStore.getState().toggleOverlay("talents"),
|
|
lootActiveBoss: () => interactWithActiveDungeonObject() || lootShelvedBoss(),
|
|
castActionBinding: (layer, control) => {
|
|
const game = useGameStore.getState();
|
|
useCombatStore.getState().castActionBinding(layer, control, game.playerPosition, Date.now());
|
|
},
|
|
cycleTarget: (direction) => {
|
|
const game = useGameStore.getState();
|
|
usePartyStore.getState().clearSelection();
|
|
useCombatStore.getState().cycleTarget(direction, game.playerPosition);
|
|
},
|
|
cyclePartyTarget: (direction) => {
|
|
useCombatStore.getState().clearTarget();
|
|
usePartyStore.getState().cycleMember(direction);
|
|
},
|
|
setPartyCommand: (command) => usePartyStore.getState().setCommand(command),
|
|
recallParty: () => usePartyStore.getState().recallParty(),
|
|
clearTarget: () => {
|
|
useCombatStore.getState().clearTarget();
|
|
usePartyStore.getState().clearSelection();
|
|
},
|
|
groundTargetActive: () => useCombatStore.getState().pendingGroundTarget !== null,
|
|
confirmGroundTarget: () => { useCombatStore.getState().confirmGroundTarget(); },
|
|
cancelGroundTarget: () => useCombatStore.getState().cancelGroundTarget(),
|
|
gameplayActionsBlocked: () => {
|
|
const game = useGameStore.getState();
|
|
return game.overlay !== null || game.companionOpen || useBiteSizedLayoutsStore.getState().editorMode === "edit";
|
|
},
|
|
setCameraLookActive: (active) => useGameStore.getState().setCameraLookActive(active),
|
|
setInputMode: (mode) => useGameStore.getState().setInputMode(mode),
|
|
setActionLayer: (layer) => useGameStore.getState().setActionLayer(layer),
|
|
}), []);
|
|
|
|
useEffect(() => () => releasePointerLock(), []);
|
|
useEffect(() => {
|
|
if (overlay || companionOpen) releasePointerLock();
|
|
}, [companionOpen, overlay]);
|
|
return null;
|
|
}
|
|
|
|
export default function GameRuntime() {
|
|
useEffect(connectDpsMeter, []);
|
|
useEffect(() => installCombatClock(dungeonSimulationNow), []);
|
|
useEffect(() => { void useDungeonRouteStore.getState().refresh(); }, []);
|
|
const editingControls = useGameStore((state) => state.overlay === "keybinds" || state.overlay === "ui-layout");
|
|
const dungeonSimulation = useDungeonSimulationStore((state) => state.sessionMode !== "play");
|
|
useEffect(() => () => {
|
|
const race = useDungeonRaceStore.getState();
|
|
if (race.inGame && useShellStore.getState().phase !== "game") void race.leave();
|
|
const activeDungeon = requireDungeonDefinition(useGameStore.getState().activeDungeonId);
|
|
clearDungeonRuntimeCaches();
|
|
releaseDungeonGltfCache(activeDungeon);
|
|
releaseShelvedGameRuntime();
|
|
const sourceDungeonId = bossTestingSourceDungeonId(activeDungeon.id);
|
|
if (sourceDungeonId) {
|
|
useGameStore.setState({ activeDungeonId: sourceDungeonId, bossTest: null, gmTestMode: false });
|
|
useShellStore.setState({ activeDungeonId: sourceDungeonId });
|
|
}
|
|
clearBossTestingDungeons();
|
|
if (useShellStore.getState().phase !== "game") useBiteSizedLayoutsStore.setState({ editorMode: null, panelFocused: false });
|
|
}, []);
|
|
return (
|
|
<DualDisplayFrame
|
|
contextLabel="Tactical"
|
|
companionDisabled={editingControls}
|
|
onActiveSurfaceChange={(surface) => useGameStore.getState().setCompanionOpen(surface === "bottom")}
|
|
top={
|
|
<main className="game-shell">
|
|
<InputBridge />
|
|
<OnlineSessionBridge />
|
|
<DungeonEncounterEffectBridge />
|
|
<DungeonRunCheckpointBridge />
|
|
<CombatBridge />
|
|
<PartyBridge />
|
|
<DungeonRunBridge />
|
|
<DungeonRaceBridge />
|
|
<BossTestingBridge />
|
|
<ShelvedGameBridges />
|
|
<SceneErrorBoundary><GameScene /></SceneErrorBoundary>
|
|
{dungeonSimulation ? <DungeonSimulationHud /> : <Hud />}
|
|
{!dungeonSimulation && <DungeonRaceHud region="overlays" />}
|
|
{!dungeonSimulation && <DungeonBossIntro />}
|
|
{!dungeonSimulation && <MapPanel />}
|
|
{!dungeonSimulation && <GameplayPanels />}
|
|
{!dungeonSimulation && <PauseMenu />}
|
|
{!dungeonSimulation && <DungeonRunResults />}
|
|
{!dungeonSimulation && <UiLayoutEditor />}
|
|
{!dungeonSimulation && <KeybindsPanel />}
|
|
<BiteSizedEditor inGame />
|
|
</main>
|
|
}
|
|
bottom={<LocalCompanionPanel />}
|
|
/>
|
|
);
|
|
}
|