feat: refine battle feedback and early guidance
This commit is contained in:
@@ -102,12 +102,75 @@ try {
|
||||
initialStoryProgress.advanceHint === 'next' &&
|
||||
initialStoryProgress.isLastPage === false &&
|
||||
boundsWithinFhdViewport(initialStoryProgress.progressBounds) &&
|
||||
boundsWithinFhdViewport(initialStoryProgress.progressPanelBounds),
|
||||
boundsWithinFhdViewport(initialStoryProgress.progressPanelBounds) &&
|
||||
boundsInside(initialStoryProgress.progressBounds, initialStoryProgress.progressPanelBounds),
|
||||
`Expected localized, FHD-safe story progress guidance: ${JSON.stringify(initialStoryProgress)}`
|
||||
);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-new-game-story.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'new game story');
|
||||
await advanceUntilBattle(page, 'first-battle-zhuo-commandery');
|
||||
const firstBattleTransition = await advanceUntilBattle(page, 'first-battle-zhuo-commandery', {
|
||||
startDeployment: false,
|
||||
captureLastStory: true
|
||||
});
|
||||
assert(
|
||||
firstBattleTransition.lastStory?.isLastPage === true &&
|
||||
firstBattleTransition.lastStory?.nextScene === 'BattleScene' &&
|
||||
firstBattleTransition.lastStory?.advanceHint === 'battle-deployment' &&
|
||||
firstBattleTransition.lastStory?.advanceLabel === '전투 배치' &&
|
||||
firstBattleTransition.lastStory?.progressLabel?.includes('전투 배치') &&
|
||||
boundsWithinFhdViewport(firstBattleTransition.lastStory?.progressBounds) &&
|
||||
boundsWithinFhdViewport(firstBattleTransition.lastStory?.progressPanelBounds) &&
|
||||
boundsInside(
|
||||
firstBattleTransition.lastStory?.progressBounds,
|
||||
firstBattleTransition.lastStory?.progressPanelBounds
|
||||
),
|
||||
`Expected the final prologue page to name the next action as battle deployment: ${JSON.stringify(firstBattleTransition)}`
|
||||
);
|
||||
const firstBattleDeployment = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
|
||||
const firstDeploymentPanel = firstBattleDeployment?.battleHud?.deploymentPanel;
|
||||
assert(
|
||||
firstBattleDeployment?.phase === 'deployment' &&
|
||||
firstDeploymentPanel?.recommended === true &&
|
||||
firstDeploymentPanel?.changeCount === 0 &&
|
||||
boundsInside(firstDeploymentPanel?.statusBounds, firstDeploymentPanel?.headerBounds),
|
||||
`Expected first deployment to show an FHD-safe recommended-formation status chip: ${JSON.stringify(firstBattleDeployment)}`
|
||||
);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-first-battle-deployment.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'first battle deployment');
|
||||
|
||||
const changedFirstDeployment = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
const recommended = scene?.deploymentSlots?.().filter((slot) => slot.unitId) ?? [];
|
||||
const first = recommended[0]?.unitId ? scene?.debugUnitById?.(recommended[0].unitId) : undefined;
|
||||
const second = recommended[1]?.unitId ? scene?.debugUnitById?.(recommended[1].unitId) : undefined;
|
||||
if (!scene || !first || !second) {
|
||||
return null;
|
||||
}
|
||||
scene.swapDeploymentUnits(first, second);
|
||||
return window.__HEROS_DEBUG__?.battle()?.battleHud?.deploymentPanel ?? null;
|
||||
});
|
||||
assert(
|
||||
changedFirstDeployment?.recommended === false &&
|
||||
changedFirstDeployment?.changeCount >= 2 &&
|
||||
boundsInside(changedFirstDeployment?.statusBounds, changedFirstDeployment?.headerBounds),
|
||||
`Expected a changed first deployment to expose a restore-ready status: ${JSON.stringify(changedFirstDeployment)}`
|
||||
);
|
||||
await page.waitForTimeout(220);
|
||||
const restoredFirstDeployment = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
scene?.restoreRecommendedDeployment?.();
|
||||
return window.__HEROS_DEBUG__?.battle()?.battleHud?.deploymentPanel ?? null;
|
||||
});
|
||||
assert(
|
||||
restoredFirstDeployment?.recommended === true && restoredFirstDeployment?.changeCount === 0,
|
||||
`Expected one restore action to return the first deployment to the recommended formation: ${JSON.stringify(restoredFirstDeployment)}`
|
||||
);
|
||||
await page.waitForTimeout(180);
|
||||
await clickBattleDeploymentStart(page);
|
||||
await page.waitForFunction(() => {
|
||||
const state = window.__HEROS_DEBUG__?.battle();
|
||||
return state?.battleId === 'first-battle-zhuo-commandery' && state?.phase === 'idle' && state?.triggeredBattleEvents?.includes('opening');
|
||||
}, undefined, { timeout: 30000 });
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-first-battle.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'first battle');
|
||||
|
||||
@@ -115,6 +178,7 @@ try {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
const state = window.__HEROS_DEBUG__?.battle();
|
||||
const sideTexts = textValues(scene?.sidePanelObjects);
|
||||
const bannerTexts = textValues(scene?.battleEventObjects);
|
||||
const miniMapTexts = textValues((scene?.miniMapObjects ?? []).filter((object) => object?.active && object?.visible));
|
||||
const miniMapFrame = (scene?.miniMapObjects ?? []).find((object) => object?.name === 'mini-map-frame');
|
||||
return {
|
||||
@@ -122,6 +186,7 @@ try {
|
||||
objectiveText: scene?.objectiveTrackerText?.text,
|
||||
objectiveSubText: scene?.objectiveTrackerSubText?.text,
|
||||
sideTexts,
|
||||
bannerTexts,
|
||||
miniMapTexts,
|
||||
actualMiniMapFrameBounds: miniMapFrame ? boundsValue(miniMapFrame.getBounds()) : null,
|
||||
hud: state?.battleHud ?? null,
|
||||
@@ -156,9 +221,10 @@ try {
|
||||
`Expected the compact objective subtext to retain only the defeat condition without duplicate route or bonus guidance: ${JSON.stringify(firstBattleProbe)}`
|
||||
);
|
||||
assert(
|
||||
firstBattleProbe.sideTexts.some((text) => text.includes('출진 군세')) &&
|
||||
!firstBattleProbe.sideTexts.some((text) => text.includes('...')),
|
||||
`Expected sortie doctrine opening message without ASCII truncation: ${JSON.stringify(firstBattleProbe.sideTexts)}`
|
||||
firstBattleProbe.bannerTexts.some((text) => text.includes('출진 군세')) &&
|
||||
firstBattleProbe.bannerTexts.some((text) => text.includes('첫 행동')) &&
|
||||
!firstBattleProbe.bannerTexts.some((text) => text.includes('...')),
|
||||
`Expected sortie doctrine and first-action guidance without ASCII truncation: ${JSON.stringify(firstBattleProbe.bannerTexts)}`
|
||||
);
|
||||
const firstBattleMiniMap = firstBattleProbe.hud?.miniMap;
|
||||
const firstBattleRecentActions = firstBattleProbe.hud?.recentActions;
|
||||
@@ -206,6 +272,93 @@ try {
|
||||
firstBattleRecentActions.bottom <= firstBattleMiniMap.frameBounds.y,
|
||||
`Expected the FHD recent-action panel to show three noncompact rows without touching the minimap frame: ${JSON.stringify(firstBattleProbe.hud)}`
|
||||
);
|
||||
const firstBattleMapFeedback = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
const unit = scene?.debugUnitById?.('liu-bei');
|
||||
const view = unit ? scene?.unitViews?.get(unit.id) : undefined;
|
||||
if (!scene || !unit || !view) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalX = view.sprite.x;
|
||||
const originalY = view.sprite.y;
|
||||
const originalSpeed = scene.battleSpeed;
|
||||
scene.battleSpeed = 'normal';
|
||||
view.sprite.x = scene.layout.mapX + 1;
|
||||
view.sprite.y = scene.layout.mapY + 1;
|
||||
scene.showMapResultPopup(unit, ['치명 피해 -999', 'HP 999→1', '공명 추격 +99'], '#ff8f5f', '#220909', 20, 38);
|
||||
const normal = window.__HEROS_DEBUG__?.battle()?.mapResultFeedback;
|
||||
const normalActualBounds = (scene.mapResultPopupObjects ?? [])
|
||||
.filter((object) => object?.active)
|
||||
.map((object) => boundsValue(object.getBounds()));
|
||||
scene.battleSpeed = 'very-fast';
|
||||
for (let index = 0; index < 6; index += 1) {
|
||||
scene.showMapResultPopup(unit, [`연속 판정 ${index + 1}`, 'HP 경계 확인'], '#ffdf7b', '#220909', 18, 30);
|
||||
}
|
||||
const fast = window.__HEROS_DEBUG__?.battle()?.mapResultFeedback;
|
||||
const fastActualBounds = (scene.mapResultPopupObjects ?? [])
|
||||
.filter((object) => object?.active)
|
||||
.map((object) => boundsValue(object.getBounds()));
|
||||
view.sprite.x = originalX;
|
||||
view.sprite.y = originalY;
|
||||
scene.battleSpeed = originalSpeed;
|
||||
scene.renderBattleSpeedControl?.();
|
||||
return {
|
||||
normal: { ...normal, actualBounds: normalActualBounds },
|
||||
fast: { ...fast, actualBounds: fastActualBounds }
|
||||
};
|
||||
|
||||
function boundsValue(bounds) {
|
||||
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
||||
}
|
||||
});
|
||||
assert(
|
||||
firstBattleMapFeedback?.normal?.last?.duration === 680 &&
|
||||
firstBattleMapFeedback.normal.last.delay === 80 &&
|
||||
boundsInside(firstBattleMapFeedback.normal.last.bounds, firstBattleMapFeedback.normal.mapBounds) &&
|
||||
boundsInside(firstBattleMapFeedback.normal.last.endBounds, firstBattleMapFeedback.normal.mapBounds) &&
|
||||
firstBattleMapFeedback.fast?.last?.duration >= 260 &&
|
||||
firstBattleMapFeedback.fast.last.duration < firstBattleMapFeedback.normal.last.duration &&
|
||||
firstBattleMapFeedback.fast.last.delay >= 30 &&
|
||||
firstBattleMapFeedback.fast.last.delay < firstBattleMapFeedback.normal.last.delay &&
|
||||
boundsInside(firstBattleMapFeedback.fast.last.bounds, firstBattleMapFeedback.fast.mapBounds) &&
|
||||
boundsInside(firstBattleMapFeedback.fast.last.endBounds, firstBattleMapFeedback.fast.mapBounds) &&
|
||||
firstBattleMapFeedback.normal.actualBounds.length === 1 &&
|
||||
firstBattleMapFeedback.normal.actualBounds.every((bounds) =>
|
||||
boundsInside(bounds, firstBattleMapFeedback.normal.mapBounds)
|
||||
) &&
|
||||
firstBattleMapFeedback.fast.actualBounds.length > 0 &&
|
||||
firstBattleMapFeedback.fast.actualBounds.every((bounds) =>
|
||||
boundsInside(bounds, firstBattleMapFeedback.fast.mapBounds)
|
||||
) &&
|
||||
firstBattleMapFeedback.fast.activeCount <= firstBattleMapFeedback.fast.maxActive &&
|
||||
firstBattleMapFeedback.fast.peakCount <= firstBattleMapFeedback.fast.maxActive,
|
||||
`Expected bounded, speed-aware, capped map result feedback at FHD: ${JSON.stringify(firstBattleMapFeedback)}`
|
||||
);
|
||||
await page.waitForTimeout(120);
|
||||
const animatingMapFeedback = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
const state = window.__HEROS_DEBUG__?.battle()?.mapResultFeedback;
|
||||
return {
|
||||
mapBounds: state?.mapBounds ?? null,
|
||||
activeBounds: (scene?.mapResultPopupObjects ?? [])
|
||||
.filter((object) => object?.active)
|
||||
.map((object) => {
|
||||
const bounds = object.getBounds();
|
||||
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
||||
})
|
||||
};
|
||||
});
|
||||
assert(
|
||||
animatingMapFeedback.activeBounds.length > 0 &&
|
||||
animatingMapFeedback.activeBounds.every((bounds) => boundsInside(bounds, animatingMapFeedback.mapBounds)),
|
||||
`Expected animated map result feedback to remain inside the map: ${JSON.stringify(animatingMapFeedback)}`
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() => window.__HEROS_DEBUG__?.battle()?.mapResultFeedback?.activeCount === 0,
|
||||
undefined,
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
await setDebugQueryParam(page, 'debugForceBondChain', '1');
|
||||
const firstBattleBondChain = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
@@ -2057,7 +2210,21 @@ try {
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__?.battle()?.sortieEvaluation?.open === false, undefined, { timeout: 30000 });
|
||||
|
||||
await clickLegacyUi(page, 738, 642);
|
||||
await waitForCampAfterBattleResult(page);
|
||||
const firstCampTransition = await waitForCampAfterBattleResult(page);
|
||||
assert(
|
||||
firstCampTransition.lastStory?.isLastPage === true &&
|
||||
firstCampTransition.lastStory?.nextScene === 'CampScene' &&
|
||||
firstCampTransition.lastStory?.advanceHint === 'camp-return' &&
|
||||
firstCampTransition.lastStory?.advanceLabel === '군영으로' &&
|
||||
firstCampTransition.lastStory?.progressLabel?.includes('군영으로') &&
|
||||
boundsWithinFhdViewport(firstCampTransition.lastStory?.progressBounds) &&
|
||||
boundsWithinFhdViewport(firstCampTransition.lastStory?.progressPanelBounds) &&
|
||||
boundsInside(
|
||||
firstCampTransition.lastStory?.progressBounds,
|
||||
firstCampTransition.lastStory?.progressPanelBounds
|
||||
),
|
||||
`Expected the post-battle story to name the next action as returning to camp: ${JSON.stringify(firstCampTransition)}`
|
||||
);
|
||||
await waitForCampSkinTransition(page, 'yellow-turban');
|
||||
await waitForCampSoundscapeTransition(page, 'camp-rally', 'campfire-ambience');
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-first-camp.png`, fullPage: true });
|
||||
@@ -2643,6 +2810,21 @@ try {
|
||||
undefined,
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
const firstCampSecondaryPanels = await page.evaluate(() => window.__HEROS_DEBUG__?.camp()?.secondaryPanels);
|
||||
const firstCampSuppliesPanel = firstCampSecondaryPanels?.supplies;
|
||||
const firstCampReportPanel = firstCampSecondaryPanels?.report;
|
||||
assert(
|
||||
firstCampSuppliesPanel?.visible === true &&
|
||||
firstCampReportPanel?.visible === true &&
|
||||
boundsWithinFhdViewport(firstCampSuppliesPanel.bounds) &&
|
||||
boundsWithinFhdViewport(firstCampReportPanel.bounds) &&
|
||||
firstCampSuppliesPanel.equipmentBoxBounds?.length > 0 &&
|
||||
firstCampSuppliesPanel.trophyBoxBounds?.length > 0 &&
|
||||
firstCampSuppliesPanel.equipmentBoxBounds?.every((bounds) => boundsInside(bounds, firstCampSuppliesPanel.bounds)) &&
|
||||
firstCampSuppliesPanel.trophyBoxBounds.every((bounds) => boundsInside(bounds, firstCampSuppliesPanel.bounds)) &&
|
||||
firstCampSuppliesPanel.bounds.y + firstCampSuppliesPanel.bounds.height <= firstCampReportPanel.bounds.y,
|
||||
`Expected supply cards inside their FHD panel without overlapping the battle report: ${JSON.stringify(firstCampSecondaryPanels)}`
|
||||
);
|
||||
const firstCampTabAudioState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp()?.audio);
|
||||
assert(
|
||||
firstCampTabAudioState?.music?.currentKey === firstCampProbe.state?.audio?.music?.currentKey &&
|
||||
@@ -2712,6 +2894,96 @@ try {
|
||||
})}`
|
||||
);
|
||||
|
||||
const equipmentPaginationFixture = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
if (!scene?.campaign) {
|
||||
return null;
|
||||
}
|
||||
const originalInventory = { ...scene.campaign.inventory };
|
||||
['황건도', '단궁', '두령부', '철창', '포의', '찰갑'].forEach((label) => {
|
||||
scene.campaign.inventory[label] = Math.max(1, scene.campaign.inventory[label] ?? 0);
|
||||
});
|
||||
scene.activeTab = 'equipment';
|
||||
scene.equipmentInventoryPage = 0;
|
||||
scene.render();
|
||||
return {
|
||||
originalInventory,
|
||||
panel: window.__HEROS_DEBUG__?.camp()?.secondaryPanels?.equipment ?? null
|
||||
};
|
||||
});
|
||||
assert(
|
||||
equipmentPaginationFixture?.panel?.visible === true &&
|
||||
equipmentPaginationFixture.panel.page === 0 &&
|
||||
equipmentPaginationFixture.panel.pageCount >= 2 &&
|
||||
equipmentPaginationFixture.panel.visibleItemIds?.length === equipmentPaginationFixture.panel.pageSize &&
|
||||
equipmentPaginationFixture.panel.nextEnabled === true &&
|
||||
boundsWithinFhdViewport(equipmentPaginationFixture.panel.bounds) &&
|
||||
boundsInside(equipmentPaginationFixture.panel.nextButtonBounds, equipmentPaginationFixture.panel.bounds),
|
||||
`Expected the equipment manager to expose the first of multiple inventory pages: ${JSON.stringify(equipmentPaginationFixture)}`
|
||||
);
|
||||
const equipmentNextBounds = equipmentPaginationFixture.panel.nextButtonBounds;
|
||||
await page.mouse.click(
|
||||
equipmentNextBounds.x + equipmentNextBounds.width / 2,
|
||||
equipmentNextBounds.y + equipmentNextBounds.height / 2
|
||||
);
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__?.camp()?.secondaryPanels?.equipment?.page === 1, undefined, { timeout: 30000 });
|
||||
const equipmentSecondPage = await page.evaluate(() => window.__HEROS_DEBUG__?.camp()?.secondaryPanels?.equipment);
|
||||
assert(
|
||||
equipmentSecondPage?.visible === true &&
|
||||
equipmentSecondPage.page === 1 &&
|
||||
equipmentSecondPage.page === equipmentSecondPage.pageCount - 1 &&
|
||||
equipmentSecondPage.previousEnabled === true &&
|
||||
equipmentSecondPage.nextEnabled === false &&
|
||||
equipmentSecondPage.visibleItemIds?.length > 0 &&
|
||||
equipmentSecondPage.visibleItemIds.every((itemId) => !equipmentPaginationFixture.panel.visibleItemIds.includes(itemId)) &&
|
||||
boundsInside(equipmentSecondPage.previousButtonBounds, equipmentSecondPage.bounds),
|
||||
`Expected equipment pagination to reach distinct lower-priority items: ${JSON.stringify({ first: equipmentPaginationFixture.panel, second: equipmentSecondPage })}`
|
||||
);
|
||||
const equipmentClampedPage = await page.evaluate((originalInventory) => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
if (!scene?.campaign) {
|
||||
return null;
|
||||
}
|
||||
scene.campaign.inventory = { ...originalInventory };
|
||||
scene.render();
|
||||
return window.__HEROS_DEBUG__?.camp()?.secondaryPanels?.equipment ?? null;
|
||||
}, equipmentPaginationFixture.originalInventory);
|
||||
assert(
|
||||
equipmentClampedPage?.page === Math.max(0, equipmentClampedPage?.pageCount - 1) &&
|
||||
equipmentClampedPage?.page < equipmentClampedPage?.pageCount,
|
||||
`Expected equipment pagination to clamp after the inventory shrinks: ${JSON.stringify(equipmentClampedPage)}`
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
scene.equipmentInventoryPage = 0;
|
||||
scene.activeTab = 'supplies';
|
||||
scene.render();
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
scene?.showCampNotice?.('첫 번째 정비 알림');
|
||||
scene?.time?.delayedCall(400, () => scene?.showCampNotice?.('두 번째 응답 알림'));
|
||||
});
|
||||
await page.waitForTimeout(1750);
|
||||
const replacementNotice = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (scene?.dialogueObjects ?? [])
|
||||
.filter((object) => object?.active)
|
||||
.map((object) => object?.text)
|
||||
.filter(Boolean);
|
||||
});
|
||||
assert(
|
||||
replacementNotice.some((text) => text.includes('두 번째 응답 알림')),
|
||||
`Expected a replacement camp notice to survive the earlier notice cleanup: ${JSON.stringify(replacementNotice)}`
|
||||
);
|
||||
await page.waitForFunction(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (scene?.dialogueObjects ?? []).filter((object) => object?.active).length === 0;
|
||||
}, undefined, { timeout: 1500 });
|
||||
|
||||
const firstCampSelectedSortieUnitIds = firstCampProbe.state?.selectedSortieUnitIds ?? [];
|
||||
const firstCampDeploymentPreview = firstCampProbe.state?.sortieDeploymentPreview ?? [];
|
||||
await clickLegacyUi(page, 1160, 38);
|
||||
@@ -7240,31 +7512,48 @@ async function waitForStoryOrEnding(page) {
|
||||
return page.evaluate(() => window.__HEROS_DEBUG__?.activeScenes() ?? []);
|
||||
}
|
||||
|
||||
async function advanceUntilBattle(page, battleId) {
|
||||
async function advanceUntilBattle(page, battleId, options = {}) {
|
||||
const { startDeployment = true, captureLastStory = false } = options;
|
||||
let lastStory = null;
|
||||
for (let i = 0; i < 48; i += 1) {
|
||||
const ready = await page.evaluate((expectedBattleId) => {
|
||||
const probe = await page.evaluate((expectedBattleId) => {
|
||||
const state = window.__HEROS_DEBUG__?.battle();
|
||||
return (
|
||||
state?.scene === 'BattleScene' &&
|
||||
state?.battleId === expectedBattleId &&
|
||||
(state?.phase === 'deployment' || state?.phase === 'idle') &&
|
||||
state?.mapBackgroundReady === true
|
||||
);
|
||||
return {
|
||||
ready: (
|
||||
state?.scene === 'BattleScene' &&
|
||||
state?.battleId === expectedBattleId &&
|
||||
(state?.phase === 'deployment' || state?.phase === 'idle') &&
|
||||
state?.mapBackgroundReady === true
|
||||
),
|
||||
story: window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.() ?? null
|
||||
};
|
||||
}, battleId);
|
||||
|
||||
if (ready) {
|
||||
await startDeploymentIfNeeded(page, battleId);
|
||||
return;
|
||||
if (
|
||||
captureLastStory &&
|
||||
probe.story?.isLastPage &&
|
||||
probe.story?.progressBounds &&
|
||||
probe.story?.progressPanelBounds
|
||||
) {
|
||||
lastStory = probe.story;
|
||||
}
|
||||
|
||||
if (probe.ready) {
|
||||
if (startDeployment) {
|
||||
await startDeploymentIfNeeded(page, battleId);
|
||||
}
|
||||
return { lastStory };
|
||||
}
|
||||
|
||||
await page.keyboard.press('Space');
|
||||
await page.waitForTimeout(220);
|
||||
}
|
||||
|
||||
await waitForBattleReady(page, battleId);
|
||||
await waitForBattleReady(page, battleId, startDeployment);
|
||||
return { lastStory };
|
||||
}
|
||||
|
||||
async function waitForBattleReady(page, battleId) {
|
||||
async function waitForBattleReady(page, battleId, startDeployment = true) {
|
||||
await page.waitForFunction((expectedBattleId) => {
|
||||
const state = window.__HEROS_DEBUG__?.battle();
|
||||
return (
|
||||
@@ -7274,7 +7563,9 @@ async function waitForBattleReady(page, battleId) {
|
||||
state?.mapBackgroundReady === true
|
||||
);
|
||||
}, battleId, { timeout: 90000 });
|
||||
await startDeploymentIfNeeded(page, battleId);
|
||||
if (startDeployment) {
|
||||
await startDeploymentIfNeeded(page, battleId);
|
||||
}
|
||||
}
|
||||
|
||||
async function startDeploymentIfNeeded(page, battleId) {
|
||||
@@ -8204,17 +8495,25 @@ async function waitForCampAfterBattleResult(page) {
|
||||
return activeScenes.includes('CampScene') || activeScenes.includes('StoryScene');
|
||||
}, undefined, { timeout: 90000 });
|
||||
|
||||
let lastStory = null;
|
||||
for (let i = 0; i < 48; i += 1) {
|
||||
const activeScenes = await page.evaluate(() => window.__HEROS_DEBUG__?.activeScenes() ?? []);
|
||||
if (activeScenes.includes('CampScene')) {
|
||||
const probe = await page.evaluate(() => ({
|
||||
activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [],
|
||||
story: window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.() ?? null
|
||||
}));
|
||||
if (probe.story?.isLastPage && probe.story?.progressBounds && probe.story?.progressPanelBounds) {
|
||||
lastStory = probe.story;
|
||||
}
|
||||
if (probe.activeScenes.includes('CampScene')) {
|
||||
await waitForCamp(page);
|
||||
return;
|
||||
return { lastStory };
|
||||
}
|
||||
await page.keyboard.press('Space');
|
||||
await page.waitForTimeout(240);
|
||||
}
|
||||
|
||||
await waitForCamp(page);
|
||||
return { lastStory };
|
||||
}
|
||||
|
||||
async function advanceStoryUntilEnding(page) {
|
||||
|
||||
Reference in New Issue
Block a user