Tighten sortie scenario data gates
This commit is contained in:
@@ -11,6 +11,7 @@ try {
|
||||
const { campaignRecruitUnits } = await server.ssrLoadModule('/src/game/data/scenario.ts');
|
||||
const { unitClasses, terrainRules } = await server.ssrLoadModule('/src/game/data/battleRules.ts');
|
||||
const { itemCatalog, equipmentSlots } = await server.ssrLoadModule('/src/game/data/battleItems.ts');
|
||||
const { sortieFormationRoles } = await server.ssrLoadModule('/src/game/data/sortieDeployment.ts');
|
||||
|
||||
const scenarioEntries = Object.entries(battleScenarios);
|
||||
const scenarioIds = new Set(scenarioEntries.map(([id]) => id));
|
||||
@@ -18,6 +19,7 @@ try {
|
||||
const terrainKeys = new Set(Object.keys(terrainRules));
|
||||
const itemKeys = new Set(Object.keys(itemCatalog));
|
||||
const itemNames = new Set(Object.values(itemCatalog).map((item) => item.name));
|
||||
const formationRoles = new Set(sortieFormationRoles);
|
||||
const knownRewardUnitIds = new Set([
|
||||
...scenarioEntries.flatMap(([, scenario]) => scenario.units.map((unit) => unit.id)),
|
||||
...(campaignRecruitUnits ?? []).map((unit) => unit.id)
|
||||
@@ -36,7 +38,7 @@ try {
|
||||
validateMap(errors, scenario, terrainKeys, context);
|
||||
validateUnits(errors, scenario, classKeys, itemKeys, equipmentSlots, context);
|
||||
validateObjectives(errors, scenario, context);
|
||||
validateSortie(errors, scenario, classKeys, context);
|
||||
validateSortie(errors, scenario, classKeys, formationRoles, context);
|
||||
validateRewards(errors, scenario, scenarioIds, itemNames, knownRewardUnitIds, context);
|
||||
});
|
||||
|
||||
@@ -190,7 +192,7 @@ function validateObjectives(errors, scenario, context) {
|
||||
});
|
||||
}
|
||||
|
||||
function validateSortie(errors, scenario, classKeys, context) {
|
||||
function validateSortie(errors, scenario, classKeys, formationRoles, context) {
|
||||
const sortie = scenario.sortie;
|
||||
if (!sortie) {
|
||||
return;
|
||||
@@ -204,20 +206,55 @@ function validateSortie(errors, scenario, classKeys, context) {
|
||||
errors.push(`${context}: requiredUnits count exceeds sortieLimit`);
|
||||
}
|
||||
|
||||
[...(sortie.requiredUnits ?? []), ...(sortie.excludedUnits ?? [])].forEach((unitId) => {
|
||||
const requiredUnits = sortie.requiredUnits ?? [];
|
||||
const excludedUnits = sortie.excludedUnits ?? [];
|
||||
const requiredUnitIds = new Set();
|
||||
const excludedUnitIds = new Set();
|
||||
|
||||
requiredUnits.forEach((unitId) => {
|
||||
if (requiredUnitIds.has(unitId)) {
|
||||
errors.push(`${context}: duplicate required unit "${unitId}"`);
|
||||
}
|
||||
requiredUnitIds.add(unitId);
|
||||
});
|
||||
excludedUnits.forEach((unitId) => {
|
||||
if (excludedUnitIds.has(unitId)) {
|
||||
errors.push(`${context}: duplicate excluded unit "${unitId}"`);
|
||||
}
|
||||
excludedUnitIds.add(unitId);
|
||||
if (requiredUnitIds.has(unitId)) {
|
||||
errors.push(`${context}: unit "${unitId}" cannot be both required and excluded`);
|
||||
}
|
||||
});
|
||||
|
||||
[...requiredUnits, ...excludedUnits].forEach((unitId) => {
|
||||
if (!unitIds.has(unitId)) {
|
||||
errors.push(`${context}: sortie references missing unit "${unitId}"`);
|
||||
}
|
||||
});
|
||||
const recommendedUnitIds = new Set();
|
||||
sortie.recommendedUnits.forEach((recommendation) => {
|
||||
if (recommendedUnitIds.has(recommendation.unitId)) {
|
||||
errors.push(`${context}: duplicate recommended unit "${recommendation.unitId}"`);
|
||||
}
|
||||
recommendedUnitIds.add(recommendation.unitId);
|
||||
if (!unitIds.has(recommendation.unitId)) {
|
||||
errors.push(`${context}: recommended unit "${recommendation.unitId}" is missing from units`);
|
||||
}
|
||||
if (excludedUnitIds.has(recommendation.unitId)) {
|
||||
errors.push(`${context}: excluded unit "${recommendation.unitId}" cannot also be recommended`);
|
||||
}
|
||||
if (recommendation.role && !formationRoles.has(recommendation.role)) {
|
||||
errors.push(`${context}/${recommendation.unitId}: invalid recommendation role "${recommendation.role}"`);
|
||||
}
|
||||
assertNonEmptyString(errors, recommendation.reason, `${context}/${recommendation.unitId}: recommendation reason`);
|
||||
});
|
||||
sortie.recommendedClasses.forEach((recommendation) => {
|
||||
assertNonEmptyString(errors, recommendation.label, `${context}: recommended class label`);
|
||||
assertNonEmptyString(errors, recommendation.reason, `${context}/${recommendation.label}: recommended class reason`);
|
||||
if (!Array.isArray(recommendation.classKeys) || recommendation.classKeys.length === 0) {
|
||||
errors.push(`${context}/${recommendation.label}: recommended class must include at least one classKey`);
|
||||
}
|
||||
recommendation.classKeys.forEach((classKey) => {
|
||||
if (!classKeys.has(classKey)) {
|
||||
errors.push(`${context}: recommended class "${recommendation.label}" references unknown classKey "${classKey}"`);
|
||||
@@ -229,11 +266,15 @@ function validateSortie(errors, scenario, classKeys, context) {
|
||||
errors.push(`${context}: deploymentSlots count is lower than sortieLimit`);
|
||||
}
|
||||
const deploymentTiles = new Set();
|
||||
const deploymentUnitIds = new Set();
|
||||
sortie.deploymentSlots.forEach((slot) => {
|
||||
if (!isInBounds(slot, scenario.map)) {
|
||||
errors.push(`${context}: deployment slot ${slot.x},${slot.y} is outside map`);
|
||||
return;
|
||||
}
|
||||
if (slot.role && !formationRoles.has(slot.role)) {
|
||||
errors.push(`${context}: deployment slot ${slot.x},${slot.y} has invalid role "${slot.role}"`);
|
||||
}
|
||||
const tileKey = `${slot.x},${slot.y}`;
|
||||
if (deploymentTiles.has(tileKey)) {
|
||||
errors.push(`${context}: duplicate deployment slot ${tileKey}`);
|
||||
@@ -242,6 +283,12 @@ function validateSortie(errors, scenario, classKeys, context) {
|
||||
if (slot.unitId && !unitIds.has(slot.unitId)) {
|
||||
errors.push(`${context}: deployment slot references missing unit "${slot.unitId}"`);
|
||||
}
|
||||
if (slot.unitId) {
|
||||
if (deploymentUnitIds.has(slot.unitId)) {
|
||||
errors.push(`${context}: duplicate deployment unit slot "${slot.unitId}"`);
|
||||
}
|
||||
deploymentUnitIds.add(slot.unitId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user