helm-1.0.0: src/Helm/Engine.hs
-- | Contains the core engine types and classes.
module Helm.Engine (
-- * Typeclasses
Engine(..),
-- * Types
Cmd(..),
GameConfig(..),
Sub(..),
MouseButton(..),
Key(..)
) where
import Control.Monad.Trans.State (StateT)
import FRP.Elerea.Param (SignalGen, Signal)
import Linear.V2 (V2)
import Helm.Graphics (Graphics)
-- | Represents a backend engine that can run a Helm game.
--
-- Helm separates the logic for running a game from the actual interaction with the user -
-- window management, event management (key presses, mouse presses, etc.) are all handled by a specific instance
-- of the engine typeclass. Meanwhile, the game loop and other core features are handled independently
-- by the Helm library itself.
class Engine e where
-- | Renders a graphics element to the engine's game window.
render :: e -> Graphics e -> IO ()
-- | Ticks (or steps) the engine forward. Generally, an engine should use this method to
-- gather any new input events from the underlying engine and sink them into the signals it provides below.
-- Depending on the implementation of the engine, it might be necessary to do other things here too.
tick :: e -> IO (Maybe e)
-- | Cleans up all resources loaded by the engine. This will be run when the engine has stopped execution,
-- hence it should do everything required to free any resources allocated by the engine.
cleanup :: e -> IO ()
-- | Get the game window size.
windowSize :: e -> IO (V2 Int)
-- | Get the current game running time.
runningTime :: e -> IO Double
-- | The mouse move signal, with events provided by the engine.
mouseMoveSignal :: e -> SignalGen e (Signal [V2 Int])
-- | The mouse down signal, with events provided by the engine.
mouseDownSignal :: e -> SignalGen e (Signal [(MouseButton, V2 Int)])
-- | The mouse up signal, with events provided by the engine.
mouseUpSignal :: e -> SignalGen e (Signal [(MouseButton, V2 Int)])
-- | The mouse click signal, with events provided by the engine.
mouseClickSignal :: e -> SignalGen e (Signal [(MouseButton, V2 Int)])
-- | The keyboard down signal, with events provided by the engine.
keyboardDownSignal :: e -> SignalGen e (Signal [Key])
-- | The keyboard up signal, with events provided by the engine.
keyboardUpSignal :: e -> SignalGen e (Signal [Key])
-- | The keyboard press signal, with events provided by the engine.
keyboardPressSignal :: e -> SignalGen e (Signal [Key])
-- | The window resize signal, with events provided by the engine.
windowResizeSignal :: e -> SignalGen e (Signal [V2 Int])
-- | Represents a subscription to a stream of events captured from a user's interaction with the engine.
-- A subscription is best thought of as a collection of events over time - which is the nature of
-- functional reactive programming (the paradigm that Helm bases it's concepts on).
-- Although Helm uses a departed version of the traditional FRP paradigm, it still follows the
-- concept closely and hence an understanding of FRP will allow you to understnad the library easily.
--
-- Functions throughout the Helm library that return a subscription will first let you map the data
-- related to the event you're subscribing to into another form (specifically, a game action).
-- These game actions are then sent to the update function of your game, i.e. the mapped
-- subscription specifies exactly how game events will interact with your game state.
--
-- Here the type variable e is an instance of the 'Engine' typeclass
-- and the variable a is the game action data type used by your game.
newtype Sub e a = Sub (SignalGen e (Signal [a]))
-- | Represents an IO-like monad with knowledge about the state of the game engine. Each command
-- contains a collection of game actions that will be applied to your game's update function to update
-- the game state. This is similar to a subscription in a way, with the difference being that
-- a command does not change over time, but rather is a lazy monad and hence contains a value that
-- from the time of the execution. A good example of the usage of a command vs. a subscription is the game
-- window size - a command would allow you to map the current window size into an action, whereas
-- a subscription would let you subscribe to when the window is resized and then map that event into
-- a game action.
--
-- Just like a subscription, any function that returns a command in the Helm library will
-- first let you map from the original contained value to a game action. It's important
-- to note that commands are **evaluated on the main-thread** - which means they can
-- block the rendering process. *Don't execute long-running monads under commands!*
--
-- Here the type variable e is an instance of the 'Engine' typeclass
-- and the variable a is the game action data type used by your game.
newtype Cmd e a = Cmd (StateT e IO [a])
-- | Represents the configuration for a Helm game.
--
-- The type variable e refers to an instance of the 'Engine' class,
-- m refers to a game model type and a refers to a game action type.
data GameConfig e m a = GameConfig {
-- | Called when the game starts up. The first value in the tuple
-- is the initial game model state and then the second value is an optional
-- command to be run. The command allows you to execute some monads
-- during game startup and build up some game actions before the game begins
-- rendering. A good example would be loading a game configuration file,
-- parsing the file contents and then mapping the parsed contents
-- to relevant game actions.
--
-- If no initial command is required, simply pass 'Cmd.none'
-- for the second tuple value. Alternatively, if there are a number of commands
-- to run, call 'Cmd.batch' to combine them into one.
initialFn :: (m, Cmd e a),
-- | Called whenever a game action is mapped from a command or subscription.
-- This is where the actual implementation of a Helm game is done.
-- The function is given a game model and the mapped action type,
-- and should produce the new game model state based off of the action.
--
-- The first tuple value is the new model state, and then the second
-- is a command that can be run to produce more game actions.
-- By having this command returnable here, you can run additional IO logic
-- based off the game action, and produce more game actions from the result.
--
-- Be very careful with what commands you run in the game update function - most importantly,
-- don't execute long-winding commands or it will block the rendering process!.
-- Helm will try to intelligently queue recursive commands to prevent blocking rendering.
-- However, having a game action that returns a specific command from the update function,
-- which in turn is executed and returns that same game action (which will then in turn return the same command,
-- and so on) is not recommend. The best way to return commands from the update function is to
-- to hide them behind conditionals based off your game state, so that they're not run every update function.
updateFn :: m -> a -> (m, Cmd e a),
-- | The subscriptions for a game. All the input sources required
-- to make the game work should be subscribed to and mapped to the relevant
-- game action type variant.
--
-- If no subscriptions are required (i.e. no user input is required),
-- pass 'Sub.none'. Alternatively, if multiple subscriptions are required
-- use 'Sub.batch' to combine them.
subscriptionsFn :: Sub e a,
-- | Called when the engine is ready to render the game.
-- The function is given the current state of the game model
-- and should produce a graphics value to be rendered to the
-- screen.
--
-- Do not rely on this function being called every game tick -
-- the engine will figure out whether it needs to be called
-- based off window exposure and whether or not the game model
-- has changed since the last render.
viewFn :: m -> Graphics e
}
-- | Represents a mouse button that can be pressed on a mouse.
data MouseButton
= LeftButton
| MiddleButton
| RightButton
| X1Button
| X2Button
| UnknownButton
deriving (Eq, Ord, Read, Show)
-- | Represents a key that can be pressed on the keyboard.
data Key
= ReturnKey
| EscapeKey
| BackspaceKey
| TabKey
| SpaceKey
| ExclaimKey
| QuoteDblKey
| HashKey
| PercentKey
| DollarKey
| AmpersandKey
| QuoteKey
| LeftParenKey
| RightParenKey
| AsteriskKey
| PlusKey
| CommaKey
| MinusKey
| PeriodKey
| SlashKey
| Number0Key
| Number1Key
| Number2Key
| Number3Key
| Number4Key
| Number5Key
| Number6Key
| Number7Key
| Number8Key
| Number9Key
| ColonKey
| SemicolonKey
| LessKey
| EqualsKey
| GreaterKey
| QuestionKey
| AtKey
| LeftBracketKey
| BackslashKey
| RightBracketKey
| CaretKey
| UnderscoreKey
| BackquoteKey
| AKey
| BKey
| CKey
| DKey
| EKey
| FKey
| GKey
| HKey
| IKey
| JKey
| KKey
| LKey
| MKey
| NKey
| OKey
| PKey
| QKey
| RKey
| SKey
| TKey
| UKey
| VKey
| WKey
| XKey
| YKey
| ZKey
| CapsLockKey
| F1Key
| F2Key
| F3Key
| F4Key
| F5Key
| F6Key
| F7Key
| F8Key
| F9Key
| F10Key
| F11Key
| F12Key
| PrintScreenKey
| ScrollLockKey
| PauseKey
| InsertKey
| HomeKey
| PageUpKey
| DeleteKey
| EndKey
| PageDownKey
| RightKey
| LeftKey
| DownKey
| UpKey
| NumLockClearKey
| KeypadDivideKey
| KeypadMultiplyKey
| KeypadMinusKey
| KeypadPlusKey
| KeypadEnterKey
| KeypadNumber1Key
| KeypadNumber2Key
| KeypadNumber3Key
| KeypadNumber4Key
| KeypadNumber5Key
| KeypadNumber6Key
| KeypadNumber7Key
| KeypadNumber8Key
| KeypadNumber9Key
| KeypadNumber0Key
| KeypadPeriodKey
| ApplicationKey
| PowerKey
| KeypadEqualsKey
| F13Key
| F14Key
| F15Key
| F16Key
| F17Key
| F18Key
| F19Key
| F20Key
| F21Key
| F22Key
| F23Key
| F24Key
| ExecuteKey
| HelpKey
| MenuKey
| SelectKey
| StopKey
| AgainKey
| UndoKey
| CutKey
| CopyKey
| PasteKey
| FindKey
| MuteKey
| VolumeUpKey
| VolumeDownKey
| KeypadCommaKey
| KeypadEqualsAS400Key
| AltEraseKey
| SysReqKey
| CancelKey
| ClearKey
| PriorKey
| Return2Key
| SeparatorKey
| OutKey
| OperKey
| ClearAgainKey
| CrSelKey
| ExSelKey
| Keypad00Key
| Keypad000Key
| ThousandsSeparatorKey
| DecimalSeparatorKey
| CurrencyUnitKey
| CurrencySubunitKey
| KeypadLeftParenKey
| KeypadRightParenKey
| KeypadLeftBraceKey
| KeypadRightBraceKey
| KeypadTabKey
| KeypadBackspaceKey
| KeypadAKey
| KeypadBKey
| KeypadCKey
| KeypadDKey
| KeypadEKey
| KeypadFKey
| KeypadXorKey
| KeypadPowerKey
| KeypadPercentKey
| KeypadLessKey
| KeypadGreaterKey
| KeypadAmpersandKey
| KeypadDblAmpersandKey
| KeypadVerticalBarKey
| KeypadDblVerticalBarKey
| KeypadColonKey
| KeypadHashKey
| KeypadSpaceKey
| KeypadAtKey
| KeypadExclamKey
| KeypadMemStoreKey
| KeypadMemRecallKey
| KeypadMemClearKey
| KeypadMemAddKey
| KeypadMemSubtractKey
| KeypadMemMultiplyKey
| KeypadMemDivideKey
| KeypadPlusMinusKey
| KeypadClearKey
| KeypadClearEntryKey
| KeypadBinaryKey
| KeypadOctalKey
| KeypadDecimalKey
| KeypadHexadecimalKey
| LeftCtrlKey
| LeftShiftKey
| LeftAltKey
| LeftGUIKey
| RightCtrlKey
| RightShiftKey
| RightAltKey
| RightGUIKey
| ModeKey
| AudioNextKey
| AudioPrevKey
| AudioStopKey
| AudioPlayKey
| AudioMuteKey
| MediaSelectKey
| WWWKey
| MailKey
| CalculatorKey
| ComputerKey
| ACSearchKey
| ACHomeKey
| ACBackKey
| ACForwardKey
| ACStopKey
| ACRefreshKey
| ACBookmarksKey
| BrightnessDownKey
| BrightnessUpKey
| DisplaySwitchKey
| KeyboardIllumToggleKey
| KeyboardIllumDownKey
| KeyboardIllumUpKey
| EjectKey
| SleepKey
| UnknownKey
deriving (Eq, Ord, Read, Show)