diff options
-rw-r--r-- | src/audio/DMAudio.cpp | 3 | ||||
-rw-r--r-- | src/audio/DMAudio.h | 2 | ||||
-rw-r--r-- | src/control/Darkel.cpp | 2 | ||||
-rw-r--r-- | src/control/Script.cpp | 4 | ||||
-rw-r--r-- | src/core/EventList.cpp | 238 | ||||
-rw-r--r-- | src/core/EventList.h | 65 | ||||
-rw-r--r-- | src/core/Pad.h | 4 | ||||
-rw-r--r-- | src/core/Pools.cpp | 8 | ||||
-rw-r--r-- | src/core/Pools.h | 6 | ||||
-rw-r--r-- | src/core/Wanted.cpp | 306 | ||||
-rw-r--r-- | src/core/Wanted.h | 73 | ||||
-rw-r--r-- | src/core/config.h | 1 | ||||
-rw-r--r-- | src/core/re3.cpp | 13 | ||||
-rw-r--r-- | src/modelinfo/ModelIndices.h | 18 | ||||
-rw-r--r-- | src/peds/CopPed.h | 33 | ||||
-rw-r--r-- | src/peds/PlayerPed.cpp | 2 | ||||
-rw-r--r-- | src/peds/PlayerPed.h | 1 | ||||
-rw-r--r-- | src/render/Draw.h | 5 | ||||
-rw-r--r-- | src/vehicles/Automobile.cpp | 132 | ||||
-rw-r--r-- | src/vehicles/Vehicle.cpp | 4 | ||||
-rw-r--r-- | src/vehicles/Vehicle.h | 6 |
21 files changed, 827 insertions, 99 deletions
diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index b4fee67f..1c1316a8 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -33,4 +33,5 @@ WRAPPER int32 cDMAudio::CreateEntity(int, void*) { EAXJMP(0x57C7C0); } WRAPPER void cDMAudio::SetEntityStatus(int32 id, uint8 enable) { EAXJMP(0x57C810); } WRAPPER void cDMAudio::SetRadioInCar(int32) { EAXJMP(0x57CE60); } WRAPPER void cDMAudio::DestroyEntity(int32) { EAXJMP(0x57C7F0); } -WRAPPER void cDMAudio::ClearMissionAudio(void) { EAXJMP(0x57CE20); }
\ No newline at end of file +WRAPPER void cDMAudio::ClearMissionAudio(void) { EAXJMP(0x57CE20); } +WRAPPER void cDMAudio::ReportCrime(eCrimeType crime, const CVector &pos) { EAXJMP(0x57CAD0); } diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 8be09ac6..0a2ce6ac 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -173,6 +173,7 @@ enum eSound : int16 }; class CEntity; +enum eCrimeType; class cDMAudio { @@ -204,5 +205,6 @@ public: uint8 IsMP3RadioChannelAvailable(); void DestroyEntity(int32); void ClearMissionAudio(void); + void ReportCrime(eCrimeType crime, const CVector &pos); }; extern cDMAudio &DMAudio; diff --git a/src/control/Darkel.cpp b/src/control/Darkel.cpp index ab28f96e..678fcd45 100644 --- a/src/control/Darkel.cpp +++ b/src/control/Darkel.cpp @@ -300,7 +300,7 @@ void CDarkel::Update() TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); - FindPlayerPed()->m_pWanted->SetWantedLevel(NOTWANTED); + FindPlayerPed()->m_pWanted->SetWantedLevel(0); if (WeaponType == WEAPONTYPE_UZI_DRIVEBY) WeaponType = WEAPONTYPE_UZI; diff --git a/src/control/Script.cpp b/src/control/Script.cpp index b50c101e..f3a18195 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -124,8 +124,8 @@ void CMissionCleanup::Process() CHud::m_ItemToFlash = -1; CHud::SetHelpMessage(nil, false); CUserDisplay::OnscnTimer.m_bDisabled = false; - CWorld::Players[0].m_pPed->m_pWanted->m_IsIgnoredByCops = false; - CWorld::Players[0].m_pPed->m_pWanted->m_IsIgnoredByEveryOne = false; + CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByCops = false; + CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByEveryone = false; CWorld::Players[0].MakePlayerSafe(false); CTheScripts::StoreVehicleIndex = -1; CTheScripts::StoreVehicleWasRandom = true; diff --git a/src/core/EventList.cpp b/src/core/EventList.cpp new file mode 100644 index 00000000..15ad49dd --- /dev/null +++ b/src/core/EventList.cpp @@ -0,0 +1,238 @@ +#include "common.h" +#include "patcher.h" +#include "Pools.h" +#include "ModelIndices.h" +#include "World.h" +#include "Wanted.h" +#include "Eventlist.h" + +int32 CEventList::ms_nFirstFreeSlotIndex; +//CEvent gaEvent[NUMEVENTS]; +CEvent *gaEvent = (CEvent*)0x6EF830; + +enum +{ + EVENT_STATE_0, + EVENT_STATE_CANDELETE, + EVENT_STATE_CLEAR, +}; + +void +CEventList::Initialise(void) +{ + int i; + + debug("Initialising CEventList..."); + for(i = 0; i < NUMEVENTS; i++){ + gaEvent[i].type = EVENT_NULL; + gaEvent[i].entityType = EVENT_ENTITY_NONE; + gaEvent[i].entityRef = 0; + gaEvent[i].posn.x = 0.0f; + gaEvent[i].posn.y = 0.0f; + gaEvent[i].posn.z = 0.0f; + gaEvent[i].timeout = 0; + gaEvent[i].state = EVENT_STATE_0; + } + ms_nFirstFreeSlotIndex = 0; +} + +void +CEventList::Update(void) +{ + int i; + + ms_nFirstFreeSlotIndex = 0; + for(i = 0; i < NUMEVENTS; i++){ + if(gaEvent[i].type == EVENT_NULL) + continue; + if(CTimer::GetTimeInMilliseconds() > gaEvent[i].timeout || gaEvent[i].state == EVENT_STATE_CANDELETE){ + gaEvent[i].type = EVENT_NULL; + gaEvent[i].state = EVENT_STATE_0; + } + if(gaEvent[i].state == EVENT_STATE_CLEAR) + gaEvent[i].state = EVENT_STATE_CANDELETE; + } +} + +void +CEventList::RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout) +{ + int i; + int ref; + bool copsDontCare; + + copsDontCare = false; + switch(entityType){ + case EVENT_ENTITY_PED: + ref = CPools::GetPedRef((CPed*)ent); + if(ent->GetModelIndex() >= MI_GANG01 && ent->GetModelIndex() <= MI_CRIMINAL02) + copsDontCare = true; + break; + case EVENT_ENTITY_VEHICLE: + ref = CPools::GetVehicleRef((CVehicle*)ent); + break; + case EVENT_ENTITY_OBJECT: + ref = CPools::GetObjectRef((CObject*)ent); + break; + default: + Error("Undefined entity type, RegisterEvent, EventList.cpp"); + ref = 0; + break; + } + + // only update time if event exists already + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type && + gaEvent[i].entityType == entityType && + gaEvent[i].entityRef == ref){ + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + return; + } + + for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) + if(gaEvent[i].type == EVENT_NULL){ + ms_nFirstFreeSlotIndex = i; + break; + } + if(i < NUMEVENTS){ + gaEvent[i].type = type; + gaEvent[i].entityType = entityType; + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + gaEvent[i].entityRef = ref; + gaEvent[i].posn = ent->GetPosition(); + gaEvent[i].criminal = criminal; + if(gaEvent[i].criminal) + gaEvent[i].criminal->RegisterReference((CEntity**)&gaEvent[i].criminal); + if(type == EVENT_GUNSHOT) + gaEvent[i].state = EVENT_STATE_CLEAR; + else + gaEvent[i].state = EVENT_STATE_0; + } + + if(criminal == FindPlayerPed()) + ReportCrimeForEvent(type, (uintptr)ent, copsDontCare); +} + +void +CEventList::RegisterEvent(eEventType type, CVector posn, int32 timeout) +{ + int i; + + // only update time if event exists already + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type && + gaEvent[i].posn.x == posn.x && + gaEvent[i].posn.y == posn.y && + gaEvent[i].posn.z == posn.z && + gaEvent[i].entityType == EVENT_ENTITY_NONE){ + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + return; + } + + for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) + if(gaEvent[i].type == EVENT_NULL){ + ms_nFirstFreeSlotIndex = i; + break; + } + if(i < NUMEVENTS){ + gaEvent[i].type = type; + gaEvent[i].entityType = EVENT_ENTITY_NONE; + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + gaEvent[i].posn = posn; + gaEvent[i].entityRef = 0; + if(type == EVENT_GUNSHOT) + gaEvent[i].state = EVENT_STATE_CLEAR; + else + gaEvent[i].state = EVENT_STATE_0; + } +} + +bool +CEventList::GetEvent(eEventType type, int32 *event) +{ + int i; + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type){ + *event = i; + return true; + } + return false; +} + +void +CEventList::ClearEvent(int32 event) +{ + if(gaEvent[event].state != EVENT_STATE_CANDELETE) + gaEvent[event].state = EVENT_STATE_CLEAR; +} + +bool +CEventList::FindClosestEvent(eEventType type, CVector posn, int32 *event) +{ + int i; + float dist; + bool found = false; + float mindist = 60.0f; + + for(i = 0; i < NUMEVENTS; i++){ + if(gaEvent[i].type == EVENT_NULL) + continue; + dist = (posn - gaEvent[i].posn).Magnitude(); + if(dist < mindist){ + mindist = dist; + found = true; + *event = i; + } + } + return found; +} + +void +CEventList::ReportCrimeForEvent(eEventType type, int32 crimeId, bool copsDontCare) +{ + eCrimeType crime; + switch(type){ + case EVENT_ASSAULT: crime = CRIME_HIT_PED; break; + case EVENT_RUN_REDLIGHT: crime = CRIME_RUN_REDLIGHT; break; + case EVENT_ASSAULT_POLICE: crime = CRIME_HIT_COP; break; + case EVENT_GUNSHOT: crime = CRIME_POSSESSION_GUN; break; + case EVENT_STEAL_CAR: crime = CRIME_STEAL_CAR; break; + case EVENT_HIT_AND_RUN: crime = CRIME_RUNOVER_PED; break; + case EVENT_HIT_AND_RUN_COP: crime = CRIME_RUNOVER_COP; break; + case EVENT_SHOOT_PED: crime = CRIME_SHOOT_PED; break; + case EVENT_SHOOT_COP: crime = CRIME_SHOOT_COP; break; + case EVENT_PED_SET_ON_FIRE: crime = CRIME_PED_BURNED; break; + case EVENT_COP_SET_ON_FIRE: crime = CRIME_COP_BURNED; break; + case EVENT_CAR_SET_ON_FIRE: crime = CRIME_VEHICLE_BURNED; break; + default: crime = CRIME_NONE; break; + } + + if(crime == CRIME_NONE) + return; + + CVector playerPedCoors = FindPlayerPed()->GetPosition(); + CVector playerCoors = FindPlayerCoors(); + + if(CWanted::WorkOutPolicePresence(playerCoors, 14.0f) != 0){ + FindPlayerPed()->m_pWanted->RegisterCrime_Immediately(crime, playerPedCoors, crimeId, copsDontCare); + FindPlayerPed()->m_pWanted->SetWantedLevelNoDrop(1); + }else + FindPlayerPed()->m_pWanted->RegisterCrime(crime, playerPedCoors, crimeId, copsDontCare); + + if(type == EVENT_ASSAULT_POLICE) + FindPlayerPed()->SetWantedLevelNoDrop(1); + if(type == EVENT_SHOOT_COP) + FindPlayerPed()->SetWantedLevelNoDrop(2); + +} + +STARTPATCHES + InjectHook(0x475B60, CEventList::Initialise, PATCH_JUMP); + InjectHook(0x475BE0, CEventList::Update, PATCH_JUMP); + InjectHook(0x475C50, (void (*)(eEventType,eEventEntity,CEntity *,CPed *,int32))CEventList::RegisterEvent, PATCH_JUMP); + InjectHook(0x475E10, (void (*)(eEventType,CVector,int32))CEventList::RegisterEvent, PATCH_JUMP); + InjectHook(0x475F40, CEventList::GetEvent, PATCH_JUMP); + InjectHook(0x475F70, CEventList::ClearEvent, PATCH_JUMP); + InjectHook(0x475F90, CEventList::FindClosestEvent, PATCH_JUMP); + InjectHook(0x476070, CEventList::ReportCrimeForEvent, PATCH_JUMP); +ENDPATCHES diff --git a/src/core/EventList.h b/src/core/EventList.h new file mode 100644 index 00000000..d0fc0847 --- /dev/null +++ b/src/core/EventList.h @@ -0,0 +1,65 @@ +#pragma once + +class CEntity; +class CPed; + +enum eEventType +{ + EVENT_NULL, + EVENT_ASSAULT, + EVENT_RUN_REDLIGHT, + EVENT_ASSAULT_POLICE, + EVENT_GUNSHOT, + EVENT_INJURED_PED, + EVENT_DEAD_PED, + EVENT_FIRE, + EVENT_STEAL_CAR, + EVENT_HIT_AND_RUN, + EVENT_HIT_AND_RUN_COP, + EVENT_SHOOT_PED, + EVENT_SHOOT_COP, + EVENT_EXPLOSION, + EVENT_PED_SET_ON_FIRE, + EVENT_COP_SET_ON_FIRE, + EVENT_CAR_SET_ON_FIRE, + EVENT_ASSAULT_NASTYWEAPON, + EVENT_ASSAULT_NASTYWEAPON_POLICE, + EVENT_ICECREAM, + EVENT_ATM, + EVENT_SHOPSTALL, + EVENT_SHOPWINDOW, + EVENT_LAST_EVENT +}; + +enum eEventEntity +{ + EVENT_ENTITY_NONE, + EVENT_ENTITY_PED, + EVENT_ENTITY_VEHICLE, + EVENT_ENTITY_OBJECT +}; + +struct CEvent +{ + eEventType type; + eEventEntity entityType; + int32 entityRef; + CPed *criminal; + CVector posn; + uint32 timeout; + int32 state; +}; + +class CEventList +{ + static int32 ms_nFirstFreeSlotIndex; +public: + static void Initialise(void); + static void Update(void); + static void RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout); + static void RegisterEvent(eEventType type, CVector posn, int32 timeout); + static bool GetEvent(eEventType type, int32 *event); + static void ClearEvent(int32 event); + static bool FindClosestEvent(eEventType type, CVector posn, int32 *event); + static void ReportCrimeForEvent(eEventType type, int32, bool); +}; diff --git a/src/core/Pad.h b/src/core/Pad.h index 30cdb8df..30a9980b 100644 --- a/src/core/Pad.h +++ b/src/core/Pad.h @@ -289,6 +289,10 @@ public: // mouse bool GetLeftMouseJustDown() { return !!(NewMouseControllerState.LMB && !OldMouseControllerState.LMB); } + bool GetRightMouseJustDown() { return !!(NewMouseControllerState.RMB && !OldMouseControllerState.RMB); } + bool GetMiddleMouseJustDown() { return !!(NewMouseControllerState.MMB && !OldMouseControllerState.MMB); } + float GetMouseX() { return NewMouseControllerState.x; } + float GetMouseY() { return NewMouseControllerState.y; } // keyboard diff --git a/src/core/Pools.cpp b/src/core/Pools.cpp index f7f93292..1f07cf54 100644 --- a/src/core/Pools.cpp +++ b/src/core/Pools.cpp @@ -14,6 +14,7 @@ void CPools::Initialise(void) { // TODO: unused right now + assert(0); ms_pPtrNodePool = new CCPtrNodePool(NUMPTRNODES); ms_pEntryInfoNodePool = new CEntryInfoNodePool(NUMENTRYINFOS); ms_pPedPool = new CPedPool(NUMPEDS); @@ -23,3 +24,10 @@ CPools::Initialise(void) ms_pObjectPool = new CObjectPool(NUMOBJECTS); ms_pDummyPool = new CDummyPool(NUMDUMMIES); } + +int32 CPools::GetPedRef(CPed *ped) { return ms_pPedPool->GetIndex(ped); } +CPed *CPools::GetPed(int32 handle) { return ms_pPedPool->GetAt(handle); } +int32 CPools::GetVehicleRef(CVehicle *vehicle) { return ms_pVehiclePool->GetIndex(vehicle); } +CVehicle *CPools::GetVehicle(int32 handle) { return ms_pVehiclePool->GetAt(handle); } +int32 CPools::GetObjectRef(CObject *object) { return ms_pObjectPool->GetIndex(object); } +CObject *CPools::GetObject(int32 handle) { return ms_pObjectPool->GetAt(handle); } diff --git a/src/core/Pools.h b/src/core/Pools.h index 3496064c..e364798c 100644 --- a/src/core/Pools.h +++ b/src/core/Pools.h @@ -40,4 +40,10 @@ public: static CDummyPool *GetDummyPool(void) { return ms_pDummyPool; } static void Initialise(void); + static int32 GetPedRef(CPed *ped); + static CPed *GetPed(int32 handle); + static int32 GetVehicleRef(CVehicle *vehicle); + static CVehicle *GetVehicle(int32 handle); + static int32 GetObjectRef(CObject *object); + static CObject *GetObject(int32 handle); }; diff --git a/src/core/Wanted.cpp b/src/core/Wanted.cpp index 4608bfef..7b865311 100644 --- a/src/core/Wanted.cpp +++ b/src/core/Wanted.cpp @@ -1,65 +1,102 @@ #include "common.h" #include "patcher.h" +#include "Pools.h" +#include "ModelIndices.h" +#include "Timer.h" +#include "World.h" +#include "ZoneCull.h" +#include "Darkel.h" +#include "DMAudio.h" #include "Wanted.h" -int32 &CWanted::MaximumWantedLevel = *(int32*)0x5F7714; +int32 &CWanted::MaximumWantedLevel = *(int32*)0x5F7714; // 6 +int32 &CWanted::nMaximumWantedLevel = *(int32*)0x5F7718; // 6400 -bool CWanted::AreSwatRequired() +void +CWanted::Initialise() +{ + int i; + + m_nChaos = 0; + m_nLastUpdateTime = 0; + m_nLastWantedLevelChange = 0; + m_CurrentCops = 0; + m_MaxCops = 0; + m_MaximumLawEnforcerVehicles = 0; + m_RoadblockDensity = 0; + m_bIgnoredByCops = false; + m_bIgnoredByEveryone = false; + m_bSwatRequired = false; + m_bFbiRequired = false; + m_bArmyRequired = false; + m_fCrimeSensitivity = 1.0f; + m_nWantedLevel = 0; + m_CopsBeatingSuspect = 0; + for(i = 0; i < 10; i++) + m_pCops[i] = nil; + ClearQdCrimes(); +} + +bool +CWanted::AreSwatRequired() { return m_nWantedLevel >= 4; } -bool CWanted::AreFbiRequired() +bool +CWanted::AreFbiRequired() { return m_nWantedLevel >= 5; } -bool CWanted::AreArmyRequired() +bool +CWanted::AreArmyRequired() { return m_nWantedLevel >= 6; } -int CWanted::NumOfHelisRequired() +int32 +CWanted::NumOfHelisRequired() { - if (m_IsIgnoredByCops) + if (m_bIgnoredByCops) return 0; - // Return value is number of helicopters, no need to name them. switch (m_nWantedLevel) { - case WANTEDLEVEL_3: - case WANTEDLEVEL_4: + case 3: + case 4: return 1; - case WANTEDLEVEL_5: - case WANTEDLEVEL_6: + case 5: + case 6: return 2; default: return 0; } } -void CWanted::SetWantedLevel(int32 level) +void +CWanted::SetWantedLevel(int32 level) { ClearQdCrimes(); switch (level) { - case NOTWANTED: + case 0: m_nChaos = 0; break; - case WANTEDLEVEL_1: + case 1: m_nChaos = 60; break; - case WANTEDLEVEL_2: + case 2: m_nChaos = 220; break; - case WANTEDLEVEL_3: + case 3: m_nChaos = 420; break; - case WANTEDLEVEL_4: + case 4: m_nChaos = 820; break; - case WANTEDLEVEL_5: + case 5: m_nChaos = 1620; break; - case WANTEDLEVEL_6: + case 6: m_nChaos = 3220; break; default: @@ -70,61 +107,212 @@ void CWanted::SetWantedLevel(int32 level) UpdateWantedLevel(); } -void CWanted::SetWantedLevelNoDrop(int32 level) +void +CWanted::SetWantedLevelNoDrop(int32 level) { if (level > m_nWantedLevel) SetWantedLevel(level); } -void CWanted::ClearQdCrimes() +void +CWanted::SetMaximumWantedLevel(int32 level) { - for (int i = 0; i < 16; i++) { - m_sCrimes[i].m_eCrimeType = CRIME_NONE; + switch(level){ + case 0: + nMaximumWantedLevel = 0; + MaximumWantedLevel = 0; + break; + case 1: + nMaximumWantedLevel = 120; + MaximumWantedLevel = 1; + break; + case 2: + nMaximumWantedLevel = 300; + MaximumWantedLevel = 2; + break; + case 3: + nMaximumWantedLevel = 600; + MaximumWantedLevel = 3; + break; + case 4: + nMaximumWantedLevel = 1200; + MaximumWantedLevel = 4; + break; + case 5: + nMaximumWantedLevel = 2400; + MaximumWantedLevel = 5; + break; + case 6: + nMaximumWantedLevel = 4800; + MaximumWantedLevel = 6; + break; } } -void CWanted::UpdateWantedLevel() +void +CWanted::RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) +{ + AddCrimeToQ(type, id, coors, false, policeDoesntCare); +} + +void +CWanted::RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) +{ + if(!AddCrimeToQ(type, id, coors, false, policeDoesntCare)) + ReportCrimeNow(type, coors, policeDoesntCare); +} + +void +CWanted::ClearQdCrimes() +{ + for (int i = 0; i < 16; i++) + m_aCrimes[i].m_nType = CRIME_NONE; +} + +// returns whether the crime had been reported already +bool +CWanted::AddCrimeToQ(eCrimeType type, int32 id, const CVector &coors, bool reported, bool policeDoesntCare) +{ + int i; + + for(i = 0; i < 16; i++) + if(m_aCrimes[i].m_nType == type && m_aCrimes[i].m_nId == id){ + if(m_aCrimes[i].m_bReported) + return true; + if(reported) + m_aCrimes[i].m_bReported = reported; + return false; + } + + for(i = 0; i < 16; i++) + if(m_aCrimes[i].m_nType == CRIME_NONE) + break; + if(i < 16){ + m_aCrimes[i].m_nType = type; + m_aCrimes[i].m_nId = id; + m_aCrimes[i].m_vecPosn = coors; + m_aCrimes[i].m_nTime = CTimer::GetTimeInMilliseconds(); + m_aCrimes[i].m_bReported = reported; + m_aCrimes[i].m_bPoliceDoesntCare = policeDoesntCare; + } + return false; +} + +void +CWanted::ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare) +{ + float sensitivity, chaos; + int wantedLevelDrop; + + if(CDarkel::FrenzyOnGoing()) + sensitivity = m_fCrimeSensitivity*0.3f; + else + sensitivity = m_fCrimeSensitivity; + + wantedLevelDrop = min(CCullZones::GetWantedLevelDrop(), 100); + + chaos = (1.0f - wantedLevelDrop/100.0f) * sensitivity; + if (policeDoesntCare) + chaos *= 0.333f; + switch(type){ + case CRIME_POSSESSION_GUN: + break; + case CRIME_HIT_PED: + m_nChaos += 5.0f*chaos; + break; + case CRIME_HIT_COP: + m_nChaos += 45.0f*chaos; + break; + case CRIME_SHOOT_PED: + m_nChaos += 30.0f*chaos; + break; + case CRIME_SHOOT_COP: + m_nChaos += 80.0f*chaos; + break; + case CRIME_STEAL_CAR: + m_nChaos += 15.0f*chaos; + break; + case CRIME_RUN_REDLIGHT: + m_nChaos += 10.0f*chaos; + break; + case CRIME_RECKLESS_DRIVING: + m_nChaos += 5.0f*chaos; + break; + case CRIME_SPEEDING: + m_nChaos += 5.0f*chaos; + break; + case CRIME_RUNOVER_PED: + m_nChaos += 18.0f*chaos; + break; + case CRIME_RUNOVER_COP: + m_nChaos += 80.0f*chaos; + break; + case CRIME_SHOOT_HELI: + m_nChaos += 400.0f*chaos; + break; + case CRIME_PED_BURNED: + m_nChaos += 20.0f*chaos; + break; + case CRIME_COP_BURNED: + m_nChaos += 80.0f*chaos; + break; + case CRIME_VEHICLE_BURNED: + m_nChaos += 20.0f*chaos; + break; + case CRIME_DESTROYED_CESSNA: + m_nChaos += 500.0f*chaos; + break; + default: + // Error("Undefined crime type, RegisterCrime, Crime.cpp"); // different file for some reason + Error("Undefined crime type, RegisterCrime, Wanted.cpp"); + } + DMAudio.ReportCrime(type, coors); + UpdateWantedLevel(); +} + +void +CWanted::UpdateWantedLevel() { int32 CurrWantedLevel = m_nWantedLevel; if (m_nChaos >= 0 && m_nChaos < 40) { - m_nWantedLevel = NOTWANTED; + m_nWantedLevel = 0; m_MaximumLawEnforcerVehicles = 0; m_MaxCops = 0; m_RoadblockDensity = 0; } else if (m_nChaos >= 40 && m_nChaos < 200) { - m_nWantedLevel = WANTEDLEVEL_1; + m_nWantedLevel = 1; m_MaximumLawEnforcerVehicles = 1; m_MaxCops = 1; m_RoadblockDensity = 0; } else if (m_nChaos >= 200 && m_nChaos < 400) { - m_nWantedLevel = WANTEDLEVEL_2; + m_nWantedLevel = 2; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 3; m_RoadblockDensity = 0; } else if (m_nChaos >= 400 && m_nChaos < 800) { - m_nWantedLevel = WANTEDLEVEL_3; + m_nWantedLevel = 3; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 4; m_RoadblockDensity = 4; } else if (m_nChaos >= 800 && m_nChaos < 1600) { - m_nWantedLevel = WANTEDLEVEL_4; + m_nWantedLevel = 4; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 6; m_RoadblockDensity = 8; } else if (m_nChaos >= 1600 && m_nChaos < 3200) { - m_nWantedLevel = WANTEDLEVEL_5; + m_nWantedLevel = 5; m_MaximumLawEnforcerVehicles = 3; m_MaxCops = 8; m_RoadblockDensity = 10; } else if (m_nChaos >= 3200) { - m_nWantedLevel = WANTEDLEVEL_6; + m_nWantedLevel = 6; m_MaximumLawEnforcerVehicles = 3; m_MaxCops = 10; m_RoadblockDensity = 12; @@ -132,4 +320,58 @@ void CWanted::UpdateWantedLevel() if (CurrWantedLevel != m_nWantedLevel) m_nLastWantedLevelChange = CTimer::GetTimeInMilliseconds(); -}
\ No newline at end of file +} + +int32 +CWanted::WorkOutPolicePresence(CVector posn, float radius) +{ + int i; + CPed *ped; + CVehicle *vehicle; + int numPolice = 0; + + i = CPools::GetPedPool()->GetSize(); + while(--i >= 0){ + ped = CPools::GetPedPool()->GetSlot(i); + if(ped && + IsPolicePedModel(ped->GetModelIndex()) && + (posn - ped->GetPosition()).Magnitude() < radius) + numPolice++; + } + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + vehicle = CPools::GetVehiclePool()->GetSlot(i); + if(vehicle && + vehicle->bIsLawEnforcer && + IsPoliceVehicleModel(vehicle->GetModelIndex()) && + vehicle != FindPlayerVehicle() && + vehicle->m_status != STATUS_ABANDONED && vehicle->m_status != STATUS_WRECKED && + (posn - vehicle->GetPosition()).Magnitude() < radius) + numPolice++; + } + + return numPolice; +} + +STARTPATCHES + InjectHook(0x4AD6E0, &CWanted::Initialise, PATCH_JUMP); +// InjectHook(0x4AD790, &CWanted::Reset, PATCH_JUMP); +// InjectHook(0x4AD7B0, &CWanted::Update, PATCH_JUMP); + InjectHook(0x4AD900, &CWanted::UpdateWantedLevel, PATCH_JUMP); + InjectHook(0x4AD9F0, &CWanted::RegisterCrime, PATCH_JUMP); + InjectHook(0x4ADA10, &CWanted::RegisterCrime_Immediately, PATCH_JUMP); + InjectHook(0x4ADA50, &CWanted::SetWantedLevel, PATCH_JUMP); + InjectHook(0x4ADAC0, &CWanted::SetWantedLevelNoDrop, PATCH_JUMP); + InjectHook(0x4ADAE0, &CWanted::SetMaximumWantedLevel, PATCH_JUMP); + InjectHook(0x4ADBA0, &CWanted::AreSwatRequired, PATCH_JUMP); + InjectHook(0x4ADBC0, &CWanted::AreFbiRequired, PATCH_JUMP); + InjectHook(0x4ADBE0, &CWanted::AreArmyRequired, PATCH_JUMP); + InjectHook(0x4ADC00, &CWanted::NumOfHelisRequired, PATCH_JUMP); +// InjectHook(0x4ADC40, &CWanted::ResetPolicePursuit, PATCH_JUMP); + InjectHook(0x4ADD00, &CWanted::WorkOutPolicePresence, PATCH_JUMP); + InjectHook(0x4ADF20, &CWanted::ClearQdCrimes, PATCH_JUMP); + InjectHook(0x4ADFD0, &CWanted::AddCrimeToQ, PATCH_JUMP); +// InjectHook(0x4AE090, &CWanted::UpdateCrimesQ, PATCH_JUMP); + InjectHook(0x4AE110, &CWanted::ReportCrimeNow, PATCH_JUMP); +ENDPATCHES diff --git a/src/core/Wanted.h b/src/core/Wanted.h index d3f6638b..1a72f839 100644 --- a/src/core/Wanted.h +++ b/src/core/Wanted.h @@ -1,16 +1,38 @@ #pragma once -#include "Entity.h" -#include "math/Vector.h" -#include "CopPed.h" - -enum eWantedLevel { - NOTWANTED, - WANTEDLEVEL_1, - WANTEDLEVEL_2, - WANTEDLEVEL_3, - WANTEDLEVEL_4, - WANTEDLEVEL_5, - WANTEDLEVEL_6, + +class CEntity; +class CCopPed; + +enum eCrimeType +{ + CRIME_NONE, + CRIME_POSSESSION_GUN, + CRIME_HIT_PED, + CRIME_HIT_COP, + CRIME_SHOOT_PED, + CRIME_SHOOT_COP, + CRIME_STEAL_CAR, + CRIME_RUN_REDLIGHT, + CRIME_RECKLESS_DRIVING, + CRIME_SPEEDING, + CRIME_RUNOVER_PED, + CRIME_RUNOVER_COP, + CRIME_SHOOT_HELI, + CRIME_PED_BURNED, + CRIME_COP_BURNED, + CRIME_VEHICLE_BURNED, + CRIME_DESTROYED_CESSNA, +}; + +class CCrimeBeingQd +{ +public: + eCrimeType m_nType; + uint32 m_nId; + int32 m_nTime; + CVector m_vecPosn; + bool m_bReported; + bool m_bPoliceDoesntCare; }; class CWanted @@ -23,28 +45,37 @@ public: uint8 m_CurrentCops; uint8 m_MaxCops; uint8 m_MaximumLawEnforcerVehicles; - int8 field_19; + uint8 m_CopsBeatingSuspect; int16 m_RoadblockDensity; - uint8 m_IsIgnoredByCops : 1; - uint8 m_IsIgnoredByEveryOne : 1; - uint8 m_IsSwatRequired : 1; - uint8 m_IsFbiRequired : 1; - uint8 m_IdArmyRequired : 1; - int8 field_23; + uint8 m_bIgnoredByCops : 1; + uint8 m_bIgnoredByEveryone : 1; + uint8 m_bSwatRequired : 1; + uint8 m_bFbiRequired : 1; + uint8 m_bArmyRequired : 1; int32 m_nWantedLevel; - CCrime m_sCrimes[16]; + CCrimeBeingQd m_aCrimes[16]; CCopPed *m_pCops[10]; + static int32 &MaximumWantedLevel; + static int32 &nMaximumWantedLevel; public: + void Initialise(); bool AreSwatRequired(); bool AreFbiRequired(); bool AreArmyRequired(); - int NumOfHelisRequired(); + int32 NumOfHelisRequired(); void SetWantedLevel(int32); void SetWantedLevelNoDrop(int32 level); + void RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); + void RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); void ClearQdCrimes(); + bool AddCrimeToQ(eCrimeType type, int32 id, const CVector &pos, bool reported, bool policeDoesntCare); + void ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare); void UpdateWantedLevel(); + + static int32 WorkOutPolicePresence(CVector posn, float radius); + static void SetMaximumWantedLevel(int32 level); }; static_assert(sizeof(CWanted) == 0x204, "CWanted: error"); diff --git a/src/core/config.h b/src/core/config.h index b1efd4b6..0736d343 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -62,6 +62,7 @@ enum Config { NUMONSCREENTIMERENTRIES = 1, NUMRADARBLIPS = 32, NUMPICKUPS = 336, + NUMEVENTS = 64, }; // We'll use this once we're ready to become independent of the game diff --git a/src/core/re3.cpp b/src/core/re3.cpp index 8bb9caee..4876c555 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -154,7 +154,7 @@ spawnCar(int id) } #endif -void +static void FixCar(void) { CVehicle *veh = FindPlayerVehicle(); @@ -167,6 +167,15 @@ FixCar(void) ((CAutomobile*)veh)->Fix(); } +static void +ToggleComedy(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + veh->bComedyControls = !veh->bComedyControls; +} + void DebugMenuPopulate(void) { @@ -212,6 +221,8 @@ DebugMenuPopulate(void) DebugMenuAddCmd("Cheats", "Nasty limbs", NastyLimbsCheat); DebugMenuAddCmd("Debug", "Fix Car", FixCar); + DebugMenuAddCmd("Debug", "Toggle Comedy Controls", ToggleComedy); + DebugMenuAddVarBool8("Debug", "Show Ped Road Groups", (int8*)&gbShowPedRoadGroups, nil); DebugMenuAddVarBool8("Debug", "Show Car Road Groups", (int8*)&gbShowCarRoadGroups, nil); DebugMenuAddVarBool8("Debug", "Show Collision Polys", (int8*)&gbShowCollisionPolys, nil); diff --git a/src/modelinfo/ModelIndices.h b/src/modelinfo/ModelIndices.h index 0d9ffb53..135f3424 100644 --- a/src/modelinfo/ModelIndices.h +++ b/src/modelinfo/ModelIndices.h @@ -465,3 +465,21 @@ IsPickupModel(int16 id) id == MI_PICKUP_KILLFRENZY || id == MI_PICKUP_CAMERA; } + +inline bool +IsPolicePedModel(int16 id) +{ + return id == MI_COP || + id == MI_SWAT || + id == MI_FBI || + id == MI_ARMY; +} + +inline bool +IsPoliceVehicleModel(int16 id) +{ + return id == MI_CHOPPER || + id == MI_PREDATOR || + id == MI_POLICE || + id == MI_ENFORCER; +} diff --git a/src/peds/CopPed.h b/src/peds/CopPed.h index 5827f9bc..f8139046 100644 --- a/src/peds/CopPed.h +++ b/src/peds/CopPed.h @@ -1,27 +1,6 @@ #pragma once #include "Ped.h" -enum eCrimeType -{ - CRIME_NONE, - CRIME_POSSESSION_GUN, - CRIME_HIT_PED, - CRIME_HIT_COP, - CRIME_SHOOT_PED, - CRIME_SHOOT_COP, - CRIME_STEAL_CAR, - CRIME_RUN_REDLIGHT, - CRIME_RECKLESS_DRIVING, - CRIME_SPEEDING, - CRIME_RUNOVER_PED, - CRIME_RUNOVER_COP, - CRIME_SHOOT_HELI, - CRIME_PED_BURNED, - CRIME_COP_BURNED, - CRIME_VEHICLE_BURNED, - CRIME_DESTROYED_CESSNA, -}; - enum eCopType { COP_STREET = 0, @@ -30,18 +9,6 @@ enum eCopType COP_ARMY = 3, }; -class CCrime -{ -public: - eCrimeType m_eCrimeType; - CEntity *m_pVictim; - int32 m_nCrimeTime; - CVector m_vecCrimePos; - int8 m_bReported; - int8 m_bMultiplier; - int8 pad_20[2]; -}; - class CCopPed : public CPed { public: diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp index 4b484a7f..3bab2d31 100644 --- a/src/peds/PlayerPed.cpp +++ b/src/peds/PlayerPed.cpp @@ -13,6 +13,8 @@ WRAPPER void CPlayerPed::ReApplyMoveAnims(void) { EAXJMP(0x4F07C0); } WRAPPER void CPlayerPed::SetupPlayerPed(int32) { EAXJMP(0x4EFB60); } WRAPPER void CPlayerPed::DeactivatePlayerPed(int32) { EAXJMP(0x4EFC00); } WRAPPER void CPlayerPed::ReactivatePlayerPed(int32) { EAXJMP(0x4EFC20); } +WRAPPER void CPlayerPed::KeepAreaAroundPlayerClear(void) { EAXJMP(0x4F3460); } + void CPlayerPed::ClearWeaponTarget() { diff --git a/src/peds/PlayerPed.h b/src/peds/PlayerPed.h index 51a45203..1dfa33fa 100644 --- a/src/peds/PlayerPed.h +++ b/src/peds/PlayerPed.h @@ -45,6 +45,7 @@ public: void ClearWeaponTarget(); void SetWantedLevel(int32 level); void SetWantedLevelNoDrop(int32 level); + void KeepAreaAroundPlayerClear(void); static void SetupPlayerPed(int32); static void DeactivatePlayerPed(int32); diff --git a/src/render/Draw.h b/src/render/Draw.h index ad14e5a9..75b2b75f 100644 --- a/src/render/Draw.h +++ b/src/render/Draw.h @@ -2,9 +2,12 @@ enum eAspectRatio { - AR_AUTO, + // Make sure these work the same as FrontEndMenuManager.m_PrefsUseWideScreen + // without widescreen support AR_4_3, AR_16_9, + + AR_AUTO, }; class CDraw diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index 7d3f8ee3..1f64228b 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -1,6 +1,7 @@ #include "common.h" #include "patcher.h" #include "General.h" +#include "Pad.h" #include "ModelIndices.h" #include "VisibilityPlugins.h" #include "DMAudio.h" @@ -141,8 +142,134 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) return numCollisions; } +static int16 nLastControlInput; +static float fMouseCentreRange = 0.35f; +static float fMouseSteerSens = -0.0035f; +static float fMouseCentreMult = 0.975f; -WRAPPER void CAutomobile::ProcessControlInputs(uint8) { EAXJMP(0x53B660); } +void +CAutomobile::ProcessControlInputs(uint8 pad) +{ + float speed = DotProduct(m_vecMoveSpeed, GetForward()); + + if(CPad::GetPad(pad)->GetExitVehicle()) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + + // Steer left/right + if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ + if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ + m_fSteerRatio += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); + nLastControlInput = 2; + if(Abs(m_fSteerRatio) < fMouseCentreRange) + m_fSteerRatio *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); + }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ + // mouse hasn't move, steer with pad like below + m_fSteerRatio += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerRatio)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + }else{ + m_fSteerRatio += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerRatio)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + m_fSteerRatio = clamp(m_fSteerRatio, -1.0f, 1.0f); + + // Accelerate/Brake + float acceleration = (CPad::GetPad(pad)->GetAccelerate() - CPad::GetPad(pad)->GetBrake())/255.0f; + if(GetModelIndex() == MI_DODO && acceleration < 0.0f) + acceleration *= 0.3f; + if(Abs(speed) < 0.01f){ + // standing still, go into direction we want + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ +#if 1 + // simpler than the code below + if(speed * acceleration < 0.0f){ + // if opposite directions, have to brake first + m_fGasPedal = 0.0f; + m_fBrakePedal = Abs(acceleration); + }else{ + // accelerating in same direction we were already going + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } +#else + if(speed < 0.0f){ + // moving backwards currently + if(acceleration < 0.0f){ + // still go backwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ + // want to go forwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = acceleration; + } + }else{ + // moving forwards currently + if(acceleration < 0.0f){ + // want to go backwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = -acceleration; + }else{ + // still go forwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + } +#endif + } + + // Actually turn wheels + static float fValue; // why static? + if(m_fSteerRatio < 0.0f) + fValue = -sq(m_fSteerRatio); + else + fValue = sq(m_fSteerRatio); + m_fSteerAngle = DEGTORAD(m_handling->fSteeringLock) * fValue; + + if(bComedyControls){ + int rnd = CGeneral::GetRandomNumber() % 10; + switch(m_comedyControlState){ + case 0: + if(rnd < 2) + m_comedyControlState = 1; + else if(rnd < 4) + m_comedyControlState = 2; + break; + case 1: + m_fSteerAngle += 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + case 2: + m_fSteerAngle -= 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + } + }else + m_comedyControlState = 0; + + // Brake if player isn't in control + // BUG: game always uses pad 0 here + if(CPad::GetPad(pad)->DisablePlayerControls){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + m_fGasPedal = 0.0f; + + FindPlayerPed()->KeepAreaAroundPlayerClear(); + + // slow down car immediately + speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.28f) + m_vecMoveSpeed *= 0.28f/speed; + } +} void CAutomobile::GetComponentWorldPosition(int32 component, CVector &pos) @@ -1091,7 +1218,7 @@ public: int32 ProcessEntityCollision_(CEntity *ent, CColPoint *colpoints){ return CAutomobile::ProcessEntityCollision(ent, colpoints); } - void ProcessControlInputs_(uint8 x) { CAutomobile::ProcessControlInputs(x); } + void ProcessControlInputs_(uint8 pad) { CAutomobile::ProcessControlInputs(pad); } void GetComponentWorldPosition_(int32 component, CVector &pos) { CAutomobile::GetComponentWorldPosition(component, pos); } bool IsComponentPresent_(int32 component) { return CAutomobile::IsComponentPresent(component); } void SetComponentRotation_(int32 component, CVector rotation) { CAutomobile::SetComponentRotation(component, rotation); } @@ -1115,6 +1242,7 @@ STARTPATCHES InjectHook(0x52D190, &CAutomobile_::SetModelIndex_, PATCH_JUMP); InjectHook(0x535180, &CAutomobile_::Teleport_, PATCH_JUMP); InjectHook(0x53B270, &CAutomobile_::ProcessEntityCollision_, PATCH_JUMP); + InjectHook(0x53B660, &CAutomobile_::ProcessControlInputs_, PATCH_JUMP); InjectHook(0x52E5F0, &CAutomobile_::GetComponentWorldPosition_, PATCH_JUMP); InjectHook(0x52E660, &CAutomobile_::IsComponentPresent_, PATCH_JUMP); InjectHook(0x52E680, &CAutomobile_::SetComponentRotation_, PATCH_JUMP); diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index d8ed1a15..b21c859a 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -58,7 +58,7 @@ CVehicle::CVehicle(uint8 CreatedBy) m_nBombTimer = 0; m_pWhoSetMeOnFire = nil; field_1FB = 0; - m_veh_flagB10 = false; + bComedyControls = false; m_veh_flagB40 = false; m_veh_flagB80 = false; m_veh_flagC1 = false; @@ -93,7 +93,7 @@ CVehicle::CVehicle(uint8 CreatedBy) m_pCurGroundEntity = nil; field_22A = 0; field_22B = 0; - field_22F = 0; + m_comedyControlState = 0; m_aCollPolys[0].valid = false; m_aCollPolys[1].valid = false; m_autoPilot.m_nCarMission = MISSION_NONE; diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h index c293b8a6..55805e9d 100644 --- a/src/vehicles/Vehicle.h +++ b/src/vehicles/Vehicle.h @@ -144,7 +144,7 @@ public: CFire *m_pCarFire; float m_fSteerAngle; float m_fGasPedal; - float m_fBreakPedal; + float m_fBrakePedal; uint8 VehicleCreatedBy; // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R* @@ -161,7 +161,7 @@ public: 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 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way) uint8 m_veh_flagB20 : 1; uint8 m_veh_flagB40 : 1; uint8 m_veh_flagB80 : 1; @@ -207,7 +207,7 @@ public: uint8 m_nCarHornTimer; int8 field_22D; bool m_bSirenOrAlarm; - int8 field_22F; + int8 m_comedyControlState; CStoredCollPoly m_aCollPolys[2]; // poly which is under front/rear part of car float m_fSteerRatio; eVehicleType m_vehType; |