project-ely/internal/game/player/player.go

140 lines
3.4 KiB
Go

package player
import (
"context"
"log"
"sync"
"time"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/channels"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/command"
"github.com/veandco/go-sdl2/sdl"
)
// player represents a collection of stuff controlled by the user's input.
// It contains the camera used to view the world,
// the viewport for the part of the screen to draw to (splitscreen support),
// as well as the entity the player is currently controlling.
type player struct {
mx sync.RWMutex
ctx context.Context
timeout time.Duration
sdlKeyboardEventsChan chan sdl.KeyboardEvent
entityChan chan command.EntityCommand
keystates map[sdl.Keycode]bool
}
func NewPlayer(ctx context.Context) *player {
sdlKeyboardEventsChan := make(chan sdl.KeyboardEvent, 10)
defaultTimeout := time.Second
keystates := make(map[sdl.Keycode]bool)
p := player{
ctx: ctx,
timeout: defaultTimeout,
sdlKeyboardEventsChan: sdlKeyboardEventsChan,
keystates: keystates,
}
return &p
}
func (p *player) Cleanup() {
p.mx.Lock()
defer p.mx.Unlock()
close(p.sdlKeyboardEventsChan)
}
func (p *player) SetEntityChan(e chan command.EntityCommand) {
p.mx.Lock()
defer p.mx.Unlock()
p.entityChan = e
}
func (p *player) GetSdlEventsChan() chan sdl.KeyboardEvent {
p.mx.RLock()
defer p.mx.RUnlock()
return p.sdlKeyboardEventsChan
}
func (p *player) Run() {
running := true
for running {
select {
case e := <-p.sdlKeyboardEventsChan:
go func(event sdl.KeyboardEvent) {
p.HandleWithTimeout(event)
}(e)
case <-p.ctx.Done():
// Graceful shutdown
running = false
// Finish up anything in the queue
for e := range p.sdlKeyboardEventsChan {
p.HandleWithTimeout(e)
}
}
}
}
func (p *player) HandleWithTimeout(event sdl.KeyboardEvent) {
err := channels.RunWithTimeout(p.timeout, func() error {
return p.Handle(event)
})
if err != nil {
log.Printf("%v\n", err)
}
}
func (p *player) Handle(e sdl.KeyboardEvent) error {
p.mx.Lock()
defer p.mx.Unlock()
// Key states (just set a boolean whether the key is actively being pressed)
keystateChanged := false
if e.Type == sdl.KEYDOWN {
if !p.keystates[e.Keysym.Sym] {
keystateChanged = true
}
p.keystates[e.Keysym.Sym] = true
} else if e.Type == sdl.KEYUP {
if p.keystates[e.Keysym.Sym] {
keystateChanged = true
}
p.keystates[e.Keysym.Sym] = false
}
// Only send events to the entity if something actually changed
if !keystateChanged {
return nil
}
// Speed
if p.keystates[sdl.K_LSHIFT] {
p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 4)
} else {
p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 2)
}
// Move X
if p.keystates[sdl.K_d] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, 1.0)
} else if p.keystates[sdl.K_a] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, -1.0)
} else if !p.keystates[sdl.K_d] && !p.keystates[sdl.K_a] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, 0.0)
}
// Move Y
if p.keystates[sdl.K_w] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, -1.0)
} else if p.keystates[sdl.K_s] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 1.0)
} else if !p.keystates[sdl.K_w] && !p.keystates[sdl.K_s] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 0.0)
}
return nil
}