diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 3504e691c..e24a9ac3b 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -4,6 +4,7 @@ import { Game, MessageType, Player, + PlayerType, TerraNullius, Unit, UnitType, @@ -76,6 +77,16 @@ export class TransportShipExecution implements Execution { return; } + if (this.target.isPlayer()) { + const targetPlayer = this.target as Player; + if ( + targetPlayer.type() !== PlayerType.Bot && + this.attacker.type() !== PlayerType.Bot + ) { + this.rejectIncomingAllianceRequests(targetPlayer); + } + } + if (this.target.isPlayer() && !this.attacker.canAttackPlayer(this.target)) { this.active = false; return; @@ -290,4 +301,13 @@ export class TransportShipExecution implements Execution { isActive(): boolean { return this.active; } + + private rejectIncomingAllianceRequests(target: Player) { + const request = this.attacker + .incomingAllianceRequests() + .find((ar) => ar.requestor() === target); + if (request !== undefined) { + request.reject(); + } + } } diff --git a/tests/Attack.test.ts b/tests/Attack.test.ts index dbf05b7c2..5f741559f 100644 --- a/tests/Attack.test.ts +++ b/tests/Attack.test.ts @@ -333,6 +333,54 @@ describe("Attack race condition with alliance requests", () => { }); }); +describe("Transport ship alliance rejection", () => { + beforeEach(async () => { + game = await setup("ocean_and_land", { + infiniteGold: true, + instantBuild: true, + infiniteTroops: true, + }); + + const playerAInfo = new PlayerInfo( + "playerA", + PlayerType.Human, + null, + "playerA_id", + ); + // close to the water to send boats + playerA = addPlayerToGame(playerAInfo, game, game.ref(7, 0)); + + const playerBInfo = new PlayerInfo( + "playerB", + PlayerType.Human, + null, + "playerB_id", + ); + playerB = addPlayerToGame(playerBInfo, game, game.ref(7, 15)); + + while (game.inSpawnPhase()) { + game.executeNextTick(); + } + }); + + test("Should cancel alliance requests if the recipient sends a transport ship", async () => { + // Player A sends alliance request to Player B + const allianceRequest = playerA.createAllianceRequest(playerB); + expect(allianceRequest).not.toBeNull(); + expect(playerB.incomingAllianceRequests()).toHaveLength(1); + + // Player B sends a transport ship toward Player A's territory + game.addExecution(new TransportShipExecution(playerB, game.ref(7, 0), 0)); + + // Execute a tick to process the transport ship launch + game.executeNextTick(); + + // Alliance request should be rejected since player B sent a naval invasion + expect(playerA.outgoingAllianceRequests()).toHaveLength(0); + expect(playerB.incomingAllianceRequests()).toHaveLength(0); + }); +}); + describe("Attack immunity", () => { beforeEach(async () => { game = await setup("ocean_and_land", {