mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 11:00:43 +00:00
Factory spawns trains (#1408)
## Description: - Change trains so it spawns from factories only - Increase train frequency as they will now spawn from a single structure. - Factory will spawn more trains depending on its level - Fix port to connect to nearby railroads - Add factory description ## 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 --------- Co-authored-by: Tom Rouillard <trouilla@mathworks.com> Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
@@ -90,6 +90,8 @@
|
||||
"build_desc": "Description",
|
||||
"build_city": "City",
|
||||
"build_city_desc": "Increases your max population. Useful when you can't expand your territory or you're about to hit your population limit.",
|
||||
"build_factory": "Factory",
|
||||
"build_factory_desc": "Creates railroads automatically with nearby structures, and spawns trains sporadically.",
|
||||
"build_defense": "Defense Post",
|
||||
"build_defense_desc": "Increases defenses around nearby borders, which show a checkered pattern. Attacks from enemies are slower and have more casualties.",
|
||||
"build_port": "Port",
|
||||
@@ -409,8 +411,9 @@
|
||||
"sam_launcher": "Defends against incoming nukes",
|
||||
"warship": "Captures trade ships, destroys ships and boats",
|
||||
"port": "Sends trade ships to generate gold",
|
||||
"defense_post": "Increase defenses of nearby borders",
|
||||
"city": "Increase max population"
|
||||
"defense_post": "Increases defenses of nearby borders",
|
||||
"city": "Increases max population",
|
||||
"factory": "Creates railroads and spawns trains"
|
||||
},
|
||||
"not_enough_money": "Not enough money"
|
||||
},
|
||||
|
||||
@@ -369,6 +369,11 @@ label.option-card:hover {
|
||||
mask: url("../../resources/images/CityIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .factory-icon {
|
||||
mask: url("../../resources/images/FactoryIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
}
|
||||
|
||||
#helpModal .defense-post-icon {
|
||||
mask: url("../../resources/images/ShieldIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
|
||||
@@ -326,7 +326,7 @@ export class DefaultConfig implements Config {
|
||||
return this._gameConfig.infiniteTroops;
|
||||
}
|
||||
trainSpawnRate(numberOfStations: number): number {
|
||||
return Math.min(1400, Math.round(70 * Math.pow(numberOfStations, 0.8)));
|
||||
return Math.min(1400, Math.round(20 * Math.pow(numberOfStations, 0.5)));
|
||||
}
|
||||
trainGold(): Gold {
|
||||
return BigInt(10_000);
|
||||
|
||||
@@ -51,11 +51,11 @@ export class FactoryExecution implements Execution {
|
||||
this.game.config().trainStationMaxRange(),
|
||||
[UnitType.City, UnitType.Port, UnitType.Factory],
|
||||
);
|
||||
// Use different seeds or trains will spawn simultaneously
|
||||
let seed = 0;
|
||||
|
||||
this.game.addExecution(new TrainStationExecution(this.factory, true));
|
||||
for (const { unit } of structures) {
|
||||
if (!unit.hasTrainStation()) {
|
||||
this.game.addExecution(new TrainStationExecution(unit, ++seed));
|
||||
this.game.addExecution(new TrainStationExecution(unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Execution, Game, Player, Unit, UnitType } from "../game/Game";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { TradeShipExecution } from "./TradeShipExecution";
|
||||
import { TrainStationExecution } from "./TrainStationExecution";
|
||||
|
||||
export class PortExecution implements Execution {
|
||||
private active = true;
|
||||
@@ -36,6 +37,7 @@ export class PortExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
this.port = this.player.buildUnit(UnitType.Port, spawn, {});
|
||||
this.createStation();
|
||||
}
|
||||
|
||||
if (!this.port.isActive()) {
|
||||
@@ -84,4 +86,18 @@ export class PortExecution implements Execution {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
createStation(): void {
|
||||
if (this.port !== null) {
|
||||
const nearbyFactory = this.mg.hasUnitNearby(
|
||||
this.port.tile()!,
|
||||
this.mg.config().trainStationMaxRange(),
|
||||
UnitType.Factory,
|
||||
this.player.id(),
|
||||
);
|
||||
if (nearbyFactory) {
|
||||
this.mg.addExecution(new TrainStationExecution(this.port));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ import { TrainExecution } from "./TrainExecution";
|
||||
export class TrainStationExecution implements Execution {
|
||||
private mg: Game;
|
||||
private active: boolean = true;
|
||||
private random: PseudoRandom | null = null;
|
||||
private random: PseudoRandom;
|
||||
private station: TrainStation | null = null;
|
||||
private numCars: number = 5;
|
||||
private lastSpawnTick: number = 0;
|
||||
private ticksCooldown: number = 10; // Minimum cooldown between two trains
|
||||
constructor(
|
||||
private unit: Unit,
|
||||
private randomSeed?: number,
|
||||
) {}
|
||||
private spawnTrains?: boolean, // If set, the station will spawn trains
|
||||
) {
|
||||
this.unit.setTrainStation(true);
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active;
|
||||
@@ -20,8 +24,9 @@ export class TrainStationExecution implements Execution {
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
this.mg = mg;
|
||||
this.random = new PseudoRandom(mg.ticks() + (this.randomSeed ?? 0));
|
||||
this.unit.setTrainStation(true);
|
||||
if (this.spawnTrains) {
|
||||
this.random = new PseudoRandom(mg.ticks());
|
||||
}
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
@@ -36,36 +41,57 @@ export class TrainStationExecution implements Execution {
|
||||
this.station = new TrainStation(this.mg, this.unit);
|
||||
this.mg.railNetwork().connectStation(this.station);
|
||||
}
|
||||
if (!this.station.isActive() || !this.random) {
|
||||
if (!this.station.isActive()) {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
const cluster = this.station.getCluster();
|
||||
this.spawnTrain(this.station, ticks);
|
||||
}
|
||||
|
||||
private shouldSpawnTrain(clusterSize: number): boolean {
|
||||
const spawnRate = this.mg.config().trainSpawnRate(clusterSize);
|
||||
for (let i = 0; i < this.unit!.level(); i++) {
|
||||
if (this.random.chance(spawnRate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private spawnTrain(station: TrainStation, currentTick: number) {
|
||||
if (
|
||||
!this.spawnTrains ||
|
||||
currentTick - this.lastSpawnTick < this.ticksCooldown
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const cluster = station.getCluster();
|
||||
if (cluster === null) {
|
||||
return;
|
||||
}
|
||||
const availableForTrade = cluster.availableForTrade(this.unit.owner());
|
||||
if (
|
||||
availableForTrade.size === 0 ||
|
||||
!this.random.chance(
|
||||
this.mg.config().trainSpawnRate(availableForTrade.size),
|
||||
)
|
||||
) {
|
||||
if (availableForTrade.size === 0) {
|
||||
return;
|
||||
}
|
||||
if (!this.shouldSpawnTrain(availableForTrade.size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pick a destination randomly.
|
||||
// Could be improved to pick a lucrative trip
|
||||
const destination = this.random.randFromSet(availableForTrade);
|
||||
if (destination !== this.station) {
|
||||
const destination: TrainStation =
|
||||
this.random.randFromSet(availableForTrade);
|
||||
if (destination !== station) {
|
||||
this.mg.addExecution(
|
||||
new TrainExecution(
|
||||
this.mg.railNetwork(),
|
||||
this.unit.owner(),
|
||||
this.station,
|
||||
station,
|
||||
destination,
|
||||
this.numCars,
|
||||
),
|
||||
);
|
||||
this.lastSpawnTick = currentTick;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,20 +45,13 @@ class PortStopHandler implements TrainStopHandler {
|
||||
}
|
||||
|
||||
class FactoryStopHandler implements TrainStopHandler {
|
||||
private factor: bigint = BigInt(2);
|
||||
onStop(
|
||||
mg: Game,
|
||||
station: TrainStation,
|
||||
trainExecution: TrainExecution,
|
||||
): void {
|
||||
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,
|
||||
});
|
||||
station.unit.owner().addGold(mg.config().trainGold(), station.tile());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user