about summary refs log tree commit diff
path: root/src/Xanthous/Data/EntityMap/Graphics.hs
blob: 0f2f2bfe16d6fb6482e737f43a78d9c11fd04bc5 (plain) (blame)
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
{-# LANGUAGE ViewPatterns #-}
--------------------------------------------------------------------------------
module Xanthous.Data.EntityMap.Graphics
  ( visiblePositions
  , visibleEntities
  , linesOfSight
  , canSee
  ) where
--------------------------------------------------------------------------------
import Xanthous.Prelude hiding (lines)
--------------------------------------------------------------------------------
import Xanthous.Util (takeWhileInclusive)
import Xanthous.Data
import Xanthous.Data.Entities
import Xanthous.Data.EntityMap
import Xanthous.Game.State
import Xanthous.Util.Graphics (circle, line)
--------------------------------------------------------------------------------

-- | Returns a set of positions that are visible, when taking into account
-- 'blocksVision', from the given position, within the given radius.
visiblePositions
  :: Entity e
  => Position
  -> Word -- ^ Vision radius
  -> EntityMap e
  -> Set Position
visiblePositions pos radius
  = setFromList . positions . visibleEntities pos radius

-- | Returns a list of individual lines of sight, each of which is a list of
-- entities at positions on that line of sight
linesOfSight
  :: forall e. Entity e
  => Position
  -> Word
  -> EntityMap e
  -> [[(Position, Vector (EntityID, e))]]
linesOfSight (view _Position -> pos) visionRadius em
  = entitiesOnLines
  <&> takeWhileInclusive
      (none (view blocksVision . entityAttributes . snd) . snd)
  where
    radius = circle pos $ fromIntegral visionRadius
    lines = line pos <$> radius
    entitiesOnLines :: [[(Position, Vector (EntityID, e))]]
    entitiesOnLines = lines <&> map getPositionedAt
    getPositionedAt :: (Int, Int) -> (Position, Vector (EntityID, e))
    getPositionedAt p =
      let ppos = _Position # p
      in (ppos, over _2 (view positioned) <$> atPositionWithIDs ppos em)

-- | Given a point and a radius of vision, returns a list of all entities that
-- are *visible* (eg, not blocked by an entity that obscures vision) from that
-- point
visibleEntities :: Entity e => Position -> Word -> EntityMap e -> EntityMap e
visibleEntities pos visionRadius
  = fromEIDsAndPositioned
  . foldMap (\(p, es) -> over _2 (Positioned p) <$> es)
  . fold
  . linesOfSight pos visionRadius

canSee :: Entity e => (e -> Bool) -> Position -> Word -> EntityMap e -> Bool
canSee match pos radius = any match . visibleEntities pos radius
-- ^ this might be optimizable