Validate special sortie flow states

This commit is contained in:
2026-07-05 16:24:07 +09:00
parent e8721aa4da
commit 5aa80480f3

View File

@@ -11,6 +11,7 @@ const campaignSteps = extractCampaignSteps(campaignStateSource);
const routedBattleSteps = extractObjectStringEntries(campaignRoutingSource, 'battleIdByCampaignStep');
const battleStepRules = extractCampaignBattleSteps(campaignStateSource);
const sortieFlowKeys = extractSortieFlowKeys(campaignFlowSource, battleScenarioIds);
const sortieFlowBodies = extractSortieFlowBodies(campaignFlowSource, battleScenarioIds);
const sortieFlowCampaignSteps = extractSortieFlowCampaignSteps(campaignFlowSource);
const sortieFlowNextBattleIds = extractSortieFlowNextBattleIds(campaignFlowSource, battleScenarioIds);
const campaignRewardUnlockBattleIds = extractCampaignRewardUnlockBattleIds(battlesSource, battleScenarioIds);
@@ -57,6 +58,25 @@ for (const flowKey of sortieFlowKeys) {
}
}
for (const flowKey of sortieFlowKeys) {
const nextBattleId = sortieFlowNextBattleIds.get(flowKey);
const campaignStep = sortieFlowCampaignSteps.get(flowKey);
const flowBody = sortieFlowBodies.get(flowKey) ?? '';
if (nextBattleId) {
continue;
}
assert(campaignStep, `Non-battle sortie flow ${flowKey} has no campaign step`);
if (campaignStep) {
assert(
!campaignStep.endsWith('-battle'),
`Non-battle sortie flow ${flowKey} points to battle campaign step ${campaignStep} without nextBattleId`
);
}
assert(/\bpages\s*:/.test(flowBody), `Non-battle sortie flow ${flowKey} must provide story pages`);
assert(/\bunavailableNotice\s*:/.test(flowBody), `Non-battle sortie flow ${flowKey} must provide an unavailableNotice for repeat clicks`);
}
for (const [battleId, unlockBattleId] of campaignRewardUnlockBattleIds.entries()) {
const nextBattleId = sortieFlowNextBattleIds.get(battleId);
assert(battleIds.has(unlockBattleId), `Battle ${battleId} campaign reward unlocks unknown battle id: ${unlockBattleId}`);
@@ -156,6 +176,21 @@ function extractSortieFlowKeys(source, scenarioIds) {
return keys;
}
function extractSortieFlowBodies(source, scenarioIds) {
const objectSource = extractConstObject(source, 'sortieFlows');
const entries = new Map();
const flowPattern = /(?:^\s*'([^']+)'\s*:|^\s*\[(\w+BattleScenario)\.id\]\s*:)\s*\{([\s\S]*?)(?=^\s*(?:'[^']+'\s*:|\[\w+BattleScenario\.id\]\s*:)|^\s*\})/gm;
for (const match of objectSource.matchAll(flowPattern)) {
const literalKey = match[1];
const computedKey = match[2] ? scenarioIds.get(match[2]) : undefined;
const flowKey = literalKey ?? computedKey ?? match[2];
if (flowKey) {
entries.set(flowKey, match[3]);
}
}
return entries;
}
function extractSortieFlowCampaignSteps(source) {
return extractSortieFlowProperty(source, /campaignStep:\s*'([^']+)'/);
}
@@ -185,7 +220,7 @@ function extractCampaignRewardUnlockBattleIds(source, scenarioIds) {
function extractSortieFlowProperty(source, propertyPattern) {
const objectSource = extractConstObject(source, 'sortieFlows');
const entries = new Map();
const flowPattern = /(?:^\s*'([^']+)'\s*:|^\s*\[(\w+BattleScenario)\.id\]\s*:)\s*\{([\s\S]*?)(?=^\s*(?:'[^']+'\s*:|\[\w+BattleScenario\.id\]\s*:)|^\};)/gm;
const flowPattern = /(?:^\s*'([^']+)'\s*:|^\s*\[(\w+BattleScenario)\.id\]\s*:)\s*\{([\s\S]*?)(?=^\s*(?:'[^']+'\s*:|\[\w+BattleScenario\.id\]\s*:)|^\s*\})/gm;
const scenarioIds = extractBattleScenarioIds(battlesSource);
for (const match of objectSource.matchAll(flowPattern)) {
const literalKey = match[1];