Files
OpenFrontIO/src/client/graphics/layers/MenuEventManager.ts
T
oleksandr-shysh 871d8c499c Multi-level radial menu (#1018)
## Description:

- Refactored the radial menu to enable multi-level functionality.
- Organized the actions into submenus.

<img width="192" alt="Знімок екрана 2025-06-03 о 16 33 24"
src="https://github.com/user-attachments/assets/6dae9792-bcae-4fc9-8ce4-1203d0efbfac"
/>
<img width="313" alt="Знімок екрана 2025-06-03 о 16 34 17"
src="https://github.com/user-attachments/assets/5d78098f-b05b-40c4-bd70-8f2e3c08da2b"
/>
<img width="308" alt="Знімок екрана 2025-06-03 о 16 40 22"
src="https://github.com/user-attachments/assets/01b00906-9e8b-47e9-8f97-cfd3c023c352"
/>
<img width="277" alt="Знімок екрана 2025-06-03 о 16 37 04"
src="https://github.com/user-attachments/assets/60718c5b-8544-43e6-b891-2833d7fb789a"
/>
<img width="353" alt="Знімок екрана 2025-06-03 о 16 36 32"
src="https://github.com/user-attachments/assets/8c35a0f8-5588-470f-8af4-8e6d4ba66d88"
/>


## 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
- [x] 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:

oleksandr037617_47021

---------

Co-authored-by: Oleksandr Shysh <oleksandr.s@develops.today>
Co-authored-by: evanpelle <evanpelle@gmail.com>
2025-06-06 17:04:24 -07:00

186 lines
5.0 KiB
TypeScript

import { EventBus } from "../../../core/EventBus";
import { Cell, PlayerActions, TerraNullius } from "../../../core/game/Game";
import { TileRef } from "../../../core/game/GameMap";
import { GameView, PlayerView } from "../../../core/game/GameView";
import {
CloseViewEvent,
ContextMenuEvent,
MouseUpEvent,
ShowBuildMenuEvent,
} from "../../InputHandler";
import { SendSpawnIntentEvent } from "../../Transport";
import { TransformHandler } from "../TransformHandler";
import { BuildMenu } from "./BuildMenu";
import { EmojiTable } from "./EmojiTable";
import { PlayerInfoOverlay } from "./PlayerInfoOverlay";
import { PlayerPanel } from "./PlayerPanel";
import { RadialMenu } from "./RadialMenu";
export type ContextMenuCallback = (
myPlayer: PlayerView,
tile: TileRef,
actions: PlayerActions,
) => void;
export class MenuEventManager {
private clickedCell: Cell | null = null;
private lastClosed: number = 0;
private originalTileOwner: PlayerView | TerraNullius | null = null;
private wasInSpawnPhase: boolean = false;
private onContextMenuCallback: ContextMenuCallback | null = null;
constructor(
private eventBus: EventBus,
private game: GameView,
private transformHandler: TransformHandler,
private radialMenu: RadialMenu,
private buildMenu: BuildMenu,
private emojiTable: EmojiTable,
private playerInfoOverlay: PlayerInfoOverlay,
private playerPanel: PlayerPanel,
) {}
init() {
this.eventBus.on(ContextMenuEvent, (e) => this.onContextMenu(e));
this.eventBus.on(MouseUpEvent, (e) => this.onPointerUp(e));
this.eventBus.on(CloseViewEvent, () => this.closeMenu());
this.eventBus.on(ShowBuildMenuEvent, (e) => this.onShowBuildMenu(e));
}
setContextMenuCallback(callback: ContextMenuCallback) {
this.onContextMenuCallback = callback;
}
onContextMenu(event: ContextMenuEvent): Cell | null {
if (this.lastClosed + 200 > new Date().getTime()) return null;
this.closeMenu();
if (this.radialMenu.isMenuVisible()) {
this.radialMenu.hideRadialMenu();
return null;
} else {
this.radialMenu.showRadialMenu(event.x, event.y);
}
this.radialMenu.disableAllButtons();
this.clickedCell = this.transformHandler.screenToWorldCoordinates(
event.x,
event.y,
);
if (
!this.clickedCell ||
!this.game.isValidCoord(this.clickedCell.x, this.clickedCell.y)
) {
return null;
}
const tile = this.game.ref(this.clickedCell.x, this.clickedCell.y);
this.originalTileOwner = this.game.owner(tile);
this.wasInSpawnPhase = this.game.inSpawnPhase();
const myPlayer = this.game.myPlayer();
if (myPlayer === null) {
throw new Error("my player not found");
}
if (myPlayer && !myPlayer.isAlive() && !this.game.inSpawnPhase()) {
this.radialMenu.hideRadialMenu();
return null;
}
if (this.game.inSpawnPhase()) {
if (this.game.isLand(tile) && !this.game.hasOwner(tile)) {
this.radialMenu.enableCenterButton(true, () => {
if (this.clickedCell === null) return;
this.eventBus.emit(new SendSpawnIntentEvent(this.clickedCell));
this.radialMenu.hideRadialMenu();
});
return this.clickedCell;
}
}
myPlayer.actions(tile).then((actions) => {
if (this.onContextMenuCallback) {
this.onContextMenuCallback(myPlayer, tile, actions);
}
});
return this.clickedCell;
}
getClickedCell(): Cell | null {
return this.clickedCell;
}
getOriginalTileOwner(): PlayerView | TerraNullius | null {
return this.originalTileOwner;
}
getWasInSpawnPhase(): boolean {
return this.wasInSpawnPhase;
}
setWasInSpawnPhase(value: boolean) {
this.wasInSpawnPhase = value;
}
onPointerUp(event: MouseUpEvent) {
this.playerInfoOverlay.hide();
this.hideEverything();
}
onShowBuildMenu(e: ShowBuildMenuEvent): TileRef | null {
const clickedCell = this.transformHandler.screenToWorldCoordinates(
e.x,
e.y,
);
if (clickedCell === null) {
return null;
}
if (!this.game.isValidCoord(clickedCell.x, clickedCell.y)) {
return null;
}
const tile = this.game.ref(clickedCell.x, clickedCell.y);
const p = this.game.myPlayer();
if (p === null) {
return null;
}
this.buildMenu.showMenu(tile);
return tile;
}
closeMenu() {
if (this.radialMenu.isMenuVisible()) {
this.radialMenu.hideRadialMenu();
}
if (this.buildMenu.isVisible) {
this.buildMenu.hideMenu();
}
if (this.emojiTable.isVisible) {
this.emojiTable.hideTable();
}
if (this.playerPanel.isVisible) {
this.playerPanel.hide();
}
}
hideEverything() {
if (this.radialMenu.isMenuVisible()) {
this.radialMenu.hideRadialMenu();
this.lastClosed = new Date().getTime();
}
this.emojiTable.hideTable();
this.buildMenu.hideMenu();
}
enableCenterButton(enabled: boolean, action: () => void) {
this.radialMenu.enableCenterButton(enabled, action);
}
}