From ed585355b1f78de15885e803138a98b75ca2b1e2 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Mon, 20 Feb 2023 21:52:22 +0000 Subject: Split entities in modules. --- src/Game/Entities/Common.hs | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/Game/Entities/Common.hs (limited to 'src/Game/Entities/Common.hs') diff --git a/src/Game/Entities/Common.hs b/src/Game/Entities/Common.hs new file mode 100644 index 0000000..b79c3db --- /dev/null +++ b/src/Game/Entities/Common.hs @@ -0,0 +1,87 @@ +module Game.Entities.Common + ( toSpriteSet, + frameLimit, + collision, + hitPlayer, + collectedBattery, + updateFrame, + updateGravity, + ) +where + +import Data.IORef +import Game.Entities.Const +import Game.Entities.Types +import qualified Game.Sprites as S +import qualified Game.State as GS +import SDL (($~)) + +-- | Convert direction into a sprite set. +toSpriteSet :: Dir -> Int +toSpriteSet DirRight = 0 +toSpriteSet DirLeft = 1 + +-- | Return the number of frames available on the entity's sprite for current direction. +frameLimit :: Entity -> Int +frameLimit e = S.frameCount e.sprite (toSpriteSet e.dir) + +-- | Collision detection of player vs entity. +-- +-- The player's head won't register, this is necessary to avoid hitting things on a platform above when jumping. +collision :: IORef Entity -> Collision +collision playerRef other = do + player <- readIORef playerRef + pure $ + player.x + 4 < other.x + 12 + && other.x + 4 < player.x + 12 + && player.y + 12 < other.y + 16 + && other.y + 4 < player.y + 24 + +-- | Update game state to reflect that the player was hit by an enemy. +hitPlayer :: IORef GS.State -> IO () +hitPlayer stateRef = + stateRef $~ updatePlayerHit + where + updatePlayerHit :: GS.State -> GS.State + updatePlayerHit s = 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 () +collectedBattery stateRef = + stateRef $~ (\s -> s {GS.batteries = s.batteries + 1}) + +-- | Update frame animation for entities that have direction. +updateFrame :: Bool -> Entity -> Entity +updateFrame updated e + | isGravityOn = e + | e.delay > 0 = e {delay = e.delay - 1} + | e.frame + 1 < frameLimit e = e {delay = frameDelay, frame = if updated then e.frame + 1 else 0} + | otherwise = e {delay = frameDelay, frame = 0} + where + isGravityOn = e.gravity > gravityOff + +applyGravity :: IsBlocked -> Int -> Entity -> Entity +applyGravity isBlocked v e + | v == 0 = e + -- hit the floor + | isGoingDown + && (isBlocked (e.x + 4) (e.y + 24) || isBlocked (e.x + 10) (e.y + 24)) + && not (isBlocked (e.x + 4) (e.y + 23)) + && not (isBlocked (e.x + 10) (e.y + 23)) = + e {jumping = False, gravity = gravityOff, delay = 0} + | otherwise = applyGravity isBlocked (v - 1) e {y = e.y + change} + where + isGoingDown = e.gravity >= gravityDown + change = if isGoingDown then 1 else -1 + +-- | Update gravity. +-- +-- XXX: hardcoded to 16x24 pixels sprite. +updateGravity :: IsBlocked -> Entity -> Entity +updateGravity isBlocked e + | current > gravityOff = applyGravity isBlocked (gravityTable !! current) e {gravity = new} + | not (isBlocked (e.x + 4) (e.y + 24) || isBlocked (e.x + 10) (e.y + 24)) = e {gravity = gravityDown, frame = jumpFrame} + | otherwise = e + where + current = e.gravity + new = if current > gravityOff && current < length gravityTable - 1 then current + 1 else current -- cgit v1.2.3