summaryrefslogtreecommitdiffstats
path: root/src/control/CarCtrl.cpp
diff options
context:
space:
mode:
authorNikolay Korolev <nickvnuk@gmail.com>2019-09-15 01:28:55 +0200
committerNikolay Korolev <nickvnuk@gmail.com>2019-09-15 01:28:55 +0200
commit8355a6dccd8f1acce398b42e0e1963c96accac60 (patch)
tree6e3b9e8bb1f43b4952ed5f4910e3d7f05d47037f /src/control/CarCtrl.cpp
parentMerge pull request #207 from Nick007J/master (diff)
parentCCarCtrl (diff)
downloadre3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar.gz
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar.bz2
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar.lz
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar.xz
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.tar.zst
re3-8355a6dccd8f1acce398b42e0e1963c96accac60.zip
Diffstat (limited to 'src/control/CarCtrl.cpp')
-rw-r--r--src/control/CarCtrl.cpp359
1 files changed, 311 insertions, 48 deletions
diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index f89b9306..cae010d2 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -2,6 +2,7 @@
#include "patcher.h"
#include "CarCtrl.h"
+#include "AccidentManager.h"
#include "Automobile.h"
#include "Camera.h"
#include "CarAI.h"
@@ -81,21 +82,14 @@ int32 &CCarCtrl::NumPermanentCars = *(int32*)0x8F29F0;
int8 &CCarCtrl::CountDownToCarsAtStart = *(int8*)0x95CD63;
int32 &CCarCtrl::MaxNumberOfCarsInUse = *(int32*)0x5EC8B8;
uint32 &CCarCtrl::LastTimeLawEnforcerCreated = *(uint32*)0x8F5FF0;
-uint32 &CCarCtrl::LastTimeFireTruckCreated = *(uint32*)0x941450;
-uint32 &CCarCtrl::LastTimeAmbulanceCreated = *(uint32*)0x880F5C;
+uint32 &CCarCtrl::LastTimeFireTruckCreated = *(uint32*)0x880F5C;
+uint32 &CCarCtrl::LastTimeAmbulanceCreated = *(uint32*)0x941450;
int32 (&CCarCtrl::TotalNumOfCarsOfRating)[TOTAL_CUSTOM_CLASSES] = *(int32(*)[TOTAL_CUSTOM_CLASSES])*(uintptr*)0x8F1A60;
int32 (&CCarCtrl::NextCarOfRating)[TOTAL_CUSTOM_CLASSES] = *(int32(*)[TOTAL_CUSTOM_CLASSES])*(uintptr*)0x9412AC;
int32 (&CCarCtrl::CarArrays)[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY] = *(int32(*)[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY])*(uintptr*)0x6EB860;
CVehicle* (&apCarsToKeep)[MAX_CARS_TO_KEEP] = *(CVehicle*(*)[MAX_CARS_TO_KEEP])*(uintptr*)0x70D830;
uint32 (&aCarsToKeepTime)[MAX_CARS_TO_KEEP] = *(uint32(*)[MAX_CARS_TO_KEEP])*(uintptr*)0x87F9A8;
-WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); }
-WRAPPER void CCarCtrl::UpdateCarCount(CVehicle*, bool) { EAXJMP(0x4202E0); }
-WRAPPER bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle*, CVector, bool) { EAXJMP(0x41FA00); }
-WRAPPER void CCarCtrl::JoinCarWithRoadSystem(CVehicle*) { EAXJMP(0x41F820); }
-WRAPPER void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* v) { EAXJMP(0x41F7A0); }
-WRAPPER void CCarCtrl::GenerateEmergencyServicesCar(void) { EAXJMP(0x41FC50); }
-
void
CCarCtrl::GenerateRandomCars()
{
@@ -753,44 +747,6 @@ CCarCtrl::CountCarsOfType(int32 mi)
return total;
}
-bool
-CCarCtrl::IsThisVehicleInteresting(CVehicle* pVehicle)
-{
- for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
- if (apCarsToKeep[i] == pVehicle)
- return true;
- }
- return false;
-}
-
-void
-CCarCtrl::RegisterVehicleOfInterest(CVehicle* pVehicle)
-{
- for(int i = 0; i < MAX_CARS_TO_KEEP; i++) {
- if (apCarsToKeep[i] == pVehicle) {
- aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds();
- return;
- }
- }
- for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
- if (!apCarsToKeep[i]) {
- apCarsToKeep[i] = pVehicle;
- aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds();
- return;
- }
- }
- uint32 oldestCarWeKeepTime = UINT_MAX;
- int oldestCarWeKeepIndex = 0;
- for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
- if (apCarsToKeep[i] && aCarsToKeepTime[i] < oldestCarWeKeepTime) {
- oldestCarWeKeepTime = aCarsToKeepTime[i];
- oldestCarWeKeepIndex = i;
- }
- }
- apCarsToKeep[oldestCarWeKeepIndex] = pVehicle;
- aCarsToKeepTime[oldestCarWeKeepIndex] = CTimer::GetTimeInMilliseconds();
-}
-
void
CCarCtrl::UpdateCarOnRails(CVehicle* pVehicle)
{
@@ -1651,7 +1607,7 @@ void CCarCtrl::PickNextNodeRandomly(CVehicle* pVehicle)
) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed);
if (pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve < 10)
/* Oh hey there Obbe */
- debug("fout\n");
+ printf("fout\n");
pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve);
}
@@ -2484,6 +2440,305 @@ void CCarCtrl::SteerAIBoatWithPhysicsHeadingForTarget(CBoat* pBoat, float target
*pSwerve = angleDiff;
}
+void
+CCarCtrl::RegisterVehicleOfInterest(CVehicle* pVehicle)
+{
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (apCarsToKeep[i] == pVehicle) {
+ aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds();
+ return;
+ }
+ }
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (!apCarsToKeep[i]) {
+ apCarsToKeep[i] = pVehicle;
+ aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds();
+ return;
+ }
+ }
+ uint32 oldestCarWeKeepTime = UINT_MAX;
+ int oldestCarWeKeepIndex = 0;
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (apCarsToKeep[i] && aCarsToKeepTime[i] < oldestCarWeKeepTime) {
+ oldestCarWeKeepTime = aCarsToKeepTime[i];
+ oldestCarWeKeepIndex = i;
+ }
+ }
+ apCarsToKeep[oldestCarWeKeepIndex] = pVehicle;
+ aCarsToKeepTime[oldestCarWeKeepIndex] = CTimer::GetTimeInMilliseconds();
+}
+
+bool
+CCarCtrl::IsThisVehicleInteresting(CVehicle* pVehicle)
+{
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (apCarsToKeep[i] == pVehicle)
+ return true;
+ }
+ return false;
+}
+
+void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* pVehicle)
+{
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ if (apCarsToKeep[i] == pVehicle)
+ apCarsToKeep[i] = nil;
+ }
+}
+
+void CCarCtrl::ClearInterestingVehicleList()
+{
+ for (int i = 0; i < MAX_CARS_TO_KEEP; i++) {
+ apCarsToKeep[i] = nil;
+ }
+}
+
+void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle* pVehicle)
+{
+ pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
+ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
+ pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
+ pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds();
+}
+
+void CCarCtrl::JoinCarWithRoadSystem(CVehicle* pVehicle)
+{
+ pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0;
+ pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo = 0;
+ int nodeId = ThePaths.FindNodeClosestToCoorsFavourDirection(pVehicle->GetPosition(), 0, pVehicle->GetForward().x, pVehicle->GetForward().y);
+ CPathNode* pNode = &ThePaths.m_pathNodes[nodeId];
+ int prevNodeId = -1;
+ float minDistance = 999999.9f;
+ for (int i = 0; i < pNode->numLinks; i++){
+ int candidateId = ThePaths.m_connections[i + pNode->firstLink];
+ CPathNode* pCandidateNode = &ThePaths.m_pathNodes[candidateId];
+ float distance = (pCandidateNode->pos - pNode->pos).Magnitude2D();
+ if (distance < minDistance){
+ minDistance = distance;
+ prevNodeId = candidateId;
+ }
+ }
+ if (prevNodeId < 0)
+ return;
+ CVector2D forward = pVehicle->GetForward();
+ CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNodeId];
+ if (forward.x == 0.0f && forward.y == 0.0f)
+ forward.x = 1.0f;
+ if (DotProduct2D(pNode->pos - pPrevNode->pos, forward) < 0.0f){
+ int tmp;
+ tmp = prevNodeId;
+ prevNodeId = nodeId;
+ nodeId = tmp;
+ }
+ pVehicle->AutoPilot.m_nPrevRouteNode = 0;
+ pVehicle->AutoPilot.m_nCurrentRouteNode = prevNodeId;
+ pVehicle->AutoPilot.m_nNextRouteNode = nodeId;
+ pVehicle->AutoPilot.m_nPathFindNodesCount = 0;
+ FindLinksToGoWithTheseNodes(pVehicle);
+ pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0;
+}
+
+bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle* pVehicle, CVector vecTarget, bool isProperNow)
+{
+ pVehicle->AutoPilot.m_vecDestinationCoors = vecTarget;
+ ThePaths.DoPathSearch(0, pVehicle->GetPosition(), -1, vecTarget, pVehicle->AutoPilot.m_aPathFindNodesInfo,
+ &pVehicle->AutoPilot.m_nPathFindNodesCount, NUM_PATH_NODES_IN_AUTOPILOT, pVehicle, nil, 999999.9f, -1);
+ ThePaths.RemoveBadStartNode(pVehicle->GetPosition(),
+ pVehicle->AutoPilot.m_aPathFindNodesInfo, &pVehicle->AutoPilot.m_nPathFindNodesCount);
+ if (pVehicle->AutoPilot.m_nPathFindNodesCount < 2){
+ pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0;
+ return 1;
+ }
+ pVehicle->AutoPilot.m_nPrevRouteNode = 0;
+ pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes;
+ pVehicle->AutoPilot.RemoveOnePathNode();
+ pVehicle->AutoPilot.m_nNextRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes;
+ pVehicle->AutoPilot.RemoveOnePathNode();
+ FindLinksToGoWithTheseNodes(pVehicle);
+ pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0;
+ return 0;
+}
+
+void CCarCtrl::FindLinksToGoWithTheseNodes(CVehicle* pVehicle)
+{
+ int nextLink;
+ CPathNode* pCurNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nCurrentRouteNode];
+ for (nextLink = 0; nextLink < 12; nextLink++)
+ if (ThePaths.m_connections[nextLink + pCurNode->firstLink] != pVehicle->AutoPilot.m_nNextRouteNode)
+ break;
+ pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink];
+ pVehicle->AutoPilot.m_nNextDirection = (pVehicle->AutoPilot.m_nCurrentRouteNode >= pVehicle->AutoPilot.m_nNextRouteNode) ? 1 : -1;
+ int curLink;
+ int curConnection;
+ if (pCurNode->numLinks == 1) {
+ curLink = 0;
+ curConnection = ThePaths.m_carPathConnections[pCurNode->firstLink];
+ }else{
+ curConnection = pVehicle->AutoPilot.m_nNextPathNodeInfo;
+ while (curConnection == pVehicle->AutoPilot.m_nNextPathNodeInfo){
+ curLink = CGeneral::GetRandomNumber() % pCurNode->numLinks;
+ curConnection = ThePaths.m_carPathConnections[curLink + pCurNode->firstLink];
+ }
+ }
+ pVehicle->AutoPilot.m_nCurrentPathNodeInfo = curConnection;
+ pVehicle->AutoPilot.m_nCurrentDirection = (ThePaths.m_connections[curLink + pCurNode->firstLink] >= pVehicle->AutoPilot.m_nCurrentRouteNode) ? 1 : -1;
+}
+
+void CCarCtrl::GenerateEmergencyServicesCar(void)
+{
+ if (FindPlayerPed()->m_pWanted->m_nWantedLevel > 3)
+ return;
+ if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars +
+ NumLawEnforcerCars + NumRandomCars > MaxNumberOfCarsInUse)
+ return;
+ if (NumAmbulancesOnDuty == 0){
+ if (gAccidentManager.CountActiveAccidents() < 2){
+ if (CStreaming::HasModelLoaded(MI_AMBULAN))
+ CStreaming::SetModelIsDeletable(MI_MEDIC);
+ }else{
+ float distance = 30.0f;
+ CAccident* pNearestAccident = gAccidentManager.FindNearestAccident(FindPlayerCoors(), &distance);
+ if (pNearestAccident){
+ if (CountCarsOfType(MI_AMBULAN) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeAmbulanceCreated + 30000){
+ CStreaming::RequestModel(MI_AMBULAN, STREAMFLAGS_DEPENDENCY);
+ CStreaming::RequestModel(MI_MEDIC, STREAMFLAGS_DONT_REMOVE);
+ if (CStreaming::HasModelLoaded(MI_AMBULAN) && CStreaming::HasModelLoaded(MI_MEDIC)){
+ if (GenerateOneEmergencyServicesCar(MI_AMBULAN, pNearestAccident->m_pVictim->GetPosition()))
+ LastTimeAmbulanceCreated = CTimer::GetTimeInMilliseconds();
+ }
+ }
+ }
+ }
+ }
+ if (NumFiretrucksOnDuty == 0){
+ if (gFireManager.GetTotalActiveFires() < 3){
+ if (CStreaming::HasModelLoaded(MI_FIRETRUCK))
+ CStreaming::SetModelIsDeletable(MI_FIREMAN);
+ }else{
+ float distance = 30.0f;
+ CFire* pNearestFire = gFireManager.FindNearestFire(FindPlayerCoors(), &distance);
+ if (pNearestFire) {
+ if (CountCarsOfType(MI_FIRETRUCK) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeFireTruckCreated + 30000){
+ CStreaming::RequestModel(MI_FIRETRUCK, STREAMFLAGS_DEPENDENCY);
+ CStreaming::RequestModel(MI_FIREMAN, STREAMFLAGS_DONT_REMOVE);
+ if (CStreaming::HasModelLoaded(MI_FIRETRUCK) && CStreaming::HasModelLoaded(MI_FIREMAN)){
+ if (GenerateOneEmergencyServicesCar(MI_FIRETRUCK, pNearestFire->m_vecPos))
+ LastTimeFireTruckCreated = CTimer::GetTimeInMilliseconds();
+ }
+ }
+ }
+ }
+ }
+}
+
+bool CCarCtrl::GenerateOneEmergencyServicesCar(uint32 mi, CVector vecPos)
+{
+ CVector pPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus);
+ bool created = false;
+ int attempts = 0;
+ CVector spawnPos;
+ int curNode, nextNode;
+ float posBetweenNodes;
+ while (!created && attempts < 5){
+ if (ThePaths.NewGenerateCarCreationCoors(pPlayerPos.x, pPlayerPos.y, 0.707f, 0.707f,
+ 120.0f, -1.0f, true, &spawnPos, &curNode, &nextNode, &posBetweenNodes, false)){
+ int16 colliding[2];
+ CWorld::FindObjectsKindaColliding(spawnPos, 10.0f, true, colliding, 2, nil, false, true, true, false, false);
+ if (colliding[0] == 0)
+ created = true;
+ }
+ attempts += 1;
+ }
+ if (attempts >= 5)
+ return nil;
+ CAutomobile* pVehicle = new CAutomobile(mi, RANDOM_VEHICLE);
+ pVehicle->AutoPilot.m_vecDestinationCoors = vecPos;
+ pVehicle->GetPosition() = spawnPos;
+ pVehicle->AutoPilot.m_nCarMission = (JoinCarWithRoadSystemGotoCoors(pVehicle, vecPos, false)) ? MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS;
+ pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed = 25;
+ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
+ pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
+ CVector2D direction = vecPos - spawnPos;
+ direction.Normalise();
+ pVehicle->GetForward() = CVector(direction.x, direction.y, 0.0f);
+ pVehicle->GetRight() = CVector(direction.y, -direction.x, 0.0f);
+ pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f);
+ spawnPos.z = posBetweenNodes * ThePaths.m_pathNodes[curNode].pos.z + (1.0f - posBetweenNodes) * ThePaths.m_pathNodes[nextNode].pos.z;
+ float groundZ = INFINITE_Z;
+ CColPoint colPoint;
+ CEntity* pEntity;
+ if (CWorld::ProcessVerticalLine(spawnPos, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil))
+ groundZ = colPoint.point.z;
+ if (CWorld::ProcessVerticalLine(spawnPos, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) {
+ if (ABS(colPoint.point.z - spawnPos.z) < ABS(groundZ - spawnPos.z))
+ groundZ = colPoint.point.z;
+ }
+ if (groundZ == INFINITE_Z) {
+ delete pVehicle;
+ return false;
+ }
+ spawnPos.z = groundZ + pVehicle->GetDistanceFromCentreOfMassToBaseOfModel();
+ pVehicle->GetPosition() = spawnPos;
+ pVehicle->SetMoveSpeed(CVector(0.0f, 0.0f, 0.0f));
+ pVehicle->m_status = STATUS_PHYSICS;
+ switch (mi){
+ case MI_FIRETRUCK:
+ pVehicle->bIsFireTruckOnDuty = true;
+ ++NumFiretrucksOnDuty;
+ CCarAI::AddFiretruckOccupants(pVehicle);
+ break;
+ case MI_AMBULAN:
+ pVehicle->bIsAmbulanceOnDuty = true;
+ ++NumAmbulancesOnDuty;
+ CCarAI::AddAmbulanceOccupants(pVehicle);
+ break;
+ }
+ pVehicle->m_bSirenOrAlarm = true;
+ CWorld::Add(pVehicle);
+ printf("CREATED EMERGENCY VEHICLE\n");
+ return true;
+}
+
+void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove)
+{
+ if (remove){
+ switch (pVehicle->VehicleCreatedBy){
+ case RANDOM_VEHICLE:
+ if (pVehicle->bIsLawEnforcer)
+ --NumLawEnforcerCars;
+ --NumRandomCars;
+ return;
+ case MISSION_VEHICLE:
+ --NumMissionCars;
+ return;
+ case PARKED_VEHICLE:
+ --NumParkedCars;
+ return;
+ case PERMANENT_VEHICLE:
+ --NumPermanentCars;;
+ return;
+ }
+ }
+ else{
+ switch (pVehicle->VehicleCreatedBy){
+ case RANDOM_VEHICLE:
+ if (pVehicle->bIsLawEnforcer)
+ ++NumLawEnforcerCars;
+ ++NumRandomCars;
+ return;
+ case MISSION_VEHICLE:
+ ++NumMissionCars;
+ return;
+ case PARKED_VEHICLE:
+ ++NumParkedCars;
+ return;
+ case PERMANENT_VEHICLE:
+ ++NumPermanentCars;;
+ return;
+ }
+ }
+}
+
bool CCarCtrl::ThisRoadObjectCouldMove(int16 mi)
{
return mi == MI_BRIDGELIFT || mi == MI_BRIDGEROADSEGMENT;
@@ -2503,4 +2758,12 @@ InjectHook(0x418320, &CCarCtrl::RemoveDistantCars, PATCH_JUMP);
InjectHook(0x418430, &CCarCtrl::PossiblyRemoveVehicle, PATCH_JUMP);
InjectHook(0x41D280, &CCarCtrl::Init, PATCH_JUMP);
InjectHook(0x41D3B0, &CCarCtrl::ReInit, PATCH_JUMP);
+InjectHook(0x41E250, &CCarCtrl::SteerAIBoatWithPhysics, PATCH_JUMP);
+InjectHook(0x41F6E0, &CCarCtrl::RegisterVehicleOfInterest, PATCH_JUMP);
+InjectHook(0x41F780, &CCarCtrl::IsThisVehicleInteresting, PATCH_JUMP);
+InjectHook(0x41F7A0, &CCarCtrl::RemoveFromInterestingVehicleList, PATCH_JUMP);
+InjectHook(0x41F7D0, &CCarCtrl::ClearInterestingVehicleList, PATCH_JUMP);
+InjectHook(0x41F7F0, &CCarCtrl::SwitchVehicleToRealPhysics, PATCH_JUMP);
+InjectHook(0x41F820, &CCarCtrl::JoinCarWithRoadSystem, PATCH_JUMP);
+InjectHook(0x41FA00, &CCarCtrl::JoinCarWithRoadSystemGotoCoors, PATCH_JUMP);
ENDPATCHES