-- | Game state reading monad and basic operations. module Game.LambdaHack.Common.MonadStateRead ( MonadStateRead(..) , getState, getLevel , getGameMode, isNoConfirmsGame, getEntryArena, pickWeaponM, displayTaunt ) where import Prelude () import Game.LambdaHack.Core.Prelude import qualified Data.EnumMap.Strict as EM import Data.Text (Text) import qualified Game.LambdaHack.Definition.Ability as Ability import Game.LambdaHack.Common.Actor import Game.LambdaHack.Common.ActorState import Game.LambdaHack.Common.Faction import Game.LambdaHack.Core.Frequency import Game.LambdaHack.Common.Item import Game.LambdaHack.Common.Kind import Game.LambdaHack.Common.Level import Game.LambdaHack.Common.Types import Game.LambdaHack.Core.Random import Game.LambdaHack.Common.ReqFailure import Game.LambdaHack.Common.State import Game.LambdaHack.Content.ModeKind -- | Monad for reading game state. A state monad with state modification -- disallowed (another constraint is needed to permit that). -- The basic server and client monads are like that, because server -- and clients freely modify their internal session data, but don't modify -- the main game state, except in very restricted and synchronized way. class (Monad m, Functor m, Applicative m) => MonadStateRead m where getsState :: (State -> a) -> m a getState :: MonadStateRead m => m State getState = getsState id getLevel :: MonadStateRead m => LevelId -> m Level getLevel lid = getsState $ (EM.! lid) . sdungeon getGameMode :: MonadStateRead m => m ModeKind getGameMode = do COps{comode} <- getsState scops t <- getsState sgameModeId return $! okind comode t isNoConfirmsGame :: MonadStateRead m => m Bool isNoConfirmsGame = do gameMode <- getGameMode return $! maybe False (> 0) $ lookup "no confirms" $ mfreq gameMode getEntryArena :: MonadStateRead m => Faction -> m LevelId getEntryArena fact = do dungeon <- getsState sdungeon let (minD, maxD) = dungeonBounds dungeon f [] = 0 f ((ln, _, _) : _) = ln return $! max minD $ min maxD $ toEnum $ f $ ginitial fact pickWeaponM :: MonadStateRead m => Bool -> Maybe DiscoveryBenefit -> [(ItemId, ItemFullKit)] -> Ability.Skills -> ActorId -> m [(Double, (Int, (ItemId, ItemFullKit)))] pickWeaponM ignoreCharges mdiscoBenefit kitAss actorSk source = do sb <- getsState $ getActorBody source localTime <- getsState $ getLocalTime (blid sb) actorMaxSk <- getsState $ getActorMaxSkills source let calmE = calmEnough sb actorMaxSk forced = bproj sb permitted = permittedPrecious forced calmE preferredPrecious = either (const False) id . permitted permAssocs = filter (preferredPrecious . fst . snd) kitAss strongest = strongestMelee ignoreCharges mdiscoBenefit localTime permAssocs return $! if | forced -> map (\ii -> (1, (1, ii))) kitAss | Ability.getSk Ability.SkMelee actorSk <= 0 -> [] | otherwise -> strongest displayTaunt :: MonadStateRead m => Bool -> (Rnd (Text, Text) -> m (Text, Text)) -> ActorId -> m (Text, Text) displayTaunt _voluntary rndToAction aid = do b <- getsState $ getActorBody aid actorMaxSk <- getsState $ getActorMaxSkills aid let canApply = Ability.getSk Ability.SkApply actorMaxSk > 2 && canHear -- if applies complex items, probably intelligent and can speak canHear = Ability.getSk Ability.SkHearing actorMaxSk > 0 && canBrace -- if hears, probably also emits sound vocally; -- disabled even by ushanka and rightly so canBrace = Ability.getSk Ability.SkWait actorMaxSk >= 2 -- not an insect, plant, geyser, faucet, fence, etc. -- so can emit sound by hitting something with body parts || Ability.getSk Ability.SkApply actorMaxSk > 2 -- and neither an impatient intelligent actor braceUneasy = [ (2, ("something", "flail around")) , (1, ("something", "toss blindly")) , (1, ("something", "squirm dizzily")) ] braceEasy = [ (2, ("something", "stretch")) , (1, ("something", "fidget")) , (1, ("something", "fret")) ] uneasy = deltasSerious (bcalmDelta b) || not (calmEnough b actorMaxSk) if bwatch b `elem` [WSleep, WWake] then rndToAction $ frequency $ toFreq "SfxTaunt" $ if uneasy then if | canApply -> (5, ("somebody", "yell")) : (3, ("somebody", "bellow")) : braceUneasy | canHear -> (5, ("somebody", "bellow")) : (3, ("something", "hiss")) : braceUneasy | canBrace -> braceUneasy | otherwise -> [(1, ("something", "drone enquiringly"))] else if | canApply -> (5, ("somebody", "yawn")) : (3, ("somebody", "grunt")) : braceEasy | canHear -> (5, ("somebody", "grunt")) : (3, ("something", "wheeze")) : braceEasy | canBrace -> braceEasy | otherwise -> [(1, ("something", "hum silently"))] else return $! if | canApply -> ("somebody", "holler a taunt") | canHear -> ("somebody", "growl menacingly") | canBrace -> ("something", "stomp repeatedly") | otherwise -> ("something", "buzz angrily")