130 lines
6.0 KiB
TypeScript
130 lines
6.0 KiB
TypeScript
import { useEffect } from "react";
|
|
import "./ui/uiLayout.css";
|
|
import { DualDisplayFrame } from "./components/DualDisplayFrame";
|
|
import { installInput, releasePointerLock } from "./game/inputManager";
|
|
import { CombatBridge } from "./game/CombatBridge";
|
|
import { useCombatStore } from "./game/combatStore";
|
|
import { releaseDungeonGltfCache } from "./game/dungeonAssetCache";
|
|
import { clearDungeonRuntimeCaches } from "./game/dungeonRuntimeReset";
|
|
import { requireDungeonDefinition } from "./game/dungeonRegistry";
|
|
import { PartyBridge } from "./game/PartyBridge";
|
|
import { ManastormBridge } from "./game/ManastormBridge";
|
|
import { releaseActiveManastormStage } from "./game/manastormStageLoader";
|
|
import { lootCurrentManastormBoss } from "./game/manastormLoot";
|
|
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 { GmWorldEditor } from "./ui/GmWorldEditor";
|
|
import { GmReviewCard } from "./ui/GmReviewCard";
|
|
import { OnlineSessionBridge } from "./game/OnlineSessionBridge";
|
|
import { DungeonRunCheckpointBridge } from "./game/DungeonRunCheckpointBridge";
|
|
import { DungeonEncounterEffectBridge } from "./game/DungeonEncounterEffectBridge";
|
|
import { interactWithActiveDungeonObject } from "./game/dungeonWorldInteraction";
|
|
import { DungeonInteractionPrompt } from "./ui/DungeonInteractionPrompt";
|
|
import { UiLayoutEditor } from "./ui/UiLayoutEditor";
|
|
import { EditableHudElement } from "./ui/EditableHudElement";
|
|
import { activeUiLayout, currentUiDevice } from "./game/uiLayoutStore";
|
|
import { actionKeyForEvent, desktopSlotForKey } from "./game/uiLayout";
|
|
|
|
function InputBridge() {
|
|
const overlay = useGameStore((state) => state.overlay);
|
|
const companionOpen = useGameStore((state) => state.companionOpen);
|
|
|
|
useEffect(() => installInput({
|
|
desktopActionBarsActive: () => currentUiDevice() === "pc" && activeUiLayout().wowBars,
|
|
handleCustomKey: (event) => {
|
|
const game = useGameStore.getState();
|
|
if (game.overlay === "ui-layout") return !["Tab", "Enter", "Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.code);
|
|
if (currentUiDevice() !== "pc" || game.overlay || game.companionOpen || useCombatStore.getState().pendingGroundTarget) return false;
|
|
const button = desktopSlotForKey(activeUiLayout(), actionKeyForEvent(event));
|
|
if (!button) return false;
|
|
if (!event.repeat && button.abilityId) useCombatStore.getState().castAbility(button.abilityId, game.playerPosition, Date.now());
|
|
game.setInputMode("keyboard");
|
|
return true;
|
|
},
|
|
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() || lootCurrentManastormBoss(),
|
|
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;
|
|
},
|
|
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(() => () => {
|
|
const activeDungeon = requireDungeonDefinition(useGameStore.getState().activeDungeonId);
|
|
clearDungeonRuntimeCaches();
|
|
releaseDungeonGltfCache(activeDungeon);
|
|
releaseActiveManastormStage();
|
|
}, []);
|
|
return (
|
|
<DualDisplayFrame
|
|
contextLabel="Tactical"
|
|
onActiveSurfaceChange={(surface) => useGameStore.getState().setCompanionOpen(surface === "bottom")}
|
|
top={
|
|
<main className="game-shell">
|
|
<InputBridge />
|
|
<OnlineSessionBridge />
|
|
<DungeonEncounterEffectBridge />
|
|
<DungeonRunCheckpointBridge />
|
|
<CombatBridge />
|
|
<PartyBridge />
|
|
<ManastormBridge />
|
|
<SceneErrorBoundary><GameScene /></SceneErrorBoundary>
|
|
<Hud />
|
|
<EditableHudElement id="interaction"><DungeonInteractionPrompt /></EditableHudElement>
|
|
<MapPanel />
|
|
<GameplayPanels />
|
|
<PauseMenu />
|
|
<UiLayoutEditor />
|
|
<GmReviewCard />
|
|
<GmWorldEditor />
|
|
</main>
|
|
}
|
|
bottom={<LocalCompanionPanel />}
|
|
/>
|
|
);
|
|
}
|