diff --git a/cocoscreator_projects/scripts/test/ui-buckets.test.mjs b/cocoscreator_projects/scripts/test/ui-buckets.test.mjs new file mode 100644 index 0000000..59f7715 --- /dev/null +++ b/cocoscreator_projects/scripts/test/ui-buckets.test.mjs @@ -0,0 +1,82 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { join } from 'node:path'; +import { layerBucket, imageBuckets } from '../ui-migration/buckets.mjs'; +import { loadSource } from '../ui-migration/gameabc-source.mjs'; +import { ROOT } from '../lib/paths.mjs'; + +const REAL_SOURCE = join(ROOT, '..', 'projects', 'Game_Surface_3', 'output'); + +test('layerBucket 按名单判 login', () => { + assert.equal(layerBucket(1, 'Logo_Layer'), 'login'); + assert.equal(layerBucket(2, 'Login_Layer'), 'login'); + assert.equal(layerBucket(18, 'Auth_layer'), 'login'); + assert.equal(layerBucket(16, 'Bind_Layer'), 'login'); + assert.equal(layerBucket(29, 'phoneInfo'), 'login'); +}); + +test('layerBucket 按 ID 段位判 hall / room / common', () => { + assert.equal(layerBucket(4, 'MainMenu_Layer'), 'hall'); + assert.equal(layerBucket(26, 'Task_Layer'), 'hall'); + assert.equal(layerBucket(50, 'MainScene_Layer'), 'room'); + assert.equal(layerBucket(202, 'Player_Head_Score_Layer'), 'room'); + assert.equal(layerBucket(418, 'ChatPannel_Layer'), 'room'); + assert.equal(layerBucket(602, 'inputPannel'), 'common'); + assert.equal(layerBucket(614, 'Loading_Layer'), 'common'); +}); + +test('layerBucket 对命名笼统的层返回 unassigned(不猜)', () => { + for (const [id, name] of [[6, 'Layer6'], [13, 'Layer13'], [403, 'Layer403'], + [411, 'Layer411'], [601, 'Layer601'], [603, 'Layer603'], [618, 'Layer618']]) { + assert.equal(layerBucket(id, name), 'unassigned', `${name} 应为 unassigned`); + } +}); + +test('layerBucket 的 unassigned 优先于段位规则', () => { + // Layer601 若只按段位会判成 common,但名字笼统 → 必须 unassigned + assert.equal(layerBucket(601, 'Layer601'), 'unassigned'); +}); + +test('imageBuckets 单一 bucket 的图归该 bucket', () => { + const source = { + objects: new Map([[1, { ObjectID: 1, ImageFileID: 7, BelongLayerID: 2 }]]), + layers: [{ layerId: 2, name: 'Login_Layer', objectIds: [1] }], + images: new Map([[7, { id: 7, bmp: 'a.png', frame_all: 1 }]]), + groups: [], + }; + assert.equal(imageBuckets(source).get(7), 'login'); +}); + +test('imageBuckets 跨 bucket 的图归 common', () => { + const source = { + objects: new Map([ + [1, { ObjectID: 1, ImageFileID: 7, BelongLayerID: 2 }], + [2, { ObjectID: 2, ImageFileID: 7, BelongLayerID: 4 }], + ]), + layers: [ + { layerId: 2, name: 'Login_Layer', objectIds: [1] }, + { layerId: 4, name: 'MainMenu_Layer', objectIds: [2] }, + ], + images: new Map([[7, { id: 7, bmp: 'a.png', frame_all: 1 }]]), + groups: [], + }; + assert.equal(imageBuckets(source).get(7), 'common'); +}); + +test('imageBuckets 无对象引用的图归 unassigned', () => { + const source = { + objects: new Map(), + layers: [], + images: new Map([[7, { id: 7, bmp: 'orphan.png', frame_all: 1 }]]), + groups: [], + }; + assert.equal(imageBuckets(source).get(7), 'unassigned'); +}); + +test('imageBuckets 在真实数据上给每张图一个归属(回归护栏)', () => { + const source = loadSource(REAL_SOURCE); + const m = imageBuckets(source); + assert.equal(m.size, source.images.size, '每张图都要有归属'); + const valid = new Set(['login', 'hall', 'room', 'common', 'unassigned']); + for (const [id, b] of m) assert.ok(valid.has(b), `图 ${id} 的 bucket 非法: ${b}`); +}); diff --git a/cocoscreator_projects/scripts/ui-migration/buckets.mjs b/cocoscreator_projects/scripts/ui-migration/buckets.mjs new file mode 100644 index 0000000..f518973 --- /dev/null +++ b/cocoscreator_projects/scripts/ui-migration/buckets.mjs @@ -0,0 +1,53 @@ +/** + * 层与图的 bucket 归属。见 spec §3.3。 + * bucket 决定资源落进 framework/ui/atlas-{common,login,hall,room} 哪个目录。 + */ + +/** 登录相关:按层名点名,因其 LayerID 散落在 1..29 段内。 */ +const LOGIN_LAYERS = new Set(['Logo_Layer', 'Login_Layer', 'Auth_layer', 'Bind_Layer', 'phoneInfo']); + +/** 命名笼统、语义不明的层:标 unassigned 交人工归位,不猜。 */ +const UNASSIGNED_LAYERS = new Set([ + 'Layer6', 'Layer13', 'Layer403', 'Layer411', 'Layer601', 'Layer603', 'Layer618', +]); + +const ROOM_ID_MIN = 50; +const COMMON_ID_MIN = 600; + +export function layerBucket(layerId, layerName) { + if (UNASSIGNED_LAYERS.has(layerName)) return 'unassigned'; + if (LOGIN_LAYERS.has(layerName)) return 'login'; + if (layerId >= COMMON_ID_MIN) return 'common'; + if (layerId >= ROOM_ID_MIN) return 'room'; + return 'hall'; +} + +/** + * 每张图按引用它的对象所在层决定 bucket。 + * 跨多个 bucket → common;无对象引用 → unassigned。 + */ +export function imageBuckets(source) { + const layerBucketById = new Map( + source.layers.map((l) => [l.layerId, layerBucket(l.layerId, l.name)]), + ); + + const bucketsPerImage = new Map(); + for (const obj of source.objects.values()) { + if (!obj.ImageFileID) continue; + const b = layerBucketById.get(obj.BelongLayerID); + if (!b) continue; + if (!bucketsPerImage.has(obj.ImageFileID)) bucketsPerImage.set(obj.ImageFileID, new Set()); + bucketsPerImage.get(obj.ImageFileID).add(b); + } + + const out = new Map(); + for (const id of source.images.keys()) { + const set = bucketsPerImage.get(id); + if (!set || set.size === 0) { out.set(id, 'unassigned'); continue; } + const real = [...set].filter((b) => b !== 'unassigned'); + if (real.length === 0) out.set(id, 'unassigned'); + else if (real.length === 1) out.set(id, real[0]); + else out.set(id, 'common'); + } + return out; +}