-- | Ecstasy is a library architected around the -- , the -- gist of which is to define a "template" type that can be reused for several -- purposes. Users of ecstasy should define a record type of 'Component's -- parameterized over a variable of kind 'StorageType': -- -- @ -- data World s = Entity -- { position :: 'Component' s ''Field' (V2 Double) -- , graphics :: 'Component' s ''Field' Graphics -- , isPlayer :: 'Component' s ''Unique' () -- } -- deriving ('Generic') -- @ -- -- Ensure that this type have an instance of 'Generic'. -- -- For usability, it might be desirable to also define the following type -- synonym: -- -- @ -- type Entity = World ''FieldOf' -- @ -- -- which is the only form of the @World@ that most users of ecstasy will -- need to interact with. -- -- Throughout this document there are references to the @HasWorld@ and -- @HasWorld'@ classes, which are implementation details and provided -- automatically by the library. module Data.Ecstasy ( -- * Defining components -- $components ComponentType (..) , Component -- * Storage -- $world , defStorage , StorageType (..) -- * The SystemT monad -- $systemt , SystemT () , runSystemT , yieldSystemT , System , runSystem , SystemState -- * Working with SystemT , createEntity , newEntity , getEntity , setEntity , deleteEntity -- * SystemT traversals -- $traversals , emap , efor , eover , unchanged , delEntity -- * Entity targets , EntTarget , allEnts , someEnts , anEnt -- * The QueryT monad -- $queryt , QueryT () , runQueryT -- * Queries -- $querying , query , with , without , queryEnt , queryMaybe , queryFlag , queryDef -- * Updates , Update (..) , maybeToUpdate -- * Introducing effects , surgery -- * Miscellany , Ent () , VTable (..) -- * Re-exports , Generic () ) where import Data.Ecstasy.Internal hiding (HasWorld, HasWorld') import Data.Ecstasy.Types import GHC.Generics -- $components -- Components are pieces of data that may or may not exist on a particular -- entity. In fact, an 'Ent' is nothing more than an identifier, against which -- components are linked. -- -- Components classified by their 'ComponentType', which describes the -- semantics behind a component. -- -- [@Field@] A 'Field' is a "normal" component and corresponds exactly to -- a 'Maybe' value. -- -- [@Unique@] A 'Unique' component may only exist on a single entity at a given -- time. They are often used to annotate "notable" entites, such as whom the -- camera should be following. -- -- [@Virtual@] A 'Virtual' component is defined in terms of monadic 'vget' and -- 'vset' actions, rather than having dedicated storage in the ECS. Virtual -- components are often used to connect to external systems, eg. to a 3rd party -- physics engine which wants to own its own data. For more information on -- using virtual components, see <#world defStorage>. -- -- -- $world -- #world# 'defStorage' provides a suitable container for storing entity data, to -- be used with 'runSystemT' and friends. If you are not using any 'Virtual' -- components, it can be used directly. -- -- However, when using 'Virtual' components, the 'VTable' for each must be set -- on 'defStorage' before being given as a parameter to 'runSystemT'. For -- example, we can write a virtual 'String' component that writes its updates -- to stdout: -- -- @ -- data World s = Entity -- { stdout :: 'Component' s ''Virtual' String -- } -- deriving ('Generic') -- -- main :: IO () -- main = do -- let storage = 'defStorage' -- { stdout = 'VTable' -- { 'vget' = \\_ -> pure Nothing -- , 'vset' = \\_ m -> for_ m putStrLn -- } -- } -- 'runSystemT' storage $ do -- void $ 'createEntity' 'newEntity' -- { stdout = Just "hello world" -- } -- @ -- -- In this example, if you were to use 'defStorage' rather than @storage@ as the -- argument to 'runSystemT', you would receive the following error: -- -- @unset VTable for Virtual component \'stdout\'@ -- $systemt -- The 'SystemT' transformer provides capabilities for creating, modifying, -- reading and deleting entities, as well as performing <#traversals query -- traversals> over them. It is the main monad of ecstasy. -- $queryt -- The 'QueryT' transformer provides an environment for <#traversals querying> -- components of an entity. Due to its 'Control.Monad.MonadPlus' instance, -- failing queries will prevent further computations in the monad from running. -- $traversals -- #traversals# 'SystemT' provides functionality for traversing over entities -- that match a 'EntTarget' and a <#querying query>. The functions 'emap' and -- 'eover' return a @world 'SetterOf@, corresponding to partial update of the -- targeted entity. -- -- A @world 'SetterOf@ is the world record where all of its selectors have the -- type @'Update' a@. For example, given a world: -- -- @ -- data World s = Entity -- { position :: 'Component' s ''Field' (V2 Double) -- , graphics :: 'Component' s ''Field' Graphics -- , isPlayer :: 'Component' s ''Unique' () -- } -- @ -- -- then @World 'SetterOf@ is equivalent to the following definition: -- -- @ -- data World 'SetterOf = Entity -- { position :: 'Update' (V2 Double) -- , graphics :: 'Update' Graphics -- , isPlayer :: 'Update' () -- } -- @ -- -- 'unchanged' provides a @world 'SetterOf@ which will update no components, -- and can have partial modifications added to it. -- -- 'delEntity' provides a @world 'SetterOf@ which will delete all components -- associated with the targeted entity. -- $querying -- #querying# The 'QueryT' monad provides functionality for performing -- computations over an 'Ent''s components. The basic primitive is 'query', -- which will pull the value of a component, and fail the query if it isn't -- set. -- -- For example, given the following world: -- -- @ -- data World s = Entity -- { position :: 'Component' s ''Field' (V2 Double) -- , velocity :: 'Component' s ''Field' (V2 Double) -- } -- deriving ('Generic') -- @ -- -- we could model a discrete time simulation via: -- -- @ -- stepTime :: 'System' World () -- stepTime = do -- 'emap' 'allEnts' $ do -- pos <- 'query' position -- vel <- 'query' velocity -- pure $ 'unchanged' -- { position = 'Set' $ pos + vel -- } -- @ -- -- which will add an entity's velocity to its position, so long as it has both -- components to begin with.