packages feed

venzone-1.0.0.0: src/Screen.hs

{-# Language ScopedTypeVariables #-}

module Screen ( module Screen,
                module Screen.Primitives,
                module Screen.ProcessKeys,
                module Screen.Check
              )
        where

import Input
import Meeple.Operate
import Room
import Plant
import Screen.Check
import Screen.Decide
import Screen.Move
import Screen.Primitives
import Screen.ProcessKeys

import Lens.Micro.Platform
import Terminal.Game

import qualified Data.List  as L


-----------
-- LOGIC --
-----------

screenLogic :: (Show s, HasScreen s) => Input -> s -> s
screenLogic k s
                -- key processing
        | isMovKey k  = s & player %~ fmap (processMovKeys k s)
        | isMenuKey k = playerDie s
                -- logic
        | otherwise   =
                  L.foldl' (flip ($)) s
                        [ -- I decide where to move
                          mappedScreen cogitate,

                          -- II actually move (and collision)
                          moveCollideGeneral,

                          -- III spawn phase (for projectile-launching
                          -- enemies
                          spawnProjectiles,

                          -- IV clean-up (reset + mark deads)
                          cleanUp
                        ]
    where
          mappedScreen :: HasScreen s => (s -> Meeple -> Meeple) -> s -> s
          mappedScreen f sw = sw & meeples . each %~ f sw

cleanUp :: HasScreen s => s -> s
cleanUp s = s & room . plant   %~ tickPlant
              & meeples . each %~ (resetClimb s .
                                   (\m -> m & draw %~ tick) .
                                   oobDead .
                                   uselessLock .
                                   oldMPlayer)
    where
          oobDead :: Meeple -> Meeple
          oobDead m@(MDead _) = m
          oobDead m           =
                let (tr, tc) = s ^. room . to boundaries
                    (mr, mc) = m ^. position
                in
                if mr < -5 || mc < -10 ||
                   mr > (tr + 5) || mc > (tc + 5)
                   then MDead None
                   else m

          uselessLock :: Meeple -> Meeple
          uselessLock m@(MLock l)
                | l ^. currNumeral <= 0 &&
                  l ^. draw . to isExpired = MDead None
                | otherwise             = m
          uselessLock m                 = m

          oldMPlayer :: Meeple -> Meeple
          oldMPlayer (MPlayer p) = MPlayer $ oldPlayer p
          oldMPlayer m           = m

resetClimb :: HasScreen s => s -> Meeple -> Meeple
resetClimb _ m@(MDead _) = m
resetClimb s m | amOutOfBounds s m  = m
               | not (canClimb s m) = noClimb m
               | otherwise          = m
    where
          noClimb (MPlayer p) = MPlayer $ p & isClimbing .~ False
          noClimb mw          = mw

spawnProjectiles :: HasScreen s => s -> s
spawnProjectiles s =
            let s' = s & meeples . each %~ genProj
                sp :: [Meeple]
                sp = concat $ s ^.. meeples . each . to fetchProj . _Just
            in s' & meeples %~ (++ sp)
    where
          fetchProj :: Meeple -> Maybe [Meeple]
          fetchProj (MBird b) = creaRef <$> b ^. ready
          fetchProj (MKram k) = creaRef <$> k ^. ready
          fetchProj MDead {} = Nothing
          fetchProj MDumb {} = Nothing
          fetchProj MLock {} = Nothing
          fetchProj MNettle {} = Nothing
          fetchProj MPlayer {} = Nothing
          fetchProj MSave {} = Nothing
          fetchProj MSmart {} = Nothing
          fetchProj MSickle {} = Nothing
          fetchProj MStar {} = Nothing
          fetchProj MStone {} = Nothing
          fetchProj MWin {} = Nothing
          fetchProj MWitch {} = Nothing


----------
-- DRAW --
----------

drawScreen :: HasScreen s => s -> Plane
drawScreen s =          blankPlane 80 24 &
               (r, c) % mergePlanes (drawRoom (s ^. room)) pcs
    where
          (r, c) = let (aw, ah) = planeSize actionPlane
                   in (div (24-ah) 2 + 1,
                       div (80-aw) 2 + 1)

          actionPlane = mergePlanes (drawRoom (s ^. room)) pcs

          meepOrd :: [Meeple]
          meepOrd = let ms = (s ^.. meeples . each . filtered (not . isDead))
                    in L.sort ms

          pcs :: [(Coords, Plane)]
          pcs = map f meepOrd

          f :: Meeple -> (Coords, Plane)
          f m = (m ^. position, m ^. draw . to fetchFrame)