197 lines
5.1 KiB
Go
197 lines
5.1 KiB
Go
package types
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
|
rl "github.com/gen2brain/raylib-go/raylib"
|
|
)
|
|
|
|
// AnimationController manages animation state and updates
|
|
type AnimationController struct {
|
|
animations AnimationSet
|
|
currentAnimation string // "idle" or "walk"
|
|
frame int32
|
|
lastUpdate time.Time
|
|
frameCount int32
|
|
}
|
|
|
|
// NewAnimationController creates a new animation controller
|
|
func NewAnimationController(animations AnimationSet) *AnimationController {
|
|
return &AnimationController{
|
|
animations: animations,
|
|
currentAnimation: "idle",
|
|
frame: 0,
|
|
lastUpdate: time.Now(),
|
|
}
|
|
}
|
|
|
|
// Update updates the animation state based on movement
|
|
func (ac *AnimationController) Update(deltaTime float32, isMoving bool) {
|
|
// Set the current animation based on movement
|
|
newAnimation := "idle"
|
|
if isMoving {
|
|
newAnimation = "walk"
|
|
}
|
|
|
|
// Reset frame counter when animation changes
|
|
if ac.currentAnimation != newAnimation {
|
|
ac.frame = 0
|
|
ac.currentAnimation = newAnimation
|
|
}
|
|
|
|
// Update the frame
|
|
ac.frame += int32(deltaTime * 60)
|
|
|
|
// Determine which animation set to use
|
|
var frames []rl.ModelAnimation
|
|
if ac.currentAnimation == "walk" && len(ac.animations.Walk) > 0 {
|
|
frames = ac.animations.Walk
|
|
} else if len(ac.animations.Idle) > 0 {
|
|
frames = ac.animations.Idle
|
|
}
|
|
|
|
// If we have frames, ensure we loop properly
|
|
if len(frames) > 0 && frames[0].FrameCount > 0 {
|
|
ac.frame = ac.frame % frames[0].FrameCount
|
|
}
|
|
}
|
|
|
|
// GetAnimFrame returns the current animation frame
|
|
func (ac *AnimationController) GetAnimFrame() int32 {
|
|
return ac.frame
|
|
}
|
|
|
|
// GetCurrentAnimation returns the current animation type
|
|
func (ac *AnimationController) GetCurrentAnimation() string {
|
|
return ac.currentAnimation
|
|
}
|
|
|
|
type Player struct {
|
|
sync.RWMutex // Keep this for network operations
|
|
Model rl.Model
|
|
Texture rl.Texture2D
|
|
PosActual rl.Vector3
|
|
PosTile Tile
|
|
TargetPath []Tile
|
|
Speed float32
|
|
ActionQueue []*pb.Action
|
|
ID int32
|
|
QuitDone chan struct{}
|
|
CurrentTick int64
|
|
UserData interface{}
|
|
FloatingMessage *FloatingMessage
|
|
IsMoving bool
|
|
AnimationFrame int32
|
|
LastAnimUpdate time.Time
|
|
LastUpdateTime time.Time
|
|
InterpolationProgress float32
|
|
PlaceholderColor rl.Color
|
|
AnimController *AnimationController
|
|
}
|
|
|
|
func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
|
|
// No need for lock here as this is called from a single thread (game loop)
|
|
targetPos := rl.Vector3{
|
|
X: float32(target.X * TileSize),
|
|
Y: mapGrid[target.X][target.Y].Height * TileHeight,
|
|
Z: float32(target.Y * TileSize),
|
|
}
|
|
|
|
direction := rl.Vector3Subtract(targetPos, p.PosActual)
|
|
distance := rl.Vector3Length(direction)
|
|
|
|
if distance > 1.0 {
|
|
p.IsMoving = true
|
|
} else {
|
|
p.IsMoving = false
|
|
}
|
|
|
|
// Update animation if controller exists
|
|
if p.AnimController != nil {
|
|
p.AnimController.Update(deltaTime, p.IsMoving)
|
|
p.AnimationFrame = p.AnimController.GetAnimFrame()
|
|
} else {
|
|
// Legacy animation update for backward compatibility
|
|
if p.IsMoving {
|
|
if !p.IsMoving {
|
|
p.AnimationFrame = 0
|
|
}
|
|
p.AnimationFrame += int32(deltaTime * 60)
|
|
} else {
|
|
wasMoving := p.IsMoving
|
|
if wasMoving {
|
|
p.AnimationFrame = 0
|
|
}
|
|
p.AnimationFrame += int32(deltaTime * 60)
|
|
}
|
|
}
|
|
|
|
if distance > 0 {
|
|
direction = rl.Vector3Scale(direction, p.Speed*deltaTime/distance)
|
|
}
|
|
|
|
if distance > 1.0 {
|
|
p.PosActual = rl.Vector3Add(p.PosActual, direction)
|
|
} else {
|
|
p.PosActual = targetPos
|
|
p.PosTile = target
|
|
if len(p.TargetPath) > 1 {
|
|
p.TargetPath = p.TargetPath[1:]
|
|
}
|
|
}
|
|
}
|
|
|
|
func NewPlayer(state *pb.PlayerState) *Player {
|
|
return &Player{
|
|
PosActual: rl.Vector3{
|
|
X: float32(state.X * TileSize),
|
|
Y: float32(state.Y * TileHeight),
|
|
Z: float32(state.Y * TileSize),
|
|
},
|
|
PosTile: Tile{X: int(state.X), Y: int(state.Y)},
|
|
Speed: 50.0,
|
|
ID: state.PlayerId,
|
|
IsMoving: false,
|
|
AnimationFrame: 0,
|
|
LastAnimUpdate: time.Now(),
|
|
LastUpdateTime: time.Now(),
|
|
InterpolationProgress: 1.0,
|
|
}
|
|
}
|
|
|
|
// InitializeAnimations sets up the animation controller for the player
|
|
func (p *Player) InitializeAnimations(animations AnimationSet) {
|
|
p.AnimController = NewAnimationController(animations)
|
|
}
|
|
|
|
func (p *Player) UpdatePosition(state *pb.PlayerState, tickRate time.Duration) {
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
|
|
targetTile := Tile{X: int(state.X), Y: int(state.Y)}
|
|
if p.PosTile != targetTile {
|
|
p.PosTile = targetTile
|
|
p.LastUpdateTime = time.Now()
|
|
p.InterpolationProgress = 0
|
|
p.TargetPath = []Tile{targetTile}
|
|
}
|
|
}
|
|
|
|
func (p *Player) ForceResync(state *pb.PlayerState) {
|
|
// Keep this lock since it's called from the network goroutine
|
|
p.Lock()
|
|
defer p.Unlock()
|
|
|
|
p.PosTile = Tile{X: int(state.X), Y: int(state.Y)}
|
|
p.PosActual = rl.Vector3{
|
|
X: float32(state.X * TileSize),
|
|
Y: float32(state.Y * TileHeight),
|
|
Z: float32(state.Y * TileSize),
|
|
}
|
|
p.TargetPath = nil
|
|
p.ActionQueue = nil
|
|
p.InterpolationProgress = 1.0
|
|
}
|