diff --git a/scripts/verify-campaign-flow-data.mjs b/scripts/verify-campaign-flow-data.mjs index 369d833..e1395a2 100644 --- a/scripts/verify-campaign-flow-data.mjs +++ b/scripts/verify-campaign-flow-data.mjs @@ -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];