The scouted prep board carries hand and 临时整备区 items as units (#129). The frame loop syncs every unit of the snapshot and ItemView had no sync, so it threw on every frame and the teammate's operators were never drawn. Contributed by @2321Robin.
63 lines
3.7 KiB
JavaScript
63 lines
3.7 KiB
JavaScript
// 观战整备区的客户端(游玩记录 #2 item 1):被侦察玩家的手牌是 m.field(prep:true).units 里手牌行
|
|
// (row 7)上的单位——干员站立、道具 kind 'item'(浮牌),和自己整备区同一渲染路径;观战棋盘用
|
|
// prep 相机(收起商店形态,bench 行进画面)。battleView 为 kind 'item' 建 ItemView(图标/颜色客户端
|
|
// 解析,同 pieceInfo),prep 面上给带装备的队友干员挂 item pips。服务器侧:test/match/prep-bench.test.js。
|
|
import { test, describe } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
const read = (p) => readFileSync(path.join(ROOT, p), 'utf8');
|
|
|
|
globalThis.fetch = async (url) => {
|
|
const name = String(url).split('/').pop();
|
|
try {
|
|
const body = readFileSync(path.join(ROOT, 'data', name), 'utf8');
|
|
return { ok: true, status: 200, json: async () => JSON.parse(body) };
|
|
} catch {
|
|
return { ok: false, status: 404, json: async () => ({}) };
|
|
}
|
|
};
|
|
const { data } = await import('../../public/js/data.js');
|
|
await data.loadAll('bonds', 'chess', 'items', 'assets');
|
|
const { resolveDetail } = await import('../../public/js/ui/detailPanel.js');
|
|
|
|
describe('the scouted prep board renders the hand like the own bench', () => {
|
|
test('the game screen frames it with the prep camera in its shop-folded form (source)', () => {
|
|
const game = read('public/js/screens/game.js');
|
|
assert.match(game, /field\.prep \? 'prep' : \(field\.kind === 'hidden' \? 'boss' : field\.kind \|\| 'normal'\)/);
|
|
assert.match(game, /field\.prep \? \{ shop: false \} : \{\}/);
|
|
assert.match(game, /const scoutPid = watchingOther && field\?\.prep/);
|
|
assert.ok(!game.includes('ScoutedBench'), 'the floating strip is gone — the hand rides the board');
|
|
assert.ok(!read('public/css/screens/game.css').includes('.sbench'), 'its styles too');
|
|
});
|
|
|
|
test('battleView builds an ItemView for a hand item and item pips for a teammate operator (source)', () => {
|
|
const app = read('public/js/render/app.js');
|
|
assert.match(app, /info\.kind === 'item' \? new ItemView\(ctx, scoutItemInfo\(info\)\)/);
|
|
assert.match(app, /battleMeta\?\.prep && Array\.isArray\(info\.items\) && info\.items\.length/);
|
|
assert.match(app, /function scoutItemInfo\(info\)/);
|
|
});
|
|
|
|
test('ItemView syncs battle samples: a hand item rides the scouted field without breaking syncBattle (render layer: source)', () => {
|
|
// syncBattle syncs EVERY unit of the field's snapshot (render/app.js `v.sync(s, renderT)`); the ItemView class
|
|
// needs a sync of its own or the frame loop throws `v.sync is not a function` on every tick and the scouted
|
|
// board's operators never draw (found in playtesting: only teammates with an item in hand / temp broke).
|
|
const units = read('public/js/render/units.js');
|
|
const start = units.indexOf('export class ItemView');
|
|
assert.ok(start > 0, 'ItemView exists');
|
|
const body = units.slice(start, units.indexOf('export class ', start + 1));
|
|
assert.match(body, /\bsync\(s\) \{ this\.x = s\.x; this\.y = s\.y; \}/);
|
|
});
|
|
|
|
test('tapping a held item on the scouted board opens its card (resolveDetail unit → item)', () => {
|
|
const IT = 'chess_item_1_01_e_a';
|
|
const d = resolveDetail({ kind: 'unit', unit: { id: 3, kind: 'item', side: 'ally', ownerId: 'p2', defId: IT } }, new Map());
|
|
assert.equal(d.type, 'item');
|
|
assert.equal(d.item.itemId || d.item.id, IT);
|
|
assert.equal(resolveDetail({ kind: 'unit', unit: { id: 4, kind: 'item', side: 'ally', ownerId: 'p2', defId: 'nope' } }, new Map()), null);
|
|
});
|
|
});
|