Validate camp campaign step references

This commit is contained in:
2026-07-05 15:52:34 +09:00
parent 8b6b8cdf54
commit 0c47bbd08e

View File

@@ -13,6 +13,7 @@ const server = await createServer({
try {
const { battleScenarios, defaultBattleScenario } = await server.ssrLoadModule('/src/game/data/battles.ts');
const { isCampaignStep } = await server.ssrLoadModule('/src/game/state/campaignState.ts');
const itemRewardArrays = collectItemRewardArrays(source);
itemRewardArrays.forEach(({ body, index, offset }) => validateItemRewards(body, index, offset));
const campBattleIdEntries = collectCampBattleIdEntries(source, defaultBattleScenario.id);
@@ -20,8 +21,9 @@ try {
validateCampBattleIdEntries(campBattleIdEntries, battleScenarios);
const dialogueEvents = collectCampEvents(source, 'campDialogues');
const visitEvents = collectCampEvents(source, 'campVisits');
validateCampEventCollection(dialogueEvents, 'campDialogues', campBattleIdKeys);
validateCampEventCollection(visitEvents, 'campVisits', campBattleIdKeys);
const campaignStepReferences =
validateCampEventCollection(dialogueEvents, 'campDialogues', campBattleIdKeys, isCampaignStep) +
validateCampEventCollection(visitEvents, 'campVisits', campBattleIdKeys, isCampaignStep);
validateNumericProperties('gold', { min: 1, max: 20000 });
validateNumericProperties('bondExp', { min: 0, max: 200 });
validateNumericProperties('rewardExp', { min: 1, max: 200 });
@@ -31,7 +33,7 @@ try {
}
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.`
`Verified ${dialogueEvents.length} camp dialogues, ${visitEvents.length} camp visits, ${campBattleIdEntries.length} camp battle ids, ${campaignStepReferences} campaign-step references, ${itemRewardArrays.length} camp item reward lists, and camp reward numeric fields.`
);
} finally {
await server.close();
@@ -133,8 +135,9 @@ function collectCampEvents(text, arrayName) {
}));
}
function validateCampEventCollection(events, collectionName, campBattleIdKeys) {
function validateCampEventCollection(events, collectionName, campBattleIdKeys, isCampaignStep) {
const seenIds = new Map();
let campaignStepReferenceCount = 0;
if (events.length === 0) {
errors.push(`${sourcePath}: ${collectionName} has no entries`);
}
@@ -176,7 +179,29 @@ function validateCampEventCollection(events, collectionName, campBattleIdKeys) {
errors.push(`${sourcePath}:${lineNumber(event.offset)} ${context} references unknown campBattleIds.${reference[1]}`);
}
}
campaignStepReferenceCount += validateAvailableDuringSteps(event, context, isCampaignStep);
});
return campaignStepReferenceCount;
}
function validateAvailableDuringSteps(event, context, isCampaignStep) {
const availableDuringMatch = /availableDuringSteps:\s*\[([\s\S]*?)\]/.exec(event.body);
if (!availableDuringMatch) {
return 0;
}
const body = availableDuringMatch[1];
const bodyOffset = event.offset + (availableDuringMatch.index ?? 0) + availableDuringMatch[0].indexOf(body);
const steps = collectStringLiterals(body, bodyOffset);
if (steps.length === 0) {
errors.push(`${sourcePath}:${lineNumber(bodyOffset)} ${context} has an empty availableDuringSteps list`);
}
steps.forEach((step) => {
if (!isCampaignStep(step.value)) {
errors.push(`${sourcePath}:${lineNumber(step.offset)} ${context} references unknown campaign step "${step.value}"`);
}
});
return steps.length;
}
function collectStringProperties(text, propertyName) {
@@ -187,6 +212,13 @@ function collectStringProperties(text, propertyName) {
}));
}
function collectStringLiterals(text, offset = 0) {
return [...text.matchAll(/'((?:\\'|[^'])*)'/g)].map((match) => ({
value: match[1].replace(/\\'/g, "'"),
offset: offset + (match.index ?? 0)
}));
}
function parseRewardLabel(reward) {
const normalized = String(reward).replace(/\s+/g, ' ').trim();
const match = normalized.match(/^(.*?)(?:\s*[+xX]?\s*(\d+))$/);