module Game.LambdaHack.Common.Perception
( Perception(..), PerceptionVisible(..), PerActor
, totalVisible, smellVisible
, actorSeesPos, nullPer, addPer, diffPer, smellFromActors
, FactionPers, Pers
) where
import Data.Binary
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import GHC.Generics (Generic)
import Game.LambdaHack.Common.Actor
import Game.LambdaHack.Common.ActorState
import Game.LambdaHack.Common.Faction
import qualified Game.LambdaHack.Common.Kind as Kind
import Game.LambdaHack.Common.Level
import Game.LambdaHack.Common.Point
import Game.LambdaHack.Common.State
import Game.LambdaHack.Content.ActorKind
newtype PerceptionVisible = PerceptionVisible
{ pvisible :: ES.EnumSet Point}
deriving (Show, Eq, Binary)
type PerActor = EM.EnumMap ActorId PerceptionVisible
data Perception = Perception
{ perActor :: !PerActor
, ptotal :: !PerceptionVisible
, psmell :: !PerceptionVisible
}
deriving (Show, Eq, Generic)
instance Binary Perception
type FactionPers = EM.EnumMap LevelId Perception
type Pers = EM.EnumMap FactionId FactionPers
totalVisible :: Perception -> ES.EnumSet Point
totalVisible = pvisible . ptotal
smellVisible :: Perception -> ES.EnumSet Point
smellVisible = pvisible . psmell
actorSeesPos :: Perception -> ActorId -> Point -> Bool
actorSeesPos per aid pos =
let isIn = (pos `ES.member`) . pvisible
in maybe False isIn $ EM.lookup aid $ perActor per
nullPer :: Perception -> Bool
nullPer per = ES.null (totalVisible per)
addPer :: Perception -> Perception -> Perception
addPer per1 per2 =
let f :: (PerceptionVisible -> PerceptionVisible -> PerceptionVisible)
f pv1 pv2 = PerceptionVisible $ pvisible pv1 `ES.union` pvisible pv2
in Perception
{ perActor = EM.unionWith f (perActor per1) (perActor per2)
, ptotal = PerceptionVisible
$ totalVisible per1 `ES.union` totalVisible per2
, psmell = PerceptionVisible
$ smellVisible per1 `ES.union` smellVisible per2
}
diffPer :: Perception -> Perception -> Perception
diffPer per1 per2 =
let f :: (PerceptionVisible -> PerceptionVisible -> Maybe PerceptionVisible)
f pv1 pv2 =
let diff = pvisible pv1 ES.\\ pvisible pv2
in if ES.null diff then Nothing else Just $ PerceptionVisible diff
in Perception
{ perActor = EM.differenceWith f (perActor per1) (perActor per2)
, ptotal = PerceptionVisible
$ totalVisible per1 ES.\\ totalVisible per2
, psmell = PerceptionVisible
$ smellVisible per1 ES.\\ smellVisible per2
}
smellFromActors :: Kind.COps -> State -> PerActor -> PerceptionVisible
smellFromActors Kind.COps{coactor=Kind.Ops{okind}} s perActor =
let actorCanSmell aid =
not (EM.notMember aid $ sactorD s)
&& let b = getActorBody aid s
in asmell $ okind $ bkind b
visSmell = filter (actorCanSmell . fst) $ EM.assocs perActor
setSmell = map (pvisible . snd) visSmell
in PerceptionVisible $ ES.unions setSmell