feedback5: merge W5M — the nine 0.1.4 PRs (#150 #66 #146 #74 #72 #103 #149 #109 #160) and W4A (高台 per the trait, 阿戈尔 revive slots, 联防 devour) ported into the split layout

Conflicts: server/match/bot.js (W4A's comment + W5K's player data view pgd), test/golden/diy.json (regenerated). data/ equals an offline build of the merged tools/build-data.mjs. Golden regenerated on the merged code: against feedback5 the moved roster / bonds / fields / matches / standins scenarios are exactly W5M's per-PR attribution (#160 skill events, #109 deploy-timed lifecycle, #66 灵巧 sources and hidden layers, 83fc408 歌蕾蒂娅 placement), plus coop2-NORMAL-70-diy (fields operators those PRs touch) and diy-051 / diy-052 (events.skill only: Misery's deploy passives, #160); against fb5-merge4 only feedback5's own additions (the diy kits, coop2-NORMAL-70-diy).
This commit is contained in:
sganggs committed 2026-10-06 00:33:15 +08:00
commit 9ce5435373
74 files changed
+2966 -1338

No files matched your search

+15 -2
View File
@@ -163,6 +163,7 @@ function MatchScreen() {
const [watchWho, setWatchWho] = useState(null); // { fieldId, playerId }: the teammate picked with 前往查看
const [drawer, setDrawer] = useState(null); // 'enemies' | 'info' | null
const [bondOpen, setBondOpen] = useState(null); // { id, ownerId, from }: the bond popup and whose bond it shows
const [bondsCollapsed, setBondsCollapsed] = useState(false);
const [detail, setDetail] = useState(null); // detail target
const [collapsed, setCollapsed] = useState(false);
const [rewardMin, setRewardMin] = useState(false);
@@ -1215,8 +1216,20 @@ function MatchScreen() {
live=${liveLpNow} spectator=${spectator} />
<div class="gm__bonds">
<${BondStrip} bonds=${stripBonds} layersDisabled=${layersDisabled} openId=${bondPop && bondPop.ownerId === strip.ownerId ? bondPop.bondId : null}
owner=${strip.name} onOpen=${(id) => openBond(id, strip.ownerId, 'strip')} />
<button type="button" class="bonds-toggle" aria-expanded=${!bondsCollapsed} aria-controls="match-bond-strip"
aria-label=${bondsCollapsed ? '展开盟约' : '收起盟约'} title=${bondsCollapsed ? '展开盟约' : '收起盟约'}
onKeyDown=${(e) => {
// Keep native Space activation here without also firing the global ready / pause shortcut.
if (e.key === ' ') e.stopPropagation();
}}
onClick=${() => {
if (!bondsCollapsed && bondOpen?.from === 'strip') setBondOpen(null);
setBondsCollapsed(!bondsCollapsed);
}}><${Icon} name=${bondsCollapsed ? 'chevronRight' : 'chevronLeft'} /><span>${bondsCollapsed ? '盟约' : '收起'}</span></button>
<div id="match-bond-strip" class="gm__bond-list" hidden=${bondsCollapsed}>
<${BondStrip} bonds=${stripBonds} layersDisabled=${layersDisabled} openId=${bondPop && bondPop.ownerId === strip.ownerId ? bondPop.bondId : null}
owner=${strip.name} onOpen=${(id) => openBond(id, strip.ownerId, 'strip')} />
</div>
</div>
<${TeamPanel} pub=${pub} myId=${myId} watching=${watchingNow} bubbles=${bubbles} onWatch=${watchPlayer} cap=${gd.config?.lpCapPerRound ?? 10} uniteLocal=${uniteLocal}
+5 -3
View File
@@ -155,9 +155,11 @@ function SpectatorBar({ facts, myId, busy, onRemove, onSit }) {
</section>`;
}
function InviteBox({ code }) {
function InviteBox({ code, name, difficulty }) {
const copy = async (what) => {
const ok = await copyText(what === 'code' ? code : inviteLink(code));
// the copied link carries an invitation line (#103): 「{name}邀请你加入卫戍协议:盟约【{difficulty}】」
const invite = t('{name}邀请你加入卫戍协议:盟约【{difficulty}】', { name: name ?? '', difficulty: DIFFICULTY_NAMES[difficulty] ? t(DIFFICULTY_NAMES[difficulty]) : '' });
const ok = await copyText(what === 'code' ? code : `${inviteLink(code)} ${invite}`);
if (ok) toast(what === 'code' ? t('已复制同盟密钥 {code}', { code }) : t('已复制邀请链接'), 'success');
else toast(t('复制失败,请手动复制'), 'warn');
};
@@ -282,7 +284,7 @@ export function RoomScreen() {
<h1 class="topbar__title">${coop ? t('同盟模拟') : t('独立模拟')}<span class="topbar__sep"></span><${DifficultyTag} difficulty=${room.difficulty} size="lg" /></h1>
</div>
<div class="topbar__right">
${coop ? html`<${InviteBox} code=${room.code} />` : html`<div class="solo-note"><${MicroLabel}>SINGLE OPERATOR<//><span>${t('仅限 1 名博士')}</span></div>`}
${coop ? html`<${InviteBox} code=${room.code} name=${me.name} difficulty=${room.difficulty} />` : html`<div class="solo-note"><${MicroLabel}>SINGLE OPERATOR<//><span>${t('仅限 1 名博士')}</span></div>`}
</div>
</header>