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

407 lines
9.6 KiB
C++

// FILE : TaskReactToImminentExplosion.h
// File header
#include "Task/Response/TaskReactToImminentExplosion.h"
// Game headers
#include "Peds/ped.h"
#include "Peds/PedIntelligence.h"
#include "task/Combat/Cover/TaskCover.h"
#include "task/Combat/Subtasks/TaskReactToBuddyShot.h"
#include "task/Combat/Subtasks/TaskVariedAimPose.h"
#include "task/General/TaskBasic.h"
#include "task/Movement/TaskNavMesh.h"
#include "task/Response/TaskDiveToGround.h"
AI_OPTIMISATIONS()
////////////////////////////////////////////////////////////////////////////////
// CTaskReactToImminentExplosion
////////////////////////////////////////////////////////////////////////////////
u32 CTaskReactToImminentExplosion::s_uLastTimeEscapeUsedOnScreen = 0;
CTaskReactToImminentExplosion::Tunables CTaskReactToImminentExplosion::sm_Tunables;
IMPLEMENT_RESPONSE_TASK_TUNABLES(CTaskReactToImminentExplosion, 0xb3727566);
CTaskReactToImminentExplosion::CTaskReactToImminentExplosion(const CAITarget& rExplosionTarget, float fRadius, u32 uTimeOfExplosion)
: m_ExplosionTarget(rExplosionTarget)
, m_fRadius(fRadius)
, m_uTimeOfExplosion(uTimeOfExplosion)
{
SetInternalTaskType(CTaskTypes::TASK_REACT_TO_IMMINENT_EXPLOSION);
}
CTaskReactToImminentExplosion::~CTaskReactToImminentExplosion()
{
}
#if !__FINAL
void CTaskReactToImminentExplosion::Debug() const
{
CTask::Debug();
}
#endif // !__FINAL
#if !__FINAL
const char * CTaskReactToImminentExplosion::GetStaticStateName(s32 iState)
{
Assert(iState >= State_Start && iState <= State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_Flinch",
"State_Escape",
"State_Dive",
"State_Finish",
};
return aStateNames[iState];
}
#endif // !__FINAL
bool CTaskReactToImminentExplosion::IsValid(const CPed& rPed, const CAITarget& rExplosionTarget, float fRadius, const CAITarget& rAimTarget, bool bIgnoreCover)
{
//Ensure the explosion target is valid.
if(!rExplosionTarget.GetIsValid())
{
return false;
}
//Ensure the ped is on foot.
if(!rPed.GetIsOnFoot())
{
return false;
}
//Ensure the ped is not crouching.
if(rPed.GetIsCrouching())
{
return false;
}
//Ensure the ped is not a ragdoll.
if(rPed.GetUsingRagdoll())
{
return false;
}
//Check if we are not ignoring cover.
if(!bIgnoreCover)
{
//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 getting up.
if(rPed.GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GET_UP, true))
{
return false;
}
return (ChooseState(rPed, rExplosionTarget, fRadius, rAimTarget) != State_Finish);
}
bool CTaskReactToImminentExplosion::MustTargetBeValid() const
{
//Check the state.
switch(GetState())
{
case State_Flinch:
case State_Dive:
{
return false;
}
default:
{
break;
}
}
return true;
}
void CTaskReactToImminentExplosion::ProcessTimeOfExplosion()
{
//Check if the entity is valid.
const CEntity* pEntity = m_ExplosionTarget.GetEntity();
if(pEntity)
{
//Calculate the time until the explosion.
float fTime;
if(CExplosionHelper::CalculateTimeUntilExplosion(*pEntity, fTime))
{
m_uTimeOfExplosion = fwTimer::GetTimeInMilliseconds() + (u32)(fTime * 1000.0f);
}
}
}
bool CTaskReactToImminentExplosion::CanEscape(const CPed& rPed)
{
//Ensure the ped can run from fires and explosions.
if(!rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_RunFromFiresAndExplosions))
{
return false;
}
//Ensure the ped is not in an interior.
if(rPed.GetIsInInterior())
{
return false;
}
//Ensure the ped is not on stairs.
if(rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_OnStairs))
{
return false;
}
//Ensure we have not used escape recently.
static dev_u32 s_uMinTime = 150;
if(CTimeHelpers::GetTimeSince(s_uLastTimeEscapeUsedOnScreen) < s_uMinTime)
{
return false;
}
return true;
}
bool CTaskReactToImminentExplosion::CanFlinch(const CPed& rPed, const CAITarget& rExplosionTarget, const CAITarget& rAimTarget)
{
//Ensure the task is valid.
if(!CTaskReactToBuddyShot::IsValid(rPed, rExplosionTarget, rAimTarget))
{
return false;
}
return true;
}
s32 CTaskReactToImminentExplosion::ChooseState(const CPed& rPed, const CAITarget& rExplosionTarget, float fRadius, const CAITarget& rAimTarget)
{
//Grab the target position.
Vec3V vTargetPosition;
rExplosionTarget.GetPosition(RC_VECTOR3(vTargetPosition));
//Generate the distance between the ped and the imminent explosion.
ScalarV scDist = Dist(rPed.GetTransform().GetPosition(), vTargetPosition);
scDist = Subtract(scDist, ScalarVFromF32(fRadius));
//Check if we can escape.
if(CanEscape(rPed))
{
//Check if we are inside the max distance.
ScalarV scMaxDiveDist = ScalarVFromF32(sm_Tunables.m_MaxEscapeDistance);
if(IsLessThanAll(scDist, scMaxDiveDist))
{
return State_Escape;
}
}
//Check if we can flinch.
if(CanFlinch(rPed, rExplosionTarget, rAimTarget))
{
//Check if we are inside the max distance.
ScalarV scMaxFlinchDist = ScalarVFromF32(sm_Tunables.m_MaxFlinchDistance);
if(IsLessThanAll(scDist, scMaxFlinchDist))
{
return State_Flinch;
}
}
return State_Finish;
}
aiTask* CTaskReactToImminentExplosion::Copy() const
{
//Create the task.
CTaskReactToImminentExplosion* pTask = rage_new CTaskReactToImminentExplosion(m_ExplosionTarget, m_fRadius, m_uTimeOfExplosion);
pTask->SetAimTarget(m_AimTarget);
return pTask;
}
CTask::FSM_Return CTaskReactToImminentExplosion::ProcessPreFSM()
{
//Check if the target must be valid.
if(MustTargetBeValid())
{
//Ensure the target is valid.
if(!m_ExplosionTarget.GetIsValid())
{
return FSM_Quit;
}
}
//Process the time of explosion.
ProcessTimeOfExplosion();
return FSM_Continue;
}
CTask::FSM_Return CTaskReactToImminentExplosion::UpdateFSM(const s32 iState, const FSM_Event iEvent)
{
FSM_Begin
FSM_State(State_Start)
FSM_OnUpdate
return Start_OnUpdate();
FSM_State(State_Flinch)
FSM_OnEnter
Flinch_OnEnter();
FSM_OnUpdate
return Flinch_OnUpdate();
FSM_State(State_Escape)
FSM_OnEnter
Escape_OnEnter();
FSM_OnUpdate
return Escape_OnUpdate();
FSM_State(State_Dive)
FSM_OnEnter
Dive_OnEnter();
FSM_OnUpdate
return Dive_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
CTask::FSM_Return CTaskReactToImminentExplosion::Start_OnUpdate()
{
//Set the state.
SetState(ChooseState(*GetPed(), m_ExplosionTarget, m_fRadius, m_AimTarget));
return FSM_Continue;
}
void CTaskReactToImminentExplosion::Flinch_OnEnter()
{
//Create the task.
taskAssert(CTaskReactToBuddyShot::IsValid(*GetPed(), m_ExplosionTarget, m_AimTarget));
CTask* pTask = rage_new CTaskReactToBuddyShot(m_ExplosionTarget, m_AimTarget);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToImminentExplosion::Flinch_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
void CTaskReactToImminentExplosion::Escape_OnEnter()
{
//Check if the ped is on-screen.
if(GetPed()->GetPedConfigFlag(CPED_CONFIG_FLAG_VisibleOnScreen))
{
//Set the time.
s_uLastTimeEscapeUsedOnScreen = fwTimer::GetTimeInMilliseconds();
}
//Grab the target position.
Vector3 vTargetPosition;
m_ExplosionTarget.GetPosition(vTargetPosition);
//Create the nav params.
CNavParams params;
params.m_vTarget = vTargetPosition;
params.m_fMoveBlendRatio = MOVEBLENDRATIO_RUN;
params.m_bFleeFromTarget = true;
params.m_fFleeSafeDistance = m_fRadius + 3.0f;
//Create the move task.
CTaskMoveFollowNavMesh* pTaskMove = rage_new CTaskMoveFollowNavMesh(params);
pTaskMove->SetQuitTaskIfRouteNotFound(true);
//Create the task.
CTask* pTask = rage_new CTaskComplexControlMovement(pTaskMove);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToImminentExplosion::Escape_OnUpdate()
{
//Check if the explosion is about to occur.
static const u32 s_uTimeToDiveBeforeExplosion = 1000;
if((fwTimer::GetTimeInMilliseconds() + s_uTimeToDiveBeforeExplosion) > m_uTimeOfExplosion)
{
//Move to the dive state.
SetState(State_Dive);
}
//Check if the sub-task has finished.
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
void CTaskReactToImminentExplosion::Dive_OnEnter()
{
//Grab the target position.
Vec3V vTargetPosition;
m_ExplosionTarget.GetPosition(RC_VECTOR3(vTargetPosition));
//Calculate the direction.
Vec3V vDirection = Subtract(GetPed()->GetTransform().GetPosition(), vTargetPosition);
vDirection = NormalizeFastSafe(vDirection, GetPed()->GetTransform().GetForward());
//Create the task.
static fwMvClipSetId s_ClipSetId("explosions",0x87941A0F);
CTaskDiveToGround* pTask = rage_new CTaskDiveToGround(vDirection, s_ClipSetId);
pTask->SetBlendInDelta(SLOW_BLEND_IN_DELTA);
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskReactToImminentExplosion::Dive_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsSubtaskFinished(CTaskTypes::TASK_DIVE_TO_GROUND))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}