feedback5 WN: 补位 display follows the official game (the owner's recall of 2026-10-06) — a chess the player does not own is shown as its stand-in wherever the player's copy appears: shop and reward cards (the stand-in's portrait, name, class and backup skill; the chess's bonds, tier and price; a small 「替补」 mark), the hand and 临时整备区 pieces (the stand-in's model, a 「替补」 tag; the board already was), the detail card (the stand-in's portrait, name, skill, talents and module with the chess's bonds and 特质, 「银灰的替补」 under the name [ASSUMED]), bond popups (own and a teammate's by UnitInfo standInFor through ownerBoard / ownerOps), the result lineup (m.result standInFor), prep scouting of the bench, the equip-replace dialog, the elite and gift tickers; rules unchanged (bonds, price, tier, merges, pools and 特质 stay the chess's); new strings through t() with English; tests updated (standin-ui 15, standin-ownership 7, watch-bonds wiring) and the e2e now ends on the result screen (fastServer SP_FINISH_AFTER); golden unchanged
This commit is contained in:
1 parent
78251f499c
commit
0e8004a382
32 files changed
+518
-181
No files matched your search
@@ -1,18 +1,23 @@
|
||||
// ui/gameLogic/standIn.js — 补位 stand-ins in the match UI (0.2.0, the approved plan — owner's decision 2026-10-05).
|
||||
// Re-exported from ../gameLogic.js.
|
||||
//
|
||||
// A chess the player marked as not owned (干员持有) keeps its card — name, bonds, price, the shop / reward / hand art —
|
||||
// with a 「替补:<stand-in>」 badge, and is deployed as its official stand-in: the board model, the battle unit and the
|
||||
// detail card's body (portrait, class, 特性, stats, range, skill, talents, module) are the stand-in's, its identity
|
||||
// (name, tier, bonds, 特质) the chess's. Whose pieces: the player's own come from m.private.standIns (the list the seat
|
||||
// had when the match started — the server's, not this browser's current setting, which applies from the next match);
|
||||
// another player's units say it themselves (UnitInfo `standInFor`: the sim's, and Match.prepFieldMeta's for scouting).
|
||||
// The composed record is shared/standIn.js standInRecord over data/backups.json — the one the sim fields.
|
||||
// A chess the player marked as not owned (干员持有) is SHOWN as its official stand-in wherever the player's copy of it
|
||||
// appears — the shop and reward cards, the hand (整备区) and 临时整备区 pieces, the board, the detail card, the result
|
||||
// screen, a teammate's prep scouting and bond popups — the owner's first-hand recall of the official mode, 2026-10-06
|
||||
// (it reverses the first 0.2.0 build, where cards and the bench kept the chess's art and name with a 「替补:X」 badge).
|
||||
// Only what is shown changes: the record (shared/standIn.js standInRecord over data/backups.json — the one the sim
|
||||
// fields) keeps the chess's identity — ids, tier, bonds, 特质, price, merge — so every rule still reads the chess; its
|
||||
// body — portrait / avatar, name, class, model, 特性, stats, range, skill, talents, module — is the stand-in's; a small
|
||||
// 「替补」 mark (standInLabel) and the replaced operator's name on the detail card (standInForText) tell which chess it
|
||||
// is. Whose pieces: the player's own come from m.private.standIns (the list the seat had when the match started — the
|
||||
// server's, not this browser's current setting, which applies from the next match); another player's units say it
|
||||
// themselves (UnitInfo `standInFor`: the sim's, Match.prepFieldMeta's for scouting, the m.result lineups).
|
||||
|
||||
import { standInRecord } from '../../../../shared/standIn.js';
|
||||
import { resolveLoadout } from '../../../../shared/protocol.js';
|
||||
import { chessLoadout } from './loadout.js';
|
||||
import { isObj } from './shared.js';
|
||||
import { t } from '../../../../shared/i18n.js';
|
||||
|
||||
/** Base chess ids the player fields as stand-ins in this match (m.private.standIns; [] when absent). */
|
||||
export function standInIds(priv) {
|
||||
@@ -33,10 +38,13 @@ const CACHE = new WeakMap();
|
||||
/**
|
||||
* The composed 补位 record of a chess record (shared/standIn.js standInRecord: the chess's identity, the stand-in's
|
||||
* body; `standInFor` = the replaced charId), cached per data object; null for a PRESET / 自选 chess or missing data.
|
||||
* A composed record is its own stand-in (a lookup that already hands out stand-in records stays idempotent).
|
||||
* @param {any} chess data/chess.json record @param {any} backups data/backups.json
|
||||
*/
|
||||
export function standInOf(chess, backups) {
|
||||
if (!isObj(chess) || !isObj(backups)) return null;
|
||||
if (!isObj(chess)) return null;
|
||||
if (typeof chess.standInFor === 'string' && chess.standInFor) return chess;
|
||||
if (!isObj(backups)) return null;
|
||||
let m = CACHE.get(backups);
|
||||
if (!m) { m = new WeakMap(); CACHE.set(backups, m); }
|
||||
if (m.has(chess)) return m.get(chess);
|
||||
@@ -47,8 +55,18 @@ export function standInOf(chess, backups) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The stand-in record the card of a chess shows for this viewer, or null: the player's own piece / card (m.private
|
||||
* standIns), or a unit that carries `standInFor` (another player's — the sim's UnitInfo, prep scouting).
|
||||
* The stand-in record the player's own card / piece of `chess` shows (the player fields its stand-in: m.private
|
||||
* standIns), or null.
|
||||
* @param {any} chess @param {any} priv m.private @param {any} backups
|
||||
*/
|
||||
export function ownStandIn(chess, priv, backups) {
|
||||
return fieldsStandIn(priv, chess) ? standInOf(chess, backups) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The stand-in record the card of a chess shows for this viewer, or null: a unit / lineup entry that carries
|
||||
* `standInFor` (another player's — the sim's UnitInfo, prep scouting, m.result), else the player's own piece / card
|
||||
* (m.private standIns).
|
||||
* @param {any} chess @param {{ priv?: any, unit?: any, backups?: any }} o
|
||||
*/
|
||||
export function cardStandIn(chess, { priv = null, unit = null, backups = null } = {}) {
|
||||
@@ -57,6 +75,27 @@ export function cardStandIn(chess, { priv = null, unit = null, backups = null }
|
||||
return mine ? standInOf(chess, backups) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The stand-in a bond popup's member row of base chess `chess` shows for the board `priv`, or null: a chess the player
|
||||
* fields as its stand-in (m.private.standIns — any copy, in play or not, as the shop shows it), or one whose piece on a
|
||||
* teammate's board says it is a stand-in (ui/watchBonds.js ownerBoard: the units' `standInFor`).
|
||||
* @param {any} priv m.private, or a teammate's board ({ board, hand, temp } of pieces with `standInFor`)
|
||||
* @param {any} chess the member's chess record @param {any} backups
|
||||
* @param {(id: string) => any} [getChess] the pieces' records (their base ids)
|
||||
*/
|
||||
export function memberStandIn(priv, chess, backups, getChess = () => null) {
|
||||
if (!isObj(chess) || typeof chess.chessId !== 'string') return null;
|
||||
if (fieldsStandIn(priv, chess)) return standInOf(chess, backups);
|
||||
const base = chess.baseId || chess.chessId;
|
||||
const baseOf = (id) => getChess(id)?.baseId || (typeof id === 'string' ? id.replace(/_b$/, '_a') : id);
|
||||
for (const list of [priv?.board, priv?.hand, priv?.temp]) {
|
||||
for (const p of Array.isArray(list) ? list : []) {
|
||||
if (isObj(p) && typeof p.standInFor === 'string' && p.standInFor && baseOf(p.id) === base) return standInOf(chess, backups);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** id → stand-in record (else the chess record): the lookup a stand-in's loadout / options resolve against. */
|
||||
export function standInGetter(getChess, backups) {
|
||||
return (id) => {
|
||||
@@ -104,7 +143,17 @@ export function deployedModuleId(chess, priv, getChess, backups) {
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
/** The 「替补:X」 badge text of a stand-in record (null without one). */
|
||||
/** The small 「替补」 mark of a stand-in record (null without one): cards, the own pieces' tags, thumbnails. */
|
||||
export function standInLabel(rec) {
|
||||
return isObj(rec) && rec.standInFor && rec.name ? `替补:${rec.name}` : null;
|
||||
return isObj(rec) && rec.standInFor ? t('替补') : null;
|
||||
}
|
||||
|
||||
/** The replaced operator's line of a stand-in's detail card: 「银灰的替补」 (`chessName` = the chess's own name). */
|
||||
export function standInForText(chessName) {
|
||||
return t('{name}的替补', { name: chessName || '' });
|
||||
}
|
||||
|
||||
/** The 「替补」 mark's title: which chess the stand-in fields for, and what stays the chess's ('' without a stand-in). */
|
||||
export function standInTip(rec, chessName) {
|
||||
return isObj(rec) && rec.standInFor ? t('未持有{name}:由替补干员 {standIn} 上场(盟约、特质、阶级与价格不变)', { name: chessName || '', standIn: rec.name || '' }) : '';
|
||||
}
|
||||
Reference in new issue
Block a user