Improve final unit move turn prompt

This commit is contained in:
2026-06-24 16:58:39 +09:00
parent 9fae89a55f
commit 3eda3e8d14
2 changed files with 88 additions and 17 deletions

View File

@@ -237,7 +237,7 @@ try {
throw new Error(`Expected positional bond bonuses and reduced counter damage: ${JSON.stringify(combatMechanicsProbe)}`);
}
const moveWaitTurnPromptState = await page.evaluate(() => {
const moveWaitTurnPromptStates = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
const state = window.__HEROS_DEBUG__?.battle();
const lastAlly = state?.units?.find((unit) => unit.id === 'zhang-fei');
@@ -261,11 +261,26 @@ try {
scene.selectUnit(liveLastAlly);
scene.moveSelectedUnit(liveLastAlly.x, liveLastAlly.y);
const postMoveState = window.__HEROS_DEBUG__?.battle();
scene.completeUnitAction(liveLastAlly, 'wait');
return window.__HEROS_DEBUG__?.battle();
const postWaitState = window.__HEROS_DEBUG__?.battle();
return { postMoveState, postWaitState };
});
if (!moveWaitTurnPromptState?.turnPromptVisible || moveWaitTurnPromptState.phase !== 'idle') {
throw new Error(`Expected move-wait completion to show turn-end prompt: ${JSON.stringify(moveWaitTurnPromptState)}`);
if (
!moveWaitTurnPromptStates?.postMoveState?.turnPromptVisible ||
moveWaitTurnPromptStates.postMoveState.turnPromptMode !== 'post-move' ||
moveWaitTurnPromptStates.postMoveState.phase !== 'command' ||
moveWaitTurnPromptStates.postMoveState.pendingMove?.unitId !== 'zhang-fei' ||
moveWaitTurnPromptStates.postMoveState.actedUnitIds?.includes('zhang-fei')
) {
throw new Error(`Expected final ally move to show post-move turn prompt: ${JSON.stringify(moveWaitTurnPromptStates)}`);
}
if (
!moveWaitTurnPromptStates.postWaitState?.turnPromptVisible ||
moveWaitTurnPromptStates.postWaitState.turnPromptMode !== 'turn-end' ||
moveWaitTurnPromptStates.postWaitState.phase !== 'idle'
) {
throw new Error(`Expected move-wait completion to show turn-end prompt: ${JSON.stringify(moveWaitTurnPromptStates)}`);
}
await page.evaluate(() => {

View File

@@ -1015,6 +1015,17 @@ type MapMenuAction = 'endTurn' | 'threat' | 'save' | 'load' | 'roster' | 'bond'
type EnemyAiBehavior = 'aggressive' | 'guard' | 'hold';
type BattleOutcome = 'victory' | 'defeat';
type SaveSlotMode = 'save' | 'load';
type TurnPromptMode = 'turn-end' | 'post-move';
type TurnEndPromptOptions = {
mode?: TurnPromptMode;
title?: string;
body?: string;
primaryLabel?: string;
secondaryLabel?: string;
primaryAction?: () => void;
secondaryAction?: () => void;
};
type BattleSceneData = {
battleId?: string;
@@ -2716,6 +2727,7 @@ export class BattleScene extends Phaser.Scene {
private alertDismissEvent?: Phaser.Time.TimerEvent;
private battleEventObjects: Phaser.GameObjects.GameObject[] = [];
private turnPromptObjects: Phaser.GameObjects.GameObject[] = [];
private turnPromptMode?: TurnPromptMode;
private combatCutInObjects: Phaser.GameObjects.GameObject[] = [];
private resultObjects: Phaser.GameObjects.GameObject[] = [];
private resultGaugeAnimations: ResultGaugeAnimation[] = [];
@@ -3403,8 +3415,38 @@ export class BattleScene extends Phaser.Scene {
this.moveUnitView(unit, x, y, view);
}
this.showCommandMenu(unit, pointer?.x ?? targetX, pointer?.y ?? targetY);
const finalAllyHint = this.remainingAllyCount() === 1 ? '\n마지막 미행동 장수입니다. 대기를 선택하면 턴 종료 확인이 열립니다.' : '';
const isFinalAlly = this.remainingAllyCount() === 1;
const finalAllyHint = isFinalAlly ? '\n마지막 미행동 장수입니다. 바로 턴 종료 확인을 선택할 수 있습니다.' : '';
this.renderUnitDetail(unit, `공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.${finalAllyHint}`);
if (isFinalAlly) {
this.showPostMoveTurnEndPrompt(unit);
return;
}
this.hideTurnEndPrompt();
}
private showPostMoveTurnEndPrompt(unit: UnitData) {
this.showTurnEndPrompt(undefined, {
mode: 'post-move',
title: '마지막 장수 이동 완료',
body: '대기로 행동을 마치고 턴을 종료할까요?',
primaryLabel: '대기 후 턴 종료',
secondaryLabel: '명령 선택',
primaryAction: () => {
if (this.phase !== 'command' || this.selectedUnit?.id !== unit.id || this.actedUnitIds.has(unit.id)) {
this.hideTurnEndPrompt();
return;
}
this.completeUnitAction(unit, 'wait');
if (this.activeFaction === 'ally' && !this.battleOutcome) {
this.endAllyTurn();
}
},
secondaryAction: () => this.hideTurnEndPrompt()
});
}
private canMoveTo(unit: UnitData, x: number, y: number) {
@@ -3768,6 +3810,7 @@ export class BattleScene extends Phaser.Scene {
return;
}
if (choicePointer.leftButtonDown()) {
this.hideTurnEndPrompt();
if (command === 'strategy' || command === 'item') {
this.showUsableMenu(unit, command, choicePointer.x, choicePointer.y);
return;
@@ -4013,6 +4056,7 @@ export class BattleScene extends Phaser.Scene {
return;
}
this.hideTurnEndPrompt();
this.phase = 'targeting';
this.targetingAction = action;
this.selectedUsable = usable;
@@ -4041,6 +4085,7 @@ export class BattleScene extends Phaser.Scene {
return;
}
this.hideTurnEndPrompt();
this.phase = 'targeting';
this.targetingAction = usable.command;
this.selectedUsable = usable;
@@ -8014,6 +8059,7 @@ export class BattleScene extends Phaser.Scene {
this.selectedUnit = unit;
this.pendingMove = undefined;
this.hideCommandMenu();
this.hideTurnEndPrompt();
this.updateMiniMap();
soundDirector.playSelect();
@@ -8396,11 +8442,12 @@ export class BattleScene extends Phaser.Scene {
});
}
private showTurnEndPrompt(message?: string) {
private showTurnEndPrompt(message?: string, options: TurnEndPromptOptions = {}) {
this.hideTurnEndPrompt();
this.turnPromptMode = options.mode ?? 'turn-end';
const width = 360;
const height = 156;
const width = 388;
const height = 162;
const left = this.layout.mapX + this.layout.mapWidth / 2 - width / 2;
const top = this.layout.mapY + this.layout.mapHeight / 2 - height / 2;
const panel = this.add.rectangle(left, top, width, height, 0x101821, 0.96);
@@ -8409,7 +8456,7 @@ export class BattleScene extends Phaser.Scene {
panel.setStrokeStyle(2, palette.gold, 0.9);
this.turnPromptObjects.push(panel);
const title = this.add.text(left + width / 2, top + 26, '모든 아군의 행동이 종료되었습니다.', {
const title = this.add.text(left + width / 2, top + 26, options.title ?? '모든 아군의 행동이 종료되었습니다.', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '20px',
color: '#f2e3bf',
@@ -8419,31 +8466,38 @@ export class BattleScene extends Phaser.Scene {
title.setDepth(41);
this.turnPromptObjects.push(title);
const body = this.add.text(left + width / 2, top + 58, '턴을 종료하시겠습니까?', {
const body = this.add.text(left + width / 2, top + 58, options.body ?? '턴을 종료하시겠습니까?', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '17px',
color: '#d4dce6'
color: '#d4dce6',
align: 'center',
wordWrap: { width: width - 44, useAdvancedWrap: true }
});
body.setOrigin(0.5);
body.setDepth(41);
this.turnPromptObjects.push(body);
const primaryLabel = options.primaryLabel ?? '턴 종료';
const secondaryLabel = options.secondaryLabel ?? '전장 확인';
const primaryAction = options.primaryAction ?? (() => this.endAllyTurn());
const secondaryAction = options.secondaryAction ?? (() => this.hideTurnEndPrompt());
const buttonWidth = 136;
const buttons = [
{ label: '턴 종료', x: left + 106, action: () => this.endAllyTurn() },
{ label: '전장 확인', x: left + 254, action: () => this.hideTurnEndPrompt() }
{ label: primaryLabel, x: left + 119, action: primaryAction, primary: true },
{ label: secondaryLabel, x: left + 269, action: secondaryAction, primary: false }
];
buttons.forEach(({ label, x, action }) => {
const bg = this.add.rectangle(x, top + 112, 116, 36, 0x1a2630, 0.95);
buttons.forEach(({ label, x, action, primary }) => {
const bg = this.add.rectangle(x, top + 116, buttonWidth, 36, 0x1a2630, 0.95);
bg.setDepth(41);
bg.setStrokeStyle(1, label === '턴 종료' ? palette.gold : palette.blue, 0.72);
bg.setStrokeStyle(1, primary ? palette.gold : palette.blue, 0.72);
bg.setInteractive({ useHandCursor: true });
bg.on('pointerover', () => bg.setFillStyle(0x283947, 0.98));
bg.on('pointerout', () => bg.setFillStyle(0x1a2630, 0.95));
bg.on('pointerdown', action);
this.turnPromptObjects.push(bg);
const text = this.add.text(x, top + 112, label, {
const text = this.add.text(x, top + 116, label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '16px',
color: '#f2e3bf',
@@ -8462,6 +8516,7 @@ export class BattleScene extends Phaser.Scene {
private hideTurnEndPrompt() {
this.turnPromptObjects.forEach((object) => object.destroy());
this.turnPromptObjects = [];
this.turnPromptMode = undefined;
}
private pushBattleLog(message: string) {
@@ -9180,6 +9235,7 @@ export class BattleScene extends Phaser.Scene {
mapBackgroundReady: this.mapBackground?.texture.key === battleScenario.mapTextureKey,
resultVisible: this.resultObjects.length > 0,
turnPromptVisible: this.turnPromptObjects.length > 0,
turnPromptMode: this.turnPromptMode ?? null,
markerCount: this.markers.length,
threatMarkerCount: this.markers.filter((marker) => marker.getData('markerType') === 'threat').length,
enemyMoveMarkerCount: this.markers.filter((marker) => marker.getData('markerType') === 'enemy-move').length,