StateMachine.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. /*
  2. ** Command & Conquer Generals(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. //-----------------------------------------------------------------------------
  83. /**
  84. * Given a return code, handle state transitions
  85. */
  86. StateReturnType State::friend_checkForTransitions( StateReturnType status )
  87. {
  88. static Int checkfortransitionsnum = 0;
  89. StIncrementer inc(checkfortransitionsnum);
  90. if (checkfortransitionsnum >= 20)
  91. {
  92. DEBUG_CRASH(("checkfortransitionsnum is > 20"));
  93. return STATE_FAILURE;
  94. }
  95. DEBUG_ASSERTCRASH(!IS_STATE_SLEEP(status), ("Please handle sleep states prior to this"));
  96. // handle transitions
  97. switch( status )
  98. {
  99. case STATE_SUCCESS:
  100. // check if machine should exit
  101. if (m_successStateID == EXIT_MACHINE_WITH_SUCCESS)
  102. {
  103. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  104. return STATE_SUCCESS;
  105. }
  106. else if (m_successStateID == EXIT_MACHINE_WITH_FAILURE)
  107. {
  108. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  109. return STATE_FAILURE;
  110. }
  111. // move to new state
  112. return getMachine()->internalSetState( m_successStateID );
  113. case STATE_FAILURE:
  114. // check if machine should exit
  115. if (m_failureStateID == EXIT_MACHINE_WITH_SUCCESS)
  116. {
  117. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  118. return STATE_SUCCESS;
  119. }
  120. else if (m_failureStateID == EXIT_MACHINE_WITH_FAILURE)
  121. {
  122. getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
  123. return STATE_FAILURE;
  124. }
  125. // move to new state
  126. return getMachine()->internalSetState( m_failureStateID );
  127. case STATE_CONTINUE:
  128. // check transition condition list
  129. if (!m_transitions.empty())
  130. {
  131. for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
  132. {
  133. if (it->test( this, it->userData ))
  134. {
  135. // test returned true, change to associated state
  136. #ifdef STATE_MACHINE_DEBUG
  137. if (getMachine()->getWantsDebugOutput())
  138. {
  139. DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
  140. getMachine()->getName().str(), it->description ? it->description : "[no description]"));
  141. }
  142. #endif
  143. // check if machine should exit
  144. if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
  145. {
  146. return STATE_SUCCESS;
  147. }
  148. else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
  149. {
  150. return STATE_FAILURE;
  151. }
  152. // move to new state
  153. return getMachine()->internalSetState( it->toStateID );
  154. }
  155. }
  156. }
  157. break;
  158. }
  159. // the machine keeps running
  160. return STATE_CONTINUE;
  161. }
  162. //-----------------------------------------------------------------------------
  163. /**
  164. * Given a return code, handle state transitions
  165. */
  166. StateReturnType State::friend_checkForSleepTransitions( StateReturnType status )
  167. {
  168. static Int checkfortransitionsnum = 0;
  169. StIncrementer inc(checkfortransitionsnum);
  170. if (checkfortransitionsnum >= 20)
  171. {
  172. DEBUG_CRASH(("checkforsleeptransitionsnum is > 20"));
  173. return STATE_FAILURE;
  174. }
  175. DEBUG_ASSERTCRASH(IS_STATE_SLEEP(status), ("Please only pass sleep states here"));
  176. // check transition condition list
  177. if (m_transitions.empty())
  178. return status;
  179. for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
  180. {
  181. if (!it->test( this, it->userData ))
  182. continue;
  183. // test returned true, change to associated state
  184. #ifdef STATE_MACHINE_DEBUG
  185. if (getMachine()->getWantsDebugOutput())
  186. {
  187. DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
  188. getMachine()->getName().str(), it->description ? it->description : "[no description]"));
  189. }
  190. #endif
  191. // check if machine should exit
  192. if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
  193. {
  194. return STATE_SUCCESS;
  195. }
  196. else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
  197. {
  198. return STATE_FAILURE;
  199. }
  200. else
  201. {
  202. // move to new state
  203. return getMachine()->internalSetState( it->toStateID );
  204. }
  205. }
  206. return status;
  207. }
  208. //-----------------------------------------------------------------------------
  209. //-----------------------------------------------------------------------------
  210. //-----------------------------------------------------------------------------
  211. //-----------------------------------------------------------------------------
  212. /**
  213. * Constructor
  214. */
  215. StateMachine::StateMachine( Object *owner, AsciiString name )
  216. {
  217. m_owner = owner;
  218. m_sleepTill = 0;
  219. m_defaultStateID = INVALID_STATE_ID;
  220. m_defaultStateInited = false;
  221. m_currentState = NULL;
  222. m_locked = false;
  223. #ifdef STATE_MACHINE_DEBUG
  224. m_name = name;
  225. m_debugOutput = false;
  226. m_lockedby = NULL;
  227. #endif
  228. internalClear();
  229. }
  230. //-----------------------------------------------------------------------------
  231. /**
  232. * Destructor. Destroy any states attached to this machine.
  233. */
  234. StateMachine::~StateMachine()
  235. {
  236. // do not allow current state to exit
  237. if (m_currentState)
  238. m_currentState->onExit( EXIT_RESET );
  239. std::map<StateID, State *>::iterator i;
  240. // delete all states in the mapping
  241. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
  242. {
  243. if ((*i).second)
  244. (*i).second->deleteInstance();
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. #ifdef STATE_MACHINE_DEBUG
  249. Bool StateMachine::getWantsDebugOutput() const
  250. {
  251. if (m_debugOutput)
  252. {
  253. return true;
  254. }
  255. if (TheGlobalData->m_stateMachineDebug)
  256. {
  257. return true;
  258. }
  259. #ifdef DEBUG_OBJECT_ID_EXISTS
  260. if (TheObjectIDToDebug != 0 && getOwner() != NULL && getOwner()->getID() == TheObjectIDToDebug)
  261. {
  262. return true;
  263. }
  264. #endif
  265. return false;
  266. }
  267. #endif
  268. //-----------------------------------------------------------------------------
  269. /**
  270. * Clear the internal variables of state machine to known values.
  271. */
  272. void StateMachine::internalClear()
  273. {
  274. m_goalObjectID = INVALID_ID;
  275. m_goalPosition.x = 0.0f;
  276. m_goalPosition.y = 0.0f;
  277. m_goalPosition.z = 0.0f;
  278. #ifdef STATE_MACHINE_DEBUG
  279. if (getWantsDebugOutput())
  280. {
  281. DEBUG_LOG(("%d '%s' -- '%s' %x internalClear()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
  282. }
  283. #endif
  284. }
  285. //-----------------------------------------------------------------------------
  286. /**
  287. * Clear the machine
  288. */
  289. void StateMachine::clear()
  290. {
  291. // if the machine is locked, it cannot be cleared
  292. if (m_locked)
  293. {
  294. #ifdef STATE_MACHINE_DEBUG
  295. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  296. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  297. #endif
  298. return;
  299. }
  300. // invoke the old state's onExit()
  301. if (m_currentState)
  302. m_currentState->onExit( EXIT_RESET );
  303. m_currentState = NULL;
  304. internalClear();
  305. }
  306. //-----------------------------------------------------------------------------
  307. /**
  308. * Reset the machine to its default state
  309. */
  310. StateReturnType StateMachine::resetToDefaultState()
  311. {
  312. // if the machine is locked, it cannot be reset
  313. if (m_locked)
  314. {
  315. #ifdef STATE_MACHINE_DEBUG
  316. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  317. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  318. #endif
  319. return STATE_FAILURE;
  320. }
  321. if (!m_defaultStateInited)
  322. {
  323. DEBUG_CRASH(("you may not call resetToDefaultState before initDefaultState"));
  324. return STATE_FAILURE;
  325. }
  326. // allow current state to exit with EXIT_RESET if present
  327. if (m_currentState)
  328. m_currentState->onExit( EXIT_RESET );
  329. m_currentState = NULL;
  330. //
  331. // the current state has done an onExit, clear the internal guts before we set
  332. // the new state, to clear it after the new state is set might be overwriting
  333. // things the new state transition causes to happen
  334. //
  335. internalClear();
  336. // change to the default state
  337. StateReturnType status = internalSetState( m_defaultStateID );
  338. DEBUG_ASSERTCRASH( status != STATE_FAILURE, ( "StateMachine::resetToDefaultState() Error setting default state" ) );
  339. return status;
  340. }
  341. //-----------------------------------------------------------------------------
  342. /**
  343. * Run one step of the machine
  344. */
  345. StateReturnType StateMachine::updateStateMachine()
  346. {
  347. UnsignedInt now = TheGameLogic->getFrame();
  348. if (m_sleepTill != 0 && now < m_sleepTill)
  349. {
  350. if( m_currentState == NULL )
  351. {
  352. return STATE_FAILURE;
  353. }
  354. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  355. }
  356. // not sleeping anymore
  357. m_sleepTill = 0;
  358. if (m_currentState)
  359. {
  360. // update() can change m_currentState, so save it for a moment...
  361. State* stateBeforeUpdate = m_currentState;
  362. // execute this state
  363. StateReturnType status = m_currentState->update();
  364. // it is possible that the state's update() method may cause the state to be destroyed
  365. if (m_currentState == NULL)
  366. {
  367. return STATE_FAILURE;
  368. }
  369. // here's the scenario:
  370. // -- State A calls foo() and then says "sleep for 2000 frames".
  371. // -- however, foo() called setState() to State B. thus our current state is not the same.
  372. // -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
  373. // so that the new state will be called immediately.
  374. if (stateBeforeUpdate != m_currentState)
  375. {
  376. status = STATE_CONTINUE;
  377. }
  378. if (IS_STATE_SLEEP(status))
  379. {
  380. // hey, we're sleepy!
  381. m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
  382. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  383. }
  384. else
  385. {
  386. // check for state transitions, possibly exiting this machine
  387. return m_currentState->friend_checkForTransitions( status );
  388. }
  389. }
  390. else
  391. {
  392. DEBUG_CRASH(("State machine has no current state -- did you remember to call initDefaultState?"));
  393. return STATE_FAILURE;
  394. }
  395. }
  396. //-----------------------------------------------------------------------------
  397. /**
  398. * Given a unique (for this machine) id number, and an instance of the
  399. * State class, the machine records this as a possible state, and
  400. * retains the id mapping.
  401. * These state id's are used to change the machine's state via setState().
  402. */
  403. void StateMachine::defineState( StateID id, State *state, StateID successID, StateID failureID, const StateConditionInfo* conditions )
  404. {
  405. #ifdef STATE_MACHINE_DEBUG
  406. DEBUG_ASSERTCRASH(m_stateMap.find( id ) == m_stateMap.end(), ("duplicate state ID in statemachine %s\n",m_name.str()));
  407. #endif
  408. // map the ID to the state
  409. m_stateMap.insert( std::map<StateID, State *>::value_type( id, state ) );
  410. // store the ID in the state itself, as well
  411. state->friend_setID( id );
  412. state->friend_onSuccess(successID);
  413. state->friend_onFailure(failureID);
  414. while (conditions && conditions->test != NULL)
  415. {
  416. state->friend_onCondition(conditions->test, conditions->toStateID, conditions->userData);
  417. ++conditions;
  418. }
  419. if (m_defaultStateID == INVALID_STATE_ID)
  420. m_defaultStateID = id;
  421. }
  422. //-----------------------------------------------------------------------------
  423. /**
  424. * Given a state ID, return the state instance
  425. */
  426. State *StateMachine::internalGetState( StateID id )
  427. {
  428. // locate the actual state associated with the given ID
  429. std::map<StateID, State *>::iterator i;
  430. i = m_stateMap.find( id );
  431. if (i == m_stateMap.end())
  432. {
  433. DEBUG_CRASH(( "StateMachine::internalGetState(): Invalid state" ));
  434. throw ERROR_BAD_ARG;
  435. }
  436. return (*i).second;
  437. }
  438. //-----------------------------------------------------------------------------
  439. /**
  440. * Change the current state of the machine.
  441. * This causes the old state's onExit() method to be invoked,
  442. * and the new state's onEnter() method to be invoked.
  443. */
  444. StateReturnType StateMachine::setState( StateID newStateID )
  445. {
  446. // if the machine is locked, it cannot change state via external events
  447. if (m_locked)
  448. {
  449. #ifdef STATE_MACHINE_DEBUG
  450. if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
  451. DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
  452. #endif
  453. return STATE_CONTINUE;
  454. }
  455. return internalSetState( newStateID );
  456. }
  457. //-----------------------------------------------------------------------------
  458. /**
  459. * Change the current state of the machine.
  460. * This causes the old state's onExit() method to be invoked,
  461. * and the new state's onEnter() method to be invoked.
  462. */
  463. StateReturnType StateMachine::internalSetState( StateID newStateID )
  464. {
  465. State *newState = NULL;
  466. // anytime the state changes, stop sleeping
  467. m_sleepTill = 0;
  468. // if we're not setting the "done" state ID we will continue with the actual transition
  469. if( newStateID != MACHINE_DONE_STATE_ID )
  470. {
  471. // if incoming state is invalid, go to the machine's default state
  472. if (newStateID == INVALID_STATE_ID)
  473. {
  474. newStateID = m_defaultStateID;
  475. if (newStateID == INVALID_STATE_ID)
  476. {
  477. DEBUG_CRASH(("you may NEVER set the current state to an invalid state id."));
  478. return STATE_FAILURE;
  479. }
  480. }
  481. // extract the state associated with the given ID
  482. newState = internalGetState( newStateID );
  483. #ifdef STATE_MACHINE_DEBUG
  484. if (getWantsDebugOutput())
  485. {
  486. StateID curState = INVALID_STATE_ID;
  487. if (m_currentState) {
  488. curState = m_currentState->getID();
  489. }
  490. DEBUG_LOG(("%d '%s' -- '%s' %x exit ", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
  491. if (m_currentState) {
  492. DEBUG_LOG((" '%s' ", m_currentState->getName().str()));
  493. } else {
  494. DEBUG_LOG((" INVALID_STATE_ID "));
  495. }
  496. if (newState) {
  497. DEBUG_LOG(("enter '%s' \n", newState->getName().str()));
  498. } else {
  499. DEBUG_LOG(("to INVALID_STATE\n"));
  500. }
  501. }
  502. #endif
  503. }
  504. // invoke the old state's onExit()
  505. if (m_currentState)
  506. m_currentState->onExit( EXIT_NORMAL );
  507. // set the new state
  508. m_currentState = newState;
  509. // invoke the new state's onEnter()
  510. /// @todo It might be useful to pass the old state in... (MSB)
  511. if( m_currentState )
  512. {
  513. // onEnter() could conceivably change m_currentState, so save it for a moment...
  514. State* stateBeforeEnter = m_currentState;
  515. StateReturnType status = m_currentState->onEnter();
  516. // it is possible that the state's onEnter() method may cause the state to be destroyed
  517. if (m_currentState == NULL)
  518. {
  519. return STATE_FAILURE;
  520. }
  521. // here's the scenario:
  522. // -- State A calls foo() and then says "sleep for 2000 frames".
  523. // -- however, foo() called setState() to State B. thus our current state is not the same.
  524. // -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
  525. // so that the new state will be called immediately.
  526. if (stateBeforeEnter != m_currentState)
  527. {
  528. status = STATE_CONTINUE;
  529. }
  530. if (IS_STATE_SLEEP(status))
  531. {
  532. // hey, we're sleepy!
  533. UnsignedInt now = TheGameLogic->getFrame();
  534. m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
  535. return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
  536. }
  537. else
  538. {
  539. // check for state transitions, possibly exiting this machine
  540. return m_currentState->friend_checkForTransitions( status );
  541. }
  542. }
  543. else
  544. {
  545. return STATE_CONTINUE; // irrelevant return code, but we must return something
  546. }
  547. }
  548. //-----------------------------------------------------------------------------
  549. /**
  550. * Define the default state of the machine, and
  551. * set the machine's state to it.
  552. */
  553. StateReturnType StateMachine::initDefaultState()
  554. {
  555. DEBUG_ASSERTCRASH(!m_locked, ("Machine is locked here, but probably should not be"));
  556. if (m_defaultStateInited)
  557. {
  558. DEBUG_CRASH(("you may not call initDefaultState twice for the same StateMachine"));
  559. return STATE_FAILURE;
  560. }
  561. else
  562. {
  563. m_defaultStateInited = true;
  564. return internalSetState( m_defaultStateID );
  565. }
  566. }
  567. //-----------------------------------------------------------------------------
  568. void StateMachine::setGoalObject( const Object *obj )
  569. {
  570. if (m_locked)
  571. return;
  572. internalSetGoalObject( obj );
  573. }
  574. //-----------------------------------------------------------------------------
  575. Bool StateMachine::isGoalObjectDestroyed() const
  576. {
  577. if (m_goalObjectID == 0)
  578. {
  579. return false; // never had a goal object
  580. }
  581. return getGoalObject() == NULL;
  582. }
  583. //-----------------------------------------------------------------------------
  584. void StateMachine::halt()
  585. {
  586. m_locked = true;
  587. m_currentState = NULL; // don't exit current state, just clear it.
  588. #ifdef STATE_MACHINE_DEBUG
  589. if (getWantsDebugOutput())
  590. {
  591. DEBUG_LOG(("%d '%s' -- '%s' %x halt()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
  592. }
  593. #endif
  594. }
  595. //-----------------------------------------------------------------------------
  596. void StateMachine::internalSetGoalObject( const Object *obj )
  597. {
  598. if (obj) {
  599. m_goalObjectID = obj->getID();
  600. internalSetGoalPosition(obj->getPosition());
  601. }
  602. else {
  603. m_goalObjectID = INVALID_ID;
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. Object *StateMachine::getGoalObject()
  608. {
  609. return TheGameLogic->findObjectByID( m_goalObjectID );
  610. }
  611. //-----------------------------------------------------------------------------
  612. const Object *StateMachine::getGoalObject() const
  613. {
  614. return TheGameLogic->findObjectByID( m_goalObjectID );
  615. }
  616. //-----------------------------------------------------------------------------
  617. void StateMachine::setGoalPosition( const Coord3D *pos )
  618. {
  619. if (m_locked)
  620. return;
  621. internalSetGoalPosition( pos );
  622. }
  623. //-----------------------------------------------------------------------------
  624. void StateMachine::internalSetGoalPosition( const Coord3D *pos )
  625. {
  626. if (pos) {
  627. m_goalPosition = *pos;
  628. // Don't clear the goal object, or everything breaks. Like construction of buildings.
  629. }
  630. }
  631. // ------------------------------------------------------------------------------------------------
  632. /** CRC */
  633. // ------------------------------------------------------------------------------------------------
  634. void StateMachine::crc( Xfer *xfer )
  635. {
  636. } // end crc
  637. // ------------------------------------------------------------------------------------------------
  638. /** Xfer Method
  639. * Version Info
  640. * 1: Initial version
  641. */
  642. // ------------------------------------------------------------------------------------------------
  643. void StateMachine::xfer( Xfer *xfer )
  644. {
  645. // version
  646. XferVersion currentVersion = 1;
  647. XferVersion version = currentVersion;
  648. xfer->xferVersion( &version, currentVersion );
  649. xfer->xferUnsignedInt(&m_sleepTill);
  650. xfer->xferUnsignedInt(&m_defaultStateID);
  651. StateID curStateID = getCurrentStateID();
  652. xfer->xferUnsignedInt(&curStateID);
  653. if (xfer->getXferMode() == XFER_LOAD) {
  654. // We are going to jump into the current state. We don't call onEnter or onExit, because the
  655. // state was already active when we saved.
  656. m_currentState = internalGetState( curStateID );
  657. }
  658. Bool snapshotAllStates = false;
  659. #ifdef _DEBUG
  660. //snapshotAllStates = true;
  661. #endif
  662. xfer->xferBool(&snapshotAllStates);
  663. if (snapshotAllStates) {
  664. std::map<StateID, State *>::iterator i;
  665. // count all states in the mapping
  666. Int count = 0;
  667. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
  668. count++;
  669. Int saveCount = count;
  670. xfer->xferInt(&saveCount);
  671. if (saveCount!=count) {
  672. DEBUG_CRASH(("State count mismatch - %d expected, %d read", count, saveCount));
  673. throw SC_INVALID_DATA;
  674. }
  675. for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
  676. State *state = (*i).second;
  677. StateID id = state->getID();
  678. xfer->xferUnsignedInt(&id);
  679. if (id!=state->getID()) {
  680. DEBUG_CRASH(("State ID mismatch - %d expected, %d read", state->getID(), id));
  681. throw SC_INVALID_DATA;
  682. }
  683. xfer->xferSnapshot(state);
  684. }
  685. } else {
  686. xfer->xferSnapshot(m_currentState);
  687. }
  688. xfer->xferObjectID(&m_goalObjectID);
  689. xfer->xferCoord3D(&m_goalPosition);
  690. xfer->xferBool(&m_locked);
  691. xfer->xferBool(&m_defaultStateInited);
  692. } // end xfer
  693. // ------------------------------------------------------------------------------------------------
  694. /** Load post process */
  695. // ------------------------------------------------------------------------------------------------
  696. void StateMachine::loadPostProcess( void )
  697. {
  698. } // end loadPostProcess