From 707281212aa547daaebc36952556a6864eef8464 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Tue, 27 Feb 2024 18:20:00 +0100 Subject: [PATCH] added player loop detection, loose level if last player loops --- TODO.md | 14 -------- assets/levels/05-space.lvl | 8 ++--- components/player.go | 3 ++ game/levels.go | 2 +- grid/grid.go | 1 + systems/player_system.go | 73 ++++++++++++++++++++++++++++++++++---- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/TODO.md b/TODO.md index 331416c..7aafb9f 100644 --- a/TODO.md +++ b/TODO.md @@ -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 diff --git a/assets/levels/05-space.lvl b/assets/levels/05-space.lvl index f5c1c36..42a4c26 100644 --- a/assets/levels/05-space.lvl +++ b/assets/levels/05-space.lvl @@ -4,14 +4,14 @@ Background: background-lila - # + W - S + S # o - # - + o + # diff --git a/components/player.go b/components/player.go index fb92b45..6e88cdf 100644 --- a/components/player.go +++ b/components/player.go @@ -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 { diff --git a/game/levels.go b/game/levels.go index b02b538..12fd3e4 100644 --- a/game/levels.go +++ b/game/levels.go @@ -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)) diff --git a/grid/grid.go b/grid/grid.go index 068c402..3dec702 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -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) diff --git a/systems/player_system.go b/systems/player_system.go index a5f03d3..d43c12e 100644 --- a/systems/player_system.go +++ b/systems/player_system.go @@ -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 +}