diff --git a/src/client/graphics/layers/ChatIntegration.ts b/src/client/graphics/layers/ChatIntegration.ts index 044b0b18f..cadabed58 100644 --- a/src/client/graphics/layers/ChatIntegration.ts +++ b/src/client/graphics/layers/ChatIntegration.ts @@ -3,7 +3,7 @@ import { GameView, PlayerView } from "../../../core/game/GameView"; import { SendQuickChatEvent } from "../../Transport"; import { translateText } from "../../Utils"; import { ChatModal, QuickChatPhrase, quickChatPhrases } from "./ChatModal"; -import { COLORS, MenuElement } from "./RadialMenuElements"; +import { COLORS, MenuElement, MenuElementParams } from "./RadialMenuElements"; export class ChatIntegration { private ctModal: ChatModal; @@ -51,7 +51,7 @@ export class ChatIntegration { return { id: `phrase-${category.id}-${phrase.key}`, name: phraseText, - disabled: false, + disabled: () => false, text: this.shortenText(phraseText), fontSize: "10px", color: categoryColor, @@ -61,7 +61,7 @@ export class ChatIntegration { className: "description", }, ], - action: () => { + action: (params: MenuElementParams) => { if (phrase.requiresPlayer) { this.ctModal.openWithSelection( category.id, @@ -86,7 +86,7 @@ export class ChatIntegration { return { id: `chat-category-${category.id}`, name: categoryTranslation, - disabled: false, + disabled: () => false, text: categoryTranslation, color: categoryColor, _action: () => {}, // Empty action placeholder for RadialMenu diff --git a/src/client/graphics/layers/MainRadialMenu.ts b/src/client/graphics/layers/MainRadialMenu.ts index 739815f3c..eb93a2642 100644 --- a/src/client/graphics/layers/MainRadialMenu.ts +++ b/src/client/graphics/layers/MainRadialMenu.ts @@ -18,10 +18,8 @@ import { RadialMenu, RadialMenuConfig } from "./RadialMenu"; import { COLORS, MenuElementParams, + rootMenuItems, Slot, - createRadialMenuItems, - getRootMenuItems, - updateCenterButton, } from "./RadialMenuElements"; import boatIcon from "../../../../resources/images/BoatIconWhite.svg"; @@ -85,7 +83,7 @@ export class MainRadialMenu extends LitElement implements Layer { this.chatIntegration = new ChatIntegration(this.game, this.eventBus); - this.radialMenu.setRootMenuItems(getRootMenuItems()); + this.radialMenu.setRootMenuItems(rootMenuItems); } init() { @@ -127,9 +125,8 @@ export class MainRadialMenu extends LitElement implements Layer { closeMenu: () => this.menuEventManager.closeMenu(), }; - const menuItems = createRadialMenuItems(params); - - this.radialMenu.setRootMenuItems(menuItems); + this.radialMenu.setRootMenuItems(rootMenuItems); + this.radialMenu.setParams(params); updateCenterButton(params, (enabled, action) => { this.radialMenu.enableCenterButton(enabled, action); @@ -283,3 +280,22 @@ export class MainRadialMenu extends LitElement implements Layer { // No redraw implementation needed } } + +function updateCenterButton( + params: MenuElementParams, + enableCenterButton: (enabled: boolean, action?: (() => void) | null) => void, +) { + if (params.playerActions.canAttack) { + enableCenterButton(true, () => { + if (params.tileOwner !== params.myPlayer) { + params.playerActionHandler.handleAttack( + params.myPlayer, + params.tileOwner.id(), + ); + } + params.closeMenu(); + }); + } else { + enableCenterButton(false); + } +} diff --git a/src/client/graphics/layers/PlayerActionHandler.ts b/src/client/graphics/layers/PlayerActionHandler.ts index b8bb154bf..ebe872830 100644 --- a/src/client/graphics/layers/PlayerActionHandler.ts +++ b/src/client/graphics/layers/PlayerActionHandler.ts @@ -1,5 +1,10 @@ import { EventBus } from "../../../core/EventBus"; -import { Cell, PlayerActions, UnitType } from "../../../core/game/Game"; +import { + Cell, + PlayerActions, + PlayerID, + UnitType, +} from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; import { PlayerView } from "../../../core/game/GameView"; import { @@ -42,7 +47,7 @@ export class PlayerActionHandler { handleBoatAttack( player: PlayerView, - targetId: string, + targetId: PlayerID | null, targetCell: Cell, spawnTile: Cell | null, ) { diff --git a/src/client/graphics/layers/RadialMenu.ts b/src/client/graphics/layers/RadialMenu.ts index 0ce75f91e..561c5523b 100644 --- a/src/client/graphics/layers/RadialMenu.ts +++ b/src/client/graphics/layers/RadialMenu.ts @@ -2,7 +2,7 @@ import * as d3 from "d3"; import backIcon from "../../../../resources/images/BackIconWhite.svg"; import disabledIcon from "../../../../resources/images/DisabledIcon.svg"; import { Layer } from "./Layer"; -import { MenuElement } from "./RadialMenuElements"; +import { MenuElement, MenuElementParams } from "./RadialMenuElements"; export interface TooltipItem { text: string; @@ -68,6 +68,8 @@ export class RadialMenu implements Layer { private navigationInProgress: boolean = false; private originalCenterButtonIcon: string = ""; + private params: MenuElementParams; + constructor(config: RadialMenuConfig = {}) { this.config = { menuSize: config.menuSize ?? 190, @@ -287,10 +289,10 @@ export class RadialMenu implements Layer { .attr("class", "menu-item-path") .attr("d", arc) .attr("fill", (d) => { - const color = d.data.disabled + const color = d.data.disabled(this.params) ? this.config.disabledColor : d.data.color || "#333333"; - const opacity = d.data.disabled ? 0.5 : 0.7; + const opacity = d.data.disabled(this.params) ? 0.5 : 0.7; if (d.data.id === this.selectedItemId && this.currentLevel > level) { return color; @@ -300,8 +302,10 @@ export class RadialMenu implements Layer { }) .attr("stroke", "#ffffff") .attr("stroke-width", "2") - .style("cursor", (d) => (d.data.disabled ? "not-allowed" : "pointer")) - .style("opacity", (d) => (d.data.disabled ? 0.5 : 1)) + .style("cursor", (d) => + d.data.disabled(this.params) ? "not-allowed" : "pointer", + ) + .style("opacity", (d) => (d.data.disabled(this.params) ? 0.5 : 1)) .style( "transition", `filter ${this.config.menuTransitionDuration / 2}ms, stroke-width ${ @@ -323,7 +327,7 @@ export class RadialMenu implements Layer { path.attr("filter", "url(#glow)"); path.attr("stroke-width", "3"); - const color = d.data.disabled + const color = d.data.disabled(this.params) ? this.config.disabledColor : d.data.color || "#333333"; path.attr("fill", color); @@ -354,7 +358,7 @@ export class RadialMenu implements Layer { ) { const onHover = (d: d3.PieArcDatum, path: any) => { if ( - d.data.disabled || + d.data.disabled(this.params) || (this.currentLevel > 0 && this.currentLevel !== level) || this.navigationInProgress ) @@ -362,7 +366,7 @@ export class RadialMenu implements Layer { path.attr("filter", "url(#glow)"); path.attr("stroke-width", "3"); - const color = d.data.disabled + const color = d.data.disabled(this.params) ? this.config.disabledColor : d.data.color || "#333333"; path.attr("fill", color); @@ -371,10 +375,11 @@ export class RadialMenu implements Layer { this.showTooltip(d.data.tooltipItems); } + const subMenu = d.data.subMenu?.(this.params); if ( - d.data.children && - d.data.children.length > 0 && - !d.data.disabled && + subMenu && + subMenu.length > 0 && + !d.data.disabled(this.params) && !( this.currentLevel > 0 && d.data.id === this.selectedItemId && @@ -390,7 +395,7 @@ export class RadialMenu implements Layer { if (this.navigationInProgress) return; this.navigationInProgress = true; this.selectedItemId = d.data.id; - this.navigateToSubMenu(d.data.children || []); + this.navigateToSubMenu(subMenu); this.setCenterButtonAsBack(); }, 200); } @@ -405,7 +410,7 @@ export class RadialMenu implements Layer { this.hideTooltip(); if ( - d.data.disabled || + d.data.disabled(this.params) || (this.currentLevel > 0 && level === 0 && d.data.id === this.selectedItemId) @@ -413,10 +418,10 @@ export class RadialMenu implements Layer { return; path.attr("filter", null); path.attr("stroke-width", "2"); - const color = d.data.disabled + const color = d.data.disabled(this.params) ? this.config.disabledColor : d.data.color || "#333333"; - const opacity = d.data.disabled ? 0.5 : 0.7; + const opacity = d.data.disabled(this.params) ? 0.5 : 0.7; path.attr( "fill", d3.color(color)?.copy({ opacity: opacity })?.toString() || color, @@ -425,7 +430,7 @@ export class RadialMenu implements Layer { const onClick = (d: d3.PieArcDatum, event: Event) => { event.stopPropagation(); - if (d.data.disabled || this.navigationInProgress) return; + if (d.data.disabled(this.params) || this.navigationInProgress) return; if ( this.currentLevel > 0 && @@ -434,16 +439,15 @@ export class RadialMenu implements Layer { ) return; - if (d.data.children && d.data.children.length > 0) { + const subMenu = d.data.subMenu?.(this.params); + if (subMenu && subMenu.length > 0) { this.navigationInProgress = true; this.selectedItemId = d.data.id; - this.navigateToSubMenu(d.data.children || []); + this.navigateToSubMenu(subMenu); this.setCenterButtonAsBack(); - } else if (d.data._action) { - d.data._action(); - this.hideRadialMenu(); } else { - throw new Error(`Menu item action is not a function: ${d.data.id}`); + d.data.action?.(this.params); + this.hideRadialMenu(); } }; @@ -513,14 +517,16 @@ export class RadialMenu implements Layer { .attr("fill", "white") .attr("font-size", d.data.fontSize ?? "12px") .attr("font-family", "Arial, sans-serif") - .style("opacity", d.data.disabled ? 0.5 : 1) + .style("opacity", d.data.disabled(this.params) ? 0.5 : 1) .text(d.data.text); } else { content .append("image") .attr( "xlink:href", - d.data.disabled ? disabledIcon : d.data.icon || disabledIcon, + d.data.disabled(this.params) + ? disabledIcon + : d.data.icon || disabledIcon, ) .attr("width", this.config.iconSize) .attr("height", this.config.iconSize) @@ -646,10 +652,10 @@ export class RadialMenu implements Layer { const item = this.findMenuItem(this.selectedItemId); if (item) { - const color = item.disabled + const color = item.disabled(this.params) ? this.config.disabledColor : item.color || "#333333"; - const opacity = item.disabled ? 0.5 : 0.7; + const opacity = item.disabled(this.params) ? 0.5 : 0.7; selectedPath.attr( "fill", d3.color(color)?.copy({ opacity: opacity })?.toString() || color, @@ -862,7 +868,6 @@ export class RadialMenu implements Layer { this.enableCenterButton(false); for (const item of this.currentMenuItems) { - item.disabled = true; item.color = this.config.disabledColor; } } @@ -956,7 +961,6 @@ export class RadialMenu implements Layer { const item = this.findMenuItem(id); if (item) { - item.disabled = !enabled; if (color) item.color = enabled ? color : this.config.disabledColor; if (icon) item.icon = icon; if (text !== undefined) item.text = text; @@ -1003,6 +1007,10 @@ export class RadialMenu implements Layer { } } + public setParams(params: MenuElementParams) { + this.params = params; + } + private findMenuItem(id: string): MenuElement | undefined { return this.currentMenuItems.find((item) => item.id === id); } diff --git a/src/client/graphics/layers/RadialMenuElements.ts b/src/client/graphics/layers/RadialMenuElements.ts index 1b5ed0fa0..a8b180ef6 100644 --- a/src/client/graphics/layers/RadialMenuElements.ts +++ b/src/client/graphics/layers/RadialMenuElements.ts @@ -3,7 +3,6 @@ import { Cell, PlayerActions, TerraNullius, - UnitType, } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView } from "../../../core/game/GameView"; @@ -45,20 +44,16 @@ export interface MenuElementParams { export interface MenuElement { id: string; name: string; - disabled: boolean; - displayed?: boolean; + displayed?: boolean | ((params: MenuElementParams) => boolean); color?: string; icon?: string; text?: string; fontSize?: string; tooltipItems?: TooltipItem[]; + disabled: (params: MenuElementParams) => boolean; action?: (params: MenuElementParams) => void; // For leaf items that perform actions subMenu?: (params: MenuElementParams) => MenuElement[]; // For non-leaf items that open submenus - - // Runtime properties used by RadialMenu (not to be set by menu element creators) - children?: MenuElement[]; - _action?: () => void; } export const COLORS = { @@ -96,42 +91,220 @@ export enum Slot { Back = "back", } -/** - * Convert a MenuElement tree to a version usable by the RadialMenu - * by resolving subMenu functions and setting up actions - */ -export function prepareMenuElementsForRadialMenu( - elements: MenuElement[], - params: MenuElementParams, -): MenuElement[] { - return elements.map((element) => { - const prepared: MenuElement = { ...element }; +const infoChatElement: MenuElement = { + id: "info_chat", + name: "chat", + disabled: () => false, + color: COLORS.chat.default, + icon: chatIcon, + subMenu: (params: MenuElementParams) => + params.chatIntegration + .createQuickChatMenu(params.selected!) + .map((item) => ({ + ...item, + action: item.action + ? (_params: MenuElementParams) => item.action!(params) + : undefined, + })), +}; - // If the element has a subMenu function, execute it to get the children - if (element.subMenu) { - prepared.children = prepareMenuElementsForRadialMenu( - element.subMenu(params), - params, - ); - // We don't need the subMenu function anymore - prepared.subMenu = undefined; +const allyTargetElement: MenuElement = { + id: "ally_target", + name: "target", + disabled: () => false, + color: COLORS.target, + icon: targetIcon, + action: (params: MenuElementParams) => { + params.playerActionHandler.handleTargetPlayer(params.selected!.id()); + params.closeMenu(); + }, +}; + +const allyTradeElement: MenuElement = { + id: "ally_trade", + name: "trade", + disabled: (params: MenuElementParams) => + !!params.playerActions?.interaction?.canEmbargo, + displayed: (params: MenuElementParams) => + !params.playerActions?.interaction?.canEmbargo, + color: COLORS.trade, + text: translateText("player_panel.start_trade"), + action: (params: MenuElementParams) => { + params.playerActionHandler.handleEmbargo(params.selected!, "start"); + params.closeMenu(); + }, +}; + +const allyEmbargoElement: MenuElement = { + id: "ally_embargo", + name: "embargo", + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canEmbargo, + displayed: (params: MenuElementParams) => + !!params.playerActions?.interaction?.canEmbargo, + color: COLORS.embargo, + text: translateText("player_panel.stop_trade"), + action: (params: MenuElementParams) => { + params.playerActionHandler.handleEmbargo(params.selected!, "stop"); + params.closeMenu(); + }, +}; + +const allyRequestElement: MenuElement = { + id: "ally_request", + name: "request", + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canSendAllianceRequest, + displayed: (params: MenuElementParams) => + !params.playerActions?.interaction?.canBreakAlliance, + color: COLORS.ally, + icon: allianceIcon, + action: (params: MenuElementParams) => { + params.playerActionHandler.handleAllianceRequest( + params.myPlayer, + params.selected!, + ); + params.closeMenu(); + }, +}; + +const allyBreakElement: MenuElement = { + id: "ally_break", + name: "break", + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canBreakAlliance, + displayed: (params: MenuElementParams) => + !!params.playerActions?.interaction?.canBreakAlliance, + color: COLORS.breakAlly, + icon: traitorIcon, + action: (params: MenuElementParams) => { + params.playerActionHandler.handleBreakAlliance( + params.myPlayer, + params.selected!, + ); + params.closeMenu(); + }, +}; + +const allyDonateGoldElement: MenuElement = { + id: "ally_donate_gold", + name: "donate gold", + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canDonate, + color: COLORS.ally, + icon: donateGoldIcon, + action: (params: MenuElementParams) => { + params.playerActionHandler.handleDonateGold(params.selected!); + params.closeMenu(); + }, +}; + +const allyDonateTroopsElement: MenuElement = { + id: "ally_donate_troops", + name: "donate troops", + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canDonate, + color: COLORS.ally, + icon: donateTroopIcon, + action: (params: MenuElementParams) => { + params.playerActionHandler.handleDonateTroops(params.selected!); + params.closeMenu(); + }, +}; + +const infoPlayerElement: MenuElement = { + id: "info_player", + name: "player", + disabled: () => false, + color: COLORS.info, + icon: infoIcon, + action: (params: MenuElementParams) => { + params.playerPanel.show(params.playerActions, params.tile); + }, +}; + +const infoEmojiElement: MenuElement = { + id: "info_emoji", + name: "emoji", + disabled: () => false, + color: COLORS.infoEmoji, + icon: emojiIcon, + subMenu: (params: MenuElementParams) => { + const emojiElements: MenuElement[] = []; + + const emojiCount = 15; + for (let i = 0; i < emojiCount; i++) { + emojiElements.push({ + id: `emoji_${i}`, + name: flattenedEmojiTable[i], + text: flattenedEmojiTable[i], + disabled: () => false, + fontSize: "25px", + action: (params: MenuElementParams) => { + const targetPlayer = + params.selected === params.game.myPlayer() + ? AllPlayers + : params.selected; + params.playerActionHandler.handleEmoji(targetPlayer!, i); + params.closeMenu(); + }, + }); } - // Set up the action function to call the element's action with params - if (element.action) { - prepared._action = () => element.action!(params); - } else { - prepared._action = () => {}; - } + emojiElements.push({ + id: "emoji_more", + name: "more", + disabled: () => false, + color: COLORS.infoEmoji, + icon: emojiIcon, + action: (params: MenuElementParams) => { + params.emojiTable.showTable((emoji) => { + const targetPlayer = + params.selected === params.game.myPlayer() + ? AllPlayers + : params.selected; + params.playerActionHandler.handleEmoji( + targetPlayer!, + flattenedEmojiTable.indexOf(emoji), + ); + params.emojiTable.hideTable(); + }); + }, + }); - return prepared; - }); -} + return emojiElements; + }, +}; + +export const infoMenuElement: MenuElement = { + id: Slot.Info, + name: "info", + disabled: () => false, + icon: infoIcon, + color: COLORS.info, + + subMenu: (params: MenuElementParams) => { + if (!params.selected) return []; + + return [ + infoChatElement, + allyTargetElement, + allyTradeElement, + allyEmbargoElement, + allyRequestElement, + allyBreakElement, + allyDonateGoldElement, + allyDonateTroopsElement, + infoPlayerElement, + infoEmojiElement, + ].filter((item) => item.displayed !== false); + }, +}; export const buildMenuElement: MenuElement = { id: Slot.Build, name: "build", - disabled: false, + disabled: () => false, icon: buildIcon, color: COLORS.build, @@ -142,7 +315,8 @@ export const buildMenuElement: MenuElement = { name: item.key ? item.key.replace("unit_type.", "") : item.unitType.toString(), - disabled: !params.buildMenu.canBuild(item), + disabled: (params: MenuElementParams) => + !params.buildMenu.canBuild(item), color: params.buildMenu.canBuild(item) ? COLORS.building : undefined, icon: item.icon, tooltipItems: [ @@ -173,7 +347,7 @@ export const buildMenuElement: MenuElement = { buildElements.push({ id: "build_menu", name: "build", - disabled: false, + disabled: () => false, color: COLORS.build, icon: buildIcon, action: (params: MenuElementParams) => { @@ -188,13 +362,11 @@ export const buildMenuElement: MenuElement = { export const boatMenuElement: MenuElement = { id: Slot.Boat, name: "boat", - disabled: false, + disabled: () => false, icon: boatIcon, color: COLORS.boat, action: async (params: MenuElementParams) => { - if (!params.selected) return; - const spawn = await params.playerActionHandler.findBestTransportShipSpawn( params.myPlayer, params.tile, @@ -207,7 +379,7 @@ export const boatMenuElement: MenuElement = { params.playerActionHandler.handleBoatAttack( params.myPlayer, - params.selected.id(), + params.selected?.id() || null, new Cell(params.game.x(params.tile), params.game.y(params.tile)), spawnTile, ); @@ -216,256 +388,8 @@ export const boatMenuElement: MenuElement = { }, }; -export const infoMenuElement: MenuElement = { - id: Slot.Info, - name: "info", - disabled: false, - icon: infoIcon, - color: COLORS.info, - - subMenu: (params: MenuElementParams) => { - if (!params.selected) return []; - - return [ - { - id: "info_chat", - name: "chat", - disabled: false, - color: COLORS.chat.default, - icon: chatIcon, - subMenu: (params: MenuElementParams) => - params.chatIntegration - .createQuickChatMenu(params.selected!) - .map((item) => ({ - ...item, - action: item.action - ? (_params: MenuElementParams) => item.action!(params) - : undefined, - })), - }, - { - id: "ally_target", - name: "target", - disabled: false, - color: COLORS.target, - icon: targetIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleTargetPlayer(params.selected!.id()); - params.closeMenu(); - }, - }, - { - id: "ally_trade", - name: "trade", - disabled: !!params.playerActions?.interaction?.canEmbargo, - displayed: !params.playerActions?.interaction?.canEmbargo, - color: COLORS.trade, - text: translateText("player_panel.start_trade"), - action: (params: MenuElementParams) => { - params.playerActionHandler.handleEmbargo(params.selected!, "start"); - params.closeMenu(); - }, - }, - { - id: "ally_embargo", - name: "embargo", - disabled: !params.playerActions?.interaction?.canEmbargo, - displayed: !!params.playerActions?.interaction?.canEmbargo, - color: COLORS.embargo, - text: translateText("player_panel.stop_trade"), - action: (params: MenuElementParams) => { - params.playerActionHandler.handleEmbargo(params.selected!, "stop"); - params.closeMenu(); - }, - }, - { - id: "ally_request", - name: "request", - disabled: !params.playerActions?.interaction?.canSendAllianceRequest, - displayed: !params.playerActions?.interaction?.canBreakAlliance, - color: COLORS.ally, - icon: allianceIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleAllianceRequest( - params.myPlayer, - params.selected!, - ); - params.closeMenu(); - }, - }, - { - id: "ally_break", - name: "break", - disabled: !params.playerActions?.interaction?.canBreakAlliance, - displayed: !!params.playerActions?.interaction?.canBreakAlliance, - color: COLORS.breakAlly, - icon: traitorIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleBreakAlliance( - params.myPlayer, - params.selected!, - ); - params.closeMenu(); - }, - }, - - { - id: "ally_donate_gold", - name: "donate gold", - disabled: !params.playerActions?.interaction?.canDonate, - color: COLORS.ally, - icon: donateGoldIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleDonateGold(params.selected!); - params.closeMenu(); - }, - }, - { - id: "ally_donate_troops", - name: "donate troops", - disabled: !params.playerActions?.interaction?.canDonate, - color: COLORS.ally, - icon: donateTroopIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleDonateTroops(params.selected!); - params.closeMenu(); - }, - }, - { - id: "info_player", - name: "player", - disabled: false, - color: COLORS.info, - icon: infoIcon, - action: (params: MenuElementParams) => { - params.playerPanel.show(params.playerActions, params.tile); - }, - }, - { - id: "info_emoji", - name: "emoji", - disabled: false, - color: COLORS.infoEmoji, - icon: emojiIcon, - subMenu: () => { - const emojiElements: MenuElement[] = []; - - const emojiCount = 15; - for (let i = 0; i < emojiCount; i++) { - emojiElements.push({ - id: `emoji_${i}`, - name: flattenedEmojiTable[i], - text: flattenedEmojiTable[i], - disabled: false, - fontSize: "25px", - action: (params: MenuElementParams) => { - const targetPlayer = - params.selected === params.game.myPlayer() - ? AllPlayers - : params.selected; - params.playerActionHandler.handleEmoji(targetPlayer!, i); - params.closeMenu(); - }, - }); - } - - emojiElements.push({ - id: "emoji_more", - name: "more", - disabled: false, - color: COLORS.infoEmoji, - icon: emojiIcon, - action: (params: MenuElementParams) => { - params.emojiTable.showTable((emoji) => { - const targetPlayer = - params.selected === params.game.myPlayer() - ? AllPlayers - : params.selected; - params.playerActionHandler.handleEmoji( - targetPlayer!, - flattenedEmojiTable.indexOf(emoji), - ); - params.emojiTable.hideTable(); - }); - }, - }); - - return emojiElements; - }, - }, - ].filter((item) => item.displayed !== false); - }, -}; - -export function createMenuItems(params: MenuElementParams): MenuElement[] { - const canBuildTransport = params.playerActions.buildableUnits.find( - (bu) => bu.type === UnitType.TransportShip, - )?.canBuild; - - return [ - { - ...boatMenuElement, - disabled: !canBuildTransport || !params.selected, - }, - { - ...buildMenuElement, - disabled: params.game.inSpawnPhase(), - }, - { - ...infoMenuElement, - disabled: !params.game.hasOwner(params.tile), - }, - ]; -} - -export function createRadialMenuItems( - params: MenuElementParams, -): MenuElement[] { - const elements = createMenuItems(params); - return prepareMenuElementsForRadialMenu(elements, params); -} - -export function getRootMenuItems(): MenuElement[] { - return [ - { - id: Slot.Boat, - name: "boat", - disabled: true, - _action: () => {}, - icon: boatIcon, - }, - { - id: Slot.Build, - name: "build", - disabled: true, - _action: () => {}, - icon: buildIcon, - }, - { - id: Slot.Info, - name: "info", - disabled: true, - _action: () => {}, - icon: infoIcon, - }, - ]; -} - -export function updateCenterButton( - params: MenuElementParams, - enableCenterButton: (enabled: boolean, action?: (() => void) | null) => void, -) { - if (params.playerActions.canAttack) { - enableCenterButton(true, () => { - if (params.tileOwner !== params.myPlayer) { - params.playerActionHandler.handleAttack( - params.myPlayer, - params.tileOwner.id(), - ); - } - params.closeMenu(); - }); - } else { - enableCenterButton(false); - } -} +export const rootMenuItems: MenuElement[] = [ + boatMenuElement, + buildMenuElement, + infoMenuElement, +];