venzone-1.0.0.0: src/Screen/Decide.hs
{-# Language FlexibleContexts #-}
{-# Language RankNTypes #-}
-- decide where to move kaj ktp
module Screen.Decide where
import Terminal.Game
import Lens.Micro.Platform
import Input
import Meeple.Operate
import Screen.Check
import Screen.Primitives
import qualified Line.Draw as D
-- cogitate = the part of r/c speed inluenced by the player (i.e. not
-- gravity).
cogitate :: HasScreen s => s -> Meeple -> Meeple
cogitate _ m@(MDead _) = m
cogitate s (MBird b) = periodicMove s b MBird decideMoveBird stopAll
cogitate s (MDumb d) = periodicMove s d MDumb decideMoveDumbWalker stopH
cogitate s (MKram k) = periodicMove s k MKram decideMoveSmartWalker stopH
cogitate _ m@(MLock _) = m
cogitate _ m@(MNettle _) = m
cogitate s (MPlayer p) = dmp s p
cogitate _ m@(MSave _) = m
cogitate s (MSmart m) = periodicMove s m MSmart decideMoveSmartWalker stopH
cogitate _ m@(MStar _) = m
cogitate _ m@(MStone _) = m
cogitate _ m@(MSickle _) = m
cogitate s (MWin w) = periodicMove s w MWin decideMoveWin stopV
cogitate s (MWitch w) = periodicMove s w MWitch decideMoveWitch stopAll
periodicMove :: forall s a. (HasScreen s,
HasDelayed a,
HasMoving a) =>
s -> a -> -- input
(a -> Meeple) -> -- constructor
(s -> a -> a) -> -- decide mov function
(s -> a -> a) -> -- wait function
Meeple
periodicMove s a mc fmov wmov =
let t = a ^. delay in
if fetchFrame t
then mc $ fmov s a & delay %~ tick
else mc $ wmov s a & delay %~ tick
& speed . _2 .~ 0
---------------
-- MOVEMENTS --
---------------
dmp :: HasScreen s => s -> Player -> Meeple
dmp s p | p ^. amSlow = periodicMove s p MPlayer decideMovePlayer stopH
| otherwise = MPlayer $ decideMovePlayer s p
-- non delayed
decideMovePlayer :: HasScreen s => s -> Player -> Player
decideMovePlayer _ p = p & nuSpeed
where
mc = p ^. action
nuSpeed = case mc of
AJumpL -> jumpMod AGoW
AJumpR -> jumpMod AGoE
AJumpX -> jumpMod ANoAction
AClimb -> \lp -> lp & speed . _1 .~ (-1)
& speed . _2 .~ 0
ADescend -> \lp -> lp & speed . _1 .~ 1
& speed . _2 .~ 0
AGoW -> speed . _2 .~ (-1)
AGoE -> speed . _2 .~ 1
ANoAction -> id
-- what action after jumping? follow horizontal speed
jumpMod na wp =
wp & speed . _1 .~ (-3)
& action .~ na
decideMoveBird :: HasScreen s => s -> Bird -> Bird
decideMoveBird _ b =
-- move randomly
let (vs, b') = casualRandom (-1,1) b
(hs, b'') = casualRandom (-1,1) b'
(r, c) = b ^. position in
-- don't go too far away from where you spawned
if ellOut (r+vs, c+hs)
then b'' & speed .~ (0, 0)
-- do not move, pop the seed
else b'' & speed . _1 .~ vs
& speed . _2 .~ hs
where
dist :: Coords -> Coords -> Int
dist pa pb = length (D.bresenham pa pb) - 1
-- I am outside an ellipse?
ellOut :: Coords -> Bool
ellOut cs =
let -- focals
cc = b ^. spawnpoint
md = 10
f1 = cc & _1 %~ subtract md
f2 = cc & _1 %~ (+md)
in dist cs f1 + dist cs f2 > 20
decideMoveWitch :: HasScreen s => s -> Witch -> Witch
decideMoveWitch s w =
let -- possible random move
(vsr, w') = casualRandom (-1,1) w
(hsr, w'') = casualRandom (-1,1) w' in
case s ^. player of
Nothing -> w & speed .~ (vsr, hsr)
(Just p) ->
let -- possible towards player move
(pr, pc) = p ^. position
(wr, wc) = w ^. position
vsp = signum (pr - wr)
hsp = signum (pc - wc)
sn :: Integer
(sn, w''') = casualRandom (1, 10) w''
in
if sn > 7 then w''' & speed .~ (vsr, hsr)
else w''' & speed .~ (vsp, hsp)
decideMoveDumbWalker :: (HasScreen s, Meepeable m, HasWalker m, HasMoving m, HasStationary m) => s -> m -> m
decideMoveDumbWalker s b =
let b' = updateDumbWalker s b
nhs = b' ^. cardinal . to projhs
in b' & speed . _2 .~ nhs
decideMoveSmartWalker :: (HasScreen s, Meepeable m, HasWalker m, HasMoving m, HasStationary m) => s -> m -> m
decideMoveSmartWalker s m =
let m' = updateSmartWalker s m
nhs = m' ^. cardinal . to projhs
in m' & speed . _2 .~ nhs
decideMoveWin :: HasScreen s => s -> Win -> Win
decideMoveWin _ w =
let nvs = w ^. floatino . to projvs
in w & speed . _1 .~ nvs
& floatino %~ turnAround
-- decide = update state / update speed (auto)
-- update its state
updateDumbWalker :: (HasScreen s, Meepeable m, HasWalker m, HasStationary m) => s -> m -> m
updateDumbWalker s m =
let hs = m ^. cardinal . to projhs
ucs = m ^. position & _2 +~ hs
-- projected state
in
if checkSolidMeeps s m ucs
then m & cardinal %~ turnAround
else m
-- all smart (don't fallon ledges) walkers
updateSmartWalker :: (HasScreen s, Meepeable m, HasWalker m, HasStationary m) => s -> m -> m
updateSmartWalker s m =
if checkSolidMeeps s m ucs ||
not stepVoid
then m & cardinal %~ turnAround
else m
where
hs = m ^. cardinal . to projhs
ucs = m ^. position & _2 +~ hs
stepVoid :: Bool
stepVoid = checkGround s m (ucs & _1 +~ 1)
-----------------
-- ANCILLARIES --
-----------------
-- meeples don't move as fast as player, usually the move once per couple
-- of frames. What do we do when it's not our turn to move? Allow some stuff
-- (gravity, etc.) but not our "sentient" movement.
stopV :: (HasScreen s, HasMoving a) => s -> a -> a
stopV _ m = m & speed . _1 .~ 0
stopH :: (HasScreen s, HasMoving a) => s -> a -> a
stopH _ m = m & speed . _2 .~ 0
stopAll :: (HasScreen s, HasMoving a) => s -> a -> a
stopAll _ m = m & speed .~ (0, 0)
-- projected horizontal speed
projhs :: Cardinal -> Integer
projhs E = 1
projhs W = -1
projhs _ = error "Cardinal not valid hs"
-- projected horizontal speed
projvs :: Cardinal -> Integer
projvs S = 1
projvs N = -1
projvs _ = error "Cardinal not valid vs"