mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-14 02:50:00 +08:00
fixed # 补全部分卡牌
fixed # 修复部分已知BUG
This commit is contained in:
+6
-4
@@ -55,7 +55,8 @@ export function renderBonds(parent, run, saveRun, onBusy = () => {}) {
|
||||
card.className = "mengsan-bond-card-shuying";
|
||||
root.appendChild(card);
|
||||
text("h3", definition.name, card);
|
||||
const ready = Boolean(getBondCombat(id, npc.level));
|
||||
const combat = getBondCombat(id, npc.level);
|
||||
const ready = Boolean(combat);
|
||||
const state = npc.down ? "濒死 · 等待救助" :
|
||||
ready ? "可助战" : "战斗配置待补";
|
||||
text("p", `${state}${npc.down && !ready ?
|
||||
@@ -69,14 +70,14 @@ export function renderBonds(parent, run, saveRun, onBusy = () => {}) {
|
||||
text("p", npc.level === 10 ? "羁绊已满级" :
|
||||
`升级进度 ${npc.progress}% / 100%`, card);
|
||||
const button = text("button", bonds.selected === id ?
|
||||
"已指定助战" : "指定助战", card);
|
||||
"已指定(按概率到场)" : "指定助战", card);
|
||||
button.type = "button";
|
||||
button.setAttribute("aria-pressed", String(bonds.selected === id));
|
||||
button.addEventListener("click", () => choose(id));
|
||||
buttons.push(button);
|
||||
const combat = getBondCombat(id, npc.level);
|
||||
text("p", combat ? `生命 ${combat.maxHp} · 初始手牌 ${combat.hand}` :
|
||||
"生命、初始手牌及技能等待配置。", card);
|
||||
if (combat) text("p", `每回合摸 ${combat.draw} 张 · 手牌上限 ${combat.handLimit} · 每回合费用 ${combat.energy}`, card);
|
||||
text("p", `独立牌库 ${getBondDeck(definition).length} 张 · 意图 ${
|
||||
getBondIntents(definition).map(move => move.name).join(" → ")}`,
|
||||
card);
|
||||
@@ -86,6 +87,7 @@ export function renderBonds(parent, run, saveRun, onBusy = () => {}) {
|
||||
rescue.disabled = true;
|
||||
}
|
||||
}
|
||||
text("p", "指定 NPC 后,每场战斗结束:到场 +50%,未到场 +25%。");
|
||||
text("p", "指定助战后,每场按当前等级概率判定到场;濒死或本等级未配置完整时无法上场。角色专属首战不触发助战,也不累计羁绊成长。");
|
||||
text("p", "其余战斗完成结算:到场 +50%,未到场 +25%。");
|
||||
return root;
|
||||
}
|
||||
|
||||
+107
-31
@@ -1,11 +1,17 @@
|
||||
import { lib, get, ui } from "../../../../noname.js";
|
||||
import { cardDefinitions } from "../cards/card-definitions.js";
|
||||
import { cardTypes, cardRarityLabels } from "../cards/classification-types.js";
|
||||
import { DAZED_NAME } from "../cards/status-cards.js";
|
||||
import { SLIMED_NAME } from "../cards/slimed-card.js";
|
||||
import { cardCost } from "../battle/combat-rules.js";
|
||||
import { AFFIX_INFO } from "../cards/affixes.js";
|
||||
import { cardAffixes } from "../cards/intrinsic-affixes.js";
|
||||
import { cardUpgradeLevel, cardUpgradeRule } from "../cards/upgrades.js";
|
||||
|
||||
import { getCardRarity, applyCardRarity } from "../cards/rarity.js";
|
||||
import { cleanCardDescription } from "../cards/description.js";
|
||||
import { createVirtualCardGrid } from "./virtual-card-grid.js";
|
||||
|
||||
const suits = { spade: "♠ 黑桃", heart: "♥ 红桃", club: "♣ 梅花", diamond: "♦ 方块" };
|
||||
const types = { basic: "基本牌", trick: "锦囊牌", delay: "延时锦囊", equip: "装备牌", status: "状态牌" };
|
||||
const affixes = Object.fromEntries(Object.entries(AFFIX_INFO).map(([key, info]) => [key, `${info.name}:${info.description}`]));
|
||||
@@ -23,12 +29,13 @@ export function describeLibraryCard(card) {
|
||||
return {
|
||||
name: translate(card.name), suit: suits[card.suit] || "无花色",
|
||||
number: ({ 1: "A", 11: "J", 12: "Q", 13: "K" })[card.number] || String(card.number ?? "—"),
|
||||
nature: nature || "无属性", type: types[info?.type] || "未知类别",
|
||||
nature: nature || "无属性", type: cardTypes[cardDefinitions[card.name]?.cardType || info?.cardType] || types[info?.type] || "未知类别",
|
||||
red: card.suit === "heart" || card.suit === "diamond",
|
||||
description: text(cardUpgradeRule(card.name) && info?.cardPrompt
|
||||
description: cleanCardDescription(text((cardUpgradeRule(card.name)||info?.mengsanShared_shuying) && info?.cardPrompt
|
||||
? info.cardPrompt(card)
|
||||
: lib.translate[`${card.name}_info`] || "暂无卡牌介绍。"),
|
||||
affixes: (Array.isArray(card.affixes) ? card.affixes : []).filter(key => key !== "annihilate").map(key => affixes[key] || `词缀:${translate(key)}`),
|
||||
: lib.translate[`${card.name}_info`] || "暂无卡牌介绍。")),
|
||||
affixes: cardAffixes(card).filter(key => key !== "annihilate").map(key => affixes[key] || `词缀:${translate(key)}`),
|
||||
rarity: getCardRarity(card),
|
||||
upgrade: cardUpgradeLevel(card),
|
||||
upgradeLimit: cardUpgradeRule(card.name)?.maxLevel || 0,
|
||||
available: Boolean(info),
|
||||
@@ -44,14 +51,14 @@ export function getCardCatalog() {
|
||||
.map(name => ({ name, nature: null, affixes: [], upgrade: 0 }));
|
||||
}
|
||||
|
||||
const categories = { damage: "攻击", utility: "辅助", recovery: "恢复", status: "状态" };
|
||||
const categories = { ...cardTypes, unknown: "未分类" };
|
||||
function describeCatalogCard(card) {
|
||||
const details = describeLibraryCard(card), info = lib.card[card.name];
|
||||
const definition = cardDefinitions[card.name];
|
||||
const category = info?.type === "status" || card.name === DAZED_NAME || card.name === SLIMED_NAME
|
||||
? "status" : definition?.category || "utility";
|
||||
? "status" : definition?.cardType || info?.cardType || "unknown";
|
||||
const unplayable = info?.enable === false;
|
||||
const cost = info && !unplayable ? cardCost(card) : null;
|
||||
const cost = info && !unplayable ? (info.mengsanXCost_shuying ? "X" : cardCost(card)) : null;
|
||||
return { ...details, category, categoryName: categories[category], cost, unplayable,
|
||||
costLabel: !info ? "未加载" : unplayable ? "不可打出" : `${cost}费`,
|
||||
exclusive: definition?.owner ? translate(definition.owner) : "" };
|
||||
@@ -75,9 +82,11 @@ function button(label, parent, action, className = "ms-deck-button") {
|
||||
// Read-only DOM previews, never game.createCard or Card.init: no IDs, triggers or pile writes.
|
||||
function preview(card, details, parent, catalog = false) {
|
||||
const face = element("div", "ms-deck-face", null, parent);
|
||||
applyCardRarity(face, card);
|
||||
element("span", `ms-deck-corner${details.red ? " ms-deck-red" : ""}`, catalog ? details.costLabel : `${details.suit} ${details.number}`, face);
|
||||
const art = element("div", "ms-deck-art", null, face);
|
||||
element("span", "ms-deck-glyph", details.name, art);
|
||||
if (card.name === "mengsan_infection_shuying") art.dataset.mengsanOverlay = "infection";
|
||||
const glyph = element("span", "ms-deck-glyph", details.name, art);
|
||||
const imageName = lib.card[card.name]?.cardimage || card.name;
|
||||
const modeImage = lib.card[card.name]?.image;
|
||||
const localArt = typeof modeImage === "string" &&
|
||||
@@ -88,11 +97,15 @@ function preview(card, details, parent, catalog = false) {
|
||||
const image = element("img", "ms-deck-image", null, art);
|
||||
image.alt = "";
|
||||
image.loading = "lazy";
|
||||
image.addEventListener("error", () => image.remove(), { once: true });
|
||||
image.decoding = "async";
|
||||
image.addEventListener("load", () => { glyph.hidden = true; }, { once: true });
|
||||
image.addEventListener("error", () => { image.remove(); glyph.hidden = false; }, { once: true });
|
||||
image.src = `${lib.assetURL || ""}${localArt || `image/card/${imageName}.png`}`;
|
||||
}
|
||||
element("strong", "ms-deck-name", details.name, face);
|
||||
element("span", "ms-deck-meta", catalog ? `${details.categoryName} · ${details.exclusive ? "专属:" + details.exclusive : details.category === "status" ? "战斗生成" : "通用"}` : `${details.type} · ${details.nature}`, face);
|
||||
element("span", "ms-deck-meta", catalog ? `${details.categoryName} · ${details.exclusive ? "专属:" + details.exclusive : cardDefinitions[card.name]?.generatedOnly ? "战斗生成" : "通用"}` : `${details.type} · ${details.nature}`, face);
|
||||
face.setAttribute("aria-label", `${details.name},稀有度:${details.rarity.label}`);
|
||||
face.title = `稀有度:${details.rarity.label}`;
|
||||
if (details.upgrade || details.affixes.length) {
|
||||
element("span", "ms-deck-tags", [details.upgrade ? `+${details.upgrade}` : "", ...details.affixes.map(value => value.split(":")[0])].filter(Boolean).join(" · "), face);
|
||||
}
|
||||
@@ -109,10 +122,14 @@ export function openCardLibrary(run, options = {}) {
|
||||
const title = element("div", "ms-deck-title-group", null, header);
|
||||
element("span", "ms-deck-eyebrow", options.eyebrow || "梦三 · 行囊", title);
|
||||
element("h2", "ms-deck-title", options.title || "卡牌库", title);
|
||||
let closed = false;
|
||||
let closed = false, sizeObserver = null, virtualGrid = null, searchTimer = 0;
|
||||
const close = () => {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
clearTimeout(searchTimer);
|
||||
virtualGrid?.dispose();
|
||||
sizeObserver?.disconnect();
|
||||
if (typeof window !== "undefined") window.removeEventListener?.("resize", fitCards);
|
||||
if (dialog.open) dialog.close();
|
||||
dialog.remove();
|
||||
if (previousFocus?.isConnected) previousFocus.focus();
|
||||
@@ -137,17 +154,26 @@ export function openCardLibrary(run, options = {}) {
|
||||
for (const [key, label] of Object.entries({ "": "全部类型", ...categories })) {
|
||||
const option = element("option", "", label, categoryFilter); option.value = key;
|
||||
}
|
||||
const rarityFilter = element("select", "ms-catalog-select", null, field("稀有度"));
|
||||
rarityFilter.setAttribute("aria-label", "图鉴稀有度");
|
||||
for (const [key, label] of Object.entries({ "": "全部稀有度", ...cardRarityLabels })) {
|
||||
const option = element("option", "", label, rarityFilter); option.value = key;
|
||||
}
|
||||
const costFilter = element("select", "ms-catalog-select", null, field("费用"));
|
||||
costFilter.setAttribute("aria-label", "图鉴费用");
|
||||
for (const [key, label] of [["", "全部费用"], ["0", "0费"], ["1", "1费"], ["2", "2费"], ["3+", "3费及以上"], ["unplayable", "不可打出"]]) {
|
||||
for (const [key, label] of [["", "全部费用"], ["0", "0费"], ["1", "1费"], ["2", "2费"], ["3+", "3费及以上"], ["X", "X费"], ["unplayable", "不可打出"]]) {
|
||||
const option = element("option", "", label, costFilter); option.value = key;
|
||||
}
|
||||
button("清空筛选", filters, () => {
|
||||
search.value = ""; categoryFilter.value = ""; costFilter.value = "";
|
||||
search.value = ""; categoryFilter.value = ""; rarityFilter.value = ""; costFilter.value = "";
|
||||
render(); search.focus();
|
||||
});
|
||||
search.addEventListener("input", render);
|
||||
search.addEventListener("input", () => {
|
||||
clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(render, 140);
|
||||
});
|
||||
categoryFilter.addEventListener("change", render);
|
||||
rarityFilter.addEventListener("change", render);
|
||||
costFilter.addEventListener("change", render);
|
||||
const summary = element("p", "ms-deck-summary", null, dialog);
|
||||
summary.setAttribute("aria-live", "polite");
|
||||
@@ -155,15 +181,20 @@ export function openCardLibrary(run, options = {}) {
|
||||
const footer = element("p", "ms-deck-footer", "点击卡牌查看完整介绍 · 仅供查看,不改变牌组或抽牌顺序", dialog);
|
||||
const current = Array.isArray(run?.player?.deck) ? run.player.deck : [];
|
||||
let detail = false, selectedButton = null, scrollTop = 0;
|
||||
const list = element("div", "ms-deck-grid", null, body);
|
||||
const list = element("div", options.sections ? "ms-deck-grid" : "ms-deck-grid ms-library-grid", null, body);
|
||||
const detailPanel = element("section", "ms-deck-detail", null, body);
|
||||
detailPanel.hidden = true;
|
||||
let catalogRecords = null, descriptionCache = new WeakMap();
|
||||
if (!options.sections) virtualGrid = createVirtualCardGrid(body, list, createTile);
|
||||
function back() {
|
||||
detail = false; detailPanel.hidden = true; list.hidden = false;
|
||||
detailPanel.replaceChildren(); body.scrollTop = scrollTop;
|
||||
selectedButton?.focus({ preventScroll: true });
|
||||
detailPanel.replaceChildren(); body.scrollTop = scrollTop; fitCards();
|
||||
virtualGrid?.resume();
|
||||
if (selectedButton?.isConnected) selectedButton.focus({ preventScroll: true });
|
||||
else if (virtualGrid && selectedButton) virtualGrid.focus(Number(selectedButton.dataset.cardIndex));
|
||||
}
|
||||
function showDetail(card, details, owner, catalog = false) {
|
||||
clearTimeout(searchTimer);
|
||||
selectedButton = owner; scrollTop = body.scrollTop;
|
||||
detail = true; list.hidden = true; detailPanel.hidden = false;
|
||||
detailPanel.replaceChildren();
|
||||
@@ -172,18 +203,45 @@ export function openCardLibrary(run, options = {}) {
|
||||
preview(card, details, content, catalog);
|
||||
const copy = element("div", "ms-deck-detail-copy", null, content);
|
||||
element("h3", "", details.name, copy);
|
||||
element("p", "", catalog ? `${details.costLabel} · ${details.categoryName} · ${details.type}` :
|
||||
element("p", "", catalog ? `${details.costLabel} · ${details.categoryName}` :
|
||||
`${details.suit} ${details.number} · ${details.type} · ${details.nature}`, copy);
|
||||
if (catalog && details.exclusive) element("p", "ms-deck-rules", `专属角色:${details.exclusive}`, copy);
|
||||
element("p", "ms-deck-rarity-label", `稀有度:${details.rarity.label}`, copy);
|
||||
element("h4", "", "卡牌介绍", copy);
|
||||
element("p", "ms-deck-rules", details.description, copy);
|
||||
if (!details.available) element("p", "ms-deck-warning", "该卡牌定义未加载;保留存档记录,不删除此牌。", copy);
|
||||
for (const affix of details.affixes) element("p", "ms-deck-rules", affix, copy);
|
||||
body.scrollTop = 0; backButton.focus();
|
||||
}
|
||||
function fitCards() {
|
||||
if (options.sections || closed || !body.clientHeight) return;
|
||||
const padding = getComputedStyle(body);
|
||||
const available = body.clientHeight - parseFloat(padding.paddingTop) - parseFloat(padding.paddingBottom);
|
||||
const height = Math.max(64, Math.min(218, Math.floor((available - 8) / 2)));
|
||||
dialog.style.setProperty("--ms-card-height", String(height) + "px");
|
||||
dialog.style.setProperty("--ms-card-art-width", String(Math.max(8, Math.min(98, Math.floor((height - 80) * 5 / 7)))) + "px");
|
||||
virtualGrid?.layout(height);
|
||||
}
|
||||
function createTile(record, index) {
|
||||
const { card, catalog = false, ordered = false } = record;
|
||||
let details = record.details || descriptionCache.get(card);
|
||||
if (!details) {
|
||||
details = describeLibraryCard(card); descriptionCache.set(card, details);
|
||||
}
|
||||
const tile = button("", null, () => showDetail(card, details, tile, catalog), "ms-deck-card");
|
||||
tile.setAttribute("aria-label", catalog ? `${details.name},${details.categoryName},${details.costLabel},查看详情` :
|
||||
`${ordered ? `第 ${index + 1} 张,` : ""}${details.name},${details.suit}${details.number},${details.nature},查看详情`);
|
||||
if (ordered) element("span", "ms-deck-order", index === 0 ? "1 · 下一张" : `${index + 1}`, tile);
|
||||
preview(card, details, tile, catalog);
|
||||
element("span", "ms-deck-excerpt", details.description, tile);
|
||||
return tile;
|
||||
}
|
||||
function render() {
|
||||
clearTimeout(searchTimer);
|
||||
detail = false; list.hidden = false; detailPanel.hidden = true;
|
||||
detailPanel.replaceChildren(); list.replaceChildren(); body.scrollTop = 0;
|
||||
detailPanel.replaceChildren();
|
||||
if (!virtualGrid) list.replaceChildren();
|
||||
body.scrollTop = 0;
|
||||
selectedButton = null;
|
||||
const catalog = !options.sections && view === "catalog";
|
||||
filters.hidden = !catalog;
|
||||
@@ -193,21 +251,36 @@ export function openCardLibrary(run, options = {}) {
|
||||
let records = [];
|
||||
if (catalog) {
|
||||
const query = search.value.trim().toLocaleLowerCase();
|
||||
records = getCardCatalog().map(card => ({ card, details: describeCatalogCard(card) }));
|
||||
catalogRecords ||= getCardCatalog().map(card => {
|
||||
const details = describeCatalogCard(card);
|
||||
return { card, catalog: true, details,
|
||||
searchText: `${details.name} ${card.name} ${details.description}`.toLocaleLowerCase() };
|
||||
});
|
||||
records = catalogRecords;
|
||||
const total = records.length;
|
||||
records = records.filter(({ card, details }) =>
|
||||
(!query || `${details.name} ${card.name} ${details.description}`.toLocaleLowerCase().includes(query)) &&
|
||||
records = records.filter(({ details, searchText }) =>
|
||||
(!query || searchText.includes(query)) &&
|
||||
(!categoryFilter.value || details.category === categoryFilter.value) &&
|
||||
(!rarityFilter.value || details.rarity.key === rarityFilter.value) &&
|
||||
(!costFilter.value || (costFilter.value === "unplayable" ? details.unplayable :
|
||||
costFilter.value === "3+" ? details.cost !== null && details.cost >= 3 :
|
||||
costFilter.value === "X" ? details.cost === "X" :
|
||||
costFilter.value === "3+" ? typeof details.cost === "number" && details.cost >= 3 :
|
||||
details.cost !== null && details.cost === Number(costFilter.value))));
|
||||
summary.textContent = `梦三卡牌图鉴 · 共 ${total} 种 · 当前显示 ${records.length} 种`;
|
||||
summary.textContent = `卡牌图鉴 · 共 ${total} 种 · 当前显示 ${records.length} 种`;
|
||||
} else {
|
||||
summary.textContent = options.sections ? "剩余牌堆按从左到右、从上到下的顺序摸取;第 1 张为下一张。" :
|
||||
`本次征程实际携带 · 共 ${current.length} 张 · 同名不同花色、点数分别展示`;
|
||||
}
|
||||
footer.textContent = catalog ? "图鉴仅供查阅,不会获得卡牌或改变征程牌组 · 点击卡牌查看完整介绍" :
|
||||
"点击卡牌查看完整介绍 · 仅供查看,不改变牌组或抽牌顺序";
|
||||
if (virtualGrid) {
|
||||
virtualGrid.setRecords(catalog ? records : current.map(card => ({ card })));
|
||||
if (!(catalog ? records : current).length) {
|
||||
element("p", "ms-deck-empty", catalog ? "没有符合筛选的卡牌,可清空筛选后查看全部图鉴。" : "此处暂无卡牌。", list);
|
||||
}
|
||||
fitCards();
|
||||
return;
|
||||
}
|
||||
const sections = catalog ? [{ title: "", cards: records.map(record => record.card) }] :
|
||||
options.sections ? options.sections() : [{ title: "", cards: current }];
|
||||
for (const section of sections) {
|
||||
@@ -215,13 +288,7 @@ export function openCardLibrary(run, options = {}) {
|
||||
if (section.title) element("h3", "ms-deck-section", `${section.title} · ${cards.length} 张`, list);
|
||||
if (!cards.length) element("p", "ms-deck-empty", catalog ? "没有符合筛选的卡牌,可清空筛选后查看全部图鉴。" : section.ordered ? "剩余牌堆为空;下一次摸牌需按规则洗切弃牌堆,洗切后的顺序尚未确定。" : "此处暂无卡牌。", list);
|
||||
cards.forEach((card, index) => {
|
||||
const details = catalog ? records[index].details : describeLibraryCard(card);
|
||||
const tile = button("", list, () => showDetail(card, details, tile, catalog), "ms-deck-card");
|
||||
tile.setAttribute("aria-label", catalog ? `${details.name},${details.categoryName},${details.costLabel},查看详情` :
|
||||
`${section.ordered ? `第 ${index + 1} 张,` : ""}${details.name},${details.suit}${details.number},${details.nature},查看详情`);
|
||||
if (section.ordered) element("span", "ms-deck-order", index === 0 ? "1 · 下一张" : `${index + 1}`, tile);
|
||||
preview(card, details, tile, catalog);
|
||||
element("span", "ms-deck-excerpt", details.description, tile);
|
||||
list.appendChild(createTile({ card, ordered: section.ordered }, index));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -232,12 +299,21 @@ export function openCardLibrary(run, options = {}) {
|
||||
render();
|
||||
document.body.appendChild(dialog);
|
||||
try { dialog.showModal(); } catch (error) { close(); throw error; }
|
||||
fitCards();
|
||||
if (!options.sections && typeof ResizeObserver !== "undefined") {
|
||||
sizeObserver = new ResizeObserver(fitCards);
|
||||
sizeObserver.observe(body);
|
||||
} else if (!options.sections && typeof window !== "undefined") {
|
||||
window.addEventListener?.("resize", fitCards);
|
||||
}
|
||||
header.querySelector("button").focus();
|
||||
close.refresh = () => {
|
||||
if (closed) return;
|
||||
// Rebuild from live arrays after a pile mutation, including while viewing details.
|
||||
const wasDetail = detail, offset = body.scrollTop;
|
||||
catalogRecords = null; descriptionCache = new WeakMap();
|
||||
render(); body.scrollTop = offset;
|
||||
virtualGrid?.resume();
|
||||
if (wasDetail) header.querySelector("button").focus();
|
||||
};
|
||||
return close;
|
||||
|
||||
@@ -48,7 +48,7 @@ export const chooseButtons = (title, choices, description = "", options = {}) =>
|
||||
button.addEventListener("click", () => finish(choice.id));
|
||||
buttons.appendChild(button);
|
||||
});
|
||||
mountMenu(overlay);
|
||||
if (options.menu !== false) mountMenu(overlay);
|
||||
overlay.addEventListener("keydown", event => {
|
||||
if (event.key === "Escape" && options.back) {
|
||||
event.preventDefault(); finish(options.back); return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AFFIX_INFO } from "../cards/affixes.js";
|
||||
import { hasCardAffix } from "../cards/intrinsic-affixes.js";
|
||||
import { describeLibraryCard } from "./card-library.js";
|
||||
|
||||
const node = (tag, className, label, parent) => {
|
||||
@@ -49,7 +50,7 @@ export function mountGMManager(session, owner, piles, battle, resources, game) {
|
||||
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 applied = hasCardAffix(selected,key);
|
||||
const item = button(`${info.name}${applied ? " · 已有" : ""}`, choices, () => {
|
||||
if (!available() || !owner.getCards("h").includes(selected)) { render(); return; }
|
||||
piles.addAffix(selected, key);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { cardUpgradeLevel } from "../../cards/upgrades.js";
|
||||
import { isXCostCard } from "../../battle/combat-rules.js";
|
||||
import { applyCardRarity } from "../../cards/rarity.js";
|
||||
|
||||
export function createHandOverlays(document, cardCost) {
|
||||
const entries = new Map();
|
||||
@@ -15,14 +17,31 @@ export function createHandOverlays(document, cardCost) {
|
||||
const upgrade = document.createElement("span");
|
||||
upgrade.className = "mengsan-hand-upgrade-shuying";
|
||||
upgrade.setAttribute("role", "img");
|
||||
entry = { cost, upgrade };
|
||||
const rarity = document.createElement("span");
|
||||
rarity.className = "mengsan-hand-rarity-shuying";
|
||||
rarity.setAttribute("role", "img");
|
||||
entry = { cost, upgrade, rarity, border: null };
|
||||
entries.set(card, entry);
|
||||
}
|
||||
const cost = String(cardCost(card));
|
||||
const rarity = applyCardRarity(card, card);
|
||||
entry.rarity.setAttribute("aria-label", `稀有度:${rarity.label}`);
|
||||
if (entry.rarity.parentNode !== card) card.appendChild(entry.rarity);
|
||||
const cost = isXCostCard(card) ? "X" : String(cardCost(card));
|
||||
setData(entry.cost, "mengsanCost", cost);
|
||||
entry.cost.setAttribute("aria-label", `费用 ${cost}`);
|
||||
if (card.name === "mengsan_dazed_shuying") entry.cost.remove();
|
||||
else if (entry.cost.parentNode !== card) card.appendChild(entry.cost);
|
||||
// Empty decorative node: Decade UI scans child innerText when cards move.
|
||||
if (card.name === "mengsan_infection_shuying") {
|
||||
if (!entry.border) {
|
||||
entry.border = document.createElement("span");
|
||||
entry.border.className = "mengsan-infection-border-shuying";
|
||||
entry.border.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
if (entry.border.parentNode !== card) card.appendChild(entry.border);
|
||||
} else {
|
||||
entry.border?.remove();
|
||||
}
|
||||
const level = cardUpgradeLevel(card);
|
||||
if (level) {
|
||||
setData(entry.upgrade, "mengsanUpgrade", String(level));
|
||||
@@ -37,6 +56,11 @@ export function createHandOverlays(document, cardCost) {
|
||||
if (!entry) return;
|
||||
entry.cost.remove();
|
||||
entry.upgrade.remove();
|
||||
entry.border?.remove();
|
||||
entry.rarity.remove();
|
||||
delete card.dataset.mengsanRarity;
|
||||
delete card.dataset.mengsanRarityLabel;
|
||||
for (const key of ["accent", "dark", "ink", "metal"]) card.style.removeProperty(`--ms-rarity-${key}`);
|
||||
entries.delete(card);
|
||||
}
|
||||
return {
|
||||
|
||||
+212
-12
@@ -187,12 +187,12 @@ body.mengsan-hand-ui-shuying :is(
|
||||
}
|
||||
}
|
||||
|
||||
/* 梦三本地竖幅立绘使用完整卡面;只协调梦三手牌里的原生显示节点。 */
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip) {
|
||||
/* 梦三本地竖幅立绘:刘备及四类共享牌统一使用完整卡面;只协调梦三手牌里的原生显示节点。 */
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip) {
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
display: block !important;
|
||||
top: auto !important;
|
||||
bottom: 8px !important;
|
||||
@@ -214,14 +214,14 @@ body.mengsan-hand-ui-shuying :is(
|
||||
z-index: 7 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) br {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) br {
|
||||
display: none;
|
||||
}
|
||||
/* 十周年同时保留竖排名称与 top-name 时,只显示后者。 */
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip):has(> .top-name) > .name {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip):has(> .top-name) > .name {
|
||||
display: none !important;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip) > :is(.info:not(.gaintag), .suit-num) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip) > :is(.info:not(.gaintag), .suit-num) {
|
||||
top: 6px !important;
|
||||
left: 6px !important;
|
||||
right: auto !important;
|
||||
@@ -241,17 +241,17 @@ body.mengsan-hand-ui-shuying :is(
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]) > .image,
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]) > .info:empty {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]) > .image,
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]) > .info:empty {
|
||||
display: none !important;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):is([data-suit="heart"], [data-suit="diamond"]):not(.infohidden):not(.infoflip) > :is(.info:not(.gaintag), .suit-num) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):is([data-suit="heart"], [data-suit="diamond"]):not(.infohidden):not(.infoflip) > :is(.info:not(.gaintag), .suit-num) {
|
||||
color: #a52219 !important;
|
||||
}
|
||||
|
||||
/* 强化卡牌的完整卡面样式优先级更高,窄屏同样为角标留出空隙。 */
|
||||
@media (max-width: 640px), (max-height: 500px) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"]):not(.infohidden):not(.infoflip):has(> .mengsan-hand-upgrade-shuying) > .suit-num {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name^="mengsan_ic_"], [data-card-name^="mengsan_colorless_"], [data-card-name^="mengsan_event_"], [data-card-name^="mengsan_status_"], [data-card-name^="mengsan_curse_"], [data-card-name="mengsan_zhongsha"], [data-card-name="mengsan_fangyu"], [data-card-name="mengsan_fennu"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"], [data-card-name="mengsan_wuzhuang"], [data-card-name="mengsan_zhanli"], [data-card-name="mengsan_xueqiang"], [data-card-name="mengsan_qiling"], [data-card-name="mengsan_yuanhen"], [data-card-name="mengsan_baozou"], [data-card-name="mengsan_xuanfengzhan"], [data-card-name="mengsan_dazed_shuying"], [data-card-name="mengsan_slimed_shuying"], [data-card-name="mengsan_infection_shuying"], [data-card-name="mengsan_wound_shuying"]):not(.infohidden):not(.infoflip):has(> .mengsan-hand-upgrade-shuying) > .suit-num {
|
||||
top: 4px !important;
|
||||
padding: 0 2px !important;
|
||||
font-size: 18px !important;
|
||||
@@ -260,14 +260,214 @@ body.mengsan-hand-ui-shuying :is(
|
||||
}
|
||||
|
||||
/* 长牌名完整显示,不挤出卡面;只作用于梦三手牌。 */
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_ic_ashen_strike"], [data-card-name="mengsan_ic_fight_me"], [data-card-name="mengsan_ic_demonic_shield"], [data-card-name="mengsan_ic_forgotten_ritual"], [data-card-name="mengsan_ic_burning_pact"], [data-card-name="mengsan_ic_second_wind"], [data-card-name="mengsan_ic_pacts_end"], [data-card-name="mengsan_ic_fiend_fire"], [data-card-name="mengsan_ic_impervious"], [data-card-name="mengsan_ic_not_yet"], [data-card-name="mengsan_ic_demon_form"], [data-card-name="mengsan_ic_pyre"], [data-card-name="mengsan_ic_crimson_mantle"], [data-card-name="mengsan_ic_flame_barrier"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 12px !important;
|
||||
letter-spacing: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
@media (max-width: 640px), (max-height: 500px) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_ic_ashen_strike"], [data-card-name="mengsan_ic_fight_me"], [data-card-name="mengsan_ic_demonic_shield"], [data-card-name="mengsan_ic_forgotten_ritual"], [data-card-name="mengsan_ic_burning_pact"], [data-card-name="mengsan_ic_second_wind"], [data-card-name="mengsan_ic_pacts_end"], [data-card-name="mengsan_ic_fiend_fire"], [data-card-name="mengsan_ic_impervious"], [data-card-name="mengsan_ic_not_yet"], [data-card-name="mengsan_ic_demon_form"], [data-card-name="mengsan_ic_pyre"], [data-card-name="mengsan_ic_crimson_mantle"], [data-card-name="mengsan_ic_flame_barrier"], [data-card-name="mengsan_feijianhuixuanbiao"], [data-card-name="mengsan_jianbingdaji"], [data-card-name="mengsan_quanshenzhuangji"], [data-card-name="mengsan_rongrongzhiquan"], [data-card-name="mengsan_shandianpili"], [data-card-name="mengsan_shuangchongdaji"], [data-card-name="mengsan_tiezhanbo"], [data-card-name="mengsan_touchui"], [data-card-name="mengsan_tupo"], [data-card-name="mengsan_yubeidaji"], [data-card-name="mengsan_wanmeidaji"], [data-card-name="mengsan_yujin"], [data-card-name="mengsan_fangxue"], [data-card-name="mengsan_jianyi"], [data-card-name="mengsan_pomie"], [data-card-name="mengsan_songjianwushi"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 10px !important;
|
||||
padding: 2px 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Infection only; keep cost/name above it and leave native card::after alone. */
|
||||
.mengsan-infection-border-shuying { display: none; }
|
||||
.mengsan-hand-zone-shuying > .card[data-card-name="mengsan_infection_shuying"]:not(.infohidden):not(.infoflip) > .mengsan-infection-border-shuying {
|
||||
display: block;
|
||||
z-index: 5;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
background-image: url("../../assets/card-overlays/infection-sprite.webp");
|
||||
/* 30 source frames, six columns/five rows, 192x260 per cell. */
|
||||
background-size: 600% 500%;
|
||||
background-position: 0% 0%;
|
||||
background-repeat: no-repeat;
|
||||
animation: mengsan-infection-frames-shuying 2s steps(1, end) infinite;
|
||||
}
|
||||
@keyframes mengsan-infection-frames-shuying {
|
||||
0% { background-position: 0% 0%; }
|
||||
3.333333% { background-position: 20% 0%; }
|
||||
6.666667% { background-position: 40% 0%; }
|
||||
10% { background-position: 60% 0%; }
|
||||
13.333333% { background-position: 80% 0%; }
|
||||
16.666667% { background-position: 100% 0%; }
|
||||
20% { background-position: 0% 25%; }
|
||||
23.333333% { background-position: 20% 25%; }
|
||||
26.666667% { background-position: 40% 25%; }
|
||||
30% { background-position: 60% 25%; }
|
||||
33.333333% { background-position: 80% 25%; }
|
||||
36.666667% { background-position: 100% 25%; }
|
||||
40% { background-position: 0% 50%; }
|
||||
43.333333% { background-position: 20% 50%; }
|
||||
46.666667% { background-position: 40% 50%; }
|
||||
50% { background-position: 60% 50%; }
|
||||
53.333333% { background-position: 80% 50%; }
|
||||
56.666667% { background-position: 100% 50%; }
|
||||
60% { background-position: 0% 75%; }
|
||||
63.333333% { background-position: 20% 75%; }
|
||||
66.666667% { background-position: 40% 75%; }
|
||||
70% { background-position: 60% 75%; }
|
||||
73.333333% { background-position: 80% 75%; }
|
||||
76.666667% { background-position: 100% 75%; }
|
||||
80% { background-position: 0% 100%; }
|
||||
83.333333% { background-position: 20% 100%; }
|
||||
86.666667% { background-position: 40% 100%; }
|
||||
90% { background-position: 60% 100%; }
|
||||
93.333333% { background-position: 80% 100%; }
|
||||
96.666667% { background-position: 100% 100%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mengsan-hand-zone-shuying > .card[data-card-name="mengsan_infection_shuying"]:not(.infohidden):not(.infoflip) > .mengsan-infection-border-shuying {
|
||||
animation: none;
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 第二批刘备牌组长牌名,沿用现有手牌字号。 */
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_ic_relentless_assault"], [data-card-name="mengsan_ic_howl_from_beyond"], [data-card-name="mengsan_ic_battle_trance"], [data-card-name="mengsan_ic_infernal_blade"], [data-card-name="mengsan_ic_eager"], [data-card-name="mengsan_ic_feel_no_pain"], [data-card-name="mengsan_ic_stone_armor"], [data-card-name="mengsan_ic_primal_force"], [data-card-name="mengsan_ic_battle_ready"], [data-card-name="mengsan_ic_hellraiser"], [data-card-name="mengsan_ic_dark_embrace"], [data-card-name="mengsan_ic_unshakable"], [data-card-name="mengsan_ic_juggernaut"], [data-card-name="mengsan_ic_rally"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 12px !important; letter-spacing: 0 !important; white-space: nowrap !important;
|
||||
}
|
||||
@media (max-width: 640px), (max-height: 500px) {
|
||||
.mengsan-hand-zone-shuying > .card:is([data-card-name="mengsan_ic_relentless_assault"], [data-card-name="mengsan_ic_howl_from_beyond"], [data-card-name="mengsan_ic_battle_trance"], [data-card-name="mengsan_ic_infernal_blade"], [data-card-name="mengsan_ic_eager"], [data-card-name="mengsan_ic_feel_no_pain"], [data-card-name="mengsan_ic_stone_armor"], [data-card-name="mengsan_ic_primal_force"], [data-card-name="mengsan_ic_battle_ready"], [data-card-name="mengsan_ic_hellraiser"], [data-card-name="mengsan_ic_dark_embrace"], [data-card-name="mengsan_ic_unshakable"], [data-card-name="mengsan_ic_juggernaut"], [data-card-name="mengsan_ic_rally"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 10px !important; padding: 2px 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 四类共享牌的长名称与刘备牌组采用相同字号,窄屏仍完整显示。 */
|
||||
.mengsan-hand-zone-shuying > .card:is(
|
||||
[data-card-name="mengsan_colorless_dramatic_entrance"],
|
||||
[data-card-name="mengsan_colorless_ultimate_strike"],
|
||||
[data-card-name="mengsan_colorless_gang_up"],
|
||||
[data-card-name="mengsan_colorless_seeker_strike"],
|
||||
[data-card-name="mengsan_colorless_thrumming_hatchet"],
|
||||
[data-card-name="mengsan_colorless_mind_blast"],
|
||||
[data-card-name="mengsan_colorless_tag_team"],
|
||||
[data-card-name="mengsan_colorless_dark_shackles"],
|
||||
[data-card-name="mengsan_colorless_jack_of_all_trades"],
|
||||
[data-card-name="mengsan_colorless_thinking_ahead"],
|
||||
[data-card-name="mengsan_colorless_production"],
|
||||
[data-card-name="mengsan_colorless_believe_in_you"],
|
||||
[data-card-name="mengsan_colorless_restlessness"],
|
||||
[data-card-name="mengsan_colorless_panic_button"],
|
||||
[data-card-name="mengsan_colorless_ultimate_defend"],
|
||||
[data-card-name="mengsan_colorless_coordinate"],
|
||||
[data-card-name="mengsan_colorless_panache"],
|
||||
[data-card-name="mengsan_colorless_prowess"],
|
||||
[data-card-name="mengsan_colorless_prep_time"],
|
||||
[data-card-name="mengsan_colorless_hand_of_greed"],
|
||||
[data-card-name="mengsan_colorless_the_gambit"],
|
||||
[data-card-name="mengsan_colorless_secret_technique"],
|
||||
[data-card-name="mengsan_colorless_secret_weapon"],
|
||||
[data-card-name="mengsan_colorless_master_of_strategy"],
|
||||
[data-card-name="mengsan_colorless_alchemize"],
|
||||
[data-card-name="mengsan_colorless_scrawl"],
|
||||
[data-card-name="mengsan_colorless_hidden_gem"],
|
||||
[data-card-name="mengsan_colorless_beacon_of_hope"],
|
||||
[data-card-name="mengsan_colorless_eternal_armor"],
|
||||
[data-card-name="mengsan_event_byrd_swoop"],
|
||||
[data-card-name="mengsan_event_mad_science_sapping"],
|
||||
[data-card-name="mengsan_event_mad_science_violence"],
|
||||
[data-card-name="mengsan_event_mad_science_choking"],
|
||||
[data-card-name="mengsan_event_feeding_frenzy"],
|
||||
[data-card-name="mengsan_event_mad_science_energized"],
|
||||
[data-card-name="mengsan_event_mad_science_wisdom"],
|
||||
[data-card-name="mengsan_event_mad_science_chaos"],
|
||||
[data-card-name="mengsan_event_toric_toughness"],
|
||||
[data-card-name="mengsan_event_mad_science_expertise"],
|
||||
[data-card-name="mengsan_event_mad_science_curious"],
|
||||
[data-card-name="mengsan_event_mad_science_improvement"],
|
||||
[data-card-name="mengsan_event_neows_fury"],
|
||||
[data-card-name="mengsan_event_brightest_flame"],
|
||||
[data-card-name="mengsan_status_frantic_escape"],
|
||||
[data-card-name="mengsan_curse_spore_mind"],
|
||||
[data-card-name="mengsan_curse_ascenders_bane"],
|
||||
[data-card-name="mengsan_curse_curse_of_the_bell"],
|
||||
[data-card-name="mengsan_curse_poor_sleep"],
|
||||
[data-card-name="mengsan_status_deprecated_card"]
|
||||
):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 12px !important;
|
||||
letter-spacing: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
@media (max-width: 640px), (max-height: 500px) {
|
||||
.mengsan-hand-zone-shuying > .card:is(
|
||||
[data-card-name="mengsan_colorless_dramatic_entrance"],
|
||||
[data-card-name="mengsan_colorless_ultimate_strike"],
|
||||
[data-card-name="mengsan_colorless_gang_up"],
|
||||
[data-card-name="mengsan_colorless_seeker_strike"],
|
||||
[data-card-name="mengsan_colorless_thrumming_hatchet"],
|
||||
[data-card-name="mengsan_colorless_mind_blast"],
|
||||
[data-card-name="mengsan_colorless_tag_team"],
|
||||
[data-card-name="mengsan_colorless_dark_shackles"],
|
||||
[data-card-name="mengsan_colorless_jack_of_all_trades"],
|
||||
[data-card-name="mengsan_colorless_thinking_ahead"],
|
||||
[data-card-name="mengsan_colorless_production"],
|
||||
[data-card-name="mengsan_colorless_believe_in_you"],
|
||||
[data-card-name="mengsan_colorless_restlessness"],
|
||||
[data-card-name="mengsan_colorless_panic_button"],
|
||||
[data-card-name="mengsan_colorless_ultimate_defend"],
|
||||
[data-card-name="mengsan_colorless_coordinate"],
|
||||
[data-card-name="mengsan_colorless_panache"],
|
||||
[data-card-name="mengsan_colorless_prowess"],
|
||||
[data-card-name="mengsan_colorless_prep_time"],
|
||||
[data-card-name="mengsan_colorless_hand_of_greed"],
|
||||
[data-card-name="mengsan_colorless_the_gambit"],
|
||||
[data-card-name="mengsan_colorless_secret_technique"],
|
||||
[data-card-name="mengsan_colorless_secret_weapon"],
|
||||
[data-card-name="mengsan_colorless_master_of_strategy"],
|
||||
[data-card-name="mengsan_colorless_alchemize"],
|
||||
[data-card-name="mengsan_colorless_scrawl"],
|
||||
[data-card-name="mengsan_colorless_hidden_gem"],
|
||||
[data-card-name="mengsan_colorless_beacon_of_hope"],
|
||||
[data-card-name="mengsan_colorless_eternal_armor"],
|
||||
[data-card-name="mengsan_event_byrd_swoop"],
|
||||
[data-card-name="mengsan_event_mad_science_sapping"],
|
||||
[data-card-name="mengsan_event_mad_science_violence"],
|
||||
[data-card-name="mengsan_event_mad_science_choking"],
|
||||
[data-card-name="mengsan_event_feeding_frenzy"],
|
||||
[data-card-name="mengsan_event_mad_science_energized"],
|
||||
[data-card-name="mengsan_event_mad_science_wisdom"],
|
||||
[data-card-name="mengsan_event_mad_science_chaos"],
|
||||
[data-card-name="mengsan_event_toric_toughness"],
|
||||
[data-card-name="mengsan_event_mad_science_expertise"],
|
||||
[data-card-name="mengsan_event_mad_science_curious"],
|
||||
[data-card-name="mengsan_event_mad_science_improvement"],
|
||||
[data-card-name="mengsan_event_neows_fury"],
|
||||
[data-card-name="mengsan_event_brightest_flame"],
|
||||
[data-card-name="mengsan_status_frantic_escape"],
|
||||
[data-card-name="mengsan_curse_spore_mind"],
|
||||
[data-card-name="mengsan_curse_ascenders_bane"],
|
||||
[data-card-name="mengsan_curse_curse_of_the_bell"],
|
||||
[data-card-name="mengsan_curse_poor_sleep"],
|
||||
[data-card-name="mengsan_status_deprecated_card"]
|
||||
):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
font-size: 10px !important;
|
||||
padding: 2px 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 稀有度是独立装饰层,不占用原生 card::after,也不改变尺寸、命中区或立绘。 */
|
||||
.mengsan-hand-rarity-shuying { display: none; pointer-events: none; }
|
||||
.mengsan-hand-zone-shuying > .card[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]):not(.infohidden):not(.infoflip) > .mengsan-hand-rarity-shuying {
|
||||
display: block; position: absolute; inset: 0; z-index: 4; border-radius: inherit;
|
||||
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||
box-shadow: inset 0 0 0 2px var(--ms-rarity-accent), inset 0 0 0 4px var(--ms-rarity-dark);
|
||||
background: linear-gradient(90deg, var(--ms-rarity-dark), var(--ms-rarity-accent), var(--ms-rarity-metal)) top / 100% 5px no-repeat;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]):not(.infohidden):not(.infoflip) > :is(.name, .top-name) {
|
||||
background: var(--ms-rarity-accent) !important; color: var(--ms-rarity-ink) !important;
|
||||
text-shadow: none !important; border-color: var(--ms-rarity-metal) !important;
|
||||
}
|
||||
.mengsan-hand-zone-shuying > .card[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]):not(.infohidden):not(.infoflip) > .mengsan-hand-cost-shuying {
|
||||
background: var(--ms-rarity-dark); color: var(--ms-rarity-metal); border-color: var(--ms-rarity-accent);
|
||||
}
|
||||
|
||||
@@ -94,12 +94,17 @@ export function mountEnemyIntent(player, session, assetBase, document,
|
||||
const hasSummon = typeof intent.summon === "string";
|
||||
const hasSlimed = Number.isInteger(intent.slimed) && intent.slimed > 0;
|
||||
const hasInfection = Number.isInteger(intent.infection) && intent.infection > 0;
|
||||
const hasWound = Number.isInteger(intent.wound) && intent.wound > 0;
|
||||
const hasRinging = intent.ringing === true;
|
||||
const hasPlow = Number.isInteger(intent.plow) && intent.plow > 0;
|
||||
const hasDazed = Number.isInteger(intent.dazed) && intent.dazed > 0;
|
||||
const hasDebuff = typeof intent.debuff === "string" &&
|
||||
Number.isInteger(intent.stacks) && intent.stacks > 0;
|
||||
debuffLine.hidden = !(hasDebuff || hasDazed || hasSlimed || hasInfection);
|
||||
const debuffSrc = `${assetBase}/assets/intent/${hasDazed || hasSlimed || hasInfection ? "Intent_status_card.png" : "Intent_debuff.png"}`;
|
||||
debuffLine.hidden = !(hasDebuff || hasDazed || hasSlimed || hasInfection || hasWound || hasRinging);
|
||||
const debuffSrc = `${assetBase}/assets/intent/${hasDazed || hasSlimed || hasInfection || hasWound ? "Intent_status_card.png" : "Intent_debuff.png"}`;
|
||||
if (debuffIcon.getAttribute("src") !== debuffSrc) debuffIcon.src = debuffSrc;
|
||||
if (hasRinging) debuffAmount.textContent = "×1";
|
||||
if (hasWound) debuffAmount.textContent = `×${intent.wound}`;
|
||||
if (hasInfection) debuffAmount.textContent = `×${intent.infection}`;
|
||||
if (hasSlimed) debuffAmount.textContent = `×${intent.slimed}`;
|
||||
if (hasDazed) debuffAmount.textContent = `×${intent.dazed}`;
|
||||
@@ -114,7 +119,8 @@ export function mountEnemyIntent(player, session, assetBase, document,
|
||||
const buffSrc = `${assetBase}/assets/intent/${hasSummon ? "Intent_summon.png" : "Intent_buff.png"}`;
|
||||
if (buffIcon.getAttribute("src") !== buffSrc) buffIcon.src = buffSrc;
|
||||
blockLine.hidden = !hasBlock;
|
||||
buffLine.hidden = !(hasStrength || hasSummon);
|
||||
buffLine.hidden = !(hasStrength || hasSummon || hasPlow);
|
||||
if (hasPlow) buffAmount.textContent = `撞${Math.max(1, Math.round(intent.plow * player.maxHp / 252))}`;
|
||||
if (hasSummon) buffAmount.textContent = "×1";
|
||||
if (hasBlock) blockAmount.textContent = String(intent.block);
|
||||
if (hasStrength) {
|
||||
@@ -148,11 +154,14 @@ export function mountEnemyIntent(player, session, assetBase, document,
|
||||
}
|
||||
if (hasDebuff) {
|
||||
const label = intent.debuff === "脆弱" ? "Frail" :
|
||||
intent.debuff === "易伤" ? "Vulnerable" : intent.debuff === "缩小" ? "Shrink" : intent.debuff === "缠结" ? "Tangled" : "Debuff";
|
||||
intent.debuff === "虚弱" ? "Weak" : intent.debuff === "易伤" ? "Vulnerable" : intent.debuff === "缩小" ? "Shrink" : intent.debuff === "缠结" ? "Tangled" : "Debuff";
|
||||
effects.push(`${label} ×${intent.stacks}`);
|
||||
}
|
||||
if (hasRinging) effects.push("Ringing ×1");
|
||||
if (hasPlow) effects.push(`Plow ${Math.max(1, Math.round(intent.plow * player.maxHp / 252))}`);
|
||||
if (hasSummon) effects.push("Summon: 利齿之眼 ×1");
|
||||
if (hasDazed) effects.push(`Dazed ×${intent.dazed}`);
|
||||
if (hasWound) effects.push(`Wound ×${intent.wound}`);
|
||||
if (hasInfection) effects.push(`Infection ×${intent.infection}`);
|
||||
if (hasSlimed) effects.push(`Slimed ×${intent.slimed}`);
|
||||
if (hasBlock) effects.push(`Block ${intent.block}`);
|
||||
@@ -165,7 +174,7 @@ export function mountEnemyIntent(player, session, assetBase, document,
|
||||
effects.push(intent.sleep ? "Sleep" : "Unknown");
|
||||
}
|
||||
const label = effects.join(", ");
|
||||
badge.title = hasInfection ? `向敌对角色的个人弃牌堆加入${intent.infection}张感染;不能被打出,回合结束在手牌中每张造成3点非攻击伤害。` : intent.sleep ? "沉睡:本回合不行动,不消耗费用。" : intent.debuff === "缠结" ? "紧绕藤蔓:攻击后施加1回合缠结,攻击牌费用增加1;自身回合结束减少1回合。" : intent.shrink ? "缩小:攻击伤害减少30%,不重复叠加;来源死亡解除。" : hasSlimed ? `黏液:向敌对角色的个人弃牌堆加入${intent.slimed}张黏液;耗1费用、抽1张牌、消耗。` : hasSummon ? "虚幻孢子:召唤1只6生命的利齿之眼。" :
|
||||
badge.title = hasRinging ? "野兽咆哮:敌对角色下一次自身回合只能主动使用1张牌,不限制响应,回合结束解除。" : hasPlow ? "跺地:获得横冲直撞,受伤且生命不高于其层数时击晕、清零力量并进入第二阶段。" : hasWound ? `肢解:攻击后向存活敌对角色的个人弃牌堆加入${intent.wound}张伤口;不能被打出,无虚无和额外伤害。` : hasInfection ? `向敌对角色的个人弃牌堆加入${intent.infection}张感染;不能被打出,回合结束在手牌中每张造成3点非攻击伤害。` : intent.sleep ? "沉睡:本回合不行动,不消耗费用。" : intent.debuff === "缠结" ? "紧绕藤蔓:攻击后施加1回合缠结,攻击牌费用增加1;自身回合结束减少1回合。" : intent.shrink ? "缩小:攻击伤害减少30%,不重复叠加;来源死亡解除。" : hasSlimed ? `黏液:向敌对角色的个人弃牌堆加入${intent.slimed}张黏液;耗1费用、抽1张牌、消耗。` : hasSummon ? "虚幻孢子:召唤1只6生命的利齿之眼。" :
|
||||
hasDazed ? "牵制:向敌对角色的个人弃牌堆加入3张晕眩;晕眩不能被打出,具有虚无。" :
|
||||
"攻击与负面效果作用于所有存活敌对角色;伤害按各目标状态计算,护甲另行抵扣。";
|
||||
if (badge.getAttribute("aria-label") !== label) badge.setAttribute("aria-label", label);
|
||||
|
||||
@@ -72,7 +72,7 @@ export async function chooseRewardPackage(choices, {
|
||||
};
|
||||
return chooseVictoryOptions([upgrade, relic, {
|
||||
id: "claim-package", label: "领取奖励并继续",
|
||||
description: "领取必得奖励与已选奖励,未选择的项目视为跳过。",
|
||||
description: "领取固定奖励与已选奖励,未选择的项目视为跳过。",
|
||||
choose: () => ({ cardId, relicId }),
|
||||
}, {
|
||||
id: "skip-package", label: "跳过",
|
||||
@@ -81,9 +81,9 @@ export async function chooseRewardPackage(choices, {
|
||||
}], {
|
||||
title: "初识的卢", allowSkip: false,
|
||||
rewardLabel: "搜刮 · 战利品列表",
|
||||
rewardItems: [{ name: `金币 · ${pack.gold}(必得)`,
|
||||
rewardItems: [{ name: `金币 · ${pack.gold}`,
|
||||
description: "无论选择还是跳过,金币都会结算。" },
|
||||
...fixedRewards.map(reward => ({ ...reward, name: `${reward.name}(必得)` }))],
|
||||
...fixedRewards.map(reward => ({ ...reward }))],
|
||||
description: `固定获得 ${pack.gold} 金币与${fixedRewards.map(
|
||||
reward => `【${reward.name}】`).join("、")}。` +
|
||||
"强化与遗物点击后选择,也可跳过。",
|
||||
|
||||
+29
-15
@@ -3,6 +3,9 @@ import { cardCost } from "../battle/combat-rules.js";
|
||||
import { cardUpgradeRule } from "../cards/upgrades.js";
|
||||
import { getRelic } from "../relics/definitions.js";
|
||||
|
||||
import { getCardRarity, applyCardRarity } from "../cards/rarity.js";
|
||||
import { cleanCardDescription } from "../cards/description.js";
|
||||
|
||||
const element = (tag, className, text, parent) => {
|
||||
const node = document.createElement(tag);
|
||||
node.className = className;
|
||||
@@ -33,11 +36,12 @@ const viewCard = choice => {
|
||||
? `${lib.assetURL || ""}image/card/${imageName}.png` : null;
|
||||
const type = { basic: "基本牌", trick: "锦囊牌", delay: "延时锦囊", equip: "装备牌" }[info?.type] || "卡牌";
|
||||
return {
|
||||
name: get.translation(name), type, image, cost: cardCost(choice.card || { name }),
|
||||
description: plainText(cardUpgradeRule(name) && info?.cardPrompt
|
||||
rarity: getCardRarity(name),
|
||||
name: get.translation(name), type, image, cost: info?.mengsanXCost_shuying ? "X" : cardCost(choice.card || { name }),
|
||||
description: cleanCardDescription(plainText(cardUpgradeRule(name) && info?.cardPrompt
|
||||
? info.cardPrompt(choice.card || { name })
|
||||
: lib.translate[`${name}_info`] || choice.description ||
|
||||
"暂无卡牌介绍。"),
|
||||
"暂无卡牌介绍。")),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -116,7 +120,8 @@ function chooseTilesDialog(choices, {
|
||||
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}。${actionLabel}`);
|
||||
if (view.rarity) applyCardRarity(card, cardNameOf(choice));
|
||||
card.setAttribute("aria-label", `${view.name},${view.type}${view.rarity ? `,稀有度:${view.rarity.label}` : ""},${view.description}。${actionLabel}`);
|
||||
card.addEventListener("click", () => finish(choice.id));
|
||||
if (view.cost !== null) {
|
||||
element("span", "mengsan-card-choice-cost-shuying", String(view.cost), card);
|
||||
@@ -129,7 +134,7 @@ function chooseTilesDialog(choices, {
|
||||
image.addEventListener("error", () => image.remove(), { once: true });
|
||||
}
|
||||
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-kind-shuying", view.rarity ? `${view.type} · ${view.rarity.label}` : view.type, card);
|
||||
element("span", "mengsan-card-choice-description-shuying", view.description, card);
|
||||
element("span", "mengsan-reward-tile-action-shuying", actionLabel, card);
|
||||
}
|
||||
@@ -240,7 +245,7 @@ export function chooseVictoryOptions(options, {
|
||||
}
|
||||
|
||||
// Gold and fixed rewards are guaranteed; random loot opens a separate one-of-N selector.
|
||||
export function chooseBattleReward(choices, { fixedRewards = [] } = {}) {
|
||||
export function chooseBattleReward(choices, { fixedRewards = [], boss = false, elite = false } = {}) {
|
||||
if (!Array.isArray(choices)) return Promise.resolve(null);
|
||||
const gold = choices.filter(choice => choice.kind === "gold")
|
||||
.reduce((amount, choice) => amount + choice.amount, 0);
|
||||
@@ -250,23 +255,32 @@ export function chooseBattleReward(choices, { fixedRewards = [] } = {}) {
|
||||
const kind = rewards.every(isCardReward) ? "卡牌"
|
||||
: rewards.every(choice => getRelic(choice.relic)) ? "遗物" : "战利品";
|
||||
options.push({
|
||||
id: "choose-reward", label: `${kind}奖励 · ${rewards.length}选1`,
|
||||
description: `点击查看候选${kind},选择一项领取,或点击跳过。`,
|
||||
choose: () => chooseRewardDialog(rewards),
|
||||
id: "choose-reward", label: `${boss && kind === "卡牌" ? "稀有卡牌" : kind}奖励 · ${rewards.length}选1`,
|
||||
description: elite && kind === "卡牌"
|
||||
? "角色专属卡牌,每项候选80%概率为罕见或稀有;可选择一张或跳过。"
|
||||
: `点击查看候选${kind},选择一项领取,或点击跳过。`,
|
||||
choose: () => rewards.every(isCardReward) ? chooseCardDialog(rewards, {
|
||||
title: boss ? "Boss奖励 · 稀有卡牌" : "卡牌奖励",
|
||||
description: "从本次候选中选择一张加入牌组,也可跳过。",
|
||||
}) : chooseRewardDialog(rewards),
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
id: "skip-loot", label: "跳过",
|
||||
description: rewards.length ? "跳过本次可选奖励,必得奖励照常领取。"
|
||||
: "本次没有可选奖励,领取必得奖励后继续。",
|
||||
description: rewards.length ? "跳过本次可选奖励,其余奖励照常领取。"
|
||||
: "本次没有可选奖励,领取奖励后继续。",
|
||||
choose: () => null,
|
||||
});
|
||||
return chooseVictoryOptions(options, {
|
||||
title: "过关奖励", allowSkip: false,
|
||||
description: `本关必得 ${gold} 金币。点击奖励选项后择一领取,也可跳过。`,
|
||||
title: boss ? "Boss奖励" : elite ? "精英奖励" : "过关奖励", allowSkip: false,
|
||||
description: `本关奖励:${gold} 金币。` + (elite
|
||||
? fixedRewards.some(reward => getRelic(reward.relic))
|
||||
? "同时获得一件遗物,跳过卡牌也会领取。"
|
||||
: "遗物池已全部收集,本次不重复发放。"
|
||||
: "") + "点击奖励选项后择一领取,也可跳过。",
|
||||
rewardLabel: "搜刮 · 战利品列表",
|
||||
rewardItems: [{name: `金币 · ${gold}(必得)`,
|
||||
rewardItems: [{name: `金币 · ${gold}`,
|
||||
description: "无论选择还是跳过,金币都会结算。"},
|
||||
...fixedRewards.map(reward => ({ ...reward, name: `${reward.name}(必得)` }))],
|
||||
...fixedRewards.map(reward => ({ ...reward }))],
|
||||
});
|
||||
}
|
||||
|
||||
+120
-6
@@ -1231,22 +1231,22 @@ dialog.mengsan-deck-dialog-shuying::backdrop { background: #080706b8; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-button[aria-pressed="true"] { background: #dcb578; color: #241a10; border-color: #f9dda6; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-summary, .mengsan-deck-dialog-shuying .ms-deck-footer { color: var(--ms-muted); font-size: 13px; flex-shrink: 0; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-body { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 5px; scrollbar-gutter: stable; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; align-items: stretch; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-card { display: flex; flex-direction: column; gap: 8px; width: 100%; min-width: 0; padding: 8px; border: 1px solid #9e7b49; background: #e4d0aa; color: #2e2419; text-align: left; box-shadow: 0 4px 10px #0005; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid { display: grid; grid-template-columns: repeat(auto-fill, 176px); gap: 16px; align-items: start; align-content: start; justify-content: start; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-card { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 8px; width: 100%; min-width: 0; height: 400px; overflow: hidden; padding: 8px; border: 1px solid #9e7b49; background: #e4d0aa; color: #2e2419; text-align: left; box-shadow: 0 4px 10px #0005; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-card:hover { border-color: #ffe3a5; background: #f0ddb7; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-face { display: flex; flex-direction: column; gap: 5px; min-width: 0; color: #2e2419; background: #e4d0aa; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-corner { font-size: 14px; font-weight: 700; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-red { color: #9b2920; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-art { position: relative; display: grid; place-items: center; height: 116px; overflow: hidden; background: linear-gradient(145deg, #8a765b, #c0aa83); border: 1px solid #8b734f; border-radius: 3px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-art { position: relative; display: grid; place-items: center; width: 100%; height: auto; min-height: 0; aspect-ratio: 5 / 7; align-self: stretch; flex-shrink: 0; overflow: hidden; background: linear-gradient(145deg, #8a765b, #c0aa83); border: 1px solid #8b734f; border-radius: 3px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-glyph { font: 700 28px "KaiTi", serif; text-align: center; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-name { font: 700 21px/1.4 "KaiTi", serif; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-meta, .mengsan-deck-dialog-shuying .ms-deck-tags { font-size: 12px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-tags { color: #813d18; font-weight: 700; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-excerpt { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 12px; line-height: 1.5; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-excerpt { flex: 0 0 36px; height: 36px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 12px; line-height: 1.5; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 28px; margin-top: 18px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content > .ms-deck-face { padding: 14px; border-radius: 6px; align-self: start; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content .ms-deck-art { height: 190px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content .ms-deck-art { width: 100%; height: auto; aspect-ratio: 5 / 7; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-copy { display: flex; flex-direction: column; gap: 14px; min-width: 0; overflow-wrap: anywhere; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-copy h3 { font-size: 26px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-rules { white-space: pre-line; line-height: 1.85; }
|
||||
@@ -1255,7 +1255,7 @@ dialog.mengsan-deck-dialog-shuying::backdrop { background: #080706b8; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-empty { padding: 24px; grid-column: 1 / -1; }
|
||||
@media (max-width: 600px) {
|
||||
dialog.mengsan-deck-dialog-shuying { width: calc(100vw - 12px); height: calc(100dvh - 12px); max-height: calc(100dvh - 12px); padding: 14px; gap: 10px !important; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid { grid-template-columns: repeat(2, minmax(0, 176px)); gap: 10px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content { grid-template-columns: minmax(0, 1fr); gap: 18px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-detail-content > .ms-deck-face { width: 190px; justify-self: center; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-summary { font-size: 12px; }
|
||||
@@ -1445,3 +1445,117 @@ body.mengsan-battle-ui-shuying :is(
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-field:not(:first-child) { flex: 1 1 90px; }
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-filters > button { padding: 8px 10px; }
|
||||
}
|
||||
|
||||
/* Same stable card geometry in held deck, encyclopedia and personal battle piles. */
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-face { display: grid; grid-template-rows: 21px auto 30px 18px 18px; gap: 5px; width: 100%; min-width: 0; flex-shrink: 0; align-items: stretch; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-corner,
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-name,
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-meta,
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-tags { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-art { width: 100%; height: auto; aspect-ratio: 5 / 7; }
|
||||
@media (max-width: 600px) {
|
||||
.mengsan-deck-dialog-shuying .ms-deck-grid .ms-deck-card { height: 374px; }
|
||||
}
|
||||
|
||||
/* Compact browsing only; full descriptions live in the existing detail view. */
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid { grid-template-columns: repeat(auto-fill, 112px); gap: 8px; justify-content: space-between; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-card { position: relative; height: var(--ms-card-height, 218px); padding: 6px; gap: 3px; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-face { flex: 1; min-height: 0; grid-template-rows: 18px minmax(0, 1fr) 20px; gap: 2px; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-art { width: var(--ms-card-art-width, 98px); max-width: 100%; height: auto; aspect-ratio: 5 / 7; justify-self: center; align-self: center; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-corner { font-size: 12px; line-height: 18px; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-name { font-size: 16px; line-height: 20px; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-meta { display: none; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-tags { position: absolute; top: 6px; right: 6px; max-width: 54px; font-size: 12px; line-height: 18px; background: #e4d0aa; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-excerpt { flex: 0 0 18px; height: 18px; line-height: 18px; -webkit-line-clamp: 1; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-card { contain: layout paint style; }
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid .ms-deck-card:focus-visible { outline-offset: -3px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-body { overflow-anchor: none; scroll-behavior: auto; }
|
||||
@media (max-width: 600px) {
|
||||
.mengsan-deck-dialog-shuying .ms-library-grid { grid-template-columns: repeat(2, minmax(0, 112px)); }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
dialog.mengsan-deck-dialog-shuying { padding: 10px; gap: 6px !important; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-title { font-size: 22px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-eyebrow { display: none; }
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-field > span { display: none; }
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-field:not(:first-child) { flex: 1 1 64px; }
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-filters > button { padding: 8px; font-size: 13px; }
|
||||
.mengsan-deck-dialog-shuying .ms-catalog-select { font-size: 13px; padding: 8px 6px; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-footer { font-size: 12px; line-height: 18px; }
|
||||
}
|
||||
|
||||
/* Same transparent infection frame in held deck, atlas, piles and detail. */
|
||||
.mengsan-deck-dialog-shuying .ms-deck-art[data-mengsan-overlay="infection"]::before {
|
||||
content: "";
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
background-image: url("../assets/card-overlays/infection-sprite.webp");
|
||||
/* 30 source frames, six columns/five rows, 192x260 per cell. */
|
||||
background-size: 600% 500%;
|
||||
background-position: 0% 0%;
|
||||
background-repeat: no-repeat;
|
||||
animation: mengsan-infection-frames-shuying 2s steps(1, end) infinite;
|
||||
}
|
||||
@keyframes mengsan-infection-frames-shuying {
|
||||
0% { background-position: 0% 0%; }
|
||||
3.333333% { background-position: 20% 0%; }
|
||||
6.666667% { background-position: 40% 0%; }
|
||||
10% { background-position: 60% 0%; }
|
||||
13.333333% { background-position: 80% 0%; }
|
||||
16.666667% { background-position: 100% 0%; }
|
||||
20% { background-position: 0% 25%; }
|
||||
23.333333% { background-position: 20% 25%; }
|
||||
26.666667% { background-position: 40% 25%; }
|
||||
30% { background-position: 60% 25%; }
|
||||
33.333333% { background-position: 80% 25%; }
|
||||
36.666667% { background-position: 100% 25%; }
|
||||
40% { background-position: 0% 50%; }
|
||||
43.333333% { background-position: 20% 50%; }
|
||||
46.666667% { background-position: 40% 50%; }
|
||||
50% { background-position: 60% 50%; }
|
||||
53.333333% { background-position: 80% 50%; }
|
||||
56.666667% { background-position: 100% 50%; }
|
||||
60% { background-position: 0% 75%; }
|
||||
63.333333% { background-position: 20% 75%; }
|
||||
66.666667% { background-position: 40% 75%; }
|
||||
70% { background-position: 60% 75%; }
|
||||
73.333333% { background-position: 80% 75%; }
|
||||
76.666667% { background-position: 100% 75%; }
|
||||
80% { background-position: 0% 100%; }
|
||||
83.333333% { background-position: 20% 100%; }
|
||||
86.666667% { background-position: 40% 100%; }
|
||||
90% { background-position: 60% 100%; }
|
||||
93.333333% { background-position: 80% 100%; }
|
||||
96.666667% { background-position: 100% 100%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mengsan-deck-dialog-shuying .ms-deck-art[data-mengsan-overlay="infection"]::before {
|
||||
animation: none;
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 配色统一来自 cards/rarity.js;pending 不改变旧卡面,尚未分配不等于普通。 */
|
||||
.mengsan-deck-dialog-shuying .ms-deck-face[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]),
|
||||
.mengsan-card-choice-tile-shuying[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) {
|
||||
border-color: var(--ms-rarity-metal); box-shadow: inset 0 0 0 2px var(--ms-rarity-accent);
|
||||
background: linear-gradient(145deg, var(--ms-rarity-dark), #231f1b);
|
||||
}
|
||||
.mengsan-deck-dialog-shuying .ms-deck-face[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) :is(.ms-deck-name, .ms-deck-corner),
|
||||
.mengsan-card-choice-tile-shuying[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) :is(.mengsan-card-choice-name-shuying, .mengsan-card-choice-kind-shuying) {
|
||||
background: var(--ms-rarity-accent); color: var(--ms-rarity-ink); border-color: var(--ms-rarity-metal);
|
||||
}
|
||||
.mengsan-deck-dialog-shuying .ms-deck-face[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) .ms-deck-art,
|
||||
.mengsan-card-choice-tile-shuying[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) .mengsan-card-choice-art-shuying { border-color: var(--ms-rarity-accent); }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-face[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]) :is(.ms-deck-meta, .ms-deck-tags) { color: #f6ebd6; }
|
||||
.mengsan-deck-dialog-shuying .ms-deck-rarity-label { color: #d7c5a2; }
|
||||
.mengsan-card-choice-tile-shuying[data-mengsan-rarity]:not([data-mengsan-rarity="pending"]):hover { border-color: var(--ms-rarity-accent); }
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// 保留完整滚动高度,只挂载可见行与上下各两行;滚动时不重建仍可见的卡牌。
|
||||
export function createVirtualCardGrid(viewport, grid, createCard) {
|
||||
const mounted = new Map();
|
||||
let records = [], columns = 1, height = 218, gap = 8, padding = 5, viewportHeight = 0;
|
||||
let frame = 0, disposed = false;
|
||||
function draw() {
|
||||
if (disposed || grid.hidden || !viewportHeight) return;
|
||||
const stride = height + gap;
|
||||
const offset = viewport.scrollTop;
|
||||
const firstRow = Math.max(0, Math.floor((offset - padding) / stride) - 2);
|
||||
const lastRow = Math.ceil((offset + viewportHeight - padding) / stride) + 2;
|
||||
const first = firstRow * columns, end = Math.min(records.length, lastRow * columns);
|
||||
// 不因滚动删除正在使用键盘操作的卡牌。
|
||||
for (const [index, node] of mounted) {
|
||||
if ((index < first || index >= end) && node !== document.activeElement) {
|
||||
node.remove(); mounted.delete(index);
|
||||
}
|
||||
}
|
||||
for (let index = first; index < end; index++) {
|
||||
if (mounted.has(index)) continue;
|
||||
const node = createCard(records[index], index);
|
||||
node.dataset.cardIndex = String(index);
|
||||
node.style.gridRow = String(Math.floor(index / columns) + 1);
|
||||
node.style.gridColumn = String(index % columns + 1);
|
||||
// 保持 DOM 顺序与屏幕顺序一致,方便键盘与读屏浏览。
|
||||
let next = null;
|
||||
for (const [otherIndex, other] of mounted) {
|
||||
if (otherIndex > index && (!next || otherIndex < Number(next.dataset.cardIndex))) next = other;
|
||||
}
|
||||
grid.insertBefore(node, next); mounted.set(index, node);
|
||||
}
|
||||
}
|
||||
function schedule() {
|
||||
if (!frame && !disposed) frame = requestAnimationFrame(() => { frame = 0; draw(); });
|
||||
}
|
||||
function layout(cardHeight = height) {
|
||||
height = cardHeight;
|
||||
if (disposed || grid.hidden || !grid.clientWidth) return;
|
||||
const style = getComputedStyle(grid);
|
||||
viewportHeight = viewport.clientHeight;
|
||||
const nextColumns = Math.max(1, style.gridTemplateColumns.split(/\s+/).filter(track => parseFloat(track) > 0).length);
|
||||
gap = parseFloat(style.rowGap) || 0;
|
||||
padding = parseFloat(getComputedStyle(viewport).paddingTop) || 0;
|
||||
columns = nextColumns;
|
||||
const rows = Math.ceil(records.length / columns);
|
||||
grid.style.gridTemplateRows = rows ? `repeat(${rows}, ${height}px)` : "none";
|
||||
for (const [index, node] of mounted) {
|
||||
node.style.gridRow = String(Math.floor(index / columns) + 1);
|
||||
node.style.gridColumn = String(index % columns + 1);
|
||||
}
|
||||
draw();
|
||||
}
|
||||
function focus(index) {
|
||||
if (!Number.isInteger(index) || index < 0 || index >= records.length || disposed) return;
|
||||
const top = padding + Math.floor(index / columns) * (height + gap);
|
||||
if (top < viewport.scrollTop) viewport.scrollTop = top;
|
||||
else if (top + height > viewport.scrollTop + viewportHeight) {
|
||||
viewport.scrollTop = top + height - viewportHeight;
|
||||
}
|
||||
draw(); mounted.get(index)?.focus({ preventScroll: true });
|
||||
}
|
||||
function keydown(event) {
|
||||
const card = event.target.closest("[data-card-index]");
|
||||
if (!card || !grid.contains(card) || event.altKey || event.ctrlKey || event.metaKey) return;
|
||||
const index = Number(card.dataset.cardIndex);
|
||||
const delta = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -columns, ArrowDown: columns,
|
||||
Tab: event.shiftKey ? -1 : 1 }[event.key];
|
||||
if (delta == null) return;
|
||||
const target = index + delta;
|
||||
if (target < 0 || target >= records.length) return;
|
||||
event.preventDefault(); focus(target);
|
||||
}
|
||||
viewport.addEventListener("scroll", schedule, { passive: true });
|
||||
grid.addEventListener("keydown", keydown);
|
||||
return {
|
||||
setRecords(value) {
|
||||
// 调用方更新筛选栏后再统一量尺寸,避免用旧高度先生成一批预览。
|
||||
records = value; mounted.clear(); grid.replaceChildren();
|
||||
},
|
||||
layout,
|
||||
focus,
|
||||
resume() { draw(); },
|
||||
dispose() {
|
||||
disposed = true; cancelAnimationFrame(frame);
|
||||
viewport.removeEventListener("scroll", schedule);
|
||||
grid.removeEventListener("keydown", keydown);
|
||||
mounted.clear(); records = [];
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user