Record train trading stats (#2891)

## 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
This commit is contained in:
DevelopingTom
2026-01-14 02:00:55 +01:00
committed by GitHub
parent 3fff628642
commit c80ccaece9
5 changed files with 30 additions and 29 deletions
+2
View File
@@ -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
+6
View File
@@ -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);
}
+10
View File
@@ -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 {}
}
+8 -29
View File
@@ -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<Record<UnitType, TrainStopHandler>> {
return {
[UnitType.City]: new CityStopHandler(),
[UnitType.Port]: new PortStopHandler(random),
[UnitType.City]: new TradeStationStopHandler(),
[UnitType.Port]: new TradeStationStopHandler(),
[UnitType.Factory]: new FactoryStopHandler(),
};
}
+4
View File
@@ -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 = {