packages feed

werewolf-1.2.0.0: app/Game/Werewolf/Message/Engine.hs

{-|
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 (
    playerBootedMessage, villageDrunkJoinedPackMessages, villageDrunkJoinedVillageMessage,
    playerTurnedToStoneMessage, playerSeenMessage, playerDivinedMessage, playerDevouredMessage,
    noPlayerDevouredMessage, scapegoatChoseAllowedVotersMessage, playerPoisonedMessage,
    scapegoatLynchedMessage, playerLynchedMessage, noPlayerLynchedMessage, jesterLynchedMessage,
    ferinaGruntsMessage, orphanJoinedPackMessages, playerKilledMessage, playerLostMessage,
    playerContributedMessage, playerWonMessage, witchsTurnMessages, firstWerewolvesTurnMessages,
    villagesTurnMessage, nightFallsMessage, sunriseMessage, villageDrunksTurnMessages,
    seersTurnMessages, scapegoatsTurnMessage, protectorsTurnMessages, orphansTurnMessages,
    oraclesTurnMessages, huntersTurnMessages, stageMessages, fallenAngelMessage,
    trueVillagerMessage, spitefulGhostMessage, beholderMessage, newPlayerMessage,
    rolesInGameMessage, newPlayersInGameMessage, newGameMessages, gameOverMessages,
    fallenAngelWonMessage,
) where

import Control.Lens
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.NoRoleKnowledge.Engine as NoRoleKnowledge
import Game.Werewolf.Variant.Standard.Engine        as Standard

playerBootedMessage :: Player -> Message
playerBootedMessage = publicMessage . playerBootedText

gameOverMessages :: Game -> [Message]
gameOverMessages game
    | hasFallenAngelWon game    = concat
        [ [fallenAngelWonMessage]
        , [playerRolesMessage game]
        , [playerWonMessage fallenAngelsName]
        , map playerLostMessage (game ^.. players . names \\ [fallenAngelsName])
        ]
    | 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)

        fallenAngelsName = game ^?! players . fallenAngels . name

fallenAngelWonMessage :: Message
fallenAngelWonMessage = publicMessage fallenAngelWonText

allegianceWonMessage :: Allegiance -> Message
allegianceWonMessage = publicMessage . allegianceWonText

playerRolesMessage :: Game -> Message
playerRolesMessage = publicMessage . playerRolesText

newGameMessages :: Game -> [Message]
newGameMessages game = concat
    [ [newPlayersInGameMessage game]
    , [rolesInGameMessage Nothing game]
    , map newPlayerMessage players'
    , beholderMessages
    , spitefulGhostMessages
    , trueVillagerMessages
    , fallenAngelMessages
    , stageMessages game
    ]
    where
        players'                = game ^. players
        beholderMessages        = case (,) <$> players' ^? beholders <*> players' ^? seers of
            Just (beholder, _)  -> [beholderMessage (beholder ^. name) game]
            _                   -> []
        spitefulGhostMessages   = case players' ^? spitefulGhosts of
            Just spitefulGhost  -> [spitefulGhostMessage (spitefulGhost ^. name) game]
            _                   -> []
        trueVillagerMessages    = [trueVillagerMessage game | has trueVillagers players']
        fallenAngelMessages     = if has fallenAngels players'
            then [fallenAngelMessage]
            else []

newPlayersInGameMessage :: Game -> Message
newPlayersInGameMessage = publicMessage . playersInGameText

rolesInGameMessage :: Maybe Text -> Game -> Message
rolesInGameMessage mTo game
    | has (variant . _NoRoleKnowledge) game = Message mTo $ NoRoleKnowledge.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

spitefulGhostMessage :: Text -> Game -> Message
spitefulGhostMessage to = privateMessage to . spitefulGhostText

trueVillagerMessage :: Game -> Message
trueVillagerMessage = publicMessage . trueVillagerText

fallenAngelMessage :: Message
fallenAngelMessage = publicMessage fallenAngelText

stageMessages :: Game -> [Message]
stageMessages game = case game ^. stage of
    DruidsTurn          -> []
    GameOver            -> []
    HuntersTurn1        -> huntersTurnMessages game
    HuntersTurn2        -> huntersTurnMessages game
    Lynching            -> []
    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
        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

oraclesTurnMessages :: Text -> Game -> [Message]
oraclesTurnMessages to game
    | has (variant . _NoRoleKnowledge) game =
        [ privateMessage to oraclesTurnPrivateText ]
    | otherwise                             =
        [ publicMessage oraclesTurnPublicText
        , privateMessage to oraclesTurnPrivateText
        ]

orphansTurnMessages :: Text -> Game -> [Message]
orphansTurnMessages to game
    | has (variant . _NoRoleKnowledge) game =
        [ privateMessage to orphansTurnPrivateText ]
    | otherwise                             =
        [ publicMessage orphansTurnPublicText
        , privateMessage to orphansTurnPrivateText
        ]

protectorsTurnMessages :: Text -> Game -> [Message]
protectorsTurnMessages to game
    | has (variant . _NoRoleKnowledge) 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 ]
    | otherwise                             =
        [ publicMessage seersTurnPublicText
        , privateMessage to seersTurnPrivateText
        ]

villageDrunksTurnMessages :: Game -> [Message]
villageDrunksTurnMessages game
    | has (variant . _NoRoleKnowledge) 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
    | 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 = []
            | 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

playerLynchedMessage :: Player -> Message
playerLynchedMessage player
    | is simpleWerewolf player
        || is alphaWolf player  = publicMessage $ werewolfLynchedText player
    | otherwise                 = publicMessage $ playerLynchedText player

scapegoatLynchedMessage :: Game -> Message
scapegoatLynchedMessage = publicMessage . scapegoatLynchedText

scapegoatChoseAllowedVotersMessage :: Game -> Message
scapegoatChoseAllowedVotersMessage = publicMessage . scapegoatsTurnEndedText

noPlayerDevouredMessage :: Message
noPlayerDevouredMessage = publicMessage noPlayerDevouredText

playerDevouredMessage :: Player -> Message
playerDevouredMessage = publicMessage . playerDevouredText

playerDivinedMessage :: Text -> Player -> Message
playerDivinedMessage to = privateMessage to . playerDivinedText

playerPoisonedMessage :: Player -> Message
playerPoisonedMessage = publicMessage . playerPoisonedText

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 -> Message
playerTurnedToStoneMessage = publicMessage . playerTurnedToStoneText

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]