mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-07 17:10:02 +08:00
fixed # 更新刘备配置和奖励
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { get } from "../../../../noname.js";
|
||||
import { cardUpgradeRule } from "../cards/upgrades.js";
|
||||
import { getRelic } from "../relics/definitions.js";
|
||||
import {
|
||||
chooseCardDialog, chooseRelicDialog, chooseVictoryOptions,
|
||||
RETURN_TO_VICTORY,
|
||||
} from "./reward-ui.js";
|
||||
|
||||
const upgradeDescription = choice => {
|
||||
const card = choice.card;
|
||||
const rule = cardUpgradeRule(card.name);
|
||||
const damage = card.name === "sha" ? 6 : 8;
|
||||
const suit = get.translation(card.suit);
|
||||
return `${suit}${card.number} · 伤害 ${damage} → ${rule.damage}` +
|
||||
(rule.vulnerable ? `;易伤 2 → ${rule.vulnerable} 层` : "") +
|
||||
"。费用不变,最多强化一次。";
|
||||
};
|
||||
|
||||
export async function chooseRewardPackage(choices, {
|
||||
rewardPackage: pack, fixedRewards = [],
|
||||
}) {
|
||||
let cardId = null, relicId = null;
|
||||
const upgradeChoices = pack.upgradeChoices.map(card => ({
|
||||
id: card.id, card,
|
||||
}));
|
||||
const ready = () => (!upgradeChoices.length || cardId !== null) &&
|
||||
(!pack.relicChoices.length || relicId !== null);
|
||||
const upgrade = {
|
||||
id: "upgrade", label: "强化一张牌",
|
||||
description: upgradeChoices.length
|
||||
? "从个人牌库中择一磨砺,查看强化前后的变化。"
|
||||
: "当前没有可强化卡牌,此项无需选择。",
|
||||
isDisabled: () => !upgradeChoices.length,
|
||||
async choose() {
|
||||
const selected = await chooseCardDialog(upgradeChoices, {
|
||||
title: "磨砺 · 强化一张牌", skipLabel: "返回奖励",
|
||||
description: "择一磨砺,锋芒更盛。只强化你选中的一张牌。",
|
||||
describeChoice: upgradeDescription, actionLabel: "强化此牌",
|
||||
});
|
||||
if (selected !== null) {
|
||||
cardId = selected;
|
||||
const choice = upgradeChoices.find(card => card.id === selected);
|
||||
upgrade.label = `已选强化 · ${get.translation(choice.card.name)}`;
|
||||
upgrade.description = upgradeDescription(choice);
|
||||
}
|
||||
return RETURN_TO_VICTORY;
|
||||
},
|
||||
};
|
||||
const relic = {
|
||||
id: "relic", label: "遗物 · 三选一",
|
||||
description: pack.relicChoices.length
|
||||
? "检视本次随机珍藏,择一伴你踏上下一段征途。"
|
||||
: "已持有奖励池中的全部遗物,此项无需选择。",
|
||||
isDisabled: () => !pack.relicChoices.length,
|
||||
async choose() {
|
||||
const selected = await chooseRelicDialog(pack.relicChoices, {
|
||||
skipLabel: "返回奖励", actionLabel: "携此遗物",
|
||||
});
|
||||
if (selected !== null) {
|
||||
relicId = selected;
|
||||
const choice = pack.relicChoices.find(item => item.id === selected);
|
||||
const definition = getRelic(choice.relic);
|
||||
relic.label = `已选遗物 · ${definition.name}`;
|
||||
relic.description = definition.description;
|
||||
}
|
||||
return RETURN_TO_VICTORY;
|
||||
},
|
||||
};
|
||||
return chooseVictoryOptions([upgrade, relic, {
|
||||
id: "claim-package", label: "领取全部奖励",
|
||||
description: "完成以上选择后,金币、强化、遗物与固定卡牌一并入囊。",
|
||||
isDisabled: () => !ready(),
|
||||
choose: () => ({ cardId, relicId }),
|
||||
}], {
|
||||
title: "初识的卢", allowSkip: false,
|
||||
rewardLabel: "固定收获 · 完成两项选择后领取",
|
||||
description: `固定获得 ${pack.gold} 金币与${fixedRewards.map(
|
||||
reward => `【${reward.name}】`).join("、")}。` +
|
||||
"另可强化一张牌,并从随机遗物中择一件。",
|
||||
});
|
||||
}
|
||||
+55
-11
@@ -1,6 +1,7 @@
|
||||
import { lib, get } from "../../../../noname.js";
|
||||
import { cardCost } from "../battle/combat-rules.js";
|
||||
import { cardUpgradeRule } from "../cards/upgrades.js";
|
||||
import { getRelic } from "../relics/definitions.js";
|
||||
import { chooseButtons } from "./flow-ui.js";
|
||||
|
||||
const element = (tag, className, text, parent) => {
|
||||
@@ -42,14 +43,39 @@ const viewCard = choice => {
|
||||
};
|
||||
|
||||
const restoreFocus = previous => { if (previous?.isConnected) previous.focus(); };
|
||||
const RETURN_TO_VICTORY = Symbol("return to victory choice");
|
||||
export const RETURN_TO_VICTORY = Symbol("return to victory choice");
|
||||
|
||||
// Reusable card-only selector. It accepts three cards today and can lay out four or five later.
|
||||
// Callers supply stable choice IDs; this UI never creates or awards cards itself.
|
||||
export function chooseCardDialog(choices, { title = "选择一张牌", allowSkip = true, skipLabel = "跳过" } = {}) {
|
||||
export function chooseCardDialog(choices, options = {}) {
|
||||
if (!Array.isArray(choices) || !choices.length || choices.some(choice => !choice?.id || !isCardReward(choice))) {
|
||||
return Promise.reject(new TypeError("chooseCardDialog requires nonempty card choices with IDs"));
|
||||
}
|
||||
return chooseTilesDialog(choices, options, viewCard);
|
||||
}
|
||||
|
||||
export function chooseRelicDialog(choices, options = {}) {
|
||||
if (!Array.isArray(choices) || !choices.length || choices.some(choice =>
|
||||
!choice?.id || !getRelic(choice.relic))) {
|
||||
return Promise.reject(new TypeError("遗物候选无效"));
|
||||
}
|
||||
return chooseTilesDialog(choices, {
|
||||
title: "择一件遗物",
|
||||
description: `${choices.length} 件珍藏,择一伴你远行。`,
|
||||
actionLabel: "携此遗物",
|
||||
...options,
|
||||
}, choice => {
|
||||
const relic = getRelic(choice.relic);
|
||||
return { name: relic.name, type: "遗物", image: null, cost: null,
|
||||
description: relic.description };
|
||||
});
|
||||
}
|
||||
|
||||
function chooseTilesDialog(choices, {
|
||||
title = "选择一张牌", allowSkip = true, skipLabel = "跳过",
|
||||
description = "择一入阵,余者留于身后", describeChoice,
|
||||
actionLabel = "选择此牌",
|
||||
} = {}, viewChoice) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const previousFocus = document.activeElement;
|
||||
const dialog = element("dialog", "mengsan-card-choice-dialog-shuying", null);
|
||||
@@ -58,7 +84,7 @@ export function chooseCardDialog(choices, { title = "选择一张牌", allowSkip
|
||||
const banner = element("header", "mengsan-card-choice-heading-shuying", null, stage);
|
||||
element("span", "mengsan-reward-overline-shuying", "梦三 · 战利品", banner);
|
||||
element("h2", "", title, banner);
|
||||
element("p", "", "择一入阵,余者留于身后", banner);
|
||||
element("p", "", description, banner);
|
||||
const cards = element("div", "mengsan-card-choice-grid-shuying", null, stage);
|
||||
cards.style.setProperty("--choice-count", String(Math.min(choices.length, 5)));
|
||||
let settled = false;
|
||||
@@ -71,12 +97,15 @@ export function chooseCardDialog(choices, { title = "选择一张牌", allowSkip
|
||||
resolve(id);
|
||||
};
|
||||
for (const choice of choices) {
|
||||
const view = viewCard(choice);
|
||||
const view = viewChoice(choice);
|
||||
if (describeChoice) view.description = describeChoice(choice);
|
||||
const card = element("button", "mengsan-card-choice-tile-shuying", null, cards);
|
||||
card.type = "button";
|
||||
card.setAttribute("aria-label", `${view.name},${view.type},${view.description}。选择此牌`);
|
||||
card.setAttribute("aria-label", `${view.name},${view.type},${view.description}。${actionLabel}`);
|
||||
card.addEventListener("click", () => finish(choice.id));
|
||||
element("span", "mengsan-card-choice-cost-shuying", String(view.cost), card);
|
||||
if (view.cost !== null) {
|
||||
element("span", "mengsan-card-choice-cost-shuying", String(view.cost), card);
|
||||
}
|
||||
element("span", "mengsan-card-choice-name-shuying", view.name, card);
|
||||
const artwork = element("span", "mengsan-card-choice-art-shuying", null, card);
|
||||
if (view.image) {
|
||||
@@ -87,6 +116,7 @@ export function chooseCardDialog(choices, { title = "选择一张牌", allowSkip
|
||||
element("span", "mengsan-card-choice-glyph-shuying", view.name.slice(0, 1), artwork);
|
||||
element("span", "mengsan-card-choice-kind-shuying", view.type, card);
|
||||
element("span", "mengsan-card-choice-description-shuying", view.description, card);
|
||||
element("span", "mengsan-reward-tile-action-shuying", actionLabel, card);
|
||||
}
|
||||
if (allowSkip) {
|
||||
const skip = element("button", "mengsan-card-choice-skip-shuying", skipLabel, stage);
|
||||
@@ -125,6 +155,15 @@ export function chooseVictoryOptions(options, {
|
||||
const board = element("div", "mengsan-victory-options-shuying", null, stage);
|
||||
element("span", "mengsan-victory-eyebrow-shuying", rewardLabel, board);
|
||||
let settled = false, choosing = false;
|
||||
const entries = [];
|
||||
const refresh = () => {
|
||||
for (const { option, entry, label, detail } of entries) {
|
||||
label.textContent = option.label;
|
||||
detail.textContent = option.description || "";
|
||||
entry.disabled = Boolean(option.isDisabled?.());
|
||||
entry.setAttribute("aria-label", `${option.label}。${option.description || ""}`);
|
||||
}
|
||||
};
|
||||
const finish = id => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
@@ -139,19 +178,20 @@ export function chooseVictoryOptions(options, {
|
||||
entry.setAttribute("aria-label", `${option.label}。${option.description || ""}`);
|
||||
element("span", "mengsan-victory-option-index-shuying", String(index + 1).padStart(2, "0"), entry);
|
||||
const copy = element("span", "mengsan-victory-option-copy-shuying", null, entry);
|
||||
element("strong", "", option.label, copy);
|
||||
if (option.description) element("small", "", option.description, copy);
|
||||
const label = element("strong", "", option.label, copy);
|
||||
const detail = element("small", "", option.description || "", copy);
|
||||
entries.push({ option, entry, label, detail });
|
||||
element("span", "mengsan-victory-option-arrow-shuying", "›", entry).setAttribute("aria-hidden", "true");
|
||||
entry.addEventListener("click", async () => {
|
||||
if (settled || choosing) return;
|
||||
if (settled || choosing || entry.disabled) return;
|
||||
choosing = true;
|
||||
entry.disabled = true;
|
||||
if (dialog.open) dialog.close();
|
||||
try {
|
||||
const selected = option.choose ? await option.choose() : option.id;
|
||||
if (selected === RETURN_TO_VICTORY) {
|
||||
refresh();
|
||||
dialog.showModal();
|
||||
entry.disabled = false;
|
||||
choosing = false;
|
||||
entry.focus();
|
||||
} else finish(selected);
|
||||
@@ -161,6 +201,7 @@ export function chooseVictoryOptions(options, {
|
||||
}
|
||||
});
|
||||
}
|
||||
refresh();
|
||||
if (allowSkip) {
|
||||
const skip = element("button", "mengsan-victory-skip-shuying", "放弃本次选择", stage);
|
||||
skip.type = "button";
|
||||
@@ -169,7 +210,10 @@ export function chooseVictoryOptions(options, {
|
||||
dialog.addEventListener("cancel", event => event.preventDefault());
|
||||
dialog.addEventListener("keydown", event => event.stopPropagation());
|
||||
document.documentElement.appendChild(dialog);
|
||||
try { dialog.showModal(); board.querySelector("button")?.focus(); }
|
||||
try {
|
||||
dialog.showModal();
|
||||
entries.find(({ entry }) => !entry.disabled)?.entry.focus();
|
||||
}
|
||||
catch (error) { dialog.remove(); restoreFocus(previousFocus); reject(error); }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1100,6 +1100,11 @@ button.mengsan-card-choice-tile-shuying:hover, button.mengsan-card-choice-tile-s
|
||||
.mengsan-card-choice-glyph-shuying { color: #e9d4ae; font: 700 52px/1 "KaiTi",serif; }
|
||||
.mengsan-card-choice-kind-shuying { align-self: center; margin-bottom: 6px; padding: 1px 12px; border: 1px solid #b99a6b; color: #f0dfba; font-size: 12px; }
|
||||
.mengsan-card-choice-description-shuying { display: block !important; flex: 1; overflow: hidden; padding: 2px 3px 0; color: #e8e5d6; font-size: 13px; line-height: 1.55; }
|
||||
.mengsan-reward-tile-action-shuying {
|
||||
display: block !important; margin-top: 12px; padding: 10px 8px;
|
||||
border-top: 1px solid #9e8261; color: #f4d7a0;
|
||||
font: 700 16px/1.4 "KaiTi", "STKaiti", serif;
|
||||
}
|
||||
button.mengsan-card-choice-skip-shuying {
|
||||
min-height: 48px; padding: 9px 28px; border: 1px solid #82978b; border-radius: 3px;
|
||||
color: var(--reward-muted); background: #1a2b2f; font: 16px/1.4 "Microsoft YaHei",sans-serif; cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user