aboutsummaryrefslogtreecommitdiff
path: root/src/Game/Entities/Common.hs
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-02-20 21:52:22 +0000
committerJuan J. Martinez <jjm@usebox.net>2023-02-20 21:52:22 +0000
commited585355b1f78de15885e803138a98b75ca2b1e2 (patch)
treeda25f10539fa39ee284dc49ab9d5e53721bc008f /src/Game/Entities/Common.hs
parent92785d2df84e18953048a6537b71c824a2a4b288 (diff)
downloadspace-plat-hs-ed585355b1f78de15885e803138a98b75ca2b1e2.tar.gz
space-plat-hs-ed585355b1f78de15885e803138a98b75ca2b1e2.zip
Split entities in modules.
Diffstat (limited to 'src/Game/Entities/Common.hs')
-rw-r--r--src/Game/Entities/Common.hs87
1 files changed, 87 insertions, 0 deletions
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