feedback1 draft-info: the strategy draft shows the briefing's disabled bonds and banned operators again (GitHub issue #8 item 1)
One component, public/js/ui/matchInfo.js: matchInfoModel (the drawn set D and the mode's inactive bonds via disabledBondSets, the briefing's bond order, banned operators by tier, bannedPerBond) and the briefing's bond rows, legend and banned-operator grid. The briefing renders it; the band draft opens it read-only in a dialog (查看禁用盟约与干员 under the order list; 关闭, a tap outside or Esc close it; its footer repeats the turn and the countdown; a turn change closes it, the end of the draft unmounts it); the in-game 本局信息 tab reads the same model, so its banned operators are listed by tier too. In the dialog every size has a px floor, so phones keep readable text and a scrollable body. Tests: test/ui/match-info.test.js; opt-in test/ui/match-info.e2e.test.js (SP_E2E=1: solo 标准 on desktop, co-op 绝境 with a desktop and a phone).
This commit is contained in:
1 parent
842e1e13c4
commit
7188e0af89
8 files changed
+847
-56
No files matched your search
@@ -1,13 +1,16 @@
|
||||
// 本局信息 dialog under the top bar (opened by the left 🔍, official btn_check_player / autochess_hud_player_info_dialog,
|
||||
// research 09 §2.1) with two tabs:
|
||||
// 本局信息 — own strategy, stage, disabled bonds with banned-member counts and the banned operator list (default);
|
||||
// 本局信息 — own strategy, stage, disabled bonds with banned-member counts and the banned operator list (default) —
|
||||
// the same bonds, states and banned operators (by tier) as the briefing and the strategy draft's 本局信息
|
||||
// dialog: ui/matchInfo.js matchInfoModel, drawn here as compact chips and avatars;
|
||||
// 敌方情报 — the secondary enemy list: m.private.nextEnemies (icons, names, counts, tags, 特训 faction tags) + this
|
||||
// match's factions and the enemy leader on boss rounds. The primary enemy preview is the pen on the board
|
||||
// (the right 🔍▶▶ pans the camera there; tapping an enemy in the pen opens its detail card) — research 09 §6.2.
|
||||
|
||||
import { html, Icon, Tabs, MicroLabel } from './components.js';
|
||||
import { Img, UnitThumb, BondGlyph, BandIcon, RichText, GIcon } from './gameComponents.js';
|
||||
import { groupEnemies, factionTypes, bannedPerBond, disabledBondSets, briefingBondTip } from './gameLogic.js';
|
||||
import { groupEnemies, factionTypes, briefingBondTip } from './gameLogic.js';
|
||||
import { matchInfoModel } from './matchInfo.js';
|
||||
import { factionIconUrl } from './assetUrls.js';
|
||||
import { data } from '../data.js';
|
||||
|
||||
@@ -57,12 +60,10 @@ function EnemiesTab({ pub, priv, onEnemy }) {
|
||||
}
|
||||
|
||||
function InfoTab({ pub, priv, onChess }) {
|
||||
const sets = disabledBondSets(pub, data.get('config')?.modes?.[pub?.modeId]?.inactiveBondIds);
|
||||
const disabled = new Set([...sets.drawn, ...sets.off]);
|
||||
const stateOf = (id) => (sets.off.has(id) ? 'off' : sets.drawn.has(id) ? 'drawn' : null);
|
||||
const bonds = data.list('bonds');
|
||||
const banned = Array.isArray(pub?.bannedChess) ? pub.bannedChess : [];
|
||||
const perBond = bannedPerBond(bonds, banned);
|
||||
const { bonds, banned, perBond, stateOf } = matchInfoModel(pub, {
|
||||
bonds: data.list('bonds'), chess: (id) => data.lookup('chess', id), mode: data.get('config')?.modes?.[pub?.modeId],
|
||||
});
|
||||
const disabled = new Set(bonds.filter((b) => stateOf(b.bondId)).map((b) => b.bondId));
|
||||
const band = priv?.bandId ? data.lookup('bands', priv.bandId) : null;
|
||||
const stage = pub?.stageId ? data.lookup('stages', pub.stageId) : null;
|
||||
const withBans = bonds.filter((b) => disabled.has(b.bondId) || (perBond.get(b.bondId) || 0) > 0)
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Match info — the bonds and banned operators of a match (research 06 §4.1 "1/2 确认本局信息", research 01 A2 / 06
|
||||
// addendum D), one implementation for every place that shows them, so they never drift:
|
||||
// * the INFO_CHECK briefing's right column (screens/briefing.js);
|
||||
// * the strategy draft's 本局信息 dialog (screens/bandDraft.js; GitHub issue #8 item 1 "选策略时没法返回查看禁用的干员和盟约":
|
||||
// players choose a strategy partly by the bonds and operators the match leaves them);
|
||||
// * the data of the in-game 本局信息 tab (ui/enemyDrawer.js InfoTab, its own compact chips and avatars).
|
||||
// matchInfoModel (pure) derives everything from m.public and the static data: the two kinds of greyed bonds
|
||||
// (gameLogic disabledBondSets over config.modes[modeId].inactiveBondIds — the drawn set D "部分盟约所含干员阵容不完整",
|
||||
// still activatable through other bonds' operators or items, and the mode's static inactive bonds "本局禁用", e.g.
|
||||
// 标准模拟's 10), every bond in the briefing order (bondOrder, then identifier) split into 核心 / 附加, the banned
|
||||
// operators (m.public bannedChess, known chess only) sorted by tier, and the banned-member count per bond (gameLogic
|
||||
// bannedPerBond). The blocks are hookless (unit-tested by calling them): the 核心盟约 / 附加盟约 rows of bond discs —
|
||||
// greyed with the ✕, the red banned-member badge, the briefingBondTip tooltip — the legend and the 本局禁用干员 grid.
|
||||
// MatchInfoDialog shows the same blocks read-only in a components.js Modal (关闭, a tap outside or Esc close it) with a
|
||||
// status line from its caller (the draft's turn and countdown, so the running clock stays in view).
|
||||
// Styles: css/screens/briefing.css (.brief-*), css/screens/draft.css (.minfo-dlg).
|
||||
|
||||
import { html, Button, Icon, MicroLabel, BondDisc, Tooltip, Modal } from './components.js';
|
||||
import { UnitThumb } from './gameComponents.js';
|
||||
import { bannedPerBond, disabledBondSets, briefingBondTip } from './gameLogic.js';
|
||||
import { bondIconUrl } from './assetUrls.js';
|
||||
import { data } from '../data.js';
|
||||
|
||||
const cx = (...p) => p.flat().filter(Boolean).join(' ');
|
||||
|
||||
/**
|
||||
* @typedef {{ sets: { drawn: Set<string>, off: Set<string> }, stateOf: (bondId: string) => 'off'|'drawn'|null,
|
||||
* bonds: any[], core: any[], addon: any[], banned: string[], perBond: Map<string, number> }} MatchInfoModel
|
||||
*/
|
||||
|
||||
/**
|
||||
* Everything the match-info blocks show.
|
||||
* @param {any} pub m.public (drawnDisabledBonds / disabledBonds, bannedChess)
|
||||
* @param {{ bonds?: any[], chess?: (id: string) => any, mode?: any }} [src]
|
||||
* bonds: bonds.json records (data.list('bonds')); chess: the chess lookup; mode: config.json modes[pub.modeId]
|
||||
* @returns {MatchInfoModel}
|
||||
* stateOf: 'off' = the mode never activates the bond (本局禁用), 'drawn' = in the drawn set D (阵容不完整), null = normal;
|
||||
* banned: the banned chess ids the data knows, by tier (ties keep the server's order); perBond: bondId → banned members
|
||||
*/
|
||||
export function matchInfoModel(pub, { bonds = [], chess = () => null, mode = null } = {}) {
|
||||
const sets = disabledBondSets(pub, mode?.inactiveBondIds);
|
||||
const stateOf = (id) => (sets.off.has(id) ? 'off' : sets.drawn.has(id) ? 'drawn' : null);
|
||||
const list = (Array.isArray(bonds) ? bonds : []).filter((b) => !!b && typeof b === 'object' && typeof b.bondId === 'string')
|
||||
.sort((a, b) => (a.bondOrder ?? 0) - (b.bondOrder ?? 0) || (a.identifier ?? 0) - (b.identifier ?? 0));
|
||||
const tierOf = (id) => chess(id)?.tier ?? 0;
|
||||
const banned = (Array.isArray(pub?.bannedChess) ? pub.bannedChess : []).filter((id) => typeof id === 'string' && !!chess(id))
|
||||
.sort((a, b) => tierOf(a) - tierOf(b));
|
||||
return {
|
||||
sets, stateOf, bonds: list, core: list.filter((b) => b.isCore), addon: list.filter((b) => !b.isCore),
|
||||
banned, perBond: bannedPerBond(list, banned),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* One row of bond discs (核心盟约 / 附加盟约): a greyed disc (✕) for a bond in the drawn set D or one the mode never
|
||||
* activates, the red badge with its banned members, a red ring on an enabled bond that lost members; the tooltip says
|
||||
* which (briefingBondTip).
|
||||
* @param {{ title: string, micro: string, bonds: any[], model: MatchInfoModel }} props
|
||||
*/
|
||||
export function MatchBondRow({ title, micro, bonds, model }) {
|
||||
const m = data.get('assets');
|
||||
return html`<div class="brief-bonds">
|
||||
<h3 class="brief-h"><span>${title}</span><${MicroLabel}>${micro}</${MicroLabel}></h3>
|
||||
<div class="brief-bonds__row">
|
||||
${bonds.map((b) => {
|
||||
const state = model.stateOf(b.bondId);
|
||||
const off = !!state;
|
||||
const bannedN = model.perBond.get(b.bondId) || 0;
|
||||
return html`<${Tooltip} key=${b.bondId} text=${briefingBondTip(b.name, state, bannedN)}>
|
||||
<div class=${cx('brief-bond', off && 'is-off', state === 'drawn' && 'is-incomplete', !off && bannedN > 0 && 'is-partial')} data-bond=${b.bondId}>
|
||||
<${BondDisc} name=${b.name} icon=${bondIconUrl(m, b.bondId)} active=${!off} disabled=${off} tier=${off ? 0 : (b.thresholds?.length || 1)}
|
||||
maxTier=${Math.max(1, b.thresholds?.length || 1)} size="md" />
|
||||
${bannedN > 0 ? html`<span class="brief-bond__ban num"><${Icon} name="user" />${bannedN}</span>` : null}
|
||||
</div>
|
||||
<//>`;
|
||||
})}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The legend under the bond rows ("或本模式禁用" only when the mode switches bonds off).
|
||||
* @param {{ model: MatchInfoModel }} props
|
||||
*/
|
||||
export function MatchLegend({ model }) {
|
||||
return html`<p class="brief-legend"><span class="brief-legend__off"></span>灰色:部分盟约所含干员阵容不完整(仍可通过其他盟约的干员或装备激活)${model.sets.off.size ? ',或本模式禁用' : ''} · <span class="brief-legend__ban"><${Icon} name="user" /></span>该盟约中无法出现的干员数</p>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本局禁用干员: the count and the greyed avatars by tier.
|
||||
* @param {{ model: MatchInfoModel }} props
|
||||
*/
|
||||
export function BannedOperators({ model }) {
|
||||
const { banned } = model;
|
||||
return html`<div class="brief-banned">
|
||||
<h3 class="brief-h"><span>本局禁用干员</span><${MicroLabel}>BANNED OPERATORS</${MicroLabel}><b class="num brief-banned__n">${banned.length}</b></h3>
|
||||
${banned.length ? html`<div class="brief-banned__grid">
|
||||
${banned.map((id) => html`<${UnitThumb} key=${id} kind="chess" id=${id} size="sm" dim=${true} />`)}
|
||||
</div>` : html`<p class="t-dim">本局没有禁用干员</p>`}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The briefing's bonds and banned operators: 核心盟约, 附加盟约, the legend, 本局禁用干员.
|
||||
* @param {{ model: MatchInfoModel }} props
|
||||
*/
|
||||
export function MatchInfo({ model }) {
|
||||
return html`<${MatchBondRow} title="核心盟约" micro="CORE BONDS" bonds=${model.core} model=${model} />
|
||||
<${MatchBondRow} title="附加盟约" micro="ADD-ON BONDS" bonds=${model.addon} model=${model} />
|
||||
<${MatchLegend} model=${model} />
|
||||
<${BannedOperators} model=${model} />`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The read-only match-info dialog (the strategy draft's 本局信息): MatchInfo in a Modal that 关闭, a tap outside or Esc
|
||||
* closes. `status` (the caller's line, e.g. the draft's turn and countdown) sits left of 关闭.
|
||||
* @param {{ open: boolean, onClose: Function, model: MatchInfoModel|null, status?: any }} props
|
||||
*/
|
||||
export function MatchInfoDialog({ open, onClose, model, status = null }) {
|
||||
return html`<${Modal} open=${open && !!model} onClose=${onClose} title="本局信息" micro="MATCH INFO // BONDS & BANNED OPERATORS" class="minfo-dlg"
|
||||
actions=${html`${status ? html`<div class="minfo-dlg__status" data-testid="match-info-status">${status}</div>` : null}
|
||||
<${Button} variant="secondary" icon="close" data-autofocus data-testid="match-info-close" onClick=${onClose}>关闭<//>`}>
|
||||
${open && model ? html`<${MatchInfo} model=${model} />` : null}
|
||||
<//>`;
|
||||
}
|
||||
Reference in new issue
Block a user