Files
GTASource/game/task/Physics/TaskNM.h
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1641 lines
49 KiB
C++

// Filename : TaskNM.h
// Description: FSM base classes for all natural motion related tasks.
// Contains CTaskNMBehaviour from which all natural motion tasks are derived and
// CTaskNMControl which is a high level FSM derived class responsible for starting,
// running and stopping each NM behaviour task.
#ifndef INC_TASKNM_H_
#define INC_TASKNM_H_
// --- Include Files ------------------------------------------------------------
// rage headers
#include "fragmentnm/nmbehavior.h"
#include "fragmentnm/nm_channel.h"
#include "fragmentnm/messageparams.h"
// Game headers
#include "animation/MovePed.h"
#include "peds/QueriableInterface.h"
#include "Task/Physics/AnimPoseHelper.h"
#include "Task/System/Task.h"
#include "Task/System/TaskFSMClone.h"
#include "Task/System/TaskTypes.h"
#include "system/FileMgr.h"
#include "Physics/gtaInst.h"
// ------------------------------------------------------------------------------
#if !__NO_OUTPUT
#define nmEntityDebugf(pEnt, fmt,...) if (pEnt) { nmDebugf3("[%u:%u] - %s(%p): " fmt, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount(), (pEnt)->GetModelName(), (pEnt), ##__VA_ARGS__); }
#define nmTaskDebugf(pTask, fmt,...) if (pTask) { nmDebugf3("[%u:%u] - Task:%s %s(%p): " fmt, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount(), pTask->GetName().c_str() , pTask->GetEntity() ? pTask->GetEntity()->GetModelName() : "No entity", pTask->GetEntity(), ##__VA_ARGS__); }
#else
#define nmEntityDebugf(pEnt,fmt,...) do {} while(false)
#define nmTaskDebugf(pTask,fmt,...) do {} while(false)
#endif //__NO_OUTPUT
#if (__DEV) && (__ASSERT)
#define ASSERT_PARAMETER_NAME(s) Assertf(!strcmp(name, s), "%s expected got %s!", s, name)
#else
#define ASSERT_PARAMETER_NAME(s)
#endif
#define NMSTR_MSG(STRING_ENUM) CTaskNMBehaviour::GetMsgString(STRING_ENUM)
#define NMSTR_PARAM(STRING_ENUM) CTaskNMBehaviour::GetParamString(STRING_ENUM)
#define LOCAL_POS_MAG_LIMIT 10.0f
// Forward declarations:
class ARTFeedbackInterfaceGta;
class CTaskNMControl;
class CTaskNMBehaviour;
XPARAM(nmtuning);
// nm message list. Should be allocated on the stack, and populated from code / nm tuning sets
class CNmMessageList
{
public:
CNmMessageList()
{
}
~CNmMessageList()
{
Clear();
}
// gets the appropriate message (adding a new one if necessary)
ART::MessageParams& GetMessage(const char * messageName, s32 numFreeParams = 1)
{
atFinalHashString hash(messageName);
return GetMessage(hash, numFreeParams);
}
ART::MessageParams& GetMessage(atFinalHashString messageHash, s32 numFreeParams = 1);
bool HasMessage(const char * messageName)
{
atFinalHashString hash(messageName);
return HasMessage(hash);
}
bool HasMessage(atFinalHashString messageHash);
void Post(fragInstNMGta* pInst);
void Clear();
static ART::MessageParamsBase::Parameter* FindParam(ART::MessageParams& msg, const char* pParamName)
{
for (s32 i=msg.getUsedParamCount()-1; i>=0; i--)
{
if (!strcmp(msg.getParam(i).m_name,pParamName))
{
return &msg.getParam(i);
}
}
return NULL;
}
private:
struct message{
atFinalHashString m_name;
ART::MessageParams* m_message;
};
atArray< message> m_messages;
};
class CNmParameter
{
public:
static atHashString ms_ResetAllParameters;
CNmParameter() { }
virtual ~CNmParameter() { }
atFinalHashString m_Name;
virtual void AddTo(ART::MessageParams& UNUSED_PARAM(msg)) const
{
}
#if __BANK
virtual void AddWidgets(bkGroup&, const NMBehavior&)
{
}
#endif //__BANK
PAR_PARSABLE;
};
class CNmParameterResetMessage : public CNmParameter
{
public:
CNmParameterResetMessage() { }
virtual ~CNmParameterResetMessage() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.reset();
}
#if __BANK
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
PAR_PARSABLE;
};
class CNmParameterFloat : public CNmParameter
{
public:
CNmParameterFloat() { }
virtual ~CNmParameterFloat() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addFloat(m_Name.GetCStr(), m_Value);
}
#if __BANK
CNmParameterFloat(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
float m_Value;
PAR_PARSABLE;
};
class CNmParameterRandomFloat : public CNmParameter
{
public:
CNmParameterRandomFloat() { }
virtual ~CNmParameterRandomFloat() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addFloat(m_Name.GetCStr(), fwRandom::GetRandomNumberInRange(m_Min, m_Max));
}
#if __BANK
CNmParameterRandomFloat(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
float m_Min;
float m_Max;
PAR_PARSABLE;
};
class CNmParameterInt : public CNmParameter
{
public:
CNmParameterInt() { }
virtual ~CNmParameterInt() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addInt(m_Name.GetCStr(), m_Value);
}
#if __BANK
CNmParameterInt(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
int m_Value;
PAR_PARSABLE;
};
class CNmParameterRandomInt : public CNmParameter
{
public:
CNmParameterRandomInt() { }
virtual ~CNmParameterRandomInt() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addInt(m_Name.GetCStr(), fwRandom::GetRandomNumberInRange(m_Min, m_Max));
}
#if __BANK
CNmParameterRandomInt(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
s32 m_Min;
s32 m_Max;
PAR_PARSABLE;
};
class CNmParameterBool : public CNmParameter
{
public:
CNmParameterBool() { }
virtual ~CNmParameterBool() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addBool(m_Name.GetCStr(), m_Value);
}
#if __BANK
CNmParameterBool(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
bool m_Value;
PAR_PARSABLE;
};
class CNmParameterString : public CNmParameter
{
public:
CNmParameterString() { }
virtual ~CNmParameterString() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addString(m_Name.GetCStr(), m_Value.GetCStr());
}
#if __BANK
CNmParameterString(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
atFinalHashString m_Value;
PAR_PARSABLE;
};
class CNmParameterVector : public CNmParameter
{
public:
CNmParameterVector() { }
virtual ~CNmParameterVector() { }
virtual void AddTo(ART::MessageParams& msg) const
{
msg.addVector3(m_Name.GetCStr(), m_Value.x, m_Value.y, m_Value.z);
}
#if __BANK
CNmParameterVector(const NMParam* pParam);
virtual void AddWidgets(bkGroup& bank, const NMBehavior& behavior);
#endif //__BANK
Vector3 m_Value;
PAR_PARSABLE;
};
class CNmMessage : datBase
{
public:
CNmMessage()
#if __BANK
: m_pWidgets(NULL)
, m_SelectedParameter(0)
#endif //__BANK
{ }
virtual ~CNmMessage()
{
#if __BANK
m_pWidgets=NULL;
#endif //__BANK
}
void AddTo(CNmMessageList& list, CTaskNMBehaviour* pTask) const;
void Post(CPed& ped, CTaskNMBehaviour* pTask) const;
// adds the editing widgets for the message
#if __BANK
void AddWidgets(bkGroup& bank);
bkGroup* m_pWidgets;
s32 m_SelectedParameter;
void AddSingleParameter();
void AddRandomRange();
void AddAllParameters();
void RemoveSingleParameter();
void RemoveDefaultValueParameters();
void RemoveAllParameters();
void PromoteSelectedParam();
void DemoteSelectedParam();
void AddParameter(const char * name, bool withRandomRange = false);
void RemoveParameter(const char * name);
void RegenerateParamWidgets();
#endif //__BANK
atFinalHashString m_Name;
atArray<CNmParameter*> m_Params;
bool m_ForceNewMessage;
bool m_TaskMessage;
PAR_SIMPLE_PARSABLE;
};
class CNmTuningSet
{
public:
CNmTuningSet();
virtual ~CNmTuningSet();
void AddTo(CNmMessageList& list, CTaskNMBehaviour* pTask) const;
void Post(CPed& ped, CTaskNMBehaviour* pTask) const;
void Post(CPed& ped, CTaskNMBehaviour* pTask, atHashString messageHash) const;
bool IsEmpty() const { return m_Messages.GetCount()==0;}
bool IsEnabled() const { return m_Enabled;}
// adds the editing widgets for the message
#if __BANK
void AddWidgets(bkBank& bank) { AddWidgetsInternal(*static_cast<bkGroup*>(bank.GetCurrentGroup())); }
void AddWidgetsInternal(bkGroup& bank);
bkGroup* m_pWidgets;
static s32 sm_SelectedMessage;
static atArray<const char *> sm_MessageNames;
static atMap<u32, atArray<const char *> * > sm_ParameterNamesMap;
void AddSingleMessage();
void RemoveSingleMessage();
void RemoveAllMessages();
void PromoteSelectedMessage();
void DemoteSelectedMessage();
void AddMessage(const char * name);
void RemoveMessage(const char * name);
void RegenerateMessageWidgets();
static NMBehaviorPool sm_TaskMessageDefs;
#endif //__BANK
atHashString m_Id;
int m_Priority;
bool m_Enabled;
atArray<CNmMessage*> m_Messages;
PAR_SIMPLE_PARSABLE;
};
class CNmTuningSetGroup
{
public:
CNmTuningSet* Get(const atHashString& setName)
{
return m_sets.Access(setName);
}
void Append(CNmTuningSet* newItem, atHashString key)
{
if(!m_sets.Access(key))
{
m_sets.Insert(key,*newItem);
m_sets.FinishInsertion();
}
}
void Revert(atHashString key)
{
m_sets.Remove(m_sets.GetIndexFromDataPtr(m_sets.Access(key)));
#if __BANK
bkWidget* child = m_pWidgets->GetChild();
while(child)
{
bkWidget*lastChild = child;
child = child->GetNext();
if (atStringHash(lastChild->GetTitle())==key)
{
lastChild->Destroy();
}
}
#endif //__BANK
}
private:
atBinaryMap< CNmTuningSet, atHashString > m_sets;
#if __BANK
public:
void AddParamWidgets(bkBank& bank);
bkGroup* m_pWidgets;
static char ms_SetName[128];
void AddSet();
void RemoveSet();
#endif //__BANK
PAR_SIMPLE_PARSABLE;
};
////////////////////////////////////////////////////////////////////////
// FSM version of the base class of all natural motion behaviour tasks.
////////////////////////////////////////////////////////////////////////
class CTaskNMBehaviour : public CTaskFSMClone
{
public:
struct Tunables : CTuning
{
Tunables();
CNmTuningSet m_animPoseDefault;
CNmTuningSet m_animPoseAttachDefault;
CNmTuningSet m_animPoseAttachToVehicle;
CNmTuningSet m_animPoseHandsCuffed;
CNmTuningSet m_forceFall;
struct TunableForce
{
bool ShouldApply(u32 startTime);
// Pass in a normalised direction vector for the force.
void GetImpulseVec(Vec3V_InOut forceVec, Vec3V_In velVec, const CPed* pPed);
bool m_Enable;
float m_Mag;
bool m_ScaleWithVelocity;
float m_VelocityMin;
float m_VelocityMax;
float m_ForceAtMinVelocity;
float m_ForceAtMaxVelocity;
bool m_ClampImpulse;
float m_MinImpulse;
float m_MaxImpulse;
u32 m_Delay;
u32 m_Duration;
bool m_ScaleWithUpright;
PAR_SIMPLE_PARSABLE;
};
struct InverseMassScales
{
bool Apply(phContactIterator* pImpacts);
bool m_ApplyVehicleScale;
float m_VehicleScale;
bool m_ApplyPedScale;
float m_PedScale;
PAR_SIMPLE_PARSABLE;
};
struct Damping
{
bool Apply(CPed* pPed);
bool m_ApplyLinear;
Vector3 m_Constant;
Vector3 m_Velocity;
Vector3 m_Velocity2;
float m_Max;
PAR_SIMPLE_PARSABLE;
};
struct ActivationLimitModifiers
{
float m_BumpedByCar;
float m_BumpedByCarFriendly;
float m_PlayerBumpedByCar;
float m_MinVehicleWarning;
float m_BumpedByPedMinVel;
float m_BumpedByPedMinDotVel;
float m_BumpedByPed;
float m_BumpedByPlayerRagdoll;
float m_BumpedByPedRagdoll;
float m_BumpedByPedIsQuadruped;
float m_BumpedByPedFriendly;
float m_BumpedByObject;
float m_Walking;
float m_Running;
float m_Sprinting;
float m_MaxPlayerActivationLimit;
float m_MaxAiActivationLimit;
PAR_SIMPLE_PARSABLE;
};
struct BoundWeight
{
RagdollComponent m_Bound;
float m_Weight;
PAR_SIMPLE_PARSABLE;
};
struct RagdollUnderWheelTuning
{
float m_fMinSpeedForPush;
float m_fImpulseMultLimbs;
float m_fImpulseMultSpine;
float m_fFastCarPushImpulseMult;
PAR_SIMPLE_PARSABLE;
};
RagdollUnderWheelTuning m_RagdollUnderWheelTuning;
struct KickOnGroundTuning
{
float m_fPronePedKickImpulse;
PAR_SIMPLE_PARSABLE;
};
KickOnGroundTuning m_KickOnGroundTuning;
struct BlendOutThreshold
{
float m_MaxLinearVelocity;
float m_MaxAngularVelocity;
u32 GetSettledTime(u32 seed) const;
private:
u32 m_SettledTimeMS;
bool m_RandomiseSettledTime;
u32 m_SettledTimeMinMS;
public:
PAR_SIMPLE_PARSABLE;
};
struct StandardBlendOutThresholds
{
const BlendOutThreshold& PickBlendOut(const CPed* pPed) const;
BlendOutThreshold m_Ai;
BlendOutThreshold m_Player;
BlendOutThreshold m_PlayerMp;
PAR_SIMPLE_PARSABLE;
};
struct PedCapsuleVehicleImpactTuning
{
bool m_EnableActivationsFromCapsuleImpacts;
float m_VehicleVelToImpactNormalMinDot;
bool m_EnableSideSwipeActivations;
bool m_EnableSideSwipeActivationsFirstPerson;
float m_MinSideNormalForSideSwipe;
float m_MinVelThroughNormalForSideSwipe;
float m_MinAccumulatedImpactForSideSwipe;
float m_MinVehVelMagForSideSwipe;
float m_MinVehVelMagForBicycleSideSwipe;
float m_MinAccumulatedImpactForSideSwipeCNC;
float m_MinVehVelMagForSideSwipeCNC;
float m_MinVehVelMagForBicycleSideSwipeCNC;
PAR_SIMPLE_PARSABLE;
};
PedCapsuleVehicleImpactTuning m_CapsuleVehicleHitTuning;
void ParserPostLoad();
bool m_EnableRagdollPooling;
u32 m_MaxGameplayNmAgents;
u32 m_MaxRageRagdolls;
bool m_ReserveLocalPlayerNmAgent;
bool m_EnableRagdollPoolingMp;
u32 m_MaxGameplayNmAgentsMp;
u32 m_MaxRageRagdollsMp;
bool m_ReserveLocalPlayerNmAgentMp;
bool m_BlockOffscreenShotReactions;
bool m_UsePreEmptiveEdgeActivation;
bool m_UsePreEmptiveEdgeActivationMp;
bool m_UseBalanceForEdgeActivation;
float m_PreEmptiveEdgeActivationMaxVel;
float m_PreEmptiveEdgeActivationMaxHeadingDiff;
float m_PreEmptiveEdgeActivationMinDotVel;
float m_PreEmptiveEdgeActivationMaxDistance;
float m_PreEmptiveEdgeActivationMinDesiredMBR2;
void OnMaxPlayerAgentsChanged();
void OnMaxGameplayAgentsChanged();
void OnMaxRageRagdollsChanged();
StandardBlendOutThresholds m_StandardBlendOutThresholds;
atArray<BoundWeight> m_CamAttachPositionWeights;
ActivationLimitModifiers m_SpActivationModifiers;
ActivationLimitModifiers m_MpActivationModifiers;
float m_PlayerBumpedByCloneCarActivationModifier; // this one isn't needed for single player.
float m_ClonePlayerBumpedByCarActivationModifier; // this one isn't needed for single player.
float m_ClonePedBumpedByCarActivationModifier; // this one isn't needed for single player.
float m_MaxVehicleCapsulePushTimeForRagdollActivation;
float m_MaxVehicleCapsulePushTimeForPlayerRagdollActivation;
float m_VehicleMinSpeedForContinuousPushActivation;
float m_MinContactDepthForContinuousPushActivation;
float m_DurationRampDownCapsulePushedByVehicle;
float m_VehicleMinSpeedForAiActivation;
float m_VehicleMinSpeedForStationaryAiActivation;
float m_VehicleMinSpeedForPlayerActivation;
float m_VehicleMinSpeedForStationaryPlayerActivation;
float m_VehicleMinSpeedForWarningActivation;
float m_VehicleFallingSpeedWeight;
float m_VehicleActivationForceMultiplierDefault;
float m_VehicleActivationForceMultiplierBicycle;
float m_VehicleActivationForceMultiplierBike;
float m_VehicleActivationForceMultiplierBoat;
float m_VehicleActivationForceMultiplierPlane;
float m_VehicleActivationForceMultiplierQuadBike;
float m_VehicleActivationForceMultiplierHeli;
float m_VehicleActivationForceMultiplierTrain;
float m_VehicleActivationForceMultiplierRC;
bool m_ExcludePedBumpAngleFromPushCalculation;
float m_PedActivationForceMultiplier;
u32 m_MaxPlayerCapsulePushTimeForRagdollActivation;
float m_PlayerCapsuleMinSpeedForContinuousPushActivation;
float m_ObjectMinSpeedForActivation;
float m_ObjectActivationForceMultiplier;
float m_StuckOnVehicleMaxTime;
StandardBlendOutThresholds m_StuckOnVehicleBlendOutThresholds;
CNmTuningSet m_Start;
CNmTuningSet m_TeeterControl;
PAR_PARSABLE;
};
enum
{
ANIM_POSE_DEFAULT,
ANIM_POSE_ATTACH_DEFAULT,
ANIM_POSE_ATTACH_TO_VEHICLE,
ANIM_POSE_HANDS_CUFFED,
};
enum
{
State_Start = 0,
State_StreamingClips,
State_BehaviourRunning,
State_AnimatedFallback,
State_Finish
};
enum eMonitorState
{
MONITOR_RELAX = 0,
MONITOR_FALL,
MONITOR_STAND,
MONITOR_SUBMERGED,
MONITOR_NUM_STATES
};
enum eMonitorType
{
MONITOR_AI = 0,
MONITOR_PLAYER,
MONITOR_PLAYER_MP,
MONITOR_NUM_TYPES
};
enum eMonitorFlags {
FLAG_VEL_CHECK = BIT(0), // Finish the task once the peds velocity drops below a certain threshold
FLAG_SKIP_DEAD_CHECK = BIT(1), // Don't abort the task immediately on death
FLAG_SKIP_WATER_CHECK = BIT(2), // unused
FLAG_RELAX_AP_LOW_HEALTH = BIT(3), // When exiting the task, tell nm control to do a relax if the peds health is low (below 110)
FLAG_QUIT_IN_WATER = BIT(4),
FLAG_SKIP_MIN_TIME_CHECK = BIT(5)
};
enum eNMBehaviourFlags
{
ALL_FLAGS_CLEAR = 0,
// In case an clipPose behaviour is required for a particular behaviour (but not meant to
// carry over to any other NM task which might get started), we include the ability to
// set this flag through the behaviour task.
DO_CLIP_POSE = BIT(0),
DONT_SWITCH_TO_ANIMATED_ON_ABORT = BIT(1),
DONT_FINISH = BIT(2) // used for clone peds to keep the task running until told to stop via a network update
};
enum eRagdollPool
{
kRagdollPoolNmGameplay = 0,
kRagdollPoolRageRagdoll,
kRagdollPoolLocalPlayer,
kNumRagdollPools,
kRagdollPoolInvalid,
kRagdollPoolMax = kRagdollPoolInvalid
};
CompileTimeAssert(kRagdollPoolMax < BIT(3));
class CRagdollPool
{
public:
CRagdollPool();
~CRagdollPool();
void AddToPool(CPed& ped);
void RemoveFromPool(CPed& ped);
inline s32 GetMaxRagdolls() { return m_MaxRagdolls; }
inline void SetMaxRagdolls(s32 max) {m_MaxRagdolls = max; }
inline s32 GetRagdollCount() { return m_Peds.GetCount(); }
inline s32 GetFreeSlots() { return GetMaxRagdolls() - GetRagdollCount(); }
CPed* GetPed(s32 i) {
if(i>=0 && i<m_Peds.GetCount())
return m_Peds[i];
else
return NULL;
}
private:
u32 m_MaxRagdolls;
atArray<RegdPed> m_Peds;
};
static void AddToRagdollPool(eRagdollPool pool, CPed& ped);
static void RemoveFromRagdollPool(CPed& ped);
static bool RagdollPoolHasSpaceForPed(eRagdollPool pool, const CPed& ped, bool abortExisting = false, float priority = 0.0f);
static bool IsPoolingEnabled()
{
if (NetworkInterface::IsGameInProgress())
{
if (!CTaskNMBehaviour::sm_Tunables.m_EnableRagdollPoolingMp)
return false;
}
else if (!CTaskNMBehaviour::sm_Tunables.m_EnableRagdollPooling)
{
return false;
}
return true;
}
static float CalcRagdollEventScore(CPed* pPed, eRagdollTriggerTypes nTrigger, CEntity* pEntityResponsible, float fPushValue);
static float CalcRagdollMultiplierScore(CPed* pPed, eRagdollTriggerTypes nTrigger);
// PURPOSE: Called by the ragdoll priority system when activating a new ragdoll
// If the new incoming activation is blocked due to a lack of space in the ragdoll pool,
// The lowest priority ragdoll is aborted (assuming its score is lower than the incoming task)
static float CalcRagdollScore(CPed* pPed, eRagdollTriggerTypes nTrigger, CEntity* pEntityResponsible, float fPushValue);
static void StartNetworkGame();
static void EndNetworkGame();
#if !__FINAL
static const char * GetRagdollPoolName(eRagdollPool pool)
{
switch(pool)
{
case kRagdollPoolNmGameplay: return "Gameplay"; break;
case kRagdollPoolRageRagdoll: return "RageRagdoll"; break;
case kRagdollPoolLocalPlayer: return "Player"; break;
case kRagdollPoolInvalid: return "None"; break;
default: return "Unknown!"; break;
}
}
#endif //!__FINAL
static bool IsMessageString(eNMStrings nString);
static const char* GetMsgString(eNMStrings nString){Assertf(IsMessageString(nString), "NM msg enum %d is not a message", nString); return CNmDefines::ms_aNMStrings[nString];}
static const char* GetParamString(eNMStrings nString){Assertf(!IsMessageString(nString), "NM param enum %d is not a param", nString); return CNmDefines::ms_aNMStrings[nString];}
static eEffectorMaskStrings GetMaskEnumFromString(const char * maskString);
// Use this to send start messages
// Either gets run when the task starts updating or when the ragdoll first activates
// Since for certain events there can be a physics update between the ragdoll first activating
// and the task being run.
void SendStartMessages(CPed* pPed);
// Constructor / Destructor.
CTaskNMBehaviour(u32 nMinTime, u32 nMaxTime);
virtual ~CTaskNMBehaviour();
///////////////////////
// CTask functions:
///////////////////////
virtual bool IsNMBehaviourTask() const { return true; }
protected:
CTaskNMBehaviour( const CTaskNMBehaviour& otherTask);
virtual FSM_Return ProcessPreFSM();
virtual FSM_Return UpdateFSM(const s32 iState, const FSM_Event iEvent);
virtual FSM_Return UpdateClonedFSM(const s32 iState, const FSM_Event iEvent);
virtual bool ControlPassingAllowed(CPed* pPed, const netPlayer& player, eMigrationType migrationType);
virtual CTaskFSMClone* CreateTaskForClonePed(CPed* pPed);
virtual CTaskFSMClone* CreateTaskForLocalPed(CPed* pPed);
virtual s32 GetDefaultStateAfterAbort() const {return State_Finish;}
#if !__FINAL
friend class CTaskClassInfoManager;
static const char *GetStaticStateName(s32 iState)
{
Assert(iState>=State_Start&&iState<=State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_StreamingClips",
"State_BehaviourRunning",
"State_AnimatedFallback",
"State_Finish"
};
return aStateNames[iState];
}
// Especially useful for doing debug drawing while game is paused.
virtual void Debug() const {};
#endif // !__FINAL
protected:
// Helper functions for FSM state management:
void Start_OnEnter(CPed* pPed);
FSM_Return Start_OnUpdate(CPed* pPed);
void StreamingClips_OnEnter(CPed* pPed);
FSM_Return StreamingClips_OnUpdate(CPed* pPed);
void BehaviourRunning_OnEnter(CPed* pPed);
FSM_Return BehaviourRunning_OnUpdate(CPed* pPed);
void BehaviourRunning_OnExit(CPed* pPed);
void AnimatedFallback_OnEnter(CPed* pPed);
FSM_Return AnimatedFallback_OnUpdate(CPed* pPed);
void AnimatedFallback_OnExit(CPed* pPed);
public:
// Functions to modify the behaviour's timing variables.
void ResetStartTime();
void ForceTimeout() {m_nMaxTime = 1; m_nStartTime = 1;}
#if __BANK
static void InitCreateWidgetsButton();
static void InitWidgets();
#endif
// Accessors for the NM control flags.
public:
u32 GetFlags() const {return m_nFlags;}
void SetFlag(u32 nFlagToSet) {m_nFlags |= nFlagToSet;}
void ClearFlag(u32 nFlagToClear) { m_nFlags &= ~nFlagToClear;}
// Should this nm behaviour keep running after the ped dies?
virtual bool ShouldContinueAfterDeath() const { return false; }
// should this nm behaviour end when a particular type of damage is received, or keep running and handle it?
virtual bool ShouldAbortForWeaponDamage(CEntity* UNUSED_PARAM(pFiringEntity), const CWeaponInfo* UNUSED_PARAM(pWeaponInfo), const f32 UNUSED_PARAM(fWeaponDamage), const fwFlags32& UNUSED_PARAM(flags),
const bool bWasKilledOrInjured, const Vector3& UNUSED_PARAM(vStart), WorldProbe::CShapeTestHitPoint* UNUSED_PARAM(pResult),
const Vector3& UNUSED_PARAM(vRagdollImpulseDir), const f32 UNUSED_PARAM(fRagdollImpulseMag))
{
if (bWasKilledOrInjured && ShouldContinueAfterDeath())
{
return false;
}
else
{
return true;
}
}
// Called by the physics system when the ragdoll is already active and a significant impact occurs.
// Return true to indicate that this nm behaviour has handled the impact and there's no need to
// generate a new ragdoll task. Return false to have ProcessRagdollImpact add a new
// ragdoll task to deal with the collision.
virtual bool HandleRagdollImpact(float UNUSED_PARAM(fMag), const CEntity* UNUSED_PARAM(pEntity), const Vector3& UNUSED_PARAM(vPedNormal), int UNUSED_PARAM(nComponent), phMaterialMgr::Id UNUSED_PARAM(nMaterialId)) { return false; }
// Functions related to the clipPose helper.
public:
CClipPoseHelper& GetClipPoseHelper();
void SetClipPoseHelperClip(const fwMvClipSetId &clipSetId, const fwMvClipId &clipId);
void SetClipPoseHelperClip(s32 iClipDictIndex, u32 ClipHashKey);
// Returns false if clip data is "invalid".
bool GetClipPoseHelperClip(fwMvClipSetId &clipSetId, fwMvClipId &clipId);
bool GetClipPoseHelperClip(s32 &nClipDictIndex, u32 &nClipHash);
bool GetIsClipDictLoaded(s32 iClipDictIndex);
// Helper function to save checking and casting every time a reference to the NM control task is required from within a behaviour class.
CTaskNMControl* GetControlTask();
// The following functions (which don't have to be implemented) allow NM behaviour tasks to react to
// feedback messages from the natural motion subsystem. Note: use QueryNmFeedbackMessage() to ensure the
// feedback interface is for the correct behaviour.
public:
virtual void BehaviourFailure(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourSuccess(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourStart(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourFinish(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourEvent(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
#if ART_ENABLE_BSPY
// Handy functions for adding items to the bSpy scratchpad
void SendTaskStateToBSpy();
void SetbSpyScratchpadString(const char* pDebugString);
void SetbSpyScratchpadVector3(const Vector3& pDebugVec);
void SetbSpyScratchpadBool(bool debugBool);
void SetbSpyScratchpadInt(int debugInt);
void SetbSpyScratchpadFloat(float debugFloat);
#endif
// Some public access functions to query the state of the current behaviour.
bool GetHasAborted() {return m_bHasAborted;}
bool GetHasFailed() {return m_bHasFailed;}
bool GetHasSucceeded() {return m_bHasSucceeded;}
bool GetHasStarted() {return m_nStartTime > 0;}
virtual bool HasBalanceFailed() const { return false; }
// PURPOSE: This function will be called by other systems when they want the ped to fall over immediately
virtual void ForceFallOver();
virtual bool HandlesRagdoll(const CPed*) const
{
return true;
}
// PURPOSE: Applies a task tuning messsage from the tuning sets
virtual void ApplyTaskTuningMessage(const CNmMessage& message);
// Allow read only access to this behaviour's minimum and maximum time variables. Used when wrapping a
// behaviour task by a CTaskNMControl.
u32 GetMinTime() { return m_nMinTime; }
u32 GetMaxTime() { return m_nMaxTime; }
bool IsHighLODNMAgent(CPed* pPed);
// helper stuff to decide what to do next - either next behaviour to run or how to blend back to clip
fwMvClipId GetSuggestedNextClipId() const {return m_suggestedClipId;}
fwMvClipSetId GetSuggestedNextClipGroup() const {return m_suggestedClipSetId;}
eBlendFromNMOptions GetSuggestedNextBlendOption() const {return m_nSuggestedBlendOption;}
CTaskTypes::eTaskType GetSuggestedNextTask() {return m_nSuggestedNextTask;}
bool GetIsStuckOnVehicle();
virtual float GetPlayerControlForce() {return ms_fStdPlayerRagdollControlForce;}
// Static functions related to the NM message / parameter strings.
static void InitStrings();
static bool IsParamForThisMessage(eNMStrings nMsgString, eNMStrings nParamString);
static int GetMessageStringIndex(eNMStrings nString);
static eNMStrings GetMessageFromIndex(int nMessageIndex) {return CNmDefines::ms_nMessageList[nMessageIndex];}
static CRagdollPool& GetRagdollPool(eRagdollPool pool) { taskAssert(pool>=kRagdollPoolNmGameplay && pool<kNumRagdollPools); return sm_RagdollPools[pool]; }
static bool DoesResponseHandleRagdoll(const CPed* pPed, const aiTask* pResponseTask);
static bool CanUseRagdoll(CPed* pPed, eRagdollTriggerTypes nTrigger, CEntity* pEntityResponsible=NULL, float fPushValue=0.0f);
static bool CanUseRagdoll(CPed* pPed, eRagdollTriggerTypes nTrigger, float fScore);
static float WantsToRagdoll(CPed* pPed, eRagdollTriggerTypes nTrigger, CEntity* pEntityResponsible=NULL, float fPushValue=0.0f);
static bool DoesGroundPhysicalMatch(const CPhysical* pGroundPhysical, const CVehicle* pVehicle);
static float CalculateActivationForce(const CPed* pPed, eRagdollTriggerTypes trigger, const CEntity* pEntityResponsible, Vec3V_ConstPtr pHitPosition = NULL, Vec3V_ConstPtr pHitNormal = NULL, int nEntityComponent = 0, bool testRagdollInst = false);
static bool ShouldUseArmedBumpResistance(const CPed* pPed);
static float GetMinSpeedThreshold(const CPed* pPed, const Vector3& pedVel);
virtual bool ProcessPhysics(float UNUSED_PARAM(fTimeStep), int UNUSED_PARAM(nTimeSlice));
static bool LoadTaskParameters();
// Helper function to test whether the feedback interface passed in corresponds to the
// NM behaviour referenced by "paramName".
static bool QueryNmFeedbackMessage(ARTFeedbackInterfaceGta* pFeedbackInterface, enum eNMStrings paramName);
static bool ShouldReactToVehicleHit(CPed* pPed, CVehicle* pVehicle, const CCollisionRecord* pColRecord);
static float GetPronePedKickImpulse() { return sm_Tunables.m_KickOnGroundTuning.m_fPronePedKickImpulse; }
static float GetWheelMinSpeedForPush() { return sm_Tunables.m_RagdollUnderWheelTuning.m_fMinSpeedForPush; }
static float GetWheelImpulseMultLimbs() { return sm_Tunables.m_RagdollUnderWheelTuning.m_fImpulseMultLimbs; }
static float GetWheelImpulseMultSpine() { return sm_Tunables.m_RagdollUnderWheelTuning.m_fImpulseMultSpine; }
static float GetWheelFastCarPushImpulseMult() { return sm_Tunables.m_RagdollUnderWheelTuning.m_fFastCarPushImpulseMult; }
protected:
// Determine whether or not the task should abort.
virtual bool ShouldAbort( const AbortPriority iPriority, const aiEvent* pEvent);
// Handle aborting the task - this will be called in addition to CleanUp when we are aborting.
virtual void DoAbort(const AbortPriority iPriority, const aiEvent* pEvent);
// Ensure the nm performance will continue for at least the specified amount of time
void BumpPerformanceTime(u32 time);
// returns a normalised float (from -1.0f to 1.0) giving a rough indication of how 'upright' the ragdoll currently is
// 1.0f = upright
// -1.0f = upside down
// 0.0f = horizontal
float CalculateUprightDot(const CPed * pPed);
// All inherited NM behaviours need to implement StartBehaviour() and FinishConditions(). The latter returns a bool
// to inform the caller if the behaviour will quit. A default implementation of ControlBehaviour() is provided, so
// inherited tasks only need to override this if they want.
virtual void StartBehaviour(CPed* pPed) = 0;
virtual void ControlBehaviour(CPed* UNUSED_PARAM(pPed)) {;}
virtual bool FinishConditions(CPed* pPed) = 0;
void CreateAnimatedFallbackTask(CPed* pPed, bool bFalling);
virtual void StartAnimatedFallback(CPed* pPed);
virtual bool ControlAnimatedFallback(CPed* pPed);
virtual void EndAnimatedFallback(CPed* UNUSED_PARAM(pPed)) {;}
virtual void AddStartMessages(CPed* UNUSED_PARAM(pPed), CNmMessageList& UNUSED_PARAM(list)) {;}
bool ProcessBalanceBehaviourEvent(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
bool ProcessFinishConditionsBase(CPed* pPed, eMonitorState nState, int nFlags, const CTaskNMBehaviour::Tunables::BlendOutThreshold* pCustomBlendOutThreshold = NULL);
bool CheckBlendOutThreshold(CPed* pPed, const CTaskNMBehaviour::Tunables::BlendOutThreshold& threshold);
bool OnMovingGround(CPed* pPed);
void AddTuningSet(const CNmTuningSet* pSet);
void ClearTuningSets();
void ApplyTuningSetsToList(CNmMessageList& list, bool bClearAfterSending =true);
static s32 CompareTuningSetPriorityFunc(const CNmTuningSet* const* a, const CNmTuningSet* const* b)
{
return (*a)->m_Priority - (*b)->m_Priority;
}
protected:
bool m_bHasAborted : 1;
bool m_bHasFailed : 1;
bool m_bHasSucceeded : 1;
bool m_bDoBehaviorStart : 1;
bool m_bCuffedClipLoaded : 1;
bool m_RefFrameVelChecked : 1;
bool m_bUseAdaptiveAngularVel : 1;
bool m_bStartMessagesSent : 1;
fwMvClipId m_suggestedClipId;
fwMvClipSetId m_suggestedClipSetId;
float m_suggestedClipPhase;
eBlendFromNMOptions m_nSuggestedBlendOption:8;
CTaskTypes::eTaskType m_nSuggestedNextTask:16;
// Store the clip data for the clipPose helper.
fwMvClipSetId m_clipPoseSetId;
fwMvClipId m_clipPoseId;
s32 m_iClipDictIndex;
s32 m_iClipHash;
atArray<const CNmTuningSet*> m_Sets;
u16 m_nMinTime;
u16 m_nMaxTime;
u32 m_nStartTime;
u32 m_nSettledStartTime; // used when blending from nm. Tracks when the ragdoll first becomes 'settled' enough to blend from nm (based on the threshold passed into processfinishconditionsbase)
u32 m_nFlags;
float m_AdaptiveAngVelMinVel;
float m_AdaptiveAngVelMaxVel;
float m_AdaptiveAngVelDampingVel2;
float m_ContinuousContactTime;
#if __DEV
static bool ms_bDisplayDebug;
#endif
private:
static char sm_pParameterFile[128];
static float ms_fStdPlayerRagdollControlForce;
// some global tunable stuff
public:
static Tunables sm_Tunables;
static CRagdollPool sm_RagdollPools[kNumRagdollPools];
static bool sm_OnlyUseRageRagdolls;
static bool sm_DoOverrideBulletImpulses;
static float sm_OverrideImpulse;
static float sm_ArmsImpulseCap;
static float sm_DontFallUntilDeadStrength;
static float sm_MaxShotUprightForce;
static float sm_MaxShotUprightTorque;
static float sm_ThighImpulseMin;
static float sm_CharacterHealth;
static float sm_CharacterStrength;
static float sm_StayUprightMagnitude;
static float sm_RigidBodyImpulseRatio;
static float sm_ShotRelaxAmount;
static float sm_BulletPopupImpulse;
static float sm_SuccessiveImpulseIncreaseScale;
static float sm_LastStandMaxTimeBetweenHits;
static bool sm_DoLastStand;
static bool ms_bLeanInDirApplyAsForce;
static bool ms_bUseParameterSets;
static bool ms_bDisableBumpGrabForPlayer;
#if __BANK
struct TuningSetEntry
{
atHashString id;
u32 time;
};
static s32 SortTuningSetHistoryFunc(TuningSetEntry const* a, TuningSetEntry const* b)
{
return a->time - b->time;
}
static RegdPed m_pTuningSetHistoryPed;
static atArray<TuningSetEntry> m_TuningSetHistory;
#endif //__BANK
};
////////////////////////////////////////////////////////////////////////////////////////////
// Callback function for setting up grabbing parameters when creating a clone NM task
////////////////////////////////////////////////////////////////////////////////////////////
typedef void (*fnGetGrabParamsCallback)(aiTask *pTaskRequestingGrab, aiTask *pTaskNM);
////////////////////////////////////////////////////////////////////////////////////////////
// This task is used to control all NM behaviour tasks (used to be the old CTaskNMComplex).
////////////////////////////////////////////////////////////////////////////////////////////
class CTaskNMControl : public CTaskFSMClone
{
friend class CTaskNMBehaviour;
public:
struct Tunables : CTuning
{
Tunables();
struct DriveToGetup
{
bool m_AllowDriveToGetup;
bool m_OnlyAllowForShot;
bool m_AllowWhenBalanced;
float m_MinHealth;
float m_MaxSpeed;
float m_MaxUprightRatio;
u32 m_MatchTimer;
PAR_SIMPLE_PARSABLE;
};
DriveToGetup m_DriveToGetup;
CNmTuningSet m_OnEnableDriveToGetup;
CNmTuningSet m_OnDisableDriveToGetup;
PAR_PARSABLE;
};
enum
{
State_Start = 0,
State_ControllingTask,
State_DecidingOnNextTask,
State_Finish
};
enum eNMControlFlags
{
ALL_FLAGS_CLEAR = 0,
// It may not be desirable to allow a blend back to clip. By clearing the DO_BLEND_FROM_NM flag
// a task can cause the blend to be skipped if, for example, the ped was killed and shouldn't
// get up.
DO_BLEND_FROM_NM = BIT(0),
// This flag is checked before calling the main NM behaviour task's start, control and finish methods.
// If set, we use the clip pose helper class defined below to play an authored clip on a masked subset
// of the ped's skeleton while the main NM task controls the rest of the ragdoll.
DO_CLIP_POSE = BIT(1),
// Use this when the behavior is already running and you don't want to re-run it
ALREADY_RUNNING = BIT(2),
// Tells the control task that it has switched to an underwater behavior already
//IN_WATER_INITD = BIT(3),
// Tells the control task that it is running on the motion task tree, rather than the main one
// can be used to stop any child tasks from forcing motion task tree and hence deleting themselves.
ON_MOTION_TASK_TREE = BIT(4),
// clone task flags :
// Used when the task is running as a clone task, set when the ped is forced into a relax to get him on the ground.
// This happens when the master ped has fallen but the clone is still standing.
DO_RELAX = BIT(5),
DOING_RELAX = BIT(6),
// Only used in clone tasks to force the ped to drop
FORCE_FALL_OVER = BIT(7),
// When true, the ped is in the process of driving to the getup pose
DRIVING_TO_GETUP_POSE = BIT(8),
// set this to disable driving to the getup pose
BLOCK_DRIVE_TO_GETUP = BIT(9),
// ONLY set this when aborting the nm control task for another ragdoll task
ABORTING_FOR_RAGDOLL_EVENT = BIT(10),
// can be set by tasks who want to move on to a high fall task in order to
// block the optional quick getup.
BLOCK_QUICK_GETUP_ON_HIGH_FALL = BIT(11),
// Un-hide the weapon object
// We hide the weapon object for ragdolling player's with
// two-handed weapons equipped when starting NM so this flag lets us know we need
// to un-hide the weapon object
EQUIPPED_WEAPON_OBJECT_VISIBLE = BIT(12),
// ONLY set this when clone task is being aborted and replaced with a local task, or vice versa
CLONE_LOCAL_SWITCH = BIT(13),
NUM_SYNCED_CONTROL_FLAGS = 1, // only sync DO_BLEND_FROM_NM
};
// Definitions of the NM behaviour feedback flags.
enum eNMFeedbackFlags
{
ALL_FEEDBACK_FLAGS_CLEAR = 0,
BALANCE_FAILURE = BIT(0),
BALANCE_STARTED = BIT(1)
};
enum eNMHandPose
{
HAND_POSE_NONE = 0,
HAND_POSE_LOOSE,
HAND_POSE_HOLD_WEAPON,
HAND_POSE_GRAB,
HAND_POSE_BRACED,
HAND_POSE_FLAIL,
HAND_POSE_IMPACT,
NUM_HAND_POSES
};
CTaskNMControl(u32 nMinTime, u32 nMaxTime, aiTask* pForceFirstSubTask, u32 nNMControlFlags, float fDamageTaken=0.0f);
~CTaskNMControl();
#if !__NO_OUTPUT
// PURPOSE: Display debug information specific to this task
virtual void Debug() const;
#endif //!__NO_OUTPUT
virtual aiTask* Copy() const;
// Accessors for the NM control flags.
public:
u32 GetFlags() const {return m_nFlags;}
void SetFlag(u32 nFlagToSet) {m_nFlags |= nFlagToSet;}
void ClearFlag(u32 nFlagToClear) { m_nFlags &= ~nFlagToClear;}
void SetDontSwitchToAnimatedOnAbort(bool b)
{
if (GetSubTask() && GetSubTask()->IsNMBehaviourTask())
{
if (b)
{
smart_cast<CTaskNMBehaviour*>(GetSubTask())->SetFlag(CTaskNMBehaviour::DONT_SWITCH_TO_ANIMATED_ON_ABORT);
}
else
{
smart_cast<CTaskNMBehaviour*>(GetSubTask())->ClearFlag(CTaskNMBehaviour::DONT_SWITCH_TO_ANIMATED_ON_ABORT);
}
}
}
// Determine whether or not the task should abort.
virtual bool ShouldAbort( const AbortPriority iPriority, const aiEvent* pEvent);
virtual bool MakeAbortable( const AbortPriority iPriority, const aiEvent* pEvent);
void CleanUp();
// used by the dead task to determine if it's safe to
bool IsDoingRelax()
{
if (GetSubTask())
{
if (GetSubTask()->GetTaskType()==CTaskTypes::TASK_NM_RELAX)
{
return true;
}
else if (GetSubTask()->GetTaskType()==CTaskTypes::TASK_RAGE_RAGDOLL)
{
return true;
}
}
return false;
}
virtual bool HandlesRagdoll(const CPed* pPed) const
{
if (m_ForceNextSubTask.GetTask())
return true;
if (GetSubTask())
return GetSubTask()->HandlesRagdoll(pPed);
else
return true;
}
#if !__FINAL
virtual atString GetName() const;
#endif //!__FINAL
void SendStartMessages(CPed* pPed)
{
if (m_ForceNextSubTask.GetTask() && ((CTask*)m_ForceNextSubTask.GetTask())->IsNMBehaviourTask())
{
CTaskNMBehaviour* pBehaviourTask = static_cast<CTaskNMBehaviour*>(m_ForceNextSubTask.GetTask());
pBehaviourTask->SendStartMessages(pPed);
}
}
// Accessors for the NM behaviour feedback flags (which are read-only -- can be set and cleared by the behaviour tasks
// because they are "friend" classes).
public:
u32 GetFeedbackFlags() {return m_nFeedbackFlags;}
bool IsFeedbackFlagSet(u32 nQueryFlags) const { return (m_nFeedbackFlags & nQueryFlags) != 0; }
private:
void SetFeedbackFlags(CPed* pPed, u32 nFeedbackFlagsToSet);
void ClearFeedbackFlag(u32 nFlagToClear) {m_nFeedbackFlags &= ~nFlagToClear;}
public:
CClipPoseHelper& GetClipPoseHelper() {return m_clipPoseHelper;}
int GetTaskTypeInternal() const {return CTaskTypes::TASK_NM_CONTROL;}
void SwitchClonePedToRagdoll(CPed* pPed);
////////////////////////////
// CTask functions:
protected:
FSM_Return ProcessPreFSM();
FSM_Return ProcessPostFSM();
FSM_Return UpdateFSM(const s32 iState, const FSM_Event iEvent);
s32 GetDefaultStateAfterAbort() const {return State_Finish;}
virtual bool MayDeleteOnAbort() const {return true;} // This task doesn't resume if it gets aborted, should be safe to delete.
#if !__FINAL
friend class CTaskClassInfoManager;
static const char *GetStaticStateName(s32 iState)
{
Assert(iState>=State_Start&&iState<=State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_ControllingTask",
"State_DecidingOnNextTask",
"State_Finish"
};
return aStateNames[iState];
}
#endif // !__FINAL
void ProcessWeaponHiding(CPed* pPed);
// Add debug draw objects to the control class so that they persist better across different NM behaviour tasks.
#if DEBUG_DRAW
public:
void AddDebugLine(Vec3V_In vStart, Vec3V_In vEnd, const Color32& colour, u32 uExpiryTime = 0, u32 uKey = 0)
{
ms_debugDraw.AddLine(vStart, vEnd, colour, uExpiryTime, uKey);
}
void AddDebugSphere(Vec3V_In vPos, float fRadius, const Color32& colour, u32 uExpiryTime = 0, u32 uKey = 0)
{
ms_debugDraw.AddSphere(vPos, fRadius, colour, uExpiryTime, uKey);
}
#endif //DEBUG_DRAW
protected:
// Clone task implementation
// When cloned, this task continually syncs to the remote state
virtual bool OverridesNetworkBlender(CPed *pPed);
virtual bool OverridesNetworkHeadingBlender(CPed* pPed);
virtual bool OverridesVehicleState() const { return true; }
virtual CTaskInfo* CreateQueriableState() const;
virtual void ReadQueriableState(CClonedFSMTaskInfo* pTaskInfo);
virtual void OnCloneTaskNoLongerRunningOnOwner();
virtual FSM_Return UpdateClonedFSM (const s32 iState, const FSM_Event iEvent);
virtual bool IsInScope(const CPed* pPed);
bool HandleLocalToRemoteSwitch(CPed* pPed, CClonedFSMTaskInfo* pTaskInfo);
bool CanRetainLocalTaskOnFailedSwitch() { return true; }
virtual bool RequiresTaskInfoToMigrate() const { return false; }
virtual bool IgnoresCloneTaskPriorities() const { return true; }
void HandleDriveToGetup(CPed* pPed);
void ClearDriveToGetup(CPed* pPed);
public:
CTaskFSMClone* CreateTaskForClonePed(CPed *pPed);
CTaskFSMClone* CreateTaskForLocalPed(CPed *pPed);
void WarpCloneRagdollingPed(CPed *pPed, const Vector3 &newPosition);
public:
// this is just used by the task counter - uses const pointer
const aiTask* GetForcedSubTask() const {return m_ForceNextSubTask.GetTask();}
void SetDamageDone(float fDamage) {if(fDamage > m_fDamageTaken) m_fDamageTaken = fDamage;}
static CTask* FindBackgroundAiTask(CPed* pPed);
static void CleanupUnhandledRagdoll(CPed* pPed);
static bool IsValidNMControlSubTask(const CTask* pSubTask);
void SetGetGrabParametersCallback(fnGetGrabParamsCallback pGetGrabParamsCallback, aiTask *pTask);
void ClearGetGrabParametersCallback();
void AlwaysAllowControlPassing() { m_alwaysAllowControlPassing = true; }
void ResetStartTime();
static float GetCNCRagdollDurationModifier();
private:
//////////////////////////////////////////////
// Helper functions for FSM state management:
void Start_OnEnter(CPed* pPed);
FSM_Return Start_OnUpdate(CPed* pPed);
FSM_Return Start_OnUpdateClone(CPed* pPed);
void ControllingTask_OnEnter(CPed* pPed);
FSM_Return ControllingTask_OnUpdate(CPed* pPed);
void ControllingTask_OnExit(CPed* pPed);
FSM_Return ControllingTask_OnUpdateClone(CPed* pPed);
void DecidingOnNextTask_OnEnter(CPed* pPed);
void DecidingOnNextTask_OnEnterClone(CPed* pPed);
FSM_Return DecidingOnNextTask_OnUpdate(CPed* pPed);
FSM_Return DecidingOnNextTask_OnUpdateClone(CPed* pPed);
void Finish_OnEnterClone(CPed* pPed);
aiTask* CreateNewNMTask(const int iSubTaskType, CPed *pPed);
aiTask* CreateNewNMTaskClone(CPed *pPed);
public:
void ForceNewSubTask(aiTask* pNewSubTask);
// PURPOSE: Sets the requested animation hand pose on the provided hand
// PARAMS:
// pose - the pose to apply
// pedHand - the hand to apply the pose to
void SetCurrentHandPose(eNMHandPose pose, CMovePed::ePedHand pedHand, float blendDuration=NORMAL_BLEND_DURATION);
eNMHandPose GetCurrentHandPose(CMovePed::ePedHand pedHand) { return m_CurrentHandPoses[pedHand]; }
private:
// This helper class is used to start an "clipPose" behaviour along with the main NM behaviour task.
CClipPoseHelper m_clipPoseHelper;
u32 m_nMinTime;
u32 m_nMaxTime;
u32 m_nStartTime;
u32 m_nFlags;
u32 m_nFeedbackFlags;
eNMHandPose m_CurrentHandPoses[CMovePed::kNumHands];
u32 m_nDriveToGetupMatchTimer;
eNmBlendOutSet m_DriveToGetupMatchedBlendOutSet;
CNmBlendOutPoseItem* m_pDriveToGetupMatchedBlendOutPoseItem;
CAITarget m_DriveToGetupTarget;
RegdTask m_pDriveToGetupMoveTask;
// callback and parameter for setting up grab parameters for a clone NM task launched by another AI task
RegdaiTask m_pTask;
fnGetGrabParamsCallback m_pGetGrabParamsCallback;
public:
#if DEBUG_DRAW
static bool m_bDisplayFlags;
#endif //DEBUG_DRAW
static u32 m_DebugFlags; // For debugging only.
static bool ms_bTeeterEnabled;
static Tunables sm_Tunables;
private:
class CNextSubTask
{
public:
CNextSubTask(aiTask* pNextSubTask) : m_pNextSubTask(pNextSubTask) {}
~CNextSubTask() { if(m_pNextSubTask) delete m_pNextSubTask; }
void SetTask(aiTask* pNextSubTask) { if(m_pNextSubTask) delete m_pNextSubTask; m_pNextSubTask = pNextSubTask; }
const aiTask* GetTask() const { return m_pNextSubTask; }
aiTask* GetTask() { return m_pNextSubTask; }
aiTask* RelinquishTask() { aiTask* pTask = m_pNextSubTask; m_pNextSubTask = NULL; return pTask; }
private:
RegdaiTask m_pNextSubTask;
};
CNextSubTask m_ForceNextSubTask;
float m_fDamageTaken;
// used by clone task:
s32 m_currentTask;
s32 m_nextTask;
bool m_waitForNextTask;
bool m_bHasAborted;
bool m_bGrabbed2Handed;
bool m_bCloneTaskFinished;
u16 m_randomSeed;
bool m_alwaysAllowControlPassing;
};
//
// Task info for CTaskNMControl
//
class CClonedNMControlInfo : public CSerialisedFSMTaskInfo
{
public:
CClonedNMControlInfo(s32 nmTaskType, u32 nControlFlags, bool bHasFallen, u16 randomSeed);
CClonedNMControlInfo();
~CClonedNMControlInfo() {}
virtual s32 GetTaskInfoType( ) const {return INFO_TYPE_NM_CONTROL;}
s32 GetNMTaskType() const { return m_nNMTaskType == -1 ? m_nNMTaskType : m_nNMTaskType+CTaskTypes::TASK_NM_RELAX; }
bool GetHasFallen() const { return m_bHasFallen; }
u16 GetRandomSeed() const { return m_randomSeed; }
// an NM control task running a generic attach is not networked as this will be handled by the animated attach task
virtual bool IsNetworked() const { return m_nNMTaskType+CTaskTypes::TASK_NM_RELAX != CTaskTypes::TASK_NM_GENERIC_ATTACH; }
virtual CTaskFSMClone *CreateCloneFSMTask();
void Serialise(CSyncDataBase& serialiser)
{
CSerialisedFSMTaskInfo::Serialise(serialiser);
u32 controlFlags = (u32)m_nControlFlags;
s32 nmTaskType = (s32)m_nNMTaskType;
SERIALISE_UNSIGNED(serialiser, m_randomSeed, SIZEOF_RANDOM_SEED, "Random Seed");
SERIALISE_INTEGER(serialiser, nmTaskType, SIZEOF_TASK_TYPE, "Task type");
SERIALISE_UNSIGNED(serialiser, controlFlags, SIZEOF_CONTROL_FLAGS, "Control flags");
SERIALISE_BOOL(serialiser, m_bHasFallen, "Has fallen");
m_nControlFlags = (u8)controlFlags;
m_nNMTaskType = (s8)nmTaskType;
}
private:
CClonedNMControlInfo(const CClonedNMControlInfo &);
CClonedNMControlInfo &operator=(const CClonedNMControlInfo &);
static const unsigned int SIZEOF_RANDOM_SEED = 16;
static const unsigned int NUM_NM_TASKS = CTaskTypes::TASK_RAGDOLL_LAST-CTaskTypes::TASK_NM_RELAX;
static const unsigned int SIZEOF_TASK_TYPE = datBitsNeeded<NUM_NM_TASKS>::COUNT + 1;
static const unsigned int SIZEOF_CONTROL_FLAGS = CTaskNMControl::NUM_SYNCED_CONTROL_FLAGS;
u16 m_randomSeed;
s8 m_nNMTaskType;
u8 m_nControlFlags;
bool m_bHasFallen;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline CTaskNMControl* CTaskNMBehaviour::GetControlTask()
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
taskAssert(GetParent());
taskAssertf(dynamic_cast<CTaskNMControl*>(GetParent()), "Parent task should be an NM Control task");
return smart_cast<CTaskNMControl*>(GetParent());
}
#endif // !INC_TASKNM_H_