Skip to content

WebGL: return of factory/defence post radii, and railroad highlighting when placing city/port right on top#3981

Merged
evanpelle merged 6 commits into
mainfrom
facatory-radius
May 22, 2026
Merged

WebGL: return of factory/defence post radii, and railroad highlighting when placing city/port right on top#3981
evanpelle merged 6 commits into
mainfrom
facatory-radius

Conversation

@VariableVince
Copy link
Copy Markdown
Contributor

@VariableVince VariableVince commented May 21, 2026

Description:

Show factory and defence post radius for ghost structure when placing structures from build bar (unitdisplay).

Show when city/port is placed directly over existing railroad, by highlighting the railroad green. The railroad is not highlighted when instead a city/port nearby the ghost structure will be upgraded instead of placing it on the railroad. This works with the existing code in buildableUnits in PlayerImpl: it would already return an empty array [] for overlappingRailroads and for ghostRailPaths when canUpgrade is false. So the old checks for uiState for Canvas2D in BuildPreviewController weren't even needed per se, they followed the same logic as buildableUnits in PlayerImpl already did.

Both changes emulate how it worked before the move to WebGL.

  • OverlappingRailroads now returns TileRefs instead of a railroad ID, and it does so with less allocations than the previous code. It's a determistic outcome, sorted and deduplicated. In doubt about this a bit, because it's better also in case we ever do desync checks using this data, but for the rendering it isn't needed per se and could be more performant without allocations.
  • Also: Cleanup obsolete Canvas2D rail highlighting state (UIState) that was superseded by GhostPreviewData.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • 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:

tryout33

… structures from build bar (unitdisplay).

Show when city/port is placed directly over existing railroad, by highlighting the railroad green.

Both changes emulate how it worked before the move to WebGL. OverlappingRailroads now returns TileRefs instead of a railroad ID, and it does so with less allocations than the previous code.
@VariableVince VariableVince added this to the v32 milestone May 21, 2026
@VariableVince VariableVince self-assigned this May 21, 2026
@VariableVince VariableVince requested a review from a team as a code owner May 21, 2026 23:18
@VariableVince VariableVince added the UI/UX UI/UX changes including assets, menus, QoL, etc. label May 21, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef9aeb0b-9623-4e08-88ce-e8e74a1c47bb

📥 Commits

Reviewing files that changed from the base of the PR and between b171347 and e7cdf5d.

📒 Files selected for processing (4)
  • src/client/UIState.ts
  • src/core/game/RailNetworkImpl.ts
  • tests/InputHandler.test.ts
  • tests/core/game/RailNetwork.test.ts
💤 Files with no reviewable changes (2)
  • tests/InputHandler.test.ts
  • src/client/UIState.ts

Walkthrough

Rail-overlap APIs and types were migrated from numeric IDs to TileRef arrays across network, model, and renderer layers. Build preview radius selection was refactored to a switch and now handles Factory and DefensePost via game.config(); UIState rail preview fields were removed and tests updated.

Changes

Rail overlap type refinement and range radius extension

Layer / File(s) Summary
Rail overlap contract and implementation
src/core/game/RailNetwork.ts, src/core/game/RailNetworkImpl.ts
RailNetwork.overlappingRailroads() now returns TileRef[] instead of number[]. Implementation aggregates railroad.tiles from spatial-grid results and returns a deduplicated, sorted TileRef[].
Type alignment in models and renderer
src/core/game/Game.ts, src/client/render/types/Renderer.ts
BuildableUnit.overlappingRailroads and GhostPreviewData rail fields (ghostRailPaths, overlappingRailroads) now use TileRef types. TileRef import added to renderer types.
Build preview UI and range radius
src/client/controllers/BuildPreviewController.ts, src/client/UIState.ts, src/client/hud/GameRenderer.ts
buildGhostPreviewData refactors radius selection to a switch on u.type and adds cases for UnitType.Factory (uses game.config().trainStationMaxRange()) and UnitType.DefensePost (uses game.config().defensePostRange()). UIState fields overlappingRailroads and ghostRailPaths were removed and render/clear functions no longer set/clear those fields.
Tests and fixtures
tests/InputHandler.test.ts, tests/core/game/RailNetwork.test.ts
Test fixtures updated to drop removed UIState fields. New overlappingRailroads tests assert deduplication, deterministic sorting, and empty results when no overlaps exist.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BuildPreviewController
  participant GameConfig as Game.config
  participant Renderer
  Client->>BuildPreviewController: buildGhostPreviewData(unit)
  BuildPreviewController->>GameConfig: trainStationMaxRange()/defensePostRange() (for Factory/DefensePost)
  GameConfig-->>BuildPreviewController: radius value
  BuildPreviewController->>Renderer: updateGhostPreview / updateNukeTrajectory
  Renderer-->>Client: rendered preview / trajectory
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • evanpelle

Poem

Tiles swapped numbers for names in gentle rows,
Tracks now point to places where each railroad goes.
Factories and turrets check their reach with care,
The preview draws ranges clear in open air. 🚂✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: restoring factory/defence post radii display and railroad highlighting when placing city/port on top of existing railroad.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of changes, technical details about overlappingRailroads refactoring, and cleanup of obsolete state.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@VariableVince VariableVince marked this pull request as draft May 21, 2026 23:19
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 21, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/client/controllers/BuildPreviewController.ts (1)

339-341: ⚡ Quick win

Factory range uses trainStationMaxRange() on purpose (no factory-specific config exists)

  • src/core/configuration/Config.ts defines only trainStationMinRange()/trainStationMaxRange(); no factory*Range methods exist.
  • Core logic passes trainStationMaxRange() for UnitType.Factory (e.g., src/core/execution/FactoryExecution.ts, src/core/execution/PortExecution.ts, src/core/game/RailNetworkImpl.ts).
  • Naming is confusing—consider a clearer alias/comment that “factory uses train-station range”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/controllers/BuildPreviewController.ts` around lines 339 - 341, The
switch case for UnitType.Factory sets rangeRadius from
this.game.config().trainStationMaxRange() but that reuse is intentional; clarify
intent by adding a concise inline comment next to the UnitType.Factory case
stating that factories share the train-station range, and (optionally) create a
descriptive alias in Config like factoryMaxRange() that forwards to
trainStationMaxRange() and replace the direct call with
this.game.config().factoryMaxRange() so callers (e.g., the case handling
rangeRadius and any uses in FactoryExecution/PortExecution/RailNetworkImpl) are
self-documenting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/client/controllers/BuildPreviewController.ts`:
- Around line 339-341: The switch case for UnitType.Factory sets rangeRadius
from this.game.config().trainStationMaxRange() but that reuse is intentional;
clarify intent by adding a concise inline comment next to the UnitType.Factory
case stating that factories share the train-station range, and (optionally)
create a descriptive alias in Config like factoryMaxRange() that forwards to
trainStationMaxRange() and replace the direct call with
this.game.config().factoryMaxRange() so callers (e.g., the case handling
rangeRadius and any uses in FactoryExecution/PortExecution/RailNetworkImpl) are
self-documenting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 51b6725b-c2a9-480a-93c0-859abafb553e

📥 Commits

Reviewing files that changed from the base of the PR and between 42ab6c5 and a311f2f.

📒 Files selected for processing (1)
  • src/client/controllers/BuildPreviewController.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/core/game/RailNetworkImpl.ts (2)

226-232: ⚠️ Potential issue | 🟠 Major

Make overlappingRailroads() ordering deterministic + add core tests

RailSpatialGrid.query() uses deterministic nested loops, but overlappingRailroads() concatenates railroad.tiles in the iteration order of Set<Railroad> returned by railGrid.query(). That order ultimately depends on Set insertion order from register() (rail registration order / rail.tiles iteration order), and there’s no sorting when producing the TileRef[].

Also, tests/core/game/RailNetwork.test.ts doesn’t cover overlappingRailroads() (only computeGhostRailPaths), and the only overlappingRailroads occurrences in tests are UI fixtures set to []. Add a core unit test asserting the returned TileRef[] content (and, if order matters, order) for overlappingRailroads().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/game/RailNetworkImpl.ts` around lines 226 - 232,
overlappingRailroads() currently concatenates railroad.tiles in the iteration
order of the Set returned by railGrid.query(), which makes results
non-deterministic; change overlappingRailroads(tile: TileRef) to produce a
deterministic, deduplicated TileRef[] by collecting tiles from each railroad
returned by this.railGrid.query(tile, this.stationRadius) into a canonical
holder (e.g., a Set keyed by tile coordinates), then sort the resulting TileRef
array by a stable key (e.g., y then x or a single index) before returning;
update references to overlappingRailroads and add a core unit test in
tests/core/game/RailNetwork.test.ts that constructs a known configuration, calls
RailNetworkImpl.overlappingRailroads, and asserts the returned TileRef[]
contains the expected tiles (and the expected order if order is meaningful).

226-232: ⚠️ Potential issue | 🟠 Major

Add unit tests for RailNetworkImpl.overlappingRailroads (TileRef[] contract).
overlappingRailroads in src/core/game/RailNetworkImpl.ts has no direct unit test coverage; existing test hits only mock the UI state field overlappingRailroads: [], not the method behavior. Add tests that assert the returned TileRef[] flattening from railGrid.query(tile, stationRadius), including multiple overlapping railroads and empty-results cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/game/RailNetworkImpl.ts` around lines 226 - 232, Add unit tests for
RailNetworkImpl.overlappingRailroads to verify it returns a flattened TileRef[]
from railGrid.query(tile, this.stationRadius); specifically, write tests that
(1) stub or mock the instance's railGrid.query to return multiple railroad
objects each with a tiles array and assert overlappingRailroads(tile)
concatenates them in order, and (2) stub railGrid.query to return an empty array
and assert overlappingRailroads(tile) returns an empty array; reference the
method overlappingRailroads(tile: TileRef) and the instance property
stationRadius when setting up the RailNetworkImpl instance and mocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/core/game/RailNetworkImpl.ts`:
- Around line 226-232: overlappingRailroads() currently concatenates
railroad.tiles in the iteration order of the Set returned by railGrid.query(),
which makes results non-deterministic; change overlappingRailroads(tile:
TileRef) to produce a deterministic, deduplicated TileRef[] by collecting tiles
from each railroad returned by this.railGrid.query(tile, this.stationRadius)
into a canonical holder (e.g., a Set keyed by tile coordinates), then sort the
resulting TileRef array by a stable key (e.g., y then x or a single index)
before returning; update references to overlappingRailroads and add a core unit
test in tests/core/game/RailNetwork.test.ts that constructs a known
configuration, calls RailNetworkImpl.overlappingRailroads, and asserts the
returned TileRef[] contains the expected tiles (and the expected order if order
is meaningful).
- Around line 226-232: Add unit tests for RailNetworkImpl.overlappingRailroads
to verify it returns a flattened TileRef[] from railGrid.query(tile,
this.stationRadius); specifically, write tests that (1) stub or mock the
instance's railGrid.query to return multiple railroad objects each with a tiles
array and assert overlappingRailroads(tile) concatenates them in order, and (2)
stub railGrid.query to return an empty array and assert
overlappingRailroads(tile) returns an empty array; reference the method
overlappingRailroads(tile: TileRef) and the instance property stationRadius when
setting up the RailNetworkImpl instance and mocks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cb5e10c8-516b-4a95-8fbe-586c291d2dd5

📥 Commits

Reviewing files that changed from the base of the PR and between a311f2f and fc097c4.

📒 Files selected for processing (1)
  • src/core/game/RailNetworkImpl.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 21, 2026
Cleanup obsolete Canvas2D rail highlighting state that was superseded by GhostPreviewData.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 21, 2026
…eeded, so we could revert it back to just outputting an unsorted array, but maybe in case we ever do decync checks on its outcome this is better.

Fix UIState mock in InputHandler tests

 Add tests in RailNetwork.test.ts

Remove overlappingRailroads and ghostRailPaths initialization from tests after they were removed from the UIState interface.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/core/game/RailNetwork.test.ts`:
- Around line 171-197: The tests currently mutate the private railGrid and use
vi.fn mocks; instead create a real game via the setup() helper and exercise
public APIs so overlappingRailroads is tested through normal simulation: call
setup() from tests/util/Setup.ts to build a game instance, obtain the
RailNetwork instance (the same object referenced as network in the test) via the
public game/core accessor, use public game actions or helper utilities to
place/define rail segments on tiles that produce overlapping railroads (instead
of injecting railGrid or stubbing query), then call
network.overlappingRailroads(tile) and assert the deterministic deduplicated
result; remove direct assignment to (network as any).railGrid and any vi.fn
usage so the test operates through public behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac669c9a-e484-416e-bdf0-a59445cab296

📥 Commits

Reviewing files that changed from the base of the PR and between b171347 and fdd76a9.

📒 Files selected for processing (4)
  • src/client/UIState.ts
  • src/core/game/RailNetworkImpl.ts
  • tests/InputHandler.test.ts
  • tests/core/game/RailNetwork.test.ts
💤 Files with no reviewable changes (1)
  • src/client/UIState.ts

Comment thread tests/core/game/RailNetwork.test.ts
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management May 22, 2026
@VariableVince VariableVince marked this pull request as ready for review May 22, 2026 00:26
@github-project-automation github-project-automation Bot moved this from Development to Final Review in OpenFront Release Management May 22, 2026
@evanpelle evanpelle merged commit 2ee2fb9 into main May 22, 2026
14 checks passed
@evanpelle evanpelle deleted the facatory-radius branch May 22, 2026 09:33
@github-project-automation github-project-automation Bot moved this from Final Review to Complete in OpenFront Release Management May 22, 2026
@coderabbitai coderabbitai Bot mentioned this pull request May 22, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

UI/UX UI/UX changes including assets, menus, QoL, etc.

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

2 participants