Automatic train stations (#1353)

## Description:

Train stations are now built automatically when a factory is
constructed.

Changes:
- When a factory is built, nearby structures are connected to the rail
network
- When a city is built near a factory, it is connected to the rail
network
    - All structures behave the same when a train stops: to be defined
    - Removed station badge
    - Gold income is now related to the structure's level

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
This commit is contained in:
DevelopingTom
2025-07-06 13:15:49 -07:00
committed by GitHub
parent 3108921637
commit 6353a5d6f7
23 changed files with 105 additions and 185 deletions
+6
View File
@@ -633,6 +633,12 @@ export interface Game extends GameMap {
// Units
units(...types: UnitType[]): Unit[];
unitInfo(type: UnitType): UnitInfo;
hasUnitNearby(
tile: TileRef,
searchRange: number,
type: UnitType,
playerId: PlayerID,
);
nearbyUnits(
tile: TileRef,
searchRange: number,
+9
View File
@@ -715,6 +715,15 @@ export class GameImpl implements Game {
}
}
hasUnitNearby(
tile: TileRef,
searchRange: number,
type: UnitType,
playerId: PlayerID,
) {
return this.unitGrid.hasUnitNearby(tile, searchRange, type, playerId);
}
nearbyUnits(
tile: TileRef,
searchRange: number,
+1
View File
@@ -120,6 +120,7 @@ export class RailNetworkImpl implements RailNetwork {
newCluster.addStations(stations);
}
}
station.unit.setTrainStation(false);
}
/**
+20 -9
View File
@@ -1,4 +1,3 @@
import { TradeShipExecution } from "../execution/TradeShipExecution";
import { TrainExecution } from "../execution/TrainExecution";
import { GraphAdapter } from "../pathfinding/SerialAStar";
import { PseudoRandom } from "../PseudoRandom";
@@ -14,30 +13,34 @@ 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 {
private factor: bigint = BigInt(2);
onStop(
mg: Game,
station: TrainStation,
trainExecution: TrainExecution,
): void {
const goldBonus = mg.config().trainGold();
const level = BigInt(station.unit.level() + 1);
const goldBonus = (mg.config().trainGold() * level) / this.factor;
station.unit.owner().addGold(goldBonus, station.tile());
}
}
class PortStopHandler implements TrainStopHandler {
private factor: bigint = BigInt(2);
constructor(private random: PseudoRandom) {}
onStop(
mg: Game,
station: TrainStation,
trainExecution: TrainExecution,
): void {
const unit = station.unit;
const ports = unit.owner().tradingPorts(unit);
if (ports.length === 0) return;
const port = this.random.randElement(ports);
mg.addExecution(new TradeShipExecution(unit.owner(), unit, port));
const level = BigInt(station.unit.level() + 1);
const goldBonus = (mg.config().trainGold() * level) / this.factor;
station.unit.owner().addGold(goldBonus, station.tile());
}
}
@@ -47,7 +50,15 @@ class FactoryStopHandler implements TrainStopHandler {
station: TrainStation,
trainExecution: TrainExecution,
): void {
trainExecution.loadCargo();
const goldBonus = mg.config().trainGold();
station.unit.owner().addGold(goldBonus);
mg.addUpdate({
type: GameUpdateType.BonusEvent,
tile: station.tile(),
gold: Number(goldBonus),
workers: 0,
troops: 0,
});
}
}
+1
View File
@@ -249,6 +249,7 @@ export class UnitImpl implements Unit {
case UnitType.Port:
case UnitType.SAMLauncher:
case UnitType.Warship:
case UnitType.Factory:
this.mg.stats().unitDestroy(destroyer, this._type);
this.mg.stats().unitLose(this.owner(), this._type);
break;