openquell/grid/grid.go

238 lines
6.1 KiB
Go
Raw Normal View History

2024-02-10 19:45:06 +01:00
package grid
2024-02-06 15:26:20 +01:00
import (
"image"
"log"
"openquell/assets"
"openquell/components"
2024-02-11 14:24:30 +01:00
"openquell/config"
"openquell/observers"
2024-02-06 15:26:20 +01:00
2024-02-10 19:45:06 +01:00
"github.com/mlange-42/arche/ecs"
2024-02-06 15:26:20 +01:00
"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
2024-02-06 15:26:20 +01:00
}
// FIXME: put component addition into extra callable function, to be called
// by game.go in a level-loop. Also remove components of previous level
2024-02-10 19:45:06 +01:00
func NewGrid(world *ecs.World,
tilesize, width, height int, mapslice map[image.Point]*assets.Tile) *Grid {
2024-02-06 15:26:20 +01:00
// we use this to turn our tiles into iterable entities, used for
// collision detection, transformation and other things
playermapper := generic.NewMap4[
2024-02-06 15:26:20 +01:00
components.Position,
components.Velocity,
components.Renderable,
2024-02-10 19:45:06 +01:00
components.Player](world)
2024-02-07 18:01:58 +01:00
2024-02-06 15:26:20 +01:00
solidmapper := generic.NewMap4[
components.Position,
components.Renderable,
components.Tilish,
2024-02-10 19:45:06 +01:00
components.Solid](world)
2024-02-07 18:01:58 +01:00
2024-02-10 19:45:06 +01:00
emptymapper := generic.NewMap2[components.Position, components.Tilish](world)
2024-02-06 15:26:20 +01:00
2024-02-07 18:01:58 +01:00
colmapper := generic.NewMap3[
components.Position,
components.Renderable,
2024-02-10 19:45:06 +01:00
components.Collectible](world)
2024-02-07 18:01:58 +01:00
obsmapper := generic.NewMap4[
components.Position,
components.Velocity,
components.Renderable,
components.Obstacle](world)
transmapper := generic.NewMap3[
components.Position,
components.Renderable,
components.Transient](world)
destructiblemapper := generic.NewMap3[
components.Position,
components.Renderable,
components.Destroyable](world)
switchmapper := generic.NewMap4[
components.Position,
components.Renderable,
components.Bond,
components.Switch](world)
doormapper := generic.NewMap4[
components.Position,
components.Renderable,
components.Bond,
components.Door](world)
2024-02-06 15:26:20 +01:00
var pos *components.Position
var vel *components.Velocity
2024-02-06 15:26:20 +01:00
var render *components.Renderable
var transient *components.Transient
var player *components.Player
var destroyable *components.Destroyable
var switcher *components.Switch
var door *components.Door
var bond *components.Bond
2024-02-06 15:26:20 +01:00
playerID := ecs.ComponentID[components.Player](world)
obstacleID := ecs.ComponentID[components.Obstacle](world)
observer := observers.GetGameObserver(world)
switches := []ecs.Entity{}
doors := []ecs.Entity{}
2024-02-06 15:26:20 +01:00
for point, tile := range mapslice {
switch tile.Renderable {
case true:
switch {
case tile.Solid:
entity := solidmapper.New()
pos, render, _, _ = solidmapper.Get(entity)
2024-02-06 15:26:20 +01:00
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}
2024-02-07 18:01:58 +01:00
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.Wall = tile.ToggleSprite
case tile.Destroyable:
entity := destructiblemapper.New()
pos, render, destroyable = destructiblemapper.Get(entity)
destroyable.Sprites = tile.Tiles
render.DamageImage = tile.Alpha
render.Shader = tile.Shader
render.Damaged = 0
case tile.Switch:
entity := switchmapper.New()
pos, render, _, switcher = switchmapper.Get(entity)
switcher.CloseSprite = tile.Sprite
switcher.OpenSprite = tile.ToggleSprite
switcher.Ref = tile.Ref
switches = append(switches, entity)
case tile.Door:
entity := doormapper.New()
pos, render, _, door = doormapper.Get(entity)
door.CloseSprite = tile.Sprite
door.OpenSprite = tile.ToggleSprite
door.Id = tile.Id
doors = append(doors, entity)
2024-02-06 15:26:20 +01:00
default:
log.Fatalln("unsupported tile type encountered")
}
render.Image = tile.Sprite
render.Pos = pos
2024-02-06 15:26:20 +01:00
//if tile.AnimateOnDestruct {
if tile.AnimationTrigger != "" {
// FIXME: be more generic, use LDTK enum
render.Animate.Sprites = tile.AnimationSpriteSheet.Sprites
render.Animate.Width = tile.AnimationSpriteSheet.Width
render.Animate.Height = tile.AnimationSpriteSheet.Height
render.Animate.Trigger = tile.AnimationTrigger
}
2024-02-06 15:26:20 +01:00
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)
}
// check for switch->door references
for _, switchentity := range switches {
_, _, bond, switcher = switchmapper.Get(switchentity)
if switcher.Ref != "" {
// this switch has a reference
for _, doorentity := range doors {
_, _, _, door = doormapper.Get(doorentity)
if door.Id == switcher.Ref {
// the switch reference matches the door id, relate the two
bond.Ref = switcher.Ref
relID := ecs.ComponentID[components.Bond](world)
world.Relations().Set(switchentity, relID, doorentity)
}
}
}
}
2024-02-06 15:26:20 +01:00
return &Grid{
Size: len(mapslice),
Tilesize: tilesize,
2024-02-10 19:45:06 +01:00
Width: width,
Height: height,
2024-02-06 15:26:20 +01:00
Map: mapslice,
World: world,
TilesX: width / tilesize,
TilesY: height / tilesize,
2024-02-06 15:26:20 +01:00
}
}
2024-02-10 19:45:06 +01:00
// return the tile the moving object would end up on if indeed moving
func (grid *Grid) GetTile(
2024-02-06 15:26:20 +01:00
position *components.Position,
2024-02-10 19:45:06 +01:00
velocity *components.Velocity) *assets.Tile {
2024-02-10 19:45:06 +01:00
newpoint := image.Point{
int(position.X+velocity.Data.X) / grid.Tilesize,
int(position.Y+velocity.Data.Y) / grid.Tilesize,
2024-02-06 15:26:20 +01:00
}
2024-02-10 19:45:06 +01:00
tile := grid.Map[newpoint]
return tile
2024-02-06 15:26:20 +01:00
}
func (grid *Grid) RemoveTile(point image.Point) {
delete(grid.Map, point)
}
func (grid *Grid) SetFloorTile(point image.Point) {
grid.Map[point] = assets.Tiles["floor"]
}
func (grid *Grid) SetSolidTile(tile *assets.Tile, point image.Point) {
grid.Map[point] = tile
}