feat: make guidance recoverable across camp and battle

This commit is contained in:
2026-07-28 05:57:42 +09:00
parent 495f98372c
commit 5f6490b2ae
7 changed files with 1300 additions and 44 deletions

View File

@@ -3372,28 +3372,26 @@ try {
completedFirstVictoryDialogueProbe.state?.campTabs?.find(
(tab) => tab.id === 'dialogue'
)?.newBadgeVisible === true &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.label === '출진 준비' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.guidedKind === 'sortie' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.targetId === null &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.bypass === null &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.label === '전우 기억 되짚기' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.guidedKind === 'dialogue' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.targetId ===
'first-battle-camaraderie-memory' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.bypass?.label === '바로 출진' &&
completedFirstVictoryDialogueProbe.state?.sortieCommand?.bypass?.interactive === true &&
completedFirstVictoryDialogueProbe.remainingRowMarkerCount === 0 &&
firstVictoryDialoguePersisted(completedFirstVictoryDialogueSave.current) &&
firstVictoryDialoguePersisted(completedFirstVictoryDialogueSave.slot1),
`Expected the first-victory choice to persist while the separate camaraderie memory keeps the dialogue NEW state pending: ${JSON.stringify({
`Expected the first-victory choice to persist while the primary command guides the pending, next-battle camaraderie memory without removing the direct-sortie bypass: ${JSON.stringify({
probe: completedFirstVictoryDialogueProbe,
save: completedFirstVictoryDialogueSave,
choiceId: firstVictoryChoice.id
})}`
);
const firstBattleCamaraderieDialogueRow = await readCampDialogueControlPoint(
await clickSceneBounds(
page,
'row',
'first-battle-camaraderie-memory'
);
await page.mouse.click(
firstBattleCamaraderieDialogueRow.x,
firstBattleCamaraderieDialogueRow.y
'CampScene',
completedFirstVictoryDialogueProbe.state.sortieCommand.bounds
);
await page.waitForFunction(
() => (
@@ -3542,6 +3540,10 @@ try {
completedFirstBattleCamaraderieState?.campTabs?.find(
(tab) => tab.id === 'dialogue'
)?.newBadgeVisible === false &&
completedFirstBattleCamaraderieState?.sortieCommand?.label === '전우 조 편성하기' &&
completedFirstBattleCamaraderieState?.sortieCommand?.guidedKind === 'sortie' &&
completedFirstBattleCamaraderieState?.sortieCommand?.targetId === null &&
completedFirstBattleCamaraderieState?.sortieCommand?.bypass === null &&
firstBattleCamaraderiePersisted(completedFirstBattleCamaraderieSave.current) &&
firstBattleCamaraderiePersisted(completedFirstBattleCamaraderieSave.slot1),
`Expected the chosen camaraderie memory to persist in current/slot saves, unlock +5%p, and still avoid auto-selection: ${JSON.stringify({
@@ -4759,6 +4761,30 @@ try {
undefined,
{ timeout: 30000 }
);
const unselectedCamaraderieLaunchWarning = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
if (!scene || typeof scene.startVictoryStory !== 'function') {
return { ready: false };
}
scene.startVictoryStory();
return {
ready: true,
navigationPending: scene.navigationPending ?? false,
notice: (scene.dialogueObjects ?? [])
.filter((object) => object?.type === 'Text' && object.active)
.map((object) => object.text)
.join('\n')
};
});
assert(
unselectedCamaraderieLaunchWarning.ready === true &&
unselectedCamaraderieLaunchWarning.navigationPending === false &&
unselectedCamaraderieLaunchWarning.notice.includes('전우 기억 미지정') &&
unselectedCamaraderieLaunchWarning.notice.includes('유비↔장비') &&
unselectedCamaraderieLaunchWarning.notice.includes('+5%p') &&
unselectedCamaraderieLaunchWarning.notice.includes('출진을 한 번 더'),
`Expected the first launch attempt without the remembered core pair to warn once instead of silently forfeiting its next-battle bonus: ${JSON.stringify(unselectedCamaraderieLaunchWarning)}`
);
const firstSortiePursuitSaveBefore = await readCampaignSave(page);
const firstSortiePursuitMutation = await page.evaluate(() => {
@@ -10678,10 +10704,19 @@ async function openCampSortiePrep(page) {
const rewardVisible = await page.evaluate(
() => window.__HEROS_DEBUG__?.camp?.()?.victoryRewardAcknowledgement?.visible === true
);
const point = rewardVisible
? await readCampVictoryRewardControlPoint(page, 'sortie')
: await readCampStaticTextControlPoint(page, '출진 준비');
await page.mouse.click(point.x, point.y);
if (rewardVisible) {
const point = await readCampVictoryRewardControlPoint(page, 'sortie');
await page.mouse.click(point.x, point.y);
} else {
const command = await page.evaluate(
() => window.__HEROS_DEBUG__?.camp?.()?.sortieCommand ?? null
);
assert(
command?.interactive === true && isFiniteBounds(command.bounds),
`Expected a visible interactive camp sortie command: ${JSON.stringify(command)}`
);
await clickSceneBounds(page, 'CampScene', command.bounds);
}
await waitForSortiePrep(page);
}