From c80ccaece90941063b7373701bd8e669d4d28aed Mon Sep 17 00:00:00 2001 From: DevelopingTom Date: Wed, 14 Jan 2026 02:00:55 +0100 Subject: [PATCH] Record train trading stats (#2891) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: The current gold stats don’t include gold generated by trains, even though this is a significant part of the economy for many players. This PR tracks those stats with two values: - other players trains visits the player station - the player trains visits any station Linked to this infra PR: https://github.com/openfrontio/infra/pull/242 ## 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: IngloriousTom --- src/core/StatsSchemas.ts | 2 ++ src/core/game/Stats.ts | 6 +++++ src/core/game/StatsImpl.ts | 10 ++++++++ src/core/game/TrainStation.ts | 37 ++++++---------------------- tests/core/game/TrainStation.test.ts | 4 +++ 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/core/StatsSchemas.ts b/src/core/StatsSchemas.ts index a911d9459..7596e9fbf 100644 --- a/src/core/StatsSchemas.ts +++ b/src/core/StatsSchemas.ts @@ -78,6 +78,8 @@ export const GOLD_INDEX_WORK = 0; // Gold earned by workers export const GOLD_INDEX_WAR = 1; // Gold earned by conquering players export const GOLD_INDEX_TRADE = 2; // Gold earned by trade ships export const GOLD_INDEX_STEAL = 3; // Gold earned by capturing trade ships +export const GOLD_INDEX_TRAIN_SELF = 4; // Gold earned by own trains +export const GOLD_INDEX_TRAIN_OTHER = 5; // Gold earned by other players trains // Other Units export const OTHER_INDEX_BUILT = 0; // Structures and warships built diff --git a/src/core/game/Stats.ts b/src/core/game/Stats.ts index 3dc644f2d..5dccedc7a 100644 --- a/src/core/game/Stats.ts +++ b/src/core/game/Stats.ts @@ -101,4 +101,10 @@ export interface Stats { // player was killed (0 tiles) playerKilled(player: Player, tick: number): void; + + // Player's train arrives at any station, generating gold + trainSelfTrade(player: Player, gold: number | bigint): void; + + // Another player's train arrives at own station + trainExternalTrade(player: Player, goldPlayer: number | bigint); } diff --git a/src/core/game/StatsImpl.ts b/src/core/game/StatsImpl.ts index b62956108..56e394769 100644 --- a/src/core/game/StatsImpl.ts +++ b/src/core/game/StatsImpl.ts @@ -13,6 +13,8 @@ import { BOMB_INDEX_LAUNCH, GOLD_INDEX_STEAL, GOLD_INDEX_TRADE, + GOLD_INDEX_TRAIN_OTHER, + GOLD_INDEX_TRAIN_SELF, GOLD_INDEX_WAR, GOLD_INDEX_WORK, NukeType, @@ -274,5 +276,13 @@ export class StatsImpl implements Stats { this._addPlayerKilled(player, tick); } + trainSelfTrade(player: Player, gold: BigIntLike): void { + this._addGold(player, GOLD_INDEX_TRAIN_SELF, gold); + } + + trainExternalTrade(player: Player, gold: BigIntLike): void { + this._addGold(player, GOLD_INDEX_TRAIN_OTHER, gold); + } + lobbyFillTime(fillTimeMs: number): void {} } diff --git a/src/core/game/TrainStation.ts b/src/core/game/TrainStation.ts index 170cfab7a..9917737d9 100644 --- a/src/core/game/TrainStation.ts +++ b/src/core/game/TrainStation.ts @@ -12,11 +12,7 @@ interface TrainStopHandler { onStop(mg: Game, station: TrainStation, trainExecution: TrainExecution): void; } -/** - * All stop handlers share the same logic for the time being - * Behavior to be defined - */ -class CityStopHandler implements TrainStopHandler { +class TradeStationStopHandler implements TrainStopHandler { onStop( mg: Game, station: TrainStation, @@ -24,31 +20,14 @@ class CityStopHandler implements TrainStopHandler { ): void { const stationOwner = station.unit.owner(); const trainOwner = trainExecution.owner(); - const goldBonus = mg.config().trainGold(rel(trainOwner, stationOwner)); + const gold = mg.config().trainGold(rel(trainOwner, stationOwner)); // Share revenue with the station owner if it's not the current player if (trainOwner !== stationOwner) { - stationOwner.addGold(goldBonus, station.tile()); - } - trainOwner.addGold(goldBonus, station.tile()); - } -} - -class PortStopHandler implements TrainStopHandler { - constructor(private random: PseudoRandom) {} - onStop( - mg: Game, - station: TrainStation, - trainExecution: TrainExecution, - ): void { - const stationOwner = station.unit.owner(); - const trainOwner = trainExecution.owner(); - const goldBonus = mg.config().trainGold(rel(trainOwner, stationOwner)); - - trainOwner.addGold(goldBonus, station.tile()); - // Share revenue with the station owner if it's not the current player - if (trainOwner !== stationOwner) { - stationOwner.addGold(goldBonus, station.tile()); + stationOwner.addGold(gold, station.tile()); + mg.stats().trainExternalTrade(trainOwner, gold); } + trainOwner.addGold(gold, station.tile()); + mg.stats().trainSelfTrade(trainOwner, gold); } } @@ -64,8 +43,8 @@ export function createTrainStopHandlers( random: PseudoRandom, ): Partial> { return { - [UnitType.City]: new CityStopHandler(), - [UnitType.Port]: new PortStopHandler(random), + [UnitType.City]: new TradeStationStopHandler(), + [UnitType.Port]: new TradeStationStopHandler(), [UnitType.Factory]: new FactoryStopHandler(), }; } diff --git a/tests/core/game/TrainStation.test.ts b/tests/core/game/TrainStation.test.ts index 29e966b01..48045b253 100644 --- a/tests/core/game/TrainStation.test.ts +++ b/tests/core/game/TrainStation.test.ts @@ -22,6 +22,10 @@ describe("TrainStation", () => { }), addUpdate: vi.fn(), addExecution: vi.fn(), + stats: vi.fn().mockReturnValue({ + trainExternalTrade: vi.fn(), + trainSelfTrade: vi.fn(), + }), } as any; player = {