packages feed

werewolf-0.5.2.0: src/Game/Werewolf/Role.hs

{-|
Module      : Game.Werewolf.Role
Description : Simplistic role data structure and instances.

Copyright   : (c) Henry J. Wylde, 2016
License     : BSD3
Maintainer  : public@hjwylde.com

Roles are split into four categories:

* The Ambiguous.
* The Loners.
* The Villagers.
* The Werewolves.
-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types        #-}
{-# LANGUAGE TemplateHaskell   #-}

module Game.Werewolf.Role (
    -- * Role
    Role,
    name, allegiance, balance, description, rules,

    Allegiance(..),
    _Angel, _Villagers, _Werewolves,

    -- ** Instances
    allRoles, restrictedRoles,

    -- *** The Ambiguous
    -- | No-one knows the true nature of the Ambiguous, sometimes not even the Ambiguous themselves!
    --
    --   The Ambiguous are able to change allegiance throughout the game.
    devotedServantRole, orphanRole, wolfHoundRole,

    -- *** The Loners
    -- | The Loners look out for themselves and themselves alone.

    --   The Loners must complete their own objective.
    angelRole,

    -- *** The Villagers
    -- | Fraught with fear of the unseen enemy, the Villagers must work together to determine the
    --   truth and eliminate the threat to Fougères. The task before them will not be easy, but a
    --   certain few have learnt some tricks over the years that may turn out rather useful.

    --   The Villagers must lynch all of the Werewolves.
    druidRole, hunterRole, jesterRole, protectorRole, scapegoatRole, seerRole, simpleVillagerRole,
    villagerVillagerRole, witchRole,

    -- *** The Werewolves
    -- | Hiding in plain sight, the Werewolves are not a small trifle.

    --   The Werewolves must devour all of the Villagers.
    simpleWerewolfRole,

    -- * Utility functions
    is, isn't, filteredBy,
) where

import Control.Lens hiding (isn't)

import           Data.Function
import           Data.List
import           Data.Monoid
import           Data.Text     (Text)
import qualified Data.Text     as T

-- | Role definitions require only a few pieces of information.
--   Most of the game logic behind a role is implemented in "Game.Werewolf.Command" and
--   "Game.Werewolf.Engine".
--
--   The @balance@ attribute on a role indicates the allegiance it favours. For example, a Simple
--   Werewolf has a balance of -4 while the Seer has a balance of 2. A balance of 0 means it favours
--   neither allegiance.
--
--   N.B., role equality is defined on just the 'name' as a role's 'allegiance' may change
--   throughout the game.
data Role = Role
    { _name        :: Text
    , _allegiance  :: Allegiance
    , _balance     :: Int
    , _description :: Text
    , _rules       :: Text
    } deriving (Read, Show)

-- | The Loner allegiances are seldom used, rather they are present for correctness.
data Allegiance = Angel | Villagers | Werewolves
    deriving (Eq, Read, Show)

makeLenses ''Role

instance Eq Role where
    (==) = (==) `on` view name

makePrisms ''Allegiance

-- | A list containing all the roles defined in this file.
allRoles :: [Role]
allRoles =
    [ angelRole
    , devotedServantRole
    , druidRole
    , hunterRole
    , jesterRole
    , orphanRole
    , protectorRole
    , scapegoatRole
    , seerRole
    , simpleVillagerRole
    , simpleWerewolfRole
    , villagerVillagerRole
    , witchRole
    , wolfHoundRole
    ]

-- | A list containing roles that are restricted to a single instance per 'Game'.
--
--   @
--   'restrictedRoles' = 'allRoles' \\\\ ['simpleVillagerRole', 'simpleWerewolfRole']
--   @
restrictedRoles :: [Role]
restrictedRoles = allRoles \\ [simpleVillagerRole, simpleWerewolfRole]

-- | /Who could dream of a better servant than one willing to give up her life for that of her/
--   /masters? Don't rejoice too fast, as the devouring ambition within her could spell the end of/
--   /the village!/
--
--   Before the revelation of the card of the player eliminated by the village's vote, the Devoted
--   Servant can reveal herself by showing her card and taking on the role of the eliminated player.
--   Upon taking on her master's role, any special abilities are reset and any first turns are
--   replayed.
devotedServantRole :: Role
devotedServantRole = Role
    { _name         = "Devoted Servant"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "Who could dream of a better servant than one willing to give up her life for that of her"
        , "masters? Don't rejoice too fast, as the devouring ambition within her could spell the"
        , "end of the village!"
        ]
    , _rules        = T.unwords
        [ "Before the revelation of the card of the player eliminated by the village's vote, the"
        , "Devoted Servant can reveal herself by showing her card and taking on the role of the"
        , "eliminated player. Upon taking on her master's role, any special abilities are reset and"
        , "any first turns are replayed."
        ]
    }

-- | /Abandoned by their parents as a child, with no-one wanting to look after another mouth to/
--   /feed, the Orphan was left to fend for themself. No-one looks twice at the Orphan and even/
--   /fewer offer kindness towards the lonely child. One day however, one townsperson changes all/
--   /this. He offers the Orphan food, water and a roof over his head. Grateful for his chairty and/
--   /affection, the Orphan makes him their role model. Pray that no ill should befall their role/
--   /model, for who knows in such an event whom, or what, the Orphan may turn to.../
--
--   On the first night, the Orphan may choose a player to become his role model. If during the game
--   the role model is eliminated, the Orphan becomes a Werewolf. He will then wake up the next
--   night with his peers and will devour with them each night until the end of the game. However
--   for as long as the Orphan's role model is alive, he remains a Villager.
orphanRole :: Role
orphanRole = Role
    { _name         = "Orphan"
    , _allegiance   = Villagers
    , _balance      = -1
    , _description  = T.unwords
        [ "Abandoned by their parents as a child, with no-one wanting to look after another mouth"
        , "to feed, the Orphan was left to fend for themself. No-one looks twice at the Orphan and"
        , "even fewer offer kindness towards the lonely child. One day however, one townsperson"
        , "changes all this. He offers the Orphan food, water and a roof over his head. Grateful"
        , "for his chairty and affection, the Orphan makes him their role model. Pray that no ill"
        , "should befall their role model, for who knows in such an event whom, or what, the Orphan"
        , "may turn to..."
        ]
    , _rules        = T.unwords
        [ "On the first night, the Orphan may choose a player to become his role model. If during"
        , "the game the role model is eliminated, the Orphan becomes a Werewolf. He will then wake"
        , "up the next night with his peers and will devour with them each night until the end of"
        , "the game. However for as long as the Orphan's role model is alive, he remains a"
        , "Villager."
        ]
    }

-- | /All dogs know in the depths of their soul that their ancestors were wolves and that it's/
--   /mankind who has kept them in the state of childishness and fear, the faithful and generous/
--   /companions. In any case, only the Wolf-hound can decide if he'll obey his human and civilized/
--   /master or if he'll listen to the call of wild nature buried within him./
--
--   On the first night, the Wolf-hound chooses if he wants to be a Simple Villager or Werewolf. The
--   choice is final.
wolfHoundRole :: Role
wolfHoundRole = Role
    { _name         = "Wolf-hound"
    , _allegiance   = Villagers
    , _balance      = -1
    , _description  = T.unwords
        [ "All dogs know in the depths of their soul that their ancestors were wolves and that it's"
        , "mankind who has kept them in the state of childishness and fear, the faithful and"
        , "generous companions. In any case, only the Wolf-hound can decide if he'll obey his human"
        , "and civilized master or if he'll listen to the call of wild nature buried within him."
        ]
    , _rules        = T.unwords
        [ "On the first night, the Wolf-hound chooses if he wants to be a Simple Villager or"
        , "Werewolf. The choice is final."
        ]
    }

-- | /The muddy life of a village infested with evil creatures repulses him; he wishes to believe/
--   /he's the victim of a terrible nightmare, in order to finally wake up in his comfortable bed./
--
--   When the Angel is in play, the game always begins with the village's debate followed by an
--   elimination vote, and then the first night.
--
--   The Angel wins if he manages to get eliminated on the first round (day or night).
--   If he fails, then he becomes a Simple Villager for the rest of the game.
angelRole :: Role
angelRole = Role
    { _name         = "Angel"
    , _allegiance   = Angel
    , _balance      = 0
    , _description  = T.unwords
        [ "The muddy life of a village infested with evil creatures repulses him; he wishes to"
        , "believe he's the victim of a terrible nightmare, in order to finally wake up in his"
        , "comfortable bed."
        ]
    , _rules        = T.intercalate "\n"
        [ T.unwords
            [ "When the Angel is in play, the game always begins with the village's debate followed"
            , "by an elimination vote, and then the first night."
            ]
        , T.unwords
            [ "The Angel wins if he manages to get eliminated on the first round (day or night). If"
            , "he fails, then he becomes a Simple Villager for the rest of the game."
            ]
        ]
    }

-- | /How honoured we are to be in the presence of such a noble leader. The return of the Druid/
--   /marks an exceptional time in Fougères's history! Friend of the woodland creatures, practiced/
--   /philosopher and now, with the help of Ferina their companion, a bane to the Werewolves/
--   /themselves! My does she have a nose on her, strong enough to sniff out lycanthropes in close/
--   /proximity! Listen for her grunt and heed the Druid's words for they will not let you down./
--
--   Each morning when Ferina wakes from her slumber she will be alert and cautious. If the Druid is
--   next to a Werewolf then Ferina will grunt in warning.
druidRole :: Role
druidRole = Role
    { _name         = "Druid"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "How honoured we are to be in the presence of such a noble leader. The return of the"
        , "Druid marks an exceptional time in Fougères's history! Friend of the woodland creatures,"
        , "practiced philosopher and now, with the help of Ferina their companion, a bane to the"
        , "Werewolves themselves! My does she have a nose on her, strong enough to sniff out"
        , "lycanthropes in close proximity! Listen for her grunt and heed the Druid's words for"
        , "they will not let you down."
        ]
    , _rules        = T.unwords
        [ "Each morning when Ferina wakes from her slumber she will be alert and cautious. If the"
        , "Druid is next to a Werewolf then Ferina will grunt in warning."
        ]
    }

-- | /A skilled marksman with quick reflexes. In the unfortunate situation that they are jumped and/
--   /killed unjustly, they are able to let off a shot at their attacker, killing them instantly./
--   /The Hunter never misses./
--
--   If the Hunter gets killed they get to choose one player, believed to be an attacker, to kill
--   immediately.
hunterRole :: Role
hunterRole = Role
    { _name         = "Hunter"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "A skilled marksman with quick reflexes. In the unfortunate situation that they are"
        , "jumped and killed unjustly, they are able to let off a shot at their attacker, killing"
        , "them instantly. The Hunter never misses."
        ]
    , _rules        = T.unwords
        [ "If the Hunter gets killed they get to choose one player, believed to be an attacker, to"
        , "kill immediately."
        ]
    }

-- | /The Protector is one of the few pure of heart and altruistic Villagers. They are forever/
--   /putting others needs above their own, standing guard at night against this terrifying foe./
--   /Each night they fight against the Werewolves with naught but a sword and shield, potentially/
--   /saving an innocents life./
--
--   Each night the Protector may choose a player deemed worthy of their protection. That player is
--   safe for that night night (and only that night) against the Werewolves.
--
--   The Protector may not protect the same player two nights in a row.
protectorRole :: Role
protectorRole = Role
    { _name         = "Protector"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "The Protector is one of the few pure of heart and altruistic Villagers. They are forever"
        , "putting others needs above their own, standing guard at night against this terrifying"
        , "foe. Each night they fight against the Werewolves with naught but a sword and shield,"
        , "potentially saving an innocents life."
        ]
    , _rules        = T.intercalate "\n"
        [ T.unwords
            [ "Each night the Protector may choose a player deemed worthy of their protection. That"
            , "player is safe for that night night (and only that night) against the Werewolves."
            ]
        , "The Protector may not protect the same player two nights in a row."
        ]
    }

-- | /Werewolves don't just spring up out of the ground! That's where dwarves come from. Clearly/
--   /someone is to blame for this affliction to Fougères. Unluckily for the Scapegoat, since/
--   /no-one actually knows who brought them here, the blame is always laid upon them!/
--
--   If the village's vote ends in a tie, it's the Scapegoat who is eliminated instead of no-one.
--
--   In this event, the Scapegoat has one last task to complete: he must choose whom is permitted to
--   vote or not on the next day.
scapegoatRole :: Role
scapegoatRole = Role
    { _name         = "Scapegoat"
    , _allegiance   = Villagers
    , _balance      = 0
    , _description  = T.unwords
        [ "Werewolves don't just spring up out of the ground! That's where dwarves come from."
        , "Clearly someone is to blame for this affliction to Fougères. Unluckily for the"
        , "Scapegoat, since no-one actually knows who brought them here, the blame is always laid"
        , "upon them!"
        ]
    , _rules        = T.intercalate "\n"
        [ T.unwords
            [ "If the village's vote ends in a tie, it's the Scapegoat who is eliminated instead of"
            , "no-one."
            ]
        , T.unwords
            [ "In this event, the Scapegoat has one last task to complete: he must choose whom is"
            , "permitted to vote or not on the next day."
            ]
        ]
    }

-- | /Frequently misunderstood and thought to be a fortune teller, the Seer has the ability to see/
--   /into fellow townsfolk and determine their true nature. This ability to see is not given out/
--   /lightly, for certain it is a gift! Visions will always be true, but only for the present as/
--   /not even the Seer knowns what the future holds./
--
--   Each night the Seer sees the allegiance of a player of their choice.
seerRole :: Role
seerRole = Role
    { _name         = "Seer"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "Frequently misunderstood and thought to be a fortune teller, the Seer has the ability to"
        , "see into fellow townsfolk and determine their true nature. This ability to see is not"
        , "given out lightly, for certain it is a gift! Visions will always be true, but only for"
        , "the present as not even the Seer knowns what the future holds."
        ]
    , _rules        = "Each night the Seer sees the allegiance of a player of their choice."
    }

-- | /A simple, ordinary townsperson in every way. Some may be cobblers, others bakers or even/
--   /nobles. No matter their differences though, the plight of Werewolves in Fougères unites them/
--   /in this unfortunate time./
--
--   A Simple Villager has no special abilities, they must use their guile to determine whom among
--   them is not who they say they are.
simpleVillagerRole :: Role
simpleVillagerRole = Role
    { _name         = "Simple Villager"
    , _allegiance   = Villagers
    , _balance      = 1
    , _description  = T.unwords
        [ "A simple, ordinary townsperson in every way. Some may be cobblers, others bakers or even"
        , "nobles. No matter their differences though, the plight of Werewolves in Fougères unites"
        , "them in this unfortunate time."
        ]
    , _rules        = T.unwords
        [ "A Simple Villager has no special abilities, they must use their guile to determine whom"
        , "among them is not who they say they are."
        ]
    }

-- | /Every village needs a Jester, they're so stupid but provide so much entertainment! The Jester/
--   /may not have any special abilities, but at least no one in the village would want to hurt/
--   /him./
--
--   If the village votes to lynch the Jester, his identity is revealed. The village realise there's
--   no point in burning him and so he is set free.
--
--   The Jester continues to play but may no longer vote as no one can take him seriously.
jesterRole :: Role
jesterRole = Role
    { _name         = "Jester"
    , _allegiance   = Villagers
    , _balance      = 0
    , _description  = T.unwords
        [ "Every village needs a Jester, they're so stupid but provide so much entertainment! The"
        , "Jester may not have any special abilities, but at least no one in the village would want"
        , "to hurt him."
        ]
    , _rules        = T.intercalate "\n"
        [ T.unwords
            [ "If the village votes to lynch the Jester, his identity is revealed. The village"
            , "realise there's no point in burning him and so he is set free."
            ]
        , T.unwords
            [ "The Jester continues to play but may no longer vote as no one can take him"
            , "seriously."
            ]
        ]
    }

-- | /This person has a soul as clear and transparent as the water from a mountain stream. They/
--   /will deserve the attentive ear of their peers and will make their word decisive in crucial/
--   /moments./
--
--   When the game begins, the village is told the identity of the Villager-Villager, thus ensuring
--   certainty that its owner is truly an innocent Villager.
villagerVillagerRole :: Role
villagerVillagerRole = Role
    { _name         = "Villager-Villager"
    , _allegiance   = Villagers
    , _balance      = 2
    , _description  = T.unwords
        [ "This person has a soul as clear and transparent as the water from a mountain stream."
        , "They will deserve the attentive ear of their peers and will make their word decisive in"
        , "crucial moments."
        ]
    , _rules        = T.unwords
        [ "When the game begins, the village is told the identity of the Villager-Villager, thus"
        , "ensuring certainty that its owner is truly an innocent Villager."
        ]
    }

-- | /Somehow forgotten with the coming of the Werewolves, the Witch has a chance to prove themself/
--   /valuable to the village. The Witch is blessed (or maybe cursed) with the ability to make two/
--   /powerful potions; one of which may heal a victim of the Werewolves, the other able to poison/
--   /a player. The use of these potions could alter the village's currently misguided perception/
--   /of the Witch./
--
--   The Witch is called after the Werewolves. They are able to heal and poison one player per game.
--   There is no restriction on using both potions in one night or to heal themself.
witchRole :: Role
witchRole = Role
    { _name         = "Witch"
    , _allegiance   = Villagers
    , _balance      = 3
    , _description  = T.unwords
        [ "Somehow forgotten with the coming of the Werewolves, the Witch has a chance to prove"
        , "themself valuable to the village. The Witch is blessed (or maybe cursed) with the"
        , "ability to make two powerful potions; one of which may heal a victim of the Werewolves,"
        , "the other able to poison a player. The use of these potions could alter the village's"
        , "currently misguided perception of the Witch."
        ]
    , _rules        = T.unwords
        [ "The Witch is called after the Werewolves. They are able to heal and poison one player"
        , "per game. There is no restriction on using both potions in one night or to heal"
        , "themself."
        ]
    }

-- | /It's difficult to tell, but a Werewolf is merely a Villager underneath; unfortunately one/
--   /that has been afflicted with lycanthropy and consequently transforms each night into a/
--   /fearsome lupine. Not all hope is yet lost, some Villagers would like to try and cure the/
--   /curse of lycanthropy. Perhaps one day this dream will be realised./
--
--   A Werewolf may never devour another Werewolf.
simpleWerewolfRole :: Role
simpleWerewolfRole = Role
    { _name         = "Simple Werewolf"
    , _allegiance   = Werewolves
    , _balance      = -4
    , _description  = T.unwords
        [ "It's difficult to tell, but a Werewolf is merely a Villager underneath; unfortunately"
        , "one that has been afflicted with lycanthropy and consequently transforms each night into"
        , "a fearsome lupine. Not all hope is yet lost, some Villagers would like to try and cure"
        , "the curse of lycanthropy. Perhaps one day this dream will be realised."
        ]
    , _rules        = "A Werewolf may never devour another Werewolf."
    }

-- | The counter-part to 'isn't', but more general as it takes a 'Getting' instead.
is :: Getting Any s a -> s -> Bool
is query = has query

-- | A re-write of 'Control.Lens.Prism.isn't' to be more general by taking a 'Getting' instead.
isn't :: Getting All s a -> s -> Bool
isn't query = hasn't query

-- | A companion to 'filtered' that, rather than using a predicate, filters on the given lens for
-- matches.
filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a
filteredBy lens value = filtered ((value ==) . view lens)