From 7dd45e8dd4a0ff0a426254bb16b818504e22bc63 Mon Sep 17 00:00:00 2001
From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com>
Date: Tue, 28 Oct 2025 09:03:33 +0000
Subject: [PATCH] Bomb target location (same logic as naval invasions) (#2309)
## Description:
Added logic to hydro/atom bombs - using same FX as the boat invasion
(keeping PR smaller, can add more UX for hydro/atoms later)
Only you & teammates can see the target.
Viewpoint from two players:
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] 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:
w.o.n
---
src/client/graphics/layers/FxLayer.ts | 38 +++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/src/client/graphics/layers/FxLayer.ts b/src/client/graphics/layers/FxLayer.ts
index e5ddf3831..729458b6e 100644
--- a/src/client/graphics/layers/FxLayer.ts
+++ b/src/client/graphics/layers/FxLayer.ts
@@ -30,6 +30,7 @@ export class FxLayer implements Layer {
private allFx: Fx[] = [];
private boatTargetFxByUnitId: Map = new Map();
+ private nukeTargetFxByUnitId: Map = new Map();
constructor(private game: GameView) {
this.theme = this.game.config().theme();
@@ -87,6 +88,28 @@ export class FxLayer implements Layer {
}
}
+ // Register a persistent nuke target marker for the current player or teammates
+ private createNukeTargetFxIfOwned(unit: UnitView) {
+ const my = this.game.myPlayer();
+ if (!my) return;
+ // Show nuke marker owned by the player or by players on the same team
+ if (
+ (unit.owner() === my || my.isOnSameTeam(unit.owner())) &&
+ unit.isActive()
+ ) {
+ if (!this.nukeTargetFxByUnitId.has(unit.id())) {
+ const t = unit.targetTile();
+ if (t !== undefined) {
+ const x = this.game.x(t);
+ const y = this.game.y(t);
+ const fx = new TargetFx(x, y, 0, true);
+ this.allFx.push(fx);
+ this.nukeTargetFxByUnitId.set(unit.id(), fx);
+ }
+ }
+ }
+ }
+
onBonusEvent(bonus: BonusEventUpdate) {
if (this.game.player(bonus.player) !== this.game.myPlayer()) {
// Only display text fx for the current player
@@ -135,13 +158,19 @@ export class FxLayer implements Layer {
}
break;
}
- case UnitType.AtomBomb:
+ case UnitType.AtomBomb: {
+ this.createNukeTargetFxIfOwned(unit);
+ this.onNukeEvent(unit, 160);
+ break;
+ }
case UnitType.MIRVWarhead:
this.onNukeEvent(unit, 70);
break;
- case UnitType.HydrogenBomb:
+ case UnitType.HydrogenBomb: {
+ this.createNukeTargetFxIfOwned(unit);
this.onNukeEvent(unit, 160);
break;
+ }
case UnitType.Warship:
this.onWarshipEvent(unit);
break;
@@ -270,6 +299,11 @@ export class FxLayer implements Layer {
onNukeEvent(unit: UnitView, radius: number) {
if (!unit.isActive()) {
+ const fx = this.nukeTargetFxByUnitId.get(unit.id());
+ if (fx) {
+ fx.end();
+ this.nukeTargetFxByUnitId.delete(unit.id());
+ }
if (!unit.reachedTarget()) {
this.handleSAMInterception(unit);
} else {