1 /**
2  * This is a state machine for an entity. The state machine manages a set of states,
3  * each of which has a set of component providers. When the state machine changes the state, it removes
4  * components associated with the previous state and adds components associated with the new state.
5  */
6 module ashd.fsm.entityStateMachine;
7 
8 
9 import ashd.core.entity           : Entity;
10 import ashd.fsm.entityState       : EntityState;
11 import ashd.fsm.IComponentProvider: IComponentProvider;
12 import ashd.fsm.ISystemProvider   : ISystemProvider;
13 
14 
15 public class EntityStateMachine
16 {
17     private
18     {
19         Entity              mEntity;            // The entity whose state machine this is
20         EntityState[string] mStates;            // available states
21         EntityState         mCurrentState;      // The current state of the state machine.
22     }
23 
24     /**
25      * Constructor. Creates an EntityStateMachine.
26      */
27     public this( Entity entity_a )
28     {
29         mEntity = entity_a;
30     }
31 
32     /**
33      * Add a state to this state machine.
34      * 
35      * @param name The name of this state - used to identify it later in the changeState method call.
36      * @param state The state.
37      * @return This state machine, so methods can be chained.
38      */
39     public EntityStateMachine addState( string name_a, EntityState state_a )
40     {
41         mStates[name_a] = state_a;
42         return this;
43     }
44     
45     /**
46      * Create a new state in this state machine.
47      * 
48      * @param name The name of the new state - used to identify it later in the changeState method call.
49      * @return The new EntityState object that is the state. This will need to be configured with
50      * the appropriate component providers.
51      */
52     public EntityState createState( string name_a )
53     {
54         EntityState state = new EntityState();
55         mStates[name_a] = state;
56         return state;
57     }
58 
59     /**
60      * Change to a new state. The components from the old state will be removed and the components
61      * for the new state will be added.
62      * 
63      * @param name The name of the state to change to.
64      */
65     public void changeState( string name_a )
66     {
67         if ( name_a !in mStates )
68         {
69             throw new Exception( "State '" ~ name_a ~ "' does not exist." );
70         }
71 
72         EntityState newState = mStates[name_a];
73         if ( newState == mCurrentState )
74         {
75             newState = null;
76             return;
77         }
78 
79         IComponentProvider[ClassInfo] toAdd = newState.providers();
80 
81         if ( mCurrentState )
82         {
83             foreach ( ClassInfo key, IComponentProvider t; mCurrentState.providers() )
84             {
85                 IComponentProvider other;
86                 if ( key in toAdd )
87                     other = toAdd[key];
88 
89                 if ( other && other.identifier == mCurrentState.providers[key].identifier )
90                 {
91                     toAdd.remove(key);
92                 }
93                 else
94                 {
95                     mEntity.remove( key );
96                 }
97             }
98         }
99 
100         foreach ( ClassInfo key, IComponentProvider provider; toAdd )
101         {
102             mEntity.add( provider.getComponent( key ), key );
103         }
104         mCurrentState = newState;
105     }
106 }