create basic radial menu

This commit is contained in:
evanpelle
2024-09-26 16:24:58 -07:00
parent e5bcdb986f
commit 851e12613a
10 changed files with 1055 additions and 92 deletions
+1 -1
View File
@@ -137,10 +137,10 @@
* fake humans handle alliances DONE 9/22/2024
* make alliances expire DONE 9/22/2024
* make alliance request mobile friendly
* request to attack other players
* make year clock
* block user inputs if too far behind server
* BUG: FakeHuman don't be enemy if don't share border
* BUG: when send boat only captures one pixel
* store cookies
* BUG: names dissapear on bottom of screen
* UI: boats
+874 -79
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -16,6 +16,7 @@
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.24.7",
"@types/chai": "^4.3.17",
"@types/d3": "^7.4.3",
"@types/jest": "^29.5.12",
"@types/mocha": "^10.0.7",
"@types/node": "^22.5.2",
@@ -55,9 +56,11 @@
"@types/google-protobuf": "^3.15.12",
"@types/jimp": "^0.2.28",
"@types/msgpack5": "^3.4.6",
"@types/raphael": "^2.3.9",
"binary-loader": "^0.0.1",
"colord": "^2.9.3",
"crypto": "^1.0.1",
"d3": "^7.9.0",
"express": "^4.19.2",
"google-auth-library": "^9.14.0",
"googleapis": "^143.0.0",
@@ -68,8 +71,10 @@
"priority-queue-typescript": "^1.0.1",
"protobufjs": "^7.3.2",
"pureimage": "^0.4.13",
"raphael": "^2.3.0",
"typia": "^6.5.2",
"uuid": "^10.0.0",
"wheelnav": "^1.7.1",
"ws": "^8.18.0",
"zod": "^3.23.8"
},
+11 -5
View File
@@ -15,7 +15,7 @@ export class MouseDownEvent implements GameEvent {
) { }
}
export class RightClickEvent implements GameEvent {
export class ContextMenuEvent implements GameEvent {
constructor(
public readonly x: number,
public readonly y: number,
@@ -59,7 +59,7 @@ export class InputHandler {
this.canvas.addEventListener("wheel", (e) => this.onScroll(e), {passive: false});
this.canvas.addEventListener('pointermove', this.onPointerMove.bind(this));
this.canvas.addEventListener('contextmenu', (e: MouseEvent) => {
this.onRightClick(e);
this.onContextMenu(e);
});
this.pointers.clear()
}
@@ -92,9 +92,15 @@ export class InputHandler {
}
this.pointerDown = false
this.pointers.delete(event.pointerId);
const dist = Math.abs(event.x - this.lastPointerDownX) + Math.abs(event.y - this.lastPointerDownY);
if (dist < 10) {
this.eventBus.emit(new MouseUpEvent(event.x, event.y))
if (event.pointerType == "touch") {
event.preventDefault()
this.eventBus.emit(new ContextMenuEvent(event.clientX, event.clientY))
} else {
this.eventBus.emit(new MouseUpEvent(event.x, event.y))
}
}
}
@@ -134,9 +140,9 @@ export class InputHandler {
}
}
private onRightClick(event: MouseEvent) {
private onContextMenu(event: MouseEvent) {
event.preventDefault()
this.eventBus.emit(new RightClickEvent(event.clientX, event.clientY))
this.eventBus.emit(new ContextMenuEvent(event.clientX, event.clientY))
}
private getPinchDistance(): number {
+3 -2
View File
@@ -3,12 +3,12 @@ import {NameLayer} from "./layers/NameLayer";
import {TerrainLayer} from "./layers/TerrainLayer";
import {TerritoryLayer} from "./layers/TerritoryLayer";
import {ClientID} from "../../core/Schemas";
import {createCanvas} from "./Utils";
import {UILayer} from "./layers/UILayer";
import {EventBus} from "../../core/EventBus";
import {TransformHandler} from "./TransformHandler";
import {Layer} from "./layers/Layer";
import {EventsDisplay} from "./layers/EventsDisplay";
import {RadialMenu} from "./layers/RadialMenu";
export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer {
@@ -19,7 +19,8 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus:
new TerritoryLayer(game, eventBus),
new NameLayer(game, game.config().theme(), transformHandler, clientID),
new UILayer(eventBus, game, clientID, transformHandler),
new EventsDisplay(eventBus, game, clientID)
new EventsDisplay(eventBus, game, clientID),
new RadialMenu(eventBus),
]
return new GameRenderer(game, eventBus, canvas, transformHandler, layers)
+131
View File
@@ -0,0 +1,131 @@
import {EventBus} from "../../../core/EventBus";
import {ContextMenuEvent} from "../../InputHandler";
import {Layer} from "./Layer";
import * as d3 from 'd3';
export class RadialMenu implements Layer {
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
private isVisible: boolean = false;
private readonly menuItems = [
{name: "sub", color: "#3498db"},
{name: "color", color: "#e74c3c"},
{name: "shape", color: "#2ecc71"},
{name: "font", color: "#f39c12"},
{name: "stroke", color: "#9b59b6"}
];
private readonly menuSize = 300; // Increased size
private readonly centerButtonSize = 60;
constructor(private eventBus: EventBus) { }
init() {
this.eventBus.on(ContextMenuEvent, e => this.onContextMenu(e))
this.createMenuElement();
}
private createMenuElement() {
this.menuElement = d3.select(document.body)
.append('div')
.style('position', 'fixed')
.style('display', 'none')
.style('z-index', '9999')
.style('touch-action', 'none'); // Prevent default touch actions
const svg = this.menuElement.append('svg')
.attr('width', this.menuSize)
.attr('height', this.menuSize)
.append('g')
.attr('transform', `translate(${this.menuSize / 2},${this.menuSize / 2})`);
const pie = d3.pie<any>()
.value(() => 1)
.padAngle(0.03);
const arc = d3.arc<any>()
.innerRadius(this.centerButtonSize + 10)
.outerRadius(this.menuSize / 2 - 10);
const arcs = svg.selectAll('path')
.data(pie(this.menuItems))
.enter()
.append('g');
arcs.append('path')
.attr('d', arc)
.attr('fill', d => d.data.color)
.on('click', (event, d) => this.handleMenuSelection(d.data.name))
.on('touchstart', (event, d) => {
event.preventDefault();
this.handleMenuSelection(d.data.name);
});
arcs.append('text')
.attr('transform', d => `translate(${arc.centroid(d)})`)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.style('font-size', '14px')
.text(d => d.data.name);
// Add center button
svg.append('circle')
.attr('r', this.centerButtonSize)
.attr('fill', '#2c3e50')
.on('click', () => this.handleCenterButtonClick())
.on('touchstart', (event) => {
event.preventDefault();
this.handleCenterButtonClick();
});
svg.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '0.3em')
.attr('fill', 'white')
.style('font-size', '14px')
.text('Close');
}
tick() {
// Update logic if needed
}
render(context: CanvasRenderingContext2D) {
// No need to render anything on the canvas
}
shouldTransform(): boolean {
return false;
}
private onContextMenu(event: ContextMenuEvent) {
console.log('on context menu')
if (this.isVisible) {
this.hideRadialMenu()
} else {
this.showRadialMenu(event.x, event.y);
}
}
private showRadialMenu(x: number, y: number) {
this.menuElement
.style('left', `${x - this.menuSize / 2}px`)
.style('top', `${y - this.menuSize / 2}px`)
.style('display', 'block');
this.isVisible = true;
}
private hideRadialMenu() {
this.menuElement.style('display', 'none');
this.isVisible = false;
}
private handleMenuSelection(action: string) {
console.log(`Selected action: ${action}`);
this.hideRadialMenu();
}
private handleCenterButtonClick() {
console.log('Center button clicked');
this.hideRadialMenu();
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ import {EventBus, GameEvent} from "../../../core/EventBus";
import {WinEvent} from "../../../core/execution/WinCheckExecution";
import {AllianceRequest, AllianceRequestReplyEvent, Game, Player} from "../../../core/game/Game";
import {ClientID} from "../../../core/Schemas";
import {RightClickEvent} from "../../InputHandler";
import {ContextMenuEvent} from "../../InputHandler";
import {Layer} from "./Layer";
import {TransformHandler} from "../TransformHandler";
import {MessageType} from "./EventsDisplay";
@@ -74,7 +74,7 @@ export class UILayer implements Layer {
this.createWinModal()
this.initRightClickMenu()
this.eventBus.on(WinEvent, (e) => this.onWinEvent(e))
this.eventBus.on(RightClickEvent, (e) => this.onRightClick(e))
this.eventBus.on(ContextMenuEvent, (e) => this.onRightClick(e))
}
initRightClickMenu() {
@@ -236,7 +236,7 @@ export class UILayer implements Layer {
window.location.reload();
}
private onRightClick(e: RightClickEvent) {
private onRightClick(e: ContextMenuEvent) {
const cell = this.transformHandler.screenToWorldCoordinates(e.x, e.y)
if (!this.game.isOnMap(cell)) {
return
+1
View File
@@ -47,6 +47,7 @@
</div>
<div id="app"></div>
<div id="radialMenu" class="radial-menu"></div>
<style>
body {
+24
View File
@@ -298,4 +298,28 @@ h3 {
display: block;
margin: 5px 0;
}
}
/* Radial Menu */
.radial-menu {
position: absolute;
display: none;
width: 150px;
height: 150px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.5);
}
.menu-item {
position: absolute;
width: 40px;
height: 40px;
border-radius: 50%;
background: white;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
cursor: pointer;
}
+2 -2
View File
@@ -10,10 +10,10 @@ export const devConfig = new class extends DefaultConfig {
return 80
}
gameCreationRate(): number {
return 10 * 1000
return 2 * 1000
}
lobbyLifetime(): number {
return 10 * 1000
return 2 * 1000
}
turnIntervalMs(): number {
return 100