mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 16:10:53 +00:00
refactor radial, fix boat on terra nullius not working fixes (#1095)
## Description: refactor the MenuElements to be more decoupled by passing in MenuParams. Fix boat not working on terra nullius. fixes #1088 ## Please complete the following: - [ ] I have added screenshots for all UI updates - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [ ] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: <DISCORD USERNAME>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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<MenuElement>, 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<MenuElement>, 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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user