diff --git a/resources/images/ExclamationMarkIcon.svg b/resources/images/ExclamationMarkIcon.svg
deleted file mode 100644
index 8c417fc73..000000000
--- a/resources/images/ExclamationMarkIcon.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts
index 2ccccb39a..1410cdbbd 100644
--- a/src/client/graphics/GameRenderer.ts
+++ b/src/client/graphics/GameRenderer.ts
@@ -24,7 +24,6 @@ import { MainRadialMenu } from "./layers/MainRadialMenu";
import { MultiTabModal } from "./layers/MultiTabModal";
import { NameLayer } from "./layers/NameLayer";
import { NukeTrajectoryPreviewLayer } from "./layers/NukeTrajectoryPreviewLayer";
-import { PingTrajectoryPreviewLayer } from "./layers/PingTrajectoryPreviewLayer";
import { PerformanceOverlay } from "./layers/PerformanceOverlay";
import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay";
import { PlayerPanel } from "./layers/PlayerPanel";
@@ -205,13 +204,13 @@ export function createRenderer(
headsUpMessage.game = game;
const structureLayer = new StructureLayer(game, eventBus, transformHandler);
- const samRadiusLayer = new SAMRadiusLayer(
- game,
- eventBus,
- transformHandler,
- uiState,
- );
- const pingTrajectoryPreviewLayer = new PingTrajectoryPreviewLayer(game, eventBus, transformHandler);
+ const samRadiusLayer = new SAMRadiusLayer(
+ game,
+ eventBus,
+ transformHandler,
+ uiState,
+ );
+
const performanceOverlay = document.querySelector(
"performance-overlay",
) as PerformanceOverlay;
@@ -244,10 +243,9 @@ export function createRenderer(
structureLayer,
samRadiusLayer,
new UnitLayer(game, eventBus, transformHandler),
- new FxLayer(game, eventBus),
+ new FxLayer(game),
new UILayer(game, eventBus, transformHandler),
new NukeTrajectoryPreviewLayer(game, eventBus, transformHandler),
- pingTrajectoryPreviewLayer,
new StructureIconsLayer(game, eventBus, uiState, transformHandler),
new NameLayer(game, transformHandler, eventBus),
eventsDisplay,
@@ -294,7 +292,6 @@ export function createRenderer(
export class GameRenderer {
private context: CanvasRenderingContext2D;
- private inputHandler: InputHandler;
constructor(
private game: GameView,
@@ -308,11 +305,9 @@ export class GameRenderer {
const context = canvas.getContext("2d");
if (context === null) throw new Error("2d context not supported");
this.context = context;
- this.inputHandler = new InputHandler(uiState, canvas, eventBus, transformHandler);
}
initialize() {
- this.inputHandler.initialize();
this.eventBus.on(RedrawGraphicsEvent, () => this.redraw());
this.layers.forEach((l) => l.init?.());
diff --git a/src/client/graphics/fx/PingFx.ts b/src/client/graphics/fx/PingFx.ts
deleted file mode 100644
index 872632046..000000000
--- a/src/client/graphics/fx/PingFx.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import { GameView } from "../../../core/game/GameView";
-import { PingType } from "../../../core/game/Ping";
-import { TileRef } from "../../../core/game/GameMap";
-import { Fx } from "./Fx";
-
-
-export class PingFx implements Fx {
- private readonly durationMs: number = 3000; // Ping visible for 3 seconds
- private startTime: number;
- private readonly pingColor: string;
- private get icon(): HTMLImageElement | null {
- return PingFx.iconCache.get(this.pingType) || null;
- }
-
-
-
- constructor(
- private game: GameView,
- private pingType: PingType,
- private tile: TileRef,
- ) {
- this.startTime = performance.now();
- this.pingColor = this.getPingColor(pingType);
- // Trigger preload but don't store the result
- const iconPath = this.getIconPath(pingType);
- if (iconPath) {
- PingFx.preloadIcon(pingType, iconPath);
- }
- }
-
- private getPingColor(pingType: PingType): string {
- switch (pingType) {
- case PingType.Attack:
- return "rgba(255, 0, 0, 0.7)"; // Red
- case PingType.Retreat:
- return "rgba(0, 255, 0, 0.7)"; // Green
- case PingType.Defend:
- return "rgba(0, 0, 255, 0.7)"; // Blue
- case PingType.WatchOut:
- return "rgba(255, 255, 0, 0.7)"; // Yellow
- default:
- return "rgba(128, 128, 128, 0.7)"; // Default to gray
- }
- }
-
- private getIconPath(pingType: PingType): string | null {
- switch (pingType) {
- case PingType.Attack:
- return "/resources/images/SwordIconWhite.svg";
- case PingType.Retreat:
- return "/resources/images/BackIconWhite.svg";
- case PingType.Defend:
- return "/resources/images/ShieldIconWhite.svg";
- case PingType.WatchOut:
- return "/resources/images/QuestionMarkIcon.svg";
- default:
- return null;
- }
- }
-private static iconCache = new Map();
- private static preloadIcon(pingType: PingType, iconPath: string): void {
- if (!PingFx.iconCache.has(pingType)) {
- const img = new Image();
- img.onload = () => {
- PingFx.iconCache.set(pingType, img);
- };
- img.onerror = () => {
- console.error(`Failed to load ping icon: ${iconPath}`);
- PingFx.iconCache.set(pingType, null); // Mark as failed
- };
- img.src = iconPath;
- }
- }
-
- renderTick(duration: number, context: CanvasRenderingContext2D): boolean {
- const elapsed = performance.now() - this.startTime;
- if (elapsed > this.durationMs) {
- return false; // Fx is finished
- }
-
- const x = this.game.x(this.tile);
- const y = this.game.y(this.tile);
-
- // Calculate offset to center coordinates (same as canvas drawing)
- const offsetX = -this.game.width() / 2;
- const offsetY = -this.game.height() / 2;
-
- context.save();
- context.globalAlpha = 1 - elapsed / this.durationMs; // Fade out effect
-
- // Draw colored circle
- context.fillStyle = this.pingColor;
- context.beginPath();
- context.arc(x + offsetX, y + offsetY, 15, 0, 2 * Math.PI);
- context.fill();
-
- // Draw icon
- if (this.icon && this.icon.complete) {
- const iconSize = 20;
- context.drawImage(
- this.icon,
- x + offsetX - iconSize / 2,
- y + offsetY - iconSize / 2,
- iconSize,
- iconSize,
- );
- }
-
- context.restore();
- return true; // Fx is still active
- }
-}
diff --git a/src/client/graphics/layers/FxLayer.ts b/src/client/graphics/layers/FxLayer.ts
index f243590a7..e1c454e5c 100644
--- a/src/client/graphics/layers/FxLayer.ts
+++ b/src/client/graphics/layers/FxLayer.ts
@@ -18,11 +18,6 @@ import { SpriteFx } from "../fx/SpriteFx";
import { TargetFx } from "../fx/TargetFx";
import { TextFx } from "../fx/TextFx";
import { UnitExplosionFx } from "../fx/UnitExplosionFx";
-import { PingPlacedEvent, PingType } from "../../../core/game/Ping";
-import { PingFx } from "../fx/PingFx";
-import { EventBus } from "../../../core/EventBus";
-import { PingPlacedEvent, PingType } from "../../../core/game/Ping";
-import { PingFx } from "../fx/PingFx";
import { Layer } from "./Layer";
export class FxLayer implements Layer {
private canvas: HTMLCanvasElement;
@@ -38,7 +33,7 @@ export class FxLayer implements Layer {
private boatTargetFxByUnitId: Map = new Map();
private nukeTargetFxByUnitId: Map = new Map();
- constructor(private game: GameView, private eventBus: EventBus) {
+ constructor(private game: GameView) {
this.theme = this.game.config().theme();
}
@@ -358,16 +353,6 @@ export class FxLayer implements Layer {
} catch (err) {
console.error("Failed to load FX sprites:", err);
}
-
- this.eventBus.on(PingPlacedEvent, (event) => {
- const pingFx = new PingFx(
- this.game,
- this.animatedSpriteLoader,
- event.type,
- event.tile,
- );
- this.allFx.push(pingFx);
- });
}
redraw(): void {
diff --git a/src/core/game/Ping.ts b/src/core/game/Ping.ts
deleted file mode 100644
index 81e3d4147..000000000
--- a/src/core/game/Ping.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { TileRef } from "./GameMap";
-
-export enum PingType {
- Attack,
- Retreat,
- Defend,
- WatchOut,
-}
-
-export class Ping {
- constructor(
- public type: PingType,
- public tile: TileRef,
- ) {}
-}
-
-export class PingPlacedEvent extends Ping {}
diff --git a/tests/client/graphics/ProgressBar.test.ts b/tests/client/graphics/ProgressBar.test.ts
index 11a86a14c..50189a9e3 100644
--- a/tests/client/graphics/ProgressBar.test.ts
+++ b/tests/client/graphics/ProgressBar.test.ts
@@ -11,39 +11,17 @@ describe("ProgressBar", () => {
canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 20;
- ctx = {
- clearRect: jest.fn(),
- fillRect: jest.fn(),
- beginPath: jest.fn(),
- arc: jest.fn(),
- fill: jest.fn(),
- stroke: jest.fn(),
- measureText: jest.fn(() => ({ width: 10 })),
- fillText: jest.fn(),
- save: jest.fn(),
- restore: jest.fn(),
- translate: jest.fn(),
- rotate: jest.fn(),
- drawImage: jest.fn(),
- setTransform: jest.fn(),
- globalAlpha: 1,
- fillStyle: "",
- strokeStyle: "",
- lineWidth: 1,
- font: "",
- } as unknown as CanvasRenderingContext2D;
- jest
- .spyOn(HTMLCanvasElement.prototype, "getContext")
- .mockReturnValue(ctx);
+ ctx = canvas.getContext("2d")!;
});
it("should initialize and draw the background", () => {
const spyClearRect = jest.spyOn(ctx, "clearRect");
const spyFillRect = jest.spyOn(ctx, "fillRect");
+ const spyFillStyle = jest.spyOn(ctx, "fillStyle", "set");
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10, 0.5);
expect(spyClearRect).toHaveBeenCalledWith(0, 0, 82, 12);
expect(spyFillRect).toHaveBeenCalledWith(1, 1, 80, 10);
- expect(ctx.fillStyle).toBe("#00ff00");
+ expect(spyFillStyle).toHaveBeenCalledWith("#00ff00");
expect(bar.getX()).toBe(2);
expect(bar.getY()).toBe(2);
});
diff --git a/tests/client/graphics/UILayer.test.ts b/tests/client/graphics/UILayer.test.ts
index e40fc57c2..c899ca079 100644
--- a/tests/client/graphics/UILayer.test.ts
+++ b/tests/client/graphics/UILayer.test.ts
@@ -30,31 +30,6 @@ describe("UILayer", () => {
};
eventBus = { on: jest.fn() };
transformHandler = {};
-
- // Mock the HTMLCanvasElement.prototype.getContext method
- jest
- .spyOn(HTMLCanvasElement.prototype, "getContext")
- .mockReturnValue({
- clearRect: jest.fn(),
- fillRect: jest.fn(),
- beginPath: jest.fn(),
- arc: jest.fn(),
- fill: jest.fn(),
- stroke: jest.fn(),
- measureText: jest.fn(() => ({ width: 10 })),
- fillText: jest.fn(),
- save: jest.fn(),
- restore: jest.fn(),
- translate: jest.fn(),
- rotate: jest.fn(),
- drawImage: jest.fn(),
- setTransform: jest.fn(),
- globalAlpha: 1,
- fillStyle: "",
- strokeStyle: "",
- lineWidth: 1,
- font: "",
- } as unknown as CanvasRenderingContext2D);
});
it("should initialize and redraw canvas", () => {