feedback6 R21C: the bond popup lists every 自选 pick of the player (m.private.diy, the V and VI slots) in each bond its operator carries — dimmed like an unowned member until a copy is owned (not bought, or not in the shop yet), ✕ like a banned member when all its bonds are off this match (m.private.diyBanned), one row per slot drawn as its normal form with the 「自选」 tag, a tap opening the operator's card; 在场 and the 成员 header count an unowned pick for nothing, the order rule is kept (the owner's report of 2026-10-07 「自选编队的干员在局内对应盟约展开的名单里没有显示出来」); a watched teammate's popup still lists only their 自选 pieces on the field (m.public sends no picks), and such a row now hands the unit's pick to the card (BondPopup onMember → the detail target's diy → resolveDetail) instead of opening the empty 甄选干员 slot's card; golden: unchanged (client)

This commit is contained in:
sganggs committed 2026-10-07 17:43:37 +08:00
1 parent 783bd30afd
commit 41fd3c7e39
7 files changed
+264 -17

No files matched your search

+32 -8
View File
@@ -1,6 +1,7 @@
// ui/gameLogic/bonds.js — bond counts, members, harmony, 本局禁用. Re-exported from ../gameLogic.js.
import { int, isObj } from './shared.js';
import { diyPicks } from './diy.js';
import { t } from '../../../../shared/i18n.js';
@@ -158,10 +159,17 @@ export function harmonyMembers(priv, getChess = () => null) {
* like the count's distinct members), so "成员 x/y" agrees with the count the server reports (在场; memberHeadCount).
* 0.2.0 自选编队: the 自选 pieces of the bond (a DIY slot filled with an operator, whose bonds come from its factions — the
* own pieces through `getChess` (gameLogic/diy.js diyGetter), a teammate's through `pieceRecord` (its UnitInfo `diy`
* pick) are rows too (`diy: true`, the operator's name and record `rec`).
* pick) are rows too (`diy: true`, the operator's name and record `rec`: its normal form, one row per slot like a
* member's; `pick`: a teammate's piece's pick — the card the row opens composes the operator from it). Since 0.2.1
* (the owner's report of 2026-10-07 「自选编队的干员在局内对应盟约展开的名单里没有显示出来」) every 自选 pick of the
* player (m.private.diy: the V and VI slots) is a row of each bond its operator carries, owned or not — an unowned one
* (not bought, or not in the shop yet: the slot's 调度中心 level) dimmed like an unowned member, one whose bonds are all
* off this match (m.private.diyBanned: no stock, it never shows in the shop) `banned` like a banned member. A
* teammate's board carries no picks (m.public sends none): their 自选 pieces on the field only.
* `inHand`: in the 整备区 (hand), not the 5 temporary slots — what BOARD_AND_DECK bonds (投资人 远见 奇迹) count.
* @param {any} bond bonds.json record
* @param {any} priv m.private (hand/board/temp) — or a teammate's field operators (ui/watchBonds.js ownerBoard)
* @param {any} priv m.private (hand/board/temp, the 自选 picks `diy` / `diyBanned`) — or a teammate's field operators
* (ui/watchBonds.js ownerBoard)
* @param {Set<string>|string[]} [banned] banned base chess ids
* @param {(id:string)=>any} [getChess]
* @param {(id:string)=>any} [getItem] items.json lookup (the 变形同构体 pairings)
@@ -178,14 +186,20 @@ export function bondMembers(bond, priv, banned = [], getChess = () => null, getI
/** base id → { on: on the board?, hand: in the hand?, items: the wearer's item ids } — operators of the player that join this bond through 变形同构体 */
const granted = new Map();
const grants = (p) => typeof bond?.bondId === 'string' && grantedBonds(p.items, getItem).includes(bond.bondId);
/** base id → { on, hand, rec } — the player's 自选 pieces whose operator carries this bond */
const recOf = (p) => (pieceRecord ? pieceRecord(p) : getChess(p.id));
/** a composed 自选 record whose operator carries this bond */
const diyOfBond = (rec) => isObj(rec) && typeof rec.diyFor === 'string' && Array.isArray(rec.bonds) && rec.bonds.includes(bond?.bondId);
/** slot base id → { on, hand, owned, rec, pick } — the player's 自选 pieces and picks whose operator carries this bond */
const diy = new Map();
const diyOf = (p, on, hand) => {
const rec = pieceRecord ? pieceRecord(p) : getChess(p.id);
if (!rec || typeof rec.diyFor !== 'string' || !Array.isArray(rec.bonds) || !rec.bonds.includes(bond?.bondId)) return false;
const rec = recOf(p);
if (!diyOfBond(rec)) return false;
const g = diy.get(rec.diyFor);
if (!g) diy.set(rec.diyFor, { on, hand, rec });
else { g.on = g.on || on; g.hand = g.hand || hand; }
if (g) { g.on = g.on || on; g.hand = g.hand || hand; return true; }
// the row draws the normal form, as a member row its base chess (an elite copy is the same member)
const pick = isObj(p.diy) && typeof p.diy.charId === 'string' ? p.diy : null;
const normal = rec.isGolden ? recOf(pick ? { kind: 'chess', id: rec.diyFor, diy: pick } : { kind: 'chess', id: rec.diyFor }) : rec;
diy.set(rec.diyFor, { on, hand, owned: true, rec: diyOfBond(normal) && normal.diyFor === rec.diyFor ? normal : rec, pick });
return true;
};
const itemIds = (p) => p.items.map((it) => (typeof it === 'string' ? it : it?.id)).filter((x) => typeof x === 'string');
@@ -209,6 +223,14 @@ export function bondMembers(bond, priv, banned = [], getChess = () => null, getI
else if (hand) g.hand = true;
}
}
// the player's picks not owned (the slot's record through the own getter: gameLogic/diy.js diyGetter) — rows like an
// unowned member's (counted by neither 在场 nor the 成员 header)
for (const slotId of Object.keys(diyPicks(priv))) {
if (diy.has(slotId)) continue;
const rec = recOf({ kind: 'chess', id: slotId });
if (diyOfBond(rec) && rec.diyFor === slotId) diy.set(slotId, { on: false, hand: false, owned: false, rec, pick: null });
}
const diyBanned = new Set(Array.isArray(priv?.diyBanned) ? priv.diyBanned : []);
const rows = members.map((id) => {
const c = getChess(id);
return { id, tier: c?.tier ?? 0, name: c?.name ?? id, onBoard: onBoard.has(id), owned: owned.has(id), inHand: inHand.has(id), banned: bannedSet.has(id) };
@@ -217,7 +239,9 @@ export function bondMembers(bond, priv, banned = [], getChess = () => null, getI
const c = getChess(id);
rows.push({ id, tier: c?.tier ?? 0, name: c?.name ?? id, onBoard: g.on, owned: true, inHand: g.hand, banned: false, granted: true, items: g.items });
}
for (const [id, g] of diy) rows.push({ id, tier: g.rec.tier ?? 0, name: g.rec.name ?? id, onBoard: g.on, owned: true, inHand: g.hand, banned: false, diy: true, rec: g.rec });
for (const [id, g] of diy) {
rows.push({ id, tier: g.rec.tier ?? 0, name: g.rec.name ?? id, onBoard: g.on, owned: g.owned, inHand: g.hand, banned: bannedSet.has(id) || diyBanned.has(id), diy: true, rec: g.rec, ...(g.pick ? { pick: g.pick } : {}) });
}
return rows.sort((a, b) => (b.onBoard - a.onBoard) || (b.owned - a.owned) || (a.tier - b.tier) || (a.id < b.id ? -1 : 1));
}