import { describe, expect, it } from 'vitest' import { existsSync, readFileSync } from 'node:fs' import { join } from 'node:path' import { MONSTER_ART_MAP } from '../src/game/monster-mapping.ts' describe('offline pre-baked entity atlases (Issue #30)', () => { const packRoot = join(process.cwd(), 'samples', 'd2-packs') const hasPacks = existsSync(join(packRoot, 'act1', 'entities', 'char-so.json')) it.skipIf(!hasPacks)('bakes Sorceress char-so PNG + JSON for Acts 1..5', () => { for (let act = 1; act <= 5; act += 1) { const dir = join(packRoot, `act${act}`, 'entities') const jsonPath = join(dir, 'char-so.json') const pngPath = join(dir, 'char-so.png') expect(existsSync(jsonPath)).toBe(true) expect(existsSync(pngPath)).toBe(true) const meta = JSON.parse(readFileSync(jsonPath, 'utf8')) expect(meta.file).toBe('char-so.png') expect(meta.directions).toBe(16) expect(meta.walk).toBe(0) expect(meta.stand).toBe(16) expect(meta.members).toBeGreaterThanOrEqual(16) expect(meta.groups.length).toBe(32) // 16 walk + 16 stand const png = readFileSync(pngPath) // Check PNG magic header expect(png[0]).toBe(137) expect(png[1]).toBe(80) // P expect(png[2]).toBe(78) // N expect(png[3]).toBe(71) // G // Read IHDR width and height const view = new DataView(png.buffer, png.byteOffset, png.byteLength) expect(view.getUint32(16)).toBe(meta.width) expect(view.getUint32(20)).toBe(meta.height) } }) it.skipIf(!hasPacks)('bakes monster PNG + JSON atlases for all mapped monster specs across Acts 1..5', () => { const uniqueSpecs = new Map() for (const spec of Object.values(MONSTER_ART_MAP)) { const key = `${spec.token}-${spec.weapon}` if (!uniqueSpecs.has(key)) uniqueSpecs.set(key, spec) } for (let act = 1; act <= 5; act += 1) { const dir = join(packRoot, `act${act}`, 'entities') for (const spec of uniqueSpecs.values()) { const slug = `monster-${spec.token.toLowerCase()}-${spec.weapon.toLowerCase()}` const jsonPath = join(dir, `${slug}.json`) const pngPath = join(dir, `${slug}.png`) expect(existsSync(jsonPath), `missing ${jsonPath}`).toBe(true) expect(existsSync(pngPath), `missing ${pngPath}`).toBe(true) const meta = JSON.parse(readFileSync(jsonPath, 'utf8')) expect(meta.token).toBe(spec.token) expect(meta.weapon).toBe(spec.weapon) expect(meta.directions).toBe(8) expect(meta.walkOffset).toBe(0) expect(meta.standOffset).toBe(8) expect(meta.groups.length).toBe(16) // 8 walk + 8 stand const png = readFileSync(pngPath) const view = new DataView(png.buffer, png.byteOffset, png.byteLength) expect(view.getUint32(16)).toBe(meta.width) expect(view.getUint32(20)).toBe(meta.height) } } }) })