Stop getting gold from conquering inactive players 🔧 (#3020)

## Description:

Maybe for v29. 

In the 5M starting gold modifier games you can conquer a inactive player
(spawned but didn't do anything) and get their 5M gold.
Huge unfair advantage.
I think that even without the starting gold modifier you should not get
the gold of inactive players because its unfair.

I identify inactive players (spawned but didn't do anything) by checking
the attack stats.
I added a translation for the displayMessage "Conquered {name}, received
{gold} gold". Why was that not translated?
I added a new message "Conquered {name} (Inactive player, received no
gold)".

## 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:

FloPinguin

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
This commit is contained in:
FloPinguin
2026-02-04 23:12:30 +01:00
committed by GitHub
parent 41a9bb80c0
commit c2663944e5
3 changed files with 63 additions and 13 deletions
+2
View File
@@ -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}",
+36 -13
View File
@@ -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);
}
}
+25
View File
@@ -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++) {