diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2022-12-27 14:29:39 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-01-27 16:43:43 +0100 |
| commit | 7f7389422834f628c9e45278e90fab6911173b25 (patch) | |
| tree | 0bd8c0f4daaed7619d467cf82e7f7add09ef1f32 | |
| parent | 3c2ee4ee48480a28dc606878503f314ec8862d45 (diff) | |
| download | muhqs-game-7f7389422834f628c9e45278e90fab6911173b25.tar.gz muhqs-game-7f7389422834f628c9e45278e90fab6911173b25.zip | |
intermediate commit
| -rw-r--r-- | go/.gitignore | 3 | ||||
| -rw-r--r-- | go/Makefile | 25 | ||||
| -rw-r--r-- | go/assets/retrieve.go | 82 | ||||
| -rw-r--r-- | go/client/main.go | 58 | ||||
| -rw-r--r-- | go/game/action.go | 6 | ||||
| -rw-r--r-- | go/game/card.go | 18 | ||||
| -rw-r--r-- | go/game/deck.go | 5 | ||||
| -rw-r--r-- | go/game/discardPiles.go | 5 | ||||
| -rw-r--r-- | go/game/hand.go | 5 | ||||
| -rw-r--r-- | go/game/map.go | 181 | ||||
| -rw-r--r-- | go/game/map_test.go | 53 | ||||
| -rw-r--r-- | go/game/permanent.go | 8 | ||||
| -rw-r--r-- | go/game/player.go | 10 | ||||
| -rw-r--r-- | go/game/pos.go | 10 | ||||
| -rw-r--r-- | go/game/set.go | 15 | ||||
| -rw-r--r-- | go/game/stack.go | 5 | ||||
| -rw-r--r-- | go/game/state.go | 12 | ||||
| -rw-r--r-- | go/game/store.go | 5 | ||||
| -rw-r--r-- | go/game/tile.go | 87 | ||||
| -rw-r--r-- | go/go.mod | 17 | ||||
| -rw-r--r-- | go/go.sum | 80 | ||||
| -rw-r--r-- | go/server/main.go | 13 | ||||
| -rw-r--r-- | go/ui/map.go | 109 |
23 files changed, 812 insertions, 0 deletions
diff --git a/go/.gitignore b/go/.gitignore new file mode 100644 index 00000000..eff64396 --- /dev/null +++ b/go/.gitignore @@ -0,0 +1,3 @@ +client/client +client/client.wasm +server/server diff --git a/go/Makefile b/go/Makefile new file mode 100644 index 00000000..220a1022 --- /dev/null +++ b/go/Makefile @@ -0,0 +1,25 @@ +PACKAGES := assets game server client + +APPS := server client +OBJS := $(foreach app,$(APPS),$(app)/$(app)) + +.PHONY: all fmt test server/server client/client client/client.wasm +all: $(OBJS) + +server/server: + @echo Building Server ... + pushd $(@D) >/dev/null; go build; popd >/dev/null + +client/client: + @echo Building Client ... + pushd $(@D) >/dev/null; go build; popd >/dev/null + +client/client.wasm: + @echo Building Client.wasm ... + pushd $(@D) >/dev/null; env GOOS=js GOARCH=wasm go build -o $(@F); popd >/dev/null + +fmt: + for package in $(PACKAGES); do pushd $$package >/dev/null; go fmt; popd >/dev/null; done + +test: + for package in $(PACKAGES); do pushd $$package >/dev/null; go test; popd >/dev/null; done diff --git a/go/assets/retrieve.go b/go/assets/retrieve.go new file mode 100644 index 00000000..fea3d141 --- /dev/null +++ b/go/assets/retrieve.go @@ -0,0 +1,82 @@ +package assets + +import ( + "image/png" + "log" + "net/http" + "path" + "strings" + + "github.com/hajimehoshi/ebiten/v2" +) + +const ( + PROTOCOL string = "http://" + BASE_URL string = "localhost:8000/" + IMAGE_URL_PART string = "html/build/latex-build" + TILE_URL_PART string = "assets/tiles" + SYMBOL_URL_PART string = "assets/symbols" +) + +func retrieveAsset(path string) *ebiten.Image { + log.Printf("loading %s\n", path) + resp, err := http.Get(PROTOCOL + path) + if err != nil { + log.Fatal(err) + } + + defer resp.Body.Close() + + img, err := png.Decode(resp.Body) + if err != nil { + log.Fatal("decoding ", err) + } + return ebiten.NewImageFromImage(img) +} + +var cards = map[string]map[string]map[string]*ebiten.Image{} + +func GetCard(cardPath string, language string) *ebiten.Image { + card := path.Base(cardPath) + set := path.Dir(cardPath) + img, found := cards[language][set][card] + if !found { + url := path.Join(BASE_URL, IMAGE_URL_PART, language, cardPath+".png") + img = retrieveAsset(url) + + if cards[language] == nil { + cards[language] = map[string]map[string]*ebiten.Image{} + } + + if cards[language][set] == nil { + cards[language][set] = map[string]*ebiten.Image{} + } + + cards[language][set][card] = img + } + return img +} + +var tiles = map[string]*ebiten.Image{} + +func GetTile(tile string) *ebiten.Image { + img, found := tiles[tile] + if !found { + url := path.Join(BASE_URL, TILE_URL_PART, strings.Replace(tile, " ", "_", -1)+".png") + img = retrieveAsset(url) + tiles[tile] = img + } + return img +} + +var symbols = map[string]*ebiten.Image{} + +func GetSymbol(symbol string) *ebiten.Image { + img, found := symbols[symbol] + if !found { + url := path.Join(BASE_URL, SYMBOL_URL_PART, symbol+".png") + img = retrieveAsset(url) + symbols[symbol] = img + } + return img +} diff --git a/go/client/main.go b/go/client/main.go new file mode 100644 index 00000000..b91b15f7 --- /dev/null +++ b/go/client/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "log" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + + "muhq.space/muhqs-game/go/assets" + "muhq.space/muhqs-game/go/game" + "muhq.space/muhqs-game/go/ui" +) + +type Game struct{} + +const ( + screenWidth = 1920 + screenHeight = 1080 +) + +var ( + m *ui.MapLayer + players []game.Player +) + +func (g *Game) Update() error { + return nil +} + +func (g *Game) 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) + m.Draw(screen) + ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f FPS: %0.2f", ebiten.ActualTPS(), ebiten.ActualFPS())) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + ebiten.SetWindowSize(screenWidth, screenHeight) + ebiten.SetWindowTitle("Muhq's Game") + _m, err := game.GetMap("2P-ring-street") + if err != nil { + log.Fatal(err) + } + m = ui.NewMapLayerFromMap(_m) + + if err := ebiten.RunGame(&Game{}); err != nil { + log.Fatal(err) + } +} diff --git a/go/game/action.go b/go/game/action.go new file mode 100644 index 00000000..723d4866 --- /dev/null +++ b/go/game/action.go @@ -0,0 +1,6 @@ +package game + +type Action struct { + Owner *Player + Card *Card +} diff --git a/go/game/card.go b/go/game/card.go new file mode 100644 index 00000000..6cd20257 --- /dev/null +++ b/go/game/card.go @@ -0,0 +1,18 @@ +package game + +type CardType int + +const ( + Unit CardType = iota + 1 + Spell + Artifact + Equipment + Potion + Itention +) + +type Card struct { + Name string + Set SetIdentifier + Type CardType +} diff --git a/go/game/deck.go b/go/game/deck.go new file mode 100644 index 00000000..33126ae8 --- /dev/null +++ b/go/game/deck.go @@ -0,0 +1,5 @@ +package game + +type Deck struct { + Cards []Card +} diff --git a/go/game/discardPiles.go b/go/game/discardPiles.go new file mode 100644 index 00000000..9c1c1e45 --- /dev/null +++ b/go/game/discardPiles.go @@ -0,0 +1,5 @@ +package game + +type DiscardPile struct { + Cards []Card +} diff --git a/go/game/hand.go b/go/game/hand.go new file mode 100644 index 00000000..42187f5a --- /dev/null +++ b/go/game/hand.go @@ -0,0 +1,5 @@ +package game + +type Hand struct { + Cards []Card +} diff --git a/go/game/map.go b/go/game/map.go new file mode 100644 index 00000000..0b6fe87c --- /dev/null +++ b/go/game/map.go @@ -0,0 +1,181 @@ +package game + +import ( + "fmt" + "io" + "log" + "net/http" + "os" + "path" + "strings" + + "gopkg.in/yaml.v3" +) + +const ( + PROTOCOL string = "http://" + BASE_URL string = "localhost:8000/" + MAP_URL_PART string = "html/build/maps" + + DEFAULT_RESSOURCE_GAIN int = 5 +) + +type MapYml struct { + Map string `yaml:"map"` + Symbols map[string]string `yaml:"symbols"` +} + +type Map struct { + Tiles [][]Tile + RessourceGain int +} + +func parseMap(r io.Reader) (*Map, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + mapYml := MapYml{} + err = yaml.Unmarshal(data, &mapYml) + if err != nil { + return nil, err + } + + m := &Map{[][]Tile{}, DEFAULT_RESSOURCE_GAIN} + + mapYml.Symbols[" "] = "neutral" + + rows := strings.Split(mapYml.Map, "\n") + for y := 0; y < len(rows); y++ { + m.Tiles = append(m.Tiles, []Tile{}) + row := rows[y] + for x := 0; x < len(rows[y]); x++ { + s := string(row[x]) + tile, err := NewTileFromString(mapYml.Symbols[s], Position{x, y}) + if err != nil { + return nil, err + } + m.Tiles[y] = append(m.Tiles[y], tile) + } + } + + m.selectStreets() + + return m, nil +} + +func (m *Map) selectStreets() { + for y := 0; y < len(m.Tiles); y++ { + for x := 0; x < len(m.Tiles[y]); x++ { + tile := &m.Tiles[y][x] + if tile.Type != TileTypes.Street { + continue + } + connections, left, right, above, below := m.FindStreetConnections(x, y) + + // Decide if curve or straight + if connections == 2 && (left || right) && (above || below) { + tile.Raw = "street 2" + } + + if connections > 2 { + tile.Raw = fmt.Sprintf("street %d", connections) + } + } + } +} + +func (m *Map) FindNeighbours(x, y int) (left, right, above, below *Tile) { + left, right, above, below = nil, nil, nil, nil + if y > 0 { + above = &m.Tiles[y-1][x] + } + + if y < len(m.Tiles) { + below = &m.Tiles[y+1][x] + } + + if x > 0 { + left = &m.Tiles[y][x-1] + } + + if x < len(m.Tiles[y]) { + right = &m.Tiles[y][x+1] + } + + return left, right, above, below +} + +func (m *Map) FindAnyConnections(x, y int) (connections int, left, right, above, below bool) { + leftT, rightT, aboveT, belowT := m.FindNeighbours(x, y) + connections = 0 + + if leftT.Type != TileTypes.Neutral { + left = true + connections += 1 + } + if rightT.Type != TileTypes.Neutral { + right = true + connections += 1 + } + if aboveT.Type != TileTypes.Neutral { + above = true + connections += 1 + } + if belowT.Type != TileTypes.Neutral { + below = true + connections += 1 + } + + return +} + +func (m *Map) FindStreetConnections(x int, y int) (connections int, left, right, above, below bool) { + leftT, rightT, aboveT, belowT := m.FindNeighbours(x, y) + connections = 0 + + if leftT.Type == TileTypes.Street { + left = true + connections += 1 + } + if rightT.Type == TileTypes.Street { + right = true + connections += 1 + } + if aboveT.Type == TileTypes.Street { + above = true + connections += 1 + } + if belowT.Type == TileTypes.Street { + below = true + connections += 1 + } + + return +} + +func GetMap(name string) (*Map, error) { + url := path.Join(BASE_URL, MAP_URL_PART, name+".yml") + log.Printf("loading map from %s\n", url) + resp, err := http.Get(PROTOCOL + url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + return parseMap(resp.Body) +} + +func LoadMap(file string) (*Map, error) { + f, err := os.Open("/tmp/123") + if err != nil { + log.Fatal("Opening map ", file, " failed: ", err) + } + defer f.Close() + + return parseMap(f) +} + +func (m *Map) TileAt(p Position) *Tile { + return &m.Tiles[p.Y][p.X] +} diff --git a/go/game/map_test.go b/go/game/map_test.go new file mode 100644 index 00000000..732c8b86 --- /dev/null +++ b/go/game/map_test.go @@ -0,0 +1,53 @@ +package game + +import ( + "strings" + "testing" +) + +func TestParseMap(t *testing.T) { + s := `map: |1+ + T H HT + FSSSSS2H + SHSH HS + S SST S + H HFH H + S TSS S + SH HSHS + H1SSSSSF + TH H T + +symbols: + T: tower + H: house + F: farm + S: street + 1: spawn player 1 + 2: spawn player 2 + +kings: + - (1,9) + - (9,1)` + + r := strings.NewReader(s) + m, err := parseMap(r) + if err != nil { + t.Fatalf("Parsing failed with: %v", err) + } + + if m.Tiles[0][0].Type != TileTypes.Tower { + t.Fatalf("Unexpected tile at 0,0: %v (expected %v)", m.Tiles[0][0], TileTypes.Tower) + } + + for y := 0; y < len(m.Tiles); y++ { + for x := 0; y < len(m.Tiles[y]); y++ { + if m.TileAt(Position{x, y}) != &m.Tiles[y][x] { + t.Fatalf("TileAt and Tiles differ") + } + } + } + + if m.RessourceGain != DEFAULT_RESSOURCE_GAIN { + t.Fatalf("ressource gain is not the default one") + } +} diff --git a/go/game/permanent.go b/go/game/permanent.go new file mode 100644 index 00000000..a4418e3e --- /dev/null +++ b/go/game/permanent.go @@ -0,0 +1,8 @@ +package game + +type Permanent struct { + Card *Card + Tile *Tile + Damage int + Pile []*Permanent +} diff --git a/go/game/player.go b/go/game/player.go new file mode 100644 index 00000000..6b9041ad --- /dev/null +++ b/go/game/player.go @@ -0,0 +1,10 @@ +package game + +type Player struct { + Id int + Name string + Hand *Hand + DiscardPile *DiscardPile + Deck *Deck + Ressource int +} diff --git a/go/game/pos.go b/go/game/pos.go new file mode 100644 index 00000000..2b3459cd --- /dev/null +++ b/go/game/pos.go @@ -0,0 +1,10 @@ +package game + +type Position struct { + X int + Y int +} + +func INVALID_POSITION() Position { + return Position{-1, -1} +} diff --git a/go/game/set.go b/go/game/set.go new file mode 100644 index 00000000..4136eac6 --- /dev/null +++ b/go/game/set.go @@ -0,0 +1,15 @@ +package game + +type SetIdentifier int + +const ( + Base SetIdentifier = iota + 1 + Magic + Equipments + Misc + Nautics + Potions + Kraken + Tyrant + Exp1 +) diff --git a/go/game/stack.go b/go/game/stack.go new file mode 100644 index 00000000..5d62e601 --- /dev/null +++ b/go/game/stack.go @@ -0,0 +1,5 @@ +package game + +type Stack struct { + Actions []Action +} diff --git a/go/game/state.go b/go/game/state.go new file mode 100644 index 00000000..9dcf73c4 --- /dev/null +++ b/go/game/state.go @@ -0,0 +1,12 @@ +package game + +type State struct { + Decks []Deck + Stores []Store + Map Map + Stack Stack + Hands []Hand + DiscardPiles []DiscardPile + Players []Player + Permanents []Permanent +} diff --git a/go/game/store.go b/go/game/store.go new file mode 100644 index 00000000..3f5793fb --- /dev/null +++ b/go/game/store.go @@ -0,0 +1,5 @@ +package game + +type Store struct { + Cards []Card +} diff --git a/go/game/tile.go b/go/game/tile.go new file mode 100644 index 00000000..fb62420b --- /dev/null +++ b/go/game/tile.go @@ -0,0 +1,87 @@ +package game + +import ( + "log" + "strconv" + "strings" +) + +type TileType int + +const ( + neutral TileType = iota + 1 + tower + house + farm + street + spawn +) + +func (t TileType) String() string { + switch t { + case neutral: + return "neutral" + case tower: + return "tower" + case house: + return "house" + case farm: + return "farm" + case street: + return "street" + case spawn: + return "spawn" + } + log.Panicf("Switch not exhausting %d", t) + return "" +} + +var TileTypes = struct { + Neutral TileType + Tower TileType + House TileType + Farm TileType + Street TileType + Spawn TileType +}{ + Neutral: neutral, + Tower: tower, + House: house, + Farm: farm, + Street: street, + Spawn: spawn} + +var TileNames = map[string]TileType{ + "neutral": neutral, + "tower": tower, + "house": house, + "farm": farm, + "street": street, + "spawn": spawn, +} + +type Tile struct { + Position Position + Permanent *Permanent + Type TileType + Raw string + PlayerId int +} + +func INVALID_TILE() Tile { + return Tile{INVALID_POSITION(), nil, 0, "", -1} +} + +func NewTileFromString(tile string, pos Position) (Tile, error) { + tokens := strings.Split(tile, " ") + tileType := TileNames[strings.ToLower(tokens[0])] + t := Tile{pos, nil, tileType, tile, -1} + if tokens[0] == "spawn" { + playerId, err := strconv.Atoi(tokens[2]) + if err != nil { + return INVALID_TILE(), err + } + t.PlayerId = playerId + } + return t, nil +} diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 00000000..963c3bd7 --- /dev/null +++ b/go/go.mod @@ -0,0 +1,17 @@ +module muhq.space/muhqs-game/go + +go 1.19 + +require github.com/hajimehoshi/ebiten/v2 v2.4.13 + +require ( + github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad // indirect + github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 // indirect + github.com/jezek/xgb v1.0.1 // indirect + golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect + golang.org/x/image v0.1.0 // indirect + golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect + golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 00000000..99412296 --- /dev/null +++ b/go/go.sum @@ -0,0 +1,80 @@ +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 h1:A8UnJ/5OKzki4HBDwoRQz7I6sxKsokpMXcGh+fUxpfc= +github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad h1:kX51IjbsJPCvzV9jUoVQG9GEUqIq5hjfYzXTqQ52Rh8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/hajimehoshi/bitmapfont/v2 v2.2.2/go.mod h1:Ua/x9Dkz7M9CU4zr1VHWOqGwjKdXbOTRsH7lWfb1Co0= +github.com/hajimehoshi/ebiten/v2 v2.4.13 h1:ZZ5y+bFkAbUeD2WGquHF+xSbg83SIbcsxCwEVeZgHWM= +github.com/hajimehoshi/ebiten/v2 v2.4.13/go.mod h1:BZcqCU4XHmScUi+lsKexocWcf4offMFwfp8dVGIB/G4= +github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 h1:s01qIIRG7vN/5ndLwkDktjx44ulFk6apvAjVBYR50Yo= +github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= +github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= +github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= +github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= +github.com/jakecoffman/cp v1.2.1/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= +github.com/jezek/xgb v1.0.1 h1:YUGhxps0aR7J2Xplbs23OHnV1mWaxFVcOl9b+1RQkt8= +github.com/jezek/xgb v1.0.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= +github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= +golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= +golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go/server/main.go b/go/server/main.go new file mode 100644 index 00000000..ddc689ef --- /dev/null +++ b/go/server/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + + "muhq.space/muhqs-game/go/game" +) + +var games = []game.State{} + +func main() { + fmt.Println("Started muhq's game server") +} diff --git a/go/ui/map.go b/go/ui/map.go new file mode 100644 index 00000000..2e83ad2d --- /dev/null +++ b/go/ui/map.go @@ -0,0 +1,109 @@ +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{}) +} |
