From 0b973bda77a2f86e9d95dbc34f6c533c11d750f1 Mon Sep 17 00:00:00 2001 From: scamiv <6170744+scamiv@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:07:51 +0100 Subject: [PATCH] =?UTF-8?q?Reused=20the=20quick=E2=80=91info=20hover=20log?= =?UTF-8?q?ic=20so=20territory=20highlighting=20now=20follows=20boats/ship?= =?UTF-8?q?s=20too.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added shared hover helper in HoverInfo.ts (same rules as PlayerInfoOverlay, including nearby ships on water). - PlayerInfoOverlay.ts now uses getHoverInfo(...) instead of its local hover logic. - TerritoryLayer.ts now uses getHoverInfo(...) and highlights the unit owner when hovering ships/boats. --- src/client/graphics/HoverInfo.ts | 73 +++++++++++++++++++ .../graphics/layers/PlayerInfoOverlay.ts | 70 +++++++----------- src/client/graphics/layers/TerritoryLayer.ts | 25 ++----- 3 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 src/client/graphics/HoverInfo.ts diff --git a/src/client/graphics/HoverInfo.ts b/src/client/graphics/HoverInfo.ts new file mode 100644 index 000000000..c99f25777 --- /dev/null +++ b/src/client/graphics/HoverInfo.ts @@ -0,0 +1,73 @@ +import { UnitType } from "../../core/game/Game"; +import { TileRef } from "../../core/game/GameMap"; +import { GameView, PlayerView, UnitView } from "../../core/game/GameView"; + +export type HoverInfo = { + player: PlayerView | null; + unit: UnitView | null; + isWilderness: boolean; + isIrradiatedWilderness: boolean; +}; + +function euclideanDistWorld( + coord: { x: number; y: number }, + tileRef: TileRef, + game: GameView, +): number { + const x = game.x(tileRef); + const y = game.y(tileRef); + const dx = coord.x - x; + const dy = coord.y - y; + return Math.sqrt(dx * dx + dy * dy); +} + +function distSortUnitWorld(coord: { x: number; y: number }, game: GameView) { + return (a: UnitView, b: UnitView) => { + const distA = euclideanDistWorld(coord, a.tile(), game); + const distB = euclideanDistWorld(coord, b.tile(), game); + return distA - distB; + }; +} + +export function getHoverInfo( + game: GameView, + worldCoord: { x: number; y: number }, +): HoverInfo { + const info: HoverInfo = { + player: null, + unit: null, + isWilderness: false, + isIrradiatedWilderness: false, + }; + + if (!game.isValidCoord(worldCoord.x, worldCoord.y)) { + return info; + } + + const tile = game.ref(worldCoord.x, worldCoord.y); + const owner = game.owner(tile); + + if (owner && owner.isPlayer()) { + info.player = owner as PlayerView; + return info; + } + + if (owner && !owner.isPlayer() && game.isLand(tile)) { + info.isIrradiatedWilderness = game.hasFallout(tile); + info.isWilderness = !info.isIrradiatedWilderness; + return info; + } + + if (!game.isLand(tile)) { + const units = game + .units(UnitType.Warship, UnitType.TradeShip, UnitType.TransportShip) + .filter((u) => euclideanDistWorld(worldCoord, u.tile(), game) < 50) + .sort(distSortUnitWorld(worldCoord, game)); + + if (units.length > 0) { + info.unit = units[0]; + } + } + + return info; +} diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index e720738f4..df7d485e6 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -6,10 +6,8 @@ import { PlayerProfile, PlayerType, Relation, - Unit, UnitType, } from "../../../core/game/Game"; -import { TileRef } from "../../../core/game/GameMap"; import { AllianceView } from "../../../core/game/GameUpdates"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; import { @@ -24,6 +22,7 @@ import { renderTroops, translateText, } from "../../Utils"; +import { getHoverInfo } from "../HoverInfo"; import { EMOJI_ICON_KIND, getFirstPlacePlayer, @@ -47,26 +46,6 @@ const portIcon = assetUrl("images/PortIcon.svg"); const samLauncherIcon = assetUrl("images/SamLauncherIconWhite.svg"); const soldierIcon = assetUrl("images/SoldierIcon.svg"); -function euclideanDistWorld( - coord: { x: number; y: number }, - tileRef: TileRef, - game: GameView, -): number { - const x = game.x(tileRef); - const y = game.y(tileRef); - const dx = coord.x - x; - const dy = coord.y - y; - return Math.sqrt(dx * dx + dy * dy); -} - -function distSortUnitWorld(coord: { x: number; y: number }, game: GameView) { - return (a: Unit | UnitView, b: Unit | UnitView) => { - const distA = euclideanDistWorld(coord, a.tile(), game); - const distB = euclideanDistWorld(coord, b.tile(), game); - return distA - distB; - }; -} - @customElement("player-info-overlay") export class PlayerInfoOverlay extends LitElement implements Layer { @property({ type: Object }) @@ -87,6 +66,12 @@ export class PlayerInfoOverlay extends LitElement implements Layer { @state() private unit: UnitView | null = null; + @state() + private isWilderness: boolean = false; + + @state() + private isIrradiatedWilderness: boolean = false; + @state() private _isInfoVisible: boolean = false; @@ -134,36 +119,28 @@ export class PlayerInfoOverlay extends LitElement implements Layer { this.setVisible(false); this.unit = null; this.player = null; + this.isWilderness = false; + this.isIrradiatedWilderness = false; } public maybeShow(x: number, y: number) { this.hide(); const worldCoord = this.transform.screenToWorldCoordinates(x, y); - if (!this.game.isValidCoord(worldCoord.x, worldCoord.y)) { - return; - } + const info = getHoverInfo(this.game, worldCoord); - const tile = this.game.ref(worldCoord.x, worldCoord.y); - if (!tile) return; - - const owner = this.game.owner(tile); - - if (owner && owner.isPlayer()) { - this.player = owner as PlayerView; + if (info.player) { + this.player = info.player; this.player.profile().then((p) => { this.playerProfile = p; }); this.setVisible(true); - } else if (!this.game.isLand(tile)) { - const units = this.game - .units(UnitType.Warship, UnitType.TradeShip, UnitType.TransportShip) - .filter((u) => euclideanDistWorld(worldCoord, u.tile(), this.game) < 50) - .sort(distSortUnitWorld(worldCoord, this.game)); - - if (units.length > 0) { - this.unit = units[0]; - this.setVisible(true); - } + } else if (info.isWilderness || info.isIrradiatedWilderness) { + this.isWilderness = info.isWilderness; + this.isIrradiatedWilderness = info.isIrradiatedWilderness; + this.setVisible(true); + } else if (info.unit) { + this.unit = info.unit; + this.setVisible(true); } } @@ -506,6 +483,15 @@ export class PlayerInfoOverlay extends LitElement implements Layer {