packages feed

werewolf-1.5.1.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 (
    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