174 lines
4.9 KiB
Haskell
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
|