// Combat HUD (research 06 §11.3/§11.7, research 09 §3.1 / §6.3): DP counter at the right edge and the bottom-centre // pills. // Client-side combat (`client` prop, DESIGN §14 — the official behaviour): // * own normal battle over: "⌛ 作战结束,等待队友完成作战" with each teammate's live progress (kills / total, ✓); // * observing a teammate (team row → 前往查看): "👁 name" + 返回战场; // * 联防 / 最终攻势: the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half ("你自己" / name / "全景"); it // replaces the observing pill on such a field watched with 前往查看 (a 联防 leaker, an eliminated spectator — the // official non-helper pill "‹ 👁 helper# ›"), with 返回战场 beside the arrows (DESIGN §20.15); // * there is NO view switcher during normal combat. // Server-run combat (legacy streaming mode): the ‹ 自己 › switcher cycling the live fields of m.public.fields. import { html, Icon } from './components.js'; import { DpCounter } from './hud.js'; import { GIcon } from './gameComponents.js'; import { switcherLabel, cycleField } from './gameLogic.js'; import { t } from '../../../shared/i18n.js'; const cx = (...p) => p.flat().filter(Boolean).join(' '); /** Teammates' progress under the waiting pill (css/screens/game.css .chud__progress / .chud__prog). */ function ProgressList({ list }) { if (!Array.isArray(list) || !list.length) return null; return html`
${list.map((p) => html` ${p.name} ${p.done ? html`✓` : html`${(p.resolved ?? p.killed) != null && p.total != null ? `${p.resolved ?? p.killed}/${p.total}` : '•••'}`} `)}
`; } /** * @param {{ pub:any, myId:string, watching:string|null, hud:any, myDone:boolean, onWatch:(fieldId:string)=>void, spectating?: boolean, * client?: null | { progress?: any[]|null, observing?: { name: string }|null, onBack?: () => void, * layers?: Array<{ key: string, label: string, self: boolean, watch: boolean }>, layer?: string, onLayer?: (k: string) => void } }} props */ export function CombatHud({ pub, myId, watching, hud, myDone, onWatch, spectating = false, spectator = false, client = null }) { // `spectator`: a spectator seat (community report #26) watches like an eliminated player, under its own caption if (client) return ClientHud({ hud, myDone, spectating, spectator, client }); const fields = (Array.isArray(pub?.fields) ? pub.fields : []).filter((f) => f && f.live !== false); // the watched field may already have finished (not live): still name it const label = switcherLabel(pub, watching, myId, spectating); const canCycle = fields.length > 1; return html`
<${DpCounter} dp=${hud?.dp} />
${myDone ? html`
<${Icon} name="hourglass" />${t('作战结束,等待队友完成作战')}
` : null} ${spectating ? (spectator ? html`
<${GIcon} name="eye" />${t('观战中')}
` : html`
<${Icon} name="close" />${t('你已被淘汰,正在观战')}
`) : null} ${fields.length ? html`
${label}
` : null}
`; } function ClientHud({ hud, myDone, spectating, spectator = false, client }) { const layers = Array.isArray(client.layers) ? client.layers : []; const idx = Math.max(0, layers.findIndex((l) => l.key === (client.layer || 'ALL'))); const cur = layers[idx] || null; const step = (d) => { if (!layers.length || !client.onLayer) return; const n = Math.min(layers.length - 1, Math.max(0, idx + d)); if (n !== idx) client.onLayer(layers[n].key); }; const observing = client.observing; const halves = layers.length > 0; return html`
<${DpCounter} dp=${hud?.dp} />
${myDone && !observing ? html`
<${Icon} name="hourglass" />${t('作战结束,等待队友完成作战')}
<${ProgressList} list=${client.progress} />` : null} ${spectating && !observing ? (spectator ? html`
<${GIcon} name="eye" />${t('观战中,可点击左侧成员头像前往查看')}
` : html`
<${Icon} name="close" />${t('你已被淘汰,可点击队友头像前往查看')}
`) : null} ${observing && !halves ? html`
<${GIcon} name="eye" />${observing.name} ${client.onBack ? html`` : null}
` : null} ${halves ? html`
${cur && cur.watch ? html`<${GIcon} name="eye" />` : null}${cur ? cur.label : t('全景')} ${observing && client.onBack ? html`` : null}
` : null}
`; }