implement quest completion, Builder's Hall, and misc. specialized actions
This commit is contained in:
parent
2a7fe3abea
commit
17a40c68bf
|
|
@ -1,3 +1,4 @@
|
||||||
|
#! /usr/bin/runhaskell
|
||||||
{-# LANGUAGE GADTs #-}
|
{-# LANGUAGE GADTs #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
@ -5,7 +6,6 @@
|
||||||
import Waterdeep.Types
|
import Waterdeep.Types
|
||||||
import Waterdeep.Monad
|
import Waterdeep.Monad
|
||||||
import Waterdeep.Logic
|
import Waterdeep.Logic
|
||||||
import Waterdeep.Util (mrepeat)
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
|
@ -41,71 +41,71 @@ p2 = ("Ned", f2, l2)
|
||||||
q1 = Quest { _questType = Arcana
|
q1 = Quest { _questType = Arcana
|
||||||
, _questTitle = "Research Palantirs"
|
, _questTitle = "Research Palantirs"
|
||||||
, _questQuote = ""
|
, _questQuote = ""
|
||||||
, _questAction = ReturnResource [Cleric]
|
, _questAction = ReturnResources 1 [Cleric]
|
||||||
<> ReturnResource [Rogue]
|
<> ReturnResources 1 [Rogue]
|
||||||
<> ReturnResource [Rogue]
|
<> ReturnResources 1 [Rogue]
|
||||||
<> ReturnResource [Wizard]
|
<> ReturnResources 1 [Wizard]
|
||||||
<> mrepeat 4 (ReturnResource [Gold])
|
<> ReturnResources 4 [Gold]
|
||||||
<> ScorePoints 15
|
<> ScorePoints 15
|
||||||
<> mrepeat 8 (TakeResource [Gold])
|
<> TakeResources 8 [Gold]
|
||||||
, _questPlotActions = []
|
, _questPlotActions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
q2 = Quest { _questType = Skullduggery
|
q2 = Quest { _questType = Skullduggery
|
||||||
, _questTitle = "Rob Waterdeep Bank"
|
, _questTitle = "Rob Waterdeep Bank"
|
||||||
, _questQuote = ""
|
, _questQuote = ""
|
||||||
, _questAction = mrepeat 8 (ReturnResource [Rogue])
|
, _questAction = ReturnResources 8 [Rogue]
|
||||||
<> ScorePoints 10
|
<> ScorePoints 10
|
||||||
<> mrepeat 16 (TakeResource [Gold])
|
<> TakeResources 16 [Gold]
|
||||||
, _questPlotActions = []
|
, _questPlotActions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
b1 = Building { _buildingCost = 6
|
b1 = Building { _buildingCost = 6
|
||||||
, _buildingTitle = "Monastary"
|
, _buildingTitle = "Monastary"
|
||||||
, _buildingAction = mrepeat 2 (TakeResource [Cleric])
|
, _buildingAction = TakeResources 2 [Cleric]
|
||||||
, _buildingOwnerAction = TakeResource [Cleric]
|
, _buildingOwnerAction = TakeResources 1 [Cleric]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b2 = Building { _buildingCost = 4
|
b2 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Training Hall"
|
, _buildingTitle = "Training Hall"
|
||||||
, _buildingAction = mrepeat 2 (TakeResource [Fighter])
|
, _buildingAction = TakeResources 2 [Fighter]
|
||||||
, _buildingOwnerAction = TakeResource [Fighter]
|
, _buildingOwnerAction = TakeResources 1 [Fighter]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b3 = Building { _buildingCost = 4
|
b3 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Prison Yard"
|
, _buildingTitle = "Prison Yard"
|
||||||
, _buildingAction = mrepeat 2 (TakeResource [Rogue])
|
, _buildingAction = TakeResources 2 [Rogue]
|
||||||
, _buildingOwnerAction = TakeResource [Rogue]
|
, _buildingOwnerAction = TakeResources 1 [Rogue]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b4 = Building { _buildingCost = 6
|
b4 = Building { _buildingCost = 6
|
||||||
, _buildingTitle = "Wizard School"
|
, _buildingTitle = "Wizard School"
|
||||||
, _buildingAction = mrepeat 2 (TakeResource [Wizard])
|
, _buildingAction = TakeResources 2 [Wizard]
|
||||||
, _buildingOwnerAction = TakeResource [Wizard]
|
, _buildingOwnerAction = TakeResources 1 [Wizard]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b5 = Building { _buildingCost = 4
|
b5 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Gold Mine"
|
, _buildingTitle = "Gold Mine"
|
||||||
, _buildingAction = mrepeat 4 (TakeResource [Gold])
|
, _buildingAction = TakeResources 4 [Gold]
|
||||||
, _buildingOwnerAction = mrepeat 4 (TakeResource [Gold])
|
, _buildingOwnerAction = TakeResources 2 [Gold]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
i1 = IntrigueCard { _intrigueTitle = "Graduation Day"
|
i1 = IntrigueCard { _intrigueTitle = "Graduation Day"
|
||||||
, _intrigueType = Utility
|
, _intrigueType = Utility
|
||||||
, _intrigueAction = mrepeat 2 (TakeResource [Wizard])
|
, _intrigueAction = TakeResources 2 [Wizard]
|
||||||
<> OneOpponent (TakeResource [Wizard])
|
<> OneOpponent (TakeResources 1 [Wizard])
|
||||||
, _intrigueQuote = ""
|
, _intrigueQuote = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
i2 = IntrigueCard { _intrigueTitle = "Call for Adventurers"
|
i2 = IntrigueCard { _intrigueTitle = "Call for Adventurers"
|
||||||
, _intrigueType = Utility
|
, _intrigueType = Utility
|
||||||
, _intrigueAction = mrepeat 2 (TakeResource [Cleric, Fighter, Rogue, Wizard])
|
, _intrigueAction = TakeResources 2 [Cleric, Fighter, Rogue, Wizard]
|
||||||
<> OneOpponent (TakeResource [Cleric, Fighter, Rogue, Wizard])
|
<> OneOpponent (TakeResources 1 [Cleric, Fighter, Rogue, Wizard])
|
||||||
, _intrigueQuote = ""
|
, _intrigueQuote = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import Control.Lens
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Random
|
import Control.Monad.Random
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Control.Monad.Writer
|
import Control.Monad.Trans.Writer
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
|
@ -88,13 +88,16 @@ newBuildingState p b =
|
||||||
|
|
||||||
basicBuildings :: [Building]
|
basicBuildings :: [Building]
|
||||||
basicBuildings =
|
basicBuildings =
|
||||||
[ basicBuilding "Aurora's Realms Shop" (mrepeat 4 $ TakeResource [Gold])
|
[ basicBuilding "Aurora's Realms Shop" (TakeResources 4 [Gold])
|
||||||
, basicBuilding "Blackstaff Tower" (TakeResource [Wizard])
|
, basicBuilding "Blackstaff Tower" (TakeResources 1 [Wizard])
|
||||||
, basicBuilding "Builder's Hall" BuyBuilding
|
, basicBuilding "Builder's Hall" BuyBuilding
|
||||||
, basicBuilding "Castle Waterdeep" (BecomeFirstPlayer `Then` DrawIntrigue)
|
, basicBuilding "Castle Waterdeep" (BecomeFirstPlayer <> DrawIntrigue)
|
||||||
, basicBuilding "Field of Triumph" (mrepeat 2 $ TakeResource [Fighter])
|
, basicBuilding "Field of Triumph" (TakeResources 2 [Fighter])
|
||||||
, basicBuilding "The Grinning Lion Tavern" (mrepeat 2 $ TakeResource [Rogue])
|
, basicBuilding "The Grinning Lion Tavern" (TakeResources 2 [Rogue])
|
||||||
, basicBuilding "The Plinth" (TakeResource [Cleric])
|
, basicBuilding "The Plinth" (TakeResources 1 [Cleric])
|
||||||
|
, basicBuilding "Cliffwatch Inn (2 Gold)" (ChooseQuest <> TakeResources 2 [Gold])
|
||||||
|
, basicBuilding "Cliffwatch Inn (Intrigue)" (ChooseQuest <> DrawIntrigue)
|
||||||
|
, basicBuilding "Cliffwatch Inn (Reset)" (ReplaceQuests <> ChooseQuest)
|
||||||
]
|
]
|
||||||
|
|
||||||
basicBuilding :: String -> GameAction -> Building
|
basicBuilding :: String -> GameAction -> Building
|
||||||
|
|
@ -115,15 +118,19 @@ waterdeepGame = do
|
||||||
forM_ ps $ \p -> do
|
forM_ ps $ \p -> do
|
||||||
replicateM_ 2 $ performAction p DrawQuest
|
replicateM_ 2 $ performAction p DrawQuest
|
||||||
replicateM_ 2 $ performAction p DrawIntrigue
|
replicateM_ 2 $ performAction p DrawIntrigue
|
||||||
replicateM_ (3 + p) $ performAction p $ TakeResource [Gold]
|
performAction p $ TakeResources (3 + p) [Gold]
|
||||||
forM_ [1..8] $ \round -> do
|
forM_ [1..8] $ \round -> do
|
||||||
beginRound round
|
beginRound round
|
||||||
|
-- TODO: Assign ambassador (if in play)
|
||||||
fix $ \loop -> do
|
fix $ \loop -> do
|
||||||
p <- use gameCurrentPlayer
|
p <- use gameCurrentPlayer
|
||||||
success <- filterChoices $ performAction p AssignAgent
|
success <- filterChoices $ performAction p AssignAgent
|
||||||
case success of
|
case success of
|
||||||
True -> do
|
True -> do
|
||||||
filterChoices $ performAction p CompleteQuest
|
filterChoices $ join $ solicitChoice p "Complete quest?" $
|
||||||
|
[ ("Yes", performAction p CompleteQuest)
|
||||||
|
, ("No", return True)
|
||||||
|
]
|
||||||
gameCurrentPlayer .= (p `mod` length ps) + 1
|
gameCurrentPlayer .= (p `mod` length ps) + 1
|
||||||
gameConsecutivePasses .= 0
|
gameConsecutivePasses .= 0
|
||||||
loop
|
loop
|
||||||
|
|
@ -190,6 +197,31 @@ filterChoices m = do
|
||||||
forwardPrompt (NotifyState w) = get >>= \w0 -> put w >> notifyState >> put w0
|
forwardPrompt (NotifyState w) = get >>= \w0 -> put w >> notifyState >> put w0
|
||||||
forwardPrompt (Broadcast s) = broadcast' s
|
forwardPrompt (Broadcast s) = broadcast' s
|
||||||
|
|
||||||
|
-- Returns True if there exists some sequence of choices
|
||||||
|
-- which would make the action succeed, or False otherwise.
|
||||||
|
canPerformAction :: PlayerID -> GameAction -> Waterdeep Bool
|
||||||
|
canPerformAction p a = do
|
||||||
|
return . tryChoice . runWaterdeepC Done Cont (performAction p a) =<< get
|
||||||
|
where
|
||||||
|
tryChoice :: ContWD Bool -> Bool
|
||||||
|
tryChoice (Done a) = fst a
|
||||||
|
tryChoice (Cont (NotifyState w) cont) = tryChoice $ cont ()
|
||||||
|
tryChoice (Cont (Broadcast s) cont) = tryChoice $ cont ()
|
||||||
|
tryChoice (Cont (SolicitChoice p t cs) cont) =
|
||||||
|
or $ map (tryChoice . cont . snd) cs
|
||||||
|
|
||||||
|
-- Like (<$>), but with the same fixity and precedence as ($)
|
||||||
|
(<$$>) :: Applicative f => (a -> b) -> f a -> f b
|
||||||
|
(<$$>) = (<$>)
|
||||||
|
infixr 0 <$$>
|
||||||
|
|
||||||
|
joinStrings :: [String] -> String
|
||||||
|
joinStrings [] = "nothing"
|
||||||
|
joinStrings [x] = x
|
||||||
|
joinStrings [x,y] = x ++ " and " ++ y
|
||||||
|
joinStrings [x,y,z] = x ++ ", " ++ y ++ ", and " ++ z
|
||||||
|
joinStrings (x:xs) = x ++ ", " ++ joinStrings xs
|
||||||
|
|
||||||
performAction :: PlayerID -> GameAction -> Waterdeep Bool
|
performAction :: PlayerID -> GameAction -> Waterdeep Bool
|
||||||
performAction p a = do
|
performAction p a = do
|
||||||
name <- use $ gamePlayer p . playerName
|
name <- use $ gamePlayer p . playerName
|
||||||
|
|
@ -197,50 +229,218 @@ performAction p a = do
|
||||||
NoAction -> return True
|
NoAction -> return True
|
||||||
ScorePoints n -> do
|
ScorePoints n -> do
|
||||||
gamePlayer p . playerScore += n
|
gamePlayer p . playerScore += n
|
||||||
broadcast $ name ++ " scored " ++ show n ++ "points."
|
broadcast $ name ++ " scored " ++ show n ++ " points."
|
||||||
return True
|
return True
|
||||||
TakeResource rs -> do
|
TakeResources n rs -> do
|
||||||
|
received <- map (head &&& length) . group . sort <$$> replicateM n $ do
|
||||||
r <- solicitChoice p "Take one item:" $ map (show &&& id) rs
|
r <- solicitChoice p "Take one item:" $ map (show &&& id) rs
|
||||||
gamePlayer p . playerTavern %= flip (M.unionWith (+)) (M.fromList [(r, 1)])
|
gamePlayer p . playerTavern %= M.insertWith' (+) r 1
|
||||||
broadcast $ name ++ " received one " ++ show r ++ "."
|
return r
|
||||||
|
let items = joinStrings $ map (\(r,n) -> show n ++ " " ++ show r) received
|
||||||
|
broadcast $ name ++ " received " ++ items ++ "."
|
||||||
return True
|
return True
|
||||||
ReturnResource rs -> do
|
ReturnResources n rs -> do
|
||||||
|
maybeReturned <- replicateM n $ do
|
||||||
tavern <- use $ gamePlayer p . playerTavern
|
tavern <- use $ gamePlayer p . playerTavern
|
||||||
let rs' = filter ((> 0) . maybe 0 id . flip M.lookup tavern) rs
|
let rs' = filter ((>= 1) . maybe 0 id . flip M.lookup tavern) rs
|
||||||
if null rs'
|
if null rs'
|
||||||
then return False
|
then return Nothing
|
||||||
else do
|
else do
|
||||||
r <- solicitChoice p "Return one item:" $ map (show &&& id) rs'
|
r <- solicitChoice p "Return one item:" $ map (show &&& id) rs'
|
||||||
let removeOne x = if x > 1 then Just (x-1) else Nothing
|
let removeOne x = if x > 1 then Just (x-1) else Nothing
|
||||||
gamePlayer p . playerTavern %= M.update removeOne r
|
gamePlayer p . playerTavern %= M.update removeOne r
|
||||||
broadcast $ name ++ " returned one " ++ show r ++ " to the supply."
|
return (Just r)
|
||||||
return True
|
let success = and . map isJust $ maybeReturned
|
||||||
|
let returned = map (head &&& length) . group . catMaybes $ maybeReturned
|
||||||
|
let items = joinStrings $ map (\(r,n) -> show n ++ " " ++ show r) $ returned
|
||||||
|
broadcast $ name ++ " returned " ++ items ++ " to the supply."
|
||||||
|
return success
|
||||||
ChooseQuest -> do
|
ChooseQuest -> do
|
||||||
qs <- use gameCliffwatchInn
|
quests <- use gameCliffwatchInn
|
||||||
if null qs
|
if null quests
|
||||||
then return False
|
then return False
|
||||||
else do
|
else do
|
||||||
let names = qs ^.. traverse . questTitle
|
let titles = quests ^.. traverse . questTitle
|
||||||
q <- solicitChoice p "Please choose a quest:" $ zip names qs
|
i <- solicitChoice p "Choose one quest:" $ zip titles [0..]
|
||||||
gameCliffwatchInn %= (\\ [q])
|
let quest = quests !! i
|
||||||
gamePlayer p . playerIncompleteQuests <>= [q]
|
gameCliffwatchInn %= deleteAt i
|
||||||
broadcast $ name ++ " chose " ++ (q ^. questTitle) ++ " from Cliffwatch Inn."
|
gamePlayer p . playerIncompleteQuests <>= [quest]
|
||||||
|
broadcast $ name ++ " chose " ++ (quest ^. questTitle) ++ " from Cliffwatch Inn."
|
||||||
|
restockCliffwatchInn
|
||||||
|
return True
|
||||||
|
ReplaceQuests -> do
|
||||||
|
quests <- gameCliffwatchInn <<.= []
|
||||||
|
gameQuestDiscard <>= quests
|
||||||
restockCliffwatchInn
|
restockCliffwatchInn
|
||||||
return True
|
return True
|
||||||
DrawQuest -> do
|
DrawQuest -> do
|
||||||
mq <- drawQuest
|
mq <- drawQuest
|
||||||
case mq of
|
case mq of
|
||||||
Nothing -> return False
|
Nothing -> do
|
||||||
|
broadcast $ name ++ " failed to draw a quest because the quest deck is empty."
|
||||||
|
return False
|
||||||
Just q -> do
|
Just q -> do
|
||||||
gamePlayer p . playerIncompleteQuests <>= [q]
|
gamePlayer p . playerIncompleteQuests <>= [q]
|
||||||
broadcast $ name ++ " drew " ++ (q ^. questTitle) ++ " from the quest deck."
|
broadcast $ name ++ " drew " ++ (q ^. questTitle) ++ " from the quest deck."
|
||||||
return True
|
return True
|
||||||
|
DrawNamedQuestType -> do
|
||||||
|
let qtypes = [Piety, Warfare, Skullduggery, Arcana, Commerce]
|
||||||
|
qtype <- solicitChoice p "Choose a quest type:" $ map (show &&& id) qtypes
|
||||||
|
broadcast $ name ++ " chose the " ++ show qtype ++ " quest type."
|
||||||
|
flip fix [] $ \loop discards -> do
|
||||||
|
mq <- drawQuest
|
||||||
|
case mq of
|
||||||
|
Nothing -> do
|
||||||
|
broadcast $ "There were no " ++ show qtype ++ " quests available."
|
||||||
|
gameQuestDiscard <>= discards
|
||||||
|
return False
|
||||||
|
Just q -> do
|
||||||
|
broadcast $ printf "%s drew the %s quest %s."
|
||||||
|
name (show (q ^. questType)) (q ^. questTitle)
|
||||||
|
if (q ^. questType /= qtype)
|
||||||
|
then loop (q : discards) >> return True
|
||||||
|
else do
|
||||||
|
gameQuestDiscard <>= discards
|
||||||
|
gamePlayer p . playerIncompleteQuests <>= [q]
|
||||||
|
return True
|
||||||
|
DistributeQuests -> do
|
||||||
|
np <- IM.size <$> use gamePlayers
|
||||||
|
let loop p' remQuests = do
|
||||||
|
unless (null remQuests) $ do
|
||||||
|
i <- solicitChoice p' "Choose a quest:" $
|
||||||
|
zip (map (view questTitle) remQuests) [0..]
|
||||||
|
gamePlayer p' . playerIncompleteQuests <>= [remQuests !! i]
|
||||||
|
loop ((p' `mod` np) + 1) (deleteAt i remQuests)
|
||||||
|
loop p =<< return . catMaybes =<< replicateM np drawQuest
|
||||||
|
return True
|
||||||
|
CompleteQuest -> do
|
||||||
|
quests <- use (gamePlayer p . playerIncompleteQuests)
|
||||||
|
if null quests
|
||||||
|
then return False
|
||||||
|
else filterChoices $ do
|
||||||
|
let choices = zip (map (view questTitle) quests) [0..]
|
||||||
|
i <- solicitChoice p "Complete one quest:" choices
|
||||||
|
let quest = quests !! i
|
||||||
|
if (quest ^. questType /= Mandatory &&
|
||||||
|
or (map (\q -> q ^. questType == Mandatory) quests))
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
gamePlayer p . playerIncompleteQuests %= deleteAt i
|
||||||
|
success <- performAction p (quest ^. questAction)
|
||||||
|
if null (quest ^. questPlotActions)
|
||||||
|
then gamePlayer p . playerCompletedQuests <>= [quest]
|
||||||
|
else gamePlayer p . playerActivePlotQuests <>= [quest]
|
||||||
|
broadcast $ name ++ " completed the " ++ (quest ^. questTitle) ++ " quest."
|
||||||
|
return success
|
||||||
|
ChooseAndCompleteQuest bonusAction -> do
|
||||||
|
quests <- use gameCliffwatchInn
|
||||||
|
if null quests
|
||||||
|
then return False
|
||||||
|
else filterChoices $ do
|
||||||
|
let titles = quests ^.. traverse . questTitle
|
||||||
|
i <- solicitChoice p "Choose one quest:" $ zip titles [0..]
|
||||||
|
let quest = quests !! i
|
||||||
|
if (quest ^. questType /= Mandatory &&
|
||||||
|
or (map (\q -> q ^. questType == Mandatory) quests))
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
gameCliffwatchInn %= deleteAt i
|
||||||
|
broadcast $ name ++ " chose " ++ (quest ^. questTitle) ++ " from Cliffwatch Inn."
|
||||||
|
restockCliffwatchInn
|
||||||
|
doQuest <- solicitChoice p "Complete this quest immediately?"
|
||||||
|
[("Yes", True), ("No", False)]
|
||||||
|
case doQuest of
|
||||||
|
True -> do
|
||||||
|
questSuccess <- performAction p (quest ^. questAction)
|
||||||
|
bonusSuccess <- performAction p bonusAction
|
||||||
|
if null (quest ^. questPlotActions)
|
||||||
|
then gamePlayer p . playerCompletedQuests <>= [quest]
|
||||||
|
else gamePlayer p . playerActivePlotQuests <>= [quest]
|
||||||
|
broadcast $ name ++ " completed the " ++ (quest ^. questTitle) ++ " quest."
|
||||||
|
return (questSuccess && bonusSuccess)
|
||||||
|
False -> do
|
||||||
|
gamePlayer p . playerIncompleteQuests <>= [quest]
|
||||||
|
return True
|
||||||
|
DiscardUncompletedQuest -> do
|
||||||
|
quests <- use (gamePlayer p . playerIncompleteQuests)
|
||||||
|
if null quests
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
i <- solicitChoice p "Choose a quest to discard:" $
|
||||||
|
zip (map (view questTitle) quests) [0..]
|
||||||
|
let quest = quests !! i
|
||||||
|
gamePlayer p . playerIncompleteQuests %= deleteAt i
|
||||||
|
gameQuestDiscard <>= [quest]
|
||||||
|
broadcast $ name ++ " discarded the " ++ (quest ^. questTitle) ++ " quest."
|
||||||
|
return True
|
||||||
|
BuyBuilding -> do
|
||||||
|
buildings <- use gameBuildersHall
|
||||||
|
if null buildings
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
let labels = flip map buildings $ \b -> printf "%s (%d Gold, %d Points)"
|
||||||
|
(b ^. building . buildingTitle)
|
||||||
|
(b ^. building . buildingCost)
|
||||||
|
(b ^. buildingAccumulation)
|
||||||
|
filterChoices $ do
|
||||||
|
i <- solicitChoice p "Choose a building from Builder's Hall:" $
|
||||||
|
zip labels [0..]
|
||||||
|
let b = buildings !! i
|
||||||
|
let cost = b ^. building . buildingCost
|
||||||
|
paid <- performAction p $ ReturnResources cost [Gold]
|
||||||
|
performAction p $ ScorePoints (b ^. buildingAccumulation)
|
||||||
|
gameBuildersHall %= deleteAt i
|
||||||
|
gameBuildings <>= [b & buildingOwner .~ p & buildingAccumulation .~ 0]
|
||||||
|
broadcast $ name ++ " built the " ++ (b ^. building . buildingTitle) ++ "."
|
||||||
|
restockBuildersHall
|
||||||
|
return paid
|
||||||
|
ChooseFreeBuilding -> do
|
||||||
|
buildings <- use gameBuildersHall
|
||||||
|
if null buildings
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
let labels = flip map buildings $ \b -> printf "%s (%d Points)"
|
||||||
|
(b ^. building . buildingTitle)
|
||||||
|
(b ^. buildingAccumulation)
|
||||||
|
i <- solicitChoice p "Choose a building from Builder's Hall:" $
|
||||||
|
zip labels [0..]
|
||||||
|
let b = buildings !! i
|
||||||
|
performAction p $ ScorePoints (b ^. buildingAccumulation)
|
||||||
|
gameBuildersHall %= deleteAt i
|
||||||
|
gameBuildings <>= [b & buildingOwner .~ p & buildingAccumulation .~ 0]
|
||||||
|
broadcast $ name ++ " built the " ++ (b ^. building . buildingTitle) ++ " for free."
|
||||||
|
restockBuildersHall
|
||||||
|
return True
|
||||||
|
DrawFreeBuilding -> do
|
||||||
|
mb <- drawBuilding
|
||||||
|
case mb of
|
||||||
|
Nothing -> return False
|
||||||
|
Just b -> do
|
||||||
|
gameBuildings <>= [newBuildingState p b]
|
||||||
|
broadcast $ name ++ " built the " ++ (b ^. buildingTitle) ++ " for free."
|
||||||
|
return True
|
||||||
|
DiscardUnoccupiedBuilding -> do
|
||||||
|
allBuildings <- use gameBuildings
|
||||||
|
let choices = flip mapMaybe (zip allBuildings [0..]) $ \(b, i) ->
|
||||||
|
case b ^. buildingOwner == p of
|
||||||
|
True -> Just (b ^. building . buildingTitle, i)
|
||||||
|
False -> Nothing
|
||||||
|
if null choices
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
i <- solicitChoice p "Choose a building to discard:" choices
|
||||||
|
let b = allBuildings !! i
|
||||||
|
gameBuildings %= deleteAt i
|
||||||
|
gameBuildingDiscard <>= [b ^. building]
|
||||||
|
broadcast $ name ++ " discarded the " ++ (b ^. building . buildingTitle) ++ "."
|
||||||
|
return True
|
||||||
DrawIntrigue -> do
|
DrawIntrigue -> do
|
||||||
mi <- drawIntrigue
|
mi <- drawIntrigue
|
||||||
case mi of
|
case mi of
|
||||||
Nothing -> return False
|
Nothing -> return False
|
||||||
Just i -> do
|
Just i -> do
|
||||||
gamePlayer p . playerIntrigueCards <>= [i]
|
gamePlayer p . playerIntrigueCards <>= [i]
|
||||||
|
broadcast $ name ++ " drew an Intrigue card."
|
||||||
return True
|
return True
|
||||||
PlayIntrigue -> do
|
PlayIntrigue -> do
|
||||||
intrigues <- use (gamePlayer p . playerIntrigueCards)
|
intrigues <- use (gamePlayer p . playerIntrigueCards)
|
||||||
|
|
@ -250,15 +450,20 @@ performAction p a = do
|
||||||
let sameTitle a b = fst a == fst b
|
let sameTitle a b = fst a == fst b
|
||||||
let choices = nubBy sameTitle $ zip (map (view intrigueTitle) intrigues) [0..]
|
let choices = nubBy sameTitle $ zip (map (view intrigueTitle) intrigues) [0..]
|
||||||
i <- solicitChoice p "Play one intrigue card:" choices
|
i <- solicitChoice p "Play one intrigue card:" choices
|
||||||
let deleteAt n l = (take n l) ++ (drop (n + 1) l)
|
let intrigue = intrigues !! i
|
||||||
|
filterChoices $ do
|
||||||
gamePlayer p . playerIntrigueCards %= deleteAt i
|
gamePlayer p . playerIntrigueCards %= deleteAt i
|
||||||
performAction p (intrigues ^. singular (ix i) . intrigueAction)
|
broadcast $ name ++ " played the " ++ (intrigue ^. intrigueTitle) ++ " Intrigue card."
|
||||||
|
success <- performAction p (intrigue ^. intrigueAction)
|
||||||
|
gameIntrigueDiscard <>= [intrigue]
|
||||||
|
return success
|
||||||
|
ReturnAgent -> return False -- TODO
|
||||||
|
ReturnAgentFromHarbor -> return False -- TODO
|
||||||
AssignAgent -> do
|
AssignAgent -> do
|
||||||
agents <- use (gamePlayer p . playerAgentsInPool)
|
agents <- use (gamePlayer p . playerAgentsInPool)
|
||||||
if agents <= 0
|
if agents <= 0
|
||||||
then return False
|
then return False
|
||||||
else do
|
else do
|
||||||
w <- get
|
|
||||||
let assignToBuilding :: Lens' WaterdeepState BuildingState -> Waterdeep Bool
|
let assignToBuilding :: Lens' WaterdeepState BuildingState -> Waterdeep Bool
|
||||||
assignToBuilding bl = do
|
assignToBuilding bl = do
|
||||||
gamePlayer p . playerAgentsInPool -= 1
|
gamePlayer p . playerAgentsInPool -= 1
|
||||||
|
|
@ -266,7 +471,7 @@ performAction p a = do
|
||||||
bName <- use (bl . building . buildingTitle)
|
bName <- use (bl . building . buildingTitle)
|
||||||
broadcast $ name ++ " assigned an agent to " ++ bName ++ "."
|
broadcast $ name ++ " assigned an agent to " ++ bName ++ "."
|
||||||
owner <- use (bl . buildingOwner)
|
owner <- use (bl . buildingOwner)
|
||||||
when (owner /= noPlayerID) $
|
when (owner `notElem` [noPlayerID, p]) $
|
||||||
void . performAction owner =<< use (bl . building . buildingOwnerAction)
|
void . performAction owner =<< use (bl . building . buildingOwnerAction)
|
||||||
performAction p =<< use (bl . building . buildingAction)
|
performAction p =<< use (bl . building . buildingAction)
|
||||||
let assignToHarbor l = do
|
let assignToHarbor l = do
|
||||||
|
|
@ -274,8 +479,8 @@ performAction p a = do
|
||||||
gameWaterdeepHarbor . l <>= [p]
|
gameWaterdeepHarbor . l <>= [p]
|
||||||
broadcast $ name ++ " assigned an agent to Waterdeep Harbor."
|
broadcast $ name ++ " assigned an agent to Waterdeep Harbor."
|
||||||
performAction p PlayIntrigue
|
performAction p PlayIntrigue
|
||||||
let findBuildings :: Writer [(String, Waterdeep Bool)] ()
|
buildings <- execWriterT $ do
|
||||||
findBuildings = do
|
w <- get
|
||||||
forM_ [0 .. length (w ^. gameBuildings) - 1] $ \i -> do
|
forM_ [0 .. length (w ^. gameBuildings) - 1] $ \i -> do
|
||||||
let l :: Lens' WaterdeepState BuildingState
|
let l :: Lens' WaterdeepState BuildingState
|
||||||
l = gameBuildings . singular (ix i)
|
l = gameBuildings . singular (ix i)
|
||||||
|
|
@ -286,17 +491,45 @@ performAction p a = do
|
||||||
(_, [], _) -> tell [("Waterdeep Harbor", assignToHarbor _2)]
|
(_, [], _) -> tell [("Waterdeep Harbor", assignToHarbor _2)]
|
||||||
(_, _, []) -> tell [("Waterdeep Harbor", assignToHarbor _3)]
|
(_, _, []) -> tell [("Waterdeep Harbor", assignToHarbor _3)]
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
let buildings = execWriter findBuildings
|
|
||||||
if null buildings
|
if null buildings
|
||||||
then return False
|
then return False
|
||||||
else do
|
else do
|
||||||
join $ solicitChoice p "Assign one agent to:" buildings
|
join $ solicitChoice p "Assign one agent to:" buildings
|
||||||
Optional a1 -> do
|
AssignAgentToBuildersHall -> return False -- TODO
|
||||||
join $ solicitChoice p "Perform action?" $
|
AssignAgentToOpponentsSpace -> return False -- TODO
|
||||||
[ ("Yes", performAction p a1)
|
UseOpponentsSpace -> return False -- TODO
|
||||||
, ("No", return False)]
|
GainLieutenant -> do
|
||||||
|
gamePlayer p . playerHasLieutenant .= True
|
||||||
|
gamePlayer p . playerAgentsInPool += 1
|
||||||
|
broadcast $ name ++ " gained the Lieutenant."
|
||||||
|
return True
|
||||||
|
GainAmbassador -> do
|
||||||
|
unavail <- or . toListOf (traverse . playerHasAmbassador) <$> use gamePlayers
|
||||||
|
if unavail
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
gameBuildings . each . buildingAgents %= (\\ [noPlayerID])
|
||||||
|
gameWaterdeepHarbor . each %= (\\ [noPlayerID])
|
||||||
|
gamePlayer p . playerHasAmbassador .= True
|
||||||
|
broadcast $ name ++ " gained the Ambassador."
|
||||||
|
return True
|
||||||
|
TakeAccumulated ->
|
||||||
|
return True
|
||||||
|
AssignMandatoryQuest quest -> do
|
||||||
|
players <- toListOf traverse <$> use gamePlayers
|
||||||
|
let opponents = filter (\player -> (player ^. playerNumber) /= p) players
|
||||||
|
let choices = map (\p -> (p ^. playerName, p ^. playerNumber)) opponents
|
||||||
|
opID <- solicitChoice p "Choose one opponent:" choices
|
||||||
|
opName <- use (gamePlayer opID . playerName)
|
||||||
|
gamePlayer opID . playerIncompleteQuests <>= [quest]
|
||||||
|
broadcast $ name ++ " assigned " ++ opName ++ " a mandatory quest."
|
||||||
|
return True
|
||||||
|
BecomeFirstPlayer -> do
|
||||||
|
gameFirstPlayer .= p
|
||||||
|
broadcast $ name ++ " is now the first player."
|
||||||
|
return True
|
||||||
ChooseOne as -> do
|
ChooseOne as -> do
|
||||||
a1 <- solicitChoice p "Choose an action:" $ map (show &&& id) as
|
a1 <- solicitChoice p "Choose one:" $ map (show &&& id) as
|
||||||
performAction p a1
|
performAction p a1
|
||||||
a1 `Then` a2 -> do
|
a1 `Then` a2 -> do
|
||||||
(&&) <$> performAction p a1 <*> performAction p a2
|
(&&) <$> performAction p a1 <*> performAction p a2
|
||||||
|
|
@ -327,7 +560,6 @@ performAction p a = do
|
||||||
return True
|
return True
|
||||||
CurrentPlayer a1 ->
|
CurrentPlayer a1 ->
|
||||||
flip performAction a1 =<< use gameCurrentPlayer
|
flip performAction a1 =<< use gameCurrentPlayer
|
||||||
_ -> return False
|
|
||||||
|
|
||||||
scoreFinalPoints :: Waterdeep ()
|
scoreFinalPoints :: Waterdeep ()
|
||||||
scoreFinalPoints = do
|
scoreFinalPoints = do
|
||||||
|
|
|
||||||
|
|
@ -193,16 +193,16 @@ data AccumulationType = NoAccumulation
|
||||||
|
|
||||||
data GameAction = NoAction
|
data GameAction = NoAction
|
||||||
| ScorePoints Int
|
| ScorePoints Int
|
||||||
| TakeResource [Resource]
|
| TakeResources Int [Resource]
|
||||||
| ReturnResource [Resource]
|
| ReturnResources Int [Resource]
|
||||||
| GiveResource [Resource]
|
| GiveResources Int [Resource]
|
||||||
| ChooseQuest
|
| ChooseQuest
|
||||||
| ReplaceQuests
|
| ReplaceQuests
|
||||||
| DrawQuest
|
| DrawQuest
|
||||||
| DrawNamedQuestType
|
| DrawNamedQuestType
|
||||||
| DistributeQuests
|
| DistributeQuests
|
||||||
| CompleteQuest
|
| CompleteQuest
|
||||||
| ChooseAndCompleteQuest
|
| ChooseAndCompleteQuest GameAction
|
||||||
| DiscardUncompletedQuest
|
| DiscardUncompletedQuest
|
||||||
| BuyBuilding
|
| BuyBuilding
|
||||||
| ChooseFreeBuilding
|
| ChooseFreeBuilding
|
||||||
|
|
@ -219,10 +219,8 @@ data GameAction = NoAction
|
||||||
| GainLieutenant
|
| GainLieutenant
|
||||||
| GainAmbassador
|
| GainAmbassador
|
||||||
| TakeAccumulated
|
| TakeAccumulated
|
||||||
| TakeResourceFromOpponent [Resource]
|
|
||||||
| AssignMandatoryQuest Quest
|
| AssignMandatoryQuest Quest
|
||||||
| BecomeFirstPlayer
|
| BecomeFirstPlayer
|
||||||
| Optional GameAction
|
|
||||||
| ChooseOne [GameAction]
|
| ChooseOne [GameAction]
|
||||||
| Then GameAction GameAction
|
| Then GameAction GameAction
|
||||||
| OrElse GameAction GameAction
|
| OrElse GameAction GameAction
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
module Waterdeep.Util
|
module Waterdeep.Util
|
||||||
( mrepeat
|
( mrepeat
|
||||||
|
, deleteAt
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
|
||||||
mrepeat :: (Monoid m) => Int -> m -> m
|
mrepeat :: (Monoid m) => Int -> m -> m
|
||||||
mrepeat n m = mconcat $ replicate n m
|
mrepeat n m = mconcat $ replicate n m
|
||||||
|
|
||||||
|
deleteAt :: Int -> [a] -> [a]
|
||||||
|
deleteAt n l = (take n l) ++ (drop (n + 1) l)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue