From 0a8751afa51e470ef22787e55c1709ea7b1c545f Mon Sep 17 00:00:00 2001 From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:22:03 -0400 Subject: [PATCH 1/4] Fixed bug --- src/core/execution/WarshipExecution.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index bd29e55211..fdb7fec501 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -240,7 +240,7 @@ export class WarshipExecution implements Execution { ); // Trade-ship-specific state, lazily computed. - let hasPort: boolean | undefined; + let hasReachablePort: boolean | undefined; let patrolTile: number | undefined; let patrolRangeSquared: number | undefined; let warshipComponent: number | null | undefined = undefined; @@ -264,13 +264,23 @@ export class WarshipExecution implements Execution { const type = unit.type(); if (includeTradeShips && type === UnitType.TradeShip) { - if (hasPort === undefined) { - hasPort = owner.unitCount(UnitType.Port) > 0; + if (warshipComponent === undefined) { + warshipComponent = mg.getWaterComponent(this.warship.tile()); + } + if (hasReachablePort === undefined && warshipComponent !== null) { + hasReachablePort = false; + for (const port of owner.units(UnitType.Port)) { + hasReachablePort = mg.hasWaterComponent( + port.tile(), + warshipComponent, + ); + if (hasReachablePort) break; + } patrolTile = this.warship.warshipState().patrolTile; patrolRangeSquared = config.warshipPatrolRange() ** 2; } if ( - !hasPort || + !hasReachablePort || patrolTile === undefined || unit.isSafeFromPirates() || unit.targetUnit()?.owner() === owner || @@ -278,9 +288,6 @@ export class WarshipExecution implements Execution { ) { continue; } - if (warshipComponent === undefined) { - warshipComponent = mg.getWaterComponent(this.warship.tile()); - } if ( warshipComponent !== null && !mg.hasWaterComponent(unit.tile(), warshipComponent) From 8448f003f6cbc1e98c41859d6daa0b5a7d70aae6 Mon Sep 17 00:00:00 2001 From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:11:29 -0400 Subject: [PATCH 2/4] Added tests --- tests/Warship.test.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index ea2dcd0511..d70c9eea18 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -119,6 +119,41 @@ describe("Warship", () => { expect(tradeShip.owner().id()).toBe(player2.id()); }); + test("Warship doesn't capture trade if there is no port on it's water component", async () => { + const portTile = game.ref(coastX, 10); + player1.buildUnit(UnitType.Port, portTile, {}); + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 11), + { + patrolTile: game.ref(coastX + 1, 11), + }, + ); + game.addExecution(new WarshipExecution(warship)); + + const tradeShip = player2.buildUnit( + UnitType.TradeShip, + game.ref(coastX + 1, 11), + { + targetUnit: player1.buildUnit(UnitType.Port, game.ref(coastX, 11), {}), + }, + ); + + const warshipTile = warship.tile(); + vi.spyOn(game, "getWaterComponent").mockImplementation((tile) => + tile === warshipTile ? 1 : 2, + ); + vi.spyOn(game, "hasWaterComponent").mockReturnValue(false); + + expect(tradeShip.owner().id()).toBe(player2.id()); + // Let plenty of time for warship to potentially capture trade ship + for (let i = 0; i < 10; i++) { + game.executeNextTick(); + } + + expect(tradeShip.owner().id()).toBe(player2.id()); + }); + test("Warship does not target trade ships that are safe from pirates", async () => { // build port so warship can target trade ships player1.buildUnit(UnitType.Port, game.ref(coastX, 10), {}); From 8769095b8494127dd22a7e8b02391a33ea362d41 Mon Sep 17 00:00:00 2001 From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:19:48 -0400 Subject: [PATCH 3/4] Made all mocks reset after each test --- tests/Warship.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index d70c9eea18..aa5b72220f 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -12,6 +12,7 @@ import { PathStatus } from "../src/core/pathfinding/types"; import { setup } from "./util/Setup"; import { executeTicks } from "./util/utils"; + const coastX = 7; let game: Game; let player1: Player; @@ -34,7 +35,9 @@ describe("Warship", () => { // Advance past the manualMoveRetreatDisabledDuration window. executeTicks(game, 50); }); - + afterEach(() => { + vi.restoreAllMocks(); + }) test("Warship heals only if player has port", async () => { const maxHealth = game.config().unitInfo(UnitType.Warship).maxHealth; if (typeof maxHealth !== "number") { From 777f728c42915bcd3711136b73047b06279d6ed0 Mon Sep 17 00:00:00 2001 From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:22:40 -0400 Subject: [PATCH 4/4] Formatted --- tests/Warship.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index aa5b72220f..10ec8e2be5 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -12,7 +12,6 @@ import { PathStatus } from "../src/core/pathfinding/types"; import { setup } from "./util/Setup"; import { executeTicks } from "./util/utils"; - const coastX = 7; let game: Game; let player1: Player; @@ -37,7 +36,7 @@ describe("Warship", () => { }); afterEach(() => { vi.restoreAllMocks(); - }) + }); test("Warship heals only if player has port", async () => { const maxHealth = game.config().unitInfo(UnitType.Warship).maxHealth; if (typeof maxHealth !== "number") {