From f0a6bd1bddaacab430a3df6802e9f8866abd69ad Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 12:01:42 +0100 Subject: [PATCH 01/13] Add alliance request to main dial --- src/client/hud/layers/RadialMenu.ts | 80 ++++++++++++++------- src/client/hud/layers/RadialMenuElements.ts | 3 + src/core/GameRunner.ts | 2 + src/core/game/Game.ts | 2 + src/core/game/PlayerImpl.ts | 19 +++++ 5 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 740a0b90e4..b2df355e40 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -330,12 +330,21 @@ export class RadialMenu implements Controller { .append("path") .attr("class", "menu-item-path") .attr("d", arc) + .attr("alliance", true) .attr("fill", (d) => { const disabled = this.params === null || d.data.disabled(this.params); - const color = disabled + + const isAllianceCooldown = + d.data.id === "ally_request" && + (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + + const color = isAllianceCooldown + ? "#0891b2" + : disabled ? this.config.disabledColor : (resolveColor(d.data, this.params) ?? "#1e3a5f"); - const opacity = disabled ? 0.4 : 0.82; + + const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; if (d.data.id === this.selectedItemId && this.currentLevel > level) { return color; @@ -349,9 +358,14 @@ export class RadialMenu implements Controller { ? "not-allowed" : "pointer", ) - .style("opacity", (d) => - this.params === null || d.data.disabled(this.params) ? 0.5 : 1, - ) + + .style("opacity", (d) => { + const disabled = this.params === null || d.data.disabled(this.params); + const isAllianceCooldown = d.data.id === "ally_request" && (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + //to remove the fade down change 0.85 to 1 + return isAllianceCooldown ? 0.85 : disabled ? 0.5 : 1; + }) + .style( "transition", `filter ${this.config.menuTransitionDuration / 2}ms, fill ${this.config.menuTransitionDuration / 2}ms`, @@ -419,11 +433,17 @@ export class RadialMenu implements Controller { ) { path.attr("filter", "url(#glow)"); - const color = - this.params === null || d.data.disabled(this.params) - ? this.config.disabledColor - : (resolveColor(d.data, this.params) ?? "#1e3a5f"); - path.attr("fill", color); + const isAllianceCooldown = + d.data.id === "ally_request" && + (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + const color = isAllianceCooldown + ? "#0891b2" + : this.params === null || d.data.disabled(this.params) + ? this.config.disabledColor + : (resolveColor(d.data, this.params) ?? "#1e3a5f"); + + const opacity = isAllianceCooldown ? 0.82 : this.params === null || d.data.disabled(this.params) ? 0.4 : 0.82; + path.attr("fill", d3.color(color)?.copy({ opacity: opacity })?.toString() ?? color); } }); @@ -484,10 +504,13 @@ export class RadialMenu implements Controller { ) return; path.style("filter", null); - const color = disabled + const isAllianceCooldown = + d.data.id === "ally_request" && + (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + const color = isAllianceCooldown? "#0891b2": disabled ? this.config.disabledColor : (resolveColor(d.data, this.params) ?? "#333333"); - const opacity = disabled ? 0.4 : 0.82; + const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; if (d.data.timerFraction) { path.attr("fill", `url(#timer-gradient-${d.data.id})`); @@ -596,6 +619,9 @@ export class RadialMenu implements Controller { const contentId = d.data.id; const content = d3.select(`g[data-id="${contentId}"]`); const disabled = this.isItemDisabled(d.data); + const isAllianceCooldown = + d.data.id === "ally_request" && + (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; if (d.data.renderType && this.params) { const stateKey = this.getStateKeyByType( @@ -627,7 +653,7 @@ export class RadialMenu implements Controller { .attr("fill", "white") .attr("font-size", d.data.fontSize ?? "12px") .attr("font-family", "Arial, sans-serif") - .style("opacity", disabled ? 0.5 : 1) + .style("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1) .text(d.data.text); } else { const imgSel = content @@ -637,7 +663,7 @@ export class RadialMenu implements Controller { .attr("height", this.config.iconSize) .attr("x", arc.centroid(d)[0] - this.config.iconSize / 2) .attr("y", arc.centroid(d)[1] - this.config.iconSize / 2) - .attr("opacity", disabled ? 0.5 : 1); + .attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); getSvgAspectRatio(d.data.icon!).then((aspect) => { if (!aspect || aspect === 1) return; @@ -655,7 +681,7 @@ export class RadialMenu implements Controller { .attr("width", width) .attr("height", height) .attr("x", arc.centroid(d)[0] - width / 2) - .attr("y", arc.centroid(d)[1] - height / 2); + .attr("y", isAllianceCooldown ? 42 : arc.centroid(d)[1] - height / 2); }); if (this.params && d.data.cooldown?.(this.params)) { @@ -663,10 +689,10 @@ export class RadialMenu implements Controller { content .append("text") .attr("class", `cooldown-text`) - .text(cooldown + "s") + .text(String(cooldown)) .attr("fill", "white") - .attr("opacity", disabled ? 0.5 : 1) - .attr("font-size", "14px") + .attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1) + .attr("font-size", "20px") .attr("font-weight", "bold") .attr("x", arc.centroid(d)[0] - this.config.iconSize / 4) .attr("y", arc.centroid(d)[1] + this.config.iconSize / 2 + 7); @@ -1119,12 +1145,12 @@ export class RadialMenu implements Controller { this.menuPaths.forEach((path, itemId) => { const item = this.findMenuItem(itemId); if (item) { + const isAllianceCooldown = item.id === "ally_request" && (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; const disabled = this.isItemDisabled(item); - const color = disabled + const color = isAllianceCooldown ? "#0891b2" : disabled ? this.config.disabledColor : (resolveColor(item, this.params) ?? "#333333"); - const opacity = disabled ? 0.4 : 0.82; - + const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; // Update path appearance (skip fill for timer items — gradient handles it) if (!item.timerFraction) { path.attr( @@ -1132,7 +1158,7 @@ export class RadialMenu implements Controller { d3.color(color)?.copy({ opacity: opacity })?.toString() ?? color, ); } - path.style("opacity", disabled ? 0.5 : 1); + path.style("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); path.style("cursor", disabled ? "not-allowed" : "pointer"); // Update icon/text appearance using the same logic as renderIconsAndText @@ -1144,13 +1170,13 @@ export class RadialMenu implements Controller { // Update text opacity const textElement = icon.select("text"); if (!textElement.empty()) { - textElement.style("opacity", disabled ? 0.5 : 1); + textElement.style("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); } // Update image opacity const imageElement = icon.select("image"); if (!imageElement.empty()) { - imageElement.attr("opacity", disabled ? 0.5 : 1); + imageElement.attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); } // Update cooldown text if applicable @@ -1160,7 +1186,7 @@ export class RadialMenu implements Controller { if (cooldown <= 0) { cooldownElement.remove(); } else { - cooldownElement.text(cooldown + "s"); + cooldownElement.text(cooldown); } } } @@ -1288,11 +1314,11 @@ export class RadialMenu implements Controller { const otherAgreed = interaction?.allianceInfo?.otherAgreedToExtend ?? false; const ns = "http://www.w3.org/2000/svg"; - const smallSize = iconSize * 0.8; + const smallSize = iconSize; const iconUrl = icon ?? ""; getSvgAspectRatio(iconUrl).then((ratio) => { - const width = smallSize * (ratio ?? 1); + const width = smallSize; const gap = 2; const totalWidth = width * 2 + gap; diff --git a/src/client/hud/layers/RadialMenuElements.ts b/src/client/hud/layers/RadialMenuElements.ts index 9ef673c83e..706ef4277e 100644 --- a/src/client/hud/layers/RadialMenuElements.ts +++ b/src/client/hud/layers/RadialMenuElements.ts @@ -96,6 +96,7 @@ export const COLORS = { boat: "#2a82c9", disabled: "#94a3b8", ally: "#4ade80", + alianceTimeLeft: "#0891b2", breakAlly: "#dc2626", breakAllyNoDebuff: "#d97706", delete: "#ef4444", @@ -218,6 +219,8 @@ const allyRequestElement: MenuElement = { name: "request", disabled: (params: MenuElementParams) => !params.playerActions?.interaction?.canSendAllianceRequest, + cooldown: (params: MenuElementParams) => + params.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0, displayed: (params: MenuElementParams) => !params.playerActions?.interaction?.canBreakAlliance, color: COLORS.ally, diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index 3532720953..996b215b39 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -262,6 +262,8 @@ export class GameRunner { canSendEmoji: player.canSendEmoji(other), canTarget: player.canTarget(other), canSendAllianceRequest: player.canSendAllianceRequest(other), + allianceRequestCooldownRemaining: + player.allianceRequestCooldownRemaining(other), canBreakAlliance: player.isAlliedWith(other), canDonateGold: player.canDonateGold(other), canDonateTroops: player.canDonateTroops(other), diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 4018ab690f..085afc4a30 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -668,6 +668,7 @@ export interface Player { allianceWith(other: Player): MutableAlliance | null; allianceInfo(other: Player): AllianceInfo | null; canSendAllianceRequest(other: Player): boolean; + allianceRequestCooldownRemaining(other: Player): number; breakAlliance(alliance: Alliance): void; removeAllAlliances(): void; createAllianceRequest(recipient: Player): AllianceRequest | null; @@ -964,6 +965,7 @@ export interface PlayerInteraction { sharedBorder: boolean; canSendEmoji: boolean; canSendAllianceRequest: boolean; + allianceRequestCooldownRemaining?: number; canBreakAlliance: boolean; canTarget: boolean; canDonateGold: boolean; diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index b468916db9..5d4cff1fa4 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -736,6 +736,25 @@ export class PlayerImpl implements Player { return delta >= this.mg.config().allianceRequestCooldown(); } + allianceRequestCooldownRemaining(other: Player): number { + + if (this.isFriendly(other)) { + return 0; + } + + const recent = this.pastOutgoingAllianceRequests + .filter((ar) => ar.recipient() === other) + .sort((a, b) => b.createdAt() - a.createdAt()); + + if (recent.length === 0) { + return 0; + } + + const delta = this.mg.ticks() - recent[0].createdAt(); + + return Math.max((this.mg.config().allianceRequestCooldown() - delta) / 10, 0); + } + breakAlliance(alliance: MutableAlliance): void { this.mg.breakAlliance(this, alliance); } From efcdb7359b39511cdc799be9439718a9ca8d7180 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 12:47:18 +0100 Subject: [PATCH 02/13] fix unwanted changes --- src/client/hud/layers/RadialMenu.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index b2df355e40..02da7a0198 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -694,7 +694,8 @@ export class RadialMenu implements Controller { .attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1) .attr("font-size", "20px") .attr("font-weight", "bold") - .attr("x", arc.centroid(d)[0] - this.config.iconSize / 4) + .attr("text-anchor", "middle") + .attr("x", arc.centroid(d)[0]) .attr("y", arc.centroid(d)[1] + this.config.iconSize / 2 + 7); } } @@ -1314,11 +1315,11 @@ export class RadialMenu implements Controller { const otherAgreed = interaction?.allianceInfo?.otherAgreedToExtend ?? false; const ns = "http://www.w3.org/2000/svg"; - const smallSize = iconSize; + const smallSize = iconSize * 0.8; const iconUrl = icon ?? ""; getSvgAspectRatio(iconUrl).then((ratio) => { - const width = smallSize; + const width = smallSize * (ratio ?? 1); const gap = 2; const totalWidth = width * 2 + gap; From 4ffca98d3f8fc3e1a4addd074983e8d854b18c9c Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 13:01:39 +0100 Subject: [PATCH 03/13] Formatting --- src/client/hud/layers/RadialMenu.ts | 78 ++++++++++++++++++++--------- src/core/game/PlayerImpl.ts | 8 +-- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 02da7a0198..17d2264385 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -336,13 +336,14 @@ export class RadialMenu implements Controller { const isAllianceCooldown = d.data.id === "ally_request" && - (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; const color = isAllianceCooldown ? "#0891b2" : disabled - ? this.config.disabledColor - : (resolveColor(d.data, this.params) ?? "#1e3a5f"); + ? this.config.disabledColor + : (resolveColor(d.data, this.params) ?? "#1e3a5f"); const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; @@ -358,13 +359,16 @@ export class RadialMenu implements Controller { ? "not-allowed" : "pointer", ) - + .style("opacity", (d) => { const disabled = this.params === null || d.data.disabled(this.params); - const isAllianceCooldown = d.data.id === "ally_request" && (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + const isAllianceCooldown = + d.data.id === "ally_request" && + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; //to remove the fade down change 0.85 to 1 return isAllianceCooldown ? 0.85 : disabled ? 0.5 : 1; - }) + }) .style( "transition", @@ -435,15 +439,23 @@ export class RadialMenu implements Controller { const isAllianceCooldown = d.data.id === "ally_request" && - (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; const color = isAllianceCooldown ? "#0891b2" : this.params === null || d.data.disabled(this.params) - ? this.config.disabledColor - : (resolveColor(d.data, this.params) ?? "#1e3a5f"); + ? this.config.disabledColor + : (resolveColor(d.data, this.params) ?? "#1e3a5f"); - const opacity = isAllianceCooldown ? 0.82 : this.params === null || d.data.disabled(this.params) ? 0.4 : 0.82; - path.attr("fill", d3.color(color)?.copy({ opacity: opacity })?.toString() ?? color); + const opacity = isAllianceCooldown + ? 0.82 + : this.params === null || d.data.disabled(this.params) + ? 0.4 + : 0.82; + path.attr( + "fill", + d3.color(color)?.copy({ opacity: opacity })?.toString() ?? color, + ); } }); @@ -506,11 +518,14 @@ export class RadialMenu implements Controller { path.style("filter", null); const isAllianceCooldown = d.data.id === "ally_request" && - (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; - const color = isAllianceCooldown? "#0891b2": disabled - ? this.config.disabledColor - : (resolveColor(d.data, this.params) ?? "#333333"); - const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; + const color = isAllianceCooldown + ? "#0891b2" + : disabled + ? this.config.disabledColor + : (resolveColor(d.data, this.params) ?? "#333333"); + const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; if (d.data.timerFraction) { path.attr("fill", `url(#timer-gradient-${d.data.id})`); @@ -621,7 +636,8 @@ export class RadialMenu implements Controller { const disabled = this.isItemDisabled(d.data); const isAllianceCooldown = d.data.id === "ally_request" && - (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; if (d.data.renderType && this.params) { const stateKey = this.getStateKeyByType( @@ -681,7 +697,10 @@ export class RadialMenu implements Controller { .attr("width", width) .attr("height", height) .attr("x", arc.centroid(d)[0] - width / 2) - .attr("y", isAllianceCooldown ? 42 : arc.centroid(d)[1] - height / 2); + .attr( + "y", + isAllianceCooldown ? 42 : arc.centroid(d)[1] - height / 2, + ); }); if (this.params && d.data.cooldown?.(this.params)) { @@ -1146,11 +1165,16 @@ export class RadialMenu implements Controller { this.menuPaths.forEach((path, itemId) => { const item = this.findMenuItem(itemId); if (item) { - const isAllianceCooldown = item.id === "ally_request" && (this.params?.playerActions?.interaction?.allianceRequestCooldownRemaining ?? 0) > 0; + const isAllianceCooldown = + item.id === "ally_request" && + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; const disabled = this.isItemDisabled(item); - const color = isAllianceCooldown ? "#0891b2" : disabled - ? this.config.disabledColor - : (resolveColor(item, this.params) ?? "#333333"); + const color = isAllianceCooldown + ? "#0891b2" + : disabled + ? this.config.disabledColor + : (resolveColor(item, this.params) ?? "#333333"); const opacity = isAllianceCooldown ? 0.82 : disabled ? 0.4 : 0.82; // Update path appearance (skip fill for timer items — gradient handles it) if (!item.timerFraction) { @@ -1171,13 +1195,19 @@ export class RadialMenu implements Controller { // Update text opacity const textElement = icon.select("text"); if (!textElement.empty()) { - textElement.style("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); + textElement.style( + "opacity", + isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1, + ); } // Update image opacity const imageElement = icon.select("image"); if (!imageElement.empty()) { - imageElement.attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1); + imageElement.attr( + "opacity", + isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1, + ); } // Update cooldown text if applicable diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 5d4cff1fa4..903b22b562 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -737,12 +737,11 @@ export class PlayerImpl implements Player { } allianceRequestCooldownRemaining(other: Player): number { - if (this.isFriendly(other)) { return 0; } - const recent = this.pastOutgoingAllianceRequests + const recent = this.pastOutgoingAllianceRequests .filter((ar) => ar.recipient() === other) .sort((a, b) => b.createdAt() - a.createdAt()); @@ -752,7 +751,10 @@ export class PlayerImpl implements Player { const delta = this.mg.ticks() - recent[0].createdAt(); - return Math.max((this.mg.config().allianceRequestCooldown() - delta) / 10, 0); + return Math.max( + (this.mg.config().allianceRequestCooldown() - delta) / 10, + 0, + ); } breakAlliance(alliance: MutableAlliance): void { From 7920d22334d44896f71b64d80a95221f402fe807 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 15:50:33 +0100 Subject: [PATCH 04/13] Fixed code rabbit issues --- src/client/hud/layers/RadialMenu.ts | 54 +++++++++++++-------- src/client/hud/layers/RadialMenuElements.ts | 2 +- src/core/game/PlayerImpl.ts | 38 +++++++++++++-- 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 17d2264385..b3159332d0 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -7,6 +7,7 @@ import { PlaySoundEffectEvent } from "../../sound/Sounds"; import { getSvgAspectRatio, translateText } from "../../Utils"; import { CenterButtonElement, + COLORS, MenuElement, MenuElementParams, TooltipKey, @@ -330,7 +331,6 @@ export class RadialMenu implements Controller { .append("path") .attr("class", "menu-item-path") .attr("d", arc) - .attr("alliance", true) .attr("fill", (d) => { const disabled = this.params === null || d.data.disabled(this.params); @@ -340,7 +340,7 @@ export class RadialMenu implements Controller { ?.allianceRequestCooldownRemaining ?? 0) > 0; const color = isAllianceCooldown - ? "#0891b2" + ? COLORS.allianceTimeLeft : disabled ? this.config.disabledColor : (resolveColor(d.data, this.params) ?? "#1e3a5f"); @@ -442,7 +442,7 @@ export class RadialMenu implements Controller { (this.params?.playerActions?.interaction ?.allianceRequestCooldownRemaining ?? 0) > 0; const color = isAllianceCooldown - ? "#0891b2" + ? COLORS.allianceTimeLeft : this.params === null || d.data.disabled(this.params) ? this.config.disabledColor : (resolveColor(d.data, this.params) ?? "#1e3a5f"); @@ -521,7 +521,7 @@ export class RadialMenu implements Controller { (this.params?.playerActions?.interaction ?.allianceRequestCooldownRemaining ?? 0) > 0; const color = isAllianceCooldown - ? "#0891b2" + ? COLORS.allianceTimeLeft : disabled ? this.config.disabledColor : (resolveColor(d.data, this.params) ?? "#333333"); @@ -639,6 +639,11 @@ export class RadialMenu implements Controller { (this.params?.playerActions?.interaction ?.allianceRequestCooldownRemaining ?? 0) > 0; + content.attr( + "data-cooldown-active", + isAllianceCooldown ? "true" : "false", + ); + if (d.data.renderType && this.params) { const stateKey = this.getStateKeyByType( d.data.renderType, @@ -1171,7 +1176,7 @@ export class RadialMenu implements Controller { ?.allianceRequestCooldownRemaining ?? 0) > 0; const disabled = this.isItemDisabled(item); const color = isAllianceCooldown - ? "#0891b2" + ? COLORS.allianceTimeLeft : disabled ? this.config.disabledColor : (resolveColor(item, this.params) ?? "#333333"); @@ -1248,28 +1253,37 @@ export class RadialMenu implements Controller { ); const prevState = icon.attr("data-prev-state"); - if (stateKey && stateKey === prevState) { - // State unchanged, skip re-render to preserve animations - } else { + const cooldownActive = + (this.params?.playerActions?.interaction + ?.allianceRequestCooldownRemaining ?? 0) > 0; + + const previousCooldownActive = icon.attr("data-cooldown-active") === "true"; + + const cooldownChanged = cooldownActive !== previousCooldownActive; + + const stateChanged = stateKey !== prevState; + + if (!cooldownChanged && !stateChanged) { + icon.attr("data-cooldown-active", cooldownActive ? "true" : "false"); + const cx = parseFloat(icon.attr("data-cx") || "0"); const cy = parseFloat(icon.attr("data-cy") || "0"); if (stateKey) { icon.attr("data-prev-state", stateKey); + // State unchanged, skip re-render to preserve animations } else { - icon.selectAll("*").remove(); + this.renderAllyExtendIcon( + icon.node()! as SVGGElement, + cx, + cy, + this.config.iconSize, + disabled, + this.params, + item.icon, + true, + ); } - - this.renderAllyExtendIcon( - icon.node()! as SVGGElement, - cx, - cy, - this.config.iconSize, - disabled, - this.params, - item.icon, - true, - ); } } diff --git a/src/client/hud/layers/RadialMenuElements.ts b/src/client/hud/layers/RadialMenuElements.ts index 706ef4277e..46d0fdbd9d 100644 --- a/src/client/hud/layers/RadialMenuElements.ts +++ b/src/client/hud/layers/RadialMenuElements.ts @@ -96,7 +96,7 @@ export const COLORS = { boat: "#2a82c9", disabled: "#94a3b8", ally: "#4ade80", - alianceTimeLeft: "#0891b2", + allianceTimeLeft: "#0891b2", breakAlly: "#dc2626", breakAllyNoDebuff: "#d97706", delete: "#ef4444", diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 903b22b562..879f5f81ee 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -737,7 +737,36 @@ export class PlayerImpl implements Player { } allianceRequestCooldownRemaining(other: Player): number { - if (this.isFriendly(other)) { + if (this.mg.config().disableAlliances()) { + return 0; + } + if (other === this) { + return 0; + } + if (this.isDisconnected() || other.isDisconnected()) { + // Disconnected players are marked as not-friendly even if they are allies, + // so we need to return early if either player is disconnected. + // Otherwise we could end up sending an alliance request to someone + // we are already allied with. + return 0; + } + if (this.isFriendly(other) || !this.isAlive()) { + return 0; + } + + const hasPending = this.outgoingAllianceRequests().some( + (ar) => ar.recipient() === other, + ); + + if (hasPending) { + return 0; + } + + const hasIncoming = this.incomingAllianceRequests().some( + (ar) => ar.requestor() === other, + ); + + if (hasIncoming) { return 0; } @@ -751,10 +780,9 @@ export class PlayerImpl implements Player { const delta = this.mg.ticks() - recent[0].createdAt(); - return Math.max( - (this.mg.config().allianceRequestCooldown() - delta) / 10, - 0, - ); + const remainingTicks = this.mg.config().allianceRequestCooldown() - delta; + + return Math.max(Math.floor((remainingTicks + 9) / 10), 0); } breakAlliance(alliance: MutableAlliance): void { From a6597a044c1e866cbb0e6ea867a4201562bc9c35 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 16:06:58 +0100 Subject: [PATCH 05/13] added early return if nothing changed --- src/client/hud/layers/RadialMenu.ts | 37 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index b3159332d0..fc69264a6a 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -1264,27 +1264,28 @@ export class RadialMenu implements Controller { const stateChanged = stateKey !== prevState; if (!cooldownChanged && !stateChanged) { - icon.attr("data-cooldown-active", cooldownActive ? "true" : "false"); + return; + } - const cx = parseFloat(icon.attr("data-cx") || "0"); - const cy = parseFloat(icon.attr("data-cy") || "0"); + icon.attr("data-cooldown-active", cooldownActive ? "true" : "false"); - if (stateKey) { - icon.attr("data-prev-state", stateKey); - // State unchanged, skip re-render to preserve animations - } else { - this.renderAllyExtendIcon( - icon.node()! as SVGGElement, - cx, - cy, - this.config.iconSize, - disabled, - this.params, - item.icon, - true, - ); - } + const cx = parseFloat(icon.attr("data-cx") || "0"); + const cy = parseFloat(icon.attr("data-cy") || "0"); + + if (stateKey) { + icon.attr("data-prev-state", stateKey); + // State unchanged, skip re-render to preserve animations } + this.renderAllyExtendIcon( + icon.node()! as SVGGElement, + cx, + cy, + this.config.iconSize, + disabled, + this.params, + item.icon, + true, + ); } private maybeUpdateTimerGradient( From 98dc90f2754af398bc3048d2812da1eaa62436d5 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 16:30:06 +0100 Subject: [PATCH 06/13] removed early exit for the non-square aspect ratio check, allowing the y value to be updated --- src/client/hud/layers/RadialMenu.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index fc69264a6a..9f68c9830f 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -687,15 +687,17 @@ export class RadialMenu implements Controller { .attr("opacity", isAllianceCooldown ? 0.82 : 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; + + if (aspect !== null && aspect !== 1) { + const biggerLength = Math.round(width * aspect); + + if (aspect > 1) { + width = biggerLength; + } else { + height = biggerLength; + } } imgSel From 3c444fa2aa162f9609119960d847ec8abf0c59eb Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Wed, 12 Aug 2026 16:48:25 +0100 Subject: [PATCH 07/13] added Tests for core cooldown contract --- tests/PlayerImpl.test.ts | 106 ++++++++++++++++++++++++++++++++++ tests/core/GameRunner.test.ts | 30 ++++++++++ 2 files changed, 136 insertions(+) diff --git a/tests/PlayerImpl.test.ts b/tests/PlayerImpl.test.ts index ed6199596c..142edc6110 100644 --- a/tests/PlayerImpl.test.ts +++ b/tests/PlayerImpl.test.ts @@ -169,4 +169,110 @@ describe("PlayerImpl", () => { expect(player.numTilesOwned()).toBe(0); }); }); + + describe("allianceRequestCooldownRemaining()", () => { + let other: Player; + let friend: Player; + + beforeEach(async () => { + game = await setup("plains", { instantBuild: true }, [ + new PlayerInfo("player", PlayerType.Human, null, "player_id"), + new PlayerInfo("other", PlayerType.Human, null, "other_id"), + new PlayerInfo("friend", PlayerType.Human, null, "friend_id"), + ]); + + player = game.player("player_id"); + other = game.player("other_id"); + friend = game.player("friend_id"); + + player.conquer(game.ref(0, 0)); + other.conquer(game.ref(50, 50)); + friend.conquer(game.ref(30, 30)); + }); + + test("returns 0 when alliances are disabled", async () => { + const disabledGame = await setup( + "plains", + { instantBuild: true, disableAlliances: true }, + [ + new PlayerInfo("player", PlayerType.Human, null, "player_id"), + new PlayerInfo("other", PlayerType.Human, null, "other_id"), + ], + ); + const disabledPlayer = disabledGame.player("player_id"); + const disabledOther = disabledGame.player("other_id"); + disabledPlayer.conquer(disabledGame.ref(0, 0)); + disabledOther.conquer(disabledGame.ref(50, 50)); + + expect( + disabledPlayer.allianceRequestCooldownRemaining(disabledOther), + ).toBe(0); + }); + + test("returns 0 for self", () => { + expect(player.allianceRequestCooldownRemaining(player)).toBe(0); + }); + + test("returns 0 when either player is disconnected", () => { + player.markDisconnected(true); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + player.markDisconnected(false); + other.markDisconnected(true); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("returns 0 for friendly players", () => { + const request = player.createAllianceRequest(other); + expect(request).not.toBeNull(); + request!.accept(); + + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("returns 0 for dead players", () => { + const allTiles = Array.from(player.tiles()); + for (const tile of allTiles) { + player.relinquish(tile); + } + expect(player.isAlive()).toBe(false); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("returns 0 while a request is pending", () => { + const request = player.createAllianceRequest(other); + expect(request).not.toBeNull(); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("returns 0 while an incoming request exists", () => { + const request = other.createAllianceRequest(player); + expect(request).not.toBeNull(); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("returns 0 when no recent request exists", () => { + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + + test("rounds remaining cooldown up to whole seconds and expires", () => { + const request = player.createAllianceRequest(other); + expect(request).not.toBeNull(); + request!.reject(); + + const cooldownTicks = game.config().allianceRequestCooldown(); + const expectedSeconds = Math.floor((cooldownTicks + 9) / 10); + expect(player.allianceRequestCooldownRemaining(other)).toBe( + expectedSeconds, + ); + + const ticksUntilLastSecond = cooldownTicks - 1; + for (let i = 0; i < ticksUntilLastSecond; i++) { + game.executeNextTick(); + } + + expect(player.allianceRequestCooldownRemaining(other)).toBe(1); + game.executeNextTick(); + expect(player.allianceRequestCooldownRemaining(other)).toBe(0); + }); + }); }); diff --git a/tests/core/GameRunner.test.ts b/tests/core/GameRunner.test.ts index f02b078235..c96beaaada 100644 --- a/tests/core/GameRunner.test.ts +++ b/tests/core/GameRunner.test.ts @@ -1,5 +1,7 @@ import { NationExecution } from "../../src/core/execution/NationExecution"; import { SpawnExecution } from "../../src/core/execution/SpawnExecution"; +import { Executor } from "../../src/core/execution/ExecutionManager"; +import { GameRunner } from "../../src/core/GameRunner"; import { Cell, Nation, PlayerInfo, PlayerType } from "../../src/core/game/Game"; import { GameConfig, GameID } from "../../src/core/Schemas"; import { setup } from "../util/Setup"; @@ -103,3 +105,31 @@ describe("Nation spawn ordering with random spawn", () => { expect(game.player(nations[0].info.id).isAlive()).toBe(true); }); }); + +describe("GameRunner playerActions interaction payload", () => { + test("forwards player alliance cooldown remaining through playerActions", async () => { + const game = await setup("plains", { instantBuild: true }, [ + new PlayerInfo("player", PlayerType.Human, null, "player_id"), + new PlayerInfo("other", PlayerType.Human, null, "other_id"), + ]); + + const player = game.player("player_id"); + const other = game.player("other_id"); + player.conquer(game.ref(0, 0)); + other.conquer(game.ref(50, 50)); + + const runner = new GameRunner( + game, + new Executor(game, gameID, undefined, []), + () => {}, + ); + + const expected = player.allianceRequestCooldownRemaining(other); + const actions = runner.playerActions(player.id(), 50, 50); + + expect(actions.interaction).toBeDefined(); + expect(actions.interaction?.allianceRequestCooldownRemaining).toBe( + expected, + ); + }); +}); From a2b43e5b43c946d8d85f99427635769876bdb157 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Mon, 17 Aug 2026 12:46:16 +0100 Subject: [PATCH 08/13] added text to translations and fixed race conditions --- resources/lang/en.json | 3 +- src/client/hud/layers/RadialMenu.ts | 64 +++++++++++++++++++++++++---- tests/core/GameRunner.test.ts | 9 ++-- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index a8f56fef7f..79d76e696e 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -545,7 +545,8 @@ "trade_ship_captured": "Your trade ship was captured by {name}", "unit_destroyed": "Your {unit} was destroyed", "unit_voluntarily_deleted": "Unit voluntarily deleted", - "wants_to_renew_alliance": "{name} wants to renew your alliance" + "wants_to_renew_alliance": "{name} wants to renew your alliance", + "alliance_cooldown_text": "{amount}" }, "featured_stream": { "expand": "Expand", diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 9f68c9830f..5d7e5c6d29 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -712,10 +712,15 @@ export class RadialMenu implements Controller { if (this.params && d.data.cooldown?.(this.params)) { const cooldown = Math.ceil(d.data.cooldown?.(this.params)); + content .append("text") .attr("class", `cooldown-text`) - .text(String(cooldown)) + .text( + translateText("events_display.alliance_cooldown_text", { + amount: cooldown, + }), + ) .attr("fill", "white") .attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1) .attr("font-size", "20px") @@ -1208,9 +1213,14 @@ export class RadialMenu implements Controller { ); } - // Update image opacity + // Update image opacity and position const imageElement = icon.select("image"); if (!imageElement.empty()) { + const height = parseFloat(imageElement.attr("height") ?? "0"); + const cy = parseFloat(icon.attr("data-cy") ?? "0"); + + imageElement.attr("y", isAllianceCooldown ? 42 : cy - height / 2); + imageElement.attr( "opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1, @@ -1218,19 +1228,44 @@ export class RadialMenu implements Controller { } // Update cooldown text if applicable - const cooldownElement = icon.select(".cooldown-text"); - if (this.params && !cooldownElement.empty() && item.cooldown) { + if (this.params && item.cooldown) { const cooldown = Math.ceil(item.cooldown(this.params)); + let cooldownText = icon.select(".cooldown-text"); + if (cooldown <= 0) { - cooldownElement.remove(); + cooldownText.remove(); } else { - cooldownElement.text(cooldown); + if (cooldownText.empty()) { + const cx = parseFloat(icon.attr("data-cx") ?? "0"); + const cy = parseFloat(icon.attr("data-cy") ?? "0"); + + cooldownText = icon + .append("text") + .attr("class", "cooldown-text") + .attr("fill", "white") + .attr("font-size", "20px") + .attr("font-weight", "bold") + .attr("text-anchor", "middle") + .attr("x", cx) + .attr("y", cy + this.config.iconSize / 2 + 7); + } + + cooldownText + .text( + translateText("events_display.alliance_cooldown_text", { + amount: cooldown, + }), + ) + .attr( + "opacity", + isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1, + ); } } - } - // Update timer gradient - this.maybeUpdateTimerGradient(item, color, opacity); + // Update timer gradient + this.maybeUpdateTimerGradient(item, color, opacity); + } } } }); @@ -1353,6 +1388,11 @@ export class RadialMenu implements Controller { icon?: string, update?: boolean, ): void { + const generation = + Number(content.getAttribute("data-render-generation") ?? "0") + 1; + + content.setAttribute("data-render-generation", generation.toString()); + if (update) { while (content.firstChild) content.removeChild(content.firstChild); } @@ -1366,6 +1406,12 @@ export class RadialMenu implements Controller { const iconUrl = icon ?? ""; getSvgAspectRatio(iconUrl).then((ratio) => { + if ( + Number(content.getAttribute("data-render-gneration")) !== generation + ) { + return; + } + const width = smallSize * (ratio ?? 1); const gap = 2; const totalWidth = width * 2 + gap; diff --git a/tests/core/GameRunner.test.ts b/tests/core/GameRunner.test.ts index c96beaaada..997e4f2ea7 100644 --- a/tests/core/GameRunner.test.ts +++ b/tests/core/GameRunner.test.ts @@ -1,8 +1,8 @@ +import { Executor } from "../../src/core/execution/ExecutionManager"; import { NationExecution } from "../../src/core/execution/NationExecution"; import { SpawnExecution } from "../../src/core/execution/SpawnExecution"; -import { Executor } from "../../src/core/execution/ExecutionManager"; -import { GameRunner } from "../../src/core/GameRunner"; import { Cell, Nation, PlayerInfo, PlayerType } from "../../src/core/game/Game"; +import { GameRunner } from "../../src/core/GameRunner"; import { GameConfig, GameID } from "../../src/core/Schemas"; import { setup } from "../util/Setup"; import { executeTicks } from "../util/utils"; @@ -124,9 +124,12 @@ describe("GameRunner playerActions interaction payload", () => { () => {}, ); + const request = player.createAllianceRequest(other); + expect(request).not.toBeNull(); + request!.reject(); const expected = player.allianceRequestCooldownRemaining(other); + expect(expected).toBeGreaterThan(0); const actions = runner.playerActions(player.id(), 50, 50); - expect(actions.interaction).toBeDefined(); expect(actions.interaction?.allianceRequestCooldownRemaining).toBe( expected, From 0cc6e56c7fc53edb2ba5ccec1a1a3f41749494dd Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Mon, 17 Aug 2026 12:53:37 +0100 Subject: [PATCH 09/13] reordered en.json for alphabetical order --- resources/lang/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 79d76e696e..1cdbb6ba36 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -510,6 +510,7 @@ "about_to_expire": "Your alliance with {name} is about to expire!", "accept_alliance": "Accept", "alliance_accepted": "accepted", + "alliance_cooldown_text": "{amount}", "alliance_expired": "Your alliance with {name} expired", "alliance_nukes_destroyed_incoming": "{count, plural, one {# nuke launched by {name} was destroyed due to the alliance} other {# nukes launched by {name} were destroyed due to the alliance}}", "alliance_nukes_destroyed_outgoing": "{count, plural, one {# nuke launched toward {name} was destroyed due to the alliance} other {# nukes launched toward {name} were destroyed due to the alliance}}", @@ -545,8 +546,7 @@ "trade_ship_captured": "Your trade ship was captured by {name}", "unit_destroyed": "Your {unit} was destroyed", "unit_voluntarily_deleted": "Unit voluntarily deleted", - "wants_to_renew_alliance": "{name} wants to renew your alliance", - "alliance_cooldown_text": "{amount}" + "wants_to_renew_alliance": "{name} wants to renew your alliance" }, "featured_stream": { "expand": "Expand", From d236d0f2b380d1de9bdfd17fcc7d9179d6157380 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Thu, 20 Aug 2026 18:22:39 +0100 Subject: [PATCH 10/13] use renderDuration instead of translate text to render numbers. --- resources/lang/en.json | 1 - src/client/hud/layers/RadialMenu.ts | 14 +++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 1cdbb6ba36..a8f56fef7f 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -510,7 +510,6 @@ "about_to_expire": "Your alliance with {name} is about to expire!", "accept_alliance": "Accept", "alliance_accepted": "accepted", - "alliance_cooldown_text": "{amount}", "alliance_expired": "Your alliance with {name} expired", "alliance_nukes_destroyed_incoming": "{count, plural, one {# nuke launched by {name} was destroyed due to the alliance} other {# nukes launched by {name} were destroyed due to the alliance}}", "alliance_nukes_destroyed_outgoing": "{count, plural, one {# nuke launched toward {name} was destroyed due to the alliance} other {# nukes launched toward {name} were destroyed due to the alliance}}", diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 5d7e5c6d29..2911f018c7 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -4,7 +4,7 @@ import { EventBus, GameEvent } from "../../../core/EventBus"; import { Controller } from "../../Controller"; import { CloseViewEvent } from "../../InputHandler"; import { PlaySoundEffectEvent } from "../../sound/Sounds"; -import { getSvgAspectRatio, translateText } from "../../Utils"; +import { getSvgAspectRatio, renderDuration, translateText } from "../../Utils"; import { CenterButtonElement, COLORS, @@ -716,11 +716,7 @@ export class RadialMenu implements Controller { content .append("text") .attr("class", `cooldown-text`) - .text( - translateText("events_display.alliance_cooldown_text", { - amount: cooldown, - }), - ) + .text(renderDuration(cooldown)) .attr("fill", "white") .attr("opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1) .attr("font-size", "20px") @@ -1251,11 +1247,7 @@ export class RadialMenu implements Controller { } cooldownText - .text( - translateText("events_display.alliance_cooldown_text", { - amount: cooldown, - }), - ) + .text(renderDuration(cooldown)) .attr( "opacity", isAllianceCooldown ? 0.82 : disabled ? 0.5 : 1, From 05e4337c760747ff11eaeebc894b1ad400ffecf9 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Thu, 27 Aug 2026 16:28:09 +0100 Subject: [PATCH 11/13] fixed spelling mistake --- src/client/hud/layers/RadialMenu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 2911f018c7..1148659c37 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -1399,7 +1399,7 @@ export class RadialMenu implements Controller { getSvgAspectRatio(iconUrl).then((ratio) => { if ( - Number(content.getAttribute("data-render-gneration")) !== generation + Number(content.getAttribute("data-render-genration")) !== generation ) { return; } From 2f9b9c1a44674b5cebc093ff825537d300f05f2c Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Thu, 27 Aug 2026 16:50:53 +0100 Subject: [PATCH 12/13] fix claude code requests. --- src/client/hud/layers/RadialMenu.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index 1148659c37..ab62036c75 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -1254,10 +1254,10 @@ export class RadialMenu implements Controller { ); } } - - // Update timer gradient - this.maybeUpdateTimerGradient(item, color, opacity); } + // Update timer gradient + this.maybeUpdateTimerGradient(item, color, opacity); + } } }); @@ -1399,7 +1399,7 @@ export class RadialMenu implements Controller { getSvgAspectRatio(iconUrl).then((ratio) => { if ( - Number(content.getAttribute("data-render-genration")) !== generation + Number(content.getAttribute("data-render-generation")) !== generation ) { return; } From c0c32f6033714bb6345466595fd4b455ce499e80 Mon Sep 17 00:00:00 2001 From: Luke Dawes Date: Thu, 27 Aug 2026 16:59:04 +0100 Subject: [PATCH 13/13] remove a line for prettier --- src/client/hud/layers/RadialMenu.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/hud/layers/RadialMenu.ts b/src/client/hud/layers/RadialMenu.ts index ab62036c75..f7b2ec1d1a 100644 --- a/src/client/hud/layers/RadialMenu.ts +++ b/src/client/hud/layers/RadialMenu.ts @@ -1257,7 +1257,6 @@ export class RadialMenu implements Controller { } // Update timer gradient this.maybeUpdateTimerGradient(item, color, opacity); - } } });