fix: prevent dialogue input rebound
This commit is contained in:
@@ -748,6 +748,10 @@ async function openResonanceChoice(page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openShop(page) {
|
async function openShop(page) {
|
||||||
|
await waitForCityInteractionIdle(
|
||||||
|
page,
|
||||||
|
fixture.marketActorId
|
||||||
|
);
|
||||||
let interacted = false;
|
let interacted = false;
|
||||||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||||||
interacted = await page.evaluate((actorId) =>
|
interacted = await page.evaluate((actorId) =>
|
||||||
@@ -761,10 +765,18 @@ async function openShop(page) {
|
|||||||
}
|
}
|
||||||
await page.waitForTimeout(120);
|
await page.waitForTimeout(120);
|
||||||
}
|
}
|
||||||
|
const diagnostic = interacted
|
||||||
|
? null
|
||||||
|
: await readCityInteractionDiagnostic(
|
||||||
|
page,
|
||||||
|
fixture.marketActorId
|
||||||
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
interacted,
|
interacted,
|
||||||
true,
|
true,
|
||||||
`${renderer}: could not open the city shop.`
|
`${renderer}: could not open the city shop: ${JSON.stringify(
|
||||||
|
diagnostic
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
await waitForShopOpen(page);
|
await waitForShopOpen(page);
|
||||||
const city = await readCity(page);
|
const city = await readCity(page);
|
||||||
@@ -864,17 +876,115 @@ function assertDialogue(
|
|||||||
async function finishCityDialogue(page) {
|
async function finishCityDialogue(page) {
|
||||||
for (let attempt = 0; attempt < 12; attempt += 1) {
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
||||||
const city = await readCity(page);
|
const city = await readCity(page);
|
||||||
if (!city?.dialogue?.active) {
|
if (
|
||||||
|
city?.ready === true &&
|
||||||
|
city.dialogue?.active === false
|
||||||
|
) {
|
||||||
|
await waitForCityInteractionIdle(page);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (city?.dialogue?.active === true) {
|
||||||
await page.waitForTimeout(150);
|
await page.waitForTimeout(150);
|
||||||
await page.keyboard.press('e');
|
await page.keyboard.press('e');
|
||||||
|
} else {
|
||||||
|
await page.waitForTimeout(150);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${renderer}: city dialogue did not finish.`
|
`${renderer}: city dialogue did not finish.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForCityInteractionIdle(
|
||||||
|
page,
|
||||||
|
targetActorId
|
||||||
|
) {
|
||||||
|
const expected = { targetActorId };
|
||||||
|
const waitForIdle = () =>
|
||||||
|
page.waitForFunction(
|
||||||
|
({ targetActorId: actorId }) => {
|
||||||
|
const debug = window.__HEROS_DEBUG__;
|
||||||
|
const city = debug?.cityStay?.();
|
||||||
|
return (
|
||||||
|
debug
|
||||||
|
?.activeScenes?.()
|
||||||
|
.includes('CityStayScene') === true &&
|
||||||
|
city?.scene === 'CityStayScene' &&
|
||||||
|
city.ready === true &&
|
||||||
|
city.navigationPending === false &&
|
||||||
|
city.dialogue?.active === false &&
|
||||||
|
city.shop?.open === false &&
|
||||||
|
city.choice?.open === false &&
|
||||||
|
city.campaignObjectiveJournal?.view?.visible ===
|
||||||
|
false &&
|
||||||
|
(!actorId ||
|
||||||
|
city.actors?.some(
|
||||||
|
({ id }) => id === actorId
|
||||||
|
))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
expected,
|
||||||
|
{ timeout: 30000 }
|
||||||
|
);
|
||||||
|
await waitForIdle();
|
||||||
|
await afterTwoFrames(page);
|
||||||
|
const stable = await readCityInteractionDiagnostic(
|
||||||
|
page,
|
||||||
|
targetActorId
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
stable.idle,
|
||||||
|
true,
|
||||||
|
`${renderer}: city interaction became blocked after reaching idle; the closing key may have reopened a modal: ${JSON.stringify(
|
||||||
|
stable
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readCityInteractionDiagnostic(
|
||||||
|
page,
|
||||||
|
targetActorId
|
||||||
|
) {
|
||||||
|
return page.evaluate((actorId) => {
|
||||||
|
const debug = window.__HEROS_DEBUG__;
|
||||||
|
const scene = debug?.scene('CityStayScene');
|
||||||
|
const city = debug?.cityStay?.() ?? null;
|
||||||
|
const activeScenes = debug?.activeScenes?.() ?? [];
|
||||||
|
const actor =
|
||||||
|
city?.actors?.find(({ id }) => id === actorId) ??
|
||||||
|
null;
|
||||||
|
const objectiveJournalOpen =
|
||||||
|
city?.campaignObjectiveJournal?.view?.visible ??
|
||||||
|
null;
|
||||||
|
const idle =
|
||||||
|
activeScenes.includes('CityStayScene') &&
|
||||||
|
city?.scene === 'CityStayScene' &&
|
||||||
|
city.ready === true &&
|
||||||
|
city.navigationPending === false &&
|
||||||
|
city.dialogue?.active === false &&
|
||||||
|
city.shop?.open === false &&
|
||||||
|
city.choice?.open === false &&
|
||||||
|
objectiveJournalOpen === false &&
|
||||||
|
(!actorId || Boolean(actor));
|
||||||
|
return {
|
||||||
|
idle,
|
||||||
|
activeScenes,
|
||||||
|
sceneActive:
|
||||||
|
scene?.scene?.isActive?.() ?? false,
|
||||||
|
scenePaused:
|
||||||
|
scene?.scene?.isPaused?.() ?? false,
|
||||||
|
ready: city?.ready ?? null,
|
||||||
|
navigationPending:
|
||||||
|
city?.navigationPending ?? null,
|
||||||
|
dialogue: city?.dialogue ?? null,
|
||||||
|
shopOpen: city?.shop?.open ?? null,
|
||||||
|
choiceOpen: city?.choice?.open ?? null,
|
||||||
|
objectiveJournalOpen,
|
||||||
|
actor
|
||||||
|
};
|
||||||
|
}, targetActorId);
|
||||||
|
}
|
||||||
|
|
||||||
async function finishCityDialogueToCamp(page) {
|
async function finishCityDialogueToCamp(page) {
|
||||||
for (let attempt = 0; attempt < 12; attempt += 1) {
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
||||||
const activeScenes = await page.evaluate(
|
const activeScenes = await page.evaluate(
|
||||||
@@ -1229,7 +1339,7 @@ async function ensureLocalServer(url) {
|
|||||||
}
|
}
|
||||||
await delay(250);
|
await delay(250);
|
||||||
}
|
}
|
||||||
child.kill();
|
await stopServerProcess(child);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Vite server did not start at ${url}\n${stderr.join('')}`
|
`Vite server did not start at ${url}\n${stderr.join('')}`
|
||||||
);
|
);
|
||||||
@@ -1247,11 +1357,25 @@ async function canReach(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function stopServerProcess(child) {
|
async function stopServerProcess(child) {
|
||||||
if (!child || child.killed) {
|
if (
|
||||||
|
!child ||
|
||||||
|
child.exitCode !== null ||
|
||||||
|
child.signalCode !== null
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const exited = new Promise((resolve) =>
|
||||||
|
child.once('exit', resolve)
|
||||||
|
);
|
||||||
child.kill();
|
child.kill();
|
||||||
await delay(80);
|
await Promise.race([exited, delay(5000)]);
|
||||||
|
if (
|
||||||
|
child.exitCode === null &&
|
||||||
|
child.signalCode === null
|
||||||
|
) {
|
||||||
|
child.kill('SIGKILL');
|
||||||
|
await Promise.race([exited, delay(2000)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function delay(ms) {
|
function delay(ms) {
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ export class CityStayScene extends Phaser.Scene {
|
|||||||
private navigationPending = false;
|
private navigationPending = false;
|
||||||
private inputReadyAt = Number.POSITIVE_INFINITY;
|
private inputReadyAt = Number.POSITIVE_INFINITY;
|
||||||
private autoDialogueInputReadyAt = 0;
|
private autoDialogueInputReadyAt = 0;
|
||||||
|
private interactionInputReadyAt = 0;
|
||||||
private stepIndex = 0;
|
private stepIndex = 0;
|
||||||
private lastStepAt = 0;
|
private lastStepAt = 0;
|
||||||
private lastNotice = '';
|
private lastNotice = '';
|
||||||
@@ -367,6 +368,9 @@ export class CityStayScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.consumeInteractionRequest()) {
|
if (this.consumeInteractionRequest()) {
|
||||||
|
if (time < this.interactionInputReadyAt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.tryInteract();
|
this.tryInteract();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -677,6 +681,7 @@ export class CityStayScene extends Phaser.Scene {
|
|||||||
this.navigationPending = false;
|
this.navigationPending = false;
|
||||||
this.inputReadyAt = Number.POSITIVE_INFINITY;
|
this.inputReadyAt = Number.POSITIVE_INFINITY;
|
||||||
this.autoDialogueInputReadyAt = 0;
|
this.autoDialogueInputReadyAt = 0;
|
||||||
|
this.interactionInputReadyAt = 0;
|
||||||
this.explorationInput = undefined;
|
this.explorationInput = undefined;
|
||||||
this.stepIndex = 0;
|
this.stepIndex = 0;
|
||||||
this.lastStepAt = 0;
|
this.lastStepAt = 0;
|
||||||
@@ -3220,6 +3225,9 @@ export class CityStayScene extends Phaser.Scene {
|
|||||||
this.dialogueState = undefined;
|
this.dialogueState = undefined;
|
||||||
this.dialoguePanel?.setVisible(false);
|
this.dialoguePanel?.setVisible(false);
|
||||||
this.stopPlayerMovement();
|
this.stopPlayerMovement();
|
||||||
|
this.interactionInputReadyAt =
|
||||||
|
this.time.now + inputCarryoverGuardMs;
|
||||||
|
this.explorationInput?.discardInteractionRequest();
|
||||||
this.restoreActorDirection(sourceTargetId);
|
this.restoreActorDirection(sourceTargetId);
|
||||||
const checkpointCommitted = onComplete?.() === true;
|
const checkpointCommitted = onComplete?.() === true;
|
||||||
if (!checkpointCommitted) {
|
if (!checkpointCommitted) {
|
||||||
@@ -3233,6 +3241,9 @@ export class CityStayScene extends Phaser.Scene {
|
|||||||
this.dialogueState = undefined;
|
this.dialogueState = undefined;
|
||||||
this.dialoguePanel?.setVisible(false);
|
this.dialoguePanel?.setVisible(false);
|
||||||
this.stopPlayerMovement();
|
this.stopPlayerMovement();
|
||||||
|
this.interactionInputReadyAt =
|
||||||
|
this.time.now + inputCarryoverGuardMs;
|
||||||
|
this.explorationInput?.discardInteractionRequest();
|
||||||
this.restoreActorDirection(sourceTargetId);
|
this.restoreActorDirection(sourceTargetId);
|
||||||
this.persistExplorationCheckpoint(true);
|
this.persistExplorationCheckpoint(true);
|
||||||
this.refreshInteractionPrompt();
|
this.refreshInteractionPrompt();
|
||||||
|
|||||||
Reference in New Issue
Block a user