diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 9c55e837a..26d8f6c27 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -18,6 +18,12 @@ export class MouseOverEvent implements GameEvent { public readonly y: number, ) {} } +export class TouchEvent implements GameEvent { + constructor( + public readonly x: number, + public readonly y: number, + ) {} +} /** * Event emitted when a unit is selected or deselected @@ -476,7 +482,7 @@ export class InputHandler { Math.abs(event.y - this.lastPointerDownY); if (dist < 10) { if (event.pointerType === "touch") { - this.eventBus.emit(new ContextMenuEvent(event.clientX, event.clientY)); + this.eventBus.emit(new TouchEvent(event.x, event.y)); event.preventDefault(); return; } diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index d0c5f8dd9..e7f69089d 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -7,7 +7,9 @@ import { GameView, UnitView } from "../../../core/game/GameView"; import { BezenhamLine } from "../../../core/utilities/Line"; import { AlternateViewEvent, + ContextMenuEvent, MouseUpEvent, + TouchEvent, UnitSelectionEvent, } from "../../InputHandler"; import { MoveWarshipIntentEvent } from "../../Transport"; @@ -73,6 +75,7 @@ export class UnitLayer implements Layer { init() { this.eventBus.on(AlternateViewEvent, (e) => this.onAlternativeViewEvent(e)); this.eventBus.on(MouseUpEvent, (e) => this.onMouseUp(e)); + this.eventBus.on(TouchEvent, (e) => this.onTouch(e)); this.eventBus.on(UnitSelectionEvent, (e) => this.onUnitSelectionChange(e)); this.redraw(); @@ -81,16 +84,10 @@ export class UnitLayer implements Layer { /** * Find player-owned warships near the given cell within a configurable radius - * @param cell The cell to check + * @param clickRef The tile to check * @returns Array of player's warships in range, sorted by distance (closest first) */ - private findWarshipsNearCell(cell: { x: number; y: number }): UnitView[] { - if (!this.game.isValidCoord(cell.x, cell.y)) { - // The cell coordinate were invalid (user probably clicked outside the map), therefore no warships can be found - return []; - } - const clickRef = this.game.ref(cell.x, cell.y); - + private findWarshipsNearCell(clickRef: TileRef): UnitView[] { // Only select warships owned by the player return this.game .units(UnitType.Warship) @@ -109,29 +106,75 @@ export class UnitLayer implements Layer { }); } - private onMouseUp(event: MouseUpEvent) { - // Convert screen coordinates to world coordinates + private onMouseUp( + event: MouseUpEvent, + clickRef?: TileRef, + nearbyWarships?: UnitView[], + ) { + if (clickRef === undefined) { + // Convert screen coordinates to world coordinates + const cell = this.transformHandler.screenToWorldCoordinates( + event.x, + event.y, + ); + if (!this.game.isValidCoord(cell.x, cell.y)) return; + + clickRef = this.game.ref(cell.x, cell.y); + } + if (!this.game.isOcean(clickRef)) return; + + if (this.selectedUnit) { + this.eventBus.emit( + new MoveWarshipIntentEvent(this.selectedUnit.id(), clickRef), + ); + // Deselect + this.eventBus.emit(new UnitSelectionEvent(this.selectedUnit, false)); + return; + } + + // Find warships near this tile, sorted by distance + nearbyWarships ??= this.findWarshipsNearCell(clickRef); + if (nearbyWarships.length > 0) { + // Toggle selection of the closest warship + this.eventBus.emit(new UnitSelectionEvent(nearbyWarships[0], true)); + } + } + + private onTouch(event: TouchEvent) { const cell = this.transformHandler.screenToWorldCoordinates( event.x, event.y, ); - // Find warships near this cell, sorted by distance - const nearbyWarships = this.findWarshipsNearCell(cell); + const clickRef = this.game.ref(cell.x, cell.y); + if (!this.game.isOcean(clickRef)) { + // No isValidCoord/Ref check yet, that is done for ContextMenuEvent later + // No warship to find because no Ocean tile, open Radial Menu + this.eventBus.emit(new ContextMenuEvent(event.x, event.y)); + return; + } + + if (!this.game.isValidRef(clickRef)) { + return; + } if (this.selectedUnit) { - const clickRef = this.game.ref(cell.x, cell.y); - if (this.game.isOcean(clickRef)) { - this.eventBus.emit( - new MoveWarshipIntentEvent(this.selectedUnit.id(), clickRef), - ); - } - // Deselect - this.eventBus.emit(new UnitSelectionEvent(this.selectedUnit, false)); - } else if (nearbyWarships.length > 0) { - // Toggle selection of the closest warship - const clickedUnit = nearbyWarships[0]; - this.eventBus.emit(new UnitSelectionEvent(clickedUnit, true)); + // Reuse the mouse logic, send clickRef to avoid fetching it again + this.onMouseUp(new MouseUpEvent(event.x, event.y), clickRef); + return; + } + + const nearbyWarships = this.findWarshipsNearCell(clickRef); + + if (nearbyWarships.length > 0) { + this.onMouseUp( + new MouseUpEvent(event.x, event.y), + clickRef, + nearbyWarships, + ); + } else { + // No warships selected or nearby, open Radial Menu + this.eventBus.emit(new ContextMenuEvent(event.x, event.y)); } }