mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 12:20:46 +00:00
Reused the quick‑info hover logic so territory highlighting now follows boats/ships too.
- 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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
<div
|
||||
class="bg-gray-800/92 backdrop-blur-sm shadow-xs min-[1200px]:rounded-lg sm:rounded-b-lg shadow-lg text-white text-lg lg:text-base w-full sm:w-[500px] overflow-hidden ${containerClasses}"
|
||||
>
|
||||
${this.isWilderness || this.isIrradiatedWilderness
|
||||
? html`<div class="p-2 font-bold">
|
||||
${translateText(
|
||||
this.isIrradiatedWilderness
|
||||
? "player_info_overlay.irradiated_wilderness_title"
|
||||
: "player_info_overlay.wilderness_title",
|
||||
)}
|
||||
</div>`
|
||||
: ""}
|
||||
${this.player !== null ? this.renderPlayerInfo(this.player) : ""}
|
||||
${this.unit !== null ? this.renderUnitInfo(this.unit) : ""}
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
MouseOverEvent,
|
||||
} from "../../InputHandler";
|
||||
import { FrameProfiler } from "../FrameProfiler";
|
||||
import { getHoverInfo } from "../HoverInfo";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
import { TerritoryWebGLRenderer } from "./TerritoryWebGLRenderer";
|
||||
@@ -316,12 +317,14 @@ export class TerritoryLayer implements Layer {
|
||||
this.lastMousePosition.x,
|
||||
this.lastMousePosition.y,
|
||||
);
|
||||
if (!this.game.isValidCoord(cell.x, cell.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousTerritory = this.highlightedTerritory;
|
||||
const territory = this.getTerritoryAtCell(cell);
|
||||
const info = getHoverInfo(this.game, cell);
|
||||
let territory: PlayerView | null = null;
|
||||
if (info.player) {
|
||||
territory = info.player;
|
||||
} else if (info.unit) {
|
||||
territory = info.unit.owner();
|
||||
}
|
||||
|
||||
if (territory) {
|
||||
this.highlightedTerritory = territory;
|
||||
@@ -336,18 +339,6 @@ export class TerritoryLayer implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
private getTerritoryAtCell(cell: { x: number; y: number }) {
|
||||
const tile = this.game.ref(cell.x, cell.y);
|
||||
if (!tile) {
|
||||
return null;
|
||||
}
|
||||
if (!this.game.hasOwner(tile)) {
|
||||
return null;
|
||||
}
|
||||
const owner = this.game.owner(tile);
|
||||
return owner instanceof PlayerView ? owner : null;
|
||||
}
|
||||
|
||||
redraw() {
|
||||
this.lastMyPlayerSmallId = this.game.myPlayer()?.smallID() ?? null;
|
||||
this.cachedTerritoryPatternsEnabled = this.userSettings.territoryPatterns();
|
||||
|
||||
Reference in New Issue
Block a user