Files
OpenFrontIO/src/client/graphics/layers/DynamicUILayer.ts
T
Vivacious Box ee4b91a422 Fix nuke telegraph for allies (#2983)
## Description:

Adds back the nuke overlay for teammates

## 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:

Mr. Box
2026-01-21 15:13:55 -08:00

188 lines
5.1 KiB
TypeScript

import { renderNumber } from "src/client/Utils";
import { EventBus } from "src/core/EventBus";
import { UnitType } from "src/core/game/Game";
import {
BonusEventUpdate,
ConquestUpdate,
GameUpdateType,
} from "src/core/game/GameUpdates";
import type { GameView, UnitView } from "../../../core/game/GameView";
import { MoveWarshipIntentEvent } from "../../Transport";
import { TransformHandler } from "../TransformHandler";
import { MoveIndicatorUI } from "../ui/MoveIndicatorUI";
import { NavalTarget } from "../ui/NavalTarget";
import { NukeTelegraph } from "../ui/NukeTelegraph";
import { TextIndicator } from "../ui/TextIndicator";
import { UIElement } from "../ui/UIElement";
import { Layer } from "./Layer";
const TEXT_OFFSET_Y = -5;
const TEXT_STACK_SPACING = 8;
const TEXT_DURATION = 2500;
export class DynamicUILayer implements Layer {
private readonly uiElements: Array<UIElement> = [];
private lastRefresh = Date.now();
constructor(
private readonly game: GameView,
private transformHandler: TransformHandler,
private eventBus: EventBus,
) {}
init() {
// Listen for warship move clicks for MoveIndicatorUI
this.eventBus.on(MoveWarshipIntentEvent, (e) => {
const x = this.game.x(e.tile);
const y = this.game.y(e.tile);
this.uiElements.push(new MoveIndicatorUI(this.transformHandler, x, y));
});
}
shouldTransform(): boolean {
return false;
}
tick() {
if (!this.game.config().userSettings()?.fxLayer()) {
return;
}
const updates = this.game.updatesSinceLastTick();
if (!updates) return;
updates[GameUpdateType.Unit]?.forEach((unit) => {
const unitView = this.game.unit(unit.id);
if (!unitView) return;
this.onUnitEvent(unitView);
});
updates[GameUpdateType.BonusEvent]?.forEach((bonusEvent) => {
if (bonusEvent === undefined) return;
this.onBonusEvent(bonusEvent);
});
updates[GameUpdateType.ConquestEvent]?.forEach((update) => {
if (update === undefined) return;
this.onConquestEvent(update);
});
}
onBonusEvent(bonus: BonusEventUpdate) {
// Only display text fx for the current player
if (this.game.player(bonus.player) !== this.game.myPlayer()) {
return;
}
const tile = bonus.tile;
const x = this.game.x(tile);
let y = this.game.y(tile) + TEXT_OFFSET_Y;
const gold = bonus.gold;
const troops = bonus.troops;
if (gold !== 0) {
this.addNumber(gold, x, y, 1000, 10);
y += TEXT_STACK_SPACING; // increase y so the next popup starts below
}
if (troops !== 0) {
this.addNumber(troops, x, y, 1000, 10);
}
}
onConquestEvent(conquest: ConquestUpdate) {
// Only display text for the current player
const conqueror = this.game.player(conquest.conquerorId);
if (conqueror !== this.game.myPlayer()) {
return;
}
const nameLocation = this.game.player(conquest.conqueredId).nameLocation();
const x = nameLocation.x;
const y = nameLocation.y;
this.addNumber(conquest.gold, x, y + 8, TEXT_DURATION, 0);
}
onUnitEvent(unit: UnitView) {
switch (unit.type()) {
case UnitType.HydrogenBomb:
case UnitType.AtomBomb: {
this.onBombEvent(unit);
break;
}
case UnitType.TransportShip: {
this.onTransportShipEvent(unit);
break;
}
}
}
onBombEvent(unit: UnitView) {
const myPlayer = this.game.myPlayer();
if (!myPlayer) {
return;
}
if (
this.createdThisTick(unit) &&
(unit.owner() === myPlayer || unit.owner().isOnSameTeam(myPlayer))
) {
const target = new NukeTelegraph(this.transformHandler, this.game, unit);
this.uiElements.push(target);
}
}
onTransportShipEvent(unit: UnitView) {
const myPlayer = this.game.myPlayer();
if (!myPlayer) {
return;
}
if (this.createdThisTick(unit) && unit.owner() === myPlayer) {
const target = new NavalTarget(this.transformHandler, this.game, unit);
this.uiElements.push(target);
}
}
renderLayer(context: CanvasRenderingContext2D) {
const now = Date.now();
const dt = now - this.lastRefresh;
this.lastRefresh = now;
if (this.game.config().userSettings()?.fxLayer()) {
this.renderUIElements(context, dt);
}
}
renderUIElements(context: CanvasRenderingContext2D, delta: number) {
for (let i = this.uiElements.length - 1; i >= 0; i--) {
if (!this.uiElements[i].render(context, delta)) {
this.uiElements.splice(i, 1);
}
}
}
private createdThisTick(unit: UnitView): boolean {
return unit.createdAt() === this.game.ticks();
}
private addNumber(
num: bigint | number,
x: number,
y: number,
duration: number,
riseDistance: number,
) {
if (BigInt(num) === 0n) return; // Don't show anything for 0
const absNum =
typeof num === "bigint" ? (num < 0n ? -num : num) : Math.abs(num);
const shortened = renderNumber(absNum, 0);
const sign = num >= 0 ? "+" : "-";
this.uiElements.push(
new TextIndicator(
this.transformHandler,
`${sign} ${shortened}`,
x,
y,
duration,
riseDistance,
),
);
}
}