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

11468 lines
469 KiB
C++

#include "TaskVehicleGoToAutomobile.h"
#include "math/vecmath.h"
#include "math/angmath.h"
#include "phbound/BoundComposite.h"
// Framework headers
#include "fwdebug/vectormap.h"
#include "fwmaths/geomutil.h"
// Game headers
#include "Vehicles/Planes.h"
#include "Vehicles/vehicle.h"
#include "Vehicles/vehiclepopulation.h"
#include "Vehicles/virtualroad.h"
#include "Vehicles/Trailer.h"
#include "debug/DebugScene.h"
#include "debug/DebugRecorder.h"
#include "game/ModelIndices.h"
#include "Task/Physics/TaskNMBrace.h"
#include "Peds/ped.h"
#include "event/eventmovement.h"
#include "ModelInfo/ModelInfo.h"
#include "ModelInfo/ModelInfo_Factories.h"
#include "Objects/Door.h"
#include "Objects/DoorTuning.h"
#include "scene/world/gameWorld.h"
#include "physics/PhysicsHelpers.h"
#include "physics/Physics.h"
#include "Peds/PedIntelligence.h"
#include "Vehicles/metadata/AIHandlingInfo.h"
#include "VehicleAi/driverpersonality.h"
#include "VehicleAi/task/TaskVehicleThreePointTurn.h"
#include "VehicleAi/task/TaskVehicleGoTo.h"
#include "VehicleAi/task/TaskVehicleCruise.h"
#include "VehicleAi/task/TaskVehicleMissionBase.h"
#include "VehicleAi/task/TaskVehiclePark.h"
#include "VehicleAi/task/TaskVehicleTempAction.h"
#include "VehicleAi/task/TaskVehicleEscort.h"
#include "vehicleAi/task/DeferredTasks.h"
#include "VehicleAi/VehicleIntelligence.h"
#include "control/trafficLights.h"
#include "task/Movement/TaskMoveWander.h"
#include "Task/Scenario/Info/ScenarioInfo.h"
#include "Task/Scenario/ScenarioManager.h"
#include "Task/Movement/TaskCollisionResponse.h"
#include "Task/Vehicle/TaskCar.h"
#include "Task/Vehicle/TaskVehicleWeapon.h"
#include "peds/Ped.h"
#include "peds/PedDebugVisualiser.h"
#include "phbound/BoundBox.h"
#include "renderer/HierarchyIds.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
VEHICLE_OPTIMISATIONS()
// TODO: Clean up the code based on the current values of these #defines, at some point.
#define USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE 1
#define USE_FOLLOWROUTE_EDGES_FOR_AVOIDANCE 1
Vector2 CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_vAvoidXExtents(1.5f*PED_HUMAN_RADIUS, 0.0f);
Vector2 CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_vNmXExtents(0.4f*PED_HUMAN_RADIUS, 0.75f);
Vector2 CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_vNmVelocityScale(0.25f, 0.5f);
//dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fChangeLanesVelocityRatio = 1.25f;
//u32 CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_TimeBeforeOvertake = 0;
//u32 CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_TimeBeforeUndertake = 1000;
dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fAVOIDANCE_LOOKAHEAD_TIME = 1.7f;
dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fAVOIDANCE_LOOKAHEAD_TIME_FOR_FAST_VEHICLES = 3.0f;
dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fExtraWidthForBikes = 0.75f;
dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fExtraWidthForCars = 0.75f;
dev_float CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_fMinLengthForDoors = 0.33f;
int CTaskVehicleGoToPointWithAvoidanceAutomobile::ThreePointTurnInfo::ms_iMaxAttempts = 3;
#if __BANK
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_bEnableNewAvoidanceDebugDraw = false;
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_bDisplayObstructionProbeScores = false;
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ms_bDisplayAvoidanceRoadSegments = false;
#endif //__BANK
PF_PAGE(GTA_LaneChange, "GTA LaneChange");
PF_GROUP(GTA_LANES);
PF_LINK(GTA_LaneChange, GTA_LANES);
PF_TIMER(AI_LaneChange, GTA_LANES);
PF_PAGE(GTA_VehicleAvoidance, "GTA Vehicle Avoidance");
PF_GROUP(GTA_VEHICLE_AVOIDANCE);
PF_LINK(GTA_VehicleAvoidance, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_Avoidance, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceShouldWaitForTrafficBeforeTurningLeft, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceTailgateCarInFront, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceFindAngleToWeaveThroughTraffic, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceFindAngleToAvoidBuildings, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceFindMaximumSpeedForThisCarInTraffic, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceSlowCarForOtherCar, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceFindMaximumSpeedForRacingCar, GTA_VEHICLE_AVOIDANCE);
PF_TIMER(AI_AvoidanceNewSearchForUnobstructedDirections, GTA_VEHICLE_AVOIDANCE);
static const ScalarV s_vScalarZero(V_ZERO);
static const int s_iNumObstructionProbes = 30;
static const int kMaxObstructionProbeDirections = ((s_iNumObstructionProbes + 3) & ~3);
/////////////////////////////////////////////////////////////////////////////////
// CTaskVehicleGoToPointWithAvoidanceAutomobile
/////////////////////////////////////////////////////////////////////////////////
CTaskVehicleGoToPointWithAvoidanceAutomobile::Tunables CTaskVehicleGoToPointWithAvoidanceAutomobile::sm_Tunables;
IMPLEMENT_VEHICLE_AI_TASK_TUNABLES(CTaskVehicleGoToPointWithAvoidanceAutomobile, 0x6f61cca2);
/////////////////////////////////////////////////////////////////
CTaskVehicleGoToPointWithAvoidanceAutomobile::CTaskVehicleGoToPointWithAvoidanceAutomobile(
const sVehicleMissionParams& params,
const bool bAllowThreePointTurns,
const bool bAllowAchieveMission ) :
CTaskVehicleGoTo(params)
{
m_fSmallestCollisionTSoFar = 99999.9f;
m_startTimeAvoidCarsUntilClear = 0;
m_SteeringTargetPos = params.GetTargetPosition();//copy the target pos.
m_pOptimization_pLastCarBlockingUs = NULL;
m_pOptimization_pLastCarWeSlowedFor = NULL;
m_pOptimization_pVehicleTooCloseToTailgate = NULL;
m_bStoppingForTraffic = false;
m_bSlowingDownForCar = false;
m_bSlowingDownForPed = false;
m_bReEnableSlowForCarsWhenDoneAvoiding = false;
//m_bShouldChangeLanesForTraffic = false;
//m_bWaitingToOvertakeThisFrame = false;
//m_bWaitingToUndertakeThisFrame = false;
m_bAvoidedLeftLastFrame = false;
m_bAvoidedRightLastFrame = false;
m_bAllowThreePointTurns = bAllowThreePointTurns;
m_fAvoidedEntityCounter = 0.0f;
m_fMostImminentCollisionThisFrame = FLT_MAX;
m_fAvoidanceMarginForOtherLawEnforcementVehicles = -1.0f;
m_bWaitingForPlayerPed = false;
m_startTimeWaitingForPlayerPed = 0;
m_lastTimeHonkedAtAnyPed = 0;
m_bSlowlyPushingPlayerThisFrame = false;
m_vCachedGiveWarningPosition.Zero();
m_bAllowAchieveMission = bAllowAchieveMission;
m_bAvoidingCarsUntilClear = false;
m_bPathContainsUTurn = false;
m_bOvertakeCurrentCarRequested = false;
m_bReverseBeforeOvertakeCurrentCarRequested = false;
//m_NextTimeAllowedToChangeLanesForTraffic = 0;
//m_TimeWaitingForOvertake = 0;
//m_TimeWaitingForUndertake = 0;
m_iTargetSideFlags = 0;
SetDrivingFlag(DF_TargetPositionOverridesEntity, true);
//Assertf(m_Params.m_vTargetPos.Dist2(ORIGIN) > SMALL_FLOAT, "Low-level drive task being told to drive to the origin. You probably don't want this.");
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_GOTO_POINT_WITH_AVOIDANCE_AUTOMOBILE);
}
/////////////////////////////////////////////////////////////////
CTaskVehicleGoToPointWithAvoidanceAutomobile::~CTaskVehicleGoToPointWithAvoidanceAutomobile()
{
if (GetVehicle())
{
GetVehicle()->m_nVehicleFlags.bIsWaitingToTurnLeft = false;
GetVehicle()->m_nVehicleFlags.bWillTurnLeftAgainstOncomingTraffic = false;
}
}
/////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
pVehicle->m_nVehicleFlags.bIsWaitingToTurnLeft = false;
pVehicle->m_nVehicleFlags.bWillTurnLeftAgainstOncomingTraffic = false;
m_bBumperInsideBoundsThisFrame = false;
//if (pVehicle->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle)
// if (m_bWaitingForPlayerPed)
// {
// Vec3V vAboveVehPos = pVehicle->GetVehiclePosition();
// vAboveVehPos.SetZf(vAboveVehPos.GetZf() + 10.0f);
// CVehicle::ms_debugDraw.AddLine(pVehicle->GetVehiclePosition(), vAboveVehPos, Color_green);
// }
///////////////////////
FSM_Begin
// Initial state
FSM_State(State_GoToPoint)
FSM_OnEnter
GoToPointWithAvoidance_OnEnter(pVehicle);
FSM_OnUpdate
return GoToPointWithAvoidance_OnUpdate(pVehicle);
//
FSM_State(State_ThreePointTurn)
FSM_OnEnter
ThreePointTurn_OnEnter(pVehicle);
FSM_OnUpdate
return ThreePointTurn_OnUpdate(pVehicle);
FSM_OnExit
return ThreePointTurn_OnExit(pVehicle);
//
FSM_State(State_WaitForTraffic)
FSM_OnEnter
WaitForTraffic_OnEnter(pVehicle);
FSM_OnUpdate
return Wait_OnUpdate(pVehicle);
//
FSM_State(State_WaitForPed)
FSM_OnEnter
WaitForPed_OnEnter(pVehicle);
FSM_OnUpdate
return Wait_OnUpdate(pVehicle);
//
FSM_State(State_TemporarilyBrake)
FSM_OnEnter
TemporarilyBrake_OnEnter(pVehicle);
FSM_OnUpdate
return TemporarilyBrake_OnUpdate(pVehicle);
FSM_State(State_Swerve)
FSM_OnEnter
SwerveForHeadOnCollision_OnEnter(pVehicle);
FSM_OnUpdate
return SwerveForHeadOnCollision_OnUpdate(pVehicle);
FSM_End
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::CheckIsAboutToMakeSharpTurn(const CVehicle& veh, Vec3V_In tgtPos) const
{
const CVehicle* pVehicle = &veh;
// Note: when this check was done in CTaskVehicleCruiseNew, we used the driving flag from that task,
// while now we use the driving flags from CTaskVehicleGoToPointWithAvoidanceAutomobile. Need to
// confirm that we deal with DF_DriveInReverse properly.
const Vec3V svZero(V_ZERO);
const Vec3V vDriveDir = CVehicleFollowRouteHelper::GetVehicleDriveDir(pVehicle, IsDrivingFlagSet(DF_DriveInReverse));
const Vec3V vSteeringPos = CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, IsDrivingFlagSet(DF_DriveInReverse));
const ScalarV maxSteeringAngleV(pVehicle->GetIntelligence()->FindMaxSteerAngle());
// Compute the two vectors, flat.
const Vec2V vec1FlatV = vDriveDir.GetXY();
const Vec2V vec2FlatV = Subtract(tgtPos.GetXY(), vSteeringPos.GetXY());
// Compute the dot product.
const ScalarV dotNonNormV = Dot(vec1FlatV, vec2FlatV);
// Compute the inverse of the product of the two magnitudes.
const ScalarV invMagsSqV = InvSqrtFast(Scale(MagSquared(vec1FlatV), MagSquared(vec2FlatV)));
// Compute what the dot product between the normalized vectors would have been.
const ScalarV dotV = Scale(dotNonNormV, invMagsSqV);
if(IsLessThanAll(dotV, maxSteeringAngleV))
{
return true;
}
return false;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::ProcessSuperDummy(CVehicle* pVehicle, float fTimeStep) const
{
if(pVehicle && pVehicle->m_nVehicleFlags.bTasksAllowDummying && pVehicle->GetVehicleAiLod().GetDummyMode() == VDM_SUPERDUMMY )
{
CVehicleFollowRouteHelper* pFollowRoute = pVehicle->GetIntelligence()->GetFollowRouteHelperMutable();
if(pFollowRoute)
{
pFollowRoute->ProcessSuperDummy( pVehicle, ScalarV( GetCruiseSpeed() ), GetDrivingFlags(), fTimeStep );
}
}
}
float CTaskVehicleGoToPointWithAvoidanceAutomobile::CalculateOtherVehicleMarginForAvoidance(const CVehicle& rVehicle, const CVehicle& rOtherVehicle, bool bGiveWarning) const
{
//Check if the other vehicle is a law enforcement vehicle.
float fMargin = 0.0f;
if(rOtherVehicle.IsLawEnforcementVehicle())
{
//Check if we don't have an override.
//Give cops some space, by default.
if(m_fAvoidanceMarginForOtherLawEnforcementVehicles < 0.0f)
{
fMargin = 0.5f;
}
else
{
fMargin = m_fAvoidanceMarginForOtherLawEnforcementVehicles;
}
}
//if we have a trailer or are a bike, or the other vehicle is a cop, give the other guy some space
else if ((bGiveWarning && rOtherVehicle.GetDriver() && rOtherVehicle.GetDriver()->IsPlayer())
|| rVehicle.InheritsFromBike() || rVehicle.InheritsFromBoat())
{
fMargin = 0.5f;
}
else if (rVehicle.GetAttachedTrailer())
{
fMargin = 0.25f;
}
else if (rOtherVehicle.m_nVehicleFlags.bIsThisAParkedCar)
{
fMargin = 0.5f;
}
//if the other vehicle is on fire, REALLY avoid it
if (rOtherVehicle.IsOnFire())
{
fMargin = rage::Max(0.75f, fMargin);
}
//add any additional buffer we've been given for this target
const CPhysical* pOtherAvoidanceTarget = rVehicle.GetIntelligence()->GetAvoidanceCache().m_pTarget;
if(&rOtherVehicle == pOtherAvoidanceTarget)
{
fMargin += rVehicle.GetIntelligence()->GetAvoidanceCache().m_fAdditionalBuffer;
}
return fMargin;
}
/////////////////////////////////////////////////////////////////////////////////
// State_GoTo
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::GoToPointWithAvoidance_OnEnter(CVehicle* UNUSED_PARAM(pVehicle))
{
m_iTargetSideFlags = 0;
m_vCachedGiveWarningPosition.Zero();
m_bStoppingForTraffic = false;
//SetNewTask( rage_new CTaskVehicleGoToPointAutomobile( (DrivingModeType) m_iDrivingStyle, GetCruiseSpeed(), &m_vTargetPos, m_missionFlags ));
SetNewTask( rage_new CTaskVehicleGoToPointAutomobile( m_Params));
//Assertf(m_Params.m_TargetEntity.GetEntity() == NULL, "Entity Target being passed down to a low-level drive task that requires a position.");
Assertf(m_Params.GetTargetPosition().Dist2(ORIGIN) > SMALL_FLOAT, "Low-level drive task being told to drive to the origin. You probably don't want this.");
//SetNewTask( rage_new CTaskVehicleGoToPointAutomobile( (DrivingModeType) m_iDrivingStyle, GetCruiseSpeed(), &m_vTargetPos, m_missionFlags.GetAllFlags() ));
}
/////////////////////////////////////////////////////////////////////////////////
#if __STATS
EXT_PF_TIMER(VehicleAvoidanceTask_Run);
#endif // __STATS
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::GoToPointWithAvoidance_OnUpdate(CVehicle* pVehicle)
{
const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
const Vector3 vVehicleSteeringPos = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, IsDrivingFlagSet(DF_DriveInReverse), true));
const float fDistToTargetSq = rage::Min((m_Params.GetTargetPosition() - vVehicleSteeringPos).XYMag2(), (m_Params.GetTargetPosition() - vVehiclePos).XYMag2());
//are we done?
if(m_Params.m_fTargetArriveDist > 0.0f)//only check if the arrive distance is greater then 0
{
if(m_Params.GetTargetPosition().z < vVehicleSteeringPos.z+CTaskVehicleMissionBase::ACHIEVE_Z_DISTANCE && m_Params.GetTargetPosition().z > vVehicleSteeringPos.z-CTaskVehicleMissionBase::ACHIEVE_Z_DISTANCE)//make sure it's vaguely close in the Z
{
//check whether we have achieved our destination
if (fDistToTargetSq < m_Params.m_fTargetArriveDist * m_Params.m_fTargetArriveDist)
{
if ( m_bAllowAchieveMission )
{
m_bMissionAchieved = true;
}
pVehicle->GetIntelligence()->InvalidateCachedNodeList();
pVehicle->GetIntelligence()->InvalidateCachedFollowRoute();
return FSM_Quit;
}
}
}
if( m_threePointTurnInfo.m_iNumRecentThreePointTurns > 0 )
{
if(fwTimer::GetTimeInMilliseconds() - m_threePointTurnInfo.m_fLastThreePointTurnTime > 7000 ||
(vVehiclePos - m_threePointTurnInfo.m_vLastTurnPosition).XYMag2() > 100.0f)
{
//reset three point turns if we haven't done one recently, or have moved away from point
m_threePointTurnInfo.m_iNumRecentThreePointTurns = 0;
}
}
aiDeferredTasks::TaskData taskData(this, pVehicle, GetTimeStep(), true);
#if !__PS3
TUNE_GROUP_BOOL(AI_DEFERRED_TASKS, USE_DEFERRED_AVOIDANCE, true);
if(USE_DEFERRED_AVOIDANCE)
{
// Queue avoidance
aiDeferredTasks::g_VehicleAvoidance.Queue(taskData);
}
else
{
#endif
PF_START(VehicleAvoidanceTask_Run);
GoToPointWithAvoidance_OnDeferredTask(taskData);
GoToPointWithAvoidance_OnDeferredTask_ProcessSuperDummy(taskData);
GoToPointWithAvoidance_OnDeferredTaskCompletion(taskData);
PF_STOP(VehicleAvoidanceTask_Run);
#if !__PS3
}
#endif
return FSM_Continue;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::GoToPointWithAvoidance_OnDeferredTask(const aiDeferredTasks::TaskData& data)
{
Assert(data.m_Task == this);
ASSERT_ONLY(SetCanChangeState(true);)
CVehicle* pVehicle = data.m_Vehicle;
const float& fTimeStep = data.m_TimeStep;
const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
const Vector3 vVehicleSteeringPos = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, IsDrivingFlagSet(DF_DriveInReverse), true));
float dirToTargetOrientation;
pVehicle->GetIntelligence()->CacheNodeList();
pVehicle->GetIntelligence()->CacheFollowRoute();
const bool bWasSlowlyPushingPlayer = pVehicle->GetIntelligence()->HasBeenSlowlyPushingPlayer();
m_bSlowlyPushingPlayerThisFrame = false;
m_bPathContainsUTurn = false;
TUNE_GROUP_BOOL (FOLLOW_PATH_AI_STEER, handle3PointStuff, true);
const float fDistToTargetSq = rage::Min((m_Params.GetTargetPosition() - vVehicleSteeringPos).XYMag2(), (m_Params.GetTargetPosition() - vVehiclePos).XYMag2());
// the steering pos target should always start out the same as the desired target
m_SteeringTargetPos = m_Params.GetTargetPosition();
// Get the current drive direction and orientation.
const Vector3 vehDriveDir = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVehicle, IsDrivingFlagSet(DF_DriveInReverse)));
float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
vehDriveOrientation = fwAngle::LimitRadianAngleSafe(vehDriveOrientation);
// Work out the target orientation.
// To make things a little bit more stable we aim ahead a little bit.
// TargetPoint_Ahead = TargetPoint + pTargetCar->GetB() * 3.0f;
Vector3 vTargetPos = m_Params.GetTargetPosition();
dirToTargetOrientation = fwAngle::GetATanOfXY(vTargetPos.x - vVehicleSteeringPos.x, vTargetPos.y - vVehicleSteeringPos.y);
dirToTargetOrientation = fwAngle::LimitRadianAngleSafe(dirToTargetOrientation);
// If there is a big difference in target and car orientation we will consider doing a 3 point turn.
if(handle3PointStuff && GetCruiseSpeed() > 0.0f)
{
float fDirToTargetOrientationFromOriginal = fwAngle::GetATanOfXY(vTargetPos.x - vVehiclePos.x, vTargetPos.y - vVehiclePos.y);
fDirToTargetOrientationFromOriginal = fwAngle::LimitRadianAngleSafe(fDirToTargetOrientationFromOriginal);
if(ShouldDoThreePointTurn(pVehicle, &fDirToTargetOrientationFromOriginal, &vehDriveOrientation))
{
pVehicle->GetIntelligence()->InvalidateCachedNodeList();
pVehicle->GetIntelligence()->InvalidateCachedFollowRoute();
SetState(State_ThreePointTurn);
return;
}
}
if (m_bOvertakeCurrentCarRequested)
{
m_bOvertakeCurrentCarRequested = false;
if (StartOvertakeCurrentCar())
{
return;
}
}
float cruiseSpeed = GetCruiseSpeed();
bool bGiveWarning = false;
if (aiVerify(FPIsFinite(dirToTargetOrientation)) && aiVerify(FPIsFinite(vehDriveOrientation)))
{
DealWithTrafficAndAvoidance( pVehicle, cruiseSpeed, dirToTargetOrientation, vehDriveOrientation, bGiveWarning, bWasSlowlyPushingPlayer, fTimeStep);
}
// boats should avoid shorelines
DealWithShoreAvoidance(pVehicle, cruiseSpeed, dirToTargetOrientation, vehDriveOrientation);
//if we didn't change states, reset the waiting for player ped flag
if (GetState() == State_GoToPoint && !m_bSlowlyPushingPlayerThisFrame)
{
SetWaitingForPlayerPed(false);
m_startTimeWaitingForPlayerPed = 0;
}
if (bGiveWarning)
{
bool bSwerve = true;
if (pVehicle->GetIntelligence()->m_pSteeringAroundEntity && pVehicle->GetIntelligence()->m_pSteeringAroundEntity->GetIsTypeVehicle())
{
const CVehicle* pOtherVehicle = static_cast<const CVehicle*>(pVehicle->GetIntelligence()->m_pSteeringAroundEntity.Get());
bSwerve = !pOtherVehicle->GetIntelligence()->m_bDontSwerveForUs;
}
if (bSwerve)
{
pVehicle->GetIntelligence()->InvalidateCachedNodeList();
pVehicle->GetIntelligence()->InvalidateCachedFollowRoute();
SetState(State_Swerve);
return;
}
}
/*
//very rubbish braking for a corner code.
//adjust the speed depending on how big the difference between veh orientation and target orientation is
float desiredAngleDiff = dirToTargetOrientation - vehDriveOrientation;
desiredAngleDiff = fwAngle::LimitRadianAngle(desiredAngleDiff);
float speedModifier = (rage::Abs(desiredAngleDiff)/PI)*5.0f;
speedModifier = 1.0f -((cruiseSpeed/30.0f) * speedModifier);
speedModifier = rage::Min(1.0f, speedModifier);//clamp the modifier so we don't go above the desired cruise speed.
speedModifier = rage::Max(0.2f, speedModifier);//make sure we are always moving a little bit
//modify cruise speed by the amount we are trying to turn.
cruiseSpeed = cruiseSpeed * speedModifier;
*/
// If we've circled the target, then it might be inside our turning circle - it will be necessary to slow down
if(vVehiclePos.x < vTargetPos.x)
m_iTargetSideFlags |= FLAG_NEG_X;
else if(vVehiclePos.x > vTargetPos.x)
m_iTargetSideFlags |= FLAG_POS_X;
if(vVehiclePos.y < vTargetPos.y)
m_iTargetSideFlags |= FLAG_NEG_Y;
else if(vVehiclePos.y > vTargetPos.y)
m_iTargetSideFlags |= FLAG_POS_Y;
// Been all sides? Then apply handbrake to slow down.
const float desiredAngleDiff = fwAngle::LimitRadianAngleSafe(dirToTargetOrientation - vehDriveOrientation);
if(((m_iTargetSideFlags ^ FLAG_ALL_SIDES) == 0) && Abs(desiredAngleDiff)>0.05f && fDistToTargetSq > 1.0f)
{
// TODO: adjust cruiseSpeed accordingly.
// commented out for the moment, until this can be tested further..
//cruiseSpeed = 1.0f;
#if __BANK
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVehicle))
{
grcDebugDraw::Cross(VECTOR3_TO_VEC3V(vVehiclePos+Vector3(0.0f,2.0f,0.0f)), 1.0f, Color_red);
grcDebugDraw::Cross(VECTOR3_TO_VEC3V(vTargetPos+Vector3(0.0f,2.0f,0.0f)), 1.0f, Color_green);
grcDebugDraw::Line(RCC_VEC3V(vVehiclePos), RCC_VEC3V(vTargetPos), Color_red, Color_green);
}
#endif
}
pVehicle->GetIntelligence()->m_fDesiredSpeedThisFrame = cruiseSpeed;
SetCruiseSpeed(cruiseSpeed);
//now turn the target orientation into a target vector
Vector3 LineBase = vVehiclePos;
Vector3 Offset1 = 5.0f * rage::Cosf(vehDriveOrientation - dirToTargetOrientation) * (IsDrivingFlagSet(DF_DriveInReverse) ? -1.0f : 1.0f) * VEC3V_TO_VECTOR3(pVehicle->GetVehicleForwardDirection());
Vector3 Offset2 = 5.0f * rage::Sinf(vehDriveOrientation - dirToTargetOrientation) * (IsDrivingFlagSet(DF_DriveInReverse) ? -1.0f : 1.0f) * VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetA());
m_SteeringTargetPos = (LineBase + Offset1 + Offset2);
// set the target for the goto point task
if(GetSubTask() && GetSubTask()->GetTaskType()==CTaskTypes::TASK_VEHICLE_GOTO_POINT_AUTOMOBILE)
{
CTaskVehicleGoToPointAutomobile * pGoToTask = (CTaskVehicleGoToPointAutomobile*)GetSubTask();
pGoToTask->SetTargetPosition(&m_SteeringTargetPos);
pGoToTask->SetCruiseSpeed(GetCruiseSpeed());
}
// Check if we have computed a target position that looks like we are about to
// make a sharp turn, and make sure that we don't use superdummy mode in the near
// future.
Vec3V tgtPosV;
FindTargetCoors(pVehicle, RC_VECTOR3(tgtPosV));
if(CheckIsAboutToMakeSharpTurn(*pVehicle, tgtPosV))
{
pVehicle->m_nVehicleFlags.bPreventBeingSuperDummyThisFrame = true;
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::GoToPointWithAvoidance_OnDeferredTask_ProcessSuperDummy(const aiDeferredTasks::TaskData& data)
{
CVehicle* pVehicle = data.m_Vehicle;
ProcessSuperDummy(pVehicle, data.m_TimeStep);
pVehicle->GetIntelligence()->InvalidateCachedNodeList();
pVehicle->GetIntelligence()->InvalidateCachedFollowRoute();
ASSERT_ONLY(SetCanChangeState(false);)
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::GoToPointWithAvoidance_OnDeferredTaskCompletion(const aiDeferredTasks::TaskData& data)
{
Assert(data.m_Task == this);
CVehicle* pVeh = data.m_Vehicle;
if (pVeh->GetIntelligence()->m_fDesiredSpeedThisFrame >= 1.0f
&& (pVeh->m_nVehicleFlags.bActAsIfHasSirenOn || (pVeh->m_nVehicleFlags.GetIsSirenOn() && pVeh->UsesSiren()
&& pVeh->GetModelIndex() != MI_CAR_TOWTRUCK && pVeh->GetModelIndex() != MI_CAR_TOWTRUCK_2 && pVeh->GetModelIndex() != MI_CAR_PBUS2))
&& !pVeh->m_nVehicleFlags.bTellOthersToHurry
&& pVeh->GetIntelligence()->GetSirenReactionDistributer().IsRegistered()
&& pVeh->GetIntelligence()->GetSirenReactionDistributer().ShouldBeProcessedThisFrame())
{
//static fn call
MakeWayForCarWithSiren(pVeh);
}
//also do this for vehs with bToldToGetOutOfTheWay, so we propgate it fwd
if (pVeh->m_nVehicleFlags.bTellOthersToHurry || pVeh->m_nVehicleFlags.bToldToGetOutOfTheWay)
{
MakeWayForCarTellingOthersToFlee(pVeh);
}
}
/////////////////////////////////////////////////////////////////////////////////
// State_ThreePointTurn
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::ThreePointTurn_OnEnter (CVehicle* pVehicle)
{
//start the three point turn vehicle sub task
sVehicleMissionParams params = m_Params;
params.m_fCruiseSpeed = 8.0f;
params.ClearTargetEntity();
m_threePointTurnInfo.m_vLastTurnPosition = VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
SetNewTask( rage_new CTaskVehicleThreePointTurn( params, m_bPathContainsUTurn ) );
}
//////////////////////////////////////////////////////////////////////////////////////
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::ThreePointTurn_OnUpdate (CVehicle* pVehicle)
{
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_THREE_POINT_TURN))
{
SetState(State_GoToPoint); //finished three point turn, go back to driving to destination.
}
//if task thinks we should be stopped then return to goto point state
if(GetCruiseSpeed() == 0.0f)
{
SetState(State_GoToPoint);
}
if(GetSubTask() && GetSubTask()->GetTaskType()==CTaskTypes::TASK_VEHICLE_THREE_POINT_TURN)
{
CTaskVehicleThreePointTurn* pSubtask = static_cast<CTaskVehicleThreePointTurn*>(GetSubTask());
pSubtask->SetTargetPosition(&m_Params.GetTargetPosition());
}
ProcessSuperDummy(pVehicle, GetTimeStep());
return FSM_Continue;
}
//////////////////////////////////////////////////////////////////////////////////////
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::ThreePointTurn_OnExit (CVehicle* UNUSED_PARAM(pVehicle))
{
++m_threePointTurnInfo.m_iNumRecentThreePointTurns;
m_threePointTurnInfo.m_fLastThreePointTurnTime = fwTimer::GetTimeInMilliseconds();
return FSM_Continue;
}
/////////////////////////////////////////////////////////////////////////////////
// State_WaitForTraffic
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::WaitForTraffic_OnEnter (CVehicle* pVehicle)
{
//don't wait if a junction's telling us to go somewhere
u32 Wait = pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO ? 0 : CDriverPersonality::FindDelayBeforeAcceleratingAfterObstructionGone(pVehicle->GetDriver(), pVehicle);
SetNewTask( rage_new CTaskVehicleWait(NetworkInterface::GetSyncedTimeInMilliseconds() + Wait) );
}
/////////////////////////////////////////////////////////////////////////////////
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::Wait_OnUpdate (CVehicle* pVehicle)
{
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_WAIT))
{
SetState(State_GoToPoint); //finished waiting so now head for target again.
}
ProcessSuperDummy(pVehicle, GetTimeStep());
pVehicle->GetIntelligence()->UpdateCarHasReasonToBeStopped();
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::WaitForPed_OnEnter (CVehicle* pVehicle)
{
u32 Wait = pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO && !m_bWaitingForPlayerPed ? 0
: CDriverPersonality::FindDelayBeforeAcceleratingAfterObstructionGone(pVehicle->GetDriver(), pVehicle, m_bWaitingForPlayerPed);
SetNewTask( rage_new CTaskVehicleWait(NetworkInterface::GetSyncedTimeInMilliseconds() + Wait) );
}
//////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::TemporarilyBrake_OnEnter (CVehicle* pVehicle)
{
u32 Wait = pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO && !m_bWaitingForPlayerPed ? 0
: CDriverPersonality::FindDelayBeforeAcceleratingAfterObstructionGone(pVehicle->GetDriver(), pVehicle, m_bWaitingForPlayerPed);
SetNewTask( rage_new CTaskVehicleBrake(NetworkInterface::GetSyncedTimeInMilliseconds() + Wait) );
}
/////////////////////////////////////////////////////////////////////////////////////
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::TemporarilyBrake_OnUpdate (CVehicle* pVehicle)
{
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_BRAKE))
{
SetState(State_GoToPoint); //finished braking so now head for target again.
}
ProcessSuperDummy(pVehicle, GetTimeStep());
pVehicle->GetIntelligence()->UpdateCarHasReasonToBeStopped();
return FSM_Continue;
}
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SwerveForHeadOnCollision_OnEnter(CVehicle* pVehicle)
{
u32 Wait = 2000;
//avoid in the opposite direction of the opposing vehicle's velocity. the ShouldGiveWarning()
//checks have already ensured that it's roughly in front of us and moving straight on to us anyway
CTaskVehicleHeadonCollision::SwerveDirection swerveDir = CTaskVehicleHeadonCollision::Swerve_NoPreference;
if (m_vCachedGiveWarningPosition.IsNonZero())
{
Vector3 vMyRightFlat = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetRight());
vMyRightFlat.z = 0.0f;
Vector3 vDelta = m_vCachedGiveWarningPosition - VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
vDelta.z = 0.0f;
CVehicleNodeList* pNodelist = pVehicle->GetIntelligence()->GetNodeList();
bool bIsOnNarrowRoad = false;
if (pNodelist)
{
const s32 iTargetNodeIndex = pNodelist->GetTargetNodeIndex();
const s16 iTargetLinkIndex = pNodelist->GetPathLinkIndex(iTargetNodeIndex);
const CPathNode* pTargetNode = pNodelist->GetPathNode(iTargetNodeIndex);
const CPathNodeLink* pLink = (iTargetLinkIndex >= 0 && pTargetNode)
? ThePaths.FindLinkPointerSafe(pTargetNode->GetAddrRegion(),iTargetLinkIndex)
: NULL;
bIsOnNarrowRoad = pLink && pLink->m_1.m_NarrowRoad;
}
const float fDot = vDelta.Dot(vMyRightFlat);
if (bIsOnNarrowRoad)
{
swerveDir = CTaskVehicleHeadonCollision::Swerve_Straight;
}
else if (fDot > 0.0f)
{
swerveDir = CTaskVehicleHeadonCollision::Swerve_Left;
}
else
{
swerveDir = CTaskVehicleHeadonCollision::Swerve_Right;
}
}
SetNewTask( rage_new CTaskVehicleHeadonCollision(NetworkInterface::GetSyncedTimeInMilliseconds() + Wait, swerveDir) );
}
/////////////////////////////////////////////////////////////////////////////////////
aiTask::FSM_Return CTaskVehicleGoToPointWithAvoidanceAutomobile::SwerveForHeadOnCollision_OnUpdate(CVehicle* pVehicle)
{
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_HEADON_COLLISION))
{
SetState(State_GoToPoint); //finished braking so now head for target again.
}
ProcessSuperDummy(pVehicle, GetTimeStep());
pVehicle->GetIntelligence()->UpdateCarHasReasonToBeStopped();
return FSM_Continue;
}
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldDoThreePointTurn(CVehicle *pVehicle, float* pDirToTargetOrientation, float* pVehDriveOrientation)
{
if (!m_bAllowThreePointTurns)
{
return false;
}
//B* 2160704; don't allow large vehicles to do three point turns when they enter goto state
TUNE_GROUP_BOOL (FOLLOW_PATH_AI_STEER, allowInstantThreePointTurning, false);
if(!allowInstantThreePointTurning && pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG) && GetTimeInState() < 0.1f)
{
return false;
}
bool isStuck;
float desiredAngleDiff, absDesiredAngleDiff;
u32 impatienceTimer;
const bool bOnSmallRoad = pVehicle->GetIntelligence()->IsOnSmallRoad();
bool bRouteContainsUTurn = false;
CTaskVehicleCruiseNew* pTask = static_cast<CTaskVehicleCruiseNew*>(FindParentTaskOfType(CTaskTypes::TASK_VEHICLE_CRUISE_NEW));
if (!pTask)
{
pTask = static_cast<CTaskVehicleGoToAutomobileNew*>(FindParentTaskOfType(CTaskTypes::TASK_VEHICLE_GOTO_AUTOMOBILE_NEW));
}
if (pTask)
{
bRouteContainsUTurn = pTask->DoesPathContainUTurn();
}
m_bPathContainsUTurn = bRouteContainsUTurn;
return CTaskVehicleThreePointTurn::ShouldDoThreePointTurn(pVehicle, pDirToTargetOrientation, pVehDriveOrientation, IsDrivingFlagSet(DF_StopAtLights), isStuck, desiredAngleDiff, absDesiredAngleDiff, impatienceTimer, bOnSmallRoad, bRouteContainsUTurn );
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldWaitForTrafficBeforeTurningLeft(CVehicle* pVeh, const CEntity* pException, const spdAABB& /*boundBox*/) const
{
PF_FUNC(AI_AvoidanceShouldWaitForTrafficBeforeTurningLeft);
if (pVeh->GetIntelligence()->GetJunctionTurnDirection() != BIT_TURN_LEFT)
{
return false;
}
CVehicleNodeList* pNodeList = pVeh->GetIntelligence()->GetNodeList();
if (!pNodeList)
{
return false;
}
//not a junction or a junction with train tracks.
//we don't want to left turn yield here because generally the spot we choose
//for yielding is on the tracks
const CNodeAddress& junctionNode = pVeh->GetIntelligence()->GetJunctionNode();
CJunction* pJunction = pVeh->GetIntelligence()->GetJunction();
if (!pJunction || pJunction->ShouldCarsStopForTrain())
{
return false;
}
const CPathNode* pJunctionNode = ThePaths.FindNodePointerSafe(junctionNode);
if (!pJunctionNode)
{
return false;
}
const Vector3 vVehicleBonnetPos = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVeh, IsDrivingFlagSet(DF_DriveInReverse)));
const Vector3 vJunctionPos = pJunctionNode->GetPos();
Vector2 vEntryDir, vExitDir;
pJunction->FindEntryAndExitDirs(pVeh, vEntryDir, vExitDir);
CNodeAddress entryNode, exitNode;
int entryLane = 0, exitLane = 0;
pJunction->FindEntryAndExitNodes(pVeh, entryNode, exitNode, entryLane, exitLane);
//don't wait if we aren't ready to turn
//(as opposed to just have entered the intersection)
bool bInGoodPositionToStop = false;
if (!entryNode.IsEmpty())
{
s32 iEntranceIndex = pJunction->FindEntranceIndexWithNode(entryNode);
if(iEntranceIndex >= 0)
{
const CJunctionEntrance& rMyEntrance = pJunction->GetEntrance(iEntranceIndex);
const Vector3& vEntryPos = rMyEntrance.m_vPositionOfNode;
const float fEntryLinkLengthSqr = vEntryPos.Dist2(vJunctionPos);
const float fDistSqrToJunction = vJunctionPos.Dist2(vVehicleBonnetPos);
static dev_float s_fEntryLinkLengthThresholdForStoppingSqr = 0.4f * 0.4f;
if (fDistSqrToJunction < (fEntryLinkLengthSqr * s_fEntryLinkLengthThresholdForStoppingSqr))
{
bInGoodPositionToStop = true;
}
}
}
const Vector3 vehDriveDir = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse)));
const Vec3V vehBonnetPos = CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVeh, IsDrivingFlagSet(DF_DriveInReverse));
//have we already passed the junction?
Vector3 vCarToJunction = vJunctionPos - vVehicleBonnetPos;
vCarToJunction.z = 0.0f;
static dev_float s_fOverJunctionThreshold = -1.0f;
if (vCarToJunction.Dot(vehDriveDir) < s_fOverJunctionThreshold)
{
return false;
}
//if our light went red already, bail out and just get the hell out of the intersection
if (CVehicleJunctionHelper::ApproachingRedLight(pVeh))
{
return false;
}
//next, iterate through every car that the junction knows about
//and see if they might collide with us
// -they need to either share the link from the junction to our destination with us (turning in the same direction)
// -or they need to be traveling down the road we were coming from (going straight ahead)
// -can't be stopping for lights/traffic
// -can't have a give way node (that would mean they should defer to us)
//int iOurEntrance;
//float fOurDistToJunction;
//pJunction->CalculateDistanceToJunction(pVeh, &iOurEntrance, fOurDistToJunction);
for (int i = 0; i < CJunction::JUNCTION_MAX_VEHICLES; i++)
{
CVehicle* pOtherVehicle = pJunction->GetVehicle(i);
if (!pOtherVehicle)
{
continue;
}
//don't stop for ourselves!
if (pVeh == pOtherVehicle)
{
continue;
}
//don't stop for the exception vehicle
if (pOtherVehicle == pException)
{
continue;
}
if (pOtherVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_LIGHTS
|| pOtherVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_TRAFFIC
|| pOtherVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GIVE_WAY)
{
continue;
}
//if they've already passed us, don't bother
const Vec3V vOtherBonnetPos = CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pOtherVehicle, false);
Vector3 vUsToThem = VEC3V_TO_VECTOR3(vOtherBonnetPos - vehBonnetPos);
vUsToThem.z = 0.0f;
if (vehDriveDir.Dot(vUsToThem) < 0.0f)
{
continue;
}
//don't yield for stopped cars either
if (pOtherVehicle->GetAiXYSpeed() < 0.5f)
{
continue;
}
//from here on the tests involve iterating through the node lists...try and put simpler
//rejections above here
// CVehicleNodeList* pOtherNodeList = pOtherVehicle->GetIntelligence()->GetNodeList();
// if (!pOtherNodeList)
// {
// continue;
// }
// if (pOtherNodeList->GivesWayBeforeNextJunction())
// {
// continue;
// }
int iEntrance;
float fDistToJunction;
if (!pJunction->CalculateDistanceToJunction(pOtherVehicle, &iEntrance, fDistToJunction))
{
continue;
}
if (iEntrance == -1)
{
continue;
}
CJunctionEntrance& rEntrance = pJunction->GetEntrance(iEntrance);
const CPathNode* pOtherEntranceNode = ThePaths.FindNodePointerSafe(rEntrance.m_Node);
if (!pOtherEntranceNode || pOtherEntranceNode->IsGiveWay())
{
continue;
}
//not coming from in front of us
if ((-rEntrance.m_vEntryDir).Dot(vEntryDir) > -0.7f)
//if (rEntrance.m_vEntryDir.Dot(rMyEntrance.m_vEntryDir) > -0.7f)
{
continue;
}
//not turning into a conflicted lane
//conflicted lanes:
// -any in the direction we're coming from
// -the direction we're going toward, unless it's
// in a lane to our right
CNodeAddress rPrevNode, rNextNode;
s32 iEntryLane, iExitLane;
if (!pJunction->FindEntryAndExitNodes(pOtherVehicle, rPrevNode, rNextNode, iEntryLane, iExitLane))
{
continue;
}
const CPathNode* pOtherExitNode = ThePaths.FindNodePointerSafe(rNextNode);
if (!pOtherExitNode)
{
continue;
}
Vector3 vOtherExitDir = pOtherExitNode->GetPos() - pJunction->GetJunctionCenter();
vOtherExitDir.NormalizeSafe();
if (vOtherExitDir.Dot(Vector3(vEntryDir.x, vEntryDir.y, 0.0f)) > -0.7f && vOtherExitDir.Dot(Vector3(vExitDir.x, vExitDir.y, 0.0f)) < 0.7f)
{
continue;
}
//if the other guy has a red light, don't wait for him
const bool bApproachingRedLight = CVehicleJunctionHelper::ApproachingRedLight(pOtherVehicle);
if (bApproachingRedLight)
{
continue;
}
const float fOtherVehSpeed = pOtherVehicle->GetAiVelocity().Mag();
const float fTimeToJunction = fOtherVehSpeed < 0.01f ? LARGE_FLOAT : fDistToJunction / fOtherVehSpeed;
TUNE_GROUP_FLOAT (FOLLOW_PATH_AI_STEER, fTimeItTakesUsToLeaveJunction, 1.5f, 0.0f, 100.0f, 0.1f)
if (fTimeToJunction < fTimeItTakesUsToLeaveJunction)
{
if (bInGoodPositionToStop)
{
#if __BANK
if (CJunctions::m_bDebug)
{
//grcDebugDraw::Sphere(pVeh->GetVehiclePosition(), 1.5f, Color_red, false);
grcDebugDraw::Line(pVeh->GetVehiclePosition(), pOtherVehicle->GetVehiclePosition(), Color_white, Color_orange);
}
#endif //__BANK
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
return true;
}
else
{
pVeh->m_nVehicleFlags.bWillTurnLeftAgainstOncomingTraffic = true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::DealWithShoreAvoidance(const CVehicle *pVeh, float &desiredSpeed, float &toTargetOrientation, float UNUSED_PARAM(vehDriveOrientation))
{
if ( pVeh->InheritsFromBoat() )
{
CBoatAvoidanceHelper* pBoatAvoidanceHelper = pVeh->GetIntelligence()->GetBoatAvoidanceHelper();
if ( pBoatAvoidanceHelper )
{
pBoatAvoidanceHelper->SetDesiredOrientation(toTargetOrientation);
static float s_AvoidanceMaxSlowDown = 0.95f;
static float s_AvoidanceMaxSlowDownRacing = 0.95f;
desiredSpeed = pBoatAvoidanceHelper->ComputeAvoidanceSpeed(desiredSpeed, pVeh->m_nVehicleFlags.bIsRacing ? s_AvoidanceMaxSlowDownRacing : s_AvoidanceMaxSlowDown);
toTargetOrientation = pBoatAvoidanceHelper->ComputeAvoidanceOrientation(toTargetOrientation);
}
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::DealWithTrafficAndAvoidance(CVehicle *pVeh, float &desiredSpeed, float &toTargetOrientation, float vehDriveOrientation, bool& bGiveWarning, const bool bWasSlowlyPushingPlayer, const float fTimeStep)
{
PF_FUNC(AI_Avoidance);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, WEAVEBLOCKSIZE, 21.0f, 0.0f, 100.0f, 0.1f)
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, SMALLWEAVEBLOCKSIZE, 10.0f, 0.0f, 100.0f, 0.1f)
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, WEAVE_SPEED_MULT, 24.0f, 0.0f, 100.0f, 0.1f)
TUNE_GROUP_BOOL (FOLLOW_PATH_AI_STEER, handleTraffic, true);
TUNE_GROUP_BOOL (FOLLOW_PATH_AI_STEER, handleBuildings, true);
//Clear the obstruction information.
m_ObstructionInformation.Clear();
const CTaskVehicleMissionBase* pActiveTask = pVeh->GetIntelligence()->GetActiveTask();
const bool bVehicleIsFleeing = pActiveTask && pActiveTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_FLEE;
//ambient vehicles have DF_SwerveAroundAllCars set by default.
//we may want to disable it if we have a non-aggressive driver
const bool bShouldPreventBikeSwerving = pVeh->InheritsFromBike()
&& (pVeh->PopTypeIsRandom() || (pVeh->GetDriver() && pVeh->GetDriver()->PopTypeIsRandom()))
&& (!pVeh->GetDriver() || !pVeh->GetDriver()->IsLawEnforcementPed())
&& !CDriverPersonality::WeavesInBetweenLanesOnBike(pVeh->GetDriver(), pVeh)
&& !bVehicleIsFleeing;
CarAvoidanceLevel carAvoidanceLevel = CAR_AVOIDANCE_NONE;
if( (IsDrivingFlagSet(DF_SwerveAroundAllCars) && !bShouldPreventBikeSwerving) || m_bAvoidingCarsUntilClear
|| pVeh->m_nVehicleFlags.bToldToGetOutOfTheWay)
{
carAvoidanceLevel = CAR_AVOIDANCE_FULL;
}
else if( IsDrivingFlagSet(DF_SteerAroundStationaryCars) || IsDrivingFlagSet(DF_SwerveAroundAllCars) )
{
carAvoidanceLevel = CAR_AVOIDANCE_LITTLE;
//avoidanceRadius = SMALLWEAVEBLOCKSIZE;
}
spdAABB tempBox;
const spdAABB& boundBox = pVeh->GetLocalSpaceBoundBox(tempBox);
// Increase the size of the area being checked for cars going faster.
//const float fSpeed = rage::Sqrtf(pVeh->GetAiVelocity().x * pVeh->GetAiVelocity().x + pVeh->GetAiVelocity().y * pVeh->GetAiVelocity().y);
const CAIHandlingInfo* pAIHandlingInfo = pVeh->GetAIHandlingInfo();
Assert(pAIHandlingInfo);
const float avoidanceRadius = Max(pAIHandlingInfo->GetDistToStopAtCurrentSpeed(pVeh->GetAiVelocity().XYMag()), pAIHandlingInfo->GetMinBrakeDistance())
+ boundBox.GetMax().GetYf();
//float fSpeedMult = 1.0f + (fSpeed / WEAVE_SPEED_MULT);
//fSpeedMult = rage::Min(fSpeedMult, 2.0f);
//avoidanceRadius *= fSpeedMult;
//if we got fed up with the player after waiting for him and started driving,
//we should be allowed to steer around peds here
//also steer around peds if we'd normally be allowed to stop for them, and are on a junction with
//a train appraoching
const bool bOnJunctionWithTrainApproaching = pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO
&& pVeh->GetIntelligence()->GetJunction() && pVeh->GetIntelligence()->GetJunction()->ShouldCarsStopForTrain();
const bool bAvoidPeds = IsDrivingFlagSet(DF_SteerAroundPeds) || bWasSlowlyPushingPlayer
|| (IsDrivingFlagSet(DF_StopForPeds) && bOnJunctionWithTrainApproaching);
const bool bAvoidObjs = IsDrivingFlagSet(DF_SteerAroundObjects);
// TODO: Replace this with bitfields
const bool bWillStopForCars = IsDrivingFlagSet(DF_StopForCars);
const bool bWillStopForPeds = IsDrivingFlagSet(DF_StopForPeds);
const bool bWillStopForLights = IsDrivingFlagSet(DF_StopAtLights);
const bool bWillSwerveForObjects = IsDrivingFlagSet(DF_SteerAroundObjects);
//const bool bCopperBlockedCouldLeaveCar = pVeh->m_nVehicleFlags.bCopperBlockedCouldLeaveCar;
bool bHandleSteering = handleTraffic;
//did we already have a lane change queued up?
//const bool bAlreadyTryingToChangeLanes = m_bShouldChangeLanesForTraffic;
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, TimeToKeepAvoiding, 5.0f, 0.0f, 100.0f, 0.1f);
if( m_fAvoidedEntityCounter > 0.0f )
{
m_fAvoidedEntityCounter -= fTimeStep;
// If we were recently avoiding something, don't allow timeslicing.
if(!ShouldAllowTimeslicingWhenAvoiding())
{
pVeh->m_nVehicleFlags.bLodCanUseTimeslicing = false;
pVeh->m_nVehicleFlags.bLodShouldUseTimeslicing = false;
}
pVeh->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
}
else if(!m_bStoppingForTraffic && pVeh->m_nVehicleFlags.bLodShouldUseTimeslicing)
{
// In this case, we are not stopping for traffic, but the base class had set
// the aggressive bLodShouldUseTimeslicing option. We now downgrade this to
// bLodCanUseTimeslicing, meaning that timeslicing will only be used at a distance.
pVeh->m_nVehicleFlags.bLodShouldUseTimeslicing = false;
pVeh->m_nVehicleFlags.bLodCanUseTimeslicing = true;
}
// Only periodically update the steering if nothing has been avoided recently.
// As soon as something is avoided we update it every frame for a few seconds
if( m_fAvoidedEntityCounter <= 0.0f && carAvoidanceLevel == CAR_AVOIDANCE_LITTLE )
{
const bool bOnlyCarOnJunction = pVeh->GetIntelligence()->GetJunction() && pVeh->GetIntelligence()->GetJunction()->GetNumberOfCars() <=1;
const bool bHasGoCommand = pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO;
const bool bUpdateDueToBeingInJunction = bHasGoCommand && !bOnlyCarOnJunction && !pVeh->IsSuperDummy();
if(!bUpdateDueToBeingInJunction && pVeh->GetIntelligence()->GetSteeringDistributer().IsRegistered())
{
if(!pVeh->GetIntelligence()->GetSteeringDistributer().ShouldBeProcessedThisFrame())
{
bHandleSteering = false;
}
}
}
const CEntity* pException = NULL;
if( IsDrivingFlagSet(DF_DontAvoidTarget) )
{
pException = GetTargetEntity();
}
if (handleTraffic && bWillStopForCars)
{
pVeh->m_nVehicleFlags.bIsWaitingToTurnLeft = ShouldWaitForTrafficBeforeTurningLeft(pVeh, pException, boundBox);
}
//m_bShouldChangeLanesForTraffic = CanSwitchLanesToAvoidObstruction(pVeh, pVeh->GetIntelligence()->GetCarWeAreBehind(), desiredSpeed);
//There are a couple situations when tailgating should be disabled:
// 1) This car does not stop for other cars.
// 2) This car is in full avoidance mode (typically means the driver wants to swerve around other cars).
bool bTailgating = false;
if (handleTraffic && bWillStopForCars && (carAvoidanceLevel != CAR_AVOIDANCE_FULL) && CVehicleIntelligence::m_bTailgateOtherCars)
{
bTailgating = TailgateCarInFront(pVeh, &desiredSpeed);
}
float MaxSpeed = desiredSpeed;
if(m_pOptimization_pLastCarBlockingUs)
{
if (handleTraffic && !pVeh->m_nVehicleFlags.bIsWaitingToTurnLeft && !bTailgating && bWillStopForCars && desiredSpeed > 0.0f
&& (IsDrivingFlagSet(DF_StopForCars) || IsDrivingFlagSet(DF_StopForPeds)) )
{
bool bCheckForBraking = true;
// Only periodically update the braking if the car is stationary.
// As soon as it is free to move again we update hte brakes each frame
if(pVeh->GetIntelligence()->GetBrakingDistributer().IsRegistered())
{
if(!pVeh->GetIntelligence()->GetBrakingDistributer().ShouldBeProcessedThisFrame())
{
bCheckForBraking = false;
}
}
if( bCheckForBraking || m_bAvoidingCarsUntilClear)
{
CVehicle *pObstructor = m_pOptimization_pLastCarBlockingUs;
m_pOptimization_pLastCarBlockingUs = NULL;
//clear information about cars that are blocking us
pVeh->GetIntelligence()->m_pCarThatIsBlockingUs = NULL; // Will be filled in if we are blocked by a car later in this function.
m_fSmallestCollisionTSoFar = 100.0f; // This is used to make sure we find the nearest collision.
const bool bPreventFullStop = false;
SlowCarDownForOtherCar(pVeh, pObstructor, MaxSpeed, desiredSpeed, toTargetOrientation, true, bGiveWarning, boundBox, pVeh->GetBoundRadius(), carAvoidanceLevel, bPreventFullStop, true);
}
}
else if (!IsDrivingFlagSet(DF_StopForCars) && !IsDrivingFlagSet(DF_StopForPeds))
{
// Make sure we don't early out from the avoidance angle tests - normally this is cleared or set via SlowCarDownForOtherCar
m_pOptimization_pLastCarBlockingUs = NULL;
}
}
else
{
//clear information about cars that are blocking us
pVeh->GetIntelligence()->m_pCarThatIsBlockingUs = NULL; // Will be filled in if we are blocked by a car later in this function.
m_fSmallestCollisionTSoFar = 100.0f; // This is used to make sure we find the nearest collision.
}
bool bUseAltSteerDirectionForSlowdownTest = false;
const float fDesiredDirectionBeforeWeaveThroughTraffic = toTargetOrientation;
if(handleTraffic && bHandleSteering && !pVeh->m_nVehicleFlags.bIsWaitingToTurnLeft && !bTailgating)
{
// If the car finds something to avoid set a timer to make sure we update the avoidance every frame for a few seconds.
float newOrientation = FindAngleToWeaveThroughTraffic(pVeh, pException, toTargetOrientation, vehDriveOrientation, 1.0f, carAvoidanceLevel, bAvoidPeds, bAvoidObjs, avoidanceRadius, bGiveWarning, bWillStopForCars, boundBox, desiredSpeed);
if( ABS(toTargetOrientation - newOrientation) > FLT_EPSILON)
{
toTargetOrientation = newOrientation;
bUseAltSteerDirectionForSlowdownTest = true;
m_fAvoidedEntityCounter = TimeToKeepAvoiding;
// We are now avoiding something, don't allow timeslicing right now.
if(!ShouldAllowTimeslicingWhenAvoiding())
{
pVeh->m_nVehicleFlags.bLodCanUseTimeslicing = false;
pVeh->m_nVehicleFlags.bLodShouldUseTimeslicing = false;
}
pVeh->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelperMutable();
if (pFollowRoute)
{
pFollowRoute->GiveLaneSlack();
}
}
else if (m_bReEnableSlowForCarsWhenDoneAvoiding)
{
SetDrivingFlag(DF_StopForCars, true);
m_bReEnableSlowForCarsWhenDoneAvoiding = false;
}
//pVeh->m_nVehicleFlags.bCopperBlockedCouldLeaveCar = bCopperBlockedCouldLeaveCar;
if(bGiveWarning)
{
//CVehicle::ms_debugDraw.AddLine(pVeh->GetVehiclePosition(), pVeh->GetVehiclePosition() + Vec3V(0.0f, 0.0f, 10.0f), Color_yellow2);
pVeh->GiveWarning();
}
}
else
{
m_bAvoidedRightLastFrame = false;
m_bAvoidedLeftLastFrame = false;
if (m_bReEnableSlowForCarsWhenDoneAvoiding)
{
SetDrivingFlag(DF_StopForCars, true);
m_bReEnableSlowForCarsWhenDoneAvoiding = false;
}
}
// This bit avoids buildings by steering extremely if we are in the process of hitting one
if(handleBuildings && pVeh->GetIntelligence()->m_bSteerForBuildings)
{
float desiredAngleDiff = SubtractAngleShorter(toTargetOrientation, vehDriveOrientation);
toTargetOrientation = fwAngle::LimitRadianAngle(toTargetOrientation);
toTargetOrientation = FindAngleToAvoidBuildings(pVeh, (desiredAngleDiff < 0.0f), vehDriveOrientation, toTargetOrientation, boundBox);
}
// Possibly slow down to avoid collisions with other cars.
float speedMultiplierTraffic = pVeh->m_nVehicleFlags.bIsWaitingToTurnLeft ? 0.0f : 1.0f;
if((handleTraffic && !pVeh->m_nVehicleFlags.bIsWaitingToTurnLeft && !bTailgating) || m_bBumperInsideBoundsThisFrame)
{
if( bWillStopForCars || bWillStopForPeds || bWillStopForLights || bWillSwerveForObjects || m_bBumperInsideBoundsThisFrame)
{
if(desiredSpeed > 0.0f) // avoid div by zero
{
const float fMaxSpeed = FindMaximumSpeedForThisCarInTraffic(pVeh, toTargetOrientation, bUseAltSteerDirectionForSlowdownTest, bGiveWarning, MaxSpeed, avoidanceRadius, boundBox, carAvoidanceLevel, bWasSlowlyPushingPlayer);
speedMultiplierTraffic = fMaxSpeed / desiredSpeed;
}
else
{
speedMultiplierTraffic = 0.0f;
}
}
// @RSGNWE-JW handle the case where a crazy driver wants to bull rush, erm, ram through a Ped.
else if ( IsDrivingFlagSet(DMode_PloughThrough) )
{
float MinX = 0.0f, MinY = 0.0f, MaxX = 0.0f, MaxY = 0.0f;
const Vector3 vecVehiclePosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
GenerateMinMaxForPedAvoidance(vecVehiclePosition, avoidanceRadius, MinX, MinY, MaxX, MaxY);
//even though we have bWasSlowlyPushingPlayer in this scope, if the driver is plowing through
//we don't want to do anything different
SlowCarDownForPedsSectorListHelper(this, pVeh, MinX, MinY, MaxX, MaxY, &MaxSpeed, MaxSpeed, false);
}
}
desiredSpeed *= rage::Min(1.0f, speedMultiplierTraffic);
if (!bWillStopForCars && pVeh->m_nVehicleFlags.bIsRacing)
{
desiredSpeed = rage::Min(desiredSpeed, FindMaximumSpeedForRacingCar(pVeh, desiredSpeed, boundBox));
}
if (desiredSpeed < SMALL_FLOAT)
{
//if we found a new direction but then stopped,
//and we were already stopped, don't change direction
//this just causes us to flick our wheels in place.
//allow it if we're moving or want to move
if (pVeh->GetAiXYSpeed() < 0.1f * 0.1f)
{
toTargetOrientation = fDesiredDirectionBeforeWeaveThroughTraffic;
//if we want to swerve around all cars, and also stop for them,
//and we come to a complete halt, unset DF_StopForCars until we're moving again
if (IsDrivingFlagSet(DF_SwerveAroundAllCars) && IsDrivingFlagSet(DF_StopForCars))
{
m_bReEnableSlowForCarsWhenDoneAvoiding = true;
SetDrivingFlag(DF_StopForCars, false);
}
}
}
if (desiredSpeed < CVehicleIntelligence::ms_fMoveSpeedBelowWhichToCheckIfStuck)
{
//this is done down here now since it's not set on a per-vehicle basis anymore
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::GenerateMinMaxForPedAvoidance(const Vector3& vecVehiclePosition, const float avoidanceRadius, float& MinX, float& MinY, float& MaxX, float& MaxY)
{
MinX = vecVehiclePosition.x - avoidanceRadius;
MaxX = vecVehiclePosition.x + avoidanceRadius;
MinY = vecVehiclePosition.y - avoidanceRadius;
MaxY = vecVehiclePosition.y + avoidanceRadius;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindAngleToAvoidBuildings
// PURPOSE : Calculates the illegal angles and returns the one closest to the
// original angle.
/////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAngleToAvoidBuildings(CVehicle* const pVeh, bool bTurningClockwise, float vehDriveOrientation, float dirToTargetOrientation, const spdAABB& boundBox)
{
PF_FUNC(AI_AvoidanceFindAngleToAvoidBuildings);
// Only mission vehicles do this as it's expensive.
const bool bAcceptableVehOrDriver = pVeh->PopTypeIsMission() || (pVeh->GetDriver() && (pVeh->GetDriver()->PopTypeIsMission() && !pVeh->GetDriver()->IsPlayer()));
if(!bAcceptableVehOrDriver)
{
return dirToTargetOrientation;
}
phIntersection testIntersection;
phSegment testSegment;
#define LINESCANDIST (4.0f)
//#define LINESCANSIDEFRACTION(0.2f)
if(IsDrivingFlagSet(DF_DriveInReverse))
{
if(bTurningClockwise)
{
testSegment.A = pVeh->TransformIntoWorldSpace(Vector3(boundBox.GetMin().GetXf(), -boundBox.GetMax().GetYf(), 0.4f));
testSegment.B = testSegment.A -(LINESCANDIST * VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
}
else
{
testSegment.A = pVeh->TransformIntoWorldSpace(Vector3(boundBox.GetMax().GetXf(), -boundBox.GetMax().GetYf(), 0.4f));
testSegment.B = testSegment.A -(LINESCANDIST * VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
}
}
else
{
if(bTurningClockwise)
{
testSegment.A = pVeh->TransformIntoWorldSpace(Vector3(boundBox.GetMin().GetXf(), boundBox.GetMax().GetYf(), 0.4f));
testSegment.B = testSegment.A +(LINESCANDIST * VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
}
else
{
testSegment.A = pVeh->TransformIntoWorldSpace(Vector3(boundBox.GetMax().GetXf(), boundBox.GetMax().GetYf(), 0.4f));
testSegment.B = testSegment.A +(LINESCANDIST * VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
}
}
bool bHit;
if(CPhysics::GetLevel()->TestProbe(testSegment, &testIntersection, pVeh->GetCurrentPhysicsInst(), ArchetypeFlags::GTA_MAP_TYPE_MOVER) &&
testIntersection.GetNormal().GetZf() < 0.7f)
{
bHit = true;
}
else
{
bHit = false;
}
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Color32 Col = Color32(155, 155, 155, 255);
if(bHit)
{
Col = Color32(255, 255, 0, 255);
}
grcDebugDraw::Line(testSegment.A, testSegment.B, Col);
}
#endif
if(bHit)
{
//count swerving for buildings as something that dirties a vehicle
pVeh->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
if(bTurningClockwise)
{
return vehDriveOrientation - 1.0f;
}
else
{
return vehDriveOrientation + 1.0f;
}
}
return dirToTargetOrientation;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindAngleToWeaveThroughTraffic
// PURPOSE : Calculates the illegal angles and returns the one closest to the
// original angle.
/////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAngleToWeaveThroughTraffic(CVehicle* pVeh, const CEntity *pException, float Direction
, float vehDriveOrientation, float CheckDistMult, CarAvoidanceLevel carAvoidanceLevel, bool bAvoidPeds, bool bAvoidObjs
, float avoidanceRadius, bool& bGiveWarning, const bool bWillStopForCars, const spdAABB& boundBox, const float fDesiredSpeed)
{
PF_FUNC(AI_AvoidanceFindAngleToWeaveThroughTraffic);
float MinX, MaxX, MinY, MaxY;
// float LeftAngle, RightAngle, DiffL, DiffR;// OrientationDiff;
// float LeftAngleOld, RightAngleOld;
//bool bMustGoLeft = false, bMustGoRight = false;
const float originalDirection = Direction;
#if __DEV
static CVehicle *pDebugVeh = NULL;
if(pDebugVeh == pVeh)
{
Displayf("Our car\n");
}
#endif
// Identify scan area.(Block around car)
// Shift it forwads in the direction of travel slightly to allow us to reduce the size of the box.
float fAreaSize = avoidanceRadius * CheckDistMult;
const Vector3 vecVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
Vector3 vecToTarget = m_Params.GetTargetPosition() - vecVehiclePos;
vecToTarget.NormalizeFast();
Vector3 vCentre = vecVehiclePos + vecToTarget*fAreaSize*0.33f;
MinX = vCentre.x - fAreaSize;
MaxX = vCentre.x + fAreaSize;
MinY = vCentre.y - fAreaSize;
MaxY = vCentre.y + fAreaSize;
#if __BANK
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, RENDER_AVOIDANCE_SEARCH, false );
if( RENDER_AVOIDANCE_SEARCH && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::BoxAxisAligned(Vec3V(MinX, MinY, vecVehiclePos.z - 7.0f), Vec3V(MaxX, MaxY, vecVehiclePos.z +7.0f ), Color_red);
}
#endif // __BANK
const bool bAvoidVehs = (carAvoidanceLevel != CAR_AVOIDANCE_NONE);
PF_START(AI_AvoidanceNewSearchForUnobstructedDirections);
Direction = TestDirectionsForObstructionsNew(pVeh, pException, MinX, MinY, MaxX, MaxY, originalDirection, vehDriveOrientation, carAvoidanceLevel, bAvoidVehs, bAvoidPeds, bAvoidObjs, bGiveWarning, bWillStopForCars, boundBox, fDesiredSpeed);
PF_STOP(AI_AvoidanceNewSearchForUnobstructedDirections);
Direction = fwAngle::LimitRadianAngleSafe(Direction);
// If we haven't had to change direction we are clear and can re-set our driving mode.
if(m_bAvoidingCarsUntilClear && AreNearlyEqual(originalDirection, Direction))
{
if(fwTimer::GetTimeInMilliseconds() > m_startTimeAvoidCarsUntilClear + 3000)
{
//SetDrivingFlag(DF_AvoidingCarsUntilClear, false);
m_bAvoidingCarsUntilClear = false;
}
}
return Direction;
}
void HelperDoDriveByOnTarget(CVehicle* pVeh, CPed* pDriver, const CPhysical* pTarget, const u32 uWeaponHash = -1)
{
if (!pDriver || !pVeh || !pTarget)
{
return;
}
CAITarget target(pTarget);
const u32 uFiringPattern = ATSTRINGHASH("FIRING_PATTERN_BURST_FIRE_DRIVEBY", 0x0d31265f2);
const float fAbortRange = 40.0f;
//only do this for wandering peds, even though all the logic lives in CTaskCarDrive, and it would be functional there too.
CTask* pCurrent = pDriver->GetPedIntelligence()->GetTaskAtPriority(PED_TASK_PRIORITY_DEFAULT);
CTaskCarDriveWander* pControlVehicleTask = pCurrent && pCurrent->GetTaskType()==CTaskTypes::TASK_CAR_DRIVE_WANDER ? static_cast<CTaskCarDriveWander*>(pCurrent) : NULL;
if(pControlVehicleTask)
{
pControlVehicleTask->RequestDriveBy(target, 1.0f, fAbortRange, uFiringPattern, g_ReplayRand.GetRanged(3000, 6000), uWeaponHash);
}
}
void HelperFlipOffTarget(CVehicle* pVeh, CPed* pDriver, const CPhysical* pTarget)
{
if (pDriver)
{
HelperDoDriveByOnTarget(pVeh, pDriver, pTarget, pDriver->GetDefaultUnarmedWeaponHash());
}
}
void HelperMaybePlayHorn(CVehicle* pVeh, const bool bPlayHorn, const bool bLongHorn, const CPhysical* pTarget, const bool bRestrictDriveBy = false, const bool bAllowDingDing = false)
{
if (bPlayHorn)
{
const CVehicle* pOtherVehicle = NULL;
if (pTarget && pTarget->GetIsTypeVehicle())
{
pOtherVehicle = static_cast<const CVehicle*>(pTarget);
}
else if (pTarget && pTarget->GetIsTypePed())
{
pOtherVehicle = static_cast<const CPed*>(pTarget)->GetVehiclePedInside();
}
if (pOtherVehicle &&
(pOtherVehicle->IsLawEnforcementVehicle()
|| pOtherVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_EMERGENCY_SERVICE)))
{
return;
}
//generally don't use our bell for bicycles, unless we're overtaking
if (pVeh->GetVehicleType() == VEHICLE_TYPE_BICYCLE
&& !bAllowDingDing)
{
return;
}
//don't honk if we're both racing
if (pVeh->m_nVehicleFlags.bIsRacing && pOtherVehicle && pOtherVehicle->m_nVehicleFlags.bIsRacing)
{
return;
}
u32 hornTypeHash = bLongHorn ? ATSTRINGHASH("HELDDOWN",0x839504CB) : 0;
pVeh->PlayCarHorn(true, hornTypeHash);
#if __DEV
if (pTarget && pVeh && CPlayerInfo::GetDisplayRecklessDriving())
{
Color32 honkColor = bLongHorn ? Color_red : Color_yellow;
grcDebugDraw::Sphere(pTarget->GetTransform().GetPosition(), 1.5f, honkColor, false);
grcDebugDraw::Arrow(pVeh->GetVehiclePosition(), pTarget->GetTransform().GetPosition(), 1.0f, honkColor);
}
#endif //__DEV
if (bLongHorn && !bRestrictDriveBy)
{
HelperFlipOffTarget(pVeh, pVeh->GetDriver(), pTarget);
}
}
}
struct AvoidanceTaskObstructionData
{
/// These are all the "input" parameters
const CVehicle* pVeh;
const CEntity *pException;
float MinX, MinY, MaxX, MaxY;
float fOriginalDirection;
float fDesiredSpeed;
float fCurrentSpeed;
float vehDriveOrientation;
u32 turnDir;
CarAvoidanceLevel carAvoidanceLevel;
Vec3V vBoundBoxMin;
Vec3V vBoundBoxMax;
Vec3V vPosOfOriginalObstruction;
bool bIsCurrentlyOnJunction;
bool bWillStopForCars;
bool bOnSingleTrackRoad;
bool bRacing;
/// This is data that we precompute and pass around to various functions
Vec3V vNormalizedDriveDir;
Vec3V vFrontLeftCorner;
Vec3V vRearLeftCorner;
Vec3V vBumperVector;
Vec3V vSideVector;
Vec3V vBumperCenter;
float fAvoidanceLookaheadTimeShort, fAvoidanceLookaheadTimeLong;
__forceinline float GetAvoidanceLookaheadTime(bool longerProbe) const { return longerProbe ? fAvoidanceLookaheadTimeLong : fAvoidanceLookaheadTimeShort; }
/// And these are the lists of obstructions themselves
struct RoadSegment
{
Vec2f m_LeftStart, m_LeftEnd;
Vec2f m_RightStart, m_RightEnd;
bool m_Valid;
};
struct ObstructionPoly
{
static const int MAX_OBSTRUCTION_POLY_SIZE = 9; // Start with a triangle, sweep up to 3 times = 3 + 2*3 = 9
// I'm not using an atFixedArray here because the dumbass compiler can't optimize out the constructor calls for the Vec2f constructor.
Vec2f m_Verts[MAX_OBSTRUCTION_POLY_SIZE];
int m_Count;
const CVehicle* m_Vehicle;
};
// Polar coordinates (relative to the front bumper of the car) for where the circular obstruction is
struct ObstructionCircle
{
float m_Orientation;
float m_Distance;
float m_HalfBlockedAngleRange; // the "radius" of the circle, as measured in radians for a circle at m_Distance
const CEntity* m_Entity;
CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo::eObstructionType m_ObstructionType;
};
atFixedArray<RoadSegment, CVehicleNodeList::CAR_MAX_NUM_PATHNODES_STORED> m_RoadSegments;
atFixedArray<ObstructionPoly, CEntityScanner::MAX_NUM_ENTITIES> m_VehicleObstructions;
atFixedArray<ObstructionCircle, 2 * CEntityScanner::MAX_NUM_ENTITIES> m_PedAndObjectObstructions;
#if 0
inline bool CanCullSegment(Vec2f testPoint1, Vec2f testPoint2, Vec2f center, Vec2f planeNormal1, Vec2f planeNormal2, float radiusSq)
{
Vec2f testPoint1InWedgeSpace = testPoint1 - center;
Vec2f testPoint2InWedgeSpace = testPoint2 - center;
float dist1 = MagSquared(testPoint1InWedgeSpace);
float dist2 = MagSquared(testPoint2InWedgeSpace);
if (dist1 > radiusSq && dist2 > radiusSq)
{
return true;
}
float dot11 = Dot(testPoint1InWedgeSpace, planeNormal1);
float dot21 = Dot(testPoint2InWedgeSpace, planeNormal1);
if (dot11 < 0.0f && dot21 < 0.0f)
{
return true;
}
float dot12 = Dot(testPoint1InWedgeSpace, planeNormal2);
float dot22 = Dot(testPoint2InWedgeSpace, planeNormal2);
if (dot12 < 0.0f && dot22 < 0.0f)
{
return true;
}
return false;
}
inline bool CanCullPoint(Vec2f testPoint, Vec2f center, Vec2f planeNormal1, Vec2f planeNormal2)
{
Vec2f testPointInWedgeSpace = testPoint - center;
float dot1 = Dot(testPointInWedgeSpace, planeNormal1);
float dot2 = Dot(testPointInWedgeSpace, planeNormal2);
return (dot1 < 0.0f || dot2 < 0.0f);
}
void WedgeCull(float minAngle, float maxAngle)
{
// Based on the test direction being computed like this:
// Vec2f testDir(::rage::Cosf(avoidanceInfo.fDirection), ::rage::Sinf(avoidanceInfo.fDirection));
// I want a perpendicular vector to that, that points "in" to the wedge for minAngle and maxAngle
const Vec3V& centerV = pVeh->GetVehiclePosition();
Vec2f center(centerV.GetXf(), centerV.GetYf());
Vec2f planeNormal1(-::rage::Sinf(minAngle), ::rage::Cosf(minAngle));
Vec2f planeNormal2(::rage::Sinf(maxAngle), -::rage::Cosf(maxAngle));
// cull it all!
// Road segments
float edgeCullDist = fDesiredSpeed * 1.7f;
for(int i = 0; i < m_RoadSegments.GetCount(); i++)
{
if (!m_RoadSegments[i].m_Valid)
{
continue;
}
RoadSegment& rs = m_RoadSegments[i];
rs.m_Culled =
CanCullSegment(rs.m_LeftStart, rs.m_LeftEnd, center, planeNormal1, planeNormal2, edgeCullDist) &&
CanCullSegment(rs.m_RightStart, rs.m_RightEnd, center, planeNormal1, planeNormal2, edgeCullDist);
}
for(int i = 0; i < m_VehicleObstructions.GetCount(); i++)
{
ObstructionPoly& obs = m_VehicleObstructions[i];
bool canCull = true;
for(int i = 0; i < obs.m_Count; i++)
{
if (!CanCullPoint(obs.m_Verts[i], center, planeNormal1, planeNormal2))
{
canCull = false;
break;
}
}
obs.m_Culled = canCull;
}
}
#endif
void DebugDraw(const CVehicle* pVeh)
{
// Road segments
for(int i = 0; i < m_RoadSegments.GetCount(); i++)
{
if (!m_RoadSegments[i].m_Valid)
{
continue;
}
fwVectorMap::DrawLine(m_RoadSegments[i].m_LeftStart, m_RoadSegments[i].m_LeftEnd, Color_green, Color_green);
fwVectorMap::DrawLine(m_RoadSegments[i].m_RightStart, m_RoadSegments[i].m_RightEnd, Color_blue, Color_blue);
}
for(int i = 0; i < m_VehicleObstructions.GetCount(); i++)
{
ObstructionPoly& poly = m_VehicleObstructions[i];
Vec2f vecStart = poly.m_Verts[poly.m_Count-1];
for(int j = 0; j < poly.m_Count; j++)
{
Vec2f vecEnd = poly.m_Verts[j];
fwVectorMap::DrawLine(vecStart, vecEnd, Color_LightSalmon, Color_LightSalmon);
fwVectorMap::DrawLine(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition()), Color_grey, Color_LightSalmon);
vecStart = vecEnd;
}
}
for(int i = 0; i < m_PedAndObjectObstructions.GetCount(); i++)
{
ObstructionCircle& circle = m_PedAndObjectObstructions[i];
Vec3V pos = pVeh->GetVehiclePosition();
// Convert "back" from polar coords
float DiffX = rage::Cosf(circle.m_Orientation);
float DiffY = rage::Sinf(circle.m_Orientation);
pos += Vec3V(DiffX * circle.m_Distance, DiffY * circle.m_Distance, 0.0f);
// tan(theta) = radius / distance
float radius = /*rage::Tanf*/(circle.m_HalfBlockedAngleRange) * circle.m_Distance;
Color32 color = circle.m_ObstructionType == CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo::Obstruction_Ped ? Color_gold : Color_LightBlue;
fwVectorMap::DrawCircle(RCC_VECTOR3(pos), radius, color, false);
}
}
};
bool HelperShouldUseRacingLogic(const bool bIsRacing, const bool bIsBike, const float fCurrentSpeed, const CVehicle* pOtherCar)
{
const float fOtherCarSpeed = pOtherCar->GetAiXYSpeed();
bool bIsConservative = pOtherCar->m_nVehicleFlags.bIsRacingConservatively;
bool bIsVelValid = bIsConservative;
if(!bIsVelValid)
{
static dev_float s_fVelThresholdForRacingBike = 10.0f;
static dev_float s_fVelThresholdForRacingCar = 10.0f;
const float fVelThreshold = !bIsBike ? s_fVelThresholdForRacingCar : s_fVelThresholdForRacingBike;
bIsVelValid = (fabsf(fCurrentSpeed - fOtherCarSpeed) < fVelThreshold);
}
return (bIsRacing && pOtherCar->m_nVehicleFlags.bIsRacing)
&& fOtherCarSpeed > 1.0f
&& bIsVelValid;
};
Vector3 HelperGetOtherVehsRacingVelocity(const CVehicle* pOtherVeh)
{
//make this slightly larger so when two vehicles have the same
//desired velocity (common at the start of a race)
//the one behind will yield
//if the actual velocity is higher than the desired--use that
static dev_float s_fVelocityMultiplier = 1.01f;
const CTaskVehicleMissionBase* pOtherActiveTask = pOtherVeh->GetIntelligence()->GetActiveTask();
const float fDesiredVel = pOtherActiveTask ? (pOtherActiveTask->GetCruiseSpeed() * s_fVelocityMultiplier)
: pOtherVeh->GetIntelligence()->m_fDesiredSpeedThisFrame * s_fVelocityMultiplier;
const float fAiXYSpeed = pOtherVeh->GetAiXYSpeed();
if (fDesiredVel >= 0.0f && fDesiredVel > fAiXYSpeed)
{
return VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection() * ScalarV(fDesiredVel));
}
else
{
//we were just created and probably haven't cached a velocity yet
return pOtherVeh->GetAiVelocity();
}
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TestOneDirectionAgainstObstructions(const AvoidanceTaskObstructionData& obstructionData, AvoidanceInfo& avoidanceInfo, bool testRoadEdges, bool& bShouldGiveWarning, const CPhysical** pHitEntity)
{
const CVehicle* pVeh = obstructionData.pVeh;
const Vector3 vOurVelocity = pVeh->GetAiVelocity();
// Test against the vehicle polys
Vec3V vehPos(pVeh->GetVehiclePosition());
Vec2f vehPos2d(vehPos.GetXf(), vehPos.GetYf());
const bool bCanUseLongerProbe = pVeh->PopTypeIsMission() || pVeh->m_nVehicleFlags.bIsLawEnforcementVehicle || (pVeh->GetDriver() && pVeh->GetDriver()->PopTypeIsMission());
const float fLookaheadTime = obstructionData.GetAvoidanceLookaheadTime(bCanUseLongerProbe);
// The car might be stopped... so the desired speed might not actually be achievable in fLookaheadTime.
// Lets do a rough approximation of acceleration to rein in the lookahead distance to something more reasonable
// Useful upper bound? - supercars go 0-60 in 2.5s (!), which is an acceleration of about 10m/s^2.
//TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fMaxAccelerationForLookahead, 5.0f, 0.0f, 100.0f, 0.1f);
//const float fAchievableSpeed = Min(obstructionData.fDesiredSpeed, Max(0.0f, obstructionData.fCurrentSpeed + (s_fMaxAccelerationForLookahead * fLookaheadTime)));
//const float fLookaheadDistance = obstructionData.fDesiredSpeed * fLookaheadTime;
//obstructionData.fLookaheadDistance = fLookaheadDistance;
//obstructionData.fAchievableDistance = fAchievableSpeed * fLookaheadTime;
//TODO SPARR; Do we really want to use desired speed here?!
Vec2f testDir(::rage::Cosf(avoidanceInfo.fDirection), ::rage::Sinf(avoidanceInfo.fDirection));
testDir *= Vec2f(obstructionData.fDesiredSpeed * fLookaheadTime, obstructionData.fDesiredSpeed * fLookaheadTime); // Look ahead 1.7s into the future
bool bHitSomething = false;
// Note: we could test this single direction using the new vectorized functions, by simply doing this:
// TestAndScoreLineSegmentsAgainstVehicles(obstructionData, &avoidanceInfo, 1, &bHitSomething, bShouldGiveWarning, pHitEntity);
// TestAndScoreLineSegmentsAgainstCylindricalObstructions(obstructionData, &avoidanceInfo, 1, &bHitSomething, pHitEntity);
// However, when I measured that, it was unfortunately slower that way, maybe by 2x for the vehicle case and 4x for
// the ped/object case.
int polyCount = obstructionData.m_VehicleObstructions.GetCount();
for(int i = 0; i < polyCount; i++)
{
const AvoidanceTaskObstructionData::ObstructionPoly& poly = obstructionData.m_VehicleObstructions[i];
float tEnter, tLeave;
bool hitSomething = geom2D::Test2DSegVsPolygon(tEnter, tLeave, vehPos2d, vehPos2d + testDir, &poly.m_Verts[0], poly.m_Count);
if (hitSomething)
{
// The distance isn't the intersection point, it's the distance between car centers
const float fDistToOtherCar = tEnter * obstructionData.fDesiredSpeed * fLookaheadTime;
bool bIsStationaryCar = IsThisAStationaryCar(poly.m_Vehicle);
if (fDistToOtherCar < FLT_EPSILON)
{
// Problem! tEnter is actually inside the obstruction poly, making us consider every direction obstructed.
// This is not helpful. So do the old test that lets this car move out of the way of the obstruction car
// before the obstruction car actually obstructs.
bool bIsParkedCar = IsThisAParkedCar(poly.m_Vehicle);
bool bOldTestSuccess = false;
//if the first test failed because our search pos was inside the poly, dont' add any
//padding we may have done the first time
const bool bPreventAddMarginForOtherCar = true;
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
bOldTestSuccess = TestDirectionsAgainstSingleVehicle(obstructionData, const_cast<CVehicle*>(poly.m_Vehicle), bIsStationaryCar, bIsParkedCar, bShouldGiveWarning, &avoidanceInfo, 1, bPreventAddMarginForOtherCar);
#else
bOldTestSuccess = TestDirectionAgainstSingleVehicle(obstructionData, const_cast<CVehicle*>(poly.m_Vehicle), bIsStationaryCar, bIsParkedCar, bShouldGiveWarning, avoidanceInfo, bPreventAddMarginForOtherCar);
#endif
if (bOldTestSuccess)
{
bHitSomething = true;
if (pHitEntity)
{
*pHitEntity = poly.m_Vehicle;
}
}
continue; // The function we just called scored the obstruction, if there was one.
}
//const Vector3 vOtherVelocity = poly.m_Vehicle->GetAiVelocity();
const Vector3 vOtherVelocity = HelperShouldUseRacingLogic(obstructionData.bRacing, pVeh->InheritsFromBike(), obstructionData.fCurrentSpeed, poly.m_Vehicle)
? HelperGetOtherVehsRacingVelocity(poly.m_Vehicle)
: poly.m_Vehicle->GetAiVelocity();
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_Vehicle, fDistToOtherCar, vOurVelocity, vOtherVelocity, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
// dist to the other car is the dist to t value tEnter. Length of the segment is fDesiredSpeed * 1.7
avoidanceInfo.fDistToObstruction = tEnter * obstructionData.fDesiredSpeed * fLookaheadTime;
// avoidanceInfo.fDistToObstruction = Dist(poly.m_Vehicle->GetVehiclePosition(), vehPos).Getf();
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_Vehicle;
avoidanceInfo.vVelocity = vOtherVelocity;
Vector3 vDirToObstruction = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition() - vehPos);
avoidanceInfo.fDirToObstruction = fwAngle::GetATanOfXY(vDirToObstruction.x, vDirToObstruction.y);
avoidanceInfo.fDirToObstruction = fwAngle::LimitRadianAngle(avoidanceInfo.fDirToObstruction);
avoidanceInfo.vPosition = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition());
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsStationaryCar = bIsStationaryCar;
avoidanceInfo.bIsObstructedByLaw = IsObstructedByLawEnforcementVehicle(poly.m_Vehicle);
avoidanceInfo.bIsTurningLeftAtJunction = poly.m_Vehicle->m_nVehicleFlags.bTurningLeftAtJunction || poly.m_Vehicle->m_nVehicleFlags.bIsWaitingToTurnLeft;
avoidanceInfo.bIsHesitating = poly.m_Vehicle->m_nVehicleFlags.bIsHesitating;
if (ShouldGiveWarning(pVeh, poly.m_Vehicle))
{
avoidanceInfo.bGiveWarning = true;
bShouldGiveWarning = true;
m_vCachedGiveWarningPosition = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition());
}
bHitSomething = true;
if (pHitEntity)
{
*pHitEntity = poly.m_Vehicle;
}
}
}
}
// Test against all circular obstructions
// Use the coordinates of our front bumper.
Vector3 bumperCrs = RCC_VECTOR3(obstructionData.vBumperCenter);
int circleCount = obstructionData.m_PedAndObjectObstructions.GetCount();
for(int i = 0; i < circleCount; i++)
{
const AvoidanceTaskObstructionData::ObstructionCircle& circle = obstructionData.m_PedAndObjectObstructions[i];
float desiredSteerAngle = SubtractAngleShorterFast(circle.m_Orientation, avoidanceInfo.fDirection);
desiredSteerAngle = fwAngle::LimitRadianAngle(desiredSteerAngle);
// bool bHitPed = false;
if(ABS(desiredSteerAngle) < circle.m_HalfBlockedAngleRange)
{
Vector3 vOtherVelocity(Vector3::ZeroType);
if (circle.m_ObstructionType == AvoidanceInfo::Obstruction_Ped)
{
vOtherVelocity = reinterpret_cast<const CPed*>(circle.m_Entity)->GetVelocity();
}
else if (circle.m_ObstructionType == AvoidanceInfo::Obstruction_Object)
{
vOtherVelocity = reinterpret_cast<const CObject*>(circle.m_Entity)->GetVelocity();
}
const float fObstructionScore = ScoreOneObstructionAngleIndependent(circle.m_ObstructionType, circle.m_Distance, vOurVelocity, vOtherVelocity, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = circle.m_Distance;
avoidanceInfo.ObstructionType = circle.m_ObstructionType;
Vector3 vDirToObstruction = VEC3V_TO_VECTOR3(circle.m_Entity->GetTransform().GetPosition()) - bumperCrs;
avoidanceInfo.fDirToObstruction = fwAngle::GetATanOfXY(vDirToObstruction.x, vDirToObstruction.y);
avoidanceInfo.fDirToObstruction = fwAngle::LimitRadianAngle(avoidanceInfo.fDirToObstruction);
avoidanceInfo.vPosition = VEC3V_TO_VECTOR3(circle.m_Entity->GetTransform().GetPosition());
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.vVelocity = vOtherVelocity;
avoidanceInfo.bIsObstructedByLaw = (
((circle.m_ObstructionType == AvoidanceInfo::Obstruction_Ped) && IsObstructedByLawEnforcementPed((CPed*)circle.m_Entity)) ||
((circle.m_ObstructionType == AvoidanceInfo::Obstruction_Vehicle) && IsObstructedByLawEnforcementVehicle((CVehicle*)circle.m_Entity)));
avoidanceInfo.bIsTurningLeftAtJunction = false;
avoidanceInfo.bIsHesitating = false;
bHitSomething = true;
if (pHitEntity)
{
*pHitEntity = (const CPhysical*)circle.m_Entity;
}
}
}
}
if (!testRoadEdges || IsDrivingFlagSet(DF_GoOffRoadWhenAvoiding))
{
return bHitSomething;
}
#if USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
TestAndScoreLineSegmentsCrossRoadBoundariesUsingFollowRoute(obstructionData, &avoidanceInfo, 1, &bHitSomething);
#else // USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
// Test against road edges
const Vector2 min2d(obstructionData.MinX, obstructionData.MinY);
const Vector2 max2d(obstructionData.MaxX, obstructionData.MaxY);
const float fTestDistance = min2d.Dist(max2d);
float fDistToRoadEdge = -1.0f;
#if USE_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
if (DoesLineSegmentCrossRoadBoundariesUsingFollowRoute(obstructionData, avoidanceInfo.fDirection, fTestDistance, fDistToRoadEdge))
#else
if (DoesLineSegmentCrossRoadBoundariesUsingNodeList(obstructionData, avoidanceInfo.fDirection, fTestDistance, fDistToRoadEdge))
#endif
{
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_RoadEdge, fDistToRoadEdge, vOurVelocity, ORIGIN, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = fDistToRoadEdge;
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_RoadEdge;
avoidanceInfo.vVelocity = ORIGIN;
avoidanceInfo.vPosition = ORIGIN; //TODO: return a position from DoesLineSegmentCrossRoadBoundaries
avoidanceInfo.fDirToObstruction = avoidanceInfo.fDirection;
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsObstructedByLaw = false;
avoidanceInfo.bIsWaitingToTurnLeft = false;
bHitSomething = true;
}
}
#endif // USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
return bHitSomething;
}
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
void CTaskVehicleGoToPointWithAvoidanceAutomobile::TestAndScoreLineSegmentsAgainstVehicles(const AvoidanceTaskObstructionData& obstructionData, AvoidanceInfo* avoidanceInfoArray, int numDirections, bool* bHitSomethingArrayInOut, bool& bShouldGiveWarning, const CPhysical** pHitEntity)
{
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, USE_HIGH_SPEED_PROJECTION, true );
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, MAX_LOOKAHEAD_DIST, 80.0f, 0.0f, 200.0f, 1.0f)
const CVehicle* pVeh = obstructionData.pVeh;
const Vector3 vOurVelocity = pVeh->GetAiVelocity();
const ScalarV fOurSpeed = ScalarV(vOurVelocity.Mag());
// Test against the vehicle polys
Vec3V vehPos(pVeh->GetVehiclePosition());
Vec2f vehPos2d(vehPos.GetXf(), vehPos.GetYf());
const bool bCanUseLongerProbe = pVeh->PopTypeIsMission() || pVeh->m_nVehicleFlags.bIsLawEnforcementVehicle || (pVeh->GetDriver() && pVeh->GetDriver()->PopTypeIsMission());
const float fLookaheadTime = obstructionData.GetAvoidanceLookaheadTime(bCanUseLongerProbe);
// The car might be stopped... so the desired speed might not actually be achievable in fLookaheadTime.
// Lets do a rough approximation of acceleration to rein in the lookahead distance to something more reasonable
// Useful upper bound? - supercars go 0-60 in 2.5s (!), which is an acceleration of about 10m/s^2.
//TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fMaxAccelerationForLookahead, 5.0f, 0.0f, 100.0f, 0.1f);
//const float fAchievableSpeed = Min(obstructionData.fDesiredSpeed, Max(0.0f, obstructionData.fCurrentSpeed + (s_fMaxAccelerationForLookahead * fLookaheadTime)));
//const float fLookaheadDistance = obstructionData.fDesiredSpeed * fLookaheadTime;
//obstructionData.fLookaheadDistance = fLookaheadDistance;
//obstructionData.fAchievableDistance = fAchievableSpeed * fLookaheadTime;
Assert(numDirections <= kMaxObstructionProbeDirections);
static const int kMaxVecs = kMaxObstructionProbeDirections/4;
CompileTimeAssert(kMaxVecs*4 == kMaxObstructionProbeDirections);
Vec4V testDeltaXArrayVecs[kMaxVecs];
Vec4V testDeltaYArrayVecs[kMaxVecs];
Vec4V distToIsectArrayVecs[kMaxVecs];
VecBoolV hitArrayVecs[kMaxVecs];
//VecBoolV tooCloseArrayVecs[kMaxVecs];
const ScalarV desiredSpeedV = LoadScalar32IntoScalarV(obstructionData.fDesiredSpeed);
const ScalarV lookAheadTimeV(fLookaheadTime);
const ScalarV desiredLookAheadDistV = Scale(desiredSpeedV, lookAheadTimeV);
const ScalarV lookAheadDistV = rage::Min(desiredLookAheadDistV, ScalarV(MAX_LOOKAHEAD_DIST));
// Loop over the avoidance objects and initialize testDelta[X/Y]ArrayVecs.
const int lastDir = numDirections - 1;
for(int j = 0, k = 0; j < numDirections; j += 4, k++)
{
// Note: the Min() stuff here is a bit messy, but reading past the array passed in
// by the user would be a bit risky.
const AvoidanceInfo& avoidanceInfo0 = avoidanceInfoArray[j];
const AvoidanceInfo& avoidanceInfo1 = avoidanceInfoArray[Min(j + 1, lastDir)];
const AvoidanceInfo& avoidanceInfo2 = avoidanceInfoArray[Min(j + 2, lastDir)];
const AvoidanceInfo& avoidanceInfo3 = avoidanceInfoArray[Min(j + 3, lastDir)];
// Load up the four directions in vector registers.
const ScalarV dir0V = LoadScalar32IntoScalarV(avoidanceInfo0.fDirection);
const ScalarV dir1V = LoadScalar32IntoScalarV(avoidanceInfo1.fDirection);
const ScalarV dir2V = LoadScalar32IntoScalarV(avoidanceInfo2.fDirection);
const ScalarV dir3V = LoadScalar32IntoScalarV(avoidanceInfo3.fDirection);
// Put them together in one vector, and compute the angle we want to compute cos and sin for.
const Vec4V dirV(dir0V, dir1V, dir2V, dir3V);
// Compute the sin and cos values of the four angles.
Vec4V sinV, cosV;
SinAndCos(sinV, cosV, dirV);
const Vec4V testDirXV = Scale(cosV, lookAheadDistV);
const Vec4V testDirYV = Scale(sinV, lookAheadDistV);
// Store the probe deltas.
testDeltaXArrayVecs[k] = testDirXV;
testDeltaYArrayVecs[k] = testDirYV;
}
const int numVecs = (numDirections + 3)/4;
// Test4x2DSegVsPolygon() needs an array of Vec2V's, not Vec2f, so we have to convert.
// This array provides the space to do so. We probably don't want to store them as Vec2V's
// due to the memory overhead, though it might be an option if this is just data that exists
// temporarily.
Vec2V vertsVecArray[AvoidanceTaskObstructionData::ObstructionPoly::MAX_OBSTRUCTION_POLY_SIZE];
// Loop over the polygons (representing vehicles).
const int polyCount = obstructionData.m_VehicleObstructions.GetCount();
for(int i = 0; i < polyCount; i++)
{
Assert(numDirections <= kMaxObstructionProbeDirections);
const AvoidanceTaskObstructionData::ObstructionPoly& poly = obstructionData.m_VehicleObstructions[i];
// Convert the vertices to Vec2V format.
const int numVerts = poly.m_Count;
Assert(numVerts <= AvoidanceTaskObstructionData::ObstructionPoly::MAX_OBSTRUCTION_POLY_SIZE);
for(int q = 0; q < numVerts; q++)
{
vertsVecArray[q] = Vec2V(poly.m_Verts[q].GetX(), poly.m_Verts[q].GetY());
}
// Splat the vehicle position.
const Vec4V vehPos2dXV(vehPos.GetX());
const Vec4V vehPos2dYV(vehPos.GetY());
// We will keep track of if we hit anything, and if any of
// the intersections were too close to be useful.
VecBoolV hitAnything4V(V_F_F_F_F);
VecBoolV anyTooClose4V(V_F_F_F_F);
// Could also keep track of if all intersections are too close:
#if __ASSERT
//VecBoolV allTooClose4V(V_T_T_T_T);
#endif
const ScalarV tooCloseThresholdV(V_FLT_EPSILON);
spdAABB tempBox;
const spdAABB& boundBox = poly.m_Vehicle->GetLocalSpaceBoundBox(tempBox);
const ScalarV passByThresholdVSqr(16.0f + boundBox.GetMax().GetYf() * boundBox.GetMax().GetYf());
const Vec4V vOtherCarPos2DXV(poly.m_Vehicle->GetVehiclePosition().GetX());
const Vec4V vOtherCarPos2DYV(poly.m_Vehicle->GetVehiclePosition().GetY());
const Vec3V vOtherCarVel = VECTOR3_TO_VEC3V(poly.m_Vehicle->GetVelocity());
Vector3 vOtherCarVelNorm = poly.m_Vehicle->GetVelocity();
vOtherCarVelNorm.Normalize();
Vector3 vOurVelNorm = vOurVelocity;
vOurVelNorm.Normalize();
float fVelDot = vOurVelNorm.Dot(vOtherCarVelNorm);
//SPARR; limit this to chasing vehicles for now as don't want to risk affecting everything else
bool bDoHighSpeedProjection = USE_HIGH_SPEED_PROJECTION && pVeh->GetIntelligence()->GetAvoidanceCache().m_pTarget != NULL &&
pVeh->PopTypeIsMission() && IsGreaterThanAll(fOurSpeed, ScalarV(20.0f)) && fVelDot < 0.8f;
ScalarV fIntersectRangeScalar = ScalarV(RampValue(Abs(fVelDot), 0.5f, 0.8f, 1.0f, 2.0f));
for(int k = 0; k < numVecs; k++)
{
// Get coordinates of four 2D segments (directions) to test.
const Vec4V testDeltaXV = testDeltaXArrayVecs[k];
const Vec4V testDeltaYV = testDeltaYArrayVecs[k];
// Note: Test4x2DSegVsPolygon() actually has to subtract these again, so there is an opportunity
// to optimize that here.
const Vec4V segEndXV = Add(vehPos2dXV, testDeltaXV);
const Vec4V segEndYV = Add(vehPos2dYV, testDeltaYV);
Vec4V tEnterV, tLeaveV;
const VecBoolV hitV = geom2D::Test4x2DSegVsPolygon(tEnterV, tLeaveV, vehPos2dXV, vehPos2dYV, segEndXV, segEndYV, &vertsVecArray[0], poly.m_Count);
// Compute the distance to the intersections by multiplying the T values by
// the lookahead distance, which is the length of the segment. This only gives us something meaningful
// for the ones that are hits, but we check that elsewhere.
const Vec4V distToIsectsV = Scale(tEnterV, lookAheadDistV);
VecBoolV hitWithinRangeV = hitV;
if(bDoHighSpeedProjection)
{
//compute positions at intersect time
const Vec4V fTimeToIsect = distToIsectsV / fOurSpeed;
const Vec4V carFuturePosXV = Add(vehPos2dXV, Scale(testDeltaXV, tEnterV));
const Vec4V carFuturePosYV = Add(vehPos2dYV, Scale(testDeltaYV, tEnterV));
const Vec4V otherCarFuturePosXV = Add(vOtherCarPos2DXV, Scale(vOtherCarVel.GetX(), fTimeToIsect));
const Vec4V otherCarFuturePosYV = Add(vOtherCarPos2DYV, Scale(vOtherCarVel.GetY(), fTimeToIsect));
const Vec4V fDistAtFutureIsectXV = otherCarFuturePosXV - carFuturePosXV;
const Vec4V fDistAtFutureIsectYV = otherCarFuturePosYV - carFuturePosYV;
const Vec4V fSumXYDistsSqr = square(fDistAtFutureIsectXV) + square(fDistAtFutureIsectYV);
const VecBoolV bOutsideDistRange = IsLessThan(fSumXYDistsSqr, Scale(Vec4V(passByThresholdVSqr), fIntersectRangeScalar));
//ignore hits that are outside our intersect range
hitWithinRangeV = And(bOutsideDistRange, hitV);
}
// Check if the distance is within the "too close" threshold, if it exists.
const VecBoolV tooCloseNoHitCheckV = IsLessThan(distToIsectsV, Vec4V(tooCloseThresholdV));
// Mask out anything that we thought may be too close, but wasn't actually a hit.
const VecBoolV tooCloseV = And(tooCloseNoHitCheckV, hitWithinRangeV);
#if __ASSERT
//allTooClose4V = And(allTooClose4V, Or(tooCloseNoHitCheckV, InvertBits(hitV)));
#endif
// OR together our findings.
anyTooClose4V = Or(anyTooClose4V, tooCloseV);
hitAnything4V = Or(hitAnything4V, hitWithinRangeV);
// Store the distance and hit status in arrays.
distToIsectArrayVecs[k] = distToIsectsV;
hitArrayVecs[k] = hitWithinRangeV;
}
// Check if we hit anything at all. If we didn't, there is nothing more to do with
// this vehicle.
if(IsFalseAll(hitAnything4V))
{
continue;
}
const bool bIsStationaryCar = IsThisAStationaryCar(poly.m_Vehicle);
bool anyDirHit = false;
if(!IsFalseAll(anyTooClose4V))
{
// If one of the directions start inside of the polygon, they probably all do,
// so this would probably hold up:
// Assert(IsTrueAll(allTooClose4V));
// Problem! tEnter is actually inside the obstruction poly, making us consider every direction obstructed.
// This is not helpful. So do the old test that lets this car move out of the way of the obstruction car
// before the obstruction car actually obstructs.
bool bIsParkedCar = IsThisAParkedCar(poly.m_Vehicle);
//if we failed the first test for being inside the poly, don't add
//any margin to the other vehicle's bb this time
const bool bPreventAddMarginForOtherVehicle = true;
TestDirectionsAgainstSingleVehicle(obstructionData, const_cast<CVehicle*>(poly.m_Vehicle), bIsStationaryCar, bIsParkedCar, bShouldGiveWarning, avoidanceInfoArray, numDirections, bPreventAddMarginForOtherVehicle);
// We are basically assuming here that if any of the intersections were too close (zero length),
// they all are, and we should use the old algorithm in all directions. Also, we didn't check
// which intersections were valid, because they should all be since all the directions started
// inside the polygon.
continue;
}
// Compute some stuff about the obstruction and its relation to us, independently of the test direction.
const bool giveWarningIfHit = ShouldGiveWarning(pVeh, poly.m_Vehicle);
const Vector3 vOtherVelocity = HelperShouldUseRacingLogic(obstructionData.bRacing, pVeh->InheritsFromBike(), obstructionData.fCurrentSpeed, poly.m_Vehicle)
? HelperGetOtherVehsRacingVelocity(poly.m_Vehicle)
: poly.m_Vehicle->GetAiVelocity();
const Vector3 vDirToObstruction = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition() - vehPos);
const float dirToObstruction = fwAngle::LimitRadianAngle(fwAngle::GetATanOfXY(vDirToObstruction.x, vDirToObstruction.y));
// Reinterpret the hit/distance arrays to work on them without vectors.
const u32 *hitArray = (const u32*)hitArrayVecs;
const float* distToIsectArray = (const float*)distToIsectArrayVecs;
for(int j = 0; j < numDirections; j++)
{
// Check if this direction was a hit.
if(hitArray[j])
{
AvoidanceInfo& avoidanceInfo = avoidanceInfoArray[j];
// Score the obstruction for this direction. Note that we pass in the distance to the intersection
// here, so this score will not be the same for all directions.
const float fDistToOtherCar = distToIsectArray[j];
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_Vehicle, fDistToOtherCar, vOurVelocity, vOtherVelocity, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
// dist to the other car is the dist to t value tEnter. Length of the segment is fDesiredSpeed * 1.7
avoidanceInfo.fDistToObstruction = fDistToOtherCar;
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_Vehicle;
avoidanceInfo.vVelocity = vOtherVelocity;
avoidanceInfo.fDirToObstruction = dirToObstruction;
avoidanceInfo.vPosition = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition());
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsStationaryCar = bIsStationaryCar;
avoidanceInfo.bIsObstructedByLaw = IsObstructedByLawEnforcementVehicle(poly.m_Vehicle);
avoidanceInfo.bIsTurningLeftAtJunction = poly.m_Vehicle->m_nVehicleFlags.bTurningLeftAtJunction || poly.m_Vehicle->m_nVehicleFlags.bIsWaitingToTurnLeft;
avoidanceInfo.bIsHesitating = poly.m_Vehicle->m_nVehicleFlags.bIsHesitating;
anyDirHit = true;
if (giveWarningIfHit)
{
avoidanceInfo.bGiveWarning = true;
}
// Record that we hit something.
bHitSomethingArrayInOut[j] = true;
// Store the entity. This really only makes sense if we have just one direction.
if(pHitEntity)
{
*pHitEntity = poly.m_Vehicle;
}
}
}
}
// If we had any intersections which beat the previous scores, do some warning-related stuff.
// No need to do this for each direction.
if(anyDirHit)
{
if (giveWarningIfHit)
{
bShouldGiveWarning = true;
m_vCachedGiveWarningPosition = VEC3V_TO_VECTOR3(poly.m_Vehicle->GetVehiclePosition());
}
}
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::TestAndScoreLineSegmentsAgainstCylindricalObstructions(
const AvoidanceTaskObstructionData& obstructionData,
AvoidanceInfo* avoidanceInfoArray, int numDirections, bool* bHitSomethingArrayInOut,
const CPhysical** pHitEntity) const
{
const CVehicle* pVeh = obstructionData.pVeh;
const Vec3V ourVelocityV = VECTOR3_TO_VEC3V(pVeh->GetAiVelocity());
// Test against all circular obstructions
// Use the coordinates of our front bumper.
const Vec3V bumperCrsV = obstructionData.vBumperCenter;
const int circleCount = obstructionData.m_PedAndObjectObstructions.GetCount();
// We will need two arrays of vectors, one for the directions to test in,
// and one for the results.
Assert(numDirections <= kMaxObstructionProbeDirections);
static const int kMaxVecs = kMaxObstructionProbeDirections/4;
CompileTimeAssert(kMaxVecs*4 == kMaxObstructionProbeDirections);
Vec4V avoidanceInfoDirVecs[kMaxVecs];
VecBoolV hitThisObstructionVecs[kMaxVecs];
// Look at four directions at a time, and stuff the angles into the avoidanceInfoDirVecs array.
const int lastDir = numDirections - 1;
for(int j = 0, k = 0; j < numDirections; j += 4, k++)
{
const AvoidanceInfo& avoidanceInfo0 = avoidanceInfoArray[j];
const AvoidanceInfo& avoidanceInfo1 = avoidanceInfoArray[Min(j + 1, lastDir)];
const AvoidanceInfo& avoidanceInfo2 = avoidanceInfoArray[Min(j + 2, lastDir)];
const AvoidanceInfo& avoidanceInfo3 = avoidanceInfoArray[Min(j + 3, lastDir)];
// Load up the four directions in vector registers.
// TODO: Should consider stuffing this in the W component of the position or velocity,
// so we can load them as aligned vectors.
const ScalarV heading0V = LoadScalar32IntoScalarV(avoidanceInfo0.fDirection);
const ScalarV heading1V = LoadScalar32IntoScalarV(avoidanceInfo1.fDirection);
const ScalarV heading2V = LoadScalar32IntoScalarV(avoidanceInfo2.fDirection);
const ScalarV heading3V = LoadScalar32IntoScalarV(avoidanceInfo3.fDirection);
// Store the search positions.
const Vec4V headingV(heading0V, heading1V, heading2V, heading3V);
avoidanceInfoDirVecs[k] = headingV;
}
const int numVecs = (numDirections + 3)/4;
// Some constants that we will need.
const Vec4V piV(V_PI);
const Vec4V minPiV(V_NEG_PI);
const Vec4V twoPiV(V_TWO_PI);
// Loop over the circular obstructions one by one.
for(int i = 0; i < circleCount; i++)
{
// Grab the information from the obstruction that we need to do the tests.
const AvoidanceTaskObstructionData::ObstructionCircle& circle = obstructionData.m_PedAndObjectObstructions[i];
const Vec4V circleOrientationV(LoadScalar32IntoScalarV(circle.m_Orientation));
const Vec4V halfBlockedAngleRangeV(LoadScalar32IntoScalarV(circle.m_HalfBlockedAngleRange));
// Loop over the vectors containing avoidance directions, and test four directions at a
// time against this obstruction.
VecBoolV hitAny4V(V_FALSE);
for(int k = 0; k < numVecs; k++)
{
const Vec4V dirV = avoidanceInfoDirVecs[k];
// We basically want to do this test, for each direction:
// float desiredSteerAngle = SubtractAngleShorterFast(circle.m_Orientation, avoidanceInfo.fDirection);
// desiredSteerAngle = fwAngle::LimitRadianAngle(desiredSteerAngle);
// if(ABS(desiredSteerAngle) < circle.m_HalfBlockedAngleRange)
// First, subtract the angle between the circle and the avoidance direction,
// and add/subtract two times PI.
const Vec4V angleV = Subtract(circleOrientationV, dirV);
const Vec4V angleMinTwoPiV = Subtract(angleV, twoPiV);
const Vec4V anglePlusTwoPiV = Add(angleV, twoPiV);
// Next, use vector selection to wrap the angle to the -PI..PI range.
// Note: looks like circle.m_Orientation is currently in the 0..2*PI range,
// but the avoidance angles should be -PI..PI. The difference between the angles
// must still be within the -3*PI..3*PI range, so should be OK to do it like this:
// if (angle>PI) return angle-2.0f*PI;
// if (angle<-PI) return angle+2.0f*PI;
const Vec4V angleClampV = SelectFT(IsGreaterThan(angleV, piV), angleV, angleMinTwoPiV);
const Vec4V desiredSteerAngleV = SelectFT(IsLessThan(angleV, minPiV), angleClampV, anglePlusTwoPiV);
// This should hold up.
// Assert(desiredSteerAngleV.GetXf() >= -PI && desiredSteerAngleV.GetXf() <= PI);
// Assert(desiredSteerAngleV.GetYf() >= -PI && desiredSteerAngleV.GetYf() <= PI);
// Assert(desiredSteerAngleV.GetZf() >= -PI && desiredSteerAngleV.GetZf() <= PI);
// Assert(desiredSteerAngleV.GetWf() >= -PI && desiredSteerAngleV.GetWf() <= PI);
// Check if the absolute value of the angle is within the range.
const Vec4V absDesiredSteerAngleV = Abs(desiredSteerAngleV);
const VecBoolV hitV = IsLessThan(absDesiredSteerAngleV, halfBlockedAngleRangeV);
// OR together all the results, between vectors. Note that we don't
// OR together between the components here, i.e. the X component of this will
// be for direction 0, 4, 8, etc, Y would be 1, 5, 9, and so on.
hitAny4V = Or(hitV, hitAny4V);
// Store the results.
hitThisObstructionVecs[k] = hitV;
}
// Check if there were any hits at all.
if(!IsFalseAll(hitAny4V))
{
// Compute the score and other things that are constant between all the avoidance directions.
Vec3V otherVelocityV(V_ZERO);
bool obstructedByLaw = false;
AvoidanceInfo::eObstructionType obstructionType = circle.m_ObstructionType;
if (obstructionType == AvoidanceInfo::Obstruction_Ped)
{
otherVelocityV = VECTOR3_TO_VEC3V(reinterpret_cast<const CPed*>(circle.m_Entity)->GetVelocity());
obstructedByLaw = IsObstructedByLawEnforcementPed((CPed*)circle.m_Entity);
}
else if (obstructionType == AvoidanceInfo::Obstruction_Object)
{
otherVelocityV = VECTOR3_TO_VEC3V(reinterpret_cast<const CObject*>(circle.m_Entity)->GetVelocity());
}
else if (obstructionType == AvoidanceInfo::Obstruction_Vehicle) // Can this actually happen here?
{
obstructedByLaw = IsObstructedByLawEnforcementVehicle((CVehicle*)circle.m_Entity);
}
const float fObstructionScore = ScoreOneObstructionAngleIndependent(circle.m_ObstructionType, circle.m_Distance,
VEC3V_TO_VECTOR3(ourVelocityV), VEC3V_TO_VECTOR3(otherVelocityV), obstructionData.fCurrentSpeed);
const Vec3V entityPosV = circle.m_Entity->GetTransform().GetPosition();
const Vec3V dirToObstructionV = Subtract(entityPosV, bumperCrsV);
const float dirToObstruction = fwAngle::LimitRadianAngle(fwAngle::GetATanOfXY(dirToObstructionV.GetXf(), dirToObstructionV.GetYf()));
const float distToObstruction = circle.m_Distance;
// Loop over the result array, reinterpreting it as a u32 array of boolean values.
const u32* bHitThisObstruction = reinterpret_cast<const u32*>(hitThisObstructionVecs);
for(int j = 0; j < numDirections; j++)
{
// Check if this direction hit the obstruction.
if(!bHitThisObstruction[j])
{
continue;
}
// Update the avoidance results, if this score is better than what we had before.
AvoidanceInfo& avoidanceInfo = avoidanceInfoArray[j];
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = distToObstruction;
avoidanceInfo.ObstructionType = obstructionType;
avoidanceInfo.fDirToObstruction = dirToObstruction;
avoidanceInfo.vPosition = VEC3V_TO_VECTOR3(entityPosV);
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.vVelocity = VEC3V_TO_VECTOR3(otherVelocityV);
avoidanceInfo.bIsObstructedByLaw = obstructedByLaw;
avoidanceInfo.bIsTurningLeftAtJunction = false;
avoidanceInfo.bIsHesitating = false;
bHitSomethingArrayInOut[j] = true;
if(pHitEntity)
{
*pHitEntity = (const CPhysical*)circle.m_Entity;
}
}
}
}
}
}
#endif // USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
#if USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
void CTaskVehicleGoToPointWithAvoidanceAutomobile::TestAndScoreLineSegmentsCrossRoadBoundariesUsingFollowRoute(const AvoidanceTaskObstructionData& obstructionData,
AvoidanceInfo* avoidanceInfoArray, int numDirections, bool* bHitSomethingArrayInOut) const
{
const Vector2 min2d(obstructionData.MinX, obstructionData.MinY);
const Vector2 max2d(obstructionData.MaxX, obstructionData.MaxY);
const float fTestDistance = min2d.Dist(max2d);
// We need a temporary vector-aligned array for distances to road edges for all the directions,
// since we will do the tests in parallel.
Assert(numDirections <= kMaxObstructionProbeDirections);
static const int kMaxVecs = kMaxObstructionProbeDirections/4;
CompileTimeAssert(kMaxVecs*4 == kMaxObstructionProbeDirections);
Vec4V distToRoadEdgeVecArray[kMaxVecs];
#if !USE_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
# error "Only USE_FOLLOWROUTE_EDGES_FOR_AVOIDANCE is supported now."
#endif
// Perform the tests. This will just populate distToRoadEdgeVecArray[], either with -1 or
// the distance to the road edge.
if(!DoLineSegmentsCrossRoadBoundariesUsingFollowRoute(obstructionData, avoidanceInfoArray, numDirections, fTestDistance, distToRoadEdgeVecArray))
{
// In this case, DoLineSegmentsCrossRoadBoundariesUsingFollowRoute() bailed out before looking
// for any actual intersections, so we should not do the stuff below.
return;
}
const CVehicle* pVeh = obstructionData.pVeh;
const Vec3V ourVelocityV = VECTOR3_TO_VEC3V(pVeh->GetAiVelocity());
// We now need to access the array as floats, but also as integers for improved branching performance.
const s32* distToRoadEdgeAsInt = (const s32*)&distToRoadEdgeVecArray[0];
const float* distToRoadEdgeAsFloat = (const float*)&distToRoadEdgeVecArray[0];
// Loop over the directions and update the scores.
// Note: there would probably be some opportunities for vectorizing parts of this stuff too.
for(int j = 0; j < numDirections; j++)
{
AvoidanceInfo& avoidanceInfo = avoidanceInfoArray[j];
// Any non-negative valid float should also be a non-negative s32.
if(distToRoadEdgeAsInt[j] >= 0)
{
// We hit the road edge, update the score if it's better than previous scores.
const float fDistToRoadEdge = distToRoadEdgeAsFloat[j];
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_RoadEdge, fDistToRoadEdge, RCC_VECTOR3(ourVelocityV), ORIGIN, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = fDistToRoadEdge;
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_RoadEdge;
avoidanceInfo.vVelocity = ORIGIN;
avoidanceInfo.vPosition = ORIGIN; //TODO: return a position from DoesLineSegmentCrossRoadBoundaries
avoidanceInfo.fDirToObstruction = avoidanceInfo.fDirection;
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsObstructedByLaw = false;
avoidanceInfo.bIsTurningLeftAtJunction = false;
avoidanceInfo.bIsHesitating = false;
bHitSomethingArrayInOut[j] = true;
}
}
}
}
#endif // USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
float CTaskVehicleGoToPointWithAvoidanceAutomobile::ScoreOneObstructionAngleIndependent(const AvoidanceInfo::eObstructionType obstructionType, const float fDistToObstruction, const Vector3& vOurVelocity, const Vector3& vTheirVelocity, const float fOurCurrentSpeed) const
{
float fScore = 0.0f;
//if this probe hit anything, weight it based on the type and distance
//if (AvoidanceInfoElement.HasObstruction())
{
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fDistanceScoreMultiplier, 100.0, -1000.0f, 1000.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fVehicleScoreMultiplier, 30.0, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fPedScoreMultiplier, 30.0, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fObjectScoreMultiplier, 30.0, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fRoadEdgeScoreMultiplier, 30.0, -100.0f, 100.0f, 0.1f);
//static dev_float s_fDistanceScoreMultiplier = 100.0f;
//static dev_float s_fVehicleScoreMultiplier = 15.0f;
//static dev_float s_fPedScoreMultiplier = 8.0f;
//static dev_float s_fObjectScoreMultiplier = 15.0f;
//static dev_float s_fRoadEdgeScoreMultiplier = 15.0f;
//static dev_float s_fRelativeVelocityScoreMultiplier = -1.0f;
//care more about obstructions that are closer
if (fDistToObstruction >= SMALL_FLOAT)
{
fScore += s_fDistanceScoreMultiplier / (fDistToObstruction * fDistToObstruction);
}
//I guess these aren't really multipliers since they aren't being added in
switch (obstructionType)
{
case AvoidanceInfo::Obstruction_Vehicle:
fScore += s_fVehicleScoreMultiplier;
break;
case AvoidanceInfo::Obstruction_Ped:
fScore += s_fPedScoreMultiplier;
break;
case AvoidanceInfo::Obstruction_Object:
fScore += s_fObjectScoreMultiplier;
break;
case AvoidanceInfo::Obstruction_RoadEdge:
//if we're willing to stop, REALLY prefer not going off road
fScore += s_fRoadEdgeScoreMultiplier;
break;
default:
Assertf(0, "Somehow we detected an obstruction without a type :(");
break;
}
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fRelativeVelocityScoreMultiplier, -1.0, -100.0f, 100.0f, 0.1f);
if (obstructionType == AvoidanceInfo::Obstruction_Vehicle)
{
//subtract some from the weight if this guy is moving in the same direction as us
//map his velocity onto ours
Vector3 vOurVelocityNormalized = vOurVelocity;
vOurVelocityNormalized.z = 0.0f;
vOurVelocityNormalized.NormalizeSafe(ORIGIN);
Vector3 vTheirVelocityFlat = vTheirVelocity;
vTheirVelocityFlat.z = 0.0f;
const float fSpeedInOurDirection = vTheirVelocityFlat.Dot(vOurVelocityNormalized);
//only do this if our velocities are generally in the same direction--
//we only want to make cars moving in the same direction less of an obstruction,
//not make the ones moving toward us bigger
if (fSpeedInOurDirection - fOurCurrentSpeed > 0.0f)
{
fScore += (fSpeedInOurDirection - fOurCurrentSpeed) * s_fRelativeVelocityScoreMultiplier;
//did we make this direction more desirable than it would be if there were no obstruction there?
//Assert(scores[i] >= fUnobstructedScore);
//fScore = rage::Max(fScore, fUnobstructedScore);
}
}
// if (AvoidanceInfoArray[i].bGiveWarning)
// {
// //add another penalty
// const float fGiveWarningPenalty = s_fVehicleScoreMultiplier;
// scores[i] += fGiveWarningPenalty;
// }
// if (bGivesWarning)
// {
// //angles closer to the obstruction should be weighted higher
// scores[i] += fabsf(SubtractAngleShorter(AvoidanceInfoArray[i].fDirection, fDirToCloseVehicleObstruction)) * s_fRecoveryAngleWeight;
// }
}
return fScore;
}
float CTaskVehicleGoToPointWithAvoidanceAutomobile::ScoreAvoidanceDirections( const AvoidanceTaskObstructionData& obstructionData, AvoidanceInfo* AvoidanceInfoArray,
const float distToOriginalObstruction)
{
const CVehicle* pVeh = obstructionData.pVeh;
const float fOurSpeed = pVeh->GetAiXYSpeed();
//are all directions blocked by the same vehicle?
const int iNumDirsNeedToBeBlocked = (s_iNumObstructionProbes*1) / 2;
int iNumDirsActuallyBlocked = 0;
int iNumDirsActuallyBlockedByObject = 0;
bool bEnoughDirectionsBlockedBySameVeh = true;
bool bEnoughDirectionsBlockedBySameObjectOrPed = true;
bool bFoundWayOut = false;
bool bFoundWayOutForScoring = false;
//float fAllBlockedDist = FLT_MAX;
Vector3 vAllBlockedPos(Vector3::ZeroType);
float fDirToCloseVehicleObstruction = 0.0f;
float fDirToCloseObstacleOrPedObstruction = 0.0f;
static const ScalarV svEpsilon(V_FLT_EPSILON);
//bool bGivesWarning = false;
if( s_iNumObstructionProbes > 0)
{
if (AvoidanceInfoArray[0].ObstructionType == AvoidanceInfo::Obstruction_Vehicle)
{
vAllBlockedPos = AvoidanceInfoArray[0].vPosition;
//fAllBlockedDist = AvoidanceInfoArray[i].fDistToObstruction;
fDirToCloseVehicleObstruction = AvoidanceInfoArray[0].fDirToObstruction;
++iNumDirsActuallyBlocked;
}
else
{
bEnoughDirectionsBlockedBySameVeh = false;
}
if( bEnoughDirectionsBlockedBySameVeh)
{
for (int i=1; i < s_iNumObstructionProbes; i++)
{
if (IsCloseAll(VECTOR3_TO_VEC3V(vAllBlockedPos), VECTOR3_TO_VEC3V(AvoidanceInfoArray[i].vPosition), svEpsilon))
{
++iNumDirsActuallyBlocked;
}
}
}
if (AvoidanceInfoArray[0].ObstructionType == AvoidanceInfo::Obstruction_Object || AvoidanceInfoArray[0].ObstructionType == AvoidanceInfo::Obstruction_Ped)
{
//reuse vAllBlockedPos here
vAllBlockedPos = AvoidanceInfoArray[0].vPosition;
fDirToCloseObstacleOrPedObstruction = AvoidanceInfoArray[0].fDirToObstruction;
++iNumDirsActuallyBlockedByObject;
}
else
{
bEnoughDirectionsBlockedBySameObjectOrPed = false;
}
if( bEnoughDirectionsBlockedBySameObjectOrPed)
{
for (int i=1; i < s_iNumObstructionProbes; i++)
{
//if (AreNearlyEqual(fAllBlockedDist, AvoidanceInfoArray[i].fDistToObstruction))
if (IsCloseAll(VECTOR3_TO_VEC3V(vAllBlockedPos), VECTOR3_TO_VEC3V(AvoidanceInfoArray[i].vPosition), svEpsilon))
{
++iNumDirsActuallyBlockedByObject;
}
}
}
}
if (iNumDirsActuallyBlocked < iNumDirsNeedToBeBlocked)
{
bEnoughDirectionsBlockedBySameVeh = false;
}
if (iNumDirsActuallyBlockedByObject < iNumDirsNeedToBeBlocked)
{
bEnoughDirectionsBlockedBySameObjectOrPed = false;
}
if (iNumDirsActuallyBlocked >= s_iNumObstructionProbes
|| iNumDirsActuallyBlockedByObject >= s_iNumObstructionProbes)
{
m_ObstructionInformation.m_uFlags.SetFlag(ObstructionInformation::IsCompletelyObstructedBySingleObstruction);
}
//unfortunately, the closest thing we have to a key in this data structure is its vPosition
const Vec3V vBumperCoords = obstructionData.vBumperCenter;
float fFurthestDistToClosestRealObstruction = distToOriginalObstruction;
const float fDistSqrToCenterOfClosestRealObstruction = DistSquared(vBumperCoords, obstructionData.vPosOfOriginalObstruction).Getf();
for (int i=0; i < s_iNumObstructionProbes; i++)
{
if (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Vehicle
|| AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Object
|| AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Ped)
{
const float fDistSqrToThisRealObstruction = DistSquared(vBumperCoords, VECTOR3_TO_VEC3V(AvoidanceInfoArray[i].vPosition)).Getf();
if (AreNearlyEqual(fDistSqrToCenterOfClosestRealObstruction, fDistSqrToThisRealObstruction))
{
if (AvoidanceInfoArray[i].fDistToObstruction > fFurthestDistToClosestRealObstruction)
{
fFurthestDistToClosestRealObstruction = AvoidanceInfoArray[i].fDistToObstruction;
}
}
}
}
static dev_float fCloseVehiclePadding = 3.0f;
fFurthestDistToClosestRealObstruction += fCloseVehiclePadding;
//look for a way out
for (int i=0; i < s_iNumObstructionProbes; i++)
{
const bool bWayOutConditionsForScoring = !AvoidanceInfoArray[i].HasObstruction()
|| (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_RoadEdge
&& AvoidanceInfoArray[i].fDistToObstruction > fFurthestDistToClosestRealObstruction);
if (bWayOutConditionsForScoring
|| (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Vehicle
&& (AvoidanceInfoArray[i].vVelocity.Mag2() > 1.0f || AvoidanceInfoArray[i].bIsHesitating))
)
{
bFoundWayOut = true;
if (bWayOutConditionsForScoring)
{
bFoundWayOutForScoring = true;
break;
}
}
}
//our velocity relative to the other
//Vector3 ourVelocityNormalized = pVeh->GetAiVelocity();
//ourVelocityNormalized.NormalizeSafe(ORIGIN);
const float fSteeringAngle = pVeh->GetSteerAngle();
//look ahead a bit further on the road from where our obstruction is
//if we're turning to the right or left a significant amount, prefer that direction
float fRightness = 0.0f;
if (obstructionData.bIsCurrentlyOnJunction
&& AvoidanceInfoArray[0].HasObstruction()
/*&& AvoidanceInfoArray[0].ObstructionType != AvoidanceInfo::Obstruction_RoadEdge*/)
{
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
if (pFollowRoute && pFollowRoute->GetCurrentTargetPoint() > 0)
{
const int iPointAheadOnRoute = rage::Min(pFollowRoute->FindClosestPointToPosition(obstructionData.vPosOfOriginalObstruction, pFollowRoute->GetCurrentTargetPoint()) + 2, (pFollowRoute->GetNumPoints()-1));
Assert(iPointAheadOnRoute >= 0);
Vector3 vDirectionOfCurrentSegment = VEC3V_TO_VECTOR3(pFollowRoute->GetRoutePoints()[pFollowRoute->GetCurrentTargetPoint()].GetPosition() - pFollowRoute->GetRoutePoints()[pFollowRoute->GetCurrentTargetPoint()-1].GetPosition());
vDirectionOfCurrentSegment.z = 0.0f;
vDirectionOfCurrentSegment.NormalizeSafe(XAXIS);
Vector3 vDirectionToPointAhead = VEC3V_TO_VECTOR3(pFollowRoute->GetRoutePoints()[iPointAheadOnRoute].GetPosition() - pFollowRoute->GetRoutePoints()[pFollowRoute->GetCurrentTargetPoint()-1].GetPosition());
vDirectionToPointAhead.z = 0.0f;
//don't need or want to normalize vDirectionToPointAhead--we want to know how far it is along the perp of vDirectionOfCurrentSegment
Vector3 vCurrentSegmentRight(vDirectionOfCurrentSegment.y, -vDirectionOfCurrentSegment.x, 0.0f);
//grcDebugDraw::Line(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()) + (vCurrentSegmentRight * 5.0f), Color_orange );
fRightness = vCurrentSegmentRight.Dot(vDirectionToPointAhead);
}
}
//get the segment length
const Vector2 min2d(obstructionData.MinX, obstructionData.MinY);
const Vector2 max2d(obstructionData.MaxX, obstructionData.MaxY);
const float fTestDistance = min2d.Dist(max2d);
const bool bIsFacingAVehicleTurningLeft = AvoidanceInfoArray[0].bIsTurningLeftAtJunction
|| AvoidanceInfoArray[1].bIsTurningLeftAtJunction
|| AvoidanceInfoArray[2].bIsTurningLeftAtJunction;
//now weight all the factors and chose the least-obstructed direction
//prefer smaller angles
//prefer longer distances
//prefer peds over vehicles over objects
//in roughly that order
atRangeArray<float, s_iNumObstructionProbes> scores;
float fBestScore = FLT_MAX;
int iBestIndex = -1;
for (int i = 0; i < s_iNumObstructionProbes; i++)
{
//low score wins
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fAngleScoreMultiplier, 0.5f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fLastDirectionScoreMultiplier, 0.75f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fDestinationAngleScoreMultiplier, 50.0f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fDestinationAngleScoreMultiplierStopped, 100.0f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fDestinationAngleScoreMultiplierForObstructions, 1.0f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fRouteAheadDirectionScoreMultiplier, 10.0f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fCloserThanCloseTargetScoreMultiplier, 15.0f, -100.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fSingleTrackRoadLeftSideMultiplier, 50.0f, -100.0f, 100.0f, 0.1f);
//initialize score to the angle difference
//TODO: do something with bWillStopForCars. I'm thinking if it's set, we should weight angle more heavily
//so we're more likely to choose a direction close to the center and slow down for it
//weight choice of direction toward velocity, not current heading
float fWheelOrientation = obstructionData.vehDriveOrientation + fSteeringAngle;
Assertf(fWheelOrientation <= THREE_PI,
"fWheelOrientation outside of acceptable range. fWheelOrientation: %.2f vehDriveOrientation %.2f fSteeringAngle %.2f"
, fWheelOrientation, obstructionData.vehDriveOrientation, fSteeringAngle);
//TODO: change this back to a LimitRadianAngle. there's no reason it shoudl be out of bounds
//but bernie encountered a crash here, and I'd really like the magdemo build to not crash
fWheelOrientation = fwAngle::LimitRadianAngleSafe(fWheelOrientation);
//only care about angle difference if we're moving. if we're stopped or moving really slowly,
//we shouldn't prefer closer angles since we don't have any momentum to conserve -JM
const float fAngleScoreMultActual = fOurSpeed >= 0.5f ? s_fAngleScoreMultiplier : 0.0f;
scores[i] = fabsf(SubtractAngleShorter(fWheelOrientation, AvoidanceInfoArray[i].fDirection)) * fAngleScoreMultActual;
//give a bump to the side we didn't avoid last time
if (i != 0 && ((m_bAvoidedRightLastFrame && i % 2 == 0)
|| (m_bAvoidedLeftLastFrame && i % 2 != 0)))
{
scores[i] += s_fLastDirectionScoreMultiplier;
}
//if we're on a single track road, prefer avoiding to the right, so add a penalty to all probes to the left
//if front is also blocked by road edge then add multiplier to front as well to allow target angle to be dominant when all probes blocked by road edge
if ((obstructionData.bOnSingleTrackRoad
|| (obstructionData.turnDir != BIT_TURN_LEFT && (AvoidanceInfoArray[i].bIsTurningLeftAtJunction || bIsFacingAVehicleTurningLeft)))
&& ( i != 0 || AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_RoadEdge )
&& i % 2 == 0)
{
//CVehicle::ms_debugDraw.AddSphere(pVeh->GetVehiclePosition(), 2.5f, Color_purple2, 0, 0, false);
scores[i] += s_fSingleTrackRoadLeftSideMultiplier;
}
else if ((i == 0 || (i % 2) != 0) && obstructionData.turnDir == BIT_TURN_LEFT
&& (AvoidanceInfoArray[i].bIsTurningLeftAtJunction || bIsFacingAVehicleTurningLeft))
{
//CVehicle::ms_debugDraw.AddSphere(pVeh->GetVehiclePosition(), 2.5f, Color_turquoise, 0, 0, false);
//if we are turning left, and so is the other guy, add a penalty to all probes to the right
scores[i] += s_fSingleTrackRoadLeftSideMultiplier;
}
//prefer angles closer to our target direction,
//unless they are blocked by a non-road edge obstruction
const float fRadiansWithinToNotCareAboutTargetDir = 0.0f;
const float fDestinationAngleMultActual = fOurSpeed >= 1.0f ? s_fDestinationAngleScoreMultiplier : s_fDestinationAngleScoreMultiplierStopped;
const float fAngleDifferenceBetweenProbeAndDesired = rage::Max(0.0f,
fabsf(SubtractAngleShorter(obstructionData.fOriginalDirection, AvoidanceInfoArray[i].fDirection)) - fRadiansWithinToNotCareAboutTargetDir);
if (!AvoidanceInfoArray[i].HasObstruction() ||
(AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_RoadEdge
&& AvoidanceInfoArray[i].fDistToObstruction > fFurthestDistToClosestRealObstruction))
{
scores[i] += ((fAngleDifferenceBetweenProbeAndDesired * fAngleDifferenceBetweenProbeAndDesired) * fDestinationAngleMultActual);
}
else
{
//otherwise add the maximum possible value the above block could generate
//but differentiate a little bit in order to break ties
scores[i] += ((PI * PI) * fDestinationAngleMultActual) +
((fAngleDifferenceBetweenProbeAndDesired * fAngleDifferenceBetweenProbeAndDesired) * s_fDestinationAngleScoreMultiplierForObstructions);
}
//if there's a clear way ahead, add a penalty to each probe that hits something closer than
//the closest thing we want to avoid
if (bFoundWayOutForScoring && AvoidanceInfoArray[i].HasObstruction()
&& obstructionData.turnDir != BIT_TURN_LEFT
&& obstructionData.turnDir != BIT_TURN_RIGHT
&& AvoidanceInfoArray[i].fDistToObstruction < fFurthestDistToClosestRealObstruction)
{
scores[i] += s_fCloserThanCloseTargetScoreMultiplier;
}
//if we're getting out of a deadlock, prefer sharper angles
//don't do this at high speeds though, can cause a bad-looking swerve
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fRecoveryAngleWeight, -15.0, -100.0f, 100.0f, 0.1f);
//static dev_float s_fRecoveryAngleWeight = -5.0f;
const float s_fRecoveryAngleScoreMultiplier = 90.0f * DtoR * s_fRecoveryAngleWeight;
if ((fOurSpeed <= 5.0f || m_bBumperInsideBoundsThisFrame) && (m_bAvoidingCarsUntilClear || bEnoughDirectionsBlockedBySameVeh ||
bEnoughDirectionsBlockedBySameObjectOrPed/* || m_threePointTurnInfo.m_iNumRecentThreePointTurns >= m_threePointTurnInfo.ms_iMaxAttempts*/))
{
if (bEnoughDirectionsBlockedBySameVeh)
{
//angles closer to the obstruction should be weighted higher
scores[i] += fabsf(SubtractAngleShorter(AvoidanceInfoArray[i].fDirection, fDirToCloseVehicleObstruction)) * s_fRecoveryAngleWeight;
}
else
{
scores[i] += fabsf(SubtractAngleShorter(obstructionData.vehDriveOrientation,AvoidanceInfoArray[i].fDirection)) * s_fRecoveryAngleScoreMultiplier;
}
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VECTOR3_TO_VEC3V(pVeh->TransformIntoWorldSpace(Vector3(0.0f, pVeh->GetBoundingBoxMax().y * 0.9f, 0.0f))), 1.5f, Color_orange, 0, 0, false);
}
#endif //BANK
}
//if (m_bAvoidedRightLastFrame && i % 2 == 0
// || m_bAvoidedLeftLastFrame && i % 2 != 0)
if (i != 0 && ((fRightness > 0.0f && i % 2 == 0) || (fRightness < 0.0f && i % 2 != 0)))
{
scores[i] += fabsf(fRightness) * s_fRouteAheadDirectionScoreMultiplier;
}
//cache this off
const float fUnobstructedScore = scores[i];
//add in the score based on distance and type of obstruction
//TODO: maybe move this to the top, it's just here because this is where
//the code used to be
scores[i] += AvoidanceInfoArray[i].fObstructionScore;
//if there's no obstruction, initialize the score to what it would be if we
//hit a road edge at the very edge of our testing boundaries,
//so we don't get a big discontinuity btwn adjacent probes where one hits a
//road edge and the other stops before crossing one
if (!AvoidanceInfoArray[i].HasObstruction())
{
scores[i] += ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_RoadEdge, fTestDistance, ORIGIN, ORIGIN, obstructionData.fCurrentSpeed);
}
//did we make this direction more desirable than it would be if there were no obstruction there?
//Assert(scores[i] >= fUnobstructedScore);
scores[i] = rage::Max(scores[i], fUnobstructedScore);
if (scores[i] < fBestScore)
{
fBestScore = scores[i];
iBestIndex = i;
}
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
// Use the coordinates of our front bumper.
Vector3 vBumperPos = pVeh->TransformIntoWorldSpace(Vector3(0.0f, obstructionData.vBoundBoxMax.GetYf() * 0.9f, 0.0f));
Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetTransform().GetA()));
Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
if (IsDrivingFlagSet(DF_DriveInReverse))
{
VehRightVector.Negate();
VehForwardVector.Negate();
}
//Vector3 VehPosition(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()));
const float fLineLength = AvoidanceInfoArray[i].HasObstruction() ? AvoidanceInfoArray[i].fDistToObstruction : 2.0f;
Vector3 Offset1 = fLineLength * rage::Cosf(obstructionData.vehDriveOrientation /*+ fOriginalDirection*/ - AvoidanceInfoArray[i].fDirection) * VehForwardVector;
Vector3 Offset2 = fLineLength * rage::Sinf(obstructionData.vehDriveOrientation /*+ fOriginalDirection*/ - AvoidanceInfoArray[i].fDirection) * VehRightVector;
Vector3 vResultPos = vBumperPos + Offset1 + Offset2;
if (!AvoidanceInfoArray[i].HasObstruction())
{
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vBumperPos), VECTOR3_TO_VEC3V(vResultPos), Color_green);
fwVectorMap::DrawLine(vBumperPos, vResultPos, Color_green, Color_green);
fwVectorMap::DrawLine(vBumperPos, vResultPos, Color_green, Color_green);
}
else
{
float markerDist = AvoidanceInfoArray[i].fDistToObstruction;
Vector3 vMarkerOffset = Offset1 + Offset2;
vMarkerOffset.Scale(0.2f);
vMarkerOffset.Scale(markerDist);
Color32 debugColor = Color_red;
if (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_RoadEdge)
{
debugColor = Color_blue;
}
else if (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Ped)
{
debugColor = Color_orange;
}
else if (AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Object)
{
debugColor = Color_yellow;
}
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vBumperPos), VECTOR3_TO_VEC3V(vResultPos), debugColor);
fwVectorMap::DrawLine(vBumperPos, vResultPos, debugColor, debugColor);
fwVectorMap::DrawMarker(vBumperPos + vMarkerOffset, debugColor, 0.2f);
}
char sWeight[64];
sprintf(sWeight, "%.2f", scores[i]);
if (bFoundWayOutForScoring && AvoidanceInfoArray[i].HasObstruction()
&& obstructionData.turnDir != BIT_TURN_LEFT
&& obstructionData.turnDir != BIT_TURN_RIGHT
&& AvoidanceInfoArray[i].fDistToObstruction < fFurthestDistToClosestRealObstruction)
{
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vResultPos), VECTOR3_TO_VEC3V(vResultPos) + Vec3V(0.0f, 0.0f, 0.25f), Color_orange);
}
if (ms_bDisplayObstructionProbeScores)
{
grcDebugDraw::Text(vResultPos, Color_white, 0, 0, sWeight, true, -1);
}
//******************************
}
#endif // __BANK
}
Assert(iBestIndex >= 0);
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 vBumperPos = pVeh->TransformIntoWorldSpace(Vector3(0.0f, obstructionData.vBoundBoxMax.GetYf() * 0.9f, 0.0f));
Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetTransform().GetA()));
Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
//Vector3 VehPosition(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()));
Vector3 Offset1 = 4.0f * rage::Cosf(obstructionData.vehDriveOrientation /*+ fOriginalDirection*/ - AvoidanceInfoArray[iBestIndex].fDirection) * VehForwardVector;
Vector3 Offset2 = 4.0f * rage::Sinf(obstructionData.vehDriveOrientation /*+ fOriginalDirection*/ - AvoidanceInfoArray[iBestIndex].fDirection) * VehRightVector;
Vector3 vResultPos = vBumperPos + Offset1 + Offset2; vResultPos.z += 0.05f;
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vBumperPos), VECTOR3_TO_VEC3V(vResultPos), Color_black);
}
#endif //__BANK
const float fDistToOriginalObstruction = Min(AvoidanceInfoArray[0].fDistToObstruction, AvoidanceInfoArray[iBestIndex].fDistToObstruction);
float fLerpAmount = 1.0f;
static dev_float s_fMinSpeedToDoLerping = 10.0f;
if (fOurSpeed > s_fMinSpeedToDoLerping)
{
const float fEstTimeToCollision = fDistToOriginalObstruction / fOurSpeed;
//static dev_float s_fMinTimeToDoFullAvoidance = 1.0f;
//static dev_float s_fMinLerp = 0.25f;
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fMinTimeToDoFullAvoidance, 1.0f, 0.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, s_fMinLerp, 0.05f, 0.0f, 1.0f, 0.01f);
fLerpAmount = Clamp((1.0f - fEstTimeToCollision) + s_fMinTimeToDoFullAvoidance, s_fMinLerp, 1.0f);
// if (fEstTimeToCollision < s_fMinTimeToDoFullAvoidance)
// {
// grcDebugDraw::Sphere(pVeh->GetVehiclePosition(), 2.0f, Color_SkyBlue, false);
// }
}
m_bAvoidedLeftLastFrame = false;
m_bAvoidedRightLastFrame = false;
if (iBestIndex % 2 != 0 && iBestIndex != 0)
{
m_bAvoidedRightLastFrame = true;
}
else if (iBestIndex != 0)
{
m_bAvoidedLeftLastFrame = true;
}
//Set the obstruction information.
{
//IsCompletelyObstructed may be set to true even if some of the obstructions
//are far away road edges. we use the "close" variant to specify when the vehicle
//is really blocked in all directions
const float fStopCloseDistance = 10.0f + obstructionData.vBoundBoxMax.GetYf();
if (!bFoundWayOut && fDistSqrToCenterOfClosestRealObstruction < (fStopCloseDistance * fStopCloseDistance))
{
m_ObstructionInformation.m_uFlags.SetFlag(ObstructionInformation::IsCompletelyObstructedClose);
}
//Iterate over the angles closely around the best direction -- in a lot of situations,
//just checking the best direction is not good enough. It results in a lot of collisions.
static int s_iNumAnglesAroundBest = 1;
for(int i = iBestIndex - s_iNumAnglesAroundBest; i <= iBestIndex + s_iNumAnglesAroundBest; ++i)
{
//Ensure the index is valid.
if((i < 0) || (i >= s_iNumObstructionProbes))
{
continue;
}
//Check if the obstruction is law enforcement.
bool bIsObstructedByLawEnforcementPed = (AvoidanceInfoArray[i].bIsObstructedByLaw &&
(AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Ped));
if(bIsObstructedByLawEnforcementPed)
{
m_ObstructionInformation.m_uFlags.SetFlag(ObstructionInformation::IsObstructedByLawEnforcementPed);
}
bool bIsObstructedByLawEnforcementVehicle = (AvoidanceInfoArray[i].bIsObstructedByLaw &&
(AvoidanceInfoArray[i].ObstructionType == AvoidanceInfo::Obstruction_Vehicle));
if(bIsObstructedByLawEnforcementVehicle)
{
m_ObstructionInformation.m_uFlags.SetFlag(ObstructionInformation::IsObstructedByLawEnforcementVehicle);
}
if(bIsObstructedByLawEnforcementPed || bIsObstructedByLawEnforcementVehicle)
{
m_ObstructionInformation.m_fDistanceToLawEnforcementObstruction = Min(
m_ObstructionInformation.m_fDistanceToLawEnforcementObstruction,
AvoidanceInfoArray[i].fDistToObstruction);
}
}
}
DR_ONLY(debugPlayback::RecordVehicleObstructionArray(pVeh->GetCurrentPhysicsInst(), AvoidanceInfoArray, s_iNumObstructionProbes, obstructionData.vehDriveOrientation, pVeh->GetTransform().GetA(), pVeh->GetVehicleForwardDirection()));
const float fRetVal = fwAngle::LerpTowards(obstructionData.vehDriveOrientation, AvoidanceInfoArray[iBestIndex].fDirection, fLerpAmount);
Assert(fRetVal > -THREE_PI && fRetVal < THREE_PI);
return fRetVal;
}
float CTaskVehicleGoToPointWithAvoidanceAutomobile::TestDirectionsForObstructionsNew(CVehicle* pVeh, const CEntity *pException
, float MinX, float MinY, float MaxX, float MaxY, const float fOriginalDirection, const float vehDriveOrientation
, CarAvoidanceLevel carAvoidanceLevel, bool bAvoidVehs, bool bAvoidPeds, bool bAvoidObjs, bool& bGiveWarning
, const bool bWillStopForCars, const spdAABB& boundBox, const float fDesiredSpeed)
{
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, RENDER_CAR_OBSTRUCTIONS, false );
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, USE_NEW_OBSTRUCTION_RESULTS, true);
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, USE_QUICK_BLOCKING_CHECK, true);
//if we don't have "swerve around peds" set,
//but we do have "swerve around objects" set,
//it's probably because we've got "stop for peds" set instead.
//we don't want to stop for dead peds though, so allow
//steering around them
const bool bOnlyAvoidTrulyStationaryPeds = !bAvoidPeds && bAvoidObjs;
if (m_pOptimization_pLastCarBlockingUs && USE_QUICK_BLOCKING_CHECK)
{
// Another car was blocking us last frame, assume it still is.
#if __BANK
if (RENDER_CAR_OBSTRUCTIONS && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
{
fwVectorMap::DrawLine(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), VEC3V_TO_VECTOR3(m_pOptimization_pLastCarBlockingUs->GetVehiclePosition()), Color_red, Color_red);
}
}
#endif
return fOriginalDirection;
}
AvoidanceTaskObstructionData obstructionData;
Vec3V bboxMin = boundBox.GetMin();
Vec3V bboxMax = boundBox.GetMax();
obstructionData.pVeh = pVeh;
obstructionData.pException = pException;
obstructionData.MinX = MinX;
obstructionData.MinY = MinY;
obstructionData.MaxX = MaxX;
obstructionData.MaxY = MaxY;
obstructionData.fOriginalDirection = fOriginalDirection;
obstructionData.fDesiredSpeed = pVeh->pHandling ? rage::Min(fDesiredSpeed, pVeh->pHandling->m_fEstimatedMaxFlatVel) : fDesiredSpeed;
obstructionData.fCurrentSpeed = pVeh->GetAiXYSpeed();
obstructionData.vehDriveOrientation = vehDriveOrientation;
obstructionData.carAvoidanceLevel = carAvoidanceLevel;
obstructionData.vBoundBoxMin = bboxMin;
obstructionData.vBoundBoxMax = bboxMax;
obstructionData.bIsCurrentlyOnJunction = pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO;
obstructionData.bWillStopForCars = bWillStopForCars;
obstructionData.vPosOfOriginalObstruction.ZeroComponents();
obstructionData.bOnSingleTrackRoad = pVeh->GetIntelligence()->IsOnSingleTrackRoad();
obstructionData.bRacing = pVeh->m_nVehicleFlags.bIsRacing;
if (!obstructionData.bIsCurrentlyOnJunction)
{
obstructionData.turnDir = BIT_NOT_SET;
}
else
{
//this fn will find the direction of the jn regardless of how far ahead it is,
//but we only want to set this data if the vehicle is already on the junction
obstructionData.turnDir = pVeh->GetIntelligence()->GetJunctionTurnDirection();
}
const Vec3V vNormalizedDriveDir = CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse));
obstructionData.vNormalizedDriveDir = vNormalizedDriveDir;
DEV_ONLY(static) ScalarV vfPercentOfLengthToUse(0.33f);
const ScalarV vfCarMarginUs = !pVeh->InheritsFromBoat() ? ScalarV(V_ZERO) : ScalarV(V_QUARTER);
Mat34V vehMat = pVeh->GetMatrix();
ScalarV vfCarLength = (bboxMax - bboxMin).GetY();
const ScalarV vfDistFromBumper = vfCarLength * vfPercentOfLengthToUse;
//Vec3V aiCenter = vehMat.GetCol3();
ScalarV vfZero(V_ZERO);
Vec3V aiFrontLeftCornerLocalSpace = Vec3V(bboxMin.GetX() - vfCarMarginUs, bboxMax.GetY() + vfCarMarginUs, vfZero);
Vec3V aiFrontLeftCornerWorldSpace = Transform(vehMat, aiFrontLeftCornerLocalSpace);
Vec3V aiRearLeftCornerLocalSpace = Vec3V(bboxMin.GetX() - vfCarMarginUs, bboxMin.GetY() - vfCarMarginUs, vfZero);
Vec3V aiRearLeftCornerWorldSpace = Transform(vehMat, aiRearLeftCornerLocalSpace);
Vec3V aiBumperVectorLocalSpace = Vec3V(bboxMax.GetX() - bboxMin.GetX() + vfCarMarginUs + vfCarMarginUs, vfZero, vfZero);
Vec3V aiBumperVectorWorldSpace = Transform3x3(vehMat, aiBumperVectorLocalSpace);
Vec3V aiSideVectorLocalSpace = Vec3V(vfZero, Negate(vfDistFromBumper + vfCarMarginUs + vfCarMarginUs), vfZero);
Vec3V aiSideVectorWorldSpace = Transform3x3(vehMat, aiSideVectorLocalSpace);
Vec3V aiBumperCenterLocalSpace = Vec3V(vfZero, bboxMax.GetY() * ScalarV(0.9f), vfZero);
Vec3V aiBumperCenterWorldSpace = Transform(vehMat, aiBumperCenterLocalSpace);
obstructionData.vFrontLeftCorner = aiFrontLeftCornerWorldSpace;
obstructionData.vRearLeftCorner = aiRearLeftCornerWorldSpace;
obstructionData.vBumperVector = aiBumperVectorWorldSpace;
obstructionData.vSideVector = aiSideVectorWorldSpace;
obstructionData.vBumperCenter = aiBumperCenterWorldSpace;
const float speed = pVeh->GetAiXYSpeed();
obstructionData.fAvoidanceLookaheadTimeShort = GetAvoidanceLookaheadTime(speed, false);
obstructionData.fAvoidanceLookaheadTimeLong = GetAvoidanceLookaheadTime(speed, true);
if (bAvoidVehs)
{
FindVehicleObstructions(obstructionData, bGiveWarning);
}
if (bAvoidPeds || bOnlyAvoidTrulyStationaryPeds)
{
FindPedObstructions(obstructionData, bOnlyAvoidTrulyStationaryPeds);
}
if (bAvoidObjs)
{
FindObjectObstructions(obstructionData);
}
//Test the original direction
AvoidanceInfo originalDirTest;
originalDirTest.fDirection = fOriginalDirection;
const CPhysical* pHitEntity = NULL;
TestOneDirectionAgainstObstructions(obstructionData, originalDirTest, false, bGiveWarning, &pHitEntity);
if (!originalDirTest.HasObstruction())
{
#if __BANK
if (RENDER_CAR_OBSTRUCTIONS && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
obstructionData.DebugDraw(pVeh);
}
#endif
m_bAvoidedLeftLastFrame = false;
m_bAvoidedRightLastFrame = false;
return fOriginalDirection;
}
else if (pHitEntity)
{
if (originalDirTest.bIsStationaryCar)
{
pVeh->m_nVehicleFlags.bIsOvertakingStationaryCar = true;
}
pVeh->GetIntelligence()->m_pSteeringAroundEntity = pHitEntity;
}
obstructionData.vPosOfOriginalObstruction = VECTOR3_TO_VEC3V(originalDirTest.vPosition);
if (!IsDrivingFlagSet(DF_GoOffRoadWhenAvoiding) && !pVeh->m_nVehicleFlags.bDisableRoadExtentsForAvoidance)
{
#if USE_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
FindRoadEdgesUsingFollowRoute(obstructionData);
#else
FindRoadEdgesUsingNodeList(obstructionData);
#endif
}
TUNE_GROUP_FLOAT( FOLLOW_PATH_AI_STEER, sfProbeAngleScaler, 1.75f, 0.0f, 10.0f, 0.1f);
float fProbeScaler = 1.0f;
if( originalDirTest.HasObstruction() && originalDirTest.ObstructionType == AvoidanceInfo::Obstruction_Object)
{
//we've been avoiding an object for a while now, increase probe angle to try and drive around
if( m_threePointTurnInfo.m_iNumRecentThreePointTurns >= m_threePointTurnInfo.ms_iMaxAttempts)
{
fProbeScaler = sfProbeAngleScaler + ( m_threePointTurnInfo.m_iNumRecentThreePointTurns - m_threePointTurnInfo.ms_iMaxAttempts) * 0.2f;
}
}
const float s_fMaxAvoidAngle = pVeh->pHandling->m_fSteeringLock * fProbeScaler;
const float s_fRadiansBetweenProbes = (2.0f * s_fMaxAvoidAngle) / (float)s_iNumObstructionProbes;
#if 0
if (USE_WEDGE_CULLING)
{
float minAngle = vehDriveOrientation + Min((s_iNumObstructionProbes / 2) * -1.0f * s_fRadiansBetweenProbes, s_fMaxAvoidAngle);
minAngle = fwAngle::LimitRadianAngleSafe(minAngle);
float maxAngle = vehDriveOrientation + Min((s_iNumObstructionProbes / 2) * 1.0f * s_fRadiansBetweenProbes, s_fMaxAvoidAngle);
maxAngle = fwAngle::LimitRadianAngleSafe(maxAngle);
obstructionData.WedgeCull(minAngle, maxAngle);
}
#endif
#if __BANK
if (RENDER_CAR_OBSTRUCTIONS && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
obstructionData.DebugDraw(pVeh);
}
#endif
atRangeArray<AvoidanceInfo, s_iNumObstructionProbes> AvoidanceInfoArray;
AvoidanceInfoArray[0].fDirection = vehDriveOrientation;
// Initialize the remaining directions
int iOffset = 1;
for (int i = 1; i < s_iNumObstructionProbes; i+=2)
{
AvoidanceInfoArray[i].fDirection = vehDriveOrientation + Min(iOffset * -1.0f * s_fRadiansBetweenProbes, s_fMaxAvoidAngle);
AvoidanceInfoArray[i].fDirection = fwAngle::LimitRadianAngleSafe(AvoidanceInfoArray[i].fDirection);
//don't overrun the array!
if (i+1 < s_iNumObstructionProbes)
{
AvoidanceInfoArray[i+1].fDirection = vehDriveOrientation + Min(iOffset * s_fRadiansBetweenProbes, s_fMaxAvoidAngle);
AvoidanceInfoArray[i+1].fDirection = fwAngle::LimitRadianAngleSafe(AvoidanceInfoArray[i+1].fDirection);
}
iOffset++;
}
//Note that at this point, we think we are completely obstructed.
//This flag will be cleared if it turns out that this is no longer the case.
m_ObstructionInformation.m_uFlags.SetFlag(ObstructionInformation::IsCompletelyObstructed);
#if !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
for (int i = 0; i < s_iNumObstructionProbes; i++) // start at 0 (redo the straight-ahead test because we want to test road edges too now)
{
if(!TestOneDirectionAgainstObstructions(obstructionData, AvoidanceInfoArray[i], true, bGiveWarning))
{
//Note that we are not completely obstructed.
m_ObstructionInformation.m_uFlags.ClearFlag(ObstructionInformation::IsCompletelyObstructed);
}
}
#else // !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
bool bHitSomethingArray[s_iNumObstructionProbes];
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
for (int i = 0; i < s_iNumObstructionProbes; i++) // start at 0 (redo the straight-ahead test because we want to test road edges too now)
{
bHitSomethingArray[i] = false;
}
// Test against all the vehicles, and update bHitsomethingArray and AvoidainceInfoArray.
TestAndScoreLineSegmentsAgainstVehicles(obstructionData, AvoidanceInfoArray.GetElements(), s_iNumObstructionProbes, bHitSomethingArray, bGiveWarning);
// Test against all the peds and objects, and update bHitsomethingArray and AvoidainceInfoArray.
TestAndScoreLineSegmentsAgainstCylindricalObstructions(obstructionData, AvoidanceInfoArray.GetElements(), s_iNumObstructionProbes, bHitSomethingArray);
#else
for (int i = 0; i < s_iNumObstructionProbes; i++) // start at 0 (redo the straight-ahead test because we want to test road edges too now)
{
bHitSomethingArray[i] = TestOneDirectionAgainstObstructions(obstructionData, AvoidanceInfoArray[i], false, bGiveWarning);
}
#endif
if(!IsDrivingFlagSet(DF_GoOffRoadWhenAvoiding))
{
TestAndScoreLineSegmentsCrossRoadBoundariesUsingFollowRoute(obstructionData, AvoidanceInfoArray.GetElements(), s_iNumObstructionProbes, bHitSomethingArray);
}
bool bHitEverything = true;
for(int j = 0; j < s_iNumObstructionProbes; j++)
{
if(!bHitSomethingArray[j])
{
bHitEverything = false;
break;
}
}
if(!bHitEverything)
{
//Note that we are not completely obstructed.
m_ObstructionInformation.m_uFlags.ClearFlag(ObstructionInformation::IsCompletelyObstructed);
}
#endif // !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
const float fReturnDirection = ScoreAvoidanceDirections(obstructionData, &AvoidanceInfoArray[0], originalDirTest.fDistToObstruction);
//potentially honk at the thing that was obstructing us originally:
if (originalDirTest.HasObstruction() && originalDirTest.ObstructionType == AvoidanceInfo::Obstruction_Vehicle)
{
const CPed* pOtherCarDriver = pHitEntity && pHitEntity->GetIsTypeVehicle() ? (static_cast<const CVehicle*>(pHitEntity))->GetDriver() : NULL;
if (pOtherCarDriver
&& pOtherCarDriver->IsPlayer()
&& originalDirTest.bIsStationaryCar
&& !m_ObstructionInformation.m_uFlags.IsFlagSet(ObstructionInformation::IsCompletelyObstructedClose)
&& (pVeh->GetDriver() && pVeh->GetDriver()->CheckBraveryFlags(BF_PLAY_CAR_HORN))
&& obstructionData.fCurrentSpeed < 0.1f)
{
//
//grcDebugDraw::Sphere(pVeh->GetVehiclePosition(), 1.5f, Color_green, false, 10);
////////
CVehicle* pVehNonConst = const_cast<CVehicle*>(pVeh);
if (pVehNonConst->m_iNextValidHornTime < fwTimer::GetTimeInMilliseconds())
{
HelperMaybePlayHorn(pVehNonConst, true, true, pOtherCarDriver, false, true);
TUNE_GROUP_INT(CAR_AI, RndMinHonkDelay, 500, 0, 20000, 100);
TUNE_GROUP_INT(CAR_AI, RndMaxHonkDelay, 7500, 0, 20000, 100);
pVehNonConst->m_iNextValidHornTime = fwTimer::GetTimeInMilliseconds() + fwRandom::GetRandomNumberInRange(RndMinHonkDelay, RndMaxHonkDelay);
}
// HelperMaybePlayHorn(pVehNonConst, true, true, pOtherCarDriver, false, true);
}
}
return fReturnDirection;
}
CVehicle* CTaskVehicleGoToPointWithAvoidanceAutomobile::GetTrailerParentFromVehicle(const CVehicle* pVehicle)
{
if( pVehicle )
{
return pVehicle->GetAttachParentVehicleDummyOrPhysical();
}
return NULL;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindVehicleObstructions(AvoidanceTaskObstructionData& obstructionData, const bool bGiveWarning)
{
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_STEER, sfIgnoreCarForWeavingHeight, 5.0f, 0.0f, 100.0f, 0.1f);
const CVehicle* pVeh = obstructionData.pVeh;
// If the exception is a ped who's in a vehicle, ignore the vehicle
const CEntity* pVehicleException = obstructionData.pException;
if( pVehicleException && pVehicleException->GetIsTypePed() )
{
const CPed* pPed = static_cast<const CPed*>(pVehicleException);
if( pPed && pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPed->GetMyVehicle() )
{
pVehicleException = pPed->GetMyVehicle();
}
}
const Vector3 ourPosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
if(pEntity == pVehicleException)
{
continue;
}
//skip vehicles about to be removed
if (pEntity->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
continue;
}
Assert(pEntity->GetIsTypeVehicle());
CVehicle *pVehObstacle =(CVehicle *)pEntity;
//if we aren't going to stop for the cars later, still swerve around them here
if ((pVehObstacle->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle
|| pVehObstacle->m_nVehicleFlags.bIsAmbulanceOnDuty
|| pVehObstacle->m_nVehicleFlags.bIsFireTruckOnDuty)
&& obstructionData.carAvoidanceLevel < CAR_AVOIDANCE_FULL)
{
continue;
}
Vector3 otherVehCentre = VEC3V_TO_VECTOR3(pVehObstacle->GetVehiclePosition());
if (otherVehCentre.x < obstructionData.MinX
|| otherVehCentre.x > obstructionData.MaxX
|| otherVehCentre.y < obstructionData.MinY
|| otherVehCentre.y > obstructionData.MaxY
|| ABS(otherVehCentre.z - ourPosition.z) > sfIgnoreCarForWeavingHeight)
{
continue;
}
if (ShouldIgnoreNonDirtyVehicle(*pVeh, *pVehObstacle, IsDrivingFlagSet(DF_DriveInReverse), obstructionData.bOnSingleTrackRoad))
{
continue;
}
// Ignore trailers we're towing
if(pVehObstacle->InheritsFromTrailer() && pVehObstacle->m_nVehicleFlags.bHasParentVehicle)
{
// check whether the trailer is attached to the parent vehicle
//CTrailer* pTrailer = static_cast<CTrailer*>(pVehObstacle);
const CVehicle* pAttachParent = GetTrailerParentFromVehicle(pVehObstacle);
if(pAttachParent == pVeh || (pAttachParent == pVehicleException && pAttachParent != NULL))
{
continue;
}
}
//ignore cars on the back of a trailer
if (pVehObstacle->m_nVehicleFlags.bHasParentVehicle)
{
CVehicle* pVehParent = GetTrailerParentFromVehicle(pVehObstacle);
if (pVehParent && (pVehParent->InheritsFromTrailer() || pVehParent == pVeh))
{
continue;
}
}
//if our parent task has told us to exclude a certain vehicle, also
//exclude its attached trailer, if any
if (pVehicleException && pVehicleException->GetIsTypeVehicle())
{
const CVehicle* pExceptionVehicle = static_cast<const CVehicle*>(pVehicleException);
const CTrailer* pExceptionTrailer = pExceptionVehicle->GetAttachedTrailer();
if (pEntity == pExceptionTrailer)
{
continue;
}
//if we're supposed to avoid a trailer, also exclude its attach parent
if (pExceptionVehicle->InheritsFromTrailer() && pVehObstacle == GetTrailerParentFromVehicle(pExceptionVehicle))
{
continue;
}
}
{
// Now actually do a collision threat analysis for this car
CPed* driver = pVehObstacle->GetDriver();
if (driver && driver->IsPlayer())
{
CPlayerInfo* playerInfo = CGameWorld::GetMainPlayerInfo();
playerInfo->NearVehicleMiss(pVehObstacle, pVeh, false);
}
// Check if the car should not avoid the other car because they are in a convoy together.
if(pVehObstacle != pVeh && !InConvoyTogether(pVeh, pVehObstacle) && !ShouldNotAvoidVehicle(*pVeh, *pVehObstacle)) //(pVeh->m_nVehicleFlags.bPartOfConvoy && pVehObstacle->m_nVehicleFlags.bPartOfConvoy))
{
bool bIsStationaryCar = IsThisAStationaryCar(pVehObstacle);
bool bIsParkedCar = IsThisAParkedCar(pVehObstacle);
//WeaveForOtherCar(pVeh, pVehObstacle, pLeftAngle, pRightAngle, bIsStationaryCar, bIsParkedCar, carAvoidanceLevel, bWeMustGoLeft, bWeMustGoRight, bIgnoreFurtherCars, bCopperBlockedCouldLeaveCar, bGiveWarning);
AddObstructionForSingleVehicle(obstructionData, pVehObstacle, bIsStationaryCar, bIsParkedCar, bGiveWarning);
}
}
}
}
bool HelperPedIsStationaryForAvoidance(const CPed* pPed)
{
Assert(pPed);
return pPed->IsDead() || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsInStationaryScenario);
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindPedObstructions(AvoidanceTaskObstructionData& obstructionData, bool bOnlyAvoidTrulyStationaryPeds)
{
const CVehicle* pVeh = obstructionData.pVeh;
CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetPedScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
if(pEntity == obstructionData.pException)
{
continue;
}
CPed* pPed = static_cast<CPed*>(pEntity);
if (pPed->GetIsInVehicle())
{
continue;
}
if (pPed->GetIsOnMount())
{
continue;
}
//skip peds about to be removed
if (pPed->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
continue;
}
if (pPed->GetCapsuleInfo()->IsBird())
{
continue;
}
//skip non-dead peds if we don't want to aovid them
//also support scenario peds here now.
const bool bIsStationaryPed = HelperPedIsStationaryForAvoidance(pPed);
if (bOnlyAvoidTrulyStationaryPeds
&& !bIsStationaryPed)
{
continue;
}
const Vector3 vecEntityPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
const float fVehicleZ = pVeh->GetVehiclePosition().GetZf();
if( vecEntityPos.x > obstructionData.MinX && vecEntityPos.x < obstructionData.MaxX &&
vecEntityPos.y > obstructionData.MinY && vecEntityPos.y < obstructionData.MaxY &&
ABS(vecEntityPos.z - fVehicleZ) < 4.0f)
{
// if this guy is actually standing on the car we will avoid avoiding it.
if(pPed->GetGroundPhysical() != pVeh && pPed->GetAttachParent() != pVeh)
{
// Now actually do a collision threat analysis for this ped
AddObstructionForSinglePed(obstructionData, pPed);
}
}
}
}
bool HelperShouldIgnoreDoorEntirely(const CDoor* pDoor)
{
//we couldn't give a fuck about barrier arms that are all the way up,
//even if their bounding boxes extend into our driveable space
Assert(pDoor);
static dev_float s_fDoorOpenRatio = 0.89f;
float doorOpenRatio = pDoor->GetDoorOpenRatio();
if((doorOpenRatio > s_fDoorOpenRatio && (pDoor->GetDoorType() == CDoor::DOOR_TYPE_BARRIER_ARM || pDoor->GetDoorType() == CDoor::DOOR_TYPE_BARRIER_ARM_SC)) ||
(doorOpenRatio < (1.0f - s_fDoorOpenRatio ) && (pDoor->GetDoorType() == CDoor::DOOR_TYPE_RAIL_CROSSING_BARRIER || pDoor->GetDoorType() == CDoor::DOOR_TYPE_RAIL_CROSSING_BARRIER_SC)))
{
return true;
}
return false;
}
bool HelperWillDoorAutoOpen(const CDoor* pDoor)
{
bool bIsAutoOpen = CDoor::DoorTypeAutoOpensForVehicles(pDoor->GetDoorType());
bIsAutoOpen &= !pDoor->GetDoorSystemData() || !pDoor->GetDoorSystemData()->GetLocked();
return bIsAutoOpen;
}
bool HelperShouldVehicleStopVsSwerveForDoor(Vec3V_In vVehFwdDir, ScalarV_In fVehicleWidth, const CDoor* pDoor)
{
Assert(pDoor);
static dev_float s_fDoorOpenRatio = 0.89f;
if (Abs(pDoor->GetDoorOpenRatio()) > s_fDoorOpenRatio)
{
return false;
}
//don't stop for doors that are narrower than the car, we may have accidentally encountered a ped door
spdAABB tempBox;
const spdAABB& boundBox = pDoor->GetLocalSpaceBoundBox(tempBox);
ScalarV sfDoorWidth = boundBox.GetMax().GetX() - boundBox.GetMin().GetX();
//currently only for sliding doors, will want to change this in future
if( pDoor->GetIsDoubleDoor() )
{
const CDoor* pOtherDoor = pDoor->GetOtherDoubleDoor();
if( pOtherDoor != NULL)
{
const spdAABB& otherBoundBox = pOtherDoor->GetLocalSpaceBoundBox(tempBox);
ScalarV otherWidth = otherBoundBox.GetMax().GetX() - otherBoundBox.GetMin().GetX();
sfDoorWidth += otherWidth;
}
}
else
{
//some double doors currently aren't marked as so, so add a small buffer
//single ped doors will still be too small, but vehicle double doors will be valid
static const ScalarV extraBuffer = ScalarV(0.25f);
sfDoorWidth += extraBuffer;
}
if (IsGreaterThanAll(fVehicleWidth, sfDoorWidth))
{
return false;
}
Vec3V_In vObjFwdDir = pDoor->GetTransform().GetForward();
static const ScalarV vDotThreshold(V_HALF);
const ScalarV vDot = Dot(vVehFwdDir, vObjFwdDir);
return (IsGreaterThanAll(Abs(vDot), vDotThreshold) != 0);
}
bool IsVehicleAtTollbooth(Vec3V_In vPos)
{
static const spdAABB booth1(Vec3V(783.0f, -3174.2f, -5.9f), Vec3V(815.9f, -3137.1f, 15.9f));
static const spdAABB booth2(Vec3V(1077.9f, -3341.8f, -5.9f), Vec3V(1109.1f, -3311.8f, 15.9f));
if (!booth1.ContainsPointFlat(vPos)
&& !booth2.ContainsPointFlat(vPos))
{
return false;
}
return true;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindObjectObstructions(AvoidanceTaskObstructionData& obstructionData)
{
const CVehicle* pVeh = obstructionData.pVeh;
//const float fVehicleZ = pVeh->GetVehiclePosition().GetZf();
static dev_float s_fPadding = 0.25f;
const float fOurMinHeight = obstructionData.vBoundBoxMin.GetZf() - s_fPadding;
const float fOurMaxHeight = obstructionData.vBoundBoxMax.GetZf() + s_fPadding;
const bool bIsVehicleAtTollBooth = IsVehicleAtTollbooth(obstructionData.vBumperCenter);
const CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetObjectScanner().GetIterator();
for( const CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
if (pEntity->GetBaseModelInfo()->GetNotAvoidedByPeds())
{
continue;
}
//skip objects about to be removed
if (pEntity->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
continue;
}
const Vec3V vOtherPosWorldSpace = pEntity->GetTransform().GetPosition();
const Vector3 vecEntityPosition = VEC3V_TO_VECTOR3(vOtherPosWorldSpace);
CObject* pObject = (CObject*)pEntity;
if( vecEntityPosition.x > obstructionData.MinX && vecEntityPosition.x < obstructionData.MaxX &&
vecEntityPosition.y > obstructionData.MinY && vecEntityPosition.y < obstructionData.MaxY )
{
const CEntity* pAttachParent = static_cast<const CEntity*>(pEntity->GetAttachParent());
if (pAttachParent && pAttachParent->GetIsTypeVehicle())
{
continue;
}
spdAABB aabb;
pEntity->GetAABB(aabb);
//const Vec3V vOtherPosCarSpace = pVeh->GetTransform().UnTransform(vOtherPosWorldSpace);
const float fOtherMinHeightCarSpace = pVeh->GetTransform().UnTransform(aabb.GetMin()).GetZf();
const float fOtherMaxHeightCarSpace = pVeh->GetTransform().UnTransform(aabb.GetMax()).GetZf();
const bool bOtherMinIsWithinZRange = fOtherMinHeightCarSpace > fOurMinHeight && fOtherMinHeightCarSpace < fOurMaxHeight;
const bool bOtherMaxIsWithinZRange = fOtherMaxHeightCarSpace > fOurMinHeight && fOtherMaxHeightCarSpace < fOurMaxHeight;
const bool bOtherCompletelyWithinZRange = fOtherMinHeightCarSpace < fOurMinHeight && fOtherMaxHeightCarSpace > fOurMaxHeight;
const float fOtherHeight = fOtherMaxHeightCarSpace - fOtherMinHeightCarSpace;
const bool bObjectIsTallEnoughToAvoid = fOtherHeight > 0.9f;
bool bObjectIsAutoOpenDoor = false;
static const u32 barrierHash = ATSTRINGHASH("prop_sec_barier_04a", 2703851248);
if (pObject->IsADoor())
{
const CDoor* pDoor = static_cast<const CDoor*>(pObject);
bObjectIsAutoOpenDoor = HelperWillDoorAutoOpen(pDoor)
&& HelperShouldVehicleStopVsSwerveForDoor(obstructionData.vNormalizedDriveDir, obstructionData.vBoundBoxMax.GetX() - obstructionData.vBoundBoxMin.GetX(), pDoor);
if (HelperShouldIgnoreDoorEntirely(pDoor))
{
continue;
}
}
else if ((pObject->GetBaseModelInfo()
&& pObject->GetBaseModelInfo()->GetModelNameHash() == barrierHash)
|| bIsVehicleAtTollBooth)
{
//hack time: if the model is not a door, and is this one particular gate model,
//ignore it and smash through
continue;
}
if (!bObjectIsAutoOpenDoor &&
(bOtherMinIsWithinZRange || bOtherMaxIsWithinZRange || bOtherCompletelyWithinZRange) &&
(bObjectIsTallEnoughToAvoid || pObject->GetForceVehiclesToAvoid())
)
{
// Now actually do a collision threat analysis for this object
Assert(pEntity->GetIsTypeObject());
//WeaveForObject(pVeh, (CObject *)pEntity, pLeftAngle, pRightAngle);
AddObstructionForSingleObject(obstructionData, pObject);
}
}
}
}
inline void BuildCarPoly(Vec2f verts[3], Vector3::Param ina, Vector3::Param inb, Vector3::Param inc)
{
Vector3 a(ina), b(inb), c(inc);
verts[0] = Vec2f(a.x, a.y);
verts[1] = Vec2f(b.x, b.y);
verts[2] = Vec2f(c.x, c.y);
}
inline void OffsetPoly(Vec2f* verts, int numVerts, Vec2f offset)
{
for(int i = 0; i < numVerts; i++)
{
verts[i] += offset;
}
}
void HelperGetHeliAvoidanceBounds(const CVehicle* pVehicle, spdAABB& boundBox)
{
if(pVehicle->GetIsHeli() && !pVehicle->IsEngineOn())
{
//for helicopters that aren't moving, we use the bounds of the chassis and tail fin, as bounds for whole vehicle is huge
phBoundComposite* pBoundComp = static_cast<phBoundComposite*>(pVehicle->GetVehicleFragInst()->GetArchetype()->GetBound());
phBound* chassisBound = pBoundComp->GetBound(0);
boundBox.SetMin(chassisBound->GetBoundingBoxMin());
boundBox.SetMax(chassisBound->GetBoundingBoxMax());
int tailIndex = pVehicle->GetVehicleFragInst()->GetComponentFromBoneIndex(pVehicle->GetBoneIndex(HELI_TAIL));
if(tailIndex != -1)
{
phBound* tailBound = pBoundComp->GetBound(tailIndex);
if(tailBound)
{
Vector3 vecTemp;
RCC_MATRIX34(pBoundComp->GetCurrentMatrix(tailIndex)).Transform(VEC3V_TO_VECTOR3(tailBound->GetBoundingBoxMin()), vecTemp);
vecTemp.Min(vecTemp, VEC3V_TO_VECTOR3(boundBox.GetMin()));
boundBox.SetMin(VECTOR3_TO_VEC3V(vecTemp));
RCC_MATRIX34(pBoundComp->GetCurrentMatrix(tailIndex)).Transform(VEC3V_TO_VECTOR3(tailBound->GetBoundingBoxMax()), vecTemp);
vecTemp.Max(vecTemp,VEC3V_TO_VECTOR3(boundBox.GetMax()));
boundBox.SetMax(VECTOR3_TO_VEC3V(vecTemp));
}
}
//add a bit of a buffer
const Vec3V offset(0.5f, 0.5f,0.0f);
boundBox.SetMax(boundBox.GetMax() + offset);
boundBox.SetMin(boundBox.GetMin() - offset);
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::AddObstructionForSingleVehicle(AvoidanceTaskObstructionData& obstructionData, const CVehicle* const pOtherCar, bool bStationaryCar, bool bParkedCar, bool bGiveWarning)
{
const CVehicle* pVeh = obstructionData.pVeh;
if(obstructionData.carAvoidanceLevel == CAR_AVOIDANCE_NONE && !bParkedCar)
{
return;
}
Assert(pVeh);
Assert(pOtherCar);
//static dev_float s_fCarMarginUs = 0.0f;
float s_fCarMarginOther = CalculateOtherVehicleMarginForAvoidance(*pVeh, *pOtherCar, bGiveWarning);
const Vector3 vecVehPosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
const Vector3 vecOtherCarPosition = VEC3V_TO_VECTOR3(pOtherCar->GetVehiclePosition());
const float DiffX = vecOtherCarPosition.x - vecVehPosition.x;
const float DiffY = vecOtherCarPosition.y - vecVehPosition.y;
const Vector3 vecPositionDiff = vecOtherCarPosition - vecVehPosition;
// Get the current drive direction and orientation.
Vector3 vehDriveDir = RCC_VECTOR3(obstructionData.vNormalizedDriveDir);
// If the other car is already behind us there is no need trying to avoid it.
// However, if the car is stationary (or moving very slow), test against any cars
// that are further ahead than our rear bumper in case we're turning into it
const bool bUseRacingLogic = HelperShouldUseRacingLogic(obstructionData.bRacing, pVeh->InheritsFromBike(), obstructionData.fCurrentSpeed, pOtherCar);
const Vector3 vOtherVel = !bUseRacingLogic ? pOtherCar->GetAiVelocity()
: HelperGetOtherVehsRacingVelocity(pOtherCar);
const float vOtherVelMag2 = vOtherVel.Mag2();
const float fThresholdForAvoidBehind = vOtherVelMag2 > 1.0f ? 0.0f : obstructionData.vBoundBoxMin.GetYf();
if(DiffX * vehDriveDir.x + DiffY * vehDriveDir.y < fThresholdForAvoidBehind)
{
return;
}
// If the other car is attached to a trailer (riding on top of it),
// don't avoid it
if (CVehicle::IsEntityAttachedToTrailer(pOtherCar))
{
return;
}
// If we are not allowed to go crazy an the avoidance and the other car is in front of us facing away
// from us and not stuck we don't want to try and avoid us;
// It is probably just traffic.
const Vector3 otherB = VEC3V_TO_VECTOR3(pOtherCar->GetVehicleForwardDirection());
const float fDebugCarsFacingDot = vehDriveDir.Dot(otherB);
if((obstructionData.carAvoidanceLevel == CAR_AVOIDANCE_LITTLE) && !bStationaryCar
&& !pOtherCar->m_nVehicleFlags.bForceOtherVehiclesToOvertakeThisVehicle
&& pOtherCar != m_pVehicleToIgnoreWhenStopping)
{
// If the cars face roughly the same direction.
static dev_float s_fCarsFacingDotThreshold = 0.6f;
const float fAngleThresholdInRads = rage::Acosf(s_fCarsFacingDotThreshold);
const float fOtherOrientation = fwAngle::LimitRadianAngle(fwAngle::GetATanOfXY(otherB.x, otherB.y));
const float fThetaBetweenOurDesiredOtherActual = fabsf(SubtractAngleShorter(obstructionData.vehDriveOrientation, fOtherOrientation));
if(fDebugCarsFacingDot > s_fCarsFacingDotThreshold || fThetaBetweenOurDesiredOtherActual < fAngleThresholdInRads)
{
// If the obstacle car is right in front of us.
Vector3 diffNorm = Vector3(DiffX, DiffY, 0.0f);
diffNorm.Normalize();
const float fDebugOtherCarPositionDeltaDot = diffNorm.Dot(vehDriveDir);
static dev_float s_fOtherCarPositionDeltaDotThreshold = 0.3f;
if(fDebugOtherCarPositionDeltaDot > s_fOtherCarPositionDeltaDotThreshold)
{
// Don't bother avoiding this car. Instead we want to pull
// up to the car and stop.
return;
}
}
}
bool bWillingToStop = IsDrivingFlagSet(DF_StopForCars);
if(bWillingToStop)
{
if(pVeh->m_nVehicleFlags.bIsRacingConservatively && !pOtherCar->m_nVehicleFlags.bIsRacingConservatively)
{
bWillingToStop = false;
}
}
static dev_float s_fVelThresholdSqr = 1.0f * 1.0f;
const float fDriveDirDotOtherVel = vehDriveDir.Dot(vOtherVel);
const float fDriveDirDotOtherVelSqr = square(fDriveDirDotOtherVel);
static dev_float s_fVelocityPercentThreshold = 0.6f;
const float fOtherVelMag2Scaled = s_fVelocityPercentThreshold * s_fVelocityPercentThreshold * vOtherVelMag2;
//if we aren't doing full avoidance, and the guy is moving perpendicular to us, we'd rather slow down
if (bWillingToStop && vOtherVelMag2 > s_fVelThresholdSqr && fDriveDirDotOtherVelSqr < fOtherVelMag2Scaled)
{
//grcDebugDraw::Sphere(vecVehPosition, 1.5f, Color_red, false);
return;
}
//sometimes while a vehicle is changing lane and coming towards us, the collision prediction (correctly)
//assumes we'll collide in the future, but we actually won't as other vehicle will straighten up.
//so we ignore vehicles that are changing lane, when we are happily driving in ours.
if(fDebugCarsFacingDot < 0.0f && !obstructionData.bIsCurrentlyOnJunction)
{
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
const CVehicleFollowRouteHelper* pOtherFollowRoute = pOtherCar->GetIntelligence()->GetFollowRouteHelper();
if (pFollowRoute && pOtherFollowRoute && pOtherFollowRoute->CurrentRouteContainsLaneChange() )
{
s16 currentTargetPoint = pFollowRoute->GetCurrentTargetPoint();
s16 otherCurrentTargetPoint = pOtherFollowRoute->GetCurrentTargetPoint();
if(currentTargetPoint > 0 && otherCurrentTargetPoint > 0)
{
const CRoutePoint* pOtherPoint = &pOtherFollowRoute->GetRoutePoints()[otherCurrentTargetPoint];
const CRoutePoint* pOtherLastPoint = &pOtherFollowRoute->GetRoutePoints()[otherCurrentTargetPoint - 1];
//other vehicle is changing lane now
if(rage::round(pOtherPoint->GetLane().Getf()) != rage::round(pOtherLastPoint->GetLane().Getf()))
{
const CRoutePoint* pPoint = &pFollowRoute->GetRoutePoints()[currentTargetPoint];
const CRoutePoint* pLastPoint = &pFollowRoute->GetRoutePoints()[currentTargetPoint - 1];
Vector3 laneDirection = VEC3V_TO_VECTOR3(pPoint->GetPosition() - pLastPoint->GetPosition());
laneDirection.Normalize();
float laneCentreOffset = pPoint->m_fCentralLaneWidth + rage::round(pPoint->GetLane().Getf()) * pPoint->m_fLaneWidth;
Vector2 side = Vector2(pPoint->GetPosition().GetYf() - pLastPoint->GetPosition().GetYf(), pLastPoint->GetPosition().GetXf() - pPoint->GetPosition().GetXf());
side.Normalize();
Vector3 laneCentreOffsetVec((side.x * laneCentreOffset),(side.y * laneCentreOffset), 0.0f);
//target point in our lane
Vector3 laneTargetPos = VEC3V_TO_VECTOR3(pPoint->GetPosition()) + laneCentreOffsetVec;
float distToTarget = geomDistances::DistanceLineToPoint(laneTargetPos, laneDirection, vecVehPosition);
const static dev_float s_fLaneDotThreshold = 0.8f;
//check we are driving along in our lane
if( laneDirection.Dot(vehDriveDir) > s_fLaneDotThreshold && fabsf(distToTarget) < pPoint->m_fLaneWidth * 0.5f)
{
return;
}
}
}
}
}
//if we aren't doing full avoidance, and the guy is turning left across us at a junction,
//prefer slowing down
if (bWillingToStop && vOtherVelMag2 > s_fVelThresholdSqr && (DiffX * otherB.x + DiffY * otherB.y) < 0.0f
&& (obstructionData.turnDir == BIT_TURN_STRAIGHT_ON || obstructionData.turnDir == BIT_NOT_SET)
&& pOtherCar->m_nVehicleFlags.bTurningLeftAtJunction)
{
return;
}
//if someone's moving in the same direction as us, and going faster than us, don't avoid them
if ((bWillingToStop || bUseRacingLogic) && vOtherVelMag2 >= 1.0f && vOtherVelMag2 > obstructionData.fCurrentSpeed * obstructionData.fCurrentSpeed)
{
Vector3 vOtherVelNorm = vOtherVel;
vOtherVelNorm.NormalizeSafe(ORIGIN);
if (vehDriveDir.Dot(vOtherVelNorm) > 0.7f)
{
//grcDebugDraw::Sphere(vecVehPosition, 1.5f, Color_orange, false);
return;
}
}
int numVerts = 3;
Vec2f aiCarPoly[3];
// Approximate the obstacle car as a triangle, using the nearest two sides.
// If it has a velocity, sweep the triangle forward in time by N seconds to see where it will be,
// and stay out of this whole region. Then expand the region by the size of the
// pVeh car (actually just expand by the width of the pVeh car and some percentage of its length)
Matrix34 otherVehMat = RCC_MATRIX34(pOtherCar->GetMatrixRef());
spdAABB tempBox;
const spdAABB& otherBox = pOtherCar->GetLocalSpaceBoundBox(tempBox);
tempBox = otherBox;
if(pOtherCar->GetIsHeli())
{
HelperGetHeliAvoidanceBounds(pOtherCar, tempBox);
}
const float minX = tempBox.GetMin().GetXf();
const float maxX = tempBox.GetMax().GetXf();
const float minY = tempBox.GetMin().GetYf();
const float maxY = tempBox.GetMax().GetYf();
float fTimePositionIsProjected=0.0f;
if(!pVeh->m_nVehicleFlags.bDisableAvoidanceProjection)
{
Vector3 rvMyVel = pVeh->GetAiVelocity();
float dodgyCombined = rvMyVel.Dot(rvMyVel - vOtherVel);
fTimePositionIsProjected=5.0f;
if (Abs(dodgyCombined) > SMALL_FLOAT)
{
float velDotDist = vecPositionDiff.Dot(rvMyVel);
fTimePositionIsProjected = Clamp(velDotDist / dodgyCombined, 0.0f, fTimePositionIsProjected);
otherVehMat.d += vOtherVel * fTimePositionIsProjected;
}
}
Vector3 otherFrontLeftCorner;
otherVehMat.Transform(Vector3(minX - s_fCarMarginOther, maxY + s_fCarMarginOther, 0.0f), otherFrontLeftCorner);
Vector3 otherBumperVector;
otherVehMat.Transform3x3(Vector3(maxX - minX + 2 * s_fCarMarginOther , 0.0f, 0.0f), otherBumperVector);
Vector3 otherSideVector;
otherVehMat.Transform3x3(Vector3(0.0f, maxY - minY + 2 * s_fCarMarginOther, 0.0f), otherSideVector);
// The vert order here is selected so that we have clockwise winding orders
if (otherSideVector.Dot(vecPositionDiff) < 0.0f) // Other car is facing AI car
{
if (otherBumperVector.Dot(vecPositionDiff) < 0.0f) // Right side is closer
{
BuildCarPoly(aiCarPoly, otherFrontLeftCorner, otherFrontLeftCorner + otherBumperVector, otherFrontLeftCorner + otherBumperVector - otherSideVector);
}
else // Left side is closer
{
BuildCarPoly(aiCarPoly, otherFrontLeftCorner, otherFrontLeftCorner + otherBumperVector, otherFrontLeftCorner - otherSideVector);
}
}
else // Other car is facing away from AI car
{
if (otherBumperVector.Dot(vecPositionDiff) < 0.0f) // Right side is closer
{
BuildCarPoly(aiCarPoly, otherFrontLeftCorner - otherSideVector, otherFrontLeftCorner + otherBumperVector, otherFrontLeftCorner + otherBumperVector - otherSideVector);
}
else // Left side is closer
{
BuildCarPoly(aiCarPoly, otherFrontLeftCorner, otherFrontLeftCorner + otherBumperVector - otherSideVector, otherFrontLeftCorner - otherSideVector);
}
}
if (otherVehMat.c.z < 0.0f) { // vehicle is upside down
SwapEm(aiCarPoly[0], aiCarPoly[1]); // need to reorder the verts in the triangle so that we still have a clockwise winding
}
const Matrix34& vehMat = RCC_MATRIX34(pVeh->GetMatrixRef());
Vector3 aiCenter = vehMat.d;
AvoidanceTaskObstructionData::ObstructionPoly& obsPoly = obstructionData.m_VehicleObstructions.Append();
obsPoly.m_Count = 0;
obsPoly.m_Vehicle = pOtherCar;
if(pVeh->InheritsFromBike() && !bStationaryCar && pOtherCar->GetVehicleType() == VEHICLE_TYPE_CAR )
{
Vec3V vRelativeBumperPos = pOtherCar->GetTransform().UnTransform(obstructionData.vBumperCenter);
m_bBumperInsideBoundsThisFrame |= otherBox.ContainsPoint(vRelativeBumperPos);
}
Vec2f aiCenter2d(aiCenter.x, aiCenter.y);
Vec2f aiFrontLeftCorner2d(obstructionData.vFrontLeftCorner.GetXf(), obstructionData.vFrontLeftCorner.GetYf());
Vec2f aiBumperVector2d(obstructionData.vBumperVector.GetXf(), obstructionData.vBumperVector.GetYf());
Vec2f aiSideVector2d(obstructionData.vSideVector.GetXf(), obstructionData.vSideVector.GetYf());
// Think of the two sweeps we're about to do as shrinking my car to a point while growing the other car.
// To shrink my car we sweep along the bumper vector, then along the side vector.
// This will effectively shrink the whole car down to the point at the bottom right corner. So first offset the collision poly
// by the distance from the bottom right corner to the car center, so that all tests can be done relative to the car center
Vec2f aiBottomRightCorner2d = aiFrontLeftCorner2d + aiBumperVector2d + aiSideVector2d;
OffsetPoly(aiCarPoly, numVerts, aiCenter2d - aiBottomRightCorner2d);
// Is the obstacle car moving?
if (vOtherVelMag2 > SMALL_FLOAT)
{
// Project it forward in time
const bool bCanUseLongerProbe = pVeh->PopTypeIsMission() || pVeh->m_nVehicleFlags.bIsLawEnforcementVehicle || (pVeh->GetDriver() && pVeh->GetDriver()->PopTypeIsMission());
const float fLookaheadTime = Max(obstructionData.GetAvoidanceLookaheadTime(bCanUseLongerProbe) - fTimePositionIsProjected, 0.0f);
if (fLookaheadTime > 0.0f)
{
Vec2f vFuturePosOffset2d(vOtherVel.x * fLookaheadTime, vOtherVel.y * fLookaheadTime);
Vec2f tempPoly0[10];
Vec2f tempPoly1[10];
geom2D::SweepPolygon(aiCarPoly, tempPoly0, numVerts, vFuturePosOffset2d); numVerts += 2;
geom2D::SweepPolygon(tempPoly0, tempPoly1, numVerts, aiBumperVector2d); numVerts += 2;
obsPoly.m_Count = numVerts + 2;
geom2D::SweepPolygon(tempPoly1, &obsPoly.m_Verts[0], numVerts, aiSideVector2d);
}
}
else
{
Vec2f tempPoly1[10];
geom2D::SweepPolygon(aiCarPoly, tempPoly1, numVerts, aiBumperVector2d); numVerts += 2;
obsPoly.m_Count = numVerts + 2;
geom2D::SweepPolygon(tempPoly1, &obsPoly.m_Verts[0], numVerts, aiSideVector2d);
}
return;
}
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TestDirectionsAgainstSingleVehicle(const AvoidanceTaskObstructionData& obstructionData,
CEntity* pEntity, bool bStationaryCar, bool bParkedCar, bool& bGiveWarning, AvoidanceInfo* avoidanceInfoArray, int numDirections, const bool bPreventAddMarginForOtherCar)
#else // USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TestDirectionAgainstSingleVehicle(const AvoidanceTaskObstructionData& obstructionData, CEntity* pEntity, bool bStationaryCar, bool bParkedCar, bool& bGiveWarning, AvoidanceInfo& avoidanceInfo, const bool bPreventAddMarginForOtherCar)
#endif // USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
{
TUNE_GROUP_BOOL( FOLLOW_PATH_AI_STEER, RENDER_CAR_AVOIDANCE_DETAIL, false );
const CVehicle* pVeh = obstructionData.pVeh;
if(obstructionData.carAvoidanceLevel == CAR_AVOIDANCE_NONE && !bParkedCar)
{
return false;
}
Assert(pEntity);
Assert(pVeh);
// float oldLeftAngle = *pLeftAngle;
// float oldRightAngle = *pRightAngle;
// bool bFoundWayAroundLeft = false, bFoundWayAroundRight = false;
bool bHasChanged = false;
CVehicle *pOtherCar =(CVehicle *)pEntity;
//static dev_float s_fCarMarginUs = 0.0f;
float s_fCarMarginOther = !bPreventAddMarginForOtherCar
? CalculateOtherVehicleMarginForAvoidance(*pVeh, *pOtherCar, bGiveWarning)
: 0.0f;
if ((pOtherCar->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle
|| pOtherCar->m_nVehicleFlags.bIsAmbulanceOnDuty
|| pOtherCar->m_nVehicleFlags.bIsFireTruckOnDuty)
&& obstructionData.bWillStopForCars)
{
return false;
}
const Vector3 vecVehPosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
const Vector3 vecOtherCarPosition = VEC3V_TO_VECTOR3(pOtherCar->GetVehiclePosition());
const float DiffX = vecOtherCarPosition.x - vecVehPosition.x;
const float DiffY = vecOtherCarPosition.y - vecVehPosition.y;
// Get the current drive direction and orientation.
Vector3 vehDriveDir = RCC_VECTOR3(obstructionData.vNormalizedDriveDir);
// If the other car is already behind us there is no need trying to avoid it.
if(DiffX * vehDriveDir.x + DiffY * vehDriveDir.y < obstructionData.vBoundBoxMin.GetYf())
{
return false;
}
// If the other car is attached to a trailer (riding on top of it),
// don't avoid it
if (CVehicle::IsEntityAttachedToTrailer(pEntity))
{
return false;
}
// If we are not allowed to go crazy an the avoidance and the other car is in front of us facing away
// from us and not stuck we don't want to try and avoid us;
// It is probably just traffic.
if((obstructionData.carAvoidanceLevel == CAR_AVOIDANCE_LITTLE) && !bStationaryCar)
{
Vector3 otherB = VEC3V_TO_VECTOR3(pOtherCar->GetVehicleForwardDirection());
otherB.z = 0.0f;
// If the cars face roughly the same direction.
const float fDebugCarsFacingDot = vehDriveDir.Dot(otherB);
static dev_float s_fCarsFacingDotThreshold = 0.6f;
const float fAngleThresholdInRads = rage::Acosf(s_fCarsFacingDotThreshold);
const float fOtherOrientation = fwAngle::LimitRadianAngle(fwAngle::GetATanOfXY(otherB.x, otherB.y));
const float fThetaBetweenOurDesiredOtherActual = SubtractAngleShorter(obstructionData.vehDriveOrientation, fOtherOrientation);
if(fDebugCarsFacingDot > s_fCarsFacingDotThreshold || fThetaBetweenOurDesiredOtherActual < fAngleThresholdInRads)
{
// If the obstacle car is right in front of us.
Vector3 diffNorm = Vector3(DiffX, DiffY, 0.0f);
diffNorm.Normalize();
const float fDebugOtherCarPositionDeltaDot = diffNorm.Dot(vehDriveDir);
static dev_float s_fOtherCarPositionDeltaDotThreshold = 0.6f;
if(fDebugOtherCarPositionDeltaDot > s_fOtherCarPositionDeltaDotThreshold)
{
// Don't bother avoiding this car. Instead we want to pull
// up to the car and stop.
return false;
}
}
}
bool bWillingToStop = IsDrivingFlagSet(DF_StopForCars);
if(bWillingToStop)
{
if(pVeh->m_nVehicleFlags.bIsRacingConservatively && !pOtherCar->m_nVehicleFlags.bIsRacingConservatively)
{
bWillingToStop = false;
}
}
const bool bUseRacingLogic = HelperShouldUseRacingLogic(obstructionData.bRacing, pVeh->InheritsFromBike(), obstructionData.fCurrentSpeed, pOtherCar);
const Vector3 vOtherVel = !bUseRacingLogic ? pOtherCar->GetAiVelocity()
: HelperGetOtherVehsRacingVelocity(pOtherCar);
const float vOtherVelMag2 = vOtherVel.Mag2();
static dev_float s_fVelThresholdSqr = 1.0f * 1.0f;
//if we aren't doing full avoidance, and the guy is moving perpendicular to us, we'd rather slow down
if (bWillingToStop && vOtherVelMag2 > s_fVelThresholdSqr && square(vehDriveDir.Dot(vOtherVel)) < 0.5f * 0.5f * vOtherVelMag2)
{
//grcDebugDraw::Sphere(vecVehPosition, 1.5f, Color_red, false);
return false;
}
//if someone's moving in the same direction as us, and going faster than us, don't avoid them
if ((bWillingToStop || bUseRacingLogic) && vOtherVelMag2 >= 1.0f && vOtherVelMag2 > obstructionData.fCurrentSpeed * obstructionData.fCurrentSpeed)
{
Vector3 vOtherVelNorm = vOtherVel;
vOtherVelNorm.NormalizeSafe(ORIGIN);
if (vehDriveDir.Dot(vOtherVelNorm) > 0.7f)
{
//grcDebugDraw::Sphere(vecVehPosition, 1.5f, Color_orange, false);
return false;
}
}
// Rather than looking at the car in it's current position we estimate when we will collide
// with it. We then take his coordinates at that point in time.
// Vector3 otherCarPos = pOtherCar->GetPosition();
#if !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
const Vector3& vBoundBoxMax = RCC_VECTOR3(obstructionData.vBoundBoxMax);
#endif // !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
// Find the two lines in the ai car closest to the Other car.
Vector3 Line1Start, Line2Start, Line1Delta, Line2Delta;
float DotProd1 = vehDriveDir.x * DiffX + vehDriveDir.y * DiffY;
Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()));
float DotProd2 = VehRightVector.x * DiffX + VehRightVector.y * DiffY;
bool bChoseFront = false;
if(DotProd1 > 0.0f)
{ // Front bumper line
Line1Start = RCC_VECTOR3(obstructionData.vFrontLeftCorner);
bChoseFront = true;
}
else
{ // Rear bumper line
Line1Start = RCC_VECTOR3(obstructionData.vRearLeftCorner);
}
Line1Delta = RCC_VECTOR3(obstructionData.vBumperVector);
if(DotProd2 > 0.0f)
{ // Right side of car line
// ChoseFront:
// x = maxx + margin
// y = maxy - dist - margin
// fl + bumper + side:
// x = (minx - margin) + (maxx - minx + 2margin) + (0) = maxx + margin
// y = (maxy + margin) + (0) + -(dist + 2margin) = maxy - dist - margin
// !ChoseFront:
// x = maxx + margin
// y = miny + dist + margin
// rl + bumper - side (the -side seems wrong, unless we change the Line2Delta also)
// x = (minx - margin) + (maxx - minx + 2margin) + (0) = maxx + margin
// y = (miny - margin) + (0) - -(dist + 2margin) = miny + dist + margin
//Line2Start = pVeh->TransformIntoWorldSpace(Vector3(vBoundBoxMax.x + s_fCarMarginUs
// , (bChoseFront ? vBoundBoxMax.y - fDistFromBumper -s_fCarMarginUs
// : vBoundBoxMin.y + fDistFromBumper + s_fCarMarginUs)
// , 0.0f));
// I don't think the above tests are right. Seems to do the Line2 rear test from the middle of the car going forward
// This makes more sense to me:
Line2Start = Line1Start + Line1Delta; // (front right or rear right)
}
else
{ // Left side of car line
// ChoseFront:
// x = minx - margin
// y = maxy - dist - margin
// fl + side
// x = (minx - margin) + (0)
// y = (maxy + margin) + -(dist + 2margin) = maxy - dist - margin
// !ChoseFront:
// x = minx - margin
// y = miny + dist + margin
// rl - side
// x = minx - margin + 0
// y = (miny - margin) - -(dist + 2margin) = miny + dist + margin
//old code:
//Line2Start = pVeh->TransformIntoWorldSpace(Vector3(vBoundBoxMin.x - s_fCarMarginUs
// , (bChoseFront ? vBoundBoxMax.y - fDistFromBumper -s_fCarMarginUs
// : vBoundBoxMin.y + fDistFromBumper + s_fCarMarginUs)
// , 0.0f));
Line2Start = Line1Start;
}
Line2Delta = bChoseFront ? VEC3V_TO_VECTOR3(obstructionData.vSideVector) : VEC3V_TO_VECTOR3(-obstructionData.vSideVector); // backward from front, or forward from back
//grcDebugDraw::Line(Line1Start, Line1Start+Line1Delta, Color_green);
//grcDebugDraw::Line(Line2Start, Line2Start+Line2Delta, Color_green);
//Line2Delta = VEC3V_TO_VECTOR3(Negate(obstructionData.vSideVector));
//vehMat.Transform3x3(Vector3(0.0f
// , (fDistFromBumper + 2.0f * s_fCarMarginUs)
// , 0.0f)
// , Line2Delta);
// Find the two lines in the other car that are closest to the ai car.
Vector3 Line1Start_Other, Line2Start_Other, Line1Delta_Other, Line2Delta_Other;
Vector3 OtherCarRightVector(VEC3V_TO_VECTOR3(pOtherCar->GetVehicleRightDirection()));
Vector3 OtherCarForwardVector(VEC3V_TO_VECTOR3(pOtherCar->GetVehicleForwardDirection()));
DotProd1 = OtherCarForwardVector.x * DiffX + OtherCarForwardVector.y * DiffY;
DotProd2 = OtherCarRightVector.x * DiffX + OtherCarRightVector.y * DiffY;
spdAABB tempBox;
const spdAABB& otherBox = pOtherCar->GetLocalSpaceBoundBox(tempBox);
const float minX = otherBox.GetMin().GetXf();
const float maxX = otherBox.GetMax().GetXf();
const float minY = otherBox.GetMin().GetYf();
const float maxY = otherBox.GetMax().GetYf();
if(DotProd1 < 0.0f)
{ // Front bumper line
Line1Start_Other = pOtherCar->TransformIntoWorldSpace(Vector3(minX - s_fCarMarginOther, maxY + s_fCarMarginOther, 0.0f));
}
else
{ // Rear bumper line
Line1Start_Other = pOtherCar->TransformIntoWorldSpace(Vector3(minX - s_fCarMarginOther, minY - s_fCarMarginOther, 0.0f));
}
Line1Delta_Other = VEC3V_TO_VECTOR3(pOtherCar->GetTransform().Transform3x3(Vec3V(maxX - minX + 2 * s_fCarMarginOther, 0.0f, 0.0f)));
if(DotProd2 < 0.0f)
{ // Right side of car line
Line2Start_Other = pOtherCar->TransformIntoWorldSpace(Vector3(maxX + s_fCarMarginOther, minY - s_fCarMarginOther, 0.0f));
}
else
{ // Left side of car line
Line2Start_Other = pOtherCar->TransformIntoWorldSpace(Vector3(minX - s_fCarMarginOther, minY - s_fCarMarginOther, 0.0f));
}
Line2Delta_Other = VEC3V_TO_VECTOR3(pOtherCar->GetTransform().Transform3x3(Vec3V(0.0f, maxY - minY + 2 * s_fCarMarginOther, 0.0f)));
//grcDebugDraw::Line(Line1Start_Other, Line1Start_Other+Line1Delta_Other, Color_blue);
//grcDebugDraw::Line(Line2Start_Other, Line2Start_Other+Line2Delta_Other, Color_blue);
#if !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
// Narrow vehicles(bikes) will not detect hitting wider vehicles sometimes.
// This is why we have to change the order of the tests for them.
bool bSwapRound = false;
if(vBoundBoxMax.x <
maxX)
{
bSwapRound = true;
}
#endif // !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
// Scale up the vehicles velocity so vehicles look further ahead when considering vehicles for avoidance.
// This is to fix issues where vehicles have closely matched speeds.
//static dev_float OUT_SPEED_SCALE = 1.5f;
//float OurMoveSpeed = GetCruiseSpeed();//rage::Sqrtf(pVeh->GetAiVelocity().x * pVeh->GetAiVelocity().x + pVeh->GetAiVelocity().y * pVeh->GetAiVelocity().y)*OUT_SPEED_SCALE;
float OurMoveSpeed = obstructionData.fCurrentSpeed;
OurMoveSpeed = rage::Max(pVeh->GetAiVelocity().XYMag(), 10.0f);
const bool bCanUseLongerProbe = pVeh->PopTypeIsMission() || pVeh->m_nVehicleFlags.bIsLawEnforcementVehicle || (pVeh->GetDriver() && pVeh->GetDriver()->PopTypeIsMission());
const float fLookaheadTime = obstructionData.GetAvoidanceLookaheadTime(bCanUseLongerProbe);
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
Assert(numDirections <= kMaxObstructionProbeDirections);
static const int kMaxVecs = kMaxObstructionProbeDirections/4;
CompileTimeAssert(kMaxVecs*4 == kMaxObstructionProbeDirections);
Vec2V testDirXYArray[kMaxVecs*4];
const int lastDir = numDirections - 1;
for(int j = 0, k = 0; j < numDirections; j += 4, k++)
{
// Note: the Min() stuff here is a bit messy, but reading past the array passed in
// by the user would be a bit risky.
const AvoidanceInfo& avoidanceInfo0 = avoidanceInfoArray[j];
const AvoidanceInfo& avoidanceInfo1 = avoidanceInfoArray[Min(j + 1, lastDir)];
const AvoidanceInfo& avoidanceInfo2 = avoidanceInfoArray[Min(j + 2, lastDir)];
const AvoidanceInfo& avoidanceInfo3 = avoidanceInfoArray[Min(j + 3, lastDir)];
// Load up the four directions in vector registers.
const ScalarV dir0V = LoadScalar32IntoScalarV(avoidanceInfo0.fDirection);
const ScalarV dir1V = LoadScalar32IntoScalarV(avoidanceInfo1.fDirection);
const ScalarV dir2V = LoadScalar32IntoScalarV(avoidanceInfo2.fDirection);
const ScalarV dir3V = LoadScalar32IntoScalarV(avoidanceInfo3.fDirection);
// Put them together in one vector, and compute the angle we want to compute cos and sin for.
const Vec4V dirV(dir0V, dir1V, dir2V, dir3V);
// Compute the sin and cos values of the four angles.
Vec4V sinV, cosV;
SinAndCos(sinV, cosV, dirV);
const Vec4V zeroV(V_ZERO);
const Vec4V temp0V = MergeXY(cosV, zeroV);
const Vec4V temp1V = MergeXY(sinV, zeroV);
const Vec4V temp2V = MergeZW(cosV, zeroV);
const Vec4V temp3V = MergeZW(sinV, zeroV);
const Vec4V xy00AV = MergeXY(temp0V, temp1V);
const Vec4V xy00BV = MergeZW(temp0V, temp1V);
const Vec4V xy00CV = MergeXY(temp2V, temp3V);
const Vec4V xy00DV = MergeZW(temp2V, temp3V);
testDirXYArray[j ] = xy00AV.GetXY();
testDirXYArray[j + 1] = xy00BV.GetXY();
testDirXYArray[j + 2] = xy00CV.GetXY();
testDirXYArray[j + 3] = xy00DV.GetXY();
}
//const Vec3V otherCarVelV = VECTOR3_TO_VEC3V(pOtherCar->GetAiVelocity());
bool hitAngleArray[kMaxObstructionProbeDirections];
bool gotHits = false;
for(int i = 0; i < numDirections; i++)
{
const bool hitThisAngle = TestForThisAngleNew(testDirXYArray[i],
VECTOR3_TO_VEC3V(Line1Start).GetXY(), VECTOR3_TO_VEC3V(Line1Delta).GetXY(),
VECTOR3_TO_VEC3V(Line2Start).GetXY(), VECTOR3_TO_VEC3V(Line2Delta).GetXY(),
VECTOR3_TO_VEC3V(Line1Start_Other).GetXY(), VECTOR3_TO_VEC3V(Line1Delta_Other).GetXY(),
VECTOR3_TO_VEC3V(Line2Start_Other).GetXY(), VECTOR3_TO_VEC3V(Line2Delta_Other).GetXY(),
pOtherCar->GetAiVelocity().x, pOtherCar->GetAiVelocity().y,
OurMoveSpeed, fLookaheadTime);
hitAngleArray[i] = hitThisAngle;
gotHits |= hitThisAngle;
}
if(gotHits)
{
const float fDistToOtherCar = (Line1Start - vecOtherCarPosition).XYMag();
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_Vehicle, fDistToOtherCar, pVeh->GetAiVelocity(), vOtherVel, obstructionData.fCurrentSpeed);
const Vector3 vDirToObstruction = vecOtherCarPosition - Line1Start;
const float dirToObstruction = fwAngle::LimitRadianAngle(fwAngle::GetATanOfXY(vDirToObstruction.x, vDirToObstruction.y));
const bool shouldGiveWarningIfChanged = ShouldGiveWarning(pVeh, pOtherCar);
for(int i = 0; i < numDirections; i++)
{
if (hitAngleArray[i])
{
AvoidanceInfo& avoidanceInfo = avoidanceInfoArray[i];
//update this avoidanceInfo struct
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = fDistToOtherCar;
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_Vehicle;
avoidanceInfo.vVelocity = pOtherCar->GetAiVelocity();
avoidanceInfo.vPosition = vecOtherCarPosition;
avoidanceInfo.fDirToObstruction = dirToObstruction;
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsStationaryCar = bStationaryCar;
avoidanceInfo.bIsObstructedByLaw = false;
avoidanceInfo.bIsTurningLeftAtJunction = pOtherCar->m_nVehicleFlags.bTurningLeftAtJunction || pOtherCar->m_nVehicleFlags.bIsWaitingToTurnLeft;
avoidanceInfo.bIsHesitating = pOtherCar->m_nVehicleFlags.bIsHesitating;
if(shouldGiveWarningIfChanged)
{
avoidanceInfo.bGiveWarning = true;
}
bHasChanged = true;
}
}
}
// See if we want to flash the lights
if(bHasChanged)
{
// Possibly flash the headlights, etc.
if(shouldGiveWarningIfChanged)
{
bGiveWarning = true;
m_vCachedGiveWarningPosition = VEC3V_TO_VECTOR3(pOtherCar->GetVehiclePosition());
}
}
}
#else
if (TestForThisAngle(avoidanceInfo.fDirection, &Line1Start, &Line1Delta, &Line2Start, &Line2Delta, &Line1Start_Other, &Line1Delta_Other, &Line2Start_Other, &Line2Delta_Other, pOtherCar->GetAiVelocity().x, pOtherCar->GetAiVelocity().y, OurMoveSpeed, bSwapRound, fLookaheadTime))
{
//update this avoidanceInfo struct
const float fDistToOtherCar = (Line1Start - vecOtherCarPosition).XYMag();
const float fObstructionScore = ScoreOneObstructionAngleIndependent(AvoidanceInfo::Obstruction_Vehicle, fDistToOtherCar, pVeh->GetAiVelocity(), vOtherVel, obstructionData.fCurrentSpeed);
if (!avoidanceInfo.HasObstruction() || fObstructionScore > avoidanceInfo.fObstructionScore)
{
avoidanceInfo.fDistToObstruction = fDistToOtherCar;
avoidanceInfo.ObstructionType = AvoidanceInfo::Obstruction_Vehicle;
avoidanceInfo.vVelocity = pOtherCar->GetAiVelocity();
avoidanceInfo.vPosition = vecOtherCarPosition;
Vector3 vDirToObstruction = vecOtherCarPosition - Line1Start;
avoidanceInfo.fDirToObstruction = fwAngle::GetATanOfXY(vDirToObstruction.x, vDirToObstruction.y);
avoidanceInfo.fDirToObstruction = fwAngle::LimitRadianAngle(avoidanceInfo.fDirToObstruction);
avoidanceInfo.fObstructionScore = fObstructionScore;
avoidanceInfo.bIsStationaryCar = bStationaryCar;
avoidanceInfo.bIsObstructedByLaw = false;
avoidanceInfo.bIsTurningLeftAtJunction = pOtherCar->m_nVehicleFlags.bTurningLeftAtJunction || pOtherCar->m_nVehicleFlags.bIsWaitingToTurnLeft;
avoidanceInfo.bIsHesitating = pOtherCar->m_nVehicleFlags.bIsHesitating;
bHasChanged = true;
}
}
// See if we want to flash the lights
if(bHasChanged)
{
// Possibly flash the headlights, etc.
if(ShouldGiveWarning(pVeh, pOtherCar))
{
avoidanceInfo.bGiveWarning = true;
bGiveWarning = true;
m_vCachedGiveWarningPosition = VEC3V_TO_VECTOR3(pOtherCar->GetVehiclePosition());
}
}
#endif // USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
#if __BANK
if( RENDER_CAR_AVOIDANCE_DETAIL && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()) + ZAXIS*4.0f, VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) + ZAXIS*4.0f, Color_red, Color_red);
#if 0
// Do some vectormap rendering of the collisions we're doing
fwVectorMap::DrawLine(Line1Start, Line1Start + Line1Delta, Color_BlueViolet, Color_BlueViolet);
fwVectorMap::DrawLine(Line2Start, Line2Start + Line2Delta, Color_BlueViolet, Color_BlueViolet);
fwVectorMap::DrawLine(Line1Start_Other, Line1Start_Other + Line1Delta_Other, Color_burlywood, Color_burlywood);
fwVectorMap::DrawLine(Line2Start_Other, Line2Start_Other + Line2Delta_Other, Color_burlywood, Color_burlywood);
// Now try out the sweeps and see if they will work?
Vector3 lookAhead = vOtherVel;
lookAhead.Scale(fLookaheadTime);
Vec2f lookAhead2d(lookAhead.x, lookAhead.y);
Vec2f occludedAreaPoly0[10];
Vec2f occludedAreaPoly1[10];
Vec2f occludedAreaPoly2[10];
// This is annoying, the lines don't go in any particularly useful order
Vector3 corner0, corner1, corner2;
if (DotProd1 < 0.0f)
{
// Line1 is the front
corner0 = Line1Start_Other;
corner1 = Line1Start_Other + Line1Delta_Other;
corner2 = Line2Start_Other;
}
else
{
// Line1 is the back
corner0 = Line2Start_Other + Line2Delta_Other;
corner1 = Line1Start_Other + Line1Delta_Other;
corner2 = Line1Start_Other;
}
occludedAreaPoly0[0] = Vec2f(corner0.x, corner0.y);
occludedAreaPoly0[1] = Vec2f(corner1.x, corner1.y);
occludedAreaPoly0[2] = Vec2f(corner2.x, corner2.y);
Vec2f bumperDir(Line1Delta.x, Line1Delta.y);
Vec2f halfBumperDir = bumperDir * Vec2f(0.5f, 0.5f);
Vec2f sideDir(Line2Delta.x, Line2Delta.y);
// offset by half bumper dir
for(int i = 0; i < 3; i++)
{
occludedAreaPoly0[i] = occludedAreaPoly0[i] - halfBumperDir;
}
int numVerts = 3;
if (lookAhead.Mag2() > SMALL_FLOAT)
{
geom2D::SweepPolygon(occludedAreaPoly0, occludedAreaPoly1, numVerts, bumperDir); numVerts += 2;// -> 5-gon
geom2D::SweepPolygon(occludedAreaPoly1, occludedAreaPoly2, numVerts, sideDir); numVerts += 2; // -> 7-gon
geom2D::SweepPolygon(occludedAreaPoly2, occludedAreaPoly0, numVerts, lookAhead2d); numVerts += 2;// -> 9-gon
}
else
{
geom2D::SweepPolygon(occludedAreaPoly0, occludedAreaPoly1, numVerts, bumperDir); numVerts += 2;// -> 5-gon
geom2D::SweepPolygon(occludedAreaPoly1, occludedAreaPoly0, numVerts, sideDir); numVerts += 2; // -> 7-gon
}
for(int i = 0; i < numVerts; i++)
{
fwVectorMap::DrawLine(occludedAreaPoly0[i], occludedAreaPoly0[(i+1)%numVerts], Color_LightSalmon, Color_LightSalmon);
}
// Check for a collision
Vec2f probeStart = Vec2f(Line1Start.x, Line1Start.y) + halfBumperDir;
float OurSpeedX = OurMoveSpeed * rage::Cosf(avoidanceInfo.fDirection);
float OurSpeedY = OurMoveSpeed * rage::Sinf(avoidanceInfo.fDirection);
Vec2f probeDir(OurSpeedX * fLookaheadTime, OurSpeedY * fLookaheadTime);
Vec2f probeEnd = probeStart + probeDir;
float tEnter, tLeave;
bool hitSomething = geom2D::Test2DSegVsPolygon(tEnter, tLeave, probeStart, probeEnd, occludedAreaPoly0, numVerts);
if (hitSomething)
{
fwVectorMap::DrawLine(probeStart, probeEnd, Color_orange, Color_orange);
Vec2f isectPoint = Lerp(tEnter, probeStart, probeEnd);
fwVectorMap::DrawMarker(Vector3(isectPoint.GetX(), isectPoint.GetY(), 0.0f), Color_red, 0.5f);
}
else
{
fwVectorMap::DrawLine(probeStart, probeEnd, Color_blue, Color_blue);
}
#endif
}
#endif // __BANK
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && bHasChanged && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()) + Vector3(0.0f,0.0f,1.0f),
VEC3V_TO_VECTOR3(pOtherCar->GetVehiclePosition()) + Vector3(0.0f,0.0f,1.0f),
Color32(100, 0, 200, 255));
grcDebugDraw::Line(Line1Start + Vector3(0.0f,0.0f,2.0f),
Line1Start + Line1Delta + Vector3(0.0f,0.0f,2.0f),
Color32(100, 100, 200, 255));
grcDebugDraw::Line(Line2Start + Vector3(0.0f,0.0f,2.0f),
Line2Start + Line2Delta + Vector3(0.0f,0.0f,2.0f),
Color32(100, 100, 200, 255));
grcDebugDraw::Line(Line1Start_Other + Vector3(0.0f,0.0f,2.0f),
Line1Start_Other + Line1Delta_Other + Vector3(0.0f,0.0f,2.0f),
Color32(100, 100, 200, 255));
grcDebugDraw::Line(Line2Start_Other + Vector3(0.0f,0.0f,2.0f),
Line2Start_Other + Line2Delta_Other + Vector3(0.0f,0.0f,2.0f),
Color32(100, 100, 200, 255));
}
#endif //__DEV
return bHasChanged;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::AddObstructionForSinglePed(AvoidanceTaskObstructionData& obstructionData, CEntity* pEntity)
{
Assert(pEntity);
Assert(pEntity->GetIsTypePed());
Assert(obstructionData.pVeh);
TUNE_GROUP_FLOAT(VEH_AI_AVOIDANCE, pedAvoidanceScale, 0.4f, 0.0f, 10.0f, 0.01f);
// There are some conditions in which we don't want to avoid the ped
if(IsDrivingFlagSet(DF_DontSteerAroundPlayerPed) && pEntity == FindPlayerPed())
{
return;
}
// Use the coordinates of our front bumper.
Vector3 bumperCrs = RCC_VECTOR3(obstructionData.vBumperCenter);
const Vector3 vecEntityPosition = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
const float DiffX = vecEntityPosition.x - bumperCrs.x; //pVeh->GetPosition().x;
const float DiffY = vecEntityPosition.y - bumperCrs.y; //pVeh->GetPosition().y;
// Angle to other car
const float Orientation = fwAngle::GetATanOfXY(DiffX, DiffY);
// Distance to other car
const float Distance = rage::Sqrtf(DiffX*DiffX + DiffY*DiffY);
const float fCloseDistThreshold = !obstructionData.pVeh->InheritsFromBike() ? 1.0f : 0.25f;
if(Distance < fCloseDistThreshold)
{
return; // Ped is too close. Probably standing on roof. Want to avoid div by zero below.
}
//Don't avoid anything that's further away than we want to check.
//Slow moving cars care way too much about peds and objects otherwise
const float fLookaheadTime = obstructionData.GetAvoidanceLookaheadTime(false);
const float averagedSpeed = (obstructionData.fDesiredSpeed + obstructionData.fCurrentSpeed) * 0.5f;
if (Distance > averagedSpeed * fLookaheadTime)
{
return;
}
// Calculated how much of the angles are blocked
// Work out the proper steering angle.
// Radius of a ped and a bit more + allow for the size of our
// car(so that we steer wide a bit further)
// static dev_float s_fExtraDistance = 0.0f;
const float fExtraWidth = !obstructionData.pVeh->InheritsFromBike() ? ms_fExtraWidthForCars : ms_fExtraWidthForBikes;
const CPed* pPed = static_cast<CPed*>(pEntity);
float fProjectedSizeBase = pPed->GetIsDeadOrDying() ? 1.1f : 0.8f;
if (pPed == m_Params.GetTargetEntity().GetEntity())
{
fProjectedSizeBase *= m_Params.GetTargetObstructionSizeModifier();
}
const float ProjectedSize = fProjectedSizeBase + ((obstructionData.vBoundBoxMax.GetXf() + fExtraWidth) * 2.4f);
//const float halfBlockedAngleRange = 2.0f * rage::Atan2f(ProjectedSize, Distance + s_fExtraDistance) * pedAvoidanceScale;
const float halfBlockedAngleRange = (ProjectedSize / Distance) * pedAvoidanceScale;
AvoidanceTaskObstructionData::ObstructionCircle& circle = obstructionData.m_PedAndObjectObstructions.Append();
circle.m_Distance = Distance;
circle.m_Orientation = Orientation;
circle.m_HalfBlockedAngleRange = halfBlockedAngleRange;
circle.m_Entity = pEntity;
circle.m_ObstructionType = AvoidanceInfo::Obstruction_Ped;
}
bool HelperObjectIsAFence(const CObject* pObject)
{
static const int s_iNumFences = 6;
static const u32 fences[s_iNumFences] =
{
ATSTRINGHASH("prop_fnc_farm_01a", 0xf2e78a52),
ATSTRINGHASH("prop_fnc_farm_01b", 0x5972fb1),
ATSTRINGHASH("prop_fnc_farm_01c", 0x4ed9c235),
ATSTRINGHASH("prop_fnc_farm_01d", 0xb9c69815),
ATSTRINGHASH("prop_fnc_farm_01e", 0xcc003c88),
ATSTRINGHASH("prop_fnc_farm_01f", 0x1649d11a)
};
if (!pObject->GetBaseModelInfo() )
{
return false;
}
const u32 modelNameHash = pObject->GetBaseModelInfo()->GetModelNameHash();
for (int i = 0; i < s_iNumFences; i++)
{
if (modelNameHash == fences[i])
{
return true;
}
}
return false;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::AddObstructionForSingleObject(AvoidanceTaskObstructionData& obstructionData, CObject* pEntity)
{
phBound *pBound = NULL;
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
if(pModelInfo->GetFragType())
{
pBound =(phBound *)pModelInfo->GetFragType()->GetPhysics(0)->GetCompositeBounds();
}
else if(pModelInfo->GetPhysics())
{
phArchetype *pArchetype = pModelInfo->GetPhysics();
pBound = pArchetype->GetBound();
}
if(!pBound)
{
return;
}
fwDynamicArchetypeComponent* pDynamicComponent = pModelInfo->GetDynamicComponentPtr();
if(!pDynamicComponent)
{
//the following pool is not thread safe
AI_AVOIDANCE_POOL_LOCK
if(!fwDynamicArchetypeComponent::_ms_pPool->IsFull())
{
pDynamicComponent = &pModelInfo->CreateDynamicComponentIfMissing();
}
}
if(!pDynamicComponent)
{
return;
}
Vec4V avoidancePoint = pDynamicComponent->GetAvoidancePoint();
if(IsEqualAll(avoidancePoint, Vec4V(V_FLT_MAX))) //created as V_FLT_MAX = not yet checked
{
avoidancePoint = Vec4V(V_NEG_FLT_MAX); //assign to V_NEG_FLT_MAX to prevent checking multiple times
if (!HelperObjectIsAFence(pEntity))
{
FindAvoidancePoints(pBound, avoidancePoint);
}
pDynamicComponent->SetAvoidancePoint(avoidancePoint);
}
//if avoidancePoint equals V_NEG_FLT_MAX then it's been checked and avoidance isn't required
if(IsEqualAll(avoidancePoint, Vec4V(V_NEG_FLT_MAX)))
{
return;
}
// Work out world coordinates of object
Vector3 CollPoint = pEntity->TransformIntoWorldSpace(VEC3V_TO_VECTOR3(avoidancePoint.GetXYZ()));
// Use the coordinates of our front bumper.
Vector3 bumperCrs = RCC_VECTOR3(obstructionData.vBumperCenter);
float DiffX = CollPoint.x - bumperCrs.x; //pVeh->GetPosition().x;
float DiffY = CollPoint.y - bumperCrs.y; //pVeh->GetPosition().y;
// Angle to object
float Orientation = fwAngle::GetATanOfXY(DiffX, DiffY);
// Distance to other car
// Take into account its size
const float Distance = rage::Sqrtf(DiffX*DiffX + DiffY*DiffY);
const float fDistanceMinusRadius = rage::Max(0.0f, Distance - avoidancePoint.GetWf());
//Don't avoid anything that's further away than we want to check.
//Slow moving cars care way too much about peds and objects otherwise
const float fLookaheadTime = obstructionData.GetAvoidanceLookaheadTime(false);
const float averagedSpeed = (obstructionData.fDesiredSpeed + obstructionData.fCurrentSpeed) * 0.5f;
if (fDistanceMinusRadius > averagedSpeed * fLookaheadTime)
{
return;
}
// Calculated how much of the angles are blocked
// Work out the proper steering angle.
TUNE_GROUP_FLOAT( FOLLOW_PATH_AI_STEER, sfProjectedSizeScaler, 0.75f, 0.0f, 10.0f, 0.1f);
float fSizeScaler = 1.0f;
if( m_threePointTurnInfo.m_iNumRecentThreePointTurns >= m_threePointTurnInfo.ms_iMaxAttempts)
{
fSizeScaler = Max(0.1f, sfProjectedSizeScaler - ( m_threePointTurnInfo.m_iNumRecentThreePointTurns - m_threePointTurnInfo.ms_iMaxAttempts) * 0.1f);
}
static dev_float s_fExtraProjectedSize = 0.2f;
float ProjectedSize = s_fExtraProjectedSize + avoidancePoint.GetWf(); // Radius of a pole and a bit more
// Allow for the size of our car(so that we steer wide a bit further)
const float fExtraWidth = !obstructionData.pVeh->InheritsFromBike() ? ms_fExtraWidthForCars : ms_fExtraWidthForBikes;
ProjectedSize += (obstructionData.vBoundBoxMax.GetXf() + fExtraWidth) * 2.4f;
ProjectedSize *= fSizeScaler;
float BlockedRange = ProjectedSize / Distance;
// TUNE_GROUP_BOOL(ASDF, DrawObjectAvoidance, true);
// if (DrawObjectAvoidance)
// {
// grcDebugDraw::Line(bumperCrs, CollPoint, Color_white, -1);
// grcDebugDraw::Sphere(CollPoint, ProjectedSize, Color_yellow3, false, -1);
// }
AvoidanceTaskObstructionData::ObstructionCircle& circle = obstructionData.m_PedAndObjectObstructions.Append();
circle.m_Distance = fDistanceMinusRadius;
circle.m_Orientation = Orientation;
circle.m_HalfBlockedAngleRange = BlockedRange * 0.5f;
circle.m_Entity = pEntity;
circle.m_ObstructionType = AvoidanceInfo::Obstruction_Object;
}
float CTaskVehicleGoToPointWithAvoidanceAutomobile::FindMaximumSpeedForRacingCar(CVehicle* pVeh, const float fDesiredSpeed, const spdAABB& boundBox)
{
PF_FUNC(AI_AvoidanceFindMaximumSpeedForRacingCar);
Assert(pVeh->m_nVehicleFlags.bIsRacing);
float MinX, MaxX, MinY, MaxY;
// Identify scan area.(Block around car)
const Vector3 vecVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
GenerateMinMaxForPedAvoidance(vecVehiclePos, 20.0f, MinX, MinY, MaxX, MaxY);
float fMaxSpeed = fDesiredSpeed;
const float fActualSpeed = pVeh->GetAiXYSpeed();
const bool bIsBike = pVeh->InheritsFromBike();
if (bIsBike || pVeh->InheritsFromQuadBike() || pVeh->InheritsFromAmphibiousQuadBike())
{
return fMaxSpeed;
}
// Get the current drive direction and orientation.
Vec3V vVehDriveDir = CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse));
Vector3 vehDriveDir = VEC3V_TO_VECTOR3(vVehDriveDir);
//float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
// If we are turning we turn the front vector a bit as well. This way we won't stop for cars directly ahead
// of us when we are turning but we will stop for cars that we are turning into.
float TurnAngle = 0.5f * pVeh->GetSteerAngle();
float cosTurnAngle = rage::Cosf(TurnAngle);
float sinTurnAngle = rage::Sinf(TurnAngle);
float ourNewDriveDirX = vehDriveDir.x * cosTurnAngle - vehDriveDir.y * sinTurnAngle;
vehDriveDir.y = vehDriveDir.y * cosTurnAngle + vehDriveDir.x * sinTurnAngle;
vehDriveDir.x = ourNewDriveDirX;
const float OurCarDX = vehDriveDir.x * fDesiredSpeed; // 1 sec
const float OurCarDY = vehDriveDir.y * fDesiredSpeed;
CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
CVehicle* pOtherVehicle = (CVehicle*) pEntity;
if (!pOtherVehicle)
{
continue;
}
if(pOtherVehicle == pVeh)
{
continue;
}
if (!pOtherVehicle->m_nVehicleFlags.bIsRacing)
{
continue;
}
const bool bShouldUsingRacingLogic = HelperShouldUseRacingLogic(true, bIsBike, fActualSpeed, pOtherVehicle);
//if we don't want to use the racing speed, check how far in front th
if (!bShouldUsingRacingLogic)
{
continue;
}
const Vector3 centre = VEC3V_TO_VECTOR3(pOtherVehicle->GetVehiclePosition()); // GetPosition is faster than pOtherVehicle->GetBoundCentre();
if( centre.x <= MinX ||
centre.x >= MaxX ||
centre.y <= MinY ||
centre.y >= MaxY ||
ABS(centre.z - vecVehiclePos.z) >= 5.0f) // was 5.0f.
{
continue;
}
//const Vec3V vecOtherVehPosition = pOtherVehicle->GetVehiclePosition();
//TUNE_GROUP_FLOAT(CAR_AI, fAvoidBehindDist, 0.0f, -100.0f, 100.0f, 0.1f);
const Vec3V vDeltaPos = pOtherVehicle->GetVehiclePosition() - pVeh->GetVehiclePosition();
//const Vec3V vDeltaPosNormalized = NormalizeFastSafe(vDeltaPos, Vec3V(V_ZERO));
if (Dot(vDeltaPos.GetXY(), RCC_VEC3V(vehDriveDir).GetXY()).Getf() < boundBox.GetMin().GetYf())
{
continue;
}
Vec3V vOtherDriveDir = pOtherVehicle->GetVehicleForwardDirection();
vOtherDriveDir.SetZ(ScalarV(V_ZERO));
vOtherDriveDir = NormalizeFast(vOtherDriveDir);
//don't do this if facing the wrong dir
if (IsLessThanAll(Dot(vVehDriveDir, vOtherDriveDir), ScalarV(V_ZERO)))
{
continue;
}
const float fOtherSpeed = pOtherVehicle->GetAiXYSpeed();
if (fOtherSpeed < 1.0f)
{
continue;
}
spdAABB otherBoxModified;
const spdAABB& otherBox = pOtherVehicle->GetLocalSpaceBoundBox(otherBoxModified);
otherBoxModified = otherBox;
//if the other vehicle is a bike, add some padding so we dont' sidle up next to bikes that aren't
//in the exact center of a lane
if (pOtherVehicle->InheritsFromBike())
{
const Vec3V offset(0.5f, 1.0f, 0.5f);
otherBoxModified.SetMax(otherBoxModified.GetMax() + offset);
otherBoxModified.SetMin(otherBoxModified.GetMin() - offset);
}
const Vector3& vOtherVel = pOtherVehicle->GetAiVelocity();
const float OtherCarDX = vOtherVel.x; // 1 sec
const float OtherCarDY = vOtherVel.y;
const float DeltaSpeedX = OtherCarDX - OurCarDX;
const float DeltaSpeedY = OtherCarDY - OurCarDY;
float CollisionT = Test2MovingRectCollision_OnlyFrontBumper(pVeh, pOtherVehicle,
DeltaSpeedX, DeltaSpeedY,
vehDriveDir, VEC3V_TO_VECTOR3(vOtherDriveDir), boundBox, otherBoxModified, false);
const float CollisionT2 = Test2MovingRectCollision( pOtherVehicle, pVeh,
-DeltaSpeedX, -DeltaSpeedY,
VEC3V_TO_VECTOR3(vOtherDriveDir), vehDriveDir, otherBoxModified, boundBox, false);
CollisionT = rage::Min(CollisionT, CollisionT2);
const float fDistToCollision = fDesiredSpeed * CollisionT;
static dev_float s_fMaxDistToDoRacingSlowdown = 2.0f;
static dev_float s_fMaxDistToDoRacingSlowdownConservative = 5.0f;
float fMaxDistToDoRacingSlowdown = !pVeh->m_nVehicleFlags.bIsRacingConservatively ?
s_fMaxDistToDoRacingSlowdown : s_fMaxDistToDoRacingSlowdownConservative;
if (fDistToCollision < fMaxDistToDoRacingSlowdown)
{
//CVehicle::ms_debugDraw.AddLine(pVeh->GetVehiclePosition(), pVeh->GetVehiclePosition() + Vec3V(0.0f, 0.0f, 4.0f), Color_orange);
static dev_float s_fSlowdown = 0.75f;
static dev_float s_fSlowdownConservative = 3.0f;
float fSlowdown = !pVeh->m_nVehicleFlags.bIsRacingConservatively ?
s_fSlowdown : s_fSlowdownConservative;
fMaxSpeed = rage::Min(fMaxSpeed, fOtherSpeed - fSlowdown);
fMaxSpeed = rage::Max(0.0f, fMaxSpeed);
}
}
return fMaxSpeed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindMaximumSpeedForThisCarInTraffic
// PURPOSE : Takes traffic into account and works out the maximum speed
// we can go at.
/////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleGoToPointWithAvoidanceAutomobile::FindMaximumSpeedForThisCarInTraffic(CVehicle* pVeh, const float SteerDirection, const bool bUseAltSteerDirection, const bool bGiveWarning, float RequestedSpeed, float avoidanceRadius, const spdAABB& boundBox, CarAvoidanceLevel carAvoidanceLevel, bool bWasSlowlyPushingPlayer)
{
PF_FUNC(AI_AvoidanceFindMaximumSpeedForThisCarInTraffic);
float MinX, MaxX, MinY, MaxY, MaxSpeed;
// s32 LoopX, LoopY;
float fOriginalSpeed = RequestedSpeed;
const bool bAvoidVehs = IsDrivingFlagSet(DF_StopForCars);
const bool bAvoidPeds = IsDrivingFlagSet(DF_StopForPeds);
const bool bStopForTraffic = IsDrivingFlagSet(DF_StopAtLights); //used for slowing for doors
const bool bSteerAroundObjects = IsDrivingFlagSet(DF_SteerAroundObjects); //also used for slowing for doors
if( !bAvoidVehs && !bAvoidPeds && !bStopForTraffic && !bSteerAroundObjects)
{
return(RequestedSpeed);
}
// Identify scan area.(Block around car)
const Vector3 vecVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
GenerateMinMaxForPedAvoidance(vecVehiclePos, avoidanceRadius, MinX, MinY, MaxX, MaxY);
// If we were blocked to stop by a car last frame we test that one first. Chances are it still blocks us and we can jump straight out.
MaxSpeed = RequestedSpeed; // Don't want to go faster than this anyhow
if(m_pOptimization_pLastCarBlockingUs)
{
bool bCheckForBraking = true;
// Only periodically update the braking if the car is stationary.
// As soon as it is free to move again we update hte brakes each frame
if(pVeh->GetIntelligence()->GetBrakingDistributer().IsRegistered())
{
if(!pVeh->GetIntelligence()->GetBrakingDistributer().ShouldBeProcessedThisFrame())
{
bCheckForBraking = false;
}
}
if( bCheckForBraking || m_bAvoidingCarsUntilClear)
{
// CVehicle *pObstructor = m_pOptimization_pLastCarBlockingUs;
// m_pOptimization_pLastCarBlockingUs = NULL;
// SlowCarDownForOtherCar(pVeh, pObstructor, &MaxSpeed, fOriginalSpeed, SteerDirection, bUseAltSteerDirection, bGiveWarning, vBoundBoxMin, vBoundBoxMax, pVeh->GetBoundRadius(), true);
}
else
{
MaxSpeed = 0.0f;
}
}
bool bStoppedForDoor = false;
if(MaxSpeed > SMALL_FLOAT)
{
// if(pAvoidanceLists->bInAnInterior)
// {
// if(bAvoidVehs)
// {
// SlowCarDownForCarsSectorList(pVeh, pAvoidanceLists->MloExtractedVehObjectList, MinX, MinY, MaxX, MaxY, &MaxSpeed, cruiseSpeedMult);
// }
//
// if(bAvoidPeds)
// {
// SlowCarDownForPedsSectorList(pVeh, pAvoidanceLists->MloExtractedPedObjectList, MinX, MinY, MaxX, MaxY, &MaxSpeed, cruiseSpeedMult);
// }
// }
// else
{
// get lists of entities to check against - don't access the sector blocks directly(it's not allowed...)
// First we check for cars
if(bAvoidVehs || m_bBumperInsideBoundsThisFrame)
{
SlowCarDownForCarsSectorList(pVeh, MinX, MinY, MaxX, MaxY, MaxSpeed, fOriginalSpeed, SteerDirection, bUseAltSteerDirection, bGiveWarning, boundBox, carAvoidanceLevel);
}
//if(bAvoidPeds)
{
SlowCarDownForPedsSectorList(pVeh, MinX, MinY, MaxX, MaxY, &MaxSpeed, fOriginalSpeed, bWasSlowlyPushingPlayer);
}
if (bAvoidVehs || bStopForTraffic || bSteerAroundObjects)
{
const bool bStopForBreakableDoors = bStopForTraffic;
const float fMaxSpeedBeforeDoors = MaxSpeed;
SlowCarDownForDoorsSectorList(pVeh, MinX, MinY, MaxX, MaxY, &MaxSpeed, fOriginalSpeed, boundBox, bStopForBreakableDoors);
if (MaxSpeed <= 1.0f && fMaxSpeedBeforeDoors < 1.0f)
{
bStoppedForDoor = true;
}
}
}
}
// set flag in vehicle so we know car has processed ped warnings
pVeh->m_nVehicleFlags.bWarnedPeds = true;
// If we can go and previously we were stuck we wait a little bit.(Cars slow to pull away when light goes green)
if(MaxSpeed > 0.01f && m_bStoppingForTraffic)
{
m_bStoppingForTraffic = false;
m_pOptimization_pLastCarWeSlowedFor = NULL;
m_pOptimization_pLastCarBlockingUs = NULL;
SetState(State_WaitForTraffic);
return RequestedSpeed;
}
else if(MaxSpeed <= 0.0f && pVeh->GetVelocity().Mag2() < 0.25f * 0.25f)
{
m_bStoppingForTraffic = true;
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
}
if(IsDrivingFlagSet(DF_StopForCars) || bStoppedForDoor)
{
return(MaxSpeed);
}
else
{
return((MaxSpeed + RequestedSpeed) * 0.5f);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindAvoidancePoints
// PURPOSE : Goes through the collision primitives in a model and identifies the
// ones that a car could collide with(below 2 meters or so)
// For those a
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAvoidancePoints(phBound *bound, Vec4V& Result)
{
bool bSomethingFound = false;
Vector3 MinVec, MaxVec;
MinVec = Vector3(999999.9f, 999999.9f, 999999.9f);
MaxVec = Vector3(-999999.9f, -999999.9f, -999999.9f);
Matrix34 tempMat;
tempMat.Identity();
FindAvoidancePointsBound(bound, MinVec, MaxVec, bSomethingFound, tempMat);
if(bSomethingFound)
{
Result.SetX((MinVec.x + MaxVec.x) * 0.5f);
Result.SetY((MinVec.y + MaxVec.y) * 0.5f);
Result.SetZ(0.0f);
Result.SetW(rage::Max(MaxVec.x - MinVec.x, MaxVec.y - MinVec.y) * 0.5f);
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAvoidancePointsBound(phBound *bound, Vector3& MinVec, Vector3& MaxVec, bool& bResult, Matrix34 &mat)
{
if(bound->GetType() == phBound::COMPOSITE)
{
phBoundComposite *boundComposite =(phBoundComposite*)bound;
for(s32 i=0; i<boundComposite->GetNumBounds(); i++)
{
phBound* pChildBound = boundComposite->GetBound(i);
if(pChildBound)
{
Matrix34 localMatrix;
localMatrix.Dot(RCC_MATRIX34(boundComposite->GetCurrentMatrix(i)), mat);
FindAvoidancePointsBound(pChildBound, MinVec, MaxVec, bResult, localMatrix);
}
}
}
else if(bound->GetType() == phBound::GEOMETRY)
{
Assert(dynamic_cast<phBoundPolyhedron*>(bound));
phBoundPolyhedron *boundPoly =static_cast<phBoundPolyhedron*>(bound);
for(s32 i=0; i<boundPoly->GetNumPolygons(); i++)
{
const phPolygon& currPoly = boundPoly->GetPolygon(i);
// Identify the verts(Treat quads as triangles. Shouldn't make any difference)
Vector3 vtx0 = mat * VEC3V_TO_VECTOR3(boundPoly->GetVertex(currPoly.GetVertexIndex(0)));
Vector3 vtx1 = mat * VEC3V_TO_VECTOR3(boundPoly->GetVertex(currPoly.GetVertexIndex(1)));
Vector3 vtx2 = mat * VEC3V_TO_VECTOR3(boundPoly->GetVertex(currPoly.GetVertexIndex(2)));
FindAvoidancePointsPoly(vtx0, vtx1 ,vtx2, MinVec, MaxVec, bResult);
}
}
else if(bound->GetType() == phBound::BOX)
{
Assert(dynamic_cast<phBoundBox*>(bound));
phBoundBox *boundBox =static_cast<phBoundBox*>(bound);
FindAvoidancePointsBox(boundBox->GetBoxSize(), Transform(RCC_MAT34V(mat), boundBox->GetCentroidOffset()), MinVec, MaxVec, bResult);
}
else if(bound->GetType() == phBound::SPHERE)
{
phBoundSphere *boundSphere =(phBoundSphere*)bound;
Vector3 transformedCenter;
//store the current centroid offset
Vector3 centroidOffset = VEC3V_TO_VECTOR3(boundSphere->GetCentroidOffset());
mat.Transform(VEC3V_TO_VECTOR3(boundSphere->GetCentroidOffset()), transformedCenter);
boundSphere->SetCentroidOffset(RCC_VEC3V(transformedCenter));
FindAvoidancePointsSphere(boundSphere, MinVec, MaxVec, bResult);
//restore the centroid offset
boundSphere->SetCentroidOffset(RCC_VEC3V(centroidOffset));
}
}
#define CUTOFFHEIGHT 2.0f
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAvoidancePointsPoly(const Vector3 &vtx0, const Vector3 &vtx1, const Vector3 &vtx2, Vector3& MinVec, Vector3& MaxVec, bool& bResult)
{
// Only consider this polygon if it has at least one vertex below 2 meters height
if(vtx0.z < CUTOFFHEIGHT || vtx1.z < CUTOFFHEIGHT || vtx2.z < CUTOFFHEIGHT)
{
MinVec.x = rage::Min(vtx0.x, MinVec.x);
MinVec.x = rage::Min(vtx1.x, MinVec.x);
MinVec.x = rage::Min(vtx2.x, MinVec.x);
MinVec.y = rage::Min(vtx0.y, MinVec.y);
MinVec.y = rage::Min(vtx1.y, MinVec.y);
MinVec.y = rage::Min(vtx2.y, MinVec.y);
MaxVec.x = rage::Max(vtx0.x, MaxVec.x);
MaxVec.x = rage::Max(vtx1.x, MaxVec.x);
MaxVec.x = rage::Max(vtx2.x, MaxVec.x);
MaxVec.y = rage::Max(vtx0.y, MaxVec.y);
MaxVec.y = rage::Max(vtx1.y, MaxVec.y);
MaxVec.y = rage::Max(vtx2.y, MaxVec.y);
bResult = true;
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAvoidancePointsSphere(const phBoundSphere *boundSphere, Vector3& MinVec, Vector3& MaxVec, bool& bResult)
{
Vector3 Centre = VEC3V_TO_VECTOR3(boundSphere->GetCentroidOffset());
float Radius = boundSphere->GetRadius();
if(Centre.z - Radius < CUTOFFHEIGHT)
{
MinVec.x = rage::Min(Centre.x - Radius, MinVec.x);
MaxVec.x = rage::Max(Centre.x + Radius, MaxVec.x);
MinVec.y = rage::Min(Centre.y - Radius, MinVec.y);
MaxVec.y = rage::Max(Centre.y + Radius, MaxVec.y);
bResult = true;
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindAvoidancePointsBox(Vec3V_In BoxSize, Vec3V_In CentroidOffset, Vector3& MinVec, Vector3& MaxVec, bool& bResult)
{
Vector3 Centre = VEC3V_TO_VECTOR3(CentroidOffset);
Vector3 boxSize = VEC3V_TO_VECTOR3(BoxSize);
if( (Centre.z - (boxSize.z/2.0f)) < CUTOFFHEIGHT)
{
MinVec.x = rage::Min(Centre.x - (boxSize.x/2), MinVec.x);
MaxVec.x = rage::Max(Centre.x + (boxSize.x/2), MaxVec.x);
MinVec.y = rage::Min(Centre.y - (boxSize.y/2), MinVec.y);
MaxVec.y = rage::Max(Centre.y + (boxSize.y/2), MaxVec.y);
bResult = true;
}
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsObstructedByLawEnforcementPed(const CPed* pPed) const
{
//Ensure the ped is valid.
if(!pPed)
{
return false;
}
//Ensure the ped is law enforcement.
if(!pPed->IsLawEnforcementPed())
{
return false;
}
return true;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsObstructedByLawEnforcementVehicle(const CVehicle* pVehicle) const
{
//Ensure the vehicle is valid.
if(!pVehicle)
{
return false;
}
//Ensure the vehicle is law enforcement.
if(!pVehicle->IsLawEnforcementVehicle())
{
return false;
}
//Ensure the vehicle is stationary.
if(!IsThisAStationaryCar(pVehicle))
{
return false;
}
return true;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsThisAStationaryCar(const CVehicle* const pVeh) const
{
bool bIsStationary = pVeh->m_nVehicleFlags.bIsThisAStationaryCar;
if (!bIsStationary || !pVeh->GetDriver() || !pVeh->GetDriver()->IsPlayer())
{
return bIsStationary;
}
//if the driver is a player, this bisThisAStationaryCar flag will be
//set to true if the player doesn't move for a few seconds.
//however, if the player is just sitting in traffic or at a red light,
//we want to consider him as not stationary--the same as what an
//AI car would be considered if it were stopped. we will probably need
//to test these cases separately
CVehicle* pVehNonConstHack = const_cast<CVehicle*>(pVeh);
const CVehicle* pMyVeh = GetVehicle();
Assert(pMyVeh);
//fast rejection--the junction has already told us to stop
const s8 iJunctionCommand = pMyVeh->GetIntelligence()->GetJunctionCommand();
if (iJunctionCommand == JUNCTION_COMMAND_WAIT_FOR_LIGHTS || iJunctionCommand == JUNCTION_COMMAND_WAIT_FOR_TRAFFIC)
{
bIsStationary = false;
//pVehNonConstHack->GetIntelligence()->LastTimeNotStuck = fwTimer::GetTimeInMilliseconds() - 3000;
}
//slower: is there any car in front of this guy that's stopped? or moving really slow?
//in that case, just assume player knows what he's doing and stop for him
const CVehicle* pCarPlayerBehind = pVeh->GetIntelligence()->GetCarWeAreBehind();
if (pCarPlayerBehind
&& pCarPlayerBehind->GetAiXYSpeed() < 1.0f
&& pVeh->GetIntelligence()->GetDistanceBehindCarSq() < 15.0f * 15.0f
&& !pCarPlayerBehind->m_nVehicleFlags.bIsThisAStationaryCar
&& !pCarPlayerBehind->m_nVehicleFlags.bIsThisAParkedCar
&& IsGreaterThanAll(Dot(pCarPlayerBehind->GetVehicleForwardDirection(), pVeh->GetVehicleForwardDirection()), ScalarV(V_ZERO))
&& IsLessThanAll(Abs(pCarPlayerBehind->GetVehiclePosition().GetZ() - pVeh->GetVehiclePosition().GetZ()), ScalarV(V_SIX)))
{
//grcDebugDraw::Line(pVeh->GetVehiclePosition(), pCarPlayerBehind->GetVehiclePosition(), Color_green, Color_yellow);
bIsStationary = false;
pVehNonConstHack->GetIntelligence()->LastTimeNotStuck = fwTimer::GetTimeInMilliseconds() - 3000;
}
// if (bIsStationary)
// {
//Draw a red line if we're going to do the expensive test.
// grcDebugDraw::Line(pMyVeh->GetVehiclePosition(), pVeh->GetVehiclePosition(), Color_orange, Color_red);
// }
//B* 2076557; this is causing player driver to almost never be considered stationary so cars won't avoid it when stationary
//slowest: iterate through our nodelist and see if there are any red lights
// if (bIsStationary && iJunctionCommand != JUNCTION_COMMAND_NOT_ON_JUNCTION && CVehicleJunctionHelper::ApproachingRedLight(pMyVeh))
// {
// bIsStationary = false;
//
// pVehNonConstHack->GetIntelligence()->LastTimeNotStuck = fwTimer::GetTimeInMilliseconds() - 3000;
// }
return bIsStationary;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsFirstCarBestOneToGo
// PURPOSE : Given the positions of these 2 vehicles that are facing each other this function
// returns true if the first one is the best candidate to go.
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsFirstCarBestOneToGo(const CVehicle* const pVeh, const CVehicle* const pOtherVeh
, const bool bFirstCarIsAlreadyGoing)
{
//never try and go around a car we're forced to stop for.
if (pOtherVeh->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle
|| pOtherVeh->m_nVehicleFlags.bIsAmbulanceOnDuty
|| pOtherVeh->m_nVehicleFlags.bIsFireTruckOnDuty)
{
return false;
}
if (pOtherVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_LIGHTS
|| pOtherVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_TRAFFIC)
{
return true;
}
//if pOtherVeh is following pVeh, pVeh shouldn't ever wait for it, because if they're both deadlocked,
//pOtherVeh's desired velocity is probably 0 and it probably won't ever move.
//actually figuring out his desired velocity would require a bit of recursion, so we can just
//make use of the avoidance cache to see who's stopping for whom
const CPhysical* pOtherAvoidanceTarget = pOtherVeh->GetIntelligence()->GetAvoidanceCache().m_pTarget;
if (pOtherAvoidanceTarget && pOtherAvoidanceTarget == pVeh)
{
return true;
}
// We need to find out what the distance is of each cars' centre point to the centre line of the other.
Vec3V diff = pOtherVeh->GetVehiclePosition() - pVeh->GetVehiclePosition();
// float lengthAlong1 = diff.Dot(pVeh->GetB());
ScalarV lengthAlong1(Dot(diff, pVeh->GetVehicleForwardDirection()));
// float lengthAlong2 = -diff.Dot(pOtherVeh->GetB());
ScalarV lengthAlong2(-Dot(diff, pOtherVeh->GetVehicleForwardDirection()));
if (bFirstCarIsAlreadyGoing)
{
static const ScalarV scBonusForAlreadyAvoiding(V_ONE);
lengthAlong1 -= scBonusForAlreadyAvoiding;
}
// return (lengthAlong1 < lengthAlong2);
return (IsLessThanAll(lengthAlong1, lengthAlong2)) != 0;
}
// bool CTaskVehicleGoToPointWithAvoidanceAutomobile::CanSwitchLanesToAvoidObstruction(const CVehicle* const pVeh, const CPhysical* const pAvoidEnt, const float fDesiredSpeed)
// {
// TUNE_GROUP_BOOL(CAR_AI, EnableLaneChanges, false);
// if (!EnableLaneChanges)
// {
// return false;
// }
// if (!IsDrivingFlagSet(DF_ChangeLanesAroundObstructions))
// {
// return false;
// }
//
// const CPed* pDriver = pVeh->GetDriver();
// const bool bIsMission = pVeh->PopTypeIsMission() || pDriver && pDriver->PopTypeIsMission();
// const bool bIsAggressiveEnough = CDriverPersonality::WillChangeLanes(pDriver);
// if (!bIsMission && !bIsAggressiveEnough)
// {
// return false;
// }
//
// if (!pAvoidEnt)
// {
// return false;
// }
//
// const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
// if (!pFollowRoute)
// {
// return false;
// }
//
// //if there's only one lane, we can't change lanes
// const int iCurrentPoint = pFollowRoute->GetCurrentTargetPoint();
// Assert(iCurrentPoint > 0 && iCurrentPoint < pFollowRoute->GetNumPoints());
// const CRoutePoint* pTargetPoint = &pFollowRoute->GetRoutePoints()[iCurrentPoint];
// //CRoutePoint* pCurrentPoint = &pFollowRoute->GetRoutePoints()[iCurrentPoint-1];
//
// if (pTargetPoint->m_iNoLanesThisSide <= 1)
// {
// return false;
// }
//
// if (!ShouldChangeLanesForThisEntity(pVeh, pAvoidEnt, fDesiredSpeed, IsDrivingFlagSet(DF_DriveInReverse)))
// {
// return false;
// }
//
// const int iCurrentLane = (int)Floorf(pTargetPoint->GetLane().Getf());
// if (iCurrentLane < pTargetPoint->m_iNoLanesThisSide - 1)
// {
// m_bWaitingToUndertakeThisFrame = true;
// m_TimeWaitingForUndertake += fwTimer::GetTimeStepInMilliseconds();
// m_TimeWaitingForUndertake = Min(m_TimeWaitingForUndertake, ms_TimeBeforeUndertake);
// }
//
// if (iCurrentLane > 0)
// {
// m_bWaitingToOvertakeThisFrame = true;
// m_TimeWaitingForOvertake += fwTimer::GetTimeStepInMilliseconds();
// m_TimeWaitingForOvertake = Min(m_TimeWaitingForOvertake, ms_TimeBeforeOvertake);
// }
//
// return m_bWaitingToOvertakeThisFrame && m_TimeWaitingForOvertake >= ms_TimeBeforeOvertake
// || m_bWaitingToUndertakeThisFrame && m_TimeWaitingForUndertake >= ms_TimeBeforeUndertake;
// }
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsThereRoomToOvertakeOtherVehicle
// PURPOSE : Does a check to make sure there isn't any oncoming traffic in the
// way if we want to overtake this car.
// The car is probably a parked car.
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsThereRoomToOvertakeVehicle(const CVehicle* const pVeh, const CVehicle* const pOtherVeh, const bool bOnlyWaitForCarsMovingInOppositeDirection) const
{
#define OVERTAKE_MARGIN 0.1f
// First we identify the area that needs to be clear of oncoming traffic.
// Find the width of our car.
float VehHalfWidth = pVeh->GetBoundingBoxMax().x;
const Vector3 vOtherBoundingBoxMax = pOtherVeh->GetBoundingBoxMax();
float LineOffsetFromVehToOvertake = vOtherBoundingBoxMax.x;
bool bGetTravelDirFromVehicle = true;
// Find the direction we are travelling in.
// We take the forward vector of the parked car.
Vector3 TravelDir, vSide, vSideRightSeenFromUs;
#if 0
TUNE_GROUP_BOOL(ASDF, EnableNewPlayerOvertakeDirTest, false);
CPed* pOtherDriver = pOtherVeh->GetDriver();
if (EnableNewPlayerOvertakeDirTest && pOtherDriver && pOtherDriver->IsLocalPlayer())
{
//assume we'll get this info from the link
bGetTravelDirFromVehicle = false;
const CPlayerInfo* pPlayerInfo = pOtherDriver->GetPlayerInfo();
Assert(pPlayerInfo);
const CPathNode* pPlayerNode = ThePaths.FindNodePointerSafe(pPlayerInfo->GetPlayerDataNodeAddress());
if (!pPlayerNode)
{
bGetTravelDirFromVehicle = true;
}
const CPathNodeLink* pLink = &ThePaths.GetNodesLink(pPlayerNode, pPlayerInfo->GetPlayerDataLocalLinkIndex());
if (pLink)
{
const CPathNode* pOtherNode = ThePaths.FindNodePointerSafe(pLink->m_OtherNode);
if (pOtherNode)
{
TravelDir = pOtherNode->GetPos() - pPlayerNode->GetPos();
TravelDir.z = 0.0f;
TravelDir.Normalize();
vSide = Vector3(TravelDir.y, -TravelDir.x, 0.0f);
vSideRightSeenFromUs = vSide;
const float fDotCarRight = fabsf(vSide.Dot(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleRightDirection())));
const float fDotCarFwd = fabsf(vSide.Dot(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection())));
//do some stupid cheap blending instead of getting the half length of the vehicle's
//bounding box on an arbitrary axis
LineOffsetFromVehToOvertake = fDotCarRight * vOtherBoundingBoxMax.x + fDotCarFwd * fDotCarFwd;
}
else
{
bGetTravelDirFromVehicle = true;
}
}
else
{
bGetTravelDirFromVehicle = true;
}
}
#endif //0
if (IsThisAParkedCar(pOtherVeh))
{
//for parked cars, just use their direction since they're spawned
//parallel from the road
bGetTravelDirFromVehicle = true;
}
else
{
//otherwise use the direction of our path node at that point
//the followroute should be cached already here so this should be a cheap lookup
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
if (pFollowRoute)
{
s16 iClosestPointToPosition = pFollowRoute->FindClosestPointToPosition(pOtherVeh->GetVehiclePosition()
, rage::Max(0, pFollowRoute->GetCurrentTargetPoint()-1));
if (iClosestPointToPosition >= 0 && pFollowRoute->GetNumPoints() > 1)
{
if (iClosestPointToPosition < pFollowRoute->GetNumPoints()-1)
{
TravelDir = VEC3V_TO_VECTOR3(pFollowRoute->GetRoutePoints()[iClosestPointToPosition+1].GetPosition()
- pFollowRoute->GetRoutePoints()[iClosestPointToPosition].GetPosition());
}
else
{
Assert(iClosestPointToPosition > 0);
TravelDir = VEC3V_TO_VECTOR3(pFollowRoute->GetRoutePoints()[iClosestPointToPosition].GetPosition()
- pFollowRoute->GetRoutePoints()[iClosestPointToPosition-1].GetPosition());
}
TravelDir.z = 0.0f;
TravelDir.Normalize();
vSide = Vector3(TravelDir.y, -TravelDir.x, 0.0f);
vSideRightSeenFromUs = vSide;
const float fDotCarRight = fabsf(vSide.Dot(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleRightDirection())));
const float fDotCarFwd = fabsf(vSide.Dot(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection())));
//do some stupid cheap blending instead of getting the half length of the vehicle's
//bounding box on an arbitrary axis
LineOffsetFromVehToOvertake = fDotCarRight * vOtherBoundingBoxMax.x + fDotCarFwd * fDotCarFwd;
bGetTravelDirFromVehicle = false;
}
}
}
if (bGetTravelDirFromVehicle)
{
const float VehToOvertakeHalfWidth = vOtherBoundingBoxMax.x;
LineOffsetFromVehToOvertake = VehToOvertakeHalfWidth;
TravelDir = (VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection()));
TravelDir.z = 0.0f;
TravelDir.Normalize();
vSide = (VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleRightDirection()));
vSide.z = 0.0f;
vSide.Normalize();
vSideRightSeenFromUs = vSide;
// wipavoidance
// Make sure it is pointing away from us.
if(DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()), TravelDir) < 0.0f)
{
TravelDir = -TravelDir;
vSideRightSeenFromUs = -vSideRightSeenFromUs;
}
}
//CVehicle::ms_debugDraw.AddArrow(pOtherVeh->GetVehiclePosition(), pOtherVeh->GetVehiclePosition() + VECTOR3_TO_VEC3V(vSideRightSeenFromUs) * ScalarV(V_FIVE)
// , 0.25f, Color_orange);
// The centre point of the line is to the left side of the parked car.
Vector3 LineBase = VEC3V_TO_VECTOR3(pOtherVeh->GetVehiclePosition());
float Offset = VehHalfWidth + LineOffsetFromVehToOvertake + OVERTAKE_MARGIN;
// If the avoidance data is set up(parked car) we HAVE to go round the car a certain direction.
// otherwise we have a choice.
// if(IsThisAParkedCar(pOtherVeh))
Vec3V vFlatDelta = pOtherVeh->GetVehiclePosition()-pVeh->GetVehiclePosition();
vFlatDelta.SetZ(ScalarV(V_ZERO));
if(IsLessThanAll(Dot(pVeh->GetVehicleRightDirection(), (vFlatDelta)), ScalarV(V_ZERO)))
{ // Going on right hand side of target car.
vSide = vSideRightSeenFromUs;
}
else
{
vSide = -vSideRightSeenFromUs;
}
LineBase += Offset * vSide;
// Find out how much we have to move the linebase back.
//Back is expected to be negative
float Back =Dot((pVeh->GetVehiclePosition() - pOtherVeh->GetVehiclePosition()), RCC_VEC3V(TravelDir)).Getf();
float Forward = 15.0f; // Look 15 meters ahead.
LineBase += Back * TravelDir;
Vector3 LineEnd = LineBase + TravelDir *(Forward - Back);
// Now LineBase is the start of the line to test and LineEnd is the end.
const Vector3 vecVehPosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
Assert(pEntity->GetIsTypeVehicle());
CVehicle *pVehTraffic =(CVehicle *)pEntity;
if( !pVehTraffic ||
pVehTraffic==pVeh ||
pVehTraffic==pOtherVeh ||
IsThisAStationaryCar(pVehTraffic))
{
continue;
}
// Make sure this vehicle is in the right z range
const Vector3 vecVehTrafficPosition = VEC3V_TO_VECTOR3(pVehTraffic->GetVehiclePosition());
if(rage::Abs(vecVehPosition.z - vecVehTrafficPosition.z) >= 4.0f)
{
continue;
}
// Make sure this vehicle is traveling towards us
if(bOnlyWaitForCarsMovingInOppositeDirection && TravelDir.Dot(pVehTraffic->GetAiVelocity()) >= -0.2f)
{
continue;
}
// Now test whether this vehicle would actually block our path.
float DistToTravelLine = fwGeom::DistToLine(LineBase, LineEnd, vecVehTrafficPosition);
if(DistToTravelLine >= VehHalfWidth + pVehTraffic->GetBoundingBoxMax().x + OVERTAKE_MARGIN)
{
continue;
}
// Our path is blocked. Wait behind parked car for a bit.
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(LineBase, LineEnd, Color32(255, 0, 0, 255));
}
#endif //__DEV
return false;
}
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(LineBase, LineEnd, Color32(0, 255, 0, 255));
}
#endif //__DEV
return true;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldAllowTimeslicingWhenAvoiding() const
{
const CVehicle& veh = *GetVehicle();
// If we are standing still and are stopping for traffic and were already
// set to use aggressive timeslicing, then continue to allow timeslicing.
bool exceptionToStillAllow = m_bStoppingForTraffic
&& veh.m_nVehicleFlags.bLodShouldUseTimeslicing
&& veh.GetAiXYSpeed() < 0.5f;
return exceptionToStillAllow;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ShouldGiveWarning
// PURPOSE : We may consider flashing headlights at the other car
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldGiveWarning(const CVehicle* const pVeh, const CVehicle* const pOtherVeh) const
{
static dev_float s_fAbilityThresholdForGiveWarning = 0.2f; //80% of drivers give warning
if(!pOtherVeh->GetDriver() || !pOtherVeh->GetDriver()->IsPlayer()){return false;}
//don't flash headlights if we aren't driving recklessly
if (!pOtherVeh->m_nVehicleFlags.bShouldBeBeepedAt && !pOtherVeh->m_nVehicleFlags.bPlayerDrivingAgainstTraffic){ return false;}
if(!pVeh->GetDriver() || pVeh->GetDriver()->GetPedType() == PEDTYPE_COP){return false;}
// random seed can't be applied before the charge event, there shouldn't be any randomization for it
if(CDriverPersonality::FindDriverAbility(pVeh->GetDriver(), pVeh) < s_fAbilityThresholdForGiveWarning) {return false;}
if(pOtherVeh->GetAiVelocity().Mag2() <= 81.0f) {return false;}
if(pVeh->GetAiXYSpeed() <= 1.0f) {return false;}
if(Dot(pVeh->GetVehicleForwardDirection(), pOtherVeh->GetVehicleForwardDirection()).Getf() >= -0.93f) {return false;}
// Make sure cars are 'head on' close to each others center lines.
if(IsGreaterThanOrEqualAll(Dot(pOtherVeh->GetVehicleRightDirection(), (pVeh->GetVehiclePosition() - pOtherVeh->GetVehiclePosition())), ScalarV(V_TWO))) {return false;}
//if(pOtherVeh->GetIntelligence()->m_bDontSwerveForUs) {return false;}
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : InConvoyTogether()
// PURPOSE : Processes one sector pedList.
// RETURNS : True if we are in a convoy together.
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::InConvoyTogether(const CVehicle* const pVeh, const CVehicle* const pOtherVeh)
{
if(GetParent() && GetParent()->GetTaskType() == CTaskTypes::TASK_VEHICLE_ESCORT)
{
CTaskVehicleEscort *pEscortMission = static_cast<CTaskVehicleEscort*>(GetParent());
return pEscortMission->InConvoyTogether(pVeh, pOtherVeh);
}
else
{
//Not escorting so not in a convoy
return false;
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SetWaitingForPlayerPed(bool bWaitingForPlayerPed)
{
m_bWaitingForPlayerPed = bWaitingForPlayerPed;
//we might get in here after we've already been stopped for the player,
//so don't allow this to set the time to a later timestamp
//if this is a continuous stoppage
if (m_bWaitingForPlayerPed && m_startTimeWaitingForPlayerPed == 0)
{
m_startTimeWaitingForPlayerPed = fwTimer::GetTimeInMilliseconds();
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindPlayerNearMissTargets(const CVehicle* pPlayerVehicle, CPlayerInfo* playerInfo, bool trackPreciseNearMiss /* = false */)
{
Assertf(pPlayerVehicle->GetDriver() && pPlayerVehicle->GetDriver()->IsPlayer(), "Near miss vehicle doesn't have player driver");
const Vector3 ourPosition = VEC3V_TO_VECTOR3(pPlayerVehicle->GetVehiclePosition());
CEntityScannerIterator entityList = pPlayerVehicle->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
Assert(pEntity->GetIsTypeVehicle());
CVehicle *pVehObstacle =(CVehicle *)pEntity;
Vector3 otherVehCentre = VEC3V_TO_VECTOR3(pVehObstacle->GetVehiclePosition());
float fDist = otherVehCentre.Dist2(ourPosition);
if (fDist > 30.0f*30.0f || ABS(otherVehCentre.z - ourPosition.z) > 5.0f)
{
continue;
}
playerInfo->NearVehicleMiss(pPlayerVehicle, pVehObstacle, trackPreciseNearMiss);
}
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldIgnoreNonDirtyVehicle(const CVehicle& rVeh, const CVehicle& rOtherVeh, const bool bDriveInReverse, const bool bOnSingleTrackRoad) const
{
//TUNE_GROUP_BOOL(CAR_AI, EnableDirtyFlagRejection, true);
static dev_bool bEnableDirtyFlagRejection = true;
if (!bEnableDirtyFlagRejection)
{
return false;
}
//if I'm dirty, I better be careful and avoid everyone else
if (rVeh.m_nVehicleFlags.bAvoidanceDirtyFlag)
{
return false;
}
//if I'm on a single track road, I'll tend to encounter other vehicles
//facing me, so don't ignore them here
if (bOnSingleTrackRoad)
{
return false;
}
//parked and stationary cars are always considered dirty
if (rOtherVeh.m_nVehicleFlags.bAvoidanceDirtyFlag || rOtherVeh.m_nVehicleFlags.bIsOvertakingStationaryCar || IsThisAParkedCar(&rOtherVeh))
{
return false;
}
//treat everyone facing the same direction as us the same as before
static const ScalarV scZero(V_ZERO);
if (IsGreaterThanAll(Dot(CVehicleFollowRouteHelper::GetVehicleDriveDir(&rVeh, bDriveInReverse).GetXY(), rOtherVeh.GetVehicleForwardDirection().GetXY()), scZero))
{
return false;
}
//if the other guy is in a junction, he should be treated as dirty,
//but we don't actually set the dirty flag for him because we don't want those
//vehicles doing avoidance on waiting traffic by returning early above
//for having their dirty flag set when it's their turn to do avoidance.
//so, we evaluate that here
if (rOtherVeh.GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO)
{
const CJunction* pOtherJn = rOtherVeh.GetIntelligence()->GetJunction() ? rOtherVeh.GetIntelligence()->GetJunction() : rOtherVeh.GetIntelligence()->GetPreviousJunction();
const bool bOtherJunctionIsFake = !pOtherJn || pOtherJn->IsOnlyJunctionBecauseHasSwitchedOffEntrances();
if(!bOtherJunctionIsFake)
{
return false;
}
}
//parked and stationary cars are always considered dirty
if (IsThisAStationaryCar(&rOtherVeh) || rOtherVeh.m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle)
{
return false;
}
//always consider player driven clones as dirty; who knows what they'll do
if(rOtherVeh.IsNetworkClone() && rOtherVeh.GetDriver() && rOtherVeh.GetDriver()->IsPlayer())
{
return false;
}
#if __BANK
if (CVehicleIntelligence::m_bDisplayDirtyCarPilons)
{
CVehicle::ms_debugDraw.AddLine(rVeh.GetVehiclePosition(), rOtherVeh.GetVehiclePosition(), Color_yellow3);
}
#endif //__BANK
return true;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::ShouldNotAvoidVehicle(const CVehicle& rVeh, const CVehicle& rOtherVeh)
{
//Grab the avoidance target.
const CVehicleIntelligence::AvoidanceCache& rCache = rVeh.GetIntelligence()->GetAvoidanceCache();
const CEntity* pTarget = rCache.m_pTarget;
if(!pTarget)
{
return false;
}
//If this vehicle is an attached trailer, reject it based on its parent cab
const CVehicle* pOtherVehToCompare = &rOtherVeh;
if (rOtherVeh.InheritsFromTrailer() && rOtherVeh.m_nVehicleFlags.bHasParentVehicle)
{
const CVehicle* pParentCab = GetTrailerParentFromVehicle(pOtherVehToCompare);
if (pParentCab)
{
pOtherVehToCompare = pParentCab;
}
}
//Ensure the avoidance targets match.
const CVehicleIntelligence::AvoidanceCache& rOtherCache = pOtherVehToCompare->GetIntelligence()->GetAvoidanceCache();
if(pTarget != rOtherCache.m_pTarget)
{
return false;
}
//The targets match.
//Check if we are supposed to be behind the other vehicle.
//Ensure our desired offset is greater than the other vehicle's desired offset.
if(rCache.m_fDesiredOffset <= rOtherCache.m_fDesiredOffset)
{
return false;
}
//avoid if either vehicle on a small road
if(rVeh.GetIntelligence()->IsOnSmallRoad() || rOtherVeh.GetIntelligence()->IsOnSmallRoad())
{
return false;
}
//We are supposed to be behind the other vehicle.
//Check if we are actually behind them.
//Grab the vehicle position.
Vec3V vPosition = rVeh.GetVehiclePosition();
//Grab the other vehicle position.
Vec3V vOtherPosition = pOtherVehToCompare->GetVehiclePosition();
//Grab the other vehicle's velocity.
Vec3V vOtherVelocity = VECTOR3_TO_VEC3V(pOtherVehToCompare->GetAiVelocity());
//Grab the other vehicle forward.
Vec3V vOtherForward = pOtherVehToCompare->GetVehicleForwardDirection();
//Calculate the other vehicle's direction.
Vec3V vOtherDirection = NormalizeFastSafe(vOtherVelocity, vOtherForward);
//Generate a vector from the other vehicle to the vehicle.
Vec3V vOtherToVeh = Subtract(vPosition, vOtherPosition);
ScalarV fDist = MagSquared(vOtherToVeh);
if(IsLessThanAll(fDist, ScalarV(V_FOUR)) && rVeh.GetVehicleType() == VEHICLE_TYPE_BIKE)
{
return false;
}
//Ensure we are behind the other vehicle.
ScalarV scDot = Dot(vOtherDirection, vOtherToVeh);
if(IsGreaterThanAll(scDot, ScalarV(V_ZERO)))
{
return false;
}
return true;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsAnyVehicleTooCloseToTailgate(const CVehicle& rVehicle, const CVehicle& rTailgateVehicle)
{
//Generate the vehicle bounding box.
//Increase the size of the box by a small amount.
spdAABB vehicleBoundingBox = rVehicle.GetBaseModelInfo()->GetBoundingBox();
vehicleBoundingBox.SetMin(Add(vehicleBoundingBox.GetMin(), Vec3V(V_NEGONE)));
vehicleBoundingBox.SetMax(Add(vehicleBoundingBox.GetMax(), Vec3V(V_ONE)));
//Calculate the max distance.
//This is fairly arbitrary.
ScalarV scMaxDistance = Subtract(vehicleBoundingBox.GetMax().GetX(), vehicleBoundingBox.GetMin().GetX());
scMaxDistance = Scale(scMaxDistance, ScalarV(V_TWO));
ScalarV scMaxDistanceSq = Scale(scMaxDistance, scMaxDistance);
//Grab the vehicle matrix.
Mat34V mVehicle = rVehicle.GetMatrix();
//Translate the matrix one frame ahead.
//This should make vehicles slightly more responsive to vehicles in front of them, as compared to those behind them.
Vec3V vVehicleVelocity = VECTOR3_TO_VEC3V(rVehicle.GetAiVelocity());
Vec3V vVehicleOffset = Scale(vVehicleVelocity, ScalarVFromF32(GetTimeStep()));
mVehicle.SetCol3(Add(mVehicle.GetCol3(), vVehicleOffset));
//Transform the bounding box.
vehicleBoundingBox.Transform(mVehicle);
//Check if we have a cached vehicle that is too close to tailgate.
const CVehicle* pVehicleTooCloseToTailgate = m_pOptimization_pVehicleTooCloseToTailgate;
if(pVehicleTooCloseToTailgate)
{
//Check if the vehicle is still too close to tailgate.
if(IsVehicleTooCloseToTailgate(vehicleBoundingBox, *pVehicleTooCloseToTailgate))
{
return true;
}
}
//Grab the vehicle position.
Vec3V vVehiclePosition = rVehicle.GetTransform().GetPosition();
//Iterate over the vehicles.
CEntityScannerIterator entityList = rVehicle.GetIntelligence()->GetVehicleScanner().GetIterator();
for(CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext())
{
//Grab the other vehicle.
const CVehicle* pOtherVehicle = static_cast<CVehicle *>(pEntity);
//Ensure the vehicle does not match.
if(pOtherVehicle == &rVehicle)
{
continue;
}
//Ensure the tailgate vehicle does not match.
if(pOtherVehicle == &rTailgateVehicle)
{
continue;
}
//Ensure the optimization vehicle does not match (we should have already checked it above).
if(pOtherVehicle == pVehicleTooCloseToTailgate)
{
continue;
}
//Grab the other vehicle position.
Vec3V vOtherVehiclePosition = pOtherVehicle->GetTransform().GetPosition();
//Ensure the distance has not exceeded the threshold.
ScalarV scDistanceSq = DistSquared(vVehiclePosition, vOtherVehiclePosition);
if(IsGreaterThanAll(scDistanceSq, scMaxDistanceSq))
{
break;
}
//Ensure the vehicle is too close to tailgate.
if(!IsVehicleTooCloseToTailgate(vehicleBoundingBox, *pOtherVehicle))
{
continue;
}
//Set the vehicle that is too close to tailgate.
m_pOptimization_pVehicleTooCloseToTailgate = pOtherVehicle;
return true;
}
//Clear the vehicle that is too close to tailgate.
m_pOptimization_pVehicleTooCloseToTailgate = NULL;
return false;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::IsVehicleTooCloseToTailgate(const spdAABB& rVehicleBoundingBox, const CVehicle& rOtherVehicle) const
{
//Grab the other vehicle bounding box.
spdAABB otherVehicleBoundingBox;
const spdAABB& rOtherVehicleBoundingBox = rOtherVehicle.GetBoundBox(otherVehicleBoundingBox);
//Check if the bounding boxes intersect.
return (rVehicleBoundingBox.IntersectsAABB(rOtherVehicleBoundingBox));
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TailgateCarInFront(const CVehicle* pVeh, float* pMaxSpeed)
{
PF_FUNC(AI_AvoidanceTailgateCarInFront);
//Check if we are behind someone.
const CVehicle* pOtherVeh = pVeh->GetIntelligence()->GetCarWeAreBehind();
if(!pOtherVeh)
{
return false;
}
//Grab the driver.
CPed* pDriver = pVeh->GetDriver();
if(pDriver && pDriver->PopTypeIsMission())
{
//If the driver is a mission ped, do not tailgate. It is causing too many
//issues with missions and for now it is best to promote stability.
return false;
}
//Check if we have exceeded the max tailgate distance.
float fDistSq = pVeh->GetIntelligence()->GetDistanceBehindCarSq();
float fMaxDistSq = sm_Tunables.m_TailgateDistanceMaxSq;
if(fDistSq > fMaxDistSq)
{
return false;
}
//Generate the magnitude of the other vehicle's velocity.
float fOtherMag = pOtherVeh->GetAiXYSpeed();
//Check if the other magnitude has exceeded the min tailgate velocity.
if(fOtherMag < sm_Tunables.m_TailgateVelocityMin)
{
return false;
}
//Check if the player is closer than tailgate distance. Otherwise, we don't want to tailgate
const CPed* pPlayer = CGameWorld::FindLocalPlayer();
if (pPlayer)
{
const Vector3 vPlayerPos = VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition());
const Vector3 vOurPos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
if ((vPlayerPos - vOurPos).XYMag2() < fMaxDistSq)
{
return false;
}
//also test the player's vehicle, since sometimes players like to park their car in the middle
//of the street and walk away like an asshole
const CVehicle* pPlayerVeh = pPlayer->GetMyVehicle();
if (pPlayerVeh)
{
const Vector3 vPlayerVehPos = VEC3V_TO_VECTOR3(pPlayerVeh->GetVehiclePosition());
if ((vPlayerVehPos - vOurPos).XYMag2() < fMaxDistSq)
{
return false;
}
}
}
//don't tailgate in junctions, I've seen too many cases of vehicles hitting each other
//when turning across traffic
if (pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO
&& pVeh->GetIntelligence()->GetJunction()
&& !pVeh->GetIntelligence()->GetJunction()->IsOnlyJunctionBecauseHasSwitchedOffEntrances()
&& pVeh->GetIntelligence()->GetJunction()->GetNumEntrances() > 1)
{
return false;
}
//Check if we are too close to another vehicle to tailgate.
if(IsAnyVehicleTooCloseToTailgate(*pVeh, *pOtherVeh))
{
return false;
}
#if __BANK
if(CVehicleIntelligence::m_bDisplayDebugTailgate && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vec3V vPos = pVeh->GetTransform().GetPosition();
vPos.SetZf(vPos.GetZf() + pVeh->GetBoundingBoxMax().z + 0.5f);
grcDebugDraw::Sphere(vPos, 0.25f, Color_green);
}
#endif
//Calculate the ideal distance based on how fast the tailgated vehicle is traveling.
float fIdealDistSq = rage::square(fOtherMag);
const float fExtraStoppingDistanceFromPersonality = CDriverPersonality::GetExtraStoppingDistanceForTailgating(pDriver, pVeh);
fIdealDistSq = rage::Clamp(fIdealDistSq
, sm_Tunables.m_TailgateIdealDistanceMinSq + fExtraStoppingDistanceFromPersonality
, sm_Tunables.m_TailgateIdealDistanceMaxSq + fExtraStoppingDistanceFromPersonality);
//Calculate the speed multiplier.
float fMult = (fDistSq / fIdealDistSq);
const float fExtraSpeedMultiplierFromPersonality = CDriverPersonality::GetExtraSpeedMultiplierForTailgating(pDriver, pVeh);
fMult = rage::Clamp(fMult
, sm_Tunables.m_TailgateSpeedMultiplierMin - fExtraSpeedMultiplierFromPersonality
, sm_Tunables.m_TailgateSpeedMultiplierMax + fExtraSpeedMultiplierFromPersonality);
//Assign the speed.
*pMaxSpeed = rage::Min(*pMaxSpeed, fOtherMag * fMult);
//tell our vehicle intelligence we aren't stuck
if (*pMaxSpeed <= 1.0f)
{
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
}
//Tailgating was successful.
return true;
}
void HelperTellOtherVehicleToHurry(CVehicle* pOtherVeh, CPed* pDriver)
{
if (pOtherVeh->m_nVehicleFlags.bToldToGetOutOfTheWay)
{
return;
}
//don't do this for mission vehicles
if (pOtherVeh->PopTypeIsMission() || (pDriver && pDriver->PopTypeIsMission()))
{
return;
}
pOtherVeh->m_nVehicleFlags.bToldToGetOutOfTheWay = true;
if (pDriver)
{
pDriver->GetPedIntelligence()->SetDriverAggressivenessOverride(1.0f);
}
else
{
pOtherVeh->GetIntelligence()->SetPretendOccupantAggressivness(1.0f);
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::MakeWayForCarTellingOthersToFlee(CVehicle* pVehicleToFlee)
{
Assert(pVehicleToFlee);
static const ScalarV scTwentyFive(25.0f);
static const ScalarV scZero(V_ZERO);
const Vec3V vehDriveDir = CVehicleFollowRouteHelper::GetVehicleDriveDir(pVehicleToFlee, IsDrivingFlagSet(DF_DriveInReverse));
CEntityScannerIterator entityList = pVehicleToFlee->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
CVehicle* pVehicle = (CVehicle*)pEntity;
if( pVehicle && (pVehicle->InheritsFromAutomobile() || pVehicle->InheritsFromBike()) )
{
//test if the other car is relatively right in front of the car to flee
const Vec3V vUsToThem = pVehicle->GetVehiclePosition() - CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicleToFlee, IsDrivingFlagSet(DF_DriveInReverse));
const ScalarV scRightOffset = Dot(pVehicleToFlee->GetVehicleRightDirection(), vUsToThem);
const ScalarV scFrontOffset = Dot(vehDriveDir, vUsToThem);
const ScalarV scFrontThreshold = scTwentyFive;
const ScalarV scSideThreshold = ScalarV(pVehicleToFlee->GetBoundingBoxMax().x + 1.0f);
if (IsGreaterThanAll(Dot(vehDriveDir, pVehicle->GetVehicleForwardDirection()), scZero)
&& IsLessThanAll(Abs(scRightOffset), scSideThreshold)
&& IsLessThanAll(scFrontOffset, scFrontThreshold)
&& IsGreaterThanAll(scFrontOffset, scZero)
)
{
HelperTellOtherVehicleToHurry(pVehicle, pVehicle->GetDriver());
}
}
}
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::StartOvertakeCurrentCar()
{
m_pVehicleToIgnoreWhenStopping = m_pOptimization_pLastCarWeSlowedFor;
if (m_bReverseBeforeOvertakeCurrentCarRequested && GetVehicle())
{
GetVehicle()->GetIntelligence()->MillisecondsNotMoving = 5000; //pretend we're stuck
SetState(State_ThreePointTurn);
//return true for a state change
return true;
}
return false;
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::MakeWayForCarWithSiren(CVehicle *pVehicleWithSiren)
{
Assert(pVehicleWithSiren);
//CVehicle::Pool *VehiclePool = CVehicle::GetPool();
//s32 i=VehiclePool->GetSize();
float Distance, DotProd, SirenCarSpeed, DotProd2;
float SirenCarForwardX, SirenCarForwardY;
float DeltaX, DeltaY, CarSpeedX, CarSpeedY, CarSpeed;
//we sometimes call this from the player control update, which happens while
//the vehicle cached ai data is invalid, so we can't use it here
Vector3 vVelocityOfSirenCar = pVehicleWithSiren->GetVelocity();
SirenCarSpeed = vVelocityOfSirenCar.Mag();
if (SirenCarSpeed < 0.1f) return; // Have to go above a certain speed.
SirenCarForwardX = vVelocityOfSirenCar.x;
SirenCarForwardY = vVelocityOfSirenCar.y;
SirenCarForwardX /= SirenCarSpeed;
SirenCarForwardY /= SirenCarSpeed;
const bool bSirenCarDriverIsPlayer = pVehicleWithSiren->GetDriver() && pVehicleWithSiren->GetDriver()->IsPlayer();
//while(i--)
CEntityScannerIterator entityList = pVehicleWithSiren->GetIntelligence()->GetVehicleScanner().GetIterator();
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
CVehicle* pVehicle = (CVehicle*)pEntity;
CTask* pAlreadyInPursuit = pVehicle->GetDriver() ? pVehicle->GetDriver()->GetPedIntelligence()->FindTaskByType(CTaskTypes::TASK_REACT_TO_PURSUIT) : NULL;
if( pVehicle && (pVehicle->InheritsFromAutomobile() || pVehicle->InheritsFromBike()) )
{
//check car and driver (police can commandeer civilian cars)
bool bLawVehicleOrPed = pVehicle->IsLawEnforcementVehicle() || ( pVehicle->GetDriver() && pVehicle->GetDriver()->IsLawEnforcementPed() );
if (!bLawVehicleOrPed && pVehicle->PopTypeIsRandom() && !pAlreadyInPursuit && pVehicle->GetStatus() == STATUS_PHYSICS &&
(pVehicle->GetDriver() == NULL || pVehicle->GetDriver()->PopTypeIsRandom()) && // Chis' vigilante mission has random cars driven by mission chars
(pVehicle!=pVehicleWithSiren) && (!pVehicle->m_nVehicleFlags.bIsAmbulanceOnDuty)
&& (!pVehicle->m_nVehicleFlags.bIsFireTruckOnDuty) && (!pVehicle->m_nVehicleFlags.bMadDriver) // mad drivers need to ignore sirens or it fucks up the police chases
/*&& !pVehicle->m_nVehicleFlags.bUsingPretendOccupants*/ && !pVehicle->m_nVehicleFlags.bActAsIfHasSirenOn)
{
if ( ABS(pVehicleWithSiren->GetVehiclePosition().GetZf() - pVehicle->GetVehiclePosition().GetZf()) < 5.0f )
{
DeltaX = pVehicle->GetVehiclePosition().GetXf() - pVehicleWithSiren->GetVehiclePosition().GetXf();
DeltaY = pVehicle->GetVehiclePosition().GetYf() - pVehicleWithSiren->GetVehiclePosition().GetYf();
Distance = rage::Sqrtf( DeltaX*DeltaX + DeltaY*DeltaY );
if (Distance < 60.0f + SirenCarSpeed)
{ // Close enough
Vector3 vOtherCarVelocity = pVehicle->GetVelocity();
CarSpeedX = vOtherCarVelocity.x;
CarSpeedY = vOtherCarVelocity.y;
CarSpeed = rage::Sqrtf(CarSpeedX * CarSpeedX + CarSpeedY * CarSpeedY);
CVehicleNodeList* pNodeList = NULL;
if (CarSpeed > 0.05f)
{
//CMission *pMission = CCarIntelligence::FindUberMissionForCar(pVehicle);
//VehTempActType tempAct = TEMPACT_NONE;
CTaskVehicleReactToCopSiren::ReactToCopSirenType reactType = CTaskVehicleReactToCopSiren::React_JustWait;
u32 nTempActDuration = 0;
CTaskVehicleMissionBase* pActiveTask = pVehicle->GetIntelligence()->GetActiveTask();
const bool bAlreadyPullingOver = pActiveTask &&
(pActiveTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_PULL_OVER || pActiveTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_PARK_NEW);
const bool bAlreadyBrakingOrWaiting = pActiveTask
&& (pActiveTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_WAIT
||pActiveTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_BRAKE);
if (pActiveTask && pActiveTask->GetTaskType() != CTaskTypes::TASK_VEHICLE_FLEE) //don't do for parked/stopped/empty cars
{
bool bGiveNewTask = false;
// Make sure the other car is in front of the sirened car
DotProd = (SirenCarForwardX * DeltaX + SirenCarForwardY * DeltaY) / Distance;
if (DotProd > 0.8f && (!pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG))) // Car is indeed ahead of the car with siren
{ // Right in front of us.
// If the car is driving away from us he swirves left or right
DotProd2 = SirenCarForwardX * pVehicle->GetVehicleForwardDirection().GetXf() +
SirenCarForwardY * pVehicle->GetVehicleForwardDirection().GetYf();
if (DotProd2 > 0.7f || DotProd2 < -0.9f)
{ // Swerve
if (!bAlreadyPullingOver)
{ // Work out whether to swirve left or right
if (DeltaX * SirenCarForwardY - DeltaY * SirenCarForwardX > 0.0f)
{
//tempAct = TEMPACT_SWERVERIGHT_STOP;
reactType = CTaskVehicleReactToCopSiren::React_PullOverRight;
}
else
{
//tempAct = TEMPACT_SWERVELEFT_STOP;
reactType = CTaskVehicleReactToCopSiren::React_PullOverLeft;
}
// Swap them round if the car is coming towards us.
if (DotProd2 < 0.0f)
{
//for AI-controlled vehicles, don't swerve for oncoming sirens,
//just stop. it looks weird when the player sees ambient traffic do this
if (!bSirenCarDriverIsPlayer)
{
reactType = CTaskVehicleReactToCopSiren::React_JustWait;
}
else if (reactType == CTaskVehicleReactToCopSiren::React_PullOverRight)
{
reactType = CTaskVehicleReactToCopSiren::React_PullOverLeft;
//tempAct = TEMPACT_SWERVELEFT_STOP;
}
else
{
reactType = CTaskVehicleReactToCopSiren::React_PullOverRight;
//tempAct = TEMPACT_SWERVERIGHT_STOP;
}
}
pNodeList = pVehicle->GetIntelligence()->GetNodeList();
bool bInLeftLane = false;
bool bHasOpposingLaneOfTraffic = false;
bool bHasShoulderOnLeft = true;
if (pNodeList && reactType == CTaskVehicleReactToCopSiren::React_PullOverLeft )
{
s32 iTargetNodeIndex = pNodeList->GetTargetNodeIndex();
s32 iSrcNodeIndex = iTargetNodeIndex;
s32 iLinkIndex = pNodeList->GetPathLinkIndex(iSrcNodeIndex);
if (iLinkIndex < 0 && iSrcNodeIndex > 0)
{
iSrcNodeIndex = iTargetNodeIndex-1;
iLinkIndex = pNodeList->GetPathLinkIndex(iSrcNodeIndex);
}
if (iLinkIndex >= 0)
{
const CNodeAddress& iSrcNodeAddr = pNodeList->GetPathNodeAddr(iSrcNodeIndex);
const u32 iSrcNodeRegion = iSrcNodeAddr.GetRegion();
if (!iSrcNodeAddr.IsEmpty() && ThePaths.IsRegionLoaded(iSrcNodeRegion))
{
CPathNodeLink* pLink = ThePaths.FindLinkPointerSafe(iSrcNodeRegion,iLinkIndex);
if (aiVerify(pLink))
{
bHasOpposingLaneOfTraffic = pLink->m_1.m_LanesFromOtherNode > 0;
bInLeftLane = pNodeList->GetPathLaneIndex(iSrcNodeIndex) == 0;
//TODO: do something to set bHasSHoulderOnLeft
}
}
}
}
// Don't ever swirve left. It confuses the player.
// Unless we're already in the left lane and don't have any traffic coming
// in the other direction
if (reactType == CTaskVehicleReactToCopSiren::React_PullOverLeft && !(bInLeftLane && !bHasOpposingLaneOfTraffic && bHasShoulderOnLeft))
{
//tempAct = TEMPACT_WAIT;
reactType = CTaskVehicleReactToCopSiren::React_JustWait;
}
nTempActDuration = 6000;
bGiveNewTask = true;
}
}
else
{ // Only if this guy heads in our direction will we make him stop
if ( vOtherCarVelocity.x*DeltaX + vOtherCarVelocity.y*DeltaY < 0.0f)
{
if (!bAlreadyBrakingOrWaiting)
{
reactType = CTaskVehicleReactToCopSiren::React_JustWait;
nTempActDuration = 6000;
bGiveNewTask = true;
}
}
}
}
else
{ // Only if this guy heads in our direction will we make him stop
if ( vOtherCarVelocity.x*DeltaX + vOtherCarVelocity.y*DeltaY < 0.0f)
{
if (!bAlreadyBrakingOrWaiting)
{
reactType = CTaskVehicleReactToCopSiren::React_JustWait;
nTempActDuration = 3000;
bGiveNewTask = true;
}
}
}
if (bGiveNewTask)
{
//aiTask* pTask = reactType == CTaskVehicleReactToCopSiren::React_JustWait
// ? CVehicleIntelligence::GetTempActionTaskFromTempActionType(tempAct, nTempActDuration)
// : rage_new CTaskVehiclePullOver(true, pulloverDir, 4000, true);
aiTask* pTask = rage_new CTaskVehicleReactToCopSiren(reactType, nTempActDuration);
pVehicle->GetIntelligence()->AddTask(VEHICLE_TASK_TREE_PRIMARY, pTask, VEHICLE_TASK_PRIORITY_PRIMARY, false);
if (pVehicle->m_nVehicleFlags.bUsingPretendOccupants)
{
sVehicleMissionParams pullOverParams;
pVehicle->GetIntelligence()->SetPretendOccupantEventData(VehicleEventPriority::VEHICLE_EVENT_PRIORITY_RESPOND_TO_SIREN, pullOverParams);
}
//Color32 sphereColor = reactType == CTaskVehicleReactToCopSiren::React_JustWait ? Color_orange : Color_red;
//grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition()), 2.0f, sphereColor, false, 15);
}
}
}
}
}
}
}
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::UpdatePlayerOnFootIsBlockingUs(CVehicle* pVeh, CPed* pDriver, CPed* pBlockingPed, float *pMaxSpeed, const float fDistFromSideOfCar, const float fDistFromFrontOfCarBumper)
{
//this should have been set above
Assert(m_startTimeWaitingForPlayerPed > 0);
const int iTimeToWaitForPlayer = CDriverPersonality::FindDelayBeforeAcceleratingAfterObstructionGone(pDriver, pVeh, true);
const int iTimeToWaitBeforeSwearingMs = iTimeToWaitForPlayer - 1;
const int iTimeToWaitBeforeHonkingMs = (2 * iTimeToWaitForPlayer) - 1;
const int iTimeToWaitBeforePushingPedOutOfWayMs = (3 * iTimeToWaitForPlayer) - 1;
const int iTimeWaitingForPlayer = fwTimer::GetTimeInMilliseconds() - m_startTimeWaitingForPlayerPed;
const bool bPlayerIsGettingUp = pBlockingPed->GetPedIntelligence()->IsPedGettingUp();
const bool bPlayerIsInRagdoll = pBlockingPed->GetRagdollState()==RAGDOLL_STATE_PHYS;
if (bPlayerIsInRagdoll || bPlayerIsGettingUp)
{
//don't honk or anything if they're knocked over
SetState(State_TemporarilyBrake);
return;
}
//Check if we will try to push the ped out of the way.
bool bIsDriverAgitated = (pDriver && pDriver->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAgitated));
bool bWillTryToPushPedOutOfWay = !bIsDriverAgitated;
static dev_float s_fDistFromEdgeToConsiderSlightlyBlocked = 0.25f;
static dev_float s_fDistFromFrontOfCarToConsiderThreePointTurn = 2.0f;
//if the ped's only slightly blocking us, get antsy faster
bool bForceRunOverNow = false;
if (fDistFromSideOfCar < s_fDistFromEdgeToConsiderSlightlyBlocked
&& iTimeWaitingForPlayer > iTimeToWaitBeforeSwearingMs)
{
bForceRunOverNow = true;
}
if ((bWillTryToPushPedOutOfWay && (bForceRunOverNow || (iTimeWaitingForPlayer > iTimeToWaitBeforePushingPedOutOfWayMs)))
|| ((fwTimer::GetTimeInMilliseconds() - pVeh->GetIntelligence()->m_lastTimeTriedToPushPlayerPed < 30000) && pVeh->GetIntelligence()->m_lastTimeTriedToPushPlayerPed > 0))
{
//don't wait again here, we wanna push through
*pMaxSpeed = 3.0f;
m_bSlowlyPushingPlayerThisFrame = true;
pVeh->GetIntelligence()->m_lastTimeTriedToPushPlayerPed = fwTimer::GetTimeInMilliseconds();
if (fDistFromFrontOfCarBumper < s_fDistFromFrontOfCarToConsiderThreePointTurn)
{
pVeh->GetIntelligence()->MillisecondsNotMoving = 5000; //pretend we're stuck
SetState(State_ThreePointTurn);
}
}
else if (iTimeWaitingForPlayer > iTimeToWaitBeforeHonkingMs)
{
bool bWillBecomeAgitated = (pDriver && pDriver->PopTypeIsRandom() &&
pDriver->IsLawEnforcementPed() && !bIsDriverAgitated);
if(bWillBecomeAgitated)
{
//Add an agitated event.
CEventAgitated event(pBlockingPed, AT_Loitering);
pDriver->GetPedIntelligence()->AddEvent(event);
}
else
{
mthRandom rnd(pVeh->GetRandomSeed());
if(pDriver && pDriver->CheckBraveryFlags(BF_PLAY_CAR_HORN))
{
//only a small chance of flicking the player off when we honk here
const bool bRestrictDriveBy = rnd.GetFloat() < 0.05f;
HelperMaybePlayHorn(pVeh, true, true, pBlockingPed, bRestrictDriveBy);
}
else if (pDriver)
{
if (rnd.GetFloat() < 0.125f)
{
HelperFlipOffTarget(pVeh, pDriver, pBlockingPed);
}
if(!pBlockingPed->IsDead())
{
pDriver->NewSay("BLOCKED_GENERIC");
}
}
}
SetState(State_TemporarilyBrake);
}
else if (iTimeWaitingForPlayer > iTimeToWaitBeforeSwearingMs)
{
if (pDriver)
{
pDriver->NewSay("BLOCKED_GENERIC");
}
SetState(State_TemporarilyBrake);
}
else
{
SetState(State_TemporarilyBrake);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SlowCarDownForPedsSectorList
// PURPOSE : Processes one sector list.(Slows down for the cars in it)
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SlowCarDownForPedsSectorList(CVehicle* pVeh, const float MinX, const float MinY, const float MaxX, const float MaxY, float *pMaxSpeed, float OriginalMaxSpeed, bool bWasSlowlyPushingPlayer)
{
SlowCarDownForPedsSectorListHelper(this, pVeh, MinX, MinY, MaxX, MaxY, pMaxSpeed, OriginalMaxSpeed, bWasSlowlyPushingPlayer);
}
#define BIKE_WIDTH_MULTIPLIER 1.6f
#define BRAKE_DISTANCE 13.0f
#define BRAKE_DISTANCE_PLAYER 15.0f
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SlowCarDownForPedsSectorListHelper(CTaskVehicleGoToPointWithAvoidanceAutomobile* pTask,
CVehicle* pVeh, const float MinX, const float MinY, const float MaxX, const float MaxY, float *pMaxSpeed, float OriginalMaxSpeed, bool bWasSlowlyPushingPlayer)
{
//NOTE: Do not use GetAiVelocity inside of this function, it is indirectly
// called from places that are not part of the vehicle AI update!
float DotFront, DotSide, CarFrontSize, CarRearSize, CarRightSize, VelFront, VelRight;
Vector3 Delta;
if(pTask)
{
pTask->m_bSlowingDownForPed = false;
}
CPedScanner& scanner = pVeh->GetIntelligence()->GetPedScanner();
const int numEntities = scanner.GetNumEntities();
if(!numEntities)
{
return;
}
if (!aiVerifyf(pVeh->pHandling, "No vehicle handling info found for player car."))
{
return;
}
const Vector3& vBoundBoxMax = pVeh->GetBoundingBoxMax();
CarRightSize = vBoundBoxMax.x;
// make bikes a bit wider cause it might be leant over a bit
if(pVeh->InheritsFromBike())
{
CarRightSize *= BIKE_WIDTH_MULTIPLIER;
}
VelFront = DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()), pVeh->GetVelocity());
VelRight = DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()), pVeh->GetVelocity());
CPed* pDriver = pVeh->GetDriver();
TUNE_GROUP_BOOL(CAR_AI, UseNewStopDistance, true);
const float fBrakingMinDist = UseNewStopDistance ?
pVeh->GetAIHandlingInfo()->GetMaxBrakeDistance()
: Max(CDriverPersonality::GetStopDistanceForPeds(pDriver, pVeh), VelFront);
// if the player's using their horn then extend the distance forward that peds will react to avoid car
// if(pVeh->pDriver && pVeh->pDriver->IsPlayer() && pVeh->IsHornOn())
// fWarnPedMinDist *= 2.0f;
const bool bPlayingHorn = pDriver && pDriver->IsPlayer() && pVeh->IsHornOn();
const bool bHasRecentlyRunSomeoneOver = pDriver && pDriver->IsPlayer() && (CTimeHelpers::GetTimeSince(pDriver->GetPlayerInfo()->GetLastTimeBumpedPedInVehicle()) < 7500);
// Don't bother to avoid cars driving safely on rails
bool bShouldBeAvoided =
(pDriver && pDriver->IsPlayer()) ||
pVeh->InheritsFromTrain() ||
(!(pTask && pTask->IsDrivingFlagSet(DF_StopForCars))) ||
pVeh->GetStatus() == STATUS_PHYSICS ||
(pTask && pTask->IsDrivingFlagSet(DMode_PloughThrough)); // @RSGNWE-JW make sure to dodge them crazy drivers.
if(bShouldBeAvoided)
{
//Check if the vehicle is a heli.
if(pVeh->InheritsFromHeli())
{
//Check if the flat speed does not exceed the threshold.
const Vector3& vVelocity = pVeh->GetVelocity();
float fXYSpeedSq = vVelocity.XYMag2();
static dev_float s_fMinXYSpeed = 5.0f;
float fMinXYSpeedSq = square(s_fMinXYSpeed);
//if(pVeh->GetAiXYSpeed() < s_fMinXYSpeed)
if (fXYSpeedSq < fMinXYSpeedSq)
{
bShouldBeAvoided = false;
}
}
}
CarFrontSize = vBoundBoxMax.y;
CarRearSize = pVeh->GetBoundingBoxMin().y;
//Calculate the multiplier.
float fMultiplier = 1.0f;
if(pVeh->GetDrivenDangerouslyTime() > sm_Tunables.m_MinTimeToConsiderDangerousDriving)
{
fMultiplier *= sm_Tunables.m_MultiplierForDangerousDriving;
}
float fSpeedAboveMin = rage::Max(0.0f, VelFront - sm_Tunables.m_MinSpeedForAvoid);
if(VelFront < 0.0f)
fSpeedAboveMin = -VelFront;
const float fPercBetweenMinAndMax = fSpeedAboveMin /(sm_Tunables.m_MaxSpeedForAvoid - sm_Tunables.m_MinSpeedForAvoid);
float fAvoidInFrontDistance = 0.0f;
float fAvoidToSideDistance = 0.0f;
if(fSpeedAboveMin > 0.0f)
{
fAvoidInFrontDistance = sm_Tunables.m_MinDistanceForAvoid +(fPercBetweenMinAndMax *(sm_Tunables.m_MaxDistanceForAvoid - sm_Tunables.m_MinDistanceForAvoid));
fAvoidInFrontDistance *= fMultiplier;
//When driving on pavement, avoid a bit more to the side.
if(pVeh->m_nVehicleFlags.bHasWheelOnPavement && !pVeh->InheritsFromBike())
{
fAvoidToSideDistance = (fPercBetweenMinAndMax *(sm_Tunables.m_MaxDistanceToSideOnPavement - sm_Tunables.m_MinDistanceToSideOnPavement));
}
//When skidding, add some side distance based on velocity.
float fScale = (Max(0.0f, VelRight) * ms_vNmVelocityScale.x);
float fAmount = (CarRightSize * (1.0f + fScale)) - CarRightSize;
fAvoidToSideDistance += fAmount;
fAvoidToSideDistance *= fMultiplier;
}
Vector2 vAvoidXExtents (ms_vAvoidXExtents.x + CarRightSize + fAvoidToSideDistance, CarFrontSize + fAvoidInFrontDistance);
Vector2 vThreatXExtents (ms_vAvoidXExtents.x + CarRightSize, CarFrontSize + fAvoidInFrontDistance);
Vector4 vNmXExtents ( ms_vNmXExtents.x + (CarRightSize * (1 + (rage::Abs(rage::Min(0.0f,VelRight)) * ms_vNmVelocityScale.x))), //LEFT
ms_vNmXExtents.x + (CarRightSize * (1 + (rage::Max(0.0f,VelRight) * ms_vNmVelocityScale.x))), //RIGHT
CarFrontSize + rage::Min((Max(0.0f,VelFront)) * ms_vNmVelocityScale.y, sm_Tunables.m_MinDistanceForAvoid), //FRONT
rage::Abs(CarRearSize) + rage::Min(Abs(Min(0.0f,VelFront)* ms_vNmVelocityScale.y), sm_Tunables.m_MinDistanceForAvoid)); //BACK
if(VelFront < 0.0f)
{
vAvoidXExtents = Vector2(ms_vAvoidXExtents.x + CarRightSize + fAvoidToSideDistance, CarRearSize - fAvoidInFrontDistance);
vThreatXExtents = Vector2(ms_vAvoidXExtents.x + CarRightSize, CarRearSize - fAvoidInFrontDistance);
vNmXExtents = Vector4( ms_vNmXExtents.x + (CarRightSize * (1 + (rage::Abs(Min(0.0f,VelRight) * ms_vNmVelocityScale.x)))),
ms_vNmXExtents.x + (CarRightSize * (1 + (rage::Max(0.0f,VelRight) * ms_vNmVelocityScale.x))),
CarFrontSize + rage::Max((Max(0.0f,VelFront)) * ms_vNmVelocityScale.y, -sm_Tunables.m_MinDistanceForAvoid),
rage::Abs(CarRearSize) + rage::Min(rage::Abs(rage::Min(0.0f,VelFront) * ms_vNmVelocityScale.y), sm_Tunables.m_MinDistanceForAvoid));
}
float fMinSpeedForAvoidDirected = sm_Tunables.m_MinSpeedForAvoidDirected;
float fMaxSpeedForAvoidDirected = sm_Tunables.m_MaxSpeedForAvoidDirected;
float fSpeedForAvoidDirected = Clamp(Abs(VelFront), fMinSpeedForAvoidDirected, fMaxSpeedForAvoidDirected);
float fLerp = (fSpeedForAvoidDirected - fMinSpeedForAvoidDirected) / (fMaxSpeedForAvoidDirected - fMinSpeedForAvoidDirected);
float fMinDistanceForAvoidDirected = sm_Tunables.m_MinDistanceForAvoidDirected;
float fMaxDistanceForAvoidDirected = sm_Tunables.m_MaxDistanceForAvoidDirected;
float fDistanceForAvoidDirected = Lerp(fLerp, fMinDistanceForAvoidDirected, fMaxDistanceForAvoidDirected);
fDistanceForAvoidDirected *= fMultiplier;
Vector2 vAvoidDirectedExtents = vAvoidXExtents;
vAvoidDirectedExtents.x += fDistanceForAvoidDirected;
vAvoidDirectedExtents.y = (VelFront >= 0.0f) ? CarFrontSize + fDistanceForAvoidDirected : CarRearSize - fDistanceForAvoidDirected;
#if __DEV
TUNE_GROUP_BOOL(CAR_AI, RenderAvoidanceWarnings, false);
if(RenderAvoidanceWarnings && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 avNmWarningBox[4];
Vector3 avAvoidWarningBox[4];
Vector3 avAvoidDirectedWarningBox[4];
Vector3 VehicleRightVector(VEC3V_TO_VECTOR3(pVeh->GetTransform().GetA()));
Vector3 VehicleForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
Vector3 vecVehiclePosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
avNmWarningBox[0] = vecVehiclePosition - (VehicleRightVector * vNmXExtents.x) - (VehicleForwardVector * vNmXExtents.w); // MiddleLeft
avNmWarningBox[1] = vecVehiclePosition - (VehicleRightVector * vNmXExtents.x) + (VehicleForwardVector * vNmXExtents.z); // ForwardLeft
avNmWarningBox[2] = vecVehiclePosition + (VehicleRightVector * vNmXExtents.y) + (VehicleForwardVector * vNmXExtents.z); // MiddleRight
avNmWarningBox[3] = vecVehiclePosition + (VehicleRightVector * vNmXExtents.y) - (VehicleForwardVector * vNmXExtents.w); // ForwardRight
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
// Offsetting to get rid of the annoying Z-fighting issue.
for (int i = 0; i < 4; ++i)
{
avNmWarningBox[i].SetZ(avNmWarningBox[i].GetZ() + 0.1f);
}
avAvoidWarningBox[0] = vecVehiclePosition -(VehicleRightVector * vAvoidXExtents.x); // MiddleLeft
avAvoidWarningBox[1] = avAvoidWarningBox[0] + (VehicleForwardVector * vAvoidXExtents.y); // ForwardLeft
avAvoidWarningBox[2] = vecVehiclePosition +(VehicleRightVector * vAvoidXExtents.x); // MiddleRight
avAvoidWarningBox[3] = avAvoidWarningBox[2] + (VehicleForwardVector * vAvoidXExtents.y); // ForwardRight
avAvoidDirectedWarningBox[0] = vecVehiclePosition -(VehicleRightVector * vAvoidDirectedExtents.x); // MiddleLeft
avAvoidDirectedWarningBox[1] = avAvoidDirectedWarningBox[0] + (VehicleForwardVector * vAvoidDirectedExtents.y); // ForwardLeft
avAvoidDirectedWarningBox[2] = vecVehiclePosition +(VehicleRightVector * vAvoidDirectedExtents.x); // MiddleRight
avAvoidDirectedWarningBox[3] = avAvoidDirectedWarningBox[2] + (VehicleForwardVector * vAvoidDirectedExtents.y); // ForwardRight
for (int i = 0; i < 4; ++i)
{
avAvoidDirectedWarningBox[i].SetZ(avAvoidDirectedWarningBox[i].GetZ() - 0.1f);
}
grcDebugDraw::Poly(RCC_VEC3V(avNmWarningBox[0]), RCC_VEC3V(avNmWarningBox[1]), RCC_VEC3V(avNmWarningBox[2]), Color_red, true);
grcDebugDraw::Poly(RCC_VEC3V(avNmWarningBox[3]), RCC_VEC3V(avNmWarningBox[2]), RCC_VEC3V(avNmWarningBox[0]), Color_red, true);
grcDebugDraw::Poly(RCC_VEC3V(avAvoidWarningBox[0]), RCC_VEC3V(avAvoidWarningBox[1]), RCC_VEC3V(avAvoidWarningBox[3]), Color_blue, true);
grcDebugDraw::Poly(RCC_VEC3V(avAvoidWarningBox[3]), RCC_VEC3V(avAvoidWarningBox[2]), RCC_VEC3V(avAvoidWarningBox[0]), Color_blue, true);
grcDebugDraw::Poly(RCC_VEC3V(avAvoidDirectedWarningBox[0]), RCC_VEC3V(avAvoidDirectedWarningBox[1]), RCC_VEC3V(avAvoidDirectedWarningBox[3]), Color_purple, true);
grcDebugDraw::Poly(RCC_VEC3V(avAvoidDirectedWarningBox[3]), RCC_VEC3V(avAvoidDirectedWarningBox[2]), RCC_VEC3V(avAvoidDirectedWarningBox[0]), Color_purple, true);
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
// Generate another debug draw plane for helping to visualize the
// side the ped needs to dodge towards.
Vector3 avDividingPlane[4];
avDividingPlane[0] = vecVehiclePosition; // BackBottom
avDividingPlane[1] = avDividingPlane[0] + (VehicleForwardVector * vAvoidXExtents.y); // ForwardBottom
avDividingPlane[2] = avDividingPlane[0]; // BackTop
avDividingPlane[2].SetZ(avDividingPlane[2].GetZ() + 2.0f);
avDividingPlane[3] = avDividingPlane[1]; // ForwardTop
avDividingPlane[3].SetZ(avDividingPlane[1].GetZ() + 2.0f);
grcDebugDraw::Poly(RCC_VEC3V(avDividingPlane[0]), RCC_VEC3V(avDividingPlane[1]), RCC_VEC3V(avDividingPlane[3]), Color_orange, true);
grcDebugDraw::Poly(RCC_VEC3V(avDividingPlane[3]), RCC_VEC3V(avDividingPlane[2]), RCC_VEC3V(avDividingPlane[0]), Color_orange, true);
// Vector3 avScanBox[4];
// avScanBox[0] = Vector3(MinX, MinY, vecVehiclePosition.z-0.1f);
// avScanBox[1] = Vector3(MinX, MaxY, vecVehiclePosition.z-0.1f);
// avScanBox[2] = Vector3(MaxX, MaxY, vecVehiclePosition.z-0.1f);
// avScanBox[3] = Vector3(MaxX, MinY, vecVehiclePosition.z-0.1f);
// grcDebugDraw::Poly(RCC_VEC3V(avScanBox[0]), RCC_VEC3V(avScanBox[1]), RCC_VEC3V(avScanBox[2]), Color_DarkSeaGreen, false);
// grcDebugDraw::Poly(RCC_VEC3V(avScanBox[3]), RCC_VEC3V(avScanBox[2]), RCC_VEC3V(avScanBox[0]), Color_DarkSeaGreen, false);
}
#endif // __DEV
// Get the current drive direction and orientation.
const Vector3 vehFwdDir = VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection());
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
// Magic removed due to the cold, hard reality that reversing the
// vehDriveDir prevents a Ped from dodging a reversing car.
const CPhysical* pFleeEntity = pVeh->GetIntelligence()->GetFleeEntity();
const bool bOnJunctionWithTrainApproaching = pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO
&& pVeh->GetIntelligence()->GetJunction() && pVeh->GetIntelligence()->GetJunction()->ShouldCarsStopForTrain();
for(int entityIndex = 0; entityIndex < numEntities; entityIndex++)
{
const int nextEntityIndex = entityIndex + 1;
if(nextEntityIndex < numEntities)
{
CEntity* pNextEntity = scanner.GetEntityByIndex(nextEntityIndex);
if(pNextEntity)
{
// Prefetch some stuff that we are likely to need for the next ped:
// - The first 256 bytes or so, for matrix pointers, IsBaseFlagSet(), etc.
// - Config flags, for GetIsInVehicle(), etc.
PrefetchBuffer<256>(pNextEntity);
static_cast<CPed*>(pNextEntity)->PrefetchPedConfigFlags();
}
}
CEntity* pEntity = scanner.GetEntityByIndex(entityIndex);
if(!pEntity)
{
continue;
}
Assert(pEntity->GetIsTypePed());
CPed * pPed = (CPed*)pEntity;
const bool bPedIsStationaryForAvoidance = (pPed->IsDead());
if(!bPedIsStationaryForAvoidance)
{
//don't stop for the thing we're fleeing
if (pPed == pFleeEntity)
{
continue;
}
//skip peds in vehicles
if (pPed->GetIsInVehicle())
{
continue;
}
//skip peds on mounts
if (pPed->GetIsOnMount())
{
continue;
}
//skip peds about to be removed
if (pPed->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
continue;
}
//if this is the player and we were driving around him recently,
//don't stop if he gets in the way
if (bWasSlowlyPushingPlayer && pPed->IsPlayer())
{
continue;
}
if (pPed->GetCapsuleInfo()->IsBird())
{
continue;
}
Vector3 centre = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
const Vector3 vecVehiclePosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
if(centre.x > MinX && centre.x < MaxX &&
centre.y > MinY && centre.y < MaxY &&
rage::Abs(centre.z - vecVehiclePosition.z) < 6.0f)
{
// Do a slightly more sophisticated z check. Work out what the height
// of our car would be at the point nearest the other car.
const float DistAhead = fwGeom::DistAlongLine2D(vecVehiclePosition.x, vecVehiclePosition.y, vehFwdDir.x, vehFwdDir.y, centre.x, centre.y);
const float OurHeight = vecVehiclePosition.z + DistAhead * vehFwdDir.z;
if(rage::Abs(centre.z - OurHeight) < 2.5f) // This was 3.0 but there are subway tunnels RIGHT below the road surface.
{
// Now actually do a collision threat analysis for this car
Delta = centre - vecVehiclePosition;
DotFront = DotProduct(vehFwdDir, Delta);
// Car braking
// Only do this stuff if the car is friendly enough to stop for peds
//(Not necessarily the case since the 'pedsjumpingoutoftheway' stuff will always call this function.
if(pTask && *pMaxSpeed > 0.0f && pTask->IsDrivingFlagSet(DF_StopForPeds) && !bOnJunctionWithTrainApproaching &&
!HelperPedIsStationaryForAvoidance(pPed))
{
CPed *pPlayerPed = NULL;
if(((CPed*)pEntity)->IsAPlayerPed())
pPlayerPed = (CPed*)pEntity;
if(!pEntity || !pPlayerPed || pPlayerPed->GetPlayerInfo()->GetLastTargetVehicle()!=pVeh)
{
//don't stop if the ped is within our bounding box on the font--he's probably standing on top of our car
//so add a z check to make sure we stop for cars that might be underneath our car, even if they're
//within the bounding box
if((DotFront > CarFrontSize || (centre.z < vecVehiclePosition.z && DotFront > 0.0f)) && DotFront - CarFrontSize < fBrakingMinDist)
{
DotSide = rage::Abs(DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()), Delta));
if (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_BeganCrossingRoad))
{
//estimate the ped's position at the time of collision, and test against whichever
//point is closer to the car
const float fEstTimeToCollision = (OriginalMaxSpeed >= SMALL_FLOAT && DotFront >= SMALL_FLOAT) ? DotFront / OriginalMaxSpeed : FLT_MAX;
const Vector3 vEstFuturePosition = centre + (pPed->GetVelocity() * fEstTimeToCollision);
//grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(centre), VECTOR3_TO_VEC3V(vEstFuturePosition), 0.25f, Color_cyan);
if (fEstTimeToCollision < FLT_MAX)
{
DotFront = rage::Min(DotFront, DotProduct(vehFwdDir, (vEstFuturePosition - vecVehiclePosition)));
DotSide = rage::Min(DotSide, rage::Abs(DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()), (vEstFuturePosition - vecVehiclePosition))));
}
}
const float fPadding = (pPed->IsLawEnforcementPed() || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_BeganCrossingRoad)) ? 1.0f : 0.5f;
if(DotSide <= CarRightSize + fPadding)
{
const float fBrakeDistance = UseNewStopDistance ? Max(pVeh->GetAIHandlingInfo()->GetDistToStopAtCurrentSpeed(VelFront), pVeh->pHandling->GetAIHandlingInfo()->GetMinBrakeDistance())
: ((pPlayerPed) ? BRAKE_DISTANCE_PLAYER:BRAKE_DISTANCE);
// only do Car braking if within braking distance
if((DotFront - CarFrontSize) < fBrakeDistance)
{
Assert(pEntity->GetIsTypePed());
// For certain scenarios we don't stop for the ped as the ped will be close to a car. We avoid the car instead so that we can drive round it.
s32 runningScenario =((CPed *)pEntity)->GetPedIntelligence()->GetQueriableInterface()->GetRunningScenarioType();
const CScenarioInfo* pScenarioInfo = CScenarioManager::GetScenarioInfo(runningScenario);
if( !pScenarioInfo || (pScenarioInfo && !pScenarioInfo->GetIsFlagSet(CScenarioInfoFlags::IgnoredByCars)) )
{
// We might have to slow down a bit
static dev_float fTweakDistance = 1.0f;
float fRatio = (DotFront - CarFrontSize - fTweakDistance) / fBrakeDistance;
//If the ped is jay walking then increase the stopping distance. Let the standard behavior handle the rest.
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_JayWalking))
{
fRatio *= 1.5f;
}
fRatio = Clamp(fRatio, 0.0f, 1.0f);
*pMaxSpeed = Clamp(fRatio * OriginalMaxSpeed, 0.0f, *pMaxSpeed );
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(vecVehiclePosition, 2.0f, Color_yellow2, false);
}
#endif //__BANK
// Set flag manually if saved speed is used
pTask->m_bSlowingDownForPed = true;
//pTask->m_bShouldChangeLanesForTraffic = pTask->CanSwitchLanesToAvoidObstruction(pVeh, pPed, OriginalMaxSpeed);
// Keep a note of the obstructive ped.
pVeh->GetIntelligence()->m_pObstructingEntity = (CPhysical*)pEntity;
//if(pUberMission) // Only respond if we actually have a driver in control.
if(DotFront - CarFrontSize < CDriverPersonality::GetStopDistanceForPeds(pDriver, pVeh))
{
pTask->SetWaitingForPlayerPed(pPlayerPed != NULL);
if (pPlayerPed != NULL)
{
pTask->UpdatePlayerOnFootIsBlockingUs(pVeh, pDriver, pPlayerPed, pMaxSpeed, CarRightSize - DotSide, DotFront - CarFrontSize);
}
else
{
pTask->SetState(State_TemporarilyBrake);
}
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(vecVehiclePosition, 2.0f, Color_red, false);
}
#endif //__BANK
}
// else if(DotFront - CarFrontSize < CDriverPersonality::GetDriveSlowDistanceForPeds(pDriver))
// {
// pTask->SetWaitingForPlayerPed(pPlayerPed != NULL);
// pTask->SetState(State_WaitForPed);
//
// #if __BANK
// if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
// {
// grcDebugDraw::Sphere(vecVehiclePosition, 2.0f, Color_orange, false);
// }
// #endif //__BANK
// }
// If this is the player or a non-cop ped
const int iModDenominator = pTask->m_bSlowlyPushingPlayerThisFrame ? 6 : 3;
if(pDriver && pDriver->CheckBraveryFlags(BF_PLAY_CAR_HORN)
&& (((pVeh->GetRandomSeed() + fwTimer::GetSystemFrameCount()) & iModDenominator) == 2)
&& (pPlayerPed || !pPed->IsLawEnforcementPed()))
{
const u32 nCurrentTimeMs = fwTimer::GetTimeInMilliseconds();
const u32 nTimeBtwnHonks = CDriverPersonality::GetTimeBetweenHonkingAtPeds(pDriver, pVeh);
if (nCurrentTimeMs > pTask->m_lastTimeHonkedAtAnyPed + nTimeBtwnHonks)
{
//if it's been a while since we've honked at anyone, always start with a honk
//otherwise, 50/50 chance of honking vs. playing BLOCKED_GENERIC
if (nCurrentTimeMs > pTask->m_lastTimeHonkedAtAnyPed + (nTimeBtwnHonks * 2))
{
HelperMaybePlayHorn(pVeh, true, false, (CPhysical*)pEntity);
if (OriginalMaxSpeed > 20.0f)
{
pDriver->NewSay("CAR_SLOW_DOWN_CURSE_HIGH");
}
else
{
pDriver->NewSay("CAR_SLOW_DOWN_CURSE_MED");
}
}
else if (g_ReplayRand.GetRanged(0, 3) < 2)
{
HelperMaybePlayHorn(pVeh, true, g_ReplayRand.GetBool(), (CPhysical*)pEntity);
}
else
{
pDriver->NewSay("BLOCKED_GENERIC");
}
//always update the timer, even if we do a BLOCKED_GENERIC
pTask->m_lastTimeHonkedAtAnyPed = fwTimer::GetTimeInMilliseconds();
}
}
}
}
}
}
}
}
// Ped avoidance
// We want peds to avoid cars which are reversing as well, and so
// will need a separate section from the car-braking bit above
// Want to Let Ped know it's in danger - and to step or dive away
if(pEntity->GetIsTypePed())
{
//don't fire avoidance event for mission peds when we're not stopping
if(pTask && !pTask->IsDrivingFlagSet(DF_StopForPeds) && pPed->PopTypeIsMission())
{
continue;
}
CPed *pPed =(CPed *)pEntity;
bool bPedStandingOnCar = pPed->GetGroundPhysical() == pVeh;
#if __DEV
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
// Draw the expected direction in which a dodging Ped should dodge
// towards.
TUNE_GROUP_BOOL(CAR_AI, RenderExpectedDodgeDirection, false);
if(RenderExpectedDodgeDirection && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 vecVehicleRightVector = VEC3V_TO_VECTOR3(pVeh->GetTransform().GetA());
Vector3 vecVehiclePosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
Vector3 vecPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
Vector3 vecArrowEndPosition = vecVehicleRightVector * 3.0f;
Vector3 vecPedOffset = vecPedPosition - vecVehiclePosition;
if (0.0f > DotProduct(vecVehicleRightVector, vecPedOffset))
{
vecArrowEndPosition = -vecArrowEndPosition;
}
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vecPedPosition), VECTOR3_TO_VEC3V(vecPedPosition + vecArrowEndPosition), 1.0f, Color_purple);
}
#endif
if(bShouldBeAvoided && !bPedStandingOnCar && VelFront != 0.0f &&
rage::Abs(VelFront) > CVehiclePotentialCollisionScanner::ms_fMinAvoidSpeed &&
!pPed->GetBlockingOfNonTemporaryEvents() && // Designers probably don't want any of these reactions if non-temporary events are blocked.
!pPed->GetTaskData().GetIsFlagSet(CTaskFlags::DisableBraceForImpact) )
{
DotFront = DotProduct(vehFwdDir, Delta);
DotSide = rage::Abs(DotProduct(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()), Delta));
float absDotFront = rage::Abs(DotFront);
bool bPedSeenCar = bPlayingHorn || bHasRecentlyRunSomeoneOver || (absDotFront < CarFrontSize) || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_AlwaysSeeApproachingVehicles) || DotProduct(VEC3V_TO_VECTOR3(pPed->GetTransform().GetB()), Delta) < 0.0f;
if(!bPedSeenCar)
{
bPedSeenCar = (fwRandom::GetRandomNumberInRange(0.0f, 1.0f) < sm_Tunables.m_ChanceOfPedSeeingCarFromBehind);
}
if(bPedSeenCar)
{
bool bIsThreatened = pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_THREAT_RESPONSE);
Vector2* pAvoidExtents = !bIsThreatened ? &vAvoidXExtents : &vThreatXExtents;
float fExtraExtents = !bIsThreatened ? 2.0f : 0.0f;
bool bInDiveYArea = rage::Abs(DotFront) > CarFrontSize && SIGN(DotFront) == SIGN(VelFront) && DotFront < pAvoidExtents->y && DotFront > CarFrontSize + fExtraExtents;
if(VelFront < 0.0f)
bInDiveYArea = DotFront > pAvoidExtents->y && DotFront < CarRearSize - fExtraExtents;
bool bIsInDirectedArea = (SIGN(DotFront) == SIGN(VelFront)) && (Abs(DotFront) <= vAvoidDirectedExtents.y) &&
(Abs(DotSide) <= vAvoidDirectedExtents.x);
bool bIsAffectedByDirectedArea = bIsInDirectedArea && !bIsThreatened;
if(bIsAffectedByDirectedArea)
{
if(pPed->IsSecurityPed() && pPed->GetPedResetFlag(CPED_RESET_FLAG_IsInStationaryScenario))
{
bIsAffectedByDirectedArea = false;
}
}
if(bIsAffectedByDirectedArea)
{
const CTaskMoveCrossRoadAtTrafficLights* pTask = static_cast<CTaskMoveCrossRoadAtTrafficLights *>(
pPed->GetPedIntelligence()->FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_CROSS_ROAD_AT_TRAFFIC_LIGHTS));
if(pTask && !pTask->IsCrossingRoad())
{
bIsAffectedByDirectedArea = false;
}
}
if(bIsAffectedByDirectedArea)
{
Vec3V vPedVelocity = VECTOR3_TO_VEC3V(pPed->GetVelocity());
Vec3V vPedForward = pPed->GetTransform().GetForward();
Vec3V vPedDirection = NormalizeFastSafe(vPedVelocity, vPedForward);
Vec3V vVehVelocity = VECTOR3_TO_VEC3V(pVeh->GetVelocity());
Vec3V vVehForward = pVeh->GetTransform().GetForward();
Vec3V vVehDirection = NormalizeFastSafe(vVehVelocity, vVehForward);
ScalarV scAbsDot = Abs(Dot(vPedDirection, vVehDirection));
ScalarV scMaxDot = ScalarVFromF32(sm_Tunables.m_MaxAbsDotForAvoidDirected);
if(IsGreaterThanAll(scAbsDot, scMaxDot))
{
bIsAffectedByDirectedArea = false;
}
}
Vector3 pPedOffset = VEC3V_TO_VECTOR3(pVeh->GetTransform().UnTransform3x3(pPed->GetTransform().GetPosition()- pVeh->GetVehiclePosition()));
bool bInNmYArea = pPedOffset.x > -vNmXExtents.x &&
pPedOffset.x < vNmXExtents.y &&
pPedOffset.y < vNmXExtents.z &&
pPedOffset.y > -vNmXExtents.w;
bool useNm = bInNmYArea && rage::Abs(VelFront) < sm_Tunables.m_MaxSpeedForBrace
&& DotSide <= pAvoidExtents->x;
// Don't use the natural motion behaviour if already diving out the way,
// it would only fail anyway because we'll be horizontal
if(useNm)
{
if(pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMPLEX_EVASIVE_STEP))
{
CTaskComplexEvasiveStep* pEvasiveTask =(CTaskComplexEvasiveStep*)pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_COMPLEX_EVASIVE_STEP);
if(pEvasiveTask && pEvasiveTask->GetIsDiving())
{
useNm = false;
}
}
}
if(useNm)
{
#if __DEV
if(RenderAvoidanceWarnings && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()), Color_red);
#endif
if(pPed->GetRagdollState()==RAGDOLL_STATE_ANIM &&
CTaskNMBehaviour::CanUseRagdoll(pPed, RAGDOLL_TRIGGER_IMPACT_CAR_WARNING, pVeh))
{
Vector3 vPedVelocity(pPed->GetVelocity());
if (MagSquared(pPed->GetGroundVelocityIntegrated()).Getf() > vPedVelocity.Mag2())
{
vPedVelocity = VEC3V_TO_VECTOR3(pPed->GetGroundVelocityIntegrated());
}
CTask* pTaskNM = rage_new CTaskNMBrace(1000, 30000, pVeh, CTaskNMBrace::BRACE_DEFAULT, vPedVelocity);
/// set weak option based on agility and health
static float fHealthThresholdMissionCharPlayer = 110.0f, fHealthThresholdOther = 140.0f;
const float fHealthThreshold =(pPed->IsPlayer() ||(pPed->PopTypeIsMission()) ? fHealthThresholdMissionCharPlayer : fHealthThresholdOther);
if(CTaskNMBehaviour::ms_bUseParameterSets &&(!pPed->CheckAgilityFlags(AF_RAGDOLL_BRACE_STRONG) ||(pPed->GetHealth() < fHealthThreshold))) {
((CTaskNMBrace*)pTaskNM)->SetType(CTaskNMBrace::BRACE_WEAK);
}
CEventSwitch2NM eventRagdoll(30000, pTaskNM);
pPed->SwitchToRagdoll(eventRagdoll);
pPed->GetPedIntelligence()->Dodged(*pVeh);
// Trigger audio to accompany this behaviour
if(pPed->IsGangPed() && pDriver && pDriver->IsLocalPlayer())
{
if(fwRandom::GetRandomNumberInRange(0.0f, 1.0f) < 0.25f)
{
if(!pPed->NewSay("GANG_DODGE_WARNING"))
pPed->NewSay("DODGE");
}
}
else
{
pPed->RandomlySay("DODGE", 0.125f);
}
}
}
else if(((Abs(VelFront) > sm_Tunables.m_MinSpeedForDive) &&
((DotSide <= pAvoidExtents->x) && bInDiveYArea)) || (bIsAffectedByDirectedArea))
{
#if __DEV
if(RenderAvoidanceWarnings && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
grcDebugDraw::Line(pVeh->GetVehiclePosition(), pPed->GetTransform().GetPosition(), Color_blue);
#endif
#if DEBUG_DRAW
if(!CPedDebugVisualiserMenu::ShouldDisableEvasiveDives())
#endif
{
//Check if we have not recently evaded the vehicle.
static const u32 s_uMinTimeSinceLastEvaded = 3000;
if((pPed->GetPedIntelligence()->GetLastEntityEvaded() != pVeh) ||
((pPed->GetPedIntelligence()->GetLastTimeEvaded() + s_uMinTimeSinceLastEvaded) < fwTimer::GetTimeInMilliseconds()))
{
CEventPotentialGetRunOver new_event(pVeh);
pPed->GetPedIntelligence()->AddEvent(new_event);
}
}
}
}
else
{
#if __DEV
if(RenderAvoidanceWarnings && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()), Color_green, Color_green);
#endif
}
}
}
}
}
}
}
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SlowCarDownForDoorsSectorList(CVehicle* pVeh, const float MinX, const float MinY, const float MaxX, const float MaxY, float *pMaxSpeed, float OriginalMaxSpeed, const spdAABB& boundBox, const bool bStopForBreakableDoors)
{
CEntityScannerIterator entityList = pVeh->GetIntelligence()->GetObjectScanner().GetIterator();
if(!entityList.GetCount())
{
return;
}
static dev_float s_fPadding = 0.25f;
const float fOurMinHeight = boundBox.GetMin().GetZf() - s_fPadding;
const float fOurMaxHeight = boundBox.GetMax().GetZf() + s_fPadding;
Vector3 vehDriveDir = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse)));
const Vec3V vecVehPosition = pVeh->GetVehiclePosition();
//const Vector3 vOurVelocity = pVeh->GetAiVelocity();
// If we are turning we turn the front vector a bit as well. This way we won't stop for cars directly ahead
// of us when we are turning but we will stop for cars that we are turning into.
const float fTurnAngle = 0.5f * pVeh->GetSteerAngle();
const float cosTurnAngle = rage::Cosf(fTurnAngle);
const float sinTurnAngle = rage::Sinf(fTurnAngle);
const float ourNewDriveDirX = vehDriveDir.x * cosTurnAngle - vehDriveDir.y * sinTurnAngle;
vehDriveDir.y = vehDriveDir.y * cosTurnAngle + vehDriveDir.x * sinTurnAngle;
vehDriveDir.x = ourNewDriveDirX;
const float OurCarDX = vehDriveDir.x * OriginalMaxSpeed; // 1 sec
const float OurCarDY = vehDriveDir.y * OriginalMaxSpeed;
for( CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext() )
{
if (!pEntity)
{
continue;
}
const CObject* pObject = static_cast<const CObject*>(pEntity);
if (!pObject->IsADoor())
{
continue;
}
const CDoor* pDoor = static_cast<const CDoor*>(pObject);
const bool bObjectIsAutoOpenDoor = CDoor::DoorTypeAutoOpensForVehicles(pDoor->GetDoorType());
if (!bObjectIsAutoOpenDoor)
{
continue;
}
const CDoorTuning* pDoorTuning = pDoor->GetTuning();
const bool bDoorIsBreakable = (pDoorTuning && pDoorTuning->m_BreakableByVehicle)
|| pDoor->GetDoorType() == CDoor::DOOR_TYPE_STD
|| pDoor->GetDoorType() == CDoor::DOOR_TYPE_STD_SC;
if (!bStopForBreakableDoors && bDoorIsBreakable)
{
continue;
}
const Vec3V vOtherPosWorldSpace = pObject->GetTransform().GetPosition();
const Vector3 vecObjectPosition = VEC3V_TO_VECTOR3(vOtherPosWorldSpace);
//reject doors behind us
const Vec3V vDeltaPos = vOtherPosWorldSpace - vecVehPosition;
if (Dot(vDeltaPos.GetXY(), RCC_VEC3V(vehDriveDir).GetXY()).Getf() < boundBox.GetMin().GetYf())
{
continue;
}
// reject barrier arm doors that close to the opposite side of us
if ((pDoor->GetDoorType() == CDoor::DOOR_TYPE_BARRIER_ARM || pDoor->GetDoorType() == CDoor::DOOR_TYPE_BARRIER_ARM_SC) &&
IsGreaterThanAll(Dot(vDeltaPos, pDoor->GetTransform().GetA()), ScalarV(V_ZERO)))
{
continue;
}
const Vec3V vOtherFwd = pObject->GetTransform().GetForward();
//if we're approaching at a sharp angle, swerve instead of stop
if (!HelperShouldVehicleStopVsSwerveForDoor(RCC_VEC3V(vehDriveDir), boundBox.GetMax().GetX() - boundBox.GetMin().GetX(), pDoor))
{
continue;
}
if (HelperShouldIgnoreDoorEntirely(pDoor))
{
continue;
}
if( vecObjectPosition.x > MinX && vecObjectPosition.x < MaxX &&
vecObjectPosition.y > MinY && vecObjectPosition.y < MaxY )
{
//do we need to care about door height? possibly.
spdAABB aabb;
pEntity->GetAABB(aabb);
//const Vec3V vOtherPosCarSpace = pVeh->GetTransform().UnTransform(vOtherPosWorldSpace);
const float fOtherMinHeightCarSpace = pVeh->GetTransform().UnTransform(aabb.GetMin()).GetZf();
const float fOtherMaxHeightCarSpace = pVeh->GetTransform().UnTransform(aabb.GetMax()).GetZf();
const bool bOtherMinIsWithinZRange = fOtherMinHeightCarSpace > fOurMinHeight && fOtherMinHeightCarSpace < fOurMaxHeight;
const bool bOtherMaxIsWithinZRange = fOtherMaxHeightCarSpace > fOurMinHeight && fOtherMaxHeightCarSpace < fOurMaxHeight;
if (bOtherMinIsWithinZRange || bOtherMaxIsWithinZRange)
{
const float DeltaSpeedX = -OurCarDX;
const float DeltaSpeedY = -OurCarDY;
spdAABB otherBoxModified;
const spdAABB& otherBox = pObject->GetLocalSpaceBoundBox(otherBoxModified);
otherBoxModified = otherBox;
//do some trajectory tests
float CollisionT = Test2MovingRectCollision_OnlyFrontBumper(pVeh, pObject,
DeltaSpeedX, DeltaSpeedY,
vehDriveDir, VEC3V_TO_VECTOR3(vOtherFwd), boundBox, otherBoxModified, true);
//float CollisionT = Test2MovingRectCollision_OnlyFrontBumper(pObject, pVeh,
// -DeltaSpeedX, -DeltaSpeedY, VEC3V_TO_VECTOR3(vOtherFwd), vehDriveDir, otherBoxModified, boundBox);
//const float CollisionT2 = Test2MovingRectCollision( pObject, pVeh,
// -DeltaSpeedX, -DeltaSpeedY,
// VEC3V_TO_VECTOR3(vOtherFwd), vehDriveDir, otherBoxModified, boundBox);
const float CollisionT2 = Test2MovingRectCollision(pVeh, pObject,
DeltaSpeedX, DeltaSpeedY,
vehDriveDir, VEC3V_TO_VECTOR3(vOtherFwd), boundBox, otherBoxModified, true);
CollisionT = rage::Min(CollisionT, CollisionT2);
const float fDistToCollision = OriginalMaxSpeed * CollisionT;
const float fDistToStop = pVeh->GetAIHandlingInfo()->GetDistToStopAtCurrentSpeed(OriginalMaxSpeed);
const float fDriveSlowBeyondDistance = rage::Min((boundBox.GetMax().GetY() - boundBox.GetMin().GetY()).Getf(), 5.0f);
if (fDistToCollision < fDistToStop && fDistToCollision < fDriveSlowBeyondDistance)
{
*pMaxSpeed = 0.0f;
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
pVeh->m_nVehicleFlags.bWasStoppedForDoor = true;
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), 2.0f, Color_red, false);
}
#endif //__BANK
return;
}
else if (fDistToCollision < fDistToStop)
{
*pMaxSpeed = rage::Min(*pMaxSpeed, 1.0f);
//pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped();
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), 2.0f, Color_yellow, false);
}
#endif //__BANK
}
}
}
}
}
bool HelperShouldPreventFullStop(const CVehicle* /*pVeh*/, const CVehicle* pOtherVehicle, const CarAvoidanceLevel carAvoidanceLevel
, const bool bOnJunction, const bool bGoingStraightAtJunction, const bool bOnSingleTrackRoad, const bool bDrivingAgainstTraffic
, const bool bTrainApproaching, Vec3V_In vehDriveDir)
{
//static const ScalarV scZero(V_ZERO);
static const ScalarV scThreshold(-0.5f);
//we only want to do this against vehicles that are going against us,
//but the dot product is anded in at the end of the below if-statement
const bool bPreferYieldingToLeftTurner = bGoingStraightAtJunction && pOtherVehicle->m_nVehicleFlags.bTurningLeftAtJunction
&& pOtherVehicle->GetAiXYSpeed() >= 1.0f;
if ((pOtherVehicle->m_nVehicleFlags.bIsWaitingToTurnLeft
|| (bOnJunction && pOtherVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO && !bPreferYieldingToLeftTurner)
|| bOnSingleTrackRoad
|| bDrivingAgainstTraffic
|| (bOnJunction && bTrainApproaching && !pOtherVehicle->InheritsFromTrain()))
&& carAvoidanceLevel >= CAR_AVOIDANCE_LITTLE
&& IsLessThanAll(Dot(NormalizeFast(vehDriveDir.GetXY()), NormalizeFast(pOtherVehicle->GetVehicleForwardDirection().GetXY())), scThreshold))
{
return true;
}
else
{
return false;
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SlowCarDownForCarsSectorList
// PURPOSE : Processes one sector list.(Slows down for the cars in it)
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SlowCarDownForCarsSectorList(CVehicle* pVeh, float MinX, float MinY, float MaxX, float MaxY, float &fMaxSpeed, float OriginalMaxSpeed, const float SteerDirection, const bool bUseAltSteerDirection, const bool bGiveWarning, const spdAABB& boundBox, CarAvoidanceLevel carAvoidanceLevel)
{
TUNE_GROUP_FLOAT(FOLLOW_PATH_AI_THROTTLE, sfIgnoreCarForStoppingHeight, 5.0f, 0.0f, 100.0f, 0.1f);
const Vector3 ourPosition = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
m_fMostImminentCollisionThisFrame = FLT_MAX;
m_bSlowingDownForCar = false;
const CPhysical* pFleeEntity = pVeh->GetIntelligence()->GetFleeEntity();
if (pFleeEntity && pFleeEntity->GetIsTypePed())
{
const CPed* pFleePed = static_cast<const CPed*>(pFleeEntity);
const CVehicle* pFleeVehicle = pFleePed->GetVehiclePedInside();
if (pFleeVehicle)
{
pFleeEntity = pFleeVehicle;
}
}
//test the car we were slowing for last frame here, and skip it in the loop
const float fThisBoundRadius = pVeh->GetBoundRadius();
const Vec3V vehDriveDir = CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse));
//float fLeftness, fDotProduct;
//bool bSharpTurn = false;
//const u32 turnDir = CPathNodeRouteSearchHelper::FindUpcomingJunctionTurnDirection(pVeh->GetIntelligence()->GetJunctionNode(), pVeh->GetIntelligence()->GetNodeList(), &bSharpTurn, &fLeftness, CPathNodeRouteSearchHelper::sm_fDefaultDotThresholdForRoadStraightAhead, &fDotProduct);
//const bool bTurningLeft = turnDir == BIT_TURN_LEFT;
const bool bOnSingleTrackRoad = pVeh->GetIntelligence()->IsOnSingleTrackRoad();
const bool bOnJunction = pVeh->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GO;
const bool bGoingStraightAtJunction = pVeh->GetIntelligence()->GetJunctionFilter() == JUNCTION_FILTER_MIDDLE || !pVeh->m_nVehicleFlags.bTurningLeftAtJunction;
const bool bDrivingAgainstTraffic = IsDrivingFlagSet(DF_DriveIntoOncomingTraffic) || (!pVeh->GetIntelligence()->IsOnHighway() && IsDrivingFlagSet(DF_AvoidTargetCoors));
const bool bTrainApproaching = pVeh->GetIntelligence()->GetJunction() && pVeh->GetIntelligence()->GetJunction()->ShouldCarsStopForTrain();
if (m_pOptimization_pLastCarWeSlowedFor)
{
const bool bPreventFullStop = HelperShouldPreventFullStop(pVeh, m_pOptimization_pLastCarWeSlowedFor, carAvoidanceLevel, bOnJunction, bGoingStraightAtJunction, bOnSingleTrackRoad, bDrivingAgainstTraffic, bTrainApproaching, vehDriveDir);
SlowCarDownForOtherCar(pVeh, m_pOptimization_pLastCarWeSlowedFor, fMaxSpeed, OriginalMaxSpeed, SteerDirection, bUseAltSteerDirection, bGiveWarning, boundBox, fThisBoundRadius, carAvoidanceLevel, bPreventFullStop, true);
}
TUNE_GROUP_BOOL(FOLLOW_AI_SLOW, sbAllowVehicleSlowdownRoundRobin, true);
TUNE_GROUP_INT(FOLLOW_AI_SLOW, siMinDummyLevelForRoundRobin, VDM_DUMMY, VDM_REAL, VDM_SUPERDUMMY, 1);
bool bCheckThisFrame = true;
if (sbAllowVehicleSlowdownRoundRobin
&& pVeh->GetVehicleAiLod().GetDummyMode() >= siMinDummyLevelForRoundRobin
&& pVeh->GetIntelligence()->GetDummyAvoidanceDistributer().IsRegistered()
&& !pVeh->GetIntelligence()->GetDummyAvoidanceDistributer().ShouldBeProcessedThisFrame()
&& fwTimer::GetTimeInMilliseconds() != pVeh->m_TimeOfCreation) //if this is the first update, definitely slow
{
bCheckThisFrame = false;
}
if (bCheckThisFrame)
{
const Vector3 vehDriveDirVector3 = RCC_VECTOR3(vehDriveDir);
CVehicleScanner& scanner = pVeh->GetIntelligence()->GetVehicleScanner();
const int numEntities = scanner.GetNumEntities();
for(int entityIndex = 0; entityIndex < numEntities; entityIndex++)
{
const int nextEntityIndex = entityIndex + 1;
if(nextEntityIndex < numEntities)
{
CEntity* pNextEntity = scanner.GetEntityByIndex(nextEntityIndex);
if(pNextEntity)
{
// Prefetch some stuff that we are likely to need for the next vehicle,
// incl. things like the transform pointer, IsBaseFlagSet(), etc.
PrefetchBuffer<256>(pNextEntity);
}
}
CEntity* pEntity = scanner.GetEntityByIndex(entityIndex);
if(!pEntity)
{
continue;
}
CVehicle* pOtherVehicle = (CVehicle*) pEntity;
if(pOtherVehicle == pVeh)
{
continue;
}
//skip the car we were slowing for last frame, because we should have tested it above, outside of the loop
if (pOtherVehicle == m_pOptimization_pLastCarWeSlowedFor)
{
continue;
}
//don't slow down for the thing we're fleeing
if (pOtherVehicle == pFleeEntity)
{
continue;
}
//skip vehs about to be removed
if (pOtherVehicle->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
continue;
}
if(pVeh->m_nVehicleFlags.bIsRacingConservatively && !pOtherVehicle->m_nVehicleFlags.bIsRacingConservatively)
{
continue;
}
const Vector3 centre = VEC3V_TO_VECTOR3(pOtherVehicle->GetVehiclePosition()); // GetPosition is faster than pOtherVehicle->GetBoundCentre();
if( centre.x <= MinX ||
centre.x >= MaxX ||
centre.y <= MinY ||
centre.y >= MaxY ||
ABS(centre.z - ourPosition.z) >= sfIgnoreCarForStoppingHeight) // was 5.0f.
{
continue;
}
//check the dirty bit
if (ShouldIgnoreNonDirtyVehicle(*pVeh, *pOtherVehicle, IsDrivingFlagSet(DF_DriveInReverse), bOnSingleTrackRoad))
{
continue;
}
//B*2794186 prevent slowing down for player ped only for this mission
if(pVeh->GetIntelligence()->m_bSwerveAroundPlayerVehicleForRiotVanMission && pOtherVehicle->GetDriver() &&pOtherVehicle->GetDriver()->IsPlayer())
{
continue;
}
//Potentially avoid vehicles following the same target as us
if (ShouldNotAvoidVehicle(*pVeh, *pOtherVehicle))
{
continue;
}
//if the other car is waiting to turn left and facing us,
//and we're able to do any steering, don't stop for them
//(tends to get junctions jammed up)
// Get the current drive direction and orientation.
const bool bPreventFullStop = HelperShouldPreventFullStop(pVeh, pOtherVehicle, carAvoidanceLevel, bOnJunction, bGoingStraightAtJunction, bOnSingleTrackRoad, bDrivingAgainstTraffic, bTrainApproaching, vehDriveDir);
// Ignore trailers we're towing
if(pOtherVehicle->InheritsFromTrailer() && pOtherVehicle->m_nVehicleFlags.bHasParentVehicle)
{
// check whether the trailer is attached to the parent vehicle
if (GetTrailerParentFromVehicle(pOtherVehicle) == pVeh)
{
continue;
}
}
else if (pOtherVehicle->m_nVehicleFlags.bHasParentVehicle)
{
Assert(!pOtherVehicle->InheritsFromTrailer());
const CVehicle* pOtherVehParent = GetTrailerParentFromVehicle(pOtherVehicle);
//if we're not a trailer, but have an attach parent and it's a trailer,
//or are attached to the back of this car
//just exclude us
if (pOtherVehParent &&
(pOtherVehParent->InheritsFromTrailer() || pOtherVehParent == pVeh)
)
{
continue;
}
}
// Do a slightly more sophisticated z check. Work out what the height
// of our car would be at the point nearest the other car.
float DistAhead = fwGeom::DistAlongLine2D(ourPosition.x, ourPosition.y, vehDriveDirVector3.x, vehDriveDirVector3.y, centre.x, centre.y);
float OurHeight = ourPosition.z + DistAhead * vehDriveDirVector3.z;
if(ABS(centre.z - OurHeight) < 3.0f)
{
// Now actually do a collision threat analysis for this car
SlowCarDownForOtherCar(pVeh, pOtherVehicle, fMaxSpeed, OriginalMaxSpeed, SteerDirection, bUseAltSteerDirection, bGiveWarning, boundBox, fThisBoundRadius, carAvoidanceLevel, bPreventFullStop, false);
}
}
}
//if we didn't slow down any this frame, throw away the pointer
if (fMaxSpeed > OriginalMaxSpeed-SMALL_FLOAT)
{
m_pOptimization_pLastCarWeSlowedFor = NULL;
}
}
// Does a quick test to find upper and lower bounds on the time that two AABBs would collide.
// On return, check the lowerBound and upperBound:
// If lowerBound <= upperBound:
// The two boxes will collide at some point in time
// More return values:
// If upperBound < 0:
// The two boxes would have collided in the past, but not now
// If lowerBound == upperBound == + or - Infinity:
// The two boxes will not collide (and are not moving relative to one another)
// If lowerBound == -Infinity and upperBound = +Infinity
// The two boxes are colliding (and are not moving relative to one another)
__forceinline void FindAABBCollisionTime(
ScalarV_InOut beginCollisionTime, ScalarV_InOut endCollisionTime,
Vec2V_In boxAMin, Vec2V_In boxAMax,
Vec2V_In boxBMin, Vec2V_In boxBMax,
Vec2V_In relativePosition, Vec2V_In relativeVelocity) // Relative Position is B - A
{
// Combine the two AABBs so we can test a single AABB vs. a point
Vec2V combinedAABBMax = boxBMax - boxAMin;
Vec2V combinedAABBMin = boxBMin - boxAMax;
Vec2V worldSpaceBoxMax = combinedAABBMax + relativePosition;
Vec2V worldSpaceBoxMin = combinedAABBMin + relativePosition;
// Find the times where the worldspace box edges cross 0
Vec2V inverseVel = InvertSafe(relativeVelocity, Vec2V(V_INF));
Vec2V isect1 = -worldSpaceBoxMax * inverseVel; // Solve p + t * v = 0 => t = -p/v
Vec2V isect2 = -worldSpaceBoxMin * inverseVel;
Vec2V intervalMins = Min(isect1, isect2); // earliest collision time for each axis
Vec2V intervalMaxs = Max(isect1, isect2); // latest collision time for each axis
beginCollisionTime = MaxElement(intervalMins); // lowest time where there's a collision on both axes
endCollisionTime = MaxElement(intervalMaxs); // highest time where there's a collision on both axes
}
// Given local space corner points for a bounding box and a 2d world space direction vector
// Find the world space AABB that encloses the rotated box
__forceinline void FindAABBForOOBB(
Vec2V_InOut outAABoxMin, Vec2V_InOut outAABoxMax,
Vec2V_In dir, Vec2V_In ooBoxMin, Vec2V_In ooBoxMax)
{
Vec2V dirPerp(dir.GetY(), -dir.GetX());
// Find world space corner points
Vec2V a = Scale(dirPerp, ooBoxMin.GetX()) + Scale(dir, ooBoxMin.GetY());
Vec2V b = Scale(dirPerp, ooBoxMax.GetX()) + Scale(dir, ooBoxMin.GetY());
Vec2V c = Scale(dirPerp, ooBoxMin.GetX()) + Scale(dir, ooBoxMax.GetY());
Vec2V d = Scale(dirPerp, ooBoxMax.GetX()) + Scale(dir, ooBoxMax.GetY());
outAABoxMin = Min(a,b,c,d);
outAABoxMax = Max(a,b,c,d);
// TODO: This code seems shorter, but MinElement and MaxElement don't really exist. Maybe there's a workaround though?
// The permutes here generate the four corner points we want (x1y1, x1y2, x2y1, x2y2)
//Vec4V boxXCoords = GetFromTwo<Vec::X1, Vec::X2, Vec::X1, Vec::X2>(Vec4V(ooBoxMin.GetIntrin128()), Vec4V(ooBoxMax.GetIntrin128()));
//Vec4V boxYCoords = GetFromTwo<Vec::Y1, Vec::Y1, Vec::Y2, Vec::Y2>(Vec4V(ooBoxMin.GetIntrin128()), Vec4V(ooBoxMax.GetIntrin128()));
//Vec4V xformedXCoords = Scale(boxXCoords, dirPerp.GetX()) + Scale(boxYCoords, dir.GetX());
//Vec4V xformedYCoords = Scale(boxXCoords, dirPerp.GetY()) + Scale(boxYCoords, dir.GetY());
//outAABoxMin = Vec2V(MinElement(xformedXCoords), MinElement(xformedYCoords));
//outAABoxMax = Vec2V(MaxElement(xformedXCoords), MaxElement(xformedYCoords));
}
void FindCollisionTimeUpperAndLowerBoundsFast(
ScalarV_InOut lowerBound, ScalarV_InOut upperBound,
Vec2V_In relativeVel,
Vec2V_In myCarPos, Vec2V_In myCarDir, Vec2V_In myCarBboxMin, Vec2V_In myCarBboxMax,
Vec2V_In otherCarPos, Vec2V_In otherCarDir, Vec2V_In otherCarBboxMin, Vec2V_In otherCarBboxMax
)
{
// Basic algorithm:
// Find the AABB for myCar and otherCar
// Expand the otherCar AABB by myCar's size.
// Offset the AABB by the starting location of the otherCar relative to myCar
// Solve for time intervals when the top and bottom AABB sides cross the X axis, and when the left and right sides cross the Y axis
// The intersection of the two intervals is the collision time
// vehicle code does a weird thing where it uses max.x and -max.x instead of max.x and min.x
Vec2V myCarBboxMinLocal(myCarBboxMin);
Vec2V otherCarBboxMinLocal(otherCarBboxMin);
myCarBboxMinLocal.SetX(-myCarBboxMax.GetX());
otherCarBboxMinLocal.SetX(-otherCarBboxMax.GetX());
Vec2V myAABBMin, myAABBMax;
FindAABBForOOBB(myAABBMin, myAABBMax, myCarDir, myCarBboxMinLocal, myCarBboxMax);
// Test2MovingRectCollision has what appears to be a bug where 'our' bounding box ends up getting used as
// the other car's bounding box, because of the way the function is called where 'our' and 'other' are swapped.
// So here I take the max of the two boxes as the other box.
Vec2V combinedCarBboxMax = Max(otherCarBboxMax, myCarBboxMax);
Vec2V combinedCarBboxMin = Min(otherCarBboxMinLocal, myCarBboxMinLocal);
Vec2V otherAABBMin, otherAABBMax;
FindAABBForOOBB(otherAABBMin, otherAABBMax, otherCarDir, combinedCarBboxMin, combinedCarBboxMax);
FindAABBCollisionTime(lowerBound, upperBound,
myAABBMin, myAABBMax,
otherAABBMin, otherAABBMax,
otherCarPos - myCarPos, relativeVel);
}
//if this is called, we have already detected an imminent collision using 2d box tests
void HelperMaybeDoLongHorn(const CVehicle* pVeh, const CVehicle* pOtherVeh, const bool bOtherDriverIsPlayer, bool& bShouldPlayCarHorn, bool& bShouldUseLongHorn)
{
static dev_float s_fMinSpeedForLongHorn = 9.0f;
if (pVeh->GetAiXYSpeed() >= s_fMinSpeedForLongHorn && pOtherVeh->GetAiXYSpeed() >= s_fMinSpeedForLongHorn)
{
bShouldPlayCarHorn = true;
bShouldUseLongHorn = true;
//if he's doing something dumb, and going against us relatively quickly, use a long horn
if (!bShouldUseLongHorn && pOtherVeh->m_nVehicleFlags.bShouldBeBeepedAt
&& IsLessThanAll(Dot(pVeh->GetVehicleForwardDirection().GetXY(), pOtherVeh->GetVehicleForwardDirection().GetXY()), ScalarV(V_ZERO)))
{
bShouldUseLongHorn = true;
}
}
else if (pVeh->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG)
&& bOtherDriverIsPlayer)
{
//only test for collisions if we're a big vehicle and testing against the player
const CCollisionHistory* pCollisionHistory = pVeh->GetFrameCollisionHistory();
if(pCollisionHistory->GetNumCollidedEntities() > 0)
{
//Vehicles
if(pCollisionHistory->HasCollidedWithAnyOfTypes(ENTITY_TYPE_MASK_VEHICLE))
{
bShouldPlayCarHorn = true;
bShouldUseLongHorn = true;
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SlowCarDownForOtherCar
// PURPOSE : How much should this car slow down taking into account that other car
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointWithAvoidanceAutomobile::SlowCarDownForOtherCar(CVehicle* pVeh, CVehicle* pOtherVeh, float &fMaxSpeed, float OriginalMaxSpeed, const float SteerDirection, const bool bUseAltSteerDirection, const bool bGiveWarning, const spdAABB& boundBox, const float fBoundRadius, CarAvoidanceLevel carAvoidanceLevel, const bool bPreventFullStop, const bool bSkipSphereCheck)
{
PF_FUNC(AI_AvoidanceSlowCarForOtherCar);
float DotPr;
float OurCarDX, OurCarDY, OtherCarDX, OtherCarDY;
float CollisionT;
bool bUseCloseRelSpeedPercentage = false;
Assert(pOtherVeh);
Assert(pVeh);
float fMaxSpeedForThisCar = fMaxSpeed;
#if __DEV
static CVehicle *pStoredVeh0 = NULL;
static CVehicle *pStoredVeh1 = NULL;
if((pStoredVeh0 == pOtherVeh && pStoredVeh1 == pVeh) ||
(pStoredVeh1 == pOtherVeh && pStoredVeh0 == pVeh))
{
printf("Cars match\n");
}
#endif
// Get the current drive direction and orientation.
Vector3 vehDriveDir = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, IsDrivingFlagSet(DF_DriveInReverse)));
//float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
// If we are turning we turn the front vector a bit as well. This way we won't stop for cars directly ahead
// of us when we are turning but we will stop for cars that we are turning into.
float TurnAngle = 0.5f * pVeh->GetSteerAngle();
float cosTurnAngle = rage::Cosf(TurnAngle);
float sinTurnAngle = rage::Sinf(TurnAngle);
float ourNewDriveDirX = vehDriveDir.x * cosTurnAngle - vehDriveDir.y * sinTurnAngle;
vehDriveDir.y = vehDriveDir.y * cosTurnAngle + vehDriveDir.x * sinTurnAngle;
vehDriveDir.x = ourNewDriveDirX;
// Reject cars that are behind us(too late to slow down for them)
const Vec3V vecVehPosition = pVeh->GetVehiclePosition();
const Vec3V vecOtherVehPosition = pOtherVeh->GetVehiclePosition();
//TUNE_GROUP_FLOAT(CAR_AI, fAvoidBehindDist, 0.0f, -100.0f, 100.0f, 0.1f);
const Vec3V vDeltaPos = vecOtherVehPosition - vecVehPosition;
//const Vec3V vDeltaPosNormalized = NormalizeFastSafe(vDeltaPos, Vec3V(V_ZERO));
if (Dot(vDeltaPos.GetXY(), RCC_VEC3V(vehDriveDir).GetXY()).Getf() < boundBox.GetMin().GetYf())
{
return;
}
//if we're swerving for this player, don't slow down as well, it prevents us from getting out
//of the way in time
CPed* pOtherDriver = pOtherVeh->GetDriver();
if (bGiveWarning && pOtherDriver && pOtherDriver->IsPlayer() && carAvoidanceLevel > CAR_AVOIDANCE_NONE)
{
return;
}
Vec3V vOtherDriveDir = pOtherVeh->GetVehicleForwardDirection();
vOtherDriveDir.SetZ(ScalarV(V_ZERO));
vOtherDriveDir = NormalizeFast(vOtherDriveDir);
if (pOtherVeh->GetAiXYSpeed() > 1.0f
&& IsLessThanAll(Dot(VECTOR3_TO_VEC3V(pOtherVeh->GetAiVelocity()).GetXY(), vOtherDriveDir.GetXY()), ScalarV(V_ZERO))
)
{
vOtherDriveDir = -vOtherDriveDir;
}
bool bShouldPlayCarHorn = false;
bool bShouldUseLongHorn = false;
const bool bPersonalityAllowsHonking = pVeh->GetDriver() && pVeh->GetDriver()->CheckBraveryFlags(BF_PLAY_CAR_HORN);
const bool bOtherDriverIsPlayer = pOtherDriver && pOtherDriver->IsPlayer();
const bool bShouldBeBeepedAt = pOtherVeh->m_nVehicleFlags.bShouldBeBeepedAt
|| (pOtherVeh->m_nVehicleFlags.bPlayerDrivingAgainstTraffic
&& IsLessThanAll(Dot(RCC_VEC3V(vehDriveDir).GetXY(), vOtherDriveDir.GetXY()), ScalarV(V_ZERO))
);
if(bShouldBeBeepedAt && bOtherDriverIsPlayer)
{
//if((vecOtherVehPosition - vecVehPosition).XYMag() < 20.0f)
const float fDistToHonk = rage::Max(20.0f, fabsf((pVeh->GetAiVelocity() - pOtherVeh->GetAiVelocity()).Mag()));
if (DistXYFast(vecOtherVehPosition, vecVehPosition).Getf() < fDistToHonk)
{
if(bPersonalityAllowsHonking)
{
//defer this now, so if we have a close call with a vehicle, we can
//chose to use the "HeldDown" horn pattern -JM
if (pVeh->m_iNextValidHornTime < fwTimer::GetTimeInMilliseconds())
{
//don't honk for three seconds - enough to prevent this car honking until driven by
pVeh->m_iNextValidHornTime = fwTimer::GetTimeInMilliseconds() + 1500;
//B*1927556, lots more cars in NG, lets reduce number of horns
float fRandom = fwRandom::GetRandomNumberInRange(0.0f, 1.0f);
if(fRandom < (pOtherDriver->GetPlayerInfo()->GetPlayerDataPlayerOnHighway() ? 0.25f : 0.5f))
{
bShouldPlayCarHorn = true;
}
}
}
}
}
// Ignore vehicles escorting us from the front. Otherwise we can get into a deadlock.
// Only if they're stopped
if (pOtherVeh->GetAiXYSpeed() < 0.1f)
{
const CPhysical* pOtherAvoidanceTarget = pOtherVeh->GetIntelligence()->GetAvoidanceCache().m_pTarget;
if (pOtherAvoidanceTarget)
{
if (pOtherAvoidanceTarget == pVeh || pOtherAvoidanceTarget == pVeh->GetAttachedTrailer())
{
return;
}
}
}
// Ignore the vehicle the task has specified avoiding
if (pOtherVeh == m_pVehicleToIgnoreWhenStopping)
{
return;
}
const Vector3 vOtherVel = pOtherVeh->GetAiVelocity();
// Reject cars that are not going to collide over the next second or so.
// test the two line segments against each other.
vehDriveDir.z = 0.0f;
OtherCarDX = vOtherVel.x; // 1 sec
OtherCarDY = vOtherVel.y;
//if we're supposed to use the alternate steer direction, apply it here
Vector3 vTestDir = vehDriveDir;
TUNE_GROUP_BOOL(FOLLOW_AI_SLOW, bEnableNewSlowdownDirectionTest, true)
if (bUseAltSteerDirection && bEnableNewSlowdownDirectionTest)
{
vTestDir = Vector3(rage::Cosf(SteerDirection), rage::Sinf(SteerDirection), 0.0f);
}
// For our car we use a line segment as if we were moving at full
// speed in the direction we're heading.
OurCarDX = vTestDir.x * OriginalMaxSpeed; // 1 sec
OurCarDY = vTestDir.y * OriginalMaxSpeed;
TUNE_GROUP_BOOL(FOLLOW_AI_SLOW, bEnableCoarseSlowdownRejectDraw, false);
//if the car isn't behind us (checked above)
//and has a greater velocity in our desired direction than we do
//we should never hit each other (right?)
const Vec3V vOurDesiredVel = Vec3V(OurCarDX, OurCarDY, 0.0f);
const Vec3V vRelVelocity = vOurDesiredVel - RCC_VEC3V(vOtherVel);
//TUNE_GROUP_BOOL(FOLLOW_AI_SLOW, bEnableRelVelocityCheck, false);
if (/*(bEnableRelVelocityCheck && (Dot(vOurDesiredVel, vRelVelocity) < s_vScalarZero).Getb()) || */
(Dot(vRelVelocity, vDeltaPos) < s_vScalarZero).Getb())
{
#if __BANK
if (bEnableCoarseSlowdownRejectDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(vecVehPosition, vecOtherVehPosition, Color_green);
}
#endif //__BANK
const CPhysical* pHonkTarget = pOtherDriver ? pOtherDriver : static_cast<const CPhysical*>(pOtherVeh);
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
return;
}
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 LineBase = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
LineBase.z += 1.7f;
grcDebugDraw::Line(LineBase,
LineBase + Vector3(OurCarDX, OurCarDY, 0.0f),
Color32(255, 255, 255, 255));
}
#endif // __DEV
//if the bounding sphere test result is greater than the result of a car we've already
//chosen to stop for, ignore it and don't do the expensive test
if (!bSkipSphereCheck &&
(Test2MovingSphereCollision(pVeh, pOtherVeh, VEC3V_TO_VECTOR3(vOurDesiredVel), vOtherVel, fBoundRadius)
>= m_fMostImminentCollisionThisFrame))
{
#if __BANK
if (bEnableCoarseSlowdownRejectDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(vecVehPosition, vecOtherVehPosition, Color_orange);
}
#endif //__BANK
const CPhysical* pHonkTarget = pOtherDriver ? pOtherDriver : static_cast<const CPhysical*>(pOtherVeh);
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
return;
}
//Vector3 otherDriveDir(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection()));
//otherDriveDir.z = 0.0f;
//float Length = rage::Sqrtf(otherDriveDir.x * otherDriveDir.x + otherDriveDir.y * otherDriveDir.y);
const float DeltaSpeedX = OtherCarDX - OurCarDX;
const float DeltaSpeedY = OtherCarDY - OurCarDY;
#if __BANK
if (bEnableCoarseSlowdownRejectDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(vecVehPosition, vecOtherVehPosition, Color_red);
}
#endif //__BANK
const float fRelativeSpeed = Min(vOtherVel.Dot(vTestDir), OriginalMaxSpeed);
TUNE_GROUP_BOOL(CAR_AI, useNewVehicleSlowdownTest, true);
ScalarV collisionTimeLowerBound, collisionTimeUpperBound;
spdAABB otherBoxModified;
const spdAABB& otherBox = pOtherVeh->GetLocalSpaceBoundBox(otherBoxModified);
otherBoxModified = otherBox;
//if the other vehicle is a bike, add some padding so we don't sidle up next to bikes that aren't in the exact center of a lane
if (pOtherVeh->InheritsFromBike())
{
const Vec3V offset(0.5f, 1.0f, 0.5f);
otherBoxModified.SetMax(otherBoxModified.GetMax() + offset);
otherBoxModified.SetMin(otherBoxModified.GetMin() - offset);
}
else if(pOtherVeh->GetIsHeli())
{
HelperGetHeliAvoidanceBounds(pOtherVeh, otherBoxModified);
}
FindCollisionTimeUpperAndLowerBoundsFast(
collisionTimeLowerBound, collisionTimeUpperBound,
-vRelVelocity.GetXY(),
vecVehPosition.GetXY(), RCC_VEC3V(vTestDir).GetXY(), boundBox.GetMin().GetXY(), boundBox.GetMax().GetXY(),
vecOtherVehPosition.GetXY(), vOtherDriveDir.GetXY(), otherBoxModified.GetMin().GetXY(), otherBoxModified.GetMax().GetXY());
float startInterval = Max(0.0f, collisionTimeLowerBound.Getf());
float endInterval = Min(5.0f, collisionTimeUpperBound.Getf()); // TODO: Fix the hardcoded value here, it comes from Test2MovingRectCollision
bool bailEarly = (startInterval >= endInterval);
if (bailEarly)
{
const CPhysical* pHonkTarget = pOtherDriver ? pOtherDriver : static_cast<const CPhysical*>(pOtherVeh);;
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
return;
}
CollisionT = Test2MovingRectCollision_OnlyFrontBumper(pVeh, pOtherVeh,
DeltaSpeedX, DeltaSpeedY,
vTestDir, VEC3V_TO_VECTOR3(vOtherDriveDir), boundBox, otherBoxModified, false);
// Unfortunately we have to do this the other way around as well.
//(Otherwise collisions could go undetected if the points of carA intersect carB but
// the points of carB don't intersect carA.
//TUNE_GROUP_BOOL(CAR_AI, enable2ndVehicleSlowdownTest, true);
//if (enable2ndVehicleSlowdownTest)
{
const float CollisionT2 = Test2MovingRectCollision( pOtherVeh, pVeh,
-DeltaSpeedX, -DeltaSpeedY,
VEC3V_TO_VECTOR3(vOtherDriveDir), vTestDir, otherBoxModified, boundBox, false);
//
// if (CollisionT2 < CollisionT /*&& (CollisionT > 3.0f || CollisionT2 < 0.1f && CollisionT > 0.2f)*/)
// {
// Vec3V vecAboveVehPosition = vecVehPosition;
// vecAboveVehPosition.SetZf(vecVehPosition.GetZf() + 10.0f);
// grcDebugDraw::Line(vecVehPosition, vecAboveVehPosition, Color_purple);
// aiDisplayf("Car %p Using 2nd test: %.2f : %.2f", pVeh, CollisionT, CollisionT2);
// }
////////////////
CollisionT = rage::Min(CollisionT, CollisionT2);
}
// Draw a line indicating the distance we get before colliding
/*
if(CollisionT >= 0.0f && CollisionT < 1.0f)
{
grcDebugDraw::Line(pVeh->GetPosition().x + CollisionT*OurCarDX, pVeh->GetPosition().y + CollisionT*OurCarDY, 20.0f,
pVeh->GetPosition().x + CollisionT*OurCarDX, pVeh->GetPosition().y + CollisionT*OurCarDY, 10.0f,
0xffff00ff, 0xffff00ff);
grcDebugDraw::Line(pEntity->GetPosition().x + CollisionT*OtherCarDX, pEntity->GetPosition().y + CollisionT*OtherCarDY, 20.0f,
pEntity->GetPosition().x + CollisionT*OtherCarDX, pEntity->GetPosition().y + CollisionT*OtherCarDY, 10.0f,
0xffff00ff, 0xffff00ff);
}
*/
/*
// Just draw a line between the two cars for now.
grcDebugDraw::Line(pEntity->GetPosition().x, pEntity->GetPosition().y, 16.0f,
pVeh->GetPosition().x, pVeh->GetPosition().y, 16.0f,
0xffff0000, 0xffffffff);
// If the car is closeby we slow down more
Distance = rage::Sqrtf((pEntity->GetPosition().x - pVeh->GetPosition().x)*(pEntity->GetPosition().x - pVeh->GetPosition().x) +
(pEntity->GetPosition().y - pVeh->GetPosition().y)*(pEntity->GetPosition().y - pVeh->GetPosition().y));
Distance = MAX(0.0f, Distance - 4.5f); // within a certain distance we always stop
fMaxSpeedForThisCar = rage::Min((fMaxSpeedForThisCar), Distance);
*/
const float fDistToCollision = OriginalMaxSpeed * CollisionT;
const float fDistToStop = pVeh->GetAIHandlingInfo()->GetDistToStopAtCurrentSpeed(OriginalMaxSpeed);
// if (pOtherVeh->GetDriver() && pOtherVeh->GetDriver()->IsPlayer())
// {
// Displayf("[JM] Address: %p, CollisionT: %f, DistToCollision: %f", pVeh, CollisionT, fDistToCollision);
// }
const CPhysical* pHonkTarget = pOtherDriver ? pOtherDriver : static_cast<const CPhysical*>(pOtherVeh);
TUNE_GROUP_FLOAT(FOLLOW_AI_SLOW, fPercentOfMaxSpeedWhenOvertakingStationary, 0.9f, 0.0f, 1.0f, 0.01f);
//static dev_float s_fMaxTimeToCareAboutStopping = 3.0f;
if((useNewVehicleSlowdownTest && fDistToCollision < fDistToStop)
|| (!useNewVehicleSlowdownTest && CollisionT >= 0.0f && CollisionT < 1.5f))
{
const bool bIsOtherCarStationary = IsThisAStationaryCar(pOtherVeh);
// Don't slow down for parked cars that we can pass.
if(bIsOtherCarStationary || pOtherVeh->m_nVehicleFlags.bForceOtherVehiclesToOvertakeThisVehicle
|| pOtherVeh == m_pVehicleToIgnoreWhenStopping)
{
//only ambients can be blocked close
const bool bDriverIsPlayer = pVeh->GetDriver() && pVeh->GetDriver()->IsPlayer();
bool bOtherCarWillNeverMove = !pOtherVeh->GetDriver() || pOtherVeh->GetDriver()->IsDead();
const bool bBlockedClose = m_ObstructionInformation.m_uFlags.IsFlagSet(ObstructionInformation::IsCompletelyObstructedClose)
&& !pVeh->PopTypeIsMission()
&& (!pOtherVeh->GetDriver() || !pOtherVeh->GetDriver()->IsPlayer())
&& !pVeh->IsLawEnforcementVehicle()
&& !pVeh->m_nVehicleFlags.bIsFireTruckOnDuty
&& !pVeh->m_nVehicleFlags.bIsAmbulanceOnDuty
&& !m_ObstructionInformation.m_uFlags.IsFlagSet(ObstructionInformation::IsCompletelyObstructedBySingleObstruction)
&& pVeh->GetAiXYSpeed() < 1.0f
&& !bDriverIsPlayer
&& !bOtherCarWillNeverMove;
if(carAvoidanceLevel > CAR_AVOIDANCE_NONE && IsThereRoomToOvertakeVehicle(pVeh, pOtherVeh, true)
&& !bBlockedClose)
{
//when we're overtaking a stationary car, make everyone else wait for us
//or else Bad Things happen
pVeh->m_nVehicleFlags.bIsOvertakingStationaryCar = true;
if(pVeh->GetAiVelocity().XYMag() < 1.0f)
{
pOtherVeh->WeAreBlockingSomeone(); // If the car is abandoned this may cause the car to be removed by ambient peds.(thiefs or police)
}
fMaxSpeedForThisCar = rage::Min(fMaxSpeedForThisCar, OriginalMaxSpeed * fPercentOfMaxSpeedWhenOvertakingStationary);
fMaxSpeed = rage::Min(fMaxSpeed, fMaxSpeedForThisCar);
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
return;
}
else
{
if(pVeh->GetAiVelocity().XYMag() < 1.0f)
{
pOtherVeh->WeAreBlockingSomeone(); // If the car is abandoned this may cause the car to be removed by ambient peds.(thiefs or police)
}
fMaxSpeedForThisCar = 0.0f;
fMaxSpeed = fMaxSpeedForThisCar; // Wait until we are clear to overtake
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped(); // Make sure we don't go and start a 3point turn.
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
return;
}
}
m_bSlowingDownForCar = true;
if (CollisionT < m_fMostImminentCollisionThisFrame)
{
m_pOptimization_pLastCarWeSlowedFor = pOtherVeh;
m_fMostImminentCollisionThisFrame = CollisionT;
}
//m_bShouldChangeLanesForTraffic = CanSwitchLanesToAvoidObstruction(pVeh, pOtherVeh, OriginalMaxSpeed);
//keep track of the obstructing vehicle
pVeh->GetIntelligence()->m_pObstructingEntity = pOtherVeh;
// If we are coming down a slope we should slow down earlier so we don't bump into car in front of us.
float slopeMultiplier = 1.0f + 4.0f * 0.0f;//rage::Max(0.0f, FindMeasureForSlope(pVeh));//TODO add this in
Assertf((slopeMultiplier < 9.0f),"Slope greater than 2:1; path nodes are probably set up wrong, please check pos %f, %f, %f.", pVeh->GetVehiclePosition().GetXf(), pVeh->GetVehiclePosition().GetYf(), pVeh->GetVehiclePosition().GetZf());
CollisionT /= slopeMultiplier;
//use the bounding box of the car we're stopping for, not our own.
//this way cars naturally leave space for buses, rather than buses leaving space for small cars
const float fOtherVehBoundingBoxMaxY = pOtherVeh->GetBoundingBoxMax().y;
const u32 nMsHowLongAgoWeWereHonkedAt = fwTimer::GetTimeInMilliseconds() - pVeh->GetIntelligence()->LastTimeHonkedAt;
const bool bHasBeenHonkedAtRecently = pVeh->GetIntelligence()->LastTimeHonkedAt > 0
&& nMsHowLongAgoWeWereHonkedAt < 5000 && nMsHowLongAgoWeWereHonkedAt > CDriverPersonality::FindDelayBeforeRespondingToVehicleHonk(pOtherVeh->GetDriver(), pOtherVeh);
const bool bAllowPullForwardAfterBeingHonkedAt = CDriverPersonality::WillRespondToOtherVehiclesHonking(pOtherDriver, pOtherVeh);
// if (bHasBeenHonkedAtRecently && bAllowPullForwardAfterBeingHonkedAt)
// {
// grcDebugDraw::Sphere(pVeh->GetVehiclePosition(), 1.5f, Color_purple, false, 10);
// }
const float fDriveStopDistNew = !bHasBeenHonkedAtRecently || !bAllowPullForwardAfterBeingHonkedAt ? fOtherVehBoundingBoxMaxY : fOtherVehBoundingBoxMaxY * 0.5f;
float fDriveStopDistMultiplier = CDriverPersonality::GetStopDistanceMultiplierForOtherCars(pVeh->GetDriver(), pVeh);
//if the other car is stationary, and we're here, it means we want to overtake it but we think the route ahead is blocked
//by oncoming traffic. if so, pad out the distance we stop for it here--so when we decide to go, we'll have more room -JM
if (bIsOtherCarStationary)
{
fDriveStopDistMultiplier *= 2.0f;
}
if (((!bPreventFullStop && (useNewVehicleSlowdownTest && fDistToCollision < fDriveStopDistNew * fDriveStopDistMultiplier))
|| (!useNewVehicleSlowdownTest && CollisionT < CDriverPersonality::GetStopDistanceForOtherCarsOld(pVeh->GetDriver(), pVeh) / OriginalMaxSpeed)))
{
fMaxSpeedForThisCar = 0.0f;
bUseCloseRelSpeedPercentage = true;
//I don't think we need this anymore--FindMaximumSpeed will look at the MaxSpeed
//and decide to call this if it's zero. We may not want to call this here as
//we could decide below to start avoiding this guy, and we might want to start a
//3 point turn if we're stuck, which this would prevent
//pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped(); // Make sure we don't go and start a 3point turn.
//this happens at the end of the function now, in case we want to
//bail out of setting our speed to 0.0 because the other car's
//relative velocity is high enough that we dont have to stop for it
//m_pOptimization_pLastCarBlockingUs = pOtherVeh;
if (bPersonalityAllowsHonking)
{
const bool bOtherDriverIsPlayer = pOtherDriver && pOtherDriver->IsPlayer();
if(bOtherDriverIsPlayer)
{
if (pVeh->m_iNextValidHornTime < fwTimer::GetTimeInMilliseconds())
{
//don't honk for three seconds - enough to prevent this car honking until driven by
pVeh->m_iNextValidHornTime = fwTimer::GetTimeInMilliseconds() + 1500;
//B*1927556, lots more cars in NG, lets reduce number of horns on highways
float fRandom = fwRandom::GetRandomNumberInRange(0.0f, 1.0f);
if(fRandom < (pOtherDriver->GetPlayerInfo()->GetPlayerDataPlayerOnHighway() ? 0.25f : 0.5f))
{
HelperMaybeDoLongHorn(pVeh, pOtherVeh, bOtherDriverIsPlayer, bShouldPlayCarHorn, bShouldUseLongHorn);
}
}
}
else
{
HelperMaybeDoLongHorn(pVeh, pOtherVeh, bOtherDriverIsPlayer, bShouldPlayCarHorn, bShouldUseLongHorn);
}
}
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), 2.0f, Color_red, false);
}
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
char debugText[100];
sprintf(debugText, "Blocked by car:%.1f NotStuck:%d(%d)", CollisionT, pVeh->GetIntelligence()->LastTimeNotStuck, fwTimer::GetTimeInMilliseconds()-pVeh->GetIntelligence()->LastTimeNotStuck);
grcDebugDraw::Text(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()) + Vector3(0.f,0.f,2.5f), CRGBA(255, 255, 255, 255), debugText);
}
#endif
}
// If we are within 3m we drive very slowly.
else if(!useNewVehicleSlowdownTest && fDistToCollision < CDriverPersonality::GetDriveSlowDistanceForOtherCars(pVeh->GetDriver(), pVeh))
{
fMaxSpeedForThisCar = rage::Min((fMaxSpeedForThisCar), 1.0f);
pVeh->GetIntelligence()->UpdateCarHasReasonToBeStopped(); // Make sure we don't go and start a 3point turn.
if (bPersonalityAllowsHonking)
{
HelperMaybeDoLongHorn(pVeh, pOtherVeh, bOtherDriverIsPlayer, bShouldPlayCarHorn, bShouldUseLongHorn);
}
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), 2.0f, Color_orange, false);
}
#endif //__BANK
}
else
{
// Slow down according to distance/initial speed.
if (!useNewVehicleSlowdownTest)
{
CollisionT -= CDriverPersonality::GetDriveSlowDistanceForOtherCars(pVeh->GetDriver(), pVeh) / OriginalMaxSpeed;
// CollisionT = MAX(0.0f, (CollisionT - 0.3f)*(1.0f / 1.2f));
fMaxSpeedForThisCar = rage::Min((fMaxSpeedForThisCar), CollisionT * OriginalMaxSpeed);
}
else
{
float fRatio = (fDistToCollision /*- fTweakDistance*/) / fDistToStop;
fRatio = Clamp(fRatio, 0.0f, 1.0f);
fMaxSpeedForThisCar = Clamp(fRatio * OriginalMaxSpeed, 0.0f, fMaxSpeedForThisCar );
}
if(OriginalMaxSpeed >= 1.0f)
{
fMaxSpeedForThisCar = rage::Max(fMaxSpeedForThisCar, 1.0f);
}
#if __BANK
if (CVehicleIntelligence::m_bDisplayDebugSlowForTraffic && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition()), 2.0f, Color_yellow2, false);
}
#endif //__DEV
// Possible flash the headlights, etc.
if(/*!m_bShouldChangeLanesForTraffic && */ShouldGiveWarning(pVeh, pOtherVeh))
{
pVeh->GiveWarning();
}
}
}
// There is an exception: If the two cars are on collision course and
// in fact in a deadlock situation, one of them is given the green
// light. This might cause a collision but at least it keeps things
// moving.
// We also test whether the cars have been stationary for a while.
static dev_float s_fMaxTValueForDeadlockResolution = 1.0f;
if(CollisionT >= 0.0f && CollisionT < s_fMaxTValueForDeadlockResolution)
{
Assert(pOtherVeh);
if(CollisionT < m_fSmallestCollisionTSoFar) // This makes sure we store the closest car blocking us(otherwise some emergency measures later on don't work)
{
m_fSmallestCollisionTSoFar = CollisionT;
pVeh->GetIntelligence()->m_pCarThatIsBlockingUs = pOtherVeh;
}
//allow us to fall through into here if we've already got the DF_AvoidingCarsUntilHere flag set.
//the whole point of this code is to allow us to move, and once we move, the timer is reset,
//preventing us from getting in here
if(m_bAvoidingCarsUntilClear || ((fwTimer::GetTimeInMilliseconds() - pVeh->GetIntelligence()->LastTimeNotStuck > 4000) &&
(fwTimer::GetTimeInMilliseconds() - pOtherVeh->GetIntelligence()->LastTimeNotStuck) > 4000))
{
CTaskVehicleMissionBase *pTask = pOtherVeh->GetIntelligence()->GetActiveTask();
if(/*!IsDrivingFlagSet(DF_AvoidingCarsUntilClear) &&*/ IsDrivingFlagSet(DF_StopForCars) && pTask && pTask->IsDrivingFlagSet(DF_StopForCars) )
{
Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
Vector3 OtherVehForwardVector(VEC3V_TO_VECTOR3(pOtherVeh->GetVehicleForwardDirection()));
DotPr = VehForwardVector.x * OtherVehForwardVector.x +
VehForwardVector.y * OtherVehForwardVector.y;
if(!pOtherDriver || !pOtherDriver->IsPlayer())
{
if(DotPr < -0.0f || pOtherVeh->GetIntelligence()->m_pCarThatIsBlockingUs == pVeh)
{
if(pOtherVeh->GetIntelligence()->m_pCarThatIsBlockingUs != pVeh
|| IsFirstCarBestOneToGo(pVeh, pOtherVeh, m_bAvoidingCarsUntilClear) ) // Decide which one is in the best position to avoid the other.(If the other car isn't waiting on us we should go; he is probably blocked by a 3rd car)
{
fMaxSpeedForThisCar = rage::Max(fMaxSpeedForThisCar, OriginalMaxSpeed * 0.2f);
//SetDrivingFlag(DF_AvoidingCarsUntilClear, true);
m_bAvoidingCarsUntilClear = true;
m_startTimeAvoidCarsUntilClear = fwTimer::GetTimeInMilliseconds();
SetCruiseSpeed(rage::Min(GetCruiseSpeed(), 10.0f));
m_bStoppingForTraffic = false; // Do this or the car will wait a little before starting to go and by that time will have reverted its driving mode.
}
}
}
}
}
}
if(fMaxSpeedForThisCar < 0.03f && pVeh->GetAiVelocity().XYMag() < 1.0f)
{
pOtherVeh->WeAreBlockingSomeone(); // If the car is abandoned this may cause the car to be removed by ambient peds.(thiefs or police)
}
if (bPersonalityAllowsHonking && bOtherDriverIsPlayer && !bShouldUseLongHorn)
{
//we don't want this to actually cause us to play the horn where we wouldn't before,
//just possibly use a long horn instead
bool bFakeShouldPlayHorn = false;
HelperMaybeDoLongHorn(pVeh, pOtherVeh, bOtherDriverIsPlayer, bFakeShouldPlayHorn, bShouldUseLongHorn);
}
static dev_float REL_SPEED_PERC_BASE = 0.9f;
static dev_float REL_SPEED_PERC_CLOSE_BASE = 0.5f;
mthRandom rngSpeedPerc(pVeh->GetRandomSeed());
const float fRelSpeedPercentageFar = rage::Min(0.95f, rngSpeedPerc.GetGaussian(REL_SPEED_PERC_BASE, 0.05f));
const float fRelSpeedPercentageClose = rngSpeedPerc.GetGaussian(REL_SPEED_PERC_CLOSE_BASE, 0.05f);
const float fRelSpeedPercentage = bUseCloseRelSpeedPercentage ? fRelSpeedPercentageClose : fRelSpeedPercentageFar;
// Never slow down much more than the relative speed
fMaxSpeedForThisCar = Max( fMaxSpeedForThisCar, fRelativeSpeed*fRelSpeedPercentage );
fMaxSpeed = Min(fMaxSpeed, fMaxSpeedForThisCar);
if (fMaxSpeedForThisCar < 0.03f) //the 0.03 came from the check above
{
m_pOptimization_pLastCarBlockingUs = pOtherVeh;
}
//This needs to be called before every return from this function.
HelperMaybePlayHorn(pVeh, bShouldPlayCarHorn, bShouldUseLongHorn, pHonkTarget);
}
//this test returns
float CTaskVehicleGoToPointWithAvoidanceAutomobile::Test2MovingSphereCollision(const CVehicle* const pVeh, const CVehicle* const pOtherVeh, const Vector3& vOurVel, const Vector3& vTheirVel, const float fBoundRadius)
{
//TODO: actually, this whole function looks like a really good candidate for vectorization
//const float fMyRadius = pVeh->GetBoundRadius();
const float fTheirRadius = pOtherVeh->GetBaseModelInfo()->GetBoundingSphereRadius() + pOtherVeh->GetForceAddToBoundRadius();//pOtherVeh->GetBoundRadius();
// #if __ASSERT
// const float fRadiusDiff = fTheirRadius - pOtherVeh->GetBoundRadius();
// Assertf(fRadiusDiff > -0.1f, "fRadiusDiff = %f", fRadiusDiff);
// #endif //__ASSERT
const Vec3V vOurPos = pVeh->GetVehiclePosition();
const Vec3V vTheirPos = pOtherVeh->GetVehiclePosition();
const ScalarV svfDistBtwn = DistFast(vOurPos, vTheirPos);
Vec3V vUsToThemNormalized(V_ZERO);
if (svfDistBtwn.Getf() >= SMALL_FLOAT)
{
vUsToThemNormalized = (vTheirPos - vOurPos) / svfDistBtwn;
}
//get the relative velocity in the direction of the position delta
//for vehicles moving fast in the same direction this should be small
const Vec3V vRelVelocity = RCC_VEC3V(vOurVel) - RCC_VEC3V(vTheirVel);
const ScalarV fRelSpeedInDeltaDirection = Dot(vUsToThemNormalized, vRelVelocity);
const ScalarV fNumerator = svfDistBtwn - ScalarV(fBoundRadius) - ScalarV(fTheirRadius);
return fRelSpeedInDeltaDirection.Getf() >= SMALL_FLOAT ? (fNumerator /fRelSpeedInDeltaDirection).Getf() : FLT_MAX;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Test2MovingRectCollision_OnlyFrontBumper
// PURPOSE : Test whether the points of the second rectangle fall within the
// first at any point. Returns the time of the first collision(0..1)
/////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleGoToPointWithAvoidanceAutomobile::Test2MovingRectCollision_OnlyFrontBumper(const CPhysical* const pVeh, const CPhysical* const pOtherVeh,
float OtherSpeedX, float OtherSpeedY,
const Vector3& ourNewDriveDir,
const Vector3& otherDriveDir,
const spdAABB& boundBox,
const spdAABB& otherBox,
const bool bOtherEntIsDoor)
{
float ReturnVal;
s32 OtherPoints;
Vector3 LineStart;
// We had to reshuffle the way the arguments are passed. Otherwise the optimiser
// would fuck stuff up. Basically arguments changed during the function call.
// A printf before and after the call resulted in different results.
Vector3 ourPosn = VEC3V_TO_VECTOR3(pVeh->GetTransform().GetPosition());
Vector3 hisPosn = VEC3V_TO_VECTOR3(pOtherVeh->GetTransform().GetPosition());
//Vector3 ourBBMax = pVeh->GetBoundingBoxMax();
const float OtherCarX = hisPosn.x;// - pVeh->GetPosition().x;
const float OtherCarY = hisPosn.y;// - pVeh->GetPosition().y;
const float OurCarX = ourPosn.x;
const float OurCarY = ourPosn.y;
const float OurBBHalfLengthPos = boundBox.GetMax().GetYf();
const float OurBBHalfWidth = boundBox.GetMax().GetXf();
const float OurBBHalfWidthNeg = -boundBox.GetMin().GetXf();
//const float OurBBHalfLengthNeg = -boundBox.GetMin().GetYf();
float OtherBBHalfLengthPos = otherBox.GetMax().GetYf();
const float OtherBBHalfWidth = otherBox.GetMax().GetXf();
const float OtherBBHalfWidthNeg = -otherBox.GetMin().GetXf(); //still positive to make below code more readable
float OtherBBHalfLengthNeg = -otherBox.GetMin().GetYf();
//really skinny doors have a way of messing up our shapetests below
//artificially fatten the doors
if (bOtherEntIsDoor)
{
OtherBBHalfLengthPos = rage::Max(ms_fMinLengthForDoors, OtherBBHalfLengthPos);
OtherBBHalfLengthNeg = rage::Max(ms_fMinLengthForDoors, OtherBBHalfLengthNeg);
}
// ReturnVal == LARGE_FLOAT means no collision.
ReturnVal = LARGE_FLOAT;
static dev_float s_fMaxTimeToLookAhead = 5.0f; //seconds
#if __DEV
TUNE_GROUP_BOOL(FOLLOW_AI_SLOW, bDrawBumperTests, false);
#endif //__DEV
// Front and Side perpendicular so:
// pOtherSide->x = otherDriveDir.y
// pOtherSide->y = -otherDriveDir.x
// NOTE: we now only do the 2 front points of the ai car. We're not really
// interested in collisions the rear point might cause(we can't do anything
// about that anyway)
// Calculate the 2 points of our bumper.
Vector3 BumperFrontRight, BumperFrontLeft, BumperNormal;
BumperFrontRight.x = OurCarX + OurBBHalfLengthPos*ourNewDriveDir.x + OurBBHalfWidth*ourNewDriveDir.y;
BumperFrontRight.y = OurCarY + OurBBHalfLengthPos*ourNewDriveDir.y - OurBBHalfWidth*ourNewDriveDir.x;
BumperFrontLeft.x = OurCarX + OurBBHalfLengthPos*ourNewDriveDir.x - OurBBHalfWidthNeg*ourNewDriveDir.y;
BumperFrontLeft.y = OurCarY + OurBBHalfLengthPos*ourNewDriveDir.y + OurBBHalfWidthNeg*ourNewDriveDir.x;
BumperNormal.x = ourNewDriveDir.x;
BumperNormal.y = ourNewDriveDir.y;
BumperFrontRight.z = ourPosn.z;
BumperFrontLeft.z = ourPosn.z;
Vector3 vOtherPoints[4];
vOtherPoints[0].x = OtherCarX + OtherBBHalfLengthPos*otherDriveDir.x + OtherBBHalfWidth*otherDriveDir.y;
vOtherPoints[0].y = OtherCarY + OtherBBHalfLengthPos*otherDriveDir.y - OtherBBHalfWidth*otherDriveDir.x;
vOtherPoints[0].z = 0.0f;
vOtherPoints[1].x = OtherCarX + OtherBBHalfLengthPos*otherDriveDir.x - OtherBBHalfWidthNeg*otherDriveDir.y;
vOtherPoints[1].y = OtherCarY + OtherBBHalfLengthPos*otherDriveDir.y + OtherBBHalfWidthNeg*otherDriveDir.x;
vOtherPoints[1].z = 0.0f;
vOtherPoints[2].x = OtherCarX - OtherBBHalfLengthNeg*otherDriveDir.x + OtherBBHalfWidth*otherDriveDir.y;
vOtherPoints[2].y = OtherCarY - OtherBBHalfLengthNeg*otherDriveDir.y - OtherBBHalfWidth*otherDriveDir.x;
vOtherPoints[2].z = 0.0f;
vOtherPoints[3].x = OtherCarX - OtherBBHalfLengthNeg*otherDriveDir.x - OtherBBHalfWidthNeg*otherDriveDir.y;
vOtherPoints[3].y = OtherCarY - OtherBBHalfLengthNeg*otherDriveDir.y + OtherBBHalfWidthNeg*otherDriveDir.x;
vOtherPoints[3].z = 0.0f;
const Vector3 vLineDelta = Vector3(OtherSpeedX, OtherSpeedY, 0.0f) * s_fMaxTimeToLookAhead;
//const Vector3 vLinePerp = Vector3(OtherSpeedY, -OtherSpeedX, 0.0f) * s_fMaxTimeToLookAhead;
float fTempDot1s[4];
float fTempDot2s[4];
for (int i = 0; i < 4; i++)
{
fTempDot1s[i] =(BumperFrontRight.x - vOtherPoints[i].x) * OtherSpeedY -(BumperFrontRight.y - vOtherPoints[i].y) * OtherSpeedX;
fTempDot2s[i] =(BumperFrontLeft.x - vOtherPoints[i].x) * OtherSpeedY -(BumperFrontLeft.y - vOtherPoints[i].y) * OtherSpeedX;
}
for(OtherPoints = 0; OtherPoints < 4; OtherPoints++)
{
LineStart = vOtherPoints[OtherPoints];
const Vector3 LineEnd = LineStart + vLineDelta;
// Now check whether the Line goes through the bumper.
const float DotProduct1 =(LineStart.x - BumperFrontRight.x) * BumperNormal.x +(LineStart.y - BumperFrontRight.y) * BumperNormal.y;
const float DotProduct2 =(LineEnd.x - BumperFrontRight.x) * BumperNormal.x +(LineEnd.y - BumperFrontRight.y) * BumperNormal.y;
if(DotProduct1 > 0.0f && DotProduct2 < 0.0f)
{
// Other points are on either side of our bumper. Potential collision.
// If the dot products of this point to each of the front bumper points have the same sign,
// they must both be to one side of the vehicle, therefore the vehicles won't collide.
// Exception: both bumper points are in between the bumpers of the opposing car.
// We'll test for that later
const float CollTime = (DotProduct1 * s_fMaxTimeToLookAhead) /(DotProduct1 - DotProduct2);
const bool bBumpersAreOnDifferentSidesOfPoint = fTempDot1s[OtherPoints] * fTempDot2s[OtherPoints] < 0.0f;
if(bBumpersAreOnDifferentSidesOfPoint)
{
// We have a collision.
ReturnVal = rage::Min(ReturnVal, CollTime);
}
else if (CollTime < ReturnVal)
{
//if this potential collision may be closer than the one we already detected,
//do a more accurate test at the t-value of the collision
const Vector3 vDeltaToPotentialCollision = Vector3(OtherSpeedX, OtherSpeedY, 0.0f) * CollTime;
const Vector3 vPoint0 = vOtherPoints[0] + vDeltaToPotentialCollision;
const Vector3 vPoint1 = vOtherPoints[1] + vDeltaToPotentialCollision;
const Vector3 vPoint2 = vOtherPoints[2] + vDeltaToPotentialCollision;
const Vector3 vPoint3 = vOtherPoints[3] + vDeltaToPotentialCollision;
#if __DEV
if (bDrawBumperTests)
{
Vector3 vPoint0Draw = vPoint0;
Vector3 vPoint1Draw = vPoint1;
Vector3 vPoint2Draw = vPoint2;
Vector3 vPoint3Draw = vPoint3;
vPoint0Draw.z = vPoint1Draw.z = vPoint2Draw.z = vPoint3Draw.z = hisPosn.z;
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vPoint0Draw), VECTOR3_TO_VEC3V(vPoint1Draw), Color_purple);
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vPoint1Draw), VECTOR3_TO_VEC3V(vPoint3Draw), Color_purple);
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vPoint3Draw), VECTOR3_TO_VEC3V(vPoint2Draw), Color_purple);
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vPoint2Draw), VECTOR3_TO_VEC3V(vPoint0Draw), Color_purple);
CVehicle::ms_debugDraw.AddSphere(VECTOR3_TO_VEC3V(BumperFrontRight), 0.1f, Color_yellow);
CVehicle::ms_debugDraw.AddSphere(VECTOR3_TO_VEC3V(BumperFrontLeft), 0.1f, Color_yellow);
}
#endif //__DEV
//project each point into the future
if (!bOtherEntIsDoor)
{
if (geom2D::IntersectPointFlat(BumperFrontLeft.x, BumperFrontLeft.y, vPoint0.x, vPoint0.y,
vPoint1.x, vPoint1.y, vPoint2.x, vPoint2.y, vPoint3.x, vPoint3.y)
&& geom2D::IntersectPointFlat(BumperFrontRight.x, BumperFrontRight.y, vPoint0.x, vPoint0.y,
vPoint1.x, vPoint1.y, vPoint2.x, vPoint2.y, vPoint3.x, vPoint3.y))
{
////////////////////////////////////
// Vec3V vVehPos = pVeh->GetVehiclePosition();
// Vec3V vUpTopPos = vVehPos;
// vUpTopPos.SetZf(vVehPos.GetZf() + 10.0f);
// CVehicle::ms_debugDraw.AddLine(vVehPos, vUpTopPos, Color_purple);
///////////////////////////////////
ReturnVal = CollTime; //already checked that it was less than ReturnVal above
}
}
else
{
//float fDummyT1, fDummyT2;
if (geom2D::IntersectPointFlat(BumperFrontLeft.x, BumperFrontLeft.y, vPoint0.x, vPoint0.y,
vPoint1.x, vPoint1.y, vPoint2.x, vPoint2.y, vPoint3.x, vPoint3.y)
|| geom2D::IntersectPointFlat(BumperFrontRight.x, BumperFrontRight.y, vPoint0.x, vPoint0.y,
vPoint1.x, vPoint1.y, vPoint2.x, vPoint2.y, vPoint3.x, vPoint3.y)
/*|| geom2D::Test2DSegVsSegFast(fDummyT1, fDummyT2, BumperFrontLeft.x, BumperFrontLeft.y, BumperFrontRight.x, BumperFrontRight.y
, vPoint0.x, vPoint0.y, vPoint1.x, vPoint1.y)
|| geom2D::Test2DSegVsSegFast(fDummyT1, fDummyT2, BumperFrontLeft.x, BumperFrontLeft.y, BumperFrontRight.x, BumperFrontRight.y
, vPoint2.x, vPoint2.y, vPoint3.x, vPoint3.y)*/)
{
////////////////////////////////////
// Vec3V vVehPos = pVeh->GetVehiclePosition();
// Vec3V vUpTopPos = vVehPos;
// vUpTopPos.SetZf(vVehPos.GetZf() + 10.0f);
// CVehicle::ms_debugDraw.AddLine(vVehPos, vUpTopPos, Color_purple);
///////////////////////////////////
ReturnVal = CollTime; //already checked that it was less than ReturnVal above
}
}
}
}
}
return(ReturnVal);
}
// void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindRoadEdgesUsingNodeList(AvoidanceTaskObstructionData& obstructionData)
// {
// const CVehicle* pVeh = obstructionData.pVeh;
// Assert(pVeh);
// CVehicleNodeList* pNodeList = pVeh->GetIntelligence()->GetNodeList();
//
// //for now, don't do this if we don't have a nodelist
// //TODO: replace this with a version that uses the follow route
// if (!pNodeList)
// {
// return;
// }
//
// const float fVehicleHalfWidth = obstructionData.vBoundBoxMax.GetXf();
//
// const int iNumNodes = CVehicleNodeList::CAR_MAX_NUM_PATHNODES_STORED;
//
// bool bClampRoadEdgesToCenter = obstructionData.carAvoidanceLevel <= CAR_AVOIDANCE_LITTLE
// && !IsDrivingFlagSet(DF_AvoidingCarsUntilClear)
// && !obstructionData.bIsCurrentlyOnJunction;
//
// //this is a hack to allow cars to steer on the other side of the road when avoiding abandoned/stopped playercars,
// //while keeping them in their lanes for avoiding moving traffic -JM
// const CPhysical* pEntitySteeringAround = pVeh->GetIntelligence()->GetEntitySteeringAround();
// const CVehicle* pVehicleSteeringAround = pEntitySteeringAround && pEntitySteeringAround->GetIsTypeVehicle() ? static_cast<const CVehicle*>(pEntitySteeringAround) : NULL;
// if (bClampRoadEdgesToCenter && pVehicleSteeringAround &&
// (IsThisAStationaryCar(pVehicleSteeringAround) || pVehicleSteeringAround->m_nVehicleFlags.bForceOtherVehiclesToOvertakeThisVehicle)
// && IsThereRoomToOvertakeVehicle(pVeh, pVehicleSteeringAround, true))
// {
// bClampRoadEdgesToCenter = false;
// }
//
// const Vector3 vBumperPos = RCC_VECTOR3(obstructionData.vBumperCenter);
// const Vec2f vVehiclePos2d(vBumperPos.x, vBumperPos.y);
//
// Vector3 vLastLeftEnd(Vector3::ZeroType), vLastRightEnd(Vector3::ZeroType);
// int iLastEndIndex = -1;
// for (int i = 0; i < iNumNodes-1; i++)
// {
// Vector3 vLeftEnd, vRightEnd, vLeftStart, vRightStart;
// CNodeAddress fromNode = pNodeList->GetPathNodeAddr(i);
// CNodeAddress toNode = pNodeList->GetPathNodeAddr(i+1);
// CNodeAddress nextNode;
// if (i < iNumNodes-2)
// {
// nextNode = pNodeList->GetPathNodeAddr(i+2);
// }
//
// if (!ThePaths.GetBoundarySegmentsBetweenNodes(fromNode, toNode, nextNode, bClampRoadEdgesToCenter, vLeftEnd, vRightEnd, vLeftStart, vRightStart, fVehicleHalfWidth))
// {
// AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
// newSeg.m_Valid = false;
// continue;
// }
//
// Vec2f carToLeft = Vec2f(vLeftStart.x, vLeftStart.y) - vVehiclePos2d;
// Vec2f carToRight = Vec2f(vRightStart.x, vRightStart.y) - vVehiclePos2d;
// Vec2f leftToRight = Vec2f(vLeftStart.x, vLeftStart.y) - Vec2f(vRightStart.x, vRightStart.y);
//
// const float fDotLeft = Dot(leftToRight, carToLeft);
// const float fDotRight = Dot(leftToRight, carToRight);
// if (fDotRight * fDotLeft > 0.0f)
// {
// // both positive or both negative
// //we're outside the bounds, try again with wider edges
// if (!ThePaths.GetBoundarySegmentsBetweenNodes(fromNode, toNode, nextNode, bClampRoadEdgesToCenter, vLeftEnd, vRightEnd, vLeftStart, vRightStart, 0.0f))
// {
// AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
// newSeg.m_Valid = false;
// continue;
// }
// }
//
// //if the last coords we got were from the previous node in our path,
// //just use them
// if (iLastEndIndex > -1 && iLastEndIndex == i-1)
// {
// vLeftStart = vLastLeftEnd;
// vRightStart = vLastRightEnd;
// }
//
// vLastLeftEnd = vLeftEnd;
// vLastRightEnd = vRightEnd;
// iLastEndIndex = i;
//
// AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
// newSeg.m_LeftStart = Vec2f(vLeftStart.x, vLeftStart.y);
// newSeg.m_LeftEnd = Vec2f(vLeftEnd.x, vLeftEnd.y);
// newSeg.m_RightStart = Vec2f(vRightStart.x, vRightStart.y);
// newSeg.m_RightEnd = Vec2f(vRightEnd.x, vRightEnd.y);
// newSeg.m_Valid = true;
// }
// }
void CTaskVehicleGoToPointWithAvoidanceAutomobile::FindRoadEdgesUsingFollowRoute(AvoidanceTaskObstructionData& obstructionData)
{
const CVehicle* pVeh = obstructionData.pVeh;
Assert(pVeh);
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
//for now, don't do this if we don't have a followroute
if (!pFollowRoute)
{
return;
}
const float fVehicleHalfWidth = obstructionData.vBoundBoxMax.GetXf();
const int iNumNodes = pFollowRoute->GetNumPoints();
bool bClampRoadEdgesToCenter = obstructionData.carAvoidanceLevel <= CAR_AVOIDANCE_LITTLE
&& !m_bAvoidingCarsUntilClear
&& !obstructionData.bIsCurrentlyOnJunction;
//this is a hack to allow cars to steer on the other side of the road when avoiding abandoned/stopped playercars,
//while keeping them in their lanes for avoiding moving traffic -JM
const CPhysical* pEntitySteeringAround = pVeh->GetIntelligence()->GetEntitySteeringAround();
const CVehicle* pVehicleSteeringAround = pEntitySteeringAround && pEntitySteeringAround->GetIsTypeVehicle() ? static_cast<const CVehicle*>(pEntitySteeringAround) : NULL;
if (bClampRoadEdgesToCenter
&& pVehicleSteeringAround
&& (IsThisAStationaryCar(pVehicleSteeringAround) || pVehicleSteeringAround->m_nVehicleFlags.bForceOtherVehiclesToOvertakeThisVehicle)
&& IsThereRoomToOvertakeVehicle(pVeh, pVehicleSteeringAround, true))
{
bClampRoadEdgesToCenter = false;
}
const Vector3 vBumperPos = RCC_VECTOR3(obstructionData.vBumperCenter);
const Vec2f vVehiclePos2d(vBumperPos.x, vBumperPos.y);
Vector3 vLastLeftEnd(Vector3::ZeroType), vLastRightEnd(Vector3::ZeroType);
int iLastEndIndex = -1;
AvoidanceTaskObstructionData::RoadSegment* pLastSeg = NULL;
AvoidanceTaskObstructionData::RoadSegment* pLastLastSeg = NULL;
const CRoutePoint* pRoutePoints = pFollowRoute->GetRoutePoints();
for (int i = 0; i < iNumNodes-1; i++)
{
Vector3 vLeftEnd, vRightEnd, vLeftStart, vRightStart;
int iNextIndex = -1;
if (i < iNumNodes-2)
{
iNextIndex = i+2;
}
const CRoutePoint& rFromPoint = pRoutePoints[i];
//on single-track roads, we want to prefer going right, and since the "avoid road edges"
//weight is quadratic, moving them in closer by the half-width increases the weight of the right side
//at a rate much quicker than on the left. this can overwhelm the score of the "prefer going right
//on single track roads" rule
const float fHalfWidthToUseForFirstTestLeftSide = fVehicleHalfWidth;
float fHalfWidthToUseForFirstTestRightSide = fVehicleHalfWidth;
static dev_float s_fHackExtraWidthBase = 1.0f;
float fHalfWidthToUseForSecondTestRightSide = 0.0f;
if (rFromPoint.m_bIsSingleTrack)
{
fHalfWidthToUseForFirstTestRightSide = -s_fHackExtraWidthBase;
fHalfWidthToUseForSecondTestRightSide = -s_fHackExtraWidthBase;
}
else if (rFromPoint.m_bIsHighway)
{
fHalfWidthToUseForFirstTestRightSide = 0.0f;
}
if (!pFollowRoute->GetBoundarySegmentsBetweenPoints(i, i+1, iNextIndex, bClampRoadEdgesToCenter, vLeftEnd, vRightEnd, vLeftStart, vRightStart, fHalfWidthToUseForFirstTestLeftSide, fHalfWidthToUseForFirstTestRightSide, true))
{
AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
newSeg.m_Valid = false;
continue;
}
Vec2f carToLeft = Vec2f(vLeftStart.x, vLeftStart.y) - vVehiclePos2d;
Vec2f carToRight = Vec2f(vRightStart.x, vRightStart.y) - vVehiclePos2d;
Vec2f leftToRight = Vec2f(vLeftStart.x, vLeftStart.y) - Vec2f(vRightStart.x, vRightStart.y);
const float fDotLeft = Dot(leftToRight, carToLeft);
const float fDotRight = Dot(leftToRight, carToRight);
if (fDotRight * fDotLeft > 0.0f)
{
// both positive or both negative
//we're outside the bounds, try again with wider edges
if (!pFollowRoute->GetBoundarySegmentsBetweenPoints(i, i+1, iNextIndex, bClampRoadEdgesToCenter, vLeftEnd, vRightEnd, vLeftStart, vRightStart, 0.0f, fHalfWidthToUseForSecondTestRightSide, true))
{
AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
newSeg.m_Valid = false;
continue;
}
}
#if __BANK
if (ms_bDisplayAvoidanceRoadSegments && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Color32 color = Color_white;
if (pRoutePoints[i].m_bJunctionNode)
{
color = Color_green;
}
if (pRoutePoints[i+1].m_bJunctionNode)
{
color = Color_magenta;
}
if (iNextIndex >= 0 && pRoutePoints[iNextIndex].m_bJunctionNode)
{
color = Color_cyan;
}
const Vector3 vHeightOffset(0.0f, 0.0f, 5.0f);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vLeftStart + vHeightOffset), VECTOR3_TO_VEC3V(vLeftEnd + vHeightOffset), 0.5f, color, -1);
grcDebugDraw::Line(vLeftEnd + vHeightOffset, vRightEnd + vHeightOffset, color, color, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vRightStart + vHeightOffset), VECTOR3_TO_VEC3V(vRightEnd + vHeightOffset), 0.5f, color, -1);
grcDebugDraw::Line(vRightStart + vHeightOffset, vLeftStart + vHeightOffset, color, color, -1);
}
#endif // __BANK
Vec2f thisLeftStart = Vec2f(vLeftStart.x, vLeftStart.y);
Vec2f thisRightStart = Vec2f(vRightStart.x, vRightStart.y);
//if the last coords we got were from the previous node in our path, just use them
if (iLastEndIndex > -1 && iLastEndIndex == i-1)
{
if (!rFromPoint.m_bIgnoreForNavigation && pRoutePoints[iLastEndIndex].m_bJunctionNode && pLastSeg && pLastLastSeg)
{
//work out what way we're going at this junction
Vector3 vLastToJunction = VEC3V_TO_VECTOR3(pRoutePoints[iLastEndIndex].GetPosition() - pRoutePoints[iLastEndIndex-1].GetPosition());
Vec2f lastToJunction = Vec2f(vLastToJunction.x, vLastToJunction.y);
Vector3 vLastToCurrent = VEC3V_TO_VECTOR3(pRoutePoints[i].GetPosition() - pRoutePoints[iLastEndIndex-1].GetPosition());
Vec2f lastToCurrent = Vec2f(vLastToCurrent.x, vLastToCurrent.y);
//check we aren't looping back onto previous point
if(IsZeroAll(lastToJunction) || IsZeroAll(lastToCurrent))
{
obstructionData.m_RoadSegments.clear();
break;
}
lastToJunction = NormalizeFast(lastToJunction);
lastToCurrent = NormalizeFast(lastToCurrent);
float fDotJunction = Dot(lastToJunction, lastToCurrent);
if(fDotJunction < 0.9f)
{
float fRoadWidthLeft=0.0f, fRoadWidthRight=0.0f;
pRoutePoints[iLastEndIndex-1].FindRoadBoundaries(fRoadWidthLeft, fRoadWidthRight);
static float s_fRoadWidthScalar = 0.25f;
Vec2f vJunctionCenter = Vec2f(pRoutePoints[iLastEndIndex].GetPosition().GetXf(),pRoutePoints[iLastEndIndex].GetPosition().GetYf());
if(Cross(lastToJunction, lastToCurrent) < 0.0f)
{
//going right
//average start positions of segments either side of junction
Vec2f lastLastStartToThisStart = (pLastLastSeg->m_RightStart + thisRightStart) * 0.5f;
Vec2f vLastStartToJunctionCenter = vJunctionCenter - lastLastStartToThisStart;
vLastStartToJunctionCenter = Normalize(vLastStartToJunctionCenter);
//add small buffer towards junction center to round out corner
lastLastStartToThisStart += vLastStartToJunctionCenter * fRoadWidthRight * s_fRoadWidthScalar;
pLastLastSeg->m_RightEnd = lastLastStartToThisStart;
pLastSeg->m_RightStart = lastLastStartToThisStart;
}
else
{
//going left
Vec2f lastLastStartToThisStart = (pLastLastSeg->m_LeftStart + thisLeftStart) * 0.5f;
Vec2f vLastStartToJunctionCenter = vJunctionCenter - lastLastStartToThisStart;
vLastStartToJunctionCenter = Normalize(vLastStartToJunctionCenter);
lastLastStartToThisStart += vLastStartToJunctionCenter * fRoadWidthLeft * s_fRoadWidthScalar;
pLastLastSeg->m_LeftEnd = lastLastStartToThisStart;
pLastSeg->m_LeftStart = lastLastStartToThisStart;
}
}
else
{
//going straight
//simply create segment that makes straight line across junction
Vec2f lastLastStartToThisStartRight = (pLastLastSeg->m_RightStart + thisRightStart) * 0.5f;
Vec2f lastLastStartToThisStartLeft = (pLastLastSeg->m_LeftStart + thisLeftStart) * 0.5f;
pLastLastSeg->m_LeftEnd = lastLastStartToThisStartLeft;
pLastLastSeg->m_RightEnd = lastLastStartToThisStartRight;
pLastSeg->m_LeftStart = lastLastStartToThisStartLeft;
pLastSeg->m_RightStart = lastLastStartToThisStartRight;
}
pLastSeg->m_LeftEnd = thisLeftStart;
pLastSeg->m_RightEnd = thisRightStart;
}
else
{
vLeftStart = vLastLeftEnd;
vRightStart = vLastRightEnd;
}
}
vLastLeftEnd = vLeftEnd;
vLastRightEnd = vRightEnd;
iLastEndIndex = i;
AvoidanceTaskObstructionData::RoadSegment& newSeg = obstructionData.m_RoadSegments.Append();
newSeg.m_LeftStart = Vec2f(vLeftStart.x, vLeftStart.y);
newSeg.m_LeftEnd = Vec2f(vLeftEnd.x, vLeftEnd.y);
newSeg.m_RightStart = Vec2f(vRightStart.x, vRightStart.y);
newSeg.m_RightEnd = Vec2f(vRightEnd.x, vRightEnd.y);
newSeg.m_Valid = true;
//if this is the last segment, and the last point on the route
//is a junction, don't add any segment. if a junction weren't
//the last node, we'd remove the segment in the direction we're turning,
//so since we don't know which way that may be, just don't prevent them
//from going either way for now
const CRoutePoint& rToPoint = pRoutePoints[i+1];
if (IsDrivingFlagSet(DF_ExtendRouteWithWanderResults) && i==iNumNodes-2 && rToPoint.m_bJunctionNode)
{
newSeg.m_Valid = false;
}
pLastLastSeg = pLastSeg;
pLastSeg = &newSeg;
}
}
// bool CTaskVehicleGoToPointWithAvoidanceAutomobile::DoesLineSegmentCrossRoadBoundariesUsingNodeList(const AvoidanceTaskObstructionData& obstructionData, const float fHeading, const float fSegmentLength, float& fDistToIntersectionOut) const
// {
// const CVehicle* pVeh = obstructionData.pVeh;
//
// Assert(pVeh);
// CVehicleNodeList* pNodeList = pVeh->GetIntelligence()->GetNodeList();
//
// // Fine to have no node list, no extents to check.
// if (!pNodeList)
// {
// return false;
// }
//
// //if this is 0, it's probably because we didn't build this array due to not
// //having a nodelist. while the above early return for !pNodeList is probably
// //sufficient, I'm adding this in the interest of robustness. if something were to happen
// //to give us a valid nodelist in the meantime, we don't care if we didn't
// //build the m_RoadSegments list up with information about the nodelist already
// if (obstructionData.m_RoadSegments.GetCount() == 0)
// {
// return false;
// }
//
// fDistToIntersectionOut = -1.0f;
//
// //if we have a valid nodelist,
// //iterate through each segment of our path
// //generate edges
// //test the segment against each edge
// //return early if we find a collision
// Vector3 vehDriveDir = RCC_VECTOR3(obstructionData.vNormalizedDriveDir);
// const float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
// //const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
// // Use the coordinates of our front bumper.
// Vector3 vBumperPos = RCC_VECTOR3(obstructionData.vBumperCenter);
// const Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()));
// const Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
// const Vector3 Offset1 = fSegmentLength * rage::Cosf(vehDriveOrientation - fHeading) * VehForwardVector;
// const Vector3 Offset2 = fSegmentLength * rage::Sinf(vehDriveOrientation - fHeading) * VehRightVector;
// const Vector3 vSearchPos = vBumperPos + Offset1 + Offset2;
// const Vec2f vVehiclePos2d(vBumperPos.x, vBumperPos.y);
// const Vec2f vSearchPos2d(vSearchPos.x, vSearchPos.y);
//
// //? const bool bClampRoadEdgesToCenter = carAvoidanceLevel <= CAR_AVOIDANCE_LITTLE;
//
// const int iNumNodes = CVehicleNodeList::CAR_MAX_NUM_PATHNODES_STORED;
//
// s32 iTargetNode = pNodeList->GetTargetNodeIndex();
// s32 iOldNode = iTargetNode - 1;
//
// //if we aren't on the road at all, don't let this code prevent us from getting back on
// const AvoidanceTaskObstructionData::RoadSegment& currentRoadSeg = obstructionData.m_RoadSegments[Max(iOldNode, 0)];
// if (currentRoadSeg.m_Valid)
// {
// Vec2f carToLeft = currentRoadSeg.m_LeftStart - vVehiclePos2d;
// Vec2f carToRight = currentRoadSeg.m_RightStart - vVehiclePos2d;
// Vec2f leftToRight = currentRoadSeg.m_LeftStart - currentRoadSeg.m_RightStart;
//
// const float fDotLeft = Dot(leftToRight, carToLeft);
// const float fDotRight = Dot(leftToRight, carToRight);
//
// if (fDotRight * fDotLeft > 0.0f)
// {
// // both positive or both negative
// #if __BANK
// if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
// {
// grcDebugDraw::Sphere(VECTOR3_TO_VEC3V(vBumperPos), 1.0f, Color_purple, 0, 0, false);
// }
// #endif //BANK
// return false;
// }
//
// }
//
// for (int i = 0; i < iNumNodes-1; i++)
// {
// const AvoidanceTaskObstructionData::RoadSegment& roadSeg = obstructionData.m_RoadSegments[i];
//
// if (!roadSeg.m_Valid)
// {
// continue;
// }
//
// #if __BANK
// if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
// {
// Vector3 vLeftStart(roadSeg.m_LeftStart.GetX(), roadSeg.m_LeftStart.GetY(), vBumperPos.z);
// Vector3 vLeftEnd(roadSeg.m_LeftEnd.GetX(), roadSeg.m_LeftEnd.GetY(), vBumperPos.z);
// Vector3 vRightStart(roadSeg.m_RightStart.GetX(), roadSeg.m_RightStart.GetY(), vBumperPos.z);
// Vector3 vRightEnd(roadSeg.m_RightEnd.GetX(), roadSeg.m_RightEnd.GetY(), vBumperPos.z);
// grcDebugDraw::Line(vLeftStart, vLeftEnd, Color_green);
// grcDebugDraw::Line(vRightStart, vRightEnd, Color_blue);
// }
// #endif //__BANK
//
// float fTBorder, fTTestSegment;
// if (geom2D::Test2DSegVsSegFast(fTTestSegment, fTBorder,
// vVehiclePos2d.GetX(), vVehiclePos2d.GetY(),
// vSearchPos2d.GetX(), vSearchPos2d.GetY(),
// roadSeg.m_LeftStart.GetX(), roadSeg.m_LeftStart.GetY(),
// roadSeg.m_LeftEnd.GetX(), roadSeg.m_LeftEnd.GetY()))
// {
// fDistToIntersectionOut = fTTestSegment * fSegmentLength;
// return true;
// }
// if (geom2D::Test2DSegVsSegFast(fTTestSegment, fTBorder,
// vVehiclePos2d.GetX(), vVehiclePos2d.GetY(),
// vSearchPos2d.GetX(), vSearchPos2d.GetY(),
// roadSeg.m_RightStart.GetX(), roadSeg.m_RightStart.GetY(),
// roadSeg.m_RightEnd.GetX(), roadSeg.m_RightEnd.GetY()))
// {
// fDistToIntersectionOut = fTTestSegment * fSegmentLength;
// return true;
// }
// }
//
// return false;
// }
#if !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::DoesLineSegmentCrossRoadBoundariesUsingFollowRoute(const AvoidanceTaskObstructionData& obstructionData, const float fHeading, const float fSegmentLength, float& fDistToIntersectionOut) const
{
const CVehicle* pVeh = obstructionData.pVeh;
Assert(pVeh);
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
// Fine to have no followroute, no extents to check.
if (!pFollowRoute)
{
return false;
}
//if this is 0, it's probably because we didn't build this array due to not
//having a nodelist. while the above early return for !pFollowRoute is probably
//sufficient, I'm adding this in the interest of robustness. if something were to happen
//to give us a valid nodelist in the meantime, we don't care if we didn't
//build the m_RoadSegments list up with information about the nodelist already
if (obstructionData.m_RoadSegments.GetCount() == 0)
{
return false;
}
fDistToIntersectionOut = -1.0f;
//if we have a valid nodelist,
//iterate through each segment of our path
//generate edges
//test the segment against each edge
//return early if we find a collision
Vector3 vehDriveDir = RCC_VECTOR3(obstructionData.vNormalizedDriveDir);
const float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
//const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
// Use the coordinates of our front bumper.
Vector3 vBumperPos = RCC_VECTOR3(obstructionData.vBumperCenter);
const Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()));
const Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
const Vector3 Offset1 = fSegmentLength * rage::Cosf(vehDriveOrientation - fHeading) * VehForwardVector;
const Vector3 Offset2 = fSegmentLength * rage::Sinf(vehDriveOrientation - fHeading) * VehRightVector;
const Vector3 vSearchPos = vBumperPos + Offset1 + Offset2;
const Vec2f vVehiclePos2d(vBumperPos.x, vBumperPos.y);
const Vec2f vSearchPos2d(vSearchPos.x, vSearchPos.y);
//? const bool bClampRoadEdgesToCenter = carAvoidanceLevel <= CAR_AVOIDANCE_LITTLE;
const int iNumNodes = CVehicleNodeList::CAR_MAX_NUM_PATHNODES_STORED;
s32 iTargetNode = pFollowRoute->GetCurrentTargetPoint();
s32 iOldNode = iTargetNode - 1;
if(Max(iOldNode, 0) >= obstructionData.m_RoadSegments.GetCount())
{
return false;
}
//if we aren't on the road at all, don't let this code prevent us from getting back on
const AvoidanceTaskObstructionData::RoadSegment& currentRoadSeg = obstructionData.m_RoadSegments[Max(iOldNode, 0)];
if (currentRoadSeg.m_Valid)
{
Vec2f carToLeft = currentRoadSeg.m_LeftStart - vVehiclePos2d;
Vec2f carToRight = currentRoadSeg.m_RightStart - vVehiclePos2d;
Vec2f leftToRight = currentRoadSeg.m_LeftStart - currentRoadSeg.m_RightStart;
const float fDotLeft = Dot(leftToRight, carToLeft);
const float fDotRight = Dot(leftToRight, carToRight);
if (fDotRight * fDotLeft > 0.0f)
{
// both positive or both negative
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Sphere(VECTOR3_TO_VEC3V(vBumperPos), 1.0f, Color_purple, 0, 0, false);
}
#endif //BANK
return false;
}
}
for (int i = 0; i < iNumNodes-1 && i < obstructionData.m_RoadSegments.GetCount(); i++)
{
const AvoidanceTaskObstructionData::RoadSegment& roadSeg = obstructionData.m_RoadSegments[i];
if (!roadSeg.m_Valid)
{
continue;
}
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 vLeftStart(roadSeg.m_LeftStart.GetX(), roadSeg.m_LeftStart.GetY(), vBumperPos.z);
Vector3 vLeftEnd(roadSeg.m_LeftEnd.GetX(), roadSeg.m_LeftEnd.GetY(), vBumperPos.z);
Vector3 vRightStart(roadSeg.m_RightStart.GetX(), roadSeg.m_RightStart.GetY(), vBumperPos.z);
Vector3 vRightEnd(roadSeg.m_RightEnd.GetX(), roadSeg.m_RightEnd.GetY(), vBumperPos.z);
//grcDebugDraw::Line(vLeftStart, vLeftEnd, Color_green);
//grcDebugDraw::Line(vRightStart, vRightEnd, Color_blue);
CVehicle::ms_debugDraw.AddLine(vLeftStart, vLeftEnd, Color_green);
CVehicle::ms_debugDraw.AddLine(vRightStart, vRightEnd, Color_blue);
}
#endif //__BANK
float fTBorder, fTTestSegment;
if (geom2D::Test2DSegVsSegFast(fTTestSegment, fTBorder,
vVehiclePos2d.GetX(), vVehiclePos2d.GetY(),
vSearchPos2d.GetX(), vSearchPos2d.GetY(),
roadSeg.m_LeftStart.GetX(), roadSeg.m_LeftStart.GetY(),
roadSeg.m_LeftEnd.GetX(), roadSeg.m_LeftEnd.GetY()))
{
fDistToIntersectionOut = fTTestSegment * fSegmentLength;
return true;
}
if (geom2D::Test2DSegVsSegFast(fTTestSegment, fTBorder,
vVehiclePos2d.GetX(), vVehiclePos2d.GetY(),
vSearchPos2d.GetX(), vSearchPos2d.GetY(),
roadSeg.m_RightStart.GetX(), roadSeg.m_RightStart.GetY(),
roadSeg.m_RightEnd.GetX(), roadSeg.m_RightEnd.GetY()))
{
fDistToIntersectionOut = fTTestSegment * fSegmentLength;
return true;
}
}
return false;
}
#else // !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
/*
PURPOSE
Works like geom2D::Test2DSegVsSegFast, except that it operates on
four segments to test against four other segments.
PARAMS
hitT1OutV - T values for the intersections, if they exist, along the first segments.
start1xV - X start coordinates of the first four segments.
start1yV - Y start coordinates of the first four segments.
end1xV - X end coordinates of the first four segments.
end1yV - Y end coordinates of the first four segments.
start2xV - X start coordinates of the second four segments.
start2yV - Y start coordinates of the second four segments.
end2xV - X end coordinates of the second four segments.
end2yV - Y end coordinates of the second four segments.
RETURNS
A vector bool mask representing which intersections were found.
*/
static VecBoolV_Out sTest4x2DSegVsSegOneIsect(
Vec4V_InOut hitT1OutV, /* Vec4V_InOut hitT2OutV, */
Vec4V_In start1xV, Vec4V_In start1yV,
Vec4V_In end1xV, Vec4V_In end1yV,
Vec4V_In start2xV, Vec4V_In start2yV,
Vec4V_In end2xV, Vec4V_In end2yV)
{
const Vec4V parallelThresholdV(V_FLT_EPSILON);
const Vec4V zeroV(V_ZERO);
const Vec4V oneV(V_ONE);
const Vec4V uXV = Subtract(end1xV, start1xV);
const Vec4V uYV = Subtract(end1yV, start1yV);
const Vec4V vXV = Subtract(end2xV, start2xV);
const Vec4V negVYV = Subtract(start2yV, end2yV);
const Vec4V vPerpXV = negVYV;
const Vec4V vPerpYV = vXV;
const Vec4V uPerpXV = Negate(uYV);
const Vec4V uPerpYV = uXV;
const Vec4V wXV = Subtract(start1xV, start2xV);
const Vec4V wYV = Subtract(start1yV, start2yV);
const Vec4V negVPerpDotUV = AddScaled(Scale(vPerpXV, uXV), vPerpYV, uYV);
const Vec4V absVPerpDotUV = Abs(negVPerpDotUV);
const Vec4V s0V = AddScaled(Scale(vPerpXV, wXV), vPerpYV, wYV);
const Vec4V t0V = AddScaled(Scale(uPerpXV, wXV), uPerpYV, wYV);
const VecBoolV vPerpDotUGreaterThanZero = IsLessThan(negVPerpDotUV, zeroV);
const Vec4V sV = SelectFT(vPerpDotUGreaterThanZero, Negate(s0V), s0V);
const Vec4V tV = SelectFT(vPerpDotUGreaterThanZero, Negate(t0V), t0V);
const Vec4V hitT1V = InvScale(sV, absVPerpDotUV);
// Could do this, if we needed the intersections with the second segments:
// const Vec4V hitT2V = InvScale(tV, absVPerpDotUV);
const VecBoolV linesNotParallelV = IsGreaterThanOrEqual(absVPerpDotUV, parallelThresholdV);
const VecBoolV isect1ValidV = And(IsGreaterThanOrEqual(sV, zeroV), IsLessThanOrEqual(sV, absVPerpDotUV));
const VecBoolV isect2ValidV = And(IsGreaterThanOrEqual(tV, zeroV), IsLessThanOrEqual(tV, absVPerpDotUV));
const VecBoolV retV = And(And(isect1ValidV, isect2ValidV), linesNotParallelV);
hitT1OutV = hitT1V;
// Could do this, if we needed the intersections with the second segments:
// hitT2OutV = hitT2V;
return retV;
}
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::DoLineSegmentsCrossRoadBoundariesUsingFollowRoute(
const AvoidanceTaskObstructionData& obstructionData, const AvoidanceInfo* avoidanceInfoArray, const int numDirections,
const float& fSegmentLength, Vec4V* distToRoadEdgeVectorArrayOut) const
{
const CVehicle* pVeh = obstructionData.pVeh;
Assert(pVeh);
const CVehicleFollowRouteHelper* pFollowRoute = pVeh->GetIntelligence()->GetFollowRouteHelper();
// Fine to have no followroute, no extents to check.
if (!pFollowRoute)
{
return false;
}
s32 iTargetNode = pFollowRoute->GetCurrentTargetPoint();
s32 iOldNode = iTargetNode - 1;
//possible if we don't have a fully valid route (u-turn etc)
if(Max(iOldNode, 0) >= obstructionData.m_RoadSegments.GetCount())
{
return false;
}
const Vec2f vVehiclePos2d(obstructionData.vBumperCenter.GetXf(), obstructionData.vBumperCenter.GetYf());
// This part doesn't actually depend on the directions at all, so we do the test outside of the loop over the directions
// (could possibly make sense to even break it out of the function, since we may still do it more than once per vehicle now):
//if we aren't on the road at all, don't let this code prevent us from getting back on
const AvoidanceTaskObstructionData::RoadSegment& currentRoadSeg = obstructionData.m_RoadSegments[Max(iOldNode, 0)];
if (currentRoadSeg.m_Valid)
{
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
grcDebugDraw::Line(Vector3(currentRoadSeg.m_LeftStart.GetX(), currentRoadSeg.m_LeftStart.GetY(), obstructionData.vBumperCenter.GetZf()), VEC3V_TO_VECTOR3(obstructionData.vBumperCenter), Color_purple, Color_purple);
grcDebugDraw::Line(VEC3V_TO_VECTOR3(obstructionData.vBumperCenter), Vector3(currentRoadSeg.m_RightStart.GetX(), currentRoadSeg.m_RightStart.GetY(), obstructionData.vBumperCenter.GetZf()), Color_purple, Color_purple);
grcDebugDraw::Line(Vector3(currentRoadSeg.m_RightStart.GetX(), currentRoadSeg.m_RightStart.GetY(), obstructionData.vBumperCenter.GetZf()), Vector3(currentRoadSeg.m_LeftStart.GetX(), currentRoadSeg.m_LeftStart.GetY(), obstructionData.vBumperCenter.GetZf()), Color_purple, Color_purple);
grcDebugDraw::Arrow(Vec3V(currentRoadSeg.m_LeftStart.GetX(), currentRoadSeg.m_LeftStart.GetY(), obstructionData.vBumperCenter.GetZf()), Vec3V(currentRoadSeg.m_LeftEnd.GetX(), currentRoadSeg.m_LeftEnd.GetY(), obstructionData.vBumperCenter.GetZf()), 0.5f, Color_purple);
grcDebugDraw::Line(Vector3(currentRoadSeg.m_LeftEnd.GetX(), currentRoadSeg.m_LeftEnd.GetY(), obstructionData.vBumperCenter.GetZf()), Vector3(currentRoadSeg.m_RightEnd.GetX(), currentRoadSeg.m_RightEnd.GetY(), obstructionData.vBumperCenter.GetZf()), Color_purple, Color_purple);
grcDebugDraw::Arrow(Vec3V(currentRoadSeg.m_RightStart.GetX(), currentRoadSeg.m_RightStart.GetY(), obstructionData.vBumperCenter.GetZf()), Vec3V(currentRoadSeg.m_RightEnd.GetX(), currentRoadSeg.m_RightEnd.GetY(), obstructionData.vBumperCenter.GetZf()), 0.5f, Color_purple);
}
#endif // __BANK
Vec2f carToLeft = currentRoadSeg.m_LeftStart - vVehiclePos2d;
Vec2f carToRight = currentRoadSeg.m_RightStart - vVehiclePos2d;
Vec2f leftToRight = currentRoadSeg.m_LeftStart - currentRoadSeg.m_RightStart;
const float fDotLeft = Dot(leftToRight, carToLeft);
const float fDotRight = Dot(leftToRight, carToRight);
if (fDotRight * fDotLeft > 0.0f)
{
// both positive or both negative
#if __BANK
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
const Vec3V bumperPosV = obstructionData.vBumperCenter;
grcDebugDraw::Sphere(bumperPosV, 1.0f, Color_purple);
}
#endif //BANK
return false;
}
}
const Vec3V bumperPosV = obstructionData.vBumperCenter;
// We need temporary arrays for X and Y positions in SoA form:
Assert(numDirections <= kMaxObstructionProbeDirections);
static const int kMaxVecs = kMaxObstructionProbeDirections/4;
CompileTimeAssert(kMaxVecs*4 == kMaxObstructionProbeDirections);
Vec4V searchPosXArrayVecs[kMaxVecs];
Vec4V searchPosYArrayVecs[kMaxVecs];
const ScalarV segmentLengthV = LoadScalar32IntoScalarV(fSegmentLength);
//if we have a valid nodelist,
//iterate through each segment of our path
//generate edges
//test the segment against each edge
//return early if we find a collision
// This could be done more efficiently:
const float vehDriveOrientation = fwAngle::GetATanOfXY(obstructionData.vNormalizedDriveDir.GetXf(), obstructionData.vNormalizedDriveDir.GetYf());
const ScalarV vehDriveOrientationV(vehDriveOrientation);
// Get directional vectors for the vehicle, and scale.
const Vec3V vehRightVectorV = pVeh->GetVehicleRightDirection();
const Vec3V vehForwardVectorV = pVeh->GetVehicleForwardDirection();
const Vec3V scaledVehForwardV = Scale(vehForwardVectorV, segmentLengthV);
const Vec3V scaledVehRightV = Scale(vehRightVectorV, segmentLengthV);
// Loop over the avoidance objects and initialize searchPos[X/Y]ArrayVecs.
const int lastDir = numDirections - 1;
for(int j = 0, k = 0; j < numDirections; j += 4, k++)
{
// Note: the Min() stuff here is a bit messy, but reading past the array passed in
// by the user would be a bit risky. And, we do want valid data at the end of the array,
// so we can stop the algorithm early if we have find obstructions in all directions.
const AvoidanceInfo& avoidanceInfo0 = avoidanceInfoArray[j];
const AvoidanceInfo& avoidanceInfo1 = avoidanceInfoArray[Min(j + 1, lastDir)];
const AvoidanceInfo& avoidanceInfo2 = avoidanceInfoArray[Min(j + 2, lastDir)];
const AvoidanceInfo& avoidanceInfo3 = avoidanceInfoArray[Min(j + 3, lastDir)];
// Load up the four directions in vector registers.
const ScalarV heading0V = LoadScalar32IntoScalarV(avoidanceInfo0.fDirection);
const ScalarV heading1V = LoadScalar32IntoScalarV(avoidanceInfo1.fDirection);
const ScalarV heading2V = LoadScalar32IntoScalarV(avoidanceInfo2.fDirection);
const ScalarV heading3V = LoadScalar32IntoScalarV(avoidanceInfo3.fDirection);
// Put them together in one vector, and compute the angle we want to compute cos and sin for.
const Vec4V headingV(heading0V, heading1V, heading2V, heading3V);
const Vec4V angleV = Subtract(Vec4V(vehDriveOrientationV), headingV);
// Compute the sin and cos values of the four angles.
Vec4V sinV, cosV;
SinAndCos(sinV, cosV, angleV);
// Use the coordinates of our front bumper, and compute the search positions.
const Vec4V searchPos1XV = AddScaled(Vec4V(bumperPosV.GetX()), cosV, scaledVehForwardV.GetX());
const Vec4V searchPos1YV = AddScaled(Vec4V(bumperPosV.GetY()), cosV, scaledVehForwardV.GetY());
const Vec4V searchPosXV = AddScaled(searchPos1XV, sinV, scaledVehRightV.GetX());
const Vec4V searchPosYV = AddScaled(searchPos1YV, sinV, scaledVehRightV.GetY());
// Store the search positions.
searchPosXArrayVecs[k] = searchPosXV;
searchPosYArrayVecs[k] = searchPosYV;
}
const int iNumNodes = pFollowRoute->GetNumPoints();
#if __BANK
// This part used to be done for each direction, but it's actually direction-independent.
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
for (int i = 0; i < iNumNodes-1 && i < obstructionData.m_RoadSegments.GetCount(); i++)
{
const AvoidanceTaskObstructionData::RoadSegment& roadSeg = obstructionData.m_RoadSegments[i];
if (!roadSeg.m_Valid)
{
continue;
}
const float bumperZ = bumperPosV.GetZf();
Vector3 vLeftStart(roadSeg.m_LeftStart.GetX(), roadSeg.m_LeftStart.GetY(), bumperZ + 2.0f);
Vector3 vLeftEnd(roadSeg.m_LeftEnd.GetX(), roadSeg.m_LeftEnd.GetY(), bumperZ + 2.0f);
Vector3 vRightStart(roadSeg.m_RightStart.GetX(), roadSeg.m_RightStart.GetY(), bumperZ + 2.0f);
Vector3 vRightEnd(roadSeg.m_RightEnd.GetX(), roadSeg.m_RightEnd.GetY(), bumperZ + 2.0f);
//grcDebugDraw::Line(vLeftStart, vLeftEnd, Color_green);
//grcDebugDraw::Line(vRightStart, vRightEnd, Color_blue);
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vLeftStart), VECTOR3_TO_VEC3V(vLeftEnd), Color_green);
CVehicle::ms_debugDraw.AddLine(VECTOR3_TO_VEC3V(vRightStart), VECTOR3_TO_VEC3V(vRightEnd), Color_blue);
}
}
#endif
const Vec4V vVehiclePosX(ScalarV(vVehiclePos2d.GetX()));
const Vec4V vVehiclePosY(ScalarV(vVehiclePos2d.GetY()));
const int numVecs = (numDirections + 3)/4;
const Vec4V negOneV(V_NEGONE);
const Vec4V zeroV(V_ZERO);
// Initialize distToRoadEdgeVectorArrayOut, and also an array that we will use for keeping
// track of which vectors have already hit something, and don't need to be tested further.
bool resultsFinalized[kMaxVecs];
for(int i = 0; i < numVecs; i++)
{
distToRoadEdgeVectorArrayOut[i] = negOneV;
resultsFinalized[i] = false;
}
int numResultsFinalized = 0;
for (int i = 0; i < iNumNodes-1 && i < obstructionData.m_RoadSegments.GetCount(); i++)
{
const AvoidanceTaskObstructionData::RoadSegment& roadSeg = obstructionData.m_RoadSegments[i];
if (!roadSeg.m_Valid)
{
continue;
}
// Load up the road segment coordinates into splatted vector registers, straight from memory.
const Vec4V leftStartXV(LoadScalar32IntoScalarV(roadSeg.m_LeftStart.GetIntrinConstRef().x));
const Vec4V leftStartYV(LoadScalar32IntoScalarV(roadSeg.m_LeftStart.GetIntrinConstRef().y));
const Vec4V leftEndXV(LoadScalar32IntoScalarV(roadSeg.m_LeftEnd.GetIntrinConstRef().x));
const Vec4V leftEndYV(LoadScalar32IntoScalarV(roadSeg.m_LeftEnd.GetIntrinConstRef().y));
const Vec4V rightStartXV(LoadScalar32IntoScalarV(roadSeg.m_RightStart.GetIntrinConstRef().x));
const Vec4V rightStartYV(LoadScalar32IntoScalarV(roadSeg.m_RightStart.GetIntrinConstRef().y));
const Vec4V rightEndXV(LoadScalar32IntoScalarV(roadSeg.m_RightEnd.GetIntrinConstRef().x));
const Vec4V rightEndYV(LoadScalar32IntoScalarV(roadSeg.m_RightEnd.GetIntrinConstRef().y));
// Loop over all the avoidance directions.
for(int j = 0; j < numVecs; j++)
{
// Check if all the four directions in this vector have hit something already.
// If so, we can skip the test below.
if(resultsFinalized[j])
{
continue;
}
// Load the previous distances, and the search positions for these four directions.
const Vec4V distToRoadEdgeBeforeV = distToRoadEdgeVectorArrayOut[j];
const Vec4V searchPosXV = searchPosXArrayVecs[j];
const Vec4V searchPosYV = searchPosYArrayVecs[j];
// Check which intersections exist already.
const VecBoolV existingIsectsV = IsGreaterThanOrEqual(distToRoadEdgeBeforeV, zeroV);
// Test each of the four direction test line segments against the left and right side of this section of the road.
Vec4V tTestSegmentLeftV;
Vec4V tTestSegmentRightV;
const VecBoolV leftIsectV = sTest4x2DSegVsSegOneIsect(tTestSegmentLeftV,
vVehiclePosX, vVehiclePosY, searchPosXV, searchPosYV,
leftStartXV, leftStartYV, leftEndXV, leftEndYV);
const VecBoolV rightIsectV = sTest4x2DSegVsSegOneIsect(tTestSegmentRightV,
vVehiclePosX, vVehiclePosY, searchPosXV, searchPosYV,
rightStartXV, rightStartYV, rightEndXV, rightEndYV);
// Compute the distances to road edges, if they were hits.
const Vec4V distToCurrentRoadEdgeLeftV = Scale(tTestSegmentLeftV, segmentLengthV);
const Vec4V distToCurrentRoadEdgeRightV = Scale(tTestSegmentRightV, segmentLengthV);
// Mask in -1 for the distances that didn't hit.
const Vec4V newDistToRoadEdgeLeftV = SelectFT(leftIsectV, negOneV, distToCurrentRoadEdgeLeftV);
const Vec4V newDistToRoadEdgeRightV = SelectFT(rightIsectV, negOneV, distToCurrentRoadEdgeRightV);
// Update with the left intersections.
const Vec4V distToRoadEdgeWithLeftV = SelectFT(existingIsectsV, newDistToRoadEdgeLeftV, distToRoadEdgeBeforeV);
const VecBoolV existingIsectsWithLeftV = Or(existingIsectsV, leftIsectV);
// Update with the right intersections. Note that left takes precedence for no other reason
// than preserving existing behavior. Not sure if we ever hit both left and right, and if so,
// if it would make sense to try to pick the closest intersection or something.
const Vec4V distToRoadEdgeWithLeftAndRightV = SelectFT(existingIsectsWithLeftV, newDistToRoadEdgeRightV, distToRoadEdgeWithLeftV);
// Store back the results to the distance to road array.
distToRoadEdgeVectorArrayOut[j] = distToRoadEdgeWithLeftAndRightV;
// Probably worth checking if we are done with all four of these directions. If so,
// we store that as a bool we can check cheaply later, and increase the count in
// case we can stop completely.
if(IsGreaterThanAll(distToRoadEdgeWithLeftAndRightV, zeroV))
{
resultsFinalized[j] = true;
numResultsFinalized++;
}
}
// Check if all directions have hit something, in which case we can stop early.
if(numResultsFinalized == numVecs)
{
break;
}
}
#if __BANK && 0 // Can be enabled to debug draw the intersection positions.
if (ms_bEnableNewAvoidanceDebugDraw && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVeh))
{
Vector3 vehDriveDir = RCC_VECTOR3(obstructionData.vNormalizedDriveDir);
const float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
//const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVeh->GetVehiclePosition());
// Use the coordinates of our front bumper.
Vector3 vBumperPos = RCC_VECTOR3(obstructionData.vBumperCenter);
for(int i = 0; i < numDirections; i++)
{
const float fHeading = avoidanceInfoArray[i].fDirection;
const float distToRoadEdge = ((float*)distToRoadEdgeVectorArrayOut)[i];
if(distToRoadEdge < 0.0f)
{
continue;
}
const Vector3 VehRightVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleRightDirection()));
const Vector3 VehForwardVector(VEC3V_TO_VECTOR3(pVeh->GetVehicleForwardDirection()));
const Vector3 Offset1 = distToRoadEdge * rage::Cosf(vehDriveOrientation - fHeading) * VehForwardVector;
const Vector3 Offset2 = distToRoadEdge * rage::Sinf(vehDriveOrientation - fHeading) * VehRightVector;
const Vector3 vSearchPos = vBumperPos + Offset1 + Offset2;
grcDebugDraw::Line(vSearchPos, vSearchPos + Vector3(0.0f, 0.0f, 3.0f), Color_yellow);
}
}
#endif // __BANK
return true;
}
#endif // !USE_VECTORIZED_FOLLOWROUTE_EDGES_FOR_AVOIDANCE
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Test2MovingRectCollision
// PURPOSE : Test whether the points of the second rectangle fall within the
// first at any point. Returns the time of the first collision(0..1)
/////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleGoToPointWithAvoidanceAutomobile::Test2MovingRectCollision(const CPhysical* const pVeh, const CPhysical* const pOtherVeh,
float OtherSpeedX, float OtherSpeedY,
const Vector3& ourNewDriveDir,
const Vector3& otherDriveDir,
const spdAABB& boundBox,
const spdAABB& otherBox,
const bool bOtherEntIsDoor)
{
float ReturnVal, DistFromCentralLine, ChangeInDist;
s16 OtherPoints;
Vector3 LineStart;
float MinVal1, MaxVal1, MinVal2, MaxVal2;
float CrossVal, MinVal;
// We had to reshuffle the way the arguments are passed. Otherwise the optimiser
// would fuck stuff up. Basically arguments changed during the function call.
// A printf before and after the call resulted in different results.
Vector3 hisPosn = VEC3V_TO_VECTOR3(pOtherVeh->GetTransform().GetPosition() - pVeh->GetTransform().GetPosition());
//Vector3 ourBBMax = pVeh->GetBoundingBoxMax();
//Vector3 vOtherBBMin = pOtherVeh->GetBoundingBoxMin();
float OtherCarX = hisPosn.x;
float OtherCarY = hisPosn.y;
const float OurBBHalfLengthPos = boundBox.GetMax().GetYf();
const float OurBBHalfWidth = boundBox.GetMax().GetXf();
const float OurBBHalfWidthNeg = -boundBox.GetMin().GetXf();
const float OurBBHalfLengthNeg = -boundBox.GetMin().GetYf();
float OtherBBHalfLengthPos = otherBox.GetMax().GetYf();//hisBBMax.y;
const float OtherBBHalfWidth = otherBox.GetMax().GetXf();//hisBBMax.x;
const float OtherBBHalfWidthNeg = -otherBox.GetMin().GetXf(); //still a positive value to make below code more readable
//float OtherBBHalfLengthNeg = -pOther->GetBoundingBoxMin().y;
if (bOtherEntIsDoor)
{
OtherBBHalfLengthPos = rage::Max(ms_fMinLengthForDoors, OtherBBHalfLengthPos);
}
/* float OtherCarX = pOther->GetPosition().x - pVeh->GetPosition().x;
float OtherCarY = pOther->GetPosition().y - pVeh->GetPosition().y;
float OurBBHalfLengthPos = pVeh->GetBoundingBoxMax().y;
float OurBBHalfWidth = pVeh->GetBoundingBoxMax().x;
float OurBBHalfLengthNeg = -pVeh->GetBoundingBoxMin().y;
float OtherBBHalfLengthPos = pOther->GetBoundingBoxMax().y;
float OtherBBHalfWidth = pOther->GetBoundingBoxMax().x;
// float OtherBBHalfLengthNeg = -pOther->GetBoundingBoxMin().y;*/
// We could add some fast rejection tests here.
// If the cars are moving away from eachother there is no point in proceeding
// ReturnVal == LARGE_FLOAT means no collision.
ReturnVal = LARGE_FLOAT;
// Front and Side perpendicular so:
// pOtherSide->x = otherDriveDir.y
// pOtherSide->y = -otherDriveDir.x
// NOTE: we now only do the 2 front points of the ai car. We're not really
// interested in collisions the rear point might cause(we can't do anything
// about that anyway)
for(OtherPoints = 0; OtherPoints < 2; OtherPoints++)
{
switch(OtherPoints)
{
case 0:
LineStart.x = OtherCarX + OtherBBHalfLengthPos*otherDriveDir.x + OtherBBHalfWidth*otherDriveDir.y;
LineStart.y = OtherCarY + OtherBBHalfLengthPos*otherDriveDir.y - OtherBBHalfWidth*otherDriveDir.x;
break;
case 1:
LineStart.x = OtherCarX + OtherBBHalfLengthPos*otherDriveDir.x - OtherBBHalfWidthNeg*otherDriveDir.y;
LineStart.y = OtherCarY + OtherBBHalfLengthPos*otherDriveDir.y + OtherBBHalfWidthNeg*otherDriveDir.x;
break;
// case 2:
// LineStart.x = OtherCarX - OtherBBHalfLengthNeg*otherDriveDir.x + OtherBBHalfWidth*otherDriveDir.y;
// LineStart.y = OtherCarY - OtherBBHalfLengthNeg*otherDriveDir.y - OtherBBHalfWidthNeg*otherDriveDir.x;
// break;
// case 3:
// LineStart.x = OtherCarX - OtherBBHalfLengthNeg*otherDriveDir.x - OtherBBHalfWidthNeg*otherDriveDir.y;
// LineStart.y = OtherCarY - OtherBBHalfLengthNeg*otherDriveDir.y + OtherBBHalfWidth*otherDriveDir.x;
// break;
}
// First deal with our line along initialLength of car
MinVal1 = 0.0f;
MaxVal1 = 1.0f;
DistFromCentralLine = LineStart.x * ourNewDriveDir.y - LineStart.y * ourNewDriveDir.x;
ChangeInDist = OtherSpeedX * ourNewDriveDir.y - OtherSpeedY * ourNewDriveDir.x;
// Find out when we cross the line
if(DistFromCentralLine > OurBBHalfWidth)
{
if(ChangeInDist < 0.0f)
{
CrossVal = -(DistFromCentralLine-OurBBHalfWidth) / ChangeInDist;
if(CrossVal < 1.0f)
{
MinVal1 = CrossVal;
// We might go out on the other side as well
CrossVal -=(2.0f*OurBBHalfWidth) / ChangeInDist;
if(CrossVal < 1.0f)
{
MaxVal1 = CrossVal;
}
}
else
{
MinVal1 = 1.0f;
}
}
else
{
MinVal1 = MaxVal1 = 1.0f;
}
}
else if(DistFromCentralLine < -OurBBHalfWidthNeg)
{
if(ChangeInDist > 0.0f)
{
CrossVal = -(DistFromCentralLine+OurBBHalfWidthNeg) / ChangeInDist; // Changed
if(CrossVal < 1.0f)
{
MinVal1 = CrossVal;
// We might go out on the other side as well
CrossVal +=(2.0f*OurBBHalfWidthNeg) / ChangeInDist;
if(CrossVal < 1.0f)
{
MaxVal1 = CrossVal;
}
}
else
{
MinVal1 = 1.0f;
}
}
else
{
MinVal1 = MaxVal1 = 1.0f;
}
}
else // We start in the middle. Check whether we jump out at some point
{
if(ChangeInDist > 0.0f)
{
CrossVal =(OurBBHalfWidth - DistFromCentralLine) / ChangeInDist;
MaxVal1 = CrossVal;
}
else if(ChangeInDist < 0.0f)
{
CrossVal = -(OurBBHalfWidth + DistFromCentralLine) / ChangeInDist;
MaxVal1 = CrossVal;
}
}
// Now deal with our line along width of car
MinVal2 = 0.0f;
MaxVal2 = 1.0f;
DistFromCentralLine = LineStart.x * ourNewDriveDir.x + LineStart.y * ourNewDriveDir.y;
ChangeInDist = OtherSpeedX * ourNewDriveDir.x + OtherSpeedY * ourNewDriveDir.y;
// Find out when we cross the line
if(DistFromCentralLine > OurBBHalfLengthPos)
{
if(ChangeInDist < 0.0f)
{
CrossVal = -(DistFromCentralLine-OurBBHalfLengthPos) / ChangeInDist;
if(CrossVal < 1.0f)
{
MinVal2 = CrossVal;
// We might go out on the other side as well
CrossVal -=(OurBBHalfLengthPos + OurBBHalfLengthNeg) / ChangeInDist;
if(CrossVal < 1.0f)
{
MaxVal2 = CrossVal;
}
}
else
{
MinVal2 = 1.0f;
}
}
else
{
MinVal2 = MaxVal2 = 1.0f;
}
}
else if(DistFromCentralLine < -OurBBHalfLengthNeg)
{
if(ChangeInDist > 0.0f)
{
CrossVal = -(DistFromCentralLine+OurBBHalfLengthNeg) / ChangeInDist; // changed
if(CrossVal < 1.0f)
{
MinVal2 = CrossVal;
// We might go out on the other side as well
CrossVal +=(OurBBHalfLengthPos + OurBBHalfLengthNeg) / ChangeInDist;
if(CrossVal < 1.0f)
{
MaxVal2 = CrossVal;
}
}
else
{
MinVal2 = 1.0f;
}
}
else
{
MinVal2 = MaxVal2 = 1.0f;
}
}
else // We start in the middle. Check whether we jump out at some point
{
if(ChangeInDist > 0.0f)
{
CrossVal =(OurBBHalfLengthPos - DistFromCentralLine) / ChangeInDist;
MaxVal2 = CrossVal;
}
else if(ChangeInDist < 0.0f)
{
CrossVal = -(OurBBHalfLengthNeg + DistFromCentralLine) / ChangeInDist;
MaxVal2 = CrossVal;
}
}
// Right. We now have two intervals(MinVal1, MaxVal1) and(MinVal2, MaxVal2).
// The first time both are set is the time value we collide.
MinVal = rage::Max(MinVal1, MinVal2);
if(MinVal < MaxVal1 && MinVal < MaxVal2)
{ // We found a collision here.
ReturnVal = rage::Min(ReturnVal, MinVal);
}
}
return(ReturnVal);
}
#if !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : TestForThisAngle
// PURPOSE : For a specific angle(direction) we test whether the given 2 lines and 3 points collide.
// RETURNS : true for a collision.
/////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TestForThisAngle(float Angle, Vector3 *pLine1Start, Vector3 *pLine1Delta, Vector3 *pLine2Start, Vector3 *pLine2Delta, Vector3 *pLine1Start_Other, Vector3 *pLine1Delta_Other, Vector3 *pLine2Start_Other, Vector3 *pLine2Delta_Other, float OtherCarMoveSpeedX, float OtherCarMoveSpeedY, float OurMoveSpeed, bool bSwapRound, float fLookaheadTime) const
{
Vector3 Coors1, Coors2, Coors3, Coors4;
float OurSpeedX = OurMoveSpeed * rage::Cosf(Angle);
float OurSpeedY = OurMoveSpeed * rage::Sinf(Angle);
// These speeds are such that the player car doesn't move and the other one does all the movement.
float SpeedX_Combined = OtherCarMoveSpeedX - OurSpeedX;
float SpeedY_Combined = OtherCarMoveSpeedY - OurSpeedY;
// Work out the vehToNewNodeDelta vector for this line(combined speeds * time).
Vector3 DeltaVec = Vector3(SpeedX_Combined, SpeedY_Combined, 0.0f) * fLookaheadTime; // look 1.7 seconds ahead
TUNE_GROUP_BOOL(FOLLOW_PATH_AI_STEER, bAlwaysSwapRound, true);
if(bSwapRound || bAlwaysSwapRound)
{
Vector3 *pTemp;
pTemp = pLine1Start;
pLine1Start = pLine1Start_Other;
pLine1Start_Other = pTemp;
pTemp = pLine1Delta;
pLine1Delta = pLine1Delta_Other;
pLine1Delta_Other = pTemp;
pTemp = pLine2Start;
pLine2Start = pLine2Start_Other;
pLine2Start_Other = pTemp;
pTemp = pLine2Delta;
pLine2Delta = pLine2Delta_Other;
pLine2Delta_Other = pTemp;
DeltaVec = -DeltaVec;
}
// The 2 lines of the other car both turn into 4(a rectangle) because of the speed.
Coors1 = *pLine1Start_Other;
Coors2 = *pLine1Start_Other + *pLine1Delta_Other;
Coors3 = Coors1 + DeltaVec;
Coors4 = Coors2 + DeltaVec;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
if (CPathFind::HelperIsPointWithinArbitraryArea(pLine1Start->x, pLine1Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y)
|| CPathFind::HelperIsPointWithinArbitraryArea(pLine2Start->x, pLine2Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y))
{
return true;
}
Coors1 = *pLine2Start_Other;
Coors2 = *pLine2Start_Other + *pLine2Delta_Other;
Coors3 = Coors1 + DeltaVec;
Coors4 = Coors2 + DeltaVec;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
if (CPathFind::HelperIsPointWithinArbitraryArea(pLine1Start->x, pLine1Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y)
|| CPathFind::HelperIsPointWithinArbitraryArea(pLine2Start->x, pLine2Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y))
{
return true;
}
return false;
}
#endif // !USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
// PURPOSE: Helper function to check if (each component of) two vectors have the same sign.
// PARAMS: valAV - First vector.
// valBV - Second vector.
// RETURNS: True for each component where the two vectors have the same sign.
static __forceinline VecBoolV sSameSign(Vec4V_In valAV, Vec4V_In valBV)
{
// XOR the two vectors together, then keep just the sign bit of that,
// i.e. the XOR of the sign bits.
const Vec4V signBitXorV = And(Xor(valAV, valBV), Vec4V(V_80000000));
// Convert to a true bool vector. Note that we need to use IsEqualInt(),
// not IsEqual(), because 0x80000000 is actually the negative zero,
// which is also zero when compared as a float...
const VecBoolV sameSignV = IsEqualInt(signBitXorV, Vec4V(V_ZERO));
// Note: not sure that this is really important, but the original code
// essentially did this:
// return valAV*valBV > 0.0f
// i.e. if one of the values was zero, we would return false. We could do this
// if we really need that behavior:
// const VecBoolV isZeroAV = IsZero(valAV);
// const VecBoolV isZeroBV = IsZero(valBV);
// const VecBoolV eitherZeroV = Or(isZeroAV, isZeroBV);
//
// // Return true if they were both the same sign, using the XOR of the sign bit,
// // and neither vector was zero.
// return Andc(sameSignV, eitherZeroV);
return sameSignV;
}
// PURPOSE: Helper function used as a part of CTaskVehicleGoToPointWithAvoidanceAutomobile::TestForThisAngleNew(),
// for testing intersections between 2D line segments. Four pairs of line segments are tested
// by a single call to the function.
// PARAMS: start1XV - X coordinates of the starting points of the first segment in each pair.
// start1YV - Y coordinates of the starting points of the first segment in each pair.
// delta1XV - X coordinate of the first segment delta in each pair.
// delta1YV - Y coordinate of the first segment delta in each pair.
// start2XV - X coordinates of the starting points of the second segment in each pair.
// start2YV - Y coordinates of the starting points of the second segment in each pair.
// delta2XV - X coordinate of the second segment delta in each pair.
// delta2YV - Y coordinate of the second segment delta in each pair.
// RETURNS: Vector where each element is true if the corresponding pair of segments intersects.
// NOTES: The function works by finding out whether for line 1 both points of line 2 are on separate
// side of the line and it will do the same thing for the other line.
static __forceinline VecBoolV sTest2DLinesAgainst2DLines(
Vec4V_In start1XV, Vec4V_In start1YV, Vec4V_In delta1XV, Vec4V_In delta1YV,
Vec4V_In start2XV, Vec4V_In start2YV, Vec4V_In delta2XV, Vec4V_In delta2YV)
{
// Compute the delta between the start positions of the segments in each pair.
const Vec4V start1To2XV = Subtract(start2XV, start1XV);
const Vec4V start1To2YV = Subtract(start2YV, start1YV);
// Compute some dot products.
// This was all adapted from fwGeom::Test2DLineAgainst2DLine()
// float DotPr1 = (Start2X - Start1X) * Delta1Y - (Start2Y - Start1Y) * Delta1X;
// float DotPr2 = (Start2X - Start1X + Delta2X) * Delta1Y - (Start2Y - Start1Y + Delta2Y) * Delta1X;
// if (DotPr1 * DotPr2 > 0.0f) return false;
// DotPr1 = (Start1X - Start2X) * Delta2Y - (Start1Y - Start2Y) * Delta2X;
// DotPr2 = (Start1X - Start2X + Delta1X) * Delta2Y - (Start1Y - Start2Y + Delta1Y) * Delta2X;
// if (DotPr1 * DotPr2 > 0.0f) return false;
// return true;
const Vec4V startDeltaTimesDeltaXV = Scale(start1To2XV, delta1YV);
const Vec4V startDeltaTimesDeltaYV = Scale(start1To2YV, delta2XV);
const Vec4V tmp1V = Scale(delta1XV, delta2YV);
const Vec4V dotPr1V = SubtractScaled(startDeltaTimesDeltaXV, start1To2YV, delta1XV);
const Vec4V dotPr3V = SubtractScaled(startDeltaTimesDeltaYV, start1To2XV, delta2YV);
const Vec4V tmpV = SubtractScaled(tmp1V, delta1YV, delta2XV);
const Vec4V dotPr2V = Subtract(dotPr1V, tmpV);
const Vec4V dotPr4V = Add(dotPr3V, tmpV);
// Check if the dot products have the same sign, meaning that the two end points
// of one segment is on the same side of the infinite line through the other segment,
// and vice versa.
const VecBoolV sameSign12V = sSameSign(dotPr1V, dotPr2V);
const VecBoolV sameSign34V = sSameSign(dotPr3V, dotPr4V);
// If the signs are different from both directions, the segments intersect.
return InvertBits(Or(sameSign12V, sameSign34V));
}
// PURPOSE: Helper function used as a part of CTaskVehicleGoToPointWithAvoidanceAutomobile::TestForThisAngleNew(),
// for testing one point against a convex 2D quad.
// PARAMS: testPointXV - X coordinate of the point to test.
// testPointYV - Y coordinate of the point to test.
// quadVertsXV - X coordinates of the quad vertices, in CW or perhaps CCW order(?).
// quadVertsYV - Y coordinates of the quad vertices.
// quadDeltaXV - X deltas of the edges of the quad vertices. Redundant given the vertex coordinates, but passed in since the caller code is already computing it.
// quadDeltaYV - Y deltas of the edges of the quad vertices.
// RETURNS: A bool vector that is true if the point was within the quad.
// NOTES: This was adapted from CPathFind::HelperIsPointWithinArbitraryArea, but uses a different
// ordering of the vertices.
static __forceinline BoolV_Out sHelperIsPointWithinArbitraryArea(
ScalarV_In testPointXV, ScalarV_In testPointYV,
Vec4V_In quadVertsXV, Vec4V_In quadVertsYV,
Vec4V_In quadDeltaXV, Vec4V_In quadDeltaYV)
{
// Note: quadDeltaXV and quadDeltaYV should be computed like this:
// const Vec4V quadVertsShiftedXV = quadVertsXV.Get<Vec::Y, Vec::Z, Vec::W, Vec::X>();
// const Vec4V quadVertsShiftedYV = quadVertsYV.Get<Vec::Y, Vec::Z, Vec::W, Vec::X>();
// const Vec4V quadDeltaXV = Subtract(quadVertsShiftedXV, quadVertsXV);
// const Vec4V quadDeltaYV = Subtract(quadVertsShiftedYV, quadVertsYV);
// Compute deltas between the test point and all quad vertices.
const Vec4V testDeltaXV = Subtract(Vec4V(testPointXV), quadVertsXV);
const Vec4V testDeltaYV = Subtract(Vec4V(testPointYV), quadVertsYV);
// Basically compute a dot product between the (non-normalized) normal
// of each edge and the vector between the test point and one of its vertices.
const Vec4V temp1V = Scale(testDeltaXV, quadDeltaYV);
const Vec4V temp2V = SubtractScaled(temp1V, testDeltaYV, quadDeltaXV);
// Use the dot products to check if we are inside each edge of the polygon.
const VecBoolV withinEdgesV = IsGreaterThanOrEqual(temp2V, Vec4V(V_ZERO));
// We are interested in knowing if we are inside all of the edges, so we need
// to AND together the components:
const VecBoolV withinEdgesShifted1V = withinEdgesV.Get<Vec::Y, Vec::Z, Vec::W, Vec::X>();
const VecBoolV combined1V = And(withinEdgesV, withinEdgesShifted1V);
const VecBoolV combined1Shifted2V = combined1V.Get<Vec::Z, Vec::W, Vec::X, Vec::Y>();
const VecBoolV combinedV = And(combined1V, combined1Shifted2V);
// Return the result.
return combinedV.GetX();
}
// PURPOSE: Helper function used by CTaskVehicleGoToPointWithAvoidanceAutomobile::TestForThisAngle(),
// basically testing a 2D polygon formed by extruding a line segment along a vector,
// against two other line segments.
// PARAMS: quadLineStartV - Starting coordinates of the line which will be extruded into a polygon.
// quadLineDeltaV - Delta of the line to extrude, to get to its end point from its start.
// deltaVecV - The vector we will be extruding by.
// line1StartV - Starting point of the first line.
// line1DeltaV - Delta of the first line.
// line2StartV - Starting point of the second line.
// line2DeltaV - Delta of the second line.
// RETURNS: True if any of the lines intersect an edge of the polygon, or if their starting point
// is inside of it.
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
static bool sTestForThisAngleHelper(Vec2V_In quadLineStartV, Vec2V_In quadLineDeltaV, Vec2V_In deltaVecV,
Vec2V_In line1StartV, Vec2V_In line1DeltaV, Vec2V_In line2StartV, Vec2V_In line2DeltaV)
{
// This function is basically equivalent to what this old code was doing:
// if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) return true;
// if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) return true;
// if (CPathFind::HelperIsPointWithinArbitraryArea(pLine1Start->x, pLine1Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y)
// || CPathFind::HelperIsPointWithinArbitraryArea(pLine2Start->x, pLine2Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y))
// {
// return true;
// }
// return false;
// Compute the vertices of the polygon.
const Vec2V coors1V = quadLineStartV;
const Vec2V coors2V = Add(coors1V, quadLineDeltaV);
const Vec2V coors3V = Add(coors1V, deltaVecV);
const Vec2V coors4V = Add(coors2V, deltaVecV);
// Transpose to SoA vector format, equivalent to this:
// const Vec4V coorsAXV(coors1V.GetX(), coors3V.GetX(), coors4V.GetX(), coors2V.GetX());
// const Vec4V coorsAYV(coors1V.GetY(), coors3V.GetY(), coors4V.GetY(), coors2V.GetY());
// Note that the order here is shifted a bit, so that the vertices are in order of
// the edges when going around the polygon.
const Vec4V temp0AV = MergeXY(coors1V, coors4V);
const Vec4V temp1AV = MergeXY(coors3V, coors2V);
const Vec4V coorsAXV = MergeXY(temp0AV, temp1AV);
const Vec4V coorsAYV = MergeZW(temp0AV, temp1AV);
// Splat the X and Y coordinates of the lines to test against the polygon.
const ScalarV line1StartXV(line1StartV.GetX());
const ScalarV line1StartYV(line1StartV.GetY());
const ScalarV line2StartXV(line2StartV.GetX());
const ScalarV line2StartYV(line2StartV.GetY());
const ScalarV line1DeltaXV(line1DeltaV.GetX());
const ScalarV line1DeltaYV(line1DeltaV.GetY());
const ScalarV line2DeltaXV(line2DeltaV.GetX());
const ScalarV line2DeltaYV(line2DeltaV.GetY());
// Rotate the coordinates by one element.
const Vec4V coorsBXV = coorsAXV.Get<Vec::Y, Vec::Z, Vec::W, Vec::X>();
const Vec4V coorsBYV = coorsAYV.Get<Vec::Y, Vec::Z, Vec::W, Vec::X>();
// Subtract to compute the deltas of all edges of the polygon.
const Vec4V deltaAToBXV = Subtract(coorsBXV, coorsAXV);
const Vec4V deltaAToBYV = Subtract(coorsBYV, coorsAYV);
// Check for intersections between the first line and all edges of the polygon.
const VecBoolV lineTestResults1V = sTest2DLinesAgainst2DLines(
Vec4V(line1StartXV), Vec4V(line1StartYV), Vec4V(line1DeltaXV), Vec4V(line1DeltaYV),
coorsAXV, coorsAYV, deltaAToBXV, deltaAToBYV);
// Check for intersections between the second line and all edges of the polygon.
const VecBoolV lineTestResults2V = sTest2DLinesAgainst2DLines(
Vec4V(line2StartXV), Vec4V(line2StartYV), Vec4V(line2DeltaXV), Vec4V(line2DeltaYV),
coorsAXV, coorsAYV, deltaAToBXV, deltaAToBYV);
// Check if the first line's starting point is inside of the polygon.
const BoolV inArea1V = sHelperIsPointWithinArbitraryArea(line1StartXV, line1StartYV, coorsAXV, coorsAYV, deltaAToBXV, deltaAToBYV);
// Check if the second line's starting point is inside of the polygon.
const BoolV inArea2V = sHelperIsPointWithinArbitraryArea(line2StartXV, line2StartYV, coorsAXV, coorsAYV, deltaAToBXV, deltaAToBYV);
// Combine the result and convert to a boolean value.
// Note: we could keep this as a vector, but may be worth branching at this point.
return !IsFalseAll(Or(Or(lineTestResults1V, lineTestResults2V), VecBoolV(Or(inArea1V, inArea2V))));
}
#endif
#if USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
bool CTaskVehicleGoToPointWithAvoidanceAutomobile::TestForThisAngleNew(Vec2V_In dirXYV, Vec2V_In line1StartOtherV, Vec2V_In line1DeltaOtherV, Vec2V_In line2StartOtherV, Vec2V_In line2DeltaOtherV, Vec2V_In line1StartV, Vec2V_In line1DeltaV, Vec2V_In line2StartV, Vec2V_In line2DeltaV, float OtherCarMoveSpeedX, float OtherCarMoveSpeedY, const float& OurMoveSpeed, const float& fLookaheadTime) const
{
// Load the floats as scalars.
const ScalarV ourMoveSpeedV = LoadScalar32IntoScalarV(OurMoveSpeed);
const ScalarV lookAheadTimeV = LoadScalar32IntoScalarV(fLookaheadTime);
// Compute the relative velocity between the vehicles if we move in this direction,
// and compute a delta vector representing the relative movement during the lookahead time.
const Vec2V ourSpeedV = Scale(ourMoveSpeedV, dirXYV);
const Vec2V otherCarMoveSpeedV(OtherCarMoveSpeedX, OtherCarMoveSpeedY);
const Vec2V speedCombinedNegV = Subtract(ourSpeedV, otherCarMoveSpeedV);
const Vec2V deltaVecV = Scale(speedCombinedNegV, lookAheadTimeV);
// This can be enabled to verify the results against the old algorithm.
#if __ASSERT && 0
float OurSpeedX = OurMoveSpeed * rage::Cosf(Angle);
float OurSpeedY = OurMoveSpeed * rage::Sinf(Angle);
// These speeds are such that the player car doesn't move and the other one does all the movement.
float SpeedX_Combined = OtherCarMoveSpeedX - OurSpeedX;
float SpeedY_Combined = OtherCarMoveSpeedY - OurSpeedY;
// Work out the vehToNewNodeDelta vector for this line(combined speeds * time).
Vector3 DeltaVec = Vector3(SpeedX_Combined, SpeedY_Combined, 0.0f) * fLookaheadTime; // look 1.7 seconds ahead
TUNE_GROUP_BOOL(FOLLOW_PATH_AI_STEER, bAlwaysSwapRound, true);
if(/*bSwapRound ||*/ bAlwaysSwapRound)
{
Vector3 *pTemp;
pTemp = pLine1Start;
pLine1Start = pLine1Start_Other;
pLine1Start_Other = pTemp;
pTemp = pLine1Delta;
pLine1Delta = pLine1Delta_Other;
pLine1Delta_Other = pTemp;
pTemp = pLine2Start;
pLine2Start = pLine2Start_Other;
pLine2Start_Other = pTemp;
pTemp = pLine2Delta;
pLine2Delta = pLine2Delta_Other;
pLine2Delta_Other = pTemp;
DeltaVec = -DeltaVec;
}
// The 2 lines of the other car both turn into 4(a rectangle) because of the speed.
const Vector3 Coors1 = *pLine1Start_Other;
const Vector3 Coors2 = *pLine1Start_Other + *pLine1Delta_Other;
const Vector3 Coors3 = Coors1 + DeltaVec;
const Vector3 Coors4 = Coors2 + DeltaVec;
bool ret = false;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors2.x - Coors1.x, Coors2.y - Coors1.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors1.x, Coors1.y, Coors3.x - Coors1.x, Coors3.y - Coors1.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors3.x, Coors3.y, Coors4.x - Coors3.x, Coors4.y - Coors3.y)) { ret = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors4.x, Coors4.y, Coors2.x - Coors4.x, Coors2.y - Coors4.y)) { ret = true; }
//Assert(ret == retNew);
bool inArea1Old = CPathFind::HelperIsPointWithinArbitraryArea(pLine1Start->x, pLine1Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y);
bool inArea2Old = CPathFind::HelperIsPointWithinArbitraryArea(pLine2Start->x, pLine2Start->y, Coors1.x, Coors1.y, Coors2.x, Coors2.y, Coors3.x, Coors3.y, Coors4.x, Coors4.y);
//Assert(inArea1 == inArea1Old);
//Assert(inArea2 == inArea2Old);
ret = ret | inArea1Old | inArea2Old;
const Vector3 Coors5 = *pLine2Start_Other;
const Vector3 Coors6 = *pLine2Start_Other + *pLine2Delta_Other;
const Vector3 Coors7 = Coors5 + DeltaVec;
const Vector3 Coors8 = Coors6 + DeltaVec;
bool ret2 = false;
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors5.x, Coors5.y, Coors6.x - Coors5.x, Coors6.y - Coors5.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors5.x, Coors5.y, Coors7.x - Coors5.x, Coors7.y - Coors5.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors7.x, Coors7.y, Coors8.x - Coors7.x, Coors8.y - Coors7.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine1Start->x, pLine1Start->y, pLine1Delta->x, pLine1Delta->y, Coors8.x, Coors8.y, Coors6.x - Coors8.x, Coors6.y - Coors8.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors5.x, Coors5.y, Coors6.x - Coors5.x, Coors6.y - Coors5.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors5.x, Coors5.y, Coors7.x - Coors5.x, Coors7.y - Coors5.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors7.x, Coors7.y, Coors8.x - Coors7.x, Coors8.y - Coors7.y)) { ret2 = true; }
if(fwGeom::Test2DLineAgainst2DLine(pLine2Start->x, pLine2Start->y, pLine2Delta->x, pLine2Delta->y, Coors8.x, Coors8.y, Coors6.x - Coors8.x, Coors6.y - Coors8.y)) { ret2 = true; }
//Assert(ret2 == retNew2);
bool inArea3Old = CPathFind::HelperIsPointWithinArbitraryArea(pLine1Start->x, pLine1Start->y, Coors5.x, Coors5.y, Coors6.x, Coors6.y, Coors7.x, Coors7.y, Coors8.x, Coors8.y);
bool inArea4Old = CPathFind::HelperIsPointWithinArbitraryArea(pLine2Start->x, pLine2Start->y, Coors5.x, Coors5.y, Coors6.x, Coors6.y, Coors7.x, Coors7.y, Coors8.x, Coors8.y);
//Assert(inArea3 == inArea3Old);
//Assert(inArea4 == inArea4Old);
//bool finalRetOld = ret || ret2 || inArea1Old || inArea2Old || inArea3Old || inArea4Old;
//Assert(finalRetOld == !IsFalseAll(combinedV));
//return !IsFalseAll(combinedV);
ret2 = ret2 | inArea3Old | inArea4Old;
bool retNew1 = sTestForThisAngleHelper(line1StartOtherV, line1DeltaOtherV, deltaVecV, line1StartV, line1DeltaV, line2StartV, line2DeltaV);
bool retNew2 = sTestForThisAngleHelper(line2StartOtherV, line2DeltaOtherV, deltaVecV, line1StartV, line1DeltaV, line2StartV, line2DeltaV);
Assert(ret == retNew1);
Assert(ret2 == retNew2);
#endif // __ASSERT
// Use the helper function to perform the tests.
// Note that we could consider using a vector bool to store the results and hold off on the branch,
// but it looks like there is a large enough chance of intersecting during the first test that
// it's probably worth the branch to avoid doing the second test sometimes.
if(sTestForThisAngleHelper(line1StartOtherV, line1DeltaOtherV, deltaVecV, line1StartV, line1DeltaV, line2StartV, line2DeltaV))
{
return true;
}
if(sTestForThisAngleHelper(line2StartOtherV, line2DeltaOtherV, deltaVecV, line1StartV, line1DeltaV, line2StartV, line2DeltaV))
{
return true;
}
return false;
}
#endif // USE_VECTORIZED_VEH_PED_OBJ_OBSTRUCTION_TESTS
/////////////////////////////////////////////////////////////////////////////////
#if !__FINAL
const char * CTaskVehicleGoToPointWithAvoidanceAutomobile::GetStaticStateName( s32 iState )
{
Assert(iState>=State_GoToPoint&&iState<=State_Swerve);
static const char* aStateNames[] =
{
"State_GoToPoint",
"State_ThreePointTurn",
"State_WaitForTraffic",
"State_WaitForPed",
"State_TemporarilyBrake",
"State_Swerve",
};
return aStateNames[iState];
}
#endif
/////////////////////////////////////////////////////////////////////////////////
// CTaskVehicleGoToPointAutomobile
/////////////////////////////////////////////////////////////////////////////////
CTaskVehicleGoToPointAutomobile::CTaskVehicleGoToPointAutomobile( const sVehicleMissionParams& params )
: CTaskVehicleGoTo(params)
, m_fMaxThrottleFromTraction(1.0f)
, m_bCanUseHandBrakeForTurns(true)
{
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_GOTO_POINT_AUTOMOBILE);
}
/////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleGoToPointAutomobile::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
FSM_Begin
// Initial state
FSM_State(State_GoToPoint)
FSM_OnEnter
GoToPoint_OnEnter(pVehicle);
FSM_OnUpdate
return GoToPoint_OnUpdate(pVehicle);
FSM_End
}
/////////////////////////////////////////////////////////////////////////////////
// State_GoTo
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointAutomobile::GoToPoint_OnEnter(CVehicle* pVehicle)
{
pVehicle->SwitchEngineOn();
// Update the local copy of the controls so its the same as the vehicles
m_vehicleControls.SetSteerAngle(pVehicle->GetSteerAngle());
m_vehicleControls.SetThrottle(pVehicle->GetThrottle());
m_vehicleControls.SetBrake(pVehicle->GetBrake());
m_vehicleControls.SetHandBrake(pVehicle->GetHandBrake());
}
EXT_PF_TIMER(VehicleGoToTask_Run);
aiTask::FSM_Return CTaskVehicleGoToPointAutomobile::GoToPoint_OnUpdate(CVehicle* pVehicle)
{
aiDeferredTasks::TaskData taskData(this, pVehicle, GetTimeStep(), true);
#if !__PS3
TUNE_GROUP_BOOL(AI_DEFERRED_TASKS, USE_DEFERRED_GOTO, true);
if(USE_DEFERRED_GOTO)
{
aiDeferredTasks::g_VehicleGoTo.Queue(taskData);
}
else
{
#endif
PF_START(VehicleGoToTask_Run);
GoToPoint_OnDeferredTask(taskData);
PF_STOP(VehicleGoToTask_Run);
#if !__PS3
}
#endif
return FSM_Continue;
}
void CTaskVehicleGoToPointAutomobile::GoToPoint_OnDeferredTask(const aiDeferredTasks::TaskData& data)
{
CVehicle* pVehicle = data.m_Vehicle;
const float& fTimeStep = data.m_TimeStep;
float dirToTargetOrientation, desiredSteerAngle;
Vector3 SpeedVector;
Vector3 Side, PlayerSpeed, Front;
#if __DEV
if(CVehicleIntelligence::m_bDisplayCarAiDebugInfo && CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVehicle))
{
grcDebugDraw::Line(m_Params.GetTargetPosition() - Vector3(1.0f,0.0f,0.0f),m_Params.GetTargetPosition() + Vector3(1.0f,0.0f,0.0f), Color32(1.0f, 1.0f, 1.0f, 1.0f));
grcDebugDraw::Line(m_Params.GetTargetPosition() - Vector3(0.0f,1.0f,0.0f),m_Params.GetTargetPosition() + Vector3(0.0f,1.0f,0.0f), Color32(1.0f, 1.0f, 1.0f, 1.0f));
grcDebugDraw::Line(m_Params.GetTargetPosition() - Vector3(0.0f,0.0f,1.0f),m_Params.GetTargetPosition() + Vector3(0.0f,0.0f,1.0f), Color32(1.0f, 1.0f, 1.0f, 1.0f));
}
#endif
// Get the current drive direction and orientation.
Vec3f vehForward;
LoadAsScalar(vehForward, pVehicle->GetVehicleForwardDirectionRef());
Vec3f vehDriveDir = IsDrivingFlagSet(DF_DriveInReverse) ? -vehForward : vehForward;
const float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.GetX(), vehDriveDir.GetY());
// Work out the proper steering angle.
// To make things a little bit more stable we aim ahead a little bit.
// TargetPoint_Ahead = TargetPoint + pTargetCar->GetB() * 3.0f;
Vec3f vecVehiclePosition;
LoadAsScalar(vecVehiclePosition, pVehicle->GetMatrixRef().GetCol3ConstRef());
dirToTargetOrientation = fwAngle::GetATanOfXY(m_Params.GetTargetPosition().x - vecVehiclePosition.GetX(), m_Params.GetTargetPosition().y - vecVehiclePosition.GetY());
desiredSteerAngle = SubtractAngleShorterFast(dirToTargetOrientation, vehDriveOrientation);
desiredSteerAngle = IsDrivingFlagSet(DF_DriveInReverse) ? -desiredSteerAngle : desiredSteerAngle;
// Clamp the steer angle to the vehicles ranges and adjust the gas
// and brake pedal input to try to achieve the desired speed (taking
// into account slopes).
float desiredSpeed = GetCruiseSpeed();
//if we want to drive in reverse, ensure we have a negative speed.
//if we want to drive forward, ensure we have a positive speed
#if __BANK
if (desiredSpeed < 0.0f)
{
pVehicle->GetIntelligence()->PrintTasks();
}
#endif //__BANK
taskAssertf(desiredSpeed >= 0.0f, "Negative cruise speed %.2f used for a task unsupported. To drive in reverse, use the DF_DriveInReverse flag", desiredSpeed);
if (IsDrivingFlagSet(DF_DriveInReverse))
{
desiredSpeed = -fabsf(desiredSpeed);
}
else
{
desiredSpeed = fabsf(desiredSpeed);
}
bool bIsInVehicleChase = (pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsInVehicleChase));
bool bIsSteerIntoSkidsFlagSet = (pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_SteerIntoSkids));
bool bWantsToSteerIntoSkids = (bIsInVehicleChase || bIsSteerIntoSkidsFlagSet);
#if __BANK
TUNE_GROUP_BOOL(CAR_AI, bForceSteerIntoSkids, false);
if(bForceSteerIntoSkids)
{
bWantsToSteerIntoSkids = true;
}
#endif
bool bSteerIntoSkids = (!pVehicle->m_nVehicleFlags.bIsDoingHandBrakeTurn && bWantsToSteerIntoSkids);
if(bSteerIntoSkids)
{
TUNE_GROUP_FLOAT(CAR_AI, sfSideSlipSteerInfluence, -0.4f, -10.0f, 10.0f, 0.1f);
float fSideSlip = DotProduct(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetA()), pVehicle->GetAiVelocity());
fSideSlip /= Abs(DotProduct(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetB()), pVehicle->GetAiVelocity())) + 1.0f;
desiredSteerAngle += fSideSlip * sfSideSlipSteerInfluence;
}
//copy this over to the stack so we aren't constantly fetching it from memory.
//it gets hammered a lot in AdjustControls and HumaniseCarControlInput
CVehControls vehControls;
vehControls.Copy(m_vehicleControls);
AdjustControls(pVehicle, &vehControls, desiredSteerAngle, desiredSpeed);
const float fCurrentVelocitySqr = pVehicle->GetAiVelocity().Mag2();
//Check if we can use the handbrake for turns.
bool bWasUsingHandBrake = vehControls.m_handBrake;
if(m_bCanUseHandBrakeForTurns)
{
// If we're going very fast we use the handbrake to turn
static dev_float s_fVelThresholdForModerateHandbrakeTurns = 20.0f;
static dev_float s_fVelThresholdForExtremeHandbrakeTurns = 10.0f;
if(fCurrentVelocitySqr > (s_fVelThresholdForModerateHandbrakeTurns * s_fVelThresholdForModerateHandbrakeTurns) && ABS(desiredSteerAngle) > 0.7f)
{
//grcDebugDraw::Sphere(pVehicle->GetVehiclePosition(), 2.0f, Color_yellow, false);
vehControls.m_handBrake = true;
}
else if (fCurrentVelocitySqr > (s_fVelThresholdForExtremeHandbrakeTurns * s_fVelThresholdForExtremeHandbrakeTurns)
&& ABS(desiredSteerAngle) > HALF_PI)
{
//grcDebugDraw::Sphere(pVehicle->GetVehiclePosition(), 2.0f, Color_red, false);
vehControls.m_handBrake = true;
}
}
pVehicle->m_nVehicleFlags.bIsDoingHandBrakeTurn = (!bWasUsingHandBrake && vehControls.m_handBrake);
// TUNE_GROUP_BOOL(ASDF, EverybodyVeerRight, false);
// TUNE_GROUP_BOOL(ASDF, EverybodyVeerLeft, false);
// if (EverybodyVeerRight)
// {
// vehControls.m_steerAngle = -1.0f;
// }
// else if (EverybodyVeerLeft)
// {
// vehControls.m_steerAngle = 1.0f;
// }
// The values we found may be completely different from the ones last frame.
// The following function smooths out the values and in some cases clips them.
HumaniseCarControlInput( pVehicle, &vehControls, pVehicle->GetIntelligence()->GetHumaniseControls(), (fCurrentVelocitySqr <(5.0f*5.0f)), fTimeStep, desiredSpeed);
m_vehicleControls.Copy(vehControls);
if ( pVehicle->InheritsFromPlane() )
{
static bool s_enable_plane_controls = true;
if ( s_enable_plane_controls )
{
CPlane* pPlane = static_cast<CPlane*>(pVehicle);
pPlane->SetThrottleControl(m_vehicleControls.GetThrottle());
// turn off wing lift so we don't take off
pPlane->SetVirtualSpeedControl(0.0f);
static dev_float s_OrientationDeltaToTurnMax = 10.0f;
float fOrientationDeltaScaled = desiredSteerAngle / ( s_OrientationDeltaToTurnMax * DtoR );
const float fYawControl = -pPlane->GetPlaneIntelligence()->GetYawController().Update(fOrientationDeltaScaled);
const float fYawControlClamped = Clamp(fYawControl, -5.0f, 5.0f);
pPlane->SetYawControl(fYawControlClamped);
}
}
//not sure how this could become > 1.0f, changing the compare value for now
//since I'm not sure yet if it's a bug
//Assertf(pVehicle->GetSteerAngle() > -1.1f && pVehicle->GetSteerAngle() < 1.1f, "Steer angle out of range: %.2f", pVehicle->GetSteerAngle());
}
/////////////////////////////////////////////////////////////////////////////////
// AdjustControls
// Clamp the steer angle to the vehicles ranges and adjust the gas
// and brake pedal input to try to achieve the desired speed (taking
// into account slopes).
/////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleGoToPointAutomobile::AdjustControls(CVehicle* pVehicle, CVehControls* pVehControls, const float desiredSteerAngle, const float desiredSpeed)
{
Assertf(pVehicle, "AdjustControls expected a valid set vehicle.");
Assertf(pVehControls, "AdjustControls expected a valid set of vehicle controls.");
CVehControls vehControls;
vehControls.Copy(*pVehControls);
#if __DEV
if(CVehicleIntelligence::AllowCarAiDebugDisplayForThisVehicle(pVehicle))
{
TUNE_GROUP_BOOL(CAR_AI, showSteerDir, false);
if(showSteerDir)
{
// Get the current drive direction and orientation.
const Vector3 vehDriveDir = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVehicle, IsDrivingFlagSet(DF_DriveInReverse)));
const float vehDriveOrientation = fwAngle::GetATanOfXY(vehDriveDir.x, vehDriveDir.y);
TUNE_GROUP_FLOAT(CAR_AI, alpha, 1.0f, 0.0f, 1.0f, 0.01f);
Color32 col(0.0f, 1.0f, 0.0f, alpha);
Vector3 startPos = VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
Vector3 endPos = startPos +(Vector3(rage::Cosf(desiredSteerAngle + vehDriveOrientation), rage::Sinf(desiredSteerAngle + vehDriveOrientation), 0.0f) * 5.0f);
grcDebugDraw::Line(startPos, endPos, col);
}
}
#endif // __DEV
// Clip steering to sensible values
const float maxSteerAngle = pVehicle->GetIntelligence()->FindMaxSteerAngle();
const float newSteerAngle = rage::Clamp(desiredSteerAngle, -maxSteerAngle, maxSteerAngle);
Assert(newSteerAngle > -2.0f && newSteerAngle < 2.0f);
TUNE_GROUP_FLOAT (CAR_AI, MinSteerAngleToAdjustThrottle, 0.05f, 0.0f, 100.0f, 0.1f);
const float fSteeringAngleToAffectThrottle = rage::Clamp(rage::Abs(newSteerAngle)-MinSteerAngleToAdjustThrottle, 0.0f, maxSteerAngle);
const float portionOfSteerMaxAngle = fSteeringAngleToAffectThrottle / maxSteerAngle;
const float fInverseMaxGasAllowed = pVehicle->GetAiXYSpeed() > 10.0f ? 0.7f : 0.5f;
const float maxGasAllowed = 1.0f -(fInverseMaxGasAllowed * portionOfSteerMaxAngle); // Only allowed 30% gas when at maximum steering angle.
// Work out the speed so that we can adjust it with gas/brakes.
Vec3f VehForwardVector;
LoadAsScalar(VehForwardVector, pVehicle->GetVehicleForwardDirectionRef());
Vec3f VehVelocity;
LoadAsScalar(VehVelocity, pVehicle->GetAiVelocityConstRef());
float forwardSpeed = Dot(VehVelocity, VehForwardVector);
const float speedDiffDesired = desiredSpeed - forwardSpeed; // forwardSpeed is in m/s. 40km/u=11.1m/s 60=16.7 80=22.2 100=27.8 120=33.3 140=38.9
float slopeGasMultiplier = 1.0f;
float slopeBrakeMultiplier = 1.0f;
const float slope = VehForwardVector.GetZ();// Positive = uphill.
const float slopeGasMultiplierForward = 1.0f + rage::Max(0.0f, slope * 6.0f);
const float slopeGasMultiplierBackwards = 1.0f + rage::Max(0.0f, -slope * 5.0f);
const float slopeBrakeMultiplierForward = 1.0f + rage::Max(0.0f, -slope * 4.0f);
const float slopeBrakeMultiplierBackwards = 1.0f + rage::Max(0.0f, slope * 4.0f);
const float maxGas = 1.0f;
float maxBrake = 1.0f;
const float minGas = -1.0f;
const float minBrake = -1.0f;
vehControls.m_brake = 0.0f;
if (rage::Abs(desiredSpeed) > 0.05f || rage::Abs(speedDiffDesired) > 0.5f) // was 0.03f
{
if(desiredSpeed < 0.0f)
{
// Car is trying to go backwards, but moving forward at a moderate clip
if(forwardSpeed > 2.0f)
{
vehControls.m_brake = 1.0f;
vehControls.m_throttle = 0.0f;
}
else if(speedDiffDesired < 0.0f)
{
// We should accelerate backwards
if(forwardSpeed > -2.0f) // Accelerate quickly initially so that reverse fudge doesn't kick in
{
slopeGasMultiplier = slopeGasMultiplierBackwards;
vehControls.m_throttle = speedDiffDesired / 4.0f;
}
else
{
slopeGasMultiplier = slopeGasMultiplierBackwards;
vehControls.m_throttle = speedDiffDesired / 9.0f;
}
}
else
{
// We should slow down a bit
vehControls.m_throttle = 0.0f;
// Going too fast. Use brake to slow down
slopeBrakeMultiplier = slopeBrakeMultiplierBackwards;
maxBrake = 0.5f;
vehControls.m_brake = speedDiffDesired / 12.0f;
}
}
else // Traveling forward
{
if( speedDiffDesired > 0.0f )
{
TUNE_GROUP_FLOAT (CAR_AI, ACC_TO_THROTTLE_MAPPING_SLOW, 4.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT (CAR_AI, ACC_TO_THROTTLE_MAPPING_MED, 8.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT (CAR_AI, ACC_TO_THROTTLE_MAPPING_HIGH, 2.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT (CAR_AI, LOW_THROTTLE_MAPPING_THRESHOLD, 2.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT (CAR_AI, MED_THROTTLE_MAPPING_THRESHOLD, 10.0f, 0.0f, 100.0f, 0.1f);
if(forwardSpeed < LOW_THROTTLE_MAPPING_THRESHOLD) // Accelerate quickly initially so that reverse fudge doesn't kick in
{
slopeGasMultiplier = slopeGasMultiplierForward;
vehControls.m_throttle = speedDiffDesired / ACC_TO_THROTTLE_MAPPING_SLOW;
}
else if(forwardSpeed < MED_THROTTLE_MAPPING_THRESHOLD)
{
slopeGasMultiplier = slopeGasMultiplierForward;
vehControls.m_throttle = speedDiffDesired / ACC_TO_THROTTLE_MAPPING_MED;
}
else
{
slopeGasMultiplier = slopeGasMultiplierForward;
vehControls.m_throttle = speedDiffDesired / ACC_TO_THROTTLE_MAPPING_HIGH;
}
}
else
{
if ( pVehicle->InheritsFromBoat() )
{
TUNE_GROUP_FLOAT (CAR_AI, BOAT_SLOWDOWN_THROTTLE_MAPPING_THRESHOLD, 2.0f, 0.0f, 100.0f, 0.1f);
vehControls.m_throttle = speedDiffDesired / BOAT_SLOWDOWN_THROTTLE_MAPPING_THRESHOLD;
}
else
{
vehControls.m_throttle = 0.0f;
}
// Going too fast. Use brake to slow down
slopeBrakeMultiplier = slopeBrakeMultiplierForward;
vehControls.m_brake = -speedDiffDesired / 10.0f;
if(pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsInVehicleChase))
{
maxBrake = 1.0f;
}
else if(GetShouldObeyTrafficLights() &&
(pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_LIGHTS
|| pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_WAIT_FOR_TRAFFIC
|| pVehicle->GetIntelligence()->GetJunctionCommand() == JUNCTION_COMMAND_GIVE_WAY))
{
maxBrake = 0.9f; // more aggressive braking for stopping at junctions
}
else
{
maxBrake = 0.75f; // original default value
}
}
}
// Depending on our steering angle we're only allowed to accelerate a certain amount.
// This should avoid cars skidding out when they accelerate out of bends.
vehControls.m_throttle = rage::Min(maxGasAllowed, vehControls.m_throttle);
vehControls.m_throttle *= slopeGasMultiplier;
vehControls.m_brake *= slopeBrakeMultiplier;
vehControls.m_throttle = rage::Clamp((vehControls.m_throttle), minGas, maxGas);
vehControls.m_brake = rage::Clamp((vehControls.m_brake), minBrake, maxBrake);
}
else
{
// Special case if we want to stop and are going slowly. Use full brakes.
// This stops cars from sneaking past traffic lights.
vehControls.m_brake = 1.0f;
vehControls.m_throttle = 0.0f;
}
// We have the problem where the car is steering fully and accelerating at the same time.
// This causes traction to be used up by the acceleration and the steering to not happen.
// Fix this here(if steering extremely don't accelerate)
const float gasDownMult = portionOfSteerMaxAngle * rage::Clamp(((forwardSpeed - 8.0f) / 8.0f), 0.0f, 1.0f);
TUNE_GROUP_FLOAT (CAR_AI, SLIP_THROTTLE_REDUCTION_LERP, 0.3f, 0.0f, 1.0f, 0.01f);
// Finally apply reduction based on the traction available through the wheels
const float fMaxThrottleFromTraction = CalculateMaximumThrottleBasedOnTraction(pVehicle);
const float reducedMaxThrottle = Lerp(SLIP_THROTTLE_REDUCTION_LERP, m_fMaxThrottleFromTraction, fMaxThrottleFromTraction);
m_fMaxThrottleFromTraction = reducedMaxThrottle;
// If we're slipping at all, remove any power increase
if( pVehicle->GetCheatPowerIncrease() > 1.0f )
{
if( m_fMaxThrottleFromTraction < 1.0f )
{
pVehicle->SetCheatPowerIncrease(Lerp(m_fMaxThrottleFromTraction, 1.0f, pVehicle->GetCheatPowerIncrease()));
}
}
float fDesiredThrottle = (vehControls.m_throttle)*(1.0f - gasDownMult);
fDesiredThrottle = rage::Min(fDesiredThrottle, reducedMaxThrottle);
vehControls.m_throttle = fDesiredThrottle;
vehControls.SetSteerAngle( newSteerAngle );
//reverse the controls if we have momentum in the opposite direction from the one we want to go in
if (
((forwardSpeed < 0.0f && desiredSpeed > 1.0f)
||
(forwardSpeed > 0.0f && desiredSpeed < -1.0f))
)
{
vehControls.SetSteerAngle( -newSteerAngle );
}
vehControls.m_handBrake = false;
// Boats will use their handbrake of they need to make a tight corner.
if(pVehicle->GetVehicleType() == VEHICLE_TYPE_BOAT)
{
if(forwardSpeed > 2.0f && rage::Abs(desiredSteerAngle) > ( DtoR * 25.0f))
{
vehControls.m_handBrake = true;
}
}
Assert(vehControls.m_steerAngle >= -HALF_PI && vehControls.m_steerAngle <= HALF_PI && vehControls.m_steerAngle == vehControls.m_steerAngle);
Assert(vehControls.m_throttle >= -1.0f && vehControls.m_throttle <= 1.0f);
Assert(vehControls.m_brake >= 0.0f && vehControls.m_brake <= 1.0f);
pVehControls->Copy(vehControls);
}
////////////////////////////////////////////////////
// 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 CTaskVehicleGoToPointAutomobile::HumaniseCarControlInput(CVehicle* RESTRICT pVeh, CVehControls* RESTRICT pVehControls
, bool bConservativeDriving, bool bGoingSlowly, const float fTimeStep, const float fDesiredSpeed)
{
Assertf(pVeh, "HumaniseCarControlInput expected a valid set vehicle.");
Assertf(pVehControls, "HumaniseCarControlInput expected a valid set of vehicle controls.");
if( bConservativeDriving || bGoingSlowly )
{
// Humanize the steering
const bool bStopped = pVeh->GetAiXYSpeed() < 0.1;
const float steerAngleDiff = pVehControls->m_steerAngle - pVeh->GetSteerAngle();
const float fMultiplier = bStopped ? 0.5f : 2.0f;
const float maxsteerAngleChange = (fMultiplier) * fTimeStep;
pVehControls->SetSteerAngle( pVeh->GetSteerAngle() + Clamp(steerAngleDiff, -maxsteerAngleChange, 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 = Selectf(brakeDiff, 10.0f, 1.0f) * fTimeStep;
pVehControls->m_brake = pVeh->GetBrake() + Clamp(brakeDiff, -maxBrakeChange, maxBrakeChange);
const bool bDrivingInReverse = fDesiredSpeed < -0.05f;
const float fForwardSpeed = pVeh->GetAiVelocity().Dot(VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleDriveDir(pVeh, bDrivingInReverse)));
//only worry about fiddling with the throttle if we're not reversing
//and not moving backward
if(pVehControls->m_throttle > 0.0f && fForwardSpeed > -0.05f)
{
// Humanize the gas.
// Sensible cars don't go flat out.
const float gassDiff = pVehControls->m_throttle - pVeh->GetThrottle();
const float maxGasChange = Selectf(gassDiff, 1.0f, 10.0f) * fTimeStep;
pVehControls->m_throttle = pVeh->GetThrottle() + Clamp(gassDiff, -maxGasChange, 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->GetVehicleForwardDirectionRef().GetZf()));
//for conservative drivers, always end up clamping to 0.99 since there is some
//special case "wheels spinning" behavior for when the throttle is exactly 1.0
//this is intentionally done after the addition above since we had problems with
//wheels slipping on upward slopes (see b* 1737917)
maxGas = rage::Min(maxGas, 0.99f);
pVehControls->m_throttle = rage::Min(pVehControls->m_throttle, maxGas);
}
}
pVeh->SwitchEngineOn();
pVeh->SetSteerAngle(pVehControls->m_steerAngle);
pVeh->SetThrottle(pVehControls->m_throttle);
pVeh->SetBrake(pVehControls->m_brake);
pVeh->SetHandBrake(pVehControls->m_handBrake);
}
float CTaskVehicleGoToPointAutomobile::CalculateMaximumThrottleBasedOnTraction(const CVehicle* pVehicle)
{
eVehicleDummyMode dummyMode = pVehicle->GetVehicleAiLod().GetDummyMode();
if(dummyMode == VDM_SUPERDUMMY)
{
return 1.0f;
}
TUNE_GROUP_FLOAT (CAR_AI, SLIP_ANGLE_MIN, 0.95f, 0.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT (CAR_AI, SLIP_ANGLE_MAX, 2.5f, 0.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT (CAR_AI, SLIP_THROTTLE_REDUCTION_MIN, 0.0f, 0.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT (CAR_AI, SLIP_THROTTLE_REDUCTION_MAX, 1.0f, 0.0f, 10.0f, 0.01f);
float fMaxSlipAngle = 0.0f;
// Preload all the wheels (we'll still probably get a partial cache miss on the first wheel, but
// by the time we need the next ones they should already be cached)
const CWheel * const * ppWheels = pVehicle->GetWheels();
const int iNumWheels = pVehicle->GetNumWheels();
for(s32 i = 0; i < iNumWheels; i++)
{
const CWheel* wheel = ppWheels[i];
PrefetchObject(&wheel->GetDynamicFlags());
PrefetchObject(&wheel->GetEffectiveSlipAngleRef());
}
for(s32 i = 0 ; i < iNumWheels; i++)
{
const CWheel* wheel = ppWheels[i];
if(wheel->GetIsTouching())
{
fMaxSlipAngle = rage::Max(fMaxSlipAngle, wheel->GetEffectiveSlipAngle());
}
}
const float fSlipAnglePerc = Clamp((fMaxSlipAngle-SLIP_ANGLE_MIN)/(SLIP_ANGLE_MAX-SLIP_ANGLE_MIN), 0.0f, 1.0f);
const float fMaxThrottle = Lerp(fSlipAnglePerc, SLIP_THROTTLE_REDUCTION_MAX, SLIP_THROTTLE_REDUCTION_MIN);
return fMaxThrottle;
}
/////////////////////////////////////////////////////////////////////////////////////
#if !__FINAL
const char * CTaskVehicleGoToPointAutomobile::GetStaticStateName( s32 iState )
{
Assert(iState>=State_GoToPoint&&iState<=State_GoToPoint);
static const char* aStateNames[] =
{
"State_GoToPoint",
};
return aStateNames[iState];
}
#endif
dev_float CTaskVehicleGoToNavmesh::ms_fBoundRadiusMultiplier = 0.85f;
dev_float CTaskVehicleGoToNavmesh::ms_fBoundRadiusMultiplierBike = 0.9f;
dev_float CTaskVehicleGoToNavmesh::ms_fMaxNavigableAngleRadians = 35.0f * DtoR;
CTaskVehicleGoToNavmesh::CTaskVehicleGoToNavmesh(const sVehicleMissionParams& params)
:CTaskVehicleMissionBase(params)
, m_fPathLength(0.0f)
, m_iOnRoadCheckTimer(0)
, m_bWasOnRoad(false)
{
m_RouteSearchHelper.ResetSearch();
m_iNumSearchFailures = 0;
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_GOTO_NAVMESH);
}
CTaskVehicleGoToNavmesh::~CTaskVehicleGoToNavmesh()
{
m_RouteSearchHelper.ResetSearch();
}
void CTaskVehicleGoToNavmesh::CleanUp()
{
CVehicle *pVehicle = GetVehicle();
if (pVehicle)
{
pVehicle->m_nVehicleFlags.bDisableRoadExtentsForAvoidance = false;
}
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::ProcessPreFSM()
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
if (pVehicle)
{
//only allow dummying if we're on a road
if(fwTimer::GetTimeInMilliseconds() - m_iOnRoadCheckTimer > 500)
{
m_bWasOnRoad = CTaskVehicleThreePointTurn::IsPointOnRoad(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()), pVehicle);
m_iOnRoadCheckTimer = fwTimer::GetTimeInMilliseconds();
}
pVehicle->m_nVehicleFlags.bTasksAllowDummying = m_bWasOnRoad;
//assume all navmesh navigation is dirty
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
//we don't have proper width or camber information, so prevent
//dummying unless we have to
pVehicle->m_nVehicleFlags.bPreventBeingDummyUnlessCollisionLoadedThisFrame = true;
}
return FSM_Continue;
}
CTask::FSM_Return CTaskVehicleGoToNavmesh::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
FSM_Begin
FSM_State(State_Start)
FSM_OnEnter
return Start_OnEnter();
FSM_OnUpdate
return Start_OnUpdate();
FSM_State(State_WaitingForResults)
FSM_OnEnter
return WaitingForResults_OnEnter();
FSM_OnUpdate
return WaitingForResults_OnUpdate();
FSM_State(State_Goto)
FSM_OnEnter
return Goto_OnEnter();
FSM_OnUpdate
return Goto_OnUpdate();
FSM_State(State_Stop)
FSM_OnEnter
return Stop_OnEnter();
FSM_OnUpdate
return Stop_OnUpdate();
FSM_End
}
void CTaskVehicleGoToNavmesh::CloneUpdate(CVehicle* pVehicle)
{
CTaskVehicleMissionBase::CloneUpdate(pVehicle);
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
}
const Vector3& CTaskVehicleGoToNavmesh::GetClosestPointFoundToTarget() const
{
if ( GetState() == State_Goto )
{
return m_RouteSearchHelper.GetClosestPointFoundToTarget();
}
return m_Params.GetTargetPosition();
}
//TODO: this and its counterparts in CTaskVehicleCruiseNew and CTaskVehicleFollowWaypointRecording could be combined and
//moved into the follow route task helper
float CTaskVehicleGoToNavmesh::FindTargetPositionAndCapSpeed( CVehicle* pVehicle, const Vector3& vStartCoords, Vector3& vTargetPosition, float& fOutSpeed )
{
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfDistanceToProgressRoute, 0.01f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfCruiseSpeedFraction, 0.2f, 0.0f, 1.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfMaxLookaheadSpeed, 32.0f, 0.0f, 100.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfDefaultLookahead, 4.0f, 0.0f, 100.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfWheelbaseMultiplier, 0.5f, 0.0f, 1.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfSpeedMultiplier, 1.0f, 0.0f, 1.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfSpeedThreshold, 8.0f, 0.0f, 100.0f, 0.01f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfNavmeshMaxLookahead, 8.0f, 0.0f, 100.0f, 0.01f);
// Determine the amount to look down the path.
// First work out an approximate speed we should consider for lookahead
// Use the speed we calculated last time as a base for the lookahead distance.
CTaskVehicleGoToPointWithAvoidanceAutomobile* pTask = static_cast<CTaskVehicleGoToPointWithAvoidanceAutomobile*>(FindSubTaskOfType(CTaskTypes::TASK_VEHICLE_GOTO_POINT_WITH_AVOIDANCE_AUTOMOBILE));
const float fDesiredSpeed = pTask ? pTask->GetCruiseSpeed() : GetCruiseSpeed();
const float fCurrentSpeed = pVehicle->GetVelocity().XYMag();
float speedForLookAheadDist = rage::Min((sfCruiseSpeedFraction * fDesiredSpeed) + ((1.0f - sfCruiseSpeedFraction) * fCurrentSpeed), sfMaxLookaheadSpeed);
// Include that whilst considering the wheel base length to work out a lookahead distance.
float lookAheadDist = sfDefaultLookahead * (1.0f + sfWheelbaseMultiplier * pVehicle->GetVehicleModelInfo()->GetWheelBaseMultiplier()) +
sfSpeedMultiplier * rage::Max(0.0f, speedForLookAheadDist - sfSpeedThreshold);
lookAheadDist = rage::Min(sfNavmeshMaxLookahead, lookAheadDist);
const float fDistSearched = m_followRoute.FindTargetCoorsAndSpeed(pVehicle, lookAheadDist, sfDistanceToProgressRoute
, RCC_VEC3V(vStartCoords), IsDrivingFlagSet(DF_DriveInReverse), RC_VEC3V(vTargetPosition), fOutSpeed, false, true, false, -1, false);
// boat slow down
if ( pVehicle->InheritsFromBoat() )
{
static bool s_SlowDownForDirectionToPath = true;
if ( s_SlowDownForDirectionToPath )
{
Vector3 vTangent;
Vec3V vForward = pVehicle->GetTransform().GetForward();
Vec3V vSegment = VECTOR3_TO_VEC3V(m_followRoute.ComputeCurrentSegmentTangent(vTangent));
fOutSpeed *= 0.5f + ((1.0f + Dot(vForward, vSegment).Getf()) / 2.0f) / 2.0f;
}
}
return fDistSearched;
}
void CTaskVehicleGoToNavmesh::ResetLastTargetPos()
{
FindTargetCoors(GetVehicle(), m_vLastTargetPosition);
}
bool CTaskVehicleGoToNavmesh::UpdateTargetMoved()
{
//don't do this if we aren't something that can move
if (!HasMovingTarget())
{
return false;
}
static dev_float s_fTargetMovedThresholdSqr = 5.0f * 5.0f;
Vector3 vCurrentTarget;
FindTargetCoors(GetVehicle(), vCurrentTarget);
if (vCurrentTarget.Dist2(m_vLastTargetPosition) > s_fTargetMovedThresholdSqr)
{
m_vLastTargetPosition = vCurrentTarget;
return true;
}
//don't update the last target pos otherwise, so if we're slowly creepin along,
//we'll eventually trigger a replan when we hit the threshold, unstead of staying
//underneath the threshold each frame
return false;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Start_OnEnter()
{
CVehicle* pVehicle = GetVehicle();
FindTargetCoors(pVehicle, m_Params);
u64 iPathFlags = PATH_FLAG_NEVER_CLIMB_OVER_STUFF|PATH_FLAG_NEVER_DROP_FROM_HEIGHT
|PATH_FLAG_NEVER_USE_LADDERS|PATH_FLAG_DONT_LIMIT_SEARCH_EXTENTS|PATH_FLAG_DONT_AVOID_DYNAMIC_OBJECTS;
if (!(pVehicle->GetVehicleType() == VEHICLE_TYPE_BOAT || pVehicle->GetVehicleType() == VEHICLE_TYPE_SUBMARINE))
{
iPathFlags |= PATH_FLAG_NEVER_ENTER_WATER;
iPathFlags |= PATH_FLAG_NEVER_START_IN_WATER;
}
else
{
iPathFlags |= PATH_FLAG_NEVER_LEAVE_WATER;
iPathFlags |= PATH_FLAG_USE_BEST_ALTERNATE_ROUTE_IF_NONE_FOUND;
}
float fReferenceDistance = 0.0f;
if ( IsDrivingFlagSet(DF_AvoidTargetCoors) )
{
static float s_FleeDistance = 30.0f;
fReferenceDistance = s_FleeDistance;
iPathFlags |= PATH_FLAG_FLEE_TARGET;
}
const Vector3 vStartCoords = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPositionForNavmeshQueries(pVehicle, IsDrivingFlagSet(DF_DriveInReverse)));
const float fBoundRadius = pVehicle->GetBoundRadius();
spdAABB tempBox;
const spdAABB& boundBox = pVehicle->GetLocalSpaceBoundBox(tempBox);
//create the influence sphere. this originates at the rear bonnect position (or front if going in reverse)
//of the vehicle and is used to prevent us from getting navmesh queries that originate from the from bonnet
//and go directly behind the vehicle. it doesn't need to be too accurate--all we really want is for the pathfinder
//to make the decision about whether to turn left or right for us, since avoidance doesn't do a great job of that
static const u32 nNumInfluenceSpheres = 2;
TInfluenceSphere influnceSpheres[nNumInfluenceSpheres];
const Vector3 vInfluenceSphereCoordsBase = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, !IsDrivingFlagSet(DF_DriveInReverse)));
const Vector3 vVehicleRightDir = VEC3V_TO_VECTOR3(pVehicle->GetVehicleRightDirection());
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, LocalSphereInnerMultiplier, 5.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, LocalSphereOuterMultiplier, 1.0f, 0.0f, 100.0f, 0.1f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, LocalSphereRadiusMultiplier, 1.0f, 0.0f, 100.0f, 0.1f);
const float fInfluenceSphereRadius = fBoundRadius * LocalSphereRadiusMultiplier;
influnceSpheres[0].Init(vInfluenceSphereCoordsBase + vVehicleRightDir * boundBox.GetMax().GetXf(), fInfluenceSphereRadius
, LocalSphereInnerMultiplier * INFLUENCE_SPHERE_MULTIPLIER, LocalSphereOuterMultiplier * INFLUENCE_SPHERE_MULTIPLIER);
influnceSpheres[1].Init(vInfluenceSphereCoordsBase + vVehicleRightDir * boundBox.GetMin().GetXf(), fInfluenceSphereRadius
, LocalSphereInnerMultiplier * INFLUENCE_SPHERE_MULTIPLIER, LocalSphereOuterMultiplier * INFLUENCE_SPHERE_MULTIPLIER);
const float fBoundRadiusMultiplierToUse = pVehicle->InheritsFromBike() ? ms_fBoundRadiusMultiplierBike : ms_fBoundRadiusMultiplier;
m_RouteSearchHelper.SetEntityRadius(fBoundRadius * fBoundRadiusMultiplierToUse);
m_RouteSearchHelper.SetMaxNavigableAngle(ms_fMaxNavigableAngleRadians);
m_RouteSearchHelper.StartSearch( NULL, vStartCoords, m_Params.GetTargetPosition(), m_Params.m_fTargetArriveDist, iPathFlags, nNumInfluenceSpheres, &influnceSpheres[0], fReferenceDistance, NULL, true );
ResetLastTargetPos();
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Start_OnUpdate()
{
SetState(State_WaitingForResults);
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::WaitingForResults_OnEnter()
{
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::WaitingForResults_OnUpdate()
{
if (!m_RouteSearchHelper.IsSearchActive())
{
if (m_iNumSearchFailures >= 3)
{
SetState(State_Stop);
}
else if( GetTimeInState() > 1.0f )
{
++m_iNumSearchFailures;
SetState(State_Start);
}
return FSM_Continue;
}
float fDistance = 0.0f;
Vector3 vLastPos(0.0f, 0.0f, 0.0f);
SearchStatus eSearchStatus;
static const s32 sMaxPathPoints = CVehicleFollowRouteHelper::MAX_ROUTE_SIZE;
s32 iPathPoints = sMaxPathPoints;
Vector3 pvRoutePoints[sMaxPathPoints];
if (m_RouteSearchHelper.GetSearchResults(eSearchStatus, fDistance, vLastPos, &pvRoutePoints[0], &iPathPoints))
{
//we shouldn't have to check iPathPoints here, but I was seeing some cases where searchstatus was
//SS_SearchSuccessful and only one point was being returned. I put in a bug for JB to take a look at why that
//is and am just handling the case here -JM
if( eSearchStatus == SS_SearchSuccessful && iPathPoints > 1 )
{
m_iNumSearchFailures = 0;
m_followRoute.ConstructFromNavmeshRoute(GetVehicle(), pvRoutePoints, iPathPoints);
SetState(State_Goto);
return FSM_Continue;
}
else if ( eSearchStatus == SS_SearchFailed )
{
++m_iNumSearchFailures;
SetState(State_Stop);
return FSM_Continue;
}
}
//I saw some longer queries take long enough to trigger a 3 point turn
GetVehicle()->GetIntelligence()->UpdateCarHasReasonToBeStopped();
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Goto_OnEnter()
{
// Calculate the initial target position
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
const Vector3 vStartCoords = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, IsDrivingFlagSet(DF_DriveInReverse)));
Vector3 vTargetPosition;
float fSpeed = GetCruiseSpeed();
m_fPathLength = FindTargetPositionAndCapSpeed(pVehicle, vStartCoords, vTargetPosition, fSpeed);
sVehicleMissionParams params = m_Params;
params.SetTargetPosition(vTargetPosition);
params.m_fTargetArriveDist = 0.0f;
params.m_fCruiseSpeed = fSpeed;
if (pVehicle->InheritsFromPlane())
{
params.m_iDrivingFlags = DMode_StopForCars_Strict;
}
//params.m_TargetEntity = NULL;
SetNewTask( rage_new CTaskVehicleGoToPointWithAvoidanceAutomobile(params, true));
pVehicle->m_nVehicleFlags.bDisableRoadExtentsForAvoidance = true;
ResetLastTargetPos();
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Goto_OnUpdate()
{
CVehicle* pVehicle = GetVehicle();
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfDistanceToFindNewPath, 25.0f, 0.0f, 999.0f, 0.1f);
TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfMinTimeToFindNewPath, 0.5f, 0.0f, 100.0f, 0.1f);
//TUNE_GROUP_FLOAT(VEHICLE_GOTO_NAVMESH, sfDistanceToConsiderRouteCompleted, 2.0f, 0.0f, 100.0f, 0.1f);
const Vector3 vStartCoords = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(pVehicle, IsDrivingFlagSet(DF_DriveInReverse)));
Vector3 vTargetPosition;
float fSpeed = GetCruiseSpeed();
float fDistanceToTheEndOfRoute = FindTargetPositionAndCapSpeed(pVehicle, vStartCoords, vTargetPosition, fSpeed);
const float fDistToTargetSqr = (vStartCoords - m_RouteSearchHelper.GetClosestPointFoundToTarget()).XYMag2();
if (fDistToTargetSqr < m_Params.m_fTargetArriveDist*m_Params.m_fTargetArriveDist)
{
SetState(State_Stop);
return FSM_Continue;
}
//update progress on the waypoint recording here and regen our follow route if necessary
if( fDistanceToTheEndOfRoute < rage::Min( m_fPathLength*0.5f, sfDistanceToFindNewPath ) && GetTimeInState() > sfMinTimeToFindNewPath )
{
//update path length
m_fPathLength = FindTargetPositionAndCapSpeed(pVehicle, vStartCoords, vTargetPosition, fSpeed);
fDistanceToTheEndOfRoute = m_fPathLength;
SetState(State_Start);
return FSM_Continue;
}
//we may need to re-path to the target if it's moved a significant amount
if (UpdateTargetMoved())
{
SetState(State_Start);
return FSM_Continue;
}
// set the target for the gotopointwithavoidance task
if(GetSubTask() && GetSubTask()->GetTaskType()==CTaskTypes::TASK_VEHICLE_GOTO_POINT_WITH_AVOIDANCE_AUTOMOBILE)
{
CTaskVehicleGoToPointWithAvoidanceAutomobile * pGoToTask = (CTaskVehicleGoToPointWithAvoidanceAutomobile*)GetSubTask();
pGoToTask->SetTargetPosition(&vTargetPosition);
pGoToTask->SetCruiseSpeed(fSpeed);
pVehicle->m_nVehicleFlags.bDisableRoadExtentsForAvoidance = true;
}
return FSM_Continue;
}
void CTaskVehicleGoToNavmesh::Goto_OnExit()
{
CVehicle* pVehicle = GetVehicle();
if(pVehicle)
{
pVehicle->m_nVehicleFlags.bDisableRoadExtentsForAvoidance = false;
}
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Stop_OnEnter()
{
SetNewTask(rage_new CTaskVehicleStop());
return FSM_Continue;
}
aiTask::FSM_Return CTaskVehicleGoToNavmesh::Stop_OnUpdate()
{
//only quit if we've got 0 search failures (meaning a search success)
//or more than some number, right now 3
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_STOP) && (m_iNumSearchFailures == 0 || m_iNumSearchFailures >= 3))
{
return FSM_Quit;
}
if( GetTimeInState() > 1.0f && m_iNumSearchFailures > 0)
{
SetState(State_Start);
return FSM_Continue;
}
return FSM_Continue;
}
#if !__FINAL
const char * CTaskVehicleGoToNavmesh::GetStaticStateName( s32 iState )
{
Assert(iState>=State_Start&&iState<=State_Stop);
static const char* aStateNames[] =
{
"State_Start",
"State_WaitingForResults",
"State_Goto",
"State_Stop"
};
return aStateNames[iState];
}
void CTaskVehicleGoToPointWithAvoidanceAutomobile::Debug() const
{
#if DEBUG_DRAW
if ( GetVehicle()->InheritsFromBoat() )
{
Vector3 vTargetPosition = m_Params.GetTargetPosition();
const Vector3 vStartCoords = VEC3V_TO_VECTOR3(CVehicleFollowRouteHelper::GetVehicleBonnetPosition(GetVehicle(), IsDrivingFlagSet(DF_DriveInReverse)));
grcDebugDraw::Sphere(vStartCoords + ZAXIS, 0.5f, Color_green, false);
grcDebugDraw::Sphere(vTargetPosition + ZAXIS, Max(m_Params.m_fTargetArriveDist, 0.5f), Color_green, false);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vStartCoords + ZAXIS), VECTOR3_TO_VEC3V(vTargetPosition + ZAXIS), 1.0f, Color_OrangeRed);
if ( GetVehicle()->GetIntelligence()->GetBoatAvoidanceHelper() )
{
GetVehicle()->GetIntelligence()->GetBoatAvoidanceHelper()->Debug(*GetVehicle());
}
}
if ( GetVehicle()->InheritsFromPlane() )
{
Vec3V vPlanePos = GetVehicle()->GetTransform().GetPosition();
CPlaneIntelligence* pPlaneIntelligence = static_cast<CPlaneIntelligence*>(GetVehicle()->GetIntelligence());
CVehPIDController& pitchContr = pPlaneIntelligence->GetPitchController();
CVehPIDController& rollContr = pPlaneIntelligence->GetRollController();
CVehPIDController& yawContr = pPlaneIntelligence->GetYawController();
CVehPIDController& throttleContr = pPlaneIntelligence->GetThrottleController();
char controllerDebugText[128];
sprintf(controllerDebugText, "Pitch: %.3f, %.3f, %.3f -> %.3f",
pitchContr.GetPreviousInput(), pitchContr.GetIntegral(), pitchContr.GetPreviousDerivative(), pitchContr.GetPreviousOutput());
grcDebugDraw::Text(vPlanePos, Color_black, 0, 20, controllerDebugText);
sprintf(controllerDebugText, "Roll: %.3f, %.3f, %.3f -> %.3f",
rollContr.GetPreviousInput(), rollContr.GetIntegral(), rollContr.GetPreviousDerivative(), rollContr.GetPreviousOutput());
grcDebugDraw::Text(vPlanePos, Color_black, 0, 30, controllerDebugText);
sprintf(controllerDebugText, "Yaw: %.3f, %.3f, %.3f -> %.3f",
yawContr.GetPreviousInput(), yawContr.GetIntegral(), yawContr.GetPreviousDerivative(), yawContr.GetPreviousOutput());
grcDebugDraw::Text(vPlanePos, Color_black, 0, 40, controllerDebugText);
sprintf(controllerDebugText, "Throttle: %.3f, %.3f, %.3f -> %.3f",
throttleContr.GetPreviousInput(), throttleContr.GetIntegral(), throttleContr.GetPreviousDerivative(), throttleContr.GetPreviousOutput());
grcDebugDraw::Text(vPlanePos, Color_black, 0, 50, controllerDebugText);
sprintf(controllerDebugText, "Speed: %.3f", GetVehicle()->GetVelocity().Mag());
grcDebugDraw::Text(vPlanePos, Color_black, 0, 60, controllerDebugText);
}
// char stopDistanceText[128];
// const float fStopDistanceScalar = CDriverPersonality::GetStopDistanceMultiplierForOtherCars(GetVehicle()->GetDriver(), GetVehicle());
// const float fDriverAggressiveness = CDriverPersonality::FindDriverAggressiveness(GetVehicle()->GetDriver(), GetVehicle());
// //const u32 nCarRandomSeed = GetVehicle()->GetRandomSeed();
// //const u32 nDriverRandomSeed = GetVehicle()->GetDriver() ? GetVehicle()->GetDriver()->GetRandomSeed() : 0;
// sprintf(stopDistanceText, "Stop Distance Scale: %.2f", fStopDistanceScalar);
// grcDebugDraw::Text(GetVehicle()->GetVehiclePosition(), Color_white, 0, 0, stopDistanceText);
// sprintf(stopDistanceText, "Driver Aggressiveness: %.2f", fDriverAggressiveness);
// grcDebugDraw::Text(GetVehicle()->GetVehiclePosition(), Color_white, 0, 10, stopDistanceText);
#endif
}
// PURPOSE: Display debug information specific to this task
void CTaskVehicleGoToNavmesh::Debug() const
{
#if DEBUG_DRAW
#endif
}
#endif