added player loop detection, loose level if last player loops

This commit is contained in:
Thomas von Dein 2024-02-27 18:20:00 +01:00
parent 5d2475d525
commit 707281212a
6 changed files with 76 additions and 25 deletions

14
TODO.md
View File

@ -2,9 +2,6 @@
- ignore comments in lvl files
- check when sphere bounces from one end to the other endlessly w/o
any solids in between. this is a game lock and equals game over
- Start the game end timer and add a score FIXME: put the actual max
reachable score into the level definition along with the minimum
steps required to reach it, also add a step counter
@ -15,19 +12,8 @@
- Add some final message when the player reaches the last level, start
from scratch or a message to buy me some beer, whatever
- Check player-player collisions!
- Add player mergers, possibly as an option, so maybe we could have
different primary and secondary players: one pair can merge, the
other not. Like S + s and M + m or something.
- Add shaders for animation (player destruction etc)
- Add player HUD + Stats (as hud_system!)
- Entity Observers don't work, removed entities remain there, errors
about dead entities, had to add Alive checks, and in obstacle_system
even remove the player manually from the world.
## Collider Rework

View File

@ -4,14 +4,14 @@ Background: background-lila
W
S # o
o
#
S
#

View File

@ -5,6 +5,9 @@ import "github.com/hajimehoshi/ebiten/v2"
type Player struct {
IsPrimary bool
Sprites []*ebiten.Image
LoopPos *Position
LoopStart bool
LoopCount int
}
func (player *Player) SwitchSprite() *ebiten.Image {

View File

@ -44,7 +44,7 @@ func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level {
systemlist = append(systemlist, systems.NewObstacleSystem(game.World, gridcontainer))
systemlist = append(systemlist,
systems.NewPlayerSystem(game.World, gridcontainer))
systems.NewPlayerSystem(game.World, gridcontainer, game.ScreenWidth, game.ScreenHeight))
systemlist = append(systemlist, systems.NewParticleSystem(game.World, game.Cellsize))

View File

@ -90,6 +90,7 @@ func NewGrid(world *ecs.World,
vel.Speed = config.PLAYERSPEED
player.IsPrimary = tile.IsPrimary
player.Sprites = tile.Tiles
player.LoopPos = &components.Position{Cellsize: tilesize}
case tile.Collectible:
entity := colmapper.New()
pos, render, _ = colmapper.Get(entity)

View File

@ -18,13 +18,16 @@ type PlayerSystem struct {
World *ecs.World
Selector *generic.Filter4[Position, Velocity, Player, Renderable]
GridContainer *grid.GridContainer
Width, Height int
}
func NewPlayerSystem(world *ecs.World, gridcontainer *grid.GridContainer) System {
func NewPlayerSystem(world *ecs.World, gridcontainer *grid.GridContainer, width, height int) System {
system := &PlayerSystem{
Selector: generic.NewFilter4[Position, Velocity, Player, Renderable](),
GridContainer: gridcontainer,
World: world,
Width: width,
Height: height,
}
return system
@ -48,7 +51,7 @@ func (system PlayerSystem) Update() error {
}
// check if the user alters or initiates movement
system.CheckMovement(velocity)
system.CheckMovement(playerposition, velocity, player)
// check if player collides with walls or edges
system.CheckGridCollision(playerposition, velocity)
@ -56,6 +59,15 @@ func (system PlayerSystem) Update() error {
if count > 1 {
// check if player collides with another player, fuse them if any
EntitiesToRemove = system.CheckPlayerCollision(playerposition, velocity, query.Entity())
} else {
// only 1 player left or one is max
EntitiesToRemove = system.CheckPlayerLooping(
playerposition, velocity, player, query.Entity())
}
if !velocity.Moving() {
// disable loop detection
player.LoopCount = 0
}
}
@ -130,17 +142,33 @@ func (system *PlayerSystem) SwitchPlayers() {
}
}
func (system *PlayerSystem) CheckMovement(velocity *components.Velocity) {
func (system *PlayerSystem) CheckMovement(
position *components.Position,
velocity *components.Velocity,
player *components.Player) {
moved := false
if !velocity.Moving() {
switch {
case ebiten.IsKeyPressed(ebiten.KeyRight):
velocity.Change(East)
moved = true
case ebiten.IsKeyPressed(ebiten.KeyLeft):
velocity.Change(West)
moved = true
case ebiten.IsKeyPressed(ebiten.KeyDown):
velocity.Change(South)
moved = true
case ebiten.IsKeyPressed(ebiten.KeyUp):
velocity.Change(North)
moved = true
}
if moved {
// will be reset every time the user initiates player movement
player.LoopPos.Set(position)
player.LoopCount = 0
}
}
}
@ -161,9 +189,6 @@ func (system *PlayerSystem) CheckGridCollision(
tilepos.Point(), "playerpos", playerposition)
intersects, newpos := tilepos.Intersects(playerposition, velocity)
if intersects {
// slog.Debug("collision detected", "tile",
// tilepos, "player", playerposition, "new", newpos)
playerposition.Set(newpos)
velocity.Change(Stop)
}
@ -202,3 +227,39 @@ func (system *PlayerSystem) CheckPlayerCollision(
// FIXME: add an animation highlighting the fusion
return EntitiesToRemove
}
func (system *PlayerSystem) CheckPlayerLooping(
position *components.Position,
velocity *components.Velocity,
player *components.Player,
entity ecs.Entity) []ecs.Entity {
if player.LoopPos.Rect == nil {
// hasn't moved
return nil
}
EntitiesToRemove := []ecs.Entity{}
ok, _ := player.LoopPos.Intersects(position, velocity)
if ok {
// Fatal: loop detected with last player
player.LoopStart = true
} else {
// no intersection with old pos anymore
if player.LoopStart {
// loop detection active
player.LoopCount++
max := system.Width
if velocity.Direction == North || velocity.Direction == South {
max = system.Height
}
// we haved moved one time around the whole screen, loose
if (player.LoopCount * velocity.Speed) > max {
EntitiesToRemove = append(EntitiesToRemove, entity)
}
}
}
return EntitiesToRemove
}