client/spectator.go

238 lines
5.6 KiB
Go
Raw Normal View History

package client
import (
2016-07-13 19:49:59 -07:00
"errors"
2015-08-31 21:55:18 -07:00
"fmt"
2014-05-12 22:11:44 -07:00
"math"
"hackerbots.us/server"
2016-07-13 19:49:59 -07:00
"github.com/nsf/termbox-go"
)
2016-07-13 19:49:59 -07:00
const d = 'v'
const u = '^'
const r = '>'
const l = '<'
// NewSpectator initializes and returns a *Spectator.
func NewSpectator(w, h float64) *Spectator {
return &Spectator{
StateStream: make(chan *server.Boardstate),
Die: make(chan struct{}),
width: w,
height: h,
}
}
// Spectator encodes a termbox ui.
type Spectator struct {
// max dimensions of field
width, height float64
// dimensions of the terminal window
viewX, viewY int
StateStream chan *server.Boardstate
2014-05-04 00:33:46 -07:00
2016-07-13 19:49:59 -07:00
// when closed will cause the Spectator to exit the render loop.
Die chan struct{}
// User, if populated, will return a stream of (ostensibly keyboard) events
// for use outside of the Spectator.
User chan termbox.Event
2014-05-04 00:33:46 -07:00
}
2016-07-13 19:49:59 -07:00
// SetIDs is implemented so Spectator can be used as a client.Player.
func (s Spectator) SetIDs(map[string]string) {}
// GetStats is implemented so Spectator can be used as a client.Player.
func (s Spectator) GetStats() map[string]server.StatsRequest { return nil }
// Update is implemented so Spectator can be used as a client.Player.
func (s Spectator) Update(bs *server.Boardstate) map[string]server.Instruction {
2015-08-31 21:48:25 -07:00
s.StateStream <- bs
return nil
}
2014-05-04 00:33:46 -07:00
2016-07-13 19:49:59 -07:00
// Spectate runs the termbox render loop.
func (s *Spectator) Spectate() error {
2014-05-04 00:33:46 -07:00
err := termbox.Init()
if err != nil {
2016-07-13 19:49:59 -07:00
return err
2014-05-04 00:33:46 -07:00
}
termbox.SetInputMode(termbox.InputMouse)
2014-05-04 00:33:46 -07:00
s.viewX, s.viewY = termbox.Size()
2015-08-31 21:48:25 -07:00
events := make(chan termbox.Event, 1024)
2014-05-04 00:33:46 -07:00
go func() {
for {
events <- termbox.PollEvent()
}
}()
termbox.HideCursor()
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
func() {
for {
select {
case event := <-events:
switch event.Type {
case termbox.EventKey:
switch event.Key {
case termbox.KeyCtrlZ, termbox.KeyCtrlC:
return
}
switch event.Ch {
case 'q':
return
2016-07-13 19:49:59 -07:00
case 'f':
termbox.SetCell(
20,
20,
'*',
termbox.ColorRed, termbox.ColorBlack,
)
2014-05-04 00:33:46 -07:00
case 'c':
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
}
case termbox.EventResize:
s.viewX, s.viewY = event.Width, event.Height
case termbox.EventError:
2016-07-13 19:49:59 -07:00
err = fmt.Errorf("Quitting because of termbox error:\n%v\n", event.Err)
return
2014-05-04 00:33:46 -07:00
}
if s.User != nil {
s.User <- event
}
2016-07-13 19:49:59 -07:00
case update := <-s.StateStream:
2014-05-04 00:33:46 -07:00
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
2016-07-13 19:49:59 -07:00
for _, obstacle := range update.Obstacles {
2014-05-04 00:33:46 -07:00
startX := int((obstacle.Bounds.A.X / s.width) * float64(s.viewX))
stopX := int((obstacle.Bounds.B.X / s.width) * float64(s.viewX))
startY := int((obstacle.Bounds.A.Y / s.height) * float64(s.viewY))
stopY := int((obstacle.Bounds.B.Y / s.height) * float64(s.viewY))
for x := startX; x < stopX; x++ {
for y := startY; y < stopY; y++ {
termbox.SetCell(
x,
s.viewY-y,
' ',
termbox.ColorBlack, termbox.ColorBlue,
)
}
}
}
2016-07-13 19:49:59 -07:00
for _, bot := range update.OtherRobots {
2015-08-31 21:48:25 -07:00
x := int((bot.Position.X / s.width) * float64(s.viewX))
y := int((bot.Position.Y / s.height) * float64(s.viewY))
var b rune
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
if bot.Heading.X > 0 {
2016-07-13 19:49:59 -07:00
b = r
2015-08-31 21:48:25 -07:00
} else {
2016-07-13 19:49:59 -07:00
b = l
2015-08-31 21:48:25 -07:00
}
} else {
if bot.Heading.Y > 0 {
2016-07-13 19:49:59 -07:00
b = u
2015-08-31 21:48:25 -07:00
} else {
2016-07-13 19:49:59 -07:00
b = d
2015-08-31 21:48:25 -07:00
}
}
c := termbox.ColorRed
if bot.Health <= 0 {
c = termbox.ColorBlack
}
termbox.SetCell(
x,
s.viewY-y,
b,
c, termbox.ColorBlack,
)
}
2016-07-13 19:49:59 -07:00
for _, p := range update.Projectiles {
2014-05-04 00:33:46 -07:00
x := int((p.Position.X / s.width) * float64(s.viewX))
y := int((p.Position.Y / s.height) * float64(s.viewY))
termbox.SetCell(
x,
s.viewY-y,
'·',
termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlack,
)
}
2016-07-13 19:49:59 -07:00
for _, splosion := range update.Splosions {
2014-05-12 22:11:44 -07:00
startX := int(((splosion.Position.X - float64(splosion.Radius)) / s.width) * float64(s.viewX))
startY := int(((splosion.Position.Y - float64(splosion.Radius)) / s.height) * float64(s.viewY))
stopX := int(((splosion.Position.X+float64(splosion.Radius))/s.width)*float64(s.viewX)) + 1
stopY := int(((splosion.Position.Y+float64(splosion.Radius))/s.height)*float64(s.viewY)) + 1
for x := startX; x < stopX; x++ {
for y := startY; y < stopY; y++ {
realX := float64(x) * s.width / float64(s.viewX)
realY := float64(y) * s.height / float64(s.viewY)
dX := realX - splosion.Position.X
dY := realY - splosion.Position.Y
curRad := math.Sqrt(dX*dX + dY*dY)
if curRad < float64(splosion.Radius) {
termbox.SetCell(
x,
s.viewY-y,
'·',
termbox.ColorYellow|termbox.AttrBold, termbox.ColorRed,
)
}
}
}
2014-05-04 00:33:46 -07:00
}
2016-07-13 19:49:59 -07:00
for _, bot := range update.MyRobots {
2015-08-31 21:59:21 -07:00
x := int((bot.Position.X / s.width) * float64(s.viewX))
y := int((bot.Position.Y / s.height) * float64(s.viewY))
var b rune
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
if bot.Heading.X > 0 {
2016-07-13 19:49:59 -07:00
b = r
2015-08-31 21:59:21 -07:00
} else {
2016-07-13 19:49:59 -07:00
b = l
2015-08-31 21:59:21 -07:00
}
} else {
if bot.Heading.Y > 0 {
2016-07-13 19:49:59 -07:00
b = u
2015-08-31 21:59:21 -07:00
} else {
2016-07-13 19:49:59 -07:00
b = d
2015-08-31 21:59:21 -07:00
}
}
c := termbox.ColorWhite
if bot.Health <= 0 {
c = termbox.ColorBlack
}
termbox.SetCell(
x,
s.viewY-y,
b,
c|termbox.AttrBold, termbox.ColorBlack,
)
}
2016-07-13 19:49:59 -07:00
err = termbox.Flush()
2014-05-13 00:23:14 -07:00
case <-s.Die:
2016-07-13 19:49:59 -07:00
err = errors.New("was told to die")
2014-05-13 00:23:14 -07:00
return
2014-05-04 00:33:46 -07:00
}
}
}()
termbox.Close()
2016-07-13 19:49:59 -07:00
return err
2014-05-04 00:33:46 -07:00
}