diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-01-20 03:18:06 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-01-27 16:43:44 +0100 |
| commit | c4cb9637f5c85d03aa162968f0dfec3c193fc9dc (patch) | |
| tree | 3ad32e5dfd7c4ae72f66b86e77edef7b700a8b7a /go/client | |
| parent | 9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74 (diff) | |
| download | muhqs-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.go | 236 |
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) |
