aboutsummaryrefslogtreecommitdiff
path: root/go/client
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-01-20 03:18:06 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:44 +0100
commitc4cb9637f5c85d03aa162968f0dfec3c193fc9dc (patch)
tree3ad32e5dfd7c4ae72f66b86e77edef7b700a8b7a /go/client
parent9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74 (diff)
downloadmuhqs-game-c4cb9637f5c85d03aa162968f0dfec3c193fc9dc.tar.gz
muhqs-game-c4cb9637f5c85d03aa162968f0dfec3c193fc9dc.zip
intermediate commit
Implement actions and multiple ui widgets
Diffstat (limited to 'go/client')
-rw-r--r--go/client/main.go236
1 files changed, 203 insertions, 33 deletions
diff --git a/go/client/main.go b/go/client/main.go
index b0696fdf..ec8fff5e 100644
--- a/go/client/main.go
+++ b/go/client/main.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "image/color"
"log"
"github.com/hajimehoshi/ebiten/v2"
@@ -16,6 +17,13 @@ import (
const (
screenWidth = 1280
screenHeight = 1024
+
+ DEFAULT_BUTTON_HEIGHT = 40
+
+ RESOLVE_BUTTON_X = 500
+ STACK_BUFFER_WIDTH = 300
+
+ HOVER_THRESHOLD = 60
)
type HandLayer struct {
@@ -23,23 +31,71 @@ type HandLayer struct {
func (l *HandLayer) Draw(screen *ebiten.Image) {
cardImg := assets.GetCard("base/archer", "en")
- // _, cardImgHeight := cardImg.Size()
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(0, float64(screenHeight))
op.GeoM.Scale(0.5, 0.5)
screen.DrawImage(cardImg, op)
}
+var (
+ playerGreen = color.RGBA{1, 0xff, 1, 0xff}
+ playerBlue = color.RGBA{1, 1, 0xff, 0xff}
+)
+
+type hoverDetector struct {
+ last_x, last_y int
+ ticks int
+ resetFunc func()
+}
+
+func (h *hoverDetector) update(x, y int) {
+ if x == h.last_x && y == h.last_y {
+ h.ticks++
+ } else {
+ h.last_x, h.last_y = x, y
+ h.reset()
+ }
+}
+func (h *hoverDetector) reset() {
+ h.ticks = 0
+ if h.resetFunc != nil {
+ h.resetFunc()
+ h.resetFunc = nil
+ }
+}
+
+func (h *hoverDetector) hovering() bool {
+ return h.ticks >= HOVER_THRESHOLD
+}
+
+func (h *hoverDetector) startedHovering() bool {
+ return h.ticks == HOVER_THRESHOLD
+}
+
type Game struct {
- gameState *game.State
- handLayer *HandLayer
- mapView *ui.MapView
- activeObject interface{}
+ gameState *game.State
+ handLayer *HandLayer
+ mapView *ui.MapView
+ resolveButton *ui.SimpleButton
+ stackBuffer *ui.Buffer
+ widgets []ui.Widget
+ selectedObject interface{}
+ hoverDetector hoverDetector
}
func NewGame() *Game {
g := &Game{}
+ g.widgets = []ui.Widget{}
g.gameState = game.NewState()
+
+ g.resolveButton = ui.NewSimpleButton(screenWidth-125, screenHeight-DEFAULT_BUTTON_HEIGHT,
+ 125, DEFAULT_BUTTON_HEIGHT, func(*ui.SimpleButton) {
+ g.resolveAction()
+ }, "Resolve")
+ g.addWidget(g.resolveButton)
+
+ g.stackBuffer = ui.NewBuffer(screenWidth-STACK_BUFFER_WIDTH, g.resolveButton.Y-400,
+ STACK_BUFFER_WIDTH, 400)
return g
}
@@ -58,8 +114,8 @@ func (g *Game) loadMap(mapName string) {
g.mapView = ui.NewMapView(g.gameState)
}
-func (g *Game) addPlayer(name string, deck *game.Deck) {
- g.gameState.AddNewPlayer(name, deck)
+func (g *Game) addPlayer(name string, deck *game.Deck, color color.Color) {
+ g.gameState.AddNewPlayer(name, deck, color)
}
func (g *Game) getPlayer(name string) *game.Player {
@@ -71,13 +127,50 @@ func (g *Game) getPlayer(name string) *game.Player {
return nil
}
-func (g *Game) moveUnit(unit *game.Unit, tile *game.Tile) {
- g.gameState.MoveUnit(unit, tile)
+func (g *Game) addWidget(w ui.Widget) {
+ g.widgets = append(g.widgets, w)
+}
+
+func (g *Game) removeWidget(widget ui.Widget) {
+ nwidgets := len(g.widgets)
+ for i, w := range g.widgets {
+ if w != widget {
+ continue
+ }
+
+ g.widgets[i] = g.widgets[nwidgets-1]
+ g.widgets = g.widgets[:nwidgets-1]
+ return
+ }
+}
+
+func (g *Game) clearMapHighlights() {
+ g.mapView.ClearPermanentsHighlights()
g.mapView.ClearTileHighlights()
g.mapView.ForceRedraw()
}
+func (g *Game) declareAction(a game.Action) {
+ a.Declare(g.gameState)
+ g.clearMapHighlights()
+ g.stackBuffer.AddLine(a.String())
+}
+
+func (g *Game) resolveAction() {
+ if g.gameState.Stack.IsEmpty() {
+ return
+ }
+ g.gameState.Stack.Pop()
+ g.clearMapHighlights()
+ g.stackBuffer.RemoveLast()
+}
+
func (g *Game) findObjectAt(x, y int) interface{} {
+ for _, b := range g.widgets {
+ if obj := b.FindObjectAt(x, y); obj != nil {
+ return obj
+ }
+ }
if obj := g.mapView.FindObjectAt(x, y); obj != nil {
return obj
}
@@ -86,33 +179,98 @@ func (g *Game) findObjectAt(x, y int) interface{} {
}
func (g *Game) Update() error {
+ x, y := ebiten.CursorPosition()
+ g.hoverDetector.update(x, y)
+
+ obj := g.findObjectAt(x, y)
+
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
- x, y := ebiten.CursorPosition()
- obj := g.findObjectAt(x, y)
- if obj != nil {
- switch obj.(type) {
- case *game.Tile:
- tile := obj.(*game.Tile)
- if g.activeObject != nil {
- switch g.activeObject.(type) {
- case *game.Unit:
- unit := g.activeObject.(*game.Unit)
- g.moveUnit(unit, tile)
- g.activeObject = nil
- }
- } else {
+ if obj == nil {
+ fmt.Printf("No object found at cursor position (%d, %d)\n", x, y)
+ return nil
+ }
+
+ log.Printf("Active object is of type %T\n", g.selectedObject)
+
+ switch obj.(type) {
+ case ui.Button:
+ button := obj.(ui.Button)
+ button.Click(x, y)
+
+ case *game.Tile:
+ tile := obj.(*game.Tile)
+ if g.selectedObject != nil {
+ switch g.selectedObject.(type) {
+ case *game.MoveActionPrototype:
+ proto := g.selectedObject.(*game.MoveActionPrototype)
+ action := proto.Specialize(tile)
+ g.selectedObject = nil
+ g.declareAction(action)
g.mapView.HighlightTile(tile)
}
- case *game.Unit:
- unit := obj.(*game.Unit)
- g.activeObject = unit
- g.mapView.HighlightTiles(unit.MoveRangeTiles(g.gameState.Map))
- default:
- log.Fatalf("Object of type %T not handled", obj)
+ } else {
+ g.mapView.HighlightTile(tile)
}
- } else {
- fmt.Printf("No object found at cursor position (%d, %d)\n", x, y)
+
+ case game.Permanent:
+ perm := obj.(game.Permanent)
+ if g.selectedObject == nil {
+ actions := perm.GetAvailActionPrototypes()
+ if len(actions) > 0 {
+ g.selectedObject = perm
+ onClick := func(c *ui.Choice, x, y int) {
+ g.removeWidget(c)
+ g.selectedObject = actions[c.GetChoosen(x, y)]
+ }
+ g.addWidget(ui.NewActionChoice(500, 200, actions, onClick))
+ }
+ } else {
+ switch g.selectedObject.(type) {
+ case *game.AttackActionPrototype:
+ proto := g.selectedObject.(*game.AttackActionPrototype)
+ action := proto.Specialize(perm)
+ g.selectedObject = nil
+ g.declareAction(action)
+ g.mapView.HighlightPermanent(perm)
+ }
+ }
+
+ default:
+ log.Fatalf("Object of type %T not handled", obj)
+ }
+
+ switch g.selectedObject.(type) {
+ case *game.MoveActionPrototype:
+ proto := g.selectedObject.(*game.MoveActionPrototype)
+ g.mapView.HighlightTiles(proto.Unit.MoveRangeTiles(g.gameState.Map))
+ case *game.AttackActionPrototype:
+ proto := g.selectedObject.(*game.AttackActionPrototype)
+ perms := proto.Unit.AttackablePermanents(g.gameState.Map)
+ g.mapView.HighlightPermanents(perms)
+ }
+ } else if g.hoverDetector.startedHovering() && g.selectedObject == nil && obj != nil {
+ var hoverHint *ui.TextBox
+ switch obj.(type) {
+ case *game.Unit:
+ u := obj.(*game.Unit)
+ hoverHint = ui.NewUnitInfo(x+ui.TILE_WIDTH, y, u)
+ case game.Permanent:
+ p := obj.(game.Permanent)
+ hoverHint = ui.NewPermInfo(x+ui.TILE_WIDTH, y, p)
}
+
+ if hoverHint != nil {
+ g.addWidget(hoverHint)
+ g.hoverDetector.resetFunc = func() {
+ g.removeWidget(hoverHint)
+ }
+ }
+ } else if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
+ g.resolveAction()
+ } else if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
+ g.selectedObject = nil
+ g.hoverDetector.reset()
+ g.clearMapHighlights()
}
return nil
}
@@ -120,6 +278,12 @@ func (g *Game) Update() error {
func (g *Game) Draw(screen *ebiten.Image) {
g.handLayer.Draw(screen)
g.mapView.Draw(screen)
+ for _, b := range g.widgets {
+ b.Draw(screen)
+ }
+ if !g.gameState.Stack.IsEmpty() {
+ g.stackBuffer.Draw(screen)
+ }
ebitenutil.DebugPrint(screen,
fmt.Sprintf("TPS: %0.2f FPS: %0.2f", ebiten.ActualTPS(), ebiten.ActualFPS()))
}
@@ -129,14 +293,20 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
}
func main() {
+ ui.InitFont()
+
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Muhq's Game")
g := NewGameFromMap("2P-ring-street")
- g.addPlayer("muhq", game.NewDeckFromDeckList(g.gameState.Map.StartDeckList))
+ g.addPlayer("muhq", game.NewDeckFromDeckList(g.gameState.Map.StartDeckList), playerBlue)
muhq := g.getPlayer("muhq")
- g.gameState.AddNewUnit("base/archer", game.Position{0, 0}, muhq)
+ g.gameState.AddNewUnit("base/archer", game.Position{X: 0, Y: 0}, muhq)
+
+ g.addPlayer("enemy", game.NewDeckFromDeckList(g.gameState.Map.StartDeckList), playerGreen)
+ enemy := g.getPlayer("enemy")
+ g.gameState.AddNewArtifact("base/palisade", game.Position{X: 1, Y: 1}, enemy)
if err := ebiten.RunGame(g); err != nil {
log.Fatal(err)