Skip to content

Clear ghost structure when launching atom/hydrogen bomb if user runs out of gold#3380

Open
wozniakpl wants to merge 5 commits intoopenfrontio:mainfrom
wozniakpl:fix/clear-nuke-ghost-when-out-of-gold
Open

Clear ghost structure when launching atom/hydrogen bomb if user runs out of gold#3380
wozniakpl wants to merge 5 commits intoopenfrontio:mainfrom
wozniakpl:fix/clear-nuke-ghost-when-out-of-gold

Conversation

@wozniakpl
Copy link
Contributor

@wozniakpl wozniakpl commented Mar 8, 2026

Description:

Summary

When the atom or hydrogen bomb ghost is active and the player no longer has enough gold to place one, the ghost is now cleared instead of staying on screen with a red "can't build" outline.

Problem

If the user selected atom/hydrogen bomb placement and then ran out of gold (e.g. gold spent elsewhere or lost), the ghost stayed visible with a red outline. That was confusing because the user couldn't place a bomb but the UI still suggested the action was available.

Solution

  • In StructureIconsLayer, when buildables() returns canBuild === false for the current ghost, we check whether the ghost is a nuke (atom/hydrogen bomb) and the player's gold is below the build cost.
  • In that case we call removeGhostStructure() so the ghost is cleared and the nuke "placement mode" is exited.
  • Other "can't build" cases (e.g. no silo in range) still only show the red outline; only the "out of gold" case for nukes clears the ghost.

Changes

  • src/client/graphics/layers/StructureIconsLayer.ts
    • Added shouldClearNukeGhostWhenOutOfGold(ghostType, unit, player) and use it in renderGhost() when unit.canBuild === false to clear the ghost when the player can't afford the nuke.
  • tests/client/graphics/layers/StructureIconsLayer.test.ts
    • Added 7 tests for shouldClearNukeGhostWhenOutOfGold (nuke + insufficient gold → clear; can afford / non-nuke / can place / null player / undefined cost → no clear).

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:

.wozniakpl

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

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: a1c43117-7c02-4921-9cf6-55eae5765ab7

📥 Commits

Reviewing files that changed from the base of the PR and between 15b5caf and 141e448.

📒 Files selected for processing (1)
  • src/client/graphics/layers/StructureIconsLayer.ts

Walkthrough

Adds state and affordability checks to clear ghosted nuclear structures when the player cannot afford them, integrates those checks into render and placement flows, and adds tests for the new helper functions and scenarios.

Changes

Cohort / File(s) Summary
StructureIconsLayer logic
src/client/graphics/layers/StructureIconsLayer.ts
Add hasPlacedNukeWithCurrentGhost flag; export shouldClearNukeGhostWhenOutOfGold(...) and shouldClearNukeGhostInRenderWhenOutOfGold(...); use affordability + placement-state checks in render to clear nuke ghosts early; set/reset flag on create/clear flows.
Tests
tests/client/graphics/layers/StructureIconsLayer.test.ts
Add unit tests for both exported helpers and scenarios: nuke vs non-nuke, canBuild flag, null player, undefined cost treated as 0, gold < cost vs >= cost, and render-time behavior depending on prior placement.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StructureIconsLayer
  participant BuildableUnit
  participant Player

  Client->>StructureIconsLayer: renderGhost(ghostType, unit, player)
  StructureIconsLayer->>BuildableUnit: read unit.canBuild, unit.cost
  StructureIconsLayer->>StructureIconsLayer: shouldClearNukeGhostInRenderWhenOutOfGold(hasPlacedNukeWithCurrentGhost, ghostType, unit, player)
  StructureIconsLayer->>StructureIconsLayer: shouldClearNukeGhostWhenOutOfGold(ghostType, unit, player)
  StructureIconsLayer->>Player: player.gold() (if player)
  alt unaffordable nuke & hasPlacedNukeWithCurrentGhost
    StructureIconsLayer->>StructureIconsLayer: clearGhostStructure()
    StructureIconsLayer-->>Client: stop rendering ghost
  else affordable or non-nuke or not placed
    StructureIconsLayer-->>Client: continue ghost rendering (maybe red outline)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A phantom bunker fades from view,
When coins run low it says adieu.
A tiny flag, a check so clear,
The ghost steps back when funds aren't near. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.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 clearly and specifically describes the main change: clearing the nuke ghost when the player runs out of gold, which matches the core functionality added in the changeset.
Description check ✅ Passed The description is well-related to the changeset, explaining the problem, solution, and specific changes made to the codebase and tests.

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

🧹 Nitpick comments (1)
tests/client/graphics/layers/StructureIconsLayer.test.ts (1)

98-106: Good edge case test for TileRef vs false.

Using 0 as BuildableUnit["canBuild"] correctly tests that strict equality !== false distinguishes between a valid TileRef (even if 0) and the literal false. A brief inline comment could make this clearer for future readers:

💡 Optional: add clarifying comment
   test("returns false when unit canBuild is not false (e.g. can place)", () => {
     const unit = nukeBuildable(UnitType.AtomBomb, {
+      // 0 is a valid TileRef; strict equality (canBuild !== false) distinguishes it from literal `false`
       canBuild: 0 as BuildableUnit["canBuild"],
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/client/graphics/layers/StructureIconsLayer.test.ts` around lines 98 -
106, The test correctly uses 0 as BuildableUnit["canBuild"] to ensure strict
inequality against false; update the test surrounding
shouldClearNukeGhostWhenOutOfGold (the case using nukeBuildable and
UnitType.AtomBomb) to include a short inline comment explaining that 0 is a
valid TileRef and not the boolean false, referencing the canBuild field to
clarify intent for future readers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/client/graphics/layers/StructureIconsLayer.test.ts`:
- Around line 98-106: The test correctly uses 0 as BuildableUnit["canBuild"] to
ensure strict inequality against false; update the test surrounding
shouldClearNukeGhostWhenOutOfGold (the case using nukeBuildable and
UnitType.AtomBomb) to include a short inline comment explaining that 0 is a
valid TileRef and not the boolean false, referencing the canBuild field to
clarify intent for future readers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee7354b7-aa90-4360-a894-85010fc87f6e

📥 Commits

Reviewing files that changed from the base of the PR and between a4b3d13 and 602fc3c.

📒 Files selected for processing (2)
  • src/client/graphics/layers/StructureIconsLayer.ts
  • tests/client/graphics/layers/StructureIconsLayer.test.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
@wozniakpl wozniakpl marked this pull request as ready for review March 8, 2026 00:38
@wozniakpl wozniakpl requested a review from a team as a code owner March 8, 2026 00:38
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.

I noticed a problem, if i don't have enough for an atom bomb, then pressing "8" just flashes and disaappears.

I'm thinking we should only check after click? I'm imagining it like:

  1. click 8
  2. launch a bunch of atom bombs
  3. run out of money
  4. nuke preview exits

…d after placing

- Do not clear nuke preview in render path when user has not placed a nuke yet
  (press 8 with no gold shows red outline, no flash)
- Clear nuke ghost only when user has placed at least one nuke and then runs
  out of gold (preview exits after last affordable nuke)
- Add hasPlacedNukeWithCurrentGhost flag, shouldClearNukeGhostInRenderWhenOutOfGold
- Simplify createStructure ghost-keep logic with keepGhostForMultiplePlacement
- Add tests for both no-flash and clear-when-out-of-gold cases
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/StructureIconsLayer.ts`:
- Around line 140-141: The hasPlacedNukeWithCurrentGhost flag can become stale
because uiState.ghostStructure is changed elsewhere; modify the shared
ghost-selection/change path to clear this flag whenever the ghost is changed
(i.e., whenever uiState.ghostStructure is set/cleared), not only in
clearGhostStructure(); locate the central function or handler that updates
uiState.ghostStructure (also update the other similar spot referenced around
600-602) and add a reset of hasPlacedNukeWithCurrentGhost = false there so a
newly-selected ghost is never treated as already-placed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4110464b-6b2b-400d-a477-38d52aa70510

📥 Commits

Reviewing files that changed from the base of the PR and between 602fc3c and 15b5caf.

📒 Files selected for processing (2)
  • src/client/graphics/layers/StructureIconsLayer.ts
  • tests/client/graphics/layers/StructureIconsLayer.test.ts

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Mar 8, 2026
@wozniakpl
Copy link
Contributor Author

I noticed a problem, if i don't have enough for an atom bomb, then pressing "8" just flashes and disaappears.

I'm thinking we should only check after click? I'm imagining it like:

  1. click 8
  2. launch a bunch of atom bombs
  3. run out of money
  4. nuke preview exits

Covered in shouldClearNukeGhostInRenderWhenOutOfGold.

Comment on lines +63 to +65
if (ghostType !== UnitType.AtomBomb && ghostType !== UnitType.HydrogenBomb) {
return false;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was playing the beta tests today, I actually think it should dissappear when you can't build a nuke, regardless of wether your silo is reloading or you run out of money.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean I quite like to have the ghost structure (as do many others) to know how to plan the bomb,

also if I am like 20k gold off, I wanna get my bomb ready and send it the second I get enough

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 you should be able to open the ghost structure, it should just disappear after you shoot the atom bomb and can't shoot another one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

3 participants