feat: align campaign audiovisual story flow

This commit is contained in:
2026-07-23 02:14:59 +09:00
parent 8bda36d46d
commit a9e401aedf
18 changed files with 1405 additions and 243 deletions

View File

@@ -1668,17 +1668,28 @@ async function verifyBattleEnvironment(page, battleId) {
const environment = state?.environment;
const expected = battleEnvironmentExpectations.get(battleId);
const actualAmbienceKey = state?.audio?.currentAmbienceKey ?? state?.audio?.pendingAmbienceKey ?? null;
const actualMusicKey = state?.audio?.currentMusicKey ?? state?.audio?.pendingMusicKey ?? null;
if (!expected) {
if (
environment?.profileId !== null ||
!environment?.presentationArcId ||
environment?.profileId !== `arc-${environment.presentationArcId}` ||
environment?.effect !== 'arc-wash' ||
environment?.musicKey !== actualMusicKey ||
actualAmbienceKey !== null ||
environment?.active !== false ||
environment?.objectCount !== 0 ||
environment?.ambienceKey !== null ||
environment?.active !== true ||
environment?.objectCount !== 1 ||
environment?.maskedObjectCount !== 1 ||
environment?.masked !== true ||
environment?.tintCount !== 1 ||
!(environment?.tintAlpha > 0 && environment.tintAlpha <= 0.08) ||
environment?.particleCount !== 0 ||
environment?.hazeCount !== 0
) {
throw new Error(`Unexpected battle environment for ${battleId}: ${JSON.stringify(environment)}`);
throw new Error(
`Campaign arc wash mismatch for ${battleId}: ${JSON.stringify({ actualMusicKey, actualAmbienceKey, environment })}`
);
}
return;
}
@@ -1689,6 +1700,7 @@ async function verifyBattleEnvironment(page, battleId) {
Math.abs(depthRange.maximum - expected.depthRange[1]) < 0.001;
if (
environment?.profileId !== expected.profileId ||
environment?.musicKey !== actualMusicKey ||
environment?.ambienceKey !== expected.ambienceKey ||
actualAmbienceKey !== expected.ambienceKey ||
environment?.active !== true ||
@@ -1700,7 +1712,7 @@ async function verifyBattleEnvironment(page, battleId) {
!depthMatches
) {
throw new Error(
`Battle environment mismatch for ${battleId}: ${JSON.stringify({ expected, actualAmbienceKey, environment })}`
`Battle environment mismatch for ${battleId}: ${JSON.stringify({ expected, actualMusicKey, actualAmbienceKey, environment })}`
);
}
}
@@ -1819,7 +1831,7 @@ async function enterCampaignCampFromBattleResult(page, battle, route) {
settlement = await page.evaluate(() => window.__HEROS_DEBUG__?.battle()?.resultSettlement);
}
const expectedDestination = battle.no === 1 ? 'victory-story' : 'camp';
const expectedDestination = 'victory-story';
if (settlement?.cta?.destination !== expectedDestination) {
throw new Error(
`Battle ${battle.no} result continuation mismatch: ${JSON.stringify({ settlement, expectedDestination })}`
@@ -1829,22 +1841,7 @@ async function enterCampaignCampFromBattleResult(page, battle, route) {
await page.mouse.click(continuePoint.x, continuePoint.y);
console.log(`QA intermission ${battle.no}: continuing to ${expectedDestination}`);
if (expectedDestination === 'victory-story') {
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
return activeScenes.includes('StoryScene') || activeScenes.includes('CampScene');
}, undefined, { timeout: 30000 });
for (let index = 0; index < 24; index += 1) {
const reachedCamp = await page.evaluate(() => (
window.__HEROS_DEBUG__?.activeScenes()?.includes('CampScene') ?? false
));
if (reachedCamp) {
break;
}
await page.evaluate(() => window.__HEROS_DEBUG__?.scene('StoryScene')?.advance?.());
await page.waitForTimeout(260);
}
}
await advancePostBattleStoryToCamp(page);
console.log(`QA intermission ${battle.no}: waiting for the reward camp`);
await page.waitForFunction(
@@ -1900,12 +1897,13 @@ async function enterFinalCampFromBattleResult(page) {
);
settlement = await page.evaluate(() => window.__HEROS_DEBUG__?.battle()?.resultSettlement);
}
if (settlement?.cta?.destination !== 'camp') {
throw new Error(`Final victory must continue to the final camp before the epilogue: ${JSON.stringify(settlement)}`);
if (settlement?.cta?.destination !== 'victory-story') {
throw new Error(`Final victory must continue through its aftermath before the final camp: ${JSON.stringify(settlement)}`);
}
const continuePoint = await readBattleResultCtaPoint(page);
await page.mouse.click(continuePoint.x, continuePoint.y);
await advancePostBattleStoryToCamp(page);
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const camp = window.__HEROS_DEBUG__?.camp?.();
@@ -1946,6 +1944,26 @@ async function enterFinalCampFromBattleResult(page) {
};
}
async function advancePostBattleStoryToCamp(page) {
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
return activeScenes.includes('StoryScene') || activeScenes.includes('CampScene');
}, undefined, { timeout: 30000 });
for (let index = 0; index < 64; index += 1) {
const reachedCamp = await page.evaluate(() => (
window.__HEROS_DEBUG__?.activeScenes()?.includes('CampScene') ?? false
));
if (reachedCamp) {
return;
}
await page.evaluate(() => window.__HEROS_DEBUG__?.scene('StoryScene')?.advance?.());
await page.waitForTimeout(260);
}
throw new Error('Post-battle aftermath did not reach CampScene within 64 story advances.');
}
async function readBattleResultCtaPoint(page) {
const point = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');