mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-20 07:41:36 +00:00
Show rail ghost for initial factory 🚂 (#4294)
## Problem Fixes #4284. When you build a factory in an area with **no pre-existing factory** (e.g. just a city nearby), no rail ghost preview appeared — even though building the factory *would* lay rail lines connecting it to that city. ## Root cause `computeGhostRailPaths` in `RailNetworkImpl.ts` had two factory-hostile assumptions: 1. It bailed out early unless a `Factory` was already in range (`hasUnitNearby(..., UnitType.Factory)`). 2. It only matched neighbors that were *already* train stations (`findStation(...)` → skipped if null). But a **Factory** always becomes a station itself and *promotes* nearby City/Port/Factory into the rail network (see `FactoryExecution`). So it needs no pre-existing factory, and its neighbors won't be stations yet on first build. A **City/Port** only joins the network when a factory already exists (`CityExecution`/`PortExecution`) — so their behavior is correctly left unchanged. ## Fix - Skip the "factory must be nearby" gate when the placed unit is itself a `Factory`. - For a factory build, pathfind to nearby City/Port/Factory even if they aren't stations yet. City/Port keep connecting only to existing stations. ## Tests Added two cases to `RailNetwork.test.ts` (factory connects with no pre-existing factory; city still doesn't without one). All 25 tests pass. ## Note on scope As @Katokoda noted on the issue, a fully build-exact preview (neighboring structures also connecting to *each other*, merging existing networks, etc.) is larger and order-dependent. This PR resolves the reported bug — the initial factory now shows its rail ghost — and leaves the exact-match cascade as a separate follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -253,8 +253,15 @@ export class RailNetworkImpl implements RailNetwork {
|
||||
const minRangeSquared = this.game.config().trainStationMinRange() ** 2;
|
||||
const maxPathSize = this.game.config().railroadMaxSize();
|
||||
|
||||
// Cannot connect if outside the max range of a factory
|
||||
if (!this.game.hasUnitNearby(tile, maxRange, UnitType.Factory)) {
|
||||
// A City or Port only joins the rail network when a Factory is already in
|
||||
// range (see CityExecution/PortExecution). A Factory always becomes a
|
||||
// station and pulls nearby City/Port/Factory into the network itself, so
|
||||
// it needs no pre-existing factory to connect to.
|
||||
const buildingFactory = unitType === UnitType.Factory;
|
||||
if (
|
||||
!buildingFactory &&
|
||||
!this.game.hasUnitNearby(tile, maxRange, UnitType.Factory)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -273,22 +280,34 @@ export class RailNetworkImpl implements RailNetwork {
|
||||
if (neighbor.distSquared <= minRangeSquared) continue;
|
||||
|
||||
const neighborStation = this._stationManager.findStation(neighbor.unit);
|
||||
if (!neighborStation) continue;
|
||||
|
||||
const alreadyReachable = connectedStations.some(
|
||||
(s) =>
|
||||
this.distanceFrom(
|
||||
neighborStation,
|
||||
s,
|
||||
this.maxConnectionDistance - 1,
|
||||
) !== -1,
|
||||
);
|
||||
if (alreadyReachable) continue;
|
||||
// Building a factory connects to nearby structures even if they aren't
|
||||
// stations yet — they get promoted to stations when the factory is
|
||||
// built. For a city/port, only existing stations are relevant.
|
||||
let targetTile: TileRef;
|
||||
if (neighborStation) {
|
||||
const alreadyReachable = connectedStations.some(
|
||||
(s) =>
|
||||
this.distanceFrom(
|
||||
neighborStation,
|
||||
s,
|
||||
this.maxConnectionDistance - 1,
|
||||
) !== -1,
|
||||
);
|
||||
if (alreadyReachable) continue;
|
||||
targetTile = neighborStation.tile();
|
||||
} else if (buildingFactory) {
|
||||
targetTile = neighbor.unit.tile();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = this.pathService.findTilePath(tile, neighborStation.tile());
|
||||
const path = this.pathService.findTilePath(tile, targetTile);
|
||||
if (path.length > 0 && path.length < maxPathSize) {
|
||||
paths.push(path);
|
||||
connectedStations.push(neighborStation);
|
||||
if (neighborStation) {
|
||||
connectedStations.push(neighborStation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user