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

513 lines
18 KiB
C++

#include "TaskVehicleMissionBase.h"
// Framework headers
#include "math/vecmath.h"
// Game headers
#include "Vehicles/vehicle.h"
#include "VehicleAi/vehicleintelligence.h"
#include "VehicleAi/driverpersonality.h"
#include "debug/DebugScene.h"
#include "control/trafficLights.h"
#include "scene/world/gameWorld.h"
#include "control/record.h"
#include "peds/ped.h"
#include "peds/PedIntelligence.h"
#include "Task/Scenario/Types/TaskVehicleScenario.h"
#include "vehicleAi/task/TaskVehicleCruise.h"
#include "vehicles/vehiclepopulation.h"
#include "vehicles\virtualroad.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
FW_INSTANTIATE_CLASS_POOL(CTaskVehicleSerialiserBase, 1, atHashString("CTaskVehicleSerialiserBase",0x76f457ec));
CTaskVehicleMissionBase::Tunables CTaskVehicleMissionBase::sm_Tunables;
IMPLEMENT_VEHICLE_AI_TASK_TUNABLES(CTaskVehicleMissionBase, 0x3b6bb73c);
const float sVehicleMissionParams::DEFAULT_CRUISE_SPEED = 10.0f;
const float sVehicleMissionParams::DEFAULT_TARGET_REACHED_DIST = 4.0f;
const float sVehicleMissionParams::DEFAULT_SWITCH_TO_STRAIGHT_LINE_DIST = 20.0f;
const float CTaskVehicleMissionBase::MAX_CRUISE_SPEED = 120.0f;
//////////////////////////////////////////////////////////////////////////
CTaskVehicleMissionBase::CTaskVehicleMissionBase()
{
m_bSoftResetRequested = false;
m_bMissionAchieved = false;
}
CTaskVehicleMissionBase::CTaskVehicleMissionBase(const sVehicleMissionParams& params)
{
m_bSoftResetRequested = false;
m_bMissionAchieved = false;
m_Params = params;
#if (!(HACK_GTA4) || (HACK_RDR3)) && (__ASSERT)
if (!GetTargetEntity())
{
Assertf(m_Params.GetTargetPosition().x >= WORLDLIMITS_XMIN && m_Params.GetTargetPosition().x <= WORLDLIMITS_XMAX, "invalid target X position %.2f", m_Params.GetTargetPosition().x);
Assertf(m_Params.GetTargetPosition().y >= WORLDLIMITS_YMIN && m_Params.GetTargetPosition().y <= WORLDLIMITS_YMAX, "invalid target Y position %.2f", m_Params.GetTargetPosition().y);
Assertf(m_Params.GetTargetPosition().z >= WORLDLIMITS_ZMIN && m_Params.GetTargetPosition().z <= WORLDLIMITS_ZMAX, "invalid target Z position %.2f", m_Params.GetTargetPosition().z);
}
#endif
#if __ASSERT
Assertf(m_Params.m_fCruiseSpeed >= 0.0f, "Desired cruise speed is negative! Use DF_DriveInReverse instead");
#endif //__ASSERT
#if __ASSERT
Assertf(m_Params.m_fCruiseSpeed < MAX_CRUISE_SPEED,"Trying to set cruise speed to %.2f, but MAX allowed is %.2f", m_Params.m_fCruiseSpeed, MAX_CRUISE_SPEED);
#endif //__ASSERT
m_Params.m_fCruiseSpeed = rage::Min(m_Params.m_fCruiseSpeed, MAX_CRUISE_SPEED);
}
//////////////////////////////////////////////////////////////////////////
CTaskVehicleMissionBase::~CTaskVehicleMissionBase()
{
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindTargetCoors
// PURPOSE : Finds the target coors for this mission. If there is an entity
// it will be those coordinates. If there isn't it will be the actual
// coordinates.
// RETURNS :
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleMissionBase::FindTargetCoors(const CVehicle *pVeh, sVehicleMissionParams &params) const
{
Vector3 TargetCoors;
FindTargetCoors(pVeh, TargetCoors);
params.SetTargetPosition(TargetCoors);
}
void CTaskVehicleMissionBase::FindTargetCoors(const CVehicle* UNUSED_PARAM(pVeh), Vector3 &TargetCoors) const
{
if (GetTargetEntity() && !IsDrivingFlagSet(DF_TargetPositionOverridesEntity))
{
TargetCoors = VEC3V_TO_VECTOR3(GetTargetEntity()->GetTransform().GetPosition());
Assertf(TargetCoors.x >= WORLDLIMITS_XMIN && TargetCoors.x <= WORLDLIMITS_XMAX, "Target Entity has an invalid target X position %.2f on a %s", TargetCoors.x, GetTaskName());
Assertf(TargetCoors.y >= WORLDLIMITS_YMIN && TargetCoors.y <= WORLDLIMITS_YMAX, "Target Entity has an invalid target Y position %.2f on a %s", TargetCoors.y, GetTaskName());
Assertf(TargetCoors.z >= WORLDLIMITS_ZMIN && TargetCoors.z <= WORLDLIMITS_ZMAX, "Target Entity has an invalid target Z position %.2f on a %s", TargetCoors.z, GetTaskName());
}
else
{
TargetCoors = m_Params.GetTargetPosition();
Assertf(TargetCoors.x >= WORLDLIMITS_XMIN && TargetCoors.x <= WORLDLIMITS_XMAX, "Target has invalid target X position %.2f on a %s", TargetCoors.x, GetTaskName());
Assertf(TargetCoors.y >= WORLDLIMITS_YMIN && TargetCoors.y <= WORLDLIMITS_YMAX, "Target has invalid target Y position %.2f on a %s", TargetCoors.y, GetTaskName());
Assertf(TargetCoors.z >= WORLDLIMITS_ZMIN && TargetCoors.z <= WORLDLIMITS_ZMAX, "Target has invalid target Z position %.2f on a %s", TargetCoors.z, GetTaskName());
}
}
void CTaskVehicleMissionBase::SetTargetArriveDist(float arrivalDist)
{
m_Params.m_fTargetArriveDist = arrivalDist;
}
//////////////////////////////////////////////////////////////////////////
void CTaskVehicleMissionBase::SetCruiseSpeed(float cruiseSpeed)
{
if (m_Params.m_fMaxCruiseSpeed > 0.0f)
{
cruiseSpeed = rage::Min(cruiseSpeed, m_Params.m_fMaxCruiseSpeed);
}
Assertf(cruiseSpeed >= 0.0f, "Desired cruise speed is negative! Use DF_DriveInReverse instead");
Assertf(cruiseSpeed < MAX_CRUISE_SPEED,"Trying to set cruise speed to %.2f, but MAX allowed is %.2f", cruiseSpeed, MAX_CRUISE_SPEED);
cruiseSpeed = rage::Min(cruiseSpeed, MAX_CRUISE_SPEED);
//if(m_Params.m_fCruiseSpeed != cruiseSpeed)
//{
m_Params.m_fCruiseSpeed = cruiseSpeed;
//}
}
//////////////////////////////////////////////////////////////////////////
void CTaskVehicleMissionBase::SetMaxCruiseSpeed(float maxCruiseSpeed)
{
//if(m_Params.m_fMaxCruiseSpeed != maxCruiseSpeed)
//{
m_Params.m_fMaxCruiseSpeed = maxCruiseSpeed;
//}
}
//////////////////////////////////////////////////////////////////////////
const CPhysical* CTaskVehicleMissionBase::GetTargetEntity() const
{
return static_cast<const CPhysical*>(m_Params.GetTargetEntity().GetEntity());
}
///////////////////////////////////////////////////////////////////////////
// set the target position to 0,0,0 if no target is set
///////////////////////////////////////////////////////////////////////////
void CTaskVehicleMissionBase::SetTargetPosition(const Vector3 *pTargetPosition)
{
if( pTargetPosition )
{
#if __ASSERT
//SetTargetPosition can be called before the m_pEntity pointer is fixed up
if (GetEntity())
{
Assertf(pTargetPosition->x >= WORLDLIMITS_XMIN && pTargetPosition->x <= WORLDLIMITS_XMAX, "Trying to set an invalid target X position %.2f on a %s. Veh position: %.2f, %.2f, %.2f"
, pTargetPosition->x, GetTaskName(), GetVehicle()->GetVehiclePosition().GetXf(), GetVehicle()->GetVehiclePosition().GetYf(), GetVehicle()->GetVehiclePosition().GetZf());
Assertf(pTargetPosition->y >= WORLDLIMITS_YMIN && pTargetPosition->y <= WORLDLIMITS_YMAX, "Trying to set an invalid target Y position %.2f on a %s. Veh position: %.2f, %.2f, %.2f"
, pTargetPosition->y, GetTaskName(), GetVehicle()->GetVehiclePosition().GetXf(), GetVehicle()->GetVehiclePosition().GetYf(), GetVehicle()->GetVehiclePosition().GetZf());
Assertf(pTargetPosition->z >= WORLDLIMITS_ZMIN && pTargetPosition->z <= WORLDLIMITS_ZMAX, "Trying to set an invalid target Z position %.2f on a %s. Veh position: %.2f, %.2f, %.2f"
, pTargetPosition->z, GetTaskName(), GetVehicle()->GetVehiclePosition().GetXf(), GetVehicle()->GetVehiclePosition().GetYf(), GetVehicle()->GetVehiclePosition().GetZf());
}
#endif //__ASSERT
m_Params.SetTargetPosition(*pTargetPosition);
}
else
{
m_Params.ClearTargetPosition();
}
}
//////////////////////////////////////////////////////////////////////////
const Vector3 *CTaskVehicleMissionBase::GetTargetPosition()
{
return &m_Params.GetTargetPosition();
}
bool CTaskVehicleMissionBase::HasMovingTarget() const
{
return GetTargetEntity() && GetTargetEntity()->GetIsPhysical();
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindTargetVehicle
// PURPOSE : Finds the target vehicle for this mission. If the target is a ped in a
// vehicle this is what will be returned.
// RETURNS :
/////////////////////////////////////////////////////////////////////////////////
CVehicle *CTaskVehicleMissionBase::FindTargetVehicle() const
{
const CPhysical* pTargetEntity = GetTargetEntity();
if (pTargetEntity)
{
if (pTargetEntity->GetIsTypeVehicle())
{
return (CVehicle *)pTargetEntity;
}
if (pTargetEntity->GetIsTypePed())
{
CPed *pPed = (CPed*)pTargetEntity;
if (pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
return pPed->GetMyVehicle();
}
}
}
return NULL;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindVehicleWithPhysical
// PURPOSE : If the physical passed in is a ped; this function returns the car he
// is in. Otherwise the same physical is returned.
/////////////////////////////////////////////////////////////////////////////////
const CPhysical *CTaskVehicleMissionBase::FindVehicleWithPhysical(const CPhysical *pPhysical) const
{
Assert(pPhysical);
if(pPhysical->GetIsTypePed())
{
CPed *pPed =(CPed *)pPhysical;
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPed->GetMyVehicle())
{
return pPed->GetMyVehicle();
}
}
return pPhysical;
}
////////////////////////////////////////////////////
// FUNCTION: HumaniseCarControlInput
// Takes the controls the AI has decided on and smooths them out.
// In the case of conservative driving they can also be clipped.
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::HumaniseCarControlInput(CVehicle* pVeh, CVehControls* pVehControls, bool bConservativeDriving, bool bGoingSlowly)
{
Assertf(pVeh, "HumaniseCarControlInput expected a valid set vehicle.");
Assertf(pVehControls, "HumaniseCarControlInput expected a valid set of vehicle controls.");
Assertf(pVeh->GetSteerAngle() == pVeh->GetSteerAngle(), "Actual vehicle steer angle is NaN");
if(!bConservativeDriving && !bGoingSlowly)
{
pVeh->SetSteerAngle(pVehControls->m_steerAngle);
pVeh->SetThrottle(pVehControls->m_throttle);
pVeh->SetBrake(pVehControls->m_brake);
pVeh->SetHandBrake(pVehControls->m_handBrake);
return;
}
// Humanize the steering
const float steerAngleDiff = pVehControls->m_steerAngle - pVeh->GetSteerAngle();
const float maxsteerAngleChange = ((bConservativeDriving)?2.0f:4.0f) * fwTimer::GetTimeStep();
if(rage::Abs(steerAngleDiff) >= maxsteerAngleChange)
{
if(steerAngleDiff > 0.0f)
{
pVehControls->SetSteerAngle( pVeh->GetSteerAngle() + maxsteerAngleChange );
}
else
{
pVehControls->SetSteerAngle( pVeh->GetSteerAngle() - maxsteerAngleChange );
}
}
// Check if we should bother humanizing the gas and brakes.
if(bConservativeDriving)
{
// Humanize the brakes.
const float brakeDiff = pVehControls->m_brake - pVeh->GetBrake();
const float maxBrakeChange = ((brakeDiff > 0.0f)?10.0f:1.0f) * fwTimer::GetTimeStep();
if(rage::Abs(brakeDiff) >= maxBrakeChange)
{
if(brakeDiff > 0.0f)
{
pVehControls->m_brake = pVeh->GetBrake() + maxBrakeChange;
}
else
{
pVehControls->m_brake = pVeh->GetBrake() - maxBrakeChange;
}
}
if(pVehControls->m_throttle > 0.0f)//only worry about fiddling with the throttle if we're not reversing
{
// Humanize the gas.
// Sensible cars don't go flat out.
const float gassDiff = pVehControls->m_throttle - pVeh->GetThrottle();
const float maxGasChange = ((gassDiff > 0.0f)?1.0f:10.0f) * fwTimer::GetTimeStep();
if(rage::Abs(gassDiff) >= maxGasChange)
{
if(gassDiff > 0.0f)
{
pVehControls->m_throttle = pVeh->GetThrottle() + maxGasChange;
}
else
{
pVehControls->m_throttle = pVeh->GetThrottle() - maxGasChange;
}
}
// Add a little bit to the max gas allowed if we're on an upward slope.
float maxGas = CDriverPersonality::FindMaxAcceleratorInput(pVeh->GetDriver(), pVeh);
maxGas += rage::Max(0.0f, (4.0f * pVeh->GetTransform().GetB().GetZf()));
pVehControls->m_throttle = rage::Min(pVehControls->m_throttle, maxGas);
}
}
pVeh->SetSteerAngle(pVehControls->m_steerAngle);
pVeh->SetThrottle(pVehControls->m_throttle);
pVeh->SetBrake(pVehControls->m_brake);
pVeh->SetHandBrake(pVehControls->m_handBrake);
}
////////////////////////////////////////////////////
// FUNCTION: ReadTaskData
// Reads the task's data from a network update
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::ReadTaskData(datBitBuffer &messageBuffer)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->ReadTaskData(this, messageBuffer);
delete pTaskSerialiser;
}
}
}
////////////////////////////////////////////////////
// FUNCTION: WriteTaskData
// Writes the task's data for a network update
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::WriteTaskData(datBitBuffer &messageBuffer)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->WriteTaskData(this, messageBuffer);
delete pTaskSerialiser;
}
}
}
////////////////////////////////////////////////////
// FUNCTION: LogTaskData
// Logs the task's data for a network update
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::LogTaskData(netLoggingInterface &log)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->LogTaskData(this, log);
delete pTaskSerialiser;
}
}
}
////////////////////////////////////////////////////
// FUNCTION: ReadTaskMigrationData
// Reads the task's migration data from a network migration event
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::ReadTaskMigrationData(datBitBuffer &messageBuffer)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->ReadTaskMigrationData(this, messageBuffer);
delete pTaskSerialiser;
}
}
}
////////////////////////////////////////////////////
// FUNCTION: WriteTaskMigrationData
// Writes the task's migration data for a network migration event
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::WriteTaskMigrationData(datBitBuffer &messageBuffer)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->WriteTaskMigrationData(this, messageBuffer);
delete pTaskSerialiser;
}
}
}
////////////////////////////////////////////////////
// FUNCTION: LogTaskMigrationData
// Logs the task's migration data for a network migration event
////////////////////////////////////////////////////
void CTaskVehicleMissionBase::LogTaskMigrationData(netLoggingInterface &log)
{
if (IsSyncedAcrossNetwork())
{
CTaskVehicleSerialiserBase* pTaskSerialiser = GetTaskSerialiser();
if (pTaskSerialiser)
{
pTaskSerialiser->LogTaskMigrationData(this, log);
delete pTaskSerialiser;
}
}
}
void CTaskVehicleMissionBase::SetupAfterMigration(CVehicle *pVehicle)
{
if(taskVerifyf(pVehicle, "Invalid vehicle!") &&
taskVerifyf(pVehicle->GetNetworkObject(), "Calling SetupAfterMigration on a non-networked vehicle!"))
{
if(GetTaskType() != CTaskTypes::TASK_VEHICLE_CRUISE_NEW)
{
CopyNodeList(&static_cast<CNetObjVehicle*>(pVehicle->GetNetworkObject())->GetVehicleNodeList());
CVehicleNodeList *pNodeList = GetNodeList();
if(pNodeList)
{
CVehicleFollowRouteHelper *pFollowRouteHelper = const_cast<CVehicleFollowRouteHelper *>(GetFollowRouteHelper());
if(pFollowRouteHelper)
{
bool bUpdateLanesForTurns = false;
const CPathNodeRouteSearchHelper* pRouteSearchHelper = GetRouteSearchHelper();
if (pRouteSearchHelper && (!pRouteSearchHelper->IsDoingWanderRoute() || pRouteSearchHelper->ShouldAvoidTurns()))
{
bUpdateLanesForTurns = true;
}
pFollowRouteHelper->ConstructFromNodeList(pVehicle, *pNodeList, bUpdateLanesForTurns);
// ensure we have constructed a valid route
//taskAssertf(pFollowRouteHelper->GetNumPoints() > 0 || pNodeList->FindNumPathNodes() == 0
// , "Route with no points generated, but we had some in the NodeList!");
#if __ASSERT
for(unsigned index = 0; index < pFollowRouteHelper->GetNumPoints(); index++)
{
const CRoutePoint& rPoint1 = pFollowRouteHelper->GetRoutePoints()[index];
Vector3 node1Coors = VEC3V_TO_VECTOR3(rPoint1.GetPosition());
Assertf(node1Coors.x >= WORLDLIMITS_XMIN && node1Coors.x <= WORLDLIMITS_XMAX &&
node1Coors.y >= WORLDLIMITS_YMIN && node1Coors.y <= WORLDLIMITS_YMAX &&
node1Coors.z >= WORLDLIMITS_ZMIN && node1Coors.z <= WORLDLIMITS_ZMAX,
"Route generated with invalid points!");
}
#endif // __ASSERT
}
}
}
}
}
bool CTaskVehicleMissionBase::GetShouldObeyTrafficLights()
{
return IsDrivingFlagSet(DF_StopAtLights);
}
bool CTaskVehicleMissionBase::ShouldTurnOffEngineAndLights(const CVehicle* pVehicle)
{
bool bTurnOffEngineAndLights = true;
if (pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence())
{
CTaskUseVehicleScenario* pTask = static_cast<CTaskUseVehicleScenario*>(pVehicle->GetDriver()->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_USE_VEHICLE_SCENARIO));
if (pTask)
{
CScenarioPoint* pPoint = pTask->GetScenarioPoint();
if (pPoint)
{
float fTimeUntilPedLeaves = pPoint->GetTimeTillPedLeaves();
if (fTimeUntilPedLeaves >= 0.0f && fTimeUntilPedLeaves < sm_Tunables.m_MinTimeToKeepEngineAndLightsOnWhileParked)
{
bTurnOffEngineAndLights = false;
}
}
}
}
return bTurnOffEngineAndLights;
}