summaryrefslogtreecommitdiffstats
path: root/src/vehicles
diff options
context:
space:
mode:
Diffstat (limited to 'src/vehicles')
-rw-r--r--src/vehicles/Automobile.cpp18
-rw-r--r--src/vehicles/Automobile.h67
-rw-r--r--src/vehicles/Boat.cpp14
-rw-r--r--src/vehicles/Boat.h17
-rw-r--r--src/vehicles/DamageManager.cpp243
-rw-r--r--src/vehicles/DamageManager.h92
-rw-r--r--src/vehicles/HandlingMgr.cpp247
-rw-r--r--src/vehicles/HandlingMgr.h139
-rw-r--r--src/vehicles/Heli.cpp15
-rw-r--r--src/vehicles/Heli.h17
-rw-r--r--src/vehicles/Plane.cpp19
-rw-r--r--src/vehicles/Plane.h18
-rw-r--r--src/vehicles/Train.cpp14
-rw-r--r--src/vehicles/Train.h26
-rw-r--r--src/vehicles/Transmission.cpp37
-rw-r--r--src/vehicles/Transmission.h26
-rw-r--r--src/vehicles/Vehicle.cpp489
-rw-r--r--src/vehicles/Vehicle.h240
18 files changed, 1738 insertions, 0 deletions
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
new file mode 100644
index 00000000..54eed17a
--- /dev/null
+++ b/src/vehicles/Automobile.cpp
@@ -0,0 +1,18 @@
+#include "common.h"
+#include "patcher.h"
+#include "Automobile.h"
+
+CAutomobile::CAutomobile(int mi, uint8 owner)
+{
+ ctor(mi, owner);
+}
+
+WRAPPER CAutomobile* CAutomobile::ctor(int, uint8) { EAXJMP(0x52C6B0); }
+
+WRAPPER void CAutomobile::SetDoorDamage(int32, uint32, bool) { EAXJMP(0x530200); }
+WRAPPER void CAutomobile::SetPanelDamage(int32, uint32, bool) { EAXJMP(0x5301A0); }
+WRAPPER void CAutomobile::SetBumperDamage(int32, uint32, bool) { EAXJMP(0x530120); }
+
+STARTPATCHES
+InjectHook(0x52D170, &CAutomobile::dtor, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h
new file mode 100644
index 00000000..630635c7
--- /dev/null
+++ b/src/vehicles/Automobile.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "DamageManager.h"
+#include "Vehicle.h"
+
+struct CDoor
+{
+ float m_fAngleWhenOpened;
+ float m_fAngleWhenClosed;
+ char field_8;
+ char field_9;
+ char field_10;
+ char field_11;
+ float m_fAngle;
+ float m_fPreviousAngle;
+ float m_fAngularVelocity;
+ CVector m_vecVelocity;
+};
+
+class CAutomobile : public CVehicle
+{
+public:
+ // 0x288
+ CDamageManager Damage;
+ CDoor Doors[6];
+ RwFrame *m_aCarNodes[NUM_CAR_NODES];
+ CColPoint m_aWheelColPoints[4];
+ float m_aSuspensionSpringRatio[4];
+ float m_aSuspensionSpringRatioPrev[4];
+ float m_aWheelSkidThing[4];
+ int field_49C;
+ bool m_aWheelSkidmarkMuddy[4];
+ bool m_aWheelSkidmarkBloody[4];
+ float m_aWheelRotation[4];
+ float m_aWheelPosition[4];
+ float m_aWheelSpeed[4];
+ uint8 stuff3[12];
+ uint32 m_nBusDoorTimerEnd;
+ uint32 m_nBusDoorTimerStart;
+ float m_aSuspensionSpringLength[4];
+ float m_aSuspensionLineLength[4];
+ float m_fHeightAboveRoad;
+ float m_fImprovedHandling;
+ uint8 stuff6[32];
+ CPhysical *m_aGroundPhysical[4]; // physicals touching wheels
+ CVector m_aGroundOffset[4]; // from ground object to colpoint
+ CEntity *m_pBlowUpEntity;
+ float m_weaponThingA; // TODO
+ float m_weaponThingB; // TODO
+ float m_fCarGunLR;
+ float m_fCarGunUD;
+ float m_fWindScreenRotation;
+ uint8 stuff4[4];
+ uint8 m_nWheelsOnGround_2;
+ uint8 m_nWheelsOnGround;
+ uint8 m_nWheelsOnGroundPrev;
+ uint8 stuff5[5];
+ int32 m_aWheelState[4];
+
+ CAutomobile(int, uint8);
+ CAutomobile* ctor(int, uint8);
+ void SetDoorDamage(int32, uint32, bool); /* TODO: eDoors */
+ void SetPanelDamage(int32, uint32, bool); /* TODO: ePanels */
+ void SetBumperDamage(int32, uint32, bool); /* TODO: ePanels */
+ void dtor() { this->CAutomobile::~CAutomobile(); }
+};
+static_assert(sizeof(CAutomobile) == 0x5A8, "CAutomobile: error");
diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp
new file mode 100644
index 00000000..076a910e
--- /dev/null
+++ b/src/vehicles/Boat.cpp
@@ -0,0 +1,14 @@
+#include "common.h"
+#include "patcher.h"
+#include "Boat.h"
+
+CBoat::CBoat(int mi, uint8 owner)
+{
+ ctor(mi, owner);
+}
+
+WRAPPER CBoat* CBoat::ctor(int, uint8) { EAXJMP(0x53E3E0); }
+
+STARTPATCHES
+InjectHook(0x53E790, &CBoat::dtor, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/vehicles/Boat.h b/src/vehicles/Boat.h
new file mode 100644
index 00000000..6d6482a4
--- /dev/null
+++ b/src/vehicles/Boat.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Vehicle.h"
+
+class CBoat : public CVehicle
+{
+public:
+ // 0x288
+ uint8 stuff1[57];
+ bool m_bIsAnchored;
+ uint8 stuff[450];
+
+ CBoat(int, uint8);
+ CBoat* ctor(int, uint8);
+ void dtor() { this->CBoat::~CBoat(); };
+};
+static_assert(sizeof(CBoat) == 0x484, "CBoat: error");
diff --git a/src/vehicles/DamageManager.cpp b/src/vehicles/DamageManager.cpp
new file mode 100644
index 00000000..1a7f25ed
--- /dev/null
+++ b/src/vehicles/DamageManager.cpp
@@ -0,0 +1,243 @@
+#include "common.h"
+#include "patcher.h"
+#include "General.h"
+#include "DamageManager.h"
+
+
+float G_aComponentDamage[] = { 2.5f, 1.25f, 3.2f, 1.4f, 2.5f, 2.8f, 0.5f };
+
+void
+CDamageManager::ResetDamageStatus(void)
+{
+ memset(this, 0, sizeof(*this));
+}
+
+void
+CDamageManager::FuckCarCompletely(void)
+{
+ int i;
+
+ m_wheelStatus[0] = 2;
+ // wheels 1-3 not reset?
+
+ m_doorStatus[0] = 3;
+ m_doorStatus[1] = 3;
+ m_doorStatus[2] = 3;
+ m_doorStatus[3] = 3;
+ m_doorStatus[4] = 3;
+ m_doorStatus[5] = 3;
+
+ for(i = 0; i < 3; i++){
+#ifdef FIX_BUGS
+ ProgressPanelDamage(VEHBUMPER_FRONT);
+ ProgressPanelDamage(VEHBUMPER_REAR);
+#else
+ // this can't be right
+ ProgressPanelDamage(COMPONENT_BUMPER_FRONT);
+ ProgressPanelDamage(COMPONENT_BUMPER_REAR);
+#endif
+ }
+ // Why set to no damage?
+ m_lightStatus = 0;
+ m_panelStatus = 0;
+ SetEngineStatus(250);
+}
+
+bool
+CDamageManager::ApplyDamage(tComponent component, float damage, float unused)
+{
+ tComponentGroup group;
+ uint8 subComp;
+
+ GetComponentGroup(component, &group, &subComp);
+ damage *= G_aComponentDamage[group];
+ if(damage > 150.0f){
+ switch(group){
+ case COMPGROUP_WHEEL:
+ ProgressWheelDamage(subComp);
+ break;
+ case COMPGROUP_DOOR:
+ case COMPGROUP_BOOT:
+ ProgressDoorDamage(subComp);
+ break;
+ case COMPGROUP_BONNET:
+ if(damage > 220.0f)
+ ProgressEngineDamage();
+ ProgressDoorDamage(subComp);
+ break;
+ case COMPGROUP_PANEL:
+ // so windscreen is a light?
+ SetLightStatus((eLights)subComp, 1);
+ // fall through
+ case COMPGROUP_BUMPER:
+ if(damage > 220.0f &&
+ (component == COMPONENT_PANEL_FRONT_LEFT ||
+ component == COMPONENT_PANEL_FRONT_RIGHT ||
+ component == COMPONENT_PANEL_WINDSCREEN))
+ ProgressEngineDamage();
+ ProgressPanelDamage(subComp);
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+CDamageManager::GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *subComp)
+{
+ *subComp = -2; // ??
+
+ // This is done very strangely in the game, maybe an optimized switch?
+ if(component >= COMPONENT_PANEL_FRONT_LEFT){
+ if(component >= COMPONENT_BUMPER_FRONT)
+ *componentGroup = COMPGROUP_BUMPER;
+ else
+ *componentGroup = COMPGROUP_PANEL;
+ *subComp = component - COMPONENT_PANEL_FRONT_LEFT;
+ return true;
+ }else if(component >= COMPONENT_DOOR_BONNET){
+ if(component == COMPONENT_DOOR_BONNET)
+ *componentGroup = COMPGROUP_BONNET;
+ else if(component == COMPONENT_DOOR_BOOT)
+ *componentGroup = COMPGROUP_BOOT;
+ else
+ *componentGroup = COMPGROUP_DOOR;
+ *subComp = component - COMPONENT_DOOR_BONNET;
+ return true;
+ }else if(component >= COMPONENT_WHEEL_FRONT_LEFT){
+ *componentGroup = COMPGROUP_WHEEL;
+ *subComp = component - COMPONENT_WHEEL_FRONT_LEFT;
+ return true;
+ }else if(component >= COMPONENT_DEFAULT){
+ *componentGroup = COMPGROUP_DEFAULT;
+ *subComp = component - COMPONENT_DEFAULT;
+ return true;
+ }else
+ return false;
+}
+
+void
+CDamageManager::SetDoorStatus(int32 door, uint32 status)
+{
+ m_doorStatus[door] = status;
+}
+
+int32
+CDamageManager::GetDoorStatus(int32 door)
+{
+ return m_doorStatus[door];
+}
+
+bool
+CDamageManager::ProgressDoorDamage(uint8 door)
+{
+ int status = GetDoorStatus(door);
+ if(status == 3)
+ return false;
+ SetDoorStatus(door, status+1);
+ return true;
+}
+
+void
+CDamageManager::SetPanelStatus(int32 panel, uint32 status)
+{
+ m_panelStatus = dpb(status, panel*4, 4, m_panelStatus);
+}
+
+int32
+CDamageManager::GetPanelStatus(int32 panel)
+{
+ return ldb(panel*4, 4, m_panelStatus);
+}
+
+bool
+CDamageManager::ProgressPanelDamage(uint8 panel)
+{
+ int status = GetPanelStatus(panel);
+ if(status == 3)
+ return false;
+ SetPanelStatus(panel, status+1);
+ return true;
+}
+
+void
+CDamageManager::SetLightStatus(eLights light, uint32 status)
+{
+ m_lightStatus = dpb(status, light*2, 2, m_lightStatus);
+}
+
+int32
+CDamageManager::GetLightStatus(eLights light)
+{
+ return ldb(light*2, 2, m_lightStatus);
+}
+
+void
+CDamageManager::SetWheelStatus(int32 wheel, uint32 status)
+{
+ m_wheelStatus[wheel] = status;
+}
+
+int32
+CDamageManager::GetWheelStatus(int32 wheel)
+{
+ return m_wheelStatus[wheel];
+}
+
+bool
+CDamageManager::ProgressWheelDamage(uint8 wheel)
+{
+ int status = GetWheelStatus(wheel);
+ if(status == 3)
+ return false;
+ SetWheelStatus(wheel, status+1);
+ return true;
+}
+
+void
+CDamageManager::SetEngineStatus(uint32 status)
+{
+ if(status > 250)
+ m_engineStatus = 250;
+ else
+ m_engineStatus = status;
+}
+
+int32
+CDamageManager::GetEngineStatus(void)
+{
+ return m_engineStatus;
+}
+
+bool
+CDamageManager::ProgressEngineDamage(void)
+{
+ int status = GetEngineStatus();
+ int newstatus = status + 32 + (CGeneral::GetRandomNumber() & 0x1F);
+ if(status < 225 && newstatus > 224)
+ newstatus = 224;
+ SetEngineStatus(newstatus);
+ return true;
+}
+
+STARTPATCHES
+ InjectHook(0x545850, &CDamageManager::ResetDamageStatus, PATCH_JUMP);
+ InjectHook(0x545B70, &CDamageManager::FuckCarCompletely, PATCH_JUMP);
+ InjectHook(0x545790, &CDamageManager::GetComponentGroup, PATCH_JUMP);
+ InjectHook(0x545A80, &CDamageManager::ApplyDamage, PATCH_JUMP);
+ InjectHook(0x545920, &CDamageManager::SetDoorStatus, PATCH_JUMP);
+ InjectHook(0x545930, &CDamageManager::GetDoorStatus, PATCH_JUMP);
+ InjectHook(0x545970, &CDamageManager::ProgressDoorDamage, PATCH_JUMP);
+ InjectHook(0x5458B0, &CDamageManager::SetPanelStatus, PATCH_JUMP);
+ InjectHook(0x5458E0, (int32 (CDamageManager::*)(int32))&CDamageManager::GetPanelStatus, PATCH_JUMP);
+ InjectHook(0x545A00, &CDamageManager::ProgressPanelDamage, PATCH_JUMP);
+ InjectHook(0x545860, &CDamageManager::SetLightStatus, PATCH_JUMP);
+ InjectHook(0x545890, &CDamageManager::GetLightStatus, PATCH_JUMP);
+ InjectHook(0x545900, &CDamageManager::SetWheelStatus, PATCH_JUMP);
+ InjectHook(0x545910, &CDamageManager::GetWheelStatus, PATCH_JUMP);
+ InjectHook(0x545A40, &CDamageManager::ProgressWheelDamage, PATCH_JUMP);
+ InjectHook(0x545940, &CDamageManager::SetEngineStatus, PATCH_JUMP);
+ InjectHook(0x545960, &CDamageManager::GetEngineStatus, PATCH_JUMP);
+ InjectHook(0x5459B0, &CDamageManager::ProgressEngineDamage, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/vehicles/DamageManager.h b/src/vehicles/DamageManager.h
new file mode 100644
index 00000000..1fdbc6b1
--- /dev/null
+++ b/src/vehicles/DamageManager.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include "common.h"
+
+// TODO: move some of this into Vehicle.h
+
+enum tComponent
+{
+ COMPONENT_DEFAULT,
+ COMPONENT_WHEEL_FRONT_LEFT,
+ COMPONENT_WHEEL_FRONT_RIGHT,
+ COMPONENT_WHEEL_REAR_LEFT,
+ COMPONENT_WHEEL_REAR_RIGHT,
+ COMPONENT_DOOR_BONNET,
+ COMPONENT_DOOR_BOOT,
+ COMPONENT_DOOR_FRONT_LEFT,
+ COMPONENT_DOOR_FRONT_RIGHT,
+ COMPONENT_DOOR_REAR_LEFT,
+ COMPONENT_DOOR_REAR_RIGHT,
+ COMPONENT_PANEL_FRONT_LEFT,
+ COMPONENT_PANEL_FRONT_RIGHT,
+ COMPONENT_PANEL_REAR_LEFT,
+ COMPONENT_PANEL_REAR_RIGHT,
+ COMPONENT_PANEL_WINDSCREEN,
+ COMPONENT_BUMPER_FRONT,
+ COMPONENT_BUMPER_REAR,
+};
+
+enum tComponentGroup
+{
+ COMPGROUP_BUMPER,
+ COMPGROUP_WHEEL,
+ COMPGROUP_DOOR,
+ COMPGROUP_BONNET,
+ COMPGROUP_BOOT,
+ COMPGROUP_PANEL,
+ COMPGROUP_DEFAULT,
+};
+
+enum eLights
+{
+ VEHLIGHT_FRONT_LEFT,
+ VEHLIGHT_FRONT_RIGHT,
+ VEHLIGHT_REAR_LEFT,
+ VEHLIGHT_REAR_RIGHT,
+};
+
+enum {
+ VEHPANEL_FRONT_LEFT,
+ VEHPANEL_FRONT_RIGHT,
+ VEHPANEL_REAR_LEFT,
+ VEHPANEL_REAR_RIGHT,
+ VEHPANEL_WINDSCREEN,
+ VEHBUMPER_FRONT,
+ VEHBUMPER_REAR,
+};
+
+class CDamageManager
+{
+public:
+
+ float field_0;
+ uint8 m_engineStatus;
+ uint8 m_wheelStatus[4];
+ uint8 m_doorStatus[6];
+ uint32 m_lightStatus;
+ uint32 m_panelStatus;
+ uint32 field_24;
+
+ void ResetDamageStatus(void);
+ void FuckCarCompletely(void);
+ bool ApplyDamage(tComponent component, float damage, float unused);
+ bool GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *foo);
+
+ void SetDoorStatus(int32 door, uint32 status);
+ int32 GetDoorStatus(int32 door);
+ bool ProgressDoorDamage(uint8 door);
+ void SetPanelStatus(int32 panel, uint32 status);
+ int32 GetPanelStatus(int32 panel);
+ bool ProgressPanelDamage(uint8 panel);
+ // needed for CReplay
+ static int32 GetPanelStatus(uint32 panelstatus, int32 panel) { return ldb(panel*4, 4, panelstatus); }
+ void SetLightStatus(eLights light, uint32 status);
+ int32 GetLightStatus(eLights light);
+ void SetWheelStatus(int32 wheel, uint32 status);
+ int32 GetWheelStatus(int32 wheel);
+ bool ProgressWheelDamage(uint8 wheel);
+ void SetEngineStatus(uint32 status);
+ int32 GetEngineStatus(void);
+ bool ProgressEngineDamage(void);
+};
+VALIDATE_SIZE(CDamageManager, 0x1C);
diff --git a/src/vehicles/HandlingMgr.cpp b/src/vehicles/HandlingMgr.cpp
new file mode 100644
index 00000000..47d0564c
--- /dev/null
+++ b/src/vehicles/HandlingMgr.cpp
@@ -0,0 +1,247 @@
+#include "common.h"
+#include "patcher.h"
+#include "main.h"
+#include "FileMgr.h"
+#include "HandlingMgr.h"
+
+cHandlingDataMgr &mod_HandlingManager = *(cHandlingDataMgr*)0x728060;
+
+char *HandlingFilename = "HANDLING.CFG";
+
+char VehicleNames[NUMHANDLINGS][14] = {
+ "LANDSTAL",
+ "IDAHO",
+ "STINGER",
+ "LINERUN",
+ "PEREN",
+ "SENTINEL",
+ "PATRIOT",
+ "FIRETRUK",
+ "TRASH",
+ "STRETCH",
+ "MANANA",
+ "INFERNUS",
+ "BLISTA",
+ "PONY",
+ "MULE",
+ "CHEETAH",
+ "AMBULAN",
+ "FBICAR",
+ "MOONBEAM",
+ "ESPERANT",
+ "TAXI",
+ "KURUMA",
+ "BOBCAT",
+ "MRWHOOP",
+ "BFINJECT",
+ "POLICE",
+ "ENFORCER",
+ "SECURICA",
+ "BANSHEE",
+ "PREDATOR",
+ "BUS",
+ "RHINO",
+ "BARRACKS",
+ "TRAIN",
+ "HELI",
+ "DODO",
+ "COACH",
+ "CABBIE",
+ "STALLION",
+ "RUMPO",
+ "RCBANDIT",
+ "BELLYUP",
+ "MRWONGS",
+ "MAFIA",
+ "YARDIE",
+ "YAKUZA",
+ "DIABLOS",
+ "COLUMB",
+ "HOODS",
+ "AIRTRAIN",
+ "DEADDODO",
+ "SPEEDER",
+ "REEFER",
+ "PANLANT",
+ "FLATBED",
+ "YANKEE",
+ "BORGNINE"
+};
+
+cHandlingDataMgr::cHandlingDataMgr(void)
+{
+ memset(this, 0, sizeof(this));
+}
+
+void
+cHandlingDataMgr::Initialise(void)
+{
+ LoadHandlingData();
+ field_0 = 0.1f;
+ field_4 = 0.9f;
+ field_8 = 1.0f;
+ field_C = 0.8f;
+ field_10 = 0.98f;
+}
+
+void
+cHandlingDataMgr::LoadHandlingData(void)
+{
+ char *start, *end;
+ char line[201]; // weird value
+ char delim[4]; // not sure
+ char *word;
+ int field, handlingId;
+ int keepGoing;
+ tHandlingData *handling;
+
+ CFileMgr::SetDir("DATA");
+ CFileMgr::LoadFile(HandlingFilename, work_buff, sizeof(work_buff), "r");
+ CFileMgr::SetDir("");
+
+ start = (char*)work_buff;
+ end = start+1;
+ handling = nil;
+ keepGoing = 1;
+
+ while(keepGoing){
+ // find end of line
+ while(*end != '\n') end++;
+
+ // get line
+ strncpy(line, start, end - start);
+ line[end - start] = '\0';
+ start = end+1;
+ end = start+1;
+
+ // yeah, this is kinda crappy
+ if(strncmp(line, ";the end", 9) == 0)
+ keepGoing = 0;
+ else if(line[0] != ';'){
+ field = 0;
+ strcpy(delim, " \t");
+ // FIX: game seems to use a do-while loop here
+ for(word = strtok(line, delim); word; word = strtok(nil, delim)){
+ switch(field){
+ case 0:
+ handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS);
+ assert(handlingId >= 0 && handlingId < NUMHANDLINGS);
+ handling = &HandlingData[handlingId];
+ handling->nIdentifier = handlingId;
+ break;
+ case 1: handling->fMass = strtod(word, nil); break;
+ case 2: handling->Dimension.x = strtod(word, nil); break;
+ case 3: handling->Dimension.y = strtod(word, nil); break;
+ case 4: handling->Dimension.z = strtod(word, nil); break;
+ case 5: handling->CentreOfMass.x = strtod(word, nil); break;
+ case 6: handling->CentreOfMass.y = strtod(word, nil); break;
+ case 7: handling->CentreOfMass.z = strtod(word, nil); break;
+ case 8: handling->nPercentSubmerged = atoi(word); break;
+ case 9: handling->fTractionMultiplier = strtod(word, nil); break;
+ case 10: handling->fTractionLoss = strtod(word, nil); break;
+ case 11: handling->fTractionBias = strtod(word, nil); break;
+ case 12: handling->TransmissionData.nNumberOfGears = atoi(word); break;
+ case 13: handling->TransmissionData.fMaxVelocity = strtod(word, nil); break;
+ case 14: handling->TransmissionData.fEngineAcceleration = strtod(word, nil) * 0.4f; break;
+ case 15: handling->TransmissionData.nDriveType = word[0]; break;
+ case 16: handling->TransmissionData.nEngineType = word[0]; break;
+ case 17: handling->fBrakeDeceleration = strtod(word, nil); break;
+ case 18: handling->fBrakeBias = strtod(word, nil); break;
+ case 19: handling->bABS = !!atoi(word); break;
+ case 20: handling->fSteeringLock = strtod(word, nil); break;
+ case 21: handling->fSuspensionForceLevel = strtod(word, nil); break;
+ case 22: handling->fSuspensionDampingLevel = strtod(word, nil); break;
+ case 23: handling->fSeatOffsetDistance = strtod(word, nil); break;
+ case 24: handling->fCollisionDamageMultiplier = strtod(word, nil); break;
+ case 25: handling->nMonetaryValue = atoi(word); break;
+ case 26: handling->fSuspensionUpperLimit = strtod(word, nil); break;
+ case 27: handling->fSuspensionLowerLimit = strtod(word, nil); break;
+ case 28: handling->fSuspensionBias = strtod(word, nil); break;
+ case 29:
+ sscanf(word, "%x", &handling->Flags);
+ handling->TransmissionData.Flags = handling->Flags;
+ break;
+ case 30: handling->FrontLights = atoi(word); break;
+ case 31: handling->RearLights = atoi(word); break;
+ }
+ field++;
+ }
+ ConvertDataToGameUnits(handling);
+ }
+ }
+}
+
+int
+cHandlingDataMgr::FindExactWord(const char *word, const char *words, int wordLen, int numWords)
+{
+ int i;
+
+ for(i = 0; i < numWords; i++){
+ // BUG: the game does something really stupid here, it's fixed here
+ if(strncmp(word, words, wordLen) == 0)
+ return i;
+ words += wordLen;
+ }
+ return numWords;
+}
+
+
+void
+cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling)
+{
+ // TODO: figure out what exactly is being converted here
+ float velocity, a, b, specificVolume;
+
+ handling->TransmissionData.fEngineAcceleration /= 2500.0f;
+ handling->TransmissionData.fMaxVelocity /= 180.0f;
+ handling->fBrakeDeceleration /= 2500.0f;
+ handling->fTurnMass = (sq(handling->Dimension.x) + sq(handling->Dimension.y)) * handling->fMass / 12.0f;
+ if(handling->fTurnMass < 10.0f)
+ handling->fTurnMass *= 5.0f;
+ handling->fInvMass = 1.0f/handling->fMass;
+ handling->fBuoyancy = 100.0f/handling->nPercentSubmerged * 0.008*handling->fMass;
+
+ // What the hell is going on here?
+ specificVolume = handling->Dimension.x*handling->Dimension.z*0.5f / handling->fMass; // ?
+ a = 0.0f;
+ b = 100.0f;
+ velocity = handling->TransmissionData.fMaxVelocity;
+ while(a < b && velocity > 0.0f){
+ velocity -= 0.01;
+ a = handling->TransmissionData.fEngineAcceleration/6.0f;
+ b = -velocity * (1.0f/(specificVolume * sq(velocity) + 1.0f) - 1.0f);
+ }
+
+ if(handling->nIdentifier == HANDLING_RCBANDIT){
+ handling->TransmissionData.fUnkMaxVelocity = handling->TransmissionData.fMaxVelocity;
+ }else{
+ handling->TransmissionData.fUnkMaxVelocity = velocity;
+ handling->TransmissionData.fMaxVelocity = velocity * 1.2f;
+ }
+ handling->TransmissionData.fMaxReverseVelocity = -0.2f;
+
+ if(handling->TransmissionData.nDriveType == '4')
+ handling->TransmissionData.fEngineAcceleration /= 4.0f;
+ else
+ handling->TransmissionData.fEngineAcceleration /= 2.0f;
+
+ handling->TransmissionData.InitGearRatios();
+}
+
+int32
+cHandlingDataMgr::GetHandlingId(const char *name)
+{
+ int i;
+ for(i = 0; i < NUMHANDLINGS; i++)
+ if(strncmp(VehicleNames[i], name, 14) == 0)
+ break;
+ return i;
+}
+
+STARTPATCHES
+ InjectHook(0x546D80, &cHandlingDataMgr::Initialise, PATCH_JUMP);
+ InjectHook(0x546DB0, &cHandlingDataMgr::LoadHandlingData, PATCH_JUMP);
+ InjectHook(0x546BB0, &cHandlingDataMgr::ConvertDataToGameUnits, PATCH_JUMP);
+ InjectHook(0x546AA0, &cHandlingDataMgr::FindExactWord, PATCH_JUMP);
+ InjectHook(0x546B70, &cHandlingDataMgr::GetHandlingId, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/vehicles/HandlingMgr.h b/src/vehicles/HandlingMgr.h
new file mode 100644
index 00000000..958e2351
--- /dev/null
+++ b/src/vehicles/HandlingMgr.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#include "Transmission.h"
+
+enum eHandlingId
+{
+ HANDLING_LANDSTAL,
+ HANDLING_IDAHO,
+ HANDLING_STINGER,
+ HANDLING_LINERUN,
+ HANDLING_PEREN,
+ HANDLING_SENTINEL,
+ HANDLING_PATRIOT,
+ HANDLING_FIRETRUK,
+ HANDLING_TRASH,
+ HANDLING_STRETCH,
+ HANDLING_MANANA,
+ HANDLING_INFERNUS,
+ HANDLING_BLISTA,
+ HANDLING_PONY,
+ HANDLING_MULE,
+ HANDLING_CHEETAH,
+ HANDLING_AMBULAN,
+ HANDLING_FBICAR,
+ HANDLING_MOONBEAM,
+ HANDLING_ESPERANT,
+ HANDLING_TAXI,
+ HANDLING_KURUMA,
+ HANDLING_BOBCAT,
+ HANDLING_MRWHOOP,
+ HANDLING_BFINJECT,
+ HANDLING_POLICE,
+ HANDLING_ENFORCER,
+ HANDLING_SECURICA,
+ HANDLING_BANSHEE,
+ HANDLING_PREDATOR,
+ HANDLING_BUS,
+ HANDLING_RHINO,
+ HANDLING_BARRACKS,
+ HANDLING_TRAIN,
+ HANDLING_HELI,
+ HANDLING_DODO,
+ HANDLING_COACH,
+ HANDLING_CABBIE,
+ HANDLING_STALLION,
+ HANDLING_RUMPO,
+ HANDLING_RCBANDIT,
+ HANDLING_BELLYUP,
+ HANDLING_MRWONGS,
+ HANDLING_MAFIA,
+ HANDLING_YARDIE,
+ HANDLING_YAKUZA,
+ HANDLING_DIABLOS,
+ HANDLING_COLUMB,
+ HANDLING_HOODS,
+ HANDLING_AIRTRAIN,
+ HANDLING_DEADDODO,
+ HANDLING_SPEEDER,
+ HANDLING_REEFER,
+ HANDLING_PANLANT,
+ HANDLING_FLATBED,
+ HANDLING_YANKEE,
+ HANDLING_BORGNINE
+};
+
+enum
+{
+ HANDLING_1G_BOOST = 1,
+ HANDLING_2G_BOOST = 2,
+ HANDLING_REV_BONNET = 4,
+ HANDLING_HANGING_BOOT = 8,
+ HANDLING_NO_DOORS = 0x10,
+ HANDLING_IS_VAN = 0x20,
+ HANDLING_IS_BUS = 0x40,
+ HANDLING_IS_LOW = 0x80,
+ HANDLING_DBL_EXHAUST = 0x100,
+ HANDLING_TAILGATE_BOOT = 0x200,
+ HANDLING_NOSWING_BOOT = 0x400,
+ HANDLING_NONPLAYER_STABILISER = 0x800,
+ HANDLING_NEUTRALHANDLING = 0x1000,
+ HANDLING_HAS_NO_ROOF = 0x2000,
+ HANDLING_IS_BIG = 0x4000,
+ HANDLING_HALOGEN_LIGHTS = 0x8000,
+};
+
+struct tHandlingData
+{
+ int32 nIdentifier;
+ float fMass;
+ float fInvMass;
+ float fTurnMass;
+ CVector Dimension;
+ CVector CentreOfMass;
+ int8 nPercentSubmerged;
+ float fBuoyancy;
+ float fTractionMultiplier;
+ cTransmission TransmissionData;
+ float fBrakeDeceleration;
+ float fBrakeBias;
+ int8 bABS;
+ float fSteeringLock;
+ float fTractionLoss;
+ float fTractionBias;
+ uint32 field_AC;
+ float fSuspensionForceLevel;
+ float fSuspensionDampingLevel;
+ float fSuspensionUpperLimit;
+ float fSuspensionLowerLimit;
+ float fSuspensionBias;
+ float fCollisionDamageMultiplier;
+ uint32 Flags;
+ float fSeatOffsetDistance;
+ int32 nMonetaryValue;
+ int8 FrontLights;
+ int8 RearLights;
+};
+VALIDATE_SIZE(tHandlingData, 0xD8);
+
+class cHandlingDataMgr
+{
+ float field_0; // unused it seems
+ float field_4; // wheel related
+ float field_8; //
+ float field_C; // unused it seems
+ float field_10; //
+ tHandlingData HandlingData[NUMHANDLINGS];
+ uint32 field_302C; // unused it seems, padding?
+
+public:
+ cHandlingDataMgr(void);
+ void Initialise(void);
+ void LoadHandlingData(void);
+ int FindExactWord(const char *word, const char *words, int wordLen, int numWords);
+ void ConvertDataToGameUnits(tHandlingData *handling);
+ int32 GetHandlingId(const char *name);
+ tHandlingData *GetHandlingData(eHandlingId id) { return &HandlingData[id]; }
+};
+VALIDATE_SIZE(cHandlingDataMgr, 0x3030);
+extern cHandlingDataMgr &mod_HandlingManager;
diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp
new file mode 100644
index 00000000..01ee5375
--- /dev/null
+++ b/src/vehicles/Heli.cpp
@@ -0,0 +1,15 @@
+#include "common.h"
+#include "patcher.h"
+#include "Heli.h"
+
+CHeli::CHeli(int mi, uint8 owner)
+{
+ ctor(mi, owner);
+}
+
+WRAPPER CHeli* CHeli::ctor(int, uint8) { EAXJMP(0x547220); }
+WRAPPER void CHeli::SpecialHeliPreRender(void) { EAXJMP(0x54AE10); }
+
+STARTPATCHES
+InjectHook(0x5474A0, &CHeli::dtor, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/vehicles/Heli.h b/src/vehicles/Heli.h
new file mode 100644
index 00000000..da7bb171
--- /dev/null
+++ b/src/vehicles/Heli.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Vehicle.h"
+
+class CHeli : public CVehicle
+{
+public:
+ // 0x288
+ uint8 stuff[180];
+
+ CHeli(int, uint8);
+ CHeli* ctor(int, uint8);
+ void dtor(void) { this->CHeli::~CHeli(); }
+
+ static void SpecialHeliPreRender(void);
+};
+static_assert(sizeof(CHeli) == 0x33C, "CHeli: error");
diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp
new file mode 100644
index 00000000..6e30bced
--- /dev/null
+++ b/src/vehicles/Plane.cpp
@@ -0,0 +1,19 @@
+#include "common.h"
+#include "patcher.h"
+#include "Plane.h"
+
+CPlane::CPlane(int mi, uint8 owner)
+{
+ ctor(mi, owner);
+}
+
+WRAPPER CPlane* CPlane::ctor(int, uint8) { EAXJMP(0x54B170); }
+
+CPlane::~CPlane()
+{
+ DeleteRwObject();
+}
+
+STARTPATCHES
+InjectHook(0x54B270, &CPlane::dtor, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/vehicles/Plane.h b/src/vehicles/Plane.h
new file mode 100644
index 00000000..e26008f6
--- /dev/null
+++ b/src/vehicles/Plane.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "common.h"
+#include "Vehicle.h"
+
+class CPlane : public CVehicle
+{
+public:
+ // 0x288
+ uint8 stuff[20];
+
+ CPlane(int, uint8);
+ ~CPlane(void);
+ CPlane* ctor(int, uint8);
+ void dtor(void) { this->CPlane::~CPlane(); }
+ void FlagToDestroyWhenNextProcessed() { bRemoveFromWorld = true; }
+};
+static_assert(sizeof(CPlane) == 0x29C, "CPlane: error");
diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp
new file mode 100644
index 00000000..62fd53ec
--- /dev/null
+++ b/src/vehicles/Train.cpp
@@ -0,0 +1,14 @@
+#include "common.h"
+#include "patcher.h"
+#include "Train.h"
+
+CTrain::CTrain(int mi, uint8 owner)
+{
+ ctor(mi, owner);
+}
+
+WRAPPER CTrain* CTrain::ctor(int, uint8) { EAXJMP(0x54E2A0); }
+
+STARTPATCHES
+InjectHook(0x54E450, &CTrain::dtor, PATCH_JUMP);
+ENDPATCHES \ No newline at end of file
diff --git a/src/vehicles/Train.h b/src/vehicles/Train.h
new file mode 100644
index 00000000..84b6faf5
--- /dev/null
+++ b/src/vehicles/Train.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "common.h"
+#include "patcher.h"
+#include "Vehicle.h"
+
+enum
+{
+ TRAIN_DOOR_STATE2 = 2
+};
+
+class CTrain : public CVehicle
+{
+public:
+ // 0x288
+ uint8 stuff1[20];
+ uint8 m_trackId;
+ uint8 stuff2[7];
+ int16 m_doorState;
+ uint8 stuff3[62];
+
+ CTrain(int, uint8);
+ CTrain* ctor(int, uint8);
+ void dtor(void) { this->CTrain::~CTrain(); }
+};
+static_assert(sizeof(CTrain) == 0x2E4, "CTrain: error");
diff --git a/src/vehicles/Transmission.cpp b/src/vehicles/Transmission.cpp
new file mode 100644
index 00000000..2be25cbb
--- /dev/null
+++ b/src/vehicles/Transmission.cpp
@@ -0,0 +1,37 @@
+#include "common.h"
+#include "patcher.h"
+#include "Transmission.h"
+
+void
+cTransmission::InitGearRatios(void)
+{
+ static tGear *pGearRatio0 = nil;
+ static tGear *pGearRatio1 = nil;
+ int i;
+ float velocityDiff;
+
+ memset(Gears, 0, sizeof(Gears));
+
+ for(i = 1; i <= nNumberOfGears; i++){
+ pGearRatio0 = &Gears[i-1];
+ pGearRatio1 = &Gears[i];
+
+ pGearRatio1->fMaxVelocity = (float)i / nNumberOfGears * fMaxVelocity;
+
+ velocityDiff = pGearRatio1->fMaxVelocity - pGearRatio0->fMaxVelocity;
+
+ if(i >= nNumberOfGears){
+ pGearRatio1->fShiftUpVelocity = fMaxVelocity;
+ }else{
+ Gears[i+1].fShiftDownVelocity = velocityDiff*0.42f + pGearRatio0->fMaxVelocity;
+ pGearRatio1->fShiftUpVelocity = velocityDiff*0.6667f + pGearRatio0->fMaxVelocity;
+ }
+ }
+
+ // Reverse gear
+ Gears[0].fMaxVelocity = fMaxReverseVelocity;
+ Gears[0].fShiftUpVelocity = -0.01f;
+ Gears[0].fShiftDownVelocity = fMaxReverseVelocity;
+
+ Gears[1].fShiftDownVelocity = -0.01f;
+}
diff --git a/src/vehicles/Transmission.h b/src/vehicles/Transmission.h
new file mode 100644
index 00000000..686e0aca
--- /dev/null
+++ b/src/vehicles/Transmission.h
@@ -0,0 +1,26 @@
+#pragma once
+
+struct tGear
+{
+ float fMaxVelocity;
+ float fShiftUpVelocity;
+ float fShiftDownVelocity;
+};
+
+class cTransmission
+{
+public:
+ // Gear 0 is reverse, 1-5 are forward
+ tGear Gears[6];
+ char nDriveType;
+ char nEngineType;
+ int8 nNumberOfGears;
+ uint8 Flags;
+ float fEngineAcceleration;
+ float fMaxVelocity;
+ float fUnkMaxVelocity;
+ float fMaxReverseVelocity;
+ float field_5C;
+
+ void InitGearRatios(void);
+};
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
new file mode 100644
index 00000000..dccd9195
--- /dev/null
+++ b/src/vehicles/Vehicle.cpp
@@ -0,0 +1,489 @@
+#include "common.h"
+#include "main.h"
+#include "patcher.h"
+#include "Timer.h"
+#include "Vehicle.h"
+#include "Pools.h"
+#include "HandlingMgr.h"
+#include "CarCtrl.h"
+#include "Population.h"
+#include "ModelIndices.h"
+#include "World.h"
+#include "Lights.h"
+#include "PointLights.h"
+#include "Renderer.h"
+#include "DMAudio.h"
+#include "Radar.h"
+
+bool &CVehicle::bWheelsOnlyCheat = *(bool *)0x95CD78;
+bool &CVehicle::bAllDodosCheat = *(bool *)0x95CD75;
+bool &CVehicle::bCheat3 = *(bool *)0x95CD66;
+bool &CVehicle::bCheat4 = *(bool *)0x95CD65;
+bool &CVehicle::bCheat5 = *(bool *)0x95CD64;
+bool &CVehicle::m_bDisableMouseSteering = *(bool *)0x60252C;
+
+void *CVehicle::operator new(size_t sz) { return CPools::GetVehiclePool()->New(); }
+void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehiclePool()->New(handle); }
+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); }
+
+CVehicle::~CVehicle()
+{
+ m_nAlarmState = 0;
+ if (m_audioEntityId >= 0){
+ DMAudio.DestroyEntity(m_audioEntityId);
+ m_audioEntityId = -5;
+ }
+ CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(this));
+ if (pDriver)
+ pDriver->FlagToDestroyWhenNextProcessed();
+ for (int i = 0; i < m_nNumMaxPassengers; i++){
+ if (pPassengers[i])
+ pPassengers[i]->FlagToDestroyWhenNextProcessed();
+ }
+ if (m_pCarFire)
+ m_pCarFire->Extinguish();
+ CCarCtrl::UpdateCarCount(this, true);
+ if (bIsAmbulanceOnDuty){
+ CCarCtrl::NumAmbulancesOnDuty--;
+ bIsAmbulanceOnDuty = false;
+ }
+ if (bIsFireTruckOnDuty){
+ CCarCtrl::NumFiretrucksOnDuty--;
+ bIsFireTruckOnDuty = false;
+ }
+}
+
+void
+CVehicle::SetModelIndex(uint32 id)
+{
+ CEntity::SetModelIndex(id);
+ m_aExtras[0] = CVehicleModelInfo::ms_compsUsed[0];
+ m_aExtras[1] = CVehicleModelInfo::ms_compsUsed[1];
+ m_nNumMaxPassengers = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(id);
+}
+
+bool
+CVehicle::SetupLighting(void)
+{
+ ActivateDirectional();
+ SetAmbientColoursForPedsCarsAndObjects();
+
+ if(bRenderScorched){
+ WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f);
+ }else{
+ CVector coors = GetPosition();
+ float lighting = CPointLights::GenerateLightsAffectingObject(&coors);
+ if(!bHasBlip && lighting != 1.0f){
+ SetAmbientAndDirectionalColours(lighting);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+CVehicle::RemoveLighting(bool reset)
+{
+ CRenderer::RemoveVehiclePedLights(this, reset);
+}
+
+float
+CVehicle::GetHeightAboveRoad(void)
+{
+ return -1.0f * CModelInfo::GetModelInfo(GetModelIndex())->GetColModel()->boundingBox.min.z;
+}
+
+
+bool
+CVehicle::IsLawEnforcementVehicle(void)
+{
+ switch(GetModelIndex()){
+ case MI_FBICAR:
+ case MI_POLICE:
+ case MI_ENFORCER:
+ case MI_PREDATOR:
+ case MI_RHINO:
+ case MI_BARRACKS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+CVehicle::UsesSiren(uint32 id)
+{
+ switch(id){
+ case MI_FIRETRUCK:
+ case MI_AMBULAN:
+ case MI_FBICAR:
+ case MI_MRWHOOP:
+ case MI_POLICE:
+ case MI_ENFORCER:
+ case MI_PREDATOR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+CVehicle::IsVehicleNormal(void)
+{
+ if(pDriver && m_nNumPassengers == 0 && m_status != STATUS_WRECKED){
+ switch(GetModelIndex())
+ case MI_FIRETRUCK:
+ case MI_AMBULAN:
+ case MI_TAXI:
+ case MI_POLICE:
+ case MI_ENFORCER:
+ case MI_BUS:
+ case MI_RHINO:
+ case MI_BARRACKS:
+ case MI_DODO:
+ case MI_COACH:
+ case MI_CABBIE:
+ case MI_RCBANDIT:
+ case MI_BORGNINE:
+ return false;
+ }
+ return false;
+}
+
+bool
+CVehicle::CarHasRoof(void)
+{
+ if((m_handling->Flags & HANDLING_HAS_NO_ROOF) == 0)
+ return true;
+ if(m_aExtras[0] && m_aExtras[1])
+ return false;
+ return true;
+}
+
+bool
+CVehicle::IsUpsideDown(void)
+{
+ if(GetUp().z > -0.9f)
+ return false;
+ return true;
+}
+
+bool
+CVehicle::IsOnItsSide(void)
+{
+ if(GetRight().z < 0.8f && GetRight().z > -0.8f)
+ return false;
+ return true;
+}
+
+bool
+CVehicle::CanBeDeleted(void)
+{
+ int i;
+
+ if(m_nNumGettingIn || m_nGettingOutFlags)
+ return false;
+
+ if(pDriver){
+ // This looks like it was inlined
+ if(pDriver->CharCreatedBy == MISSION_CHAR)
+ return false;
+ if(pDriver->GetPedState() != PED_DRIVING &&
+ pDriver->GetPedState() != PED_DEAD)
+ return false;
+ }
+
+ for(i = 0; i < 8; i++){
+ // Same check as above
+ if(pPassengers[i]){
+ if(pPassengers[i]->CharCreatedBy == MISSION_CHAR)
+ return false;
+ if(pPassengers[i]->GetPedState() != PED_DRIVING &&
+ pPassengers[i]->GetPedState() != PED_DEAD)
+ return false;
+ }
+ // and then again... probably because something was inlined
+ if(pPassengers[i]){
+ if(pPassengers[i]->GetPedState() != PED_DRIVING &&
+ pPassengers[i]->GetPedState() != PED_DEAD)
+ return false;
+ }
+ }
+
+ switch(VehicleCreatedBy){
+ case RANDOM_VEHICLE: return true;
+ case MISSION_VEHICLE: return false;
+ case PARKED_VEHICLE: return true;
+ case PERMANENT_VEHICLE: return false;
+ }
+ return true;
+}
+
+bool
+CVehicle::CanPedOpenLocks(CPed *ped)
+{
+ if(m_nDoorLock == CARLOCK_LOCKED ||
+ m_nDoorLock == CARLOCK_COP_CAR ||
+ m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE)
+ return false;
+ if(ped->IsPlayer() && m_nDoorLock == CARLOCK_LOCKOUT_PLAYER_ONLY)
+ return false;
+ return true;
+}
+
+bool
+CVehicle::CanPedEnterCar(void)
+{
+ CVector up = GetUp();
+ // can't enter when car is on side
+ if(up.z > 0.1f || up.z < -0.1f){
+ // also when car is moving too fast
+ if(m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f))
+ return false;
+ if(m_vecTurnSpeed.MagnitudeSqr() > sq(0.2f))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+bool
+CVehicle::CanPedExitCar(void)
+{
+ CVector up = GetUp();
+ if(up.z > 0.1f || up.z < -0.1f){
+ // can't exit when car is moving too fast
+ if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f)
+ return false;
+ // if car is slow enough, check turn speed
+ if(fabs(m_vecTurnSpeed.x) > 0.01f ||
+ fabs(m_vecTurnSpeed.y) > 0.01f ||
+ fabs(m_vecTurnSpeed.z) > 0.01f)
+ return false;
+ return true;
+ }else{
+ // What is this? just > replaced by >= ??
+
+ // can't exit when car is moving too fast
+ if(m_vecMoveSpeed.MagnitudeSqr() >= 0.005f)
+ return false;
+ // if car is slow enough, check turn speed
+ if(fabs(m_vecTurnSpeed.x) >= 0.01f ||
+ fabs(m_vecTurnSpeed.y) >= 0.01f ||
+ fabs(m_vecTurnSpeed.z) >= 0.01f)
+ return false;
+ return true;
+ }
+}
+
+void
+CVehicle::ChangeLawEnforcerState(uint8 enable)
+{
+ if (enable) {
+ if (!bIsLawEnforcer) {
+ bIsLawEnforcer = true;
+ CCarCtrl::NumLawEnforcerCars++;
+ }
+ } else {
+ if (bIsLawEnforcer) {
+ bIsLawEnforcer = false;
+ CCarCtrl::NumLawEnforcerCars--;
+ }
+ }
+}
+
+CPed*
+CVehicle::SetUpDriver(void)
+{
+ if(pDriver)
+ return pDriver;
+ if(VehicleCreatedBy != RANDOM_VEHICLE)
+ return nil;
+
+ pDriver = CPopulation::AddPedInCar(this);
+ pDriver->m_pMyVehicle = this;
+ pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle);
+ pDriver->bInVehicle = true;
+ pDriver->SetPedState(PED_DRIVING);
+ if(bIsBus)
+ pDriver->m_ped_flagC4 = false;
+ return pDriver;
+}
+
+CPed*
+CVehicle::SetupPassenger(int n)
+{
+ if(pPassengers[n])
+ return pPassengers[n];
+
+ pPassengers[n] = CPopulation::AddPedInCar(this);
+ pPassengers[n]->m_pMyVehicle = this;
+ pPassengers[n]->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle);
+ pPassengers[n]->bInVehicle = true;
+ pPassengers[n]->SetPedState(PED_DRIVING);
+ if(bIsBus)
+ pPassengers[n]->m_ped_flagC4 = false;
+ return pPassengers[n];
+}
+
+void
+CVehicle::SetDriver(CPed *driver)
+{
+ pDriver = driver;
+ pDriver->RegisterReference((CEntity**)&pDriver);
+
+ if(bFreebies && driver == FindPlayerPed()){
+ if(GetModelIndex() == MI_AMBULAN)
+ FindPlayerPed()->m_fHealth = min(FindPlayerPed()->m_fHealth + 20.0f, 100.0f);
+ else if(GetModelIndex() == MI_TAXI)
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25;
+ else if(GetModelIndex() == MI_POLICE)
+ driver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5);
+ else if(GetModelIndex() == MI_ENFORCER)
+ driver->m_fArmour = max(driver->m_fArmour, 100.0f);
+ else if(GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE)
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25;
+ bFreebies = false;
+ }
+
+ ApplyTurnForce(0.0f, 0.0f, -0.2f*driver->m_fMass,
+ driver->GetPosition().x - GetPosition().x,
+ driver->GetPosition().y - GetPosition().y,
+ 0.0f);
+}
+
+bool
+CVehicle::AddPassenger(CPed *passenger)
+{
+ int i;
+
+ ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass,
+ passenger->GetPosition().x - GetPosition().x,
+ passenger->GetPosition().y - GetPosition().y,
+ 0.0f);
+
+ for(i = 0; i < m_nNumMaxPassengers; i++)
+ if(pPassengers[i] == nil){
+ pPassengers[i] = passenger;
+ m_nNumPassengers++;
+ return true;
+ }
+ return false;
+}
+
+bool
+CVehicle::AddPassenger(CPed *passenger, uint8 n)
+{
+ if(bIsBus)
+ return AddPassenger(passenger);
+
+ ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass,
+ passenger->GetPosition().x - GetPosition().x,
+ passenger->GetPosition().y - GetPosition().y,
+ 0.0f);
+
+ if(n < m_nNumMaxPassengers && pPassengers[n] == nil){
+ pPassengers[n] = passenger;
+ m_nNumPassengers++;
+ return true;
+ }
+ return false;
+}
+
+void
+CVehicle::RemoveDriver(void)
+{
+ m_status = STATUS_ABANDONED;
+ pDriver = nil;
+}
+
+void
+CVehicle::RemovePassenger(CPed *p)
+{
+ if (IsTrain()){
+ for (int i = 0; i < 8; i++){
+ if (pPassengers[i] == p) {
+ pPassengers[i] = nil;
+ m_nNumPassengers--;
+ return;
+ }
+ }
+ return;
+ }
+ for (int i = 0; i < m_nNumMaxPassengers; i++){
+ if (pPassengers[i] == p){
+ pPassengers[i] = nil;
+ m_nNumPassengers--;
+ return;
+ }
+ }
+}
+
+void
+CVehicle::ProcessCarAlarm(void)
+{
+ uint32 step;
+
+ if(m_nAlarmState == 0 || m_nAlarmState == -1)
+ return;
+
+ step = CTimer::GetTimeStepInMilliseconds();
+ if((uint16)m_nAlarmState < step)
+ m_nAlarmState = 0;
+ else
+ m_nAlarmState -= step;
+}
+
+bool
+CVehicle::IsSphereTouchingVehicle(float sx, float sy, float sz, float radius)
+{
+ float x, y, z;
+ // sphere relative to vehicle
+ CVector sph = CVector(sx, sy, sz) - GetPosition();
+ CColModel *colmodel = CModelInfo::GetModelInfo(GetModelIndex())->GetColModel();
+
+ x = DotProduct(sph, GetRight());
+ if(colmodel->boundingBox.min.x - radius > x ||
+ colmodel->boundingBox.max.x + radius < x)
+ return false;
+ y = DotProduct(sph, GetForward());
+ if(colmodel->boundingBox.min.y - radius > y ||
+ colmodel->boundingBox.max.y + radius < y)
+ return false;
+ z = DotProduct(sph, GetUp());
+ if(colmodel->boundingBox.min.z - radius > z ||
+ colmodel->boundingBox.max.z + radius < z)
+ return false;
+
+ return true;
+}
+
+STARTPATCHES
+ InjectHook(0x551170, &CVehicle::SetModelIndex_, PATCH_JUMP);
+ InjectHook(0x4A7DD0, &CVehicle::SetupLighting_, PATCH_JUMP);
+ InjectHook(0x4A7E60, &CVehicle::RemoveLighting_, PATCH_JUMP);
+ InjectHook(0x417E60, &CVehicle::GetHeightAboveRoad_, PATCH_JUMP);
+
+ InjectHook(0x552880, &CVehicle::IsLawEnforcementVehicle, PATCH_JUMP);
+ InjectHook(0x552820, &CVehicle::ChangeLawEnforcerState, PATCH_JUMP);
+ InjectHook(0x552200, &CVehicle::UsesSiren, PATCH_JUMP);
+ InjectHook(0x5527E0, &CVehicle::IsVehicleNormal, PATCH_JUMP);
+ InjectHook(0x552B70, &CVehicle::CarHasRoof, PATCH_JUMP);
+ InjectHook(0x552230, &CVehicle::IsUpsideDown, PATCH_JUMP);
+ InjectHook(0x552260, &CVehicle::IsOnItsSide, PATCH_JUMP);
+ InjectHook(0x5511B0, &CVehicle::CanBeDeleted, PATCH_JUMP);
+ InjectHook(0x5522A0, &CVehicle::CanPedOpenLocks, PATCH_JUMP);
+ InjectHook(0x5522F0, &CVehicle::CanPedEnterCar, PATCH_JUMP);
+ InjectHook(0x5523C0, &CVehicle::CanPedExitCar, PATCH_JUMP);
+ InjectHook(0x5520C0, &CVehicle::SetUpDriver, PATCH_JUMP);
+ InjectHook(0x552160, &CVehicle::SetupPassenger, PATCH_JUMP);
+ InjectHook(0x551F20, &CVehicle::SetDriver, PATCH_JUMP);
+ InjectHook(0x551D90, (bool (CVehicle::*)(CPed*))&CVehicle::AddPassenger, PATCH_JUMP);
+ InjectHook(0x551E10, (bool (CVehicle::*)(CPed*,uint8))&CVehicle::AddPassenger, PATCH_JUMP);
+ InjectHook(0x5520A0, &CVehicle::RemoveDriver, PATCH_JUMP);
+ InjectHook(0x551EB0, &CVehicle::RemovePassenger, PATCH_JUMP);
+ InjectHook(0x5525A0, &CVehicle::ProcessCarAlarm, PATCH_JUMP);
+ InjectHook(0x552620, &CVehicle::IsSphereTouchingVehicle, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
new file mode 100644
index 00000000..39a56fe0
--- /dev/null
+++ b/src/vehicles/Vehicle.h
@@ -0,0 +1,240 @@
+#pragma once
+
+#include "Physical.h"
+#include "AutoPilot.h"
+
+class CPed;
+class CFire;
+struct tHandlingData;
+
+enum {
+ RANDOM_VEHICLE = 1,
+ MISSION_VEHICLE = 2,
+ PARKED_VEHICLE = 3,
+ PERMANENT_VEHICLE = 4,
+};
+
+enum eCarLock {
+ CARLOCK_NOT_USED,
+ CARLOCK_UNLOCKED,
+ CARLOCK_LOCKED,
+ CARLOCK_LOCKOUT_PLAYER_ONLY,
+ CARLOCK_LOCKED_PLAYER_INSIDE,
+ CARLOCK_COP_CAR,
+ CARLOCK_FORCE_SHUT_DOORS,
+ CARLOCK_SKIP_SHUT_DOORS
+};
+
+
+enum eCarNodes
+{
+ CAR_WHEEL_RF = 1,
+ CAR_WHEEL_RM,
+ CAR_WHEEL_RB,
+ CAR_WHEEL_LF,
+ CAR_WHEEL_LM,
+ CAR_WHEEL_LB,
+ CAR_BUMP_FRONT,
+ CAR_BUMP_REAR,
+ CAR_WING_RF,
+ CAR_WING_RR,
+ CAR_DOOR_RF,
+ CAR_DOOR_RR,
+ CAR_WING_LF,
+ CAR_WING_LR,
+ CAR_DOOR_LF,
+ CAR_DOOR_LR,
+ CAR_BONNET,
+ CAR_BOOT,
+ CAR_WINDSCREEN,
+ NUM_CAR_NODES,
+};
+
+enum
+{
+ CAR_POS_HEADLIGHTS,
+ CAR_POS_TAILLIGHTS,
+ CAR_POS_FRONTSEAT,
+ CAR_POS_BACKSEAT,
+ CAR_POS_EXHAUST = 9,
+};
+
+enum eDoors
+{
+ DOOR_BONNET = 0,
+ DOOR_BOOT,
+ DOOR_FRONT_LEFT,
+ DOOR_FRONT_RIGHT,
+ DOOR_REAR_LEFT,
+ DOOR_REAR_RIGHT
+};
+
+class CVehicle : public CPhysical
+{
+public:
+ // 0x128
+ tHandlingData *m_handling;
+ CAutoPilot m_autoPilot;
+ uint8 m_currentColour1;
+ uint8 m_currentColour2;
+ uint8 m_aExtras[2];
+ int16 m_nAlarmState; // m_nWantedStarsOnEnter on DK22
+ int16 m_nMissionValue;
+ CPed *pDriver;
+ CPed *pPassengers[8];
+ uint8 m_nNumPassengers;
+ int8 m_nNumGettingIn;
+ int8 m_nGettingInFlags;
+ int8 m_nGettingOutFlags;
+ uint8 m_nNumMaxPassengers;
+ char field_1CD[19];
+ CEntity *m_pCurSurface;
+ CFire *m_pCarFire;
+ float m_fSteerAngle;
+ float m_fGasPedal;
+ float m_fBreakPedal;
+ uint8 VehicleCreatedBy;
+
+ // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R*
+ uint8 bIsLawEnforcer: 1; // Is this guy chasing the player at the moment
+ uint8 bIsAmbulanceOnDuty: 1; // Ambulance trying to get to an accident
+ uint8 bIsFireTruckOnDuty: 1; // Firetruck trying to get to a fire
+ uint8 bIsLocked: 1; // Is this guy locked by the script (cannot be removed)
+ uint8 bEngineOn: 1; // For sound purposes. Parked cars have their engines switched off (so do destroyed cars)
+ uint8 bIsHandbrakeOn: 1; // How's the handbrake doing ?
+ uint8 bLightsOn: 1; // Are the lights switched on ?
+ uint8 bFreebies: 1; // Any freebies left in this vehicle ?
+
+ uint8 bIsVan: 1; // Is this vehicle a van (doors at back of vehicle)
+ uint8 bIsBus: 1; // Is this vehicle a bus
+ uint8 bIsBig: 1; // Is this vehicle a bus
+ uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims
+ uint8 m_veh_flagB10 : 1;
+ uint8 m_veh_flagB20 : 1;
+ uint8 m_veh_flagB40 : 1;
+ uint8 m_veh_flagB80 : 1;
+
+ uint8 m_veh_flagC1 : 1;
+ uint8 m_veh_flagC2 : 1;
+ uint8 m_veh_flagC4 : 1;
+ uint8 m_veh_flagC8 : 1;
+ uint8 m_veh_flagC10 : 1;
+ uint8 m_veh_flagC20 : 1;
+ uint8 m_veh_flagC40 : 1;
+ uint8 m_veh_flagC80 : 1;
+
+ uint8 m_veh_flagD1 : 1;
+ uint8 m_veh_flagD2 : 1;
+ uint8 m_veh_flagD4 : 1;
+ uint8 m_veh_flagD8 : 1;
+ uint8 bRecordedForReplay : 1;
+ uint8 m_veh_flagD20 : 1;
+ uint8 m_veh_flagD40 : 1;
+ uint8 m_veh_flagD80 : 1;
+
+ int8 field_1F9;
+ uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default)
+ int8 field_1FB;
+ int8 field_1FC[4];
+ float m_fHealth; // 1000.0f = full health. 0 -> explode
+ uint8 m_nCurrentGear;
+ int8 field_205[3];
+ int field_208;
+ uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats)
+ uint32 m_nTimeOfDeath;
+ int16 field_214;
+ int16 m_nBombTimer; // goes down with each frame
+ CPed *m_pWhoDetonatedMe;
+ float field_21C;
+ float field_220;
+ eCarLock m_nDoorLock;
+ int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage
+ int8 m_nRadioStation;
+ int8 field_22A;
+ int8 field_22B;
+ uint8 m_nCarHornTimer;
+ int8 field_22D;
+ uint8 m_nSirenOrAlarm;
+ int8 field_22F;
+ // TODO: this is an array
+ CStoredCollPoly m_frontCollPoly; // poly which is under front part of car
+ CStoredCollPoly m_rearCollPoly; // poly which is under rear part of car
+ float m_fSteerRatio;
+ eVehicleType m_vehType;
+
+ static void *operator new(size_t);
+ static void *operator new(size_t sz, int slot);
+ static void operator delete(void*, size_t);
+ static void operator delete(void*, int);
+
+ ~CVehicle(void);
+ // from CEntity
+ void SetModelIndex(uint32 i);
+ bool SetupLighting(void);
+ void RemoveLighting(bool);
+ void FlagToDestroyWhenNextProcessed(void) {}
+
+ virtual void ProcessControlInputs(uint8) {}
+ virtual void GetComponentWorldPosition(int32 component, CVector &pos) {}
+ virtual bool IsComponentPresent(int32 component) { return false; }
+ virtual void SetComponentRotation(int32 component, CVector rotation) {}
+ virtual void OpenDoor(int32, eDoors door, float) {}
+ virtual void ProcessOpenDoor(uint32, uint32, float) {}
+ virtual bool IsDoorReady(eDoors door) { return false; }
+ virtual bool IsDoorFullyOpen(eDoors door) { return false; }
+ virtual bool IsDoorClosed(eDoors door) { return false; }
+ virtual bool IsDoorMissing(eDoors door) { return false; }
+ virtual void RemoveRefsToVehicle(CEntity *ent) {}
+ virtual void BlowUpCar(CEntity *ent) {}
+ virtual bool SetUpWheelColModel(CColModel *colModel) { return false; }
+ virtual void BurstTyre(uint8 tyre) {}
+ virtual bool IsRoomForPedToLeaveCar(uint32, CVector *) { return false;}
+ virtual float GetHeightAboveRoad(void);
+ virtual void PlayCarHorn(void) {}
+
+ bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; }
+ bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; }
+ bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; }
+ bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; }
+ bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; }
+ bool IsLawEnforcementVehicle(void);
+ void ChangeLawEnforcerState(uint8 enable);
+ bool UsesSiren(uint32 id);
+ bool IsVehicleNormal(void);
+ bool CarHasRoof(void);
+ bool IsUpsideDown(void);
+ bool IsOnItsSide(void);
+ bool CanBeDeleted(void);
+ bool CanPedOpenLocks(CPed *ped);
+ bool CanPedEnterCar(void);
+ bool CanPedExitCar(void);
+ // do these two actually return something?
+ CPed *SetUpDriver(void);
+ CPed *SetupPassenger(int n);
+ void SetDriver(CPed *driver);
+ bool AddPassenger(CPed *passenger);
+ bool AddPassenger(CPed *passenger, uint8 n);
+ void RemovePassenger(CPed *passenger);
+ void RemoveDriver(void);
+ void ProcessCarAlarm(void);
+ bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius);
+
+ static bool &bWheelsOnlyCheat;
+ static bool &bAllDodosCheat;
+ static bool &bCheat3;
+ static bool &bCheat4;
+ static bool &bCheat5;
+ static bool &m_bDisableMouseSteering;
+
+
+ void dtor(void) { CVehicle::~CVehicle(); }
+ void SetModelIndex_(uint32 id) { CVehicle::SetModelIndex(id); }
+ bool SetupLighting_(void) { return CVehicle::SetupLighting(); }
+ void RemoveLighting_(bool reset) { CVehicle::RemoveLighting(reset); }
+ float GetHeightAboveRoad_(void) { return CVehicle::GetHeightAboveRoad(); }
+};
+
+static_assert(sizeof(CVehicle) == 0x288, "CVehicle: error");
+static_assert(offsetof(CVehicle, m_pCurSurface) == 0x1E0, "CVehicle: error");
+static_assert(offsetof(CVehicle, m_nAlarmState) == 0x1A0, "CVehicle: error");
+static_assert(offsetof(CVehicle, m_nLastWeaponDamage) == 0x228, "CVehicle: error");