Auto-reject alliance request when transport ship is sent to target player (#3477)

## Description:

When a player sends a transport ship toward another player's territory,
any pending alliance request from the target is now automatically
rejected.
This mirrors the behavior already in place for direct attacks,
preventing a player from exploiting a pending alliance request while
launching a naval invasion.

## 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
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

deshack_82603
This commit is contained in:
Mattia Migliorini
2026-03-20 18:56:36 +01:00
committed by GitHub
parent e02553c9b8
commit 2ec12f0a3a
2 changed files with 68 additions and 0 deletions
@@ -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();
}
}
}
+48
View File
@@ -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", {