Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/client/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,56 @@ export function isInIframe(): boolean {
return true;
}
}

export async function getSvgAspectRatio(src: string): Promise<number | null> {
const self = getSvgAspectRatio as any;
self.svgAspectRatioCache ??= new Map();

const cached = self.svgAspectRatioCache.get(src);
if (cached !== undefined) return cached;

try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const resp = await fetch(src, { signal: controller.signal });
clearTimeout(timeoutId);
if (!resp.ok) throw new Error(`Fetch failed: ${resp.status}`);
const text = await resp.text();

// Try parse viewBox
const vbMatch = text.match(/viewBox="([^"]+)"/i);
if (vbMatch) {
const parts = vbMatch[1]
.trim()
.split(/[\s,]+/)
.map(Number);
if (parts.length === 4 && parts.every((n) => !Number.isNaN(n))) {
const [, , vbW, vbH] = parts;
if (vbW > 0 && vbH > 0) {
const ratio = vbW / vbH;
self.svgAspectRatioCache.set(src, ratio);
return ratio;
}
}
}

// Fallback to width/height attributes (may be with units; strip px)
const widthMatch = text.match(/<svg[^>]*\swidth="([^"]+)"/i);
const heightMatch = text.match(/<svg[^>]*\sheight="([^"]+)"/i);
if (widthMatch && heightMatch) {
const parseNum = (s: string) => Number(s.replace(/[^0-9.]/g, ""));
const w = parseNum(widthMatch[1]);
const h = parseNum(heightMatch[1]);
if (w > 0 && h > 0) {
const ratio = w / h;
self.svgAspectRatioCache.set(src, ratio);
return ratio;
}
}
// Not an SVG or no usable metadata
} catch (e) {
// fetch may fail due to CORS or non-SVG..
}

return null;
}
100 changes: 30 additions & 70 deletions src/client/graphics/layers/EventsDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,22 @@ export class EventsDisplay extends LitElement implements Layer {
`;
}

private renderToggleButton(src: string, category: MessageCategory) {
// Adding the literal for the default size ensures tailwind will generate the class
const toggleButtonSizeMap = { default: "h-5" };
return this.renderButton({
content: html`<img
src="${src}"
class="${toggleButtonSizeMap["default"]}"
style="filter: ${this.eventsFilters.get(category)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () => this.toggleEventFilter(category),
className: "cursor-pointer pointer-events-auto",
});
}

private toggleHidden() {
this._hidden = !this._hidden;
if (this._hidden) {
Expand Down Expand Up @@ -935,76 +951,20 @@ export class EventsDisplay extends LitElement implements Layer {
>
<div class="flex justify-between items-center">
<div class="flex gap-4">
${this.renderButton({
content: html`<img
src="${swordIcon}"
class="w-5 h-5"
style="filter: ${this.eventsFilters.get(
MessageCategory.ATTACK,
)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () =>
this.toggleEventFilter(MessageCategory.ATTACK),
className: "cursor-pointer pointer-events-auto",
})}
${this.renderButton({
content: html`<img
src="${nukeIcon}"
class="w-5 h-5"
style="filter: ${this.eventsFilters.get(
MessageCategory.NUKE,
)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () =>
this.toggleEventFilter(MessageCategory.NUKE),
className: "cursor-pointer pointer-events-auto",
})}
${this.renderButton({
content: html`<img
src="${donateGoldIcon}"
class="w-5 h-5"
style="filter: ${this.eventsFilters.get(
MessageCategory.TRADE,
)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () =>
this.toggleEventFilter(MessageCategory.TRADE),
className: "cursor-pointer pointer-events-auto",
})}
${this.renderButton({
content: html`<img
src="${allianceIcon}"
class="w-5 h-5"
style="filter: ${this.eventsFilters.get(
MessageCategory.ALLIANCE,
)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () =>
this.toggleEventFilter(MessageCategory.ALLIANCE),
className: "cursor-pointer pointer-events-auto",
})}
${this.renderButton({
content: html`<img
src="${chatIcon}"
class="w-5 h-5"
style="filter: ${this.eventsFilters.get(
MessageCategory.CHAT,
)
? "grayscale(1) opacity(0.5)"
: "none"}"
/>`,
onClick: () =>
this.toggleEventFilter(MessageCategory.CHAT),
className: "cursor-pointer pointer-events-auto",
})}
${this.renderToggleButton(
swordIcon,
MessageCategory.ATTACK,
)}
${this.renderToggleButton(nukeIcon, MessageCategory.NUKE)}
${this.renderToggleButton(
donateGoldIcon,
MessageCategory.TRADE,
)}
${this.renderToggleButton(
allianceIcon,
MessageCategory.ALLIANCE,
)}
${this.renderToggleButton(chatIcon, MessageCategory.CHAT)}
</div>
<div class="flex items-center gap-3">
${this.latestGoldAmount !== null
Expand Down
23 changes: 21 additions & 2 deletions src/client/graphics/layers/RadialMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as d3 from "d3";
import backIcon from "../../../../resources/images/BackIconWhite.svg";
import { EventBus, GameEvent } from "../../../core/EventBus";
import { CloseViewEvent } from "../../InputHandler";
import { translateText } from "../../Utils";
import { getSvgAspectRatio, translateText } from "../../Utils";
import { Layer } from "./Layer";
import {
CenterButtonElement,
Expand Down Expand Up @@ -542,7 +542,7 @@ export class RadialMenu implements Layer {
.style("opacity", disabled ? 0.5 : 1)
.text(d.data.text);
} else {
content
const imgSel = content
.append("image")
.attr("xlink:href", d.data.icon!)
.attr("width", this.config.iconSize)
Expand All @@ -551,6 +551,25 @@ export class RadialMenu implements Layer {
.attr("y", arc.centroid(d)[1] - this.config.iconSize / 2)
.attr("opacity", disabled ? 0.5 : 1);

getSvgAspectRatio(d.data.icon!).then((aspect) => {
if (!aspect || aspect === 1) return;

let width = this.config.iconSize;
let height = this.config.iconSize;
const biggerLength = Math.round(width * aspect);
if (aspect > 1) {
width = biggerLength;
} else {
height = biggerLength;
}

imgSel
.attr("width", width)
.attr("height", height)
.attr("x", arc.centroid(d)[0] - width / 2)
.attr("y", arc.centroid(d)[1] - height / 2);
});

if (this.params && d.data.cooldown?.(this.params)) {
const cooldown = Math.ceil(d.data.cooldown?.(this.params));
content
Expand Down
Loading