Set a targetable status for nukes (#1174)

## Description:

Set a targetable status for units (specifically atom bomb and hydro)
A nuke is targetable near launch and target but is untargetable mid air.
An untargetable unit is half transparent to show that it cannot be
destroyed.


![targetable](https://github.com/user-attachments/assets/cc6769ff-95ab-4294-9a8e-10f909711f68)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

Vivacious Box
This commit is contained in:
Vivacious Box
2025-06-15 08:23:13 +02:00
committed by GitHub
parent 4317285b17
commit 7fa11ed035
9 changed files with 107 additions and 16 deletions
+8
View File
@@ -535,6 +535,11 @@ export class UnitLayer implements Layer {
);
if (unit.isActive()) {
const targetable = unit.targetable();
if (!targetable) {
this.context.save();
this.context.globalAlpha = 0.4;
}
this.context.drawImage(
sprite,
Math.round(x - sprite.width / 2),
@@ -542,6 +547,9 @@ export class UnitLayer implements Layer {
sprite.width,
sprite.width,
);
if (!targetable) {
this.context.restore();
}
}
}
}
+24
View File
@@ -13,6 +13,8 @@ import { ParabolaPathFinder } from "../pathfinding/PathFinding";
import { PseudoRandom } from "../PseudoRandom";
import { NukeType } from "../StatsSchemas";
const NUKE_TARGETABLE_RADIUS = 120;
const SPRITE_RADIUS = 16;
export class NukeExecution implements Execution {
@@ -99,6 +101,7 @@ export class NukeExecution implements Execution {
this.active = false;
return;
}
this.src = spawn;
this.pathFinder.computeControlPoints(
spawn,
this.dst,
@@ -163,10 +166,31 @@ export class NukeExecution implements Execution {
this.detonate();
return;
} else {
this.updateNukeTargetable();
this.nuke.move(nextTile);
}
}
public getNuke(): Unit | null {
return this.nuke;
}
private updateNukeTargetable() {
if (this.nuke === null || this.nuke.targetTile() === undefined) {
return;
}
const targetRangeSquared = NUKE_TARGETABLE_RADIUS * NUKE_TARGETABLE_RADIUS;
const targetTile = this.nuke.targetTile();
this.nuke.setTargetable(
this.mg.euclideanDistSquared(this.nuke.tile(), targetTile!) <
targetRangeSquared ||
(this.src !== undefined &&
this.src !== null &&
this.mg.euclideanDistSquared(this.src, this.nuke.tile()) <
targetRangeSquared),
);
}
private detonate() {
if (this.nuke === null) {
throw new Error("Not initialized");
+1 -13
View File
@@ -37,18 +37,6 @@ export class SAMLauncherExecution implements Execution {
this.mg = mg;
}
private nukeTargetInRange(nuke: Unit) {
const targetTile = nuke.targetTile();
if (this.sam === null || targetTile === undefined) {
return false;
}
const targetRangeSquared = this.targetRangeRadius * this.targetRangeRadius;
return (
this.mg.euclideanDistSquared(this.sam.tile(), targetTile) <
targetRangeSquared
);
}
private getSingleTarget(): Unit | null {
if (this.sam === null) return null;
const nukes = this.mg
@@ -60,7 +48,7 @@ export class SAMLauncherExecution implements Execution {
({ unit }) =>
unit.owner() !== this.player &&
!this.player.isFriendly(unit.owner()) &&
this.nukeTargetInRange(unit),
unit.isTargetable(),
);
return (
+2
View File
@@ -383,6 +383,8 @@ export interface Unit {
targetedBySAM(): boolean;
setReachedTarget(): void;
reachedTarget(): boolean;
isTargetable(): boolean;
setTargetable(targetable: boolean): void;
// Health
hasHealth(): boolean;
+1
View File
@@ -76,6 +76,7 @@ export interface UnitUpdate {
isActive: boolean;
reachedTarget: boolean;
retreating: boolean;
targetable: boolean;
targetUnitId?: number; // Only for trade ships
targetTile?: TileRef; // Only for nukes
health?: number;
+4
View File
@@ -72,6 +72,10 @@ export class UnitView {
return this.data.id;
}
targetable(): boolean {
return this.data.targetable;
}
type(): UnitType {
return this.data.unitType;
}
+14
View File
@@ -30,6 +30,8 @@ export class UnitImpl implements Unit {
private _readyMissileCount: number = 1;
private _patrolTile: TileRef | undefined;
private _level: number = 1;
private _targetable: boolean = true;
constructor(
private _type: UnitType,
private mg: GameImpl,
@@ -63,6 +65,17 @@ export class UnitImpl implements Unit {
}
}
setTargetable(targetable: boolean): void {
if (this._targetable !== targetable) {
this._targetable = targetable;
this.mg.addUpdate(this.toUpdate());
}
}
isTargetable(): boolean {
return this._targetable;
}
setPatrolTile(tile: TileRef): void {
this._patrolTile = tile;
}
@@ -101,6 +114,7 @@ export class UnitImpl implements Unit {
reachedTarget: this._reachedTarget,
retreating: this._retreating,
pos: this._tile,
targetable: this._targetable,
lastPos: this._lastTile,
health: this.hasHealth() ? Number(this._health) : undefined,
constructionType: this._constructionType,