mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 08:20:50 +00:00
add troop send amount bar. can full send!
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface UIState {
|
||||
attackRatio: number
|
||||
}
|
||||
@@ -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>
|
||||
`;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user