501 lines
18 KiB
Haskell
501 lines
18 KiB
Haskell
{-# LANGUAGE TupleSections #-}
|
|
{-# LANGUAGE BangPatterns #-}
|
|
{- |
|
|
Basic helpers.
|
|
Consider splitting. -}
|
|
module Dodge.Base
|
|
where
|
|
import Dodge.Data
|
|
import Dodge.Zone
|
|
import Dodge.Zone.Data
|
|
import Dodge.Base.Window
|
|
import Geometry
|
|
--import Picture
|
|
import qualified IntMapHelp as IM
|
|
import FoldableHelp
|
|
|
|
import Control.Lens
|
|
import qualified Control.Foldl as L
|
|
import Data.Monoid
|
|
import Data.Maybe
|
|
--import Data.Bifunctor
|
|
--import qualified Data.IntSet as IS
|
|
--import qualified Data.Set as S
|
|
|
|
|
|
{- | Implementation copied from
|
|
- https://hackage.haskell.org/package/utility-ht-0.0.16/docs/src/Data.List.HT.Private.html#takeUntil
|
|
-}
|
|
takeUntil :: Foldable t => (a -> Bool) -> t a -> [a]
|
|
takeUntil p = foldr (\x xs -> x : if p x then [] else xs) []
|
|
|
|
you :: World -> Creature
|
|
you w = _creatures w IM.! _yourID w
|
|
|
|
aCrPos :: Int -> World -> Point2
|
|
aCrPos i w = _crPos $ _creatures w IM.! i
|
|
|
|
yourItem :: World -> Item
|
|
yourItem w = _crInv (you w) IM.! _crInvSel (you w)
|
|
|
|
crItem :: World -> Int -> Item
|
|
crItem w cid = _crInv cr IM.! _crInvSel cr
|
|
where
|
|
cr = _creatures w IM.! cid
|
|
|
|
yourItemRef
|
|
:: Applicative f
|
|
=> World
|
|
-> (Item -> f Item)
|
|
-> World
|
|
-> f World
|
|
yourItemRef w = creatures . ix (_yourID w) . crInv . ix (_crInvSel (you w))
|
|
|
|
wallNormal :: Wall -> Point2
|
|
wallNormal wl = normalizeV . vNormal $ a -.- b
|
|
where
|
|
(a,b) = _wlLine wl
|
|
|
|
wallsOnLine :: Point2 -> Point2 -> IM.IntMap Wall -> [Wall]
|
|
wallsOnLine p1 p2 ws = hitWalls
|
|
where
|
|
hitPoint w = uncurry (intersectSegSeg p1 p2) (_wlLine w)
|
|
hitWalls = filter (isJust . hitPoint) (IM.elems ws)
|
|
|
|
wallsOnCirc :: Point2 -> Float -> IM.IntMap Wall -> IM.IntMap Wall
|
|
wallsOnCirc p r = IM.filter f
|
|
where
|
|
f wl = uncurry circOnSeg (_wlLine wl) p r
|
|
|
|
allWalls :: World -> IM.IntMap Wall
|
|
allWalls w = IM.unions $ concatMap IM.elems $ IM.elems $ _znObjects $ _wallsZone w
|
|
|
|
creaturesNearPoint :: Point2 -> World -> IM.IntMap Creature
|
|
creaturesNearPoint p w = IM.unions [f b $ f a $ _znObjects $ _creaturesZone w | a<-[x-1,x,x+1] , b<-[y-1,y,y+1]]
|
|
where
|
|
(x,y) = crZoneOfPoint p
|
|
f i m = case IM.lookup i m of
|
|
Just val -> val
|
|
_ -> IM.empty
|
|
|
|
creaturesNearPointI :: Int -> Point2 -> World -> IM.IntMap Creature
|
|
creaturesNearPointI n p w = IM.unions [f b $ f a $ _znObjects $ _creaturesZone w | a<-[x-n..x+n] , b<-[y-n..y+n]]
|
|
where
|
|
(x,y) = crZoneOfPoint p
|
|
f i m = case IM.lookup i m of
|
|
Just val -> val
|
|
_ -> IM.empty
|
|
|
|
-- possible BUG, occurs when used in thingsHitLongLine
|
|
creaturesAlongLine :: Point2 -> Point2 -> World -> IM.IntMap Creature
|
|
--creaturesAlongLine a b w = IM.unions [f y $ f x $ _creaturesZone w | (x,y) <- zoneOfLine a b]
|
|
-- where f i m = case IM.lookup i m of Just val -> val
|
|
-- _ -> IM.empty
|
|
creaturesAlongLine a b w = IM.foldrWithKey' g IM.empty kps
|
|
where g x s = IM.union (IM.unions (IM.restrictKeys (f x $ _znObjects $ _creaturesZone w) s))
|
|
kps = zoneOfLineIntMap a b
|
|
f i m = case IM.lookup i m of Just val -> val
|
|
_ -> IM.empty
|
|
{- | Expands a line out to a given thickness. -}
|
|
lineGeom :: Float -> Point2 -> Point2 -> [Point2]
|
|
lineGeom t x y
|
|
| x == y = []
|
|
| otherwise = [x +.+ n x y, x -.- n x y, y +.+ n x y, y -.- n x y]
|
|
where
|
|
n a b = (t*0.5) *.* errorNormalizeV 4200 (vNormal (a -.- b))
|
|
{- | A triangular wedge thick at the first point and
|
|
- tapering off to the second. -}
|
|
wedgeGeom
|
|
:: Float -- Thickness
|
|
-> Point2
|
|
-> Point2
|
|
-> [Point2]
|
|
wedgeGeom t x y
|
|
| x == y = []
|
|
| otherwise = [x +.+ n x y, x -.- n x y, y]
|
|
where
|
|
n a b = (t*0.5) *.* errorNormalizeV 4200 (vNormal (a -.- b))
|
|
|
|
insertInZoneWith
|
|
:: Int -- ^ First Key
|
|
-> Int -- ^ Second Key
|
|
-> (a -> a -> a) -- ^ Combining function
|
|
-> a -- ^ Value to insert
|
|
-> IM.IntMap (IM.IntMap a)
|
|
-> IM.IntMap (IM.IntMap a)
|
|
insertInZoneWith x y fun obj = IM.insertWith f x $ IM.singleton y obj
|
|
where
|
|
f _ = IM.insertWith fun y obj
|
|
{- | I believe this overwrites the value if it already exists, but not sure. -}
|
|
insertIMInZone
|
|
:: Int -- ^ First key
|
|
-> Int -- ^ Second key
|
|
-> Int -- ^ Third key
|
|
-> a -- ^ Item to insert
|
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
|
insertIMInZone x y obid obj = IM.insertWith f x $ IM.singleton y $ IM.singleton obid obj
|
|
where
|
|
f _ = IM.insertWith g y $ IM.singleton obid obj
|
|
g _ = IM.insert obid obj
|
|
adjustIMZone
|
|
:: (a -> a) -- ^ Update function
|
|
-> Int -- ^ First key
|
|
-> Int -- ^ Second key
|
|
-> Int -- ^ Third key
|
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
|
adjustIMZone f x y n = IM.adjust f' x
|
|
where
|
|
f' = IM.adjust f'' y
|
|
f'' = IM.adjust f n
|
|
{- | Finds unused projectile key. -}
|
|
newProjectileKey :: World -> Int
|
|
newProjectileKey = IM.newKey . _projectiles
|
|
{- | Finds unused creature key. -}
|
|
newCrKey :: World -> Int
|
|
newCrKey = IM.newKey . _creatures
|
|
{- | TODO: determine precisely what this does. -}
|
|
reflectPointCreature :: Point2 -> Point2 -> Creature -> Maybe (Point2, Point2, Int)
|
|
reflectPointCreature p1 p2 cr = case collidePointCirc p1 p2 (_crRad cr) (_crPos cr) of
|
|
Nothing -> Nothing
|
|
Just _ -> Just
|
|
( p1
|
|
, errorNormalizeV 35 (ssaTriPoint p2 (_crPos cr) p1 (_crRad cr) -.- _crPos cr)
|
|
+.+ (_crPos cr -.- _crOldPos cr)
|
|
, _crID cr)
|
|
{- | TODO: determine precisely what this does. -}
|
|
reflectPointCreatures :: Point2 -> Point2 -> IM.IntMap Creature -> Maybe (Point2,Point2,Int)
|
|
reflectPointCreatures p1 p2 cs
|
|
= safeMinimumOn f
|
|
$ IM.mapMaybe (reflectPointCreature p1 p2) cs
|
|
where
|
|
f (a,_,_) = magV (a -.- p1)
|
|
{- | TODO: determine precisely what this does. -}
|
|
reflectCircCreature
|
|
:: Float -- ^ Radius
|
|
-> Point2 -- ^ Start point
|
|
-> Point2 -- ^ End point
|
|
-> Creature
|
|
-> Maybe (Point2, Point2, Int)
|
|
reflectCircCreature rad p1 p2 cr = case collidePointCirc p1 p2 (rad + _crRad cr) (_crPos cr) of
|
|
Nothing -> Nothing
|
|
Just _ -> Just
|
|
( p1
|
|
, errorNormalizeV 37 (ssaTriPoint p2 (_crPos cr) p1 (_crRad cr) -.- _crPos cr)
|
|
+.+ (_crPos cr -.- _crOldPos cr)
|
|
, _crID cr
|
|
)
|
|
{- | TODO: determine precisely what this does. -}
|
|
reflectCircCreatures
|
|
:: Float -- ^ Radius
|
|
-> Point2 -- ^ Start point
|
|
-> Point2 -- ^ End point
|
|
-> IM.IntMap Creature
|
|
-> Maybe (Point2,Point2,Int)
|
|
reflectCircCreatures rad p1 p2 cs
|
|
= safeMinimumOn f
|
|
$ IM.mapMaybe (reflectCircCreature rad p1 p2) cs
|
|
where
|
|
f (a,_,_) = magV (a -.- p1)
|
|
|
|
-- | collides a point with forcefields
|
|
-- if found, returns point of collision, deflection if required, and the id
|
|
collidePointFFs :: a
|
|
collidePointFFs = undefined
|
|
collidePointFF :: a
|
|
collidePointFF = undefined
|
|
--
|
|
-- | Looks for overlap of a circle with walls.
|
|
-- If found, gives wall
|
|
overlapCircWallsReturnWall :: Point2 -> Float -> IM.IntMap Wall -> Maybe Wall
|
|
overlapCircWallsReturnWall p rad
|
|
= L.fold (safeMinimumOnMaybeL (fmap (dist p) . f . _wlLine))
|
|
where
|
|
f (a,b) = intersectSegSeg p (p -.- rad *.* vNormal (normalizeV (a -.- b))) a b
|
|
|
|
-- | Looks for any collision of a circle with walls.
|
|
-- If found, gives point and reflection velocity, reflection damped in normal.
|
|
-- note that in this version the circle can overlap the wall
|
|
collidePointAnyWalls :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
|
collidePointAnyWalls p1 p2
|
|
= getFirst
|
|
. foldMap (First . findPoint . _wlLine)
|
|
where
|
|
findPoint (x,y) = case intersectSegSeg p1 p2 x y of
|
|
Just ip -> Just (ip +.+ normalizeV (vNormal (x -.- y)), reflectInParam 0.5 (x -.- y) (p2 -.- p1))
|
|
Nothing -> Nothing
|
|
|
|
-- | Looks for first collision of a circle with walls.
|
|
-- If found, gives point and reflection velocity, reflection damped in normal.
|
|
-- note that the "intersection" point is the center of the circle flush against the wall
|
|
collideCircWalls' :: Point2 -> Point2 -> Float -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
|
collideCircWalls' p1 p2 rad = either (const Nothing) Just . foldr findPoint (Left p2)
|
|
where
|
|
findPoint wl eip = maybe eip Right $ doReflection (getp eip) $ shiftByRad $ _wlLine wl
|
|
getp (Left p) = p
|
|
getp (Right (p,_)) = p
|
|
doReflection p (x,y) = case intersectSegSeg p1 p x y of
|
|
Nothing -> Nothing
|
|
Just ip -> Just (ip +.+ normalizeV (vNormal (x -.- y)), reflectInParam 0.5 (x -.- y) (p2 -.- p1))
|
|
shiftByRad (a,b) =
|
|
(g $ a +.+ rad *.* normalizeV (a -.-b)
|
|
,g $ b +.+ rad *.* normalizeV (b -.-a)
|
|
)
|
|
where
|
|
g = ((rad *.* normalizeV (vNormal $ a -.- b)) +.+)
|
|
|
|
-- | Looks for first collision of a circle with walls.
|
|
-- If found, gives point and reflection velocity, reflection damped in normal.
|
|
collideCircWalls'' :: Point2 -> Point2 -> Float -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
|
collideCircWalls'' p1 p2 rad ws
|
|
= safeMinimumOn (dist p1 . fst)
|
|
$ IM.mapMaybe
|
|
(( \(x:y:_) -> fmap
|
|
((, reflectInParam 0.5 (x -.- y) (p2 -.- p1))
|
|
. (+.+ normalizeV (vNormal (x -.- y)))
|
|
)
|
|
(intersectSegSeg p1 p2 x y)
|
|
)
|
|
. shiftByRad . _wlLine
|
|
) ws
|
|
where
|
|
shiftByRad (a,b) = map ((rad *.* normalizeV (vNormal $ a -.- b)) +.+)
|
|
[a +.+ rad *.* normalizeV (a -.-b)
|
|
,b +.+ rad *.* normalizeV (b -.-a)
|
|
]
|
|
-- this shifts the wall out, and for outer corners extends the wall
|
|
-- not sure what this does for inner corners, hopefully won't cause a problem
|
|
-- the alternative would be to separately bounce off corner points...
|
|
-- unfortunately, doesn't allow for collisions when the circle spawns on the
|
|
-- wall
|
|
|
|
-- | Looks for first collision of a point with a list of lines.
|
|
-- If found, gives point and normal of wall.
|
|
collidePointLines :: Point2 -> Point2 -> [Wall'] -> Maybe (Point2,Point2)
|
|
collidePointLines p1 p2 ws
|
|
= safeMinimumOn f
|
|
$ mapMaybe (( \(x,y) -> intersectSegSeg p1 p2 x y <&> ( , vNormal $ x -.- y ) )
|
|
. _wlLine') ws
|
|
where
|
|
f (a,_) = magV (p1 -.- a)
|
|
|
|
-- | Looks for first collision of a point with walls.
|
|
-- If found, gives point and normal of wall.
|
|
collidePointWallsNorm :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
|
collidePointWallsNorm p1 p2 ws
|
|
= safeMinimumOn f
|
|
$ IM.mapMaybe (( \(x,y) -> intersectSegSeg p1 p2 x y <&> ( , vNormal $ x -.- y ) )
|
|
. _wlLine) ws
|
|
where
|
|
f (a,_) = magV (p1 -.- a)
|
|
-- | Returns the first creature, if any, that a point intersects with.
|
|
collidePointCreatures :: Point2 -> Point2 -> World -> Maybe Int
|
|
collidePointCreatures p1 p2 w
|
|
= fmap fst
|
|
. safeMinimumOn snd
|
|
. IM.toList
|
|
. IM.mapMaybe (\x -> collidePointCirc' p1 p2 (_crRad x) (_crPos x))
|
|
$_creatures w
|
|
-- | As for 'collidePointCreatures', only increases the radius of creatures by a
|
|
--fixed amount, thus collides a moving circle with creaures.
|
|
collideCircCreatures :: Point2 -> Point2 -> Float -> World -> Maybe Int
|
|
collideCircCreatures p1 p2 rad w
|
|
= fmap fst
|
|
. safeMinimumOn snd
|
|
. IM.toList
|
|
. IM.mapMaybe (\x -> collidePointCirc' p1 p2 (rad + _crRad x) (_crPos x))
|
|
$ _creatures w
|
|
-- | Returns the first creature id, if any, that a point intersects with, gives point
|
|
--in creature on line.
|
|
collidePointCrsPoint :: Point2 -> Point2 -> World -> Maybe (Point2,Int)
|
|
collidePointCrsPoint p1 p2 w
|
|
= fmap f
|
|
. safeMinimumOn (snd . snd)
|
|
. IM.toList
|
|
. IM.mapMaybe (\x -> collidePointCirc'' p1 p2 (_crRad x) (_crPos x))
|
|
$ _creatures w
|
|
where
|
|
f (cID,(p,_)) = (p,cID)
|
|
{- | Finds the first creature hit on a line.
|
|
Maybe evaluates the creature id and hit point. -}
|
|
collideCircCrsPoint :: Point2 -> Point2 -> Float -> World -> Maybe (Point2,Int)
|
|
collideCircCrsPoint p1 p2 rad w
|
|
= fmap f
|
|
. safeMinimumOn (snd . snd)
|
|
. IM.toList
|
|
. IM.mapMaybe (\x -> collidePointCirc'' p1 p2 (rad + _crRad x) (_crPos x))
|
|
$ _creatures w
|
|
where
|
|
f (cID,(p,_)) = (p,cID)
|
|
-- | Makes a creature not hittable.
|
|
collidePointCrsWithoutPoint :: Int -> Point2 -> Point2 -> World -> Maybe (Point2,Int)
|
|
collidePointCrsWithoutPoint cid p1 p2 w
|
|
= fmap f
|
|
. safeMinimumOn (snd . snd)
|
|
. IM.toList
|
|
. IM.mapMaybe (\x -> collidePointCirc'' p1 p2 (_crRad x) (_crPos x))
|
|
. IM.delete cid
|
|
$ _creatures w
|
|
where
|
|
f (cID,(p,_)) = (p,cID)
|
|
{- | Test if a circle collides with any wall. -}
|
|
circOnSomeWall :: Point2 -> Float -> World -> Bool
|
|
circOnSomeWall p rad w
|
|
= any (\(x,y) -> circOnSeg x y p rad)
|
|
. fmap _wlLine
|
|
. IM.elems
|
|
$ wallsNearPoint p w
|
|
{- | Test whether there is a creature of weight 4 or greater near a line. -}
|
|
isHeavyCrNearLine
|
|
:: Float
|
|
-> [Point2]
|
|
-> World
|
|
-> Bool
|
|
isHeavyCrNearLine d (p1:p2:_) w
|
|
= any (\c -> circOnSeg p1 p2 (_crPos c) (d + _crRad c))
|
|
. IM.filter (\cr -> _crMass cr > 4)
|
|
$ _creatures w
|
|
isHeavyCrNearLine _ _ _ = error "Testing whether creature is near empty line"
|
|
{- | Adds the distance to the creature radius, tests whether the center is in
|
|
the circle of this size centered at the point -}
|
|
crsNearPoint :: Float -> Point2 -> World -> Bool
|
|
crsNearPoint d p w = any (\c -> dist (_crPos c) p < (d + _crRad c)) (_creatures w)
|
|
{- | Produce an unordered list of creatures on a line. -}
|
|
crsOnLine :: Point2 -> Point2 -> World -> [Creature]
|
|
crsOnLine p1 p2 w
|
|
= IM.elems
|
|
. IM.filter (\cr -> circOnSeg p1 p2 (_crPos cr) (_crRad cr))
|
|
$ _creatures w
|
|
{- | Produce an unordered list of creatures on a wide line. -}
|
|
crsOnThickLine :: Float -> Point2 -> Point2 -> World -> [Creature]
|
|
crsOnThickLine thickness p1 p2 w
|
|
= IM.elems
|
|
. IM.filter (\cr -> circOnSeg p1 p2 (_crPos cr) (_crRad cr + thickness))
|
|
$ _creatures w
|
|
{- | Find 'Maybe' the closest creature to a point, within a circle.
|
|
-}
|
|
nearestCrInRad :: Point2 -> Float -> World -> Maybe Creature
|
|
nearestCrInRad p r w
|
|
= safeMinimumOn (dist p . _crPos)
|
|
$ IM.filter (\cr -> dist p (_crPos cr) < r) $ _creatures w
|
|
{- | Find 'Maybe' the closest creature in front of a point in a right-angle-triangle shape. -}
|
|
nearestCrInTri
|
|
:: Point2
|
|
-> Float -- ^ Direction (radians +ve anticlockwise from x-axis).
|
|
-> Float -- ^ Distance.
|
|
-> World -> Maybe Creature
|
|
nearestCrInTri p dir x w
|
|
= safeMinimumOn (dist p . _crPos)
|
|
$ IM.filter (\cr -> pointInPolygon (_crPos cr) tri) $ _creatures w
|
|
where
|
|
tri =
|
|
[p
|
|
,p +.+ rotateV (dir-pi/4) (V2 x 0)
|
|
,p +.+ rotateV (dir+pi/4) (V2 x 0)
|
|
]
|
|
{- | Find 'Maybe' the closes creature in front of a point in a given direction for
|
|
a given distance.
|
|
The shapes within which creatures are searched are a triangle then rectangle. -}
|
|
nearestCrInFront
|
|
:: Point2
|
|
-> Float -- ^ Direction (radians +ve anticlockwise from x-axis).
|
|
-> Float -- ^ Distance.
|
|
-> World -> Maybe Creature
|
|
nearestCrInFront p dir x w
|
|
= safeMinimumOn (dist p . _crPos)
|
|
$ IM.filter (\cr -> pointInPolygon (_crPos cr) rec) $ _creatures w
|
|
where
|
|
rec = [p, pR, pR1, pL1, pL ]
|
|
pR = p +.+ rotateV (dir - pi*(3/8)) (V2 (x/2) 0)
|
|
pL = p +.+ rotateV (dir + pi*(3/8)) (V2 (x/2) 0)
|
|
pR1 = pR +.+ rotateV dir (V2 (x/2) 0)
|
|
pL1 = pL +.+ rotateV dir (V2 (x/2) 0)
|
|
{- | Test whether a creature is in a polygon. -}
|
|
crInPolygon :: Creature -> [Point2] -> Bool
|
|
crInPolygon cr = pointInPolygon (_crPos cr)
|
|
|
|
{- | Transform coordinates from world position to screen coordinates. -}
|
|
worldPosToScreenNorm :: World -> Point2 -> Point2
|
|
worldPosToScreenNorm w = doWindowScale . doRotate . doZoom . doTranslate
|
|
where
|
|
doTranslate p = p -.- _cameraCenter w
|
|
doZoom p = _cameraZoom w *.* p
|
|
doRotate p = rotateV (negate $ _cameraRot w) p
|
|
doWindowScale (V2 x y) = V2
|
|
( x * 2 / getWindowX w)
|
|
( y * 2 / getWindowY w)
|
|
{- | Transform world coordinates to scaled screen coordinates.
|
|
- These have to be according to the size of the window to get actual screen positions.
|
|
- This allows for line thicknesses etc to correspond to pixel sizes.-}
|
|
worldPosToScreen :: World -> Point2 -> Point2
|
|
worldPosToScreen w = doRotate . doZoom . doTranslate
|
|
where
|
|
doTranslate p = p -.- _cameraCenter w
|
|
doZoom p = _cameraZoom w *.* p
|
|
doRotate p = rotateV (negate $ _cameraRot w) p
|
|
{- | Transform coordinates from the map position to screen
|
|
coordinates. -}
|
|
cartePosToScreen :: World -> Point2 -> Point2
|
|
cartePosToScreen w = doWindowScale . doRotate . doZoom . doTranslate
|
|
where
|
|
doTranslate p = p -.- _carteCenter w
|
|
doZoom p = _carteZoom w *.* p
|
|
doRotate p = rotateV (negate $ _carteRot w) p
|
|
doWindowScale (V2 x y) = V2
|
|
( x * 2 / getWindowX w)
|
|
( y * 2 / getWindowY w)
|
|
{- | The mouse position in world coordinates. -}
|
|
mouseWorldPos :: World -> Point2
|
|
mouseWorldPos w = _cameraCenter w +.+ (1/_cameraZoom w) *.* rotateV (_cameraRot w) (_mousePos w)
|
|
{- | The mouse position in map coordinates -}
|
|
mouseCartePos :: World -> Point2
|
|
mouseCartePos w = _carteCenter w +.+ (1/_carteZoom w) *.* rotateV (_carteRot w) (_mousePos w)
|
|
{- | Create a logistic function given three parameters. -}
|
|
logistic :: Float -> Float -> Float -> (Float -> Float)
|
|
logistic x0 l k x = l / (1 + exp (k*(x0 - x)))
|
|
{- | given a target and a start point, shift toward the end point by a given
|
|
amount.
|
|
If close enough, end up on the end point -}
|
|
mvPointTowardAtSpeed
|
|
:: Float -- ^ Speed.
|
|
-> Point2 -- ^ End point.
|
|
-> Point2 -- ^ Start point.
|
|
-> Point2
|
|
mvPointTowardAtSpeed !speed !ep !p
|
|
| dist p ep < speed = ep
|
|
| otherwise = p +.+ speed *.* normalizeV (ep -.- p)
|
|
{- | given a target and a start point, shift toward the end point by 1.
|
|
If close enough, end up on the end point -}
|
|
mvPointToward
|
|
:: Point2 -- ^ End point.
|
|
-> Point2 -- ^ Start point.
|
|
-> Point2
|
|
mvPointToward !ep !p
|
|
| dist p ep < 1 = ep
|
|
| otherwise = p +.+ normalizeV (ep -.- p)
|
|
|
|
isAnimate :: Creature -> Bool
|
|
{-# INLINE isAnimate #-}
|
|
isAnimate cr = case _crActionPlan cr of
|
|
Inanimate -> False
|
|
_ -> True
|
|
|
|
sigmoid :: Floating a => a -> a
|
|
sigmoid x = x/sqrt(1+x^(2::Int))
|
|
|
|
normalizeAnglePi :: Float -> Float
|
|
normalizeAnglePi angle
|
|
| normalizeAngle angle > pi = normalizeAngle angle - 2*pi
|
|
| otherwise = normalizeAngle angle
|
|
|
|
-- | Taken from online, splits a list into its even and odd elements
|
|
evenOddSplit :: [a] -> ([a],[a])
|
|
evenOddSplit = foldr f ([],[])
|
|
where
|
|
f a (ls,rs) = (rs, a : ls)
|
|
|
|
dbArg :: (a -> a -> b) -> a -> b
|
|
{-# INLINE dbArg #-}
|
|
dbArg f x = f x x
|