/**@class WeaponStableState
* @brief represents weapon's stable state (i.e. the basic states that the weapon will spend the most time in)
*
* Stable states have associated animation states that they supposed to be in.
* If they are not, the SetWeaponAnimState is called on the weapon in order to
* set to required (configured) state
*
* @NOTE: OnExit from stable state, the weapon's animation state (@see
* Weapon_Base::m_weaponAnimState) is set to -1 (unknown) In case of action
* abort the final stable state is forced to set proper animation state
* according to configuration (@see m_animState)
**/
enum MuzzleState
{
//! UNKNOWN
U = -1,
//! EMPTY
E = 0,
//! FIRED
F = 1,
//! LOADED
L = 2
}
class WeaponStableState extends WeaponStateBase
{
int m_animState;
ref array<MuzzleState> m_muzzleHasBullet = new array<MuzzleState>();
void WeaponStableState(Weapon_Base w = NULL, WeaponStateBase parent = NULL, int anim_state = -1)
{
m_animState = anim_state;
InitMuzzleArray();
ValidateMuzzleArray();
}
void SyncAnimState()
{
int curr = m_weapon.GetWeaponAnimState();
if (curr != m_animState)
{
//if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " synchronizing anim state: " + curr + " --> " + m_animState);
PlayerBase p;
if (Class.CastTo(p, m_weapon.GetHierarchyParent()))
{
if (p.GetItemInHands() == m_weapon)
{
HumanCommandWeapons hcw = p.GetCommandModifier_Weapons();
if (hcw)
{
hcw.SetInitState(m_animState);
if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " state=" + m_weapon.GetCurrentState().Type() + " synchronized anim state: " + typename.EnumToString(PistolAnimState, curr) + " --> " + typename.EnumToString(PistolAnimState, m_animState));
}
else
{
Human wpnOwner = Human.Cast(m_weapon.GetHierarchyRootPlayer());
HumanCommandWeapons.StaticSetInitState(wpnOwner, m_animState);
if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[wpnfsm] " + Object.GetDebugName(m_weapon) + " state=" + m_weapon.GetCurrentState().Type() + " synchronized remote anim state: " + typename.EnumToString(PistolAnimState, curr) + " --> " + typename.EnumToString(PistolAnimState, m_animState));
}
}
m_weapon.SetWeaponAnimState(m_animState);
}
else
{
m_weapon.SetGroundAnimFrameIndex(m_animState);
}
}
}
override void OnEntry(WeaponEventBase e)
{
super.OnEntry(e);
if (e)
SyncAnimState();
m_weapon.SetJammed(IsJammed());
m_weapon.SetCharged(!IsDischarged());
m_weapon.SetWeaponOpen(IsWeaponOpen());
m_weapon.ResetWeaponAnimState();
}
override void OnUpdate(float dt)
{
super.OnUpdate(dt);
SyncAnimState();
}
override void OnExit(WeaponEventBase e)
{
m_weapon.ResetWeaponAnimState();
super.OnExit(e);
}
override bool IsIdle() { return true; }
int GetCurrentStateID() { return 0; }
/** \name Weapon state properties
* Properties defining the state in several properties for other systems
* Override these to set them up
*
* Several systems depends on this:
* WeaponFSM.RandomizeFSMState, which picks out a suitable FSM state when attaching magazine through CE
* WeaponFSM.ValidateAndRepair, which will attempt to identify a desync and repair the FSM state if so
*
* @WARNING So it is important to set these up correctly to prevent any desync of weapon FSM and actual wepon state
*/
//@{
/**@brief Whether WeaponFSM.ValidateAndRepair should be applied on this state
* @NOTE: This property was implemented at the same time as ValARep
* To have it disabled by default
* In case there are modders who created their own state
* But potentially did not set up their properties correctly
* As having ValARep run on an improper setup would result in horrible VME spam and desync
* @WARNING: When enabling repair, it is imperative that all properties are set up correctly.
**/
bool IsRepairEnabled() { return false; }
/**@brief Whether there is a bullet in the chamber
* @NOTE: This should only be false when it is empty
* So this is true when there is a bullet in the chamber
* Regardless of the bullet being loaded, firedout or jammed
**/
bool HasBullet() { return false; }
//! Whether there is a magazine attached
bool HasMagazine() { return false; }
//! Whether the gun is jammed
bool IsJammed() { return false; }
//! Whether the gun is discharged
bool IsDischarged() { return false; }
//! Whether the gun is open
bool IsWeaponOpen() { return false; }
//! Override with the filling of m_muzzleHasBullet
void InitMuzzleArray() { m_muzzleHasBullet = { MuzzleState.U }; }
//! Special one for when the weapon only has one singular state (like Magnum)
bool IsSingleState() { return false; }
//@}
/** \name Weapon state properties helpers
* Several helpers for better access of certain properties
*/
//@{
//! Get chamber state of the muzzle at index
MuzzleState GetMuzzleState(int idx) { return m_muzzleHasBullet[idx]; }
int GetMuzzleStateCount() { return m_muzzleHasBullet.Count(); }
bool IsChamberValid(int idx) { return m_muzzleHasBullet[idx] != MuzzleState.U; }
bool IsChamberFiredOut(int idx) { return m_muzzleHasBullet[idx] == MuzzleState.F; }
bool IsChamberFull(int idx) { return m_muzzleHasBullet[idx] != MuzzleState.E; }
//@}
//! Safety check and error message in case not set up correctly
void ValidateMuzzleArray()
{
// There is only one
if (IsSingleState())
return;
bool failed = false;
if (!failed)
{
foreach (MuzzleState state : m_muzzleHasBullet)
{
if ( state == MuzzleState.U )
{
failed = true;
break;
}
}
}
if (failed)
{
ErrorExSeverity severity = ErrorExSeverity.ERROR;
#ifdef DEVELOPER
if (DayZGame.m_IsPreviewSpawn)
{
// we do not want VMEs when spawning the item in order to visualize it for preview in script console
severity = ErrorExSeverity.INFO;
}
#endif
ErrorEx("Muzzle array validation has failed. Please set up the correct muzzle states by overriding InitMuzzleArray.", severity);
}
}
};
Esc
↑↓ to navigate · ↵ to open · type Class.member to scope