aboutsummaryrefslogtreecommitdiff
path: root/src/Game/Entities/Runner.hs
blob: 76f0db2427df1289d8a48290ed264368169f1b32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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)
              }