import { lazy, Suspense, useMemo } from "react"; import { classById } from "../app/characterCatalog"; import { useShellStore } from "../app/shellStore"; import { useCombatStore } from "../game/combatStore"; import { abilityAtLevel, abilityRankForLevel, abilityTalentNodeId, learnedAbilityRank, } from "../game/abilityCatalog"; import { useGameStore } from "../game/store"; import { OptionsPanel } from "./OptionsPanel"; import { AbilityBindingsPanel } from "./AbilityBindingsPanel"; import { SpellbookPanel } from "./SpellbookPanel"; import { EquipmentPanel } from "./EquipmentPanel"; import { RomSkillsPanel } from "./RomSkillsPanel"; import { getLoadedTalentGameplayPanel, loadTalentGameplayPanel } from "./talentGameplayResources"; import { getLoadedBossGuidePanel, getLoadedPartyManagementPanel, loadBossGuidePanel, loadPartyManagementPanel, } from "./gameplayResources"; const PartyManagementPanel = lazy(() => loadPartyManagementPanel().then((module) => ({ default: module.PartyManagementPanel }))); const BossGuidePanel = lazy(() => loadBossGuidePanel().then((module) => ({ default: module.BossGuidePanel }))); const TalentGameplayPanel = lazy(loadTalentGameplayPanel); const BossCardCollectionScreen = lazy(() => import("./BossCardCollection")); function TalentsPanelLoading() { const openOverlay = useGameStore((state) => state.openOverlay); return (

Specialization

Talents

Preparing talent trees…

); } /** Maps the data-driven combat catalogs into the full-screen game panels. */ export function GameplayPanels() { const classId = useCombatStore((state) => state.classId); const level = useCombatStore((state) => state.level); const abilities = useCombatStore((state) => state.abilities); const talentRanks = useCombatStore((state) => state.talentRanks); const settings = useCombatStore((state) => state.settings); const presentationSettings = useShellStore((state) => state.combatPresentationSettings); const overlay = useGameStore((state) => state.overlay); const classDefinition = classById(classId); const PreloadedTalentGameplayPanel = getLoadedTalentGameplayPanel(); const PreloadedPartyManagementPanel = getLoadedPartyManagementPanel(); const PreloadedBossGuidePanel = getLoadedBossGuidePanel(); const spellbookAbilities = useMemo(() => [...abilities] .sort((left, right) => left.unlockLevel - right.unlockLevel) .map((catalogAbility) => { const learnedTalentRank = learnedAbilityRank(catalogAbility, talentRanks); const ability = abilityAtLevel(catalogAbility, level, learnedTalentRank); const activeRank = abilityRankForLevel(catalogAbility, level, learnedTalentRank); return { id: ability.id, name: ability.name, description: ability.description, level: ability.unlockLevel, color: classDefinition.color, sigil: ability.sigil, iconUrl: ability.icon, school: classDefinition.name, resourceCost: ability.cost.amount, resourceLabel: useCombatStore.getState().resourceName, cooldownSeconds: ability.cooldownMs / 1_000, castSeconds: ability.castTimeMs / 1_000, range: ability.range.max, known: !abilityTalentNodeId(catalogAbility) || Boolean(learnedTalentRank), talentLocked: Boolean(abilityTalentNodeId(catalogAbility)), passive: ability.passive, rank: ability.source === "runewaker" ? undefined : activeRank?.rank, rankCount: ability.source === "runewaker" ? undefined : catalogAbility.ranks?.length, ranks: ability.source === "runewaker" ? undefined : catalogAbility.ranks?.map((rank) => ({ rank: rank.rank, level: rank.level, spellId: rank.spellId, description: rank.description, })), }; }), [abilities, classDefinition.color, classDefinition.name, level, talentRanks]); const castAbility = (abilityId: string) => { const game = useGameStore.getState(); game.closeOverlay(); useCombatStore.getState().castAbility(abilityId, game.playerPosition, Date.now()); }; return ( <> {overlay === "boss-cards" ? : null} {overlay === "party" ? PreloadedPartyManagementPanel ? : : null} {overlay === "boss-guide" ? PreloadedBossGuidePanel ? : : null} {overlay === "talents" ? classId.startsWith("rom-") ? : PreloadedTalentGameplayPanel ? : }> : null} useCombatStore.getState().updateSettings(patch)} onReset={() => useCombatStore.getState().resetSettings()} presentationSettings={presentationSettings} onPresentationChange={(patch) => useShellStore.getState().updateCombatPresentationSettings(patch)} onPresentationReset={() => useShellStore.getState().resetCombatPresentationSettings()} /> ); }