/**@class WeaponStateBase
* @brief represent weapon state base
*
* Class comes with entry/update/abort/exit hooks that can be overriden in custom states
*
* Class is ready for hierarchic composition, i.e. this state having a sub-machine running
* under hood. If no m_fsm member is configured, class acts as ordinary plain
* finite machine state.
**/
class WeaponStateBase
{
Weapon_Base m_weapon; /// weapon that this state relates to
WeaponStateBase m_parentState; /// hierarchical parent state of this state (or null)
ref WeaponFSM m_fsm; /// nested state machine (or null)
int m_InternalID = -1; /// internal state id used for load/restore
void WeaponStateBase (Weapon_Base w = NULL, WeaponStateBase parent = NULL) { m_weapon = w; m_parentState = parent; }
/**@fn SetParentState
* @brief allows construction of hierarchical state machine
**/
void SetParentState (WeaponStateBase parent) { m_parentState = parent; }
/**@fn GetParentState
* @return state that owns this sub-state (or null if plain state)
**/
WeaponStateBase GetParentState () { return m_parentState; }
bool HasFSM () { return m_fsm != NULL; }
WeaponFSM GetFSM () { return m_fsm; }
void SetInternalStateID (int i) { m_InternalID = i; }
int GetInternalStateID () { return m_InternalID; }
bool SaveCurrentFSMState (ParamsWriteContext ctx)
{
if (HasFSM())
{
if (IsIdle())
{
if (LogManager.IsWeaponLogEnable()) { wpnDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::SaveCurrentFSMState - idle state, skipping other substates"); }
return m_fsm.SaveCurrentFSMState(ctx);
}
else
{
// if parent state is !idle (unstable) then save whole machine
if (LogManager.IsWeaponLogEnable()) { wpnDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::SaveCurrentFSMState - NOT idle state, saving full submachine state"); }
return m_fsm.SaveCurrentUnstableFSMState(ctx);
}
return false;
}
return true;
}
bool LoadCurrentFSMState (ParamsReadContext ctx, int version)
{
if (HasFSM())
{
if (IsIdle())
{
if (LogManager.IsWeaponLogEnable()) { wpnDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::LoadCurrentFSMState - idle state, skipping other substates"); }
if (m_fsm.LoadCurrentFSMState(ctx, version))
return true;
else
Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::LoadCurrentFSMState - Cannot load stable state for weapon=" + this);
}
else
{
// if parent state is !idle (unstable) then load whole machine
if (LogManager.IsWeaponLogEnable()) { wpnDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::LoadCurrentFSMState - NOT idle state, loading full submachine state"); }
if (m_fsm.LoadCurrentUnstableFSMState(ctx, version))
return true;
else
Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " WeaponStateBase::LoadCurrentFSMState - Cannot load unstable state for weapon=" + this);
}
return false;
}
return true;
}
bool ProcessEvent (WeaponEventBase e)
{
if (HasFSM())
return m_fsm.ProcessEvent(e);
return false;
}
/**@fn AddTransition
* @brief adds transition into m_fsm transition table
**/
void AddTransition (WeaponTransition t)
{
if (HasFSM())
m_fsm.AddTransition(t);
else
Error("[wpnfsm] " + Object.GetDebugName(m_weapon) + " adding transition to state without FSM. Configure FSM first.");
}
/**@fn OnEntry
* @brief called upon entry to state
* @NOTE if state has (non-running) sub-machine, it's started on entry
* @param[in] e the event that triggered transition to this state
**/
void OnEntry (WeaponEventBase e)
{
if (HasFSM() && !m_fsm.IsRunning())
{
if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " { " + this.Type().ToString() + " Has Sub-FSM! Starting submachine..."); }
m_fsm.Start(e);
}
else
if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " { " + this.Type().ToString()); }
}
/**@fn OnUpdate
* @brief ongoing behavior, performed while being in the state
*
* @NOTE: this is supposed to be the Do() operation in UML speak
**/
void OnUpdate (float dt)
{
if (HasFSM() && m_fsm.IsRunning())
m_fsm.GetCurrentState().OnUpdate(dt);
}
/**@fn OnAbort
* @brief called when abort signal arrives
* @param[in] e the event that triggered abort from this state
**/
void OnAbort (WeaponEventBase e)
{
if (HasFSM() && m_fsm.IsRunning())
{
if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " OnAbort " + this.Type().ToString() + " Has Sub-FSM! Aborting submachine..."); }
m_fsm.Abort(e);
}
if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " } ABORTED " + this.Type().ToString()); }
}
/**@fn OnExit
* @brief called on exit from state
* @param[in] e the event that triggered transition from this state
**/
void OnExit (WeaponEventBase e)
{
if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(m_weapon) + " } " + this.Type().ToString()); }
}
/**@fn IsWaitingForActionFinish
* @brief waiting for active animation action/actionType finish
* @return true if this state or active substate is waiting for finish signal
**/
bool IsWaitingForActionFinish () { return HasFSM() && m_fsm.IsRunning() && m_fsm.GetCurrentState().IsWaitingForActionFinish(); }
/**@fn IsIdle
* @brief idle state does not expect any animation events
* @return true if this state is idle
**/
bool IsIdle () { return false; }
/**@fn IsBoltOpen
* @return true if weapon bolt is open
**/
bool IsBoltOpen () { return false; }
/**@fn OnSubMachineChanged
* @brief called when sub-machine has changed its state
* @param[in] src from state (previous)
* @param[in] dst to state (current)
**/
void OnSubMachineChanged (WeaponStateBase src, WeaponStateBase dst) { }
/**@fn OnStateChanged
* @brief called on current state when state machine has changed its state
* @param[in] src from state (previous)
* @param[in] dst to state (current)
**/
void OnStateChanged (WeaponStateBase src, WeaponStateBase dst) { }
float GetCoolDown() { return 0; }
};
Esc
↑↓ to navigate · ↵ to open · type Class.member to scope