Add Xuzhou breakout chapter

This commit is contained in:
2026-06-23 02:09:38 +09:00
parent 91b8abc7e3
commit 6eacee48fc
11 changed files with 739 additions and 16 deletions

View File

@@ -1017,6 +1017,91 @@ try {
throw new Error(`Expected ninth camp to expose Xuzhou gate dialogue set and preserve recruits: ${JSON.stringify(ninthCampState)}`);
}
await page.mouse.click(1120, 38);
await page.waitForTimeout(160);
const ninthCampSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (!ninthCampSortieState?.sortieVisible) {
throw new Error(`Expected ninth camp sortie prep overlay: ${JSON.stringify(ninthCampSortieState)}`);
}
if (!ninthCampSortieState.selectedSortieUnitIds?.includes('guan-yu')) {
await page.mouse.click(256, 420);
await page.waitForTimeout(120);
}
const ninthCampGuanYuSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (!ninthCampGuanYuSortieState.selectedSortieUnitIds?.includes('guan-yu')) {
throw new Error(`Expected Guan Yu to be selectable again for Xuzhou breakout: ${JSON.stringify(ninthCampGuanYuSortieState)}`);
}
assertSortieTacticalRoster(ninthCampGuanYuSortieState, ['liu-bei', 'guan-yu', 'zhang-fei', 'jian-yong', 'mi-zhu']);
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-xuzhou-loss-story.png', fullPage: true });
for (let i = 0; i < 14; i += 1) {
const enteredTenthBattle = await page.evaluate(() => {
const state = window.__HEROS_DEBUG__?.battle();
return state?.scene === 'BattleScene' && state?.battleId === 'tenth-battle-xuzhou-breakout';
});
if (enteredTenthBattle) {
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 === 'tenth-battle-xuzhou-breakout' && state?.battleOutcome === null && state?.phase === 'idle';
});
await page.screenshot({ path: 'dist/verification-tenth-battle.png', fullPage: true });
const tenthBattleState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
const tenthEnemies = tenthBattleState.units.filter((unit) => unit.faction === 'enemy');
const tenthAllies = tenthBattleState.units.filter((unit) => unit.faction === 'ally');
const tenthEnemyBehaviors = new Set(tenthEnemies.map((unit) => unit.ai));
if (
tenthBattleState.camera?.mapWidth !== 28 ||
tenthBattleState.camera?.mapHeight !== 22 ||
tenthBattleState.victoryConditionLabel !== '송헌 격파' ||
tenthEnemies.length < 11 ||
!tenthEnemyBehaviors.has('aggressive') ||
!tenthEnemyBehaviors.has('guard') ||
!tenthEnemyBehaviors.has('hold') ||
!tenthAllies.some((unit) => unit.id === 'liu-bei') ||
!tenthAllies.some((unit) => unit.id === 'guan-yu') ||
!tenthAllies.some((unit) => unit.id === 'zhang-fei') ||
!tenthAllies.some((unit) => unit.id === 'jian-yong') ||
!tenthAllies.some((unit) => unit.id === 'mi-zhu')
) {
throw new Error(`Expected tenth battle to use breakout map, mixed AI, and selected full Xu Province sortie: ${JSON.stringify(tenthBattleState)}`);
}
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 tenthCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
tenthCampState?.campBattleId !== 'tenth-battle-xuzhou-breakout' ||
tenthCampState.campTitle !== '서주 상실 후 군영' ||
tenthCampState.availableDialogueIds?.length !== 3 ||
!tenthCampState.availableDialogueIds.every((id) => id.endsWith('xuzhou-loss')) ||
!tenthCampState.campaign?.roster?.some((unit) => unit.id === 'guan-yu') ||
!tenthCampState.campaign?.roster?.some((unit) => unit.id === 'jian-yong') ||
!tenthCampState.campaign?.roster?.some((unit) => unit.id === 'mi-zhu')
) {
throw new Error(`Expected tenth camp to expose Xuzhou loss dialogue set and preserve full roster: ${JSON.stringify(tenthCampState)}`);
}
await page.evaluate(() => window.__HEROS_GAME__?.scene.start('TitleScene'));
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
@@ -1028,7 +1113,7 @@ try {
return activeScenes.includes('CampScene');
});
console.log(`Verified title-to-ninth-battle flow, recruit sortie selection, result states, and debug API at ${targetUrl}`);
console.log(`Verified title-to-tenth-battle flow, recruit sortie selection, result states, and debug API at ${targetUrl}`);
} finally {
await browser?.close();
if (serverProcess && !serverProcess.killed) {