4286 lines
121 KiB
C++
4286 lines
121 KiB
C++
// FILE : TaskSearch.h
|
|
// PURPOSE : AI scour an area based on the last known position of the target and try to re-aquire.
|
|
// They can search using vehicles, on foot and transition between the two.
|
|
// AUTHOR : Phil H
|
|
|
|
// File header
|
|
#include "Task/Combat/Subtasks/TaskVehicleCombat.h"
|
|
|
|
// Rage headers
|
|
#include "grcore/debugdraw.h"
|
|
|
|
// Game headers
|
|
#include "audio/vehicleaudioentity.h"
|
|
#include "audio/caraudioentity.h"
|
|
#include "Network/Cloud/Tunables.h"
|
|
#include "Objects/Door.h"
|
|
#include "PedGroup/PedGroup.h"
|
|
#include "Peds/Ped.h"
|
|
#include "peds/PedIKSettings.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "Peds/PedWeapons/PedTargeting.h"
|
|
#include "Peds/VehicleCombatAvoidanceArea.h"
|
|
#include "Task/Vehicle/TaskInVehicle.h"
|
|
#include "Task/Vehicle/TaskVehicleWeapon.h"
|
|
#include "Task/Combat/TaskCombat.h"
|
|
#include "Task/Combat/Subtasks/TaskBoatCombat.h"
|
|
#include "Task/Combat/Subtasks/TaskHeliCombat.h"
|
|
#include "Task/Combat/Subtasks/TaskShootAtTarget.h"
|
|
#include "Task/Combat/Subtasks/TaskShootOutTire.h"
|
|
#include "Task/Combat/Subtasks/TaskSubmarineCombat.h"
|
|
#include "Task/Combat/Subtasks/TaskVehicleChase.h"
|
|
#include "Task/Service/Swat/TaskSwat.h"
|
|
#include "Task/Vehicle/TaskCar.h"
|
|
#include "Task/Vehicle/TaskExitVehicle.h"
|
|
#include "Task/Vehicle/TaskVehicleBase.h"
|
|
#include "vehicles/Boat.h"
|
|
#include "Vehicles/Heli.h"
|
|
#include "Vehicles/Metadata/AIHandlingInfo.h"
|
|
#include "Vehicles/Metadata/VehicleMetadataManager.h"
|
|
#include "Vehicles/Metadata/VehicleSeatInfo.h"
|
|
#include "vehicles/Train.h"
|
|
#include "vehicleAi/task/TaskVehicleApproach.h"
|
|
#include "vehicleAi/task/TaskVehicleAttack.h"
|
|
#include "vehicleAi/task/TaskVehicleFleeBoat.h"
|
|
#include "vehicleAi/task/TaskVehicleCruise.h"
|
|
#include "vehicleAi/task/TaskVehicleGoto.h"
|
|
#include "vehicleAi/task/TaskVehicleBlock.h"
|
|
#include "vehicleAi/task/TaskVehiclePark.h"
|
|
#include "vehicleAi/task/TaskVehicleTempAction.h"
|
|
#include "Weapons/Info/WeaponInfoManager.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_VEHICLE_OPTIMISATIONS()
|
|
|
|
CTaskVehiclePersuit::Tunables CTaskVehiclePersuit::sm_Tunables;
|
|
|
|
IMPLEMENT_COMBAT_TASK_TUNABLES(CTaskVehiclePersuit, 0x50f150c4);
|
|
|
|
bank_float CTaskVehiclePersuit::EXIT_FAILED_TIME_IN_STATE = 2.0f;
|
|
|
|
CTaskVehiclePersuit::CTaskVehiclePersuit( const CAITarget& target, fwFlags8 uConfigFlags )
|
|
: m_vTargetPositionWhenWaitBegan(V_ZERO)
|
|
, m_vPositionWhenCollidedWithLockedDoor(V_ZERO)
|
|
, m_target(target)
|
|
, m_uTimeCollidedWithLockedDoor(0)
|
|
, m_uConfigFlags(uConfigFlags)
|
|
, m_uRunningFlags(0)
|
|
, m_fTimeVehicleUndriveable(0.0f)
|
|
, m_fJumpOutTimer(0.0f)
|
|
, m_ExitToAimClipSetId(CLIP_SET_ID_INVALID)
|
|
#if !__FINAL
|
|
, m_uNumObstructionPoints(0)
|
|
#endif
|
|
{
|
|
taskFatalAssertf(target.GetEntity(), "CTaskVehiclePersuit requires a valid target entity!");
|
|
taskFatalAssertf(target.GetEntity()->GetIsPhysical(), "CTaskVehiclePersuit requires a valid PHYSICAL target entity!");
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PERSUIT);
|
|
}
|
|
|
|
CTaskVehiclePersuit::~CTaskVehiclePersuit()
|
|
{
|
|
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ProcessPreFSM()
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
// Exit the task immediately if the ped is not inside a vehicle
|
|
if( pPed->GetVehiclePedInside() == NULL && MustPedBeInVehicle() )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
// If the target becomes no longer valid - quit
|
|
if( !m_target.GetIsValid() || m_target.GetEntity() == NULL )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
//Process timers.
|
|
ProcessTimers();
|
|
|
|
//Process audio.
|
|
ProcessAudio();
|
|
|
|
ProcessIk();
|
|
|
|
ProcessBlockedByLockedDoor();
|
|
|
|
//Activate timeslicing.
|
|
ActivateTimeslicing();
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
FSM_Begin
|
|
// initial state
|
|
FSM_State(State_start)
|
|
FSM_OnUpdate
|
|
return Start_OnUpdate(pPed);
|
|
|
|
// flee from the target rather than chase/attack
|
|
FSM_State(State_fleeFromTarget)
|
|
FSM_OnEnter
|
|
FleeFromTarget_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return FleeFromTarget_OnUpdate(pPed);
|
|
|
|
FSM_State(State_WaitWhileCruising)
|
|
FSM_OnEnter
|
|
WaitWhileCruising_OnEnter();
|
|
FSM_OnUpdate
|
|
return WaitWhileCruising_OnUpdate();
|
|
|
|
FSM_State(State_attack)
|
|
FSM_OnEnter
|
|
Attack_OnEnter();
|
|
FSM_OnUpdate
|
|
return Attack_OnUpdate();
|
|
|
|
// Chase the target in a car
|
|
FSM_State(State_chaseInCar)
|
|
FSM_OnEnter
|
|
ChaseInCar_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return ChaseInCar_OnUpdate(pPed);
|
|
|
|
// Follow the car at a distance, allowing passengers to shoot
|
|
FSM_State(State_followInCar)
|
|
FSM_OnEnter
|
|
FollowInCar_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return FollowInCar_OnUpdate(pPed);
|
|
|
|
// Engage the target in combat in a heli
|
|
FSM_State(State_heliCombat)
|
|
FSM_OnEnter
|
|
HeliCombat_OnEnter();
|
|
FSM_OnUpdate
|
|
return HeliCombat_OnUpdate();
|
|
|
|
// Engage the target in combat in a sub
|
|
FSM_State(State_subCombat)
|
|
FSM_OnEnter
|
|
SubCombat_OnEnter();
|
|
FSM_OnUpdate
|
|
return SubCombat_OnUpdate();
|
|
|
|
// Engage the target in combat in a boat
|
|
FSM_State(State_boatCombat)
|
|
FSM_OnEnter
|
|
BoatCombat_OnEnter();
|
|
FSM_OnUpdate
|
|
return BoatCombat_OnUpdate();
|
|
|
|
// Ride as passenger - firing weapons if they are available
|
|
FSM_State(State_passengerInVehicle)
|
|
FSM_OnEnter
|
|
PassengerInVehicle_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return PassengerInVehicle_OnUpdate(pPed);
|
|
|
|
// Wait for buddies to enter the car
|
|
FSM_State(State_waitForBuddiesToEnter)
|
|
FSM_OnEnter
|
|
WaitForBuddiesToEnter_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return WaitForBuddiesToEnter_OnUpdate(pPed);
|
|
|
|
// Slow down until can use handbrake safely
|
|
FSM_State(State_slowdownSafely)
|
|
FSM_OnEnter
|
|
SlowdownSafely_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return SlowdownSafely_OnUpdate(pPed);
|
|
|
|
// Ride as passenger - firing weapons if they are available
|
|
FSM_State(State_emergencyStop)
|
|
FSM_OnEnter
|
|
EmergencyStop_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return EmergencyStop_OnUpdate(pPed);
|
|
FSM_OnExit
|
|
EmergencyStop_OnExit();
|
|
|
|
// Exit the vehicle
|
|
FSM_State(State_exitVehicle)
|
|
FSM_OnEnter
|
|
ExitVehicle_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return ExitVehicle_OnUpdate(pPed);
|
|
|
|
// Exit the vehicle - without waiting for it to slow
|
|
FSM_State(State_exitVehicleImmediately)
|
|
FSM_OnEnter
|
|
ExitVehicleImmediately_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return ExitVehicleImmediately_OnUpdate(pPed);
|
|
|
|
// Provide cover for others exiting the vehicle
|
|
FSM_State(State_provideCoverDuringExitVehicle)
|
|
FSM_OnEnter
|
|
ProvideCoverDuringExitVehicle_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return ProvideCoverDuringExitVehicle_OnUpdate(pPed);
|
|
FSM_OnExit
|
|
ProvideCoverDuringExitVehicle_OnExit(pPed);
|
|
|
|
//Approach the target
|
|
FSM_State(State_approachTarget)
|
|
FSM_OnEnter
|
|
ApproachTarget_OnEnter();
|
|
FSM_OnUpdate
|
|
return ApproachTarget_OnUpdate();
|
|
|
|
//Shoot at the target while waiting for vehicle exit
|
|
FSM_State(State_waitForVehicleExit)
|
|
FSM_OnEnter
|
|
WaitForVehicleExit_OnEnter();
|
|
FSM_OnUpdate
|
|
return WaitForVehicleExit_OnUpdate();
|
|
|
|
FSM_State(state_waitInCar)
|
|
FSM_OnEnter
|
|
WaitInCar_OnEnter();
|
|
FSM_OnUpdate
|
|
return WaitInCar_OnUpdate();
|
|
|
|
// Quit the task
|
|
FSM_State(State_exit)
|
|
FSM_OnUpdate
|
|
return FSM_Quit;
|
|
|
|
FSM_End
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ProcessPostFSM()
|
|
{
|
|
const CPed& ped = *GetPed();
|
|
if (ped.GetIsInVehicle())
|
|
{
|
|
if (m_ExitToAimClipSetId.IsNotNull())
|
|
{
|
|
m_clipSetRequestHelper.Request(m_ExitToAimClipSetId);
|
|
}
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProcessIk()
|
|
{
|
|
//Ensure the target ped is valid.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
if(!pTargetPed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure a second has elapsed.
|
|
if(floorf(GetTimeRunning()) == floorf(GetTimeRunning() - GetTimeStep()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Look at the target.
|
|
GetPed()->GetIkManager().LookAt(0, pTargetPed,
|
|
2000, BONETAG_HEAD, NULL, LF_WHILE_NOT_IN_FOV, 500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::Start_OnUpdate( CPed* pPed )
|
|
{
|
|
taskAssertf(pPed->GetVehiclePedInside(), "Task started when not in a vehicle!");
|
|
|
|
//Check if we are a law enforcement ped, and we are driving.
|
|
if(pPed->IsLawEnforcementPed() && pPed->GetIsDrivingVehicle())
|
|
{
|
|
//Check if the vehicle is law enforcement.
|
|
CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(pVehicle->IsLawEnforcementVehicle())
|
|
{
|
|
//Turn the siren on.
|
|
pVehicle->TurnSirenOn(true, ChooseDefaultState() == State_chaseInCar);
|
|
}
|
|
}
|
|
|
|
// Find out which clip set to request based on our equipped weapon or best weapon and then request it
|
|
m_ExitToAimClipSetId = CTaskVehicleFSM::GetExitToAimClipSetIdForPed(*pPed);
|
|
|
|
// Find out which clip set to request based on our equipped weapon or best weapon and then request it
|
|
if(m_ExitToAimClipSetId != CLIP_SET_ID_INVALID)
|
|
{
|
|
CVehicle* pMyVehicle = pPed->GetMyVehicle();
|
|
if (pMyVehicle)
|
|
{
|
|
int iSeatIndex = pMyVehicle->GetSeatManager()->GetPedsSeatIndex(pPed);
|
|
|
|
// Then check if we can exit the vehicle early. Right now we only want to do this when exiting the vehicle to aim
|
|
const CVehicleSeatAnimInfo* pSeatAnimInfo = pMyVehicle->GetSeatAnimationInfo(iSeatIndex);
|
|
if(pSeatAnimInfo && pSeatAnimInfo->GetCanExitEarlyInCombat())
|
|
{
|
|
m_uRunningFlags.SetFlag(RF_CanExitVehicleEarly);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if we should exit immediately.
|
|
if(m_uConfigFlags.IsFlagSet(CF_ExitImmediately))
|
|
{
|
|
SetState(State_exitVehicleImmediately);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// If the ped is currently driving a vehicle,
|
|
if( pPed->GetIsDrivingVehicle() )
|
|
{
|
|
//Check if we should flee.
|
|
if( pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_FleeWhilstInVehicle) )
|
|
{
|
|
SetState(State_fleeFromTarget);
|
|
}
|
|
else if( ShouldWaitWhileCruising() )
|
|
{
|
|
SetState(State_WaitWhileCruising);
|
|
}
|
|
else if( ShouldAttack() )
|
|
{
|
|
SetState(State_attack);
|
|
}
|
|
else if( pPed->GetVehiclePedInside()->GetIsRotaryAircraft() )
|
|
{
|
|
SetState(State_heliCombat);
|
|
}
|
|
else if( pPed->GetVehiclePedInside()->InheritsFromSubmarine() || pPed->GetVehiclePedInside()->InheritsFromSubmarineCar())
|
|
{
|
|
SetState(State_subCombat);
|
|
}
|
|
else if( pPed->GetVehiclePedInside()->InheritsFromBoat() )
|
|
{
|
|
SetState(State_boatCombat);
|
|
}
|
|
else if( pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_JustFollowInVehicle) )
|
|
{
|
|
SetState(State_followInCar);
|
|
}
|
|
else
|
|
{
|
|
SetState(ChooseDefaultState());
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
// The ped is a passenger, enter the passenger state
|
|
else
|
|
{
|
|
SetState(State_passengerInVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::FleeFromTarget_OnEnter( CPed* pPed )
|
|
{
|
|
taskFatalAssertf(m_target.GetEntity(), "Null target passed!" );
|
|
taskFatalAssertf(m_target.GetEntity()->GetIsPhysical(), "Target is non-physical!");
|
|
//const CPhysical* pPhysicalTarget = static_cast<const CPhysical*>(m_target.GetEntity());
|
|
|
|
sVehicleMissionParams params;
|
|
params.SetTargetEntity(const_cast<CEntity*>(m_target.GetEntity()));
|
|
params.m_iDrivingFlags = DMode_AvoidCars|DF_AvoidTargetCoors|DF_UseStringPullingAtJunctions|DF_SteerAroundPeds;
|
|
params.m_fCruiseSpeed = pPed->GetPedIntelligence()->GetCombatBehaviour().GetCombatFloat(kAttribFloatSpeedToFleeInVehicle);
|
|
|
|
CTaskVehicleMissionBase* pFleeTask = NULL;
|
|
if ( pPed->GetMyVehicle() && pPed->GetMyVehicle()->InheritsFromBoat() )
|
|
{
|
|
pFleeTask = rage_new CTaskVehicleFleeBoat(params);
|
|
}
|
|
else
|
|
{
|
|
pFleeTask = rage_new CTaskVehicleFlee(params);
|
|
}
|
|
|
|
|
|
SetNewTask(rage_new CTaskControlVehicle(pPed->GetMyVehicle(), pFleeTask, rage_new CTaskVehicleCombat(&m_target)));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::FleeFromTarget_OnUpdate( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
// If the subtask has terminated - restart
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) )
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
return FSM_Continue;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//the following functions are mostly copied from TaskPolicePatrol in order to get non-police vehicles to crusie ahead of target vehicles
|
|
void CTaskVehiclePersuit::WaitWhileCruising_OnEnter()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
sVehicleMissionParams params;
|
|
params.m_iDrivingFlags = DMode_AvoidCars_Reckless | DF_AdjustCruiseSpeedBasedOnRoadSpeed;
|
|
params.m_fCruiseSpeed = 10.0f;
|
|
CTask* pTask = CVehicleIntelligence::CreateCruiseTask(*pPed->GetMyVehicle(), params);
|
|
|
|
//Start the task.
|
|
SetNewTask(rage_new CTaskControlVehicle(pPed->GetMyVehicle(), pTask, rage_new CTaskVehicleCombat(&m_target)));
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::FacingSameDirectionAsTarget() const
|
|
{
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
if(!pTargetVehicle || !pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vec3V vOurForward = pVehicle->GetTransform().GetForward();
|
|
Vec3V vTargetForward = pTargetVehicle->GetTransform().GetForward();
|
|
|
|
//Check if the target has passed the officer.
|
|
ScalarV scDot = Dot(vOurForward, vTargetForward);
|
|
if(IsGreaterThanAll(scDot, ScalarV(V_ZERO)))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::HasTargetPassedUs() const
|
|
{
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
if(!pTargetVehicle || !pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vec3V vOurPosition = pVehicle->GetTransform().GetPosition();
|
|
Vec3V vTargetPosition = pTargetVehicle->GetTransform().GetPosition();
|
|
|
|
Vec3V vTargetToUs = Subtract(vOurPosition, vTargetPosition);
|
|
Vec3V vTargetForward = pTargetVehicle->GetTransform().GetForward();
|
|
|
|
//Check if the target has passed the officer.
|
|
ScalarV scDot = Dot(vTargetForward, vTargetToUs);
|
|
if(IsLessThanAll(scDot, ScalarV(V_ZERO)))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::WillTargetVehicleOvertakeUsWithinTime(float fTime) const
|
|
{
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
if(!pVehicle || pTargetVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float fVehicleSpeed = pVehicle->GetVelocity().XYMag();
|
|
float fTargetVehicleSpeed = pTargetVehicle->GetVelocity().XYMag();
|
|
|
|
//Ensure the target vehicle is moving faster than the officer.
|
|
float fSpeedDifference = fTargetVehicleSpeed - fVehicleSpeed;
|
|
if(fSpeedDifference <= 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the distance between the officer and the target.
|
|
float fDistance = Dist(pVehicle->GetTransform().GetPosition(), pTargetVehicle->GetTransform().GetPosition()).Getf();
|
|
|
|
//Calculate the time to overtake.
|
|
float fTimeToOvertake = fDistance / fSpeedDifference;
|
|
|
|
return (fTimeToOvertake < fTime);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::WaitWhileCruising_OnUpdate()
|
|
{
|
|
//Failure conditions:
|
|
// 1) The sub-task is finished.
|
|
// 2) The officer has waited too long.
|
|
// 3) The officer is not in a vehicle.
|
|
// 4) The target is not in a vehicle.
|
|
// 5) The target's vehicle is moving slowly.
|
|
// 6) The target has passed the officer.
|
|
|
|
//Success conditions:
|
|
// 1) The officer can see the target.
|
|
// 2) The target is within a certain distance, or will overtake the officer soon.
|
|
|
|
CPed* pPed = GetPed();
|
|
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
bool bTargetMovingSlowly = true;
|
|
if(pTargetVehicle)
|
|
{
|
|
float fSpeedSq = pTargetVehicle->GetVelocity().XYMag2();
|
|
float fMaxSpeedSq = square(10.0f);
|
|
if(fSpeedSq >= fMaxSpeedSq)
|
|
{
|
|
bTargetMovingSlowly = false;
|
|
}
|
|
}
|
|
|
|
//Check the failure conditions.
|
|
if( !GetSubTask() || GetTimeInState() > 60.0f || !pVehicle || !pTargetVehicle || bTargetMovingSlowly || HasTargetPassedUs())
|
|
{
|
|
//enter default state
|
|
SetState(ChooseDefaultState());
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//Check the success conditions.
|
|
//Ensure the distance is valid.
|
|
static dev_float s_fMaxDistance = pVehicle->GetVehicleType() != VEHICLE_TYPE_BIKE ? 45.0f : 20.0f;
|
|
ScalarV scDistSq = DistSquared(pVehicle->GetTransform().GetPosition(), pTargetVehicle->GetTransform().GetPosition());
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(s_fMaxDistance));
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq) || WillTargetVehicleOvertakeUsWithinTime(1.0f))
|
|
{
|
|
//enter chase task
|
|
SetState(State_chaseInCar);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::Attack_OnEnter()
|
|
{
|
|
taskFatalAssertf(m_target.GetEntity(), "Null target passed!" );
|
|
taskFatalAssertf(m_target.GetEntity()->GetIsPhysical(), "Target is non-physical!");
|
|
|
|
sVehicleMissionParams params;
|
|
params.SetTargetEntity(const_cast<CEntity*>(m_target.GetEntity()));
|
|
params.m_iDrivingFlags = DMode_AvoidCars|DF_UseStringPullingAtJunctions;
|
|
const CVehicle* pVehicle = GetPed()->GetMyVehicle();
|
|
params.m_fCruiseSpeed = pVehicle->m_Transmission.GetDriveMaxVelocity();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
|
|
//b*2327046 push out aircraft arrival distance as the default 4 is very close for combat air vehicles
|
|
if(pVehicle->GetIsAircraft() && pTargetVehicle && pTargetVehicle->GetIsAircraft())
|
|
{
|
|
params.m_fTargetArriveDist = 25.0f;
|
|
}
|
|
|
|
float fScriptOverrideDist = pVehicle->GetOverrideArriveDistForVehPersuitAttack();
|
|
if (!IsClose(fScriptOverrideDist,-1.0f))
|
|
{
|
|
params.m_fTargetArriveDist = fScriptOverrideDist;
|
|
}
|
|
|
|
CTaskVehicleAttack* pTask = rage_new CTaskVehicleAttack(params);
|
|
SetNewTask(rage_new CTaskControlVehicle(GetPed()->GetMyVehicle(), pTask, rage_new CTaskVehicleCombat(&m_target)));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::Attack_OnUpdate()
|
|
{
|
|
// check for an exit vehicle state
|
|
VehiclePersuitState newState = GetDesiredState(GetPed());
|
|
if( newState != State_invalid )
|
|
{
|
|
SetState(newState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Restart the state.
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ChaseInCar_OnEnter( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
//Assert the target ped is valid.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
taskAssertf(pTargetPed, "Target is invalid.");
|
|
|
|
//Create the task.
|
|
CTaskVehicleChase* pTask = rage_new CTaskVehicleChase(pTargetPed);
|
|
|
|
//Check if the target is not a player.
|
|
if(!pTargetPed->IsPlayer())
|
|
{
|
|
//Don't make aggressive moves as often.
|
|
static float s_fAggressiveMoveDelayMin = 15.0f;
|
|
static float s_fAggressiveMoveDelayMax = 20.0f;
|
|
pTask->SetAggressiveMoveDelay(s_fAggressiveMoveDelayMin, s_fAggressiveMoveDelayMax);
|
|
}
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ChaseInCar_OnUpdate( CPed* pPed )
|
|
{
|
|
//Check if the state should change.
|
|
VehiclePersuitState iState = GetDesiredState(pPed);
|
|
if(iState != State_invalid)
|
|
{
|
|
//Change the state.
|
|
SetState(iState);
|
|
}
|
|
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Move to the start state.
|
|
SetState(State_start);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::FollowInCar_OnEnter( CPed* pPed )
|
|
{
|
|
taskFatalAssertf(m_target.GetEntity(), "Null target passed!" );
|
|
taskFatalAssertf(m_target.GetEntity()->GetIsPhysical(), "Target is non-physical!");
|
|
//const CPhysical* pPhysicalTarget = static_cast<const CPhysical*>(m_target.GetEntity());
|
|
|
|
sVehicleMissionParams params;
|
|
params.SetTargetEntity(const_cast<CEntity*>(m_target.GetEntity()));
|
|
params.m_fCruiseSpeed = pPed->GetMyVehicle() ? pPed->GetMyVehicle()->m_Transmission.GetDriveMaxVelocity() : 40.0f;
|
|
params.m_iDrivingFlags = DMode_AvoidCars_Reckless|DF_UseStringPullingAtJunctions;
|
|
CTaskVehicleFollow* pTask = rage_new CTaskVehicleFollow(params, sm_Tunables.m_DistanceToFollowInCar);
|
|
SetNewTask( rage_new CTaskControlVehicle(pPed->GetMyVehicle(), pTask ,rage_new CTaskVehicleCombat(&m_target) ) );
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::FollowInCar_OnUpdate( CPed* pPed )
|
|
{
|
|
// If the subtask has terminated - restart
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) )
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
return FSM_Continue;
|
|
}
|
|
// check for an exit vehicle state
|
|
VehiclePersuitState newState = GetDesiredState(pPed);
|
|
if( newState != State_invalid )
|
|
{
|
|
SetState(newState);
|
|
return FSM_Continue;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::SubCombat_OnEnter()
|
|
{
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskSubmarineCombat(m_target);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::SubCombat_OnUpdate()
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Finish the task.
|
|
SetState(State_exit);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::HeliCombat_OnEnter()
|
|
{
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskHeliCombat(m_target);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::HeliCombat_OnUpdate()
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Finish the task.
|
|
SetState(State_exit);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskVehiclePersuit::VehiclePersuitState iDesiredAircraftState;
|
|
if(GetDesiredAircraftState(GetPed(), iDesiredAircraftState) && iDesiredAircraftState != State_invalid)
|
|
{
|
|
SetState(iDesiredAircraftState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::BoatCombat_OnEnter()
|
|
{
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskBoatCombat(m_target);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::BoatCombat_OnUpdate()
|
|
{
|
|
//Check if we should exit the boat.
|
|
if(ShouldExitBoat())
|
|
{
|
|
//Exit the vehicle.
|
|
SetState(State_exitVehicle);
|
|
}
|
|
//Check if the sub-task has finished.
|
|
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Finish the task.
|
|
SetState(State_exit);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::PassengerInVehicle_OnEnter( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
SetNewTask(rage_new CTaskVehicleCombat(&m_target) );
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::PassengerInVehicle_OnUpdate( CPed* pPed )
|
|
{
|
|
// If our target is a ped and is not in a vehicle, prevent shuffling to the driver seat if our driver is injured
|
|
if( m_target.GetEntity()->GetIsTypePed() && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_AllowAutoShuffleToDriversSeat) )
|
|
{
|
|
const CPed* pPedTarget = static_cast<const CPed*>(m_target.GetEntity());
|
|
if(!pPedTarget->GetIsInVehicle())
|
|
{
|
|
// We don't want AI getting out of the vehicle at really long distances away so make sure we are far enough away
|
|
ScalarV scDistSq = DistSquared(pPed->GetTransform().GetPosition(), pPedTarget->GetTransform().GetPosition());
|
|
ScalarV scMaxDistSq = ScalarVFromF32(MAX(sm_Tunables.m_IdealDistanceShotAt, sm_Tunables.m_IdealDistanceCouldLeaveCar) + sm_Tunables.m_PreventShufflingExtraRange);
|
|
scMaxDistSq = (scMaxDistSq * scMaxDistSq);
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisableSeatShuffleDueToInjuredDriver, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the subtask has terminated - restart
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) )
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// check for an exit vehicle state
|
|
VehiclePersuitState newState = GetDesiredState(pPed);
|
|
if( newState != State_invalid )
|
|
{
|
|
SetState(newState);
|
|
return FSM_Continue;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
void CTaskVehiclePersuit::SlowdownSafely_OnEnter( CPed* pPed )
|
|
{
|
|
//CTaskVehicleHandBrake/CTaskVehicleStop simply brakes fully above s_fMaxSpeedForHandbrake with no avoidance
|
|
//so instead use cruise task to slow us down until we can handbrake so we keep avoiding as long as possible
|
|
if( pPed->GetMyVehicle()->IsDriver(pPed) )
|
|
{
|
|
float fVehicleSpeedSqr = pPed->GetMyVehicle()->GetVelocity().Mag2();
|
|
if(fVehicleSpeedSqr > CTaskVehicleHandBrake::s_fMaxSpeedForHandbrake * CTaskVehicleHandBrake::s_fMaxSpeedForHandbrake)
|
|
{
|
|
CreateSlowDownGoToTask(pPed->GetMyVehicle());
|
|
}
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::SlowdownSafely_OnUpdate( CPed* pPed )
|
|
{
|
|
float fVehicleSpeedSqr = pPed->GetMyVehicle()->GetVelocity().Mag2();
|
|
if(fVehicleSpeedSqr < CTaskVehicleHandBrake::s_fMaxSpeedForHandbrake * CTaskVehicleHandBrake::s_fMaxSpeedForHandbrake)
|
|
{
|
|
SetState(ShouldWaitInCar() ? state_waitInCar : State_emergencyStop);
|
|
}
|
|
else
|
|
{
|
|
CTaskVehicleMissionBase* pVehicleTask = pPed->GetMyVehicle()->GetIntelligence()->GetActiveTask();
|
|
if(pVehicleTask)
|
|
{
|
|
if(pPed->GetMyVehicle()->IsDriver(pPed) )
|
|
{
|
|
pVehicleTask->SetCruiseSpeed(0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::EmergencyStop_OnEnter( CPed* pPed )
|
|
{
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (pPed && pPed->GetMyVehicle() && pPed->GetMyVehicle()->IsDriver(pPed) && pPed->GetMyVehicle()->IsNetworkClone())
|
|
return;
|
|
}
|
|
|
|
const CPed* pPedTarget = NULL;
|
|
taskFatalAssertf(m_target.GetEntity(), "Null target passed!" );
|
|
if( m_target.GetEntity()->GetIsTypePed() )
|
|
pPedTarget = static_cast<const CPed*>(m_target.GetEntity());
|
|
|
|
// Work out where the target is in relation to where the player is
|
|
Vector3 vToTarget = VEC3V_TO_VECTOR3(m_target.GetEntity()->GetTransform().GetPosition() - pPed->GetTransform().GetPosition());
|
|
vToTarget.z = 0.0f;
|
|
float fDistanceSq = vToTarget.Mag2();
|
|
vToTarget.NormalizeSafe();
|
|
float fDot = DotProduct(vToTarget, VEC3V_TO_VECTOR3(pPed->GetTransform().GetB()));
|
|
|
|
m_fJumpOutTimer = 0.0f;
|
|
m_uRunningFlags.ClearFlag(RF_RunningTempAction);
|
|
|
|
//Check if we should reverse or accelerate.
|
|
bool bReverse = false;
|
|
bool bAccelerate = false;
|
|
|
|
const bool bTargetInVehicle = pPedTarget && pPedTarget->GetVehiclePedInside();
|
|
const bool bTargetArmed = pPedTarget && pPedTarget->GetWeaponManager() && pPedTarget->GetWeaponManager()->GetIsArmed();
|
|
const bool bIsVehicleMovingSlowly = (pPed->GetMyVehicle()->GetVelocity().XYMag2() < square(1.0f));
|
|
const bool bIsVehicleAppropriate = (!pPed->GetMyVehicle()->InheritsFromBike());
|
|
const bool bIsTargetInWater = (pPedTarget && CTargetInWaterHelper::IsInWater(*pPedTarget));
|
|
if(!bTargetInVehicle && bTargetArmed && bIsVehicleMovingSlowly && pPed->GetIsDrivingVehicle() && bIsVehicleAppropriate && !bIsTargetInWater)
|
|
{
|
|
// Is the player just in front, or behind the car
|
|
const bool bInFrontOfCar = fDistanceSq < rage::square(10.0f) && fDot > 0.707f;
|
|
const bool bBehindOfCar = fDistanceSq < rage::square(7.0f) && fDot < -0.707f;
|
|
|
|
if( pPed->PopTypeGet() != POPTYPE_RANDOM_SCENARIO && bInFrontOfCar )
|
|
{
|
|
if(fwRandom::GetRandomTrueFalse())
|
|
{
|
|
bAccelerate = true;
|
|
}
|
|
}
|
|
else if( bBehindOfCar )
|
|
{
|
|
if(fwRandom::GetRandomTrueFalse())
|
|
{
|
|
bReverse = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bReverse)
|
|
{
|
|
SetNewTask(rage_new CTaskCarSetTempAction(pPed->GetMyVehicle(), (s8)TEMPACT_REVERSE_STRAIGHT_HARD, 1000 ));
|
|
m_uRunningFlags.SetFlag(RF_RunningTempAction);
|
|
}
|
|
else if(bAccelerate)
|
|
{
|
|
SetNewTask(rage_new CTaskCarSetTempAction(pPed->GetMyVehicle(), (s8)TEMPACT_GOFORWARD_HARD, 2000 ));
|
|
m_uRunningFlags.SetFlag(RF_RunningTempAction);
|
|
}
|
|
// If the car is moving, handbrake to a stop
|
|
else if( pPed->GetMyVehicle()->IsDriver(pPed) && pPed->GetMyVehicle()->GetVelocity().Mag2() > 5.0f )
|
|
{
|
|
s8 iBrakeType;
|
|
bool bDontTurnWhileBraking = (bIsTargetInWater && CCreekHackHelperGTAV::IsAffected(*pPed->GetMyVehicle()));
|
|
if(pPed->GetMyVehicle()->InheritsFromBike() || bDontTurnWhileBraking)
|
|
{
|
|
iBrakeType = (s8) TEMPACT_BRAKE;
|
|
}
|
|
else
|
|
{
|
|
//Search for obstructions on the left/right.
|
|
bool bObstructedL;
|
|
bool bObstructedR;
|
|
if(!FindObstructions(bObstructedL, bObstructedR))
|
|
{
|
|
bObstructedL = false;
|
|
bObstructedR = false;
|
|
}
|
|
|
|
//Generate the left, center, and right actions.
|
|
s8 iActionL = (s8)TEMPACT_HANDBRAKETURNLEFT_INTELLIGENT;
|
|
s8 iActionC = (s8)TEMPACT_BRAKE;
|
|
s8 iActionR = (s8)TEMPACT_HANDBRAKETURNRIGHT_INTELLIGENT;
|
|
|
|
//Brake correctly based on where the surrounding obstructions are.
|
|
if(bObstructedL && bObstructedR)
|
|
{
|
|
iBrakeType = iActionC;
|
|
|
|
//Blocked left and right. Tell our task just to stop rather than using TEMPACT_BRAKE as then we'll perform avoidance while slowing down
|
|
CreateSlowDownGoToTask(pPed->GetMyVehicle());
|
|
return;
|
|
}
|
|
else if(bObstructedL)
|
|
{
|
|
iBrakeType = iActionR;
|
|
}
|
|
else if(bObstructedR)
|
|
{
|
|
iBrakeType = iActionL;
|
|
}
|
|
else
|
|
{
|
|
iBrakeType = (fwRandom::GetRandomTrueFalse() ? iActionL : iActionR);
|
|
}
|
|
}
|
|
|
|
SetNewTask(rage_new CTaskCarSetTempAction(pPed->GetMyVehicle(), iBrakeType, 1000));
|
|
m_uRunningFlags.SetFlag(RF_RunningTempAction);
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::CreateSlowDownGoToTask(CVehicle* vehicle)
|
|
{
|
|
sVehicleMissionParams params;
|
|
|
|
params.m_iDrivingFlags = DMode_AvoidCars_Reckless|DF_DriveIntoOncomingTraffic|DF_SteerAroundPeds|
|
|
DF_UseStringPullingAtJunctions|DF_AdjustCruiseSpeedBasedOnRoadSpeed|DF_DontTerminateTaskWhenAchieved;
|
|
params.m_fTargetArriveDist = 0.0f;
|
|
params.m_fCruiseSpeed = 0.0f;
|
|
Vector3 vTarget;
|
|
m_target.GetPosition(vTarget);
|
|
params.SetTargetPosition(vTarget);
|
|
|
|
//Create the vehicle task.
|
|
CTask* pVehicleTask = CVehicleIntelligence::CreateAutomobileGotoTask(params, 10.0f);
|
|
|
|
//Create combat subtask
|
|
CTask* pSubTask = rage_new CTaskVehicleCombat(&m_target);
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskControlVehicle(vehicle, pVehicleTask, pSubTask);
|
|
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::EmergencyStop_OnUpdate( CPed* pPed )
|
|
{
|
|
bool bStopped = false;
|
|
CVehicle* pMyVehicle = pPed->GetMyVehicle();
|
|
bool bRunningTempAction = m_uRunningFlags.IsFlagSet(RF_RunningTempAction);
|
|
if(!bRunningTempAction && pMyVehicle && pPed->GetMyVehicle()->IsDriver(pPed))
|
|
{
|
|
CTaskVehicleMissionBase* pVehicleTask = pMyVehicle->GetIntelligence()->GetActiveTask();
|
|
if(pVehicleTask)
|
|
{
|
|
pVehicleTask->SetCruiseSpeed(0.0f);
|
|
}
|
|
else
|
|
{
|
|
bRunningTempAction = true;
|
|
}
|
|
|
|
bStopped = pMyVehicle->GetVelocity().Mag2() < 0.1f *0.1f;
|
|
}
|
|
|
|
// If the subtask has terminated - quit
|
|
if( ( bRunningTempAction && ( GetIsFlagSet(aiTaskFlags::SubTaskFinished) || GetSubTask() == NULL ) ) || bStopped)
|
|
{
|
|
if( pPed->GetIsInVehicle() && CanExitVehicle() )
|
|
{
|
|
SetState(State_exitVehicle);
|
|
}
|
|
else
|
|
{
|
|
SetState(State_exit);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::EmergencyStop_OnExit()
|
|
{
|
|
m_uRunningFlags.ClearFlag(RF_RunningTempAction);
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ExitVehicle_OnEnter( CPed* pPed )
|
|
{
|
|
//Create the exit vehicle task.
|
|
VehicleEnterExitFlags vehicleFlags;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::DelayForTime);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::ExitToWalk);
|
|
|
|
//Check if we should not close the door.
|
|
if(!ShouldCloseDoor())
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::DontCloseDoor);
|
|
}
|
|
|
|
//Check if we should exit to aim.
|
|
if(ShouldExitVehicleToAim())
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::ExitToAim);
|
|
}
|
|
|
|
//Check if we are forcing a warp
|
|
if(m_uRunningFlags.IsFlagSet(RF_ForceExitVehicleWarp))
|
|
{
|
|
m_uRunningFlags.ClearFlag(RF_ForceExitVehicleWarp);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::WarpIfDoorBlocked);
|
|
}
|
|
|
|
const CPed* pPedTarget = NULL;
|
|
taskFatalAssertf(m_target.GetEntity(), "NULL target entity!");
|
|
if( m_target.GetEntity()->GetIsTypePed() )
|
|
{
|
|
pPedTarget = static_cast<const CPed*>(m_target.GetEntity());
|
|
}
|
|
|
|
float fDelayTime;
|
|
if(pPed->GetMyVehicle() && pPed->GetMyVehicle()->IsDriver(pPed))
|
|
{
|
|
if(pPedTarget && pPedTarget->IsExitingVehicle() && (pPedTarget->GetVehiclePedInside() || pPedTarget->GetMountPedOn()))
|
|
{
|
|
fDelayTime = pPed->GetRandomNumberInRangeFromSeed(sm_Tunables.m_MinDelayExitTime, sm_Tunables.m_MaxDelayExitTime);
|
|
}
|
|
else
|
|
{
|
|
fDelayTime = 0.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fDelayTime = pPed->GetRandomNumberInRangeFromSeed(sm_Tunables.m_MinDelayExitTime, sm_Tunables.m_MaxDelayExitTime);
|
|
}
|
|
|
|
CTaskExitVehicle* pTask = rage_new CTaskExitVehicle(pPed->GetMyVehicle(),vehicleFlags, fDelayTime);
|
|
if(pPedTarget)
|
|
{
|
|
pTask->SetDesiredTarget(pPedTarget);
|
|
}
|
|
|
|
//Set the min time to wait for other ped to exit.
|
|
pTask->SetMinTimeToWaitForOtherPedToExit(-1.0f);
|
|
|
|
//Check if anyone in the vehicle should provide cover during exit vehicle.
|
|
if(ShouldAnyoneProvideCoverDuringExitVehicle())
|
|
{
|
|
//Check if this ped should provide cover during exit vehicle.
|
|
fwMvClipSetId clipSetId;
|
|
if(ShouldPedProvideCoverDuringExitVehicle(*pPed, clipSetId))
|
|
{
|
|
//Note that we are providing cover during exit vehicle.
|
|
m_uRunningFlags.SetFlag(RF_ProvidingCoverDuringExitVehicle);
|
|
|
|
//The cover animations typically assume the weapon is in the hand of the ped.
|
|
//If this is no longer accurate, an addition should be made to the anim metadata to specify this option.
|
|
//Create the weapon object in the hand of the ped.
|
|
CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
|
|
if(pWeaponManager)
|
|
{
|
|
pWeaponManager->EquipBestWeapon();
|
|
pWeaponManager->CreateEquippedWeaponObject();
|
|
pWeaponManager->BackupEquippedWeapon();
|
|
}
|
|
|
|
//Use the streamed exit.
|
|
pTask->SetRunningFlag(CVehicleEnterExitFlags::IsStreamedExit);
|
|
pTask->SetStreamedExitClipsetId(clipSetId);
|
|
}
|
|
|
|
//If we are not providing cover, we are on the receiving end.
|
|
//Delay our exit a bit longer to give the others time to provide the cover.
|
|
if(!m_uRunningFlags.IsFlagSet(RF_ProvidingCoverDuringExitVehicle))
|
|
{
|
|
pTask->SetDelayTime(pTask->GetDelayTime() + 3.0f);
|
|
}
|
|
}
|
|
|
|
//Set the new task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ExitVehicle_OnUpdate( CPed* pPed )
|
|
{
|
|
// If the subtask has terminated - quit
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) || !GetSubTask() )
|
|
{
|
|
if( pPed->GetIsInVehicle() )
|
|
{
|
|
SetState(State_waitForVehicleExit);
|
|
return FSM_Continue;
|
|
}
|
|
else
|
|
{
|
|
//Check if we should equip our best weapon.
|
|
if(ShouldEquipBestWeaponAfterExitingVehicle())
|
|
{
|
|
//Equip the best weapon upon leaving the vehicle.
|
|
//The best weapon inside/outside the vehicle can differ.
|
|
CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
|
|
if(pWeaponManager)
|
|
{
|
|
pWeaponManager->EquipBestWeapon();
|
|
}
|
|
}
|
|
|
|
//Check if we are providing cover during exit vehicle.
|
|
if(m_uRunningFlags.IsFlagSet(RF_ProvidingCoverDuringExitVehicle))
|
|
{
|
|
//When a ped exits a vehicle, their equipped weapon is destroyed for some reason.
|
|
//The cover animations assume the weapon is always in the hands of the ped,
|
|
//so we need to re-create it here.
|
|
CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
|
|
if(pWeaponManager)
|
|
{
|
|
pWeaponManager->CreateEquippedWeaponObject();
|
|
}
|
|
|
|
//Move to the provide cover state.
|
|
SetState(State_provideCoverDuringExitVehicle);
|
|
}
|
|
else
|
|
{
|
|
SetState(State_exit);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ExitVehicleImmediately_OnEnter( CPed* pPed )
|
|
{
|
|
//Create the vehicle flags.
|
|
VehicleEnterExitFlags vehicleFlags;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::DontCloseDoor);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::WarpIfDoorBlocked);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::DontWaitForCarToStop);
|
|
|
|
//Check if we should jump out.
|
|
if(ShouldJumpOutOfVehicle())
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::JumpOut);
|
|
}
|
|
//Check if we should exit to aim.
|
|
else if(ShouldExitVehicleToAim())
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::ExitToAim);
|
|
}
|
|
|
|
float fExitDelayTime = pPed->GetMyVehicle() && pPed->GetMyVehicle()->IsDriver(pPed) ? 0.0f : (float)( pPed->GetRandomSeed()/(float)RAND_MAX_16 );
|
|
CTaskExitVehicle* pTask = rage_new CTaskExitVehicle(pPed->GetMyVehicle(),vehicleFlags, fExitDelayTime);
|
|
if(m_target.GetEntity() && m_target.GetEntity()->GetIsTypePed())
|
|
{
|
|
pTask->SetDesiredTarget(static_cast<const CPed*>(m_target.GetEntity()));
|
|
}
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ExitVehicleImmediately_OnUpdate( CPed* pPed )
|
|
{
|
|
// If the subtask has terminated - quit
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) || !GetSubTask() )
|
|
{
|
|
if( pPed->GetIsInVehicle() )
|
|
{
|
|
if (GetTimeInState() > EXIT_FAILED_TIME_IN_STATE)
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetState(State_exit);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::HasClearVehicleExitPoint() const
|
|
{
|
|
const CPed* pPed = GetPed();
|
|
|
|
CVehicle* pVehicle = pPed->GetMyVehicle();
|
|
if(pVehicle)
|
|
{
|
|
s32 iPedsSeatIndex = pVehicle->GetSeatManager()->GetPedsSeatIndex(pPed);
|
|
if(iPedsSeatIndex < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
VehicleEnterExitFlags vehicleFlags;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::IsExitingVehicle);
|
|
s32 iTargetEntryPoint = CModelSeatInfo::EvaluateSeatAccessFromAllEntryPoints(pPed, pVehicle, SR_Specific, iPedsSeatIndex, false, vehicleFlags);
|
|
if(iTargetEntryPoint >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::WaitForVehicleExit_OnEnter()
|
|
{
|
|
m_NextExitCheckTimer.Set(fwTimer::GetTimeInMilliseconds(), fwRandom::GetRandomNumberInRange(750, 1000));
|
|
SetNewTask(rage_new CTaskVehicleCombat(&m_target, CTaskVehicleCombat::Flag_forceAllowDriveBy) );
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::WaitForVehicleExit_OnUpdate()
|
|
{
|
|
VehiclePersuitState iState = GetDesiredState(GetPed());
|
|
if(iState == State_invalid && m_fJumpOutTimer <= 0.0f)
|
|
{
|
|
// If we didn't choose an exit vehicle or stop state again then it likely means we should not be exiting anymore
|
|
SetState(GetPed()->GetIsDrivingVehicle() ? ChooseDefaultState() : State_passengerInVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
else
|
|
{
|
|
if(m_NextExitCheckTimer.IsOutOfTime())
|
|
{
|
|
m_NextExitCheckTimer.Set(fwTimer::GetTimeInMilliseconds(), fwRandom::GetRandomNumberInRange(750, 1000));
|
|
|
|
CTaskVehicleCombat* pSubTask = static_cast<CTaskVehicleCombat*>(FindSubTaskOfType(CTaskTypes::TASK_VEHICLE_COMBAT));
|
|
if(HasClearVehicleExitPoint())
|
|
{
|
|
if(pSubTask && pSubTask->GetState() == CTaskVehicleCombat::State_vehicleGun)
|
|
{
|
|
GetSubTask()->RequestTermination();
|
|
}
|
|
else
|
|
{
|
|
SetState(State_exitVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
else if(GetPed()->PopTypeIsMission() && GetTimeInState() > sm_Tunables.m_MaxTimeWaitForExitBeforeWarp)
|
|
{
|
|
m_uRunningFlags.SetFlag(RF_ForceExitVehicleWarp);
|
|
SetState(State_exitVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
else if(!pSubTask)
|
|
{
|
|
SetState(State_exitVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::WaitForBuddiesToEnter_OnEnter( CPed* pPed )
|
|
{
|
|
SetNewTask(rage_new CTaskCarDriveWander(pPed->GetMyVehicle(), DMode_StopForCars, 0 ));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::WaitForBuddiesToEnter_OnUpdate( CPed* pPed )
|
|
{
|
|
// If the subtask has terminated carry on
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) )
|
|
{
|
|
SetState(State_start);
|
|
return FSM_Continue;
|
|
}
|
|
// no-one is entering the car, carry on
|
|
if( CountNumberBuddiesEnteringVehicle(pPed, pPed->GetMyVehicle()) == 0 )
|
|
{
|
|
SetState(State_start);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProvideCoverDuringExitVehicle_OnEnter( CPed* pPed )
|
|
{
|
|
//Grab the target ped.
|
|
const CPed* pTarget = GetTargetPed();
|
|
|
|
//Check if the ped has a LOS to the target.
|
|
CAITarget target;
|
|
s32 iFlags = 0;
|
|
if(pTarget && DoesPedHaveLosToTarget(*pPed, *pTarget))
|
|
{
|
|
//Set the target.
|
|
target.SetEntity(pTarget);
|
|
}
|
|
else
|
|
{
|
|
//Calculate the position to aim at.
|
|
Vec3V vAimPosition = pPed->GetTransform().GetPosition() + pPed->GetTransform().GetForward();
|
|
|
|
//Set the target position.
|
|
target.SetPosition(VEC3V_TO_VECTOR3(vAimPosition));
|
|
|
|
//Aim only.
|
|
iFlags |= CTaskShootAtTarget::Flag_aimOnly;
|
|
}
|
|
|
|
//Set the shoot at target task.
|
|
SetNewTask(rage_new CTaskShootAtTarget(target, iFlags));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ProvideCoverDuringExitVehicle_OnUpdate( CPed* pPed )
|
|
{
|
|
//Ensure the ped is crouching.
|
|
if(!pPed->GetIsCrouching())
|
|
{
|
|
//Force the ped into the crouching state.
|
|
//This is because the cover animations end with the ped crouching.
|
|
pPed->SetIsCrouching(true);
|
|
pPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Crouch_Idle);
|
|
}
|
|
|
|
//Grab the vehicle.
|
|
CVehicle* pVehicle = pPed->GetMyVehicle();
|
|
|
|
//Wait until there are no more alive peds in the vehicle.
|
|
if(!pVehicle || !pVehicle->HasAlivePedsInIt())
|
|
{
|
|
SetState(State_exit);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProvideCoverDuringExitVehicle_OnExit( CPed* pPed )
|
|
{
|
|
//Clear the crouched state.
|
|
pPed->SetIsCrouching(false);
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ApproachTarget_OnEnter()
|
|
{
|
|
//Ensure the vehicle is valid.
|
|
CVehicle* pVehicle = GetPed()->GetMyVehicle();
|
|
if(!taskVerifyf(pVehicle, "The vehicle is invalid."))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Create the parameters.
|
|
sVehicleMissionParams params;
|
|
params.SetTargetEntity(const_cast<CEntity *>(m_target.GetEntity()));
|
|
params.m_iDrivingFlags = DMode_AvoidCars_Reckless|DF_DriveIntoOncomingTraffic|DF_SteerAroundPeds|DF_UseStringPullingAtJunctions|DF_AdjustCruiseSpeedBasedOnRoadSpeed;
|
|
params.m_fTargetArriveDist = sm_Tunables.m_ApproachTarget.m_TargetArriveDist;
|
|
params.m_fCruiseSpeed = sm_Tunables.m_ApproachTarget.m_CruiseSpeed;
|
|
|
|
//Create the vehicle task.
|
|
CTask* pVehicleTask = rage_new CTaskVehicleApproach(params);
|
|
|
|
//Create the sub-task.
|
|
CTask* pSubTask = rage_new CTaskVehicleCombat(&m_target);
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskControlVehicle(pVehicle, pVehicleTask, pSubTask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::ApproachTarget_OnUpdate()
|
|
{
|
|
//Update the vehicle task.
|
|
UpdateVehicleTaskForApproachTarget();
|
|
|
|
//Check if the sub-task is invalid.
|
|
if(!GetSubTask())
|
|
{
|
|
//Finish the task.
|
|
SetState(State_exit);
|
|
}
|
|
//Check if the sub-task has finished.
|
|
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Make an emergency stop.
|
|
SetState(State_slowdownSafely);
|
|
}
|
|
else
|
|
{
|
|
//Check if we have a new desired state.
|
|
VehiclePersuitState iState = GetDesiredState(GetPed());
|
|
if(iState != State_invalid)
|
|
{
|
|
//Move to the new state.
|
|
SetState(iState);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::WaitInCar_OnEnter()
|
|
{
|
|
//Grab the ped.
|
|
CPed* pPed = GetPed();
|
|
|
|
//Ensure the ped is driving a vehicle.
|
|
if(!taskVerifyf(pPed->GetIsDrivingVehicle(), "The ped is not driving a vehicle."))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Set the target position.
|
|
taskAssert(m_target.GetIsValid());
|
|
m_target.GetPosition(RC_VECTOR3(m_vTargetPositionWhenWaitBegan));
|
|
|
|
//Create the vehicle task.
|
|
CTask* pVehicleTask = rage_new CTaskVehicleStop(CTaskVehicleStop::SF_DontTerminateWhenStopped | CTaskVehicleStop::SF_UseFullBrake);
|
|
|
|
//Create the sub-task.
|
|
CTask* pSubTask = rage_new CTaskVehicleCombat(&m_target);
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskControlVehicle(GetPed()->GetVehiclePedInside(), pVehicleTask, pSubTask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehiclePersuit::WaitInCar_OnUpdate()
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(!GetSubTask() || GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(State_start);
|
|
}
|
|
else
|
|
{
|
|
//Check if we can exit the vehicle.
|
|
if(CanExitVehicle())
|
|
{
|
|
//Check if we've been waiting for a while.
|
|
static dev_float s_fMinTimeInState = 3.0f;
|
|
static dev_float s_fMaxTimeInState = 5.0f;
|
|
float fMinTimeInState = GetPed()->GetRandomNumberInRangeFromSeed(s_fMinTimeInState, s_fMaxTimeInState);
|
|
if(GetTimeInState() > fMinTimeInState)
|
|
{
|
|
SetState(State_exitVehicle);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
//Check if we have a new desired state.
|
|
VehiclePersuitState iState = GetDesiredState(GetPed());
|
|
if(iState != State_invalid && iState != State_slowdownSafely) //we entered here from slowdownSafely, don't want to loop
|
|
{
|
|
SetState(iState);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Creates a car stealing a vehicle and persuing the car
|
|
//-------------------------------------------------------------------------
|
|
s32 CTaskVehiclePersuit::CountNumberBuddiesEnteringVehicle( CPed* pPed, CVehicle* pVehicle)
|
|
{
|
|
s32 iCount = 0;
|
|
|
|
// Make sure no other friendly ped is trying to enter it
|
|
const CEntityScannerIterator entityList = pPed->GetPedIntelligence()->GetNearbyPeds();
|
|
for(const CEntity* pEnt = entityList.GetFirst(); pEnt; pEnt = entityList.GetNext())
|
|
{
|
|
CPed* pFriendlyPed = (CPed*) pEnt;
|
|
if( !pPed->GetPedIntelligence()->IsFriendlyWith( *pFriendlyPed ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CPedIntelligence* pFriendlyPedIntelligence = pFriendlyPed->GetPedIntelligence();
|
|
|
|
// is ped entering this car?
|
|
if ((pFriendlyPedIntelligence->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_ENTER_VEHICLE ) &&
|
|
( ((CVehicle*)pFriendlyPedIntelligence->GetQueriableInterface()->GetTargetForTaskType(CTaskTypes::TASK_ENTER_VEHICLE)) == pVehicle ) ))
|
|
{
|
|
++iCount;
|
|
}
|
|
// If the ped was previously in the car, could he be trying to go back even if he is not currently entering?
|
|
else
|
|
{
|
|
const s32 iPedPreviousSet = pVehicle->GetSeatManager()->GetLastPedsSeatIndex(pFriendlyPed);
|
|
const bool bWillGoBackToThisVehicle = (pFriendlyPed->GetMyVehicle() == pVehicle) && iPedPreviousSet >= 0;
|
|
if (bWillGoBackToThisVehicle)
|
|
{
|
|
if (!pFriendlyPed->IsFatallyInjured())
|
|
{
|
|
const s32 iCurrentTaskPriority = pFriendlyPedIntelligence->GetActiveTaskPriority();
|
|
|
|
// the ped is in a temporary response -> wait for him
|
|
if (iCurrentTaskPriority <= PED_TASK_PRIORITY_EVENT_RESPONSE_TEMP)
|
|
{
|
|
++iCount;
|
|
}
|
|
// the ped is running a non temporary response -> wait for him for a limited time
|
|
else if (iCurrentTaskPriority == PED_TASK_PRIORITY_EVENT_RESPONSE_NONTEMP)
|
|
{
|
|
TUNE_GROUP_INT(VEHICLE_PURSUIT, uMAX_TIME_TO_WAIT_FOR_A_NONTEMP_TASKED_PED_MS, 5000u, 0u, 10000000u, 500u);
|
|
if (pFriendlyPedIntelligence->GetLastTimeStartedEnterVehicle() + uMAX_TIME_TO_WAIT_FOR_A_NONTEMP_TASKED_PED_MS > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
++iCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return iCount;
|
|
}
|
|
|
|
#define TIME_TO_LEAVE_CAR_WHEN_UNDRIVABLE (5.0f)
|
|
|
|
bool CTaskVehiclePersuit::GetDesiredAircraftState(CPed* pPed, CTaskVehiclePersuit::VehiclePersuitState &iDesiredState)
|
|
{
|
|
// Don't exit an aircraft, unless its landed
|
|
if(pPed->GetMyVehicle()->GetIsAircraft())
|
|
{
|
|
bool bIsSubmerged = (pPed->GetMyVehicle()->m_Buoyancy.GetStatus() != NOT_IN_WATER && pPed->GetMyVehicle()->m_Buoyancy.GetSubmergedLevel() >= 0.5f);
|
|
if(bIsSubmerged)
|
|
{
|
|
iDesiredState = State_exitVehicleImmediately;
|
|
return true;
|
|
}
|
|
else if(pPed->GetMyVehicle()->HasContactWheels() == false)
|
|
{
|
|
iDesiredState = State_invalid;
|
|
return true;
|
|
}
|
|
else if(pPed->GetMyVehicle()->InheritsFromPlane())
|
|
{
|
|
iDesiredState = State_emergencyStop;
|
|
return true;
|
|
}
|
|
else if( pPed->GetMyVehicle()->GetVelocity().Mag2() > rage::square(1.0f))
|
|
{
|
|
if(!pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanUseVehicles))
|
|
{
|
|
iDesiredState = State_slowdownSafely;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
iDesiredState = State_invalid;
|
|
return true;
|
|
}
|
|
}
|
|
else if(pPed->GetMyVehicle()->InheritsFromHeli())
|
|
{
|
|
//Check if the heli is landed.
|
|
const CHeli* pHeli = static_cast<CHeli *>(pPed->GetMyVehicle());
|
|
if(pHeli->GetHeliIntelligence()->IsLanded())
|
|
{
|
|
//Check if we can leave the heli.
|
|
static float s_fMinTimeLanded = 0.5f;
|
|
bool bCanLeaveHeli = (pHeli->GetHeliIntelligence()->GetTimeSpentLanded() > s_fMinTimeLanded) &&
|
|
( !pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanUseVehicles) ||
|
|
!pPed->GetIsDrivingVehicle() || pHeli->GetIsMainRotorBroken() || (pHeli->GetStatus() == STATUS_WRECKED) || !pHeli->IsInDriveableState() );
|
|
if(bCanLeaveHeli)
|
|
{
|
|
iDesiredState = State_exitVehicle;
|
|
return true;
|
|
}
|
|
}
|
|
else if(pHeli->GetTimeSpentCollidingNotLanded() > CTaskHeliOrderResponse::GetTunables().m_MaxTimeCollidingBeforeExit)
|
|
{
|
|
iDesiredState = State_exitVehicle;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CTaskVehiclePersuit::VehiclePersuitState CTaskVehiclePersuit::GetDesiredState(CPed* pPed)
|
|
{
|
|
if(m_uRunningFlags.IsFlagSet(RF_TargetChanged))
|
|
{
|
|
m_uRunningFlags.ClearFlag(RF_TargetChanged);
|
|
return State_start;
|
|
}
|
|
|
|
// If the flag isn't set allowing the ped to exit the vehicle - don't exit
|
|
if(!CanExitVehicle())
|
|
{
|
|
return State_invalid;
|
|
}
|
|
|
|
taskFatalAssertf(pPed->GetMyVehicle(), "Ped vehicle pointer is NULL");
|
|
|
|
// Process aircraft state before attack state, otherwise we'll end up stuck firing in an undriveable aircraft on the ground
|
|
CTaskVehiclePersuit::VehiclePersuitState iDesiredAircraftState;
|
|
if(GetDesiredAircraftState(pPed, iDesiredAircraftState))
|
|
{
|
|
return iDesiredAircraftState;
|
|
}
|
|
|
|
// don't exit if we are in the attack state and we can still attack
|
|
if(GetState() == State_attack && ShouldAttack())
|
|
{
|
|
return State_invalid;
|
|
}
|
|
|
|
// Exit the vehicle if we aren't allowed to use vehicles
|
|
if(!pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanUseVehicles))
|
|
{
|
|
return State_exitVehicle;
|
|
}
|
|
|
|
//Check if the vehicle is undriveable.
|
|
bool bIncrementedVehicleUndriveableTime = false;
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(pVehicle)
|
|
{
|
|
if (CVehicleUndriveableHelper::IsUndriveable(*pVehicle))
|
|
{
|
|
bIncrementedVehicleUndriveableTime = true;
|
|
m_fTimeVehicleUndriveable += GetTimeStep();
|
|
if( m_fTimeVehicleUndriveable > TIME_TO_LEAVE_CAR_WHEN_UNDRIVABLE)
|
|
{
|
|
if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_GetOutUndriveableVehicle))
|
|
{
|
|
return State_exitVehicle;
|
|
}
|
|
|
|
CPedGroup* pPedGroup = pPed->GetPedsGroup();
|
|
if(!pPedGroup || pPedGroup != CGameWorld::FindLocalPlayerGroup())
|
|
{
|
|
return State_exitVehicle;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fTimeVehicleUndriveable = 0.f;
|
|
}
|
|
}
|
|
|
|
// Fill out a ped target pointer if the target is a ped.
|
|
const CPed* pPedTarget = NULL;
|
|
taskFatalAssertf(m_target.GetEntity(), "NULL target entity!");
|
|
if( m_target.GetEntity()->GetIsTypePed() )
|
|
pPedTarget = static_cast<const CPed*>(m_target.GetEntity());
|
|
|
|
if(IsPedOnMovingVehicleOrTrain(pPedTarget))
|
|
{
|
|
return State_invalid;
|
|
}
|
|
|
|
// If the driver turns out to be a hated ped, exit straight away
|
|
CPed* pDriver = pPed->GetMyVehicle()->GetDriver();
|
|
bool bHatesDriver = ( (pPedTarget && pDriver == pPedTarget) || (pDriver && pPed->GetPedIntelligence()->IsThreatenedBy(*pDriver)) );
|
|
if( bHatesDriver )
|
|
return State_exitVehicleImmediately; // Immediately, doesn't wait for the car to slow
|
|
|
|
const bool bPedIsDriver = pPed->GetMyVehicle()->IsDriver(pPed);
|
|
|
|
// Passenger exit check - exit if the driver seat becomes empty because they have left or been killed
|
|
if( !bPedIsDriver )
|
|
{
|
|
// Check if we should exit the vehicle early and make sure it's going slow enough
|
|
if( m_uRunningFlags.IsFlagSet(RF_CanExitVehicleEarly) && pDriver &&
|
|
pPed->GetMyVehicle()->GetVelocity().Mag2() < rage::square(sm_Tunables.m_MaxSpeedForEarlyCombatExit) )
|
|
{
|
|
// Then make sure the driver is running a combat task as well
|
|
CTaskVehiclePersuit* pDriverTask = static_cast<CTaskVehiclePersuit*>(pDriver->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VEHICLE_PERSUIT));
|
|
if( pDriverTask )
|
|
{
|
|
// And make sure the driver is attempting to stop or exit
|
|
s32 iDriverState = pDriverTask->GetState();
|
|
if( iDriverState == State_slowdownSafely || iDriverState == State_emergencyStop || iDriverState == State_exitVehicle || iDriverState == State_exitVehicleImmediately )
|
|
{
|
|
return State_exitVehicleImmediately;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if the driver seat is empty.
|
|
const bool bIsDriverSeatEmpty = (!pDriver || pDriver->IsInjured());
|
|
|
|
//Check if the driver is exiting the vehicle.
|
|
bool bIsDriverExitingVehicle = false;
|
|
if(pDriver)
|
|
{
|
|
if( pDriver->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_EXIT_VEHICLE) )
|
|
{
|
|
bIsDriverExitingVehicle = true;
|
|
}
|
|
else
|
|
{
|
|
CTaskVehiclePersuit* pDriverTask = static_cast<CTaskVehiclePersuit*>(pDriver->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VEHICLE_PERSUIT));
|
|
if(pDriverTask && pDriverTask->GetState() == State_waitForVehicleExit)
|
|
{
|
|
bIsDriverExitingVehicle = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if the driver is trying to exit the vehicle.
|
|
const bool bIsDriverTryingToExitVehicle = pDriver && IsPedTryingToExitVehicle(*pDriver);
|
|
|
|
//Check if our old driver is entering the vehicle.
|
|
CPed* pOldDriver = pPed->GetMyVehicle()->GetSeatManager()->GetLastPedInSeat(pPed->GetMyVehicle()->GetDriverSeat());
|
|
bool bIsOldDriverEnteringVehicle = pOldDriver && !pOldDriver->IsInjured() &&
|
|
pOldDriver->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_ENTER_VEHICLE) &&
|
|
pOldDriver->GetVehiclePedEntering() == pPed->GetMyVehicle();
|
|
|
|
//Dont exit aquatic vehicles
|
|
bool bAquaticVehicleInWater = pVehicle && pVehicle->GetIsAquatic() && pVehicle->GetIsInWater();
|
|
|
|
//Check if we should exit the vehicle.
|
|
bool bExitVehicle = !bAquaticVehicleInWater && !bIsOldDriverEnteringVehicle &&
|
|
(bIsDriverSeatEmpty || bIsDriverExitingVehicle || bIsDriverTryingToExitVehicle);
|
|
|
|
if(bExitVehicle)
|
|
{
|
|
float fTimeToLeave = pPed->GetRandomNumberInRangeFromSeed(sm_Tunables.m_MinPassengerTimeToLeaveVehicle, sm_Tunables.m_MaxPassengerTimeToLeaveVehicle);
|
|
m_fJumpOutTimer += GetTimeStep();
|
|
if(m_fJumpOutTimer > fTimeToLeave)
|
|
{
|
|
return State_exitVehicle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fJumpOutTimer = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Don't exit tanks
|
|
if(pPed->GetMyVehicle()->IsTank())
|
|
{
|
|
return State_invalid;
|
|
}
|
|
|
|
// If the ped is in a group and isn't driving - don't exit
|
|
if (pPed->GetPedsGroup() && !bPedIsDriver)
|
|
{
|
|
return State_invalid;
|
|
}
|
|
|
|
//Check if we should make an emergency stop due to an avoidance area.
|
|
if(ShouldMakeEmergencyStopDueToAvoidanceArea())
|
|
{
|
|
return State_slowdownSafely;
|
|
}
|
|
|
|
// Ped target specific state changes
|
|
if( pPedTarget )
|
|
{
|
|
//Check if the target is in a vehicle or on a mount.
|
|
const CVehicle* pVehicleTargetInside = pPedTarget->GetVehiclePedInside();
|
|
const CPed* pMountTargetOn = pPedTarget->GetMountPedOn();
|
|
|
|
// If the target ped isn't in a vehicle and is not on a mount
|
|
if( (!pVehicleTargetInside && !pMountTargetOn) ||
|
|
pPedTarget->IsExitingVehicle() )
|
|
{
|
|
// Driver only checks - the passenger will exit in response to the driver deciding to give chase on foot
|
|
if( bPedIsDriver )
|
|
{
|
|
CPedTargetting* pPedTargetting = pPed->GetPedIntelligence()->GetTargetting( true );
|
|
LosStatus losStatus = pPedTargetting ? pPedTargetting->GetLosStatus( (CEntity*) pPedTarget ) : Los_clear;
|
|
|
|
//Grab the vehicle.
|
|
pVehicle = pPed->GetMyVehicle();
|
|
|
|
//Keep track of whether we can leave the car.
|
|
bool bCouldLeaveCar = false;
|
|
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation obstructionInformation;
|
|
if(GetObstructionInformation(obstructionInformation))
|
|
{
|
|
//Check if we are completely obstructed.
|
|
if(obstructionInformation.m_uFlags.IsFlagSet(CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsCompletelyObstructed) ||
|
|
obstructionInformation.m_uFlags.IsFlagSet(CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsCompletelyObstructedClose))
|
|
{
|
|
//Check if we are generally stopped.
|
|
float fSpeedSq = pVehicle->GetVelocity().XYMag2();
|
|
if(fSpeedSq < 0.25f * 0.25f)
|
|
{
|
|
bCouldLeaveCar = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if a collision with a law enforcement vehicle is imminent.
|
|
static float s_fMinDistanceToLeave = 5.0f;
|
|
static float s_fMaxDistanceToLeave = 10.0f;
|
|
float fDistanceToLeave = pPed->GetRandomNumberInRangeFromSeed(s_fMinDistanceToLeave, s_fMaxDistanceToLeave);
|
|
static float s_fMinTimeToLeave = 0.5f;
|
|
static float s_fMaxTimeToLeave = 1.0f;
|
|
float fTimeToLeave = pPed->GetRandomNumberInRangeFromSeed(s_fMinTimeToLeave, s_fMaxTimeToLeave);
|
|
bool bWillCollideWithPed = false;
|
|
bool bWillCollideWithVehicle = false;
|
|
if(IsCollisionWithLawEnforcementImminent(fDistanceToLeave, fTimeToLeave, bWillCollideWithPed, bWillCollideWithVehicle))
|
|
{
|
|
bCouldLeaveCar = true;
|
|
}
|
|
|
|
//Check if we have been shot at.
|
|
bool bShotAt = (pPed->IsLawEnforcementPed() && (pPed->GetMyVehicle()->GetWeaponDamageEntity() == pPedTarget));
|
|
|
|
//Generate the ideal distance away from the target.
|
|
float fDistanceToCheck = 0.0f;
|
|
|
|
//Check if we could leave our car.
|
|
if(bCouldLeaveCar)
|
|
{
|
|
fDistanceToCheck = Max(fDistanceToCheck, sm_Tunables.m_IdealDistanceCouldLeaveCar);
|
|
}
|
|
|
|
//Check if the vehicle has been shot.
|
|
if(bShotAt)
|
|
{
|
|
fDistanceToCheck = Max(fDistanceToCheck, sm_Tunables.m_IdealDistanceShotAt);
|
|
}
|
|
|
|
//Check if the vehicle is a bike.
|
|
if(pVehicle->InheritsFromBike())
|
|
{
|
|
fDistanceToCheck = Max(fDistanceToCheck, pPedTarget->GetWeaponManager() && !pPedTarget->GetWeaponManager()->GetIsArmed() ? sm_Tunables.m_IdealDistanceOnBikeAndTargetUnarmed : sm_Tunables.m_IdealDistanceOnBikeAndTargetArmed);
|
|
}
|
|
else
|
|
{
|
|
fDistanceToCheck = Max(fDistanceToCheck, pPedTarget->GetWeaponManager() && !pPedTarget->GetWeaponManager()->GetIsArmed() ? sm_Tunables.m_IdealDistanceInVehicleAndTargetUnarmed : sm_Tunables.m_IdealDistanceInVehicleAndTargetArmed);
|
|
}
|
|
|
|
//Calculate the distance it will take for the vehicle to stop.
|
|
float fDistanceToStop = GetDistToStopAtCurrentSpeed(*pVehicle);
|
|
|
|
//Generate the position offset for the stopping distance.
|
|
Vec3V vDistanceToStop = Scale(pVehicle->GetTransform().GetB(), ScalarVFromF32(fDistanceToStop));
|
|
|
|
//Add the distance to stop offset to the vehicle position.
|
|
Vec3V vVehiclePos = Add(pVehicle->GetTransform().GetPosition(), vDistanceToStop);
|
|
|
|
//Get the target position for the emergency stop.
|
|
Vec3V vTargetPosition = GetTargetPositionForEmergencyStop();
|
|
|
|
//Calculate the distance to the target.
|
|
const float fDistanceSqToTarget = DistSquared(vTargetPosition, vVehiclePos).Getf();
|
|
|
|
//Check if we are inside the stopping distance.
|
|
if(fDistanceSqToTarget < rage::square(fDistanceToCheck))
|
|
{
|
|
//Check if the LOS is blocked.
|
|
bool bTargetPositionKnown = (pPedTargetting && pPedTargetting->AreTargetsWhereaboutsKnown(NULL, pPedTarget));
|
|
bool bLosBlocked = (losStatus == Los_blocked);
|
|
|
|
//If our LOS is not blocked, or we could leave our car, or we were shot at, we can make the emergency stop.
|
|
if(!bLosBlocked || bTargetPositionKnown || bCouldLeaveCar || bShotAt)
|
|
{
|
|
return State_slowdownSafely;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// The target is in a vehicle or on a mount
|
|
else
|
|
{
|
|
// Only leave if our vehicle is no longer drivable
|
|
if(pVehicleTargetInside)
|
|
{
|
|
const bool bVehcileStuckOrNotDriveable = (pPed->GetMyVehicle()->IsOnItsSide() || pPed->GetMyVehicle()->IsUpsideDown() || !pPed->GetMyVehicle()->m_nVehicleFlags.bIsThisADriveableCar );
|
|
if( bVehcileStuckOrNotDriveable )
|
|
{
|
|
if (!bIncrementedVehicleUndriveableTime)
|
|
{
|
|
bIncrementedVehicleUndriveableTime = true;
|
|
m_fTimeVehicleUndriveable += GetTimeStep();
|
|
}
|
|
if( m_fTimeVehicleUndriveable > TIME_TO_LEAVE_CAR_WHEN_UNDRIVABLE)
|
|
{
|
|
return State_exitVehicle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fTimeVehicleUndriveable = 0.f;
|
|
}
|
|
}
|
|
|
|
//Check if we are the driver.
|
|
if(bPedIsDriver)
|
|
{
|
|
//Check if the target is moving slowly.
|
|
float fSpeedSq = pVehicleTargetInside ? pVehicleTargetInside->GetVelocity().Mag2() : pMountTargetOn->GetVelocity().Mag2();
|
|
static dev_float s_fMaxSpeed = 1.75f;
|
|
bool bIsMovingSlowly = (fSpeedSq < square(s_fMaxSpeed));
|
|
if(bIsMovingSlowly)
|
|
{
|
|
m_fJumpOutTimer += GetTimeStep();
|
|
}
|
|
else
|
|
{
|
|
m_fJumpOutTimer = 0.0f;
|
|
}
|
|
|
|
//Check if we can leave the vehicle.
|
|
float fMinDriverTimeToLeaveVehicle = pPed->GetRandomNumberInRangeFromSeed(
|
|
sm_Tunables.m_MinDriverTimeToLeaveVehicle, sm_Tunables.m_MaxDriverTimeToLeaveVehicle);
|
|
bool bCanLeaveVehicle = (m_fJumpOutTimer >= fMinDriverTimeToLeaveVehicle);
|
|
if(!bCanLeaveVehicle)
|
|
{
|
|
//We are also allowed to leave if the smoothed speed is very low -- this can happen
|
|
//when cops enter combat very close to a vehicle that hasn't been moving for a while.
|
|
static dev_float s_fMaxSmoothedSpeed = 0.25f;
|
|
bCanLeaveVehicle = (pVehicleTargetInside && !pVehicleTargetInside->IsNetworkClone() &&
|
|
(pVehicleTargetInside->GetIntelligence()->GetSmoothedSpeedSq() < square(s_fMaxSmoothedSpeed)));
|
|
}
|
|
|
|
bool bNeedLOS = true;
|
|
|
|
//Check if we are generally stopped and the target isn't moving much
|
|
if(pVehicleTargetInside->GetVelocity().XYMag2() < 1.0f * 1.0f && pVehicle->GetVelocity().XYMag2() < 0.25f * 0.25f)
|
|
{
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation obstructionInformation;
|
|
if(!bCanLeaveVehicle && GetObstructionInformation(obstructionInformation))
|
|
{
|
|
//Check if we are completely obstructed.
|
|
if(obstructionInformation.m_uFlags.IsFlagSet(CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsCompletelyObstructed) ||
|
|
obstructionInformation.m_uFlags.IsFlagSet(CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsCompletelyObstructedClose))
|
|
{
|
|
bCanLeaveVehicle = true;
|
|
bNeedLOS = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if we can leave the vehicle.
|
|
if(bCanLeaveVehicle)
|
|
{
|
|
//Get the target position for the emergency stop.
|
|
Vec3V vTargetPosition = GetTargetPositionForEmergencyStop();
|
|
|
|
//Check if we can make an emergency stop.
|
|
CPedTargetting* pPedTargetting = pPed->GetPedIntelligence()->GetTargetting(false);
|
|
bool bIsLosBlocked = (pPedTargetting && (pPedTargetting->GetLosStatus(pPedTarget) == Los_blocked));
|
|
bool bIsWithinZThreshold = (Abs(vTargetPosition.GetZf() - pPed->GetTransform().GetPosition().GetZf()) <= 4.0f);
|
|
bool bCanMakeEmergencyStop = (!bIsLosBlocked || bIsWithinZThreshold || !bNeedLOS);
|
|
if(bCanMakeEmergencyStop)
|
|
{
|
|
//Check if we are in range.
|
|
ScalarV scDistSq = DistSquared(pPed->GetTransform().GetPosition(), vTargetPosition);
|
|
static dev_float s_fMaxDistance = 30.0f;
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(s_fMaxDistance));
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
return State_slowdownSafely;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bPedIsDriver && m_uRunningFlags.IsFlagSet(RF_IsBlockedByLockedDoor))
|
|
{
|
|
GetPed()->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_CanUseVehicles);
|
|
|
|
return State_exitVehicle;
|
|
}
|
|
|
|
// If buddies are currently trying to enter the car, wait for them
|
|
if( bPedIsDriver && !pPed->GetMyVehicle()->GetIsAircraft() )
|
|
{
|
|
if( CountNumberBuddiesEnteringVehicle(pPed, pPed->GetMyVehicle()) > 0 )
|
|
{
|
|
return State_waitForBuddiesToEnter;
|
|
}
|
|
}
|
|
|
|
//come to a stop if there's too many parked police vehicles ahead of us
|
|
if(GetState() == State_approachTarget)
|
|
{
|
|
if(ShouldStopDueToBlockingLawVehicles())
|
|
{
|
|
return State_slowdownSafely;
|
|
}
|
|
}
|
|
|
|
//Check if the current state is valid.
|
|
if(!IsCurrentStateValid())
|
|
{
|
|
//Choose a default state.
|
|
return ChooseDefaultState();
|
|
}
|
|
|
|
return State_invalid;
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ActivateTimeslicing()
|
|
{
|
|
TUNE_GROUP_BOOL(VEHICLE_PURSUIT, ACTIVATE_TIMESLICING, true);
|
|
if(ACTIVATE_TIMESLICING)
|
|
{
|
|
GetPed()->GetPedAiLod().ClearBlockedLodFlag(CPedAILod::AL_LodTimesliceIntelligenceUpdate);
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::AreAbnormalExitsDisabledForSeat() const
|
|
{
|
|
const CVehicleSeatAnimInfo* pVehicleSeatAnimInfo = CVehicleMetadataMgr::GetSeatAnimInfoFromPed(GetPed());
|
|
return (pVehicleSeatAnimInfo && pVehicleSeatAnimInfo->GetDisableAbnormalExits());
|
|
}
|
|
|
|
Vec3V_Out CTaskVehiclePersuit::CalculateDirectionForPed(const CPed& rPed) const
|
|
{
|
|
//Check if the ped is in a vehicle.
|
|
const CVehicle* pVehicle = rPed.GetVehiclePedInside();
|
|
if(pVehicle)
|
|
{
|
|
return pVehicle->GetTransform().GetForward();
|
|
}
|
|
else
|
|
{
|
|
return rPed.GetTransform().GetForward();
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::CanChangeState() const
|
|
{
|
|
//Check the state.
|
|
switch(GetState())
|
|
{
|
|
case State_chaseInCar:
|
|
case State_approachTarget:
|
|
{
|
|
//Ensure we are not stuck.
|
|
if(IsStuck())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::CanExitVehicle() const
|
|
{
|
|
//Ensure the flag is set.
|
|
if(!GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanLeaveVehicle))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if the ped is random, and abnormal exits are disabled for the seat.
|
|
if(GetPed()->PopTypeIsRandom() && AreAbnormalExitsDisabledForSeat())
|
|
{
|
|
//Apparently we want to allow abnormal exits in some cases.
|
|
bool bAllowAbnormalExit = GetPed()->GetIsDrivingVehicle();
|
|
if(!bAllowAbnormalExit)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CTaskVehiclePersuit::VehiclePersuitState CTaskVehiclePersuit::ChooseDefaultState() const
|
|
{
|
|
//Check if we should wait in a car.
|
|
if(ShouldWaitInCar())
|
|
{
|
|
return State_slowdownSafely;
|
|
}
|
|
//Check if we should chase in a car.
|
|
else if(ShouldChaseInCar())
|
|
{
|
|
return State_chaseInCar;
|
|
}
|
|
else
|
|
{
|
|
return State_approachTarget;
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::DoesPedHaveLosToTarget(const CPed& rPed, const CPed& rTarget) const
|
|
{
|
|
//Ensure the targeting is valid.
|
|
CPedTargetting* pPedTargetting = rPed.GetPedIntelligence()->GetTargetting(true);
|
|
if(!pPedTargetting)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Get the Los status.
|
|
if(pPedTargetting->GetLosStatus(&rTarget) != Los_clear)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::FindObstructions(bool& bLeft, bool& bRight)
|
|
{
|
|
//Grab the ped.
|
|
const CPed* pPed = GetPed();
|
|
if(!pPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the vehicle.
|
|
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Calculate the magnitude for the probe.
|
|
float fMagnitude = GetDistToStopAtCurrentSpeed(*pVehicle);
|
|
|
|
//Grab the vehicle position.
|
|
Vec3V vVehiclePosition = pVehicle->GetTransform().GetPosition();
|
|
|
|
//Calculate the left position.
|
|
Vec3V vPosL;
|
|
int iBoneIndexL = pVehicle->GetBoneIndex(VEH_HEADLIGHT_L);
|
|
if(iBoneIndexL >= 0)
|
|
{
|
|
Matrix34 mBoneL;
|
|
pVehicle->GetGlobalMtx(iBoneIndexL, mBoneL);
|
|
vPosL = VECTOR3_TO_VEC3V(mBoneL.d);
|
|
}
|
|
else
|
|
{
|
|
vPosL = vVehiclePosition;
|
|
}
|
|
|
|
//Calculate the right position.
|
|
Vec3V vPosR;
|
|
int iBoneIndexR = pVehicle->GetBoneIndex(VEH_HEADLIGHT_R);
|
|
if(iBoneIndexR >= 0)
|
|
{
|
|
Matrix34 mBoneR;
|
|
pVehicle->GetGlobalMtx(iBoneIndexR, mBoneR);
|
|
vPosR = VECTOR3_TO_VEC3V(mBoneR.d);
|
|
|
|
}
|
|
else
|
|
{
|
|
vPosR = vVehiclePosition;
|
|
}
|
|
|
|
//Check if we are off-road.
|
|
bool bIsOffRoad = ThePaths.IsPositionOffRoadGivenNodelist(pVehicle, vVehiclePosition);
|
|
|
|
//Check for obstructions on the left at the angles.
|
|
bLeft = FindObstructionAtAngle(pVehicle, vPosL, sm_Tunables.m_ObstructionProbeAngleA, fMagnitude, bIsOffRoad);
|
|
if(!bLeft)
|
|
{
|
|
bLeft = FindObstructionAtAngle(pVehicle, vPosL, sm_Tunables.m_ObstructionProbeAngleB, fMagnitude, bIsOffRoad);
|
|
if(!bLeft)
|
|
{
|
|
bLeft = FindObstructionAtAngle(pVehicle, vPosL, sm_Tunables.m_ObstructionProbeAngleC, fMagnitude, bIsOffRoad);
|
|
}
|
|
}
|
|
|
|
//Check for obstructions on the right at the angles.
|
|
bRight = FindObstructionAtAngle(pVehicle, vPosR, -sm_Tunables.m_ObstructionProbeAngleA, fMagnitude, bIsOffRoad);
|
|
if(!bRight)
|
|
{
|
|
bRight = FindObstructionAtAngle(pVehicle, vPosR, -sm_Tunables.m_ObstructionProbeAngleB, fMagnitude, bIsOffRoad);
|
|
if(!bRight)
|
|
{
|
|
bRight = FindObstructionAtAngle(pVehicle, vPosR, -sm_Tunables.m_ObstructionProbeAngleC, fMagnitude, bIsOffRoad);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::FindObstructionAtAngle(const CVehicle* pVehicle, Vec3V_ConstRef vStart, const float fAngle, const float fMagnitude, bool bIsOffRoad)
|
|
{
|
|
//Generate a rotator for the angle.
|
|
QuatV qRotator = QuatVFromAxisAngle(pVehicle->GetTransform().GetC(), ScalarVFromF32(fAngle * DtoR));
|
|
|
|
//Rotate the forward vector about the up axis.
|
|
Vec3V vProbe = Transform(qRotator, pVehicle->GetTransform().GetB());
|
|
|
|
//Scale the rotated vector.
|
|
vProbe = Scale(vProbe, ScalarVFromF32(fMagnitude));
|
|
|
|
//Generate the start and end positions.
|
|
Vec3V vEnd = vStart + vProbe;
|
|
|
|
//Generate the radius.
|
|
float fRadius = pVehicle->GetBaseModelInfo()->GetBoundingSphereRadius();
|
|
|
|
#if !__FINAL
|
|
//Append the obstruction probe points.
|
|
if(m_uNumObstructionPoints < ms_uMaxObstructionProbePoints)
|
|
{
|
|
m_avObstructionProbeStartPoints[m_uNumObstructionPoints] = vStart;
|
|
m_avObstructionProbeEndPoints[m_uNumObstructionPoints] = vEnd;
|
|
m_avObstructionProbeRadii[m_uNumObstructionPoints] = fRadius;
|
|
++m_uNumObstructionPoints;
|
|
}
|
|
#endif
|
|
|
|
//Check if we are not off-road.
|
|
if(!bIsOffRoad)
|
|
{
|
|
//Ensure the end point is not off-road.
|
|
if(ThePaths.IsPositionOffRoadGivenNodelist(pVehicle, vEnd))
|
|
{
|
|
//Consider this direction obstructed.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//Execute a shape test. Check for buildings, vehicles, and peds.
|
|
WorldProbe::CShapeTestCapsuleDesc probeDesc;
|
|
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_BUILDING_INCLUDE_TYPES|ArchetypeFlags::GTA_VEHICLE_TYPE|ArchetypeFlags::GTA_PED_TYPE);
|
|
probeDesc.SetTypeFlags(ArchetypeFlags::GTA_AI_TEST);
|
|
probeDesc.SetExcludeEntity(pVehicle);
|
|
probeDesc.SetCapsule(VEC3V_TO_VECTOR3(vStart), VEC3V_TO_VECTOR3(vEnd), fRadius);
|
|
return WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc);
|
|
}
|
|
|
|
float CTaskVehiclePersuit::GetDistToStopAtCurrentSpeed(const CVehicle& rVehicle) const
|
|
{
|
|
//Generate the distance it will take to stop at the current speed.
|
|
float fDistanceToStop = rVehicle.GetAIHandlingInfo()->GetDistToStopAtCurrentSpeed(rVehicle.GetVelocity().Mag());
|
|
|
|
//HACK: GetDistToStopAtCurrentSpeed appears to be broken and giving values that are way too high.
|
|
// This is likely due to the function assuming a linear decrease in speed and not taking deceleration into account.
|
|
//TODO: Remove specialized tuning values if/when GetDistToStopAtCurrentSpeed is fixed or replaced with a more accurate version.
|
|
|
|
//Apply the distance to stop multiplier.
|
|
fDistanceToStop *= sm_Tunables.m_DistanceToStopMultiplier;
|
|
|
|
//Generate the mass multiplier.
|
|
float fDistanceToStopMassMultiplier = (rVehicle.pHandling->m_fMass / sm_Tunables.m_DistanceToStopMassIdeal);
|
|
|
|
//Lerp the mass multiplier towards 1.0 based on the mass weight.
|
|
fDistanceToStopMassMultiplier = Lerp(sm_Tunables.m_DistanceToStopMassWeight, 1.0f, fDistanceToStopMassMultiplier);
|
|
|
|
//Apply the mass multiplier.
|
|
fDistanceToStop *= fDistanceToStopMassMultiplier;
|
|
|
|
return fDistanceToStop;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::GetObstructionInformation(CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation& rInformation) const
|
|
{
|
|
//Ensure we are in a vehicle.
|
|
CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle task is valid.
|
|
CTaskVehicleMissionBase* pVehicleTask = pVehicle->GetIntelligence()->GetActiveTask();
|
|
if(!pVehicleTask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the avoidance task is valid.
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile* pAvoidanceTask = static_cast<CTaskVehicleGoToPointWithAvoidanceAutomobile *>(pVehicleTask->FindSubTaskOfType(CTaskTypes::TASK_VEHICLE_GOTO_POINT_WITH_AVOIDANCE_AUTOMOBILE));
|
|
if(!pAvoidanceTask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Set the obstruction information.
|
|
rInformation = pAvoidanceTask->GetObstructionInformation();
|
|
|
|
return true;
|
|
}
|
|
|
|
CPed* CTaskVehiclePersuit::GetTargetMount() const
|
|
{
|
|
//Ensure the target ped is valid.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
if(!pTargetPed)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pTargetPed->GetMountPedOn();
|
|
}
|
|
|
|
const CPed* CTaskVehiclePersuit::GetTargetPed() const
|
|
{
|
|
//Grab the target entity.
|
|
const CEntity* pEntity = m_target.GetEntity();
|
|
if(!pEntity)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//Ensure the entity is a ped.
|
|
if(!pEntity->GetIsTypePed())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return static_cast<const CPed *>(pEntity);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::GetTargetPositionForApproach(Vec3V_InOut vTargetPosition) const
|
|
{
|
|
//Ensure the vehicle is valid.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the task is valid.
|
|
CTaskVehicleMissionBase* pTask = pVehicle->GetIntelligence()->GetActiveTask();
|
|
if(!pTask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the task type is valid.
|
|
if(pTask->GetTaskType() != CTaskTypes::TASK_VEHICLE_APPROACH)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Get the target position.
|
|
if(!static_cast<CTaskVehicleApproach *>(pTask)->GetTargetPosition(vTargetPosition))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Vec3V_Out CTaskVehiclePersuit::GetTargetPositionForEmergencyStop() const
|
|
{
|
|
//Keep track of the alternate position.
|
|
Vec3V vAlternatePosition(V_ZERO);
|
|
|
|
//Check the state.
|
|
switch(GetState())
|
|
{
|
|
case State_approachTarget:
|
|
{
|
|
//Get the target position for approach.
|
|
GetTargetPositionForApproach(vAlternatePosition);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Get the target position.
|
|
Vec3V vTargetPosition;
|
|
m_target.GetPosition(RC_VECTOR3(vTargetPosition));
|
|
|
|
//Check if the alternate position is valid.
|
|
if(!IsCloseAll(vAlternatePosition, Vec3V(V_ZERO), ScalarVFromF32(SMALL_FLOAT)))
|
|
{
|
|
//Check which position is closer.
|
|
ScalarV scDistAlternateSq = DistSquared(GetPed()->GetTransform().GetPosition(), vAlternatePosition);
|
|
ScalarV scDistTargetSq = DistSquared(GetPed()->GetTransform().GetPosition(), vTargetPosition);
|
|
if(IsLessThanAll(scDistAlternateSq, scDistTargetSq))
|
|
{
|
|
return vAlternatePosition;
|
|
}
|
|
}
|
|
|
|
return vTargetPosition;
|
|
}
|
|
|
|
CVehicle* CTaskVehiclePersuit::GetTargetVehicle() const
|
|
{
|
|
//Ensure the target ped is valid.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
if(!pTargetPed)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pTargetPed->GetVehiclePedInside();
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::HasMountedGuns() const
|
|
{
|
|
weaponAssert(GetPed()->GetWeaponManager() && GetPed()->GetInventory());
|
|
const bool bHasWeaponsAtSeat = GetPed()->GetMyVehicle()->GetSeatHasWeaponsOrTurrets(GetPed()->GetMyVehicle()->GetSeatManager()->GetPedsSeatIndex(GetPed()));
|
|
return bHasWeaponsAtSeat;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsCollidingWithLockedDoor() const
|
|
{
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!taskVerifyf(pVehicle, "The vehicle is invalid."))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CCollisionRecord* pColRecord = pVehicle->GetFrameCollisionHistory()->GetMostSignificantCollisionRecord();
|
|
if(!pColRecord)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CEntity* pColEntity = pColRecord->m_pRegdCollisionEntity;
|
|
if(!pColEntity || !pColEntity->GetIsTypeObject())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CObject* pColObject = static_cast<const CObject *>(pColEntity);
|
|
if(!pColObject->IsADoor())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CDoor* pColDoor = static_cast<const CDoor *>(pColObject);
|
|
bool bLocked = pColDoor->IsBaseFlagSet(fwEntity::IS_FIXED);
|
|
if(!bLocked)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsCollisionWithLawEnforcementImminent(float fMaxDistance, float fMaxTime, bool& bWithPed, bool& bWithVehicle) const
|
|
{
|
|
//Ensure the obstruction information is valid.
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation obstructionInformation;
|
|
if(!GetObstructionInformation(obstructionInformation))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we have been obstructed by law enforcement.
|
|
bWithPed = obstructionInformation.m_uFlags.IsFlagSet(
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsObstructedByLawEnforcementPed);
|
|
bWithVehicle = obstructionInformation.m_uFlags.IsFlagSet(
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation::IsObstructedByLawEnforcementVehicle);
|
|
if(!bWithPed && !bWithVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the vehicle.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
taskAssert(pVehicle);
|
|
|
|
//Check if the distance is within the threshold.
|
|
float fDistanceToObstruction = obstructionInformation.m_fDistanceToLawEnforcementObstruction;
|
|
if(fDistanceToObstruction < fMaxDistance)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the time is within the threshold.
|
|
float fInvSpeed = 1.0f / pVehicle->GetVelocity().XYMag();
|
|
float fTimeToObstruction = fDistanceToObstruction * fInvSpeed;
|
|
if(fTimeToObstruction < fMaxTime)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsCurrentStateValid() const
|
|
{
|
|
//Check if we can change state.
|
|
if(CanChangeState())
|
|
{
|
|
//Check the state.
|
|
switch(GetState())
|
|
{
|
|
case State_chaseInCar:
|
|
{
|
|
return (!ShouldWaitInCar() && ShouldChaseInCar());
|
|
}
|
|
case State_approachTarget:
|
|
{
|
|
return (!ShouldWaitInCar() && !ShouldChaseInCar());
|
|
}
|
|
case state_waitInCar:
|
|
{
|
|
return (ShouldWaitInCar());
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsInFistFight() const
|
|
{
|
|
//Ensure the target is valid.
|
|
const CPed* pTarget = GetTargetPed();
|
|
if(!pTarget)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target is not armed with a gun.
|
|
if(IsPedArmedWithGun(*pTarget))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is not armed with a gun.
|
|
if(IsPedArmedWithGun(*GetPed()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsPedArmedWithGun(const CPed& rPed) const
|
|
{
|
|
//Ensure the weapon manager is valid.
|
|
const CPedWeaponManager* pWeaponManager = rPed.GetWeaponManager();
|
|
if(!pWeaponManager)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return pWeaponManager->GetIsArmedGun();
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsPedTryingToExitVehicle(const CPed& rPed) const
|
|
{
|
|
//Ensure the ped is running the task.
|
|
const CTask* pTask = rPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VEHICLE_PERSUIT);
|
|
if(!pTask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the state is valid.
|
|
s32 iState = pTask->GetState();
|
|
switch(iState)
|
|
{
|
|
case State_exitVehicle:
|
|
case State_exitVehicleImmediately:
|
|
case State_waitForVehicleExit:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsPedsBestWeaponAGun(const CPed& rPed) const
|
|
{
|
|
//Ensure the weapon manager is valid.
|
|
const CPedWeaponManager* pWeaponManager = rPed.GetWeaponManager();
|
|
if(!pWeaponManager)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the best weapon info is valid (ignore the vehicle check).
|
|
const CWeaponInfo* pWeaponInfo = pWeaponManager->GetBestWeaponInfo(true);
|
|
if(!pWeaponInfo)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the weapon is a gun.
|
|
if(!pWeaponInfo->GetIsGun())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsStuck() const
|
|
{
|
|
//Ensure our vehicle is valid.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return CVehicleStuckDuringCombatHelper::IsStuck(*pVehicle);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsTargetInFrontOfUs() const
|
|
{
|
|
//Ensure the target is valid.
|
|
const CPed* pTarget = GetTargetPed();
|
|
if(!pTarget)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the ped.
|
|
const CPed* pPed = GetPed();
|
|
|
|
//Grab the ped position.
|
|
Vec3V vPedPosition = pPed->GetTransform().GetPosition();
|
|
|
|
//Grab the target position.
|
|
Vec3V vTargetPosition = pTarget->GetTransform().GetPosition();
|
|
|
|
//Generate a vector from the ped to the target.
|
|
Vec3V vPedToTarget = Subtract(vTargetPosition, vPedPosition);
|
|
|
|
//Calculate the direction for the ped.
|
|
Vec3V vPedDirection = CalculateDirectionForPed(*pPed);
|
|
|
|
//Ensure the target is in front of us.
|
|
ScalarV scDot = Dot(vPedToTarget, vPedDirection);
|
|
ScalarV scMinDot = ScalarV(V_ZERO);
|
|
if(IsLessThanAll(scDot, scMinDot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsTargetInVehicle() const
|
|
{
|
|
return (GetTargetVehicle() != NULL);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsTargetInBoat() const
|
|
{
|
|
return GetTargetVehicle() && GetTargetVehicle()->InheritsFromBoat() && GetTargetVehicle()->GetIsInWater();
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsTargetMovingSlowlyInVehicle() const
|
|
{
|
|
//Ensure the target is in a vehicle.
|
|
const CVehicle* pVehicle = GetTargetVehicle();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if the speed is slow.
|
|
float fSpeedSq = pVehicle->GetVelocity().Mag2();
|
|
static dev_float s_fMaxSpeed = 1.75f;
|
|
float fMaxSpeedSq = square(s_fMaxSpeed);
|
|
return (fSpeedSq < fMaxSpeedSq);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsTargetOnMount() const
|
|
{
|
|
return (GetTargetMount() != NULL);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::MustPedBeInVehicle() const
|
|
{
|
|
switch(GetState())
|
|
{
|
|
case State_exitVehicle:
|
|
case State_exitVehicleImmediately:
|
|
case State_provideCoverDuringExitVehicle:
|
|
{
|
|
return false;
|
|
}
|
|
default:
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProcessAudio()
|
|
{
|
|
//Ensure the audio should be processed this frame.
|
|
if(!GetPed()->GetPedIntelligence()->GetAudioDistributer().ShouldBeProcessedThisFrame())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the flag is not set.
|
|
if(GetPed()->GetPedResetFlag(CPED_RESET_FLAG_DisableCombatAudio))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure some time has passed before saying any lines.
|
|
static dev_float s_fMinTime = 2.0f;
|
|
if(GetTimeRunning() < s_fMinTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
|
|
//These are arranged in prioritized order.
|
|
if(GetPed()->IsLawEnforcementPed() && pTargetVehicle)
|
|
{
|
|
//Check if we have not said initial audio.
|
|
if(!m_uRunningFlags.IsFlagSet(RF_HasSaidInitialAudio))
|
|
{
|
|
//Check if we are close enough.
|
|
Vec3V vTargetPosition;
|
|
m_target.GetPosition(RC_VECTOR3(vTargetPosition));
|
|
ScalarV scDistSq = DistSquared(GetPed()->GetTransform().GetPosition(), vTargetPosition);
|
|
static dev_float s_fMaxDistance = 30.0f;
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(s_fMaxDistance));
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
//Set the flag.
|
|
m_uRunningFlags.SetFlag(RF_HasSaidInitialAudio);
|
|
|
|
if(pTargetVehicle->GetVehicleType() == VEHICLE_TYPE_CAR)
|
|
{
|
|
bool isMarkedAsCarByAudio = true;
|
|
if(pTargetVehicle->GetVehicleAudioEntity())
|
|
{
|
|
audCarAudioEntity* carAudEnt = static_cast<audCarAudioEntity*>(pTargetVehicle->GetVehicleAudioEntity());
|
|
if(carAudEnt && carAudEnt->GetCarAudioSettings())
|
|
{
|
|
isMarkedAsCarByAudio = (AUD_GET_TRISTATE_VALUE(carAudEnt->GetCarAudioSettings()->Flags, FLAG_ID_CARAUDIOSETTINGS_IAMNOTACAR) != AUD_TRISTATE_TRUE);
|
|
}
|
|
}
|
|
if(isMarkedAsCarByAudio)
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_CAR_MEGAPHONE");
|
|
}
|
|
else
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_GENERIC_MEGAPHONE");
|
|
}
|
|
}
|
|
else if(pTargetVehicle->InheritsFromBoat())
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_BOAT_MEGAPHONE");
|
|
}
|
|
else if(pTargetVehicle->GetVehicleType() != VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_GENERIC_MEGAPHONE");
|
|
}
|
|
}
|
|
}
|
|
//Check if we have not said follow-up audio.
|
|
else if(!m_uRunningFlags.IsFlagSet(RF_HasSaidFollowUpAudio))
|
|
{
|
|
//Set the flag.
|
|
m_uRunningFlags.SetFlag(RF_HasSaidFollowUpAudio);
|
|
|
|
if(pTargetVehicle->GetVehicleType() == VEHICLE_TYPE_CAR)
|
|
{
|
|
bool isMarkedAsCarByAudio = true;
|
|
if(pTargetVehicle->GetVehicleAudioEntity())
|
|
{
|
|
audCarAudioEntity* carAudEnt = static_cast<audCarAudioEntity*>(pTargetVehicle->GetVehicleAudioEntity());
|
|
if(carAudEnt && carAudEnt->GetCarAudioSettings())
|
|
{
|
|
isMarkedAsCarByAudio = (AUD_GET_TRISTATE_VALUE(carAudEnt->GetCarAudioSettings()->Flags, FLAG_ID_CARAUDIOSETTINGS_IAMNOTACAR) != AUD_TRISTATE_TRUE);
|
|
}
|
|
}
|
|
if(isMarkedAsCarByAudio)
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_CAR_WARNING_MEGAPHONE");
|
|
}
|
|
else
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_GENERIC_WARNING_MEGAPHONE");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetPed()->NewSay("STOP_VEHICLE_GENERIC_WARNING_MEGAPHONE");
|
|
}
|
|
}
|
|
else if(pTargetVehicle->GetVehicleType() != VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
GetPed()->NewSay("CHASE_VEHICLE_MEGAPHONE");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetPed()->NewSay("VEHICLE_CHASE");
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProcessBlockedByLockedDoor()
|
|
{
|
|
if(!GetPed()->PopTypeIsRandom())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!GetPed()->GetIsDrivingVehicle())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_uRunningFlags.ClearFlag(RF_IsBlockedByLockedDoor);
|
|
|
|
if(m_uTimeCollidedWithLockedDoor > 0)
|
|
{
|
|
ScalarV scDistSq = DistSquared(GetPed()->GetTransform().GetPosition(), m_vPositionWhenCollidedWithLockedDoor);
|
|
static dev_float s_fMinDistance = 10.0f;
|
|
ScalarV scMinDistSq = ScalarVFromF32(square(s_fMinDistance));
|
|
if(IsGreaterThanAll(scDistSq, scMinDistSq))
|
|
{
|
|
m_vPositionWhenCollidedWithLockedDoor.ZeroComponents();
|
|
m_uTimeCollidedWithLockedDoor = 0;
|
|
}
|
|
else
|
|
{
|
|
static dev_u32 s_uMinTime = 15000;
|
|
if(CTimeHelpers::GetTimeSince(m_uTimeCollidedWithLockedDoor) > s_uMinTime)
|
|
{
|
|
m_uRunningFlags.SetFlag(RF_IsBlockedByLockedDoor);
|
|
}
|
|
}
|
|
}
|
|
else if(IsCollidingWithLockedDoor())
|
|
{
|
|
m_vPositionWhenCollidedWithLockedDoor = GetPed()->GetTransform().GetPosition();
|
|
m_uTimeCollidedWithLockedDoor = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
m_vPositionWhenCollidedWithLockedDoor.ZeroComponents();
|
|
m_uTimeCollidedWithLockedDoor = 0;
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::ProcessTimers()
|
|
{
|
|
//Check if the target is moving slowly in a vehicle.
|
|
if(IsTargetMovingSlowlyInVehicle())
|
|
{
|
|
m_fTimeTargetMovingSlowlyInVehicle += GetTimeStep();
|
|
}
|
|
else
|
|
{
|
|
m_fTimeTargetMovingSlowlyInVehicle = 0.0f;
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldAnyoneProvideCoverDuringExitVehicle() const
|
|
{
|
|
//Ensure the ped is valid.
|
|
const CPed* pPed = GetPed();
|
|
if(!pPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle is valid.
|
|
const CVehicle* pVehicle = pPed->GetMyVehicle();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the seat manager is valid.
|
|
const CSeatManager* pSeatManager = pVehicle->GetSeatManager();
|
|
if(!pSeatManager)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if any of the peds will provide cover.
|
|
for(s32 i = 0; i < pSeatManager->GetMaxSeats(); ++i)
|
|
{
|
|
const CPed* pPassenger = pSeatManager->GetPedInSeat(i);
|
|
if(!pPassenger)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Check if the passenger will provide cover.
|
|
fwMvClipSetId clipSetId;
|
|
if(ShouldPedProvideCoverDuringExitVehicle(*pPassenger, clipSetId))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldWaitWhileCruising() const
|
|
{
|
|
TUNE_GROUP_BOOL(VEHICLE_PURSUIT, ALWAYS_ALLOW_CRUISE_WAITING, false);
|
|
if(!ALWAYS_ALLOW_CRUISE_WAITING && !GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CruiseAndBlockInVehicle))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure us and target are in a vehicle
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
if(!pVehicle || !pTargetVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//ensure we are in front of target and facing same direction as them
|
|
if(HasTargetPassedUs() || !FacingSameDirectionAsTarget())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(pTargetVehicle->GetVelocity().Mag2() < 100.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure outside distance
|
|
static dev_float s_fMaxDistance = 45.0f;
|
|
ScalarV scDistSq = DistSquared(pVehicle->GetTransform().GetPosition(), pTargetVehicle->GetTransform().GetPosition());
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(s_fMaxDistance));
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq) || WillTargetVehicleOvertakeUsWithinTime(1.0f))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// //Check if the player is not on a highway.
|
|
// if(!pPed->GetPlayerInfo()->GetPlayerDataPlayerOnHighway())
|
|
// {
|
|
// //Ensure no one has the order.
|
|
// if(DoesAnyoneHaveOrder(CPoliceOrder::PO_WANTED_WAIT_CRUISING_AS_DRIVER, *pIncident))
|
|
// {
|
|
// return false;
|
|
// }
|
|
// }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldAttack() const
|
|
{
|
|
//Check if the flag is set.
|
|
if(GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_UseVehicleAttack))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the conditional flag is set.
|
|
if(GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_UseVehicleAttackIfVehicleHasMountedGuns))
|
|
{
|
|
//Check if the vehicle has mounted guns.
|
|
if(HasMountedGuns())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//Check if the vehicle is a plane.
|
|
CVehicle* pMyVehicle = GetPed()->GetMyVehicle();
|
|
if(pMyVehicle->InheritsFromPlane() && !pMyVehicle->HasContactWheels())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the vehicle is a tank.
|
|
if(pMyVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_IS_TANK))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldChaseInCar() const
|
|
{
|
|
//Ensure we are driving.
|
|
if(!GetPed()->GetIsDrivingVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if the target is in a vehicle.
|
|
const CVehicle* pTargetVehicle = GetTargetVehicle();
|
|
if(pTargetVehicle)
|
|
{
|
|
//Check if the target is in water.
|
|
if(CTargetInWaterHelper::IsInWater(*pTargetVehicle))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if the target has been moving slowly.
|
|
float fSmoothedSpeedSq = pTargetVehicle->GetIntelligence()->GetSmoothedSpeedSq();
|
|
static dev_float s_fMaxSmoothedSpeed = 1.0f;
|
|
bool bIsTargetMovingSlowly = (fSmoothedSpeedSq < square(s_fMaxSmoothedSpeed));
|
|
if(!bIsTargetMovingSlowly)
|
|
{
|
|
static dev_float s_fMinTimeMovingSlowly = 1.0f;
|
|
bIsTargetMovingSlowly = (m_fTimeTargetMovingSlowlyInVehicle > s_fMinTimeMovingSlowly);
|
|
}
|
|
if(bIsTargetMovingSlowly)
|
|
{
|
|
//Check the target is not moving quickly.
|
|
float fSpeedSq = pTargetVehicle->GetVelocity().XYMag2();
|
|
static dev_float s_fMaxSpeed = 4.0f;
|
|
if(fSpeedSq < square(s_fMaxSpeed))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//Check if the target is on a mount.
|
|
else if(IsTargetOnMount())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldCloseDoor() const
|
|
{
|
|
//Check if the target is in front of us, and we won't have a gun after exiting the vehicle.
|
|
if(IsTargetInFrontOfUs() && !WillHaveGunAfterExitingVehicle())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldEquipBestWeaponAfterExitingVehicle() const
|
|
{
|
|
//Grab the ped.
|
|
const CPed* pPed = GetPed();
|
|
|
|
//Check if the ped is random, not law-enforcement, and is in a fist fight.
|
|
if(pPed->PopTypeIsRandom() && !pPed->IsLawEnforcementPed() && IsInFistFight())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldExitBoat() const
|
|
{
|
|
//Ensure we can exit the vehicle.
|
|
if(!CanExitVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the vehicle.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle is a boat.
|
|
if(!pVehicle->InheritsFromBoat())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the boat.
|
|
const CBoat* pBoat = static_cast<const CBoat *>(pVehicle);
|
|
|
|
//Check if the time that the boat has been out of water exceeds the threshold.
|
|
float fTimeOutOfWater = pBoat->m_BoatHandling.GetOutOfWaterTime();
|
|
float fMinTimeOutOfWaterForExit = sm_Tunables.m_MinTimeBoatOutOfWaterForExit;
|
|
if(fTimeOutOfWater >= fMinTimeOutOfWaterForExit)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the target is standing on our boat.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
if(pTargetPed && (pTargetPed->GetGroundPhysical() == pBoat))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldExitVehicleToAim() const
|
|
{
|
|
//Ensure the parent task is valid.
|
|
const CTask* pTask = GetParent();
|
|
if(!pTask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we are in combat.
|
|
if(pTask->GetTaskType() != CTaskTypes::TASK_COMBAT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we should equip our best weapon after exiting the vehicle.
|
|
//Exiting the vehicle to aim will automatically equip our best weapon.
|
|
if(!ShouldEquipBestWeaponAfterExitingVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldJumpOutOfVehicle() const
|
|
{
|
|
//Grab the vehicle.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure our speed has exceeded the threshold.
|
|
float fMinSpeedToJumpOutOfVehicle = sm_Tunables.m_MinSpeedToJumpOutOfVehicle;
|
|
float fMinSpeedToJumpOutOfVehicleSq = square(fMinSpeedToJumpOutOfVehicle);
|
|
float fSpeedSq = pVehicle->GetVelocity().Mag2();
|
|
if(fSpeedSq < fMinSpeedToJumpOutOfVehicleSq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldMakeEmergencyStopDueToAvoidanceArea() const
|
|
{
|
|
//Ensure the ped is valid.
|
|
const CPed* pPed = GetPed();
|
|
if(!pPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is in a vehicle.
|
|
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is driving.
|
|
if(!pVehicle->IsDriver(pPed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the position.
|
|
Vec3V vPosition = pVehicle->GetTransform().GetPosition();
|
|
|
|
//Grab the velocity.
|
|
Vec3V vVelocity = RCC_VEC3V(pVehicle->GetVelocity());
|
|
|
|
//Grab the velocity direction.
|
|
Vec3V vVelocityDirection = NormalizeFastSafe(vVelocity, Vec3V(V_ZERO));
|
|
|
|
//Calculate the distance it will take for the vehicle to stop.
|
|
ScalarV scDistanceToStop = ScalarVFromF32(GetDistToStopAtCurrentSpeed(*pVehicle));
|
|
|
|
//Add the distance to the position in the direction of the velocity.
|
|
vPosition = AddScaled(vPosition, vVelocityDirection, scDistanceToStop);
|
|
|
|
//Check if the point is in the vehicle avoidance areas.
|
|
return CVehicleCombatAvoidanceArea::IsPointInAreas(vPosition);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldPedProvideCoverDuringExitVehicle(const CPed& rPed, fwMvClipSetId& clipSetId) const
|
|
{
|
|
//Ensure the ped is not injured.
|
|
if(rPed.IsInjured())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the conditional clip set.
|
|
const CVehicleClipRequestHelper* pClipRequestHelper = CTaskVehicleFSM::GetVehicleClipRequestHelper(&rPed);
|
|
const CConditionalClipSet* pClipSet = pClipRequestHelper ? pClipRequestHelper->GetConditionalClipSet() : NULL;
|
|
if(!pClipSet)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the clipset is providing cover.
|
|
if(!pClipSet->GetIsProvidingCover())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Assign the clip set id.
|
|
clipSetId = pClipSet->GetClipSetId();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldWaitInCar() const
|
|
{
|
|
//Ensure the target is valid.
|
|
if(!m_target.GetIsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target vehicle is valid.
|
|
const CVehicle* pVehicle = GetTargetVehicle();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we are driving.
|
|
if(!GetPed()->GetIsDrivingVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Get the target position.
|
|
Vec3V vTargetPosition;
|
|
m_target.GetPosition(RC_VECTOR3(vTargetPosition));
|
|
|
|
//Ensure we are close.
|
|
ScalarV scDistSq = DistSquared(GetPed()->GetTransform().GetPosition(), vTargetPosition);
|
|
static dev_float s_fMaxDistance = 25.0f;
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(s_fMaxDistance));
|
|
if(IsGreaterThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if we are not waiting.
|
|
if(GetState() != state_waitInCar)
|
|
{
|
|
//Ensure the speed is slow.
|
|
float fSpeedSq = pVehicle->GetVelocity().Mag2();
|
|
static dev_float s_fMaxSpeed = 1.0f;
|
|
float fMaxSpeedSq = square(s_fMaxSpeed);
|
|
if(fSpeedSq > fMaxSpeedSq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//Ensure the speed is slow.
|
|
float fSpeedSq = pVehicle->GetVelocity().Mag2();
|
|
static dev_float s_fMaxSpeed = 3.0f;
|
|
float fMaxSpeedSq = square(s_fMaxSpeed);
|
|
if(fSpeedSq > fMaxSpeedSq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target hasn't deviated too much from their original position.
|
|
ScalarV scTargetDeviationSq = DistSquared(vTargetPosition, m_vTargetPositionWhenWaitBegan);
|
|
static dev_float s_fMaxTargetDeviation = 7.5f;
|
|
ScalarV scMaxTargetDeviationSq = ScalarVFromF32(square(s_fMaxTargetDeviation));
|
|
if(IsGreaterThanAll(scTargetDeviationSq, scMaxTargetDeviationSq))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void CTaskVehiclePersuit::UpdateVehicleTaskForApproachTarget()
|
|
{
|
|
//Grab the ped.
|
|
CPed* pPed = GetPed();
|
|
|
|
//Ensure we are in a vehicle.
|
|
CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure we are driving the vehicle.
|
|
if(!pPed->GetIsDrivingVehicle())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the vehicle task is valid.
|
|
CTaskVehicleMissionBase* pVehicleTask = pVehicle->GetIntelligence()->GetActiveTask();
|
|
if(!pVehicleTask)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//just come to a stop and get out if we're trying to do a Uturn on a narrow road as big vehicle
|
|
const bool bOnSmallRoad = pVehicle->GetIntelligence()->IsOnSmallRoad();
|
|
const bool bBigVehicle = pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG);
|
|
const bool bDoingThreePointTurn = pVehicleTask->FindSubTaskOfType(CTaskTypes::TASK_VEHICLE_THREE_POINT_TURN) != NULL;
|
|
if(bOnSmallRoad && bBigVehicle && bDoingThreePointTurn)
|
|
{
|
|
SetState(State_slowdownSafely);
|
|
return;
|
|
}
|
|
|
|
//Keep track of the obstruction information.
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::ObstructionInformation obstructionInformation;
|
|
|
|
//Find the avoidance task.
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile* pAvoidanceTask = static_cast<CTaskVehicleGoToPointWithAvoidanceAutomobile *>(pVehicleTask->FindSubTaskOfType(CTaskTypes::TASK_VEHICLE_GOTO_POINT_WITH_AVOIDANCE_AUTOMOBILE));
|
|
if(pAvoidanceTask)
|
|
{
|
|
//Copy the obstruction information.
|
|
obstructionInformation = pAvoidanceTask->GetObstructionInformation();
|
|
|
|
//Set the avoidance margin for other law enforcement vehicles.
|
|
//This was added to allow law enforcement vehicles to space themselves out.
|
|
pAvoidanceTask->SetAvoidanceMarginForOtherLawEnforcementVehicles(sm_Tunables.m_AvoidanceMarginForOtherLawEnforcementVehicles);
|
|
}
|
|
|
|
//Calculate the cruise speed.
|
|
const float fInitialCruiseSpeed = sm_Tunables.m_ApproachTarget.m_CruiseSpeed;
|
|
float fCruiseSpeed = fInitialCruiseSpeed;
|
|
|
|
//Check if a collision with a law enforcement vehicle is imminent.
|
|
static float s_fMinDistanceToSlowDown = 10.0f;
|
|
static float s_fMaxDistanceToSlowDown = 20.0f;
|
|
float fDistanceToSlowDown = pPed->GetRandomNumberInRangeFromSeed(s_fMinDistanceToSlowDown, s_fMaxDistanceToSlowDown);
|
|
static float s_fMinTimeToSlowDown = 3.0f;
|
|
static float s_fMaxTimeToSlowDown = 5.0f;
|
|
float fTimeToSlowDown = pPed->GetRandomNumberInRangeFromSeed(s_fMinTimeToSlowDown, s_fMaxTimeToSlowDown);
|
|
bool bWillCollideWithPed = false;
|
|
bool bWillCollideWithVehicle = false;
|
|
if(IsCollisionWithLawEnforcementImminent(fDistanceToSlowDown, fTimeToSlowDown, bWillCollideWithPed, bWillCollideWithVehicle))
|
|
{
|
|
if(bWillCollideWithPed)
|
|
{
|
|
fCruiseSpeed = Min(fCruiseSpeed, sm_Tunables.m_ApproachTarget.m_CruiseSpeedWhenObstructedByLawEnforcementPed);
|
|
}
|
|
|
|
if(bWillCollideWithVehicle)
|
|
{
|
|
fCruiseSpeed = Min(fCruiseSpeed, sm_Tunables.m_ApproachTarget.m_CruiseSpeedWhenObstructedByLawEnforcementVehicle);
|
|
}
|
|
}
|
|
|
|
if(fCruiseSpeed == fInitialCruiseSpeed )
|
|
{
|
|
//Grab the ped position.
|
|
Vec3V vPedPosition = pPed->GetTransform().GetPosition();
|
|
|
|
//Grab the target position.
|
|
Vec3V vTargetPosition = GetTargetPositionForEmergencyStop();
|
|
|
|
//Check if the distance has exceeded the threshold.
|
|
ScalarV scDistSq = DistSquared(vPedPosition, vTargetPosition);
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(sm_Tunables.m_ApproachTarget.m_MaxDistanceToConsiderClose));
|
|
if(IsLessThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
//Update the cruise speed.
|
|
fCruiseSpeed = Min(fCruiseSpeed, sm_Tunables.m_ApproachTarget.m_CruiseSpeedWhenClose);
|
|
|
|
//slow vehicles down more if there's already a large number of vehicles ahead of us
|
|
s32 iNumVehicles = 0;
|
|
s32 iNumPeds = 0;
|
|
CountNumberVehiclesAndPedsNearTarget(iNumVehicles, iNumPeds);
|
|
if(iNumVehicles >= sm_Tunables.m_ApproachTarget.m_MaxNumberVehiclesNearTarget ||
|
|
iNumPeds >= sm_Tunables.m_ApproachTarget.m_MaxNumberPedsNearTarget)
|
|
{
|
|
fCruiseSpeed = Min(fCruiseSpeed, sm_Tunables.m_ApproachTarget.m_CruiseSpeedTooManyNearbyEntities);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Massive hack to handle the creek situation in GTA V.
|
|
const CPed* pTargetPed = GetTargetPed();
|
|
if(pTargetPed && CTargetInWaterHelper::IsInWater(*pTargetPed) &&
|
|
CCreekHackHelperGTAV::IsAffected(*pVehicle))
|
|
{
|
|
static dev_float s_fMaxSpeed = 8.0f;
|
|
fCruiseSpeed = Min(fCruiseSpeed, s_fMaxSpeed);
|
|
}
|
|
|
|
//Set the cruise speed.
|
|
pVehicleTask->SetCruiseSpeed(fCruiseSpeed);
|
|
|
|
//Check if we can exit the vehicle.
|
|
bool bCanExitVehicle = CanExitVehicle();
|
|
pVehicleTask->SetDrivingFlag(DF_DontTerminateTaskWhenAchieved, !bCanExitVehicle);
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::ShouldStopDueToBlockingLawVehicles() const
|
|
{
|
|
//Ensure we are in a vehicle.
|
|
const CVehicle* pVehicle = GetPed()->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vec3V vPedPosition = GetPed()->GetTransform().GetPosition();
|
|
ScalarV fMaxDistSqr(sm_Tunables.m_ApproachTarget.m_DistanceToConsiderEntityBlocking*sm_Tunables.m_ApproachTarget.m_DistanceToConsiderEntityBlocking);
|
|
Vec3V vUsToTarget = Normalize(GetTargetPed()->GetTransform().GetPosition() - vPedPosition );
|
|
|
|
s32 iNumVehiclesBlocking = 0;
|
|
CEntityScannerIterator vehicleList = pVehicle->GetIntelligence()->GetVehicleScanner().GetIterator();
|
|
for( CEntity* pEntity = vehicleList.GetFirst(); pEntity; pEntity = vehicleList.GetNext() )
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
if(pVehicle && pVehicle->IsLawEnforcementCar() && pVehicle->GetVelocity().Mag2() < 1.0f)
|
|
{
|
|
Vec3V vToOTherPosition = pEntity->GetTransform().GetPosition() - vPedPosition;
|
|
ScalarV fDist2ToVehicle = MagSquared(vToOTherPosition);
|
|
if(IsLessThanAll(fDist2ToVehicle, fMaxDistSqr))
|
|
{
|
|
Vec3V vToOtherVehicle = Normalize(vToOTherPosition);
|
|
if(IsGreaterThanAll(Dot(vToOtherVehicle, vUsToTarget), ScalarV(0.75f)))
|
|
{
|
|
++iNumVehiclesBlocking;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (iNumVehiclesBlocking >= 2);
|
|
}
|
|
|
|
void CTaskVehiclePersuit::CountNumberVehiclesAndPedsNearTarget(s32& iNumVehicles, s32& iNumPeds) const
|
|
{
|
|
Vec3V vTargetPosition = GetTargetPositionForEmergencyStop();
|
|
ScalarV fMaxDistSqr(sm_Tunables.m_ApproachTarget.m_DistanceToConsiderCloseEntitiesTarget*sm_Tunables.m_ApproachTarget.m_DistanceToConsiderCloseEntitiesTarget);
|
|
|
|
iNumVehicles = 0;
|
|
CEntityScannerIterator vehicleList = GetTargetPed()->GetPedIntelligence()->GetNearbyVehicles();
|
|
for( CEntity* pEntity = vehicleList.GetFirst(); pEntity; pEntity = vehicleList.GetNext() )
|
|
{
|
|
ScalarV fDist2ToVehicle = DistSquared(vTargetPosition, pEntity->GetTransform().GetPosition());
|
|
if(IsLessThanAll(fDist2ToVehicle, fMaxDistSqr))
|
|
{
|
|
++iNumVehicles;
|
|
}
|
|
}
|
|
|
|
iNumPeds = 0;
|
|
CEntityScannerIterator pedList = GetTargetPed()->GetPedIntelligence()->GetNearbyPeds();
|
|
for( CEntity* pEntity = pedList.GetFirst(); pEntity; pEntity = pedList.GetNext() )
|
|
{
|
|
ScalarV fDist2ToPed = DistSquared(vTargetPosition, pEntity->GetTransform().GetPosition());
|
|
if(IsLessThanAll(fDist2ToPed, fMaxDistSqr))
|
|
{
|
|
++iNumPeds;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::WillHaveGunAfterExitingVehicle() const
|
|
{
|
|
//Grab the ped.
|
|
const CPed* pPed = GetPed();
|
|
|
|
//Check if the ped is armed with a gun.
|
|
if(IsPedArmedWithGun(*pPed))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if we will equip a gun after exiting the vehicle.
|
|
if(ShouldEquipBestWeaponAfterExitingVehicle() && IsPedsBestWeaponAGun(*pPed))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehiclePersuit::IsPedOnMovingVehicleOrTrain(const CPed* pPed)
|
|
{
|
|
if(!pPed || pPed->GetIsInVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vec3V vVelocity;
|
|
CEntity* pTargetAttachedEntity = static_cast<CEntity*>(pPed->GetAttachParent());
|
|
CPhysical* pTargetGroundPhysical = pPed->GetGroundPhysical();
|
|
|
|
if( pTargetGroundPhysical && pTargetGroundPhysical->GetIsTypeVehicle() )
|
|
{
|
|
vVelocity = VECTOR3_TO_VEC3V(pTargetGroundPhysical->GetVelocity());
|
|
}
|
|
else if( pTargetAttachedEntity && pTargetAttachedEntity->GetIsTypeVehicle() &&
|
|
static_cast<CVehicle*>(pTargetAttachedEntity)->InheritsFromTrain() )
|
|
{
|
|
CTrain* pTrain = static_cast<CTrain*>(pTargetAttachedEntity);
|
|
vVelocity = VECTOR3_TO_VEC3V(pTrain->GetVelocity());
|
|
}
|
|
else if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_RidingTrain))
|
|
{
|
|
vVelocity = VECTOR3_TO_VEC3V(pPed->GetVelocity());
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ScalarV scMinTrainSpeedSq = LoadScalar32IntoScalarV(sm_Tunables.m_MinTargetStandingOnTrainSpeed);
|
|
scMinTrainSpeedSq = (scMinTrainSpeedSq * scMinTrainSpeedSq);
|
|
|
|
return IsGreaterThanAll(MagSquared(vVelocity), scMinTrainSpeedSq) != 0;
|
|
}
|
|
|
|
#if !__FINAL
|
|
|
|
void CTaskVehiclePersuit::Debug() const
|
|
{
|
|
#if DEBUG_DRAW
|
|
//Call the base class version.
|
|
CTask::Debug();
|
|
|
|
//Check if the vehicle is making an emergency stop.
|
|
if(GetState() == State_emergencyStop)
|
|
{
|
|
//Draw the obstruction capsules.
|
|
for(u32 i = 0; i < m_uNumObstructionPoints; ++i)
|
|
{
|
|
grcDebugDraw::Capsule(m_avObstructionProbeStartPoints[i], m_avObstructionProbeEndPoints[i], m_avObstructionProbeRadii[i], Color_red, false);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const char * CTaskVehiclePersuit::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_start&&iState<=State_exit);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_start",
|
|
"State_fleeFromTarget",
|
|
"State_WaitWhileCruising",
|
|
"State_attack",
|
|
"State_chaseInCar",
|
|
"State_followInCar",
|
|
"State_heliCombat",
|
|
"State_subCombat",
|
|
"State_boatCombat",
|
|
"State_passengerInVehicle",
|
|
"State_waitForBuddiesToEnter",
|
|
"State_slowdownSafely",
|
|
"State_emergencyStop",
|
|
"State_exitVehicle",
|
|
"State_exitVehicleImmediately",
|
|
"State_provideCoverDuringExitVehicle",
|
|
"State_approachTarget",
|
|
"State_waitForVehicleExit",
|
|
"State_waitInCar",
|
|
"State_exit"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
|
|
#endif // !__FINAL
|
|
|
|
void CTaskVehiclePersuit::SetTarget( CAITarget& val )
|
|
{
|
|
// If the target has changed, set the flag so the task can be restarted.
|
|
if( val != m_target )
|
|
m_uRunningFlags.SetFlag(RF_TargetChanged);
|
|
|
|
m_target = val;
|
|
|
|
taskFatalAssertf(m_target.GetEntity() != NULL, "CTaskVehiclePersuit requires a valid target entity!");
|
|
taskFatalAssertf(m_target.GetEntity()->GetIsPhysical(), "CTaskVehiclePersuit requires a valid PHYSICAL target entity!");
|
|
|
|
// If the subtask is vehicle combat, pass the target through
|
|
if( GetSubTask() && GetSubTask()->GetTaskType() == CTaskTypes::TASK_VEHICLE_COMBAT)
|
|
{
|
|
static_cast<CTaskVehicleCombat*>(GetSubTask())->SetTarget(m_target);
|
|
}
|
|
}
|
|
|
|
u32 CTaskVehicleCombat::ms_uNextShootOutTireTimeGlobal = 0;
|
|
eWantedLevel CTaskVehicleCombat::ms_TargetDriveByWantedLevel = WANTED_LEVEL4;
|
|
CTaskVehicleCombat::Tunables CTaskVehicleCombat::ms_Tunables;
|
|
|
|
IMPLEMENT_COMBAT_TASK_TUNABLES(CTaskVehicleCombat, 0xfa437d84);
|
|
|
|
CTaskVehicleCombat::CTaskVehicleCombat( const CAITarget* pTarget, u32 uFlags, float fFireTolerance )
|
|
: m_flags(uFlags)
|
|
, m_uNextShootOutTireTime(0)
|
|
, m_uWeaponPrepareTime(0)
|
|
, m_uRequestWaitForClearShotTime(0)
|
|
, m_fFireTolerance(fFireTolerance)
|
|
{
|
|
if(pTarget)
|
|
{
|
|
m_target = (*pTarget);
|
|
}
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_COMBAT);
|
|
}
|
|
|
|
CTaskVehicleCombat::~CTaskVehicleCombat()
|
|
{
|
|
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::ProcessPreFSM()
|
|
{
|
|
if (!m_target.GetIsValid() && !m_flags.IsFlagSet(Flag_useCamera))
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
FSM_Begin
|
|
// Initial state
|
|
FSM_State(State_start)
|
|
FSM_OnUpdate
|
|
Start_OnUpdate(pPed);
|
|
|
|
// Waiting for clear LOS to the target.
|
|
FSM_State(State_waitForClearShot)
|
|
FSM_OnEnter
|
|
WaitForClearShot_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return WaitForClearShot_OnUpdate(pPed);
|
|
|
|
// Firing a mounted weapon.
|
|
FSM_State(State_mountedWeapon)
|
|
FSM_OnEnter
|
|
MountedWeapon_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return MountedWeapon_OnUpdate(pPed);
|
|
|
|
// Firing a standard weapon from a vehicle
|
|
FSM_State(State_vehicleGun)
|
|
FSM_OnEnter
|
|
VehicleGun_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return VehicleGun_OnUpdate(pPed);
|
|
|
|
// Throwing a projectile
|
|
FSM_State(State_projectile)
|
|
FSM_OnEnter
|
|
Projectile_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return Projectile_OnUpdate(pPed);
|
|
|
|
// Shooting out a tire
|
|
FSM_State(State_shootOutTire)
|
|
FSM_OnEnter
|
|
ShootOutTire_OnEnter(pPed);
|
|
FSM_OnUpdate
|
|
return ShootOutTire_OnUpdate(pPed);
|
|
|
|
// Exit state
|
|
FSM_State(State_exit)
|
|
FSM_OnUpdate
|
|
FSM_End
|
|
}
|
|
|
|
#if !__FINAL
|
|
const char * CTaskVehicleCombat::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_start&&iState<=State_exit);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_start",
|
|
"State_waitForClearShot",
|
|
"State_mountedWeapon",
|
|
"State_vehicleGun",
|
|
"State_projectile",
|
|
"State_shootOutTire",
|
|
"State_exit"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif //_FINAL
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::Start_OnUpdate( CPed* pPed )
|
|
{
|
|
//Check if the next shoot out tire time global has not been initialized.
|
|
if(ms_uNextShootOutTireTimeGlobal == 0)
|
|
{
|
|
//Generate the next shoot out tire time global.
|
|
ms_uNextShootOutTireTimeGlobal = CTaskVehicleCombat::GenerateNextShootOutTireTimeGlobal();
|
|
}
|
|
|
|
//Generate the next shoot out tire time.
|
|
m_uNextShootOutTireTime = GenerateNextShootOutTireTime();
|
|
|
|
//Add a generic delay before ped can start attacking (B* 1386704)
|
|
if (!pPed->PopTypeIsMission())
|
|
m_uWeaponPrepareTime = fwTimer::GetTimeInMilliseconds() + (u32)((fwRandom::GetRandomNumberInRange(ms_Tunables.m_MinTimeToPrepareWeapon, ms_Tunables.m_MaxTimeToPrepareWeapon) * 1000.0f));
|
|
|
|
InitialiseTargetingSystems(pPed);
|
|
SetState(State_waitForClearShot);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::WaitForClearShot_OnEnter( CPed* pPed )
|
|
{
|
|
CVehicleWeapon* pMountedWeapon = pPed->GetWeaponManager()->GetEquippedVehicleWeapon();
|
|
if(pMountedWeapon && pMountedWeapon->GetType() == VGT_RADAR)
|
|
{
|
|
u32 uWeaponHash = pPed->GetWeaponManager()->GetWeaponSelector()->GetBestPedWeapon(pPed, CWeaponInfoManager::GetSlotListBest(), false);
|
|
if(uWeaponHash)
|
|
{
|
|
pPed->GetWeaponManager()->EquipWeapon(uWeaponHash, -1, true);
|
|
}
|
|
}
|
|
|
|
// Reset the target changed flag, this task is reverted to during a target switch
|
|
m_flags.ClearFlag(Flag_targetChanged);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::WaitForClearShot_OnUpdate( CPed* pPed )
|
|
{
|
|
// Reset the target changed flag, this task is reverted to during a target switch
|
|
m_flags.ClearFlag(Flag_targetChanged);
|
|
|
|
// Check the desired state, switching states if its changed
|
|
VehicleCombatState firingState = GetDesiredState(pPed);
|
|
if( firingState != GetState() )
|
|
{
|
|
if (firingState == State_vehicleGun || firingState == State_mountedWeapon)
|
|
{ //Adding some delays to control task oscillations, B* 738133
|
|
static dev_float suMinAimTimeBeforeShootingAgain = 1.0f;
|
|
if (GetPreviousState() != State_start && GetTimeInState() < suMinAimTimeBeforeShootingAgain)
|
|
return FSM_Continue;
|
|
}
|
|
SetState(firingState);
|
|
return FSM_Continue;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::MountedWeapon_OnEnter( CPed* pPed )
|
|
{
|
|
CTaskVehicleMountedWeapon::eTaskMode mode = CTaskVehicleMountedWeapon::Mode_Fire;
|
|
if(m_flags.IsFlagSet(Flag_useCamera))
|
|
{
|
|
mode = CTaskVehicleMountedWeapon::Mode_Camera;
|
|
}
|
|
else if( m_flags.IsFlagSet(Flag_justAim) || !CanFireWithMountedWeapon(pPed) )
|
|
{
|
|
mode = CTaskVehicleMountedWeapon::Mode_Aim;
|
|
}
|
|
CTaskVehicleMountedWeapon* pNewTask = rage_new CTaskVehicleMountedWeapon(mode, &m_target, m_fFireTolerance);
|
|
SetNewTask(pNewTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::MountedWeapon_OnUpdate( CPed* pPed )
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
// Go back to basic drive task
|
|
SetState(State_waitForClearShot);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// See if we want to change state
|
|
s32 iDesiredState = GetDesiredState(pPed);
|
|
if(iDesiredState != GetState())
|
|
{
|
|
SetState(iDesiredState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
taskFatalAssertf(GetSubTask() && GetSubTask()->GetTaskType() == CTaskTypes::TASK_VEHICLE_MOUNTED_WEAPON, "vehicle mounted weapon subtask expected!" );
|
|
CTaskVehicleMountedWeapon* pMountedWeaponTask = static_cast<CTaskVehicleMountedWeapon*>(GetSubTask());
|
|
// Check LOS to determine whether the AI should be aiming or firing
|
|
if( !m_flags.IsFlagSet(Flag_useCamera) )
|
|
{
|
|
if( !m_flags.IsFlagSet(Flag_justAim) && CheckTargetingLOS(pPed) && CanFireWithMountedWeapon(pPed) )
|
|
{
|
|
pMountedWeaponTask->SetMode(CTaskVehicleMountedWeapon::Mode_Fire);
|
|
}
|
|
else
|
|
{
|
|
pMountedWeaponTask->SetMode(CTaskVehicleMountedWeapon::Mode_Aim);
|
|
}
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::VehicleGun_OnEnter( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
m_uRequestWaitForClearShotTime = 0;
|
|
CTaskVehicleGun::eTaskMode mode = CTaskVehicleGun::Mode_Fire;
|
|
if(m_flags.IsFlagSet(Flag_justAim))
|
|
{
|
|
mode = CTaskVehicleGun::Mode_Aim;
|
|
}
|
|
CTaskVehicleGun* pNewTask = rage_new CTaskVehicleGun(mode, ATSTRINGHASH("FIRING_PATTERN_BURST_FIRE_DRIVEBY", 0x0d31265f2), &m_target);
|
|
SetNewTask(pNewTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::VehicleGun_OnUpdate( CPed* pPed )
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
// Go back to basic drive task
|
|
SetState(State_waitForClearShot);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
taskFatalAssertf(GetSubTask() && GetSubTask()->GetTaskType() == CTaskTypes::TASK_VEHICLE_GUN, "Vehicle gun subtask expected!");
|
|
|
|
// See if we want to change state
|
|
s32 iDesiredState = GetIsFlagSet(aiTaskFlags::TerminationRequested) ? State_exit : GetDesiredState(pPed);
|
|
if(iDesiredState != GetState())
|
|
{
|
|
bool bAttemptTerminatation = true;
|
|
bool bDriveByAllowed = m_flags.IsFlagSet(Flag_forceAllowDriveBy) || pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanDoDrivebys);
|
|
if (iDesiredState == State_waitForClearShot && bDriveByAllowed)
|
|
{
|
|
static dev_u32 suMinAimTimeBeforeWaitForClearShot = 2000;
|
|
//Adding some delay to control task oscillations, B* 738133
|
|
if (!m_uRequestWaitForClearShotTime)
|
|
{
|
|
m_uRequestWaitForClearShotTime = fwTimer::GetTimeInMilliseconds();
|
|
bAttemptTerminatation = false;
|
|
}
|
|
else if ((m_uRequestWaitForClearShotTime + suMinAimTimeBeforeWaitForClearShot) > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
bAttemptTerminatation = false;
|
|
}
|
|
}
|
|
|
|
if (bAttemptTerminatation)
|
|
{
|
|
if( GetSubTask()->SupportsTerminationRequest() )
|
|
{
|
|
GetSubTask()->RequestTermination();
|
|
}
|
|
else if(GetSubTask()->MakeAbortable(ABORT_PRIORITY_URGENT,NULL))
|
|
{
|
|
// We aborted instantly so change state
|
|
SetState(iDesiredState);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_uRequestWaitForClearShotTime = 0;
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::Projectile_OnEnter( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
CTaskVehicleProjectile* pNewTask = rage_new CTaskVehicleProjectile(CTaskVehicleProjectile::Mode_Fire);
|
|
SetNewTask(pNewTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::Projectile_OnUpdate( CPed* pPed )
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
// Go back to basic drive task
|
|
SetState(State_waitForClearShot);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// See if we want to change state
|
|
s32 iDesiredState = GetDesiredState(pPed);
|
|
if(iDesiredState != GetState())
|
|
{
|
|
SetState(iDesiredState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::ShootOutTire_OnEnter( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
//Generate the next shoot out tire time global.
|
|
ms_uNextShootOutTireTimeGlobal = GenerateNextShootOutTireTimeGlobal();
|
|
|
|
//Generate the next shoot out tire time.
|
|
m_uNextShootOutTireTime = GenerateNextShootOutTireTime();
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskShootOutTire(m_target, ShouldApplyReactionWhenShootingOutTire() ? CTaskShootOutTire::SOTCF_ApplyReactionToVehicle : 0);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskVehicleCombat::ShootOutTire_OnUpdate( CPed* UNUSED_PARAM(pPed) )
|
|
{
|
|
//Wait for the task to finish.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Wait for a clear shot.
|
|
SetState(State_waitForClearShot);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskVehicleCombat::InitialiseTargetingSystems( CPed* pPed )
|
|
{
|
|
// We cant use the targeting systems if the target is a world co-ordinate
|
|
if( m_target.GetEntity() )
|
|
{
|
|
// Start up the peds targeting systems and register the current target if valid
|
|
CPedTargetting* pPedTargetting = pPed->GetPedIntelligence()->GetTargetting( true );
|
|
if( pPedTargetting )
|
|
{
|
|
if( !pPedTargetting->FindTarget(m_target.GetEntity()) )
|
|
{
|
|
pPedTargetting->RegisterThreat(m_target.GetEntity());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskVehicleCombat::CheckTargetingLOS( CPed* pPed )
|
|
{
|
|
// Always start checking LOS
|
|
if( m_target.GetEntity() == NULL )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CPedTargetting* pPedTargetting = pPed->GetPedIntelligence()->GetTargetting( true );
|
|
if( pPedTargetting )
|
|
{
|
|
pPedTargetting->SetInUse();
|
|
if( pPedTargetting->GetLosStatus(m_target.GetEntity()) == Los_clear )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
taskErrorf("Should never get here!");
|
|
return false;
|
|
}
|
|
|
|
bool CTaskVehicleCombat::CanFireWithMountedWeapon( CPed* pPed )
|
|
{
|
|
if (!pPed->GetIsDrivingVehicle() && pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_BlockFireForVehiclePassengerMountedGuns))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CTaskVehicleCombat::VehicleCombatState CTaskVehicleCombat::GetDesiredState( CPed* pPed )
|
|
{
|
|
// If the target has changed, always transition back to waiting in the vehicle state
|
|
// forcing the task to restart with the new target
|
|
if( m_flags.IsFlagSet(Flag_targetChanged))
|
|
{
|
|
return State_waitForClearShot;
|
|
}
|
|
|
|
// If the AI is in a mounted weapon position, use this always
|
|
// The decision to aim or fire is made in the mounted weapon update so LOS doesn't need to be checked here.
|
|
weaponAssert(pPed->GetWeaponManager() && pPed->GetInventory());
|
|
const bool bHasWeaponsAtSeat = pPed->GetMyVehicle()->GetSeatHasWeaponsOrTurrets(pPed->GetMyVehicle()->GetSeatManager()->GetPedsSeatIndex(pPed));
|
|
if(bHasWeaponsAtSeat && pPed->GetWeaponManager()->GetEquippedVehicleWeaponIndex() != -1)
|
|
{
|
|
return State_mountedWeapon;
|
|
}
|
|
|
|
// Check can do drive by conditions
|
|
bool bCanDoDrivebys = m_flags.IsFlagSet(Flag_forceAllowDriveBy) || pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanDoDrivebys);
|
|
if(!bCanDoDrivebys && pPed->ShouldBehaveLikeLaw() && !pPed->PopTypeIsMission())
|
|
{
|
|
const CEntity* pTargetEntity = m_target.GetEntity();
|
|
if(pTargetEntity)
|
|
{
|
|
// If in MP and our target ped has a particular wanted level (tweakable via cloud tunable)
|
|
if(NetworkInterface::IsGameInProgress() && pTargetEntity->GetIsTypePed())
|
|
{
|
|
const CPed* pTargetPed = static_cast<const CPed*>(pTargetEntity);
|
|
if(pTargetPed->GetPlayerWanted() && pTargetPed->GetPlayerWanted()->GetWantedLevel() >= ms_TargetDriveByWantedLevel)
|
|
{
|
|
bCanDoDrivebys = true;
|
|
}
|
|
}
|
|
|
|
// If the above logic didn't succeed then see if the target has been hostile towards us recently
|
|
if(!bCanDoDrivebys)
|
|
{
|
|
const CPedTargetting* pPedTargeting = pPed->GetPedIntelligence()->GetTargetting(false);
|
|
if(pPedTargeting)
|
|
{
|
|
const CSingleTargetInfo* pTargetInfo = pPedTargeting->FindTarget(pTargetEntity);
|
|
if(pTargetInfo && pTargetInfo->GetTimeOfLastHostileAction() > fwTimer::GetTimeInMilliseconds() - ms_Tunables.m_MaxTimeSinceTargetLastHostileForLawDriveby)
|
|
{
|
|
bCanDoDrivebys = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is a clear LOS to the target - try to start an attacking vehicle task
|
|
if( bCanDoDrivebys && CheckTargetingLOS(pPed) )
|
|
{
|
|
//Ensure the timer has exceeded the threshold.
|
|
if(fwTimer::GetTimeInMilliseconds() < m_uWeaponPrepareTime)
|
|
{
|
|
return State_waitForClearShot;
|
|
}
|
|
|
|
// Check for driveby weapons, start a driveby task if one is valid
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(pPed->GetWeaponManager()->GetEquippedWeaponHash());
|
|
if( pWeaponInfo )
|
|
{
|
|
const bool bIsOutOfAmmo = pPed->GetInventory()->GetIsWeaponOutOfAmmo(pWeaponInfo);
|
|
const bool bHasDrivebyWeapons = pWeaponInfo->CanBeUsedAsDriveBy(pPed) && !bIsOutOfAmmo &&
|
|
(!pWeaponInfo->GetIsUnarmed() || pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_CanTauntInVehicle));
|
|
|
|
|
|
if( bHasDrivebyWeapons )
|
|
{
|
|
// Check if we are restricted to shooting at peds that are on the same side of the vehicle as we are
|
|
const CVehicle* pMyVehicle = pPed->GetMyVehicle();
|
|
if(pMyVehicle && pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_RestrictInVehicleAimingToCurrentSide))
|
|
{
|
|
const Vec3V vVehiclePosition = pMyVehicle->GetTransform().Transform(Average(VECTOR3_TO_VEC3V(pMyVehicle->GetBoundingBoxMin()), VECTOR3_TO_VEC3V(pMyVehicle->GetBoundingBoxMax())));
|
|
Vector3 vTargetPosition;
|
|
m_target.GetPosition(vTargetPosition);
|
|
|
|
const ScalarV scVehiclePedDot = Dot(pMyVehicle->GetTransform().GetRight(), pPed->GetTransform().GetPosition() - vVehiclePosition);
|
|
const ScalarV scVehicleTargetDot = Dot(pMyVehicle->GetTransform().GetRight(), VECTOR3_TO_VEC3V(vTargetPosition) - pPed->GetTransform().GetPosition());
|
|
if(IsGreaterThanAll(scVehiclePedDot, ScalarV(V_ZERO)) != IsGreaterThanAll(scVehicleTargetDot, ScalarV(V_ZERO)))
|
|
{
|
|
return State_waitForClearShot;
|
|
}
|
|
}
|
|
|
|
// Check for throwing grenades
|
|
if( pWeaponInfo->GetIsThrownWeapon() )
|
|
{
|
|
return State_projectile;
|
|
} //Check if the ped can shoot out a tire.
|
|
else if(CanShootOutTire())
|
|
{
|
|
return State_shootOutTire;
|
|
}
|
|
else
|
|
{
|
|
return State_vehicleGun;
|
|
}
|
|
}
|
|
|
|
}
|
|
return State_waitForClearShot;
|
|
}
|
|
// No clear los - go back to being seated
|
|
else
|
|
{
|
|
return State_waitForClearShot;
|
|
}
|
|
}
|
|
|
|
void CTaskVehicleCombat::SetTarget( CAITarget& val )
|
|
{
|
|
// If the target has changed, set the flag so the task can be restarted.
|
|
if( val != m_target )
|
|
m_flags.SetFlag(Flag_targetChanged);
|
|
|
|
m_target = val;
|
|
}
|
|
|
|
void CTaskVehicleCombat::InitTunables()
|
|
{
|
|
static int s_iDefaultTargetWantedLevel = static_cast<int>(ms_TargetDriveByWantedLevel);
|
|
ms_TargetDriveByWantedLevel = static_cast<eWantedLevel>(::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("TARGET_DRIVEBY_WANTED_LEVEL", 0xa12169c4), s_iDefaultTargetWantedLevel));
|
|
}
|
|
|
|
bool CTaskVehicleCombat::CanShootOutTire() const
|
|
{
|
|
return false;
|
|
|
|
/*
|
|
//Ensure the global timer has has exceeded the threshold.
|
|
u32 uTime = fwTimer::GetTimeInMilliseconds();
|
|
if(uTime < ms_uNextShootOutTireTimeGlobal)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the timer has exceeded the threshold.
|
|
if(uTime < m_uNextShootOutTireTime)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is valid.
|
|
const CPed* pPed = GetPed();
|
|
if(!pPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is a swat member.
|
|
if(pPed->GetPedType() != PEDTYPE_SWAT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the ped is in a vehicle.
|
|
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle is a helicopter.
|
|
if(!pVehicle->InheritsFromHeli())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target is valid.
|
|
const CEntity* pEntity = m_target.GetEntity();
|
|
if(!pEntity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the entity is a ped.
|
|
if(!pEntity->GetIsTypePed())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target is in a vehicle.
|
|
const CPed* pTarget = static_cast<const CPed *>(pEntity);
|
|
if(!pTarget->GetVehiclePedInside())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the target is a player.
|
|
if(!pTarget->IsPlayer())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
*/
|
|
}
|
|
|
|
const CPed* CTaskVehicleCombat::GetTargetPed() const
|
|
{
|
|
//Ensure the entity is valid.
|
|
const CEntity* pEntity = m_target.GetEntity();
|
|
if(!pEntity)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//Ensure the entity is a ped.
|
|
if(!pEntity->GetIsTypePed())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return static_cast<const CPed *>(pEntity);
|
|
}
|
|
|
|
const CVehicle* CTaskVehicleCombat::GetTargetVehicle() const
|
|
{
|
|
//Ensure the target ped is valid.
|
|
const CPed* pPed = GetTargetPed();
|
|
if(!pPed)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pPed->GetVehiclePedInside();
|
|
}
|
|
|
|
bool CTaskVehicleCombat::ShouldApplyReactionWhenShootingOutTire() const
|
|
{
|
|
//Ensure the random number is within the threshold.
|
|
float fRandom = fwRandom::GetRandomNumberInRange(0.0f, 1.0f);
|
|
float fMaxRandom = ms_Tunables.m_ChancesToApplyReactionWhenShootingOutTire;
|
|
if(fRandom >= fMaxRandom)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
u32 CTaskVehicleCombat::GenerateNextShootOutTireTime()
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() + (u32)((fwRandom::GetRandomNumberInRange(ms_Tunables.m_MinTimeInCombatToShootOutTires, ms_Tunables.m_MaxTimeInCombatToShootOutTires) * 1000.0f));
|
|
}
|
|
|
|
u32 CTaskVehicleCombat::GenerateNextShootOutTireTimeGlobal()
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() + (u32)((fwRandom::GetRandomNumberInRange(ms_Tunables.m_MinTimeBetweenShootOutTiresGlobal, ms_Tunables.m_MaxTimeBetweenShootOutTiresGlobal) * 1000.0f));
|
|
}
|