diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index c3c093426d..7c14316655 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -491,7 +491,7 @@ export class ClientGameRunner { this.eventBus.emit( new SendAttackIntentEvent( this.gameView.owner(tile).id(), - this.myPlayer.troops() * this.renderer.uiState.attackRatio, + this.renderer.uiState.attackRatio, ), ); } else if (this.canAutoBoat(actions, tile)) { @@ -607,7 +607,7 @@ export class ClientGameRunner { this.eventBus.emit( new SendAttackIntentEvent( this.gameView.owner(tile).id(), - this.myPlayer.troops() * this.renderer.uiState.attackRatio, + this.renderer.uiState.attackRatio, ), ); } diff --git a/src/client/Transport.ts b/src/client/Transport.ts index 98b8bde167..08f8a8b1fa 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -72,7 +72,7 @@ export class SendSpawnIntentEvent implements GameEvent { export class SendAttackIntentEvent implements GameEvent { constructor( public readonly targetID: PlayerID | null, - public readonly troops: number, + public readonly attackRatio: number, ) {} } @@ -80,7 +80,7 @@ export class SendBoatAttackIntentEvent implements GameEvent { constructor( public readonly targetID: PlayerID | null, public readonly dst: TileRef, - public readonly troops: number, + public readonly attackRatio: number, public readonly src: TileRef | null = null, ) {} } @@ -456,7 +456,7 @@ export class Transport { type: "attack", clientID: this.lobbyConfig.clientID, targetID: event.targetID, - troops: event.troops, + attackRatio: event.attackRatio, }); } @@ -465,7 +465,7 @@ export class Transport { type: "boat", clientID: this.lobbyConfig.clientID, targetID: event.targetID, - troops: event.troops, + attackRatio: event.attackRatio, dst: event.dst, src: event.src, }); diff --git a/src/client/graphics/layers/PlayerActionHandler.ts b/src/client/graphics/layers/PlayerActionHandler.ts index 2ea2e89709..06864f8af5 100644 --- a/src/client/graphics/layers/PlayerActionHandler.ts +++ b/src/client/graphics/layers/PlayerActionHandler.ts @@ -32,10 +32,7 @@ export class PlayerActionHandler { handleAttack(player: PlayerView, targetId: string | null) { this.eventBus.emit( - new SendAttackIntentEvent( - targetId, - this.uiState.attackRatio * player.troops(), - ), + new SendAttackIntentEvent(targetId, this.uiState.attackRatio), ); } @@ -49,7 +46,7 @@ export class PlayerActionHandler { new SendBoatAttackIntentEvent( targetId, targetTile, - this.uiState.attackRatio * player.troops(), + this.uiState.attackRatio, spawnTile, ), ); diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index acedd062a9..a5c0ec03ba 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -232,7 +232,7 @@ export const AllianceExtensionIntentSchema = BaseIntentSchema.extend({ export const AttackIntentSchema = BaseIntentSchema.extend({ type: z.literal("attack"), targetID: ID.nullable(), - troops: z.number().nonnegative().nullable(), + attackRatio: z.number().nonnegative().nullable(), }); export const SpawnIntentSchema = BaseIntentSchema.extend({ @@ -243,7 +243,7 @@ export const SpawnIntentSchema = BaseIntentSchema.extend({ export const BoatAttackIntentSchema = BaseIntentSchema.extend({ type: z.literal("boat"), targetID: ID.nullable(), - troops: z.number().nonnegative(), + attackRatio: z.number().nonnegative(), dst: z.number(), src: z.number().nullable(), }); diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 5997c13202..c2745f0cfd 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -21,6 +21,8 @@ export class AttackExecution implements Execution { private random = new PseudoRandom(123); + private startTroops: number | null = null; + private target: Player | TerraNullius; private mg: Game; @@ -28,11 +30,12 @@ export class AttackExecution implements Execution { private attack: Attack | null = null; constructor( - private startTroops: number | null = null, + private attackRatio: number | null = null, private _owner: Player, private _targetID: PlayerID | null, private sourceTile: TileRef | null = null, private removeTroops: boolean = true, + private extraTroops?: number, ) {} public targetID(): PlayerID | null { @@ -102,13 +105,22 @@ export class AttackExecution implements Execution { } } - this.startTroops ??= this.mg - .config() - .attackAmount(this._owner, this.target); + const currentTroops = this._owner.troops(); + if (this.attackRatio) { + this.startTroops = this.attackRatio * currentTroops; + } else if (this.extraTroops) { + this.startTroops = this.extraTroops; + } else { + this.startTroops = this.mg + .config() + .attackAmount(this._owner, this.target); + } + if (this.removeTroops) { this.startTroops = Math.min(this._owner.troops(), this.startTroops); this._owner.removeTroops(this.startTroops); } + this.attack = this._owner.createAttack( this.target, this.startTroops, diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 4a8e5df916..85cf1822c4 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -56,7 +56,7 @@ export class Executor { switch (intent.type) { case "attack": { return new AttackExecution( - intent.troops, + intent.attackRatio, player, intent.targetID, null, @@ -75,7 +75,7 @@ export class Executor { player, intent.targetID, intent.dst, - intent.troops, + intent.attackRatio, intent.src, ); case "allianceRequest": diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 10805d4b17..b1822dcd69 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -413,13 +413,7 @@ export class FakeHumanExecution implements Execution { return; } this.mg.addExecution( - new TransportShipExecution( - this.player, - other.id(), - closest.y, - this.player.troops() / 5, - null, - ), + new TransportShipExecution(this.player, other.id(), closest.y, 0.2, null), ); } @@ -623,7 +617,7 @@ export class FakeHumanExecution implements Execution { this.player, this.mg.owner(dst).id(), dst, - this.player.troops() / 5, + 0.2, null, ), ); diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 6e37e066d1..3900d88cec 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -30,6 +30,7 @@ export class TransportShipExecution implements Execution { // TODO make private public path: TileRef[]; private dst: TileRef | null; + private startTroops: number | null = null; private boat: Unit; @@ -41,7 +42,7 @@ export class TransportShipExecution implements Execution { private attacker: Player, private targetID: PlayerID | null, private ref: TileRef, - private startTroops: number, + private attackRatio: number, private src: TileRef | null, ) { this.originalOwner = this.attacker; @@ -94,9 +95,13 @@ export class TransportShipExecution implements Execution { this.target = mg.player(this.targetID); } - this.startTroops ??= this.mg - .config() - .boatAttackAmount(this.attacker, this.target); + if (this.attackRatio) { + this.startTroops = this.attacker.troops() * this.attackRatio; + } else { + this.startTroops = this.mg + .config() + .boatAttackAmount(this.attacker, this.target); + } this.startTroops = Math.min(this.startTroops, this.attacker.troops()); @@ -249,11 +254,12 @@ export class TransportShipExecution implements Execution { } else { this.mg.addExecution( new AttackExecution( - this.boat.troops(), + null, // Don't use ratio for creating attack on landing this.attacker, this.targetID, this.dst, false, + this.boat.troops(), ), ); } diff --git a/src/core/execution/utils/BotBehavior.ts b/src/core/execution/utils/BotBehavior.ts index d059125017..b3ac8ef0a7 100644 --- a/src/core/execution/utils/BotBehavior.ts +++ b/src/core/execution/utils/BotBehavior.ts @@ -378,7 +378,7 @@ export class BotBehavior { forceSendAttack(target: Player | TerraNullius) { this.game.addExecution( new AttackExecution( - this.player.troops() / 2, + 0.5, this.player, target.isPlayer() ? target.id() : this.game.terraNullius().id(), ), @@ -396,9 +396,10 @@ export class BotBehavior { const targetTroops = maxTroops * reserveRatio; const troops = this.player.troops() - targetTroops; if (troops < 1) return; + const ratio = troops / this.player.troops(); this.game.addExecution( new AttackExecution( - troops, + ratio, this.player, target.isPlayer() ? target.id() : this.game.terraNullius().id(), ),