reduce train gold after each city (#3400)

## Description:

Now that cities snap to existing rails, it's possible to line up dozens
of cities in a row, producing way too much gold. This PR reduces the
gold after each stop to prevent that. Gold only starts decreasing after
the 3rd city.

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

evan
This commit is contained in:
Evan
2026-03-10 20:16:47 -07:00
committed by GitHub
parent 97767fa364
commit 3e65d08942
7 changed files with 136 additions and 17 deletions
+4 -1
View File
@@ -132,7 +132,10 @@ export interface Config {
tradeShipSpawnRejections: number,
numTradeShips: number,
): number;
trainGold(rel: "self" | "team" | "ally" | "other"): Gold;
trainGold(
rel: "self" | "team" | "ally" | "other",
citiesVisited: number,
): Gold;
trainSpawnRate(numPlayerFactories: number): number;
trainStationMinRange(): number;
trainStationMaxRange(): number;
+14 -8
View File
@@ -20,7 +20,7 @@ import { PlayerView } from "../game/GameView";
import { UserSettings } from "../game/UserSettings";
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
import { NukeType } from "../StatsSchemas";
import { assertNever, sigmoid, simpleHash, within } from "../Util";
import { assertNever, sigmoid, simpleHash, toInt, within } from "../Util";
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
import { Env } from "./Env";
import { PastelTheme } from "./PastelTheme";
@@ -273,22 +273,28 @@ export class DefaultConfig implements Config {
// expected number of trains = numPlayerFactories / trainSpawnRate(numPlayerFactories)
return (numPlayerFactories + 10) * 18;
}
trainGold(rel: "self" | "team" | "ally" | "other"): Gold {
const multiplier = this.goldMultiplier();
let baseGold: bigint;
trainGold(
rel: "self" | "team" | "ally" | "other",
citiesVisited: number,
): Gold {
// No penalty for the first 3 cities.
citiesVisited = Math.max(0, citiesVisited - 2);
let baseGold: number;
switch (rel) {
case "ally":
baseGold = 35_000n;
baseGold = 35_000;
break;
case "team":
case "other":
baseGold = 25_000n;
baseGold = 25_000;
break;
case "self":
baseGold = 10_000n;
baseGold = 10_000;
break;
}
return BigInt(Math.floor(Number(baseGold) * multiplier));
const distPenalty = citiesVisited * 5_000;
const gold = Math.max(5000, baseGold - distPenalty);
return toInt(gold * this.goldMultiplier());
}
trainStationMinRange(): number {
+9
View File
@@ -24,6 +24,7 @@ export class TrainExecution implements Execution {
private stations: TrainStation[] = [];
private currentRailroad: OrientedRailroad | null = null;
private speed: number = 2;
private _tradeStopsVisited: number = 0;
constructor(
private railNetwork: RailNetwork,
@@ -37,6 +38,10 @@ export class TrainExecution implements Execution {
return this.player;
}
public tradeStopsVisited(): number {
return this._tradeStopsVisited;
}
init(mg: Game, ticks: number): void {
this.mg = mg;
const stations = this.railNetwork.findStationsPath(
@@ -261,6 +266,10 @@ export class TrainExecution implements Execution {
throw new Error("Not initialized");
}
this.stations[1].onTrainStop(this);
const stationType = this.stations[1].unit.type();
if (stationType === UnitType.City || stationType === UnitType.Port) {
this._tradeStopsVisited++;
}
return;
}
@@ -697,7 +697,10 @@ export class NationStructureBehavior {
unitToCluster.set(station.unit, station.getCluster());
}
const maxTradeGold = Math.max(Number(game.config().trainGold("ally")), 1);
const maxTradeGold = Math.max(
Number(game.config().trainGold("ally", 0)),
1,
);
const result: Array<{
tile: TileRef;
cluster: Cluster | null;
@@ -705,7 +708,8 @@ export class NationStructureBehavior {
}> = [];
// Own structures — weighted by "self" trade gold.
const selfWeight = Number(game.config().trainGold("self")) / maxTradeGold;
const selfWeight =
Number(game.config().trainGold("self", 0)) / maxTradeGold;
for (const unit of player.units(
UnitType.City,
UnitType.Port,
@@ -730,7 +734,7 @@ export class NationStructureBehavior {
: player.isAlliedWith(neighbor)
? "ally"
: "other";
const weight = Number(game.config().trainGold(relType)) / maxTradeGold;
const weight = Number(game.config().trainGold(relType, 0)) / maxTradeGold;
for (const unit of neighbor.units(
UnitType.City,
UnitType.Port,
+6 -1
View File
@@ -20,7 +20,12 @@ class TradeStationStopHandler implements TrainStopHandler {
): void {
const stationOwner = station.unit.owner();
const trainOwner = trainExecution.owner();
const gold = mg.config().trainGold(rel(trainOwner, stationOwner));
const gold = mg
.config()
.trainGold(
rel(trainOwner, stationOwner),
trainExecution.tradeStopsVisited(),
);
// Share revenue with the station owner if it's not the current player
if (trainOwner !== stationOwner) {
stationOwner.addGold(gold, station.tile());