diff --git a/simgear/structure/StateMachine.cxx b/simgear/structure/StateMachine.cxx index 21dff7e3..e1921739 100644 --- a/simgear/structure/StateMachine.cxx +++ b/simgear/structure/StateMachine.cxx @@ -71,6 +71,7 @@ public: SGBindingList _bindings; std::set _sourceStates; ///< weak refs to source states State* _target; + bool _excludeTarget; SGSharedPtr _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; } diff --git a/simgear/structure/StateMachine.hxx b/simgear/structure/StateMachine.hxx index 7e9f4be5..a382760f 100644 --- a/simgear/structure/StateMachine.hxx +++ b/simgear/structure/StateMachine.hxx @@ -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_ptr; typedef SGSharedPtr Transition_ptr; + void initFromPlist(SGPropertyNode* desc, SGPropertyNode* root); + /** * create a state machine from a property list description */ diff --git a/simgear/structure/state_machine_test.cxx b/simgear/structure/state_machine_test.cxx index a22ae639..d1a4d26d 100644 --- a/simgear/structure/state_machine_test.cxx +++ b/simgear/structure/state_machine_test.cxx @@ -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; }