summaryrefslogtreecommitdiff
path: root/src/peds/CivilianPed.cpp
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
committerclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
commit9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 (patch)
treea84756b82513739a2672db3a1f0ec579db6d18ff /src/peds/CivilianPed.cpp
downloadre3-9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686.tar.gz
re3-9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686.zip
Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation)HEADmiami
Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami.
Diffstat (limited to 'src/peds/CivilianPed.cpp')
-rw-r--r--src/peds/CivilianPed.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/src/peds/CivilianPed.cpp b/src/peds/CivilianPed.cpp
new file mode 100644
index 0000000..f351136
--- /dev/null
+++ b/src/peds/CivilianPed.cpp
@@ -0,0 +1,610 @@
+#include "common.h"
+
+#include "CivilianPed.h"
+#include "Phones.h"
+#include "General.h"
+#include "PlayerPed.h"
+#include "Wanted.h"
+#include "DMAudio.h"
+#include "World.h"
+#include "Vehicle.h"
+#include "SurfaceTable.h"
+#include "Weather.h"
+#include "PedAttractor.h"
+#include "Object.h"
+#include "CarCtrl.h"
+
+#ifndef _WIN32
+#include <float.h>
+#endif
+
+CCivilianPed::CCivilianPed(ePedType pedtype, uint32 mi) : CPed(pedtype)
+{
+ SetModelIndex(mi);
+ for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) {
+ m_nearPeds[i] = nil;
+ }
+ m_bLookForVacantCars = false;
+ if (pedtype == PEDTYPE_CRIMINAL)
+ m_bLookForVacantCars = true;
+
+ m_nLookForVacantCarsCounter = 0;
+ m_bJustStoleACar = false;
+ m_bStealCarEvenIfThereIsSomeoneInIt = false;
+ for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) {
+#ifdef FIX_BUGS
+ uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LAST_VEHICLE);
+#else
+ uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LANDSTAL + VEHICLEMODELSIZE);
+#endif
+ if (CModelInfo::IsCarModel(randomCarModel) || CModelInfo::IsBikeModel(randomCarModel))
+ m_nStealWishList[i] = randomCarModel;
+ else
+ m_nStealWishList[i] = MI_CHEETAH;
+ }
+ m_nAttractorCycleState = 0;
+ m_bAttractorUnk = (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 1.25f);
+}
+
+void
+CCivilianPed::CivilianAI(void)
+{
+ if (CTimer::GetTimeInMilliseconds() <= m_fleeTimer || m_objective != OBJECTIVE_NONE && !bRespondsToThreats
+ || !IsPedInControl()) {
+
+ if (m_objective == OBJECTIVE_GUARD_SPOT)
+ return;
+
+ if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) {
+ if (m_pedInObjective && m_pedInObjective->IsPlayer())
+ return;
+ }
+ if (CTimer::GetTimeInMilliseconds() <= m_lookTimer)
+ return;
+
+ ScanForDelayedResponseThreats();
+ if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer)
+ return;
+ CheckThreatValidity();
+ uint32 closestThreatFlag = m_threatFlags;
+ m_threatFlags = 0;
+ m_threatCheckTimer = 0;
+ if (closestThreatFlag == PED_FLAG_EXPLOSION) {
+ float angleToFace = CGeneral::GetRadianAngleBetweenPoints(
+ m_eventOrThreat.x, m_eventOrThreat.y,
+ GetPosition().x, GetPosition().y);
+ SetLookFlag(angleToFace, true);
+ SetLookTimer(500);
+
+ } else if (closestThreatFlag == PED_FLAG_GUN) {
+ SetLookFlag(m_threatEntity, true);
+ SetLookTimer(500);
+ }
+ return;
+ }
+ ScanForDelayedResponseThreats();
+ if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer)
+ return;
+ CheckThreatValidity();
+ uint32 closestThreatFlag = m_threatFlags;
+ m_threatFlags = 0;
+ m_threatCheckTimer = 0;
+ if (closestThreatFlag == PED_FLAG_GUN) {
+ if (!m_threatEntity || !m_threatEntity->IsPed())
+ return;
+
+ CPed *threatPed = (CPed*)m_threatEntity;
+ float threatDistSqr = (m_threatEntity->GetPosition() - GetPosition()).MagnitudeSqr2D();
+ if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared) {
+ SetDuck(10000, true);
+ SetLookFlag(m_threatEntity, false);
+ SetLookTimer(500);
+ return;
+ }
+ if (m_pedStats->m_fear <= m_pedStats->m_lawfulness) {
+ if (m_pedStats->m_temper <= m_pedStats->m_fear) {
+ if (!threatPed->IsPlayer() || !RunToReportCrime(CRIME_POSSESSION_GUN)) {
+ if (threatDistSqr < sq(30.0f)) {
+ bMakeFleeScream = true;
+ SetFindPathAndFlee(m_threatEntity, 10000);
+ } else {
+ SetFindPathAndFlee(m_threatEntity->GetPosition(), 5000, true);
+ }
+ }
+ } else if (m_objective != OBJECTIVE_NONE || GetWeapon()->IsTypeMelee()) {
+ SetFindPathAndFlee(m_threatEntity, 5000);
+ if (threatDistSqr < sq(20.0f)) {
+ SetMoveState(PEDMOVE_RUN);
+ bMakeFleeScream = true;
+ } else {
+ SetMoveState(PEDMOVE_WALK);
+ }
+ } else if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) {
+ SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
+
+ } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) {
+ SetFindPathAndFlee(m_threatEntity, 5000);
+ if (threatDistSqr < sq(30.0f)) {
+ SetMoveState(PEDMOVE_RUN);
+ } else {
+ SetMoveState(PEDMOVE_WALK);
+ }
+ } else {
+ SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
+ }
+ } else {
+ if (threatDistSqr < sq(30.0f)) {
+ bMakeFleeScream = true;
+ SetFindPathAndFlee(m_threatEntity, 10000);
+ SetMoveState(PEDMOVE_SPRINT);
+ } else {
+ bMakeFleeScream = false;
+ SetFindPathAndFlee(m_threatEntity, 5000);
+ SetMoveState(PEDMOVE_RUN);
+ }
+ }
+ SetLookFlag(m_threatEntity, false);
+ SetLookTimer(500);
+ } else if (closestThreatFlag == PED_FLAG_DEADPEDS) {
+ float eventDistSqr = (m_pEventEntity->GetPosition() - GetPosition()).MagnitudeSqr2D();
+ if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(5.0f)) {
+ SetDuck(10000, true);
+
+ } else if (((CPed*)m_pEventEntity)->bIsDrowning || IsGangMember() && m_nPedType == ((CPed*)m_pEventEntity)->m_nPedType) {
+ if (eventDistSqr < sq(5.0f)) {
+ SetFindPathAndFlee(m_pEventEntity, 2000);
+ SetMoveState(PEDMOVE_RUN);
+ }
+ } else if (IsGangMember() || eventDistSqr > sq(5.0f)) {
+ bool investigateDeadPed = true;
+ CEntity *killerOfDeadPed = ((CPed*)m_pEventEntity)->m_threatEntity;
+ if (killerOfDeadPed && killerOfDeadPed->IsPed()) {
+ CVector killerPos = killerOfDeadPed->GetPosition();
+ CVector deadPedPos = m_pEventEntity->GetPosition();
+ if (CVector2D(killerPos - deadPedPos).MagnitudeSqr() < sq(10.0f))
+ investigateDeadPed = false;
+ }
+
+ if (investigateDeadPed)
+ SetInvestigateEvent(EVENT_DEAD_PED, CVector2D(m_pEventEntity->GetPosition()), 1.0f, 20000, 0.0f);
+
+ } else {
+ SetFindPathAndFlee(m_pEventEntity, 5000);
+ SetMoveState(PEDMOVE_RUN);
+ }
+ } else if (closestThreatFlag == PED_FLAG_EXPLOSION) {
+ CVector2D eventDistVec = m_eventOrThreat - GetPosition();
+ float eventDistSqr = eventDistVec.MagnitudeSqr();
+ if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(20.0f)) {
+ SetDuck(10000, true);
+
+ } else if (eventDistSqr < sq(20.0f)) {
+ bMakeFleeScream = true;
+ SetFlee(m_eventOrThreat, 2000);
+ float angleToFace = CGeneral::GetRadianAngleBetweenPoints(
+ m_eventOrThreat.x, m_eventOrThreat.y,
+ GetPosition().x, GetPosition().y);
+ SetLookFlag(angleToFace, true);
+ SetLookTimer(500);
+ } else if (eventDistSqr < sq(40.0f)) {
+ if (bGonnaInvestigateEvent) {
+ if (CharCreatedBy != MISSION_CHAR && !IsGangMember())
+ SetInvestigateEvent(EVENT_EXPLOSION, m_eventOrThreat, 6.0f, 30000, 0.0f);
+
+ } else {
+ float eventHeading = CGeneral::GetRadianAngleBetweenPoints(eventDistVec.x, eventDistVec.y, 0.0f, 0.0f);
+ eventHeading = CGeneral::LimitRadianAngle(eventHeading);
+ if (eventHeading < 0.0f)
+ eventHeading = eventHeading + TWOPI;
+
+ SetWanderPath(eventHeading / 8.0f);
+ }
+ }
+ } else {
+ if (m_threatEntity && m_threatEntity->IsPed()) {
+ CPed *threatPed = (CPed*)m_threatEntity;
+ if (m_pedStats->m_fear <= 100 - threatPed->m_pedStats->m_temper && threatPed->m_nPedType != PEDTYPE_COP) {
+ if (threatPed->GetWeapon()->IsTypeMelee() || !GetWeapon()->IsTypeMelee()) {
+ if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) {
+ SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
+
+ } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) {
+ if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) {
+ SetFindPathAndFlee(m_threatEntity, 10000);
+ }
+ } else {
+ SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
+ }
+ }
+ } else {
+ SetFindPathAndFlee(m_threatEntity, 10000, true);
+ }
+ }
+ }
+}
+
+void
+CCivilianPed::ProcessControl(void)
+{
+ if (CharCreatedBy == UNK_CHAR)
+ return;
+
+ CPed::ProcessControl();
+
+ if (bWasPostponed)
+ return;
+
+ if (DyingOrDead())
+ return;
+
+ GetWeapon()->Update(m_audioEntityId, nil);
+ switch (m_nPedState) {
+ case PED_WANDER_RANGE:
+ case PED_WANDER_PATH:
+ if (IsVisible())
+ ScanForInterestingStuff();
+ break;
+ case PED_SEEK_ENTITY:
+ if (!m_pSeekTarget) {
+ RestorePreviousState();
+ break;
+ }
+ m_vecSeekPos = m_pSeekTarget->GetPosition();
+
+ // fall through
+ case PED_SEEK_POS:
+ if (Seek()) {
+ if ((m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA ||
+ IsUseAttractorObjective(m_objective)) && m_pNextPathNode) {
+ m_pNextPathNode = nil;
+
+ } else if (bRunningToPhone) {
+ if (gPhoneInfo.m_aPhones[m_phoneId].m_nState != PHONE_STATE_FREE) {
+ RestorePreviousState();
+ m_phoneId = -1;
+ } else {
+ gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_REPORTING_CRIME;
+ SetPedState(PED_FACE_PHONE);
+ }
+ } else if (m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
+ if (m_pedInObjective && m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) {
+ if (m_moved.Magnitude() == 0.0f) {
+ if (m_pedInObjective->m_nMoveState == PEDMOVE_STILL)
+ m_fRotationDest = m_pedInObjective->m_fRotationCur;
+ }
+ } else if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT
+ && m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) {
+ SetMoveState(m_pedInObjective->m_nMoveState);
+ } else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA ||
+ IsUseAttractorObjective(m_objective)) {
+ SetIdle();
+ } else {
+ RestorePreviousState();
+ }
+ }
+ }
+ break;
+ case PED_FACE_PHONE:
+ if (FacePhone())
+ SetPedState(PED_MAKE_CALL);
+ break;
+ case PED_MAKE_CALL:
+ if (MakePhonecall())
+ SetWanderPath(CGeneral::GetRandomNumber() & 7);
+ break;
+ case PED_MUG:
+ Mug();
+ break;
+ case PED_SOLICIT:
+ Solicit();
+ break;
+ case PED_UNKNOWN:
+ {
+ int pedsInSameState = 0;
+ Idle();
+ for (int i = 0; i < m_numNearPeds; ++i) {
+ CPed *nearPed = m_nearPeds[i];
+ if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_UNKNOWN) {
+ ++pedsInSameState;
+ }
+ }
+ if (pedsInSameState < 5) {
+ for (int j = 0; j < m_numNearPeds; ++j) {
+ CPed *nearPed = m_nearPeds[j];
+ if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_WANDER_PATH) {
+ nearPed->SetPedState(PED_UNKNOWN);
+ }
+ }
+ }
+ break;
+ }
+ case PED_DRIVING:
+ if (m_nPedType != PEDTYPE_PROSTITUTE)
+ break;
+
+ if (CWorld::Players[CWorld::PlayerInFocus].m_pHooker != this)
+ break;
+
+ if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime) {
+ if (m_nPedState == PED_DRIVING
+ && m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->IsPlayer() && m_pMyVehicle->pDriver->m_nPedState == PED_DRIVING) {
+ CColPoint foundCol;
+ CEntity* foundEnt;
+
+ CWorld::ProcessVerticalLine(m_pMyVehicle->GetPosition(), -100.0f,
+ foundCol, foundEnt, true, false, false, false, false, false, nil);
+
+ if (m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr() < sq(0.01f)
+ && foundCol.surfaceB != SURFACE_DEFAULT && foundCol.surfaceB != SURFACE_TARMAC && foundCol.surfaceB != SURFACE_PAVEMENT) {
+
+ if (m_pMyVehicle->CarHasRoof()) {
+ m_pMyVehicle->ApplyTurnForce(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(-0.8f, -1.2f) * m_fMass,
+ GetPosition().x - m_pMyVehicle->GetPosition().x, GetPosition().y - m_pMyVehicle->GetPosition().y, 0.0f);
+
+ DMAudio.PlayOneShot(m_pMyVehicle->m_audioEntityId, SOUND_CAR_JERK, 0.0f);
+ m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_BEFORESEX);
+ Say(SOUND_PED_PLAYER_BEFORESEX);
+
+ int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency;
+ if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= 10 && playerSexFrequency > 250) {
+ CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + playerSexFrequency;
+ if (playerSexFrequency >= 350) {
+ CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 30);
+ } else {
+ CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 10);
+ }
+
+ m_pMyVehicle->pDriver->m_fHealth = Min(CWorld::Players[0].m_nMaxHealth + 25.0f, 1.0f + m_pMyVehicle->pDriver->m_fHealth);
+ if (CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency == 250)
+ CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000;
+ } else {
+ bWanderPathAfterExitingCar = true;
+ CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
+ ClearLeader();
+ SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
+ m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
+ }
+ } else {
+ bWanderPathAfterExitingCar = true;
+ CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
+ m_pMyVehicle->pDriver->m_fHealth = CWorld::Players[0].m_nMaxHealth + 25.0f;
+ ClearLeader();
+ SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
+ m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
+ }
+ } else {
+ CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000;
+ int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency;
+ if (playerSexFrequency >= 1000 || playerSexFrequency <= 250)
+ CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 1200;
+ else
+ CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250;
+ }
+ } else {
+ bWanderPathAfterExitingCar = true;
+ CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
+ ClearLeader();
+ SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
+ }
+ }
+
+ if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime) {
+ int playerMoney = CWorld::Players[CWorld::PlayerInFocus].m_nMoney;
+ if (playerMoney <= 1) {
+ CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250;
+ } else {
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, playerMoney - 1);
+ }
+ CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000;
+ }
+ break;
+ default:
+ break;
+ }
+ if (IsPedInControl())
+ CivilianAI();
+
+ if (CharCreatedBy == RANDOM_CHAR) {
+ EnterVacantNearbyCars();
+ UseNearbyAttractors();
+ }
+
+ if (CTimer::GetTimeInMilliseconds() > m_timerUnused) {
+ m_stateUnused = 0;
+ m_timerUnused = 0;
+ }
+
+ if (m_moved.Magnitude() > 0.0f)
+ Avoid();
+}
+
+bool
+CPed::RunToReportCrime(eCrimeType crimeToReport)
+{
+ // They changed true into false to make this function unusable. So running to phone actually starts but first frame after that cancels it.
+ if (m_nPedState == PED_SEEK_POS)
+ return false;
+
+ CVector pos = GetPosition();
+ int phoneId = gPhoneInfo.FindNearestFreePhone(&pos);
+
+ if (phoneId == -1)
+ return false;
+
+ CPhone* phone = &gPhoneInfo.m_aPhones[phoneId];
+ if (phone->m_nState != PHONE_STATE_FREE)
+ return false;
+
+ bRunningToPhone = true;
+ SetSeek(phone->m_vecPos, 0.3f);
+ SetMoveState(PEDMOVE_RUN);
+ m_phoneId = phoneId;
+ m_crimeToReportOnPhone = crimeToReport;
+ return true;
+}
+
+const int32 gFrequencyOfAttractorAttempt = 11;
+const float gDistanceToSeekAttractors = 50.0f;
+const float gMaxDistanceToAttract = 10.0f;
+
+/* Probably this was inlined */
+void CCivilianPed::FindNearbyAttractorsSectorList(CPtrList& list, float& minDistance, C2dEffect*& pClosestAttractor, CEntity*& pAttractorEntity)
+{
+ for (CPtrNode* pNode = list.first; pNode != nil; pNode = pNode->next) {
+ CEntity* pEntity = (CEntity*)pNode->item;
+ if (pEntity->IsObject() && (!pEntity->GetIsStatic() || ((CObject*)pEntity)->bHasBeenDamaged))
+ continue;
+ CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(pEntity->GetModelIndex());
+ for (int i = 0; i < pModelInfo->GetNum2dEffects(); i++) {
+ C2dEffect* pEffect = pModelInfo->Get2dEffect(i);
+ if (pEffect->type != EFFECT_PED_ATTRACTOR)
+ continue;
+ if (!IsAttractedTo(pEffect->pedattr.type))
+ continue;
+ CVector pos;
+ CPedAttractorManager::ComputeEffectPos(pEffect, pEntity->GetMatrix(), pos);
+ if ((pos - GetPosition()).MagnitudeSqr() < minDistance) {
+ CPedAttractorManager* pManager = GetPedAttractorManager();
+ if (pManager->HasEmptySlot(pEffect) && pManager->IsApproachable(pEffect, pEntity->GetMatrix(), 0, this)) {
+ pClosestAttractor = pEffect;
+ pAttractorEntity = pEntity;
+ minDistance = (pos - GetPosition()).MagnitudeSqr();
+ }
+ }
+ }
+ }
+}
+
+void CCivilianPed::UseNearbyAttractors()
+{
+ if (CWeather::Rain < 0.2f && !m_bAttractorUnk)
+ return;
+ if (HasAttractor())
+ return;
+ if (m_nAttractorCycleState != gFrequencyOfAttractorAttempt) {
+ m_nAttractorCycleState++;
+ return;
+ }
+ m_nAttractorCycleState = 0;
+ if (!IsPedInControl())
+ return;
+ if (m_nPedState == PED_FLEE_ENTITY)
+ return;
+
+ float left = GetPosition().x - gDistanceToSeekAttractors;
+ float right = GetPosition().x + gDistanceToSeekAttractors;
+ float top = GetPosition().y - gDistanceToSeekAttractors;
+ float bottom = GetPosition().y + gDistanceToSeekAttractors;
+ int xstart = Max(0, CWorld::GetSectorIndexX(left));
+ int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right));
+ int ystart = Max(0, CWorld::GetSectorIndexY(top));
+ int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom));
+ assert(xstart <= xend);
+ assert(ystart <= yend);
+
+ float minDistance = SQR(gMaxDistanceToAttract);
+ C2dEffect* pClosestAttractor = nil;
+ CEntity* pAttractorEntity = nil;
+
+ for (int y = ystart; y <= yend; y++) {
+ for (int x = xstart; x <= xend; x++) {
+ CSector* s = CWorld::GetSector(x, y);
+ FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_BUILDINGS], minDistance, pClosestAttractor, pAttractorEntity);
+ FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_OBJECTS], minDistance, pClosestAttractor, pAttractorEntity);
+ }
+ }
+ if (pClosestAttractor)
+ GetPedAttractorManager()->RegisterPedWithAttractor(this, pClosestAttractor, pAttractorEntity->GetMatrix());
+}
+
+bool CCivilianPed::IsAttractedTo(int8 type)
+{
+ switch (type) {
+ case ATTRACTOR_ATM: return true;
+ case ATTRACTOR_SEAT: return true;
+ case ATTRACTOR_STOP: return true;
+ case ATTRACTOR_PIZZA: return true;
+ case ATTRACTOR_SHELTER: return CWeather::Rain >= 0.2f;
+ case ATTRACTOR_ICECREAM: return false;
+ }
+ return false;
+}
+
+void
+CCivilianPed::EnterVacantNearbyCars(void)
+{
+ if (!m_bLookForVacantCars)
+ return;
+
+ if (m_bJustStoleACar && bInVehicle && m_carInObjective == m_pMyVehicle) {
+ m_bJustStoleACar = false;
+ m_pMyVehicle->SetStatus(STATUS_PHYSICS);
+ m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
+ m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 10;
+ m_pMyVehicle->bEngineOn = true;
+
+ } else if (!bHasAlreadyStoleACar) {
+ if (m_nLookForVacantCarsCounter == 8) {
+ m_nLookForVacantCarsCounter = 0;
+ if (IsPedInControl() && m_objective == OBJECTIVE_NONE) {
+
+ CVehicle *foundCar = nil;
+ float closestDist = FLT_MAX;
+ int minX = CWorld::GetSectorIndexX(GetPosition().x - 10.0f);
+ if (minX < 0) minX = 0;
+ int minY = CWorld::GetSectorIndexY(GetPosition().y - 10.0f);
+ if (minY < 0) minY = 0;
+ int maxX = CWorld::GetSectorIndexX(GetPosition().x + 10.0f);
+ if (maxX > NUMSECTORS_X - 1) maxX = NUMSECTORS_X - 1;
+ int maxY = CWorld::GetSectorIndexY(GetPosition().y + 10.0f);
+ if (maxY > NUMSECTORS_Y - 1) maxY = NUMSECTORS_Y - 1;
+
+ for (int curY = minY; curY <= maxY; curY++) {
+ for (int curX = minX; curX <= maxX; curX++) {
+ CSector* sector = CWorld::GetSector(curX, curY);
+ for (CPtrNode* node = sector->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next) {
+ CVehicle* veh = (CVehicle*)node->item;
+ if (veh && veh->IsCar()) {
+
+ // Looks like PARKED_VEHICLE condition isn't there in Mobile.
+ if (veh->VehicleCreatedBy == RANDOM_VEHICLE || veh->VehicleCreatedBy == PARKED_VEHICLE) {
+ if (IsOnStealWishList(veh->GetModelIndex()) && !veh->IsLawEnforcementVehicle()
+ && (m_bStealCarEvenIfThereIsSomeoneInIt || !veh->pDriver && !veh->m_nNumPassengers)
+ && !veh->m_nNumGettingIn && !veh->m_nGettingInFlags && !veh->m_nGettingOutFlags
+ && !veh->m_pCarFire && veh->m_fHealth > 800.0f
+ && !veh->IsUpsideDown() && !veh->IsOnItsSide() && veh->CanPedEnterCar()) {
+ float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr();
+ if (dist < sq(10.0f) && dist < closestDist && veh->IsClearToDriveAway()) {
+ foundCar = veh;
+ closestDist = dist;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (foundCar) {
+ m_bJustStoleACar = true;
+ bHasAlreadyStoleACar = true;
+ CCarCtrl::JoinCarWithRoadSystem(foundCar);
+ SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, foundCar);
+ SetObjectiveTimer(10000);
+ }
+ }
+ } else {
+ ++m_nLookForVacantCarsCounter;
+ }
+ }
+}
+
+bool
+CCivilianPed::IsOnStealWishList(int32 model)
+{
+ for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) {
+ if (model == m_nStealWishList[i]) {
+ return true;
+ }
+ }
+ return false;
+} \ No newline at end of file