feat: guide early stays and remember player choices
This commit is contained in:
@@ -91,8 +91,70 @@ try {
|
||||
);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await window.__HEROS_DEBUG__.goToCampVisitExploration();
|
||||
await window.__HEROS_DEBUG__.goToCamp();
|
||||
});
|
||||
await page.waitForFunction(
|
||||
({ expectedVisitId }) => {
|
||||
const debug = window.__HEROS_DEBUG__;
|
||||
const camp = debug?.camp?.();
|
||||
const reward =
|
||||
camp?.victoryRewardAcknowledgement;
|
||||
const guided = reward?.actions?.find(
|
||||
(action) => action.id === 'guided'
|
||||
);
|
||||
return (
|
||||
debug?.activeScenes?.().includes('CampScene') &&
|
||||
camp?.campaign?.step === 'first-camp' &&
|
||||
reward?.visible === true &&
|
||||
reward.actions
|
||||
.map((action) => action.id)
|
||||
.join(',') ===
|
||||
'equipment,supplies,guided,sortie,close' &&
|
||||
guided?.primary === true &&
|
||||
guided?.label === '북문 정찰막 둘러보기' &&
|
||||
guided?.interactive === true &&
|
||||
camp?.sortieCommand?.guidedKind === 'visit' &&
|
||||
camp.sortieCommand.targetId === expectedVisitId &&
|
||||
camp.sortieCommand.label === '정찰막 둘러보기' &&
|
||||
camp.sortieCommand.bypass?.label === '바로 출진' &&
|
||||
camp.sortieCommand.bypass?.interactive === true
|
||||
);
|
||||
},
|
||||
{ expectedVisitId: visitId },
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
const guidedCamp = await page.evaluate(() =>
|
||||
window.__HEROS_DEBUG__?.camp?.()
|
||||
);
|
||||
const guidedReward =
|
||||
guidedCamp.victoryRewardAcknowledgement;
|
||||
const guidedAction = guidedReward.actions.find(
|
||||
(action) => action.id === 'guided'
|
||||
);
|
||||
assert.equal(
|
||||
guidedReward.actions.filter((action) => action.primary)
|
||||
.length,
|
||||
1
|
||||
);
|
||||
assert.equal(
|
||||
guidedReward.actions.find(
|
||||
(action) => action.id === 'sortie'
|
||||
)?.label,
|
||||
'바로 출진'
|
||||
);
|
||||
assertBoundsInsideViewport(
|
||||
guidedAction.bounds,
|
||||
'first-camp guided reward action'
|
||||
);
|
||||
await captureStableScreenshot(
|
||||
page,
|
||||
'dist/verification-first-pursuit-camp-guided-reward.png'
|
||||
);
|
||||
await clickNamedSceneBounds(
|
||||
page,
|
||||
'CampScene',
|
||||
guidedAction.bounds
|
||||
);
|
||||
await waitForExplorationReady(page);
|
||||
await page.waitForTimeout(380);
|
||||
|
||||
@@ -269,7 +331,7 @@ try {
|
||||
);
|
||||
},
|
||||
{ key: sceneKey, actorId: 'jian-yong' },
|
||||
{ timeout: 12000 }
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
exploration = await readExploration(page);
|
||||
assert(
|
||||
@@ -502,7 +564,7 @@ try {
|
||||
);
|
||||
},
|
||||
{ key: sceneKey, exitId: 'camp-exit' },
|
||||
{ timeout: 12000 }
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
await page.keyboard.press('e');
|
||||
await waitForCampReturn(page);
|
||||
@@ -517,6 +579,16 @@ try {
|
||||
camp.firstPursuitScoutMemory.activeForNextSortie,
|
||||
true
|
||||
);
|
||||
assert.notEqual(
|
||||
camp.sortieCommand.guidedKind,
|
||||
'visit',
|
||||
'Completing the guided scout visit must advance the primary camp action.'
|
||||
);
|
||||
assert.equal(
|
||||
camp.sortieCommand.bypass,
|
||||
null,
|
||||
'The direct-sortie bypass must collapse once the seed has no pending guided step.'
|
||||
);
|
||||
assertRelevantProgressEqual(
|
||||
savesAfterChoice,
|
||||
await readCampaignSaves(page),
|
||||
@@ -540,7 +612,7 @@ try {
|
||||
console.log(
|
||||
`Verified the first-pursuit camp exploration at ${desktopBrowserViewport.width}x${desktopBrowserViewport.height}, ` +
|
||||
`DPR ${desktopBrowserDeviceScaleFactor}: dedicated Jian Yong SD art, fixed NPC positions, keyboard and pointer movement, ` +
|
||||
'distance-gated interaction, face portraits, two real choices, one-time resonance/item rewards, duplicate protection, ' +
|
||||
'first-victory guided reward routing, distance-gated interaction, face portraits, two real choices, one-time resonance/item rewards, duplicate protection, ' +
|
||||
'CampScene return, and current/slot-1 save persistence.'
|
||||
);
|
||||
} finally {
|
||||
@@ -801,6 +873,37 @@ async function clickSceneBounds(page, bounds) {
|
||||
);
|
||||
}
|
||||
|
||||
async function clickNamedSceneBounds(page, requestedSceneKey, bounds) {
|
||||
const point = await page.evaluate(
|
||||
({ key, requestedBounds }) => {
|
||||
const scene = window.__HEROS_DEBUG__?.scene(key);
|
||||
const canvas = document.querySelector('canvas');
|
||||
const canvasBounds = canvas?.getBoundingClientRect();
|
||||
if (!scene || !canvasBounds) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
x:
|
||||
canvasBounds.left +
|
||||
(requestedBounds.x + requestedBounds.width / 2) *
|
||||
canvasBounds.width /
|
||||
scene.scale.width,
|
||||
y:
|
||||
canvasBounds.top +
|
||||
(requestedBounds.y + requestedBounds.height / 2) *
|
||||
canvasBounds.height /
|
||||
scene.scale.height
|
||||
};
|
||||
},
|
||||
{ key: requestedSceneKey, requestedBounds: bounds }
|
||||
);
|
||||
assert(
|
||||
point && Number.isFinite(point.x) && Number.isFinite(point.y),
|
||||
`Unable to map ${requestedSceneKey} bounds ${JSON.stringify(bounds)}.`
|
||||
);
|
||||
await page.mouse.click(point.x, point.y);
|
||||
}
|
||||
|
||||
async function readCampaignSaves(page) {
|
||||
return page.evaluate(() => {
|
||||
const parse = (key) => {
|
||||
|
||||
Reference in New Issue
Block a user