aboutsummaryrefslogtreecommitdiff
path: root/go/ui
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-01-24 00:27:50 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:44 +0100
commit73d40903aecad3efcf7c6055c133f0db86558203 (patch)
treee9c8f00fb186e270da92f273f570579f2d0b0d95 /go/ui
parentfbba48698d8f8b83dfd7d914b0e37338e0d4e2ef (diff)
downloadmuhqs-game-73d40903aecad3efcf7c6055c133f0db86558203.tar.gz
muhqs-game-73d40903aecad3efcf7c6055c133f0db86558203.zip
intermediate commit
Diffstat (limited to 'go/ui')
-rw-r--r--go/ui/buffer.go39
-rw-r--r--go/ui/button.go45
-rw-r--r--go/ui/choice.go8
-rw-r--r--go/ui/font.go24
-rw-r--r--go/ui/hand.go68
-rw-r--r--go/ui/mapView.go19
-rw-r--r--go/ui/stateBar.go42
-rw-r--r--go/ui/textBox.go42
-rw-r--r--go/ui/widget.go40
9 files changed, 219 insertions, 108 deletions
diff --git a/go/ui/buffer.go b/go/ui/buffer.go
index cc9f2377..e0e6b001 100644
--- a/go/ui/buffer.go
+++ b/go/ui/buffer.go
@@ -5,6 +5,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/text"
+
+ "muhq.space/muhqs-game/go/font"
)
var (
@@ -13,47 +15,30 @@ var (
)
type Buffer struct {
- X, Y int
- Width, Height int
- img *ebiten.Image
- lines []string
- pos int
+ WidgetBase
+ lines []string
+ pos int
}
func NewBuffer(x, y int, width, height int) *Buffer {
- return &Buffer{x, y, width, height, nil, []string{}, 0}
+ b := &Buffer{NewWidgetBase(x, y, width, height), []string{}, 0}
+ b.renderImpl = func() *ebiten.Image { return b.render() }
+ return b
}
-func (b *Buffer) render() {
+func (b *Buffer) render() *ebiten.Image {
img := ebiten.NewImage(b.Width, b.Height)
img.Fill(BUFFER_BACKGROUND)
y := 0
for _, line := range b.lines[b.pos:] {
- bounds := text.BoundString(Font, line)
+ bounds := text.BoundString(font.Font, line)
y += bounds.Dy()
if y > b.Height {
break
}
- text.Draw(img, line, Font, 0, y, BUFFER_FOREGROUND)
- }
- b.img = img
-}
-
-func (b *Buffer) Draw(screen *ebiten.Image) {
- if b.img == nil {
- b.render()
+ text.Draw(img, line, font.Font, 0, y, BUFFER_FOREGROUND)
}
- op := &ebiten.DrawImageOptions{}
- op.GeoM.Translate(float64(b.X), float64(b.Y))
- screen.DrawImage(b.img, op)
-}
-
-func (b *Buffer) FindObjectAt(x, y int) interface{} {
- return nil
-}
-
-func (b *Buffer) ForceRedraw() {
- b.img = nil
+ return img
}
func (b *Buffer) AddLines(lines []string) {
diff --git a/go/ui/button.go b/go/ui/button.go
index 93180204..97dbad22 100644
--- a/go/ui/button.go
+++ b/go/ui/button.go
@@ -6,43 +6,36 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/text"
+
+ "muhq.space/muhqs-game/go/font"
)
type Button interface {
Widget
- Contains(x, y int) bool
Click(x, y int)
}
type SimpleButton struct {
- X, Y int
- Width, Height int
- img *ebiten.Image
- OnClick func(b *SimpleButton)
+ WidgetBase
+ label string
+ OnClick func(b *SimpleButton)
+ BG color.Color
+ FG color.Color
}
-func NewSimpleButton(x, y, w, h int, onClick func(b *SimpleButton), label string) *SimpleButton {
- img := ebiten.NewImage(w, h)
-
- // b := text.BoundString(f, label)
- ebitenutil.DrawRect(img, float64(0), float64(0), float64(w), float64(h), gray)
- text.Draw(img, label, Font, 20, 25, color.White)
-
- return &SimpleButton{x, y, w, h, img, onClick}
+func NewSimpleButton(x, y, w, h int, label string, onClick func(b *SimpleButton)) *SimpleButton {
+ sb := &SimpleButton{NewWidgetBase(x, y, w, h), label, onClick, gray, color.White}
+ sb.renderImpl = func() *ebiten.Image { return sb.render() }
+ return sb
}
-func (b *SimpleButton) Draw(screen *ebiten.Image) {
- op := &ebiten.DrawImageOptions{}
- op.GeoM.Translate(float64(b.X), float64(b.Y))
- screen.DrawImage(b.img, op)
-}
-
-func (b *SimpleButton) Click(x, y int) {
- b.OnClick(b)
-}
+func (b *SimpleButton) render() *ebiten.Image {
+ img := ebiten.NewImage(b.Width, b.Height)
-func (b *SimpleButton) Contains(x, y int) bool {
- return x >= b.X && x <= b.X+b.Width && y >= b.Y && y <= b.Y+b.Height
+ // b := text.BoundString(f, label)
+ ebitenutil.DrawRect(img, float64(0), float64(0), float64(b.Width), float64(b.Height), b.BG)
+ text.Draw(img, b.label, font.Font, 20, 25, b.FG)
+ return img
}
func (b *SimpleButton) FindObjectAt(x, y int) interface{} {
@@ -51,3 +44,7 @@ func (b *SimpleButton) FindObjectAt(x, y int) interface{} {
}
return nil
}
+
+func (b *SimpleButton) Click(x, y int) {
+ b.OnClick(b)
+}
diff --git a/go/ui/choice.go b/go/ui/choice.go
index 2020ed0a..71308d1b 100644
--- a/go/ui/choice.go
+++ b/go/ui/choice.go
@@ -21,8 +21,8 @@ func NewChoice(x, y int, width, choiceHeight int, choices []string,
c := &Choice{x, y, width, choiceHeight, choices, onClick, []Button{}}
for i, choice := range c.choices {
- b := NewSimpleButton(c.X, c.Y+i*c.choiceHeight, c.Width, c.choiceHeight,
- choiceButtonOnClickDummy, choice)
+ b := NewSimpleButton(c.X, c.Y+i*c.choiceHeight, c.Width, c.choiceHeight, choice,
+ choiceButtonOnClickDummy)
c.buttons = append(c.buttons, b)
}
@@ -55,6 +55,10 @@ func (c *Choice) FindObjectAt(x, y int) interface{} {
return nil
}
+func (c *Choice) Render() *ebiten.Image {
+ return nil
+}
+
func NewActionChoice(x, y int, actions []game.ActionPrototype, onClick func(*Choice, int, int)) *Choice {
// TODO: use dynamic with width and height
width := 200
diff --git a/go/ui/font.go b/go/ui/font.go
deleted file mode 100644
index 065ac0d8..00000000
--- a/go/ui/font.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package ui
-
-import (
- "log"
-
- "golang.org/x/image/font"
- "golang.org/x/image/font/gofont/goregular"
- "golang.org/x/image/font/opentype"
-)
-
-var Font font.Face
-
-func InitFont() {
- tt, err := opentype.Parse(goregular.TTF)
- if err != nil {
- log.Fatal(err)
- }
-
- opts := &opentype.FaceOptions{Size: 24, DPI: 72}
- Font, err = opentype.NewFace(tt, opts)
- if err != nil {
- log.Fatal(err)
- }
-}
diff --git a/go/ui/hand.go b/go/ui/hand.go
new file mode 100644
index 00000000..0fdde253
--- /dev/null
+++ b/go/ui/hand.go
@@ -0,0 +1,68 @@
+package ui
+
+import (
+ "log"
+
+ "github.com/hajimehoshi/ebiten/v2"
+ "muhq.space/muhqs-game/go/assets"
+ "muhq.space/muhqs-game/go/game"
+)
+
+const (
+ HAND_CARD_WIDTH = 200
+)
+
+type HandCard struct {
+ C *game.Card
+}
+
+type HandView struct {
+ WidgetBase
+ hand *game.Hand
+ scale float64
+}
+
+func NewHandView(x, y, width, height int, h *game.Hand) *HandView {
+ hv := &HandView{NewWidgetBase(x, y, width, height), h, -1}
+ hv.renderImpl = func() *ebiten.Image { return hv.render() }
+ return hv
+}
+
+func (hv *HandView) render() *ebiten.Image {
+ img := ebiten.NewImage(hv.Width, hv.Height)
+ for i, card := range hv.hand.GetCards() {
+ cardImg := assets.GetCard(card.Path(), "en")
+ if cardImg == nil {
+ log.Fatalf("No image found for %s", card.Path())
+ }
+
+ if hv.scale == -1 {
+ b := cardImg.Bounds()
+ x_scale := float64(HAND_CARD_WIDTH) / float64(b.Dx())
+ y_scale := float64(hv.Height) / float64(b.Dy())
+ hv.scale = x_scale
+ if y_scale < x_scale {
+ hv.scale = y_scale
+ }
+ }
+
+ op := &ebiten.DrawImageOptions{}
+ op.GeoM.Scale(hv.scale, hv.scale)
+ op.GeoM.Translate(float64(i*HAND_CARD_WIDTH), 0)
+ img.DrawImage(cardImg, op)
+ }
+ return img
+}
+
+func (hv *HandView) FindObjectAt(x, y int) interface{} {
+ if !hv.Contains(x, y) {
+ return nil
+ }
+
+ i := x / HAND_CARD_WIDTH
+ if i >= hv.hand.Size() {
+ return nil
+ }
+
+ return HandCard{hv.hand.GetCards()[i]}
+}
diff --git a/go/ui/mapView.go b/go/ui/mapView.go
index 39c92ae4..6c887920 100644
--- a/go/ui/mapView.go
+++ b/go/ui/mapView.go
@@ -1,9 +1,11 @@
package ui
import (
+ "fmt"
"log"
"math"
"strings"
+ "unicode"
"golang.org/x/exp/slices"
@@ -33,6 +35,10 @@ func NewMapView(g *game.State) *MapView {
return &vw
}
+func (mv *MapView) GetHeight() int {
+ return len(mv.gameState.Map.Tiles) * TILE_HEIGHT
+}
+
func rotateStreetImg(x, y int, radian float64, op *ebiten.DrawImageOptions) {
// Move the image's center to the screen's upper-left corner.
// This is a preparation for rotating. When geometry matrices are applied,
@@ -130,11 +136,14 @@ func (vw *MapView) drawPermanentsLayer(screen *ebiten.Image) {
if vw.permanentsLayer == nil {
vw.permanentsLayer = vw.newLayerImage()
- for _, p := range vw.gameState.Permanents {
+ for i, p := range vw.gameState.Permanents {
permanentSymbol := assets.GetSymbol(strings.ToLower(p.GetCard().Name))
- // TODO: Implement generic symbols
if permanentSymbol == nil {
- continue
+ symbol := fmt.Sprintf("%c%d", unicode.ToUpper(rune(p.GetCard().Type.String()[0])), i)
+ permanentSymbol = assets.GetGenericSymbol(symbol)
+ if permanentSymbol == nil {
+ log.Fatalf("Failed to generate generic symbol %s", symbol)
+ }
}
t := p.GetTile()
@@ -225,3 +234,7 @@ func (vw *MapView) HighlightPermanent(p game.Permanent) {
func (vw *MapView) ClearPermanentsHighlights() {
vw.HighlightPermanents([]game.Permanent{})
}
+
+func (*MapView) Render(Widget) *ebiten.Image {
+ return nil
+}
diff --git a/go/ui/stateBar.go b/go/ui/stateBar.go
new file mode 100644
index 00000000..e175c0c5
--- /dev/null
+++ b/go/ui/stateBar.go
@@ -0,0 +1,42 @@
+package ui
+
+import (
+ "fmt"
+
+ "github.com/hajimehoshi/ebiten/v2"
+ "muhq.space/muhqs-game/go/game"
+)
+
+type StateBar struct {
+ X, Y int
+ Width, Height int
+ gameState *game.State
+ activePlayer *game.Player
+ ressourceView *TextBox
+}
+
+func NewStateBar(x, y int, width, height int, s *game.State, p *game.Player) *StateBar {
+ sb := &StateBar{x, y, width, height, s, p, nil}
+ return sb
+}
+
+func (sb *StateBar) ressourceLabel() string {
+ return fmt.Sprintf("%d", sb.activePlayer.Ressource)
+}
+
+func (sb *StateBar) Draw(screen *ebiten.Image) {
+ if sb.ressourceView == nil {
+ sb.ressourceView = NewFixedTextBox(sb.X, sb.Y+sb.Height-sb.Width, sb.Width, sb.Width,
+ sb.ressourceLabel())
+ }
+ sb.ressourceView.Draw(screen)
+}
+
+func (sb *StateBar) FindObjectAt(x, y int) interface{} {
+ return nil
+}
+
+func (sb *StateBar) ForceRedraw() {
+ sb.ressourceView.Text = sb.ressourceLabel()
+ sb.ressourceView = nil
+}
diff --git a/go/ui/textBox.go b/go/ui/textBox.go
index e07440b5..5e2d140b 100644
--- a/go/ui/textBox.go
+++ b/go/ui/textBox.go
@@ -6,31 +6,30 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/text"
+ "muhq.space/muhqs-game/go/font"
"muhq.space/muhqs-game/go/game"
)
var (
TEXTBOX_BACKGROUND = gray
TEXTBOX_FOREGROUND = color.White
+
+ TEXTBOX_AUTO_BORDER = 3
)
type TextBox struct {
- X, Y int
- Width, Height int
- img *ebiten.Image
- text string
+ WidgetBase
+ Text string
}
func NewFixedTextBox(x, y int, width, height int, t string) *TextBox {
- tb := &TextBox{x, y, width, height, nil, t}
- tb.render()
+ tb := &TextBox{NewWidgetBase(x, y, width, height), t}
+ tb.renderImpl = func() *ebiten.Image { return tb.render() }
return tb
}
func NewAutoTextBox(x, y int, t string) *TextBox {
- tb := &TextBox{x, y, -1, -1, nil, t}
- tb.render()
- return tb
+ return NewFixedTextBox(x, y, -1, -1, t)
}
func NewUnitInfo(x, y int, u *game.Unit) *TextBox {
@@ -45,23 +44,14 @@ func NewPermInfo(x, y int, p game.Permanent) *TextBox {
return NewAutoTextBox(x, y, info)
}
-func (tb *TextBox) render() {
- b := text.BoundString(Font, tb.text)
+func (tb *TextBox) render() *ebiten.Image {
+ b := text.BoundString(font.Font, tb.Text)
if tb.Width == -1 || tb.Height == -1 {
- tb.Width = b.Dx()
- tb.Height = b.Dy()
+ tb.Width = b.Dx() + 2*TEXTBOX_AUTO_BORDER
+ tb.Height = b.Dy() + 2*TEXTBOX_AUTO_BORDER
}
- tb.img = ebiten.NewImage(tb.Width, tb.Height)
- tb.img.Fill(TEXTBOX_BACKGROUND)
- text.Draw(tb.img, tb.text, Font, b.Min.X, -b.Min.Y, TEXTBOX_FOREGROUND)
-}
-
-func (tb *TextBox) Draw(screen *ebiten.Image) {
- op := &ebiten.DrawImageOptions{}
- op.GeoM.Translate(float64(tb.X), float64(tb.Y))
- screen.DrawImage(tb.img, op)
-}
-
-func (tb TextBox) FindObjectAt(x, y int) interface{} {
- return nil
+ img := ebiten.NewImage(tb.Width, tb.Height)
+ img.Fill(TEXTBOX_BACKGROUND)
+ text.Draw(img, tb.Text, font.Font, TEXTBOX_AUTO_BORDER+b.Min.X, TEXTBOX_AUTO_BORDER-b.Min.Y, TEXTBOX_FOREGROUND)
+ return img
}
diff --git a/go/ui/widget.go b/go/ui/widget.go
index c8fb0816..d783eb36 100644
--- a/go/ui/widget.go
+++ b/go/ui/widget.go
@@ -5,6 +5,42 @@ import (
)
type Widget interface {
- Draw(screen *ebiten.Image)
- FindObjectAt(x, y int) interface{}
+ Draw(*ebiten.Image)
+ FindObjectAt(int, int) interface{}
+}
+
+type WidgetBase struct {
+ X, Y int
+ Width, Height int
+ img *ebiten.Image
+ renderImpl func() *ebiten.Image
+}
+
+func renderDummy() *ebiten.Image {
+ return nil
+}
+
+func NewWidgetBase(x, y, width, height int) WidgetBase {
+ return WidgetBase{x, y, width, height, nil, renderDummy}
+}
+
+func (w *WidgetBase) Draw(screen *ebiten.Image) {
+ if w.img == nil {
+ w.img = w.renderImpl()
+ }
+ op := &ebiten.DrawImageOptions{}
+ op.GeoM.Translate(float64(w.X), float64(w.Y))
+ screen.DrawImage(w.img, op)
+}
+
+func (w *WidgetBase) ForceRedraw() {
+ w.img = nil
+}
+
+func (w *WidgetBase) Contains(x, y int) bool {
+ return x >= 0 && y >= 0 && x >= w.X && x <= w.X+w.Width && y >= w.Y && y <= w.Y+w.Height
+}
+
+func (w *WidgetBase) FindObjectAt(int, int) interface{} {
+ return nil
}