Files
GTASource/game/task/Response/TaskReactToExplosion.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

572 lines
14 KiB
C++

// FILE : TaskReactToExplosion.h
// CREATED : 11-4-2011
// File header
#include "Network/NetworkInterface.h"
#include "Peds/ped.h"
#include "Peds/PedIntelligence.h"
#include "task/Combat/TaskThreatResponse.h"
#include "task/Combat/Cover/TaskCover.h"
#include "Task/Combat/Subtasks/TaskReactToBuddyShot.h"
#include "Task/Combat/Subtasks/TaskShellShocked.h"
#include "Task/Combat/Subtasks/TaskVariedAimPose.h"
#include "Task/Movement/TaskCollisionResponse.h"
#include "task/Response/TaskFlee.h"
#include "task/Response/TaskReactAndFlee.h"
#include "Task/Response/TaskReactToExplosion.h"
AI_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
////////////////////////////////////////////////////////////////////////////////
// CTaskReactToExplosion
////////////////////////////////////////////////////////////////////////////////
CTaskReactToExplosion::Tunables CTaskReactToExplosion::sm_Tunables;
IMPLEMENT_RESPONSE_TASK_TUNABLES(CTaskReactToExplosion, 0x761cd2c7);
CTaskReactToExplosion::CTaskReactToExplosion(Vec3V_In vPosition, CEntity* pOwner, float fRadius, fwFlags8 uConfigFlags)
: m_vPosition(vPosition)
, m_pOwner(pOwner)
, m_fRadius(fRadius)
, m_nDurationOverrideForShellShocked(CTaskShellShocked::Duration_Invalid)
, m_uConfigFlags(uConfigFlags)
, m_uConfigFlagsForShellShocked(0)
{
SetInternalTaskType(CTaskTypes::TASK_REACT_TO_EXPLOSION);
}
CTaskReactToExplosion::~CTaskReactToExplosion()
{
}
#if !__FINAL
void CTaskReactToExplosion::Debug() const
{
CTask::Debug();
}
#endif // !__FINAL
#if !__FINAL
const char * CTaskReactToExplosion::GetStaticStateName( s32 iState )
{
Assert(iState>=State_Start&&iState<=State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_Resume",
"State_ShellShocked",
"State_Flinch",
"State_ThreatResponse",
"State_Flee",
"State_Finish",
};
return aStateNames[iState];
}
#endif // !__FINAL
bool CTaskReactToExplosion::IsTemporary(const CPed& rPed, CEntity* pOwner, fwFlags8 uConfigFlags)
{
return (ChooseStateForThreatResponse(rPed, pOwner, uConfigFlags) == State_Finish);
}
bool CTaskReactToExplosion::IsValid(const CPed& rPed, Vec3V_In vPosition, CEntity* pOwner, float fRadius, fwFlags8 uConfigFlags, const CAITarget& rAimTarget)
{
//Ensure the ped is not crouching.
if(rPed.GetIsCrouching())
{
return false;
}
//Ensure the ped is not a ragdoll.
else if(rPed.GetUsingRagdoll())
{
return false;
}
//Check if the ped is in cover.
CTaskInCover* pTaskInCover = static_cast<CTaskInCover *>(
rPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_IN_COVER));
if(pTaskInCover)
{
//Ensure the ped is aiming.
if(pTaskInCover->GetState() != CTaskInCover::State_Aim)
{
return false;
}
}
//Check if the ped is using a varied aim pose.
CTaskVariedAimPose* pTaskVariedAimPose = static_cast<CTaskVariedAimPose *>(
rPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VARIED_AIM_POSE));
if(pTaskVariedAimPose)
{
//Ensure the ped's current pose is not crouching.
if(pTaskVariedAimPose->IsCurrentPoseCrouching())
{
return false;
}
}
//Ensure the ped is not diving to the ground.
if(rPed.GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_DIVE_TO_GROUND, true))
{
return false;
}
//Ensure the ped is not getting up.
else if(rPed.GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GET_UP, true))
{
return false;
}
return (ChooseState(rPed, vPosition, pOwner, fRadius, uConfigFlags, rAimTarget) != State_Finish);
}
aiTask* CTaskReactToExplosion::Copy() const
{
CTaskReactToExplosion* pTask = rage_new CTaskReactToExplosion(m_vPosition, m_pOwner, m_fRadius, m_uConfigFlags);
pTask->SetAimTarget(m_AimTarget);
pTask->SetDurationOverrideForShellShocked(m_nDurationOverrideForShellShocked);
pTask->SetConfigFlagsForShellShocked(m_uConfigFlagsForShellShocked);
return pTask;
}
CTask::FSM_Return CTaskReactToExplosion::ProcessPreFSM()
{
//Grab the ped.
CPed* pPed = GetPed();
// Allow certain lod modes
pPed->GetPedAiLod().ClearBlockedLodFlag(CPedAILod::AL_LodMotionTask);
return FSM_Continue;
}
CTask::FSM_Return CTaskReactToExplosion::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
FSM_Begin
FSM_State(State_Start)
FSM_OnUpdate
return Start_OnUpdate();
FSM_State(State_Resume)
FSM_OnUpdate
return Resume_OnUpdate();
FSM_State(State_ShellShocked)
FSM_OnEnter
ShellShocked_OnEnter();
FSM_OnUpdate
return ShellShocked_OnUpdate();
FSM_State(State_Flinch)
FSM_OnEnter
Flinch_OnEnter();
FSM_OnUpdate
return Flinch_OnUpdate();
FSM_State(State_ThreatResponse)
FSM_OnEnter
ThreatResponse_OnEnter();
FSM_OnUpdate
return ThreatResponse_OnUpdate();
FSM_State(State_Flee)
FSM_OnEnter
Flee_OnEnter();
FSM_OnUpdate
return Flee_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
CTaskInfo* CTaskReactToExplosion::CreateQueriableState() const
{
return rage_new CClonedReactToExplosionInfo(GetState(), VEC3V_TO_VECTOR3(m_vPosition), m_pOwner, m_uConfigFlagsForShellShocked);
}
void CTaskReactToExplosion::ReadQueriableState(CClonedFSMTaskInfo* pTaskInfo)
{
Assert( pTaskInfo && pTaskInfo->GetTaskInfoType() == CTaskInfo::INFO_TYPE_REACT_TO_EXPLOSION );
CClonedReactToExplosionInfo *reactToExplosionInfo = static_cast<CClonedReactToExplosionInfo*>(pTaskInfo);
m_vPosition = VECTOR3_TO_VEC3V(reactToExplosionInfo->GetExplosionPosition());
m_pOwner = reactToExplosionInfo->GetExplosionOwner();
m_uConfigFlagsForShellShocked = reactToExplosionInfo->GetConfigFlagsForShellShocked();
CTaskFSMClone::ReadQueriableState(pTaskInfo);
}
CTask::FSM_Return CTaskReactToExplosion::UpdateClonedFSM (const s32 iState, const FSM_Event iEvent)
{
return UpdateFSM(iState, iEvent);
}
void CTaskReactToExplosion::OnCloneTaskNoLongerRunningOnOwner()
{
SetStateFromNetwork(State_Finish);
}
CTask::FSM_Return CTaskReactToExplosion::Start_OnUpdate()
{
//Choose the state.
if (GetPed()->IsNetworkClone())
{
SetState(GetStateFromNetwork());
}
else
{
SetState(ChooseState(*GetPed(), m_vPosition, m_pOwner, m_fRadius, m_uConfigFlags, m_AimTarget));
}
return FSM_Continue;
}
CTask::FSM_Return CTaskReactToExplosion::Resume_OnUpdate()
{
//Set the state to resume to.
bool bKeepSubTask = false;
int iStateToResumeTo = ChooseStateToResumeTo(bKeepSubTask);
SetState(iStateToResumeTo);
//Check if we should keep the sub-task.
if(bKeepSubTask)
{
//Keep the sub-task across the transition.
SetFlag(aiTaskFlags::KeepCurrentSubtaskAcrossTransition);
}
return FSM_Continue;
}
void CTaskReactToExplosion::ShellShocked_OnEnter()
{
//Choose the duration.
CTaskShellShocked::Duration nDuration = m_nDurationOverrideForShellShocked;
if(nDuration == CTaskShellShocked::Duration_Invalid)
{
fwRandom::SetRandomSeed(GetPed()->GetRandomSeed());
nDuration = fwRandom::GetRandomTrueFalse() ? CTaskShellShocked::Duration_Short : CTaskShellShocked::Duration_Long;
}
//Create the task.
CTask* pTask = rage_new CTaskShellShocked(m_vPosition, nDuration, m_uConfigFlagsForShellShocked);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToExplosion::ShellShocked_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsSubtaskFinished(CTaskTypes::TASK_SHELL_SHOCKED))
{
//Set the state for threat response.
SetStateForThreatResponse();
}
return FSM_Continue;
}
void CTaskReactToExplosion::Flinch_OnEnter()
{
//Create the task. Clones don't assert - some of this info won't sync correctly and can be different.
if(!GetPed()->IsNetworkClone())
{
taskAssert(CTaskReactToBuddyShot::IsValid(*GetPed(), CAITarget(VEC3V_TO_VECTOR3(m_vPosition)), m_AimTarget));
}
CTask* pTask = rage_new CTaskReactToBuddyShot(CAITarget(VEC3V_TO_VECTOR3(m_vPosition)), m_AimTarget);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToExplosion::Flinch_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsSubtaskFinished(CTaskTypes::TASK_REACT_TO_BUDDY_SHOT))
{
//Set the state for threat response.
SetStateForThreatResponse();
}
return FSM_Continue;
}
void CTaskReactToExplosion::ThreatResponse_OnEnter()
{
//Check if we are resuming the state. Keep the sub-task, if possible.
if(GetSubTask() && (GetSubTask()->GetTaskType() == CTaskTypes::TASK_THREAT_RESPONSE))
{
return;
}
taskAssert(CanUseThreatResponse(*GetPed(), m_pOwner));
//Create the task.
CTaskThreatResponse* pTask = rage_new CTaskThreatResponse(GetOwnerPed());
pTask->GetConfigFlags().SetFlag(CTaskThreatResponse::CF_ThreatIsIndirect);
//Check if we can't flee.
if(!CanFlee(*GetPed()))
{
//Force us to fight.
pTask->SetThreatResponseOverride(CTaskThreatResponse::TR_Fight);
}
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToExplosion::ThreatResponse_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
void CTaskReactToExplosion::Flee_OnEnter()
{
//Check if we are resuming the state. Keep the sub-task, if possible.
if(GetSubTask() && (GetSubTask()->GetTaskType() == CTaskTypes::TASK_REACT_AND_FLEE))
{
return;
}
taskAssert(CanFlee(*GetPed()));
//Get the target.
CAITarget target;
GetTargetToFleeFrom(target);
taskAssert(target.GetIsValid());
//Create the task.
CTaskReactAndFlee* pTask = rage_new CTaskReactAndFlee(target, CTaskReactAndFlee::BA_Gunfire);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToExplosion::Flee_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
s32 CTaskReactToExplosion::ChooseStateForThreatResponse() const
{
if (GetPed()->IsNetworkClone())
{
return GetStateFromNetwork();
}
return (ChooseStateForThreatResponse(*GetPed(), m_pOwner, m_uConfigFlags));
}
int CTaskReactToExplosion::ChooseStateToResumeTo(bool& bKeepSubTask) const
{
//Keep the sub-task.
bKeepSubTask = true;
//Check if the sub-task is valid.
const CTask* pSubTask = GetSubTask();
if(pSubTask)
{
//Check the task type.
switch(pSubTask->GetTaskType())
{
case CTaskTypes::TASK_THREAT_RESPONSE:
{
return State_ThreatResponse;
}
case CTaskTypes::TASK_REACT_AND_FLEE:
{
return State_Flee;
}
default:
{
break;
}
}
}
//Do not keep the sub-task.
bKeepSubTask = false;
return State_Finish;
}
CPed* CTaskReactToExplosion::GetOwnerPed() const
{
return (GetOwnerPed(m_pOwner));
}
void CTaskReactToExplosion::GetTargetToFleeFrom(CAITarget& rTarget) const
{
//Check if the owner ped is valid.
CPed* pOwnerPed = GetOwnerPed();
if(pOwnerPed)
{
rTarget.SetEntity(pOwnerPed);
}
else
{
rTarget.SetPosition(VEC3V_TO_VECTOR3(m_vPosition));
}
}
void CTaskReactToExplosion::SetStateForThreatResponse()
{
//Set the state.
s32 iState = ChooseStateForThreatResponse();
SetState(iState);
}
bool CTaskReactToExplosion::CanFlee(const CPed& rPed)
{
//Ensure the ped is not law enforcement.
if(rPed.IsLawEnforcementPed())
{
return false;
}
//Ensure the ped is not security.
if(rPed.IsSecurityPed())
{
return false;
}
//Ensure the flag is set.
if(!rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_RunFromFiresAndExplosions))
{
return false;
}
return true;
}
bool CTaskReactToExplosion::CanUseThreatResponse(const CPed& rPed, CEntity* pOwner)
{
//Ensure the owner ped is valid.
CPed* pOwnerPed = GetOwnerPed(pOwner);
if(!pOwnerPed)
{
return false;
}
//Check if we shouldn't flee.
if(!CanFlee(rPed))
{
//Ensure we would have made a decision to fight (we will force this in threat response when we can't flee).
CTaskThreatResponse::ThreatResponse nThreatResponse = CTaskThreatResponse::DetermineThreatResponseState(rPed, pOwnerPed);
if(nThreatResponse != CTaskThreatResponse::TR_Fight)
{
return false;
}
}
return true;
}
s32 CTaskReactToExplosion::ChooseState(const CPed& rPed, Vec3V_In vPosition, CEntity* pOwner, float fRadius, fwFlags8 uConfigFlags, const CAITarget& rAimTarget)
{
//Generate the distance between the ped and the explosion.
ScalarV scDist = Dist(rPed.GetTransform().GetPosition(), vPosition);
scDist = Subtract(scDist, ScalarVFromF32(fRadius));
//Check if we can be shell-shocked.
if(CTaskShellShocked::IsValid(rPed, vPosition))
{
//Check if we are inside the shell-shocked distance.
ScalarV scMaxShellShockedDist = ScalarVFromF32(sm_Tunables.m_MaxShellShockedDistance);
if(IsLessThanAll(scDist, scMaxShellShockedDist))
{
return State_ShellShocked;
}
}
//Check if we can flinch.
if(CTaskReactToBuddyShot::IsValid(rPed, CAITarget(VEC3V_TO_VECTOR3(vPosition)), rAimTarget))
{
//Check if we are inside the flinch distance.
ScalarV scMaxFlinchDist = ScalarVFromF32(sm_Tunables.m_MaxFlinchDistance);
if(IsLessThanAll(scDist, scMaxFlinchDist))
{
return State_Flinch;
}
}
//Choose the state for threat response.
s32 iState = ChooseStateForThreatResponse(rPed, pOwner, uConfigFlags);
return iState;
}
s32 CTaskReactToExplosion::ChooseStateForThreatResponse(const CPed& rPed, CEntity* pOwner, fwFlags8 uConfigFlags)
{
//Ensure the flag is not set.
if(uConfigFlags.IsFlagSet(CF_DisableThreatResponse))
{
return State_Finish;
}
//Ensure the ped is random.
if(!rPed.PopTypeIsRandom())
{
return State_Finish;
}
//Check if we can use threat response.
if(CanUseThreatResponse(rPed, pOwner))
{
return State_ThreatResponse;
}
//Check if we can flee.
else if(CanFlee(rPed))
{
return State_Flee;
}
return State_Finish;
}
CPed* CTaskReactToExplosion::GetOwnerPed(CEntity* pOwner)
{
//Ensure the owner is valid.
if(!pOwner)
{
return NULL;
}
//Check if the owner is a ped.
if(pOwner->GetIsTypePed())
{
return static_cast<CPed *>(pOwner);
}
//Check if the owner is a vehicle.
else if(pOwner->GetIsTypeVehicle())
{
return static_cast<CVehicle *>(pOwner)->GetDriver();
}
return NULL;
}