diff --git a/README.md b/README.md index 773f554..fa22bcf 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,14 @@ Run the following command to build a `boxcars` executable: Run `~/go/bin/boxcars` to play. -## Android +### Android *Coming soon* +## Gameplay notes + +- To bear off a piece, drag it from your home on to the bar. + ## Support Please share issues and suggestions [here](https://code.rocketnine.space/tslocum/boxcars/issues). @@ -35,4 +39,5 @@ Please share issues and suggestions [here](https://code.rocketnine.space/tslocum - [ebiten](https://github.com/hajimehoshi/ebiten) - 2D game engine - [draw2d](https://github.com/llgcode/draw2d) - 2D shape drawing +- [kibodo](https://code.rocketnine.space/tslocum/kibodo) - On-screen keyboard - [resize](https://github.com/nfnt/resize) - Image resizing diff --git a/client_web.go b/client_web.go new file mode 100644 index 0000000..4ab1653 --- /dev/null +++ b/client_web.go @@ -0,0 +1,13 @@ +// +build js,wasm + +package main + +import ( + "code.rocketnine.space/tslocum/fibs" +) + +func init() { + fibs.DefaultProxyAddress = "wss://fibsproxy.rocketnine.space" + + AutoWatch = true +} diff --git a/game/board.go b/game/board.go index eff2d20..d7c198e 100644 --- a/game/board.go +++ b/game/board.go @@ -17,6 +17,7 @@ import ( type stateUpdate struct { from int to int + s []string v []int } @@ -24,6 +25,8 @@ type board struct { x, y int w, h int + innerW, innerH int + op *ebiten.DrawImageOptions backgroundImage *ebiten.Image @@ -47,13 +50,19 @@ type board struct { overlapSize int lastDirection int - V []int + + s []string + v []int moveQueue chan *stateUpdate drawFrame chan bool debug int // Print and draw debug information + + Client *fibs.Client + + dragX, dragY int } func NewBoard() *board { @@ -69,7 +78,8 @@ func NewBoard() *board { }, spaces: make([][]*Sprite, 26), spaceRects: make([][4]int, 26), - V: make([]int, 42), + s: make([]string, 2), + v: make([]int, 42), moveQueue: make(chan *stateUpdate, 10), drawFrame: make(chan bool, 10), } @@ -270,9 +280,60 @@ func (b *board) draw(screen *ebiten.Image) { b.op.GeoM.Translate(float64(b.x), float64(b.y)) screen.DrawImage(b.backgroundImage, b.op) + if b.debug == 2 { + b.iterateSpaces(func(space int) { + x, y, w, h := b.spaceRect(space) + spaceImage := ebiten.NewImage(w, h) + if space%2 == 0 { + spaceImage.Fill(color.RGBA{50, 50, 50, 150}) + } else { + spaceImage.Fill(color.RGBA{255, 255, 255, 150}) + } + x, y = b.offsetPosition(x, y) + b.op.GeoM.Reset() + b.op.GeoM.Translate(float64(x), float64(y)) + screen.DrawImage(spaceImage, b.op) + }) + } + drawSprite := func(sprite *Sprite) { + x, y := float64(sprite.x), float64(sprite.y) + if !sprite.toStart.IsZero() { + progress := float64(time.Since(sprite.toStart)) / float64(sprite.toTime) + if x == float64(sprite.toX) && y == float64(sprite.toY) { + sprite.toStart = time.Time{} + sprite.x = sprite.toX + sprite.y = sprite.toY + } else { + if x < float64(sprite.toX) { + x += (float64(sprite.toX) - x) * progress + if x > float64(sprite.toX) { + x = float64(sprite.toX) + } + } else if x > float64(sprite.toX) { + x -= (x - float64(sprite.toX)) * progress + if x < float64(sprite.toX) { + x = float64(sprite.toX) + } + } + + if y < float64(sprite.toY) { + y += (float64(sprite.toY) - y) * progress + if y > float64(sprite.toY) { + y = float64(sprite.toY) + } + } else if y > float64(sprite.toY) { + y -= (y - float64(sprite.toY)) * progress + if y < float64(sprite.toY) { + y = float64(sprite.toY) + } + } + } + // Schedule another frame + ebiten.ScheduleFrame() + } b.op.GeoM.Reset() - b.op.GeoM.Translate(float64(sprite.x), float64(sprite.y)) + b.op.GeoM.Translate(x, y) if sprite.colorWhite { screen.DrawImage(imgCheckerWhite, b.op) @@ -314,6 +375,63 @@ func (b *board) draw(screen *ebiten.Image) { } }) + // Draw opponent name and dice + + borderSize := b.horizontalBorderSize + if borderSize > b.barWidth/2 { + borderSize = b.barWidth / 2 + } + + playerColor := color.White + opponentColor := color.Black + if b.v[fibs.StatePlayerColor] == -1 { + playerColor = color.Black + opponentColor = color.White + } + + if b.v[fibs.StateOpponentDice1] > 0 { + label := fmt.Sprintf("%s %d %d", b.s[fibs.StateOpponentName], b.v[fibs.StateOpponentDice1], b.v[fibs.StateOpponentDice2]) + + bounds := text.BoundString(mplusNormalFont, label) + img := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2) + text.Draw(img, label, mplusNormalFont, 0, bounds.Dy(), opponentColor) + + x := ((b.innerW - borderSize) / 4) - (bounds.Dx() / 2) + y := (b.innerH / 2) - (bounds.Dy() / 2) + x, y = b.offsetPosition(x, y) + b.op.GeoM.Reset() + b.op.GeoM.Translate(float64(x), float64(y)) + screen.DrawImage(img, b.op) + } + + // Draw player name and dice + + if b.v[fibs.StatePlayerDice1] > 0 { + label := fmt.Sprintf("%s %d %d", b.s[fibs.StatePlayerName], b.v[fibs.StatePlayerDice1], b.v[fibs.StatePlayerDice2]) + + bounds := text.BoundString(mplusNormalFont, label) + img := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2) + text.Draw(img, label, mplusNormalFont, 0, bounds.Dy(), playerColor) + + x := ((b.innerW / 4) * 3) - (bounds.Dx() / 2) + y := (b.innerH / 2) - (bounds.Dy() / 2) + x, y = b.offsetPosition(x, y) + b.op.GeoM.Reset() + b.op.GeoM.Translate(float64(x), float64(y)) + screen.DrawImage(img, b.op) + } + + /* TODO centering issue + img := ebiten.NewImage(115, 20) + img.Fill(color.White) + x := (((b.innerW - borderSize) / 2) / 2) - (115 / 2) + y := (b.innerH / 2) - (20 / 2) + x = (b.innerW - borderSize) / 4 + x, y = b.offsetPosition(x, y) + b.op.GeoM.Reset() + b.op.GeoM.Translate(float64(x), float64(y)) + screen.DrawImage(img, b.op)*/ + // Draw moving sprite if b.moving != nil { drawSprite(b.moving) @@ -328,22 +446,14 @@ func (b *board) draw(screen *ebiten.Image) { b.iterateSpaces(func(space int) { x, y, w, h := b.spaceRect(space) spaceImage := ebiten.NewImage(w, h) - if space%2 == 0 { - spaceImage.Fill(color.RGBA{0, 0, 0, 150}) - } else { - spaceImage.Fill(color.RGBA{255, 255, 255, 150}) - } - br := "" if b.bottomRow(space) { br = "B" } - ebitenutil.DebugPrint(spaceImage, fmt.Sprintf(" %d %s", space, br)) - x, y = b.offsetPosition(x, y) - b.op.GeoM.Reset() + b.op.GeoM.Scale(2, 2) b.op.GeoM.Translate(float64(x), float64(y)) screen.DrawImage(spaceImage, b.op) }) @@ -390,6 +500,13 @@ func (b *board) setRect(x, y, w, h int) { b.horizontalBorderSize = 0 } + borderSize := b.horizontalBorderSize + if borderSize > b.barWidth/2 { + borderSize = b.barWidth / 2 + } + b.innerW = b.w - (b.horizontalBorderSize * 2) + b.innerH = b.h - (b.verticalBorderSize * 2) + loadAssets(b.spaceWidth) for i := 0; i < b.Sprites.num; i++ { @@ -422,6 +539,8 @@ func (b *board) positionCheckers() { s.x += (w - s.w) / 2 } } + + b.ScheduleFrame() } func (b *board) spriteAt(x, y int) *Sprite { @@ -447,21 +566,14 @@ func (b *board) spaceAt(x, y int) int { return -1 } -// TODO move to fibs library func (b *board) iterateSpaces(f func(space int)) { - if b.V[fibs.StateDirection] == 1 { - for space := 0; space <= 25; space++ { - f(space) - } - return - } - for space := 25; space >= 0; space-- { + for space := 0; space <= 25; space++ { f(space) } } func (b *board) translateSpace(space int) int { - if b.V[fibs.StateDirection] == -1 { + if b.v[fibs.StateDirection] == -1 { // Spaces range from 24 - 1. if space == 0 || space == 25 { space = 25 - space @@ -526,7 +638,7 @@ func (b *board) bottomRow(space int) bool { bottomStart := 1 bottomEnd := 12 bottomBar := 25 - if b.V[fibs.StateDirection] == 1 { + if b.v[fibs.StateDirection] == 1 { bottomStart = 13 bottomEnd = 24 bottomBar = 0 @@ -562,12 +674,13 @@ func (b *board) stackSpaceRect(space int, stack int) (x, y, w, h int) { return x, y, w, h } -func (b *board) SetState(v []int) { - b.moveQueue <- &stateUpdate{-1, -1, v} +func (b *board) SetState(s []string, v []int) { + // TODO setstate include playernames, use string? + b.moveQueue <- &stateUpdate{-1, -1, s, v} } func (b *board) ProcessState() { - v := b.V + v := b.v if b.lastDirection != v[fibs.StateDirection] { b.setSpaceRects() @@ -578,21 +691,26 @@ func (b *board) ProcessState() { b.spaces = make([][]*Sprite, 26) for space := 0; space < 26; space++ { spaceValue := v[fibs.StateBoardSpace0+space] - if spaceValue == 0 { - continue - } white := spaceValue > 0 - // TODO reverse bar spaces - always? - // TODO take direction into account + if spaceValue == 0 { + white = v[fibs.StatePlayerColor] == 1 + } abs := spaceValue if abs < 0 { abs *= -1 } - for i := 0; i < abs; i++ { + var preMovesTo = b.Client.Board.Premoveto[space] + var preMovesFrom = b.Client.Board.Premovefrom[space] + + for i := 0; i < (abs+preMovesTo)-preMovesFrom; i++ { s := b.newSprite(white) + if i >= abs { + s.colorWhite = v[fibs.StatePlayerColor] == 1 + // TODO draw special premove overlay? + } b.spaces[space] = append(b.spaces[space], s) b.Sprites.sprites = append(b.Sprites.sprites, s) } @@ -602,9 +720,13 @@ func (b *board) ProcessState() { b.positionCheckers() } -func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) { - moveSize := 1 - moveDelay := time.Duration(1/speed) * time.Millisecond +func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int, pause bool) { + //moveSize := 1 + //moveDelay := time.Duration(1/speed) * time.Millisecond + moveTime := time.Second / time.Duration(speed) + pauseTime := 750 * time.Millisecond + + b.moving = sprite space := from for { @@ -616,95 +738,76 @@ func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) { space-- } + var diag bool + // Go to bar or home immediately - if from == 0 || from == 25 || to == 0 || to == 25 { + // TODO always enabled + if true || from == 0 || from == 25 || to == 0 || to == 25 { space = to + diag = true } stack := len(b.spaces[space]) if stack == 1 && sprite.colorWhite != b.spaces[space][0].colorWhite { stack = 0 // Hit + } else if space != to { + stack++ } x, y, _, _ := b.stackSpaceRect(space, stack) x, y = b.offsetPosition(x, y) cy := y - if cy > sprite.y == b.bottomRow(space) { + if cy > sprite.y == b.bottomRow(space) && !diag { cy = sprite.y } - if sprite.x != x { - // Center - for { - if sprite.y == cy { - break - } - if sprite.y < cy { - sprite.y += moveSize - if sprite.y > cy { - sprite.y = cy - } - } else if sprite.y > cy { - sprite.y -= moveSize - if sprite.y < cy { - sprite.y = cy - } - } - b.ScheduleFrame() - time.Sleep(moveDelay) - } - for { - if sprite.x == x { - break - } - if sprite.x < x { - sprite.x += moveSize - if sprite.x > x { - sprite.x = x - } - } else if sprite.x > x { - sprite.x -= moveSize - if sprite.x < x { - sprite.x = x - } - } - b.ScheduleFrame() - time.Sleep(moveDelay / 2) - } - } - for { - if sprite.x == x && sprite.y == y { - break + if diag { + sprite.toX = x + sprite.toY = y + sprite.toTime = moveTime + sprite.toStart = time.Now() + ebiten.ScheduleFrame() + time.Sleep(sprite.toTime) + sprite.x = x + sprite.y = y + } else { + if sprite.y != cy { + sprite.toX = sprite.x + sprite.toY = cy + sprite.toTime = moveTime / 4 + sprite.toStart = time.Now() + ebiten.ScheduleFrame() + time.Sleep(sprite.toTime) + sprite.toStart = time.Time{} + sprite.y = cy } - if sprite.x < x { - sprite.x += moveSize - if sprite.x > x { - sprite.x = x - } - } else if sprite.x > x { - sprite.x -= moveSize - if sprite.x < x { - sprite.x = x - } + + if sprite.x != x { + sprite.toX = x + sprite.toY = sprite.y + sprite.toTime = moveTime / 4 + sprite.toStart = time.Now() + ebiten.ScheduleFrame() + time.Sleep(sprite.toTime) + sprite.toStart = time.Time{} + sprite.x = x } - if sprite.y < y { - sprite.y += moveSize - if sprite.y > y { - sprite.y = y - } - } else if sprite.y > y { - sprite.y -= moveSize - if sprite.y < y { - sprite.y = y - } + + if sprite.x != x || sprite.y != y { + sprite.toX = x + sprite.toY = y + sprite.toTime = moveTime / 4 + sprite.toStart = time.Now() + ebiten.ScheduleFrame() + time.Sleep(sprite.toTime) + sprite.toStart = time.Time{} + sprite.x = x + sprite.y = y } - b.ScheduleFrame() - time.Sleep(moveDelay) } } - // TODO do not add bear off pieces b.spaces[to] = append(b.spaces[to], sprite) for i, s := range b.spaces[from] { if s == sprite { @@ -716,13 +819,41 @@ func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) { b.ScheduleFrame() - time.Sleep(time.Second) + if !pause { + return + } + time.Sleep(pauseTime) } func (b *board) handlePieceMoves() { for u := range b.moveQueue { if u.from == -1 || u.to == -1 { - b.V = u.v + same := true + if len(b.s) == len(u.s) { + for i := range b.s { + if b.s[i] != u.s[i] { + same = false + break + } + } + } else { + same = false + } + if len(b.v) == len(u.v) { + for i := range b.v { + if b.v[i] != u.v[i] { + same = false + break + } + } + } else { + same = false + } + if same { + continue + } + b.s = u.s + b.v = u.v b.ProcessState() continue } @@ -743,16 +874,16 @@ func (b *board) handlePieceMoves() { } } - b._movePiece(sprite, from, to, 1) + b._movePiece(sprite, from, to, 1, moveAfter == nil) if moveAfter != nil { toBar := 0 - if b.V[fibs.StateTurn] == b.V[fibs.StatePlayerColor] { - toBar = 25 // TODO how is this determined? + if b.v[fibs.StateDirection] == -1 { + toBar = 25 } - if b.V[fibs.StateDirection] == -1 { - toBar = 25 - toBar + if b.v[fibs.StateTurn] != b.v[fibs.StatePlayerColor] { + toBar = 25 - toBar // TODO how is this determined? } - b._movePiece(moveAfter, to, toBar, 2) + b._movePiece(moveAfter, to, toBar, 1, true) } b.positionCheckers() @@ -761,7 +892,7 @@ func (b *board) handlePieceMoves() { // Do not call directly func (b *board) movePiece(from int, to int) { - b.moveQueue <- &stateUpdate{from, to, nil} + b.moveQueue <- &stateUpdate{from, to, nil, nil} } func (b *board) update() { @@ -781,6 +912,9 @@ func (b *board) update() { b.touchIDs = inpututil.AppendJustPressedTouchIDs(b.touchIDs[:0]) for _, id := range b.touchIDs { x, y := ebiten.TouchPosition(id) + + b.dragX, b.dragY = x, y + s := b.spriteAt(x, y) if s != nil { b.dragging = s @@ -792,6 +926,12 @@ func (b *board) update() { x, y := ebiten.CursorPosition() if b.dragTouchId != -1 { x, y = ebiten.TouchPosition(b.dragTouchId) + + if x != 0 || y != 0 { // 0,0 is returned when the touch is released + b.dragX, b.dragY = x, y + } else { + x, y = b.dragX, b.dragY + } } var dropped *Sprite @@ -810,12 +950,16 @@ func (b *board) update() { index := b.spaceAt(x, y) if index >= 0 { - for space, pieces := range b.spaces { - for stackIndex, piece := range pieces { - if piece == dropped { - b.spaces[space] = append(b.spaces[space][:stackIndex], b.spaces[space][stackIndex+1:]...) - b.spaces[index] = append(b.spaces[index], dropped) - break + if b.Client != nil { + for space, pieces := range b.spaces { + for _, piece := range pieces { + if piece == dropped { + if space != index { + b.Client.Board.AddPreMove(space, index) + b.ProcessState() + } + break + } } } } diff --git a/game/game.go b/game/game.go index d89efd9..a4672a5 100644 --- a/game/game.go +++ b/game/game.go @@ -113,6 +113,10 @@ type Sprite struct { h int x int y int + toStart time.Time + toTime time.Duration + toX int + toY int colorWhite bool } @@ -147,6 +151,7 @@ type Game struct { screenW, screenH int drawBuffer bytes.Buffer + lastDraw time.Time spinnerIndex int @@ -157,6 +162,7 @@ type Game struct { usernameConfirmed bool Watch bool + TV bool Client *fibs.Client @@ -200,8 +206,12 @@ func NewGame() *Game { func (g *Game) handleEvents() { for e := range g.Client.Event { switch event := e.(type) { + case *fibs.EventBoardState: + g.Board.SetState(event.S, event.V) case *fibs.EventMove: g.Board.movePiece(event.From, event.To) + case *fibs.EventDraw: + g.Board.ProcessState() } } } @@ -214,17 +224,21 @@ func (g *Game) Connect() { address = defaultServerAddress } g.Client = fibs.NewClient(address, g.Username, g.Password) + g.Board.Client = g.Client go g.handleEvents() c := g.Client - if g.Watch { + if g.TV { go func() { - time.Sleep(1 * time.Second) - c.WatchRandomGame() - - go g.renderLoop() + time.Sleep(time.Second) + c.Out <- []byte("tv") + }() + } else if g.Watch { + go func() { + time.Sleep(time.Second) + c.Out <- []byte("watch") }() } @@ -236,15 +250,6 @@ func (g *Game) Connect() { }() } -func (g *Game) renderLoop() { - t := time.NewTicker(time.Second) - for range t.C { - gameBoard := g.Board - v := g.Client.Board.GetIntState() - gameBoard.SetState(v) - } -} - func (g *Game) leftTouched() bool { for _, id := range g.touchIDs { x, _ := ebiten.TouchPosition(id) @@ -322,7 +327,11 @@ func (g *Game) Update() error { // Called by ebiten only when input occurs g.inputBuffer += string(input.Rune) continue } - if input.Key == ebiten.KeyEnter { + if input.Key == ebiten.KeyBackspace { + if len(g.inputBuffer) > 0 { + g.inputBuffer = g.inputBuffer[:len(g.inputBuffer)-1] + } + } else if input.Key == ebiten.KeyEnter { g.inputBuffer += "\n" } } @@ -381,6 +390,12 @@ func (g *Game) Update() error { // Called by ebiten only when input occurs } func (g *Game) Draw(screen *ebiten.Image) { + frameTime := time.Second / 175 + if time.Since(g.lastDraw) < frameTime { + time.Sleep(time.Until(g.lastDraw.Add(frameTime))) + } + g.lastDraw = time.Now() + screen.Fill(color.RGBA{0, 102, 51, 255}) // Log in screen diff --git a/go.mod b/go.mod index 6168c40..7a6c87d 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,24 @@ module code.rocketnine.space/tslocum/boxcars go 1.17 require ( - code.rocketnine.space/tslocum/fibs v0.0.0-00010101000000-000000000000 - code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56 - github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5 - github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a + code.rocketnine.space/tslocum/kibodo v0.0.0-20210908010225-5e1a355e9061 + github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.13 + github.com/llgcode/draw2d v0.0.0-20210904075650-80aa0a2a901d github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d ) require ( + code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/klauspost/compress v1.13.5 // indirect github.com/reiver/go-oi v1.0.0 // indirect github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e // indirect golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect - golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect + golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect golang.org/x/text v0.3.7 // indirect + nhooyr.io/websocket v1.8.7 // indirect ) - -replace code.rocketnine.space/tslocum/fibs => /home/trevor/programming/fibs diff --git a/go.sum b/go.sum index 0780a39..59f1ccb 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,29 @@ -code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56 h1:+KVT4Zw9CEQkxdNP81wuTvX3EHRPpPUQFwn6UVLoMFY= -code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56/go.mod h1:nWGK8LvmYgMZQcwGMYOOuZ19VsVYL5E1hREEJ2gV46M= +code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238 h1:l6mAo+aReaHfL07SF+MWxOAmNVl1ShXhoWTe8HqI/+c= +code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238/go.mod h1:f7Cz0zJWOnJ9DW7R/GTuE8z5XWQtkbXkKpksW9SDu2c= +code.rocketnine.space/tslocum/kibodo v0.0.0-20210908010225-5e1a355e9061/go.mod h1:AWfFB/VvYWk+Lugwdpsko9hq8eMmJIgJx2JFSpVewNw= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-gl/gl v0.0.0-20180407155706-68e253793080/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= -github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326 h1:QqWaXlVeUGwSH7hO8giZP2Y06Qjl1LWR+FWC22YQsU8= github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/hajimehoshi/bitmapfont/v2 v2.1.3 h1:JefUkL0M4nrdVwVq7MMZxSTh6mSxOylm+C4Anoucbb0= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= -github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5 h1:fUSKz2wvklV02UTmBXXDlNKc6molRGUu5O8b80AvEa4= -github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5/go.mod h1:B4Cje+Kb1ZjztrKFPaiMWOGXO3Vp8u+zIBdxkZqkyD4= +github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.13/go.mod h1:44O6eBPGyRv8YctRbfzaqUH2sek5UdXh0aLWOP02ELI= github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= @@ -19,35 +31,43 @@ github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2 github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a h1:gl2CmDrcVtYJY4YBKfNdpa4Igj3iNIBsaC3m8TQipYw= -github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA= -github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb h1:61ndUreYSlWFeCY44JxDDkngVoI7/1MVhEl98Nm0KOk= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/llgcode/draw2d v0.0.0-20210904075650-80aa0a2a901d/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA= github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM= github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI= github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas= github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= -golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= +golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -55,27 +75,34 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/main.go b/main.go index c10ea2a..d7eabea 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,8 @@ const ( screenHeight = 768 ) +var AutoWatch bool // WASM only + func main() { ebiten.SetWindowTitle("Boxcars") ebiten.SetWindowSize(screenWidth, screenHeight) @@ -38,8 +40,13 @@ func main() { flag.StringVar(&g.Password, "password", "", "Password") flag.StringVar(&g.ServerAddress, "address", "fibs.com:4321", "Server address") flag.BoolVar(&g.Watch, "watch", false, "Watch random game") + flag.BoolVar(&g.TV, "tv", false, "Watch random games continuously") flag.Parse() + if AutoWatch { + g.Watch = true + } + // Auto-connect if g.Username != "" && g.Password != "" { g.Connect() @@ -60,10 +67,6 @@ func main() { g.Client.Out <- append(scanner.Bytes()) } - - if scanner.Err() != nil { - // TODO - } }() sigc := make(chan os.Signal, 1)