fix: preserve pursuit target through finisher
This commit is contained in:
@@ -73,6 +73,79 @@ async function clickSceneBounds(page, sceneKey, bounds) {
|
||||
await page.mouse.click(point.x, point.y);
|
||||
}
|
||||
|
||||
async function clickTitleContinue(page, slot = 1) {
|
||||
const continueItem = await page.evaluate(() => {
|
||||
const state =
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('TitleScene')
|
||||
?.getDebugState?.();
|
||||
return (
|
||||
state?.focus?.scope === 'main'
|
||||
? state.focus.items.find(
|
||||
(item) =>
|
||||
item.id === 'continue' &&
|
||||
item.enabled === true
|
||||
)
|
||||
: undefined
|
||||
) ?? null;
|
||||
});
|
||||
assert(
|
||||
continueItem?.bounds,
|
||||
`Expected an enabled title continue control: ${JSON.stringify(
|
||||
continueItem
|
||||
)}`
|
||||
);
|
||||
await clickSceneBounds(
|
||||
page,
|
||||
'TitleScene',
|
||||
continueItem.bounds
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const activeScenes =
|
||||
window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||
const state =
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('TitleScene')
|
||||
?.getDebugState?.();
|
||||
return (
|
||||
!activeScenes.includes('TitleScene') ||
|
||||
state?.navigating === true ||
|
||||
state?.panels?.saveSlots === true
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
|
||||
const saveSlotItem = await page.evaluate(
|
||||
(requestedSlot) => {
|
||||
const state =
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('TitleScene')
|
||||
?.getDebugState?.();
|
||||
if (state?.panels?.saveSlots !== true) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
state.focus?.items?.find(
|
||||
(item) =>
|
||||
item.id === `slot-${requestedSlot}` &&
|
||||
item.enabled === true
|
||||
) ?? null
|
||||
);
|
||||
},
|
||||
slot
|
||||
);
|
||||
if (saveSlotItem) {
|
||||
await clickSceneBounds(
|
||||
page,
|
||||
'TitleScene',
|
||||
saveSlotItem.bounds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function clickBattleDeploymentStart(page) {
|
||||
const point = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
@@ -223,7 +296,7 @@ try {
|
||||
);
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForStoryReady(page);
|
||||
const resumedPrologue = await page.evaluate(() => window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.());
|
||||
const resumedPrologueSave = await readCampaignSave(page);
|
||||
@@ -1398,11 +1471,13 @@ try {
|
||||
cutInOrder.push(isPrimary ? 'primary' : result === counterResult || result.isCounter ? 'counter' : 'other');
|
||||
if (isPrimary) {
|
||||
primaryCutInStarted = true;
|
||||
this.updateCameraView();
|
||||
visibleAtCutInStart = viewState();
|
||||
}
|
||||
await originalPlayCombatCutIn.call(this, result);
|
||||
if (isPrimary) {
|
||||
primaryCutInCompleted = true;
|
||||
this.updateCameraView();
|
||||
visibleAtCutInEnd = viewState();
|
||||
}
|
||||
};
|
||||
@@ -1448,6 +1523,10 @@ try {
|
||||
visibleAtCutInEnd,
|
||||
defeatApplications,
|
||||
visibleAfterWrapper: viewState(),
|
||||
pendingDefeatPresentationAfterWrapper:
|
||||
scene.pendingDefeatPresentationUnitIds.has(
|
||||
target.id
|
||||
),
|
||||
counterCalls,
|
||||
counterProduced,
|
||||
cutInOrder,
|
||||
@@ -1594,8 +1673,10 @@ try {
|
||||
productionFinisher.visibleAfterWrapper.spriteVisible === false &&
|
||||
productionFinisher.visibleAfterWrapper.spriteAlpha === 0 &&
|
||||
productionFinisher.visibleAfterWrapper.labelVisible === false &&
|
||||
productionFinisher.visibleAfterWrapper.hitZoneVisible === false &&
|
||||
productionFinisher.visibleAfterWrapper.miniMapDotVisible === false &&
|
||||
productionFinisher.counterCalls === 1 &&
|
||||
productionFinisher.pendingDefeatPresentationAfterWrapper === false &&
|
||||
productionFinisher.counterCalls === 0 &&
|
||||
productionFinisher.counterProduced === false &&
|
||||
productionFinisher.result.counter === false &&
|
||||
JSON.stringify(productionFinisher.cutInOrder) === JSON.stringify(['primary']),
|
||||
@@ -2692,7 +2773,7 @@ try {
|
||||
);
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForStoryReady(page);
|
||||
const resumedFirstAftermath = await page.evaluate(() => window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.());
|
||||
const resumedFirstAftermathSave = await readCampaignSave(page);
|
||||
@@ -5826,7 +5907,7 @@ try {
|
||||
});
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
|
||||
const requiredCasualtyRecruitment = await page.evaluate(() => {
|
||||
@@ -5893,7 +5974,7 @@ try {
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
const requiredCasualtyCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||
assert(
|
||||
@@ -6204,7 +6285,7 @@ try {
|
||||
});
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
const midCampaignSelectionFixture = await page.evaluate((requestedUnitIds) => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
@@ -7351,7 +7432,7 @@ try {
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
const restoredUnderCapacityState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||
assert(
|
||||
@@ -8334,7 +8415,7 @@ try {
|
||||
});
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
await waitForCampSkinTransition(page, 'northern');
|
||||
await waitForCampSoundscapeTransition(page, 'camp-frontier', 'mountain-wind-ambience');
|
||||
@@ -8476,7 +8557,7 @@ try {
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForCamp(page);
|
||||
await waitForCampSkinTransition(page, 'northern');
|
||||
const resumedFinalCampAfterRewardClose = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||
@@ -8515,7 +8596,7 @@ try {
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await waitForTitle(page);
|
||||
await clickLegacyUi(page, 962, 310);
|
||||
await clickTitleContinue(page);
|
||||
await waitForEnding(page);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-ending-continue.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'ending continue');
|
||||
@@ -8573,8 +8654,28 @@ async function waitForTitle(page) {
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__ !== undefined, undefined, { timeout: 90000 });
|
||||
await page.waitForFunction(() => {
|
||||
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||
return activeScenes.includes('TitleScene');
|
||||
const state =
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('TitleScene')
|
||||
?.getDebugState?.();
|
||||
return (
|
||||
activeScenes.includes('TitleScene') &&
|
||||
state?.scene === 'TitleScene' &&
|
||||
state.navigating === false &&
|
||||
Array.isArray(state.focus?.items) &&
|
||||
state.focus.items.some(
|
||||
(item) =>
|
||||
item.enabled === true &&
|
||||
Number.isFinite(item.bounds?.x) &&
|
||||
Number.isFinite(item.bounds?.y) &&
|
||||
Number.isFinite(item.bounds?.width) &&
|
||||
Number.isFinite(item.bounds?.height)
|
||||
)
|
||||
);
|
||||
}, undefined, { timeout: 90000 });
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(resolve));
|
||||
}));
|
||||
}
|
||||
|
||||
async function assertReleaseShellReady(page) {
|
||||
@@ -10641,16 +10742,41 @@ async function waitForBattleOutcome(page, outcome) {
|
||||
}
|
||||
|
||||
async function waitForCamp(page) {
|
||||
await page.waitForFunction(() => {
|
||||
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||
const scene = window.__HEROS_DEBUG__?.scene('CampScene');
|
||||
return (
|
||||
activeScenes.includes('CampScene') &&
|
||||
!activeScenes.includes('BattleScene') &&
|
||||
window.__HEROS_DEBUG__?.camp()?.scene === 'CampScene' &&
|
||||
(scene?.contentObjects?.length ?? 0) > 0
|
||||
try {
|
||||
await page.waitForFunction(() => {
|
||||
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||
const scene = window.__HEROS_DEBUG__?.scene('CampScene');
|
||||
return (
|
||||
activeScenes.includes('CampScene') &&
|
||||
!activeScenes.includes('BattleScene') &&
|
||||
window.__HEROS_DEBUG__?.camp()?.scene === 'CampScene' &&
|
||||
(scene?.contentObjects?.length ?? 0) > 0
|
||||
);
|
||||
}, undefined, { timeout: 90000 });
|
||||
} catch (error) {
|
||||
const diagnostic = await page.evaluate(() => ({
|
||||
activeScenes:
|
||||
window.__HEROS_DEBUG__?.activeScenes() ?? [],
|
||||
title:
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('TitleScene')
|
||||
?.getDebugState?.() ?? null,
|
||||
battle:
|
||||
window.__HEROS_DEBUG__?.battle?.() ?? null,
|
||||
camp:
|
||||
window.__HEROS_DEBUG__?.camp?.() ?? null,
|
||||
story:
|
||||
window.__HEROS_DEBUG__
|
||||
?.scene('StoryScene')
|
||||
?.getDebugState?.() ?? null
|
||||
}));
|
||||
throw new Error(
|
||||
`Expected a ready camp scene: ${JSON.stringify(
|
||||
diagnostic
|
||||
)}`,
|
||||
{ cause: error }
|
||||
);
|
||||
}, undefined, { timeout: 90000 });
|
||||
}
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(resolve));
|
||||
}));
|
||||
@@ -12411,12 +12537,33 @@ async function readBattleDoctrineProbe(page) {
|
||||
}
|
||||
|
||||
async function seedCampaignSave(page, options) {
|
||||
await page.evaluate(() => {
|
||||
const game = window.__HEROS_GAME__;
|
||||
game?.scene
|
||||
.getScenes(true)
|
||||
.forEach((scene) =>
|
||||
game.scene.stop(scene.scene.key)
|
||||
);
|
||||
});
|
||||
await page.evaluate(
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
requestAnimationFrame(() =>
|
||||
requestAnimationFrame(resolve)
|
||||
)
|
||||
)
|
||||
);
|
||||
await page.evaluate((seed) => {
|
||||
const storageKey = 'heros-web:campaign-state';
|
||||
const current = JSON.parse(window.localStorage.getItem(storageKey) ?? 'null');
|
||||
if (!current?.firstBattleReport) {
|
||||
throw new Error('Expected an existing campaign report before seeding RC save.');
|
||||
}
|
||||
// These fixtures model a completed victory already resting at camp.
|
||||
// Do not inherit a live battle auto-save or an older navigation checkpoint.
|
||||
Object.keys(window.localStorage)
|
||||
.filter((key) => key.startsWith('heros-web:battle:'))
|
||||
.forEach((key) => window.localStorage.removeItem(key));
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const objectives = [
|
||||
@@ -12492,6 +12639,12 @@ async function seedCampaignSave(page, options) {
|
||||
step: seed.step,
|
||||
activeSaveSlot: 1,
|
||||
gold: seed.gold,
|
||||
pendingAftermathBattleId: undefined,
|
||||
activeCityStayId: undefined,
|
||||
activeCampVisitId: undefined,
|
||||
storyCheckpoint: undefined,
|
||||
explorationCheckpoint: undefined,
|
||||
sortiePreparationCheckpoint: undefined,
|
||||
inventory: { ...(current.inventory ?? {}), 콩: 12, 상처약: 6, 탁주: 4 },
|
||||
selectedSortieUnitIds: seed.selectedSortieUnitIds,
|
||||
latestBattleId: seed.battleId,
|
||||
@@ -12516,6 +12669,26 @@ async function seedCampaignSave(page, options) {
|
||||
|
||||
window.localStorage.setItem(storageKey, JSON.stringify(next));
|
||||
window.localStorage.setItem(`${storageKey}:slot-1`, JSON.stringify(next));
|
||||
const persisted = JSON.parse(
|
||||
window.localStorage.getItem(
|
||||
`${storageKey}:slot-1`
|
||||
) ?? 'null'
|
||||
);
|
||||
if (
|
||||
persisted?.step !== seed.step ||
|
||||
persisted?.storyCheckpoint ||
|
||||
persisted?.explorationCheckpoint ||
|
||||
persisted?.sortiePreparationCheckpoint ||
|
||||
persisted?.pendingAftermathBattleId ||
|
||||
persisted?.activeCityStayId ||
|
||||
persisted?.activeCampVisitId
|
||||
) {
|
||||
throw new Error(
|
||||
`Camp seed retained a navigation checkpoint: ${JSON.stringify(
|
||||
persisted
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}, options);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user