Validate camp visit bond references

This commit is contained in:
2026-07-05 16:06:50 +09:00
parent 955a186568
commit 4f2ad79bae
2 changed files with 37 additions and 19 deletions

View File

@@ -13,19 +13,22 @@ const server = await createServer({
try {
const { battleScenarios, defaultBattleScenario } = await server.ssrLoadModule('/src/game/data/battles.ts');
const scenarioData = await server.ssrLoadModule('/src/game/data/scenario.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);
const campBattleIdKeys = new Set(campBattleIdEntries.map((entry) => entry.key));
validateCampBattleIdEntries(campBattleIdEntries, battleScenarios);
const knownBondsById = collectKnownBondsById(battleScenarios);
const knownBondsById = collectKnownBondsById(battleScenarios, scenarioData);
const dialogueEvents = collectCampEvents(source, 'campDialogues');
const visitEvents = collectCampEvents(source, 'campVisits');
const campaignStepReferences =
validateCampEventCollection(dialogueEvents, 'campDialogues', campBattleIdKeys, isCampaignStep) +
validateCampEventCollection(visitEvents, 'campVisits', campBattleIdKeys, isCampaignStep);
const dialogueBondReferences = validateCampDialogueBondReferences(dialogueEvents, knownBondsById);
const campBondReferences =
validateCampBondReferences(dialogueEvents, 'campDialogues', knownBondsById, { matchUnitIds: true }) +
validateCampBondReferences(visitEvents, 'campVisits', knownBondsById);
validateNumericProperties('gold', { min: 1, max: 20000 });
validateNumericProperties('bondExp', { min: 0, max: 200 });
validateNumericProperties('rewardExp', { min: 1, max: 200 });
@@ -35,7 +38,7 @@ try {
}
console.log(
`Verified ${dialogueEvents.length} camp dialogues, ${visitEvents.length} camp visits, ${campBattleIdEntries.length} camp battle ids, ${campaignStepReferences} campaign-step references, ${dialogueBondReferences} camp dialogue bonds, ${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, ${campBondReferences} camp bond references, ${itemRewardArrays.length} camp item reward lists, and camp reward numeric fields.`
);
} finally {
await server.close();
@@ -129,18 +132,41 @@ function validateCampBattleIdEntries(entries, battleScenarios) {
});
}
function collectKnownBondsById(battleScenarios) {
function collectKnownBondsById(battleScenarios, scenarioData) {
const bondsById = new Map();
Object.values(battleScenarios).forEach((scenario) => {
(scenario.bonds ?? []).forEach((bond) => {
const addBonds = (bonds) => {
(bonds ?? []).forEach((bond) => {
if (!bondsById.has(bond.id)) {
bondsById.set(bond.id, bond);
}
});
};
Object.values(battleScenarios).forEach((scenario) => {
addBonds(scenario.bonds);
});
Object.values(scenarioData).forEach((value) => {
if (isBattleBondArray(value)) {
addBonds(value);
}
});
return bondsById;
}
function isBattleBondArray(value) {
return Array.isArray(value) && value.length > 0 && value.every(isBattleBondEntry);
}
function isBattleBondEntry(value) {
return (
value &&
typeof value.id === 'string' &&
Array.isArray(value.unitIds) &&
typeof value.title === 'string' &&
Number.isFinite(value.level) &&
Number.isFinite(value.exp)
);
}
function collectCampEvents(text, arrayName) {
const { body, offset } = extractConstArray(text, arrayName);
return collectTopLevelObjects(body).map((event) => ({
@@ -218,10 +244,10 @@ function validateAvailableDuringSteps(event, context, isCampaignStep) {
return steps.length;
}
function validateCampDialogueBondReferences(events, knownBondsById) {
function validateCampBondReferences(events, collectionName, knownBondsById, options = {}) {
let bondReferenceCount = 0;
events.forEach((event, index) => {
const context = `campDialogues[${index}]`;
const context = `${collectionName}[${index}]`;
const bondIdEntries = collectStringProperties(event.body, 'bondId');
if (bondIdEntries.length === 0) {
return;
@@ -231,8 +257,8 @@ function validateCampDialogueBondReferences(events, knownBondsById) {
errors.push(`${sourcePath}:${lineNumber(event.offset)} ${context} has multiple bondId properties`);
}
const unitIds = collectStringArrayProperty(event.body, 'unitIds', event.offset);
if (unitIds.length === 0) {
const unitIds = options.matchUnitIds ? collectStringArrayProperty(event.body, 'unitIds', event.offset) : [];
if (options.matchUnitIds && unitIds.length === 0) {
errors.push(`${sourcePath}:${lineNumber(event.offset)} ${context} has bondId but no unitIds`);
}
@@ -242,7 +268,7 @@ function validateCampDialogueBondReferences(events, knownBondsById) {
errors.push(`${sourcePath}:${lineNumber(event.offset + entry.offset)} ${context} references unknown bondId "${entry.value}"`);
return;
}
if (!sameStringSet(unitIds.map((unit) => unit.value), bond.unitIds ?? [])) {
if (options.matchUnitIds && !sameStringSet(unitIds.map((unit) => unit.value), bond.unitIds ?? [])) {
errors.push(
`${sourcePath}:${lineNumber(event.offset + entry.offset)} ${context} bondId "${entry.value}" units [${(
bond.unitIds ?? []