495 lines
11 KiB
C++
495 lines
11 KiB
C++
// FILE : TaskBoatCombat.h
|
|
// PURPOSE : Subtask of combat used for boat combat
|
|
|
|
// File header
|
|
#include "Task/Combat/Subtasks/TaskBoatCombat.h"
|
|
|
|
// Rage headers
|
|
#include "ai/navmesh/pathserverbase.h"
|
|
#include "grcore/debugdraw.h"
|
|
|
|
// Game headers
|
|
#include "Peds/Ped.h"
|
|
#include "task/Combat/Subtasks/TaskBoatChase.h"
|
|
#include "task/Combat/Subtasks/TaskBoatStrafe.h"
|
|
#include "task/Combat/Subtasks/TaskVehicleCombat.h"
|
|
#include "task/Vehicle/TaskCar.h"
|
|
#include "vehicleAi/VehicleIntelligence.h"
|
|
#include "vehicleAi/task/TaskVehicleCruise.h"
|
|
#include "vehicleAi/task/TaskVehicleTempAction.h"
|
|
#include "vehicleAi/VehicleIntelligence.h"
|
|
#include "Vehicles/boat.h"
|
|
#include "Vehicles/vehicle.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
|
|
//=========================================================================
|
|
// CTaskBoatCombat
|
|
//=========================================================================
|
|
|
|
CTaskBoatCombat::Tunables CTaskBoatCombat::sm_Tunables;
|
|
IMPLEMENT_COMBAT_TASK_TUNABLES(CTaskBoatCombat, 0xb083954e);
|
|
|
|
CTaskBoatCombat::CTaskBoatCombat(const CAITarget& rTarget)
|
|
: m_CollisionProbeResults()
|
|
, m_LandProbeResults()
|
|
, m_NavMeshRouteSearchHelper()
|
|
, m_Target(rTarget)
|
|
, m_uRunningFlags(0)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_BOAT_COMBAT);
|
|
}
|
|
|
|
CTaskBoatCombat::~CTaskBoatCombat()
|
|
{
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CTaskBoatCombat::Debug() const
|
|
{
|
|
CTask::Debug();
|
|
}
|
|
|
|
const char* CTaskBoatCombat::GetStaticStateName(s32 iState)
|
|
{
|
|
Assert(iState >= State_Start && iState <= State_Finish);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_Start",
|
|
"State_Chase",
|
|
"State_Strafe",
|
|
"State_Wait",
|
|
"State_Finish"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
CBoat* CTaskBoatCombat::GetBoat() const
|
|
{
|
|
//Grab the ped.
|
|
const CPed* pPed = GetPed();
|
|
|
|
//Ensure the ped is in a vehicle.
|
|
CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//Ensure the vehicle is a boat.
|
|
if(!pVehicle->InheritsFromBoat())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return static_cast<CBoat *>(pVehicle);
|
|
}
|
|
|
|
void CTaskBoatCombat::ProcessProbes()
|
|
{
|
|
//Grab the boat.
|
|
const CBoat* pBoat = GetBoat();
|
|
|
|
//Grab the boat position.
|
|
Vec3V vBoatPosition = pBoat->GetTransform().GetPosition();
|
|
|
|
//Grab the boat velocity.
|
|
Vec3V vBoatVelocity = VECTOR3_TO_VEC3V(pBoat->GetVelocity());
|
|
vBoatVelocity.SetZ(ScalarV(V_ZERO));
|
|
|
|
//Calculate the look-ahead position.
|
|
Vec3V vLookAheadPosition = AddScaled(vBoatPosition, vBoatVelocity, ScalarVFromF32(sm_Tunables.m_TimeToLookAheadForCollision));
|
|
|
|
//Check if the collision probe is not waiting or ready.
|
|
if(!m_CollisionProbeResults.GetResultsWaitingOrReady())
|
|
{
|
|
#if DEBUG_DRAW
|
|
if(sm_Tunables.m_Rendering.m_Enabled && sm_Tunables.m_Rendering.m_CollisionProbe)
|
|
{
|
|
grcDebugDraw::Line(vBoatPosition, vLookAheadPosition, Color_red, -1);
|
|
}
|
|
#endif
|
|
|
|
//Create the probe.
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
probeDesc.SetResultsStructure(&m_CollisionProbeResults);
|
|
probeDesc.SetStartAndEnd(VEC3V_TO_VECTOR3(vBoatPosition), VEC3V_TO_VECTOR3(vLookAheadPosition));
|
|
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
|
|
|
|
//Start the test.
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
else
|
|
{
|
|
//Check if the probe is waiting.
|
|
if(m_CollisionProbeResults.GetWaitingOnResults())
|
|
{
|
|
//Nothing to do, we are waiting on the results.
|
|
}
|
|
else
|
|
{
|
|
//Update the flag.
|
|
m_uRunningFlags.ChangeFlag(RF_IsCollisionImminent, (m_CollisionProbeResults.GetNumHits() != 0));
|
|
|
|
//Reset the results.
|
|
m_CollisionProbeResults.Reset();
|
|
}
|
|
}
|
|
|
|
if(pBoat->GetIntelligence()->m_bSetBoatIgnoreLandProbes)
|
|
{
|
|
m_uRunningFlags.ChangeFlag(RF_IsLandImminent, false);
|
|
}
|
|
else
|
|
{
|
|
//Check if the land probe is not waiting or ready.
|
|
if(!m_LandProbeResults.GetResultsWaitingOrReady())
|
|
{
|
|
//Calculate the land position.
|
|
Vec3V vLandPosition = vLookAheadPosition;
|
|
vLandPosition.SetZf(vLookAheadPosition.GetZf() - sm_Tunables.m_DepthForLandProbe);
|
|
|
|
#if DEBUG_DRAW
|
|
if(sm_Tunables.m_Rendering.m_Enabled && sm_Tunables.m_Rendering.m_LandProbe)
|
|
{
|
|
grcDebugDraw::Line(vLookAheadPosition, vLandPosition, Color_red, -1);
|
|
}
|
|
#endif
|
|
|
|
//Create the probe.
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
probeDesc.SetResultsStructure(&m_LandProbeResults);
|
|
probeDesc.SetStartAndEnd(VEC3V_TO_VECTOR3(vLookAheadPosition), VEC3V_TO_VECTOR3(vLandPosition));
|
|
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
|
|
|
|
//Start the test.
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
else
|
|
{
|
|
//Check if the probe is waiting.
|
|
if(m_LandProbeResults.GetWaitingOnResults())
|
|
{
|
|
//Nothing to do, we are waiting on the results.
|
|
}
|
|
else
|
|
{
|
|
//Update the flag.
|
|
m_uRunningFlags.ChangeFlag(RF_IsLandImminent, (m_LandProbeResults.GetNumHits() != 0));
|
|
|
|
//Reset the results.
|
|
m_LandProbeResults.Reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTaskBoatCombat::ProcessTargetUnreachable()
|
|
{
|
|
//Check if the search is not active.
|
|
if(!m_NavMeshRouteSearchHelper.IsSearchActive())
|
|
{
|
|
//Grab the boat.
|
|
const CBoat* pBoat = GetBoat();
|
|
|
|
//Set the entity radius.
|
|
float fRadius = pBoat->GetBaseModelInfo()->GetBoundingSphereRadius();
|
|
m_NavMeshRouteSearchHelper.SetEntityRadius(fRadius);
|
|
|
|
//Grab the boat position.
|
|
Vec3V vBoatPosition = pBoat->GetTransform().GetPosition();
|
|
|
|
//Grab the target position.
|
|
Vector3 vTargetPosition;
|
|
m_Target.GetPosition(vTargetPosition);
|
|
|
|
//Generate the flags.
|
|
u64 uFlags = PATH_FLAG_USE_LARGER_SEARCH_EXTENTS|PATH_FLAG_NEVER_LEAVE_WATER;
|
|
|
|
//Start a search.
|
|
m_NavMeshRouteSearchHelper.StartSearch(GetPed(), VEC3V_TO_VECTOR3(vBoatPosition), vTargetPosition, 0.0f, uFlags);
|
|
}
|
|
else
|
|
{
|
|
//Check if the search is complete.
|
|
SearchStatus nSearchStatus;
|
|
float fDistance;
|
|
Vector3 vLastPoint;
|
|
if(m_NavMeshRouteSearchHelper.GetSearchResults(nSearchStatus, fDistance, vLastPoint))
|
|
{
|
|
//Keep track of whether the target is unreachable.
|
|
bool bIsTargetUnreachable = true;
|
|
|
|
//Check if the search was successful.
|
|
if(nSearchStatus == SS_SearchSuccessful)
|
|
{
|
|
bIsTargetUnreachable = false;
|
|
}
|
|
|
|
//Change the flag.
|
|
m_uRunningFlags.ChangeFlag(RF_IsTargetUnreachable, bIsTargetUnreachable);
|
|
|
|
//Reset the search.
|
|
m_NavMeshRouteSearchHelper.ResetSearch();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskBoatCombat::ShouldChase() const
|
|
{
|
|
//Ensure the entity is valid.
|
|
const CEntity* pEntity = m_Target.GetEntity();
|
|
if(!pEntity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the entity is a ped.
|
|
if(!pEntity->GetIsTypePed())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the ped.
|
|
const CPed* pPed = static_cast<const CPed *>(pEntity);
|
|
|
|
//Ensure the ped is in a vehicle.
|
|
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
|
|
if(!pVehicle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle speed exceeds the threshold.
|
|
float fSpeedSq = pVehicle->GetVelocity().Mag2();
|
|
float fMinSpeedSq = square(sm_Tunables.m_MinSpeedForChase);
|
|
if(fSpeedSq < fMinSpeedSq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskBoatCombat::ShouldStrafe() const
|
|
{
|
|
return !ShouldChase();
|
|
}
|
|
|
|
bool CTaskBoatCombat::ShouldWait() const
|
|
{
|
|
//Ensure our target is unreachable.
|
|
if(!m_uRunningFlags.IsFlagSet(RF_IsTargetUnreachable))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure a collision, or land, is imminent.
|
|
if(!m_uRunningFlags.IsFlagSet(RF_IsCollisionImminent) && !m_uRunningFlags.IsFlagSet(RF_IsLandImminent))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::ProcessPreFSM()
|
|
{
|
|
//Ensure the boat is valid.
|
|
if(!GetBoat())
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
//Ensure the target is valid.
|
|
if(!m_Target.GetIsValid())
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
//Process the probes.
|
|
ProcessProbes();
|
|
|
|
//Process whether the target is unreachable.
|
|
ProcessTargetUnreachable();
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
FSM_Begin
|
|
|
|
FSM_State(State_Start)
|
|
FSM_OnUpdate
|
|
return Start_OnUpdate();
|
|
|
|
FSM_State(State_Chase)
|
|
FSM_OnEnter
|
|
Chase_OnEnter();
|
|
FSM_OnUpdate
|
|
return Chase_OnUpdate();
|
|
|
|
FSM_State(State_Strafe)
|
|
FSM_OnEnter
|
|
Strafe_OnEnter();
|
|
FSM_OnUpdate
|
|
return Strafe_OnUpdate();
|
|
|
|
FSM_State(State_Wait)
|
|
FSM_OnEnter
|
|
Wait_OnEnter();
|
|
FSM_OnUpdate
|
|
return Wait_OnUpdate();
|
|
|
|
FSM_State(State_Finish)
|
|
FSM_OnUpdate
|
|
return FSM_Quit;
|
|
|
|
FSM_End
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::Start_OnUpdate()
|
|
{
|
|
//Check if we should chase.
|
|
if(ShouldChase())
|
|
{
|
|
//Move to the chase state.
|
|
SetState(State_Chase);
|
|
}
|
|
//Check if we should strafe.
|
|
else if(ShouldStrafe())
|
|
{
|
|
//Move to the strafe state.
|
|
SetState(State_Strafe);
|
|
}
|
|
else
|
|
{
|
|
taskAssertf(false, "The boat does not want to chase or strafe.");
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskBoatCombat::Chase_OnEnter()
|
|
{
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskBoatChase(m_Target);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::Chase_OnUpdate()
|
|
{
|
|
//Check if the target is unreachable.
|
|
if(ShouldWait())
|
|
{
|
|
//Move to the wait state.
|
|
SetState(State_Wait);
|
|
}
|
|
//Check if we should strafe.
|
|
else if(ShouldStrafe())
|
|
{
|
|
//Move to the strafe state.
|
|
SetState(State_Strafe);
|
|
}
|
|
//Check if the sub-task has finished.
|
|
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Finish the task.
|
|
SetState(State_Finish);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskBoatCombat::Strafe_OnEnter()
|
|
{
|
|
//TODO: There should probably be something controlling the radius,
|
|
// to ensure boats don't hit each other.
|
|
|
|
//Generate the radius.
|
|
float fRadius = fwRandom::GetRandomNumberInRange(15.0f, 25.0f);
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskBoatStrafe(m_Target, fRadius);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::Strafe_OnUpdate()
|
|
{
|
|
//Check if the target is unreachable.
|
|
if(ShouldWait())
|
|
{
|
|
//Move to the wait state.
|
|
SetState(State_Wait);
|
|
}
|
|
//Check if we should chase.
|
|
else if(ShouldChase())
|
|
{
|
|
//Move to the chase state.
|
|
SetState(State_Chase);
|
|
}
|
|
//Check if the sub-task has finished.
|
|
else if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Finish the task.
|
|
SetState(State_Finish);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskBoatCombat::Wait_OnEnter()
|
|
{
|
|
//Grab the boat.
|
|
CBoat* pBoat = GetBoat();
|
|
|
|
//Create the vehicle task.
|
|
u32 uTime = NetworkInterface::GetSyncedTimeInMilliseconds() + (u32)(sm_Tunables.m_TimeToWait * 1000.0f);
|
|
CTaskVehicleBrake* pVehicleTask = rage_new CTaskVehicleBrake(uTime);
|
|
pVehicleTask->SetUseHandBrake(true);
|
|
pVehicleTask->SetUseReverse(true);
|
|
|
|
//Create the sub-task.
|
|
CTask* pSubTask = rage_new CTaskVehicleCombat(&m_Target);
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskControlVehicle(pBoat, pVehicleTask, pSubTask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBoatCombat::Wait_OnUpdate()
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Check if we should chase.
|
|
if(ShouldChase())
|
|
{
|
|
//Move to the chase state.
|
|
SetState(State_Chase);
|
|
}
|
|
//Check if we should strafe.
|
|
else if(ShouldStrafe())
|
|
{
|
|
//Move to the strafe state.
|
|
SetState(State_Strafe);
|
|
}
|
|
else
|
|
{
|
|
//Finish the task.
|
|
SetState(State_Finish);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|