Update Healer Man server

This commit is contained in:
phenom committed 2026-09-23 21:58:28 -04:00
1 parent a641a6e948
commit d869d6b51c
2 files changed
+100 -45

No files matched your search

+28 -3
View File
@@ -623,6 +623,22 @@ kbd { color: var(--moss-bright); font: inherit; font-size: 0.64rem; }
.pause-menu h2 { margin: 0; font-family: Georgia, serif; font-size: clamp(1.7rem, 5vw, 2.3rem); font-weight: 400; } .pause-menu h2 { margin: 0; font-family: Georgia, serif; font-size: clamp(1.7rem, 5vw, 2.3rem); font-weight: 400; }
.pause-menu > p:not(.eyebrow) { color: var(--muted); line-height: 1.5; } .pause-menu > p:not(.eyebrow) { color: var(--muted); line-height: 1.5; }
.pause-menu__actions { display: grid; gap: 8px; margin: 21px 0; } .pause-menu__actions { display: grid; gap: 8px; margin: 21px 0; }
.pause-menu--hub { width: min(860px, 100%); padding: clamp(16px, 2vw, 24px); }
.pause-menu__header { text-align: center; }
.pause-menu--hub h2 { font-family: inherit; font-size: 1.2rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; }
.pause-menu__character { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 10px; }
.pause-menu__character img { width: 48px; height: 48px; flex: none; border: 2px solid var(--edge); border-radius: 10px; object-fit: cover; }
.pause-menu__character div { display: grid; gap: 3px; min-width: 0; text-align: left; }
.pause-menu__character strong { overflow: hidden; font-size: 1.05rem; text-overflow: ellipsis; white-space: nowrap; }
.pause-menu__character span, .pause-menu__context { color: var(--muted); font-size: 0.78rem; }
.pause-menu__context { margin: 7px 0 0; }
.pause-menu__resume { display: flex; justify-content: center; margin: 14px 0; }
.pause-menu__resume .button { width: min(320px, 100%); }
.pause-menu__groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.pause-menu__group { min-width: 0; padding: 12px; border: 1px solid rgba(188, 207, 160, 0.15); border-radius: 10px; background: rgba(170, 194, 141, 0.035); }
.pause-menu__group h3 { margin: 0 0 9px; color: var(--muted); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; }
.pause-menu--hub .pause-menu__actions { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 0; }
.pause-menu--hub .pause-menu__actions .button { width: 100%; min-width: 0; padding: 7px 9px; line-height: 1.15; }
.button { min-height: 42px; padding: 0 16px; border: 1px solid var(--edge); border-radius: 9px; background: rgba(170, 194, 141, 0.07); color: var(--ink); cursor: pointer; } .button { min-height: 42px; padding: 0 16px; border: 1px solid var(--edge); border-radius: 9px; background: rgba(170, 194, 141, 0.07); color: var(--ink); cursor: pointer; }
.button:hover { background: rgba(170, 194, 141, 0.16); } .button:hover { background: rgba(170, 194, 141, 0.16); }
.button--primary { border-color: rgba(200, 222, 151, 0.32); background: #587348; color: #fbfee9; } .button--primary { border-color: rgba(200, 222, 151, 0.32); background: #587348; color: #fbfee9; }
@@ -645,6 +661,10 @@ kbd { color: var(--moss-bright); font: inherit; font-size: 0.64rem; }
} }
@media (max-width: 620px) { @media (max-width: 620px) {
.pause-menu__groups { grid-template-columns: minmax(0, 1fr); gap: 8px; }
.pause-menu--hub .pause-menu__group { padding: 9px; }
.pause-menu--hub .pause-menu__character { margin-top: 8px; }
.pause-menu--hub .pause-menu__resume { margin: 10px 0; }
.objective-chip { width: calc(100vw - 116px); } .objective-chip { width: calc(100vw - 116px); }
.objective-chip .eyebrow, .objective-chip > p:last-child { display: none; } .objective-chip .eyebrow, .objective-chip > p:last-child { display: none; }
.objective-chip h1 { overflow: hidden; font-size: 0.92rem; text-overflow: ellipsis; white-space: nowrap; } .objective-chip h1 { overflow: hidden; font-size: 0.92rem; text-overflow: ellipsis; white-space: nowrap; }
@@ -1717,9 +1737,14 @@ kbd { color: var(--moss-bright); font: inherit; font-size: 0.64rem; }
.talent-details > p { margin: 7px 0; } .talent-details > p { margin: 7px 0; }
.talent-details__requirements { margin: 6px 0; } .talent-details__requirements { margin: 6px 0; }
.option-row { min-height: 34px; padding: 3px 2px; } .option-row { min-height: 34px; padding: 3px 2px; }
.pause-menu { width: min(560px, 100%); padding: 16px 20px; } .pause-menu:not(.pause-menu--hub) { width: min(560px, 100%); padding: 16px 20px; }
.pause-menu__actions { grid-template-columns: repeat(2, 1fr); gap: 6px; margin: 9px 0; } .pause-menu:not(.pause-menu--hub) .pause-menu__actions { grid-template-columns: repeat(2, 1fr); gap: 6px; margin: 9px 0; }
.pause-menu__actions .button { min-height: 34px; } .pause-menu:not(.pause-menu--hub) .pause-menu__actions .button { min-height: 34px; }
.pause-menu--hub { padding: 14px 16px; }
.pause-menu--hub .pause-menu__actions { gap: 6px; }
.pause-menu--hub .pause-menu__group { padding: 9px; }
.pause-menu--hub .pause-menu__group h3 { margin-bottom: 6px; }
.pause-menu--hub .pause-menu__resume { margin: 9px 0; }
.boss-guide-panel__layout { padding-top: 7px; } .boss-guide-panel__layout { padding-top: 7px; }
.boss-guide-panel__layout nav h3 { padding: 5px 8px; } .boss-guide-panel__layout nav h3 { padding: 5px 8px; }
.boss-guide-panel__layout nav button { min-height: 33px; padding: 4px 6px; } .boss-guide-panel__layout nav button { min-height: 33px; padding: 4px 6px; }
+72 -42
View File
@@ -4,13 +4,12 @@ import { useGameStore } from "../game/store";
import { useMenuController, type MenuAction } from "../input/useMenuController"; import { useMenuController, type MenuAction } from "../input/useMenuController";
import { ControllerButton } from "./ControllerButton"; import { ControllerButton } from "./ControllerButton";
import { sessionCanUseShelvedGmTools } from "../app/releaseFeatures"; import { sessionCanUseShelvedGmTools } from "../app/releaseFeatures";
import { useInputBindingLabels } from "../input/useInputBindingLabels";
import { INPUT_ACTION_BY_ID } from "../input/keybinds";
import { DungeonIssueReportPanel } from "./DungeonIssueReportPanel"; import { DungeonIssueReportPanel } from "./DungeonIssueReportPanel";
import { BossTestingReviewPanel } from "./BossTestingReviewPanel"; import { BossTestingReviewPanel } from "./BossTestingReviewPanel";
import { classById } from "../app/characterCatalog";
import { specializationById, specializationIconUrl } from "../game/specializationCatalog";
function PauseDialog() { function PauseDialog() {
const bindingLabel = useInputBindingLabels();
const closeOverlay = useGameStore((state) => state.closeOverlay); const closeOverlay = useGameStore((state) => state.closeOverlay);
const openOverlay = useGameStore((state) => state.openOverlay); const openOverlay = useGameStore((state) => state.openOverlay);
const reset = useGameStore((state) => state.resetAtEntrance); const reset = useGameStore((state) => state.resetAtEntrance);
@@ -37,29 +36,34 @@ function PauseDialog() {
}; };
const skillMenuLabel = activeCharacter?.categoryId === "rom" ? "Skills" : "Talents"; const skillMenuLabel = activeCharacter?.categoryId === "rom" ? "Skills" : "Talents";
const isGm = sessionCanUseShelvedGmTools(session); const isGm = sessionCanUseShelvedGmTools(session);
const characterClass = activeCharacter ? classById(activeCharacter.classId) : null;
const specialization = specializationById(activeCharacter?.specializationId);
const classDetail = activeCharacter?.secondaryClassId
? classById(activeCharacter.secondaryClassId).name
: specialization && specialization.classId === activeCharacter?.classId ? specialization.displayName : null;
const actions = useMemo<MenuAction[]>(() => [ const actions = useMemo<MenuAction[]>(() => [
{ id: "pause-resume", run: resume }, { id: "pause-resume", run: resume, neighbors: { down: "pause-party", right: "pause-party", up: "pause-main-menu", left: "pause-main-menu" } },
{ id: "pause-party", run: () => openOverlay("party") }, { id: "pause-party", run: () => openOverlay("party"), neighbors: { up: "pause-resume" } },
{ id: "pause-inventory", run: () => openOverlay("inventory") },
{ id: "pause-boss-cards", run: () => openOverlay("boss-cards") },
{ id: "pause-map", run: () => openOverlay("map") }, { id: "pause-map", run: () => openOverlay("map") },
{ id: "pause-boss-guide", run: () => openOverlay("boss-guide") }, { id: "pause-boss-guide", run: () => openOverlay("boss-guide") },
{ id: "pause-boss-cards", run: () => openOverlay("boss-cards") },
{ id: "pause-inventory", run: () => openOverlay("inventory") },
{ id: "pause-spellbook", run: () => openOverlay("spellbook") }, { id: "pause-spellbook", run: () => openOverlay("spellbook") },
{ id: "pause-bindings", run: () => openOverlay("bindings") },
{ id: "pause-talents", run: () => openOverlay("talents") }, { id: "pause-talents", run: () => openOverlay("talents") },
{ id: "pause-keybinds", run: () => openOverlay("keybinds") }, { id: "pause-bindings", run: () => openOverlay("bindings") },
{ id: "pause-options", run: () => openOverlay("options") }, { id: "pause-options", run: () => openOverlay("options") },
{ id: "pause-keybinds", run: () => openOverlay("keybinds") },
{ id: "pause-ui-layout", run: () => openOverlay("ui-layout") }, { id: "pause-ui-layout", run: () => openOverlay("ui-layout") },
...(isGm ? [{ id: "pause-gm", run: () => openOverlay("gm") }] : []),
...(gameMode === "dungeon" ? [{ id: "pause-report-issue", run: () => openOverlay("report-issue") }] : []), ...(gameMode === "dungeon" ? [{ id: "pause-report-issue", run: () => openOverlay("report-issue") }] : []),
...(bossTest ? [{ id: "pause-boss-testing-review", run: () => openOverlay("boss-test-review") }] : []), ...(bossTest ? [{ id: "pause-boss-testing-review", run: () => openOverlay("boss-test-review") }] : []),
...(isGm ? [{ id: "pause-gm", run: () => openOverlay("gm") }] : []),
...(gameMode === "manastorm" ...(gameMode === "manastorm"
? [{ id: "pause-abandon-manastorm", run: abandonManastorm }] ? [{ id: "pause-abandon-manastorm", run: abandonManastorm }]
: [{ id: "pause-entrance", run: returnToEntrance }]), : [{ id: "pause-entrance", run: returnToEntrance }]),
{ id: "pause-main-menu", run: leaveDungeon }, { id: "pause-main-menu", run: leaveDungeon },
], [abandonManastormRun, bossTest, closeOverlay, gameMode, isGm, openOverlay, reset, returnToMainMenu]); ], [abandonManastormRun, bossTest, closeOverlay, gameMode, isGm, openOverlay, reset, returnToMainMenu]);
const controller = useMenuController(actions, { initialId: "pause-resume", onBack: resume }); const controller = useMenuController(actions, { columns: 2, initialId: "pause-resume", onBack: resume });
const menuButton = (id: string, label: string, run: () => void, primary = false) => ( const menuButton = (id: string, label: string, run: () => void, primary = false) => (
<ControllerButton <ControllerButton
@@ -74,38 +78,64 @@ function PauseDialog() {
return ( return (
<div className="modal-backdrop" role="presentation"> <div className="modal-backdrop" role="presentation">
<section className="pause-menu" role="dialog" aria-modal="true" aria-labelledby="pause-title"> <section className="pause-menu pause-menu--hub" role="dialog" aria-modal="true" aria-labelledby="pause-title">
<p className="eyebrow">Healer Man paused</p> <header className="pause-menu__header">
<h2 id="pause-title">{bossTest ? `${bossTest.name} — Boss Testing` : "The cavern waits."}</h2> <h2 id="pause-title">Paused</h2>
<p>Resume play or open any character and system menu from this hub.</p> <div className="pause-menu__character">
<div className="pause-menu__actions"> {activeCharacter && <img
{menuButton("pause-resume", "Resume expedition", resume, true)} src={specializationIconUrl(activeCharacter.classId, activeCharacter.specializationId)}
{bossTest && menuButton("pause-boss-testing-review", "Boss readiness & report", () => openOverlay("boss-test-review"))} alt=""
{menuButton("pause-party", "Party management", () => openOverlay("party"))} aria-hidden="true"
{menuButton("pause-inventory", "Inventory & equipment", () => openOverlay("inventory"))} style={{ borderColor: characterClass?.color }}
{menuButton("pause-boss-cards", "Boss card collection", () => openOverlay("boss-cards"))} />}
{menuButton("pause-map", "Dungeon map", () => openOverlay("map"))} <div>
{menuButton("pause-boss-guide", "Boss guide", () => openOverlay("boss-guide"))} <strong>{activeCharacter?.name ?? "Adventurer"}</strong>
{menuButton("pause-spellbook", "Spellbook", () => openOverlay("spellbook"))} <span>Level {activeCharacter?.level ?? 1} · {characterClass?.name ?? "Explorer"}{classDetail ? ` / ${classDetail}` : ""}</span>
{menuButton("pause-bindings", "Ability controls", () => openOverlay("bindings"))} </div>
{menuButton("pause-talents", skillMenuLabel, () => openOverlay("talents"))} </div>
{menuButton("pause-keybinds", "Keybinds", () => openOverlay("keybinds"))} {bossTest && <p className="pause-menu__context">Boss Testing · {bossTest.name}</p>}
{menuButton("pause-options", "Options", () => openOverlay("options"))} </header>
{menuButton("pause-ui-layout", "Edit UI & templates", () => openOverlay("ui-layout"))} <div className="pause-menu__resume">{menuButton("pause-resume", "Resume game", resume, true)}</div>
{gameMode === "dungeon" ? menuButton("pause-report-issue", "Report an issue", () => openOverlay("report-issue")) : null} <div className="pause-menu__groups">
{isGm ? menuButton("pause-gm", "GM placement editor", () => openOverlay("gm")) : null} <section className="pause-menu__group" aria-labelledby="pause-adventure-title">
{gameMode === "manastorm" <h3 id="pause-adventure-title">Adventure</h3>
? menuButton("pause-abandon-manastorm", "Abandon Manastorm run", abandonManastorm) <div className="pause-menu__actions">
: menuButton("pause-entrance", bossTest ? "Restart boss test" : "Return to entrance", returnToEntrance)} {menuButton("pause-party", "Party management", () => openOverlay("party"))}
{menuButton("pause-main-menu", "Return to main menu", leaveDungeon)} {menuButton("pause-map", "Dungeon map", () => openOverlay("map"))}
{menuButton("pause-boss-guide", "Boss guide", () => openOverlay("boss-guide"))}
{menuButton("pause-boss-cards", "Boss cards", () => openOverlay("boss-cards"))}
</div>
</section>
<section className="pause-menu__group" aria-labelledby="pause-character-title">
<h3 id="pause-character-title">Character</h3>
<div className="pause-menu__actions">
{menuButton("pause-inventory", "Inventory & equipment", () => openOverlay("inventory"))}
{menuButton("pause-spellbook", "Spellbook", () => openOverlay("spellbook"))}
{menuButton("pause-talents", skillMenuLabel, () => openOverlay("talents"))}
{menuButton("pause-bindings", "Ability controls", () => openOverlay("bindings"))}
</div>
</section>
<section className="pause-menu__group" aria-labelledby="pause-settings-title">
<h3 id="pause-settings-title">Settings</h3>
<div className="pause-menu__actions">
{menuButton("pause-options", "Options", () => openOverlay("options"))}
{menuButton("pause-keybinds", "Keybinds", () => openOverlay("keybinds"))}
{menuButton("pause-ui-layout", "Edit UI & templates", () => openOverlay("ui-layout"))}
{isGm && menuButton("pause-gm", "GM placement editor", () => openOverlay("gm"))}
</div>
</section>
<section className="pause-menu__group" aria-labelledby="pause-session-title">
<h3 id="pause-session-title">Session</h3>
<div className="pause-menu__actions">
{gameMode === "dungeon" && menuButton("pause-report-issue", "Report an issue", () => openOverlay("report-issue"))}
{bossTest && menuButton("pause-boss-testing-review", "Boss readiness & report", () => openOverlay("boss-test-review"))}
{gameMode === "manastorm"
? menuButton("pause-abandon-manastorm", "Abandon Manastorm run", abandonManastorm)
: menuButton("pause-entrance", bossTest ? "Restart boss test" : "Return to entrance", returnToEntrance)}
{menuButton("pause-main-menu", "Main menu", leaveDungeon)}
</div>
</section>
</div> </div>
<dl className="controls-list">
{["move-forward", "jump", "target-next", "party-next", "loot", "inventory", "spellbook", "pause"].map((id) => <div key={id}>
<dt>{INPUT_ACTION_BY_ID[id].label}</dt><dd>{bindingLabel("pc", id)} / {bindingLabel("controller", id)}</dd>
</div>)}
<div><dt>Camera</dt><dd>Hold right mouse and drag</dd></div>
<div><dt>All controls</dt><dd>Options → Keybinds</dd></div>
</dl>
</section> </section>
</div> </div>
); );