This commit is contained in:
116
scripts/validate-content.ts
Normal file
116
scripts/validate-content.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { getCharacterHomeSceneId, getCharacterNpcSceneIds, ROLE_TEMPLATE_CHARACTERS } from '../src/data/characterPresets.ts';
|
||||
import { MONSTER_PRESETS_BY_WORLD } from '../src/data/hostileNpcPresets.ts';
|
||||
import { getSceneHostileNpcPresetIds, getScenePresetsByWorld } from '../src/data/scenePresets.ts';
|
||||
import { buildStateFunctionDefinitions } from '../src/data/stateFunctions.ts';
|
||||
import { WorldType } from '../src/types.ts';
|
||||
|
||||
function addError(errors: string[], message: string) {
|
||||
errors.push(message);
|
||||
}
|
||||
|
||||
function validateScenes(errors: string[]) {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const scenes = getScenePresetsByWorld(worldType);
|
||||
const sceneIdSet = new Set(scenes.map(scene => scene.id));
|
||||
const monsterIdSet = new Set(MONSTER_PRESETS_BY_WORLD[worldType].map(monster => monster.id));
|
||||
const duplicateSceneIds = scenes
|
||||
.map(scene => scene.id)
|
||||
.filter((id, index, all) => all.indexOf(id) !== index);
|
||||
|
||||
duplicateSceneIds.forEach(sceneId => {
|
||||
addError(errors, `[scene] duplicate id "${sceneId}" in ${worldType}`);
|
||||
});
|
||||
|
||||
scenes.forEach(scene => {
|
||||
if (scene.forwardSceneId && !sceneIdSet.has(scene.forwardSceneId)) {
|
||||
addError(errors, `[scene] ${scene.id} forwardSceneId "${scene.forwardSceneId}" not found in ${worldType}`);
|
||||
}
|
||||
|
||||
scene.connectedSceneIds.forEach(connectedSceneId => {
|
||||
if (!sceneIdSet.has(connectedSceneId)) {
|
||||
addError(errors, `[scene] ${scene.id} connectedSceneId "${connectedSceneId}" not found in ${worldType}`);
|
||||
}
|
||||
});
|
||||
|
||||
getSceneHostileNpcPresetIds(scene).forEach(monsterId => {
|
||||
if (!monsterIdSet.has(monsterId)) {
|
||||
addError(errors, `[scene] ${scene.id} references unknown monster "${monsterId}" in ${worldType}`);
|
||||
}
|
||||
});
|
||||
|
||||
const npcIds = new Set<string>();
|
||||
scene.npcs.forEach(npc => {
|
||||
if (npcIds.has(npc.id)) {
|
||||
addError(errors, `[scene] ${scene.id} has duplicate npc id "${npc.id}"`);
|
||||
}
|
||||
npcIds.add(npc.id);
|
||||
|
||||
if (npc.characterId && !ROLE_TEMPLATE_CHARACTERS.some(character => character.id === npc.characterId)) {
|
||||
addError(errors, `[scene] ${scene.id} npc "${npc.id}" references unknown character "${npc.characterId}"`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateCharacters(errors: string[]) {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneIdSet = new Set(getScenePresetsByWorld(worldType).map(scene => scene.id));
|
||||
|
||||
ROLE_TEMPLATE_CHARACTERS.forEach(character => {
|
||||
const homeSceneId = getCharacterHomeSceneId(worldType, character.id);
|
||||
if (homeSceneId && !sceneIdSet.has(homeSceneId)) {
|
||||
addError(errors, `[character] ${character.id} homeSceneId "${homeSceneId}" not found in ${worldType}`);
|
||||
}
|
||||
|
||||
getCharacterNpcSceneIds(worldType, character.id).forEach(sceneId => {
|
||||
if (!sceneIdSet.has(sceneId)) {
|
||||
addError(errors, `[character] ${character.id} npc scene "${sceneId}" not found in ${worldType}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateStateFunctions(errors: string[]) {
|
||||
const definitions = buildStateFunctionDefinitions();
|
||||
const duplicateIds = definitions
|
||||
.map(definition => definition.id)
|
||||
.filter((id, index, all) => all.indexOf(id) !== index);
|
||||
|
||||
duplicateIds.forEach(id => {
|
||||
addError(errors, `[function] duplicate function id "${id}"`);
|
||||
});
|
||||
|
||||
definitions.forEach(definition => {
|
||||
if (!definition.text.trim()) {
|
||||
addError(errors, `[function] ${definition.id} has empty text`);
|
||||
}
|
||||
if (!definition.description.trim()) {
|
||||
addError(errors, `[function] ${definition.id} has empty description`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const errors: string[] = [];
|
||||
|
||||
validateScenes(errors);
|
||||
validateCharacters(errors);
|
||||
validateStateFunctions(errors);
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(`Content validation failed with ${errors.length} issue(s):`);
|
||||
errors.forEach(error => console.error(`- ${error}`));
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const sceneCount = getScenePresetsByWorld(WorldType.WUXIA).length + getScenePresetsByWorld(WorldType.XIANXIA).length;
|
||||
const monsterCount = MONSTER_PRESETS_BY_WORLD[WorldType.WUXIA].length + MONSTER_PRESETS_BY_WORLD[WorldType.XIANXIA].length;
|
||||
const functionCount = buildStateFunctionDefinitions().length;
|
||||
|
||||
console.log(`Content validation passed. scenes=${sceneCount} monsters=${monsterCount} characters=${ROLE_TEMPLATE_CHARACTERS.length} functions=${functionCount}`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user