diff --git a/assets/levels/gurke.lvl b/assets/levels/gurke.lvl index 943eb97..7b38eee 100644 --- a/assets/levels/gurke.lvl +++ b/assets/levels/gurke.lvl @@ -9,9 +9,9 @@ Background: background-orange # ######## ### # ############ # - # # # + # o # # # # ######## # - # # + # # o # ############ diff --git a/assets/loader-levels.go b/assets/loader-levels.go index 89d5a9c..048fad3 100644 --- a/assets/loader-levels.go +++ b/assets/loader-levels.go @@ -20,13 +20,14 @@ var Tiles = InitTiles() // Tile: contains image, identifier (as used in level data) and // additional properties type Tile struct { - Id byte - Sprite *ebiten.Image - Class string - Solid bool - Player bool - Renderable bool - Velocity bool + Id byte + Sprite *ebiten.Image + Class string + Solid bool + Player bool + Renderable bool + Velocity bool + Collectible bool } func NewTilePlayer() *Tile { @@ -50,6 +51,17 @@ func NewTileBlock(class string) *Tile { } } +func NewTileCollectible(class string) *Tile { + return &Tile{ + Id: 'o', + Sprite: Assets[class], + Class: class, + Solid: false, + Renderable: true, + Collectible: true, + } +} + // used to map level data bytes to actual tiles type TileRegistry map[byte]*Tile @@ -77,6 +89,7 @@ func InitTiles() TileRegistry { ' ': {Id: ' ', Class: "floor", Renderable: false}, '#': NewTileBlock("block-grey32"), 'S': NewTilePlayer(), + 'o': NewTileCollectible("collectible-orange"), } } diff --git a/components/components.go b/components/components.go index 9ca2deb..f178a6c 100644 --- a/components/components.go +++ b/components/components.go @@ -15,3 +15,4 @@ type Tilish struct{} type Solid struct{} type Floor struct{} type Player struct{} +type Collectible struct{} diff --git a/game/grid.go b/game/grid.go index 38ab634..c43c5b4 100644 --- a/game/grid.go +++ b/game/grid.go @@ -32,13 +32,20 @@ func NewGrid(game *Game, tilesize int, mapslice map[image.Point]*assets.Tile) *G components.Velocity, components.Renderable, components.Player](game.World) + solidmapper := generic.NewMap4[ components.Position, components.Renderable, components.Tilish, components.Solid](game.World) + emptymapper := generic.NewMap2[components.Position, components.Tilish](game.World) + colmapper := generic.NewMap3[ + components.Position, + components.Renderable, + components.Collectible](game.World) + var pos *components.Position var render *components.Renderable @@ -53,6 +60,9 @@ func NewGrid(game *Game, tilesize int, mapslice map[image.Point]*assets.Tile) *G entity := playermapper.New() pos, _, render, _ = playermapper.Get(entity) fmt.Printf("player start pos: %d,%d\n", point.X*tilesize, point.Y*tilesize) + case tile.Collectible: + entity := colmapper.New() + pos, render, _ = colmapper.Get(entity) default: log.Fatalln("unsupported tile type encountered") } diff --git a/game/levels.go b/game/levels.go index 2f682f7..d6acc57 100644 --- a/game/levels.go +++ b/game/levels.go @@ -3,6 +3,7 @@ package game import ( "fmt" "image" + "image/draw" "log" "openquell/assets" "openquell/components" @@ -22,9 +23,30 @@ type Level struct { Description string Background *ebiten.Image Mapslice map[image.Point]*assets.Tile + UseCache bool + Cache *ebiten.Image + Selector map[string]ecs.Mask + Component map[string]ecs.ID } func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level { + cache := ebiten.NewImage(game.ScreenWidth, game.ScreenHeight) + + positionid := ecs.ComponentID[components.Position](game.World) + velocityid := ecs.ComponentID[components.Velocity](game.World) + playerid := ecs.ComponentID[components.Player](game.World) + colid := ecs.ComponentID[components.Collectible](game.World) + + selectors := map[string]ecs.Mask{} + selectors["player"] = filter.All(positionid, velocityid, playerid) + selectors["collectible"] = filter.All(positionid, colid) + + components := map[string]ecs.ID{} + components["position"] = positionid + components["velocity"] = velocityid + components["player"] = playerid + components["collectible"] = colid + return &Level{ Mapslice: LevelToSlice(game, plan, cellsize), Cellsize: cellsize, @@ -33,53 +55,35 @@ func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level { Height: game.ScreenHeight, Description: plan.Description, Background: plan.Background, + UseCache: false, + Cache: cache, + Selector: selectors, + Component: components, } } func (level *Level) Update() { - positionid := ecs.ComponentID[components.Position](level.World) - velocityid := ecs.ComponentID[components.Velocity](level.World) - playerid := ecs.ComponentID[components.Player](level.World) - - selector := filter.All(positionid, velocityid, playerid) - query := level.World.Query(selector) + query := level.World.Query(level.Selector["player"]) + colquery := level.World.Query(level.Selector["collectible"]) for query.Next() { - playerposition := (*components.Position)(query.Get(positionid)) - velocity := (*components.Velocity)(query.Get(velocityid)) + playerposition := (*components.Position)(query.Get(level.Component["position"])) + velocity := (*components.Velocity)(query.Get(level.Component["velocity"])) - switch { - case ebiten.IsKeyPressed(ebiten.KeyRight): - velocity.Change(East) - case ebiten.IsKeyPressed(ebiten.KeyLeft): - velocity.Change(West) - case ebiten.IsKeyPressed(ebiten.KeyDown): - velocity.Change(South) - case ebiten.IsKeyPressed(ebiten.KeyUp): - velocity.Change(North) - // other keys: : switch player, etc + if !velocity.Moving() { + switch { + case ebiten.IsKeyPressed(ebiten.KeyRight): + velocity.Change(East) + case ebiten.IsKeyPressed(ebiten.KeyLeft): + velocity.Change(West) + case ebiten.IsKeyPressed(ebiten.KeyDown): + velocity.Change(South) + case ebiten.IsKeyPressed(ebiten.KeyUp): + velocity.Change(North) + // other keys: : switch player, etc + } } - /* - // Use this to check against obstacles, collectibles etc - for tilequery.Next() { - tilepos := (*components.Position)(tilequery.Get(positionid)) - - if velocity.Moving() { - intersects, newpos := tilepos.Intersects(playerposition, velocity) - if intersects { - fmt.Printf("collision detected. tile: %s\n", tilepos) - fmt.Printf(" player: %s\n", playerposition) - fmt.Printf(" new: %s\n", newpos) - - playerposition.Set(newpos) - fmt.Printf(" player new: %s\n", playerposition) - velocity.Change(Stop) - } - } - } - */ - if velocity.Moving() { ok, newpos := level.Grid.BumpEdge(playerposition, velocity) if ok { @@ -100,6 +104,15 @@ func (level *Level) Update() { } } } + + for colquery.Next() { + collectible := (*components.Collectible)(colquery.Get(level.Component["collectible"])) + colposition := (*components.Position)(colquery.Get(level.Component["position"])) + ok, _ := playerposition.Intersects(colposition, velocity) + if ok { + fmt.Printf("bumped into collectible %v\n", collectible) + } + } } playerposition.Move(velocity) @@ -125,16 +138,41 @@ func (level *Level) GetTile(position *components.Position, velocity *components. } func (level *Level) Draw(screen *ebiten.Image) { - // FIXME: move out - op := &ebiten.DrawImageOptions{} - screen.DrawImage(level.Background, op) - rid := ecs.ComponentID[components.Renderable](level.World) pid := ecs.ComponentID[components.Position](level.World) + sid := ecs.ComponentID[components.Solid](level.World) - selector := filter.All(rid, pid) + if !level.UseCache { + // map not cached or cacheable, write it to the cache + draw.Draw(level.Cache, level.Background.Bounds(), level.Background, image.ZP, draw.Src) - query := level.World.Query(selector) + selector := filter.All(rid, pid, sid) + + query := level.World.Query(selector) + for query.Next() { + pos := (*components.Position)(query.Get(pid)) + sprite := (*components.Renderable)(query.Get(rid)) + + draw.Draw( + level.Cache, + image.Rect(pos.X, pos.Y, pos.X+pos.Cellsize, pos.Y+pos.Cellsize), + sprite.Image, image.ZP, draw.Over) + } + + op := &ebiten.DrawImageOptions{} + screen.DrawImage(level.Cache, op) + + level.UseCache = true + } else { + // use the cached map + op := &ebiten.DrawImageOptions{} + screen.DrawImage(level.Cache, op) + } + + // write the movable tiles + selector := filter.All(rid, pid).Without(sid) + + query := level.World.Query(&selector) for query.Next() { pos := (*components.Position)(query.Get(pid)) sprite := (*components.Renderable)(query.Get(rid)) diff --git a/game/levelscene.go b/game/levelscene.go index 0aa9e73..4c4e4ca 100644 --- a/game/levelscene.go +++ b/game/levelscene.go @@ -11,6 +11,7 @@ type LevelScene struct { Levels []*Level Next int Whoami int + UseCache bool } // Implements the actual playing Scene diff --git a/openquell b/openquell index 7d11b44..68aa7bf 100755 Binary files a/openquell and b/openquell differ