module Game.LambdaHack.Server.BroadcastAtomic
( handleAndBroadcast, sendPer
#ifdef EXPOSE_INTERNAL
, handleCmdAtomicServer, atomicRemember
#endif
) where
import Prelude ()
import Game.LambdaHack.Common.Prelude
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import Data.Key (mapWithKeyM_)
import Game.LambdaHack.Atomic
import Game.LambdaHack.Common.Actor
import Game.LambdaHack.Common.ActorState
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.Perception
import Game.LambdaHack.Common.State
import qualified Game.LambdaHack.Common.Tile as Tile
import qualified Game.LambdaHack.Content.ItemKind as IK
import Game.LambdaHack.Server.ItemM
import Game.LambdaHack.Server.MonadServer
import Game.LambdaHack.Server.ProtocolM
import Game.LambdaHack.Server.State
handleCmdAtomicServer :: MonadStateWrite m => PosAtomic -> UpdAtomic -> m ()
{-# INLINE handleCmdAtomicServer #-}
handleCmdAtomicServer posAtomic cmd =
when (seenAtomicSer posAtomic) $
handleUpdAtomic cmd
handleAndBroadcast :: (MonadStateWrite m, MonadServerReadRequest m)
=> CmdAtomic -> m ()
handleAndBroadcast atomic = do
(ps, atomicBroken, psBroken) <-
case atomic of
UpdAtomic cmd -> do
ps <- posUpdAtomic cmd
atomicBroken <- breakUpdAtomic cmd
psBroken <- mapM posUpdAtomic atomicBroken
handleCmdAtomicServer ps cmd
return (ps, atomicBroken, psBroken)
SfxAtomic sfx -> do
ps <- posSfxAtomic sfx
return (ps, [], [])
knowEvents <- getsServer $ sknowEvents . sdebugSer
sperFidOld <- getsServer sperFid
let sendAtomic fid (UpdAtomic cmd) = sendUpdate fid cmd
sendAtomic fid (SfxAtomic sfx) = sendSfx fid sfx
breakSend lid fid fact perFidLid = do
let hear atomic2 = do
local <- case _gleader fact of
Nothing -> return True
Just leader -> do
body <- getsState $ getActorBody leader
return $! (blid body == lid)
loud <- case atomic2 of
UpdAtomic cmd -> loudUpdAtomic local cmd
SfxAtomic cmd -> loudSfxAtomic local cmd
case loud of
Nothing -> return ()
Just msg -> sendSfx fid $ SfxMsgFid fid msg
send2 (cmd2, ps2) =
when (seenAtomicCli knowEvents fid perFidLid ps2) $
sendUpdate fid cmd2
case psBroken of
_ : _ -> mapM_ send2 $ zip atomicBroken psBroken
[] -> hear atomic
anySend lid fid fact perFidLid =
if seenAtomicCli knowEvents fid perFidLid ps
then sendAtomic fid atomic
else breakSend lid fid fact perFidLid
posLevel lid fid fact =
anySend lid fid fact $ sperFidOld EM.! fid EM.! lid
send fid fact = case ps of
PosSight lid _ -> posLevel lid fid fact
PosFidAndSight _ lid _ -> posLevel lid fid fact
PosFidAndSer (Just lid) _ -> posLevel lid fid fact
PosSmell lid _ -> posLevel lid fid fact
PosFid fid2 -> when (fid == fid2) $ sendAtomic fid atomic
PosFidAndSer Nothing fid2 ->
when (fid == fid2) $ sendAtomic fid atomic
PosSer -> return ()
PosAll -> sendAtomic fid atomic
PosNone -> error $ "" `showFailure` (fid, fact, atomic)
factionD <- getsState sfactionD
mapWithKeyM_ send factionD
loudUpdAtomic :: MonadStateRead m => Bool -> UpdAtomic -> m (Maybe SfxMsg)
loudUpdAtomic local cmd = do
Kind.COps{coTileSpeedup} <- getsState scops
mcmd <- case cmd of
UpdDestroyActor _ body _ | not $ bproj body -> return $ Just cmd
UpdCreateItem _ _ _ (CActor _ CGround) -> return $ Just cmd
UpdTrajectory aid (Just (l, _)) Nothing | local && not (null l) -> do
b <- getsState $ getActorBody aid
trunk <- getsState $ getItemBody $ btrunk b
return $! if actorTrunkIsBlast trunk then Nothing else Just cmd
UpdAlterTile _ _ fromTile _ -> return $!
if Tile.isDoor coTileSpeedup fromTile
then if local then Just cmd else Nothing
else Just cmd
UpdAlterExplorable{} -> return $ Just cmd
_ -> return Nothing
return $! SfxLoudUpd local <$> mcmd
loudSfxAtomic :: MonadServer m => Bool -> SfxAtomic -> m (Maybe SfxMsg)
loudSfxAtomic local cmd =
case cmd of
SfxStrike source _ iid cstore | local -> do
itemToF <- itemToFullServer
sb <- getsState $ getActorBody source
bag <- getsState $ getBodyStoreBag sb cstore
let kit = EM.findWithDefault (1, []) iid bag
itemFull = itemToF iid kit
ik = itemKindId $ fromJust $ itemDisco itemFull
distance = 20
return $ Just $ SfxLoudStrike local ik distance
SfxEffect _ aid (IK.Summon grp p) _ | local -> do
b <- getsState $ getActorBody aid
return $ Just $ SfxLoudSummon (bproj b) grp p
_ -> return Nothing
sendPer :: MonadServerReadRequest m
=> FactionId -> LevelId
-> Perception -> Perception -> Perception -> m ()
{-# INLINE sendPer #-}
sendPer fid lid outPer inPer perNew = do
sendUpdate fid $ UpdPerception lid outPer inPer
remember <- getsState $ atomicRemember lid inPer
let seenNew = seenAtomicCli False fid perNew
psRem <- mapM posUpdAtomic remember
let !_A = assert (allB seenNew psRem) ()
mapM_ (sendUpdate fid) remember
atomicRemember :: LevelId -> Perception -> State -> [UpdAtomic]
{-# INLINE atomicRemember #-}
atomicRemember lid inPer s =
let inFov = ES.elems $ totalVisible inPer
lvl = sdungeon s EM.! lid
inAssocs = concatMap (\p -> posToAssocs p lid s) inFov
fActor (aid, b) = let ais = getCarriedAssocs b s
in UpdSpotActor aid b ais
inActor = map fActor inAssocs
pMaybe p = maybe Nothing (\x -> Just (p, x))
inContainer fc itemFloor =
let inItem = mapMaybe (\p -> pMaybe p $ EM.lookup p itemFloor) inFov
fItem p (iid, kit) =
UpdSpotItem True iid (getItemBody iid s) kit (fc lid p)
fBag (p, bag) = map (fItem p) $ EM.assocs bag
in concatMap fBag inItem
inFloor = inContainer CFloor (lfloor lvl)
inEmbed = inContainer CEmbed (lembed lvl)
Kind.COps{cotile} = scops s
hideTile p = Tile.hideAs cotile $ lvl `at` p
inTileMap = map (\p -> (p, hideTile p)) inFov
atomicTile = if null inTileMap then [] else [UpdSpotTile lid inTileMap]
inSmellFov = ES.elems $ totalSmelled inPer
inSm = mapMaybe (\p -> pMaybe p $ EM.lookup p (lsmell lvl)) inSmellFov
atomicSmell = if null inSm then [] else [UpdSpotSmell lid inSm]
in atomicTile ++ inFloor ++ inEmbed ++ atomicSmell ++ inActor