summaryrefslogtreecommitdiff
path: root/src/weapons/ProjectileInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/weapons/ProjectileInfo.cpp')
-rw-r--r--src/weapons/ProjectileInfo.cpp437
1 files changed, 437 insertions, 0 deletions
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;
+}