StateMachine.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // StateMachine.cpp
  24. // Implementation of basic state machine
  25. // Author: Michael S. Booth, January 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/Errors.h"
  28. #include "Common/StateMachine.h"
  29. #include "Common/ThingTemplate.h"
  30. #include "Common/GameState.h"
  31. #include "Common/GlobalData.h"
  32. #include "Common/Xfer.h"
  33. #include "GameLogic/GameLogic.h"
  34. #include "GameLogic/Object.h"
  35. #ifdef _INTERNAL
  36. // for occasional debugging...
  37. //#pragma optimize("", off)
  38. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  39. #endif
  40. //------------------------------------------------------------------------------ Performance Timers
  41. //#include "Common/PerfMetrics.h"
  42. //#include "Common/PerfTimer.h"
  43. //static PerfTimer s_stateMachineTimer("StateMachine::update", false, PERFMETRICS_LOGIC_STARTFRAME, PERFMETRICS_LOGIC_STOPFRAME);
  44. //-------------------------------------------------------------------------------------------------
  45. //-----------------------------------------------------------------------------
  46. /**
  47. * Constructor
  48. */
  49. State::State( StateMachine *machine, AsciiString name )
  50. #ifdef STATE_MACHINE_DEBUG
  51. : m_name(name)
  52. #endif
  53. {
  54. m_ID = INVALID_STATE_ID;
  55. m_successStateID = INVALID_STATE_ID;
  56. m_failureStateID = INVALID_STATE_ID;
  57. m_machine = machine;
  58. }
  59. //-----------------------------------------------------------------------------
  60. /**
  61. * Add another state transition condition for this state
  62. */
  63. void State::friend_onCondition( StateTransFuncPtr test, StateID toStateID, void* userData, const char* description )
  64. {
  65. m_transitions.push_back(TransitionInfo(test, toStateID, userData, description));
  66. }
  67. //-----------------------------------------------------------------------------
  68. class StIncrementer
  69. {
  70. private:
  71. Int& num;
  72. public:
  73. StIncrementer(Int& n) : num(n)
  74. {
  75. ++num;
  76. }
  77. ~StIncrementer()
  78. {
  79. --num;
  80. }
  81. };
  82. #ifdef STATE_MACHINE_DEBUG
  83. //-----------------------------------------------------------------------------
  84. std::vector<StateID> * State::getTransitions( void )
  85. {
  86. std::vector<StateID> *ids = new std::vector<StateID>;
  87. ids->push_back(m_successStateID);
  88. ids->push_back(m_failureStateID);
  89. // check transition condition list
  90. if (!m_transitions.empty())
  91. {
  92. for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
  93. {
  94. ids->push_back(it->toStateID);
  95. }
  96. }
  97. return ids;
  98. }
  99. #endif
  100. //-----------------------------------------------------------------------------
  101. /**
  102. * Given a return code, handle state transitions
  103. */
  104. StateReturnType State::friend_checkForTransitions( StateReturnType status )
  105. {
  106. static Int checkfortransitionsnum = 0;
  107. StIncrementer inc(checkfortransitionsnum);
  108. if (checkfortransitionsnum >= 20)
  109. {
  110. DEBUG_CRASH(("checkfortransitionsnum is > 20"));
  111. return STATE_FAILURE;
  112. }
  113. DEBUG_ASSERTCRASH(!IS_STATE_SLEEP(status), ("Please handle sleep states prior to this"));
  114. // handle transitions
  115. switch( status )
  116. {
  117. case STATE_SUCCESS:
  118. // check if machine should exit
  119. if (m_successStateID == EXIT_MACHINE_WITH_SUCCESS)
  120. {
  121. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  122. return STATE_SUCCESS;
  123. }
  124. else if (m_successStateID == EXIT_MACHINE_WITH_FAILURE)
  125. {
  126. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  127. return STATE_FAILURE;
  128. }
  129. // move to new state
  130. return getMachine()->internalSetState( m_successStateID );
  131. case STATE_FAILURE:
  132. // check if machine should exit
  133. if (m_failureStateID == EXIT_MACHINE_WITH_SUCCESS)
  134. {
  135. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  136. return STATE_SUCCESS;
  137. }
  138. else if (m_failureStateID == EXIT_MACHINE_WITH_FAILURE)
  139. {
  140. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  141. return STATE_FAILURE;
  142. }
  143. // move to new state
  144. return getMachine()->internalSetState( m_failureStateID );
  145. case STATE_CONTINUE:
  146. // check transition condition list
  147. if (!m_transitions.empty())
  148. {
  149. for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
  150. {
  151. if (it->test( this, it->userData ))
  152. {
  153. // test returned true, change to associated state
  154. #ifdef STATE_MACHINE_DEBUG
  155. if (getMachine()->getWantsDebugOutput())
  156. {
  157. DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
  158. getMachine()->getName().str(), it->description ? it->description : "[no description]"));
  159. }
  160. #endif
  161. // check if machine should exit
  162. if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
  163. {
  164. return STATE_SUCCESS;
  165. }
  166. else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
  167. {
  168. return STATE_FAILURE;//Lorenzen wants to know why...
  169. }
  170. // move to new state
  171. return getMachine()->internalSetState( it->toStateID );
  172. }
  173. }
  174. }
  175. break;
  176. }
  177. // the machine keeps running
  178. return STATE_CONTINUE;
  179. }
  180. //-----------------------------------------------------------------------------
  181. /**
  182. * Given a return code, handle state transitions
  183. */
  184. StateReturnType State::friend_checkForSleepTransitions( StateReturnType status )
  185. {
  186. static Int checkfortransitionsnum = 0;
  187. StIncrementer inc(checkfortransitionsnum);
  188. if (checkfortransitionsnum >= 20)
  189. {
  190. DEBUG_CRASH(("checkforsleeptransitionsnum is > 20"));
  191. return STATE_FAILURE;
  192. }
  193. DEBUG_ASSERTCRASH(IS_STATE_SLEEP(status), ("Please only pass sleep states here"));
  194. // check transition condition list
  195. if (m_transitions.empty())
  196. return status;
  197. for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
  198. {
  199. if (!it->test( this, it->userData ))
  200. continue;
  201. // test returned true, change to associated state
  202. #ifdef STATE_MACHINE_DEBUG
  203. if (getMachine()->getWantsDebugOutput())
  204. {
  205. DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
  206. getMachine()->getName().str(), it->description ? it->description : "[no description]"));
  207. }
  208. #endif
  209. // check if machine should exit
  210. if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
  211. {
  212. return STATE_SUCCESS;
  213. }
  214. else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
  215. {
  216. return STATE_FAILURE;
  217. }
  218. else
  219. {
  220. // move to new state
  221. return getMachine()->internalSetState( it->toStateID );
  222. }
  223. }
  224. return status;
  225. }
  226. //-----------------------------------------------------------------------------
  227. //-----------------------------------------------------------------------------
  228. //-----------------------------------------------------------------------------
  229. //-----------------------------------------------------------------------------
  230. /**
  231. * Constructor
  232. */
  233. StateMachine::StateMachine( Object *owner, AsciiString name )
  234. {
  235. m_owner = owner;
  236. m_sleepTill = 0;
  237. m_defaultStateID = INVALID_STATE_ID;
  238. m_defaultStateInited = false;
  239. m_currentState = NULL;
  240. m_locked = false;
  241. #ifdef STATE_MACHINE_DEBUG
  242. m_name = name;
  243. m_debugOutput = false;
  244. m_lockedby = NULL;
  245. #endif
  246. internalClear();
  247. }
  248. //-----------------------------------------------------------------------------
  249. /**
  250. * Destructor. Destroy any states attached to this machine.
  251. */
  252. StateMachine::~StateMachine()
  253. {
  254. // do not allow current state to exit
  255. if (m_currentState)
  256. m_currentState->onExit( EXIT_RESET );
  257. std::map<StateID, State *>::iterator i;
  258. // delete all states in the mapping
  259. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
  260. {
  261. if ((*i).second)
  262. (*i).second->deleteInstance();
  263. }
  264. }
  265. //-----------------------------------------------------------------------------
  266. #ifdef STATE_MACHINE_DEBUG
  267. Bool StateMachine::getWantsDebugOutput() const
  268. {
  269. if (m_debugOutput)
  270. {
  271. return true;
  272. }
  273. if (TheGlobalData->m_stateMachineDebug)
  274. {
  275. return true;
  276. }
  277. #ifdef DEBUG_OBJECT_ID_EXISTS
  278. if (TheObjectIDToDebug != 0 && getOwner() != NULL && getOwner()->getID() == TheObjectIDToDebug)
  279. {
  280. return true;
  281. }
  282. #endif
  283. return false;
  284. }
  285. #endif
  286. //-----------------------------------------------------------------------------
  287. /**
  288. * Clear the internal variables of state machine to known values.
  289. */
  290. void StateMachine::internalClear()
  291. {
  292. m_goalObjectID = INVALID_ID;
  293. m_goalPosition.x = 0.0f;
  294. m_goalPosition.y = 0.0f;
  295. m_goalPosition.z = 0.0f;
  296. #ifdef STATE_MACHINE_DEBUG
  297. if (getWantsDebugOutput())
  298. {
  299. DEBUG_LOG(("%d '%s'%x -- '%s' %x internalClear()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_owner, m_name.str(), this));
  300. }
  301. #endif
  302. }
  303. //-----------------------------------------------------------------------------
  304. /**
  305. * Clear the machine
  306. */
  307. void StateMachine::clear()
  308. {
  309. // if the machine is locked, it cannot be cleared
  310. if (m_locked)
  311. {
  312. #ifdef STATE_MACHINE_DEBUG
  313. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  314. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  315. #endif
  316. return;
  317. }
  318. // invoke the old state's onExit()
  319. if (m_currentState)
  320. m_currentState->onExit( EXIT_RESET );
  321. m_currentState = NULL;
  322. internalClear();
  323. }
  324. //-----------------------------------------------------------------------------
  325. /**
  326. * Reset the machine to its default state
  327. */
  328. StateReturnType StateMachine::resetToDefaultState()
  329. {
  330. // if the machine is locked, it cannot be reset
  331. if (m_locked)
  332. {
  333. #ifdef STATE_MACHINE_DEBUG
  334. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  335. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  336. #endif
  337. return STATE_FAILURE;
  338. }
  339. if (!m_defaultStateInited)
  340. {
  341. DEBUG_CRASH(("you may not call resetToDefaultState before initDefaultState"));
  342. return STATE_FAILURE;
  343. }
  344. // allow current state to exit with EXIT_RESET if present
  345. if (m_currentState)
  346. m_currentState->onExit( EXIT_RESET );
  347. m_currentState = NULL;
  348. //
  349. // the current state has done an onExit, clear the internal guts before we set
  350. // the new state, to clear it after the new state is set might be overwriting
  351. // things the new state transition causes to happen
  352. //
  353. internalClear();
  354. // change to the default state
  355. StateReturnType status = internalSetState( m_defaultStateID );
  356. DEBUG_ASSERTCRASH( status != STATE_FAILURE, ( "StateMachine::resetToDefaultState() Error setting default state" ) );
  357. return status;
  358. }
  359. //-----------------------------------------------------------------------------
  360. /**
  361. * Run one step of the machine
  362. */
  363. StateReturnType StateMachine::updateStateMachine()
  364. {
  365. UnsignedInt now = TheGameLogic->getFrame();
  366. if (m_sleepTill != 0 && now < m_sleepTill)
  367. {
  368. if( m_currentState == NULL )
  369. {
  370. return STATE_FAILURE;
  371. }
  372. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  373. }
  374. // not sleeping anymore
  375. m_sleepTill = 0;
  376. if (m_currentState)
  377. {
  378. // update() can change m_currentState, so save it for a moment...
  379. State* stateBeforeUpdate = m_currentState;
  380. // execute this state
  381. StateReturnType status = m_currentState->update();
  382. // it is possible that the state's update() method may cause the state to be destroyed
  383. if (m_currentState == NULL)
  384. {
  385. return STATE_FAILURE;
  386. }
  387. // here's the scenario:
  388. // -- State A calls foo() and then says "sleep for 2000 frames".
  389. // -- however, foo() called setState() to State B. thus our current state is not the same.
  390. // -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
  391. // so that the new state will be called immediately.
  392. if (stateBeforeUpdate != m_currentState)
  393. {
  394. status = STATE_CONTINUE;
  395. }
  396. if (IS_STATE_SLEEP(status))
  397. {
  398. // hey, we're sleepy!
  399. m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
  400. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  401. }
  402. else
  403. {
  404. // check for state transitions, possibly exiting this machine
  405. return m_currentState->friend_checkForTransitions( status );
  406. }
  407. }
  408. else
  409. {
  410. DEBUG_CRASH(("State machine has no current state -- did you remember to call initDefaultState?"));
  411. return STATE_FAILURE;
  412. }
  413. }
  414. //-----------------------------------------------------------------------------
  415. /**
  416. * Given a unique (for this machine) id number, and an instance of the
  417. * State class, the machine records this as a possible state, and
  418. * retains the id mapping.
  419. * These state id's are used to change the machine's state via setState().
  420. */
  421. void StateMachine::defineState( StateID id, State *state, StateID successID, StateID failureID, const StateConditionInfo* conditions )
  422. {
  423. #ifdef STATE_MACHINE_DEBUG
  424. DEBUG_ASSERTCRASH(m_stateMap.find( id ) == m_stateMap.end(), ("duplicate state ID in statemachine %s\n",m_name.str()));
  425. #endif
  426. // map the ID to the state
  427. m_stateMap.insert( std::map<StateID, State *>::value_type( id, state ) );
  428. // store the ID in the state itself, as well
  429. state->friend_setID( id );
  430. state->friend_onSuccess(successID);
  431. state->friend_onFailure(failureID);
  432. while (conditions && conditions->test != NULL)
  433. {
  434. state->friend_onCondition(conditions->test, conditions->toStateID, conditions->userData);
  435. ++conditions;
  436. }
  437. if (m_defaultStateID == INVALID_STATE_ID)
  438. m_defaultStateID = id;
  439. }
  440. //-----------------------------------------------------------------------------
  441. /**
  442. * Given a state ID, return the state instance
  443. */
  444. State *StateMachine::internalGetState( StateID id )
  445. {
  446. // locate the actual state associated with the given ID
  447. std::map<StateID, State *>::iterator i;
  448. i = m_stateMap.find( id );
  449. if (i == m_stateMap.end())
  450. {
  451. DEBUG_CRASH( ("StateMachine::internalGetState(): Invalid state for object %s using state %d", m_owner->getTemplate()->getName().str(), id) );
  452. DEBUG_LOG(("Transisioning to state #d\n", (Int)id));
  453. DEBUG_LOG(("Attempting to recover - locating default state...\n"));
  454. i = m_stateMap.find(m_defaultStateID);
  455. if (i == m_stateMap.end()) {
  456. DEBUG_LOG(("Failed to located default state. Aborting...\n"));
  457. throw ERROR_BAD_ARG;
  458. } else {
  459. DEBUG_LOG(("Located default state to recover.\n"));
  460. }
  461. }
  462. return (*i).second;
  463. }
  464. //-----------------------------------------------------------------------------
  465. /**
  466. * Change the current state of the machine.
  467. * This causes the old state's onExit() method to be invoked,
  468. * and the new state's onEnter() method to be invoked.
  469. */
  470. StateReturnType StateMachine::setState( StateID newStateID )
  471. {
  472. // if the machine is locked, it cannot change state via external events
  473. if (m_locked)
  474. {
  475. #ifdef STATE_MACHINE_DEBUG
  476. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  477. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  478. #endif
  479. return STATE_CONTINUE;
  480. }
  481. return internalSetState( newStateID );
  482. }
  483. //-----------------------------------------------------------------------------
  484. /**
  485. * Change the current state of the machine.
  486. * This causes the old state's onExit() method to be invoked,
  487. * and the new state's onEnter() method to be invoked.
  488. */
  489. StateReturnType StateMachine::internalSetState( StateID newStateID )
  490. {
  491. State *newState = NULL;
  492. // anytime the state changes, stop sleeping
  493. m_sleepTill = 0;
  494. // if we're not setting the "done" state ID we will continue with the actual transition
  495. if( newStateID != MACHINE_DONE_STATE_ID )
  496. {
  497. // if incoming state is invalid, go to the machine's default state
  498. if (newStateID == INVALID_STATE_ID)
  499. {
  500. newStateID = m_defaultStateID;
  501. if (newStateID == INVALID_STATE_ID)
  502. {
  503. DEBUG_CRASH(("you may NEVER set the current state to an invalid state id."));
  504. return STATE_FAILURE;
  505. }
  506. }
  507. // extract the state associated with the given ID
  508. newState = internalGetState( newStateID );
  509. #ifdef STATE_MACHINE_DEBUG
  510. if (getWantsDebugOutput())
  511. {
  512. StateID curState = INVALID_STATE_ID;
  513. if (m_currentState) {
  514. curState = m_currentState->getID();
  515. }
  516. DEBUG_LOG(("%d '%s'%x -- '%s' %x exit ", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_owner, m_name.str(), this));
  517. if (m_currentState) {
  518. DEBUG_LOG((" '%s' ", m_currentState->getName().str()));
  519. } else {
  520. DEBUG_LOG((" INVALID_STATE_ID "));
  521. }
  522. if (newState) {
  523. DEBUG_LOG(("enter '%s' \n", newState->getName().str()));
  524. } else {
  525. DEBUG_LOG(("to INVALID_STATE\n"));
  526. }
  527. }
  528. #endif
  529. }
  530. // invoke the old state's onExit()
  531. if (m_currentState)
  532. m_currentState->onExit( EXIT_NORMAL );
  533. // set the new state
  534. m_currentState = newState;
  535. // invoke the new state's onEnter()
  536. /// @todo It might be useful to pass the old state in... (MSB)
  537. if( m_currentState )
  538. {
  539. // onEnter() could conceivably change m_currentState, so save it for a moment...
  540. State* stateBeforeEnter = m_currentState;
  541. StateReturnType status = m_currentState->onEnter();
  542. // it is possible that the state's onEnter() method may cause the state to be destroyed
  543. if (m_currentState == NULL)
  544. {
  545. return STATE_FAILURE;
  546. }
  547. // here's the scenario:
  548. // -- State A calls foo() and then says "sleep for 2000 frames".
  549. // -- however, foo() called setState() to State B. thus our current state is not the same.
  550. // -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
  551. // so that the new state will be called immediately.
  552. if (stateBeforeEnter != m_currentState)
  553. {
  554. status = STATE_CONTINUE;
  555. }
  556. if (IS_STATE_SLEEP(status))
  557. {
  558. // hey, we're sleepy!
  559. UnsignedInt now = TheGameLogic->getFrame();
  560. m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
  561. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  562. }
  563. else
  564. {
  565. // check for state transitions, possibly exiting this machine
  566. return m_currentState->friend_checkForTransitions( status );
  567. }
  568. }
  569. else
  570. {
  571. return STATE_CONTINUE; // irrelevant return code, but we must return something
  572. }
  573. }
  574. //-----------------------------------------------------------------------------
  575. /**
  576. * Define the default state of the machine, and
  577. * set the machine's state to it.
  578. */
  579. StateReturnType StateMachine::initDefaultState()
  580. {
  581. #if defined(_DEBUG) || defined(_INTERNAL)
  582. #ifdef STATE_MACHINE_DEBUG
  583. #define REALLY_VERBOSE_LOG(x) /* */
  584. // Run through all the transitions and make sure there aren't any transitions to undefined states. jba. [8/18/2003]
  585. std::map<StateID, State *>::iterator i;
  586. REALLY_VERBOSE_LOG(("SM_BEGIN\n"));
  587. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
  588. State *state = (*i).second;
  589. StateID id = state->getID();
  590. // Check transitions. [8/18/2003]
  591. std::vector<StateID> *ids = state->getTransitions();
  592. // check transitions
  593. REALLY_VERBOSE_LOG(("State %s(%d) : ", state->getName().str(), id));
  594. if (!ids->empty())
  595. {
  596. for(std::vector<StateID>::const_iterator it = ids->begin(); it != ids->end(); ++it)
  597. {
  598. StateID curID = *it;
  599. REALLY_VERBOSE_LOG(("%d('", curID));
  600. if (curID == INVALID_STATE_ID) {
  601. REALLY_VERBOSE_LOG(("INVALID_STATE_ID', "));
  602. continue;
  603. }
  604. if (curID == EXIT_MACHINE_WITH_SUCCESS) {
  605. REALLY_VERBOSE_LOG(("EXIT_MACHINE_WITH_SUCCESS', "));
  606. continue;
  607. }
  608. if (curID == EXIT_MACHINE_WITH_FAILURE) {
  609. REALLY_VERBOSE_LOG(("EXIT_MACHINE_WITH_FAILURE', "));
  610. continue;
  611. }
  612. // locate the actual state associated with the given ID
  613. std::map<StateID, State *>::iterator i;
  614. i = m_stateMap.find( curID );
  615. if (i == m_stateMap.end()) {
  616. DEBUG_LOG(("\nState %s(%d) : ", state->getName().str(), id));
  617. DEBUG_LOG(("Transition %d not found\n", curID));
  618. DEBUG_LOG(("This MUST BE FIXED!!!jba\n"));
  619. DEBUG_CRASH(("Invalid transition."));
  620. } else {
  621. State *st = (*i).second;
  622. if (st->getName().isNotEmpty()) {
  623. REALLY_VERBOSE_LOG(("%s') ", st->getName().str()));
  624. }
  625. }
  626. }
  627. }
  628. REALLY_VERBOSE_LOG(("\n"));
  629. delete ids;
  630. ids = NULL;
  631. }
  632. REALLY_VERBOSE_LOG(("SM_END\n\n"));
  633. #endif
  634. #endif
  635. DEBUG_ASSERTCRASH(!m_locked, ("Machine is locked here, but probably should not be"));
  636. if (m_defaultStateInited)
  637. {
  638. DEBUG_CRASH(("you may not call initDefaultState twice for the same StateMachine"));
  639. return STATE_FAILURE;
  640. }
  641. else
  642. {
  643. m_defaultStateInited = true;
  644. return internalSetState( m_defaultStateID );
  645. }
  646. }
  647. //-----------------------------------------------------------------------------
  648. void StateMachine::setGoalObject( const Object *obj )
  649. {
  650. if (m_locked)
  651. return;
  652. internalSetGoalObject( obj );
  653. }
  654. //-----------------------------------------------------------------------------
  655. Bool StateMachine::isGoalObjectDestroyed() const
  656. {
  657. if (m_goalObjectID == 0)
  658. {
  659. return false; // never had a goal object
  660. }
  661. return getGoalObject() == NULL;
  662. }
  663. //-----------------------------------------------------------------------------
  664. void StateMachine::halt()
  665. {
  666. m_locked = true;
  667. m_currentState = NULL; // don't exit current state, just clear it.
  668. #ifdef STATE_MACHINE_DEBUG
  669. if (getWantsDebugOutput())
  670. {
  671. DEBUG_LOG(("%d '%s' -- '%s' %x halt()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
  672. }
  673. #endif
  674. }
  675. //-----------------------------------------------------------------------------
  676. void StateMachine::internalSetGoalObject( const Object *obj )
  677. {
  678. if (obj) {
  679. m_goalObjectID = obj->getID();
  680. internalSetGoalPosition(obj->getPosition());
  681. }
  682. else {
  683. m_goalObjectID = INVALID_ID;
  684. }
  685. }
  686. //-----------------------------------------------------------------------------
  687. Object *StateMachine::getGoalObject()
  688. {
  689. return TheGameLogic->findObjectByID( m_goalObjectID );
  690. }
  691. //-----------------------------------------------------------------------------
  692. const Object *StateMachine::getGoalObject() const
  693. {
  694. return TheGameLogic->findObjectByID( m_goalObjectID );
  695. }
  696. //-----------------------------------------------------------------------------
  697. void StateMachine::setGoalPosition( const Coord3D *pos )
  698. {
  699. if (m_locked)
  700. return;
  701. internalSetGoalPosition( pos );
  702. }
  703. //-----------------------------------------------------------------------------
  704. void StateMachine::internalSetGoalPosition( const Coord3D *pos )
  705. {
  706. if (pos) {
  707. m_goalPosition = *pos;
  708. // Don't clear the goal object, or everything breaks. Like construction of buildings.
  709. }
  710. }
  711. // ------------------------------------------------------------------------------------------------
  712. /** CRC */
  713. // ------------------------------------------------------------------------------------------------
  714. void StateMachine::crc( Xfer *xfer )
  715. {
  716. } // end crc
  717. // ------------------------------------------------------------------------------------------------
  718. /** Xfer Method
  719. * Version Info
  720. * 1: Initial version
  721. */
  722. // ------------------------------------------------------------------------------------------------
  723. void StateMachine::xfer( Xfer *xfer )
  724. {
  725. // version
  726. XferVersion currentVersion = 1;
  727. XferVersion version = currentVersion;
  728. xfer->xferVersion( &version, currentVersion );
  729. xfer->xferUnsignedInt(&m_sleepTill);
  730. xfer->xferUnsignedInt(&m_defaultStateID);
  731. StateID curStateID = getCurrentStateID();
  732. xfer->xferUnsignedInt(&curStateID);
  733. if (xfer->getXferMode() == XFER_LOAD) {
  734. // We are going to jump into the current state. We don't call onEnter or onExit, because the
  735. // state was already active when we saved.
  736. m_currentState = internalGetState( curStateID );
  737. }
  738. Bool snapshotAllStates = false;
  739. #ifdef _DEBUG
  740. //snapshotAllStates = true;
  741. #endif
  742. xfer->xferBool(&snapshotAllStates);
  743. if (snapshotAllStates) {
  744. std::map<StateID, State *>::iterator i;
  745. // count all states in the mapping
  746. Int count = 0;
  747. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
  748. count++;
  749. Int saveCount = count;
  750. xfer->xferInt(&saveCount);
  751. if (saveCount!=count) {
  752. DEBUG_CRASH(("State count mismatch - %d expected, %d read", count, saveCount));
  753. throw SC_INVALID_DATA;
  754. }
  755. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
  756. State *state = (*i).second;
  757. StateID id = state->getID();
  758. xfer->xferUnsignedInt(&id);
  759. if (id!=state->getID()) {
  760. DEBUG_CRASH(("State ID mismatch - %d expected, %d read", state->getID(), id));
  761. throw SC_INVALID_DATA;
  762. }
  763. if( state == NULL )
  764. {
  765. DEBUG_ASSERTCRASH(state != NULL, ("state was NULL on xfer, trying to heal..."));
  766. // Hmm... too late to find out why we are getting NULL in our state, but if we let it go, we will Throw in xferSnapshot.
  767. state = internalGetState(m_defaultStateID);
  768. }
  769. xfer->xferSnapshot(state);
  770. }
  771. } else {
  772. if( m_currentState == NULL )
  773. {
  774. DEBUG_ASSERTCRASH(m_currentState != NULL, ("currentState was NULL on xfer, trying to heal..."));
  775. // Hmm... too late to find out why we are getting NULL in our state, but if we let it go, we will Throw in xferSnapshot.
  776. m_currentState = internalGetState(m_defaultStateID);
  777. }
  778. xfer->xferSnapshot(m_currentState);
  779. }
  780. xfer->xferObjectID(&m_goalObjectID);
  781. xfer->xferCoord3D(&m_goalPosition);
  782. xfer->xferBool(&m_locked);
  783. xfer->xferBool(&m_defaultStateInited);
  784. } // end xfer
  785. // ------------------------------------------------------------------------------------------------
  786. /** Load post process */
  787. // ------------------------------------------------------------------------------------------------
  788. void StateMachine::loadPostProcess( void )
  789. {
  790. } // end loadPostProcess