Skip to content

Conversation

@BCNelson
Copy link

@BCNelson BCNelson commented Nov 30, 2025

Description:

Screencast_20251130_101359.webm
  • Show patrol area when warship is selected (solid line)
  • Show preview area at cursor when selecting new patrol location (dashed line)
  • ESC key deselects warship
  • Expose patrolTile to client via UnitUpdate for visualization

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:

bcnelson

- Show patrol area circle when warship is selected (solid line)
- Show preview circle at cursor when selecting new patrol location (dashed line)
- ESC key deselects warship
- Expose patrolTile to client via UnitUpdate for visualization
@BCNelson BCNelson requested a review from a team as a code owner November 30, 2025 08:01
@CLAassistant
Copy link

CLAassistant commented Nov 30, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 2025

Walkthrough

Adds a new WarshipRadiusLayer that renders selected and placement patrol indicators, wires it into the renderer layer stack, subscribes selection/mouse events, and propagates a new optional patrolTile field through unit update/view/serialization.

Changes

Cohort / File(s) Summary
Rendering layer added
src/client/graphics/layers/WarshipRadiusLayer.ts
New exported WarshipRadiusLayer class: tracks selected warship and ghost placement, converts screen→world coords, renders solid patrol square for selection and animated dashed preview during placement, subscribes to UnitSelectionEvent and MouseMoveEvent, implements tick/redraw and exposes renderLayer.
Renderer integration
src/client/graphics/GameRenderer.ts
Imports and instantiates WarshipRadiusLayer, inserting it into the renderer layers array between the SAM radius layer and UnitLayer, so it participates in tick/render ordering.
Unit layer event handling
src/client/graphics/layers/UnitLayer.ts
Adds CloseViewEvent import and subscription; adds onCloseView handler which deselects the currently selected unit by emitting UnitSelectionEvent(selected=false).
Unit data model & view
src/core/game/GameUpdates.ts, src/core/game/GameView.ts, src/core/game/UnitImpl.ts
Adds optional patrolTile?: TileRef to UnitUpdate, exposes patrolTile() accessor on UnitView, and includes patrolTile in UnitImpl.toUpdate() so patrol center is serialized to clients.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review rendering math and screen↔world transforms and coordinate rounding in WarshipRadiusLayer.ts.
  • Check tick/redraw gating and dash animation to avoid extra CPU draws.
  • Verify events: UnitSelectionEvent, MouseMoveEvent, CloseViewEvent interactions and ordering with UnitLayer.
  • Confirm patrolTile serialization matches existing UnitUpdate patterns and typing.

Possibly related PRs

Suggested labels

Feature - New, UI/UX

Suggested reviewers

  • evanpelle
  • scottanderson

Poem

⚓ A dashed square dances where the cursor goes,
A solid watch keeps where the patrol rose.
Selection hums, the preview spins,
Small waves of code where rendering begins. 🎨

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a visual indicator for warship patrol areas, which is the primary feature across all modified files.
Description check ✅ Passed The description is directly related to the changeset, covering the main features (solid patrol area display, dashed preview, ESC deselection) and the supporting data exposure through UnitUpdate.

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.

Copy link
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab53ee6 and bc66a6e.

📒 Files selected for processing (6)
  • src/client/graphics/GameRenderer.ts (3 hunks)
  • src/client/graphics/layers/UnitLayer.ts (3 hunks)
  • src/client/graphics/layers/WarshipRadiusLayer.ts (1 hunks)
  • src/core/game/GameUpdates.ts (1 hunks)
  • src/core/game/GameView.ts (1 hunks)
  • src/core/game/UnitImpl.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-26T20:49:29.110Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2519
File: src/core/game/GameView.ts:516-525
Timestamp: 2025-11-26T20:49:29.110Z
Learning: In GameView.ts, when usesSharedTileState is true (SAB mode), packedTileUpdates contains unpacked tile references as BigInt(tileRef) only, because all tile state lives in the shared Uint16Array. In non-SAB mode, packedTileUpdates contains packed TileUpdate bigints in the format (tileRef << 16n | state), which must be decoded via updateTile(tu). Therefore, Number(tu) is correct in SAB mode and shifting right by 16 bits would be wrong.

Applied to files:

  • src/core/game/GameView.ts
  • src/core/game/UnitImpl.ts
  • src/core/game/GameUpdates.ts
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.

Applied to files:

  • src/client/graphics/layers/WarshipRadiusLayer.ts
  • src/client/graphics/GameRenderer.ts
🧬 Code graph analysis (5)
src/core/game/GameView.ts (1)
src/core/game/GameMap.ts (1)
  • TileRef (3-3)
src/client/graphics/layers/UnitLayer.ts (1)
src/client/InputHandler.ts (2)
  • CloseViewEvent (78-78)
  • UnitSelectionEvent (31-36)
src/client/graphics/layers/WarshipRadiusLayer.ts (6)
src/core/game/GameView.ts (5)
  • UnitView (44-182)
  • GameView (462-788)
  • patrolTile (133-135)
  • x (685-687)
  • y (688-690)
src/server/GameManager.ts (1)
  • game (24-26)
src/client/graphics/TransformHandler.ts (1)
  • TransformHandler (15-333)
src/client/graphics/UIState.ts (1)
  • UIState (3-6)
src/client/InputHandler.ts (2)
  • UnitSelectionEvent (31-36)
  • MouseMoveEvent (45-50)
src/core/game/UnitImpl.ts (1)
  • patrolTile (97-99)
src/core/game/GameUpdates.ts (1)
src/core/game/GameMap.ts (1)
  • TileRef (3-3)
src/client/graphics/GameRenderer.ts (1)
src/client/graphics/layers/WarshipRadiusLayer.ts (1)
  • WarshipRadiusLayer (15-189)
🔇 Additional comments (8)
src/client/graphics/GameRenderer.ts (1)

214-219: LGTM! Consistent with existing layer patterns.

The WarshipRadiusLayer is instantiated and inserted in the correct position, following the same pattern as SAMRadiusLayer. The layer ordering (radius layers before unit layer) makes sense for visual layering.

Also applies to: 252-252

src/core/game/GameView.ts (1)

133-135: LGTM! Clean accessor following existing patterns.

The patrolTile() accessor matches the style of targetTile() directly above it.

src/core/game/UnitImpl.ts (1)

143-143: LGTM! Proper serialization of patrol tile.

The patrolTile field is correctly added to the update payload, matching the pattern of other optional fields like targetTile.

src/core/game/GameUpdates.ts (1)

137-137: LGTM! Clear interface extension with good documentation.

The patrolTile field follows the existing pattern of optional unit-specific fields with a helpful comment.

src/client/graphics/layers/UnitLayer.ts (1)

194-201: LGTM! Simple and effective ESC deselection handler.

The onCloseView method cleanly handles ESC key deselection by emitting a UnitSelectionEvent. The existing onUnitSelectionChange handler will update the state correctly.

src/client/graphics/layers/WarshipRadiusLayer.ts (3)

1-57: Good layer setup following existing patterns.

Clean imports with proper type annotations. Canvas setup and event subscriptions follow the established layer patterns (similar to SAMRadiusLayer).


59-94: Tick and animation logic is well-structured.

The animation timing, ghost mode detection, and destroyed warship cleanup are all handled correctly. The modulo guard on dashOffset prevents numeric overflow over long sessions.


133-188: Clean drawing implementation.

The redraw(), drawCurrentPatrol(), and drawPreviewPatrol() methods are straightforward and follow canvas best practices with proper save()/restore() pairs.

@bibizu
Copy link
Contributor

bibizu commented Nov 30, 2025

Good idea. I thought warships patrol square areas instead of circle ones though?

Warships patrol in a square area (random x/y within ±range/2), not a
circular one. Updated the visual indicator to match the actual behavior.
Copy link
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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f29264 and 2a4947b.

📒 Files selected for processing (1)
  • src/client/graphics/layers/WarshipRadiusLayer.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.

Applied to files:

  • src/client/graphics/layers/WarshipRadiusLayer.ts
📚 Learning: 2025-06-16T03:03:59.778Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1192
File: src/client/graphics/layers/RadialMenuElements.ts:312-314
Timestamp: 2025-06-16T03:03:59.778Z
Learning: In RadialMenuElements.ts, when fixing undefined params errors in subMenu functions, use explicit checks like `if (params === undefined || params.selected === null)` rather than optional chaining, as it makes the intent clearer and matches the specific error scenarios where params can be undefined during spawn phase operations.

Applied to files:

  • src/client/graphics/layers/WarshipRadiusLayer.ts
📚 Learning: 2025-10-27T09:47:26.395Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:770-795
Timestamp: 2025-10-27T09:47:26.395Z
Learning: In src/core/execution/FakeHumanExecution.ts, the selectSteamrollStopTarget() method intentionally allows MIRV targeting when secondHighest city count is 0 (e.g., nuclear endgame scenarios where structures are destroyed). This is valid game design—"if you can afford it, you're good to go"—and should not be flagged as requiring a guard condition.

Applied to files:

  • src/client/graphics/layers/WarshipRadiusLayer.ts
📚 Learning: 2025-06-22T21:51:14.990Z
Learnt from: devalnor
Repo: openfrontio/OpenFrontIO PR: 1248
File: src/client/graphics/layers/TerritoryInfoLayer.ts:20-20
Timestamp: 2025-06-22T21:51:14.990Z
Learning: In TerritoryInfoLayer.ts, the highlightedTerritory field uses both null and undefined intentionally: undefined represents initial state or inactive layer (Ctrl released), while null represents active layer with no territory being highlighted at cursor position. This distinction is important for proper state change detection.

Applied to files:

  • src/client/graphics/layers/WarshipRadiusLayer.ts
🧬 Code graph analysis (1)
src/client/graphics/layers/WarshipRadiusLayer.ts (1)
src/client/graphics/UIState.ts (1)
  • UIState (3-6)
🔇 Additional comments (8)
src/client/graphics/layers/WarshipRadiusLayer.ts (8)

1-7: LGTM!

Imports are clean and necessary for the layer implementation.


9-31: LGTM!

Field declarations are well-organized with clear state tracking, animation, and cursor management. The note about matching SAMRadiusLayer's animation speed is helpful for consistency.


33-47: LGTM!

Constructor properly initializes canvas and context with good error handling.


49-57: LGTM!

Standard layer initialization with proper event subscriptions and initial redraw.


96-104: LGTM!

Standard layer rendering implementation with correct centered coordinate offset.


117-131: LGTM!

Mouse handling logic is correct with proper early returns and coordinate conversion.


133-150: LGTM!

Redraw logic correctly handles both solid patrol area and dashed preview with proper conditional rendering and defensive checks.


59-94: Verify that isActive() method exists on UnitView.

The tick method calls this.selectedWarship.isActive() on line 71. Ensure this method exists on the UnitView interface/class and returns a boolean indicating whether the unit is still active.

@BCNelson BCNelson changed the title Add warship patrol radius indicator Add warship patrol area indicator Nov 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants