단일 소스 data/enemies.json appearance → Models/Monsters/<enemyId>.model. EntryKey model://monster-<id> 네임스페이스(기존 모델과 충돌 가드), 태생 AI-free. appearance 미확보 9종(slime 등)은 Task 2(RUID 수확) 후 추가 예정. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011xhLoQbJvQYL65kBtDNDTy
30 lines
1.5 KiB
JavaScript
30 lines
1.5 KiB
JavaScript
import { readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
import { buildMonsterModel, modelEntryId } from './lib/monster-model.mjs';
|
|
|
|
// 적 18종 각각의 전용 모델(.model) emit. 단일 소스: data/enemies.json(appearance) + ChaseMonster.model(골격).
|
|
const OUT_DIR = 'RootDesk/MyDesk/Models/Monsters';
|
|
const enemies = JSON.parse(readFileSync('data/enemies.json', 'utf8')).enemies;
|
|
const skeleton = JSON.parse(readFileSync('Global/ChaseMonster.model', 'utf8'));
|
|
|
|
// EntryKey 충돌 가드 (LEA-3015 예방): 기존 .model들의 EntryKey 수집 (경로별)
|
|
const existing = []; // { key, path }
|
|
for (const dir of ['Global', OUT_DIR]) {
|
|
for (const f of readdirSync(dir).filter((n) => n.endsWith('.model'))) {
|
|
const path = `${dir}/${f}`;
|
|
existing.push({ key: JSON.parse(readFileSync(path, 'utf8')).EntryKey, path });
|
|
}
|
|
}
|
|
|
|
const written = [];
|
|
const skipped = [];
|
|
for (const [enemyId, enemy] of Object.entries(enemies)) {
|
|
if (!enemy.appearance) { skipped.push(enemyId); continue; }
|
|
const file = buildMonsterModel(enemyId, enemy, skeleton);
|
|
const outPath = `${OUT_DIR}/${enemyId}.model`;
|
|
const clash = existing.find((e) => e.key === file.EntryKey && e.path !== outPath);
|
|
if (clash) throw new Error(`[gen-monster-models] EntryKey 충돌: ${file.EntryKey} (기존 ${clash.path})`);
|
|
writeFileSync(outPath, JSON.stringify(file, null, 2) + '\n', 'utf8');
|
|
written.push(enemyId);
|
|
}
|
|
console.log(`[gen-monster-models] ${written.length}종 emit${skipped.length ? ` / appearance 없음 스킵: ${skipped.join(', ')}` : ''}`);
|