diff --git a/README.md b/README.md index ba6d04c3..0c509a16 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ CMotionBlurStreaks CObject CPacManPickups CPedPath -CPopulation CRadar CRecordDataForChase CRoadBlocks diff --git a/src/control/GameLogic.cpp b/src/control/GameLogic.cpp index ccba05c9..1e5b72c3 100644 --- a/src/control/GameLogic.cpp +++ b/src/control/GameLogic.cpp @@ -249,7 +249,7 @@ CGameLogic::RestorePlayerStuffDuringResurrection(CPlayerPed *pPlayerPed, CVector pPlayerPed->m_fHealth = 100.0f; pPlayerPed->m_fArmour = 0.0f; pPlayerPed->bIsVisible = true; - pPlayerPed->m_bloodyFootprintCount = 0; + pPlayerPed->m_bloodyFootprintCountOrDeathTime = 0; pPlayerPed->bDoBloodyFootprints = false; pPlayerPed->ClearAdrenaline(); pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina; diff --git a/src/core/PlayerInfo.cpp b/src/core/PlayerInfo.cpp index 05f3984c..e0c0259e 100644 --- a/src/core/PlayerInfo.cpp +++ b/src/core/PlayerInfo.cpp @@ -153,7 +153,7 @@ void CPlayerInfo::CancelPlayerEnteringCars(CVehicle *car) { if (!car || car == m_pPed->m_pMyVehicle) { - if (m_pPed->m_nPedState == PED_CARJACK || m_pPed->m_nPedState == PED_ENTER_CAR) + if (m_pPed->EnteringCar()) m_pPed->QuitEnteringCar(); } if (m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) diff --git a/src/core/Streaming.cpp b/src/core/Streaming.cpp index b0933063..3a830d37 100644 --- a/src/core/Streaming.cpp +++ b/src/core/Streaming.cpp @@ -1287,7 +1287,7 @@ CStreaming::StreamVehiclesAndPeds(void) if(timeBeforeNextLoad >= 0) timeBeforeNextLoad--; else if(ms_numVehiclesLoaded <= desiredNumVehiclesLoaded){ - for(i = 0; i <= 10; i++){ + for(i = 1; i <= 10; i++){ model = CCarCtrl::ChooseCarModel(modelQualityClass); modelQualityClass++; if(modelQualityClass >= NUM_VEHICLE_CLASSES) @@ -1893,9 +1893,9 @@ CStreaming::AddModelsToRequestList(const CVector &pos) CWorld::AdvanceCurrentScanCode(); - for(iy = iymin; iy < iymax; iy++){ + for(iy = iymin; iy <= iymax; iy++){ dy = iy - CWorld::GetSectorIndexY(pos.y); - for(ix = ixmin; ix < ixmax; ix++){ + for(ix = ixmin; ix <= ixmax; ix++){ if(CRenderer::m_loadingPriority && ms_numModelsRequested > 5) return; @@ -2124,7 +2124,7 @@ CStreaming::DeleteRwObjectsAfterDeath(const CVector &pos) CSector *sect; ix = CWorld::GetSectorIndexX(pos.x); - iy = CWorld::GetSectorIndexX(pos.y); + iy = CWorld::GetSectorIndexY(pos.y); for(x = 0; x < NUMSECTORS_X; x++) for(y = 0; y < NUMSECTORS_Y; y++) @@ -2153,13 +2153,13 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) return; ix = CWorld::GetSectorIndexX(TheCamera.GetPosition().x); - iy = CWorld::GetSectorIndexX(TheCamera.GetPosition().y); + iy = CWorld::GetSectorIndexY(TheCamera.GetPosition().y); if(Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y)){ // looking west/east ymin = max(iy - 10, 0); - ymax = min(iy + 10, NUMSECTORS_Y); + ymax = min(iy + 10, NUMSECTORS_Y - 1); assert(ymin <= ymax); // Delete a block of sectors that we know is behind the camera @@ -2170,8 +2170,8 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) inc = 1; }else{ // looking west - xmax = min(ix + 2, NUMSECTORS_X); - xmin = min(ix + 10, NUMSECTORS_X); + xmax = min(ix + 2, NUMSECTORS_X - 1); + xmin = min(ix + 10, NUMSECTORS_X - 1); inc = -1; } for(y = ymin; y <= ymax; y++){ @@ -2192,8 +2192,8 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) inc = 1; }else{ // looking west - xmax = min(ix - 10, NUMSECTORS_X); - xmin = min(ix + 2, NUMSECTORS_X); + xmax = min(ix - 10, NUMSECTORS_X - 1); + xmin = min(ix + 2, NUMSECTORS_X - 1); inc = -1; } for(y = ymin; y <= ymax; y++){ @@ -2223,7 +2223,7 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) // looking north/south xmin = max(ix - 10, 0); - xmax = min(ix + 10, NUMSECTORS_X); + xmax = min(ix + 10, NUMSECTORS_X - 1); assert(xmin <= xmax); // Delete a block of sectors that we know is behind the camera @@ -2234,8 +2234,8 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) inc = 1; }else{ // looking south - ymax = min(iy + 2, NUMSECTORS_Y); - ymin = min(iy + 10, NUMSECTORS_Y); + ymax = min(iy + 2, NUMSECTORS_Y - 1); + ymin = min(iy + 10, NUMSECTORS_Y - 1); inc = -1; } for(x = xmin; x <= xmax; x++){ @@ -2256,8 +2256,8 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) inc = 1; }else{ // looking south - ymax = min(iy - 10, NUMSECTORS_Y); - ymin = min(iy + 2, NUMSECTORS_Y); + ymax = min(iy - 10, NUMSECTORS_Y - 1); + ymin = min(iy + 2, NUMSECTORS_Y - 1); inc = -1; } for(x = xmin; x <= xmax; x++){ diff --git a/src/core/World.cpp b/src/core/World.cpp index f97f0ebf..f6106bb5 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -143,9 +143,9 @@ CWorld::ProcessLineOfSight(const CVector &point1, const CVector &point2, CColPoi dist = 1.0f; xstart = GetSectorIndexX(point1.x); - ystart = GetSectorIndexX(point1.y); + ystart = GetSectorIndexY(point1.y); xend = GetSectorIndexX(point2.x); - yend = GetSectorIndexX(point2.y); + yend = GetSectorIndexY(point2.y); #define LOSARGS CColLine(point1, point2), point, dist, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, ignoreSeeThrough, ignoreSomeObjects @@ -416,9 +416,9 @@ CWorld::GetIsLineOfSightClear(const CVector &point1, const CVector &point2, bool AdvanceCurrentScanCode(); xstart = GetSectorIndexX(point1.x); - ystart = GetSectorIndexX(point1.y); + ystart = GetSectorIndexY(point1.y); xend = GetSectorIndexX(point2.x); - yend = GetSectorIndexX(point2.y); + yend = GetSectorIndexY(point2.y); #define LOSARGS CColLine(point1, point2), checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, ignoreSeeThrough, ignoreSomeObjects @@ -638,20 +638,24 @@ void CWorld::FindObjectsInRange(CVector ¢re, float distance, bool ignoreZ, short *nextObject, short lastObject, CEntity **objects, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies) { int minX = GetSectorIndexX(centre.x - distance); - if (minX <= 0) - minX = 0; + if (minX <= 0) minX = 0; int minY = GetSectorIndexY(centre.y - distance); - if (minY <= 0) - minY = 0; + if (minY <= 0) minY = 0; int maxX = GetSectorIndexX(centre.x + distance); - if (maxX >= NUMSECTORS_X) - maxX = NUMSECTORS_X; +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif int maxY = GetSectorIndexY(centre.y + distance); - if (maxY >= NUMSECTORS_Y) - maxY = NUMSECTORS_Y; +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif AdvanceCurrentScanCode(); @@ -689,20 +693,24 @@ CWorld::TestSphereAgainstWorld(CVector centre, float distance, CEntity *entityTo CEntity* foundE = nil; int minX = GetSectorIndexX(centre.x - distance); - if (minX <= 0) - minX = 0; + if (minX <= 0) minX = 0; int minY = GetSectorIndexY(centre.y - distance); - if (minY <= 0) - minY = 0; + if (minY <= 0) minY = 0; int maxX = GetSectorIndexX(centre.x + distance); - if (maxX >= NUMSECTORS_X) - maxX = NUMSECTORS_X; +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif int maxY = GetSectorIndexY(centre.y + distance); - if (maxY >= NUMSECTORS_Y) - maxY = NUMSECTORS_Y; +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif AdvanceCurrentScanCode(); @@ -1171,8 +1179,7 @@ CWorld::Process(void) for (CPtrNode* node = ms_listMovingEntityPtrs.first; node; node = node->next) { CPed* movingPed = (CPed*)node->item; if (movingPed->IsPed()) { - if (movingPed->bInVehicle && movingPed->m_nPedState != PED_EXIT_TRAIN - || movingPed->m_nPedState == PED_ENTER_CAR || movingPed->m_nPedState == PED_CARJACK) { + if (movingPed->bInVehicle && movingPed->m_nPedState != PED_EXIT_TRAIN || movingPed->EnteringCar()) { CVehicle *movingCar = movingPed->m_pMyVehicle; if (movingCar) { if (movingCar->IsTrain()) { diff --git a/src/objects/DummyObject.cpp b/src/objects/DummyObject.cpp index 41b15129..9649cf7a 100644 --- a/src/objects/DummyObject.cpp +++ b/src/objects/DummyObject.cpp @@ -16,8 +16,12 @@ class CDummyObject_ : public CDummyObject { public: void dtor(void) { CDummyObject::~CDummyObject(); } + CDummyObject *ctor(void) { return ::new (this) CDummyObject(); } + CDummyObject *ctor(CObject *obj) { return ::new (this) CDummyObject(obj); } }; STARTPATCHES + InjectHook(0x4BAAF0, (CDummyObject* (CDummyObject::*)(void)) &CDummyObject_::ctor, PATCH_JUMP); + InjectHook(0x4BAB10, (CDummyObject* (CDummyObject::*)(CObject*)) &CDummyObject_::ctor, PATCH_JUMP); InjectHook(0x4BAB70, &CDummyObject_::dtor, PATCH_JUMP); -ENDPATCHES +ENDPATCHES \ No newline at end of file diff --git a/src/objects/Object.h b/src/objects/Object.h index 1c33b07f..b9c570f5 100644 --- a/src/objects/Object.h +++ b/src/objects/Object.h @@ -52,7 +52,7 @@ public: int8 field_17D; int8 field_17E; int8 field_17F; - int32 m_nEndOfLifeTime; + uint32 m_nEndOfLifeTime; int16 m_nRefModelIndex; int8 field_186; int8 field_187; diff --git a/src/peds/CopPed.cpp b/src/peds/CopPed.cpp index 40481ad9..bf0cc59c 100644 --- a/src/peds/CopPed.cpp +++ b/src/peds/CopPed.cpp @@ -1,10 +1,14 @@ #include "common.h" #include "patcher.h" +#include "World.h" +#include "PlayerPed.h" #include "CopPed.h" #include "ModelIndices.h" +#include "Vehicle.h" +#include "RpAnimBlend.h" +#include "General.h" WRAPPER void CCopPed::ProcessControl() { EAXJMP(0x4C1400); } -WRAPPER void CCopPed::SetArrestPlayer(CPed*) { EAXJMP(0x4C2B00); } CCopPed::CCopPed(eCopType copType) : CPed(PEDTYPE_COP) { @@ -66,7 +70,210 @@ CCopPed::~CCopPed() ClearPursuit(); } -WRAPPER void CCopPed::ClearPursuit(void) { EAXJMP(0x4C28C0); } +// Parameter should always be CPlayerPed, but it seems they considered making civilians arrestable at some point +void +CCopPed::SetArrestPlayer(CPed *player) +{ + if (!IsPedInControl() || !player) + return; + + switch (m_nCopType) { + case COP_FBI: + Say(SOUND_PED_ARREST_FBI); + break; + case COP_SWAT: + Say(SOUND_PED_ARREST_SWAT); + break; + default: + Say(SOUND_PED_ARREST_COP); + break; + } + if (player->EnteringCar()) { + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) + return; + + // why? + player->bGonnaKillTheCarJacker = true; + + // Genius + FindPlayerPed()->m_bCanBeDamaged = false; + ((CPlayerPed*)player)->m_pArrestingCop = this; + this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); + + } else if (player->m_nPedState != PED_DIE && player->m_nPedState != PED_DEAD && player->m_nPedState != PED_ARRESTED) { + player->m_nLastPedState = player->m_nPedState; + player->m_nPedState = PED_ARRESTED; + + FindPlayerPed()->m_bCanBeDamaged = false; + ((CPlayerPed*)player)->m_pArrestingCop = this; + this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); + } + + m_nPedState = PED_ARREST_PLAYER; + SetObjective(OBJECTIVE_NONE); + m_prevObjective = OBJECTIVE_NONE; + bIsPointingGunAt = false; + m_pSeekTarget = player; + m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + SetCurrentWeapon(WEAPONTYPE_COLT45); + if (player->InVehicle()) { + player->m_pMyVehicle->m_nNumGettingIn = 0; + player->m_pMyVehicle->m_nGettingInFlags = 0; + player->m_pMyVehicle->bIsHandbrakeOn = true; + player->m_pMyVehicle->m_status = STATUS_PLAYER_DISABLED; + } + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) + SetCurrentWeapon(WEAPONTYPE_COLT45); +} + +void +CCopPed::ClearPursuit(void) +{ + CPlayerPed *player = FindPlayerPed(); + if (!player) + return; + + CWanted *wanted = player->m_pWanted; + int ourCopId = 0; + bool foundMyself = false; + int biggestCopId = 0; + if (!m_bIsInPursuit) + return; + + m_bIsInPursuit = false; + for (int i = 0; i < max(wanted->m_MaxCops, wanted->m_CurrentCops); ++i) { + if (!foundMyself && wanted->m_pCops[i] == this) { + wanted->m_pCops[i] = nil; + --wanted->m_CurrentCops; + foundMyself = true; + ourCopId = i; + biggestCopId = i; + } else { + if (wanted->m_pCops[i]) + biggestCopId = i; + } + } + if (foundMyself && biggestCopId > ourCopId) { + wanted->m_pCops[ourCopId] = wanted->m_pCops[biggestCopId]; + wanted->m_pCops[biggestCopId] = nil; + } + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + m_nLastPedState = PED_NONE; + bIsRunning = false; + bNotAllowedToDuck = false; + bKindaStayInSamePlace = false; + m_bZoneDisabledButClose = false; + m_bZoneDisabled = false; + ClearObjective(); + if (IsPedInControl()) { + if (!m_pMyVehicle || wanted->m_nWantedLevel != 0) { + if (m_pMyVehicle && (m_pMyVehicle->GetPosition() - GetPosition()).MagnitudeSqr() < sq(5.0f)) { + m_nLastPedState = PED_IDLE; + SetSeek((CEntity*)m_pMyVehicle, 2.5f); + } else { + m_nLastPedState = PED_WANDER_PATH; + SetFindPathAndFlee(FindPlayerPed()->GetPosition(), 10000, true); + } + } else { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } +} + +// TO-DO: m_MaxCops in for loop may be a bug, check it out after CopAI +void +CCopPed::SetPursuit(bool iMayAlreadyBeInPursuit) +{ + CWanted *wanted = FindPlayerPed()->m_pWanted; + if (m_bIsInPursuit || !IsPedInControl()) + return; + + if (wanted->m_CurrentCops < wanted->m_MaxCops || iMayAlreadyBeInPursuit) { + for (int i = 0; i < wanted->m_MaxCops; ++i) { + if (!wanted->m_pCops[i]) { + m_bIsInPursuit = true; + ++wanted->m_CurrentCops; + wanted->m_pCops[i] = this; + break; + } + } + if (m_bIsInPursuit) { + ClearObjective(); + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed()); + SetObjectiveTimer(0); + bNotAllowedToDuck = true; + bIsRunning = true; + m_bZoneDisabledButClose = false; + } + } +} + +void +CCopPed::ArrestPlayer(void) +{ + m_pVehicleAnim = nil; + CPed *suspect = (CPed*)m_pSeekTarget; + if (suspect) { + if (suspect->CanSetPedState()) + suspect->m_nPedState = PED_ARRESTED; + + if (suspect->bInVehicle && m_pMyVehicle && suspect->m_pMyVehicle == m_pMyVehicle) { + + // BUG? I will never understand why they used LINE_UP_TO_CAR_2... + LineUpPedWithCar(LINE_UP_TO_CAR_2); + } + + if (suspect && (suspect->m_nPedState == PED_ARRESTED || suspect->DyingOrDead() || suspect->EnteringCar())) { + + CAnimBlendAssociation *arrestAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ARREST_GUN); + if (!arrestAssoc || arrestAssoc->blendDelta < 0.0f) + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_ARREST_GUN, 4.0f); + + CVector suspMidPos; + suspect->m_pedIK.GetComponentPosition((RwV3d*)suspMidPos, PED_MID); + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(suspMidPos.x, suspMidPos.y, + GetPosition().x, GetPosition().y); + + m_fRotationCur = m_fRotationDest; + SetOrientation(0.0f, 0.0f, m_fRotationCur); + } else { + ClearPursuit(); + } + } else { + ClearPursuit(); + } +} + +void +CCopPed::ScanForCrimes(void) +{ + CVehicle *playerVeh = FindPlayerVehicle(); + + // Look for car alarms + if (playerVeh && playerVeh->IsCar()) { + if (playerVeh->IsAlarmOn()) { + if ((playerVeh->GetPosition() - GetPosition()).MagnitudeSqr() < sq(20.0f)) + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + } + + // Look for stolen cop cars (it was broken until now) + if (!m_bIsInPursuit) { + CPlayerPed *player = FindPlayerPed(); +#ifdef FIX_BUGS + if ((player->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || player->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) +#else + if ((m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) +#endif + && player->m_pWanted->m_nWantedLevel == 0 && player->m_pMyVehicle) { + + if (player->m_pMyVehicle->bIsLawEnforcer) + player->SetWantedLevelNoDrop(1); + } + } +} class CCopPed_ : public CCopPed { @@ -78,4 +285,9 @@ public: STARTPATCHES InjectHook(0x4C11B0, &CCopPed_::ctor, PATCH_JUMP); InjectHook(0x4C13E0, &CCopPed_::dtor, PATCH_JUMP); + InjectHook(0x4C28C0, &CCopPed::ClearPursuit, PATCH_JUMP); + InjectHook(0x4C2B00, &CCopPed::SetArrestPlayer, PATCH_JUMP); + InjectHook(0x4C27D0, &CCopPed::SetPursuit, PATCH_JUMP); + InjectHook(0x4C2C90, &CCopPed::ArrestPlayer, PATCH_JUMP); + InjectHook(0x4C26A0, &CCopPed::ScanForCrimes, PATCH_JUMP); ENDPATCHES diff --git a/src/peds/CopPed.h b/src/peds/CopPed.h index e0f00b7e..7705eb12 100644 --- a/src/peds/CopPed.h +++ b/src/peds/CopPed.h @@ -37,6 +37,9 @@ public: void ClearPursuit(void); void ProcessControl(void); void SetArrestPlayer(CPed*); + void SetPursuit(bool); + void ArrestPlayer(void); + void ScanForCrimes(void); }; static_assert(sizeof(CCopPed) == 0x558, "CCopPed: error"); diff --git a/src/peds/EmergencyPed.cpp b/src/peds/EmergencyPed.cpp index fbccc8a0..ee559f57 100644 --- a/src/peds/EmergencyPed.cpp +++ b/src/peds/EmergencyPed.cpp @@ -303,7 +303,7 @@ CEmergencyPed::MedicAI(void) if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f || m_pRevivedPed->bFadeOut) { m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; } else { - m_pRevivedPed->m_bloodyFootprintCount = CTimer::GetTimeInMilliseconds(); + m_pRevivedPed->m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); SetMoveState(PEDMOVE_STILL); m_nPedState = PED_CPR; m_nLastPedState = PED_CPR; diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index f4e7ce2f..3c62512e 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -49,19 +49,56 @@ #include "ParticleObject.h" #include "Floater.h" -#define FEET_OFFSET 1.04f +#define CAN_SEE_ENTITY_ANGLE_THRESHOLD DEGTORAD(60.0f) CPed *gapTempPedList[50]; uint16 gnNumTempPedList; -bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; -bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; -bool &CPed::bPedCheat3 = *(bool*)0x95CD59; - CColPoint &aTempPedColPts = *(CColPoint*)0x62DB14; -// TODO: CommentWaitTime should be hardcoded into exe, and it isn't reversed yet. -CPedAudioData (&CPed::CommentWaitTime)[38] = *(CPedAudioData(*)[38]) * (uintptr*)0x5F94C4; +// Corresponds to ped sounds (from SOUND_PED_DEATH to SOUND_PED_TAXI_CALL) +PedAudioData CommentWaitTime[39] = { + {500, 800, 500, 2}, + {500, 800, 500, 2}, + {500, 800, 500, 2}, + {500, 800, 500, 2}, + {100, 2, 100, 2}, + {700, 500, 1000, 500}, + {700, 500, 1000, 500}, + {5000, 2000, 15000, 3000}, + {5000, 2000, 15000, 3000}, + {5000, 2000, 15000, 3000}, + {6000, 6000, 6000, 6000}, + {1000, 1000, 2000, 2000}, + {1000, 500, 2000, 1500}, + {1000, 500, 2000, 1500}, + {800, 200, 1000, 500}, + {800, 200, 1000, 500}, + {800, 400, 2000, 1000}, + {800, 400, 2000, 1000}, + {400, 300, 2000, 1000}, + {2000, 1000, 2500, 1500}, + {200, 200, 200, 200}, + {6000, 3000, 5000, 6000}, + {6000, 3000, 9000, 5000}, + {6000, 3000, 9000, 5000}, + {6000, 3000, 9000, 5000}, + {400, 300, 4000, 1000}, + {400, 300, 4000, 1000}, + {400, 300, 4000, 1000}, + {1000, 500, 3000, 1000}, + {1000, 500, 1000, 1000}, + {3000, 2000, 3000, 2000}, + {1000, 500, 3000, 6000}, + {1000, 500, 2000, 4000}, + {1000, 500, 2000, 5000}, + {1000, 500, 3000, 2000}, + {1600, 1000, 2000, 2000}, + {3000, 2000, 5000, 3000}, + {1000, 1000, 1000, 1000}, + {1000, 1000, 5000, 5000}, +}; +// *(CPedAudioData(*)[39]) * (uintptr*)0x5F94C4; uint16 nPlayerInComboMove; @@ -106,6 +143,9 @@ CVector vecPedQuickDraggedOutCarAnimOffset; CVector vecPedDraggedOutCarAnimOffset; CVector vecPedTrainDoorAnimOffset; +bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; +bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; +bool &CPed::bPedCheat3 = *(bool*)0x95CD59; CVector2D CPed::ms_vec2DFleePosition; void *CPed::operator new(size_t sz) { return CPools::GetPedPool()->New(); } @@ -351,7 +391,7 @@ CPed::~CPed(void) m_pMyVehicle->m_nGettingOutFlags &= ~door_flag; bInVehicle = false; m_pMyVehicle = nil; - } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK){ + } else if (EnteringCar()) { QuitEnteringCar(); } if (m_pFire) @@ -745,8 +785,7 @@ CPed::UseGroundColModel(void) bool CPed::CanSetPedState(void) { - return m_nPedState != PED_DIE && m_nPedState != PED_ARRESTED && - m_nPedState != PED_ENTER_CAR && m_nPedState != PED_DEAD && m_nPedState != PED_CARJACK && m_nPedState != PED_STEAL_CAR; + return !DyingOrDead() && m_nPedState != PED_ARRESTED && !EnteringCar() && m_nPedState != PED_STEAL_CAR; } bool @@ -1912,7 +1951,7 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) // Smoothly change ped position neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; - } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK) { + } else if (EnteringCar()) { neededPos.z = max(currentZ, autoZPos.z); } #ifdef VC_PED_PORTS @@ -1999,10 +2038,10 @@ void CPed::PlayFootSteps(void) { if (bDoBloodyFootprints) { - if (m_bloodyFootprintCount > 0 && m_bloodyFootprintCount < 300) { - m_bloodyFootprintCount--; + if (m_bloodyFootprintCountOrDeathTime > 0 && m_bloodyFootprintCountOrDeathTime < 300) { + m_bloodyFootprintCountOrDeathTime--; - if (m_bloodyFootprintCount == 0) + if (m_bloodyFootprintCountOrDeathTime == 0) bDoBloodyFootprints = false; } } @@ -2055,11 +2094,11 @@ CPed::PlayFootSteps(void) right.x, right.y, 255, 255, 0, 0, 4.0f, 3000.0f, 1.0f); - if (m_bloodyFootprintCount <= 20) { - m_bloodyFootprintCount = 0; + if (m_bloodyFootprintCountOrDeathTime <= 20) { + m_bloodyFootprintCountOrDeathTime = 0; bDoBloodyFootprints = false; } else { - m_bloodyFootprintCount -= 20; + m_bloodyFootprintCountOrDeathTime -= 20; } } if (CWeather::Rain <= 0.1f || CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) { @@ -2482,7 +2521,7 @@ CPed::CanPedReturnToState(void) } bool -CPed::CanSeeEntity(CEntity *entity, float threshold) +CPed::CanSeeEntity(CEntity *entity, float threshold = CAN_SEE_ENTITY_ANGLE_THRESHOLD) { float neededAngle = CGeneral::GetRadianAngleBetweenPoints( entity->GetPosition().x, @@ -3353,7 +3392,7 @@ CPed::CheckForPointBlankPeds(CPed *pedToVerify) if (nearPedState == PED_FALL || nearPedState == PED_GETUP || nearPedState == PED_DIE || nearPedState == PED_DEAD || nearPedState == PED_DIVE_AWAY) return NO_POINT_BLANK_PED; - if (neededTurn < DEGTORAD(60.0f)) { + if (neededTurn < CAN_SEE_ENTITY_ANGLE_THRESHOLD) { if (pedToVerify == nearPed) return POINT_BLANK_FOR_WANTED_PED; else @@ -3415,6 +3454,13 @@ CPed::ClearAttack(void) if (m_nPedState != PED_ATTACK || bIsDucking || m_nWaitState == WAITSTATE_PLAYANIM_DUCK) return; +#ifdef VC_PED_PORTS + // VC uses CCamera::Using1stPersonWeaponMode + if (FindPlayerPed() == this && (TheCamera.PlayerWeaponMode.Mode == CCam::MODE_SNIPER || + TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER)) { + SetPointGunAt(nil); + } else +#endif if (bIsPointingGunAt) { if (m_pLookTarget) SetPointGunAt(m_pLookTarget); @@ -3504,7 +3550,7 @@ CPed::SetDie(AnimationId animId, float delta, float speed) } else if (bInVehicle) { if (m_pVehicleAnim) m_pVehicleAnim->blendDelta = -1000.0f; - } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK) { + } else if (EnteringCar()) { QuitEnteringCar(); } @@ -3529,8 +3575,7 @@ CPed::SetDie(AnimationId animId, float delta, float speed) if (!bInVehicle) StopNonPartialAnims(); - // BUG: This is not timer. - m_bloodyFootprintCount = CTimer::GetTimeInMilliseconds(); + m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); } bool @@ -5924,8 +5969,7 @@ CPed::SetDead(void) CreateDeadPedMoney(); } - // BUG: Is this count or timer?! - m_bloodyFootprintCount = CTimer::GetTimeInMilliseconds(); + m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); m_deadBleeding = false; bDoBloodyFootprints = false; bVehExitWillBeInstant = false; @@ -6066,7 +6110,7 @@ CPed::DuckAndCover(void) CVector pos = GetPosition(); int16 lastVehicle; CEntity *vehicles[8]; - CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle *veh = (CVehicle*) vehicles[i]; @@ -6971,11 +7015,11 @@ CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg) 0.14f * ped->GetRight().y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); - if (ped->m_bloodyFootprintCount <= 40) { - ped->m_bloodyFootprintCount = 0; + if (ped->m_bloodyFootprintCountOrDeathTime <= 40) { + ped->m_bloodyFootprintCountOrDeathTime = 0; ped->bDoBloodyFootprints = false; } else { - ped->m_bloodyFootprintCount -= 40; + ped->m_bloodyFootprintCountOrDeathTime -= 40; } } } @@ -8674,18 +8718,29 @@ CPed::LookForInterestingNodes(void) bool found = false; uint8 randVal = CGeneral::GetRandomNumber() % 256; - int minX = CWorld::GetSectorIndexX(GetPosition().x - 15.0f); + int minX = CWorld::GetSectorIndexX(GetPosition().x - CHECK_NEARBY_THINGS_MAX_DIST); if (minX < 0) minX = 0; - int minY = CWorld::GetSectorIndexY(GetPosition().y - 15.0f); + int minY = CWorld::GetSectorIndexY(GetPosition().y - CHECK_NEARBY_THINGS_MAX_DIST); if (minY < 0) minY = 0; - int maxX = CWorld::GetSectorIndexX(GetPosition().x + 15.0f); - if (maxX > NUMSECTORS_X) maxX = NUMSECTORS_X; - int maxY = CWorld::GetSectorIndexY(GetPosition().y + 15.0f); - if (maxY > NUMSECTORS_Y) maxY = NUMSECTORS_Y; + int maxX = CWorld::GetSectorIndexX(GetPosition().x + CHECK_NEARBY_THINGS_MAX_DIST); +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(GetPosition().y + CHECK_NEARBY_THINGS_MAX_DIST); +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif for (int curY = minY; curY <= maxY && !found; curY++) { for (int curX = minX; curX <= maxX && !found; curX++) { - for (ptrNode = CWorld::GetSector(curX, curY)->m_lists[ENTITYLIST_VEHICLES].first; ptrNode && !found; ptrNode = ptrNode->next) { + CSector *sector = CWorld::GetSector(curX, curY); + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode && !found; ptrNode = ptrNode->next) { CVehicle *veh = (CVehicle*)ptrNode->item; model = veh->GetModelInfo(); if (model->m_num2dEffects != 0) { @@ -8703,7 +8758,7 @@ CPed::LookForInterestingNodes(void) } } } - for (ptrNode = CWorld::GetSector(curX, curY)->m_lists[ENTITYLIST_OBJECTS].first; ptrNode && !found; ptrNode = ptrNode->next) { + for (ptrNode = sector->m_lists[ENTITYLIST_OBJECTS].first; ptrNode && !found; ptrNode = ptrNode->next) { CObject *obj = (CObject*)ptrNode->item; model = CModelInfo::GetModelInfo(obj->m_modelIndex); if (model->m_num2dEffects != 0) { @@ -8721,7 +8776,7 @@ CPed::LookForInterestingNodes(void) } } } - for (ptrNode = CWorld::GetSector(curX, curY)->m_lists[ENTITYLIST_BUILDINGS].first; ptrNode && !found; ptrNode = ptrNode->next) { + for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS].first; ptrNode && !found; ptrNode = ptrNode->next) { CBuilding *building = (CBuilding*)ptrNode->item; model = CModelInfo::GetModelInfo(building->m_modelIndex); if (model->m_num2dEffects != 0) { @@ -8739,7 +8794,7 @@ CPed::LookForInterestingNodes(void) } } } - for (ptrNode = CWorld::GetSector(curX, curY)->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first; ptrNode && !found; ptrNode = ptrNode->next) { + for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first; ptrNode && !found; ptrNode = ptrNode->next) { CBuilding *building = (CBuilding*)ptrNode->item; model = CModelInfo::GetModelInfo(building->m_modelIndex); if (model->m_num2dEffects != 0) { @@ -8847,11 +8902,8 @@ CPed::LookForSexyPeds(void) return; for (int i = 0; i < m_numNearPeds; i++) { - - if (CanSeeEntity(m_nearPeds[i], DEGTORAD(60.0f))) { - + if (CanSeeEntity(m_nearPeds[i])) { if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 10.0f) { - CPed *nearPed = m_nearPeds[i]; if ((nearPed->m_pedStats->m_sexiness > m_pedStats->m_sexiness) && nearPed->m_nPedType == PEDTYPE_CIVFEMALE) { @@ -9097,7 +9149,7 @@ CPed::PedAnimAlignCB(CAnimBlendAssociation *animAssoc, void *arg) if (!ped->IsNotInWreckedVehicle()) return; - if (ped->m_nPedState != PED_ENTER_CAR && ped->m_nPedState != PED_CARJACK) { + if (!ped->EnteringCar()) { ped->QuitEnteringCar(); return; } @@ -9237,7 +9289,7 @@ CPed::ProcessControl(void) #else if (CGame::nastyGame && !bIsInWater) { #endif - uint32 remainingBloodyFpTime = CTimer::GetTimeInMilliseconds() - m_bloodyFootprintCount; + uint32 remainingBloodyFpTime = CTimer::GetTimeInMilliseconds() - m_bloodyFootprintCountOrDeathTime; float timeDependentDist; if (remainingBloodyFpTime >= 2000) { if (remainingBloodyFpTime <= 7000) @@ -9253,7 +9305,7 @@ CPed::ProcessControl(void) if (!nearPed->DyingOrDead()) { CVector dist = nearPed->GetPosition() - GetPosition(); if (dist.MagnitudeSqr() < sq(timeDependentDist)) { - nearPed->m_bloodyFootprintCount = 200; + nearPed->m_bloodyFootprintCountOrDeathTime = 200; nearPed->bDoBloodyFootprints = true; if (nearPed->IsPlayer()) { if (!nearPed->bIsLooking && nearPed->m_nPedState != PED_ATTACK) { @@ -10313,7 +10365,7 @@ CPed::ProcessControl(void) CPed::Chat(); break; case PED_AIM_GUN: - if (m_pPointGunAt && m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->CanSeeEntity(this, DEGTORAD(120.0f))) { + if (m_pPointGunAt && m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->CanSeeEntity(this, CAN_SEE_ENTITY_ANGLE_THRESHOLD * 2)) { ((CPed*)m_pPointGunAt)->ReactToPointGun(this); } PointGunAt(); @@ -10588,7 +10640,7 @@ CPed::PedAnimDoorCloseCB(CAnimBlendAssociation *animAssoc, void *arg) if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) return; - if (ped->m_nPedState == PED_CARJACK || ped->m_nPedState == PED_ENTER_CAR) { + if (ped->EnteringCar()) { bool isLow = veh->bLowVehicle; if (!veh->bIsBus) @@ -10681,7 +10733,7 @@ CPed::PedAnimDoorOpenCB(CAnimBlendAssociation* animAssoc, void* arg) if (!ped->IsNotInWreckedVehicle()) return; - if (ped->m_nPedState != PED_CARJACK && ped->m_nPedState != PED_ENTER_CAR) { + if (!ped->EnteringCar()) { ped->QuitEnteringCar(); return; } @@ -10926,7 +10978,7 @@ CPed::PedAnimGetInCB(CAnimBlendAssociation *animAssoc, void *arg) if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) return; - if (ped->m_nPedState != PED_CARJACK && ped->m_nPedState != PED_ENTER_CAR) { + if (!ped->EnteringCar()) { ped->QuitEnteringCar(); return; } @@ -11018,7 +11070,10 @@ CPed::PedAnimGetInCB(CAnimBlendAssociation *animAssoc, void *arg) if ((ped->m_nPedType != PEDTYPE_EMERGENCY || veh->pDriver->m_nPedType != PEDTYPE_EMERGENCY) && (ped->m_nPedType != PEDTYPE_COP || veh->pDriver->m_nPedType != PEDTYPE_COP)) { veh->pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, veh); - veh->pDriver->Say(SOUND_PICKUP_WEAPON_BOUGHT); + veh->pDriver->Say(SOUND_PED_CAR_JACKED); +#ifdef VC_PED_PORTS + veh->pDriver->SetRadioStation(); +#endif } else { ped->m_objective = OBJECTIVE_ENTER_CAR_AS_PASSENGER; } @@ -11063,7 +11118,7 @@ CPed::PedAnimPullPedOutCB(CAnimBlendAssociation* animAssoc, void* arg) if (animAssoc) animAssoc->blendDelta = -1000.0f; - if (ped->m_nPedState == PED_CARJACK || ped->m_nPedState == PED_ENTER_CAR) { + if (ped->EnteringCar()) { if (!ped->IsNotInWreckedVehicle()) return; @@ -12809,10 +12864,10 @@ CPed::ProcessObjective(void) if (distWithTargetSc > wepRange || m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() || m_pedInObjective->m_nPedState == PED_ARRESTED - || (m_pedInObjective->m_nPedState == PED_ENTER_CAR || m_pedInObjective->m_nPedState == PED_CARJACK) && distWithTargetSc < 3.0f - || distWithTargetSc > m_distanceToCountSeekDone && !CanSeeEntity(m_pedInObjective, DEGTORAD(60.0f))) { + || m_pedInObjective->EnteringCar() && distWithTargetSc < 3.0f + || distWithTargetSc > m_distanceToCountSeekDone && !CanSeeEntity(m_pedInObjective)) { - if (m_pedInObjective->m_nPedState == PED_ENTER_CAR || m_pedInObjective->m_nPedState == PED_CARJACK) + if (m_pedInObjective->EnteringCar()) wepRangeAdjusted = 2.0f; if (bUsePedNodeSeek) { @@ -13110,7 +13165,7 @@ CPed::ProcessObjective(void) break; } if (m_objectiveTimer && m_objectiveTimer < CTimer::GetTimeInMilliseconds()) { - if (m_nPedState != PED_CARJACK && m_nPedState != PED_ENTER_CAR) { + if (!EnteringCar()) { bool foundSeat = false; if (m_carInObjective->pPassengers[0] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RF) { if (m_carInObjective->pPassengers[1] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_LR) { @@ -13452,7 +13507,7 @@ CPed::ProcessObjective(void) SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); } else { CVehicle* trainToEnter = nil; - float closestCarDist = 15.0f; + float closestCarDist = CHECK_NEARBY_THINGS_MAX_DIST; CVector pos = GetPosition(); int16 lastVehicle; CEntity* vehicles[8]; @@ -13500,7 +13555,7 @@ CPed::ProcessObjective(void) int16 lastVehicle; CEntity *vehicles[8]; - CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for(int i = 0; i < lastVehicle; i++) { CVehicle *nearVeh = (CVehicle*)vehicles[i]; if (nearVeh->VehicleCreatedBy != MISSION_VEHICLE) { @@ -15497,7 +15552,7 @@ CPed::ScanForInterestingStuff(void) } } - if (m_nPedState == PEDTYPE_CIVFEMALE) { + if (m_nPedState == PED_WANDER_PATH) { #ifndef VC_PED_PORTS if (CTimer::GetTimeInMilliseconds() > m_standardTimer) { @@ -15509,7 +15564,7 @@ CPed::ScanForInterestingStuff(void) else { if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() >= 1.8f) { m_standardTimer = CTimer::GetTimeInMilliseconds() + 30000; - } else if (CanSeeEntity(m_nearPeds[i], DEGTORAD(60.0f))) { + } else if (CanSeeEntity(m_nearPeds[i])) { int time = CGeneral::GetRandomNumber() % 4000 + 10000; SetChat(m_nearPeds[i], time); m_nearPeds[i]->SetChat(this, time); @@ -15525,10 +15580,10 @@ CPed::ScanForInterestingStuff(void) } else { if (CTimer::GetTimeInMilliseconds() > m_standardTimer) { for (int i = 0; i < m_numNearPeds; i ++) { - if (m_nearPeds[i]->m_nPedState == PED_WANDER_PATH) { + if (m_nearPeds[i] && m_nearPeds[i]->m_nPedState == PED_WANDER_PATH) { if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 1.8f - && CanSeeEntity(m_nearPeds[i], DEGTORAD(60.0f) - && m_nearPeds[i]->CanSeeEntity(this, DEGTORAD(60.0f))) + && CanSeeEntity(m_nearPeds[i]) + && m_nearPeds[i]->CanSeeEntity(this) && WillChat(m_nearPeds[i])) { int time = CGeneral::GetRandomNumber() % 4000 + 10000; @@ -15550,7 +15605,7 @@ CPed::ScanForInterestingStuff(void) CVector pos = GetPosition(); int16 lastVehicle; CEntity* vehicles[8]; - CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle* veh = (CVehicle*)vehicles[i]; @@ -15570,7 +15625,7 @@ CPed::ScanForInterestingStuff(void) CVector pos = GetPosition(); int16 lastVehicle; CEntity* vehicles[8]; - CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle* veh = (CVehicle*)vehicles[i]; diff --git a/src/peds/Ped.h b/src/peds/Ped.h index 4f159210..a19dc9f0 100644 --- a/src/peds/Ped.h +++ b/src/peds/Ped.h @@ -13,11 +13,14 @@ #include "DMAudio.h" #include "EventList.h" +#define FEET_OFFSET 1.04f +#define CHECK_NEARBY_THINGS_MAX_DIST 15.0f + struct CPathNode; class CAccident; class CObject; -struct CPedAudioData +struct PedAudioData { int m_nFixedDelayTime; int m_nOverrideFixedDelayTime; @@ -81,7 +84,7 @@ struct FightMove }; static_assert(sizeof(FightMove) == 0x18, "FightMove: error"); -// TO-DO: This is eFightState on mobile. +// TODO: This is eFightState on mobile. enum PedFightMoves { FIGHTMOVE_NULL, @@ -359,7 +362,7 @@ public: uint8 bStartWanderPathOnFoot : 1; // exits the car if he's in it, reset after path found uint8 bOnBoat : 1; // not just driver, may be just standing uint8 bBusJacked : 1; - uint8 bGonnaKillTheCarJacker : 1; // only set when car is jacked from right door + uint8 bGonnaKillTheCarJacker : 1; // only set when car is jacked from right door and when arrested by police uint8 bFadeOut : 1; uint8 bKnockedUpIntoAir : 1; // has ped been knocked up into the air by a car collision @@ -496,7 +499,7 @@ public: uint32 m_objectiveTimer; uint32 m_duckTimer; uint32 m_duckAndCoverTimer; - int32 m_bloodyFootprintCount; + uint32 m_bloodyFootprintCountOrDeathTime; // Death time when bDoBloodyFootprints is false. Weird decision uint8 m_panicCounter; bool m_deadBleeding; int8 m_bodyPartBleeding; // PedNode, but -1 if there isn't @@ -791,10 +794,13 @@ public: CWeapon &GetWeapon(uint8 weaponType) { return m_weapons[weaponType]; } CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } + PedState GetPedState(void) { return m_nPedState; } void SetPedState(PedState state) { m_nPedState = state; } bool DyingOrDead(void) { return m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } bool InVehicle(void) { return bInVehicle && m_pMyVehicle; } // True when ped is sitting/standing in vehicle, not in enter/exit state. + bool EnteringCar(void) { return m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK; } + void ReplaceWeaponWhenExitingVehicle(void); void RemoveWeaponWhenEnteringVehicle(void); bool IsNotInWreckedVehicle(); @@ -827,7 +833,6 @@ public: static bool &bPedCheat2; static bool &bPedCheat3; static CVector2D ms_vec2DFleePosition; - static CPedAudioData (&CommentWaitTime)[38]; #ifdef TOGGLEABLE_BETA_FEATURES static bool bPopHeadsOnHeadshot; diff --git a/src/peds/PedPlacement.cpp b/src/peds/PedPlacement.cpp index 42cabbc2..b22e1d58 100644 --- a/src/peds/PedPlacement.cpp +++ b/src/peds/PedPlacement.cpp @@ -32,7 +32,7 @@ CPedPlacement::FindZCoorForPed(CVector* pos) zForPed = max(foundColZ, foundColZ2); if (zForPed > -99.0f) - pos->z = 1.04f + zForPed; + pos->z = FEET_OFFSET + zForPed; } CEntity* diff --git a/src/peds/PedPlacement.h b/src/peds/PedPlacement.h index cbdf8013..b1b5be93 100644 --- a/src/peds/PedPlacement.h +++ b/src/peds/PedPlacement.h @@ -1,7 +1,6 @@ #pragma once -class CVector; -class CEntity; +#include "Ped.h" class CPedPlacement { public: diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp index 4aeae797..c6580d32 100644 --- a/src/peds/PlayerPed.cpp +++ b/src/peds/PlayerPed.cpp @@ -735,7 +735,7 @@ CPlayerPed::KeepAreaAroundPlayerClear(void) if (nearPed->m_objective == OBJECTIVE_NONE) { nearPed->SetFindPathAndFlee(this, 5000, true); } else { - if (nearPed->m_nPedState == PED_ENTER_CAR || nearPed->m_nPedState == PED_CARJACK) + if (nearPed->EnteringCar()) nearPed->QuitEnteringCar(); nearPed->ClearObjective(); @@ -750,7 +750,7 @@ CPlayerPed::KeepAreaAroundPlayerClear(void) CVector pos = GetPosition(); int16 lastVehicle; CEntity *vehicles[8]; - CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle *veh = (CVehicle*)vehicles[i]; @@ -1181,7 +1181,7 @@ CPlayerPed::ProcessControl(void) } if (m_nPedState == PED_DIE) { ClearWeaponTarget(); - if (CTimer::GetTimeInMilliseconds() > (uint32)m_bloodyFootprintCount + 4000) + if (CTimer::GetTimeInMilliseconds() > m_bloodyFootprintCountOrDeathTime + 4000) SetDead(); return; } diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp index bcbe7794..496ece34 100644 --- a/src/peds/Population.cpp +++ b/src/peds/Population.cpp @@ -18,11 +18,13 @@ #include "PedPlacement.h" #include "DummyObject.h" #include "Script.h" +#include "Shadows.h" -#define CREATION_DIST_MULT_TO_DIST 40.0f -#define CREATION_RANGE 10.0f // Being added over the CREATION_DIST_MULT_TO_DIST. -#define OFFSCREEN_CREATION_MULT 0.5f -// Also there are some hardcoded values in GeneratePedsAtStartOfGame. +#define MIN_CREATION_DIST 40.0f // not for start of the game (look at the GeneratePedsAtStartOfGame) +#define CREATION_RANGE 10.0f // added over the MIN_CREATION_DIST. +#define OFFSCREEN_CREATION_MULT 0.5f +#define PED_REMOVE_DIST (MIN_CREATION_DIST + CREATION_RANGE + 1.0f) +#define PED_REMOVE_DIST_SPECIAL (MIN_CREATION_DIST + CREATION_RANGE + 15.0f) // for peds with bCullExtraFarAway flag // TO-DO: These are hard-coded, reverse them. // More clearly they're transition areas between zones. @@ -57,9 +59,6 @@ CVector &CPopulation::RegenerationPoint_a = *(CVector*)0x8E2AA4; CVector &CPopulation::RegenerationPoint_b = *(CVector*)0x8E2A98; CVector &CPopulation::RegenerationForward = *(CVector*)0x8F1AD4; -WRAPPER void CPopulation::ManagePopulation(void) { EAXJMP(0x4F3B90); } -WRAPPER bool CPopulation::TestSafeForRealObject(CDummyObject*) { EAXJMP(0x4F4700); } - void CPopulation::Initialise() { @@ -416,10 +415,10 @@ CPopulation::Update() + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; if (!CCutsceneMgr::IsRunning()) { float pcdm = PedCreationDistMultiplier(); - AddToPopulation(pcdm * (CREATION_DIST_MULT_TO_DIST * TheCamera.GenerationDistMultiplier), - pcdm * ((CREATION_DIST_MULT_TO_DIST + CREATION_RANGE) * TheCamera.GenerationDistMultiplier), - pcdm * (CREATION_DIST_MULT_TO_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT - CREATION_RANGE, - pcdm * (CREATION_DIST_MULT_TO_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT); + AddToPopulation(pcdm * (MIN_CREATION_DIST * TheCamera.GenerationDistMultiplier), + pcdm * ((MIN_CREATION_DIST + CREATION_RANGE) * TheCamera.GenerationDistMultiplier), + pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT - CREATION_RANGE, + pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT); } } } @@ -437,8 +436,8 @@ CPopulation::GeneratePedsAtStartOfGame() + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; // Min dist is 10.0f only for start of the game (naturally) - AddToPopulation(10.0f, PedCreationDistMultiplier() * (CREATION_DIST_MULT_TO_DIST + CREATION_RANGE), - 10.0f, PedCreationDistMultiplier() * (CREATION_DIST_MULT_TO_DIST + CREATION_RANGE)); + AddToPopulation(10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE), + 10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE)); } } @@ -570,13 +569,13 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree || CCarCtrl::NumFiretrucksOnDuty + CCarCtrl::NumAmbulancesOnDuty + CCarCtrl::NumParkedCars + CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse)) { addCop = true; - minDist = PedCreationDistMultiplier() * CREATION_DIST_MULT_TO_DIST; - maxDist = PedCreationDistMultiplier() * (CREATION_DIST_MULT_TO_DIST + CREATION_RANGE); + minDist = PedCreationDistMultiplier() * MIN_CREATION_DIST; + maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE); } } // Yeah, float - float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier * CIniFile::PedNumberMultiplier; - maxPossiblePedsForArea = min(maxPossiblePedsForArea, MaxNumberOfPedsInUse); + float maxPossiblePedsForArea = 10.0f * (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier * CIniFile::PedNumberMultiplier; + // maxPossiblePedsForArea = min(maxPossiblePedsForArea, MaxNumberOfPedsInUse); if (ms_nTotalPeds < maxPossiblePedsForArea || addCop) { int decisionThreshold = CGeneral::GetRandomNumberInRange(0, 1000); @@ -689,7 +688,7 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree bool farEnoughToAdd = true; CMatrix mat(TheCamera.GetCameraMatrix()); if (TheCamera.IsSphereVisible(generatedCoors, 2.0f, &mat)) { - if (PedCreationDistMultiplier() * CREATION_DIST_MULT_TO_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D()) + if (PedCreationDistMultiplier() * MIN_CREATION_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D()) farEnoughToAdd = false; } if (!farEnoughToAdd) @@ -927,7 +926,6 @@ CPopulation::ConvertAllObjectsToDummyObjects() for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CObject *obj = CPools::GetObjectPool()->GetSlot(poolIndex); - if (obj) { if (obj->CanBeDeleted()) ConvertToDummyObject(obj); @@ -970,8 +968,6 @@ void CPopulation::ConvertToDummyObject(CObject *obj) { CDummyObject *dummy = new CDummyObject(obj); - if (!dummy) - return; dummy->GetMatrix() = obj->m_objectMatrix; dummy->GetMatrix().UpdateRW(); @@ -1005,6 +1001,163 @@ CPopulation::TestRoomForDummyObject(CObject *obj) return collidingObjs == 0; } +bool +CPopulation::TestSafeForRealObject(CDummyObject *dummy) +{ + CPtrNode *ptrNode; + CColModel *dummyCol = dummy->GetColModel(); + float colRadius = dummy->GetBoundRadius(); + CVector colCentre = dummy->GetBoundCentre(); + + int minX = CWorld::GetSectorIndexX(dummy->GetPosition().x - colRadius); + if (minX < 0) minX = 0; + int minY = CWorld::GetSectorIndexY(dummy->GetPosition().y - colRadius); + if (minY < 0) minY = 0; + int maxX = CWorld::GetSectorIndexX(dummy->GetPosition().x + colRadius); +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(dummy->GetPosition().y + colRadius); +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + static CColPoint aTempColPoints; + + for (int curY = minY; curY <= maxY; curY++) { + for (int curX = minX; curX <= maxX; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode; ptrNode = ptrNode->next) { + CVehicle *veh = (CVehicle*)ptrNode->item; + if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { + if (veh->GetIsTouching(colCentre, colRadius)) { + veh->m_scanCode = CWorld::GetCurrentScanCode(); + if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), &aTempColPoints, nil, nil) > 0) + return false; + } + } + } + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first; ptrNode; ptrNode = ptrNode->next) { + CVehicle *veh = (CVehicle*)ptrNode->item; + if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { + if (veh->GetIsTouching(colCentre, colRadius)) { + veh->m_scanCode = CWorld::GetCurrentScanCode(); + if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), &aTempColPoints, nil, nil) > 0) + return false; + } + } + } + } + } + return true; +} + +void +CPopulation::ManagePopulation(void) +{ + int frameMod32 = CTimer::GetFrameCounter() & 31; + CVector playerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + + // Why this code is here?! Delete temporary objects when they got too far, and convert others to "dummy" objects. (like lamp posts) + int objectPoolSize = CPools::GetObjectPool()->GetSize(); + for (int i = objectPoolSize * frameMod32 / 32; i < objectPoolSize * (frameMod32 + 1) / 32; i++) { + CObject *obj = CPools::GetObjectPool()->GetSlot(i); + if (obj && obj->CanBeDeleted()) { + if ((obj->GetPosition() - playerPos).Magnitude() <= 80.0f || + (obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() <= 80.0f) { + if (obj->ObjectCreatedBy == TEMP_OBJECT && CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) { + CWorld::Remove(obj); + delete obj; + } + } else { + if (obj->ObjectCreatedBy == TEMP_OBJECT) { + CWorld::Remove(obj); + delete obj; + } else if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) { + ConvertToDummyObject(obj); + } + } + } + } + + // Convert them back to real objects. Dummy objects don't have collisions, so they need to be converted. + int dummyPoolSize = CPools::GetDummyPool()->GetSize(); + for (int i = dummyPoolSize * frameMod32 / 32; i < dummyPoolSize * (frameMod32 + 1) / 32; i++) { + CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); + if (dummy) { + if ((dummy->GetPosition() - playerPos).Magnitude() < 80.0f) + ConvertToRealObject((CDummyObject*)dummy); + } + } + + int pedPoolSize = CPools::GetPedPool()->GetSize(); + for (int poolIndex = pedPoolSize-1; poolIndex >= 0; poolIndex--) { + CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); + + if (ped && !ped->IsPlayer() && ped->CanBeDeleted() && !ped->bInVehicle) { + if (ped->m_nPedState == PED_DEAD && CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime > 60000) + ped->bFadeOut = true; + + if (ped->bFadeOut && CVisibilityPlugins::GetClumpAlpha(ped->GetClump()) == 0) { + RemovePed(ped); + continue; + } + + float dist = (ped->GetPosition() - playerPos).Magnitude2D(); + bool pedIsFarAway = false; + if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist + || (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist) + || (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist + && !ped->GetIsOnScreen() + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT + && !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft + && !TheCamera.Cams[TheCamera.ActiveCam].LookingRight + && !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind)) + pedIsFarAway = true; + + if (!pedIsFarAway) + continue; + + if (ped->m_nPedState == PED_DEAD && !ped->bFadeOut) { + CVector pedPos = ped->GetPosition(); + + float randAngle = (uint8) CGeneral::GetRandomNumber() * (3.14f / 128.0f); // Not PI, 3.14 + switch (CGeneral::GetRandomNumber() % 3) { + case 0: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline1Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + case 1: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline2Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + case 2: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline3Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + default: + break; + } + } + if (ped->GetIsOnScreen()) + ped->bFadeOut = true; + else + RemovePed(ped); + } + } +} + STARTPATCHES InjectHook(0x4F3770, &CPopulation::Initialise, PATCH_JUMP); InjectHook(0x4F5780, &CPopulation::ChooseGangOccupation, PATCH_JUMP); diff --git a/src/render/Hud.cpp b/src/render/Hud.cpp index be7329e2..b87f7c71 100644 --- a/src/render/Hud.cpp +++ b/src/render/Hud.cpp @@ -130,12 +130,9 @@ void CHud::Draw() Draw Crosshairs */ if (TheCamera.Cams->Using3rdPersonMouseCam() && (!CPad::GetPad(0)->GetLookBehindForPed() || TheCamera.m_bPlayerIsInGarage) || Mode == CCam::MODE_1STPERSON_RUNABOUT) { - if (FindPlayerPed()) { - int32 State = FindPlayerPed()->m_nPedState; - if (State != PED_ENTER_CAR && State != PED_CARJACK) { - if ((WeaponType >= WEAPONTYPE_COLT45 && WeaponType <= WEAPONTYPE_M16) || WeaponType == WEAPONTYPE_FLAMETHROWER) - Mode_RunAround = 1; - } + if (FindPlayerPed() && !FindPlayerPed()->EnteringCar()) { + if ((WeaponType >= WEAPONTYPE_COLT45 && WeaponType <= WEAPONTYPE_M16) || WeaponType == WEAPONTYPE_FLAMETHROWER) + Mode_RunAround = 1; } } diff --git a/src/render/Shadows.h b/src/render/Shadows.h index c12327b1..8af8d02d 100644 --- a/src/render/Shadows.h +++ b/src/render/Shadows.h @@ -178,3 +178,6 @@ public: extern RwTexture *&gpBloodPoolTex; extern RwTexture *&gpShadowExplosionTex; extern RwTexture *&gpShadowHeadLightsTex; +extern RwTexture *&gpOutline1Tex; +extern RwTexture *&gpOutline2Tex; +extern RwTexture *&gpOutline3Tex; diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index 64f756ad..a05a1236 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -3002,9 +3002,9 @@ CAutomobile::RcbanditCheckHitWheels(void) if(xmin < 0) xmin = 0; xmax = CWorld::GetSectorIndexX(GetPosition().x + 2.0f); if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; - ymin = CWorld::GetSectorIndexX(GetPosition().y - 2.0f); + ymin = CWorld::GetSectorIndexY(GetPosition().y - 2.0f); if(ymin < 0) ymin = 0; - ymax = CWorld::GetSectorIndexX(GetPosition().y + 2.0f); + ymax = CWorld::GetSectorIndexY(GetPosition().y + 2.0f); if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1; CWorld::AdvanceCurrentScanCode(); diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp index f3a669d6..1c73ed05 100644 --- a/src/vehicles/Train.cpp +++ b/src/vehicles/Train.cpp @@ -261,9 +261,9 @@ CTrain::ProcessControl(void) if(xmin < 0) xmin = 0; xmax = CWorld::GetSectorIndexX(front.x + 3.0f); if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; - ymin = CWorld::GetSectorIndexX(front.y - 3.0f); + ymin = CWorld::GetSectorIndexY(front.y - 3.0f); if(ymin < 0) ymin = 0; - ymax = CWorld::GetSectorIndexX(front.y + 3.0f); + ymax = CWorld::GetSectorIndexY(front.y + 3.0f); if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1; CWorld::AdvanceCurrentScanCode();