feat: Add ping feature

This commit introduces a new ping system that allows players to communicate on the map.

- Adds a radial ping menu to select different ping types (Attack, Retreat, Defend, Watch Out).
- Implements the logic to place pings on the map.
- Adds visual effects for pings, including a fading circle and an icon.
- Shows a preview of the ping location before placing it.
This commit is contained in:
Restart2008
2025-11-21 20:39:36 -08:00
parent 33810e41c5
commit 2157bfc5bc
10 changed files with 414 additions and 10 deletions
+40
View File
@@ -2,7 +2,9 @@ import { EventBus, GameEvent } from "../core/EventBus";
import { UnitType } from "../core/game/Game";
import { UnitView } from "../core/game/GameView";
import { UserSettings } from "../core/game/UserSettings";
import { PingType } from "../core/game/Ping";
import { UIState } from "./graphics/UIState";
import { TransformHandler } from "./graphics/TransformHandler";
import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
export class MouseUpEvent implements GameEvent {
@@ -124,6 +126,9 @@ export class AutoUpgradeEvent implements GameEvent {
public readonly y: number,
) {}
}
export class PingSelectedEvent implements GameEvent {
constructor(public readonly pingType: PingType | null) {}
}
export class TickMetricsEvent implements GameEvent {
constructor(
@@ -131,6 +136,13 @@ export class TickMetricsEvent implements GameEvent {
public readonly tickDelay?: number,
) {}
}
export class PingPlacedEvent implements GameEvent {
constructor(
public readonly pingType: PingType,
public readonly x: number,
public readonly y: number,
) {}
}
export class InputHandler {
private lastPointerX: number = 0;
@@ -160,6 +172,7 @@ export class InputHandler {
public uiState: UIState,
private canvas: HTMLCanvasElement,
private eventBus: EventBus,
private transformHandler: TransformHandler,
) {}
initialize() {
@@ -481,6 +494,33 @@ export class InputHandler {
Math.abs(event.x - this.lastPointerDownX) +
Math.abs(event.y - this.lastPointerDownY);
if (dist < 10) {
if (this.uiState.currentPingType !== null) {
const rect = this.transformHandler.boundingRect();
if (!rect) {
this.uiState.currentPingType = null;
this.eventBus.emit(new PingSelectedEvent(null));
return;
}
{
const localX = event.clientX - rect.left;
const localY = event.clientY - rect.top;
const worldCoords = this.transformHandler.screenToWorldCoordinates(
localX,
localY,
);
this.eventBus.emit(
new PingPlacedEvent(
this.uiState.currentPingType,
worldCoords.x,
worldCoords.y,
),
);
}
this.uiState.currentPingType = null;
this.eventBus.emit(new PingSelectedEvent(null)); // Clear ping preview
return;
}
if (event.pointerType === "touch") {
this.eventBus.emit(new TouchEvent(event.x, event.y));
event.preventDefault();