import { useEffect, useMemo, useState } from "react"; import { selectedCharacter, useShellStore } from "../app/shellStore"; import { useArenaStore } from "../game/arenaStore"; import { arenaAiProfile } from "../game/arenaRules"; import { ARENA_STAGES } from "../game/arenaStages"; import { romGearProfile, specializationById, specializationsForClass } from "../game/specializationCatalog"; import { DualDisplayFrame } from "../components/DualDisplayFrame"; import { ControllerLegend, FrontSurface } from "./FrontSurface"; import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel"; const MODES = [ { id: "offline-practice", name: "Offline Practice", detail: "Choose any arena and bot rating. Results never affect rating." }, { id: "rated-ai", name: "Rated AI", detail: "Fill both teams immediately with rating-matched opponents." }, { id: "ranked-pvp", name: "Ranked PvP", detail: "Match humans first; all open slots lock to bots after 30 seconds." }, ] as const; export function ArenaSelectScreen() { const character = useShellStore(selectedCharacter); const session = useShellStore((state) => state.session); const returnToMainMenu = useShellStore((state) => state.returnToMainMenu); const arena = useArenaStore(); const [loadoutName, setLoadoutName] = useState("Arena Loadout"); const specs = useMemo(() => { if (!character) return []; if (character.categoryId === "rom") return [romGearProfile(character.classId, "healer"), romGearProfile(character.classId, "damage")]; return specializationsForClass(character.classId).filter((spec) => spec.role !== "tank"); }, [character]); const fallbackSpec = specs.find((spec) => spec.id === character?.specializationId) ?? specs[0] ?? null; const [specializationId, setSpecializationId] = useState(fallbackSpec?.id ?? ""); const profile = arenaAiProfile(arena.botDifficulty); const randomStage = arena.stageId === "random"; const selectedStage = ARENA_STAGES.find((stage) => stage.id === arena.stageId) ?? ARENA_STAGES[0]; const selectedLoadout = arena.remote?.loadouts.find((loadout) => loadout.slot === arena.selectedLoadoutSlot) ?? null; const rating = arena.remote?.ratings[arena.bracket]; useEffect(() => { if (character) void arena.load(session, character); }, [character?.id, session?.accessToken]); useEffect(() => { if (!fallbackSpec) return; setSpecializationId(fallbackSpec.id); setLoadoutName(`${fallbackSpec.displayName} PvP`); }, [fallbackSpec?.id]); if (!character || !selectedStage) return null; const online = Boolean(session?.accessToken); const rated = arena.mode !== "offline-practice"; const nativeRuleset = character.categoryId === "coa" || character.categoryId === "rom" ? character.categoryId : "wow"; const stageHeading = rated ? "Random Rated Arena" : randomStage ? "Random Arena" : selectedStage.name; const queueLabel = arena.queue.status === "queued" ? "Leave queue" : arena.queue.status === "ready-check" ? "Ready" : arena.loading ? "Working..." : arena.mode === "offline-practice" ? "Start practice" : "Enter rated queue"; const primaryAction = async () => { if (arena.queue.status === "queued") { await arena.leave(session); return; } if (arena.queue.status === "ready-check" && session) { await arena.ready(session); return; } await arena.queueMatch(session, character); }; return (
+Arena Command

{character.name}{arena.bracket} / {rating ? `${rating.rating} ${rating.title}` : "Practice"}

Eight battlegrounds

{stageHeading}

validated
{ARENA_STAGES.map((stage, index) => ( ))}

{rated ? "Rated matches choose uniformly from the validated pool and avoid your immediately previous arena when possible." : randomStage ? "Practice will select one of the eight validated arenas when the match starts." : selectedStage.validation.notes[0]}

{arena.mode === "offline-practice" ? ( <>

Opponent calibration

{arena.botDifficulty} / {profile.title}

arena.configure({ botDifficulty: Number(event.target.value) })} />
Reaction
{profile.reactionMinSeconds.toFixed(2)}–{profile.reactionMaxSeconds.toFixed(2)}s
Healer kicks
{Math.round(profile.healerInterruptChance * 100)}%
Minimum error
{Math.round(profile.minimumErrorChance * 100)}%
) : ( <>

Level 80 PvP profile

{selectedLoadout?.name ?? "Automatic first loadout"}

{!online &&

Sign in to use rated queues. Practice remains fully offline.

} {online && (
)}

Gear and prestige are replaced by versioned arena templates. Cosmetics remain visible.

)} {arena.error &&

{arena.error}

}
)} bottom={} /> ); }