hfsmbase.c 24 KB

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