added collectibles support

This commit is contained in:
Thomas von Dein 2024-02-07 18:01:58 +01:00
parent ce7fcd038a
commit a0b40139c7
7 changed files with 116 additions and 53 deletions

View File

@ -9,9 +9,9 @@ Background: background-orange
# ######## ### # ######## ###
# #
############ # ############ #
# # # # o # #
# # ######## # # # ######## #
# # # # o
# ############ # ############

View File

@ -20,13 +20,14 @@ var Tiles = InitTiles()
// Tile: contains image, identifier (as used in level data) and // Tile: contains image, identifier (as used in level data) and
// additional properties // additional properties
type Tile struct { type Tile struct {
Id byte Id byte
Sprite *ebiten.Image Sprite *ebiten.Image
Class string Class string
Solid bool Solid bool
Player bool Player bool
Renderable bool Renderable bool
Velocity bool Velocity bool
Collectible bool
} }
func NewTilePlayer() *Tile { 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 // used to map level data bytes to actual tiles
type TileRegistry map[byte]*Tile type TileRegistry map[byte]*Tile
@ -77,6 +89,7 @@ func InitTiles() TileRegistry {
' ': {Id: ' ', Class: "floor", Renderable: false}, ' ': {Id: ' ', Class: "floor", Renderable: false},
'#': NewTileBlock("block-grey32"), '#': NewTileBlock("block-grey32"),
'S': NewTilePlayer(), 'S': NewTilePlayer(),
'o': NewTileCollectible("collectible-orange"),
} }
} }

View File

@ -15,3 +15,4 @@ type Tilish struct{}
type Solid struct{} type Solid struct{}
type Floor struct{} type Floor struct{}
type Player struct{} type Player struct{}
type Collectible struct{}

View File

@ -32,13 +32,20 @@ func NewGrid(game *Game, tilesize int, mapslice map[image.Point]*assets.Tile) *G
components.Velocity, components.Velocity,
components.Renderable, components.Renderable,
components.Player](game.World) components.Player](game.World)
solidmapper := generic.NewMap4[ solidmapper := generic.NewMap4[
components.Position, components.Position,
components.Renderable, components.Renderable,
components.Tilish, components.Tilish,
components.Solid](game.World) components.Solid](game.World)
emptymapper := generic.NewMap2[components.Position, components.Tilish](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 pos *components.Position
var render *components.Renderable var render *components.Renderable
@ -53,6 +60,9 @@ func NewGrid(game *Game, tilesize int, mapslice map[image.Point]*assets.Tile) *G
entity := playermapper.New() entity := playermapper.New()
pos, _, render, _ = playermapper.Get(entity) pos, _, render, _ = playermapper.Get(entity)
fmt.Printf("player start pos: %d,%d\n", point.X*tilesize, point.Y*tilesize) 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: default:
log.Fatalln("unsupported tile type encountered") log.Fatalln("unsupported tile type encountered")
} }

View File

@ -3,6 +3,7 @@ package game
import ( import (
"fmt" "fmt"
"image" "image"
"image/draw"
"log" "log"
"openquell/assets" "openquell/assets"
"openquell/components" "openquell/components"
@ -22,9 +23,30 @@ type Level struct {
Description string Description string
Background *ebiten.Image Background *ebiten.Image
Mapslice map[image.Point]*assets.Tile 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 { 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{ return &Level{
Mapslice: LevelToSlice(game, plan, cellsize), Mapslice: LevelToSlice(game, plan, cellsize),
Cellsize: cellsize, Cellsize: cellsize,
@ -33,53 +55,35 @@ func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level {
Height: game.ScreenHeight, Height: game.ScreenHeight,
Description: plan.Description, Description: plan.Description,
Background: plan.Background, Background: plan.Background,
UseCache: false,
Cache: cache,
Selector: selectors,
Component: components,
} }
} }
func (level *Level) Update() { func (level *Level) Update() {
positionid := ecs.ComponentID[components.Position](level.World) query := level.World.Query(level.Selector["player"])
velocityid := ecs.ComponentID[components.Velocity](level.World) colquery := level.World.Query(level.Selector["collectible"])
playerid := ecs.ComponentID[components.Player](level.World)
selector := filter.All(positionid, velocityid, playerid)
query := level.World.Query(selector)
for query.Next() { for query.Next() {
playerposition := (*components.Position)(query.Get(positionid)) playerposition := (*components.Position)(query.Get(level.Component["position"]))
velocity := (*components.Velocity)(query.Get(velocityid)) velocity := (*components.Velocity)(query.Get(level.Component["velocity"]))
switch { if !velocity.Moving() {
case ebiten.IsKeyPressed(ebiten.KeyRight): switch {
velocity.Change(East) case ebiten.IsKeyPressed(ebiten.KeyRight):
case ebiten.IsKeyPressed(ebiten.KeyLeft): velocity.Change(East)
velocity.Change(West) case ebiten.IsKeyPressed(ebiten.KeyLeft):
case ebiten.IsKeyPressed(ebiten.KeyDown): velocity.Change(West)
velocity.Change(South) case ebiten.IsKeyPressed(ebiten.KeyDown):
case ebiten.IsKeyPressed(ebiten.KeyUp): velocity.Change(South)
velocity.Change(North) case ebiten.IsKeyPressed(ebiten.KeyUp):
// other keys: <tab>: switch player, etc velocity.Change(North)
// other keys: <tab>: 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() { if velocity.Moving() {
ok, newpos := level.Grid.BumpEdge(playerposition, velocity) ok, newpos := level.Grid.BumpEdge(playerposition, velocity)
if ok { 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) playerposition.Move(velocity)
@ -125,16 +138,41 @@ func (level *Level) GetTile(position *components.Position, velocity *components.
} }
func (level *Level) Draw(screen *ebiten.Image) { 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) rid := ecs.ComponentID[components.Renderable](level.World)
pid := ecs.ComponentID[components.Position](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() { for query.Next() {
pos := (*components.Position)(query.Get(pid)) pos := (*components.Position)(query.Get(pid))
sprite := (*components.Renderable)(query.Get(rid)) sprite := (*components.Renderable)(query.Get(rid))

View File

@ -11,6 +11,7 @@ type LevelScene struct {
Levels []*Level Levels []*Level
Next int Next int
Whoami int Whoami int
UseCache bool
} }
// Implements the actual playing Scene // Implements the actual playing Scene

BIN
openquell

Binary file not shown.