aboutsummaryrefslogtreecommitdiff
path: root/src/Game/Entities/Player.hs
blob: 72de1bbecdbdb3d4bedf8171e196a33e5cb84389 (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
module Game.Entities.Player (mkPlayer, dyingSet) where

import Data.IORef
import qualified Game.Controller as C
import Game.Entities.Common
import Game.Entities.Const
import Game.Entities.Types
import qualified Game.Sprites as S

dyingSet :: Int
dyingSet = 2

mkPlayer :: S.SpriteSheet -> Int -> Int -> IORef C.Controls -> IsBlocked -> IsBlocked -> IO Entity
mkPlayer sprites x y controls isBlocked isBlockedDeadly = do
  s <- S.get sprites "player"
  pure
    Entity
      { typ = TypePlayer,
        x = x,
        y = y,
        delay = 0,
        frame = 0,
        set = toSpriteSet DirRight,
        jumping = False,
        gravity = gravityOff,
        dir = DirRight,
        sprite = s,
        update = updatePlayer controls isBlocked isBlockedDeadly,
        destroy = False,
        actions = [],
        dat = PlayerData x y
      }

updateHorizontal :: IsBlocked -> Bool -> Bool -> Entity -> Entity
updateHorizontal isBlocked left right e
  -- prevent pressing both directions (keyboard)
  | left && right = e
  -- change direction first
  | left && e.dir /= DirLeft = e {dir = DirLeft, set = toSpriteSet DirLeft, delay = 0}
  | right && e.dir /= DirRight = e {dir = DirRight, set = toSpriteSet DirRight, delay = 0}
  | left && isNotGoingDown = if isBlocked (e.x - 1) (e.y + 23) then e else e {x = e.x - 1}
  | left && not isNotGoingDown = if isBlocked (e.x - 1) (e.y + 23) && isBlocked (e.x - 1) (e.y + 17) then e else e {x = e.x - 1}
  | right && isNotGoingDown = if isBlocked (e.x + 17) (e.y + 23) then e else e {x = e.x + 1}
  | right && not isNotGoingDown = if isBlocked (e.x + 17) (e.y + 23) && isBlocked (e.x + 17) (e.y + 17) then e else e {x = e.x + 1}
  | otherwise = e
  where
    isNotGoingDown = e.gravity == gravityOff || e.gravity >= gravityDown

updateVertical :: IsBlocked -> Bool -> Bool -> Entity -> Entity
updateVertical isBlocked jump down e
  | not jump
      || e.jumping
      -- make jumping easier with "Coyote time"
      || (e.gravity /= gravityOff && (e.gravity < gravityDown || e.gravity > jumpLimit)) =
      e
  | not down = e {jumping = True, gravity = gravityUp, frame = jumpFrame, actions = [ActionAddEffect e.x (e.y + 8) "dust"]}
  -- go down a 8 pixel tall platform; not ideal to have these values hardcoded here
  -- but to be fair, the player height/width is hardcoded as well
  | down
      && not (isBlocked (e.x + 4) (e.y + 24 + 8))
      && not (isBlocked (e.x + 10) (e.y + 24 + 8)) =
      e {gravity = gravityDown, frame = jumpFrame, y = e.y + 1}
  | otherwise = e

updateGravityCheckDeadly :: IsBlocked -> IsBlocked -> Entity -> Entity
updateGravityCheckDeadly isBlocked isBlockedDeadly e
  | isBlockedDeadly (updated.x + 8) (updated.y + 24) = updated {actions = updated.actions ++ [ActionHitPlayerDeadly]}
  | updated.gravity == gravityOff && e.gravity /= gravityOff = updated {dat = PlayerData updated.x updated.y}
  | otherwise = updated
  where
    updated = updateGravity isBlocked e

updatePlayer :: IORef C.Controls -> IsBlocked -> IsBlocked -> Entity -> IO Entity
updatePlayer controls isBlocked isBlockedDeadly e
  | e.set /= dyingSet = do
      ctl <- readIORef controls
      pure $
        updateGravityCheckDeadly isBlocked isBlockedDeadly $
          updateVertical isBlocked ctl.a ctl.down $
            updateHorizontal isBlocked ctl.left ctl.right $
              -- left or right, but not both (keyboard)
              updateFrame ((ctl.left || ctl.right) && (ctl.left /= ctl.right)) e
  | otherwise = pure $ updateGravity (\_ _ -> False) e