module Game.LambdaHack.Atomic.PosAtomicRead
( PosAtomic(..), posUpdAtomic, posSfxAtomic
, resetsFovCmdAtomic, breakUpdAtomic, breakSfxAtomic, loudUpdAtomic
, seenAtomicCli, seenAtomicSer, generalMoveItem
) where
import Control.Applicative
import Control.Exception.Assert.Sugar
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import qualified NLP.Miniutter.English as MU
import Game.LambdaHack.Atomic.CmdAtomic
import Game.LambdaHack.Common.Actor
import Game.LambdaHack.Common.ActorState
import qualified Game.LambdaHack.Common.Effect as Effect
import Game.LambdaHack.Common.Faction
import Game.LambdaHack.Common.Item
import qualified Game.LambdaHack.Common.Kind as Kind
import Game.LambdaHack.Common.Level
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Common.MonadStateRead
import Game.LambdaHack.Common.Msg
import Game.LambdaHack.Common.Perception
import Game.LambdaHack.Common.Point
import Game.LambdaHack.Common.State
import qualified Game.LambdaHack.Common.Tile as Tile
import Game.LambdaHack.Content.ModeKind as ModeKind
data PosAtomic =
PosSight !LevelId ![Point]
| PosFidAndSight ![FactionId] !LevelId ![Point]
| PosSmell !LevelId ![Point]
| PosFid !FactionId
| PosFidAndSer !(Maybe LevelId) !FactionId
| PosSer
| PosAll
| PosNone
deriving (Show, Eq)
posUpdAtomic :: MonadStateRead m => UpdAtomic -> m PosAtomic
posUpdAtomic cmd = case cmd of
UpdCreateActor _ body _ -> posProjBody body
UpdDestroyActor _ body _ -> posProjBody body
UpdCreateItem _ _ _ c -> singleContainer c
UpdDestroyItem _ _ _ c -> singleContainer c
UpdSpotActor _ body _ -> posProjBody body
UpdLoseActor _ body _ -> posProjBody body
UpdSpotItem _ _ _ c -> singleContainer c
UpdLoseItem _ _ _ c -> singleContainer c
UpdMoveActor aid fromP toP -> do
b <- getsState $ getActorBody aid
return $! if bproj b
then PosSight (blid b) [fromP, toP]
else PosFidAndSight [bfid b] (blid b) [fromP, toP]
UpdWaitActor aid _ -> singleAid aid
UpdDisplaceActor source target -> do
sb <- getsState $ getActorBody source
tb <- getsState $ getActorBody target
let ps = [bpos sb, bpos tb]
lid = assert (blid sb == blid tb) $ blid sb
return $! if bproj sb && bproj tb
then PosSight lid ps
else if bproj sb
then PosFidAndSight [bfid tb] lid ps
else if bproj tb
then PosFidAndSight [bfid sb] lid ps
else PosFidAndSight [bfid sb, bfid tb] lid ps
UpdMoveItem _ _ aid _ CSha -> do
b <- getsState $ getActorBody aid
return $! PosFidAndSer (Just $ blid b) (bfid b)
UpdMoveItem _ _ aid CSha _ -> do
b <- getsState $ getActorBody aid
return $! PosFidAndSer (Just $ blid b) (bfid b)
UpdMoveItem _ _ aid _ _ -> singleAid aid
UpdAgeActor aid _ -> singleAid aid
UpdRefillHP aid _ -> singleAid aid
UpdRefillCalm aid _ -> singleAid aid
UpdOldFidActor aid _ _ -> singleAid aid
UpdTrajectory aid _ _ -> singleAid aid
UpdColorActor aid _ _ -> singleAid aid
UpdQuitFaction{} -> return PosAll
UpdLeadFaction fid _ _ -> do
fact <- getsState $ (EM.! fid) . sfactionD
return $! if fleaderMode (gplayer fact) /= LeaderNull
then PosFidAndSer Nothing fid
else PosNone
UpdDiplFaction{} -> return PosAll
UpdTacticFaction fid _ _ -> return $! PosFidAndSer Nothing fid
UpdAutoFaction{} -> return PosAll
UpdRecordKill aid _ _ -> singleFidAndAid aid
UpdAlterTile lid p _ _ -> return $! PosSight lid [p]
UpdAlterClear{} -> return PosAll
UpdSearchTile aid p _ _ -> do
(lid, pos) <- posOfAid aid
return $! PosSight lid [pos, p]
UpdLearnSecrets aid _ _ -> singleAid aid
UpdSpotTile lid ts -> do
let ps = map fst ts
return $! PosSight lid ps
UpdLoseTile lid ts -> do
let ps = map fst ts
return $! PosSight lid ps
UpdAlterSmell lid p _ _ -> return $! PosSmell lid [p]
UpdSpotSmell lid sms -> do
let ps = map fst sms
return $! PosSmell lid ps
UpdLoseSmell lid sms -> do
let ps = map fst sms
return $! PosSmell lid ps
UpdAgeGame _ _ -> return PosAll
UpdDiscover lid p _ _ _ -> return $! PosSight lid [p]
UpdCover lid p _ _ _ -> return $! PosSight lid [p]
UpdDiscoverKind lid p _ _ -> return $! PosSight lid [p]
UpdCoverKind lid p _ _ -> return $! PosSight lid [p]
UpdDiscoverSeed lid p _ _ -> return $! PosSight lid [p]
UpdCoverSeed lid p _ _ -> return $! PosSight lid [p]
UpdPerception{} -> return PosNone
UpdRestart fid _ _ _ _ _ -> return $! PosFid fid
UpdRestartServer _ -> return PosSer
UpdResume fid _ -> return $! PosFid fid
UpdResumeServer _ -> return PosSer
UpdKillExit fid -> return $! PosFid fid
UpdWriteSave -> return PosAll
UpdMsgAll{} -> return PosAll
UpdRecordHistory fid -> return $! PosFid fid
posSfxAtomic :: MonadStateRead m => SfxAtomic -> m PosAtomic
posSfxAtomic cmd = case cmd of
SfxStrike source target _ _ -> do
(slid, sp) <- posOfAid source
(tlid, tp) <- posOfAid target
return $! assert (slid == tlid) $ PosSight slid [sp, tp]
SfxRecoil source target _ _ -> do
(slid, sp) <- posOfAid source
(tlid, tp) <- posOfAid target
return $! assert (slid == tlid) $ PosSight slid [sp, tp]
SfxProject aid _ -> singleAid aid
SfxCatch aid _ -> singleAid aid
SfxActivate aid _ _ -> singleAid aid
SfxCheck aid _ _ -> singleAid aid
SfxTrigger aid p _ -> do
(lid, pa) <- posOfAid aid
return $! PosSight lid [pa, p]
SfxShun aid p _ -> do
(lid, pa) <- posOfAid aid
return $! PosSight lid [pa, p]
SfxEffect _ aid _ -> singleAid aid
SfxMsgFid fid _ -> return $! PosFid fid
SfxMsgAll _ -> return PosAll
SfxActorStart aid -> singleAid aid
posProjBody :: Monad m => Actor -> m PosAtomic
posProjBody body = return $!
if bproj body
then PosSight (blid body) [bpos body]
else PosFidAndSight [bfid body] (blid body) [bpos body]
singleFidAndAid :: MonadStateRead m => ActorId -> m PosAtomic
singleFidAndAid aid = do
body <- getsState $ getActorBody aid
return $! PosFidAndSight [bfid body] (blid body) [bpos body]
singleAid :: MonadStateRead m => ActorId -> m PosAtomic
singleAid aid = do
(lid, p) <- posOfAid aid
return $! PosSight lid [p]
singleContainer :: MonadStateRead m => Container -> m PosAtomic
singleContainer (CFloor lid p) = return $! PosSight lid [p]
singleContainer (CActor aid CSha) = do
b <- getsState $ getActorBody aid
return $! PosFidAndSer (Just $ blid b) (bfid b)
singleContainer (CActor aid _) = do
(lid, p) <- posOfAid aid
return $! PosSight lid [p]
singleContainer (CTrunk fid lid p) = return $! PosFidAndSight [fid] lid [p]
resetsFovCmdAtomic :: UpdAtomic -> Bool
resetsFovCmdAtomic cmd = case cmd of
UpdCreateActor{} -> True
UpdDestroyActor{} -> True
UpdCreateItem{} -> True
UpdDestroyItem{} -> True
UpdSpotActor{} -> True
UpdLoseActor{} -> True
UpdSpotItem{} -> True
UpdLoseItem{} -> True
UpdMoveActor{} -> True
UpdDisplaceActor{} -> True
UpdMoveItem{} -> True
UpdRefillCalm{} -> True
UpdAlterTile{} -> True
UpdSpotTile{} -> True
UpdLoseTile{} -> True
_ -> False
breakUpdAtomic :: MonadStateRead m => UpdAtomic -> m [UpdAtomic]
breakUpdAtomic cmd = case cmd of
UpdMoveActor aid _ toP -> do
b <- getsState $ getActorBody aid
ais <- getsState $ getCarriedAssocs b
return [ UpdLoseActor aid b ais
, UpdSpotActor aid b {bpos = toP, boldpos = bpos b} ais ]
UpdDisplaceActor source target -> do
sb <- getsState $ getActorBody source
sais <- getsState $ getCarriedAssocs sb
tb <- getsState $ getActorBody target
tais <- getsState $ getCarriedAssocs tb
return [ UpdLoseActor source sb sais
, UpdSpotActor source sb {bpos = bpos tb, boldpos = bpos sb} sais
, UpdLoseActor target tb tais
, UpdSpotActor target tb {bpos = bpos sb, boldpos = bpos tb} tais
]
UpdMoveItem iid k aid cstore1 cstore2 | cstore1 == CSha
|| cstore2 == CSha -> do
item <- getsState $ getItemBody iid
return [ UpdLoseItem iid item k (CActor aid cstore1)
, UpdSpotItem iid item k (CActor aid cstore2) ]
_ -> return [cmd]
breakSfxAtomic :: MonadStateRead m => SfxAtomic -> m [SfxAtomic]
breakSfxAtomic cmd = case cmd of
SfxStrike source target _ _ -> do
sb <- getsState $ getActorBody source
return $! [ SfxEffect (bfid sb) source (Effect.RefillCalm (1))
| not $ bproj sb ]
++ [SfxEffect (bfid sb) target (Effect.RefillHP (1))]
_ -> return [cmd]
loudUpdAtomic :: MonadStateRead m
=> Bool -> FactionId -> UpdAtomic -> m (Maybe Msg)
loudUpdAtomic local fid cmd = do
msound <- case cmd of
UpdDestroyActor _ body _
| not $ fid == bfid body || bproj body -> return $ Just "shriek"
UpdCreateItem{} -> return $ Just "clatter"
UpdAlterTile _ _ fromTile _ -> do
Kind.COps{cotile} <- getsState scops
if Tile.isDoor cotile fromTile
then return $ Just "creaking sound"
else return $ Just "rumble"
_ -> return Nothing
let distant = if local then [] else ["distant"]
hear sound = makeSentence [ "you hear"
, MU.AW $ MU.Phrase $ distant ++ [sound] ]
return $! hear <$> msound
seenAtomicCli :: Bool -> FactionId -> Perception -> PosAtomic -> Bool
seenAtomicCli knowEvents fid per posAtomic =
case posAtomic of
PosSight _ ps -> all (`ES.member` totalVisible per) ps || knowEvents
PosFidAndSight fids _ ps ->
fid `elem` fids || all (`ES.member` totalVisible per) ps || knowEvents
PosSmell _ ps -> all (`ES.member` smellVisible per) ps || knowEvents
PosFid fid2 -> fid == fid2
PosFidAndSer _ fid2 -> fid == fid2
PosSer -> False
PosAll -> True
PosNone -> assert `failure` "no position possible" `twith` fid
seenAtomicSer :: PosAtomic -> Bool
seenAtomicSer posAtomic =
case posAtomic of
PosFid _ -> False
PosNone -> False
_ -> True
generalMoveItem :: MonadStateRead m
=> ItemId -> Int -> Container -> Container
-> m [UpdAtomic]
generalMoveItem iid k c1 c2 = do
case (c1, c2) of
(CActor aid1 cstore1, CActor aid2 cstore2) | aid1 == aid2 -> do
return [UpdMoveItem iid k aid1 cstore1 cstore2]
_ -> do
item <- getsState $ getItemBody iid
return [ UpdLoseItem iid item k c1
, UpdSpotItem iid item k c2 ]