package pacman import ( "time" "github.com/ChausseBenjamin/pacgo/internal/render" ) type Direction uint8 const ( UP Direction = iota DOWN LEFT RIGHT ) type Pacman struct { renderer *render.Renderer bright bool x float64 y float64 dir Direction refreshRate int // times/second the position is updated vSpeed float64 // tiles per second hSpeed float64 // tiles per second moveTicker *time.Ticker moveDone chan bool blinkRate float64 // stateChanges per second blinkTicker *time.Ticker blinkDone chan bool } func (p *Pacman) blink() { p.bright = !p.bright } func (p *Pacman) drawInstruction() render.DrawInstruction { return render.DrawInstruction{ X: int(p.x), Y: int(p.y), Content: p.Icon(), Decorators: []string{"\033[38;2;250;249;8m"}, } } // clear will remove the pacman from the screen. This is useful in two cases: // - Remove pacman's trail/previous position // - Remove pacman from the screen on death/level change/etc. // This function will not update pacman's position. This means it must be // called before updating the position. func (p *Pacman) clearInstruction() render.ClearInstruction { return render.ClearInstruction{ X: int(p.x), Y: int(p.y), Size: 1, } } // move will update pacman's position constantly // based on the direction it is facing. func (p *Pacman) move() { p.renderer.Push(p.clearInstruction()) switch p.dir { case UP: p.y -= (float64(p.vSpeed) * float64(p.refreshRate)) / 1000 case DOWN: p.y += (float64(p.vSpeed) * float64(p.refreshRate)) / 1000 case LEFT: p.x -= (float64(p.hSpeed) * float64(p.refreshRate)) / 1000 case RIGHT: p.x += (float64(p.hSpeed) * float64(p.refreshRate)) / 1000 } p.renderer.Push(p.drawInstruction()) } func (p *Pacman) Pos() (float64, float64) { return p.x, p.y } func (p *Pacman) Redirect(dir Direction) { p.dir = dir } func (p *Pacman) Icon() string { icns := map[Direction]map[bool]string{ // UP: {true: "V", false: "v"}, // DOWN: {true: "^", false: "A"}, // RIGHT: {true: "{", false: "<"}, // LEFT: {true: "}", false: ">"}, UP: {true: "", false: "󰬧"}, DOWN: {true: "", false: "󰬭"}, LEFT: {true: "", false: "󰬫"}, RIGHT: {true: "", false: "󰬩"}, } return icns[p.dir][p.bright] } func (p *Pacman) Start() { p.blinkDone = make(chan bool) p.moveDone = make(chan bool) // fmt.Println(time.Duration(1/p.blinkRate) * time.Second) blinkTicker := time.NewTicker(time.Duration(float64(time.Second) / p.blinkRate)) moveTicker := time.NewTicker(time.Duration(float64(time.Second) / float64(p.refreshRate))) go func() { for { select { case <-blinkTicker.C: p.blink() case <-moveTicker.C: p.move() case <-p.blinkDone: return case <-p.moveDone: return } } }() } func (p *Pacman) Stop() { p.blinkTicker.Stop() p.moveTicker.Stop() p.blinkDone <- true p.moveDone <- true } func NewPacman(x float64, y float64, rdr *render.Renderer) *Pacman { p := &Pacman{x: x, y: y} p.dir = RIGHT p.refreshRate = 200 p.vSpeed = 0.1 p.hSpeed = 0.2 p.blinkRate = 3 p.renderer = rdr go p.blink() go p.move() return p } // // Icons // pub const PACMAN_UP_ON :char = ''; // pub const PACMAN_DOWN_ON :char = ''; // pub const PACMAN_LEFT_ON :char = ''; // pub const PACMAN_RIGHT_ON :char = ''; // pub const GHOST_ON :char = '󰊠'; // pub const PACMAN_UP_OFF :char = '󰬧'; // pub const PACMAN_DOWN_OFF :char = '󰬭'; // pub const PACMAN_LEFT_OFF :char = '󰬩'; // pub const PACMAN_RIGHT_OFF :char = '󰬫'; // pub const GHOST_OFF :char = '󱙝';