Validate camp dialogue bond references
This commit is contained in:
@@ -19,11 +19,13 @@ try {
|
||||
const campBattleIdEntries = collectCampBattleIdEntries(source, defaultBattleScenario.id);
|
||||
const campBattleIdKeys = new Set(campBattleIdEntries.map((entry) => entry.key));
|
||||
validateCampBattleIdEntries(campBattleIdEntries, battleScenarios);
|
||||
const knownBondsById = collectKnownBondsById(battleScenarios);
|
||||
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);
|
||||
validateNumericProperties('gold', { min: 1, max: 20000 });
|
||||
validateNumericProperties('bondExp', { min: 0, max: 200 });
|
||||
validateNumericProperties('rewardExp', { min: 1, max: 200 });
|
||||
@@ -33,7 +35,7 @@ try {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`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.`
|
||||
`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.`
|
||||
);
|
||||
} finally {
|
||||
await server.close();
|
||||
@@ -127,6 +129,18 @@ function validateCampBattleIdEntries(entries, battleScenarios) {
|
||||
});
|
||||
}
|
||||
|
||||
function collectKnownBondsById(battleScenarios) {
|
||||
const bondsById = new Map();
|
||||
Object.values(battleScenarios).forEach((scenario) => {
|
||||
(scenario.bonds ?? []).forEach((bond) => {
|
||||
if (!bondsById.has(bond.id)) {
|
||||
bondsById.set(bond.id, bond);
|
||||
}
|
||||
});
|
||||
});
|
||||
return bondsById;
|
||||
}
|
||||
|
||||
function collectCampEvents(text, arrayName) {
|
||||
const { body, offset } = extractConstArray(text, arrayName);
|
||||
return collectTopLevelObjects(body).map((event) => ({
|
||||
@@ -204,6 +218,42 @@ function validateAvailableDuringSteps(event, context, isCampaignStep) {
|
||||
return steps.length;
|
||||
}
|
||||
|
||||
function validateCampDialogueBondReferences(events, knownBondsById) {
|
||||
let bondReferenceCount = 0;
|
||||
events.forEach((event, index) => {
|
||||
const context = `campDialogues[${index}]`;
|
||||
const bondIdEntries = collectStringProperties(event.body, 'bondId');
|
||||
if (bondIdEntries.length === 0) {
|
||||
return;
|
||||
}
|
||||
bondReferenceCount += bondIdEntries.length;
|
||||
if (bondIdEntries.length > 1) {
|
||||
errors.push(`${sourcePath}:${lineNumber(event.offset)} ${context} has multiple bondId properties`);
|
||||
}
|
||||
|
||||
const unitIds = collectStringArrayProperty(event.body, 'unitIds', event.offset);
|
||||
if (unitIds.length === 0) {
|
||||
errors.push(`${sourcePath}:${lineNumber(event.offset)} ${context} has bondId but no unitIds`);
|
||||
}
|
||||
|
||||
bondIdEntries.forEach((entry) => {
|
||||
const bond = knownBondsById.get(entry.value);
|
||||
if (!bond) {
|
||||
errors.push(`${sourcePath}:${lineNumber(event.offset + entry.offset)} ${context} references unknown bondId "${entry.value}"`);
|
||||
return;
|
||||
}
|
||||
if (!sameStringSet(unitIds.map((unit) => unit.value), bond.unitIds ?? [])) {
|
||||
errors.push(
|
||||
`${sourcePath}:${lineNumber(event.offset + entry.offset)} ${context} bondId "${entry.value}" units [${(
|
||||
bond.unitIds ?? []
|
||||
).join(', ')}] do not match dialogue unitIds [${unitIds.map((unit) => unit.value).join(', ')}]`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
return bondReferenceCount;
|
||||
}
|
||||
|
||||
function collectStringProperties(text, propertyName) {
|
||||
const pattern = new RegExp(`\\b${propertyName}:\\s*'((?:\\\\'|[^'])*)'`, 'g');
|
||||
return [...text.matchAll(pattern)].map((match) => ({
|
||||
@@ -219,6 +269,25 @@ function collectStringLiterals(text, offset = 0) {
|
||||
}));
|
||||
}
|
||||
|
||||
function collectStringArrayProperty(text, propertyName, eventOffset = 0) {
|
||||
const pattern = new RegExp(`\\b${propertyName}:\\s*\\[([\\s\\S]*?)\\]`);
|
||||
const match = pattern.exec(text);
|
||||
if (!match) {
|
||||
return [];
|
||||
}
|
||||
const arrayBody = match[1];
|
||||
const arrayOffset = eventOffset + (match.index ?? 0) + match[0].indexOf(arrayBody);
|
||||
return collectStringLiterals(arrayBody, arrayOffset);
|
||||
}
|
||||
|
||||
function sameStringSet(left, right) {
|
||||
if (left.length !== right.length) {
|
||||
return false;
|
||||
}
|
||||
const rightSet = new Set(right);
|
||||
return left.every((value) => rightSet.has(value));
|
||||
}
|
||||
|
||||
function parseRewardLabel(reward) {
|
||||
const normalized = String(reward).replace(/\s+/g, ' ').trim();
|
||||
const match = normalized.match(/^(.*?)(?:\s*[+xX]?\s*(\d+))$/);
|
||||
|
||||
Reference in New Issue
Block a user