Watching a teammate shows their hand, 临时整备区, equipment, strategy and effects (#129)

The prep scout of another player's board carries their 整备区 hand and 临时整备区 as units on the bench rows (like the own prep bench), the equipped items of their operators, their strategy in the drawer and their effects column (the official mode shows a teammate's hand too). The scout signature includes the hand / temp slots, so a moved card refreshes the watcher. Closes #128. Contributed by @2321Robin.
This commit is contained in:
Luo Xingyu authored and GitHub committed 2026-10-05 16:47:33 +08:00
1 parent bd892a4dd6
commit bce182703b
15 files changed
+521 -20

No files matched your search

+47 -3
View File
@@ -943,7 +943,8 @@ export class Match {
return previewOf([...this.wave.spawns, ...bounty]);
}
/** UnitInfo list of a player's board (prep scouting). */
/** UnitInfo list of a player's board and hand (prep scouting): board pieces on their tiles, held pieces on the
* hand row (row 7) — the scout renders like the own prep bench. */
prepFieldMeta(ps) {
const units = [];
for (const { r, c, piece } of boardOrder(ps.board)) {
@@ -963,20 +964,63 @@ export class Match {
items: piece.kind === 'chess' && Array.isArray(piece.items) && piece.items.length ? piece.items.map((it) => it.id) : undefined,
});
}
// the hand (整备区) and the 临时整备区 scout exactly like the own prep bench renders them: pieces as units on
// their rows (hand row 7, col = hand slot; temp row 8, cols 4..8 = temp slots; no dir — bench pieces face right),
// items included (the client draws their floating plates). PRTS 帮助 counts the temp area with the hand (review of
// PR #129). Part of the meta for every watcher alike — the spectator seat's copy equals a teammate's
// (test/match/spectator.test.js). User playtest #2 item 1 (GitHub #44).
const benchUnit = (piece, i, y) => {
const rec = piece.kind === 'item' ? this.gd.item(piece.id) : piece.kind === 'token' ? this.gd.token(piece.id) : this.gd.chess(piece.id);
const assets = (rec && rec.assets) || {};
const lo = piece.kind === 'chess' && rec ? ps.loadoutFor(rec) : null;
units.push({
id: piece.uid, uid: piece.uid, kind: piece.kind === 'token' ? 'token' : piece.kind === 'item' ? 'item' : 'op',
side: 'ally', ownerId: ps.playerId, defId: piece.id,
name: rec ? rec.name : piece.id, tier: rec && Number.isInteger(rec.tier) ? rec.tier : 1, golden: !!(rec && rec.isGolden),
spine: assets.spine || (rec && rec.charId) || piece.id, avatar: assets.avatar || (rec && rec.charId) || piece.id,
x: i, y, maxHp: rec && rec.stats && Number.isFinite(rec.stats.maxHp) ? rec.stats.maxHp : 1,
skillIndex: lo && Number.isInteger(lo.skillIndex) ? lo.skillIndex : undefined,
moduleId: lo && typeof lo.moduleId === 'string' ? lo.moduleId : undefined,
items: piece.kind === 'chess' && Array.isArray(piece.items) && piece.items.length ? piece.items.map((it) => it.id) : undefined,
});
};
for (let i = 0; i < ps.hand.length; i++) {
if (ps.hand[i]) benchUnit(ps.hand[i], i, GEO.HAND_ROW);
}
for (let i = 0; i < ps.temp.length; i++) {
if (ps.temp[i]) benchUnit(ps.temp[i], GEO.TEMP_C0 + i, GEO.TEMP_ROW);
}
// `nextEnemies`: the scouted player's coming enemies — their preview pen shows on the scouting board too (research 09
// §2.2 "Teammates"; render/app.js enterBattle({ prep: true, nextEnemies }))
let nextEnemies = [];
try { nextEnemies = this.nextEnemiesFor(ps); } catch (e) { this.reportError('nextEnemies', e); }
return { t: 'm.field', fieldId: `n:${ps.playerId}`, kind: 'normal', rect: { ...GEO.NORMAL_RECT }, stageId: this.stageId, units, prep: true, nextEnemies };
// the scouted player's effects column (策略 / 机变 / 悬赏 …), display-ready (user playtest #2: while scouting, the
// right column shows the watched player's effects, not one's own)
return { t: 'm.field', fieldId: `n:${ps.playerId}`, kind: 'normal', rect: { ...GEO.NORMAL_RECT }, stageId: this.stageId, units, effects: ps.effectsView(), prep: true, nextEnemies };
}
/** Board signature of a prep scout view (units only: a shop or funds change is not a board change). */
/** Board signature of a prep scout view (units and hand: a shop or funds change is not a board change). */
/** Board signature of a prep scout view (board, hand and temp rows: a shop or funds change is not a board change).
* Hand / temp entries carry their slot — `prepFieldMeta` draws x from it, so a piece moved to another slot is a
* change (review of PR #129). */
_prepScoutSig(ps) {
const parts = [];
for (const { r, c, piece } of boardOrder(ps.board)) {
const items = piece.kind === 'chess' && Array.isArray(piece.items) ? piece.items.map((it) => `${it.uid}:${it.id}`).join(',') : '';
parts.push(`${piece.uid}:${piece.id}@${r},${c}:${pieceDir(piece)}:${items}`);
}
for (let i = 0; i < ps.hand.length; i++) {
const piece = ps.hand[i];
if (!piece) continue;
const items = piece.kind === 'chess' && Array.isArray(piece.items) ? piece.items.map((it) => `${it.uid}:${it.id}`).join(',') : '';
parts.push(`h${i}:${piece.uid}:${piece.id}:${items}`);
}
for (let i = 0; i < ps.temp.length; i++) {
const piece = ps.temp[i];
if (!piece) continue;
const items = piece.kind === 'chess' && Array.isArray(piece.items) ? piece.items.map((it) => `${it.uid}:${it.id}`).join(',') : '';
parts.push(`t${i}:${piece.uid}:${piece.id}:${items}`);
}
return parts.join(';');
}