feedback1 integrate: WK bond popup and card chips include 变形同构体 wearers

# Conflicts:
#	docs/SIM.md
#	public/js/ui/bondStrip.js
#	public/js/ui/detailPanel.js
#	server/sim/snapshot.js
This commit is contained in:
sganggs committed 2026-10-03 06:56:27 +08:00
commit ff0f8d0211
15 files changed
+526 -45

No files matched your search

+60 -8
View File
@@ -375,26 +375,78 @@ export function nextThreshold(count, thresholds) {
}
/**
* Member rows of a bond popup: every visible member with owned / on-board / banned state.
* Bonds granted by 变形同构体: an item with `canGiveBond` worn together with an item that has a `giveBondId` makes the
* wearer a member of that other item's bond (band 变形者集群 "与特定装备一同装备时装备者将视为特定盟约成员"; the item's
* talent, character_table trap_1073_acarm073, lists the 14 pairings). The same rule as the server's count
* (server/match/bondsMeta.js pieceBonds) and the battle's (server/sim/content/support unitBonds).
* @param {Array<string|{id:string}>|null|undefined} items the carried items (ids or { id } pieces)
* @param {(id:string)=>any} [getItem] items.json lookup
* @returns {string[]}
*/
export function grantedBonds(items, getItem = () => null) {
const list = Array.isArray(items) ? items : [];
if (list.length < 2) return [];
const recs = list.map((it) => getItem(typeof it === 'string' ? it : it?.id)).filter(isObj);
if (!recs.some((r) => r.canGiveBond)) return [];
const out = [];
for (const r of recs) if (!r.canGiveBond && typeof r.giveBondId === 'string' && r.giveBondId && !out.includes(r.giveBondId)) out.push(r.giveBondId);
return out;
}
/**
* A chess piece's bonds: its record's + those its 变形同构体 pairing grants (grantedBonds) — the detail card's chips.
* @param {any} chess chess.json record @param {Array<string|{id:string}>|null|undefined} items @param {(id:string)=>any} [getItem]
* @returns {string[]}
*/
export function pieceBondIds(chess, items, getItem = () => null) {
const out = (Array.isArray(chess?.bonds) ? chess.bonds : []).filter((b) => typeof b === 'string');
for (const b of grantedBonds(items, getItem)) if (!out.includes(b)) out.push(b);
return out;
}
/**
* Member rows of a bond popup: every visible member with owned / on-board / banned state, plus the player's operators
* that are members through 变形同构体 (grantedBonds; `granted: true` and `items`: the item ids of the copy that wears the
* pair — the card the row opens shows them; one row per operator — normal and elite copies are one member, like the
* count's distinct members), so "成员 x/y" agrees with the count the server reports (在场).
* @param {any} bond bonds.json record
* @param {any} priv m.private (hand/board/temp)
* @param {any} priv m.private (hand/board/temp) — 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)
*/
export function bondMembers(bond, priv, banned = [], getChess = () => null) {
export function bondMembers(bond, priv, banned = [], getChess = () => null, getItem = () => null) {
const bannedSet = banned instanceof Set ? banned : new Set(Array.isArray(banned) ? banned : []);
const members = Array.isArray(bond?.visibleMembers) && bond.visibleMembers.length ? bond.visibleMembers : (Array.isArray(bond?.members) ? bond.members : []);
const baseOf = (id) => getChess(id)?.baseId || (typeof id === 'string' ? id.replace(/_b$/, '_a') : id);
const onBoard = new Set();
const owned = new Set();
for (const p of Array.isArray(priv?.board) ? priv.board : []) if (p?.kind === 'chess') { onBoard.add(baseOf(p.id)); owned.add(baseOf(p.id)); }
for (const p of [...(Array.isArray(priv?.hand) ? priv.hand : []), ...(Array.isArray(priv?.temp) ? priv.temp : [])]) {
if (p?.kind === 'chess') owned.add(baseOf(p.id));
const memberSet = new Set(members);
/** base id → { on: on the board?, 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);
const itemIds = (p) => p.items.map((it) => (typeof it === 'string' ? it : it?.id)).filter((x) => typeof x === 'string');
for (const p of Array.isArray(priv?.board) ? priv.board : []) {
if (p?.kind !== 'chess') continue;
const base = baseOf(p.id);
onBoard.add(base); owned.add(base);
if (!memberSet.has(base) && !granted.has(base) && grants(p)) granted.set(base, { on: true, items: itemIds(p) });
}
return members.map((id) => {
for (const p of [...(Array.isArray(priv?.hand) ? priv.hand : []), ...(Array.isArray(priv?.temp) ? priv.temp : [])]) {
if (p?.kind !== 'chess') continue;
const base = baseOf(p.id);
owned.add(base);
if (!memberSet.has(base) && !granted.has(base) && grants(p)) granted.set(base, { on: false, items: itemIds(p) });
}
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), banned: bannedSet.has(id) };
}).sort((a, b) => (b.onBoard - a.onBoard) || (b.owned - a.owned) || (a.tier - b.tier) || (a.id < b.id ? -1 : 1));
});
for (const [id, g] of granted) {
const c = getChess(id);
rows.push({ id, tier: c?.tier ?? 0, name: c?.name ?? id, onBoard: g.on, owned: true, banned: false, granted: true, items: g.items });
}
return rows.sort((a, b) => (b.onBoard - a.onBoard) || (b.owned - a.owned) || (a.tier - b.tier) || (a.id < b.id ? -1 : 1));
}
/**