From da7897ed77dea235c7bb55f0ded693b576683020 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Wed, 29 Jul 2026 13:02:00 +0900 Subject: [PATCH] fix: isolate battle save modal input --- scripts/verify-interaction-ux.mjs | 38 ++++++++++++++++++++++++++++--- src/game/scenes/BattleScene.ts | 27 +++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/scripts/verify-interaction-ux.mjs b/scripts/verify-interaction-ux.mjs index d9a041f..15a0340 100644 --- a/scripts/verify-interaction-ux.mjs +++ b/scripts/verify-interaction-ux.mjs @@ -705,7 +705,17 @@ async function verifyBattlePointerFlow(page) { coveredBounds: coveredBounds ? { x: coveredBounds.x, y: coveredBounds.y, width: coveredBounds.width, height: coveredBounds.height } : null, - selectedUnitId: scene.selectedUnit?.id ?? null + persistentActionBounds: persistentActionBounds + ? { + x: persistentActionBounds.x, + y: persistentActionBounds.y, + width: persistentActionBounds.width, + height: persistentActionBounds.height + } + : null, + selectedUnitId: scene.selectedUnit?.id ?? null, + activeFaction: scene.activeFaction, + turnNumber: scene.turnNumber }; function pointInsideBounds(point, bounds) { @@ -716,8 +726,10 @@ async function verifyBattlePointerFlow(page) { } }); assert( - battleModalProbe.blocker && battleModalProbe.coveredBounds, - `Expected a full-screen battle save blocker and an interactive control behind it: ${JSON.stringify(battleModalProbe)}` + battleModalProbe.blocker && + battleModalProbe.coveredBounds && + battleModalProbe.persistentActionBounds, + `Expected a full-screen battle save blocker, an interactive control, and the persistent turn-end action behind it: ${JSON.stringify(battleModalProbe)}` ); await clickSceneBounds(page, 'BattleScene', battleModalProbe.coveredBounds); await page.waitForTimeout(120); @@ -735,6 +747,26 @@ async function verifyBattlePointerFlow(page) { } })}` ); + await clickSceneBounds(page, 'BattleScene', battleModalProbe.persistentActionBounds); + await page.waitForTimeout(120); + const battleAfterCoveredTurnEndClick = await page.evaluate(() => window.__HEROS_DEBUG__?.battle()); + assert( + battleAfterCoveredTurnEndClick?.saveSlotPanelMode === 'save' && + battleAfterCoveredTurnEndClick?.selectedUnitId === battleModalProbe.selectedUnitId && + battleAfterCoveredTurnEndClick?.turnPromptVisible === false && + battleAfterCoveredTurnEndClick?.activeFaction === battleModalProbe.activeFaction && + battleAfterCoveredTurnEndClick?.turnNumber === battleModalProbe.turnNumber, + `Battle save modal allowed the persistent turn-end action behind it: ${JSON.stringify({ + before: battleModalProbe, + after: { + saveSlotPanelMode: battleAfterCoveredTurnEndClick?.saveSlotPanelMode, + selectedUnitId: battleAfterCoveredTurnEndClick?.selectedUnitId, + turnPromptVisible: battleAfterCoveredTurnEndClick?.turnPromptVisible, + activeFaction: battleAfterCoveredTurnEndClick?.activeFaction, + turnNumber: battleAfterCoveredTurnEndClick?.turnNumber + } + })}` + ); await verifyEarlyTurnEndSafety(page); } diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index dd213e1..8156412 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -4541,6 +4541,7 @@ export class BattleScene extends Phaser.Scene { ambienceKey: presentationSoundscape?.ambienceKey ?? environmentProfile?.soundscape.ambienceKey, ambienceVolume: presentationSoundscape?.ambienceVolume ?? environmentProfile?.soundscape.ambienceVolume }); + this.input.setTopOnly(true); this.input.mouse?.disableContextMenu(); this.installBattleInputHandlers(); this.installBattleAutosaveHandlers(); @@ -22868,6 +22869,15 @@ export class BattleScene extends Phaser.Scene { shade.setOrigin(0); shade.setDepth(depth - 1); shade.setInteractive(); + shade.on( + 'pointerdown', + ( + _pointer: Phaser.Input.Pointer, + _localX: number, + _localY: number, + event?: { stopPropagation: () => void } + ) => event?.stopPropagation() + ); this.saveSlotPanelObjects.push(shade); const panel = this.add.rectangle(left, top, width, height, 0x0f1720, 0.98); @@ -22875,6 +22885,15 @@ export class BattleScene extends Phaser.Scene { panel.setDepth(depth); panel.setStrokeStyle(this.battleUiLength(2), mode === 'save' ? palette.gold : palette.blue, 0.9); panel.setInteractive(); + panel.on( + 'pointerdown', + ( + _pointer: Phaser.Input.Pointer, + _localX: number, + _localY: number, + event?: { stopPropagation: () => void } + ) => event?.stopPropagation() + ); this.saveSlotPanelObjects.push(panel); const title = this.add.text(left + this.battleUiLength(22), top + this.battleUiLength(18), mode === 'save' ? '전투 수동 저장' : '수동 기록 불러오기', { @@ -31683,7 +31702,13 @@ export class BattleScene extends Phaser.Scene { text.setDepth(33); const activate = () => { - if (this.activeFaction !== 'ally' || this.battleOutcome || this.remainingAllyCount() > 0) { + if ( + this.saveSlotPanelObjects.length > 0 || + this.turnPromptObjects.length > 0 || + this.activeFaction !== 'ally' || + this.battleOutcome || + this.remainingAllyCount() > 0 + ) { return; } this.suppressNextLeftClick = true;