DayZ Scripts
v1.21.156300 · Jun 20, 2023
 
Loading...
Searching...
No Matches
HFSMBase.c
Go to the documentation of this file.
1void fsmDebugPrint (string s)
2{
3 //Print("" + s); // comment/uncomment to hide/see debug logs
4}
5void fsmDebugSpam (string s)
6{
7 //Print("" + s); // comment/uncomment to hide/see debug spam
8}
9
10
19class HFSMBase<Class FSMStateBase, Class FSMEventBase, Class FSMActionBase, Class FSMGuardBase>
20{
21 protected ref FSMStateBase m_State;
22 protected FSMStateBase m_OwnerState;
23 protected ref FSMStateBase m_InitialState;
25 protected bool m_HasCompletions = false;
26
27 void HFSMBase (FSMStateBase ownerState = NULL)
28 {
29 m_OwnerState = ownerState;
30 }
31
35 FSMStateBase GetCurrentState ()
36 {
37 return m_State;
38 }
42 FSMStateBase GetOwnerState ()
43 {
44 return m_OwnerState;
45 }
52 bool GetHierarchyPath (FSMStateBase state, out array<FSMStateBase> path)
53 {
54 FSMStateBase curr = state;
55 while (curr)
56 {
57 path.Insert(curr);
58 curr = curr.GetParentState();
59 }
60 return path.Count() > 0;
61 }
62
67 void SetInitialState (FSMStateBase initial_state)
68 {
69 m_InitialState = initial_state;
70 }
75 {
76 m_Transitions.Insert(t);
77 //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);
78 if (t.m_event == NULL)
79 {
80 Print("Warning (performance): FSM " + this + " has completion transition for src=" + t.m_srcState + " ---NULL----|> dst=" + t.m_dstState);
81 m_HasCompletions = true;
82 }
83 }
84
89 void Start (FSMEventBase initial_event = NULL, bool useExistingState = false)
90 {
91 fsmDebugPrint("[hfsm] " + this.ToString() + "::Start(" + initial_event.ToString() + "), init_state=" + m_InitialState.ToString());
92
93 if (!useExistingState)
94 m_State = m_InitialState;
95
96 m_State.OnEntry(initial_event);
97
98 if (m_HasCompletions)
99 ProcessCompletionTransitions();
100 }
104 bool IsRunning () { return m_State != NULL; }
108 void Terminate (FSMEventBase terminal_event = NULL)
109 {
110 fsmDebugPrint("[hfsm] " + this.ToString() + "::Terminate(" + terminal_event.ToString() + ")");
111 if (IsRunning())
112 {
113 m_State.OnExit(terminal_event);
114 m_State = NULL;
115 }
116 }
117 void Abort (FSMEventBase abort_event = NULL)
118 {
119 fsmDebugPrint("[hfsm] " + this.ToString() + "::Abort(" + abort_event.ToString() + ")");
120 if (IsRunning())
121 {
122 m_State.OnAbort(abort_event);
123 m_State = NULL;
124 }
125 }
129 void Update (float dt)
130 {
131 if (IsRunning())
132 m_State.OnUpdate(dt);
133 }
134
136 {
137 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());
138
139 m_State.OnAbort(e); // 1) call onAbort on old state
140
141 if (t.m_action)
142 t.m_action.Action(e); // 2) execute transition action (if any)
143
144 auto tmp = t.m_srcState.GetParentState();
145 if (tmp == t.m_dstState.GetParentState())
146 {
147 m_State = t.m_dstState; // 3) change state to new (or NULL)
148
149 if (t.m_dstState != NULL)
150 {
151 m_State.OnEntry(e); // 4a1) call onEntry on new state (see 4a2) )
152 return ProcessEventResult.FSM_OK;
153 }
154 else
155 {
156 fsmDebugPrint("[hfsm] abort & terminating fsm: state=" + t.m_srcState.ToString() + " event=" + e.ToString());
157 return ProcessEventResult.FSM_TERMINATED; // 4b) or terminate
158 }
159 }
160 else
161 {
162 m_State = NULL;
163 return ProcessEventResult.FSM_ABORTED; // 4c) or signal abort to parent (with appropriate transition)
164 }
165 }
166
175 FSMStateBase FindAbortDestinationState (FSMEventBase e)
176 {
177 if (GetOwnerState())
178 fsmDebugPrint("[hfsm] SUB! " + GetOwnerState().Type().ToString() + "::FindAbortDestinationState(" + e.Type().ToString() + ")");
179 else
180 fsmDebugPrint("[hfsm] root::FindAbortDestinationState(" + e.Type().ToString() + ")");
181
182 // 1) look in submachine first (if any)
183 if (m_State.HasFSM())
184 {
186 FSMStateBase abort_dst = a.FindAbortDestinationState(e);
187
188 if (abort_dst)
189 {
190 return abort_dst;
191 }
192 }
193
194 // 2) local transitions
195 int i = FindFirstUnguardedTransition(e);
196 if (i == -1)
197 {
198 fsmDebugPrint("[hfsm] abort event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
199 return NULL;
200 }
201
203 return t.m_dstState;
204 }
205
214 FSMStateBase ProcessAbortEvent (FSMEventBase e, out ProcessEventResult result)
215 {
216 if (GetOwnerState())
217 fsmDebugPrint("[hfsm] SUB! " + GetOwnerState().Type().ToString() + "::ProcessAbortEvent(" + e.Type().ToString() + ")");
218 else
219 fsmDebugPrint("[hfsm] root::ProcessAbortEvent(" + e.Type().ToString() + ")");
220
221 // 1) look in submachine first (if any)
222 if (m_State.HasFSM())
223 {
225 ProcessEventResult subfsm_res;
226 FSMStateBase abort_dst = a.ProcessAbortEvent(e, subfsm_res);
227
228 switch (subfsm_res)
229 {
230 case ProcessEventResult.FSM_OK:
231 {
232 fsmDebugPrint("[hfsm] event processed by sub machine=" + m_State.ToString());
233 result = subfsm_res; // 1.1) submachine accepted event
234 return NULL;
235 }
236 case ProcessEventResult.FSM_ABORTED:
237 {
238 fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString());
239
240 m_State.OnAbort(e); // 1.2) submachine aborted, abort submachine owner (i.e. this)
241
242 if (GetOwnerState() == abort_dst.GetParentState())
243 {
244 fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString() + " & abort destination reached.");
245 m_State = abort_dst;
246 m_State.OnEntry(e); // 1.3) submachine aborted, call onEntry on new state (cross-hierarchy transition)
247 result = ProcessEventResult.FSM_OK;
248 return NULL;
249 }
250 else
251 {
252 result = ProcessEventResult.FSM_ABORTED; // 1.4) submachine has aborted, look for destination state in parent
253 return NULL;
254 }
255
256 break;
257 }
258 case ProcessEventResult.FSM_TERMINATED:
259 {
260 break; // submachine has finished, look for local transitions from exited submachine
261 }
262 case ProcessEventResult.FSM_NO_TRANSITION:
263 {
264 fsmDebugPrint("[hfsm] aborted (but no transition) sub machine=" + m_State.ToString());
265 break; // submachine has no transition, look for transitions in local machine
266 }
267 }
268 }
269
270 // 2) local transitions
271 int i = FindFirstUnguardedTransition(e);
272 if (i == -1)
273 {
274 fsmDebugPrint("[hfsm] abort event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
275 result = ProcessEventResult.FSM_NO_TRANSITION;
276 return NULL;
277 }
278
280 ProcessEventResult res = ProcessAbortTransition(t, e);
281 result = res;
282 switch (res)
283 {
284 case ProcessEventResult.FSM_OK:
285 {
286 //fsmDebugSpam("[hfsm] abort event processed by machine=" + m_State.ToString());
287 return NULL; // machine accepted event
288 }
289 case ProcessEventResult.FSM_ABORTED:
290 {
291 fsmDebugPrint("[hfsm] aborted sub machine=" + m_State.ToString() + " will fall-through to dst=" + t.m_dstState);
292 return t.m_dstState; // store destination state for parent(s)
293 }
294
295 case ProcessEventResult.FSM_TERMINATED:
296 {
297 fsmDebugPrint("[hfsm] aborted & terminated sub machine=" + m_State.ToString());
298 break; // submachine has finished, look for local transitions from exited submachine
299 }
300 case ProcessEventResult.FSM_NO_TRANSITION:
301 {
302 break; // submachine has no transition, look for transitions in local machine
303 }
304 }
305 return NULL;
306 }
307
308
317 {
318 if (GetOwnerState())
319 fsmDebugPrint("[hfsm] SUB!::" + GetOwnerState().Type().ToString() + "::ProcessEvent(" + e.Type().ToString() + ")");
320 else
321 fsmDebugPrint("[hfsm] root::ProcessEvent(" + e.Type().ToString() + " =" + e.DumpToString());
322
323 // 1) completion transitions have priority (if any)
324 if (m_HasCompletions)
325 ProcessCompletionTransitions();
326
327 // 2) submachine then (if any)
328 if (m_State.HasFSM())
329 {
330 ProcessEventResult subfsm_res = m_State.ProcessEvent(e);
331
332 switch (subfsm_res)
333 {
334 case ProcessEventResult.FSM_OK:
335 {
336 fsmDebugSpam("[hfsm] event processed by sub machine=" + m_State.ToString());
337 return subfsm_res; // submachine accepted event
338 }
339 case ProcessEventResult.FSM_TERMINATED:
340 {
341 break; // submachine has finished, look for local transitions from exited submachine
342 }
343 case ProcessEventResult.FSM_NO_TRANSITION:
344 {
345 break; // submachine has no transition, look for transitions in local machine
346 }
347 }
348 }
349
350 // 3) local transitions
351 int i = FindFirstUnguardedTransition(e);
352 if (i == -1)
353 {
354 fsmDebugPrint("[hfsm] event has no transition: src=" + m_State.ToString() + " e=" + e.Type().ToString());
355 return ProcessEventResult.FSM_NO_TRANSITION;
356 }
357
360 if (row.m_dstState != NULL)
361 {
362 // this is regular transition
363 if (row.m_srcState.GetParentState() == row.m_dstState.GetParentState())
364 res = LocalTransition(i, e); // transition is within this state machine
365 else
366 Error("cross-hierarchy transition or misconfigured transition detected!");
367 //res = HierarchicTransition(i, e); // transition has to cross hierarchy
368 }
369 else
370 {
371 // this is terminating transition
372 if (row.m_srcState.GetParentState() == GetOwnerState())
373 res = LocalTransition(i, e); // terminating transition is within this state machine
374 else
375 Error("cross-hierarchy transition or misconfigured transition detected!");
376 //res = HierarchicTransition(i, e); // source node crosses hierarchy (terminate lies always within this machine)
377 }
378 return res;
379 }
380
381 protected int FindFirstUnguardedTransition (FSMEventBase e)
382 {
383 FSMStateBase curr_state = m_State;
384
385 int count = m_Transitions.Count();
386 for (int i = 0; i < count; ++i)
387 {
389 if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
390 {
391 //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);
392 bool hasGuard = t.m_guard != NULL;
393 if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(e))) // 1) exec guard (if any)
394 {
395 return i;
396 }
397 }
398 //else 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);
399 }
400 return -1;
401 }
402
403 FSMStateBase FindTransitionState(FSMStateBase s, FSMEventBase e)
404 {
405 FSMStateBase curr_state = s;
406
407 int count = m_Transitions.Count();
408 for (int i = 0; i < count; ++i)
409 {
411 if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
412 {
413 return t.m_dstState;
414 }
415 }
416 return null;
417 }
418
419 FSMStateBase FindGuardedTransitionState(FSMStateBase s, FSMEventBase e)
420 {
421 FSMStateBase curr_state = s;
422
423 int count = m_Transitions.Count();
424 for (int i = 0; i < count; ++i)
425 {
427 if ((t.m_srcState == curr_state) && (t.m_event != NULL) && (t.m_event.Type() == e.Type()))
428 {
429 bool hasGuard = t.m_guard != NULL;
430 if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(e))) // 1) exec guard (if any)
431 {
432 return t.m_dstState;
433 }
434 }
435 }
436 return null;
437 }
438
440 {
441 if (IsRunning())
442 {
443 FSMStateBase curr_state = m_State;
444
445 int count = m_Transitions.Count();
446 for (int i = 0; i < count; ++i)
447 {
449
450 //fsmDebugPrint("[hfsm] (local) matching state=" + t.m_srcState + "-------- event=" + t.m_event + "[G=" + t.m_guard +"]/A=" + t.m_action + " --------|> dst=" + t.m_dstState);
451 if ((t.m_srcState.Type() == curr_state.Type()) && (t.m_event == NULL))
452 {
453 bool hasGuard = t.m_guard != NULL;
454 if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(NULL))) // 1) exec guard (if any)
455 {
456 return i;
457 }
458 }
459 }
460 }
461 return -1;
462 }
463
464
472 {
473 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());
474
475 m_State.OnExit(e); // 1) call onExit on old state
476
477 if (t.m_action)
478 t.m_action.Action(e); // 2) execute transition action (if any)
479
480 m_State = t.m_dstState; // 3) change state to new
481
482 if (t.m_dstState != NULL)
483 {
484 m_State.OnEntry(e); // 4a) call onEntry on new state
485
486 if (GetOwnerState())
487 GetOwnerState().OnSubMachineChanged(t.m_srcState, t.m_dstState); // 5a) notify owner state about change in submachine
488
489 if (m_State)
490 m_State.OnStateChanged(t.m_srcState, t.m_dstState); // 5b) notify current state about change in machine
491
492 return ProcessEventResult.FSM_OK;
493 }
494 else
495 {
496 fsmDebugPrint("[hfsm] terminating fsm: state=" + t.m_srcState.ToString() + " event=" + e.ToString());
497
498 if (GetOwnerState())
499 GetOwnerState().OnSubMachineChanged(t.m_srcState, NULL); // 5) notify owner state about change in submachine
500 return ProcessEventResult.FSM_TERMINATED; // 4b) or terminate
501 }
502 }
509 protected ProcessEventResult LocalTransition (int i, FSMEventBase e)
510 {
512 ProcessEventResult ret = ProcessLocalTransition(t, e);
513 return ret;
514 }
515
517 {
518 int completionIdx = FindFirstCompletionTransition();
519 while (completionIdx != -1)
520 {
523 if (row.m_dstState != NULL)
524 {
525 // this is regular completion transition
526 if (row.m_srcState.GetParentState() == row.m_dstState.GetParentState())
527 res = LocalTransition(completionIdx, NULL); // transition is within this state machine
528 else
529 Error("cross-hierarchy transition or misconfigured transition detected!");
530 //res = HierarchicTransition(completionIdx, NULL); // transition has to cross hierarchy
531 }
532 else
533 {
534 // this is terminating completion transition
535 if (row.m_srcState.GetParentState() == GetOwnerState())
536 res = LocalTransition(completionIdx, NULL); // terminating transition is within this state machine
537 else
538 Error("cross-hierarchy transition or misconfigured transition detected!");
539 //res = HierarchicTransition(completionIdx, NULL); // source node crosses hierarchy (terminate lies always within this machine)
540 }
541
542 completionIdx = FindFirstCompletionTransition();
543 }
544 return ProcessEventResult.FSM_NO_TRANSITION;
545 }
546};
547
proto string ToString()
ProcessEventResult
Definition FSMBase.c:33
void fsmDebugSpam(string s)
Definition HFSMBase.c:5
void fsmDebugPrint(string s)
Definition HFSMBase.c:1
string Type
protected float m_DrainThreshold protected bool m_State
Super root of all classes in Enforce script.
Definition EnScript.c:11
represents transition src -— event[guard]/action -—|> dst
ProcessEventResult ProcessEvent(FSMEventBase e)
instructs the hierarchical state machine to process the event e
Definition HFSMBase.c:316
void Start(FSMEventBase initial_event=NULL, bool useExistingState=false)
starts the state machine by entering the initial_state (using intial_event as argument to initial sta...
Definition HFSMBase.c:89
FSMStateBase ProcessAbortEvent(FSMEventBase e, out ProcessEventResult result)
instructs the hierarchical state machine to process the event e
Definition HFSMBase.c:214
protected ref FSMStateBase m_InitialState
state that owns this fsm (or null if root)
Definition HFSMBase.c:23
void Update(float dt)
if machine running, call OnUpdate() on current state
Definition HFSMBase.c:129
void AddTransition(FSMTransition< FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase > t)
adds transition into transition table
Definition HFSMBase.c:74
protected ProcessEventResult ProcessAbortTransition(FSMTransition< FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase > t, FSMEventBase e)
Definition HFSMBase.c:135
bool GetHierarchyPath(FSMStateBase state, out array< FSMStateBase > path)
returns hierarchic state (path to root) of a state
Definition HFSMBase.c:52
protected ProcessEventResult ProcessLocalTransition(FSMTransition< FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase > t, FSMEventBase e)
instructs the state machine to process the event locally - no hierarchy is crossed
Definition HFSMBase.c:471
void Terminate(FSMEventBase terminal_event=NULL)
terminates the state machine
Definition HFSMBase.c:108
base class for hierarchic finite state machine
Result for an object found in CGame.IsBoxCollidingGeometryProxy.
proto string ToString()
void Error(string err)
Messagebox with error message.
Definition EnDebug.c:90
proto void Print(void var)
Prints content of variable to console/log.
static proto string ToString(void var, bool type=false, bool name=false, bool quotes=true)
Return string representation of variable.
bool IsRunning()
Definition tools.c:263