2019-06-30 12:59:55 +02:00
|
|
|
#include "common.h"
|
2020-04-17 15:31:11 +02:00
|
|
|
|
2019-06-30 12:59:55 +02:00
|
|
|
#include "CivilianPed.h"
|
2019-07-15 14:11:40 +02:00
|
|
|
#include "Phones.h"
|
2019-11-25 03:25:10 +01:00
|
|
|
#include "General.h"
|
|
|
|
#include "PlayerPed.h"
|
2020-03-28 15:47:52 +01:00
|
|
|
#include "Wanted.h"
|
|
|
|
#include "DMAudio.h"
|
2019-11-25 03:25:10 +01:00
|
|
|
#include "World.h"
|
|
|
|
#include "Vehicle.h"
|
|
|
|
#include "SurfaceTable.h"
|
2020-05-14 12:46:11 +02:00
|
|
|
#include "Weather.h"
|
|
|
|
#include "PedAttractor.h"
|
2020-05-16 10:22:12 +02:00
|
|
|
#include "Object.h"
|
2020-09-23 22:29:56 +02:00
|
|
|
#include "CarCtrl.h"
|
2019-06-30 23:50:40 +02:00
|
|
|
|
2020-09-23 22:29:56 +02:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <float.h>
|
|
|
|
#endif
|
|
|
|
|
2020-09-27 01:36:49 +02:00
|
|
|
// --MIAMI: file done except TODOs
|
|
|
|
|
2020-02-16 03:15:58 +01:00
|
|
|
CCivilianPed::CCivilianPed(ePedType pedtype, uint32 mi) : CPed(pedtype)
|
2019-06-30 12:59:55 +02:00
|
|
|
{
|
2019-07-14 11:49:03 +02:00
|
|
|
SetModelIndex(mi);
|
2019-11-25 03:25:10 +01:00
|
|
|
for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) {
|
2019-06-30 23:50:40 +02:00
|
|
|
m_nearPeds[i] = nil;
|
|
|
|
}
|
2020-09-23 22:29:56 +02:00
|
|
|
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++) {
|
|
|
|
uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LAST_VEHICLE + 1);
|
|
|
|
if (CModelInfo::IsCarModel(randomCarModel) || CModelInfo::IsBikeModel(randomCarModel))
|
|
|
|
m_nStealWishList[i] = randomCarModel;
|
|
|
|
else
|
|
|
|
m_nStealWishList[i] = MI_CHEETAH;
|
|
|
|
}
|
2020-05-14 12:46:11 +02:00
|
|
|
m_nAttractorCycleState = 0;
|
|
|
|
m_bAttractorUnk = (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 1.25f);
|
2019-06-30 12:59:55 +02:00
|
|
|
}
|
|
|
|
|
2019-11-25 03:25:10 +01:00
|
|
|
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) {
|
2020-04-10 17:06:49 +02:00
|
|
|
if (m_pedInObjective && m_pedInObjective->IsPlayer())
|
|
|
|
return;
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
if (CTimer::GetTimeInMilliseconds() <= m_lookTimer)
|
|
|
|
return;
|
|
|
|
|
2020-08-29 18:22:25 +02:00
|
|
|
ScanForDelayedResponseThreats();
|
|
|
|
if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer)
|
|
|
|
return;
|
|
|
|
CheckThreatValidity();
|
|
|
|
uint32 closestThreatFlag = m_threatFlags;
|
|
|
|
m_threatFlags = 0;
|
|
|
|
m_threatCheckTimer = 0;
|
2019-11-25 03:25:10 +01:00
|
|
|
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;
|
|
|
|
}
|
2020-08-29 18:22:25 +02:00
|
|
|
ScanForDelayedResponseThreats();
|
|
|
|
if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer)
|
|
|
|
return;
|
|
|
|
CheckThreatValidity();
|
|
|
|
uint32 closestThreatFlag = m_threatFlags;
|
|
|
|
m_threatFlags = 0;
|
|
|
|
m_threatCheckTimer = 0;
|
2019-11-25 03:25:10 +01:00
|
|
|
if (closestThreatFlag == PED_FLAG_GUN) {
|
|
|
|
if (!m_threatEntity || !m_threatEntity->IsPed())
|
|
|
|
return;
|
|
|
|
|
|
|
|
CPed *threatPed = (CPed*)m_threatEntity;
|
|
|
|
float threatDistSqr = (m_threatEntity->GetPosition() - GetPosition()).MagnitudeSqr2D();
|
2020-08-29 18:22:25 +02:00
|
|
|
if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared) {
|
|
|
|
SetDuck(10000, true);
|
|
|
|
SetLookFlag(m_threatEntity, false);
|
|
|
|
SetLookTimer(500);
|
|
|
|
return;
|
|
|
|
}
|
2019-11-25 03:25:10 +01:00
|
|
|
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)) {
|
2020-08-29 18:22:25 +02:00
|
|
|
if (threatDistSqr < sq(30.0f)) {
|
|
|
|
bMakeFleeScream = true;
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 10000);
|
2019-11-25 03:25:10 +01:00
|
|
|
} else {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity->GetPosition(), 5000, true);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (m_objective != OBJECTIVE_NONE || GetWeapon()->IsTypeMelee()) {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 5000);
|
2019-11-25 03:25:10 +01:00
|
|
|
if (threatDistSqr < sq(20.0f)) {
|
|
|
|
SetMoveState(PEDMOVE_RUN);
|
2020-08-29 18:22:25 +02:00
|
|
|
bMakeFleeScream = true;
|
2019-11-25 03:25:10 +01:00
|
|
|
} else {
|
|
|
|
SetMoveState(PEDMOVE_WALK);
|
|
|
|
}
|
2020-08-29 18:22:25 +02:00
|
|
|
} else if (threatPed->IsPlayer() && IsGangMember() && b158_80) {
|
|
|
|
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
|
|
|
|
|
2020-04-10 17:06:49 +02:00
|
|
|
} else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 5000);
|
2020-08-29 18:22:25 +02:00
|
|
|
if (threatDistSqr < sq(30.0f)) {
|
2019-11-25 03:25:10 +01:00
|
|
|
SetMoveState(PEDMOVE_RUN);
|
|
|
|
} else {
|
|
|
|
SetMoveState(PEDMOVE_WALK);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
|
|
|
|
}
|
|
|
|
} else {
|
2020-08-29 18:22:25 +02:00
|
|
|
if (threatDistSqr < sq(30.0f)) {
|
|
|
|
bMakeFleeScream = true;
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 10000);
|
2019-11-25 03:25:10 +01:00
|
|
|
SetMoveState(PEDMOVE_SPRINT);
|
|
|
|
} else {
|
2020-08-29 18:22:25 +02:00
|
|
|
bMakeFleeScream = false;
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 5000);
|
2019-11-25 03:25:10 +01:00
|
|
|
SetMoveState(PEDMOVE_RUN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SetLookFlag(m_threatEntity, false);
|
|
|
|
SetLookTimer(500);
|
|
|
|
} else if (closestThreatFlag == PED_FLAG_DEADPEDS) {
|
|
|
|
float eventDistSqr = (m_pEventEntity->GetPosition() - GetPosition()).MagnitudeSqr2D();
|
2020-08-29 18:22:25 +02:00
|
|
|
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) {
|
2019-11-25 03:25:10 +01:00
|
|
|
if (eventDistSqr < sq(5.0f)) {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_pEventEntity, 2000);
|
2019-11-25 03:25:10 +01:00
|
|
|
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 {
|
2020-06-22 03:02:44 +02:00
|
|
|
SetFindPathAndFlee(m_pEventEntity, 5000);
|
|
|
|
SetMoveState(PEDMOVE_RUN);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
} else if (closestThreatFlag == PED_FLAG_EXPLOSION) {
|
|
|
|
CVector2D eventDistVec = m_eventOrThreat - GetPosition();
|
|
|
|
float eventDistSqr = eventDistVec.MagnitudeSqr();
|
2020-08-29 18:22:25 +02:00
|
|
|
if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(20.0f)) {
|
|
|
|
SetDuck(10000, true);
|
|
|
|
|
|
|
|
} else if (eventDistSqr < sq(20.0f)) {
|
|
|
|
bMakeFleeScream = true;
|
2019-11-25 03:25:10 +01:00
|
|
|
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)) {
|
2020-03-02 01:03:39 +01:00
|
|
|
if (bGonnaInvestigateEvent) {
|
2019-11-25 03:25:10 +01:00
|
|
|
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) {
|
2020-08-29 18:22:25 +02:00
|
|
|
if (threatPed->GetWeapon()->IsTypeMelee() || !GetWeapon()->IsTypeMelee()) {
|
|
|
|
if (threatPed->IsPlayer() && IsGangMember() && b158_80) {
|
|
|
|
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
|
|
|
|
|
|
|
|
} else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) {
|
2019-11-25 03:25:10 +01:00
|
|
|
if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 10000);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2020-01-23 21:21:50 +01:00
|
|
|
SetFindPathAndFlee(m_threatEntity, 10000, true);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CCivilianPed::ProcessControl(void)
|
|
|
|
{
|
2020-07-04 14:31:28 +02:00
|
|
|
if (CharCreatedBy == TODO_CHAR)
|
|
|
|
return;
|
|
|
|
|
2019-11-25 03:25:10 +01:00
|
|
|
CPed::ProcessControl();
|
|
|
|
|
|
|
|
if (bWasPostponed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (DyingOrDead())
|
|
|
|
return;
|
|
|
|
|
2020-05-17 19:36:48 +02:00
|
|
|
GetWeapon()->Update(m_audioEntityId, nil);
|
2019-11-25 03:25:10 +01:00
|
|
|
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()) {
|
2020-07-04 14:31:28 +02:00
|
|
|
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) {
|
2019-11-25 03:25:10 +01:00
|
|
|
m_pNextPathNode = nil;
|
2020-06-22 03:02:44 +02:00
|
|
|
|
2019-11-25 03:25:10 +01:00
|
|
|
} 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;
|
2020-07-04 14:31:28 +02:00
|
|
|
SetPedState(PED_FACE_PHONE);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
} else if (m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
|
2020-07-04 14:31:28 +02:00
|
|
|
if (m_pedInObjective && m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) {
|
2019-11-25 03:25:10 +01:00
|
|
|
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);
|
2020-07-04 14:31:28 +02:00
|
|
|
} else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA ||
|
|
|
|
IsUseAttractorObjective(m_objective)) {
|
2019-11-25 03:25:10 +01:00
|
|
|
SetIdle();
|
|
|
|
} else {
|
|
|
|
RestorePreviousState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PED_FACE_PHONE:
|
|
|
|
if (FacePhone())
|
2020-07-04 14:31:28 +02:00
|
|
|
SetPedState(PED_MAKE_CALL);
|
2019-11-25 03:25:10 +01:00
|
|
|
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->m_nPedState = 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);
|
2020-07-04 14:31:28 +02:00
|
|
|
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_BEFORESEX);
|
|
|
|
Say(SOUND_PED_PLAYER_BEFORESEX);
|
2019-11-25 03:25:10 +01:00
|
|
|
|
|
|
|
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) {
|
2020-04-19 18:34:08 +02:00
|
|
|
CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 30);
|
2019-11-25 03:25:10 +01:00
|
|
|
} else {
|
2020-04-19 18:34:08 +02:00
|
|
|
CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 10);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 22:06:33 +02:00
|
|
|
m_pMyVehicle->pDriver->m_fHealth = Min(CWorld::Players[0].m_nMaxHealth + 25.0f, 1.0f + m_pMyVehicle->pDriver->m_fHealth);
|
2019-11-25 03:25:10 +01:00
|
|
|
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;
|
2020-07-04 14:31:28 +02:00
|
|
|
ClearLeader();
|
2020-06-22 03:02:44 +02:00
|
|
|
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
2020-07-04 14:31:28 +02:00
|
|
|
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bWanderPathAfterExitingCar = true;
|
|
|
|
CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
|
2020-05-16 22:06:33 +02:00
|
|
|
m_pMyVehicle->pDriver->m_fHealth = CWorld::Players[0].m_nMaxHealth + 25.0f;
|
2020-07-04 14:31:28 +02:00
|
|
|
ClearLeader();
|
2020-06-22 03:02:44 +02:00
|
|
|
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
2020-07-04 14:31:28 +02:00
|
|
|
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
} 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;
|
2020-07-04 14:31:28 +02:00
|
|
|
ClearLeader();
|
2020-06-22 03:02:44 +02:00
|
|
|
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-04-19 18:34:08 +02:00
|
|
|
CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, playerMoney - 1);
|
2019-11-25 03:25:10 +01:00
|
|
|
}
|
|
|
|
CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (IsPedInControl())
|
|
|
|
CivilianAI();
|
|
|
|
|
2020-05-14 12:46:11 +02:00
|
|
|
if (CharCreatedBy == RANDOM_CHAR) {
|
2020-09-23 22:29:56 +02:00
|
|
|
EnterVacantNearbyCars();
|
2020-05-14 12:46:11 +02:00
|
|
|
UseNearbyAttractors();
|
|
|
|
}
|
|
|
|
|
2019-11-25 03:25:10 +01:00
|
|
|
if (CTimer::GetTimeInMilliseconds() > m_timerUnused) {
|
|
|
|
m_stateUnused = 0;
|
|
|
|
m_timerUnused = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_moved.Magnitude() > 0.0f)
|
|
|
|
Avoid();
|
|
|
|
}
|
2020-05-14 12:46:11 +02:00
|
|
|
|
|
|
|
const int32 gFrequencyOfAttractorAttempt = 11;
|
|
|
|
const float gDistanceToSeekAttractors = 50.0f;
|
|
|
|
const float gMaxDistanceToAttract = 10.0f;
|
|
|
|
|
2020-05-16 09:53:20 +02:00
|
|
|
/* 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;
|
2020-05-16 10:22:12 +02:00
|
|
|
if (pEntity->IsObject() && (!pEntity->IsStatic() || ((CObject*)pEntity)->bHasBeenDamaged))
|
|
|
|
continue;
|
2020-05-16 09:53:20 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 12:46:11 +02:00
|
|
|
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);
|
2020-05-16 09:53:20 +02:00
|
|
|
FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_BUILDINGS], minDistance, pClosestAttractor, pAttractorEntity);
|
|
|
|
FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_OBJECTS], minDistance, pClosestAttractor, pAttractorEntity);
|
2020-05-14 12:46:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2020-05-16 00:49:30 +02:00
|
|
|
case ATTRACTOR_ICECREAM: return false;
|
2020-05-14 12:46:11 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-23 22:29:56 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|