import { useMemo, useState } from "react"; import { buildBossGuideCatalog } from "../game/bossGuide"; import { BOSS_GUIDE_OVERRIDES } from "../game/bossGuideOverrides"; import { DUNGEON_DEFINITIONS, dungeonDefinitionById } from "../game/dungeonRegistry"; import { useGameStore } from "../game/store"; import { useMenuController, type MenuAction } from "../input/useMenuController"; import { ControllerButton } from "./ControllerButton"; function BossGuideDialog() { const activeDungeonId = useGameStore((state) => state.activeDungeonId); const sessionRevision = useGameStore((state) => state.sessionRevision); const bossTest = useGameStore((state) => state.bossTest); const openOverlay = useGameStore((state) => state.openOverlay); const guideCatalog = useMemo(() => buildBossGuideCatalog( DUNGEON_DEFINITIONS.map((definition) => ( definition.id === activeDungeonId ? dungeonDefinitionById(activeDungeonId) ?? definition : definition )), BOSS_GUIDE_OVERRIDES, ), [activeDungeonId, sessionRevision]); const initialDungeon = guideCatalog.find((dungeon) => dungeon.id === (bossTest?.sourceDungeonId ?? activeDungeonId)) ?? guideCatalog[0]; const [selectedDungeonId, setSelectedDungeonId] = useState(initialDungeon?.id ?? ""); const selectedDungeon = guideCatalog.find((dungeon) => dungeon.id === selectedDungeonId) ?? initialDungeon; const [selectedBossId, setSelectedBossId] = useState(selectedDungeon?.bosses.find((boss) => boss.entityId === bossTest?.entityId)?.id ?? selectedDungeon?.bosses[0]?.id ?? ""); const selectedBoss = selectedDungeon?.bosses.find((boss) => boss.id === selectedBossId) ?? selectedDungeon?.bosses[0]; const close = () => openOverlay("pause"); const selectDungeon = (dungeonId: string) => { const dungeon = guideCatalog.find((candidate) => candidate.id === dungeonId); setSelectedDungeonId(dungeonId); setSelectedBossId(dungeon?.bosses[0]?.id ?? ""); }; const actions = useMemo(() => { const dungeonActions = guideCatalog.map((dungeon, index): MenuAction => ({ id: `boss-guide-dungeon:${dungeon.id}`, run: () => selectDungeon(dungeon.id), neighbors: { up: `boss-guide-dungeon:${guideCatalog[(index - 1 + guideCatalog.length) % guideCatalog.length].id}`, down: `boss-guide-dungeon:${guideCatalog[(index + 1) % guideCatalog.length].id}`, ...(dungeon.id === selectedDungeon?.id && dungeon.bosses.length ? { right: `boss-guide-boss:${dungeon.bosses[0].id}` } : {}), }, })); const bosses = selectedDungeon?.bosses ?? []; const bossActions = bosses.map((boss, index): MenuAction => ({ id: `boss-guide-boss:${boss.id}`, run: () => setSelectedBossId(boss.id), neighbors: { up: `boss-guide-boss:${bosses[(index - 1 + bosses.length) % bosses.length].id}`, down: `boss-guide-boss:${bosses[(index + 1) % bosses.length].id}`, left: `boss-guide-dungeon:${selectedDungeon!.id}`, }, })); return [...dungeonActions, ...bossActions]; }, [guideCatalog, selectedDungeon]); const controller = useMenuController(actions, { initialId: `boss-guide-dungeon:${selectedDungeon?.id ?? ""}`, onBack: close, }); return (

Encounter field manual

Boss Guide

{selectedDungeon?.bosses.length ?? 0} bosses in {selectedDungeon?.title ?? "this dungeon"}

{selectedBoss ? ( <>

{selectedDungeon?.title}

{selectedBoss.name}

{selectedBoss.title && selectedBoss.title !== selectedBoss.name ?

{selectedBoss.title}

: null}
{selectedBoss.mechanics.length ? (
    {selectedBoss.mechanics.map((mechanic, index) => (
  1. {index + 1}

    {mechanic.name}

    {mechanic.fidelityLabel}
    Trigger
    {mechanic.trigger}
    Timing
    {mechanic.timing}
    Target / effect
    {mechanic.targetAndEffect}
    Warning area
    {mechanic.telegraph}

    What to do{mechanic.counterplay}

    How to test it{mechanic.testInstruction}

  2. ))}
) : (
Basic attacks only

This boss has no special mechanics in the current runtime definition. There is no timer, health trigger, or warning area to verify yet.

)} ) : (
No boss selected

Select a dungeon with registered bosses to inspect its live mechanic definitions.

)}
↑↓ Browse←→ Change columnA / Enter SelectB / Esc Pause menu
); } export function BossGuidePanel() { const open = useGameStore((state) => state.overlay === "boss-guide"); return open ? : null; }