Files
ark-stronghold-protocol/public/js/ui/enemyDrawer.js
T
Luo Xingyu bce182703b Watching a teammate shows their hand, 临时整备区, equipment, strategy and effects (#129)
The prep scout of another player's board carries their 整备区 hand and 临时整备区 as units on the bench rows (like the own prep bench), the equipped items of their operators, their strategy in the drawer and their effects column (the official mode shows a teammate's hand too). The scout signature includes the hand / temp slots, so a moved card refreshes the watcher. Closes #128. Contributed by @2321Robin.
2026-10-05 16:47:33 +08:00

106 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 本局信息 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) —
// 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, briefingBondTip } from './gameLogic.js';
import { matchInfoModel } from './matchInfo.js';
import { factionIconUrl } from './assetUrls.js';
import { data } from '../data.js';
const cx = (...p) => p.flat().filter(Boolean).join(' ');
const TAG = { boss: '领袖', bounty: '悬赏', escort: '护卫' };
function FactionChips({ types }) {
const f = data.get('factions')?.types || {};
const m = data.get('assets');
return html`<div class="fchips">${types.map((t) => html`<span key=${t} class="fchip" title=${f[t]?.desc || ''}>
<${Img} src=${factionIconUrl(m, f[t]?.icon)} class="fchip__icon" /><span>${f[t]?.name || t}</span></span>`)}</div>`;
}
function EnemiesTab({ pub, priv, onEnemy }) {
const rows = groupEnemies(priv?.nextEnemies, (k) => data.lookup('enemies', k));
const types = factionTypes(pub?.factions);
const f = data.get('factions')?.types || {};
const total = rows.reduce((s, r) => s + r.count, 0);
const boss = pub?.bossId ? data.lookup('bosses', pub.bossId) : null;
return html`<div class="edrawer__body">
<div class="edrawer__sum">
<span>第 <b class="num">${pub?.round ?? '-'}</b> 回合 · 即将迎击 <b class="num t-orange">${total}</b> 名敌人</span>
${types.length ? html`<${FactionChips} types=${types} />` : null}
</div>
${rows.length ? html`<div class="elist">
${rows.map((r) => html`<button key=${`${r.enemyKey}|${r.tag}`} type="button" class=${cx('erow', r.tag && `erow--${r.tag}`, `erow--${r.rank.toLowerCase()}`)}
onClick=${() => onEnemy(r.enemyKey, r.count)}>
<${UnitThumb} kind="enemy" id=${r.enemyKey} size="md" />
<span class="erow__text">
<b class="erow__name">${r.name}</b>
<span class="erow__tags">
${r.tag ? html`<span class=${`etag etag--${r.tag}`}>${TAG[r.tag] || r.tag}</span>` : null}
${r.rank === 'ELITE' ? html`<span class="etag etag--elite">精英</span>` : null}
${r.fly ? html`<span class="etag etag--fly">空中</span>` : null}
${r.acTypes.map((t) => html`<span key=${t} class="etag etag--faction">${(f[t]?.name || t).replace('特训敌人·', '')}</span>`)}
</span>
</span>
<b class="erow__count num">×${r.count}</b>
</button>`)}
</div>` : html`<p class="edrawer__empty"><${GIcon} name="target" />暂无敌方情报</p>`}
${boss ? html`<div class="eboss">
<${UnitThumb} kind="enemy" id=${boss.enemyKey} size="md" />
<div><${MicroLabel} tone="gold">ENEMY LEADER // 敌方领袖</${MicroLabel}><b>${boss.name}</b>
<span class="t-lo">第 ${data.get('config')?.modes?.[pub.modeId]?.bossRound ?? pub.lastRound ?? 14} 回合 · 最终攻势</span></div>
</div>` : null}
</div>`;
}
function InfoTab({ pub, priv, onChess, bandId = null, bandOwner = null }) {
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));
// while scouting a teammate's prep board the drawer shows THEIR 策略 in place of one's own (user playtest #2 item 2)
const band = (bandId || priv?.bandId) ? data.lookup('bands', bandId || 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)
.sort((a, b) => (disabled.has(b.bondId) - disabled.has(a.bondId)) || ((perBond.get(b.bondId) || 0) - (perBond.get(a.bondId) || 0)));
return html`<div class="edrawer__body">
${band ? html`<div class="iband">
<${BandIcon} bandId=${band.bandId} size="md" />
<div><${MicroLabel} tone="mint">STRATEGY // ${bandOwner ? `${bandOwner} 的策略` : '我的策略'}</${MicroLabel}><b>${band.name} <small class="t-lo">${band.effectName}</small></b>
<${RichText} text=${band.descRaw || band.desc} class="iband__desc" /></div>
</div>` : null}
${stage ? html`<p class="istage"><${Icon} name="rook" />战场:<b>${stage.name || stage.id}</b></p>` : null}
<h4 class="ihead">本局禁用干员情况 <small class="num">${banned.length}</small></h4>
<div class="ibonds">
${withBans.length ? withBans.map((b) => html`<span key=${b.bondId} class=${cx('ibond', disabled.has(b.bondId) && 'is-off')} title=${briefingBondTip(b.name, stateOf(b.bondId), perBond.get(b.bondId) || 0)}>
<${BondGlyph} bondId=${b.bondId} /><span>${b.name}</span>
${perBond.get(b.bondId) ? html`<span class="ibond__ban num" title="该盟约中被禁用的干员数"><${Icon} name="user" />${perBond.get(b.bondId)}</span>` : null}
</span>`) : html`<span class="t-dim">本局没有禁用盟约</span>`}
</div>
${banned.length ? html`<div class="ibanned">${banned.map((id) => html`<button key=${id} type="button" class="ibanned__one" onClick=${() => onChess(id)}>
<${UnitThumb} kind="chess" id=${id} size="sm" dim=${true} /></button>`)}</div>` : null}
</div>`;
}
/**
* @param {{ tab:'enemies'|'info', onTab:(t:string)=>void, pub:any, priv:any, onClose:Function, onEnemy:(key:string, count:number)=>void, onChess:(id:string)=>void,
* bandId?: string|null, bandOwner?: string|null }} props
* bandId / bandOwner: while scouting a teammate's prep board, the watched player's 策略 (m.public players[].bandId)
* replaces one's own in the 本局信息 tab (game.js scoutBandId; user playtest #2 item 2)
*/
export function EnemyDrawer({ tab, onTab, pub, priv, onClose, onEnemy, onChess, bandId = null, bandOwner = null }) {
return html`<div class="edrawer brackets" role="dialog" aria-label=${tab === 'info' ? '本局信息' : '敌方情报'}>
<div class="edrawer__top">
<${Tabs} size="sm" value=${tab} onChange=${onTab} items=${[{ id: 'info', label: '本局信息' }, { id: 'enemies', label: '敌方情报' }]} />
<button type="button" class="edrawer__close tapx" aria-label="关闭" onClick=${onClose}><${Icon} name="close" /></button>
</div>
${tab === 'info' ? html`<${InfoTab} pub=${pub} priv=${priv} onChess=${onChess} bandId=${bandId} bandOwner=${bandOwner} />` : html`<${EnemiesTab} pub=${pub} priv=${priv} onEnemy=${onEnemy} />`}
</div>`;
}