From 10cd4fc63f3dd0839bd284f07cfc49d7e70ab348 Mon Sep 17 00:00:00 2001
From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:39:33 -0400
Subject: [PATCH] Fixed rail network path length limit and readded tests for it
(#4406)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
> **Before opening a PR:** discuss new features on
[Discord](https://discord.gg/K9zernJB5z) first, and file bugs or small
improvements as
[issues](https://github.com/openfrontio/OpenFrontIO/issues/new/choose).
You must be assigned to an `approved` issue — unsolicited PRs will be
auto-closed.
**Add approved & assigned issue number here:**
Resolves #4396
## Description:
Makes the max rail length 1.4142 * the max station radius to be minimum
amount outside of the factory effect radius.
Bug:
Fixed:
## 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
## Please put your Discord username so you can be contacted if a bug or
regression is found:
tktk1234567
---
src/core/configuration/Config.ts | 3 ++
src/core/game/RailNetworkImpl.ts | 35 ++++++++++++----------
tests/core/game/RailNetwork.test.ts | 45 +++++++++++++++--------------
3 files changed, 47 insertions(+), 36 deletions(-)
diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts
index 68b3ef916..c86586306 100644
--- a/src/core/configuration/Config.ts
+++ b/src/core/configuration/Config.ts
@@ -243,6 +243,9 @@ export class Config {
trainStationMaxRange(): number {
return 110;
}
+ railroadMaxSize(): number {
+ return this.trainStationMaxRange() * 1.4142;
+ }
tradeShipGold(dist: number, player: Player | PlayerView): Gold {
// Sigmoid: concave start, sharp S-curve middle, linear end - heavily punishes trades under range debuff.
diff --git a/src/core/game/RailNetworkImpl.ts b/src/core/game/RailNetworkImpl.ts
index e347c8f18..5b94bc45d 100644
--- a/src/core/game/RailNetworkImpl.ts
+++ b/src/core/game/RailNetworkImpl.ts
@@ -251,6 +251,7 @@ export class RailNetworkImpl implements RailNetwork {
const maxRange = this.game.config().trainStationMaxRange();
const minRangeSquared = this.game.config().trainStationMinRange() ** 2;
+ const maxPathSize = this.game.config().railroadMaxSize();
// A City or Port only joins the rail network when a Factory is already in
// range (see CityExecution/PortExecution). A Factory always becomes a
@@ -300,11 +301,13 @@ export class RailNetworkImpl implements RailNetwork {
} else {
continue;
}
+
const path = this.pathService.findTilePath(tile, targetTile);
- if (path.length === 0) continue;
- paths.push(path);
- if (neighborStation) {
- connectedStations.push(neighborStation);
+ if (path.length > 0 && path.length < maxPathSize) {
+ paths.push(path);
+ if (neighborStation) {
+ connectedStations.push(neighborStation);
+ }
}
}
@@ -375,17 +378,19 @@ export class RailNetworkImpl implements RailNetwork {
private connect(from: TrainStation, to: TrainStation) {
const path = this.pathService.findTilePath(from.tile(), to.tile());
- if (path.length === 0) return false;
- const railroad = new Railroad(from, to, path, this.nextId++);
- this.game.addUpdate({
- type: GameUpdateType.RailroadConstructionEvent,
- id: railroad.id,
- tiles: railroad.tiles,
- });
- from.addRailroad(railroad);
- to.addRailroad(railroad);
- this.railGrid.register(railroad);
- return true;
+ if (path.length > 0 && path.length < this.game.config().railroadMaxSize()) {
+ const railroad = new Railroad(from, to, path, this.nextId++);
+ this.game.addUpdate({
+ type: GameUpdateType.RailroadConstructionEvent,
+ id: railroad.id,
+ tiles: railroad.tiles,
+ });
+ from.addRailroad(railroad);
+ to.addRailroad(railroad);
+ this.railGrid.register(railroad);
+ return true;
+ }
+ return false;
}
private distanceFrom(
diff --git a/tests/core/game/RailNetwork.test.ts b/tests/core/game/RailNetwork.test.ts
index c99d746d7..c11a5b5a9 100644
--- a/tests/core/game/RailNetwork.test.ts
+++ b/tests/core/game/RailNetwork.test.ts
@@ -71,6 +71,7 @@ describe("RailNetworkImpl", () => {
config: () => ({
trainStationMaxRange: () => 80,
trainStationMinRange: () => 10,
+ railroadMaxSize: () => 100,
}),
x: vi.fn(() => 0),
y: vi.fn(() => 0),
@@ -249,11 +250,7 @@ describe("RailNetworkImpl", () => {
pathService.findTilePath.mockReturnValue(mockPath);
game.nearbyUnits.mockReturnValue([
- {
- unit: neighborStation.unit,
- distSquared: 400,
- euclideanDist: Math.sqrt(400),
- },
+ { unit: neighborStation.unit, distSquared: 400 },
]);
const result = network.computeGhostRailPaths(UnitType.City, tile);
@@ -272,11 +269,7 @@ describe("RailNetworkImpl", () => {
// distSquared = 50 <= minRange^2 (10^2 = 100)
game.nearbyUnits.mockReturnValue([
- {
- unit: neighborStation.unit,
- distSquared: 50,
- euclideanDist: Math.sqrt(50),
- },
+ { unit: neighborStation.unit, distSquared: 50 },
]);
const result = network.computeGhostRailPaths(UnitType.City, tile);
@@ -290,8 +283,26 @@ describe("RailNetworkImpl", () => {
stationManager.findStation.mockReturnValue(null);
+ game.nearbyUnits.mockReturnValue([{ unit: { id: 1 }, distSquared: 400 }]);
+
+ const result = network.computeGhostRailPaths(UnitType.City, tile);
+ expect(result).toEqual([]);
+ });
+
+ test("skips paths that exceed max railroad size", () => {
+ const tile = 42 as any;
+ const railGridMock = { query: vi.fn(() => new Set()) };
+ (network as any).railGrid = railGridMock;
+
+ const neighborStation = createMockStation(1);
+ neighborStation.tile.mockReturnValue(100);
+ stationManager.findStation.mockReturnValue(neighborStation);
+
+ // Path length >= railroadMaxSize (100)
+ pathService.findTilePath.mockReturnValue(new Array(100));
+
game.nearbyUnits.mockReturnValue([
- { unit: { id: 1 }, distSquared: 400, euclideanDist: Math.sqrt(400) },
+ { unit: neighborStation.unit, distSquared: 400 },
]);
const result = network.computeGhostRailPaths(UnitType.City, tile);
@@ -303,19 +314,11 @@ describe("RailNetworkImpl", () => {
const railGridMock = { query: vi.fn(() => new Set()) };
(network as any).railGrid = railGridMock;
- const neighbors: Array<{
- unit: any;
- distSquared: number;
- euclideanDist: number;
- }> = [];
+ const neighbors: Array<{ unit: any; distSquared: number }> = [];
for (let i = 0; i < 7; i++) {
const station = createMockStation(i);
station.tile.mockReturnValue(100 + i);
- neighbors.push({
- unit: station.unit,
- distSquared: 400 + i,
- euclideanDist: Math.sqrt(400 + i),
- });
+ neighbors.push({ unit: station.unit, distSquared: 400 + i });
}
stationManager.findStation.mockImplementation((unit: any) => {