content: {-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving #-}
  -- | Actors perceiving other actors and the dungeon level.
  --
  -- Visibility works according to KISS. Everything that player sees is real.
  -- There are no unmarked hidden tiles and only solid tiles can be marked,
  -- so there are no invisible walls and to pass through an illusory wall,
  -- you have to use a turn bumping into it first. Only tiles marked with Suspect
  -- can turn out to be another tile. (So, if all tiles are marked with
  -- Suspect, the player knows nothing for sure, but this should be avoided,
  -- because searching becomes too time-consuming.)
  -- Each actor sees adjacent tiles, even when blind, so adjacent tiles are
  -- known, so the actor can decide accurately whether to pass thorugh
  -- or alter, etc.
  --
  -- Items are always real and visible. Actors are real, but can be invisible.
  -- Invisible actors in walls can't be hit, but are hinted at when altering
  -- the tile, so the player can flee or block. Invisible actors in open
  -- space can be hit.
  module Game.LambdaHack.Common.Perception
    ( PerVisible(..)
    , PerSmelled(..)
    , Perception(..)
    , PerLid
    , PerFid
    , totalVisible, totalSmelled
    , emptyPer, nullPer, addPer, diffPer
    ) where
  
  import Prelude ()
  
  import Game.LambdaHack.Core.Prelude
  
  import           Data.Binary
  import qualified Data.EnumMap.Strict as EM
  import qualified Data.EnumSet as ES
  import           GHC.Generics (Generic)
  
  import Game.LambdaHack.Common.Types
  import Game.LambdaHack.Common.Point
  
  -- | Visible positions.
  newtype PerVisible = PerVisible {pvisible :: ES.EnumSet Point}
    deriving (Show, Eq, Binary)
  
  -- | Smelled positions.
  newtype PerSmelled = PerSmelled {psmelled :: ES.EnumSet Point}
    deriving (Show, Eq, Binary)
  
  -- | The type representing the perception of a faction on a level.
  data Perception = Perception
    { psight :: PerVisible
    , psmell :: PerSmelled
    }
    deriving (Show, Eq, Generic)
  
  instance Binary Perception
  
  -- | Perception of a single faction, indexed by level identifier.
  type PerLid = EM.EnumMap LevelId Perception
  
  -- | Perception indexed by faction identifier.
  -- This can't be added to @FactionDict@, because clients can't see it
  -- for other factions.
  type PerFid = EM.EnumMap FactionId PerLid
  
  -- | The set of tiles visible by at least one hero.
  totalVisible :: Perception -> ES.EnumSet Point
  totalVisible = pvisible . psight
  
  -- | The set of tiles smelt by at least one hero.
  totalSmelled :: Perception -> ES.EnumSet Point
  totalSmelled = psmelled . psmell
  
  emptyPer :: Perception
  emptyPer = Perception { psight = PerVisible ES.empty
                        , psmell = PerSmelled ES.empty }
  
  nullPer :: Perception -> Bool
  nullPer per = per == emptyPer
  
  addPer :: Perception -> Perception -> Perception
  addPer per1 per2 =
    Perception
      { psight = PerVisible
                 $ totalVisible per1 `ES.union` totalVisible per2
      , psmell = PerSmelled
                 $ totalSmelled per1 `ES.union` totalSmelled per2
      }
  
  diffPer :: Perception -> Perception -> Perception
  diffPer per1 per2 =
    Perception
      { psight = PerVisible
                 $ totalVisible per1 ES.\\ totalVisible per2
      , psmell = PerSmelled
                 $ totalSmelled per1 ES.\\ totalSmelled per2
      }
  