Browse Source

Use proportional font for chat and game status

master
Trevor Slocum 2 weeks ago
parent
commit
22a97cdbfe
  1. 21
      flags_web.go
  2. 71
      game/board.go
  3. 28
      game/game.go
  4. 20
      game/lobby.go
  5. 31
      game/tabbedbuffers.go
  6. 30
      game/textbuffer.go
  7. 6
      go.mod
  8. 12
      go.sum

21
flags_web.go

@ -4,9 +4,28 @@
package main
import (
"net/http"
"syscall/js"
"code.rocketnine.space/tslocum/boxcars/game"
)
func parseFlags(g *game.Game) {
// Do nothing
v := js.Global().Get("document").Get("cookie").String()
header := http.Header{}
header.Add("Cookie", v)
request := http.Request{Header: header}
cookieV, err := request.Cookie("BoxcarsUsername")
if err == nil {
g.Username = cookieV.Value
}
cookieV, err = request.Cookie("BoxcarsPassword")
if err == nil {
g.Password = cookieV.Value
}
//alert := js.Global().Get("alert")
//alert.Invoke(fmt.Sprintf("%+v", request.Cookies()))
}

71
game/board.go

@ -130,7 +130,7 @@ func (b *board) handleDraw() {
func (b *board) newSprite(white bool) *Sprite {
s := &Sprite{}
s.colorWhite = white
s.w, s.h = imgCheckerWhite.Size()
s.w, s.h = imgCheckerLight.Size()
return s
}
@ -321,14 +321,43 @@ func (b *board) draw(screen *ebiten.Image) {
// Schedule another frame
ebiten.ScheduleFrame()
}
// Draw shadow.
b.op.GeoM.Reset()
b.op.GeoM.Translate(x, y)
b.op.ColorM.Scale(0, 0, 0, 1)
if sprite.colorWhite {
screen.DrawImage(imgCheckerWhite, b.op)
} else {
screen.DrawImage(imgCheckerBlack, b.op)
b.op.Filter = ebiten.FilterLinear
screen.DrawImage(imgCheckerLight, b.op)
b.op.ColorM.Reset()
// Draw checker.
checkerScale := 0.94
b.op.GeoM.Reset()
b.op.GeoM.Translate(-b.spaceWidth/2, -b.spaceWidth/2)
b.op.GeoM.Scale(checkerScale, checkerScale)
b.op.GeoM.Translate((b.spaceWidth/2)+x, (b.spaceWidth/2)+y)
c := lightCheckerColor
if !sprite.colorWhite {
c = darkCheckerColor
}
b.op.ColorM.Scale(0.0, 0.0, 0.0, 1)
r := float64(c.R) / 0xff
g := float64(c.G) / 0xff
bl := float64(c.B) / 0xff
b.op.ColorM.Translate(r, g, bl, 0)
screen.DrawImage(imgCheckerLight, b.op)
b.op.ColorM.Reset()
b.op.Filter = ebiten.FilterNearest
}
b.iterateSpaces(func(space int) {
@ -360,9 +389,9 @@ func (b *board) draw(screen *ebiten.Image) {
labelColor = color.RGBA{0, 0, 0, 255}
}
bounds := text.BoundString(normalFont, overlayText)
bounds := text.BoundString(mediumFont, overlayText)
overlayImage := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2)
text.Draw(overlayImage, overlayText, normalFont, 0, bounds.Dy(), labelColor)
text.Draw(overlayImage, overlayText, mediumFont, 0, bounds.Dy(), labelColor)
x, y, w, h := b.stackSpaceRect(space, numPieces-1)
x += (w / 2) - (bounds.Dx() / 2)
@ -386,7 +415,7 @@ func (b *board) draw(screen *ebiten.Image) {
}
space := b.spaceAt(dx, dy)
if space >= 0 {
if space > 0 && space < 25 {
x, y, w, h := b.spaceRect(space)
spaceImage := ebiten.NewImage(w, h)
spaceImage.Fill(color.RGBA{255, 255, 255, 50})
@ -406,13 +435,17 @@ func (b *board) draw(screen *ebiten.Image) {
playerColor := color.White
opponentColor := color.Black
playerBorderColor := lightCheckerColor
opponentBorderColor := darkCheckerColor
if b.v[fibs.StatePlayerColor] == -1 {
playerColor = color.Black
opponentColor = color.White
playerBorderColor = darkCheckerColor
opponentBorderColor = lightCheckerColor
}
drawLabel := func(label string, labelColor color.Color, border bool, borderColor color.Color) *ebiten.Image {
bounds := text.BoundString(normalFont, label)
bounds := text.BoundString(mediumFont, label)
w := int(float64(bounds.Dx()) * 1.5)
h := int(float64(bounds.Dy()) * 2)
@ -432,7 +465,7 @@ func (b *board) draw(screen *ebiten.Image) {
}
img := ebiten.NewImageFromImage(baseImg)
text.Draw(img, label, normalFont, (w-bounds.Dx())/2, int(float64(h-(bounds.Max.Y/2))*0.75), labelColor)
text.Draw(img, label, mediumFont, (w-bounds.Dx())/2, int(float64(h-(bounds.Max.Y/2))*0.75), labelColor)
return img
}
@ -440,7 +473,7 @@ func (b *board) draw(screen *ebiten.Image) {
if b.s[fibs.StateOpponentName] != "" {
label := fmt.Sprintf("%s %d %d", b.s[fibs.StateOpponentName], b.v[fibs.StateOpponentDice1], b.v[fibs.StateOpponentDice2])
img := drawLabel(label, opponentColor, b.v[fibs.StateTurn] != b.v[fibs.StatePlayerColor], opponentColor)
img := drawLabel(label, opponentColor, b.v[fibs.StateTurn] != b.v[fibs.StatePlayerColor], opponentBorderColor)
bounds := img.Bounds()
x := int(((float64(b.innerW) - borderSize) / 4) - (float64(bounds.Dx()) / 2))
@ -456,7 +489,7 @@ func (b *board) draw(screen *ebiten.Image) {
if b.s[fibs.StatePlayerName] != "" {
label := fmt.Sprintf("%s %d %d", b.s[fibs.StatePlayerName], b.v[fibs.StatePlayerDice1], b.v[fibs.StatePlayerDice2])
img := drawLabel(label, playerColor, b.v[fibs.StateTurn] == b.v[fibs.StatePlayerColor], playerColor)
img := drawLabel(label, playerColor, b.v[fibs.StateTurn] == b.v[fibs.StatePlayerColor], playerBorderColor)
bounds := img.Bounds()
x := ((b.innerW / 4) * 3) - (bounds.Dx() / 2)
@ -486,8 +519,8 @@ func (b *board) draw(screen *ebiten.Image) {
img.DrawImage(ebiten.NewImageFromImage(baseImg), nil)
label := "Reset"
bounds := text.BoundString(normalFont, label)
text.Draw(img, label, normalFont, (w-bounds.Dx())/2, (h+(bounds.Dy()/2))/2, color.Black)
bounds := text.BoundString(mediumFont, label)
text.Draw(img, label, mediumFont, (w-bounds.Dx())/2, (h+(bounds.Dy()/2))/2, color.Black)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
@ -570,7 +603,7 @@ func (b *board) setRect(x, y, w, h int) {
for i := 0; i < b.Sprites.num; i++ {
s := b.Sprites.sprites[i]
s.w, s.h = imgCheckerWhite.Size()
s.w, s.h = imgCheckerLight.Size()
}
b.setSpaceRects()
@ -779,8 +812,12 @@ func (b *board) ProcessState() {
abs *= -1
}
var preMovesTo = b.Client.Board.Premoveto[space]
var preMovesFrom = b.Client.Board.Premovefrom[space]
var preMovesTo int
var preMovesFrom int
if b.Client != nil {
preMovesTo = b.Client.Board.Premoveto[space]
preMovesFrom = b.Client.Board.Premovefrom[space]
}
for i := 0; i < abs+(preMovesTo-preMovesFrom); i++ {
s := b.newSprite(white)

28
game/game.go

@ -5,6 +5,7 @@ import (
"embed"
"fmt"
"image"
"image/color"
_ "image/png"
"log"
"os"
@ -19,6 +20,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/nfnt/resize"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
@ -32,19 +34,26 @@ var debugExtra []byte
var debugGame *Game
var (
imgCheckerWhite *ebiten.Image
imgCheckerBlack *ebiten.Image
imgCheckerLight *ebiten.Image
imgCheckerDark *ebiten.Image
smallFont font.Face
normalFont font.Face
mediumFont font.Face
monoFont font.Face
largeFont font.Face
)
var (
lightCheckerColor = color.RGBA{232, 211, 162, 255}
darkCheckerColor = color.RGBA{51, 0, 111, 255}
)
const defaultServerAddress = "fibs.com:4321"
const maxStatusWidthRatio = 0.5
const bufferCharacterWidth = 54
func init() {
loadAssets(0)
@ -52,8 +61,9 @@ func init() {
}
func loadAssets(width int) {
imgCheckerWhite = loadAsset("assets/checker_white.png", width)
imgCheckerBlack = loadAsset("assets/checker_black.png", width)
imgCheckerLight = loadAsset("assets/checker_white.png", width)
imgCheckerDark = loadAsset("assets/checker_white.png", width)
//imgCheckerDark = loadAsset("assets/checker_black.png", width)
}
func loadAsset(assetPath string, width int) *ebiten.Image {
@ -89,8 +99,8 @@ func initializeFonts() {
if err != nil {
log.Fatal(err)
}
normalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: 24,
mediumFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: mediumFontSize,
DPI: dpi,
Hinting: font.HintingFull,
})
@ -98,7 +108,7 @@ func initializeFonts() {
log.Fatal(err)
}
largeFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: 32,
Size: largeFontSize,
DPI: dpi,
Hinting: font.HintingFull,
})
@ -502,7 +512,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
g.screenW, g.screenH = outsideWidth, outsideHeight
statusBufferWidth := g.statusBuffer.chatFontSize * 77
statusBufferWidth := text.BoundString(g.statusBuffer.chatFont, strings.Repeat("A", bufferCharacterWidth)).Dx()
if statusBufferWidth > int(float64(g.screenW)*maxStatusWidthRatio) {
statusBufferWidth = int(float64(g.screenW) * maxStatusWidthRatio)
}

20
game/lobby.go

@ -120,12 +120,12 @@ func (l *lobby) _drawBufferButtons() {
}
}
}
bounds := text.BoundString(normalFont, button.label)
bounds := text.BoundString(mediumFont, button.label)
labelColor := triangleA
img := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2)
text.Draw(img, button.label, normalFont, 0, bounds.Dy(), labelColor)
text.Draw(img, button.label, mediumFont, 0, bounds.Dy(), labelColor)
l.op.GeoM.Reset()
l.op.GeoM.Translate(float64(buttonWidth*i)+float64((buttonWidth-bounds.Dx())/2), float64(l.buttonBarHeight-bounds.Dy())/2-float64(bounds.Dy()/2))
@ -149,10 +149,10 @@ func (l *lobby) drawBuffer() {
lineHeight := 30
padding := 24.0
for i, label := range labels {
bounds := text.BoundString(normalFont, label)
bounds := text.BoundString(mediumFont, label)
labelColor := triangleA
img := ebiten.NewImage(l.w-int(l.padding*2), int(l.entryH))
text.Draw(img, label, normalFont, 4, bounds.Dy(), labelColor)
text.Draw(img, label, mediumFont, 4, bounds.Dy(), labelColor)
l.op.GeoM.Reset()
l.op.GeoM.Translate(padding, padding+float64(i*lineHeight))
l.buffer.DrawImage(img, l.op)
@ -160,9 +160,9 @@ func (l *lobby) drawBuffer() {
} else {
var img *ebiten.Image
drawEntry := func(cx float64, cy float64, colA string, colB string, colC string, highlight bool) {
boundsA := text.BoundString(normalFont, colA)
boundsB := text.BoundString(normalFont, colB)
boundsC := text.BoundString(normalFont, colC)
boundsA := text.BoundString(mediumFont, colA)
boundsB := text.BoundString(mediumFont, colB)
boundsC := text.BoundString(mediumFont, colC)
y := (boundsA.Dy() + boundsB.Dy() + boundsC.Dy()) / 3 // TODO this isn't correct
labelColor := triangleA
@ -184,9 +184,9 @@ func (l *lobby) drawBuffer() {
}
}
text.Draw(img, colA, normalFont, 4, y+2, labelColor)
text.Draw(img, colB, normalFont, int(250*ebiten.DeviceScaleFactor()), y+2, labelColor)
text.Draw(img, colC, normalFont, int(500*ebiten.DeviceScaleFactor()), y+2, labelColor)
text.Draw(img, colA, mediumFont, 4, y+2, labelColor)
text.Draw(img, colB, mediumFont, int(250*ebiten.DeviceScaleFactor()), y+2, labelColor)
text.Draw(img, colC, mediumFont, int(500*ebiten.DeviceScaleFactor()), y+2, labelColor)
l.op.GeoM.Reset()
l.op.GeoM.Translate(cx, cy)

31
game/tabbedbuffers.go

@ -19,8 +19,17 @@ const (
const windowStartingAlpha = 0.9
const smallFontSize = 14
const monoFontSize = 10
const (
smallFontSize = 20
monoFontSize = 10
mediumFontSize = 24
largeFontSize = 32
)
const (
monoLineHeight = 14
standardLineHeight = 22
)
type tabbedBuffers struct {
buffers []*textBuffer
@ -54,8 +63,9 @@ type tabbedBuffers struct {
client *fibs.Client
chatFont font.Face
chatFontSize int
chatFont font.Face
chatFontSize int
chatLineHeight int
acceptInput bool
}
@ -68,11 +78,13 @@ func newTabbedBuffers() *tabbedBuffers {
op: &ebiten.DrawImageOptions{},
chatFont: monoFont,
chatFontSize: monoFontSize,
chatLineHeight: monoLineHeight,
}
// TODO
//tab.chatFont = smallFont
//tab.chatFontSize = smallFontSize
tab.chatFont = smallFont
tab.chatFontSize = smallFontSize
tab.chatLineHeight = standardLineHeight
b := &textBuffer{
tab: tab,
@ -122,8 +134,7 @@ func (t *tabbedBuffers) drawBuffer() {
l := len(b.contentWrapped)
lineHeight := 14
showLines := t.h / lineHeight
showLines := t.h / t.chatLineHeight
if showLines > 1 {
showLines--
}
@ -140,9 +151,7 @@ func (t *tabbedBuffers) drawBuffer() {
for i := 0; i < showLines; i++ {
line := b.contentWrapped[l-showLines+i]
bounds := text.BoundString(t.chatFont, line)
_ = bounds
text.Draw(t.buffer, line, t.chatFont, t.padding*2, t.padding+(lineHeight*(i+1)), textColor)
text.Draw(t.buffer, line, t.chatFont, t.padding*2, t.padding+(t.chatLineHeight*(i+1)), textColor)
}
if t.acceptInput {

30
game/textbuffer.go

@ -5,6 +5,7 @@ import (
"unicode"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/text"
)
type textBuffer struct {
@ -34,19 +35,34 @@ func (b *textBuffer) Write(p []byte) {
func (b *textBuffer) wrapContent() {
b.contentWrapped = nil
for _, line := range b.content {
lineStr := string(line)
if b.tab.wrapWidth == 0 {
b.contentWrapped = append(b.contentWrapped, string(line))
b.contentWrapped = append(b.contentWrapped, lineStr)
continue
}
lineStr := string(line)
l := len(lineStr)
for start := 0; start < l; start += b.tab.wrapWidth {
end := start + b.tab.wrapWidth
if end > l {
end = l
var start int
var end int
for start < l {
for end = l; end > start; end-- {
bounds := text.BoundString(b.tab.chatFont, lineStr[start:end])
if bounds.Dx() < b.tab.w-(b.tab.padding*2) {
// Break on whitespace.
if end < l && !unicode.IsSpace(rune(lineStr[end])) {
for endOffset := 0; endOffset < end-start; endOffset++ {
if unicode.IsSpace(rune(lineStr[end-endOffset])) {
end = end - endOffset
break
}
}
}
b.contentWrapped = append(b.contentWrapped, lineStr[start:end])
break
}
}
b.contentWrapped = append(b.contentWrapped, lineStr[start:end])
start = end
}
}
}

6
go.mod

@ -3,7 +3,7 @@ module code.rocketnine.space/tslocum/boxcars
go 1.17
require (
code.rocketnine.space/tslocum/fibs v0.0.0-20211110163919-18c4cdbc32e9
code.rocketnine.space/tslocum/fibs v0.0.0-20211112042838-16c24d47934a
code.rocketnine.space/tslocum/kibodo v0.0.0-20211027223129-7b870790d865
github.com/hajimehoshi/ebiten/v2 v2.2.2
github.com/llgcode/draw2d v0.0.0-20210904075650-80aa0a2a901d
@ -20,10 +20,10 @@ require (
github.com/modern-go/reflect2 v1.0.1 // 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-20211109222223-9df80dc805b5 // indirect
golang.org/x/exp v0.0.0-20211111183329-cb5df436b1a8 // indirect
golang.org/x/mobile v0.0.0-20211109191125-d61a72f26a1a // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 // indirect
golang.org/x/sys v0.0.0-20211111213525-f221eed1c01e // indirect
golang.org/x/text v0.3.7 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)

12
go.sum

@ -1,7 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
code.rocketnine.space/tslocum/fibs v0.0.0-20211110163919-18c4cdbc32e9 h1:L8YUzxCVOs8aK8I6vnheKZiladcw3Ykq4fhLQz234ck=
code.rocketnine.space/tslocum/fibs v0.0.0-20211110163919-18c4cdbc32e9/go.mod h1:zxixHM4hy1D4t6peJ79AcYa0lCl5+PaeAybgaRD2qp0=
code.rocketnine.space/tslocum/fibs v0.0.0-20211112042838-16c24d47934a h1:iKYMwcn8EPPHRr0GyuCdTSpte0eeZSUPU+MC+fy09hg=
code.rocketnine.space/tslocum/fibs v0.0.0-20211112042838-16c24d47934a/go.mod h1:zxixHM4hy1D4t6peJ79AcYa0lCl5+PaeAybgaRD2qp0=
code.rocketnine.space/tslocum/kibodo v0.0.0-20211027223129-7b870790d865 h1:Sm6hHfKceNAPvGw+zOmQm5u+TePLJgZzz8zyk2Q/HC0=
code.rocketnine.space/tslocum/kibodo v0.0.0-20211027223129-7b870790d865/go.mod h1:pQfyfr10kXO/Cqw/T+bTDEg3Xbhjxb4vn/vaPzPW/Vk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@ -350,8 +350,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20211012155715-ffe10e552389/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA=
golang.org/x/exp v0.0.0-20211109222223-9df80dc805b5 h1:z5bapnFL1WqWwTUpFQTSwLVQEgLuY39KXDYgBTM+DrI=
golang.org/x/exp v0.0.0-20211109222223-9df80dc805b5/go.mod h1:OyI624f2tQ/aU3IMa7GB16Hk54CHURAfHfj6tMqtyhA=
golang.org/x/exp v0.0.0-20211111183329-cb5df436b1a8 h1:FJS8Pv3WYbMBIQqVzFH33DT5AuGjULOduxuHS5zlv+A=
golang.org/x/exp v0.0.0-20211111183329-cb5df436b1a8/go.mod h1:OyI624f2tQ/aU3IMa7GB16Hk54CHURAfHfj6tMqtyhA=
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=
@ -441,8 +441,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211111213525-f221eed1c01e h1:zeJt6jBtVDK23XK9QXcmG0FvO0elikp0dYZQZOeL1y0=
golang.org/x/sys v0.0.0-20211111213525-f221eed1c01e/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=

Loading…
Cancel
Save