module Game.LambdaHack.Server.Fov
( dungeonPerception, levelPerception, fullscan
) where
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
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.Perception
import Game.LambdaHack.Common.Point
import Game.LambdaHack.Common.State
import qualified Game.LambdaHack.Common.Tile as Tile
import Game.LambdaHack.Common.Vector
import Game.LambdaHack.Content.ActorKind
import Game.LambdaHack.Content.RuleKind
import Game.LambdaHack.Content.TileKind
import Game.LambdaHack.Server.Fov.Common
import qualified Game.LambdaHack.Server.Fov.Digital as Digital
import qualified Game.LambdaHack.Server.Fov.Permissive as Permissive
import qualified Game.LambdaHack.Server.Fov.Shadow as Shadow
newtype PerceptionReachable = PerceptionReachable
{preachable :: [Point]}
deriving Show
levelPerception :: Kind.COps -> FovMode -> FactionId
-> LevelId -> Level -> State
-> Perception
levelPerception cops@Kind.COps{cotile, coactor=Kind.Ops{okind}}
configFov fid lid lvl@Level{lxsize, lysize} s =
let hs = actorNotProjList (== fid) lid s
cR b = preachable $ computeReachable cops configFov lvl b
totalReachable = PerceptionReachable $ concatMap cR hs
pAndVicinity p = p : vicinity lxsize lysize p
lightsBodies = map (\b -> (pAndVicinity $ bpos b, b)) hs
light = concat $ map fst lightsBodies
ptotal = computeVisible cotile totalReachable lvl light
canSmell b = asmell $ okind $ bkind b
psmell = PerceptionVisible $ ES.fromList
$ concat $ map fst $ filter (canSmell . snd) lightsBodies
in Perception ptotal psmell
factionPerception :: Kind.COps -> FovMode -> FactionId -> State
-> FactionPers
factionPerception cops configFov fid s =
EM.mapWithKey (\lid lvl -> levelPerception cops configFov fid lid lvl s)
$ sdungeon s
dungeonPerception :: Kind.COps -> FovMode -> State -> Pers
dungeonPerception cops configFov s =
let f fid _ = factionPerception cops configFov fid s
in EM.mapWithKey f $ sfactionD s
computeVisible :: Kind.Ops TileKind -> PerceptionReachable
-> Level -> [Point] -> PerceptionVisible
computeVisible cotile PerceptionReachable{preachable} lvl lights =
let isVisible pos = Tile.isLit cotile (lvl `at` pos)
in PerceptionVisible $ ES.fromList $ lights ++ filter isVisible preachable
computeReachable :: Kind.COps -> FovMode -> Level -> Actor
-> PerceptionReachable
computeReachable Kind.COps{cotile, coactor=Kind.Ops{okind}}
configFov lvl body =
let sight = asight $ okind $ bkind body
fovMode = if sight then configFov else Blind
ppos = bpos body
scan = fullscan cotile fovMode ppos lvl
in PerceptionReachable scan
fullscan :: Kind.Ops TileKind
-> FovMode
-> Point
-> Level
-> [Point]
fullscan cotile fovMode spectatorPos lvl = spectatorPos :
case fovMode of
Shadow ->
concatMap (\tr -> map tr (Shadow.scan (isCl . tr) 1 (0, 1))) tr8
Permissive ->
concatMap (\tr -> map tr (Permissive.scan (isCl . tr))) tr4
Digital r ->
concatMap (\tr -> map tr (Digital.scan r (isCl . tr))) tr4
Blind ->
let radiusOne = 1
in concatMap (\tr -> map tr (Digital.scan radiusOne (isCl . tr))) tr4
where
isCl :: Point -> Bool
isCl = Tile.isClear cotile . (lvl `at`)
trV :: X -> Y -> Point
trV x y = shift spectatorPos $ Vector x y
tr8 :: [(Distance, Progress) -> Point]
tr8 =
[ \(p, d) -> trV p d
, \(p, d) -> trV (p) d
, \(p, d) -> trV p (d)
, \(p, d) -> trV (p) (d)
, \(p, d) -> trV d p
, \(p, d) -> trV (d) p
, \(p, d) -> trV d (p)
, \(p, d) -> trV (d) (p)
]
tr4 :: [Bump -> Point]
tr4 =
[ \B{..} -> trV bx (by)
, \B{..} -> trV by bx
, \B{..} -> trV (bx) by
, \B{..} -> trV (by) (bx)
]