module Game.Entities.Runner (mkRunner) where import Data.Bits (Bits (..)) import Game.Entities.Common import Game.Entities.Const import Game.Entities.Types import Game.Sprites qualified as S import System.Random (randomRIO) mkRunner :: S.SpriteSheet -> Int -> Int -> Dir -> Collision -> IsBlocked -> IsBlocked -> IO Entity mkRunner sprites x y d playerCollision isBlocked isBlockedDeadly = do s <- S.get sprites "runner" pure Entity { typ = TypeEnemy, x = x, y = y, delay = frameDelay, frame = 0, set = toSpriteSet d, jumping = False, gravity = gravityOff, dir = d, sprite = s, update = updateRunner playerCollision isBlocked isBlockedDeadly, destroy = False, actions = [], dat = RunnerData 0 } updateRunner :: Collision -> IsBlocked -> IsBlocked -> Entity -> IO Entity updateRunner touchedPlayer isBlocked isBlockedDeadly e = do touched <- touchedPlayer e if touched then pure e {actions = [ActionHitPlayer]} else do let gravityUpdated = updateGravity isBlocked e if gravityUpdated.gravity /= gravityOff then pure gravityUpdated else updateMovement $ updateFrame True gravityUpdated where updateMovement :: Entity -> IO Entity updateMovement ent | testBit ent.delay 1 = pure ent | ent.dir == DirLeft && ( isBlocked (ent.x - 1) (ent.y + 17) || isBlocked (ent.x - 1) (ent.y + 17) || checkDeadlyFloor (ent.x - 1) (ent.y + 24) ) = tryJumpingOrTurn ent | ent.dir == DirLeft = pure ent {x = ent.x - 1} | ent.dir == DirRight && ( isBlocked (ent.x + 16) (ent.y + 17) || isBlocked (ent.x + 16) (ent.y + 17) || checkDeadlyFloor (ent.x + 16) (ent.y + 24) ) = tryJumpingOrTurn ent | ent.dir == DirRight = pure ent {x = ent.x + 1} | otherwise = pure ent checkDeadlyFloor :: Int -> Int -> Bool checkDeadlyFloor x y | isBlockedDeadly x y = True | isBlocked x y = False | otherwise = checkDeadlyFloor x (y + 8) randomWallCount :: [Int] randomWallCount = [0, 0, 0, 2, 2, 3] tryJumpingOrTurn :: Entity -> IO Entity tryJumpingOrTurn ent | ent.dat.wallCount > 2 && not (isBlocked (ent.x + 8) (ent.y + 24 + 8)) && not (checkDeadlyFloor (ent.x + 8) (ent.y + 24 + 8)) = do r <- randomRIO (0, length randomWallCount - 1) :: IO Int pure ent { dir = turn ent.dir, set = toSpriteSet $ turn ent.dir, y = e.y + 1, gravity = gravityDown, frame = jumpFrame, dat = RunnerData $ randomWallCount !! r } | ent.dat.wallCount > 1 && not (isBlocked (ent.x + 8) (ent.y - 1)) && isBlocked (ent.x + 8) ent.y = do r <- randomRIO (0, length randomWallCount - 1) :: IO Int pure ent { dir = turn ent.dir, set = toSpriteSet $ turn ent.dir, gravity = gravityUp, frame = jumpFrame, actions = [ActionAddEffect ent.x (ent.y + 8) "dust"], dat = RunnerData $ randomWallCount !! r } | otherwise = pure ent { dir = turn ent.dir, set = toSpriteSet $ turn ent.dir, dat = RunnerData (ent.dat.wallCount + 1) }