// build-index.mjs — Node.js utility script // Scans portraits/ and regenerates portraits/index.json // Run with: node scripts/build-index.mjs // (This script is NOT loaded by Foundry — it's a dev tool only.) import { readdir, writeFile } from 'fs/promises'; import { join, extname, basename, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const MODULE_DIR = join(__dirname, '..'); const PORTRAITS = join(MODULE_DIR, 'portraits'); const MODULE_ID = 'npc-portrait-pack'; // ── Category classification ──────────────────────────────────────────── function classify(filename) { const name = basename(filename, '.png'); // Named NPC: npc_NNNN_description if (/^npc_\d+_/.test(name)) { const parts = name.split('_'); const tags = []; let gender = null; for (const p of parts.slice(2)) { if (p === 'male' || p === 'female') { gender = p; tags.unshift(p); } else if (p === 'elderly') { tags.push('elderly'); } else { tags.push(p); } } return { category: 'named_npc', race: 'human', gender, tags }; } // Race + gender: race_gender_NN const rg = name.match(/^(human|ebon|frother|shaktar|stormer|waister|wraith)_(male|female)_\d+$/); if (rg) { const [, race, gender] = rg; return { category: race, race, gender, tags: [gender] }; } if (/^creature_\d+$/.test(name)) return { category: 'creature', race: 'creature', gender: null, tags: ['creature'] }; if (/^mutant_\d+$/.test(name)) return { category: 'creature', race: 'mutant', gender: null, tags: ['mutant','creature'] }; if (/^serial_killer_\d+$/.test(name))return { category: 'serial_killer', race: 'human', gender: null, tags: ['serial_killer','threat'] }; if (/^portrait_\d+$/.test(name)) return { category: 'generic', race: 'human', gender: null, tags: [] }; return { category: 'generic', race: 'unknown', gender: null, tags: [] }; } // ── Main ─────────────────────────────────────────────────────────────── async function buildIndex() { const files = await readdir(PORTRAITS); const pngFiles = files.filter(f => extname(f).toLowerCase() === '.png').sort(); const portraits = pngFiles.map(filename => { const id = basename(filename, '.png'); const { category, race, gender, tags } = classify(filename); return { id, path: `modules/${MODULE_ID}/portraits/${filename}`, category, race, gender, tags, }; }); portraits.sort((a, b) => a.id.localeCompare(b.id)); const index = { version: '1.0.0', count: portraits.length, portraits }; const outputPath = join(PORTRAITS, 'index.json'); await writeFile(outputPath, JSON.stringify(index, null, 2), 'utf-8'); // Summary const cats = portraits.reduce((acc, p) => { acc[p.category] = (acc[p.category] ?? 0) + 1; return acc; }, {}); console.log(`✓ Built index: ${portraits.length} portraits → ${outputPath}`); console.log('\nBy category:'); for (const [cat, count] of Object.entries(cats).sort()) { console.log(` ${cat.padEnd(15)} ${count}`); } } buildIndex().catch(err => { console.error('Error:', err); process.exit(1); });