diff --git a/resources/lang/en.json b/resources/lang/en.json index 44ebe941a4..6f06528dc1 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -771,6 +771,8 @@ "attack_cancelled_retreat": "Attack cancelled, {troops} soldiers killed during retreat", "received_gold_from_captured_ship": "Received {gold} gold from ship captured from {name}", "received_gold_from_trade": "Received {gold} gold from trade with {name}", + "received_gold_from_conquest": "Conquered {name}, received {gold} gold", + "conquered_no_gold": "Conquered {name} (didn't play, no gold awarded)", "missile_intercepted": "Missile intercepted {unit}", "mirv_warheads_intercepted": "{count, plural, one {{count} MIRV warhead intercepted} other {{count} MIRV warheads intercepted}}", "sent_troops_to_player": "Sent {troops} troops to {name}", diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 6d409fc589..1f6a97add4 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -7,6 +7,7 @@ import { import { AStarWaterHierarchical } from "../pathfinding/algorithms/AStar.WaterHierarchical"; import { PathFinder } from "../pathfinding/types"; import { AllPlayersStats, ClientID, Winner } from "../Schemas"; +import { ATTACK_INDEX_SENT } from "../StatsSchemas"; import { simpleHash } from "../Util"; import { AllianceImpl } from "./AllianceImpl"; import { AllianceRequestImpl } from "./AllianceRequestImpl"; @@ -1097,26 +1098,48 @@ export class GameImpl implements Game { } } - const gold = conquered.gold(); - this.displayMessage( - `Conquered ${conquered.displayName()} received ${renderNumber( + // Don't transfer gold when the conquered player didn't play (never attacked anyone) + // This is especially important when starting gold is enabled + const stats = this._stats.getPlayerStats(conquered); + const attacksSent = stats?.attacks?.[ATTACK_INDEX_SENT] ?? 0n; + const skipGoldTransfer = + attacksSent === 0n && conquered.type() === PlayerType.Human; + const gold = skipGoldTransfer ? 0n : conquered.gold(); + + if (skipGoldTransfer) { + this.displayMessage( + "events_display.conquered_no_gold", + MessageType.CONQUERED_PLAYER, + conqueror.id(), + undefined, + { + name: conquered.displayName(), + }, + ); + } else { + this.displayMessage( + "events_display.received_gold_from_conquest", + MessageType.CONQUERED_PLAYER, + conqueror.id(), gold, - )} gold`, - MessageType.CONQUERED_PLAYER, - conqueror.id(), - gold, - ); - conqueror.addGold(gold); - conquered.removeGold(gold); + { + gold: renderNumber(gold), + name: conquered.displayName(), + }, + ); + conqueror.addGold(gold); + conquered.removeGold(gold); + + // Record stats + this.stats().goldWar(conqueror, conquered, gold); + } + this.addUpdate({ type: GameUpdateType.ConquestEvent, conquerorId: conqueror.id(), conqueredId: conquered.id(), gold, }); - - // Record stats - this.stats().goldWar(conqueror, conquered, gold); } } diff --git a/tests/AttackStats.test.ts b/tests/AttackStats.test.ts index 10cb236c3e..23e0ef98f3 100644 --- a/tests/AttackStats.test.ts +++ b/tests/AttackStats.test.ts @@ -34,11 +34,36 @@ describe("AttackStats", () => { test("should increase war gold stat when a player is eliminated", () => { expect(player1.sharesBorderWith(player2)).toBeTruthy(); + // Player2 must attack to be considered active (otherwise gold won't transfer) + game.addExecution( + new AttackExecution(1, player2, game.terraNullius().id()), + ); + game.executeNextTick(); performAttack(game, player1, player2); expectWarGoldStatIsIncreasedAfterKill(game, player1, player2); }); + test("should NOT increase war gold stat when a inactive player is eliminated", () => { + expect(player1.sharesBorderWith(player2)).toBeTruthy(); + + const attackerStatsBefore = game.stats().stats()[player1.clientID()!]; + const warGoldBefore = attackerStatsBefore?.gold?.[GOLD_INDEX_WAR] ?? 0n; + + performAttack(game, player1, player2); + + const attackerStatsAfter = game.stats().stats()[player1.clientID()!]; + const warGoldAfter = attackerStatsAfter?.gold?.[GOLD_INDEX_WAR] ?? 0n; + + expect(warGoldAfter).toBe(warGoldBefore); + }); + test("should increase war gold stat when elimination occurs via territory annexation", () => { + // Player2 must attack to be considered active (otherwise gold won't transfer) + game.addExecution( + new AttackExecution(1, player2, game.terraNullius().id()), + ); + game.executeNextTick(); + // Mark every tile on the map as owned by player1 for (let x = 0; x < game.map().width(); x++) { for (let y = 0; y < game.map().height(); y++) {