diff --git a/TODO.txt b/TODO.txt index d76f7148b..f4f3806cd 100644 --- a/TODO.txt +++ b/TODO.txt @@ -242,6 +242,10 @@ * better error logging in server DONE 12/16/2024 * store and archive player cookies DONE 12/17/2024 * make ips less precise for user safety DONE 12/17/2024 +* send client logs back to server DONE 12/18/2024 +* render more info in info overlay +* right click brings up player info menu +* create new view for enemies & personal units * send client logs back to server DONE 12/17/2024 * make info panel merge with X, display more info * right click brings up player info menu diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 2226b3a19..a35459948 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -68,6 +68,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: playerInfo.eventBus = eventBus playerInfo.clientID = clientID playerInfo.transform = transformHandler + playerInfo.game = game const layers: Layer[] = [ diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index 4eac09d54..5cdb26699 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -6,51 +6,64 @@ import { ClientID } from '../../../core/Schemas'; import { EventBus } from '../../../core/EventBus'; import { TransformHandler } from '../TransformHandler'; import { MouseMoveEvent } from '../../InputHandler'; -import { dist, distSortUnit, euclideanDist, manhattanDist } from '../../../core/Util'; +import { euclideanDist, distSortUnit } from '../../../core/Util'; +import { renderNumber, renderTroops } from '../../Utils'; @customElement('player-info-overlay') export class PlayerInfoOverlay extends LitElement implements Layer { - private game: Game; - public clientID: ClientID; - public eventBus: EventBus; - public transform: TransformHandler; + @property({ type: Object }) + public game!: Game; + + @property({ type: String }) + public clientID!: ClientID; + + @property({ type: Object }) + public eventBus!: EventBus; + + @property({ type: Object }) + public transform!: TransformHandler; @state() - private selectedType: "player" | "unit" | null = null + private player: Player | null = null; @state() - private player: Player - - @state() - private unit: Unit + private unit: Unit | null = null; @state() private _isVisible: boolean = false; init(game: Game) { this.game = game; - this.eventBus.on(MouseMoveEvent, e => this.onMouseEvent(e)); + this.eventBus.on(MouseMoveEvent, (e: MouseMoveEvent) => this.onMouseEvent(e)); } private onMouseEvent(event: MouseMoveEvent) { - this._isVisible = false; + this.setVisible(false); + this.unit = null; + this.player = null; + const worldCoord = this.transform.screenToWorldCoordinates(event.x, event.y); if (!this.game.isOnMap(worldCoord)) { return; + return; } + const tile = this.game.tile(worldCoord); - let owner = tile.owner(); - if (!owner.isPlayer()) { - if (tile.isLand()) { - return; + const owner = tile.owner(); + + if (owner.isPlayer()) { + this.player = owner; + this.setVisible(true); + } else if (!tile.isLand()) { + const units = this.game.units() + .filter(u => euclideanDist(worldCoord, u.tile().cell()) < 50) + .sort(distSortUnit(tile)); + + if (units.length > 0) { + this.unit = units[0]; + this.setVisible(true); } - const units = this.game.units().filter(u => euclideanDist(worldCoord, u.tile().cell()) < 50).sort(distSortUnit(tile)); - if (units.length == 0) { - return; - } - owner = units[0].owner(); } - this._isVisible = true; } private onExitButtonClick() { @@ -58,11 +71,14 @@ export class PlayerInfoOverlay extends LitElement implements Layer { } tick() { - this.unit = this.unit + if (this.game.ticks() % 10 == 0) { + this.requestUpdate() + } + // Implementation for Layer interface } renderLayer(context: CanvasRenderingContext2D) { - // Render any necessary canvas elements + // Implementation for Layer interface } shouldTransform(): boolean { @@ -74,47 +90,70 @@ export class PlayerInfoOverlay extends LitElement implements Layer { this.requestUpdate(); } + private myPlayer(): Player | null { + if (!this.game) { + return null; + } + return this.game.playerByClientID(this.clientID); + } + + private renderPlayerInfo(player: Player) { + const isAlly = (this.myPlayer()?.isAlliedWith(player) || player == this.myPlayer()) ?? false; + return html` +
+
${player.name()}
+
Troops: ${renderTroops(player.troops())}
+
Gold: ${renderNumber(player.gold())}
+
+ `; + } + + private renderUnitInfo(unit: Unit) { + const isAlly = (unit.owner() == this.myPlayer() || this.myPlayer()?.isAlliedWith(unit.owner())) ?? false; + return html` +
+
${unit.owner().name()}
+
+
${unit.type()}
+
+
+ ` + } + + render() { + return html` +
+
+ +
+
+ ${this.player != null ? this.renderPlayerInfo(this.player) : ''} + ${this.unit != null ? this.renderUnitInfo(this.unit) : ''} +
+
+ `; + } + static styles = css` :host { display: block; } - .overlay-container { + + .container { position: fixed; top: 10px; right: 10px; z-index: 9999; display: flex; flex-direction: column; - align-items: flex-end; } + .controls { - position: relative; - display: flex; - justify-content: flex-end; - width: 40px; /* Same as button width */ - margin-bottom: 10px; - } - .exit-button { - width: 40px; - height: 40px; - font-size: 20px; - font-weight: bold; - background-color: rgba(255, 0, 0, 0.4); - color: white; - border: none; - border-radius: 50%; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - transition: background-color 0.3s; - } - .exit-button:hover { - background-color: rgba(255, 0, 0, 0.5); - } - .exit-button:active { - background-color: rgba(255, 0, 0, 0.6); + align-self: flex-end; + margin-bottom: 4px; + z-index: 2; } + .player-info { background-color: rgba(30, 30, 30, 0.7); padding: 8px 12px; @@ -123,84 +162,85 @@ export class PlayerInfoOverlay extends LitElement implements Layer { backdrop-filter: blur(5px); transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out; color: white; + font-size: 18px; min-width: 120px; text-align: left; - font-size: 18px; } .hidden { opacity: 0; visibility: hidden; - display: none; + pointer-events: none; + } + + .info-content { + margin-top: 8px; } .player-name { font-weight: bold; margin-bottom: 4px; } + .player-name.ally { color: #4CAF50; } + + .type-label { + font-size: 14px; + opacity: 0.8; + } + + .unit-details { + margin-top: 4px; + } + + .health-bar { + height: 4px; + background-color: rgba(255, 255, 255, 0.2); + border-radius: 2px; + margin-top: 4px; + } + + .health-fill { + height: 100%; + background-color: #4CAF50; + border-radius: 2px; + transition: width 0.2s ease-out; + } + + .exit-button { + background: none; + border: none; + color: white; + font-size: 40px; + cursor: pointer; + padding: 4px; + opacity: 0.7; + transition: opacity 0.2s; + } + + .exit-button:hover { + opacity: 1; + } + @media (max-width: 768px) { - .overlay-container { + .container { top: 5px; right: 5px; } - .controls { - width: 30px; - } + .player-info { padding: 6px 10px; font-size: 12px; min-width: 100px; } + .exit-button { - width: 30px; - height: 30px; - font-size: 16px; - .exit-button { - width: 30px; - height: 30px; font-size: 16px; } + + .type-label { + font-size: 12px; + } } `; - - private renderPlayerInfo(player: Player) { - return html` -
-
${player.name}
-
Player Territory
-
- `; - } - - private renderUnitInfo(unit: Unit) { - return html` -
-
${unit.owner}
-
-
${unit.type}
-
-
-
-
-
- `; - } - - render() { - return html` -
-
- -
-
- ${this._infoType === 'player' && this._playerInfo - ? this.renderPlayerInfo(this._playerInfo) - : this._infoType === 'unit' && this._unitInfo - ? this.renderUnitInfo(this._unitInfo) - : nothing} -
-
- `; - } } \ No newline at end of file diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 601a2db27..8912a76de 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -1,13 +1,9 @@ -import { GameEnv, Theme } from "../../../core/configuration/Config"; import { EventBus } from "../../../core/EventBus"; import { WinEvent } from "../../../core/execution/WinCheckExecution"; import { AllianceRequest, AllianceRequestReplyEvent, Game, Player } from "../../../core/game/Game"; import { ClientID } from "../../../core/Schemas"; -import { ContextMenuEvent } from "../../InputHandler"; import { Layer } from "./Layer"; import { TransformHandler } from "../TransformHandler"; -import { MessageType } from "./EventsDisplay"; -import { SendBreakAllianceIntentEvent } from "../../Transport"; import { consolex } from "../../../core/Consolex"; interface MenuOption { @@ -151,6 +147,7 @@ export class UILayer implements Layer { button.onmouseout = () => button.style.backgroundColor = '#4A90E2'; } + onWinEvent(event: WinEvent) { consolex.log(`${event.winner.name()} won the game!!}`) this.showWinModal(event.winner) diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index c51099402..9b472ef3b 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -8,7 +8,7 @@ export const devConfig = new class extends DefaultConfig { unitInfo(type: UnitType): UnitInfo { const info = super.unitInfo(type) const oldCost = info.cost - info.cost = (p: Player) => oldCost(p) / 20 + info.cost = (p: Player) => oldCost(p) / 10000 return info }