add target visualization for boat attacks (#2025)

## Description:
- Adds warship count, transport count (deployed out of maximum) to unit
display
- Adds a target that appears when a boat attack is dispatched, which
disappears when the boat attack arrives
- Updates the unit display alt text to pass through translation

## Please complete the following:

- [X] I have added screenshots for all UI updates (see below)
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file (in this case it is only alt-text)
- [X] I have added relevant tests to the test directory (n/a, fully
visual)
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

See new target effect and addition to units display 

As each transport ship arrives, the target draw stops, together with the
effect for the trail.


https://github.com/user-attachments/assets/c36c57d3-e2b7-456e-85ab-1e786bd28a07

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

@dxtron_28992 (my invite is still pending to dev discord)
This commit is contained in:
Will Dunlop
2025-09-24 11:08:52 -07:00
committed by GitHub
parent 019a9dc57c
commit eeb9f0c279
3 changed files with 117 additions and 0 deletions
+43
View File
@@ -13,6 +13,7 @@ import { conquestFxFactory } from "../fx/ConquestFx";
import { Fx, FxType } from "../fx/Fx";
import { nukeFxFactory, ShockwaveFx } from "../fx/NukeFx";
import { SpriteFx } from "../fx/SpriteFx";
import { TargetFx } from "../fx/TargetFx";
import { TextFx } from "../fx/TextFx";
import { UnitExplosionFx } from "../fx/UnitExplosionFx";
import { Layer } from "./Layer";
@@ -27,6 +28,7 @@ export class FxLayer implements Layer {
new AnimatedSpriteLoader();
private allFx: Fx[] = [];
private boatTargetFxByUnitId: Map<number, TargetFx> = new Map();
constructor(private game: GameView) {
this.theme = this.game.config().theme();
@@ -37,6 +39,7 @@ export class FxLayer implements Layer {
}
tick() {
this.manageBoatTargetFx();
this.game
.updatesSinceLastTick()
?.[GameUpdateType.Unit]?.map((unit) => this.game.unit(unit.id))
@@ -65,6 +68,24 @@ export class FxLayer implements Layer {
});
}
private manageBoatTargetFx() {
// End markers for boats that arrived or retreated
for (const [unitId, fx] of Array.from(
this.boatTargetFxByUnitId.entries(),
)) {
const unit = this.game.unit(unitId);
if (
!unit ||
!unit.isActive() ||
unit.reachedTarget() ||
unit.retreating()
) {
(fx as any).end?.();
this.boatTargetFxByUnitId.delete(unitId);
}
}
}
onBonusEvent(bonus: BonusEventUpdate) {
if (this.game.player(bonus.player) !== this.game.myPlayer()) {
// Only display text fx for the current player
@@ -94,8 +115,30 @@ export class FxLayer implements Layer {
this.allFx.push(textFx);
}
addTargetFx(x: number, y: number) {
const fx = new TargetFx(x, y, 1200, 12);
this.allFx.push(fx);
}
onUnitEvent(unit: UnitView) {
switch (unit.type()) {
case UnitType.TransportShip: {
const my = this.game.myPlayer();
if (!my) return;
if (unit.owner() !== my) return;
if (!unit.isActive()) return;
if (this.boatTargetFxByUnitId.has(unit.id())) return;
const t = unit.targetTile();
if (t !== undefined) {
const x = this.game.x(t);
const y = this.game.y(t);
// persistent until boat finishes or retreats
const fx = new TargetFx(x, y, 0, 12, true);
this.allFx.push(fx);
this.boatTargetFxByUnitId.set(unit.id(), fx);
}
break;
}
case UnitType.AtomBomb:
case UnitType.MIRVWarhead:
this.onNukeEvent(unit, 70);