mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:40:44 +00:00
radial menu can send attack
This commit is contained in:
@@ -269,11 +269,7 @@ export class ClientGame {
|
||||
this.gs.config().boatAttackAmount(this.myPlayer, owner)
|
||||
))
|
||||
} else {
|
||||
this.eventBus.emit(new SendAttackIntentEvent(
|
||||
targetID,
|
||||
cell,
|
||||
this.gs.config().attackAmount(this.myPlayer, owner)
|
||||
))
|
||||
this.eventBus.emit(new SendAttackIntentEvent(targetID))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {EventBus, GameEvent} from "../core/EventBus"
|
||||
import {AllianceRequest, Cell, Player, PlayerID, PlayerType} from "../core/game/Game"
|
||||
import {AllianceRequest, Cell, Game, Player, PlayerID, PlayerType} from "../core/game/Game"
|
||||
import {ClientID, ClientIntentMessageSchema, GameID, Intent} from "../core/Schemas"
|
||||
|
||||
|
||||
@@ -31,9 +31,8 @@ export class SendSpawnIntentEvent implements GameEvent {
|
||||
}
|
||||
|
||||
export class SendAttackIntentEvent implements GameEvent {
|
||||
constructor(public readonly targetID: PlayerID,
|
||||
public readonly cell: Cell,
|
||||
public readonly troops: number
|
||||
constructor(
|
||||
public readonly targetID: PlayerID,
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -108,11 +107,11 @@ export class Transport {
|
||||
clientID: this.clientID,
|
||||
attackerID: this.playerID,
|
||||
targetID: event.targetID,
|
||||
troops: event.troops,
|
||||
troops: null,
|
||||
sourceX: null,
|
||||
sourceY: null,
|
||||
targetX: event.cell.x,
|
||||
targetY: event.cell.y
|
||||
targetX: null,
|
||||
targetY: null,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus:
|
||||
new NameLayer(game, game.config().theme(), transformHandler, clientID),
|
||||
new UILayer(eventBus, game, clientID, transformHandler),
|
||||
new EventsDisplay(eventBus, game, clientID),
|
||||
new RadialMenu(eventBus),
|
||||
new RadialMenu(eventBus, game, transformHandler, clientID),
|
||||
]
|
||||
|
||||
return new GameRenderer(game, eventBus, canvas, transformHandler, layers)
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import {EventBus} from "../../../core/EventBus";
|
||||
import {ContextMenuEvent} from "../../InputHandler";
|
||||
import {Cell, Game, Player, PlayerID} from "../../../core/game/Game";
|
||||
import {ClientID} from "../../../core/Schemas";
|
||||
import {ContextMenuEvent, MouseUpEvent} from "../../InputHandler";
|
||||
import {SendAttackIntentEvent} from "../../Transport";
|
||||
import {TransformHandler} from "../TransformHandler";
|
||||
import {Layer} from "./Layer";
|
||||
import * as d3 from 'd3';
|
||||
|
||||
export class RadialMenu implements Layer {
|
||||
private clickedCell: Cell | null = null
|
||||
|
||||
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
|
||||
private isVisible: boolean = false;
|
||||
private readonly menuItems = [
|
||||
@@ -16,10 +22,16 @@ export class RadialMenu implements Layer {
|
||||
private readonly menuSize = 300; // Increased size
|
||||
private readonly centerButtonSize = 60;
|
||||
|
||||
constructor(private eventBus: EventBus) { }
|
||||
constructor(
|
||||
private eventBus: EventBus,
|
||||
private game: Game,
|
||||
private transformHandler: TransformHandler,
|
||||
private clientID: ClientID,
|
||||
) { }
|
||||
|
||||
init() {
|
||||
this.eventBus.on(ContextMenuEvent, e => this.onContextMenu(e))
|
||||
this.eventBus.on(MouseUpEvent, e => this.onPointerUp(e))
|
||||
this.createMenuElement();
|
||||
}
|
||||
|
||||
@@ -66,22 +78,30 @@ export class RadialMenu implements Layer {
|
||||
.style('font-size', '14px')
|
||||
.text(d => d.data.name);
|
||||
|
||||
// Add center button
|
||||
// Create a larger, transparent circle for better click detection
|
||||
svg.append('circle')
|
||||
.attr('r', this.centerButtonSize)
|
||||
.attr('fill', '#2c3e50')
|
||||
.attr('fill', 'transparent')
|
||||
.style('cursor', 'pointer')
|
||||
.on('click', () => this.handleCenterButtonClick())
|
||||
.on('touchstart', (event) => {
|
||||
event.preventDefault();
|
||||
this.handleCenterButtonClick();
|
||||
});
|
||||
|
||||
// Add visible center button circle
|
||||
svg.append('circle')
|
||||
.attr('r', this.centerButtonSize - 10)
|
||||
.attr('fill', '#2c3e50')
|
||||
.style('pointer-events', 'none');
|
||||
|
||||
// Add text to the center button
|
||||
svg.append('text')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dy', '0.3em')
|
||||
.attr('fill', 'white')
|
||||
.style('font-size', '14px')
|
||||
.text('Close');
|
||||
.style('font-size', '16px')
|
||||
.style('pointer-events', 'none')
|
||||
.text('Attack');
|
||||
}
|
||||
|
||||
tick() {
|
||||
@@ -99,6 +119,7 @@ export class RadialMenu implements Layer {
|
||||
private onContextMenu(event: ContextMenuEvent) {
|
||||
console.log('on context menu')
|
||||
|
||||
this.clickedCell = this.transformHandler.screenToWorldCoordinates(event.x, event.y)
|
||||
if (this.isVisible) {
|
||||
this.hideRadialMenu()
|
||||
} else {
|
||||
@@ -106,6 +127,10 @@ export class RadialMenu implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
private onPointerUp(event: MouseUpEvent) {
|
||||
this.hideRadialMenu()
|
||||
}
|
||||
|
||||
private showRadialMenu(x: number, y: number) {
|
||||
this.menuElement
|
||||
.style('left', `${x - this.menuSize / 2}px`)
|
||||
@@ -126,6 +151,10 @@ export class RadialMenu implements Layer {
|
||||
|
||||
private handleCenterButtonClick() {
|
||||
console.log('Center button clicked');
|
||||
const clicked = this.game.tile(this.clickedCell)
|
||||
if (clicked.owner().clientID() != this.clientID) {
|
||||
this.eventBus.emit(new SendAttackIntentEvent(clicked.owner().id()))
|
||||
}
|
||||
this.hideRadialMenu();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const AttackIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('attack'),
|
||||
attackerID: z.string(),
|
||||
targetID: z.string().nullable(),
|
||||
troops: z.number(),
|
||||
troops: z.number().nullable(),
|
||||
sourceX: z.number().nullable(),
|
||||
sourceY: z.number().nullable(),
|
||||
targetX: z.number().nullable(),
|
||||
|
||||
@@ -27,7 +27,7 @@ export class AttackExecution implements Execution {
|
||||
private border = new Set<Tile>()
|
||||
|
||||
constructor(
|
||||
private troops: number,
|
||||
private troops: number | null,
|
||||
private _ownerID: PlayerID,
|
||||
private _targetID: PlayerID | null,
|
||||
private sourceCell: Cell | null,
|
||||
@@ -52,8 +52,12 @@ export class AttackExecution implements Execution {
|
||||
|
||||
this._owner = mg.player(this._ownerID)
|
||||
this.target = this._targetID == this.mg.terraNullius().id() ? mg.terraNullius() : mg.player(this._targetID)
|
||||
|
||||
if (this.troops == null) {
|
||||
this.troops = this.mg.config().attackAmount(this._owner, this.target)
|
||||
}
|
||||
this.troops = Math.min(this._owner.troops(), this.troops)
|
||||
this._owner.setTroops(this._owner.troops() - this.troops)
|
||||
this._owner.removeTroops(this.troops)
|
||||
|
||||
for (const exec of mg.executions()) {
|
||||
if (exec.isActive() && exec instanceof AttackExecution && exec != this) {
|
||||
|
||||
@@ -120,6 +120,7 @@ export interface TerraNullius {
|
||||
ownsTile(cell: Cell): boolean
|
||||
isPlayer(): false
|
||||
id(): PlayerID // always zero, maybe make it TerraNulliusID?
|
||||
clientID(): ClientID
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {ClientID} from "../Schemas";
|
||||
import {TerraNullius, Cell, Tile, PlayerID} from "./Game";
|
||||
import {GameImpl} from "./GameImpl";
|
||||
|
||||
@@ -8,10 +9,14 @@ export class TerraNulliusImpl implements TerraNullius {
|
||||
|
||||
constructor(private gs: GameImpl) {
|
||||
}
|
||||
clientID(): ClientID {
|
||||
return "TERRA_NULLIUS_CLIENT_ID"
|
||||
}
|
||||
|
||||
id(): PlayerID {
|
||||
return null
|
||||
}
|
||||
|
||||
ownsTile(cell: Cell): boolean {
|
||||
return this.tiles.has(cell);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user