Files
2026-03-31 17:00:15 +01:00

84 lines
3.4 KiB
JavaScript

// 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); });