Implement HTML-based lobby system with player list management, host controls, and real-time updates. Add JavaScript-WASM communication for lobby state changes and game start triggers.
This commit is contained in:
@@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// In WASM: Menü und Leaderboard werden in HTML angezeigt
|
// In WASM: Menü, Lobby und Leaderboard werden in HTML angezeigt
|
||||||
// Lobby wird aber normal gezeichnet (für Co-op Warteraum)
|
// Canvas zeigt nur das eigentliche Spiel
|
||||||
|
|
||||||
func (g *Game) drawMenu(screen *ebiten.Image) {
|
func (g *Game) drawMenu(screen *ebiten.Image) {
|
||||||
// Schwarzer Hintergrund - HTML-Menü ist darüber
|
// Schwarzer Hintergrund - HTML-Menü ist darüber
|
||||||
@@ -18,8 +18,8 @@ func (g *Game) drawMenu(screen *ebiten.Image) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) drawLobby(screen *ebiten.Image) {
|
func (g *Game) drawLobby(screen *ebiten.Image) {
|
||||||
// Lobby wird normal gezeichnet (für Co-op Warteraum)
|
// Schwarzer Hintergrund - HTML-Lobby ist darüber
|
||||||
g.DrawLobby(screen)
|
screen.Fill(color.RGBA{0, 0, 0, 255})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) drawLeaderboard(screen *ebiten.Image) {
|
func (g *Game) drawLeaderboard(screen *ebiten.Image) {
|
||||||
|
|||||||
@@ -355,6 +355,16 @@ func (g *Game) updateLobby() {
|
|||||||
g.gameState = game.GameState{Players: make(map[string]game.PlayerState)}
|
g.gameState = game.GameState{Players: make(map[string]game.PlayerState)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lobby State Change Detection (für HTML-Updates)
|
||||||
|
g.stateMutex.Lock()
|
||||||
|
currentPlayerCount := len(g.gameState.Players)
|
||||||
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
|
if currentPlayerCount != g.lastPlayerCount {
|
||||||
|
g.lastPlayerCount = currentPlayerCount
|
||||||
|
g.sendLobbyUpdateToJS()
|
||||||
|
}
|
||||||
|
|
||||||
// Spiel wurde gestartet?
|
// Spiel wurde gestartet?
|
||||||
if g.gameState.Status == "COUNTDOWN" || g.gameState.Status == "RUNNING" {
|
if g.gameState.Status == "COUNTDOWN" || g.gameState.Status == "RUNNING" {
|
||||||
g.appState = StateGame
|
g.appState = StateGame
|
||||||
|
|||||||
@@ -80,11 +80,19 @@ func (g *Game) setupJavaScriptBridge() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// startGameFromLobby_WASM() - Host startet Spiel aus HTML Lobby
|
||||||
|
startGameFromLobbyFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||||
|
log.Println("🎮 Host startet Spiel aus HTML Lobby")
|
||||||
|
go g.sendStartRequest()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Im globalen Scope registrieren
|
// Im globalen Scope registrieren
|
||||||
js.Global().Set("startGame", startGameFunc)
|
js.Global().Set("startGame", startGameFunc)
|
||||||
js.Global().Set("requestLeaderboard", requestLeaderboardFunc)
|
js.Global().Set("requestLeaderboard", requestLeaderboardFunc)
|
||||||
js.Global().Set("setMusicVolume", setMusicVolumeFunc)
|
js.Global().Set("setMusicVolume", setMusicVolumeFunc)
|
||||||
js.Global().Set("setSFXVolume", setSFXVolumeFunc)
|
js.Global().Set("setSFXVolume", setSFXVolumeFunc)
|
||||||
|
js.Global().Set("startGameFromLobby_WASM", startGameFromLobbyFunc)
|
||||||
|
|
||||||
log.Println("✅ JavaScript Bridge registriert")
|
log.Println("✅ JavaScript Bridge registriert")
|
||||||
}
|
}
|
||||||
@@ -117,3 +125,30 @@ func (g *Game) sendGameOverToJS(score int) {
|
|||||||
log.Printf("💀 Game Over an JavaScript gesendet: Score=%d", score)
|
log.Printf("💀 Game Over an JavaScript gesendet: Score=%d", score)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lobby Player List an JavaScript senden
|
||||||
|
func (g *Game) sendLobbyPlayersToJS() {
|
||||||
|
g.stateMutex.Lock()
|
||||||
|
players := make([]interface{}, 0, len(g.gameState.Players))
|
||||||
|
hostID := g.gameState.HostID
|
||||||
|
|
||||||
|
for id, p := range g.gameState.Players {
|
||||||
|
name := p.Name
|
||||||
|
if name == "" {
|
||||||
|
name = id
|
||||||
|
}
|
||||||
|
isHost := (id == hostID)
|
||||||
|
players = append(players, map[string]interface{}{
|
||||||
|
"name": name,
|
||||||
|
"is_host": isHost,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
|
// JavaScript-Funktion aufrufen
|
||||||
|
if updateFunc := js.Global().Get("updateLobbyPlayers"); !updateFunc.IsUndefined() {
|
||||||
|
jsPlayers := js.ValueOf(players)
|
||||||
|
updateFunc.Invoke(jsPlayers)
|
||||||
|
log.Printf("👥 Lobby-Spieler an JavaScript gesendet: %d Spieler", len(players))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -91,10 +91,17 @@ function startSoloGame() {
|
|||||||
localStorage.setItem('escape_game_mode', 'solo');
|
localStorage.setItem('escape_game_mode', 'solo');
|
||||||
localStorage.setItem('escape_room_id', '');
|
localStorage.setItem('escape_room_id', '');
|
||||||
|
|
||||||
// Hide menu and show canvas
|
// Hide ALL screens including main menu
|
||||||
hideAllScreens();
|
hideAllScreens();
|
||||||
|
document.getElementById('menu').style.display = 'none';
|
||||||
gameStarted = true;
|
gameStarted = true;
|
||||||
|
|
||||||
|
// Canvas sichtbar machen
|
||||||
|
const canvas = document.querySelector('canvas');
|
||||||
|
if (canvas) {
|
||||||
|
canvas.classList.add('game-active');
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger WASM game start
|
// Trigger WASM game start
|
||||||
if (window.startGame) {
|
if (window.startGame) {
|
||||||
window.startGame('solo', playerName, '');
|
window.startGame('solo', playerName, '');
|
||||||
@@ -120,17 +127,17 @@ function createRoom() {
|
|||||||
localStorage.setItem('escape_team_name', teamName);
|
localStorage.setItem('escape_team_name', teamName);
|
||||||
localStorage.setItem('escape_is_host', 'true');
|
localStorage.setItem('escape_is_host', 'true');
|
||||||
|
|
||||||
// Hide menu and show canvas (Lobby wird vom WASM gezeichnet)
|
// Verstecke ALLE Screens inkl. Hauptmenü
|
||||||
hideAllScreens();
|
hideAllScreens();
|
||||||
gameStarted = true;
|
document.getElementById('menu').style.display = 'none';
|
||||||
|
|
||||||
// Canvas sichtbar machen für Lobby
|
// Zeige HTML Lobby Screen
|
||||||
const canvas = document.querySelector('canvas');
|
document.getElementById('lobbyScreen').classList.remove('hidden');
|
||||||
if (canvas) {
|
document.getElementById('lobbyRoomCode').textContent = roomID;
|
||||||
canvas.classList.add('game-active');
|
document.getElementById('lobbyHostControls').classList.remove('hidden');
|
||||||
}
|
document.getElementById('lobbyStatus').textContent = 'Du bist Host - starte wenn bereit!';
|
||||||
|
|
||||||
// Trigger WASM game start
|
// Trigger WASM game start (im Hintergrund)
|
||||||
if (window.startGame) {
|
if (window.startGame) {
|
||||||
window.startGame('coop', playerName, roomID, teamName, true);
|
window.startGame('coop', playerName, roomID, teamName, true);
|
||||||
}
|
}
|
||||||
@@ -160,17 +167,17 @@ function joinRoom() {
|
|||||||
localStorage.setItem('escape_team_name', teamName);
|
localStorage.setItem('escape_team_name', teamName);
|
||||||
localStorage.setItem('escape_is_host', 'false');
|
localStorage.setItem('escape_is_host', 'false');
|
||||||
|
|
||||||
// Hide menu and show canvas
|
// Verstecke ALLE Screens inkl. Hauptmenü
|
||||||
hideAllScreens();
|
hideAllScreens();
|
||||||
gameStarted = true;
|
document.getElementById('menu').style.display = 'none';
|
||||||
|
|
||||||
// Canvas sichtbar machen für Lobby
|
// Zeige HTML Lobby Screen
|
||||||
const canvas = document.querySelector('canvas');
|
document.getElementById('lobbyScreen').classList.remove('hidden');
|
||||||
if (canvas) {
|
document.getElementById('lobbyRoomCode').textContent = roomID;
|
||||||
canvas.classList.add('game-active');
|
document.getElementById('lobbyHostControls').classList.add('hidden');
|
||||||
}
|
document.getElementById('lobbyStatus').textContent = 'Warte auf Host...';
|
||||||
|
|
||||||
// Trigger WASM game start
|
// Trigger WASM game start (im Hintergrund)
|
||||||
if (window.startGame) {
|
if (window.startGame) {
|
||||||
window.startGame('coop', playerName, roomID, teamName, false);
|
window.startGame('coop', playerName, roomID, teamName, false);
|
||||||
}
|
}
|
||||||
@@ -178,6 +185,47 @@ function joinRoom() {
|
|||||||
console.log('🎮 Joining room:', roomID);
|
console.log('🎮 Joining room:', roomID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lobby Functions
|
||||||
|
function startGameFromLobby() {
|
||||||
|
// Host startet das Spiel
|
||||||
|
hideAllScreens();
|
||||||
|
document.getElementById('menu').style.display = 'none';
|
||||||
|
gameStarted = true;
|
||||||
|
|
||||||
|
// Canvas sichtbar machen
|
||||||
|
const canvas = document.querySelector('canvas');
|
||||||
|
if (canvas) {
|
||||||
|
canvas.classList.add('game-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal an WASM senden dass Spiel starten soll
|
||||||
|
if (window.startGameFromLobby_WASM) {
|
||||||
|
window.startGameFromLobby_WASM();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎮 Host started game from lobby');
|
||||||
|
}
|
||||||
|
|
||||||
|
function leaveLobby() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Lobby Player List (called by WASM)
|
||||||
|
function updateLobbyPlayers(players) {
|
||||||
|
const list = document.getElementById('lobbyPlayerList');
|
||||||
|
if (!players || players.length === 0) {
|
||||||
|
list.innerHTML = '<div style="color: #888;">Warte auf Spieler...</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.innerHTML = players.map((player, index) => {
|
||||||
|
const hostBadge = player.is_host ? ' <span style="color:#ffcc00;">[HOST]</span>' : '';
|
||||||
|
return `<div style="padding:5px 0; color:#fff;">• ${player.name}${hostBadge}</div>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
console.log('👥 Lobby players updated:', players.length);
|
||||||
|
}
|
||||||
|
|
||||||
function loadLeaderboard() {
|
function loadLeaderboard() {
|
||||||
const list = document.getElementById('leaderboardList');
|
const list = document.getElementById('leaderboardList');
|
||||||
list.innerHTML = '<div style="text-align:center; padding:20px;">Lädt Leaderboard...</div>';
|
list.innerHTML = '<div style="text-align:center; padding:20px;">Lädt Leaderboard...</div>';
|
||||||
@@ -197,16 +245,16 @@ function loadLeaderboard() {
|
|||||||
|
|
||||||
// Called by WASM to update leaderboard
|
// Called by WASM to update leaderboard
|
||||||
function updateLeaderboard(entries) {
|
function updateLeaderboard(entries) {
|
||||||
// Update main leaderboard page
|
// Update ALL leaderboard displays
|
||||||
const list = document.getElementById('leaderboardList');
|
const list = document.getElementById('leaderboardList');
|
||||||
|
|
||||||
// Update start screen leaderboard
|
|
||||||
const startList = document.getElementById('startLeaderboardList');
|
const startList = document.getElementById('startLeaderboardList');
|
||||||
|
const gameOverList = document.getElementById('gameOverLeaderboardList');
|
||||||
|
|
||||||
if (!entries || entries.length === 0) {
|
if (!entries || entries.length === 0) {
|
||||||
const emptyMsg = '<div style="text-align:center; padding:20px; color:#888;">Noch keine Einträge</div>';
|
const emptyMsg = '<div style="text-align:center; padding:20px; color:#888;">Noch keine Einträge</div>';
|
||||||
if (list) list.innerHTML = emptyMsg;
|
if (list) list.innerHTML = emptyMsg;
|
||||||
if (startList) startList.innerHTML = emptyMsg;
|
if (startList) startList.innerHTML = emptyMsg;
|
||||||
|
if (gameOverList) gameOverList.innerHTML = emptyMsg;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +271,7 @@ function updateLeaderboard(entries) {
|
|||||||
|
|
||||||
if (list) list.innerHTML = html;
|
if (list) list.innerHTML = html;
|
||||||
if (startList) startList.innerHTML = html;
|
if (startList) startList.innerHTML = html;
|
||||||
|
if (gameOverList) gameOverList.innerHTML = html;
|
||||||
|
|
||||||
console.log('📊 Leaderboard updated with', entries.length, 'entries');
|
console.log('📊 Leaderboard updated with', entries.length, 'entries');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
<title>Escape From Teacher</title>
|
<title>Escape From Teacher</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<style>
|
||||||
|
/* DEBUG: Force menu visibility */
|
||||||
|
#menu {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
background: rgba(0, 0, 0, 0.95) !important;
|
||||||
|
z-index: 10000 !important;
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -115,6 +128,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- LOBBY SCREEN (CO-OP WAITING ROOM) -->
|
||||||
|
<div id="lobbyScreen" class="overlay-screen hidden">
|
||||||
|
<div class="center-box">
|
||||||
|
<h1>LOBBY</h1>
|
||||||
|
|
||||||
|
<div style="margin: 20px 0;">
|
||||||
|
<p style="font-size: 14px; color: #aaa;">Raum-Code:</p>
|
||||||
|
<div style="background: rgba(0,0,0,0.6); border: 4px solid #ffcc00; padding: 20px; font-size: 32px; color: #ffcc00; letter-spacing: 4px;">
|
||||||
|
<span id="lobbyRoomCode">XXXXX</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin: 20px 0; width: 100%; max-width: 400px;">
|
||||||
|
<p style="font-size: 14px; color: #aaa; margin-bottom: 10px;">Spieler im Raum:</p>
|
||||||
|
<div id="lobbyPlayerList" style="background: rgba(0,0,0,0.5); border: 2px solid #666; padding: 15px; min-height: 100px; font-family: sans-serif; font-size: 14px;">
|
||||||
|
<div style="color: #888;">Warte auf Spieler...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="lobbyHostControls" class="hidden" style="margin: 20px 0;">
|
||||||
|
<button class="big-btn" onclick="startGameFromLobby()" style="background: #00cc00;">SPIEL STARTEN</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="lobbyStatus" style="font-size: 12px; color: #ffcc00; margin: 20px 0;">Warte auf Host...</p>
|
||||||
|
|
||||||
|
<button class="back-btn" onclick="leaveLobby()">← LOBBY VERLASSEN</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- GAME OVER SCREEN -->
|
<!-- GAME OVER SCREEN -->
|
||||||
<div id="gameOverScreen" class="overlay-screen hidden">
|
<div id="gameOverScreen" class="overlay-screen hidden">
|
||||||
<div class="center-box">
|
<div class="center-box">
|
||||||
@@ -124,7 +166,7 @@
|
|||||||
Dein Score: <span id="finalScore" style="color:yellow; font-size: 24px;">0</span>
|
Dein Score: <span id="finalScore" style="color:yellow; font-size: 24px;">0</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div id="leaderboardList" class="leaderboard-box" style="margin: 20px 0;">
|
<div id="gameOverLeaderboardList" class="leaderboard-box" style="margin: 20px 0;">
|
||||||
Lade Leaderboard...
|
Lade Leaderboard...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user