hfsmbase.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. void fsmDebugPrint (string s)
  2. {
  3. #ifdef FSM_DEBUG
  4. PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
  5. #else
  6. //Print("" + s); // comment/uncomment to hide/see debug logs
  7. #endif
  8. }
  9. void fsmDebugSpam (string s)
  10. {
  11. #ifdef FSM_DEBUG_SPAM
  12. PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
  13. #else
  14. //Print("" + s); // comment/uncomment to hide/see debug logs
  15. #endif
  16. }
  17. /**@class HFSMBase
  18. * @brief base class for hierarchic finite state machine
  19. *
  20. * stores current state (m_State) and transition table with possible transitions from each state
  21. * to another state via event
  22. *
  23. * each state can have nested state machine, thus creating hierarchy
  24. **/
  25. class HFSMBase<Class FSMStateBase, Class FSMEventBase, Class FSMActionBase, Class FSMGuardBase>
  26. {
  27. protected ref FSMStateBase m_State; /// current fsm state
  28. protected FSMStateBase m_OwnerState; /// state that owns this fsm (or null if root)
  29. protected ref FSMStateBase m_InitialState; /// configurable initial state of the machine
  30. protected ref array<ref FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase>> m_Transitions = new array<ref FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase>>; /// fsm transition table
  31. protected bool m_HasCompletions = false;
  32. void HFSMBase (FSMStateBase ownerState = NULL)
  33. {
  34. m_OwnerState = ownerState;
  35. }
  36. /**@fn GetCurrentState
  37. * @return returns currently active state within this machine (i.e. not hierarchic state)
  38. **/
  39. FSMStateBase GetCurrentState ()
  40. {
  41. return m_State;
  42. }
  43. /**@fn SetCurrentState
  44. * @return returns currently active state within this machine (i.e. not hierarchic state)
  45. **/
  46. void SetCurrentState (FSMStateBase state)
  47. {
  48. if(m_State != state)
  49. {
  50. m_State = state;
  51. }
  52. }
  53. /**@fn GetOwnerState
  54. * @return returns state that is owner of this fsm submachine. returns null if this is a root machine.
  55. **/
  56. FSMStateBase GetOwnerState ()
  57. {
  58. return m_OwnerState;
  59. }
  60. /**@fn GetHierarchyPath
  61. * @brief returns hierarchic state (path to root) of a state
  62. * @param[in] state \p state the path is starting
  63. * @param[out] path \p current hierarchic state
  64. * @return true if current state returned in path argument, false otherwise
  65. **/
  66. bool GetHierarchyPath (FSMStateBase state, out array<FSMStateBase> path)
  67. {
  68. FSMStateBase curr = state;
  69. while (curr)
  70. {
  71. path.Insert(curr);
  72. curr = curr.GetParentState();
  73. }
  74. return path.Count() > 0;
  75. }
  76. /**@fn SetInitialState
  77. * @param[in] initial_state \p state the machine will be entered to after ::Start()
  78. * @param[in] initial_event \p event that will be used to start the machine
  79. **/
  80. void SetInitialState (FSMStateBase initial_state)
  81. {
  82. m_InitialState = initial_state;
  83. }
  84. /**@fn AddTransition
  85. * @brief adds transition into transition table
  86. **/
  87. void AddTransition (FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t)
  88. {
  89. m_Transitions.Insert(t);
  90. //if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[hfsm] +++ ow=" + this.GetOwnerState() + " this=" + this + " t=" + t + " state=" + t.m_srcState + "-------- event=" + t.m_event + "[G=" + t.m_guard +"]/A=" + t.m_action + " --------|> dst=" + t.m_dstState);
  91. if (t.m_event == NULL)
  92. {
  93. Print("Warning (performance): FSM " + this + " has completion transition for src=" + t.m_srcState + " ---NULL----|> dst=" + t.m_dstState);
  94. m_HasCompletions = true;
  95. }
  96. }
  97. /**@fn Start
  98. * @brief starts the state machine by entering the initial_state (using intial_event as argument to initial state's onEntry)
  99. * @param[in] e \p optional event for starting the machind
  100. **/
  101. void Start (FSMEventBase initial_event = NULL, bool useExistingState = false)
  102. {
  103. if (LogManager.IsInventoryHFSMLogEnable())
  104. {
  105. fsmDebugPrint("[hfsm] " + this.ToString() + "::Start(" + initial_event.ToString() + "), init_state=" + m_InitialState.ToString());
  106. }
  107. if (!useExistingState)
  108. m_State = m_InitialState;
  109. m_State.OnEntry(initial_event);
  110. if (m_HasCompletions)
  111. ProcessCompletionTransitions();
  112. }
  113. /**@fn IsRunning
  114. * @brief returns true if machine is in running state
  115. **/
  116. bool IsRunning () { return m_State != NULL; }
  117. /**@fn Terminate
  118. * @brief terminates the state machine
  119. **/
  120. void Terminate (FSMEventBase terminal_event = NULL)
  121. {
  122. if (LogManager.IsInventoryHFSMLogEnable())
  123. {
  124. fsmDebugPrint("[hfsm] " + this.ToString() + "::Terminate(" + terminal_event.ToString() + ")");
  125. }
  126. if (IsRunning())
  127. {
  128. m_State.OnExit(terminal_event);
  129. m_State = NULL;
  130. }
  131. }
  132. void Abort (FSMEventBase abort_event = NULL)
  133. {
  134. if (LogManager.IsInventoryHFSMLogEnable())
  135. {
  136. fsmDebugPrint("[hfsm] " + this.ToString() + "::Abort(" + abort_event.ToString() + ")");
  137. }
  138. if (IsRunning())
  139. {
  140. m_State.OnAbort(abort_event);
  141. m_State = NULL;
  142. }
  143. }
  144. /**@fn Update
  145. * @brief if machine running, call OnUpdate() on current state
  146. **/
  147. void Update (float dt)
  148. {
  149. if (IsRunning())
  150. m_State.OnUpdate(dt);
  151. }
  152. protected ProcessEventResult ProcessAbortTransition (FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t, FSMEventBase e)
  153. {
  154. if (LogManager.IsInventoryHFSMLogEnable())
  155. {
  156. fsmDebugPrint("[hfsm] (local abort) state=" + t.m_srcState.ToString() + "-------- ABORT event=" + e.ToString() + "[G=" + t.m_guard.ToString() +"]/A=" + t.m_action.ToString() + " --------|> dst=" + t.m_dstState.ToString());
  157. }
  158. m_State.OnAbort(e); // 1) call onAbort on old state
  159. if (t.m_action)
  160. t.m_action.Action(e); // 2) execute transition action (if any)
  161. auto tmp = t.m_srcState.GetParentState();
  162. if (tmp == t.m_dstState.GetParentState())
  163. {
  164. m_State = t.m_dstState; // 3) change state to new (or NULL)
  165. if (t.m_dstState != NULL)
  166. {
  167. m_State.OnEntry(e); // 4a1) call onEntry on new state (see 4a2) )
  168. return ProcessEventResult.FSM_OK;
  169. }
  170. else
  171. {
  172. if (LogManager.IsInventoryHFSMLogEnable())
  173. {
  174. fsmDebugPrint("[hfsm] abort & terminating fsm: state=" + t.m_srcState.ToString() + " event=" + e.ToString());
  175. }
  176. return ProcessEventResult.FSM_TERMINATED; // 4b) or terminate
  177. }
  178. }
  179. else
  180. {
  181. m_State = NULL;
  182. return ProcessEventResult.FSM_ABORTED; // 4c) or signal abort to parent (with appropriate transition)
  183. }
  184. }
  185. /**@fn FindAbortDestinationState
  186. * @brief i
  187. * @param[in] e \p event that will be used to find suitable transition from current state
  188. * @return FSM_OK if transition found and allowed by guard (if any)
  189. * @return FSM_NO_TRANSITION if no unguarded transition found in submachine or local machine
  190. * @return FSM_TERMINATED state machine has terminated
  191. * @return FSM_ABORTED state machine has aborted
  192. **/
  193. FSMStateBase FindAbortDestinationState (FSMEventBase e)
  194. {
  195. if (LogManager.IsInventoryHFSMLogEnable())
  196. {
  197. if (GetOwnerState())
  198. fsmDebugPrint("[hfsm] SUB! " + GetOwnerState().Type().ToString() + "::FindAbortDestinationState(" + e.Type().ToString() + ")");
  199. else
  200. fsmDebugPrint("[hfsm] root::FindAbortDestinationState(" + e.Type().ToString() + ")");
  201. }
  202. // 1) look in submachine first (if any)
  203. if (m_State && m_State.HasFSM())
  204. {
  205. HFSMBase<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> a = m_State.GetFSM();
  206. FSMStateBase abort_dst = a.FindAbortDestinationState(e);
  207. if (abort_dst)
  208. {
  209. return abort_dst;
  210. }
  211. }
  212. // 2) local transitions
  213. int i = FindFirstUnguardedTransition(e);
  214. if (i == -1)
  215. {
  216. if (LogManager.IsInventoryHFSMLogEnable())
  217. {
  218. fsmDebugPrint("[hfsm] abort event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
  219. }
  220. return NULL;
  221. }
  222. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  223. return t.m_dstState;
  224. }
  225. /**@fn ProcessAbortEvent
  226. * @brief instructs the hierarchical state machine to process the event e
  227. * @param[in] e \p event that will be used to find suitable transition from current state
  228. * @return FSM_OK if transition found and allowed by guard (if any)
  229. * @return FSM_NO_TRANSITION if no unguarded transition found in submachine or local machine
  230. * @return FSM_TERMINATED state machine has terminated
  231. * @return FSM_ABORTED state machine has aborted
  232. **/
  233. FSMStateBase ProcessAbortEvent(FSMEventBase e, out ProcessEventResult result)
  234. {
  235. if (LogManager.IsInventoryHFSMLogEnable())
  236. {
  237. if (GetOwnerState())
  238. fsmDebugPrint("[hfsm] SUB! " + GetOwnerState().Type().ToString() + "::ProcessAbortEvent(" + e.Type().ToString() + ")");
  239. else
  240. fsmDebugPrint("[hfsm] root::ProcessAbortEvent(" + e.Type().ToString() + ")");
  241. }
  242. // 1) look in submachine first (if any)
  243. if (m_State && m_State.HasFSM())
  244. {
  245. HFSMBase<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> a = m_State.GetFSM();
  246. ProcessEventResult subfsm_res;
  247. FSMStateBase abort_dst = a.ProcessAbortEvent(e, subfsm_res);
  248. switch (subfsm_res)
  249. {
  250. case ProcessEventResult.FSM_OK:
  251. {
  252. if (LogManager.IsInventoryHFSMLogEnable())
  253. {
  254. fsmDebugPrint("[hfsm] event processed by sub machine=" + m_State.ToString());
  255. }
  256. result = subfsm_res; // 1.1) submachine accepted event
  257. return NULL;
  258. }
  259. case ProcessEventResult.FSM_ABORTED:
  260. {
  261. if (LogManager.IsInventoryHFSMLogEnable())
  262. {
  263. fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString());
  264. }
  265. m_State.OnAbort(e); // 1.2) submachine aborted, abort submachine owner (i.e. this)
  266. if (GetOwnerState() == abort_dst.GetParentState())
  267. {
  268. if (LogManager.IsInventoryHFSMLogEnable())
  269. {
  270. fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString() + " & abort destination reached.");
  271. }
  272. m_State = abort_dst;
  273. m_State.OnEntry(e); // 1.3) submachine aborted, call onEntry on new state (cross-hierarchy transition)
  274. result = ProcessEventResult.FSM_OK;
  275. return NULL;
  276. }
  277. else
  278. {
  279. result = ProcessEventResult.FSM_ABORTED; // 1.4) submachine has aborted, look for destination state in parent
  280. return NULL;
  281. }
  282. break;
  283. }
  284. case ProcessEventResult.FSM_TERMINATED:
  285. {
  286. break; // submachine has finished, look for local transitions from exited submachine
  287. }
  288. case ProcessEventResult.FSM_NO_TRANSITION:
  289. {
  290. if (LogManager.IsInventoryHFSMLogEnable())
  291. {
  292. fsmDebugPrint("[hfsm] aborted (but no transition) sub machine=" + m_State.ToString());
  293. }
  294. break; // submachine has no transition, look for transitions in local machine
  295. }
  296. }
  297. }
  298. // 2) local transitions
  299. int i = FindFirstUnguardedTransition(e);
  300. if (i == -1)
  301. {
  302. if (LogManager.IsInventoryHFSMLogEnable())
  303. {
  304. fsmDebugPrint("[hfsm] abort event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
  305. }
  306. result = ProcessEventResult.FSM_NO_TRANSITION;
  307. return NULL;
  308. }
  309. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  310. ProcessEventResult res = ProcessAbortTransition(t, e);
  311. result = res;
  312. switch (res)
  313. {
  314. case ProcessEventResult.FSM_OK:
  315. {
  316. //if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[hfsm] abort event processed by machine=" + m_State.ToString());
  317. return NULL; // machine accepted event
  318. }
  319. case ProcessEventResult.FSM_ABORTED:
  320. {
  321. if (LogManager.IsInventoryHFSMLogEnable())
  322. {
  323. fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString() + " will fall-through to dst=" + t.m_dstState);
  324. }
  325. return t.m_dstState; // store destination state for parent(s)
  326. }
  327. case ProcessEventResult.FSM_TERMINATED:
  328. {
  329. if (LogManager.IsInventoryHFSMLogEnable())
  330. {
  331. fsmDebugPrint("[hfsm] aborted & terminated sub machine=" + m_State.ToString());
  332. }
  333. break; // submachine has finished, look for local transitions from exited submachine
  334. }
  335. case ProcessEventResult.FSM_NO_TRANSITION:
  336. {
  337. break; // submachine has no transition, look for transitions in local machine
  338. }
  339. }
  340. return NULL;
  341. }
  342. /**@fn ProcessEvent
  343. * @brief instructs the hierarchical state machine to process the event e
  344. * @param[in] e \p event that will be used to find suitable transition from current state
  345. * @return FSM_OK if transition found and allowed by guard (if any)
  346. * @return FSM_NO_TRANSITION if no unguarded transition found in submachine or local machine
  347. * @return FSM_TERMINATED state machine has terminated
  348. **/
  349. ProcessEventResult ProcessEvent(FSMEventBase e)
  350. {
  351. if (LogManager.IsInventoryHFSMLogEnable())
  352. {
  353. if (GetOwnerState())
  354. fsmDebugPrint("[hfsm] SUB!::" + GetOwnerState().Type().ToString() + "::ProcessEvent(" + e.Type().ToString() + ")");
  355. else
  356. fsmDebugPrint("[hfsm] root::ProcessEvent(" + e.Type().ToString() + " =" + e.DumpToString());
  357. }
  358. // 1) completion transitions have priority (if any)
  359. if (m_HasCompletions)
  360. ProcessCompletionTransitions();
  361. // 2) submachine then (if any)
  362. if (m_State && m_State.HasFSM())
  363. {
  364. ProcessEventResult subfsm_res = m_State.ProcessEvent(e);
  365. switch (subfsm_res)
  366. {
  367. case ProcessEventResult.FSM_OK:
  368. {
  369. if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[hfsm] event processed by sub machine=" + m_State.ToString());
  370. return subfsm_res; // submachine accepted event
  371. }
  372. case ProcessEventResult.FSM_TERMINATED:
  373. {
  374. break; // submachine has finished, look for local transitions from exited submachine
  375. }
  376. case ProcessEventResult.FSM_NO_TRANSITION:
  377. {
  378. break; // submachine has no transition, look for transitions in local machine
  379. }
  380. }
  381. }
  382. // 3) local transitions
  383. int i = FindFirstUnguardedTransition(e);
  384. if (i == -1)
  385. {
  386. if (LogManager.IsInventoryHFSMLogEnable())
  387. {
  388. fsmDebugPrint("[hfsm] event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
  389. }
  390. return ProcessEventResult.FSM_NO_TRANSITION;
  391. }
  392. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> row = m_Transitions.Get(i);
  393. ProcessEventResult res;
  394. if (row.m_dstState != NULL)
  395. {
  396. // this is regular transition
  397. if (row.m_srcState.GetParentState() == row.m_dstState.GetParentState())
  398. res = LocalTransition(i, e); // transition is within this state machine
  399. else
  400. Error("cross-hierarchy transition or misconfigured transition detected!");
  401. //res = HierarchicTransition(i, e); // transition has to cross hierarchy
  402. }
  403. else
  404. {
  405. // this is terminating transition
  406. if (row.m_srcState.GetParentState() == GetOwnerState())
  407. res = LocalTransition(i, e); // terminating transition is within this state machine
  408. else
  409. Error("cross-hierarchy transition or misconfigured transition detected!");
  410. //res = HierarchicTransition(i, e); // source node crosses hierarchy (terminate lies always within this machine)
  411. }
  412. return res;
  413. }
  414. protected int FindFirstUnguardedTransition (FSMEventBase e)
  415. {
  416. FSMStateBase curr_state = m_State;
  417. int count = m_Transitions.Count();
  418. for (int i = 0; i < count; ++i)
  419. {
  420. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  421. if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
  422. {
  423. //if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[hfsm] [" + i + "/" + count + "] *** matched! t=" + t + " state=" + t.m_srcState + "-------- event=" + t.m_event + "[G=" + t.m_guard +"]/A=" + t.m_action + " --------|> dst=" + t.m_dstState);
  424. bool hasGuard = t.m_guard != NULL;
  425. if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(e))) // 1) exec guard (if any)
  426. {
  427. return i;
  428. }
  429. }
  430. //else if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[hfsm][" + i + "/" + count + "] ... matching t=" + t + " state=" + t.m_srcState + "-------- event=" + t.m_event + "[G=" + t.m_guard +"]/A=" + t.m_action + " --------|> dst=" + t.m_dstState);
  431. }
  432. return -1;
  433. }
  434. FSMStateBase FindTransitionState(FSMStateBase s, FSMEventBase e)
  435. {
  436. FSMStateBase curr_state = s;
  437. int count = m_Transitions.Count();
  438. for (int i = 0; i < count; ++i)
  439. {
  440. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  441. if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
  442. {
  443. return t.m_dstState;
  444. }
  445. }
  446. return null;
  447. }
  448. FSMStateBase FindGuardedTransitionState(FSMStateBase s, FSMEventBase e)
  449. {
  450. FSMStateBase curr_state = s;
  451. int count = m_Transitions.Count();
  452. for (int i = 0; i < count; ++i)
  453. {
  454. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  455. if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
  456. {
  457. bool hasGuard = t.m_guard != NULL;
  458. if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(e))) // 1) exec guard (if any)
  459. {
  460. return t.m_dstState;
  461. }
  462. }
  463. }
  464. return null;
  465. }
  466. protected int FindFirstCompletionTransition ()
  467. {
  468. if (IsRunning())
  469. {
  470. FSMStateBase curr_state = m_State;
  471. int count = m_Transitions.Count();
  472. for (int i = 0; i < count; ++i)
  473. {
  474. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  475. //if (LogManager.IsInventoryHFSMLogEnable()) fsmDebugPrint("[hfsm] (local) matching state=" + t.m_srcState + "-------- event=" + t.m_event + "[G=" + t.m_guard +"]/A=" + t.m_action + " --------|> dst=" + t.m_dstState);
  476. if ((t.m_srcState.Type() == curr_state.Type()) && (t.m_event == NULL))
  477. {
  478. bool hasGuard = t.m_guard != NULL;
  479. if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(NULL))) // 1) exec guard (if any)
  480. {
  481. return i;
  482. }
  483. }
  484. }
  485. }
  486. return -1;
  487. }
  488. /**@fn ProcessLocalTransition
  489. * @brief instructs the state machine to process the event locally - no hierarchy is crossed
  490. * @param[in] t \p the transition in m_Transitions
  491. * @param[in] e \p event that will be used to process transition from current state
  492. * @return FSM_OK or FSM_TERMINATED
  493. **/
  494. protected ProcessEventResult ProcessLocalTransition (FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t, FSMEventBase e)
  495. {
  496. if (LogManager.IsInventoryHFSMLogEnable())
  497. {
  498. fsmDebugPrint("[hfsm] (local) state=" + t.m_srcState.ToString() + "-------- event=" + e.ToString() + "[G=" + t.m_guard.ToString() +"]/A=" + t.m_action.ToString() + " --------|> dst=" + t.m_dstState.ToString());
  499. }
  500. m_State.OnExit(e); // 1) call onExit on old state
  501. if (t.m_action)
  502. t.m_action.Action(e); // 2) execute transition action (if any)
  503. m_State = t.m_dstState; // 3) change state to new
  504. if (t.m_dstState != NULL)
  505. {
  506. m_State.OnEntry(e); // 4a) call onEntry on new state
  507. if (GetOwnerState())
  508. GetOwnerState().OnSubMachineChanged(t.m_srcState, t.m_dstState); // 5a) notify owner state about change in submachine
  509. if (m_State)
  510. m_State.OnStateChanged(t.m_srcState, t.m_dstState); // 5b) notify current state about change in machine
  511. return ProcessEventResult.FSM_OK;
  512. }
  513. else
  514. {
  515. if (LogManager.IsInventoryHFSMLogEnable())
  516. {
  517. fsmDebugPrint("[hfsm] terminating fsm: state=" + t.m_srcState.ToString() + " event=" + e.ToString());
  518. }
  519. if (GetOwnerState())
  520. GetOwnerState().OnSubMachineChanged(t.m_srcState, NULL); // 5) notify owner state about change in submachine
  521. return ProcessEventResult.FSM_TERMINATED; // 4b) or terminate
  522. }
  523. }
  524. /**@fn LocalProcessEvent
  525. * @brief instructs the state machine to process the event e
  526. * @param[in] i \p index of the transition in m_Transitions
  527. * @param[in] e \p event that will be used to process transition from current state
  528. * @return FSM_OK or FSM_TERMINATED
  529. **/
  530. protected ProcessEventResult LocalTransition (int i, FSMEventBase e)
  531. {
  532. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  533. ProcessEventResult ret = ProcessLocalTransition(t, e);
  534. return ret;
  535. }
  536. protected ProcessEventResult ProcessCompletionTransitions ()
  537. {
  538. int completionIdx = FindFirstCompletionTransition();
  539. while (completionIdx != -1)
  540. {
  541. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> row = m_Transitions.Get(completionIdx);
  542. ProcessEventResult res;
  543. if (row.m_dstState != NULL)
  544. {
  545. // this is regular completion transition
  546. if (row.m_srcState.GetParentState() == row.m_dstState.GetParentState())
  547. res = LocalTransition(completionIdx, NULL); // transition is within this state machine
  548. else
  549. Error("cross-hierarchy transition or misconfigured transition detected!");
  550. //res = HierarchicTransition(completionIdx, NULL); // transition has to cross hierarchy
  551. }
  552. else
  553. {
  554. // this is terminating completion transition
  555. if (row.m_srcState.GetParentState() == GetOwnerState())
  556. res = LocalTransition(completionIdx, NULL); // terminating transition is within this state machine
  557. else
  558. Error("cross-hierarchy transition or misconfigured transition detected!");
  559. //res = HierarchicTransition(completionIdx, NULL); // source node crosses hierarchy (terminate lies always within this machine)
  560. }
  561. completionIdx = FindFirstCompletionTransition();
  562. }
  563. return ProcessEventResult.FSM_NO_TRANSITION;
  564. }
  565. };
  566. /** cross-hierarchy transitions (for future use):
  567. protected bool RemoveCommonParents (out array<WeaponStateBase> src_path, out array<WeaponStateBase> dst_path)
  568. {
  569. //for (int j = 0; j < src_path.Count(); ++j) fsmDebugPrint("[hfsm] curr state src_path[" + j + "] = " + src_path[j].ToString());
  570. //for (int k = 0; k < dst_path.Count(); ++k) fsmDebugPrint("[hfsm] next state dst_path[" + k + "] = " + dst_path[k].ToString());
  571. while ((src_path.Count() > 0) && (dst_path.Count() > 0) && (src_path.Get(src_path.Count() - 1) == dst_path.Get(dst_path.Count() - 1)))
  572. {
  573. src_path.Remove(src_path.Count() - 1);
  574. dst_path.Remove(dst_path.Count() - 1);
  575. }
  576. //for (int je = 0; je < src_path.Count(); ++je) fsmDebugPrint("[hfsm] curr state src_path[" + je + "] = " + src_path[je].ToString());
  577. //for (int ke = 0; ke < dst_path.Count(); ++ke) fsmDebugPrint("[hfsm] next state dst_path[" + ke + "] = " + dst_path[ke].ToString());
  578. }
  579. protected ProcessEventResult HierarchicTransition (int i, FSMEventBase e)
  580. {
  581. fsmDebugPrint("[hfsm] { " + this.ToString() + "::HierarchicTransition(" + e.Type().ToString() + ")");
  582. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_Transitions.Get(i);
  583. array<WeaponStateBase> src_path = new array<WeaponStateBase>;
  584. array<WeaponStateBase> dst_path = new array<WeaponStateBase>;
  585. if (GetHierarchyPath(t.m_srcState, src_path))
  586. {
  587. if (t.m_dstState == NULL)
  588. {
  589. Error("cross hierarchy terminate not yet implemented");
  590. WeaponStateBase curr_state = GetCurrentState();
  591. //for (int ex = 0; ex < src_path.Count(); ++ex) { fsmDebugPrint("[hfsm] exiting level=" + ex); src_path[ex].OnExit(e); }
  592. //for (int en = dst_path.Count(); en --> 0;)
  593. //for (int en = 0; en < dst_path.Count(); ++en) { fsmDebugPrint("[hfsm] entering level=" + en); int ii = dst_path.Count() - 1 - en; dst_path[ii].OnEntry(e); }
  594. fsmDebugPrint("[hfsm] } " + this.ToString() + "::HierarchicTransition(" + e.Type().ToString() + ") --> terminated");
  595. return ProcessEventResult.FSM_TERMINATED;
  596. }
  597. else if (GetHierarchyPath(t.m_dstState, dst_path))
  598. {
  599. Error("cross hierarchy event implemented, not fully debugged");
  600. RemoveCommonParents(src_path, dst_path);
  601. for (int ex = 0; ex < src_path.Count(); ++ex)
  602. {
  603. src_path[ex].OnExit(e); // 1) call OnExit on all nodes on src_path ()
  604. }
  605. //if (t.m_action)
  606. // t.m_action.Action(e); // 2) execute transition action (if any)
  607. //m_State = t.m_dstState; // 4) change state to new
  608. //for (int en = dst_path.Count(); en --> 0;)
  609. for (int en = 0; en < dst_path.Count(); ++en)
  610. {
  611. int ii = dst_path.Count() - 1 - en;
  612. dst_path[ii].OnEntry(e); // 5) call OnEntry on all nodes on dst_path (@NOTE: reverse order)
  613. }
  614. fsmDebugPrint("[hfsm] } " + this.ToString() + "::HierarchicTransition(" + e.Type().ToString() + ") --> OK");
  615. return ProcessEventResult.FSM_OK;
  616. }
  617. else
  618. {
  619. Error("unhandled case, what did you do?");
  620. }
  621. }
  622. fsmDebugPrint("[hfsm] } " + this.ToString() + "::HierarchicTransition(" + e.Type().ToString() + ") --> no transition");
  623. return ProcessEventResult.FSM_NO_TRANSITION;
  624. }
  625. */