From 347f0a0e9c687a198ba7fd0ead67a2d63b7880f2 Mon Sep 17 00:00:00 2001
From: Nikolay Korolev <nickvnuk@gmail.com>
Date: Wed, 1 Apr 2020 01:58:40 +0300
Subject: [PATCH] vehicles missing functions + fixes

---
 src/control/Garages.cpp  |  12 ++-
 src/control/Pickups.cpp  |  29 +++---
 src/vehicles/Vehicle.cpp | 188 ++++++++++++++++++++++++++++++++++++++-
 src/vehicles/Vehicle.h   |   3 +-
 src/weapons/Weapon.cpp   |   1 +
 src/weapons/Weapon.h     |   2 +
 6 files changed, 207 insertions(+), 28 deletions(-)

diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 93857b14..68d58b10 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -2078,14 +2078,12 @@ void CGarage::CenterCarInGarage(CVehicle* pVehicle)
 		return;
 	if (IsAnyOtherPedTouchingGarage(FindPlayerPed()))
 		return;
-	float posX = pVehicle->GetPosition().x;
-	float posY = pVehicle->GetPosition().y;
-	float posZ = pVehicle->GetPosition().z;
+	CVector pos = pVehicle->GetPosition();
 	float garageX = GetGarageCenterX();
 	float garageY = GetGarageCenterY();
-	float offsetX = garageX - posX;
-	float offsetY = garageY - posY;
-	float offsetZ = posZ - posZ;
+	float offsetX = garageX - pos.x;
+	float offsetY = garageY - pos.y;
+	float offsetZ = pos.z - pos.z;
 	float distance = CVector(offsetX, offsetY, offsetZ).Magnitude();
 	if (distance < RESPRAY_CENTERING_COEFFICIENT) {
 		pVehicle->GetPosition().x = GetGarageCenterX();
@@ -2096,7 +2094,7 @@ void CGarage::CenterCarInGarage(CVehicle* pVehicle)
 		pVehicle->GetPosition().y += offsetY * RESPRAY_CENTERING_COEFFICIENT / distance;
 	}
 	if (!IsEntityEntirelyInside3D(pVehicle, 0.1f))
-		pVehicle->GetPosition() = CVector(posX, posY, posZ);
+		pVehicle->GetPosition() = pos;
 }
 
 void CGarages::CloseHideOutGaragesBeforeSave()
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index b1832f0e..3e3c2a48 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -20,6 +20,9 @@
 #include "Fire.h"
 #include "PointLights.h"
 #include "Pools.h"
+#ifdef FIX_BUGS
+#include "Replay.h"
+#endif
 #include "Script.h"
 #include "Shadows.h"
 #include "SpecialFX.h"
@@ -642,32 +645,26 @@ CPickups::AddToCollectedPickupsArray(int32 index)
 void
 CPickups::Update()
 {
-#ifndef FIX_BUGS
-	// BUG: this code can only reach 318 out of 320 pickups
+#ifdef FIX_BUGS // RIP speedrunning (solution from SA)
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
 #define PICKUPS_FRAME_SPAN (6)
-#define PICKUPS_PER_FRAME (NUMGENERALPICKUPS/PICKUPS_FRAME_SPAN)
-
-	for (uint32 i = PICKUPS_PER_FRAME * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < PICKUPS_PER_FRAME * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) {
+#ifdef FIX_BUGS
+	for (uint32 i = NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN) / PICKUPS_FRAME_SPAN; i < NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1) / PICKUPS_FRAME_SPAN; i++) {
+#else // BUG: this code can only reach 318 out of 320 pickups
+	for (uint32 i = NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) {
+#endif
 		if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
 			AddToCollectedPickupsArray(i);
 		}
 	}
-
+#undef PICKUPS_FRAME_SPAN
 	for (uint32 i = NUMGENERALPICKUPS; i < NUMPICKUPS; i++) {
 		if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
 			AddToCollectedPickupsArray(i);
 		}
 	}
-
-#undef PICKUPS_FRAME_SPAN
-#undef PICKUPS_PER_FRAME
-#else
-	for (uint32 i = 0; i < NUMPICKUPS; i++) {
-		if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
-			AddToCollectedPickupsArray(i);
-		}
-	}
-#endif
 }
 
 void
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 1fe02953..adeba19e 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -31,10 +31,17 @@ void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehicleP
 void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
 void CVehicle::operator delete(void *p, int handle) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
 
-WRAPPER bool CVehicle::ShufflePassengersToMakeSpace(void) { EAXJMP(0x5528A0); }
-// or Weapon.cpp?
-WRAPPER void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage) { EAXJMP(0x563B00); }
-WRAPPER void CVehicle::InflictDamage(CEntity *damagedBy, uint32 weaponType, float damage) { EAXJMP(0x551950); }
+#ifdef FIX_BUGS
+// I think they meant that
+#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (MYRAND_MAX * 35 / 100)
+#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (MYRAND_MAX * 70 / 100)
+#else
+#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (35000)
+#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (70000)
+#endif
+#define DAMAGE_HEALTH_TO_FLEE_ALWAYS (200.0f)
+#define DAMAGE_HEALTH_TO_CATCH_FIRE (250.0f)
+
 
 CVehicle::CVehicle(uint8 CreatedBy)
 {
@@ -361,6 +368,119 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec
 	return angularVelocity * CTimer::GetTimeStep();
 }
 
+void
+CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage)
+{
+	if (!bCanBeDamaged)
+		return;
+	if (bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle()))
+		return;
+	bool bFrightensDriver = false;
+	switch (weaponType) {
+	case WEAPONTYPE_UNARMED:
+	case WEAPONTYPE_BASEBALLBAT:
+		if (bMeleeProof)
+			return;
+		break;
+	case WEAPONTYPE_COLT45:
+	case WEAPONTYPE_UZI:
+	case WEAPONTYPE_SHOTGUN:
+	case WEAPONTYPE_AK47:
+	case WEAPONTYPE_M16:
+	case WEAPONTYPE_SNIPERRIFLE:
+	case WEAPONTYPE_TOTAL_INVENTORY_WEAPONS:
+	case WEAPONTYPE_UZI_DRIVEBY:
+		if (bBulletProof)
+			return;
+		bFrightensDriver = true;
+		break;
+	case WEAPONTYPE_ROCKETLAUNCHER:
+	case WEAPONTYPE_MOLOTOV:
+	case WEAPONTYPE_GRENADE:
+	case WEAPONTYPE_EXPLOSION:
+		if (bExplosionProof)
+			return;
+		bFrightensDriver = true;
+		break;
+	case WEAPONTYPE_FLAMETHROWER:
+		if (bFireProof)
+			return;
+		break;
+	case WEAPONTYPE_RAMMEDBYCAR:
+		if (bCollisionProof)
+			return;
+		break;
+	default:
+		break;
+	}
+	if (m_fHealth > 0.0f) {
+		if (VehicleCreatedBy == RANDOM_VEHICLE && pDriver &&
+			(m_status == STATUS_SIMPLE || m_status == STATUS_PHYSICS) &&
+			AutoPilot.m_nCarMission == MISSION_CRUISE) {
+			if (m_randomSeed < DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE) {
+				CCarCtrl::SwitchVehicleToRealPhysics(this);
+				AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
+				AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * pHandling->Transmission.fUnkMaxVelocity;
+				m_status = STATUS_PHYSICS;
+			}
+		}
+		m_nLastWeaponDamage = weaponType;
+		float oldHealth = m_fHealth;
+		if (m_fHealth > damage) {
+			m_fHealth -= damage;
+			if (VehicleCreatedBy == RANDOM_VEHICLE &&
+				(m_fHealth < DAMAGE_HEALTH_TO_FLEE_ALWAYS ||
+					bFrightensDriver && m_randomSeed > DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE)) {
+				switch (m_status) {
+				case STATUS_SIMPLE:
+				case STATUS_PHYSICS:
+					if (pDriver) {
+						m_status = STATUS_ABANDONED;
+						pDriver->bFleeAfterExitingCar = true;
+						pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this);
+					}
+					for (int i = 0; i < m_nNumMaxPassengers; i++) {
+						if (pPassengers[i]) {
+							pPassengers[i]->bFleeAfterExitingCar = true;
+							pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this);
+						}
+					}
+					break;
+				default:
+					break;
+				}
+			}
+			if (oldHealth > DAMAGE_HEALTH_TO_CATCH_FIRE && m_fHealth < DAMAGE_HEALTH_TO_CATCH_FIRE) {
+				if (IsCar()) {
+					CAutomobile* pThisCar = (CAutomobile*)this;
+					pThisCar->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE);
+					pThisCar->m_pSetOnFireEntity = damagedBy;
+					if (damagedBy)
+						damagedBy->RegisterReference((CEntity**)&pThisCar->m_pSetOnFireEntity);
+				}
+			}
+		}
+		else {
+			m_fHealth = 0.0f;
+			if (weaponType == WEAPONTYPE_EXPLOSION) {
+				// between 1000 and 3047. Also not very nice: can't be saved by respray or cheat
+				m_nBombTimer = 1000 + CGeneral::GetRandomNumber() & 0x7FF;
+				m_pBlowUpEntity = damagedBy;
+				if (damagedBy)
+					damagedBy->RegisterReference((CEntity**)&m_pBlowUpEntity);
+			}
+			else
+				BlowUpCar(damagedBy);
+		}
+	}
+#ifdef FIX_BUGS // removing dumb case when shooting police car in player's own garage gives wanted level
+	if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && !bHasBeenOwnedByPlayer)
+#else
+	if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed())
+#endif
+		FindPlayerPed()->SetWantedLevelNoDrop(1);
+}
+
 void
 CVehicle::ExtinguishCarFire(void)
 {
@@ -375,6 +495,65 @@ CVehicle::ExtinguishCarFire(void)
 	}
 }
 
+bool
+CVehicle::ShufflePassengersToMakeSpace(void)
+{
+	if (m_nNumPassengers >= m_nNumMaxPassengers)
+		return false;
+	if (pPassengers[1] &&
+		!(m_nGettingInFlags & CAR_DOOR_FLAG_LR) &&
+		IsRoomForPedToLeaveCar(COMPONENT_DOOR_REAR_LEFT, nil)) {
+		if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) {
+			pPassengers[2] = pPassengers[1];
+			pPassengers[1] = nil;
+			pPassengers[2]->m_vehEnterType = CAR_DOOR_RR;
+			return true;
+		}
+		if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) {
+			pPassengers[0] = pPassengers[1];
+			pPassengers[1] = nil;
+			pPassengers[0]->m_vehEnterType = CAR_DOOR_RF;
+			return true;
+		}
+		return false;
+	}
+	if (pPassengers[2] &&
+		!(m_nGettingInFlags & CAR_DOOR_FLAG_RR) &&
+		IsRoomForPedToLeaveCar(COMPONENT_DOOR_REAR_RIGHT, nil)) {
+		if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) {
+			pPassengers[1] = pPassengers[2];
+			pPassengers[2] = nil;
+			pPassengers[1]->m_vehEnterType = CAR_DOOR_LR;
+			return true;
+		}
+		if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) {
+			pPassengers[0] = pPassengers[2];
+			pPassengers[2] = nil;
+			pPassengers[0]->m_vehEnterType = CAR_DOOR_RF;
+			return true;
+		}
+		return false;
+	}
+	if (pPassengers[0] &&
+		!(m_nGettingInFlags & CAR_DOOR_FLAG_RF) &&
+		IsRoomForPedToLeaveCar(COMPONENT_DOOR_FRONT_RIGHT, nil)) {
+		if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) {
+			pPassengers[1] = pPassengers[0];
+			pPassengers[0] = nil;
+			pPassengers[1]->m_vehEnterType = CAR_DOOR_LR;
+			return true;
+		}
+		if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) {
+			pPassengers[2] = pPassengers[0];
+			pPassengers[0] = nil;
+			pPassengers[2]->m_vehEnterType = CAR_DOOR_RR;
+			return true;
+		}
+		return false;
+	}
+	return false;
+}
+
 void
 CVehicle::ProcessDelayedExplosion(void)
 {
@@ -831,4 +1010,5 @@ STARTPATCHES
 	InjectHook(0x551EB0, &CVehicle::RemovePassenger, PATCH_JUMP);
 	InjectHook(0x5525A0, &CVehicle::ProcessCarAlarm, PATCH_JUMP);
 	InjectHook(0x552620, &CVehicle::IsSphereTouchingVehicle, PATCH_JUMP);
+	InjectHook(0x551950, &CVehicle::InflictDamage, PATCH_JUMP);
 ENDPATCHES
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 2ca97841..4639f3e1 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -4,6 +4,7 @@
 #include "AutoPilot.h"
 #include "ModelIndices.h"
 #include "AnimManager.h"
+#include "Weapon.h"
 
 class CPed;
 class CFire;
@@ -266,7 +267,7 @@ public:
 	void ProcessCarAlarm(void);
 	bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius);
 	bool ShufflePassengersToMakeSpace(void);
-	void InflictDamage(CEntity *damagedBy, uint32 weaponType, float damage);
+	void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage);
 
 	bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; }
 	CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); }
diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp
index 09844c23..0f41264f 100644
--- a/src/weapons/Weapon.cpp
+++ b/src/weapons/Weapon.cpp
@@ -14,6 +14,7 @@ WRAPPER void CWeapon::AddGunshell(CEntity*, CVector const&, CVector2D const&, fl
 WRAPPER void CWeapon::Update(int32 audioEntity) { EAXJMP(0x563A10); }
 WRAPPER void CWeapon::DoTankDoomAiming(CEntity *playerVehicle, CEntity *playerPed, CVector *start, CVector *end) { EAXJMP(0x563200); }
 WRAPPER void CWeapon::InitialiseWeapons(void) { EAXJMP(0x55C2D0); }
+WRAPPER void FireOneInstantHitRound(CVector* shotSource, CVector* shotTarget, int32 damage) { EAXJMP(0x563B00); }
 
 void
 CWeapon::Initialise(eWeaponType type, int ammo)
diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h
index 74145564..84760550 100644
--- a/src/weapons/Weapon.h
+++ b/src/weapons/Weapon.h
@@ -81,3 +81,5 @@ public:
 	static void UpdateWeapons(void);
 };
 static_assert(sizeof(CWeapon) == 0x18, "CWeapon: error");
+
+void FireOneInstantHitRound(CVector* shotSource, CVector* shotTarget, int32 damage);
\ No newline at end of file