-- | 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)