From 7692be294f56dcd7b0e63e6c39e9f5ea672bced0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20B=C3=A9lisle?= Date: Wed, 3 Jun 2026 21:52:36 +0200 Subject: [PATCH 1/4] Fix warship freezing when PatrolTile's proximity is unreachable from current water component - Warship rejects new PatrolTile when in a different water component --- src/core/execution/MoveWarshipExecution.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/execution/MoveWarshipExecution.ts b/src/core/execution/MoveWarshipExecution.ts index fb21d8858b..afe36b05ea 100644 --- a/src/core/execution/MoveWarshipExecution.ts +++ b/src/core/execution/MoveWarshipExecution.ts @@ -13,6 +13,12 @@ export class MoveWarshipExecution implements Execution { console.warn(`MoveWarshipExecution: position ${this.position} not valid`); return; } + // Get water component of new TargetTile for connectivity check + const newPatrolTileWaterComponent = mg.getWaterComponent(this.position); + if (newPatrolTileWaterComponent === null) { + console.warn(`MoveWarshipExecution: position ${this.position} not water`); + return; + } // Cache warship list and build a lookup map — avoids repeated iteration const warshipMap = new Map( this.owner.units(UnitType.Warship).map((u) => [u.id(), u]), @@ -28,6 +34,10 @@ export class MoveWarshipExecution implements Execution { console.warn(`MoveWarshipExecution: warship ${unitId} is not active`); continue; } + // Do not update the warship's patrolTile if it is in a different Water Component + if (!mg.hasWaterComponent(warship.tile(), newPatrolTileWaterComponent)) { + continue; + } warship.updateWarshipState({ patrolTile: this.position, }); From a7f46ef077de63ea1a9e91d6b55458a2b094604b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20B=C3=A9lisle?= Date: Wed, 3 Jun 2026 21:53:59 +0200 Subject: [PATCH 2/4] Add a test verifying that warship rejects new PatrolTile when in a different water component --- tests/Warship.test.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index 8820842815..2dc2b2a813 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -916,3 +916,35 @@ describe("Warship", () => { expect(tradeShip.owner()).toBe(player1); }); }); + +test("Warship doesn't accept a new patrol tile if in a different water component", async () => { + const newPatrolTile = game.ref(coastX + 5, 15); + + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 10), + { + patrolTile: game.ref(coastX + 1, 10), + }, + ); + + game.addExecution(new WarshipExecution(warship)); + + // Mock different water components + game.getWaterComponent = (tile: TileRef) => { + if (tile === newPatrolTile) return 1; + return 2; + }; + + game.hasWaterComponent = (tile: TileRef, component: number) => { + return game.getWaterComponent(tile) === component; + }; + + game.addExecution( + new MoveWarshipExecution(player1, [warship.id()], newPatrolTile), + ); + + executeTicks(game, 10); + + expect(warship.warshipState().patrolTile).toBe(game.ref(coastX + 1, 10)); +}); From bbfced62d5e350813cc9773361a46b5558b60ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20B=C3=A9lisle?= <79760461+Katokoda@users.noreply.github.com> Date: Thu, 4 Jun 2026 08:07:01 +0200 Subject: [PATCH 3/4] Move the new warship test within the Warship-Description as pointed out by CodeRabbitAI --- tests/Warship.test.ts | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index 2dc2b2a813..ea2dcd0511 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -915,36 +915,36 @@ describe("Warship", () => { } expect(tradeShip.owner()).toBe(player1); }); -}); -test("Warship doesn't accept a new patrol tile if in a different water component", async () => { - const newPatrolTile = game.ref(coastX + 5, 15); + test("Warship doesn't accept a new patrol tile if in a different water component", async () => { + const newPatrolTile = game.ref(coastX + 5, 15); - const warship = player1.buildUnit( - UnitType.Warship, - game.ref(coastX + 1, 10), - { - patrolTile: game.ref(coastX + 1, 10), - }, - ); + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 10), + { + patrolTile: game.ref(coastX + 1, 10), + }, + ); - game.addExecution(new WarshipExecution(warship)); + game.addExecution(new WarshipExecution(warship)); - // Mock different water components - game.getWaterComponent = (tile: TileRef) => { - if (tile === newPatrolTile) return 1; - return 2; - }; + // Mock different water components + game.getWaterComponent = (tile: TileRef) => { + if (tile === newPatrolTile) return 1; + return 2; + }; - game.hasWaterComponent = (tile: TileRef, component: number) => { - return game.getWaterComponent(tile) === component; - }; + game.hasWaterComponent = (tile: TileRef, component: number) => { + return game.getWaterComponent(tile) === component; + }; - game.addExecution( - new MoveWarshipExecution(player1, [warship.id()], newPatrolTile), - ); + game.addExecution( + new MoveWarshipExecution(player1, [warship.id()], newPatrolTile), + ); - executeTicks(game, 10); + executeTicks(game, 10); - expect(warship.warshipState().patrolTile).toBe(game.ref(coastX + 1, 10)); + expect(warship.warshipState().patrolTile).toBe(game.ref(coastX + 1, 10)); + }); }); From 926675d67528be5d0c98f5a83d90cf3ab7c42f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20B=C3=A9lisle?= <79760461+Katokoda@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:05:03 +0200 Subject: [PATCH 4/4] Removes redundant test of warship being on water as recommended by evan --- src/core/execution/MoveWarshipExecution.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/execution/MoveWarshipExecution.ts b/src/core/execution/MoveWarshipExecution.ts index afe36b05ea..f8bbc0560a 100644 --- a/src/core/execution/MoveWarshipExecution.ts +++ b/src/core/execution/MoveWarshipExecution.ts @@ -15,10 +15,6 @@ export class MoveWarshipExecution implements Execution { } // Get water component of new TargetTile for connectivity check const newPatrolTileWaterComponent = mg.getWaterComponent(this.position); - if (newPatrolTileWaterComponent === null) { - console.warn(`MoveWarshipExecution: position ${this.position} not water`); - return; - } // Cache warship list and build a lookup map — avoids repeated iteration const warshipMap = new Map( this.owner.units(UnitType.Warship).map((u) => [u.id(), u]), @@ -35,7 +31,7 @@ export class MoveWarshipExecution implements Execution { continue; } // Do not update the warship's patrolTile if it is in a different Water Component - if (!mg.hasWaterComponent(warship.tile(), newPatrolTileWaterComponent)) { + if (!mg.hasWaterComponent(warship.tile(), newPatrolTileWaterComponent!)) { continue; } warship.updateWarshipState({