update workflows and game logic: add CERT_ISSUER support, enhance offline mode with countdown, and adjust default audio settings
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 1m50s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 1m50s
This commit is contained in:
7
.github/workflows/deploy.yaml
vendored
7
.github/workflows/deploy.yaml
vendored
@@ -35,11 +35,13 @@ jobs:
|
|||||||
APP_URL="${{ env.BASE_DOMAIN }}"
|
APP_URL="${{ env.BASE_DOMAIN }}"
|
||||||
TARGET_NS="${REPO_LOWER}"
|
TARGET_NS="${REPO_LOWER}"
|
||||||
BUILD_MODE="main"
|
BUILD_MODE="main"
|
||||||
|
CERT_ISSUER="letsencrypt-prod"
|
||||||
echo "Mode: PRODUCTION (Root Domain)"
|
echo "Mode: PRODUCTION (Root Domain)"
|
||||||
else
|
else
|
||||||
APP_URL="${REPO_LOWER}-${BRANCH_LOWER}.${{ env.BASE_DOMAIN }}"
|
APP_URL="${REPO_LOWER}-${BRANCH_LOWER}.${{ env.BASE_DOMAIN }}"
|
||||||
TARGET_NS="${REPO_LOWER}-${BRANCH_LOWER}"
|
TARGET_NS="${REPO_LOWER}-${BRANCH_LOWER}"
|
||||||
BUILD_MODE="dev"
|
BUILD_MODE="dev"
|
||||||
|
CERT_ISSUER="letsencrypt-prod"
|
||||||
echo "Mode: DEVELOPMENT (Subdomain)"
|
echo "Mode: DEVELOPMENT (Subdomain)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ jobs:
|
|||||||
echo "DEBUG: URL: $APP_URL"
|
echo "DEBUG: URL: $APP_URL"
|
||||||
echo "DEBUG: Branch-Tag: $BRANCH_TAG"
|
echo "DEBUG: Branch-Tag: $BRANCH_TAG"
|
||||||
echo "DEBUG: Build-Mode: $BUILD_MODE"
|
echo "DEBUG: Build-Mode: $BUILD_MODE"
|
||||||
|
echo "DEBUG: Cert-Issuer: $CERT_ISSUER"
|
||||||
|
|
||||||
# In Gitea Actions Environment schreiben
|
# In Gitea Actions Environment schreiben
|
||||||
echo "FULL_IMAGE_PATH=$FULL_IMAGE_PATH" >> $GITHUB_ENV
|
echo "FULL_IMAGE_PATH=$FULL_IMAGE_PATH" >> $GITHUB_ENV
|
||||||
@@ -62,6 +65,7 @@ jobs:
|
|||||||
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
||||||
echo "BRANCH_TAG=$BRANCH_TAG" >> $GITHUB_ENV
|
echo "BRANCH_TAG=$BRANCH_TAG" >> $GITHUB_ENV
|
||||||
echo "BUILD_MODE=$BUILD_MODE" >> $GITHUB_ENV
|
echo "BUILD_MODE=$BUILD_MODE" >> $GITHUB_ENV
|
||||||
|
echo "CERT_ISSUER=$CERT_ISSUER" >> $GITHUB_ENV
|
||||||
|
|
||||||
# 3. Prüfen ob ein Image-Rebuild nötig ist
|
# 3. Prüfen ob ein Image-Rebuild nötig ist
|
||||||
- name: Detect Source Changes
|
- name: Detect Source Changes
|
||||||
@@ -170,6 +174,9 @@ jobs:
|
|||||||
|
|
||||||
# TARGET_NS überall ersetzen (z.B. für Middlewares oder explizite Namespaces)
|
# TARGET_NS überall ersetzen (z.B. für Middlewares oder explizite Namespaces)
|
||||||
find k8s/ -name "*.yaml" -exec sed -i "s|\${TARGET_NS}|${{ env.TARGET_NS }}|g" {} +
|
find k8s/ -name "*.yaml" -exec sed -i "s|\${TARGET_NS}|${{ env.TARGET_NS }}|g" {} +
|
||||||
|
|
||||||
|
# CERT_ISSUER in allen K8s-Manifesten ersetzen
|
||||||
|
find k8s/ -name "*.yaml" -exec sed -i "s|\${CERT_ISSUER}|${{ env.CERT_ISSUER }}|g" {} +
|
||||||
|
|
||||||
# Admin-Credentials Secret anlegen/aktualisieren (aus Gitea Secret)
|
# Admin-Credentials Secret anlegen/aktualisieren (aus Gitea Secret)
|
||||||
kubectl create secret generic admin-credentials \
|
kubectl create secret generic admin-credentials \
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ func NewAudioSystem() *AudioSystem {
|
|||||||
|
|
||||||
as := &AudioSystem{
|
as := &AudioSystem{
|
||||||
audioContext: ctx,
|
audioContext: ctx,
|
||||||
musicVolume: 0.3, // 30% Standard-Lautstärke
|
musicVolume: 0.7, // 70% Standard-Lautstärke
|
||||||
sfxVolume: 0.5, // 50% Standard-Lautstärke
|
sfxVolume: 0.3, // 30% Standard-Lautstärke
|
||||||
muted: false,
|
muted: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ func (g *Game) startOfflineGame() {
|
|||||||
g.connected = false // Explizit offline
|
g.connected = false // Explizit offline
|
||||||
g.appState = StateGame
|
g.appState = StateGame
|
||||||
|
|
||||||
// Initialen GameState lokal erstellen
|
// Initialen GameState lokal erstellen (mit Countdown)
|
||||||
g.stateMutex.Lock()
|
g.stateMutex.Lock()
|
||||||
g.gameState = game.GameState{
|
g.gameState = game.GameState{
|
||||||
Status: "RUNNING",
|
Status: "COUNTDOWN",
|
||||||
|
TimeLeft: 3,
|
||||||
RoomID: "offline_solo",
|
RoomID: "offline_solo",
|
||||||
Players: make(map[string]game.PlayerState),
|
Players: make(map[string]game.PlayerState),
|
||||||
WorldChunks: []game.ActiveChunk{{ChunkID: "start", X: 0}},
|
WorldChunks: []game.ActiveChunk{{ChunkID: "start", X: 0}},
|
||||||
@@ -30,7 +31,7 @@ func (g *Game) startOfflineGame() {
|
|||||||
CollectedCoins: make(map[string]bool),
|
CollectedCoins: make(map[string]bool),
|
||||||
CollectedPowerups: make(map[string]bool),
|
CollectedPowerups: make(map[string]bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lokalen Spieler hinzufügen
|
// Lokalen Spieler hinzufügen
|
||||||
g.gameState.Players[g.playerName] = game.PlayerState{
|
g.gameState.Players[g.playerName] = game.PlayerState{
|
||||||
ID: g.playerName,
|
ID: g.playerName,
|
||||||
@@ -48,28 +49,48 @@ func (g *Game) startOfflineGame() {
|
|||||||
log.Println("⚠️ Warnung: Keine Chunks in Library geladen!")
|
log.Println("⚠️ Warnung: Keine Chunks in Library geladen!")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.roundStartTime = time.Now()
|
// Startzeit für Countdown
|
||||||
|
g.roundStartTime = time.Now().Add(3 * time.Second)
|
||||||
g.predictedX = 100
|
g.predictedX = 100
|
||||||
g.predictedY = 200
|
g.predictedY = 200
|
||||||
g.currentSpeed = config.RunSpeed
|
g.currentSpeed = 0 // Stillstand während Countdown
|
||||||
|
|
||||||
g.audio.PlayMusic()
|
|
||||||
g.notifyGameStarted()
|
g.notifyGameStarted()
|
||||||
log.Println("🕹️ Offline-Modus gestartet")
|
log.Println("🕹️ Offline-Modus mit Countdown gestartet")
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateOfflineLoop simuliert die Server-Logik lokal
|
// updateOfflineLoop simuliert die Server-Logik lokal
|
||||||
func (g *Game) updateOfflineLoop() {
|
func (g *Game) updateOfflineLoop() {
|
||||||
if !g.isOffline || g.gameState.Status != "RUNNING" {
|
if !g.isOffline || g.gameState.Status == "GAMEOVER" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g.stateMutex.Lock()
|
g.stateMutex.Lock()
|
||||||
defer g.stateMutex.Unlock()
|
defer g.stateMutex.Unlock()
|
||||||
|
|
||||||
|
// 1. Status Logic (Countdown -> Running)
|
||||||
|
if g.gameState.Status == "COUNTDOWN" {
|
||||||
|
rem := time.Until(g.roundStartTime)
|
||||||
|
g.gameState.TimeLeft = int(rem.Seconds()) + 1
|
||||||
|
|
||||||
|
if rem <= 0 {
|
||||||
|
log.Println("🚀 Offline: GO!")
|
||||||
|
g.gameState.Status = "RUNNING"
|
||||||
|
g.gameState.TimeLeft = 0
|
||||||
|
g.audio.PlayMusic()
|
||||||
|
// Reset roundStartTime auf den tatsächlichen Spielstart für Schwierigkeits-Skalierung
|
||||||
|
g.roundStartTime = time.Now()
|
||||||
|
}
|
||||||
|
return // Während Countdown keine weitere Logik (kein Scrolling, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.gameState.Status != "RUNNING" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
elapsed := time.Since(g.roundStartTime).Seconds()
|
elapsed := time.Since(g.roundStartTime).Seconds()
|
||||||
|
|
||||||
// 1. Schwierigkeit & Speed
|
// 2. Schwierigkeit & Speed
|
||||||
g.gameState.DifficultyFactor = elapsed / config.MaxDifficultySeconds
|
g.gameState.DifficultyFactor = elapsed / config.MaxDifficultySeconds
|
||||||
if g.gameState.DifficultyFactor > 1.0 {
|
if g.gameState.DifficultyFactor > 1.0 {
|
||||||
g.gameState.DifficultyFactor = 1.0
|
g.gameState.DifficultyFactor = 1.0
|
||||||
@@ -79,10 +100,10 @@ func (g *Game) updateOfflineLoop() {
|
|||||||
g.gameState.CurrentSpeed = config.RunSpeed + speedIncrease
|
g.gameState.CurrentSpeed = config.RunSpeed + speedIncrease
|
||||||
g.currentSpeed = g.gameState.CurrentSpeed
|
g.currentSpeed = g.gameState.CurrentSpeed
|
||||||
|
|
||||||
// 2. Scrolling
|
// 3. Scrolling
|
||||||
g.gameState.ScrollX += g.currentSpeed
|
g.gameState.ScrollX += g.currentSpeed
|
||||||
|
|
||||||
// 3. Chunks nachladen
|
// 4. Chunks nachladen
|
||||||
mapEnd := 0.0
|
mapEnd := 0.0
|
||||||
for _, c := range g.gameState.WorldChunks {
|
for _, c := range g.gameState.WorldChunks {
|
||||||
chunkDef := g.world.ChunkLibrary[c.ChunkID]
|
chunkDef := g.world.ChunkLibrary[c.ChunkID]
|
||||||
@@ -96,7 +117,7 @@ func (g *Game) updateOfflineLoop() {
|
|||||||
g.spawnOfflineChunk(mapEnd)
|
g.spawnOfflineChunk(mapEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Entferne alte Chunks
|
// 5. Entferne alte Chunks
|
||||||
if len(g.gameState.WorldChunks) > 5 {
|
if len(g.gameState.WorldChunks) > 5 {
|
||||||
if g.gameState.WorldChunks[0].X < g.gameState.ScrollX-2000 {
|
if g.gameState.WorldChunks[0].X < g.gameState.ScrollX-2000 {
|
||||||
// Bereinige auch Moving Platforms des alten Chunks
|
// Bereinige auch Moving Platforms des alten Chunks
|
||||||
@@ -112,15 +133,15 @@ func (g *Game) updateOfflineLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Update Moving Platforms
|
// 6. Update Moving Platforms
|
||||||
g.updateOfflineMovingPlatforms()
|
g.updateOfflineMovingPlatforms()
|
||||||
|
|
||||||
// 6. Player State Update (Score, Powerups, Collisions)
|
// 7. Player State Update (Score, Powerups, Collisions)
|
||||||
p, ok := g.gameState.Players[g.playerName]
|
p, ok := g.gameState.Players[g.playerName]
|
||||||
if ok && p.IsAlive {
|
if ok && p.IsAlive {
|
||||||
// Basis-Score aus Distanz
|
// Basis-Score aus Distanz
|
||||||
p.Score = int(g.gameState.ScrollX / 10)
|
p.Score = int(g.gameState.ScrollX / 10)
|
||||||
|
|
||||||
// Synchronisiere Prediction-State zurück in GameState (für Rendering)
|
// Synchronisiere Prediction-State zurück in GameState (für Rendering)
|
||||||
p.X = g.predictedX
|
p.X = g.predictedX
|
||||||
p.Y = g.predictedY
|
p.Y = g.predictedY
|
||||||
@@ -128,7 +149,7 @@ func (g *Game) updateOfflineLoop() {
|
|||||||
p.VY = g.predictedVY
|
p.VY = g.predictedVY
|
||||||
p.OnGround = g.predictedGround
|
p.OnGround = g.predictedGround
|
||||||
p.OnWall = g.predictedOnWall
|
p.OnWall = g.predictedOnWall
|
||||||
|
|
||||||
// Lokale Kollisionsprüfung für Coins/Powerups
|
// Lokale Kollisionsprüfung für Coins/Powerups
|
||||||
g.checkOfflineCollisions(&p)
|
g.checkOfflineCollisions(&p)
|
||||||
|
|
||||||
@@ -177,25 +198,25 @@ func (g *Game) spawnOfflineChunk(atX float64) {
|
|||||||
X: atX,
|
X: atX,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Extrahiere Moving Platforms aus dem neuen Chunk
|
// Extrahiere Plattformen aus dem neuen Chunk
|
||||||
chunkDef := g.world.ChunkLibrary[randomID]
|
chunkDef := g.world.ChunkLibrary[randomID]
|
||||||
for i, obj := range chunkDef.Objects {
|
for i, obj := range chunkDef.Objects {
|
||||||
asset, ok := g.world.Manifest.Assets[obj.AssetID]
|
asset, ok := g.world.Manifest.Assets[obj.AssetID]
|
||||||
if ok && asset.Type == "moving_platform" && obj.MovingPlatform != nil {
|
// In Solo gibt es keine MovingPlatformData, Plattformen sind statisch
|
||||||
mp := obj.MovingPlatform
|
if ok && asset.Type == "moving_platform" {
|
||||||
p := &MovingPlatform{
|
p := &MovingPlatform{
|
||||||
ChunkID: randomID,
|
ChunkID: randomID,
|
||||||
ObjectIdx: i,
|
ObjectIdx: i,
|
||||||
AssetID: obj.AssetID,
|
AssetID: obj.AssetID,
|
||||||
StartX: atX + mp.StartX,
|
StartX: atX + obj.X,
|
||||||
StartY: mp.StartY,
|
StartY: obj.Y,
|
||||||
EndX: atX + mp.EndX,
|
EndX: atX + obj.X,
|
||||||
EndY: mp.EndY,
|
EndY: obj.Y,
|
||||||
Speed: mp.Speed,
|
Speed: 0,
|
||||||
Direction: 1.0,
|
Direction: 1.0,
|
||||||
IsActive: true,
|
IsActive: false,
|
||||||
CurrentX: atX + mp.StartX,
|
CurrentX: atX + obj.X,
|
||||||
CurrentY: mp.StartY,
|
CurrentY: obj.Y,
|
||||||
HitboxW: asset.Hitbox.W,
|
HitboxW: asset.Hitbox.W,
|
||||||
HitboxH: asset.Hitbox.H,
|
HitboxH: asset.Hitbox.H,
|
||||||
DrawOffX: asset.DrawOffX,
|
DrawOffX: asset.DrawOffX,
|
||||||
@@ -256,7 +277,7 @@ func (g *Game) checkOfflineCollisions(p *game.PlayerState) {
|
|||||||
pDrawX = def.DrawOffX
|
pDrawX = def.DrawOffX
|
||||||
pDrawY = def.DrawOffY
|
pDrawY = def.DrawOffY
|
||||||
}
|
}
|
||||||
|
|
||||||
pRect := game.Rect{
|
pRect := game.Rect{
|
||||||
OffsetX: p.X + pDrawX + pOffX,
|
OffsetX: p.X + pDrawX + pOffX,
|
||||||
OffsetY: p.Y + pDrawY + pOffY,
|
OffsetY: p.Y + pDrawY + pOffY,
|
||||||
@@ -268,25 +289,29 @@ func (g *Game) checkOfflineCollisions(p *game.PlayerState) {
|
|||||||
chunkDef := g.world.ChunkLibrary[ac.ChunkID]
|
chunkDef := g.world.ChunkLibrary[ac.ChunkID]
|
||||||
for i, obj := range chunkDef.Objects {
|
for i, obj := range chunkDef.Objects {
|
||||||
asset, ok := g.world.Manifest.Assets[obj.AssetID]
|
asset, ok := g.world.Manifest.Assets[obj.AssetID]
|
||||||
if !ok { continue }
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
objID := fmt.Sprintf("%s_%d", ac.ChunkID, i)
|
objID := fmt.Sprintf("%s_%d", ac.ChunkID, i)
|
||||||
|
|
||||||
// 1. COINS
|
// 1. COINS
|
||||||
if asset.Type == "coin" {
|
if asset.Type == "coin" {
|
||||||
if g.gameState.CollectedCoins[objID] { continue }
|
if g.gameState.CollectedCoins[objID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
coinX := ac.X + obj.X + asset.DrawOffX + asset.Hitbox.OffsetX
|
coinX := ac.X + obj.X + asset.DrawOffX + asset.Hitbox.OffsetX
|
||||||
coinY := obj.Y + asset.DrawOffY + asset.Hitbox.OffsetY
|
coinY := obj.Y + asset.DrawOffY + asset.Hitbox.OffsetY
|
||||||
|
|
||||||
// Magnet-Effekt?
|
// Magnet-Effekt?
|
||||||
if p.HasMagnet {
|
if p.HasMagnet {
|
||||||
playerCenterX := pRect.OffsetX + pRect.W/2
|
playerCenterX := pRect.OffsetX + pRect.W/2
|
||||||
playerCenterY := pRect.OffsetY + pRect.H/2
|
playerCenterY := pRect.OffsetY + pRect.H/2
|
||||||
coinCenterX := coinX + asset.Hitbox.W/2
|
coinCenterX := coinX + asset.Hitbox.W/2
|
||||||
coinCenterY := coinY + asset.Hitbox.H/2
|
coinCenterY := coinY + asset.Hitbox.H/2
|
||||||
|
|
||||||
dist := math.Sqrt(math.Pow(playerCenterX - coinCenterX, 2) + math.Pow(playerCenterY - coinCenterY, 2))
|
dist := math.Sqrt(math.Pow(playerCenterX-coinCenterX, 2) + math.Pow(playerCenterY-coinCenterY, 2))
|
||||||
if dist < 300 {
|
if dist < 300 {
|
||||||
// Münze wird eingesammelt wenn im Magnet-Radius
|
// Münze wird eingesammelt wenn im Magnet-Radius
|
||||||
g.gameState.CollectedCoins[objID] = true
|
g.gameState.CollectedCoins[objID] = true
|
||||||
@@ -306,8 +331,10 @@ func (g *Game) checkOfflineCollisions(p *game.PlayerState) {
|
|||||||
|
|
||||||
// 2. POWERUPS
|
// 2. POWERUPS
|
||||||
if asset.Type == "powerup" {
|
if asset.Type == "powerup" {
|
||||||
if g.gameState.CollectedPowerups[objID] { continue }
|
if g.gameState.CollectedPowerups[objID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
puRect := game.Rect{
|
puRect := game.Rect{
|
||||||
OffsetX: ac.X + obj.X + asset.DrawOffX + asset.Hitbox.OffsetX,
|
OffsetX: ac.X + obj.X + asset.DrawOffX + asset.Hitbox.OffsetX,
|
||||||
OffsetY: obj.Y + asset.DrawOffY + asset.Hitbox.OffsetY,
|
OffsetY: obj.Y + asset.DrawOffY + asset.Hitbox.OffsetY,
|
||||||
@@ -318,7 +345,7 @@ func (g *Game) checkOfflineCollisions(p *game.PlayerState) {
|
|||||||
if game.CheckRectCollision(pRect, puRect) {
|
if game.CheckRectCollision(pRect, puRect) {
|
||||||
g.gameState.CollectedPowerups[objID] = true
|
g.gameState.CollectedPowerups[objID] = true
|
||||||
g.audio.PlayPowerUp()
|
g.audio.PlayPowerUp()
|
||||||
|
|
||||||
switch obj.AssetID {
|
switch obj.AssetID {
|
||||||
case "jumpboost":
|
case "jumpboost":
|
||||||
p.HasDoubleJump = true
|
p.HasDoubleJump = true
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ import (
|
|||||||
// ApplyInput wendet einen Input auf den vorhergesagten Zustand an
|
// ApplyInput wendet einen Input auf den vorhergesagten Zustand an
|
||||||
// Nutzt die gemeinsame Physik-Engine aus pkg/physics
|
// Nutzt die gemeinsame Physik-Engine aus pkg/physics
|
||||||
func (g *Game) ApplyInput(input InputState) {
|
func (g *Game) ApplyInput(input InputState) {
|
||||||
|
g.stateMutex.Lock()
|
||||||
|
status := g.gameState.Status
|
||||||
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
|
if status == "COUNTDOWN" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Horizontale Bewegung mit analogem Joystick
|
// Horizontale Bewegung mit analogem Joystick
|
||||||
moveX := 0.0
|
moveX := 0.0
|
||||||
if input.Left {
|
if input.Left {
|
||||||
@@ -203,40 +211,14 @@ func (g *Game) checkSoloRound() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Lokale Todes-Erkennung (Obstacles & Grenzen)
|
// 1. Lokale Todes-Erkennung (Nur noch Grenzen im Solo-Modus)
|
||||||
// Wir nutzen die vorhergesagte Position
|
|
||||||
pConst := physics.DefaultPlayerConstants()
|
|
||||||
checkX := g.predictedX + pConst.DrawOffX + pConst.HitboxOffX
|
|
||||||
checkY := g.predictedY + pConst.DrawOffY + pConst.HitboxOffY
|
|
||||||
|
|
||||||
g.stateMutex.Lock()
|
g.stateMutex.Lock()
|
||||||
collisionChecker := &physics.ClientCollisionChecker{
|
|
||||||
World: g.world,
|
|
||||||
ActiveChunks: g.gameState.WorldChunks,
|
|
||||||
MovingPlatforms: g.gameState.MovingPlatforms,
|
|
||||||
}
|
|
||||||
scrollX := g.gameState.ScrollX
|
scrollX := g.gameState.ScrollX
|
||||||
|
|
||||||
hasGodMode := false
|
|
||||||
for _, p := range g.gameState.Players {
|
|
||||||
if p.Name == g.playerName {
|
|
||||||
hasGodMode = p.HasGodMode
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.stateMutex.Unlock()
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
// Kollision mit Hindernis?
|
|
||||||
hit, colType := collisionChecker.CheckCollision(checkX, checkY, pConst.Width, pConst.Height)
|
|
||||||
|
|
||||||
isDead := false
|
isDead := false
|
||||||
deathReason := ""
|
deathReason := ""
|
||||||
|
|
||||||
if hit && colType == "obstacle" && !hasGodMode {
|
|
||||||
isDead = true
|
|
||||||
deathReason = "Hindernis berührt"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aus dem linken Bildschirmrand gefallen?
|
// Aus dem linken Bildschirmrand gefallen?
|
||||||
if g.predictedX < scrollX-50 {
|
if g.predictedX < scrollX-50 {
|
||||||
isDead = true
|
isDead = true
|
||||||
|
|||||||
@@ -630,8 +630,8 @@ function toggleAudio() {
|
|||||||
if (window.setSFXVolume) window.setSFXVolume(0);
|
if (window.setSFXVolume) window.setSFXVolume(0);
|
||||||
} else {
|
} else {
|
||||||
btn.textContent = '🔊';
|
btn.textContent = '🔊';
|
||||||
const musicVol = parseInt(localStorage.getItem('escape_music_volume') || 70) / 100;
|
const musicVol = parseInt(localStorage.getItem('escape_music_volume') || 80) / 100;
|
||||||
const sfxVol = parseInt(localStorage.getItem('escape_sfx_volume') || 70) / 100;
|
const sfxVol = parseInt(localStorage.getItem('escape_sfx_volume') || 40) / 100;
|
||||||
if (window.setMusicVolume) window.setMusicVolume(musicVol);
|
if (window.setMusicVolume) window.setMusicVolume(musicVol);
|
||||||
if (window.setSFXVolume) window.setSFXVolume(sfxVol);
|
if (window.setSFXVolume) window.setSFXVolume(sfxVol);
|
||||||
}
|
}
|
||||||
@@ -656,7 +656,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load saved value
|
// Load saved value
|
||||||
const savedMusic = localStorage.getItem('escape_music_volume') || 70;
|
const savedMusic = localStorage.getItem('escape_music_volume') || 80;
|
||||||
musicSlider.value = savedMusic;
|
musicSlider.value = savedMusic;
|
||||||
musicValue.textContent = savedMusic + '%';
|
musicValue.textContent = savedMusic + '%';
|
||||||
}
|
}
|
||||||
@@ -673,7 +673,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load saved value
|
// Load saved value
|
||||||
const savedSFX = localStorage.getItem('escape_sfx_volume') || 70;
|
const savedSFX = localStorage.getItem('escape_sfx_volume') || 40;
|
||||||
sfxSlider.value = savedSFX;
|
sfxSlider.value = savedSFX;
|
||||||
sfxValue.textContent = savedSFX + '%';
|
sfxValue.textContent = savedSFX + '%';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,14 +95,14 @@
|
|||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<label>MUSIK LAUTSTÄRKE:</label>
|
<label>MUSIK LAUTSTÄRKE:</label>
|
||||||
<input type="range" id="musicVolume" min="0" max="100" value="70">
|
<input type="range" id="musicVolume" min="0" max="100" value="80">
|
||||||
<span id="musicValue">70%</span>
|
<span id="musicValue">80%</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<label>SFX LAUTSTÄRKE:</label>
|
<label>SFX LAUTSTÄRKE:</label>
|
||||||
<input type="range" id="sfxVolume" min="0" max="100" value="70">
|
<input type="range" id="sfxVolume" min="0" max="100" value="40">
|
||||||
<span id="sfxValue">70%</span>
|
<span id="sfxValue">40%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ kind: Ingress
|
|||||||
metadata:
|
metadata:
|
||||||
name: game-ingress
|
name: game-ingress
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
cert-manager.io/cluster-issuer: ${CERT_ISSUER}
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: web, websecure
|
traefik.ingress.kubernetes.io/router.entrypoints: web, websecure
|
||||||
traefik.ingress.kubernetes.io/router.middlewares: gitea-redirect-https@kubernetescrd,${TARGET_NS}-compress@kubernetescrd
|
traefik.ingress.kubernetes.io/router.middlewares: gitea-redirect-https@kubernetescrd,${TARGET_NS}-compress@kubernetescrd
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
Reference in New Issue
Block a user