Add Cao break chapter and Sun Qian sortie

This commit is contained in:
2026-06-23 04:08:15 +09:00
parent 3cdbbe8d5a
commit 09c1390ba5
11 changed files with 900 additions and 23 deletions

View File

@@ -1339,9 +1339,93 @@ try {
!thirteenthCampState.availableDialogueIds.every((id) => id.endsWith('lu-bu-fall')) ||
!thirteenthCampState.campaign?.roster?.some((unit) => unit.id === 'guan-yu') ||
!thirteenthCampState.campaign?.roster?.some((unit) => unit.id === 'jian-yong') ||
!thirteenthCampState.campaign?.roster?.some((unit) => unit.id === 'mi-zhu')
!thirteenthCampState.campaign?.roster?.some((unit) => unit.id === 'mi-zhu') ||
!thirteenthCampState.campaign?.roster?.some((unit) => unit.id === 'sun-qian')
) {
throw new Error(`Expected thirteenth camp to expose Lu Bu fall dialogue set and preserve full roster: ${JSON.stringify(thirteenthCampState)}`);
throw new Error(`Expected thirteenth camp to expose Lu Bu fall dialogue set, recruit Sun Qian, and preserve full roster: ${JSON.stringify(thirteenthCampState)}`);
}
await page.mouse.click(1120, 38);
await page.waitForTimeout(160);
const thirteenthCampSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
!thirteenthCampSortieState?.sortieVisible ||
!thirteenthCampSortieState.sortiePlan?.objectiveLine?.includes('서주 재기') ||
!thirteenthCampSortieState.sortieRoster?.some((unit) => unit.id === 'sun-qian' && unit.recruited && unit.recommended)
) {
throw new Error(`Expected thirteenth camp sortie prep to target Cao break battle with Sun Qian as a recruit option: ${JSON.stringify(thirteenthCampSortieState)}`);
}
assertSortieTacticalRoster(thirteenthCampSortieState, ['liu-bei', 'guan-yu', 'zhang-fei', 'jian-yong', 'mi-zhu', 'sun-qian']);
await clickSortieRosterUnit(page, 'sun-qian');
const thirteenthCampSunQianSelectedState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (!thirteenthCampSunQianSelectedState.sortieRoster?.some((unit) => unit.id === 'sun-qian' && unit.selected)) {
throw new Error(`Expected Sun Qian to be selectable for the fourteenth battle sortie: ${JSON.stringify(thirteenthCampSunQianSelectedState)}`);
}
await page.screenshot({ path: 'dist/verification-cao-break-sortie.png', fullPage: true });
await page.mouse.click(1068, 646);
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
return activeScenes.includes('StoryScene');
});
await page.screenshot({ path: 'dist/verification-cao-break-story.png', fullPage: true });
for (let i = 0; i < 18; i += 1) {
const enteredFourteenthBattle = await page.evaluate(() => {
const state = window.__HEROS_DEBUG__?.battle();
return state?.scene === 'BattleScene' && state?.battleId === 'fourteenth-battle-cao-break';
});
if (enteredFourteenthBattle) {
break;
}
await page.keyboard.press('Space');
await page.waitForTimeout(320);
}
await page.waitForFunction(() => {
const state = window.__HEROS_DEBUG__?.battle();
return state?.scene === 'BattleScene' && state?.battleId === 'fourteenth-battle-cao-break' && state?.battleOutcome === null && state?.phase === 'idle';
});
await page.screenshot({ path: 'dist/verification-fourteenth-battle.png', fullPage: true });
const fourteenthBattleState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
const fourteenthEnemies = fourteenthBattleState.units.filter((unit) => unit.faction === 'enemy');
const fourteenthAllies = fourteenthBattleState.units.filter((unit) => unit.faction === 'ally');
const fourteenthEnemyBehaviors = new Set(fourteenthEnemies.map((unit) => unit.ai));
if (
fourteenthBattleState.camera?.mapWidth !== 32 ||
fourteenthBattleState.camera?.mapHeight !== 24 ||
fourteenthBattleState.victoryConditionLabel !== '차주 격파' ||
fourteenthEnemies.length < 15 ||
!fourteenthEnemyBehaviors.has('aggressive') ||
!fourteenthEnemyBehaviors.has('guard') ||
!fourteenthEnemyBehaviors.has('hold') ||
!fourteenthEnemies.some((unit) => unit.id === 'cao-break-leader-che-zhou') ||
!fourteenthAllies.some((unit) => unit.id === 'sun-qian')
) {
throw new Error(`Expected fourteenth battle to use Cao break map, Che Zhou objective, mixed AI, and selected Sun Qian sortie: ${JSON.stringify(fourteenthBattleState)}`);
}
await page.evaluate(() => window.__HEROS_DEBUG__?.forceBattleOutcome('victory'));
await page.waitForFunction(() => {
const state = window.__HEROS_DEBUG__?.battle();
return state?.battleOutcome === 'victory' && state?.phase === 'resolved' && state?.resultVisible === true;
});
await page.mouse.click(738, 642);
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
return activeScenes.includes('CampScene');
});
const fourteenthCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
fourteenthCampState?.campBattleId !== 'fourteenth-battle-cao-break' ||
fourteenthCampState.campTitle !== '서주 재기 후 군영' ||
fourteenthCampState.availableDialogueIds?.length !== 3 ||
!fourteenthCampState.availableDialogueIds.every((id) => id.endsWith('cao-break')) ||
!fourteenthCampState.campaign?.roster?.some((unit) => unit.id === 'sun-qian')
) {
throw new Error(`Expected fourteenth camp to expose Cao break dialogue set and preserve Sun Qian in roster: ${JSON.stringify(fourteenthCampState)}`);
}
await page.evaluate(() => window.__HEROS_GAME__?.scene.start('TitleScene'));
@@ -1355,7 +1439,7 @@ try {
return activeScenes.includes('CampScene');
});
console.log(`Verified title-to-thirteenth-battle flow, recruit sortie selection, result states, and debug API at ${targetUrl}`);
console.log(`Verified title-to-fourteenth-battle flow, recruit sortie selection, result states, and debug API at ${targetUrl}`);
} finally {
await browser?.close();
if (serverProcess && !serverProcess.killed) {