werewolf 1.4.0.1 → 1.5.0.0
raw patch · 32 files changed
+321/−138 lines, 32 files
Files
- CHANGELOG.md +16/−0
- README.md +1/−1
- app/Game/Werewolf/Message/Command.hs +16/−12
- app/Game/Werewolf/Message/Engine.hs +35/−34
- app/Game/Werewolf/Message/Error.hs +4/−1
- app/Game/Werewolf/Variant/Standard/Command.hs +8/−4
- app/Game/Werewolf/Variant/Standard/Error.hs +4/−1
- app/Werewolf/Command/Help.hs +18/−6
- app/Werewolf/Command/Start.hs +20/−9
- app/Werewolf/Options.hs +8/−11
- src/Game/Werewolf.hs +4/−2
- src/Game/Werewolf/Game.hs +2/−15
- src/Game/Werewolf/Player.hs +33/−33
- src/Game/Werewolf/Variant.hs +121/−0
- variant/no-role-knowledge/description.txt +1/−0
- variant/no-role-knowledge/name.txt +1/−0
- variant/no-role-reveal/description.txt +1/−0
- variant/no-role-reveal/name.txt +1/−0
- variant/spiteful-village/description.txt +1/−0
- variant/spiteful-village/name.txt +1/−0
- variant/standard/command/help/help-commands.txt +1/−0
- variant/standard/command/help/variant.txt +2/−0
- variant/standard/description.txt +1/−0
- variant/standard/engine/lynching/jester-lynched.txt +1/−1
- variant/standard/error/command/start/variant-does-not-exist.txt +1/−0
- variant/standard/name.txt +1/−0
- variant/standard/role/medusa/description.txt +1/−1
- variant/standard/role/necromancer/description.txt +1/−1
- variant/standard/role/necromancer/rules.txt +2/−3
- variant/standard/role/zombie/description.txt +1/−1
- variant/standard/role/zombie/rules.txt +1/−1
- werewolf.cabal +12/−1
CHANGELOG.md view
@@ -7,6 +7,22 @@ ### Upcoming +### 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*
README.md view
@@ -94,7 +94,7 @@ * Standard * No role knowledge (players are not informed of the roles in play at the start of a game) * No role reveal (player roles are not revealed upon death)-* Spiteful Villagers (the default Villager role becomes the Spiteful Villager)+* Spiteful village (the default Villager role becomes the Spiteful Villager) ### Installing
app/Game/Werewolf/Message/Command.hs view
@@ -31,7 +31,7 @@ necromancerCommandsMessage, oracleCommandsMessage, orphanCommandsMessage, protectorCommandsMessage, roleMessage, rulesMessage, scapegoatCommandsMessage, seerCommandsMessage, stagesMessage, standardCommandsMessage, statusCommandsMessage,- witchCommandsMessage,+ variantMessage, witchCommandsMessage, -- * Ping pingDiurnalRoleMessage, pingNocturnalRoleMessage, pingPlayerMessage, pingVillageMessage,@@ -64,14 +64,15 @@ 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.NoRoleReveal.Command as NoRoleReveal import Game.Werewolf.Variant.Standard.Command as Standard playerQuitMessage :: Player -> Game -> Message playerQuitMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.callerQuitText player- | otherwise = publicMessage $ Standard.callerQuitText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.callerQuitText player+ | otherwise = publicMessage $ Standard.callerQuitText player playerVotedToBootMessage :: Player -> Player -> Message playerVotedToBootMessage caller = publicMessage . callerVotedBootText caller@@ -81,8 +82,8 @@ playerShotMessage :: Player -> Game -> Message playerShotMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerShotText player- | otherwise = publicMessage $ Standard.playerShotText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerShotText player+ | otherwise = publicMessage $ Standard.playerShotText player gameEndedMessage :: Text -> Message gameEndedMessage = publicMessage . gameEndedText@@ -161,6 +162,9 @@ 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 @@ -169,8 +173,8 @@ pingNocturnalRoleMessage :: Role -> Game -> Message pingNocturnalRoleMessage role game- | has (variant . _NoRoleKnowledge) game = publicMessage $ NoRoleKnowledge.nocturnalRolePingedText role- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.nocturnalRolePingedText role+ | has (variant . noRoleKnowledge) game = publicMessage $ NoRoleKnowledge.nocturnalRolePingedText role+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.nocturnalRolePingedText role | otherwise = publicMessage $ Standard.nocturnalRolePingedText role pingPlayerMessage :: Text -> Message@@ -181,8 +185,8 @@ pingWerewolvesMessage :: Game -> Message pingWerewolvesMessage game- | has (variant . _NoRoleKnowledge) game = publicMessage NoRoleKnowledge.werewolvesPingedText- | has (variant . _NoRoleReveal) game = publicMessage NoRoleReveal.werewolvesPingedText+ | has (variant . noRoleKnowledge) game = publicMessage NoRoleKnowledge.werewolvesPingedText+ | has (variant . noRoleReveal) game = publicMessage NoRoleReveal.werewolvesPingedText | otherwise = publicMessage Standard.werewolvesPingedText deadRaisedMessages :: Game -> [Message]@@ -203,8 +207,8 @@ currentNocturnalTurnMessage :: Text -> Game -> Message currentNocturnalTurnMessage to game- | has (variant . _NoRoleKnowledge) game = privateMessage to $ NoRoleKnowledge.currentNocturnalTurnText game- | has (variant . _NoRoleReveal) game = privateMessage to $ NoRoleReveal.currentNocturnalTurnText game+ | has (variant . noRoleKnowledge) game = privateMessage to $ NoRoleKnowledge.currentNocturnalTurnText game+ | has (variant . noRoleReveal) game = privateMessage to $ NoRoleReveal.currentNocturnalTurnText game | otherwise = privateMessage to $ Standard.currentNocturnalTurnText game gameIsOverMessage :: Text -> Message@@ -219,7 +223,7 @@ alivePlayersText' = alivePlayersText game deadPlayersText' | hasn't (players . traverse . dead) game = T.empty- | has (variant . _NoRoleReveal) game = NoRoleReveal.deadPlayersText game+ | has (variant . noRoleReveal) game = NoRoleReveal.deadPlayersText game | otherwise = Standard.deadPlayersText game playerRescindedVoteMessage :: Text -> Player -> Message
app/Game/Werewolf/Message/Engine.hs view
@@ -35,14 +35,15 @@ 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.NoRoleReveal.Engine as NoRoleReveal import Game.Werewolf.Variant.Standard.Engine as Standard playerBootedMessage :: Player -> Game -> Message playerBootedMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerBootedText player- | otherwise = publicMessage $ Standard.playerBootedText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerBootedText player+ | otherwise = publicMessage $ Standard.playerBootedText player gameOverMessages :: Game -> [Message] gameOverMessages game@@ -127,7 +128,7 @@ players' = game ^. players gameVariantMessages = [ gameVariantMessage game- | hasn't (variant . _Standard) game+ | hasn't (variant . standard) game ] beholderMessages = [ beholderMessage beholderName game@@ -153,7 +154,7 @@ rolesInGameMessage :: Maybe Text -> Game -> Message rolesInGameMessage mTo game- | has (variant . _NoRoleKnowledge) game = Message mTo $ NoRoleKnowledge.rolesInGameText game+ | has (variant . noRoleKnowledge) game = Message mTo $ NoRoleKnowledge.rolesInGameText game | otherwise = Message mTo $ Standard.rolesInGameText game newPlayerMessage :: Player -> Message@@ -167,8 +168,8 @@ spitefulVillagerKilledMessage :: Text -> Game -> Message spitefulVillagerKilledMessage to game- | has (variant . _NoRoleReveal) game = privateMessage to $ NoRoleReveal.spitefulVillagerKilledText game- | otherwise = privateMessage to $ Standard.spitefulVillagerKilledText game+ | has (variant . noRoleReveal) game = privateMessage to $ NoRoleReveal.spitefulVillagerKilledText game+ | otherwise = privateMessage to $ Standard.spitefulVillagerKilledText game trueVillagerMessage :: Game -> Message trueVillagerMessage = publicMessage . trueVillagerText@@ -213,9 +214,9 @@ necromancersTurnMessages :: Text -> Game -> [Message] necromancersTurnMessages to game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = [ privateMessage to necromancersTurnPrivateText ]- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = [ privateMessage to necromancersTurnPrivateText ] | otherwise = [ publicMessage necromancersTurnPublicText@@ -224,9 +225,9 @@ oraclesTurnMessages :: Text -> Game -> [Message] oraclesTurnMessages to game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = [ privateMessage to oraclesTurnPrivateText ]- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = [ privateMessage to oraclesTurnPrivateText ] | otherwise = [ publicMessage oraclesTurnPublicText@@ -235,9 +236,9 @@ orphansTurnMessages :: Text -> Game -> [Message] orphansTurnMessages to game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = [ privateMessage to orphansTurnPrivateText ]- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = [ privateMessage to orphansTurnPrivateText ] | otherwise = [ publicMessage orphansTurnPublicText@@ -246,9 +247,9 @@ protectorsTurnMessages :: Text -> Game -> [Message] protectorsTurnMessages to game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = [ privateMessage to protectorsTurnPrivateText ]- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = [ privateMessage to protectorsTurnPrivateText ] | otherwise = [ publicMessage protectorsTurnPublicText@@ -260,9 +261,9 @@ seersTurnMessages :: Text -> Game -> [Message] seersTurnMessages to game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = [ privateMessage to seersTurnPrivateText ]- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = [ privateMessage to seersTurnPrivateText ] | otherwise = [ publicMessage seersTurnPublicText@@ -271,8 +272,8 @@ villageDrunksTurnMessages :: Game -> [Message] villageDrunksTurnMessages game- | has (variant . _NoRoleKnowledge) game = []- | has (variant . _NoRoleReveal) game = []+ | has (variant . noRoleKnowledge) game = []+ | has (variant . noRoleReveal) game = [] | otherwise = [publicMessage villageDrunksTurnText] sunriseMessage :: Message@@ -294,9 +295,9 @@ werewolvesTurnMessages :: [Text] -> Game -> [Message] werewolvesTurnMessages tos game- | has (variant . _NoRoleKnowledge) game =+ | has (variant . noRoleKnowledge) game = groupMessages tos werewolvesTurnPrivateText- | has (variant . _NoRoleReveal) game =+ | has (variant . noRoleReveal) game = groupMessages tos werewolvesTurnPrivateText | otherwise = publicMessage werewolvesTurnPublicText@@ -312,8 +313,8 @@ where to = game ^?! players . witches . name wakeUpMessages- | has (variant . _NoRoleKnowledge) game = []- | has (variant . _NoRoleReveal) game = []+ | has (variant . noRoleKnowledge) game = []+ | has (variant . noRoleReveal) game = [] | otherwise = [publicMessage witchsTurnText] passMessage = privateMessage to passText healMessages@@ -355,18 +356,18 @@ werewolfLynchedMessage :: Player -> Game -> Message werewolfLynchedMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player- | otherwise = publicMessage $ Standard.werewolfLynchedText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player+ | otherwise = publicMessage $ Standard.werewolfLynchedText player playerLynchedMessage :: Player -> Game -> Message playerLynchedMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player- | otherwise = publicMessage $ Standard.playerLynchedText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerLynchedText player+ | otherwise = publicMessage $ Standard.playerLynchedText player saintLynchedMessage :: [Player] -> Game -> Message saintLynchedMessage voters game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.saintLynchedText voters- | otherwise = publicMessage $ Standard.saintLynchedText voters+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.saintLynchedText voters+ | otherwise = publicMessage $ Standard.saintLynchedText voters scapegoatLynchedMessage :: Game -> Message scapegoatLynchedMessage = publicMessage . scapegoatLynchedText@@ -379,16 +380,16 @@ playerDevouredMessage :: Player -> Game -> Message playerDevouredMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerDevouredText player- | otherwise = publicMessage $ Standard.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 . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerPoisonedText player- | otherwise = publicMessage $ Standard.playerPoisonedText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerPoisonedText player+ | otherwise = publicMessage $ Standard.playerPoisonedText player playerSeenMessage :: Text -> Player -> Message playerSeenMessage to player@@ -398,8 +399,8 @@ playerTurnedToStoneMessage :: Player -> Game -> Message playerTurnedToStoneMessage player game- | has (variant . _NoRoleReveal) game = publicMessage $ NoRoleReveal.playerTurnedToStoneText player- | otherwise = publicMessage $ Standard.playerTurnedToStoneText player+ | has (variant . noRoleReveal) game = publicMessage $ NoRoleReveal.playerTurnedToStoneText player+ | otherwise = publicMessage $ Standard.playerTurnedToStoneText player villageDrunkJoinedVillageMessage :: Text -> Message villageDrunkJoinedVillageMessage to = privateMessage to villageDrunkJoinedVillageText
app/Game/Werewolf/Message/Error.hs view
@@ -35,7 +35,7 @@ -- ** Start gameAlreadyRunningMessage, mustHaveAtLeast7PlayersMessage, playerNamesMustBeUniqueMessage,- roleCountRestrictedMessage, roleDoesNotExistMessage,+ roleCountRestrictedMessage, roleDoesNotExistMessage, variantDoesNotExistMessage, -- ** Unvote playerHasNotVotedMessage,@@ -107,6 +107,9 @@ 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
app/Game/Werewolf/Variant/Standard/Command.hs view
@@ -32,8 +32,8 @@ oracleCommandsText, oraclesTurnText, orphanCommandsText, orphansTurnText, protectorCommandsText, protectorsTurnText, roleDescriptionText, scapegoatCommandsText, scapegoatsTurnText, seerCommandsText, seersTurnText, standardCommandsText, standardCycleText, statusCommandsText,- sunriseText, sunsetText, villageDrunksTurnText, villagesTurnText, werewolvesTurnText,- winConditionText, witchCommandsText, witchsTurnText,+ sunriseText, sunsetText, variantDescriptionText, villageDrunksTurnText, villagesTurnText,+ werewolvesTurnText, winConditionText, witchCommandsText, witchsTurnText, -- * Ping diurnalRolePingedText, nocturnalRolePingedText, playerPingedText, villagePingedText,@@ -67,8 +67,9 @@ import qualified Data.Text as T import Data.Version -import Game.Werewolf-import Game.Werewolf.Message+import Game.Werewolf+import Game.Werewolf.Message+import qualified Game.Werewolf.Variant as Variant import Werewolf.Version @@ -158,6 +159,9 @@ 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|]
app/Game/Werewolf/Variant/Standard/Error.hs view
@@ -37,7 +37,7 @@ -- ** Start gameAlreadyRunningText, playerCountTooLowText, playerNamesNotUniqueText,- roleCountRestrictedText, roleDoesNotExistText,+ roleCountRestrictedText, roleDoesNotExistText, variantDoesNotExistText, -- ** Unvote callerNotVotedText,@@ -108,6 +108,9 @@ 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|]
app/Werewolf/Command/Help.hs view
@@ -42,6 +42,8 @@ { optAll :: Bool } | Roles { optAll :: Bool+ } | Variants+ { optAll :: Bool } deriving (Eq, Show) handle :: MonadIO m => Text -> Text -> Options -> m ()@@ -53,9 +55,9 @@ } handle callerName tag (Options (Just (Roles optAll))) = do mGame <- getGame tag optAll- let mGame' = mGame >>= \game -> case game ^. variant of- NoRoleKnowledge -> Nothing- _ -> Just game+ 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' @@ -64,9 +66,9 @@ } handle callerName tag (Options (Just (Rules optAll))) = do mGame <- getGame tag optAll- let mGame' = mGame >>= \game -> case game ^. variant of- NoRoleKnowledge -> Nothing- _ -> Just game+ let mGame' = mGame >>= \game -> if has (variant . noRoleKnowledge) game+ then Nothing+ else Just game exitWith success { messages = rulesMessages callerName mGame'@@ -74,6 +76,16 @@ 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 =
app/Werewolf/Command/Start.hs view
@@ -29,9 +29,10 @@ import Data.Text (Text) -import Game.Werewolf-import Game.Werewolf.Engine-import Game.Werewolf.Message.Error+import Game.Werewolf+import Game.Werewolf.Engine+import Game.Werewolf.Message.Error+import qualified Game.Werewolf.Variant as Variant import System.Random.Shuffle @@ -39,7 +40,7 @@ data Options = Options { optExtraRoles :: ExtraRoles- , optVariant :: Variant+ , optVariant :: Text , argPlayers :: [Text] } deriving (Eq, Show) @@ -58,12 +59,14 @@ Random -> randomExtraRoles $ length playerNames Use roleNames -> useExtraRoles callerName roleNames - let defaultVillagerRole = if variant == SpitefulVillagers then spitefulVillagerRole else simpleVillagerRole+ 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+ runWriterT $ startGame callerName variant' players >>= execStateT checkStage case result of Left errorMessages -> exitWith failure { messages = errorMessages }@@ -79,12 +82,20 @@ take count <$> shuffleM restrictedRoles useExtraRoles :: MonadError [Message] m => Text -> [Text] -> m [Role]-useExtraRoles callerName roleNames = forM roleNames $ \roleName -> case findByTag roleName of+useExtraRoles callerName roleNames = forM roleNames $ \roleName -> case findRoleByTag roleName of Just role -> return role Nothing -> throwError [roleDoesNotExistMessage callerName roleName] -findByTag :: Text -> Maybe Role-findByTag tag' = restrictedRoles ^? traverse . filteredBy tag tag'+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
app/Werewolf/Options.hs view
@@ -19,14 +19,15 @@ werewolfPrefs, werewolfInfo, werewolf, ) where +import Control.Lens+ import Data.Text (Text) import qualified Data.Text as T import Data.Version (showVersion) -import Game.Werewolf (Variant (..))+import Game.Werewolf.Variant import Options.Applicative-import Options.Applicative.Types import qualified Werewolf.Command.Boot as Boot import qualified Werewolf.Command.Choose as Choose@@ -149,6 +150,7 @@ [ 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'@@ -181,20 +183,13 @@ start :: Parser Command start = fmap Start $ Start.Options <$> (extraRolesOption <|> randomExtraRolesOption)- <*> variantOption (mconcat [+ <*> fmap T.pack (strOption $ mconcat [ long "variant", short 'v', metavar "VARIANT",- value Standard, showDefaultWith $ const "standard",+ value standardVariantTag, showDefaultWith $ const standardVariantTag, help "Specify the game variant" ]) <*> some (T.pack <$> strArgument (metavar "PLAYER...")) where- variantOption = option $ readerAsk >>= \opt -> case opt of- "standard" -> return Standard- "no-role-knowledge" -> return NoRoleKnowledge- "no-role-reveal" -> return NoRoleReveal- "spiteful-villagers" -> return SpitefulVillagers- _ -> readerError $ "unrecognised variant `" ++ opt ++ "'"- extraRolesOption = fmap (Start.Use . filter (/= T.empty) . T.splitOn "," . T.pack) (strOption $ mconcat [ long "extra-roles", short 'e', metavar "ROLE,..." , value []@@ -205,6 +200,8 @@ [ long "random-extra-roles", short 'r' , help "Use random extra roles" ]++ standardVariantTag = T.unpack $ standardVariant ^. tag status :: Parser Command status = pure Status
src/Game/Werewolf.hs view
@@ -12,9 +12,10 @@ * "Game.Werewolf.Player" * "Game.Werewolf.Response" * "Game.Werewolf.Role"+* "Game.Werewolf.Variant" -N.B., where clashes are found between "Game.Werewolf.Player" and "Game.Werewolf.Role", the-"Game.Werewolf.Player" functions are preferred.+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 (@@ -25,3 +26,4 @@ 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)
src/Game/Werewolf/Game.hs view
@@ -22,9 +22,6 @@ healUsed, hunterRetaliated, jesterRevealed, marks, passed, poison, poisonUsed, priorProtect, protect, roleModel, scapegoatBlamed, see, votes, - Variant(..),- _Standard, _NoRoleKnowledge, _NoRoleReveal,- Stage(..), _DruidsTurn, _GameOver, _HuntersTurn1, _HuntersTurn2, _Lynching, _NecromancersTurn, _OraclesTurn, _OrphansTurn, _ProtectorsTurn, _ScapegoatsTurn, _SeersTurn, _Sunrise, _Sunset,@@ -60,7 +57,8 @@ import Data.Text (Text) import Game.Werewolf.Player-import Game.Werewolf.Role hiding (activity, name)+import Game.Werewolf.Role hiding (activity, name)+import Game.Werewolf.Variant hiding (name) import Prelude hiding (round) @@ -98,15 +96,6 @@ , _votes :: Map Text Text -- ^ Villagers and Werewolves } deriving (Eq, Read, Show) -data Variant = Standard | NoRoleKnowledge | NoRoleReveal | SpitefulVillagers- deriving (Eq, Read, Show)--instance Humanise Variant where- humanise Standard = "standard"- humanise NoRoleKnowledge = "no role knowledge"- humanise NoRoleReveal = "no role reveal"- humanise SpitefulVillagers = "spiteful villagers"- -- | 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).@@ -138,8 +127,6 @@ humanise WitchsTurn = "Witch's turn" makeLenses ''Game--makePrisms ''Variant makePrisms ''Stage
src/Game/Werewolf/Player.hs view
@@ -295,7 +295,7 @@ werewolf :: Traversal' Player () werewolf = role . allegiance . _Werewolves --- | This 'Traversal' provides the traversal of 'Player' names.+-- | The traversal of 'Player' names. -- -- @ -- 'names' = 'traverse' . 'name'@@ -303,7 +303,7 @@ names :: Traversable t => Traversal' (t Player) Text names = traverse . name --- | This 'Traversal' provides the traversal of 'Player' roles.+-- | The traversal of 'Player' roles. -- -- @ -- 'roles' = 'traverse' . 'role'@@ -311,7 +311,7 @@ roles :: Traversable t => Traversal' (t Player) Role roles = traverse . role --- | This 'Traversal' provides the traversal of 'Player' states.+-- | The traversal of 'Player' states. -- -- @ -- 'states' = 'traverse' . 'state'@@ -319,7 +319,7 @@ states :: Traversable t => Traversal' (t Player) State states = traverse . state --- | This 'Traversal' provides the traversal of 'Player's with the given name.+-- | The traversal of 'Player's with the given name. -- -- @ -- 'named' name' = 'filteredBy' . 'name' name'@@ -327,7 +327,7 @@ named :: Text -> Traversal' Player Player named name' = filteredBy name name' --- | This 'Traversal' provides the traversal of 'alphaWolf' 'Player's.+-- | The traversal of 'alphaWolf' 'Player's. -- -- @ -- 'alphaWolves' = 'traverse' . 'filtered' ('is' 'alphaWolf')@@ -335,7 +335,7 @@ alphaWolves :: Traversable t => Traversal' (t Player) Player alphaWolves = traverse . filtered (is alphaWolf) --- | This 'Traversal' provides the traversal of 'beholder' 'Player's.+-- | The traversal of 'beholder' 'Player's. -- -- @ -- 'beholders' = 'traverse' . 'filtered' ('is' 'beholder')@@ -343,7 +343,7 @@ beholders :: Traversable t => Traversal' (t Player) Player beholders = traverse . filtered (is beholder) --- | This 'Traversal' provides the traversal of 'crookedSenator' 'Player's.+-- | The traversal of 'crookedSenator' 'Player's. -- -- @ -- 'crookedSenators' = 'traverse' . 'filtered' ('is' 'crookedSenator')@@ -351,7 +351,7 @@ crookedSenators :: Traversable t => Traversal' (t Player) Player crookedSenators = traverse . filtered (is crookedSenator) --- | This 'Traversal' provides the traversal of 'dullahan' 'Player's.+-- | The traversal of 'dullahan' 'Player's. -- -- @ -- 'dullahans' = 'traverse' . 'filtered' ('is' 'dullahan')@@ -359,7 +359,7 @@ dullahans :: Traversable t => Traversal' (t Player) Player dullahans = traverse . filtered (is dullahan) --- | This 'Traversal' provides the traversal of 'druid' 'Player's.+-- | The traversal of 'druid' 'Player's. -- -- @ -- 'druids' = 'traverse' . 'filtered' ('is' 'druid')@@ -367,7 +367,7 @@ druids :: Traversable t => Traversal' (t Player) Player druids = traverse . filtered (is druid) --- | This 'Traversal' provides the traversal of 'fallenAngel' 'Player's.+-- | The traversal of 'fallenAngel' 'Player's. -- -- @ -- 'fallenAngels' = 'traverse' . 'filtered' ('is' 'fallenAngel')@@ -375,7 +375,7 @@ fallenAngels :: Traversable t => Traversal' (t Player) Player fallenAngels = traverse . filtered (is fallenAngel) --- | This 'Traversal' provides the traversal of 'hunter' 'Player's.+-- | The traversal of 'hunter' 'Player's. -- -- @ -- 'hunters' = 'traverse' . 'filtered' ('is' 'hunter')@@ -383,7 +383,7 @@ hunters :: Traversable t => Traversal' (t Player) Player hunters = traverse . filtered (is hunter) --- | This 'Traversal' provides the traversal of 'jester' 'Player's.+-- | The traversal of 'jester' 'Player's. -- -- @ -- 'jesters' = 'traverse' . 'filtered' ('is' 'jester')@@ -391,7 +391,7 @@ jesters :: Traversable t => Traversal' (t Player) Player jesters = traverse . filtered (is jester) --- | This 'Traversal' provides the traversal of 'lycan' 'Player's.+-- | The traversal of 'lycan' 'Player's. -- -- @ -- 'lycans' = 'traverse' . 'filtered' ('is' 'lycan')@@ -399,7 +399,7 @@ lycans :: Traversable t => Traversal' (t Player) Player lycans = traverse . filtered (is lycan) --- | This 'Traversal' provides the traversal of 'medusa' 'Player's.+-- | The traversal of 'medusa' 'Player's. -- -- @ -- 'medusas' = 'traverse' . 'filtered' ('is' 'medusa')@@ -407,7 +407,7 @@ medusas :: Traversable t => Traversal' (t Player) Player medusas = traverse . filtered (is medusa) --- | This 'Traversal' provides the traversal of 'necromancer' 'Player's.+-- | The traversal of 'necromancer' 'Player's. -- -- @ -- 'necromancers' = 'traverse' . 'filtered' ('is' 'necromancer')@@ -415,7 +415,7 @@ necromancers :: Traversable t => Traversal' (t Player) Player necromancers = traverse . filtered (is necromancer) --- | This 'Traversal' provides the traversal of 'oracle' 'Player's.+-- | The traversal of 'oracle' 'Player's. -- -- @ -- 'oracles' = 'traverse' . 'filtered' ('is' 'oracle')@@ -423,7 +423,7 @@ oracles :: Traversable t => Traversal' (t Player) Player oracles = traverse . filtered (is oracle) --- | This 'Traversal' provides the traversal of 'orphan' 'Player's.+-- | The traversal of 'orphan' 'Player's. -- -- @ -- 'orphans' = 'traverse' . 'filtered' ('is' 'orphan')@@ -431,7 +431,7 @@ orphans :: Traversable t => Traversal' (t Player) Player orphans = traverse . filtered (is orphan) --- | This 'Traversal' provides the traversal of 'protector' 'Player's.+-- | The traversal of 'protector' 'Player's. -- -- @ -- 'protectors' = 'traverse' . 'filtered' ('is' 'protector')@@ -439,7 +439,7 @@ protectors :: Traversable t => Traversal' (t Player) Player protectors = traverse . filtered (is protector) --- | This 'Traversal' provides the traversal of 'saint' 'Player's.+-- | The traversal of 'saint' 'Player's. -- -- @ -- 'saints' = 'traverse' . 'filtered' ('is' 'saint')@@ -447,7 +447,7 @@ saints :: Traversable t => Traversal' (t Player) Player saints = traverse . filtered (is saint) --- | This 'Traversal' provides the traversal of 'scapegoat' 'Player's.+-- | The traversal of 'scapegoat' 'Player's. -- -- @ -- 'scapegoats' = 'traverse' . 'filtered' ('is' 'scapegoat')@@ -455,7 +455,7 @@ scapegoats :: Traversable t => Traversal' (t Player) Player scapegoats = traverse . filtered (is scapegoat) --- | This 'Traversal' provides the traversal of 'seer' 'Player's.+-- | The traversal of 'seer' 'Player's. -- -- @ -- 'seers' = 'traverse' . 'filtered' ('is' 'seer')@@ -463,7 +463,7 @@ seers :: Traversable t => Traversal' (t Player) Player seers = traverse . filtered (is seer) --- | This 'Traversal' provides the traversal of 'simpleVillager' 'Player's.+-- | The traversal of 'simpleVillager' 'Player's. -- -- @ -- 'simpleVillagers' = 'traverse' . 'filtered' ('is' 'simpleVillager')@@ -471,7 +471,7 @@ simpleVillagers :: Traversable t => Traversal' (t Player) Player simpleVillagers = traverse . filtered (is simpleVillager) --- | This 'Traversal' provides the traversal of 'simpleWerewolf' 'Player's.+-- | The traversal of 'simpleWerewolf' 'Player's. -- -- @ -- 'simpleWerewolves' = 'traverse' . 'filtered' ('is' 'simpleWerewolf')@@ -479,7 +479,7 @@ simpleWerewolves :: Traversable t => Traversal' (t Player) Player simpleWerewolves = traverse . filtered (is simpleWerewolf) --- | This 'Traversal' provides the traversal of 'spitefulVillager' 'Player's.+-- | The traversal of 'spitefulVillager' 'Player's. -- -- @ -- 'spitefulVillagers' = 'traverse' . 'filtered' ('is' 'spitefulVillager')@@ -487,7 +487,7 @@ spitefulVillagers :: Traversable t => Traversal' (t Player) Player spitefulVillagers = traverse . filtered (is spitefulVillager) --- | This 'Traversal' provides the traversal of 'trueVillager' 'Player's.+-- | The traversal of 'trueVillager' 'Player's. -- -- @ -- 'trueVillagers' = 'traverse' . 'filtered' ('is' 'trueVillager')@@ -495,7 +495,7 @@ trueVillagers :: Traversable t => Traversal' (t Player) Player trueVillagers = traverse . filtered (is trueVillager) --- | This 'Traversal' provides the traversal of 'villageDrunk' 'Player's.+-- | The traversal of 'villageDrunk' 'Player's. -- -- @ -- 'villageDrunks' = 'traverse' . 'filtered' ('is' 'villageDrunk')@@ -503,7 +503,7 @@ villageDrunks :: Traversable t => Traversal' (t Player) Player villageDrunks = traverse . filtered (is villageDrunk) --- | This 'Traversal' provides the traversal of 'witch' 'Player's.+-- | The traversal of 'witch' 'Player's. -- -- @ -- 'witches' = 'traverse' . 'filtered' ('is' 'witch')@@ -511,7 +511,7 @@ witches :: Traversable t => Traversal' (t Player) Player witches = traverse . filtered (is witch) --- | This 'Traversal' provides the traversal of 'zombie' 'Player's.+-- | The traversal of 'zombie' 'Player's. -- -- @ -- 'zombies' = 'traverse' . 'filtered' ('is' 'zombie')@@ -519,7 +519,7 @@ zombies :: Traversable t => Traversal' (t Player) Player zombies = traverse . filtered (is zombie) --- | This 'Traversal' provides the traversal of 'loner' 'Player's.+-- | The traversal of 'loner' 'Player's. -- -- @ -- 'loners' = 'traverse' . 'filtered' . ('is' 'loner')@@ -527,7 +527,7 @@ loners :: Traversable t => Traversal' (t Player) Player loners = traverse . filtered (is loner) --- | This 'Traversal' provides the traversal of 'villager' 'Player's.+-- | The traversal of 'villager' 'Player's. -- -- @ -- 'villagers' = 'traverse' . 'filtered' ('is' 'villager')@@ -535,7 +535,7 @@ villagers :: Traversable t => Traversal' (t Player) Player villagers = traverse . filtered (is villager) --- | This 'Traversal' provides the traversal of 'werewolf' 'Player's.+-- | The traversal of 'werewolf' 'Player's. -- -- @ -- 'werewolves' = 'traverse' . 'filtered' ('is' 'werewolf')@@ -543,7 +543,7 @@ werewolves :: Traversable t => Traversal' (t Player) Player werewolves = traverse . filtered (is werewolf) --- | This 'Traversal' provides the traversal of 'Alive' 'Player's.+-- | The traversal of 'Alive' 'Player's. -- -- @ -- 'alive' = 'filtered' ('has' $ 'state' . '_Alive')@@ -551,7 +551,7 @@ alive :: Traversal' Player Player alive = filtered (has $ state . _Alive) --- | This 'Traversal' provides the traversal of 'Dead' 'Player's.+-- | The traversal of 'Dead' 'Player's. -- -- @ -- 'dead' = 'filtered' ('has' $ 'state' . '_Dead')
+ src/Game/Werewolf/Variant.hs view
@@ -0,0 +1,121 @@+{-|+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, noRoleRevealVariant, spitefulVillageVariant,++ -- ** Traversals+ standard, noRoleKnowledge, 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+ , 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|]+ }++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 '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
+ variant/no-role-knowledge/description.txt view
@@ -0,0 +1,1 @@+In "No role knowledge", players are not informed of the roles in play at the start of a game. Further, nocturnal player turns are obscured.
+ variant/no-role-knowledge/name.txt view
@@ -0,0 +1,1 @@+No role knowledge
+ variant/no-role-reveal/description.txt view
@@ -0,0 +1,1 @@+In "No role reveal", players are not informed of each others roles upon death. Further, nocturnal player turns are obscured.
+ variant/no-role-reveal/name.txt view
@@ -0,0 +1,1 @@+No role reveal
+ variant/spiteful-village/description.txt view
@@ -0,0 +1,1 @@+In "Spiteful village", the default Villager role becomes the #{humanise spitefulVillagerRole}.
+ variant/spiteful-village/name.txt view
@@ -0,0 +1,1 @@+Spiteful village
variant/standard/command/help/help-commands.txt view
@@ -2,3 +2,4 @@ - `help commands [-a | --all]` - `help roles [-a | --all]` - `help rules [-a | --all]`+- `help variants [-a | --all]`
+ variant/standard/command/help/variant.txt view
@@ -0,0 +1,2 @@+#{humanise variant} (#{variant ^. Variant.tag}):+#{variant ^. Variant.description}
+ variant/standard/description.txt view
@@ -0,0 +1,1 @@+The Standard variant; everything plays out as normal.
variant/standard/engine/lynching/jester-lynched.txt view
@@ -1,1 +1,1 @@-Just as the townsfolk tie #{humanise jester} up to the pyre, a voice in the crowd yells out. "We can't burn #{humanise jester}! He's the joke of the town!" #{humanise jester} the #{humanise jesterRole} is quickly untied and apologised to.+Just 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.
+ variant/standard/error/command/start/variant-does-not-exist.txt view
@@ -0,0 +1,1 @@+Variant #{variantName} does not exist.
+ variant/standard/name.txt view
@@ -0,0 +1,1 @@+Standard
variant/standard/role/medusa/description.txt view
@@ -1,1 +1,1 @@-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.+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 their true form would see live snakes for hair and the few that further look into their eyes are turned to stone.
variant/standard/role/necromancer/description.txt view
@@ -1,1 +1,1 @@-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.+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 #{humanise zombieRole}s.
variant/standard/role/necromancer/rules.txt view
@@ -1,3 +1,2 @@-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.+Once 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.
variant/standard/role/zombie/description.txt view
@@ -1,1 +1,1 @@-A loyal follower of the Necromancer. A Zombie has no mind of its own and blindly obeys every command of their master.+A loyal follower of the #{humanise necromancerRole}. A Zombie has no mind of its own and blindly obeys every command of their master.
variant/standard/role/zombie/rules.txt view
@@ -1,1 +1,1 @@-A Zombie wins with the Necromancer. They cannot be killed, however they die when the Necromancer dies.+A Zombie wins with the #{humanise necromancerRole}. They cannot be killed, however they die when the #{humanise necromancerRole} dies.
werewolf.cabal view
@@ -1,5 +1,5 @@ name: werewolf-version: 1.4.0.1+version: 1.5.0.0 author: Henry J. Wylde maintainer: public@hjwylde.com@@ -21,10 +21,14 @@ 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-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@@ -39,6 +43,10 @@ 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@@ -68,6 +76,7 @@ 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@@ -176,6 +185,7 @@ 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@@ -346,6 +356,7 @@ Game.Werewolf.Player Game.Werewolf.Response Game.Werewolf.Role+ Game.Werewolf.Variant default-language: Haskell2010 other-extensions: