Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 19:27:37 -04:00
1 parent 97ee0c13f9
commit f864b5aea7
20 files changed
+468 -211

No files matched your search

+127 -140
View File
@@ -106,6 +106,7 @@ function MainMenuDestinations({ onOpenDownloads, onOpenReports, onOpenCards, onO
readonly onOpenRandomizedBiteSized: () => void;
readonly onOpenEditor: (editor: MainMenuEditor) => void;
}) {
const [gmMenuOpen, setGmMenuOpen] = useState(false);
const activeCharacter = useShellStore((state) => state.activeCharacter);
const rosterCharacter = useShellStore(selectedCharacter);
const openDungeons = useShellStore((state) => state.openDungeons);
@@ -120,35 +121,23 @@ function MainMenuDestinations({ onOpenDownloads, onOpenReports, onOpenCards, onO
const isRom = character?.categoryId === "rom";
const isGm = sessionCanUseGmTools(session);
const actions = useMemo<MenuAction[]>(() => [
const actions = useMemo<MenuAction[]>(() => gmMenuOpen ? [
...(isGm ? [{ id: "gm-menu-gm-tools", run: openGmAdmin, neighbors: { up: "gm-menu-close" } }] : []),
{ id: "gm-menu-boss-testing", run: onOpenBossTesting, ...(!isGm ? { neighbors: { up: "gm-menu-close" } } : {}) },
{ id: "gm-menu-companion-readiness", run: onOpenReadiness },
...(isGm ? [{ id: "gm-menu-balance-lab", run: onOpenBalanceLab }] : []),
{ id: "gm-menu-reports", run: onOpenReports },
{ id: "gm-menu-close", run: () => setGmMenuOpen(false), neighbors: { down: isGm ? "gm-menu-gm-tools" : "gm-menu-boss-testing" } },
] : [
{
id: "mode-dungeons",
run: openDungeons,
neighbors: { right: "mode-randomized-bite-sized", down: "back-to-roster" },
neighbors: { up: "back-to-roster", right: "mode-randomized-bite-sized" },
},
{
id: "mode-randomized-bite-sized",
run: onOpenRandomizedBiteSized,
neighbors: { left: "mode-dungeons", right: SHELVED_MODES_ENABLED ? "mode-manastorms" : "mode-downloads", down: "back-to-roster" },
},
...(SHELVED_MODES_ENABLED ? [{
id: "mode-manastorms",
run: openManastorms,
neighbors: { left: "mode-randomized-bite-sized", 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" },
neighbors: { up: "back-to-roster", left: "mode-dungeons" },
},
...(character ? [
{ id: "mode-class-hall", run: openClassHall },
@@ -156,23 +145,59 @@ function MainMenuDestinations({ onOpenDownloads, onOpenReports, onOpenCards, onO
{ id: "mode-keybinds", run: () => onOpenEditor("keybinds") },
{ id: "mode-bindings", run: () => onOpenEditor("bindings") },
] : []),
...(isGm ? [
{ id: "mode-gm-tools", run: openGmAdmin },
{ id: "mode-balance-lab", run: onOpenBalanceLab },
] : []),
{ id: "mode-reports", run: onOpenReports },
{ id: "mode-cards", run: onOpenCards },
{ id: "mode-boss-testing", run: onOpenBossTesting },
{ id: "mode-companion-readiness", run: onOpenReadiness },
], [character, isGm, onOpenBalanceLab, onOpenBossTesting, onOpenCards, onOpenDownloads, onOpenEditor, onOpenRandomizedBiteSized, onOpenReadiness, onOpenReports, openArenas, openClassHall, openDungeons, openGmAdmin, openManastorms, returnToCharacters]);
{ id: "mode-gm-menu", run: () => setGmMenuOpen(true) },
...(SHELVED_MODES_ENABLED ? [
{ id: "mode-manastorms", run: openManastorms },
{ id: "mode-arenas", run: openArenas },
] : []),
{ id: "mode-downloads", run: onOpenDownloads },
{ id: "back-to-roster", run: returnToCharacters, neighbors: { down: "mode-dungeons" } },
], [character, gmMenuOpen, isGm, onOpenBalanceLab, onOpenBossTesting, onOpenCards, onOpenDownloads, onOpenEditor, onOpenRandomizedBiteSized, onOpenReadiness, onOpenReports, openArenas, openClassHall, openDungeons, openGmAdmin, openManastorms, returnToCharacters]);
const controller = useMenuController(actions, {
initialId: "mode-dungeons",
onBack: returnToCharacters,
columns: 2,
initialId: gmMenuOpen ? (isGm ? "gm-menu-gm-tools" : "gm-menu-boss-testing") : "mode-dungeons",
onBack: gmMenuOpen ? () => setGmMenuOpen(false) : returnToCharacters,
});
const characterClass = character ? classById(character.classId) : null;
const specialization = specializationById(character?.specializationId);
const secondaryClass = character?.secondaryClassId ? classById(character.secondaryClassId) : null;
const renderGameMode = (mode: (typeof GAME_MODES)[number]) => mode.available ? (
<ControllerButton
key={mode.id}
controlId={`mode-${mode.id}`}
selectedId={controller.selectedId}
select={controller.select}
className="front-button main-menu-mode main-menu-mode--available"
type="button"
onClick={mode.id === "manastorms"
? openManastorms
: mode.id === "arenas"
? openArenas
: mode.id === "downloads"
? onOpenDownloads
: mode.id === "randomized-bite-sized"
? onOpenRandomizedBiteSized
: openDungeons}
>
<b aria-hidden="true">{mode.number}</b>
<span><strong>{mode.name}</strong><small>{mode.description}</small></span>
<em>Available</em>
</ControllerButton>
) : (
<button
key={mode.id}
className="front-button main-menu-mode main-menu-mode--coming-soon"
type="button"
disabled
aria-label={`${mode.name}, coming soon`}
>
<b aria-hidden="true">{mode.number}</b>
<span><strong>{mode.name}</strong><small>{mode.description}</small></span>
<em>Coming soon</em>
</button>
);
return (
<DualDisplayFrame
@@ -214,120 +239,82 @@ function MainMenuDestinations({ onOpenDownloads, onOpenReports, onOpenCards, onO
<main className="main-menu-modes">
<nav className="main-menu-mode-grid" aria-label="Main menu destinations">
<ControllerButton controlId="mode-companion-readiness" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={onOpenReadiness}>
<b aria-hidden="true">CR</b><span><strong>Companion Readiness</strong><small>Review the certified NPC specialization pool and Playerbots identities.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-boss-testing" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={onOpenBossTesting}>
<b aria-hidden="true">BT</b><span><strong>Boss Testing</strong><small>Choose a boss or fight a random encounter in an empty testing room.</small></span><em>Open</em>
</ControllerButton>
{character && <>
<ControllerButton controlId="mode-talents" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("talents")}>
<b aria-hidden="true">T</b><span><strong>{isRom ? "Skills" : "Talents"}</strong><small>{isRom ? "Review your primary, secondary, and elite skills." : "View your talent trees, spend points, or reset your build."}</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-keybinds" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("keybinds")}>
<b aria-hidden="true">K</b><span><strong>Keybinds</strong><small>View and change keyboard or controller bindings.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-bindings" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("bindings")}>
<b aria-hidden="true">A</b><span><strong>Button assignments</strong><small>Choose the abilities on your action bars and controller buttons.</small></span><em>Open</em>
</ControllerButton>
</>}
<ControllerButton controlId="mode-cards" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={onOpenCards}>
<b aria-hidden="true">◆</b><span><strong>Boss Cards</strong><small>Track your collection, rarities, and copies from every dungeon.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-reports" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={onOpenReports}>
<b aria-hidden="true">!</b>
<span><strong>View Reports</strong><small>Browse dungeon issues and follow their status.</small></span>
<em>Open</em>
</ControllerButton>
{isGm && (
<div className="main-menu-gm-stack">
<ControllerButton
controlId="mode-gm-tools"
selectedId={controller.selectedId}
select={controller.select}
className="front-button main-menu-mode main-menu-mode--available main-menu-mode--gm"
type="button"
onClick={openGmAdmin}
>
<b aria-hidden="true">GM</b>
<span><strong>GM Tools</strong><small>Run dungeon route simulations, inspect findings, and review recordings.</small></span>
<em>Authorized</em>
</ControllerButton>
<ControllerButton
controlId="mode-balance-lab"
selectedId={controller.selectedId}
select={controller.select}
className="front-button main-menu-mode main-menu-mode--available main-menu-mode--gm"
type="button"
onClick={onOpenBalanceLab}
>
<b aria-hidden="true">BL</b>
<span><strong>Balance Lab</strong><small>Queue automatic party and boss simulations on the game server.</small></span>
<em>Server</em>
</ControllerButton>
<section className="main-menu-card main-menu-card--adventures" aria-labelledby="main-menu-adventures-title">
<header><span>01</span><strong id="main-menu-adventures-title">Adventures</strong></header>
<div className="main-menu-card__grid">
{GAME_MODES.filter((mode) => mode.id === "dungeons" || mode.id === "randomized-bite-sized").map(renderGameMode)}
</div>
)}
{character && (
<ControllerButton
controlId="mode-class-hall"
selectedId={controller.selectedId}
select={controller.select}
className="front-button main-menu-mode main-menu-mode--available main-menu-mode--class-hall"
type="button"
onClick={openClassHall}
>
<b aria-hidden="true">CH</b>
<span><strong>Class Hall</strong><small>{isRom ? "Choose a permanent partner class and swap your active ordering." : "Change specialization and retune your heirlooms."}</small></span>
<em>{isRom && character.level < 10 ? "Unlocks at 10" : "Open"}</em>
</ControllerButton>
)}
{GAME_MODES.map((mode) => mode.available ? (
<ControllerButton
key={mode.id}
controlId={`mode-${mode.id}`}
selectedId={controller.selectedId}
select={controller.select}
className="front-button main-menu-mode main-menu-mode--available"
type="button"
onClick={mode.id === "manastorms"
? openManastorms
: mode.id === "arenas"
? openArenas
: mode.id === "downloads"
? onOpenDownloads
: mode.id === "randomized-bite-sized"
? onOpenRandomizedBiteSized
: openDungeons}
>
<b aria-hidden="true">{mode.number}</b>
<span><strong>{mode.name}</strong><small>{mode.description}</small></span>
<em>Available</em>
</ControllerButton>
) : (
<button
key={mode.id}
className="front-button main-menu-mode main-menu-mode--coming-soon"
type="button"
disabled
aria-label={`${mode.name}, coming soon`}
>
<b aria-hidden="true">{mode.number}</b>
<span><strong>{mode.name}</strong><small>{mode.description}</small></span>
<em>Coming soon</em>
</button>
))}
</section>
<section className="main-menu-card main-menu-card--secondary" aria-labelledby="main-menu-secondary-title">
<header><span>02</span><strong id="main-menu-secondary-title">Character &amp; Collection</strong></header>
<div className="main-menu-card__grid">
{character && <>
<ControllerButton controlId="mode-class-hall" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available main-menu-mode--class-hall" type="button" onClick={openClassHall}>
<b aria-hidden="true">CH</b><span><strong>Class Hall</strong><small>{isRom ? "Choose a permanent partner class and swap your active ordering." : "Change specialization and retune your heirlooms."}</small></span><em>{isRom && character.level < 10 ? "Unlocks at 10" : "Open"}</em>
</ControllerButton>
<ControllerButton controlId="mode-talents" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("talents")}>
<b aria-hidden="true">T</b><span><strong>{isRom ? "Skills" : "Talents"}</strong><small>{isRom ? "Review your primary, secondary, and elite skills." : "View your talent trees, spend points, or reset your build."}</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-keybinds" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("keybinds")}>
<b aria-hidden="true">K</b><span><strong>Keybinds</strong><small>View and change keyboard or controller bindings.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-bindings" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={() => onOpenEditor("bindings")}>
<b aria-hidden="true">A</b><span><strong>Button assignments</strong><small>Choose the abilities on your action bars and controller buttons.</small></span><em>Open</em>
</ControllerButton>
</>}
<ControllerButton controlId="mode-cards" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available" type="button" onClick={onOpenCards}>
<b aria-hidden="true">◆</b><span><strong>Boss Cards</strong><small>Track your collection, rarities, and copies from every dungeon.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="mode-gm-menu" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--available main-menu-mode--gm-menu" type="button" onClick={() => setGmMenuOpen(true)}>
<b aria-hidden="true">GM</b><span><strong>GM Menu</strong><small>Testing, readiness, reports, and balance tools.</small></span><em>Open</em>
</ControllerButton>
{GAME_MODES.filter((mode) => mode.id !== "dungeons" && mode.id !== "randomized-bite-sized").map(renderGameMode)}
</div>
</section>
</nav>
{notice && <p className="front-notice main-menu-notice" role="status" aria-live="polite">{notice}</p>}
</main>
<ControllerLegend back />
{gmMenuOpen && (
<div className="front-modal-backdrop">
<section className="main-menu-gm-modal" role="dialog" aria-modal="true" aria-labelledby="gm-menu-title">
<header>
<div><span>Authorized utilities</span><h2 id="gm-menu-title">GM Menu</h2></div>
<ControllerButton controlId="gm-menu-close" selectedId={controller.selectedId} select={controller.select} type="button" onClick={() => setGmMenuOpen(false)}>Close</ControllerButton>
</header>
<div className="main-menu-gm-grid">
<ControllerButton controlId="gm-menu-gm-tools" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--gm" type="button" disabled={!isGm} onClick={openGmAdmin}>
<b aria-hidden="true">GM</b><span><strong>GM Tools</strong><small>Run dungeon simulations, inspect findings, and review recordings.</small></span><em>{isGm ? "Authorized" : "GM account required"}</em>
</ControllerButton>
<ControllerButton controlId="gm-menu-boss-testing" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--gm" type="button" onClick={onOpenBossTesting}>
<b aria-hidden="true">BT</b><span><strong>Boss Testing</strong><small>Fight a selected or random encounter in an empty testing room.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="gm-menu-companion-readiness" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--gm" type="button" onClick={onOpenReadiness}>
<b aria-hidden="true">CR</b><span><strong>Companion Readiness</strong><small>Review certified NPC specializations and Playerbots identities.</small></span><em>Open</em>
</ControllerButton>
<ControllerButton controlId="gm-menu-balance-lab" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--gm" type="button" disabled={!isGm} onClick={onOpenBalanceLab}>
<b aria-hidden="true">BL</b><span><strong>Balance Lab</strong><small>Queue automatic party and boss simulations on the server.</small></span><em>{isGm ? "Server" : "GM account required"}</em>
</ControllerButton>
<ControllerButton controlId="gm-menu-reports" selectedId={controller.selectedId} select={controller.select}
className="front-button main-menu-mode main-menu-mode--gm" type="button" onClick={onOpenReports}>
<b aria-hidden="true">!</b><span><strong>View Reports</strong><small>Browse dungeon issues and follow their status.</small></span><em>Open</em>
</ControllerButton>
</div>
</section>
</div>
)}
</FrontSurface>
}
bottom={<LazyLocalCompanionPanel />}