From 41fd3c7e3913890cd82c7325f4f59ca3bb9a827e Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 17:43:37 +0800 Subject: [PATCH] =?UTF-8?q?feedback6=20R21C:=20the=20bond=20popup=20lists?= =?UTF-8?q?=20every=20=E8=87=AA=E9=80=89=20pick=20of=20the=20player=20(m.p?= =?UTF-8?q?rivate.diy,=20the=20V=20and=20VI=20slots)=20in=20each=20bond=20?= =?UTF-8?q?its=20operator=20carries=20=E2=80=94=20dimmed=20like=20an=20uno?= =?UTF-8?q?wned=20member=20until=20a=20copy=20is=20owned=20(not=20bought,?= =?UTF-8?q?=20or=20not=20in=20the=20shop=20yet),=20=E2=9C=95=20like=20a=20?= =?UTF-8?q?banned=20member=20when=20all=20its=20bonds=20are=20off=20this?= =?UTF-8?q?=20match=20(m.private.diyBanned),=20one=20row=20per=20slot=20dr?= =?UTF-8?q?awn=20as=20its=20normal=20form=20with=20the=20=E3=80=8C?= =?UTF-8?q?=E8=87=AA=E9=80=89=E3=80=8D=20tag,=20a=20tap=20opening=20the=20?= =?UTF-8?q?operator's=20card;=20=E5=9C=A8=E5=9C=BA=20and=20the=20=E6=88=90?= =?UTF-8?q?=E5=91=98=20header=20count=20an=20unowned=20pick=20for=20nothin?= =?UTF-8?q?g,=20the=20order=20rule=20is=20kept=20(the=20owner's=20report?= =?UTF-8?q?=20of=202026-10-07=20=E3=80=8C=E8=87=AA=E9=80=89=E7=BC=96?= =?UTF-8?q?=E9=98=9F=E7=9A=84=E5=B9=B2=E5=91=98=E5=9C=A8=E5=B1=80=E5=86=85?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9B=9F=E7=BA=A6=E5=B1=95=E5=BC=80=E7=9A=84?= =?UTF-8?q?=E5=90=8D=E5=8D=95=E9=87=8C=E6=B2=A1=E6=9C=89=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=E3=80=8D);=20a=20watched=20teammate's=20popu?= =?UTF-8?q?p=20still=20lists=20only=20their=20=E8=87=AA=E9=80=89=20pieces?= =?UTF-8?q?=20on=20the=20field=20(m.public=20sends=20no=20picks),=20and=20?= =?UTF-8?q?such=20a=20row=20now=20hands=20the=20unit's=20pick=20to=20the?= =?UTF-8?q?=20card=20(BondPopup=20onMember=20=E2=86=92=20the=20detail=20ta?= =?UTF-8?q?rget's=20diy=20=E2=86=92=20resolveDetail)=20instead=20of=20open?= =?UTF-8?q?ing=20the=20empty=20=E7=94=84=E9=80=89=E5=B9=B2=E5=91=98=20slot?= =?UTF-8?q?'s=20card;=20golden:=20unchanged=20(client)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/screens/game-panels.css | 3 + public/js/screens/game.js | 2 +- public/js/ui/bondStrip.js | 14 +- public/js/ui/detailPanel.js | 7 +- public/js/ui/gameLogic/bonds.js | 40 +++- test/ui/feedback6-diy-bond-members.test.js | 211 +++++++++++++++++++++ test/ui/watch-bonds.test.js | 4 +- 7 files changed, 264 insertions(+), 17 deletions(-) create mode 100644 test/ui/feedback6-diy-bond-members.test.js diff --git a/public/css/screens/game-panels.css b/public/css/screens/game-panels.css index 4589b71f..1cda7adb 100644 --- a/public/css/screens/game-panels.css +++ b/public/css/screens/game-panels.css @@ -298,6 +298,9 @@ .bpop__ban .icon { width: .14rem; height: .14rem; } /* a member through 变形同构体 (the item pairing, not the operator's own bond): a gold 同构 tag on the portrait */ .bpop__iso { position: absolute; top: .02rem; right: .08rem; padding: 0 .04rem; font-size: .11rem; line-height: 1.45; font-weight: 700; color: #141414; background: var(--gold-2); } +/* a 自选 pick all of whose bonds are off this match (m.private.diyBanned): its 「自选」 tag moves down to the portrait's + bottom corner (the 替补 mark's place), clear of the red ✕ and the tier chip */ +.bpop__member.is-banned .bpop__diy { top: .42rem; } .dbonds--top .dbond.is-granted { border-style: dashed; } .dbond__iso { font-size: .11rem; font-weight: 700; color: var(--gold-2); } /* a count that holds 调和's +1 (the server's bond entry `harmony`, DESIGN §21.26): 在场 n(含调和 +1) and the 调和 row that diff --git a/public/js/screens/game.js b/public/js/screens/game.js index 35f6d399..b72d54c9 100644 --- a/public/js/screens/game.js +++ b/public/js/screens/game.js @@ -1389,7 +1389,7 @@ function MatchScreen() { ${bondPop ? html`<${BondPopup} bondId=${bondPop.bondId} entry=${bondPop.entry} priv=${bondPop.priv} banned=${pub?.bannedChess || []} owner=${bondPop.name} off=${offBonds.has(bondPop.bondId)} place=${bpPlace} over=${!!resolved && bpPlace === dSide} - onClose=${() => setBondOpen(null)} onMember=${(id, items, standInFor) => setDetail({ kind: 'chess', id, owner: bondPop.ownerId, items: items || null, standInFor: standInFor || null })} />` : null} + onClose=${() => setBondOpen(null)} onMember=${(id, items, standInFor, diy) => setDetail({ kind: 'chess', id, owner: bondPop.ownerId, items: items || null, standInFor: standInFor || null, diy: diy || null })} />` : null} ${resolved ? html`<${DetailPanel} detail=${resolved} snapHp=${snapHp} onClose=${() => { setDetail(null); setSel(null); }} bonds=${detailBonds} offBonds=${offBonds} loadout=${detailLoadout} side=${dSide} shopOpen=${shopOpen} live=${liveStats} voice=${combat} diff --git a/public/js/ui/bondStrip.js b/public/js/ui/bondStrip.js index 17068757..b656f4f4 100644 --- a/public/js/ui/bondStrip.js +++ b/public/js/ui/bondStrip.js @@ -21,6 +21,11 @@ // 0.2.0 补位: a member the player fields as its stand-in (the own m.private.standIns; a teammate's unit that says so — // UnitInfo `standInFor`) is drawn as the stand-in — avatar, name, a small 「替补」 mark on the thumbnail — and its row // opens the stand-in's card (gameLogic memberStandIn; the owner's recall of the official mode, 2026-10-06). +// 0.2.1 自选编队 (the owner's report of 2026-10-07): each 自选 pick of the player (m.private.diy) is a row of every bond its +// operator carries — the operator's name and portrait with the 「自选」 tag, dimmed until owned (not bought, or not in the +// shop yet), ✕ when all its bonds are off this match (m.private.diyBanned) — and opens the operator's card; a watched +// teammate's 自选 pieces on the field are rows too (their picks are not sent), opening THEIR operator's card (the unit's +// pick goes with onMember: never the empty 甄选干员 slot's card). import { html, BondDisc, Icon, MicroLabel, Tooltip } from './components.js'; import { RichText, UnitThumb, BondGlyph, GIcon } from './gameComponents.js'; @@ -86,12 +91,13 @@ export function BondStrip({ bonds, layersDisabled = false, onOpen, openId = null /** * Bond detail popup. * @param {{ bondId: string, entry?: any, priv?: any, banned?: string[], onClose: Function, - * onMember?: (chessId:string, items?:string[]|null, standInFor?:string|null)=>void, + * onMember?: (chessId:string, items?:string[]|null, standInFor?:string|null, diy?:any)=>void, * place?: 'left'|'beside'|'besideR'|'right'|null, over?: boolean, beside?: 'left'|'right'|null, owner?: string|null, off?: boolean }} props — * `place`: where it opens (gameLogic bondPopupPlace; `beside: 'left'` = the older spelling of 'beside'); `over`: above the * detail card; `owner`: the watched teammate's name (`entry` / `priv` are then theirs: ui/watchBonds.js); `onMember` * gets a 变形同构体 row's item ids too (its card shows the pair and the granted chip), null for a plain member, and a - * stand-in row's `standInFor` (0.2.0 补位: a teammate's card then shows the stand-in too); `off`: the + * stand-in row's `standInFor` (0.2.0 补位: a teammate's card then shows the stand-in too), and a teammate's 自选 row's + * pick (`diy`, its unit's: the card composes their operator; the own rows' card reads m.private.diy); `off`: the * mode never activates this bond (gameLogic modeOffBonds — 标准's 10 inactive bonds): 本局禁用 instead of 未激活, with a * note, no 在场 count and no 当前效果 block (its numbers would promise an effect the mode never gives; the bond text stays). * `entry.harmony` (the server's bond views: the +1 调和 added to `count`): 在场 n(含调和 +1) and the 调和 row @@ -166,8 +172,8 @@ export function BondPopup({ bondId, entry, priv, banned = [], onClose, onMember, const name = si ? si.name : mb.name; const items = mb.granted && Array.isArray(mb.items) ? mb.items : null; return html`