Add Luofeng Slope ambush chapter

This commit is contained in:
2026-06-23 10:09:41 +09:00
parent cc6365d5ba
commit 0872c584b4
11 changed files with 1250 additions and 34 deletions

View File

@@ -1736,7 +1736,7 @@ try {
if (
progressTabState?.activeTab !== 'progress' ||
progressTabState.campaignProgress?.completedKnown !== 17 ||
progressTabState.campaignProgress?.totalKnown !== 29 ||
progressTabState.campaignProgress?.totalKnown !== 30 ||
progressTabState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
progressTabState.campaignProgress?.latestBattleTitle !== '융중 방문로' ||
progressTabState.campaignProgress?.nextBattleTitle !== '박망파 매복전'
@@ -1888,7 +1888,7 @@ try {
if (
postBowangProgressState?.activeTab !== 'progress' ||
postBowangProgressState.campaignProgress?.completedKnown !== 18 ||
postBowangProgressState.campaignProgress?.totalKnown !== 29 ||
postBowangProgressState.campaignProgress?.totalKnown !== 30 ||
postBowangProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
postBowangProgressState.campaignProgress?.latestBattleTitle !== '박망파 매복전' ||
postBowangProgressState.campaignProgress?.nextBattleTitle !== '장판파 피난로'
@@ -2026,7 +2026,7 @@ try {
if (
postChangbanProgressState?.activeTab !== 'progress' ||
postChangbanProgressState.campaignProgress?.completedKnown !== 19 ||
postChangbanProgressState.campaignProgress?.totalKnown !== 29 ||
postChangbanProgressState.campaignProgress?.totalKnown !== 30 ||
postChangbanProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
postChangbanProgressState.campaignProgress?.latestBattleTitle !== '장판파 피난로' ||
postChangbanProgressState.campaignProgress?.nextBattleTitle !== '강동 사절로'
@@ -2164,7 +2164,7 @@ try {
if (
postJiangdongProgressState?.activeTab !== 'progress' ||
postJiangdongProgressState.campaignProgress?.completedKnown !== 20 ||
postJiangdongProgressState.campaignProgress?.totalKnown !== 29 ||
postJiangdongProgressState.campaignProgress?.totalKnown !== 30 ||
postJiangdongProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
postJiangdongProgressState.campaignProgress?.latestBattleTitle !== '강동 사절로' ||
postJiangdongProgressState.campaignProgress?.nextBattleTitle !== '적벽 전초전'
@@ -2304,7 +2304,7 @@ try {
if (
postRedCliffsProgressState?.activeTab !== 'progress' ||
postRedCliffsProgressState.campaignProgress?.completedKnown !== 21 ||
postRedCliffsProgressState.campaignProgress?.totalKnown !== 29 ||
postRedCliffsProgressState.campaignProgress?.totalKnown !== 30 ||
postRedCliffsProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
postRedCliffsProgressState.campaignProgress?.latestBattleTitle !== '적벽 전초전' ||
postRedCliffsProgressState.campaignProgress?.nextBattleTitle !== '적벽 화공전'
@@ -2444,7 +2444,7 @@ try {
if (
postFireAttackProgressState?.activeTab !== 'progress' ||
postFireAttackProgressState.campaignProgress?.completedKnown !== 22 ||
postFireAttackProgressState.campaignProgress?.totalKnown !== 29 ||
postFireAttackProgressState.campaignProgress?.totalKnown !== 30 ||
postFireAttackProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postFireAttackProgressState.campaignProgress?.latestBattleTitle !== '적벽 화공전' ||
postFireAttackProgressState.campaignProgress?.nextBattleTitle !== '형주 남부 진입전'
@@ -2587,7 +2587,7 @@ try {
if (
postJingzhouProgressState?.activeTab !== 'progress' ||
postJingzhouProgressState.campaignProgress?.completedKnown !== 23 ||
postJingzhouProgressState.campaignProgress?.totalKnown !== 29 ||
postJingzhouProgressState.campaignProgress?.totalKnown !== 30 ||
postJingzhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postJingzhouProgressState.campaignProgress?.latestBattleTitle !== '형주 남부 진입전' ||
postJingzhouProgressState.campaignProgress?.nextBattleTitle !== '계양 설득전'
@@ -2729,7 +2729,7 @@ try {
if (
postGuiyangProgressState?.activeTab !== 'progress' ||
postGuiyangProgressState.campaignProgress?.completedKnown !== 24 ||
postGuiyangProgressState.campaignProgress?.totalKnown !== 29 ||
postGuiyangProgressState.campaignProgress?.totalKnown !== 30 ||
postGuiyangProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postGuiyangProgressState.campaignProgress?.latestBattleTitle !== '계양 설득전' ||
postGuiyangProgressState.campaignProgress?.nextBattleTitle !== '무릉 산길 확보전'
@@ -2872,7 +2872,7 @@ try {
if (
postWulingProgressState?.activeTab !== 'progress' ||
postWulingProgressState.campaignProgress?.completedKnown !== 25 ||
postWulingProgressState.campaignProgress?.totalKnown !== 29 ||
postWulingProgressState.campaignProgress?.totalKnown !== 30 ||
postWulingProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postWulingProgressState.campaignProgress?.latestBattleTitle !== '무릉 산길 확보전' ||
postWulingProgressState.campaignProgress?.nextBattleTitle !== '장사 노장 대면전'
@@ -3021,7 +3021,7 @@ try {
if (
postChangshaProgressState?.activeTab !== 'progress' ||
postChangshaProgressState.campaignProgress?.completedKnown !== 26 ||
postChangshaProgressState.campaignProgress?.totalKnown !== 29 ||
postChangshaProgressState.campaignProgress?.totalKnown !== 30 ||
postChangshaProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postChangshaProgressState.campaignProgress?.latestBattleTitle !== '장사 노장 대면전' ||
postChangshaProgressState.campaignProgress?.nextBattleTitle !== '익주 원군로'
@@ -3166,7 +3166,7 @@ try {
if (
postYizhouProgressState?.activeTab !== 'progress' ||
postYizhouProgressState.campaignProgress?.completedKnown !== 27 ||
postYizhouProgressState.campaignProgress?.totalKnown !== 29 ||
postYizhouProgressState.campaignProgress?.totalKnown !== 30 ||
postYizhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postYizhouProgressState.campaignProgress?.latestBattleTitle !== '익주 원군로' ||
postYizhouProgressState.campaignProgress?.nextBattleTitle !== '부수관 진입전'
@@ -3313,7 +3313,7 @@ try {
if (
postFuPassProgressState?.activeTab !== 'progress' ||
postFuPassProgressState.campaignProgress?.completedKnown !== 28 ||
postFuPassProgressState.campaignProgress?.totalKnown !== 29 ||
postFuPassProgressState.campaignProgress?.totalKnown !== 30 ||
postFuPassProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postFuPassProgressState.campaignProgress?.latestBattleTitle !== '부수관 진입전' ||
postFuPassProgressState.campaignProgress?.nextBattleTitle !== '낙성 외곽전'
@@ -3462,15 +3462,161 @@ try {
if (
postLuoProgressState?.activeTab !== 'progress' ||
postLuoProgressState.campaignProgress?.completedKnown !== 29 ||
postLuoProgressState.campaignProgress?.totalKnown !== 29 ||
postLuoProgressState.campaignProgress?.totalKnown !== 30 ||
postLuoProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postLuoProgressState.campaignProgress?.latestBattleTitle !== '낙성 외곽전' ||
postLuoProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
postLuoProgressState.campaignProgress?.nextBattleTitle !== '낙봉파 매복전'
) {
throw new Error(`Expected post-Luo progress tab to complete the outer-wall battle and pause on Luofeng preparation: ${JSON.stringify(postLuoProgressState?.campaignProgress)}`);
throw new Error(`Expected post-Luo progress tab to complete the outer-wall battle and open Luofeng Slope ambush: ${JSON.stringify(postLuoProgressState?.campaignProgress)}`);
}
await page.screenshot({ path: 'dist/verification-post-luo-progress.png', fullPage: true });
await page.mouse.click(1120, 38);
await page.waitForTimeout(180);
const luofengSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
!luofengSortieState?.sortieVisible ||
!luofengSortieState.sortiePlan?.objectiveLine?.includes('낙봉파 매복전') ||
!luofengSortieState.sortieRoster?.some((unit) => unit.id === 'pang-tong' && unit.recruited && unit.recommended) ||
!luofengSortieState.sortieRoster?.some((unit) => unit.id === 'wu-yi' && unit.recruited && unit.recommended) ||
!luofengSortieState.sortieRoster?.some((unit) => unit.id === 'fa-zheng' && unit.recruited && unit.recommended) ||
!luofengSortieState.sortieRoster?.some((unit) => unit.id === 'zhao-yun' && unit.recruited && unit.recommended) ||
!luofengSortieState.sortieRoster?.some((unit) => unit.id === 'huang-zhong' && unit.recruited && unit.recommended) ||
luofengSortieState.sortieRoster?.length < 16 ||
luofengSortieState.sortiePlan?.maxCount !== 6
) {
throw new Error(`Expected Luo camp sortie prep to target Luofeng with Pang Tong, Wu Yi, Fa Zheng, Zhao Yun, and Huang Zhong recommended: ${JSON.stringify(luofengSortieState)}`);
}
assertSortieTacticalRoster(luofengSortieState, [
'liu-bei',
'guan-yu',
'zhang-fei',
'jian-yong',
'mi-zhu',
'sun-qian',
'zhao-yun',
'zhuge-liang',
'ma-liang',
'yi-ji',
'gong-zhi',
'huang-zhong',
'wei-yan',
'pang-tong',
'fa-zheng',
'wu-yi'
]);
const luofengPriorityUnits = ['pang-tong', 'wu-yi', 'fa-zheng', 'zhao-yun', 'huang-zhong'];
for (const unitId of luofengPriorityUnits) {
const currentSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (!currentSortieState.sortieRoster?.some((unit) => unit.id === unitId && unit.selected)) {
const removable = currentSortieState.sortieRoster?.find(
(unit) => unit.selected && unit.id !== 'liu-bei' && !luofengPriorityUnits.includes(unit.id)
);
if (removable) {
await clickSortieRosterUnit(page, removable.id);
}
await clickSortieRosterUnit(page, unitId);
}
}
const luofengSortieReadyState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
!luofengPriorityUnits.every((unitId) =>
luofengSortieReadyState.sortieRoster?.some((unit) => unit.id === unitId && unit.selected)
) ||
luofengSortieReadyState.sortiePlan?.selectedCount !== 6 ||
luofengSortieReadyState.sortiePlan?.recommendedSelectedCount < 6
) {
throw new Error(`Expected Luofeng sortie to deploy Pang Tong, Wu Yi, Fa Zheng, Zhao Yun, and Huang Zhong while preserving six-officer pressure: ${JSON.stringify(luofengSortieReadyState)}`);
}
await page.screenshot({ path: 'dist/verification-luofeng-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-luofeng-story.png', fullPage: true });
for (let i = 0; i < 50; i += 1) {
const enteredThirtiethBattle = await page.evaluate(() => {
const state = window.__HEROS_DEBUG__?.battle();
return state?.scene === 'BattleScene' && state?.battleId === 'thirtieth-battle-luofeng-ambush';
});
if (enteredThirtiethBattle) {
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 === 'thirtieth-battle-luofeng-ambush' && state?.battleOutcome === null && state?.phase === 'idle';
});
await page.screenshot({ path: 'dist/verification-thirtieth-battle.png', fullPage: true });
const thirtiethBattleState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
const thirtiethEnemies = thirtiethBattleState.units.filter((unit) => unit.faction === 'enemy');
const thirtiethAllies = thirtiethBattleState.units.filter((unit) => unit.faction === 'ally');
const thirtiethEnemyBehaviors = new Set(thirtiethEnemies.map((unit) => unit.ai));
if (
thirtiethBattleState.camera?.mapWidth !== 60 ||
thirtiethBattleState.camera?.mapHeight !== 42 ||
thirtiethBattleState.victoryConditionLabel !== '장임 매복 격파' ||
thirtiethEnemies.length < 23 ||
!thirtiethEnemyBehaviors.has('aggressive') ||
!thirtiethEnemyBehaviors.has('guard') ||
!thirtiethEnemyBehaviors.has('hold') ||
!thirtiethEnemies.some((unit) => unit.id === 'luofeng-leader-zhang-ren') ||
!thirtiethAllies.some((unit) => unit.id === 'pang-tong') ||
!thirtiethAllies.some((unit) => unit.id === 'wu-yi') ||
!thirtiethAllies.some((unit) => unit.id === 'fa-zheng')
) {
throw new Error(`Expected thirtieth battle to use Luofeng map, Zhang Ren ambush objective, selected allied officers, and mixed AI: ${JSON.stringify(thirtiethBattleState)}`);
}
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 thirtiethCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
thirtiethCampState?.campBattleId !== 'thirtieth-battle-luofeng-ambush' ||
thirtiethCampState.campTitle !== '낙봉파 매복전 후 군영' ||
thirtiethCampState.availableDialogueIds?.length !== 3 ||
!thirtiethCampState.availableDialogueIds.every((id) => id.endsWith('luofeng')) ||
thirtiethCampState.availableVisitIds?.length !== 2 ||
!thirtiethCampState.campaign?.roster?.some((unit) => unit.id === 'wu-yi') ||
thirtiethCampState.rosterCollection?.total < 16
) {
throw new Error(`Expected thirtieth camp to preserve Wu Yi and expose Luofeng dialogue/visit sets: ${JSON.stringify(thirtiethCampState)}`);
}
await page.screenshot({ path: 'dist/verification-luofeng-camp.png', fullPage: true });
await page.mouse.click(966, 38);
await page.waitForTimeout(180);
const postLuofengProgressState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
if (
postLuofengProgressState?.activeTab !== 'progress' ||
postLuofengProgressState.campaignProgress?.completedKnown !== 30 ||
postLuofengProgressState.campaignProgress?.totalKnown !== 30 ||
postLuofengProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
postLuofengProgressState.campaignProgress?.latestBattleTitle !== '낙봉파 매복전' ||
postLuofengProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
) {
throw new Error(`Expected post-Luofeng progress tab to complete Luofeng and pause before Luo Castle proper: ${JSON.stringify(postLuofengProgressState?.campaignProgress)}`);
}
await page.screenshot({ path: 'dist/verification-post-luofeng-progress.png', fullPage: true });
await page.evaluate(() => window.__HEROS_GAME__?.scene.start('TitleScene'));
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
@@ -3482,7 +3628,7 @@ try {
return activeScenes.includes('CampScene');
});
console.log(`Verified title-to-twenty-ninth-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, Wei Yan, Pang Tong, Fa Zheng, and Wu Yi joins, result states, and debug API at ${targetUrl}`);
console.log(`Verified title-to-thirtieth-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, Wei Yan, Pang Tong, Fa Zheng, Wu Yi joins, Luofeng ambush, result states, and debug API at ${targetUrl}`);
} finally {
await browser?.close();
if (serverProcess && !serverProcess.killed) {