{-| Contains functions for composing units of time and signals that sample from the game clock. -} module FRP.Helm.Time ( -- * Types Time, -- * Composing millisecond, second, minute, hour, inMilliseconds, inSeconds, inMinutes, inHours, fps, -- * Clock State running, delta, delay ) where import Control.Applicative import FRP.Elerea.Simple hiding (delay) import qualified Graphics.UI.SDL as SDL {-| A type describing an amount of time in an arbitary unit. Use the time composing/converting functions to manipulate time values. -} type Time = Double {-| A time value representing one millisecond. -} millisecond :: Time millisecond = 1 {-| A time value representing one second. -} second :: Time second = 1000 {-| A time value representing one minute. -} minute :: Time minute = 60000 {-| A time value representing one hour. -} hour :: Time hour = 3600000 {-| Converts a time value to a fractional value, in milliseconds. -} inMilliseconds :: Time -> Double inMilliseconds n = n {-| Converts a time value to a fractional value, in seconds. -} inSeconds :: Time -> Double inSeconds n = n / second {-| Converts a time value to a fractional value, in minutes. -} inMinutes :: Time -> Double inMinutes n = n / minute {-| Converts a time value to a fractional value, in hours. -} inHours :: Time -> Double inHours n = n / hour {-| Converts a frames-per-second value into a time value. -} fps :: Int -> Time fps n = second / realToFrac n {-| A signal that returns the time that the game has been running for when sampled. -} running :: SignalGen (Signal Time) running = effectful $ realToFrac <$> SDL.getTicks {-| A signal that returns the time since it was last sampled when sampled. -} delta :: SignalGen (Signal Time) delta = running >>= delta' {-| A utility function that does the real magic for 'delta'. -} delta' :: Signal Time -> SignalGen (Signal Time) delta' t = (fmap . fmap) snd $ transfer (0, 0) (\t2 (t1, _) -> (t2, t2 - t1)) t {-| A signal that blocks the game thread for a certain amount of time when sampled and then returns the amount of time it blocked for. Please note that delaying by values smaller than 1 millisecond can have platform-specific results. -} delay :: Time -> SignalGen (Signal Time) delay t = effectful $ do before <- SDL.getTicks SDL.delay fixed realToFrac <$> flip (-) before <$> SDL.getTicks where fixed = max 0 $ round t