#!/usr/bin/env node /** * 自测等距 frontKey 排序(与 UnityDrawSort.ts 对齐) * 运行: node tools/verify-unity-draw-sort.mjs */ const CELL = 100; const RANK_FLOOR = 0; const RANK_PICKABLE = 20; const RANK_VEHICLE = 30; const RANK_PLAYER = 35; const RANK_WALL = 40; function frontKey(x, y, rank) { return -(x + y) * CELL + rank; } function entityInFrontOfTile(entity, tile) { const eRank = entity.isPickable ? RANK_PICKABLE : (entity.isPlayer ? RANK_PLAYER : RANK_VEHICLE); let eKey = frontKey(entity.cellX, entity.cellY, eRank); // 贴墙立面抬升 const faces = [ { x: tile.cellX, y: tile.cellY - 1 }, { x: tile.cellX - 1, y: tile.cellY }, ]; const onFace = faces.some((f) => f.x === entity.cellX && f.y === entity.cellY); const tKey = frontKey(tile.cellX, tile.cellY, tile.tileName === 'WallBlock' ? RANK_WALL : RANK_FLOOR); if (tile.tileName === 'WallBlock' && onFace) eKey = Math.max(eKey, tKey + 1); return eKey > tKey; } function tileInFrontOfTile(a, b) { return frontKey(a.x, a.y, RANK_FLOOR) > frontKey(b.x, b.y, RANK_FLOOR); } const cases = [ { name: '砖:南 (0,-2) 挡北 (0,-1)', pass: tileInFrontOfTile({ x: 0, y: -2 }, { x: 0, y: -1 }), }, { name: '北墙 (-1,1) 挡住中心角色 (0,0)', pass: !entityInFrontOfTile( { cellX: 0, cellY: 0 }, { cellX: -1, cellY: 1, tileName: 'WallBlock' }, ), }, { name: '东墙 (1,-1) 挡住中心角色 (0,0)(同对角立面)', pass: !entityInFrontOfTile( { cellX: 0, cellY: 0 }, { cellX: 1, cellY: -1, tileName: 'WallBlock' }, ), }, { name: '南 tip 墙 (-1,-3) 挡住金币 (0,-3)', pass: !entityInFrontOfTile( { cellX: 0, cellY: -3, isPickable: true }, { cellX: -1, cellY: -3, tileName: 'WallBlock' }, ), }, { name: '金币 (0,-3) 在东后墙 (1,-3) 之前(屏上金币更靠前)', pass: entityInFrontOfTile( { cellX: 0, cellY: -3, isPickable: true }, { cellX: -1, cellY: -2, tileName: 'Baseblock' }, ) && entityInFrontOfTile( { cellX: 0, cellY: -3, isPickable: true }, { cellX: 1, cellY: -3, tileName: 'WallBlock' }, ), }, { name: '贴南墙立面 (0,-1) 露在墙 (0,0) 前', pass: entityInFrontOfTile( { cellX: 0, cellY: -1 }, { cellX: 0, cellY: 0, tileName: 'WallBlock' }, ), }, { name: '南墙 (0,-1) 挡住角色 (0,0)', pass: !entityInFrontOfTile( { cellX: 0, cellY: 0 }, { cellX: 0, cellY: -1, tileName: 'WallBlock' }, ), }, { name: '左墙 (-6,-2) 挡住角色 (-4,-3)', pass: !entityInFrontOfTile( { cellX: -4, cellY: -3 }, { cellX: -6, cellY: -2, tileName: 'WallBlock' }, ), }, { name: '载具朝前钉起点:相对南侧地砖仍被挡', pass: !entityInFrontOfTile( { cellX: 0, cellY: 0, isPlayer: false }, { cellX: 0, cellY: -1, tileName: 'Baseblock' }, ), }, { name: '载具朝后用落点:从(0,0)→(1,0) 比钉起点更靠后(继续被下方砖挡)', pass: (() => { const floor = { cellX: 0, cellY: -1, tileName: 'Baseblock' }; // 起点更靠前,相对该南侧砖「不挡得那么死」;落点更靠后应仍被挡住 const startKeyWins = entityInFrontOfTile( { cellX: 0, cellY: 0, isPlayer: false }, floor, ); const landOccluded = !entityInFrontOfTile( { cellX: 1, cellY: 0, isPlayer: false }, floor, ); return !startKeyWins && landOccluded; })(), }, { name: '角色朝前用落点:从(0,1)→(0,0) 途中露前', pass: (() => { const from = { cellX: 0, cellY: 1 }; const to = { cellX: 0, cellY: 0 }; const wall = { cellX: 1, cellY: 0, tileName: 'WallBlock' }; return !entityInFrontOfTile(from, wall) && entityInFrontOfTile(to, wall); })(), }, { name: '同格:角色永远盖过载具', pass: frontKey(0, 0, RANK_PLAYER) > frontKey(0, 0, RANK_VEHICLE), }, { name: '南侧地砖可挡住同侧偏后的实体(下方地砖应遮挡载具)', pass: !entityInFrontOfTile( { cellX: 0, cellY: 0 }, { cellX: 0, cellY: -1, tileName: 'Baseblock' }, ), }, { name: '人物与载具同格 frontKey 条带一致(主题无关)', pass: Math.floor(frontKey(2, -1, RANK_PLAYER) / 100) === Math.floor(frontKey(2, -1, RANK_VEHICLE) / 100), }, ]; let failed = 0; for (const c of cases) { const ok = !!c.pass; console.log(`${ok ? '✓' : '✗'} ${c.name}`); if (!ok) failed++; } if (failed > 0) { console.error(`\n${failed} case(s) failed`); process.exit(1); } console.log(`\nAll ${cases.length} cases passed.`);