--------------------------------------------------------------------------------
module Xanthous.Data.EntityMap.Graphics
( visiblePositions
, visibleEntities
, lineOfSight
, 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 entities on the *line of sight* from the first position
-- to the second position
lineOfSight
:: forall e. Entity e
=> Position -- ^ Origin
-> Position -- ^ Destination
-> EntityMap e
-> [(Position, Vector (EntityID, e))]
lineOfSight (view _Position -> origin) (view _Position -> destination) em =
takeWhileInclusive (none (view blocksVision . entityAttributes . snd) . snd)
$ getPositionedAt <$> line origin destination
where
getPositionedAt :: V2 Int -> (Position, Vector (EntityID, e))
getPositionedAt (review _Position -> p) =
(p, over _2 (view positioned) <$> atPositionWithIDs p em)
-- | 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 -- ^ Centerpoint
-> Word -- ^ Radius
-> EntityMap e
-> [[(Position, Vector (EntityID, e))]]
linesOfSight pos visionRadius em =
radius <&> \edge -> lineOfSight pos (_Position # edge) em
where
radius = circle (pos ^. _Position) $ fromIntegral visionRadius
-- | 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