Support logging in

This commit is contained in:
Trevor Slocum 2021-08-25 20:30:04 -07:00
parent 63d44b37af
commit 23fba33928
5 changed files with 746 additions and 183 deletions

View file

@ -1,40 +1,57 @@
package game
import (
"fmt"
"image"
"image/color"
"math/rand"
"github.com/llgcode/draw2d/draw2dimg"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"time"
"code.rocketnine.space/tslocum/fibs"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/llgcode/draw2d/draw2dimg"
)
type stateUpdate struct {
from int
to int
v []int
}
type board struct {
Sprites *Sprites
op *ebiten.DrawImageOptions
spaces [][]*Sprite
backgroundImage *ebiten.Image
dragging *Sprite
dragTouchId ebiten.TouchID
touchIDs []ebiten.TouchID
x, y int
w, h int
spaceWidth int // spaceWidth is also the width and height of checkers
op *ebiten.DrawImageOptions
backgroundImage *ebiten.Image
Sprites *Sprites
spaces [][]*Sprite // Space contents
spaceRects [][4]int
dragging *Sprite
moving *Sprite // Moving automatically
dragTouchId ebiten.TouchID
touchIDs []ebiten.TouchID
spaceWidth int
barWidth int
triangleOffset float64
horizontalBorderSize int
verticalBorderSize int
overlapSize int
lastDirection int
V []int
moveQueue chan *stateUpdate
debug int // Print and draw debug information
}
func NewBoard() *board {
@ -48,27 +65,31 @@ func NewBoard() *board {
sprites: make([]*Sprite, 30),
num: 30,
},
spaces: make([][]*Sprite, 26),
spaces: make([][]*Sprite, 26),
spaceRects: make([][4]int, 26),
V: make([]int, 42),
moveQueue: make(chan *stateUpdate, 10),
}
for i := range b.Sprites.sprites {
s := &Sprite{}
r := rand.Intn(2)
s.colorWhite = r != 1
s.w, s.h = imgCheckerWhite.Size()
s := b.newSprite(i%2 == 1)
b.Sprites.sprites[i] = s
space := (i % 24) + 1
if i > 25 {
space = 3
space := i
if space > 25 {
if space%2 == 0 {
space = 0
} else {
space = 25
}
}
b.spaces[space] = append(b.spaces[space], s)
}
go b.handlePieceMoves()
b.op = &ebiten.DrawImageOptions{}
b.dragTouchId = -1
@ -76,13 +97,11 @@ func NewBoard() *board {
return b
}
// relX, relY
func (b *board) spacePosition(index int) (int, int) {
if index <= 12 {
return b.spaceWidth * (index - 1), 0
}
// TODO add innerW innerH
return b.spaceWidth * (index - 13), (b.h - (b.verticalBorderSize)*2) - b.spaceWidth
func (b *board) newSprite(white bool) *Sprite {
s := &Sprite{}
s.colorWhite = white
s.w, s.h = imgCheckerWhite.Size()
return s
}
func (b *board) updateBackgroundImage() {
@ -104,7 +123,7 @@ func (b *board) updateBackgroundImage() {
img = ebiten.NewImageFromImage(box)
img.Fill(borderColor)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(b.horizontalBorderSize-borderSize), float64(b.verticalBorderSize))
b.op.GeoM.Translate(float64(b.horizontalBorderSize-borderSize), 0)
b.backgroundImage.DrawImage(img, b.op)
// Face
@ -128,7 +147,7 @@ func (b *board) updateBackgroundImage() {
// Draw triangles
for i := 0; i < 2; i++ {
triangleTip := float64(b.h / 2)
triangleTip := float64((b.h - (b.verticalBorderSize * 2)) / 2)
if i == 0 {
triangleTip -= b.triangleOffset
} else {
@ -171,8 +190,7 @@ func (b *board) draw(screen *ebiten.Image) {
b.op.GeoM.Translate(float64(b.x), float64(b.y))
screen.DrawImage(b.backgroundImage, b.op)
for i := 0; i < b.Sprites.num; i++ {
sprite := b.Sprites.sprites[i]
drawSprite := func(sprite *Sprite) {
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(sprite.x), float64(sprite.y))
@ -182,35 +200,112 @@ func (b *board) draw(screen *ebiten.Image) {
screen.DrawImage(imgCheckerBlack, b.op)
}
}
b.iterateSpaces(func(space int) {
var numPieces int
for _, sprite := range b.spaces[space] {
if sprite == b.dragging || sprite == b.moving {
continue
}
numPieces++
drawSprite(sprite)
if numPieces > 5 {
label := fmt.Sprintf("%d", numPieces)
labelColor := color.RGBA{255, 255, 255, 255}
if sprite.colorWhite {
labelColor = color.RGBA{0, 0, 0, 255}
}
bounds := text.BoundString(mplusNormalFont, label)
overlayImage := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2)
text.Draw(overlayImage, label, mplusNormalFont, 0, bounds.Dy(), labelColor)
x, y, w, h := b.stackSpaceRect(space, numPieces)
x += (w / 2) - (bounds.Dx() / 2)
y += (h / 2) - (bounds.Dy() / 2)
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(overlayImage, b.op)
}
}
})
// Draw moving sprite
if b.moving != nil {
drawSprite(b.moving)
}
// Draw dragged sprite
if b.dragging != nil {
drawSprite(b.dragging)
}
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{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.Translate(float64(x), float64(y))
screen.DrawImage(spaceImage, b.op)
})
}
}
func (b *board) setRect(x, y, w, h int) {
if b.x == x && b.y == y && b.w == w && b.h == h {
return
}
const stackAllowance = 0.97 // TODO configurable
b.x, b.y, b.w, b.h = x, y, w, h
b.horizontalBorderSize = 0
b.triangleOffset = float64(b.h) / 30
b.triangleOffset = float64(b.h-(b.verticalBorderSize*2)) / 33
for {
b.verticalBorderSize = 0 // TODO configurable
b.verticalBorderSize = 7 // TODO configurable
b.spaceWidth = (b.w - (b.horizontalBorderSize * 2)) / 13
b.barWidth = b.spaceWidth
b.overlapSize = (((b.h - (b.verticalBorderSize * 2)) - (int(b.triangleOffset) * 2)) / 2) / 5
if b.overlapSize >= b.spaceWidth {
b.overlapSize = b.spaceWidth
o := int(float64(b.spaceWidth) * stackAllowance)
if b.overlapSize >= o {
b.overlapSize = o
break
}
b.horizontalBorderSize++
}
extraSpace := b.w - (b.spaceWidth * 12)
largeBarWidth := int(float64(b.spaceWidth) * 1.25)
if extraSpace >= largeBarWidth {
b.barWidth = largeBarWidth
}
// TODO barwidth in calcs is wrong
b.horizontalBorderSize = ((b.w - (b.spaceWidth * 12)) - b.barWidth) / 2
if b.horizontalBorderSize < 0 {
b.horizontalBorderSize = 0
@ -222,6 +317,7 @@ func (b *board) setRect(x, y, w, h int) {
s.w, s.h = imgCheckerWhite.Size()
}
b.setSpaceRects()
b.updateBackgroundImage()
b.positionCheckers()
}
@ -231,7 +327,7 @@ func (b *board) offsetPosition(x, y int) (int, int) {
}
func (b *board) positionCheckers() {
for space := 1; space < 25; space++ {
for space := 0; space < 26; space++ {
sprites := b.spaces[space]
for i := range sprites {
@ -240,33 +336,327 @@ func (b *board) positionCheckers() {
continue
}
x, y := b.spacePosition(space)
x, y, w, _ := b.stackSpaceRect(space, i)
s.x, s.y = b.offsetPosition(x, y)
if (space > 6 && space < 13) || (space > 18 && space < 25) {
s.x += b.barWidth
}
s.x += (b.spaceWidth - s.w) / 2
s.y += i * b.overlapSize
// Center piece in space
s.x += (w - s.w) / 2
}
}
}
func (b *board) spriteAt(x, y int) *Sprite {
for i := 0; i < b.Sprites.num; i++ {
s := b.Sprites.sprites[i]
if x >= s.x && y >= s.y && x <= s.x+s.w && y <= s.y+s.h {
// Bring sprite to front
b.Sprites.sprites = append(b.Sprites.sprites[:i], b.Sprites.sprites[i+1:]...)
b.Sprites.sprites = append(b.Sprites.sprites, s)
space := b.spaceAt(x, y)
if space == -1 {
return nil
}
pieces := b.spaces[space]
if len(pieces) == 0 {
return nil
}
return pieces[len(pieces)-1]
}
return s
func (b *board) spaceAt(x, y int) int {
for i := 0; i < 26; i++ {
sx, sy, sw, sh := b.spaceRect(i)
sx, sy = b.offsetPosition(sx, sy)
if x >= sx && x <= sx+sw && y >= sy && y <= sy+sh {
return i
}
}
return nil
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-- {
f(space)
}
}
func (b *board) translateSpace(space int) int {
if b.V[fibs.StateDirection] == -1 {
// Spaces range from 24 - 1.
if space == 0 || space == 25 {
space = 25 - space
} else if space <= 12 {
space = 12 + space
} else {
space = space - 12
}
}
return space
}
func (b *board) setSpaceRects() {
var x, y, w, h int
for i := 0; i < 26; i++ {
trueSpace := i
space := b.translateSpace(i)
if !b.bottomRow(trueSpace) {
y = 0
} else {
y = (b.h / 2) - b.verticalBorderSize
}
w = b.spaceWidth
var hspace int // horizontal space
var add int
if space == 0 {
hspace = 6
w = b.barWidth
} else if space == 25 {
hspace = 6
w = b.barWidth
} else if space <= 6 {
hspace = space - 1
} else if space <= 12 {
hspace = space - 1
add = b.barWidth
} else if space <= 18 {
hspace = 24 - space
add = b.barWidth
} else {
hspace = 24 - space
}
x = (b.spaceWidth * hspace) + add
h = (b.h - (b.verticalBorderSize * 2)) / 2
b.spaceRects[trueSpace] = [4]int{x, y, w, h}
}
}
// relX, relY
func (b *board) spaceRect(space int) (x, y, w, h int) {
rect := b.spaceRects[space]
return rect[0], rect[1], rect[2], rect[3]
}
func (b *board) bottomRow(space int) bool {
bottomStart := 1
bottomEnd := 12
bottomBar := 25
if b.V[fibs.StateDirection] == 1 {
bottomStart = 13
bottomEnd = 24
bottomBar = 0
}
return space == bottomBar || (space >= bottomStart && space <= bottomEnd)
}
// relX, relY
func (b *board) stackSpaceRect(space int, stack int) (x, y, w, h int) {
x, y, w, h = b.spaceRect(space)
// Stack pieces
osize := float64(stack)
var o int
if stack > 4 {
osize = 3.5
}
if b.bottomRow(space) {
osize += 1.0
}
o = int(osize * float64(b.overlapSize))
if !b.bottomRow(space) {
y += o
} else {
y = y + (h - o)
}
w, h = b.spaceWidth, b.spaceWidth
if space == 0 || space == 25 {
w = b.barWidth
}
return x, y, w, h
}
func (b *board) SetState(v []int) {
b.moveQueue <- &stateUpdate{-1, -1, v}
}
func (b *board) ProcessState() {
v := b.V
if b.lastDirection != v[fibs.StateDirection] {
b.setSpaceRects()
}
b.lastDirection = v[fibs.StateDirection]
b.Sprites = &Sprites{}
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
abs := spaceValue
if abs < 0 {
abs *= -1
}
for i := 0; i < abs; i++ {
s := b.newSprite(white)
b.spaces[space] = append(b.spaces[space], s)
b.Sprites.sprites = append(b.Sprites.sprites, s)
}
}
b.Sprites.num = len(b.Sprites.sprites)
b.positionCheckers()
}
func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) {
moveSize := 1
moveDelay := time.Duration(2/speed) * time.Millisecond
stackTo := len(b.spaces[to])
if stackTo == 1 && sprite.colorWhite != b.spaces[to][0].colorWhite {
stackTo = 0 // Hit
}
x, y, _, _ := b.stackSpaceRect(to, stackTo)
x, y = b.offsetPosition(x, y)
if sprite.x != x {
// Center
cy := (b.h / 2) - (b.spaceWidth / 2)
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
}
}
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
}
}
time.Sleep(moveDelay / 2)
}
}
for {
if sprite.x == x && sprite.y == y {
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
}
}
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
}
}
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 {
b.spaces[from] = append(b.spaces[from][:i], b.spaces[from][i+1:]...)
break
}
}
b.moving = nil
}
func (b *board) handlePieceMoves() {
for u := range b.moveQueue {
if u.from == -1 || u.to == -1 {
b.V = u.v
b.ProcessState()
continue
}
from, to := u.from, u.to
pieces := b.spaces[from]
if len(pieces) == 0 {
continue
}
sprite := pieces[len(pieces)-1]
var moveAfter *Sprite
if len(b.spaces[to]) == 1 {
if sprite.colorWhite != b.spaces[to][0].colorWhite {
moveAfter = b.spaces[to][0]
}
}
b._movePiece(sprite, from, to, 1)
if moveAfter != nil {
toBar := 0
if b.V[fibs.StateDirection] == 1 {
toBar = 25
}
b._movePiece(moveAfter, to, toBar, 2)
}
b.positionCheckers()
}
}
// Do not call directly
func (b *board) movePiece(from int, to int) {
b.moveQueue <- &stateUpdate{from, to, nil}
}
func (b *board) update() {
if b.dragging == nil {
// TODO allow grabbing multiple pieces by grabbing further down the stack
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
x, y := ebiten.CursorPosition()
@ -289,20 +679,41 @@ func (b *board) update() {
}
}
x, y := ebiten.CursorPosition()
if b.dragTouchId != -1 {
x, y = ebiten.TouchPosition(b.dragTouchId)
}
var dropped *Sprite
if b.dragTouchId == -1 {
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
dropped = b.dragging
b.dragging = nil
}
} else if inpututil.IsTouchJustReleased(b.dragTouchId) {
dropped = b.dragging
b.dragging = nil
}
if dropped != nil {
// TODO allow dragging anywhere outside of board to bear off
// allow dragging on to bar to bear off
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
}
}
}
}
b.positionCheckers()
}
if b.dragging != nil {
x, y := ebiten.CursorPosition()
if b.dragTouchId != -1 {
x, y = ebiten.TouchPosition(b.dragTouchId)
}
sprite := b.dragging
sprite.x = x - (sprite.w / 2)
sprite.y = y - (sprite.h / 2)

View file

@ -6,22 +6,19 @@ import (
"fmt"
"image"
"image/color"
_ "image/png"
"log"
"math"
"math/rand"
"strings"
"time"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"golang.org/x/image/font/opentype"
"github.com/nfnt/resize"
// Asset decoding
_ "image/png"
"code.rocketnine.space/tslocum/fibs"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/nfnt/resize"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
)
// Copyright 2020 The Ebiten Authors
@ -38,8 +35,6 @@ import (
// See the License for the specific language governing permissions and
// limitations under the License.
const maxAngle = 256
//go:embed assets
var assetsFS embed.FS
@ -53,6 +48,8 @@ var (
mplusBigFont font.Face
)
const defaultServerAddress = "fibs.com:4321"
func init() {
loadAssets(0)
@ -108,65 +105,6 @@ func initializeFonts() {
}
}
// TODO copied
func line(x0, y0, x1, y1 float32, clr color.RGBA) ([]ebiten.Vertex, []uint16) {
const width = 1
theta := math.Atan2(float64(y1-y0), float64(x1-x0))
theta += math.Pi / 2
dx := float32(math.Cos(theta))
dy := float32(math.Sin(theta))
r := float32(clr.R) / 0xff
g := float32(clr.G) / 0xff
b := float32(clr.B) / 0xff
a := float32(clr.A) / 0xff
return []ebiten.Vertex{
{
DstX: x0 - width*dx/2,
DstY: y0 - width*dy/2,
SrcX: 1,
SrcY: 1,
ColorR: r,
ColorG: g,
ColorB: b,
ColorA: a,
},
{
DstX: x0 + width*dx/2,
DstY: y0 + width*dy/2,
SrcX: 1,
SrcY: 1,
ColorR: r,
ColorG: g,
ColorB: b,
ColorA: a,
},
{
DstX: x1 - width*dx/2,
DstY: y1 - width*dy/2,
SrcX: 1,
SrcY: 1,
ColorR: r,
ColorG: g,
ColorB: b,
ColorA: a,
},
{
DstX: x1 + width*dx/2,
DstY: y1 + width*dy/2,
SrcX: 1,
SrcY: 1,
ColorR: r,
ColorG: g,
ColorB: b,
ColorA: a,
},
}, []uint16{0, 1, 2, 1, 2, 3}
}
type Sprite struct {
image *ebiten.Image
w int
@ -196,29 +134,105 @@ const (
MaxSprites = 50000
)
var spinner = []byte(`-\|/`)
type Game struct {
touchIDs []ebiten.TouchID
sprites Sprites
op *ebiten.DrawImageOptions
board *board
Board *board
screenW, screenH int
drawBuffer bytes.Buffer
spinnerIndex int
ServerAddress string
Username string
Password string
loggedIn bool
usernameConfirmed bool
Watch bool
Client *fibs.Client
runeBuffer []rune
inputBuffer string
Debug int
}
func NewGame() *Game {
rand.Seed(time.Now().UnixNano())
go func() {
// TODO fetch HTTP request, set debugExtra
}()
return &Game{
g := &Game{
op: &ebiten.DrawImageOptions{
Filter: ebiten.FilterNearest,
},
board: NewBoard(),
Board: NewBoard(),
runeBuffer: make([]rune, 24),
}
go func() {
t := time.NewTicker(time.Second / 4)
for range t.C {
_ = g.update()
}
}()
return g
}
func (g *Game) handleEvents() {
for e := range g.Client.Event {
switch event := e.(type) {
case *fibs.EventMove:
g.Board.movePiece(event.From, event.To)
}
}
}
func (g *Game) Connect() {
g.loggedIn = true
address := g.ServerAddress
if address == "" {
address = defaultServerAddress
}
g.Client = fibs.NewClient(address, g.Username, g.Password)
go g.handleEvents()
c := g.Client
if g.Watch {
go func() {
time.Sleep(1 * time.Second)
c.WatchRandomGame()
go g.renderLoop()
}()
}
go func() {
err := c.Connect()
if err != nil {
log.Fatal(err)
}
}()
}
func (g *Game) renderLoop() {
t := time.NewTicker(time.Second)
for range t.C {
gameBoard := g.Board
v := g.Client.Board.GetIntState()
gameBoard.SetState(v)
}
}
@ -244,7 +258,61 @@ func (g *Game) rightTouched() bool {
return false
}
func (g *Game) Update() error {
// Separate update function for all normal update logic, as Update may only be
// called when there is user input when vsync is disabled.
func (g *Game) update() error {
return nil
}
func (g *Game) Update() error { // Called by ebiten only when input occurs
err := g.update()
if err != nil {
return err
}
if !g.loggedIn {
f := func() {
var clearBuffer bool
defer func() {
if clearBuffer {
g.inputBuffer = ""
if !g.usernameConfirmed {
g.usernameConfirmed = true
} else if g.Password != "" {
g.Connect()
}
}
}()
if inpututil.IsKeyJustPressed(ebiten.KeyBackspace) && len(g.inputBuffer) > 0 {
g.inputBuffer = g.inputBuffer[:len(g.inputBuffer)-1]
}
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) {
clearBuffer = true
}
g.runeBuffer = ebiten.AppendInputChars(g.runeBuffer[:0])
if len(g.runeBuffer) > 0 {
g.inputBuffer += string(g.runeBuffer)
if strings.ContainsRune(g.inputBuffer, '\n') {
g.inputBuffer = strings.Split(g.inputBuffer, "\n")[0]
clearBuffer = true
}
if !g.usernameConfirmed {
g.Username = g.inputBuffer
} else {
g.Password = g.inputBuffer
}
log.Println("INPUT BUFFER IS:" + g.inputBuffer)
}
}
f()
}
g.touchIDs = ebiten.AppendTouchIDs(g.touchIDs[:0])
// Decrease the number of the sprites.
@ -263,7 +331,15 @@ func (g *Game) Update() error {
}
}
g.board.update()
if inpututil.IsKeyJustPressed(ebiten.KeyD) {
g.Debug++
if g.Debug == 3 {
g.Debug = 0
}
g.Board.debug = g.Debug
}
g.Board.update()
//g.sprites.Update()
return nil
@ -272,44 +348,73 @@ func (g *Game) Update() error {
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.RGBA{0, 102, 51, 255})
g.board.draw(screen)
// Log in screen
if !g.loggedIn {
const welcomeText = `Please enter your FIBS username and password.
If you do not have a FIBS account yet, visit
http://www.fibs.com/help.html#register`
debugBox := image.NewRGBA(image.Rect(0, 0, g.screenW, g.screenH))
debugImg := ebiten.NewImageFromImage(debugBox)
debugBox := image.NewRGBA(image.Rect(10, 20, 200, 200))
debugImg := ebiten.NewImageFromImage(debugBox)
if !g.usernameConfirmed {
ebitenutil.DebugPrint(debugImg, welcomeText+fmt.Sprintf("\n\nUsername: %s", g.Username))
} else {
ebitenutil.DebugPrint(debugImg, welcomeText+fmt.Sprintf("\n\nPassword: %s", strings.Repeat("*", len(g.Password))))
}
g.drawBuffer.Reset()
g.drawBuffer.Write([]byte(fmt.Sprintf("FPS %0.0f\nTPS %0.0f", ebiten.CurrentFPS(), ebiten.CurrentTPS())))
scaleFactor := ebiten.DeviceScaleFactor()
if scaleFactor != 1.0 {
g.drawBuffer.WriteRune('\n')
g.drawBuffer.Write([]byte(fmt.Sprintf("SCA %0.1f", scaleFactor)))
g.resetImageOptions()
g.op.GeoM.Scale(2, 2)
screen.DrawImage(debugImg, g.op)
return
}
if debugExtra != nil {
g.drawBuffer.WriteRune('\n')
g.drawBuffer.Write(debugExtra)
// Game screen
g.Board.draw(screen)
if g.Debug == 1 {
debugBox := image.NewRGBA(image.Rect(10, 20, 200, 200))
debugImg := ebiten.NewImageFromImage(debugBox)
g.drawBuffer.Reset()
g.drawBuffer.Write([]byte(fmt.Sprintf("FPS %0.0f\nTPS %0.0f", ebiten.CurrentFPS(), ebiten.CurrentTPS())))
/* TODO enable when vsync is able to be turned off
g.spinnerIndex++
if g.spinnerIndex == 4 {
g.spinnerIndex = 0
}*/
scaleFactor := ebiten.DeviceScaleFactor()
if scaleFactor != 1.0 {
g.drawBuffer.WriteRune('\n')
g.drawBuffer.Write([]byte(fmt.Sprintf("SCA %0.1f", scaleFactor)))
}
if debugExtra != nil {
g.drawBuffer.WriteRune('\n')
g.drawBuffer.Write(debugExtra)
}
ebitenutil.DebugPrint(debugImg, g.drawBuffer.String())
g.resetImageOptions()
g.op.GeoM.Translate(3, 0)
g.op.GeoM.Scale(2, 2)
screen.DrawImage(debugImg, g.op)
}
ebitenutil.DebugPrint(debugImg, g.drawBuffer.String())
g.resetImageOptions()
g.op.GeoM.Translate(3, 0)
g.op.GeoM.Scale(2, 2)
screen.DrawImage(debugImg, g.op)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
s := ebiten.DeviceScaleFactor()
outsideWidth, outsideHeight = int(float64(outsideWidth)*s), int(float64(outsideHeight)*s)
if g.screenW == outsideWidth && g.screenH == outsideHeight {
return outsideWidth, outsideHeight
}
g.screenW, g.screenH = outsideWidth, outsideHeight
g.board.setRect(0, 0, g.screenW, g.screenH)
// TODO use scale factor
g.Board.setRect(0, 0, g.screenW, g.screenH)
return outsideWidth, outsideHeight
}

11
go.mod
View file

@ -3,7 +3,8 @@ module code.rocketnine.space/tslocum/boxcars
go 1.17
require (
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.11.0.20210818171906-9f1113b733b9
code.rocketnine.space/tslocum/fibs v0.0.0-00010101000000-000000000000
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210825183521-91a72880271d
github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
@ -12,9 +13,13 @@ require (
require (
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/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/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect
golang.org/x/text v0.3.7 // indirect
)
replace code.rocketnine.space/tslocum/fibs => /home/trevor/programming/fibs

24
go.sum
View file

@ -4,24 +4,20 @@ github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326 h1:QqWaXlVeUGwSH7hO8giZ
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/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
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/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.11.0.20210818171906-9f1113b733b9 h1:7BPQ3xdKXg80VujVnjcmlDrZnwwV+Pi5OWIW8dBYLpo=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.11.0.20210818171906-9f1113b733b9/go.mod h1:2jj60Rz9WF8YOAJrh9355q9d0yoa9VKqhGxo112+rSU=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210825183521-91a72880271d h1:EMEmUvZhS8hZ4eq1732CWs7aKcSWXhVvcxllfhVZc9Y=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210825183521-91a72880271d/go.mod h1:B4Cje+Kb1ZjztrKFPaiMWOGXO3Vp8u+zIBdxkZqkyD4=
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=
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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=
@ -29,6 +25,10 @@ github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
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/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/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=
@ -40,7 +40,6 @@ golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86h
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-20210220032944-ac19c3e999fb/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=
@ -62,13 +61,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/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 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
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/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=
@ -76,4 +77,3 @@ 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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

44
main.go
View file

@ -1,7 +1,11 @@
package main
import (
"bufio"
"flag"
"log"
"os"
"time"
"code.rocketnine.space/tslocum/boxcars/game"
"github.com/hajimehoshi/ebiten/v2"
@ -19,6 +23,10 @@ func main() {
ebiten.SetMaxTPS(60) // TODO allow users to set custom value
ebiten.SetRunnableOnUnfocused(true) // Note - this currently does nothing in ebiten
// TODO set up system to call scheduleframe automatically
//ebiten.SetFPSMode(ebiten.FPSModeVsyncOffMinimum)
// TODO breaks justpressedkey
//ebiten.SetWindowClosingHandled(true) TODO implement
fullscreenWidth, fullscreenHeight := ebiten.ScreenSizeInFullscreen()
@ -26,7 +34,41 @@ func main() {
ebiten.SetFullscreen(true)
}
if err := ebiten.RunGame(game.NewGame()); err != nil {
g := game.NewGame()
flag.StringVar(&g.Username, "username", "", "Username")
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.Parse()
// Auto-connect
if g.Username != "" && g.Password != "" {
g.Connect()
}
go func() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
// TODO temporary
if string(scanner.Bytes()) == "/board" {
g.Client.Out <- []byte("set boardstyle 2")
time.Sleep(time.Second / 2)
g.Client.Out <- []byte("board")
time.Sleep(time.Second / 2)
g.Client.Out <- []byte("set boardstyle 3")
continue
}
g.Client.Out <- append(scanner.Bytes())
}
if scanner.Err() != nil {
// TODO
}
}()
if err := ebiten.RunGame(g); err != nil {
log.Fatal(err)
}
}