Validate camp battle id references
This commit is contained in:
@@ -1,28 +1,42 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createServer } from 'vite';
|
||||
|
||||
const sourcePath = 'src/game/scenes/CampScene.ts';
|
||||
const source = readFileSync(sourcePath, 'utf8');
|
||||
const errors = [];
|
||||
|
||||
const itemRewardArrays = collectItemRewardArrays(source);
|
||||
itemRewardArrays.forEach(({ body, index, offset }) => validateItemRewards(body, index, offset));
|
||||
const campBattleIdKeys = collectCampBattleIdKeys(source);
|
||||
const dialogueEvents = collectCampEvents(source, 'campDialogues');
|
||||
const visitEvents = collectCampEvents(source, 'campVisits');
|
||||
validateCampEventCollection(dialogueEvents, 'campDialogues');
|
||||
validateCampEventCollection(visitEvents, 'campVisits');
|
||||
validateNumericProperties('gold', { min: 1, max: 20000 });
|
||||
validateNumericProperties('bondExp', { min: 0, max: 200 });
|
||||
validateNumericProperties('rewardExp', { min: 1, max: 200 });
|
||||
const server = await createServer({
|
||||
logLevel: 'error',
|
||||
server: { middlewareMode: true },
|
||||
appType: 'custom'
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Camp reward data verification failed:\n${errors.map((error) => `- ${error}`).join('\n')}`);
|
||||
try {
|
||||
const { battleScenarios, defaultBattleScenario } = await server.ssrLoadModule('/src/game/data/battles.ts');
|
||||
const itemRewardArrays = collectItemRewardArrays(source);
|
||||
itemRewardArrays.forEach(({ body, index, offset }) => validateItemRewards(body, index, offset));
|
||||
const campBattleIdEntries = collectCampBattleIdEntries(source, defaultBattleScenario.id);
|
||||
const campBattleIdKeys = new Set(campBattleIdEntries.map((entry) => entry.key));
|
||||
validateCampBattleIdEntries(campBattleIdEntries, battleScenarios);
|
||||
const dialogueEvents = collectCampEvents(source, 'campDialogues');
|
||||
const visitEvents = collectCampEvents(source, 'campVisits');
|
||||
validateCampEventCollection(dialogueEvents, 'campDialogues', campBattleIdKeys);
|
||||
validateCampEventCollection(visitEvents, 'campVisits', campBattleIdKeys);
|
||||
validateNumericProperties('gold', { min: 1, max: 20000 });
|
||||
validateNumericProperties('bondExp', { min: 0, max: 200 });
|
||||
validateNumericProperties('rewardExp', { min: 1, max: 200 });
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Camp reward data verification failed:\n${errors.map((error) => `- ${error}`).join('\n')}`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Verified ${dialogueEvents.length} camp dialogues, ${visitEvents.length} camp visits, ${campBattleIdEntries.length} camp battle ids, ${itemRewardArrays.length} camp item reward lists, and camp reward numeric fields.`
|
||||
);
|
||||
} finally {
|
||||
await server.close();
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Verified ${dialogueEvents.length} camp dialogues, ${visitEvents.length} camp visits, ${itemRewardArrays.length} camp item reward lists, and camp reward numeric fields.`
|
||||
);
|
||||
|
||||
function collectItemRewardArrays(text) {
|
||||
return [...text.matchAll(/itemRewards:\s*\[([\s\S]*?)\]/g)].map((match, index) => ({
|
||||
body: match[1],
|
||||
@@ -63,9 +77,52 @@ function validateNumericProperties(propertyName, { min, max }) {
|
||||
}
|
||||
}
|
||||
|
||||
function collectCampBattleIdKeys(text) {
|
||||
function collectCampBattleIdEntries(text, defaultBattleScenarioId) {
|
||||
const objectSource = extractConstObject(text, 'campBattleIds');
|
||||
return new Set([...objectSource.matchAll(/^\s*(\w+):/gm)].map((match) => match[1]));
|
||||
const objectOffset = text.indexOf(objectSource);
|
||||
return [...objectSource.matchAll(/^\s*(\w+):\s*([^,\n]+)/gm)].map((match) => {
|
||||
const expression = match[2].trim();
|
||||
return {
|
||||
key: match[1],
|
||||
expression,
|
||||
value: parseCampBattleIdExpression(expression, defaultBattleScenarioId),
|
||||
offset: objectOffset + (match.index ?? 0)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function parseCampBattleIdExpression(expression, defaultBattleScenarioId) {
|
||||
const stringMatch = /^'([^']+)'$/.exec(expression);
|
||||
if (stringMatch) {
|
||||
return stringMatch[1];
|
||||
}
|
||||
if (expression === 'defaultBattleScenario.id') {
|
||||
return defaultBattleScenarioId;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function validateCampBattleIdEntries(entries, battleScenarios) {
|
||||
const knownBattleIds = new Set(Object.keys(battleScenarios));
|
||||
const seenBattleIds = new Map();
|
||||
entries.forEach((entry) => {
|
||||
const context = `campBattleIds.${entry.key}`;
|
||||
if (!entry.value) {
|
||||
errors.push(`${sourcePath}:${lineNumber(entry.offset)} ${context} uses unsupported expression "${entry.expression}"`);
|
||||
return;
|
||||
}
|
||||
if (!knownBattleIds.has(entry.value)) {
|
||||
errors.push(`${sourcePath}:${lineNumber(entry.offset)} ${context} references unknown battle id "${entry.value}"`);
|
||||
}
|
||||
if (seenBattleIds.has(entry.value)) {
|
||||
errors.push(
|
||||
`${sourcePath}:${lineNumber(entry.offset)} ${context} duplicates battle id "${entry.value}" first used by campBattleIds.${seenBattleIds.get(
|
||||
entry.value
|
||||
)}`
|
||||
);
|
||||
}
|
||||
seenBattleIds.set(entry.value, entry.key);
|
||||
});
|
||||
}
|
||||
|
||||
function collectCampEvents(text, arrayName) {
|
||||
@@ -76,7 +133,7 @@ function collectCampEvents(text, arrayName) {
|
||||
}));
|
||||
}
|
||||
|
||||
function validateCampEventCollection(events, collectionName) {
|
||||
function validateCampEventCollection(events, collectionName, campBattleIdKeys) {
|
||||
const seenIds = new Map();
|
||||
if (events.length === 0) {
|
||||
errors.push(`${sourcePath}: ${collectionName} has no entries`);
|
||||
|
||||
Reference in New Issue
Block a user