72 lines
3.9 KiB
JavaScript
72 lines
3.9 KiB
JavaScript
// 回归测试:多面地图的跨面传送门 + 存档点带面 + 内置关卡兼容
|
||
const handlers = {};
|
||
const addL = (t, fn) => { (handlers[t] = handlers[t] || []).push(fn); };
|
||
globalThis.addEventListener = addL;
|
||
globalThis.window = { addEventListener: addL };
|
||
const key = (type, code) => (handlers[type] || []).forEach(fn => fn({ code, preventDefault() { }, repeat: false }));
|
||
|
||
const { Level } = await import('../js/level.js');
|
||
const { Player } = await import('../js/player.js');
|
||
const { Input } = await import('../js/input.js');
|
||
|
||
const W = 20, TILE = 32;
|
||
let fail = 0;
|
||
const err = m => { console.log(' ✗ ' + m); fail++; };
|
||
const ok = m => console.log(' ✓ ' + m);
|
||
|
||
// 两面临门地图:A 面门 col5 指向 B 面门;B 面门 col10 指回 A 面门
|
||
const gridA = ['#'.repeat(W), '#' + 'S' + '.'.repeat(W - 3) + '#', '#' + '.'.repeat(4) + 'W' + '.'.repeat(W - 6) + '#', '#'.repeat(W), '#X' + '.'.repeat(W - 3) + '#', '#'.repeat(W), '#'.repeat(W), '#'.repeat(W), '#'.repeat(W), '#'.repeat(W)];
|
||
const gridB = ['#'.repeat(W), '#' + 'S' + '.'.repeat(W - 3) + '#', '#' + '.'.repeat(9) + 'W' + '.'.repeat(W - 11) + '#', '#'.repeat(W), '#X' + '.'.repeat(W - 3) + '#', '#'.repeat(W), '#'.repeat(W), '#'.repeat(W), '#'.repeat(W), '#'.repeat(W)];
|
||
const doorA = { id: 1, tx: 5, ty: 2, to: { face: 1, door: 1 } };
|
||
const doorB = { id: 1, tx: 10, ty: 2, to: { face: 0, door: 1 } };
|
||
const L0 = new Level({ name: 'A', type: 'custom', width: W, grid: gridA, doors: [doorA] }); L0.faceIndex = 0;
|
||
const L1 = new Level({ name: 'B', type: 'custom', width: W, grid: gridB, doors: [doorB] }); L1.faceIndex = 1;
|
||
|
||
// 1) 门目标正确挂接
|
||
{
|
||
const w0 = L0.entities.find(e => e.type === 'warp');
|
||
const w1 = L1.entities.find(e => e.type === 'warp');
|
||
if (!w0 || !w1) err('门实体未生成');
|
||
else {
|
||
if (w0.to && w0.to.face === 1 && w0.to.door === 1 && w0.doorId === 1) ok('A 面门挂上跨面目标 {face:1,door:1}');
|
||
else err('A 面门目标错误: ' + JSON.stringify(w0.to));
|
||
if (w1.to && w1.to.face === 0 && w1.to.door === 1 && w1.doorId === 1) ok('B 面门挂上跨面目标 {face:0,door:1}');
|
||
else err('B 面门目标错误: ' + JSON.stringify(w1.to));
|
||
}
|
||
}
|
||
|
||
// 2) 踩门触发 onPortal 钩子(main 据此切面)
|
||
{
|
||
const P = new Player(L0);
|
||
let captured = null; P.onPortal = (e) => { captured = e; };
|
||
const w0 = L0.entities.find(e => e.type === 'warp');
|
||
P.x = w0.x; P.y = w0.y; P.vx = 0; P.vy = 0;
|
||
L0.update(1, P, null); P.update(1); Input.endFrame();
|
||
if (captured && captured.to && captured.to.face === 1) ok('踩到门触发 onPortal,目标面=1');
|
||
else err('踩门未触发跨面 onPortal');
|
||
}
|
||
|
||
// 3) 存档点带 faceIndex
|
||
{
|
||
const gridC = ['#'.repeat(W), '#' + 'S' + '.'.repeat(W - 3) + '#', '#' + 'G' + '.'.repeat(W - 3) + '#', '#X' + '.'.repeat(W - 3) + '#'];
|
||
const LC = new Level({ name: 'C', type: 'custom', width: W, grid: gridC, doors: [] }); LC.faceIndex = 2;
|
||
const P = new Player(LC);
|
||
const g = LC.entities.find(e => e.type === 'save');
|
||
P.x = g.x; P.y = g.y;
|
||
LC.update(1, P, null); P.update(1); Input.endFrame();
|
||
if (P.savePoint && P.savePoint.face === 2) ok('存档点记录 face=2');
|
||
else err('存档点未带 face,得到 ' + (P.savePoint && P.savePoint.face));
|
||
}
|
||
|
||
// 4) 兼容:无 doors 的内置风格关卡,门仍走原有 pair 逻辑(to 为 null)
|
||
{
|
||
const gridD = ['#'.repeat(W), '#' + 'S' + '.'.repeat(W - 3) + '#', '#' + '.'.repeat(4) + 'W' + '.'.repeat(W - 6) + '#', '#'.repeat(W), '#' + '.'.repeat(4) + 'W' + '.'.repeat(W - 6) + '#', '#X' + '.'.repeat(W - 3) + '#'];
|
||
const LD = new Level({ name: 'D', type: 'jump', width: W, grid: gridD });
|
||
const ws = LD.entities.filter(e => e.type === 'warp');
|
||
if (ws.length === 2 && ws[0].to === null && ws[1].to === null) ok('无 doors 时门 to=null(走原有 pair 逻辑,不退化)');
|
||
else err('无 doors 时门目标异常: ' + JSON.stringify(ws.map(w => w.to)));
|
||
}
|
||
|
||
console.log(fail ? `\n❌ ${fail} 项失败` : '\n✅ 多面地图/传送门 全部通过');
|
||
process.exit(fail ? 1 : 0);
|