diff --git a/index.html b/index.html index 0398a3beb..77bb7750d 100644 --- a/index.html +++ b/index.html @@ -239,17 +239,17 @@
+
+ +
-
- -
diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index a6b03abad..c6f000405 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -1,6 +1,5 @@ import { LitElement, html } from "lit"; import { customElement, state } from "lit/decorators.js"; -import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; import { Gold } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; @@ -9,6 +8,9 @@ import { AttackRatioEvent } from "../../InputHandler"; import { renderNumber, renderTroops } from "../../Utils"; import { UIState } from "../UIState"; import { Layer } from "./Layer"; +import goldCoinIcon from "/images/GoldCoinIcon.svg?url"; +import soldierIcon from "/images/SoldierIcon.svg?url"; +import swordIcon from "/images/SwordIcon.svg?url"; @customElement("control-panel") export class ControlPanel extends LitElement implements Layer { @@ -35,6 +37,12 @@ export class ControlPanel extends LitElement implements Layer { @state() private _gold: Gold; + @state() + private _attackingTroops: number = 0; + + @state() + private _touchDragging = false; + private _troopRateIsIncreasing: boolean = true; private _lastTroopIncreaseRate: number; @@ -49,12 +57,7 @@ export class ControlPanel extends LitElement implements Layer { ); this.uiState.attackRatio = this.attackRatio; this.eventBus.on(AttackRatioEvent, (event) => { - let newAttackRatio = - (parseInt( - (document.getElementById("attack-ratio") as HTMLInputElement).value, - ) + - event.attackRatio) / - 100; + let newAttackRatio = this.attackRatio + event.attackRatio / 100; if (newAttackRatio < 0.01) { newAttackRatio = 0.01; @@ -90,6 +93,10 @@ export class ControlPanel extends LitElement implements Layer { this._maxTroops = this.game.config().maxTroops(player); this._gold = player.gold(); this._troops = player.troops(); + this._attackingTroops = player + .outgoingAttacks() + .map((a) => a.troops) + .reduce((a, b) => a + b, 0); this.troopRate = this.game.config().troopIncreaseRate(player) * 10; this.requestUpdate(); } @@ -120,119 +127,255 @@ export class ControlPanel extends LitElement implements Layer { this.requestUpdate(); } + private _outsideTouchHandler: ((ev: Event) => void) | null = null; + + private handleAttackTouchStart(e: TouchEvent) { + e.preventDefault(); + e.stopPropagation(); + + if (this._touchDragging) { + this.closeAttackBar(); + return; + } + + this._touchDragging = true; + + setTimeout(() => { + this._outsideTouchHandler = () => { + this.closeAttackBar(); + }; + document.addEventListener("touchstart", this._outsideTouchHandler); + }, 0); + } + + private closeAttackBar() { + this._touchDragging = false; + if (this._outsideTouchHandler) { + document.removeEventListener("touchstart", this._outsideTouchHandler); + this._outsideTouchHandler = null; + } + } + + private handleBarTouch(e: TouchEvent) { + e.preventDefault(); + e.stopPropagation(); + + this.setRatioFromTouch(e.touches[0]); + + const onMove = (ev: TouchEvent) => { + ev.preventDefault(); + this.setRatioFromTouch(ev.touches[0]); + }; + + const onEnd = () => { + document.removeEventListener("touchmove", onMove); + document.removeEventListener("touchend", onEnd); + }; + + document.addEventListener("touchmove", onMove, { passive: false }); + document.addEventListener("touchend", onEnd); + } + + private setRatioFromTouch(touch: Touch) { + const barEl = this.querySelector(".attack-drag-bar"); + if (!barEl) return; + + const rect = barEl.getBoundingClientRect(); + const ratio = (rect.bottom - touch.clientY) / (rect.bottom - rect.top); + this.attackRatio = + Math.round(Math.max(1, Math.min(100, ratio * 100))) / 100; + this.onAttackRatioChange(this.attackRatio); + } + + private handleRatioSliderInput(e: Event) { + const value = Number((e.target as HTMLInputElement).value); + this.attackRatio = value / 100; + this.onAttackRatioChange(this.attackRatio); + } + + private renderTroopBar() { + const base = Math.max(this._maxTroops, 1); + const greenPercentRaw = (this._troops / base) * 100; + const orangePercentRaw = (this._attackingTroops / base) * 100; + + const greenPercent = Math.max(0, Math.min(100, greenPercentRaw)); + const orangePercent = Math.max( + 0, + Math.min(100 - greenPercent, orangePercentRaw), + ); + + return html` +
+
+ ${greenPercent > 0 + ? html`
` + : ""} + ${orangePercent > 0 + ? html`
` + : ""} +
+
+ ${renderTroops(this._troops)} + ${renderTroops(this._maxTroops)} +
+
+ + +${renderTroops(this.troopRate)}/s +
+
+ `; + } + render() { return html` -
e.preventDefault()} > -
-
- ${translateText("control_panel.troops")}: - ${renderTroops(this._troops)} / ${renderTroops(this._maxTroops)} - + +
+ + ${renderNumber(this._gold)} +
+ +
${this.renderTroopBar()}
+ +
this.handleAttackTouchStart(e)} + > +
+
(+${renderTroops(this.troopRate)}) -
-
- ${translateText("control_panel.gold")}: - ${renderNumber(this._gold)} -
-
- -
-
+
(${renderTroops( (this.game?.myPlayer()?.troops() ?? 0) * this.attackRatio, )}) - - - -
- -
- -
- - { - this.attackRatio = - parseInt((e.target as HTMLInputElement).value) / 100; - this.onAttackRatioChange(this.attackRatio); - }} - class="absolute left-0 right-0 top-2 m-0 h-4 cursor-pointer attackRatio" - /> +
+
+ +
+
+
+
+ ${this._touchDragging + ? html` +
this.handleBarTouch(e)} + > + ${(this.attackRatio * 100).toFixed(0)}% +
+
+
+
+ ` + : ""} +
+ +
`; } diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index 2b66f3336..db68fe987 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -1013,7 +1013,9 @@ export class EventsDisplay extends LitElement implements Layer { ${this._hidden ? html` -
+
${this.renderButton({ content: html` Events @@ -1033,7 +1035,7 @@ export class EventsDisplay extends LitElement implements Layer { : html`