aboutsummaryrefslogtreecommitdiff
path: root/go/ui
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-01-12 20:58:14 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:44 +0100
commit9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74 (patch)
treeb0ed4b509da71d0d6320f979a1eebbd91fdf7321 /go/ui
parent41d16fb4910fc0402b6b081302c953f7dabc8cf9 (diff)
downloadmuhqs-game-9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74.tar.gz
muhqs-game-9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74.zip
implement unit movement
Diffstat (limited to 'go/ui')
-rw-r--r--go/ui/map.go109
-rw-r--r--go/ui/mapView.go212
2 files changed, 212 insertions, 109 deletions
diff --git a/go/ui/map.go b/go/ui/map.go
deleted file mode 100644
index 2e83ad2d..00000000
--- a/go/ui/map.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package ui
-
-import (
- "log"
- "math"
-
- "github.com/hajimehoshi/ebiten/v2"
- "muhq.space/muhqs-game/go/assets"
- "muhq.space/muhqs-game/go/game"
-)
-
-const (
- TILE_WIDTH int = 50
- TILE_HEIGHT int = 50
-)
-
-type MapLayer struct {
- m *game.Map
- img *ebiten.Image
-}
-
-func NewMapLayerFromMap(m *game.Map) *MapLayer {
- mv := MapLayer{m, nil}
- return &mv
-}
-
-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,
- // the origin point is the upper-left corner.
- op.GeoM.Translate(-float64(TILE_WIDTH)/2, -float64(TILE_HEIGHT)/2)
-
- // Rotate the image. As a result, the anchor point of this rotate is
- // the center of the image.
- op.GeoM.Rotate(radian)
-
- // Reset translation
- op.GeoM.Translate(float64(TILE_WIDTH)/2, float64(TILE_HEIGHT)/2)
-}
-
-func (m *MapLayer) rotateStreet(x, y int, op *ebiten.DrawImageOptions) {
- connections, left, right, above, below := m.m.FindStreetConnections(x, y)
-
- if connections == 0 {
- // This street is not connected to another street ->
- // check any other non neutral tiles
- connections, left, right, above, below = m.m.FindAnyConnections(x, y)
- }
-
- // This street is not connected to anything. Seams odd!
- if connections == 0 {
- log.Printf("Street at (%d, %d) is not connected", x, y)
- } else if (connections == 1 ||
- (connections == 2 && ((left && right) || (above && below)))) &&
- (above || below) {
- rotateStreetImg(x, y, math.Pi/2, op)
- } else if connections == 2 {
- // normal orientation above and right
- if right && below {
- rotateStreetImg(x, y, 3*math.Pi/2, op)
- } else if left && below {
- rotateStreetImg(x, y, math.Pi, op)
- } else if above && left {
- rotateStreetImg(x, y, math.Pi/2, op)
- }
- } else if connections == 3 {
- // normal orientation left above right
- if above && right && below {
- rotateStreetImg(x, y, 3*math.Pi/2, op)
- } else if left && below && right {
- rotateStreetImg(x, y, math.Pi, op)
- } else if below && left && above {
- rotateStreetImg(x, y, math.Pi/2, op)
- }
- }
-}
-
-func (m *MapLayer) Draw(screen *ebiten.Image) {
- if m.img == nil {
- // TODO: support non symetric maps
- maxWidth := len(m.m.Tiles[0]) * TILE_WIDTH
- maxHeight := len(m.m.Tiles) * TILE_HEIGHT
- m.img = ebiten.NewImage(maxWidth, maxHeight)
-
- x_px, y_px := 0.0, 0.0
- for y := 0; y < len(m.m.Tiles); y++ {
- for x := 0; x < len(m.m.Tiles[y]); x++ {
- tile := &m.m.Tiles[y][x]
- tileImg := assets.GetTile(tile.Raw)
-
- op := &ebiten.DrawImageOptions{}
- // Rotate Street
- if tile.Type == game.TileTypes.Street {
- m.rotateStreet(x, y, op)
- }
-
- op.GeoM.Translate(x_px, y_px)
-
- m.img.DrawImage(tileImg, op)
-
- x_px += float64(TILE_WIDTH)
- }
- x_px = 0
- y_px += float64(TILE_HEIGHT)
- }
- }
-
- screen.DrawImage(m.img, &ebiten.DrawImageOptions{})
-}
diff --git a/go/ui/mapView.go b/go/ui/mapView.go
new file mode 100644
index 00000000..10929f68
--- /dev/null
+++ b/go/ui/mapView.go
@@ -0,0 +1,212 @@
+package ui
+
+import (
+ "log"
+ "math"
+ "strings"
+
+ "golang.org/x/exp/slices"
+
+ "github.com/hajimehoshi/ebiten/v2"
+ "muhq.space/muhqs-game/go/assets"
+ "muhq.space/muhqs-game/go/game"
+)
+
+const (
+ PERMANENT_WIDTH int = 40
+ PERMANENT_HEIGHT int = 40
+
+ TILE_WIDTH int = 50
+ TILE_HEIGHT int = 50
+)
+
+type MapView struct {
+ gameState *game.State
+ mapLayer *ebiten.Image
+ permanentsLayer *ebiten.Image
+ tileHighlights []game.Position
+ permanentsHighlights []*game.Permanent
+}
+
+func NewMapView(g *game.State) *MapView {
+ vw := MapView{g, nil, nil, []game.Position{}, []*game.Permanent{}}
+ return &vw
+}
+
+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,
+ // the origin point is the upper-left corner.
+ op.GeoM.Translate(-float64(TILE_WIDTH)/2, -float64(TILE_HEIGHT)/2)
+
+ // Rotate the image. As a result, the anchor point of this rotate is
+ // the center of the image.
+ op.GeoM.Rotate(radian)
+
+ // Reset translation
+ op.GeoM.Translate(float64(TILE_WIDTH)/2, float64(TILE_HEIGHT)/2)
+}
+
+func (vw *MapView) rotateStreet(x, y int, op *ebiten.DrawImageOptions) {
+ connections, left, right, above, below := vw.gameState.Map.FindStreetConnections(x, y)
+
+ if connections == 0 {
+ // This street is not connected to another street ->
+ // check any other non neutral tiles
+ connections, left, right, above, below = vw.gameState.Map.FindAnyConnections(x, y)
+ }
+
+ // This street is not connected to anything. Seams odd!
+ if connections == 0 {
+ log.Printf("Street at (%d, %d) is not connected", x, y)
+ } else if (connections == 1 ||
+ (connections == 2 && ((left && right) || (above && below)))) &&
+ (above || below) {
+ rotateStreetImg(x, y, math.Pi/2, op)
+ } else if connections == 2 {
+ // normal orientation above and right
+ if right && below {
+ rotateStreetImg(x, y, 3*math.Pi/2, op)
+ } else if left && below {
+ rotateStreetImg(x, y, math.Pi, op)
+ } else if above && left {
+ rotateStreetImg(x, y, math.Pi/2, op)
+ }
+ } else if connections == 3 {
+ // normal orientation left above right
+ if above && right && below {
+ rotateStreetImg(x, y, 3*math.Pi/2, op)
+ } else if left && below && right {
+ rotateStreetImg(x, y, math.Pi, op)
+ } else if below && left && above {
+ rotateStreetImg(x, y, math.Pi/2, op)
+ }
+ }
+}
+
+func (vw *MapView) newLayerImage() *ebiten.Image {
+ // TODO: support non symetric maps
+ maxWidth := len(vw.gameState.Map.Tiles[0]) * TILE_WIDTH
+ maxHeight := len(vw.gameState.Map.Tiles) * TILE_HEIGHT
+ return ebiten.NewImage(maxWidth, maxHeight)
+}
+
+func (vw *MapView) drawMapLayer(screen *ebiten.Image) {
+ if vw.mapLayer == nil {
+ vw.mapLayer = vw.newLayerImage()
+
+ x_px, y_px := 0.0, 0.0
+ for y := 0; y < len(vw.gameState.Map.Tiles); y++ {
+ for x := 0; x < len(vw.gameState.Map.Tiles[y]); x++ {
+ pos := game.Position{x, y}
+ tile := vw.gameState.Map.TileAt(pos)
+ tileImg := assets.GetTile(tile.Raw)
+
+ op := &ebiten.DrawImageOptions{}
+ // Rotate Street
+ if tile.Type == game.TileTypes.Street {
+ vw.rotateStreet(x, y, op)
+ }
+
+ op.GeoM.Translate(x_px, y_px)
+
+ if slices.Contains(vw.tileHighlights, pos) {
+ op.ColorM.Scale(1, 0, 0, 0.5)
+ }
+
+ vw.mapLayer.DrawImage(tileImg, op)
+
+ x_px += float64(TILE_WIDTH)
+ }
+ x_px = 0
+ y_px += float64(TILE_HEIGHT)
+ }
+ }
+
+ screen.DrawImage(vw.mapLayer, &ebiten.DrawImageOptions{})
+}
+
+func (vw *MapView) drawPermanentsLayer(screen *ebiten.Image) {
+ if vw.permanentsLayer == nil {
+ vw.permanentsLayer = vw.newLayerImage()
+
+ for _, p := range vw.gameState.Permanents {
+ permanentSymbol := assets.GetSymbol(strings.ToLower(p.Card.Name))
+ // TODO: Implement generic symbols
+ if permanentSymbol == nil {
+ continue
+ }
+
+ op := &ebiten.DrawImageOptions{}
+ x_px := p.Tile.Position.X * TILE_WIDTH + (TILE_WIDTH - PERMANENT_WIDTH) / 2
+ y_px := p.Tile.Position.Y * TILE_HEIGHT + (TILE_HEIGHT - PERMANENT_HEIGHT) / 2
+ op.GeoM.Translate(float64(x_px), float64(y_px))
+ vw.permanentsLayer.DrawImage(permanentSymbol, op)
+ }
+ }
+
+ screen.DrawImage(vw.permanentsLayer, &ebiten.DrawImageOptions{})
+}
+
+func (vw *MapView) Draw(screen *ebiten.Image) {
+ vw.drawMapLayer(screen)
+ vw.drawPermanentsLayer(screen)
+}
+
+func (vw *MapView) ForceRedraw() {
+ vw.mapLayer = nil
+ vw.permanentsLayer = nil
+}
+
+func (vw *MapView) FindObjectAt(x_px, y_px int) interface{} {
+ x := x_px / TILE_WIDTH
+ y := y_px / TILE_HEIGHT
+
+ // TODO: detect if a permanent or the containing tile was selected
+ for _, u := range vw.gameState.Units {
+ pos := u.Tile.Position
+ if pos.X == x && pos.Y == y {
+ return u
+ }
+ }
+
+ if y < len(vw.gameState.Map.Tiles) && x < len(vw.gameState.Map.Tiles[y]) {
+ return &vw.gameState.Map.Tiles[y][x]
+ }
+
+ return nil
+}
+
+func (vw *MapView) HighlightPositions(pos []game.Position) {
+ vw.ForceRedraw()
+ vw.tileHighlights = pos
+}
+
+func (vw *MapView) HighlightTiles(tiles []*game.Tile) {
+ pos := make([]game.Position, 0, len(tiles))
+ for _, t := range tiles {
+ pos = append(pos, t.Position)
+ }
+ vw.HighlightPositions(pos)
+}
+
+func (vw *MapView) HighlightTile(t *game.Tile) {
+ vw.HighlightTiles([]*game.Tile{t})
+}
+
+func (vw *MapView) ClearTileHighlights() {
+ vw.HighlightTiles([]*game.Tile{})
+}
+
+func (vw *MapView) HighlightPermanents(permanents []*game.Permanent) {
+ vw.ForceRedraw()
+ vw.permanentsHighlights = permanents
+}
+
+func (vw *MapView) HighlightPermanent(p *game.Permanent) {
+ vw.HighlightPermanents([]*game.Permanent{p})
+}
+
+func (vw *MapView) ClearPermanentsHighlights() {
+ vw.HighlightPermanents([]*game.Permanent{})
+}