1 /**
2  * Represents a state for an EntityStateMachine. The state contains any number of ComponentProviders which
3  * are used to add components to the entity when this state is entered.
4  */
5 module ashd.fsm.entityState;
6 
7 import std.conv: to;
8 
9 
10 import ashd.core.component                : Component;
11 import ashd.fsm.componentInstanceProvider : ComponentInstanceProvider;
12 import ashd.fsm.componentSingletonProvider: ComponentSingletonProvider;
13 import ashd.fsm.componentTypeProvider     : ComponentTypeProvider;
14 import ashd.fsm.dynamicComponentProvider  : DynamicComponentProvider;
15 import ashd.fsm.IComponentProvider        : IComponentProvider;
16 
17 
18 ///
19 public class EntityState
20 {
21     private
22     {
23         IComponentProvider[ClassInfo] mProviders;
24     }
25 
26 
27     @property IComponentProvider[ClassInfo] providers() { return mProviders; };
28 
29     /**
30      * Add a new ComponentMapping to this state. The mapping is a utility class that is used to
31      * map a component type to the provider that provides the component.
32      * 
33      * @param type The type of component to be mapped
34      * @return The component mapping to use when setting the provider for the component
35      */
36     public StateComponentMapping add( ClassInfo class_a )
37     {
38         return new StateComponentMapping( this, class_a );
39     }
40    
41     /**
42      * Get the ComponentProvider for a particular component type.
43      * 
44      * @param type The type of component to get the provider for
45      * @return The ComponentProvider
46      */
47     public IComponentProvider get( ClassInfo class_a )
48     {
49         if ( class_a in mProviders )
50             return mProviders[ class_a ];
51         else
52             return null;
53     }
54 
55 
56     /**
57      * To determine whether this state has a provider for a specific component type.
58      * 
59      * @param type The type of component to look for a provider for
60      * @return true if there is a provider for the given type, false otherwise
61      */
62     public bool has( ClassInfo class_a )
63     {
64         return ( class_a in mProviders) != null;
65     }
66 
67 }
68 
69 
70 /**
71  * Used by the EntityState class to create the mappings of components to providers via a fluent interface.
72  */
73 private class StateComponentMapping
74 {
75     private
76     {
77         EntityState        mCreatingState;
78         IComponentProvider mProvider;
79         ClassInfo          mComponentType;
80     }
81 
82     /**
83      * Used internally, the constructor creates a component mapping. The constructor
84      * creates a ComponentTypeProvider as the default mapping, which will be replaced
85      * by more specific mappings if other methods are called.
86      *
87      * @param creatingState The EntityState that the mapping will belong to
88      * @param type The component type for the mapping
89      */
90     public this( EntityState creatingState_a, ClassInfo class_a )
91     {
92         mCreatingState = creatingState_a;
93         mComponentType = class_a;
94         this.withType( class_a );
95     }
96 
97     /**
98      * Creates a mapping for the component type to a specific component instance. A
99      * ComponentInstanceProvider is used for the mapping.
100      *
101      * @param component The component instance to use for the mapping
102      * @return This ComponentMapping, so more modifications can be applied
103      */
104     public StateComponentMapping withInstance(T)( T component_a )
105     {
106         setProvider( new ComponentInstanceProvider!T( component_a ), T.classinfo );
107         return this;
108     }
109 
110     /**
111      * Creates a mapping for the component type to new instances of the provided type.
112      * The type should be the same as or extend the type for this mapping. A ComponentTypeProvider
113      * is used for the mapping.
114      *
115      * @param type The type of components to be created by this mapping
116      * @return This ComponentMapping, so more modifications can be applied
117      */
118     public StateComponentMapping withType( ClassInfo type_a )
119     {
120         setProvider( new ComponentTypeProvider( type_a ), type_a );
121         return this;
122     }
123 
124     /**
125      * Creates a mapping for the component type to a single instance of the provided type.
126      * The instance is not created until it is first requested. The type should be the same
127      * as or extend the type for this mapping. A ComponentSingletonProvider is used for
128      * the mapping.
129      *
130      * @param The type of the single instance to be created. If omitted, the type of the
131      * mapping is used.
132      * @return This ComponentMapping, so more modifications can be applied
133      */
134     public StateComponentMapping withSingleton( ClassInfo type_a = null )
135     {
136         if ( !type_a )
137         {
138             type_a = mComponentType;
139         }
140         setProvider( new ComponentSingletonProvider( type_a ), type_a );
141         return this;
142     }
143 
144     /**
145      * Creates a mapping for the component type to a method call. A
146      * DynamicComponentProvider is used for the mapping.
147      *
148      * @param method The method to return the component instance
149      * @return This ComponentMapping, so more modifications can be applied
150      */
151     public StateComponentMapping withMethod(T)( T delegate() method_a )
152     {
153         setProvider( new DynamicComponentProvider!T( method_a ), T.classinfo );
154         return this;
155     }
156  
157     /**
158      * Creates a mapping for the component type to any ComponentProvider.
159      *
160      * @param provider The component provider to use.
161      * @return This ComponentMapping, so more modifications can be applied.
162      */
163     public StateComponentMapping withProvider( IComponentProvider provider_a, ClassInfo class_a )
164     {
165         setProvider( provider_a, class_a );
166         return this;
167     }
168 
169     /**
170      * Maps through to the add method of the EntityState that this mapping belongs to
171      * so that a fluent interface can be used when configuring entity states.
172      *
173      * @param type The type of component to add a mapping to the state for
174      * @return The new ComponentMapping for that type
175      */
176     public StateComponentMapping add( ClassInfo class_a )
177     {
178         return mCreatingState.add( class_a );
179     }
180 
181     private void setProvider( IComponentProvider provider_a, ClassInfo class_a )
182     {
183         mProvider = provider_a;
184 
185         mCreatingState.mProviders[ class_a ] = provider_a;
186     }
187 }
188