From 897bfe0f72ddc80a020a421cbe987b54716e571e Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Sat, 11 Mar 2023 23:17:21 +0000 Subject: Exit transition --- src/Game.hs | 2 +- src/Game/Entities.hs | 18 +++++++++++++----- src/Game/Entities/Common.hs | 2 +- src/Game/Entities/Exit.hs | 19 ++++++++++++++----- src/Game/Entities/Types.hs | 4 ++-- src/Game/State.hs | 10 ++++++---- 6 files changed, 37 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/Game.hs b/src/Game.hs index 8c416e3..be34a08 100644 --- a/src/Game.hs +++ b/src/Game.hs @@ -166,7 +166,7 @@ gameLoop e = do | state.batteries == state.totalBatteries && not state.exit = do es <- E.addExit env.entities x (y - 8) -- adjusted to player's height pure env {entities = es, state = state {GS.exit = True}} - | state.levelCompleted = do + | state.levelCompleted == GS.ExitDone = do map' <- M.load (maps !! (env.state.currentLevel + 1)) env.tsTexture entities <- E.mkEntities env.sprites map' env.controls pure $ diff --git a/src/Game/Entities.hs b/src/Game/Entities.hs index b53e4e3..8a939f4 100644 --- a/src/Game/Entities.hs +++ b/src/Game/Entities.hs @@ -60,8 +60,8 @@ addExit es x y = do updateAll :: Entities -> GS.State -> IO (Entities, GS.State) updateAll es state = do - -- update the player first (including the reference) - updatedPlayer <- player.update player + -- update the player first (including the reference), unless the level is completed + updatedPlayer <- if state.levelCompleted /= GS.ExitOff then pure player else player.update player void $ writeIORef es.player updatedPlayer -- then the other entities updated <- (updatedPlayer :) <$> traverse (updateFilter $ state.hitDelay > 0) others @@ -91,14 +91,21 @@ updateAll es state = do if s.lives == 1 then ( s {GS.lives = 0, GS.gameOverDelay = gameOverDelay}, - (head ents) {dir = Dying, gravity = gravityUp, frame = 0} : tail ents + -- the player is not in the action, changing then type disables collision detection + (head ents) {typ = TypeEffect, dir = Dying, gravity = gravityUp, frame = 0} : tail ents ) else ( s {GS.lives = s.lives - 1, GS.hitDelay = hitDelay}, ents ) processActions s' ents' t - ActionExitLevel -> processActions s {GS.levelCompleted = True} ents t + ActionExitStarted -> + processActions + s {GS.levelCompleted = GS.ExitStarted} + -- the player is not in the action, changing the type disables collision detection + ((head ents) {typ = TypeEffect} : tail ents) + t + ActionExitDone -> processActions s {GS.levelCompleted = GS.ExitDone} ents t processActions s ents [] = pure (s, ents) -- Update entities skipping enemies if the player was hit @@ -134,7 +141,8 @@ render renderer es state = do else traverse_ (renderWiggling ((.&.) 2 state.hitDelay)) others -- always render player last -- won't draw all the frames if the player was hit - if testBit state.hitDelay 2 then pure () else renderOne player + -- or we are exiting the level + if testBit state.hitDelay 2 || state.levelCompleted /= GS.ExitOff then pure () else renderOne player where player = head es.entities others = tail es.entities diff --git a/src/Game/Entities/Common.hs b/src/Game/Entities/Common.hs index 2e447f5..69786e5 100644 --- a/src/Game/Entities/Common.hs +++ b/src/Game/Entities/Common.hs @@ -30,7 +30,7 @@ collision :: IORef Entity -> Int -> Collision collision playerRef otherHeight other = do player <- readIORef playerRef pure $ - player.dir /= Dying + player.typ == TypePlayer && player.x + 4 < other.x + 12 && other.x + 4 < player.x + 12 && player.y + otherHeight - 4 < other.y + otherHeight diff --git a/src/Game/Entities/Exit.hs b/src/Game/Entities/Exit.hs index 12f160d..b87c115 100644 --- a/src/Game/Entities/Exit.hs +++ b/src/Game/Entities/Exit.hs @@ -25,15 +25,24 @@ mkExit sprites x y playerCollision = do } updateExit :: Collision -> Entity -> IO Entity -updateExit touchedPlayer e = do - touched <- if e.frame < fullyOpen then pure False else touchedPlayer e - pure $ if touched then e {destroy = True, actions = [ActionExitLevel]} else update +updateExit touchedPlayer e + -- right is the set 0, left set 1 + | e.dir == DirRight = do + touched <- if e.frame < fullyOpen then pure False else touchedPlayer e + pure $ if touched then e {dir = DirLeft, frame = 0, actions = [ActionExitStarted]} else updateLoop + | otherwise = pure updateOnce where fullyOpen :: Int fullyOpen = 7 - update :: Entity - update + updateLoop :: Entity + updateLoop | e.delay > 0 = e {delay = e.delay - 1} | e.frame + 1 < frameLimit e = e {delay = frameDelay, frame = e.frame + 1} | otherwise = e {delay = frameDelay, frame = fullyOpen} + + updateOnce :: Entity + updateOnce + | e.delay > 0 = e {delay = e.delay - 1} + | e.frame + 1 < frameLimit e = e {delay = frameDelay, frame = e.frame + 1} + | otherwise = e {destroy = True, actions = [ActionExitDone]} diff --git a/src/Game/Entities/Types.hs b/src/Game/Entities/Types.hs index 392a86f..4ece6c5 100644 --- a/src/Game/Entities/Types.hs +++ b/src/Game/Entities/Types.hs @@ -14,7 +14,7 @@ import qualified Game.Sprites as S data Dir = DirRight | DirLeft | Dying deriving (Eq) -data Type = TypePlayer | TypePickup | TypeEffect | TypeEnemy +data Type = TypePlayer | TypePickup | TypeEffect | TypeEnemy deriving (Eq) type Collision = Entity -> IO Bool @@ -29,7 +29,7 @@ data Entities = Entities -- | The effect name must match the sprite name in the spritesheet. type EffectName = String -data Action = ActionAddEffect Int Int EffectName | ActionAddBattery Int Int | ActionHitPlayer | ActionExitLevel +data Action = ActionAddEffect Int Int EffectName | ActionAddBattery Int Int | ActionHitPlayer | ActionExitStarted | ActionExitDone data Entity = Entity { typ :: Type, diff --git a/src/Game/State.hs b/src/Game/State.hs index 3773268..4f46c01 100644 --- a/src/Game/State.hs +++ b/src/Game/State.hs @@ -1,4 +1,4 @@ -module Game.State (State (..), initialState, levelState, maxLives) where +module Game.State (State (..), initialState, levelState, maxLives, ExitState (..)) where import Game.Entities.Const (hitDelay) import qualified Game.Map as M @@ -6,6 +6,8 @@ import qualified Game.Map as M maxLives :: Int maxLives = 4 +data ExitState = ExitOff | ExitStarted | ExitDone deriving (Eq) + data State = State { batteries :: Int, totalBatteries :: Int, @@ -15,7 +17,7 @@ data State = State gameOverDelay :: Int, exit :: Bool, lastBattery :: (Int, Int), - levelCompleted :: Bool, + levelCompleted :: ExitState, currentLevel :: Int } @@ -31,7 +33,7 @@ initialState m = exit = False, -- doesn't matter where lastBattery = (0, 0), - levelCompleted = False, + levelCompleted = ExitOff, currentLevel = 0 } @@ -45,5 +47,5 @@ levelState s m = exit = False, -- doesn't matter where lastBattery = (0, 0), - levelCompleted = False + levelCompleted = ExitOff } -- cgit v1.2.3