package grid import ( "image" "log" "openquell/assets" "openquell/components" "openquell/config" "openquell/observers" "github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/generic" ) type Grid struct { World *ecs.World Width int Height int Size int Tilesize int Map map[image.Point]*assets.Tile } // FIXME: put component addition into extra callable function, to be called // by game.go in a level-loop. Also remove components of previous level func NewGrid(world *ecs.World, tilesize, width, height int, mapslice map[image.Point]*assets.Tile) *Grid { // we use this to turn our tiles into iterable entities, used for // collision detection, transformation and other things playermapper := generic.NewMap5[ components.Position, components.Velocity, components.Renderable, components.Speed, components.Player](world) solidmapper := generic.NewMap4[ components.Position, components.Renderable, components.Tilish, components.Solid](world) emptymapper := generic.NewMap2[components.Position, components.Tilish](world) colmapper := generic.NewMap3[ components.Position, components.Renderable, components.Collectible](world) obsmapper := generic.NewMap5[ components.Position, components.Velocity, components.Renderable, components.Speed, components.Obstacle](world) transmapper := generic.NewMap3[ components.Position, components.Renderable, components.Transient](world) var pos *components.Position var vel *components.Velocity var render *components.Renderable var speed *components.Speed var transient *components.Transient playerobserver := observers.GetPlayerObserver(world) obstacleobserver := observers.GetObstacleObserver(world) for point, tile := range mapslice { switch tile.Renderable { case true: switch { case tile.Solid: entity := solidmapper.New() pos, render, _, _ = solidmapper.Get(entity) case tile.Player: entity := playermapper.New() pos, _, render, speed, _ = playermapper.Get(entity) playerobserver.AddEntity(entity) speed.Value = config.PLAYERSPEED case tile.Collectible: entity := colmapper.New() pos, render, _ = colmapper.Get(entity) case tile.Obstacle: entity := obsmapper.New() pos, vel, render, speed, _ = obsmapper.Get(entity) vel.Direction = tile.Direction vel.PointingAt = tile.Direction speed.Value = config.PLAYERSPEED obstacleobserver.AddEntity(entity) case tile.Transient: entity := transmapper.New() pos, render, transient = transmapper.Get(entity) transient.Sprites = tile.TileNames default: log.Fatalln("unsupported tile type encountered") } render.Image = tile.Sprite default: // empty cell, this is where the player[s] move. No // sprite required since every level has a background // image. entity := emptymapper.New() pos, _ = emptymapper.Get(entity) } // however, every tile has a position pos.Update(point.X*tilesize, point.Y*tilesize, tilesize) } return &Grid{ Size: len(mapslice), Tilesize: tilesize, Width: width, Height: height, Map: mapslice, World: world, } } // return the tile the moving object would end up on if indeed moving func (grid *Grid) GetTile( position *components.Position, velocity *components.Velocity) *assets.Tile { newpoint := image.Point{ int(position.X+velocity.Data.X) / grid.Tilesize, int(position.Y+velocity.Data.Y) / grid.Tilesize, } tile := grid.Map[newpoint] return tile } func (grid *Grid) SetTile(tile *assets.Tile, point image.Point) { solidmapper := generic.NewMap4[ components.Position, components.Renderable, components.Tilish, components.Solid](grid.World) grid.Map[point] = tile entity := solidmapper.New() pos, render, _, _ := solidmapper.Get(entity) render.Image = tile.Sprite pos.Update(point.X*grid.Tilesize, point.Y*grid.Tilesize, grid.Tilesize) }