-
Notifications
You must be signed in to change notification settings - Fork 16
/
Perception.hs
91 lines (84 loc) · 3.85 KB
/
Perception.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
module Perception where
import Data.Set as S
import Data.List as L
import Geometry
import State
import Level
import Monster
import FOV
import qualified Config
data Perception =
Perception { preachable :: Set Loc, pvisible :: Set Loc }
perception_ :: State -> Perception
perception_ state@(State { slevel = Level { lmap = lmap },
sconfig = config,
ssensory = sensory }) =
let mode = Config.getDefault "shadow" config "engine" "fovMode"
radius = Config.getDefault 40 config "engine" "fovRadius"
fovMode =
-- terrible, temporary hack
case sensory of
Vision 3 -> Digital radius
Vision 2 -> Permissive radius
Vision 1 -> Shadow
_ ->
-- this is not a hack
case mode of
"permissive" -> Permissive radius
"digital" -> Digital radius
"shadow" -> Shadow
_ -> error $ "perception_: unknown mode: " ++ show mode
hs = levelHeroList state
pers = L.map (\ pl -> perception fovMode (mloc pl) lmap) hs
reachable = S.unions (L.map preachable pers)
visible = S.unions (L.map pvisible pers)
-- TODO: update individual hero perceptions here; see https://github.com/Mikolaj/LambdaHack/issues/issue/31
in Perception reachable visible
perception :: FovMode -> Loc -> LMap -> Perception
perception fovMode ploc lmap =
let
-- This part is simple. "reachable" contains everything that is on an
-- unblocked path from the player position.
reachable = fullscan fovMode ploc lmap
-- In "actVisible", we store the locations that have light and are
-- reachable. Furthermore, the player location itself is always
-- visible.
litVisible = S.filter (\ loc -> light (lmap `at` loc)) reachable
actVisible = S.insert ploc litVisible
srnd = S.fromList $ surroundings ploc
-- In "dirVisible", we store locations in the surroundings that are
-- perceptible from the current position.
dirVisible = S.filter (\ loc -> let p = perceptible (lmap `at` loc) :: [Dir]
in any (\ d -> shift loc d == ploc) p)
srnd
ownVisible = S.union actVisible dirVisible
-- Something is "pasVisible" if it is reachable passively visible from an
-- "actVisible" location, *or* if it is in the surroundings and passively
-- visible from a "dirVisible" location. (This is complicated, and I'd
-- like to simplify it, but for now, it seems to at least do what I
-- want.)
pasVisible = S.filter (\ loc -> let p = passive (lmap `at` loc)
dp = S.member loc srnd
s = if dp then ownVisible else actVisible
in any (\ d -> S.member (shift loc d) s) p)
reachable
visible = S.unions [pasVisible, actVisible, dirVisible]
-- A simpler way to make walls of lit rooms visible, at the cost of making
-- them reflect light from all sides, also from corridors.
-- Can be hacked around by checking for corridors in the condition below.
-- The version in the comment assumes player light has diameter 3, not 1,
-- which looks a bit differently in dark rooms, revealing more walls.
openSurroundings = S.filter (\ loc -> open (lmap `at` loc)) srnd
openVisible = S.union actVisible openSurroundings
simpleVisible =
S.filter
(\ loc -> S.member loc openVisible
|| (reflects (lmap `at` loc)
&& L.any
(\ l -> S.member l actVisible{-openVisible-})
(surroundings loc))
) (S.insert ploc reachable)
in
case fovMode of
Shadow -> Perception reachable visible
_ -> Perception reachable simpleVisible