diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 062c2abba..d4663178a 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -214,14 +214,13 @@ export class GameRunner { if (tile.isLand()) { if (tile.hasOwner()) { if (this.myPlayer.sharesBorderWith(tile.owner())) { - this.eventBus.emit(new SendAttackIntentEvent(targetID)) + this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio)) } } else { - outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.isLand(), dist(tile, 200)))) { for (const n of t.neighbors()) { if (n.owner() == this.myPlayer) { - this.eventBus.emit(new SendAttackIntentEvent(targetID)) + this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio)) break outer_loop } } diff --git a/src/client/Transport.ts b/src/client/Transport.ts index e17bd68b9..8fd187f59 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -35,6 +35,7 @@ export class SendSpawnIntentEvent implements GameEvent { export class SendAttackIntentEvent implements GameEvent { constructor( public readonly targetID: PlayerID, + public readonly troops: number, ) { } } @@ -237,7 +238,7 @@ export class Transport { clientID: this.clientID, attackerID: this.playerID, targetID: event.targetID, - troops: null, + troops: event.troops, sourceX: null, sourceY: null, targetX: null, diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 9ef9eed11..dbdb80f0c 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -1,22 +1,25 @@ -import {Game} from "../../core/game/Game"; -import {NameLayer} from "./layers/NameLayer"; -import {TerrainLayer} from "./layers/TerrainLayer"; -import {TerritoryLayer} from "./layers/TerritoryLayer"; -import {ClientID} from "../../core/Schemas"; -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/radial/RadialMenu"; -import {EmojiTable} from "./layers/radial/EmojiTable"; -import {Leaderboard} from "./layers/Leaderboard"; -import {ControlPanel} from "./layers/ControlPanel"; +import { Game } from "../../core/game/Game"; +import { NameLayer } from "./layers/NameLayer"; +import { TerrainLayer } from "./layers/TerrainLayer"; +import { TerritoryLayer } from "./layers/TerritoryLayer"; +import { ClientID } from "../../core/Schemas"; +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/radial/RadialMenu"; +import { EmojiTable } from "./layers/radial/EmojiTable"; +import { Leaderboard } from "./layers/Leaderboard"; +import { ControlPanel } from "./layers/ControlPanel"; +import { UIState } from "./UIState"; export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer { const transformHandler = new TransformHandler(game, eventBus, canvas) + const uiState = { attackRatio: 20 } + const emojiTable = document.querySelector('emoji-table') as EmojiTable; if (!emojiTable || !(emojiTable instanceof EmojiTable)) { console.error('EmojiTable element not found in the DOM'); @@ -33,6 +36,9 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: if (!(controlPanel instanceof ControlPanel)) { console.error('ControlPanel element not found in the DOM'); } + controlPanel.clientID = clientID + controlPanel.eventBus = eventBus + controlPanel.uiState = uiState const layers: Layer[] = [ new TerrainLayer(game), @@ -40,12 +46,12 @@ 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, game, transformHandler, clientID, emojiTable as EmojiTable), + new RadialMenu(eventBus, game, transformHandler, clientID, emojiTable as EmojiTable, uiState), leaderboard, controlPanel, ] - return new GameRenderer(game, eventBus, canvas, transformHandler, layers) + return new GameRenderer(game, eventBus, canvas, transformHandler, uiState, layers) } @@ -53,7 +59,7 @@ export class GameRenderer { private context: CanvasRenderingContext2D - constructor(private game: Game, private eventBus: EventBus, private canvas: HTMLCanvasElement, public transformHandler: TransformHandler, private layers: Layer[]) { + constructor(private game: Game, private eventBus: EventBus, private canvas: HTMLCanvasElement, public transformHandler: TransformHandler, public uiState: UIState, private layers: Layer[]) { this.context = canvas.getContext("2d") } diff --git a/src/client/graphics/UIState.ts b/src/client/graphics/UIState.ts new file mode 100644 index 000000000..cdd163441 --- /dev/null +++ b/src/client/graphics/UIState.ts @@ -0,0 +1,3 @@ +export interface UIState { + attackRatio: number +} \ No newline at end of file diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 9774ddfe8..8b9dfd0b8 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -1,29 +1,61 @@ -import {LitElement, html, css} from 'lit'; -import {customElement, property, state} from 'lit/decorators.js'; -import {Layer} from './Layer'; -import {Game} from '../../../core/game/Game'; -import {ClientID} from '../../../core/Schemas'; +import { LitElement, html, css } from 'lit'; +import { customElement, property, state } from 'lit/decorators.js'; +import { Layer } from './Layer'; +import { Game } from '../../../core/game/Game'; +import { ClientID } from '../../../core/Schemas'; +import { renderTroops } from '../Utils'; +import { EventBus } from '../../../core/EventBus'; +import { UIState } from '../UIState'; @customElement('control-panel') export class ControlPanel extends LitElement implements Layer { private game: Game public clientID: ClientID + public eventBus: EventBus + public uiState: UIState @state() - private _numTroops = 50; + private attackRatio: number = .2; + + @state() + private _troops: number; + + @state() + private _maxTroops: number; + + @state() + private _manpower: number = 0; + + @state() + private _reserve: number = 0; + + @state() + private _gold: number = 0 @state() private _isVisible = false; init(game: Game) { - this.game = game + this.game = game; + this.attackRatio = .20 + this.uiState.attackRatio = this.attackRatio } tick() { // Update game state based on numTroops value if needed if (!this._isVisible && !this.game.inSpawnPhase()) { - this.toggleVisibility() + this.toggleVisibility(); } + + const player = this.game.playerByClientID(this.clientID) + if (player == null) { + return + } + this._troops = player.troops() + } + + onAttackRatioChange(newRatio: number) { + this.uiState.attackRatio = newRatio } renderLayer(context: CanvasRenderingContext2D) { @@ -31,7 +63,7 @@ export class ControlPanel extends LitElement implements Layer { } shouldTransform(): boolean { - return false + return false; } toggleVisibility() { @@ -39,6 +71,11 @@ export class ControlPanel extends LitElement implements Layer { this.requestUpdate(); } + targetTroops(): number { + return this._maxTroops * this.attackRatio + } + + static styles = css` :host { display: block; @@ -63,6 +100,21 @@ export class ControlPanel extends LitElement implements Layer { .slider-container { margin-bottom: 15px; } + .control-panel-info { + color: white; + margin-bottom: 15px; + padding: 10px; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 5px; + } + .info-row { + display: flex; + justify-content: space-between; + margin-bottom: 5px; + } + .info-label { + font-weight: bold; + } label { display: block; color: white; @@ -80,10 +132,19 @@ export class ControlPanel extends LitElement implements Layer { render() { return html`