Bug
A bot can dispatch a TransportShipExecution targeting a tile it owns, causing a spurious warning and silently cancelling the ship.
How it happens
- Bot decides to attack an enemy tile and queues a
TransportShipExecution with ref pointing to that tile.
- Before
init() runs, the bot (or a teammate) conquers that tile — so mg.owner(this.ref) is now the attacker itself.
this.target is set to the attacker → self-targeting.
- The guard at line 94 is supposed to catch this:
if (this.target.isPlayer() && !this.attacker.canAttackPlayer(this.target)) {
this.active = false;
return;
}
- But
canAttackPlayer(self) returns true for bots, because:
// canAttackPlayer (bot branch)
return !this.isFriendly(player, treatAFKFriendly);
// isFriendly
return this.isOnSameTeam(other) || this.isAlliedWith(other);
// isOnSameTeam explicitly short-circuits self
if (other === this) return false;
So isFriendly(self) → false → canAttackPlayer(self) → true. The guard passes, execution continues, and canBuild(UnitType.TransportShip, dst) fails because the destination is now friendly territory.
Observed output
Player:{name:Kazakhstan,...}] cannot send ship to Player:{name:Kazakhstan,...}], cannot find start tile
Both attacker and target are the same player.
Root cause
isFriendly has no self-check. isOnSameTeam intentionally returns false for other === this, but this causes isFriendly(self) to also return false, which makes a bot believe it can attack itself.
Suggested fix
Add a self-check to isFriendly in src/core/game/PlayerImpl.ts:
isFriendly(other: Player, treatAFKFriendly: boolean = false): boolean {
if (other === this) return true;
if (other.isDisconnected() && !treatAFKFriendly) return false;
return this.isOnSameTeam(other) || this.isAlliedWith(other);
}
Alternatively, add an early exit in TransportShipExecution.init():
if (this.target === this.attacker) {
this.active = false;
return;
}
Bug
A bot can dispatch a
TransportShipExecutiontargeting a tile it owns, causing a spurious warning and silently cancelling the ship.How it happens
TransportShipExecutionwithrefpointing to that tile.init()runs, the bot (or a teammate) conquers that tile — somg.owner(this.ref)is now the attacker itself.this.targetis set to the attacker → self-targeting.canAttackPlayer(self)returnstruefor bots, because:So
isFriendly(self)→false→canAttackPlayer(self)→true. The guard passes, execution continues, andcanBuild(UnitType.TransportShip, dst)fails because the destination is now friendly territory.Observed output
Both attacker and target are the same player.
Root cause
isFriendlyhas no self-check.isOnSameTeamintentionally returnsfalseforother === this, but this causesisFriendly(self)to also returnfalse, which makes a bot believe it can attack itself.Suggested fix
Add a self-check to
isFriendlyinsrc/core/game/PlayerImpl.ts:Alternatively, add an early exit in
TransportShipExecution.init():