added transient entities: when crossed, turns into wall tile
This commit is contained in:
@@ -109,7 +109,7 @@ func (system *CollectibleSystem) AddParticle(position *components.Position) {
|
||||
particleobserver.AddEntity(entity)
|
||||
|
||||
particle.Index = assets.Tiles['*'].Particle
|
||||
particle.Particles = assets.Tiles['*'].Particles
|
||||
particle.Tiles = assets.Tiles['*'].Tiles
|
||||
|
||||
pos.Update(
|
||||
position.X-(16), // FIXME: use global tilesize!
|
||||
|
||||
@@ -18,6 +18,7 @@ type GridSystem struct {
|
||||
Selector *generic.Filter3[Renderable, Position, Solid]
|
||||
UseCache bool
|
||||
Cache *ebiten.Image
|
||||
Count int // register tile count, invalidates cache
|
||||
Background *ebiten.Image
|
||||
Width, Height, TilesX, TilesY, Tilesize int
|
||||
Grid *grid.Grid
|
||||
@@ -52,12 +53,13 @@ func (system *GridSystem) Update() {}
|
||||
|
||||
func (system *GridSystem) Draw(screen *ebiten.Image) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
if !system.UseCache {
|
||||
if !system.UseCache || query.Count() != system.Count {
|
||||
// map not cached or cacheable, write it to the cache
|
||||
draw.Draw(system.Cache, system.Background.Bounds(), system.Background, image.ZP, draw.Src)
|
||||
|
||||
query := system.Selector.Query(system.World)
|
||||
system.Count = query.Count()
|
||||
|
||||
for query.Next() {
|
||||
sprite, pos, _ := query.Get()
|
||||
@@ -76,6 +78,7 @@ func (system *GridSystem) Draw(screen *ebiten.Image) {
|
||||
// use the cached map
|
||||
op.GeoM.Reset()
|
||||
screen.DrawImage(system.Cache, op)
|
||||
query.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +91,7 @@ func (system *GridSystem) GetSolidNeighborPosition(
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// set to true, ifwe are already on the last tile in the current
|
||||
// set to true, if we are already on the last tile in the current
|
||||
// direction, i.e. on the edge of the grid
|
||||
edge := true
|
||||
neighborpos := position.Point()
|
||||
|
||||
@@ -15,15 +15,16 @@ import (
|
||||
)
|
||||
|
||||
type ObstacleSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter4[Position, Velocity, Obstacle, Renderable]
|
||||
PreviousFreePos *components.Position
|
||||
World *ecs.World
|
||||
Selector *generic.Filter5[Position, Velocity, Obstacle, Renderable, Speed]
|
||||
Grid *GridSystem
|
||||
}
|
||||
|
||||
func NewObstacleSystem(world *ecs.World) *ObstacleSystem {
|
||||
func NewObstacleSystem(world *ecs.World, grid *GridSystem) *ObstacleSystem {
|
||||
system := &ObstacleSystem{
|
||||
Selector: generic.NewFilter4[Position, Velocity, Obstacle, Renderable](),
|
||||
Selector: generic.NewFilter5[Position, Velocity, Obstacle, Renderable, Speed](),
|
||||
World: world,
|
||||
Grid: grid,
|
||||
}
|
||||
|
||||
return system
|
||||
@@ -32,6 +33,7 @@ func NewObstacleSystem(world *ecs.World) *ObstacleSystem {
|
||||
func (system *ObstacleSystem) Update() {
|
||||
playerobserver := observers.GetPlayerObserver(system.World)
|
||||
gameobserver := observers.GetGameObserver(system.World)
|
||||
obstacleobserver := observers.GetObstacleObserver(system.World)
|
||||
|
||||
if gameobserver.Lost {
|
||||
return
|
||||
@@ -46,27 +48,67 @@ func (system *ObstacleSystem) Update() {
|
||||
gameover := false
|
||||
|
||||
for query.Next() {
|
||||
obsposition, obsvelocity, obstacle, _ := query.Get()
|
||||
obsposition, obsvelocity, _, _, speed := query.Get()
|
||||
|
||||
// check if one player has bumped into current obstacle
|
||||
for player := range playerobserver.Entities {
|
||||
playerposition := (*Position)(system.World.Get(player, posID))
|
||||
playervelocity := (*Velocity)(system.World.Get(player, veloID))
|
||||
|
||||
ok, newpos := obsposition.Intersects(playerposition, playervelocity)
|
||||
if ok {
|
||||
slog.Debug("bumped into obstacle", "obstacle", obstacle)
|
||||
// slog.Debug("bumped into obstacle", "obstacle", obstacle)
|
||||
|
||||
if CheckObstacleSide(playervelocity, obsvelocity.Direction) {
|
||||
// player died
|
||||
EntitiesToRemove = append(EntitiesToRemove, player)
|
||||
gameover = true
|
||||
} else {
|
||||
playervelocity.Change(Stop)
|
||||
// bumped into nonlethal obstacle side, stop the
|
||||
// player, set the obstacle in motion, if possible
|
||||
slog.Debug("bump not die", "originalpos", playerposition)
|
||||
obsvelocity.Set(playervelocity)
|
||||
playervelocity.Change(Stop)
|
||||
playerposition.Set(newpos)
|
||||
slog.Debug("bump not die", "newpos", newpos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if current obstacle bumped into another obstacle
|
||||
for foreign_obstacle := range obstacleobserver.Entities {
|
||||
if foreign_obstacle == query.Entity() {
|
||||
// don't check obstacle against itself
|
||||
continue
|
||||
}
|
||||
|
||||
foreign_obstacle_position := (*Position)(system.World.Get(foreign_obstacle, posID))
|
||||
|
||||
ok, newpos := foreign_obstacle_position.Intersects(obsposition, obsvelocity)
|
||||
if ok {
|
||||
//slog.Debug("bumped into foreign obstacle", "obstacle", foreign_obstacle)
|
||||
obsposition.Set(newpos)
|
||||
obsvelocity.ResetDirectionAndStop()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is the same loop as in player_system, unite the
|
||||
// two, just iterate over all entities with pos,vel,render, dammit
|
||||
if obsvelocity.Moving() {
|
||||
ok, tilepos := system.Grid.GetSolidNeighborPosition(obsposition, obsvelocity, true)
|
||||
if ok {
|
||||
intersects, newpos := tilepos.Intersects(obsposition, obsvelocity)
|
||||
if intersects {
|
||||
// slog.Debug("collision with foreign obstacle detected", "tile",
|
||||
// tilepos, "obs", obsposition, "new", newpos)
|
||||
|
||||
obsposition.Set(newpos)
|
||||
obsvelocity.ResetDirectionAndStop()
|
||||
}
|
||||
}
|
||||
|
||||
obsposition.Move(obsvelocity, speed)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, entity := range EntitiesToRemove {
|
||||
@@ -91,7 +133,7 @@ func (system *ObstacleSystem) Draw(screen *ebiten.Image) {
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
pos, _, _, sprite := query.Get()
|
||||
pos, _, _, sprite, _ := query.Get()
|
||||
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
@@ -114,7 +156,7 @@ func (system *ObstacleSystem) AddParticle(position *components.Position) {
|
||||
particleobserver.AddEntity(entity)
|
||||
|
||||
particle.Index = assets.Tiles['*'].Particle
|
||||
particle.Particles = assets.Tiles['*'].Particles
|
||||
particle.Tiles = assets.Tiles['*'].Tiles
|
||||
|
||||
pos.Update(
|
||||
position.X-(16), // FIXME: use global tilesize!
|
||||
|
||||
@@ -40,7 +40,7 @@ func (system *ParticleSystem) Update() {
|
||||
if timer.IsReady() {
|
||||
switch {
|
||||
// particle shows from earlier tick, animate
|
||||
case particle.Index > -1 && particle.Index < len(particle.Particles)-1:
|
||||
case particle.Index > -1 && particle.Index < len(particle.Tiles)-1:
|
||||
particle.Index++
|
||||
timer.Start(config.PARTICLE_LOOPWAIT)
|
||||
default:
|
||||
@@ -69,7 +69,7 @@ func (system *ParticleSystem) Draw(screen *ebiten.Image) {
|
||||
if particle.Show {
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
screen.DrawImage(particle.Particles[particle.Index], op)
|
||||
screen.DrawImage(particle.Tiles[particle.Index], op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
. "openquell/components"
|
||||
. "openquell/config"
|
||||
|
||||
@@ -16,6 +15,7 @@ type PlayerSystem struct {
|
||||
Particle *ParticleSystem
|
||||
Collectible *CollectibleSystem
|
||||
Obstacle *ObstacleSystem
|
||||
Transient *TransientSystem
|
||||
Grid *GridSystem
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ func NewPlayerSystem(world *ecs.World, grid *GridSystem) *PlayerSystem {
|
||||
Selector: generic.NewFilter5[Position, Velocity, Player, Renderable, Speed](),
|
||||
Particle: NewParticleSystem(world, grid.Tilesize),
|
||||
Collectible: NewCollectibleSystem(world),
|
||||
Obstacle: NewObstacleSystem(world),
|
||||
Obstacle: NewObstacleSystem(world, grid),
|
||||
Transient: NewTransientSystem(world, grid),
|
||||
Grid: grid,
|
||||
World: world,
|
||||
}
|
||||
@@ -55,15 +56,15 @@ func (system PlayerSystem) Update() error {
|
||||
if velocity.Moving() {
|
||||
ok, newpos := system.Grid.BumpEdge(playerposition, velocity)
|
||||
if ok {
|
||||
slog.Debug("falling off the edge", "newpos", newpos)
|
||||
//slog.Debug("falling off the edge", "newpos", newpos)
|
||||
playerposition.Set(newpos)
|
||||
} else {
|
||||
ok, tilepos := system.Grid.GetSolidNeighborPosition(playerposition, velocity, true)
|
||||
if ok {
|
||||
intersects, newpos := tilepos.Intersects(playerposition, velocity)
|
||||
if intersects {
|
||||
slog.Debug("collision detected", "tile",
|
||||
tilepos, "player", playerposition, "new", newpos)
|
||||
// slog.Debug("collision detected", "tile",
|
||||
// tilepos, "player", playerposition, "new", newpos)
|
||||
|
||||
playerposition.Set(newpos)
|
||||
velocity.Change(Stop)
|
||||
@@ -76,6 +77,7 @@ func (system PlayerSystem) Update() error {
|
||||
system.Particle.Update() // may set player position
|
||||
system.Obstacle.Update()
|
||||
system.Collectible.Update()
|
||||
system.Transient.Update()
|
||||
|
||||
query = system.Selector.Query(system.World)
|
||||
for query.Next() {
|
||||
@@ -104,4 +106,5 @@ func (system *PlayerSystem) Draw(screen *ebiten.Image) {
|
||||
system.Collectible.Draw(screen)
|
||||
system.Particle.Draw(screen)
|
||||
system.Obstacle.Draw(screen)
|
||||
system.Transient.Draw(screen)
|
||||
}
|
||||
|
||||
116
systems/transient_system.go
Normal file
116
systems/transient_system.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"openquell/assets"
|
||||
"openquell/components"
|
||||
. "openquell/components"
|
||||
"openquell/observers"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/mlange-42/arche/ecs"
|
||||
"github.com/mlange-42/arche/generic"
|
||||
)
|
||||
|
||||
type TransientSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter3[Position, Renderable, Transient]
|
||||
Grid *GridSystem
|
||||
SolidMapper generic.Map4[ // needed for replacement
|
||||
components.Position,
|
||||
components.Renderable,
|
||||
components.Tilish,
|
||||
components.Solid]
|
||||
}
|
||||
|
||||
type TransientToWall struct {
|
||||
Entity ecs.Entity
|
||||
NewSprite string
|
||||
Position components.Position
|
||||
}
|
||||
|
||||
func NewTransientSystem(world *ecs.World, grid *GridSystem) *TransientSystem {
|
||||
solidmapper := generic.NewMap4[
|
||||
components.Position,
|
||||
components.Renderable,
|
||||
components.Tilish,
|
||||
components.Solid](world)
|
||||
|
||||
system := &TransientSystem{
|
||||
Selector: generic.NewFilter3[Position, Renderable, Transient](),
|
||||
World: world,
|
||||
Grid: grid,
|
||||
SolidMapper: solidmapper,
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
func (system *TransientSystem) Update() {
|
||||
playerobserver := observers.GetPlayerObserver(system.World)
|
||||
posID := ecs.ComponentID[components.Position](system.World)
|
||||
veloID := ecs.ComponentID[components.Velocity](system.World)
|
||||
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
EntitiestoMakeSolid := []TransientToWall{}
|
||||
|
||||
for query.Next() {
|
||||
transientposition, _, transient := query.Get()
|
||||
|
||||
for player := range playerobserver.Entities {
|
||||
playerposition := (*Position)(system.World.Get(player, posID))
|
||||
playervelocity := (*Velocity)(system.World.Get(player, veloID))
|
||||
|
||||
ok, _ := transientposition.Intersects(playerposition, playervelocity)
|
||||
if ok {
|
||||
// display the transient sprite as long as the player crosses it
|
||||
transient.Activated = true
|
||||
} else {
|
||||
// the player crossed the transient wall completely
|
||||
if transient.Activated {
|
||||
EntitiestoMakeSolid = append(EntitiestoMakeSolid, TransientToWall{
|
||||
Entity: query.Entity(),
|
||||
Position: *transientposition,
|
||||
NewSprite: transient.GetNext(),
|
||||
})
|
||||
slog.Debug("done transient", "transient", transientposition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, convertible := range EntitiestoMakeSolid {
|
||||
// remove transient entity
|
||||
system.World.RemoveEntity(convertible.Entity)
|
||||
|
||||
// replace with solid entity
|
||||
entity := system.SolidMapper.New()
|
||||
pos, render, _, _ := system.SolidMapper.Get(entity)
|
||||
|
||||
// set it up apropriately
|
||||
pos.Set(&convertible.Position)
|
||||
render.Image = assets.Assets[convertible.NewSprite]
|
||||
|
||||
// also setup the grid tile with a new solid, so that
|
||||
// collision detection works
|
||||
system.Grid.Grid.SetTile(
|
||||
assets.NewTileBlock(convertible.NewSprite),
|
||||
convertible.Position.Point(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (system *TransientSystem) Draw(screen *ebiten.Image) {
|
||||
// write transients (these are no tiles!)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
pos, render, _ := query.Get()
|
||||
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
screen.DrawImage(render.Image, op)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user