-- | Contains functions for composing units of time and -- subscriptions to events from the game clock. module Helm.Time ( -- * Types Time -- * Units , millisecond , second , minute , hour , inMilliseconds , inSeconds , inMinutes , inHours -- * Commands , now -- * Subscriptions , every , fps ) where import Control.Monad.State (get) import Control.Monad.IO.Class (liftIO) import FRP.Elerea.Param (transfer, input, snapshot, effectful) import Helm.Engine (Cmd(..), Sub(..), Engine(..)) -- | Represents an amount of time in an arbitary unit. -- This type can then be composed with the relevant utility functions. 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 -- | Map the running time of the engine to a game action. -- Note that this is not the current clock time but rather the engine time, -- i.e. when the engine first starts running, the applied value will be zero. now :: Engine e => (Time -> a) -- ^ The function to map the running time to an action. -> Cmd e a -- ^ The mapped command. now f = Cmd $ do engine <- get ticks <- liftIO $ f <$> runningTime engine return [ticks] -- | Generic subscription for time interval-based events. every' :: Engine e => (e -> Time -> (Time, [Double]) -> (Time, [Double])) -> (Time -> a) -> Sub e a every' step f = Sub $ do engine <- input >>= snapshot time <- effectful $ runningTime engine sig <- transfer (0, []) step time return $ map f . snd <$> sig -- | Subscribe to the running time of the engine and map to a game action, -- producing events at a provided interval. every :: Engine e => Time -- ^ The interval of time to produce events at. -> (Time -> a) -- ^ The function to map the running time to an action. -> Sub e a -- ^ The mapped subscription. every interval = every' step where step _ t (lt, _) = if t - lt >= interval then (t, [t]) -- Use new t value with t event else (lt, []) -- Use old t value with no event -- | Subscribe to events that emit at a provided frames per second and map to a game action, -- producing events at a provided interval. The time value applied is the delta between -- the current and last event emission time. fps :: Engine e => Int -- ^ The frames per second. -> (Time -> a) -- ^ The function to map the time delta to an action. -> Sub e a -- ^ The mapped subscription. fps frames = every' step where interval = 1000 / fromIntegral frames step _ t (lt, _) = if t - lt >= interval then (t, [t - lt]) -- Use new t value with t event else (lt, []) -- Use old t value with no event