Files
loop/src/Dodge/WallCreatureCollisions.hs
T

77 lines
2.5 KiB
Haskell

-- | Deals with moving creature wall collisions.
module Dodge.WallCreatureCollisions (colCrsWalls) where
import Control.Lens
import Data.Foldable
import qualified Data.IntSet as IS
import Dodge.Base
import Dodge.Creature.Radius
import Dodge.Data.Universe
import Dodge.Zoning.Wall
import Geometry
import Linear
colCrsWalls :: Universe -> Universe
colCrsWalls uv = uv & uvWorld . cWorld . lWorld . creatures %~ fmap (noclipCheck (_uvConfig uv) (_uvWorld uv))
noclipCheck :: Config -> World -> Creature -> Creature
noclipCheck cfig w c
| debugOn Noclip cfig && _crID c == 0 = c -- for noclip
| otherwise = colCrWall w c
colCrWall :: World -> Creature -> Creature
colCrWall w c = cornpush . wallpush $ pushthrough c
where
cornpush = crPos . _xy %~ pushOutFromCorners r ls'
wallpush = pushCr wls
pushthrough = crPos . _xy %~ fst . flip (collidePoint p1) wls -- check push throughs
r = crRad (c ^. crType) + wallBuffer
p1 = c ^. crOldPos . _xy
p2 = c ^. crPos . _xy
ls = _wlLine <$> wls
ls' = filter (uncurry $ isLHS p1) ls
wls = filter notff $ wlsNearCirc p2 r w
notff x = x ^. wlMaterial /= ForceField
pushCr :: [Wall] -> Creature -> Creature
pushCr wls cr
| dist ep sp > r && dist ep ap > r =
ecr
& crDamage <>~ [Crushing 1000 0]
& crPos . _xy .~ ap
| otherwise = ecr
where
ep = ecr ^. crPos . _xy
ap = cr ^. crOldPos . _xy
sp = cr ^. crPos . _xy
ecr = foldl' f (cr & crWallTouch .~ mempty) wls
r = crRad (cr ^. crType)
f acr wl = case pushOutFromWall r (acr ^. crPos . _xy) (wl ^. wlLine) of
Just p -> acr & crPos . _xy .~ p & crWallTouch %~ IS.insert (wl ^. wlID)
Nothing -> acr
-- the amount to push creatures out from walls, extra to their radius
wallBuffer :: Float
wallBuffer = 0
-- assumes that the wall is orientated
-- assumes wall points are different
pushOutFromWall :: Float -> Point2 -> (Point2, Point2) -> Maybe Point2
pushOutFromWall rad cp2 (wp1, wp2)
| isOnWall = Just newP
| otherwise = Nothing
where
n = errorNormalizeV 61 $ vNormal (wp1 - wp2)
wp1' = wp1 + rad *^ n
wp2' = wp2 + rad *^ n
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isOnWall = circOnSegNoEndpoints wp1 wp2 cp2 rad
pushOutFromCorners :: Float -> [(Point2, Point2)] -> Point2 -> Point2
pushOutFromCorners r = flip $ foldr (squashIntersectCirclePoint r . fst)
squashIntersectCirclePoint :: Float -> Point2 -> Point2 -> Point2
squashIntersectCirclePoint rad p cCen
| dist cCen p > rad = cCen
| otherwise = p + (rad *^ squashNormalizeV (cCen - p))