Private
Public Access
1
0
Files
EscapeFromTeacher/cmd/client/wasm_bridge.go
Sebastian Unterschütz b7043b017f
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m0s
refine asset scaling and labeling: adjust scaling logic, introduce label display, and update CSS and animation styles
2026-04-22 20:49:38 +02:00

251 lines
7.4 KiB
Go

//go:build js && wasm
// +build js,wasm
package main
import (
"encoding/base64"
"log"
"syscall/js"
"time"
)
// notifyWasmReady signalisiert JavaScript dass WASM vollständig geladen ist
func (g *Game) notifyWasmReady() {
if readyFunc := js.Global().Get("onWasmReady"); !readyFunc.IsUndefined() {
readyFunc.Invoke()
log.Println("✅ WASM Ready-Signal an JavaScript gesendet")
}
}
// setupJavaScriptBridge registriert JavaScript-Funktionen für HTML-Menü Interaktion
func (g *Game) setupJavaScriptBridge() {
// startGame(mode, playerName, roomID, teamName, isHost)
startGameFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) < 2 {
log.Println("❌ startGame: Nicht genug Argumente")
return nil
}
mode := args[0].String() // "solo" oder "coop"
playerName := args[1].String() // Spielername
// Spieler-Daten setzen
g.playerName = playerName
g.gameMode = mode
g.savePlayerName(playerName)
if mode == "solo" {
// Solo Mode - Jetzt standardmäßig OFFLINE
g.isHost = true
g.startOfflineGame()
log.Printf("🎮 Solo-Spiel OFFLINE gestartet: %s", playerName)
return nil
} else if mode == "coop" && len(args) >= 5 {
// Co-op Mode - in die Lobby
roomID := args[2].String()
teamName := args[3].String()
isHost := args[4].Bool()
g.roomID = roomID
g.teamName = teamName
g.isHost = isHost
g.appState = StateLobby
log.Printf("🎮 Co-op-Lobby erstellt: %s (Room: %s, Team: %s, Host: %v)", playerName, roomID, teamName, isHost)
}
// Verbindung starten
go g.connectAndStart()
return nil
})
// requestLeaderboard()
requestLeaderboardFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
log.Println("📊 Leaderboard angefordert")
go g.requestLeaderboard()
return nil
})
// setMusicVolume(volume)
setMusicVolumeFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) > 0 {
volume := args[0].Float()
g.audio.SetMusicVolume(volume)
log.Printf("🎵 Musik-Lautstärke: %.0f%%", volume*100)
}
return nil
})
// setSFXVolume(volume)
setSFXVolumeFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) > 0 {
volume := args[0].Float()
g.audio.SetSFXVolume(volume)
log.Printf("🔊 SFX-Lautstärke: %.0f%%", volume*100)
}
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
})
// setTeamName_WASM(teamName) - Host setzt Team-Namen
setTeamNameFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) > 0 {
teamName := args[0].String()
g.teamName = teamName
log.Printf("🏷️ Team-Name gesetzt: '%s'", teamName)
go g.sendSetTeamNameInput(teamName)
}
return nil
})
// togglePresentationMode_WASM()
togglePresFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
log.Println("⌨️ F1: Toggle Presentation Mode")
// Simulate F1 key press in WASM
if g.appState == StatePresentation {
g.appState = StateMenu
g.disconnectFromServer()
// JS Callback to hide screen
js.Global().Call("showMainMenu")
} else {
g.appState = StatePresentation
g.presAssets = nil
g.presQuoteTime = time.Now()
g.gameMode = "coop"
g.isHost = true
g.roomID = "PRES" + generateRoomCode()
g.playerName = "PRESENTATION"
go g.connectAndStart()
joinURL := "https://escape-from-school.de/?room=" + g.roomID
g.presQRCode = generateQRCode(joinURL)
g.notifyPresentationStarted_Platform(g.roomID)
}
return nil
})
// Im globalen Scope registrieren
js.Global().Set("startGame", startGameFunc)
js.Global().Set("requestLeaderboard", requestLeaderboardFunc)
js.Global().Set("setMusicVolume", setMusicVolumeFunc)
js.Global().Set("setSFXVolume", setSFXVolumeFunc)
js.Global().Set("startGameFromLobby_WASM", startGameFromLobbyFunc)
js.Global().Set("setTeamName_WASM", setTeamNameFunc)
js.Global().Set("togglePresentationMode_WASM", togglePresFunc)
log.Println("✅ JavaScript Bridge registriert")
log.Printf("🔍 window.startGame defined: %v", !js.Global().Get("startGame").IsUndefined())
log.Printf("🔍 window.startGameFromLobby_WASM defined: %v", !js.Global().Get("startGameFromLobby_WASM").IsUndefined())
}
// Leaderboard an JavaScript senden
func (g *Game) sendLeaderboardToJS() {
g.leaderboardMutex.Lock()
entries := make([]interface{}, len(g.leaderboard))
for i, entry := range g.leaderboard {
entries[i] = map[string]interface{}{
"player_name": entry.PlayerName,
"score": entry.Score,
"player_code": entry.PlayerCode,
}
}
g.leaderboardMutex.Unlock()
// JavaScript-Funktion aufrufen
if updateFunc := js.Global().Get("updateLeaderboard"); !updateFunc.IsUndefined() {
jsEntries := js.ValueOf(entries)
updateFunc.Invoke(jsEntries)
log.Printf("📤 Leaderboard an JavaScript gesendet: %d Einträge", len(entries))
}
}
// Game Over Screen an JavaScript senden
func (g *Game) sendGameOverToJS(score int) {
if gameOverFunc := js.Global().Get("showGameOver"); !gameOverFunc.IsUndefined() {
gameOverFunc.Invoke(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
teamName := g.gameState.TeamName
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))
}
// Team-Name an JavaScript senden
if updateTeamFunc := js.Global().Get("updateLobbyTeamName"); !updateTeamFunc.IsUndefined() {
myID := g.getMyPlayerID()
isHost := (myID == hostID)
updateTeamFunc.Invoke(teamName, isHost)
log.Printf("🏷️ Team-Name an JavaScript gesendet: '%s' (isHost: %v)", teamName, isHost)
}
}
// notifyPresentationStarted benachrichtigt JS dass der Presi-Modus aktiv ist
func (g *Game) notifyPresentationStarted(roomID string, qrCode []byte) {
if presFunc := js.Global().Get("onPresentationStarted"); !presFunc.IsUndefined() {
// Konvertiere QR Code zu Base64 für JS
qrBase64 := ""
if qrCode != nil {
qrBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(qrCode)
}
presFunc.Invoke(roomID, qrBase64)
log.Printf("📺 Präsentationsmodus an JS signalisiert: %s", roomID)
}
}
// updatePresentationState sendet den aktuellen Status (Spieler, Emotes) an JS
func (g *Game) updatePresentationState() {
if updateFunc := js.Global().Get("onPresentationUpdate"); !updateFunc.IsUndefined() {
g.stateMutex.Lock()
players := g.gameState.Players
g.stateMutex.Unlock()
// Vereinfachte Spieler-Daten für JS
jsPlayers := make([]interface{}, 0)
for _, p := range players {
if !p.IsAlive || p.Name == "PRESENTATION" {
continue
}
jsPlayers = append(jsPlayers, map[string]interface{}{
"id": p.ID,
"x": p.X,
"y": p.Y,
"vy": p.VY,
"state": p.State,
})
}
updateFunc.Invoke(jsPlayers)
}
}