Skip to content

Conversation

@bibizu
Copy link
Contributor

@bibizu bibizu commented Jan 17, 2026

Description:

Adds a new layer, NukeRenderUtilLayer, which does not draw anything but consolidates all nuke prediction code to be used by trajectory drawing layer, SAM radii drawing layer, and structure icon layer rather than recalculated each time. The layer is calculated first, and a reference of this layer is passed to all layers needing it.

pr_ex.1.mp4

SAM radii now change color/transparency when in "nuke mode":

  • Opaque and highlighted radii means the owner will intercept this nuke.
  • Other opaque radii means that the owner is "stressed" by this nuke, and launching it will break alliances, or owner is enemy.
  • Orange radii symbolize SAMs who are friendly but will break alliance if nuke is launched.

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:

bibizu

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Walkthrough

A new NukeRenderUtilLayer centralizes nuke trajectory, spawn selection, interceptor detection, and alliance-impact state. GameRenderer creates and injects it into dependent render layers (StructureIconsLayer, SAMRadiusLayer, NukeTrajectoryPreviewLayer), which now consume its state instead of recomputing trajectories or alliance checks.

Changes

Cohort / File(s) Summary
Core utilities & execution
src/core/execution/Util.ts, src/core/execution/NukeExecution.ts
Added listNukeBreakAlliance and extended NukeAllianceCheckParams (accepts `Game
New render util layer
src/client/graphics/layers/NukeRenderUtilLayer.ts
New NukeRenderUtilLayer that listens to input events, computes trajectory/spawn/interceptor data, tracks alliance-stressed players, and exposes init(), tick(), renderLayer() and getters for trajectory and player sets.
Renderer wiring
src/client/graphics/GameRenderer.ts
Instantiates NukeRenderUtilLayer, places it early in the layers array, and injects it into dependent layers (StructureIconsLayer, SAMRadiusLayer, NukeTrajectoryPreviewLayer).
Trajectory preview simplified
src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts
Constructor changed to (game, nukeRenderUtilLayer); removed local trajectory/event logic and now renders using data from NukeRenderUtilLayer.
SAM radius rendering
src/client/graphics/layers/SAMRadiusLayer.ts
Constructor now accepts NukeRenderUtilLayer; added theme usage and colord import; rendering adapts stroke/fill colors for nuke-mode, stressed allies, and intercepting players.
Structure icons nuke logic
src/client/graphics/layers/StructureIconsLayer.ts
Injects NukeRenderUtilLayer and replaces wouldNukeBreakAlliance checks with getAllianceStressedPlayers() membership checks to determine targeting-ally state.

Sequence Diagram(s)

sequenceDiagram
  participant GR as GameRenderer
  participant EB as EventBus
  participant NR as NukeRenderUtilLayer
  participant CV as CanvasLoop
  participant SR as SAMRadiusLayer
  participant SI as StructureIconsLayer
  participant NT as NukeTrajectoryPreviewLayer

  GR->>NR: new NukeRenderUtilLayer(game, eventBus, uiState, transform)
  GR->>SR: construct(..., nukeRenderUtilLayer)
  GR->>SI: construct(..., nukeRenderUtilLayer)
  GR->>NT: construct(game, nukeRenderUtilLayer)

  EB->>NR: MouseMove / GhostStructureChanged / SwapRocketDirection
  NR->>NR: compute trajectory, spawnTile, interceptors, stressedPlayers

  CV->>NR: tick() / renderLayer()
  CV->>SR: render() -> NR.getInterceptingPlayers(), NR.isNukeGhostActive()
  CV->>SI: render() -> NR.getAllianceStressedPlayers()
  CV->>NT: render() -> NR.getTrajectoryInfo()
  SR->>CV: draw SAM arcs (theme + nuke-mode)
  SI->>CV: draw ghost icons (ally-stressed)
  NT->>CV: draw trajectory preview
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🌠 A single layer maps the rocket's flight,
Counts friends and foes in soft twilight,
Many renderers read one truth,
One source of sky for every view,
Small code, big picture — clear and bright.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: introducing NukeRenderUtilLayer and updating SAM visual feedback based on nuke prediction.
Description check ✅ Passed The description clearly explains the purpose of NukeRenderUtilLayer, how it's shared across dependent layers, and details the visual changes to SAM radii in nuke mode.

✏️ 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.

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

🤖 Fix all issues with AI agents
In `@src/client/graphics/layers/NukeRenderUtilLayer.ts`:
- Around line 255-259: The early return in renderLayer (after checking
this.findTargetTile() === null || !this.spawnTile) leaves stale state in
this.allianceStressedPlayers and this.interceptingPlayers; update the block so
that before returning you also clear this.allianceStressedPlayers = [] and
this.interceptingPlayers = [] (and keep clearing this.trajectoryPoints = [] as
already done) so no old colors or intercept markers remain when there is no
valid target or spawnTile.
- Around line 54-64: When ghost structure changes (in the
GhostStructureChangedEvent handler) reset the cached target by setting
lastTargetTile = null (like SwapRocketDirectionEvent does) and also make
trajectoryPreviewTick resilient to stale async results by recording the
currentGhostStructure (or a request ID) before awaiting player.actions() and,
after the await, verify that currentGhostStructure (and any relevant request ID)
still matches before assigning spawnTile; update both the
GhostStructureChangedEvent handler (references: currentGhostStructure,
nukeGhostActive, lastTargetTile) and the async logic inside
trajectoryPreviewTick (references: player.actions(), spawnTile) — apply the same
guard for the other similar block noted (lines ~105-127).
🧹 Nitpick comments (1)
src/client/graphics/layers/SAMRadiusLayer.ts (1)

274-349: Check intercepting opacity for allies/self.
In nuke mode, friendly/self inline strokes stay translucent unless stressed is true. If getInterceptingPlayers() can include allies or self, the intercepting cue becomes too subtle. Please confirm the set excludes them; if not, make intercepting force full opacity. Also, nukeMode / stressed / intercepting are constant per SAM, so you can compute them once outside the arc loop to avoid repeated lookups.

Possible tweak if intercepting can include allies/self
-        if (a.owner.isMe()) {
-          ctx.strokeStyle = lineColorSelf.alpha(0.3).toRgbString();
-        } else if (this.game.myPlayer()?.isFriendly(a.owner)) {
-          if (stressed) {
-            ctx.strokeStyle = lineColorStressed.toRgbString();
-          } else {
-            ctx.strokeStyle = lineColorFriend.alpha(0.3).toRgbString();
-          }
-        } else {
-          if (intercepting || stressed) {
-            ctx.strokeStyle = lineColorEnemy.toRgbString();
-          } else {
-            ctx.strokeStyle = lineColorEnemy.alpha(0.3).toRgbString();
-          }
-        }
+        if (a.owner.isMe()) {
+          ctx.strokeStyle = (
+            intercepting ? lineColorSelf : lineColorSelf.alpha(0.3)
+          ).toRgbString();
+        } else if (this.game.myPlayer()?.isFriendly(a.owner)) {
+          if (stressed) {
+            ctx.strokeStyle = lineColorStressed.toRgbString();
+          } else if (intercepting) {
+            ctx.strokeStyle = lineColorFriend.toRgbString();
+          } else {
+            ctx.strokeStyle = lineColorFriend.alpha(0.3).toRgbString();
+          }
+        } else {
+          if (intercepting || stressed) {
+            ctx.strokeStyle = lineColorEnemy.toRgbString();
+          } else {
+            ctx.strokeStyle = lineColorEnemy.alpha(0.3).toRgbString();
+          }
+        }

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

🤖 Fix all issues with AI agents
In `@src/client/graphics/layers/NukeRenderUtilLayer.ts`:
- Around line 273-276: When the early return after calling this.game.myPlayer()
executes, ensure you clear any lingering state to avoid stale data: reset
this.allianceStressedPlayers and any trajectory-related state (e.g.
this.trajectory*, trajectoryPoints, collision/preview state) before returning.
Update the method in NukeRenderUtilLayer where you call this.game.myPlayer() so
that on !player you explicitly clear those fields and then return.
🧹 Nitpick comments (1)
src/client/graphics/layers/NukeRenderUtilLayer.ts (1)

267-267: Prefix unused parameter with underscore.

The context parameter is intentionally unused since this is a utility layer that doesn't draw. Prefix it with underscore to signal intent and silence linter warnings.

Suggested fix
-  renderLayer(context: CanvasRenderingContext2D) {
+  renderLayer(_context: CanvasRenderingContext2D) {

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 20, 2026
@bibizu bibizu marked this pull request as ready for review January 20, 2026 17:38
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 20, 2026
@evanpelle evanpelle requested a review from a team as a code owner January 22, 2026 23:08
@evanpelle evanpelle added this to the v30 milestone Jan 22, 2026
Copy link
Collaborator

@evanpelle evanpelle left a comment

Choose a reason for hiding this comment

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

Let's make the friendly SAMs yellow, since that's what we use for friendly trade ships

Comment on lines +273 to +274
new SAMRadiusLayer(game, eventBus, uiState, nukeRenderUtilLayer),
new NukeTrajectoryPreviewLayer(game, nukeRenderUtilLayer),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it would be a bit cleaner to merge these two layers into one layer, and then that layer has a nukeRenderUtil layer that it calls into?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Structure Icon Layer was also running more or less the same logic, so there are three layers that all require the same calculation. This also makes it easy if I want to add more layers as well needing it as well. (such as I might wanna work on showing which structures in StructureLayer get destroyed by the nuke).

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.

2 participants