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

2844 lines
87 KiB
C++

#include "Task/Movement/TaskJetpack.h"
#include "Peds/Ped.h"
#include "grcore/debugdraw.h"
#include "Task/Vehicle/TaskMountAnimalWeapon.h"
#include "camera/CamInterface.h"
#include "camera/base/BaseCamera.h"
#include "camera/gameplay/follow/FollowPedCamera.h"
#include "task/Motion/TaskMotionBase.h"
#include "Task/System/MotionTaskData.h"
#include "Task/Movement/TaskFall.h"
#include "system/control.h"
#include "Vehicles/Metadata/VehicleMetadataManager.h"
#include "Task/Vehicle/TaskVehicleWeapon.h"
#include "camera/gameplay/GameplayDirector.h"
#include "Peds/PedIntelligence.h"
#include "Task/Movement/JetpackObject.h"
#include "Task/Movement/Jumping/TaskJump.h"
#include "Task/Vehicle/TaskCar.h"
#include "AI/Ambient/ConditionalAnimManager.h"
#include "Vfx/Systems/VfxGadget.h"
#include "scene\world\GameWorldHeightMap.h"
#include "Pickups/Pickup.h"
#include "vehicleAi/VehicleNavigationHelper.h"
AI_OPTIMISATIONS()
#if ENABLE_JETPACK
CTaskJetpack::Tunables CTaskJetpack::sm_Tunables;
IMPLEMENT_MOVEMENT_TASK_TUNABLES(CTaskJetpack, 0xaf8a8260);
#if __BANK
bool CTaskJetpack::ms_bDebugControls = false;
bool CTaskJetpack::ms_bDebugAngles = false;
bool CTaskJetpack::ms_bDebugAI = false;
Vector3 CTaskJetpack::ms_vTargetPositionGoTo(-3.5f, 74.0f, 30.0f);
Vector3 CTaskJetpack::ms_vTargetPositionShoot(20.0f, 74.0f, 30.0f);
bool CTaskJetpack::ms_bGoToCoords(true);
bool CTaskJetpack::ms_bShootAtCoords(true);
float CTaskJetpack::ms_fAbortRange = 0.0f;
float CTaskJetpack::ms_fProbeHeight = 50.0f;
Vector3 CTaskJetpack::ms_vTargetLocationTestStart(0.0f, 0.0f, 0.0f);
Vector3 CTaskJetpack::ms_vTargetLocationTestEnd(0.0f, 0.0f, 0.0f);
Vector3 CTaskJetpack::ms_vHeightmapSamplePosition(0.0f, 0.0f ,0.0f);
Vector3 CTaskJetpack::ms_vJetpackTestPoint1(-3.5f, 74.0f, 20.0f);
Vector3 CTaskJetpack::ms_vJetpackTestPoint2(-47.0f, 203.0f, 15.0f);
Vector3 CTaskJetpack::ms_vJetpackTestPoint3(-9.0f, 143.0f, 15.0f);
float CTaskJetpack::ms_fMinHeightAboveSurface(20.0f);
bool CTaskJetpack::ms_bDisableThrustAtDestination = false;
bool CTaskJetpack::ms_bUseRandomTimerWhenClose = false;
#endif
// FLIGHT MODE
CFlightModelHelper CTaskJetpack::ms_FlightModeHelper(
0.0f, // Thrust
0.03f, // Yaw rate
0.01f, // Pitch rate
0.01f, // Roll rate
0.01f, // Yaw stabilise
0.01f, // Pitch stabilise
0.01f, // Roll stabilise
0.001f, // Form lift mult
0.015f, // Attack lift mult
0.015f, // Attack dive mult
0.0f, // Side slip mult
0.0f, // throttle fall off,
Vector3(0.0f,0.0f,0.0f), // ConstV
Vector3(0.0f,0.0f,0.0f), // LinearV
Vector3(0.0f,0.0f,0.0f), // SquaredV
Vector3(1.0f,1.0f,1.0f), // ConstAV
Vector3(1.0f,1.0f,1.0f), // LinearAV
Vector3(0.0f,1.0f,0.0f) // SquaredAV
);
const fwMvRequestId CTaskJetpack::ms_Hover("Hover",0x9578b4fe);
const fwMvRequestId CTaskJetpack::ms_Flight("Flight",0x2570a839);
const fwMvBooleanId CTaskJetpack::ms_HoverOnEnter("Hover_OnEnter",0xa219885a);
const fwMvBooleanId CTaskJetpack::ms_FlightOnEnter("Flight_OnEnter",0xfa892f70);
const fwMvStateId CTaskJetpack::ms_HoverState("HoverState",0x19e57ec3);
const fwMvStateId CTaskJetpack::ms_FlightState("FlightState",0xfeb33318);
const fwMvFloatId CTaskJetpack::ms_PitchId("Pitch",0x3f4bb8cc);
const fwMvFloatId CTaskJetpack::ms_RollId("Roll",0xcb2448c6);
const fwMvFloatId CTaskJetpack::ms_YawId("Yaw",0xfdffbe30);
const fwMvFloatId CTaskJetpack::ms_PitchAngleId("PitchAngle",0x809a226d);
const fwMvFloatId CTaskJetpack::ms_RollAngleId("RollAngle",0x94fc4d71);
const fwMvFloatId CTaskJetpack::ms_YawAngleId("YawAngle",0x15de02b1);
const fwMvFloatId CTaskJetpack::ms_AngVelocityXId("AngVelocityX",0x07eef1c4);
const fwMvFloatId CTaskJetpack::ms_AngVelocityYId("AngVelocityY",0x32a9c739);
const fwMvFloatId CTaskJetpack::ms_AngVelocityZId("AngVelocityZ",0x2463aaad);
const fwMvFloatId CTaskJetpack::ms_ThrusterLeftId("ThrusterLeft",0x121a5976);
const fwMvFloatId CTaskJetpack::ms_ThrusterRightId("ThrusterRight",0x4bd3baa2);
const fwMvFloatId CTaskJetpack::ms_SpeedId("Speed",0xf997622b);
const fwMvFloatId CTaskJetpack::ms_HoverModeBoostId("HoverModeBoost",0x22993062);
const fwMvFloatId CTaskJetpack::ms_CombinedThrustId("CombinedThrust",0x0963dcd6);
CTaskJetpack::CTaskJetpack(fwJetpackFlags flags)
: m_iFlags(flags)
, m_fThrusterLeft(0.0f)
, m_fThrusterRight(0.0f)
, m_fThrusterLeftInput(0.0f)
, m_fThrusterRightInput(0.0f)
, m_fPitch(0.0f)
, m_fYaw(0.0f)
, m_fRoll(0.0f)
, m_fNearGroundTime(0.0f)
, m_bCrashLanded(false)
, m_fHoverModeBoost(0.0f)
, m_fHolsterTimer(0.0f)
, m_vAngularVelocity(Vector3::ZeroType)
, m_vAvoidTargetPosition(Vector3::ZeroType)
, m_vTargetPositionGoTo(V_ZERO)
, m_vTargetPositionShootAt(V_ZERO)
, m_vCachedTargetPositionGoToDelay(V_ZERO)
, m_pTargetEntityGoTo(NULL)
, m_pTargetEntityShootAt(NULL)
, m_bDisableThrustAtDestination(false)
, m_bIsAiming(false)
, m_bRestartShootingTask(false)
, m_bTerminateDrivebyTask(false)
, m_bArrivedAtDestination(false)
, m_bStopRotationToTarget(false)
, m_bIsAvoidingEntity(false)
, m_rotateForAvoid(false)
, m_fAbortRange(0.0f)
, m_iFrequencyPercentage(0)
, m_iFiringPatternHash(0)
, m_fMinHeightAboveSurface(0.0f)
, m_fProbeHeight(50.0f)
, m_fHitObstacleDistance(-1.0f)
, m_bUseRandomTimerWhenClose(false)
, m_bFlyingPedAvoidance(true)
, m_fMovementDelayTime(-1.0f)
, m_fMovementTimer(-1.0f)
{
m_targetLocationTestResults = rage_new WorldProbe::CShapeTestSingleResult[NUM_VERTICAL_CAPSULE_TESTS];
SetInternalTaskType(CTaskTypes::TASK_JETPACK);
}
CTaskJetpack::~CTaskJetpack()
{
delete[] m_targetLocationTestResults;
}
void CTaskJetpack::CleanUp()
{
CPed* pPed = GetPed();
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir, false);
pPed->SetPedResetFlag(CPED_RESET_FLAG_IsUsingJetpack, false);
pPed->GetMovePed().ClearTaskNetwork(m_networkHelper, SLOW_BLEND_DURATION);
m_networkHelper.ReleaseNetworkPlayer();
//we're done flying
CFlyingPedCollector::RemovePed(RegdPed(pPed));
}
bool CTaskJetpack::ShouldStickToFloor() const
{
//Check the state.
switch(GetState())
{
case State_Land:
{
return true;
}
default:
break;
}
return false;
}
bool CTaskJetpack::ShouldUseAiControls() const
{
//Ensure the target is valid.
if(!m_iFlags.IsFlagSet(JPF_HasTarget) && !m_iFlags.IsFlagSet(JPF_HasShootAtTarget))
{
return false;
}
//Grab the ped.
const CPed* pPed = GetPed();
//Check if the ped is not a player.
if(!pPed->IsPlayer())
{
return true;
}
else
{
return false;
}
}
bool CTaskJetpack::ShouldUsePlayerControls() const
{
//Grab the ped.
const CPed* pPed = GetPed();
//Ensure the ped is a local player.
if(!pPed->IsLocalPlayer())
{
return false;
}
return true;
}
void CTaskJetpack::SetTargetGoTo(const CPhysical* pEntity, Vec3V_In vPosition)
{
//Reset arrived flag if position is different
if (!IsEqualAll(vPosition, m_vTargetPositionGoTo))
{
m_bArrivedAtDestination = false;
}
//Assign the entity.
m_pTargetEntityGoTo = pEntity;
//Assign the position.
m_vTargetPositionGoTo = vPosition;
//Check if the target is valid.
bool bHasTarget = false;
if(m_pTargetEntityGoTo)
{
bHasTarget = true;
}
else if(!IsEqualAll(m_vTargetPositionGoTo, Vec3V(V_ZERO)))
{
bHasTarget = true;
}
//Change the flag.
m_iFlags.ChangeFlag(JPF_HasTarget, bHasTarget);
}
void CTaskJetpack::SetTargetEntityGoTo(const CPhysical* pEntity)
{
SetTargetGoTo(pEntity, Vec3V(V_ZERO));
}
void CTaskJetpack::SetTargetPositionGoTo(Vec3V_In vPosition)
{
SetTargetGoTo(NULL, vPosition);
Vector3 vJetpackPos = VEC3V_TO_VECTOR3(GetPed()->GetTransform().GetPosition());
Vector3 vStartPos(vJetpackPos.x, vJetpackPos.y, vJetpackPos.z + m_fMinHeightAboveSurface);
Vector3 vEndPos(vJetpackPos.x, vJetpackPos.y, vJetpackPos.z - m_fMinHeightAboveSurface);
WorldProbe::CShapeTestHitPoint hitPoint;
WorldProbe::CShapeTestProbeDesc probeDesc;
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES|ArchetypeFlags::GTA_RIVER_TYPE);
probeDesc.SetExcludeInstance(GetPed()->GetCurrentPhysicsInst());
probeDesc.SetResultsStructure(&m_startLocationTestResults);
probeDesc.SetMaxNumResultsToUse(1);
probeDesc.SetStartAndEnd(vStartPos, vEndPos);
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
}
void CTaskJetpack::SetTargetShootAt(const CPhysical* pEntity, Vec3V_In vPosition)
{
//Assign the entity.
m_pTargetEntityShootAt = pEntity;
//Assign the position.
m_vTargetPositionShootAt = vPosition;
//Check if the target is valid.
bool bHasTarget = false;
if(m_pTargetEntityShootAt)
{
bHasTarget = true;
}
else if(!IsEqualAll(m_vTargetPositionShootAt, Vec3V(V_ZERO)))
{
bHasTarget = true;
}
//Change the flag.
m_iFlags.ChangeFlag(JPF_HasShootAtTarget, bHasTarget);
}
void CTaskJetpack::SetTargetEntityShootAt(const CPhysical* pEntity)
{
SetTargetShootAt(pEntity, Vec3V(V_ZERO));
}
void CTaskJetpack::SetTargetPositionShootAt(Vec3V_In vPosition)
{
SetTargetShootAt(NULL, vPosition);
}
void CTaskJetpack::SetGoToParametersForAI(CPhysical *pTargetEntity, Vector3 vTargetPosition, float fMinHeightAboveSurface, bool bDisableThrustAtDestination, bool bUseRandomTimerWhenClose, float fProbeHeight)
{
m_fMinHeightAboveSurface = fMinHeightAboveSurface;
m_bDisableThrustAtDestination = bDisableThrustAtDestination;
m_fProbeHeight = fProbeHeight;
m_bUseRandomTimerWhenClose = bUseRandomTimerWhenClose;
SetTargetGoTo(pTargetEntity, VECTOR3_TO_VEC3V(vTargetPosition));
}
void CTaskJetpack::SetShootAtParametersForAI(CPhysical *pTargetEntity, Vector3 vTargetPos, float fAbortRange, int iFrequencyPercentage, int iFiringPatternHash)
{
SetTargetShootAt(pTargetEntity, VECTOR3_TO_VEC3V(vTargetPos));
m_bIsAiming = true;
m_fAbortRange = fAbortRange;
m_iFrequencyPercentage = iFrequencyPercentage;
m_iFiringPatternHash = iFiringPatternHash;
m_bRestartShootingTask = true;
}
Vec3V_Out CTaskJetpack::CalculateTargetGoToPosition()
{
Vec3V vTargetPos(V_ZERO);
//Check if the target entity is valid.
if(m_pTargetEntityGoTo)
{
vTargetPos = m_pTargetEntityGoTo->GetTransform().GetPosition();
//Transform the offset and add it to the target position (if we have one)
if (!IsEqualAll(m_vTargetPositionGoTo, Vec3V(V_ZERO)))
{
vTargetPos += m_pTargetEntityGoTo->GetTransform().Transform3x3(m_vTargetPositionGoTo);
}
}
else
{
//Use the target position.
vTargetPos = m_vTargetPositionGoTo;
}
#if __BANK
if (ms_bDebugAI)
{
grcDebugDraw::Sphere(vTargetPos, 0.2f, Color_blue, true, -1);
if( m_bIsAvoidingEntity)
{
grcDebugDraw::Sphere(m_vAvoidTargetPosition, 0.5f, Color_red, false, -1);
}
}
#endif
if (m_bUseRandomTimerWhenClose)
{
// Use a small random delay if the AI ped is relatively close to the target entity and the position moves
// before allowing the AI to move as well
Vec3V vPedPos = GetPed()->GetTransform().GetPosition();
Vec3V vPedToTarget = Subtract(vTargetPos, vPedPos);
float fDistToTarget = VEC3V_TO_VECTOR3(vPedToTarget).Mag2();
static dev_float fDistanceThreshold = 400.0f;
bool bAllowMovement = true;
if (fDistToTarget <= fDistanceThreshold)
{
bAllowMovement = false;
if (m_fMovementDelayTime == -1.0f)
{
static dev_float fMinTime = 4.0f;
static dev_float fMaxTime = 10.0f;
m_fMovementDelayTime = fwRandom::GetRandomNumberInRange(fMinTime, fMaxTime);
m_vCachedTargetPositionGoToDelay = vTargetPos;
m_fMovementTimer = 0.0f;
}
if (m_fMovementDelayTime != -1.0f)
{
m_fMovementTimer += fwTimer::GetTimeStep();
if (m_fMovementTimer >= m_fMovementDelayTime)
{
bAllowMovement = true;
m_fMovementDelayTime = -1.0f;
m_fMovementTimer = -1.0f;
}
}
}
else
{
m_vCachedTargetPositionGoToDelay = vTargetPos;
m_fMovementDelayTime = -1.0f;
m_fMovementTimer = -1.0f;
}
if (!bAllowMovement)
{
vTargetPos = m_vCachedTargetPositionGoToDelay;
}
#if __BANK
if (ms_bDebugAI)
{
Color32 sphereColor = Color_blue;
if (fDistToTarget <= fDistanceThreshold)
{
sphereColor = Color_yellow;
}
grcDebugDraw::Sphere(vTargetPos, 0.75f, sphereColor, false, -1);
}
#endif
}
return vTargetPos;
}
Vec3V_Out CTaskJetpack::CalculateTargetShootAtPosition() const
{
//Check if the target entity is valid.
if(m_pTargetEntityShootAt)
{
Vec3V vTargetPos = m_pTargetEntityShootAt->GetTransform().GetPosition();
//Transform the offset and add it to the target position (if we have one)
if (!IsEqualAll(m_vTargetPositionShootAt, Vec3V(V_ZERO)))
{
vTargetPos += m_pTargetEntityShootAt->GetTransform().Transform3x3(m_vTargetPositionShootAt);
}
return vTargetPos;
}
else
{
//Use the target position.
return m_vTargetPositionShootAt;
}
}
Vec3V_Out CTaskJetpack::CalculateTargetPositionFuture()
{
//Calculate the target position.
Vec3V vTargetPosition = CalculateTargetGoToPosition();
//Ensure the target entity is valid.
if(!m_pTargetEntityGoTo)
{
return vTargetPosition;
}
//Ensure we are hovering.
if(GetState() != State_Hover)
{
return vTargetPosition;
}
//Ensure the jetpack is valid.
if(!GetJetpack()->GetObject())
{
return vTargetPosition;
}
//Grab the ped.
const CPed* pPed = GetPed();
//Grab the ped position.
Vec3V vPedPosition = pPed->GetTransform().GetPosition();
//Generate a vector from the ped to the target.
Vec3V vPedToTarget = Subtract(vTargetPosition, vPedPosition);
//Ensure the ped is above the target.
ScalarV scOffsetZ = vPedToTarget.GetZ();
if(IsGreaterThanAll(scOffsetZ, ScalarV(V_ZERO)))
{
return vTargetPosition;
}
//Grab the target velocity.
Vec3V vTargetVelocity = VECTOR3_TO_VEC3V(m_pTargetEntityGoTo->GetVelocity());
//Grab the jetpack velocity.
Vec3V vJetpackVelocity = VECTOR3_TO_VEC3V(pPed->GetVelocity());
//Ensure the ped will reach the target eventually.
ScalarV scVelocityZ = Subtract(vJetpackVelocity.GetZ(), vTargetVelocity.GetZ());
if(IsGreaterThanAll(scVelocityZ, ScalarV(V_ZERO)))
{
return vTargetPosition;
}
//Ensure the velocity difference is valid.
scVelocityZ = Min(ScalarV(V_NEGONE), scVelocityZ);
//Figure out how long it will take the ped to descend to the target height.
ScalarV scTime = Scale(scOffsetZ, Invert(scVelocityZ));
if(IsLessThanAll(scTime, ScalarV(V_ZERO)))
{
return vTargetPosition;
}
//Clamp the time.
scTime = Clamp(scTime, ScalarV(V_ZERO), ScalarV(3.0f));//ScalarV(sm_Tunables.m_MaxTimeToLookAheadForFutureTargetPosition));
//Calculate the future target position.
Vec3V vFutureTargetPosition = AddScaled(vTargetPosition, vTargetVelocity, scTime);
return vFutureTargetPosition;
}
bool CTaskJetpack::CalcDesiredVelocity(const Matrix34& UNUSED_PARAM(mUpdatedPed), float UNUSED_PARAM(fTimeStep), Vector3& UNUSED_PARAM(vDesiredVelocity)) const
{
//! TO DO - override velocity if necessary.
return false;
}
void CTaskJetpack::GetPitchConstraintLimits(float& UNUSED_PARAM(fMinOut), float& UNUSED_PARAM(fMaxOut)) const
{
//! not used right now.
}
bool CTaskJetpack::ProcessPhysics(float fTimeStep, int UNUSED_PARAM(nTimeSlice))
{
CPed *pPed = GetPed();
if(GetState() == State_Hover)
{
ProcessHoverPhysics(pPed, fTimeStep);
}
else if(GetState() == State_Flight)
{
ProcessFlightPhysics(pPed);
}
return true;
}
void CTaskJetpack::ProcessPreRender2()
{
CPed* pPed = GetPed();
CObject* pJetPackObject = static_cast<CObject*>(GetJetpack()->GetObject());
if (pPed && pJetPackObject)
{
s32 nozzleBoneIndex = -1;
if (pJetPackObject->GetSkeletonData().ConvertBoneIdToIndex((u16)Nozzle_Up_R, nozzleBoneIndex))
{
g_vfxGadget.UpdatePtFxJetPackNozzle(pPed, pJetPackObject, nozzleBoneIndex, m_fThrusterLeft, m_fThrusterRight, 0);
}
if (pJetPackObject->GetSkeletonData().ConvertBoneIdToIndex((u16)Nozzle_Up_L, nozzleBoneIndex))
{
g_vfxGadget.UpdatePtFxJetPackNozzle(pPed, pJetPackObject, nozzleBoneIndex, m_fThrusterLeft, m_fThrusterRight, 1);
}
if (pJetPackObject->GetSkeletonData().ConvertBoneIdToIndex((u16)Nozzle_Lo_R, nozzleBoneIndex))
{
g_vfxGadget.UpdatePtFxJetPackNozzle(pPed, pJetPackObject, nozzleBoneIndex, m_fThrusterLeft, m_fThrusterRight, 2);
}
if (pJetPackObject->GetSkeletonData().ConvertBoneIdToIndex((u16)Nozzle_Lo_L, nozzleBoneIndex))
{
g_vfxGadget.UpdatePtFxJetPackNozzle(pPed, pJetPackObject, nozzleBoneIndex, m_fThrusterLeft, m_fThrusterRight, 3);
}
}
}
float CalcAccel(float fCurrentVel, float fDesiredVel, float fControlMult, float fMaxAccel)
{
// Velocity diff
float fTemp = fDesiredVel - fCurrentVel;
// Accel is proportional to this diff
fTemp *= fControlMult;
// Clamp Accel
fTemp = rage::Clamp(fTemp, -fMaxAccel, fMaxAccel);
return fTemp;
}
void CTaskJetpack::ProcessHoverPhysics(CPed *pPed, float fTimeStep)
{
Vector3 vAccel = VEC3_ZERO;
Vector3 vInput(m_fRoll, m_fPitch, 0.0f);
vInput.z = m_fThrusterRight - m_fThrusterLeft;
Vector3 vDesiredLocalSpeed = vInput;
float fThrustSpeed = GetUpThrust();
float fThrustMag = fThrustSpeed;
if(sm_Tunables.m_HoverControlParams.m_bAntiGravityThrusters)
{
fThrustMag = Abs(m_fThrusterRight - m_fThrusterLeft);
}
Vector3 vMinStrafeSpeed = sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMinThrust + ((sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMaxThrust - sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMinThrust) * fThrustMag);
Vector3 vMaxStrafeSpeed = sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMinThrust + ((sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMaxThrust - sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMinThrust) * fThrustMag);
if(m_fHoverModeBoost > 0.0f)
{
Vector3 vMinStrafeSpeedBoost = sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMinThrustBoost + ((sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMaxThrustBoost - sm_Tunables.m_HoverControlParams.m_vMinStrafeSpeedMinThrustBoost) * m_fHoverModeBoost);
Vector3 vMaxStrafeSpeedBoost = sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMinThrustBoost + ((sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMaxThrustBoost - sm_Tunables.m_HoverControlParams.m_vMaxStrafeSpeedMinThrustBoost) * m_fHoverModeBoost);
vMinStrafeSpeed.Lerp(m_fHoverModeBoost, vMinStrafeSpeed, vMinStrafeSpeedBoost);
vMaxStrafeSpeed.Lerp(m_fHoverModeBoost, vMaxStrafeSpeed, vMaxStrafeSpeedBoost);
}
vDesiredLocalSpeed.x *= vDesiredLocalSpeed.x >= 0.0f ? vMaxStrafeSpeed.x : vMinStrafeSpeed.x;
vDesiredLocalSpeed.y *= vDesiredLocalSpeed.y >= 0.0f ? vMaxStrafeSpeed.y : vMinStrafeSpeed.y;
vDesiredLocalSpeed.z *= vDesiredLocalSpeed.z >= 0.0f ? vMaxStrafeSpeed.z : vMinStrafeSpeed.z;
Vector3 vCurrentLocalSpeed = pPed->GetVelocity();
vCurrentLocalSpeed = VEC3V_TO_VECTOR3(pPed->GetTransform().UnTransform3x3(VECTOR3_TO_VEC3V(vCurrentLocalSpeed)));
Vector3 vAccelLimit;
vAccelLimit.x = vDesiredLocalSpeed.x - vCurrentLocalSpeed.x >= 0.0f ? sm_Tunables.m_HoverControlParams.m_vMaxStrafeAccel.x : sm_Tunables.m_HoverControlParams.m_vMinStrafeAccel.x;
vAccelLimit.y = vDesiredLocalSpeed.y - vCurrentLocalSpeed.y >= 0.0f ? sm_Tunables.m_HoverControlParams.m_vMaxStrafeAccel.y : sm_Tunables.m_HoverControlParams.m_vMinStrafeAccel.y;
vAccelLimit.z = vDesiredLocalSpeed.z - vCurrentLocalSpeed.z >= 0.0f ? sm_Tunables.m_HoverControlParams.m_vMaxStrafeAccel.z : sm_Tunables.m_HoverControlParams.m_vMinStrafeAccel.z;
vAccel.x = CalcAccel(vCurrentLocalSpeed.x,vDesiredLocalSpeed.x,sm_Tunables.m_HoverControlParams.m_fHorThrustModifier,vAccelLimit.x);
vAccel.y = CalcAccel(vCurrentLocalSpeed.y,vDesiredLocalSpeed.y,sm_Tunables.m_HoverControlParams.m_fForThrustModifier,vAccelLimit.y);
vAccel.z = CalcAccel(vCurrentLocalSpeed.z,vDesiredLocalSpeed.z,sm_Tunables.m_HoverControlParams.m_fVerThrustModifier,vAccelLimit.z);
//! Vertical acceleration (assume z is always up).
if(sm_Tunables.m_HoverControlParams.m_bAntiGravityThrusters)
{
float fGrav = -CPhysics::GetGravitationalAcceleration();
vAccel.z -= (fGrav * sm_Tunables.m_HoverControlParams.m_fGravityDamping);
}
else
{
vAccel.z = fThrustSpeed;
vAccel.z *= sm_Tunables.m_HoverControlParams.m_fVerThrustModifier;
//! If standing, reset accel so that we don't cause ped to jitter about.
if(pPed->GetIsStanding() && vAccel.z <= 0.0f)
{
vAccel.z = 0.0f;
}
else
{
float fThrustSpeedInput = m_fThrusterLeftInput + m_fThrusterRightInput;
//! If accelerating upwards, but not providing thrust, attempt to more aggressively pull jetpack. This gives more responsive controls.
float fDecelerationModifier = vCurrentLocalSpeed.z > 0.0f && fThrustSpeedInput <= 0.001f ? sm_Tunables.m_HoverControlParams.m_fGravityDecelerationModifier : 1.0f;
//! Gravity damping is a constant acceleration provided by the boosters. So, if 0.5f, then we fall at half the speed of gravity.
vAccel.z += (CPhysics::GetGravitationalAcceleration() * sm_Tunables.m_HoverControlParams.m_fGravityDamping * fDecelerationModifier);
}
}
vAccel.Scale(pPed->GetMass());
if(sm_Tunables.m_HoverControlParams.m_bDoRelativeToCam)
{
camInterface::GetPlayerControlCamAimFrame().GetWorldMatrix().Transform3x3(vAccel);
}
else
{
vAccel = VEC3V_TO_VECTOR3(pPed->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(vAccel)));
}
CFlightModelHelper::ApplyForceCgSafe(vAccel,pPed);
bool bForceSyncToCamera = false;
CTask* pSubTask = GetSubTask();
if(pSubTask && (pSubTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_GUN || pSubTask->GetTaskType() == CTaskTypes::TASK_MOUNT_THROW_PROJECTILE))
{
//! If doing drive by, sync to camera heading.
bForceSyncToCamera = true;
}
else if(sm_Tunables.m_HoverControlParams.m_bSyncHeadingToDesired && pPed->IsPlayer())
{
// roll with camera. similar to on foot, we basically rotate around when we roll stick.
float fHeadingModifier = sm_Tunables.m_HoverControlParams.m_fDesiredHeadingModifier +
(( sm_Tunables.m_HoverControlParams.m_fDesiredHeadingModifierBoost - sm_Tunables.m_HoverControlParams.m_fDesiredHeadingModifier) * m_fHoverModeBoost);
if(pPed->GetControlFromPlayer()->GetPedWalkUpDown().GetNorm() < sm_Tunables.m_HoverControlParams.m_fMinPitchForHeadingChange ||
abs(pPed->GetControlFromPlayer()->GetPedWalkLeftRight().GetNorm()) < sm_Tunables.m_HoverControlParams.m_fMinYawForHeadingChange )
{
//Only heading sync to camera if we're using the ped follow cam or third person aim cam
const camBaseCamera * pRenderCam = camInterface::GetDominantRenderedCamera();
if(pRenderCam && (pRenderCam->GetClassId()==camFollowPedCamera::GetStaticClassId()))
{
//! Note: This is a hack, and we should remove it when we create a jetpack camera that doesn't lag as much.
bForceSyncToCamera = true;
}
}
else
{
//! updates desired heading and SetExtraHeadingChangeThisFrame.
CTaskFall::UpdateHeading(pPed, pPed->GetControlFromPlayer(), fHeadingModifier);
}
}
bool bRotateToGoToPosition = !m_bArrivedAtDestination && !m_bStopRotationToTarget;
bool bRotateToShootAtPosition = !bRotateToGoToPosition && GetSubTask() && (GetSubTask()->GetTaskType() == CTaskTypes::TASK_VEHICLE_GUN || GetSubTask()->GetTaskType() == CTaskTypes::TASK_MOUNT_THROW_PROJECTILE);
if((sm_Tunables.m_HoverControlParams.m_bSyncHeadingToCamera || bForceSyncToCamera) && pPed->IsPlayer())
{
// sync up our heading with camera (e.g. if we rotate the camera, our ped heading changes too).
float fCamHeading = camInterface::GetPlayerControlCamHeading(*pPed);
pPed->SetDesiredHeading(fCamHeading);
CTaskMotionBase* pTask = pPed->GetCurrentMotionTask();
float fHeadingDelta = pTask ? pTask->CalcDesiredDirection(): fCamHeading;
float fHeadingChangeThisFrame = fHeadingDelta * sm_Tunables.m_HoverControlParams.m_fCameraHeadingModifier * fTimeStep;
pPed->GetMotionData()->SetExtraHeadingChangeThisFrame(fHeadingChangeThisFrame);
}
//If we aren't a player and we have a target position, rotate towards it (unless we have already arrived at our destination)
//If we have arrived at our GoTo destination but we are running the drive-by task, rotate towards the ShootAt target
else if (!pPed->IsPlayer() && (bRotateToGoToPosition || bRotateToShootAtPosition))
{
Vector3 vTargetPos; vTargetPos.Zero();
float fHeadingChangeRate = GetPed()->GetHeadingChangeRate();
if( m_bIsAvoidingEntity && !m_bArrivedAtDestination)
{
//if we're not at our target, steer towards avoidance direction
vTargetPos = m_vAvoidTargetPosition;
}
else
{
if (bRotateToGoToPosition || m_rotateForAvoid)
{
vTargetPos = VEC3V_TO_VECTOR3(CalculateTargetGoToPosition());
}
else if (bRotateToShootAtPosition)
{
static dev_float fTunedHeadingChangeRate = 5.0f;
fHeadingChangeRate = fTunedHeadingChangeRate;
vTargetPos = VEC3V_TO_VECTOR3(CalculateTargetShootAtPosition());
}
}
Vector3 vPedPos = pPed->GetBonePositionCached(BONETAG_L_FOOT);
vPedPos.Average(pPed->GetBonePositionCached(BONETAG_R_FOOT));
const float fDesiredHeading = fwAngle::GetRadianAngleBetweenPoints(vTargetPos.x, vTargetPos.y, vPedPos.x, vPedPos.y);
GetPed()->SetDesiredHeading(fDesiredHeading);
const float fHeadingDiff = SubtractAngleShorter(fDesiredHeading, GetPed()->GetCurrentHeading());
const float fHeadingDelta = Min(Abs(fHeadingDiff), fHeadingChangeRate * fwTimer::GetTimeStep());
GetPed()->GetMotionData()->SetExtraHeadingChangeThisFrame(Sign(fHeadingDiff) * fHeadingDelta);
}
//! Ang vel.
Vector3 vAngularVelocity = GetPed()->GetAngVelocity();
vAngularVelocity = VEC3V_TO_VECTOR3(GetPed()->GetTransform().UnTransform3x3(VECTOR3_TO_VEC3V(vAngularVelocity)));
m_vAngularVelocity.Lerp(sm_Tunables.m_HoverControlParams.m_fAngVelocityLerpRate, m_vAngularVelocity, vAngularVelocity);
}
void CTaskJetpack::ProcessFlightPhysics(CPed *pPed)
{
float fThrustSpeed = GetUpThrust();
Vector3 vAccel = VEC3_ZERO;
//! Apply a forward thrust.
vAccel.y = fThrustSpeed;
vAccel.y *= sm_Tunables.m_FlightControlParams.m_fThrustModifier;
vAccel.Scale(pPed->GetMass());
vAccel = VEC3V_TO_VECTOR3(pPed->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(vAccel)));
CFlightModelHelper::ApplyForceCgSafe(vAccel,pPed);
// Adjust the air speed based on the left stick input
Vector3 vAirSpeedIn = VEC3_ZERO;
Vector3 vLinearV = VEC3_ZERO;
vAirSpeedIn = VEC3V_TO_VECTOR3(pPed->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(vAirSpeedIn)));
//Set the linear velocity.
ms_FlightModeHelper.SetDragCoeff(vLinearV, CFlightModelHelper::FT_Linear_V);
bool bFLattenVelocity = GetTimeInState() < sm_Tunables.m_FlightControlParams.m_fTimeToFlattenVelocity;
//Process the flight model.
ms_FlightModeHelper.ProcessFlightModel(pPed, 0.0f, m_fPitch, m_fRoll, m_fYaw, vAirSpeedIn, true, 1.0f, 1.0f, bFLattenVelocity);
//! Ang vel.
Vector3 vAngularVelocity = GetPed()->GetAngVelocity();
vAngularVelocity = VEC3V_TO_VECTOR3(GetPed()->GetTransform().UnTransform3x3(VECTOR3_TO_VEC3V(vAngularVelocity)));
m_vAngularVelocity.Lerp(sm_Tunables.m_FlightControlParams.m_fAngVelocityLerpRate, m_vAngularVelocity, vAngularVelocity);
}
CTask::FSM_Return CTaskJetpack::ProcessPreFSM()
{
CPed *pPed = GetPed();
pPed->SetPedResetFlag(CPED_RESET_FLAG_ProcessPreRender2, true);
if(!DoesPedHaveJetpack(pPed))
{
return FSM_Quit;
}
if(pPed->GetIsSwimming())
{
return FSM_Quit;
}
if(pPed->GetCurrentMotionTask() && pPed->GetCurrentMotionTask()->IsUnderWater())
{
return FSM_Quit;
}
if(ShouldBlockWeaponSwitching())
{
pPed->SetPedResetFlag(CPED_RESET_FLAG_TemporarilyBlockWeaponSwitching, true);
}
//! Don't allow ped to parachute when using the jetpack as models overlap.
pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableParachuting);
pPed->SetPedResetFlag(CPED_RESET_FLAG_IsUsingJetpack, true);
switch(GetState())
{
case State_Flight:
//! The flight physics require us to take control of the peds capsule so that we can roll, pitch, do loop the loops etc :)
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePedCapsuleControl, true);
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePedConstraints, true);
//fall through.
case State_Hover:
{
//! process physics.
pPed->SetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasks, true);
//! don't scale capsule.
pPed->SetPedResetFlag( CPED_RESET_FLAG_DisableDynamicCapsuleRadius, true);
//! prevent jitter.
pPed->SetPedResetFlag(CPED_RESET_FLAG_ForceProcessPhysicsUpdateEachSimStep, true);
//! Disable probes otherwise we end up sticking to the ground on take off.
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisableProcessProbes, true);
//! Don't sync desired to current.
//pPed->SetPedResetFlag(CPED_RESET_FLAG_SyncDesiredHeadingToCurrentHeading, false);
break;
}
default:
{
break;
}
}
return FSM_Continue;
}
CTask::FSM_Return CTaskJetpack::UpdateFSM(const s32 iState, const FSM_Event iEvent)
{
CPed* pPed = GetPed();
FSM_Begin
FSM_State(State_Start)
FSM_OnEnter
Start_OnEnter(pPed);
FSM_OnUpdate
return Start_OnUpdate(pPed);
FSM_State(State_StreamAssets)
FSM_OnEnter
StreamAssets_OnEnter(pPed);
FSM_OnUpdate
return StreamAssets_OnUpdate(pPed);
FSM_State(State_Hover)
FSM_OnEnter
Hover_OnEnter(pPed);
FSM_OnUpdate
return Hover_OnUpdate(pPed);
FSM_OnExit
Hover_OnExit(pPed);
FSM_State(State_Flight)
FSM_OnEnter
Flight_OnEnter(pPed);
FSM_OnUpdate
return Flight_OnUpdate(pPed);
FSM_State(State_Fall)
FSM_OnEnter
Fall_OnEnter(pPed);
FSM_OnUpdate
return Fall_OnUpdate(pPed);
FSM_State(State_Land)
FSM_OnEnter
Land_OnEnter(pPed);
FSM_OnUpdate
return Land_OnUpdate(pPed);
FSM_State(State_Quit)
FSM_OnUpdate
return Quit_OnUpdate(pPed);
FSM_End
}
CTaskJetpack::FSM_Return CTaskJetpack::ProcessPostFSM()
{
UpdateMoVE();
return FSM_Continue;
}
void CTaskJetpack::UpdateMoVE()
{
if(m_networkHelper.IsNetworkActive())
{
float fPitchNormalised = (m_fPitch + 1.0f) * 0.5f;
float fRollNormalised = (m_fRoll + 1.0f) * 0.5f;
float fYawNormalised = (m_fYaw + 1.0f) * 0.5f;
Vector3 vAngles;
GetAnglesForMoVE(vAngles);
Vector3 vAngVel = GetAngularVelocityForMoVE(m_vAngularVelocity);
m_networkHelper.SetFloat(ms_PitchId, fPitchNormalised);
m_networkHelper.SetFloat(ms_RollId, fRollNormalised);
m_networkHelper.SetFloat(ms_YawId, fYawNormalised);
m_networkHelper.SetFloat(ms_PitchAngleId, vAngles.x);
m_networkHelper.SetFloat(ms_RollAngleId, vAngles.y);
m_networkHelper.SetFloat(ms_YawAngleId, vAngles.z);
m_networkHelper.SetFloat(ms_AngVelocityXId, vAngVel.x);
m_networkHelper.SetFloat(ms_AngVelocityYId, vAngVel.y);
m_networkHelper.SetFloat(ms_AngVelocityZId, vAngVel.z);
m_networkHelper.SetFloat(ms_ThrusterLeftId, m_fThrusterLeft);
m_networkHelper.SetFloat(ms_ThrusterRightId, m_fThrusterRight);
m_networkHelper.SetFloat(ms_SpeedId, GetFlightSpeedForMoVE());
m_networkHelper.SetFloat(ms_HoverModeBoostId, m_fHoverModeBoost);
float fCombinedThrust = ((m_fThrusterRight - m_fThrusterLeft) + 1.0f) * 0.5f;
fCombinedThrust = Clamp(fCombinedThrust, 0.0f, 1.0f);
m_networkHelper.SetFloat(ms_CombinedThrustId, fCombinedThrust);
}
}
void CTaskJetpack::Start_OnEnter(CPed* pPed)
{
//add us to the list of flying peds
CFlyingPedCollector::AddPed(RegdPed(pPed));
}
CTask::FSM_Return CTaskJetpack::Start_OnUpdate(CPed* UNUSED_PARAM(pPed))
{
SetState(State_StreamAssets);
return FSM_Continue;
}
void CTaskJetpack::StreamAssets_OnEnter(CPed* UNUSED_PARAM(pPed))
{
// Get the motion task data set to get our clipset for this ped type
// CPedModelInfo *pModelInfo = pPed->GetPedModelInfo();
// const CMotionTaskDataSet* pMotionTaskDataSet = CMotionTaskDataManager::GetDataSet(pModelInfo->GetMotionTaskDataSetHash().GetHash());
}
CTask::FSM_Return CTaskJetpack::StreamAssets_OnUpdate(CPed* pPed)
{
m_networkHelper.RequestNetworkDef(CClipNetworkMoveInfo::ms_NetworkTaskJetpack);
if (m_networkHelper.IsNetworkDefStreamedIn(CClipNetworkMoveInfo::ms_NetworkTaskJetpack))
{
m_networkHelper.CreateNetworkPlayer(pPed, CClipNetworkMoveInfo::ms_NetworkTaskJetpack);
pPed->GetMovePed().SetTaskNetwork(m_networkHelper.GetNetworkPlayer(), SLOW_BLEND_DURATION );
TakeOff(pPed);
SetState(State_Hover);
}
return FSM_Continue;
}
void CTaskJetpack::TakeOff(CPed* pPed)
{
//Note that the ped is not standing.
pPed->SetIsStanding(false);
//Note that the ped is in the air.
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir, true);
//Ensure gravity is turned on for the ped.
pPed->SetUseExtractedZ(false);
//Destroy the weapon in the ped's hand.
weaponAssert(pPed->GetWeaponManager());
pPed->GetWeaponManager()->DestroyEquippedWeaponObject();
}
void CTaskJetpack::Hover_OnEnter(CPed* pPed)
{
taskAssert(m_networkHelper.IsNetworkActive());
m_networkHelper.SetClipSet(fwMvClipSetId(sm_Tunables.m_JetpackClipsets.m_HoverClipSetHash));
//Set the move network state.
SetMoveNetworkState(ms_Hover, ms_HoverOnEnter, ms_HoverState);
//Check if we can aim.
if(CanAim())
{
//Backup the equipped weapon.
pPed->GetWeaponManager()->BackupEquippedWeapon();
//Equip the best weapon.
pPed->GetWeaponManager()->EquipBestWeapon();
}
m_fNearGroundTime = 0.0f;
}
CTask::FSM_Return CTaskJetpack::Hover_OnUpdate(CPed* pPed)
{
//Ensure the state has been entered.
if(!m_networkHelper.IsInTargetState())
{
return FSM_Continue;
}
if(CanActivateKillSwitch(pPed))
{
SetState(State_Fall);
return FSM_Continue;
}
if(CheckForLanding(pPed))
{
SetState(State_Land);
return FSM_Continue;
}
if(GetTimeInState() > 0.1f && CanActivateFlightMode(pPed))
{
SetState(State_Flight);
return FSM_Continue;
}
ProcessHoverControls(pPed);
//! HACK. Restart jetpack if ambient clip killed it. If jetpack existed at motion task layer, this wouldn't be an issue, but it is :(
if(!pPed->GetMovePed().GetTaskNetwork())
{
pPed->GetMovePed().SetTaskNetwork(m_networkHelper.GetNetworkPlayer(), SLOW_BLEND_DURATION );
SetFlag( aiTaskFlags::RestartCurrentState );
}
return FSM_Continue;
}
void CTaskJetpack::Hover_OnExit(CPed* pPed)
{
//Check if we can aim.
if(CanAim())
{
//Restore the backup weapon.
pPed->GetWeaponManager()->RestoreBackupWeapon();
//Destroy the equipped weapon object.
pPed->GetWeaponManager()->DestroyEquippedWeaponObject();
}
}
void CTaskJetpack::Flight_OnEnter(CPed* UNUSED_PARAM(pPed))
{
taskAssert(m_networkHelper.IsNetworkActive());
m_networkHelper.SetClipSet(fwMvClipSetId(sm_Tunables.m_JetpackClipsets.m_FlightClipSetHash));
//Set the move network state.
SetMoveNetworkState(ms_Flight, ms_FlightOnEnter, ms_FlightState);
}
CTask::FSM_Return CTaskJetpack::Flight_OnUpdate(CPed* pPed)
{
//Ensure the state has been entered.
if(!m_networkHelper.IsInTargetState())
{
return FSM_Continue;
}
ProcessFlightControls(pPed);
if(ScanForCrashCollision())
{
//! Force NM.
m_bCrashLanded = true;
SetState(State_Fall);
}
if(GetTimeInState() > 0.1f && CanActivateFlightMode(pPed))
{
SetState(State_Hover);
return FSM_Continue;
}
return FSM_Continue;
}
void CTaskJetpack::Fall_OnEnter(CPed* UNUSED_PARAM(pPed))
{
fwFlags32 fallFlags = FF_DisableLandRoll;
if(m_bCrashLanded)
{
fallFlags.SetFlag(FF_ForceHighFallNM);
}
//! Don't land roll with a jetpack :)
CTaskFall* pNewTask = rage_new CTaskFall(fallFlags);
SetNewTask(pNewTask);
}
CTask::FSM_Return CTaskJetpack::Fall_OnUpdate(CPed* UNUSED_PARAM(pPed))
{
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
//! Go back to hover if we are in mid-air still?
SetState(State_Quit);
}
return FSM_Continue;
}
void CTaskJetpack::Land_OnEnter(CPed* UNUSED_PARAM(pPed))
{
//! Don't land roll with a jetpack :)
fwFlags32 fallFlags = FF_DisableLandRoll | FF_SkipToLand;
CTaskFall* pNewTask = rage_new CTaskFall(fallFlags);
SetNewTask(pNewTask);
}
CTask::FSM_Return CTaskJetpack::Land_OnUpdate(CPed* UNUSED_PARAM(pPed))
{
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
SetState(State_Quit);
}
return FSM_Continue;
}
CTask::FSM_Return CTaskJetpack::Quit_OnUpdate(CPed* UNUSED_PARAM(pPed))
{
return FSM_Quit;
}
bool CTaskJetpack::DoesPedHaveJetpack(const CPed *pPed)
{
//Ensure the ped's inventory is valid.
const CPedInventory* pPedInventory = pPed->GetInventory();
if(!pPedInventory)
{
return false;
}
//Ensure the ped has a jetpack in inventory.
if(!pPedInventory->GetWeapon(GADGETTYPE_JETPACK))
{
return false;
}
//Ensure the ped can get a jetpack.
if(pPed->GetJetpack() == NULL)
{
return false;
}
return true;
}
void CTaskJetpack::ProcessHoverControls(const CPed *pPed)
{
// Make sure we're not a clone...
Assert(GetPed() && !GetPed()->IsNetworkClone());
//Check if we should use AI controls.
if(ShouldUseAiControls())
{
//Process the parachuting AI controls.
ProcessHoverControlsForAI(pPed);
}
else if(ShouldUsePlayerControls())
{
//Process the parachuting player controls.
ProcessHoverControlsForPlayer(pPed);
}
}
void CTaskJetpack::ProcessHoverControlsForPlayer(const CPed *pPed)
{
const CControl *pControl = pPed->GetControlFromPlayer();
if(pControl)
{
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work
// (ideally we would alter this but its safer to fix the issue this way).
m_fThrusterLeftInput = Abs(pControl->GetVehicleFlyYawLeft().GetNorm());
m_fThrusterRightInput = Abs(pControl->GetVehicleFlyYawRight().GetNorm());
if ((m_fThrusterLeftInput == 1.0f) || (m_fThrusterRightInput == 1.0f))
{
m_fThrusterLeft = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpRate, m_fThrusterLeft, m_fThrusterLeftInput);
m_fThrusterRight = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpRate, m_fThrusterRight, m_fThrusterRightInput);
}
else
{
m_fThrusterLeft = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpOutRate, m_fThrusterLeft, m_fThrusterLeftInput);
m_fThrusterRight = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpOutRate, m_fThrusterRight, m_fThrusterRightInput);
}
float fPitchInput = -pControl->GetPedWalkUpDown().GetNorm();
float fRollInput = pControl->GetPedWalkLeftRight().GetNorm();
if(pControl->GetPedSprintIsDown())
{
//If we have no pitch input from the player, set it as fully forwards
if (fPitchInput == 0)
{
fPitchInput = 1.0f;
}
Approach(m_fHoverModeBoost, 1.0f, sm_Tunables.m_HoverControlParams.m_fBoostApproachRateIn, fwTimer::GetTimeStep());
}
else
{
Approach(m_fHoverModeBoost, 0.0f, sm_Tunables.m_HoverControlParams.m_fBoostApproachRateOut, fwTimer::GetTimeStep());
}
if (fPitchInput != 0)
{
m_fPitch = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fPitchLerpRate, m_fPitch, fPitchInput);
}
else
{
m_fPitch = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fPitchLerpOutRate, m_fPitch, fPitchInput);
}
if (fRollInput != 0)
{
m_fRoll = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fRollLerpRate, m_fRoll, fRollInput);
}
else
{
m_fRoll = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fRollLerpOutRate, m_fRoll, fRollInput);
}
bool bAiming = (pControl->GetPedTargetIsDown() || pControl->GetPedAttack().IsDown());
if(pPed->IsLocalPlayer())
{
ProcessAimControls(bAiming);
}
}
else
{
m_fThrusterLeftInput = 0.0f;
m_fThrusterRightInput = 0.0f;
}
}
void CTaskJetpack::ProcessHoverControlsForAI(const CPed *pPed)
{
ProcessAimControls(m_bIsAiming);
//Calculate the target position.
Vector3 vTargetPosition = VEC3V_TO_VECTOR3(CalculateTargetPositionFuture());
//Calculate the ped position.
Vector3 vPedPosition = pPed->GetBonePositionCached(BONETAG_L_FOOT);
vPedPosition.Average(pPed->GetBonePositionCached(BONETAG_R_FOOT));
//Calculate if we need to apply some vertical avoidance
vTargetPosition.z = ComputeTargetZToAvoidTerrain(pPed, vPedPosition, vTargetPosition);
//Calculate a vector from the ped to the target.
Vector3 vPedToTarget = vTargetPosition - vPedPosition;
//give some leeway for AI to move around while still being at the destination
float fArrivalThreshold = m_bIsAvoidingEntity ? sm_Tunables.m_AvoidanceSettings.m_fArrivalTolerance : 0.75f;
m_bArrivedAtDestination = vPedToTarget.Mag() < fArrivalThreshold;
m_bStopRotationToTarget = vPedToTarget.XYMag() < fArrivalThreshold;
//Stop pitching towards target if we have a drastic change in target Z position (we likely have an obstacle to fly over).
//or if our XY distance is v small
static dev_float fThreshold = 10.0f;
bool bDisablePitch = false;
bool bShouldGoStraightUp = (m_fHitObstacleDistance <= 10.0f && m_fHitObstacleDistance >= 0.0f) && (vTargetPosition.z > vPedPosition.z + fThreshold);
if (bShouldGoStraightUp || (m_bStopRotationToTarget && m_bArrivedAtDestination))
{
bDisablePitch = true;
}
//Calculate and set jetpack control parameters
CalculateStickForPitchForJetpackAI(vPedToTarget, bDisablePitch);
CalculateThrusterValuesForJetpackAI(vPedToTarget.z);
CalculateStickForRollForJetpackAI();
static dev_bool s_bEnabledHeadingAvoidance = true;
if( m_bFlyingPedAvoidance && s_bEnabledHeadingAvoidance )
{
CalculateDesiredHeadingForAvoidance(pPed, vPedPosition, vPedToTarget);
}
}
void CTaskJetpack::CalculateDesiredHeadingForAvoidance(const CPed *pPed, Vector3 vPedPosition, Vector3 vPedToTarget)
{
m_bIsAvoidingEntity = false;
m_rotateForAvoid = false;
Vector3 vVelocity = pPed->GetVelocity();
float fspeed = vVelocity.Mag2();
static dev_float fSpeedLimit = 9.0f;
if( fspeed < fSpeedLimit )
{
float distToTarget = vPedToTarget.Mag2();
if( distToTarget > 100.0f)
{
//going slowly and far from target
for(int i = 0; i < CFlyingPedCollector::m_flyingPeds.GetCount(); i++)
{
const CPed* pOtherPed = CFlyingPedCollector::m_flyingPeds[i];
if( pOtherPed && pPed != pOtherPed)
{
Vector3 vOtherPedPosition = pOtherPed->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pOtherPed->GetBonePositionCached(BONETAG_R_FOOT));
Vector3 vMeToOtherVehicle = vOtherPedPosition - vPedPosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if( fDistance2 < 9.0f)
{
float currentHeading = pPed->GetCurrentHeading();
vPedToTarget.Normalize();
float desiredHeading = rage::Atan2f(-vPedToTarget.x, vPedToTarget.y);
float headingDiff = SubtractAngleShorter(desiredHeading, currentHeading);
if(fabsf(headingDiff) > DtoR * sm_Tunables.m_AvoidanceSettings.m_fMinTurnAngle)
{
//we are very close to someone else, instead of moving, rotate to target first
//prevents groups hitting each other when realigning to new target
m_rotateForAvoid = true;
m_fPitch = 0.0f;
m_fThrusterRightInput = 0.0f;
m_fThrusterLeftInput = 0.0f;
}
}
}
}
}
else
{
//going slowly near our destination, steer away from nearby peds
Vector3 desiredVelocity = Vector3(Vector3::ZeroType);
float fAvoidDistance2 = sm_Tunables.m_AvoidanceSettings.m_fAvoidanceRadius*sm_Tunables.m_AvoidanceSettings.m_fAvoidanceRadius;
int groupCount = 0;
for(int i = 0; i < CFlyingPedCollector::m_flyingPeds.GetCount(); i++)
{
const CPed* pOtherPed = CFlyingPedCollector::m_flyingPeds[i];
if( pOtherPed && pPed != pOtherPed)
{
Vector3 vOtherPedPosition = pOtherPed->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pOtherPed->GetBonePositionCached(BONETAG_R_FOOT));
//2dify
vOtherPedPosition.z = vPedPosition.z;
Vector3 vMeToOtherVehicle = vOtherPedPosition - vPedPosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if( fDistance2 < fAvoidDistance2)
{
vMeToOtherVehicle.Normalize();
vMeToOtherVehicle.Scale(Min(1.0f,1.0f/fDistance2));
desiredVelocity -= vMeToOtherVehicle;
++groupCount;
#if __BANK
if (ms_bDebugAI)
{
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vPedPosition), VECTOR3_TO_VEC3V(vOtherPedPosition), Color_red, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vPedPosition), VECTOR3_TO_VEC3V(vPedPosition - vMeToOtherVehicle), 0.2f, Color_blue, -1);
}
#endif
}
}
}
if( groupCount > 0)
{
desiredVelocity /= (float)groupCount;
//average of avoidance and to target
//vPedToTarget.Normalize();
//desiredVelocity.Average(vPedToTarget);
desiredVelocity.Normalize();
//convert with respect to heading
float currentHeading = pPed->GetCurrentHeading();
float worldHeading = rage::Atan2f(-desiredVelocity.x, desiredVelocity.y);
float newHeading = worldHeading - currentHeading;
newHeading = fwAngle::LimitRadianAngle( newHeading );
//turn into pitch and roll
float fDesiredPitch = Clamp(rage::Cosf( newHeading ), -1.0f, 1.0f);
float fDesiredRoll = Clamp(-rage::Sinf( newHeading ), -1.0f, 1.0f);
fDesiredPitch *= sm_Tunables.m_AvoidanceSettings.m_fAvoidanceMultiplier;
fDesiredRoll *= sm_Tunables.m_AvoidanceSettings.m_fAvoidanceMultiplier;;
m_fPitch = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fPitchLerpRate, m_fPitch, fDesiredPitch);
m_fRoll = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fRollLerpRate, m_fRoll, fDesiredRoll);
#if __BANK
if (ms_bDebugAI)
{
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vPedPosition), VECTOR3_TO_VEC3V(vPedPosition + desiredVelocity), 0.2f, Color_red, -1);
}
#endif
}
}
}
else
{
//and avoid those that are near destination
const CPed* closestPed = NULL;
float avoidanceRadius = sm_Tunables.m_AvoidanceSettings.m_fAvoidanceRadius;
float closestDistance = avoidanceRadius * avoidanceRadius * 2.0f;
for(int i = 0; i < CFlyingPedCollector::m_flyingPeds.GetCount(); i++)
{
const CPed* pOtherPed = CFlyingPedCollector::m_flyingPeds[i];
if( pOtherPed && pPed != pOtherPed)
{
Vector3 vOtherPedPosition = pOtherPed->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pOtherPed->GetBonePositionCached(BONETAG_R_FOOT));
Vector3 vMeToOtherVehicle = vOtherPedPosition - vPedPosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if( fDistance2 < closestDistance)
{
Vector3 direction = pPed->GetVelocity();
direction.Normalize();
vMeToOtherVehicle.Normalize();
float dotDirection = direction.Dot(vMeToOtherVehicle);
if( dotDirection > 0.0f)
{
closestDistance = fDistance2;
closestPed = pOtherPed;
}
}
}
}
if ( closestPed != NULL )
{
Vector3 direction = vPedToTarget;// pPed->GetVelocity();
direction.z = 0.0f;
direction.Normalize();
Vector3 vFuturePosition = vPedPosition + direction * closestDistance;
Vector3 vToFuturePosition = vFuturePosition - vPedPosition;
Vector3 vOtherPedPosition = closestPed->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(closestPed->GetBonePositionCached(BONETAG_R_FOOT));
//2dify
vOtherPedPosition.z = vPedPosition.z;
vFuturePosition.z = vPedPosition.z;
const float vTValue = geomTValues::FindTValueOpenSegToPoint(vPedPosition, vToFuturePosition, vOtherPedPosition);
if ( vTValue > 0.0f )
{
Vector3 vClosestPoint = vPedPosition + (vToFuturePosition * vTValue);
Vector3 vCenterToClosestDir = vClosestPoint - vOtherPedPosition;
float distance2 = vCenterToClosestDir.Mag2();
if ( distance2 > FLT_EPSILON && distance2 < avoidanceRadius*avoidanceRadius)
{
m_bIsAvoidingEntity = true;
float distance = sqrt(distance2);
vCenterToClosestDir *= 1/distance;
//find point on circle around other ped tangential to us and closest to original direction
Vector3 vTargetOffset = vOtherPedPosition + vCenterToClosestDir * avoidanceRadius;
Vector3 vDirToNewTarget = vTargetOffset - vPedPosition;
vDirToNewTarget.Normalize();
float distanceToTarget = vPedToTarget.Mag();
//push new target away from us by the current distance to our target
m_vAvoidTargetPosition = vPedPosition + ( vDirToNewTarget * distanceToTarget);
#if __BANK
if (ms_bDebugAI)
{
grcDebugDraw::Sphere(vOtherPedPosition, 0.2f, Color_green, false, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vOtherPedPosition), VECTOR3_TO_VEC3V(vTargetOffset), 0.2f, Color_blue, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vPedPosition), VECTOR3_TO_VEC3V(m_vAvoidTargetPosition), 0.2f, Color_red, -1);
}
#endif
}
}
}
}
}
void CTaskJetpack::CalculateThrusterValuesForJetpackAI(float fDistanceZ)
{
bool bDisableThrust = m_bArrivedAtDestination && m_bDisableThrustAtDestination;
if (bDisableThrust)
{
m_fThrusterRightInput = 0.0f;
m_fThrusterLeftInput = 0.0f;
}
else
{
float fThrust = 0.0f;
float fDesiredThrust = 1.0f;
static dev_float fSlowDownDistance = 10.0f;
static dev_float fSlowDownDistanceMin = 0.0f;
//Check if the slow down distance is valid.
if(fSlowDownDistance > 0.0f)
{
//Slow down as we approach the target.
fDesiredThrust *= Min(1.0f, Max(Abs(fDistanceZ) - fSlowDownDistanceMin, 0.0f) / fSlowDownDistance);
}
//Ensure we apply the thrust in the right direction
fThrust = fDistanceZ < 0 ? -fDesiredThrust : fDesiredThrust;
m_fThrusterRightInput = fThrust;
m_fThrusterLeftInput = 0.0f;
}
m_fThrusterLeft = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpRate, m_fThrusterLeft, m_fThrusterLeftInput);
m_fThrusterRight = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fThrustLerpRate, m_fThrusterRight, m_fThrusterRightInput);
}
void CTaskJetpack::CalculateStickForPitchForJetpackAI(Vector3 vPedToTarget, bool bDisablePitch)
{
if (bDisablePitch || m_bArrivedAtDestination)
{
static dev_float fStopPitchLerpRate = 0.15f;
m_fPitch = rage::Lerp(fStopPitchLerpRate, m_fPitch, 0.0f);
}
else
{
float fPitch = 0.0f;
//Calculate the flat distance from the ped to the target.
float fTargetDist = vPedToTarget.XYMag2();
float fDesiredPitch = 1.0f;
static dev_float fSlowDownDistance = 225.0f;
static dev_float fSlowDownDistanceMin = 0.0f;
//Check if the slow down distance is valid.
if(fSlowDownDistance > 0.0f)
{
//Slow down as we approach the target.
fDesiredPitch *= Min(1.0f, Max( fTargetDist - fSlowDownDistanceMin, 0.0f ) / fSlowDownDistance);
}
fPitch = fDesiredPitch;
m_fPitch = rage::Lerp(sm_Tunables.m_HoverControlParams.m_fPitchLerpRate, m_fPitch, fPitch);
}
}
void CTaskJetpack::CalculateStickForRollForJetpackAI()
{
m_fRoll = 0.0f;
}
float CTaskJetpack::ComputeTargetZToAvoidTerrain(const CPed* pPed, const Vector3& vJetpackPos, const Vector3& vTargetPosition)
{
float targetZ = vTargetPosition.z;
// check if we should use ground probes or height map
static dev_bool bDisableGroundProbes = false;
if (!bDisableGroundProbes)
{
// avoid terrain mins. should avoid mountains and stuff. also should project for long distances
targetZ = VehicleNavigationHelper::ComputeTargetZToAvoidHeightMap(vJetpackPos, pPed->GetVelocity().XYMag(), vTargetPosition,
m_fMinHeightAboveSurface, CGameWorldHeightMap::GetMinHeightFromWorldHeightMap);
// Clamp to min height above ground
if (m_fMinHeightAboveSurface > 0)
{
bool bDetectedObstacle = false;
Vector3 vHitPosition; vHitPosition.Zero();
float fDistFromHitToEndOfCapsule = 0.0f; //Used to scale vertical probe spread
Vector3 vStartPos(vJetpackPos.x, vJetpackPos.y, vJetpackPos.z);
Vector3 vEndPos = vTargetPosition;
//If distance to target pos is greater than the defined max distance, then scale it down to that length
float fDistanceToTarget = (vEndPos - vStartPos).Mag();
static dev_float fMaxCapsuleDistance = 50.0f;
if (fDistanceToTarget > fMaxCapsuleDistance)
{
vEndPos = vEndPos - vStartPos;
vEndPos.Normalize();
vEndPos.Scale(fMaxCapsuleDistance);
vEndPos = vStartPos + vEndPos;
}
if (m_startLocationTestResults.GetResultsReady())
{
if (m_startLocationTestResults.GetNumHits() > 0)
{
vHitPosition = VEC3V_TO_VECTOR3(m_startLocationTestResults[0].GetPosition());
m_fHitObstacleDistance = (vHitPosition - vJetpackPos).Mag();
fDistFromHitToEndOfCapsule = (vEndPos - vHitPosition).Mag();
bDetectedObstacle = true;
}
else
{
m_fHitObstacleDistance = -1.0f;
// No obstacles, reset target height
m_fPreviousTargetHeight = vTargetPosition.z;
}
m_startLocationTestResults.Reset();
}
if (!m_startLocationTestResults.GetWaitingOnResults())
{
WorldProbe::CShapeTestCapsuleDesc capsuleDescForwards;
capsuleDescForwards.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES|ArchetypeFlags::GTA_RIVER_TYPE);
capsuleDescForwards.SetExcludeInstance(GetPed()->GetCurrentPhysicsInst());
capsuleDescForwards.SetResultsStructure(&m_startLocationTestResults);
capsuleDescForwards.SetMaxNumResultsToUse(1);
static dev_float fRadius = 1.0f;
capsuleDescForwards.SetCapsule(vStartPos, vEndPos, fRadius);
capsuleDescForwards.SetDoInitialSphereCheck(true);
capsuleDescForwards.SetIsDirected(true);
WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDescForwards, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
#if __BANK
if (ms_bDebugAI)
{
ms_debugDraw.AddCapsule(VECTOR3_TO_VEC3V(vStartPos), VECTOR3_TO_VEC3V(vEndPos), 1.0f, Color_red, 1, 0, false);
}
#endif
}
// If we detected a hit on the forward-capsule, perform vertical probe tests to determine a target height
if (bDetectedObstacle)
{
if ( m_targetLocationTestResults[0].GetResultsReady()
&& m_targetLocationTestResults[1].GetResultsReady()
&& m_targetLocationTestResults[2].GetResultsReady()
&& m_targetLocationTestResults[3].GetResultsReady()
&& m_targetLocationTestResults[4].GetResultsReady())
{
m_fPreviousTargetHeight = vTargetPosition.z;
for (int i = 0; i < NUM_VERTICAL_CAPSULE_TESTS; i++)
{
if (m_targetLocationTestResults[i].GetNumHits() > 0)
{
float fDetectedHeight = m_targetLocationTestResults[i][0].GetPosition().GetZf() + static_cast<float>(m_fMinHeightAboveSurface);
if (fDetectedHeight > m_fPreviousTargetHeight)
{
m_fPreviousTargetHeight = fDetectedHeight;
}
}
float fWaterHeight = -LARGE_FLOAT;
if (Water::GetWaterLevelNoWaves(vTargetPosition, &fWaterHeight, POOL_DEPTH, 999999.9f, NULL))
{
m_fPreviousTargetHeight = rage::Max(m_fPreviousTargetHeight, fWaterHeight + m_fMinHeightAboveSurface);
}
m_targetLocationTestResults[i].Reset();
}
}
if ( !m_targetLocationTestResults[0].GetWaitingOnResults()
&& !m_targetLocationTestResults[1].GetWaitingOnResults()
&& !m_targetLocationTestResults[2].GetWaitingOnResults()
&& !m_targetLocationTestResults[3].GetWaitingOnResults()
&& !m_targetLocationTestResults[4].GetWaitingOnResults())
{
WorldProbe::CShapeTestHitPoint hitPoint;
WorldProbe::CShapeTestCapsuleDesc capsuleDesc[NUM_VERTICAL_CAPSULE_TESTS];
for (int i = 0; i < NUM_VERTICAL_CAPSULE_TESTS; i++)
{
Vector3 vJetpackDirXY = vTargetPosition - vJetpackPos;
vJetpackDirXY.Normalize();
//Spread out tests from hit position to capsule end position
float fDistanceMult = fDistFromHitToEndOfCapsule / (float)NUM_VERTICAL_CAPSULE_TESTS;
fDistanceMult *= (float)i;
vJetpackDirXY.x *= fDistanceMult;
vJetpackDirXY.y *= fDistanceMult;
Vector3 vSamplePosition = /*vJetpackPos*/ vHitPosition + vJetpackDirXY;
Vector3 vStartPos(vSamplePosition.x, vSamplePosition.y, vSamplePosition.z + m_fProbeHeight);
Vector3 vEndPos(vSamplePosition.x, vSamplePosition.y, vSamplePosition.z - m_fProbeHeight);
#if __BANK
if (ms_bDebugAI)
{
Color32 capsuleColor = Color_green;
switch (i)
{
case 0:
capsuleColor = Color_green4;
break;
case 1:
capsuleColor = Color_green4;
break;
case 2:
capsuleColor = Color_green3;
break;
case 3:
capsuleColor = Color_green2;
break;
case 4:
capsuleColor = Color_green;
break;
default:
capsuleColor = Color_green;
break;
}
ms_debugDraw.AddCapsule(VECTOR3_TO_VEC3V(vStartPos), VECTOR3_TO_VEC3V(vEndPos), 0.5f, capsuleColor, 1, 0, false);
ms_vTargetLocationTestStart = vStartPos;
ms_vTargetLocationTestEnd = vEndPos;
}
#endif //__BANK
capsuleDesc[i].SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES|ArchetypeFlags::GTA_RIVER_TYPE);
capsuleDesc[i].SetExcludeInstance(GetPed()->GetCurrentPhysicsInst());
capsuleDesc[i].SetResultsStructure(&m_targetLocationTestResults[i]);
capsuleDesc[i].SetMaxNumResultsToUse(1);
static dev_float fRadius = 0.5f;
capsuleDesc[i].SetCapsule(vStartPos, vEndPos, fRadius);
capsuleDesc[i].SetDoInitialSphereCheck(false);
capsuleDesc[i].SetIsDirected(true);
WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDesc[i], WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
}
}
}
targetZ = Max(targetZ, m_fPreviousTargetHeight );
}
}
else
{
targetZ = VehicleNavigationHelper::ComputeTargetZToAvoidHeightMap(vJetpackPos, pPed->GetVelocity().XYMag(),
vTargetPosition, m_fMinHeightAboveSurface, CGameWorldHeightMap::GetMaxHeightFromWorldHeightMap);
}
targetZ = ComputeTargetZToAvoidFlyingPeds(pPed, vJetpackPos,vTargetPosition,targetZ);
return targetZ;
}
float CTaskJetpack::ComputeTargetZToAvoidFlyingPeds(const CPed* pPed, const Vector3& vJetpackPos, const Vector3& vTargetPosition, float targetZ)
{
float newTargetZ = targetZ;
const CPed* pClosestAbove = NULL;
const CPed* pClosestBelow = NULL;
float fDistanceAbove = sm_Tunables.m_AvoidanceSettings.m_fVerticalBuffer;
float fDistanceBelow = -sm_Tunables.m_AvoidanceSettings.m_fVerticalBuffer;
float fAvoidance2 = sm_Tunables.m_AvoidanceSettings.m_fAvoidanceRadius*sm_Tunables.m_AvoidanceSettings.m_fAvoidanceRadius;
for(int i = 0; i < CFlyingPedCollector::m_flyingPeds.GetCount(); i++)
{
const CPed* pOtherPed = CFlyingPedCollector::m_flyingPeds[i];
if( pOtherPed && pPed != pOtherPed)
{
Vector3 vOtherPedPosition = pOtherPed->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pOtherPed->GetBonePositionCached(BONETAG_R_FOOT));
Vector3 vMeToOtherVOed = vOtherPedPosition - vJetpackPos;
float fVerticalDistance = vMeToOtherVOed.GetZ();
float fHorizontalDistance = vMeToOtherVOed.XYMag2();
if(fHorizontalDistance < fAvoidance2)
{
if( fVerticalDistance < -0.5f && fVerticalDistance > fDistanceBelow)
{
fDistanceBelow = fVerticalDistance;
pClosestBelow = pOtherPed;
}
else if( fVerticalDistance > 0.5f && fVerticalDistance < fDistanceAbove)
{
fDistanceAbove = fVerticalDistance;
pClosestAbove = pOtherPed;
}
}
}
}
if( pClosestAbove && pClosestBelow )
{
//do nothing
}
else if( pClosestBelow )
{
//go up
Vector3 vOtherPedPosition = pClosestBelow->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pClosestBelow->GetBonePositionCached(BONETAG_R_FOOT));
newTargetZ = Max(vOtherPedPosition.GetZ() + sm_Tunables.m_AvoidanceSettings.m_fVerticalBuffer, targetZ);
}
else if( pClosestAbove )
{
Vector3 vOtherPedPosition = pClosestAbove->GetBonePositionCached(BONETAG_L_FOOT);
vOtherPedPosition.Average(pClosestAbove->GetBonePositionCached(BONETAG_R_FOOT));
if(vTargetPosition.GetZ() == targetZ) //not avoiding terrain
{
//go down
newTargetZ = Min(vOtherPedPosition.GetZ() - sm_Tunables.m_AvoidanceSettings.m_fVerticalBuffer, targetZ);
}
else
{
//stay where we are?
//newTargetZ = vJetpackPos.GetZ();
}
}
return newTargetZ;
}
void CTaskJetpack::ProcessFlightControls(const CPed *pPed)
{
const CControl *pControl = pPed->GetControlFromPlayer();
if(pControl)
{
if(sm_Tunables.m_FlightControlParams.m_bThrustersAlwaysOn)
{
m_fThrusterLeftInput = 1.0f;
m_fThrusterRightInput = 1.0f;
}
else
{
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work
// (ideally we would alter this but its safer to fix the issue this way).
m_fThrusterLeftInput = Abs(pControl->GetVehicleFlyYawLeft().GetNorm());
m_fThrusterRightInput = Abs(pControl->GetVehicleFlyYawRight().GetNorm());
}
m_fThrusterLeft = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fThrustLerpRate, m_fThrusterLeft, m_fThrusterLeftInput);
m_fThrusterRight = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fThrustLerpRate, m_fThrusterRight, m_fThrusterRightInput);
float fPitchInput = -pControl->GetPedWalkUpDown().GetNorm();
float fRollInput = pControl->GetPedWalkLeftRight().GetNorm();
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work
// (ideally we would alter this but its safer to fix the issue this way).
float fYawInput = Abs(pControl->GetVehicleFlyYawRight().GetNorm()) - Abs(pControl->GetVehicleFlyYawLeft().GetNorm());
m_fPitch = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fPitchLerpRate, m_fPitch, fPitchInput);
m_fRoll = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fRollLerpRate, m_fRoll, fRollInput);
m_fYaw = rage::Lerp(sm_Tunables.m_FlightControlParams.m_fYawLerpRate, m_fYaw, fYawInput);
}
else
{
m_fThrusterLeftInput = 0.0f;
m_fThrusterRightInput = 0.0f;
}
}
void CTaskJetpack::ProcessAimControls(bool bAiming)
{
CPed *pPed = GetPed();
bool bShouldAim = (bAiming && CanAim());
//Check if our weapon is valid.
bool bIsWeaponValid = pPed->GetWeaponManager() && pPed->GetWeaponManager()->GetEquippedWeaponInfo() &&
pPed->GetWeaponManager()->GetEquippedWeaponInfo()->CanBeUsedAsDriveBy(pPed);
if(!bIsWeaponValid)
{
bShouldAim = false;
}
else
{
//Check if the ped can drive by with the equipped weapon.
if(!CVehicleMetadataMgr::CanPedDoDriveByWithEquipedWeapon(pPed))
{
bShouldAim = false;
}
}
CTask* pSubTask = GetSubTask();
//Check if we should aim.
if(bShouldAim)
{
//Reset holster timer if we aim again
m_fHolsterTimer = 0.0f;
//Check if the sub-task is valid.
if(pSubTask)
{
//Check if the sub-task is not the right type.
if(pSubTask->GetTaskType() == CTaskTypes::TASK_AMBIENT_CLIPS)
{
//Request termination.
taskAssert(pSubTask->SupportsTerminationRequest());
pSubTask->RequestTermination();
}
//Terminate driveby subtask if restart requested
if((m_bRestartShootingTask || m_bTerminateDrivebyTask) && (pSubTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_GUN || pSubTask->GetTaskType() == CTaskTypes::TASK_MOUNT_THROW_PROJECTILE))
{
if (m_bRestartShootingTask)
m_bRestartShootingTask = false;
if (m_bTerminateDrivebyTask)
{
m_bIsAiming = false;
m_bTerminateDrivebyTask = false;
}
//Request termination.
taskAssert(pSubTask->SupportsTerminationRequest());
pSubTask->RequestTermination();
}
}
else
{
//Create the task.
pSubTask = CreateTaskForAiming();
//Start the task.
SetNewTask(pSubTask);
}
}
else
{
//Check if the sub-task is valid.
if(pSubTask)
{
if(pSubTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_GUN || pSubTask->GetTaskType() == CTaskTypes::TASK_MOUNT_THROW_PROJECTILE)
{
m_fHolsterTimer += fwTimer::GetTimeStep();
bool bHolsterTimerFinished = m_fHolsterTimer >= sm_Tunables.m_HoverControlParams.m_fMaxHolsterTimer;
if (bHolsterTimerFinished)
{
//Request termination.
taskAssert(pSubTask->SupportsTerminationRequest());
pSubTask->RequestTermination();
m_fHolsterTimer = 0.0f;
}
}
}
else
{
//Create the task.
pSubTask = CreateTaskForAmbientClips();
//Start the task.
SetNewTask(pSubTask);
}
}
}
CTask* CTaskJetpack::CreateTaskForAiming() const
{
const CPed *pPed = GetPed();
if (pPed->IsPlayer())
{
const CWeaponInfo* pWeaponInfo = pPed->GetWeaponManager()->GetEquippedWeaponInfo();
if(pWeaponInfo && pWeaponInfo->GetIsThrownWeapon())
{
return rage_new CTaskMountThrowProjectile(CWeaponController::WCT_Player, 0, CWeaponTarget());
}
else
{
return rage_new CTaskVehicleGun(CTaskVehicleGun::Mode_Player);
}
}
else
{
CAITarget target;
// Use the entity as the target if we have been provided with one, else use the coordinates
if (m_pTargetEntityShootAt)
{
// Set m_vTargetPositionShootAt as an offset if it is not zero
if (!IsEqualAll(m_vTargetPositionShootAt, Vec3V(V_ZERO)))
{
target.SetEntityAndOffsetUnlimited(m_pTargetEntityShootAt, VEC3V_TO_VECTOR3(m_vTargetPositionShootAt));
}
else
{
target.SetEntity(m_pTargetEntityShootAt);
}
}
else
{
target.SetPosition(VEC3V_TO_VECTOR3(m_vTargetPositionShootAt));
}
return rage_new CTaskVehicleGun(CTaskVehicleGun::Mode_Fire, m_iFiringPatternHash, &target, ((float)m_iFrequencyPercentage)/100.0f, m_fAbortRange);
}
}
CTask *CTaskJetpack::CreateTaskForAmbientClips() const
{
return rage_new CTaskAmbientClips(CTaskAmbientClips::Flag_PlayIdleClips, CONDITIONALANIMSMGR.GetConditionalAnimsGroup("PLAYER_IDLES"));
}
bool CTaskJetpack::CanActivateKillSwitch(CPed *pPed) const
{
CControl *pControl = pPed->GetControlFromPlayer();
if(sm_Tunables.m_HoverControlParams.m_bUseKillSwitch && pControl && pControl->GetMeleeAttackLight().IsPressed())
{
return true;
}
return false;
}
bool CTaskJetpack::CanActivateFlightMode(CPed *pPed) const
{
if(sm_Tunables.m_FlightControlParams.m_bEnabled)
{
CControl *pControl = pPed->GetControlFromPlayer();
if(pControl && pControl->GetPedDuck().IsPressed())
{
return true;
}
}
return false;
}
bool CTaskJetpack::CheckForLanding(CPed *pPed)
{
if(sm_Tunables.m_HoverControlParams.m_bAutoLand && GetUpThrustInput() <= 0.0f) //don't land if we have upthrust.
{
if( CTaskJump::IsPedNearGround(pPed, sm_Tunables.m_HoverControlParams.m_fLandHeightThreshold) )
{
m_fNearGroundTime += fwTimer::GetTimeStep();
if( (m_fNearGroundTime >= sm_Tunables.m_HoverControlParams.m_fTimeNearGroundToLand) ||
(sm_Tunables.m_HoverControlParams.m_bAntiGravityThrusters && (m_fThrusterLeftInput > 0.0f)) )
{
return true;
}
}
else
{
m_fNearGroundTime = 0.0f;
}
}
return false;
}
float CTaskJetpack::GetUpThrust() const
{
if(sm_Tunables.m_HoverControlParams.m_bAntiGravityThrusters)
{
return m_fThrusterRight;
}
else
{
return (m_fThrusterLeft + m_fThrusterRight) * 0.5f; //normalise.
}
}
float CTaskJetpack::GetUpThrustInput() const
{
if(sm_Tunables.m_HoverControlParams.m_bAntiGravityThrusters)
{
return m_fThrusterRightInput;
}
else
{
return (m_fThrusterLeftInput + m_fThrusterRightInput) * 0.5f; //normalise.
}
}
bool CTaskJetpack::IsInMotion() const
{
if(GetState() == State_Hover)
{
if(m_fThrusterLeft < 0.01f && m_fThrusterRight < 0.01f &&
GetPed()->GetVelocity().Mag2() < rage::square(sm_Tunables.m_HoverControlParams.m_MaxIdleAnimVelocity) &&
GetPed()->GetAngVelocity().Mag2() < rage::square(sm_Tunables.m_HoverControlParams.m_MaxIdleAnimVelocity))
{
return false;
}
}
return true;
}
void CTaskJetpack::GetOverriddenGameplayCameraSettings(tGameplayCameraSettings& settings) const
{
//Make sure that there is a valid camera at all times
settings.m_CameraHash = sm_Tunables.m_CameraSettings.m_HoverCamera.GetHash();
settings.m_MaxOrientationDeltaToInterpolate = PI;
switch(GetState())
{
case State_Hover:
settings.m_CameraHash = sm_Tunables.m_CameraSettings.m_HoverCamera.GetHash();
break;
case State_Flight:
settings.m_CameraHash = sm_Tunables.m_CameraSettings.m_FlightCamera.GetHash();
break;
default:
settings.m_CameraHash = 0;
break;
}
}
const CJetpack *CTaskJetpack::GetJetpack() const
{
const CPed *pPed = GetPed();
if(pPed)
{
return pPed->GetJetpack();
}
return NULL;
}
bool CTaskJetpack::ShouldBlockWeaponSwitching() const
{
//Check if we are parachuting, and can aim.
if((GetState() == State_Hover) && CanAim())
{
return false;
}
return true;
}
bool CTaskJetpack::CanAim() const
{
//Ensure aiming is not disabled.
if(!sm_Tunables.m_AimSettings.m_bEnabled)
{
return false;
}
//Ensure the weapon manager is valid.
if(!GetPed()->GetWeaponManager())
{
return false;
}
return true;
}
bool CTaskJetpack::ScanForCrashCollision() const
{
const CPed *pPed = GetPed();
const CCollisionRecord* pColRecord = pPed->GetFrameCollisionHistory()->GetMostSignificantCollisionRecord();
if(!pColRecord)
{
return false;
}
const CEntity* pEntity = pColRecord->m_pRegdCollisionEntity;
if(pEntity)
{
if (pEntity->GetIsTypeObject())
{
//Ensure the object is a parachute.
if(static_cast<CObject*>(GetJetpack()->GetObject()) == pEntity)
{
return false;
}
}
}
return true;
}
float CTaskJetpack::GetFlightSpeedForMoVE() const
{
float fSpeed = Clamp(GetPed()->GetVelocity().Mag2() / rage::square(sm_Tunables.m_FlightControlParams.m_fMaxSpeedForMoVE), 0.0f, 1.0f);
return fSpeed;
}
float RemapTo360(float fAngle)
{
if(fAngle < 0.0f)
{
fAngle += 2.0f * PI;
}
return fAngle;
}
void CTaskJetpack::GetAnglesForMoVE(Vector3 &vAngles) const
{
Mat34V m = GetPed()->GetMatrix();
MAT34V_TO_MATRIX34(m).ToEulersXYZ(vAngles);
//! Bit hacky, but change order to get full roll.
{
Vector3 vTemp;
Mat34V m = GetPed()->GetMatrix();
MAT34V_TO_MATRIX34(m).ToEulersYXZ(vTemp);
vAngles.y = vTemp.y;
}
vAngles.x = RemapTo360(vAngles.x);
vAngles.y = RemapTo360(vAngles.y);
vAngles.z = RemapTo360(vAngles.z);
//! normalise to range 0.0f -> 1.0f
vAngles.x /= (2.0f*PI);
vAngles.y /= (2.0f*PI);
vAngles.z /= (2.0f*PI);
}
Vector3 CTaskJetpack::GetAngularVelocityForMoVE(const Vector3 &vAngVel) const
{
Vector3 vAngVelLocal = vAngVel;
//! Get ang vel in range -1.0f -> 1.0f
if(GetState()==State_Hover)
{
vAngVelLocal.x = 0.0f;
vAngVelLocal.y = 0.0f;
vAngVelLocal.z = Clamp(vAngVel.z/sm_Tunables.m_HoverControlParams.m_fMaxAngularVelocityZ, -1.0f, 1.0f);
}
else if(GetState()==State_Flight)
{
vAngVelLocal.x = Clamp(vAngVel.x/sm_Tunables.m_FlightControlParams.m_fMaxAngularVelocityX, -1.0f, 1.0f);
vAngVelLocal.y = Clamp(vAngVel.y/sm_Tunables.m_FlightControlParams.m_fMaxAngularVelocityY, -1.0f, 1.0f);
vAngVelLocal.z = Clamp(vAngVel.z/sm_Tunables.m_FlightControlParams.m_fMaxAngularVelocityZ, -1.0f, 1.0f);
}
//! Normalise to range 0.0f -> 1.0f
vAngVelLocal.x = (vAngVelLocal.x + 1.0f) * 0.5f;
vAngVelLocal.y = (vAngVelLocal.y + 1.0f) * 0.5f;
vAngVelLocal.z = (vAngVelLocal.z + 1.0f) * 0.5f;
return vAngVelLocal;
}
bool CTaskJetpack::IsMoveTransitionAvailable(s32 UNUSED_PARAM(iNextState)) const
{
//Note: At the point at which this function is called, we are always in the AI state of the new state.
// We need to check that there is a move transition from the previous state to the current.
//Grab the previous state.
int iPreviousState = GetPreviousState();
//Grab the state.
int iState = GetState();
//Check the previous state.
switch(iPreviousState)
{
case State_StreamAssets:
{
//Check the state.
switch(iState)
{
case State_Hover:
case State_Flight:
{
return true;
}
default:
{
return false;
}
}
}
case State_Hover:
{
//Check the state.
switch(iState)
{
case State_Hover:
case State_Flight:
{
return true;
}
default:
{
return false;
}
}
}
case State_Flight:
{
//Check the state.
switch(iState)
{
case State_Hover:
{
return true;
}
default:
{
return false;
}
}
}
default:
{
return false;
}
}
}
void CTaskJetpack::OnCloneTaskNoLongerRunningOnOwner()
{
SetStateFromNetwork(State_Quit);
}
CTaskInfo* CTaskJetpack::CreateQueriableState() const
{
CClonedJetpackInfo::InitParams params(GetState(), (u8)m_iFlags);
return rage_new CClonedJetpackInfo( params );
}
void CTaskJetpack::ReadQueriableState(CClonedFSMTaskInfo* pTaskInfo)
{
// Need to read the state first as we use it below to decide if we want to read something or not...
CTaskFSMClone::ReadQueriableState(pTaskInfo);
Assert( pTaskInfo->GetTaskInfoType() == CTaskInfo::INFO_TYPE_JETPACK );
CClonedJetpackInfo *jetpackInfo = static_cast<CClonedJetpackInfo*>(pTaskInfo);
m_iFlags = jetpackInfo->GetFlags();
}
bool CTaskJetpack::OverridesNetworkAttachment(CPed* UNUSED_PARAM(pPed))
{
//! TO DO.
return false;
}
bool CTaskJetpack::OverridesNetworkBlender(CPed* UNUSED_PARAM(pPed))
{
//! TO DO.
return false;
}
bool CTaskJetpack::IsInScope(const CPed* UNUSED_PARAM(pPed))
{
return true;
}
void CTaskJetpack::UpdateClonedSubTasks(CPed* UNUSED_PARAM(pPed), int const UNUSED_PARAM(iState))
{
//! TO DO.
}
CTask::FSM_Return CTaskJetpack::UpdateClonedFSM(const s32 iState, const FSM_Event iEvent)
{
//! TO DO.
return UpdateFSM(iState, iEvent);
}
CTaskFSMClone *CTaskJetpack::CreateTaskForClonePed(CPed* UNUSED_PARAM(pPed))
{
//! TO DO.
CTaskJetpack *newTask = rage_new CTaskJetpack(0);
newTask->SetState(GetState());
return newTask;
}
CTaskFSMClone *CTaskJetpack::CreateTaskForLocalPed(CPed* UNUSED_PARAM(pPed))
{
//! TO DO.
CTaskJetpack *newTask = rage_new CTaskJetpack(0);
newTask->SetState(GetState());
return newTask;
}
CClonedJetpackInfo::CClonedJetpackInfo( CClonedJetpackInfo::InitParams const & _initParams )
: m_iFlags(_initParams.m_iFlags)
{
SetStatusFromMainTaskState(_initParams.m_state);
}
CClonedJetpackInfo::CClonedJetpackInfo()
: m_iFlags(0)
{
}
CClonedJetpackInfo::~CClonedJetpackInfo()
{}
CTaskFSMClone* CClonedJetpackInfo::CreateCloneFSMTask()
{
return rage_new CTaskJetpack( m_iFlags );
}
#if !__FINAL
const char * CTaskJetpack::GetStaticStateName(s32 iState)
{
Assert(iState >= State_Start && iState <= State_Quit);
static const char* aStateNames[] =
{
"State_Start",
"State_StreamAssets",
"State_Hover",
"State_Flight",
"State_Fall",
"State_Land",
"State_Quit"
};
return aStateNames[iState];
}
#endif // !__FINAL
#if DEBUG_DRAW
float ConvertToDebugRange(float fValue)
{
float fReturn = (fValue * -2.0f) - 1.0f;
return fReturn;
}
void CTaskJetpack::Debug() const
{
static dev_float fScale = 0.15f;
static dev_float fHalfScale = fScale * 0.5f;
static dev_float fEndWidth = 0.01f;
if(ms_bDebugControls)
{
Vector2 vPitchDebugPos(0.25f, 0.15f);
Vector2 vRollDebugPos(vPitchDebugPos.x - fHalfScale, vPitchDebugPos.y + fHalfScale);
Vector2 vYawDebugPos(vRollDebugPos.x, vRollDebugPos.y + fHalfScale + fScale);
Vector2 vLeftThrusterDebugPos(0.75f, 0.15f);
Vector2 vRightThrusterDebugPos(vLeftThrusterDebugPos.x + fHalfScale, vLeftThrusterDebugPos.y);
Vector2 vSpeedDebugPos(vLeftThrusterDebugPos.x + (fHalfScale * 2.0f), vLeftThrusterDebugPos.y);
float fMeterLeftThrust = ConvertToDebugRange(m_fThrusterLeft);
float fMeterRightThrust = ConvertToDebugRange(m_fThrusterRight);
float fSpeed = ConvertToDebugRange(GetFlightSpeedForMoVE());
grcDebugDraw::Meter(vPitchDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Pitch");
grcDebugDraw::MeterValue(vPitchDebugPos, Vector2(0.0f,1.0f), fScale, m_fPitch, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vRollDebugPos,Vector2(1.0f,0.0f),fScale,fEndWidth,Color32(255,255,255),"Roll");
grcDebugDraw::MeterValue(vRollDebugPos, Vector2(1.0f,0.0f), fScale, m_fRoll, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vYawDebugPos,Vector2(1.0f,0.0f),fScale,fEndWidth,Color32(255,255,255),"Yaw");
grcDebugDraw::MeterValue(vYawDebugPos, Vector2(1.0f,0.0f), fScale, m_fYaw, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vLeftThrusterDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"L Thrust");
Vector2 vLeftThrusterValueDebugPos = vLeftThrusterDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vLeftThrusterValueDebugPos, Vector2(0.0f,1.0f), fScale, fMeterLeftThrust, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vRightThrusterDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"R Thrust");
Vector2 vRightThrusterValueDebugPos = vRightThrusterDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vRightThrusterValueDebugPos, Vector2(0.0f,1.0f), fScale, fMeterRightThrust, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vSpeedDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Speed");
Vector2 vSpeedValueDebugPos = vSpeedDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vSpeedValueDebugPos, Vector2(0.0f,1.0f), fScale, fSpeed, fEndWidth, Color32(255,0,0));
}
if(ms_bDebugAngles)
{
Vector2 vPitchAngleDebugPos(0.75f, 0.4f);
Vector2 vRollAngleDebugPos(vPitchAngleDebugPos.x + fHalfScale, vPitchAngleDebugPos.y);
Vector2 vYawAngleDebugPos(vPitchAngleDebugPos.x + (fHalfScale * 2.0f), vPitchAngleDebugPos.y);
Vector2 vAngularVelocityXPos(0.75f, 0.65f);
Vector2 vAngularVelocityYPos(vAngularVelocityXPos.x + fHalfScale, vAngularVelocityXPos.y);
Vector2 vAngularVelocityZPos(vAngularVelocityXPos.x + (fHalfScale * 2.0f), vAngularVelocityXPos.y);
//! Get angles.
Vector3 vAngles;
GetAnglesForMoVE(vAngles);
vAngles.x = ConvertToDebugRange(vAngles.x);
vAngles.y = ConvertToDebugRange(vAngles.y);
vAngles.z = ConvertToDebugRange(vAngles.z);
//! Get ang velocity.
Vector3 vAngVel = GetAngularVelocityForMoVE(m_vAngularVelocity);
vAngVel.x = ConvertToDebugRange(vAngVel.x);
vAngVel.y = ConvertToDebugRange(vAngVel.y);
vAngVel.z = ConvertToDebugRange(vAngVel.z);
grcDebugDraw::Meter(vPitchAngleDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Pitch Angle");
Vector2 vPitchAngleValueDebugPos = vPitchAngleDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vPitchAngleValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngles.x, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vRollAngleDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Roll Angle");
Vector2 vRollAngleValueDebugPos = vRollAngleDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vRollAngleValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngles.y, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vYawAngleDebugPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Yaw Angle");
Vector2 vYawAngleValueDebugPos = vYawAngleDebugPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vYawAngleValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngles.z, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vAngularVelocityXPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Ang Vel X");
Vector2 vAngularVelocityXValueDebugPos = vAngularVelocityXPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vAngularVelocityXValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngVel.x, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vAngularVelocityYPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Ang Vel Y");
Vector2 vAngularVelocityYValueDebugPos = vAngularVelocityYPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vAngularVelocityYValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngVel.y, fEndWidth, Color32(255,0,0));
grcDebugDraw::Meter(vAngularVelocityZPos,Vector2(0.0f,1.0f),fScale,fEndWidth,Color32(255,255,255),"Ang Vel Z");
Vector2 vAngularVelocityZValueDebugPos = vAngularVelocityZPos + Vector2(0.0f,fScale);
grcDebugDraw::MeterValue(vAngularVelocityZValueDebugPos, Vector2(0.0f,1.0f), fScale, vAngVel.z, fEndWidth, Color32(255,0,0));
Vector2 v2DDebugPos(0.1f, 0.55f);
static dev_float s_fTextSize = 0.025f;
char buff[64];
sprintf(buff, "Ang Vel X: %.2f", GetPed()->GetAngVelocity().x);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += s_fTextSize;
sprintf(buff, "Ang Vel Y: %.2f", GetPed()->GetAngVelocity().y);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += s_fTextSize;
sprintf(buff, "Ang Vel Z: %.2f", GetPed()->GetAngVelocity().z);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += (s_fTextSize * 2.0f);
Vector3 vRawAngles;
Mat34V m = GetPed()->GetMatrix();
MAT34V_TO_MATRIX34(m).ToEulersXYZ(vRawAngles);
sprintf(buff, "Pitch: %.2f", vRawAngles.x * RtoD);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += s_fTextSize;
sprintf(buff, "Roll: %.2f", vRawAngles.y * RtoD);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += s_fTextSize;
sprintf(buff, "Yaw: %.2f", vRawAngles.z * RtoD);
grcDebugDraw::Text(v2DDebugPos, Color32(255,0,0), buff);
v2DDebugPos.y += (s_fTextSize * 2.0f);
}
if (ms_bDebugAI)
{
// Vec3V vGoToPos = CalculateTargetGoToPosition();
Vec3V vShootAtPos = CalculateTargetShootAtPosition();
//grcDebugDraw::Sphere(vGoToPos, 0.5f, Color32(0,0,255));
grcDebugDraw::Sphere(vShootAtPos, 0.5f, Color32(255,0,0));
grcDebugDraw::Sphere(vShootAtPos, ms_fAbortRange, Color32(0,255,0), false);
grcDebugDraw::Line(ms_vTargetLocationTestStart, ms_vTargetLocationTestEnd, Color32(255,0,0));
grcDebugDraw::Sphere(ms_vHeightmapSamplePosition, 2.0f, Color_red, true, -1);
}
}
#endif
#if __BANK
void CTaskJetpack::GiveJetpackTestCB()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
CPedInventory* pPedInventory = pFocusPed->GetInventory();
if(pPedInventory)
{
pPedInventory->AddWeapon(GADGETTYPE_JETPACK);
}
}
}
void CTaskJetpack::RemoveJetpackTestCB()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
CPedInventory* pPedInventory = pFocusPed->GetInventory();
if(pPedInventory)
{
pPedInventory->RemoveWeapon(GADGETTYPE_JETPACK);
}
}
}
void CTaskJetpack::ToggleEquipJetpack()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
bool bOld = pFocusPed->GetPedConfigFlag(CPED_CONFIG_FLAG_EquipJetpack);
pFocusPed->SetPedConfigFlag(CPED_CONFIG_FLAG_EquipJetpack, !bOld);
}
}
void CTaskJetpack::AIShootAtTest()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
//Warp into the air for tests
static dev_bool bWarpIntoAir = false;
if (pFocusPed->IsOnGround() && bWarpIntoAir)
{
Vector3 vWarpPos = VEC3V_TO_VECTOR3(pFocusPed->GetTransform().GetPosition());
vWarpPos.z += 10.0f;
pFocusPed->Teleport(vWarpPos, pFocusPed->GetCurrentHeading());
}
CTaskJetpack *pTaskJetpack = NULL;
if (pFocusPed->GetPedIntelligence() && pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK))
{
pTaskJetpack = static_cast<CTaskJetpack *>(pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK));
}
else
{
pFocusPed->GetPedIntelligence()->ClearTasks();
pTaskJetpack = rage_new CTaskJetpack();
pFocusPed->GetPedIntelligence()->AddTaskDefault(pTaskJetpack);
}
if (pTaskJetpack)
{
const u32 uFiringPattern = ATSTRINGHASH("FIRING_PATTERN_BURST_FIRE_DRIVEBY", 0x0d31265f2);
int iFreqPercentage = 100;
if (ms_bShootAtCoords)
{
pTaskJetpack->SetShootAtParametersForAI(NULL, ms_vTargetPositionShoot, ms_fAbortRange, iFreqPercentage, uFiringPattern);
}
else
{
CPed *pTargetPed = CGameWorld::FindLocalPlayer();
if (pTargetPed)
{
pTaskJetpack->SetShootAtParametersForAI(pTargetPed, ms_vTargetPositionShoot, ms_fAbortRange, iFreqPercentage, uFiringPattern);
}
}
}
}
}
void CTaskJetpack::AIGoToTest()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
CTaskJetpack *pTaskJetpack = NULL;
if (pFocusPed->GetPedIntelligence() && pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK))
{
pTaskJetpack = static_cast<CTaskJetpack *>(pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK));
}
else
{
pFocusPed->GetPedIntelligence()->ClearTasks();
pTaskJetpack = rage_new CTaskJetpack();
pFocusPed->GetPedIntelligence()->AddTaskDefault(pTaskJetpack);
}
if (pTaskJetpack)
{
if (ms_bGoToCoords)
{
pTaskJetpack->SetGoToParametersForAI(NULL, ms_vTargetPositionGoTo, ms_fMinHeightAboveSurface, ms_bDisableThrustAtDestination, ms_bUseRandomTimerWhenClose, ms_fProbeHeight);
}
else
{
CPed *pTargetPed = CGameWorld::FindLocalPlayer();
if (pTargetPed)
{
pTaskJetpack->SetGoToParametersForAI(pTargetPed, ms_vTargetPositionGoTo, ms_fMinHeightAboveSurface, ms_bDisableThrustAtDestination, ms_bUseRandomTimerWhenClose, ms_fProbeHeight);
}
}
}
}
}
void CTaskJetpack::SetDebugGoToFromCamera()
{
ms_vTargetPositionGoTo = camInterface::GetPos();
}
void CTaskJetpack::SetGoToTestPoint1()
{
ms_vTargetPositionGoTo = ms_vJetpackTestPoint1;
}
void CTaskJetpack::SetGoToTestPoint2()
{
ms_vTargetPositionGoTo = ms_vJetpackTestPoint2;
}
void CTaskJetpack::SetGoToTestPoint3()
{
ms_vTargetPositionGoTo = ms_vJetpackTestPoint3;
}
void CTaskJetpack::DisableDrivebyTest()
{
CEntity* pFocusEnt = CDebugScene::FocusEntities_Get(0);
CPed* pFocusPed = NULL;
if(pFocusEnt && pFocusEnt->GetIsTypePed())
{
pFocusPed = static_cast<CPed*>(pFocusEnt);
}
else
{
pFocusPed = CGameWorld::FindLocalPlayer();
}
if(pFocusPed)
{
CTaskJetpack *pTaskJetpack = NULL;
if (pFocusPed->GetPedIntelligence() && pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK))
{
pTaskJetpack = static_cast<CTaskJetpack *>(pFocusPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JETPACK));
}
if (pTaskJetpack)
{
pTaskJetpack->RequestDrivebyTermination(true);
}
}
}
void CTaskJetpack::InitWidgets()
{
bkBank* pBank = BANKMGR.FindBank("A.I.");
if(pBank)
{
pBank->PushGroup("Jetpack");
pBank->AddButton("Give focus ped jetpack",GiveJetpackTestCB);
pBank->AddButton("Remove focus ped jetpack",RemoveJetpackTestCB);
pBank->AddButton("Toggle Equip jetpack",ToggleEquipJetpack);
pBank->AddSlider("GoTo Target Coord X", &ms_vTargetPositionGoTo.x, WORLDLIMITS_XMIN, WORLDLIMITS_XMAX, 0.1f);
pBank->AddSlider("GoTo Target Coord Y", &ms_vTargetPositionGoTo.y, WORLDLIMITS_YMIN, WORLDLIMITS_YMAX, 0.1f);
pBank->AddSlider("GoTo Target Coord Z", &ms_vTargetPositionGoTo.z, WORLDLIMITS_ZMIN, WORLDLIMITS_ZMAX, 0.1f);
pBank->AddSlider("ShootAt Target Coord X", &ms_vTargetPositionShoot.x, WORLDLIMITS_XMIN, WORLDLIMITS_XMAX, 0.1f);
pBank->AddSlider("ShootAt Target Coord Y", &ms_vTargetPositionShoot.y, WORLDLIMITS_YMIN, WORLDLIMITS_YMAX, 0.1f);
pBank->AddSlider("ShootAt Target Coord Z", &ms_vTargetPositionShoot.z, WORLDLIMITS_ZMIN, WORLDLIMITS_ZMAX, 0.1f);
pBank->AddSlider("ShootAt: AbortRange", &ms_fAbortRange, 0.0f, 100.0f, 0.01f);
pBank->AddSlider("GoTo: Probe height", &ms_fProbeHeight, 0.0f, 200.0f, 0.01f);
pBank->AddSlider("GoTo: Min height above surface", &ms_fMinHeightAboveSurface, 0.0f, 100.0f, 0.01f);
pBank->AddToggle("GoTo: Disable thrust at destination", &ms_bDisableThrustAtDestination);
pBank->AddToggle("Debug AI", &ms_bDebugAI);
pBank->AddToggle("Go To Coords (not entity)", &ms_bGoToCoords);
pBank->AddToggle("Shoot At Coords (not entity)", &ms_bShootAtCoords);
pBank->AddToggle("Go To: Use random timer for target pos when close", &ms_bUseRandomTimerWhenClose);
pBank->AddButton("Shoot At Coord", AIShootAtTest);
pBank->AddButton("Go To Coord", AIGoToTest);
pBank->AddButton("Set Go To Coords from Camera", SetDebugGoToFromCamera);
pBank->AddButton("Set Go To: TestPoint1", SetGoToTestPoint1);
pBank->AddButton("Set Go To: TestPoint2", SetGoToTestPoint2);
pBank->AddButton("Set Go To: TestPoint3", SetGoToTestPoint3);
pBank->AddButton("Stop Driveby", DisableDrivebyTest);
pBank->PushGroup("Controls");
pBank->AddToggle("Debug controls",&ms_bDebugControls);
pBank->AddToggle("Debug angles",&ms_bDebugAngles);
pBank->PopGroup();
pBank->PushGroup("Flight Mode handling");
ms_FlightModeHelper.AddWidgetsToBank(pBank);
pBank->PopGroup();
pBank->PopGroup();
}
}
#endif
#endif // ENABLE_JETPACK
atFixedArray<RegdPed, 20> CFlyingPedCollector::m_flyingPeds;
void CFlyingPedCollector::RemovePed(const RegdPed& ped)
{
int index = m_flyingPeds.Find(ped);
if(index != -1)
{
m_flyingPeds.DeleteFast(index);
}
}
void CFlyingPedCollector::AddPed(const RegdPed& ped)
{
if(m_flyingPeds.GetAvailable() && m_flyingPeds.Find(ped) == -1)
{
m_flyingPeds.Push(ped);
}
}