From a02d148151095bb375f4e7b63fe67679c5a29732 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sat, 12 Oct 2024 08:56:11 -0700 Subject: [PATCH] Created PublicLobby lit element --- TODO.txt | 11 ++-- src/client/Client.ts | 98 ++------------------------------ src/client/PublicLobby.ts | 115 ++++++++++++++++++++++++++++++++++++++ src/client/index.html | 10 +--- 4 files changed, 128 insertions(+), 106 deletions(-) create mode 100644 src/client/PublicLobby.ts diff --git a/TODO.txt b/TODO.txt index cff2ead6e..e6e7213d4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -162,21 +162,22 @@ * disable double tap on mobile DONE 10/6/2024 * donate troops button DONE 10/7/2024 * Make fake humans spawn by their country DONE 10/9/2024 +* UI: leader board DONE 10/12/2024 +* single player menu +* private game menu +* optimize sendBoat function * Test on android -* Increase disk size * NPC more likely to accept alliance fewer alliance player has -* fake humans target enemies -* create private lobby menu +* better NPC relation logic +* surface NPC relations * block user inputs if too far behind server * BUG: FakeHuman don't be enemy if don't share border (or send boat) * store cookies -* UI: leader board * Load terrain dataImage in background * BUG: half encircle Antartica causes capture * improve front page (make map larger?) * Add additional maps -* add offline mode * REFACTOR: give terranullius an ID, game.player() returns terranullius * REFACTOR: ocean is considered TerraNullius ? * Make icons svgs diff --git a/src/client/Client.ts b/src/client/Client.ts index 1e80128fe..5e0c8125e 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -6,11 +6,10 @@ import backgroundImage from '../../resources/images/TerrainMapFrontPage.png'; import favicon from '../../resources/images/Favicon.png'; import {v4 as uuidv4} from 'uuid'; +import './PublicLobby'; import './styles.css'; -import {simpleHash} from "../core/Util"; -import {PseudoRandom} from "../core/PseudoRandom"; const usernameKey: string = 'username'; @@ -20,8 +19,6 @@ class Client { private terrainMap: Promise private game: ClientGame - private lobbiesInterval: NodeJS.Timeout | null = null; - private isLobbyHighlighted: boolean = false; private ip: Promise = null @@ -38,101 +35,17 @@ class Client { usernameInput.value = storedUsername } } + this.config = getConfig() setFavicon() this.terrainMap = loadTerrainMap() - this.startLobbyPolling() this.ip = getClientIP() + document.addEventListener('join-lobby', this.handleJoinLobby.bind(this)); } - private startLobbyPolling(): void { - this.fetchAndUpdateLobbies(); // Fetch immediately on start - this.lobbiesInterval = setInterval(() => this.fetchAndUpdateLobbies(), 1000); - } - - private async fetchAndUpdateLobbies(): Promise { - try { - const lobbies = await this.fetchLobbies(); - this.updateLobbiesDisplay(lobbies); - } catch (error) { - console.error('Error fetching and updating lobbies:', error); - } - } - - private updateLobbiesDisplay(lobbies: Lobby[]): void { - if (lobbies.length === 0) { - document.getElementById('lobby-button').style.display = 'none'; - return; - } - - const lobby = lobbies[0]; - const lobbyButton = document.getElementById('lobby-button'); - const nameElement = document.getElementById('lobby-name'); - const timerElement = document.getElementById('lobby-timer'); - const playerCountElement = document.getElementById('player-count'); - - if (lobbyButton) { - lobbyButton.style.display = 'flex'; - lobbyButton.onclick = () => this.joinLobby(lobby); - - // Preserve the highlighted state - lobbyButton.classList.toggle('highlighted', this.isLobbyHighlighted); - } - - if (nameElement) nameElement.textContent = `Game ${lobby.id.substring(0, 3)}`; - if (timerElement) { - const timeRemaining = Math.max(0, Math.floor((lobby.msUntilStart) / 1000)); - timerElement.textContent = `Starts in: ${timeRemaining}s`; - } - - if (playerCountElement) playerCountElement.textContent = `Players: ${lobby.numClients}`; - - if (lobbies.length > 1) { - const nextLobby = lobbies[1] - const nextGame = document.getElementById('next-game'); - nextGame.textContent = `Next Game: ${nextLobby.id.substring(0, 3)}` - } - } - - async fetchLobbies(): Promise { - const url = '/lobbies'; - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}, statusText: ${response.statusText}`); - } - const data = await response.json(); - return data.lobbies; - } catch (error) { - console.error('Error fetching lobbies:', error); - throw error; - } - } - - private async joinLobby(lobby: Lobby) { + private async handleJoinLobby(event: CustomEvent) { + const lobby = event.detail console.log(`joining lobby ${lobby.id}`) - const lobbyButton = document.getElementById('lobby-button'); - if (lobbyButton) { - this.isLobbyHighlighted = !this.isLobbyHighlighted; - lobbyButton.classList.toggle('highlighted', this.isLobbyHighlighted); - } - if (this.isLobbyHighlighted) { - lobbyButton.classList.remove('bg-blue-600', 'hover:bg-blue-700'); - lobbyButton.classList.add('bg-green-600', 'hover:bg-green-700'); - } else { - lobbyButton.classList.remove('bg-green-600', 'hover:bg-green-700'); - lobbyButton.classList.add('bg-blue-600', 'hover:bg-blue-700'); - } - - if (!this.isLobbyHighlighted) { - this.game.stop() - this.game = null - return - } - - if (this.game != null) { - return; - } const [terrainMap, clientIP] = await Promise.all([ this.terrainMap, this.ip @@ -223,6 +136,7 @@ async function getClientIP(timeoutMs: number = 1000): Promise { // Initialize the client when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { new Client().initialize(); + }); document.body.style.backgroundImage = `url(${backgroundImage})`; diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts new file mode 100644 index 000000000..f95d4df29 --- /dev/null +++ b/src/client/PublicLobby.ts @@ -0,0 +1,115 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, state} from 'lit/decorators.js'; +import {Lobby} from "../core/Schemas"; + +@customElement('public-lobby') +export class PublicLobby extends LitElement { + @state() private lobbies: Lobby[] = []; + @state() private isLobbyHighlighted: boolean = false; + private lobbiesInterval: number | null = null; + + static styles = css` + /* Add your styles here, based on your existing CSS */ + .lobby-button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + max-width: 20rem; + margin: 0 auto; + padding: 1.5rem 2rem; + background-color: #2563eb; + color: white; + font-weight: bold; + border-radius: 0.5rem; + transition: background-color 0.3s ease-in-out; + } + + .lobby-button:hover { + background-color: #1d4ed8; + } + + .lobby-button.highlighted { + background-color: #16a34a; + } + + .lobby-button.highlighted:hover { + background-color: #15803d; + } + + .lobby-name { font-size: 1.5rem; } + .lobby-timer { font-size: 1.25rem; } + .player-count { font-size: 1rem; } + `; + + connectedCallback() { + super.connectedCallback(); + this.fetchAndUpdateLobbies(); // Fetch immediately on start + this.lobbiesInterval = window.setInterval(() => this.fetchAndUpdateLobbies(), 1000); + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (this.lobbiesInterval !== null) { + clearInterval(this.lobbiesInterval); + this.lobbiesInterval = null; + } + } + + private async fetchAndUpdateLobbies(): Promise { + try { + const lobbies = await this.fetchLobbies(); + this.lobbies = lobbies; + } catch (error) { + console.error('Error fetching and updating lobbies:', error); + } + } + + async fetchLobbies(): Promise { + const url = '/lobbies'; + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + return data.lobbies; + } catch (error) { + console.error('Error fetching lobbies:', error); + throw error; + } + } + + render() { + if (this.lobbies.length === 0) { + return html``; + } + + const lobby = this.lobbies[0]; + const timeRemaining = Math.max(0, Math.floor(lobby.msUntilStart / 1000)); + + return html` + + ${this.lobbies.length > 1 ? html` +
Next Game: ${this.lobbies[1].id.substring(0, 3)}
+ ` : ''} + `; + } + + private joinLobby(lobby: Lobby) { + this.isLobbyHighlighted = !this.isLobbyHighlighted; + this.dispatchEvent(new CustomEvent('join-lobby', { + detail: lobby, + bubbles: true, + composed: true + })); + } +} \ No newline at end of file diff --git a/src/client/index.html b/src/client/index.html index 0a3d9335e..6a64c6651 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -57,15 +57,7 @@

Public Games

-
- -
+