Files
GTASource/game/weapons/Bullet.h

371 lines
12 KiB
C
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
#ifndef BULLET_H
#define BULLET_H
// Rage headers
#include "physics/intersection.h"
// Game headers
#include "physics/WorldProbe/worldprobe.h"
#include "Scene/RegdRefTypes.h"
#include "Weapons/Info/WeaponInfo.h"
// Forward declarations
namespace rage { class phPolygon; }
////////////////////////////////////////////////////////////////////////////////
class CBullet
{
public:
FW_REGISTER_CLASS_POOL(CBullet);
friend class CBulletManager;
// Statics
static const s32 MAX_STORAGE = 60;
static const s32 MAX_BULLETS_IN_BATCH = 16;
static const s32 MAX_BULLETS_COLLISION_PAIRS = 10;
#if __BANK
static bool ms_CollideWithMoverCollision;
#endif // __BANK
CBullet();
CBullet(CEntity* pFiringEntity, const CWeaponInfo* pWeaponInfo, const Vector3& vStart, const Vector3& vEnd, const f32 fVelocity, const f32 fLifeTime, const f32 fDamage, u32 uWeaponHash, bool bCreatesTrace, bool bAllowDamageToVehicle, bool bAllowDamageToVehicleOccupants, const Vector3& vMuzzlePosition, const Vector3& vMuzzleDirection, bool bIsAccurate, bool bPerformPenetration, bool bPerformBulletProofGlassPenetration, float fRecoilAccuracyWhenFired, bool bSilenced, float fFallOffRangeModifer, float fFallOffDamageModifer, bool bIsBentBulletFromAimAssist, CWeapon* pWeaponOwner = NULL);
~CBullet();
// Cloud tunables
static void InitTunables();
// PURPOSE: Initialise class
static void Init();
// PURPOSE: Shutdown class
static void Shutdown();
// PURPOSE: Simulates the bullet
// RETURNS: true - carry on processing, false - delete from manager
bool Process();
// PURPOSE: Add an extra probe to this bullet - simulates shotguns
bool Add(const Vector3& vStart, const Vector3& vEnd, const f32 fVelocity, u32 uWeaponHash, bool bCreatesTrace, bool bIsAccurate);
// PURPOSE: Return if there is a bullet fired in an angled area
bool ComputeIsBulletInAngledArea(Vector3& vPos1, Vector3& vPos2, float areaWidth, const CEntity* pFiringEntity);
// PURPOSE: Return if there is a bullet fired in the specified sphere
bool ComputeIsBulletInSphere(const Vector3& vPosition, f32 fRadius, const CEntity* pFiringEntity = NULL) const;
// PURPOSE: Return if there is a bullet fired in the specified box
bool ComputeIsBulletInBox(const Vector3& vMin, const Vector3& vMax, const CEntity* pFiringEntity = NULL) const;
#if __BANK
// PURPOSE: Debug
void RenderDebug() const;
#endif // __BANK
private:
//
// Internal structures
//
// PURPOSE: Individual bullet data
struct sBulletInstance
{
FW_REGISTER_CLASS_POOL(sBulletInstance);
// Statics
static const s32 MAX_STORAGE = 128;
sBulletInstance(const CWeaponInfo* pWeaponInfo, const Vector3& vStart, const Vector3& vEnd, const f32 fVelocity, u32 uWeaponHash, bool bCreatesTrace, bool bIsAccurate);
// PURPOSE: Current bullet position
Vector3 vPosition;
// PURPOSE: Last bullet position
Vector3 vOldPosition;
// PURPOSE: Bullet velocity
Vector3 vVelocity;
// PURPOSE: Current penetration
f32 fPenetration;
// PURPOSE: The hash of the weapon that created the bullet
u32 uWeaponHash;
// PURPOSE: Should be removed
bool bToBeRemoved : 1;
// PURPOSE: Should this bullet create trace vfx
bool bCreatesTrace : 1;
// PURPOSE: Allow head shots/engine fire
bool bIsAccurate : 1;
#if __BANK
// PURPOSE: Used to keep track of bullets
u32 uGenerationId;
// PURPOSE: Used to allocate new generation id
static u32 s_uNewGenerationId;
#endif // __BANK
};
// PURPOSE: Individual bullet impacts
struct sBulletImpacts
{
// Statics
static const s32 MAX_IMPACTS = 8;
sBulletImpacts() : entryImpacts(entryImpactResults, MAX_IMPACTS), exitImpacts(exitImpactResults, MAX_IMPACTS) {}
typedef WorldProbe::CShapeTestResults Impacts;
WorldProbe::CShapeTestHitPoint entryImpactResults[MAX_IMPACTS];
WorldProbe::CShapeTestHitPoint exitImpactResults[MAX_IMPACTS];
Impacts entryImpacts;
Impacts exitImpacts;
};
// PURPOSE: Batch impacts
struct sBatchImpacts
{
sBatchImpacts() : batch(MAX_BULLETS_IN_BATCH) {}
typedef atFixedArray<sBulletImpacts, MAX_BULLETS_IN_BATCH> BatchImpacts;
BatchImpacts batch;
};
struct BulletCollisionPair
{
BulletCollisionPair()
:m_pEntity(NULL),
m_pEntry(NULL),
m_pExit(NULL),
m_bAesthetic(false)
{
// Nothing for now
}
CEntity* m_pEntity;
WorldProbe::CShapeTestHitPoint* m_pEntry;
WorldProbe::CShapeTestHitPoint* m_pExit;
bool m_bAesthetic;
};
//
// Internal functions
//
// PURPOSE: Update the position based on velocity
void UpdatePositions();
// PURPOSE: Detect if we've hit anything along our path
bool ComputeImpacts(sBatchImpacts& impacts) const;
// PURPOSE: Detect if we've hit a cloth
void ComputeClothImpacts(const Vector3& vOldPosition, const Vector3& vPosition) const;
// PURPOSE: Detect if we've hit a rope
void ComputeRopeImpacts(const Vector3& vOldPosition, const Vector3& vPosition) const;
// PURPOSE: Analyzes the impact to determine if the hit is aesthetic
bool IsImpactAesthetic( CEntity* pImpactEntity, const WorldProbe::CShapeTestHitPoint* pImpactHitPoint );
// PURPOSE: Analyzes the impact to determine if the hit is a headshot
bool IsImpactHeadShot( CEntity* pImpactEntity, const WorldProbe::CShapeTestHitPoint* pImpactHitPoint );
// PURPOSE: Detect if we've hit vehicle tyres - as they are not picked up by the physics ray cast, sort the intersection list etc.
void PreprocessImpacts(sBulletImpacts::Impacts& impacts, WorldProbe::CShapeTestResults::SortFunctionCB sortFn) const;
// PURPOSE: Apply damage to detected impacts
void ProcessImpacts(sBatchImpacts& impacts);
// PURPOSE: Process any vfx on each bullet
void ProcessVfx();
// PURPOSE: Detect if we have hit ocean water
bool ProcessHitsOceanWater(u32 index);
// PURPOSE: Detect if we have hit an air defence sphere.
bool ProcessHitsAirDefenceZone(u32 index);
// PURPOSE: Determine if a bullet passes through
f32 ComputePassesThrough(const WorldProbe::CShapeTestHitPoint* entryHitPoint, const WorldProbe::CShapeTestHitPoint* exitHitPoint, f32& fInOutPenetration) const;
// PURPOSE: Send whizzed by events to the ped event group.
void GenerateWhizzedByEvents() const;
void GenerateWhizzedByEventAtEndPosition(const Vector3& vPosition) const;
// PURPOSE: Sorting the intersections by t-value
static s32 CompareEntryIntersections(const WorldProbe::CShapeTestHitPoint* a, const WorldProbe::CShapeTestHitPoint* b);
// PURPOSE: Sorting the intersections by t-value
static s32 CompareExitIntersections(const WorldProbe::CShapeTestHitPoint* a, const WorldProbe::CShapeTestHitPoint* b);
//
// Members
//
// PURPOSE: Origin of bullet - used by damage calculations
Vector3 m_vOriginPosition;
// PURPOSE: Position of weapon muzzle position - used for vfx
Vector3 m_vMuzzlePosition;
// PURPOSE: Direction of weapon muzzle - used to decide if vfx is allowed
Vector3 m_vMuzzleDirection;
// PURPOSE: Firer of the bullet
RegdEnt m_pFiringEntity;
// PURPOSE: Weapon type
RegdConstWeaponInfo m_pWeaponInfo;
// PURPOSE: Life time of bullet
f32 m_fLifeTime;
// PURPOSE: Damage of bullet
f32 m_fDamage;
// PURPOSE: Bullet instances associated with this bullet
typedef atFixedArray<sBulletInstance*, MAX_BULLETS_IN_BATCH> Batch;
Batch m_Batch;
// PURPOSE: Bullet entry and exit points
typedef atFixedArray<BulletCollisionPair, MAX_BULLETS_COLLISION_PAIRS> BulletCollisionPairs;
BulletCollisionPairs m_BulletCollisionPairs;
// PURPOSE: Store the original recoil accuracy
float m_fRecoilAccuracyWhenFired;
//
float m_fFallOffRangeModifier;
float m_fFallOffDamageModifier;
// PURPOSE: Should check vehicle tyres for impacts
bool m_bCheckVehicleTyres : 1;
// PURPOSE: Allow bullets to damage vehicle ped is in
bool m_bAllowDamageToVehicle : 1;
// PURPOSE: Allow bullets to damage to vehicle occupants of peds vehicle
bool m_bAllowDamageToVehicleOccupants : 1;
// PURPOSE: Whether or not we perform shoot through detection
bool m_bPerformPenetration : 1;
// PURPOSE: Whether or not we perform shoot through detection for bullet proof glass.
bool m_bPerformBulletProofGlassPenetration : 1;
//
bool m_bSilenced : 1;
bool m_bCheckForAirDefenceZone : 1;
bool m_bIsBentBulletFromAimAssist : 1;
// Cloud tunables
static bool sm_bSpecialAmmoFMJVehicleGlassAlwaysPenetrate;
#if __DEV
// PURPOSE: Source weapon
RegdWeapon m_WeaponOwner;
#endif // __DEV
#if !__FINAL
static u32 m_uRejectionReason;
#endif //__FINAL
};
////////////////////////////////////////////////////////////////////////////////
class CBulletManager
{
public:
// Temp: Batch tests are broken so only do this for multiple shot weapons
static bool ms_bUseBatchTestsForSingleShotWeapons;
static bool ms_bUseUndirectedBatchTests;
// PURPOSE: Init
static void Init();
// PURPOSE: Shutdown
static void Shutdown();
// PURPOSE: Add
static bool Add(CBullet* pBullet);
// PURPOSE: Update
static void Process();
// PURPOSE: Record bullet impact for script querying
static void AddBulletImpact(WorldProbe::CShapeTestHitPoint& impact, CEntity* pFiringEntity, bool bIsEntryImpact);
// PURPOSE: Reset bullets
static void ResetBullets();
// PURPOSE: Reset recorded bullet impacts
static void ResetBulletImpacts();
// PURPOSE: Return if there is a bullet fired in the angled area
static bool ComputeIsBulletInAngledArea(Vector3& vPos1, Vector3& vPos2, float areaWidth, const CEntity* pFiringEntity);
// PURPOSE: Return if there is a bullet fired in the specified sphere
static bool ComputeIsBulletInSphere(const Vector3& vPosition, f32 fRadius, const CEntity* pFiringEntity = NULL);
// PURPOSE: Return if there is a bullet fired in the specified box
static bool ComputeIsBulletInBox(const Vector3& vMin, const Vector3& vMax, const CEntity* pFiringEntity = NULL);
// PURPOSE: Return if there is a bullet impact in the specified sphere
static bool ComputeHasBulletImpactedInSphere(const Vector3& vPosition, f32 fRadius, const CEntity* pFiringEntity = NULL, bool bEntryOnly = true);
// PURPOSE: Return if there is a bullet impact in the specified box
static bool ComputeHasBulletImpactedInBox(const Vector3& vMin, const Vector3& vMax, const CEntity* pFiringEntity = NULL, bool bEntryOnly = true);
// PURPOSE: Return if there is a bullet impact behind the specified plane
static bool ComputeHasBulletImpactedBehindPlane( const Vector3& vPlanePoint, const Vector3& vPlaneNormal, bool bEntryOnly = true );
// PURPOSE: Return the number of bullet impacts in the specified sphere
static s32 ComputeNumBulletsImpactedInSphere(const Vector3& vPosition, f32 fRadius, const CEntity* pFiringEntity = NULL, bool bEntryOnly = true);
// PURPOSE: Return the number of bullet impacts in the specified box
static s32 ComputeNumBulletsImpactedInBox(const Vector3& vMin, const Vector3& vMax, const CEntity* pFiringEntity = NULL, bool bEntryOnly = true);
#if __BANK
// PURPOSE: Debug
static void RenderDebug();
#endif // __BANK
private:
// PURPOSE: Bullet storage
typedef atFixedArray<CBullet*, CBullet::MAX_STORAGE> Bullets;
static Bullets ms_Bullets;
static const s32 MAX_IMPACTS_PER_FRAME = 256;
typedef atFixedArray<Vector3, MAX_IMPACTS_PER_FRAME> BulletImpacts;
typedef atFixedArray<CEntity*, MAX_IMPACTS_PER_FRAME> BulletEntities;
static BulletImpacts ms_EntryImpacts, ms_ExitImpacts;
static BulletEntities ms_EntryEntities, ms_ExitEntities;
};
////////////////////////////////////////////////////////////////////////////////
inline s32 CBullet::CompareEntryIntersections(const WorldProbe::CShapeTestHitPoint* a, const WorldProbe::CShapeTestHitPoint* b)
{
return a->GetHitTValue() < b->GetHitTValue() ? -1 : 1;
}
////////////////////////////////////////////////////////////////////////////////
inline s32 CBullet::CompareExitIntersections(const WorldProbe::CShapeTestHitPoint* a, const WorldProbe::CShapeTestHitPoint* b)
{
return a->GetHitTValue() > b->GetHitTValue() ? -1 : 1;
}
////////////////////////////////////////////////////////////////////////////////
#endif // BULLET_H