StateMachine.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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.h
  24. // Finite state machine encapsulation
  25. // Author: Michael S. Booth, January 2002
  26. #pragma once
  27. #ifndef _STATE_MACHINE_H_
  28. #define _STATE_MACHINE_H_
  29. #include "Common/GameMemory.h"
  30. #include "Common/GameType.h"
  31. #include "Common/ModelState.h"
  32. #include "Common/Snapshot.h"
  33. #include "Common/Xfer.h"
  34. //-----------------------------------------------------------------------------
  35. //-----------------------------------------------------------------------------
  36. //-----------------------------------------------------------------------------
  37. class State;
  38. class StateMachine;
  39. class Object;
  40. #undef STATE_MACHINE_DEBUG
  41. #if defined(_DEBUG)
  42. #define STATE_MACHINE_DEBUG
  43. #endif
  44. #if defined(_INTERNAL)
  45. #define STATE_MACHINE_DEBUG //uncomment to debug state machines in internal. jba.
  46. #endif
  47. //-----------------------------------------------------------------------------
  48. //-----------------------------------------------------------------------------
  49. //-----------------------------------------------------------------------------
  50. enum { MACHINE_DONE_STATE_ID = 999998, INVALID_STATE_ID = 999999 };
  51. typedef UnsignedInt StateID; ///< used to denote individual states
  52. typedef Bool (*StateTransFuncPtr)( State *state, void* userData );
  53. /**
  54. * State return codes
  55. */
  56. enum StateReturnType
  57. {
  58. // note that all positive values are reserved for STATE_SLEEP!
  59. STATE_CONTINUE = 0, ///< stay in this state (only for update method)
  60. STATE_SUCCESS = -1, ///< state finished successfully, go to next state
  61. STATE_FAILURE = -2, ///< state finished abnormally, go to next state
  62. };
  63. #define STATE_SLEEP(numFrames) ((StateReturnType)(numFrames))
  64. #define IS_STATE_SLEEP(ret) ((Int)(ret) > 0)
  65. #define GET_STATE_SLEEP_FRAMES(ret) ((UnsignedInt)(ret))
  66. // (we use 0x3fffffff so that we can add offsets and not overflow...
  67. // at 30fps that's around ~414 days!)
  68. #define STATE_SLEEP_FOREVER STATE_SLEEP(0x3fffffff)
  69. // this is mainly useful for states that enclose other state machines...
  70. // where, even if the enclosed machine is sleeping, the encloser still needs
  71. // to run every frame.
  72. inline StateReturnType CONVERT_SLEEP_TO_CONTINUE(StateReturnType s)
  73. {
  74. return IS_STATE_SLEEP(s) ? STATE_CONTINUE : s;
  75. }
  76. // this is mainly useful for states that enclose other state machines...
  77. // where the encloser and enclosee both might sleep. we need to choose the min
  78. // sleep time that satisfies both.
  79. inline StateReturnType MIN_SLEEP(UnsignedInt encloserSleep, StateReturnType encloseeResult)
  80. {
  81. if (IS_STATE_SLEEP(encloseeResult))
  82. {
  83. UnsignedInt encloseeSleep = GET_STATE_SLEEP_FRAMES(encloseeResult);
  84. return STATE_SLEEP(min(encloserSleep, encloseeSleep));
  85. }
  86. else
  87. {
  88. // if enclosee needs to stay awake, we better do so, regardless
  89. return encloseeResult;
  90. }
  91. }
  92. /**
  93. * Special argument for onCondition. It means when the given condition
  94. * becomes true, the state machine will exit and return the given status.
  95. */
  96. enum
  97. {
  98. EXIT_MACHINE_WITH_SUCCESS = 9998,
  99. EXIT_MACHINE_WITH_FAILURE = 9999
  100. };
  101. /**
  102. * Parameters for onExit().
  103. */
  104. enum StateExitType
  105. {
  106. EXIT_NORMAL, ///< state exited due to normal state transitioning
  107. EXIT_RESET ///< state exited due to state machine reset
  108. };
  109. struct StateConditionInfo
  110. {
  111. StateTransFuncPtr test;
  112. StateID toStateID;
  113. void* userData;
  114. StateConditionInfo(StateTransFuncPtr t, StateID id, void* ud) : test(t), toStateID(id), userData(ud) { }
  115. };
  116. //-----------------------------------------------------------------------------
  117. //-----------------------------------------------------------------------------
  118. //-----------------------------------------------------------------------------
  119. /**
  120. * An abstraction of a machine's "state".
  121. */
  122. class State : public MemoryPoolObject, public Snapshot
  123. {
  124. MEMORY_POOL_GLUE_ABC( State ) ///< this abstract class needs memory pool hooks
  125. public:
  126. State( StateMachine *machine, AsciiString name);
  127. // already defined by MPO.
  128. //virtual ~State() { }
  129. virtual StateReturnType onEnter() { return STATE_CONTINUE; } ///< executed once when entering state
  130. virtual void onExit( StateExitType status ) { } ///< executed once when leaving state
  131. virtual StateReturnType update() = 0; ///< implements this state's behavior, decides when to change state
  132. virtual Bool isIdle() const { return false; }
  133. virtual Bool isAttack() const { return false; }
  134. //Definition of busy -- when explicitly in the busy state. Moving or attacking is not considered busy!
  135. virtual Bool isBusy() const { return false; }
  136. inline StateMachine* getMachine() { return m_machine; } ///< return the machine this state is part of
  137. inline StateID getID() const { return m_ID; } ///< get this state's id
  138. Object* getMachineOwner();
  139. const Object* getMachineOwner() const;
  140. Object* getMachineGoalObject();
  141. const Object* getMachineGoalObject() const;
  142. const Coord3D* getMachineGoalPosition() const;
  143. #ifdef STATE_MACHINE_DEBUG
  144. virtual AsciiString getName() const {return m_name;}
  145. #endif
  146. // for internal use by the StateMachine class ---------------------------------------------------------
  147. inline void friend_setID( StateID id ) { m_ID = id; } ///< define this state's id (for use only by StateMachine class)
  148. void friend_onSuccess( StateID toStateID ) { m_successStateID = toStateID; } ///< define which state to move to after successful completion
  149. void friend_onFailure( StateID toStateID ) { m_failureStateID = toStateID; } ///< define which state to move to after failure
  150. void friend_onCondition( StateTransFuncPtr test, StateID toStateID, void* userData, const char* description = NULL ); ///< define when to change state
  151. StateReturnType friend_checkForTransitions( StateReturnType status ); ///< given a return code, handle state transitions
  152. StateReturnType friend_checkForSleepTransitions( StateReturnType status ); ///< given a return code, handle state transitions
  153. protected:
  154. #ifdef STATE_MACHINE_DEBUG
  155. inline void setName(AsciiString n) { m_name = n; }
  156. #endif
  157. protected:
  158. // snapshot interface - pure virtual here.
  159. // Essentially all the member data gets set up on creation and shouldn't change.
  160. // So none of it needs to be saved, and it nicely forces all user states to
  161. // remember to implement crc, xfer & loadPostProcess. jba
  162. virtual void crc( Xfer *xfer )=0;
  163. virtual void xfer( Xfer *xfer )=0;
  164. virtual void loadPostProcess()=0;
  165. private:
  166. struct TransitionInfo
  167. {
  168. StateTransFuncPtr test; ///< the condition evaluation function
  169. StateID toStateID; ///< the state to transition to
  170. void* userData; ///< data passed to transFuncPtr.
  171. #ifdef STATE_MACHINE_DEBUG
  172. const char* description; ///< description (for debugging purposes)
  173. #endif
  174. TransitionInfo(StateTransFuncPtr t, StateID id, void* ud, const char* desc) :
  175. test(t),
  176. toStateID(id),
  177. userData(ud)
  178. #ifdef STATE_MACHINE_DEBUG
  179. , description(desc)
  180. #endif
  181. { }
  182. };
  183. StateID m_ID; ///< this state's ID
  184. StateID m_successStateID; ///< state to move to upon success
  185. StateID m_failureStateID; ///< state to move to upon failure
  186. std::vector<TransitionInfo> m_transitions; ///< possible transitions from this state
  187. StateMachine *m_machine; ///< the state machine this state is part of
  188. protected:
  189. #ifdef STATE_MACHINE_DEBUG
  190. AsciiString m_name; ///< Human readable name of this state - for debugging. jba.
  191. #endif
  192. };
  193. inline State::~State() { }
  194. //-----------------------------------------------------------------------------
  195. //-----------------------------------------------------------------------------
  196. //-----------------------------------------------------------------------------
  197. /**
  198. * A finite state machine.
  199. */
  200. class StateMachine : public MemoryPoolObject, public Snapshot
  201. {
  202. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( StateMachine, "StateMachinePool" );
  203. public:
  204. /**
  205. * All of the states used by this machine should be
  206. * instantiated and defined via defineState() in the
  207. * machine's constructor.
  208. */
  209. StateMachine( Object *owner, AsciiString name );
  210. // virtual destructor defined by MemoryPool
  211. virtual StateReturnType updateStateMachine(); ///< run one step of the machine
  212. virtual void clear(); ///< clear the machine's internals to a known, initialized state
  213. virtual StateReturnType resetToDefaultState(); ///< clear the machine's internals and set to the default state
  214. /** must be called to jump the StateMachine into the default state... this may fail
  215. (return a failure code), so it can't be done via the ctor. you MUST call this before
  216. using the state machine. */
  217. virtual StateReturnType initDefaultState();
  218. virtual StateReturnType setState( StateID newStateID ); ///< change the current state of the machine (which may cause further state changes, due to onEnter)
  219. StateID getCurrentStateID() const { return m_currentState ? m_currentState->getID() : INVALID_STATE_ID; } ///< return the id of the current state of the machine
  220. Bool isInIdleState() const { return m_currentState ? m_currentState->isIdle() : true; } // stateless things are considered 'idle'
  221. Bool isInAttackState() const { return m_currentState ? m_currentState->isAttack() : true; } // stateless things are considered 'idle'
  222. Bool isInForceAttackState() const { return m_currentState ? m_currentState->isIdle() : true; } // stateless things are considered 'idle'
  223. //Definition of busy -- when explicitly in the busy state. Moving or attacking is not considered busy!
  224. Bool isInBusyState() const { return m_currentState ? m_currentState->isBusy() : false; } // stateless things are not considered 'busy'
  225. // no, this is now deprecated. you should strive to avoid having to get the current state.
  226. // try to make do with getCurrentStateID() or isInIdleState() instead. (srj)
  227. // State *getCurrentState() { return m_currentState; } ///< return the current state of the machine
  228. /**
  229. * Lock/unlock this state machine.
  230. * If a machine is locked, it cannot be reset, or given external setStates(), etc.
  231. */
  232. void lock(const char* msg)
  233. {
  234. m_locked = true;
  235. #ifdef STATE_MACHINE_DEBUG
  236. m_lockedby = msg;
  237. #endif
  238. }
  239. void unlock()
  240. {
  241. m_locked = false;
  242. #ifdef STATE_MACHINE_DEBUG
  243. m_lockedby = NULL;
  244. #endif
  245. }
  246. Bool isLocked() const { return m_locked; }
  247. /**
  248. * Get the object that "owns" this machine.
  249. * There need not be an object with a machine, but it is so common
  250. * that it is useful to have this method in the generic state machine.
  251. */
  252. Object *getOwner() { return m_owner; }
  253. const Object *getOwner() const { return m_owner; }
  254. // common parameters for state machines
  255. void setGoalObject( const Object *obj );
  256. Object *getGoalObject();
  257. const Object *getGoalObject() const;
  258. void setGoalPosition( const Coord3D *pos );
  259. const Coord3D *getGoalPosition() const { return &m_goalPosition; }
  260. Bool isGoalObjectDestroyed() const; ///< Returns true if we had a goal object, but it has been destroyed.
  261. virtual void halt(void); ///< Stops the state machine & disables it in preparation for deleting it.
  262. //
  263. // The following methods are for internal use by the State class
  264. //
  265. StateReturnType internalSetState( StateID newStateID ); ///< for internal use only - change the current state of the machine
  266. #if defined(_DEBUG) || defined(_INTERNAL)
  267. UnsignedInt peekSleepTill() const { return m_sleepTill; }
  268. #endif
  269. #ifdef STATE_MACHINE_DEBUG
  270. Bool getWantsDebugOutput() const;
  271. void setDebugOutput( Bool output ) { m_debugOutput = output; }
  272. void setName( AsciiString name) {m_name = name;}
  273. inline AsciiString getName() const {return m_name;}
  274. virtual AsciiString getCurrentStateName() const { return m_currentState ? m_currentState->getName() : AsciiString::TheEmptyString;}
  275. #else
  276. inline Bool getWantsDebugOutput() const { return false; }
  277. inline AsciiString getCurrentStateName() const { return AsciiString::TheEmptyString;}
  278. #endif
  279. protected:
  280. // snapshot interface
  281. virtual void crc( Xfer *xfer );
  282. virtual void xfer( Xfer *xfer );
  283. virtual void loadPostProcess();
  284. protected:
  285. /**
  286. * Given a unique (to this machine) integer ID representing a state, and an instance
  287. * of that state, the machine records this as a possible state, and
  288. * internally maps the given integer ID to the state instance.
  289. * These state id's are used to change the machine's state via setState().
  290. */
  291. void defineState( StateID id, State *state,
  292. StateID successID,
  293. StateID failureID,
  294. const StateConditionInfo* conditions = NULL);
  295. State* internalGetState( StateID id );
  296. private:
  297. void internalClear();
  298. void internalSetGoalObject( const Object *obj );
  299. void internalSetGoalPosition( const Coord3D *pos);
  300. std::map<StateID, State *> m_stateMap; ///< the mapping of ids to states
  301. Object* m_owner; ///< object that "owns" this machine
  302. UnsignedInt m_sleepTill; ///< if nonzero, we are sleeping 'till this frame
  303. StateID m_defaultStateID; ///< the default state of the machine
  304. State* m_currentState;
  305. ObjectID m_goalObjectID; ///< the object of interest for this state
  306. Coord3D m_goalPosition; ///< the position of interest for this state
  307. Bool m_locked; ///< whether this machine is locked or not
  308. Bool m_defaultStateInited; ///< if initDefaultState has been called
  309. #ifdef STATE_MACHINE_DEBUG
  310. Bool m_debugOutput;
  311. AsciiString m_name; ///< Human readable name of this state - for debugging. jba.
  312. const char* m_lockedby;
  313. #endif
  314. };
  315. //-----------------------------------------------------------------------------
  316. inline Object* State::getMachineOwner() { return m_machine->getOwner(); }
  317. inline const Object* State::getMachineOwner() const { return m_machine->getOwner(); }
  318. inline Object* State::getMachineGoalObject() { return m_machine->getGoalObject(); } ///< return the machine this state is part of
  319. inline const Object* State::getMachineGoalObject() const { return m_machine->getGoalObject(); } ///< return the machine this state is part of
  320. inline const Coord3D* State::getMachineGoalPosition() const { return m_machine->getGoalPosition(); } ///< return the machine this state is part of
  321. //-----------------------------------------------------------------------------------------------------------
  322. /**
  323. A utility state that immediately succeeds.
  324. */
  325. class SuccessState : public State
  326. {
  327. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SuccessState, "SuccessState")
  328. public:
  329. SuccessState( StateMachine *machine ) : State( machine, "SuccessState") { }
  330. virtual StateReturnType onEnter() { return STATE_SUCCESS; }
  331. virtual StateReturnType update() { return STATE_SUCCESS; }
  332. protected:
  333. // snapshot interface STUBBED.
  334. virtual void crc( Xfer *xfer ){};
  335. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  336. virtual void loadPostProcess(){};
  337. };
  338. EMPTY_DTOR(SuccessState)
  339. //-----------------------------------------------------------------------------------------------------------
  340. /**
  341. A utility state that immediately fails.
  342. */
  343. class FailureState : public State
  344. {
  345. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(FailureState, "FailureState")
  346. public:
  347. FailureState( StateMachine *machine ) : State( machine, "FailureState") { }
  348. virtual StateReturnType onEnter() { return STATE_FAILURE; }
  349. virtual StateReturnType update() { return STATE_FAILURE; }
  350. protected:
  351. // snapshot interface STUBBED.
  352. virtual void crc( Xfer *xfer ){};
  353. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  354. virtual void loadPostProcess(){};
  355. };
  356. EMPTY_DTOR(FailureState)
  357. //-----------------------------------------------------------------------------------------------------------
  358. /**
  359. A utility state that never exits (except due to conditions).
  360. */
  361. class ContinueState : public State
  362. {
  363. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ContinueState, "ContinueState")
  364. public:
  365. ContinueState( StateMachine *machine ) : State( machine, "ContinueState" ) { }
  366. virtual StateReturnType onEnter() { return STATE_CONTINUE; }
  367. virtual StateReturnType update() { return STATE_CONTINUE; }
  368. protected:
  369. // snapshot interface STUBBED.
  370. virtual void crc( Xfer *xfer ){};
  371. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  372. virtual void loadPostProcess(){};
  373. };
  374. EMPTY_DTOR(ContinueState)
  375. //-----------------------------------------------------------------------------------------------------------
  376. /**
  377. A utility state that sleeps forever.
  378. */
  379. class SleepState : public State
  380. {
  381. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SleepState, "SleepState")
  382. public:
  383. SleepState( StateMachine *machine ) : State( machine, "SleepState" ) { }
  384. virtual StateReturnType onEnter() { return STATE_SLEEP_FOREVER; }
  385. virtual StateReturnType update() { return STATE_SLEEP_FOREVER; }
  386. protected:
  387. // snapshot interface STUBBED.
  388. virtual void crc( Xfer *xfer ){};
  389. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  390. virtual void loadPostProcess(){};
  391. };
  392. EMPTY_DTOR(SleepState)
  393. #endif // _STATE_MACHINE_H_