openquell/systems/grid_system.go

160 lines
3.5 KiB
Go
Raw Normal View History

2024-02-10 19:45:06 +01:00
package systems
import (
"image"
"image/draw"
"openquell/components"
. "openquell/components"
. "openquell/config"
"openquell/grid"
"github.com/hajimehoshi/ebiten/v2"
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
)
type GridSystem struct {
World *ecs.World
Selector *generic.Filter3[Renderable, Position, Solid]
UseCache bool
Cache *ebiten.Image
Count int // register tile count, invalidates cache
2024-02-10 19:45:06 +01:00
Background *ebiten.Image
Width, Height, TilesX, TilesY, Tilesize int
Grid *grid.Grid
}
func NewGridSystem(world *ecs.World, width, height,
tilesize int, background *ebiten.Image) *GridSystem {
cache := ebiten.NewImage(width, height)
system := &GridSystem{
Selector: generic.NewFilter3[Renderable, Position, Solid](),
UseCache: false,
Cache: cache,
Width: width,
Height: height,
TilesX: width / tilesize,
TilesY: height / tilesize,
Tilesize: tilesize,
Background: background,
World: world,
}
return system
}
func (system *GridSystem) SetGrid(grid *grid.Grid) {
system.Grid = grid
}
func (system *GridSystem) Update() {}
func (system *GridSystem) Draw(screen *ebiten.Image) {
op := &ebiten.DrawImageOptions{}
query := system.Selector.Query(system.World)
2024-02-10 19:45:06 +01:00
if !system.UseCache || query.Count() != system.Count {
2024-02-10 19:45:06 +01:00
// map not cached or cacheable, write it to the cache
draw.Draw(system.Cache, system.Background.Bounds(), system.Background, image.ZP, draw.Src)
system.Count = query.Count()
2024-02-10 19:45:06 +01:00
for query.Next() {
sprite, pos, _ := query.Get()
draw.Draw(
system.Cache,
image.Rect(pos.X, pos.Y, pos.X+pos.Cellsize, pos.Y+pos.Cellsize),
sprite.Image, image.ZP, draw.Over)
}
op.GeoM.Reset()
screen.DrawImage(system.Cache, op)
system.UseCache = true
} else {
// use the cached map
op.GeoM.Reset()
screen.DrawImage(system.Cache, op)
query.Close()
2024-02-10 19:45:06 +01:00
}
}
func (system *GridSystem) GetSolidNeighborPosition(
position *components.Position,
velocity *components.Velocity,
solid bool) (bool, *components.Position) {
if !solid {
return false, nil
}
// set to true, if we are already on the last tile in the current
2024-02-10 19:45:06 +01:00
// direction, i.e. on the edge of the grid
edge := true
neighborpos := position.Point()
switch velocity.Direction {
case East:
if neighborpos.X < system.TilesX {
neighborpos.X++
edge = false
}
case West:
if neighborpos.X > 0 {
neighborpos.X--
edge = false
}
case South:
if neighborpos.Y < system.TilesY {
neighborpos.Y++
edge = false
}
case North:
if neighborpos.Y > 0 {
neighborpos.Y--
edge = false
}
}
newpos := components.NewPosition(neighborpos, system.Tilesize)
if !edge && system.Grid.Map[neighborpos].Solid {
return true, newpos
}
return false, nil
}
func (system *GridSystem) BumpEdge(
pos *components.Position,
velocity *components.Velocity) (bool, *components.Position) {
x := pos.X + velocity.Data.X
y := pos.Y + velocity.Data.Y
if x < 0 || x > system.Width-system.Tilesize || y < 0 || y > system.Height-system.Tilesize {
newpos := &components.Position{}
X := pos.X
Y := pos.Y
switch velocity.Direction {
case East:
X = 0
case West:
X = system.Width - system.Tilesize
case South:
Y = 0
case North:
Y = system.Height - system.Tilesize
}
newpos.Update(X, Y, system.Tilesize)
return true, newpos
}
return false, nil
}