summaryrefslogblamecommitdiffstats
path: root/src/peds/CivilianPed.cpp
blob: f351136623040f51c0cbde3c546109bccd300873 (plain) (tree)
1
2
3
4
5
6
7
8
                   
 
                        
                   

                      

                    


                         

                         
                   
                    
 



                  
                                                                       
 
                          
                                                          

                                    







                                                                




                                                                                                                      




                                                                                                      

                                                                                 

 


                              






                                                                                                                  

                                                                             



                                                                   






                                                                                            












                                                                                  






                                                                                    





                                                                                                       





                                                                         


                                                                                                        

                                                                        
                                                                                          
                                                
                                                                                                              


                                                                                                 
                                                                         

                                                                  
                                                               


                                                                   
                                                                                                         

                                                                                          
                                                                                                              
                                                                         
                                                                







                                                                                          

                                                        
                                                                          

                                                             
                                                        
                                                                         






                                                                                                      



                                                                                                                                         
                                                      
                                                                         











                                                                                                 



                                                                                                                                 

                                                                 



                                                                         




                                                                                                     






                                                                                  
                                                     















                                                                                                                                       
                                                                                                           
                                                                                                                  


                                                                                                                             
                                                                                                                                                 
                                                                                                  





                                                                                                          
                                                                                







                                  
                                      

                       







                               
                                                  















                                                                    

                                                                                                                                                                     
                                                              
 



                                                                                                           

                                                                                                                       
                                                                            

                                                                                                                                        
                                                                                                                    






                                                                                                                         

                                                                                                                                                                                   





                                                                       


                                        
                                                           
























                                                                                                                           
                                                                                  




























                                                                                                                                                                          

                                                                                                               




                                                                                                                                                                                                    
                                                                                                                                                                           
                                                                                
                                                                                                                                                                           

                                                                         
                                                                                                                                                                                                 




                                                                                                                                                                                              
                                                                              
                                                                                                                
                                                                                                                      



                                                                                                               
                                                                                                                                   
                                                                      
                                                                                                        
                                                                                                              











                                                                                                                                                              
                                                      
                                                                                        







                                                                                                                                 
                                                                                                                  









                                                                                                                                          
                                           
                                        


                                      







                                                              
 
























                                                                                                                                                  



                                              




                                                                                                                                                
                                                                                                             
                                 




















                                                                                                                                          

































                                                                          

                                                                                                                                           













                                                                                                                           
                                              


                     
 













































































                                                                                                                                                                   
#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;
}