diff --git a/index.html b/index.html index b196749fde..c1ee7ccaf7 100644 --- a/index.html +++ b/index.html @@ -327,11 +327,18 @@ +
- - +
+ +
@@ -342,11 +349,8 @@ - - - diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 4df65faccd..e91307a78c 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -18,13 +18,11 @@ import { DynamicUILayer } from "./layers/DynamicUILayer"; import { EmojiTable } from "./layers/EmojiTable"; import { EventsDisplay } from "./layers/EventsDisplay"; import { FxLayer } from "./layers/FxLayer"; -import { GameLeftSidebar } from "./layers/GameLeftSidebar"; -import { GameRightSidebar } from "./layers/GameRightSidebar"; +import { GameMenu } from "./layers/GameMenu"; import { HeadsUpMessage } from "./layers/HeadsUpMessage"; import { ImmunityTimer } from "./layers/ImmunityTimer"; import { InGamePromo } from "./layers/InGamePromo"; import { Layer } from "./layers/Layer"; -import { Leaderboard } from "./layers/Leaderboard"; import { MainRadialMenu } from "./layers/MainRadialMenu"; import { MultiTabModal } from "./layers/MultiTabModal"; import { NameLayer } from "./layers/NameLayer"; @@ -33,13 +31,11 @@ import { PerformanceOverlay } from "./layers/PerformanceOverlay"; import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay"; import { PlayerPanel } from "./layers/PlayerPanel"; import { RailroadLayer } from "./layers/RailroadLayer"; -import { ReplayPanel } from "./layers/ReplayPanel"; import { SAMRadiusLayer } from "./layers/SAMRadiusLayer"; import { SettingsModal } from "./layers/SettingsModal"; import { SpawnTimer } from "./layers/SpawnTimer"; import { StructureIconsLayer } from "./layers/StructureIconsLayer"; import { StructureLayer } from "./layers/StructureLayer"; -import { TeamStats } from "./layers/TeamStats"; import { TerrainLayer } from "./layers/TerrainLayer"; import { TerritoryLayer } from "./layers/TerritoryLayer"; import { UILayer } from "./layers/UILayer"; @@ -88,28 +84,12 @@ export function createRenderer( buildMenu.uiState = uiState; buildMenu.transformHandler = transformHandler; - const leaderboard = document.querySelector("leader-board") as Leaderboard; - if (!leaderboard || !(leaderboard instanceof Leaderboard)) { - console.error("LeaderBoard element not found in the DOM"); + const gameMenu = document.querySelector("game-menu") as GameMenu; + if (!gameMenu || !(gameMenu instanceof GameMenu)) { + console.error("GameMenu element not found in the DOM"); } - leaderboard.eventBus = eventBus; - leaderboard.game = game; - - const gameLeftSidebar = document.querySelector( - "game-left-sidebar", - ) as GameLeftSidebar; - if (!gameLeftSidebar || !(gameLeftSidebar instanceof GameLeftSidebar)) { - console.error("GameLeftSidebar element not found in the DOM"); - } - gameLeftSidebar.game = game; - gameLeftSidebar.eventBus = eventBus; - - const teamStats = document.querySelector("team-stats") as TeamStats; - if (!teamStats || !(teamStats instanceof TeamStats)) { - console.error("TeamStats element not found in the DOM"); - } - teamStats.eventBus = eventBus; - teamStats.game = game; + gameMenu.game = game; + gameMenu.eventBus = eventBus; const controlPanel = document.querySelector("control-panel") as ControlPanel; if (!(controlPanel instanceof ControlPanel)) { @@ -163,22 +143,6 @@ export function createRenderer( winModal.eventBus = eventBus; winModal.game = game; - const replayPanel = document.querySelector("replay-panel") as ReplayPanel; - if (!(replayPanel instanceof ReplayPanel)) { - console.error("replay panel not found"); - } - replayPanel.eventBus = eventBus; - replayPanel.game = game; - - const gameRightSidebar = document.querySelector( - "game-right-sidebar", - ) as GameRightSidebar; - if (!(gameRightSidebar instanceof GameRightSidebar)) { - console.error("Game Right bar not found"); - } - gameRightSidebar.game = game; - gameRightSidebar.eventBus = eventBus; - const settingsModal = document.querySelector( "settings-modal", ) as SettingsModal; @@ -304,16 +268,12 @@ export function createRenderer( ), spawnTimer, immunityTimer, - leaderboard, - gameLeftSidebar, + gameMenu, unitDisplay, - gameRightSidebar, controlPanel, playerInfo, winModal, - replayPanel, settingsModal, - teamStats, playerPanel, headsUpMessage, multiTabModal, diff --git a/src/client/graphics/layers/GameLeftSidebar.ts b/src/client/graphics/layers/GameLeftSidebar.ts deleted file mode 100644 index 97cad8cc37..0000000000 --- a/src/client/graphics/layers/GameLeftSidebar.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { Colord } from "colord"; -import { html, LitElement } from "lit"; -import { customElement, state } from "lit/decorators.js"; -import { assetUrl } from "../../../core/AssetUrls"; -import { EventBus } from "../../../core/EventBus"; -import { GameMode, Team } from "../../../core/game/Game"; -import { GameView } from "../../../core/game/GameView"; -import { Platform } from "../../Platform"; -import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils"; -import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; -import { Layer } from "./Layer"; -import { SpawnBarVisibleEvent } from "./SpawnTimer"; -const leaderboardRegularIcon = assetUrl( - "images/LeaderboardIconRegularWhite.svg", -); -const leaderboardSolidIcon = assetUrl("images/LeaderboardIconSolidWhite.svg"); -const teamRegularIcon = assetUrl("images/TeamIconRegularWhite.svg"); -const teamSolidIcon = assetUrl("images/TeamIconSolidWhite.svg"); - -@customElement("game-left-sidebar") -export class GameLeftSidebar extends LitElement implements Layer { - @state() - private isLeaderboardShow = false; - @state() - private isTeamLeaderboardShow = false; - @state() - private isVisible = false; - @state() - private isPlayerTeamLabelVisible = false; - @state() - private playerTeam: Team | null = null; - @state() - private spawnBarVisible = false; - @state() - private immunityBarVisible = false; - - private playerColor: Colord = new Colord("#FFFFFF"); - public game: GameView; - public eventBus: EventBus; - private _shownOnInit = false; - - createRenderRoot() { - return this; - } - - init() { - this.isVisible = true; - this.eventBus.on(SpawnBarVisibleEvent, (e) => { - this.spawnBarVisible = e.visible; - }); - this.eventBus.on(ImmunityBarVisibleEvent, (e) => { - this.immunityBarVisible = e.visible; - }); - if (this.isTeamGame) { - this.isPlayerTeamLabelVisible = true; - } - // Make it visible by default on large screens - if (Platform.isDesktopWidth) { - // lg breakpoint - this._shownOnInit = true; - } - this.requestUpdate(); - } - - tick() { - if (!this.playerTeam && this.game.myPlayer()?.team()) { - this.playerTeam = this.game.myPlayer()!.team(); - if (this.playerTeam) { - this.playerColor = this.game - .config() - .theme() - .teamColor(this.playerTeam); - this.requestUpdate(); - } - } - - if (this._shownOnInit && !this.game.inSpawnPhase()) { - this._shownOnInit = false; - this.isLeaderboardShow = true; - this.requestUpdate(); - } - - if (!this.game.inSpawnPhase() && this.isPlayerTeamLabelVisible) { - this.isPlayerTeamLabelVisible = false; - this.requestUpdate(); - } - } - - private get barOffset(): number { - return (this.spawnBarVisible ? 7 : 0) + (this.immunityBarVisible ? 7 : 0); - } - - private toggleLeaderboard(): void { - this.isLeaderboardShow = !this.isLeaderboardShow; - } - - private toggleTeamLeaderboard(): void { - this.isTeamLeaderboardShow = !this.isTeamLeaderboardShow; - } - - private get isTeamGame(): boolean { - return this.game?.config().gameConfig().gameMode === GameMode.Team; - } - - render() { - return html` - - `; - } -} diff --git a/src/client/graphics/layers/GameMenu.ts b/src/client/graphics/layers/GameMenu.ts new file mode 100644 index 0000000000..f7e9791fd0 --- /dev/null +++ b/src/client/graphics/layers/GameMenu.ts @@ -0,0 +1,415 @@ +import { Colord } from "colord"; +import { html, LitElement } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { assetUrl } from "../../../core/AssetUrls"; +import { EventBus } from "../../../core/EventBus"; +import { GameMode, GameType, Team } from "../../../core/game/Game"; +import { GameView } from "../../../core/game/GameView"; +import { crazyGamesSDK } from "../../CrazyGamesSDK"; +import { Platform } from "../../Platform"; +import { PauseGameIntentEvent, SendWinnerEvent } from "../../Transport"; +import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils"; +import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; +import { Layer } from "./Layer"; +import { Leaderboard } from "./Leaderboard"; +import { ReplayPanel, ShowReplayPanelEvent } from "./ReplayPanel"; +import { ShowSettingsModalEvent } from "./SettingsModal"; +import { SpawnBarVisibleEvent } from "./SpawnTimer"; +import { TeamStats } from "./TeamStats"; + +const exitIcon = assetUrl("images/ExitIconWhite.svg"); +const FastForwardIconSolid = assetUrl("images/FastForwardIconSolidWhite.svg"); +const leaderboardRegularIcon = assetUrl( + "images/LeaderboardIconRegularWhite.svg", +); +const leaderboardSolidIcon = assetUrl("images/LeaderboardIconSolidWhite.svg"); +const pauseIcon = assetUrl("images/PauseIconWhite.svg"); +const playIcon = assetUrl("images/PlayIconWhite.svg"); +const settingsIcon = assetUrl("images/SettingIconWhite.svg"); +const teamRegularIcon = assetUrl("images/TeamIconRegularWhite.svg"); +const teamSolidIcon = assetUrl("images/TeamIconSolidWhite.svg"); + +@customElement("game-menu") +export class GameMenu extends LitElement implements Layer { + @property({ attribute: false }) game: GameView; + public eventBus: EventBus; + + @state() + private _isSinglePlayer: boolean = false; + + @state() + private _isReplayVisible: boolean = false; + + @state() + private _isVisible: boolean = true; + + @state() + private isPaused: boolean = false; + + @state() + private timer: number = 0; + + @state() + private isLeaderboardShow = false; + + @state() + private isTeamLeaderboardShow = false; + + @state() + private isPlayerTeamLabelVisible = false; + + @state() + private playerTeam: Team | null = null; + + private get leaderboard(): Leaderboard | null { + const el = this.querySelector("leader-board"); + return el instanceof Leaderboard ? el : null; + } + private get teamStats(): TeamStats | null { + const el = this.querySelector("team-stats"); + return el instanceof TeamStats ? el : null; + } + private get replayPanel(): ReplayPanel | null { + const el = this.querySelector("replay-panel"); + return el instanceof ReplayPanel ? el : null; + } + + private playerColor: Colord = new Colord("#FFFFFF"); + private hasWinner = false; + private isLobbyCreator = false; + private spawnBarVisible = false; + private immunityBarVisible = false; + private _shownOnInit = false; + + createRenderRoot() { + return this; + } + + init() { + this._isSinglePlayer = + this.game?.config()?.gameConfig()?.gameType === GameType.Singleplayer || + this.game.config().isReplay(); + this._isVisible = true; + + this.eventBus.on(SpawnBarVisibleEvent, (e) => { + this.spawnBarVisible = e.visible; + this.updateParentOffset(); + }); + this.eventBus.on(ImmunityBarVisibleEvent, (e) => { + this.immunityBarVisible = e.visible; + this.updateParentOffset(); + }); + + this.eventBus.on(SendWinnerEvent, () => { + this.hasWinner = true; + this.requestUpdate(); + }); + + if (this.isTeamGame) { + this.isPlayerTeamLabelVisible = true; + } + + if (Platform.isDesktopWidth) { + this._shownOnInit = true; + } + + this.requestUpdate(); + } + + getTickIntervalMs() { + return 250; + } + + tick() { + // Check if the player is the lobby creator + if (!this.isLobbyCreator && this.game.myPlayer()?.isLobbyCreator()) { + this.isLobbyCreator = true; + this.requestUpdate(); + } + + // Team color + if (!this.playerTeam && this.game.myPlayer()?.team()) { + this.playerTeam = this.game.myPlayer()!.team(); + if (this.playerTeam) { + this.playerColor = this.game + .config() + .theme() + .teamColor(this.playerTeam); + this.requestUpdate(); + } + } + + if (this._shownOnInit && !this.game.inSpawnPhase()) { + this._shownOnInit = false; + this.isLeaderboardShow = true; + this.requestUpdate(); + } + + if (!this.game.inSpawnPhase() && this.isPlayerTeamLabelVisible) { + this.isPlayerTeamLabelVisible = false; + this.requestUpdate(); + } + + // Timer logic + const maxTimerValue = this.game.config().gameConfig().maxTimerValue; + const spawnPhaseTurns = this.game.config().numSpawnPhaseTurns(); + const ticks = this.game.ticks(); + const gameTicks = Math.max(0, ticks - spawnPhaseTurns); + const elapsedSeconds = Math.floor(gameTicks / 10); // 10 ticks per second + + const hasMaxTimer = maxTimerValue !== null && maxTimerValue !== undefined; + + if (this.game.inSpawnPhase()) { + this.timer = hasMaxTimer ? maxTimerValue * 60 : 0; + return; + } + + if (this.hasWinner) { + return; + } + + if (hasMaxTimer) { + this.timer = Math.max(0, maxTimerValue * 60 - elapsedSeconds); + } else { + this.timer = elapsedSeconds; + } + + this.leaderboard?.tick(); + this.teamStats?.tick(); + this.replayPanel?.tick?.(); + } + + private updateParentOffset(): void { + const offset = + (this.spawnBarVisible ? 7 : 0) + (this.immunityBarVisible ? 7 : 0); + const parent = this.parentElement as HTMLElement; + if (parent) { + parent.style.marginTop = `${offset}px`; + } + } + + private secondsToHms = (d: number): string => { + const pad = (n: number) => (n < 10 ? `0${n}` : n); + + const h = Math.floor(d / 3600); + const m = Math.floor((d % 3600) / 60); + const s = Math.floor((d % 3600) % 60); + + if (h !== 0) { + return `${pad(h)}:${pad(m)}:${pad(s)}`; + } else { + return `${pad(m)}:${pad(s)}`; + } + }; + + private toggleReplayPanel(): void { + this._isReplayVisible = !this._isReplayVisible; + this.eventBus.emit( + new ShowReplayPanelEvent(this._isReplayVisible, this._isSinglePlayer), + ); + } + + private onPauseButtonClick() { + this.isPaused = !this.isPaused; + if (this.isPaused) { + crazyGamesSDK.gameplayStop(); + } else { + crazyGamesSDK.gameplayStart(); + } + this.eventBus.emit(new PauseGameIntentEvent(this.isPaused)); + } + + private async onExitButtonClick() { + const isAlive = this.game.myPlayer()?.isAlive(); + if (isAlive) { + const isConfirmed = confirm( + translateText("help_modal.exit_confirmation"), + ); + if (!isConfirmed) return; + } + await crazyGamesSDK.requestMidgameAd(); + await crazyGamesSDK.gameplayStop(); + window.location.href = "/"; + } + + private onSettingsButtonClick() { + this.eventBus.emit( + new ShowSettingsModalEvent(true, this._isSinglePlayer, this.isPaused), + ); + } + + private toggleLeaderboard(): void { + this.isLeaderboardShow = !this.isLeaderboardShow; + } + + private toggleTeamLeaderboard(): void { + this.isTeamLeaderboardShow = !this.isTeamLeaderboardShow; + } + + private get isTeamGame(): boolean { + return this.game?.config().gameConfig().gameMode === GameMode.Team; + } + + render() { + if (this.game === undefined) return html``; + + const timerColor = + this.game.config().gameConfig().maxTimerValue !== undefined && + this.timer < 60 + ? "text-red-400" + : ""; + + return html` +
+ + +
+ + ${this.isPlayerTeamLabelVisible + ? html` +
e.preventDefault()} + > + ${translateText("help_modal.ui_your_team")} + +  ${getTranslatedPlayerTeamLabel(this.playerTeam)} + ⦿ + +
+ ` + : null} +
+ + +
+
+
+ `; + } + + maybeRenderReplayButtons() { + const isReplayOrSingleplayer = + this._isSinglePlayer || this.game?.config()?.isReplay(); + const showPauseButton = isReplayOrSingleplayer || this.isLobbyCreator; + + return html` + ${isReplayOrSingleplayer + ? html` +
+ replay +
+ ` + : ""} + ${showPauseButton + ? html` +
+ play/pause +
+ ` + : ""} + `; + } +} diff --git a/src/client/graphics/layers/GameRightSidebar.ts b/src/client/graphics/layers/GameRightSidebar.ts deleted file mode 100644 index 1ec12be84f..0000000000 --- a/src/client/graphics/layers/GameRightSidebar.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { html, LitElement } from "lit"; -import { customElement, state } from "lit/decorators.js"; -import { assetUrl } from "../../../core/AssetUrls"; -import { EventBus } from "../../../core/EventBus"; -import { GameType } from "../../../core/game/Game"; -import { GameView } from "../../../core/game/GameView"; -import { crazyGamesSDK } from "../../CrazyGamesSDK"; -import { TogglePauseIntentEvent } from "../../InputHandler"; -import { PauseGameIntentEvent, SendWinnerEvent } from "../../Transport"; -import { translateText } from "../../Utils"; -import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; -import { Layer } from "./Layer"; -import { ShowReplayPanelEvent } from "./ReplayPanel"; -import { ShowSettingsModalEvent } from "./SettingsModal"; -import { SpawnBarVisibleEvent } from "./SpawnTimer"; -const exitIcon = assetUrl("images/ExitIconWhite.svg"); -const FastForwardIconSolid = assetUrl("images/FastForwardIconSolidWhite.svg"); -const pauseIcon = assetUrl("images/PauseIconWhite.svg"); -const playIcon = assetUrl("images/PlayIconWhite.svg"); -const settingsIcon = assetUrl("images/SettingIconWhite.svg"); -const fullscreenIcon = assetUrl("images/FullscreenIconWhite.svg"); -const exitFullscreenIcon = assetUrl("images/ExitFullscreenIconWhite.svg"); - -@customElement("game-right-sidebar") -export class GameRightSidebar extends LitElement implements Layer { - public game: GameView; - public eventBus: EventBus; - - @state() - private _isSinglePlayer: boolean = false; - - @state() - private _isReplayVisible: boolean = false; - - @state() - private _isVisible: boolean = true; - - @state() - private isPaused: boolean = false; - - @state() - private isFullscreen: boolean = false; - - @state() - private timer: number = 0; - - private hasWinner = false; - private isLobbyCreator = false; - private spawnBarVisible = false; - private immunityBarVisible = false; - - createRenderRoot() { - return this; - } - - init() { - this._isSinglePlayer = - this.game?.config()?.gameConfig()?.gameType === GameType.Singleplayer || - this.game.config().isReplay(); - this._isVisible = true; - this.game.inSpawnPhase(); - - this.eventBus.on(SpawnBarVisibleEvent, (e) => { - this.spawnBarVisible = e.visible; - this.updateParentOffset(); - }); - this.eventBus.on(ImmunityBarVisibleEvent, (e) => { - this.immunityBarVisible = e.visible; - this.updateParentOffset(); - }); - - this.eventBus.on(SendWinnerEvent, () => { - this.hasWinner = true; - this.requestUpdate(); - }); - - this.eventBus.on(TogglePauseIntentEvent, () => { - const isReplayOrSingleplayer = - this._isSinglePlayer || this.game?.config()?.isReplay(); - if (isReplayOrSingleplayer || this.isLobbyCreator) { - this.onPauseButtonClick(); - } - }); - - this.requestUpdate(); - } - - private onFullscreenChange = () => { - this.isFullscreen = !!document.fullscreenElement; - }; - - connectedCallback() { - super.connectedCallback(); - document.addEventListener("fullscreenchange", this.onFullscreenChange); - this.onFullscreenChange(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - document.removeEventListener("fullscreenchange", this.onFullscreenChange); - } - - getTickIntervalMs() { - return 250; - } - - tick() { - // Timer logic - // Check if the player is the lobby creator - if (!this.isLobbyCreator && this.game.myPlayer()?.isLobbyCreator()) { - this.isLobbyCreator = true; - this.requestUpdate(); - } - - const maxTimerValue = this.game.config().gameConfig().maxTimerValue; - const spawnPhaseTurns = this.game.config().numSpawnPhaseTurns(); - const ticks = this.game.ticks(); - const gameTicks = Math.max(0, ticks - spawnPhaseTurns); - const elapsedSeconds = Math.floor(gameTicks / 10); // 10 ticks per second - - if (this.game.inSpawnPhase()) { - this.timer = - maxTimerValue !== null && maxTimerValue !== undefined - ? maxTimerValue * 60 - : 0; - return; - } - - if (this.hasWinner) { - return; - } - - if (maxTimerValue !== null && maxTimerValue !== undefined) { - this.timer = Math.max(0, maxTimerValue * 60 - elapsedSeconds); - } else { - this.timer = elapsedSeconds; - } - } - - private updateParentOffset(): void { - const offset = - (this.spawnBarVisible ? 7 : 0) + (this.immunityBarVisible ? 7 : 0); - const parent = this.parentElement as HTMLElement; - if (parent) { - parent.style.marginTop = `${offset}px`; - } - } - - private secondsToHms = (d: number): string => { - const pad = (n: number) => (n < 10 ? `0${n}` : n); - - const h = Math.floor(d / 3600); - const m = Math.floor((d % 3600) / 60); - const s = Math.floor((d % 3600) % 60); - - if (h !== 0) { - return `${pad(h)}:${pad(m)}:${pad(s)}`; - } else { - return `${pad(m)}:${pad(s)}`; - } - }; - - private toggleReplayPanel(): void { - this._isReplayVisible = !this._isReplayVisible; - this.eventBus.emit( - new ShowReplayPanelEvent(this._isReplayVisible, this._isSinglePlayer), - ); - } - - private onPauseButtonClick() { - this.isPaused = !this.isPaused; - if (this.isPaused) { - crazyGamesSDK.gameplayStop(); - } else { - crazyGamesSDK.gameplayStart(); - } - this.eventBus.emit(new PauseGameIntentEvent(this.isPaused)); - } - - private async onExitButtonClick() { - const isAlive = this.game.myPlayer()?.isAlive(); - if (isAlive) { - const isConfirmed = confirm( - translateText("help_modal.exit_confirmation"), - ); - if (!isConfirmed) return; - } - await crazyGamesSDK.requestMidgameAd(); - await crazyGamesSDK.gameplayStop(); - // redirect to the home page - window.location.href = "/"; - } - - private onSettingsButtonClick() { - this.eventBus.emit( - new ShowSettingsModalEvent(true, this._isSinglePlayer, this.isPaused), - ); - } - - private onFullscreenButtonClick() { - if (!document.fullscreenElement) { - document.documentElement.requestFullscreen().catch((err) => { - console.warn("Failed to enter fullscreen:", err); - }); - } else { - document.exitFullscreen().catch((err) => { - console.warn("Failed to exit fullscreen:", err); - }); - } - } - - render() { - if (this.game === undefined) return html``; - - const timerColor = - this.game.config().gameConfig().maxTimerValue !== undefined && - this.game.config().gameConfig().maxTimerValue !== null && - this.timer < 60 - ? "text-red-400" - : ""; - - return html` - - `; - } - - maybeRenderReplayButtons() { - const isReplayOrSingleplayer = - this._isSinglePlayer || this.game?.config()?.isReplay(); - const showPauseButton = isReplayOrSingleplayer || this.isLobbyCreator; - - return html` - ${isReplayOrSingleplayer - ? html` -
- replay -
- ` - : ""} - ${showPauseButton - ? html` -
- play/pause -
- ` - : ""} - `; - } -} diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index 3100e88d94..43f191342f 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -2,12 +2,17 @@ import { LitElement, html } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { repeat } from "lit/directives/repeat.js"; import { renderTroops, translateText } from "../../../client/Utils"; +import { assetUrl } from "../../../core/AssetUrls"; import { EventBus } from "../../../core/EventBus"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { formatPercentage, renderNumber } from "../../Utils"; import { GoToPlayerEvent } from "../TransformHandler"; import { Layer } from "./Layer"; +const profileIcon = assetUrl("images/ProfileIcon.svg"); +const goldIcon = assetUrl("images/GoldCoinIcon.svg"); +const troopIcon = assetUrl("images/TroopIconWhite.svg"); + interface Entry { name: string; position: number; @@ -21,8 +26,8 @@ interface Entry { @customElement("leader-board") export class Leaderboard extends LitElement implements Layer { - public game: GameView | null = null; - public eventBus: EventBus | null = null; + @property({ attribute: false }) game: GameView | null = null; + @property({ attribute: false }) eventBus: EventBus | null = null; players: Entry[] = []; @@ -41,8 +46,8 @@ export class Leaderboard extends LitElement implements Layer { init() {} - willUpdate(changed: Map) { - if (changed.has("visible") && this.visible) { + willUpdate(_changed: Map) { + if (this.visible && this.game !== null) { this.updateLeaderboard(); } } @@ -177,22 +182,28 @@ export class Leaderboard extends LitElement implements Layer { >
#
- ${translateText("leaderboard.player")} + ${translateText("leaderboard.player")}
this.setSort("tiles")} > - ${translateText("leaderboard.owned")} + 🌐 ${this._sortKey === "tiles" ? this._sortOrder === "asc" ? "⬆️" @@ -200,10 +211,15 @@ export class Leaderboard extends LitElement implements Layer { : ""}
this.setSort("gold")} > - ${translateText("leaderboard.gold")} + ${translateText("leaderboard.gold")} ${this._sortKey === "gold" ? this._sortOrder === "asc" ? "⬆️" @@ -211,10 +227,15 @@ export class Leaderboard extends LitElement implements Layer { : ""}
this.setSort("maxtroops")} > - ${translateText("leaderboard.maxtroops")} + ${translateText("leaderboard.maxtroops")} ${this._sortKey === "maxtroops" ? this._sortOrder === "asc" ? "⬆️" diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index e720738f4b..5a9f969666 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -498,13 +498,13 @@ export class PlayerInfoOverlay extends LitElement implements Layer { return html`
this.hide()} @contextmenu=${(e: MouseEvent) => e.preventDefault()} >
${this.player !== null ? this.renderPlayerInfo(this.player) : ""} ${this.unit !== null ? this.renderUnitInfo(this.unit) : ""} diff --git a/src/client/graphics/layers/ReplayPanel.ts b/src/client/graphics/layers/ReplayPanel.ts index ea48bbb609..28d43a4f37 100644 --- a/src/client/graphics/layers/ReplayPanel.ts +++ b/src/client/graphics/layers/ReplayPanel.ts @@ -19,8 +19,8 @@ export class ShowReplayPanelEvent { @customElement("replay-panel") export class ReplayPanel extends LitElement implements Layer { - public game: GameView | undefined; - public eventBus: EventBus | undefined; + @property({ attribute: false }) game: GameView | undefined; + @property({ attribute: false }) eventBus: EventBus | undefined; @property({ type: Boolean }) visible: boolean = false; @@ -31,12 +31,21 @@ export class ReplayPanel extends LitElement implements Layer { @property({ type: Boolean }) isSingleplayer = false; + private _eventBusSubscribed = false; + createRenderRoot() { return this; // Enable Tailwind CSS } - init() { - if (this.eventBus) { + init() {} + + willUpdate(changed: Map) { + if ( + !this._eventBusSubscribed && + this.eventBus && + (changed.has("eventBus") || changed.size === 0) + ) { + this._eventBusSubscribed = true; this.eventBus.on(ShowReplayPanelEvent, (event: ShowReplayPanelEvent) => { this.visible = event.visible; this.isSingleplayer = event.isSingleplayer; diff --git a/src/client/graphics/layers/TeamStats.ts b/src/client/graphics/layers/TeamStats.ts index 6e846bcdf6..2625fdd605 100644 --- a/src/client/graphics/layers/TeamStats.ts +++ b/src/client/graphics/layers/TeamStats.ts @@ -27,8 +27,8 @@ interface TeamEntry { @customElement("team-stats") export class TeamStats extends LitElement implements Layer { - public game: GameView; - public eventBus: EventBus; + @property({ attribute: false }) game!: GameView; + @property({ attribute: false }) eventBus!: EventBus; @property({ type: Boolean }) visible = false; teams: TeamEntry[] = [];