packages feed

werewolf-1.0.0.0: app/Werewolf/Command/Help.hs

{-|
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.
-}

{-# LANGUAGE OverloadedStrings #-}

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.Text     (Text)
import qualified Data.Text     as T

import           Game.Werewolf      hiding (Command)
import qualified Game.Werewolf.Role as Role

import Werewolf.Game

data Options = Options
    { argCommand :: Maybe Command
    } deriving (Eq, Show)

data Command = Commands
    { optAll :: Bool
    } | Rules
    { optAll :: Bool
    } | Roles
    { optAll :: Bool
    } deriving (Eq, Show)

handle :: MonadIO m => Text -> Text -> Options -> m ()
handle callerName tag (Options (Just (Commands optAll))) = do
    mGame <- ifM (doesGameExist tag &&^ return (not optAll)) (Just <$> readGame tag) (return Nothing)

    exitWith success
        { messages = map (privateMessage callerName) (commandsMessages callerName mGame)
        }
handle callerName tag (Options (Just (Roles optAll))) = do
    roles <- (sortBy (compare `on` view Role.name) . nub) <$> ifM (doesGameExist tag &&^ return (not optAll))
        (toListOf (players . roles) <$> readGame tag)
        (return allRoles)

    exitWith success
        { messages = map (privateMessage callerName . roleMessage) roles
        }
handle callerName tag (Options (Just (Rules optAll))) = do
    mGame <- ifM (doesGameExist tag &&^ return (not optAll)) (Just <$> readGame tag) (return Nothing)

    exitWith success
        { messages = map (privateMessage callerName) (rulesMessages mGame)
        }
handle callerName _ (Options Nothing) = exitWith success
    { messages = map (privateMessage callerName) helpMessages
    }

commandsMessages :: Text -> Maybe Game -> [Text]
commandsMessages callerName mGame = map (T.intercalate "\n") $ filter (/= [])
    [ [ "Global commands:"
      , "- `start ([-e|--extra-roles ROLE,...] | [-r|--random-extra-roles]) [--include-seer] PLAYER...`"
      , "- `end`"
      , "- `boot PLAYER`"
      , "- `quit`"
      , "- `version`"
      ]
    , [ "Status 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"
      ]
    , [ "Standard commands:"
      , "- `vote PLAYER`"
      ]
    , whenPlayerHasRole callerName mGame hunterRole
      [ "Hunter commands:"
      , "- `choose PLAYER`"
      ]
    , whenPlayerHasRole callerName mGame orphanRole
      [ "Orphan commands:"
      , "- `choose PLAYER`"
      ]
    , whenPlayerHasRole callerName mGame protectorRole
      [ "Protector commands:"
      , "- `protect PLAYER`"
      ]
    , whenPlayerHasRole callerName mGame scapegoatRole
      [ "Scapegoat commands:"
      , "- `choose PLAYER...`"
      ]
    , whenPlayerHasRole callerName mGame seerRole
      [ "Seer commands:"
      , "- `see PLAYER`"
      ]
    , whenPlayerHasRole callerName mGame witchRole
      [ "Witch commands:"
      , "- `heal`"
      , "- `poison PLAYER`"
      , "- `pass`"
      ]
    ]

roleMessage :: Role -> Text
roleMessage role = T.intercalate "\n"
    [ T.concat [role ^. Role.name, " (", T.pack . show $ role ^. balance, "):"]
    , role ^. description
    , role ^. rules
    ]

rulesMessages :: Maybe Game -> [Text]
rulesMessages mGame = map (T.intercalate "\n")
    [ [ T.unwords
        [ "Each player is informed of their role (see `help roles` for a list) at the start of the"
        , "game."
        ]
      , T.unwords
        [ "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."
        ]
      , T.unwords
        [ "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."
        ]
      ]
    , filter (/= "")
      [ T.concat
        [ "A game begins at night and follows a standard cycle."
        , whenRoleInPlay mGame fallenAngelRole
            " (N.B., when the Fallen Angel is in play the game begins with the village vote.)"
        ]
      , whenRoleInPlay mGame fallenAngelRole
        "- (When the Fallen Angel is in play) the village votes to lynch a suspect."
      , "- The village falls asleep."
      , whenRoleInPlay mGame orphanRole
        "- (First round only) the Orphan wakes up and chooses a role model."
      , whenRoleInPlay mGame villageDrunkRole
        "- (Third round only) the Village Drunk sobers up and remembers their allegiance."
      , whenRoleInPlay mGame seerRole
        "- The Seer wakes up and sees someone's allegiance."
      , whenRoleInPlay mGame protectorRole
        "- The Protector wakes up and protects someone."
      , "- The Werewolves wake up and vote to devour a victim."
      , whenRoleInPlay mGame witchRole
        "- The Witch wakes up and may heal the victim and/or poison someone."
      , "- The village wakes up and find the victim."
      , whenRoleInPlay mGame hunterRole
        "- (When the Hunter is killed) the Hunter chooses someone to shoot."
      , whenRoleInPlay mGame druidRole
        "- Ferina grunts if the Druid is next to a Werewolf."
      , "- The village votes to lynch a suspect."
      , whenRoleInPlay mGame hunterRole
        "- (When the Hunter is killed) the Hunter chooses someone to shoot."
      , whenRoleInPlay mGame scapegoatRole
        "- (When the Scapegoat is blamed) the Scapegoat chooses whom may vote on the next day."
      , T.concat
        [ "The game is over when only Villagers or Werewolves are left alive"
        , ifRoleInPlay mGame fallenAngelRole
          ", or when one of the Loners completes their own objective."
          "."
        ]
      ]
    ]

helpMessages :: [Text]
helpMessages = map (T.intercalate "\n")
    [ [ T.unwords
        [ "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."
        ]
      , T.unwords
        [ "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..."
        ]
      ]
    , [ "Help commands:"
      , "- `help commands [-a | --all]`"
      , "- `help roles [-a | --all]`"
      , "- `help rules [-a | --all]`"
      ]
    ]

whenPlayerHasRole :: Monoid m => Text -> Maybe Game -> Role -> m -> m
whenPlayerHasRole _ Nothing _ m                         = m
whenPlayerHasRole callerName (Just game) role' m
    | hasn't (players . names . only callerName) game   = mempty
    | hasn't (role . only role') player                 = mempty
    | otherwise                                         = m
     where
        player = game ^?! players . traverse . filteredBy name callerName

ifRoleInPlay :: Maybe Game -> Role -> a -> a -> a
ifRoleInPlay Nothing _ true _               = true
ifRoleInPlay (Just game) role' true false
    | has (players . roles . only role') game   = true
    | otherwise                                 = false

whenRoleInPlay :: Monoid m => Maybe Game -> Role -> m -> m
whenRoleInPlay mGame role m = ifRoleInPlay mGame role m mempty