Fixed pirating not being disabled when a warship's owner has no port in that body of water (#4458)

> **Before opening a PR:** discuss new features on
[Discord](https://discord.gg/K9zernJB5z) first, and file bugs or small
improvements as
[issues](https://github.com/openfrontio/OpenFrontIO/issues/new/choose).
You must be assigned to an `approved` issue — unsolicited PRs will be
auto-closed.

**Add approved & assigned issue number here:**

Resolves #4291 

## Description:

Fixes pirating not being disabled when a warship's owner has no port in
that body of water

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tktk1234567
This commit is contained in:
TKTK123456
2026-07-10 12:31:21 -04:00
committed by GitHub
parent 8e5f27949f
commit 6a6c142388
2 changed files with 53 additions and 14 deletions
+15 -13
View File
@@ -248,7 +248,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;
@@ -272,13 +272,24 @@ 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());
hasReachablePort =
warshipComponent !== null &&
owner
.units(UnitType.Port)
.some(
(port) =>
port.isActive() &&
!port.isMarkedForDeletion() &&
!port.isUnderConstruction() &&
mg.hasWaterComponent(port.tile(), warshipComponent!),
);
patrolTile = this.warship.warshipState().patrolTile;
patrolRangeSquared = config.warshipPatrolRange() ** 2;
}
if (
!hasPort ||
!hasReachablePort ||
patrolTile === undefined ||
unit.isSafeFromPirates() ||
unit.targetUnit()?.owner() === owner ||
@@ -286,15 +297,6 @@ export class WarshipExecution implements Execution {
) {
continue;
}
if (warshipComponent === undefined) {
warshipComponent = mg.getWaterComponent(this.warship.tile());
}
if (
warshipComponent !== null &&
!mg.hasWaterComponent(unit.tile(), warshipComponent)
) {
continue;
}
if (
mg.euclideanDistSquared(patrolTile, unit.tile()) > patrolRangeSquared!
) {
+38 -1
View File
@@ -34,7 +34,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") {
@@ -158,6 +160,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), {});