packages feed

venzone-1.0.0.0: src/Screen/Move.hs

{-# Language FlexibleContexts #-}

module Screen.Move where

import Terminal.Game
import Lens.Micro.Platform

import Meeple.Operate
import Room
import Screen.Check
import Screen.Collision
import Screen.Primitives

import qualified Data.List  as L


-- moves, check for room and meeples collision <= one by one
moveCollideGeneral :: HasScreen s => s -> s
moveCollideGeneral s =
            let -- motion (non step) action
                s'   = s & meeples . each %~ applyGravity (s ^. room)
                         & meeples . each %~ limitSpeed
                -- steps
                s''  = exhaustSteps (generateAllSteps s')
                -- dead cleaning will be done in story,
                -- since messages are collected there
            in s''
    where
          -- step actions
          exhaustSteps :: HasScreen s => s -> s
          exhaustSteps sw =
              if any hasSteps (sw ^. meeples)
                then exhaustSteps (oneStep sw)
                else sw

          oneStep :: HasScreen s => s -> s
          oneStep sw = let ms = sw ^. meeples
                           ei = length ms - 1
                      in
                      L.foldl' stepCollide sw [0..ei]


------------
-- MOTION --
------------

-- *non* stepped stuff

gravity  :: Integer
gravity = 1

-- room:: jus for oob check
-- todo [refactor] refactor
applyGravity :: Room -> Meeple -> Meeple
applyGravity _ m@(MDead _)  = m
applyGravity _ m@(MBird _)  = m
applyGravity _ m@(MDumb _)  = m & speed . _1 +~ gravity
applyGravity _ m@(MKram _)  = m & speed . _1 +~ gravity
applyGravity _ m@(MLock _)  = m
applyGravity _ m@(MNettle _)  = m
applyGravity r m@(MPlayer p)
        | p ^. isClimbing = m
        | isOOBRoom r
            (p ^. position) = m -- do not apply in between room or we can't
                                -- high jump between rooms
        | otherwise       = m & speed . _1 +~ gravity
applyGravity _ m@(MSave _)  = m
applyGravity _ m@(MSmart _) = m & speed . _1 +~ gravity
applyGravity _ m@(MStar _)  = m
applyGravity _ m@(MSickle _) = m & speed . _1 +~ gravity
applyGravity _ m@(MStone _) = m & speed . _1 +~ gravity
applyGravity _ m@(MWin _)   = m
applyGravity _ m@(MWitch _)   = m

limitSpeed :: Meeple -> Meeple
limitSpeed m@(MDead _) = m
limitSpeed m     = m & speed . _1 %~ im (>=) 1
                     & speed . _2 %~ im (>=) 1
                     & speed . _2 %~ im (<=) (-1)
    where
          im :: (Integer -> Integer -> Bool) ->
                Integer -> Integer -> Integer
          im o t x = if x `o` t then t else x


-----------
-- STEPS --
-----------

generateAllSteps :: HasScreen s => s -> s
generateAllSteps s = s & meeples . each %~ generateSteps


-- single meeple, *one* step
stepCollide :: HasScreen s => s -> Index -> s
stepCollide s i = s & meeples . ix i %~ savePosition
                    & meeples . ix i %~ step s
                    & meeples . ix i %~ roomCollision s
                    & meeples        %~ meepleCollision i

savePosition :: Meeple -> Meeple
savePosition m@(MDead _) = m
savePosition m           = m & prevPos .~ m ^. position

-- this is a single step!
step :: HasScreen s => s -> Meeple -> Meeple
step _ m@(MDead _)   = m
step s m@(MBird _)   = simpleMove s m
step s m@(MDumb _)   = simpleMove s m
step s m@(MKram _)   = simpleMove s m
step _ m@(MLock _)   = m
step _ m@(MNettle _)  = m
step s m@(MPlayer _) = simpleMove s m
step _ m@(MSave _)   = m
step s m@(MSickle _)  = simpleMove s m
step s m@(MSmart _)  = simpleMove s m
step _ m@(MStar _)   = m
step s m@(MStone _)  = simpleMove s m
step s m@(MWin _)    = simpleMove s m
step s m@(MWitch _)    = everyMove s m


-----------------
-- ANCILLARIES --
-----------------

-- move everywhere, regardless of boundaries (spirit)
everyMove :: HasScreen s => s -> Meeple -> Meeple
everyMove _ m
        | not (hasSteps m) = m
        | otherwise        =
                let (sr, sc) = m ^. speed
                in m & position . _1 %~ (+sr)
                     & position . _2 %~ (+sc)
                     & id            %~ dropStep

-- try to move in a direction fallback to other solutions if possible
simpleMove :: (HasScreen s) => s -> Meeple -> Meeple
simpleMove s a
        | not (hasSteps a) = a
        | (fp:_) <- fps    = dropStep $ snd fp
        | otherwise        = dropStep $ a' & speed .~ (0, 0)
                               -- stuck? Just don't move
    where
          ps :: [(Coords, Meeple)]
          ps = map (uncurry $ makePosa a)
                   -- we first try the expected, the no vert, etc.
                   [(False, False),
                    (True, False),
                    (False, True),
                    (True, True)]

          fps :: [(Coords, Meeple)]
          fps = filter (\(c, _) -> canGoThere s a c) ps

          -- stop agi mov
          a' | (MPlayer p) <- a =
                  if p ^. amSlow . to not ||
                     (p ^. delay . to (ticks 2) . to fetchFrame)
                        -- todo [refactor] no idea why ticks 3
                    then MPlayer $ p & action .~ ANoAction
                    else MPlayer p
             | otherwise        = a

-- given Meeple we modify slightly its speed and obtain projected landing
-- places. Uper functions can check these are sensible (against walls etc.)
-- and pick the right move.
type VerticalOffset   = Bool
type HorizontalOffset = Bool
makePosa :: Meeple -> VerticalOffset -> VerticalOffset -> (Coords, Meeple)
makePosa m vo ho = (cs, m & vm & hm & position .~ cs)
    where
          pr = m ^. position . _1
          pc = m ^. position . _2

          stepSpeed = steps . to head

          sr | vo        = 0
             | otherwise = m ^. stepSpeed . _1

          sc | ho        = 0
             | otherwise = m ^. stepSpeed . _2

          cs = (pr + sr, pc + sc)

          vm | vo        = speed . _1 .~ 0
             | otherwise = id

          hm | ho        = \bm -> bm & speed . _2 .~ 0
                                     & id %~ pActStop
             | otherwise = id

          pActStop (MPlayer p) = MPlayer $ p & action .~ ANoAction
          pActStop wm          = wm