]> git.mxchange.org Git - simgear.git/commitdiff
StateMachine tweaks.
authorJames Turner <zakalawe@mac.com>
Fri, 25 Jan 2013 08:29:31 +0000 (09:29 +0100)
committerJames Turner <zakalawe@mac.com>
Fri, 25 Jan 2013 08:29:31 +0000 (09:29 +0100)
simgear/structure/StateMachine.cxx
simgear/structure/StateMachine.hxx
simgear/structure/state_machine_test.cxx

index 21dff7e36c13b24519581742906eb2e42a53bae5..e1921739df9b67b0805d8dfb848acf419e77cb26 100644 (file)
@@ -71,6 +71,7 @@ public:
     SGBindingList _bindings;
     std::set<State*> _sourceStates; ///< weak refs to source states
     State* _target;
+    bool _excludeTarget;
     SGSharedPtr<SGCondition> _condition;
 };
     
@@ -175,6 +176,7 @@ StateMachine::Transition::Transition(const std::string& aName, State* aTarget) :
     assert(aTarget);
     d->_name = aName;
     d->_target = aTarget;
+    d->_excludeTarget = true;
 }
 
 StateMachine::Transition::~Transition()
@@ -197,6 +199,13 @@ void StateMachine::Transition::addSourceState(State* aSource)
 
 bool StateMachine::Transition::applicableForState(State* aCurrent) const
 {
+    if (d->_excludeTarget && (aCurrent == d->_target)) {
+        return false;
+    }
+    
+    if (d->_sourceStates.empty()) {
+        return true;
+    }
     return d->_sourceStates.count(aCurrent);
 } 
     
@@ -225,6 +234,11 @@ void StateMachine::Transition::addBinding(SGBinding* aBinding)
     d->_bindings.push_back(aBinding);
 }
     
+void StateMachine::Transition::setExcludeTarget(bool aExclude)
+{
+    d->_excludeTarget = aExclude;
+}
+    
 ///////////////////////////////////////////////////////////////////////////
 
 StateMachine::StateMachine() :
@@ -402,10 +416,13 @@ StateMachine::createTransition(const std::string& aName, State_ptr aTarget)
     return t;
 }
 
-StateMachine* StateMachine::createFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
+void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
 {
-    StateMachine* sm = new StateMachine;
-    
+    std::string path = desc->getStringValue("branch");
+    if (!path.empty()) {
+        d->_root = root->getNode(path, 0, true);
+        assert(d->_root);
+    }
     
     BOOST_FOREACH(SGPropertyNode* stateDesc, desc->getChildren("state")) {
         std::string nm = stateDesc->getStringValue("name");
@@ -415,28 +432,36 @@ StateMachine* StateMachine::createFromPlist(SGPropertyNode* desc, SGPropertyNode
         readBindingList(stateDesc, "exit", root, st->d->_entryBindings);
         readBindingList(stateDesc, "update", root, st->d->_exitBindings);
         
-        sm->addState(st);
+        addState(st);
     } // of states iteration
     
     BOOST_FOREACH(SGPropertyNode* tDesc, desc->getChildren("transition")) {
         std::string nm = tDesc->getStringValue("name");
-        State_ptr target = sm->findStateByName(tDesc->getStringValue("target"));
+        State_ptr target = findStateByName(tDesc->getStringValue("target"));
         
         SGCondition* cond = sgReadCondition(root, tDesc->getChild("condition"));
         
         Transition_ptr t(new Transition(nm, target));
         t->setTriggerCondition(cond);
         
+        t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true));
         BOOST_FOREACH(SGPropertyNode* src, desc->getChildren("source")) {
-            State_ptr srcState = sm->findStateByName(src->getStringValue());
+            State_ptr srcState = findStateByName(src->getStringValue());
             t->addSourceState(srcState);
         }
         
         readBindingList(tDesc, "binding", root, t->d->_bindings);
         
-        sm->addTransition(t);
+        addTransition(t);
     } // of states iteration
     
+    init();
+}
+
+StateMachine* StateMachine::createFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
+{
+    StateMachine* sm = new StateMachine;
+    sm->initFromPlist(desc, root);
     return sm;
 }
 
index 7e9f4be5d0809d9298f6fad39e0e5cbc9c9cd8fb..a382760f999880030bfb794ebb45fcaab5775990 100644 (file)
@@ -74,6 +74,14 @@ public:
         
         std::string name() const;
         
+        /**
+         * Set if the target state should automatically be excluded
+         * from the source state. Defaults to true, can be cleared
+         * to allow a state to re-enter itself
+         */
+        void setExcludeTarget(bool aExclude);
+        
+        
         /**
          * The state we end in, after this transition fires
          */
@@ -116,6 +124,8 @@ public:
     typedef SGSharedPtr<State> State_ptr;
     typedef SGSharedPtr<Transition> Transition_ptr;
     
+    void initFromPlist(SGPropertyNode* desc, SGPropertyNode* root);
+    
     /**
      * create a state machine from a property list description
      */
index a22ae6390dc2fcee340b812df18857e52207c8a0..d1a4d26dcb2b0d60cdfed0dbd6b9f0d7101231f4 100644 (file)
@@ -147,6 +147,27 @@ void testBasic()
     }
 }
 
+void testNoSourcesTransition()
+{
+    BUILD_MACHINE_1();
+
+    DummyCondition* trigger4 = new DummyCondition;
+    StateMachine::Transition_ptr t4 = sm->createTransition("alwaysToA", stateA); \
+    t4->setTriggerCondition(trigger4); \
+    sm->init();
+    
+    COMPARE(sm->state()->name(), "a");
+    trigger1->_state = true;
+    sm->update(1.0);
+    trigger1->_state = false;
+    COMPARE(sm->state()->name(), "b");
+    
+    trigger4->_state = true;
+    sm->update(1.0);
+    trigger4->_state = false;
+    COMPARE(sm->state()->name(), "a");
+}
+
 void testBindings()
 {    
     SGCommandMgr* cmdMgr = SGCommandMgr::instance();
@@ -220,6 +241,8 @@ int main(int argc, char* argv[])
     testBasic();
     testBindings();
     testParse();
+    testNoSourcesTransition();
+    
     cout << __FILE__ << ": All tests passed" << endl;
     return EXIT_SUCCESS;
 }