Files
GTASource/game/task/Combat/Subtasks/TaskAdvance.cpp
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

531 lines
13 KiB
C++

// FILE : TaskAdvance.h
// File header
#include "Task/Combat/Subtasks/TaskAdvance.h"
// Game headers
#include "Peds/Ped.h"
#include "Peds/PedIntelligence.h"
#include "task/Combat/CombatManager.h"
#include "task/Combat/TacticalAnalysis.h"
#include "task/Combat/Cover/cover.h"
#include "task/Movement/TaskMoveToTacticalPoint.h"
#include "task/Movement/TaskSeekEntity.h"
#include "network\events\NetworkEventTypes.h"
AI_OPTIMISATIONS()
//=========================================================================
// CTaskAdvance
//=========================================================================
CTaskAdvance::Tunables CTaskAdvance::sm_Tunables;
IMPLEMENT_COMBAT_TASK_TUNABLES(CTaskAdvance, 0x155b6f93);
CTaskAdvance::CTaskAdvance(const CAITarget& rTarget, fwFlags8 uConfigFlags)
: m_Target(rTarget)
, m_pSubTaskToUseDuringMovement(NULL)
, m_fTimeSinceLastSeekCheck(0.0f)
, m_uConfigFlags(uConfigFlags)
{
SetInternalTaskType(CTaskTypes::TASK_ADVANCE);
}
CTaskAdvance::~CTaskAdvance()
{
//Clear the sub-task to use during movement.
ClearSubTaskToUseDuringMovement();
}
void CTaskAdvance::ClearSubTaskToUseDuringMovement()
{
//Free the task.
CTask* pSubTaskToUseDuringMovement = m_pSubTaskToUseDuringMovement;
delete pSubTaskToUseDuringMovement;
//Clear the task.
m_pSubTaskToUseDuringMovement = NULL;
}
const CPed* CTaskAdvance::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;
}
const CPed* pPed = static_cast<const CPed *>(pEntity);
return pPed;
}
bool CTaskAdvance::IsMovingToPoint() const
{
//Check the state.
switch(GetState())
{
case State_MoveToTacticalPoint:
{
//Ensure the sub-task is valid.
const CTask* pSubTask = GetSubTask();
if(!pSubTask)
{
break;
}
//Ensure the sub-task is the correct type.
if(pSubTask->GetTaskType() != CTaskTypes::TASK_MOVE_TO_TACTICAL_POINT)
{
break;
}
const CTaskMoveToTacticalPoint* pTask = static_cast<const CTaskMoveToTacticalPoint *>(pSubTask);
return pTask->IsMovingToPoint();
}
default:
{
break;
}
}
return false;
}
void CTaskAdvance::SetSubTaskToUseDuringMovement(CTask* pTask)
{
//Clear the sub-task to use during movement.
ClearSubTaskToUseDuringMovement();
//Set the sub-task to use during movement.
m_pSubTaskToUseDuringMovement = pTask;
}
float CTaskAdvance::ScoreCoverPoint(const CTaskMoveToTacticalPoint::ScoreCoverPointInput& rInput)
{
//Call the default version.
float fScore = CTaskMoveToTacticalPoint::DefaultScoreCoverPoint(rInput);
//Grab the cover point position.
Vec3V vPosition;
rInput.m_pPoint->GetCoverPoint()->GetCoverPointPosition(RC_VECTOR3(vPosition));
//Score the position.
fScore *= ScorePosition(rInput.m_pTask, vPosition);
return fScore;
}
float CTaskAdvance::ScoreNavMeshPoint(const CTaskMoveToTacticalPoint::ScoreNavMeshPointInput& rInput)
{
//Call the default version.
float fScore = CTaskMoveToTacticalPoint::DefaultScoreNavMeshPoint(rInput);
//Score the position.
fScore *= ScorePosition(rInput.m_pTask, rInput.m_pPoint->GetPosition());
return fScore;
}
float CTaskAdvance::ScorePosition(const CTaskMoveToTacticalPoint* pTask, Vec3V_In vPosition)
{
//Initialize the score.
float fScore = 1.0f;
//Grab the task.
taskAssert(pTask->GetParent()->GetTaskType() == CTaskTypes::TASK_ADVANCE);
const CTaskAdvance* pTaskAdvance = static_cast<const CTaskAdvance *>(pTask->GetParent());
//Check if we are frustrated.
if(pTaskAdvance->m_uConfigFlags.IsFlagSet(CF_IsFrustrated))
{
//Check if the target has a cover point, and is in cover.
const CPed* pTargetPed = pTaskAdvance->GetTargetPed();
CCoverPoint* pTargetCoverPoint = pTargetPed ? pTargetPed->GetCoverPoint() : NULL;
if( pTargetCoverPoint &&
pTargetPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER, true) )
{
//Check if the cover point provides cover against the position.
if(CCover::DoesCoverPointProvideCover(pTargetCoverPoint, pTargetCoverPoint->GetArc(), VEC3V_TO_VECTOR3(vPosition)))
{
//Apply a significant penalty. We are frustrated, and want a clear LOS.
static float s_fPenalty = 0.05f;
fScore *= s_fPenalty;
}
}
}
return fScore;
}
#if !__FINAL
void CTaskAdvance::Debug() const
{
CTask::Debug();
}
const char* CTaskAdvance::GetStaticStateName(s32 iState)
{
Assert(iState >= State_Start && iState <= State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_Resume",
"State_MoveToTacticalPoint",
"State_Seek",
"State_Finish"
};
return aStateNames[iState];
}
#endif // !__FINAL
bool CTaskAdvance::CanSeek() const
{
//Ensure the target is not outside our defensive area.
if(GetPed()->GetPedIntelligence()->GetDefensiveArea()->IsActive() &&
!GetPed()->GetPedIntelligence()->GetDefensiveArea()->IsPointInArea(VEC3V_TO_VECTOR3(GetTargetPed()->GetTransform().GetPosition())))
{
return false;
}
//Check if we are frustrated.
if(m_uConfigFlags.IsFlagSet(CF_IsFrustrated))
{
return true;
}
//Check if the tactical analysis is not active.
const CPed* pTarget = GetTargetPed();
if(!CTacticalAnalysis::IsActive(*pTarget))
{
return true;
}
//Check if we have no combat director.
const CCombatDirector* pCombatDirector = GetPed()->GetPedIntelligence()->GetCombatDirector();
if(!pCombatDirector)
{
return true;
}
//Check if we have not disabled seek due to
if(!GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_DisableSeekDueToLineOfSight))
{
//Check if we have not exceeded the max seekers.
static const int s_iMaxSeekers = 3;
int iSeekers = pCombatDirector->CountPedsAdvancingWithSeek(*pTarget, s_iMaxSeekers);
if(iSeekers < s_iMaxSeekers)
{
//Check if we have not exceeded the desired seekers.
int iNumPedsWithClearLineOfSightOnFoot = CCombatManager::GetCombatTaskManager()->CountPedsWithClearLosToTarget(
*GetTargetPed(), CCombatTaskManager::CPWCLTTF_MustBeOnFoot);
int iDesiredSeekers = s_iMaxSeekers - iNumPedsWithClearLineOfSightOnFoot;
if(iSeekers < iDesiredSeekers)
{
return true;
}
}
}
return false;
}
int CTaskAdvance::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_MOVE_TO_TACTICAL_POINT:
{
return State_MoveToTacticalPoint;
}
default:
{
break;
}
}
}
//Do not keep the sub-task.
bKeepSubTask = false;
return State_Start;
}
CTask* CTaskAdvance::CreateSubTaskToUseDuringMovement() const
{
//Ensure the task is valid.
CTask* pSubTaskToUseDuringMovement = m_pSubTaskToUseDuringMovement;
if(!pSubTaskToUseDuringMovement)
{
return NULL;
}
return static_cast<CTask *>(pSubTaskToUseDuringMovement->Copy());
}
aiTask* CTaskAdvance::Copy() const
{
//Create the task.
CTaskAdvance* pTask = rage_new CTaskAdvance(m_Target, m_uConfigFlags);
//Set the sub-task to use during movement.
pTask->SetSubTaskToUseDuringMovement(CreateSubTaskToUseDuringMovement());
return pTask;
}
CTask::FSM_Return CTaskAdvance::ProcessPreFSM()
{
//Ensure the target ped is valid.
if(!GetTargetPed())
{
return FSM_Quit;
}
return FSM_Continue;
}
CTask::FSM_Return CTaskAdvance::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_MoveToTacticalPoint)
FSM_OnEnter
MoveToTacticalPoint_OnEnter();
FSM_OnUpdate
return MoveToTacticalPoint_OnUpdate();
FSM_State(State_Seek)
FSM_OnEnter
Seek_OnEnter();
FSM_OnUpdate
return Seek_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
CTask::FSM_Return CTaskAdvance::Start_OnUpdate()
{
//Check if a tactical analysis is active for the target.
if(CTacticalAnalysis::IsActive(*GetTargetPed()))
{
//Move to a tactical point.
SetState(State_MoveToTacticalPoint);
}
//Check if we can seek.
else if(CanSeek())
{
//Seek the entity.
SetState(State_Seek);
}
else
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
CTask::FSM_Return CTaskAdvance::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 CTaskAdvance::MoveToTacticalPoint_OnEnter()
{
//Check if we are resuming the state. Keep the sub-task, if possible.
if(GetSubTask() && (GetSubTask()->GetTaskType() == CTaskTypes::TASK_MOVE_TO_TACTICAL_POINT))
{
return;
}
//Clear the time since the last seek check.
m_fTimeSinceLastSeekCheck = 0.0f;
//Create the task.
CTaskMoveToTacticalPoint* pTask = rage_new CTaskMoveToTacticalPoint(m_Target, CTaskMoveToTacticalPoint::CF_DontQuitWhenPointIsReached);
//Set the callbacks.
pTask->SetScoreCoverPointCallback(CTaskAdvance::ScoreCoverPoint);
pTask->SetScoreNavMeshPointCallback(CTaskAdvance::ScoreNavMeshPoint);
//Set the time to wait at the position.
pTask->SetTimeToWaitAtPosition(sm_Tunables.m_TimeToWaitAtPosition);
//Set the time between point updates.
pTask->SetTimeBetweenPointUpdates(sm_Tunables.m_TimeBetweenPointUpdates);
//Set the sub-task to use during movement.
pTask->SetSubTaskToUseDuringMovement(CreateSubTaskToUseDuringMovement());
// Check if we should require points with LOS and set the flag if this is the case
const CPed* pPed = GetPed();
if(pPed->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_UseDefaultBlockedLosPositionAndDirection))
{
pTask->GetConfigFlags().SetFlag(CTaskMoveToTacticalPoint::CF_DisablePointsWithoutClearLos);
}
//Start the task.
SetNewTask(pTask);
}
CTask::FSM_Return CTaskAdvance::MoveToTacticalPoint_OnUpdate()
{
//Check if the sub-task has finished.
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Check if we can seek.
if(CanSeek())
{
//Move to the seek state.
SetState(State_Seek);
}
else
{
//Finish the task.
SetState(State_Finish);
}
}
else
{
//Check if we are at the tactical point.
CTaskMoveToTacticalPoint* pTask = static_cast<CTaskMoveToTacticalPoint *>(GetSubTask());
if(!pTask->IsMovingToPoint() || !pTask->DoesPointWeAreMovingToHaveClearLineOfSight())
{
//Check if the time has expired.
m_fTimeSinceLastSeekCheck += GetTimeStep();
if(m_fTimeSinceLastSeekCheck > sm_Tunables.m_TimeBetweenSeekChecksAtTacticalPoint)
{
//Clear the timer.
m_fTimeSinceLastSeekCheck = 0.0f;
//Check if we can seek.
if(CanSeek())
{
//Move to the seek state.
SetState(State_Seek);
}
}
}
}
return FSM_Continue;
}
void CTaskAdvance::Seek_OnEnter()
{
//Grab the ped.
CPed* pPed = GetPed();
//Grab the target ped.
const CPed* pTargetPed = GetTargetPed();
// Create the seek task, allow the ped to navigate
TTaskMoveSeekEntityLastNavMeshIntersection* pSeekTask = rage_new TTaskMoveSeekEntityLastNavMeshIntersection(pTargetPed);
if( !pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_DrownsInWater ) )
pSeekTask->SetAllowNavigationUnderwater(true);
// If we can only melee and are supposed to never lose our target then we don't want to pass the initial "is close enough" check in seek entity
if(pPed->GetPedIntelligence()->GetCombatBehaviour().GetTargetLossResponse() == CCombatData::TLR_NeverLoseTarget &&
CCombatBehaviour::GetCombatType(pPed) == CCombatBehaviour::CT_OnFootUnarmed)
{
pSeekTask->SetTargetRadius(0.0f);
}
//Never enter water.
pSeekTask->SetNeverEnterWater(true);
//Quit the task if a route is not found.
pSeekTask->SetQuitTaskIfRouteNotFound(true);
//Create the sub-task.
CTask* pSubTask = CreateSubTaskToUseDuringMovement();
CTaskComplexControlMovement* pTask = rage_new CTaskComplexControlMovement(pSeekTask, pSubTask);
pTask->SetAllowClimbLadderSubtask(true);
SetNewTask(pTask);
// Test for nearby friendlies
static dev_s32 siMaxNumPedsToTest = 8;
int iTestedPeds = 0;
const CEntityScannerIterator entityList = pPed->GetPedIntelligence()->GetNearbyPeds();
for(const CEntity* pEnt = entityList.GetFirst(); pEnt && iTestedPeds < siMaxNumPedsToTest; pEnt = entityList.GetNext(), iTestedPeds++)
{
const CPed * pNearbyPed = static_cast<const CPed*>(pEnt);
if (pNearbyPed->GetPedIntelligence()->IsFriendlyWith(*pPed)) // It's friendly to this ped
{
if(fwRandom::GetRandomTrueFalse())
{
pPed->NewSay("COVER_ME");
}
else
{
pPed->NewSay("MOVE_IN");
}
break;
}
}
}
CTask::FSM_Return CTaskAdvance::Seek_OnUpdate()
{
//Check if the target is outside the defensive area.
const CPed* pPed = GetPed();
if(pPed->GetPedIntelligence()->GetDefensiveArea()->IsActive() &&
!pPed->GetPedIntelligence()->GetDefensiveArea()->IsPointInArea(VEC3V_TO_VECTOR3(GetTargetPed()->GetTransform().GetPosition())))
{
//Finish the task.
SetState(State_Finish);
}
//Check if the sub-task has finished.
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}