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

4981 lines
163 KiB
C++

// File header
#include "Peds/PedWeapons/PlayerPedTargeting.h"
//rage headers
#include "math/vecmath.h"
#include "fwmaths/geomutil.h"
#include "fwmaths/vectorutil.h"
// Game headers
#include "Camera/CamInterface.h"
#include "Camera/Gameplay/GameplayDirector.h"
#include "Camera/Gameplay/Aim/ThirdPersonAimCamera.h"
#include "camera/gameplay/aim/FirstPersonShooterCamera.h"
#include "Debug/DebugScene.h"
#include "Game/Localisation.h"
#include "Event/EventShocking.h"
#include "Event/EventWeapon.h"
#include "Event/ShockingEvents.h"
#include "Frontend/NewHud.h"
#include "Ik/solvers/TorsoSolver.h"
#include "Network/Network.h"
#include "PedGroup/PedGroup.h"
#include "Peds/Ped_Channel.h"
#include "Peds/PedCapsule.h"
#include "Peds/PedIntelligence.h"
#include "Peds/PedWeapons/PedTargetEvaluator.h"
#include "Peds/Ped.h"
#include "Physics/Physics.h"
#include "Task/Combat/Cover/TaskCover.h"
#include "Task/Combat/TaskThreatResponse.h"
#include "Task/Default/TaskPlayer.h"
#include "Task/Weapons/TaskProjectile.h"
#include "Task/Weapons/Gun/TaskAimGunOnFoot.h"
#include "Task/Weapons/Gun/TaskVehicleDriveBy.h"
#include "Task/Motion/TaskMotionBase.h"
#include "vehicleAi/VehicleIntelligence.h"
#include "vehicles/heli.h"
#include "Vehicles/VehicleGadgets.h"
#include "Vehicles/Metadata/VehicleLayoutInfo.h"
#include "Weapons/Weapon.h"
#include "Weapons/WeaponDebug.h"
// Parser headers
#include "Peds/PedWeapons/PedWeapons_parser.h"
AI_OPTIMISATIONS()
extern const float g_DefaultDeadPedLockOnDuration = 0.75f;
//////////////////////////////////////////////////////////////////////////
// CCurve
//////////////////////////////////////////////////////////////////////////
CCurve::CCurve()
: m_fInitialValue(0.0f)
, m_fInputMin(0.0f)
, m_fInputMax(0.0f)
, m_fResultMax(0.0f)
, m_fPow(0.0f)
, m_fOneOverInputMaxMinusInputMin(0.0f)
{
}
float CCurve::GetResult(float fInput) const
{
if(fInput <= m_fInputMax)
{
float fResult = 0.0f;
// If input min and max are equal, just effectively return m_fInitialValue
if(m_fInputMax != m_fInputMin)
{
fResult = rage::Powf((fInput - m_fInputMin) * m_fOneOverInputMaxMinusInputMin, m_fPow);
}
fResult = m_fInitialValue + fResult * Max((m_fResultMax - m_fInitialValue), 0.0f);
fResult = Clamp(fResult, 0.0f, 1.0f);
return fResult;
}
// No result for input
return -1.0f;
}
void CCurve::UpdateCurveOnChange()
{
UpdateOneOverInputMaxMinusInputMinInternal();
CPlayerPedTargeting::FinaliseCurveSets();
}
void CCurve::UpdateOneOverInputMaxMinusInputMinInternal()
{
m_fOneOverInputMaxMinusInputMin = 1.0f / (m_fInputMax - m_fInputMin);
}
//////////////////////////////////////////////////////////////////////////
// CCurveSet
//////////////////////////////////////////////////////////////////////////
CCurveSet::CCurveSet()
#if __BANK
: m_colour(Color_white)
#endif // __BANK
{
}
void CCurveSet::SetCurve(s32 iCurveIndex, float fInputMax, float fResultMax, float fPow)
{
pedFatalAssertf(iCurveIndex >= 0 && iCurveIndex < m_curves.GetCount(), "iCurveIndex is out of array bounds");
m_curves[iCurveIndex].Set(fInputMax, fResultMax, fPow);
}
float CCurveSet::GetResult(float fInput) const
{
for(s32 i = 0; i < m_curves.GetCount(); i++)
{
float fCurveResult = m_curves[i].GetResult(fInput);
if(fCurveResult >= 0.0f)
{
return fCurveResult;
}
}
return 0.0f;
}
void CCurveSet::Finalise()
{
for(s32 i = 0; i < m_curves.GetCount(); i++)
{
if(i > 0)
{
if(m_curves[i].GetInputMax() < m_curves[i-1].GetInputMax())
{
m_curves[i].SetInputMax(m_curves[i-1].GetInputMax());
}
m_curves[i].SetInputMin(m_curves[i-1].GetInputMax());
}
m_curves[i].UpdateOneOverInputMaxMinusInputMinInternal();
}
for(s32 i = 1; i < m_curves.GetCount(); i++)
{
m_curves[i].SetInitialValue(m_curves[i-1].GetResult(m_curves[i-1].GetInputMax()));
}
}
#if __BANK
void CCurveSet::PreAddWidgets(bkBank& bank)
{
bank.PushGroup(m_Name.GetCStr(), false);
}
void CCurveSet::PostAddWidgets(bkBank& bank)
{
bank.PopGroup();
}
void CCurveSet::Render(const Vector2& vOrigin, const Vector2& vEnd, float fInputScale) const
{
Vector2 vLastResult(vOrigin);
float fInput = 0.0f;
while(fInput <= fInputScale)
{
float fResult = GetResult(fInput);
Vector2 vNewPoint;
vNewPoint.x = vOrigin.x + (vEnd.x - vOrigin.x) * (fInput/fInputScale);
vNewPoint.y = vOrigin.y + (vEnd.y - vOrigin.y) * fResult;
grcDebugDraw::Line(vLastResult, vNewPoint, m_colour);
static bank_float INCREMENT = 0.01f;
fInput += (INCREMENT * fInputScale);
vLastResult = vNewPoint;
}
}
#endif // __BANK
void CTargettingDifficultyInfo::PostLoad()
{
#if __BANK
Color32 colours[CTargettingDifficultyInfo::CS_Max] = { Color_orange, Color_blue, Color_cyan, Color_magenta };
Assert(m_CurveSets.GetCount() > 0);
for(s32 i = 0; i < CTargettingDifficultyInfo::CS_Max; i++)
{
m_CurveSets[i].SetColour(colours[i]);
}
#endif // __BANK
}
//////////////////////////////////////////////////////////////////////////
// CPlayerPedTargeting
//////////////////////////////////////////////////////////////////////////
// Static member initialisation
CPlayerPedTargeting::Tunables CPlayerPedTargeting::ms_Tunables;
IMPLEMENT_PLAYER_TARGETTING_TUNABLES(CPlayerPedTargeting, 0xcb21697a);
const u8 CPlayerPedTargeting::MAX_FREE_AIM_ASYNC_RESULTS = 32;
CPlayerPedTargeting::ePlayerTargetLevel CPlayerPedTargeting::ms_ePlayerTargetLevel = CPlayerPedTargeting::PTL_TARGET_STRANGERS;
#if __BANK
bool CPlayerPedTargeting::ms_bEnableSnapShotBulletBend = false;
float CPlayerPedTargeting::ms_fSnapShotRadiusMin = 0.5f;
float CPlayerPedTargeting::ms_fSnapShotRadiusMax = 2.0f;
#endif
PARAM(enablecovervantagepointforlos, "[CPlayerPedTargeting] Use cover vantage point for LOS tests");
////////////////////////////////////////////////////////////////////////////////
const CTargettingDifficultyInfo& CPlayerPedTargeting::Tunables::GetTargettingInfo() const
{
bool bFirstPerson = FPS_MODE_SUPPORTED_ONLY(CGameWorld::FindLocalPlayer() ? CGameWorld::FindLocalPlayer()->IsFirstPersonShooterModeEnabledForPlayer(false) || CGameWorld::FindLocalPlayer()->IsInFirstPersonVehicleCamera():) false;
switch(CPedTargetEvaluator::GetTargetMode(CGameWorld::FindLocalPlayer()))
{
case(CPedTargetEvaluator::TARGETING_OPTION_GTA_TRADITIONAL):
return bFirstPerson ? m_FirstPersonEasyTargettingDifficultyInfo : m_EasyTargettingDifficultyInfo;
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_AIM):
return bFirstPerson ? m_FirstPersonNormalTargettingDifficultyInfo : m_NormalTargettingDifficultyInfo;
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_FREEAIM):
return bFirstPerson ? m_FirstPersonHardTargettingDifficultyInfo : m_HardTargettingDifficultyInfo;
case(CPedTargetEvaluator::TARGETING_OPTION_FREEAIM):
return bFirstPerson ? m_FirstPersonExpertTargettingDifficultyInfo : m_ExpertTargettingDifficultyInfo;
}
Assert(0);
return m_EasyTargettingDifficultyInfo;
}
////////////////////////////////////////////////////////////////////////////////
CPlayerPedTargeting::CPlayerPedTargeting()
: m_pPed(NULL)
, m_FreeAimProbeResults(MAX_FREE_AIM_ASYNC_RESULTS)
, m_FreeAimAssistProbeResults(MAX_FREE_AIM_ASYNC_RESULTS)
, m_fLostLockLOSTimer(0.0f)
, m_uLastLockLOSTestTime(0)
, m_pPotentialTarget(NULL)
, m_fPotentialTargetTimer(0.0f)
, m_uManualSwitchTargetTime(0)
{
Reset();
}
bool CPlayerPedTargeting::CanPromoteToHardLock(TargetingMode UNUSED_PARAM(eTargetingMode), const CWeapon* UNUSED_PARAM(pWeapon), const CWeaponInfo& UNUSED_PARAM(weaponInfo))
{
return false;
}
void CPlayerPedTargeting::SetOwner(CPed* pPed)
{
m_pPed = pPed;
Reset();
}
void CPlayerPedTargeting::Reset()
{
m_fTimeAiming = 0.0f;
m_pLockOnTarget =NULL;
m_pCachedLockOnTarget = NULL;
m_vLockOnTargetPos = Vector3::ZeroType;
m_vLastLockOnTargetPos = Vector3::ZeroType;
m_vClosestFreeAimTargetPos = Vector3::ZeroType;
#if RSG_PC
m_vClosestFreeAimTargetPosStereo = Vector3::ZeroType;
m_vFreeAimTargetDir = Vector3::ZeroType;
m_fClosestFreeAimTargetDist = 0.0f;
#endif
m_vFineAimOffset = Vector3::ZeroType;
m_fTimeFineAiming = 0.0f;
m_fTimeLockedOn = 0.0f;
m_bTargetSwitchedToLockOnTarget = false;
m_fFineAimHorizontalSpeedWeight = 0.0f;
m_bPlayerIsFineAiming = false;
m_bPlayerReleasedUpDownControlForFineAim = false;
m_bPlayerPressingUpDownControlForFineAim = false;
m_bHasAttemptedNonSpringFineAim = false;
m_bPlayerHasStartedToAim = false;
m_uTimeSinceSoftFiringAndNotAiming = 0;
m_bHasUpdatedLockOnPos = false;
m_bPlayerHasBrokenLockOn = false;
m_bWasWithinFineAimLimits = false;
m_bCheckForLeftTarget = false;
m_bCheckForRightTarget = false;
m_fTimeSoftLockedOn = 0.0f;
m_fTimeTryingToBreakSoftLock = 0.0f;
m_fAssistedAimDeadPedLockOnTimer = -1.0f;
m_bVehicleHomingEnabled = true;
m_bVehicleHomingForceDisabled = false;
m_pFreeAimTarget = NULL;
m_uStartTimeAimingAtFreeAimTarget = 0;
m_pFreeAimTargetRagdoll = NULL;
m_pFreeAimAssistTarget = NULL;
m_vFreeAimAssistTargetPos = Vector3::ZeroType;
m_vClosestFreeAimAssistTargetPos = Vector3::ZeroType;
m_bFreeAimTargetingVehicleForCamera = false;
m_fCurveSetBlend = 0.0f;
m_fLastCameraInput = 0.0f;
m_FreeAimProbeResults.Reset();
m_FreeAimAssistProbeResults.Reset();
m_fLockOnRangeOverride = -1.0f;
m_fLostLockLOSTimer = 0.0f;
m_uLastLockLOSTestTime = 0;
m_bNoReticuleAimAssist = false;
m_bBlockNoReticuleAimAssist = false;
m_uTimeSinceLastAiming = 0;
m_vLastValidStickInput.Zero();
m_LastTimeDesiredStickInputSet = 0;
m_eLastTargetingMode = None;
m_uLastWeaponHash = 0;
m_bNoTargetingMode = false;
m_bTargetingStartedInWeaponWheelHUD = false;
m_uTimeLockOnSwitchExtensionExpires = 0;
m_pLockOnSwitchExtensionTarget = NULL;
m_bCheckForLockOnSwitchExtension = false;
}
void CPlayerPedTargeting::Process()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
m_pCachedLockOnTarget = NULL;
// If we are assisted aiming, set the flag
if (!m_pPed->GetIsInVehicle())
{
// If no reticule and trying to blind fire, give an aim assist.
bool bRunAndGun = m_pPed->GetPlayerInfo()->IsRunAndGunning();
if(CanUseNoReticuleAimAssist())
{
m_bNoReticuleAimAssist = true;
m_bBlockNoReticuleAimAssist = true;
}
else if(!bRunAndGun)
{
m_bBlockNoReticuleAimAssist = false;
}
if(m_bNoReticuleAimAssist)
{
if(!m_pPed->GetPlayerInfo()->IsRunAndGunning() || m_fTimeAiming > ms_Tunables.GetMinNoReticuleAimTime())
{
m_bNoReticuleAimAssist = false;
}
if(m_fTimeAiming > ms_Tunables.GetMinNoReticuleAimTime())
{
m_bNoReticuleAimAssist = false;
}
}
// Or when the weapon wheel is active as this changes the camera
if(!CNewHud::IsShowingHUDMenu())
{
if (m_pPed->GetPlayerInfo()->IsAssistedAiming())
{
m_pPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
}
else if(!m_pPed->GetIsInCover())
{
if(m_bNoReticuleAimAssist)
{
m_pPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
m_pPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_NO_RETICULE_AIM_ASSIST_ON);
}
else if (bRunAndGun)
{
m_pPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_RUN_AND_GUN);
}
}
}
}
#if __BANK
if(CPedTargetEvaluator::DEBUG_LOCKON_TARGET_SCRORING_ENABLED)
{
#if DEBUG_DRAW
CTask::ms_debugDraw.ClearAll();
#endif
CPedTargetEvaluator::EnableDebugScoring(true);
FindLockOnTarget();
CPedTargetEvaluator::EnableDebugScoring(false);
}
if(CPedTargetEvaluator::DEBUG_MELEE_TARGET_SCRORING_ENABLED)
{
#if DEBUG_DRAW
CTask::ms_debugDraw.ClearAll();
#endif
CPedTargetEvaluator::EnableDebugScoring(true);
static dev_bool s_bDoMeleeAction = true;
FindMeleeTarget(m_pPed, GetWeaponInfo(), s_bDoMeleeAction, true);
CPedTargetEvaluator::EnableDebugScoring(false);
}
#endif
m_bCheckForLeftTarget = false;
m_bCheckForRightTarget = false;
// If we aren't allowed to update targeting, clear the lock on target
bool bClearLockOn = !ShouldUpdateTargeting();
// Otherwise update the appropriate aiming mode
if (!bClearLockOn)
{
pedFatalAssertf(GetWeaponInfo(), "Weapon Info is NULL");
const CWeapon* pEquippedWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
const CWeaponInfo& weaponInfo = *GetWeaponInfo();
//! If equipped weapon doesn't match weapon info, NULL it out as they don't match.
if(pEquippedWeapon && (weaponInfo.GetHash() != pEquippedWeapon->GetWeaponInfo()->GetHash()))
{
pEquippedWeapon = NULL;
}
TargetingMode eTargetingMode = GetTargetingMode(pEquippedWeapon, weaponInfo);
u32 uWeaponHash = pEquippedWeapon ? pEquippedWeapon->GetWeaponHash() : m_pPed->GetWeaponManager()->GetSelectedWeaponHash();
//! Cache current weapon hash. It'll reset correctly when we release targeting controls.
bool bCacheLastWeaponHash = m_eLastTargetingMode == None || m_bTargetingStartedInWeaponWheelHUD || m_pPed->GetMyVehicle();
if(bCacheLastWeaponHash)
{
if(uWeaponHash != m_uLastWeaponHash)
{
m_uLastWeaponHash = uWeaponHash;
}
}
if(CPlayerInfo::IsSoftAiming() || CPlayerInfo::IsHardAiming() || CPlayerInfo::IsDriverFiring() || (eTargetingMode != None) )
{
if( (uWeaponHash != m_uLastWeaponHash) || m_bNoTargetingMode)
{
if(weaponInfo.GetCanFreeAim())
{
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming(true);
if (!weaponInfo.GetIsOnFootHoming())
{
eTargetingMode = FreeAim;
}
}
else
{
eTargetingMode = None;
}
m_bNoTargetingMode = true;
}
}
else
{
m_bNoTargetingMode = false;
m_bTargetingStartedInWeaponWheelHUD = false;
}
// DMKH. Check if we can promote to hard lock mode.
if(CanPromoteToHardLock(eTargetingMode, pEquippedWeapon, weaponInfo))
{
eTargetingMode = HardLockOn;
}
// Update the time aiming variable
if (m_pLockOnTarget || m_pPed->GetPlayerInfo()->GetPlayerDataFreeAiming())
{
m_fTimeAiming += fwTimer::GetTimeStep();
//! If we have been aiming for min aim time (i.e. so we can move crosshair), flag last aim time.
if( (m_fTimeAiming > ms_Tunables.GetMinFineAimTime()))
{
m_uTimeSinceLastAiming = fwTimer::GetTimeInMilliseconds();
}
}
else
{
const camGameplayDirector& gameplayDirector = camInterface::GetGameplayDirector();
bool bShowReticule = gameplayDirector.ShouldDisplayReticule() || CNewHud::IsReticuleVisible();
if(bShowReticule)
{
m_uTimeSinceLastAiming = fwTimer::GetTimeInMilliseconds();
}
m_fTimeAiming = 0.0f;
}
// Update the free aim status
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming(eTargetingMode == FreeAim ? true : false);
switch (eTargetingMode)
{
case HardLockOn:
{
bClearLockOn = !UpdateHardLockOn(pEquippedWeapon, weaponInfo);
UpdateFreeAim(weaponInfo);
}
break;
case SoftLockOn:
{
if (m_fTimeSoftLockedOn == 0.0f)
{
m_bWasWithinFineAimLimits = false;
}
bClearLockOn = !UpdateSoftLockOn(pEquippedWeapon, weaponInfo);
if (bClearLockOn && m_pLockOnTarget)
{
// When initially clearing lockon, make it the freeaim target
// (Can't just update free aim as probes are asynchronous)
m_pFreeAimTarget = m_pLockOnTarget;
m_pFreeAimTargetRagdoll = m_pLockOnTarget;
}
//try to set a free aim target if aiming and not locking
UpdateFreeAim(weaponInfo);
}
break;
case FreeAim:
{
bClearLockOn = !UpdateFreeAim(weaponInfo);
}
break;
case None:
{
if(CNewHud::IsReticuleVisible())
{
bClearLockOn = !UpdateFreeAim(weaponInfo);
}
else
{
bClearLockOn = true;
// Clear Free aim targets when not aiming
ClearFreeAim();
}
}
break;
case Manual:
if (weaponInfo.GetIsOnFootHoming() && !weaponInfo.GetIsVehicleWeapon())
{
// Don't need to set bClearLockOn flag here as we handle clearing the lock on target within the ProcessOnFootHomingLockOn function.
ProcessOnFootHomingLockOn(m_pPed);
// B*2165686: Only update the free aim target if we're aiming (prevents us from triggering ped reaction events when we aren't aiming).
if (CPlayerInfo::IsAiming())
{
// B*2154594: Try to set free aim target if possible to ensure we get ped reaction events
UpdateFreeAim(weaponInfo);
}
else
{
// BlairT: Clear free aim target/pos so we don't get stuck with stale data.
ClearFreeAim();
m_vClosestFreeAimTargetPos = Vector3::ZeroType;
}
}
else
{
// BlairT: Clear free aim target/pos so we don't get stuck with stale data.
ClearFreeAim();
m_vClosestFreeAimTargetPos = Vector3::ZeroType;
}
break;
default: break;
}
//! Have we started to target when in weapon wheel?
if(m_eLastTargetingMode == None && (eTargetingMode != None) && CNewHud::IsShowingHUDMenu())
{
m_bTargetingStartedInWeaponWheelHUD = true;
}
//! Cache targeting mode
m_eLastTargetingMode = eTargetingMode;
CControl* pControl = const_cast<CControl*>(m_pPed->GetControlFromPlayer());
if( pControl )
{
// Get desired stick direction for melee lock on.
const Vector2 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
if( vStickInput.Mag2() > rage::square( 0.5f ) )
{
m_LastTimeDesiredStickInputSet = fwTimer::GetTimeInMilliseconds();
m_vLastValidStickInput = vStickInput;
}
}
}
else
{
m_fTimeAiming = 0.0f;
}
if (bClearLockOn)
{
ClearLockOnTarget();
}
if(m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_NO_RETICULE_AIM_ASSIST_ON) && !m_pLockOnTarget )
{
m_pPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
m_pPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_NO_RETICULE_AIM_ASSIST_ON);
};
// Send gun aimed at events to targeted peds
ProcessPedEvents();
m_fLockOnRangeOverride = -1.0f; // Clear the weapon range override - needs to be set every frame from script
#if __BANK
TUNE_GROUP_BOOL(PLAYER_TARGETING, bDebugBulletBending, false);
if (bDebugBulletBending)
{
const CPedWeaponManager* pWeaponManager = m_pPed->GetWeaponManager();
if (pWeaponManager)
{
const CWeapon* pWeapon = pWeaponManager->GetEquippedWeapon();
if (pWeapon && pWeapon->GetWeaponInfo() && pWeapon->GetWeaponInfo()->GetIsGunOrCanBeFiredLikeGun())
{
const CObject* pWeaponObject = pWeaponManager->GetEquippedWeaponObject();
if (pWeaponObject)
{
const bool bCachedDebugTargettingValue = CPedTargetEvaluator::ms_Tunables.m_DebugTargetting;
CPedTargetEvaluator::ms_Tunables.m_DebugTargetting = true;
GetIsFreeAimAssistTargetPosWithinConstraints(pWeapon, RCC_MATRIX34(pWeaponObject->GetMatrixRef()), CPlayerPedTargeting::IsCameraInAccurateMode(), FIRING_VEC_CALC_FROM_AIM_CAM);
CPedTargetEvaluator::ms_Tunables.m_DebugTargetting = bCachedDebugTargettingValue;
}
}
}
}
#endif // __BANK
}
void CPlayerPedTargeting::ProcessPreCamera()
{
if(m_pPed && m_pPed->IsLocalPlayer())
{
UpdateLockOnTargetPos();
}
}
const CEntity* CPlayerPedTargeting::GetTarget() const
{
if(m_pLockOnTarget)
{
// The lock-on target is definitive
return m_pLockOnTarget;
}
else if(m_pFreeAimTarget)
{
return m_pFreeAimTarget;
}
else if(m_pFreeAimAssistTarget)
{
return m_pFreeAimAssistTarget;
}
return NULL;
}
CEntity* CPlayerPedTargeting::GetTarget()
{
if(m_pLockOnTarget)
{
// The lock-on target is definitive
return m_pLockOnTarget;
}
else if(m_pFreeAimTarget)
{
return m_pFreeAimTarget;
}
else if(m_pFreeAimAssistTarget)
{
return m_pFreeAimAssistTarget;
}
return NULL;
}
void CPlayerPedTargeting::SetLockOnTarget(CEntity* pTarget, bool bTargetSwitched)
{
m_pLockOnTarget = pTarget;
m_bTargetSwitchedToLockOnTarget = bTargetSwitched;
if(m_pLockOnTarget)
{
m_pLockOnTarget->GetLockOnTargetAimAtPos(m_vLastLockOnTargetPos);
#if USE_TARGET_SEQUENCES
TUNE_GROUP_BOOL(PLAYER_TARGETING, ASSISTED_AIMING_USE_TARGET_SEQUENCES, true);
// Update the lock-on position
atHashWithStringNotFinal targetSequenceGroup = GetTargetSequenceGroup();
if ( ASSISTED_AIMING_USE_TARGET_SEQUENCES && m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON) && targetSequenceGroup.GetHash() && m_pLockOnTarget->GetIsTypePed())
{
// TODO - pick a sequence group based on the weapon type (probably from weapon metadata)
m_TargetSequenceHelper.InitSequence(targetSequenceGroup, m_pPed, static_cast<CPed*>(m_pLockOnTarget.Get()));
}
else if (m_pPed->GetIsDrivingVehicle() || !m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
#else // USE_TARGET_SEQUENCES
//if (m_pPed->GetIsDrivingVehicle() || !m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
#endif // USE_TARGET_SEQUENCES
{
m_vLockOnTargetPos = m_vLastLockOnTargetPos;
}
}
else
{
// No target, clear lock-on position
m_vLockOnTargetPos.Zero();
m_vLastLockOnTargetPos.Zero();
TARGET_SEQUENCE_ONLY(m_TargetSequenceHelper.Reset());
m_bHasUpdatedLockOnPos = false;
}
m_fTimeLockedOn = 0.0f;
// Clear the fine aim variables
ClearFineAim();
}
void CPlayerPedTargeting::ClearLockOnTarget()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pPed->IsControlledByLocalPlayer())
{
m_pLockOnTarget = NULL;
m_vLockOnTargetPos.Zero();
m_vLastLockOnTargetPos.Zero();
m_bHasUpdatedLockOnPos = false;
TARGET_SEQUENCE_ONLY(m_TargetSequenceHelper.Reset();)
m_fLostLockLOSTimer = 0.0f;
m_uLastLockLOSTestTime = 0;
m_bNoReticuleAimAssist = false;
m_fTimeLockedOn = 0.0f;
m_bTargetSwitchedToLockOnTarget = false;
}
}
bool CPlayerPedTargeting::GetFineAimingOffset(Vector3& vFineAimOffset) const
{
vFineAimOffset = camInterface::GetPlayerControlCamAimFrame().GetRight() * m_vFineAimOffset.x;
vFineAimOffset.z = m_vFineAimOffset.z;
return m_bPlayerIsFineAiming;
}
#if !__FINAL
PARAM(enableCNCFreeAimImprovements, "[CPlayerPedTargeting] Enable CNC free aim improvements.");
PARAM(forceEnableCNCFreeAimImprovements, "[CPlayerPedTargeting] Force enable CNC free aim improvements (even if not in the mode).");
#endif // !__FINAL
bool CPlayerPedTargeting::GetAreCNCFreeAimImprovementsEnabled(const CPed* CNC_MODE_ENABLED_ONLY(pPed), const bool CNC_MODE_ENABLED_ONLY(bControllerOnly))
{
#if CNC_MODE_ENABLED
if (pPed)
{
const CPlayerInfo* pPlayerInfo = pPed->GetPlayerInfo();
if (pPlayerInfo)
{
return pPlayerInfo->GetTargeting().GetAreCNCFreeAimImprovementsEnabled(bControllerOnly);
}
}
#endif // CNC_MODE_ENABLED
return false;
}
bool CPlayerPedTargeting::GetAreCNCFreeAimImprovementsEnabled(const bool CNC_MODE_ENABLED_ONLY(bControllerOnly)) const
{
#if CNC_MODE_ENABLED
#if !__FINAL
TUNE_GROUP_BOOL(PLAYER_TARGETING, bForceCnCFreeAimImprovements, false);
if (bForceCnCFreeAimImprovements || PARAM_forceEnableCNCFreeAimImprovements.Get())
{
return true;
}
#endif // !__FINAL
TUNE_GROUP_BOOL(PLAYER_TARGETING, bEnableCnCFreeAimImprovements, true);
if ((bEnableCnCFreeAimImprovements || PARAM_enableCNCFreeAimImprovements.Get()) && NetworkInterface::IsInCopsAndCrooks())
{
if (bControllerOnly)
{
#if RSG_PC
const CControl* pControl = m_pPed->GetControlFromPlayer();
if(pControl && pControl->UseRelativeLook())
{
return false;
}
#endif // RSG_PC
}
const u32 uTargetMode = CPedTargetEvaluator::GetTargetMode(m_pPed);
if (uTargetMode == CPedTargetEvaluator::TARGETING_OPTION_FREEAIM || uTargetMode == CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_FREEAIM)
{
return true;
}
}
#endif // CNC_MODE_ENABLED
return false;
}
bool CPlayerPedTargeting::GetIsFreeAimAssistTargetPosWithinConstraints(const CWeapon* pWeapon, const Matrix34& mWeapon, bool bZoomed, eFiringVecCalcType eFireVecType) const
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pFreeAimAssistTarget)
{
if(pWeapon)
{
Vector3 vStart, vEnd;
switch (eFireVecType)
{
// TODO: Fix up the parameters for unused types
case FIRING_VEC_CALC_FROM_POS:
pWeapon->CalcFireVecFromPos(m_pPed, mWeapon, vStart, vEnd, VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition()));
break;
case FIRING_VEC_CALC_FROM_ENT:
pWeapon->CalcFireVecFromEnt(m_pPed, mWeapon, vStart, vEnd, m_pFreeAimAssistTarget);
break;
case FIRING_VEC_CALC_FROM_AIM_CAM:
pWeapon->CalcFireVecFromAimCamera(m_pPed->GetMyVehicle() != NULL ? static_cast<CEntity*>(m_pPed->GetMyVehicle()) : m_pPed, mWeapon, vStart, vEnd);
break;
case FIRING_VEC_CALC_FROM_GUN_ORIENTATION:
pWeapon->CalcFireVecFromGunOrientation(m_pPed, mWeapon, vStart, vEnd);
break;
case FIRING_VEC_CALC_FROM_WEAPON_MATRIX:
{
// Hack to use bullet bending on jetpack lasers
pWeapon->CalcFireVecFromWeaponMatrix(mWeapon, vStart, vEnd);
}
break;
default:
taskAssertf(0,"Unhandled firing vector calc type");
}
return GetIsFreeAimAssistTargetPosWithinConstraints(pWeapon, vStart, vEnd, bZoomed);
}
}
return false;
}
bool CPlayerPedTargeting::GetIsFreeAimAssistTargetPosWithinConstraints(const CWeapon* pWeapon, const Vector3& vStart, Vector3& vEnd, bool bZoomed) const
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pFreeAimAssistTarget)
{
if(pWeapon)
{
float fNearRadius = 0.0f;
float fFarRadius = 0.0f;
GetBulletBendingData(m_pPed, pWeapon, bZoomed, fNearRadius, fFarRadius);
fwGeom::fwLine line(vStart, vEnd);
Vector3 vClosestPoint;
float fT = line.FindClosestPointOnLine(m_vFreeAimAssistTargetPos, vClosestPoint);
float fRadiusAtDistance = fNearRadius + fT * (fFarRadius - fNearRadius);
// If distance from closest point on aiming vector to free aim assist target position is within the radius
bool bWithinConstraints = m_vFreeAimAssistTargetPos.Dist2(vClosestPoint) < rage::square(fRadiusAtDistance);
#if __BANK
RenderAimAssistConstraints(bWithinConstraints, vStart, vEnd, vClosestPoint, fRadiusAtDistance);
#endif // __BANK
return bWithinConstraints;
}
}
return false;
}
bool CPlayerPedTargeting::GetIsFreeAimAssistTargetPosWithinReticuleSlowDownConstraints(const CWeapon* pWeapon, const Vector3& vStart, Vector3& vEnd, bool bZoomed) const
{
if (ms_Tunables.GetUseCapsuleTests())
{
return GetIsFreeAimAssistTargetPosWithinReticuleSlowDownConstraintsNew(pWeapon, vStart, vEnd, bZoomed);
}
if(m_pFreeAimAssistTarget)
{
if(pWeapon)
{
#if __BANK
float fNearRadius = 0.0f;
float fFarRadius = 0.0f;
GetBulletBendingData(m_pPed, pWeapon, bZoomed, fNearRadius, fFarRadius);
fwGeom::fwLine line(vStart, vEnd);
Vector3 vClosestPoint;
float fT = line.FindClosestPointOnLine(m_vFreeAimAssistTargetPos, vClosestPoint);
float fRadiusAtDistance = fNearRadius + fT * (fFarRadius - fNearRadius);
// If distance from closest point on aiming vector to free aim assist target position is within the radius
bool bWithinConstraints = m_vFreeAimAssistTargetPos.Dist2(vClosestPoint) < rage::square(fRadiusAtDistance);
RenderAimAssistConstraints(bWithinConstraints, vStart, vEnd, vClosestPoint, fRadiusAtDistance);
#endif // __BANK
Vector3 vOne = vStart;
Vector3 vTwo = vEnd;
const camFrame& aimFrame = camInterface::GetPlayerControlCamAimFrame();
Matrix34 mFreeAimAssistMtx = aimFrame.GetWorldMatrix();
mFreeAimAssistMtx.d = VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition());
mFreeAimAssistMtx.UnTransform(vOne);
mFreeAimAssistMtx.UnTransform(vTwo);
Vector3 vOneToTwo = vTwo - vOne;
float fUnused = 0.0f;
if (geomSegments::SegmentToDiskIntersection(vOne, vOneToTwo, ms_Tunables.GetReticuleSlowDownRadius(), &fUnused))
{
return true;
}
}
}
return false;
}
bool CPlayerPedTargeting::GetIsFreeAimAssistTargetPosWithinReticuleSlowDownConstraintsNew(const CWeapon* pWeapon, const Vector3& vStart, Vector3& vEnd, bool BANK_ONLY(bZoomed)) const
{
if(m_pFreeAimAssistTarget)
{
if(pWeapon)
{
#if __BANK
float fNearRadius = 0.0f;
float fFarRadius = 0.0f;
GetBulletBendingData(m_pPed, pWeapon, bZoomed, fNearRadius, fFarRadius);
fwGeom::fwLine line(vStart, vEnd);
Vector3 vClosestPoint;
float fT = line.FindClosestPointOnLine(m_vFreeAimAssistTargetPos, vClosestPoint);
float fRadiusAtDistance = fNearRadius + fT * (fFarRadius - fNearRadius);
// If distance from closest point on aiming vector to free aim assist target position is within the radius
bool bWithinConstraints = m_vFreeAimAssistTargetPos.Dist2(vClosestPoint) < rage::square(fRadiusAtDistance);
RenderAimAssistConstraints(bWithinConstraints, vStart, vEnd, vClosestPoint, fRadiusAtDistance);
#endif // __BANK
Vector3 vOne = vStart;
Vector3 vTwo = vEnd;
const camFrame& aimFrame = camInterface::GetPlayerControlCamAimFrame();
Matrix34 mFreeAimAssistMtx = aimFrame.GetWorldMatrix();
mFreeAimAssistMtx.d = VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition());
mFreeAimAssistMtx.UnTransform(vOne);
mFreeAimAssistMtx.UnTransform(vTwo);
// geomSegments::SegmentCapsuleIntersectDirected assumes the capsule is lying on the y axis (length) so we need to rotate the aim vector accordingly
static float ROTATE_X = HALF_PI;
vOne.RotateX(ROTATE_X);
vTwo.RotateX(ROTATE_X);
float fLength = ms_Tunables.GetReticuleSlowDownCapsuleLength();
float fRadius = ms_Tunables.GetReticuleSlowDownCapsuleRadius();
/*if(m_fCurveSetBlend > 0.0f)
{
static dev_float s_Scale = 1.2f;
fLength *= s_Scale;
fRadius *= s_Scale;
}*/
Vector3 vOneToTwo = vTwo - vOne;
Vec3V vPositionOnCapsule;
Vec3V vNormalOnCapsule;
ScalarV scFractionAlongSegment;
bool bIntersectionFound = geomSegments::SegmentCapsuleIntersectDirected(RCC_VEC3V(vOne), RCC_VEC3V(vOneToTwo), ScalarVFromF32(fLength), ScalarVFromF32(fRadius), vPositionOnCapsule, vNormalOnCapsule, scFractionAlongSegment).Getb();
if (bIntersectionFound)
{
return true;
}
}
}
return false;
}
#if __BANK
void CPlayerPedTargeting::RenderAimAssistConstraints(bool bWithinConstraints, const Vector3& vStart, const Vector3& vEnd, const Vector3& vClosestPoint, float fRadiusAtDistance) const
{
if(CPedTargetEvaluator::ms_Tunables.m_DebugTargetting)
{
Color32 debugColour = bWithinConstraints ? Color_green : Color_red;
static const u32 uDebugPartialHash = atPartialStringHash("AIM_ASSIST_CIRCLE_");
CWeaponDebug::ms_debugStore.AddLine(RCC_VEC3V(vStart), RCC_VEC3V(vEnd), debugColour, 1000, atStringHash("TargetLine", uDebugPartialHash));
Vec3V vShotDir(vEnd - vStart);
vShotDir = NormalizeFast(vShotDir);
Vec3V vRight, vUp;
vRight = CrossZAxis(vShotDir);
vRight = NormalizeFast(vRight);
vUp = Cross(vRight, vShotDir);
vUp = NormalizeFast(vUp);
const s32 NUM_POINTS = 12;
float fAngleIncrement = 2.0f * PI / NUM_POINTS;
float fRotAngle = 0;
ScalarV fRadiusAtDistanceV = ScalarVFromF32(fRadiusAtDistance);
for(s32 i = 1; i <= NUM_POINTS; i++)
{
Vec3V a(vClosestPoint);
a += fRadiusAtDistanceV * vRight * ScalarVFromF32(rage::Sinf(fRotAngle));
a += fRadiusAtDistanceV * vUp * ScalarVFromF32(rage::Cosf(fRotAngle));
fRotAngle = fAngleIncrement * i;
Vec3V b(vClosestPoint);
b += fRadiusAtDistanceV * vRight * ScalarVFromF32(rage::Sinf(fRotAngle));
b += fRadiusAtDistanceV * vUp * ScalarVFromF32(rage::Cosf(fRotAngle));
char buff[3];
sprintf(buff, "%d", i);
CWeaponDebug::ms_debugStore.AddLine(a, b, debugColour, 1000, atStringHash(buff, uDebugPartialHash));
}
}
}
#endif // __BANK
bool CPlayerPedTargeting::GetScaledLookAroundInputMagForCamera(const float fCurrentLookAroundInputMag, float& fScaledLookAroundInputMag)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if (!ms_Tunables.GetUseReticuleSlowDown())
{
return false;
}
if (m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_RUN_AND_GUN) && !ms_Tunables.GetUseReticuleSlowDownForRunAndGun())
{
return false;
}
bool bInAccurateMode = IsCameraInAccurateMode();
bool bValidCam = false;
if(camInterface::GetGameplayDirector().GetThirdPersonAimCamera() FPS_MODE_SUPPORTED_ONLY(|| camInterface::GetGameplayDirector().GetFirstPersonShooterCamera()) )
{
bValidCam = true;
}
if(bValidCam)
{
weaponAssert(m_pPed->GetWeaponManager());
const CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
if(pWeapon && pWeapon->GetWeaponInfo()->GetIsGunOrCanBeFiredLikeGun())
{
CObject* pObject = m_pPed->GetWeaponManager()->GetEquippedWeaponObject();
if (pObject)
{
const Matrix34& mWeapon = RCC_MATRIX34(pObject->GetMatrixRef());
if (ms_Tunables.GetUseNewSlowDownCode())
{
Vector3 vStart(Vector3::ZeroType);
Vector3 vEnd(Vector3::ZeroType);
if (m_pFreeAimAssistTarget && pWeapon->CalcFireVecFromAimCamera(m_pPed->GetMyVehicle() != NULL ? static_cast<CEntity*>(m_pPed->GetMyVehicle()) : m_pPed, mWeapon, vStart, vEnd) && GetIsFreeAimAssistTargetPosWithinReticuleSlowDownConstraints(pWeapon, vStart, vEnd, bInAccurateMode))
{
m_fCurveSetBlend += fwTimer::GetTimeStep() * (1.0f/ms_Tunables.GetAimAssistBlendInTime());
}
else
{
m_fCurveSetBlend -= fwTimer::GetTimeStep() * (1.0f/ms_Tunables.GetAimAssistBlendOutTime());
}
}
else
{
if((m_pFreeAimAssistTarget && GetIsFreeAimAssistTargetPosWithinConstraints(pWeapon, mWeapon, bInAccurateMode, FIRING_VEC_CALC_FROM_AIM_CAM)) ||
(ms_Tunables.m_UseRagdollTargetIfNoAssistTarget && m_pFreeAimTarget))
{
m_fCurveSetBlend += fwTimer::GetTimeStep() * (1.0f/ms_Tunables.GetAimAssistBlendInTime());
}
else
{
m_fCurveSetBlend -= fwTimer::GetTimeStep() * (1.0f/ms_Tunables.GetAimAssistBlendOutTime());
}
}
m_fCurveSetBlend = Clamp(m_fCurveSetBlend, 0.0f, 1.0f);
//! If we are strafing, need to clamp our scaled input so that it doesn't get to low as the reticule can "bounce"
//! off our free aim assist target.
Vector2 vMoveBlendRatio;
m_pPed->GetMotionData()->GetCurrentMoveBlendRatio(vMoveBlendRatio);
float fAimAssistDistanceResult = 1.0f;
float fMinScaleInput = 0.0f;
float fBlend = m_fCurveSetBlend;
if(abs(vMoveBlendRatio.x) > 0.0f)
{
vMoveBlendRatio.NormalizeSafe();
float fStrafeScale = abs(vMoveBlendRatio.x);
if(m_pFreeAimAssistTarget)
{
Vector3 vLockOnPos = VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition());
Vector3 vPedPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
Vector3 vDistance = vLockOnPos - vPedPos;
float fDistanceToFreeAimAssistTarget = vDistance.XYMag();
fAimAssistDistanceResult = fStrafeScale * GetAimDistanceScaleCurve()->GetResult(fDistanceToFreeAimAssistTarget);
if(ms_Tunables.m_UseReticuleSlowDownStrafeClamp)
{
fMinScaleInput = fAimAssistDistanceResult;
}
else
{
fBlend *= fAimAssistDistanceResult;
}
}
}
if(bInAccurateMode)
{
float fAimZoomValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimZoom)->GetResult(fCurrentLookAroundInputMag);
float fAimAssistZoomValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimAssistZoom)->GetResult(fCurrentLookAroundInputMag);
fScaledLookAroundInputMag = Lerp(fBlend, fAimZoomValue, fAimAssistZoomValue);
}
else
{
float fAimValue = GetCurveSet(CTargettingDifficultyInfo::CS_Aim)->GetResult(fCurrentLookAroundInputMag);
float fAimAssistValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimAssist)->GetResult(fCurrentLookAroundInputMag);
fScaledLookAroundInputMag = Lerp(fBlend, fAimValue, fAimAssistValue);
}
if(ms_Tunables.m_UseReticuleSlowDownStrafeClamp && (fScaledLookAroundInputMag < fMinScaleInput))
{
fScaledLookAroundInputMag = Min(fMinScaleInput, fCurrentLookAroundInputMag);
}
//! Ensure we haven't increased look around input.
taskAssert(fScaledLookAroundInputMag <= fCurrentLookAroundInputMag);
m_fLastCameraInput = fCurrentLookAroundInputMag;
return true;
}
}
}
return false;
}
#if __BANK
// void CPlayerPedTargeting::AddWidgets(bkBank& bank))
// {
// bank.PushGroup("SNAPSHOT", false);
// bank.AddToggle("Enable bullet bend", &CPlayerPedTargeting::ms_bEnableSnapShotBulletBend);
// bank.AddSlider("Min radius bullet bend influence", & CPlayerPedTargeting::ms_fSnapShotRadiusMin, 0.0f, 20.0f, 0.1f);
// bank.AddSlider("Max radius bullet bend influence", & CPlayerPedTargeting::ms_fSnapShotRadiusMax, 0.0f, 20.0f, 0.1f);
// bank.PopGroup();
// }
void CPlayerPedTargeting::Tunables::SetEntityTargetableDistCB()
{
CEntity* pFocusEntity = CDebugScene::FocusEntities_Get(0);
if (pFocusEntity)
{
pFocusEntity->SetLockOnTargetableDistance(ms_Tunables.m_fTargetableDistance);
}
}
void CPlayerPedTargeting::Tunables::SetEntityTargetThreatCB()
{
CEntity* pFocusEntity = CDebugScene::FocusEntities_Get(0);
if (pFocusEntity && pFocusEntity->GetIsTypePed())
{
((CPed*)pFocusEntity)->SetTargetThreatOverride(ms_Tunables.m_fTargetThreatOverride);
}
}
void CPlayerPedTargeting::Debug()
{
if ( ms_Tunables.m_DisplayFreeAimTargetDebug )
{
grcDebugDraw::Sphere(m_vClosestFreeAimTargetPos, 0.1f, Color_blue);
Vector3 vTargeterPosition = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
grcDebugDraw::Line(vTargeterPosition, m_vClosestFreeAimTargetPos, Color_green);
}
if( ms_Tunables.m_DisplaySoftLockDebug && (ms_Tunables.GetLockType() == LT_Soft) )
{
grcDebugDraw::AddDebugOutput(Color_green, "Time Soft Locked : %.2f", m_fTimeSoftLockedOn);
if (m_bPlayerHasStartedToAim)
grcDebugDraw::AddDebugOutput(Color_green, "Started Soft Aiming : true");
else
grcDebugDraw::AddDebugOutput(Color_green, "Started Soft Aiming : false");
if (m_bPlayerHasBrokenLockOn)
grcDebugDraw::AddDebugOutput(Color_green, "Broken Lock On : true");
else
grcDebugDraw::AddDebugOutput(Color_green, "Broken Lock On : false");
const CControl* pControl = m_pPed->GetControlFromPlayer();
if (pControl)
{
Vector2 vecStickInput;
// * 128.0f to convert to old range. We could have just printed text but it is in a different range from other relevent text.
vecStickInput.x = pControl->GetPedAimWeaponLeftRight().GetNorm() * 128.0f;
vecStickInput.y = pControl->GetPedAimWeaponUpDown().GetNorm() * 128.0f;
grcDebugDraw::AddDebugOutput(Color_green, "X: %.2f, Y: %2f.", vecStickInput.x, vecStickInput.y);
}
Vector3 vFineAim;
GetFineAimingOffset(vFineAim);
grcDebugDraw::AddDebugOutput(Color_green, "Fine Aim Offset: (%.2f,%.2f,%.2f)", vFineAim.x, vFineAim.y, vFineAim.z);
}
if(ms_Tunables.m_DebugLockOnTargets)
{
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(pWeaponInfo && pWeaponInfo->GetIsGunOrCanBeFiredLikeGun())
{
Vector3 vTargeterPosition = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
const float fWeaponRange = GetWeaponLockOnRange(pWeaponInfo, m_pLockOnTarget);
const float fLockOnRange = fWeaponRange * ms_Tunables.GetLockOnRangeModifier();
const CPedScanner& pedScanner = *(m_pPed->GetPedIntelligence()->GetPedScanner());
const CEntityScannerIterator entityList = pedScanner.GetIterator();
for (const CEntity* pEnt = static_cast<const CPed*>(entityList.GetFirst()); pEnt; pEnt = entityList.GetNext())
{
const CPed* pNearbyPed = static_cast<const CPed*>(pEnt);
if (pNearbyPed)
{
Vector3 vTargetPosition = VEC3V_TO_VECTOR3(pNearbyPed->GetTransform().GetPosition());
pNearbyPed->GetLockOnTargetAimAtPos(vTargetPosition);
Vector3 vDelta = vTargetPosition - vTargeterPosition;
float fDistanceSq = vDelta.Mag2();
grcDebugDraw::Line(vTargetPosition, vTargeterPosition, fDistanceSq > rage::square(fLockOnRange) ? Color_red : Color_green);
vTargetPosition.z += 1.0f;
grcDebugDraw::Sphere(vTargetPosition, 0.05f, fDistanceSq > rage::square(fLockOnRange) ? Color_red : Color_green);
CPedTargetEvaluator::tHeadingLimitData headingData;
headingData.fHeadingAngularLimit = ms_Tunables.GetDefaultTargetAngularLimit();
headingData.fHeadingAngularLimitClose = ms_Tunables.GetDefaultTargetAngularLimitClose();
headingData.fHeadingAngularLimitCloseDistMin = ms_Tunables.GetDefaultTargetAngularLimitCloseDistMin();
headingData.fHeadingAngularLimitCloseDistMax = ms_Tunables.GetDefaultTargetAngularLimitCloseDistMax();
const float fMaxHeadingDifference = (DtoR * headingData.GetAngularLimitForDistance(rage::Sqrtf(fDistanceSq)));
Vector3 vHeading = camInterface::GetPlayerControlCamAimFrame().GetFront();
vDelta.Normalize();
const float fOrientation = rage::Atan2f(-vDelta.x, vDelta.y);
const float fOrientationDiff = fOrientation - rage::Atan2f( -vHeading.x, vHeading.y );
vTargetPosition.z += 0.1f;
grcDebugDraw::Sphere(vTargetPosition, 0.05f, Abs(fOrientationDiff) > fMaxHeadingDifference ? Color_red : Color_green);
Vector3 vLockonTargetPos = GetLockonTargetPos();
vLockonTargetPos.z += 1.5f;
grcDebugDraw::Sphere(vLockonTargetPos, 0.05f, Color_blue);
}
}
}
}
if(CPedTargetEvaluator::ms_Tunables.m_DebugTargetting)
{
// Call the lock-on search code in order to display the debug
//GetLockOnTarget();
// If GetLockOnTarget returns a CEntity then no need to clear it here
//ClearLockOnTarget();
if(!ms_Tunables.m_DisplayAimAssistIntersections)
{
if(m_pFreeAimTarget)
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_pFreeAimTarget->GetTransform().GetPosition()), 1.0f, Color_red, false, -1);
}
if(m_pFreeAimTargetRagdoll)
{
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_pFreeAimTargetRagdoll->GetTransform().GetPosition()), 1.0f, Color_blue, false, -1);
}
if(m_pFreeAimAssistTarget)
{
grcDebugDraw::Sphere(m_vFreeAimAssistTargetPos, 0.1f, Color_green, true, -1);
}
}
if (ms_Tunables.GetUseNewSlowDownCode() && (ms_Tunables.GetLockType() != LT_None) )
{
if (m_pFreeAimAssistTarget)
{
weaponAssert(m_pPed->GetWeaponManager());
const CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
if(pWeapon && pWeapon->GetWeaponInfo()->GetIsGunOrCanBeFiredLikeGun())
{
CObject* pObject = m_pPed->GetWeaponManager()->GetEquippedWeaponObject();
if (pObject)
{
const Matrix34& mWeapon = RCC_MATRIX34(pObject->GetMatrixRef());
Vector3 vStart(Vector3::ZeroType);
Vector3 vEnd(Vector3::ZeroType);
if (pWeapon->CalcFireVecFromAimCamera(m_pPed->GetMyVehicle() != NULL ? static_cast<CEntity*>(m_pPed->GetMyVehicle()) : m_pPed, mWeapon, vStart, vEnd))
{
const camFrame& aimFrame = camInterface::GetPlayerControlCamAimFrame();
Vector3 vUp = aimFrame.GetUp();
Vector3 vSide = aimFrame.GetRight();
Vec3V vTargetPos = m_pFreeAimAssistTarget->GetTransform().GetPosition();
Vec3V vLengthOffset = VECTOR3_TO_VEC3V(vUp * ms_Tunables.GetReticuleSlowDownCapsuleLength() * 0.5f);
Vec3V vCapsuleStart = vTargetPos - vLengthOffset;
Vec3V vCapsuleEnd = vTargetPos + vLengthOffset;
if (GetIsFreeAimAssistTargetPosWithinReticuleSlowDownConstraints(pWeapon, vStart, vEnd, false))
{
grcDebugDraw::Line(RCC_VEC3V(vStart), RCC_VEC3V(vEnd), Color_green, -1);
if (ms_Tunables.GetUseCapsuleTests())
grcDebugDraw::Capsule(vCapsuleStart, vCapsuleEnd, ms_Tunables.GetReticuleSlowDownCapsuleRadius(), Color_green, false, -1);
else
grcDebugDraw::Circle(m_pFreeAimAssistTarget->GetTransform().GetPosition(), ms_Tunables.GetReticuleSlowDownRadius(), Color_green, RCC_VEC3V(vSide), RCC_VEC3V(vUp), false, false, -1);
}
else
{
grcDebugDraw::Line(RCC_VEC3V(vStart), RCC_VEC3V(vEnd), Color_red, -1);
if (ms_Tunables.GetUseCapsuleTests())
grcDebugDraw::Capsule(vCapsuleStart, vCapsuleEnd, ms_Tunables.GetReticuleSlowDownCapsuleRadius(), Color_red, false, -1);
else
grcDebugDraw::Circle(m_pFreeAimAssistTarget->GetTransform().GetPosition(), ms_Tunables.GetReticuleSlowDownRadius(), Color_red, RCC_VEC3V(vSide), RCC_VEC3V(vUp), false, false, -1);
}
}
}
}
}
}
}
if(ms_Tunables.m_DisplayAimAssistCurves)
{
// Draw some axes
static Vector2 AXIS_ORIGIN(0.8f, 0.3f);
static Vector2 AXIS_END(0.95f, 0.0733f);
grcDebugDraw::Line(AXIS_ORIGIN, Vector2(AXIS_END.x, AXIS_ORIGIN.y), Color_red);
grcDebugDraw::Line(AXIS_ORIGIN, Vector2(AXIS_ORIGIN.x, AXIS_END.y), Color_green);
// Draw the camera input line
grcDebugDraw::Line(Vector2(AXIS_ORIGIN.x + (AXIS_END.x - AXIS_ORIGIN.x) * m_fLastCameraInput, AXIS_ORIGIN.y), Vector2(AXIS_ORIGIN.x + (AXIS_END.x - AXIS_ORIGIN.x) * m_fLastCameraInput, AXIS_END.y), Color_HotPink);
//! If we are strafing, need to clamp our scaled input so that it doesn't get to low as the reticule can "bounce"
//! off our free aim assist target.
Vector2 vMoveBlendRatio;
m_pPed->GetMotionData()->GetCurrentMoveBlendRatio(vMoveBlendRatio);
float fAimAssistDistanceResult = 1.0f;
float fMinScaleInput = 0.0f;
float fBlend = m_fCurveSetBlend;
float fDistanceToFreeAimAssistTarget = 0.0f;
if(abs(vMoveBlendRatio.x) > 0.0f)
{
vMoveBlendRatio.NormalizeSafe();
float fStrafeScale = abs(vMoveBlendRatio.x);
if(m_pFreeAimAssistTarget)
{
Vector3 vLockOnPos = VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition());
Vector3 vPedPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
Vector3 vDistance = vLockOnPos - vPedPos;
fDistanceToFreeAimAssistTarget = vDistance.XYMag();
fAimAssistDistanceResult = fStrafeScale * GetAimDistanceScaleCurve()->GetResult(fDistanceToFreeAimAssistTarget);
if(ms_Tunables.m_UseReticuleSlowDownStrafeClamp)
{
fMinScaleInput = fAimAssistDistanceResult;
}
else
{
fBlend *= fAimAssistDistanceResult;
}
}
}
s32 iStart = 0, iEnd = CTargettingDifficultyInfo::CS_Max;
bool bAccurateMode = IsCameraInAccurateMode();
float fCurrentResult = 0.0f;
if(bAccurateMode)
{
iStart = CTargettingDifficultyInfo::CS_AimZoom;
float fAimZoomValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimZoom)->GetResult(m_fLastCameraInput);
float fAimAssistZoomValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimAssistZoom)->GetResult(m_fLastCameraInput);
fCurrentResult = Lerp(fBlend, fAimZoomValue, fAimAssistZoomValue);
}
else
{
iEnd = CTargettingDifficultyInfo::CS_AimZoom;
float fAimValue = GetCurveSet(CTargettingDifficultyInfo::CS_Aim)->GetResult(m_fLastCameraInput);
float fAimAssistValue = GetCurveSet(CTargettingDifficultyInfo::CS_AimAssist)->GetResult(m_fLastCameraInput);
fCurrentResult = Lerp(fBlend, fAimValue, fAimAssistValue);
}
if(ms_Tunables.m_UseReticuleSlowDownStrafeClamp && fCurrentResult < fMinScaleInput)
{
fCurrentResult = Min(fMinScaleInput, m_fLastCameraInput);
}
// Render our current result on the graph
grcDebugDraw::Circle(Vector2(AXIS_ORIGIN.x + (AXIS_END.x - AXIS_ORIGIN.x) * m_fLastCameraInput, AXIS_ORIGIN.y + (AXIS_END.y - AXIS_ORIGIN.y) * fCurrentResult), 0.005f, Color_purple4);
for(s32 i = iStart; i < iEnd; i++)
{
GetCurveSet(i)->Render(AXIS_ORIGIN, AXIS_END);
}
static Vector2 AXIS_ORIGIN_DISTANCE(0.8f, 0.6f);
static Vector2 AXIS_END_DISTANCE(0.95f, 0.3733f);
static dev_float AXIS_SCALE_DISTANCE = 250.0f;
grcDebugDraw::Line(AXIS_ORIGIN_DISTANCE, Vector2(AXIS_END_DISTANCE.x, AXIS_ORIGIN_DISTANCE.y), Color_red);
grcDebugDraw::Line(AXIS_ORIGIN_DISTANCE, Vector2(AXIS_ORIGIN_DISTANCE.x, AXIS_END_DISTANCE.y), Color_green);
float fDistanceLine = Min(fDistanceToFreeAimAssistTarget/AXIS_SCALE_DISTANCE, 1.0f);
// Draw the distance input line
grcDebugDraw::Line(Vector2(AXIS_ORIGIN_DISTANCE.x + (AXIS_END_DISTANCE.x - AXIS_ORIGIN_DISTANCE.x) * fDistanceLine, AXIS_ORIGIN_DISTANCE.y),
Vector2(AXIS_ORIGIN_DISTANCE.x + (AXIS_END_DISTANCE.x - AXIS_ORIGIN_DISTANCE.x) * fDistanceLine, AXIS_END_DISTANCE.y),
Color_HotPink);
GetAimDistanceScaleCurve()->Render(AXIS_ORIGIN_DISTANCE, AXIS_END_DISTANCE, AXIS_SCALE_DISTANCE);
}
}
#endif // __BANK
bool CPlayerPedTargeting::ShouldUpdateTargeting() const
{
//! If we haven't locked on to anything, then don't process whilst doing certain tasks. This allows us to process lock on immediately
//! afterwards without using up our soft lock time.
bool bDisableTargetingForTask = false;
if(m_pLockOnTarget == NULL)
{
bDisableTargetingForTask = IsTaskDisablingTargeting();
}
// Do not update if we don't have a weapon
if (!GetWeaponInfo() || bDisableTargetingForTask)
{
return false;
}
return true;
}
bool CPlayerPedTargeting::IsTaskDisablingTargeting() const
{
if(m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsVaulting) ||
m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping) ||
m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsFalling) ||
m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsLanding) ||
m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsExitingVehicle))
{
return true;
}
return false;
}
bool CPlayerPedTargeting::CanIncrementSoftLockTimer() const
{
if(ms_Tunables.CanPauseSoftLockTimer())
{
return !m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsReloading) && !IsTaskDisablingTargeting() FPS_MODE_SUPPORTED_ONLY( && (!m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false) || !m_pPed->GetPedResetFlag(CPED_RESET_FLAG_DoingCombatRoll)) );
}
return true;
}
bool CPlayerPedTargeting::ShouldUpdateFineAim(const CWeaponInfo& weaponInfo)
{
// Can't fine aim when using melee weapons
if (weaponInfo.GetIsMelee())
{
return false;
}
return true;
}
bool CPlayerPedTargeting::UpdateHardLockOn(const CWeapon* pWeapon, const CWeaponInfo& weaponInfo)
{
bool bClearFineAim = true;
if (CannotLockOn(pWeapon, weaponInfo, HardLockOn) || GetShouldBreakLockOnWithTarget())
{
return false;
}
if (m_pLockOnTarget)
{
if (m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
{
bool bFindNewTarget = true;
bool bNULLTarget = false;
if (m_pLockOnTarget->GetIsTypePed())
{
if(static_cast<CPed*>(m_pLockOnTarget.Get())->GetIsDeadOrDying())
{
bFindNewTarget = false;
if (m_fAssistedAimDeadPedLockOnTimer < 0.0f)
{
TUNE_GROUP_FLOAT(ASSISTED_AIM_TUNE, DEAD_PED_LOCKON_DURATION, g_DefaultDeadPedLockOnDuration, 0.0f, 5.0f, 0.1f);
m_fAssistedAimDeadPedLockOnTimer = DEAD_PED_LOCKON_DURATION;
}
else
{
m_fAssistedAimDeadPedLockOnTimer -= fwTimer::GetTimeStep();
if (m_fAssistedAimDeadPedLockOnTimer <= 0.0f)
{
bFindNewTarget = true;
bNULLTarget = true;
}
}
}
else
{
//! If we down our ped, don't count as downed for targeting check, otherwise we switch targets, which we don't want (unless they
//! are ligitimately blocked etc).
CPedTargetEvaluator::SetPedToIgnoreForDownedCheck(static_cast<CPed*>(m_pLockOnTarget.Get()));
//! Allow searching for better targets after a min time.
TUNE_GROUP_FLOAT(ASSISTED_AIM_TUNE, ASSISTED_AIM_MIN_LOCKON, 1.0f, 0.0f, 5.0f, 0.1f);
if(m_fTimeLockedOn > ASSISTED_AIM_MIN_LOCKON)
{
bNULLTarget = true;
}
}
}
if (bFindNewTarget)
{
if(bNULLTarget)
{
m_pLockOnTarget = NULL;
}
FindAndSetLockOnTarget();
}
CPedTargetEvaluator::SetPedToIgnoreForDownedCheck(NULL);
}
if (m_pLockOnTarget)
{
//! Don't allow target switching if below fine aim time.
if(m_fTimeLockedOn < ms_Tunables.GetMinFineAimTime())
{
m_bCheckForLeftTarget = false;
m_bCheckForRightTarget = false;
}
UpdateLockOn(HardLockOn, m_pLockOnTarget);
if (ShouldUpdateFineAim(weaponInfo))
{
UpdateFineAim();
bClearFineAim = false;
}
}
}
else
{
m_fAssistedAimDeadPedLockOnTimer = -1.0f;
// No lockon target currently, but want to only check in a certain direction relative to camera
if (m_bCheckForLeftTarget || m_bCheckForRightTarget)
{
aiAssertf(!(m_bCheckForLeftTarget && m_bCheckForRightTarget), "Can't select a left and right target at the same time");
FindNextLockOnTarget(m_pLockOnTarget, m_bCheckForLeftTarget ? HD_LEFT : HD_RIGHT);
}
else
{
FindAndSetLockOnTarget();
}
}
if (bClearFineAim)
{
ClearFineAim();
}
// Don't clear lock on
return true;
}
bool CPlayerPedTargeting::UpdateSoftLockOn(const CWeapon* pWeapon, const CWeaponInfo& weaponInfo)
{
// NOTE: We don't allow an extended lock-on window in cover, as it can result in delayed lock-on when transitioning from idle to aiming/firing,
// which has a negative impact on the perceived sync of the player and camera movement.
float fSoftLockTimeToAcquireTarget = m_pPed->GetIsInCover() FPS_MODE_SUPPORTED_ONLY(&& !m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false) )? ms_Tunables.GetSoftLockTimeToAcquireTargetInCover() : ms_Tunables.GetSoftLockTimeToAcquireTarget();
bool bCanLockOn = m_fTimeSoftLockedOn <= fSoftLockTimeToAcquireTarget || m_bCheckForLockOnSwitchExtension;
bool bClearFineAim = true;
//! If we kill target ped, trigger target switch extension time.
if(m_pLockOnTarget &&
(m_pLockOnTarget != m_pLockOnSwitchExtensionTarget) &&
m_pLockOnTarget->GetIsTypePed() &&
ms_Tunables.GetLockOnSwitchTimeExtensionKillTarget() > 0)
{
CPed* pTargetPed = static_cast<CPed*>(m_pLockOnTarget.Get());
if(pTargetPed && pTargetPed->IsDead() && pTargetPed->GetSourceOfDeath()==m_pPed )
{
m_uTimeLockOnSwitchExtensionExpires = Max(m_uTimeLockOnSwitchExtensionExpires, fwTimer::GetTimeInMilliseconds() + ms_Tunables.GetLockOnSwitchTimeExtensionKillTarget());
m_pLockOnSwitchExtensionTarget = m_pLockOnTarget;
}
}
if (CannotLockOn(pWeapon, weaponInfo, SoftLockOn) || GetShouldBreakSoftLockTarget())
{
m_bPlayerHasBrokenLockOn = true;
return false;
}
if (m_pLockOnTarget)
{
// Allow switching targets?
CEntity *pPreUpdateLockOnTarget = m_pLockOnTarget;
//! Don't allow target switching until we have started fine aim.
if(!m_bPlayerIsFineAiming)
{
m_bCheckForLeftTarget = false;
m_bCheckForRightTarget = false;
}
UpdateLockOn(SoftLockOn, m_pLockOnTarget);
// Reset lock on time if we switch targets?
if(pPreUpdateLockOnTarget != m_pLockOnTarget)
{
ClearFineAim();
m_fTimeSoftLockedOn = 0.0f;
m_bPlayerHasBrokenLockOn = false;
m_bWasWithinFineAimLimits = false;
}
if (ms_Tunables.GetAllowSoftLockFineAim() && ShouldUpdateFineAim(weaponInfo))
{
UpdateFineAim(ms_Tunables.GetUseFineAimSpring());
bClearFineAim = false;
}
}
else if(m_pLockOnSwitchExtensionTarget && m_bCheckForLockOnSwitchExtension)
{
UpdateLockOn(SoftLockOn, m_pLockOnSwitchExtensionTarget);
if(m_pLockOnTarget)
{
ClearFineAim();
m_fTimeSoftLockedOn = 0.0f;
m_bPlayerHasBrokenLockOn = false;
m_bWasWithinFineAimLimits = false;
}
m_bCheckForLockOnSwitchExtension = false;
}
else
{
if(bCanLockOn)
{
FindAndSetLockOnTarget();
}
else if(!m_pLockOnTarget)
{
m_bPlayerHasBrokenLockOn = true;
return false;
}
}
if (bClearFineAim)
{
ClearFineAim();
}
// Don't clear lock on
return true;
}
void CPlayerPedTargeting::ProcessPedEvents()
{
if(m_pLockOnTarget)
{
ProcessPedEvents(m_pLockOnTarget);
}
if(m_pFreeAimTarget && (m_pFreeAimTarget != m_pLockOnTarget))
{
ProcessPedEvents(m_pFreeAimTarget);
}
if(m_pFreeAimAssistTarget && (m_pFreeAimAssistTarget != m_pFreeAimTarget) && (m_pFreeAimAssistTarget != m_pLockOnTarget))
{
ProcessPedEvents(m_pFreeAimAssistTarget);
}
}
void CPlayerPedTargeting::ProcessPedEvents(CEntity* pEntity)
{
if(!pEntity || !pEntity->GetIsTypePed())
{
return;
}
CPed* pTargetPed = static_cast<CPed *>(pEntity);
#if LAZY_RAGDOLL_BOUNDS_UPDATE
// Targeted Peds should have their bounds updated
if (pTargetPed->GetRagdollInst())
{
pTargetPed->RequestRagdollBoundsUpdate();
}
#endif
// If member ped is valid
if( m_pPed )
{
//Ensure the equipped weapon is valid.
Assert( m_pPed->GetWeaponManager() );
const CWeaponInfo* pTurretWeaponInfo = m_pPed->GetWeaponManager()->GetEquippedTurretWeaponInfo();
const CWeapon* pEquippedWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
if(!pEquippedWeapon && !pTurretWeaponInfo)
{
return;
}
//Ensure the weapon info is valid.
const CWeaponInfo* pWeaponInfo = pTurretWeaponInfo ? pTurretWeaponInfo : pEquippedWeapon->GetWeaponInfo();
if(!pWeaponInfo)
{
return;
}
// Don't fire any events if you are entering or exiting a vehicle.
const bool bEnterTaskRunning = m_pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_ENTER_VEHICLE, true);
const bool bExitTaskRunning = m_pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_EXIT_VEHICLE, true);
if(bEnterTaskRunning || bExitTaskRunning)
{
return;
}
// Don't fire events when peeking from cover
if( m_pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER, true) &&
m_pPed->GetPedIntelligence()->GetQueriableInterface()->GetStateForTaskType(CTaskTypes::TASK_IN_COVER) == CTaskInCover::State_Peeking )
{
return;
}
//Ensure the time aiming is valid.
// Turret weapon infos are apparently counted as unarmed, so many of the checks below don't make sense for them.
if (!pWeaponInfo->GetIsTurret())
{
if(pWeaponInfo->GetIsUnarmed() || pWeaponInfo->GetIsMelee())
{
static dev_float s_fMinTimeAiming = 0.6f;
if(m_fTimeAiming < s_fMinTimeAiming)
{
return;
}
}
else if(m_pPed->GetIsInVehicle())
{
static dev_float s_fMinTimeAiming = 0.5f;
if(m_fTimeAiming < s_fMinTimeAiming)
{
return;
}
}
}
static float s_fProjectileVisionRange = 2.0f;
static float s_fDangerousMeleeWeaponVisionRange = 2.0f;
// Compute distance to target squared
float fDistSquared = VEC3V_TO_VECTOR3(pTargetPed->GetTransform().GetPosition()).Dist2(VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition()));
// If within visual reaction range of the target
float fRange = CEventShockingWeaponThreat::GetVisualReactionRange();
//Use the weapons range + a little if it's less than default.
fRange = rage::Min(fRange, pWeaponInfo->GetRange() + 2.0f);
if (pWeaponInfo->GetIsProjectile() && pWeaponInfo->GetIsThrownWeapon())
{
// Projectiles have a much smaller visual reaction range than normal.
fRange = s_fProjectileVisionRange;
}
// Don't add in weapon threat events for unarmed.
if((!pWeaponInfo->GetIsUnarmed() || pWeaponInfo->GetIsTurret()) && !pWeaponInfo->GetIsNonViolent() && !pWeaponInfo->GetIsMelee() && !pTargetPed->PopTypeIsMission() && fDistSquared <= rage::square(fRange))
{
// Emit a shocking event for the weapon threat, in case anyone witnesses it
CEventShockingWeaponThreat shockEvent(*m_pPed, pTargetPed);
CShockingEventsManager::Add(shockEvent);
}
// Check for gun aimed at event at target ped
if (CEventGunAimedAt::IsAimingInTargetSeeingRange(*pTargetPed, *m_pPed, *pWeaponInfo))
{
// Melee events have CEventMeleeAction as their targeting event.
if ((pWeaponInfo->GetIsMelee() || pWeaponInfo->GetIsUnarmed()) && !pWeaponInfo->GetIsDangerousLookingMeleeWeapon() && !pWeaponInfo->GetIsTurret())
{
const bool bIsOffensive = true;
CEventMeleeAction eventMeleeAction(m_pPed, pTargetPed, bIsOffensive);
pTargetPed->GetPedIntelligence()->AddEvent(eventMeleeAction);
}
// Otherwise use gun aimed at.
else if (pWeaponInfo->GetIsNonViolent() && !pWeaponInfo->GetIsTurret())
{
CPed* pFiringPed = static_cast<CPed*>(m_pPed);
if (pFiringPed)
{
CEventShockingNonViolentWeaponAimedAt event(*pFiringPed);
pTargetPed->GetPedIntelligence()->AddEvent(event);
}
}
else if ((!pWeaponInfo->GetIsProjectile() || !pWeaponInfo->GetIsThrownWeapon() || fDistSquared <= square(s_fProjectileVisionRange))
&& (!pWeaponInfo->GetIsDangerousLookingMeleeWeapon() || fDistSquared <= square(s_fDangerousMeleeWeaponVisionRange)))
{
CPed* pFiringPed = static_cast<CPed*>(m_pPed);
CEventGunAimedAt event(pFiringPed);
pTargetPed->GetPedIntelligence()->AddEvent(event);
CEventFriendlyAimedAt eventFriendlyAimedAt(pFiringPed);
pTargetPed->GetPedIntelligence()->AddEvent(eventFriendlyAimedAt);
}
}
}
}
void CPlayerPedTargeting::ProcessPedEventsForFreeAimAssist()
{
if(m_FreeAimAssistProbeResults.GetResultsReady())
{
for(WorldProbe::ResultIterator it = m_FreeAimAssistProbeResults.begin(); it < m_FreeAimAssistProbeResults.last_result(); ++it)
{
CEntity* pEntity = it->GetHitEntity();
if(pEntity && pEntity->GetIsTypePed() && (pEntity != m_pLockOnTarget) && (pEntity != m_pFreeAimTarget) && (pEntity != m_pFreeAimAssistTarget))
{
ProcessPedEvents(pEntity);
}
else
{
// We are blocked by an object or map geometry -OR- We hit a non-entity.
break;
}
}
}
}
bool CPlayerPedTargeting::UpdateFreeAim(const CWeaponInfo& weaponInfo)
{
// Cache the weapon objects
weaponAssert(m_pPed->GetWeaponManager());
const CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
const CObject* pWeaponObject = m_pPed->GetWeaponManager()->GetEquippedWeaponObject();
if(pWeapon)
{
const Matrix34* mWeapon;
if (pWeaponObject)
mWeapon = &RCC_MATRIX34(pWeaponObject->GetMatrixRef());
else // Just use the peds matrix for taunts
mWeapon = &RCC_MATRIX34(m_pPed->GetMatrixRef());
Vector3 vStart, vEnd;
pWeapon->CalcFireVecFromAimCamera(m_pPed->GetMyVehicle() != NULL ? static_cast<CEntity*>(m_pPed->GetMyVehicle()) : m_pPed, *mWeapon, vStart, vEnd);
//! Override vEnd if it is a vehicle weapon.
if(m_pPed->GetWeaponManager()->GetEquippedVehicleWeapon() && m_pPed->GetWeaponManager()->GetEquippedVehicleWeapon()->GetWeaponInfo())
{
const camFrame& aimFrame = camInterface::GetPlayerControlCamAimFrame();
Vector3 vShot = aimFrame.GetFront();
vEnd = vStart + (m_pPed->GetWeaponManager()->GetEquippedVehicleWeapon()->GetWeaponInfo()->GetRange() * vShot);
}
// Search for the ped capsule under the reticule
Vector3 vHitPos;
FindFreeAimTarget(vStart, vEnd, weaponInfo, vHitPos);
if(m_pFreeAimTargetRagdoll == NULL)
{
float fTestLength = m_pPed->GetIsInVehicle() ? ms_Tunables.GetAimAssistCapsuleMaxLengthInVehicle() : ms_Tunables.GetAimAssistCapsuleMaxLength();
if (GetAreCNCFreeAimImprovementsEnabled() && GetCurrentTargetingMode() == TargetingMode::FreeAim)
{
// Don't exceed the weapon range.
const CWeaponInfo* pWeaponInfo = pWeapon->GetWeaponInfo();
const bool bAimingSniper = pWeaponInfo && pWeaponInfo->GetGroup() == WEAPONGROUP_SNIPER && camInterface::GetGameplayDirector().IsFirstPersonAiming();
float fMaxCapsuleLength = bAimingSniper ? ms_Tunables.GetAimAssistCapsuleMaxLengthFreeAimSniper() : ms_Tunables.GetAimAssistCapsuleMaxLengthFreeAim();
fTestLength = Min(fMaxCapsuleLength, weaponInfo.GetRange());
}
// Search for the aim assist target
// 5/10/13 - cthomas - Primarily for performance reasons, clamp the range of aim assist.
Vector3 vClampedAimAssistFiringVec = vEnd - vStart;
const float clampedAimAssistRange = Min(vClampedAimAssistFiringVec.Mag(), fTestLength);
vClampedAimAssistFiringVec.Normalize();
vClampedAimAssistFiringVec.Scale(clampedAimAssistRange);
Vector3 vEndPositionForFreeAimAssist = vStart + vClampedAimAssistFiringVec;
FindFreeAimAssistTarget(vStart, vEndPositionForFreeAimAssist);
}
else
{
// Clear free aim assist target in the case of getting a valid ragdoll target
m_pFreeAimAssistTarget = m_pFreeAimTargetRagdoll;
m_vFreeAimAssistTargetPos = vHitPos;
m_vClosestFreeAimAssistTargetPos = vHitPos;
}
}
// Always clear lock on
return false;
}
bool CPlayerPedTargeting::ProcessOnFootHomingLockOn(CPed* pPed)
{
if(pPed->IsLocalPlayer())
{
weaponAssert(pPed->GetWeaponManager());
CWeapon* pEquippedWeapon = pPed->GetWeaponManager() ? pPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
int iTargetingFlags = CPedTargetEvaluator::AIRCRAFT_WEAPON_TARGET_FLAGS|CPedTargetEvaluator::TS_HOMING_MISSLE_CHECK;
iTargetingFlags &= ~CPedTargetEvaluator::TS_CHECK_PEDS;
iTargetingFlags &= ~CPedTargetEvaluator::TS_IGNORE_VEHICLES_IN_LOS;
// Find range of current weapon
float fHeadingLimit = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetAngularLimitVehicleWeapon;
float fPitchLimitMin = 0.0f;
float fPitchLimitMax = 0.0f;
float fWeaponRange = CVehicleWaterCannon::FIRETRUCK_CANNON_RANGE;
float fRejectionWeaponRange = fWeaponRange;
CEntity* pCurrentTarget = GetLockOnTarget();
const CWeaponInfo* pWeaponInfo = pEquippedWeapon ? pEquippedWeapon->GetWeaponInfo() : NULL;
if( pWeaponInfo )
{
const CAimingInfo* pAimingInfo = pWeaponInfo->GetAimingInfo();
if( pAimingInfo )
{
fHeadingLimit = pAimingInfo->GetHeadingLimit();
fPitchLimitMin = pAimingInfo->GetSweepPitchMin();
fPitchLimitMax = pAimingInfo->GetSweepPitchMax();
}
fWeaponRange = pWeaponInfo->GetRange();
fRejectionWeaponRange = fWeaponRange;
}
CPedTargetEvaluator::tHeadingLimitData headingLimitData(fHeadingLimit);
static dev_float fExpandedHeadingLimitsTune = 30.0f;
if( pCurrentTarget )
{
bool bSelectingLeft = IsSelectingLeftOnFootHomingTarget(pPed);
bool bSelectingRight = IsSelectingRightOnFootHomingTarget(pPed);
if (bSelectingLeft || bSelectingRight)
{
iTargetingFlags |= bSelectingLeft ? CPedTargetEvaluator::TS_CYCLE_LEFT : iTargetingFlags |= CPedTargetEvaluator::TS_CYCLE_RIGHT;
iTargetingFlags &= ~CPedTargetEvaluator::TS_PED_HEADING; // Ignore ped heading checks when cycling targets.
iTargetingFlags &= ~CPedTargetEvaluator::TS_USE_DISTANCE_SCORING; // Ignore distance scoring to ensure we can cycle through all vehicles
headingLimitData.fHeadingFalloffPower = 0.0f;
// Use expanded heading limits when manually scanning for targets (target must be on screen).
headingLimitData.bCamHeadingNeedsTargetOnScreen = true;
headingLimitData.fHeadingAngularLimit = fExpandedHeadingLimitsTune;
headingLimitData.fWideHeadingAngulerLimit = fExpandedHeadingLimitsTune;
fHeadingLimit = fExpandedHeadingLimitsTune;
}
}
bool bManualTargetSwitch = false;
CEntity *pCurrentTargetPreSwitch = NULL;
if(iTargetingFlags & (CPedTargetEvaluator::TS_CYCLE_LEFT | CPedTargetEvaluator::TS_CYCLE_RIGHT ))
{
bManualTargetSwitch = true;
pCurrentTargetPreSwitch = pCurrentTarget;
}
CEntity* pFoundTarget = NULL;
if(CPlayerInfo::IsAiming(false) && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun) && !pPed->GetIsInVehicle() && !m_pPed->GetPedResetFlag(CPED_RESET_FLAG_InAirDefenceSphere))
{
pFoundTarget = CPedTargetEvaluator::FindTarget( pPed, iTargetingFlags, fWeaponRange, fRejectionWeaponRange, NULL, NULL, headingLimitData, fPitchLimitMin, fPitchLimitMax );
}
//! If we tried to find a left/right target and failed, keep current target.
if(pCurrentTargetPreSwitch && !pFoundTarget)
{
pFoundTarget = pCurrentTargetPreSwitch;
}
// Reset manual target timer if we've just tried to switch but landed on the same target.
if (bManualTargetSwitch && pFoundTarget && pCurrentTarget && pFoundTarget == pCurrentTarget)
{
m_uManualSwitchTargetTime = fwTimer::GetTimeInMilliseconds();
}
if(pFoundTarget)
{
// While we have a target, increment the potential target timer
if( m_pPotentialTarget )
{
m_fPotentialTargetTimer += fwTimer::GetTimeStep();
}
// If we had no previous target, just set it
if( !pCurrentTarget )
{
ResetTimeAimingAtTarget();
SetLockOnTarget( pFoundTarget );
}
// Does our potential target differ from our current?
else if( pCurrentTarget != pFoundTarget )
{
// Check to see the current target is valid and alive
Vector3 vHeading = VEC3V_TO_VECTOR3( pPed->GetTransform().GetB() );
float fHeadingDifference = 0.0f;
bool bOutsideMinHeading = false;
bool bInInclusionRangeFailedHeading = false;
int iIsValidFlags = iTargetingFlags;
iIsValidFlags |= CPedTargetEvaluator::TS_CYCLE_LEFT;
iIsValidFlags |= CPedTargetEvaluator::TS_CYCLE_RIGHT;
bool bTargetInDeadzone = false;
// Use expanded heading limits when cycling targets manually (ignore deadzone box checks).
static dev_u32 s_uManualSwitchCooldown = 2500;
float fExpandedHeadingLimit = headingLimitData.GetAngularLimit();
bool bManuallySwitchingTargets = bManualTargetSwitch || ((fwTimer::GetTimeInMilliseconds() - s_uManualSwitchCooldown) < m_uManualSwitchTargetTime);
bool bDoDeadzoneBoxCheck = !bManuallySwitchingTargets;
if (bManuallySwitchingTargets)
{
fExpandedHeadingLimit = fExpandedHeadingLimitsTune;
// Check target validity using non-expanded heading limits. Don't allow lock on when outside of main box (by continually resetting aim timer).
bool bCanLockOnToTarget = CPedTargetEvaluator::IsTargetValid( pPed->GetTransform().GetPosition(), pCurrentTarget->GetTransform().GetPosition(), iIsValidFlags, vHeading, fWeaponRange, fHeadingDifference, bOutsideMinHeading, bInInclusionRangeFailedHeading, bTargetInDeadzone, fHeadingLimit, fPitchLimitMin, fPitchLimitMax, true, false, false, bDoDeadzoneBoxCheck );
if (!bCanLockOnToTarget)
{
ResetTimeAimingAtTarget();
}
}
bool bIsValid = CPedTargetEvaluator::IsTargetValid( pPed->GetTransform().GetPosition(), pCurrentTarget->GetTransform().GetPosition(), iIsValidFlags, vHeading, fWeaponRange, fHeadingDifference, bOutsideMinHeading, bInInclusionRangeFailedHeading, bTargetInDeadzone, fExpandedHeadingLimit, fPitchLimitMin, fPitchLimitMax, true, false, false, bDoDeadzoneBoxCheck );
bool bIsAlive = pCurrentTarget->GetIsPhysical() && ((CPhysical*)pCurrentTarget)->GetHealth() > 0;
// Homing specific logic
if( bIsValid && bIsAlive && pWeaponInfo && pWeaponInfo->GetIsHoming())
{
// Selected this manually.
if( bManualTargetSwitch )
{
ResetTimeAimingAtTarget();
SetLockOnTarget( pFoundTarget );
//! Selected target manually. Allow a longer cooldown between auto switching targets.
m_uManualSwitchTargetTime = fwTimer::GetTimeInMilliseconds();
}
// Do we have a new priority target to be?
if( pFoundTarget != m_pPotentialTarget )
{
// Cache off the new potential target and reset the change target timer
m_pPotentialTarget = pFoundTarget;
m_fPotentialTargetTimer = 0.0f;
}
// prevent switching targets every frame
const CAmmoInfo* pAmmoInfo = pWeaponInfo->GetAmmoInfo();
if( pAmmoInfo && pAmmoInfo->GetClassId() == CAmmoRocketInfo::GetStaticClassId() )
{
const CAmmoRocketInfo* pRocketInfo = static_cast<const CAmmoRocketInfo*>(pAmmoInfo);
Assert( pRocketInfo );
// Calculate the speed ratio based on the default speed
float fMaxSpeed = rage::square( DEFAULT_MAX_SPEED );
float fSpeedRatio = rage::Min( pPed->GetVelocity().Mag2(), fMaxSpeed ) / fMaxSpeed;
// Determine the time to switch targets based on the vehicle speed
float fTimeToSwitchBetweenTargets = Lerp( fSpeedRatio, pRocketInfo->GetTimeBeforeSwitchTargetMax(), pRocketInfo->GetTimeBeforeSwitchTargetMin() );
// B*2171844: If our current target is outside of the box (ie in the deadzone) and we have a target within the actual box, swap immediately.
if( (fTimeToSwitchBetweenTargets < m_fPotentialTargetTimer && ((m_uManualSwitchTargetTime == 0) || ((fwTimer::GetTimeInMilliseconds() - s_uManualSwitchCooldown) > m_uManualSwitchTargetTime))) || bTargetInDeadzone)
{
ResetTimeAimingAtTarget();
SetLockOnTarget( pFoundTarget );
m_uManualSwitchTargetTime = 0;
return true;
}
}
}
// Otherwise just set the new target
else
{
ResetTimeAimingAtTarget();
SetLockOnTarget( pFoundTarget );
return true;
}
}
if(GetLockOnTarget() && GetLockOnTarget()->GetIsTypeVehicle())
{
static_cast<CVehicle*>(GetLockOnTarget())->SetLastTimeHomedAt(fwTimer::GetTimeInMilliseconds());
}
}
// Clear the target if we don't have one
else
{
m_pPotentialTarget = NULL;
m_fPotentialTargetTimer = 0.0f;
ResetTimeAimingAtTarget();
ClearLockOnTarget();
}
#if __DEV
// Debug draw the target for now
static dev_bool bDebugDrawLockon = false;
if(bDebugDrawLockon)
{
if(pCurrentTarget)
{
grcDebugDraw::BoxOriented(RCC_VEC3V(pCurrentTarget->GetBoundingBoxMin()),VECTOR3_TO_VEC3V(pCurrentTarget->GetBoundingBoxMax()), pCurrentTarget->GetMatrix(),Color_white);
}
}
#endif
}
return false;
}
bool CPlayerPedTargeting::IsSelectingLeftOnFootHomingTarget(const CPed *pPed)
{
const CControl* pControl = pPed->GetControlFromPlayer();
if (pControl && (pControl->GetVehicleFlySelectLeftTarget().IsPressed()))
{
return true;
}
return false;
}
bool CPlayerPedTargeting::IsSelectingRightOnFootHomingTarget(const CPed *pPed)
{
const CControl* pControl = pPed->GetControlFromPlayer();
if (pControl && (pControl->GetVehicleFlySelectRightTarget().IsPressed()))
{
return true;
}
return false;
}
CPlayerPedTargeting::OnFootHomingLockOnState CPlayerPedTargeting::GetOnFootHomingLockOnState(const CPed *pPed)
{
if (pPed)
{
const CWeaponInfo *pWeaponInfo = pPed->GetWeaponManager() ? pPed->GetWeaponManager()->GetEquippedWeaponInfo() : NULL;
// Time needed before the target is considered locked on.
// Used to determine UI reticule style and whether to enable homing or not.
float fTimeBeforeHoming = 0.0f;
const CAmmoInfo* pAmmoInfo = pWeaponInfo ? pWeaponInfo->GetAmmoInfo() : NULL;
if(pAmmoInfo && (pAmmoInfo->GetClassId() == CAmmoRocketInfo::GetStaticClassId()) )
{
fTimeBeforeHoming = static_cast<const CAmmoRocketInfo*>(pAmmoInfo)->GetTimeBeforeHoming();
}
if (m_pLockOnTarget)
{
float fTotalLockOnTime = GetTimeAimingAtTarget();
if (fTotalLockOnTime < (fTimeBeforeHoming/2))
{ // Display the green reticle.
return OFH_NOT_LOCKED;
}
else if (fTotalLockOnTime < fTimeBeforeHoming)
{ // Display the flashing orange reticle.
return OFH_ACQUIRING_LOCK_ON;
}
else
{ // Display the red reticle.
// Enable homing.
return OFH_LOCKED_ON;
}
}
}
return OFH_NO_TARGET;
}
void CPlayerPedTargeting::AddTargetableEntity(CEntity* pEntity)
{
const int iEntityIndex = m_TargetableEntities.Find((RegdEnt)pEntity);
if (iEntityIndex == -1)
{
// Verify we haven't got any null entities in the array if it's full (hopefully shouldn't happen, just being safe!)
if (m_TargetableEntities.IsFull())
{
for (int i = 0; i < m_TargetableEntities.GetCount(); ++i)
{
CEntity* pEntity = m_TargetableEntities[i];
if (!pEntity)
{
m_TargetableEntities.DeleteFast(i);
}
}
}
if (pedVerifyf(m_TargetableEntities.GetCount() < m_TargetableEntities.GetMaxCount(), "CPlayerPedTargeting::s_iMaxTargetableEntities is full (MAX: %d)!", s_iMaxTargetableEntities))
{
m_TargetableEntities.Append() = pEntity;
}
}
}
void CPlayerPedTargeting::RemoveTargetableEntity(CEntity* pEntity)
{
const int iIndex = m_TargetableEntities.Find((RegdEnt)pEntity);
if (iIndex != -1)
{
m_TargetableEntities.DeleteFast(iIndex);
}
}
void CPlayerPedTargeting::ClearTargetableEntities()
{
m_TargetableEntities.clear();
}
CEntity* CPlayerPedTargeting::GetTargetableEntity(int iIndex) const
{
return m_TargetableEntities[iIndex].Get();
}
bool CPlayerPedTargeting::GetIsEntityAlreadyRegisteredInTargetableArray(CEntity* pEntity) const
{
const int iIndex = m_TargetableEntities.Find((RegdEnt)pEntity);
if (iIndex != -1)
{
return true;
}
return false;
}
#if __BANK
void CPlayerPedTargeting::DebugAddTargetableEntity()
{
CEntity* pFocusEntity = CDebugScene::FocusEntities_Get(0);
if (pFocusEntity)
{
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
if (pPlayerPed && pPlayerPed != pFocusEntity)
{
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
if (pPlayerInfo)
{
CPlayerPedTargeting& rTargeting = pPlayerInfo->GetTargeting();
rTargeting.AddTargetableEntity(pFocusEntity);
}
}
}
}
void CPlayerPedTargeting::DebugClearTargetableEntities()
{
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
if (pPlayerPed)
{
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
if (pPlayerInfo)
{
CPlayerPedTargeting& rTargeting = pPlayerInfo->GetTargeting();
rTargeting.ClearTargetableEntities();
}
}
}
#endif // __BANK
void CPlayerPedTargeting::UpdateLockOn(const TargetingMode eTargetMode, CEntity *pCurrentLockOnEntity)
{
if (eTargetMode == HardLockOn || eTargetMode == SoftLockOn)
{
if (m_bCheckForLeftTarget)
{
FindNextLockOnTarget(pCurrentLockOnEntity, HD_LEFT);
}
else if (m_bCheckForRightTarget)
{
FindNextLockOnTarget(pCurrentLockOnEntity, HD_RIGHT);
}
#if __BANK
if(CPedTargetEvaluator::DEBUG_TARGET_SWITCHING_SCORING_ENABLED)
{
#if DEBUG_DRAW
CTask::ms_debugDraw.ClearAll();
#endif
CPedTargetEvaluator::EnableDebugScoring(true);
switch(CPedTargetEvaluator::DEBUG_TARGETSCORING_DIRECTION)
{
case(CPedTargetEvaluator::eDSD_Left):
FindNextLockOnTarget(pCurrentLockOnEntity, HD_LEFT, false);
break;
case(CPedTargetEvaluator::eDSD_Right):
FindNextLockOnTarget(pCurrentLockOnEntity, HD_RIGHT, false);
break;
default:
break;
}
CPedTargetEvaluator::EnableDebugScoring(false);
}
#endif
m_fTimeLockedOn += fwTimer::GetTimeStep_NonScaledClipped();
}
}
bool CPlayerPedTargeting::CanPedLockOnWithWeapon(const CWeapon* pWeapon, const CWeaponInfo& weaponInfo)
{
bool bCanLockOn = false;
if(m_pPed->GetIsDrivingVehicle())
{
if(weaponInfo.GetForcingDriveByAssistedAim())
{
bCanLockOn = true;
}
else if(ms_Tunables.GetUseDriveByAssistedAim())
{
CVehicle *pVehicle = m_pPed->GetVehiclePedInside();
if(pVehicle->GetVelocity().Mag2() >= (ms_Tunables.GetMinVelocityForDriveByAssistedAim() * ms_Tunables.GetMinVelocityForDriveByAssistedAim()))
{
bCanLockOn = true;
}
}
}
else if(m_pPed->GetIsInVehicle())
{
if(!m_pPed->IsInFirstPersonVehicleCamera())
{
bCanLockOn = pWeapon ? pWeapon->GetCanLockonInVehicle() : weaponInfo.GetCanLockOnInVehicle();
}
}
else
{
bCanLockOn = pWeapon ? pWeapon->GetCanLockonOnFoot() : weaponInfo.GetCanLockOnOnFoot();
}
//! Disable lock on inside planes as camera is too far out for target selection to work well.
if(m_pPed->GetVehiclePedInside() && m_pPed->GetVehiclePedInside()->InheritsFromPlane())
{
bCanLockOn = false;
}
return bCanLockOn;
}
bool CPlayerPedTargeting::CannotLockOn(const CWeapon* pWeapon, const CWeaponInfo& weaponInfo, const TargetingMode eTargetMode)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
bool bCanLockOn = CanPedLockOnWithWeapon(pWeapon, weaponInfo);
if (!bCanLockOn)
{
return true;
}
//! Allow lock on for passengers in MP now.
/*if (NetworkInterface::IsGameInProgress() && bIsInVehicle && !m_pPed->GetIsDrivingVehicle())
{
return true;
}*/
if (m_pPed->GetPedResetFlag(CPED_RESET_FLAG_ClearLockonTarget))
{
return true;
}
//! Remove lock if target becomes invisible.
if(m_pLockOnTarget && !m_pLockOnTarget->GetIsVisible())
{
return true;
}
// Do not allow lock on for peds in driver seat unless we're assisted aiming
if (!CTaskAimGunVehicleDriveBy::ms_UseAssistedAiming && !m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON) && m_pPed->GetMyVehicle() && !CNetwork::IsGameInProgress())
{
const bool bVehicleHasDriver = m_pPed->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
if (bVehicleHasDriver && m_pPed->GetMyVehicle()->GetSeatManager()->GetDriver() == m_pPed)
{
return true;
}
}
// Break lock on if target is dead and player is not really pushing the aim stick
TUNE_GROUP_FLOAT(PLAYER_TARGETING, DEAD_PED_LOCK_ON_BREAK_VALUE, 50.0f, 0.0f, 100.0f, 1.0f);
if (eTargetMode == SoftLockOn)
{
const float fTimeStep = fwTimer::GetTimeStep();
if(CanIncrementSoftLockTimer())
{
m_fTimeSoftLockedOn += fTimeStep;
}
if(!m_bCheckForLockOnSwitchExtension)
{
if ((m_fTimeSoftLockedOn > ms_Tunables.GetSoftLockTime()) TARGET_SEQUENCE_ONLY(&& !IsUsingTargetSequence()))
{
return true;
}
else
{
// If allowing fine aim during soft lock, only consider soft lock as broken if we're outside the fine aim limits
bool bWantsToBreak = false;
float fBreakTime = m_fFineAimHorizontalSpeedWeight >= 1.0f ? ms_Tunables.GetMinSoftLockBreakAtMaxXStickTime() : ms_Tunables.GetMinSoftLockBreakTime();
if(ms_Tunables.GetAllowSoftLockFineAim())
{
const bool bWithinFineAimLimits = IsFineAimWithinBreakLimits();
if (m_bWasWithinFineAimLimits)
{
bWantsToBreak = !bWithinFineAimLimits;
}
else
{
m_bWasWithinFineAimLimits = bWithinFineAimLimits;
}
if(m_pLockOnTarget)
{
Vector3 vLockOnPos = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition());
Vector3 vPedPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
Vector3 vDistance = vLockOnPos - vPedPos;
float fDist = Clamp(vDistance.Mag(), ms_Tunables.GetSoftLockBreakDistanceMin(), ms_Tunables.GetSoftLockBreakDistanceMax());
float fWeight = (fDist - ms_Tunables.GetSoftLockBreakDistanceMin()) / (ms_Tunables.GetSoftLockBreakDistanceMax() - ms_Tunables.GetSoftLockBreakDistanceMin());
fWeight = Clamp(fWeight, 0.0f, 1.0f);
float fAbsoluteLimits = ms_Tunables.GetSoftLockFineAimXYAbsoluteValueClose() + ((ms_Tunables.GetSoftLockFineAimXYAbsoluteValue() - ms_Tunables.GetSoftLockFineAimXYAbsoluteValueClose()) * fWeight);
//! Break immediately if we get outside max XY limits and we are pushing stick to break.
if(!IsFineAimWithinAbsoluteXYLimits(fAbsoluteLimits) && m_fFineAimHorizontalSpeedWeight > 0.25f)
{
fBreakTime = 0.0f;
}
else if(!IsFineAimWithinBreakXYLimits() && m_pLockOnTarget)
{
//! If outside XY limits, reduce break out time if close to target to stop camera from spinning. Note: We ignore Z, as this
//! is ok.
fBreakTime = ms_Tunables.GetMinSoftLockBreakTimeCloseRange() + ((ms_Tunables.GetMinSoftLockBreakTime() - ms_Tunables.GetMinSoftLockBreakTimeCloseRange()) * fWeight);
}
}
}
else
{
bWantsToBreak = IsPushingStick(ms_Tunables.GetSoftLockBreakValue());
}
if (bWantsToBreak)
{
m_fTimeTryingToBreakSoftLock += fTimeStep;
if(m_fTimeTryingToBreakSoftLock >= fBreakTime)
{
if(ms_Tunables.GetLockOnSwitchTimeExtensionBreakLock() > 0)
{
m_pLockOnSwitchExtensionTarget = m_pLockOnTarget;
m_uTimeLockOnSwitchExtensionExpires =
Max(m_uTimeLockOnSwitchExtensionExpires, fwTimer::GetTimeInMilliseconds() + ms_Tunables.GetLockOnSwitchTimeExtensionBreakLock());
}
return true;
};
}
else
{
m_fTimeTryingToBreakSoftLock = 0.0f;
}
}
}
}
// If we have a dead lockon target, disable lockon if not pushing the aim stick
// if (HasDeadLockonTarget())
// {
// return !IsPushingStick(DEAD_PED_LOCK_ON_BREAK_VALUE);
// }
return false;
}
#if USE_TARGET_SEQUENCES
bool CPlayerPedTargeting::IsTargetSequenceReadyToFire() const
{
if (m_pLockOnTarget)
{
return m_TargetSequenceHelper.IsReadyToFire();
}
else
{
return true;
}
}
bool CPlayerPedTargeting::IsUsingTargetSequence() const
{
return m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON) && m_pLockOnTarget && m_TargetSequenceHelper.HasSequence();
}
#endif // USE_TARGET_SEQUENCES
bool CPlayerPedTargeting::IsPrecisionAiming()const
{
Vector3 vUnused;
return GetFineAimingOffset(vUnused) TARGET_SEQUENCE_ONLY(|| IsUsingTargetSequence() || m_TargetSequenceHelper.IsSequenceFinished());
}
void CPlayerPedTargeting::FinaliseCurveSets()
{
for(s32 i = 0; i < CTargettingDifficultyInfo::CS_Max; i++)
{
ms_Tunables.m_FirstPersonEasyTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_EasyTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_FirstPersonNormalTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_NormalTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_FirstPersonHardTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_HardTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_FirstPersonExpertTargettingDifficultyInfo.m_CurveSets[i].Finalise();
ms_Tunables.m_ExpertTargettingDifficultyInfo.m_CurveSets[i].Finalise();
}
}
bool CPlayerPedTargeting::IsDisabledDueToWeaponWheel() const
{
bool bDisableDueToWeaponWheel = CNewHud::IsShowingHUDMenu() && !m_pLockOnTarget;
return bDisableDueToWeaponWheel;
}
bool CPlayerPedTargeting::IsLockOnDisabled() const
{
if (m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisablePlayerLockon) || IsDisabledDueToWeaponWheel() || (m_pPed->GetPedIntelligence() && m_pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_CLIMB_LADDER)))
{
return true;
}
return false;
}
CPlayerPedTargeting::TargetingMode CPlayerPedTargeting::GetTargetingMode(const CWeapon* pWeapon, const CWeaponInfo& weaponInfo)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
eLockType eTargetLockType = ms_Tunables.GetLockType();
if (!IsLockOnDisabled())
{
bool bSoftFiringNotAiming = fwTimer::GetTimeInMilliseconds() < (m_uTimeSinceSoftFiringAndNotAiming + 100);
bool bCanLockOn = CanPedLockOnWithWeapon(pWeapon, weaponInfo);
// B*6465133: Free aim assisted (ie snap lock) shouldn't allow lock-on if already aiming over a target ped (ie we have a free aim target ped).
if (CPedTargetEvaluator::GetTargetMode(m_pPed) == CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_FREEAIM)
{
if (!m_pLockOnTarget && m_pFreeAimTarget && m_pFreeAimTarget->GetIsTypePed())
{
bCanLockOn = false;
}
}
if( CPlayerInfo::IsFlyingAircraft() || weaponInfo.GetUseManualTargetingMode() )
{
return Manual;
}
else if( (CPlayerInfo::IsHardAiming() || CPlayerInfo::IsDriverFiring() || m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON)) && bCanLockOn )
{
if(weaponInfo.GetForcingDriveByAssistedAim()) //force hard lock on for cars that force assisted aim?
{
return HardLockOn;
}
else if( m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON) && !m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_NO_RETICULE_AIM_ASSIST_ON))
{
if (!m_pPed->GetIsDrivingVehicle() || eTargetLockType != LT_None)
{
return HardLockOn;
}
}
if( eTargetLockType == LT_Hard || (weaponInfo.GetIsMelee() && !IsMeleeTargetingUsingNormalLock()) )
{
bool bSelectingLeftTarget = false;
bool bSelectingRightTarget = false;
if(GetWeaponInfo())
{
bSelectingLeftTarget = CPlayerInfo::IsSelectingLeftTarget();
bSelectingRightTarget = CPlayerInfo::IsSelectingRightTarget();
}
//! Don't oscillate between free aim and hard lock during melee.
bool bForceMeleeHardLock = (weaponInfo.GetIsMelee() && !IsMeleeTargetingUsingNormalLock());
if ( bForceMeleeHardLock || !m_bPlayerHasStartedToAim || bSoftFiringNotAiming || m_bPlayerHasBrokenLockOn || m_pLockOnTarget || bSelectingLeftTarget || bSelectingRightTarget)
{
if (!m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
{
m_bCheckForLeftTarget = bSelectingLeftTarget;
m_bCheckForRightTarget = bSelectingRightTarget;
m_bPlayerHasStartedToAim = true;
m_bPlayerHasBrokenLockOn = false;
}
return HardLockOn;
}
}
else if ( eTargetLockType == LT_Soft )
{
if (CPlayerPedTargeting::ms_Tunables.GetUseLockOnTargetSwitching())
{
if(CPlayerInfo::IsSelectingLeftTarget())
{
m_bCheckForLeftTarget = true;
}
if(CPlayerInfo::IsSelectingRightTarget())
{
m_bCheckForRightTarget = true;
}
}
m_bCheckForLockOnSwitchExtension = false;
if (!m_bPlayerHasStartedToAim || bSoftFiringNotAiming)
{
m_bPlayerHasStartedToAim = true;
m_bPlayerHasBrokenLockOn = false;
return SoftLockOn;
}
else // Player has started to soft aim
{
// If player hasn't broken lockon and has been locked on less than the max lock on time then continue soft lock
if (!m_bPlayerHasBrokenLockOn && ((m_fTimeSoftLockedOn < ms_Tunables.GetSoftLockTime()) TARGET_SEQUENCE_ONLY(|| (IsUsingTargetSequence()))))
{
return SoftLockOn;
}
u32 nCurrentTime = fwTimer::GetTimeInMilliseconds();
if( (m_bCheckForLeftTarget || m_bCheckForRightTarget) &&
m_pLockOnSwitchExtensionTarget &&
(nCurrentTime < m_uTimeLockOnSwitchExtensionExpires) )
{
m_bCheckForLockOnSwitchExtension = true;
return SoftLockOn;
}
m_bPlayerHasBrokenLockOn = true;
}
// If player has broken a lock on or didn't have a target when beginning to aim
// fall through to free aim if possible
}
}
}
bool bPlayerIsSoftFiringNotAiming = CPlayerInfo::IsSoftFiring() && !CPlayerInfo::IsAiming(false) && !CPlayerInfo::IsDriverFiring();
if(bPlayerIsSoftFiringNotAiming)
{
m_uTimeSinceSoftFiringAndNotAiming = fwTimer::GetTimeInMilliseconds();
}
// Reset soft aim variables if not aiming so we can reacquire a lock on target
if( eTargetLockType == LT_Soft )
{
if ( (bPlayerIsSoftFiringNotAiming || !CPlayerInfo::IsAiming()) && !CPlayerInfo::IsDriverFiring())
{
ClearSoftAim();
}
}
if (weaponInfo.GetCanFreeAim() && (CPlayerInfo::IsAiming() || CPlayerInfo::IsDriverFiring()) && !IsLockOnDisabled())
{
// If we've been free aiming for a bit, do not allow player to then lock on
static bool ENABLE_FREE_AIM_CHECK = true;
if( eTargetLockType == LT_Soft && ENABLE_FREE_AIM_CHECK )
{
dev_float MAX_TIME_TO_ALLOW_INITIAL_SOFT_LOCK = 0.25f;
if (m_fTimeAiming > MAX_TIME_TO_ALLOW_INITIAL_SOFT_LOCK)
{
m_bPlayerHasStartedToAim = true;
m_bPlayerHasBrokenLockOn = true;
}
}
else if( eTargetLockType == LT_Hard && !CPlayerInfo::IsHardAiming() )
{
m_bPlayerHasBrokenLockOn = true;
}
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming(true);
return FreeAim;
}
m_bPlayerHasStartedToAim = false;
m_uTimeSinceSoftFiringAndNotAiming = 0;
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming(false);
return None;
}
void CPlayerPedTargeting::UpdateLockOnTarget()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(!pWeaponInfo)
{
// No weapon
return;
}
CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
bool bCanLockon = CanPedLockOnWithWeapon(pWeapon, *pWeaponInfo);
if(!bCanLockon)
{
// Selected weapon can not lock-on, clear target
ClearLockOnTarget();
}
bool bAssistedAiming = m_pPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
// Do not allow lock on for peds in driver seat
if (m_pPed->GetMyVehicle())
{
const bool bVehicleHasDriver = m_pPed->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
if (!bVehicleHasDriver || m_pPed->GetMyVehicle()->GetSeatManager()->GetDriver() == m_pPed)
{
ClearLockOnTarget();
return;
}
}
if((bCanLockon && (bAssistedAiming || !m_pPed->GetPlayerInfo()->GetPlayerDataFreeAiming() || CPlayerInfo::IsJustHardAiming())))
{
// If not locked-on, find a target
if(!m_pLockOnTarget)
{
if(bAssistedAiming || CPlayerInfo::IsJustHardAiming() || (CPlayerInfo::IsHardAiming() && !CTaskPlayerOnFoot::ms_bAnalogueLockonAimControl))
{
FindAndSetLockOnTarget();
if (bAssistedAiming && m_pLockOnTarget)
{
Vector3 vecAim = camInterface::GetPlayerControlCamAimFrame().GetFront();
vecAim.z = 0.0f;
vecAim.Normalize();
Vector3 vToTarget = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition() - m_pPed->GetTransform().GetPosition());
vToTarget.z = 0.0f;
vToTarget.Normalize();
const float fDot = vecAim.Dot(vToTarget);
TUNE_GROUP_FLOAT(ONE_H_AIM, dotTolerance2, 0.85f, 0.0f, 1.0f, 0.01f);
if (fDot <= dotTolerance2)
{
ClearLockOnTarget();
}
if(ShouldUsePullGunSpeech(pWeaponInfo))
m_pPed->NewSay("PULL_GUN");
}
}
else
{
ClearLockOnTarget();
}
}
// Already locked on, update lock on camera mode
else
{
if(GetShouldBreakLockOnWithTarget())
{
ClearLockOnTarget();
}
// If we already have a target we can find neighbouring targets if the player presses the L R buttons
else if(m_pLockOnTarget)
{
if(CPlayerInfo::IsSelectingLeftTarget())
{
FindNextLockOnTarget(m_pLockOnTarget, HD_LEFT);
}
else if(CPlayerInfo::IsSelectingRightTarget())
{
FindNextLockOnTarget(m_pLockOnTarget, HD_RIGHT);
}
if(!m_pPed->GetCoverPoint())
{
m_pPed->SetDesiredHeading(VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition()));
}
CControl* pControl = m_pPed->GetControlFromPlayer();
Vector2 vecStickInput;
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
vecStickInput.y = pControl->GetPedWalkUpDown().GetNorm();
static dev_float s_fStickThreshold = 0.0f;
// Prevent ped from continually moving forward when locked on and making small movements if no input is received
if (Abs(vecStickInput.x) > s_fStickThreshold || Abs(vecStickInput.y) > s_fStickThreshold)
{
m_pPed->SetIsStrafing(true);
}
else
{
m_pPed->SetIsStrafing(false);
}
}
}
}
else if(m_pPed->GetPlayerInfo()->GetPlayerDataFreeAiming() &&
bCanLockon &&
(m_pPed->GetPlayerInfo()->IsHardAiming() || m_pPed->GetPlayerInfo()->IsFiring() || m_pPed->GetPlayerInfo()->IsSelectingLeftTarget() || m_pPed->GetPlayerInfo()->IsSelectingRightTarget()))
{
if(m_pPed->GetPlayerInfo()->IsSelectingLeftTarget())
{
FindNextLockOnTarget(m_pLockOnTarget, HD_LEFT);
}
else if(m_pPed->GetPlayerInfo()->IsSelectingRightTarget())
{
FindNextLockOnTarget(m_pLockOnTarget, HD_RIGHT);
}
}
if(!bAssistedAiming && !m_pLockOnTarget && pWeaponInfo->GetCanFreeAim() && (m_pPed->GetPlayerInfo()->IsAiming() || m_pPed->GetPlayerInfo()->IsFiring()))
{
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming( true );
}
else
{
m_pPed->GetPlayerInfo()->SetPlayerDataFreeAiming( false );
}
m_fLockOnRangeOverride = -1.0f; // Clear the weapon range override - needs to be set every frame from script
}
void CPlayerPedTargeting::UpdateLockOnTargetPos()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
const bool bIsAssistedAiming = m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
Vector3 vCurrentAimAtPos(Vector3::ZeroType);
const CWeapon* pWeapon = m_pPed->GetWeaponManager() ? m_pPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
const CObject* pWeaponObject = m_pPed->GetWeaponManager() ? m_pPed->GetWeaponManager()->GetEquippedWeaponObject() : NULL;
Vector3 vStart(Vector3::ZeroType);
if (pWeapon && pWeaponObject)
{
const Matrix34& mWeapon = RCC_MATRIX34(pWeaponObject->GetMatrixRef());
pWeapon->CalcFireVecFromAimCamera(m_pPed, mWeapon, vStart, vCurrentAimAtPos);
}
// Update the sequence helper
TARGET_SEQUENCE_ONLY(m_TargetSequenceHelper.UpdateTime();)
if (m_pLockOnTarget || bIsAssistedAiming)
{
static dev_float NORMAL_LERP_SPEED = 0.6f;
TUNE_GROUP_FLOAT(PLAYER_TARGETING, ASSISTED_AIMING_APPROACH_RATE, 5.0f, 0.0f, 50.0f, 0.1f);
TUNE_GROUP_FLOAT(PLAYER_TARGETING, ASSISTED_AIMING_TARGET_SEQUENCE_APPROACH_RATE, 50.0f, 0.0f, 1000.0f, 0.1f);
if (m_pLockOnTarget)
{
#if USE_TARGET_SEQUENCES
if (IsUsingTargetSequence() && bIsAssistedAiming)
{
if(m_pLockOnTarget->GetIsTypePed())
{
CPed* pTargetPed = static_cast<CPed*>(m_pLockOnTarget.Get());
if (pTargetPed->GetIsDeadOrDying())
{
m_TargetSequenceHelper.Reset();
}
else
{
pedFatalAssertf(m_pPed->GetPlayerInfo(), "m_pPed GetPlayerInfo() is NULL");
// If the player is pressing fire repeatedly, override the timing in the target sequence
if (m_pPed->GetPlayerInfo()->IsJustFiring())
{
m_TargetSequenceHelper.ForceShotReady();
}
m_TargetSequenceHelper.UpdateTargetPosition(static_cast<CPed*>(m_pLockOnTarget.Get()), vCurrentAimAtPos);
}
}
else
{
m_pLockOnTarget->GetLockOnTargetAimAtPos(vCurrentAimAtPos);
}
if (m_pPed->GetIsDrivingVehicle() || !m_bHasUpdatedLockOnPos)
{
m_vLockOnTargetPos = vCurrentAimAtPos;
m_bHasUpdatedLockOnPos = true;
}
}
else
#endif // USE_TARGET_SEQUENCES
{
if (m_pPed->GetIsDrivingVehicle() || (bIsAssistedAiming || !m_bHasUpdatedLockOnPos))
{
// We haven't updated the initial lock on position so it will currently be zero, just set it
// to be the target position
m_pLockOnTarget->GetLockOnTargetAimAtPos(m_vLockOnTargetPos);
m_bHasUpdatedLockOnPos = true;
}
m_pLockOnTarget->GetLockOnTargetAimAtPos(vCurrentAimAtPos);
}
}
if (bIsAssistedAiming)
{
//! DMKH. Removing this. It doesn't make sense to alter the target position here. If this causes a problem for external code, they
//! should fix it themselves.
/*if (!IsUsingTargetSequence())
{
Vector3 vToAimPos = vCurrentAimAtPos - vStart;
vToAimPos.Normalize();
vCurrentAimAtPos = vStart + vToAimPos * CTaskAimGunOnFoot::ms_fTargetDistanceFromCameraForAimIk;
#if DEBUG_DRAW
CPedAccuracy::ms_debugDraw.AddSphere(RCC_VEC3V(vCurrentAimAtPos), 0.025f, Color_purple, 100);
#endif
}*/
if (!m_pLockOnTarget)
{
m_vLockOnTargetPos = vCurrentAimAtPos;
}
}
TUNE_GROUP_BOOL(PLAYER_TARGETING, USE_LERP, false);
if (bIsAssistedAiming && !USE_LERP)
{
m_vLockOnTargetPos.ApproachStraight(vCurrentAimAtPos, TARGET_SEQUENCE_ONLY(IsUsingTargetSequence() ? ASSISTED_AIMING_TARGET_SEQUENCE_APPROACH_RATE :) ASSISTED_AIMING_APPROACH_RATE, fwTimer::GetTimeStep());
}
else
{
TUNE_GROUP_FLOAT(PLAYER_TARGETING, ASSISTED_AIMING_LERP_RATE, 0.1f, 0.0f, 1.0f, 0.01f);
float fLerpSpeed = bIsAssistedAiming ? ASSISTED_AIMING_LERP_RATE :NORMAL_LERP_SPEED;
fLerpSpeed *= fwTimer::GetTimeStep() / (1.0f / 30.0f);
m_vLockOnTargetPos.Lerp(fLerpSpeed, vCurrentAimAtPos);
}
Vector3 vDiff = m_vLockOnTargetPos - vCurrentAimAtPos;
static dev_float MAX_OFFSET = 0.75f;
if(!bIsAssistedAiming && vDiff.Mag2() > rage::square(MAX_OFFSET))
{
vDiff.Normalize();
vDiff.Scale(MAX_OFFSET);
m_vLockOnTargetPos = vCurrentAimAtPos + vDiff;
}
#if DEBUG_DRAW
static u32 LOCK_ON_TARGET_POS_DEBUG_1 = ATSTRINGHASH("LOCK_ON_TARGET_POS_DEBUG_1", 0x50CE1003);
static u32 LOCK_ON_TARGET_POS_DEBUG_2 = ATSTRINGHASH("LOCK_ON_TARGET_POS_DEBUG_2", 0x48BFFFE7);
CPedAccuracy::ms_debugDraw.AddLine(RCC_VEC3V(vCurrentAimAtPos), RCC_VEC3V(m_vLockOnTargetPos), Color_red, 100, LOCK_ON_TARGET_POS_DEBUG_1);
CPedAccuracy::ms_debugDraw.AddSphere(RCC_VEC3V(m_vLockOnTargetPos), 0.025f, Color_red, 100, LOCK_ON_TARGET_POS_DEBUG_2);
#endif // DEBUG_DRAW
}
else if (!bIsAssistedAiming)
{
// Clear the position
m_vLockOnTargetPos.Zero();
}
if(NetworkInterface::IsGameInProgress() && m_pLockOnTarget)
{
//! Test warp
/*static dev_bool s_bTestWarp = false;
if(m_fTimeAiming > 1.0f && s_bTestWarp)
{
Vector3 vTestWarpPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
static dev_float s_fTestWarpA = 20.0f;
static dev_float s_fTestWarpB = -20.0f;
vTestWarpPos += VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetA()) * s_fTestWarpA;
vTestWarpPos += VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetB()) * s_fTestWarpB;
m_pLockOnTarget->SetPosition(vTestWarpPos);
}*/
//! Do a test to see if target has warped.
static dev_float s_fWarpTolerance = 10.0f;
if(!m_vLockOnTargetPos.IsClose(m_vLastLockOnTargetPos, s_fWarpTolerance) )
{
ClearLockOnTarget();
}
}
m_vLastLockOnTargetPos = m_vLockOnTargetPos;
}
s32 CPlayerPedTargeting::GetFindTargetFlags() const
{
// Construct targeting flags
s32 iFlags = CPedTargetEvaluator::DEFAULT_TARGET_FLAGS;
// Allow auto targeting of players in vehicles if we're on foot
if (NetworkInterface::IsGameInProgress() && !m_pPed->GetIsInVehicle())
{
iFlags |= CPedTargetEvaluator::TS_LOCKON_TO_PLAYERS_IN_VEHICLES;
}
// If we have been aiming for a while, disable the distance scoring
// This will allow us to more accurately lock-on to peds under the reticule
static dev_float AIM_TIME_TO_IGNORE_DISTANCE = 0.8f;
if(m_fTimeAiming > AIM_TIME_TO_IGNORE_DISTANCE)
{
iFlags &= ~CPedTargetEvaluator::TS_USE_DISTANCE_SCORING;
}
// If the weapon can lock-on to vehicles, or we are in a vehicle, enable all vehicles as targets
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
pedFatalAssertf(pWeaponInfo, "Weapon Info is NULL");
if(pWeaponInfo->GetIsHoming() || m_pPed->GetVehiclePedInside() != NULL)
{
iFlags |= CPedTargetEvaluator::TS_CHECK_VEHICLES;
}
//! Do Aim vector bonus.
static dev_bool s_bDoAimBonus = false;
if(s_bDoAimBonus)
{
iFlags |= CPedTargetEvaluator::TS_DO_AIMVECTOR_BONUS_AND_TASK_MODIFIER;
}
bool bUseCoverVantagePoint = false;
#if __BANK
if(PARAM_enablecovervantagepointforlos.Get())
{
bUseCoverVantagePoint = true;
}
#endif // __BANK
//! Don't use vantage point if assisted aiming as it will slow things down as you won't actually hit the target
//! if they are in cover.
if(m_pPed->GetPlayerInfo()->IsAssistedAiming())
{
bUseCoverVantagePoint = false;
}
if(bUseCoverVantagePoint)
{
iFlags |= CPedTargetEvaluator::TS_USE_COVER_VANTAGE_POINT_FOR_LOS;
}
if(CPedTargetEvaluator::ms_Tunables.m_UseNonNormalisedScoringForPlayer)
{
iFlags |= CPedTargetEvaluator::TS_USE_NON_NORMALIZED_SCORE;
}
u32 nTimeSinceAiming = fwTimer::GetTimeInMilliseconds() - m_uTimeSinceLastAiming;
if(nTimeSinceAiming < CPedTargetEvaluator::ms_Tunables.m_TargetDistanceMaxWeightingAimTime)
{
iFlags |= CPedTargetEvaluator::TS_DO_COVER_LOCKON_REJECTION;
}
return iFlags;
}
bool CPlayerPedTargeting::FindAndSetLockOnTarget()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
// Get our held weapon info
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(!pWeaponInfo)
{
// No weapon
return false;
}
// Do not allow lock-on targeting with static reticule
if(pWeaponInfo->GetIsStaticReticulePosition())
{
return false;
}
s32 iFlags = GetFindTargetFlags();
// Check if we're already locked on
if(m_pLockOnTarget && !CPedTargetEvaluator::GetIsTargetingDisabled(m_pPed, m_pLockOnTarget, iFlags))
{
// Cache the weapon range (also check to see if overriding from script)
const float fWeaponRange = GetWeaponLockOnRange(pWeaponInfo, m_pLockOnTarget);
// Find out if the target is still valid
// Check if ped is in this weapon's range
const Vector3 vLockOnTargetPos = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition());
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
Vector2 v2DPedDistance;
v2DPedDistance.x = vLockOnTargetPos.x - vPedPosition.x;
v2DPedDistance.y = vLockOnTargetPos.y - vPedPosition.y;
float fPedDistance = v2DPedDistance.Mag();
if(fPedDistance > fWeaponRange)
{
ClearLockOnTarget();
}
}
else
{
// Prepare to find a new target
CEntity* pTargetEntity = m_pCachedLockOnTarget ? m_pCachedLockOnTarget : FindLockOnTarget();
// Check if we found a valid target
if(pTargetEntity)
{
// We found a target within weapon range - lock-on to it
SetLockOnTarget(pTargetEntity);
if(ShouldUsePullGunSpeech(pWeaponInfo))
m_pPed->NewSay("PULL_GUN");
}
}
return m_pLockOnTarget != NULL;
}
CEntity *CPlayerPedTargeting::FindLockOnTarget()
{
CEntity* pTargetEntity = NULL;
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(pWeaponInfo)
{
//! DMKH. Don't do melee targeting in cover.
if(pWeaponInfo->GetIsMelee() && !IsMeleeTargetingUsingNormalLock())
{
pTargetEntity = FindLockOnMeleeTarget(m_pPed, true);
}
else
{
float fWeaponRange = GetWeaponLockOnRange(pWeaponInfo);
bool bAllowNoReticule = true;
if(pWeaponInfo->GetIsMelee())
{
fWeaponRange = ms_Tunables.m_UnarmedInCoverTargetingDistance;
bAllowNoReticule = false;
}
CPedTargetEvaluator::tHeadingLimitData headingData;
float fPitchMin = 0.0f;
float fPitchMax = 0.0f;
float fDistanceWeighting = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceWeightingMax;
float fHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_TargetHeadingWeighting;
float fDistanceFallOff = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceFallOffMax;
if( bAllowNoReticule && m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_NO_RETICULE_AIM_ASSIST_ON) )
{
fWeaponRange *= ms_Tunables.GetNoReticuleLockOnRangeModifier();
fWeaponRange = Min(fWeaponRange, ms_Tunables.GetNoReticuleMaxLockOnRange());
headingData.fHeadingAngularLimit = ms_Tunables.GetNoReticuleTargetAngularLimit();
headingData.fHeadingAngularLimitClose = ms_Tunables.GetNoReticuleTargetAngularLimitClose();
headingData.fHeadingAngularLimitCloseDistMin = ms_Tunables.GetNoReticuleTargetAngularLimitCloseDistMin();
headingData.fHeadingAngularLimitCloseDistMax = ms_Tunables.GetNoReticuleTargetAngularLimitCloseDistMax();
fPitchMin = -ms_Tunables.GetNoReticuleTargetAngularLimitClose();
fPitchMax = ms_Tunables.GetNoReticuleTargetAngularLimitClose();
fDistanceWeighting = 1.0f;
fHeadingWeighting = 1.0f;
headingData.fAimPitchLimitMin = ms_Tunables.GetNoReticuleTargetAimPitchLimit();
headingData.fAimPitchLimitMax = ms_Tunables.GetNoReticuleTargetAimPitchLimit();
}
else
{
fWeaponRange *= ms_Tunables.GetLockOnRangeModifier();
headingData.fHeadingAngularLimitClose = ms_Tunables.GetDefaultTargetAngularLimitClose();
headingData.fHeadingAngularLimitCloseDistMin = ms_Tunables.GetDefaultTargetAngularLimitCloseDistMin();
headingData.fHeadingAngularLimitCloseDistMax = ms_Tunables.GetDefaultTargetAngularLimitCloseDistMax();
u32 nTimeSinceAiming = fwTimer::GetTimeInMilliseconds() - m_uTimeSinceLastAiming;
if(nTimeSinceAiming < CPedTargetEvaluator::ms_Tunables.m_TargetDistanceMaxWeightingAimTime)
{
float fWeight = (float)nTimeSinceAiming/(float)CPedTargetEvaluator::ms_Tunables.m_TargetDistanceMaxWeightingAimTime;
float fMinWeighting = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceWeightingMin;
float fMaxWeighting = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceWeightingMax;
fDistanceWeighting = fMinWeighting + ((fMaxWeighting - fMinWeighting) * fWeight);
float fMinFallOff = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceFallOffMin;
float fMaxFallOff = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceFallOffMax;
fDistanceFallOff = fMinFallOff + ((fMaxFallOff - fMinFallOff) * fWeight);
}
float fAngularRange = ms_Tunables.GetDefaultTargetAngularLimit();
if (m_pPed->GetIsDrivingVehicle() && m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
{
TUNE_GROUP_FLOAT(DRIVE_BY_TUNE, DRIVEBY_ANGULAR_RANGE, 90.0f, 0.0f, 180.0f, 1.0f);
fAngularRange = DRIVEBY_ANGULAR_RANGE;
}
headingData.fHeadingAngularLimit = fAngularRange;
headingData.fWideHeadingAngulerLimit = ms_Tunables.GetWideTargetAngularLimit();
headingData.fAimPitchLimitMin = ms_Tunables.GetDefaultTargetAimPitchMin();
headingData.fAimPitchLimitMax = ms_Tunables.GetDefaultTargetAimPitchMax();
}
pTargetEntity = CPedTargetEvaluator::FindTarget(m_pPed,
GetFindTargetFlags(),
fWeaponRange,
fWeaponRange * ms_Tunables.GetLockOnDistanceRejectionModifier(),
NULL,
NULL,
headingData,
fPitchMin,
fPitchMax,
fDistanceWeighting,
fHeadingWeighting,
fDistanceFallOff);
}
}
return pTargetEntity;
}
CEntity *CPlayerPedTargeting::FindLockOnMeleeTarget(const CPed *pPed, bool bAllowLockOnIfTargetFriendly)
{
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(!pWeaponInfo)
{
return NULL;
}
float fWeaponRange = GetWeaponLockOnRange(pWeaponInfo);
s32 nDefaultFlags = CPedTargetEvaluator::MELEE_TARGET_FLAGS;
CPedTargetEvaluator::tHeadingLimitData headingData;
CControl* pControl = const_cast<CControl*>(pPed->GetControlFromPlayer());
if( pControl )
{
// Check the desired direction input
const Vector2 vecStickInput( pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm() );
const float fInputMag = vecStickInput.Mag2();
if( fInputMag > rage::square( 0.5f ) )
{
nDefaultFlags |= CPedTargetEvaluator::TS_DESIRED_DIRECTION_HEADING;
nDefaultFlags |= CPedTargetEvaluator::TS_CAMERA_HEADING;
headingData.fHeadingAngularLimit = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetAngularLimitMelee;
headingData.fStickHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_MeleeLockOnStickWeighting;
headingData.fCameraHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_MeleeLockOnCameraWeighting;
}
else
{
nDefaultFlags |= CPedTargetEvaluator::TS_CAMERA_HEADING;
nDefaultFlags |= CPedTargetEvaluator::TS_PED_HEADING;
headingData.fPedHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_MeleeLockOnCameraWeightingNoStick;
headingData.fCameraHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_MeleeLockOnPedWeightingNoStick;
headingData.bPedHeadingNeedsTargetOnScreen = true;
headingData.fHeadingAngularLimit = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetAngularLimitMeleeLockOnNoStick;
}
}
//! Allows us to lock on to peds that are allowing friendly lock on (e.g. chop).
if(bAllowLockOnIfTargetFriendly)
{
nDefaultFlags |= CPedTargetEvaluator::TS_ALLOW_LOCK_ON_IF_TARGET_IS_FRIENDLY;
}
headingData.fHeadingFalloffPower = 1.0f;
CEntity *pTargetEntity = CPedTargetEvaluator::FindTarget( pPed,
nDefaultFlags,
fWeaponRange,
fWeaponRange,
NULL,
NULL,
headingData,
0.0f,
0.0f,
CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMelee,
CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMelee );
return pTargetEntity;
}
CEntity *CPlayerPedTargeting::FindMeleeTarget(const CPed *pPed,
const CWeaponInfo* pWeaponInfo,
bool bDoMeleeActionCheck,
bool bTargetDeadPeds,
bool bFallBackToPedHeading,
bool bMustUseDesiredDirection,
const CEntity* pCurrentMeleeTarget)
{
// Find range of current weapon
CPedTargetEvaluator::tHeadingLimitData headingData;
headingData.fHeadingAngularLimit = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetAngularLimitMelee;
headingData.fHeadingFalloffPower = 1.0f;
float fPitchLimitMin = 0.0f;
float fPitchLimitMax = 0.0f;
float fWeaponRange = CPedTargetEvaluator::ms_Tunables.m_DefaultMeleeRange; // Default to the unarmed range.
if( pWeaponInfo )
{
const CAimingInfo* pAimingInfo = pWeaponInfo->GetAimingInfo();
if( pAimingInfo )
{
headingData.fHeadingAngularLimit = pAimingInfo->GetHeadingLimit();
fPitchLimitMin = pAimingInfo->GetSweepPitchMin();
fPitchLimitMax = pAimingInfo->GetSweepPitchMax();
}
fWeaponRange = pWeaponInfo->GetLockOnRange();
}
float fHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMelee;
float fDistanceWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMelee;
#if FPS_MODE_SUPPORTED
bool bIsFPSMode = pPed->IsFirstPersonShooterModeEnabledForPlayer(false);
if( bIsFPSMode )
{
fHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeFPS;
fDistanceWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeFPS;
}
#endif
if(CPedTargetEvaluator::ms_Tunables.m_UseMeleeHeadingOverride)
{
float fRunningScale = 0.0;
if(pPed->GetMotionData()->GetCurrentMbrY() > MOVEBLENDRATIO_WALK)
{
fRunningScale = Min(pPed->GetMotionData()->GetCurrentMbrY() - MOVEBLENDRATIO_WALK / 1.0f, 1.0f);
}
headingData.fHeadingAngularLimit =
CPedTargetEvaluator::ms_Tunables.m_MeleeHeadingOverride + ( (CPedTargetEvaluator::ms_Tunables.m_MeleeHeadingOverrideRunning - CPedTargetEvaluator::ms_Tunables.m_MeleeHeadingOverride) * fRunningScale );
headingData.fHeadingFalloffPower = 1.0f + ((CPedTargetEvaluator::ms_Tunables.m_MeleeHeadingFalloffPowerRunning-1.0f) * fRunningScale);
#if FPS_MODE_SUPPORTED
if( bIsFPSMode )
{
fHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeFPS + ((CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeRunningFPS-CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeFPS) * fRunningScale);;
fDistanceWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeFPS + ((CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeRunningFPS-CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeFPS) * fRunningScale);
}
else
#endif
{
fHeadingWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMelee + ((CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeRunning-CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMelee) * fRunningScale);
fDistanceWeighting = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMelee + ((CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeRunning-CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMelee) * fRunningScale);
}
}
if(CPedTargetEvaluator::ms_Tunables.m_LimitMeleeRangeToDefault && (fWeaponRange > CPedTargetEvaluator::ms_Tunables.m_DefaultMeleeRange) )
{
fWeaponRange = CPedTargetEvaluator::ms_Tunables.m_DefaultMeleeRange;
}
CEntity *pTargetEntity = NULL;
s32 nDefaultFlags = CPedTargetEvaluator::MELEE_TARGET_FLAGS | CPedTargetEvaluator::TS_USE_MELEE_LOS_POSITION;
if(bDoMeleeActionCheck)
{
nDefaultFlags |= CPedTargetEvaluator::TS_CALCULATE_MELEE_ACTION;
}
if(bTargetDeadPeds)
{
nDefaultFlags |= CPedTargetEvaluator::TS_ACCEPT_INJURED_AND_DEAD_PEDS;
}
CPedTargetEvaluator::tFindTargetParams testParams;
CControl* pControl = const_cast<CControl*>(pPed->GetControlFromPlayer());
if( pControl )
{
// Check the desired direction input
const Vector2 vecStickInput( pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm() );
const float fInputMag = vecStickInput.Mag2();
if( fInputMag > rage::square( 0.5f ) )
{
nDefaultFlags |= CPedTargetEvaluator::TS_DESIRED_DIRECTION_HEADING;
}
else
{
//! If no stick input, see if we can use cached stick input
if(fwTimer::GetTimeInMilliseconds() < (m_LastTimeDesiredStickInputSet + ms_Tunables.m_TimeToAllowCachedStickInputForMelee))
{
const float fInputMag = m_vLastValidStickInput.Mag2();
if( fInputMag > rage::square( 0.5f ) )
{
testParams.pvDesiredStickOverride = &m_vLastValidStickInput;
nDefaultFlags |= CPedTargetEvaluator::TS_DESIRED_DIRECTION_HEADING;
}
}
}
}
if(!(nDefaultFlags & CPedTargetEvaluator::TS_DESIRED_DIRECTION_HEADING))
{
//! If we have no stick input, and we are asking for it, then bail, we aren't allowed to find a target.
if(bMustUseDesiredDirection)
{
return NULL;
}
//! If we want to fallback to ped heading (as opposed to 360), then do so only if we aren't choosing stick direction.
if( bFallBackToPedHeading )
{
nDefaultFlags |= CPedTargetEvaluator::TS_PED_HEADING;
}
#if FPS_MODE_SUPPORTED
//! If we're in first-person mode then ensure we use the camera heading.
if( bIsFPSMode )
{
nDefaultFlags |= CPedTargetEvaluator::TS_CAMERA_HEADING;
}
#endif
}
//! Force camera heading underwater. Need to use some form of distance scoring.
CTaskMotionBase *pMotionTask = pPed->GetCurrentMotionTask();
bool bUnderWater = pMotionTask && pMotionTask->IsUnderWater();
if(bUnderWater)
{
nDefaultFlags |= CPedTargetEvaluator::TS_CAMERA_HEADING;
}
testParams.pTargetingPed = pPed;
testParams.iFlags = nDefaultFlags;
testParams.fMaxDistance = fWeaponRange;
testParams.fMaxRejectionDistance = fWeaponRange;
testParams.headingLimitData = headingData;
testParams.fPitchLimitMin = fPitchLimitMin;
testParams.fPitchLimitMax = fPitchLimitMax;
testParams.fDistanceWeight = fDistanceWeighting;
testParams.fHeadingWeight = fHeadingWeighting;
testParams.pCurrentMeleeTarget = pCurrentMeleeTarget;
// First try to find a target that this player is attempting to attack based on controller stick direction
pTargetEntity = CPedTargetEvaluator::FindTarget( testParams );
return pTargetEntity;
}
bool CPlayerPedTargeting::FindNextLockOnTarget(CEntity* pOldTarget, HeadingDirection eDirection, bool bSetTarget)
{
// Get our held weapon info
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(!pWeaponInfo)
{
// No weapon
return false;
}
// Do not allow lock-on targeting with static reticule
if(pWeaponInfo->GetIsStaticReticulePosition())
{
return false;
}
float fLockOnRange = GetWeaponLockOnRange(pWeaponInfo);
s32 iFlags = 0;
// Allow auto targeting of players in vehicles if we're on foot
if (NetworkInterface::IsGameInProgress() && !m_pPed->GetIsInVehicle())
{
iFlags |= CPedTargetEvaluator::TS_LOCKON_TO_PLAYERS_IN_VEHICLES;
}
CPedTargetEvaluator::tHeadingLimitData headingData;
float fDistanceWeight;
float fHeadingWeight;
fDistanceWeight = CPedTargetEvaluator::ms_Tunables.m_TargetDistanceWeightingMax;
fHeadingWeight = CPedTargetEvaluator::ms_Tunables.m_TargetHeadingWeighting;
if(pWeaponInfo->GetIsMelee() && !IsMeleeTargetingUsingNormalLock())
{
iFlags |= CPedTargetEvaluator::MELEE_TARGET_FLAGS;
iFlags |= CPedTargetEvaluator::TS_CAMERA_HEADING;
headingData.fHeadingAngularLimit = ms_Tunables.GetCycleTargetAngularLimitMelee();
headingData.fHeadingFalloffPower = 1.0f;
#if FPS_MODE_SUPPORTED
if(m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
{
fDistanceWeight = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMeleeFPS;
fHeadingWeight = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMeleeFPS;
}
else
#endif
{
fDistanceWeight = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetDistanceWeightMelee;
fHeadingWeight = CPedTargetEvaluator::ms_Tunables.m_DefaultTargetHeadingWeightMelee;
}
headingData.bCamHeadingNeedsTargetOnScreen = true;
iFlags |= ((eDirection == HD_LEFT) ? CPedTargetEvaluator::TS_CYCLE_LEFT : CPedTargetEvaluator::TS_CYCLE_RIGHT);
}
else
{
if(pWeaponInfo->GetIsMelee())
{
fLockOnRange = ms_Tunables.m_UnarmedInCoverTargetingDistance;
}
headingData.fHeadingAngularLimit = ms_Tunables.GetCycleTargetAngularLimit();
headingData.fWideHeadingAngulerLimit = ms_Tunables.GetWideTargetAngularLimit();
headingData.fAimPitchLimitMin = ms_Tunables.GetDefaultTargetAimPitchMin();
headingData.fAimPitchLimitMax = ms_Tunables.GetDefaultTargetAimPitchMax();
iFlags |= ((eDirection == HD_LEFT) ? CPedTargetEvaluator::CYCLE_LEFT_TARGET_FLAGS : CPedTargetEvaluator::CYCLE_RIGHT_TARGET_FLAGS);
#if FPS_MODE_SUPPORTED
//! In 1st person, don't allow us to lock on to peds that are off screen.
if(m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
{
headingData.bCamHeadingNeedsTargetOnScreen = true;
}
#endif
}
float fRejectionRange = fLockOnRange * ms_Tunables.GetLockOnDistanceRejectionModifier();
CEntity* pTargetEntity = pTargetEntity = CPedTargetEvaluator::FindTarget(m_pPed,
iFlags,
fLockOnRange,
fRejectionRange,
pOldTarget,
NULL,
headingData,
0.0f,
0.0f,
fDistanceWeight,
fHeadingWeight);
if(bSetTarget && pTargetEntity && pTargetEntity != pOldTarget)
{
SetLockOnTarget(pTargetEntity, true);
}
return m_pLockOnTarget != NULL;
}
bool CPlayerPedTargeting::GetShouldBreakSoftLockTarget()
{
if(m_pLockOnTarget && m_pLockOnTarget->GetIsTypePed())
{
const CPed* pTargetPed = static_cast<const CPed*>(m_pLockOnTarget.Get());
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
//! Break if ped does a combat roll.
if(pTargetPed->GetPedResetFlag(CPED_RESET_FLAG_BreakTargetLock) && (!pWeaponInfo || !pWeaponInfo->GetIsMelee()))
{
return true;
}
}
return false;
}
bool CPlayerPedTargeting::GetShouldBreakLockOnWithTarget()
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pLockOnTarget)
{
bool bForceLockOnUpdate = false;
if (m_pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON))
{
TUNE_GROUP_BOOL(PLAYER_TARGETING, ENABLE_ASSISTED_ANGLE_CHECK, false);
if (ENABLE_ASSISTED_ANGLE_CHECK)
{
Vector3 vecAim = camInterface::GetPlayerControlCamAimFrame().GetFront();
vecAim.z = 0.0f;
vecAim.Normalize();
Vector3 vToTarget = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition() - m_pPed->GetTransform().GetPosition());
vToTarget.z = 0.0f;
vToTarget.Normalize();
const float fDot = vecAim.Dot(vToTarget);
TUNE_GROUP_FLOAT(PLAYER_TARGETING, dotTolerance, 0.85f, 0.0f, 1.0f, 0.01f);
if (fDot > dotTolerance)
bForceLockOnUpdate = true;
}
else
{
bForceLockOnUpdate = true;
}
}
if(!CPlayerInfo::IsHardAiming() && !CPlayerInfo::IsDriverFiring() && !bForceLockOnUpdate)
{
// Release lock if the lockon isn't held down hard
return true;
}
const CWeaponInfo* pWeaponInfo = GetWeaponInfo();
if(!pWeaponInfo)
{
// No weapon
return true;
}
CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
bool bCanLockon = CanPedLockOnWithWeapon(pWeapon, *pWeaponInfo);
if(!bCanLockon)
{
// Weapon can't lock-on
return true;
}
s32 iFlags = CPedTargetEvaluator::DEFAULT_TARGET_FLAGS;
if(pWeaponInfo->GetIsMelee() && !IsMeleeTargetingUsingNormalLock())
{
iFlags = CPedTargetEvaluator::MELEE_TARGET_FLAGS;
//! If we lose LOS, break lock.
if(GetShouldBreakLockTargetIfNoLOS(iFlags, ms_Tunables.m_MeleeLostLOSBreakTime, 250))
{
return true;
}
}
// This will test to see if the current target is still a valid lockon target
if(CPedTargetEvaluator::GetIsTargetingDisabled(m_pPed, m_pLockOnTarget, iFlags))
{
// Targeting disabled
return true;
}
if(m_pLockOnTarget->GetIsTypePed())
{
const CPed* pTargetPed = static_cast<const CPed*>(m_pLockOnTarget.Get());
if( pWeaponInfo->GetIsMelee() )
{
// Are they dead or dying
if( !pTargetPed->GetTargetWhenInjuredAllowed() && pTargetPed->GetIsDeadOrDying() )
{
return true;
}
}
// Allow auto targeting of players in vehicles if we're on foot
if (NetworkInterface::IsGameInProgress() && !m_pPed->GetIsInVehicle())
{
iFlags |= CPedTargetEvaluator::TS_LOCKON_TO_PLAYERS_IN_VEHICLES;
}
if(!CPedTargetEvaluator::GetCanPedBeTargetedInAVehicle(m_pPed, pTargetPed, iFlags))
{
// Can't target ped in a vehicle
return true;
}
if(!CLocalisation::KickingWhenDown())
{
if(pTargetPed->GetIsDeadOrDying())
{
// Break lock-on when target is dead due to localisation reasons
return true;
}
}
//! Break if ped does a combat roll.
if(pTargetPed->GetPedResetFlag(CPED_RESET_FLAG_BreakTargetLock) && !pWeaponInfo->GetIsMelee())
{
return true;
}
}
// Check if we have gone outside of lock-on range
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
const Vector3 vLockOnTargetPosition = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition());
Vector3 vDistance = vLockOnTargetPosition - vPedPosition;
float fDistanceSq = vDistance.Mag2();
const float fLockOnRange = GetWeaponLockOnRange(pWeaponInfo, m_pLockOnTarget);
if(fDistanceSq > rage::square(fLockOnRange))
{
return true;
}
if(!pWeaponInfo->GetIsMelee() || IsMeleeTargetingUsingNormalLock())
{
// Test if the aiming IK can reach the target position
// PH: Do the IK test against the root bone, primarily because NM
// can drop the target position and break lock-on even though the reticule position is valid
Vector3 vTargetPos = vLockOnTargetPosition;
if(m_pLockOnTarget->GetIsTypePed())
{
const CPed* pPedTarget = static_cast<const CPed*>(m_pLockOnTarget.Get());
s32 iBoneIndex = pPedTarget->GetBoneIndexFromBoneTag(BONETAG_ROOT);
Matrix34 globalBoneMat;
pPedTarget->GetGlobalMtx(iBoneIndex, globalBoneMat);
vTargetPos = globalBoneMat.d;
}
Vector3 vDelta = vTargetPos - vPedPosition;
vDelta.Normalize();
// Calculate and limit the pitch
float fPitch = rage::Atan2f(vDelta.z, vDelta.XYMag());
fPitch = fwAngle::LimitRadianAngle(fPitch);
if(fPitch > CTorsoIkSolver::ms_torsoInfo.maxPitch || fPitch < CTorsoIkSolver::ms_torsoInfo.minPitch)
{
// IK can't reach target
return true;
}
}
}
return false;
}
bool CPlayerPedTargeting::GetShouldBreakLockTargetIfNoLOS(s32 iFlags, float fBreakTime, u32 nTestTimeMS)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pLockOnTarget)
{
if(CPedTargetEvaluator::GetNeedsLineOfSightToTarget(m_pLockOnTarget))
{
if( (m_fLostLockLOSTimer > 0.0f) || ((fwTimer::GetTimeInMilliseconds() - nTestTimeMS) > m_uLastLockLOSTestTime) )
{
m_uLastLockLOSTestTime = fwTimer::GetTimeInMilliseconds();
bool bLostLOS = CPedTargetEvaluator::GetIsLineOfSightBlocked(m_pPed,
m_pLockOnTarget,
false, //bIgnoreTargetsCover
(iFlags & CPedTargetEvaluator::TS_IGNORE_LOW_OBJECTS)!=0, //bAlsoIgnoreLowObjects
(iFlags & CPedTargetEvaluator::TS_USE_MODIFIED_LOS_DISTANCE_IF_SET) != 0, //bUseTargetableDistance
(iFlags & CPedTargetEvaluator::TS_IGNORE_VEHICLES_IN_LOS) != 0, //bIgnoreVehiclesForLOS
(iFlags & CPedTargetEvaluator::TS_USE_COVER_VANTAGE_POINT_FOR_LOS)!=0, //bUseCoverVantagePointForLOS
(iFlags & CPedTargetEvaluator::TS_USE_MELEE_LOS_POSITION)!=0, //bUseMeleePosForLOS
false, //bTestPotentialPedTargets
iFlags);
if(bLostLOS)
{
m_fLostLockLOSTimer += fwTimer::GetTimeStep();
if(m_fLostLockLOSTimer > fBreakTime)
return true;
}
else
{
m_fLostLockLOSTimer = 0.0f;
}
}
}
}
return false;
}
void CPlayerPedTargeting::UpdateFineAim(bool bSpringback)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
if(m_pLockOnTarget)
{
float fTimeStep = fwTimer::GetTimeStep_NonScaledClipped();
float fVerticalAimMovement = ms_Tunables.GetFineAimVerticalMovement();
float fDownwardsVerticalAimMovement = ms_Tunables.GetFineAimDownwardsVerticalMovement();
float fSidewaysScale = ms_Tunables.GetFineAimSidewaysScale();
weaponAssert(m_pPed->GetWeaponManager());
if(m_pPed->GetWeaponManager()->GetIsArmedMelee())
{
// Can't fine aim when using melee weapons
ClearFineAim();
}
else
{
const CControl* pControl = m_pPed->GetControlFromPlayer();
if(pControl && !IsTaskDisablingTargeting())
{
Vector3 vFineAimOffset(Vector3::ZeroType);
bool bAttemptingFineAim = false;
static dev_float MIN_FINE_AIM_VEC = 0.05f;
if(!m_bPlayerIsFineAiming)
{
//! Clear any offset till we allow fine aiming.
m_vFineAimOffset.Zero();
}
if(bSpringback)
{
vFineAimOffset.x = pControl->GetPedAimWeaponLeftRight().GetUnboundNorm() * m_pPed->GetCapsuleInfo()->GetHalfWidth() * fSidewaysScale;
vFineAimOffset.z = -1.0f * pControl->GetPedAimWeaponUpDown().GetUnboundNorm();
if(vFineAimOffset.z > 0.0f)
{
vFineAimOffset.z *= fVerticalAimMovement;
}
else
{
vFineAimOffset.z *= fDownwardsVerticalAimMovement;
}
bAttemptingFineAim = vFineAimOffset.Mag2() > (rage::square(MIN_FINE_AIM_VEC));
}
else
{
Vector3 vLockOnPos = VEC3V_TO_VECTOR3(m_pLockOnTarget->GetTransform().GetPosition());
Vector3 vPedPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
Vector3 vDistance = vLockOnPos - vPedPos;
float fDist = Clamp(vDistance.Mag(), ms_Tunables.GetFineAimSpeedMultiplierCloseDistMin(), ms_Tunables.GetFineAimSpeedMultiplierCloseDistMax());
float fSpeedWeight = (fDist - ms_Tunables.GetFineAimSpeedMultiplierCloseDistMin()) / (ms_Tunables.GetFineAimSpeedMultiplierCloseDistMax() - ms_Tunables.GetFineAimSpeedMultiplierCloseDistMin());
fSpeedWeight = Clamp(fSpeedWeight, 0.0f, 1.0f);
float fSpeedMultiplier = ms_Tunables.GetFineAimSpeedMultiplierClose() + ((1.0f - ms_Tunables.GetFineAimSpeedMultiplierClose()) * fSpeedWeight);
fSpeedMultiplier *= ms_Tunables.GetFineAimSpeedMultiplier();
fSpeedMultiplier *= fTimeStep;
vFineAimOffset = m_vFineAimOffset;
if(abs(pControl->GetPedAimWeaponLeftRight().GetUnboundNorm()) > 0.99f)
{
m_fFineAimHorizontalSpeedWeight += ms_Tunables.GetFineAimHorWeightSpeedMultiplier() * fTimeStep;
m_fFineAimHorizontalSpeedWeight = Min(m_fFineAimHorizontalSpeedWeight, 1.0f);
}
else
{
m_fFineAimHorizontalSpeedWeight -= ms_Tunables.GetFineAimHorWeightSpeedMultiplier() * fTimeStep;
m_fFineAimHorizontalSpeedWeight = Max(m_fFineAimHorizontalSpeedWeight, 0.0f);
}
float fFineAimHorizontalSpeedWeight = rage::Powf(m_fFineAimHorizontalSpeedWeight, ms_Tunables.GetFineAimHorSpeedPower());
float fHorSpeed = ms_Tunables.GetFineAimHorSpeedMin() + ((ms_Tunables.GetFineAimHorSpeedMax() - ms_Tunables.GetFineAimHorSpeedMin()) * fFineAimHorizontalSpeedWeight);
vFineAimOffset.x += pControl->GetPedAimWeaponLeftRight().GetUnboundNorm() * fHorSpeed * fSpeedMultiplier;
vFineAimOffset.z += -1.0f * pControl->GetPedAimWeaponUpDown().GetUnboundNorm() * ms_Tunables.GetFineAimVerSpeed() * fSpeedMultiplier;
//! Start attempting to fine aim immediately.
m_bHasAttemptedNonSpringFineAim = true;
bAttemptingFineAim = m_bHasAttemptedNonSpringFineAim;
}
if(!m_bPlayerIsFineAiming)
{
if(bAttemptingFineAim)
{
m_fTimeFineAiming += fTimeStep;
}
else
{
m_fTimeFineAiming = 0.0f;
}
float fUpDown = pControl->GetPedAimWeaponUpDown().GetUnboundNorm();
static dev_float s_fReleaseThreshold = 0.1f;
static dev_float s_fUpDownThreshold = 0.75f;
if( (abs(fUpDown) < s_fReleaseThreshold))
{
m_bPlayerReleasedUpDownControlForFineAim = true;
}
if(m_bPlayerReleasedUpDownControlForFineAim && (abs(fUpDown) > s_fUpDownThreshold))
{
m_bPlayerPressingUpDownControlForFineAim = true;
}
float fMinTime = m_bPlayerPressingUpDownControlForFineAim ? ms_Tunables.GetMinFineAimTime() : ms_Tunables.GetMinFineAimTimeHoldingStick();
const camGameplayDirector& gameplayDirector = camInterface::GetGameplayDirector();
bool bShowingReticule = gameplayDirector.ShouldDisplayReticule() || CNewHud::IsReticuleVisible();
bool bAllowReticuleToEnableFineAiming = false;
if(m_fTimeFineAiming > ms_Tunables.GetMinFineAimTime())
{
bAllowReticuleToEnableFineAiming = bShowingReticule;
}
if( (m_fTimeFineAiming > fMinTime) || bAllowReticuleToEnableFineAiming)
{
m_bPlayerIsFineAiming = true;
}
}
else
{
if(bSpringback)
{
static dev_float LERP_AMOUNT = 0.5f;
m_vFineAimOffset.Lerp(LERP_AMOUNT, vFineAimOffset);
}
else
{
m_vFineAimOffset = vFineAimOffset;
}
m_fTimeFineAiming += fTimeStep;
}
}
}
}
else
{
// No lock-on target
ClearFineAim();
}
}
void CPlayerPedTargeting::ClearFineAim()
{
m_bPlayerIsFineAiming = false;
m_fTimeFineAiming = 0.0f;
m_fFineAimHorizontalSpeedWeight = 0.0f;
m_bHasAttemptedNonSpringFineAim = false;
m_bPlayerReleasedUpDownControlForFineAim = false;
m_bPlayerPressingUpDownControlForFineAim = false;
}
void CPlayerPedTargeting::ClearSoftAim()
{
m_bPlayerHasStartedToAim = false;
m_bPlayerHasBrokenLockOn = false;
m_fTimeSoftLockedOn = 0.0f;
}
void CPlayerPedTargeting::ClearFreeAim()
{
// Clear the pointers
m_pFreeAimTarget = NULL;
m_pFreeAimTargetRagdoll = NULL;
m_pFreeAimAssistTarget = NULL;
m_uStartTimeAimingAtFreeAimTarget = 0;
m_bFreeAimTargetingVehicleForCamera = false;
m_FreeAimProbeResults.Reset();
//m_FreeAimAssistProbeResults.Reset(); // Should only be cleared in CPlayerPedTargeting::FindFreeAimAssistTarget
}
void CPlayerPedTargeting::FindFreeAimTarget(const Vector3& vStart, const Vector3& vEnd, const CWeaponInfo& weaponInfo, Vector3& vHitPos)
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
bool bRejectFreeAimResults = false;
#if FPS_MODE_SUPPORTED
if( m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false) )
{
if( weaponInfo.GetIsArmed() )
{
if( m_pPed->GetPlayerInfo() && !m_pPed->GetPlayerInfo()->IsAiming() )
{
bRejectFreeAimResults = true;
}
}
else
{
// In first person, cannot aim if unarmed.
bRejectFreeAimResults = true;
}
}
#endif // FPS_MODE_SUPPORTED
CVehicle *pVehicleTargeterInside = m_pPed->GetVehiclePedInside();
bool bInLargeVehicle = pVehicleTargeterInside && (pVehicleTargeterInside->GetSeatManager()->GetMaxSeats() > 8);
//static dev_bool sbForceSychronous = false;
bool bUseSynchronousResults = false;
if (/*sbForceSychronous ||*/ m_pPed->GetIsAttached())
{
// Probe for a target
m_FreeAimProbeResults.Reset();
WorldProbe::CShapeTestProbeDesc probeDesc;
probeDesc.SetResultsStructure(&m_FreeAimProbeResults);
probeDesc.SetStartAndEnd(vStart, vEnd);
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_RAGDOLL_TYPE | ArchetypeFlags::GTA_PED_TYPE | ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_ALL_MAP_TYPES | ArchetypeFlags::GTA_OBJECT_TYPE | ArchetypeFlags::GTA_GLASS_TYPE);
probeDesc.SetExcludeEntity(m_pPed, bInLargeVehicle || m_pPed->IsInFirstPersonVehicleCamera() ? WorldProbe::EIEO_DONT_ADD_VEHICLE_OCCUPANTS : EXCLUDE_ENTITY_OPTIONS_NONE);
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
bUseSynchronousResults = true;
}
#if RSG_PC
bool bStereoEnabled = GRCDEVICE.IsStereoEnabled() && GRCDEVICE.CanUseStereo();
const camFrame& aimFrame = camInterface::GetPlayerControlCamAimFrame();
Vector3 vStartCam = aimFrame.GetPosition();
#endif
if(m_FreeAimProbeResults.GetResultsReady() || bUseSynchronousResults)
{
CEntity* pFreeAimTarget = NULL;
CEntity* pFreeAimTargetRagdoll = NULL;
m_vClosestFreeAimTargetPos.Zero();
Vector3 vClosestFreeAimTargetPos(Vector3::ZeroType);
bool bFreeAimTargetingVehicleForCamera = false;
if(m_FreeAimProbeResults.GetNumHits() > 0)
{
const CEntity* pHitEntity = m_FreeAimProbeResults[0].GetHitEntity();
if(pHitEntity && pHitEntity->GetIsTypeVehicle())
{
bFreeAimTargetingVehicleForCamera = true;
}
}
#if RSG_PC
if (bStereoEnabled)
{
m_vClosestFreeAimTargetPosStereo.Zero();
}
#endif
for(WorldProbe::ResultIterator i = m_FreeAimProbeResults.begin(); i < m_FreeAimProbeResults.last_result(); ++i)
{
const bool bIsShootThrough = PGTAMATERIALMGR->GetMtlFlagShootThrough( i->GetHitMaterialId() ) || PGTAMATERIALMGR->GetIsShootThrough( i->GetHitMaterialId() );
if (bIsShootThrough )
continue;
//Remember closest hit position
Vector3 hitPos = i->GetHitPosition();
if (vClosestFreeAimTargetPos.IsZero() || (hitPos - vStart).Mag2() < (vClosestFreeAimTargetPos-vStart).Mag2() )
vClosestFreeAimTargetPos = hitPos;
#if RSG_PC
if (bStereoEnabled)
{
if (m_vClosestFreeAimTargetPosStereo.IsZero() || (hitPos - vStartCam).Mag2() < (m_vClosestFreeAimTargetPosStereo-vStartCam).Mag2() )
{
m_vClosestFreeAimTargetPosStereo = hitPos;
m_vFreeAimTargetDir = (m_vClosestFreeAimTargetPosStereo-vStartCam);
m_fClosestFreeAimTargetDist = m_vFreeAimTargetDir.Mag();
m_vFreeAimTargetDir.Normalize();
}
}
#endif
CEntity* pEntityAimedAt = i->GetHitEntity();
if(pEntityAimedAt)
{
if(pEntityAimedAt->GetIsTypePed())
{
CPed* pPedAimedAt = static_cast<CPed*>(pEntityAimedAt);
//! Don't allow free aiming against anyone inside our own vehicle. Just skip.
//! Note: in 1st person, we allow this so you can't (so obviously) shoot through friendly peds.
if(!m_pPed->IsInFirstPersonVehicleCamera())
{
if(pVehicleTargeterInside && (pVehicleTargeterInside == pPedAimedAt->GetVehiclePedInside()))
{
continue;
}
}
//! If we hit ragdoll, set both.
if(pPedAimedAt->GetRagdollInst() == i->GetHitInst())
{
pFreeAimTarget = pPedAimedAt;
pFreeAimTargetRagdoll = pPedAimedAt;
}
else
{
// Otherwise, go through list (from current point) and check if we hit ragdoll too.
WorldProbe::ResultIterator j = i;
for(; j < m_FreeAimProbeResults.last_result(); ++j)
{
CEntity* pEntityAimedAtPostPed = j->GetHitEntity();
if(pEntityAimedAtPostPed)
{
if(pEntityAimedAtPostPed->GetIsTypePed())
{
CPed *pPedAimedAtPostPed = static_cast<CPed*>(pEntityAimedAtPostPed);
//! Note: if we have got this far it means that the capsule contact didn't actually hit
//! ragdoll bounds. In that case, allow us to pick any ragdoll inst.
if(pPedAimedAtPostPed->GetRagdollInst() == j->GetHitInst())
{
pFreeAimTargetRagdoll = pPedAimedAtPostPed;
pPedAimedAt = pPedAimedAtPostPed;
break;
}
else
{
//hit animated bounds, just keep going.
}
}
else if(pEntityAimedAtPostPed->GetIsTypeVehicle())
{
if(pPedAimedAt->GetMyVehicle() != pEntityAimedAtPostPed)
{
break;
}
//If we hit vehicle aimed at ped is in, keep going to find ragdoll inst we are aiming at.
}
else
{
// Stop out if subsequent hit isn't the same entity
break;
}
}
}
pFreeAimTarget = pPedAimedAt;
}
vHitPos = VEC3V_TO_VECTOR3(i->GetPosition());
break;
}
else if(pEntityAimedAt->GetIsTypeVehicle())
{
CVehicle* pVehicleAimedAt = static_cast<CVehicle*>(pEntityAimedAt);
//Note that the player aimed at the vehicle.
if(m_pPed->GetVehiclePedInside() != pVehicleAimedAt)
{
pVehicleAimedAt->GetIntelligence()->PlayerAimedAtOrAround();
}
// Check if we subsequently hit a ped
WorldProbe::ResultIterator j = i;
j++;
for(; j < m_FreeAimProbeResults.last_result(); ++j)
{
CEntity* pEntityAimedAtPostVehicle = j->GetHitEntity();
if(pEntityAimedAtPostVehicle && pVehicleAimedAt != pEntityAimedAtPostVehicle)
{
if(pEntityAimedAtPostVehicle->GetIsTypePed() && static_cast<CPed*>(pEntityAimedAtPostVehicle)->GetVehiclePedInside() == pEntityAimedAt)
{
pFreeAimTarget = pEntityAimedAtPostVehicle;
vHitPos = VEC3V_TO_VECTOR3(j->GetPosition());
break;
}
}
}
if (!pFreeAimTarget)
{
//! By default, choose vehicle.
pFreeAimTarget = pVehicleAimedAt;
vHitPos = VEC3V_TO_VECTOR3(i->GetPosition());
// See if we can override with a ped from inside vehicle.
s32 iMaxSeats = pVehicleAimedAt->GetSeatManager()->GetMaxSeats();
for(s32 iSeat = 0; iSeat < iMaxSeats; iSeat++)
{
CPed* pPedInSeat = pVehicleAimedAt->GetSeatManager()->GetPedInSeat(iSeat);
if(pPedInSeat)
{
pFreeAimTarget = pPedInSeat;
break;
}
}
}
break;
}
else if (pEntityAimedAt->GetType() == ENTITY_TYPE_OBJECT || pEntityAimedAt->GetType() == ENTITY_TYPE_BUILDING)
{
pFreeAimTarget = pEntityAimedAt;
break;
}
else
{
// Blocked
break;
}
}
}
if(vClosestFreeAimTargetPos.Mag2() > 0.01f)
{
m_vClosestFreeAimTargetPos = vClosestFreeAimTargetPos;
}
else
{
m_vClosestFreeAimTargetPos = vEnd;
}
if(pFreeAimTarget && !bRejectFreeAimResults)
{
// We are aiming at something, but its not the thing we were aiming at last frame
if(pFreeAimTarget != m_pFreeAimTarget)
{
m_pFreeAimTarget = pFreeAimTarget;
m_uStartTimeAimingAtFreeAimTarget = fwTimer::GetTimeInMilliseconds();
if(ShouldUsePullGunSpeech(&weaponInfo))
m_pPed->NewSay("PULL_GUN");
}
// Is it a ragdoll under the reticle?
m_pFreeAimTargetRagdoll = pFreeAimTargetRagdoll;
}
else
{
// Clear free aim target if none is found
vHitPos.Zero();
ClearFreeAim();
}
//NOTE: This is deliberately assigned after any potential call to ClearFreeAim() above.
m_bFreeAimTargetingVehicleForCamera = bFreeAimTargetingVehicleForCamera;
}
if(!bUseSynchronousResults && !m_FreeAimProbeResults.GetWaitingOnResults())
{
m_FreeAimProbeResults.Reset();
// Probe for a target
WorldProbe::CShapeTestProbeDesc probeDesc;
probeDesc.SetResultsStructure(&m_FreeAimProbeResults);
probeDesc.SetStartAndEnd(vStart, vEnd);
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_RAGDOLL_TYPE | ArchetypeFlags::GTA_PED_TYPE | ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_ALL_MAP_TYPES | ArchetypeFlags::GTA_OBJECT_TYPE | ArchetypeFlags::GTA_GLASS_TYPE);
probeDesc.SetExcludeEntity(m_pPed, bInLargeVehicle ? WorldProbe::EIEO_DONT_ADD_VEHICLE_OCCUPANTS : EXCLUDE_ENTITY_OPTIONS_NONE);
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
}
}
void CPlayerPedTargeting::FindFreeAimAssistTarget(const Vector3& vStartIn, const Vector3& vEndIn)
{
if (!ms_Tunables.GetEnableBulletBending())
{
return;
}
pedFatalAssertf(m_pPed, "m_pPed is NULL");
Vector3 vStart = vStartIn;
Vector3 vEnd = vEndIn;
if (!ms_Tunables.m_DoAynchronousProbesWhenFindingFreeAimAssistTarget)
{
ProcessFreeAimAssistTargetCapsuleTests(vStart, vEnd);
}
if(m_FreeAimAssistProbeResults.GetResultsReady())
{
CEntity* pClosestEntity = NULL;
float fBestScore = FLT_MAX;
Vector3 vIntersection;
#if DEBUG_DRAW
u32 i = 0;
#endif // DEBUG_DRAW
bool bNeedsTest = false;
for(WorldProbe::ResultIterator it = m_FreeAimAssistProbeResults.begin(); it < m_FreeAimAssistProbeResults.last_result(); ++it)
{
#if DEBUG_DRAW
if(ms_Tunables.m_DisplayAimAssistTest)
{
char buff[64];
formatf(buff, "DISPLAY_AIM_ASSIST_INTERSECTIONS_%d", i);
CWeaponDebug::ms_debugStore.AddSphere(RCC_VEC3V(it->GetHitPosition()), 0.05f, Color_orange, 1000, atStringHash(buff));
}
++i;
#endif // DEBUG_DRAW
bool bHitPed = false;
CEntity* pEntity = it->GetHitEntity();
if(pEntity)
{
if(pEntity->GetIsTypePed())
{
bHitPed = true;
CPed* pPed = static_cast<CPed*>(pEntity);
pedAssertf(m_pPed != pPed, "IsAHit = %s, hit inst = %p, ragdoll inst = %p, animated inst = %p",
it->IsAHit() ? "true" : "false",
it->GetHitInst(),
pPed->GetRagdollInst(),
pPed->GetAnimatedInst());
if(GetIsPedValidForFreeAimAssist(pPed))
{
float fScore = GetScoreForFreeAimAssistIntersection(vStart, vEnd, it->GetHitPosition());
if(fScore < fBestScore)
{
pClosestEntity = pEntity;
fBestScore = fScore;
vIntersection = it->GetHitPosition();
}
}
}
else
{
//! hit something that wasn't a ped 1st. Need to do a LOS check to determine if the
if(!bHitPed)
{
bNeedsTest = true;
}
}
}
}
if(bNeedsTest && pClosestEntity)
{
Vector3 vTestPos = vIntersection;
if(pClosestEntity->GetIsTypePed())
{
CPed* pPed = static_cast<CPed*>(pClosestEntity);
vTestPos.z = pPed->GetBonePositionCached(BONETAG_HEAD).z;
}
s32 iTargetOptions = 0;
if(!CPedGeometryAnalyser::CanPedTargetPoint(*m_pPed, vTestPos, iTargetOptions))
{
pClosestEntity = NULL;
}
}
#if __BANK
if(CPedTargetEvaluator::ms_Tunables.m_DebugTargetting && pClosestEntity)
{
CWeaponDebug::ms_debugStore.AddSphere(RCC_VEC3V(vIntersection), 0.05f, Color_purple, 1000, ATSTRINGHASH("FREE_AIM_ASSIST_TARGET_BEST_INTERSECTION", 0xB82FD8C0));
}
#endif // __BANK
m_vClosestFreeAimAssistTargetPos = vIntersection;
// Attempt to get a more accurate target position (If using the new slow down code, still get the more accurate assist position)
if(pClosestEntity && (GetMoreAccurateFreeAimAssistTargetPos(vStart, vEnd, vIntersection, pClosestEntity, m_vFreeAimAssistTargetPos) || ms_Tunables.GetUseNewSlowDownCode()))
{
m_pFreeAimAssistTarget = pClosestEntity;
}
else
{
// Clear the aim assist target
m_pFreeAimAssistTarget = NULL;
}
}
ProcessPedEventsForFreeAimAssist();
if (ms_Tunables.m_DoAynchronousProbesWhenFindingFreeAimAssistTarget)
{
ProcessFreeAimAssistTargetCapsuleTests(vStart, vEnd);
}
#if __BANK
if(CPedTargetEvaluator::ms_Tunables.m_DebugTargetting && m_pFreeAimAssistTarget)
{
Vector3 vAssistTargetPosition = VEC3V_TO_VECTOR3(m_pFreeAimAssistTarget->GetTransform().GetPosition());
vAssistTargetPosition.z += 1.0f;
CWeaponDebug::ms_debugStore.AddSphere(RCC_VEC3V(vAssistTargetPosition), 0.05f, Color_cyan, 1000, ATSTRINGHASH("FREE_AIM_ASSIST_TARGET", 0xD1F6DCE1));
}
#endif // __BANK
}
void CPlayerPedTargeting::ProcessFreeAimAssistTargetCapsuleTests(const Vector3& vStart, const Vector3& vEnd)
{
if(!m_FreeAimAssistProbeResults.GetWaitingOnResults())
{
m_FreeAimAssistProbeResults.Reset();
float fTestRadius = m_pPed->GetIsInVehicle() ? ms_Tunables.GetAimAssistCapsuleRadiusInVehicle() : ms_Tunables.GetAimAssistCapsuleRadius();
WorldProbe::eBlockingMode blockingMode = ms_Tunables.m_DoAynchronousProbesWhenFindingFreeAimAssistTarget ? WorldProbe::PERFORM_ASYNCHRONOUS_TEST : WorldProbe::PERFORM_SYNCHRONOUS_TEST;
// Perform a capsule check with ped's, this will give us the free aim target
WorldProbe::CShapeTestCapsuleDesc pedCapsuleDesc;
pedCapsuleDesc.SetResultsStructure(&m_FreeAimAssistProbeResults);
pedCapsuleDesc.SetCapsule(vStart, vEnd, fTestRadius);
s32 flags = ArchetypeFlags::GTA_RAGDOLL_TYPE | ArchetypeFlags::GTA_ALL_MAP_TYPES | ArchetypeFlags::GTA_OBJECT_TYPE;
// 3/28/13 - cthomas - We want to skip vehicles, so don't include ArchetypeFlags::GTA_VEHICLE_TYPE!
//! DMKH. but we do want to prevent sticky aiming to targets behind vehicles.
if(ms_Tunables.GetUseFirstPersonStickyAim() || GetAreCNCFreeAimImprovementsEnabled(m_pPed))
{
flags |= ArchetypeFlags::GTA_VEHICLE_TYPE;
}
pedCapsuleDesc.SetIncludeFlags(flags);
pedCapsuleDesc.SetExcludeEntity(m_pPed, WorldProbe::EIEO_DONT_ADD_VEHICLE_OCCUPANTS);
pedCapsuleDesc.SetIsDirected(true);
WorldProbe::GetShapeTestManager()->SubmitTest(pedCapsuleDesc, blockingMode);
#if DEBUG_DRAW
if(ms_Tunables.m_DisplayAimAssistTest)
{
float fTestRadius = m_pPed->GetIsInVehicle() ? ms_Tunables.GetAimAssistCapsuleRadiusInVehicle() : ms_Tunables.GetAimAssistCapsuleRadius();
CWeaponDebug::ms_debugStore.AddCapsule(RCC_VEC3V(vStart), RCC_VEC3V(vEnd), fTestRadius, Color_orange, 1000, ATSTRINGHASH("FreeAimAssistCapsule", 0xAAE041A3), false);
}
#endif // DEBUG_DRAW
}
}
bool CPlayerPedTargeting::GetIsAiming() const
{
return GetLockOnTarget() || m_pPed->GetPlayerInfo()->GetPlayerDataFreeAiming() || m_pPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
}
bool CPlayerPedTargeting::IsPushingStick(const float fThreshHold) const
{
const CControl* pControl = m_pPed->GetControlFromPlayer();
if (pControl)
{
Vector2 vecStickInput;
vecStickInput.x = pControl->GetPedAimWeaponLeftRight().GetUnboundNorm();
vecStickInput.y = pControl->GetPedAimWeaponUpDown().GetUnboundNorm();
if (Abs(vecStickInput.x) > fThreshHold|| Abs(vecStickInput.y) > fThreshHold)
{
return true;
}
}
return false;
}
bool CPlayerPedTargeting::IsFineAimWithinBreakLimits() const
{
Vector3 vFineAim;
GetFineAimingOffset(vFineAim);
if (Abs(vFineAim.x) < ms_Tunables.GetSoftLockFineAimBreakXYValue() &&
Abs(vFineAim.y) < ms_Tunables.GetSoftLockFineAimBreakXYValue() &&
Abs(vFineAim.z) < ms_Tunables.GetSoftLockFineAimBreakZValue())
{
return true;
}
return false;
}
bool CPlayerPedTargeting::IsFineAimWithinBreakXYLimits() const
{
Vector3 vFineAim;
GetFineAimingOffset(vFineAim);
if (Abs(vFineAim.x) < ms_Tunables.GetSoftLockFineAimBreakXYValue() &&
Abs(vFineAim.y) < ms_Tunables.GetSoftLockFineAimBreakXYValue())
{
return true;
}
return false;
}
bool CPlayerPedTargeting::IsFineAimWithinAbsoluteXYLimits(float fLimit) const
{
Vector3 vFineAim;
GetFineAimingOffset(vFineAim);
if (Abs(vFineAim.x) < fLimit &&
Abs(vFineAim.y) < fLimit)
{
return true;
}
return false;
}
bool CPlayerPedTargeting::HasDeadLockonTarget() const
{
return m_pLockOnTarget && m_pLockOnTarget->GetIsTypePed() && static_cast<CPed*>(m_pLockOnTarget.Get())->IsDead();
}
bool CPlayerPedTargeting::GetIsPedValidForFreeAimAssist(const CPed* pTargetPed) const
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
pedFatalAssertf(pTargetPed, "pTargetPed is NULL");
static bank_bool IGNORE_DEAD_OR_DYING = false;
if(pTargetPed->GetIsDeadOrDying() && !IGNORE_DEAD_OR_DYING)
{
// Ped is dead/dying = ignore
return false;
}
static bank_bool IGNORE_THREATENED_BY = true;
if(!pTargetPed->GetPedIntelligence()->IsThreatenedBy(*m_pPed) && !IGNORE_THREATENED_BY)
{
// Ped is not a threat
return false;
}
return true;
}
float CPlayerPedTargeting::GetScoreForFreeAimAssistIntersection(const Vector3& vStart, const Vector3& vEnd, const Vector3& vHitPos) const
{
// Construct a line representing the aiming vector
fwGeom::fwLine line(vStart, vEnd);
// Get the closest point to the intersection along the aiming vector
Vector3 vClosestPoint;
line.FindClosestPointOnLine(vHitPos, vClosestPoint);
// Do a distance check to the aiming vector
float fDistSq = vHitPos.Dist2(vClosestPoint);
return fDistSq;
}
bool CPlayerPedTargeting::GetMoreAccurateFreeAimAssistTargetPos(const Vector3& vStart, const Vector3& vEnd, const Vector3& vHitPos, const CEntity* pHitEntity, Vector3& vNewHitPos) const
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
//
// Attempt to find a more accurate target position
// Construct a horizontal probe from the nearest point on the aiming vector in the direction of the hit position
//
fwGeom::fwLine line(vStart, vEnd);
Vector3 vClosestPoint;
// SNAPSHOT special designer test
#if __BANK
if( ms_bEnableSnapShotBulletBend && pHitEntity && pHitEntity->GetIsTypePed() )
{
const CPlayerSpecialAbility* pSpecialAbility = m_pPed->GetSpecialAbility();
if( pSpecialAbility && pSpecialAbility->GetType() == SAT_SNAPSHOT && pSpecialAbility->IsActive() )
{
// Determine if the target head position is within snapshot radius
Vector3 vHeadPos = ((CPed*)pHitEntity)->GetBonePositionCached(BONETAG_HEAD);
float fLineDist = line.FindClosestPointOnLine(vHeadPos, vClosestPoint);
float fHeadDist = (vHeadPos - vClosestPoint).Mag();
if( fHeadDist < Lerp( fLineDist, ms_fSnapShotRadiusMin, ms_fSnapShotRadiusMax ) )
{
vNewHitPos = vHeadPos;
return true;
}
}
}
#endif
float fT = line.FindClosestPointOnLine(vHitPos, vClosestPoint);
float fRadiusAtDistance = GetFreeAimRadiusAtADistance( fT );
if( fRadiusAtDistance > 0.0f )
{
// Get the horizontal points
Vector3 vAimDir(vEnd - vStart);
vAimDir.NormalizeFast();
Vector3 vRight;
vRight.Cross(vAimDir, ZAXIS);
vRight.NormalizeFast();
Vector3 vToIntersectionPos(vHitPos - vClosestPoint);
vToIntersectionPos.NormalizeFast();
Vector3 vAimAtPos;
TUNE_GROUP_BOOL(PLAYER_TARGETING, USE_TO_INTERSECTION_VECTOR, true);
if (USE_TO_INTERSECTION_VECTOR)
{
vAimAtPos = vClosestPoint + (vToIntersectionPos * fRadiusAtDistance);
}
else
{
// Perform a dot product to work out the direction the probe should go
float fDot = vRight.Dot(vToIntersectionPos);
if(fDot > 0.0f)
{
vAimAtPos = vClosestPoint + (vRight * fRadiusAtDistance);
}
else
{
vAimAtPos = vClosestPoint - (vRight * fRadiusAtDistance);
}
}
// Line probe to determine if we should change the intended hit position
static dev_float CAPSULE_RADIUS = 0.05f;
WorldProbe::CShapeTestCapsuleDesc pedCapsuleDesc;
WorldProbe::CShapeTestHitPoint result;
WorldProbe::CShapeTestResults probeResult(result);
pedCapsuleDesc.SetResultsStructure( &probeResult );
pedCapsuleDesc.SetCapsule( vClosestPoint, vAimAtPos, CAPSULE_RADIUS );
pedCapsuleDesc.SetIsDirected( true );
pedCapsuleDesc.SetIncludeFlags( ArchetypeFlags::GTA_RAGDOLL_TYPE );
pedCapsuleDesc.SetIncludeEntity( pHitEntity );
pedCapsuleDesc.SetDomainForTest( WorldProbe::TEST_AGAINST_INDIVIDUAL_OBJECTS );
pedCapsuleDesc.SetDoInitialSphereCheck( true );
const bool bHitRagdoll = WorldProbe::GetShapeTestManager()->SubmitTest( pedCapsuleDesc );
#if __BANK
if(CPedTargetEvaluator::ms_Tunables.m_DebugTargetting)
{
//CWeaponDebug::ms_debugStore.AddCapsule(RCC_VEC3V(vClosestPoint), RCC_VEC3V(vAimAtPos), CAPSULE_RADIUS, bHitRagdoll ? Color_cyan : Color_red, 1000, ATSTRINGHASH("PLAYER_PED_TARGETING_HORZ_LINE", 0x6A664A90), false);
}
#endif // __BANK
if(bHitRagdoll)
{
vNewHitPos = probeResult[0].GetHitPosition();
return true;
}
}
// Getting here means we are outside the free aim assist influence and we should fire where we are pointing
vNewHitPos = vEnd;
return false;
}
// Returns the bullet bending radius given a distance
float CPlayerPedTargeting::GetFreeAimRadiusAtADistance( float fDistToTarget ) const
{
bool bZoomed = IsCameraInAccurateMode();
weaponAssert(m_pPed->GetWeaponManager());
const CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
if( pWeapon && pWeapon->GetWeaponInfo()->GetIsGunOrCanBeFiredLikeGun() )
{
float fNearRadius = 0.0f;
float fFarRadius = 0.0f;
GetBulletBendingData(m_pPed, pWeapon, bZoomed, fNearRadius, fFarRadius);
return fNearRadius + fDistToTarget * (fFarRadius - fNearRadius);
}
return 0.0f;
}
const CWeaponInfo* CPlayerPedTargeting::GetWeaponInfo() const
{
pedFatalAssertf(m_pPed, "m_pPed is NULL");
return GetWeaponInfo(m_pPed);
}
const CWeaponInfo* CPlayerPedTargeting::GetWeaponInfo(CPed *pPed)
{
pedFatalAssertf(pPed, "pPed is NULL");
CPedWeaponManager *pWeapMgr = pPed->GetWeaponManager();
if (pWeapMgr)
{
if(pWeapMgr->GetEquippedVehicleWeapon())
{
return pWeapMgr->GetEquippedVehicleWeapon()->GetWeaponInfo();
}
else
{
if(pWeapMgr->GetEquippedWeaponInfo() == pWeapMgr->GetSelectedWeaponInfo())
{
return pWeapMgr->GetEquippedWeaponInfo();
}
}
}
return NULL;
}
atHashWithStringNotFinal CPlayerPedTargeting::GetTargetSequenceGroup() const
{
const CWeaponInfo* pInfo = GetWeaponInfo();
if (pInfo)
{
return pInfo->GetTargetSequenceGroup();
}
return (u32)0;
}
CCurveSet* CPlayerPedTargeting::GetCurveSet(s32 iCurveType)
{
bool bFirstPerson = FPS_MODE_SUPPORTED_ONLY(CGameWorld::FindLocalPlayer() ? CGameWorld::FindLocalPlayer()->IsFirstPersonShooterModeEnabledForPlayer(false) :) false;
if (aiVerifyf(iCurveType >= 0 && iCurveType < CTargettingDifficultyInfo::CS_Max, "Invalid curve type %i", iCurveType))
{
switch(CPedTargetEvaluator::GetTargetMode(CGameWorld::FindLocalPlayer()))
{
case(CPedTargetEvaluator::TARGETING_OPTION_GTA_TRADITIONAL):
return bFirstPerson ? &ms_Tunables.m_FirstPersonEasyTargettingDifficultyInfo.m_CurveSets[iCurveType] : &ms_Tunables.m_EasyTargettingDifficultyInfo.m_CurveSets[iCurveType];
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_AIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonNormalTargettingDifficultyInfo.m_CurveSets[iCurveType] : &ms_Tunables.m_NormalTargettingDifficultyInfo.m_CurveSets[iCurveType];
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_FREEAIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonHardTargettingDifficultyInfo.m_CurveSets[iCurveType] : &ms_Tunables.m_HardTargettingDifficultyInfo.m_CurveSets[iCurveType];
case(CPedTargetEvaluator::TARGETING_OPTION_FREEAIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonExpertTargettingDifficultyInfo.m_CurveSets[iCurveType] : &ms_Tunables.m_ExpertTargettingDifficultyInfo.m_CurveSets[iCurveType];
}
}
Assert(0);
return &ms_Tunables.m_EasyTargettingDifficultyInfo.m_CurveSets[iCurveType];
}
CCurveSet* CPlayerPedTargeting::GetAimDistanceScaleCurve()
{
bool bFirstPerson = FPS_MODE_SUPPORTED_ONLY(CGameWorld::FindLocalPlayer() ? CGameWorld::FindLocalPlayer()->IsFirstPersonShooterModeEnabledForPlayer(false) :) false;
switch(CPedTargetEvaluator::GetTargetMode(CGameWorld::FindLocalPlayer()))
{
case(CPedTargetEvaluator::TARGETING_OPTION_GTA_TRADITIONAL):
return bFirstPerson ? &ms_Tunables.m_FirstPersonEasyTargettingDifficultyInfo.m_AimAssistDistanceCurve : &ms_Tunables.m_EasyTargettingDifficultyInfo.m_AimAssistDistanceCurve;
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_AIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonNormalTargettingDifficultyInfo.m_AimAssistDistanceCurve : &ms_Tunables.m_NormalTargettingDifficultyInfo.m_AimAssistDistanceCurve;
case(CPedTargetEvaluator::TARGETING_OPTION_ASSISTED_FREEAIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonHardTargettingDifficultyInfo.m_AimAssistDistanceCurve : &ms_Tunables.m_HardTargettingDifficultyInfo.m_AimAssistDistanceCurve;
case(CPedTargetEvaluator::TARGETING_OPTION_FREEAIM):
return bFirstPerson ? &ms_Tunables.m_FirstPersonExpertTargettingDifficultyInfo.m_AimAssistDistanceCurve : &ms_Tunables.m_ExpertTargettingDifficultyInfo.m_AimAssistDistanceCurve;
}
Assert(0);
return &ms_Tunables.m_EasyTargettingDifficultyInfo.m_AimAssistDistanceCurve;
}
bool CPlayerPedTargeting::ShouldUsePullGunSpeech(const CWeaponInfo* pWeaponInfo)
{
if(pWeaponInfo->GetDamageType() != DAMAGE_TYPE_BULLET && pWeaponInfo->GetDamageType() != DAMAGE_TYPE_BULLET_RUBBER)
return false;
CEntity* pTargetEntity = m_pLockOnTarget.Get();
if(pTargetEntity == NULL && m_pFreeAimTarget != NULL)
{
pTargetEntity = m_pFreeAimTarget.Get();
}
if(!pTargetEntity || ! m_pPed || pTargetEntity->GetType() != ENTITY_TYPE_PED || static_cast<CPed*>(pTargetEntity)->HasAnimalAudioParams() ||
!static_cast<CPed*>(pTargetEntity)->GetTaskData().GetIsFlagSet(CTaskFlags::CanBeTalkedTo) ||
static_cast<CPed*>(pTargetEntity)->GetIsDeadOrDying())
{
return false;
}
//Take logic from determining lock-on target, and swap the target and the player, to make sure the target is looking at us
Vector3 vTargetToPlayer = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition() - pTargetEntity->GetTransform().GetPosition());
if(vTargetToPlayer.Mag2() > 250.0f)
return false;
vTargetToPlayer.z = 0.0f;
vTargetToPlayer.Normalize();
Vector3 targetHeading = VEC3V_TO_VECTOR3(pTargetEntity->GetTransform().GetForward());
targetHeading.z = 0.0f;
targetHeading.Normalize();
const float fDot = targetHeading.Dot(vTargetToPlayer);
TUNE_GROUP_FLOAT(ONE_H_AIM, dotTolerance2, 0.85f, 0.0f, 1.0f, 0.01f);
return fDot > dotTolerance2;
}
// Get weapon lock on range.
float CPlayerPedTargeting::GetWeaponLockOnRange(const CWeaponInfo* pWeaponInfo, CEntity *pEntity) const
{
if(m_fLockOnRangeOverride >= 0.0f)
{
return m_fLockOnRangeOverride;
}
if(pWeaponInfo)
{
if(pWeaponInfo->GetIsUnarmed() && m_pPed->GetIsInCover())
{
return ms_Tunables.m_UnarmedInCoverTargetingDistance;
}
float fRejectionModifier = 1.0f;
if(CPedTargetEvaluator::CanBeTargetedInRejectionDistance(m_pPed, pWeaponInfo, pEntity))
{
fRejectionModifier = ms_Tunables.GetLockOnDistanceRejectionModifier();
}
return pWeaponInfo->GetLockOnRange() * fRejectionModifier;
}
return 0.0f;
}
bool CPlayerPedTargeting::IsMeleeTargetingUsingNormalLock() const
{
return m_pPed->GetIsInCover();
}
bool CPlayerPedTargeting::CanUseNoReticuleAimAssist() const
{
bool bRunAndGun = m_pPed->GetPlayerInfo()->IsRunAndGunning();
#if FPS_MODE_SUPPORTED
if( m_pPed->IsFirstPersonShooterModeEnabledForPlayer(false) )
{
return false;
}
#endif
if(!m_bNoReticuleAimAssist && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun))
{
return false;
}
const camGameplayDirector& gameplayDirector = camInterface::GetGameplayDirector();
if(!gameplayDirector.ShouldDisplayReticule() &&
!m_bBlockNoReticuleAimAssist &&
bRunAndGun &&
(m_pLockOnTarget == NULL) &&
(GetWeaponInfo() && GetWeaponInfo()->GetIsGun()) &&
!NetworkInterface::IsGameInProgress())
{
return true;
}
return false;
}
bool CPlayerPedTargeting::IsCameraInAccurateMode()
{
camThirdPersonAimCamera* pAimCam = camInterface::GetGameplayDirector().GetThirdPersonAimCamera();
if(pAimCam)
{
return pAimCam->IsInAccurateMode();
}
#if FPS_MODE_SUPPORTED
else
{
camFirstPersonShooterCamera* pFirstPersonShooterCam = camInterface::GetGameplayDirector().GetFirstPersonShooterCamera();
if(pFirstPersonShooterCam)
{
return pFirstPersonShooterCam->IsScopeAiming();
}
}
#endif
return false;
}
float CPlayerPedTargeting::ComputeExtraZoomFactorForAccurateMode(const CWeaponInfo& weaponInfo)
{
float extraZoomFactor = 1.0f;
if(CGameWorld::FindLocalPlayer())
{
//Check if this weapon has a scope component in the attach ped's inventory.
const CPed* attachPed = CGameWorld::FindLocalPlayer();
const CPedInventory* inventory = attachPed->GetInventory();
if(inventory)
{
const CWeaponItem* weaponItem = inventory->GetWeapon(weaponInfo.GetHash());
if(weaponItem)
{
if(weaponItem->GetComponents())
{
const CWeaponItem::Components& components = *weaponItem->GetComponents();
for(s32 i=0; i<components.GetCount(); i++)
{
const CWeaponComponentInfo* componentInfo = components[i].pComponentInfo;
if(componentInfo && (componentInfo->GetClassId() == WCT_Scope))
{
extraZoomFactor = static_cast<const CWeaponComponentScopeInfo*>(componentInfo)->
GetExtraZoomFactorForAccurateMode();
break;
}
}
}
}
}
}
return extraZoomFactor;
}
void CPlayerPedTargeting::GetBulletBendingData(const CPed *pPed, const CWeapon* pWeapon, bool bZoomed, float &fNear, float &fFar)
{
const bool bUsingCNCFreeAimImprovements = GetAreCNCFreeAimImprovementsEnabled(pPed, true);
// CNC Free Aim: use vehicle velocity bullet bending modifiers.
if(pPed->IsInFirstPersonVehicleCamera() || (bUsingCNCFreeAimImprovements && pPed->GetIsInVehicle()))
{
float fSpeed = 0.0f;
CVehicle *pVehicle = pPed->GetVehiclePedInside();
if(pVehicle)
{
fSpeed = pVehicle->GetVelocity().Mag();
}
float fSpeedScale = Min( fSpeed / ms_Tunables.GetInVehicleBulletBendingMaxVelocity(), 1.0f);
fNear = Lerp(fSpeedScale, ms_Tunables.GetInVehicleBulletBendingNearMinVelocity(), ms_Tunables.GetInVehicleBulletBendingNearMaxVelocity());
fFar = Lerp(fSpeedScale, ms_Tunables.GetInVehicleBulletBendingFarMinVelocity(), ms_Tunables.GetInVehicleBulletBendingFarMaxVelocity());
}
else if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
{
fNear = pWeapon->GetWeaponInfo()->GetFirstPersonBulletBendingNearRadius() * ms_Tunables.GetBulletBendingNearMultiplier();
fFar = bZoomed ? pWeapon->GetWeaponInfo()->GetFirstPersonBulletBendingZoomedRadius() * ms_Tunables.GetBulletBendingZoomMultiplier()
: pWeapon->GetWeaponInfo()->GetFirstPersonBulletBendingFarRadius() * ms_Tunables.GetBulletBendingFarMultiplier();
}
else
{
fNear = pWeapon->GetWeaponInfo()->GetBulletBendingNearRadius() * ms_Tunables.GetBulletBendingNearMultiplier();
fFar = bZoomed ? pWeapon->GetWeaponInfo()->GetBulletBendingZoomedRadius() * ms_Tunables.GetBulletBendingZoomMultiplier()
: pWeapon->GetWeaponInfo()->GetBulletBendingFarRadius() * ms_Tunables.GetBulletBendingFarMultiplier();
}
// CNC Free Aim: apply extra bullet bending modifier.
// (Don't apply modifier if player/target are not moving fast enough)
if (pPed && GetAreCNCFreeAimImprovementsEnabled(pPed))
{
const CPlayerInfo* pPlayerInfo = pPed->GetPlayerInfo();
if (pPlayerInfo)
{
const CPlayerPedTargeting& rTargeting = pPlayerInfo->GetTargeting();
const CEntity* pFreeAimAssistTargetEntity = rTargeting.GetFreeAimAssistTarget();
if (pFreeAimAssistTargetEntity && pFreeAimAssistTargetEntity->GetIsTypePed())
{
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimBulletBendingModifierMinVelocity, 0.075f, 0.0f, 50.0f, 0.01f);
const CPed* pFreeAimAssistTargetPed = static_cast<const CPed*>(pFreeAimAssistTargetEntity);
float fTargetVelocity = 0.0f;
if (pFreeAimAssistTargetPed->GetIsInVehicle())
{
const CVehicle* pFreeAimAssistTargetVehicle = pFreeAimAssistTargetPed->GetVehiclePedInside();
fTargetVelocity = pFreeAimAssistTargetVehicle->GetVelocity().Mag();
}
else
{
fTargetVelocity = Mag(Subtract(VECTOR3_TO_VEC3V(pFreeAimAssistTargetPed->GetVelocity()), pFreeAimAssistTargetPed->GetGroundVelocity())).Getf();
}
float fPlayerVelocity = 0.0f;
if (pPed->GetIsInVehicle())
{
const CVehicle* pVehicle = pPed->GetVehiclePedInside();
fPlayerVelocity = pVehicle->GetVelocity().Mag();
}
else
{
fPlayerVelocity = Mag(Subtract(VECTOR3_TO_VEC3V(pPed->GetVelocity()), pPed->GetGroundVelocity())).Getf();
}
const float fRelativeVelocity = abs(fTargetVelocity - fPlayerVelocity);
if (fTargetVelocity > fCNCFreeAimBulletBendingModifierMinVelocity ||
fPlayerVelocity > fCNCFreeAimBulletBendingModifierMinVelocity ||
fRelativeVelocity > fCNCFreeAimBulletBendingModifierMinVelocity)
{
// Using a lower value for in vehicles as bullet bending radius is already larger and scaled by velocity in the above code.
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierOnFoot, 2.2f, 1.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierInVehicle, 1.65f, 1.0f, 10.0f, 0.01f);
float fCNCFreeAimExtraBulletBendingModifier = pPed->GetIsInVehicle() ? fCNCFreeAimExtraBulletBendingModifierInVehicle : fCNCFreeAimExtraBulletBendingModifierOnFoot;
// Cop/Crook-specific modifiers.
if (pPlayerInfo->GetArcadeInformation().GetTeam() == eArcadeTeam::AT_CNC_COP)
{
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierOnFoot_COP, 2.2f, 1.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierInVehicle_COP, 1.65f, 1.0f, 10.0f, 0.01f);
fCNCFreeAimExtraBulletBendingModifier = pPed->GetIsInVehicle() ? fCNCFreeAimExtraBulletBendingModifierInVehicle_COP : fCNCFreeAimExtraBulletBendingModifierOnFoot_COP;
}
else if (pPlayerInfo->GetArcadeInformation().GetTeam() == eArcadeTeam::AT_CNC_CROOK)
{
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierOnFoot_CROOK, 1.925f, 1.0f, 10.0f, 0.01f);
TUNE_GROUP_FLOAT(PLAYER_TARGETING, fCNCFreeAimExtraBulletBendingModifierInVehicle_CROOK, 1.375f, 1.0f, 10.0f, 0.01f);
fCNCFreeAimExtraBulletBendingModifier = pPed->GetIsInVehicle() ? fCNCFreeAimExtraBulletBendingModifierInVehicle_CROOK : fCNCFreeAimExtraBulletBendingModifierOnFoot_CROOK;
}
fNear *= fCNCFreeAimExtraBulletBendingModifier;
fFar *= fCNCFreeAimExtraBulletBendingModifier;
}
}
}
}
}