/**@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; } };