Private
Public Access
1
0

add presentation mode enhancements: refine visuals, integrate HTML-based interface for presentation mode, align assets display and player states, and handle real-time JS callbacks
Some checks failed
Dynamic Branch Deploy / build-and-deploy (push) Failing after 1m36s

This commit is contained in:
Sebastian Unterschütz
2026-04-22 20:28:16 +02:00
parent c1fb3bcef0
commit d5c1e2ec82
11 changed files with 526 additions and 115 deletions

View File

@@ -6,14 +6,12 @@ import (
"image"
_ "image/png"
"image/color"
"math"
"math/rand"
"strings"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/hajimehoshi/ebiten/v2/vector"
"github.com/skip2/go-qrcode"
"golang.org/x/image/font/basicfont"
@@ -88,117 +86,8 @@ func (g *Game) updatePresentation() {
g.presAssets = newAssets
}
// drawPresentation zeichnet den Präsentationsmodus.
func (g *Game) drawPresentation(screen *ebiten.Image) {
// Hintergrund: Retro Dunkelblau
screen.Fill(color.RGBA{10, 15, 30, 255})
// Animierte Scanlines / Raster-Effekt (Retro Style)
for i := 0; i < ScreenHeight; i += 4 {
vector.DrawFilledRect(screen, 0, float32(i), float32(ScreenWidth), 1, color.RGBA{0, 0, 0, 40}, false)
}
// Überschrift
text.Draw(screen, "PRESENTATION MODE", basicfont.Face7x13, ScreenWidth/2-80, 50, color.RGBA{255, 255, 0, 255})
vector.StrokeLine(screen, ScreenWidth/2-90, 60, ScreenWidth/2+90, 60, 2, color.RGBA{255, 255, 0, 255}, false)
// Zitat groß in der Mitte
if g.presQuote.Text != "" {
quoteMsg := fmt.Sprintf("\"%s\"", g.presQuote.Text)
authorMsg := fmt.Sprintf("- %s", g.presQuote.Author)
// Einfaches Word-Wrap (sehr rudimentär)
drawWrappedText(screen, quoteMsg, ScreenWidth/2, ScreenHeight/2-20, 600, color.White)
text.Draw(screen, authorMsg, basicfont.Face7x13, ScreenWidth/2+100, ScreenHeight/2+50, color.RGBA{200, 200, 200, 255})
}
// Assets laufen unten durch
for _, a := range g.presAssets {
def, ok := g.world.Manifest.Assets[a.AssetID]
if !ok { continue }
img, ok := g.assetsImages[a.AssetID]
if !ok { continue }
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(a.Scale, a.Scale)
op.GeoM.Translate(a.X, a.Y)
// Leichtes Pulsieren/Animation
bob := math.Sin(float64(time.Now().UnixMilli())/200.0) * 5.0
op.GeoM.Translate(0, bob)
screen.DrawImage(img, op)
// Name des Assets drunter schreiben
text.Draw(screen, def.ID, basicfont.Face7x13, int(a.X), int(a.Y+80), color.RGBA{100, 200, 255, 150})
}
// Draw connected players (no names)
g.stateMutex.Lock()
for _, p := range g.gameState.Players {
if !p.IsAlive || p.Name == "PRESENTATION" {
continue // Skip Host and dead players
}
// Map player X/Y to screen
playerX := p.X
playerY := p.Y
// Keep players somewhat in bounds if they walk too far
if playerX > ScreenWidth {
playerX = math.Mod(playerX, ScreenWidth)
} else if playerX < 0 {
playerX = ScreenWidth - math.Mod(-playerX, ScreenWidth)
}
// Draw simple player sprite
img, ok := g.assetsImages["player"]
if ok {
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(playerX, playerY)
screen.DrawImage(img, op)
} else {
// Fallback rect
vector.DrawFilledRect(screen, float32(playerX), float32(playerY), 40, 60, color.RGBA{0, 255, 0, 255}, false)
}
// Draw Emote if active
if p.State != "" && strings.HasPrefix(p.State, "EMOTE_") {
emoteStr := p.State[6:] // e.g. EMOTE_1 -> "1"
emoteMap := map[string]string{
"1": "❤️",
"2": "😂",
"3": "😡",
"4": "👍",
}
if emoji, ok := emoteMap[emoteStr]; ok {
text.Draw(screen, emoji, basicfont.Face7x13, int(playerX+10), int(playerY-10), color.White)
}
}
}
g.stateMutex.Unlock()
// Draw QR Code
if g.presQRCode != nil {
qrSize := 150.0
qrW, _ := g.presQRCode.Size()
scale := float64(qrSize) / float64(qrW)
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(scale, scale)
op.GeoM.Translate(20, 20)
screen.DrawImage(g.presQRCode, op)
// Instruction
text.Draw(screen, "SCANNEN ZUM MITMACHEN!", basicfont.Face7x13, 20, 190, color.RGBA{255, 255, 0, 255})
}
// Hotkey Info
text.Draw(screen, "DRÜCKE [F1] ZUM BEENDEN", basicfont.Face7x13, ScreenWidth-250, ScreenHeight-30, color.RGBA{255, 255, 255, 100})
}
// drawWrappedText zeichnet Text mit automatischem Zeilenumbruch.
func drawWrappedText(screen *ebiten.Image, str string, x, y, maxWidth int, col color.Color) {
// DrawWrappedText zeichnet Text mit automatischem Zeilenumbruch.
func (g *Game) DrawWrappedText(screen *ebiten.Image, str string, x, y, maxWidth int, col color.Color) {
words := strings.Split(str, " ")
line := ""
currY := y