diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,26 @@
 
 ### Upcoming
 
+### v1.3.0.0
+
+*Major*
+
+* Added activity field to Role. ([#231](https://github.com/hjwylde/werewolf/issues/231))
+* Added the Dullahan role. ([#202](https://github.com/hjwylde/werewolf/issues/202))
+
+*Minor*
+
+* Added player roles message when the game is force ended. ([#225](https://github.com/hjwylde/werewolf/issues/225))
+* Added the no-role-reveal variant. ([#181](https://github.com/hjwylde/werewolf/issues/181))
+* Added a public start-of-game message for the Spiteful Ghost. ([#220](https://github.com/hjwylde/werewolf/issues/220))
+* Removed the public start-of-game message for the Fallen Angel. ([#229](https://github.com/hjwylde/werewolf/issues/229))
+* Updated role balances based on Simple Werewolf being -5. ([#233](https://github.com/hjwylde/werewolf/issues/233))
+
+*Revisions*
+
+* Fixed current stage command to declare diurnal turns. ([#231](https://github.com/hjwylde/werewolf/issues/231))
+* Fixed ping command to declare diurnal roles. ([#231](https://github.com/hjwylde/werewolf/issues/231))
+
 ### v1.2.0.3
 
 *Revisions*
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@
 
 The Loners must complete their own objective.
 
+* Dullahan
 * Fallen Angel
 * Spiteful Ghost
 
diff --git a/app/Game/Werewolf/Command/Global.hs b/app/Game/Werewolf/Command/Global.hs
--- a/app/Game/Werewolf/Command/Global.hs
+++ b/app/Game/Werewolf/Command/Global.hs
@@ -17,6 +17,7 @@
 import Control.Lens
 import Control.Monad.Except
 import Control.Monad.Extra
+import Control.Monad.State
 import Control.Monad.Writer
 
 import qualified Data.Map   as Map
@@ -50,6 +51,6 @@
 
     caller <- findPlayerBy_ name callerName
 
-    tell [playerQuitMessage caller]
+    tell . (:[]) . playerQuitMessage caller =<< get
 
     removePlayer callerName
diff --git a/app/Game/Werewolf/Command/Hunter.hs b/app/Game/Werewolf/Command/Hunter.hs
--- a/app/Game/Werewolf/Command/Hunter.hs
+++ b/app/Game/Werewolf/Command/Hunter.hs
@@ -17,6 +17,7 @@
 import Control.Lens
 import Control.Monad.Except
 import Control.Monad.Extra
+import Control.Monad.State
 import Control.Monad.Writer
 
 import Data.Text (Text)
@@ -37,7 +38,7 @@
 
     target <- findPlayerBy_ name targetName
 
-    tell [playerShotMessage target]
+    tell . (:[]) . playerShotMessage target =<< get
     killPlayer targetName
 
     hunterRetaliated .= True
diff --git a/app/Game/Werewolf/Command/Protector.hs b/app/Game/Werewolf/Command/Protector.hs
--- a/app/Game/Werewolf/Command/Protector.hs
+++ b/app/Game/Werewolf/Command/Protector.hs
@@ -14,7 +14,6 @@
     protectCommand,
 ) where
 
-import Control.Lens
 import Control.Lens.Extra
 import Control.Monad.Except
 import Control.Monad.Extra
diff --git a/app/Game/Werewolf/Command/Status.hs b/app/Game/Werewolf/Command/Status.hs
--- a/app/Game/Werewolf/Command/Status.hs
+++ b/app/Game/Werewolf/Command/Status.hs
@@ -25,11 +25,12 @@
 import Data.Text (Text)
 
 -- TODO (hjw): remove Message.Engine
-import Game.Werewolf                 hiding (getPendingVoters)
-import Game.Werewolf.Command
-import Game.Werewolf.Message.Command
-import Game.Werewolf.Message.Engine
-import Game.Werewolf.Util
+import           Game.Werewolf                 hiding (getPendingVoters)
+import           Game.Werewolf.Command
+import           Game.Werewolf.Message.Command
+import           Game.Werewolf.Message.Engine
+import qualified Game.Werewolf.Role            as Role
+import           Game.Werewolf.Util
 
 circleCommand :: Text -> Bool -> Command
 circleCommand callerName includeDead = Command $ do
@@ -60,8 +61,12 @@
 pingRole role' = do
     player <- findPlayerBy_ role role'
 
-    tell . (:[]) . pingRoleMessage role' =<< get
+    tell . (:[]) . pingRoleMessage =<< get
     tell [pingPlayerMessage $ player ^. name]
+    where
+        pingRoleMessage game
+            | has (Role.activity . _Diurnal) role'   = pingDiurnalRoleMessage role'
+            | otherwise                         = pingNocturnalRoleMessage role' game
 
 pingVillagers :: (MonadState Game m, MonadWriter [Message] m) => m ()
 pingVillagers = do
@@ -82,7 +87,12 @@
 statusCommand callerName = Command $ do
     game <- get
 
-    tell [ currentStageMessage callerName game
+    tell [ currentStageMessage game
         , rolesInGameMessage (Just callerName) game
         , playersInGameMessage callerName game
         ]
+    where
+        currentStageMessage game
+            | has (stage . _GameOver) game              = gameIsOverMessage callerName
+            | has (stage . activity . _Diurnal) game    = currentDiurnalTurnMessage callerName game
+            | otherwise                                 = currentNocturnalTurnMessage callerName game
diff --git a/app/Game/Werewolf/Command/Witch.hs b/app/Game/Werewolf/Command/Witch.hs
--- a/app/Game/Werewolf/Command/Witch.hs
+++ b/app/Game/Werewolf/Command/Witch.hs
@@ -17,7 +17,6 @@
     healCommand, passCommand, poisonCommand,
 ) where
 
-import Control.Lens
 import Control.Lens.Extra
 import Control.Monad.Except
 import Control.Monad.Extra
diff --git a/app/Game/Werewolf/Engine.hs b/app/Game/Werewolf/Engine.hs
--- a/app/Game/Werewolf/Engine.hs
+++ b/app/Game/Werewolf/Engine.hs
@@ -17,7 +17,6 @@
     checkStage, checkGameOver,
 ) where
 
-import Control.Lens         hiding (cons, isn't)
 import Control.Lens.Extra
 import Control.Monad.Except
 import Control.Monad.Extra
@@ -30,8 +29,7 @@
 import           Data.Maybe
 
 -- TODO (hjw): remove Message.Command
-import Game.Werewolf.Game            hiding (getAllowedVoters, getPendingVoters, hasAnyoneWon,
-                                      hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon)
+import Game.Werewolf.Game            hiding (getAllowedVoters, getPendingVoters, hasAnyoneWon)
 import Game.Werewolf.Message.Command
 import Game.Werewolf.Message.Engine
 import Game.Werewolf.Player
@@ -57,7 +55,7 @@
     bootees     <- mapM (findPlayerBy_ name) booteeNames
 
     forM_ (filter (is alive) bootees) $ \bootee -> do
-        tell [playerBootedMessage bootee]
+        tell . (:[]) . playerBootedMessage bootee =<< get
 
         removePlayer (bootee ^. name)
 
@@ -124,7 +122,7 @@
             target <- findPlayerBy_ name targetName
 
             killPlayer targetName
-            tell [playerPoisonedMessage target]
+            tell . (:[]) . playerPoisonedMessage target =<< get
 
         whenJustM (preuse $ players . seers . alive) $ \seer -> do
             target <- use see >>= findPlayerBy_ name . fromJust
@@ -197,10 +195,13 @@
     | is fallenAngel votee  = do
         fallenAngelLynched .= True
 
-        tell [playerLynchedMessage votee]
+        tell . (:[]) . playerLynchedMessage votee =<< get
+    | is werewolf votee     = do
+        killPlayer (votee ^. name)
+        tell . (:[]) . werewolfLynchedMessage votee =<< get
     | otherwise             = do
         killPlayer (votee ^. name)
-        tell [playerLynchedMessage votee]
+        tell . (:[]) . playerLynchedMessage votee =<< get
 lynchVotee _            = preuse (players . scapegoats . alive) >>= \mScapegoat -> case mScapegoat of
     Just scapegoat  -> do
         scapegoatBlamed .= True
@@ -213,11 +214,11 @@
 devourVotee Nothing         = tell [noPlayerDevouredMessage]
 devourVotee (Just votee)    = do
     killPlayer (votee ^. name)
-    tell [playerDevouredMessage votee]
+    tell . (:[]) . playerDevouredMessage votee =<< get
 
     when (is medusa votee) . whenJustM (getFirstAdjacentAliveWerewolf $ votee ^. name) $ \werewolf -> do
         killPlayer (werewolf ^. name)
-        tell [playerTurnedToStoneMessage werewolf]
+        tell . (:[]) . playerTurnedToStoneMessage werewolf =<< get
 
 advanceStage :: (MonadState Game m, MonadWriter [Message] m) => m ()
 advanceStage = do
diff --git a/app/Game/Werewolf/Message.hs b/app/Game/Werewolf/Message.hs
--- a/app/Game/Werewolf/Message.hs
+++ b/app/Game/Werewolf/Message.hs
@@ -23,7 +23,6 @@
     humanisePlayerWithState, article, conjugateToBe, pluralise,
 ) where
 
-import Control.Lens       hiding (isn't)
 import Control.Lens.Extra
 
 import           Data.String.Humanise
diff --git a/app/Game/Werewolf/Message/Command.hs b/app/Game/Werewolf/Message/Command.hs
--- a/app/Game/Werewolf/Message/Command.hs
+++ b/app/Game/Werewolf/Message/Command.hs
@@ -33,10 +33,11 @@
     standardCommandsMessage, statusCommandsMessage, witchCommandsMessage,
 
     -- * Ping
-    pingPlayerMessage, pingRoleMessage, pingVillageMessage, pingWerewolvesMessage,
+    pingDiurnalRoleMessage, pingNocturnalRoleMessage, pingPlayerMessage, pingVillageMessage,
+    pingWerewolvesMessage,
 
     -- * Status
-    currentStageMessage, gameIsOverMessage, playersInGameMessage,
+    currentDiurnalTurnMessage, currentNocturnalTurnMessage, gameIsOverMessage, playersInGameMessage,
 
     -- * Unvote
     playerRescindedVoteMessage,
@@ -59,10 +60,13 @@
 import Game.Werewolf.Response
 import Game.Werewolf.Role
 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 -> Message
-playerQuitMessage = publicMessage . callerQuitText
+playerQuitMessage :: Player -> Game -> Message
+playerQuitMessage player game
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.callerQuitText player
+    | otherwise                             = publicMessage $ Standard.callerQuitText player
 
 playerVotedToBootMessage :: Player -> Player -> Message
 playerVotedToBootMessage caller = publicMessage . callerVotedBootText caller
@@ -70,8 +74,10 @@
 circleMessage :: Text -> [Player] -> Message
 circleMessage to = privateMessage to . gameCircleText
 
-playerShotMessage :: Player -> Message
-playerShotMessage = publicMessage . playerShotText
+playerShotMessage :: Player -> Game -> Message
+playerShotMessage player game
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.playerShotText player
+    | otherwise                             = publicMessage $ Standard.playerShotText player
 
 gameEndedMessage :: Text -> Message
 gameEndedMessage = publicMessage . gameEndedText
@@ -148,28 +154,36 @@
 witchCommandsMessage :: Text -> Message
 witchCommandsMessage to = privateMessage to witchCommandsText
 
+pingDiurnalRoleMessage :: Role -> Message
+pingDiurnalRoleMessage role = publicMessage $ diurnalRolePingedText role
+
+pingNocturnalRoleMessage :: Role -> Game -> Message
+pingNocturnalRoleMessage role game
+    | has (variant . _NoRoleKnowledge) game = publicMessage $ NoRoleKnowledge.nocturnalRolePingedText role
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.nocturnalRolePingedText role
+    | otherwise                             = publicMessage $ Standard.nocturnalRolePingedText role
+
 pingPlayerMessage :: Text -> Message
 pingPlayerMessage to = privateMessage to playerPingedText
 
-pingRoleMessage :: Role -> Game -> Message
-pingRoleMessage role game
-    | has (variant . _NoRoleKnowledge) game = publicMessage $ NoRoleKnowledge.rolePingedText role
-    | otherwise                             = publicMessage $ Standard.rolePingedText role
-
 pingVillageMessage :: Message
 pingVillageMessage = publicMessage villagePingedText
 
 pingWerewolvesMessage :: Game -> Message
 pingWerewolvesMessage game
     | has (variant . _NoRoleKnowledge) game = publicMessage NoRoleKnowledge.werewolvesPingedText
+    | has (variant . _NoRoleReveal) game    = publicMessage NoRoleReveal.werewolvesPingedText
     | otherwise                             = publicMessage Standard.werewolvesPingedText
 
-currentStageMessage :: Text -> Game -> Message
-currentStageMessage to game
-    | has (stage . _GameOver) game          = gameIsOverMessage to
-    | has (variant . _NoRoleKnowledge) game = privateMessage to $ NoRoleKnowledge.currentTurnText game
-    | otherwise                             = privateMessage to $ Standard.currentTurnText game
+currentDiurnalTurnMessage :: Text -> Game -> Message
+currentDiurnalTurnMessage to game = privateMessage to $ Standard.currentDiurnalTurnText game
 
+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
+    | otherwise                             = privateMessage to $ Standard.currentNocturnalTurnText game
+
 gameIsOverMessage :: Text -> Message
 gameIsOverMessage to = privateMessage to gameOverText
 
@@ -178,8 +192,9 @@
     where
         alivePlayersText' = alivePlayersText game
         deadPlayersText'
-            | has (players . traverse . dead) game  = deadPlayersText game
-            | otherwise                             = T.empty
+            | hasn't (players . traverse . dead) game   = T.empty
+            | has (variant . _NoRoleReveal) game        = NoRoleReveal.deadPlayersText game
+            | otherwise                                 = Standard.deadPlayersText game
 
 playerRescindedVoteMessage :: Text -> Player -> Message
 playerRescindedVoteMessage to = privateMessage to . callerRescindedVoteText
diff --git a/app/Game/Werewolf/Message/Engine.hs b/app/Game/Werewolf/Message/Engine.hs
--- a/app/Game/Werewolf/Message/Engine.hs
+++ b/app/Game/Werewolf/Message/Engine.hs
@@ -12,7 +12,7 @@
 
 -- TODO (hjw): sort this file
 module Game.Werewolf.Message.Engine (
-    playerBootedMessage, villageDrunkJoinedPackMessages, villageDrunkJoinedVillageMessage,
+    playerRolesMessage, playerBootedMessage, villageDrunkJoinedPackMessages, villageDrunkJoinedVillageMessage,
     playerTurnedToStoneMessage, playerSeenMessage, playerDivinedMessage, playerDevouredMessage,
     noPlayerDevouredMessage, scapegoatChoseAllowedVotersMessage, playerPoisonedMessage,
     scapegoatLynchedMessage, playerLynchedMessage, noPlayerLynchedMessage, jesterLynchedMessage,
@@ -20,13 +20,12 @@
     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,
+    oraclesTurnMessages, huntersTurnMessages, stageMessages,
+    trueVillagerMessage, beholderMessage, newPlayerMessage, rolesInGameMessage,
+    newPlayersInGameMessage, newGameMessages, gameOverMessages, fallenAngelWonMessage,
+    werewolfLynchedMessage,
 ) where
 
-import Control.Lens
 import Control.Lens.Extra
 
 import Data.List.Extra
@@ -37,13 +36,22 @@
 import Game.Werewolf.Response
 import Game.Werewolf.Role                           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 -> Message
-playerBootedMessage = publicMessage . playerBootedText
+playerBootedMessage :: Player -> Game -> Message
+playerBootedMessage player game
+    | 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]
@@ -70,8 +78,12 @@
         playerContributedMessages   = map playerContributedMessage (winningPlayers ^.. traverse . dead . name)
         playerLostMessages          = map playerLostMessage (losingPlayers ^.. names)
 
-        fallenAngelsName = game ^?! players . fallenAngels . name
+        dullahansName       = game ^?! players . dullahans . name
+        fallenAngelsName    = game ^?! players . fallenAngels . name
 
+dullahanWonMessage :: Game -> Message
+dullahanWonMessage = publicMessage . dullahanWonText
+
 fallenAngelWonMessage :: Message
 fallenAngelWonMessage = publicMessage fallenAngelWonText
 
@@ -87,23 +99,33 @@
     , [rolesInGameMessage Nothing game]
     , map newPlayerMessage players'
     , beholderMessages
-    , spitefulGhostMessages
+    , dullahanMessages
+    , 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 []
+        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
+            ]
+        spitefulGhostMessages'
+            | has spitefulGhosts players'   = spitefulGhostMessages spitefulGhostName game
+            | otherwise                     = []
+            where
+                spitefulGhostName = players' ^?! spitefulGhosts . name
+        trueVillagerMessages    =
+            [ trueVillagerMessage game
+            | has trueVillagers players'
+            ]
 
 newPlayersInGameMessage :: Game -> Message
 newPlayersInGameMessage = publicMessage . playersInGameText
@@ -119,15 +141,18 @@
 beholderMessage :: Text -> Game -> Message
 beholderMessage to = privateMessage to . beholderText
 
-spitefulGhostMessage :: Text -> Game -> Message
-spitefulGhostMessage to = privateMessage to . spitefulGhostText
+dullahanMessage :: Text -> Game -> Message
+dullahanMessage to = privateMessage to . dullahanText
 
+spitefulGhostMessages :: Text -> Game -> [Message]
+spitefulGhostMessages to game =
+    [ privateMessage to $ spitefulGhostPrivateText game
+    , publicMessage $ spitefulGhostPublicText game
+    ]
+
 trueVillagerMessage :: Game -> Message
 trueVillagerMessage = publicMessage . trueVillagerText
 
-fallenAngelMessage :: Message
-fallenAngelMessage = publicMessage fallenAngelText
-
 stageMessages :: Game -> [Message]
 stageMessages game = case game ^. stage of
     DruidsTurn          -> []
@@ -168,6 +193,8 @@
 oraclesTurnMessages to game
     | has (variant . _NoRoleKnowledge) game =
         [ privateMessage to oraclesTurnPrivateText ]
+    | has (variant . _NoRoleReveal) game    =
+        [ privateMessage to oraclesTurnPrivateText ]
     | otherwise                             =
         [ publicMessage oraclesTurnPublicText
         , privateMessage to oraclesTurnPrivateText
@@ -177,6 +204,8 @@
 orphansTurnMessages to game
     | has (variant . _NoRoleKnowledge) game =
         [ privateMessage to orphansTurnPrivateText ]
+    | has (variant . _NoRoleReveal) game    =
+        [ privateMessage to orphansTurnPrivateText ]
     | otherwise                             =
         [ publicMessage orphansTurnPublicText
         , privateMessage to orphansTurnPrivateText
@@ -186,6 +215,8 @@
 protectorsTurnMessages to game
     | has (variant . _NoRoleKnowledge) game =
         [ privateMessage to protectorsTurnPrivateText ]
+    | has (variant . _NoRoleReveal) game    =
+        [ privateMessage to protectorsTurnPrivateText ]
     | otherwise                             =
         [ publicMessage protectorsTurnPublicText
         , privateMessage to protectorsTurnPrivateText
@@ -198,6 +229,8 @@
 seersTurnMessages to game
     | has (variant . _NoRoleKnowledge) game =
         [ privateMessage to seersTurnPrivateText ]
+    | has (variant . _NoRoleReveal) game    =
+        [ privateMessage to seersTurnPrivateText ]
     | otherwise                             =
         [ publicMessage seersTurnPublicText
         , privateMessage to seersTurnPrivateText
@@ -206,6 +239,7 @@
 villageDrunksTurnMessages :: Game -> [Message]
 villageDrunksTurnMessages game
     | has (variant . _NoRoleKnowledge) game = []
+    | has (variant . _NoRoleReveal) game    = []
     | otherwise                             = [publicMessage villageDrunksTurnText]
 
 sunriseMessage :: Message
@@ -229,6 +263,8 @@
 werewolvesTurnMessages tos game
     | has (variant . _NoRoleKnowledge) game =
         groupMessages tos werewolvesTurnPrivateText
+    | has (variant . _NoRoleReveal) game    =
+        groupMessages tos werewolvesTurnPrivateText
     | otherwise                             =
         publicMessage werewolvesTurnPublicText
         : groupMessages tos werewolvesTurnPrivateText
@@ -244,6 +280,7 @@
         to              = game ^?! players . witches . name
         wakeUpMessages
             | has (variant . _NoRoleKnowledge) game = []
+            | has (variant . _NoRoleReveal) game    = []
             | otherwise                             = [publicMessage witchsTurnText]
         passMessage     = privateMessage to passText
         healMessages
@@ -283,12 +320,16 @@
 noPlayerLynchedMessage :: Message
 noPlayerLynchedMessage = publicMessage noPlayerLynchedText
 
-playerLynchedMessage :: Player -> Message
-playerLynchedMessage player
-    | is simpleWerewolf player
-        || is alphaWolf player  = publicMessage $ werewolfLynchedText player
-    | otherwise                 = publicMessage $ playerLynchedText player
+werewolfLynchedMessage :: Player -> Game -> Message
+werewolfLynchedMessage player game
+    | 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
+
 scapegoatLynchedMessage :: Game -> Message
 scapegoatLynchedMessage = publicMessage . scapegoatLynchedText
 
@@ -298,14 +339,18 @@
 noPlayerDevouredMessage :: Message
 noPlayerDevouredMessage = publicMessage noPlayerDevouredText
 
-playerDevouredMessage :: Player -> Message
-playerDevouredMessage = publicMessage . playerDevouredText
+playerDevouredMessage :: Player -> Game -> Message
+playerDevouredMessage player game
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.playerDevouredText player
+    | otherwise                             = publicMessage $ Standard.playerDevouredText player
 
 playerDivinedMessage :: Text -> Player -> Message
 playerDivinedMessage to = privateMessage to . playerDivinedText
 
-playerPoisonedMessage :: Player -> Message
-playerPoisonedMessage = publicMessage . playerPoisonedText
+playerPoisonedMessage :: Player -> Game -> Message
+playerPoisonedMessage player game
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.playerPoisonedText player
+    | otherwise                             = publicMessage $ Standard.playerPoisonedText player
 
 playerSeenMessage :: Text -> Player -> Message
 playerSeenMessage to player
@@ -313,8 +358,10 @@
     | is lycan player       = privateMessage to $ lycanSeenText player
     | otherwise             = privateMessage to $ playerSeenText player
 
-playerTurnedToStoneMessage :: Player -> Message
-playerTurnedToStoneMessage = publicMessage . playerTurnedToStoneText
+playerTurnedToStoneMessage :: Player -> Game -> Message
+playerTurnedToStoneMessage player game
+    | has (variant . _NoRoleReveal) game    = publicMessage $ NoRoleReveal.playerTurnedToStoneText player
+    | otherwise                             = publicMessage $ Standard.playerTurnedToStoneText player
 
 villageDrunkJoinedVillageMessage :: Text -> Message
 villageDrunkJoinedVillageMessage to = privateMessage to villageDrunkJoinedVillageText
diff --git a/app/Game/Werewolf/Util.hs b/app/Game/Werewolf/Util.hs
--- a/app/Game/Werewolf/Util.hs
+++ b/app/Game/Werewolf/Util.hs
@@ -26,7 +26,7 @@
     -- ** Queries
     isGameOver, isHuntersTurn, isOraclesTurn, isOrphansTurn, isProtectorsTurn, isScapegoatsTurn,
     isSeersTurn, isSunrise, isVillagesTurn, isWerewolvesTurn, isWitchsTurn,
-    hasAnyoneWon, hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon,
+    hasAnyoneWon, hasVillagersWon, hasWerewolvesWon,
 
     -- * Player
 
@@ -38,7 +38,6 @@
     isPlayerAlive, isPlayerDead,
 ) where
 
-import Control.Lens         hiding (isn't)
 import Control.Lens.Extra
 import Control.Monad.Extra
 import Control.Monad.Random
@@ -51,8 +50,7 @@
 import           Data.Text  (Text)
 
 import           Game.Werewolf.Game           hiding (getAllowedVoters, getPendingVoters,
-                                               hasAnyoneWon, hasFallenAngelWon, hasVillagersWon,
-                                               hasWerewolvesWon)
+                                               hasAnyoneWon, hasVillagersWon, hasWerewolvesWon)
 import qualified Game.Werewolf.Game           as Game
 import           Game.Werewolf.Message.Engine
 import           Game.Werewolf.Player
@@ -162,9 +160,6 @@
 
 hasAnyoneWon :: MonadState Game m => m Bool
 hasAnyoneWon = gets Game.hasAnyoneWon
-
-hasFallenAngelWon :: MonadState Game m => m Bool
-hasFallenAngelWon = gets Game.hasFallenAngelWon
 
 hasVillagersWon :: MonadState Game m => m Bool
 hasVillagersWon = gets Game.hasVillagersWon
diff --git a/app/Game/Werewolf/Variant/NoRoleKnowledge/Command.hs b/app/Game/Werewolf/Variant/NoRoleKnowledge/Command.hs
--- a/app/Game/Werewolf/Variant/NoRoleKnowledge/Command.hs
+++ b/app/Game/Werewolf/Variant/NoRoleKnowledge/Command.hs
@@ -15,10 +15,10 @@
 
 module Game.Werewolf.Variant.NoRoleKnowledge.Command (
     -- * Ping
-    rolePingedText, werewolvesPingedText,
+    nocturnalRolePingedText, werewolvesPingedText,
 
     -- * Status
-    currentTurnText,
+    currentNocturnalTurnText,
 ) where
 
 import Data.String.Interpolate.Extra
@@ -26,11 +26,11 @@
 
 import Game.Werewolf
 
-rolePingedText :: Role -> Text
-rolePingedText _ = [iFile|variant/no-role-knowledge/command/ping/role-pinged.txt|]
+nocturnalRolePingedText :: Role -> Text
+nocturnalRolePingedText _ = [iFile|variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt|]
 
 werewolvesPingedText :: Text
 werewolvesPingedText = [iFile|variant/no-role-knowledge/command/ping/werewolves-pinged.txt|]
 
-currentTurnText :: Game -> Text
-currentTurnText _ = [iFile|variant/no-role-knowledge/command/status/current-turn.txt|]
+currentNocturnalTurnText :: Game -> Text
+currentNocturnalTurnText _ = [iFile|variant/no-role-knowledge/command/status/current-nocturnal-turn.txt|]
diff --git a/app/Game/Werewolf/Variant/NoRoleReveal/Command.hs b/app/Game/Werewolf/Variant/NoRoleReveal/Command.hs
new file mode 100644
--- /dev/null
+++ b/app/Game/Werewolf/Variant/NoRoleReveal/Command.hs
@@ -0,0 +1,55 @@
+{-|
+Module      : Game.Werewolf.Variant.NoRoleReveal.Command
+Description : Suite of command 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 command messages used throughout the werewolf game for the 'NoRoleReveal' variant.
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+
+module Game.Werewolf.Variant.NoRoleReveal.Command (
+    -- * Choose
+    playerShotText,
+
+    -- * Ping
+    nocturnalRolePingedText, werewolvesPingedText,
+
+    -- * Quit
+    callerQuitText,
+
+    -- * Status
+    currentNocturnalTurnText, deadPlayersText,
+) where
+
+import Control.Lens
+
+import Data.String.Humanise
+import Data.String.Interpolate.Extra
+import Data.Text                     (Text)
+
+import Game.Werewolf
+import Game.Werewolf.Message
+
+playerShotText :: Player -> Text
+playerShotText player = [iFile|variant/no-role-reveal/command/choose/player-shot.txt|]
+
+nocturnalRolePingedText :: Role -> Text
+nocturnalRolePingedText _ = [iFile|variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt|]
+
+werewolvesPingedText :: Text
+werewolvesPingedText = [iFile|variant/no-role-reveal/command/ping/werewolves-pinged.txt|]
+
+callerQuitText :: Player -> Text
+callerQuitText caller = [iFile|variant/no-role-reveal/command/quit/caller-quit.txt|]
+
+currentNocturnalTurnText :: Game -> Text
+currentNocturnalTurnText _ = [iFile|variant/no-role-reveal/command/status/current-nocturnal-turn.txt|]
+
+deadPlayersText :: Game -> Text
+deadPlayersText game = [iFile|variant/no-role-reveal/command/status/dead-players.txt|]
diff --git a/app/Game/Werewolf/Variant/NoRoleReveal/Engine.hs b/app/Game/Werewolf/Variant/NoRoleReveal/Engine.hs
new file mode 100644
--- /dev/null
+++ b/app/Game/Werewolf/Variant/NoRoleReveal/Engine.hs
@@ -0,0 +1,48 @@
+{-|
+Module      : Game.Werewolf.Variant.NoRoleReveal.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 for the 'NoRoleReveal' variant.
+-}
+
+{-# LANGUAGE QuasiQuotes #-}
+
+module Game.Werewolf.Variant.NoRoleReveal.Engine (
+    -- * General
+    playerBootedText,
+
+    -- * Lynching
+    playerLynchedText, werewolfLynchedText,
+
+    -- * Sunrise
+    playerDevouredText, playerPoisonedText, playerTurnedToStoneText,
+) where
+
+import Data.String.Humanise
+import Data.String.Interpolate.Extra
+import Data.Text                     (Text)
+
+import Game.Werewolf
+
+playerBootedText :: Player -> Text
+playerBootedText player = [iFile|variant/no-role-reveal/engine/general/player-booted.txt|]
+
+playerLynchedText :: Player -> Text
+playerLynchedText player = [iFile|variant/no-role-reveal/engine/lynching/player-lynched.txt|]
+
+werewolfLynchedText :: Player -> Text
+werewolfLynchedText werewolf = [iFile|variant/no-role-reveal/engine/lynching/werewolf-lynched.txt|]
+
+playerDevouredText :: Player -> Text
+playerDevouredText player = [iFile|variant/no-role-reveal/engine/sunrise/player-devoured.txt|]
+
+playerPoisonedText :: Player -> Text
+playerPoisonedText player = [iFile|variant/no-role-reveal/engine/sunrise/player-poisoned.txt|]
+
+playerTurnedToStoneText :: Player -> Text
+playerTurnedToStoneText player = [iFile|variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt|]
diff --git a/app/Game/Werewolf/Variant/Standard/Command.hs b/app/Game/Werewolf/Variant/Standard/Command.hs
--- a/app/Game/Werewolf/Variant/Standard/Command.hs
+++ b/app/Game/Werewolf/Variant/Standard/Command.hs
@@ -36,13 +36,15 @@
     witchCommandsText, witchsTurnText,
 
     -- * Ping
-    playerPingedText, rolePingedText, villagePingedText, werewolvesPingedText,
+    diurnalRolePingedText, nocturnalRolePingedText, playerPingedText, villagePingedText,
+    werewolvesPingedText,
 
     -- * Quit
     callerQuitText,
 
     -- * Status
-    alivePlayersText, currentTurnText, deadPlayersText, gameOverText,
+    alivePlayersText, currentDiurnalTurnText, currentNocturnalTurnText, deadPlayersText,
+    gameOverText,
 
     -- * Unvote
     callerRescindedVoteText,
@@ -166,12 +168,15 @@
 witchsTurnText :: Text
 witchsTurnText = [iFile|variant/standard/command/help/witchs-turn.txt|]
 
+diurnalRolePingedText :: Role -> Text
+diurnalRolePingedText role = [iFile|variant/standard/command/ping/diurnal-role-pinged.txt|]
+
+nocturnalRolePingedText :: Role -> Text
+nocturnalRolePingedText role = [iFile|variant/standard/command/ping/nocturnal-role-pinged.txt|]
+
 playerPingedText :: Text
 playerPingedText = [iFile|variant/standard/command/ping/player-pinged.txt|]
 
-rolePingedText :: Role -> Text
-rolePingedText role = [iFile|variant/standard/command/ping/role-pinged.txt|]
-
 villagePingedText :: Text
 villagePingedText = [iFile|variant/standard/command/ping/village-pinged.txt|]
 
@@ -184,8 +189,11 @@
 alivePlayersText :: Game -> Text
 alivePlayersText game = [iFile|variant/standard/command/status/alive-players.txt|]
 
-currentTurnText :: Game -> Text
-currentTurnText game = [iFile|variant/standard/command/status/current-turn.txt|]
+currentDiurnalTurnText :: Game -> Text
+currentDiurnalTurnText game = [iFile|variant/standard/command/status/current-diurnal-turn.txt|]
+
+currentNocturnalTurnText :: Game -> Text
+currentNocturnalTurnText game = [iFile|variant/standard/command/status/current-nocturnal-turn.txt|]
 
 deadPlayersText :: Game -> Text
 deadPlayersText game = [iFile|variant/standard/command/status/dead-players.txt|]
diff --git a/app/Game/Werewolf/Variant/Standard/Engine.hs b/app/Game/Werewolf/Variant/Standard/Engine.hs
--- a/app/Game/Werewolf/Variant/Standard/Engine.hs
+++ b/app/Game/Werewolf/Variant/Standard/Engine.hs
@@ -21,8 +21,8 @@
     playerBootedText, playerKilledText,
 
     -- * Game over
-    allegianceWonText, fallenAngelWonText, playerContributedText, playerLostText, playerRolesText,
-    playerWonText,
+    allegianceWonText, dullahanWonText, fallenAngelWonText, playerContributedText, playerLostText,
+    playerRolesText, playerWonText,
 
     -- * Hunter's turn
     huntersTurnPrivateText, huntersTurnPublicText,
@@ -32,8 +32,8 @@
     werewolfLynchedText,
 
     -- * New game
-    beholderText, fallenAngelText, newPlayerText, playersInGameText, rolesInGameText,
-    spitefulGhostText, trueVillagerText,
+    beholderText, dullahanText, newPlayerText, playersInGameText, rolesInGameText,
+    spitefulGhostPrivateText, spitefulGhostPublicText, trueVillagerText,
 
     -- * Oracle's turn
     oraclesTurnPrivateText, oraclesTurnPublicText,
@@ -73,7 +73,6 @@
 ) where
 
 import Control.Arrow
-import Control.Lens       hiding (isn't)
 import Control.Lens.Extra
 
 import           Data.List.Extra
@@ -97,6 +96,9 @@
 allegianceWonText :: Allegiance -> Text
 allegianceWonText allegiance = [iFile|variant/standard/engine/game-over/allegiance-won.txt|]
 
+dullahanWonText :: Game -> Text
+dullahanWonText game = [iFile|variant/standard/engine/game-over/dullahan-won.txt|]
+
 fallenAngelWonText :: Text
 fallenAngelWonText = [iFile|variant/standard/engine/game-over/fallen-angel-won.txt|]
 
@@ -144,8 +146,8 @@
     where
         seer = game ^?! players . seers
 
-fallenAngelText :: Text
-fallenAngelText = [iFile|variant/standard/engine/new-game/fallen-angel.txt|]
+dullahanText :: Game -> Text
+dullahanText game = [iFile|variant/standard/engine/new-game/dullahan.txt|]
 
 newPlayerText :: Player -> Text
 newPlayerText player = [iFile|variant/standard/engine/new-game/new-player.txt|]
@@ -160,8 +162,13 @@
         roleCounts      = map (head &&& length) (groupSortOn humanise roles)
         totalBalance    = sumOf (traverse . balance) roles
 
-spitefulGhostText :: Game -> Text
-spitefulGhostText game = [iFile|variant/standard/engine/new-game/spiteful-ghost.txt|]
+spitefulGhostPrivateText :: Game -> Text
+spitefulGhostPrivateText game = [iFile|variant/standard/engine/new-game/spiteful-ghost-private.txt|]
+
+spitefulGhostPublicText :: Game -> Text
+spitefulGhostPublicText game = [iFile|variant/standard/engine/new-game/spiteful-ghost-public.txt|]
+    where
+        spitefulGhost = game ^?! players . spitefulGhosts
 
 trueVillagerText :: Game -> Text
 trueVillagerText game = [iFile|variant/standard/engine/new-game/true-villager.txt|]
diff --git a/app/Werewolf/Command/End.hs b/app/Werewolf/Command/End.hs
--- a/app/Werewolf/Command/End.hs
+++ b/app/Werewolf/Command/End.hs
@@ -20,8 +20,10 @@
 
 import Data.Text (Text)
 
+-- TODO (hjw): remove Message.Engine
 import Game.Werewolf
 import Game.Werewolf.Message.Command
+import Game.Werewolf.Message.Engine
 import Game.Werewolf.Message.Error
 
 import Werewolf.System
@@ -37,4 +39,7 @@
 
     deleteGame tag
 
-    exitWith success { messages = [gameEndedMessage callerName] }
+    exitWith success { messages =
+        [ gameEndedMessage callerName
+        , playerRolesMessage game
+        ] }
diff --git a/app/Werewolf/Command/Start.hs b/app/Werewolf/Command/Start.hs
--- a/app/Werewolf/Command/Start.hs
+++ b/app/Werewolf/Command/Start.hs
@@ -20,7 +20,6 @@
     handle,
 ) where
 
-import Control.Lens
 import Control.Lens.Extra
 import Control.Monad.Except
 import Control.Monad.Extra
@@ -95,7 +94,7 @@
         simpleWerewolfBalance   = simpleWerewolfRole ^. balance
 
         -- Little magic here to calculate how many Werewolves and Villagers we want.
-        -- This tries to ensure that the balance of the game is between -2 and 2.
+        -- This tries to ensure that the balance of the game is between -3 and 2.
         simpleWerewolvesCount   = (goal - m - startingBalance) `div` (simpleWerewolfBalance - 1) + 1
         simpleVillagersCount    = m - simpleWerewolvesCount
 
diff --git a/app/Werewolf/Options.hs b/app/Werewolf/Options.hs
--- a/app/Werewolf/Options.hs
+++ b/app/Werewolf/Options.hs
@@ -186,6 +186,7 @@
         variantOption = option $ readerAsk >>= \opt -> case opt of
             "standard"          -> return Standard
             "no-role-knowledge" -> return NoRoleKnowledge
+            "no-role-reveal"    -> return NoRoleReveal
             _                   -> readerError $ "unrecognised variant `" ++ opt ++ "'"
 
         extraRolesOption = fmap (Start.Use . filter (/= T.empty) . T.splitOn "," . T.pack) (strOption $ mconcat
diff --git a/app/Werewolf/System.hs b/app/Werewolf/System.hs
--- a/app/Werewolf/System.hs
+++ b/app/Werewolf/System.hs
@@ -22,8 +22,9 @@
     filePath, readGame, writeGame, deleteGame, writeOrDeleteGame, doesGameExist,
 ) where
 
-import Control.Lens         hiding (cons)
+import Control.Lens.Extra   hiding (cons)
 import Control.Monad.Except
+import Control.Monad.Random
 import Control.Monad.Writer
 
 import           Data.List
@@ -38,22 +39,32 @@
 
 import System.Directory
 import System.FilePath
+import System.Random.Shuffle
 
-startGame :: (MonadError [Message] m, MonadWriter [Message] m) => Text -> Variant -> [Player] -> m Game
-startGame callerName variant players = do
+startGame :: (MonadError [Message] m, MonadRandom m, MonadWriter [Message] m) => Text -> Variant -> [Player] -> m Game
+startGame callerName variant players' = do
     when (playerNames /= nub playerNames)   $ throwError [playerNamesMustBeUniqueMessage callerName]
-    when (length players < 7)               $ throwError [mustHaveAtLeast7PlayersMessage callerName]
+    when (length players' < 7)              $ throwError [mustHaveAtLeast7PlayersMessage callerName]
     forM_ restrictedRoles $ \role ->
-        when (length (players ^.. roles . only role) > 1) $
+        when (length (players' ^.. roles . only role) > 1) $
             throwError [roleCountRestrictedMessage callerName role]
 
-    let game = newGame variant players
+    let game    = newGame variant players'
+    game'       <- (\marks' -> game & marks .~ marks' ^.. names) <$> randomMarks game
 
-    tell $ newGameMessages game
+    tell $ newGameMessages game'
 
-    return game
+    return game'
     where
-        playerNames = players ^.. names
+        playerNames = players' ^.. names
+
+randomMarks :: MonadRandom m => Game -> m [Player]
+randomMarks game = do
+    let count = length potentialMarks `div` 3 + 1
+
+    take count <$> shuffleM potentialMarks
+    where
+        potentialMarks = game ^.. players . traverse . filtered (isn't dullahan)
 
 filePath :: MonadIO m => Text -> m FilePath
 filePath tag = (</> ".werewolf" </> T.unpack tag) <$> liftIO getHomeDirectory
diff --git a/src/Control/Lens/Extra.hs b/src/Control/Lens/Extra.hs
--- a/src/Control/Lens/Extra.hs
+++ b/src/Control/Lens/Extra.hs
@@ -12,6 +12,8 @@
 {-# LANGUAGE Rank2Types #-}
 
 module Control.Lens.Extra (
+    module Control.Lens,
+
     -- * Folds
     is, isn't, hasuse, hasn'tuse,
 
diff --git a/src/Game/Werewolf.hs b/src/Game/Werewolf.hs
--- a/src/Game/Werewolf.hs
+++ b/src/Game/Werewolf.hs
@@ -24,4 +24,4 @@
 import Game.Werewolf.Game     as Werewolf
 import Game.Werewolf.Player   as Werewolf
 import Game.Werewolf.Response as Werewolf
-import Game.Werewolf.Role     as Werewolf hiding (name)
+import Game.Werewolf.Role     as Werewolf hiding (activity, name)
diff --git a/src/Game/Werewolf/Game.hs b/src/Game/Werewolf/Game.hs
--- a/src/Game/Werewolf/Game.hs
+++ b/src/Game/Werewolf/Game.hs
@@ -10,6 +10,7 @@
 structure and any fields required to keep track of the current state.
 -}
 
+{-# LANGUAGE CPP               #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE Rank2Types        #-}
 {-# LANGUAGE TemplateHaskell   #-}
@@ -18,16 +19,17 @@
     -- * Game
     Game,
     variant, stage, round, players, boots, allowedVoters, divine, fallenAngelLynched, healUsed,
-    hunterRetaliated, jesterRevealed, passed, poison, poisonUsed, priorProtect, protect, roleModel,
-    scapegoatBlamed, see, votes,
+    hunterRetaliated, jesterRevealed, marks, passed, poison, poisonUsed, priorProtect, protect,
+    roleModel, scapegoatBlamed, see, votes,
 
     Variant(..),
-    _Standard, _NoRoleKnowledge,
+    _Standard, _NoRoleKnowledge, _NoRoleReveal,
 
     Stage(..),
     _DruidsTurn, _GameOver, _HuntersTurn1, _HuntersTurn2, _Lynching, _OraclesTurn, _OrphansTurn,
     _ProtectorsTurn, _ScapegoatsTurn, _SeersTurn, _Sunrise, _Sunset, _VillageDrunksTurn,
     _VillagesTurn, _WerewolvesTurn, _WitchsTurn,
+    activity,
 
     allStages,
     stageCycle, stageAvailable,
@@ -41,13 +43,12 @@
     firstRound, secondRound, thirdRound,
 
     -- ** Searches
-    getAllowedVoters, getPendingVoters,
+    getAllowedVoters, getPendingVoters, getMarks,
 
     -- ** Queries
-    hasAnyoneWon, hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon,
+    hasAnyoneWon, hasDullahanWon, hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon,
 ) where
 
-import Control.Lens       hiding (isn't)
 import Control.Lens.Extra
 
 import           Data.List.Extra
@@ -58,6 +59,7 @@
 import           Data.Text            (Text)
 
 import Game.Werewolf.Player
+import Game.Werewolf.Role   hiding (activity, name)
 
 import Prelude hiding (round)
 
@@ -82,6 +84,7 @@
     , _healUsed           :: Bool             -- ^ Witch
     , _hunterRetaliated   :: Bool             -- ^ Hunter
     , _jesterRevealed     :: Bool             -- ^ Jester
+    , _marks              :: [Text]           -- ^ Dullahan
     , _passed             :: Bool             -- ^ Witch
     , _poison             :: Maybe Text       -- ^ Witch
     , _poisonUsed         :: Bool             -- ^ Witch
@@ -93,12 +96,13 @@
     , _votes              :: Map Text Text    -- ^ Villagers and Werewolves
     } deriving (Eq, Read, Show)
 
-data Variant = Standard | NoRoleKnowledge
+data Variant = Standard | NoRoleKnowledge | NoRoleReveal
     deriving (Eq, Read, Show)
 
 instance Humanise Variant where
     humanise Standard           = "standard"
     humanise NoRoleKnowledge    = "no role knowledge"
+    humanise NoRoleReveal       = "no role reveal"
 
 -- | 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
@@ -135,6 +139,30 @@
 
 makePrisms ''Stage
 
+#if __GLASGOW_HASKELL__ >= 800
+activity :: Contravariant f => (Activity -> f Activity) -> Stage -> f Stage
+#else
+activity :: Getter Stage Activity
+#endif
+activity = to getter
+    where
+        getter DruidsTurn           = Diurnal
+        getter GameOver             = Diurnal
+        getter HuntersTurn1         = Diurnal
+        getter HuntersTurn2         = Diurnal
+        getter Lynching             = Diurnal
+        getter OraclesTurn          = Nocturnal
+        getter OrphansTurn          = Nocturnal
+        getter ProtectorsTurn       = Nocturnal
+        getter ScapegoatsTurn       = Diurnal
+        getter SeersTurn            = Nocturnal
+        getter Sunrise              = Diurnal
+        getter Sunset               = Diurnal
+        getter VillageDrunksTurn    = Nocturnal
+        getter VillagesTurn         = Diurnal
+        getter WerewolvesTurn       = Nocturnal
+        getter WitchsTurn           = Nocturnal
+
 -- | All of the 'Stage's in the order that they should occur.
 allStages :: [Stage]
 allStages =
@@ -209,6 +237,7 @@
     , _healUsed             = False
     , _hunterRetaliated     = False
     , _jesterRevealed       = False
+    , _marks                = []
     , _poison               = Nothing
     , _poisonUsed           = False
     , _priorProtect         = Nothing
@@ -260,10 +289,24 @@
     where
         votes' = game ^. votes
 
+-- | Gets all the 'marks' in a game (which is names only) and maps them to their player.
+getMarks :: Game -> [Player]
+getMarks game = map (\name -> game ^?! players . traverse . named name) (game ^. marks)
+
 -- | Queries whether anyone has won.
 hasAnyoneWon :: Game -> Bool
-hasAnyoneWon game = any ($ game) [hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon]
+hasAnyoneWon game = any ($ game)
+    [ hasDullahanWon
+    , hasFallenAngelWon
+    , hasVillagersWon
+    , hasWerewolvesWon
+    ]
 
+-- | Queries whether the Dullahan has won. The Dullahan wins if they manage to eliminate all their
+--   marks.
+hasDullahanWon :: Game -> Bool
+hasDullahanWon game = has (players . dullahans . alive) game && all (is dead) (getMarks game)
+
 -- | Queries whether the Fallen Angel has won. The Fallen Angel wins if they manage to get
 --   themselves lynched by the Villagers.
 hasFallenAngelWon :: Game -> Bool
@@ -272,9 +315,11 @@
 -- | Queries whether the 'Villagers' have won. The 'Villagers' win if they are the only players
 --   surviving.
 --
---   N.B., the Fallen Angel is not considered when determining whether the 'Villagers' have won.
+--   N.B., the Dullahan and Fallen Angel are not considered when determining whether the 'Villagers'
+--   have won.
 hasVillagersWon :: Game -> Bool
-hasVillagersWon = allOf (players . traverse . alive) (\player -> is villager player || is fallenAngel player)
+hasVillagersWon = allOf (players . traverse . alive)
+    (\player -> any ($ player) [is villager, is dullahan, is fallenAngel])
 
 -- | Queries whether the 'Werewolves' have won. The 'Werewolves' win if they are the only players
 --   surviving.
diff --git a/src/Game/Werewolf/Player.hs b/src/Game/Werewolf/Player.hs
--- a/src/Game/Werewolf/Player.hs
+++ b/src/Game/Werewolf/Player.hs
@@ -24,9 +24,9 @@
     newPlayer,
 
     -- ** Traversals
-    alphaWolf, beholder, crookedSenator, druid, fallenAngel, hunter, jester, lycan, medusa, oracle,
-    orphan, protector, scapegoat, seer, simpleVillager, simpleWerewolf, spitefulGhost, trueVillager,
-    villageDrunk, witch,
+    alphaWolf, beholder, crookedSenator, druid, dullahan, fallenAngel, hunter, jester, lycan,
+    medusa, oracle, orphan, protector, scapegoat, seer, simpleVillager, simpleWerewolf,
+    spitefulGhost, trueVillager, villageDrunk, witch,
     loner, villager, werewolf,
 
     -- | The following traversals are provided just as a bit of sugar to avoid continually writing
@@ -35,14 +35,13 @@
 
     -- | N.B., the following traversals are not legal for the same reason 'filtered' isn't!
     named,
-    alphaWolves, beholders, crookedSenators, druids, fallenAngels, hunters, jesters, lycans,
-    medusas, oracles, orphans, protectors, scapegoats, seers, simpleVillagers, simpleWerewolves,
-    spitefulGhosts, trueVillagers, villageDrunks, witches,
+    alphaWolves, beholders, crookedSenators, druids, dullahans, fallenAngels, hunters, jesters,
+    lycans, medusas, oracles, orphans, protectors, scapegoats, seers, simpleVillagers,
+    simpleWerewolves, spitefulGhosts, trueVillagers, villageDrunks, witches,
     loners, villagers, werewolves,
     alive, dead,
 ) where
 
-import Control.Lens       hiding (isn't)
 import Control.Lens.Extra
 
 import Data.Function
@@ -105,6 +104,14 @@
 crookedSenator :: Traversal' Player ()
 crookedSenator = role . only crookedSenatorRole
 
+-- | The traversal of 'Player's with a 'dullahanRole'.
+--
+-- @
+-- 'dullahan' = 'role' . 'only' 'dullahanRole'
+-- @
+dullahan :: Traversal' Player ()
+dullahan = role . only dullahanRole
+
 -- | The traversal of 'Player's with a 'druidRole'.
 --
 -- @
@@ -320,6 +327,14 @@
 -- @
 crookedSenators :: Traversable t => Traversal' (t Player) Player
 crookedSenators = traverse . filtered (is crookedSenator)
+
+-- | This 'Traversal' provides the traversal of 'dullahan' 'Player's.
+--
+-- @
+-- 'dullahans' = 'traverse' . 'filtered' ('is' 'dullahan')
+-- @
+dullahans :: Traversable t => Traversal' (t Player) Player
+dullahans = traverse . filtered (is dullahan)
 
 -- | This 'Traversal' provides the traversal of 'druid' 'Player's.
 --
diff --git a/src/Game/Werewolf/Role.hs b/src/Game/Werewolf/Role.hs
--- a/src/Game/Werewolf/Role.hs
+++ b/src/Game/Werewolf/Role.hs
@@ -21,11 +21,14 @@
 module Game.Werewolf.Role (
     -- * Role
     Role,
-    tag, name, allegiance, balance, description, rules,
+    tag, name, allegiance, balance, activity, description, rules,
 
     Allegiance(..),
     _NoOne, _Villagers, _Werewolves,
 
+    Activity(..),
+    _Diurnal, _Nocturnal,
+
     -- ** Instances
     allRoles, restrictedRoles,
 
@@ -39,7 +42,7 @@
     -- | The Loners look out for themselves and themselves alone.
 
     --   The Loners must complete their own objective.
-    fallenAngelRole, spitefulGhostRole,
+    dullahanRole, fallenAngelRole, spitefulGhostRole,
 
     -- *** The Villagers
     -- | Fraught with fear of the unseen enemy, the Villagers must work together to determine the
@@ -79,6 +82,7 @@
     , _name        :: Text
     , _allegiance  :: Allegiance
     , _balance     :: Int
+    , _activity    :: Activity
     , _description :: Text
     , _rules       :: Text
     } deriving (Read, Show)
@@ -93,6 +97,19 @@
     humanise Villagers  = "Villagers"
     humanise Werewolves = "Werewolves"
 
+-- | Defines whether a role is diurnal or nocturnal. I.e., if the role's turn occurs during the day
+--   or night.
+data Activity = Diurnal | Nocturnal
+    deriving (Eq, Read, Show)
+
+instance Humanise Activity where
+    humanise Diurnal    = "diurnal"
+    humanise Nocturnal  = "nocturnal"
+
+makePrisms ''Allegiance
+
+makePrisms ''Activity
+
 makeLenses ''Role
 
 instance Eq Role where
@@ -101,8 +118,6 @@
 instance Humanise Role where
     humanise = view name
 
-makePrisms ''Allegiance
-
 -- | A list containing all the roles defined in this file.
 allRoles :: [Role]
 allRoles =
@@ -110,6 +125,7 @@
     , beholderRole
     , crookedSenatorRole
     , druidRole
+    , dullahanRole
     , fallenAngelRole
     , hunterRole
     , jesterRole
@@ -151,7 +167,8 @@
     { _tag          = "orphan"
     , _name         = T.strip [iFile|variant/standard/role/orphan/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = -1
+    , _balance      = -3
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/orphan/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/orphan/rules.txt|]
     }
@@ -171,11 +188,31 @@
     { _tag          = "village-drunk"
     , _name         = T.strip [iFile|variant/standard/role/village-drunk/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = -1
+    , _balance      = -3
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/village-drunk/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/village-drunk/rules.txt|]
     }
 
+-- | /Normally the Dullahan carries their head under one arm, however while amongst the Villagers,/
+--   /they ere on the side of caution and rest it in a more traditional place. The Dullahan rides a/
+--   /black horse as dark as night and hunts down travellers in the countryside. Beware if the/
+--   /Dullahan knows your name, for you are then marked for death and you should avoid them at all/
+--   /costs.
+--
+--   The Dullahan is given a list of player names at the start of the game. They must eliminate all
+--   of them before the end of the game.
+dullahanRole :: Role
+dullahanRole = Role
+    { _tag          = "dullahan"
+    , _name         = T.strip [iFile|variant/standard/role/dullahan/name.txt|]
+    , _allegiance   = NoOne
+    , _balance      = 0
+    , _activity     = Diurnal
+    , _description  = T.strip [iFile|variant/standard/role/dullahan/description.txt|]
+    , _rules        = T.strip [iFile|variant/standard/role/dullahan/rules.txt|]
+    }
+
 -- | /Long ago during the War in Heaven, angels fell from the sky as one by one those that followed/
 --   /Lucifer were defeated. For centuries they lived amongst mortal Villagers as punishment for/
 --   /their sins and wrongdoings. The Fallen Angel was one such being and is now one of the few/
@@ -190,6 +227,7 @@
     , _name         = T.strip [iFile|variant/standard/role/fallen-angel/name.txt|]
     , _allegiance   = NoOne
     , _balance      = 0
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/fallen-angel/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/fallen-angel/rules.txt|]
     }
@@ -207,6 +245,7 @@
     , _name         = T.strip [iFile|variant/standard/role/spiteful-ghost/name.txt|]
     , _allegiance   = NoOne
     , _balance      = 0
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/spiteful-ghost/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/spiteful-ghost/rules.txt|]
     }
@@ -222,7 +261,8 @@
     { _tag          = "beholder"
     , _name         = T.strip [iFile|variant/standard/role/beholder/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 1
+    , _balance      = 2
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/beholder/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/beholder/rules.txt|]
     }
@@ -239,6 +279,7 @@
     , _name         = T.strip [iFile|variant/standard/role/crooked-senator/name.txt|]
     , _allegiance   = Villagers
     , _balance      = 2
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/crooked-senator/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/crooked-senator/rules.txt|]
     }
@@ -256,7 +297,8 @@
     { _tag          = "druid"
     , _name         = T.strip [iFile|variant/standard/role/druid/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 3
+    , _balance      = 5
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/druid/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/druid/rules.txt|]
     }
@@ -272,7 +314,8 @@
     { _tag          = "hunter"
     , _name         = T.strip [iFile|variant/standard/role/hunter/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 2
+    , _balance      = 3
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/hunter/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/hunter/rules.txt|]
     }
@@ -290,7 +333,8 @@
     { _tag          = "jester"
     , _name         = T.strip [iFile|variant/standard/role/jester/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 0
+    , _balance      = 1
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/jester/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/jester/rules.txt|]
     }
@@ -307,7 +351,8 @@
     { _tag          = "lycan"
     , _name         = T.strip [iFile|variant/standard/role/lycan/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 0
+    , _balance      = -1
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/lycan/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/lycan/rules.txt|]
     }
@@ -326,7 +371,8 @@
     { _tag          = "medusa"
     , _name         = T.strip [iFile|variant/standard/role/medusa/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 3
+    , _balance      = 4
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/medusa/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/medusa/rules.txt|]
     }
@@ -342,7 +388,8 @@
     { _tag          = "oracle"
     , _name         = T.strip [iFile|variant/standard/role/oracle/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 2
+    , _balance      = 4
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/oracle/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/oracle/rules.txt|]
     }
@@ -360,7 +407,8 @@
     { _tag          = "protector"
     , _name         = T.strip [iFile|variant/standard/role/protector/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 2
+    , _balance      = 3
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/protector/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/protector/rules.txt|]
     }
@@ -379,6 +427,7 @@
     , _name         = T.strip [iFile|variant/standard/role/scapegoat/name.txt|]
     , _allegiance   = Villagers
     , _balance      = 0
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/scapegoat/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/scapegoat/rules.txt|]
     }
@@ -393,7 +442,8 @@
     { _tag          = "seer"
     , _name         = T.strip [iFile|variant/standard/role/seer/name.txt|]
     , _allegiance   = Villagers
-    , _balance      = 2
+    , _balance      = 4
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/seer/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/seer/rules.txt|]
     }
@@ -410,6 +460,7 @@
     , _name         = T.strip [iFile|variant/standard/role/simple-villager/name.txt|]
     , _allegiance   = Villagers
     , _balance      = 1
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/simple-villager/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/simple-villager/rules.txt|]
     }
@@ -425,6 +476,7 @@
     , _name         = T.strip [iFile|variant/standard/role/true-villager/name.txt|]
     , _allegiance   = Villagers
     , _balance      = 2
+    , _activity     = Diurnal
     , _description  = T.strip [iFile|variant/standard/role/true-villager/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/true-villager/rules.txt|]
     }
@@ -442,6 +494,7 @@
     , _name         = T.strip [iFile|variant/standard/role/witch/name.txt|]
     , _allegiance   = Villagers
     , _balance      = 3
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/witch/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/witch/rules.txt|]
     }
@@ -457,7 +510,8 @@
     { _tag          = "alpha-wolf"
     , _name         = T.strip [iFile|variant/standard/role/alpha-wolf/name.txt|]
     , _allegiance   = Werewolves
-    , _balance      = -5
+    , _balance      = -7
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/alpha-wolf/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/alpha-wolf/rules.txt|]
     }
@@ -473,7 +527,8 @@
     { _tag          = "simple-werewolf"
     , _name         = T.strip [iFile|variant/standard/role/simple-werewolf/name.txt|]
     , _allegiance   = Werewolves
-    , _balance      = -4
+    , _balance      = -5
+    , _activity     = Nocturnal
     , _description  = T.strip [iFile|variant/standard/role/simple-werewolf/description.txt|]
     , _rules        = T.strip [iFile|variant/standard/role/simple-werewolf/rules.txt|]
     }
diff --git a/variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt b/variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-knowledge/command/ping/nocturnal-role-pinged.txt
@@ -0,0 +1,1 @@
+Waiting on the nocturnal players...
diff --git a/variant/no-role-knowledge/command/ping/role-pinged.txt b/variant/no-role-knowledge/command/ping/role-pinged.txt
deleted file mode 100644
--- a/variant/no-role-knowledge/command/ping/role-pinged.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Waiting on the nocturnal players...
diff --git a/variant/no-role-knowledge/command/status/current-nocturnal-turn.txt b/variant/no-role-knowledge/command/status/current-nocturnal-turn.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-knowledge/command/status/current-nocturnal-turn.txt
@@ -0,0 +1,1 @@
+It's currently night.
diff --git a/variant/no-role-knowledge/command/status/current-turn.txt b/variant/no-role-knowledge/command/status/current-turn.txt
deleted file mode 100644
--- a/variant/no-role-knowledge/command/status/current-turn.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-It's currently night.
diff --git a/variant/no-role-reveal/command/choose/player-shot.txt b/variant/no-role-reveal/command/choose/player-shot.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/choose/player-shot.txt
@@ -0,0 +1,1 @@
+#{humanise player} slumps down to the ground, hands clutching at their chest while blood slips between their fingers and pools around them.
diff --git a/variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt b/variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt
@@ -0,0 +1,1 @@
+Waiting on the nocturnal players...
diff --git a/variant/no-role-reveal/command/ping/werewolves-pinged.txt b/variant/no-role-reveal/command/ping/werewolves-pinged.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/ping/werewolves-pinged.txt
@@ -0,0 +1,1 @@
+Waiting on the nocturnal players...
diff --git a/variant/no-role-reveal/command/quit/caller-quit.txt b/variant/no-role-reveal/command/quit/caller-quit.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/quit/caller-quit.txt
@@ -0,0 +1,1 @@
+#{humanise caller} has quit!
diff --git a/variant/no-role-reveal/command/status/current-nocturnal-turn.txt b/variant/no-role-reveal/command/status/current-nocturnal-turn.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/status/current-nocturnal-turn.txt
@@ -0,0 +1,1 @@
+It's currently night.
diff --git a/variant/no-role-reveal/command/status/dead-players.txt b/variant/no-role-reveal/command/status/dead-players.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/command/status/dead-players.txt
@@ -0,0 +1,1 @@
+The following players are dead: #{humanise $ map humanisePlayerWithRoleIfTrueVillager (game ^..  players . traverse . dead)}.
diff --git a/variant/no-role-reveal/engine/general/player-booted.txt b/variant/no-role-reveal/engine/general/player-booted.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/general/player-booted.txt
@@ -0,0 +1,1 @@
+#{humanise player} has been booted from the game!
diff --git a/variant/no-role-reveal/engine/lynching/player-lynched.txt b/variant/no-role-reveal/engine/lynching/player-lynched.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/lynching/player-lynched.txt
@@ -0,0 +1,1 @@
+#{humanise player} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse.
diff --git a/variant/no-role-reveal/engine/lynching/werewolf-lynched.txt b/variant/no-role-reveal/engine/lynching/werewolf-lynched.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/lynching/werewolf-lynched.txt
@@ -0,0 +1,1 @@
+#{humanise werewolf} is tied up to a pyre and set alight. Eventually the screams start to die and nothing is left but their charred corpse.
diff --git a/variant/no-role-reveal/engine/sunrise/player-devoured.txt b/variant/no-role-reveal/engine/sunrise/player-devoured.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/sunrise/player-devoured.txt
@@ -0,0 +1,1 @@
+As you open them you notice a door broken down and #{humanise player}'s guts half devoured and spilling out over the cobblestones.
diff --git a/variant/no-role-reveal/engine/sunrise/player-poisoned.txt b/variant/no-role-reveal/engine/sunrise/player-poisoned.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/sunrise/player-poisoned.txt
@@ -0,0 +1,1 @@
+Upon further discovery, it looks like the #{humanise witchRole} struck in the night. #{humanise player} is hanging over the side of their bed, poisoned.
diff --git a/variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt b/variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt
new file mode 100644
--- /dev/null
+++ b/variant/no-role-reveal/engine/sunrise/player-turned-to-stone.txt
@@ -0,0 +1,1 @@
+Next to them you see a stone statue, cold to the touch. #{humanise player} must have looked into the eyes of the #{humanise medusaRole} at the very end.
diff --git a/variant/standard/command/ping/diurnal-role-pinged.txt b/variant/standard/command/ping/diurnal-role-pinged.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/command/ping/diurnal-role-pinged.txt
@@ -0,0 +1,1 @@
+Waiting on the #{humanise role}...
diff --git a/variant/standard/command/ping/nocturnal-role-pinged.txt b/variant/standard/command/ping/nocturnal-role-pinged.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/command/ping/nocturnal-role-pinged.txt
@@ -0,0 +1,1 @@
+Waiting on the #{humanise role}...
diff --git a/variant/standard/command/ping/role-pinged.txt b/variant/standard/command/ping/role-pinged.txt
deleted file mode 100644
--- a/variant/standard/command/ping/role-pinged.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Waiting on the #{humanise role}...
diff --git a/variant/standard/command/status/current-diurnal-turn.txt b/variant/standard/command/status/current-diurnal-turn.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/command/status/current-diurnal-turn.txt
@@ -0,0 +1,1 @@
+It's currently the #{humanise $ game ^. stage}.
diff --git a/variant/standard/command/status/current-nocturnal-turn.txt b/variant/standard/command/status/current-nocturnal-turn.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/command/status/current-nocturnal-turn.txt
@@ -0,0 +1,1 @@
+It's currently the #{humanise $ game ^. stage}.
diff --git a/variant/standard/command/status/current-turn.txt b/variant/standard/command/status/current-turn.txt
deleted file mode 100644
--- a/variant/standard/command/status/current-turn.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-It's currently the #{humanise $ game ^. stage}.
diff --git a/variant/standard/engine/game-over/dullahan-won.txt b/variant/standard/engine/game-over/dullahan-won.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/engine/game-over/dullahan-won.txt
@@ -0,0 +1,2 @@
+The roads are owned by the #{humanise dullahanRole}. Foolishly #{humanise $ game ^. marks} tried to cross them, but to no avail.
+The game is over! The #{humanise dullahanRole} has won.
diff --git a/variant/standard/engine/new-game/dullahan.txt b/variant/standard/engine/new-game/dullahan.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/engine/new-game/dullahan.txt
@@ -0,0 +1,1 @@
+Word has come in the night: #{humanise $ game ^. marks} are marked for death. Fulfil your purpose; ride hard and run them down.
diff --git a/variant/standard/engine/new-game/fallen-angel.txt b/variant/standard/engine/new-game/fallen-angel.txt
deleted file mode 100644
--- a/variant/standard/engine/new-game/fallen-angel.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Alas, again I regrettably yield advice: an angelic menace walks among you. Do not cast your votes lightly, for they will relish in this opportunity to be free from their terrible nightmare.
diff --git a/variant/standard/engine/new-game/spiteful-ghost-private.txt b/variant/standard/engine/new-game/spiteful-ghost-private.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/engine/new-game/spiteful-ghost-private.txt
@@ -0,0 +1,1 @@
+Being ethereal seldom has it's benefits. Perhaps however this knowledge of the townsfolks' natures will bring you some joy in the afterlife: #{humanisePlayersWithRoles $ game ^.. players . traverse . filtered (isn't spitefulGhost)}.
diff --git a/variant/standard/engine/new-game/spiteful-ghost-public.txt b/variant/standard/engine/new-game/spiteful-ghost-public.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/engine/new-game/spiteful-ghost-public.txt
@@ -0,0 +1,1 @@
+In a rash decision to rid the scourge of Werewolves, the Villagers of Fougères lynched an innocent. A foolish move, as now #{humanise spitefulGhost} is just one further horror that plagues Fougères; a ghost that knows each and every one of the Villagers and their darkest secrets.
diff --git a/variant/standard/engine/new-game/spiteful-ghost.txt b/variant/standard/engine/new-game/spiteful-ghost.txt
deleted file mode 100644
--- a/variant/standard/engine/new-game/spiteful-ghost.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Being ethereal seldom has it's benefits. Perhaps however this knowledge of the townsfolks' natures will bring you some joy in the afterlife: #{humanisePlayersWithRoles $ game ^.. players . traverse . filtered (isn't spitefulGhost)}.
diff --git a/variant/standard/role/dullahan/description.txt b/variant/standard/role/dullahan/description.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/role/dullahan/description.txt
@@ -0,0 +1,1 @@
+Normally the Dullahan carries their head under one arm, however while amongst the Villagers, they ere on the side of caution and rest it in a more traditional place. The Dullahan rides a black horse as dark as night and hunts down travellers in the countryside. Beware if the Dullahan knows your name, for you are then marked for death and you should avoid them at all costs.
diff --git a/variant/standard/role/dullahan/name.txt b/variant/standard/role/dullahan/name.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/role/dullahan/name.txt
@@ -0,0 +1,1 @@
+Dullahan
diff --git a/variant/standard/role/dullahan/rules.txt b/variant/standard/role/dullahan/rules.txt
new file mode 100644
--- /dev/null
+++ b/variant/standard/role/dullahan/rules.txt
@@ -0,0 +1,1 @@
+The Dullahan is given a list of player names at the start of the game. They must eliminate all of them before the end of the game.
diff --git a/werewolf.cabal b/werewolf.cabal
--- a/werewolf.cabal
+++ b/werewolf.cabal
@@ -1,5 +1,5 @@
 name:           werewolf
-version:        1.2.0.3
+version:        1.3.0.0
 
 author:         Henry J. Wylde
 maintainer:     public@hjwylde.com
@@ -21,10 +21,22 @@
 extra-source-files:
     CHANGELOG.md
     README.md
-    variant/no-role-knowledge/command/ping/role-pinged.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-turn.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/command/choose/player-shot.txt
+    variant/no-role-reveal/command/ping/nocturnal-role-pinged.txt
+    variant/no-role-reveal/command/ping/werewolves-pinged.txt
+    variant/no-role-reveal/command/quit/caller-quit.txt
+    variant/no-role-reveal/command/status/current-nocturnal-turn.txt
+    variant/no-role-reveal/command/status/dead-players.txt
+    variant/no-role-reveal/engine/general/player-booted.txt
+    variant/no-role-reveal/engine/lynching/player-lynched.txt
+    variant/no-role-reveal/engine/lynching/werewolf-lynched.txt
+    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/standard/command/boot/player-voted-boot.txt
     variant/standard/command/choose/player-shot.txt
     variant/standard/command/circle/game-circle.txt
@@ -58,13 +70,15 @@
     variant/standard/command/help/win-condition.txt
     variant/standard/command/help/witch-commands.txt
     variant/standard/command/help/witchs-turn.txt
+    variant/standard/command/ping/diurnal-role-pinged.txt
+    variant/standard/command/ping/nocturnal-role-pinged.txt
     variant/standard/command/ping/player-pinged.txt
-    variant/standard/command/ping/role-pinged.txt
     variant/standard/command/ping/village-pinged.txt
     variant/standard/command/ping/werewolves-pinged.txt
     variant/standard/command/quit/caller-quit.txt
     variant/standard/command/status/alive-players.txt
-    variant/standard/command/status/current-turn.txt
+    variant/standard/command/status/current-diurnal-turn.txt
+    variant/standard/command/status/current-nocturnal-turn.txt
     variant/standard/command/status/dead-players.txt
     variant/standard/command/status/game-over.txt
     variant/standard/command/unvote/player-rescinded-vote.txt
@@ -73,6 +87,7 @@
     variant/standard/command/vote/player-made-lynch-vote.txt
     variant/standard/engine/druids-turn/start.txt
     variant/standard/engine/game-over/allegiance-won.txt
+    variant/standard/engine/game-over/dullahan-won.txt
     variant/standard/engine/game-over/fallen-angel-won.txt
     variant/standard/engine/game-over/player-contributed.txt
     variant/standard/engine/game-over/player-lost.txt
@@ -88,11 +103,12 @@
     variant/standard/engine/lynching/scapegoat-lynched.txt
     variant/standard/engine/lynching/werewolf-lynched.txt
     variant/standard/engine/new-game/beholder.txt
-    variant/standard/engine/new-game/fallen-angel.txt
+    variant/standard/engine/new-game/dullahan.txt
     variant/standard/engine/new-game/new-player.txt
     variant/standard/engine/new-game/players-in-game.txt
     variant/standard/engine/new-game/roles-in-game.txt
-    variant/standard/engine/new-game/spiteful-ghost.txt
+    variant/standard/engine/new-game/spiteful-ghost-private.txt
+    variant/standard/engine/new-game/spiteful-ghost-public.txt
     variant/standard/engine/new-game/true-villager.txt
     variant/standard/engine/oracles-turn/start-private.txt
     variant/standard/engine/oracles-turn/start-public.txt
@@ -161,6 +177,9 @@
     variant/standard/role/druid/description.txt
     variant/standard/role/druid/name.txt
     variant/standard/role/druid/rules.txt
+    variant/standard/role/dullahan/description.txt
+    variant/standard/role/dullahan/name.txt
+    variant/standard/role/dullahan/rules.txt
     variant/standard/role/fallen-angel/description.txt
     variant/standard/role/fallen-angel/name.txt
     variant/standard/role/fallen-angel/rules.txt
@@ -239,6 +258,8 @@
         Game.Werewolf.Util
         Game.Werewolf.Variant.NoRoleKnowledge.Command
         Game.Werewolf.Variant.NoRoleKnowledge.Engine
+        Game.Werewolf.Variant.NoRoleReveal.Command
+        Game.Werewolf.Variant.NoRoleReveal.Engine
         Game.Werewolf.Variant.Standard.Command
         Game.Werewolf.Variant.Standard.Engine
         Game.Werewolf.Variant.Standard.Error
