From 2f40109ab69892c7b7812fdbba3a83dc3ca96193 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Sat, 25 Feb 2023 13:50:04 +0000 Subject: Game over sequence WIP --- data/sprites.json | 6 ++++ data/sprites.png | Bin 13384 -> 15083 bytes src/Game.hs | 72 +++++++++++++++++++++++++++++++++++--------- src/Game/Entities/Common.hs | 5 ++- src/Game/Entities/Const.hs | 3 ++ src/Game/State.hs | 3 +- 6 files changed, 72 insertions(+), 17 deletions(-) diff --git a/data/sprites.json b/data/sprites.json index 844a796..61c751e 100644 --- a/data/sprites.json +++ b/data/sprites.json @@ -1,4 +1,10 @@ { + "game-over": { + "frames": [ + { "x": 0, "y": 224, "width": 96, "height": 16 } + ], + "sets": [[0]] + }, "player": { "frames": [ { "x": 0, "y": 0, "width": 16, "height": 24 }, diff --git a/data/sprites.png b/data/sprites.png index ee25de5..d2beccd 100644 Binary files a/data/sprites.png and b/data/sprites.png differ diff --git a/src/Game.hs b/src/Game.hs index f04acc6..4f84ad1 100644 --- a/src/Game.hs +++ b/src/Game.hs @@ -8,15 +8,16 @@ import Foreign.C.Types (CInt) import qualified Game.BitmapFont as BF import qualified Game.Controller as C import qualified Game.Entities as E +import Game.Entities.Const (gameOverDelay) import qualified Game.Hud as H import qualified Game.Map as M import qualified Game.Sprites as S import qualified Game.State as GS -import Game.Utils (isPressed) +import qualified Game.Utils as U import SDL (($=), ($~)) import qualified SDL import qualified SDL.Image -import SDL.Vect (V2 (..)) +import SDL.Vect (V2 (..), V4 (..)) name :: String name = "Haskell gamedev [Space Platformer]" @@ -87,7 +88,8 @@ main = do totalBatteries = M.totalBatteries map', lives = maxLives, totalLives = maxLives, - hitDelay = 0 + hitDelay = 0, + gameOverDelay = 0 } hud <- H.mkHud sprites state entities <- newIORef =<< E.mkEntities sprites map' controls state @@ -142,17 +144,15 @@ gameLoop = do canvas = env.canvas renderRect = env.renderRect controls = env.controls - map' = env.map - entities = env.entities - hud = env.hud + stateRef = env.state events <- map SDL.eventPayload <$> SDL.pollEvents -- F11 for fullscreen / windowed - when (fromMaybe False $ isPressed SDL.KeycodeF11 events) toggleFullscreen + when (fromMaybe False $ U.isPressed SDL.KeycodeF11 events) toggleFullscreen -- ESC or close the window to quit - let quit = fromMaybe False (isPressed SDL.KeycodeEscape events) || SDL.QuitEvent `elem` events + let quit = fromMaybe False (U.isPressed SDL.KeycodeEscape events) || SDL.QuitEvent `elem` events unless quit $ do -- update controls controls $~ flip C.update events @@ -160,14 +160,14 @@ gameLoop = do SDL.rendererRenderTarget renderer $= Just canvas SDL.clear renderer - updated <- liftIO $ E.updateAll =<< readIORef entities - entities $= updated - - -- render map and entities void $ liftIO $ do - M.render renderer map' - H.render renderer hud - E.render renderer updated + state <- readIORef stateRef + if state.lives > 0 + then playLoop env + else + if state.gameOverDelay > 0 + then fadeOutLoop env state.gameOverDelay >> stateRef $= state {GS.gameOverDelay = state.gameOverDelay - 1} + else gameOverLoop env SDL.rendererRenderTarget renderer $= Nothing SDL.clear renderer @@ -177,3 +177,45 @@ gameLoop = do SDL.present renderer gameLoop + where + playLoop :: Env -> IO () + playLoop env = do + let renderer = env.renderer + map' = env.map + entities = env.entities + hud = env.hud + + updated <- E.updateAll =<< readIORef entities + entities $= updated + + -- render map and entities + void $ do + M.render renderer map' + H.render renderer hud + E.render renderer updated + + fadeOutLoop :: Env -> Int -> IO () + fadeOutLoop env i = do + let renderer = env.renderer + map' = env.map + entities = env.entities + hud = env.hud + + -- render map and entities + -- doing a fade to black + void $ do + M.render renderer map' + H.render renderer hud + E.render renderer =<< readIORef entities + + SDL.rendererDrawBlendMode renderer $= SDL.BlendAlphaBlend + SDL.rendererDrawColor renderer $= V4 0 0 0 (fromIntegral (255 - i * (255 `div` gameOverDelay))) + SDL.fillRect renderer Nothing + + gameOverLoop :: Env -> IO () + gameOverLoop env = do + let renderer = env.renderer + sprites = env.sprites + + title <- S.get sprites "game-over" + S.render renderer title 112 80 0 0 diff --git a/src/Game/Entities/Common.hs b/src/Game/Entities/Common.hs index 2a0c6f1..251dbb4 100644 --- a/src/Game/Entities/Common.hs +++ b/src/Game/Entities/Common.hs @@ -43,7 +43,10 @@ hitPlayer stateRef = stateRef $~ updatePlayerHit where updatePlayerHit :: GS.State -> GS.State - updatePlayerHit s = s {GS.lives = s.lives - 1, GS.hitDelay = hitDelay} + updatePlayerHit s + -- if we run out of lives, trigger the game over + | s.lives == 1 = s {GS.lives = 0, GS.gameOverDelay = gameOverDelay} + | otherwise = s {GS.lives = s.lives - 1, GS.hitDelay = hitDelay} -- | Update game state to reflect that the player picked up a battery. collectedBattery :: IORef GS.State -> IO () diff --git a/src/Game/Entities/Const.hs b/src/Game/Entities/Const.hs index 4855e24..a24c304 100644 --- a/src/Game/Entities/Const.hs +++ b/src/Game/Entities/Const.hs @@ -3,6 +3,9 @@ module Game.Entities.Const where hitDelay :: Int hitDelay = 96 +gameOverDelay :: Int +gameOverDelay = 96 + frameDelay :: Int frameDelay = 6 diff --git a/src/Game/State.hs b/src/Game/State.hs index d943ac0..f8b5721 100644 --- a/src/Game/State.hs +++ b/src/Game/State.hs @@ -5,5 +5,6 @@ data State = State totalBatteries :: Int, lives :: Int, totalLives :: Int, - hitDelay :: Int + hitDelay :: Int, + gameOverDelay :: Int } -- cgit v1.2.3