diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-01-12 20:58:14 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-01-27 16:43:44 +0100 |
| commit | 9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74 (patch) | |
| tree | b0ed4b509da71d0d6320f979a1eebbd91fdf7321 /go/ui | |
| parent | 41d16fb4910fc0402b6b081302c953f7dabc8cf9 (diff) | |
| download | muhqs-game-9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74.tar.gz muhqs-game-9df5cc7b55d8ac9ecd775a14a14f80a6c36c4d74.zip | |
implement unit movement
Diffstat (limited to 'go/ui')
| -rw-r--r-- | go/ui/map.go | 109 | ||||
| -rw-r--r-- | go/ui/mapView.go | 212 |
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{}) +} |
