Files
noname-shuying/mengsan/ui/gm-manager.js
T

91 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { AFFIX_INFO } from "../cards/affixes.js";
import { describeLibraryCard } from "./card-library.js";
const node = (tag, className, label, parent) => {
const element = document.createElement(tag);
element.className = className;
if (label != null) element.textContent = label;
parent?.appendChild(element);
return element;
};
const button = (label, parent, action) => {
const element = node("button", "ms-deck-button", label, parent);
element.type = "button";
element.addEventListener("click", action);
return element;
};
// Keep the command battle-scoped; further GM actions can be added as panels here.
export function mountGMManager(session, owner, piles, battle, resources, game) {
let dialog = null, selected = null, previousFocus = null, disposed = false;
const available = () => !disposed && session.active && !battle.resolving;
const close = () => {
if (!dialog) return;
const current = dialog;
dialog = null;
if (current.open) current.close();
current.remove();
if (previousFocus?.isConnected) previousFocus.focus();
previousFocus = null;
};
const render = () => {
if (!dialog) return;
const list = dialog.querySelector(".ms-gm-hand");
const editor = dialog.querySelector(".ms-gm-editor");
list.replaceChildren(); editor.replaceChildren();
const hand = owner.getCards("h").filter(card => card.storage?.mengsanCard_shuying);
if (!hand.includes(selected)) selected = null;
if (!hand.length) node("p", "ms-deck-empty", "当前没有可编辑的梦三手牌。", list);
for (const [index, card] of hand.entries()) {
const data = card.storage.mengsanCard_shuying;
const details = describeLibraryCard({ ...data, name: card.name, suit: card.suit, number: card.number });
const label = `${index + 1}. ${details.name} ${details.suit}${details.number}${details.affixes.length ? ` · ${details.affixes.map(value => value.split(":")[0]).join("、")}` : ""}`;
const item = button(label, list, () => { selected = card; render(); });
item.classList.add("ms-gm-card");
item.setAttribute("aria-pressed", String(selected === card));
}
if (!selected) { node("p", "ms-deck-empty", "选择一张手牌,然后添加词缀。", editor); return; }
node("h3", "", `编辑:${describeLibraryCard(selected.storage.mengsanCard_shuying).name}`, editor);
const choices = node("div", "ms-gm-affixes", null, editor);
for (const [key, info] of Object.entries(AFFIX_INFO)) {
const applied = selected.storage.mengsanCard_shuying.affixes.includes(key);
const item = button(`${info.name}${applied ? " · 已有" : ""}`, choices, () => {
if (!available() || !owner.getCards("h").includes(selected)) { render(); return; }
piles.addAffix(selected, key);
render();
});
item.disabled = applied;
item.title = info.description;
}
};
const open = () => {
if (!available()) return false;
if (dialog?.open) { render(); dialog.focus(); return true; }
previousFocus = document.activeElement;
dialog = node("dialog", "mengsan-deck-dialog-shuying mengsan-gm-dialog-shuying", null);
dialog.setAttribute("aria-label", "梦三 GM 管理器");
dialog.tabIndex = -1;
const header = node("header", "ms-deck-header", null, dialog);
node("h2", "ms-deck-title", "GM 管理器", header);
button("关闭", header, close);
node("p", "ms-deck-summary", "手牌词缀 · 仅本场战斗生效,不改永久牌组或存档。", dialog);
button("刷新手牌", dialog, render);
node("div", "ms-gm-hand", null, dialog);
node("section", "ms-gm-editor", null, dialog);
const created = dialog;
dialog.addEventListener("close", () => { if (dialog === created) close(); });
dialog.addEventListener("keydown", event => event.stopPropagation());
document.body.appendChild(dialog);
try { dialog.showModal(); } catch (error) { close(); throw error; }
render();
dialog.querySelector(".ms-deck-header button").focus();
return true;
};
resources.field(globalThis, "mengsanGM_shuying", open);
resources.field(game, "mengsanGM_shuying", open);
const handle = { dispose: close };
session.ownResource(handle, () => { disposed = true; close(); });
return handle;
}