add troop send amount bar. can full send!

This commit is contained in:
Evan
2024-10-30 17:31:01 -07:00
parent 56d4b924fa
commit 3ab9f3b617
6 changed files with 123 additions and 50 deletions
+2 -3
View File
@@ -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
}
}
+2 -1
View File
@@ -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,
+23 -17
View File
@@ -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")
}
+3
View File
@@ -0,0 +1,3 @@
export interface UIState {
attackRatio: number
}
+73 -12
View File
@@ -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`
<div class="control-panel ${this._isVisible ? '' : 'hidden'}">
<div class="control-panel-info">
<div class="info-row">
<span class="info-label">Troops:</span>
<span>${renderTroops(this._troops)}</span>
</div>
</div>
<div class="slider-container">
<label for="numTroops">Number of Troops</label>
<input type="range" id="numTroops" min="0" max="100" .value=${this._numTroops}
@input=${(e: Event) => this._numTroops = parseInt((e.target as HTMLInputElement).value)}>
<label for="numTroops">Attack Ratio: ${this.attackRatio * 100}%</label>
<input type="range" id="numTroops" min="1" max="10" value=${this.attackRatio * 10}
@input=${(e: Event) => {
this.attackRatio = parseInt((e.target as HTMLInputElement).value) / 10;
this.onAttackRatioChange(this.attackRatio);
}}>
</div>
</div>
`;
+20 -17
View File
@@ -1,11 +1,11 @@
import {EventBus} from "../../../../core/EventBus";
import {AllPlayers, Cell, Game, Player} from "../../../../core/game/Game";
import {ClientID} from "../../../../core/Schemas";
import {and, bfs, dist, manhattanDist, manhattanDistWrapped, sourceDstOceanShore} from "../../../../core/Util";
import {ContextMenuEvent, MouseUpEvent} from "../../../InputHandler";
import {SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendDonateIntentEvent, SendEmojiIntentEvent, SendNukeIntentEvent, SendSpawnIntentEvent, SendTargetPlayerIntentEvent} from "../../../Transport";
import {TransformHandler} from "../../TransformHandler";
import {Layer} from "../Layer";
import { EventBus } from "../../../../core/EventBus";
import { AllPlayers, Cell, Game, Player } from "../../../../core/game/Game";
import { ClientID } from "../../../../core/Schemas";
import { and, bfs, dist, manhattanDist, manhattanDistWrapped, sourceDstOceanShore } from "../../../../core/Util";
import { ContextMenuEvent, MouseUpEvent } from "../../../InputHandler";
import { SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendDonateIntentEvent, SendEmojiIntentEvent, SendNukeIntentEvent, SendSpawnIntentEvent, SendTargetPlayerIntentEvent } from "../../../Transport";
import { TransformHandler } from "../../TransformHandler";
import { Layer } from "../Layer";
import * as d3 from 'd3';
import traitorIcon from '../../../../../resources/images/TraitorIconWhite.png';
import allianceIcon from '../../../../../resources/images/AllianceIconWhite.png';
@@ -16,7 +16,8 @@ import emojiIcon from '../../../../../resources/images/EmojiIconWhite.png';
import disabledIcon from '../../../../../resources/images/DisabledIcon.png';
import donateIcon from '../../../../../resources/images/DonateIconWhite.png';
import nukeIcon from '../../../../../resources/images/NukeIconWhite.png';
import {EmojiTable} from "./EmojiTable";
import { EmojiTable } from "./EmojiTable";
import { UIState } from "../../UIState";
enum Slot {
@@ -33,11 +34,11 @@ export class RadialMenu implements Layer {
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
private isVisible: boolean = false;
private readonly menuItems = new Map([
[Slot.Alliance, {name: "alliance", disabled: true, action: () => { }, color: null, icon: null}],
[Slot.Boat, {name: "boat", disabled: true, action: () => { }, color: null, icon: null}],
[Slot.Target, {name: "target", disabled: true, action: () => { }}],
[Slot.Emoji, {name: "emoji", disabled: true, action: () => { }}],
[Slot.Nuke, {name: "nuke", disabled: true, action: () => { }}],
[Slot.Alliance, { name: "alliance", disabled: true, action: () => { }, color: null, icon: null }],
[Slot.Boat, { name: "boat", disabled: true, action: () => { }, color: null, icon: null }],
[Slot.Target, { name: "target", disabled: true, action: () => { } }],
[Slot.Emoji, { name: "emoji", disabled: true, action: () => { } }],
[Slot.Nuke, { name: "nuke", disabled: true, action: () => { } }],
]);
private readonly menuSize = 190;
@@ -53,7 +54,8 @@ export class RadialMenu implements Layer {
private game: Game,
private transformHandler: TransformHandler,
private clientID: ClientID,
private emojiTable: EmojiTable
private emojiTable: EmojiTable,
private uiState: UIState
) { }
init() {
@@ -345,7 +347,7 @@ export class RadialMenu implements Layer {
if (manhattanDistWrapped(src.cell(), dst.cell(), this.game.width()) < this.game.config().boatMaxDistance()) {
this.activateMenuElement(Slot.Boat, "#3f6ab1", boatIcon, () => {
this.eventBus.emit(
new SendBoatAttackIntentEvent(other.id(), this.clickedCell, null)
new SendBoatAttackIntentEvent(other.id(), this.clickedCell, this.uiState.attackRatio * myPlayer.troops())
)
})
}
@@ -384,7 +386,8 @@ export class RadialMenu implements Layer {
this.eventBus.emit(new SendSpawnIntentEvent(this.clickedCell))
} else {
if (clicked.owner().clientID() != this.clientID) {
this.eventBus.emit(new SendAttackIntentEvent(clicked.owner().id()))
const myPlayer = this.game.players().find(p => p.clientID() == this.clientID)
this.eventBus.emit(new SendAttackIntentEvent(clicked.owner().id(), this.uiState.attackRatio * myPlayer.troops()))
}
}
this.hideRadialMenu();