179 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| 	TilesX, TilesY 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.NewMap4[
 | |
| 		components.Position,
 | |
| 		components.Velocity,
 | |
| 		components.Renderable,
 | |
| 		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.NewMap4[
 | |
| 		components.Position,
 | |
| 		components.Velocity,
 | |
| 		components.Renderable,
 | |
| 		components.Obstacle](world)
 | |
| 
 | |
| 	transmapper := generic.NewMap3[
 | |
| 		components.Position,
 | |
| 		components.Renderable,
 | |
| 		components.Transient](world)
 | |
| 
 | |
| 	doormapper := generic.NewMap3[
 | |
| 		components.Position,
 | |
| 		components.Renderable,
 | |
| 		components.Destroyable](world)
 | |
| 
 | |
| 	var pos *components.Position
 | |
| 	var vel *components.Velocity
 | |
| 	var render *components.Renderable
 | |
| 	var transient *components.Transient
 | |
| 	var player *components.Player
 | |
| 	var destroyable *components.Destroyable
 | |
| 
 | |
| 	playerID := ecs.ComponentID[components.Player](world)
 | |
| 	obstacleID := ecs.ComponentID[components.Obstacle](world)
 | |
| 
 | |
| 	observer := observers.GetGameObserver(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, vel, render, player = playermapper.Get(entity)
 | |
| 				observer.AddEntity(entity, playerID)
 | |
| 				vel.Speed = config.PLAYERSPEED
 | |
| 				player.IsPrimary = tile.IsPrimary
 | |
| 				player.Sprites = tile.Tiles
 | |
| 				player.LoopPos = &components.Position{Cellsize: tilesize}
 | |
| 			case tile.Collectible:
 | |
| 				entity := colmapper.New()
 | |
| 				pos, render, _ = colmapper.Get(entity)
 | |
| 			case tile.Obstacle:
 | |
| 				entity := obsmapper.New()
 | |
| 				pos, vel, render, _ = obsmapper.Get(entity)
 | |
| 				vel.Direction = tile.Direction
 | |
| 				vel.PointingAt = tile.Direction
 | |
| 				vel.Speed = config.PLAYERSPEED
 | |
| 				observer.AddEntity(entity, obstacleID)
 | |
| 			case tile.Transient:
 | |
| 				entity := transmapper.New()
 | |
| 				pos, render, transient = transmapper.Get(entity)
 | |
| 				transient.Sprites = tile.TileNames
 | |
| 			case tile.Destroyable:
 | |
| 				entity := doormapper.New()
 | |
| 				pos, render, destroyable = doormapper.Get(entity)
 | |
| 				destroyable.Sprites = tile.Tiles
 | |
| 			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,
 | |
| 		TilesX:   width / tilesize,
 | |
| 		TilesY:   height / tilesize,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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) RemoveTile(point image.Point) {
 | |
| 	delete(grid.Map, point)
 | |
| }
 | |
| 
 | |
| func (grid *Grid) SetFloorTile(point image.Point) {
 | |
| 	grid.Map[point] = assets.Tiles[' ']
 | |
| }
 | |
| 
 | |
| func (grid *Grid) SetSolidTile(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)
 | |
| }
 |