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

1158 lines
35 KiB
C++

//
#include "fwmaths/geomutil.h"
// Game headers
#include "animation/AnimDefines.h"
#include "Event/EventMovement.h"
#include "Event/EventResponse.h"
#include "Peds/PedCapsule.h"
#include "Peds/PedGeometryAnalyser.h"
#include "Peds/PedIntelligence.h"
#include "Peds/Ped.h"
#include "streaming/SituationalClipSetStreamer.h"
#include "Task/Combat/TaskNewCombat.h"
#include "task/Combat/TaskThreatResponse.h"
#include "Task/Movement/TaskCollisionResponse.h"
#include "Task/Movement/TaskFollowWaypointRecording.h"
#include "Task/Movement/TaskNavMesh.h"
#include "Task/Movement/TaskMoveWander.h"
#include "task/Response/TaskReactAndFlee.h"
#include "task/Response/TaskReactToImminentExplosion.h"
#include "Task/Vehicle/TaskEnterVehicle.h"
#include "Task/Vehicle/TaskRideTrain.h"
#include "Task/Combat/TaskCombatMelee.h"
#include "vehicles/train.h"
#include "vehicles/vehicle.h"
#include "vehicles/vehicleDamage.h"
#include "modelinfo/PedModelInfo.h"
#include "weapons/projectiles/Projectile.h"
#include "Weapons/Weapon.h"
#include "event/EventMovement_parser.h"
AI_OPTIMISATIONS()
AI_EVENT_OPTIMISATIONS()
//////////////////////////////////////////////////////////////////////////
// CEventClimbLadderOnRoute
//////////////////////////////////////////////////////////////////////////
CEventClimbLadderOnRoute::CEventClimbLadderOnRoute(const Vector3& vLadderBottom, const Vector3& vLadderTop, const int iDirection, const float fLadderHeading, const float fMoveBlendRatio)
: m_vLadderBottom(vLadderBottom)
, m_vLadderTop(vLadderTop)
, m_iDirection(iDirection)
, m_fLadderHeading(fLadderHeading)
, m_fMoveBlendRatio(fMoveBlendRatio)
{
#if !__FINAL
m_EventType = EVENT_CLIMB_LADDER_ON_ROUTE;
#endif
Assertf(m_vLadderTop.z > m_vLadderBottom.z, "Ladder at (%.1f, %.1f, %.1f) is broken", vLadderTop.x, vLadderTop.y, vLadderTop.z);
Assertf(m_iDirection == -1 || m_iDirection == 1, "Ladder at (%.1f, %.1f, %.1f) is broken", vLadderTop.x, vLadderTop.y, vLadderTop.z);
Assertf(m_fLadderHeading >= -PI && m_fLadderHeading <= PI, "Ladder at (%.1f, %.1f, %.1f) is broken", vLadderTop.x, vLadderTop.y, vLadderTop.z);
}
bool CEventClimbLadderOnRoute::AffectsPedCore(CPed* pPed) const
{
if(pPed->GetUsingRagdoll() || pPed->IsInjured() || pPed->GetIsInVehicle())
{
return false;
}
return true;
}
bool CEventClimbLadderOnRoute::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
if(pPed->GetUsingRagdoll() || pPed->IsInjured() || pPed->GetIsInVehicle())
{
return false;
}
const Vector3& vTarget = m_iDirection == 1 ? m_vLadderBottom : m_vLadderTop;
CTaskUseLadderOnRoute* pUseLadderTask = rage_new CTaskUseLadderOnRoute(m_fMoveBlendRatio, vTarget, m_fLadderHeading, NULL);
response.SetEventResponse(CEventResponse::EventResponse, pUseLadderTask);
return true;
}
//////////////////////////////////////////////////////////////////////////
// CEventClimbNavMeshOnRoute
//////////////////////////////////////////////////////////////////////////
CEventClimbNavMeshOnRoute::CEventClimbNavMeshOnRoute(const Vector3& vClimbPosition, const float fClimbHeading, const Vector3& vClimbTarget, const float fMoveBlendRatio, const u32 iWptFlags, CEntity* pEntityWeAreOn, const s32 iWarpTimer, const Vector3& vWarpTarget, bool bForceJump)
: m_vClimbPosition(vClimbPosition)
, m_vClimbTarget(vClimbTarget)
, m_fClimbHeading(fClimbHeading)
, m_fMoveBlendRatio(fMoveBlendRatio)
, m_iWptFlags(iWptFlags)
, m_pEntityWeAreOn(pEntityWeAreOn)
, m_iWarpTimer(iWarpTimer)
, m_vWarpTarget(vWarpTarget)
, m_bForceJump(bForceJump)
{
#if !__FINAL
m_EventType = EVENT_CLIMB_NAVMESH_ON_ROUTE;
#endif
}
CEventClimbNavMeshOnRoute::~CEventClimbNavMeshOnRoute()
{
}
bool CEventClimbNavMeshOnRoute::AffectsPedCore(CPed* pPed) const
{
if(pPed->GetUsingRagdoll() || pPed->IsInjured() || pPed->GetIsInVehicle())
{
return false;
}
return true;
}
bool CEventClimbNavMeshOnRoute::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
if(pPed->GetUsingRagdoll() || pPed->IsInjured() || pPed->GetIsInVehicle())
{
return false;
}
static dev_bool bUseTaskDropDown = false;
if(m_iWptFlags & WAYPOINT_FLAG_DROPDOWN)
{
if (pPed->GetPedIntelligence()->GetNavCapabilities().IsFlagSet(CPedNavCapabilityInfo::FLAG_MAY_DROP))
{
CTaskUseDropDownOnRoute* pTaskUseDropDown = rage_new CTaskUseDropDownOnRoute(
m_fMoveBlendRatio,
m_vClimbPosition,
m_fClimbHeading,
m_vClimbTarget,
m_pEntityWeAreOn,
m_iWarpTimer,
m_vWarpTarget,
false,
bUseTaskDropDown
);
response.SetEventResponse(CEventResponse::EventResponse, pTaskUseDropDown);
}
}
else
{
if (pPed->GetPedIntelligence()->GetNavCapabilities().IsFlagSet(CPedNavCapabilityInfo::FLAG_MAY_CLIMB))
{
CTaskUseClimbOnRoute* pTaskUseClimb = rage_new CTaskUseClimbOnRoute(
m_fMoveBlendRatio,
m_vClimbPosition,
m_fClimbHeading,
m_vClimbTarget,
m_pEntityWeAreOn,
m_iWarpTimer,
m_vWarpTarget,
m_bForceJump
);
response.SetEventResponse(CEventResponse::EventResponse, pTaskUseClimb);
}
}
return response.HasEventResponse();
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialBeWalkedInto
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_MOVEMENT_EVENT_TUNABLES(CEventPotentialBeWalkedInto, 0x1e8f6dd7);
CEventPotentialBeWalkedInto::Tunables CEventPotentialBeWalkedInto::sm_Tunables;
CEventPotentialBeWalkedInto::CEventPotentialBeWalkedInto(CPed* pOtherPed, const Vector3& vTarget, const float fMoveBlendRatio)
: m_pOtherPed(pOtherPed)
, m_vTarget(vTarget)
, m_fMoveBlendRatio(fMoveBlendRatio)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_BE_WALKED_INTO;
#endif
m_fOtherPedSpeedSq = pOtherPed ? pOtherPed->GetVelocity().Mag2() : 0.0f;
}
CEventPotentialBeWalkedInto::~CEventPotentialBeWalkedInto()
{
}
CEvent* CEventPotentialBeWalkedInto::Clone() const
{
CEventPotentialBeWalkedInto* pEvent = rage_new CEventPotentialBeWalkedInto((m_pOtherPed), m_vTarget, m_fMoveBlendRatio);
pEvent->m_fOtherPedSpeedSq = m_fOtherPedSpeedSq;
return pEvent;
}
bool CEventPotentialBeWalkedInto::AffectsPedCore(CPed* pPed) const
{
if(pPed->IsDead() ||
pPed->PopTypeIsMission() ||
!m_pOtherPed ||
pPed->GetIsInVehicle() ||
pPed->GetMotionData()->GetIsRunning() ||
m_pOtherPed->GetMotionData()->GetIsStill())
{
return false;
}
if (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PermanentlyDisablePotentialToBeWalkedIntoResponse))
{
return false;
}
if (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisablePotentialToBeWalkedIntoResponse))
{
return false;
}
if (pPed->GetTaskData().GetIsFlagSet(CTaskFlags::DisablePotentialToBeWalkedIntoResponse))
{
return false;
}
//Ensure we have not recently evaded the other ped.
static const u32 s_uMinTimeSinceLastEvaded = 3000;
if((pPed->GetPedIntelligence()->GetLastEntityEvaded() == m_pOtherPed) &&
((pPed->GetPedIntelligence()->GetLastTimeEvaded() + s_uMinTimeSinceLastEvaded) > fwTimer::GetTimeInMilliseconds()))
{
return false;
}
//Don't react for peds that are melee attacking us.
CTaskMeleeActionResult *pMeleeResult = m_pOtherPed->GetPedIntelligence()->GetTaskMeleeActionResult();
if(pMeleeResult && (pMeleeResult->GetTargetEntity() == pPed) && pMeleeResult->IsOffensiveMove())
{
return false;
}
//Ensure the ped is not swimming.
if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsSwimming))
{
return false;
}
//Ensure the ped is not in the water.
if(pPed->m_Buoyancy.GetStatus() != NOT_IN_WATER)
{
return false;
}
// CNC: Disable evasive step response if other ped is a player doing a melee shoulder barge towards this ped.
if (CEventPedCollisionWithPlayer::CNC_ShouldPreventEvasiveStepDueToPlayerShoulderBarging(pPed, m_pOtherPed))
{
return false;
}
return true;
}
bool CEventPotentialBeWalkedInto::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
if(!m_pOtherPed)
{
return false;
}
if (pPed->GetTaskData().GetIsFlagSet(CTaskFlags::DisableEvasiveStep))
{
return false;
}
bool bIsOtherPedInFirstPersonMode = false;
#if FPS_MODE_SUPPORTED
bIsOtherPedInFirstPersonMode = m_pOtherPed->IsFirstPersonShooterModeEnabledForPlayer();
#endif
bool bIsOtherPedRunning = !bIsOtherPedInFirstPersonMode ?
(m_pOtherPed->GetMotionData()->GetIsRunning() || m_pOtherPed->GetMotionData()->GetIsSprinting()) :
m_pOtherPed->GetMotionData()->GetIsSprinting();
if(!bIsOtherPedRunning &&
!pPed->GetMotionData()->GetIsStill())
{
return false;
}
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_DisableEvasiveDives ) ||
pPed->GetIsInVehicle())
{
return false;
}
// If this ped is armed, and the other isn't, don't react
const CWeapon* pPedsWeapon = pPed->GetWeaponManager() ? pPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
const CWeapon* pOtherPedsWeapon = m_pOtherPed->GetWeaponManager() ? m_pOtherPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
if((pPedsWeapon && pPedsWeapon->GetWeaponInfo()->GetIsGun()) && (pOtherPedsWeapon && !pOtherPedsWeapon->GetWeaponInfo()->GetIsGun()))
{
return false;
}
// Work out if the peds can see each other
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
const Vector3 vPedForward = VEC3V_TO_VECTOR3(pPed->GetTransform().GetForward());
const Vector3 vOtherPedPosition = VEC3V_TO_VECTOR3(m_pOtherPed->GetTransform().GetPosition());
const Vector3 vDiff = vOtherPedPosition - vPedPosition;
Vector3 vDirection;
vDirection.Normalize(vDiff);
// don't dodge peds that are behind us
if ( vDirection.Dot(vPedForward) < cos(DtoR * sm_Tunables.m_AngleThresholdDegrees) )
{
if(!bIsOtherPedRunning ||
(fwRandom::GetRandomNumberInRange(0.0f, 1.0f) >= sm_Tunables.m_ChancesToReactToRunningPedsBehindUs))
{
return false;
}
}
pPed->GetPedIntelligence()->Dodged(*m_pOtherPed, m_fOtherPedSpeedSq);
fwFlags8 uConfigFlags = 0;
if(!bIsOtherPedRunning || (fwRandom::GetRandomNumberInRange(0.0f, 1.0f) < sm_Tunables.m_ChanceToUseCasualAvoidAgainstRunningPed))
{
uConfigFlags.SetFlag(CTaskComplexEvasiveStep::CF_Casual);
}
//Create the task.
CTask* pTask = rage_new CTaskComplexEvasiveStep(m_pOtherPed, uConfigFlags);
response.SetEventResponse(CEventResponse::EventResponse, pTask);
return true;
}
CEntity* CEventPotentialBeWalkedInto::GetSourceEntity() const
{
return m_pOtherPed;
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialGetRunOver
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_MOVEMENT_EVENT_TUNABLES(CEventPotentialGetRunOver, 0xba56f15a);
CEventPotentialGetRunOver::Tunables CEventPotentialGetRunOver::sm_Tunables;
CEventPotentialGetRunOver::CEventPotentialGetRunOver(CVehicle* pThreatVehicle)
: m_pThreatVehicle(pThreatVehicle)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_GET_RUN_OVER;
#endif
m_fThreatVehicleSpeedSq = pThreatVehicle ? pThreatVehicle->GetVelocity().Mag2() : 0.0f;
}
CEventPotentialGetRunOver::~CEventPotentialGetRunOver()
{
}
CEventEditableResponse* CEventPotentialGetRunOver::CloneEditable() const
{
CEventPotentialGetRunOver* pEvent = rage_new CEventPotentialGetRunOver(m_pThreatVehicle);
pEvent->m_fThreatVehicleSpeedSq = m_fThreatVehicleSpeedSq;
return pEvent;
}
bool CEventPotentialGetRunOver::AffectsPedCore(CPed* pPed) const
{
if(pPed->IsDead() ||
pPed->GetIsAttached() ||
pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) ||
!m_pThreatVehicle ||
m_pThreatVehicle->InheritsFromBoat())
{
return false;
}
//Ensure the ped is not swimming.
if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsSwimming))
{
return false;
}
//Ensure the ped is not in the water.
if(pPed->m_Buoyancy.GetStatus() != NOT_IN_WATER)
{
return false;
}
//Ensure the ped is not ragdolling.
if(pPed->GetUsingRagdoll())
{
return false;
}
return true;
}
bool CEventPotentialGetRunOver::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
// If the vehicle has been deleted then don't respond to the event
if(!m_pThreatVehicle)
{
return false;
}
// If the ped is in a car then don't respond to the event
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
return false;
}
//////////////////////////////////////////////////////////////////////////
// RSNWE-JW we are already jumping aside, no need to request dupes
if(pPed->GetPedIntelligence() &&
pPed->GetPedIntelligence()->GetQueriableInterface() &&
pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMPLEX_EVASIVE_STEP))
{
return false;
}
// RSNWE-JW moved code below
if(pPed->IsPlayer())
{
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
/* Don't do all this work unless it will actually be used... */
// Compute the corners and normals of the vehicle
Vector3 corners[4];
Vector3 normals[4];
float ds[4];
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
CEntityBoundAI bound(*m_pThreatVehicle, vPedPosition.z, pPed->GetCapsuleInfo()->GetHalfWidth());
bound.GetCorners(corners);
bound.GetPlanes(normals, ds);
// Compute points at the centre of the front and rear of the car
Vector3 vFront(corners[CEntityBoundAI::FRONT_LEFT] + corners[CEntityBoundAI::FRONT_RIGHT]);
vFront *= 0.5f;
Vector3 vRear(corners[CEntityBoundAI::REAR_LEFT] + corners[CEntityBoundAI::REAR_RIGHT]);
vRear *= 0.5f;
// Compute the vehicle point (front or rear) which is going to hit the ped
Vector3 vecThreatVehicleForward(VEC3V_TO_VECTOR3(m_pThreatVehicle->GetTransform().GetB()));
const float fForwardSpeed = DotProduct(vecThreatVehicleForward, m_pThreatVehicle->GetVelocity());
Vector3 vPoint = (fForwardSpeed > 0.0f) ? vFront : vRear;
// Compute the distance along the car's forward/right dirs to the ped
Vector3 vDiff(vPedPosition - vPoint);
const float fLongDiff = DotProduct(vDiff, vecThreatVehicleForward);
// Test if the car is near the player and the player is in no immediate danger
if((rage::Abs(fLongDiff) < 3.0f) && (rage::Abs(fLongDiff) > rage::Abs(fForwardSpeed)))
{
// The car is near the player and the player is in no immediate danger
// If the ped isn't looking at anything then get him to look at the vehicle and maybe shake his fist
Vector2 vCurrMbr;
pPed->GetMotionData()->GetCurrentMoveBlendRatio(vCurrMbr);
if((vCurrMbr.Mag() < 1.0f) && (!pPed->GetIkManager().IsLooking()))
{
pPed->GetIkManager().LookAt(0, m_pThreatVehicle, 1500, BONETAG_INVALID, NULL);
}
}
}
else
{
//Grab the car speed.
float fCarSpeedSq = m_pThreatVehicle->GetVelocity().Mag2();
//Grab the driver.
CPed* pDriver = m_pThreatVehicle->GetSeatManager()->GetDriver();
//Keep track of whether we are going to dive or not.
bool bDive = false;
//Check if the car is going fast enough to dive.
//If the car is barely moving, diving looks pretty stupid.
float fMinSpeedToDiveSq = square(sm_Tunables.m_MinSpeedToDive);
float fMaxSpeedToDiveSq = square(sm_Tunables.m_MaxSpeedToDive);
if((fCarSpeedSq >= fMinSpeedToDiveSq) && (fCarSpeedSq <= fMaxSpeedToDiveSq))
{
//Check if the ped is capable of diving.
if(pPed->CheckAgilityFlags(AF_CAN_DIVE) || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanDiveAwayFromApproachingVehicles))
{
//There are a few conditions where we should always dive:
// 1) The car is moving quickly.
// 2) The driver is a player who is playing the horn.
if((fCarSpeedSq >= square(sm_Tunables.m_SpeedToAlwaysDive)) || (pDriver && pDriver->IsPlayer() && m_pThreatVehicle->IsHornOn()))
{
bDive = true;
}
//Check if we should randomly dive.
else if(fwRandom::GetRandomNumberInRange(0.0f, 1.0f) < sm_Tunables.m_ChancesToDive)
{
bDive = true;
}
}
//Always dive when in combat... even when agility flags are not set, apparently.
if(!bDive && pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMBAT))
{
bDive = true;
}
}
// @RSGNWE-JW if the Ped is fleeing, then always dive
if (pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_SMART_FLEE ) ||
pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMPLEX_LEAVE_CAR_AND_FLEE ) ||
pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_SCENARIO_FLEE ) )
{
bDive = true;
}
if(!pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_DisableEvasiveDives ) && !pPed->GetTaskData().GetIsFlagSet(CTaskFlags::DisableEvasiveStep))
{
//////////////////////////////////////////////////////////////////////////
// @RSNWE-JW
/* Don't do this work unless it will be used...
The ComputeMoveDirToAvoidEntity function is called, but is then
overwritten in the CTaskComplexEvasiveStep::CreateFirstSubTask
function. */
// Find the direction the ped has to move in order to avoid the car
// If the ped is nearest the rhs(lhs) then move further right(left)
pPed->GetPedIntelligence()->Dodged(*m_pThreatVehicle, m_fThreatVehicleSpeedSq);
fwFlags8 uConfigFlags = 0;
if(bDive)
{
uConfigFlags.SetFlag(CTaskComplexEvasiveStep::CF_Dive);
}
else if(fwRandom::GetRandomNumberInRange(0.0f, 1.0f) < sm_Tunables.m_ChancesToBeCasual)
{
uConfigFlags.SetFlag(CTaskComplexEvasiveStep::CF_Casual);
}
response.SetEventResponse(CEventResponse::EventResponse, rage_new CTaskComplexEvasiveStep(m_pThreatVehicle.Get(), uConfigFlags));
if(pDriver)
{
// If we already have a one star and are driving at a cop then we should bump the star to two
if(pPed->IsLawEnforcementPed() && pDriver->IsLocalPlayer())
{
CWanted* pPlayerWanted = pDriver->GetPlayerWanted();
if(pPlayerWanted && pPlayerWanted->GetWantedLevel() == WANTED_LEVEL1)
{
pPlayerWanted->SetWantedLevelNoDrop(VEC3V_TO_VECTOR3(pDriver->GetTransform().GetPosition()), WANTED_LEVEL2, WANTED_CHANGE_DELAY_INSTANT, WL_FROM_LAW_RESPONSE_EVENT);
}
}
}
}
}
return response.HasEventResponse();
}
CEntity* CEventPotentialGetRunOver::GetSourceEntity() const
{
CVehicle* pSourceVehicle = m_pThreatVehicle.Get();
if (pSourceVehicle)
{
CPed* pDriver = pSourceVehicle->GetSeatManager()->GetDriver();
if (pDriver)
{
return pDriver;
}
}
return pSourceVehicle;
}
bool CEventPotentialGetRunOver::GetTunableDelay(const CPed* UNUSED_PARAM(pPed), float& fMin, float& fMax) const
{
fMin = sm_Tunables.m_MinDelay;
fMax = sm_Tunables.m_MaxDelay;
return true;
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialBlast
//////////////////////////////////////////////////////////////////////////
CEventPotentialBlast::Tunables CEventPotentialBlast::sm_Tunables;
IMPLEMENT_MOVEMENT_EVENT_TUNABLES(CEventPotentialBlast, 0xcf658faa);
CEventPotentialBlast::CEventPotentialBlast(const CAITarget& rTarget, float fRadius, u32 uTimeOfExplosion)
: m_Target(rTarget)
, m_fRadius(fRadius)
, m_uTimeOfExplosion(uTimeOfExplosion)
, m_bIsSmoke(false)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_BLAST;
#endif
}
CEvent* CEventPotentialBlast::Clone() const
{
CEventPotentialBlast* pEvent = rage_new CEventPotentialBlast(m_Target, m_fRadius, m_uTimeOfExplosion);
pEvent->SetIsSmoke(GetIsSmoke());
return pEvent;
}
bool CEventPotentialBlast::AffectsPedCore(CPed* pPed) const
{
//Ensure the ped is not a player.
if(pPed->IsPlayer())
{
return false;
}
//Ensure the ped is not injured.
if(pPed->IsInjured())
{
return false;
}
//Ensure the ped is not a fireman.
if(pPed->GetPedType() == PEDTYPE_FIRE)
{
return false;
}
//Ensure reactions are not disabled.
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_DisablePotentialBlastReactions))
{
return false;
}
//Check if responses are disabled by task flags.
if(pPed->GetTaskData().GetIsFlagSet(CTaskFlags::DisablePotentialBlastResponse))
{
return false;
}
//Ensure the target is valid.
if(!m_Target.GetIsValid())
{
return false;
}
//Keep track of the projectile owner.
const CEntity* pProjectileOwner = NULL;
//Check if the target is valid.
if(m_Target.GetEntity())
{
//Ensure the target is not the ped.
if(pPed == m_Target.GetEntity())
{
return false;
}
//Ensure the target is not a projectile that the ped owns.
if(m_Target.GetEntity()->GetIsTypeObject())
{
//Check if the projectile is valid.
const CProjectile* pProjectile = static_cast<const CObject *>(m_Target.GetEntity())->GetAsProjectile();
if(pProjectile)
{
//Set the projectile owner.
pProjectileOwner = pProjectile->GetOwner();
if(pPed == pProjectileOwner)
{
return false;
}
}
}
}
//Check if the ped is in combat.
CTaskCombat* pTaskCombat = static_cast<CTaskCombat *>(
pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_COMBAT));
if(pTaskCombat)
{
pTaskCombat->OnPotentialBlast(*this);
return false;
}
//Ensure the ped is not already running threat response.
if(pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_THREAT_RESPONSE))
{
return false;
}
//Grab the target position.
Vec3V vTargetPosition;
m_Target.GetPosition(RC_VECTOR3(vTargetPosition));
//Ensure we are within the radius to react.
ScalarV scDistSq = DistSquared(pPed->GetTransform().GetPosition(), vTargetPosition);
static float s_fExtraDistanceToReact = 10.0f;
ScalarV scMaxDistSq = ScalarVFromF32(square(m_fRadius + s_fExtraDistanceToReact));
if(IsGreaterThanAll(scDistSq, scMaxDistSq))
{
return false;
}
//Check if the target position is not in our FOV.
const CPedPerception& rPerception = pPed->GetPedIntelligence()->GetPedPerception();
if(!rPerception.ComputeFOVVisibility(VEC3V_TO_VECTOR3(vTargetPosition)))
{
//Check if the projectile owner is not in our FOV.
if(!pProjectileOwner || !rPerception.ComputeFOVVisibility(VEC3V_TO_VECTOR3(pProjectileOwner->GetTransform().GetPosition())))
{
return false;
}
}
//Ensure the ped is not random (they use the shocking version).
if(pPed->PopTypeIsRandom())
{
return false;
}
//Ensure the explosion is imminent.
if(fwTimer::GetTimeInMilliseconds() + 1500 < m_uTimeOfExplosion)
{
return false;
}
//Ensure we can react.
if(!CTaskReactToImminentExplosion::IsValid(*pPed, m_Target, m_fRadius))
{
return false;
}
return true;
}
bool CEventPotentialBlast::CreateResponseTask(CPed* UNUSED_PARAM(pPed), CEventResponse& response) const
{
//Create the task.
CTask* pTaskImminentReact = rage_new CTaskReactToImminentExplosion(m_Target, m_fRadius, m_uTimeOfExplosion);
//Set the response.
response.SetEventResponse(CEventResponse::EventResponse, pTaskImminentReact);
return response.HasEventResponse();
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialWalkIntoFire
//////////////////////////////////////////////////////////////////////////
CEventPotentialWalkIntoFire::CEventPotentialWalkIntoFire(const Vector3& vFirePos, const float fFireRadius, const float fMoveBlendRatio)
: m_vFirePos(vFirePos)
, m_fFireRadius(fFireRadius)
, m_fMoveBlendRatio(fMoveBlendRatio)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_WALK_INTO_FIRE;
#endif
}
bool CEventPotentialWalkIntoFire::AffectsPedCore(CPed* pPed) const
{
if(pPed->IsDead())
{
return false;
}
if(m_fMoveBlendRatio == MOVEBLENDRATIO_STILL)
{
return false;
}
CTask* pTask = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(pTask == NULL || !pTask->IsTaskMoving())
{
return false;
}
Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
if( (m_vFirePos - vPedPos).Mag2() < CNearbyFireScanner::ms_fPotentialWalkIntoFireRange*CNearbyFireScanner::ms_fPotentialWalkIntoFireRange)
{
if(IsClose(m_vFirePos.z, vPedPos.z, 2.0f))
{
spdSphere sphere;
sphere.Set(RCC_VEC3V(m_vFirePos), ScalarV( m_fFireRadius + pPed->GetCapsuleInfo()->GetHalfWidth() ) );
Vector3 vTarget = pTask->GetMoveInterface()->GetTarget();
Vector3 v1,v2;
vTarget.z = m_vFirePos.z;
vPedPos.z = m_vFirePos.z;
if(!fwGeom::IntersectSphereEdge(sphere, vPedPos, vTarget, v1, v2))
{
return false;
}
}
}
return true;
}
bool CEventPotentialWalkIntoFire::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
CTask* pTask = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(pTask == NULL || !pTask->IsTaskMoving())
{
return false;
}
switch(GetResponseTaskType())
{
case CTaskTypes::TASK_WALK_ROUND_FIRE:
{
CTaskMoveInterface* pTaskMoveInterface = pTask->GetMoveInterface();
response.SetEventResponse(
CEventResponse::EventResponse,
rage_new CTaskWalkRoundFire(
m_fMoveBlendRatio,
m_vFirePos,
m_fFireRadius + pPed->GetCapsuleInfo()->GetHalfWidth(),
pTaskMoveInterface->GetTarget()
)
);
return true;
}
default:
return CEventEditableResponse::CreateResponseTask(pPed, response);
}
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialWalkIntoObject
//////////////////////////////////////////////////////////////////////////
CEventPotentialWalkIntoObject::CEventPotentialWalkIntoObject(CObject* pObject, const float fMoveBlendRatio)
: m_pObject(pObject)
, m_fMoveBlendRatio(fMoveBlendRatio)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_WALK_INTO_OBJECT;
#endif
}
CEventPotentialWalkIntoObject::~CEventPotentialWalkIntoObject()
{
}
bool CEventPotentialWalkIntoObject::AffectsPedCore(CPed* pPed) const
{
if(pPed->IsPlayer() ||
pPed->IsDead() ||
pPed->GetIsInVehicle() ||
!m_pObject ||
pPed->GetIsAttached() ||
m_pObject->GetAttachParent() == pPed ||
m_fMoveBlendRatio == MOVEBLENDRATIO_STILL)
{
return false;
}
if(m_pObject == pPed->GetGroundPhysical())
return false;
// Ignore very thin objects
const Vector3& vMin = m_pObject->GetBaseModelInfo()->GetBoundingBoxMin();
const Vector3& vMax = m_pObject->GetBaseModelInfo()->GetBoundingBoxMax();
if((vMax.x - vMin.x < 0.01f) || (vMax.y - vMin.y < 0.01f) || (vMax.z - vMin.z < 0.01f))
{
return false;
}
// Waypoint following tasks may wish to prevent this event from occurring at all
CTaskFollowWaypointRecording * pWaypointTask = (CTaskFollowWaypointRecording*)pPed->GetPedIntelligence()->FindTaskPrimaryByType(CTaskTypes::TASK_FOLLOW_WAYPOINT_RECORDING);
if(pWaypointTask && !pWaypointTask->GetRespondsToCollisionEvents())
return false;
const CTask* pTask = pPed->GetPedIntelligence()->GetTaskActive();
if(pTask)
{
switch(pTask->GetTaskType())
{
case CTaskTypes::TASK_WALK_ROUND_ENTITY:
{
const CTaskWalkRoundEntity* pTaskWalkRound = static_cast<const CTaskWalkRoundEntity*>(pTask);
if(pTaskWalkRound->GetEntity() == static_cast<CEntity*>(m_pObject))
{
return false;
}
}
break;
default:
break;
}
}
return true;
}
bool CEventPotentialWalkIntoObject::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
// If the object has been deleted then don't respond to the event
if(!m_pObject)
{
return false;
}
// Don't walk around things attached to us
if(m_pObject->GetIsAttached() && m_pObject->GetAttachParent() == pPed)
{
return false;
}
// If the ped is in a car then don't respond to the event
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
return false;
}
if(m_fMoveBlendRatio < MBR_WALK_BOUNDARY)
{
return false;
}
CTask* pSimplestMoveTask = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(CPedIntelligence::ms_bPedsRespondToObjectCollisionEvents && pSimplestMoveTask && pSimplestMoveTask->IsTaskMoving())
{
CTaskMoveInterface* pTaskMove = pSimplestMoveTask->GetMoveInterface();
if(pTaskMove->HasTarget() && !pPed->GetPedResetFlag(CPED_RESET_FLAG_DontWalkRoundObjects))
{
response.SetEventResponse(CEventResponse::MovementResponse, rage_new CTaskWalkRoundEntity(m_fMoveBlendRatio, pTaskMove->GetTarget(), static_cast<CEntity*>(m_pObject)));
return true;
}
}
return false;
}
CEntity* CEventPotentialWalkIntoObject::GetSourceEntity() const
{
return m_pObject;
}
//////////////////////////////////////////////////////////////////////////
// CEventPotentialWalkIntoVehicle
//////////////////////////////////////////////////////////////////////////
CEventPotentialWalkIntoVehicle::CEventPotentialWalkIntoVehicle(CVehicle* pThreatVehicle, const float fMoveBlendRatio, const Vector3 & vTarget)
: m_pThreatVehicle(pThreatVehicle)
, m_fMoveBlendRatio(fMoveBlendRatio)
, m_vTarget(vTarget)
{
#if !__FINAL
m_EventType = EVENT_POTENTIAL_WALK_INTO_VEHICLE;
#endif
}
CEventPotentialWalkIntoVehicle::~CEventPotentialWalkIntoVehicle()
{
}
bool CEventPotentialWalkIntoVehicle::AffectsPedCore(CPed* pPed) const
{
CTask * pWander = pPed->GetPedIntelligence()->FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_WANDER);
if(!pWander)
return false;
// If the ped is in a car then don't respond to the event
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
return false;
}
if(pPed->IsDead() ||
pPed->GetIsInVehicle() ||
pPed->GetIsAttached() ||
m_fMoveBlendRatio == MOVEBLENDRATIO_STILL ||
!m_pThreatVehicle)
{
return false;
}
if( m_pThreatVehicle == pPed->GetGroundPhysical() )
return false;
/*
// Player only accepts event if he is getting into a car,
// or if they have a GoTo task applied to them
CTaskEnterVehicle* pTaskNewGetIn = static_cast<CTaskEnterVehicle*>(pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_ENTER_VEHICLE));
CTask* pTaskSimplestMove = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(pTaskSimplestMove && pPed->IsPlayer() && !pTaskNewGetIn && !pTaskSimplestMove->IsTaskMoving())
{
return false;
}
if (!pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_SteersAroundObjects))
return false;
// Waypoint following tasks may wish to prevent this event from occurring at all
CTaskFollowWaypointRecording * pWaypointTask = (CTaskFollowWaypointRecording*)pPed->GetPedIntelligence()->FindTaskPrimaryByType(CTaskTypes::TASK_FOLLOW_WAYPOINT_RECORDING);
if(pWaypointTask && !pWaypointTask->GetRespondsToCollisionEvents())
return false;
CTaskWalkRoundCarWhileWandering * pWalkRndVehicle = static_cast<CTaskWalkRoundCarWhileWandering*>(pPed->GetPedIntelligence()->FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_WANDER_AROUND_VEHICLE));
if(pWalkRndVehicle && pWalkRndVehicle->GetVehicle()==m_pThreatVehicle)
return false;
// If we're following a navmesh route and are avoiding objects (and are not crossing the road), then don't accept
CTaskMoveFollowNavMesh* pNavMeshTask = static_cast<CTaskMoveFollowNavMesh*>(pPed->GetPedIntelligence()->GetTaskManager()->FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, CTaskTypes::TASK_MOVE_FOLLOW_NAVMESH, PED_TASK_MOVEMENT_GENERAL));
if(pNavMeshTask && pNavMeshTask->GetAvoidObjects())
{
if(!pPed->GetPedIntelligence()->GetTaskManager()->FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, CTaskTypes::TASK_MOVE_CROSS_ROAD_AT_TRAFFIC_LIGHTS, PED_TASK_MOVEMENT_GENERAL))
{
return false;
}
}
const bool bUsingNavMeshButNotAvoidingVehicles = pNavMeshTask && !pNavMeshTask->GetAvoidObjects();
CTaskManager* pTaskManager = pPed->GetPedIntelligence()->GetTaskManager();
Assert(pTaskManager);
// Don't accept if we're already walking around this entity
CTaskWalkRoundEntity* pWlkRndEntity = static_cast<CTaskWalkRoundEntity*>(pTaskManager->FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, CTaskTypes::TASK_WALK_ROUND_ENTITY, PED_TASK_MOVEMENT_EVENT_RESPONSE));
if(pWlkRndEntity && pWlkRndEntity->GetEntity() == m_pThreatVehicle)
{
return false;
}
if(pTaskNewGetIn)
{
// If we're entering this car, then don't accept this event (except if the car is
// about to run us over?)
if(pTaskNewGetIn->GetVehicle() == m_pThreatVehicle && !bUsingNavMeshButNotAvoidingVehicles)
{
return false;
}
}
//Check if the threat vehicle is a train.
if(m_pThreatVehicle->InheritsFromTrain())
{
//Check if we are trying to ride a train.
if(pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_RIDE_TRAIN, true))
{
//Check if we are trying to ride this train.
const CTaskRideTrain* pRideTrainTask = static_cast<CTaskRideTrain *>(pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_RIDE_TRAIN));
if(pRideTrainTask && (m_pThreatVehicle == pRideTrainTask->GetTrain()))
{
return false;
}
}
}
*/
// Don't generate event if target-pos is on same side of car as we are
CTask* pTaskSimplestMove = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(((CTaskMove*)pTaskSimplestMove)->HasTarget())
{
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
CEntityBoundAI bound(*m_pThreatVehicle, vPedPosition.z, pPed->GetCapsuleInfo()->GetHalfWidth() );
Vector3 vClosestSurfacePoint;
bound.ComputeClosestSurfacePoint(vPedPosition, vClosestSurfacePoint);
const int iCurrentSide = bound.ComputeHitSideByPosition(vClosestSurfacePoint);
const int iTargetSide = bound.ComputeHitSideByPosition(m_vTarget);
if(iCurrentSide == iTargetSide)
{
return false;
}
}
/*
// If we're already entering into this car then don't accept the event
CVehicle* pCarEntering = pPed->GetPedIntelligence()->IsInACarOrEnteringOne();
if(pCarEntering && pCarEntering == m_pThreatVehicle && !bUsingNavMeshButNotAvoidingVehicles)
{
return false;
}
*/
return true;
}
bool CEventPotentialWalkIntoVehicle::CreateResponseTask(CPed* pPed, CEventResponse& response) const
{
// If the vehicle has been deleted then don't respond to the event
if(!m_pThreatVehicle)
{
return false;
}
// If the ped is in a car then don't respond to the event
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
return false;
}
CTask* pTaskActiveSimplest = pPed->GetPedIntelligence()->GetTaskActiveSimplest();
CTask* pSimplestMoveTask = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
if(pTaskActiveSimplest && pSimplestMoveTask && pSimplestMoveTask->IsTaskMoving())
{
// Don't bother trying to walk around this if we're standing still
if(m_fMoveBlendRatio == MOVEBLENDRATIO_STILL)
{
return false;
}
// If the ped is wandering, then do a special walk-round-car response which navigates past the car(s). This is because
// the wander path finding ignores cars on the pavement so peds have a better chance of passing them - otherwise the
// wandering ped would likely turn around 180, since he is not allowed to leave the pavement during wander.
CTask * pWander = pPed->GetPedIntelligence()->FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_WANDER);
if(pWander)
{
response.SetEventResponse(CEventResponse::MovementResponse, rage_new CTaskWalkRoundCarWhileWandering(m_fMoveBlendRatio, m_vTarget, m_pThreatVehicle));
}
else
{
return false;
}
/*
else if(!pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER))
{
CTaskWalkRoundEntity* pTaskWalkRound = rage_new CTaskWalkRoundEntity(m_fMoveBlendRatio, m_vTarget, m_pThreatVehicle);
// Its too expensive to use capsule tests all the time for walking round vehicles. So instead we can switch them
// on occasionally, which should help rescue peds who are trapped between vehicles for several seconds.
static u32 iLosCheckCounter = 0;
iLosCheckCounter++;
// Only do line tests every 16 collisions
if(iLosCheckCounter >= 16 && fwRandom::GetRandomTrueFalse())
{
pTaskWalkRound->SetLineTestAgainstBuildings(true);
pTaskWalkRound->SetLineTestAgainstVehicles(true);
iLosCheckCounter = 0;
}
response.SetEventResponse(CEventResponse::MovementResponse, pTaskWalkRound);
}
*/
}
return response.HasEventResponse();
}
CEntity* CEventPotentialWalkIntoVehicle::GetSourceEntity() const
{
return m_pThreatVehicle;
}