nyx-game-0.1.0.0: src/Play/Engine/Movement.hs
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE BangPatterns #-}
module Play.Engine.Movement where
import Play.Engine.Utils
import Play.Engine.Types
import Control.Lens
import Control.DeepSeq
import GHC.Generics
import Data.Typeable
data Movement
= Movement
{ _posFloatPart :: !FPoint
, _speed :: !FPoint
, _minSpeed :: !FPoint
, _maxSpeed :: !FPoint
, _acceleration :: !FPoint
, _accelerationTimer :: !Int
}
deriving (Show, Eq, Generic, Typeable, NFData)
makeFieldsNoPrefix ''Movement
data MakeArgs
= MakeArgs
{ startspeed :: !FPoint
, minspeed :: !FPoint
, maxspeed :: !FPoint
, accel :: !FPoint
}
defArgs :: MakeArgs
defArgs = MakeArgs
{ startspeed = Point 0 0
, minspeed = Point 0 0
, maxspeed = Point 1 1
, accel = Point 0.1 0.1
}
make :: MakeArgs -> Movement
make MakeArgs{..} =
Movement
{ _posFloatPart = Point 0 0
, _speed = startspeed
, _minSpeed = fmap abs minspeed
, _maxSpeed = fmap abs maxspeed
, _acceleration = accel
, _accelerationTimer = 0
}
updateMovement :: FPoint -> Movement -> Movement
updateMovement !direction !mv =
let
updateSpeedC c spd =
let
accel =
if direction ^. c /= 0
then (mv ^. acceleration . c) * (direction ^. c)
else limit (abs spd) $ (mv ^. acceleration . c) * negate (normalize spd)
in
if mv ^. accelerationTimer == 0
then
let
newSpd = limit (mv ^. maxSpeed . c) . (+ accel) $ spd
norm = normalize newSpd * (mv ^. minSpeed . c)
in
if abs newSpd < abs norm
then norm
else newSpd
else spd
in
force $ mv
& over (speed . x) (updateSpeedC x)
& over (speed . y) (updateSpeedC y)
& over accelerationTimer (\t -> if t <= 0 then 1 else t - 1)
limit :: Float -> Float -> Float
limit !lim !n
| abs n > lim = normalize n * lim
| otherwise = n
normalize :: Float -> Float
normalize !n
| n == 0 = 0
| n > 0 = n / n
| otherwise = (-1) * (n / n)
update :: FPoint -> Movement -> (Movement, IPoint)
update !dir !mv =
let
spd = (mv ^. speed) `addPoint` (mv ^. posFloatPart)
addition = fmap (\s -> if s < 0 then ceiling s else floor s) spd
pfp = spd `addPoint` fmap (fromIntegral . negate) addition
in
force (set posFloatPart pfp $ updateMovement dir mv, addition)
straight :: FPoint -> Movement
straight spd = make $ defArgs
{ startspeed = spd
, maxspeed = spd
, accel = Point 1 1 `mulPoint` fmap abs spd
}
fastGradualStart :: FPoint -> Movement
fastGradualStart spd = make $ defArgs
{ startspeed = spd
, maxspeed = fmap (*4) spd
, accel = Point 0.11 0.11 `mulPoint` fmap abs spd
}
gradualSlowdown :: FPoint -> Movement
gradualSlowdown spd = make $ defArgs
{ startspeed = spd
, minspeed = fmap (/4) spd
, maxspeed = spd
, accel = Point (-0.02) (-0.02) `mulPoint` fmap abs spd
}