import { lazy, Suspense, useMemo, useState } from "react"; import { classById, FACTIONS, raceById } from "../app/characterCatalog"; import { selectedCharacter, useShellStore } from "../app/shellStore"; import { DualDisplayFrame } from "../components/DualDisplayFrame"; import { useMenuController, type MenuAction } from "../input/useMenuController"; import { CharacterPreview } from "./CharacterPreview"; import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel"; import { ControllerButton } from "./ControllerButton"; import { ContentManagerScreen } from "./ContentManagerScreen"; import { BrandMark, ControllerLegend, FrontSurface } from "./FrontSurface"; import { OnlineGroupPanel } from "./OnlineGroupPanel"; import { sessionCanUseShelvedGmTools, SHELVED_MODES_ENABLED } from "../app/releaseFeatures"; import { DungeonReportsScreen } from "./DungeonReportsScreen"; import { MainMenuCharacterEditor, type MainMenuEditor } from "./MainMenuCharacterEditor"; const BossCardCollectionScreen = lazy(() => import("./BossCardCollection")); const BossTestingScreen = lazy(() => import("./BossTestingScreen").then((module) => ({ default: module.BossTestingScreen }))); const GAME_MODES = [ { id: "dungeons", number: "01", name: "Dungeons", description: "Choose an expedition and lead your party into the depths.", available: true, }, ...(SHELVED_MODES_ENABLED ? [{ id: "manastorms" as const, number: "02", name: "Manastorms", description: "Chain random boss encounters as the storm grows stronger and more rewarding.", available: true, }, { id: "arenas" as const, number: "03", name: "Arenas", description: "Rated 2v2 and 3v3 combat against players or rating-matched opponents.", available: true, }] : []), { id: "trials", number: "04", name: "Trials", description: "A new mode is taking shape.", available: false, }, { id: "downloads", number: "05", name: "Downloads", description: "Install, update, or remove adventures that remain playable offline.", available: true, }, { id: "options", number: "06", name: "Options", description: "Main-menu settings are coming soon.", available: false, }, { id: "player-profile", number: "07", name: "Player Profile", description: "Profile details are coming soon.", available: false, }, ] as const; export function MainMenuScreen() { const [contentManagerOpen, setContentManagerOpen] = useState(false); const [reportsOpen, setReportsOpen] = useState(false); const [cardsOpen, setCardsOpen] = useState(false); const [bossTestingOpen, setBossTestingOpen] = useState(false); const [editor, setEditor] = useState(null); if (editor) return setEditor(null)} />; if (bossTestingOpen) return Opening Boss Testing…

}> setBossTestingOpen(false)} />
; if (cardsOpen) return Opening collection log…

}> setCardsOpen(false)} />
; if (contentManagerOpen) return setContentManagerOpen(false)} />; if (reportsOpen) return setReportsOpen(false)} />; return setContentManagerOpen(true)} onOpenReports={() => setReportsOpen(true)} onOpenCards={() => setCardsOpen(true)} onOpenBossTesting={() => setBossTestingOpen(true)} onOpenEditor={setEditor} />; } function MainMenuDestinations({ onOpenDownloads, onOpenReports, onOpenCards, onOpenBossTesting, onOpenEditor }: { readonly onOpenDownloads: () => void; readonly onOpenReports: () => void; readonly onOpenCards: () => void; readonly onOpenBossTesting: () => void; readonly onOpenEditor: (editor: MainMenuEditor) => void; }) { const activeCharacter = useShellStore((state) => state.activeCharacter); const rosterCharacter = useShellStore(selectedCharacter); const openDungeons = useShellStore((state) => state.openDungeons); const openManastorms = useShellStore((state) => state.openManastorms); const openArenas = useShellStore((state) => state.openArenas); const openClassHall = useShellStore((state) => state.openClassHall); const returnToCharacters = useShellStore((state) => state.returnToCharacters); const notice = useShellStore((state) => state.notice); const session = useShellStore((state) => state.session); const openGmAdmin = useShellStore((state) => state.openGmAdmin); const character = activeCharacter ?? rosterCharacter; const isRom = character?.categoryId === "rom"; const isGm = sessionCanUseShelvedGmTools(session); const actions = useMemo(() => [ { id: "mode-dungeons", run: openDungeons, neighbors: { right: "mode-manastorms", down: "back-to-roster" }, }, ...(SHELVED_MODES_ENABLED ? [{ id: "mode-manastorms", run: openManastorms, neighbors: { left: "mode-dungeons", down: "back-to-roster" }, }, { id: "mode-arenas", run: openArenas, neighbors: { left: "mode-manastorms", down: "back-to-roster" }, }] : []), { id: "mode-downloads", run: onOpenDownloads, neighbors: { left: SHELVED_MODES_ENABLED ? "mode-manastorms" : "mode-dungeons", down: "back-to-roster" }, }, { id: "back-to-roster", run: returnToCharacters, neighbors: { up: "mode-dungeons", left: "mode-dungeons" }, }, ...(character ? [ { id: "mode-class-hall", run: openClassHall }, { id: "mode-talents", run: () => onOpenEditor("talents") }, { id: "mode-keybinds", run: () => onOpenEditor("keybinds") }, { id: "mode-bindings", run: () => onOpenEditor("bindings") }, ] : []), ...(isGm ? [{ id: "mode-gm-tools", run: openGmAdmin }] : []), { id: "mode-reports", run: onOpenReports }, { id: "mode-cards", run: onOpenCards }, { id: "mode-boss-testing", run: onOpenBossTesting }, ], [character, isGm, onOpenBossTesting, onOpenCards, onOpenDownloads, onOpenEditor, onOpenReports, openArenas, openClassHall, openDungeons, openGmAdmin, openManastorms, returnToCharacters]); const controller = useMenuController(actions, { initialId: "mode-dungeons", onBack: returnToCharacters, }); const race = character ? raceById(character.raceId) : null; const characterClass = character ? classById(character.classId) : null; return (
Adventurer selected Choose your next journey
Change character

Main menu

Ways to play

Select a destination. More adventures will unlock here as they are built.

{notice &&

{notice}

}
} bottom={} /> ); }