From fe9eb8700c82644e5a9aa6aebda9ed76db8dae43 Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 12:03:30 +0000 Subject: [PATCH 1/8] handle findWarshipsNearCell when clicking outside the map --- src/client/graphics/layers/UnitLayer.ts | 65 +++++++++++++------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 5cae90c36..84c9fd1b7 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -1,22 +1,22 @@ import { Colord } from "colord"; -import { Theme } from "../../../core/configuration/Config"; -import { Unit, UnitType, Player } from "../../../core/game/Game"; -import { Layer } from "./Layer"; import { EventBus } from "../../../core/EventBus"; -import { - AlternateViewEvent, - MouseUpEvent, - UnitSelectionEvent, -} from "../../InputHandler"; import { ClientID } from "../../../core/Schemas"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { Theme } from "../../../core/configuration/Config"; +import { UnitType } from "../../../core/game/Game"; import { euclDistFN, manhattanDistFN, TileRef, } from "../../../core/game/GameMap"; import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { + AlternateViewEvent, + MouseUpEvent, + UnitSelectionEvent, +} from "../../InputHandler"; import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; enum Relationship { Self, @@ -82,29 +82,34 @@ export class UnitLayer implements Layer { * @returns Array of player's warships in range, sorted by distance (closest first) */ private findWarshipsNearCell(cell: { x: number; y: number }): UnitView[] { - const clickRef = this.game.ref(cell.x, cell.y); + try { + const clickRef = this.game.ref(cell.x, cell.y); - // Make sure we have the current player - if (this.myPlayer == null) { - this.myPlayer = this.game.playerByClientID(this.clientID); + // Make sure we have the current player + if (this.myPlayer == null) { + this.myPlayer = this.game.playerByClientID(this.clientID); + } + + // Only select warships owned by the player + return this.game + .units(UnitType.Warship) + .filter( + (unit) => + unit.isActive() && + unit.owner() === this.myPlayer && // Only allow selecting own warships + this.game.manhattanDist(unit.tile(), clickRef) <= + this.WARSHIP_SELECTION_RADIUS, + ) + .sort((a, b) => { + // Sort by distance (closest first) + const distA = this.game.manhattanDist(a.tile(), clickRef); + const distB = this.game.manhattanDist(b.tile(), clickRef); + return distA - distB; + }); + } catch (err) { + console.debug("User click outside the game. Ignoring the click event"); + return []; } - - // Only select warships owned by the player - return this.game - .units(UnitType.Warship) - .filter( - (unit) => - unit.isActive() && - unit.owner() === this.myPlayer && // Only allow selecting own warships - this.game.manhattanDist(unit.tile(), clickRef) <= - this.WARSHIP_SELECTION_RADIUS, - ) - .sort((a, b) => { - // Sort by distance (closest first) - const distA = this.game.manhattanDist(a.tile(), clickRef); - const distB = this.game.manhattanDist(b.tile(), clickRef); - return distA - distB; - }); } private onMouseUp(event: MouseUpEvent) { From a60bef97f0fa57afb72d2581521d436d4f71d731 Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 12:06:42 +0000 Subject: [PATCH 2/8] reverted organized imports --- src/client/graphics/layers/UnitLayer.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 84c9fd1b7..57f34ae9f 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -1,22 +1,22 @@ import { Colord } from "colord"; -import { EventBus } from "../../../core/EventBus"; -import { ClientID } from "../../../core/Schemas"; import { Theme } from "../../../core/configuration/Config"; -import { UnitType } from "../../../core/game/Game"; +import { Unit, UnitType, Player } from "../../../core/game/Game"; +import { Layer } from "./Layer"; +import { EventBus } from "../../../core/EventBus"; +import { + AlternateViewEvent, + MouseUpEvent, + UnitSelectionEvent, +} from "../../InputHandler"; +import { ClientID } from "../../../core/Schemas"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; import { euclDistFN, manhattanDistFN, TileRef, } from "../../../core/game/GameMap"; import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { - AlternateViewEvent, - MouseUpEvent, - UnitSelectionEvent, -} from "../../InputHandler"; import { TransformHandler } from "../TransformHandler"; -import { Layer } from "./Layer"; enum Relationship { Self, From 276eee11523c6aab7776d41da687f4e2cb5b65d5 Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 12:12:20 +0000 Subject: [PATCH 3/8] validates coordinates in onMouseUp --- src/client/graphics/layers/UnitLayer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 57f34ae9f..1f8b2d23f 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -118,6 +118,9 @@ export class UnitLayer implements Layer { event.x, event.y, ); + if (!this.game.isValidCoord(cell.x, cell.y)) { + return; + } // Find warships near this cell, sorted by distance const nearbyWarships = this.findWarshipsNearCell(cell); From de07fd0b4f8c081d89b09fb0fb13674098fba291 Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 12:24:35 +0000 Subject: [PATCH 4/8] removed isValidCoord in onMouseUp --- src/client/graphics/layers/UnitLayer.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 1f8b2d23f..57f34ae9f 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -118,9 +118,6 @@ export class UnitLayer implements Layer { event.x, event.y, ); - if (!this.game.isValidCoord(cell.x, cell.y)) { - return; - } // Find warships near this cell, sorted by distance const nearbyWarships = this.findWarshipsNearCell(cell); From abf79f4c8d4b7616f860885b055d6569ca4e7d7f Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 12:25:01 +0000 Subject: [PATCH 5/8] console.debug is unecessary, code comment instead. --- src/client/graphics/layers/UnitLayer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 57f34ae9f..53717f8d9 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -107,7 +107,7 @@ export class UnitLayer implements Layer { return distA - distB; }); } catch (err) { - console.debug("User click outside the game. Ignoring the click event"); + // The cell coordinate were invalid (user probably clicked outside the map), therefore no warships can be found return []; } } From 8bd1d84dae4b5fe3b493a2d93b4e62d219b9864f Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 14:06:35 +0000 Subject: [PATCH 6/8] corrects port to missile silo --- src/core/execution/MissileSiloExecution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/execution/MissileSiloExecution.ts b/src/core/execution/MissileSiloExecution.ts index 3dc1a8714..fb4547542 100644 --- a/src/core/execution/MissileSiloExecution.ts +++ b/src/core/execution/MissileSiloExecution.ts @@ -36,7 +36,7 @@ export class MissileSiloExecution implements Execution { if (this.silo == null) { if (!this.player.canBuild(UnitType.MissileSilo, this.tile)) { consolex.warn( - `player ${this.player} cannot build port at ${this.tile}`, + `player ${this.player} cannot build missile silo at ${this.tile}`, ); this.active = false; return; From 2685891e7aa93d7246bf61ec3cdc84fa5826e489 Mon Sep 17 00:00:00 2001 From: BeGj Date: Tue, 4 Mar 2025 14:30:31 +0000 Subject: [PATCH 7/8] checks if valid coord before referencing the tile. --- src/client/graphics/layers/UnitLayer.ts | 49 ++++++++++++------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 53717f8d9..3e59dc7ba 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -82,34 +82,33 @@ export class UnitLayer implements Layer { * @returns Array of player's warships in range, sorted by distance (closest first) */ private findWarshipsNearCell(cell: { x: number; y: number }): UnitView[] { - try { - const clickRef = this.game.ref(cell.x, cell.y); - - // Make sure we have the current player - if (this.myPlayer == null) { - this.myPlayer = this.game.playerByClientID(this.clientID); - } - - // Only select warships owned by the player - return this.game - .units(UnitType.Warship) - .filter( - (unit) => - unit.isActive() && - unit.owner() === this.myPlayer && // Only allow selecting own warships - this.game.manhattanDist(unit.tile(), clickRef) <= - this.WARSHIP_SELECTION_RADIUS, - ) - .sort((a, b) => { - // Sort by distance (closest first) - const distA = this.game.manhattanDist(a.tile(), clickRef); - const distB = this.game.manhattanDist(b.tile(), clickRef); - return distA - distB; - }); - } catch (err) { + 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); + + // Make sure we have the current player + if (this.myPlayer == null) { + this.myPlayer = this.game.playerByClientID(this.clientID); + } + + // Only select warships owned by the player + return this.game + .units(UnitType.Warship) + .filter( + (unit) => + unit.isActive() && + unit.owner() === this.myPlayer && // Only allow selecting own warships + this.game.manhattanDist(unit.tile(), clickRef) <= + this.WARSHIP_SELECTION_RADIUS, + ) + .sort((a, b) => { + // Sort by distance (closest first) + const distA = this.game.manhattanDist(a.tile(), clickRef); + const distB = this.game.manhattanDist(b.tile(), clickRef); + return distA - distB; + }); } private onMouseUp(event: MouseUpEvent) { From d2208755c4c03aa4df281fa8c220eaa57ceeabb5 Mon Sep 17 00:00:00 2001 From: ilan schemoul Date: Tue, 4 Mar 2025 00:46:22 +0100 Subject: [PATCH 8/8] feat: WarShips red color outside if target current player Hard to know when warship captures your trade so if they target one of your trade or war ship they are highlighted in red. Known limitation: doesn't work well if the WarShip is already in red (player's color) --- src/client/graphics/layers/UnitLayer.ts | 21 +++++++++++++-------- src/core/execution/WarshipExecution.ts | 1 + src/core/game/Game.ts | 5 +++++ src/core/game/GameUpdates.ts | 1 + src/core/game/GameView.ts | 4 +++- src/core/game/UnitImpl.ts | 15 +++++++++++++++ 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 5cae90c36..6fc7d1b30 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -1,4 +1,4 @@ -import { Colord } from "colord"; +import { colord, Colord } from "colord"; import { Theme } from "../../../core/configuration/Config"; import { Unit, UnitType, Player } from "../../../core/game/Game"; import { Layer } from "./Layer"; @@ -240,15 +240,20 @@ export class UnitLayer implements Layer { return; } + let outerColor = this.theme.territoryColor(unit.owner().info()); + if (unit.targetId()) { + const targetOwner = this.game + .units() + .find((u) => u.id() == unit.targetId()) + .owner(); + if (targetOwner == this.myPlayer) { + outerColor = colord({ r: 200, b: 0, g: 0 }); + } + } + // Paint outer territory for (const t of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), 5))) { - this.paintCell( - this.game.x(t), - this.game.y(t), - rel, - this.theme.territoryColor(unit.owner().info()), - 255, - ); + this.paintCell(this.game.x(t), this.game.y(t), rel, outerColor, 255); } // Paint border diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index 088e96cfb..70d944d01 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -96,6 +96,7 @@ export class WarshipExecution implements Execution { return distSortUnit(this.mg, this.warship)(a, b); })[0] ?? null; + this.warship.setTarget(this.target); if (this.target == null || this.target.type() != UnitType.TradeShip) { // Patrol unless we are hunting down a tradeship const result = this.pathfinder.nextTile( diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index ab2286f00..1019269f2 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -193,6 +193,8 @@ export class PlayerInfo { } export interface Unit { + id(): number; + // Properties type(): UnitType; troops(): number; @@ -209,6 +211,9 @@ export interface Unit { hasHealth(): boolean; health(): number; modifyHealth(delta: number): void; + // State for warships (currently) + setTarget(target: Unit): void; + target(): Unit; // Mutations setTroops(troops: number): void; diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index 4ed5f9ace..dbf740e39 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -71,6 +71,7 @@ export interface UnitUpdate { isActive: boolean; health?: number; constructionType?: UnitType; + targetId?: number; } export interface AttackUpdate { diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index b7e24a07d..8b534e1dd 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -5,7 +5,6 @@ import { Player, PlayerActions, PlayerProfile, - Unit, } from "./Game"; import { AttackUpdate, PlayerUpdate } from "./GameUpdates"; import { UnitUpdate } from "./GameUpdates"; @@ -92,6 +91,9 @@ export class UnitView { constructionType(): UnitType | undefined { return this.data.constructionType; } + targetId() { + return this.data.targetId; + } } export class PlayerView { diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 564557148..76bbb1de8 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -11,6 +11,8 @@ export class UnitImpl implements Unit { private _active = true; private _health: bigint; private _lastTile: TileRef = null; + // Currently only warship use it + private _target: Unit = null; private _constructionType: UnitType = undefined; @@ -28,6 +30,10 @@ export class UnitImpl implements Unit { this._lastTile = _tile; } + id() { + return this._id; + } + toUpdate(): UnitUpdate { return { type: GameUpdateType.Unit, @@ -40,6 +46,7 @@ export class UnitImpl implements Unit { lastPos: this._lastTile, health: this.hasHealth() ? Number(this._health) : undefined, constructionType: this._constructionType, + targetId: this.target() ? this.target().id() : null, }; } @@ -150,4 +157,12 @@ export class UnitImpl implements Unit { dstPort(): Unit { return this._dstPort; } + + setTarget(target: Unit) { + this._target = target; + } + + target() { + return this._target; + } }