project-ely/internal/game/entity/penguin.go

219 lines
4.1 KiB
Go

package entity
import (
"context"
"log"
"math"
"time"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/channels"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/vector"
"github.com/veandco/go-sdl2/sdl"
)
type penguin struct {
ctx context.Context
timeout time.Duration
commandChan chan EntityCommand
// Animation parameters
currentAnimation *entityAnimation
animationStep int32
facingRight bool
updateAnimation bool
// Physical parameters
worldPosition *vector.Vec2F
direction *vector.Vec2F
speed float64
}
func NewPenguin(ctx context.Context, renderer *sdl.Renderer) *penguin {
commandChan := make(chan EntityCommand, 10)
defaultTimeout := time.Second
position := vector.Vec2F{}
direction := vector.Vec2F{}
speed := 1.0
p := penguin{
ctx: ctx,
commandChan: commandChan,
timeout: defaultTimeout,
currentAnimation: penguinAnimations[PENGUIN_DEFAULT],
animationStep: 0,
facingRight: true,
worldPosition: &position,
speed: speed,
direction: &direction,
}
return &p
}
func (p *penguin) Draw() error {
step := p.animationStep / p.currentAnimation.speed
//windowPosition := worldPosToWindowPos()
windowPosition := &sdl.Point{
X: int32(math.Round(p.worldPosition.X)),
Y: int32(math.Round(p.worldPosition.Y)),
}
err := p.currentAnimation.Draw(step, windowPosition)
if err != nil {
return err
}
p.animationStep = 1 + p.animationStep
return nil
}
func (p *penguin) SetPosition(vec *vector.Vec2F) {
p.worldPosition = vec
}
func (p *penguin) SetAnimation(id int) {
a, exists := penguinAnimations[id]
if !exists {
log.Printf("animation does not exist: %v", id)
a = penguinAnimations[PENGUIN_DEFAULT]
}
if a != p.currentAnimation {
p.animationStep = 0
}
p.currentAnimation = a
}
func (p *penguin) MoveX(x float64) {
p.direction.X = x
p.direction.Normalize()
p.updateAnimation = true
}
func (p *penguin) MoveY(y float64) {
// (0,0) is the top left, so negative y moves up
p.direction.Y = y
p.direction.Normalize()
p.updateAnimation = true
}
func (p *penguin) SetSpeed(s float64) {
p.speed = s
}
func (p *penguin) GetSpeed() float64 {
return p.speed
}
func (p *penguin) SetMoveAnimation() {
x := p.direction.X
y := p.direction.Y
if x == 0 && y == 0 {
// Stationary
if p.facingRight {
p.SetAnimation(PENGUIN_STATIONARY_RIGHT)
} else {
p.SetAnimation(PENGUIN_STATIONARY_LEFT)
}
} else {
// Moving
// Update which direction we're facing
if x > 0 {
p.facingRight = true
} else if x < 0 {
p.facingRight = false
}
// if x == 0, stay facing whatever direction we were previously facing
if p.facingRight {
p.SetAnimation(PENGUIN_WALK_RIGHT)
} else {
p.SetAnimation(PENGUIN_WALK_LEFT)
}
}
}
func (p *penguin) Update() error {
if p.updateAnimation {
p.SetMoveAnimation()
p.updateAnimation = false
}
x := p.direction.X * p.speed
y := p.direction.Y * p.speed
p.worldPosition.X += x
p.worldPosition.Y += y
return nil
}
func (p *penguin) GetCommandChan() chan EntityCommand {
return p.commandChan
}
func (p *penguin) Run() {
running := true
for running {
select {
case c := <-p.commandChan:
go func(cmd EntityCommand) {
p.HandleWithTimeout(cmd)
}(c)
case <-p.ctx.Done():
// Graceful shutdown
running = false
// Finish up anything in the queue
for c := range p.commandChan {
p.HandleWithTimeout(c)
}
log.Printf("entity shutdown\n")
}
}
}
func (p *penguin) HandleWithTimeout(c EntityCommand) {
err := channels.RunWithTimeout(p.timeout, func() error {
return p.Handle(c)
})
if err != nil {
log.Printf("%v\n", err)
}
}
func (p *penguin) Handle(c EntityCommand) error {
var err error
switch c.key {
case COMMAND_DRAW:
err = p.Draw()
if err != nil {
log.Printf("error drawing entity: %v", err)
}
case COMMAND_UPDATE:
err = p.Update()
if err != nil {
log.Printf("error updating entity: %v", err)
}
case COMMAND_MOVE_X:
p.MoveX(c.value)
case COMMAND_MOVE_Y:
p.MoveY(c.value)
case COMMAND_SET_SPEED:
p.SetSpeed(c.value)
default:
log.Printf("unknown entity command: %v", c.key)
}
return nil
}