140 lines
3.4 KiB
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
|
|
}
|