fix game
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 6m49s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 6m49s
This commit is contained in:
@@ -88,6 +88,8 @@ type Game struct {
|
|||||||
predictedVY float64
|
predictedVY float64
|
||||||
predictedGround bool
|
predictedGround bool
|
||||||
predictedOnWall bool
|
predictedOnWall bool
|
||||||
|
predictedHasDoubleJump bool // Lokale Kopie des Double-Jump-Powerups
|
||||||
|
predictedDoubleJumpUsed bool // Wurde zweiter Sprung schon verbraucht?
|
||||||
currentSpeed float64 // Aktuelle Scroll-Geschwindigkeit vom Server
|
currentSpeed float64 // Aktuelle Scroll-Geschwindigkeit vom Server
|
||||||
inputSequence uint32 // Sequenznummer für Inputs
|
inputSequence uint32 // Sequenznummer für Inputs
|
||||||
pendingInputs map[uint32]InputState // Noch nicht bestätigte Inputs
|
pendingInputs map[uint32]InputState // Noch nicht bestätigte Inputs
|
||||||
|
|||||||
@@ -51,8 +51,13 @@ func (g *Game) SpawnCoinParticles(x, y float64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnPowerupAura erstellt Partikel-Aura um Spieler mit aktivem Powerup
|
// SpawnPowerupAura erstellt Partikel-Aura um Spieler mit aktivem Powerup.
|
||||||
func (g *Game) SpawnPowerupAura(x, y float64, powerupType string) {
|
// intensity: 0.0 (schwach/auslaufend) bis 1.0 (voll aktiv) – steuert Spawn-Rate und Partikelgröße.
|
||||||
|
func (g *Game) SpawnPowerupAura(x, y float64, powerupType string, intensity float64) {
|
||||||
|
if intensity <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
g.particlesMutex.Lock()
|
g.particlesMutex.Lock()
|
||||||
defer g.particlesMutex.Unlock()
|
defer g.particlesMutex.Unlock()
|
||||||
|
|
||||||
@@ -63,18 +68,24 @@ func (g *Game) SpawnPowerupAura(x, y float64, powerupType string) {
|
|||||||
particleColor = color.RGBA{100, 200, 255, 200} // Hellblau
|
particleColor = color.RGBA{100, 200, 255, 200} // Hellblau
|
||||||
case "godmode":
|
case "godmode":
|
||||||
particleColor = color.RGBA{255, 215, 0, 200} // Gold
|
particleColor = color.RGBA{255, 215, 0, 200} // Gold
|
||||||
|
case "magnet":
|
||||||
|
particleColor = color.RGBA{255, 80, 220, 200} // Pink/Magenta
|
||||||
default:
|
default:
|
||||||
particleColor = color.RGBA{200, 100, 255, 200} // Lila
|
particleColor = color.RGBA{200, 100, 255, 200} // Lila
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nur gelegentlich spawnen (nicht jedes Frame) - 1 Partikel alle 3-4 Frames
|
// Spawn-Rate skaliert mit Intensität: bei voll aktiv ~1/3 Chance, bei fast leer ~1/10
|
||||||
if rand.Intn(3) != 0 {
|
spawnThreshold := int(3.0/intensity + 0.5)
|
||||||
|
if spawnThreshold < 1 {
|
||||||
|
spawnThreshold = 1
|
||||||
|
}
|
||||||
|
if rand.Intn(spawnThreshold) != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 Partikel für sanfte Aura - spawnen in alle Richtungen
|
|
||||||
angle := rand.Float64() * 2 * math.Pi
|
angle := rand.Float64() * 2 * math.Pi
|
||||||
distance := 25.0 + rand.Float64()*20.0
|
distance := 25.0 + rand.Float64()*20.0
|
||||||
|
size := (2.5 + rand.Float64()*1.5) * intensity
|
||||||
|
|
||||||
g.particles = append(g.particles, Particle{
|
g.particles = append(g.particles, Particle{
|
||||||
X: x + math.Cos(angle)*distance,
|
X: x + math.Cos(angle)*distance,
|
||||||
@@ -82,8 +93,8 @@ func (g *Game) SpawnPowerupAura(x, y float64, powerupType string) {
|
|||||||
VX: math.Cos(angle) * 0.5,
|
VX: math.Cos(angle) * 0.5,
|
||||||
VY: math.Sin(angle) * 0.5,
|
VY: math.Sin(angle) * 0.5,
|
||||||
Life: 1.0,
|
Life: 1.0,
|
||||||
MaxLife: 1.0 + rand.Float64()*0.5,
|
MaxLife: (1.0 + rand.Float64()*0.5) * intensity,
|
||||||
Size: 2.5 + rand.Float64()*1.5,
|
Size: size,
|
||||||
Color: particleColor,
|
Color: particleColor,
|
||||||
Type: "powerup",
|
Type: "powerup",
|
||||||
Gravity: false,
|
Gravity: false,
|
||||||
@@ -299,10 +310,38 @@ func (g *Game) DetectAndSpawnParticles() {
|
|||||||
centerY := player.Y - 231 + 42 + 184/2
|
centerY := player.Y - 231 + 42 + 184/2
|
||||||
|
|
||||||
if player.HasDoubleJump {
|
if player.HasDoubleJump {
|
||||||
g.SpawnPowerupAura(centerX, centerY, "doublejump")
|
// Intensität nimmt mit verbleibender Zeit ab (0..15s → 1.0..0.1)
|
||||||
|
// Zusätzlich schwächer wenn Sprung bereits verbraucht
|
||||||
|
intensity := player.DoubleJumpRemainingSeconds / 15.0
|
||||||
|
if intensity > 1.0 {
|
||||||
|
intensity = 1.0
|
||||||
|
} else if intensity < 0.1 {
|
||||||
|
intensity = 0.1
|
||||||
|
}
|
||||||
|
if player.DoubleJumpUsed {
|
||||||
|
intensity *= 0.4
|
||||||
|
}
|
||||||
|
g.SpawnPowerupAura(centerX, centerY, "doublejump", intensity)
|
||||||
}
|
}
|
||||||
if player.HasGodMode {
|
if player.HasGodMode {
|
||||||
g.SpawnPowerupAura(centerX, centerY, "godmode")
|
// Intensität nimmt mit verbleibender Zeit ab (0..10s → 1.0..0.1)
|
||||||
|
intensity := player.GodModeRemainingSeconds / 10.0
|
||||||
|
if intensity > 1.0 {
|
||||||
|
intensity = 1.0
|
||||||
|
} else if intensity < 0.1 {
|
||||||
|
intensity = 0.1
|
||||||
|
}
|
||||||
|
g.SpawnPowerupAura(centerX, centerY, "godmode", intensity)
|
||||||
|
}
|
||||||
|
if player.HasMagnet {
|
||||||
|
// Intensität nimmt mit verbleibender Zeit ab (0..8s → 1.0..0.1)
|
||||||
|
intensity := player.MagnetRemainingSeconds / 8.0
|
||||||
|
if intensity > 1.0 {
|
||||||
|
intensity = 1.0
|
||||||
|
} else if intensity < 0.1 {
|
||||||
|
intensity = 0.1
|
||||||
|
}
|
||||||
|
g.SpawnPowerupAura(centerX, centerY, "magnet", intensity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/config"
|
||||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
|
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
|
||||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/physics"
|
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/physics"
|
||||||
)
|
)
|
||||||
@@ -31,6 +32,17 @@ func (g *Game) ApplyInput(input InputState) {
|
|||||||
OnWall: g.predictedOnWall,
|
OnWall: g.predictedOnWall,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double Jump: vor der Physik anwenden (1:1 wie Server)
|
||||||
|
if input.Jump && !g.predictedGround && g.predictedHasDoubleJump && !g.predictedDoubleJumpUsed {
|
||||||
|
g.predictedVY = -config.JumpVelocity
|
||||||
|
g.predictedDoubleJumpUsed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double Jump Reset wenn wieder am Boden
|
||||||
|
if g.predictedGround {
|
||||||
|
g.predictedDoubleJumpUsed = false
|
||||||
|
}
|
||||||
|
|
||||||
// Physik-Input vorbereiten
|
// Physik-Input vorbereiten
|
||||||
physicsInput := physics.PhysicsInput{
|
physicsInput := physics.PhysicsInput{
|
||||||
InputX: moveX,
|
InputX: moveX,
|
||||||
@@ -175,4 +187,8 @@ func (g *Game) ReconcileWithServer(serverState game.PlayerState) {
|
|||||||
g.predictedVY = replayVY
|
g.predictedVY = replayVY
|
||||||
g.predictedGround = replayGround
|
g.predictedGround = replayGround
|
||||||
g.predictedOnWall = replayOnWall
|
g.predictedOnWall = replayOnWall
|
||||||
|
|
||||||
|
// Powerup-State vom Server übernehmen (autoritativ)
|
||||||
|
g.predictedHasDoubleJump = serverState.HasDoubleJump
|
||||||
|
g.predictedDoubleJumpUsed = serverState.DoubleJumpUsed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,24 @@
|
|||||||
"Type": ""
|
"Type": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"magnet": {
|
||||||
|
"ID": "magnet",
|
||||||
|
"Type": "powerup",
|
||||||
|
"Filename": "",
|
||||||
|
"Scale": 1.0,
|
||||||
|
"ProcWidth": 50,
|
||||||
|
"ProcHeight": 50,
|
||||||
|
"DrawOffX": -25,
|
||||||
|
"DrawOffY": -50,
|
||||||
|
"Color": {"R": 255, "G": 80, "B": 220, "A": 255},
|
||||||
|
"Hitbox": {
|
||||||
|
"OffsetX": 0,
|
||||||
|
"OffsetY": 0,
|
||||||
|
"W": 50,
|
||||||
|
"H": 50,
|
||||||
|
"Type": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"h-l": {
|
"h-l": {
|
||||||
"ID": "h-l",
|
"ID": "h-l",
|
||||||
"Type": "obstacle",
|
"Type": "obstacle",
|
||||||
|
|||||||
@@ -105,7 +105,12 @@ type PlayerState struct {
|
|||||||
IsAlive bool `json:"is_alive"` // Lebt der Spieler noch?
|
IsAlive bool `json:"is_alive"` // Lebt der Spieler noch?
|
||||||
IsSpectator bool `json:"is_spectator"` // Ist im Zuschauer-Modus
|
IsSpectator bool `json:"is_spectator"` // Ist im Zuschauer-Modus
|
||||||
HasDoubleJump bool `json:"has_double_jump"` // Hat Double Jump Powerup
|
HasDoubleJump bool `json:"has_double_jump"` // Hat Double Jump Powerup
|
||||||
|
DoubleJumpUsed bool `json:"double_jump_used"` // Wurde zweiter Sprung schon benutzt?
|
||||||
|
DoubleJumpRemainingSeconds float64 `json:"double_jump_remaining_seconds"` // Verbleibende Double-Jump-Zeit in Sekunden
|
||||||
HasGodMode bool `json:"has_godmode"` // Hat Godmode Powerup
|
HasGodMode bool `json:"has_godmode"` // Hat Godmode Powerup
|
||||||
|
GodModeRemainingSeconds float64 `json:"godmode_remaining_seconds"` // Verbleibende Godmode-Zeit in Sekunden
|
||||||
|
HasMagnet bool `json:"has_magnet"` // Hat Magnet Powerup
|
||||||
|
MagnetRemainingSeconds float64 `json:"magnet_remaining_seconds"` // Verbleibende Magnet-Zeit in Sekunden
|
||||||
}
|
}
|
||||||
|
|
||||||
type GameState struct {
|
type GameState struct {
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ type ServerPlayer struct {
|
|||||||
// Powerups
|
// Powerups
|
||||||
HasDoubleJump bool // Doppelsprung aktiv?
|
HasDoubleJump bool // Doppelsprung aktiv?
|
||||||
DoubleJumpUsed bool // Wurde zweiter Sprung schon benutzt?
|
DoubleJumpUsed bool // Wurde zweiter Sprung schon benutzt?
|
||||||
|
DoubleJumpEndTime time.Time // Wann endet Double Jump?
|
||||||
HasGodMode bool // Godmode aktiv?
|
HasGodMode bool // Godmode aktiv?
|
||||||
GodModeEndTime time.Time // Wann endet Godmode?
|
GodModeEndTime time.Time // Wann endet Godmode?
|
||||||
|
HasMagnet bool // Magnet aktiv?
|
||||||
|
MagnetEndTime time.Time // Wann endet Magnet?
|
||||||
}
|
}
|
||||||
|
|
||||||
type MovingPlatform struct {
|
type MovingPlatform struct {
|
||||||
@@ -525,11 +528,29 @@ func (r *Room) Update() {
|
|||||||
// Powerup Kollision prüfen
|
// Powerup Kollision prüfen
|
||||||
r.CheckPowerupCollision(p)
|
r.CheckPowerupCollision(p)
|
||||||
|
|
||||||
|
// Double Jump Timeout prüfen
|
||||||
|
if p.HasDoubleJump && time.Now().After(p.DoubleJumpEndTime) {
|
||||||
|
p.HasDoubleJump = false
|
||||||
|
p.DoubleJumpUsed = false
|
||||||
|
log.Printf("⚡ Double Jump von %s ist abgelaufen", p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// Godmode Timeout prüfen
|
// Godmode Timeout prüfen
|
||||||
if p.HasGodMode && time.Now().After(p.GodModeEndTime) {
|
if p.HasGodMode && time.Now().After(p.GodModeEndTime) {
|
||||||
p.HasGodMode = false
|
p.HasGodMode = false
|
||||||
log.Printf("🛡️ Godmode von %s ist abgelaufen", p.Name)
|
log.Printf("🛡️ Godmode von %s ist abgelaufen", p.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Magnet Timeout prüfen
|
||||||
|
if p.HasMagnet && time.Now().After(p.MagnetEndTime) {
|
||||||
|
p.HasMagnet = false
|
||||||
|
log.Printf("🧲 Magnet von %s ist abgelaufen", p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Magnet: Coins im Umkreis automatisch einsammeln
|
||||||
|
if p.HasMagnet {
|
||||||
|
r.ApplyMagnetEffect(p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2b. Distanz-Score updaten
|
// 2b. Distanz-Score updaten
|
||||||
@@ -838,7 +859,29 @@ func (r *Room) Broadcast() {
|
|||||||
DifficultyFactor: r.DifficultyFactor,
|
DifficultyFactor: r.DifficultyFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
for id, p := range r.Players {
|
for id, p := range r.Players {
|
||||||
|
djRemaining := 0.0
|
||||||
|
if p.HasDoubleJump {
|
||||||
|
djRemaining = p.DoubleJumpEndTime.Sub(now).Seconds()
|
||||||
|
if djRemaining < 0 {
|
||||||
|
djRemaining = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
godRemaining := 0.0
|
||||||
|
if p.HasGodMode {
|
||||||
|
godRemaining = p.GodModeEndTime.Sub(now).Seconds()
|
||||||
|
if godRemaining < 0 {
|
||||||
|
godRemaining = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
magnetRemaining := 0.0
|
||||||
|
if p.HasMagnet {
|
||||||
|
magnetRemaining = p.MagnetEndTime.Sub(now).Seconds()
|
||||||
|
if magnetRemaining < 0 {
|
||||||
|
magnetRemaining = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
state.Players[id] = game.PlayerState{
|
state.Players[id] = game.PlayerState{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
@@ -853,7 +896,12 @@ func (r *Room) Broadcast() {
|
|||||||
IsAlive: p.IsAlive,
|
IsAlive: p.IsAlive,
|
||||||
IsSpectator: p.IsSpectator,
|
IsSpectator: p.IsSpectator,
|
||||||
HasDoubleJump: p.HasDoubleJump,
|
HasDoubleJump: p.HasDoubleJump,
|
||||||
|
DoubleJumpUsed: p.DoubleJumpUsed,
|
||||||
|
DoubleJumpRemainingSeconds: djRemaining,
|
||||||
HasGodMode: p.HasGodMode,
|
HasGodMode: p.HasGodMode,
|
||||||
|
GodModeRemainingSeconds: godRemaining,
|
||||||
|
HasMagnet: p.HasMagnet,
|
||||||
|
MagnetRemainingSeconds: magnetRemaining,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,34 +21,23 @@ func (r *Room) CheckCoinCollision(p *ServerPlayer) {
|
|||||||
H: r.pH,
|
H: r.pH,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durch alle aktiven Chunks iterieren
|
|
||||||
for _, activeChunk := range r.ActiveChunks {
|
for _, activeChunk := range r.ActiveChunks {
|
||||||
chunkDef, exists := r.World.ChunkLibrary[activeChunk.ChunkID]
|
chunkDef, exists := r.World.ChunkLibrary[activeChunk.ChunkID]
|
||||||
if !exists {
|
if !exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durch alle Objekte im Chunk
|
|
||||||
for objIdx, obj := range chunkDef.Objects {
|
for objIdx, obj := range chunkDef.Objects {
|
||||||
assetDef, ok := r.World.Manifest.Assets[obj.AssetID]
|
assetDef, ok := r.World.Manifest.Assets[obj.AssetID]
|
||||||
if !ok {
|
if !ok || assetDef.Type != "coin" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nur Coins prüfen
|
|
||||||
if assetDef.Type != "coin" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eindeutiger Key für diesen Coin
|
|
||||||
coinKey := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
|
coinKey := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
|
||||||
|
|
||||||
// Wurde bereits eingesammelt?
|
|
||||||
if r.CollectedCoins[coinKey] {
|
if r.CollectedCoins[coinKey] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coin-Hitbox (muss DrawOffX/Y einbeziehen wie bei Obstacles!)
|
|
||||||
coinHitbox := game.Rect{
|
coinHitbox := game.Rect{
|
||||||
OffsetX: activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
OffsetX: activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
||||||
OffsetY: obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
OffsetY: obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
||||||
@@ -56,9 +45,7 @@ func (r *Room) CheckCoinCollision(p *ServerPlayer) {
|
|||||||
H: assetDef.Hitbox.H,
|
H: assetDef.Hitbox.H,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kollision?
|
|
||||||
if game.CheckRectCollision(playerHitbox, coinHitbox) {
|
if game.CheckRectCollision(playerHitbox, coinHitbox) {
|
||||||
// Coin einsammeln!
|
|
||||||
r.CollectedCoins[coinKey] = true
|
r.CollectedCoins[coinKey] = true
|
||||||
p.BonusScore += 200
|
p.BonusScore += 200
|
||||||
p.Score = p.DistanceScore + p.BonusScore
|
p.Score = p.DistanceScore + p.BonusScore
|
||||||
@@ -81,34 +68,23 @@ func (r *Room) CheckPowerupCollision(p *ServerPlayer) {
|
|||||||
H: r.pH,
|
H: r.pH,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durch alle aktiven Chunks iterieren
|
|
||||||
for _, activeChunk := range r.ActiveChunks {
|
for _, activeChunk := range r.ActiveChunks {
|
||||||
chunkDef, exists := r.World.ChunkLibrary[activeChunk.ChunkID]
|
chunkDef, exists := r.World.ChunkLibrary[activeChunk.ChunkID]
|
||||||
if !exists {
|
if !exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durch alle Objekte im Chunk
|
|
||||||
for objIdx, obj := range chunkDef.Objects {
|
for objIdx, obj := range chunkDef.Objects {
|
||||||
assetDef, ok := r.World.Manifest.Assets[obj.AssetID]
|
assetDef, ok := r.World.Manifest.Assets[obj.AssetID]
|
||||||
if !ok {
|
if !ok || assetDef.Type != "powerup" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nur Powerups prüfen
|
|
||||||
if assetDef.Type != "powerup" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eindeutiger Key für dieses Powerup
|
|
||||||
powerupKey := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
|
powerupKey := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
|
||||||
|
|
||||||
// Wurde bereits eingesammelt?
|
|
||||||
if r.CollectedPowerups[powerupKey] {
|
if r.CollectedPowerups[powerupKey] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Powerup-Hitbox
|
|
||||||
powerupHitbox := game.Rect{
|
powerupHitbox := game.Rect{
|
||||||
OffsetX: activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
OffsetX: activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
||||||
OffsetY: obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
OffsetY: obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
||||||
@@ -116,35 +92,77 @@ func (r *Room) CheckPowerupCollision(p *ServerPlayer) {
|
|||||||
H: assetDef.Hitbox.H,
|
H: assetDef.Hitbox.H,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kollision?
|
|
||||||
if game.CheckRectCollision(playerHitbox, powerupHitbox) {
|
if game.CheckRectCollision(playerHitbox, powerupHitbox) {
|
||||||
// Powerup einsammeln!
|
|
||||||
r.CollectedPowerups[powerupKey] = true
|
r.CollectedPowerups[powerupKey] = true
|
||||||
|
|
||||||
// Powerup-Effekt anwenden
|
|
||||||
switch obj.AssetID {
|
switch obj.AssetID {
|
||||||
case "jumpboost":
|
case "jumpboost":
|
||||||
p.HasDoubleJump = true
|
p.HasDoubleJump = true
|
||||||
p.DoubleJumpUsed = false
|
p.DoubleJumpUsed = false
|
||||||
log.Printf("⚡ %s hat Double Jump erhalten!", p.Name)
|
p.DoubleJumpEndTime = time.Now().Add(15 * time.Second)
|
||||||
|
log.Printf("⚡ %s hat Double Jump erhalten! (15 Sekunden)", p.Name)
|
||||||
|
|
||||||
case "godmode":
|
case "godmode":
|
||||||
p.HasGodMode = true
|
p.HasGodMode = true
|
||||||
p.GodModeEndTime = time.Now().Add(10 * time.Second)
|
p.GodModeEndTime = time.Now().Add(10 * time.Second)
|
||||||
log.Printf("🛡️ %s hat Godmode erhalten! (10 Sekunden)", p.Name)
|
log.Printf("🛡️ %s hat Godmode erhalten! (10 Sekunden)", p.Name)
|
||||||
|
|
||||||
|
case "magnet":
|
||||||
|
p.HasMagnet = true
|
||||||
|
p.MagnetEndTime = time.Now().Add(8 * time.Second)
|
||||||
|
log.Printf("🧲 %s hat Magnet erhalten! (8 Sekunden)", p.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyMagnetEffect sammelt alle Coins im Umkreis von ~300px automatisch ein
|
||||||
|
func (r *Room) ApplyMagnetEffect(p *ServerPlayer) {
|
||||||
|
const magnetRadius = 300.0
|
||||||
|
|
||||||
|
playerCenterX := p.X + r.pDrawOffX + r.pHitboxOffX + r.pW/2
|
||||||
|
playerCenterY := p.Y + r.pDrawOffY + r.pHitboxOffY + r.pH/2
|
||||||
|
|
||||||
|
for _, activeChunk := range r.ActiveChunks {
|
||||||
|
chunkDef, exists := r.World.ChunkLibrary[activeChunk.ChunkID]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for objIdx, obj := range chunkDef.Objects {
|
||||||
|
assetDef, ok := r.World.Manifest.Assets[obj.AssetID]
|
||||||
|
if !ok || assetDef.Type != "coin" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
coinKey := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
|
||||||
|
if r.CollectedCoins[coinKey] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
coinCenterX := activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX + assetDef.Hitbox.W/2
|
||||||
|
coinCenterY := obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY + assetDef.Hitbox.H/2
|
||||||
|
|
||||||
|
dx := coinCenterX - playerCenterX
|
||||||
|
dy := coinCenterY - playerCenterY
|
||||||
|
|
||||||
|
if dx*dx+dy*dy <= magnetRadius*magnetRadius {
|
||||||
|
r.CollectedCoins[coinKey] = true
|
||||||
|
p.BonusScore += 200
|
||||||
|
p.Score = p.DistanceScore + p.BonusScore
|
||||||
|
log.Printf("🧲 %s hat Coin per Magnet eingesammelt! Score: %d", p.Name, p.Score)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateDistanceScore aktualisiert Distanz-basierte Punkte
|
// UpdateDistanceScore aktualisiert Distanz-basierte Punkte
|
||||||
func (r *Room) UpdateDistanceScore() {
|
func (r *Room) UpdateDistanceScore() {
|
||||||
if r.Status != "RUNNING" {
|
if r.Status != "RUNNING" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zähle lebende Spieler
|
|
||||||
aliveCount := 0
|
aliveCount := 0
|
||||||
for _, p := range r.Players {
|
for _, p := range r.Players {
|
||||||
if p.IsAlive && !p.IsSpectator {
|
if p.IsAlive && !p.IsSpectator {
|
||||||
@@ -156,15 +174,9 @@ func (r *Room) UpdateDistanceScore() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pro lebendem Spieler werden Punkte hinzugefügt
|
|
||||||
// Dies akkumuliert die Punkte: mehr Spieler = schnellere Punktesammlung
|
|
||||||
// Jeder Tick (bei 60 FPS) fügt aliveCount Punkte hinzu
|
|
||||||
pointsToAdd := aliveCount
|
|
||||||
|
|
||||||
// Jeder lebende Spieler bekommt die gleichen Punkte
|
|
||||||
for _, p := range r.Players {
|
for _, p := range r.Players {
|
||||||
if p.IsAlive && !p.IsSpectator {
|
if p.IsAlive && !p.IsSpectator {
|
||||||
p.DistanceScore += pointsToAdd
|
p.DistanceScore += aliveCount
|
||||||
p.Score = p.DistanceScore + p.BonusScore
|
p.Score = p.DistanceScore + p.BonusScore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +192,6 @@ func (r *Room) KillPlayer(p *ServerPlayer) {
|
|||||||
p.IsSpectator = true
|
p.IsSpectator = true
|
||||||
log.Printf("💀 %s ist gestorben! Final Score: %d", p.Name, p.Score)
|
log.Printf("💀 %s ist gestorben! Final Score: %d", p.Name, p.Score)
|
||||||
|
|
||||||
// Prüfen ob alle tot sind
|
|
||||||
aliveCount := 0
|
aliveCount := 0
|
||||||
for _, pl := range r.Players {
|
for _, pl := range r.Players {
|
||||||
if pl.IsAlive && !pl.IsSpectator {
|
if pl.IsAlive && !pl.IsSpectator {
|
||||||
|
|||||||
Reference in New Issue
Block a user