diff --git a/src/client/Client.ts b/src/client/Client.ts index fd5059a8a..e931bd712 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -12,14 +12,13 @@ import './UsernameInput'; import './styles.css'; import {UsernameInput} from "./UsernameInput"; +import {SinglePlayerModal} from "./SinglePlayerModal"; const usernameKey: string = 'username'; class Client { - - private terrainMap: Promise private game: ClientGame @@ -44,10 +43,23 @@ class Client { this.terrainMap = loadTerrainMap() this.ip = getClientIP() document.addEventListener('join-lobby', this.handleJoinLobby.bind(this)); + document.addEventListener('leave-lobby', this.handleLeaveLobby.bind(this)); + document.addEventListener('single-player', this.handleSinglePlayer.bind(this)); + + + const singlePlayerButton = document.getElementById('single-player'); + const modal = document.querySelector('single-player-modal') as SinglePlayerModal; + + if (singlePlayerButton && modal instanceof SinglePlayerModal) { + singlePlayerButton.addEventListener('click', () => { + modal.open(); + }); + } + } private async handleJoinLobby(event: CustomEvent) { - const lobby = event.detail + const lobby = event.detail.lobby console.log(`joining lobby ${lobby.id}`) const [terrainMap, clientIP] = await Promise.all([ this.terrainMap, @@ -70,8 +82,16 @@ class Client { g.stop(); }); } -} + private async handleLeaveLobby(event: CustomEvent) { + this.game.stop() + this.game = null + } + + private async handleSinglePlayer(event: CustomEvent) { + alert('coming soon') + } +} async function getClientIP(timeoutMs: number = 1000): Promise { const controller = new AbortController(); diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index f95d4df29..ab26c39e6 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -8,6 +8,8 @@ export class PublicLobby extends LitElement { @state() private isLobbyHighlighted: boolean = false; private lobbiesInterval: number | null = null; + private currLobby: Lobby = null + static styles = css` /* Add your styles here, based on your existing CSS */ .lobby-button { @@ -91,25 +93,32 @@ export class PublicLobby extends LitElement { return html` - ${this.lobbies.length > 1 ? html` -
Next Game: ${this.lobbies[1].id.substring(0, 3)}
- ` : ''} `; } - private joinLobby(lobby: Lobby) { + private lobbyClicked(lobby: Lobby) { this.isLobbyHighlighted = !this.isLobbyHighlighted; - this.dispatchEvent(new CustomEvent('join-lobby', { - detail: lobby, - bubbles: true, - composed: true - })); + if (this.currLobby == null) { + this.currLobby = lobby + this.dispatchEvent(new CustomEvent('join-lobby', { + detail: {lobby: lobby}, + bubbles: true, + composed: true + })); + } else { + this.dispatchEvent(new CustomEvent('leave-lobby', { + detail: {lobby: this.currLobby}, + bubbles: true, + composed: true + })); + this.currLobby = null + } } } \ No newline at end of file diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts new file mode 100644 index 000000000..61576eb28 --- /dev/null +++ b/src/client/SinglePlayerModal.ts @@ -0,0 +1,93 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; + + +@customElement('single-player-modal') +export class SinglePlayerModal extends LitElement { + @state() private isModalOpen = false; + + static styles = css` +.modal-overlay { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); +} + +.modal-content { + background-color: white; + margin: 15% auto; + padding: 20px; + border-radius: 8px; + width: 80%; + max-width: 500px; + text-align: center; /* Center the content inside the modal */ +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; + cursor: pointer; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} + +button { + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + background-color: #007bff; /* Changed to blue */ + color: white; + border: none; + border-radius: 4px; + transition: background-color 0.3s; + display: inline-block; /* Ensures the button takes only necessary width */ + margin-top: 20px; /* Adds some space above the button */ +} + +button:hover { + background-color: #0056b3; /* Darker blue for hover state */ +} + `; + + render() { + return html` + + `; + } + + public open() { + this.isModalOpen = true; + } + + public close() { + this.isModalOpen = false; + } + + private startGame() { + console.log('Starting single player game...'); + this.dispatchEvent(new CustomEvent('single-player', { + detail: {todo: "TODO"}, + bubbles: true, + composed: true + })); + this.close(); + } +} \ No newline at end of file diff --git a/src/client/index.html b/src/client/index.html index 4a6b8605a..d0ec86595 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -57,7 +57,6 @@ -
@@ -68,6 +67,7 @@
+ diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index f6c2a1193..0b211f167 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -9,10 +9,10 @@ export const devConfig = new class extends DefaultConfig { return 40 } gameCreationRate(): number { - return 2 * 1000 + return 20 * 1000 } lobbyLifetime(): number { - return 2 * 1000 + return 20 * 1000 } turnIntervalMs(): number { return 100