From da2e0918a44532e4e947807799f73ceba68cfaf3 Mon Sep 17 00:00:00 2001
From: unne27 <59091348+unne27@users.noreply.github.com>
Date: Mon, 20 Jul 2026 23:21:50 +0200
Subject: [PATCH] feat/highlight the owner of hovered naval units (#4610)
Resolves #4445
## Description:
Changes HoverHighlightController.ts to check for naval units if the
mouse is not currently hovering over land. It then highlights the owner
of the closest naval unit within 50px of the mouse.
The activation radius feels a little to large right now, but it is
consistent with the PlayerInfoOverlay functionality. Some input would be
appreciated
It is also currently disabled by default, not sure if this should be the
case.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
## Please put your Discord username so you can be contacted if a bug or
regression is found:
unne27
https://github.com/user-attachments/assets/7b8262e5-a097-44c8-b2ae-1ac336466623
---------
Co-authored-by: unne27 <>
---
resources/lang/en.json | 2 +
.../controllers/HoverHighlightController.ts | 42 ++++++-
.../hud/layers/GraphicsSettingsModal.ts | 35 ++++++
src/client/render/gl/GraphicsOverrides.ts | 1 +
src/client/render/gl/RenderOverrides.ts | 3 +
src/client/render/gl/RenderSettings.ts | 1 +
src/client/render/gl/render-settings.json | 1 +
.../HoverHighlightController.test.ts | 110 ++++++++++++++++++
8 files changed, 190 insertions(+), 5 deletions(-)
create mode 100644 tests/client/controllers/HoverHighlightController.test.ts
diff --git a/resources/lang/en.json b/resources/lang/en.json
index ec19fd069..9ab07da4d 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -636,6 +636,8 @@
"name_cull_desc": "Hide names smaller than this size",
"name_cull_label": "Minimum name size",
"name_scale_label": "Name Scale",
+ "naval_hover_highlight_desc": "Highlight the owner of naval units when hovering over them.",
+ "naval_hover_highlight_label": "Naval Hover Highlight",
"nuke_color_desc": "Color of the fallout tint left on territory after a nuke.",
"nuke_color_label": "Nuke fallout color",
"ocean_color_desc": "Base color of ocean.",
diff --git a/src/client/controllers/HoverHighlightController.ts b/src/client/controllers/HoverHighlightController.ts
index e33a68f6a..85ca09632 100644
--- a/src/client/controllers/HoverHighlightController.ts
+++ b/src/client/controllers/HoverHighlightController.ts
@@ -9,12 +9,13 @@
*/
import { EventBus } from "../../core/EventBus";
+import { UnitType } from "../../core/game/Game";
import { Controller } from "../Controller";
import { MouseMoveEvent } from "../InputHandler";
import { MapRenderer } from "../render/gl";
import { OWNER_MASK } from "../render/gl/utils/TileCodec";
import { TransformHandler } from "../TransformHandler";
-import { GameView } from "../view";
+import { GameView, UnitView } from "../view";
export class HoverHighlightController implements Controller {
private lastOwnerID = 0;
@@ -30,16 +31,47 @@ export class HoverHighlightController implements Controller {
this.eventBus.on(MouseMoveEvent, (e) => this.onMouseMove(e));
}
+ private navalHighlightEnabled(): boolean {
+ return this.view.getSettings().mapOverlay.navalHighlight;
+ }
+
private onMouseMove(e: MouseMoveEvent): void {
const world = this.transformHandler.screenToWorldCoordinatesFloat(e.x, e.y);
this.view.setMouseWorldPos(world.x, world.y);
const cell = this.transformHandler.screenToWorldCoordinates(e.x, e.y);
- let ownerID = 0;
- if (this.game.isValidCoord(cell.x, cell.y)) {
- const ref = this.game.ref(cell.x, cell.y);
- ownerID = this.game.tileState(ref) & OWNER_MASK;
+ if (!this.game.isValidCoord(cell.x, cell.y)) {
+ if (this.lastOwnerID !== 0) {
+ this.lastOwnerID = 0;
+ this.view.setHighlightOwner(0);
+ }
+ return;
}
+ let ownerID = 0;
+
+ const ref = this.game.ref(cell.x, cell.y);
+ if (this.game.isLand(ref)) {
+ ownerID = this.game.tileState(ref) & OWNER_MASK;
+ } else if (this.navalHighlightEnabled()) {
+ // Avoid square root for performance; 50 tile radius = 2500 tiles²
+ let closestUnit: UnitView | null = null;
+ let closestDistSquared = 2500;
+ for (const u of this.game.units(
+ UnitType.Warship,
+ UnitType.TradeShip,
+ UnitType.TransportShip,
+ )) {
+ const distSquared = this.game.euclideanDistSquared(ref, u.tile());
+ if (distSquared < closestDistSquared) {
+ closestDistSquared = distSquared;
+ closestUnit = u;
+ }
+ }
+ if (closestUnit !== null) {
+ ownerID = closestUnit.owner().smallID();
+ }
+ }
+
if (ownerID === this.lastOwnerID) return;
this.lastOwnerID = ownerID;
this.view.setHighlightOwner(ownerID);
diff --git a/src/client/hud/layers/GraphicsSettingsModal.ts b/src/client/hud/layers/GraphicsSettingsModal.ts
index ffea83f46..1a7225239 100644
--- a/src/client/hud/layers/GraphicsSettingsModal.ts
+++ b/src/client/hud/layers/GraphicsSettingsModal.ts
@@ -297,6 +297,17 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
this.requestUpdate();
}
+ private currentNavalHighlight(): boolean {
+ return (
+ this.userSettings.graphicsOverrides().mapOverlay?.navalHighlight ??
+ renderDefaults.mapOverlay.navalHighlight
+ );
+ }
+
+ private onToggleNavalHighlight() {
+ this.patchMapOverlay({ navalHighlight: !this.currentNavalHighlight() });
+ }
+
private currentHighlightFill(): number {
return (
this.userSettings.graphicsOverrides().mapOverlay?.highlightFillBrighten ??
@@ -692,6 +703,7 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
const classicIcons = this.currentClassicIcons();
const classicNumbers = this.currentClassicNumbers();
const showDots = this.currentShowDots();
+ const navalHighlight = this.currentNavalHighlight();
const highlightFill = this.currentHighlightFill();
const highlightBrighten = this.currentHighlightBrighten();
const highlightThicken = this.currentHighlightThicken();
@@ -1040,6 +1052,29 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
${translateText("graphics_setting.section_map")}
+
+