清单 §5.2 表格写的是「牌型分组标记 ×8」,但 §1.6 明确规定三种标记互斥、【每张牌】 至多一个,core/CardMark.js 也是按每张牌返回标记的。一手 28 张牌通常带 13 个以上标记、 36 张埋牌手更多,8 个远远不够。§1.6 是行为规格、比表格措辞更权威,故按 §1.6 裁决: HAND_MARK_1..36 与 HAND_CARD_1..36 一一对应。 - ID 另起 2100–2135 整段空号:1236 往后接不下 36 个连号(1250 起已是底牌/埋牌区), 换空号段比给别的 View 重新编号更安全;仍落在子游戏段 1001–2999,无重复。 - 守卫补「手牌与标记 1..36 一一对应且同群组」检查。 - 精灵总数由「> 200」改为钉死 387(test_constants / test_spriteindex 两处同一个数), 布局节点数同理已钉死——松下界删掉整个 View 都还能通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
4.0 KiB
JavaScript
89 lines
4.0 KiB
JavaScript
// View → group → 精灵 的常量树 → 扁平键名索引
|
||
const { load, throws } = require('./_load');
|
||
const t = require('./_assert')();
|
||
|
||
load('client/js/01_SubGame/codes/core/SpriteIndex.js');
|
||
|
||
// ---- 展平:Layer 与 group 的 id 都不进索引,精灵进 ----
|
||
const tree = {
|
||
TopInfoView: {
|
||
Layer: 101,
|
||
TopInfo: { id: 201, BTN_A: 1001, BTN_B: 1002 }
|
||
},
|
||
PlayerMarkView: {
|
||
Layer: 101,
|
||
LeftMark: { id: 202, P_LEFT_BANKER: 1100 },
|
||
RightMark: { id: 203, P_RIGHT_BANKER: 1110 }
|
||
}
|
||
};
|
||
t.eq('展平结果', EQW_SpriteIndex.build(tree),
|
||
{ BTN_A: 1001, BTN_B: 1002, P_LEFT_BANKER: 1100, P_RIGHT_BANKER: 1110 });
|
||
|
||
// ---- Layer 与 group 的 id 不进索引 ----
|
||
const flat = EQW_SpriteIndex.build(tree);
|
||
t.eq('Layer 不进索引', flat.Layer, undefined);
|
||
t.eq('group 的 id 不进索引', flat.id, undefined);
|
||
|
||
// ---- 群组映射:精灵 → 它所属的群组 ID ----
|
||
t.eq('群组映射', EQW_SpriteIndex.buildGroupMap(tree),
|
||
{ BTN_A: 201, BTN_B: 201, P_LEFT_BANKER: 202, P_RIGHT_BANKER: 203 });
|
||
|
||
// ---- 反面:一个 View 恰好一个 Layer;Layer1/Layer2 这种多图层写法要报错 ----
|
||
// (跨图层的界面应拆成两个 View,各带自己的 Layer——一个 View 将来对应一个 BaseComponent)
|
||
const multiLayer = {
|
||
OverlayView: { Layer1: 104, Layer2: 105, Bar: { id: 230, TIP_BG: 1750 } }
|
||
};
|
||
t.eq('多 Layer 写法报错', throws(() => EQW_SpriteIndex.build(multiLayer)), true);
|
||
|
||
// ---- 反面:View 缺 Layer 声明要报错 ----
|
||
const noLayer = { ViewA: { G1: { id: 201, BTN: 1001 } } };
|
||
t.eq('View 缺 Layer 报错', throws(() => EQW_SpriteIndex.build(noLayer)), true);
|
||
|
||
// ---- 反面:重复键报错,不静默覆盖 ----
|
||
const dupTree = {
|
||
ViewA: { Layer: 101, G1: { id: 201, SAME: 1001 } },
|
||
ViewB: { Layer: 102, G2: { id: 202, SAME: 1002 } }
|
||
};
|
||
t.eq('重复键报错', throws(() => EQW_SpriteIndex.build(dupTree)), true);
|
||
|
||
// ---- 反面:精灵值不是数字要报错 ----
|
||
const badValue = { ViewA: { Layer: 101, G1: { id: 201, NESTED: { x: 1 } } } };
|
||
t.eq('精灵值非数字报错', throws(() => EQW_SpriteIndex.build(badValue)), true);
|
||
|
||
// ---- 反面:View 下混入标量键(不是 group 容器)要报错 ----
|
||
// 例如把群组 ID 误写成 View 级的 `Group: 201`,而不是包进 group 容器
|
||
const strayScalar = { ViewA: { Layer: 101, Group: 201, G1: { id: 202, BTN: 1001 } } };
|
||
t.eq('View 下混入标量键报错', throws(() => EQW_SpriteIndex.build(strayScalar)), true);
|
||
|
||
// ---- 反面:group 容器缺 id 要报错 ----
|
||
const noGroupId = { ViewA: { Layer: 101, G1: { BTN: 1001 } } };
|
||
t.eq('group 缺 id 报错', throws(() => EQW_SpriteIndex.build(noGroupId)), true);
|
||
|
||
// ---- idOf / groupIdOf:查得到 / 查不到抛错 ----
|
||
EQW_SpriteIndex.init(tree);
|
||
t.eq('idOf 查到', EQW_SpriteIndex.idOf('BTN_A'), 1001);
|
||
t.eq('groupIdOf 查到', EQW_SpriteIndex.groupIdOf('P_RIGHT_BANKER'), 203);
|
||
t.eq('idOf 查不到抛错', throws(() => EQW_SpriteIndex.idOf('NOT_EXIST')), true);
|
||
t.eq('groupIdOf 查不到抛错', throws(() => EQW_SpriteIndex.groupIdOf('NOT_EXIST')), true);
|
||
|
||
// ---- 空树 ----
|
||
t.eq('空树', EQW_SpriteIndex.build({}), {});
|
||
|
||
// ---- 真实常量:EQW_Sprites 能完整建索引且无重复键 ----
|
||
load('client/js/01_SubGame/codes/config/Layers.js');
|
||
load('client/js/01_SubGame/codes/config/Groups.js');
|
||
load('client/js/01_SubGame/codes/config/Sprites_Table.js');
|
||
load('client/js/01_SubGame/codes/config/Sprites_Cards.js');
|
||
load('client/js/01_SubGame/codes/config/Sprites_Action.js');
|
||
load('client/js/01_SubGame/codes/config/Sprites_Result.js');
|
||
load('client/js/01_SubGame/codes/config/Sprites_CreateRoom.js');
|
||
|
||
let realOk = true;
|
||
let realIndex = {};
|
||
try { realIndex = EQW_SpriteIndex.build(EQW_Sprites); } catch (e) { realOk = false; console.log('建索引失败: ' + e.message); }
|
||
t.eq('真实精灵常量无重复键', realOk, true);
|
||
// 钉住精确数量(与 test_constants.js 同一个数):松下界删掉整个 View 都还能通过
|
||
t.eq('真实精灵数量', Object.keys(realIndex).length, 387);
|
||
|
||
process.exit(t.done('spriteindex') ? 0 : 1);
|