From dfd6a1f5f9430c8da810b36b4c75b5bfdc726f51 Mon Sep 17 00:00:00 2001 From: Mattia Migliorini Date: Thu, 22 Jan 2026 13:21:14 +0100 Subject: [PATCH] Replace donate buttons with attack ones for AFK friendly players in radial menu (#2987) Resolves #2986 ## Description: Shows donate actions in radial menu only when friendly player is NOT disconnected. This is needed in order to let mobile/touch users attack AFK teammates. Current behavior: image With this PR: image ## 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 - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: deshack_82603 --- src/client/graphics/layers/MainRadialMenu.ts | 4 +++- src/client/graphics/layers/RadialMenuElements.ts | 14 +++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/client/graphics/layers/MainRadialMenu.ts b/src/client/graphics/layers/MainRadialMenu.ts index 3151c6a48..c0b707577 100644 --- a/src/client/graphics/layers/MainRadialMenu.ts +++ b/src/client/graphics/layers/MainRadialMenu.ts @@ -134,7 +134,9 @@ export class MainRadialMenu extends LitElement implements Layer { }; const isFriendlyTarget = - recipient !== null && recipient.isFriendly(myPlayer); + recipient !== null && + recipient.isFriendly(myPlayer) && + !recipient.isDisconnected(); this.radialMenu.setCenterButtonAppearance( isFriendlyTarget ? donateTroopIcon : swordIcon, diff --git a/src/client/graphics/layers/RadialMenuElements.ts b/src/client/graphics/layers/RadialMenuElements.ts index 1dcf3eb90..0cf28cebb 100644 --- a/src/client/graphics/layers/RadialMenuElements.ts +++ b/src/client/graphics/layers/RadialMenuElements.ts @@ -117,6 +117,14 @@ function isFriendlyTarget(params: MenuElementParams): boolean { return isFriendly.call(selectedPlayer, params.myPlayer); } +function isDisconnectedTarget(params: MenuElementParams): boolean { + const selectedPlayer = params.selected; + if (selectedPlayer === null) return false; + const isDisconnected = (selectedPlayer as PlayerView).isDisconnected; + if (typeof isDisconnected !== "function") return false; + return isDisconnected.call(selectedPlayer); +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars const infoChatElement: MenuElement = { id: "info_chat", @@ -571,7 +579,7 @@ export const centerButtonElement: CenterButtonElement = { return false; } - if (isFriendlyTarget(params)) { + if (isFriendlyTarget(params) && !isDisconnectedTarget(params)) { return !params.playerActions.interaction?.canDonateTroops; } @@ -581,7 +589,7 @@ export const centerButtonElement: CenterButtonElement = { if (params.game.inSpawnPhase()) { params.playerActionHandler.handleSpawn(params.tile); } else { - if (isFriendlyTarget(params)) { + if (isFriendlyTarget(params) && !isDisconnectedTarget(params)) { const selectedPlayer = params.selected as PlayerView; const ratio = params.uiState?.attackRatio ?? 1; const troopsToDonate = Math.floor(ratio * params.myPlayer.troops()); @@ -626,7 +634,7 @@ export const rootMenuElement: MenuElement = { : [ boatMenuElement, ally, - isFriendlyTarget(params) + isFriendlyTarget(params) && !isDisconnectedTarget(params) ? donateGoldRadialElement : attackMenuElement, ]),