Files
loop/src/Dodge/WorldEvent/ThingsHit.hs
T

174 lines
4.9 KiB
Haskell

{-# LANGUAGE TupleSections #-}
{- |
Find which objects lie upon a line.
-}
module Dodge.WorldEvent.ThingsHit (
thingsHit,
thingsHitZ,
thingHit,
thingHitFilt,
thingsHitExceptCr,
crsHitRadial,
wlsHitRadial,
crHit,
crWlPbHit,
wlsHitUnsorted,
isWalkable,
) where
import Data.Monoid
import qualified Data.Set as S
import Linear
import Dodge.Data.Object
import Dodge.Creature.Radius
import Control.Monad
import Control.Lens
import Data.Bifunctor
import qualified Data.IntSet as IS
import Data.List (sortOn)
import Data.Maybe
import Dodge.Base
import Dodge.Data.World
import Dodge.Zoning.Creature
import Dodge.Zoning.Wall
import Geometry
import qualified ListHelp as List
import qualified Data.IntMap.Strict as IM
{- List those objects that appear on a line. -}
thingsHit :: Point2 -> Point2 -> World -> [(Point2, Either Creature Wall)]
thingsHit sp ep w =
List.mergeOn
(dist sp . fst)
(map (second Left) (crsHit sp ep w))
(map (second Right) (wlsHit sp ep w))
{- Does not hit corpses -}
thingsHitZ :: Float -> Point2 -> Point2 -> World -> [(Point2, Either Creature Wall)]
thingsHitZ z sp ep w =
List.mergeOn
(dist sp . fst)
(map (second Left) (crsHitZ z sp ep w))
(map (second Right) (wlsHit sp ep w))
crWlPbHit :: Point2 -> Point2 -> World -> [(Point2, Object)]
crWlPbHit sp ep w =
List.mergeOn
(dist sp . fst)
(map (fmap toobj) (thingsHit sp ep w))
(map (fmap OPulseBall) (pbsHit sp ep w))
where
toobj (Left cr) = OCreature cr
toobj (Right wl) = OWall wl
crsHit :: Point2 -> Point2 -> World -> [(Point2, Creature)]
crsHit sp ep w
| sp == ep = mempty
| otherwise =
sortOn (dist sp . fst)
. overlapSegCrs sp ep
. mapMaybe (\cid -> w ^? cWorld . lWorld . creatures . ix cid)
. IS.toList
. crixsNearSeg sp ep
$ w
crsHitZ :: Float -> Point2 -> Point2 -> World -> [(Point2, Creature)]
crsHitZ z sp ep w
| sp == ep = mempty
| otherwise =
sortOn (dist sp . fst)
. overlapSegCrs sp ep
. mapMaybe (\cid -> f =<< w ^? cWorld . lWorld . creatures . ix cid)
. IS.toList
. crixsNearSeg sp ep
$ w
where
f cr
| HP{} <- cr ^. crHP = Just cr
| CrIsCorpse{} <- cr ^. crHP
, z < 5 = Just cr
| otherwise = Nothing
pbsHit :: Point2 -> Point2 -> World -> [(Point2, PulseBall)]
pbsHit sp ep w
| sp == ep = mempty
| otherwise =
sortOn (dist sp . fst)
. mapMaybe (\pb -> (,pb) <$> fst (intersectCircSeg (_pbPos pb) 8 sp ep))
. IM.elems
$ w ^. cWorld . lWorld . pulseBalls
thingHitFilt ::
(Creature -> Bool) ->
(Wall -> Bool) ->
Point2 ->
Point2 ->
World ->
Maybe (Point2, Either Creature Wall)
thingHitFilt fcr fwl sp ep = List.find (f . snd) . thingsHit sp ep
where
f (Left cr) = fcr cr
f (Right wl) = fwl wl
thingHit :: Point2 -> Point2 -> World -> Maybe (Point2, Either Creature Wall)
thingHit sp ep = listToMaybe . thingsHit sp ep
crHit :: Point2 -> Point2 -> World -> Maybe (Point2,Creature)
--crHit sp ep = listToMaybe . sortOn (dist sp . fst) . crsHit sp ep
crHit sp ep = listToMaybe . crsHit sp ep
{- List objects that appear on a line.
Can filter out a creature. -}
thingsHitExceptCr ::
-- | A possible creature ID
Maybe Int ->
-- | Line start point
Point2 ->
-- | Line end point
Point2 ->
World ->
[(Point2, Either Creature Wall)]
thingsHitExceptCr Nothing sp ep = thingsHit sp ep
thingsHitExceptCr (Just cid) sp ep = filter t . thingsHit sp ep
where
t = (Just cid /=) . (^? _2 . _Left . crID)
wlsHit :: Point2 -> Point2 -> World -> [(Point2, Wall)]
wlsHit sp ep = sortOn (dist sp . fst) . wlsHitUnsorted sp ep
wlsHitUnsorted :: Point2 -> Point2 -> World -> [(Point2, Wall)]
wlsHitUnsorted sp ep
| sp == ep = const mempty
| otherwise =
overlapSegWalls sp ep
. wlsNearSeg sp ep
wlsHitRadial :: Point2 -> Float -> World -> [(Point2, Wall)]
wlsHitRadial p r = mapMaybe f . wlsNearCirc p r
where
f wl = uncurry (intersectSegSeg p (p - r *.* v)) (_wlLine wl) <&> (,wl)
where
v = normalizeV . vNormal . uncurry (-) $ _wlLine wl
crsHitRadial :: Point2 -> Float -> World -> [(Point2, Creature)]
crsHitRadial p r = mapMaybe f . crsNearCirc p r
where
f cr = do
let cp = cr ^. crPos . _xy
guard $ dist p cp < r + crRad (_crType cr)
return (cp + (1 + crRad (_crType cr)) *.* (cp - p), cr)
isFlyable :: Point2 -> Point2 -> World -> Bool
{-# INLINE isFlyable #-}
isFlyable p1 p2 =
not . (WallNotAutoOpen `S.member`)
. foldMap (^. _2 . wlPathFlag)
. wlsHitUnsorted p1 p2
isWalkable :: Point2 -> Point2 -> World -> Bool
{-# INLINE isWalkable #-}
isWalkable p1 p2 w = isFlyable p1 p2 w && not (getAny $ foldMap f (w ^. cWorld . chasms))
where
f = foldMap (Any . isJust . uncurry (intersectSegSeg p1 p2)) . loopPairs