Fix stand-still commands and ally pathing

This commit is contained in:
2026-06-22 23:09:57 +09:00
parent 3de598511c
commit e06b8dab58
2 changed files with 95 additions and 5 deletions

View File

@@ -105,6 +105,76 @@ try {
throw new Error('Expected all unit action textures to be loaded.');
}
const movementBlockingState = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
const state = window.__HEROS_DEBUG__?.battle();
const liuBei = state?.units?.find((unit) => unit.id === 'liu-bei');
const guanYu = state?.units?.find((unit) => unit.id === 'guan-yu');
const enemy = state?.units?.find((unit) => unit.faction === 'enemy');
if (!scene || !liuBei || !guanYu || !enemy) {
throw new Error('Expected battle scene and movement test units.');
}
return {
allyBlocksPath: scene.isMovementBlocked(liuBei, guanYu.x, guanYu.y),
enemyBlocksPath: scene.isMovementBlocked(liuBei, enemy.x, enemy.y),
allyStopTileReachable: scene.reachableTiles(liuBei).some((tile) => tile.x === guanYu.x && tile.y === guanYu.y)
};
});
if (
movementBlockingState.allyBlocksPath ||
!movementBlockingState.enemyBlocksPath ||
movementBlockingState.allyStopTileReachable
) {
throw new Error(`Expected allies to be passable path blockers only as stop tiles, and enemies to block movement: ${JSON.stringify(movementBlockingState)}`);
}
const liuBeiScreen = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
const state = window.__HEROS_DEBUG__?.battle();
const liuBei = state?.units?.find((unit) => unit.id === 'liu-bei');
if (!scene || !liuBei) {
throw new Error('Expected Liu Bei and battle scene for stand-still command test.');
}
return {
x: scene.tileCenterX(liuBei.x),
y: scene.tileCenterY(liuBei.y)
};
});
await page.mouse.click(liuBeiScreen.x, liuBeiScreen.y);
await page.waitForTimeout(140);
await page.mouse.click(liuBeiScreen.x, liuBeiScreen.y);
await page.waitForTimeout(180);
const standStillCommandState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
if (
standStillCommandState?.phase !== 'command' ||
standStillCommandState.selectedUnitId !== 'liu-bei' ||
standStillCommandState.pendingMove?.unitId !== 'liu-bei' ||
standStillCommandState.pendingMove.from.x !== standStillCommandState.pendingMove.to.x ||
standStillCommandState.pendingMove.from.y !== standStillCommandState.pendingMove.to.y
) {
throw new Error(`Expected Liu Bei to open commands without moving: ${JSON.stringify(standStillCommandState)}`);
}
await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
if (!scene) {
return;
}
scene.selectedUnit = undefined;
scene.pendingMove = undefined;
scene.targetingAction = undefined;
scene.selectedUsable = undefined;
scene.phase = 'idle';
scene.clearMarkers();
scene.hideCommandMenu();
scene.hideMapMenu();
});
await page.waitForTimeout(700);
await page.mouse.click(260, 300, { button: 'right' });
await page.waitForTimeout(120);

View File

@@ -1106,6 +1106,11 @@ export class BattleScene extends Phaser.Scene {
this.centerCameraOnTile(unit.x, unit.y);
}
if (this.phase === 'moving' && this.selectedUnit?.id === unit.id) {
this.moveSelectedUnit(unit.x, unit.y);
return;
}
if (this.phase === 'targeting' && this.selectedUnit) {
if (unit.hp > 0 && this.targetingAction) {
if (this.selectedUsable && this.selectedUsable.effect !== 'damage') {
@@ -1214,6 +1219,7 @@ export class BattleScene extends Phaser.Scene {
const fromX = unit.x;
const fromY = unit.y;
const stayingInPlace = fromX === x && fromY === y;
unit.x = x;
unit.y = y;
this.pendingMove = { unit, fromX, fromY, toX: x, toY: y };
@@ -1224,7 +1230,9 @@ export class BattleScene extends Phaser.Scene {
const targetX = this.tileCenterX(x);
const targetY = this.tileCenterY(y);
this.moveUnitView(unit, x, y, view);
if (!stayingInPlace) {
this.moveUnitView(unit, x, y, view);
}
this.showCommandMenu(unit, pointer?.x ?? targetX, pointer?.y ?? targetY);
this.renderUnitDetail(unit, '공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.');
}
@@ -1240,9 +1248,10 @@ export class BattleScene extends Phaser.Scene {
{ x: 0, y: 1 },
{ x: 0, y: -1 }
];
const costs = new Map<string, number>([[this.tileKey(unit.x, unit.y), 0]]);
const originKey = this.tileKey(unit.x, unit.y);
const costs = new Map<string, number>([[originKey, 0]]);
const queue = [{ x: unit.x, y: unit.y, cost: 0 }];
const reachable = new Map<string, { x: number; y: number; cost: number }>();
const reachable = new Map<string, { x: number; y: number; cost: number }>([[originKey, { x: unit.x, y: unit.y, cost: 0 }]]);
while (queue.length > 0) {
queue.sort((a, b) => a.cost - b.cost);
@@ -1254,7 +1263,7 @@ export class BattleScene extends Phaser.Scene {
directions.forEach((direction) => {
const nextX = current.x + direction.x;
const nextY = current.y + direction.y;
if (!this.isInBounds(nextX, nextY) || this.isOccupied(nextX, nextY, unit.id)) {
if (!this.isInBounds(nextX, nextY) || this.isMovementBlocked(unit, nextX, nextY)) {
return;
}
@@ -1271,7 +1280,9 @@ export class BattleScene extends Phaser.Scene {
costs.set(key, nextCost);
const tile = { x: nextX, y: nextY, cost: nextCost };
reachable.set(key, tile);
if (!this.isOccupied(nextX, nextY, unit.id)) {
reachable.set(key, tile);
}
queue.push(tile);
});
}
@@ -1300,6 +1311,15 @@ export class BattleScene extends Phaser.Scene {
return battleUnits.some((unit) => unit.id !== exceptUnitId && unit.hp > 0 && unit.x === x && unit.y === y);
}
private isMovementBlocked(unit: UnitData, x: number, y: number) {
return battleUnits.some((other) => {
if (other.id === unit.id || other.hp <= 0 || other.x !== x || other.y !== y) {
return false;
}
return other.faction !== unit.faction;
});
}
private tileCenterX(x: number) {
return this.tileTopLeftX(x) + this.layout.tileSize / 2;
}