werewolf-0.3.4.0: src/Game/Werewolf/Command.hs
{-|
Module : Game.Werewolf.Command
Description : Command data structures.
Copyright : (c) Henry J. Wylde, 2015
License : BSD3
Maintainer : public@hjwylde.com
Command data structures.
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
module Game.Werewolf.Command (
-- * Command
Command(..),
-- ** Instances
devourVoteCommand, lynchVoteCommand, noopCommand, pingCommand, quitCommand, seeCommand,
statusCommand,
) where
import Control.Lens hiding (only)
import Control.Monad.Except
import Control.Monad.Extra
import Control.Monad.State hiding (state)
import Control.Monad.Writer
import Data.List
import qualified Data.Map as Map
import Data.Text (Text)
import Game.Werewolf.Engine
import Game.Werewolf.Game hiding (isGameOver, isSeersTurn, isVillagersTurn, isWerewolvesTurn,
killPlayer)
import Game.Werewolf.Player hiding (doesPlayerExist)
import Game.Werewolf.Response
import Game.Werewolf.Role hiding (Villagers, Werewolves, findByName_, name, _name)
data Command = Command { apply :: forall m . (MonadError [Message] m, MonadState Game m, MonadWriter [Message] m) => m () }
devourVoteCommand :: Text -> Text -> Command
devourVoteCommand callerName targetName = Command $ do
validatePlayer callerName callerName
unlessM (isPlayerWerewolf callerName) $ throwError [playerCannotDoThatMessage callerName]
unlessM isWerewolvesTurn $ throwError [playerCannotDoThatRightNowMessage callerName]
whenJustM (getPlayerVote callerName) . const $ throwError [playerHasAlreadyVotedMessage callerName]
validatePlayer callerName targetName
whenM (isPlayerWerewolf targetName) $ throwError [playerCannotDevourAnotherWerewolf callerName]
votes %= Map.insert callerName targetName
aliveWerewolfNames <- uses players $ map _name . filterAlive . filterWerewolves
tell [playerMadeDevourVoteMessage aliveWerewolfNames callerName targetName]
lynchVoteCommand :: Text -> Text -> Command
lynchVoteCommand callerName targetName = Command $ do
validatePlayer callerName callerName
unlessM isVillagersTurn $ throwError [playerCannotDoThatRightNowMessage callerName]
whenJustM (getPlayerVote callerName) . const $ throwError [playerHasAlreadyVotedMessage callerName]
validatePlayer callerName targetName
votes %= Map.insert callerName targetName
noopCommand :: Command
noopCommand = Command $ return ()
pingCommand :: Command
pingCommand = Command $ use turn >>= \turn' -> case turn' of
Seers -> tell [pingSeersMessage]
Villagers -> do
game <- get
tell [waitingOnMessage Nothing $ filter (flip Map.notMember (game ^. votes) . _name) (filterAlive $ game ^. players)]
Werewolves -> tell [pingWerewolvesMessage]
NoOne -> return ()
NightFalling -> return ()
DayBreaking -> return ()
quitCommand :: Text -> Command
quitCommand callerName = Command $ do
validatePlayer callerName callerName
caller <- uses players $ findByName_ callerName
killPlayer caller
tell [playerQuitMessage caller]
sees %= Map.delete callerName
votes %= Map.delete callerName
seeCommand :: Text -> Text -> Command
seeCommand callerName targetName = Command $ do
validatePlayer callerName callerName
unlessM (isPlayerSeer callerName) $ throwError [playerCannotDoThatMessage callerName]
unlessM isSeersTurn $ throwError [playerCannotDoThatRightNowMessage callerName]
whenJustM (getPlayerSee callerName) . const $ throwError [playerHasAlreadySeenMessage callerName]
validatePlayer callerName targetName
sees %= Map.insert callerName targetName
statusCommand :: Text -> Command
statusCommand callerName = Command $ use turn >>= \turn' -> case turn' of
Seers -> do
game <- get
tell $ standardStatusMessages turn' (game ^. players)
Villagers -> do
game <- get
tell $ standardStatusMessages turn' (game ^. players)
tell [waitingOnMessage (Just [callerName]) $ filter (flip Map.notMember (game ^. votes) . _name) (filterAlive $ game ^. players)]
Werewolves -> do
unlessM (doesPlayerExist callerName) $ throwError [playerDoesNotExistMessage callerName callerName]
game <- get
tell $ standardStatusMessages turn' (game ^. players)
whenM (isPlayerWerewolf callerName) $ tell [waitingOnMessage (Just [callerName]) $ filter (flip Map.notMember (game ^. votes) . _name) (filterAlive . filterWerewolves $ game ^. players)]
NoOne -> do
aliveAllegiances <- uses players $ nub . map (_allegiance . _role) . filterAlive
when (length aliveAllegiances <= 1) $ turn .= NoOne >> get >>= tell . gameOverMessages
NightFalling -> return ()
DayBreaking -> return ()
where
standardStatusMessages turn players = [
currentTurnMessage callerName turn,
rolesInGameMessage (Just [callerName]) $ map _role players,
playersInGameMessage callerName players
]
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]
whenM (isPlayerDead name) $ throwError [if callerName == name then playerIsDeadMessage callerName else targetIsDeadMessage callerName name]