summaryrefslogtreecommitdiff
path: root/src/weapons
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
committerclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
commit9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 (patch)
treea84756b82513739a2672db3a1f0ec579db6d18ff /src/weapons
downloadre3-miami.tar.gz
re3-miami.zip
Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation)HEADmiami
Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami.
Diffstat (limited to 'src/weapons')
-rw-r--r--src/weapons/BulletInfo.cpp344
-rw-r--r--src/weapons/BulletInfo.h25
-rw-r--r--src/weapons/Explosion.cpp506
-rw-r--r--src/weapons/Explosion.h54
-rw-r--r--src/weapons/ProjectileInfo.cpp437
-rw-r--r--src/weapons/ProjectileInfo.h34
-rw-r--r--src/weapons/ShotInfo.cpp149
-rw-r--r--src/weapons/ShotInfo.h24
-rw-r--r--src/weapons/Weapon.cpp3300
-rw-r--r--src/weapons/Weapon.h90
-rw-r--r--src/weapons/WeaponEffects.cpp133
-rw-r--r--src/weapons/WeaponEffects.h26
-rw-r--r--src/weapons/WeaponInfo.cpp295
-rw-r--r--src/weapons/WeaponInfo.h74
-rw-r--r--src/weapons/WeaponType.h86
15 files changed, 5577 insertions, 0 deletions
diff --git a/src/weapons/BulletInfo.cpp b/src/weapons/BulletInfo.cpp
new file mode 100644
index 0000000..b49db74
--- /dev/null
+++ b/src/weapons/BulletInfo.cpp
@@ -0,0 +1,344 @@
+#include "common.h"
+
+#include "BulletInfo.h"
+
+#include "AnimBlendAssociation.h"
+#include "DMAudio.h"
+#include "AudioScriptObject.h"
+#ifdef FIX_BUGS
+#include "Collision.h"
+#endif
+#include "RpAnimBlend.h"
+#include "Entity.h"
+#include "EventList.h"
+#include "Fire.h"
+#include "Glass.h"
+#include "Particle.h"
+#include "Ped.h"
+#include "Object.h"
+#include "Stats.h"
+#include "Timer.h"
+#include "Vehicle.h"
+#include "Weapon.h"
+#include "WeaponInfo.h"
+#include "World.h"
+#include "SurfaceTable.h"
+#include "Heli.h"
+
+#ifdef SQUEEZE_PERFORMANCE
+uint32 bulletInfoInUse;
+#endif
+
+#define BULLET_LIFETIME (1000)
+#define NUM_PED_BLOOD_PARTICLES (8)
+#define BLOOD_PARTICLE_OFFSET (CVector(0.0f, 0.0f, 0.0f))
+#define NUM_VEHICLE_SPARKS (16)
+#define NUM_TYRE_POP_SMOKES (4)
+#define NUM_OTHER_SPARKS (8)
+#define BULLET_HIT_FORCE (7.5f)
+
+#define BULLET_BOUNDARY_MIN_X -2400.0f
+#define BULLET_BOUNDARY_MAX_X 1600.0f
+#define BULLET_BOUNDARY_MIN_Y -2000.0f
+#define BULLET_BOUNDARY_MAX_Y 2000.0f
+
+CBulletInfo gaBulletInfo[CBulletInfo::NUM_BULLETS];
+bool bPlayerSniperBullet;
+CVector PlayerSniperBulletStart;
+CVector PlayerSniperBulletEnd;
+
+void CBulletInfo::Initialise(void)
+{
+ debug("Initialising CBulletInfo...\n");
+ for (int i = 0; i < NUM_BULLETS; i++) {
+ gaBulletInfo[i].m_bInUse = false;
+ gaBulletInfo[i].m_eWeaponType = WEAPONTYPE_COLT45;
+ gaBulletInfo[i].m_fTimer = 0.0f;
+ gaBulletInfo[i].m_pSource = nil;
+ }
+ debug("CBulletInfo ready\n");
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse = 0;
+#endif
+}
+
+void CBulletInfo::Shutdown(void)
+{
+ debug("Shutting down CBulletInfo...\n");
+ debug("CBulletInfo shut down\n");
+}
+
+bool CBulletInfo::AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed)
+{
+ int i;
+ for (i = 0; i < NUM_BULLETS; i++) {
+ if (!gaBulletInfo[i].m_bInUse)
+ break;
+ }
+ if (i == NUM_BULLETS)
+ return false;
+ gaBulletInfo[i].m_pSource = pSource;
+ gaBulletInfo[i].m_eWeaponType = type;
+ gaBulletInfo[i].m_nDamage = CWeaponInfo::GetWeaponInfo(type)->m_nDamage;
+ gaBulletInfo[i].m_vecPosition = vecPosition;
+ gaBulletInfo[i].m_vecSpeed = vecSpeed;
+ gaBulletInfo[i].m_fTimer = CTimer::GetTimeInMilliseconds() + BULLET_LIFETIME;
+ gaBulletInfo[i].m_bInUse = true;
+
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse++;
+#endif
+ return true;
+}
+
+void CBulletInfo::Update(void)
+{
+#ifdef SQUEEZE_PERFORMANCE
+ if (bulletInfoInUse == 0)
+ return;
+#endif
+ bPlayerSniperBullet = false;
+ for (int i = 0; i < NUM_BULLETS; i++) {
+ CBulletInfo* pBullet = &gaBulletInfo[i];
+ if (pBullet->m_pSource && pBullet->m_pSource->IsPed() && !((CPed*)pBullet->m_pSource)->IsPointerValid())
+ pBullet->m_pSource = nil;
+ if (!pBullet->m_bInUse)
+ continue;
+ if (CTimer::GetTimeInMilliseconds() > pBullet->m_fTimer) {
+ pBullet->m_bInUse = false;
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse--;
+#endif
+ }
+ CVector vecOldPos = pBullet->m_vecPosition;
+ CVector vecNewPos = pBullet->m_vecPosition + pBullet->m_vecSpeed * CTimer::GetTimeStep() * 0.5f;
+
+ if ( vecNewPos.x <= BULLET_BOUNDARY_MIN_X || vecNewPos.x >= BULLET_BOUNDARY_MAX_X || vecNewPos.y <= BULLET_BOUNDARY_MIN_Y || vecNewPos.y >= BULLET_BOUNDARY_MAX_Y ) {
+ pBullet->m_bInUse = false;
+ continue;
+ }
+ CWorld::bIncludeDeadPeds = true;
+ CWorld::bIncludeBikers = true;
+ CWorld::bIncludeCarTyres = true;
+ CWorld::pIgnoreEntity = pBullet->m_pSource;
+ CColPoint point;
+ CEntity* pHitEntity;
+ if (CWorld::ProcessLineOfSight(vecOldPos, vecNewPos, point, pHitEntity, true, true, true, true, true, false, false, true)) {
+
+ CWeapon::CheckForShootingVehicleOccupant(&pHitEntity, &point, pBullet->m_eWeaponType, vecOldPos, vecNewPos);
+ if (pHitEntity->IsPed()) {
+ CPed* pPed = (CPed*)pHitEntity;
+ if (!pPed->DyingOrDead() && pPed != pBullet->m_pSource) {
+ if (pPed->IsPedInControl() && !pPed->bIsDucking) {
+ pPed->ClearAttackByRemovingAnim();
+ CAnimBlendAssociation* pAnim = CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HITBYGUN_FRONT);
+ pAnim->SetBlend(0.0f, 8.0f);
+ }
+ pPed->InflictDamage(pBullet->m_pSource, pBullet->m_eWeaponType, pBullet->m_nDamage, (ePedPieceTypes)point.pieceB, pPed->GetLocalDirection(pPed->GetPosition() - point.point));
+ CEventList::RegisterEvent(pPed->m_nPedType == PEDTYPE_COP ? EVENT_SHOOT_COP : EVENT_SHOOT_PED, EVENT_ENTITY_PED, pPed, (CPed*)pBullet->m_pSource, 1000);
+ pBullet->m_bInUse = false;
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse--;
+#endif
+ vecNewPos = point.point;
+ }
+ if (CGame::nastyGame) {
+ CVector vecParticleDirection = (point.point - pPed->GetPosition()) * 0.01f;
+ vecParticleDirection.z = 0.01f;
+ if (pPed->GetIsOnScreen()) {
+ for (int j = 0; j < NUM_PED_BLOOD_PARTICLES; j++)
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + BLOOD_PARTICLE_OFFSET, vecParticleDirection);
+ }
+ if (pPed->GetPedState() == PED_DEAD) {
+ CAnimBlendAssociation* pAnim;
+ if (RpAnimBlendClumpGetFirstAssociation(pPed->GetClump(), ASSOC_FRONTAL))
+ pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f);
+ else
+ pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f);
+ if (pAnim) {
+ pAnim->SetCurrentTime(0.0f);
+ pAnim->flags |= ASSOC_RUNNING;
+ pAnim->flags &= ~ASSOC_FADEOUTWHENDONE;
+ }
+ }
+ pBullet->m_bInUse = false;
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse--;
+#endif
+ vecNewPos = point.point;
+ }
+ }
+ else if (pHitEntity->IsVehicle()) {
+ CEntity *source = pBullet->m_pSource;
+ if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) {
+ if ( point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR ) {
+ ((CVehicle*)pHitEntity)->BurstTyre(point.pieceB, true);
+ for (int j=0; j<NUM_TYRE_POP_SMOKES; j++) {
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point.point, point.normal / 20);
+ }
+ } else {
+ // CVector sth(0.0f, 0.0f, 0.0f); // unused
+ ((CVehicle*)pHitEntity)->InflictDamage(source, pBullet->m_eWeaponType, pBullet->m_nDamage);
+ if ( pBullet->m_eWeaponType == WEAPONTYPE_FLAMETHROWER ) {
+ gFireManager.StartFire(pHitEntity, pBullet->m_pSource, 0.8f, 1);
+ } else {
+ for (int j=0; j<NUM_VEHICLE_SPARKS; j++) {
+ CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal / 20);
+ }
+ }
+ }
+ }
+#ifdef FIX_BUGS
+ pBullet->m_bInUse = false;
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse--;
+#endif
+ vecNewPos = point.point;
+#endif
+ } else {
+ for (int j = 0; j < NUM_OTHER_SPARKS; j++)
+ CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal / 20);
+ CEntity *source = pBullet->m_pSource;
+ if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) {
+ if (pHitEntity->IsObject()) {
+ CObject *pHitObject = (CObject*)pHitEntity;
+ if ( !pHitObject->bInfiniteMass && pHitObject->m_fCollisionDamageMultiplier < 99.9f) {
+ bool notStatic = !pHitObject->GetIsStatic();
+ if (notStatic && pHitObject->m_fUprootLimit <= 0.0f) {
+ pHitObject->bIsStatic = false;
+ pHitObject->AddToMovingList();
+ }
+
+ notStatic = !pHitObject->GetIsStatic();
+ if (!notStatic) {
+ CVector moveForce = point.normal * -BULLET_HIT_FORCE;
+ pHitObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z);
+ }
+ } else if (pHitObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) {
+ pHitObject->ObjectDamage(50.f);
+ }
+ }
+ }
+#ifdef FIX_BUGS
+ pBullet->m_bInUse = false;
+#ifdef SQUEEZE_PERFORMANCE
+ bulletInfoInUse--;
+#endif
+ vecNewPos = point.point;
+#endif
+ }
+ if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) {
+ cAudioScriptObject* pAudio;
+ switch (pHitEntity->GetType()) {
+ case ENTITY_TYPE_BUILDING:
+ if (!DMAudio.IsAudioInitialised())
+ break;
+
+ pAudio = new cAudioScriptObject();
+ if (pAudio)
+ pAudio->Reset();
+ pAudio->Posn = pHitEntity->GetPosition();
+ pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_1;
+ pAudio->AudioEntity = AEHANDLE_NONE;
+ DMAudio.CreateOneShotScriptObject(pAudio);
+ break;
+ case ENTITY_TYPE_OBJECT:
+ if (!DMAudio.IsAudioInitialised())
+ break;
+
+ pAudio = new cAudioScriptObject();
+ if (pAudio)
+ pAudio->Reset();
+ pAudio->Posn = pHitEntity->GetPosition();
+ pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_2;
+ pAudio->AudioEntity = AEHANDLE_NONE;
+ DMAudio.CreateOneShotScriptObject(pAudio);
+ break;
+ case ENTITY_TYPE_DUMMY:
+ if (!DMAudio.IsAudioInitialised())
+ break;
+
+ pAudio = new cAudioScriptObject();
+ if (pAudio)
+ pAudio->Reset();
+ pAudio->Posn = pHitEntity->GetPosition();
+ pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_3;
+ pAudio->AudioEntity = AEHANDLE_NONE;
+ DMAudio.CreateOneShotScriptObject(pAudio);
+ break;
+ case ENTITY_TYPE_PED:
+ ++CStats::BulletsThatHit;
+ DMAudio.PlayOneShot(((CPed*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f);
+ ((CPed*)pHitEntity)->Say(SOUND_PED_BULLET_HIT);
+ break;
+ case ENTITY_TYPE_VEHICLE:
+ ++CStats::BulletsThatHit;
+ DMAudio.PlayOneShot(((CVehicle*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f);
+ break;
+ default: break;
+ }
+ }
+ CGlass::WasGlassHitByBullet(pHitEntity, point.point);
+ CWeapon::BlowUpExplosiveThings(pHitEntity);
+ }
+ CWorld::pIgnoreEntity = nil;
+ CWorld::bIncludeDeadPeds = false;
+ CWorld::bIncludeCarTyres = false;
+ CWorld::bIncludeBikers = false;
+ if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) {
+ bPlayerSniperBullet = true;
+ PlayerSniperBulletStart = pBullet->m_vecPosition;
+ PlayerSniperBulletEnd = vecNewPos;
+ }
+ pBullet->m_vecPosition = vecNewPos;
+ CHeli::TestSniperCollision(&PlayerSniperBulletStart, &PlayerSniperBulletEnd);
+ }
+}
+
+bool CBulletInfo::TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2)
+{
+ if (!bPlayerSniperBullet)
+ return false;
+#ifdef FIX_BUGS // original code is not going work anyway...
+ CColLine line(PlayerSniperBulletStart, PlayerSniperBulletEnd);
+ CColBox box;
+ box.Set(CVector(x1, y1, z1), CVector(x2, y2, z2), SURFACE_DEFAULT, 0);
+ return CCollision::TestLineBox(line, box);
+#else
+ float minP = 0.0f;
+ float maxP = 1.0f;
+ float minX = Min(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x);
+ float maxX = Max(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x);
+ if (minX < x2 || maxX > x1) {
+ if (minX < x1)
+ minP = Min(minP, (x1 - minX) / (maxX - minX));
+ if (maxX > x2)
+ maxP = Max(maxP, (maxX - x2) / (maxX - minX));
+ }
+ else
+ return false;
+ float minY = Min(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y);
+ float maxY = Max(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y);
+ if (minY < y2 || maxY > y1) {
+ if (minY < y1)
+ minP = Min(minP, (y1 - minY) / (maxY - minY));
+ if (maxY > y2)
+ maxP = Max(maxP, (maxY - y2) / (maxY - minY));
+ }
+#ifdef FIX_BUGS
+ else
+ return false;
+#endif
+ float minZ = Min(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z);
+ float maxZ = Max(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z);
+ if (minZ < z2 || maxZ > z1) {
+ if (minZ < z1)
+ minP = Min(minP, (z1 - minZ) / (maxZ - minZ));
+ if (maxZ > z2)
+ maxP = Max(maxP, (maxZ - z2) / (maxZ - minZ));
+ }
+ else
+ return false;
+ return minP <= maxP;
+#endif
+}
diff --git a/src/weapons/BulletInfo.h b/src/weapons/BulletInfo.h
new file mode 100644
index 0000000..cf1dd27
--- /dev/null
+++ b/src/weapons/BulletInfo.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "WeaponType.h"
+
+class CEntity;
+
+class CBulletInfo
+{
+ eWeaponType m_eWeaponType;
+ CEntity* m_pSource;
+ float m_fTimer; // big mistake
+ bool m_bInUse;
+ CVector m_vecPosition;
+ CVector m_vecSpeed;
+ int16 m_nDamage;
+public:
+ enum {
+ NUM_BULLETS = 100
+ };
+ static void Initialise(void);
+ static void Shutdown(void);
+ static bool AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed);
+ static void Update(void);
+ static bool TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2);
+}; \ No newline at end of file
diff --git a/src/weapons/Explosion.cpp b/src/weapons/Explosion.cpp
new file mode 100644
index 0000000..74137dc
--- /dev/null
+++ b/src/weapons/Explosion.cpp
@@ -0,0 +1,506 @@
+#include "common.h"
+
+#include "Automobile.h"
+#include "Bike.h"
+#include "Camera.h"
+#include "Coronas.h"
+#include "DMAudio.h"
+#include "Entity.h"
+#include "EventList.h"
+#include "Explosion.h"
+#include "General.h"
+#include "Fire.h"
+#include "Pad.h"
+#include "Particle.h"
+#include "PointLights.h"
+#include "Shadows.h"
+#include "Timer.h"
+#include "Vehicle.h"
+#include "WaterLevel.h"
+#include "World.h"
+
+CExplosion gaExplosion[NUM_EXPLOSIONS];
+
+// these two were not initialised in original code, I'm really not sure what were they meant to be
+RwRGBA colMedExpl = { 0, 0, 0, 0 };
+RwRGBA colUpdate = { 0, 0, 0, 0 };
+
+const RwRGBA colAddExplosion = { 160, 160, 160, 255 };
+const RwRGBA colGrenade = { 96, 96, 96, 255 };
+
+int AudioHandle = AEHANDLE_NONE;
+
+void
+CExplosion::Initialise()
+{
+ debug("Initialising CExplosion...\n");
+ ClearAllExplosions();
+ AudioHandle = DMAudio.CreateEntity(AUDIOTYPE_EXPLOSION, (void*)1);
+ if (AudioHandle >= 0)
+ DMAudio.SetEntityStatus(AudioHandle, true);
+ debug("CExplosion ready\n");
+}
+
+void
+CExplosion::ClearAllExplosions()
+{
+ for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) {
+ gaExplosion[i].m_ExplosionType = EXPLOSION_GRENADE;
+ gaExplosion[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f);
+ gaExplosion[i].m_fRadius = 1.0f;
+ gaExplosion[i].m_fPropagationRate = 0.0f;
+ gaExplosion[i].m_fZshift = 0.0f;
+ gaExplosion[i].m_pCreatorEntity = nil;
+ gaExplosion[i].m_pVictimEntity = nil;
+ gaExplosion[i].m_fStopTime = 0.0f;
+ gaExplosion[i].m_nIteration = 0;
+ gaExplosion[i].m_fStartTime = 0.0f;
+ gaExplosion[i].m_bIsBoat = false;
+ gaExplosion[i].m_bMakeSound = true;
+ }
+}
+
+void
+CExplosion::Shutdown()
+{
+ debug("Shutting down CExplosion...\n");
+ if (AudioHandle >= 0) {
+ DMAudio.DestroyEntity(AudioHandle);
+ AudioHandle = AEHANDLE_NONE;
+ }
+ debug("CExplosion shut down\n");
+}
+
+int8
+CExplosion::GetExplosionActiveCounter(uint8 id)
+{
+ return gaExplosion[id].m_nActiveCounter;
+}
+
+void
+CExplosion::ResetExplosionActiveCounter(uint8 id)
+{
+ gaExplosion[id].m_nActiveCounter = 0;
+}
+
+uint8
+CExplosion::GetExplosionType(uint8 id)
+{
+ return gaExplosion[id].m_ExplosionType;
+}
+
+bool
+CExplosion::DoesExplosionMakeSound(uint8 id)
+{
+ return gaExplosion[id].m_bMakeSound;
+};
+
+CVector *
+CExplosion::GetExplosionPosition(uint8 id)
+{
+ return &gaExplosion[id].m_vecPosition;
+}
+
+bool
+CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound)
+{
+ CVector pPosn;
+ CVector posGround;
+
+ RwRGBA colorMedium = colMedExpl;
+ RwRGBA color = colAddExplosion;
+ RwRGBA colorGrenade = colGrenade;
+ bool bDontExplode = false;
+ pPosn = pos;
+ pPosn.z += 5.0f;
+#ifdef FIX_BUGS
+ CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 30000, 1.0f);
+#else
+ // last two arguments are swapped resulting in no shadow
+ CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 1, 30000.0f);
+#endif
+
+ int n = 0;
+#ifdef FIX_BUGS
+ while (n < ARRAY_SIZE(gaExplosion) && gaExplosion[n].m_nIteration != 0)
+#else
+ // array overrun is UB
+ while (gaExplosion[n].m_nIteration != 0 && n < ARRAY_SIZE(gaExplosion))
+#endif
+ n++;
+ if (n == ARRAY_SIZE(gaExplosion))
+ return false;
+
+ CExplosion &explosion = gaExplosion[n];
+ explosion.m_ExplosionType = type;
+ explosion.m_vecPosition = pos;
+ explosion.m_fRadius = 1.0f;
+ explosion.m_fZshift = 0.0f;
+ explosion.m_pCreatorEntity = culprit;
+ if (culprit != nil)
+ culprit->RegisterReference(&explosion.m_pCreatorEntity);
+ explosion.m_pVictimEntity = explodingEntity;
+ if (explodingEntity != nil)
+ explodingEntity->RegisterReference(&explosion.m_pVictimEntity);
+ explosion.m_nIteration = 1;
+ explosion.m_nActiveCounter = 1;
+ explosion.m_bIsBoat = false;
+ explosion.m_bMakeSound = makeSound;
+ explosion.m_nParticlesExpireTime = lifetime != 0 ? CTimer::GetTimeInMilliseconds() + lifetime : 0;
+ switch (type)
+ {
+ case EXPLOSION_GRENADE:
+ explosion.m_fRadius = 9.0f;
+ explosion.m_fPower = 300.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ posGround = pos;
+ posGround.z = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, nil);
+ CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250);
+ if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f) {
+ uint8 tmp = CGeneral::GetRandomNumberInRange(0, 64) - 64;
+ colorGrenade.green += tmp;
+ colorGrenade.blue += tmp;
+ CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 4.5f, colorGrenade);
+ }
+ break;
+ case EXPLOSION_MOLOTOV:
+ {
+ explosion.m_fRadius = 6.0f;
+ explosion.m_fPower = 0.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 3000;
+ explosion.m_fPropagationRate = 0.5f;
+ posGround = pos;
+ bool found;
+ float tmp = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, &found);
+ if (found)
+ posGround.z = tmp;
+
+ float waterLevel;
+ if (CWaterLevel::GetWaterLevelNoWaves(posGround.x, posGround.y, posGround.z, &waterLevel)
+ && posGround.z < waterLevel && waterLevel - 6.0f < posGround.z) { // some subway/tunnels check?
+ bDontExplode = true;
+ } else if (found) {
+ gFireManager.StartFire(posGround, 1.8f, false);
+ }
+ break;
+ }
+ case EXPLOSION_ROCKET:
+ explosion.m_fRadius = 10.0f;
+ explosion.m_fPower = 300.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 250);
+ if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f)
+ CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 5.5f, color);
+ break;
+ case EXPLOSION_CAR:
+ case EXPLOSION_CAR_QUICK:
+ case EXPLOSION_BOAT:
+ explosion.m_fRadius = 9.0f;
+ explosion.m_fPower = 300.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 4250;
+ explosion.m_fPropagationRate = 0.5f;
+ explosion.m_fStartTime = CTimer::GetTimeInMilliseconds();
+ if (explosion.m_pVictimEntity != nil) {
+ if (explosion.m_pVictimEntity->IsVehicle() && ((CVehicle*)explosion.m_pVictimEntity)->IsBoat())
+ explosion.m_bIsBoat = true;
+ CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, explosion.m_pVictimEntity, nil, 1000);
+ } else {
+ CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000);
+ }
+
+ if (explosion.m_pVictimEntity != nil && !explosion.m_bIsBoat) {
+ CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity;
+ CVector componentPos;
+
+ if (veh->IsBike()) {
+ veh->GetComponentWorldPosition(BIKE_FORKS_REAR, componentPos);
+ } else if (veh->IsComponentPresent(CAR_BUMP_REAR) && veh->IsComponentPresent(CAR_WHEEL_LB)) { //mb it's another enum
+ CVector tmpVec;
+ veh->GetComponentWorldPosition(CAR_BUMP_REAR, componentPos);
+ veh->GetComponentWorldPosition(CAR_WHEEL_LB, tmpVec);
+ componentPos += tmpVec;
+ componentPos /= 2.0f;
+ } else if (veh->IsComponentPresent(CAR_BOOT)) {
+ veh->GetComponentWorldPosition(CAR_BOOT, componentPos);
+ }
+ if (componentPos.x != 0.0f) {
+ int rn = (CGeneral::GetRandomNumber() & 1) + 1;
+ for (int i = 0; i < rn; i++)
+ CParticle::AddJetExplosion(componentPos, (CGeneral::GetRandomNumber() & 7) / 7.0f + 1.5f, 0.5f);
+ }
+ }
+ break;
+ case EXPLOSION_HELI:
+ case EXPLOSION_HELI2:
+ if (type == EXPLOSION_HELI2) {
+ explosion.m_fRadius = 12.0f;
+ explosion.m_fPower = 500.0f;
+ } else {
+ explosion.m_fRadius = 6.0f;
+ explosion.m_fPower = 300.0f;
+ }
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ explosion.m_fStartTime = CTimer::GetTimeInMilliseconds();
+ for (int i = 0; i < 10; i++) {
+ CVector randpos;
+
+ randpos.x = CGeneral::GetRandomNumber();
+ randpos.y = CGeneral::GetRandomNumber();
+ randpos.z = CGeneral::GetRandomNumber();
+ randpos -= CVector(128, 128, 128);
+ randpos /= 20.0f;
+ randpos += pos;
+
+ CParticle::AddParticle(PARTICLE_EXPLOSION_MFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 2.5f, color);
+
+ randpos.x = CGeneral::GetRandomNumber();
+ randpos.y = CGeneral::GetRandomNumber();
+ randpos.z = CGeneral::GetRandomNumber();
+ randpos -= CVector(128, 128, 128);
+ randpos /= 20.0f;
+ randpos += pos;
+
+ CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 5.0f, color);
+
+ randpos.x = CGeneral::GetRandomNumber();
+ randpos.y = CGeneral::GetRandomNumber();
+ randpos.z = CGeneral::GetRandomNumber();
+ randpos -= CVector(128, 128, 128);
+ randpos /= 20.0f;
+ randpos += pos;
+
+ CParticle::AddJetExplosion(randpos, 1.4f, 3.0f);
+ }
+ CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000);
+ break;
+ case EXPLOSION_MINE:
+ explosion.m_fRadius = 10.0f;
+ explosion.m_fPower = 150.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ posGround = pos;
+ //posGround.z =
+ CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused
+ CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250);
+ break;
+ case EXPLOSION_BARREL:
+ explosion.m_fRadius = 7.0f;
+ explosion.m_fPower = 150.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ for (int i = 0; i < 6; i++) {
+ CVector randpos;
+ randpos.x = CGeneral::GetRandomNumber();
+ randpos.y = CGeneral::GetRandomNumber();
+ randpos.z = CGeneral::GetRandomNumber();
+ randpos -= CVector(128, 128, 128);
+ randpos.x /= 50.0f;
+ randpos.y /= 50.0f;
+ randpos.z /= 25.0f;
+ randpos += pos;
+ CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colorMedium);
+ }
+ posGround = pos;
+ //posGround.z =
+ CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused
+ CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250);
+ break;
+ case EXPLOSION_TANK_GRENADE:
+ explosion.m_fRadius = 10.0f;
+ explosion.m_fPower = 150.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ posGround = pos;
+ //posGround.z =
+ CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused
+ CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250);
+ break;
+ case EXPLOSION_HELI_BOMB:
+ explosion.m_fRadius = 8.0f;
+ explosion.m_fPower = 50.0f;
+ explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750;
+ explosion.m_fPropagationRate = 0.5f;
+ posGround = pos;
+ //posGround.z =
+ CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused
+ CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250);
+ break;
+ default:
+ debug("Undefined explosion type, AddExplosion, Explosion.cpp");
+ break;
+ }
+
+ if (bDontExplode) {
+ explosion.m_nIteration = 0;
+ return false;
+ }
+
+ if (explosion.m_fPower != 0.0f && explosion.m_nParticlesExpireTime == 0)
+ CWorld::TriggerExplosion(pos, explosion.m_fRadius, explosion.m_fPower, culprit, (type == EXPLOSION_ROCKET || type == EXPLOSION_CAR_QUICK || type == EXPLOSION_MINE || type == EXPLOSION_BARREL || type == EXPLOSION_TANK_GRENADE || type == EXPLOSION_HELI_BOMB));
+
+ if (type == EXPLOSION_MOLOTOV) {
+ TheCamera.CamShake(0.2f, pos.x, pos.y, pos.z);
+ } else {
+ TheCamera.CamShake(0.6f, pos.x, pos.y, pos.z);
+ CPad::GetPad(0)->StartShake_Distance(300, 128, pos.x, pos.y, pos.z);
+ }
+ return true;
+}
+
+void
+CExplosion::Update()
+{
+ RwRGBA color = colUpdate;
+ for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) {
+ CExplosion &explosion = gaExplosion[i];
+ if (explosion.m_nIteration == 0) continue;
+
+ if (explosion.m_nParticlesExpireTime != 0) {
+ if (CTimer::GetTimeInMilliseconds() > explosion.m_nParticlesExpireTime) {
+ explosion.m_nParticlesExpireTime = 0;
+ if (explosion.m_fPower != 0.0f)
+ CWorld::TriggerExplosion(explosion.m_vecPosition, explosion.m_fRadius, explosion.m_fPower, explosion.m_pCreatorEntity, (explosion.m_ExplosionType == EXPLOSION_ROCKET || explosion.m_ExplosionType == EXPLOSION_CAR_QUICK || explosion.m_ExplosionType == EXPLOSION_MINE || explosion.m_ExplosionType == EXPLOSION_BARREL || explosion.m_ExplosionType == EXPLOSION_TANK_GRENADE || explosion.m_ExplosionType == EXPLOSION_HELI_BOMB));
+ }
+ } else {
+ explosion.m_fRadius += explosion.m_fPropagationRate * CTimer::GetTimeStep();
+ int32 someTime = explosion.m_fStopTime - CTimer::GetTimeInMilliseconds();
+ switch (explosion.m_ExplosionType)
+ {
+ case EXPLOSION_GRENADE:
+ case EXPLOSION_ROCKET:
+ case EXPLOSION_HELI:
+ case EXPLOSION_HELI2:
+ case EXPLOSION_MINE:
+ case EXPLOSION_BARREL:
+ if (CTimer::GetFrameCounter() & 1) {
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 20.0f, 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true);
+ CCoronas::RegisterCorona((uintptr)&explosion, 255, 255, 200, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ } else
+ CCoronas::RegisterCorona((uintptr)&explosion, 128, 128, 100, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 30, 25, 255, explosion.m_vecPosition, explosion.m_fRadius, 120.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ break;
+ case EXPLOSION_MOLOTOV:
+ CWorld::SetPedsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity);
+ CWorld::SetCarsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity);
+ if (explosion.m_nIteration < 10) {
+ if (explosion.m_nIteration == 1) {
+ CVector point1 = explosion.m_vecPosition;
+ point1.z += 5.0f;
+ CColPoint colPoint;
+ CEntity *pEntity;
+ if (CWorld::ProcessVerticalLine(point1, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil))
+ explosion.m_fZshift = colPoint.point.z;
+ else
+ explosion.m_fZshift = explosion.m_vecPosition.z;
+ }
+ float ff = ((float)explosion.m_nIteration * 0.55f);
+ for (int i = 0; i < 5 * ff; i++) {
+ float angle = CGeneral::GetRandomNumber() / 256.0f * 6.28f;
+
+ CVector pos = explosion.m_vecPosition;
+ pos.x += ff * Sin(angle);
+ pos.y += ff * Cos(angle);
+ pos.z = explosion.m_fZshift + 0.5f;
+ CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f));
+ }
+ }
+ break;
+ case EXPLOSION_CAR:
+ case EXPLOSION_CAR_QUICK:
+ case EXPLOSION_BOAT:
+ if (someTime >= 3500) {
+ if (explosion.m_pVictimEntity != nil) {
+ if ((CGeneral::GetRandomNumber() & 0xF) == 0 && !explosion.m_bIsBoat) {
+ CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity;
+ uint8 component = CAR_WING_LR;
+
+ // miami leftover
+ if (veh->IsBike())
+ component = BIKE_FORKS_REAR;
+
+ if (veh->IsComponentPresent(component)) {
+ CVector componentPos;
+ veh->GetComponentWorldPosition(component, componentPos);
+ CParticle::AddJetExplosion(componentPos, 0.5f, 0.0f);
+ }
+ }
+ if (CTimer::GetTimeInMilliseconds() > explosion.m_fStartTime) {
+ explosion.m_fStartTime = CTimer::GetTimeInMilliseconds() + 125 + (CGeneral::GetRandomNumber() & 0x7F);
+ CVector pos = explosion.m_pVictimEntity->GetPosition();
+ for (int i = 0; i < (CGeneral::GetRandomNumber() & 1) + 1; i++)
+ CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 3.5f, color);
+ }
+ }
+ if (CTimer::GetFrameCounter() & 1) {
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 15.0f, 1.0f, 0.0f, 0.0f, CPointLights::FOG_NONE, true);
+ CCoronas::RegisterCorona((uintptr)&explosion, 200, 100, 0, 255, explosion.m_vecPosition, 6.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ } else
+ CCoronas::RegisterCorona((uintptr)&explosion, 128, 0, 0, 255, explosion.m_vecPosition, 8.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+
+ CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 15, 0, 255, explosion.m_vecPosition, explosion.m_fRadius, 80.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ } else if (explosion.m_nIteration & 1) {
+ if (explosion.m_pVictimEntity != nil)
+ CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, explosion.m_pVictimEntity->GetPosition(), CVector(0.0f, 0.0f, 0.0f), nil, CGeneral::GetRandomNumberInRange(0.5f, 0.8f), color);
+ CVector pos = explosion.m_vecPosition;
+ pos.z += 1.0f;
+ CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, pos, CVector(0.0f, 0.0f, 0.11f), nil, CGeneral::GetRandomNumberInRange(0.5f, 2.0f), color);
+ }
+ break;
+ case EXPLOSION_TANK_GRENADE:
+ case EXPLOSION_HELI_BOMB:
+ if (explosion.m_nIteration < 5) {
+ float ff = ((float)explosion.m_nIteration * 0.65f);
+ for (int i = 0; i < 10 * ff; i++) {
+ uint8 x = CGeneral::GetRandomNumber(), y = CGeneral::GetRandomNumber(), z = CGeneral::GetRandomNumber();
+ CVector pos(x - 128, y - 128, (z % 128) + 1);
+
+ pos.Normalise();
+ pos *= (explosion.m_nIteration + 1) * ff / 5.0f;
+ pos += explosion.m_vecPosition;
+ pos.z += 0.5f;
+ CParticle::AddParticle(PARTICLE_EXPLOSION_LARGE, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (someTime > 0)
+ explosion.m_nIteration++;
+ else
+ explosion.m_nIteration = 0;
+ }
+ }
+}
+
+bool
+CExplosion::TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2)
+{
+ for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) {
+ if (gaExplosion[i].m_nIteration != 0) {
+ if (type == gaExplosion[i].m_ExplosionType) {
+ if (gaExplosion[i].m_vecPosition.x >= x1 && gaExplosion[i].m_vecPosition.x <= x2) {
+ if (gaExplosion[i].m_vecPosition.y >= y1 && gaExplosion[i].m_vecPosition.y <= y2) {
+ if (gaExplosion[i].m_vecPosition.z >= z1 && gaExplosion[i].m_vecPosition.z <= z2)
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void
+CExplosion::RemoveAllExplosionsInArea(CVector pos, float radius)
+{
+ for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) {
+ if (gaExplosion[i].m_nIteration != 0) {
+ if ((pos - gaExplosion[i].m_vecPosition).MagnitudeSqr() < SQR(radius))
+ gaExplosion[i].m_nIteration = 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/weapons/Explosion.h b/src/weapons/Explosion.h
new file mode 100644
index 0000000..7aa02b6
--- /dev/null
+++ b/src/weapons/Explosion.h
@@ -0,0 +1,54 @@
+#pragma once
+
+class CEntity;
+class CVector;
+
+enum eExplosionType
+{
+ EXPLOSION_GRENADE,
+ EXPLOSION_MOLOTOV,
+ EXPLOSION_ROCKET,
+ EXPLOSION_CAR,
+ EXPLOSION_CAR_QUICK,
+ EXPLOSION_BOAT,
+ EXPLOSION_HELI,
+ EXPLOSION_HELI2,
+ EXPLOSION_MINE,
+ EXPLOSION_BARREL,
+ EXPLOSION_TANK_GRENADE,
+ EXPLOSION_HELI_BOMB
+};
+
+class CExplosion
+{
+ eExplosionType m_ExplosionType;
+ CVector m_vecPosition;
+ float m_fRadius;
+ float m_fPropagationRate;
+ CEntity *m_pCreatorEntity;
+ CEntity *m_pVictimEntity;
+ float m_fStopTime;
+ uint8 m_nIteration;
+ uint8 m_nActiveCounter;
+ bool m_bIsBoat;
+ bool m_bMakeSound;
+ float m_fStartTime;
+ uint32 m_nParticlesExpireTime;
+ float m_fPower;
+ float m_fZshift;
+public:
+ static bool AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound = true); //done(new parametr in android ver is fix for one mission)
+ static void ClearAllExplosions(); //done
+ static bool DoesExplosionMakeSound(uint8 id); //done
+ static int8 GetExplosionActiveCounter(uint8 id); //done
+ static CVector *GetExplosionPosition(uint8 id); //done
+ static uint8 GetExplosionType(uint8 id); //done, mb need change type to tExplosionType
+ static void Initialise(); //done
+ static void RemoveAllExplosionsInArea(CVector pos, float radius); //done
+ static void ResetExplosionActiveCounter(uint8 id); //done
+ static void Shutdown(); //done
+ static void Update(); //done
+ static bool TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2); //done, not used
+};
+
+extern CExplosion gaExplosion[NUM_EXPLOSIONS]; \ No newline at end of file
diff --git a/src/weapons/ProjectileInfo.cpp b/src/weapons/ProjectileInfo.cpp
new file mode 100644
index 0000000..10aa3ef
--- /dev/null
+++ b/src/weapons/ProjectileInfo.cpp
@@ -0,0 +1,437 @@
+#include "common.h"
+
+#include "Camera.h"
+#include "General.h"
+#include "Heli.h"
+#include "ModelIndices.h"
+#include "Particle.h"
+#include "Ped.h"
+#include "Plane.h"
+#include "ProjectileInfo.h"
+#include "Projectile.h"
+#include "Explosion.h"
+#include "Weapon.h"
+#include "World.h"
+
+#ifdef SQUEEZE_PERFORMANCE
+uint32 projectileInUse;
+#endif
+
+CProjectileInfo gaProjectileInfo[NUM_PROJECTILES];
+CProjectile *CProjectileInfo::ms_apProjectile[NUM_PROJECTILES];
+
+#define PROJECTILE_BOUNDARY_MIN_X -2390.0f
+#define PROJECTILE_BOUNDARY_MAX_X 1590.0f
+#define PROJECTILE_BOUNDARY_MIN_Y -1990.0f
+#define PROJECTILE_BOUNDARY_MAX_Y 1990.0f
+
+void
+CProjectileInfo::Initialise()
+{
+ debug("Initialising CProjectileInfo...\n");
+
+ for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) {
+ ms_apProjectile[i] = nil;
+ gaProjectileInfo[i].m_eWeaponType = WEAPONTYPE_GRENADE;
+ gaProjectileInfo[i].m_pSource = nil;
+ gaProjectileInfo[i].m_nExplosionTime = 0;
+ gaProjectileInfo[i].m_bInUse = false;
+ }
+
+ debug("CProjectileInfo ready\n");
+
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse = 0;
+#endif
+}
+
+void
+CProjectileInfo::Shutdown()
+{
+ debug("Shutting down CProjectileInfo...\n");
+ debug("CProjectileInfo shut down\n");
+}
+
+CProjectileInfo*
+CProjectileInfo::GetProjectileInfo(int32 id)
+{
+ return &gaProjectileInfo[id];
+}
+
+bool
+CProjectileInfo::AddProjectile(CEntity *entity, eWeaponType weapon, CVector pos, float speed)
+{
+ int8 SpecialCollisionResponseCase = COLLRESPONSE_NONE;
+ bool gravity = true;
+ CMatrix matrix;
+ float elasticity = 0.75f;
+ CPed* ped = (CPed*)entity;
+ int time;
+ CVector velocity;
+
+ switch (weapon)
+ {
+ case WEAPONTYPE_ROCKET:
+ {
+ float vy = 0.35f;
+ time = CTimer::GetTimeInMilliseconds() + 2000;
+ if (entity->GetModelIndex() == MI_SPARROW || entity->GetModelIndex() == MI_HUNTER || entity->GetModelIndex() == MI_SENTINEL) {
+ matrix = ped->GetMatrix();
+ matrix.GetPosition() = pos;
+ CVector vecSpeed = ((CPhysical*)entity)->m_vecMoveSpeed;
+ vy += Max(0.0f, DotProduct(vecSpeed, entity->GetForward())) + Max(0.0f, DotProduct(vecSpeed, entity->GetUp()));
+ } else {
+ if (ped->IsPlayer()) {
+ matrix.GetForward() = TheCamera.Cams[TheCamera.ActiveCam].Front;
+ matrix.GetUp() = TheCamera.Cams[TheCamera.ActiveCam].Up;
+ matrix.GetRight() = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Up, TheCamera.Cams[TheCamera.ActiveCam].Front);
+ matrix.GetPosition() = pos;
+ } else if (ped->m_pSeekTarget != nil) {
+ float ry = CGeneral::GetRadianAngleBetweenPoints(1.0f, ped->m_pSeekTarget->GetPosition().z, 1.0f, pos.z);
+ float rz = Atan2(-ped->GetForward().x, ped->GetForward().y);
+ vy = 0.35f * speed + 0.15f;
+ matrix.SetTranslate(0.0f, 1.0f, 1.0f);
+ matrix.Rotate(0.0f, ry, rz);
+ matrix.GetPosition() += pos;
+ } else {
+ matrix = ped->GetMatrix();
+ }
+ }
+ velocity = Multiply3x3(matrix, CVector(0.0f, vy, 0.0f));
+ gravity = false;
+ break;
+ }
+ case WEAPONTYPE_MOLOTOV:
+ {
+ time = CTimer::GetTimeInMilliseconds() + 2000;
+ float scale = 0.22f * speed + 0.15f;
+ if (scale < 0.2f)
+ scale = 0.2f;
+ float angle = Atan2(-ped->GetForward().x, ped->GetForward().y);
+ matrix.SetTranslate(0.0f, 0.0f, 0.0f);
+ matrix.RotateZ(angle);
+ matrix.GetPosition() += pos;
+ velocity.x = -1.0f * scale * Sin(angle);
+ velocity.y = scale * Cos(angle);
+ velocity.z = (0.2f * speed + 0.4f) * scale;
+ break;
+ }
+ case WEAPONTYPE_TEARGAS:
+ {
+ time = CTimer::GetTimeInMilliseconds() + 20000;
+ float scale = 0.0f;
+ if (speed != 0.0f)
+ scale = 0.22f * speed + 0.15f;
+ float angle = Atan2(-ped->GetForward().x, ped->GetForward().y);
+ matrix.SetTranslate(0.0f, 0.0f, 0.0f);
+ matrix.RotateZ(angle);
+ matrix.GetPosition() += pos;
+ SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5;
+ velocity.x = -1.0f * scale * Sin(angle);
+ velocity.y = scale * Cos(angle);
+ velocity.z = (0.4f * speed + 0.4f) * scale;
+ elasticity = 0.5f;
+ break;
+ }
+ case WEAPONTYPE_GRENADE:
+ case WEAPONTYPE_DETONATOR_GRENADE:
+ {
+ time = CTimer::GetTimeInMilliseconds() + 2000;
+ float scale = 0.0f;
+ if (speed != 0.0f)
+ scale = 0.22f * speed + 0.15f;
+ float angle = Atan2(-ped->GetForward().x, ped->GetForward().y);
+ matrix.SetTranslate(0.0f, 0.0f, 0.0f);
+ matrix.RotateZ(angle);
+ matrix.GetPosition() += pos;
+ SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5;
+ velocity.x = -1.0f * scale * Sin(angle);
+ velocity.y = scale * Cos(angle);
+ velocity.z = (0.4f * speed + 0.4f) * scale;
+ elasticity = 0.5f;
+ break;
+ }
+ default:
+ Error("Undefined projectile type, AddProjectile, ProjectileInfo.cpp");
+ break;
+ }
+
+ int i = 0;
+#ifdef FIX_BUGS
+ while (i < ARRAY_SIZE(gaProjectileInfo) && gaProjectileInfo[i].m_bInUse) i++;
+#else
+ // array overrun is UB
+ while (gaProjectileInfo[i].m_bInUse && i < ARRAY_SIZE(gaProjectileInfo)) i++;
+#endif
+ if (i == ARRAY_SIZE(gaProjectileInfo))
+ return false;
+
+ switch (weapon)
+ {
+ case WEAPONTYPE_ROCKET:
+ ms_apProjectile[i] = new CProjectile(MI_MISSILE);
+ break;
+ case WEAPONTYPE_TEARGAS:
+ ms_apProjectile[i] = new CProjectile(MI_TEARGAS);
+ break;
+ case WEAPONTYPE_MOLOTOV:
+ ms_apProjectile[i] = new CProjectile(MI_MOLOTOV);
+ break;
+ case WEAPONTYPE_GRENADE:
+ case WEAPONTYPE_DETONATOR_GRENADE:
+ ms_apProjectile[i] = new CProjectile(MI_GRENADE);
+ break;
+ default: break;
+ }
+
+ if (ms_apProjectile[i] == nil)
+ return false;
+
+ gaProjectileInfo[i].m_eWeaponType = weapon;
+ gaProjectileInfo[i].m_pSource = ped;
+ ms_apProjectile[i]->GetMatrix() = matrix;
+ ms_apProjectile[i]->SetMoveSpeed(velocity);
+ ms_apProjectile[i]->bAffectedByGravity = gravity;
+
+ gaProjectileInfo[i].m_nExplosionTime = time;
+ ms_apProjectile[i]->m_fElasticity = elasticity;
+ ms_apProjectile[i]->m_nSpecialCollisionResponseCases = SpecialCollisionResponseCase;
+
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse++;
+#endif
+
+ gaProjectileInfo[i].m_bInUse = true;
+ CWorld::Add(ms_apProjectile[i]);
+
+ gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition();
+
+ if (entity && entity->IsPed() && !ped->m_pCollidingEntity) {
+ ped->m_pCollidingEntity = ms_apProjectile[i];
+ }
+ return true;
+}
+
+void
+CProjectileInfo::RemoveProjectile(CProjectileInfo *info, CProjectile *projectile)
+{
+ // TODO(Miami): New parameter: 1
+ switch (info->m_eWeaponType) {
+ case WEAPONTYPE_GRENADE:
+ CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_GRENADE, projectile->GetPosition(), 0);
+ break;
+ case WEAPONTYPE_MOLOTOV:
+ CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_MOLOTOV, projectile->GetPosition(), 0);
+ break;
+ case WEAPONTYPE_ROCKET:
+ CExplosion::AddExplosion(nil, info->m_pSource->IsVehicle() ? ((CVehicle*)info->m_pSource)->pDriver : info->m_pSource, EXPLOSION_ROCKET, projectile->GetPosition(), 0);
+ break;
+ }
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse--;
+#endif
+
+ info->m_bInUse = false;
+ CWorld::Remove(projectile);
+ delete projectile;
+}
+
+void
+CProjectileInfo::RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos)
+{
+ // TODO(Miami): New parameter: 1
+ switch (weaponType) {
+ case WEAPONTYPE_GRENADE:
+ CExplosion::AddExplosion(nil, entity, EXPLOSION_GRENADE, pos, 0);
+ break;
+ case WEAPONTYPE_MOLOTOV:
+ CExplosion::AddExplosion(nil, entity, EXPLOSION_MOLOTOV, pos, 0);
+ break;
+ case WEAPONTYPE_ROCKET:
+ CExplosion::AddExplosion(nil, entity, EXPLOSION_ROCKET, pos, 0);
+ break;
+ }
+}
+
+void
+CProjectileInfo::Update()
+{
+#ifdef SQUEEZE_PERFORMANCE
+ if (projectileInUse == 0)
+ return;
+#endif
+
+ int tearGasOffset = -0.0f; // unused
+
+ for (int i = 0; i < ARRAY_SIZE(gaProjectileInfo); i++) {
+ if (!gaProjectileInfo[i].m_bInUse) continue;
+
+ CPed *ped = (CPed*)gaProjectileInfo[i].m_pSource;
+ if (ped != nil && ped->IsPed() && !ped->IsPointerValid())
+ gaProjectileInfo[i].m_pSource = nil;
+
+ if (ms_apProjectile[i] == nil) {
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse--;
+#endif
+
+ gaProjectileInfo[i].m_bInUse = false;
+ continue;
+ }
+ if ( (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS) && ms_apProjectile[i]->m_fElasticity > 0.1f ) {
+ if ( Abs(ms_apProjectile[i]->m_vecMoveSpeed.x) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.y) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.z) < 0.05f ) {
+ ms_apProjectile[i]->m_fElasticity = 0.03f;
+ }
+ }
+ const CVector &projectilePos = ms_apProjectile[i]->GetPosition();
+ CVector nextPos = CTimer::GetTimeStep() * ms_apProjectile[i]->m_vecMoveSpeed + projectilePos;
+
+ if ( nextPos.x <= PROJECTILE_BOUNDARY_MIN_X || nextPos.x >= PROJECTILE_BOUNDARY_MAX_X || nextPos.y <= PROJECTILE_BOUNDARY_MIN_Y || nextPos.y >= PROJECTILE_BOUNDARY_MAX_Y ) {
+ // Not RemoveProjectile, because we don't want no explosion
+ gaProjectileInfo[i].m_bInUse = false;
+ CWorld::Remove(ms_apProjectile[i]);
+ delete ms_apProjectile[i];
+ continue;
+ }
+ if ( gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS && CTimer::GetTimeInMilliseconds() > gaProjectileInfo[i].m_nExplosionTime - 19500 ) {
+ CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0);
+ CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(-0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0);
+ CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(tearGasOffset, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0);
+
+ if ( CTimer::GetTimeInMilliseconds() & 0x200 )
+ CWorld::SetPedsChoking(projectilePos.x, projectilePos.y, projectilePos.z, 6.0f, gaProjectileInfo[i].m_pSource);
+ }
+
+ if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) {
+ CParticle::AddParticlesAlongLine(PARTICLE_ROCKET_SMOKE, gaProjectileInfo[i].m_vecPos, projectilePos, CVector(0.0f, 0.0f, 0.0f), 0.7f, 0, 0, 0, 3000);
+ }
+
+ if (CTimer::GetTimeInMilliseconds() <= gaProjectileInfo[i].m_nExplosionTime || gaProjectileInfo[i].m_nExplosionTime == 0) {
+ if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) {
+ CVector pos = ms_apProjectile[i]->GetPosition();
+ CWorld::pIgnoreEntity = ms_apProjectile[i];
+ if (ms_apProjectile[i]->bHasCollided
+ || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false)
+ || CHeli::TestRocketCollision(&pos) || CPlane::TestRocketCollision(&pos)) {
+ RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]);
+ }
+ CWorld::pIgnoreEntity = nil;
+ ms_apProjectile[i]->m_vecMoveSpeed *= 1.07f;
+
+ } else if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV) {
+ CVector pos = ms_apProjectile[i]->GetPosition();
+ CWorld::pIgnoreEntity = ms_apProjectile[i];
+
+ if (gaProjectileInfo[i].m_pSource == nil
+ || ((gaProjectileInfo[i].m_vecPos - gaProjectileInfo[i].m_pSource->GetPosition()).MagnitudeSqr() >= 2.0f))
+ {
+ if (ms_apProjectile[i]->bHasCollided
+ || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false)) {
+ RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]);
+ }
+ }
+ CWorld::pIgnoreEntity = nil;
+ }
+ } else {
+ if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) {
+ CEntity *ent = gaProjectileInfo[i].m_pSource;
+ if (ent->IsPed() && ((CPed*)ped)->IsPlayer()) {
+ CPed *ped = (CPed*)ent;
+ if (ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponType != WEAPONTYPE_DETONATOR
+ || ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_nAmmoTotal == 0) {
+ gaProjectileInfo[i].m_nExplosionTime = 0;
+ }
+ }
+ } else {
+ RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]);
+ }
+ }
+
+ gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition();
+ }
+}
+
+bool
+CProjectileInfo::IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove)
+{
+ bool result = false;
+ for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) {
+ if (gaProjectileInfo[i].m_bInUse) {
+ if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_GRENADE) {
+ const CVector &pos = ms_apProjectile[i]->GetPosition();
+ if (pos.x >= x1 && pos.x <= x2 && pos.y >= y1 && pos.y <= y2 && pos.z >= z1 && pos.z <= z2) {
+ result = true;
+ if (remove) {
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse--;
+#endif
+
+ gaProjectileInfo[i].m_bInUse = false;
+ CWorld::Remove(ms_apProjectile[i]);
+ delete ms_apProjectile[i];
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+
+void
+CProjectileInfo::RemoveDetonatorProjectiles()
+{
+ for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) {
+ if (gaProjectileInfo[i].m_bInUse && gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) {
+ CExplosion::AddExplosion(nil, gaProjectileInfo[i].m_pSource, EXPLOSION_GRENADE, gaProjectileInfo[i].m_vecPos, 0); // TODO(Miami): New parameter: 1
+ gaProjectileInfo[i].m_bInUse = false;
+ CWorld::Remove(ms_apProjectile[i]);
+ delete ms_apProjectile[i];
+ }
+ }
+}
+
+void
+CProjectileInfo::RemoveAllProjectiles()
+{
+#ifdef SQUEEZE_PERFORMANCE
+ if (projectileInUse == 0)
+ return;
+#endif
+
+ for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) {
+ if (gaProjectileInfo[i].m_bInUse) {
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse--;
+#endif
+
+ gaProjectileInfo[i].m_bInUse = false;
+ CWorld::Remove(ms_apProjectile[i]);
+ delete ms_apProjectile[i];
+ }
+ }
+}
+
+bool
+CProjectileInfo::RemoveIfThisIsAProjectile(CObject *object)
+{
+#ifdef SQUEEZE_PERFORMANCE
+ if (projectileInUse == 0)
+ return false;
+#endif
+
+ int i = 0;
+ while (ms_apProjectile[i++] != object) {
+ if (i >= ARRAY_SIZE(ms_apProjectile))
+ return false;
+ }
+
+#ifdef SQUEEZE_PERFORMANCE
+ projectileInUse--;
+#endif
+
+ gaProjectileInfo[i].m_bInUse = false;
+ CWorld::Remove(ms_apProjectile[i]);
+ delete ms_apProjectile[i];
+ ms_apProjectile[i] = nil;
+ return true;
+}
diff --git a/src/weapons/ProjectileInfo.h b/src/weapons/ProjectileInfo.h
new file mode 100644
index 0000000..d168894
--- /dev/null
+++ b/src/weapons/ProjectileInfo.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "WeaponType.h"
+
+class CEntity;
+class CObject;
+class CProjectile;
+
+class CProjectileInfo
+{
+public:
+ eWeaponType m_eWeaponType;
+ CEntity *m_pSource;
+ uint32 m_nExplosionTime;
+ bool m_bInUse;
+ CVector m_vecPos;
+
+public:
+ static CProjectileInfo *GetProjectileInfo(int32 id);
+ static CProjectile *ms_apProjectile[NUM_PROJECTILES];
+
+ static void Initialise();
+ static void Shutdown();
+ static bool AddProjectile(CEntity *ped, eWeaponType weapon, CVector pos, float speed);
+ static void RemoveProjectile(CProjectileInfo *info, CProjectile *projectile);
+ static void RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos);
+ static bool RemoveIfThisIsAProjectile(CObject *pObject);
+ static void RemoveAllProjectiles();
+ static void RemoveDetonatorProjectiles();
+ static void Update();
+ static bool IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove);
+};
+
+extern CProjectileInfo gaProjectileInfo[NUM_PROJECTILES]; \ No newline at end of file
diff --git a/src/weapons/ShotInfo.cpp b/src/weapons/ShotInfo.cpp
new file mode 100644
index 0000000..788bcbe
--- /dev/null
+++ b/src/weapons/ShotInfo.cpp
@@ -0,0 +1,149 @@
+#include "common.h"
+
+#include "ShotInfo.h"
+#include "Entity.h"
+#include "Weapon.h"
+#include "World.h"
+#include "WeaponInfo.h"
+#include "General.h"
+#include "Timer.h"
+#include "Ped.h"
+#include "Fire.h"
+
+CShotInfo gaShotInfo[NUMSHOTINFOS];
+float CShotInfo::ms_afRandTable[20];
+
+#ifdef SQUEEZE_PERFORMANCE
+uint32 shotInfoInUse;
+#endif
+
+/*
+ Used for flamethrower. I don't know why it's name is CShotInfo.
+ Has no relation with any visual, just calculates the area fire affects
+ (including spreading and slowing of fire) and make entities burn/flee.
+*/
+
+void
+CShotInfo::Initialise()
+{
+ debug("Initialising CShotInfo...\n");
+ for(int i=0; i<ARRAY_SIZE(gaShotInfo); i++) {
+ gaShotInfo[i].m_inUse = false;
+ gaShotInfo[i].m_weapon = WEAPONTYPE_COLT45;
+ gaShotInfo[i].m_startPos = CVector(0.0f, 0.0f, 0.0f);
+ gaShotInfo[i].m_areaAffected = CVector(0.0f, 0.0f, 0.0f);
+ gaShotInfo[i].m_radius = 1.0f;
+ gaShotInfo[i].m_sourceEntity = nil;
+ gaShotInfo[i].m_timeout = 0;
+ }
+
+ // Not random for sure
+ float nextVal = -0.05f;
+ for (int i = 0; i < ARRAY_SIZE(ms_afRandTable); i++) {
+ ms_afRandTable[i] = nextVal;
+ nextVal += 0.005f;
+ }
+ debug("CShotInfo ready\n");
+#ifdef SQUEEZE_PERFORMANCE
+ shotInfoInUse = 0;
+#endif
+}
+
+bool
+CShotInfo::AddShot(CEntity *sourceEntity, eWeaponType weapon, CVector startPos, CVector endPos)
+{
+ CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon);
+
+ int slot;
+ for (slot = 0; slot < ARRAY_SIZE(gaShotInfo) && gaShotInfo[slot].m_inUse; slot++);
+
+ if (slot == ARRAY_SIZE(gaShotInfo))
+ return false;
+
+#ifdef SQUEEZE_PERFORMANCE
+ shotInfoInUse++;
+#endif
+
+ gaShotInfo[slot].m_inUse = true;
+ gaShotInfo[slot].m_weapon = weapon;
+ gaShotInfo[slot].m_startPos = startPos;
+ gaShotInfo[slot].m_areaAffected = endPos - startPos;
+ gaShotInfo[slot].m_radius = weaponInfo->m_fRadius;
+
+ if (weaponInfo->m_fSpread != 0.0f) {
+ gaShotInfo[slot].m_areaAffected.x += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread;
+ gaShotInfo[slot].m_areaAffected.y += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread;
+ gaShotInfo[slot].m_areaAffected.z += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)];
+ }
+ gaShotInfo[slot].m_areaAffected.Normalise();
+ if (weaponInfo->IsFlagSet(WEAPONFLAG_RAND_SPEED))
+ gaShotInfo[slot].m_areaAffected *= CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] + weaponInfo->m_fSpeed;
+ else
+ gaShotInfo[slot].m_areaAffected *= weaponInfo->m_fSpeed;
+
+ gaShotInfo[slot].m_sourceEntity = sourceEntity;
+ gaShotInfo[slot].m_timeout = CTimer::GetTimeInMilliseconds() + weaponInfo->m_fLifespan;
+
+ return true;
+}
+
+void
+CShotInfo::Shutdown()
+{
+ debug("Shutting down CShotInfo...\n");
+ debug("CShotInfo shut down\n");
+}
+
+void
+CShotInfo::Update()
+{
+#ifdef SQUEEZE_PERFORMANCE
+ if (shotInfoInUse == 0)
+ return;
+#endif
+ for (int slot = 0; slot < ARRAY_SIZE(gaShotInfo); slot++) {
+ CShotInfo &shot = gaShotInfo[slot];
+ if (shot.m_sourceEntity && shot.m_sourceEntity->IsPed() && !((CPed*)shot.m_sourceEntity)->IsPointerValid())
+ shot.m_sourceEntity = nil;
+
+ if (!shot.m_inUse)
+ continue;
+
+ CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(shot.m_weapon);
+ if (CTimer::GetTimeInMilliseconds() > shot.m_timeout) {
+#ifdef SQUEEZE_PERFORMANCE
+ shotInfoInUse--;
+#endif
+ shot.m_inUse = false;
+ }
+
+ if (weaponInfo->IsFlagSet(WEAPONFLAG_SLOWS_DOWN))
+ shot.m_areaAffected *= pow(0.96, CTimer::GetTimeStep()); // FRAMERATE
+
+ if (weaponInfo->IsFlagSet(WEAPONFLAG_EXPANDS))
+ shot.m_radius += 0.075f * CTimer::GetTimeStep();
+
+ shot.m_startPos += CTimer::GetTimeStep() * shot.m_areaAffected;
+ if (shot.m_sourceEntity) {
+ assert(shot.m_sourceEntity->IsPed());
+ CPed *ped = (CPed*) shot.m_sourceEntity;
+ float radius = Max(1.0f, shot.m_radius);
+
+ for (int i = 0; i < ped->m_numNearPeds; ++i) {
+ CPed *nearPed = ped->m_nearPeds[i];
+ if (nearPed->IsPointerValid()) {
+ if (nearPed->IsPedInControl() && (nearPed->GetPosition() - shot.m_startPos).MagnitudeSqr() < radius && !nearPed->bFireProof) {
+
+ if (!nearPed->IsPlayer()) {
+ nearPed->SetFindPathAndFlee(shot.m_sourceEntity, 10000);
+ nearPed->SetMoveState(PEDMOVE_SPRINT);
+ }
+ gFireManager.StartFire(nearPed, shot.m_sourceEntity, 0.8f, true);
+ }
+ }
+ }
+ }
+ if (!((CTimer::GetFrameCounter() + slot) & 3))
+ CWorld::SetCarsOnFire(shot.m_startPos.x, shot.m_startPos.y, shot.m_startPos.z, 4.0f, shot.m_sourceEntity);
+ }
+}
diff --git a/src/weapons/ShotInfo.h b/src/weapons/ShotInfo.h
new file mode 100644
index 0000000..db6158c
--- /dev/null
+++ b/src/weapons/ShotInfo.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "WeaponType.h"
+
+class CEntity;
+
+class CShotInfo
+{
+public:
+ eWeaponType m_weapon;
+ CVector m_startPos;
+ CVector m_areaAffected;
+ float m_radius;
+ CEntity *m_sourceEntity;
+ float m_timeout;
+ bool m_inUse;
+
+ static float ms_afRandTable[20];
+
+ static void Initialise(void);
+ static bool AddShot(CEntity*, eWeaponType, CVector, CVector);
+ static void Shutdown(void);
+ static void Update(void);
+};
diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp
new file mode 100644
index 0000000..7a54941
--- /dev/null
+++ b/src/weapons/Weapon.cpp
@@ -0,0 +1,3300 @@
+#include "common.h"
+
+#include "Weapon.h"
+#include "AnimBlendAssociation.h"
+#include "AudioManager.h"
+#include "BulletInfo.h"
+#include "Camera.h"
+#include "Coronas.h"
+#include "DMAudio.h"
+#include "Explosion.h"
+#include "General.h"
+#include "Glass.h"
+#include "Heli.h"
+#include "ModelIndices.h"
+#include "Object.h"
+#include "Pad.h"
+#include "Particle.h"
+#include "Ped.h"
+#include "PointLights.h"
+#include "Pools.h"
+#include "ProjectileInfo.h"
+#include "RpAnimBlend.h"
+#include "ShotInfo.h"
+#include "SpecialFX.h"
+#include "Stats.h"
+#include "TempColModels.h"
+#include "Timer.h"
+#include "Automobile.h"
+#include "Boat.h"
+#include "WaterLevel.h"
+#include "WeaponInfo.h"
+#include "World.h"
+#include "SurfaceTable.h"
+#include "Bike.h"
+#include "Glass.h"
+#include "Sprite.h"
+#include "Pickups.h"
+
+float fReloadAnimSampleFraction[5] = { 0.5f, 0.7f, 0.75f, 0.75f, 0.7f };
+float fSeaSparrowAimingAngle = 10.0f;
+float fHunterAimingAngle = 30.0f;
+float fPlayerAimScaleDist = 5.0f;
+float fPlayerAimScale = 2.5f;
+
+bool CWeapon::bPhotographHasBeenTaken;
+
+#ifdef SECUROM
+int32 sniperPirateCheck = 0x00797743; // 'Cwy\0' ???
+#endif
+
+CWeaponInfo *
+CWeapon::GetInfo()
+{
+ CWeaponInfo *info = CWeaponInfo::GetWeaponInfo(m_eWeaponType);
+ ASSERT(info!=nil);
+ return info;
+}
+
+CWeapon::CWeapon(eWeaponType type, int32 ammo)
+{
+ m_eWeaponType = type;
+ m_eWeaponState = WEAPONSTATE_READY;
+ m_nAmmoTotal = Min(ammo, 99999);
+ m_nAmmoInClip = 0;
+ Reload();
+ m_nTimer = 0;
+ m_bAddRotOffset = false;
+}
+
+void
+CWeapon::InitialiseWeapons(void)
+{
+ CWeaponInfo::Initialise();
+ CShotInfo::Initialise();
+ CExplosion::Initialise();
+ CProjectileInfo::Initialise();
+ CBulletInfo::Initialise();
+ bPhotographHasBeenTaken = false;
+}
+
+void
+CWeapon::ShutdownWeapons(void)
+{
+ CWeaponInfo::Shutdown();
+ CShotInfo::Shutdown();
+ CExplosion::Shutdown();
+ CProjectileInfo::Shutdown();
+ CBulletInfo::Shutdown();
+}
+
+void
+CWeapon::UpdateWeapons(void)
+{
+ CShotInfo::Update();
+ CExplosion::Update();
+ CProjectileInfo::Update();
+ CBulletInfo::Update();
+}
+
+
+void
+CWeapon::Initialise(eWeaponType type, int32 ammo)
+{
+ m_eWeaponType = type;
+ m_eWeaponState = WEAPONSTATE_READY;
+ m_nAmmoTotal = Min(ammo, 99999);
+ m_nAmmoInClip = 0;
+ Reload();
+ m_nTimer = 0;
+ int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId;
+ int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id;
+
+ if ( modelId != -1 )
+ CModelInfo::GetModelInfo(modelId)->AddRef();
+ if ( model2Id != -1 )
+ CModelInfo::GetModelInfo(model2Id)->AddRef();
+}
+
+void
+CWeapon::Shutdown()
+{
+ int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId;
+ if (modelId != -1)
+ CModelInfo::GetModelInfo(modelId)->RemoveRef();
+
+ int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id;
+ if (model2Id != -1)
+ CModelInfo::GetModelInfo(model2Id)->RemoveRef();
+
+ m_eWeaponType = WEAPONTYPE_UNARMED;
+ m_eWeaponState = WEAPONSTATE_READY;
+ m_nAmmoInClip = 0;
+ m_nAmmoTotal = 0;
+ m_nTimer = 0;
+}
+
+bool
+CWeapon::Fire(CEntity *shooter, CVector *fireSource)
+{
+ ASSERT(shooter!=nil);
+
+ CVector fireOffset(0.0f, 0.0f, 0.6f);
+ CVector *source = fireSource;
+#ifdef FIX_BUGS
+ static CVector shooterSource;
+#else
+ CVector shooterSource;
+#endif
+
+ if ( !fireSource )
+ {
+ shooterSource = shooter->GetMatrix() * fireOffset;
+ source = &shooterSource;
+ }
+
+ if ( m_bAddRotOffset )
+ {
+ float heading = RADTODEG(shooter->GetForward().Heading());
+ float angle = DEGTORAD(heading);
+ (*source).x += -Sin(angle) * 0.15f;
+ (*source).y += Cos(angle) * 0.15f;
+ }
+
+ if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING )
+ return false;
+
+ bool fired;
+ bool addFireRateAsDelay = true;
+
+ if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE )
+ {
+ if (m_nAmmoInClip <= 0) {
+ if (m_nAmmoTotal <= 0 || m_eWeaponState == WEAPONSTATE_RELOADING)
+ return false;
+
+ Reload();
+ }
+
+ switch ( m_eWeaponType )
+ {
+ case WEAPONTYPE_SHOTGUN:
+ case WEAPONTYPE_SPAS12_SHOTGUN:
+ case WEAPONTYPE_STUBBY_SHOTGUN:
+ {
+ addFireRateAsDelay = true;
+ fired = FireShotgun(shooter, source);
+
+ break;
+ }
+
+ case WEAPONTYPE_SNIPERRIFLE:
+ case WEAPONTYPE_LASERSCOPE:
+ {
+ if (shooter == FindPlayerPed())
+ fired = FireSniper(shooter);
+ else
+ fired = FireInstantHit(shooter, source);
+
+ break;
+ }
+
+ case WEAPONTYPE_COLT45:
+ case WEAPONTYPE_PYTHON:
+ case WEAPONTYPE_UZI:
+ case WEAPONTYPE_TEC9:
+ case WEAPONTYPE_SILENCED_INGRAM:
+ case WEAPONTYPE_MP5:
+ case WEAPONTYPE_M4:
+ case WEAPONTYPE_RUGER:
+ case WEAPONTYPE_M60:
+ case WEAPONTYPE_MINIGUN:
+ case WEAPONTYPE_HELICANNON:
+ {
+ if ((TheCamera.PlayerWeaponMode.Mode == CCam::MODE_HELICANNON_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON)
+ && shooter == FindPlayerPed()) {
+ addFireRateAsDelay = true;
+ fired = FireM16_1stPerson(shooter);
+ } else {
+ addFireRateAsDelay = false;
+ fired = FireInstantHit(shooter, source);
+ }
+ break;
+ }
+
+ case WEAPONTYPE_ROCKETLAUNCHER:
+ {
+ if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil )
+ {
+ float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude();
+
+ if ( distToTarget > 8.0f || ((CPed*)shooter)->IsPlayer() )
+ fired = FireProjectile(shooter, source, 0.0f);
+ else
+ fired = false;
+ }
+ else
+ fired = FireProjectile(shooter, source, 0.0f);
+
+ break;
+ }
+
+ case WEAPONTYPE_MOLOTOV:
+ case WEAPONTYPE_GRENADE:
+ case WEAPONTYPE_DETONATOR_GRENADE:
+ case WEAPONTYPE_TEARGAS:
+ {
+ if ( shooter == FindPlayerPed() )
+ {
+ fired = FireProjectile(shooter, source, ((CPlayerPed*)shooter)->m_fAttackButtonCounter*0.0375f);
+ }
+ else if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil )
+ {
+ float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude();
+ float power = clamp((distToTarget-10.0f)*0.02f, 0.2f, 1.0f);
+
+ fired = FireProjectile(shooter, source, power);
+ }
+ else
+ fired = FireProjectile(shooter, source, 0.3f);
+
+ if (m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) {
+ ((CPed*)shooter)->GiveWeapon(WEAPONTYPE_DETONATOR, 1, true);
+ ((CPed*)shooter)->GetWeapon(((CPed*)shooter)->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY;
+ ((CPed*)shooter)->SetCurrentWeapon(WEAPONTYPE_DETONATOR);
+ }
+ break;
+ }
+
+ case WEAPONTYPE_FLAMETHROWER:
+ {
+ fired = FireAreaEffect(shooter, source);
+
+ break;
+ }
+
+ case WEAPONTYPE_DETONATOR:
+ {
+ CWorld::UseDetonator(shooter);
+ m_nAmmoTotal = 1;
+ m_nAmmoInClip = m_nAmmoTotal;
+ fired = true;
+
+ break;
+ }
+
+ case WEAPONTYPE_CAMERA:
+ {
+ fired = TakePhotograph(shooter);
+
+ break;
+ }
+
+ default:
+ {
+ debug("Unknown weapon type, Weapon.cpp");
+ break;
+ }
+ }
+
+ if (fired)
+ {
+ bool isPlayer = false;
+
+ if (shooter->IsPed())
+ {
+ CPed* shooterPed = (CPed*)shooter;
+
+ if ( m_eWeaponType != WEAPONTYPE_CAMERA )
+ {
+ shooterPed->bIsShooting = true;
+
+ if (shooterPed->IsPlayer())
+ isPlayer = true;
+ }
+
+ DMAudio.PlayOneShot(shooterPed->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
+
+ if ( isPlayer )
+ {
+ CPed *aimPed = (CPed *)shooterPed->m_pSeekTarget;
+ if ( aimPed )
+ {
+ if ( aimPed->IsPed() )
+ shooterPed->Say(SOUND_PED_ON_FIRE);
+ }
+ }
+ }
+
+ switch ( m_eWeaponType )
+ {
+ case WEAPONTYPE_COLT45:
+ case WEAPONTYPE_PYTHON:
+ case WEAPONTYPE_SHOTGUN:
+ case WEAPONTYPE_SPAS12_SHOTGUN:
+ case WEAPONTYPE_STUBBY_SHOTGUN:
+ case WEAPONTYPE_TEC9:
+ case WEAPONTYPE_UZI:
+ case WEAPONTYPE_SILENCED_INGRAM:
+ case WEAPONTYPE_MP5:
+ case WEAPONTYPE_M4:
+ case WEAPONTYPE_RUGER:
+ case WEAPONTYPE_SNIPERRIFLE:
+ case WEAPONTYPE_LASERSCOPE:
+ case WEAPONTYPE_M60:
+ case WEAPONTYPE_MINIGUN:
+ CStats::RoundsFiredByPlayer++;
+ break;
+
+ case WEAPONTYPE_GRENADE:
+ case WEAPONTYPE_DETONATOR_GRENADE:
+ case WEAPONTYPE_MOLOTOV:
+ case WEAPONTYPE_ROCKET:
+ case WEAPONTYPE_ROCKETLAUNCHER:
+ case WEAPONTYPE_DETONATOR:
+ case WEAPONTYPE_HELICANNON:
+ CStats::KgsOfExplosivesUsed++;
+ break;
+ }
+
+
+ if (m_nAmmoInClip > 0)
+ m_nAmmoInClip--;
+
+ if (m_nAmmoTotal > 0 && (m_nAmmoTotal < 25000 || isPlayer) && (!isPlayer || CStats::GetPercentageProgress() < 100.0f || m_eWeaponType == WEAPONTYPE_DETONATOR))
+ m_nAmmoTotal--;
+
+ if (m_eWeaponState == WEAPONSTATE_READY && m_eWeaponType == WEAPONTYPE_FLAMETHROWER)
+ DMAudio.PlayOneShot(((CPhysical*)shooter)->m_audioEntityId, SOUND_WEAPON_FLAMETHROWER_FIRE, 0.0f);
+
+ m_eWeaponState = WEAPONSTATE_FIRING;
+
+ if (m_nAmmoInClip == 0)
+ {
+ if (m_nAmmoTotal == 0) {
+ if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA)
+ CPad::GetPad(0)->Clear(false);
+
+ return true;
+ }
+
+ m_eWeaponState = WEAPONSTATE_RELOADING;
+ m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload;
+
+ if (shooter == FindPlayerPed())
+ {
+ if (CWorld::Players[CWorld::PlayerInFocus].m_bFastReload)
+ m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload / 4;
+ }
+
+ return true;
+ }
+
+ if ( addFireRateAsDelay )
+ m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nFiringRate;
+ else
+ m_nTimer = CTimer::GetTimeInMilliseconds();
+ }
+ }
+ else
+ {
+ if ( m_eWeaponState != WEAPONSTATE_FIRING )
+ {
+ m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload;
+ m_eWeaponState = WEAPONSTATE_FIRING;
+
+ if (shooter->IsPed() && m_eWeaponType != WEAPONTYPE_CHAINSAW)
+ {
+ DMAudio.PlayOneShot(((CPed*)shooter)->m_audioEntityId, SOUND_MELEE_ATTACK_START, m_eWeaponType << 8);
+ }
+ }
+
+ fired = FireMelee(shooter, *source);
+ }
+
+ if ( m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BASEBALLBAT )
+ return true;
+ else
+ return fired;
+}
+
+bool
+CWeapon::FireFromCar(CVehicle *shooter, bool left, bool right)
+{
+ ASSERT(shooter!=nil);
+
+ if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING )
+ return false;
+
+ if ( m_nAmmoInClip <= 0 )
+ return false;
+
+ if ( FireInstantHitFromCar(shooter, left, right) )
+ {
+ DMAudio.PlayOneShot(shooter->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
+
+ if ( m_nAmmoInClip > 0 )
+ m_nAmmoInClip--;
+
+ if ( m_nAmmoTotal < 25000 && m_nAmmoTotal > 0 && (!shooter || shooter->GetStatus() != STATUS_PLAYER || CStats::GetPercentageProgress() < 100.f))
+ m_nAmmoTotal--;
+
+ m_eWeaponState = WEAPONSTATE_FIRING;
+
+ if ( m_nAmmoInClip == 0 )
+ {
+ if ( m_nAmmoTotal == 0 )
+ return true;
+
+ m_eWeaponState = WEAPONSTATE_RELOADING;
+ m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload;
+
+ return true;
+ }
+
+ m_nTimer = CTimer::GetTimeInMilliseconds() + 1000;
+ }
+
+ return true;
+}
+
+bool
+CWeapon::FireMelee(CEntity *shooter, CVector &fireSource)
+{
+ ASSERT(shooter!=nil);
+
+ CWeaponInfo *info = GetInfo();
+
+ bool anim2Playing = false;
+
+ if ( CPed::GetFireAnimGround(info, false) != (AnimationId)0 )
+ {
+ if ( RpAnimBlendClumpGetAssociation(shooter->GetClump(), CPed::GetFireAnimGround(info, false)) )
+ anim2Playing = true;
+ }
+
+ ASSERT(shooter->IsPed());
+
+ CPed *shooterPed = (CPed*)shooter;
+
+ if (shooterPed == FindPlayerPed())
+ {
+ if (m_eWeaponType == WEAPONTYPE_GOLFCLUB || m_eWeaponType == WEAPONTYPE_NIGHTSTICK ||
+ (m_eWeaponType >= WEAPONTYPE_BASEBALLBAT && m_eWeaponType <= WEAPONTYPE_CHAINSAW))
+ {
+ CGlass::BreakGlassPhysically(fireSource, info->m_fRadius);
+
+ if (m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000);
+ }
+ }
+
+ int damageEntityRegistered = 0;
+
+ for ( int32 i = 0; i < shooterPed->m_numNearPeds; i++ )
+ {
+ CPed *victimPed = shooterPed->m_nearPeds[i];
+ ASSERT(victimPed!=nil);
+
+ if ( (victimPed->m_nPedType != shooterPed->m_nPedType || victimPed == shooterPed->m_pSeekTarget)
+ && victimPed != shooterPed->m_leader || !(CGeneral::GetRandomNumber() & 31)
+ && (!shooterPed->IsGangMember() || victimPed->CanBeDamagedByThisGangMember(shooterPed)) )
+ {
+ bool collided = false;
+
+ if (victimPed->m_nPedState == PED_DRIVING && (m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE
+ || info->IsFlagSet(WEAPONFLAG_FIGHTMODE)))
+ continue;
+
+ float victimPedRadius = victimPed->GetBoundRadius() + info->m_fRadius;
+ if ( victimPed->bUsesCollision || victimPed->Dead() || victimPed->Driving() )
+ {
+ CVector victimPedPos = victimPed->GetPosition();
+ if ( SQR(victimPedRadius) > (victimPedPos-fireSource).MagnitudeSqr() )
+ {
+ CVector collisionDist;
+ CColModel* victimPedCol = &CTempColModels::ms_colModelPed1;
+ bool useLocalPos = false;
+ if (victimPed->m_nPedState == PED_FALL
+ || victimPed->m_nPedState == PED_DIE && victimPed->bIsPedDieAnimPlaying
+ || victimPed->m_nWaitState == WAITSTATE_SIT_IDLE
+ || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE)
+ {
+ useLocalPos = true;
+ victimPedCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(victimPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(victimPed->GetClump());
+ } else if (victimPed->DyingOrDead()) {
+ victimPedCol = &CTempColModels::ms_colModelPedGroundHit;
+ }
+
+ int32 s = 0;
+ while ( s < victimPedCol->numSpheres )
+ {
+ CColSphere *sphere = &victimPedCol->spheres[s];
+
+ if (useLocalPos)
+ collisionDist = sphere->center - fireSource;
+ else
+ collisionDist = victimPedPos + sphere->center - fireSource;
+
+ if ( SQR(sphere->radius + info->m_fRadius) > collisionDist.MagnitudeSqr() )
+ {
+ collided = true;
+ break;
+ }
+ s++;
+ }
+
+ if ( !(victimPed->IsPlayer() && victimPed->GetPedState() == PED_GETUP) )
+ {
+ if ( collided )
+ {
+ float victimPedHealth = victimPed->m_fHealth;
+ CVector bloodPos = fireSource + (collisionDist*0.7f);
+
+ CVector2D posOffset(shooterPed->GetPosition().x-victimPedPos.x, shooterPed->GetPosition().y-victimPedPos.y);
+
+ int32 localDir = victimPed->GetLocalDirection(posOffset);
+
+ bool isHeavy = m_eWeaponType >= WEAPONTYPE_GOLFCLUB && m_eWeaponType <= WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_HAMMER;
+
+ if (shooterPed->m_fDamageImpulse == 0.0f)
+ {
+ shooterPed->m_pDamageEntity = victimPed;
+ victimPed->RegisterReference(&shooterPed->m_pDamageEntity);
+ }
+
+ damageEntityRegistered = 3;
+ if (victimPed->bInVehicle)
+ {
+ CVehicle *victimVeh = victimPed->m_pMyVehicle;
+ if (victimVeh)
+ {
+ if (victimVeh->IsBike())
+ {
+ CBike *victimBike = (CBike*)victimVeh;
+ victimBike->KnockOffRider(m_eWeaponType, localDir, victimPed, false);
+ if (victimBike->pDriver)
+ victimBike->pDriver->ReactToAttack(shooterPed);
+ else
+ {
+ if (victimVeh->pPassengers[0])
+ victimVeh->pPassengers[0]->ReactToAttack(shooterPed);
+ }
+ continue;
+ }
+ }
+ }
+
+ if ( !victimPed->DyingOrDead() )
+ victimPed->ReactToAttack(shooterPed);
+
+ uint8 hitLevel = HITLEVEL_HIGH;
+ if ( isHeavy && (victimPed->OnGround() || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE))
+ hitLevel = HITLEVEL_GROUND;
+
+ victimPed->StartFightDefend(localDir, hitLevel, 10);
+
+ if ( !victimPed->DyingOrDead() )
+ {
+ if ( shooterPed->IsPlayer() && isHeavy && anim2Playing )
+ victimPed->InflictDamage(shooterPed, m_eWeaponType, 100.0f, PEDPIECE_TORSO, localDir);
+ else if ( shooterPed->IsPlayer() && ((CPlayerPed*)shooterPed)->m_bAdrenalineActive )
+ victimPed->InflictDamage(shooterPed, m_eWeaponType, 3.5f*info->m_nDamage, PEDPIECE_TORSO, localDir);
+ else
+ {
+ if ( victimPed->IsPlayer() && isHeavy ) // wtf, it's not fair
+ victimPed->InflictDamage(shooterPed, m_eWeaponType, 2.0f*info->m_nDamage, PEDPIECE_TORSO, localDir);
+ else
+ victimPed->InflictDamage(shooterPed, m_eWeaponType, info->m_nDamage, PEDPIECE_TORSO, localDir);
+ }
+ }
+
+ if ( CGame::nastyGame && victimPed->GetIsOnScreen() )
+ {
+ CVector dir = collisionDist * RecipSqrt(1.0f, 10.0f*collisionDist.MagnitudeSqr());
+
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir);
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir);
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir);
+
+ if ( isHeavy )
+ {
+ dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f);
+ dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f);
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir);
+
+ dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f);
+ dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f);
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir);
+ }
+
+ if (m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ {
+ if (victimPed->m_nPedState != PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 1)
+ || victimPed->m_nPedState == PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 3))
+ {
+ CParticle::AddParticle(PARTICLE_TEST, bloodPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f);
+ }
+ CVector newDir(dir);
+ newDir.z += 0.2f;
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, bloodPos, newDir);
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir);
+ newDir.z = dir.z + 0.1f;
+ CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir);
+ newDir.x = 0.0f;
+ newDir.y = 0.0f;
+ newDir.z = 0.01f;
+ CParticle::AddParticle(PARTICLE_DEBRIS2, bloodPos, newDir);
+
+ CVector dropDir(CGeneral::GetRandomNumberInRange(-0.15f, 0.15f), CGeneral::GetRandomNumberInRange(0.1f, 0.35f), 0.f);
+ CVector dropPos(CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_X(50.0f), SCREEN_STRETCH_FROM_RIGHT(50.0f)),
+ CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_Y(50.0f), SCREEN_STRETCH_FROM_BOTTOM(50.0f)), 1.f);
+ CParticle::AddParticle(PARTICLE_BLOODDROP, dropPos, dropDir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f),
+ CRGBA(0, 0, 0, 0), 0, 0, CGeneral::GetRandomNumber() & 1, 0);
+
+ }
+ if (info->m_AnimToPlay == ASSOCGRP_KNIFE)
+ {
+ dir += 0.1f * shooterPed->GetUp() + 0.05f * shooterPed->GetRight();
+ CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir);
+ CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir);
+ CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir);
+ }
+ }
+
+ if ( !victimPed->OnGround() )
+ {
+ if ( victimPed->m_fHealth > 0.0f
+ && (victimPed->m_fHealth < 30.0f && victimPedHealth > 30.0f ||
+ (isHeavy || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) && !victimPed->IsPlayer()) )
+ {
+ posOffset.Normalise();
+ victimPed->bIsStanding = false;
+ if(m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 2.0f);
+ else
+ victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f);
+
+ if ( isHeavy && victimPed->IsPlayer() )
+ victimPed->SetFall(3000, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false);
+ else
+ victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false);
+
+ shooterPed->m_pSeekTarget = victimPed;
+ shooterPed->m_pSeekTarget->RegisterReference(&shooterPed->m_pSeekTarget);
+ }
+ }
+ else if (victimPed->Dying() && !anim2Playing)
+ {
+ posOffset.Normalise();
+ victimPed->bIsStanding = false;
+ if(m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ victimPed->ApplyMoveForce(posOffset.x*-1.0f, posOffset.y*-1.0f, 1.0f);
+ else
+ victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f);
+ }
+
+ m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT;
+
+ if (m_eWeaponType != WEAPONTYPE_KNIFE && m_eWeaponType != WEAPONTYPE_MACHETE
+ && m_eWeaponType != WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_CHAINSAW) {
+
+ if (victimPed->m_nPedType == PEDTYPE_COP)
+ CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000);
+ else
+ CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victimPed, shooterPed, 2000);
+ } else {
+ if (victimPed->m_nPedType == PEDTYPE_COP)
+ CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000);
+ else
+ CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victimPed, shooterPed, 2000);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ CVehicle *nearVeh = (CVehicle*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, false, true, false, false, false, false);
+ if (nearVeh && nearVeh->IsCar())
+ {
+ CAutomobile *nearCar = (CAutomobile*)nearVeh;
+ m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT;
+ if (shooterPed == FindPlayerPed())
+ {
+ if (nearCar->IsLawEnforcementVehicle())
+ {
+ FindPlayerPed()->SetWantedLevelNoDrop(1);
+ }
+ CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_VEHICLE, nearCar, shooterPed, 2000);
+ }
+ float oldHealth = nearCar->m_fHealth;
+ if (m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ {
+ for( int32 i=0; i<4; i++ )
+ {
+ CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f));
+ CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, gaTempSphereColPoints[0].normal * 0.1f);
+ }
+ }
+ if (m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ {
+ nearCar->VehicleDamage(info->m_nDamage * (0.00075f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB);
+
+ CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0);
+ }
+ else
+ {
+ nearCar->VehicleDamage(info->m_nDamage* (0.01f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB);
+ }
+ if (nearCar->m_fHealth < oldHealth)
+ {
+ nearCar->m_nLastWeaponDamage = m_eWeaponType;
+ nearCar->m_pLastDamageEntity = shooterPed;
+ }
+ if (shooterPed->m_fDamageImpulse == 0.0f)
+ {
+ shooterPed->m_pDamageEntity = nearCar;
+ nearCar->RegisterReference(&shooterPed->m_pDamageEntity);
+ }
+ damageEntityRegistered = 2;
+ if (FindPlayerPed()->GetWeapon() == this && nearCar->VehicleCreatedBy != MISSION_VEHICLE)
+ {
+ if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH
+ && (CGeneral::GetRandomTrueFalse() || nearCar->AutoPilot.m_nCarMission != MISSION_CRUISE))
+ {
+ int leaveCarDelay = 200;
+ CPed *driver = nearCar->pDriver;
+ if (driver && driver->CharCreatedBy != MISSION_CHAR)
+ {
+ if (driver->m_pedStats->m_temper <= driver->m_pedStats->m_fear)
+ {
+ driver->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE);
+ }
+ else
+ {
+ driver->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed());
+ driver->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000;
+ driver->m_prevObjective = OBJECTIVE_KILL_CHAR_ON_FOOT;
+ }
+ driver->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 200;
+ leaveCarDelay = 400;
+ }
+ for (int j = 0; j < nearCar->m_nNumPassengers; ++j)
+ {
+ CPed *passenger = nearCar->pPassengers[j];
+ if (passenger && passenger->CharCreatedBy != MISSION_CHAR)
+ {
+ nearCar->pPassengers[j]->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE);
+ passenger->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + leaveCarDelay;
+ leaveCarDelay += 200;
+ }
+ }
+ }
+ else
+ {
+ CPed *driver = nearCar->pDriver;
+ if (driver)
+ {
+ if (driver->m_objective != OBJECTIVE_LEAVE_CAR && driver->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT &&
+ driver->m_objective != OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE)
+ {
+ if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH)
+ nearCar->AutoPilot.m_nCruiseSpeed = nearCar->AutoPilot.m_nCruiseSpeed * 1.5f;
+
+ nearCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH;
+ }
+ }
+ }
+ }
+ }
+ if (m_eWeaponType == WEAPONTYPE_CHAINSAW)
+ {
+ CEntity *nearStatic = (CObject*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, true, false, false, true, false, false);
+ if (nearStatic)
+ {
+ for(int i=0; i < 4; i++) {
+ CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f), 0, 0.0f, 0, 0, 0, 0);
+ CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, 0.1f * gaTempSphereColPoints[0].normal, 0, 0.0f, 0, 0, 0, 0);
+ }
+
+ CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0);
+
+ if (!damageEntityRegistered)
+ {
+ m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT;
+ if (shooterPed->m_fDamageImpulse == 0.0f)
+ {
+ shooterPed->m_pDamageEntity = nearStatic;
+ nearStatic->RegisterReference(&shooterPed->m_pDamageEntity);
+ }
+ }
+ if (nearStatic->IsObject() && ((CObject*)nearStatic)->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY)
+ ((CObject*)nearStatic)->ObjectDamage(200.0f);
+ }
+ }
+
+ return true;
+}
+
+bool
+CWeapon::FireInstantHit(CEntity *shooter, CVector *fireSource)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(fireSource!=nil);
+
+ CWeaponInfo *info = GetInfo();
+
+ CVector source, target;
+ CColPoint point;
+ CEntity *victim = nil;
+
+ float heading = RADTODEG(shooter->GetForward().Heading());
+ float angle = DEGTORAD(heading);
+
+ CVector2D ahead(-Sin(angle), Cos(angle));
+ ahead.Normalise();
+
+ CVector vel = ((CPed *)shooter)->m_vecMoveSpeed;
+ int32 shooterMoving = false;
+ if ( Abs(vel.x) > 0.0f && Abs(vel.y) > 0.0f )
+ shooterMoving = true;
+
+ if ( shooter == FindPlayerPed() )
+ {
+ static float prev_heading = 0.0f;
+ prev_heading = ((CPed*)shooter)->m_fRotationCur;
+ }
+
+ if ( shooter->IsPed() && ((CPed *)shooter)->m_pPointGunAt )
+ {
+ CPed *shooterPed = (CPed *)shooter;
+ if ( shooterPed->m_pedIK.m_flags & CPedIK::GUN_POINTED_SUCCESSFULLY )
+ {
+ int32 accuracy = shooterPed->m_wepAccuracy;
+ int32 inaccuracy = 100-accuracy;
+
+ CPed *threatAttack = (CPed*)shooterPed->m_pPointGunAt;
+ if ( threatAttack->IsPed() )
+ {
+ threatAttack->m_pedIK.GetComponentPosition(target, PED_MID);
+ threatAttack->ReactToPointGun(shooter);
+ }
+ else
+ target = threatAttack->GetPosition();
+
+ target -= *fireSource;
+ float distToTarget = Max(target.Magnitude(), 0.01f);
+ target *= info->m_fRange / distToTarget;
+ target += *fireSource;
+
+ if (shooter == FindPlayerPed() && inaccuracy != 0.f)
+ {
+ float newInaccuracy = fPlayerAimScale * FindPlayerPed()->m_fAttackButtonCounter * (inaccuracy * Min(1.f, fPlayerAimScaleDist / distToTarget));
+ if (FindPlayerPed()->bIsDucking)
+ newInaccuracy *= 0.4f;
+
+ target.x += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy;
+ target.y += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy;
+ target.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * newInaccuracy;
+ FindPlayerPed()->m_fAttackButtonCounter += info->m_nDamage * 0.04f;
+ }
+ else if (inaccuracy > 0.f)
+ {
+ if (threatAttack == FindPlayerPed())
+ {
+ float speed = Min(0.33f, FindPlayerPed()->m_vecMoveSpeed.Magnitude());
+ inaccuracy *= (0.3f * speed * 100.f / 33.f + 0.8f);
+ }
+ target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy;
+ target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy;
+ target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracy;
+ }
+
+ if (shooter == FindPlayerPed())
+ CWorld::bIncludeDeadPeds = true;
+
+ CWorld::bIncludeBikers = true;
+ ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeDeadPeds = false;
+ CWorld::bIncludeBikers = false;
+ }
+ else
+ {
+ target.x = info->m_fRange;
+ target.y = 0.0f;
+ target.z = 0.0f;
+
+ shooterPed->TransformToNode(target, PED_HANDR);
+
+ CWorld::bIncludeBikers = true;
+ ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeBikers = false;
+ }
+ }
+ else if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() )
+ {
+ TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target);
+
+ CWorld::bIncludeBikers = true;
+ CWorld::bIncludeDeadPeds = true;
+ CWorld::bIncludeCarTyres = true;
+ ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeBikers = false;
+ CWorld::bIncludeDeadPeds = false;
+ CWorld::bIncludeCarTyres = false;
+
+ if (victim)
+ CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target);
+
+ int32 rotSpeed = 1;
+ if ( m_eWeaponType == WEAPONTYPE_M4 )
+ rotSpeed = 4;
+
+ CVector bulletPos;
+ if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, 4) )
+ {
+ for ( int32 i = 0; i < 16; i++ )
+ CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed);
+ }
+ }
+ else
+ {
+ uint32 model = shooter->GetModelIndex();
+ if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW)
+ {
+ float inaccuracyMult = 0.6f;
+ target = shooter->GetForward();
+ if (shooter->GetStatus() == STATUS_PLAYER)
+ {
+ target *= info->m_fRange;
+ target += *fireSource;
+ CWeapon::DoDriveByAutoAiming(FindPlayerPed(), (CVehicle*)shooter, fireSource, &target);
+ target -= *fireSource;
+ target.Normalise();
+ if (model == MI_SEASPAR || model == MI_SPARROW)
+ inaccuracyMult = 0.1f;
+ else
+ inaccuracyMult = 0.3f;
+ }
+ target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult;
+ target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult;
+ target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracyMult;
+
+ target.Normalise();
+ target *= info->m_fRange;
+ target += *fireSource;
+ CWorld::pIgnoreEntity = shooter;
+ ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false);
+ CWorld::pIgnoreEntity = nil;
+
+ int32 rotSpeed = 1;
+ if (m_eWeaponType == WEAPONTYPE_M4)
+ rotSpeed = 4;
+
+ CVector bulletPos;
+ if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4))
+ {
+ for (int32 i = 0; i < 16; i++)
+ CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed);
+ }
+ }
+ else
+ {
+ float shooterHeading = RADTODEG(shooter->GetForward().Heading());
+ float shooterAngle = DEGTORAD(shooterHeading);
+
+ CVector2D rotOffset(-Sin(shooterAngle), Cos(shooterAngle));
+ rotOffset.Normalise();
+
+ target = *fireSource;
+ target.x += rotOffset.x * info->m_fRange;
+ target.y += rotOffset.y * info->m_fRange;
+
+ CParticle::HandleShootableBirdsStuff(shooter, *fireSource);
+ if (shooter->IsPed() && ((CPed*)shooter)->bDoomAim && (shooter != FindPlayerPed() || !info->IsFlagSet(WEAPONFLAG_CANAIM)))
+ {
+ CWeapon::DoDoomAiming(shooter, fireSource, &target);
+ }
+
+ CWorld::bIncludeBikers = true;
+ ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeBikers = false;
+
+ int32 rotSpeed = 1;
+ if (m_eWeaponType == WEAPONTYPE_M4)
+ rotSpeed = 4;
+
+ CVector bulletPos;
+ if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4))
+ {
+ for (int32 i = 0; i < 16; i++)
+ CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed);
+ }
+ }
+ }
+
+ if ( shooter->IsPed() && victim)
+ {
+ if (victim == ((CPed*)shooter)->m_leader)
+ return false;
+
+ if (victim->IsPed() && ((CPed*)shooter)->IsGangMember() && !((CPed*)victim)->CanBeDamagedByThisGangMember((CPed*)shooter))
+ return false;
+ }
+
+ if (shooter->IsPed())
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000);
+ else if (shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver)
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000);
+
+ if ( shooter == FindPlayerPed() )
+ {
+ if ( !(CTimer::GetFrameCounter() & 3) )
+ MakePedsJumpAtShot((CPhysical*)shooter, fireSource, &target);
+ }
+
+ switch ( m_eWeaponType )
+ {
+ case WEAPONTYPE_M4:
+ case WEAPONTYPE_RUGER:
+ case WEAPONTYPE_M60:
+ case WEAPONTYPE_MINIGUN:
+ case WEAPONTYPE_HELICANNON:
+ {
+ static uint8 counter = 0;
+
+ if ( info->m_nFiringRate >= 50 || !(++counter & 1) )
+ {
+ AddGunFlashBigGuns(*fireSource, *fireSource + target);
+
+ CVector gunshellPos = *fireSource;
+ gunshellPos -= CVector(0.65f*ahead.x, 0.65f*ahead.y, 0.0f);
+ CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f));
+ dir.Normalise2D();
+ AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.02f);
+ }
+
+ break;
+ }
+
+ case WEAPONTYPE_UZI:
+ case WEAPONTYPE_TEC9:
+ case WEAPONTYPE_SILENCED_INGRAM:
+ case WEAPONTYPE_MP5:
+ {
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+
+ CVector gunflashPos = *fireSource;
+
+ if ( shooterMoving )
+ gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f);
+
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.07f);
+ gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.05f);
+ gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f);
+ gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f);
+ gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+ gunflashPos += CVector(0.02f*ahead.x, 0.02f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.01f);
+
+ CVector gunsmokePos = *fireSource;
+ float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*rnd, ahead.y*rnd, 0.0f));
+
+ CVector gunshellPos = *fireSource;
+ gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f);
+ CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f));
+ dir.Normalise2D();
+ AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f);
+
+ break;
+ }
+
+ case WEAPONTYPE_COLT45:
+ case WEAPONTYPE_PYTHON:
+ case WEAPONTYPE_SNIPERRIFLE:
+ case WEAPONTYPE_LASERSCOPE:
+ {
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+
+ CVector gunflashPos = *fireSource;
+
+ if ( shooterMoving )
+ gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f);
+
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f);
+ gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+
+ CVector gunsmokePos = *fireSource;
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.10f, ahead.y*0.10f, 0.0f), nil, 0.005f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.15f, ahead.y*0.15f, 0.0f), nil, 0.015f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.20f, ahead.y*0.20f, 0.0f), nil, 0.025f);
+
+ CVector gunshellPos = *fireSource;
+ gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f);
+ CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f));
+ dir.Normalise2D();
+ AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f);
+
+ break;
+ }
+ default: break;
+ }
+
+ DoBulletImpact(shooter, victim, fireSource, &target, &point, ahead);
+
+ return true;
+}
+
+void
+CWeapon::AddGunFlashBigGuns(CVector start, CVector end)
+{
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ start, CVector(0.0f, 0.0f, 0.0f), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+ CVector gunflashPos = start;
+
+ CVector shootVec = end - start;
+
+ // Wtf did you do there R*?
+ shootVec.Normalise();
+ CVector2D ahead = shootVec;
+ ahead.Normalise();
+
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.08f);
+ gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f);
+ gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f);
+
+ gunflashPos = start;
+ gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f);
+ gunflashPos.z += 0.04f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos.z += 0.04f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+ gunflashPos.z += 0.03f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+
+ gunflashPos = start;
+ gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f);
+ gunflashPos.z -= 0.04f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos.z -= 0.04f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+ gunflashPos.z -= 0.03f;
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+
+ CVector offset = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f));
+ offset.Normalise2D();
+
+ gunflashPos = start;
+ gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f);
+ gunflashPos += CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos += CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f);
+ gunflashPos += CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+
+ gunflashPos = start;
+ gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f);
+ gunflashPos -= CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f);
+ gunflashPos -= CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f);
+ gunflashPos -= CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f);
+
+ CVector gunsmokePos = start;
+ float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x * rnd, ahead.y * rnd, 0.0f));
+}
+
+void
+CWeapon::AddGunshell(CEntity *shooter, CVector const &source, CVector2D const &direction, float size)
+{
+ ASSERT(shooter!=nil);
+
+ if ( shooter == nil)
+ return;
+
+ CVector dir(direction.x*0.05f, direction.y*0.05f, CGeneral::GetRandomNumberInRange(0.02f, 0.08f));
+
+ static CVector prevEntityPosition(0.0f, 0.0f, 0.0f);
+ CVector entityPosition = shooter->GetPosition();
+
+ CVector diff = entityPosition - prevEntityPosition;
+
+ if ( Abs(diff.x)+Abs(diff.y)+Abs(diff.z) > 1.5f )
+ {
+ prevEntityPosition = entityPosition;
+
+ CParticle::AddParticle(PARTICLE_GUNSHELL_FIRST,
+ source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f));
+ }
+ else
+ {
+ CParticle::AddParticle(PARTICLE_GUNSHELL,
+ source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f));
+ }
+}
+
+
+void
+CWeapon::DoBulletImpact(CEntity *shooter, CEntity *victim,
+ CVector *source, CVector *target, CColPoint *point, CVector2D ahead)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(source!=nil);
+ ASSERT(target!=nil);
+ ASSERT(point!=nil);
+
+ CWeaponInfo *info = GetInfo();
+
+ if ( victim )
+ {
+ if (shooter)
+ {
+ if (shooter && shooter->IsPed() && ((CPed*)shooter)->m_attachedTo == victim)
+ return;
+
+ if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer())
+ {
+ CPed* shooterPed = (CPed*)shooter;
+ CEntity* guyWePointGun = shooterPed->m_pPointGunAt;
+ if (guyWePointGun)
+ {
+ if (victim != guyWePointGun)
+ {
+ float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude();
+ float distWithBullet = (point->point - shooter->GetPosition()).Magnitude();
+ if (distWithAim > 0.1f && distWithBullet > 0.1f)
+ {
+ // Normalize
+ CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim);
+ CVector bulletDir = (point->point - shooter->GetPosition()) * (1.0f / distWithBullet);
+
+ float dotProd = DotProduct(aimDir, bulletDir);
+ float aimAndBulletAngle;
+ if (dotProd <= 0.35f)
+ aimAndBulletAngle = PI;
+ else
+ aimAndBulletAngle = Acos(dotProd);
+
+ if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f)
+ {
+ shooterPed->bObstacleShowedUpDuringKillObjective = false;
+ shooterPed->m_shotTime = 0;
+ }
+ else
+ {
+ shooterPed->bObstacleShowedUpDuringKillObjective = true;
+ shooterPed->m_shootTimer = 0;
+ shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds();
+ if (distWithAim < 10.0f)
+ shooterPed->SetAttackTimer(1500);
+ else
+ shooterPed->SetAttackTimer(3000);
+ }
+ }
+ }
+ }
+ }
+ }
+ CGlass::WasGlassHitByBullet(victim, point->point);
+
+ CVector traceTarget = point->point;
+ CBulletTraces::AddTrace(source, &traceTarget, m_eWeaponType, shooter);
+
+ if (victim->IsPed() && shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver)
+ shooter = ((CVehicle*)shooter)->pDriver;
+
+ if ( victim->IsPed() && shooter->IsPed() &&
+ (((CPed*)shooter)->m_nPedType != ((CPed*)victim)->m_nPedType || ((CPed*)shooter)->m_nPedType == PEDTYPE_PLAYER2 ||
+ !((CPed*)shooter)->IsGangMember() && ((CPed*)shooter)->m_nPedType != PEDTYPE_COP))
+ {
+ CPed *victimPed = (CPed *)victim;
+ if ( !victimPed->DyingOrDead() && victim != shooter )
+ {
+ CVector pos = victimPed->GetPosition();
+
+ CVector2D posOffset(source->x-pos.x, source->y-pos.y);
+ int32 localDir = victimPed->GetLocalDirection(posOffset);
+
+ victimPed->ReactToAttack(shooter);
+
+ if ( !victimPed->IsPedInControl() || victimPed->bIsDucking )
+ {
+ victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir);
+ }
+ else
+ {
+ if ( victimPed->bCanBeShotInVehicle && (IsShotgun(m_eWeaponType) ||
+ (!victimPed->IsPlayer() && (m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_PYTHON))))
+ {
+ posOffset.Normalise();
+ victimPed->bIsStanding = false;
+
+ victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 5.0f);
+ victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false);
+
+ victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir);
+ }
+ else
+ {
+ if ( victimPed->IsPlayer() )
+ {
+ CPlayerPed *victimPlayer = (CPlayerPed *)victimPed;
+ if ( victimPlayer->m_nHitAnimDelayTimer < CTimer::GetTimeInMilliseconds() && victimPed->m_nPedState != PED_DRIVING )
+ {
+ victimPed->ClearAttackByRemovingAnim();
+
+ CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir));
+ ASSERT(asoc!=nil);
+
+ asoc->blendAmount = 0.0f;
+ asoc->blendDelta = 8.0f;
+
+ if ( m_eWeaponType == WEAPONTYPE_M4 )
+ victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 2500;
+ else
+ victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 1000;
+ }
+ }
+ else
+ {
+ victimPed->ClearAttackByRemovingAnim();
+
+ CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir));
+ ASSERT(asoc!=nil);
+
+ asoc->blendAmount = 0.0f;
+ asoc->blendDelta = 8.0f;
+ }
+
+ victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir);
+ }
+ }
+
+ if ( victimPed->m_nPedType == PEDTYPE_COP )
+ CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000);
+ else
+ CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000);
+
+ if ( CGame::nastyGame )
+ {
+ uint8 bloodAmount = 8;
+ if ( IsShotgun(m_eWeaponType) || m_eWeaponType == WEAPONTYPE_HELICANNON )
+ bloodAmount = 32;
+
+ CVector dir = (point->point - victim->GetPosition()) * 0.01f;
+ dir.z = 0.01f;
+
+ if ( victimPed->GetIsOnScreen() )
+ {
+ for ( uint8 i = 0; i < bloodAmount; i++ )
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point, dir);
+ }
+
+ if (m_eWeaponType == WEAPONTYPE_MINIGUN)
+ {
+ CParticle::AddParticle(PARTICLE_TEST, point->point, CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0);
+ CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(0.2f, -0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0);
+ CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(-0.2f, 0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ if ( CGame::nastyGame )
+ {
+ CVector dir = (point->point - victim->GetPosition()) * 0.01f;
+ dir.z = 0.01f;
+
+ if ( victim->GetIsOnScreen() )
+ {
+ for ( int32 i = 0; i < 8; i++ )
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point + CVector(0.0f, 0.0f, 0.15f), dir);
+ }
+
+ if ( victimPed->Dead() )
+ {
+ CAnimBlendAssociation *asoc;
+ if ( RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL) )
+ asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f);
+ else
+ asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f);
+
+ if ( asoc )
+ {
+ asoc->SetCurrentTime(0.0f);
+ asoc->flags |= ASSOC_RUNNING;
+ asoc->flags &= ~ASSOC_FADEOUTWHENDONE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_BUILDING:
+ {
+ for ( int32 i = 0; i < 16; i++ )
+ CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f);
+
+#ifndef FIX_BUGS
+ CVector dist = point->point - (*source);
+ float distMagnitude = dist.Magnitude();
+ CVector smokePos = point->point - Max(distMagnitude / 10.0f, 0.2f) * dist / distMagnitude;
+#else
+ CVector smokePos = point->point;
+#endif // !FIX_BUGS
+
+ smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+ smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+ smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f));
+
+ break;
+ }
+ case ENTITY_TYPE_VEHICLE:
+ {
+ if (point->pieceB >= CAR_PIECE_WHEEL_LF && point->pieceB <= CAR_PIECE_WHEEL_RR) {
+ ((CVehicle*)victim)->BurstTyre(point->pieceB, true);
+
+ for (int32 i = 0; i < 4; i++)
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point->point, point->normal * 0.05f);
+ }
+ else
+ {
+ ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage);
+
+ for (int32 i = 0; i < 16; i++)
+ CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal * 0.05f);
+
+#ifndef FIX_BUGS
+ CVector dist = point->point - (*source);
+ CVector offset = dist - Max(0.2f * dist.Magnitude(), 0.5f) * CVector(ahead.x, ahead.y, 0.0f);
+ CVector smokePos = *source + offset;
+#else
+ CVector smokePos = point->point;
+#endif
+
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f));
+ }
+
+
+ if ( shooter->IsPed() )
+ {
+ CPed *shooterPed = (CPed *)shooter;
+
+ if ( shooterPed->bNotAllowedToDuck )
+ {
+ if ( shooterPed->bKindaStayInSamePlace && victim != shooterPed->m_pPointGunAt )
+ {
+ shooterPed->bKindaStayInSamePlace = false;
+ shooterPed->m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + 15000;
+ }
+ }
+ }
+
+ break;
+ }
+ case ENTITY_TYPE_OBJECT:
+ {
+ for ( int32 i = 0; i < 8; i++ )
+ CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f);
+
+ CObject *victimObject = (CObject *)victim;
+
+ if ( !victimObject->bInfiniteMass && victimObject->m_fCollisionDamageMultiplier < 99.9f)
+ {
+ bool notStatic = !victimObject->GetIsStatic();
+ if (notStatic && victimObject->m_fUprootLimit <= 0.0f)
+ {
+ victimObject->SetIsStatic(false);
+ victimObject->AddToMovingList();
+ }
+
+ notStatic = !victimObject->GetIsStatic();
+ if (!notStatic)
+ {
+ CVector moveForce = point->normal * -4.0f;
+ victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z);
+ }
+ } else if (victimObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) {
+ victimObject->ObjectDamage(50.f);
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_BUILDING:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point->point);
+ break;
+ }
+ case ENTITY_TYPE_VEHICLE:
+ {
+ CStats::BulletsThatHit++;
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f);
+ break;
+ }
+ case ENTITY_TYPE_PED:
+ {
+ CStats::BulletsThatHit++;
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f);
+ ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT);
+ break;
+ }
+ case ENTITY_TYPE_OBJECT:
+ {
+ CStats::BulletsThatHit++;
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point->point);
+ break;
+ }
+ case ENTITY_TYPE_DUMMY:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point->point);
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ CBulletTraces::AddTrace(source, target, m_eWeaponType, shooter);
+
+ if ( shooter == FindPlayerPed() )
+ CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z);
+
+ BlowUpExplosiveThings(victim);
+}
+
+
+bool
+CWeapon::FireShotgun(CEntity *shooter, CVector *fireSource)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(fireSource!=nil);
+
+ CWeaponInfo *info = GetInfo();
+
+ float heading = RADTODEG(shooter->GetForward().Heading());
+ float angle = DEGTORAD(heading);
+
+ CVector2D rotOffset(-Sin(angle), Cos(angle));
+ rotOffset.Normalise();
+
+ CVector gunflashPos = *fireSource;
+ gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f);
+ gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.15f);
+ gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f);
+ CParticle::AddParticle(PARTICLE_GUNFLASH, *fireSource, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f);
+
+ CVector gunsmokePos = *fireSource;
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.10f, rotOffset.y*0.10f, 0.0f), nil, 0.1f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.15f, rotOffset.y*0.15f, 0.0f), nil, 0.1f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.20f, rotOffset.y*0.20f, 0.0f), nil, 0.1f);
+ CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.25f, rotOffset.y*0.25f, 0.0f), nil, 0.1f);
+
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000);
+
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, *fireSource, CVector(0.0, 0.0, 0.0), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+
+ float shooterAngle;
+
+ if ( shooter->IsPed() && ((CPed*)shooter)->m_pPointGunAt != nil )
+ {
+ CEntity *threatAttack = ((CPed*)shooter)->m_pPointGunAt;
+ shooterAngle = CGeneral::GetAngleBetweenPoints(threatAttack->GetPosition().x, threatAttack->GetPosition().y,
+ (*fireSource).x, (*fireSource).y);
+ }
+ else
+ shooterAngle = RADTODEG(shooter->GetForward().Heading());
+
+ int shootsAtOnce;
+ int checkObstacleOnShootNo;
+ float angleRange;
+ switch (m_eWeaponType) {
+ case WEAPONTYPE_SHOTGUN:
+ angleRange = DEGTORAD(9.0f);
+ checkObstacleOnShootNo = 1;
+ shootsAtOnce = 3;
+ break;
+ case WEAPONTYPE_SPAS12_SHOTGUN:
+ angleRange = DEGTORAD(6.0f);
+ checkObstacleOnShootNo = 1;
+ shootsAtOnce = 3;
+ break;
+ case WEAPONTYPE_STUBBY_SHOTGUN:
+ angleRange = DEGTORAD(18.0f);
+ checkObstacleOnShootNo = 2;
+ shootsAtOnce = 5;
+ break;
+ default:
+ break;
+ }
+ bool statUpdated = false;
+ float halfAngleRange = angleRange / 2.f;
+ float angleBetweenTwoShot = angleRange / (shootsAtOnce - 1.f);
+
+ for ( int32 i = 0; i < shootsAtOnce; i++ )
+ {
+ float shootAngle = DEGTORAD(RADTODEG(halfAngleRange - angleBetweenTwoShot * i) + shooterAngle);
+ CVector2D shootRot(-Sin(shootAngle), Cos(shootAngle));
+ shootRot.Normalise();
+
+ CVector source, target;
+ CColPoint point;
+ CEntity *victim;
+
+ if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() )
+ {
+ TheCamera.Find3rdPersonCamTargetVector(1.0f, *fireSource, source, target);
+ CVector Left = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Front, TheCamera.Cams[TheCamera.ActiveCam].Up);
+
+ float f = (i - (shootsAtOnce / 2)) * angleBetweenTwoShot;
+ target = f * Left + target - source;
+ target *= info->m_fRange;
+ target += source;
+ CWorld::bIncludeCarTyres = true;
+ CWorld::bIncludeBikers = true;
+ CWorld::bIncludeDeadPeds = true;
+ ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeDeadPeds = false;
+ CWorld::bIncludeCarTyres = false;
+ }
+ else
+ {
+ target = *fireSource;
+ target.x += shootRot.x * info->m_fRange;
+ target.y += shootRot.y * info->m_fRange;
+
+ if ( shooter->IsPed() )
+ {
+ CPed *shooterPed = (CPed *)shooter;
+
+ if ( shooterPed->m_pPointGunAt == nil )
+ DoDoomAiming(shooter, fireSource, &target);
+ else
+ {
+ CVector pos;
+ if (shooterPed->m_pPointGunAt->IsPed()) {
+ ((CPed*)shooterPed->m_pPointGunAt)->m_pedIK.GetComponentPosition(pos, PED_MID);
+ } else {
+ pos = ((CPed*)shooterPed->m_pPointGunAt)->GetPosition();
+ }
+
+ float distToTarget = (pos - (*fireSource)).Magnitude2D();
+ target.z += info->m_fRange / distToTarget * (pos.z - target.z);
+ }
+ }
+ if (shooter == FindPlayerPed())
+ CWorld::bIncludeDeadPeds = true;
+
+ CWorld::bIncludeBikers = true;
+ ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false);
+ CWorld::bIncludeDeadPeds = false;
+ }
+ CWorld::bIncludeBikers = false;
+
+ if ( victim )
+ {
+ CGlass::WasGlassHitByBullet(victim, point.point);
+ CWeapon::BlowUpExplosiveThings(victim);
+ if (i == checkObstacleOnShootNo)
+ {
+ if (shooter)
+ {
+ if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer())
+ {
+ CPed *shooterPed = (CPed*)shooter;
+ CEntity *guyWePointGun = shooterPed->m_pPointGunAt;
+ if (guyWePointGun)
+ {
+ if (victim != guyWePointGun)
+ {
+ float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude();
+ float distWithBullet = (point.point - shooter->GetPosition()).Magnitude();
+ if (distWithAim > 0.1f && distWithBullet > 0.1f)
+ {
+ // Normalize
+ CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim);
+ CVector bulletDir = (point.point - shooter->GetPosition()) * (1.0f / distWithBullet);
+
+ float dotProd = DotProduct(aimDir, bulletDir);
+ float aimAndBulletAngle;
+ if (dotProd <= 0.35f)
+ aimAndBulletAngle = PI;
+ else
+ aimAndBulletAngle = Acos(dotProd);
+
+ if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f)
+ {
+ shooterPed->bObstacleShowedUpDuringKillObjective = false;
+ shooterPed->m_shotTime = 0;
+ }
+ else
+ {
+ shooterPed->bObstacleShowedUpDuringKillObjective = true;
+ shooterPed->m_shootTimer = 0;
+ shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds();
+ if (distWithAim < 10.0f)
+ shooterPed->SetAttackTimer(1500);
+ else
+ shooterPed->SetAttackTimer(3000);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ CBulletTraces::AddTrace(fireSource, &point.point, m_eWeaponType, shooter);
+
+ if ( victim->IsPed() )
+ {
+ CPed *victimPed = (CPed *)victim;
+ if ( !victimPed->DyingOrDead() && victim != shooter )
+ {
+ bool cantStandup = true;
+
+ CVector pos = victimPed->GetPosition();
+
+ CVector2D posOffset((*fireSource).x-pos.x, (*fireSource).y-pos.y);
+ int32 localDir = victimPed->GetLocalDirection(posOffset);
+
+ victimPed->ReactToAttack(FindPlayerPed());
+
+ posOffset.Normalise();
+
+ if ( victimPed->m_getUpTimer > (CTimer::GetTimeInMilliseconds() - 3000) ||
+ !victimPed->bCanBeShotInVehicle)
+ cantStandup = false;
+
+ if ( victimPed->bIsStanding && cantStandup )
+ {
+ victimPed->bIsStanding = false;
+
+ victimPed->ApplyMoveForce(posOffset.x*-6.0f, posOffset.y*-6.0f, 5.0f);
+ }
+ else
+ victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 0.0f);
+
+ if ( cantStandup )
+ victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false);
+
+ victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir);
+
+ if ( victimPed->m_nPedType == PEDTYPE_COP )
+ CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000);
+ else
+ CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000);
+
+ if ( CGame::nastyGame )
+ {
+ uint8 bloodAmount = 8;
+ if ( IsShotgun(m_eWeaponType) )
+ bloodAmount = 32;
+
+ CVector dir = (point.point - victim->GetPosition()) * 0.01f;
+ dir.z = 0.01f;
+
+ if ( victimPed->GetIsOnScreen() )
+ {
+ for ( uint8 i = 0; i < bloodAmount; i++ )
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point, dir);
+ }
+ }
+ } else {
+ if (CGame::nastyGame)
+ {
+ CVector dir = (point.point - victim->GetPosition()) * 0.01f;
+ dir.z = 0.01f;
+
+ if (victimPed->GetIsOnScreen())
+ {
+ for (uint8 i = 0; i < 8; i++)
+ CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + CVector(0.0f, 0.0f, 0.15f), dir);
+ }
+ if (victimPed->Dead())
+ {
+ CAnimBlendAssociation *hitAssoc;
+ if (RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL))
+ {
+ hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f);
+ }
+ else
+ {
+ hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f);
+ }
+ if (hitAssoc)
+ {
+ hitAssoc->SetCurrentTime(0.0f);
+ hitAssoc->SetRun();
+ hitAssoc->flags &= ~ASSOC_DELETEFADEDOUT;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_VEHICLE:
+ {
+ if (point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR) {
+ ((CVehicle*)victim)->BurstTyre(point.pieceB, true);
+
+ for (int32 i = 0; i < 4; i++)
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point.point, point.normal * 0.05f);
+ }
+ else
+ {
+ ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage);
+
+ for (int32 i = 0; i < 16; i++)
+ CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal * 0.05f);
+
+#ifndef FIX_BUGS
+ CVector dist = point.point - (*fireSource);
+ CVector offset = dist - Max(0.2f * dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f);
+ CVector smokePos = *fireSource + offset;
+#else
+ CVector smokePos = point.point;
+#endif
+
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f));
+ }
+ break;
+ }
+
+ case ENTITY_TYPE_BUILDING:
+ case ENTITY_TYPE_OBJECT:
+ {
+ for ( int32 i = 0; i < 16; i++ )
+ CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal*0.05f);
+
+#ifndef FIX_BUGS
+ CVector dist = point.point - (*fireSource);
+ CVector offset = dist - Max(0.2f*dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f);
+ CVector smokePos = *fireSource + offset;
+#else
+ CVector smokePos = point.point;
+#endif
+
+ smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+ smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+ smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f);
+
+ CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f));
+
+ if ( victim->IsObject() )
+ {
+ CObject *victimObject = (CObject *)victim;
+
+ if ( !victimObject->bInfiniteMass )
+ {
+ bool notStatic = !victimObject->GetIsStatic();
+ if ( notStatic && victimObject->m_fUprootLimit <= 0.0f )
+ {
+ victimObject->SetIsStatic(false);
+ victimObject->AddToMovingList();
+ }
+
+ notStatic = !victimObject->GetIsStatic();
+ if ( !notStatic )
+ {
+ CVector moveForce = point.normal*-5.0f;
+ victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z);
+ }
+ }
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_BUILDING:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point);
+ break;
+ }
+ case ENTITY_TYPE_VEHICLE:
+ {
+ if (!statUpdated) {
+ CStats::BulletsThatHit++;
+ statUpdated = true;
+ }
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f);
+ break;
+ }
+ case ENTITY_TYPE_PED:
+ {
+ if (!statUpdated) {
+ CStats::BulletsThatHit++;
+ statUpdated = true;
+ }
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f);
+ ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT);
+ break;
+ }
+ case ENTITY_TYPE_OBJECT:
+ {
+ if (!statUpdated) {
+ CStats::BulletsThatHit++;
+ statUpdated = true;
+ }
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point);
+ break;
+ }
+ case ENTITY_TYPE_DUMMY:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point);
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ {
+ CVector traceTarget = *fireSource;
+ traceTarget += (target - (*fireSource)) * Min(info->m_fRange, 30.0f) / info->m_fRange;
+ CBulletTraces::AddTrace(fireSource, &traceTarget, m_eWeaponType, shooter);
+ }
+ }
+
+ if ( shooter == FindPlayerPed() )
+ CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z);
+
+ return true;
+}
+
+bool
+CWeapon::FireProjectile(CEntity *shooter, CVector *fireSource, float power)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(fireSource!=nil);
+
+ CVector source, target;
+ eWeaponType projectileType = m_eWeaponType;
+
+ if ( m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER )
+ {
+ source = *fireSource;
+ projectileType = WEAPONTYPE_ROCKET;
+
+ if ( shooter->IsPed() && ((CPed*)shooter)->IsPlayer() )
+ {
+ int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
+ if (!( mode == CCam::MODE_M16_1STPERSON
+ || mode == CCam::MODE_SNIPER
+ || mode == CCam::MODE_ROCKETLAUNCHER
+ || mode == CCam::MODE_M16_1STPERSON_RUNABOUT
+ || mode == CCam::MODE_SNIPER_RUNABOUT
+ || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) )
+ {
+ return false;
+ }
+
+ *fireSource += TheCamera.Cams[TheCamera.ActiveCam].Front;
+ }
+ else
+ *fireSource += shooter->GetForward();
+
+ target = *fireSource;
+ }
+ else
+ {
+ float dot = DotProduct(*fireSource-shooter->GetPosition(), shooter->GetForward());
+
+ if ( dot < 0.3f )
+ *fireSource += (0.3f-dot) * shooter->GetForward();
+
+ target = *fireSource;
+
+ if ( target.z - shooter->GetPosition().z > 0.0f )
+ target += 0.6f*shooter->GetForward();
+
+ source = *fireSource - shooter->GetPosition();
+
+ source = *fireSource - DotProduct(source, shooter->GetForward()) * shooter->GetForward();
+ }
+
+ if ( !CWorld::GetIsLineOfSightClear(source, target, true, true, false, true, false, false, false) )
+ {
+ if ( m_eWeaponType != WEAPONTYPE_GRENADE )
+ CProjectileInfo::RemoveNotAdd(shooter, projectileType, *fireSource);
+ else
+ {
+ if ( shooter->IsPed() )
+ {
+ source = shooter->GetPosition() - shooter->GetForward();
+ source.z -= 0.4f;
+
+ if ( !CWorld::TestSphereAgainstWorld(source, 0.5f, nil, false, false, true, false, false, false) )
+ CProjectileInfo::AddProjectile(shooter, WEAPONTYPE_GRENADE, source, 0.0f);
+ else
+ CProjectileInfo::RemoveNotAdd(shooter, WEAPONTYPE_GRENADE, *fireSource);
+ }
+ }
+ }
+ else
+ CProjectileInfo::AddProjectile(shooter, projectileType, *fireSource, power);
+
+
+ CWorld::pIgnoreEntity = nil;
+
+ if ( shooter->IsPed() )
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed *)shooter, 1000);
+ else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver )
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000);
+
+ return true;
+}
+
+void
+CWeapon::GenerateFlameThrowerParticles(CVector pos, CVector dir)
+{
+ dir *= 0.7f;
+ CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir);
+
+ dir *= 0.7f;
+ CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir);
+
+ dir *= 0.7f;
+ CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir);
+
+ dir *= 0.7f;
+ CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir);
+
+ dir *= 0.7f;
+ CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir);
+}
+
+bool
+CWeapon::FireAreaEffect(CEntity *shooter, CVector *fireSource)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(fireSource!=nil);
+
+ CWeaponInfo *info = GetInfo();
+
+ float heading = RADTODEG(shooter->GetForward().Heading());
+
+ CVector source;
+ CVector target;
+ CVector dir;
+
+ if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() )
+ {
+ TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target);
+ float norm = (1.0f / info->m_fRange);
+ dir = (target - source) * norm;
+ }
+ else
+ {
+ float angle = DEGTORAD(heading);
+ dir = CVector(-Sin(angle)*0.5f, Cos(angle)*0.5f, 0.0f);
+ target = *fireSource + dir;
+ }
+
+ CShotInfo::AddShot(shooter, m_eWeaponType, *fireSource, target);
+ CWeapon::GenerateFlameThrowerParticles(*fireSource, dir);
+
+ if ( shooter == (CEntity *)FindPlayerPed() )
+ {
+ for ( int32 i = 0; i < FindPlayerPed()->m_numNearPeds; i++ )
+ {
+ if ( FindPlayerPed()->m_nearPeds[i]->CharCreatedBy == RANDOM_CHAR )
+ {
+ if ( FindPlayerPed()->m_nearPeds[i]->IsPedInControl() && FindPlayerPed()->m_nearPeds[i]->m_nPedState != PED_FLEE_ENTITY )
+ FindPlayerPed()->m_nearPeds[i]->SetFlee(shooter, 10000);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+CWeapon::LaserScopeDot(CVector *pOutPos, float *pOutSize)
+{
+ CWeaponInfo *info = GetInfo();
+
+ float range = info->m_fRange;
+
+ CVector source, target;
+ CEntity *foundEnt = nil;
+ CColPoint foundCol;
+
+ source = 0.5f * TheCamera.Cams[TheCamera.ActiveCam].Front + TheCamera.Cams[TheCamera.ActiveCam].Source;
+ target = TheCamera.Cams[TheCamera.ActiveCam].Front;
+ target.Normalise();
+ target *= range;
+ target += source;
+
+ if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, false, false, false) )
+ {
+ CVector pos = foundCol.point;
+ float w, h;
+
+ if ( CSprite::CalcScreenCoors(foundCol.point, &pos, &w, &h, true) )
+ {
+ *pOutPos = pos;
+ *pOutSize = w * 0.05f;
+
+ CCoronas::RegisterCorona((uintptr)this + 7, 128, 0, 0, 255, pos, 1.2f, 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+CWeapon::FireSniper(CEntity *shooter)
+{
+ ASSERT(shooter!=nil);
+
+ if ( (CEntity *)FindPlayerPed() == shooter )
+ {
+ int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
+ if (!( mode == CCam::MODE_M16_1STPERSON
+ || mode == CCam::MODE_SNIPER
+ || mode == CCam::MODE_CAMERA
+ || mode == CCam::MODE_ROCKETLAUNCHER
+ || mode == CCam::MODE_M16_1STPERSON_RUNABOUT
+ || mode == CCam::MODE_SNIPER_RUNABOUT
+ || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) )
+ {
+ return false;
+ }
+ }
+
+#ifdef SECUROM
+ if (sniperPirateCheck){
+ // if not pirated game
+ // sniperPirateCheck = 0;
+ }
+#endif
+
+#ifndef FIX_BUGS
+ CWeaponInfo *info = GetInfo(); //unused
+#endif
+
+ CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam];
+ ASSERT(cam!=nil);
+
+ CVector source = cam->Source;
+ CVector dir = cam->Front;
+
+ if ( DotProduct(dir, CVector(0.0f, -0.9894f, 0.145f)) > 0.997f )
+ CCoronas::MoonSize = (CCoronas::MoonSize+1) & 7;
+
+ dir.Normalise();
+ dir *= 16.0f;
+
+#ifdef SECUROM
+ if (sniperPirateCheck) return true;
+#endif
+
+ CBulletInfo::AddBullet(shooter, m_eWeaponType, source, dir);
+
+ if ( shooter == FindPlayerPed() )
+ {
+ CPad::GetPad(0)->StartShake_Distance(240, 128,
+ FindPlayerPed()->GetPosition().x,
+ FindPlayerPed()->GetPosition().y,
+ FindPlayerPed()->GetPosition().z);
+
+ CParticle::HandleShootableBirdsStuff(shooter, source);
+
+ CamShakeNoPos(&TheCamera, 0.2f);
+ }
+
+ if ( shooter->IsPed() )
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000);
+ else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver )
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000);
+
+ return true;
+}
+
+bool
+CWeapon::TakePhotograph(CEntity *shooter)
+{
+ if ( TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA )
+ {
+ CSpecialFX::bSnapShotActive = true;
+ CSpecialFX::SnapShotFrames = 0;
+ CStats::PhotosTaken++;
+ bPhotographHasBeenTaken = true;
+
+ for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--)
+ {
+ CPed *ped = CPools::GetPedPool()->GetSlot(i);
+ if ( ped )
+ {
+ if ( (ped->GetPosition() - TheCamera.GetPosition()).Magnitude() < 125.0f )
+ {
+ CVector pedPos = ped->GetPosition();
+ pedPos.z += 0.8f;
+
+ CVector pos;
+ float w, h;
+
+ if ( CSprite::CalcScreenCoors(pedPos, &pos, &w, &h, false) )
+ {
+ if ( SCREEN_WIDTH * 0.1f < pos.x && SCREEN_WIDTH * 0.9f > pos.x
+ && SCREEN_HEIGHT * 0.1f < pos.y && SCREEN_HEIGHT * 0.9f > pos.y )
+ {
+ CVector source, target;
+ CEntity *foundEnt = nil;
+ CColPoint foundCol;
+
+ target = pedPos;
+ source = TheCamera.GetForward() * 2.0f + TheCamera.GetPosition();
+
+ if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, true, true, false) )
+ {
+ if ( foundEnt != (CEntity*)ped )
+ continue;
+ }
+
+ ped->bHasBeenPhotographed = true;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+CWeapon::FireM16_1stPerson(CEntity *shooter)
+{
+ ASSERT(shooter!=nil);
+
+ int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
+
+ if (!( mode == CCam::MODE_M16_1STPERSON
+ || mode == CCam::MODE_SNIPER
+ || mode == CCam::MODE_CAMERA
+ || mode == CCam::MODE_ROCKETLAUNCHER
+ || mode == CCam::MODE_M16_1STPERSON_RUNABOUT
+ || mode == CCam::MODE_SNIPER_RUNABOUT
+ || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT
+ || mode == CCam::MODE_HELICANNON_1STPERSON) )
+ {
+ return false;
+ }
+
+ CWeaponInfo *info = GetInfo();
+
+ CWorld::bIncludeCarTyres = true;
+ CWorld::bIncludeBikers = true;
+
+ CColPoint point;
+ CEntity *victim;
+
+ CWorld::pIgnoreEntity = shooter;
+ CWorld::bIncludeDeadPeds = true;
+
+ CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam];
+ ASSERT(cam!=nil);
+
+ CVector source = cam->Source;
+ CVector target = cam->Front*info->m_fRange + source;
+
+ if (ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false)) {
+ CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target);
+ }
+ CWorld::pIgnoreEntity = nil;
+ CWorld::bIncludeDeadPeds = false;
+ CWorld::bIncludeBikers = false;
+ CWorld::bIncludeCarTyres = false;
+
+ CVector2D front(cam->Front.x, cam->Front.y);
+ front.Normalise();
+
+ DoBulletImpact(shooter, victim, &source, &target, &point, front);
+
+ CVector bulletPos;
+
+ if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, (m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_HELICANNON ? 20 : 4)) )
+ {
+ for ( int32 i = 0; i < 16; i++ )
+ CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f));
+ }
+
+ if ( shooter == FindPlayerPed() )
+ {
+ float mult;
+ switch (m_eWeaponType) {
+ case WEAPONTYPE_M4:
+ mult = 0.0003f;
+ break;
+ case WEAPONTYPE_RUGER:
+ mult = 0.00015f;
+ break;
+ case WEAPONTYPE_HELICANNON:
+ case WEAPONTYPE_M60:
+ mult = 0.0003f;
+ break;
+ default:
+ mult = 0.0002f;
+ break;
+ }
+
+ if (FindPlayerPed()->bIsDucking || FindPlayerPed()->m_attachedTo)
+ mult *= 0.3f;
+
+ TheCamera.Cams[TheCamera.ActiveCam].Beta += float((CGeneral::GetRandomNumber() & 127) - 64) * mult;
+ TheCamera.Cams[TheCamera.ActiveCam].Alpha += float((CGeneral::GetRandomNumber() & 127) - 64) * mult;
+
+ // yes, double
+ double notFiringRate = (20.0 - info->m_nFiringRate) / 80.0;
+ double raisedNotFiringRate = Max(1.0, Max(0.0, notFiringRate));
+
+ uint8 shakeFreq = 80.0 * raisedNotFiringRate + 130.0;
+ CPad::GetPad(0)->StartShake(20000.0f * CTimer::GetTimeStep() / shakeFreq, shakeFreq);
+ }
+
+ return true;
+}
+
+bool
+CWeapon::FireInstantHitFromCar(CVehicle *shooter, bool left, bool right)
+{
+ CWeaponInfo *info = GetInfo();
+
+ CVehicleModelInfo *modelInfo = shooter->GetModelInfo();
+
+ CVector source, target;
+
+ if ( shooter->IsBike() )
+ {
+ if ( shooter->pDriver )
+ {
+ source = info->m_vecFireOffset;
+
+ shooter->pDriver->TransformToNode(source, PED_HANDR);
+ source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed;
+
+ if ( left )
+ target = source - info->m_fRange * shooter->GetRight();
+ else if ( right )
+ target = source + info->m_fRange * shooter->GetRight();
+ else
+ target = source + info->m_fRange * shooter->GetForward();
+
+ }
+ else if ( left )
+ {
+ source = shooter->GetMatrix() * CVector(-shooter->GetColModel()->boundingBox.max.x + -0.25f,
+ float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.05f,
+ modelInfo->GetFrontSeatPosn().z + 0.63f);
+ source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed;
+
+
+ target = shooter->GetMatrix() * CVector(-info->m_fRange,
+ modelInfo->GetFrontSeatPosn().y,
+ modelInfo->GetFrontSeatPosn().z + 0.6f);
+ }
+ else if ( right )
+ {
+ source = shooter->GetMatrix() * CVector(shooter->GetColModel()->boundingBox.max.x + 0.25f,
+ float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.18f,
+ modelInfo->GetFrontSeatPosn().z + 0.52f);
+ source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed;
+
+ target = shooter->GetMatrix() * CVector(info->m_fRange,
+ modelInfo->GetFrontSeatPosn().y,
+ modelInfo->GetFrontSeatPosn().z + 0.5f);
+ }
+ else
+ {
+ source = shooter->GetMatrix() * CVector(float(CGeneral::GetRandomNumber() & 255) * 0.001f + -0.4f,
+ modelInfo->GetFrontSeatPosn().y + shooter->GetColModel()->boundingBox.max.y + 0.2f,
+ modelInfo->GetFrontSeatPosn().z + 0.55f);
+ source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed;
+
+ target = shooter->GetMatrix() * CVector(0.0f,
+ info->m_fRange,
+ modelInfo->GetFrontSeatPosn().z + 0.5f);
+ }
+ }
+ else
+ {
+ if ( left )
+ source = info->m_vecFireOffset;
+ else
+ {
+ source = 1.8f * info->m_vecFireOffset;
+ source.z -= 0.1f;
+ }
+
+ shooter->pDriver->TransformToNode(source, PED_HANDR);
+ source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed;
+
+ if ( left )
+ target = source - info->m_fRange * shooter->GetRight();
+ else
+ target = source + info->m_fRange * shooter->GetRight();
+ }
+
+ target += CVector(float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f,
+ float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f,
+ float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f);
+
+ DoDriveByAutoAiming(FindPlayerPed(), shooter, &source, &target);
+
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000);
+
+ if ( !TheCamera.GetLookingLRBFirstPerson() )
+ {
+ if ( !shooter->IsBike() )
+ CParticle::AddParticle(PARTICLE_GUNFLASH, source, CVector(0.0f, 0.0f, 0.0f));
+ else
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.4f*shooter->m_vecMoveSpeed);
+ }
+ else
+ CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.6f*shooter->m_vecMoveSpeed, nil, 0.18f);
+
+ CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, FindPlayerPed(), 1000);
+
+ CPointLights::AddLight(CPointLights::LIGHT_POINT, source, CVector(0.0f, 0.0f, 0.0f), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+
+ CColPoint point;
+ CEntity *victim;
+
+ CWorld::bIncludeBikers = true;
+ CWorld::pIgnoreEntity = shooter;
+ ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false);
+ CWorld::pIgnoreEntity = NULL;
+ CWorld::bIncludeBikers = false;
+
+ if ( !(CTimer::GetFrameCounter() & 3) )
+ MakePedsJumpAtShot(shooter, &source, &target);
+
+ if ( victim )
+ {
+ CVector traceTarget = point.point;
+ CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter);
+
+ if ( victim->IsPed() )
+ {
+ CPed *victimPed = (CPed*)victim;
+
+ if ( !victimPed->DyingOrDead() && victim != (CEntity *)shooter )
+ {
+ CVector pos = victimPed->GetPosition();
+
+ CVector2D posOffset(source.x-pos.x, source.y-pos.y);
+ int32 localDir = victimPed->GetLocalDirection(posOffset);
+
+ victimPed->ReactToAttack(FindPlayerPed());
+ victimPed->ClearAttackByRemovingAnim();
+
+ CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir));
+ ASSERT(asoc!=nil);
+ asoc->blendAmount = 0.0f;
+ asoc->blendDelta = 8.0f;
+
+ victimPed->InflictDamage(shooter, WEAPONTYPE_UZI_DRIVEBY, 3*info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir);
+
+ pos.z += 0.8f;
+
+ if ( victimPed->GetIsOnScreen() )
+ {
+ if ( CGame::nastyGame )
+ {
+ for ( int32 i = 0; i < 4; i++ )
+ {
+ CVector dir;
+ dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+ dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+ dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+
+ CParticle::AddParticle(PARTICLE_BLOOD, pos, dir);
+ }
+ }
+ }
+
+ if ( victimPed->m_nPedType == PEDTYPE_COP )
+ CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000);
+ else
+ CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000);
+ }
+ }
+ else if ( victim->IsVehicle() )
+ ((CVehicle *)victim)->InflictDamage(FindPlayerPed(), WEAPONTYPE_UZI_DRIVEBY, info->m_nDamage);
+ else
+ CGlass::WasGlassHitByBullet(victim, point.point);
+
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_BUILDING:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point);
+ break;
+ }
+ case ENTITY_TYPE_VEHICLE:
+ {
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f);
+ break;
+ }
+ case ENTITY_TYPE_PED:
+ {
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f);
+ ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT);
+ break;
+ }
+ case ENTITY_TYPE_OBJECT:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point);
+ break;
+ }
+ case ENTITY_TYPE_DUMMY:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point);
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ {
+ float norm = 30.0f/info->m_fRange;
+ CVector traceTarget = (target-source)*norm + source;
+ CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter);
+ }
+
+ if ( shooter == FindPlayerVehicle() )
+ CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y, FindPlayerVehicle()->GetPosition().z);
+
+ return true;
+}
+
+void
+CWeapon::DoDoomAiming(CEntity *shooter, CVector *source, CVector *target)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(source!=nil);
+ ASSERT(target !=nil);
+
+#ifndef FIX_BUGS
+ CEntity entity; // unused
+#endif
+
+ CPed *shooterPed = (CPed*)shooter;
+
+ int16 lastEntity;
+ CEntity *entities[16];
+ CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, true, false, false);
+
+ float closestEntityDist = 10000.0f;
+ int16 closestEntity;
+
+ for ( int32 i = 0; i < lastEntity; i++ )
+ {
+ CEntity *victim = entities[i];
+ ASSERT(victim!=nil);
+
+ if ( (CEntity*)shooterPed != victim && shooterPed->CanSeeEntity(victim, DEGTORAD(22.5f)) )
+ {
+ if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING
+ || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING
+ || victim->GetStatus() == STATUS_HELI
+ || victim->GetStatus() == STATUS_PLANE
+ || victim->GetStatus() == STATUS_WRECKED) )
+ {
+ float distToVictim = (shooterPed->GetPosition()-victim->GetPosition()).Magnitude2D();
+ float distToVictimZ = Abs(shooterPed->GetPosition().z-victim->GetPosition().z);
+
+ if ( 1.5f*distToVictimZ < distToVictim )
+ {
+ float entityDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ));
+
+ if ( entityDist < closestEntityDist )
+ {
+ closestEntityDist = entityDist;
+ closestEntity = i;
+ }
+ }
+ }
+ }
+ }
+
+ CColPoint foundCol;
+ CEntity *foundEnt;
+ if (closestEntityDist < DOOMAUTOAIMING_MAXDIST
+ && !CWorld::ProcessLineOfSight(*source, entities[closestEntity]->GetPosition(), foundCol, foundEnt, true, false, false, false, false, false, false, true))
+ {
+ CEntity *victim = entities[closestEntity];
+ ASSERT(victim !=nil);
+
+ float distToTarget = (*target - *source).Magnitude2D();
+ float distToSource = (victim->GetPosition() - *source).Magnitude2D();
+
+ float victimZ = victim->GetPosition().z + 0.3f;
+ if ( victim->IsPed() )
+ {
+ if ( ((CPed*)victim)->bIsDucking )
+ victimZ -= 0.8f;
+ }
+
+ (*target).z = (distToTarget / distToSource) * (victimZ - (*source).z) + (*source).z;
+ }
+}
+
+void
+CWeapon::DoTankDoomAiming(CEntity *shooter, CEntity *driver, CVector *source, CVector *target)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(driver!=nil);
+ ASSERT(source!=nil);
+ ASSERT(target!=nil);
+
+#ifndef FIX_BUGS
+ CEntity entity; // unused
+#endif
+
+ int16 lastEntity;
+ CEntity *entities[16];
+ CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, false, false, false);
+
+ float closestEntityDist = 10000.0f;
+ int16 closestEntity;
+
+ float normZ = (target->z - source->z) / (*target-*source).Magnitude();
+
+ for ( int32 i = 0; i < lastEntity; i++ )
+ {
+ CEntity *victim = entities[i];
+
+ ASSERT(victim!=nil);
+
+ if ( shooter != victim && driver != victim )
+ {
+ if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING
+ || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING
+ || victim->GetStatus() == STATUS_HELI
+ || victim->GetStatus() == STATUS_PLANE) )
+ {
+ if ( !(victim->IsVehicle() && victim->bRenderScorched) )
+ {
+ float distToVictim = (shooter->GetPosition()-victim->GetPosition()).Magnitude2D();
+ float distToVictimZ = Abs(shooter->GetPosition().z - (distToVictim*normZ + victim->GetPosition().z));
+
+ if ( 3.0f*distToVictimZ < distToVictim )
+ {
+ CVector tmp = CVector(victim->GetPosition().x, victim->GetPosition().y, 0.0f);
+ if ( CCollision::DistToLine(source, target,
+ &tmp) < victim->GetBoundRadius()*3.0f )
+ {
+ float vehicleDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ));
+ if ( vehicleDist < closestEntityDist )
+ {
+ closestEntityDist = vehicleDist;
+ closestEntity = i;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( closestEntityDist < DOOMAUTOAIMING_MAXDIST )
+ {
+ CEntity *victim = entities[closestEntity];
+ ASSERT(victim!=nil);
+
+ float distToTarget = (*target - *source).Magnitude2D();
+ float distToSource = (victim->GetPosition() - *source).Magnitude2D();
+
+ (*target).z = (distToTarget / distToSource) * (0.3f + victim->GetPosition().z - (*source).z) + (*source).z;
+ }
+}
+
+
+void
+CWeapon::DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target)
+{
+ ASSERT(driver!=nil);
+ ASSERT(source!=nil);
+ ASSERT(target!=nil);
+
+#ifndef FIX_BUGS
+ CEntity entity; // unused
+#endif
+
+ CPed *shooterPed = (CPed*)driver;
+
+ int16 lastEntity;
+ CEntity *peds[16];
+ CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, peds, false, false, true, false, false);
+
+ float closestEntityDist = 10000.0f;
+ int16 closestEntity;
+
+ for ( int32 i = 0; i < lastEntity; i++ )
+ {
+ CPed *victim = (CPed*)peds[i];
+ ASSERT(victim!=nil);
+
+ if (driver != victim && !victim->DyingOrDead() && victim->m_attachedTo != vehicle)
+ {
+ float lineDist = CCollision::DistToLine(source, target, &victim->GetPosition());
+
+ uint32 model = vehicle->GetModelIndex();
+ float pedDist;
+ if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW)
+ {
+ float distToVictim = (victim->GetPosition() - vehicle->GetPosition()).Magnitude();
+ pedDist = lineDist / Max(5.f, distToVictim);
+ }
+ else
+ {
+ float distToVictim = (victim->GetPosition() - driver->GetPosition()).Magnitude();
+ pedDist = 0.15f * distToVictim + lineDist;
+ }
+
+ if ( DotProduct((*target-*source), victim->GetPosition()-*source) > 0.0f && pedDist < closestEntityDist)
+ {
+ closestEntity = i;
+ closestEntityDist = pedDist;
+ }
+ }
+ }
+ uint32 model = vehicle->GetModelIndex();
+ float maxAimDistance = CAR_DRIVEBYAUTOAIMING_MAXDIST;
+ if (model == MI_HUNTER)
+ {
+ maxAimDistance = Tan(DEGTORAD(fHunterAimingAngle));
+ }
+ else if (model == MI_SEASPAR || model == MI_SPARROW)
+ {
+ maxAimDistance = Tan(DEGTORAD(fSeaSparrowAimingAngle));
+ }
+
+ if ( closestEntityDist < maxAimDistance )
+ {
+ CEntity *victim = peds[closestEntity];
+ ASSERT(victim!=nil);
+
+ float distToTarget = (*source - *target).Magnitude();
+ float distToSource = (*source - victim->GetPosition()).Magnitude();
+ *target = (distToTarget / distToSource) * (victim->GetPosition() - *source) + *source;
+ }
+}
+
+void
+CWeapon::Reload(void)
+{
+ if (m_nAmmoTotal == 0)
+ return;
+
+ CWeaponInfo *info = GetInfo();
+
+ if (m_nAmmoTotal >= info->m_nAmountofAmmunition)
+ m_nAmmoInClip = info->m_nAmountofAmmunition;
+ else
+ m_nAmmoInClip = m_nAmmoTotal;
+}
+
+void
+CWeapon::Update(int32 audioEntity, CPed *pedToAdjustSound)
+{
+ CWeaponInfo *info = GetInfo();
+
+ switch ( m_eWeaponState )
+ {
+ case WEAPONSTATE_MELEE_MADECONTACT:
+ {
+ m_eWeaponState = WEAPONSTATE_READY;
+ break;
+ }
+
+ case WEAPONSTATE_FIRING:
+ {
+ if ( IsShotgun(m_eWeaponType) && AEHANDLE_IS_OK(audioEntity) )
+ {
+ uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType];
+ if ( CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed )
+ DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, 0.0f);
+ }
+
+ if ( CTimer::GetTimeInMilliseconds() > m_nTimer )
+ {
+ if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE && m_nAmmoTotal == 0 ) {
+ m_eWeaponState = WEAPONSTATE_OUT_OF_AMMO;
+ CPickups::RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(m_eWeaponType);
+ } else
+ m_eWeaponState = WEAPONSTATE_READY;
+ }
+
+ break;
+ }
+
+ case WEAPONSTATE_RELOADING:
+ {
+ if ( AEHANDLE_IS_OK(audioEntity) && m_eWeaponType < WEAPONTYPE_TOTALWEAPONS)
+ {
+ CAnimBlendAssociation *reloadAssoc = nil;
+ if (pedToAdjustSound) {
+ if (CPed::GetReloadAnim(info) && (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload || !pedToAdjustSound->IsPlayer())) {
+ reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetReloadAnim(info));
+ if (!reloadAssoc) {
+ reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetCrouchReloadAnim(info));
+ }
+ }
+ }
+ if (reloadAssoc && reloadAssoc->IsRunning() && reloadAssoc->blendAmount > 0.2f) {
+ float soundStart = 0.75f;
+ switch (info->m_AnimToPlay) {
+ case ASSOCGRP_PYTHON:
+ soundStart = fReloadAnimSampleFraction[0];
+ break;
+ case ASSOCGRP_COLT:
+ case ASSOCGRP_TEC:
+ soundStart = fReloadAnimSampleFraction[1];
+ break;
+ case ASSOCGRP_UZI:
+ soundStart = fReloadAnimSampleFraction[2];
+ break;
+ case ASSOCGRP_RIFLE:
+ soundStart = fReloadAnimSampleFraction[3];
+ break;
+ case ASSOCGRP_M60:
+ soundStart = fReloadAnimSampleFraction[4];
+ break;
+ default:
+ break;
+ }
+ if (reloadAssoc->GetProgress() >= soundStart && (reloadAssoc->currentTime - reloadAssoc->timeStep) / reloadAssoc->hierarchy->totalLength < soundStart)
+ DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType);
+ if (CTimer::GetTimeInMilliseconds() > m_nTimer && reloadAssoc->GetProgress() < 0.9f) {
+ m_nTimer = CTimer::GetTimeInMilliseconds();
+ }
+ } else {
+ uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType];
+ if (CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed)
+ DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType);
+ }
+ }
+
+ if ( CTimer::GetTimeInMilliseconds() > m_nTimer )
+ {
+ Reload();
+ m_eWeaponState = WEAPONSTATE_READY;
+ }
+
+ break;
+ }
+ default: break;
+ }
+}
+
+void
+FireOneInstantHitRound(CVector *source, CVector *target, int32 damage)
+{
+ ASSERT(source!=nil);
+ ASSERT(target!=nil);
+
+ CParticle::AddParticle(PARTICLE_GUNFLASH, *source, CVector(0.0f, 0.0f, 0.0f));
+
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ *source, CVector(0.0f, 0.0f, 0.0f), 5.0f,
+ 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false);
+
+ CColPoint point;
+ CEntity *victim;
+ CWorld::ProcessLineOfSight(*source, *target, point, victim, true, true, true, true, true, true, false);
+
+ CParticle::AddParticle(PARTICLE_HELI_ATTACK, *source, ((*target) - (*source)) * 0.15f);
+
+ if ( victim )
+ {
+ if ( victim->IsPed() )
+ {
+ CPed *victimPed = (CPed *)victim;
+ if ( !victimPed->DyingOrDead() )
+ {
+ CVector pos = victimPed->GetPosition();
+
+ CVector2D posOffset((*source).x-pos.x, (*source).y-pos.y);
+ int32 localDir = victimPed->GetLocalDirection(posOffset);
+
+ victimPed->ClearAttackByRemovingAnim();
+
+ CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir));
+ ASSERT(asoc!=nil);
+ asoc->blendAmount = 0.0f;
+ asoc->blendDelta = 8.0f;
+
+ victimPed->InflictDamage(nil, WEAPONTYPE_UZI, damage, (ePedPieceTypes)point.pieceB, localDir);
+
+ pos.z += 0.8f;
+
+ if ( victimPed->GetIsOnScreen() )
+ {
+ if ( CGame::nastyGame )
+ {
+ for ( int32 i = 0; i < 4; i++ )
+ {
+ CVector dir;
+ dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+ dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+ dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f);
+
+ CParticle::AddParticle(PARTICLE_BLOOD, pos, dir);
+ }
+ }
+ }
+ }
+ }
+ else if ( victim->IsVehicle() )
+ ((CVehicle *)victim)->InflictDamage(nil, WEAPONTYPE_UZI, damage);
+ //BUG ? no CGlass::WasGlassHitByBullet
+
+ switch ( victim->GetType() )
+ {
+ case ENTITY_TYPE_BUILDING:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point);
+ CParticle::AddParticle(PARTICLE_SMOKE, point.point, CVector(0.0f, 0.0f, 0.01f));
+ break;
+ }
+ case ENTITY_TYPE_VEHICLE:
+ {
+ CStats::BulletsThatHit++;
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f);
+ break;
+ }
+ case ENTITY_TYPE_PED:
+ {
+ CStats::BulletsThatHit++;
+ DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f);
+ ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT);
+ break;
+ }
+ case ENTITY_TYPE_OBJECT:
+ {
+ CStats::BulletsThatHit++;
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point);
+ break;
+ }
+ case ENTITY_TYPE_DUMMY:
+ {
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point);
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ {
+ float waterLevel;
+ if ( CWaterLevel::GetWaterLevel((*target).x, (*target).y, (*target).z + 10.0f, &waterLevel, false) )
+ {
+ CParticle::AddParticle(PARTICLE_BOAT_SPLASH, CVector((*target).x, (*target).y, waterLevel), CVector(0.0f, 0.0f, 0.01f));
+ PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_WATER, point.point); // no sound(empty)
+ }
+ }
+}
+
+bool
+CWeapon::IsTypeMelee(void)
+{
+ return CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE;
+}
+
+bool
+CWeapon::IsType2Handed(void)
+{
+ return m_eWeaponType == WEAPONTYPE_FLAMETHROWER || m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 ||
+ m_eWeaponType == WEAPONTYPE_M4 || IsShotgun(m_eWeaponType) ||
+ m_eWeaponType == WEAPONTYPE_RUGER || m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || m_eWeaponType == WEAPONTYPE_LASERSCOPE;
+}
+
+void
+CWeapon::MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target)
+{
+ ASSERT(shooter!=nil);
+ ASSERT(source!=nil);
+ ASSERT(target!=nil);
+
+ float minx = Min(source->x, target->x) - 2.0f;
+ float maxx = Max(source->x, target->x) + 2.0f;
+ float miny = Min(source->y, target->y) - 2.0f;
+ float maxy = Max(source->y, target->y) + 2.0f;
+ float minz = Min(source->z, target->z) - 2.0f;
+ float maxz = Max(source->z, target->z) + 2.0f;
+
+ for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--)
+ {
+ CPed *ped = CPools::GetPedPool()->GetSlot(i);
+
+ if ( ped )
+ {
+ if ( ped->GetPosition().x > minx && ped->GetPosition().x < maxx
+ && ped->GetPosition().y > miny && ped->GetPosition().y < maxy
+ && ped->GetPosition().z > minz && ped->GetPosition().z < maxz )
+ {
+ if ( ped != FindPlayerPed() && !((uint8)(ped->m_randomSeed ^ CGeneral::GetRandomNumber()) & 31) )
+ ped->SetEvasiveDive(shooter, 1);
+ }
+ }
+ }
+}
+
+bool
+CWeapon::HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo)
+{
+ ASSERT(holder!=nil);
+ ASSERT(aimingTo!=nil);
+
+ if (!holder->IsPed() || !((CPed*)holder)->m_pSeekTarget)
+ return false;
+
+ CWeaponInfo *info = GetInfo();
+
+ CVector adjustedOffset = info->m_vecFireOffset;
+ adjustedOffset.z += 0.6f;
+
+ CVector source, target;
+ CEntity *foundEnt = nil;
+ CColPoint foundCol;
+
+ if (fireSource)
+ source = *fireSource;
+ else
+ source = holder->GetMatrix() * adjustedOffset;
+
+ CEntity *aimEntity = aimingTo ? aimingTo : ((CPed*)holder)->m_pSeekTarget;
+ ASSERT(aimEntity!=nil);
+
+ target = aimEntity->GetPosition();
+ target.z += 0.6f;
+
+ CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, false, false, false, false, false, false);
+ if (foundEnt && foundEnt->IsBuilding()) {
+ // That was supposed to be Magnitude, according to leftover code in assembly
+ float diff = (foundCol.point.z - source.z);
+ if (diff < 0.0f && diff > -3.0f)
+ return true;
+ }
+
+ return false;
+}
+
+void
+CWeapon::BlowUpExplosiveThings(CEntity *thing)
+{
+ if ( thing )
+ {
+ CObject *object = (CObject*)thing;
+ int32 mi = object->GetModelIndex();
+ if ( IsExplosiveThingModel(mi) && !object->bHasBeenDamaged && object->IsObject() )
+ {
+ object->bHasBeenDamaged = true;
+
+ CExplosion::AddExplosion(object, FindPlayerPed(), EXPLOSION_BARREL, object->GetPosition()+CVector(0.0f,0.0f,0.5f), 100);
+
+ if ( MI_EXPLODINGBARREL == mi )
+ object->m_vecMoveSpeed.z += 0.55f;
+ else
+ object->m_vecMoveSpeed.z += 0.45f;
+
+ object->m_vecMoveSpeed.x += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f;
+ object->m_vecMoveSpeed.y += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f;
+
+ if ( object->GetIsStatic())
+ {
+ object->SetIsStatic(false);
+ object->AddToMovingList();
+ }
+ }
+ }
+}
+
+bool
+CWeapon::HasWeaponAmmoToBeUsed(void)
+{
+ // FIX: This is better (not bug tho)
+//#if 0
+ if (m_eWeaponType <= WEAPONTYPE_CHAINSAW)
+//#else
+// if (CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE)
+//#endif
+ return true;
+ else
+ return m_nAmmoTotal != 0;
+}
+
+bool
+CPed::IsPedDoingDriveByShooting(void)
+{
+#ifdef FIX_BUGS
+ if (FindPlayerPed() == this && CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nWeaponSlot == 5) {
+#else
+ if (FindPlayerPed() == this && GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) {
+#endif
+ if (TheCamera.Cams[TheCamera.ActiveCam].LookingLeft || TheCamera.Cams[TheCamera.ActiveCam].LookingRight)
+ return true;
+ }
+ return false;
+}
+
+bool
+CWeapon::ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects)
+{
+ return CWorld::ProcessLineOfSight(point1, point2, point, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, false, ignoreSomeObjects, true);
+}
+
+
+void
+CWeapon::CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target)
+{
+ if (!(*victim)->IsVehicle())
+ return;
+
+ CColSphere headSphere;
+
+ CVehicle *veh = (CVehicle*)*victim;
+ CColPoint origPoint(*point);
+ float radius = 1.0f;
+ bool found = false;
+ CColLine shootLine(source, target);
+
+ if (veh->pDriver && veh->pDriver->bCanBeShotInVehicle) {
+ CVector pos(0.f, 0.f, 0.f);
+ veh->pDriver->TransformToNode(pos, PED_HEAD);
+ headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD);
+ if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) {
+ *victim = veh->pDriver;
+ found = true;
+ }
+ }
+
+ for(int i = 0; i < ARRAY_SIZE(veh->pPassengers); i++) {
+ CPed *passenger = veh->pPassengers[i];
+ if (passenger && passenger->bCanBeShotInVehicle) {
+ CVector pos(0.f, 0.f, 0.f);
+ passenger->TransformToNode(pos, PED_HEAD);
+ headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD);
+ if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) {
+ *victim = passenger;
+ found = true;
+ }
+ }
+ }
+ if (veh->IsCar()) {
+ CVector distVec = target - source;
+ if (DotProduct(distVec, veh->GetForward()) < 0.0f && DotProduct(distVec, veh->GetUp()) <= 0.0f) {
+ CColModel *colModel = veh->GetColModel();
+ if (colModel->numTriangles > 0) {
+ bool passesGlass = false;
+ CMatrix invVehMat;
+ Invert(veh->GetMatrix(), invVehMat);
+ shootLine.p0 = invVehMat * shootLine.p0;
+ shootLine.p1 = invVehMat * shootLine.p1;
+ CCollision::CalculateTrianglePlanes(colModel);
+ for (int i = 0; i < colModel->numTriangles; i++) {
+ if (colModel->triangles[i].surface == SURFACE_GLASS &&
+ CCollision::TestLineTriangle(shootLine, colModel->vertices, colModel->triangles[i], colModel->trianglePlanes[i])) {
+ passesGlass = true;
+ break;
+ }
+ }
+ CAutomobile *car = (CAutomobile*)veh;
+
+ // No need to damage windscreen if there isn't one.
+ if (passesGlass && car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN)) {
+ if (car->Damage.GetPanelStatus(VEHPANEL_WINDSCREEN) == PANEL_STATUS_SMASHED2)
+ car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN);
+
+ car->SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN, true);
+ DMAudio.PlayOneShot(veh->m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.f);
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ *victim = veh;
+ *point = origPoint;
+ }
+}
+
+#ifdef COMPATIBLE_SAVES
+#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data));
+#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data));
+void
+CWeapon::Save(uint8*& buf)
+{
+ CopyToBuf(buf, m_eWeaponType);
+ CopyToBuf(buf, m_eWeaponState);
+ CopyToBuf(buf, m_nAmmoInClip);
+ CopyToBuf(buf, m_nAmmoTotal);
+ CopyToBuf(buf, m_nTimer);
+ CopyToBuf(buf, m_bAddRotOffset);
+ SkipSaveBuf(buf, 3);
+}
+
+void
+CWeapon::Load(uint8*& buf)
+{
+ CopyFromBuf(buf, m_eWeaponType);
+ CopyFromBuf(buf, m_eWeaponState);
+ CopyFromBuf(buf, m_nAmmoInClip);
+ CopyFromBuf(buf, m_nAmmoTotal);
+ CopyFromBuf(buf, m_nTimer);
+ CopyFromBuf(buf, m_bAddRotOffset);
+ SkipSaveBuf(buf, 3);
+}
+
+#undef CopyFromBuf
+#undef CopyToBuf
+#endif
diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h
new file mode 100644
index 0000000..f720b31
--- /dev/null
+++ b/src/weapons/Weapon.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#include "WeaponType.h"
+
+#define CAR_DRIVEBYAUTOAIMING_MAXDIST (2.5f)
+#define DOOMAUTOAIMING_MAXDIST (9000.0f)
+
+class CEntity;
+class CPhysical;
+class CVehicle;
+class CPed;
+struct CColPoint;
+class CWeaponInfo;
+
+class CWeapon
+{
+public:
+ eWeaponType m_eWeaponType;
+ eWeaponState m_eWeaponState;
+ int32 m_nAmmoInClip;
+ int32 m_nAmmoTotal;
+ uint32 m_nTimer;
+ bool m_bAddRotOffset;
+
+ static bool bPhotographHasBeenTaken;
+
+ CWeapon() {
+ m_bAddRotOffset = false;
+ }
+ CWeapon(eWeaponType type, int32 ammo);
+
+ CWeaponInfo *GetInfo();
+
+ static void InitialiseWeapons(void);
+ static void ShutdownWeapons (void);
+ static void UpdateWeapons (void);
+
+ void Initialise(eWeaponType type, int32 ammo);
+ void Shutdown();
+
+ bool Fire (CEntity *shooter, CVector *fireSource);
+ bool FireFromCar (CVehicle *shooter, bool left, bool right);
+ bool FireMelee (CEntity *shooter, CVector &fireSource);
+ bool FireInstantHit(CEntity *shooter, CVector *fireSource);
+
+ static void AddGunFlashBigGuns(CVector start, CVector end);
+ void AddGunshell (CEntity *shooter, CVector const &source, CVector2D const &direction, float size);
+ void DoBulletImpact(CEntity *shooter, CEntity *victim, CVector *source, CVector *target, CColPoint *point, CVector2D ahead);
+
+ bool FireShotgun (CEntity *shooter, CVector *fireSource);
+ bool FireProjectile(CEntity *shooter, CVector *fireSource, float power);
+
+ static void GenerateFlameThrowerParticles(CVector pos, CVector dir);
+
+ bool FireAreaEffect (CEntity *shooter, CVector *fireSource);
+ bool LaserScopeDot (CVector *pOutPos, float *pOutSize);
+ bool FireSniper (CEntity *shooter);
+ bool TakePhotograph (CEntity *shooter);
+ bool FireM16_1stPerson (CEntity *shooter);
+ bool FireInstantHitFromCar(CVehicle *shooter, bool left, bool right);
+
+ static void DoDoomAiming (CEntity *shooter, CVector *source, CVector *target);
+ static void DoTankDoomAiming (CEntity *shooter, CEntity *driver, CVector *source, CVector *target);
+ static void DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target);
+
+ void Reload(void);
+ void Update(int32 audioEntity, CPed *pedToAdjustSound);
+ bool IsTypeMelee (void);
+ bool IsType2Handed(void);
+
+ static void MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target);
+
+ bool HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo);
+ static void BlowUpExplosiveThings(CEntity *thing);
+ bool HasWeaponAmmoToBeUsed(void);
+
+ static bool IsShotgun(int weapon) { return weapon == WEAPONTYPE_SHOTGUN || weapon == WEAPONTYPE_SPAS12_SHOTGUN || weapon == WEAPONTYPE_STUBBY_SHOTGUN; }
+
+ static bool ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects);
+
+ static void CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target);
+
+#ifdef COMPATIBLE_SAVES
+ void Save(uint8*& buf);
+ void Load(uint8*& buf);
+#endif
+};
+VALIDATE_SIZE(CWeapon, 0x18);
+
+void FireOneInstantHitRound(CVector *source, CVector *target, int32 damage); \ No newline at end of file
diff --git a/src/weapons/WeaponEffects.cpp b/src/weapons/WeaponEffects.cpp
new file mode 100644
index 0000000..bb95ea8
--- /dev/null
+++ b/src/weapons/WeaponEffects.cpp
@@ -0,0 +1,133 @@
+#include "common.h"
+
+#include "main.h"
+#include "WeaponEffects.h"
+#include "TxdStore.h"
+#include "Sprite.h"
+#include "PlayerPed.h"
+#include "World.h"
+#include "WeaponType.h"
+
+RwTexture *gpCrossHairTex;
+
+CWeaponEffects gCrossHair;
+
+CWeaponEffects::CWeaponEffects()
+{
+
+}
+
+CWeaponEffects::~CWeaponEffects()
+{
+
+}
+
+void
+CWeaponEffects::Init(void)
+{
+ gCrossHair.m_bActive = false;
+ gCrossHair.m_vecPos = CVector(0.0f, 0.0f, 0.0f);
+ gCrossHair.m_nRed = 255;
+ gCrossHair.m_nGreen = 0;
+ gCrossHair.m_nBlue = 0;
+ gCrossHair.m_nAlpha = 127;
+ gCrossHair.m_fSize = 1.0f;
+ gCrossHair.m_fRotation = 0.0f;
+
+
+ CTxdStore::PushCurrentTxd();
+ int32 slot = CTxdStore::FindTxdSlot("particle");
+ CTxdStore::SetCurrentTxd(slot);
+
+ gpCrossHairTex = RwTextureRead("target256", "target256m");
+
+ CTxdStore::PopCurrentTxd();
+}
+
+void
+CWeaponEffects::Shutdown(void)
+{
+ RwTextureDestroy(gpCrossHairTex);
+ gpCrossHairTex = nil;
+}
+
+void
+CWeaponEffects::MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size)
+{
+ gCrossHair.m_bActive = true;
+ gCrossHair.m_vecPos = pos;
+ gCrossHair.m_fSize = size;
+}
+
+void
+CWeaponEffects::ClearCrossHair(void)
+{
+ gCrossHair.m_bActive = false;
+}
+
+void
+CWeaponEffects::Render(void)
+{
+ static float aCrossHairSize[WEAPONTYPE_TOTALWEAPONS] =
+ {
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 0.4f, 0.4f,
+ 0.5f,
+ 0.3f,
+ 0.9f, 0.9f, 0.9f,
+ 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
+ 0.1f, 0.1f,
+ 1.0f,
+ 0.6f,
+ 0.7f,
+ 0.0f, 0.0f
+ };
+
+
+
+ if ( gCrossHair.m_bActive )
+ {
+ float size = aCrossHairSize[FindPlayerPed()->GetWeapon()->m_eWeaponType];
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
+#ifdef FIX_BUGS
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA);
+#else
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVDESTALPHA);
+#endif
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpCrossHairTex));
+
+ RwV3d pos;
+ float w, h;
+ if ( CSprite::CalcScreenCoors(gCrossHair.m_vecPos, &pos, &w, &h, true) )
+ {
+ PUSH_RENDERGROUP("CWeaponEffects::Render");
+
+ float recipz = 1.0f / pos.z;
+ CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z,
+ w, h,
+ 255, 88, 100, 158,
+ recipz, gCrossHair.m_fRotation, gCrossHair.m_nAlpha);
+
+ float recipz2 = 1.0f / pos.z;
+
+ CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z,
+ size*w, size*h,
+ 107, 134, 247, 158,
+ recipz2, TWOPI - gCrossHair.m_fRotation, gCrossHair.m_nAlpha);
+
+ gCrossHair.m_fRotation += 0.02f;
+ if ( gCrossHair.m_fRotation > TWOPI )
+ gCrossHair.m_fRotation = 0.0;
+
+ POP_RENDERGROUP();
+ }
+
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
+ }
+} \ No newline at end of file
diff --git a/src/weapons/WeaponEffects.h b/src/weapons/WeaponEffects.h
new file mode 100644
index 0000000..f6592b3
--- /dev/null
+++ b/src/weapons/WeaponEffects.h
@@ -0,0 +1,26 @@
+#pragma once
+
+class CWeaponEffects
+{
+public:
+ bool m_bActive;
+ CVector m_vecPos;
+ uint8 m_nRed;
+ uint8 m_nGreen;
+ uint8 m_nBlue;
+ uint8 m_nAlpha;
+ float m_fSize;
+ float m_fRotation;
+
+public:
+ CWeaponEffects();
+ ~CWeaponEffects();
+
+ static void Init(void);
+ static void Shutdown(void);
+ static void MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size);
+ static void ClearCrossHair(void);
+ static void Render(void);
+};
+
+VALIDATE_SIZE(CWeaponEffects, 0x1C); \ No newline at end of file
diff --git a/src/weapons/WeaponInfo.cpp b/src/weapons/WeaponInfo.cpp
new file mode 100644
index 0000000..1f78b7d
--- /dev/null
+++ b/src/weapons/WeaponInfo.cpp
@@ -0,0 +1,295 @@
+#include "common.h"
+
+#include "main.h"
+#include "FileMgr.h"
+#include "WeaponInfo.h"
+#include "AnimManager.h"
+#include "AnimBlendAssociation.h"
+#include "Weapon.h"
+#include "ModelInfo.h"
+#include "ModelIndices.h"
+
+uint16 CWeaponInfo::ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS] =
+{
+ 0, // UNARMED
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, // GRENADE
+ 0, // DETONATEGRENADE
+ 0, // TEARGAS
+ 0, // MOLOTOV
+ 0, // ROCKET
+ 250, // COLT45
+ 250, // PYTHON
+ 650, // SHOTGUN
+ 650, // SPAS12 SHOTGUN
+ 650, // STUBBY SHOTGUN
+ 400, // TEC9
+ 400, // UZIhec
+ 400, // SILENCED_INGRAM
+ 400, // MP5
+ 300, // M16
+ 300, // AK47
+ 423, // SNIPERRIFLE
+ 423, // LASERSCOPE
+ 400, // ROCKETLAUNCHER
+ 0, // FLAMETHROWER
+ 0, // M60
+ 0, // MINIGUN
+ 0, // DETONATOR
+ 0, // HELICANNON
+ 0 // CAMERA
+};
+
+// Yeah...
+int32 CWeaponInfo::ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS] =
+{
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+CWeaponInfo aWeaponInfo[WEAPONTYPE_TOTALWEAPONS];
+char CWeaponInfo::ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32] =
+{
+ "Unarmed",
+ "BrassKnuckle",
+ "ScrewDriver",
+ "GolfClub",
+ "NightStick",
+ "Knife",
+ "BaseballBat",
+ "Hammer",
+ "Cleaver",
+ "Machete",
+ "Katana",
+ "Chainsaw",
+ "Grenade",
+ "DetonateGrenade",
+ "TearGas",
+ "Molotov",
+ "Rocket",
+ "Colt45",
+ "Python",
+ "Shotgun",
+ "Spas12Shotgun",
+ "StubbyShotgun",
+ "Tec9",
+ "Uzi",
+ "SilencedIngram",
+ "Mp5",
+ "m4",
+ "Ruger",
+ "SniperRifle",
+ "LaserScope",
+ "RocketLauncher",
+ "FlameThrower",
+ "M60",
+ "Minigun",
+ "Detonator",
+ "HeliCannon",
+ "Camera",
+};
+
+CWeaponInfo*
+CWeaponInfo::GetWeaponInfo(eWeaponType weaponType)
+{
+ return &aWeaponInfo[weaponType];
+}
+
+void
+CWeaponInfo::Initialise(void)
+{
+ debug("Initialising CWeaponInfo...\n");
+ for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) {
+ aWeaponInfo[i].m_eWeaponFire = WEAPON_FIRE_INSTANT_HIT;
+ aWeaponInfo[i].m_fRange = 0.0f;
+ aWeaponInfo[i].m_nFiringRate = 0;
+ aWeaponInfo[i].m_nReload = 0;
+ aWeaponInfo[i].m_nAmountofAmmunition = 0;
+ aWeaponInfo[i].m_nDamage = 0;
+ aWeaponInfo[i].m_fSpeed = 0.0f;
+ aWeaponInfo[i].m_fRadius = 0.0f;
+ aWeaponInfo[i].m_fLifespan = 0.0f;
+ aWeaponInfo[i].m_fSpread = 0.0f;
+ aWeaponInfo[i].m_vecFireOffset = CVector(0.0f, 0.0f, 0.0f);
+ aWeaponInfo[i].m_AnimToPlay = ASSOCGRP_UNARMED;
+ aWeaponInfo[i].m_fAnimLoopStart = 0.0f;
+ aWeaponInfo[i].m_fAnimLoopEnd = 0.0f;
+ aWeaponInfo[i].m_fAnimFrameFire = 0.0f;
+ aWeaponInfo[i].m_fAnim2LoopStart = 0.0f;
+ aWeaponInfo[i].m_fAnim2LoopEnd = 0.0f;
+ aWeaponInfo[i].m_fAnim2FrameFire = 0.0f;
+ aWeaponInfo[i].m_fAnimBreakout = 0.0f;
+ aWeaponInfo[i].m_Flags = WEAPONFLAG_USE_GRAVITY | WEAPONFLAG_SLOWS_DOWN | WEAPONFLAG_RAND_SPEED | WEAPONFLAG_EXPANDS | WEAPONFLAG_EXPLODES;
+ aWeaponInfo[i].m_nWeaponSlot = WEAPONSLOT_UNARMED;
+ }
+ debug("Loading weapon data...\n");
+ LoadWeaponData();
+ debug("CWeaponInfo ready\n");
+}
+
+void
+CWeaponInfo::LoadWeaponData(void)
+{
+ float spread, speed, lifeSpan, radius;
+ float range, fireOffsetX, fireOffsetY, fireOffsetZ;
+ float anim2LoopStart, anim2LoopEnd, delayBetweenAnim2AndFire, animBreakout;
+ float delayBetweenAnimAndFire, animLoopStart, animLoopEnd;
+ int flags, ammoAmount, damage, reload, weaponType;
+ int firingRate, modelId, modelId2, weaponSlot;
+ char line[256], weaponName[32], fireType[32];
+ char animToPlay[32];
+
+ size_t bp, buflen;
+ int lp, linelen;
+
+ CFileMgr::SetDir("DATA");
+ buflen = CFileMgr::LoadFile("WEAPON.DAT", work_buff, sizeof(work_buff), "r");
+
+ for (bp = 0; bp < buflen; ) {
+ // read file line by line
+ for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) {
+ line[linelen++] = work_buff[bp];
+ }
+ bp++;
+ line[linelen] = '\0';
+
+ // skip white space
+ for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++);
+
+ if (line[lp] == '\0' || line[lp] == '#')
+ continue;
+
+ spread = 0.0f;
+ flags = 0;
+ speed = 0.0f;
+ ammoAmount = 0;
+ lifeSpan = 0.0f;
+ radius = 0.0f;
+ range = 0.0f;
+ damage = 0;
+ reload = 0;
+ firingRate = 0;
+ fireOffsetX = 0.0f;
+ weaponName[0] = '\0';
+ fireType[0] = '\0';
+ fireOffsetY = 0.0f;
+ fireOffsetZ = 0.0f;
+ sscanf(
+ &line[lp],
+ "%s %s %f %d %d %d %d %f %f %f %f %f %f %f %s %f %f %f %f %f %f %f %d %d %x %d",
+ weaponName,
+ fireType,
+ &range,
+ &firingRate,
+ &reload,
+ &ammoAmount,
+ &damage,
+ &speed,
+ &radius,
+ &lifeSpan,
+ &spread,
+ &fireOffsetX,
+ &fireOffsetY,
+ &fireOffsetZ,
+ animToPlay,
+ &animLoopStart,
+ &animLoopEnd,
+ &delayBetweenAnimAndFire,
+ &anim2LoopStart,
+ &anim2LoopEnd,
+ &delayBetweenAnim2AndFire,
+ &animBreakout,
+ &modelId,
+ &modelId2,
+ &flags,
+ &weaponSlot);
+
+ if (strncmp(weaponName, "ENDWEAPONDATA", 13) == 0)
+ return;
+
+ weaponType = FindWeaponType(weaponName);
+
+ CVector vecFireOffset(fireOffsetX, fireOffsetY, fireOffsetZ);
+
+ aWeaponInfo[weaponType].m_eWeaponFire = FindWeaponFireType(fireType);
+ aWeaponInfo[weaponType].m_fRange = range;
+ aWeaponInfo[weaponType].m_nFiringRate = firingRate;
+ aWeaponInfo[weaponType].m_nReload = reload;
+ aWeaponInfo[weaponType].m_nAmountofAmmunition = ammoAmount;
+ aWeaponInfo[weaponType].m_nDamage = damage;
+ aWeaponInfo[weaponType].m_fSpeed = speed;
+ aWeaponInfo[weaponType].m_fRadius = radius;
+ aWeaponInfo[weaponType].m_fLifespan = lifeSpan;
+ aWeaponInfo[weaponType].m_fSpread = spread;
+ aWeaponInfo[weaponType].m_vecFireOffset = vecFireOffset;
+ aWeaponInfo[weaponType].m_fAnimLoopStart = animLoopStart / 30.0f;
+ aWeaponInfo[weaponType].m_fAnimLoopEnd = animLoopEnd / 30.0f;
+ aWeaponInfo[weaponType].m_fAnim2LoopStart = anim2LoopStart / 30.0f;
+ aWeaponInfo[weaponType].m_fAnim2LoopEnd = anim2LoopEnd / 30.0f;
+ aWeaponInfo[weaponType].m_fAnimFrameFire = delayBetweenAnimAndFire / 30.0f;
+ aWeaponInfo[weaponType].m_fAnim2FrameFire = delayBetweenAnim2AndFire / 30.0f;
+ aWeaponInfo[weaponType].m_fAnimBreakout = animBreakout / 30.0f;
+ aWeaponInfo[weaponType].m_nModelId = modelId;
+ aWeaponInfo[weaponType].m_nModel2Id = modelId2;
+ aWeaponInfo[weaponType].m_Flags = flags;
+ aWeaponInfo[weaponType].m_nWeaponSlot = weaponSlot;
+
+ if (animLoopEnd < 98.0f && weaponType != WEAPONTYPE_FLAMETHROWER && !CWeapon::IsShotgun(weaponType))
+ aWeaponInfo[weaponType].m_nFiringRate = ((aWeaponInfo[weaponType].m_fAnimLoopEnd - aWeaponInfo[weaponType].m_fAnimLoopStart) * 900.0f);
+
+ if (weaponType == WEAPONTYPE_DETONATOR || weaponType == WEAPONTYPE_HELICANNON)
+ modelId = -1;
+ else if (weaponType == WEAPONTYPE_DETONATOR_GRENADE)
+ modelId = MI_BOMB;
+
+ if (modelId != -1)
+ ((CWeaponModelInfo*)CModelInfo::GetModelInfo(modelId))->SetWeaponInfo(weaponType);
+
+ for (int i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++) {
+ if (!strcmp(animToPlay, CAnimManager::GetAnimGroupName((AssocGroupId)i))) {
+ aWeaponInfo[weaponType].m_AnimToPlay = (AssocGroupId)i;
+ break;
+ }
+ }
+ }
+}
+
+eWeaponType
+CWeaponInfo::FindWeaponType(char *name)
+{
+ for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) {
+ if (strcmp(ms_aWeaponNames[i], name) == 0) {
+ return static_cast<eWeaponType>(i);
+ }
+ }
+ return WEAPONTYPE_UNARMED;
+}
+
+eWeaponFire
+CWeaponInfo::FindWeaponFireType(char *name)
+{
+ if (strcmp(name, "MELEE") == 0) return WEAPON_FIRE_MELEE;
+ if (strcmp(name, "INSTANT_HIT") == 0) return WEAPON_FIRE_INSTANT_HIT;
+ if (strcmp(name, "PROJECTILE") == 0) return WEAPON_FIRE_PROJECTILE;
+ if (strcmp(name, "AREA_EFFECT") == 0) return WEAPON_FIRE_AREA_EFFECT;
+ if (strcmp(name, "CAMERA") == 0) return WEAPON_FIRE_CAMERA;
+ Error("Unknown weapon fire type, WeaponInfo.cpp");
+ return WEAPON_FIRE_INSTANT_HIT;
+}
+
+void
+CWeaponInfo::Shutdown(void)
+{
+ debug("Shutting down CWeaponInfo...\n");
+ debug("CWeaponInfo shut down\n");
+}
diff --git a/src/weapons/WeaponInfo.h b/src/weapons/WeaponInfo.h
new file mode 100644
index 0000000..d7f1563
--- /dev/null
+++ b/src/weapons/WeaponInfo.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "AnimManager.h"
+#include "AnimationId.h"
+#include "WeaponType.h"
+
+enum
+{
+ WEAPONFLAG_USE_GRAVITY = 1,
+ WEAPONFLAG_SLOWS_DOWN = 1 << 1,
+ WEAPONFLAG_DISSIPATES = 1 << 2,
+ WEAPONFLAG_RAND_SPEED = 1 << 3,
+ WEAPONFLAG_EXPANDS = 1 << 4,
+ WEAPONFLAG_EXPLODES = 1 << 5,
+ WEAPONFLAG_CANAIM = 1 << 6,
+ WEAPONFLAG_CANAIM_WITHARM = 1 << 7,
+ WEAPONFLAG_1ST_PERSON = 1 << 8,
+ WEAPONFLAG_HEAVY = 1 << 9,
+ WEAPONFLAG_THROW = 1 << 10,
+ WEAPONFLAG_RELOAD_LOOP2START = 1 << 11,
+ WEAPONFLAG_USE_2ND = 1 << 12,
+ WEAPONFLAG_GROUND_2ND = 1 << 13,
+ WEAPONFLAG_FINISH_3RD = 1 << 14,
+ WEAPONFLAG_RELOAD = 1 << 15,
+ WEAPONFLAG_FIGHTMODE = 1 << 16,
+ WEAPONFLAG_CROUCHFIRE = 1 << 17,
+ WEAPONFLAG_COP3_RD = 1 << 18,
+ WEAPONFLAG_GROUND_3RD = 1 << 19,
+ WEAPONFLAG_PARTIALATTACK = 1 << 20,
+ WEAPONFLAG_ANIMDETONATE = 1 << 21,
+};
+
+class CWeaponInfo {
+ static char ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32];
+public:
+ static uint16 ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS];
+ static int32 ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS];
+
+ eWeaponFire m_eWeaponFire;
+ float m_fRange;
+ uint32 m_nFiringRate;
+ uint32 m_nReload;
+ int32 m_nAmountofAmmunition;
+ uint32 m_nDamage;
+ float m_fSpeed;
+ float m_fRadius;
+ float m_fLifespan;
+ float m_fSpread;
+ CVector m_vecFireOffset;
+ AssocGroupId m_AnimToPlay;
+ float m_fAnimLoopStart;
+ float m_fAnimLoopEnd;
+ float m_fAnimFrameFire;
+ float m_fAnim2LoopStart;
+ float m_fAnim2LoopEnd;
+ float m_fAnim2FrameFire;
+ float m_fAnimBreakout;
+ int32 m_nModelId;
+ int32 m_nModel2Id;
+ uint32 m_Flags;
+
+ uint32 m_nWeaponSlot;
+
+ static void Initialise(void);
+ static void LoadWeaponData(void);
+ static CWeaponInfo *GetWeaponInfo(eWeaponType weaponType);
+ static eWeaponFire FindWeaponFireType(char *name);
+ static eWeaponType FindWeaponType(char *name);
+ static void Shutdown(void);
+ static bool IsWeaponSlotAmmoMergeable(uint32 slot) { return slot == WEAPONSLOT_SHOTGUN || slot == WEAPONSLOT_SUBMACHINEGUN || slot == WEAPONSLOT_RIFLE; }
+ bool IsFlagSet(uint32 flag) const { return (m_Flags & flag) != 0; }
+};
+
+VALIDATE_SIZE(CWeaponInfo, 0x64);
diff --git a/src/weapons/WeaponType.h b/src/weapons/WeaponType.h
new file mode 100644
index 0000000..1220196
--- /dev/null
+++ b/src/weapons/WeaponType.h
@@ -0,0 +1,86 @@
+#pragma once
+
+enum eWeaponType
+{
+ WEAPONTYPE_UNARMED,
+ WEAPONTYPE_BRASSKNUCKLE,
+ WEAPONTYPE_SCREWDRIVER,
+ WEAPONTYPE_GOLFCLUB,
+ WEAPONTYPE_NIGHTSTICK,
+ WEAPONTYPE_KNIFE,
+ WEAPONTYPE_BASEBALLBAT,
+ WEAPONTYPE_HAMMER,
+ WEAPONTYPE_CLEAVER,
+ WEAPONTYPE_MACHETE,
+ WEAPONTYPE_KATANA,
+ WEAPONTYPE_CHAINSAW,
+ WEAPONTYPE_GRENADE,
+ WEAPONTYPE_DETONATOR_GRENADE,
+ WEAPONTYPE_TEARGAS,
+ WEAPONTYPE_MOLOTOV,
+ WEAPONTYPE_ROCKET,
+ WEAPONTYPE_COLT45,
+ WEAPONTYPE_PYTHON,
+ WEAPONTYPE_SHOTGUN,
+ WEAPONTYPE_SPAS12_SHOTGUN,
+ WEAPONTYPE_STUBBY_SHOTGUN,
+ WEAPONTYPE_TEC9,
+ WEAPONTYPE_UZI,
+ WEAPONTYPE_SILENCED_INGRAM,
+ WEAPONTYPE_MP5,
+ WEAPONTYPE_M4,
+ WEAPONTYPE_RUGER,
+ WEAPONTYPE_SNIPERRIFLE,
+ WEAPONTYPE_LASERSCOPE,
+ WEAPONTYPE_ROCKETLAUNCHER,
+ WEAPONTYPE_FLAMETHROWER,
+ WEAPONTYPE_M60,
+ WEAPONTYPE_MINIGUN,
+ WEAPONTYPE_DETONATOR,
+ WEAPONTYPE_HELICANNON,
+ WEAPONTYPE_CAMERA,
+ WEAPONTYPE_TOTALWEAPONS = 37,
+ WEAPONTYPE_HEALTH = 37,
+ WEAPONTYPE_ARMOUR,
+ WEAPONTYPE_RAMMEDBYCAR,
+ WEAPONTYPE_RUNOVERBYCAR,
+ WEAPONTYPE_EXPLOSION,
+ WEAPONTYPE_UZI_DRIVEBY,
+ WEAPONTYPE_DROWNING,
+ WEAPONTYPE_FALL,
+ WEAPONTYPE_UNIDENTIFIED,
+ WEAPONTYPE_ANYMELEE,
+ WEAPONTYPE_ANYWEAPON
+};
+
+enum {
+ WEAPONSLOT_UNARMED = 0,
+ WEAPONSLOT_MELEE,
+ WEAPONSLOT_PROJECTILE,
+ WEAPONSLOT_HANDGUN,
+ WEAPONSLOT_SHOTGUN,
+ WEAPONSLOT_SUBMACHINEGUN,
+ WEAPONSLOT_RIFLE,
+ WEAPONSLOT_HEAVY,
+ WEAPONSLOT_SNIPER,
+ WEAPONSLOT_OTHER,
+ TOTAL_WEAPON_SLOTS
+};
+
+enum eWeaponFire {
+ WEAPON_FIRE_MELEE,
+ WEAPON_FIRE_INSTANT_HIT,
+ WEAPON_FIRE_PROJECTILE,
+ WEAPON_FIRE_AREA_EFFECT,
+ WEAPON_FIRE_CAMERA
+};
+
+// Taken from MTA SA, seems it's unchanged
+enum eWeaponState
+{
+ WEAPONSTATE_READY,
+ WEAPONSTATE_FIRING,
+ WEAPONSTATE_RELOADING,
+ WEAPONSTATE_OUT_OF_AMMO,
+ WEAPONSTATE_MELEE_MADECONTACT
+}; \ No newline at end of file