Files
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

700 lines
20 KiB
C++

// Filename : TaskNMShot.h
// Description: Natural Motion shot class (FSM version)
#ifndef INC_TASKNMSHOT_H_
#define INC_TASKNMSHOT_H_
// --- Include Files ------------------------------------------------------------
// Game headers
#include "Task/System/Task.h"
#include "Task\System\TaskTypes.h"
#include "Task/Physics/TaskNM.h"
#include "Task/Physics/TaskNMBrace.h"
#include "Task/Physics/ThreatenedHelper.h"
// ------------------------------------------------------------------------------
//
// Task info for CTaskNMShot
//
class CClonedNMShotInfo : public CSerialisedFSMTaskInfo
{
public:
CClonedNMShotInfo( CEntity* pShotBy, u32 nWeaponHash, int nComponent, const Vector3& vecHitPositionWorldSpace, const Vector3& vecHitImpulseWorldSpace,
const Vector3& vecHitPolyNormalWorldSpace);
CClonedNMShotInfo();
~CClonedNMShotInfo() {}
virtual s32 GetTaskInfoType( ) const {return INFO_TYPE_NM_SHOT;}
// the clone task is created by the CTaskNMControl clone task
virtual bool AutoCreateCloneTask(CPed* UNUSED_PARAM(pPed)) { return false; }
virtual CTaskFSMClone *CreateCloneFSMTask();
void Serialise(CSyncDataBase& serialiser)
{
CSerialisedFSMTaskInfo::Serialise(serialiser);
SERIALISE_BOOL(serialiser, m_bHasShotByEntity, "Has shot by entity");
if (m_bHasShotByEntity || serialiser.GetIsMaximumSizeSerialiser())
{
SERIALISE_OBJECTID(serialiser, m_shotByEntity, "Shot by entity");
}
SERIALISE_UNSIGNED(serialiser, m_nWeaponHash, SIZEOF_WEAPONHASH, "Weapon hash");
u32 component = (u32)m_nComponent;
SERIALISE_UNSIGNED(serialiser, component, SIZEOF_COMPONENT, "Component");
m_nComponent = (u8)component;
SERIALISE_VECTOR(serialiser, m_vecOriginalHitImpulseWorldSpace, 600.0f, SIZEOF_HITFORCE, "Hit world impulse");
SERIALISE_VECTOR(serialiser, m_vecOriginalHitPositionLocalSpace, LOCAL_POS_MAG_LIMIT, SIZEOF_HITPOS, "Hit local pos");
SERIALISE_VECTOR(serialiser, m_vecOriginalHitPolyNormalWorldSpace, 1.1f, SIZEOF_HITPOLYNORMAL, "Hit world poly normal");
}
u32 GetWeaponHash() const { return m_nWeaponHash; }
u8 GetComponent() const { return m_nComponent; }
const Vector3& GetHitImpulse() const { return m_vecOriginalHitImpulseWorldSpace; }
private:
CClonedNMShotInfo(const CClonedNMShotInfo &);
CClonedNMShotInfo &operator=(const CClonedNMShotInfo &);
static const unsigned int SIZEOF_WEAPONHASH = 32;
static const unsigned int SIZEOF_COMPONENT = 8;
static const unsigned int SIZEOF_HITPOS = 16;
static const unsigned int SIZEOF_HITPOLYNORMAL = 16;
static const unsigned int SIZEOF_HITFORCE = 20;
Vector3 m_vecOriginalHitPositionLocalSpace;
Vector3 m_vecOriginalHitPolyNormalWorldSpace;
Vector3 m_vecOriginalHitImpulseWorldSpace;
ObjectId m_shotByEntity;
u32 m_nWeaponHash;
u8 m_nComponent;
bool m_bHasShotByEntity;
};
//// be hit
// triggers: getting shot
// taking a melee hit
class CTaskNMShot : public CTaskNMBehaviour
{
public:
////////////////////////////////
// SHOT
////////////////////////////////
enum
{
SHOT_DEFAULT,
SHOT_NORMAL,
SHOT_SHOTGUN,
SHOT_UNDERWATER,
SHOT_DANGLING,
SHOT_AGAINST_WALL,
};
struct Tunables : CTuning
{
Tunables();
s32 m_MinimumShotReactionTimePlayerMS;
s32 m_MinimumShotReactionTimeAIMS;
bool m_bUseClipPoseHelper;
bool m_bEnableDebugDraw;
float m_fImpactConeAngleFront; // ... in degrees!
float m_fImpactConeAngleBack; // ... in degrees!
RagdollComponent m_eImpactConeRagdollComponent;
int m_iShotMinTimeBeforeGunThreaten;
int m_iShotMaxTimeBeforeGunThreaten;
int m_iShotMinTimeBetweenFireGun;
int m_iShotMaxTimeBetweenFireGun;
int m_iShotMaxBlindFireTimeL;
int m_iShotMaxBlindFireTimeH;
float m_fShotBlindFireProbability;
float m_fShotHeadAngleToFireGun;
float m_fShotWeaponAngleToFireGun;
float m_fFireWeaponStrengthForceMultiplier;
float m_fMinFinisherShotgunTotalImpulseNormal;
float m_fMinFinisherShotgunTotalImpulseBraced;
float m_fFinisherShotgunBonusArmedSpeedModifier;
bool m_ScaleSnapWithSpineOrientation;
float m_MinSnap; // minimum snap amount
int m_BlendOutDelayStanding;
int m_BlendOutDelayBalanceFailed;
struct ShotAgainstWall
{
ShotAgainstWall()
{}
float m_HealthRatioLimit; // shot against wall will only be used on peds whose health ratio is below this range
float m_WallProbeRadius; // Radius of the swept sphere probe
float m_WallProbeDistance; // Distance of the swept sphere probe
float m_ProbeHeightAbovePelvis; // height above the pelvis to perform the probe
float m_ImpulseMult;
float m_MaxWallAngle; // The maximum allowed angle between shot direction and wall normal
PAR_SIMPLE_PARSABLE;
};
ShotAgainstWall m_ShotAgainstWall;
float m_BCRExclusionZone;
struct Impulses
{
// Sniper-specific impulse tuning requested by Jason Bone. Is being used to tune hits from sniper rifles to stop peds from flipping over from hits
// to the upper body (specifically the clavicles, neck and upper spine). Should really be using the existing weapon force tuning functionality to set
// lower forces for the bones that are causing issues but this ended up being the preferred solution...
struct SniperImpulses
{
SniperImpulses() {}
float m_MaxHealthImpulseMult;
float m_MinHealthImpulseMult;
float m_MaxDamageTakenImpulseMult;
float m_MinDamageTakenImpulseMult;
float m_MaxDamageTakenThreshold;
float m_MinDamageTakenThreshold;
float m_DefaultKillShotImpulseMult;
float m_DefaultMPKillShotImpulseMult;
PAR_SIMPLE_PARSABLE;
};
Impulses()
{}
float m_MaxArmourImpulseMult;
float m_MinArmourImpulseMult;
float m_MaxHealthImpulseMult;
float m_MinHealthImpulseMult;
float m_MaxDamageTakenImpulseMult;
float m_MinDamageTakenImpulseMult;
float m_MaxDamageTakenThreshold;
float m_MinDamageTakenThreshold;
float m_DefaultKillShotImpulseMult;
float m_DefaultRapidFireKillShotImpulseMult;
float m_DefaultMPKillShotImpulseMult;
float m_DefaultMPRapidFireKillShotImpulseMult;
float m_RapidFireBoostShotImpulseMult;
int m_RapidFireBoostShotMinRandom;
int m_RapidFireBoostShotMaxRandom;
float m_ShotgunMaxSpeedForLiftImpulse;
float m_ShotgunMaxLiftImpulse;
float m_ShotgunLiftNearThreshold;
float m_ShotgunChanceToMoveSpine3ImpulseToSpine2;
float m_ShotgunChanceToMoveNeckImpulseToSpine2;
float m_ShotgunChanceToMoveHeadImpulseToSpine2;
float m_EqualizeAmount;
float m_COMImpulseScale;
u32 m_COMImpulseComponent;
float m_COMImpulseMaxRootVelocityMagnitude;
bool m_COMImpulseOnlyWhileBalancing;
float m_HeadShotImpulseMultiplier;
float m_HeadShotMPImpulseMultiplier;
bool m_ScaleHeadShotImpulseWithSpineOrientation;
float m_MinHeadShotImpulseMultiplier;
float m_AutomaticInitialSnapMult;
float m_BurstFireInitialSnapMult;
float m_FinalShotImpulseClampMax;
float m_RunningAgainstBulletImpulseMult;
float m_RunningAgainstBulletImpulseMultMax;
float m_RunningWithBulletImpulseMult;
float m_LegShotFallRootImpulseMinUpright;
float m_LegShotFallRootImpulseMult;
SniperImpulses m_SniperImpulses;
PAR_SIMPLE_PARSABLE;
};
Impulses m_Impulses;
struct HitPointRandomisation
{
bool m_Enable;
float m_TopSpread;
float m_BottomSpread;
float m_Blend;
RagdollComponent m_TopComponent;
RagdollComponent m_BottomComponent;
float m_BiasSide;
u32 m_BiasSideTime;
bool ShouldRandomiseHitPoint(RagdollComponent component) const
{
switch(component)
{
case RAGDOLL_SPINE0:
case RAGDOLL_SPINE1:
case RAGDOLL_SPINE2:
case RAGDOLL_SPINE3:
return true;
default:
return false;
}
}
bool GetRandomHitPoint(Vector3& vRandomHitPoint, const CPed* pPed, RagdollComponent eComponent, bool bBiasSideLeft) const;
PAR_SIMPLE_PARSABLE;
};
HitPointRandomisation m_HitRandomisation;
HitPointRandomisation m_HitRandomisationAutomatic;
struct ArmShot
{
ArmShot()
{}
int m_MinLookAtArmWoundTime;
int m_MaxLookAtArmWoundTime;
float m_UpperArmImpulseCap;
float m_LowerArmImpulseCap;
float m_UpperArmNoTorsoHitImpulseCap;
float m_LowerArmNoTorseHitImpulseCap;
float m_ClavicleImpulseScale;
PAR_SIMPLE_PARSABLE;
};
struct StayUpright
{
StayUpright()
{}
float m_HoldingWeaponBonus;
float m_UnarmedBonus;
float m_ArmouredBonus;
float m_MovingMultiplierBonus;
float m_HealthMultiplierBonus;
PAR_SIMPLE_PARSABLE;
};
StayUpright m_StayUpright;
ArmShot m_ArmShot;
float m_FallingSpeedForHighFall;
float m_ChanceOfFallToKneesOnCollapse;
float m_ChanceOfFallToKneesAfterLastStand;
float m_ChanceForGutShotKnockdown;
float m_LastStandMaxTotalTime;
float m_LastStandMaxArmouredTotalTime;
s32 m_RapidHitCount;
s32 m_ArmouredRapidHitCount;
bool m_AllowArmouredLegShot;
bool m_AllowArmouredKnockdown;
bool m_ReduceDownedTimeByPerformanceTime;
s32 m_MinimumDownedTime;
bool m_DisableReachForWoundOnHeadShot;
s32 m_DisableReachForWoundOnHeadShotMinDelay;
s32 m_DisableReachForWoundOnHeadShotMaxDelay;
bool m_DisableReachForWoundOnNeckShot;
s32 m_DisableReachForWoundOnNeckShotMinDelay;
s32 m_DisableReachForWoundOnNeckShotMaxDelay;
CNmTuningSetGroup m_WeaponSets;
struct ParamSets
{
// always sent
CNmTuningSet m_Base;
// weapon type
CNmTuningSet m_Melee;
CNmTuningSet m_Electrocute;
// hit location
CNmTuningSet m_SprintingLegShot;
CNmTuningSet m_SprintingDeath;
CNmTuningSet m_Sprinting;
CNmTuningSet m_AutomaticHeadShot;
CNmTuningSet m_HeadShot;
CNmTuningSet m_AutomaticNeckShot;
CNmTuningSet m_NeckShot;
CNmTuningSet m_LegShot;
CNmTuningSet m_SniperLegShot;
CNmTuningSet m_ArmShot;
CNmTuningSet m_BackShot;
// target state
CNmTuningSet m_Underwater;
CNmTuningSet m_UnderwaterRelax;
CNmTuningSet m_Armoured;
CNmTuningSet m_BoundAnkles;
CNmTuningSet m_FatallyInjured;
CNmTuningSet m_PlayerDeathSP;
CNmTuningSet m_PlayerDeathMP;
CNmTuningSet m_OnStairs;
// special
CNmTuningSet m_ShotAgainstWall;
CNmTuningSet m_LastStand;
CNmTuningSet m_LastStandArmoured;
CNmTuningSet m_HeadLook;
CNmTuningSet m_FallToKnees;
CNmTuningSet m_StaggerFall;
CNmTuningSet m_CatchFall;
CNmTuningSet m_SetFallingReactionHealthy;
CNmTuningSet m_SetFallingReactionInjured;
CNmTuningSet m_SetFallingReactionFallOverWall;
CNmTuningSet m_SetFallingReactionFallOverVehicle;
CNmTuningSet m_RubberBulletKnockdown;
CNmTuningSet m_Teeter;
CNmTuningSet m_HoldingTwoHandedWeapon;
CNmTuningSet m_HoldingSingleHandedWeapon;
CNmTuningSet m_CrouchedOrLowCover;
CNmTuningSet m_Female;
PAR_SIMPLE_PARSABLE;
};
ParamSets m_ParamSets;
CTaskNMBehaviour::Tunables::StandardBlendOutThresholds m_BlendOutThreshold;
CTaskNMBehaviour::Tunables::BlendOutThreshold m_SubmergedBlendOutThreshold;
PAR_PARSABLE;
};
CTaskNMShot(CPed* pPed, u32 nMinTime, u32 nMaxTime, CEntity* pShotBy, u32 nWeaponHash, int nComponent, const Vector3& vecHitPosWorldSpace,
const Vector3& vecHitImpulseWorldSpace, const Vector3& vecHitPolyNormalWorldSpace, s32 m_hitCountThisPerformance = 0, bool bShotgunMessageSent = false,
bool bHeldWeapon = false, bool hitPosInLocalSpace = false, u32 lastStandStartTimeMS = 0, u32 lastConfigureBulletsCall = 0,
bool lastStandActive = false, bool knockDownStarted = false);
~CTaskNMShot();
protected:
CTaskNMShot(const CTaskNMShot& otherTask);
public:
bool HasBalanceFailed() const { return m_bBalanceFailed; }
////////////////////////
// CTaskSimple functions
#if !__FINAL
virtual atString GetName() const {return m_strTaskAndStateName;}
#endif
#if __BANK
const char* GetShotTypeName() const;
#endif //__BANK
virtual int GetTaskTypeInternal() const {return CTaskTypes::TASK_NM_SHOT;}
virtual void CleanUp();
virtual aiTask* Copy() const { return rage_new CTaskNMShot(*this); }
static void AppendWeaponSets(CNmTuningSet* newItem, atHashString key)
{
sm_Tunables.m_WeaponSets.Append(newItem,key);
}
static void RevertWeaponSets(atHashString key)
{
sm_Tunables.m_WeaponSets.Revert(key);
}
////////////////////////
// CTaskFSMClone functions
virtual CTaskInfo* CreateQueriableState() const;
///////////////////////////
// CTaskNMBehaviour functions
public:
virtual void BehaviourFailure(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourSuccess(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourFinish(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
virtual void BehaviourEvent(CPed* pPed, ARTFeedbackInterfaceGta* pFeedbackInterface);
protected:
virtual void StartBehaviour(CPed* pPed);
virtual void ControlBehaviour(CPed* pPed);
virtual bool FinishConditions(CPed* pPed);
void CreateAnimatedFallbackTask(CPed* pPed, bool bFalling);
virtual void StartAnimatedFallback(CPed* pPed);
virtual bool ControlAnimatedFallback(CPed* pPed);
//////////////////////////
// Local functions and data
public:
void UpdateShot(CPed* pThisPed, CEntity* pShotBy, u32 nWeaponHash, int nComponent, const Vector3& vecHitPos, const Vector3& vecHitImpulse,
const Vector3& vecHitPolyNormal);
u32 GetWeaponHash() const { return m_nWeaponHash; }
int GetComponent() const { return m_nComponent; }
CEntity* GetShotBy() const { return m_pShotBy; }
const Vector3 & GetWorldImpulse() const { return m_vecOriginalHitImpulseWorldSpace; }
void GetWorldHitPosition(CPed *pPed, Vector3 & worldHitPos) const;
bool IsSprinting() const { return m_bSprinting; }
virtual bool ShouldContinueAfterDeath() const {return true;}
virtual bool ShouldAbortForWeaponDamage(CEntity* pFiringEntity, const CWeaponInfo* pWeaponInfo, const f32 UNUSED_PARAM(fWeaponDamage), const fwFlags32& UNUSED_PARAM(flags),
const bool UNUSED_PARAM(bWasKilledOrInjured), const Vector3& UNUSED_PARAM(vStart), WorldProbe::CShapeTestHitPoint* pResult,
const Vector3& vRagdollImpulseDir, const f32 fRagdollImpulseMag)
{
// Fire and explosion damage override shot reactions
if(pWeaponInfo->GetDamageType() == DAMAGE_TYPE_FIRE || pWeaponInfo->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE)
{
return true;
}
// Otherwise, if there's a shape test result, continue this shot reaction
else if ( pResult )
{
UpdateShot(GetPed(), pFiringEntity, pWeaponInfo->GetHash(), pResult->GetHitComponent(), pResult->GetHitPosition(), fRagdollImpulseMag * vRagdollImpulseDir, pResult->GetHitNormal());
return false;
}
// This is some unknown damage source we need to abort for.
else
{
return false;
}
}
virtual bool HandleRagdollImpact(float fMag, const CEntity* pEntity, const Vector3& vPedNormal, int nComponent, phMaterialMgr::Id nMaterialId);
protected:
void HandleNormalShotReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleShotgunReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleUnderwaterReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleBoundAnklesReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleMeleeReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleRunningReaction(CPed* pPed, bool bStart, CNmMessageList& list);
void GetLocalNormal(CPed* pPed, Vector3 &localHitNormal);
void CheckState(CPed* pPed);
void SetShotMessage(CPed* pPed, bool bStartBehaviour, CNmMessageList& list);
void ImpulseOverride(CPed* pPed);
void GatherSituationalData(CPed* pPed);
bool CheckForObjectInPath(CPed* pPed, Vector3 &objectBehindVictimPos, Vector3 &objectBehindVictimNormal);
void CalculateHealth(CPed* pPed);
void CheckInputNormalAndHitPosition();
void HandleConfigureBalance(CPed* pPed, CNmMessageList& list);
void HandleConfigureBullets(CPed* pPed, bool bStart = true);
void HandlePointGun(CPed* pPed, bool bStart, CNmMessageList& list);
void HandleApplyBulletImpulse(CPed* pPed, bool bSendNewBulletMessage = true, bool bStart = true, bool doKillShot = false, bool wallInImpulsePath = false);
void HandleNewBullet(CPed* pPed, Vector3 &posLocal, const Vector3 &normalLocal, const Vector3 &impulseWorld);
void HandleHeadLook(CPed* pPed, CNmMessageList& list);
void HandleSnap(CPed* pPed, CNmMessageList& list);
// Clamps the local hit position to the closest surface point on the bound
void ClampHitToSurfaceOfBound(CPed* pPed, Vector3 &localPos, int component);
void StartAnimatedReachForWound();
bool ShouldReachForWound(bool& withLeft);
bool IsArmoured(CPed* pPed)
{
return pPed && (pPed->GetArmour()>0.0f);
}
float GetArmouredRatio(CPed* pPed)
{
float armour = 0.0f;
if (pPed)
{
armour = pPed->GetArmour() + pPed->GetArmourLost();
if (armour>0.0f)
{
armour = pPed->GetArmour() / armour;
}
armour = ClampRange(armour, 0.0f, 1.0f);
}
return armour;
}
float GetHealthRatio(CPed* pPed)
{
float healthRatio = 0.0f;
if (pPed)
{
healthRatio = pPed->GetMaxHealth() - pPed->GetInjuredHealthThreshold();
if (healthRatio>0.0f)
{
healthRatio = 1.0f - (pPed->GetHealthLost() / healthRatio);
}
healthRatio = ClampRange(healthRatio, 0.0f, 1.0f);
}
return healthRatio;
}
bool IsCloneDying(const CPed* pPed) const;
bool ShouldBlindFireWeapon(CPed* pPed);
bool ShouldBlindFireWeaponNetworkCheck(CPed* pPed, Vector3 const& vecStart, Vector3 const& vecEnd);
bool IsAutomatic(const atHashString& shotTuningSet) const
{
if (shotTuningSet == ATSTRINGHASH("Automatic", 0x74D3AC3B) ||
shotTuningSet == ATSTRINGHASH("AutomaticMG", 0xBD26D537) ||
shotTuningSet == ATSTRINGHASH("AutomaticSMG", 0x7183C85F) ||
shotTuningSet == ATSTRINGHASH("Mini", 0x1289C7C8) ||
shotTuningSet == ATSTRINGHASH("BurstFire", 0xC48A0A41))
{
return true;
}
return false;
}
private:
enum eStates // for internal state machine.
{
State_STANDING,
State_STAGGERING,
State_FALLING,
State_ON_GROUND,
State_PARTIALLY_SUBMERGED, // In water
State_SUBMERGED, // In water
State_TEETERING
};
eStates m_eState;
u32 m_eType;
private:
CThreatenedHelper m_threatenedHelper;
WorldProbe::CShapeTestResults m_probeResult;
#if !__FINAL
// For debugging the internal state machine.
atString m_strTaskAndStateName;
#endif // !__FINAL
Vec3V m_vecOriginalHitPositionBoneSpace; // store the local space position of the wound for later use in animated reach for wound.
Vector3 m_vecOriginalHitPositionLocalSpace;
Vector3 m_vecOriginalHitPolyNormalWorldSpace;
Vector3 m_vecOriginalHitImpulseWorldSpace;
Vector3 m_vEdgeLeft, m_vEdgeRight;
bool m_FirstTeeterProbeDetectedDrop;
RegdEnt m_pShotBy;
int m_nComponent;
u32 m_nWeaponHash;
u32 m_LastConfigureBulletsCall;
u32 m_BiasSideTime;
float m_healthRatio;
u32 m_nTimeToStopBlindFire;
u32 m_nTimeTillBlindFireNextShot;
u32 m_LastStandStartTimeMS;
u32 m_DisableReachForWoundTimeMS;
u16 m_nLookAtArmWoundTime;
u16 m_CountdownToCatchfall;
s16 m_hitCountThisPerformance;
u8 m_ThrownOutImpulseCount;
u8 m_ShotsPerBurst;
bool m_bUsingBalance : 1;
bool m_bBalanceFailed : 1;
bool m_bUseHeadLook : 1;
bool m_bUsePointGun : 1;
bool m_StaggerFallStarted : 1;
bool m_bSprinting : 1;
bool m_bFront : 1;
bool m_bUpright : 1;
bool m_RapidFiring : 1;
bool m_RapidFiringBoostFrame : 1;
bool m_ElectricDamage : 1;
bool m_SettingShotMessageForShotgun : 1;
bool m_ShotgunMessageSent : 1;
bool m_HeldWeapon : 1;
bool m_bMinTimeExtended : 1;
bool m_stepInInSupportApplied : 1;
bool m_bLastStandActive : 1;
bool m_bKnockDownStarted : 1;
bool m_bKillShotUsed : 1;
bool m_bFallingToKnees : 1;
bool m_ReachingWithLeft : 1;
bool m_ReachingWithRight : 1;
bool m_DoLegShotFall : 1;
bool m_FallingOverWall : 1;
bool m_bTeeterInitd : 1;
bool m_BiasSide : 1;
bool m_WasCrouchedOrInLowCover : 1;
#if __DEV
bool m_selected : 1;
#endif
// tunable by widgets
public:
void ChooseParameterSet();
static bool IsPoseAppropriateForFiring(CPed* pFiringPed, CAITarget& target, CWeapon* pWeapon, CObject* pWeaponObject);
#if !__FINAL
#if DEBUG_DRAW
void DebugVisualise (const CPed* pPed) const; // Put here so that it can be called while the game is paused.
#endif // DEBUG_DRAW
virtual void Debug() const
{
#if DEBUG_DRAW
// Visualise the hit point and impact vector for debugging.
if(sm_Tunables.m_bEnableDebugDraw)
{
DebugVisualise(GetPed());
}
#endif // DEBUG_DRAW
CTask::Debug();
}
#endif // !__FINAL
static Tunables sm_Tunables;
};
#endif // ! INC_TASKNMSHOT_H_