werewolf-1.5.1.1/app/0000755000000000000000000000000012751254103012455 5ustar0000000000000000werewolf-1.5.1.1/app/Game/0000755000000000000000000000000012713342045013327 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/0000755000000000000000000000000012751254103015120 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Command/0000755000000000000000000000000012751254103016476 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Message/0000755000000000000000000000000012755166155016521 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Variant/0000755000000000000000000000000012755166155016541 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledge/0000755000000000000000000000000012727224206021566 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledgeOrReveal/0000755000000000000000000000000012755166155023237 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleReveal/0000755000000000000000000000000012733100663021062 5ustar0000000000000000werewolf-1.5.1.1/app/Game/Werewolf/Variant/Standard/0000755000000000000000000000000012751722041020265 5ustar0000000000000000werewolf-1.5.1.1/app/Werewolf/0000755000000000000000000000000012755163323014256 5ustar0000000000000000werewolf-1.5.1.1/app/Werewolf/Command/0000755000000000000000000000000012751722041015626 5ustar0000000000000000werewolf-1.5.1.1/src/0000755000000000000000000000000012713342045012465 5ustar0000000000000000werewolf-1.5.1.1/src/Control/0000755000000000000000000000000012713342045014105 5ustar0000000000000000werewolf-1.5.1.1/src/Control/Lens/0000755000000000000000000000000012727250373015015 5ustar0000000000000000werewolf-1.5.1.1/src/Data/0000755000000000000000000000000012713342045013336 5ustar0000000000000000werewolf-1.5.1.1/src/Data/String/0000755000000000000000000000000012751254103014603 5ustar0000000000000000werewolf-1.5.1.1/src/Data/String/Interpolate/0000755000000000000000000000000012716050452017073 5ustar0000000000000000werewolf-1.5.1.1/src/Game/0000755000000000000000000000000012751722041013336 5ustar0000000000000000werewolf-1.5.1.1/src/Game/Werewolf/0000755000000000000000000000000012755166155015144 5ustar0000000000000000werewolf-1.5.1.1/variant/0000755000000000000000000000000012755166155013356 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/0000755000000000000000000000000012755166155016706 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/0000755000000000000000000000000012755166155020600 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/0000755000000000000000000000000012755166155022216 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/choose/0000755000000000000000000000000012755166155023476 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/ping/0000755000000000000000000000000012755166155023153 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/quit/0000755000000000000000000000000012755166155023200 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/status/0000755000000000000000000000000012755166155023541 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/0000755000000000000000000000000012755166155022045 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/general/0000755000000000000000000000000012755166155023462 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/lynching/0000755000000000000000000000000012755166155023660 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/new-game/0000755000000000000000000000000012755166155023545 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/sunrise/0000755000000000000000000000000012755166155023535 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/command/0000755000000000000000000000000012727224206020313 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/command/ping/0000755000000000000000000000000012727224206021250 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/command/status/0000755000000000000000000000000012727224206021636 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/engine/0000755000000000000000000000000012733076641020147 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-knowledge/engine/new-game/0000755000000000000000000000000012717232210021633 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/0000755000000000000000000000000012755166155016205 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/command/0000755000000000000000000000000012727221700017606 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/command/choose/0000755000000000000000000000000012727221700021066 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/command/ping/0000755000000000000000000000000012727224206020547 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/command/quit/0000755000000000000000000000000012727221700020570 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/command/status/0000755000000000000000000000000012751254103021131 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/engine/0000755000000000000000000000000012727221700017435 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/engine/general/0000755000000000000000000000000012733100663021053 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/engine/lynching/0000755000000000000000000000000012733071357021260 5ustar0000000000000000werewolf-1.5.1.1/variant/no-role-reveal/engine/sunrise/0000755000000000000000000000000012727221700021125 5ustar0000000000000000werewolf-1.5.1.1/variant/spiteful-village/0000755000000000000000000000000012755166155016632 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/0000755000000000000000000000000012755166155015156 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/0000755000000000000000000000000012751254103016557 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/boot/0000755000000000000000000000000012717220441017522 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/choose/0000755000000000000000000000000012717220441020037 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/circle/0000755000000000000000000000000012717220441020020 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/end/0000755000000000000000000000000012717220441017325 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/help/0000755000000000000000000000000012751722041017510 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/ping/0000755000000000000000000000000012727224206017520 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/quit/0000755000000000000000000000000012717220441017541 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/raise/0000755000000000000000000000000012751254103017662 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/status/0000755000000000000000000000000012751254103020102 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/unvote/0000755000000000000000000000000012717220441020077 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/version/0000755000000000000000000000000012717220441020244 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/command/vote/0000755000000000000000000000000012717220441017534 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/0000755000000000000000000000000012751254103016406 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/druids-turn/0000755000000000000000000000000012717220441020666 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/game-over/0000755000000000000000000000000012751254103020270 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/general/0000755000000000000000000000000012751254103020023 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/hunters-turn/0000755000000000000000000000000012717220441021064 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/lynching/0000755000000000000000000000000012751254103020221 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/necromancers-turn/0000755000000000000000000000000012751254103022053 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/new-game/0000755000000000000000000000000012733077270020116 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/oracles-turn/0000755000000000000000000000000012717220441021024 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/orphans-turn/0000755000000000000000000000000012717220441021046 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/protectors-turn/0000755000000000000000000000000012717220441021600 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/scapegoats-turn/0000755000000000000000000000000012751254103021525 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/seers-turn/0000755000000000000000000000000012717220441020515 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/sunrise/0000755000000000000000000000000012717220441020076 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/sunset/0000755000000000000000000000000012717220441017727 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/village-drunks-turn/0000755000000000000000000000000012717220441022323 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/villages-turn/0000755000000000000000000000000012717220441021202 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/werewolves-turn/0000755000000000000000000000000012717220441021576 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/engine/witchs-turn/0000755000000000000000000000000012717220441020675 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/0000755000000000000000000000000012717220441016272 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/0000755000000000000000000000000012717220441017710 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/boot/0000755000000000000000000000000012717220441020653 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/choose/0000755000000000000000000000000012751254103021170 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/general/0000755000000000000000000000000012717220441021325 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/heal/0000755000000000000000000000000012717220441020621 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/poison/0000755000000000000000000000000012717220441021217 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/protect/0000755000000000000000000000000012717220441021370 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/start/0000755000000000000000000000000012751254103021045 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/unvote/0000755000000000000000000000000012717220441021230 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/error/command/vote/0000755000000000000000000000000012717220441020665 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/0000755000000000000000000000000012751254103016102 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/alpha-wolf/0000755000000000000000000000000012717220441020134 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/beholder/0000755000000000000000000000000012717220441017666 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/crooked-senator/0000755000000000000000000000000012717220441021201 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/druid/0000755000000000000000000000000012717220441017211 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/dullahan/0000755000000000000000000000000012733071357017702 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/fallen-angel/0000755000000000000000000000000012717220441020427 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/hunter/0000755000000000000000000000000012717220441017407 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/jester/0000755000000000000000000000000012717220441017376 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/lycan/0000755000000000000000000000000012717220441017210 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/medusa/0000755000000000000000000000000012751254103017360 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/necromancer/0000755000000000000000000000000012751254103020376 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/oracle/0000755000000000000000000000000012717220441017347 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/orphan/0000755000000000000000000000000012717220441017371 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/protector/0000755000000000000000000000000012717220441020123 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/saint/0000755000000000000000000000000012733071357017230 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/scapegoat/0000755000000000000000000000000012717220441020050 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/seer/0000755000000000000000000000000012717220441017040 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/simple-villager/0000755000000000000000000000000012717220441021176 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/simple-werewolf/0000755000000000000000000000000012717220441021223 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/spiteful-villager/0000755000000000000000000000000012733100663021541 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/true-villager/0000755000000000000000000000000012717220441020664 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/village-drunk/0000755000000000000000000000000012717220441020646 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/witch/0000755000000000000000000000000012717220441017220 5ustar0000000000000000werewolf-1.5.1.1/variant/standard/role/zombie/0000755000000000000000000000000012751254103017367 5ustar0000000000000000werewolf-1.5.1.1/src/Control/Lens/Extra.hs0000644000000000000000000000270712727250373016442 0ustar0000000000000000{-| Module : Control.Lens.Extra Description : Extra utility functions for working with lenses. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Extra utility functions for working with lenses. -} {-# LANGUAGE Rank2Types #-} module Control.Lens.Extra ( module Control.Lens, -- * Folds is, isn't, hasuse, hasn'tuse, -- * Traversals filteredBy, ) where import Control.Lens hiding (isn't) import Control.Monad.State import Data.Monoid -- | The counter-part to 'isn't', but more general as it takes a 'Getting' instead. -- -- @'is' = 'has'@ is :: Getting Any s a -> s -> Bool is = has -- | A re-write of 'Control.Lens.Prism.isn't' to be more general by taking a 'Getting' instead. -- -- @'isn't' = 'hasn't'@ isn't :: Getting All s a -> s -> Bool isn't = hasn't -- | Check to see if this 'Fold' or 'Traversal' matches 1 or more entries in the current state. -- -- @'hasuse' = 'gets' . 'has'@ hasuse :: MonadState s m => Getting Any s a -> m Bool hasuse = gets . has -- | Check to see if this 'Fold' or 'Traversal' has no matches in the current state. -- -- @'hasn'tuse' = 'gets' . 'hasn't'@ hasn'tuse :: MonadState s m => Getting All s a -> m Bool hasn'tuse = gets . hasn't -- | 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) werewolf-1.5.1.1/src/Data/String/Humanise.hs0000644000000000000000000000137612751254103016717 0ustar0000000000000000{-| Module : Data.String.Humanise Description : Humanise type class for pretty printing data structures. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Humanise type class for displaying data structures to a human. -} {-# LANGUAGE OverloadedStrings #-} module Data.String.Humanise ( -- * Humanise Humanise(..), ) where import Data.Text (Text) import qualified Data.Text as T class Humanise a where humanise :: a -> Text instance Humanise Text where humanise = id instance Humanise a => Humanise [a] where humanise [] = "no-one" humanise [word] = humanise word humanise words = T.unwords [T.intercalate ", " (map humanise $ init words), "and", humanise $ last words] werewolf-1.5.1.1/src/Data/String/Interpolate/Extra.hs0000644000000000000000000000076312716050452020520 0ustar0000000000000000{-| Module : Data.String.Interpolate.Extra Description : Extra utility functions for working with the interpolate Quasi Quoter. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Extra utility functions for working with the interpolate Quasi Quoter. -} module Data.String.Interpolate.Extra ( -- * Quasi Quoters iFile, ) where import Data.String.Interpolate.IsString import Language.Haskell.TH.Quote iFile :: QuasiQuoter iFile = quoteFile i werewolf-1.5.1.1/src/Game/Werewolf.hs0000644000000000000000000000156212751722041015470 0ustar0000000000000000{-| Module : Game.Werewolf Description : Re-exports all of the public modules under /Game.Werewolf/. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Re-exports all of the public modules under /Game.Werewolf/. These are: * "Game.Werewolf.Game" * "Game.Werewolf.Player" * "Game.Werewolf.Response" * "Game.Werewolf.Role" * "Game.Werewolf.Variant" N.B., where clashes are found between "Game.Werewolf.Player", "Game.Werewolf.Role" and "Game.Werewolf.Variant", the "Game.Werewolf.Player" functions are preferred. -} module Game.Werewolf ( module Werewolf ) where import Game.Werewolf.Game as Werewolf import Game.Werewolf.Player as Werewolf import Game.Werewolf.Response as Werewolf import Game.Werewolf.Role as Werewolf hiding (activity, name) import Game.Werewolf.Variant as Werewolf hiding (description, name, tag) werewolf-1.5.1.1/src/Game/Werewolf/Game.hs0000644000000000000000000003521412751254270016346 0ustar0000000000000000{-| Module : Game.Werewolf.Game Description : Simplistic game data structure with lenses. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A game is not quite as simple as players! Roughly speaking though, this engine is /stateful/. The game state only changes when a /command/ is issued. Thus, this module defines the 'Game' data structure and any fields required to keep track of the current state. -} {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE TemplateHaskell #-} module Game.Werewolf.Game ( -- * Game Game, variant, stage, round, players, boots, chosenVoters, deadRaised, divine, fallenAngelLynched, healUsed, hunterRetaliated, jesterRevealed, marks, passed, poison, poisonUsed, priorProtect, protect, roleModel, scapegoatBlamed, see, votes, Stage(..), _DruidsTurn, _GameOver, _HuntersTurn1, _HuntersTurn2, _Lynching, _NecromancersTurn, _OraclesTurn, _OrphansTurn, _ProtectorsTurn, _ScapegoatsTurn, _SeersTurn, _Sunrise, _Sunset, _VillageDrunksTurn, _VillagesTurn, _WerewolvesTurn, _WitchsTurn, activity, allStages, stageCycle, stageAvailable, newGame, -- ** Folds votee, allowedVoters, pendingVoters, -- ** Prisms firstRound, secondRound, thirdRound, -- ** Searches getMarks, -- ** Queries hasAnyoneWon, hasDullahanWon, hasFallenAngelWon, hasNecromancerWon, hasVillagersWon, hasWerewolvesWon, hasEveryoneLost, ) where import Control.Lens.Extra import Data.List.Extra import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe import Data.String.Humanise import Data.Text (Text) import Game.Werewolf.Player import Game.Werewolf.Role hiding (activity, name) import Game.Werewolf.Variant hiding (name) import Prelude hiding (round) -- | There are a few key pieces of information that a game always needs to hold. These are: -- -- * the 'stage', -- * the 'round' number and -- * the 'players'. -- -- Any further fields on the game are specific to one or more roles (and their respective turns!). -- Some of the additional fields are reset each round (e.g., the Seer's 'see') while others are -- kept around for the whole game (e.g., the Orphan's 'roleModel'). data Game = Game { _variant :: Variant , _stage :: Stage , _round :: Int , _players :: [Player] , _boots :: Map Text [Text] , _chosenVoters :: [Text] -- ^ Scapegoat , _deadRaised :: Bool -- ^ Necromancer , _divine :: Maybe Text -- ^ Oracle , _fallenAngelLynched :: Bool -- ^ Fallen Angel , _healUsed :: Bool -- ^ Witch , _hunterRetaliated :: Bool -- ^ Hunter , _jesterRevealed :: Bool -- ^ Jester , _marks :: [Text] -- ^ Dullahan , _passed :: Bool -- ^ Witch , _poison :: Maybe Text -- ^ Witch , _poisonUsed :: Bool -- ^ Witch , _priorProtect :: Maybe Text -- ^ Protector , _protect :: Maybe Text -- ^ Protector , _roleModel :: Maybe Text -- ^ Orphan , _scapegoatBlamed :: Bool -- ^ Scapegoat , _see :: Maybe Text -- ^ Seer , _votes :: Map Text Text -- ^ Villagers and Werewolves } deriving (Eq, Read, Show) -- | Most of these are fairly self-explainable (the turn stages). 'Sunrise' and 'Sunset' are -- provided as meaningful breaks between the day and night as, for example, a 'VillagesTurn' may -- not always be available (curse that retched Scapegoat). -- -- Once the game reaches a turn stage, it requires a /command/ to help push it past. Often only -- certain roles and commands may be performed at any given stage. data Stage = DruidsTurn | GameOver | HuntersTurn1 | HuntersTurn2 | Lynching | NecromancersTurn | OraclesTurn | OrphansTurn | ProtectorsTurn | ScapegoatsTurn | SeersTurn | Sunrise | Sunset | VillageDrunksTurn | VillagesTurn | WerewolvesTurn | WitchsTurn deriving (Eq, Read, Show) instance Humanise Stage where humanise DruidsTurn = "Druid's turn" humanise GameOver = "Game over" humanise HuntersTurn1 = "Hunter's turn" humanise HuntersTurn2 = "Hunter's turn" humanise Lynching = "Lynching" humanise NecromancersTurn = "Necromancer's turn" humanise OraclesTurn = "Oracle's turn" humanise OrphansTurn = "Orphan's turn" humanise ProtectorsTurn = "Protector's turn" humanise ScapegoatsTurn = "Scapegoat's turn" humanise SeersTurn = "Seer's turn" humanise Sunrise = "Sunrise" humanise Sunset = "Sunset" humanise VillageDrunksTurn = "Village Drunk's turn" humanise VillagesTurn = "village's turn" humanise WerewolvesTurn = "Werewolves' turn" humanise WitchsTurn = "Witch's turn" makeLenses ''Game makePrisms ''Stage #if __GLASGOW_HASKELL__ >= 800 activity :: Contravariant f => (Activity -> f Activity) -> Stage -> f Stage #else activity :: Getter Stage Activity #endif activity = to getter where getter DruidsTurn = Diurnal getter GameOver = Diurnal getter HuntersTurn1 = Diurnal getter HuntersTurn2 = Diurnal getter Lynching = Diurnal getter NecromancersTurn = Nocturnal getter OraclesTurn = Nocturnal getter OrphansTurn = Nocturnal getter ProtectorsTurn = Nocturnal getter ScapegoatsTurn = Diurnal getter SeersTurn = Nocturnal getter Sunrise = Diurnal getter Sunset = Diurnal getter VillageDrunksTurn = Nocturnal getter VillagesTurn = Diurnal getter WerewolvesTurn = Nocturnal getter WitchsTurn = Nocturnal -- | All of the 'Stage's in the order that they should occur. allStages :: [Stage] allStages = [ Sunset , OrphansTurn , VillageDrunksTurn , NecromancersTurn , SeersTurn , OraclesTurn , ProtectorsTurn , WerewolvesTurn , WitchsTurn , Sunrise , HuntersTurn2 , DruidsTurn , VillagesTurn , Lynching , HuntersTurn1 , ScapegoatsTurn , GameOver ] -- | An infinite cycle of all 'Stage's in the order that they should occur. stageCycle :: [Stage] stageCycle = cycle allStages -- | Checks whether the stage is available for the given 'Game'. Most often this just involves -- checking if there is an applicable role alive, but sometimes it is more complex. -- -- One of the more complex checks here is for the 'VillagesTurn'. If the Fallen Angel is in play, -- then the 'VillagesTurn' is available on the first day rather than only after the first night. stageAvailable :: Game -> Stage -> Bool stageAvailable game DruidsTurn = has (players . druids . alive) game stageAvailable _ GameOver = False stageAvailable game HuntersTurn1 = has (players . hunters . dead) game && not (game ^. hunterRetaliated) stageAvailable game HuntersTurn2 = has (players . hunters . dead) game && not (game ^. hunterRetaliated) stageAvailable _ Lynching = True stageAvailable game NecromancersTurn = has (players . necromancers . alive) game && not (game ^. deadRaised) stageAvailable game OraclesTurn = has (players . oracles . alive) game stageAvailable game OrphansTurn = has (players . orphans . alive) game && isNothing (game ^. roleModel) stageAvailable game ProtectorsTurn = has (players . protectors . alive) game stageAvailable game ScapegoatsTurn = game ^. scapegoatBlamed stageAvailable game SeersTurn = has (players . seers . alive) game stageAvailable _ Sunrise = True stageAvailable _ Sunset = True stageAvailable game VillageDrunksTurn = has (players . villageDrunks . alive) game && is thirdRound game stageAvailable game VillagesTurn = has allowedVoters game stageAvailable game WerewolvesTurn = has (allowedVoters . werewolf) game stageAvailable game WitchsTurn = has (players . witches . alive) game && (not (game ^. healUsed) || not (game ^. poisonUsed)) -- | Creates a new 'Game' with the given players. No validations are performed here, those are left -- to the binary. newGame :: Variant -> [Player] -> Game newGame variant players = Game { _variant = variant , _stage = head stageCycle , _round = 0 , _players = players , _boots = Map.empty , _passed = False , _chosenVoters = [] , _deadRaised = False , _divine = Nothing , _fallenAngelLynched = False , _healUsed = False , _hunterRetaliated = False , _jesterRevealed = False , _marks = [] , _poison = Nothing , _poisonUsed = False , _priorProtect = Nothing , _protect = Nothing , _roleModel = Nothing , _scapegoatBlamed = False , _see = Nothing , _votes = Map.empty } -- | The traversal of the 'votes' victim's name. This is the player, if they exist, that received -- the majority of the votes. This could be an empty list depending on whether the votes were in -- conflict. votee :: Fold Game Player votee = folding getVotee -- | Gets the 'votes' victim's name. This is the player, if they exist, that received the majority -- of the votes. This could be an empty list depending on whether the votes were in conflict. getVotee :: Game -> [Player] getVotee game | Map.null (game ^. votes) = [] | length result /= 1 = [] | otherwise = game ^.. players . traverse . named (head result) where votees = Map.elems $ game ^. votes result = last $ groupSortOn (length . (`elemIndices` votees)) (nub votees) -- | The traversal of the allowed voters during the 'VillagesTurn' or 'WerewolvesTurn'. In a -- standard game, this is all 'Alive' players. However there are two scenarios for the -- 'VillagesTurn' that may change this: -- -- 1) if the 'scapegoat' has chosen some 'chosenVoters', it is these players. -- 2) if the 'jester' has been revealed, he may not vote. allowedVoters :: Fold Game Player allowedVoters = folding getAllowedVoters -- | Gets the allowed voters during the 'VillagesTurn' or 'WerewolvesTurn'. In a standard game, this -- is all 'Alive' players. However there are two scenarios for the 'VillagesTurn' that may change -- this: -- -- 1) if the 'scapegoat' has chosen some 'chosenVoters', it is these players. -- 2) if the 'jester' has been revealed, he may not vote. getAllowedVoters :: Game -> [Player] getAllowedVoters game | not . null $ game ^. chosenVoters = filter ((`elem` game ^. chosenVoters) . view name) players' | game ^. jesterRevealed = filter (isn't jester) players' | otherwise = players' where players' | has (stage . _WerewolvesTurn) game = game ^.. players . werewolves . alive | otherwise = game ^.. players . traverse . alive -- | The traversal of all 'Alive' players that have yet to vote. This is synonymous to @voters - -- Map.keys votes@ pendingVoters :: Fold Game Player pendingVoters = folding getPendingVoters -- | Gets all 'Alive' players that have yet to vote. This is synonymous to @voters - Map.keys -- votes@ getPendingVoters :: Game -> [Player] getPendingVoters game = game ^.. allowedVoters . filtered ((`Map.notMember` votes') . view name) where votes' = game ^. votes -- | The traversal of 'Game's on the first round. firstRound :: Prism' Game Game firstRound = prism (set round 0) $ \game -> (if game ^. round == 0 then Right else Left) game -- | The traversal of 'Game's on the second round. secondRound :: Prism' Game Game secondRound = prism (set round 1) $ \game -> (if game ^. round == 1 then Right else Left) game -- | The traversal of 'Game's on the third round. thirdRound :: Prism' Game Game thirdRound = prism (set round 2) $ \game -> (if game ^. round == 2 then Right else Left) game -- | Gets all the 'marks' in a game (which is names only) and maps them to their player. getMarks :: Game -> [Player] getMarks game = map (\name -> game ^?! players . traverse . named name) (game ^. marks) -- | Queries whether anyone has won. hasAnyoneWon :: Game -> Bool hasAnyoneWon game = any ($ game) [ hasDullahanWon , hasFallenAngelWon , hasNecromancerWon , hasVillagersWon , hasWerewolvesWon ] -- | Queries whether the Dullahan has won. The Dullahan wins if they manage to eliminate all their -- marks. hasDullahanWon :: Game -> Bool hasDullahanWon game = has (players . dullahans . alive) game && all (is dead) (getMarks game) -- | Queries whether the Fallen Angel has won. The Fallen Angel wins if they manage to get -- themselves lynched by the Villagers. hasFallenAngelWon :: Game -> Bool hasFallenAngelWon game = game ^. fallenAngelLynched -- | Queries whether the Necromancer has won. The 'Necromancer' wins if they and their zombies are -- the only players surviving. -- -- N.B., the Jester is not considered when determining whether the 'Necromancer' has won. hasNecromancerWon :: Game -> Bool hasNecromancerWon game = not (hasEveryoneLost game) && allOf (players . traverse . alive) (\player -> any ($ player) [is necromancer, is zombie, is jester]) game -- | Queries whether the 'Villagers' have won. The 'Villagers' win if they are the only players -- surviving. -- -- N.B., the Dullahan and Fallen Angel are not considered when determining whether the 'Villagers' -- have won. hasVillagersWon :: Game -> Bool hasVillagersWon game = not (hasEveryoneLost game) && allOf (players . traverse . alive) (\player -> any ($ player) [is villager, is dullahan, is fallenAngel]) game -- | Queries whether the 'Werewolves' have won. The 'Werewolves' win if they are the only players -- surviving. hasWerewolvesWon :: Game -> Bool hasWerewolvesWon game = not (hasEveryoneLost game) && allOf (players . traverse . alive) (is werewolf) game -- | Queries whether everyone has lost. hasEveryoneLost :: Game -> Bool hasEveryoneLost = allOf (players . traverse) (is dead) werewolf-1.5.1.1/src/Game/Werewolf/Player.hs0000644000000000000000000003441512751254103016726 0ustar0000000000000000{-| Module : Game.Werewolf.Player Description : Simplistic player data structure with lenses for searching, filtering and querying lists of players. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Players are quite simple in themselves. They have a 'name', 'role' and 'state'. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE TemplateHaskell #-} module Game.Werewolf.Player ( -- * Player Player, name, role, state, State(..), _Alive, _Dead, newPlayer, -- ** Traversals alphaWolf, beholder, crookedSenator, druid, dullahan, fallenAngel, hunter, jester, lycan, medusa, necromancer, oracle, orphan, protector, saint, scapegoat, seer, simpleVillager, simpleWerewolf, spitefulVillager, trueVillager, villageDrunk, witch, zombie, loner, villager, werewolf, -- | The following traversals are provided just as a bit of sugar to avoid continually writing -- @'traverse' .@. names, roles, states, -- | N.B., the following traversals are not legal for the same reason 'filtered' isn't! named, alphaWolves, beholders, crookedSenators, druids, dullahans, fallenAngels, hunters, jesters, lycans, medusas, necromancers, oracles, orphans, protectors, saints, scapegoats, seers, simpleVillagers, simpleWerewolves, spitefulVillagers, trueVillagers, villageDrunks, witches, zombies, loners, villagers, werewolves, alive, dead, ) where import Control.Lens.Extra import Data.Function import Data.String.Humanise import Data.Text as T import Game.Werewolf.Role hiding (name) -- | A player has a 'name', 'role' and 'state'. Any stateful information needed for a player's -- @role@ is held on the 'Game' itself. -- -- N.B., player equality is defined on just the 'name'. data Player = Player { _name :: Text , _role :: Role , _state :: State } deriving (Read, Show) -- | Surprise surprise, players may be 'Dead' or 'Alive'. data State = Alive | Dead deriving (Eq, Read, Show) makeLenses ''Player instance Eq Player where (==) = (==) `on` view name instance Humanise Player where humanise = view name makePrisms ''State -- | Creates a new 'Alive' player. newPlayer :: Text -> Role -> Player newPlayer name role = Player name role Alive -- | The traversal of 'Player's with an 'alphaWolfRole'. -- -- @ -- 'alphaWolf' = 'role' . 'only' 'alphaWolfRole' -- @ alphaWolf :: Traversal' Player () alphaWolf = role . only alphaWolfRole -- | The traversal of 'Player's with a 'beholderRole'. -- -- @ -- 'beholder' = 'role' . 'only' 'beholderRole' -- @ beholder :: Traversal' Player () beholder = role . only beholderRole -- | The traversal of 'Player's with a 'crookedSenatorRole'. -- -- @ -- 'crookedSenator' = 'role' . 'only' 'crookedSenatorRole' -- @ crookedSenator :: Traversal' Player () crookedSenator = role . only crookedSenatorRole -- | The traversal of 'Player's with a 'dullahanRole'. -- -- @ -- 'dullahan' = 'role' . 'only' 'dullahanRole' -- @ dullahan :: Traversal' Player () dullahan = role . only dullahanRole -- | The traversal of 'Player's with a 'druidRole'. -- -- @ -- 'druid' = 'role' . 'only' 'druidRole' -- @ druid :: Traversal' Player () druid = role . only druidRole -- | The traversal of 'Player's with a 'fallenAngelRole'. -- -- @ -- 'fallenAngel' = 'role' . 'only' 'fallenAngelRole' -- @ fallenAngel :: Traversal' Player () fallenAngel = role . only fallenAngelRole -- | The traversal of 'Player's with a 'hunterRole'. -- -- @ -- 'hunter' = 'role' . 'only' 'hunterRole' -- @ hunter :: Traversal' Player () hunter = role . only hunterRole -- | The traversal of 'Player's with a 'jesterRole'. -- -- @ -- 'jester' = 'role' . 'only' 'jesterRole' -- @ jester :: Traversal' Player () jester = role . only jesterRole -- | The traversal of 'Player's with a 'lycanRole'. -- -- @ -- 'lycan' = 'role' . 'only' 'lycanRole' -- @ lycan :: Traversal' Player () lycan = role . only lycanRole -- | The traversal of 'Player's with a 'medusaRole'. -- -- @ -- 'medusa' = 'role' . 'only' 'medusaRole' -- @ medusa :: Traversal' Player () medusa = role . only medusaRole -- | The traversal of 'Player's with a 'necromancerRole'. -- -- @ -- 'necromancer' = 'role' . 'only' 'necromancerRole' -- @ necromancer :: Traversal' Player () necromancer = role . only necromancerRole -- | The traversal of 'Player's with a 'oracleRole'. -- -- @ -- 'oracle' = 'role' . 'only' 'oracleRole' -- @ oracle :: Traversal' Player () oracle = role . only oracleRole -- | The traversal of 'Player's with an 'orphanRole'. -- -- @ -- 'orphan' = 'role' . 'only' 'orphanRole' -- @ orphan :: Traversal' Player () orphan = role . only orphanRole -- | The traversal of 'Player's with a 'protectorRole'. -- -- @ -- 'protector' = 'role' . 'only' 'protectorRole' -- @ protector :: Traversal' Player () protector = role . only protectorRole -- | The traversal of 'Player's with a 'saintRole'. -- -- @ -- 'saint' = 'role' . 'only' 'saintRole' -- @ saint :: Traversal' Player () saint = role . only saintRole -- | The traversal of 'Player's with a 'scapegoatRole'. -- -- @ -- 'scapegoat' = 'role' . 'only' 'scapegoatRole' -- @ scapegoat :: Traversal' Player () scapegoat = role . only scapegoatRole -- | The traversal of 'Player's with a 'seerRole'. -- -- @ -- 'seer' = 'role' . 'only' 'seerRole' -- @ seer :: Traversal' Player () seer = role . only seerRole -- | The traversal of 'Player's with a 'simpleVillagerRole'. -- -- @ -- 'simpleVillager' = 'role' . 'only' 'simpleVillagerRole' -- @ simpleVillager :: Traversal' Player () simpleVillager = role . only simpleVillagerRole -- | The traversal of 'Player's with a 'simpleWerewolfRole'. -- -- @ -- 'simpleWerewolf' = 'role' . 'only' 'simpleWerewolfRole' -- @ simpleWerewolf :: Traversal' Player () simpleWerewolf = role . only simpleWerewolfRole -- | The traversal of 'Player's with a 'spitefulVillagerRole'. -- -- @ -- 'spitefulVillager' = 'role' . 'only' 'spitefulVillagerRole' -- @ spitefulVillager :: Traversal' Player () spitefulVillager = role . only spitefulVillagerRole -- | The traversal of 'Player's with a 'trueVillagerRole'. -- -- @ -- 'trueVillager' = 'role' . 'only' 'trueVillagerRole' -- @ trueVillager :: Traversal' Player () trueVillager = role . only trueVillagerRole -- | The traversal of 'Player's with a 'villageDrunkRole'. -- -- @ -- 'villageDrunk' = 'role' . 'only' 'villageDrunkRole' -- @ villageDrunk :: Traversal' Player () villageDrunk = role . only villageDrunkRole -- | The traversal of 'Player's with a 'witchRole'. -- -- @ -- 'witch' = 'role' . 'only' 'witchRole' -- @ witch :: Traversal' Player () witch = role . only witchRole -- | The traversal of 'Player's with a 'zombieRole'. -- -- @ -- 'zombie' = 'role' . 'only' 'zombieRole' -- @ zombie :: Traversal' Player () zombie = role . only zombieRole -- | The traversal of 'Player's aligned with 'NoOne'. -- -- @ -- 'loner' = 'role' . 'allegiance' . '_NoOne' -- @ loner :: Traversal' Player () loner = role . allegiance . _NoOne -- | The traversal of 'Player's aligned with the 'Villagers'. -- -- @ -- 'villager' = 'role' . 'allegiance' . '_Villagers' -- @ villager :: Traversal' Player () villager = role . allegiance . _Villagers -- | The traversal of 'Player's aligned with the 'Werewolves'. -- -- @ -- 'werewolf' = 'role' . 'allegiance' . '_Werewolves' -- @ werewolf :: Traversal' Player () werewolf = role . allegiance . _Werewolves -- | The traversal of 'Player' names. -- -- @ -- 'names' = 'traverse' . 'name' -- @ names :: Traversable t => Traversal' (t Player) Text names = traverse . name -- | The traversal of 'Player' roles. -- -- @ -- 'roles' = 'traverse' . 'role' -- @ roles :: Traversable t => Traversal' (t Player) Role roles = traverse . role -- | The traversal of 'Player' states. -- -- @ -- 'states' = 'traverse' . 'state' -- @ states :: Traversable t => Traversal' (t Player) State states = traverse . state -- | The traversal of 'Player's with the given name. -- -- @ -- 'named' name' = 'filteredBy' . 'name' name' -- @ named :: Text -> Traversal' Player Player named name' = filteredBy name name' -- | The traversal of 'alphaWolf' 'Player's. -- -- @ -- 'alphaWolves' = 'traverse' . 'filtered' ('is' 'alphaWolf') -- @ alphaWolves :: Traversable t => Traversal' (t Player) Player alphaWolves = traverse . filtered (is alphaWolf) -- | The traversal of 'beholder' 'Player's. -- -- @ -- 'beholders' = 'traverse' . 'filtered' ('is' 'beholder') -- @ beholders :: Traversable t => Traversal' (t Player) Player beholders = traverse . filtered (is beholder) -- | The traversal of 'crookedSenator' 'Player's. -- -- @ -- 'crookedSenators' = 'traverse' . 'filtered' ('is' 'crookedSenator') -- @ crookedSenators :: Traversable t => Traversal' (t Player) Player crookedSenators = traverse . filtered (is crookedSenator) -- | The traversal of 'dullahan' 'Player's. -- -- @ -- 'dullahans' = 'traverse' . 'filtered' ('is' 'dullahan') -- @ dullahans :: Traversable t => Traversal' (t Player) Player dullahans = traverse . filtered (is dullahan) -- | The traversal of 'druid' 'Player's. -- -- @ -- 'druids' = 'traverse' . 'filtered' ('is' 'druid') -- @ druids :: Traversable t => Traversal' (t Player) Player druids = traverse . filtered (is druid) -- | The traversal of 'fallenAngel' 'Player's. -- -- @ -- 'fallenAngels' = 'traverse' . 'filtered' ('is' 'fallenAngel') -- @ fallenAngels :: Traversable t => Traversal' (t Player) Player fallenAngels = traverse . filtered (is fallenAngel) -- | The traversal of 'hunter' 'Player's. -- -- @ -- 'hunters' = 'traverse' . 'filtered' ('is' 'hunter') -- @ hunters :: Traversable t => Traversal' (t Player) Player hunters = traverse . filtered (is hunter) -- | The traversal of 'jester' 'Player's. -- -- @ -- 'jesters' = 'traverse' . 'filtered' ('is' 'jester') -- @ jesters :: Traversable t => Traversal' (t Player) Player jesters = traverse . filtered (is jester) -- | The traversal of 'lycan' 'Player's. -- -- @ -- 'lycans' = 'traverse' . 'filtered' ('is' 'lycan') -- @ lycans :: Traversable t => Traversal' (t Player) Player lycans = traverse . filtered (is lycan) -- | The traversal of 'medusa' 'Player's. -- -- @ -- 'medusas' = 'traverse' . 'filtered' ('is' 'medusa') -- @ medusas :: Traversable t => Traversal' (t Player) Player medusas = traverse . filtered (is medusa) -- | The traversal of 'necromancer' 'Player's. -- -- @ -- 'necromancers' = 'traverse' . 'filtered' ('is' 'necromancer') -- @ necromancers :: Traversable t => Traversal' (t Player) Player necromancers = traverse . filtered (is necromancer) -- | The traversal of 'oracle' 'Player's. -- -- @ -- 'oracles' = 'traverse' . 'filtered' ('is' 'oracle') -- @ oracles :: Traversable t => Traversal' (t Player) Player oracles = traverse . filtered (is oracle) -- | The traversal of 'orphan' 'Player's. -- -- @ -- 'orphans' = 'traverse' . 'filtered' ('is' 'orphan') -- @ orphans :: Traversable t => Traversal' (t Player) Player orphans = traverse . filtered (is orphan) -- | The traversal of 'protector' 'Player's. -- -- @ -- 'protectors' = 'traverse' . 'filtered' ('is' 'protector') -- @ protectors :: Traversable t => Traversal' (t Player) Player protectors = traverse . filtered (is protector) -- | The traversal of 'saint' 'Player's. -- -- @ -- 'saints' = 'traverse' . 'filtered' ('is' 'saint') -- @ saints :: Traversable t => Traversal' (t Player) Player saints = traverse . filtered (is saint) -- | The traversal of 'scapegoat' 'Player's. -- -- @ -- 'scapegoats' = 'traverse' . 'filtered' ('is' 'scapegoat') -- @ scapegoats :: Traversable t => Traversal' (t Player) Player scapegoats = traverse . filtered (is scapegoat) -- | The traversal of 'seer' 'Player's. -- -- @ -- 'seers' = 'traverse' . 'filtered' ('is' 'seer') -- @ seers :: Traversable t => Traversal' (t Player) Player seers = traverse . filtered (is seer) -- | The traversal of 'simpleVillager' 'Player's. -- -- @ -- 'simpleVillagers' = 'traverse' . 'filtered' ('is' 'simpleVillager') -- @ simpleVillagers :: Traversable t => Traversal' (t Player) Player simpleVillagers = traverse . filtered (is simpleVillager) -- | The traversal of 'simpleWerewolf' 'Player's. -- -- @ -- 'simpleWerewolves' = 'traverse' . 'filtered' ('is' 'simpleWerewolf') -- @ simpleWerewolves :: Traversable t => Traversal' (t Player) Player simpleWerewolves = traverse . filtered (is simpleWerewolf) -- | The traversal of 'spitefulVillager' 'Player's. -- -- @ -- 'spitefulVillagers' = 'traverse' . 'filtered' ('is' 'spitefulVillager') -- @ spitefulVillagers :: Traversable t => Traversal' (t Player) Player spitefulVillagers = traverse . filtered (is spitefulVillager) -- | The traversal of 'trueVillager' 'Player's. -- -- @ -- 'trueVillagers' = 'traverse' . 'filtered' ('is' 'trueVillager') -- @ trueVillagers :: Traversable t => Traversal' (t Player) Player trueVillagers = traverse . filtered (is trueVillager) -- | The traversal of 'villageDrunk' 'Player's. -- -- @ -- 'villageDrunks' = 'traverse' . 'filtered' ('is' 'villageDrunk') -- @ villageDrunks :: Traversable t => Traversal' (t Player) Player villageDrunks = traverse . filtered (is villageDrunk) -- | The traversal of 'witch' 'Player's. -- -- @ -- 'witches' = 'traverse' . 'filtered' ('is' 'witch') -- @ witches :: Traversable t => Traversal' (t Player) Player witches = traverse . filtered (is witch) -- | The traversal of 'zombie' 'Player's. -- -- @ -- 'zombies' = 'traverse' . 'filtered' ('is' 'zombie') -- @ zombies :: Traversable t => Traversal' (t Player) Player zombies = traverse . filtered (is zombie) -- | The traversal of 'loner' 'Player's. -- -- @ -- 'loners' = 'traverse' . 'filtered' . ('is' 'loner') -- @ loners :: Traversable t => Traversal' (t Player) Player loners = traverse . filtered (is loner) -- | The traversal of 'villager' 'Player's. -- -- @ -- 'villagers' = 'traverse' . 'filtered' ('is' 'villager') -- @ villagers :: Traversable t => Traversal' (t Player) Player villagers = traverse . filtered (is villager) -- | The traversal of 'werewolf' 'Player's. -- -- @ -- 'werewolves' = 'traverse' . 'filtered' ('is' 'werewolf') -- @ werewolves :: Traversable t => Traversal' (t Player) Player werewolves = traverse . filtered (is werewolf) -- | The traversal of 'Alive' 'Player's. -- -- @ -- 'alive' = 'filtered' ('has' $ 'state' . '_Alive') -- @ alive :: Traversal' Player Player alive = filtered (has $ state . _Alive) -- | The traversal of 'Dead' 'Player's. -- -- @ -- 'dead' = 'filtered' ('has' $ 'state' . '_Dead') -- @ dead :: Traversal' Player Player dead = filtered (has $ state . _Dead) werewolf-1.5.1.1/src/Game/Werewolf/Response.hs0000644000000000000000000000716612716050452017275 0ustar0000000000000000{-| Module : Game.Werewolf.Response Description : Response and message data structures. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A response is used as a return result of calling the @werewolf@ binary. Each response has a list of associated messages. @werewolf@ was designed to be ambivalent to the playing chat client. The response-message structure reflects this by staying away from anything that could be construed as client-specific. This includes features such as emoji support. -} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} module Game.Werewolf.Response ( -- * Response Response(..), -- ** Common responses success, failure, -- ** Exit functions exitWith, -- * Message Message(..), publicMessage, privateMessage, groupMessages, ) where import Control.Monad.IO.Class import Data.Aeson #if !MIN_VERSION_aeson(0,10,0) import Data.Aeson.Types #endif import Data.Text (Text) import qualified Data.Text.Lazy.Encoding as T import qualified Data.Text.Lazy.IO as T import GHC.Generics import qualified System.Exit as Exit -- | When a user sends a command to the @werewolf@ binary, a response is always returned. -- -- The chat interface should then relay any @messages@ from the response. Whether or not the -- command was valid (indicated by the @ok@ flag) is often irrelevant as the returned @messages@ -- will include errors to the user. data Response = Response { ok :: Bool -- ^ Boolean flag to indicate success. , messages :: [Message] -- ^ List of messages. } deriving (Eq, Generic, Show) instance FromJSON Response instance ToJSON Response where toJSON = genericToJSON defaultOptions #if MIN_VERSION_aeson(0,10,0) toEncoding = genericToEncoding defaultOptions #endif -- | A successful, empty response. success :: Response success = Response True [] -- | An unsuccessful, empty response. failure :: Response failure = Response False [] -- | Exits fast with the given response. The response is encoded as JSON, printed to @stdout@ and -- then the program is exited with @0@ (success). -- -- The program always exits with success even if the response was a failure one. This is to -- distinguish between bad calls to the binary and bad commands to the werewolf engine. exitWith :: MonadIO m => Response -> m a exitWith response = liftIO $ T.putStrLn (T.decodeUtf8 $ encode response) >> Exit.exitSuccess -- | A message may be either public or private, indicated by its @to@ field. -- -- Each message contains a single text field. This field is permitted to contain special -- characters such as new lines and tabs. data Message = Message { to :: Maybe Text -- ^ The message recipient: 'Nothing' for a public message, -- 'Just' for a private message. , message :: Text -- ^ The message text. } deriving (Eq, Generic, Show) instance FromJSON Message instance ToJSON Message where toJSON = genericToJSON defaultOptions #if MIN_VERSION_aeson(0,10,0) toEncoding = genericToEncoding defaultOptions #endif -- | Creates a public message with the given text. publicMessage :: Text -> Message publicMessage = Message Nothing -- | @privateMessage to message@ -- -- Creates a private message to @to@ with the given text. privateMessage :: Text -> Text -> Message privateMessage to = Message (Just to) -- | @groupMessages tos message@ -- -- Creates multiple private messages (1 to each recipient) with the given text. groupMessages :: [Text] -> Text -> [Message] groupMessages tos message = map (`privateMessage` message) tos werewolf-1.5.1.1/src/Game/Werewolf/Role.hs0000644000000000000000000006362312751254103016376 0ustar0000000000000000{-| Module : Game.Werewolf.Role Description : Simplistic role data structure with lenses 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 QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} module Game.Werewolf.Role ( -- * Role Role, tag, name, allegiance, balance, activity, description, rules, Allegiance(..), _NoOne, _Necromancer, _Villagers, _Werewolves, Activity(..), _Diurnal, _Nocturnal, -- ** 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. orphanRole, villageDrunkRole, -- *** The Loners -- | The Loners look out for themselves and themselves alone. -- The Loners must complete their own objective. dullahanRole, fallenAngelRole, necromancerRole, zombieRole, -- *** 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. beholderRole, crookedSenatorRole, druidRole, hunterRole, jesterRole, lycanRole, medusaRole, oracleRole, protectorRole, saintRole, scapegoatRole, seerRole, simpleVillagerRole, spitefulVillagerRole, trueVillagerRole, witchRole, -- *** The Werewolves -- | Hiding in plain sight, the Werewolves are not a small trifle. -- The Werewolves must devour all of the Villagers. alphaWolfRole, simpleWerewolfRole, ) where import Control.Lens import Data.Function import Data.List import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text as T -- | Role definitions require only a few pieces of information. -- -- 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 'tag' as a role's 'allegiance' may change throughout -- the game. data Role = Role { _tag :: Text , _name :: Text , _allegiance :: Allegiance , _balance :: Int , _activity :: Activity , _description :: Text , _rules :: Text } deriving (Read, Show) -- | The 'NoOne' allegiance is used for the Loners. It is not used to determine who has won (i.e., -- if one Loner wins, the others still lose). data Allegiance = NoOne | Necromancer | Villagers | Werewolves deriving (Eq, Read, Show) instance Humanise Allegiance where humanise NoOne = "no-one" humanise Necromancer = "Necromancer" humanise Villagers = "Villagers" humanise Werewolves = "Werewolves" -- | Defines whether a role is diurnal or nocturnal. I.e., if the role's turn occurs during the day -- or night. data Activity = Diurnal | Nocturnal deriving (Eq, Read, Show) instance Humanise Activity where humanise Diurnal = "diurnal" humanise Nocturnal = "nocturnal" makePrisms ''Allegiance makePrisms ''Activity makeLenses ''Role instance Eq Role where (==) = (==) `on` view tag instance Humanise Role where humanise = view name -- | A list containing all the roles defined in this file. allRoles :: [Role] allRoles = [ alphaWolfRole , beholderRole , crookedSenatorRole , druidRole , dullahanRole , fallenAngelRole , hunterRole , jesterRole , lycanRole , medusaRole , necromancerRole , oracleRole , orphanRole , protectorRole , saintRole , scapegoatRole , seerRole , simpleVillagerRole , simpleWerewolfRole , spitefulVillagerRole , trueVillagerRole , villageDrunkRole , witchRole , zombieRole ] -- | A list containing roles that are restricted to a single instance per 'Game'. -- -- @ -- 'restrictedRoles' = 'allRoles' \\\\ ['simpleVillagerRole', 'simpleWerewolfRole', 'spitefulVillagerRole', 'zombieRole'] -- @ restrictedRoles :: [Role] restrictedRoles = allRoles \\ [simpleVillagerRole, simpleWerewolfRole, spitefulVillagerRole, zombieRole] -- | /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 looked twice at the Orphan and even/ -- /fewer showed kindness towards the lonely child. One day however, one townsperson changed all/ -- /this. He offered the Orphan food, water and a roof over their head. Grateful for his chairty/ -- /and affection, the Orphan made him their role model. Pray that no ill should befall their/ -- /role model, for they are the only one conforming the Orphan as a Villager./ -- -- On the first night, the Orphan chooses a player to become their role model. So long as the role -- model is alive, the Orphan is a Villager. If however the role model is eliminated, then the -- Orphan becomes a Werewolf. orphanRole :: Role orphanRole = Role { _tag = "orphan" , _name = T.strip [iFile|variant/standard/role/orphan/name.txt|] , _allegiance = Villagers , _balance = -3 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/orphan/description.txt|] , _rules = T.strip [iFile|variant/standard/role/orphan/rules.txt|] } -- | /Hah, maybe not as liked as the Jester, but the Drunk sure does their fair share of stupid/ -- /things in the night! No-one knows if they even actually make it home; sometimes people see/ -- /them sleeping outside the Blacksmith's home, others say they see them wandering towards the/ -- /woods. It's pointless quizzing the Village Drunk in the morning about their doings; they can/ -- /never remember what they did!/ -- -- The Village Drunk is initially aligned with the Villagers. -- -- On the third night the Village Drunk sobers up and is randomly assigned a new alignment, either -- Villagers or Werewolves. villageDrunkRole :: Role villageDrunkRole = Role { _tag = "village-drunk" , _name = T.strip [iFile|variant/standard/role/village-drunk/name.txt|] , _allegiance = Villagers , _balance = -3 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/village-drunk/description.txt|] , _rules = T.strip [iFile|variant/standard/role/village-drunk/rules.txt|] } -- | /Normally the Dullahan carries their head under one arm, however while amongst the Villagers,/ -- /they ere on the side of caution and rest it in a more traditional place. The Dullahan rides a/ -- /black horse as dark as night and hunts down travellers in the countryside. Beware if the/ -- /Dullahan knows your name, for you are then marked for death and you should avoid them at all/ -- /costs. -- -- The Dullahan is given a list of player names at the start of the game. To win, they must -- eliminate all of them before the end of the game. dullahanRole :: Role dullahanRole = Role { _tag = "dullahan" , _name = T.strip [iFile|variant/standard/role/dullahan/name.txt|] , _allegiance = NoOne , _balance = 0 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/dullahan/description.txt|] , _rules = T.strip [iFile|variant/standard/role/dullahan/rules.txt|] } -- | /Long ago during the War in Heaven, angels fell from the sky as one by one those that followed/ -- /Lucifer were defeated. For centuries they lived amongst mortal Villagers as punishment for/ -- /their sins and wrongdoings. The Fallen Angel was one such being and is now one of the few/ -- /angels left on Earth. Nothing is worse punishment for them, the Fallen Angel yearns for death/ -- /to once again be free!/ -- -- The Fallen Angel wins if they manage to get lynched by the Villagers before the end of the -- game. fallenAngelRole :: Role fallenAngelRole = Role { _tag = "fallen-angel" , _name = T.strip [iFile|variant/standard/role/fallen-angel/name.txt|] , _allegiance = NoOne , _balance = 0 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/fallen-angel/description.txt|] , _rules = T.strip [iFile|variant/standard/role/fallen-angel/rules.txt|] } -- | /The dead are feared among the living; the dead outnumber the living. In most villages the/ -- /dead remain that way, but not when the Necromancer is present. The Necromancer devoted their/ -- /life to learning black magic and methods of bringing people back to life. Unfortunately this/ -- /art is hard to perfect and all they can manage to bring back are soulless Zombies./ -- -- Once per game the Necromancer can choose to resurrect all dead players as Zombies. The Zombies -- are aligned with the Necromancer and they cannot be lynched or devoured. -- -- If the Necromancer is killed, all Zombies die with them. -- -- The Necromancer and Zombies win if they are the last ones alive. necromancerRole :: Role necromancerRole = Role { _tag = "necromancer" , _name = T.strip [iFile|variant/standard/role/necromancer/name.txt|] , _allegiance = NoOne , _balance = 0 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/necromancer/description.txt|] , _rules = T.strip [iFile|variant/standard/role/necromancer/rules.txt|] } -- | /A loyal follower of the Necromancer. A Zombie has no mind of its own and blindly obeys every/ -- /command of their master./ -- -- A Zombie wins with the Necromancer. They cannot be killed, however they die when the -- Necromancer dies. zombieRole :: Role zombieRole = Role { _tag = "zombie" , _name = T.strip [iFile|variant/standard/role/zombie/name.txt|] , _allegiance = Necromancer , _balance = 0 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/zombie/description.txt|] , _rules = T.strip [iFile|variant/standard/role/zombie/rules.txt|] } -- | /Awareness comes easy to the Beholder. They listen to their senses and trust their hunches./ -- /Over the years the Beholder has grown to know a certain few of the village just by paying/ -- /attention. Little cues here and there, the way someone talks, the way they move - it all/ -- /gives clues as to their true nature and role./ -- -- At the start of the game the Beholder is informed the Seer's identity. beholderRole :: Role beholderRole = Role { _tag = "beholder" , _name = T.strip [iFile|variant/standard/role/beholder/name.txt|] , _allegiance = Villagers , _balance = 2 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/beholder/description.txt|] , _rules = T.strip [iFile|variant/standard/role/beholder/rules.txt|] } -- | /Never trust a politician. Nor a Crooked Senator for that matter. The Crooked Senator may seem/ -- /like he has the village's best interests at heart, but let's be honest, when put in a tough/ -- /situation he looks after no-one but himself. Even when safe, the Crooked Senator may decide/ -- /to toy with the Villagers' emotions and try pit them against one another./ -- -- The Crooked Senator looks at the village votes as they come in. crookedSenatorRole :: Role crookedSenatorRole = Role { _tag = "crooked-senator" , _name = T.strip [iFile|variant/standard/role/crooked-senator/name.txt|] , _allegiance = Villagers , _balance = 2 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/crooked-senator/description.txt|] , _rules = T.strip [iFile|variant/standard/role/crooked-senator/rules.txt|] } -- | /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 her warning for she 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 in the player `circle` then Ferina will grunt in warning. druidRole :: Role druidRole = Role { _tag = "druid" , _name = T.strip [iFile|variant/standard/role/druid/name.txt|] , _allegiance = Villagers , _balance = 5 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/druid/description.txt|] , _rules = T.strip [iFile|variant/standard/role/druid/rules.txt|] } -- | /A skilled marksman with quick reflexes. In the unfortunate situation that they are jumped and/ -- /killed unjustly, they let off a shot at their attacker, killing them instantly. The Hunter/ -- /never misses./ -- -- If the Hunter is killed they choose one player, believed to be an attacker, to kill -- immediately. hunterRole :: Role hunterRole = Role { _tag = "hunter" , _name = T.strip [iFile|variant/standard/role/hunter/name.txt|] , _allegiance = Villagers , _balance = 3 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/hunter/description.txt|] , _rules = T.strip [iFile|variant/standard/role/hunter/rules.txt|] } -- | /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 wants to hurt/ -- /them./ -- -- If the village votes to lynch the Jester, their identity is revealed. The village realise -- there's no point in burning them and so they are set free. -- -- The Jester continues to play but may no longer vote as no-one can take them seriously. jesterRole :: Role jesterRole = Role { _tag = "jester" , _name = T.strip [iFile|variant/standard/role/jester/name.txt|] , _allegiance = Villagers , _balance = 1 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/jester/description.txt|] , _rules = T.strip [iFile|variant/standard/role/jester/rules.txt|] } -- | /Traditionally a Werewolf once transformed loses all memories and personality. Over years of/ -- /transforming, the Lycan has slowly evolved and learnt how to retain themself. Night after/ -- /night of devouring with the other Werewolves took its toll. The screams alone were enough to/ -- /turn the Lycan and make them question their true nature./ -- -- The Lycan is aligned with the Villagers, but appears to nature-seeing roles (e.g., the Seer) as -- a Werewolf. lycanRole :: Role lycanRole = Role { _tag = "lycan" , _name = T.strip [iFile|variant/standard/role/lycan/name.txt|] , _allegiance = Villagers , _balance = -1 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/lycan/description.txt|] , _rules = T.strip [iFile|variant/standard/role/lycan/rules.txt|] } -- | /A beautiful flirt, the Medusa is aligned with the Villagers but harbours a terrifying secret./ -- /During the day they are well known in the village of Fougères for their stunning appearance/ -- /which captures the eye and love of all the townsfolk. However when their secret takes ahold/ -- /at sundown, their true self is revealed. Any who gaze upon her true form would see live/ -- /snakes for hair and the few that further look into her eyes are turned to stone./ -- -- If Medusa attracts the attention of a Werewolf during the night and is devoured, the first -- Werewolf to their left in the player `circle` will catch their gaze and turn to stone, -- instantly killing the lupine predator. medusaRole :: Role medusaRole = Role { _tag = "medusa" , _name = T.strip [iFile|variant/standard/role/medusa/name.txt|] , _allegiance = Villagers , _balance = 4 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/medusa/description.txt|] , _rules = T.strip [iFile|variant/standard/role/medusa/rules.txt|] } -- | /Originally rejected by the townsfolk, the Oracle's prophetic divinations has earned trust/ -- /within the village. With constant precognition - and concern for the future - the Oracle/ -- /knows the village will only live if they work together./ -- -- Each night the Oracle chooses a player to divine. They are then informed of the player's role -- the following morning. This wisdom is for the Oracle to use to ensure the future of Fougères. oracleRole :: Role oracleRole = Role { _tag = "oracle" , _name = T.strip [iFile|variant/standard/role/oracle/name.txt|] , _allegiance = Villagers , _balance = 4 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/oracle/description.txt|] , _rules = T.strip [iFile|variant/standard/role/oracle/rules.txt|] } -- | /The Protector is one of the few pure of heart and altruistic Villagers; they are forever/ -- /putting others needs above their own. Each night they fight against the Werewolves with/ -- /naught but a sword and shield, potentially saving an innocents life./ -- -- Each night the Protector chooses a player deemed worthy of their protection. That player is -- safe for that 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 { _tag = "protector" , _name = T.strip [iFile|variant/standard/role/protector/name.txt|] , _allegiance = Villagers , _balance = 3 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/protector/description.txt|] , _rules = T.strip [iFile|variant/standard/role/protector/rules.txt|] } -- | /The Saint, also historically known as a hallow, is recognized as having an exceptional degree of/ -- /holiness and likeness to God. They are a humble Villager and shine light on these dark times./ -- /Extinguishing this light would not be wise/ -- -- If the Saint is lynched by the village, all who voted for them die. saintRole :: Role saintRole = Role { _tag = "saint" , _name = T.strip [iFile|variant/standard/role/saint/name.txt|] , _allegiance = Villagers , _balance = 3 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/saint/description.txt|] , _rules = T.strip [iFile|variant/standard/role/saint/rules.txt|] } -- | /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: they must choose whom is permitted -- to vote or not on the next day. scapegoatRole :: Role scapegoatRole = Role { _tag = "scapegoat" , _name = T.strip [iFile|variant/standard/role/scapegoat/name.txt|] , _allegiance = Villagers , _balance = 1 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/scapegoat/description.txt|] , _rules = T.strip [iFile|variant/standard/role/scapegoat/rules.txt|] } -- | /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! The visions will always be/ -- /true, but only for the present as not even the Seer knows what the future holds./ -- -- Each night the Seer sees the allegiance of one player of their choice. seerRole :: Role seerRole = Role { _tag = "seer" , _name = T.strip [iFile|variant/standard/role/seer/name.txt|] , _allegiance = Villagers , _balance = 4 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/seer/description.txt|] , _rules = T.strip [iFile|variant/standard/role/seer/rules.txt|] } -- | /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 all/ -- /the Villagers in this unfortunate time./ -- -- The 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 { _tag = "simple-villager" , _name = T.strip [iFile|variant/standard/role/simple-villager/name.txt|] , _allegiance = Villagers , _balance = 1 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/simple-villager/description.txt|] , _rules = T.strip [iFile|variant/standard/role/simple-villager/rules.txt|] } -- | /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 all/ -- /the Villagers in this unfortunate time./ -- -- /Yet the Spiteful Villager has no loyalty in the afterlife; whoever causes them harm may find/ -- /themselves in trouble./ -- -- When the Spiteful Villager is killed, they are informed of everyone's roles and may haunt the -- village as they wish. spitefulVillagerRole :: Role spitefulVillagerRole = Role { _tag = "spiteful-villager" , _name = T.strip [iFile|variant/standard/role/spiteful-villager/name.txt|] , _allegiance = Villagers , _balance = 1 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/spiteful-villager/description.txt|] , _rules = T.strip [iFile|variant/standard/role/spiteful-villager/rules.txt|] } -- | /The True Villager has a heart and soul as clear as day! Their allegiance and devotion to the/ -- /village are beyond reproach. If there is one person whom you should confide in, listen to and/ -- /trust, it is the True Villager./ -- -- At the start of the game the True Villager's identity is revealed. trueVillagerRole :: Role trueVillagerRole = Role { _tag = "true-villager" , _name = T.strip [iFile|variant/standard/role/true-villager/name.txt|] , _allegiance = Villagers , _balance = 2 , _activity = Diurnal , _description = T.strip [iFile|variant/standard/role/true-villager/description.txt|] , _rules = T.strip [iFile|variant/standard/role/true-villager/rules.txt|] } -- | /Somehow forgotten with the coming of the Werewolves, the Witch has a chance to prove themself/ -- /valuable to the village and maybe abolish the absurd pastime of burning and drowning their/ -- /cult. The Witch is blessed (or maybe cursed) with the ability to make two powerful potions;/ -- /one of which heals a victim of the Werewolves, the other poisons a player./ -- -- 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 on healing themself. witchRole :: Role witchRole = Role { _tag = "witch" , _name = T.strip [iFile|variant/standard/role/witch/name.txt|] , _allegiance = Villagers , _balance = 3 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/witch/description.txt|] , _rules = T.strip [iFile|variant/standard/role/witch/rules.txt|] } -- | /The Alpha Wolf leads the Werewolves in the raids against Fougères each night and not even the/ -- /Seer can see them coming. If the Werewolves caused the Villagers to question and accuse one/ -- /another beforehand, the Alpha Wolf eliminates any shred of humanity left. No-one can be/ -- /trusted anymore and no-one knows the truth./ -- -- The Alpha Wolf appears to nature-seeing roles (e.g., the Seer) as a Villager. alphaWolfRole :: Role alphaWolfRole = Role { _tag = "alpha-wolf" , _name = T.strip [iFile|variant/standard/role/alpha-wolf/name.txt|] , _allegiance = Werewolves , _balance = -7 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/alpha-wolf/description.txt|] , _rules = T.strip [iFile|variant/standard/role/alpha-wolf/rules.txt|] } -- | /The Simple Werewolf is a fearsome lupine, cunning like no other creature that roams the/ -- /forest. Their origin is unknown, but that matters little, for they present a grave threat to/ -- /Fougères. While each day they hide in plain sight as an ordinary Villager, each night they/ -- /transform and devour an innocent. There is little hope left for the village./ -- -- A Werewolf may never devour another Werewolf. simpleWerewolfRole :: Role simpleWerewolfRole = Role { _tag = "simple-werewolf" , _name = T.strip [iFile|variant/standard/role/simple-werewolf/name.txt|] , _allegiance = Werewolves , _balance = -5 , _activity = Nocturnal , _description = T.strip [iFile|variant/standard/role/simple-werewolf/description.txt|] , _rules = T.strip [iFile|variant/standard/role/simple-werewolf/rules.txt|] } werewolf-1.5.1.1/src/Game/Werewolf/Variant.hs0000644000000000000000000000746412755166155017117 0ustar0000000000000000{-| Module : Game.Werewolf.Variant Description : Simplistic variant data structure with lenses and instances. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Variants alter how a game plays out. Either by changing the messages returned, or by changing the game logic. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} module Game.Werewolf.Variant ( -- * Variant Variant, tag, name, description, -- ** Instances allVariants, standardVariant, noRoleKnowledgeVariant, noRoleKnowledgeOrRevealVariant, noRoleRevealVariant, spitefulVillageVariant, -- ** Traversals standard, noRoleKnowledge, noRoleKnowledgeOrReveal, noRoleReveal, spitefulVillage, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text as T import Game.Werewolf.Role hiding (description, name, tag) -- | Variant definitions require only a few pieces of information. data Variant = Variant { _tag :: Text , _name :: Text , _description :: Text } deriving (Eq, Read, Show) makeLenses ''Variant instance Humanise Variant where humanise = view name -- | A list containing all the variants defined in this file. allVariants :: [Variant] allVariants = [ standardVariant , noRoleKnowledgeVariant , noRoleKnowledgeOrRevealVariant , noRoleRevealVariant , spitefulVillageVariant ] standardVariant :: Variant standardVariant = Variant { _tag = "standard" , _name = T.strip [iFile|variant/standard/name.txt|] , _description = T.strip [iFile|variant/standard/description.txt|] } noRoleKnowledgeVariant :: Variant noRoleKnowledgeVariant = Variant { _tag = "no-role-knowledge" , _name = T.strip [iFile|variant/no-role-knowledge/name.txt|] , _description = T.strip [iFile|variant/no-role-knowledge/description.txt|] } noRoleKnowledgeOrRevealVariant :: Variant noRoleKnowledgeOrRevealVariant = Variant { _tag = "no-role-knowledge-or-reveal" , _name = T.strip [iFile|variant/no-role-knowledge-or-reveal/name.txt|] , _description = T.strip [iFile|variant/no-role-knowledge-or-reveal/description.txt|] } noRoleRevealVariant :: Variant noRoleRevealVariant = Variant { _tag = "no-role-reveal" , _name = T.strip [iFile|variant/no-role-reveal/name.txt|] , _description = T.strip [iFile|variant/no-role-reveal/description.txt|] } spitefulVillageVariant :: Variant spitefulVillageVariant = Variant { _tag = "spiteful-village" , _name = T.strip [iFile|variant/spiteful-village/name.txt|] , _description = T.strip [iFile|variant/spiteful-village/description.txt|] } -- | The traversal of 'standard' 'Variant's. -- -- @ -- 'standard' = 'only' 'standardVariant' -- @ standard :: Traversal' Variant () standard = only standardVariant -- | The traversal of 'noRoleKnowledge' 'Variant's. -- -- @ -- 'noRoleKnowledge' = 'only' 'noRoleKnowledgeVariant' -- @ noRoleKnowledge :: Traversal' Variant () noRoleKnowledge = only noRoleKnowledgeVariant -- | The traversal of 'noRoleKnowledgeOrReveal' 'Variant's. -- -- @ -- 'noRoleKnowledgeOrReveal' = 'only' 'noRoleKnowledgeOrRevealVariant' -- @ noRoleKnowledgeOrReveal :: Traversal' Variant () noRoleKnowledgeOrReveal = only noRoleKnowledgeOrRevealVariant -- | The traversal of 'noRoleReveal' 'Variant's. -- -- @ -- 'noRoleReveal' = 'only' 'noRoleRevealVariant' -- @ noRoleReveal :: Traversal' Variant () noRoleReveal = only noRoleRevealVariant -- | The traversal of 'spitefulVillage' 'Variant's. -- -- @ -- 'spitefulVillage' = 'only' 'spitefulVillageVariant' -- @ spitefulVillage :: Traversal' Variant () spitefulVillage = only spitefulVillageVariant werewolf-1.5.1.1/app/Main.hs0000644000000000000000000000670512751254103013705 0ustar0000000000000000{-| Module : Main Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com -} {-# LANGUAGE OverloadedStrings #-} module Main ( -- * Main main, ) where import Data.Text (Text) import qualified Data.Text as T import Options.Applicative import System.Environment import qualified Werewolf.Command.Boot as Boot import qualified Werewolf.Command.Choose as Choose import qualified Werewolf.Command.Circle as Circle import qualified Werewolf.Command.Divine as Divine import qualified Werewolf.Command.End as End import qualified Werewolf.Command.Heal as Heal import qualified Werewolf.Command.Help as Help import qualified Werewolf.Command.Interpret as Interpret import qualified Werewolf.Command.Pass as Pass import qualified Werewolf.Command.Ping as Ping import qualified Werewolf.Command.Poison as Poison import qualified Werewolf.Command.Protect as Protect import qualified Werewolf.Command.Quit as Quit import qualified Werewolf.Command.Raise as Raise import qualified Werewolf.Command.See as See import qualified Werewolf.Command.Start as Start import qualified Werewolf.Command.Status as Status import qualified Werewolf.Command.Unvote as Unvote import qualified Werewolf.Command.Version as Version import qualified Werewolf.Command.Vote as Vote import Werewolf.Options main :: IO () main = getArgs >>= run run :: [String] -> IO () run args = handleParseResult (execParserPure werewolfPrefs werewolfInfo args) >>= handle interpret :: Text -> Text -> [Text] -> IO () interpret callerName tag args = do let result = execParserPure werewolfPrefs werewolfInfo (map T.unpack $ "--caller":callerName:"--tag":tag:args) case result of Success options -> handle options _ -> handle (Options callerName tag . Help . Help.Options . Just $ Help.Commands False) handle :: Options -> IO () handle (Options callerName tag command) = case command of Choose options -> Choose.handle callerName tag options Boot options -> Boot.handle callerName tag options Circle options -> Circle.handle callerName tag options Divine options -> Divine.handle callerName tag options End -> End.handle callerName tag Heal -> Heal.handle callerName tag Help options -> Help.handle callerName tag options Interpret (Interpret.Options args) -> interpret callerName tag args Pass -> Pass.handle callerName tag Ping -> Ping.handle callerName tag Poison options -> Poison.handle callerName tag options Protect options -> Protect.handle callerName tag options Quit -> Quit.handle callerName tag Raise -> Raise.handle callerName tag See options -> See.handle callerName tag options Start options -> Start.handle callerName tag options Status -> Status.handle callerName tag Unvote -> Unvote.handle callerName tag Version -> Version.handle callerName Vote options -> Vote.handle callerName tag options werewolf-1.5.1.1/app/Game/Werewolf/Command.hs0000644000000000000000000000261212716050452017035 0ustar0000000000000000{-| Module : Game.Werewolf.Command Description : Command data structure. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Command data structures. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE Rank2Types #-} module Game.Werewolf.Command ( -- * Command Command(..), -- ** Instances noopCommand, -- ** Validation validatePlayer, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util data Command = Command { apply :: forall m . (MonadError [Message] m, MonadState Game m, MonadWriter [Message] m) => m () } noopCommand :: Command noopCommand = Command $ return () validatePlayer :: (MonadError [Message] m, MonadState Game m) => Text -> Text -> m () validatePlayer callerName name' = do whenM isGameOver $ throwError [gameIsOverMessage callerName] unlessM (doesPlayerExist name') $ throwError [playerDoesNotExistMessage callerName name'] player <- findPlayerBy_ name name' when (is dead player) $ throwError [if callerName == name' then playerIsDeadMessage callerName else targetIsDeadMessage callerName player] werewolf-1.5.1.1/app/Game/Werewolf/Command/Global.hs0000644000000000000000000000261712727221700020240 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Global Description : Global commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Global commands. -} module Game.Werewolf.Command.Global ( -- * Commands bootCommand, quitCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import qualified Data.Map as Map import Data.Maybe import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util bootCommand :: Text -> Text -> Command bootCommand callerName targetName = Command $ do validatePlayer callerName callerName validatePlayer callerName targetName caller <- findPlayerBy_ name callerName target <- findPlayerBy_ name targetName whenM (uses (boots . at targetName) $ elem callerName . fromMaybe []) $ throwError [playerHasAlreadyVotedToBootMessage callerName target] boots %= Map.insertWith (++) targetName [callerName] tell [playerVotedToBootMessage caller target] quitCommand :: Text -> Command quitCommand callerName = Command $ do validatePlayer callerName callerName caller <- findPlayerBy_ name callerName tell . (:[]) . playerQuitMessage caller =<< get removePlayer callerName werewolf-1.5.1.1/app/Game/Werewolf/Command/Hunter.hs0000644000000000000000000000252512751254103020303 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Hunter Description : Hunter commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Hunter commands. -} module Game.Werewolf.Command.Hunter ( -- * Commands chooseCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util chooseCommand :: Text -> Text -> Command chooseCommand callerName targetName = Command $ do whenM isGameOver $ throwError [gameIsOverMessage callerName] unlessM (doesPlayerExist callerName) $ throwError [playerDoesNotExistMessage callerName callerName] unlessM (isPlayerHunter callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isHuntersTurn $ throwError [playerCannotDoThatRightNowMessage callerName] validatePlayer callerName targetName whenM (isPlayerZombie targetName) $ throwError [playerCannotChooseZombieMessage callerName] target <- findPlayerBy_ name targetName tell . (:[]) . playerShotMessage target =<< get killPlayer targetName hunterRetaliated .= True werewolf-1.5.1.1/app/Game/Werewolf/Command/Necromancer.hs0000644000000000000000000000264612751254103021276 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Necromancer Description : Seer commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Necromancer commands. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Command.Necromancer ( -- * Commands passCommand, raiseCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State hiding (state) import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util passCommand :: Text -> Command passCommand callerName = Command $ do validateCommand callerName passed .= True raiseCommand :: Text -> Command raiseCommand callerName = Command $ do validateCommand callerName players . traverse . dead . role .= zombieRole players . traverse . dead . state .= Alive tell . deadRaisedMessages =<< get deadRaised .= True validateCommand :: (MonadError [Message] m, MonadState Game m) => Text -> m () validateCommand callerName = do validatePlayer callerName callerName unlessM (isPlayerNecromancer callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isNecromancersTurn $ throwError [playerCannotDoThatRightNowMessage callerName] werewolf-1.5.1.1/app/Game/Werewolf/Command/Oracle.hs0000644000000000000000000000156412733127303020246 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Oracle Description : Oracle commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Seer commands. -} module Game.Werewolf.Command.Oracle ( -- * Commands divineCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util divineCommand :: Text -> Text -> Command divineCommand callerName targetName = Command $ do validatePlayer callerName callerName unlessM (isPlayerOracle callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isOraclesTurn $ throwError [playerCannotDoThatRightNowMessage callerName] validatePlayer callerName targetName divine .= Just targetName werewolf-1.5.1.1/app/Game/Werewolf/Command/Orphan.hs0000644000000000000000000000173112716050452020265 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Orphan Description : Orphan commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Orphan commands. -} module Game.Werewolf.Command.Orphan ( -- * Commands chooseCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util chooseCommand :: Text -> Text -> Command chooseCommand callerName targetName = Command $ do validatePlayer callerName callerName unlessM (isPlayerOrphan callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isOrphansTurn $ throwError [playerCannotDoThatRightNowMessage callerName] when (callerName == targetName) $ throwError [playerCannotChooseSelfMessage callerName] validatePlayer callerName targetName roleModel .= Just targetName werewolf-1.5.1.1/app/Game/Werewolf/Command/Protector.hs0000644000000000000000000000216512733127303021020 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Protector Description : Protector commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Protector commands. -} module Game.Werewolf.Command.Protector ( -- * Commands protectCommand, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util protectCommand :: Text -> Text -> Command protectCommand callerName targetName = Command $ do validatePlayer callerName callerName unlessM (isPlayerProtector callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isProtectorsTurn $ throwError [playerCannotDoThatRightNowMessage callerName] validatePlayer callerName targetName whenM (hasuse $ priorProtect . traverse . only targetName) $ throwError [playerCannotProtectSamePlayerTwiceInARowMessage callerName] priorProtect .= Just targetName protect .= Just targetName werewolf-1.5.1.1/app/Game/Werewolf/Command/Scapegoat.hs0000644000000000000000000000271612751254103020746 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Scapegoat Description : Scapegoat commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Scapegoat commands. -} module Game.Werewolf.Command.Scapegoat ( -- * Commands chooseCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util chooseCommand :: Text -> [Text] -> Command chooseCommand callerName targetNames = Command $ do whenM isGameOver $ throwError [gameIsOverMessage callerName] unlessM (doesPlayerExist callerName) $ throwError [playerDoesNotExistMessage callerName callerName] unlessM (isPlayerScapegoat callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isScapegoatsTurn $ throwError [playerCannotDoThatRightNowMessage callerName] when (null targetNames) $ throwError [playerMustChooseAtLeastOneTargetMessage callerName] when (callerName `elem` targetNames) $ throwError [playerCannotChooseSelfMessage callerName] forM_ targetNames $ validatePlayer callerName whenM (use jesterRevealed &&^ anyM isPlayerJester targetNames) $ throwError [playerCannotChooseJesterMessage callerName] chosenVoters .= targetNames scapegoatBlamed .= False werewolf-1.5.1.1/app/Game/Werewolf/Command/Seer.hs0000644000000000000000000000154212716050452017734 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Seer Description : Seer commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Seer commands. -} module Game.Werewolf.Command.Seer ( -- * Commands seeCommand, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util seeCommand :: Text -> Text -> Command seeCommand callerName targetName = Command $ do validatePlayer callerName callerName unlessM (isPlayerSeer callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isSeersTurn $ throwError [playerCannotDoThatRightNowMessage callerName] validatePlayer callerName targetName see .= Just targetName werewolf-1.5.1.1/app/Game/Werewolf/Command/Status.hs0000644000000000000000000000666112751254103020326 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Status Description : Status commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Status commands. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Command.Status ( -- * Commands circleCommand, pingCommand, statusCommand, ) where import Control.Lens import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) -- TODO (hjw): remove Message.Engine import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Engine import qualified Game.Werewolf.Role as Role import Game.Werewolf.Util circleCommand :: Text -> Bool -> Command circleCommand callerName includeDead = Command $ do players' <- toListOf (players . traverse . if includeDead then id else alive) <$> get tell [circleMessage callerName players'] pingCommand :: Text -> Command pingCommand callerName = Command $ use stage >>= \stage' -> case stage' of DruidsTurn -> return () GameOver -> tell [gameIsOverMessage callerName] HuntersTurn1 -> pingRole hunterRole HuntersTurn2 -> pingRole hunterRole Lynching -> return () NecromancersTurn -> pingRole necromancerRole OraclesTurn -> pingRole oracleRole OrphansTurn -> pingRole orphanRole ProtectorsTurn -> pingRole protectorRole ScapegoatsTurn -> pingRole scapegoatRole SeersTurn -> pingRole seerRole Sunrise -> return () Sunset -> return () VillageDrunksTurn -> pingRole villageDrunkRole VillagesTurn -> pingVillagers WerewolvesTurn -> pingWerewolves WitchsTurn -> pingRole witchRole pingRole :: (MonadState Game m, MonadWriter [Message] m) => Role -> m () pingRole role' = do player <- findPlayerBy_ role role' tell . (:[]) . pingRoleMessage =<< get tell [pingPlayerMessage $ player ^. name] where pingRoleMessage game | has (Role.activity . _Diurnal) role' = pingDiurnalRoleMessage role' | otherwise = pingNocturnalRoleMessage role' game pingVillagers :: (MonadState Game m, MonadWriter [Message] m) => m () pingVillagers = do pendingVoterNames <- toListOf (pendingVoters . name) <$> get tell [pingVillageMessage] tell $ map pingPlayerMessage pendingVoterNames pingWerewolves :: (MonadState Game m, MonadWriter [Message] m) => m () pingWerewolves = do pendingVoterNames <- toListOf (pendingVoters . name) <$> get tell . (:[]) . pingWerewolvesMessage =<< get tell $ map pingPlayerMessage pendingVoterNames statusCommand :: Text -> Command statusCommand callerName = Command $ do game <- get tell [ currentStageMessage game , rolesInGameMessage (Just callerName) game , playersInGameMessage callerName game ] whenM (isPlayerDullahan callerName) $ tell [marksInGameMessage callerName game] where currentStageMessage game | has (stage . _GameOver) game = gameIsOverMessage callerName | has (stage . activity . _Diurnal) game = currentDiurnalTurnMessage callerName game | otherwise = currentNocturnalTurnMessage callerName game werewolf-1.5.1.1/app/Game/Werewolf/Command/Villager.hs0000644000000000000000000000432712751254103020605 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Villager Description : Villager commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Villager commands. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Command.Villager ( -- * Commands unvoteCommand, voteCommand, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import qualified Data.Map as Map import Data.Maybe import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util unvoteCommand :: Text -> Command unvoteCommand callerName = Command $ do validateCommand callerName whenM (isNothing <$> getPlayerVote callerName) $ throwError [playerHasNotVotedMessage callerName] votes %= Map.delete callerName caller <- findPlayerBy_ name callerName whenJustM (preuse $ players . crookedSenators . alive) $ \crookedSenator -> tell [playerRescindedVoteMessage (crookedSenator ^. name) caller] voteCommand :: Text -> Text -> Command voteCommand callerName targetName = Command $ do validateCommand callerName whenM (isJust <$> getPlayerVote callerName) $ throwError [playerHasAlreadyVotedMessage callerName] validatePlayer callerName targetName whenM (isPlayerZombie targetName) $ throwError [playerCannotChooseZombieMessage callerName] votes %= Map.insert callerName targetName caller <- findPlayerBy_ name callerName target <- findPlayerBy_ name targetName whenJustM (preuse $ players . crookedSenators . alive) $ \crookedSenator -> tell [playerMadeLynchVoteMessage (Just $ crookedSenator ^. name) caller target] validateCommand :: (MonadError [Message] m, MonadState Game m) => Text -> m () validateCommand callerName = do validatePlayer callerName callerName whenM (hasn'tuse $ allowedVoters . named callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isVillagesTurn $ throwError [playerCannotDoThatRightNowMessage callerName] werewolf-1.5.1.1/app/Game/Werewolf/Command/Werewolf.hs0000644000000000000000000000473012751254103020630 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Werewolf Description : Werewolf commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Werewolf commands. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Command.Werewolf ( -- * Commands unvoteCommand, voteCommand, -- ** Validation validatePlayer, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.List import qualified Data.Map as Map import Data.Maybe import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util unvoteCommand :: Text -> Command unvoteCommand callerName = Command $ do validateCommand callerName whenM (isNothing <$> getPlayerVote callerName) $ throwError [playerHasNotVotedMessage callerName] votes %= Map.delete callerName aliveWerewolfNames <- toListOf (players . werewolves . alive . name) <$> get caller <- findPlayerBy_ name callerName tell [playerRescindedVoteMessage werewolfName caller | werewolfName <- aliveWerewolfNames \\ [callerName]] voteCommand :: Text -> Text -> Command voteCommand callerName targetName = Command $ do validateCommand callerName whenM (isJust <$> getPlayerVote callerName) $ throwError [playerHasAlreadyVotedMessage callerName] validatePlayer callerName targetName whenM (isPlayerWerewolf targetName) $ throwError [playerCannotDevourAnotherWerewolfMessage callerName] whenM (isPlayerZombie targetName) $ throwError [playerCannotChooseZombieMessage callerName] votes %= Map.insert callerName targetName aliveWerewolfNames <- toListOf (players . werewolves . alive . name) <$> get caller <- findPlayerBy_ name callerName target <- findPlayerBy_ name targetName tell [playerMadeDevourVoteMessage werewolfName caller target | werewolfName <- aliveWerewolfNames \\ [callerName]] validateCommand :: (MonadError [Message] m, MonadState Game m) => Text -> m () validateCommand callerName = do validatePlayer callerName callerName unlessM (isPlayerWerewolf callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isWerewolvesTurn $ throwError [playerCannotDoThatRightNowMessage callerName] werewolf-1.5.1.1/app/Game/Werewolf/Command/Witch.hs0000644000000000000000000000370012751254103020110 0ustar0000000000000000{-| Module : Game.Werewolf.Command.Witch Description : Witch commands. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Witch commands. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Command.Witch ( -- * Commands healCommand, passCommand, poisonCommand, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Data.Map as Map import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Message.Error import Game.Werewolf.Util healCommand :: Text -> Command healCommand callerName = Command $ do validateCommand callerName whenM (use healUsed) $ throwError [playerHasAlreadyHealedMessage callerName] whenM (hasn'tuse votee) $ throwError [playerCannotDoThatRightNowMessage callerName] healUsed .= True votes .= Map.empty passCommand :: Text -> Command passCommand callerName = Command $ do validateCommand callerName passed .= True poisonCommand :: Text -> Text -> Command poisonCommand callerName targetName = Command $ do validateCommand callerName whenM (use poisonUsed) $ throwError [playerHasAlreadyPoisonedMessage callerName] validatePlayer callerName targetName whenM (isPlayerZombie targetName) $ throwError [playerCannotChooseZombieMessage callerName] whenM (hasuse $ votee . named targetName) $ throwError [playerCannotDoThatMessage callerName] poison .= Just targetName poisonUsed .= True validateCommand :: (MonadError [Message] m, MonadState Game m) => Text -> m () validateCommand callerName = do validatePlayer callerName callerName unlessM (isPlayerWitch callerName) $ throwError [playerCannotDoThatMessage callerName] unlessM isWitchsTurn $ throwError [playerCannotDoThatRightNowMessage callerName] werewolf-1.5.1.1/app/Game/Werewolf/Engine.hs0000644000000000000000000001764112751254103016672 0ustar0000000000000000{-| Module : Game.Werewolf.Engine Description : Engine functions. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Engine functions. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Game.Werewolf.Engine ( -- * Loop checkStage, checkGameOver, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.List.Extra import qualified Data.Map as Map import Data.Maybe -- TODO (hjw): remove Message.Command import Game.Werewolf.Game hiding (hasAnyoneWon, hasEveryoneLost) import Game.Werewolf.Message.Command import Game.Werewolf.Message.Engine import Game.Werewolf.Player import Game.Werewolf.Response import Game.Werewolf.Role hiding (name) import Game.Werewolf.Util import Prelude hiding (round) checkStage :: (MonadRandom m, MonadState Game m, MonadWriter [Message] m) => m () checkStage = do game <- get checkBoots >> checkStage' game' <- get when (game /= game') checkStage checkBoots :: (MonadState Game m, MonadWriter [Message] m) => m () checkBoots = do alivePlayerCount <- length . toListOf (players . traverse . alive) <$> get booteeNames <- uses boots $ Map.keys . Map.filter (\voters -> length voters > alivePlayerCount `div` 2) bootees <- mapM (findPlayerBy_ name) booteeNames forM_ (filter (is alive) bootees) $ \bootee -> do tell . (:[]) . playerBootedMessage bootee =<< get removePlayer (bootee ^. name) checkStage' :: (MonadRandom m, MonadState Game m, MonadWriter [Message] m) => m () checkStage' = use stage >>= \stage' -> case stage' of DruidsTurn -> do druid <- findPlayerBy_ role druidRole players' <- filter (isn't alphaWolf) <$> getAdjacentAlivePlayers (druid ^. name) when (has werewolves players' || has lycans players') $ tell [ferinaGruntsMessage] advanceStage GameOver -> return () HuntersTurn1 -> whenM (use hunterRetaliated) advanceStage HuntersTurn2 -> whenM (use hunterRetaliated) advanceStage Lynching -> do unlessM (Map.null <$> use votes) $ lynchVotee =<< preuse votee chosenVoters .= [] votes .= Map.empty advanceStage NecromancersTurn -> do whenM (hasuse $ players . necromancers . dead) advanceStage whenM (use deadRaised) advanceStage whenM (use passed) advanceStage OraclesTurn -> do whenM (hasuse $ players . oracles . dead) advanceStage whenM (isJust <$> use divine) advanceStage OrphansTurn -> do whenM (hasuse $ players . orphans . dead) advanceStage whenM (isJust <$> use roleModel) advanceStage ProtectorsTurn -> do whenM (hasuse $ players . protectors . dead) advanceStage whenM (isJust <$> use protect) advanceStage ScapegoatsTurn -> unlessM (use scapegoatBlamed) $ do game <- get tell [scapegoatChoseAllowedVotersMessage game] advanceStage SeersTurn -> do whenM (hasuse $ players . seers . dead) advanceStage whenM (isJust <$> use see) advanceStage Sunrise -> do round += 1 devourVotee =<< preuse votee whenJustM (use poison) $ \targetName -> do target <- findPlayerBy_ name targetName killPlayer targetName tell . (:[]) . playerPoisonedMessage target =<< get whenJustM (preuse $ players . seers . alive) $ \seer -> do target <- use see >>= findPlayerBy_ name . fromJust when (is alive target) $ tell [playerSeenMessage (seer ^. name) target] whenJustM (preuse $ players . oracles . alive) $ \oracle -> do target <- use divine >>= findPlayerBy_ name . fromJust when (is alive target) $ tell [playerDivinedMessage (oracle ^. name) target] divine .= Nothing poison .= Nothing protect .= Nothing see .= Nothing votes .= Map.empty advanceStage Sunset -> do whenJustM (use roleModel) $ \roleModelsName -> do orphan <- findPlayerBy_ role orphanRole whenM (isPlayerDead roleModelsName &&^ return (is alive orphan) &&^ return (is villager orphan)) $ do setPlayerAllegiance (orphan ^. name) Werewolves tell . orphanJoinedPackMessages (orphan ^. name) =<< get advanceStage VillageDrunksTurn -> do randomAllegiance <- getRandomAllegiance players . villageDrunks . role . allegiance .= randomAllegiance villageDrunk <- findPlayerBy_ role villageDrunkRole if is villager villageDrunk then tell [villageDrunkJoinedVillageMessage $ villageDrunk ^. name] else tell . villageDrunkJoinedPackMessages (villageDrunk ^. name) =<< get advanceStage VillagesTurn -> whenM (hasn'tuse pendingVoters) $ do uses votes Map.toList >>= mapM_ (\(voterName, voteeName) -> do voter <- findPlayerBy_ name voterName votee <- findPlayerBy_ name voteeName tell [playerMadeLynchVoteMessage Nothing voter votee] ) advanceStage WerewolvesTurn -> whenM (hasn'tuse pendingVoters) $ do whenM (liftM2 (==) (use protect) (preuses votee $ view name)) $ votes .= Map.empty advanceStage WitchsTurn -> do whenM (hasuse $ players . witches . dead) advanceStage whenM (use healUsed &&^ use poisonUsed) advanceStage whenM (use passed) advanceStage lynchVotee :: (MonadState Game m, MonadWriter [Message] m) => Maybe Player -> m () lynchVotee (Just votee) | is jester votee = do jesterRevealed .= True tell . (:[]) . jesterLynchedMessage =<< get | is fallenAngel votee = do fallenAngelLynched .= True tell . (:[]) . playerLynchedMessage votee =<< get | is saint votee = do tell . (:[]) . playerLynchedMessage votee =<< get killPlayer (votee ^. name) voterNames <- uses votes (filter (/= votee ^. name) . Map.keys . Map.filter (== votee ^. name)) forM_ voterNames killPlayer voters <- mapM (findPlayerBy_ name) voterNames tell . (:[]) . saintLynchedMessage voters =<< get | is werewolf votee = do tell . (:[]) . werewolfLynchedMessage votee =<< get killPlayer (votee ^. name) | otherwise = do tell . (:[]) . playerLynchedMessage votee =<< get killPlayer (votee ^. name) lynchVotee _ = preuse (players . scapegoats . alive) >>= \mScapegoat -> case mScapegoat of Just scapegoat -> do scapegoatBlamed .= True killPlayer (scapegoat ^. name) tell . (:[]) . scapegoatLynchedMessage =<< get _ -> tell [noPlayerLynchedMessage] devourVotee :: (MonadState Game m, MonadWriter [Message] m) => Maybe Player -> m () devourVotee Nothing = tell [noPlayerDevouredMessage] devourVotee (Just votee) = do killPlayer (votee ^. name) tell . (:[]) . playerDevouredMessage votee =<< get when (is medusa votee) . whenJustM (getFirstAdjacentAliveWerewolf $ votee ^. name) $ \werewolf -> do killPlayer (werewolf ^. name) tell . (:[]) . playerTurnedToStoneMessage werewolf =<< get advanceStage :: (MonadState Game m, MonadWriter [Message] m) => m () advanceStage = do game <- get nextStage <- ifM (hasAnyoneWon ||^ hasEveryoneLost) (return GameOver) (return . head $ filter (stageAvailable game) (drop1 $ dropWhile (game ^. stage /=) stageCycle)) stage .= nextStage boots .= Map.empty passed .= False tell . stageMessages =<< get checkGameOver :: (MonadState Game m, MonadWriter [Message] m) => m () checkGameOver = whenM (hasAnyoneWon ||^ hasEveryoneLost) $ stage .= GameOver >> get >>= tell . gameOverMessages werewolf-1.5.1.1/app/Game/Werewolf/Message.hs0000644000000000000000000000422712751254103017045 0ustar0000000000000000{-| Module : Game.Werewolf.Message Description : Utility functions for building messages. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines some utility functions for building messages. See "Game.Werewolf.Message.Command", "Game.Werewolf.Message.Engine" and "Game.Werewolf.Message.Error" for actual message definitions. @werewolf@ was designed to be ambivalent to the playing chat client. The response-message structure reflects this by staying away from anything that could be construed as client-specific. This includes features such as emoji support. -} {-# LANGUAGE OverloadedStrings #-} module Game.Werewolf.Message ( -- * Utility functions humanisePlayerWithRole, humanisePlayersWithRoles, humanisePlayerWithRoleIfKnown, humanisePlayerWithState, article, conjugateToBe, pluralise, ) where import Control.Lens.Extra import Data.String.Humanise import Data.Text (Text) import qualified Data.Text as T import Game.Werewolf.Player import Game.Werewolf.Role hiding (name) -- TODO (hjw): tidy up all of the messages humanisePlayerWithRole :: Player -> Text humanisePlayerWithRole player = T.concat [humanise player, " (", humanise $ player ^. role, ")"] humanisePlayersWithRoles :: [Player] -> Text humanisePlayersWithRoles = humanise . map humanisePlayerWithRole humanisePlayerWithRoleIfKnown :: Player -> Text humanisePlayerWithRoleIfKnown player | any ($ player) [is trueVillager, is zombie] = humanisePlayerWithRole player | otherwise = humanise player humanisePlayerWithState :: Player -> Text humanisePlayerWithState player | is alive player = humanise player | otherwise = T.concat [humanise player, " (dead)"] article :: Role -> Text article role | role `elem` restrictedRoles = "the" | otherwise = "a" conjugateToBe :: Int -> Text conjugateToBe 1 = "is" conjugateToBe _ = "are" pluralise :: Int -> Text -> Text pluralise 1 word = word pluralise _ word = T.snoc word 's' werewolf-1.5.1.1/app/Game/Werewolf/Message/Command.hs0000644000000000000000000002407512755166155020443 0ustar0000000000000000{-| Module : Game.Werewolf.Message.Command Description : Suite of command messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of command messages used throughout the werewolf game. -} module Game.Werewolf.Message.Command ( -- * Quit playerQuitMessage, -- * Boot playerVotedToBootMessage, -- * Circle circleMessage, -- * Choose playerShotMessage, -- * End gameEndedMessage, -- * Help gameDescriptionMessage, globalCommandsMessage, helpCommandsMessage, hunterCommandsMessage, necromancerCommandsMessage, oracleCommandsMessage, orphanCommandsMessage, protectorCommandsMessage, roleMessage, rulesMessage, scapegoatCommandsMessage, seerCommandsMessage, stagesMessage, standardCommandsMessage, statusCommandsMessage, variantMessage, witchCommandsMessage, -- * Ping pingDiurnalRoleMessage, pingNocturnalRoleMessage, pingPlayerMessage, pingVillageMessage, pingWerewolvesMessage, -- * Raise deadRaisedMessages, -- * Status currentDiurnalTurnMessage, currentNocturnalTurnMessage, gameIsOverMessage, marksInGameMessage, playersInGameMessage, -- * Unvote playerRescindedVoteMessage, -- * Version engineVersionMessage, -- * Vote playerMadeDevourVoteMessage, playerMadeLynchVoteMessage, ) where import Control.Lens import Data.Maybe import Data.Text (Text) import qualified Data.Text as T import Game.Werewolf.Game import Game.Werewolf.Player import Game.Werewolf.Response import Game.Werewolf.Role hiding (name) import Game.Werewolf.Variant hiding (name) import Game.Werewolf.Variant.NoRoleKnowledge.Command as NoRoleKnowledge import Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Command as NoRoleKnowledgeOrReveal import Game.Werewolf.Variant.NoRoleReveal.Command as NoRoleReveal import Game.Werewolf.Variant.Standard.Command as Standard playerQuitMessage :: Player -> Game -> Message playerQuitMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.callerQuitText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.callerQuitText player | otherwise = publicMessage $ Standard.callerQuitText player playerVotedToBootMessage :: Player -> Player -> Message playerVotedToBootMessage caller = publicMessage . callerVotedBootText caller circleMessage :: Text -> [Player] -> Message circleMessage to = privateMessage to . gameCircleText playerShotMessage :: Player -> Game -> Message playerShotMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerShotText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerShotText player | otherwise = publicMessage $ Standard.playerShotText player gameEndedMessage :: Text -> Message gameEndedMessage = publicMessage . gameEndedText gameDescriptionMessage :: Text -> Message gameDescriptionMessage to = privateMessage to gameDescriptionText globalCommandsMessage :: Text -> Message globalCommandsMessage to = privateMessage to globalCommandsText helpCommandsMessage :: Text -> Message helpCommandsMessage to = privateMessage to helpCommandsText hunterCommandsMessage :: Text -> Message hunterCommandsMessage to = privateMessage to hunterCommandsText necromancerCommandsMessage :: Text -> Message necromancerCommandsMessage to = privateMessage to necromancerCommandsText oracleCommandsMessage :: Text -> Message oracleCommandsMessage to = privateMessage to oracleCommandsText orphanCommandsMessage :: Text -> Message orphanCommandsMessage to = privateMessage to orphanCommandsText protectorCommandsMessage :: Text -> Message protectorCommandsMessage to = privateMessage to protectorCommandsText roleMessage :: Text -> Role -> Message roleMessage to = privateMessage to . roleDescriptionText rulesMessage :: Text -> Message rulesMessage to = privateMessage to gameRulesText scapegoatCommandsMessage :: Text -> Message scapegoatCommandsMessage to = privateMessage to scapegoatCommandsText seerCommandsMessage :: Text -> Message seerCommandsMessage to = privateMessage to seerCommandsText stagesMessage :: Text -> Maybe Game -> Message stagesMessage to mGame = privateMessage to . T.concat $ [ standardCycleText , sunsetText ] ++ [ orphansTurnText | isNothing mGame || has (players . orphans . named to) (fromJust mGame) ] ++ [ villageDrunksTurnText | isNothing mGame || has (players . villageDrunks . named to) (fromJust mGame) ] ++ [ necromancersTurnText | isNothing mGame || has (players . necromancers . named to) (fromJust mGame) ] ++ [ seersTurnText | isNothing mGame || has (players . seers . named to) (fromJust mGame) ] ++ [ oraclesTurnText | isNothing mGame || has (players . oracles . named to) (fromJust mGame) ] ++ [ protectorsTurnText | isNothing mGame || has (players . protectors . named to) (fromJust mGame) ] ++ [ werewolvesTurnText ] ++ [ witchsTurnText | isNothing mGame || has (players . witches . named to) (fromJust mGame) ] ++ [ sunriseText ] ++ [ huntersTurnText | isNothing mGame || has (players . hunters . named to) (fromJust mGame) ] ++ [ druidsTurnText | isNothing mGame || has (players . druids . named to) (fromJust mGame) ] ++ [ villagesTurnText ] ++ [ huntersTurnText | isNothing mGame || has (players . hunters . named to) (fromJust mGame) ] ++ [ scapegoatsTurnText | isNothing mGame || has (players . scapegoats . named to) (fromJust mGame) ] ++ [ winConditionText ] standardCommandsMessage :: Text -> Message standardCommandsMessage to = privateMessage to standardCommandsText statusCommandsMessage :: Text -> Message statusCommandsMessage to = privateMessage to statusCommandsText variantMessage :: Text -> Variant -> Message variantMessage to = privateMessage to . variantDescriptionText witchCommandsMessage :: Text -> Message witchCommandsMessage to = privateMessage to witchCommandsText pingDiurnalRoleMessage :: Role -> Message pingDiurnalRoleMessage role = publicMessage $ diurnalRolePingedText role pingNocturnalRoleMessage :: Role -> Game -> Message pingNocturnalRoleMessage role game | has (variant . noRoleKnowledge) game = publicMessage $ NoRoleKnowledge.nocturnalRolePingedText role | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.nocturnalRolePingedText role | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.nocturnalRolePingedText role | otherwise = publicMessage $ Standard.nocturnalRolePingedText role pingPlayerMessage :: Text -> Message pingPlayerMessage to = privateMessage to playerPingedText pingVillageMessage :: Message pingVillageMessage = publicMessage villagePingedText pingWerewolvesMessage :: Game -> Message pingWerewolvesMessage game | has (variant . noRoleKnowledge) game = publicMessage NoRoleKnowledge.werewolvesPingedText | has (variant . noRoleKnowledgeOrReveal) game = publicMessage NoRoleKnowledgeOrReveal.werewolvesPingedText | has (variant . noRoleReveal) game = publicMessage NoRoleReveal.werewolvesPingedText | otherwise = publicMessage Standard.werewolvesPingedText deadRaisedMessages :: Game -> [Message] deadRaisedMessages game = necromancerRaisedDeadMessage game : map (`playerRaisedFromDeadMessage` game) zombieNames where zombieNames = game ^.. players . zombies . name necromancerRaisedDeadMessage :: Game -> Message necromancerRaisedDeadMessage = publicMessage . necromancerRaisedDeadText playerRaisedFromDeadMessage :: Text -> Game -> Message playerRaisedFromDeadMessage to = privateMessage to . playerRaisedFromDeadText currentDiurnalTurnMessage :: Text -> Game -> Message currentDiurnalTurnMessage to game = privateMessage to $ Standard.currentDiurnalTurnText game currentNocturnalTurnMessage :: Text -> Game -> Message currentNocturnalTurnMessage to game | has (variant . noRoleKnowledge) game = privateMessage to $ NoRoleKnowledge.currentNocturnalTurnText game | has (variant . noRoleKnowledgeOrReveal) game = privateMessage to $ NoRoleKnowledgeOrReveal.currentNocturnalTurnText game | has (variant . noRoleReveal) game = privateMessage to $ NoRoleReveal.currentNocturnalTurnText game | otherwise = privateMessage to $ Standard.currentNocturnalTurnText game gameIsOverMessage :: Text -> Message gameIsOverMessage to = privateMessage to gameOverText marksInGameMessage :: Text -> Game -> Message marksInGameMessage to game = privateMessage to $ marksText game playersInGameMessage :: Text -> Game -> Message playersInGameMessage to game = privateMessage to $ T.append alivePlayersText' deadPlayersText' where alivePlayersText' = alivePlayersText game deadPlayersText' | hasn't (players . traverse . dead) game = T.empty | has (variant . noRoleKnowledgeOrReveal) game = NoRoleKnowledgeOrReveal.deadPlayersText game | has (variant . noRoleReveal) game = NoRoleReveal.deadPlayersText game | otherwise = Standard.deadPlayersText game playerRescindedVoteMessage :: Text -> Player -> Message playerRescindedVoteMessage to = privateMessage to . callerRescindedVoteText engineVersionMessage :: Text -> Message engineVersionMessage to = privateMessage to engineVersionText playerMadeDevourVoteMessage :: Text -> Player -> Player -> Message playerMadeDevourVoteMessage to caller = privateMessage to . callerVotedDevourText caller playerMadeLynchVoteMessage :: Maybe Text -> Player -> Player -> Message playerMadeLynchVoteMessage mTo caller = Message mTo . callerVotedLynchText caller werewolf-1.5.1.1/app/Game/Werewolf/Message/Engine.hs0000644000000000000000000004675512755166155020303 0ustar0000000000000000{-| Module : Game.Werewolf.Message.Engine Description : Suite of engine messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of engine messages used throughout the werewolf game. -} -- TODO (hjw): sort this file module Game.Werewolf.Message.Engine ( playerRolesMessage, playerBootedMessage, villageDrunkJoinedPackMessages, villageDrunkJoinedVillageMessage, playerTurnedToStoneMessage, playerSeenMessage, playerDivinedMessage, playerDevouredMessage, noPlayerDevouredMessage, scapegoatChoseAllowedVotersMessage, playerPoisonedMessage, scapegoatLynchedMessage, saintLynchedMessage, playerLynchedMessage, noPlayerLynchedMessage, jesterLynchedMessage, ferinaGruntsMessage, orphanJoinedPackMessages, playerKilledMessage, playerLostMessage, playerContributedMessage, playerWonMessage, witchsTurnMessages, firstWerewolvesTurnMessages, villagesTurnMessage, nightFallsMessage, sunriseMessage, villageDrunksTurnMessages, spitefulVillagerKilledMessage, seersTurnMessages, scapegoatsTurnMessage, protectorsTurnMessages, orphansTurnMessages, oraclesTurnMessages, huntersTurnMessages, stageMessages, trueVillagerMessage, beholderMessage, newPlayerMessage, rolesInGameMessage, newPlayersInGameMessage, newGameMessages, gameOverMessages, fallenAngelWonMessage, werewolfLynchedMessage, zombiesReturnedToGraveMessage, ) where import Control.Lens.Extra import Data.List.Extra import Data.Text (Text) import Game.Werewolf.Game import Game.Werewolf.Player import Game.Werewolf.Response import Game.Werewolf.Role hiding (name) import Game.Werewolf.Variant hiding (name) import Game.Werewolf.Variant.NoRoleKnowledge.Engine as NoRoleKnowledge import Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Engine as NoRoleKnowledgeOrReveal import Game.Werewolf.Variant.NoRoleReveal.Engine as NoRoleReveal import Game.Werewolf.Variant.Standard.Engine as Standard playerBootedMessage :: Player -> Game -> Message playerBootedMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerBootedText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerBootedText player | otherwise = publicMessage $ Standard.playerBootedText player gameOverMessages :: Game -> [Message] gameOverMessages game | hasDullahanWon game = concat [ [dullahanWonMessage game] , [playerRolesMessage game] , [playerWonMessage dullahansName] , map playerLostMessage (game ^.. players . names \\ [dullahansName]) ] | hasFallenAngelWon game = concat [ [fallenAngelWonMessage] , [playerRolesMessage game] , [playerWonMessage fallenAngelsName] , map playerLostMessage (game ^.. players . names \\ [fallenAngelsName]) ] | hasNecromancerWon game = concat [ [necromancerWonMessage] , [playerRolesMessage game] , map playerWonMessage (necromancersName:zombieNames) , map playerLostMessage (game ^.. players . names \\ (necromancersName:zombieNames)) ] | hasEveryoneLost game = concat [ [everyoneLostMessage] , [playerRolesMessage game] , map playerLostMessage (game ^.. players . names) ] | otherwise = concat [ [allegianceWonMessage winningAllegiance] , [playerRolesMessage game] , playerWonMessages , playerContributedMessages , playerLostMessages ] where winningAllegiance | hasVillagersWon game = Villagers | hasWerewolvesWon game = Werewolves | otherwise = undefined winningPlayers = game ^.. players . traverse . filteredBy (role . allegiance) winningAllegiance losingPlayers = game ^. players \\ winningPlayers playerWonMessages = map playerWonMessage (winningPlayers ^.. traverse . alive . name) playerContributedMessages = map playerContributedMessage (winningPlayers ^.. traverse . dead . name) playerLostMessages = map playerLostMessage (losingPlayers ^.. names) dullahansName = game ^?! players . dullahans . name fallenAngelsName = game ^?! players . fallenAngels . name necromancersName = game ^?! players . necromancers . name zombieNames = game ^.. players . zombies . name dullahanWonMessage :: Game -> Message dullahanWonMessage = publicMessage . dullahanWonText everyoneLostMessage :: Message everyoneLostMessage = publicMessage everyoneLostText fallenAngelWonMessage :: Message fallenAngelWonMessage = publicMessage fallenAngelWonText necromancerWonMessage :: Message necromancerWonMessage = publicMessage necromancerWonText allegianceWonMessage :: Allegiance -> Message allegianceWonMessage = publicMessage . allegianceWonText playerRolesMessage :: Game -> Message playerRolesMessage = publicMessage . playerRolesText newGameMessages :: Game -> [Message] newGameMessages game = concat [ [newPlayersInGameMessage game] , gameVariantMessages , [rolesInGameMessage Nothing game] , map newPlayerMessage players' , beholderMessages , dullahanMessages , trueVillagerMessages , stageMessages game ] where players' = game ^. players gameVariantMessages = [ gameVariantMessage game | hasn't (variant . standard) game ] beholderMessages = [ beholderMessage beholderName game | has beholders players' , has seers players' , beholderName <- players' ^.. beholders . name ] dullahanMessages = [ dullahanMessage dullahanName game | has dullahans players' , dullahanName <- players' ^.. dullahans . name ] trueVillagerMessages = [ trueVillagerMessage game | has trueVillagers players' ] newPlayersInGameMessage :: Game -> Message newPlayersInGameMessage = publicMessage . playersInGameText gameVariantMessage :: Game -> Message gameVariantMessage = publicMessage . gameVariantText rolesInGameMessage :: Maybe Text -> Game -> Message rolesInGameMessage mTo game | has (variant . noRoleKnowledge) game = Message mTo $ NoRoleKnowledge.rolesInGameText game | has (variant . noRoleKnowledgeOrReveal) game = Message mTo $ NoRoleKnowledgeOrReveal.rolesInGameText game | otherwise = Message mTo $ Standard.rolesInGameText game newPlayerMessage :: Player -> Message newPlayerMessage player = privateMessage (player ^. name) $ newPlayerText player beholderMessage :: Text -> Game -> Message beholderMessage to = privateMessage to . beholderText dullahanMessage :: Text -> Game -> Message dullahanMessage to = privateMessage to . dullahanText spitefulVillagerKilledMessage :: Text -> Game -> Message spitefulVillagerKilledMessage to game | has (variant . noRoleKnowledgeOrReveal) game = privateMessage to $ NoRoleKnowledgeOrReveal.spitefulVillagerKilledText game | has (variant . noRoleReveal) game = privateMessage to $ NoRoleReveal.spitefulVillagerKilledText game | otherwise = privateMessage to $ Standard.spitefulVillagerKilledText game trueVillagerMessage :: Game -> Message trueVillagerMessage = publicMessage . trueVillagerText stageMessages :: Game -> [Message] stageMessages game = case game ^. stage of DruidsTurn -> [] GameOver -> [] HuntersTurn1 -> huntersTurnMessages game HuntersTurn2 -> huntersTurnMessages game Lynching -> [] NecromancersTurn -> necromancersTurnMessages necromancersName game OraclesTurn -> oraclesTurnMessages oraclesName game OrphansTurn -> orphansTurnMessages orphansName game ProtectorsTurn -> protectorsTurnMessages protectorsName game ScapegoatsTurn -> [scapegoatsTurnMessage game] SeersTurn -> seersTurnMessages seersName game Sunrise -> [sunriseMessage] Sunset -> [nightFallsMessage] VillageDrunksTurn -> villageDrunksTurnMessages game VillagesTurn -> [villagesTurnMessage] WerewolvesTurn -> if is firstRound game then firstWerewolvesTurnMessages game else werewolvesTurnMessages aliveWerewolfNames game WitchsTurn -> witchsTurnMessages game where players' = game ^. players necromancersName = players' ^?! necromancers . name oraclesName = players' ^?! oracles . name orphansName = players' ^?! orphans . name protectorsName = players' ^?! protectors . name seersName = players' ^?! seers . name aliveWerewolfNames = players' ^.. werewolves . alive . name huntersTurnMessages :: Game -> [Message] huntersTurnMessages game = [ publicMessage $ huntersTurnPublicText game , privateMessage hunterName huntersTurnPrivateText ] where hunterName = game ^?! players . hunters . name necromancersTurnMessages :: Text -> Game -> [Message] necromancersTurnMessages to game | has (variant . noRoleKnowledge) game = [ privateMessage to necromancersTurnPrivateText ] | has (variant . noRoleKnowledgeOrReveal) game = [ privateMessage to necromancersTurnPrivateText ] | has (variant . noRoleReveal) game = [ privateMessage to necromancersTurnPrivateText ] | otherwise = [ publicMessage necromancersTurnPublicText , privateMessage to necromancersTurnPrivateText ] oraclesTurnMessages :: Text -> Game -> [Message] oraclesTurnMessages to game | has (variant . noRoleKnowledge) game = [ privateMessage to oraclesTurnPrivateText ] | has (variant . noRoleKnowledgeOrReveal) game = [ privateMessage to oraclesTurnPrivateText ] | has (variant . noRoleReveal) game = [ privateMessage to oraclesTurnPrivateText ] | otherwise = [ publicMessage oraclesTurnPublicText , privateMessage to oraclesTurnPrivateText ] orphansTurnMessages :: Text -> Game -> [Message] orphansTurnMessages to game | has (variant . noRoleKnowledge) game = [ privateMessage to orphansTurnPrivateText ] | has (variant . noRoleKnowledgeOrReveal) game = [ privateMessage to orphansTurnPrivateText ] | has (variant . noRoleReveal) game = [ privateMessage to orphansTurnPrivateText ] | otherwise = [ publicMessage orphansTurnPublicText , privateMessage to orphansTurnPrivateText ] protectorsTurnMessages :: Text -> Game -> [Message] protectorsTurnMessages to game | has (variant . noRoleKnowledge) game = [ privateMessage to protectorsTurnPrivateText ] | has (variant . noRoleKnowledgeOrReveal) game = [ privateMessage to protectorsTurnPrivateText ] | has (variant . noRoleReveal) game = [ privateMessage to protectorsTurnPrivateText ] | otherwise = [ publicMessage protectorsTurnPublicText , privateMessage to protectorsTurnPrivateText ] scapegoatsTurnMessage :: Game -> Message scapegoatsTurnMessage = publicMessage . scapegoatsTurnText seersTurnMessages :: Text -> Game -> [Message] seersTurnMessages to game | has (variant . noRoleKnowledge) game = [ privateMessage to seersTurnPrivateText ] | has (variant . noRoleKnowledgeOrReveal) game = [ privateMessage to seersTurnPrivateText ] | has (variant . noRoleReveal) game = [ privateMessage to seersTurnPrivateText ] | otherwise = [ publicMessage seersTurnPublicText , privateMessage to seersTurnPrivateText ] villageDrunksTurnMessages :: Game -> [Message] villageDrunksTurnMessages game | has (variant . noRoleKnowledge) game = [] | has (variant . noRoleKnowledgeOrReveal) game = [] | has (variant . noRoleReveal) game = [] | otherwise = [publicMessage villageDrunksTurnText] sunriseMessage :: Message sunriseMessage = publicMessage sunriseText nightFallsMessage :: Message nightFallsMessage = publicMessage sunsetText villagesTurnMessage :: Message villagesTurnMessage = publicMessage villagesTurnText firstWerewolvesTurnMessages :: Game -> [Message] firstWerewolvesTurnMessages game = [privateMessage (player ^. name) (firstWerewolvesTurnText player game) | length werewolves > 1, player <- werewolves] ++ werewolvesTurnMessages (map (view name) werewolves) game where werewolves = game ^.. players . traverse . alive . filtered (is werewolf) werewolvesTurnMessages :: [Text] -> Game -> [Message] werewolvesTurnMessages tos game | has (variant . noRoleKnowledge) game = groupMessages tos werewolvesTurnPrivateText | has (variant . noRoleKnowledgeOrReveal) game = groupMessages tos werewolvesTurnPrivateText | has (variant . noRoleReveal) game = groupMessages tos werewolvesTurnPrivateText | otherwise = publicMessage werewolvesTurnPublicText : groupMessages tos werewolvesTurnPrivateText witchsTurnMessages :: Game -> [Message] witchsTurnMessages game = concat [ wakeUpMessages , healMessages , poisonMessages , [passMessage] ] where to = game ^?! players . witches . name wakeUpMessages | has (variant . noRoleKnowledge) game = [] | has (variant . noRoleKnowledgeOrReveal) game = [] | has (variant . noRoleReveal) game = [] | otherwise = [publicMessage witchsTurnText] passMessage = privateMessage to passText healMessages | game ^. healUsed = [] | hasn't votee game = [] | otherwise = [privateMessage to $ healText game] poisonMessages | game ^. poisonUsed = [] | otherwise = [privateMessage to poisonText] playerWonMessage :: Text -> Message playerWonMessage to = privateMessage to playerWonText playerContributedMessage :: Text -> Message playerContributedMessage to = privateMessage to playerContributedText playerLostMessage :: Text -> Message playerLostMessage to = privateMessage to playerLostText playerKilledMessage :: Text -> Message playerKilledMessage to = privateMessage to playerKilledText orphanJoinedPackMessages :: Text -> Game -> [Message] orphanJoinedPackMessages to game = privateMessage to (orphanJoinedWerewolvesPrivateText game) : groupMessages (map (view name) werewolves) (orphanJoinedWerewolvesGroupText game) where orphan = game ^?! players . villageDrunks werewolves = game ^.. players . traverse . alive . filtered (is werewolf) \\ [orphan] ferinaGruntsMessage :: Message ferinaGruntsMessage = publicMessage druidsTurnText jesterLynchedMessage :: Game -> Message jesterLynchedMessage = publicMessage . jesterLynchedText noPlayerLynchedMessage :: Message noPlayerLynchedMessage = publicMessage noPlayerLynchedText werewolfLynchedMessage :: Player -> Game -> Message werewolfLynchedMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerLynchedText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player | otherwise = publicMessage $ Standard.werewolfLynchedText player playerLynchedMessage :: Player -> Game -> Message playerLynchedMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerLynchedText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player | otherwise = publicMessage $ Standard.playerLynchedText player saintLynchedMessage :: [Player] -> Game -> Message saintLynchedMessage voters game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.saintLynchedText voters | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.saintLynchedText voters | otherwise = publicMessage $ Standard.saintLynchedText voters scapegoatLynchedMessage :: Game -> Message scapegoatLynchedMessage = publicMessage . scapegoatLynchedText scapegoatChoseAllowedVotersMessage :: Game -> Message scapegoatChoseAllowedVotersMessage = publicMessage . scapegoatsTurnEndedText noPlayerDevouredMessage :: Message noPlayerDevouredMessage = publicMessage noPlayerDevouredText playerDevouredMessage :: Player -> Game -> Message playerDevouredMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerDevouredText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerDevouredText player | otherwise = publicMessage $ Standard.playerDevouredText player playerDivinedMessage :: Text -> Player -> Message playerDivinedMessage to = privateMessage to . playerDivinedText playerPoisonedMessage :: Player -> Game -> Message playerPoisonedMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerPoisonedText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerPoisonedText player | otherwise = publicMessage $ Standard.playerPoisonedText player playerSeenMessage :: Text -> Player -> Message playerSeenMessage to player | is alphaWolf player = privateMessage to $ alphaWolfSeenText player | is lycan player = privateMessage to $ lycanSeenText player | otherwise = privateMessage to $ playerSeenText player playerTurnedToStoneMessage :: Player -> Game -> Message playerTurnedToStoneMessage player game | has (variant . noRoleKnowledgeOrReveal) game = publicMessage $ NoRoleKnowledgeOrReveal.playerTurnedToStoneText player | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerTurnedToStoneText player | otherwise = publicMessage $ Standard.playerTurnedToStoneText player villageDrunkJoinedVillageMessage :: Text -> Message villageDrunkJoinedVillageMessage to = privateMessage to villageDrunkJoinedVillageText villageDrunkJoinedPackMessages :: Text -> Game -> [Message] villageDrunkJoinedPackMessages to game = privateMessage to (villageDrunkJoinedWerewolvesPrivateText game) : groupMessages (map (view name) werewolves) (villageDrunkJoinedWerewolvesGroupText game) where villageDrunk = game ^?! players . villageDrunks werewolves = game ^.. players . traverse . alive . filtered (is werewolf) \\ [villageDrunk] zombiesReturnedToGraveMessage :: Game -> Message zombiesReturnedToGraveMessage = publicMessage . zombiesReturnedToGraveText werewolf-1.5.1.1/app/Game/Werewolf/Message/Error.hs0000644000000000000000000001060512751254103020133 0ustar0000000000000000{-| Module : Game.Werewolf.Message.Error Description : Suite of error messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of error messages used throughout the werewolf game. -} module Game.Werewolf.Message.Error ( -- * Command -- ** Boot playerHasAlreadyVotedToBootMessage, -- ** Choose playerCannotChooseJesterMessage, playerCannotChooseSelfMessage, playerCannotChooseZombieMessage, playerMustChooseAtLeastOneTargetMessage, -- ** General noGameRunningMessage, playerCannotDoThatMessage, playerCannotDoThatRightNowMessage, playerIsDeadMessage, playerDoesNotExistMessage, targetIsDeadMessage, -- ** Heal playerHasAlreadyHealedMessage, -- ** Poison playerHasAlreadyPoisonedMessage, -- ** Protect playerCannotProtectSamePlayerTwiceInARowMessage, -- ** Start gameAlreadyRunningMessage, mustHaveAtLeast7PlayersMessage, playerNamesMustBeUniqueMessage, roleCountRestrictedMessage, roleDoesNotExistMessage, variantDoesNotExistMessage, -- ** Unvote playerHasNotVotedMessage, -- ** Vote playerHasAlreadyVotedMessage, playerCannotDevourAnotherWerewolfMessage, ) where import Data.Text (Text) import Game.Werewolf.Player import Game.Werewolf.Response import Game.Werewolf.Role import Game.Werewolf.Variant.Standard.Error playerHasAlreadyVotedToBootMessage :: Text -> Player -> Message playerHasAlreadyVotedToBootMessage to = privateMessage to . callerAlreadyVotedBootText playerCannotChooseJesterMessage :: Text -> Message playerCannotChooseJesterMessage to = privateMessage to callerCannotChooseJesterText playerCannotChooseSelfMessage :: Text -> Message playerCannotChooseSelfMessage to = privateMessage to callerCannotChooseSelfText playerCannotChooseZombieMessage :: Text -> Message playerCannotChooseZombieMessage to = privateMessage to callerCannotChooseZombieText playerMustChooseAtLeastOneTargetMessage :: Text -> Message playerMustChooseAtLeastOneTargetMessage to = privateMessage to noTargetText noGameRunningMessage :: Text -> Message noGameRunningMessage to = privateMessage to noGameRunningText playerCannotDoThatMessage :: Text -> Message playerCannotDoThatMessage to = privateMessage to callerCannotDoThatText playerCannotDoThatRightNowMessage :: Text -> Message playerCannotDoThatRightNowMessage to = privateMessage to callerCannotDoThatRightNowText playerIsDeadMessage :: Text -> Message playerIsDeadMessage to = privateMessage to callerDeadText playerDoesNotExistMessage :: Text -> Text -> Message playerDoesNotExistMessage to = privateMessage to . playerDoesNotExistText targetIsDeadMessage :: Text -> Player -> Message targetIsDeadMessage to = privateMessage to . targetDeadText playerHasAlreadyHealedMessage :: Text -> Message playerHasAlreadyHealedMessage to = privateMessage to callerAlreadyHealedText playerHasAlreadyPoisonedMessage :: Text -> Message playerHasAlreadyPoisonedMessage to = privateMessage to callerAlreadyPoisonedText playerCannotProtectSamePlayerTwiceInARowMessage :: Text -> Message playerCannotProtectSamePlayerTwiceInARowMessage to = privateMessage to callerCannotProtectSamePlayerText gameAlreadyRunningMessage :: Text -> Message gameAlreadyRunningMessage to = privateMessage to gameAlreadyRunningText mustHaveAtLeast7PlayersMessage :: Text -> Message mustHaveAtLeast7PlayersMessage to = privateMessage to playerCountTooLowText playerNamesMustBeUniqueMessage :: Text -> Message playerNamesMustBeUniqueMessage to = privateMessage to playerNamesNotUniqueText roleCountRestrictedMessage :: Text -> Role -> Message roleCountRestrictedMessage to = privateMessage to . roleCountRestrictedText roleDoesNotExistMessage :: Text -> Text -> Message roleDoesNotExistMessage to = privateMessage to . roleDoesNotExistText variantDoesNotExistMessage :: Text -> Text -> Message variantDoesNotExistMessage to = privateMessage to . variantDoesNotExistText playerHasNotVotedMessage :: Text -> Message playerHasNotVotedMessage to = privateMessage to callerNotVotedText playerHasAlreadyVotedMessage :: Text -> Message playerHasAlreadyVotedMessage to = privateMessage to callerAlreadyVotedText playerCannotDevourAnotherWerewolfMessage :: Text -> Message playerCannotDevourAnotherWerewolfMessage to = privateMessage to callerCannotDevourAnotherWerewolfText werewolf-1.5.1.1/app/Game/Werewolf/Util.hs0000644000000000000000000002005312751254103016371 0ustar0000000000000000{-| Module : Game.Werewolf.Util Description : Utility functions for working in a ('MonadState' 'Game') environment. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Utility functions for woking in a ('MonadState' 'Game') environment. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE Rank2Types #-} module Game.Werewolf.Util ( -- * Game -- ** Manipulations killPlayer, removePlayer, setPlayerAllegiance, getRandomAllegiance, -- ** Searches findPlayerBy_, getAdjacentAlivePlayers, getFirstAdjacentAliveWerewolf, getPlayerVote, -- ** Queries isGameOver, isHuntersTurn, isNecromancersTurn, isOraclesTurn, isOrphansTurn, isProtectorsTurn, isScapegoatsTurn, isSeersTurn, isSunrise, isVillagesTurn, isWerewolvesTurn, isWitchsTurn, hasAnyoneWon, hasNecromancerWon, hasVillagersWon, hasWerewolvesWon, hasEveryoneLost, -- * Player -- ** Queries doesPlayerExist, isPlayerDullahan, isPlayerHunter, isPlayerJester, isPlayerNecromancer, isPlayerOracle, isPlayerOrphan, isPlayerProtector, isPlayerScapegoat, isPlayerSeer, isPlayerWitch, isPlayerZombie, isPlayerWerewolf, isPlayerAlive, isPlayerDead, ) where import Control.Lens.Extra import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State hiding (state) import Control.Monad.Writer import Data.List import qualified Data.Map as Map import Data.Maybe import Data.Text (Text) import Game.Werewolf.Game hiding (hasAnyoneWon, hasEveryoneLost, hasVillagersWon, hasWerewolvesWon) import qualified Game.Werewolf.Game as Game import Game.Werewolf.Message.Engine import Game.Werewolf.Player import Game.Werewolf.Response import Game.Werewolf.Role hiding (name) import Prelude hiding (round) killPlayer :: (MonadState Game m, MonadWriter [Message] m) => Text -> m () killPlayer name' = do tell [playerKilledMessage name'] players . traverse . named name' . state .= Dead whenM (isPlayerSpitefulVillager name') $ tell . (:[]) . spitefulVillagerKilledMessage name' =<< get whenM (isPlayerNecromancer name') $ do zombieNames <- toListOf (players . zombies . name) <$> get unless (null zombieNames) $ do mapM_ killPlayer zombieNames tell . (:[]) . zombiesReturnedToGraveMessage =<< get removePlayer :: (MonadState Game m, MonadWriter [Message] m) => Text -> m () removePlayer name' = do killPlayer name' votes %= Map.delete name' player <- findPlayerBy_ name name' when (is orphan player) $ roleModel .= Nothing when (is protector player) $ do protect .= Nothing priorProtect .= Nothing when (is seer player) $ see .= Nothing when (is witch player) $ do healUsed .= False poison .= Nothing poisonUsed .= False -- | Fudges the player's allegiance. This function is useful for roles such as the Orphan where -- they align themselves differently given some trigger. setPlayerAllegiance :: MonadState Game m => Text -> Allegiance -> m () setPlayerAllegiance name allegiance' = modify $ players . traverse . named name . role . allegiance .~ allegiance' -- | Get a random allegiance (either Villagers or Werewolves). getRandomAllegiance :: MonadRandom m => m Allegiance getRandomAllegiance = fromList [(Villagers, 0.5), (Werewolves, 0.5)] findPlayerBy_ :: (Eq a, MonadState Game m) => Lens' Player a -> a -> m Player findPlayerBy_ lens value = fromJust <$> preuse (players . traverse . filteredBy lens value) getAdjacentAlivePlayers :: MonadState Game m => Text -> m [Player] getAdjacentAlivePlayers name' = do alivePlayers <- toListOf (players . traverse . alive) <$> get let index = fromJust $ elemIndex name' (alivePlayers ^.. names) return $ adjacentElements index alivePlayers where adjacentElements 0 list = last list : take 2 list adjacentElements index list = take 3 . drop (index - 1) $ cycle list getFirstAdjacentAliveWerewolf :: MonadState Game m => Text -> m (Maybe Player) getFirstAdjacentAliveWerewolf name = do players' <- toListOf (players . traverse) <$> get let index = fromJust $ elemIndex name (players' ^.. names) let filteredPlayers = dropWhile (isn't werewolf) (drop index (players' ++ players') ^.. traverse . alive) return $ listToMaybe filteredPlayers getPlayerVote :: MonadState Game m => Text -> m (Maybe Text) getPlayerVote playerName = use $ votes . at playerName isGameOver :: MonadState Game m => m Bool isGameOver = hasuse $ stage . _GameOver isHuntersTurn :: MonadState Game m => m Bool isHuntersTurn = orM [ hasuse $ stage . _HuntersTurn1 , hasuse $ stage . _HuntersTurn2 ] isNecromancersTurn :: MonadState Game m => m Bool isNecromancersTurn = hasuse $ stage . _NecromancersTurn isOraclesTurn :: MonadState Game m => m Bool isOraclesTurn = hasuse $ stage . _OraclesTurn isOrphansTurn :: MonadState Game m => m Bool isOrphansTurn = hasuse $ stage . _OrphansTurn isProtectorsTurn :: MonadState Game m => m Bool isProtectorsTurn = hasuse $ stage . _ProtectorsTurn isScapegoatsTurn :: MonadState Game m => m Bool isScapegoatsTurn = hasuse $ stage . _ScapegoatsTurn isSeersTurn :: MonadState Game m => m Bool isSeersTurn = hasuse $ stage . _SeersTurn isSunrise :: MonadState Game m => m Bool isSunrise = hasuse $ stage . _Sunrise isVillagesTurn :: MonadState Game m => m Bool isVillagesTurn = hasuse $ stage . _VillagesTurn isWerewolvesTurn :: MonadState Game m => m Bool isWerewolvesTurn = hasuse $ stage . _WerewolvesTurn isWitchsTurn :: MonadState Game m => m Bool isWitchsTurn = hasuse $ stage . _WitchsTurn hasAnyoneWon :: MonadState Game m => m Bool hasAnyoneWon = gets Game.hasAnyoneWon hasVillagersWon :: MonadState Game m => m Bool hasVillagersWon = gets Game.hasVillagersWon hasWerewolvesWon :: MonadState Game m => m Bool hasWerewolvesWon = gets Game.hasWerewolvesWon hasEveryoneLost :: MonadState Game m => m Bool hasEveryoneLost = gets Game.hasEveryoneLost doesPlayerExist :: MonadState Game m => Text -> m Bool doesPlayerExist name = hasuse $ players . traverse . named name isPlayerDullahan :: MonadState Game m => Text -> m Bool isPlayerDullahan name' = is dullahan <$> findPlayerBy_ name name' isPlayerHunter :: MonadState Game m => Text -> m Bool isPlayerHunter name' = is hunter <$> findPlayerBy_ name name' isPlayerJester :: MonadState Game m => Text -> m Bool isPlayerJester name' = is jester <$> findPlayerBy_ name name' isPlayerNecromancer :: MonadState Game m => Text -> m Bool isPlayerNecromancer name' = is necromancer <$> findPlayerBy_ name name' isPlayerOracle :: MonadState Game m => Text -> m Bool isPlayerOracle name' = is oracle <$> findPlayerBy_ name name' isPlayerOrphan :: MonadState Game m => Text -> m Bool isPlayerOrphan name' = is orphan <$> findPlayerBy_ name name' isPlayerProtector :: MonadState Game m => Text -> m Bool isPlayerProtector name' = is protector <$> findPlayerBy_ name name' isPlayerScapegoat :: MonadState Game m => Text -> m Bool isPlayerScapegoat name' = is scapegoat <$> findPlayerBy_ name name' isPlayerSeer :: MonadState Game m => Text -> m Bool isPlayerSeer name' = is seer <$> findPlayerBy_ name name' isPlayerSpitefulVillager :: MonadState Game m => Text -> m Bool isPlayerSpitefulVillager name' = is spitefulVillager <$> findPlayerBy_ name name' isPlayerWitch :: MonadState Game m => Text -> m Bool isPlayerWitch name' = is witch <$> findPlayerBy_ name name' isPlayerZombie :: MonadState Game m => Text -> m Bool isPlayerZombie name' = is zombie <$> findPlayerBy_ name name' isPlayerWerewolf :: MonadState Game m => Text -> m Bool isPlayerWerewolf name' = is werewolf <$> findPlayerBy_ name name' isPlayerAlive :: MonadState Game m => Text -> m Bool isPlayerAlive name' = is alive <$> findPlayerBy_ name name' isPlayerDead :: MonadState Game m => Text -> m Bool isPlayerDead name' = is dead <$> findPlayerBy_ name name' werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledge/Command.hs0000644000000000000000000000220112755165307023502 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleKnowledge.Command Description : Suite of command messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of command messages used throughout the werewolf game for the 'NoRoleKnowledge' variant. -} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleKnowledge.Command ( -- * Ping nocturnalRolePingedText, werewolvesPingedText, -- * Status currentNocturnalTurnText, ) where import Data.String.Interpolate.Extra import Data.Text (Text) import Game.Werewolf nocturnalRolePingedText :: Role -> Text nocturnalRolePingedText _ = [iFile|variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt|] werewolvesPingedText :: Text werewolvesPingedText = [iFile|variant/no-role-knowledge/command/ping/werewolves-pinged.txt|] currentNocturnalTurnText :: Game -> Text currentNocturnalTurnText _ = [iFile|variant/no-role-knowledge/command/status/current-nocturnal-turn.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledge/Engine.hs0000644000000000000000000000167012755164534023343 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleKnowledge.Engine Description : Suite of engine messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of engine messages used throughout the werewolf game for the 'NoRoleKnowledge' variant. -} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleKnowledge.Engine ( -- * New game rolesInGameText, ) where import Control.Lens import Data.String.Interpolate.Extra import Data.Text (Text) import qualified Data.Text as T import Game.Werewolf rolesInGameText :: Game -> Text rolesInGameText game = [iFile|variant/no-role-knowledge/engine/new-game/roles-in-game.txt|] where totalBalance = sumOf (players . roles . balance) game werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledgeOrReveal/Command.hs0000644000000000000000000000342612755166155025156 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Command Description : Suite of command messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of command messages used throughout the werewolf game for the 'NoRoleKnowledgeOrReveal' variant. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Command ( -- * Choose playerShotText, -- * Ping nocturnalRolePingedText, werewolvesPingedText, -- * Quit callerQuitText, -- * Status currentNocturnalTurnText, deadPlayersText, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message playerShotText :: Player -> Text playerShotText player = [iFile|variant/no-role-knowledge-or-reveal/command/choose/player-shot.txt|] nocturnalRolePingedText :: Role -> Text nocturnalRolePingedText _ = [iFile|variant/no-role-knowledge-or-reveal/command/ping/nocturnal-role-pinged.txt|] werewolvesPingedText :: Text werewolvesPingedText = [iFile|variant/no-role-knowledge-or-reveal/command/ping/werewolves-pinged.txt|] callerQuitText :: Player -> Text callerQuitText caller = [iFile|variant/no-role-knowledge-or-reveal/command/quit/caller-quit.txt|] currentNocturnalTurnText :: Game -> Text currentNocturnalTurnText _ = [iFile|variant/no-role-knowledge-or-reveal/command/status/current-nocturnal-turn.txt|] deadPlayersText :: Game -> Text deadPlayersText game = [iFile|variant/no-role-knowledge-or-reveal/command/status/dead-players.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleKnowledgeOrReveal/Engine.hs0000644000000000000000000000454612755166155025011 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Engine Description : Suite of engine messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of engine messages used throughout the werewolf game for the 'NoRoleKnowledgeOrReveal' variant. -} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Engine ( -- * General playerBootedText, spitefulVillagerKilledText, -- * Lynching playerLynchedText, saintLynchedText, werewolfLynchedText, -- * New game rolesInGameText, -- * Sunrise playerDevouredText, playerPoisonedText, playerTurnedToStoneText, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text as T import Game.Werewolf import Game.Werewolf.Message playerBootedText :: Player -> Text playerBootedText player = [iFile|variant/no-role-knowledge-or-reveal/engine/general/player-booted.txt|] spitefulVillagerKilledText :: Game -> Text spitefulVillagerKilledText game = [iFile|variant/no-role-knowledge-or-reveal/engine/general/spiteful-villager-killed.txt|] playerLynchedText :: Player -> Text playerLynchedText player = [iFile|variant/no-role-knowledge-or-reveal/engine/lynching/player-lynched.txt|] saintLynchedText :: [Player] -> Text saintLynchedText voters = [iFile|variant/no-role-knowledge-or-reveal/engine/lynching/saint-lynched.txt|] werewolfLynchedText :: Player -> Text werewolfLynchedText werewolf = [iFile|variant/no-role-knowledge-or-reveal/engine/lynching/werewolf-lynched.txt|] rolesInGameText :: Game -> Text rolesInGameText game = [iFile|variant/no-role-knowledge-or-reveal/engine/new-game/roles-in-game.txt|] where totalBalance = sumOf (players . roles . balance) game playerDevouredText :: Player -> Text playerDevouredText player = [iFile|variant/no-role-knowledge-or-reveal/engine/sunrise/player-devoured.txt|] playerPoisonedText :: Player -> Text playerPoisonedText player = [iFile|variant/no-role-knowledge-or-reveal/engine/sunrise/player-poisoned.txt|] playerTurnedToStoneText :: Player -> Text playerTurnedToStoneText player = [iFile|variant/no-role-knowledge-or-reveal/engine/sunrise/player-turned-to-stone.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleReveal/Command.hs0000644000000000000000000000324712727224206023005 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleReveal.Command Description : Suite of command messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of command messages used throughout the werewolf game for the 'NoRoleReveal' variant. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleReveal.Command ( -- * Choose playerShotText, -- * Ping nocturnalRolePingedText, werewolvesPingedText, -- * Quit callerQuitText, -- * Status currentNocturnalTurnText, deadPlayersText, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message playerShotText :: Player -> Text playerShotText player = [iFile|variant/no-role-reveal/command/choose/player-shot.txt|] nocturnalRolePingedText :: Role -> Text nocturnalRolePingedText _ = [iFile|variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt|] werewolvesPingedText :: Text werewolvesPingedText = [iFile|variant/no-role-reveal/command/ping/werewolves-pinged.txt|] callerQuitText :: Player -> Text callerQuitText caller = [iFile|variant/no-role-reveal/command/quit/caller-quit.txt|] currentNocturnalTurnText :: Game -> Text currentNocturnalTurnText _ = [iFile|variant/no-role-reveal/command/status/current-nocturnal-turn.txt|] deadPlayersText :: Game -> Text deadPlayersText game = [iFile|variant/no-role-reveal/command/status/dead-players.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/NoRoleReveal/Engine.hs0000644000000000000000000000375012733100663022630 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.NoRoleReveal.Engine Description : Suite of engine messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of engine messages used throughout the werewolf game for the 'NoRoleReveal' variant. -} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.NoRoleReveal.Engine ( -- * General playerBootedText, spitefulVillagerKilledText, -- * Lynching playerLynchedText, saintLynchedText, werewolfLynchedText, -- * Sunrise playerDevouredText, playerPoisonedText, playerTurnedToStoneText, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message playerBootedText :: Player -> Text playerBootedText player = [iFile|variant/no-role-reveal/engine/general/player-booted.txt|] spitefulVillagerKilledText :: Game -> Text spitefulVillagerKilledText game = [iFile|variant/no-role-reveal/engine/general/spiteful-villager-killed.txt|] playerLynchedText :: Player -> Text playerLynchedText player = [iFile|variant/no-role-reveal/engine/lynching/player-lynched.txt|] saintLynchedText :: [Player] -> Text saintLynchedText voters = [iFile|variant/no-role-reveal/engine/lynching/saint-lynched.txt|] werewolfLynchedText :: Player -> Text werewolfLynchedText werewolf = [iFile|variant/no-role-reveal/engine/lynching/werewolf-lynched.txt|] playerDevouredText :: Player -> Text playerDevouredText player = [iFile|variant/no-role-reveal/engine/sunrise/player-devoured.txt|] playerPoisonedText :: Player -> Text playerPoisonedText player = [iFile|variant/no-role-reveal/engine/sunrise/player-poisoned.txt|] playerTurnedToStoneText :: Player -> Text playerTurnedToStoneText player = [iFile|variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/Standard/Command.hs0000644000000000000000000002063112751722041022201 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.Standard.Command Description : Suite of command messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of command messages used throughout the werewolf game for the 'Standard' variant. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.Standard.Command ( -- * Boot callerVotedBootText, -- * Circle gameCircleText, -- * Choose playerShotText, -- * End gameEndedText, -- * Help druidsTurnText, gameDescriptionText, gameRulesText, globalCommandsText, helpCommandsText, hunterCommandsText, huntersTurnText, necromancerCommandsText, necromancersTurnText, oracleCommandsText, oraclesTurnText, orphanCommandsText, orphansTurnText, protectorCommandsText, protectorsTurnText, roleDescriptionText, scapegoatCommandsText, scapegoatsTurnText, seerCommandsText, seersTurnText, standardCommandsText, standardCycleText, statusCommandsText, sunriseText, sunsetText, variantDescriptionText, villageDrunksTurnText, villagesTurnText, werewolvesTurnText, winConditionText, witchCommandsText, witchsTurnText, -- * Ping diurnalRolePingedText, nocturnalRolePingedText, playerPingedText, villagePingedText, werewolvesPingedText, -- * Quit callerQuitText, -- * Raise necromancerRaisedDeadText, playerRaisedFromDeadText, -- * Status alivePlayersText, currentDiurnalTurnText, currentNocturnalTurnText, deadPlayersText, gameOverText, marksText, -- * Unvote callerRescindedVoteText, -- * Version engineVersionText, -- * Vote callerVotedDevourText, callerVotedLynchText, ) where import Control.Lens import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import qualified Data.Text as T import Data.Version import Game.Werewolf import Game.Werewolf.Message import qualified Game.Werewolf.Variant as Variant import Werewolf.Version callerVotedBootText :: Player -> Player -> Text callerVotedBootText caller target = [iFile|variant/standard/command/boot/player-voted-boot.txt|] gameCircleText :: [Player] -> Text gameCircleText players = [iFile|variant/standard/command/circle/game-circle.txt|] playerShotText :: Player -> Text playerShotText player = [iFile|variant/standard/command/choose/player-shot.txt|] gameEndedText :: Text -> Text gameEndedText callerName = [iFile|variant/standard/command/end/game-ended.txt|] druidsTurnText :: Text druidsTurnText = [iFile|variant/standard/command/help/druids-turn.txt|] gameDescriptionText :: Text gameDescriptionText = [iFile|variant/standard/command/help/game-description.txt|] gameRulesText :: Text gameRulesText = [iFile|variant/standard/command/help/rules.txt|] globalCommandsText :: Text globalCommandsText = [iFile|variant/standard/command/help/global-commands.txt|] helpCommandsText :: Text helpCommandsText = [iFile|variant/standard/command/help/help-commands.txt|] hunterCommandsText :: Text hunterCommandsText = [iFile|variant/standard/command/help/hunter-commands.txt|] huntersTurnText :: Text huntersTurnText = [iFile|variant/standard/command/help/hunters-turn.txt|] necromancerCommandsText :: Text necromancerCommandsText = [iFile|variant/standard/command/help/necromancer-commands.txt|] necromancersTurnText :: Text necromancersTurnText = [iFile|variant/standard/command/help/necromancers-turn.txt|] oracleCommandsText :: Text oracleCommandsText = [iFile|variant/standard/command/help/oracle-commands.txt|] oraclesTurnText :: Text oraclesTurnText = [iFile|variant/standard/command/help/oracles-turn.txt|] orphanCommandsText :: Text orphanCommandsText = [iFile|variant/standard/command/help/orphan-commands.txt|] orphansTurnText :: Text orphansTurnText = [iFile|variant/standard/command/help/orphans-turn.txt|] protectorCommandsText :: Text protectorCommandsText = [iFile|variant/standard/command/help/protector-commands.txt|] protectorsTurnText :: Text protectorsTurnText = [iFile|variant/standard/command/help/protectors-turn.txt|] roleDescriptionText :: Role -> Text roleDescriptionText role = [iFile|variant/standard/command/help/role.txt|] scapegoatCommandsText :: Text scapegoatCommandsText = [iFile|variant/standard/command/help/scapegoat-commands.txt|] scapegoatsTurnText :: Text scapegoatsTurnText = [iFile|variant/standard/command/help/scapegoats-turn.txt|] seerCommandsText :: Text seerCommandsText = [iFile|variant/standard/command/help/seer-commands.txt|] seersTurnText :: Text seersTurnText = [iFile|variant/standard/command/help/seers-turn.txt|] standardCommandsText :: Text standardCommandsText = [iFile|variant/standard/command/help/standard-commands.txt|] standardCycleText :: Text standardCycleText = [iFile|variant/standard/command/help/standard-cycle.txt|] statusCommandsText :: Text statusCommandsText = [iFile|variant/standard/command/help/status-commands.txt|] sunriseText :: Text sunriseText = [iFile|variant/standard/command/help/sunrise.txt|] sunsetText :: Text sunsetText = [iFile|variant/standard/command/help/sunset.txt|] variantDescriptionText :: Variant -> Text variantDescriptionText variant = [iFile|variant/standard/command/help/variant.txt|] villageDrunksTurnText :: Text villageDrunksTurnText = [iFile|variant/standard/command/help/village-drunks-turn.txt|] villagesTurnText :: Text villagesTurnText = [iFile|variant/standard/command/help/villages-turn.txt|] werewolvesTurnText :: Text werewolvesTurnText = [iFile|variant/standard/command/help/werewolves-turn.txt|] winConditionText :: Text winConditionText = [iFile|variant/standard/command/help/win-condition.txt|] witchCommandsText :: Text witchCommandsText = [iFile|variant/standard/command/help/witch-commands.txt|] witchsTurnText :: Text witchsTurnText = [iFile|variant/standard/command/help/witchs-turn.txt|] diurnalRolePingedText :: Role -> Text diurnalRolePingedText role = [iFile|variant/standard/command/ping/diurnal-role-pinged.txt|] nocturnalRolePingedText :: Role -> Text nocturnalRolePingedText role = [iFile|variant/standard/command/ping/nocturnal-role-pinged.txt|] playerPingedText :: Text playerPingedText = [iFile|variant/standard/command/ping/player-pinged.txt|] villagePingedText :: Text villagePingedText = [iFile|variant/standard/command/ping/village-pinged.txt|] werewolvesPingedText :: Text werewolvesPingedText = [iFile|variant/standard/command/ping/werewolves-pinged.txt|] callerQuitText :: Player -> Text callerQuitText caller = [iFile|variant/standard/command/quit/caller-quit.txt|] necromancerRaisedDeadText :: Game -> Text necromancerRaisedDeadText game = [iFile|variant/standard/command/raise/necromancer-raised-dead.txt|] where zombies' = game ^.. players . zombies playerRaisedFromDeadText :: Game -> Text playerRaisedFromDeadText game = [iFile|variant/standard/command/raise/player-raised-from-dead.txt|] where necromancer' = game ^?! players . necromancers marksText :: Game -> Text marksText game = [iFile|variant/standard/command/status/marks.txt|] where marks' = game ^.. players . traverse . filtered (\player -> player ^. name `elem` game ^. marks) alivePlayersText :: Game -> Text alivePlayersText game = [iFile|variant/standard/command/status/alive-players.txt|] currentDiurnalTurnText :: Game -> Text currentDiurnalTurnText game = [iFile|variant/standard/command/status/current-diurnal-turn.txt|] currentNocturnalTurnText :: Game -> Text currentNocturnalTurnText game = [iFile|variant/standard/command/status/current-nocturnal-turn.txt|] deadPlayersText :: Game -> Text deadPlayersText game = [iFile|variant/standard/command/status/dead-players.txt|] gameOverText :: Text gameOverText = [iFile|variant/standard/command/status/game-over.txt|] callerRescindedVoteText :: Player -> Text callerRescindedVoteText caller = [iFile|variant/standard/command/unvote/player-rescinded-vote.txt|] engineVersionText :: Text engineVersionText = [iFile|variant/standard/command/version/engine-version.txt|] callerVotedDevourText :: Player -> Player -> Text callerVotedDevourText caller target = [iFile|variant/standard/command/vote/player-made-devour-vote.txt|] callerVotedLynchText :: Player -> Player -> Text callerVotedLynchText caller target = [iFile|variant/standard/command/vote/player-made-lynch-vote.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/Standard/Engine.hs0000644000000000000000000002720712751254103022035 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.Standard.Engine Description : Suite of engine messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of engine messages used throughout the werewolf game for the 'Standard' variant. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.Standard.Engine ( -- * Druid's turn druidsTurnText, -- * General playerBootedText, playerKilledText, spitefulVillagerKilledText, zombiesReturnedToGraveText, -- * Game over allegianceWonText, dullahanWonText, everyoneLostText, fallenAngelWonText, necromancerWonText, playerContributedText, playerLostText, playerRolesText, playerWonText, -- * Hunter's turn huntersTurnPrivateText, huntersTurnPublicText, -- * Lynching jesterLynchedText, noPlayerLynchedText, playerLynchedText, saintLynchedText, scapegoatLynchedText, werewolfLynchedText, -- * Necromancer's turn necromancersTurnPrivateText, necromancersTurnPublicText, -- * New game beholderText, dullahanText, gameVariantText, newPlayerText, playersInGameText, rolesInGameText, trueVillagerText, -- * Oracle's turn oraclesTurnPrivateText, oraclesTurnPublicText, -- * Orphan's turn orphanJoinedWerewolvesGroupText, orphanJoinedWerewolvesPrivateText, orphansTurnPrivateText, orphansTurnPublicText, -- * Protector's turn protectorsTurnPrivateText, protectorsTurnPublicText, -- * Scapegoat's turn scapegoatsTurnText, scapegoatsTurnEndedText, -- * Seer's turn seersTurnPrivateText, seersTurnPublicText, alphaWolfSeenText, lycanSeenText, -- * Sunrise noPlayerDevouredText, playerDevouredText, playerDivinedText, playerPoisonedText, playerSeenText, sunriseText, playerTurnedToStoneText, -- * Sunset sunsetText, -- * Village Drunk's turn villageDrunkJoinedVillageText, villageDrunkJoinedWerewolvesGroupText, villageDrunkJoinedWerewolvesPrivateText, villageDrunksTurnText, -- * Village's turn villagesTurnText, -- * Werewolves' turn firstWerewolvesTurnText, werewolvesTurnPrivateText, werewolvesTurnPublicText, -- * Witch's turn healText, passText, poisonText, witchsTurnText, ) where import Control.Arrow import Control.Lens.Extra import Data.List.Extra import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import qualified Data.Text as T import Game.Werewolf import Game.Werewolf.Message druidsTurnText :: Text druidsTurnText = [iFile|variant/standard/engine/druids-turn/start.txt|] playerBootedText :: Player -> Text playerBootedText player = [iFile|variant/standard/engine/general/player-booted.txt|] playerKilledText :: Text playerKilledText = [iFile|variant/standard/engine/general/player-killed.txt|] spitefulVillagerKilledText :: Game -> Text spitefulVillagerKilledText game = [iFile|variant/standard/engine/general/spiteful-villager-killed.txt|] zombiesReturnedToGraveText :: Game -> Text zombiesReturnedToGraveText game = [iFile|variant/standard/engine/general/zombies-returned-to-grave.txt|] allegianceWonText :: Allegiance -> Text allegianceWonText allegiance = [iFile|variant/standard/engine/game-over/allegiance-won.txt|] dullahanWonText :: Game -> Text dullahanWonText game = [iFile|variant/standard/engine/game-over/dullahan-won.txt|] everyoneLostText :: Text everyoneLostText = [iFile|variant/standard/engine/game-over/everyone-lost.txt|] fallenAngelWonText :: Text fallenAngelWonText = [iFile|variant/standard/engine/game-over/fallen-angel-won.txt|] necromancerWonText :: Text necromancerWonText = [iFile|variant/standard/engine/game-over/necromancer-won.txt|] playerContributedText :: Text playerContributedText = [iFile|variant/standard/engine/game-over/player-contributed.txt|] playerLostText :: Text playerLostText = [iFile|variant/standard/engine/game-over/player-lost.txt|] playerRolesText :: Game -> Text playerRolesText game = [iFile|variant/standard/engine/game-over/player-roles.txt|] playerWonText :: Text playerWonText = [iFile|variant/standard/engine/game-over/player-won.txt|] huntersTurnPrivateText :: Text huntersTurnPrivateText = [iFile|variant/standard/engine/hunters-turn/start-private.txt|] huntersTurnPublicText :: Game -> Text huntersTurnPublicText game = [iFile|variant/standard/engine/hunters-turn/start-public.txt|] where hunter = game ^?! players . hunters jesterLynchedText :: Game -> Text jesterLynchedText game = [iFile|variant/standard/engine/lynching/jester-lynched.txt|] where jester = game ^?! players . jesters noPlayerLynchedText :: Text noPlayerLynchedText = [iFile|variant/standard/engine/lynching/no-player-lynched.txt|] playerLynchedText :: Player -> Text playerLynchedText player = [iFile|variant/standard/engine/lynching/player-lynched.txt|] saintLynchedText :: [Player] -> Text saintLynchedText voters = [iFile|variant/standard/engine/lynching/saint-lynched.txt|] scapegoatLynchedText :: Game -> Text scapegoatLynchedText game = [iFile|variant/standard/engine/lynching/scapegoat-lynched.txt|] where scapegoat = game ^?! players . scapegoats werewolfLynchedText :: Player -> Text werewolfLynchedText werewolf = [iFile|variant/standard/engine/lynching/werewolf-lynched.txt|] necromancersTurnPrivateText :: Text necromancersTurnPrivateText = [iFile|variant/standard/engine/necromancers-turn/start-private.txt|] necromancersTurnPublicText :: Text necromancersTurnPublicText = [iFile|variant/standard/engine/necromancers-turn/start-public.txt|] beholderText :: Game -> Text beholderText game = [iFile|variant/standard/engine/new-game/beholder.txt|] where seer = game ^?! players . seers dullahanText :: Game -> Text dullahanText game = [iFile|variant/standard/engine/new-game/dullahan.txt|] gameVariantText :: Game -> Text gameVariantText game = [iFile|variant/standard/engine/new-game/game-variant.txt|] newPlayerText :: Player -> Text newPlayerText player = [iFile|variant/standard/engine/new-game/new-player.txt|] playersInGameText :: Game -> Text playersInGameText game = [iFile|variant/standard/engine/new-game/players-in-game.txt|] rolesInGameText :: Game -> Text rolesInGameText game = [iFile|variant/standard/engine/new-game/roles-in-game.txt|] where roles = game ^.. players . traverse . role roleCounts = map (head &&& length) (groupSortOn humanise roles) totalBalance = sumOf (traverse . balance) roles trueVillagerText :: Game -> Text trueVillagerText game = [iFile|variant/standard/engine/new-game/true-villager.txt|] where trueVillager = game ^?! players . trueVillagers oraclesTurnPrivateText :: Text oraclesTurnPrivateText = [iFile|variant/standard/engine/oracles-turn/start-private.txt|] oraclesTurnPublicText :: Text oraclesTurnPublicText = [iFile|variant/standard/engine/oracles-turn/start-public.txt|] orphanJoinedWerewolvesGroupText :: Game -> Text orphanJoinedWerewolvesGroupText game = [iFile|variant/standard/engine/orphans-turn/player-joined-werewolves-group.txt|] where orphan = game ^?! players . orphans orphanJoinedWerewolvesPrivateText :: Game -> Text orphanJoinedWerewolvesPrivateText game = [iFile|variant/standard/engine/orphans-turn/player-joined-werewolves-private.txt|] where orphan = game ^?! players . orphans werewolves = game ^.. players . traverse . alive . filtered (is werewolf) \\ [orphan] orphansTurnPrivateText :: Text orphansTurnPrivateText = [iFile|variant/standard/engine/orphans-turn/start-private.txt|] orphansTurnPublicText :: Text orphansTurnPublicText = [iFile|variant/standard/engine/orphans-turn/start-public.txt|] protectorsTurnPrivateText :: Text protectorsTurnPrivateText = [iFile|variant/standard/engine/protectors-turn/start-private.txt|] protectorsTurnPublicText :: Text protectorsTurnPublicText = [iFile|variant/standard/engine/protectors-turn/start-public.txt|] scapegoatsTurnText :: Game -> Text scapegoatsTurnText game = [iFile|variant/standard/engine/scapegoats-turn/start.txt|] where scapegoat = game ^?! players . scapegoats scapegoatsTurnEndedText :: Game -> Text scapegoatsTurnEndedText game = [iFile|variant/standard/engine/scapegoats-turn/end.txt|] seersTurnPrivateText :: Text seersTurnPrivateText = [iFile|variant/standard/engine/seers-turn/start-private.txt|] seersTurnPublicText :: Text seersTurnPublicText = [iFile|variant/standard/engine/seers-turn/start-public.txt|] alphaWolfSeenText :: Player -> Text alphaWolfSeenText alphaWolf = [iFile|variant/standard/engine/sunrise/alpha-wolf-seen.txt|] lycanSeenText :: Player -> Text lycanSeenText lycan = [iFile|variant/standard/engine/sunrise/lycan-seen.txt|] noPlayerDevouredText :: Text noPlayerDevouredText = [iFile|variant/standard/engine/sunrise/no-player-devoured.txt|] playerDevouredText :: Player -> Text playerDevouredText player = [iFile|variant/standard/engine/sunrise/player-devoured.txt|] playerDivinedText :: Player -> Text playerDivinedText player = [iFile|variant/standard/engine/sunrise/player-divined.txt|] playerPoisonedText :: Player -> Text playerPoisonedText player = [iFile|variant/standard/engine/sunrise/player-poisoned.txt|] playerSeenText :: Player -> Text playerSeenText player = [iFile|variant/standard/engine/sunrise/player-seen.txt|] where article = if is loner player then "" else "the " :: Text sunriseText :: Text sunriseText = [iFile|variant/standard/engine/sunrise/start.txt|] playerTurnedToStoneText :: Player -> Text playerTurnedToStoneText player = [iFile|variant/standard/engine/sunrise/player-turned-to-stone.txt|] sunsetText :: Text sunsetText = [iFile|variant/standard/engine/sunset/start.txt|] villageDrunkJoinedVillageText :: Text villageDrunkJoinedVillageText = [iFile|variant/standard/engine/village-drunks-turn/player-joined-village.txt|] villageDrunkJoinedWerewolvesGroupText :: Game -> Text villageDrunkJoinedWerewolvesGroupText game = [iFile|variant/standard/engine/village-drunks-turn/player-joined-werewolves-group.txt|] where villageDrunk = game ^?! players . villageDrunks villageDrunkJoinedWerewolvesPrivateText :: Game -> Text villageDrunkJoinedWerewolvesPrivateText game = [iFile|variant/standard/engine/village-drunks-turn/player-joined-werewolves-private.txt|] where villageDrunk = game ^?! players . villageDrunks werewolves = game ^.. players . traverse . alive . filtered (is werewolf) \\ [villageDrunk] villageDrunksTurnText :: Text villageDrunksTurnText = [iFile|variant/standard/engine/village-drunks-turn/start.txt|] villagesTurnText :: Text villagesTurnText = [iFile|variant/standard/engine/villages-turn/start.txt|] firstWerewolvesTurnText :: Player -> Game -> Text firstWerewolvesTurnText player game = [iFile|variant/standard/engine/werewolves-turn/start-first-round-private.txt|] where werewolves = game ^.. players . traverse . alive . filtered (is werewolf) werewolvesTurnPrivateText :: Text werewolvesTurnPrivateText = [iFile|variant/standard/engine/werewolves-turn/start-private.txt|] werewolvesTurnPublicText :: Text werewolvesTurnPublicText = [iFile|variant/standard/engine/werewolves-turn/start-public.txt|] healText :: Game -> Text healText game = [iFile|variant/standard/engine/witchs-turn/heal.txt|] where victim = game ^?! votee passText :: Text passText = [iFile|variant/standard/engine/witchs-turn/pass.txt|] poisonText :: Text poisonText = [iFile|variant/standard/engine/witchs-turn/poison.txt|] witchsTurnText :: Text witchsTurnText = [iFile|variant/standard/engine/witchs-turn/start.txt|] werewolf-1.5.1.1/app/Game/Werewolf/Variant/Standard/Error.hs0000644000000000000000000001071712751254103021717 0ustar0000000000000000{-| Module : Game.Werewolf.Variant.Standard.Error Description : Suite of error messages used throughout the game. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com A 'Message' is used to relay information back to either all players or a single player. This module defines suite of error messages used throughout the werewolf game for the 'Standard' variant. -} {-# LANGUAGE QuasiQuotes #-} module Game.Werewolf.Variant.Standard.Error ( -- * Command -- ** Boot callerAlreadyVotedBootText, -- ** Choose callerCannotChooseJesterText, callerCannotChooseSelfText, callerCannotChooseZombieText, noTargetText, -- ** General callerCannotDoThatText, callerCannotDoThatRightNowText, callerDeadText, noGameRunningText, playerDoesNotExistText, targetDeadText, -- ** Heal callerAlreadyHealedText, -- ** Poison callerAlreadyPoisonedText, -- ** Protect callerCannotProtectSamePlayerText, -- ** Start gameAlreadyRunningText, playerCountTooLowText, playerNamesNotUniqueText, roleCountRestrictedText, roleDoesNotExistText, variantDoesNotExistText, -- ** Unvote callerNotVotedText, -- ** Vote callerAlreadyVotedText, callerCannotDevourAnotherWerewolfText, ) where import Data.String.Humanise import Data.String.Interpolate.Extra import Data.Text (Text) import Game.Werewolf callerAlreadyVotedBootText :: Player -> Text callerAlreadyVotedBootText target = [iFile|variant/standard/error/command/boot/caller-already-voted-boot.txt|] callerCannotChooseJesterText :: Text callerCannotChooseJesterText = [iFile|variant/standard/error/command/choose/caller-cannot-choose-jester.txt|] callerCannotChooseSelfText :: Text callerCannotChooseSelfText = [iFile|variant/standard/error/command/choose/caller-cannot-choose-self.txt|] callerCannotChooseZombieText :: Text callerCannotChooseZombieText = [iFile|variant/standard/error/command/choose/caller-cannot-choose-zombie.txt|] noTargetText :: Text noTargetText = [iFile|variant/standard/error/command/choose/no-target.txt|] callerCannotDoThatText :: Text callerCannotDoThatText = [iFile|variant/standard/error/command/general/caller-cannot-do-that.txt|] callerCannotDoThatRightNowText :: Text callerCannotDoThatRightNowText = [iFile|variant/standard/error/command/general/caller-cannot-do-that-right-now.txt|] callerDeadText :: Text callerDeadText = [iFile|variant/standard/error/command/general/caller-dead.txt|] noGameRunningText :: Text noGameRunningText = [iFile|variant/standard/error/command/general/no-game-running.txt|] playerDoesNotExistText :: Text -> Text playerDoesNotExistText name = [iFile|variant/standard/error/command/general/player-does-not-exist.txt|] targetDeadText :: Player -> Text targetDeadText target = [iFile|variant/standard/error/command/general/target-dead.txt|] callerAlreadyHealedText :: Text callerAlreadyHealedText = [iFile|variant/standard/error/command/heal/caller-already-healed.txt|] callerAlreadyPoisonedText :: Text callerAlreadyPoisonedText = [iFile|variant/standard/error/command/poison/caller-already-poisoned.txt|] callerCannotProtectSamePlayerText :: Text callerCannotProtectSamePlayerText = [iFile|variant/standard/error/command/protect/caller-cannot-protect-same-player.txt|] gameAlreadyRunningText :: Text gameAlreadyRunningText = [iFile|variant/standard/error/command/start/game-already-running.txt|] playerCountTooLowText :: Text playerCountTooLowText = [iFile|variant/standard/error/command/start/player-count-too-low.txt|] playerNamesNotUniqueText :: Text playerNamesNotUniqueText = [iFile|variant/standard/error/command/start/player-names-not-unique.txt|] roleCountRestrictedText :: Role -> Text roleCountRestrictedText role = [iFile|variant/standard/error/command/start/role-count-restricted.txt|] roleDoesNotExistText :: Text -> Text roleDoesNotExistText roleName = [iFile|variant/standard/error/command/start/role-does-not-exist.txt|] variantDoesNotExistText :: Text -> Text variantDoesNotExistText variantName = [iFile|variant/standard/error/command/start/variant-does-not-exist.txt|] callerNotVotedText :: Text callerNotVotedText = [iFile|variant/standard/error/command/unvote/caller-not-voted.txt|] callerAlreadyVotedText :: Text callerAlreadyVotedText = [iFile|variant/standard/error/command/vote/caller-already-voted.txt|] callerCannotDevourAnotherWerewolfText :: Text callerCannotDevourAnotherWerewolfText = [iFile|variant/standard/error/command/vote/caller-cannot-devour-werewolf.txt|] werewolf-1.5.1.1/app/Werewolf/Command/Boot.hs0000644000000000000000000000254612716050452017075 0ustar0000000000000000{-| Module : Werewolf.Command.Boot Description : Options and handler for the boot subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the boot subcommand. -} module Werewolf.Command.Boot ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Global import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = bootCommand callerName targetName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Choose.hs0000644000000000000000000000366512716050452017415 0ustar0000000000000000{-| Module : Werewolf.Command.Choose Description : Options and handler for the choose subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the choose subcommand. -} module Werewolf.Command.Choose ( -- * Options Options(..), -- * Handle handle, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Hunter as Hunter import Game.Werewolf.Command.Orphan as Orphan import Game.Werewolf.Command.Scapegoat as Scapegoat import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { args :: [Text] } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options args) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag command <- case game ^. stage of HuntersTurn1 -> return $ Hunter.chooseCommand callerName (head args) HuntersTurn2 -> return $ Hunter.chooseCommand callerName (head args) OrphansTurn -> return $ Orphan.chooseCommand callerName (head args) ScapegoatsTurn -> return $ Scapegoat.chooseCommand callerName args _ -> exitWith failure { messages = [playerCannotDoThatRightNowMessage callerName] } result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game, messages) -> writeOrDeleteGame tag game >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Circle.hs0000644000000000000000000000232312716050452017364 0ustar0000000000000000{-| Module : Werewolf.Command.Circle Description : Options and handler for the circle subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the circle subcommand. -} module Werewolf.Command.Circle ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Status import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { optIncludeDead :: Bool } deriving (Eq, Show) handle :: MonadIO m => Text -> Text -> Options -> m () handle callerName tag (Options includeDead) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = circleCommand callerName includeDead case runExcept . execWriterT $ execStateT (apply command) game of Left errorMessages -> exitWith failure { messages = errorMessages } Right messages -> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Divine.hs0000644000000000000000000000256012716050452017404 0ustar0000000000000000{-| Module : Werewolf.Command.Divine Description : Options and handler for the divine subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the divine subcommand. -} module Werewolf.Command.Divine ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Oracle import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = divineCommand callerName targetName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/End.hs0000644000000000000000000000203712727221700016671 0ustar0000000000000000{-| Module : Werewolf.Command.End Description : Handler for the end subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the end subcommand. -} module Werewolf.Command.End ( -- * Handle handle, ) where import Control.Lens import Control.Monad.Extra import Control.Monad.IO.Class import Data.Text (Text) -- TODO (hjw): remove Message.Engine import Game.Werewolf import Game.Werewolf.Message.Command import Game.Werewolf.Message.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: MonadIO m => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag unless (has (players . traverse . named callerName) game) $ exitWith failure { messages = [playerCannotDoThatMessage callerName] } deleteGame tag exitWith success { messages = [ gameEndedMessage callerName , playerRolesMessage game ] } werewolf-1.5.1.1/app/Werewolf/Command/Heal.hs0000644000000000000000000000226512716050452017041 0ustar0000000000000000{-| Module : Werewolf.Command.Heal Description : Handler for the heal subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the heal subcommand. -} module Werewolf.Command.Heal ( -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Witch import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: (MonadIO m, MonadRandom m) => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = healCommand callerName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Help.hs0000644000000000000000000000772212751722041017062 0ustar0000000000000000{-| Module : Werewolf.Command.Help Description : Options and handler for the help subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the help subcommand. -} module Werewolf.Command.Help ( -- * Options Options(..), Command(..), -- * Handle handle, ) where import Control.Lens import Control.Monad.Extra import Control.Monad.IO.Class import Data.Function import Data.List import Data.Maybe import Data.String.Humanise import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message.Command import Werewolf.System data Options = Options { argCommand :: Maybe Command } deriving (Eq, Show) data Command = Commands { optAll :: Bool } | Rules { optAll :: Bool } | Roles { optAll :: Bool } | Variants { optAll :: Bool } deriving (Eq, Show) handle :: MonadIO m => Text -> Text -> Options -> m () handle callerName tag (Options (Just (Commands optAll))) = do mGame <- getGame tag optAll exitWith success { messages = commandsMessages callerName mGame } handle callerName tag (Options (Just (Roles optAll))) = do mGame <- getGame tag optAll let mGame' = mGame >>= \game -> if has (variant . noRoleKnowledge) game then Nothing else Just game let roles' = sortBy (compare `on` humanise) . nub $ maybe allRoles (toListOf $ players . roles) mGame' exitWith success { messages = map (roleMessage callerName) roles' } handle callerName tag (Options (Just (Rules optAll))) = do mGame <- getGame tag optAll let mGame' = mGame >>= \game -> if has (variant . noRoleKnowledge) game then Nothing else Just game exitWith success { messages = rulesMessages callerName mGame' } handle callerName _ (Options Nothing) = exitWith success { messages = helpMessages callerName } handle callerName tag (Options (Just (Variants optAll))) = do mGame <- getGame tag optAll let variants = sortBy (compare `on` humanise) $ case mGame of Just game -> [game ^. variant] Nothing -> allVariants exitWith success { messages = map (variantMessage callerName) variants } commandsMessages :: Text -> Maybe Game -> [Message] commandsMessages callerName mGame = [ globalCommandsMessage callerName , statusCommandsMessage callerName , standardCommandsMessage callerName ] ++ [ hunterCommandsMessage callerName | isNothing mGame || has (players . hunters . named callerName) (fromJust mGame) ] ++ [ necromancerCommandsMessage callerName | isNothing mGame || has (players . necromancers . named callerName) (fromJust mGame) ] ++ [ oracleCommandsMessage callerName | isNothing mGame || has (players . oracles . named callerName) (fromJust mGame) ] ++ [ orphanCommandsMessage callerName | isNothing mGame || has (players . orphans . named callerName) (fromJust mGame) ] ++ [ protectorCommandsMessage callerName | isNothing mGame || has (players . protectors . named callerName) (fromJust mGame) ] ++ [ scapegoatCommandsMessage callerName | isNothing mGame || has (players . scapegoats . named callerName) (fromJust mGame) ] ++ [ seerCommandsMessage callerName | isNothing mGame || has (players . seers . named callerName) (fromJust mGame) ] ++ [ witchCommandsMessage callerName | isNothing mGame || has (players . witches . named callerName) (fromJust mGame) ] rulesMessages :: Text -> Maybe Game -> [Message] rulesMessages callerName mGame = [ rulesMessage callerName , stagesMessage callerName mGame ] helpMessages :: Text -> [Message] helpMessages callerName = [ gameDescriptionMessage callerName , helpCommandsMessage callerName ] getGame :: MonadIO m => Text -> Bool -> m (Maybe Game) getGame _ True = return Nothing getGame tag _ = ifM (doesGameExist tag) (Just <$> readGame tag) (return Nothing) werewolf-1.5.1.1/app/Werewolf/Command/Interpret.hs0000644000000000000000000000062612716050452020143 0ustar0000000000000000{-| Module : Werewolf.Command.Interpret Description : Options for the interpret subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options for the interpret subcommand. -} module Werewolf.Command.Interpret ( -- * Options Options(..), ) where import Data.Text (Text) data Options = Options { args :: [Text] } deriving (Eq, Show) werewolf-1.5.1.1/app/Werewolf/Command/Pass.hs0000644000000000000000000000306612751254103017074 0ustar0000000000000000{-| Module : Werewolf.Command.Pass Description : Handler for the pass subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the pass subcommand. -} module Werewolf.Command.Pass ( -- * Handle handle, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Necromancer as Necromancer import Game.Werewolf.Command.Witch as Witch import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: (MonadIO m, MonadRandom m) => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag command <- case game ^. stage of NecromancersTurn -> return $ Necromancer.passCommand callerName WitchsTurn -> return $ Witch.passCommand callerName _ -> exitWith failure { messages = [playerCannotDoThatRightNowMessage callerName] } result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Ping.hs0000644000000000000000000000202212716050452017054 0ustar0000000000000000{-| Module : Werewolf.Command.Ping Description : Handler for the ping subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the ping subcommand. -} module Werewolf.Command.Ping ( -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Status import Game.Werewolf.Message.Error import Werewolf.System handle :: MonadIO m => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = pingCommand callerName case runExcept . execWriterT $ execStateT (apply command) game of Left errorMessages -> exitWith failure { messages = errorMessages } Right messages -> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Poison.hs0000644000000000000000000000255712716050452017443 0ustar0000000000000000{-| Module : Werewolf.Command.Poison Description : Options and handler for the poison subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the poison subcommand. -} module Werewolf.Command.Poison ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Witch import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = poisonCommand callerName targetName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Protect.hs0000644000000000000000000000257012716050452017607 0ustar0000000000000000{-| Module : Werewolf.Command.Protect Description : Options and handler for the protect subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the protect subcommand. -} module Werewolf.Command.Protect ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Protector import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = protectCommand callerName targetName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Quit.hs0000644000000000000000000000226612716050452017113 0ustar0000000000000000{-| Module : Werewolf.Command.Quit Description : Handler for the quit subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the quit subcommand. -} module Werewolf.Command.Quit ( -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Global import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: (MonadIO m, MonadRandom m) => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = quitCommand callerName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Raise.hs0000644000000000000000000000230012751254103017217 0ustar0000000000000000{-| Module : Werewolf.Command.Raise Description : Handler for the raise subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the raise subcommand. -} module Werewolf.Command.Raise ( -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Necromancer import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: (MonadIO m, MonadRandom m) => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = raiseCommand callerName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/See.hs0000644000000000000000000000253712716050452016706 0ustar0000000000000000{-| Module : Werewolf.Command.See Description : Options and handler for the see subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the see subcommand. -} module Werewolf.Command.See ( -- * Options Options(..), -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Seer import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = seeCommand callerName targetName result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Start.hs0000644000000000000000000001024312751722041017257 0ustar0000000000000000{-| Module : Werewolf.Command.Start Description : Options and handler for the start subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the start subcommand. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Werewolf.Command.Start ( -- * Options Options(..), ExtraRoles(..), -- * Handle handle, ) where import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Engine import Game.Werewolf.Message.Error import qualified Game.Werewolf.Variant as Variant import System.Random.Shuffle import Werewolf.System data Options = Options { optExtraRoles :: ExtraRoles , optVariant :: Text , argPlayers :: [Text] } deriving (Eq, Show) data ExtraRoles = None | Random | Use [Text] deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options extraRoles variant playerNames) = do whenM (doesGameExist tag &&^ (hasn't (stage . _GameOver) <$> readGame tag)) $ exitWith failure { messages = [gameAlreadyRunningMessage callerName] } result <- runExceptT $ do extraRoles' <- case extraRoles of None -> return [] Random -> randomExtraRoles $ length playerNames Use roleNames -> useExtraRoles callerName roleNames variant' <- useVariant callerName variant let defaultVillagerRole = if is spitefulVillage variant' then spitefulVillagerRole else simpleVillagerRole let roles = padRoles extraRoles' (length playerNames + 1) defaultVillagerRole players <- createPlayers (callerName:playerNames) <$> shuffleM roles runWriterT $ startGame callerName variant' players >>= execStateT checkStage case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game, messages) -> writeOrDeleteGame tag game >> exitWith success { messages = messages } randomExtraRoles :: MonadRandom m => Int -> m [Role] randomExtraRoles n = do let minimum = n `div` 4 + 1 let maximum = n `div` 3 + 1 count <- getRandomR (minimum, maximum) take count <$> shuffleM restrictedRoles useExtraRoles :: MonadError [Message] m => Text -> [Text] -> m [Role] useExtraRoles callerName roleNames = forM roleNames $ \roleName -> case findRoleByTag roleName of Just role -> return role Nothing -> throwError [roleDoesNotExistMessage callerName roleName] findRoleByTag :: Text -> Maybe Role findRoleByTag tag' = restrictedRoles ^? traverse . filteredBy tag tag' useVariant :: MonadError [Message] m => Text -> Text -> m Variant useVariant callerName variantName = case findVariantByTag variantName of Just variant -> return variant Nothing -> throwError [variantDoesNotExistMessage callerName variantName] findVariantByTag :: Text -> Maybe Variant findVariantByTag tag' = allVariants ^? traverse . filteredBy Variant.tag tag' padRoles :: [Role] -> Int -> Role -> [Role] padRoles roles n defaultVillagerRole = roles ++ villagerRoles ++ simpleWerewolfRoles where goal = 3 m = max (n - length roles) 0 startingBalance = sumOf (traverse . balance) roles simpleWerewolfBalance = simpleWerewolfRole ^. balance -- Little magic here to calculate how many Werewolves and Villagers we want. -- This tries to ensure that the balance of the game is between -3 and 2. simpleWerewolvesCount = (goal - m - startingBalance) `div` (simpleWerewolfBalance - 1) + 1 villagersCount = m - simpleWerewolvesCount -- N.B., if roles is quite unbalanced then one list will be empty. villagerRoles = replicate villagersCount defaultVillagerRole simpleWerewolfRoles = replicate simpleWerewolvesCount simpleWerewolfRole createPlayers :: [Text] -> [Role] -> [Player] createPlayers = zipWith newPlayer werewolf-1.5.1.1/app/Werewolf/Command/Status.hs0000644000000000000000000000203412724464734017460 0ustar0000000000000000{-| Module : Werewolf.Command.Status Description : Handler for the status subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the status subcommand. -} module Werewolf.Command.Status ( -- * Handle handle, ) where import Control.Monad.Except import Control.Monad.Extra import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Status import Game.Werewolf.Message.Error import Werewolf.System handle :: MonadIO m => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag let command = statusCommand callerName case runExcept . execWriterT $ execStateT (apply command) game of Left errorMessages -> exitWith failure { messages = errorMessages } Right messages -> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Unvote.hs0000644000000000000000000000306012716050452017442 0ustar0000000000000000{-| Module : Werewolf.Command.Unvote Description : Handler for the unvote subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the unvote subcommand. -} module Werewolf.Command.Unvote ( -- * Handle handle, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Villager as Villager import Game.Werewolf.Command.Werewolf as Werewolf import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System handle :: (MonadIO m, MonadRandom m) => Text -> Text -> m () handle callerName tag = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag command <- case game ^. stage of VillagesTurn -> return $ Villager.unvoteCommand callerName WerewolvesTurn -> return $ Werewolf.unvoteCommand callerName _ -> exitWith failure { messages = [playerCannotDoThatRightNowMessage callerName] } result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Command/Version.hs0000644000000000000000000000102212716050452017603 0ustar0000000000000000{-| Module : Werewolf.Command.Version Description : Handler for the version subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Handler for the start subcommand. -} module Werewolf.Command.Version ( -- * Handle handle, ) where import Control.Monad.Except import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Message.Command handle :: MonadIO m => Text -> m () handle callerName = exitWith success { messages = [engineVersionMessage callerName] } werewolf-1.5.1.1/app/Werewolf/Command/Vote.hs0000644000000000000000000000333712716050452017106 0ustar0000000000000000{-| Module : Werewolf.Command.Vote Description : Options and handler for the vote subcommand. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Options and handler for the vote subcommand. -} module Werewolf.Command.Vote ( -- * Options Options(..), -- * Handle handle, ) where import Control.Lens import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random import Control.Monad.State import Control.Monad.Writer import Data.Text (Text) import Game.Werewolf import Game.Werewolf.Command import Game.Werewolf.Command.Villager as Villager import Game.Werewolf.Command.Werewolf as Werewolf import Game.Werewolf.Engine import Game.Werewolf.Message.Error import Werewolf.System data Options = Options { argTarget :: Text } deriving (Eq, Show) handle :: (MonadIO m, MonadRandom m) => Text -> Text -> Options -> m () handle callerName tag (Options targetName) = do unlessM (doesGameExist tag) $ exitWith failure { messages = [noGameRunningMessage callerName] } game <- readGame tag command <- case game ^. stage of VillagesTurn -> return $ Villager.voteCommand callerName targetName WerewolvesTurn -> return $ Werewolf.voteCommand callerName targetName _ -> exitWith failure { messages = [playerCannotDoThatRightNowMessage callerName] } result <- runExceptT . runWriterT $ execStateT (apply command >> checkStage >> checkGameOver) game case result of Left errorMessages -> exitWith failure { messages = errorMessages } Right (game', messages) -> writeOrDeleteGame tag game' >> exitWith success { messages = messages } werewolf-1.5.1.1/app/Werewolf/Options.hs0000644000000000000000000002010112755163323016237 0ustar0000000000000000{-| Module : Werewolf.Options Description : Optparse utilities. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Optparse utilities. -} {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Werewolf.Options ( -- * Options Options(..), Command(..), -- * Optparse werewolfPrefs, werewolfInfo, werewolf, ) where import Control.Lens #if MIN_VERSION_optparse_applicative(0,13,0) import Data.Monoid #endif import Data.Text (Text) import qualified Data.Text as T import Data.Version (showVersion) import Game.Werewolf.Variant import Options.Applicative import qualified Werewolf.Command.Boot as Boot import qualified Werewolf.Command.Choose as Choose import qualified Werewolf.Command.Circle as Circle import qualified Werewolf.Command.Divine as Divine import qualified Werewolf.Command.Help as Help import qualified Werewolf.Command.Interpret as Interpret import qualified Werewolf.Command.Poison as Poison import qualified Werewolf.Command.Protect as Protect import qualified Werewolf.Command.See as See import qualified Werewolf.Command.Start as Start import qualified Werewolf.Command.Vote as Vote import qualified Werewolf.Version as This data Options = Options { optCaller :: Text , optTag :: Text , argCommand :: Command } deriving (Eq, Show) data Command = Boot Boot.Options | Choose Choose.Options | Circle Circle.Options | Divine Divine.Options | End | Heal | Help Help.Options | Interpret Interpret.Options | Pass | Ping | Poison Poison.Options | Protect Protect.Options | Quit | Raise | See See.Options | Start Start.Options | Status | Unvote | Version | Vote Vote.Options deriving (Eq, Show) -- | The default preferences. -- Limits the help output to 100 columns. werewolfPrefs :: ParserPrefs #if MIN_VERSION_optparse_applicative(0,13,0) werewolfPrefs = prefs $ columns 100 <> showHelpOnEmpty #else werewolfPrefs = prefs $ columns 100 #endif -- | An optparse parser of a werewolf command. werewolfInfo :: ParserInfo Options werewolfInfo = info (infoOptions <*> werewolf) (fullDesc <> progDesc') where infoOptions = helper <*> version version = infoOption ("Version " ++ showVersion This.version) $ mconcat [ long "version", short 'V', hidden , help "Show this binary's version" ] progDesc' = progDesc "Runs the given command from the perspective of the caller" -- | An options parser. werewolf :: Parser Options werewolf = Options <$> fmap T.pack (strOption $ mconcat [ long "caller", metavar "PLAYER" , help "Specify the calling player's name" ]) <*> fmap T.pack (strOption $ mconcat [ long "tag", metavar "TAG" , help "Specify the game tag (for running multiple games at once)" ]) <*> subparser (mconcat [ command "boot" $ info (helper <*> boot) (fullDesc <> progDesc "Vote to boot a player") , command "choose" $ info (helper <*> choose) (fullDesc <> progDesc "Choose an allegiance or player(s)") , command "circle" $ info (helper <*> circle) (fullDesc <> progDesc "Get the game circle") , command "divine" $ info (helper <*> divine) (fullDesc <> progDesc "Divine a player's role") , command "end" $ info (helper <*> end) (fullDesc <> progDesc "End the current game") , command "heal" $ info (helper <*> heal) (fullDesc <> progDesc "Heal the devoured player") , command "help" $ info (helper <*> help_) (fullDesc <> progDesc "Help documents") , command "interpret" $ info (helper <*> interpret) (fullDesc <> noIntersperse <> progDesc "Interpret a command") , command "pass" $ info (helper <*> pass) (fullDesc <> progDesc "Pass") , command "ping" $ info (helper <*> ping) (fullDesc <> progDesc "Ping the status of the current game publicly") , command "poison" $ info (helper <*> poison) (fullDesc <> progDesc "Poison a player") , command "protect" $ info (helper <*> protect) (fullDesc <> progDesc "Protect a player") , command "quit" $ info (helper <*> quit) (fullDesc <> progDesc "Quit the current game") , command "raise" $ info (helper <*> raise) (fullDesc <> progDesc "Raise the dead as Zombies") , command "see" $ info (helper <*> see) (fullDesc <> progDesc "See a player's allegiance") , command "start" $ info (helper <*> start) (fullDesc <> progDesc "Start a new game") , command "status" $ info (helper <*> status) (fullDesc <> progDesc "Get the status of the current game") , command "unvote" $ info (helper <*> unvote) (fullDesc <> progDesc "Rescind a vote") , command "version" $ info (helper <*> version) (fullDesc <> progDesc "Show this engine's version") , command "vote" $ info (helper <*> vote) (fullDesc <> progDesc "Vote against a player") ]) boot :: Parser Command boot = Boot . Boot.Options <$> playerArgument choose :: Parser Command choose = Choose . Choose.Options . map T.pack <$> some (strArgument $ metavar "(ALLEGIANCE | PLAYER...)") circle :: Parser Command circle = Circle . Circle.Options <$> switch (mconcat [ long "include-dead", short 'a' , help "Include dead players" ]) divine :: Parser Command divine = Divine . Divine.Options <$> playerArgument end :: Parser Command end = pure End heal :: Parser Command heal = pure Heal help_ :: Parser Command help_ = Help . Help.Options <$> optional (subparser $ mconcat [ command "commands" $ info (Help.Commands <$> allOption) (fullDesc <> progDesc "Print the in-game commands") , command "roles" $ info (Help.Roles <$> allOption) (fullDesc <> progDesc "Print the roles and their descriptions") , command "rules" $ info (Help.Rules <$> allOption) (fullDesc <> progDesc "Print the game rules") , command "variants" $ info (Help.Variants <$> allOption) (fullDesc <> progDesc "Print the variants and their descriptions") ]) where allOption = switch $ long "all" <> short 'a' interpret :: Parser Command interpret = Interpret . Interpret.Options <$> many (T.pack <$> strArgument (metavar "-- COMMAND ARG...")) pass :: Parser Command pass = pure Pass ping :: Parser Command ping = pure Ping poison :: Parser Command poison = Poison . Poison.Options <$> playerArgument protect :: Parser Command protect = Protect . Protect.Options <$> playerArgument quit :: Parser Command quit = pure Quit raise :: Parser Command raise = pure Raise see :: Parser Command see = See . See.Options <$> playerArgument start :: Parser Command start = fmap Start $ Start.Options <$> (extraRolesOption <|> randomExtraRolesOption) <*> fmap T.pack (strOption $ mconcat [ long "variant", short 'v', metavar "VARIANT", value standardVariantTag, showDefaultWith $ const standardVariantTag, help "Specify the game variant" ]) <*> some (T.pack <$> strArgument (metavar "PLAYER...")) where extraRolesOption = fmap (Start.Use . filter (/= T.empty) . T.splitOn "," . T.pack) (strOption $ mconcat [ long "extra-roles", short 'e', metavar "ROLE,..." , value [] , help "Specify the extra roles to use" ]) randomExtraRolesOption = flag Start.None Start.Random $ mconcat [ long "random-extra-roles", short 'r' , help "Use random extra roles" ] standardVariantTag = T.unpack $ standardVariant ^. tag status :: Parser Command status = pure Status unvote :: Parser Command unvote = pure Unvote version :: Parser Command version = pure Version vote :: Parser Command vote = Vote . Vote.Options <$> playerArgument playerArgument :: Parser Text playerArgument = T.pack <$> strArgument (metavar "PLAYER") werewolf-1.5.1.1/app/Werewolf/System.hs0000644000000000000000000000537712727250373016113 0ustar0000000000000000{-| Module : Werewolf.System Description : System functions for working with a game state file. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com This module defines a few system functions for working with a game state file. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} module Werewolf.System ( -- * Game -- ** Creating anew startGame, -- ** Working with an existing filePath, readGame, writeGame, deleteGame, writeOrDeleteGame, doesGameExist, ) where import Control.Lens.Extra hiding (cons) import Control.Monad.Except import Control.Monad.Random import Control.Monad.Writer import Data.List import Data.Text (Text) import qualified Data.Text as T import Game.Werewolf import Game.Werewolf.Message.Engine import Game.Werewolf.Message.Error import Prelude hiding (round) import System.Directory import System.FilePath import System.Random.Shuffle startGame :: (MonadError [Message] m, MonadRandom m, MonadWriter [Message] m) => Text -> Variant -> [Player] -> m Game startGame callerName variant players' = do when (playerNames /= nub playerNames) $ throwError [playerNamesMustBeUniqueMessage callerName] when (length players' < 7) $ throwError [mustHaveAtLeast7PlayersMessage callerName] forM_ restrictedRoles $ \role -> when (length (players' ^.. roles . only role) > 1) $ throwError [roleCountRestrictedMessage callerName role] let game = newGame variant players' game' <- (\marks' -> game & marks .~ marks' ^.. names) <$> randomMarks game tell $ newGameMessages game' return game' where playerNames = players' ^.. names randomMarks :: MonadRandom m => Game -> m [Player] randomMarks game = do let count = length potentialMarks `div` 3 + 1 take count <$> shuffleM potentialMarks where potentialMarks = game ^.. players . traverse . filtered (isn't dullahan) filePath :: MonadIO m => Text -> m FilePath filePath tag = ( ".werewolf" T.unpack tag) <$> liftIO getHomeDirectory readGame :: MonadIO m => Text -> m Game readGame tag = liftIO . fmap read $ filePath tag >>= readFile writeGame :: MonadIO m => Text -> Game -> m () writeGame tag game = liftIO $ filePath tag >>= \tag -> do createDirectoryIfMissing True (dropFileName tag) writeFile tag (show game) deleteGame :: MonadIO m => Text -> m () deleteGame tag = liftIO $ filePath tag >>= removeFile writeOrDeleteGame :: MonadIO m => Text -> Game -> m () writeOrDeleteGame tag game | has (stage . _GameOver) game = deleteGame tag | otherwise = writeGame tag game doesGameExist :: MonadIO m => Text -> m Bool doesGameExist tag = liftIO $ filePath tag >>= doesFileExist werewolf-1.5.1.1/app/Werewolf/Version.hs0000644000000000000000000000050312716050452016230 0ustar0000000000000000{-| Module : Werewolf.Version Description : Haskell constant of the binary version. Copyright : (c) Henry J. Wylde, 2016 License : BSD3 Maintainer : public@hjwylde.com Haskell constant of the binary version. -} module Werewolf.Version ( -- * Version version, ) where import Paths_werewolf (version) werewolf-1.5.1.1/LICENSE0000644000000000000000000000270212713342045012704 0ustar0000000000000000Copyright (c) 2016, Henry J. Wylde All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of werewolf nor werewolf's contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. werewolf-1.5.1.1/Setup.hs0000644000000000000000000000005612716050452013334 0ustar0000000000000000import Distribution.Simple main = defaultMain werewolf-1.5.1.1/werewolf.cabal0000644000000000000000000004435612755167000014532 0ustar0000000000000000name: werewolf version: 1.5.1.1 author: Henry J. Wylde maintainer: public@hjwylde.com homepage: https://github.com/hjwylde/werewolf synopsis: A game engine for playing werewolf within an arbitrary chat client description: A game engine for playing werewolf within an arbitrary chat client. Werewolf is a well known social party game, commonly also called Mafia. See the for a rundown on its gameplay and history. license: BSD3 license-file: LICENSE cabal-version: >= 1.10 category: Game build-type: Simple extra-source-files: CHANGELOG.md README.md variant/no-role-knowledge/description.txt variant/no-role-knowledge/name.txt variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt variant/no-role-knowledge/command/ping/werewolves-pinged.txt variant/no-role-knowledge/command/status/current-nocturnal-turn.txt variant/no-role-knowledge/engine/new-game/roles-in-game.txt variant/no-role-knowledge-or-reveal/description.txt variant/no-role-knowledge-or-reveal/name.txt variant/no-role-knowledge-or-reveal/command/choose/player-shot.txt variant/no-role-knowledge-or-reveal/command/ping/nocturnal-role-pinged.txt variant/no-role-knowledge-or-reveal/command/ping/werewolves-pinged.txt variant/no-role-knowledge-or-reveal/command/quit/caller-quit.txt variant/no-role-knowledge-or-reveal/command/status/current-nocturnal-turn.txt variant/no-role-knowledge-or-reveal/command/status/dead-players.txt variant/no-role-knowledge-or-reveal/engine/general/player-booted.txt variant/no-role-knowledge-or-reveal/engine/general/spiteful-villager-killed.txt variant/no-role-knowledge-or-reveal/engine/lynching/player-lynched.txt variant/no-role-knowledge-or-reveal/engine/lynching/saint-lynched.txt variant/no-role-knowledge-or-reveal/engine/lynching/werewolf-lynched.txt variant/no-role-knowledge-or-reveal/engine/new-game/roles-in-game.txt variant/no-role-knowledge-or-reveal/engine/sunrise/player-devoured.txt variant/no-role-knowledge-or-reveal/engine/sunrise/player-poisoned.txt variant/no-role-knowledge-or-reveal/engine/sunrise/player-turned-to-stone.txt variant/no-role-reveal/description.txt variant/no-role-reveal/name.txt variant/no-role-reveal/command/choose/player-shot.txt variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt variant/no-role-reveal/command/ping/werewolves-pinged.txt variant/no-role-reveal/command/quit/caller-quit.txt variant/no-role-reveal/command/status/current-nocturnal-turn.txt variant/no-role-reveal/command/status/dead-players.txt variant/no-role-reveal/engine/general/player-booted.txt variant/no-role-reveal/engine/general/spiteful-villager-killed.txt variant/no-role-reveal/engine/lynching/player-lynched.txt variant/no-role-reveal/engine/lynching/saint-lynched.txt variant/no-role-reveal/engine/lynching/werewolf-lynched.txt variant/no-role-reveal/engine/sunrise/player-devoured.txt variant/no-role-reveal/engine/sunrise/player-poisoned.txt variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt variant/spiteful-village/description.txt variant/spiteful-village/name.txt variant/standard/description.txt variant/standard/name.txt variant/standard/command/boot/player-voted-boot.txt variant/standard/command/choose/player-shot.txt variant/standard/command/circle/game-circle.txt variant/standard/command/end/game-ended.txt variant/standard/command/help/druids-turn.txt variant/standard/command/help/game-description.txt variant/standard/command/help/global-commands.txt variant/standard/command/help/help-commands.txt variant/standard/command/help/hunter-commands.txt variant/standard/command/help/hunters-turn.txt variant/standard/command/help/necromancer-commands.txt variant/standard/command/help/necromancers-turn.txt variant/standard/command/help/oracle-commands.txt variant/standard/command/help/oracles-turn.txt variant/standard/command/help/orphan-commands.txt variant/standard/command/help/orphans-turn.txt variant/standard/command/help/protector-commands.txt variant/standard/command/help/protectors-turn.txt variant/standard/command/help/role.txt variant/standard/command/help/rules.txt variant/standard/command/help/scapegoat-commands.txt variant/standard/command/help/scapegoats-turn.txt variant/standard/command/help/seer-commands.txt variant/standard/command/help/seers-turn.txt variant/standard/command/help/standard-commands.txt variant/standard/command/help/standard-cycle.txt variant/standard/command/help/status-commands.txt variant/standard/command/help/sunrise.txt variant/standard/command/help/sunset.txt variant/standard/command/help/variant.txt variant/standard/command/help/village-drunks-turn.txt variant/standard/command/help/villages-turn.txt variant/standard/command/help/werewolves-turn.txt variant/standard/command/help/win-condition.txt variant/standard/command/help/witch-commands.txt variant/standard/command/help/witchs-turn.txt variant/standard/command/ping/diurnal-role-pinged.txt variant/standard/command/ping/nocturnal-role-pinged.txt variant/standard/command/ping/player-pinged.txt variant/standard/command/ping/village-pinged.txt variant/standard/command/ping/werewolves-pinged.txt variant/standard/command/quit/caller-quit.txt variant/standard/command/raise/necromancer-raised-dead.txt variant/standard/command/raise/player-raised-from-dead.txt variant/standard/command/status/alive-players.txt variant/standard/command/status/current-diurnal-turn.txt variant/standard/command/status/current-nocturnal-turn.txt variant/standard/command/status/dead-players.txt variant/standard/command/status/game-over.txt variant/standard/command/status/marks.txt variant/standard/command/unvote/player-rescinded-vote.txt variant/standard/command/version/engine-version.txt variant/standard/command/vote/player-made-devour-vote.txt variant/standard/command/vote/player-made-lynch-vote.txt variant/standard/engine/druids-turn/start.txt variant/standard/engine/game-over/allegiance-won.txt variant/standard/engine/game-over/dullahan-won.txt variant/standard/engine/game-over/everyone-lost.txt variant/standard/engine/game-over/fallen-angel-won.txt variant/standard/engine/game-over/necromancer-won.txt variant/standard/engine/game-over/player-contributed.txt variant/standard/engine/game-over/player-lost.txt variant/standard/engine/game-over/player-roles.txt variant/standard/engine/game-over/player-won.txt variant/standard/engine/general/player-booted.txt variant/standard/engine/general/player-killed.txt variant/standard/engine/general/spiteful-villager-killed.txt variant/standard/engine/general/zombies-returned-to-grave.txt variant/standard/engine/hunters-turn/start-private.txt variant/standard/engine/hunters-turn/start-public.txt variant/standard/engine/lynching/jester-lynched.txt variant/standard/engine/lynching/no-player-lynched.txt variant/standard/engine/lynching/player-lynched.txt variant/standard/engine/lynching/saint-lynched.txt variant/standard/engine/lynching/scapegoat-lynched.txt variant/standard/engine/lynching/werewolf-lynched.txt variant/standard/engine/necromancers-turn/start-private.txt variant/standard/engine/necromancers-turn/start-public.txt variant/standard/engine/new-game/beholder.txt variant/standard/engine/new-game/dullahan.txt variant/standard/engine/new-game/game-variant.txt variant/standard/engine/new-game/new-player.txt variant/standard/engine/new-game/players-in-game.txt variant/standard/engine/new-game/roles-in-game.txt variant/standard/engine/new-game/true-villager.txt variant/standard/engine/oracles-turn/start-private.txt variant/standard/engine/oracles-turn/start-public.txt variant/standard/engine/orphans-turn/player-joined-werewolves-group.txt variant/standard/engine/orphans-turn/player-joined-werewolves-private.txt variant/standard/engine/orphans-turn/start-private.txt variant/standard/engine/orphans-turn/start-public.txt variant/standard/engine/protectors-turn/start-private.txt variant/standard/engine/protectors-turn/start-public.txt variant/standard/engine/scapegoats-turn/end.txt variant/standard/engine/scapegoats-turn/start.txt variant/standard/engine/seers-turn/start-private.txt variant/standard/engine/seers-turn/start-public.txt variant/standard/engine/sunrise/alpha-wolf-seen.txt variant/standard/engine/sunrise/lycan-seen.txt variant/standard/engine/sunrise/no-player-devoured.txt variant/standard/engine/sunrise/player-devoured.txt variant/standard/engine/sunrise/player-divined.txt variant/standard/engine/sunrise/player-poisoned.txt variant/standard/engine/sunrise/player-seen.txt variant/standard/engine/sunrise/player-turned-to-stone.txt variant/standard/engine/sunrise/start.txt variant/standard/engine/sunset/start.txt variant/standard/engine/village-drunks-turn/player-joined-village.txt variant/standard/engine/village-drunks-turn/player-joined-werewolves-group.txt variant/standard/engine/village-drunks-turn/player-joined-werewolves-private.txt variant/standard/engine/village-drunks-turn/start.txt variant/standard/engine/villages-turn/start.txt variant/standard/engine/werewolves-turn/start-first-round-private.txt variant/standard/engine/werewolves-turn/start-private.txt variant/standard/engine/werewolves-turn/start-public.txt variant/standard/engine/witchs-turn/heal.txt variant/standard/engine/witchs-turn/pass.txt variant/standard/engine/witchs-turn/poison.txt variant/standard/engine/witchs-turn/start.txt variant/standard/error/command/boot/caller-already-voted-boot.txt variant/standard/error/command/choose/caller-cannot-choose-jester.txt variant/standard/error/command/choose/caller-cannot-choose-self.txt variant/standard/error/command/choose/caller-cannot-choose-zombie.txt variant/standard/error/command/choose/no-target.txt variant/standard/error/command/general/caller-cannot-do-that-right-now.txt variant/standard/error/command/general/caller-cannot-do-that.txt variant/standard/error/command/general/caller-dead.txt variant/standard/error/command/general/no-game-running.txt variant/standard/error/command/general/player-does-not-exist.txt variant/standard/error/command/general/target-dead.txt variant/standard/error/command/heal/caller-already-healed.txt variant/standard/error/command/poison/caller-already-poisoned.txt variant/standard/error/command/protect/caller-cannot-protect-same-player.txt variant/standard/error/command/start/game-already-running.txt variant/standard/error/command/start/player-count-too-low.txt variant/standard/error/command/start/player-names-not-unique.txt variant/standard/error/command/start/role-count-restricted.txt variant/standard/error/command/start/role-does-not-exist.txt variant/standard/error/command/start/variant-does-not-exist.txt variant/standard/error/command/unvote/caller-not-voted.txt variant/standard/error/command/vote/caller-already-voted.txt variant/standard/error/command/vote/caller-cannot-devour-werewolf.txt variant/standard/role/alpha-wolf/description.txt variant/standard/role/alpha-wolf/name.txt variant/standard/role/alpha-wolf/rules.txt variant/standard/role/beholder/description.txt variant/standard/role/beholder/name.txt variant/standard/role/beholder/rules.txt variant/standard/role/crooked-senator/description.txt variant/standard/role/crooked-senator/name.txt variant/standard/role/crooked-senator/rules.txt variant/standard/role/druid/description.txt variant/standard/role/druid/name.txt variant/standard/role/druid/rules.txt variant/standard/role/dullahan/description.txt variant/standard/role/dullahan/name.txt variant/standard/role/dullahan/rules.txt variant/standard/role/fallen-angel/description.txt variant/standard/role/fallen-angel/name.txt variant/standard/role/fallen-angel/rules.txt variant/standard/role/hunter/description.txt variant/standard/role/hunter/name.txt variant/standard/role/hunter/rules.txt variant/standard/role/jester/description.txt variant/standard/role/jester/name.txt variant/standard/role/jester/rules.txt variant/standard/role/lycan/description.txt variant/standard/role/lycan/name.txt variant/standard/role/lycan/rules.txt variant/standard/role/medusa/description.txt variant/standard/role/medusa/name.txt variant/standard/role/medusa/rules.txt variant/standard/role/necromancer/description.txt variant/standard/role/necromancer/name.txt variant/standard/role/necromancer/rules.txt variant/standard/role/oracle/description.txt variant/standard/role/oracle/name.txt variant/standard/role/oracle/rules.txt variant/standard/role/orphan/description.txt variant/standard/role/orphan/name.txt variant/standard/role/orphan/rules.txt variant/standard/role/protector/description.txt variant/standard/role/protector/name.txt variant/standard/role/protector/rules.txt variant/standard/role/saint/description.txt variant/standard/role/saint/name.txt variant/standard/role/saint/rules.txt variant/standard/role/scapegoat/description.txt variant/standard/role/scapegoat/name.txt variant/standard/role/scapegoat/rules.txt variant/standard/role/seer/description.txt variant/standard/role/seer/name.txt variant/standard/role/seer/rules.txt variant/standard/role/simple-villager/description.txt variant/standard/role/simple-villager/name.txt variant/standard/role/simple-villager/rules.txt variant/standard/role/simple-werewolf/description.txt variant/standard/role/simple-werewolf/name.txt variant/standard/role/simple-werewolf/rules.txt variant/standard/role/spiteful-villager/description.txt variant/standard/role/spiteful-villager/name.txt variant/standard/role/spiteful-villager/rules.txt variant/standard/role/true-villager/description.txt variant/standard/role/true-villager/name.txt variant/standard/role/true-villager/rules.txt variant/standard/role/village-drunk/description.txt variant/standard/role/village-drunk/name.txt variant/standard/role/village-drunk/rules.txt variant/standard/role/witch/description.txt variant/standard/role/witch/name.txt variant/standard/role/witch/rules.txt variant/standard/role/zombie/description.txt variant/standard/role/zombie/name.txt variant/standard/role/zombie/rules.txt source-repository head type: git location: git@github.com:hjwylde/werewolf executable werewolf main-is: Main.hs hs-source-dirs: app/ ghc-options: -threaded -with-rtsopts=-N other-modules: Game.Werewolf.Command Game.Werewolf.Command.Global Game.Werewolf.Command.Hunter Game.Werewolf.Command.Necromancer Game.Werewolf.Command.Oracle Game.Werewolf.Command.Orphan Game.Werewolf.Command.Protector Game.Werewolf.Command.Scapegoat Game.Werewolf.Command.Seer Game.Werewolf.Command.Status Game.Werewolf.Command.Villager Game.Werewolf.Command.Werewolf Game.Werewolf.Command.Witch Game.Werewolf.Engine Game.Werewolf.Message Game.Werewolf.Message.Command Game.Werewolf.Message.Engine Game.Werewolf.Message.Error Game.Werewolf.Util Game.Werewolf.Variant.NoRoleKnowledge.Command Game.Werewolf.Variant.NoRoleKnowledge.Engine Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Command Game.Werewolf.Variant.NoRoleKnowledgeOrReveal.Engine Game.Werewolf.Variant.NoRoleReveal.Command Game.Werewolf.Variant.NoRoleReveal.Engine Game.Werewolf.Variant.Standard.Command Game.Werewolf.Variant.Standard.Engine Game.Werewolf.Variant.Standard.Error Paths_werewolf Werewolf.Command.Boot Werewolf.Command.Choose Werewolf.Command.Circle Werewolf.Command.Divine Werewolf.Command.End Werewolf.Command.Heal Werewolf.Command.Help Werewolf.Command.Interpret Werewolf.Command.Pass Werewolf.Command.Ping Werewolf.Command.Poison Werewolf.Command.Protect Werewolf.Command.Quit Werewolf.Command.Raise Werewolf.Command.See Werewolf.Command.Start Werewolf.Command.Status Werewolf.Command.Unvote Werewolf.Command.Version Werewolf.Command.Vote Werewolf.Options Werewolf.System Werewolf.Version default-language: Haskell2010 other-extensions: FlexibleContexts, MultiParamTypeClasses, OverloadedStrings, QuasiQuotes, Rank2Types build-depends: aeson >= 0.8 && < 0.12, base >= 4.8 && < 5, containers == 0.5.*, directory == 1.2.*, extra == 1.4.*, filepath == 1.4.*, lens >= 4.12 && < 4.15, MonadRandom == 0.4.*, mtl == 2.2.*, optparse-applicative >= 0.11 && < 0.14, random-shuffle, text == 1.2.*, transformers >= 0.4 && < 0.6, werewolf library hs-source-dirs: src/ exposed-modules: Control.Lens.Extra Data.String.Humanise Data.String.Interpolate.Extra Game.Werewolf Game.Werewolf.Game Game.Werewolf.Player Game.Werewolf.Response Game.Werewolf.Role Game.Werewolf.Variant default-language: Haskell2010 other-extensions: CPP, DeriveGeneric, FlexibleContexts, OverloadedStrings, QuasiQuotes, Rank2Types, TemplateHaskell build-depends: aeson >= 0.8 && < 0.12, base >= 4.8 && < 5, containers == 0.5.*, extra == 1.4.*, interpolate == 0.1.*, lens >= 4.12 && < 4.15, mtl == 2.2.*, template-haskell >= 2.10 && < 2.12, text == 1.2.*, transformers >= 0.4 && < 0.6 werewolf-1.5.1.1/CHANGELOG.md0000644000000000000000000005727412755167000013530 0ustar0000000000000000# Changelog **N.B.**, prior to v1 a change was considered *breaking* if it altered how an interface interacts with the werewolf binary. Post v1 a change is further considered breaking if the state file format is incompatible with the previous version. ### Upcoming ### v1.5.1.1 *Revisions* * Added missing modules to cabal file. ### v1.5.1.0 *Minor* * Added the No Role Knowledge Or Reveal variant. ([#226](https://github.com/hjwylde/werewolf/issues/226)) *Revisions* * Bump optparse-applicative max version constraint. ([#256](https://github.com/hjwylde/werewolf/issues/256)) ### v1.5.0.0 *Major* * Changed name of Spiteful Villagers variant to Spiteful village. ([#242](https://github.com/hjwylde/werewolf/issues/242)) *Minor* * Added in a `variants` help subcommand. ([#227](https://github.com/hjwylde/werewolf/issues/227)) *Revisions* * Fixed Jester lynched message to be gender neutral. ([#248](https://github.com/hjwylde/werewolf/issues/248)) * Fixed Medusa description to be gender neutral. ([#247](https://github.com/hjwylde/werewolf/issues/247)) * Pulled variant information out into external text files. ([#242](https://github.com/hjwylde/werewolf/issues/242)) ### v1.4.0.1 *Revisions* * Ignore the Jester when considering Necromancers win condition. ([#244](https://github.com/hjwylde/werewolf/issues/244)) ### v1.4.0.0 *Major* * Added the Necromancer role. ([#211](https://github.com/hjwylde/werewolf/issues/211)) * Renamed Scapegoat's allowedVoters to chosenVoters. ([#211](https://github.com/hjwylde/werewolf/issues/211)) *Minor* * Added the Zombie role. ([#211](https://github.com/hjwylde/werewolf/issues/211)) ### v1.3.1.0 *Minor* * Added a game variant message at the start of a game. ([#235](https://github.com/hjwylde/werewolf/issues/235)) * Added the Saint role. ([#178](https://github.com/hjwylde/werewolf/issues/178)) * Added marks to the Dullahan's status command. ([#236](https://github.com/hjwylde/werewolf/issues/236)) * Changed Spiteful Ghost to be aligned with the Villagers and to start off alive. ([#238](https://github.com/hjwylde/werewolf/issues/238)) * Renamed Spiteful Ghost to Spiteful Villager. ([#241](https://github.com/hjwylde/werewolf/issues/241)) * Added the spiteful-villagers variant. ([#240](https://github.com/hjwylde/werewolf/issues/240)) *Revisions* * Fix a bug where due to Scapegoat picking 1 player the day never comes again. ([#239](https://github.com/hjwylde/werewolf/issues/239)) ### v1.3.0.1 *Revisions* * Fixed Fallen Angel won message to not refer to a warning. ([#237](https://github.com/hjwylde/werewolf/issues/237)) ### v1.3.0.0 *Major* * Added activity field to Role. ([#231](https://github.com/hjwylde/werewolf/issues/231)) * Added the Dullahan role. ([#202](https://github.com/hjwylde/werewolf/issues/202)) *Minor* * Added player roles message when the game is force ended. ([#225](https://github.com/hjwylde/werewolf/issues/225)) * Added the no-role-reveal variant. ([#181](https://github.com/hjwylde/werewolf/issues/181)) * Added a public start-of-game message for the Spiteful Ghost. ([#220](https://github.com/hjwylde/werewolf/issues/220)) * Removed the public start-of-game message for the Fallen Angel. ([#229](https://github.com/hjwylde/werewolf/issues/229)) * Updated role balances based on Simple Werewolf being -5. ([#233](https://github.com/hjwylde/werewolf/issues/233)) *Revisions* * Fixed current stage command to declare diurnal turns. ([#231](https://github.com/hjwylde/werewolf/issues/231)) * Fixed ping command to declare diurnal roles. ([#231](https://github.com/hjwylde/werewolf/issues/231)) ### v1.2.0.3 *Revisions* * Fixed help subcommands from causing an error when a game doesn't exist. ([#230](https://github.com/hjwylde/werewolf/issues/230)) * Fixed help subcommands for no-role-knowledge variant. ([#232](https://github.com/hjwylde/werewolf/issues/232)) ### v1.2.0.2 *Revisions* * Added further missing files to extra-source-files. ### v1.2.0.1 *Revisions* * Added missing files to extra-source-files. ### v1.2.0.0 *Major* * Renamed FerinasGrunt to DruidsTurn. ([#155](https://github.com/hjwylde/werewolf/issues/155)) * Added tag field to Role. ([#155](https://github.com/hjwylde/werewolf/issues/155)) * Added variant field to Game. ([#222](https://github.com/hjwylde/werewolf/issues/222)) *Minor* * Moved messages to external files using QuasiQuotes. ([#155](https://github.com/hjwylde/werewolf/issues/155)) * Moved role names, descriptions and rules to external files using QuasiQuotes. ([#155](https://github.com/hjwylde/werewolf/issues/155)) * Removed `--include-seer` option from `start`. ([#223](https://github.com/hjwylde/werewolf/issues/223)) * Removed `--force` option from `end`. ([#224](https://github.com/hjwylde/werewolf/issues/224)) * Added the no-role-knowledge variant. ([#182](https://github.com/hjwylde/werewolf/issues/182)) *Revisions* * Updated Crooked Senator's balance to 2. ### v1.1.1.0 *Revisions* * Updated minimum and maximum number of roles with `--random-extra-roles`. * Updated Medusa's balance to 3. ### v1.1.0.0 *Major* * Moved majority of library classes to app/. ([#208](https://github.com/hjwylde/werewolf/issues/208)) * Changed the Fallen Angel to be a true Loner. ([#173](https://github.com/hjwylde/werewolf/issues/173)) * Added the Oracle role. ([#141](https://github.com/hjwylde/werewolf/issues/141)) * Removed the event concept. ([#147](https://github.com/hjwylde/werewolf/issues/147)) *Minor* * Added private message to players when they die. ([#188](https://github.com/hjwylde/werewolf/issues/188)) * Added the Medusa role. ([#99](https://github.com/hjwylde/werewolf/issues/99)) * Added `unvote` command. ([#213](https://github.com/hjwylde/werewolf/issues/213)) * Removed public knowledge of Villagers and Werewolves that have voted. ([#214](https://github.com/hjwylde/werewolf/issues/214)) * Added the Spiteful Ghost role. ([#206](https://github.com/hjwylde/werewolf/issues/206)) *Revisions* * Updated the Scapegoat's turn messages to be clear that they may choose more than 1 player. ([#185](https://github.com/hjwylde/werewolf/issues/185)) * Updated Beholder's balance to 1. ### v1.0.2.2 *Revisions* * Bumped max version constraint of lens. ### v1.0.2.1 *Revisions* * Removed the binary header and updated the program description. ### v1.0.2.0 *Minor* * Added the Alpha Wolf role. ([#149](https://github.com/hjwylde/werewolf/issues/149)) * Added the Beholder role. ([#168](https://github.com/hjwylde/werewolf/issues/168)) * Added the Lycan role. ([#169](https://github.com/hjwylde/werewolf/issues/169)) *Revisions* * Moved Seer's player seen message to Sunrise. ([#184](https://github.com/hjwylde/werewolf/issues/184)) ### v1.0.1.0 *Minor* * Added the Crooked Senator role. ([#142](https://github.com/hjwylde/werewolf/issues/142)) ### v1.0.0.0 *Major* * Initial stable release! ### v0.5.4.0 *Minor* * Added `--include-seer` option to `start`. ([#179](https://github.com/hjwylde/werewolf/issues/179)) * Added the Village Drunk role. ([#160](https://github.com/hjwylde/werewolf/issues/160)) *Revisions* * Minor updates to the `help rules` text. * Removed most logical tests and kept error ones. ([#164](https://github.com/hjwylde/werewolf/issues/164)) ### v0.5.3.0 *Minor* * Removed the Devoted Servant. ([#127](https://github.com/hjwylde/werewolf/issues/127)) * Renamed the Angel to the Fallen Angel. ([#130](https://github.com/hjwylde/werewolf/issues/130)) * Renamed the Villager-Villager to the True Villager. ([#137](https://github.com/hjwylde/werewolf/issues/137)) * Removed the Wolf-hound. ([#129](https://github.com/hjwylde/werewolf/issues/129)) *Revisions* * Changed the Druid's balance to 3. ([#165](https://github.com/hjwylde/werewolf/issues/165)) * Updated the Fallen Angel's description and rules. ([#130](https://github.com/hjwylde/werewolf/issues/130)) * Updated the Simple Werewolf's description. ([#174](https://github.com/hjwylde/werewolf/issues/174)) * Updated the True Villager's description and rules. ([#137](https://github.com/hjwylde/werewolf/issues/137)) * Made the roles gender neutral. ([#101](https://github.com/hjwylde/werewolf/issues/101)) * Made the role descriptions use consistent tenses. ([#158](https://github.com/hjwylde/werewolf/issues/158)) ### v0.5.2.0 *Minor* * Renamed the Wild-child to Orphan. ([#128](https://github.com/hjwylde/werewolf/issues/128)) * Added the Hunter role. ([#3](https://github.com/hjwylde/werewolf/issues/3)) *Revisions* * Updated the Orphan's description. ([#128](https://github.com/hjwylde/werewolf/issues/128)) * Added role taken message for the Devoted Servant. ([#157](https://github.com/hjwylde/werewolf/issues/157)) ### v0.5.1.0 *Minor* * Renamed the Defender to Protector. ([#132](https://github.com/hjwylde/werewolf/issues/132)) * Automatically delete the game file if the game is over. ([#100](https://github.com/hjwylde/werewolf/issues/100)) * Added errors for using overloaded commands out of turn. ([#100](https://github.com/hjwylde/werewolf/issues/100)) *Revisions* * Updated the Protector's description and rules. ([#132](https://github.com/hjwylde/werewolf/issues/132)) * Improved the English used. ([#72](https://github.com/hjwylde/werewolf/issues/72)) * Added which player is the Villager-Villager to `status`. ([#144](https://github.com/hjwylde/werewolf/issues/144)) ### v0.5.0.0 *Major* * Added required `--tag` option for enabling multiple games at once. ([#29](https://github.com/hjwylde/werewolf/issues/29)) ### v0.4.12.0 *Minor* * Renamed the Bear Tamer to Druid. ([#131](https://github.com/hjwylde/werewolf/issues/131)) * Renamed the Village Idiot to Jester. ([#136](https://github.com/hjwylde/werewolf/issues/136)) *Revisions* * Updated the Witch's description and rules. ([#138](https://github.com/hjwylde/werewolf/issues/138)) * Updated the Scapegoat's description. ([#133](https://github.com/hjwylde/werewolf/issues/133)) * Updated the Druid's description and rules. ([#131](https://github.com/hjwylde/werewolf/issues/131)) * Updated the Jester's description and rules. ([#136](https://github.com/hjwylde/werewolf/issues/136)) ### v0.4.11.0 *Minor* * Removed the advice field from Role. ([#134](https://github.com/hjwylde/werewolf/issues/134)) *Revisions* * Replaced references to Millers Hollow with an original game description. ([#126](https://github.com/hjwylde/werewolf/issues/126)) * Fixed a bug where extra roles with spaces in them weren't recognised. * Fixed Devoted Servant messages to exclude her when joining the Werewolf pack. * Fixed Devoted Servant help messages to include how to `pass`. * Removed player cap of 24. ([#143](https://github.com/hjwylde/werewolf/issues/143)) * Fixed a bug where the Wild Child would receive role model died messages when dead. ([#145](https://github.com/hjwylde/werewolf/issues/145)) * Updated the Simple Villager's description and rules. ([#135](https://github.com/hjwylde/werewolf/issues/135)) * Updated the Seer's description. ([#134](https://github.com/hjwylde/werewolf/issues/134)) * Updated the Simple Werewolf's description. ([#139](https://github.com/hjwylde/werewolf/issues/139)) ### v0.4.10.0 *Minor* * Added `boot` command. ([#14](https://github.com/hjwylde/werewolf/issues/14)) ### v0.4.9.0 *Minor* * Added player contributed messages upon game over. ([#86](https://github.com/hjwylde/werewolf/issues/86)) * Changed `choose` command for Scapegoat to take a space separated list rather than comma separated. ([#98](https://github.com/hjwylde/werewolf/issues/98)) * Filtered `help` commands based on the current game. ([#94](https://github.com/hjwylde/werewolf/issues/94)) * Added `--all` option to `help` commands. ([#94](https://github.com/hjwylde/werewolf/issues/94)) * Added the Devoted Servant role. ([#47](https://github.com/hjwylde/werewolf/issues/47)) * Added `--force` flag to `end`. ([#77](https://github.com/hjwylde/werewolf/issues/77)) ### v0.4.8.0 *Minor* * Added role allocations to the game over messages. ([#27](https://github.com/hjwylde/werewolf/issues/27)) *Revisions* * Improved prompt to Wolf-hound on how to choose an allegiance. ([#90](https://github.com/hjwylde/werewolf/issues/90)) * Changed Scapegoat's balance to 0. ([#91](https://github.com/hjwylde/werewolf/issues/91)) * Grouped `help commands` to improve readability. ([#97](https://github.com/hjwylde/werewolf/issues/97)) * Changed the `status` and `ping` commands to tell the caller when the game is over. ([#89](https://github.com/hjwylde/werewolf/issues/89)) * Added roles in game to the `status` command. ([#93](https://github.com/hjwylde/werewolf/issues/93)) ### v0.4.7.1 *Revisions* * Fixed bug where the Wolf-hound's turn messages would be displayed on every round. ([#87](https://github.com/hjwylde/werewolf/issues/87)) * Fixed a bug causing the Angel's joining Villagers message to be shown every round. ([#95](https://github.com/hjwylde/werewolf/issues/95)) * Fixed Village Idiot text to have spaces around the name. ([#87](https://github.com/hjwylde/werewolf/issues/87)) * Fixed a bug where the Werewolves couldn't win if it was down to 1 Werewolf and the Village Idiot. ([#88](https://github.com/hjwylde/werewolf/issues/88)) ### v0.4.7.0 *Revisions* * Fixed balance calculation to ensure total balance is between -2 and 2. * Changed `--random-extra-roles` to have between `n / 3` and `n / 3 + 2` extra roles. * Added prisms and traversals to Role, Player & Game modules. ([#20](https://github.com/hjwylde/werewolf/issues/20)) * Removed fudging of roles and replaced with fudging of allegiances. * Wolf-hound now has their allegiance hidden when they are lynched. * Fixed the grammar on the first Werewolves' turn messages. * Moved Wolf-hound's turn to before the Seer's so that the Seer may see his allegiance properly. * Restricted specifying `Simple Villager` or `Simple Werewolf` as extra roles. ### v0.4.6.1 *Revisions* * Fixed Village Idiot text to have spaces around the name. ([#87](https://github.com/hjwylde/werewolf/issues/87)) * Fixed a bug where the Werewolves couldn't win if it was down to 1 Werewolf and the Village Idiot. ([#88](https://github.com/hjwylde/werewolf/issues/88)) ### v0.4.6.0 *Minor* * Added the Village Idiot role. ([#41](https://github.com/hjwylde/werewolf/issues/41)) * Added the Scapegoat's ability to choose whom may vote on the next day when he is blamed. ([#62](https://github.com/hjwylde/werewolf/issues/62)) * Added in balance concept for roles to help balance role selection. ([#81](https://github.com/hjwylde/werewolf/issues/81)) * Added `--random-extra-roles` option to `start`. ([#30](https://github.com/hjwylde/werewolf/issues/30)) * Added the Bear Tamer role. ([#45](https://github.com/hjwylde/werewolf/issues/45)) * Added a `circle` command. ([#45](https://github.com/hjwylde/werewolf/issues/45)) *Revisions* * Fixed the Defender being unable to protect himself. * Restructured library modules to only export relevant functions. ([#11](https://github.com/hjwylde/werewolf/issues/11)) ### v0.4.5.0 *Minor* * Added the Wolf-hound role. ([#50](https://github.com/hjwylde/werewolf/issues/50)) * Added a `version` command. ([#84](https://github.com/hjwylde/werewolf/issues/84)) * Added the Wild-child role. ([#49](https://github.com/hjwylde/werewolf/issues/49)) * Added the Angel role. ([#52](https://github.com/hjwylde/werewolf/issues/52)) *Revisions* * Renamed the Villager role to Simple Villager. * Renamed the Werewolf role to Simple Werewolf. * Renamed the devourVoteCommand and lynchVoteCommand to voteDevourCommand and voteLynchCommand. ([#49](https://github.com/hjwylde/werewolf/issues/49)) * Fixed `quit` to advance the stage when the only role for that stage has quit. ### v0.4.4.1 *Revisions* * Fixed grammar for the `currentStageMessages`. ([#83](https://github.com/hjwylde/werewolf/issues/83)) * Fixed the `heal` command help message to not require a `PLAYER` argument. ([#82](https://github.com/hjwylde/werewolf/issues/82)) ### v0.4.4.0 *Minor* * Removed `playerHealedMessage` and replaced with the generic `noPlayerDevouredMessage`. ([#80](https://github.com/hjwylde/werewolf/issues/80)) * Removed `playerProtectedMessage` and replaced with the generic `noPlayerDevouredMessage`. ([#80](https://github.com/hjwylde/werewolf/issues/80)) *Revisions* * Privatised underscore methods and changed old uses to using lens. ([#20](https://github.com/hjwylde/werewolf/issues/20)) * Tidied up arbitrary instances by using `newtype`'s. ([#78](https://github.com/hjwylde/werewolf/issues/78)) * Fixed the `noPlayerDevouredMessage` to be displayed after sunrise. ([#80](https://github.com/hjwylde/werewolf/issues/80)) * Removed `Show` instance for `Command` and used `Blind`. ([#78](https://github.com/hjwylde/werewolf/issues/78)) ### v0.4.3.2 *Revisions* * Fixed grammar for the `currentStageMessages`. ([#83](https://github.com/hjwylde/werewolf/issues/83)) * Fixed the `heal` command help message to not require a `PLAYER` argument. ([#82](https://github.com/hjwylde/werewolf/issues/82)) ### v0.4.3.1 *Revisions* * Added missing module to Cabal file. ### v0.4.3.0 *Minor* * Added the Defender role. ([#38](https://github.com/hjwylde/werewolf/issues/38)) ### v0.4.2.3 *Revisions* * Fixed grammar for the `currentStageMessages`. ([#83](https://github.com/hjwylde/werewolf/issues/83)) * Fixed the `heal` command help message to not require a `PLAYER` argument. ([#82](https://github.com/hjwylde/werewolf/issues/82)) ### v0.4.2.2 *Revisions* * Added missing module to Cabal file. ### v0.4.2.1 *Revisions* * Fixed a bug causing the Witch being unable to heal themselves. ([#76](https://github.com/hjwylde/werewolf/issues/76)) ### v0.4.2.0 *Minor* * Added the Villager-Villager role. ([#37](https://github.com/hjwylde/werewolf/issues/37)) ### v0.4.1.3 *Revisions* * Fixed grammar for the `currentStageMessages`. ([#83](https://github.com/hjwylde/werewolf/issues/83)) * Fixed the `heal` command help message to not require a `PLAYER` argument. ([#82](https://github.com/hjwylde/werewolf/issues/82)) ### v0.4.1.2 *Revisions* * Added missing module to Cabal file. ### v0.4.1.1 *Revisions* * Fixed a bug causing the Witch being unable to heal themselves. ([#76](https://github.com/hjwylde/werewolf/issues/76)) ### v0.4.1.0 *Minor* * Added the Witch role. ([#5](https://github.com/hjwylde/werewolf/issues/5)) ### v0.4.0.1 *Revisions* * Fixed grammar for the `currentStageMessages`. ([#83](https://github.com/hjwylde/werewolf/issues/83)) ### v0.4.0.0 *Major* * Restricted count of special roles to 1. ([#32](https://github.com/hjwylde/werewolf/issues/32)) * Changed private message structure to only ever be for a single player. ([#21](https://github.com/hjwylde/werewolf/issues/21)) *Minor* * Renamed `turn` to `stage`. ([#70](https://github.com/hjwylde/werewolf/issues/70)) * Renamed `VillagersTurn` to `VillagesTurn`. ([#70](https://github.com/hjwylde/werewolf/issues/70)) * Added `events` to the game state. ([#71](https://github.com/hjwylde/werewolf/issues/71)) * Added private pinging to the `ping` command. ([#69](https://github.com/hjwylde/werewolf/issues/69)) * Restricted `end` to players in the current game. ([#74](https://github.com/hjwylde/werewolf/issues/74)) ### v0.3.4.0 *Minor* * Added a `ping` command. ([#64](https://github.com/hjwylde/werewolf/issues/64)) *Revisions* * Added missing apostrophe to the new turn message. ([#63](https://github.com/hjwylde/werewolf/issues/63)) * Changed the "Whom would you like to lynch?" text to be a public message displayed after the devoured message. ([#56](https://github.com/hjwylde/werewolf/issues/56)) * Better prompt to action when villagers vote. * Changed devour vote messages to be sent immediately. ([#57](https://github.com/hjwylde/werewolf/issues/57)) * Removed useless `only` function. ([#55](https://github.com/hjwylde/werewolf/issues/55)) * Turned start of day and night into distinct turns. * Added private message to players when the game is over. ([#65](https://github.com/hjwylde/werewolf/issues/65)) ### v0.3.3.2 *Revisions* * Fixed missing file in Cabal file. ([#18](https://github.com/hjwylde/werewolf/issues/18)) ### v0.3.3.1 *Revisions* * Added `noIntersperse` to `interpret`. ([#60](https://github.com/hjwylde/werewolf/issues/60)) ### v0.3.3.0 *Minor* * Added the Scapegoat role. ([#40](https://github.com/hjwylde/werewolf/issues/40)) * Added a `status` command. ([#18](https://github.com/hjwylde/werewolf/issues/18)) *Revisions* * Added `--` to help description of `interpret`. ([#60](https://github.com/hjwylde/werewolf/issues/60)) ### v0.3.2.0 *Minor* * Added a "Whom would you like to lynch?" message during the Villagers' turn. ([#25](https://github.com/hjwylde/werewolf/issues/25)) * Allowed lowercase roles for `--extra-roles` in the `start` command. ([#33](https://github.com/hjwylde/werewolf/issues/33)) *Revisions* * Shrunk some of the help text to make it more readable. ([#25](https://github.com/hjwylde/werewolf/issues/25)) ### v0.3.1.3 *Revisions* * Fixed a bug where Werewolves could devour other Werewolves. ([#34](https://github.com/hjwylde/werewolf/issues/34)) * Changed Werewolf text from "kill" to "devour". ([#34](https://github.com/hjwylde/werewolf/issues/34)) ### v0.3.1.2 *Revisions* * Fixed dead werewolves being informed of votes. ([#24](https://github.com/hjwylde/werewolf/issues/24)) ### v0.3.1.1 *Revisions* * Tidied up the help text to be smaller. ([#26](https://github.com/hjwylde/werewolf/issues/26)) * Fixed a bug where the turn was advanced to Werewolves when no Werewolves were alive. ([#26](https://github.com/hjwylde/werewolf/issues/26)) ### v0.3.1.0 *Minor* * Added a message to say the names of all the players at the start of a game. ([#23](https://github.com/hjwylde/werewolf/issues/23)) * Added a message to say the roles in play at the start of a game. ([#16](https://github.com/hjwylde/werewolf/issues/16)) ### v0.3.0.5 *Revisions* * Fixed a bug where Werewolves could devour other Werewolves. ([#34](https://github.com/hjwylde/werewolf/issues/34)) * Changed Werewolf text from "kill" to "devour". ([#34](https://github.com/hjwylde/werewolf/issues/34)) ### v0.3.0.4 *Revisions* * Fixed dead werewolves being informed of votes. ([#24](https://github.com/hjwylde/werewolf/issues/24)) ### v0.3.0.3 *Revisions* * Tidied up the help text to be smaller. ([#26](https://github.com/hjwylde/werewolf/issues/26)) * Fixed a bug where the turn was advanced to Werewolves when no Werewolves were alive. ([#26](https://github.com/hjwylde/werewolf/issues/26)) ### v0.3.0.2 *Revisions* * Tidied up the help text to be smaller. ([#26](https://github.com/hjwylde/werewolf/issues/26)) * Fixed a bug where the turn was advanced to Werewolves when no Werewolves were alive. ([#26](https://github.com/hjwylde/werewolf/issues/26)) ### v0.3.0.1 *Revisions* * Fixed `interpret` to display the commands help messages when given invalid arguments. ([#22](https://github.com/hjwylde/werewolf/issues/22)) ### v0.3.0.0 *Major* * Added `--extra-roles` option to `start`. ([#12](https://github.com/hjwylde/werewolf/issues/12)) * Removed Seer from being included by default. ([#12](https://github.com/hjwylde/werewolf/issues/12)) *Minor* * Allowed `start` to work when the game has ended but `end` hasn't been called. ([#15](https://github.com/hjwylde/werewolf/issues/15)) * Added `quit` command. ([#13](https://github.com/hjwylde/werewolf/issues/13)) ### v0.2.0.2 *Revisions* * Fixed dead werewolves being informed of votes. ([#24](https://github.com/hjwylde/werewolf/issues/24)) ### v0.2.0.1 *Revisions* * Tidied up the help text to be smaller. ([#26](https://github.com/hjwylde/werewolf/issues/26)) * Fixed a bug where the turn was advanced to Werewolves when no Werewolves were alive. ([#26](https://github.com/hjwylde/werewolf/issues/26)) ### v0.2.0.0 *Major* * Added the Seer role. ([#4](https://github.com/hjwylde/werewolf/issues/4)) * Removed the need to encode / decode to JSON for the state file. ([#9](https://github.com/hjwylde/werewolf/issues/9)) ### v0.1.0.0 *Major* * Initial implementation with Villagers and Werewolves. ([#1](https://github.com/hjwylde/werewolf/issues/1)) werewolf-1.5.1.1/README.md0000644000000000000000000002032112755166155013167 0ustar0000000000000000# werewolf [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) [![Build Status](https://travis-ci.org/hjwylde/werewolf.svg?branch=master)](https://travis-ci.org/hjwylde/werewolf) [![Release](https://img.shields.io/github/release/hjwylde/werewolf.svg)](https://github.com/hjwylde/werewolf/releases/latest) [![werewolf on Stackage LTS](https://www.stackage.org/package/werewolf/badge/lts)](https://www.stackage.org/lts/package/werewolf) [![werewolf on Stackage Nightly](https://www.stackage.org/package/werewolf/badge/nightly)](https://www.stackage.org/nightly/package/werewolf) A game engine for playing werewolf within an arbitrary chat client. Werewolf is a well known social party game, commonly also called Mafia. See the [Wikipedia article](https://en.wikipedia.org/wiki/Mafia_(party_game)) for a rundown on its gameplay and history. If you're here just to play werewolf, you may wish to skip straight to [chat interfaces](https://github.com/hjwylde/werewolf#chat-interfaces). ### Game description Long has the woods been home to wild creatures, both kind and cruel. Most have faces and are known to the inhabitants of Fougères in Brittany, France; but no-one from the village has yet to lay eyes on the merciless Werewolf. Each night Werewolves attack the village and devour the innocent. For centuries no-one knew how to fight this scourge, however recently a theory has taken ahold that mayhaps the Werewolves walk among the Villagers themselves... Objective of the game: For the Loners: complete their own objective. For the Villagers: lynch all of the Werewolves. For the Werewolves: devour all of the Villagers. #### Roles The implemented roles are split into four categories. **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. * Orphan * Village Drunk **The Loners:** The Loners look out for themselves and themselves alone. The Loners must complete their own objective. * Dullahan * Fallen Angel * Necromancer (and Zombie) **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. * Beholder * Crooked Senator * Druid * Hunter * Jester * Lycan * Medusa * Oracle * Protector * Saint * Scapegoat * Seer * Simple Villager * Spiteful Villager * True Villager * Witch **The Werewolves:** Hiding in plain sight, the Werewolves are not a small trifle. The Werewolves must devour all of the Villagers. * Alpha Wolf * Simple Werewolf #### Variants Variants alter how the game plays out. * Standard * No role knowledge (players are not informed of the roles in play at the start of a game) * No role knowledge or reveal (players are not informed of the roles in play at the start of a game, nor each others roles upon death) * No role reveal (players are not informed of each others roles upon death) * Spiteful village (the default Villager role becomes the Spiteful Villager) ### Installing Installing werewolf is easiest done using either [stack](https://github.com/commercialhaskell/stack) (recommended) or [Cabal](https://github.com/haskell/cabal). **Using stack:** ```bash stack install werewolf export PATH=$PATH:~/.local/bin ``` **Using Cabal:** ```bash cabal-install werewolf export PATH=$PATH:~/.cabal/bin ``` ### Usage This section covers how a chat interface interacts with the werewolf game engine. All werewolf commands are designed to be run by a user from the chat client. E.g., to start a game: ```bash > werewolf --caller @foo --tag werewolf start --extra-roles seer @bar @baz @qux @quux @corge @grault {"ok":true,"messages":[ {"to":null,"message":"A new game of werewolf is starting with @foo, @bar, @baz, @qux, @quux, @corge and @grault!"}, {"to":null,"message":"The roles in play are Seer (1), Simple Villager (4) and Simple Werewolf (2) for a total balance of -2."}, {"to":"@foo","message":"You're a Simple Villager.\nA 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 all the Villagers in this unfortunate time.\nThe Simple Villager has no special abilities, they must use their guile to determine whom among them is not who they say they are."}, ..., {"to":null,"message":"Night falls, the village is asleep."}, {"to":null,"message":"The Seer wakes up."}, {"to":"@corge","message":"Whose allegiance would you like to `see`?"} ]} ``` In this example, user _@foo_ ran the `start` command with the player names as arguments. Note that the calling user, _@foo_, was passed in to the `--caller` option and a game tag, _werewolf_, was passed in to the `--tag` option. All commands require these options (n.b., the tag option is arbitrary, it just enables multiple games of werewolf to be running at once). Any command ran returns a JSON result. The result contains a boolean for whether the command was successful and a list of messages. The `to` header on a message may either be `null`---for a public message---or have an intended recipient. It's the Seer's turn now. ```bash > werewolf --caller @corge --tag werewolf see @grault {"ok":true,"messages":[ {"to":"@corge","message":"@grault is aligned with the Werewolves."}, {"to":"@quux","message":"You feel restless, like an old curse is keeping you from sleep. It seems you're not the only one... @grault is also emerging from their home."}, {"to":"@grault","message":"You feel restless, like an old curse is keeping you from sleep. It seems you're not the only one... @quux is also emerging from their home."}, {"to":null,"message":"The Werewolves wake up, transform and choose a new victim."}, {"to":"@quux","message":"Whom would you like to `vote` to devour?"}, {"to":"@grault","message":"Whom would you like to `vote` to devour?"} ]} ``` Let's have the Werewolves, _@quux_ and _@grault_, vote to devour a Villager. ```bash > werewolf --caller @quux --tag werewolf vote @foo {"ok":true,"messages":[ {"to":"@grault","message":"@quux voted to devour @foo."} ]} > werewolf --caller @grault --tag werewolf vote @foo {"ok":true,"messages":[ {"to":"@quux","message":"@grault voted to devour @foo."}, {"to":null,"message":"The sun rises. Everybody wakes up and opens their eyes..."}, {"to":null,"message":"As you open them you notice a door broken down and @foo's guts half devoured and spilling out over the cobblestones. From the look of their personal effects, you deduce they were a Simple Villager."}, {"to":null,"message":"As the village gathers in the square the Town Clerk calls for a vote."}, {"to":null,"message":"Whom would you like to `vote` to lynch?"} ]} ``` Too bad for _@foo_. Maybe the village can get some vengeance... ```bash > werewolf --caller @corge --tag werewolf vote @grault {"ok":true,"messages":[]} ``` This time, even though the command was successful, there are no messages. ```bash > werewolf --caller @corge --tag werewolf vote @grault {"ok":false,"messages":[{"to":"@corge","message":"You've already voted!"}]} ``` Here the command was unsuccessful and an error message is sent to _@corge_. Even though the command was unsuccessful, the chat interface probably won't need to do anything special. Relaying the error message back to the user should suffice. Thus a chat interface must implement the following: * The ability to call werewolf commands. This includes passing the `--caller` and `--tag` options and arguments correctly. It is possible to only implement the `interpret` command, which interprets the caller's input. * The ability to send resultant messages. Resultant messages may be to everyone or to a specific user. #### Commands See `werewolf --help`. #### Chat interfaces Click through for instructions on how to run a chat interface and play werewolf. * [Slack](https://github.com/hjwylde/werewolf-slack) werewolf-1.5.1.1/variant/no-role-knowledge/description.txt0000644000000000000000000000016412755166155021773 0ustar0000000000000000Players are not informed of the roles in play at the start of a game. Further, nocturnal player turns are obscured. werewolf-1.5.1.1/variant/no-role-knowledge/name.txt0000644000000000000000000000002212751254103020344 0ustar0000000000000000No role knowledge werewolf-1.5.1.1/variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt0000644000000000000000000000004412727224206026217 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-knowledge/command/ping/werewolves-pinged.txt0000644000000000000000000000004412717232210025446 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-knowledge/command/status/current-nocturnal-turn.txt0000644000000000000000000000002612727224206027050 0ustar0000000000000000It's currently night. werewolf-1.5.1.1/variant/no-role-knowledge/engine/new-game/roles-in-game.txt0000644000000000000000000000011112717232210025024 0ustar0000000000000000The roles in play have a total balance of #{T.pack $ show totalBalance}. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/description.txt0000644000000000000000000000022612755166155023664 0ustar0000000000000000Players are not informed of the roles in play at the start of a game, nor each others roles upon death. Further, nocturnal player turns are obscured. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/name.txt0000644000000000000000000000003412755166155022256 0ustar0000000000000000No role knowledge or reveal werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/choose/player-shot.txt0000644000000000000000000000021412755166155026503 0ustar0000000000000000#{humanise player} slumps down to the ground, hands clutching at their chest while blood slips between their fingers and pools around them. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/ping/nocturnal-role-pinged.txt0000644000000000000000000000004412755166155030122 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/ping/werewolves-pinged.txt0000644000000000000000000000004412755166155027360 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/quit/caller-quit.txt0000644000000000000000000000003512755166155026161 0ustar0000000000000000#{humanise caller} has quit! werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/status/current-nocturnal-turn.txt0000644000000000000000000000002612755166155030753 0ustar0000000000000000It's currently night. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/command/status/dead-players.txt0000644000000000000000000000016712755166155026660 0ustar0000000000000000The following players are dead: #{humanise $ map humanisePlayerWithRoleIfKnown (game ^.. players . traverse . dead)}. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/general/player-booted.txt0000644000000000000000000000006212755166155026767 0ustar0000000000000000#{humanise player} has been booted from the game! werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/general/spiteful-villager-killed.txt0000644000000000000000000000027412755166155031126 0ustar0000000000000000Being ethereal seldom has it's benefits. Perhaps however this knowledge of the townsfolks' natures will bring you some joy in the afterlife: #{humanisePlayersWithRoles $ game ^. players}. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/lynching/player-lynched.txt0000644000000000000000000000021212755166155027334 0ustar0000000000000000#{humanise player} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/lynching/saint-lynched.txt0000644000000000000000000000015412755166155027163 0ustar0000000000000000Furious with the village for murdering them, God enacts revenge upon those responsible: #{humanise voters}. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/lynching/werewolf-lynched.txt0000644000000000000000000000021412755166155027674 0ustar0000000000000000#{humanise werewolf} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/new-game/roles-in-game.txt0000644000000000000000000000011112755166155026736 0ustar0000000000000000The roles in play have a total balance of #{T.pack $ show totalBalance}. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/sunrise/player-devoured.txt0000644000000000000000000000020312755166155027400 0ustar0000000000000000As you open them you notice a door broken down and #{humanise player}'s guts half devoured and spilling out over the cobblestones. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/sunrise/player-poisoned.txt0000644000000000000000000000023112755166155027404 0ustar0000000000000000Upon further discovery, it looks like the #{humanise witchRole} struck in the night. #{humanise player} is hanging over the side of their bed, poisoned. werewolf-1.5.1.1/variant/no-role-knowledge-or-reveal/engine/sunrise/player-turned-to-stone.txt0000644000000000000000000000023112755166155030633 0ustar0000000000000000Next to them you see a stone statue, cold to the touch. #{humanise player} must have looked into the eyes of the #{humanise medusaRole} at the very end. werewolf-1.5.1.1/variant/no-role-reveal/description.txt0000644000000000000000000000015012755166155021265 0ustar0000000000000000Players are not informed of each others roles upon death. Further, nocturnal player turns are obscured. werewolf-1.5.1.1/variant/no-role-reveal/name.txt0000644000000000000000000000001712751254103017647 0ustar0000000000000000No role reveal werewolf-1.5.1.1/variant/no-role-reveal/command/choose/player-shot.txt0000644000000000000000000000021412727221700024073 0ustar0000000000000000#{humanise player} slumps down to the ground, hands clutching at their chest while blood slips between their fingers and pools around them. werewolf-1.5.1.1/variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt0000644000000000000000000000004412727224206025516 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-reveal/command/ping/werewolves-pinged.txt0000644000000000000000000000004412727221700024750 0ustar0000000000000000Waiting on the nocturnal players... werewolf-1.5.1.1/variant/no-role-reveal/command/quit/caller-quit.txt0000644000000000000000000000003512727221700023551 0ustar0000000000000000#{humanise caller} has quit! werewolf-1.5.1.1/variant/no-role-reveal/command/status/current-nocturnal-turn.txt0000644000000000000000000000002612727224206026347 0ustar0000000000000000It's currently night. werewolf-1.5.1.1/variant/no-role-reveal/command/status/dead-players.txt0000644000000000000000000000016712751254103024250 0ustar0000000000000000The following players are dead: #{humanise $ map humanisePlayerWithRoleIfKnown (game ^.. players . traverse . dead)}. werewolf-1.5.1.1/variant/no-role-reveal/engine/general/player-booted.txt0000644000000000000000000000006212727221700024357 0ustar0000000000000000#{humanise player} has been booted from the game! werewolf-1.5.1.1/variant/no-role-reveal/engine/general/spiteful-villager-killed.txt0000644000000000000000000000027412733100663026517 0ustar0000000000000000Being ethereal seldom has it's benefits. Perhaps however this knowledge of the townsfolks' natures will bring you some joy in the afterlife: #{humanisePlayersWithRoles $ game ^. players}. werewolf-1.5.1.1/variant/no-role-reveal/engine/lynching/player-lynched.txt0000644000000000000000000000021212727221700024724 0ustar0000000000000000#{humanise player} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse. werewolf-1.5.1.1/variant/no-role-reveal/engine/lynching/saint-lynched.txt0000644000000000000000000000015412733071357024563 0ustar0000000000000000Furious with the village for murdering them, God enacts revenge upon those responsible: #{humanise voters}. werewolf-1.5.1.1/variant/no-role-reveal/engine/lynching/werewolf-lynched.txt0000644000000000000000000000021412727221700025264 0ustar0000000000000000#{humanise werewolf} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse. werewolf-1.5.1.1/variant/no-role-reveal/engine/sunrise/player-devoured.txt0000644000000000000000000000020312727221700024770 0ustar0000000000000000As you open them you notice a door broken down and #{humanise player}'s guts half devoured and spilling out over the cobblestones. werewolf-1.5.1.1/variant/no-role-reveal/engine/sunrise/player-poisoned.txt0000644000000000000000000000023112727221700024774 0ustar0000000000000000Upon further discovery, it looks like the #{humanise witchRole} struck in the night. #{humanise player} is hanging over the side of their bed, poisoned. werewolf-1.5.1.1/variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt0000644000000000000000000000023112727221700026223 0ustar0000000000000000Next to them you see a stone statue, cold to the touch. #{humanise player} must have looked into the eyes of the #{humanise medusaRole} at the very end. werewolf-1.5.1.1/variant/spiteful-village/description.txt0000644000000000000000000000011012755166155021706 0ustar0000000000000000The default Villager role becomes the #{humanise spitefulVillagerRole}. werewolf-1.5.1.1/variant/spiteful-village/name.txt0000644000000000000000000000002112751254103020267 0ustar0000000000000000Spiteful village werewolf-1.5.1.1/variant/standard/description.txt0000644000000000000000000000004012755166155020234 0ustar0000000000000000Everything plays out as normal. werewolf-1.5.1.1/variant/standard/name.txt0000644000000000000000000000001112751254103016612 0ustar0000000000000000Standard werewolf-1.5.1.1/variant/standard/command/boot/player-voted-boot.txt0000644000000000000000000000006512717220441023640 0ustar0000000000000000#{humanise caller} voted to boot #{humanise target}! werewolf-1.5.1.1/variant/standard/command/choose/player-shot.txt0000644000000000000000000000025512717220441023051 0ustar0000000000000000#{humanise player} the #{humanise $ player ^. role} slumps down to the ground, hands clutching at their chest while blood slips between their fingers and pools around them. werewolf-1.5.1.1/variant/standard/command/circle/game-circle.txt0000644000000000000000000000020312717220441022724 0ustar0000000000000000The players are sitting in the following order: #{T.intercalate " <-> " (map humanisePlayerWithState (players ++ [head players]))} werewolf-1.5.1.1/variant/standard/command/end/game-ended.txt0000644000000000000000000000003512717220441022052 0ustar0000000000000000Game ended by #{callerName}. werewolf-1.5.1.1/variant/standard/command/help/druids-turn.txt0000644000000000000000000000010412717220441022523 0ustar0000000000000000- Ferina grunts if the #{humanise druidRole} is next to a Werewolf. werewolf-1.5.1.1/variant/standard/command/help/game-description.txt0000644000000000000000000000070712717220441023506 0ustar0000000000000000Long has the woods been home to wild creatures, both kind and cruel. Most have faces and are known to the inhabitants of Fougères in Brittany, France; but no-one from the village has yet to lay eyes on the merciless Werewolf. Each night Werewolves attack the village and devour the innocent. For centuries no-one knew how to fight this scourge, however recently a theory has taken ahold that mayhaps the Werewolves walk among the Villagers themselves... werewolf-1.5.1.1/variant/standard/command/help/global-commands.txt0000644000000000000000000000024312717220441023306 0ustar0000000000000000Global commands: - `start ([-e|--extra-roles ROLE,...] | [-r|--random-extra-roles]) [-v|--variant VARIANT] PLAYER...` - `end` - `boot PLAYER` - `quit` - `version` werewolf-1.5.1.1/variant/standard/command/help/help-commands.txt0000644000000000000000000000020512751722041022775 0ustar0000000000000000Help commands: - `help commands [-a | --all]` - `help roles [-a | --all]` - `help rules [-a | --all]` - `help variants [-a | --all]` werewolf-1.5.1.1/variant/standard/command/help/hunter-commands.txt0000644000000000000000000000006312717220441023353 0ustar0000000000000000#{humanise hunterRole} commands: - `choose PLAYER` werewolf-1.5.1.1/variant/standard/command/help/hunters-turn.txt0000644000000000000000000000014312717220441022724 0ustar0000000000000000- (When the #{humanise hunterRole} is killed) the #{humanise hunterRole} chooses someone to shoot. werewolf-1.5.1.1/variant/standard/command/help/necromancer-commands.txt0000644000000000000000000000007112751254103024341 0ustar0000000000000000#{humanise necromancerRole} commands: - `raise` - `pass` werewolf-1.5.1.1/variant/standard/command/help/necromancers-turn.txt0000644000000000000000000000010312751254103023707 0ustar0000000000000000- The #{humanise necromancerRole} wakes up and may raise the dead. werewolf-1.5.1.1/variant/standard/command/help/oracle-commands.txt0000644000000000000000000000006312717220441023313 0ustar0000000000000000#{humanise oracleRole} commands: - `divine PLAYER` werewolf-1.5.1.1/variant/standard/command/help/oracles-turn.txt0000644000000000000000000000010212717220441022657 0ustar0000000000000000- The #{humanise oracleRole} wakes up and divines someone's role. werewolf-1.5.1.1/variant/standard/command/help/orphan-commands.txt0000644000000000000000000000006312717220441023335 0ustar0000000000000000#{humanise orphanRole} commands: - `choose PLAYER` werewolf-1.5.1.1/variant/standard/command/help/orphans-turn.txt0000644000000000000000000000012312717220441022704 0ustar0000000000000000- (First round only) the #{humanise orphanRole} wakes up and chooses a role model. werewolf-1.5.1.1/variant/standard/command/help/protector-commands.txt0000644000000000000000000000006712717220441024073 0ustar0000000000000000#{humanise protectorRole} commands: - `protect PLAYER` werewolf-1.5.1.1/variant/standard/command/help/protectors-turn.txt0000644000000000000000000000007712717220441023446 0ustar0000000000000000- The #{humanise protectorRole} wakes up and protects someone. werewolf-1.5.1.1/variant/standard/command/help/role.txt0000644000000000000000000000013712717220441021212 0ustar0000000000000000#{humanise role} (#{T.pack . show $ role ^. balance}): #{role ^. description} #{role ^. rules} werewolf-1.5.1.1/variant/standard/command/help/rules.txt0000644000000000000000000000102612717220441021401 0ustar0000000000000000Each player is informed of their role (see `help roles` for a list) at the start of the game. Each night, the Werewolves transform and subsequently assault and devour one Villager. After feasting, their lycanthropic form subsides and they once again hide in plain sight. Each day, after discovering the victim, the village gathers in the town square. In a democratic fashion they then vote for whom they believe to be a Werewolf. The votee is immediately tied to a pyre and burned alive in an attempt to rid Fougères of all lupines. werewolf-1.5.1.1/variant/standard/command/help/scapegoat-commands.txt0000644000000000000000000000007112717220441024013 0ustar0000000000000000#{humanise scapegoatRole} commands: - `choose PLAYER...` werewolf-1.5.1.1/variant/standard/command/help/scapegoats-turn.txt0000644000000000000000000000016612717220441023372 0ustar0000000000000000- (When the #{humanise scapegoatRole} is blamed) the #{humanise scapegoatRole} chooses whom may vote on the next day. werewolf-1.5.1.1/variant/standard/command/help/seer-commands.txt0000644000000000000000000000005612717220441023006 0ustar0000000000000000#{humanise seerRole} commands: - `see PLAYER` werewolf-1.5.1.1/variant/standard/command/help/seers-turn.txt0000644000000000000000000000010312717220441022351 0ustar0000000000000000- The #{humanise seerRole} wakes up and sees someone's allegiance. werewolf-1.5.1.1/variant/standard/command/help/standard-commands.txt0000644000000000000000000000005612717220441023650 0ustar0000000000000000Standard commands: - `vote PLAYER` - `unvote` werewolf-1.5.1.1/variant/standard/command/help/standard-cycle.txt0000644000000000000000000000006512717220441023146 0ustar0000000000000000A game begins at night and follows a standard cycle. werewolf-1.5.1.1/variant/standard/command/help/status-commands.txt0000644000000000000000000000026212717220441023372 0ustar0000000000000000Status commands: - `ping` ping the status of the current game publicly - `status` get the status of the current game privately - `circle [-a|--include-dead]` get the game circle werewolf-1.5.1.1/variant/standard/command/help/sunrise.txt0000644000000000000000000000005412717220441021737 0ustar0000000000000000- The village wakes up and find the victim. werewolf-1.5.1.1/variant/standard/command/help/sunset.txt0000644000000000000000000000003412717220441021566 0ustar0000000000000000- The village falls asleep. werewolf-1.5.1.1/variant/standard/command/help/variant.txt0000644000000000000000000000012312751722041021711 0ustar0000000000000000#{humanise variant} (#{variant ^. Variant.tag}): #{variant ^. Variant.description} werewolf-1.5.1.1/variant/standard/command/help/village-drunks-turn.txt0000644000000000000000000000014012717220441024160 0ustar0000000000000000- (Third round only) the #{humanise villageDrunkRole} sobers up and remembers their allegiance. werewolf-1.5.1.1/variant/standard/command/help/villages-turn.txt0000644000000000000000000000005012717220441023037 0ustar0000000000000000- The village votes to lynch a suspect. werewolf-1.5.1.1/variant/standard/command/help/werewolves-turn.txt0000644000000000000000000000006612717220441023442 0ustar0000000000000000- The Werewolves wake up and vote to devour a victim. werewolf-1.5.1.1/variant/standard/command/help/win-condition.txt0000644000000000000000000000017412717220441023033 0ustar0000000000000000The game is over when only Villagers or Werewolves are left alive, or when one of the Loners completes their own objective. werewolf-1.5.1.1/variant/standard/command/help/witch-commands.txt0000644000000000000000000000010412717220441023160 0ustar0000000000000000#{humanise witchRole} commands: - `heal` - `poison PLAYER` - `pass` werewolf-1.5.1.1/variant/standard/command/help/witchs-turn.txt0000644000000000000000000000012412717220441022534 0ustar0000000000000000- The #{humanise witchRole} wakes up and may heal the victim and/or poison someone. werewolf-1.5.1.1/variant/standard/command/ping/diurnal-role-pinged.txt0000644000000000000000000000004312727224206024117 0ustar0000000000000000Waiting on the #{humanise role}... werewolf-1.5.1.1/variant/standard/command/ping/nocturnal-role-pinged.txt0000644000000000000000000000004312727224206024466 0ustar0000000000000000Waiting on the #{humanise role}... werewolf-1.5.1.1/variant/standard/command/ping/player-pinged.txt0000644000000000000000000000002212717220441023007 0ustar0000000000000000Waiting on you... werewolf-1.5.1.1/variant/standard/command/ping/village-pinged.txt0000644000000000000000000000003212717220441023137 0ustar0000000000000000Waiting on the village... werewolf-1.5.1.1/variant/standard/command/ping/werewolves-pinged.txt0000644000000000000000000000003512717220441023721 0ustar0000000000000000Waiting on the Werewolves... werewolf-1.5.1.1/variant/standard/command/quit/caller-quit.txt0000644000000000000000000000007612717220441022527 0ustar0000000000000000#{humanise caller} the #{humanise $ caller ^. role} has quit! werewolf-1.5.1.1/variant/standard/command/raise/necromancer-raised-dead.txt0000644000000000000000000000017212751254103025057 0ustar0000000000000000A dark cloud falls upon Fougères, as the #{humanise necromancerRole} begins to raise #{humanise zombies'} from the dead. werewolf-1.5.1.1/variant/standard/command/raise/player-raised-from-dead.txt0000644000000000000000000000026212751254103025020 0ustar0000000000000000#{humanise necromancer'} has raised you from the dead, bringing you back as a #{humanise zombieRole}. Abandon your past life and embrace your new master; follow them to victory! werewolf-1.5.1.1/variant/standard/command/status/alive-players.txt0000644000000000000000000000017612751254103023424 0ustar0000000000000000The following players are still alive: #{humanise $ map humanisePlayerWithRoleIfKnown (game ^.. players . traverse . alive)}. werewolf-1.5.1.1/variant/standard/command/status/current-diurnal-turn.txt0000644000000000000000000000006012727224206024747 0ustar0000000000000000It's currently the #{humanise $ game ^. stage}. werewolf-1.5.1.1/variant/standard/command/status/current-nocturnal-turn.txt0000644000000000000000000000006012727224206025316 0ustar0000000000000000It's currently the #{humanise $ game ^. stage}. werewolf-1.5.1.1/variant/standard/command/status/dead-players.txt0000644000000000000000000000014212717220441023212 0ustar0000000000000000The following players are dead: #{humanisePlayersWithRoles $ game ^.. players . traverse . dead}. werewolf-1.5.1.1/variant/standard/command/status/game-over.txt0000644000000000000000000000002212717220441022517 0ustar0000000000000000The game is over! werewolf-1.5.1.1/variant/standard/command/status/marks.txt0000644000000000000000000000011512751254103021755 0ustar0000000000000000Your marks are as follows: #{humanise $ map humanisePlayerWithState marks'}. werewolf-1.5.1.1/variant/standard/command/unvote/player-rescinded-vote.txt0000644000000000000000000000005112717220441025041 0ustar0000000000000000#{humanise caller} rescinded their vote. werewolf-1.5.1.1/variant/standard/command/version/engine-version.txt0000644000000000000000000000003712717220441023735 0ustar0000000000000000Version #{showVersion version} werewolf-1.5.1.1/variant/standard/command/vote/player-made-devour-vote.txt0000644000000000000000000000006712717220441024755 0ustar0000000000000000#{humanise caller} voted to devour #{humanise target}. werewolf-1.5.1.1/variant/standard/command/vote/player-made-lynch-vote.txt0000644000000000000000000000006612717220441024565 0ustar0000000000000000#{humanise caller} voted to lynch #{humanise target}. werewolf-1.5.1.1/variant/standard/engine/druids-turn/start.txt0000644000000000000000000000013612717220441022564 0ustar0000000000000000Ferina wakes from her slumber, disturbed and on edge. She loudly grunts as she smells danger. werewolf-1.5.1.1/variant/standard/engine/game-over/allegiance-won.txt0000644000000000000000000000006712717220441023721 0ustar0000000000000000The game is over! The #{humanise allegiance} have won. werewolf-1.5.1.1/variant/standard/engine/game-over/dullahan-won.txt0000644000000000000000000000027112727250373023432 0ustar0000000000000000The roads are owned by the #{humanise dullahanRole}. Foolishly #{humanise $ game ^. marks} tried to cross them, but to no avail. The game is over! The #{humanise dullahanRole} has won. werewolf-1.5.1.1/variant/standard/engine/game-over/everyone-lost.txt0000644000000000000000000000004512733071357023653 0ustar0000000000000000The game is over! Everyone has died. werewolf-1.5.1.1/variant/standard/engine/game-over/fallen-angel-won.txt0000644000000000000000000000020312733063164024157 0ustar0000000000000000Foolish mortals, now you have set the #{humanise fallenAngelRole} free! The game is over! The #{humanise fallenAngelRole} has won. werewolf-1.5.1.1/variant/standard/engine/game-over/necromancer-won.txt0000644000000000000000000000011712751254103024125 0ustar0000000000000000Long live the dead! The game is over! The #{humanise necromancerRole} has won. werewolf-1.5.1.1/variant/standard/engine/game-over/player-contributed.txt0000644000000000000000000000005612717220441024646 0ustar0000000000000000Your team won, but you died. Congratulations? werewolf-1.5.1.1/variant/standard/engine/game-over/player-lost.txt0000644000000000000000000000004012717220441023276 0ustar0000000000000000Feck, you lost this time round. werewolf-1.5.1.1/variant/standard/engine/game-over/player-roles.txt0000644000000000000000000000017412717220441023451 0ustar0000000000000000As I know you're all wondering who lied to you, here's the role allocations: #{humanisePlayersWithRoles $ game ^. players}. werewolf-1.5.1.1/variant/standard/engine/game-over/player-won.txt0000644000000000000000000000002212717220441023120 0ustar0000000000000000Victory! You won! werewolf-1.5.1.1/variant/standard/engine/general/player-booted.txt0000644000000000000000000000012312717220441023326 0ustar0000000000000000#{humanise player} the #{humanise $ player ^. role} has been booted from the game! werewolf-1.5.1.1/variant/standard/engine/general/player-killed.txt0000644000000000000000000000003112717220441023314 0ustar0000000000000000Guh, you've been killed! werewolf-1.5.1.1/variant/standard/engine/general/spiteful-villager-killed.txt0000644000000000000000000000032012733100663025460 0ustar0000000000000000Being ethereal seldom has it's benefits. Perhaps however this knowledge of the townsfolks' natures will bring you some joy in the afterlife: #{humanisePlayersWithRoles $ game ^.. players . traverse . alive}. werewolf-1.5.1.1/variant/standard/engine/general/zombies-returned-to-grave.txt0000644000000000000000000000025612751254103025607 0ustar0000000000000000With the #{humanise necromancerRole} gone, there is nothing keeping #{humanise $ game ^.. players . zombies} in this world. They begin to rot and return to whence they came. werewolf-1.5.1.1/variant/standard/engine/hunters-turn/start-private.txt0000644000000000000000000000006212717220441024430 0ustar0000000000000000Whom do you `choose` to kill with your last shot? werewolf-1.5.1.1/variant/standard/engine/hunters-turn/start-public.txt0000644000000000000000000000010112717220441024226 0ustar0000000000000000Just before #{humanise hunter} was murdered they let off a shot. werewolf-1.5.1.1/variant/standard/engine/lynching/jester-lynched.txt0000644000000000000000000000036212751254103023703 0ustar0000000000000000Just as the townsfolk tie #{humanise jester} up to the pyre, a voice in the crowd yells out. "We can't burn #{humanise jester}! They're the joke of the town!" #{humanise jester} the #{humanise jesterRole} is quickly untied and apologised to. werewolf-1.5.1.1/variant/standard/engine/lynching/no-player-lynched.txt0000644000000000000000000000015612717220441024316 0ustar0000000000000000Daylight is wasted as the townsfolk squabble over whom to tie up. Looks like no-one is being burned this day. werewolf-1.5.1.1/variant/standard/engine/lynching/player-lynched.txt0000644000000000000000000000031412717220441023700 0ustar0000000000000000#{humanise player} is tied up to a pyre and set alight. Eventually the screams start to die and with their last breath, they reveal themselves as #{article $ player ^. role} #{humanise $ player ^. role}. werewolf-1.5.1.1/variant/standard/engine/lynching/saint-lynched.txt0000644000000000000000000000017412733071357023536 0ustar0000000000000000Furious with the village for murdering them, God enacts revenge upon those responsible: #{humanisePlayersWithRoles voters}. werewolf-1.5.1.1/variant/standard/engine/lynching/scapegoat-lynched.txt0000644000000000000000000000040312717220441024351 0ustar0000000000000000The townsfolk squabble over whom to tie up. Just as they are about to call it a day they notice that #{humanise scapegoat} has been acting awfully suspicious. Not wanting to take any chances, #{humanise scapegoat} is promptly tied to a pyre and burned alive. werewolf-1.5.1.1/variant/standard/engine/lynching/werewolf-lynched.txt0000644000000000000000000000040412717220440024235 0ustar0000000000000000#{humanise werewolf} is tied up to a pyre and set alight. As they scream their body starts to contort and writhe, transforming into #{article $ werewolf ^. role} #{humanise $ werewolf ^. role}. Thankfully they go limp before breaking free of their restraints. werewolf-1.5.1.1/variant/standard/engine/necromancers-turn/start-private.txt0000644000000000000000000000010212751254103025412 0ustar0000000000000000Would you like to `raise` the dead? Type `pass` to end your turn. werewolf-1.5.1.1/variant/standard/engine/necromancers-turn/start-public.txt0000644000000000000000000000005212751254103025222 0ustar0000000000000000The #{humanise necromancerRole} wakes up. werewolf-1.5.1.1/variant/standard/engine/new-game/beholder.txt0000644000000000000000000000025212717220441022432 0ustar0000000000000000The #{humanise seerRole} has always been held in high regard among the Villagers. Few are as lucky as you to know the #{humanise seerRole}, #{humanise seer}, personally. werewolf-1.5.1.1/variant/standard/engine/new-game/dullahan.txt0000644000000000000000000000020012727250373022437 0ustar0000000000000000Word has come in the night: #{humanise $ game ^. marks} are marked for death. Fulfil your purpose; ride hard and run them down. werewolf-1.5.1.1/variant/standard/engine/new-game/game-variant.txt0000644000000000000000000000007412733063160023224 0ustar0000000000000000The game variant is set to "#{humanise $ game ^. variant}". werewolf-1.5.1.1/variant/standard/engine/new-game/new-player.txt0000644000000000000000000000017312717220441022733 0ustar0000000000000000You're #{article $ player ^. role} #{humanise $ player ^. role}. #{player ^. role . description} #{player ^. role . rules} werewolf-1.5.1.1/variant/standard/engine/new-game/players-in-game.txt0000644000000000000000000000011012717220441023631 0ustar0000000000000000A new game of Werewolf is starting with #{humanise $ game ^.. players}! werewolf-1.5.1.1/variant/standard/engine/new-game/roles-in-game.txt0000644000000000000000000000026612717220441023312 0ustar0000000000000000The roles in play are #{humanise $ map (\(role, count) -> T.concat [humanise role, " (", T.pack $ show count, ")"]) roleCounts} for a total balance of #{T.pack $ show totalBalance}. werewolf-1.5.1.1/variant/standard/engine/new-game/true-villager.txt0000644000000000000000000000040512717220441023430 0ustar0000000000000000Unguarded advice is seldom given, for advice is a dangerous gift, even from the wise to the wise, and all courses may run ill. Yet as you feel like you need help, I begrudgingly leave you with this: #{humanise trueVillager} is the #{humanise trueVillagerRole}. werewolf-1.5.1.1/variant/standard/engine/oracles-turn/start-private.txt0000644000000000000000000000004712717220441024373 0ustar0000000000000000Whose role would you like to `divine`? werewolf-1.5.1.1/variant/standard/engine/oracles-turn/start-public.txt0000644000000000000000000000004512717220441024175 0ustar0000000000000000The #{humanise oracleRole} wakes up. werewolf-1.5.1.1/variant/standard/engine/orphans-turn/player-joined-werewolves-group.txt0000644000000000000000000000030612717220441027702 0ustar0000000000000000#{humanise orphan} the #{humanise orphanRole} scampers off into the woods. Without their role model they have abandoned the village and are in search of a new home. You welcome them into your pack. werewolf-1.5.1.1/variant/standard/engine/orphans-turn/player-joined-werewolves-private.txt0000644000000000000000000000037312717220441030224 0ustar0000000000000000The death of your role model is distressing. Without second thought you abandon the Villagers and run off into the woods, towards a new home. As you arrive you see the #{pluralise (length werewolves) "face"} of #{humanise werewolves} waiting for you. werewolf-1.5.1.1/variant/standard/engine/orphans-turn/start-private.txt0000644000000000000000000000005412717220441024413 0ustar0000000000000000Whom do you `choose` to be your role model? werewolf-1.5.1.1/variant/standard/engine/orphans-turn/start-public.txt0000644000000000000000000000004512717220441024217 0ustar0000000000000000The #{humanise orphanRole} wakes up. werewolf-1.5.1.1/variant/standard/engine/protectors-turn/start-private.txt0000644000000000000000000000004212717220441025142 0ustar0000000000000000Whom would you like to `protect`? werewolf-1.5.1.1/variant/standard/engine/protectors-turn/start-public.txt0000644000000000000000000000005012717220441024745 0ustar0000000000000000The #{humanise protectorRole} wakes up. werewolf-1.5.1.1/variant/standard/engine/scapegoats-turn/end.txt0000644000000000000000000000027512751254103023040 0ustar0000000000000000On the next day only #{humanise $ game ^. chosenVoters} shall be allowed to vote. The Town Crier, realising how foolish it was to kill the #{humanise scapegoatRole}, grants them this wish. werewolf-1.5.1.1/variant/standard/engine/scapegoats-turn/start.txt0000644000000000000000000000025612717220441023426 0ustar0000000000000000Just before the #{humanise scapegoatRole} burns to a complete crisp, they cry out a dying wish. #{humanise scapegoat}, which players do you `choose` to vote on the next day? werewolf-1.5.1.1/variant/standard/engine/seers-turn/start-private.txt0000644000000000000000000000005212717220441024060 0ustar0000000000000000Whose allegiance would you like to `see`? werewolf-1.5.1.1/variant/standard/engine/seers-turn/start-public.txt0000644000000000000000000000004312717220441023664 0ustar0000000000000000The #{humanise seerRole} wakes up. werewolf-1.5.1.1/variant/standard/engine/sunrise/alpha-wolf-seen.txt0000644000000000000000000000006512717220441023622 0ustar0000000000000000#{humanise alphaWolf} is aligned with the Villagers. werewolf-1.5.1.1/variant/standard/engine/sunrise/lycan-seen.txt0000644000000000000000000000006212717220441022673 0ustar0000000000000000#{humanise lycan} is aligned with the Werewolves. werewolf-1.5.1.1/variant/standard/engine/sunrise/no-player-devoured.txt0000644000000000000000000000014612717220441024361 0ustar0000000000000000Surprisingly you see everyone present at the town square. Perhaps the Werewolves have left Fougères? werewolf-1.5.1.1/variant/standard/engine/sunrise/player-devoured.txt0000644000000000000000000000037312717220441023751 0ustar0000000000000000As you open them you notice a door broken down and #{humanise player}'s guts half devoured and spilling out over the cobblestones. From the look of their personal effects, you deduce they were #{article $ player ^. role} #{humanise $ player ^. role}. werewolf-1.5.1.1/variant/standard/engine/sunrise/player-divined.txt0000644000000000000000000000012012717220441023544 0ustar0000000000000000#{humanise player} is #{article $ player ^. role} #{humanise $ player ^. role}. werewolf-1.5.1.1/variant/standard/engine/sunrise/player-poisoned.txt0000644000000000000000000000027212717220441023752 0ustar0000000000000000Upon further discovery, it looks like the #{humanise witchRole} struck in the night. #{humanise player} the #{humanise $ player ^. role} is hanging over the side of their bed, poisoned. werewolf-1.5.1.1/variant/standard/engine/sunrise/player-seen.txt0000644000000000000000000000013112717220441023056 0ustar0000000000000000#{humanise player} is aligned with #{article} #{humanise $ player ^. role . allegiance}. werewolf-1.5.1.1/variant/standard/engine/sunrise/player-turned-to-stone.txt0000644000000000000000000000026612717220441025204 0ustar0000000000000000Next to them you see a stone #{humanise $ player ^. role} statue, cold to the touch. #{humanise player} must have looked into the eyes of the #{humanise medusaRole} at the very end. werewolf-1.5.1.1/variant/standard/engine/sunrise/start.txt0000644000000000000000000000007212717220441021773 0ustar0000000000000000The sun rises. Everybody wakes up and opens their eyes... werewolf-1.5.1.1/variant/standard/engine/sunset/start.txt0000644000000000000000000000004412717220441021623 0ustar0000000000000000Night falls, the village is asleep. werewolf-1.5.1.1/variant/standard/engine/village-drunks-turn/player-joined-village.txt0000644000000000000000000000021312717220441027243 0ustar0000000000000000Somehow you managed to avoid getting killed while in your drunken stupor. Thank God for that, maybe now you can actually help the village. werewolf-1.5.1.1/variant/standard/engine/village-drunks-turn/player-joined-werewolves-group.txt0000644000000000000000000000016112717220441031156 0ustar0000000000000000#{humanise villageDrunk} the #{humanise villageDrunkRole} has finally sobered up and remembered their true home. werewolf-1.5.1.1/variant/standard/engine/village-drunks-turn/player-joined-werewolves-private.txt0000644000000000000000000000027712717220441031504 0ustar0000000000000000As you start to feel sober for the first time in days a new thirst begins to take hold. The bloodthirst starts to bring back memories, memories of your true home with #{humanise werewolves}. werewolf-1.5.1.1/variant/standard/engine/village-drunks-turn/start.txt0000644000000000000000000000005412717220441024220 0ustar0000000000000000The #{humanise villageDrunkRole} sobers up. werewolf-1.5.1.1/variant/standard/engine/villages-turn/start.txt0000644000000000000000000000015612717220441023102 0ustar0000000000000000As the village gathers in the square the Town Clerk calls for a vote. Whom would you like to `vote` to lynch? werewolf-1.5.1.1/variant/standard/engine/werewolves-turn/start-first-round-private.txt0000644000000000000000000000037012717220441027416 0ustar0000000000000000You feel restless, like an old curse is keeping you from sleep. It seems you're not the only one... #{humanise (werewolves \\ [player])} #{conjugateToBe (length werewolves - 1)} also emerging from their #{pluralise (length werewolves - 1) "home"}. werewolf-1.5.1.1/variant/standard/engine/werewolves-turn/start-private.txt0000644000000000000000000000005112717220441025140 0ustar0000000000000000Whom would you like to `vote` to devour? werewolf-1.5.1.1/variant/standard/engine/werewolves-turn/start-public.txt0000644000000000000000000000007312717220441024750 0ustar0000000000000000The Werewolves wake up, transform and choose a new victim. werewolf-1.5.1.1/variant/standard/engine/witchs-turn/heal.txt0000644000000000000000000000014412717220441022346 0ustar0000000000000000You see #{humanise victim} sprawled outside bleeding uncontrollably. Would you like to `heal` them? werewolf-1.5.1.1/variant/standard/engine/witchs-turn/pass.txt0000644000000000000000000000003612717220441022403 0ustar0000000000000000Type `pass` to end your turn. werewolf-1.5.1.1/variant/standard/engine/witchs-turn/poison.txt0000644000000000000000000000004312717220441022742 0ustar0000000000000000Would you like to `poison` anyone? werewolf-1.5.1.1/variant/standard/engine/witchs-turn/start.txt0000644000000000000000000000004412717220441022571 0ustar0000000000000000The #{humanise witchRole} wakes up. werewolf-1.5.1.1/variant/standard/error/command/boot/caller-already-voted-boot.txt0000644000000000000000000000006112717220441026352 0ustar0000000000000000You've already voted to boot #{humanise target}! werewolf-1.5.1.1/variant/standard/error/command/choose/caller-cannot-choose-jester.txt0000644000000000000000000000005612717220441027224 0ustar0000000000000000You cannot choose the #{humanise jesterRole}! werewolf-1.5.1.1/variant/standard/error/command/choose/caller-cannot-choose-self.txt0000644000000000000000000000003412717220441026655 0ustar0000000000000000You cannot choose yourself! werewolf-1.5.1.1/variant/standard/error/command/choose/caller-cannot-choose-zombie.txt0000644000000000000000000000005412751254103027213 0ustar0000000000000000You cannot choose a #{humanise zombieRole}! werewolf-1.5.1.1/variant/standard/error/command/choose/no-target.txt0000644000000000000000000000004312717220441023626 0ustar0000000000000000You must choose at least 1 target! werewolf-1.5.1.1/variant/standard/error/command/general/caller-cannot-do-that-right-now.txt0000644000000000000000000000003612717220441030061 0ustar0000000000000000You cannot do that right now! werewolf-1.5.1.1/variant/standard/error/command/general/caller-cannot-do-that.txt0000644000000000000000000000002412717220441026142 0ustar0000000000000000You cannot do that! werewolf-1.5.1.1/variant/standard/error/command/general/caller-dead.txt0000644000000000000000000000003712717220441024223 0ustar0000000000000000Sshh, you're meant to be dead! werewolf-1.5.1.1/variant/standard/error/command/general/no-game-running.txt0000644000000000000000000000002412717220441025063 0ustar0000000000000000No game is running. werewolf-1.5.1.1/variant/standard/error/command/general/player-does-not-exist.txt0000644000000000000000000000003712717220441026242 0ustar0000000000000000Player #{name} does not exist. werewolf-1.5.1.1/variant/standard/error/command/general/target-dead.txt0000644000000000000000000000004412717220441024245 0ustar0000000000000000#{humanise target} is already dead! werewolf-1.5.1.1/variant/standard/error/command/heal/caller-already-healed.txt0000644000000000000000000000003712717220441025463 0ustar0000000000000000You've already healed someone! werewolf-1.5.1.1/variant/standard/error/command/poison/caller-already-poisoned.txt0000644000000000000000000000004112717220441026452 0ustar0000000000000000You've already poisoned someone! werewolf-1.5.1.1/variant/standard/error/command/protect/caller-cannot-protect-same-player.txt0000644000000000000000000000006312717220441030545 0ustar0000000000000000You cannot protect the same player twice in a row! werewolf-1.5.1.1/variant/standard/error/command/start/game-already-running.txt0000644000000000000000000000003312717220441025610 0ustar0000000000000000A game is already running. werewolf-1.5.1.1/variant/standard/error/command/start/player-count-too-low.txt0000644000000000000000000000003612717220441025625 0ustar0000000000000000Must have at least 7 players. werewolf-1.5.1.1/variant/standard/error/command/start/player-names-not-unique.txt0000644000000000000000000000003512717220441026303 0ustar0000000000000000Player names must be unique. werewolf-1.5.1.1/variant/standard/error/command/start/role-count-restricted.txt0000644000000000000000000000005212717220441026040 0ustar0000000000000000Cannot have more than 1 #{humanise role}. werewolf-1.5.1.1/variant/standard/error/command/start/role-does-not-exist.txt0000644000000000000000000000004112717220441025422 0ustar0000000000000000Role #{roleName} does not exist. werewolf-1.5.1.1/variant/standard/error/command/start/variant-does-not-exist.txt0000644000000000000000000000004712751254103026133 0ustar0000000000000000Variant #{variantName} does not exist. werewolf-1.5.1.1/variant/standard/error/command/unvote/caller-not-voted.txt0000644000000000000000000000002712717220441025147 0ustar0000000000000000You haven't voted yet! werewolf-1.5.1.1/variant/standard/error/command/vote/caller-already-voted.txt0000644000000000000000000000002612717220441025424 0ustar0000000000000000You've already voted! werewolf-1.5.1.1/variant/standard/error/command/vote/caller-cannot-devour-werewolf.txt0000644000000000000000000000004412717220441027300 0ustar0000000000000000You cannot devour another Werewolf! werewolf-1.5.1.1/variant/standard/role/alpha-wolf/description.txt0000644000000000000000000000051712717220441023223 0ustar0000000000000000The Alpha Wolf leads the Werewolves in the raids against Fougères each night and not even the #{humanise seerRole} can see them coming. If the Werewolves caused the Villagers to question and accuse one another beforehand, the Alpha Wolf eliminates any shred of humanity left. No-one can be trusted anymore and no-one knows the truth. werewolf-1.5.1.1/variant/standard/role/alpha-wolf/name.txt0000644000000000000000000000001312717220441021607 0ustar0000000000000000Alpha Wolf werewolf-1.5.1.1/variant/standard/role/alpha-wolf/rules.txt0000644000000000000000000000013612717220441022027 0ustar0000000000000000The Alpha Wolf appears to nature-seeing roles (e.g., the #{humanise seerRole}) as a Villager. werewolf-1.5.1.1/variant/standard/role/beholder/description.txt0000644000000000000000000000047412717220441022757 0ustar0000000000000000Awareness comes easy to the Beholder. They listen to their senses and trust their hunches. Over the years the Beholder has grown to know a certain few of the village just by paying attention. Little cues here and there, the way someone talks, the way they move - it all gives clues as to their true nature and role. werewolf-1.5.1.1/variant/standard/role/beholder/name.txt0000644000000000000000000000001112717220441021337 0ustar0000000000000000Beholder werewolf-1.5.1.1/variant/standard/role/beholder/rules.txt0000644000000000000000000000012712717220441021561 0ustar0000000000000000At the start of the game the Beholder is informed the #{humanise seerRole}'s identity. werewolf-1.5.1.1/variant/standard/role/crooked-senator/description.txt0000644000000000000000000000054012717220441024264 0ustar0000000000000000Never trust a politician. Nor a Crooked Senator for that matter. The Crooked Senator may seem like he has the village's best interests at heart, but let's be honest, when put in a tough situation he looks after no-one but himself. Even when safe, the Crooked Senator may decide to toy with the Villagers' emotions and try pit them against one another. werewolf-1.5.1.1/variant/standard/role/crooked-senator/name.txt0000644000000000000000000000002012717220441022652 0ustar0000000000000000Crooked Senator werewolf-1.5.1.1/variant/standard/role/crooked-senator/rules.txt0000644000000000000000000000010012717220441023063 0ustar0000000000000000The Crooked Senator looks at the village votes as they come in. werewolf-1.5.1.1/variant/standard/role/druid/description.txt0000644000000000000000000000070012717220441022272 0ustar0000000000000000How 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 her warning for she will not let you down. werewolf-1.5.1.1/variant/standard/role/druid/name.txt0000644000000000000000000000000612717220441020666 0ustar0000000000000000Druid werewolf-1.5.1.1/variant/standard/role/druid/rules.txt0000644000000000000000000000025512717220441021106 0ustar0000000000000000Each morning when Ferina wakes from her slumber she will be alert and cautious. If the Druid is next to a Werewolf in the player `circle` then Ferina will grunt in warning. werewolf-1.5.1.1/variant/standard/role/dullahan/description.txt0000644000000000000000000000057012727250373022770 0ustar0000000000000000Normally the Dullahan carries their head under one arm, however while amongst the Villagers, they ere on the side of caution and rest it in a more traditional place. The Dullahan rides a black horse as dark as night and hunts down travellers in the countryside. Beware if the Dullahan knows your name, for you are then marked for death and you should avoid them at all costs. werewolf-1.5.1.1/variant/standard/role/dullahan/name.txt0000644000000000000000000000001112727250373021353 0ustar0000000000000000Dullahan werewolf-1.5.1.1/variant/standard/role/dullahan/rules.txt0000644000000000000000000000021312733071357021571 0ustar0000000000000000The Dullahan is given a list of player names at the start of the game. To win, they must eliminate all of them before the end of the game. werewolf-1.5.1.1/variant/standard/role/fallen-angel/description.txt0000644000000000000000000000061012717220441023510 0ustar0000000000000000Long ago during the War in Heaven, angels fell from the sky as one by one those that followed Lucifer were defeated. For centuries they lived amongst mortal Villagers as punishment for their sins and wrongdoings. The Fallen Angel was one such being and is now one of the few angels left on Earth. Nothing is worse punishment for them, the Fallen Angel yearns for death to once again be free! werewolf-1.5.1.1/variant/standard/role/fallen-angel/name.txt0000644000000000000000000000001512717220441022104 0ustar0000000000000000Fallen Angel werewolf-1.5.1.1/variant/standard/role/fallen-angel/rules.txt0000644000000000000000000000014112717220441022316 0ustar0000000000000000The Fallen Angel wins if they manage to get lynched by the Villagers before the end of the game. werewolf-1.5.1.1/variant/standard/role/hunter/description.txt0000644000000000000000000000030712717220441022473 0ustar0000000000000000A skilled marksman with quick reflexes. In the unfortunate situation that they are jumped and killed unjustly, they let off a shot at their attacker, killing them instantly. The Hunter never misses. werewolf-1.5.1.1/variant/standard/role/hunter/name.txt0000644000000000000000000000000712717220441021065 0ustar0000000000000000Hunter werewolf-1.5.1.1/variant/standard/role/hunter/rules.txt0000644000000000000000000000014112717220441021276 0ustar0000000000000000If the Hunter is killed they choose one player, believed to be an attacker, to kill immediately. werewolf-1.5.1.1/variant/standard/role/jester/description.txt0000644000000000000000000000027212717220441022463 0ustar0000000000000000Every 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 wants to hurt them. werewolf-1.5.1.1/variant/standard/role/jester/name.txt0000644000000000000000000000000712717220441021054 0ustar0000000000000000Jester werewolf-1.5.1.1/variant/standard/role/jester/rules.txt0000644000000000000000000000035412717220441021273 0ustar0000000000000000If the village votes to lynch the Jester, their identity is revealed. The village realise there's no point in burning them and so they are set free. The Jester continues to play but may no longer vote as no-one can take them seriously. werewolf-1.5.1.1/variant/standard/role/lycan/description.txt0000644000000000000000000000051412717220441022274 0ustar0000000000000000Traditionally a Werewolf once transformed loses all memories and personality. Over years of transforming, the Lycan has slowly evolved and learnt how to retain themself. Night after night of devouring with the other Werewolves took its toll. The screams alone were enough to turn the Lycan and make them question their true nature. werewolf-1.5.1.1/variant/standard/role/lycan/name.txt0000644000000000000000000000000612717220441020665 0ustar0000000000000000Lycan werewolf-1.5.1.1/variant/standard/role/lycan/rules.txt0000644000000000000000000000017412717220441021105 0ustar0000000000000000The Lycan is aligned with the Villagers, but appears to nature-seeing roles (e.g., the #{humanise seerRole}) as a Werewolf. werewolf-1.5.1.1/variant/standard/role/medusa/description.txt0000644000000000000000000000070412751254103022445 0ustar0000000000000000A beautiful flirt, the Medusa is aligned with the Villagers but harbours a terrifying secret. During the day they are well known in the village of Fougères for their stunning appearance which captures the eye and love of all the townsfolk. However when their secret takes ahold at sundown, their true self is revealed. Any who gaze upon their true form would see live snakes for hair and the few that further look into their eyes are turned to stone. werewolf-1.5.1.1/variant/standard/role/medusa/name.txt0000644000000000000000000000000712717220441021036 0ustar0000000000000000Medusa werewolf-1.5.1.1/variant/standard/role/medusa/rules.txt0000644000000000000000000000033112717220441021250 0ustar0000000000000000If Medusa attracts the attention of a Werewolf during the night and is devoured, the first Werewolf to their left in the player `circle` will catch their gaze and turn to stone, instantly killing the lupine predator. werewolf-1.5.1.1/variant/standard/role/necromancer/description.txt0000644000000000000000000000056712751254103023472 0ustar0000000000000000The dead are feared among the living; the dead outnumber the living. In most villages the dead remain that way, but not when the Necromancer is present. The Necromancer devoted their life to learning black magic and methods of bringing people back to life. Unfortunately this art is hard to perfect and all they can manage to bring back are soulless #{humanise zombieRole}s. werewolf-1.5.1.1/variant/standard/role/necromancer/name.txt0000644000000000000000000000001412751254103022052 0ustar0000000000000000Necromancer werewolf-1.5.1.1/variant/standard/role/necromancer/rules.txt0000644000000000000000000000054212751254103022272 0ustar0000000000000000Once per game the Necromancer can choose to resurrect all dead players as #{humanise zombieRole}s. The #{humanise zombieRole}s are aligned with the Necromancer and they cannot be lynched or devoured. If the Necromancer is killed, all #{humanise zombieRole}s die with them. The Necromancer and #{humanise zombieRole}s win if they are the last ones alive. werewolf-1.5.1.1/variant/standard/role/oracle/description.txt0000644000000000000000000000035312717220441022434 0ustar0000000000000000Originally rejected by the townsfolk, the Oracle's prophetic divinations has earned trust within the village. With constant precognition - and concern for the future - the Oracle knows the village will only live if they work together. werewolf-1.5.1.1/variant/standard/role/oracle/name.txt0000644000000000000000000000000712717220441021025 0ustar0000000000000000Oracle werewolf-1.5.1.1/variant/standard/role/oracle/rules.txt0000644000000000000000000000027512717220441021246 0ustar0000000000000000Each night the Oracle chooses a player to divine. They are then informed of the player's role the following morning. This wisdom is for the Oracle to use to ensure the future of Fougères. werewolf-1.5.1.1/variant/standard/role/orphan/description.txt0000644000000000000000000000102412717220441022452 0ustar0000000000000000Abandoned 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 looked twice at the Orphan and even fewer showed kindness towards the lonely child. One day however, one townsperson changed all this. He offered the Orphan food, water and a roof over their head. Grateful for his chairty and affection, the Orphan made him their role model. Pray that no ill should befall their role model, for they are the only one conforming the Orphan as a Villager. werewolf-1.5.1.1/variant/standard/role/orphan/name.txt0000644000000000000000000000000712717220441021047 0ustar0000000000000000Orphan werewolf-1.5.1.1/variant/standard/role/orphan/rules.txt0000644000000000000000000000032712717220441021266 0ustar0000000000000000On the first night, the Orphan chooses a player to become their role model. So long as the role model is alive, the Orphan is a Villager. If however the role model is eliminated, then the Orphan becomes a Werewolf. werewolf-1.5.1.1/variant/standard/role/protector/description.txt0000644000000000000000000000036612717220441023214 0ustar0000000000000000The Protector is one of the few pure of heart and altruistic Villagers; they are forever putting others needs above their own. Each night they fight against the Werewolves with naught but a sword and shield, potentially saving an innocents life. werewolf-1.5.1.1/variant/standard/role/protector/name.txt0000644000000000000000000000001212717220441021575 0ustar0000000000000000Protector werewolf-1.5.1.1/variant/standard/role/protector/rules.txt0000644000000000000000000000034112717220441022014 0ustar0000000000000000Each night the Protector chooses a player deemed worthy of their protection. That player is safe for that night (and only that night) against the Werewolves. The Protector may not protect the same player two nights in a row. werewolf-1.5.1.1/variant/standard/role/saint/description.txt0000644000000000000000000000035212733071357022314 0ustar0000000000000000The Saint, also historically known as a hallow, is recognized as having an exceptional degree of holiness and likeness to God. They are a humble Villager and shine light on these dark times. Extinguishing this light would not be wise werewolf-1.5.1.1/variant/standard/role/saint/name.txt0000644000000000000000000000000612733071357020705 0ustar0000000000000000Saint werewolf-1.5.1.1/variant/standard/role/saint/rules.txt0000644000000000000000000000010412733071357021116 0ustar0000000000000000If the Saint is lynched by the village, all who voted for them die. werewolf-1.5.1.1/variant/standard/role/scapegoat/description.txt0000644000000000000000000000040512717220441023133 0ustar0000000000000000Werewolves 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! werewolf-1.5.1.1/variant/standard/role/scapegoat/name.txt0000644000000000000000000000001212717220441021522 0ustar0000000000000000Scapegoat werewolf-1.5.1.1/variant/standard/role/scapegoat/rules.txt0000644000000000000000000000033412717220441021743 0ustar0000000000000000If 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: they must choose whom is permitted to vote or not on the next day. werewolf-1.5.1.1/variant/standard/role/seer/description.txt0000644000000000000000000000041312717220441022122 0ustar0000000000000000The 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! The visions will always be true, but only for the present as not even the Seer knows what the future holds. werewolf-1.5.1.1/variant/standard/role/seer/name.txt0000644000000000000000000000000512717220441020514 0ustar0000000000000000Seer werewolf-1.5.1.1/variant/standard/role/seer/rules.txt0000644000000000000000000000010712717220441020731 0ustar0000000000000000Each night the Seer sees the allegiance of one player of their choice. werewolf-1.5.1.1/variant/standard/role/simple-villager/description.txt0000644000000000000000000000033612717220441024264 0ustar0000000000000000A 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 all the Villagers in this unfortunate time. werewolf-1.5.1.1/variant/standard/role/simple-villager/name.txt0000644000000000000000000000002012717220441022647 0ustar0000000000000000Simple Villager werewolf-1.5.1.1/variant/standard/role/simple-villager/rules.txt0000644000000000000000000000020312717220441023064 0ustar0000000000000000The Simple Villager has no special abilities, they must use their guile to determine whom among them is not who they say they are. werewolf-1.5.1.1/variant/standard/role/simple-werewolf/description.txt0000644000000000000000000000053612717220441024313 0ustar0000000000000000The Simple Werewolf is a fearsome lupine, cunning like no other creature that roams the forest. Their origin is unknown, but that matters little, for they present a grave threat to Fougères. While each day they hide in plain sight as an ordinary Villager, each night they transform and devour an innocent. There is little hope left for the village. werewolf-1.5.1.1/variant/standard/role/simple-werewolf/name.txt0000644000000000000000000000002012717220441022674 0ustar0000000000000000Simple Werewolf werewolf-1.5.1.1/variant/standard/role/simple-werewolf/rules.txt0000644000000000000000000000005612717220441023117 0ustar0000000000000000A Werewolf may never devour another Werewolf. werewolf-1.5.1.1/variant/standard/role/spiteful-villager/description.txt0000644000000000000000000000052212733100663024624 0ustar0000000000000000A 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 all the Villagers in this unfortunate time. Yet the Spiteful Villager has no loyalty in the afterlife; whoever causes them harm may find themselves in trouble. werewolf-1.5.1.1/variant/standard/role/spiteful-villager/name.txt0000644000000000000000000000002212733100663023214 0ustar0000000000000000Spiteful Villager werewolf-1.5.1.1/variant/standard/role/spiteful-villager/rules.txt0000644000000000000000000000016412733100663023435 0ustar0000000000000000When the Spiteful Villager is killed, they are informed of everyone's roles and may haunt the village as they wish. werewolf-1.5.1.1/variant/standard/role/true-villager/description.txt0000644000000000000000000000033312717220441023747 0ustar0000000000000000The True Villager has a heart and soul as clear as day! Their allegiance and devotion to the village are beyond reproach. If there is one person whom you should confide in, listen to and trust, it is the True Villager. werewolf-1.5.1.1/variant/standard/role/true-villager/name.txt0000644000000000000000000000001612717220441022342 0ustar0000000000000000True Villager werewolf-1.5.1.1/variant/standard/role/true-villager/rules.txt0000644000000000000000000000010312717220441022551 0ustar0000000000000000At the start of the game the True Villager's identity is revealed. werewolf-1.5.1.1/variant/standard/role/village-drunk/description.txt0000644000000000000000000000063412717220441023735 0ustar0000000000000000Hah, maybe not as liked as the #{humanise jesterRole}, but the Drunk sure does their fair share of stupid things in the night! No-one knows if they even actually make it home; sometimes people see them sleeping outside the Blacksmith's home, others say they see them wandering towards the woods. It's pointless quizzing the Village Drunk in the morning about their doings; they can never remember what they did! werewolf-1.5.1.1/variant/standard/role/village-drunk/name.txt0000644000000000000000000000001612717220441022324 0ustar0000000000000000Village Drunk werewolf-1.5.1.1/variant/standard/role/village-drunk/rules.txt0000644000000000000000000000026412717220441022543 0ustar0000000000000000The Village Drunk is initially aligned with the Villagers. On the third night the Village Drunk sobers up and is randomly assigned a new alignment, either Villagers or Werewolves. werewolf-1.5.1.1/variant/standard/role/witch/description.txt0000644000000000000000000000054012717220441022303 0ustar0000000000000000Somehow forgotten with the coming of the Werewolves, the Witch has a chance to prove themself valuable to the village and maybe abolish the absurd pastime of burning and drowning their cult. The Witch is blessed (or maybe cursed) with the ability to make two powerful potions; one of which heals a victim of the Werewolves, the other poisons a player. werewolf-1.5.1.1/variant/standard/role/witch/name.txt0000644000000000000000000000000612717220441020675 0ustar0000000000000000Witch werewolf-1.5.1.1/variant/standard/role/witch/rules.txt0000644000000000000000000000026312717220441021114 0ustar0000000000000000The 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 on healing themself. werewolf-1.5.1.1/variant/standard/role/zombie/description.txt0000644000000000000000000000020612751254103022451 0ustar0000000000000000A loyal follower of the #{humanise necromancerRole}. A Zombie has no mind of its own and blindly obeys every command of their master. werewolf-1.5.1.1/variant/standard/role/zombie/name.txt0000644000000000000000000000000712751254103021045 0ustar0000000000000000Zombie werewolf-1.5.1.1/variant/standard/role/zombie/rules.txt0000644000000000000000000000020712751254103021261 0ustar0000000000000000A Zombie wins with the #{humanise necromancerRole}. They cannot be killed, however they die when the #{humanise necromancerRole} dies.