packages feed

venzone-1.0.0.0: src/Meeple/Primitives.hs

{-# Language RankNTypes #-}
{-# Language TemplateHaskell #-}
{-# Language ConstraintKinds #-}

module Meeple.Primitives where

-- NPC/PC/Objects primitives

import Input
import Lens.Micro.Platform
import Terminal.Game

import qualified Data.Function as F
import qualified Data.Char as C


-------------
-- CLASSES --
-------------

-- Classes:
-- - stationary
--     - moving
--         - delayed
--             - walker
-- - casual


data Stationary = Stationary { _position :: Coords,
                               _draw     :: Animation }
              deriving (Eq, Show)
makeClassy ''Stationary

defaultStationary :: Coords -> Animation -> Stationary
defaultStationary c a = Stationary c a

-- speed in row/column
type Speed = (Row, Column)
type Step  = Speed -- movement relative to last step

data Moving = Moving
        { _movingStationary :: Stationary,
          _speed            :: Speed,
          _steps            :: [Step],
          _prevPos          :: Coords  }
              deriving (Eq, Show)
makeClassy ''Moving
instance HasStationary Moving where stationary = movingStationary

defaultMoving :: Coords -> Animation -> Moving
defaultMoving c a = Moving (defaultStationary c a)
                           (0, 0) [] c

-- delayed moving (once every x frames)
data Delayed = Delayed
        { _delayedMoving :: Moving,
          _delay         :: Timed Bool }
                        -- will wait x cycles before acting
              deriving (Eq, Show)
makeClassy ''Delayed
instance HasMoving Delayed where moving = delayedMoving
instance HasStationary Delayed where
        stationary = delayedMoving . movingStationary

defaultDelayed :: Coords -> Animation -> Integer -> Delayed
defaultDelayed c a i = Delayed (defaultMoving c a) (creaBoolTimerLoop i)

data Walker = Walker
        { _walkerDelayed :: Delayed,
          _cardinal      :: Cardinal }
          deriving (Eq, Show)
makeClassy ''Walker
instance HasDelayed Walker where delayed = walkerDelayed
instance HasMoving Walker where moving = walkerDelayed . delayedMoving
instance HasStationary Walker where
        stationary = walkerDelayed . movingStationary

defaultWalker :: Coords -> Animation -> Integer -> Cardinal -> Walker
defaultWalker c a i k = Walker (defaultDelayed c a i) k


newtype Casual = Casual { _seed :: StdGen }
              deriving (Show)
makeClassy ''Casual

-- dirty way of setting up an rng
defaultCasual :: Coords -> Casual
defaultCasual (r, c) = Casual $ mkStdGen (fromIntegral $ r + c)

-- cosa vado a creare? Non posso usare Meeple direttamente, perché grouping
-- of TH non lascia spazio per def circolari.

data MeepRef = MRStone Coords
             | MRSickle Coords Speed
            deriving (Eq, Show)

data Generator = Generator
        { _generatorCasual :: Casual,
          _ready           :: Maybe MeepRef }
              deriving (Show)
makeClassy ''Generator
instance HasCasual Generator where casual = generatorCasual
instance Eq Generator where
    ga == gb = F.on (==) _ready ga gb

defaultGenerator :: Coords -> Generator
defaultGenerator cs = Generator (defaultCasual cs) Nothing


--------------------
-- ACTUAL MEEPLES --
--------------------

-- STATIONARY --

newtype Star = Star { _starStationary :: Stationary }
        deriving (Eq, Show)
makeLenses ''Star
instance HasStationary Star where stationary = starStationary
defaultStar :: Coords -> Star
defaultStar cs = Star $
            defaultStationary
                cs
                (creaStaticAnimation $ cell '*' # color Yellow Vivid)

data Lock = Lock { _lockStationary :: Stationary,
                   _origNumeral :: Integer,   -- stars to collect to open it
                   _currNumeral :: Integer }
        deriving (Eq, Show)
makeLenses ''Lock
instance HasStationary Lock where stationary = lockStationary

defaultLock :: Coords -> Integer -> Lock
defaultLock cs i = Lock sta i i
    where
          sta = defaultStationary
                  cs
                  (creaStaticAnimation $ cell 'L')

newtype Stone = Stone { _stoneMoving :: Moving }
        deriving (Eq, Show)
makeLenses ''Stone
instance HasMoving Stone where moving = stoneMoving
instance HasStationary Stone where
    stationary = stoneMoving . movingStationary

defaultStone :: Coords -> Stone
defaultStone cs = Stone $
            defaultMoving cs
                (creaStaticAnimation $ cell 'o' # color Cyan Vivid)

newtype Sickle = Sickle { _sickleMoving :: Moving }
        deriving (Eq, Show)
makeLenses ''Sickle
instance HasMoving Sickle where moving = sickleMoving
instance HasStationary Sickle where
    stationary = sickleMoving . movingStationary

defaultSickle :: Coords -> Speed -> Sickle
defaultSickle cs s = Sickle def
    where
          sickAni = creaLoopAnimation
                      [(1, cell '<' # color Cyan Vivid),
                       (1, cell '^' # color Cyan Vivid),
                       (1, cell '>' # color Cyan Vivid),
                       (1, cell 'v' # color Cyan Vivid)
                      ]

          def = (defaultMoving cs sickAni) { _speed = s }

data NetStatus = NNo | NWake | NYes
        deriving (Eq, Show)
newtype Nettle = Nettle { _nettleStationary :: Stationary }
        deriving (Eq, Show)
makeLenses ''Nettle
instance HasStationary Nettle where stationary = nettleStationary

defaultNettle :: Coords -> Nettle
defaultNettle c = Nettle (defaultStationary c ia)
    where
          ia = creaLoopAnimation
                    [(19, cell ',' # color Green Dull),
                     ( 5, cell ';' # color Green Dull),
                     (26, cell 'A' # color Green Dull),
                     ( 5, cell ';' # color Green Dull)]

-- MOVING --

newtype Dumb = Dumb { _dumbWalker :: Walker }
        deriving (Eq, Show)
makeLenses ''Dumb
instance HasWalker Dumb where walker = dumbWalker
instance HasDelayed Dumb where delayed = dumbWalker . walkerDelayed
instance HasMoving Dumb where moving = dumbWalker . delayedMoving
instance HasStationary Dumb where stationary = dumbWalker . movingStationary

defaultDumb :: Coords -> Cardinal -> Dumb
defaultDumb cs k = Dumb $
        defaultWalker
            cs
            (creaStaticAnimation $ cell 'd' # color Red Dull)
            2 k

newtype Smart = Smart { _smartWalker :: Walker }
        deriving (Eq, Show)
makeLenses ''Smart
instance HasWalker Smart where walker = smartWalker
instance HasDelayed Smart where delayed = smartWalker . walkerDelayed
instance HasMoving Smart where moving = smartWalker . delayedMoving
instance HasStationary Smart where stationary = smartWalker . movingStationary


defaultSmart :: Coords -> Cardinal -> Smart
defaultSmart cs k = Smart $
        defaultWalker
            cs
            (creaStaticAnimation $ cell 'd' # color Cyan Dull)
            2 k

data Krampus = Krampus { _krampusWalker  :: Walker,
                         _krampusGenerator :: Generator }
        deriving (Eq, Show)
makeLenses ''Krampus
instance HasWalker Krampus where walker = krampusWalker
instance HasDelayed Krampus where delayed = krampusWalker . walkerDelayed
instance HasMoving Krampus where moving = krampusWalker . delayedMoving
instance HasStationary Krampus where
    stationary = krampusWalker . movingStationary
instance HasGenerator Krampus where generator = krampusGenerator
instance HasCasual Krampus where casual = krampusGenerator . generatorCasual

defaultKrampus :: Coords -> Cardinal -> Krampus
defaultKrampus cs d = Krampus wlk gen
    where wlk = defaultWalker
                     cs
                     (creaStaticAnimation $ cell 'k' # color Blue Vivid # bold)
                     2 d
          gen = defaultGenerator cs


-- bird state is "where I spawned". Should be "read", not state, but it's ok.
data Bird = Bird { _birdDelayed   :: Delayed,
                   _birdGenerator :: Generator,
                   _spawnpoint    :: Coords }
        deriving (Eq, Show)
makeLenses ''Bird
instance HasDelayed Bird where delayed = birdDelayed
instance HasMoving Bird where moving = birdDelayed . delayedMoving
instance HasStationary Bird where stationary = birdDelayed . movingStationary
instance HasGenerator Bird where generator = birdGenerator
instance HasCasual Bird where casual = birdGenerator . generatorCasual
defaultBird :: Coords -> Bird
defaultBird cs = Bird del gen cs
    where del = defaultDelayed
                     cs
                     (creaStaticAnimation $ cell 'v' # color Magenta Vivid)
                     2
          gen = defaultGenerator cs

data Witch = Witch { _witchDelayed :: Delayed,
                     _witchCasual  :: Casual }
        deriving (Show)
makeLenses ''Witch
instance Eq Witch where
    ga == gb = F.on (==) _witchDelayed ga gb
instance HasDelayed Witch where delayed = witchDelayed
instance HasMoving Witch where moving = witchDelayed . moving
instance HasStationary Witch where stationary = witchDelayed . stationary
instance HasCasual Witch where casual = witchCasual
defaultWitch :: Coords -> Witch
defaultWitch cs = Witch del gen
    where del = defaultDelayed
                     cs
                     (creaStaticAnimation $ cell 'X' # color Yellow Dull # bold)
                     2
          gen = defaultCasual cs



data Win = Win { _winDelayed :: Delayed,
                 _floatino   :: Cardinal }
        deriving (Eq, Show)
makeLenses ''Win
instance HasDelayed Win where delayed = winDelayed
instance HasMoving Win where moving = winDelayed . delayedMoving
instance HasStationary Win where stationary = winDelayed . movingStationary
defaultWin :: Coords -> Win
defaultWin cs = Win del N
    where
          del = defaultDelayed
                    cs
                    (creaLoopAnimation
                       [(2, cell '+' # color Red Vivid),
                        (2, cell '*' # color Yellow Vivid),
                        (2, cell '+' # color Green Vivid),
                        (2, cell '*' # color Blue Vivid)])
                    30

newtype Save = Save { _saveStationary :: Stationary }
        deriving (Eq, Show)
makeLenses ''Save
instance HasStationary Save where stationary = saveStationary
defaultSave :: Coords -> Save
defaultSave cs = Save sta
    where
          sta = defaultStationary cs
                          (creaStaticAnimation $ cell '@' # color Blue Vivid )

-- PLAYER --

data PAction = ANoAction | AJumpL | AJumpR | AJumpX | AClimb | ADescend | AGoW | AGoE
        deriving (Eq, Show)

data Player = Player {
                _playerDelayed :: Delayed,
                _action       :: PAction,
                    -- AGI direction
                _IsClimbing :: Bool,
                _IsNew      :: Bool, -- no moves yet: True
                _AmSaving   :: Bool,

                    -- slow gimmick for orcolat
                _AmSlow :: Bool
             }
    deriving (Eq, Show)
makeLenses ''Player
instance HasDelayed Player where delayed = playerDelayed
instance HasMoving Player where moving = playerDelayed . moving
instance HasStationary Player where
    stationary = playerDelayed . movingStationary

defaultPlayer :: Coords -> Player
defaultPlayer cs = Player
        (defaultDelayed cs (creaStaticAnimation $ cell '@' # invert) 2)
                -- ricorda di modificare anche a' in simpleMove
        ANoAction
        False True False
        False


--------------
-- SUM TYPE --
--------------

-- If you add anything to this you must
-- 1) add the relevant data (see e.g. Bird)
-- 2) add a wrapper here (e.g. MBird)
-- 3) add the relevant parsing char to charMeeple
-- 4) then everything will be just fixing "non-exhaustive pattern matches"
--    (turn -WAll on for this)
-- 5) add collisions in Screen/Collision.hs collTable
data Meeple = MBird Bird
            | MDead Message
            | MDumb Dumb
            | MKram Krampus
            | MLock Lock
            | MNettle Nettle
            | MPlayer Player
            | MSave Save
            | MSmart Smart
            | MSickle Sickle
            | MStar Star
            | MStone Stone
            | MWin Win
            | MWitch Witch
        deriving (Eq, Show)

-- sort is needed when drawing, player should always come on top
instance Ord Meeple where
    compare (MPlayer _) _ = GT
    compare _ (MPlayer _) = LT
    compare _ _           = EQ

data Message = PlusStar Coords -- String: Name of the Room
             | NeutralStar Coords
             | Won
             | None
        deriving (Eq, Show)

-- parsing helper
charMeeple :: Char -> Coords -> Maybe Meeple
charMeeple '*' cs = Just . MStar $ defaultStar cs
charMeeple '+' cs = Just . MWin $ defaultWin cs
charMeeple 'd' cs = Just . MDumb $ defaultDumb cs W
charMeeple 'b' cs = Just . MDumb $ defaultDumb cs E
charMeeple 'f' cs = Just . MSmart $ defaultSmart cs W
charMeeple 'j' cs = Just . MSmart $ defaultSmart cs E
charMeeple 'v' cs = Just . MBird $ defaultBird cs
charMeeple 'o' cs = Just . MStone $ defaultStone cs
charMeeple 'k' cs = Just . MKram $ defaultKrampus cs E
charMeeple 'y' cs = Just . MKram $ defaultKrampus cs W
charMeeple '^' cs = Just . MSickle $ defaultSickle cs (2, 0)
charMeeple ',' cs = Just . MNettle $ defaultNettle cs
charMeeple '@' cs = Just . MSave $ defaultSave cs
charMeeple 'X' cs = Just . MWitch $ defaultWitch cs
charMeeple c   cs | C.isDigit c =
                      Just . MLock $ defaultLock cs (read [c])
charMeeple _   _  = Nothing


------------------------
-- SUM TYPE INSTANCES --
------------------------


-- "id setter, no getter" lens
els :: forall a s. String -> Lens' a s
els t = lens (error t) const

instance HasStationary Meeple where
    stationary f (MDead x)   = MDead <$> els "HasStationary on Dead" f x
    stationary f (MBird b)   = MBird <$> stationary f b
    stationary f (MDumb d)   = MDumb <$> stationary f d
    stationary f (MKram k)   = MKram <$> stationary f k
    stationary f (MLock l)   = MLock <$> stationary f l
    stationary f (MNettle n) = MNettle <$> stationary f n
    stationary f (MPlayer p) = MPlayer <$> stationary f p
    stationary f (MSave s)   = MSave <$> stationary f s
    stationary f (MSickle s) = MSickle <$> stationary f s
    stationary f (MSmart s)  = MSmart <$> stationary f s
    stationary f (MStar s)   = MStar <$> stationary f s
    stationary f (MStone s)  = MStone <$> stationary f s
    stationary f (MWin w)    = MWin <$> stationary f w
    stationary f (MWitch w)  = MWitch <$> stationary f w

instance HasMoving Meeple where
    moving f (MDead x)   = MDead <$> els "HasMoving on Dead" f x
    moving f (MBird d)   = MBird <$> moving f d
    moving f (MDumb d)   = MDumb <$> moving f d
    moving f (MKram k)   = MKram <$> moving f k
    moving f (MLock l)   = MLock <$> els "HasMoving on Lock" f l
    moving f (MNettle n) = MNettle <$> els "HasMoving on Nettle" f n
    moving f (MPlayer p) = MPlayer <$> moving f p
    moving f (MSave s)   = MSave <$> els "HasMoving on Save" f s
    moving f (MSmart s)  = MSmart <$> moving f s
    moving f (MSickle s) = MSickle <$> moving f s
    moving f (MStar s)   = MStar <$> els "HasMoving on Star" f s
    moving f (MStone s)  = MStone <$> moving f s
    moving f (MWin w)    = MWin <$> moving f w
    moving f (MWitch w)   = MWitch <$> moving f w

----------------
-- CATEGORIES --
----------------

data MCategory = Aether | Baddie | Bonus | Projectile | CPlayer | Static
               deriving (Show, Eq)

meepleCategory :: Meeple -> MCategory
meepleCategory (MDead _)   = Aether
meepleCategory (MBird _)   = Baddie
meepleCategory (MDumb _)   = Baddie
meepleCategory (MKram _)   = Baddie
meepleCategory (MLock _)   = Static
meepleCategory (MNettle _) = Baddie
meepleCategory (MPlayer _) = CPlayer
meepleCategory (MSave _)   = Bonus
meepleCategory (MSickle _) = Projectile
meepleCategory (MStar _)   = Bonus
meepleCategory (MSmart _)  = Baddie
meepleCategory (MStone _)  = Projectile
meepleCategory (MWin _)    = Bonus
meepleCategory (MWitch _)   = Baddie

class Meepeable a where
    a2m :: a -> Meeple

type Coo m = (HasStationary m, Meepeable m)

instance Meepeable Dumb where
    a2m = MDumb
instance Meepeable Krampus where
    a2m = MKram
instance Meepeable Smart where
    a2m = MSmart
instance Meepeable Meeple where
    a2m = id