module Game.LambdaHack.Action where
import Control.Monad
import Control.Monad.State hiding (State, state)
import qualified Data.IntMap as IM
import qualified Data.Map as M
import Data.Maybe
import Game.LambdaHack.Perception
import Game.LambdaHack.Display
import Game.LambdaHack.Msg
import Game.LambdaHack.State
import Game.LambdaHack.Level
import Game.LambdaHack.Actor
import Game.LambdaHack.ActorState
import Game.LambdaHack.Content.ActorKind
import qualified Game.LambdaHack.Save as Save
import qualified Game.LambdaHack.Kind as Kind
import Game.LambdaHack.Random
import qualified Game.LambdaHack.Key as K
import Game.LambdaHack.Binding
data Session = Session
{ sfs :: FrontendSession
, scops :: Kind.COps
, skeyb :: Binding (Action ())
}
type ActionFun r a =
Session
-> (State -> Diary -> IO r)
-> Perception
-> (State -> Diary -> a -> IO r)
-> IO r
-> State
-> Diary
-> IO r
newtype Action a = Action
{ runAction :: forall r . ActionFun r a
}
instance Show (Action a) where
show _ = "an action"
instance Monad Action where
return = returnAction
(>>=) = bindAction
instance Functor Action where
fmap f (Action g) = Action (\ s e p k a st ms ->
let k' st' ms' = k st' ms' . f
in g s e p k' a st ms)
returnAction :: a -> Action a
returnAction x = Action (\ _s _e _p k _a st m -> k st m x)
bindAction :: Action a -> (a -> Action b) -> Action b
bindAction m f = Action (\ s e p k a st ms ->
let next nst nm x =
runAction (f x) s e p k a nst nm
in runAction m s e p next a st ms)
instance MonadIO Action where
liftIO x = Action (\ _s _e _p k _a st ms -> x >>= k st ms)
instance MonadState State Action where
get = Action (\ _s _e _p k _a st ms -> k st ms st)
put nst = Action (\ _s _e _p k _a _st ms -> k nst ms ())
handlerToIO :: Session -> State -> Diary -> Action () -> IO ()
handlerToIO sess@Session{sfs, scops} state diary h =
runAction h
sess
(\ ns ndiary -> Save.rmBkpSaveDiary ns ndiary
>> shutdown sfs)
(perception scops state)
(\ _ _ x -> return x)
(ioError $ userError "unhandled abort")
state
diary
rndToAction :: Rnd a -> Action a
rndToAction r = do
g <- gets srandom
let (a, ng) = runState r g
modify (\ state -> state {srandom = ng})
return a
session :: (Session -> Action a) -> Action a
session f = Action (\ sess e p k a st ms ->
runAction (f sess) sess e p k a st ms)
sessionIO :: (Session -> IO a) -> Action a
sessionIO f = Action (\ sess _e _p k _a st ms -> f sess >>= k st ms)
displayGeneric :: ColorMode -> (Msg -> Msg) -> Action Bool
displayGeneric dm f =
Action (\ Session{sfs, scops} _e p k _a st ms ->
displayLevel dm sfs scops p st (f (smsg ms)) Nothing
>>= k st ms)
displayAll :: Action Bool
displayAll = displayGeneric ColorFull id
overlay :: String -> Action Bool
overlay txt =
Action (\ Session{sfs, scops} _e p k _a st ms ->
displayLevel ColorFull sfs scops p st (smsg ms) (Just txt)
>>= k st ms)
currentDiary :: Action Diary
currentDiary = Action (\ _s _e _p k _a st diary -> k st diary diary)
diaryReset :: Diary -> Action ()
diaryReset ndiary = Action (\ _s _e _p k _a st _diary -> k st ndiary ())
currentMsg :: Action Msg
currentMsg = Action (\ _s _e _p k _a st ms -> k st ms (smsg ms))
msgReset :: Msg -> Action ()
msgReset nm = Action (\ _s _e _p k _a st ms -> k st ms{smsg = nm} ())
msgAdd :: Msg -> Action ()
msgAdd nm = Action (\ _s _e _p k _a st ms ->
k st ms{smsg = addMsg (smsg ms) nm} ())
msgClear :: Action ()
msgClear = Action (\ _s _e _p k _a st ms -> k st ms{smsg = ""} ())
contentOps :: Action Kind.COps
contentOps = Action (\ Session{scops} _e _p k _a st ms -> k st ms scops)
contentf :: (Kind.COps -> a) -> Action a
contentf f = Action (\ Session{scops} _e _p k _a st ms -> k st ms (f scops))
end :: Action ()
end = Action (\ _s e _p _k _a s diary -> e s diary)
abort :: Action a
abort = Action (\ _s _e _p _k a _st _ms -> a)
tryWith :: Action () -> Action () -> Action ()
tryWith exc h = Action (\ s e p k a st ms ->
let runA = runAction exc s e p k a st ms
in runAction h s e p k runA st ms)
tryRepeatedlyWith :: Action () -> Action () -> Action ()
tryRepeatedlyWith exc h = tryWith (exc >> tryRepeatedlyWith exc h) h
try :: Action () -> Action ()
try = tryWith (return ())
tryRepeatedly :: Action () -> Action ()
tryRepeatedly = tryRepeatedlyWith (return ())
debug :: String -> Action ()
debug _x = return ()
abortWith :: Msg -> Action a
abortWith msg = do
msgReset msg
displayAll
abort
abortIfWith :: Bool -> Msg -> Action a
abortIfWith True msg = abortWith msg
abortIfWith False _ = abortWith ""
neverMind :: Bool -> Action a
neverMind b = abortIfWith b "never mind"
nextCommand :: Session -> Action K.Key
nextCommand Session{sfs, skeyb} = do
nc <- liftIO $ nextEvent sfs
return $ fromMaybe nc $ M.lookup nc $ kmacro skeyb
getYesNo :: Session -> Action Bool
getYesNo sess@Session{sfs} = do
e <- liftIO $ nextEvent sfs
case e of
K.Char 'y' -> return True
K.Char 'n' -> return False
K.Esc -> return False
_ -> getYesNo sess
getOptionalConfirm :: (Bool -> Action a)
-> (K.Key -> Action a)
-> Session
-> Action a
getOptionalConfirm h k Session{sfs} = do
e <- liftIO $ nextEvent sfs
case e of
K.Space -> h True
K.Esc -> h False
_ -> k e
getConfirm :: Session -> Action Bool
getConfirm Session{sfs} = liftIO $ getConfirmD sfs
msgMoreConfirm :: ColorMode -> Msg -> Action Bool
msgMoreConfirm dm msg = do
msgAdd (msg ++ more)
displayGeneric dm id
session getConfirm
msgMore :: Msg -> Action ()
msgMore msg = msgClear >> msgMoreConfirm ColorFull msg >> return ()
msgYesNo :: Msg -> Action Bool
msgYesNo msg = do
msgReset (msg ++ yesno)
displayGeneric ColorBW id
session getYesNo
clearDisplay :: Action Bool
clearDisplay = do
msgClear
displayAll
return False
msgOverlaysConfirm :: Msg -> [String] -> Action Bool
msgOverlaysConfirm _msg [] = return True
msgOverlaysConfirm msg [x] = do
msgReset msg
b0 <- overlay (x ++ msgEnd)
if b0
then return True
else clearDisplay
msgOverlaysConfirm msg (x:xs) = do
msgReset msg
b0 <- overlay (x ++ more)
if b0
then do
b <- session getConfirm
if b
then msgOverlaysConfirm msg xs
else clearDisplay
else clearDisplay
withPerception :: Action () -> Action ()
withPerception h = Action (\ sess@Session{scops} e _ k a st ms ->
runAction h sess e (perception scops st) k a st ms)
currentPerception :: Action Perception
currentPerception = Action (\ _s _e p k _a st ms -> k st ms p)
updateAnyActor :: ActorId -> (Actor -> Actor) -> Action ()
updateAnyActor actor f = modify (updateAnyActorBody actor f)
updatePlayerBody :: (Actor -> Actor) -> Action ()
updatePlayerBody f = do
pl <- gets splayer
updateAnyActor pl f
advanceTime :: ActorId -> Action ()
advanceTime actor = do
Kind.Ops{okind} <- contentf Kind.coactor
time <- gets stime
let upd m = m { btime = time + aspeed (okind (bkind m)) }
pl <- gets splayer
if actor == pl || isAHero actor
then do
modify (updateLevel (updateHeroes (IM.map upd)))
unless (isAHero pl) $ updatePlayerBody upd
else do
s <- get
when (memActor actor s) $ updateAnyActor actor upd
playerAdvanceTime :: Action ()
playerAdvanceTime = do
pl <- gets splayer
advanceTime pl
displayHelp :: Action ()
displayHelp = do
let disp Session{skeyb} =
msgOverlaysConfirm "Basic keys. [press SPACE or ESC]" $ keyHelp skeyb
session disp
abort