Add gravity and player-seeking AI to zombies

This commit is contained in:
Master290 2026-06-27 21:16:39 +03:00
parent 7af0acd3a3
commit 09a694daf5
5 changed files with 89 additions and 7 deletions

View file

@ -4,6 +4,7 @@ package server
import (
"encoding/json"
"sync"
"regionio/internal/protocol"
"regionio/internal/world"
@ -55,6 +56,9 @@ type Server struct {
chunks *world.Cache
store *world.Store // nil when persistence is disabled
entities *world.EntityManager
// playerPos tracks the last known position of each player by name.
playerPos sync.Map // map[string][3]float64
}
// New constructs a Server from cfg. When cfg.WorldDir is set, the world is
@ -89,6 +93,33 @@ func (s *Server) Chunks() *world.Cache { return s.chunks }
// Entities returns the shared entity manager.
func (s *Server) Entities() *world.EntityManager { return s.entities }
// SetPlayerPosition updates the tracked position of a player.
func (s *Server) SetPlayerPosition(name string, x, y, z float64) {
s.playerPos.Store(name, [3]float64{x, y, z})
}
// RemovePlayerPosition removes a player from tracking.
func (s *Server) RemovePlayerPosition(name string) {
s.playerPos.Delete(name)
}
// NearestPlayer returns the position of the nearest player to (x, y, z).
// Returns false if no players are online.
func (s *Server) NearestPlayer(x, y, z float64) (pos [3]float64, ok bool) {
minDist := float64(-1)
s.playerPos.Range(func(key, value any) bool {
p := value.([3]float64)
dist := (p[0]-x)*(p[0]-x) + (p[1]-y)*(p[1]-y) + (p[2]-z)*(p[2]-z)
if minDist < 0 || dist < minDist {
minDist = dist
pos = p
ok = true
}
return true
})
return
}
// Store returns the on-disk world store, or nil if persistence is disabled.
func (s *Server) Store() *world.Store { return s.store }

View file

@ -1,6 +1,7 @@
package server
import (
"math"
"math/rand"
"time"
@ -20,10 +21,44 @@ func (s *Server) entityTickLoop() {
for range ticker.C {
all := s.entities.All()
for _, e := range all {
// Basic random wandering
e.X += (rand.Float64() - 0.5) * 0.2
e.Z += (rand.Float64() - 0.5) * 0.2
e.Yaw += float32((rand.Float64() - 0.5) * 10.0)
// Apply gravity
yBelow := int(e.Y - 0.1) // slightly below the entity
blockBelow := s.chunks.GetBlock(int(e.X), yBelow, int(e.Z))
if blockBelow == world.StateAir || blockBelow == world.StateWater { // Air or Water
e.VelocityY -= 80 // gravity acceleration
if e.VelocityY < -3000 {
e.VelocityY = -3000 // terminal velocity
}
} else {
e.VelocityY = 0
e.Y = float64(yBelow + 1)
// Basic random wandering or player tracking when on ground
pos, ok := s.NearestPlayer(e.X, e.Y, e.Z)
if ok && e.TypeName == "minecraft:zombie" {
// Zombies move towards the player
dx := pos[0] - e.X
dz := pos[2] - e.Z
dist := math.Sqrt(dx*dx + dz*dz)
if dist > 1.0 && dist < 32.0 {
e.X += (dx / dist) * 0.15
e.Z += (dz / dist) * 0.15
// Simple yaw calculation
e.Yaw = float32(math.Atan2(-dx, dz) * (180 / math.Pi))
}
} else {
// Random wander
e.X += (rand.Float64() - 0.5) * 0.2
e.Z += (rand.Float64() - 0.5) * 0.2
e.Yaw += float32((rand.Float64() - 0.5) * 10.0)
}
}
if e.VelocityY != 0 {
e.Y += float64(e.VelocityY) / 8000.0
}
}
}
}