2111 lines
72 KiB
C++
2111 lines
72 KiB
C++
|
|
#include "task/Motion/TaskMotionBase.h"
|
|
|
|
// rage includes
|
|
#include "creature/componentextradofs.h"
|
|
#include "crskeleton/skeleton.h"
|
|
#include "math/angmath.h"
|
|
#include "physics/debugEvents.h"
|
|
#include "system/stack.h"
|
|
#include "vector\matrix34.h"
|
|
|
|
// framework includes
|
|
#include "fwanimation/animdirector.h"
|
|
#include "fwanimation/directorcomponentragdoll.h"
|
|
#include "fwanimation/directorcomponentsyncedscene.h"
|
|
|
|
// project includes
|
|
#include "animation/debug/AnimDebug.h"
|
|
#include "modelinfo/PedModelInfo.h"
|
|
#include "ModelInfo/VehicleModelInfo.h"
|
|
#include "Network/Objects/Entities/NetObjPlayer.h"
|
|
#include "Objects/Door.h"
|
|
#include "Peds\Ped.h"
|
|
#include "Peds\PedIntelligence.h"
|
|
#include "Peds/PedIKSettings.h"
|
|
#include "Physics\Physics.h"
|
|
#include "scene/entity.h"
|
|
#include "scene/Physical.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "task/motion/Locomotion/TaskMotionPed.h"
|
|
#include "task/Motion/Locomotion/TaskMotionAiming.h"
|
|
#include "Task/System/MotionTaskData.h"
|
|
#include "Task/Weapons/TaskProjectile.h"
|
|
#include "vehicles/vehicle.h"
|
|
#include "Weapons/Info/WeaponAnimationsManager.h"
|
|
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/gameplay/aim/FirstPersonShooterCamera.h"
|
|
#include "camera/gameplay/GameplayDirector.h"
|
|
|
|
#include "Task/Movement/Jumping/TaskJump.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_MOTION_OPTIMISATIONS()
|
|
AI_MOVEMENT_OPTIMISATIONS()
|
|
|
|
#define USE_LINEAR_INTERPOLATION 1
|
|
|
|
float CTaskMotionBase::ms_fBaseStandSpringStrength = 6.0f;
|
|
float CTaskMotionBase::ms_fBaseStandSpringDamping = -2.0f*rage::Sqrtf(ms_fBaseStandSpringStrength * 9.81f);
|
|
const fwMvClipSetId CTaskMotionBase::ms_AimingStrafingFirstPersonClipSetId("move_ped_strafing_aiming_firstperson",0xBF69108E);
|
|
dev_float CTaskMotionBase::ms_fAccelLimitStd = 30.0f;
|
|
dev_float CTaskMotionBase::ms_fAccelLimitQpd = 60.0f;
|
|
dev_float CTaskMotionBase::ms_fAccelLimitHigher = 100.0f;
|
|
dev_float CTaskMotionBase::ms_fAccelLimitDead = 0.5f;
|
|
dev_float CTaskMotionBase::ms_fAccelLimitPushedByDoor = 0.1f;
|
|
|
|
#if __BANK
|
|
bool CTaskMotionBase::ms_bLockPlayerInput = false;
|
|
#endif // __BANK
|
|
#if __BANK
|
|
sysPerformanceTimer * CTaskMotionBase::m_pPerfTimer = NULL;
|
|
float CTaskMotionBase::ms_fUpdateTimeRunningTotal = 0.0f;
|
|
float CTaskMotionBase::ms_fUpdateTimeForAllPeds = 0.0f;
|
|
#endif
|
|
|
|
CTaskMotionBase::CTaskMotionBase()
|
|
: m_defaultOnFootClipSet(CLIP_SET_ID_INVALID)
|
|
, m_overrideStrafingClipSet(CLIP_SET_ID_INVALID)
|
|
, m_overrideCrouchedStrafingClipSet(CLIP_SET_ID_INVALID)
|
|
, m_defaultSwimmingClipSet(CLIP_SET_ID_INVALID)
|
|
, m_defaultDivingClipSet(CLIP_SET_ID_INVALID)
|
|
, m_overrideWeaponClipSet(CLIP_SET_ID_INVALID)
|
|
, m_overrideInVehicleContext(0)
|
|
, m_requestedCloneStateChange(-1)
|
|
, m_lastRequestedCloneState(-1)
|
|
, m_onFootBlendDuration(SLOW_BLEND_DURATION)
|
|
, m_fVelocityRotation(FLT_MAX)
|
|
, m_uLastTimeVelocityRotationActive(0)
|
|
, m_uLastTimeVelocityRotationInactive(0)
|
|
, m_alternateClipsLooping(0)
|
|
, m_GaitReductionBlockedOnCollision(false)
|
|
, m_wantsToDiveUnderwater(false)
|
|
, m_isFullyInVehicle(false)
|
|
, m_isFullyAiming(false)
|
|
, m_CleanupMotionTaskNetworkOnQuit(true)
|
|
, m_UseVelocityOverride(false)
|
|
, m_VelocityOverride(Vector3::ZeroType)
|
|
, m_overrideUsesUpperBodyShadowExpression(false)
|
|
, m_isUsingInjuredMovementOverride(false)
|
|
, m_WaitingForTargetState(false)
|
|
, m_CapsuleResults(1)
|
|
{
|
|
#if __BANK
|
|
m_bHasBeenDeleted = false;
|
|
#endif
|
|
}
|
|
|
|
CTaskMotionBase::~CTaskMotionBase()
|
|
{
|
|
#if __BANK
|
|
m_bHasBeenDeleted = true;
|
|
#endif
|
|
}
|
|
|
|
static Vec3V_Out sRotateAroundAxis(Vec3V_In rotVecV, Vec3V_In unitAxisV, ScalarV_In rotAmountV)
|
|
{
|
|
// What we will be doing here is the same as this, but should be faster by doing all
|
|
// the work in the vector pipeline with no floats or memory usage.
|
|
// Vector3 tmp = VEC3V_TO_VECTOR3(rotVecV);
|
|
// Matrix34 matNew;
|
|
// matNew.MakeRotateUnitAxis(VEC3V_TO_VECTOR3(unitAxisV), rotAmountV.Getf());
|
|
// matNew.Transform3x3(tmp);
|
|
|
|
// Set up some constants. Unfortunately there is no V_HALF_PI.
|
|
const ScalarV halfPiV(Vec::V4VConstantSplat<FLOAT_TO_INT(0.5f*PI)>());
|
|
const Vec4V zeroV(V_ZERO);
|
|
|
|
// Prepare for sine/cosine computation.
|
|
const Vec4V anglesCosSinV = MergeXY(Vec4V(halfPiV), zeroV);
|
|
const Vec4V anglesV = rage::Add(Vec4V(rotAmountV), anglesCosSinV);
|
|
|
|
// Compute the sine and cosine.
|
|
const Vec4V cosSinCosSinV = Sin(anglesV);
|
|
const ScalarV cosV = cosSinCosSinV.GetX();
|
|
const ScalarV sinV = cosSinCosSinV.GetY();
|
|
|
|
// What we are doing here is basically computing the axes a, b, and c
|
|
// of a matrix for rotating around the axis, adapted from Matrix34::MakeRotateUnitAxis():
|
|
// float omc = 1.0f - cos;
|
|
// Vector3 vscaled = omc*v;
|
|
// Vector3 vsin = sin*v;
|
|
// a.x=vscaled.x*v.x + cos;
|
|
// a.y=vscaled.x*v.y + vsin.z;
|
|
// a.z=vscaled.x*v.z - vsin.y;
|
|
// b.x=vscaled.x*v.y - vsin.z;
|
|
// b.y=vscaled.y*v.y + cos;
|
|
// b.z=vscaled.y*v.z + vsin.x;
|
|
// c.x=vscaled.x*v.z + vsin.y;
|
|
// c.y=vscaled.y*v.z - vsin.x;
|
|
// c.z=vscaled.z*v.z + cos;
|
|
|
|
const Vec3V axisTimesOneMinusCosV = SubtractScaled(unitAxisV, unitAxisV, cosV);
|
|
const Vec3V axisTimesSinV = Scale(unitAxisV, sinV);
|
|
const Vec3V negAxisTimesSinV = SubtractScaled(Vec3V(V_ZERO), unitAxisV, sinV);
|
|
|
|
// Note: the stuff below is probably not optimal, could probably be smarter about
|
|
// how we build the vectors. Probably not worth the effort, though.
|
|
|
|
const Vec3V pV(axisTimesOneMinusCosV.GetY(), axisTimesOneMinusCosV.GetY(), axisTimesOneMinusCosV.GetZ());
|
|
const Vec3V qV(unitAxisV.GetY(), unitAxisV.GetZ(), unitAxisV.GetZ());
|
|
const Vec3V pqV = Scale(pV, qV);
|
|
|
|
const Vec3V a1V = Scale(axisTimesOneMinusCosV.GetX(), unitAxisV);
|
|
const Vec3V b1V(a1V.GetY(), pqV.GetX(), pqV.GetY());
|
|
const Vec3V c1V(a1V.GetZ(), pqV.GetY(), pqV.GetZ());
|
|
|
|
const Vec3V a2V(cosV, axisTimesSinV.GetZ(), negAxisTimesSinV.GetY());
|
|
const Vec3V b2V(negAxisTimesSinV.GetZ(), cosV, axisTimesSinV.GetX());
|
|
const Vec3V c2V(axisTimesSinV.GetY(), negAxisTimesSinV.GetX(), cosV);
|
|
|
|
const Vec3V aV = Add(a1V, a2V);
|
|
const Vec3V bV = Add(b1V, b2V);
|
|
const Vec3V cV = Add(c1V, c2V);
|
|
|
|
const Vec3V sumAV = Scale(aV, rotVecV.GetX());
|
|
const Vec3V sumABV = AddScaled(sumAV, bV, rotVecV.GetY());
|
|
const Vec3V sumABC = AddScaled(sumABV, cV, rotVecV.GetZ());
|
|
|
|
return sumABC;
|
|
}
|
|
|
|
|
|
Vec3V_Out CTaskMotionBase::CalcDesiredVelocity(Mat34V_ConstRef updatedPedMatrix, float fTimestep)
|
|
{
|
|
TUNE_GROUP_FLOAT(PED_MOVEMENT, fMaxSlopeAngleCos, 0.6428f, 0.0f, 1.0f, 0.0001f); // 50 degs
|
|
TUNE_GROUP_FLOAT(PED_MOVEMENT, fIslandHeistSlopeAngle, 0.8f, 0.0f, 1.0f, 0.0001f);
|
|
|
|
|
|
CPed* pPed = GetPed();
|
|
|
|
#if __ASSERT
|
|
|
|
Vector3 vPedVel = pPed->GetVelocity();
|
|
Assertf(vPedVel == vPedVel, "Ped's existing velocity was already invalid (NaN) before CTaskMotionBase.");
|
|
|
|
bool bLogDebug = false;
|
|
if ( !(MagSquared(VECTOR3_TO_VEC3V(vPedVel)).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f)) )
|
|
{
|
|
Displayf("[0] Ped's %s current velocity Magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(VECTOR3_TO_VEC3V(vPedVel)).Getf());
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
Vec3V extractedVelV;
|
|
if(m_UseVelocityOverride)
|
|
{
|
|
extractedVelV = RCC_VEC3V(m_VelocityOverride);
|
|
}
|
|
else
|
|
{
|
|
extractedVelV = VECTOR3_TO_VEC3V(pPed->GetAnimatedVelocity());
|
|
}
|
|
|
|
const Vec3V mtrxAV = updatedPedMatrix.GetCol0();
|
|
const Vec3V mtrxBV = updatedPedMatrix.GetCol1();
|
|
|
|
const ScalarV extractedXV = extractedVelV.GetX();
|
|
const ScalarV extractedYV = extractedVelV.GetY();
|
|
Vec3V velocityV = Scale(mtrxAV, extractedXV);
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[1] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
velocityV = AddScaled(velocityV, mtrxBV, extractedYV);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[2] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
if(pPed->GetUseExtractedZ())
|
|
{
|
|
const Vec3V mtrxCV = updatedPedMatrix.GetCol2();
|
|
const ScalarV extractedZV = extractedVelV.GetZ();
|
|
velocityV = AddScaled(velocityV, mtrxCV, extractedZV);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[3] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Assertf(IsFiniteAll(velocityV), "NaNs after multiplying the animated velocity by the ped's matrix.");
|
|
|
|
const ScalarV timeStepV(fTimestep);
|
|
|
|
Vec3V desiredAngVelV = RCC_VEC3V(pPed->GetDesiredAngularVelocity());
|
|
if(!IsZeroAll(desiredAngVelV))
|
|
{
|
|
if(pPed->GetGroundPhysical())
|
|
{
|
|
desiredAngVelV = Subtract(desiredAngVelV, pPed->GetGroundAngularVelocity());
|
|
}
|
|
|
|
const ScalarV turnAmountSqV = MagSquared(desiredAngVelV);
|
|
const ScalarV thresholdSqV(Vec::V4VConstantSplat<FLOAT_TO_INT(1e-10f)>()); // square(0.00001f);
|
|
if(IsGreaterThanAll(turnAmountSqV, thresholdSqV))
|
|
{
|
|
// TODO: could potentially have used some Normalize() function here instead.
|
|
const ScalarV turnAmountV = Sqrt(turnAmountSqV);
|
|
const Vec3V turnAxisV = InvScale(desiredAngVelV, turnAmountV);
|
|
const ScalarV turnAmountThisFrameV = Scale(turnAmountV, timeStepV);
|
|
velocityV = sRotateAroundAxis(velocityV, turnAxisV, turnAmountThisFrameV);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[4] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
TUNE_GROUP_BOOL(PED_MOVEMENT, DO_FPS_VELOCITY_ROTATION, true);
|
|
bool bFPSModeEnabled = pPed->IsFirstPersonShooterModeEnabledForPlayer(false, true, true);
|
|
if(pPed->IsPlayer() && DO_FPS_VELOCITY_ROTATION && !pPed->GetMotionData()->GetCombatRoll())
|
|
{
|
|
if(m_fVelocityRotation == FLT_MAX)
|
|
m_fVelocityRotation = pPed->GetCurrentHeading();
|
|
|
|
Vector3 v(VEC3_ZERO);
|
|
|
|
if(bFPSModeEnabled &&
|
|
!pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableMotionBaseVelocityOverride) &&
|
|
(GetTaskType() == CTaskTypes::TASK_HUMAN_LOCOMOTION || GetTaskType() == CTaskTypes::TASK_MOTION_AIMING) && (pPed->IsNetworkClone() || pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_PLAYER_ON_FOOT) || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsLanding)) &&
|
|
!pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableIdleExtraHeadingChange))
|
|
{
|
|
float fDesiredHeading = pPed->GetDesiredHeading();
|
|
if(pPed->GetMotionData()->GetIsStrafing())
|
|
{
|
|
Vector3 vDesiredMBR(pPed->GetMotionData()->GetDesiredMoveBlendRatio().x, pPed->GetMotionData()->GetDesiredMoveBlendRatio().y, 0.f);
|
|
vDesiredMBR.Normalize();
|
|
fDesiredHeading = rage::Atan2f(-vDesiredMBR.x, vDesiredMBR.y);
|
|
fDesiredHeading = fDesiredHeading + pPed->GetCurrentHeading();
|
|
fDesiredHeading = fwAngle::LimitRadianAngle(fDesiredHeading);
|
|
}
|
|
|
|
bool bPassDesiredMoveRatioCheck = false;
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsLanding) || pPed->GetIsFPSSwimming())
|
|
{
|
|
CControl *pControl = pPed->GetControlFromPlayer();
|
|
if(pControl)
|
|
{
|
|
Vector3 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm(), 0.0f);
|
|
|
|
float fMag = vStickInput.Mag();
|
|
if(fMag > 0.01f)
|
|
{
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(*pPed);
|
|
float fStickAngle = rage::Atan2f(-vStickInput.x, vStickInput.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
fDesiredHeading = fStickAngle;
|
|
if (!pPed->GetIsFPSSwimming())
|
|
{
|
|
bPassDesiredMoveRatioCheck = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pPed->GetMotionData()->GetDesiredMoveBlendRatio().Mag2() > 0.0001f || bPassDesiredMoveRatioCheck)
|
|
{
|
|
TUNE_GROUP_INT(PED_MOVEMENT, FPS_VelocityRotation_BlendInTime, 1000, 0, 10000, 1);
|
|
|
|
// Blend in velocity rotation
|
|
u32 uTimeActive = fwTimer::GetTimeInMilliseconds() - m_uLastTimeVelocityRotationInactive;
|
|
if(uTimeActive < FPS_VelocityRotation_BlendInTime)
|
|
{
|
|
const float fLerpRatio = (float)uTimeActive / (float)FPS_VelocityRotation_BlendInTime;
|
|
m_fVelocityRotation = fwAngle::LerpTowards(m_fVelocityRotation, fDesiredHeading, fLerpRatio);
|
|
}
|
|
else
|
|
{
|
|
TUNE_GROUP_FLOAT(PED_MOVEMENT, FPS_VelocityRotation_LerpRate, 200.f, 0.f, 10000.f, 0.0001f);
|
|
m_fVelocityRotation = fwAngle::Lerp(m_fVelocityRotation, fDesiredHeading, FPS_VelocityRotation_LerpRate * fTimestep);
|
|
}
|
|
}
|
|
|
|
// FPS Swimming velocity slow down code:
|
|
bool bIsFPSSwimStrafingUnderwater = false;
|
|
bool bUsingLerpedSwimSlowDownVelocity = false;
|
|
if (pPed->GetIsSwimming())
|
|
{
|
|
CTaskMotionBase* pPrimaryTask = pPed->GetPrimaryMotionTask();
|
|
if( pPrimaryTask && pPrimaryTask->GetTaskType() == CTaskTypes::TASK_MOTION_PED )
|
|
{
|
|
Vector3 vSwimVel(VEC3_ZERO);
|
|
CTaskMotionPed* pTask = static_cast<CTaskMotionPed*>(pPrimaryTask);
|
|
bIsFPSSwimStrafingUnderwater = pTask && pTask->CheckForDiving();
|
|
|
|
// If we're in the aiming motion task (strafing) and have no input but still have a velocity, gradually ramp it down to 0 (so we glide)
|
|
// Fixes B*1991079 - large Z velocity pop when transitioning from diving to strafe idle
|
|
if (pTask && pPed->GetPedResetFlag(CPED_RESET_FLAG_FPSSwimUseAimingMotionTask))
|
|
{
|
|
CControl *pControl = pPed->GetControlFromPlayer();
|
|
if (pControl)
|
|
{
|
|
TUNE_GROUP_FLOAT(FPS_SWIMMING, fInputThresholdToLerpDownVelocity, 0.1f, 0.0f, 1.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(FPS_SWIMMING, fVelocityThresholdToLerpDown, 0.01f, 0.0f, 1.0f, 0.01f);
|
|
|
|
Vector2 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
|
|
Vector3 vCachedSwimVelocity = pTask->GetCachedSwimVelocity();
|
|
Vector3 vCachedSwimStrafeVelocity = pTask->GetCachedSwimStrafeVelocity();
|
|
|
|
if (vStickInput.Mag() < fInputThresholdToLerpDownVelocity && (vCachedSwimVelocity.Mag() > fVelocityThresholdToLerpDown || vCachedSwimStrafeVelocity.Mag() > fVelocityThresholdToLerpDown))
|
|
{
|
|
// Dampen velocity coming from TaskInWater
|
|
if (vCachedSwimVelocity.Mag() > fVelocityThresholdToLerpDown)
|
|
{
|
|
TUNE_GROUP_FLOAT(FPS_SWIMMING, fSwimSlowDownRate, 0.075f, 0.0f, 1.0f, 0.01f);
|
|
vCachedSwimVelocity.x = Lerp(fSwimSlowDownRate, vCachedSwimVelocity.x, 0.0f);
|
|
vCachedSwimVelocity.y = Lerp(fSwimSlowDownRate, vCachedSwimVelocity.y, 0.0f);
|
|
if (bIsFPSSwimStrafingUnderwater)
|
|
vCachedSwimVelocity.z = Lerp(fSwimSlowDownRate, vCachedSwimVelocity.z, 0.0f);
|
|
else
|
|
vCachedSwimVelocity.z = 0.0f;
|
|
pTask->SetCachedSwimVelocity(vCachedSwimVelocity);
|
|
pTask->SetCachedSwimStrafeVelocity(VEC3_ZERO);
|
|
vSwimVel = vCachedSwimVelocity;
|
|
|
|
// Don't do the velocity scaling and rotation based on heading when using a cached velocity from the swim/diving task
|
|
v = vSwimVel;
|
|
bUsingLerpedSwimSlowDownVelocity = true;
|
|
}
|
|
// Dampen velocity from TaskMotionAiming (so we don't instantly stop)
|
|
else if (vCachedSwimStrafeVelocity.Mag() > fVelocityThresholdToLerpDown)
|
|
{
|
|
TUNE_GROUP_FLOAT(FPS_SWIMMING, fSwimStrafeSlowDownRate, 0.075f, 0.0f, 1.0f, 0.01f);
|
|
vCachedSwimStrafeVelocity.x = Lerp(fSwimStrafeSlowDownRate, vCachedSwimStrafeVelocity.x, 0.0f);
|
|
vCachedSwimStrafeVelocity.y = Lerp(fSwimStrafeSlowDownRate, vCachedSwimStrafeVelocity.y, 0.0f);
|
|
if (bIsFPSSwimStrafingUnderwater)
|
|
vCachedSwimStrafeVelocity.z = Lerp(fSwimStrafeSlowDownRate, vCachedSwimStrafeVelocity.z, 0.0f);
|
|
else
|
|
vCachedSwimStrafeVelocity.z = 0.0f;
|
|
pTask->SetCachedSwimStrafeVelocity(vCachedSwimStrafeVelocity);
|
|
pTask->SetCachedSwimVelocity(VEC3_ZERO);
|
|
vSwimVel = vCachedSwimStrafeVelocity;
|
|
|
|
velocityV = VECTOR3_TO_VEC3V(vSwimVel);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[5] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pTask->SetCachedSwimVelocity(VEC3_ZERO);
|
|
pTask->SetCachedSwimStrafeVelocity(VEC3_ZERO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!bUsingLerpedSwimSlowDownVelocity)
|
|
{
|
|
v.SetY(Mag(velocityV).Getf());
|
|
Matrix34 m;
|
|
m.MakeRotateZ(m_fVelocityRotation);
|
|
if (bIsFPSSwimStrafingUnderwater)
|
|
{
|
|
// Rotate local X based on camera pitch if we're swim strafing (to give us Z velocity in our movement)
|
|
float fCamPitch = camInterface::GetPlayerControlCamPitch(*pPed);
|
|
m.RotateLocalX(-fCamPitch);
|
|
}
|
|
m.Transform3x3(v);
|
|
}
|
|
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL(FPS_SWIMMING, bEnableVelocityRender, false);
|
|
if(bEnableVelocityRender)
|
|
{
|
|
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()) + v, 0.25f, Color_blue1, false);
|
|
}
|
|
#endif //__BANK
|
|
|
|
velocityV = VECTOR3_TO_VEC3V(v);
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[7] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
m_uLastTimeVelocityRotationActive = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
const float fCurrentHeading = pPed->GetCurrentHeading();
|
|
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping))
|
|
{
|
|
TUNE_GROUP_INT(PED_MOVEMENT, FPS_VelocityRotation_BlendOutTime, 100, 0, 10000, 1);
|
|
|
|
// Blend out velocity rotation
|
|
u32 uTimeActive = fwTimer::GetTimeInMilliseconds() - m_uLastTimeVelocityRotationActive;
|
|
if(uTimeActive < FPS_VelocityRotation_BlendOutTime)
|
|
{
|
|
const float fLerpRatio = (float)uTimeActive / (float)FPS_VelocityRotation_BlendOutTime;
|
|
m_fVelocityRotation = fwAngle::LerpTowards(m_fVelocityRotation, fCurrentHeading, fLerpRatio);
|
|
|
|
v.SetY(Mag(velocityV).Getf());
|
|
Matrix34 m;
|
|
m.MakeRotateZ(m_fVelocityRotation);
|
|
m.Transform3x3(v);
|
|
velocityV = VECTOR3_TO_VEC3V(v);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[8] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_fVelocityRotation = fCurrentHeading;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fVelocityRotation = fCurrentHeading;
|
|
}
|
|
|
|
m_uLastTimeVelocityRotationInactive = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
//Account for the ground.
|
|
velocityV = Add(velocityV, pPed->GetGroundVelocityIntegrated());
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[9] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
// account for the velocity of our synced scene (if we're in one)
|
|
if (pPed->GetAnimDirector())
|
|
{
|
|
fwAnimDirectorComponentSyncedScene* pComponent = static_cast<fwAnimDirectorComponentSyncedScene*>(pPed->GetAnimDirector()->GetComponentByType(fwAnimDirectorComponent::kComponentTypeSyncedScene));
|
|
if (pComponent && pComponent->IsPlayingSyncedScene())
|
|
{
|
|
Vec3V sceneVel;
|
|
pComponent->GetSceneVelocity(sceneVel);
|
|
|
|
velocityV = Add(velocityV, sceneVel);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[10] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
//***************************************
|
|
// Stop peds walking up very steep slopes
|
|
|
|
CPhysical* pPhysical = pPed->GetGroundPhysical();
|
|
if(!pPhysical && pPed->GetPedResetFlag(CPED_RESET_FLAG_IsClimbing)) // Check reset flag to avoid the GetClimbPhysical() call in the common case.
|
|
{
|
|
pPhysical = pPed->GetClimbPhysical();
|
|
}
|
|
|
|
//pPed->SetPedResetFlag(CPED_RESET_FLAG_PedStoppedOnSteepSlope, false);
|
|
if( ((pPed->IsPlayer() && (pPed->GetMotionData()->GetPlayerHasControlOfPedThisFrame() || pPed->GetMotionData()->GetForceSteepSlopeTestThisFrame()))
|
|
|| pPed->GetPedResetFlag(CPED_RESET_FLAG_EnableSteepSlopePrevention)) && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsClimbingLadder) )
|
|
{
|
|
#if HACK_GTA4_BOUND_GEOM_SECOND_SURFACE
|
|
|
|
#define MAX_SNOW_DEPTH 1.0f
|
|
static bank_float sfMaxSlopeAngleCosInSnow = 0.85f; // 31.7 degs
|
|
static bank_float sfMaxSlopeDeepSnowRatio = 0.5f;
|
|
const float fSecondSurfaceDepthRatio = pPed->GetSecondSurfaceDepth() / MAX_SNOW_DEPTH;
|
|
const bool bSurfaceTooSteep = pPed->GetMaxGroundNormal().z < sfMaxSlopeAngleCos && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_OnStairs);
|
|
const bool bSecondSurfaceTooSteep = pPed->GetMaxGroundNormal().z < sfMaxSlopeAngleCosInSnow && (fSecondSurfaceDepthRatio > sfMaxSlopeDeepSnowRatio);
|
|
if(bSurfaceTooSteep || bSecondSurfaceTooSteep)
|
|
#else
|
|
|
|
const float fMaxSlopeAngleTemp = (ThePaths.bStreamHeistIslandNodes && pPed->GetPedResetFlag(CPED_RESET_FLAG_TooSteepForPlayer)) ? fIslandHeistSlopeAngle : fMaxSlopeAngleCos;
|
|
const bool bSurfaceTooSteep = pPed->GetMaxGroundNormal().GetZf() < fMaxSlopeAngleTemp && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_OnStairs);
|
|
if(bSurfaceTooSteep)
|
|
#endif // HACK_GTA4_BOUND_GEOM_SECOND_SURFACE
|
|
{
|
|
if(pPhysical && pPhysical->GetIsTypeVehicle() &&
|
|
(((CVehicle*)pPhysical)->GetVehicleType()==VEHICLE_TYPE_BOAT || ((CVehicle*)pPhysical)->GetVehicleType()==VEHICLE_TYPE_TRAIN || ((CVehicle*)pPhysical)->GetVehicleType()==VEHICLE_TYPE_PLANE))
|
|
{
|
|
pPed->SetSteepSlopePos(VEC3_ZERO);
|
|
}
|
|
else if(pPed->GetSteepSlopePos().IsZero())
|
|
{
|
|
pPed->SetSteepSlopePos(pPed->GetGroundPos());
|
|
}
|
|
else
|
|
{
|
|
Vector3 vecSlopeDirn = pPed->GetGroundNormal();
|
|
vecSlopeDirn.z = 0.0f;
|
|
vecSlopeDirn.Normalize();
|
|
Vector3 vecMoveDirn = pPed->GetGroundPos() - pPed->GetSteepSlopePos();
|
|
if(vecMoveDirn.Dot(vecSlopeDirn) < -0.2f)
|
|
{
|
|
const ScalarV negDesiredVelIntoSlopeV = Dot(VECTOR3_TO_VEC3V(vecSlopeDirn), velocityV);
|
|
const ScalarV zeroV(V_ZERO);
|
|
if(IsLessThanAll(negDesiredVelIntoSlopeV, zeroV))
|
|
{
|
|
//pPed->SetPedResetFlag(CPED_RESET_FLAG_PedStoppedOnSteepSlope, true);
|
|
velocityV = SubtractScaled(velocityV, VECTOR3_TO_VEC3V(vecSlopeDirn), negDesiredVelIntoSlopeV);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[11] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
PDR_ONLY(debugPlayback::RecordTaggedFloatValue(*pPed->GetCurrentPhysicsInst(), negDesiredVelIntoSlopeV.Getf(), "SteepSlope_VelocityNegation"));
|
|
PDR_ONLY(debugPlayback::RecordTaggedVectorValue(*pPed->GetCurrentPhysicsInst(), velocityV, "SteepSlope_FinalVelocity", debugPlayback::eVectorTypeVelocity));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we're jumping CPED_CONFIG_FLAG_OnStairSlope will get set before we're actually on the ground and getting
|
|
// a valid max ground normal. If this happens we end up subtracting out the entire vertical portion of the velocity.
|
|
if (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_OnStairSlope) && pPed->IsOnGround())
|
|
{
|
|
//Adjust velocity along slope
|
|
Vec3V vNormal = pPed->GetMaxGroundNormal();
|
|
ScalarV vDotWithNormal = Dot(velocityV, vNormal);
|
|
Vec3V vUp(0.0f,0.0f,1.0f);
|
|
velocityV -= vUp * vDotWithNormal;
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[12] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
pPed->SetSteepSlopePos(VEC3_ZERO);
|
|
}
|
|
}
|
|
|
|
PDR_ONLY(debugPlayback::RecordPedAnimatedVelocity(pPed->GetCurrentPhysicsInst(), velocityV));
|
|
|
|
//if gait reducing at a full stop, apply a small velocity to try and push things.
|
|
if (IsGaitReduced() && GetMotionData().GetGaitReducedMaxMoveBlendRatio()==MOVEBLENDRATIO_STILL && !GetMotionData().GetGaitReductionFlag(CPedMotionData::GR_HitIncline))
|
|
{
|
|
const ScalarV sGR_EXTRAVELO_V(V_FLT_SMALL_1); // 0.1f
|
|
const Vec3V fwdV = pPed->GetMatrixRef().GetCol1();
|
|
velocityV = AddScaled(velocityV, fwdV, sGR_EXTRAVELO_V);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001), "[13] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const Vec3V currentPedVelocityV = VECTOR3_TO_VEC3V(pPed->GetVelocity());
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(currentPedVelocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[14] Ped: %s currentPedVelocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(currentPedVelocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
// Now see if we are allowed this change in velocity
|
|
Vec3V velChangeV = Subtract(velocityV, currentPedVelocityV);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velChangeV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[15] Ped: %s velChangeV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velChangeV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
bool standingInsideVehicle = false;
|
|
|
|
if(pPhysical)
|
|
{
|
|
if(pPhysical->GetIsTypeVehicle() &&
|
|
((CVehicle*)pPhysical)->CanPedsStandOnTop())
|
|
{
|
|
standingInsideVehicle = true;
|
|
}
|
|
}
|
|
|
|
// don't clamp the velocity change for network clones, the network blending code prevents
|
|
// large velocity changes in a frame but needs to know the target velocity for the ped
|
|
TUNE_GROUP_BOOL(PED_MOVEMENT, FORCE_FPS_VEL_CLAMP, false);
|
|
static dev_bool sbUseVelocityClamp = true;
|
|
if (sbUseVelocityClamp && (!standingInsideVehicle || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping)) && (!pPed->IsNetworkClone() || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping))
|
|
#if FPS_MODE_SUPPORTED
|
|
&& ( !bFPSModeEnabled || (bFPSModeEnabled && (pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping) || GetTaskType() != CTaskTypes::TASK_MOTION_AIMING || FORCE_FPS_VEL_CLAMP)))
|
|
#endif // FPS_MODE_SUPPORTED
|
|
)
|
|
{
|
|
velChangeV = CalcVelChangeLimitAndClamp(*pPed, velChangeV, timeStepV, pPhysical);
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velChangeV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[16] Ped: %s velChangeV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velChangeV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
}
|
|
else if(!pPed->GetUseExtractedZ())
|
|
{
|
|
velChangeV.SetZ(ScalarV(V_ZERO));
|
|
}
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(currentPedVelocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[17] Ped's current velocity Magnitude2 was invalid (%f).",
|
|
MagSquared(currentPedVelocityV).Getf()))
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
|
|
if (!Verifyf(pPed->IsNetworkClone() ||
|
|
MagSquared(velChangeV).Getf() < (DEFAULT_IMPULSE_LIMIT * DEFAULT_IMPULSE_LIMIT + 0.0001f) ||
|
|
DistSquared(VECTOR3_TO_VEC3V(pPed->GetAnimatedVelocity()), VECTOR3_TO_VEC3V(pPed->GetPreviousAnimatedVelocity())).Getf() > DEFAULT_IMPULSE_LIMIT * DEFAULT_IMPULSE_LIMIT, "Ped's velocity change Magnitude2 was invalid (%f). Standing inside vehicle? %d, IndependentMoverTransition is 0? %d",
|
|
MagSquared(velChangeV).Getf(), standingInsideVehicle, pPed->GetMotionData()->GetIndependentMoverTransition() == 0))
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
|
|
if (!Verifyf(MagSquared(Add(currentPedVelocityV, velChangeV)).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[18]Ped's desired velocity is invalid (%f)",
|
|
Mag(Add(currentPedVelocityV, velChangeV)).Getf()))
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
|
|
if(bLogDebug)
|
|
{
|
|
Printf("currentPedVelocityV (%.2f,%.2f,%.2f):%.2f, velChangeV (%.2f,%.2f,%.2f):%.2f, desiredVelocity (%.2f,%.2f,%.2f):%.2f\n", currentPedVelocityV.GetXf(), currentPedVelocityV.GetYf(), currentPedVelocityV.GetZf(), Mag(currentPedVelocityV).Getf(), velChangeV.GetXf(), velChangeV.GetYf(), velChangeV.GetZf(), Mag(velChangeV).Getf(), pPed->GetDesiredVelocity().x, pPed->GetDesiredVelocity().y, pPed->GetDesiredVelocity().z, pPed->GetDesiredVelocity().Mag());
|
|
Printf("Tasks:\n");
|
|
pPed->GetPedIntelligence()->PrintTasks();
|
|
Printf("Script task history:\n");
|
|
pPed->GetPedIntelligence()->PrintScriptTaskHistory();
|
|
Printf("Anims:\n");
|
|
CAnimDebug::LogAnimations(*pPed);
|
|
}
|
|
#endif
|
|
|
|
velocityV = Add(currentPedVelocityV, velChangeV);
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(MagSquared(velocityV).Getf() < (DEFAULT_VELOCITY_LIMIT * DEFAULT_VELOCITY_LIMIT + 0.0001f), "[19] Ped: %s velocityV magnitude2 was invalid (%f).", pPed->GetModelName(), MagSquared(velocityV).Getf()) )
|
|
{
|
|
bLogDebug = true;
|
|
}
|
|
#endif
|
|
|
|
NetworkInterface::OnDesiredVelocityCalculated(*pPed);
|
|
|
|
return velocityV;
|
|
}
|
|
|
|
Vec3V_Out CTaskMotionBase::CalcVelChangeLimitAndClamp(const CPed& ped, Vec3V_In changeInV, ScalarV_In timestepV, const CPhysical* pGroundPhysical)
|
|
{
|
|
Vec3V changeV = changeInV;
|
|
if(!ped.GetUseExtractedZ())
|
|
{
|
|
changeV.SetZ(ScalarV(V_ZERO));
|
|
}
|
|
|
|
// If we're using low lod physics then don't want a vel change limit
|
|
if(ped.GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodPhysics))
|
|
{
|
|
return changeV;
|
|
}
|
|
|
|
static bank_float sfVelChangeLimitFallen = 10.0f;
|
|
static bank_float sfVelChangeLimitFallenOnVehicle = 1.0f;
|
|
|
|
const float* pChangeLimitPtr;
|
|
// if a non-ragdoll'ing dead or dying ped is on top of a vehicle, make sure they slide off easily
|
|
if(ped.IsInjured() && pGroundPhysical && pGroundPhysical->GetIsTypeVehicle() &&
|
|
!static_cast<const CVehicle*>(pGroundPhysical)->CanPedsStandOnTop())
|
|
{
|
|
pChangeLimitPtr = &ms_fAccelLimitDead;
|
|
}
|
|
// if this ped is being pushed by a door that the player is pushing from the other side, make them slide out of the way easily
|
|
else if(ped.m_PedResetFlags.GetKnockedByDoor() > 0)
|
|
{
|
|
pChangeLimitPtr = &ms_fAccelLimitPushedByDoor;
|
|
}
|
|
else if(ped.GetPedResetFlag(CPED_RESET_FLAG_FallenDown))
|
|
{
|
|
bool bSlideOffVehicle = false;
|
|
if(pGroundPhysical && pGroundPhysical->GetIsTypeVehicle())
|
|
{
|
|
const CVehicle* pVeh = static_cast<const CVehicle*>(pGroundPhysical);
|
|
if(!pVeh->CanPedsStandOnTop())
|
|
bSlideOffVehicle = true;
|
|
}
|
|
if(bSlideOffVehicle)
|
|
pChangeLimitPtr = &sfVelChangeLimitFallenOnVehicle;
|
|
else
|
|
pChangeLimitPtr = &sfVelChangeLimitFallen;
|
|
}
|
|
else
|
|
{
|
|
// limit the force the ped applies in the change direction
|
|
// this causes inertia and restricts grip when standing on moving ground
|
|
// Set by the AI to allow a higher limit (reset in CPed::ProcessPhysics)
|
|
if( ped.GetPedResetFlag(CPED_RESET_FLAG_RaiseVelocityChangeLimit) )
|
|
{
|
|
pChangeLimitPtr = &ms_fAccelLimitHigher;
|
|
}
|
|
else
|
|
{
|
|
pChangeLimitPtr = ped.GetCapsuleInfo()->IsQuadruped() ? &ms_fAccelLimitQpd : &ms_fAccelLimitStd;
|
|
}
|
|
}
|
|
|
|
const ScalarV changeLimitBeforeAnimV = LoadScalar32IntoScalarV(*pChangeLimitPtr);
|
|
|
|
// If clip has requested a massive change then allow it
|
|
const ScalarV timestepSqV = Scale(timestepV, timestepV);
|
|
const ScalarV animatedChangeMagSqV = DistSquared(VECTOR3_TO_VEC3V(ped.GetAnimatedVelocity()), VECTOR3_TO_VEC3V(ped.GetPreviousAnimatedVelocity()));
|
|
const ScalarV changeLimitBeforeAnimSqV = Scale(changeLimitBeforeAnimV, changeLimitBeforeAnimV);
|
|
const ScalarV changeLimitTimesTimeStepBeforeAnimSqV = Scale(timestepSqV, changeLimitBeforeAnimSqV);
|
|
const ScalarV timestepTimesLimitSqV = Max(animatedChangeMagSqV, changeLimitTimesTimeStepBeforeAnimSqV);
|
|
|
|
const ScalarV changeMagSqV = MagSquared(changeV);
|
|
if(IsGreaterThanAll(changeMagSqV, timestepTimesLimitSqV))
|
|
{
|
|
// Note: could easily have removed the branch here, but the stuff we do here is much
|
|
// more expensive than the stuff above, and it should be a quite rare case - generally
|
|
// only when the player does something like jumping, so branching may be better.
|
|
const ScalarV scaleV = Sqrt(InvScale(timestepTimesLimitSqV, changeMagSqV));
|
|
changeV = Scale(changeV, scaleV);
|
|
|
|
//! If doing super jump, choose different XY scale.
|
|
if( ped.GetPedResetFlag(CPED_RESET_FLAG_IsJumping) && !ped.GetPedResetFlag(CPED_RESET_FLAG_IsLanding))
|
|
{
|
|
const CTaskJump* jumpTask = static_cast<const CTaskJump*>(ped.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JUMP));
|
|
if (jumpTask && jumpTask->GetIsDoingSuperJump())
|
|
{
|
|
static bank_float sfVelChangeLimitXY = 1.0f;
|
|
pChangeLimitPtr = &sfVelChangeLimitXY;
|
|
|
|
const ScalarV changeLimitBeforeAnimV = LoadScalar32IntoScalarV(*pChangeLimitPtr);
|
|
|
|
// If clip has requested a massive change then allow it
|
|
const ScalarV changeLimitBeforeAnimSqV = Scale(changeLimitBeforeAnimV, changeLimitBeforeAnimV);
|
|
const ScalarV changeLimitTimesTimeStepBeforeAnimSqV = Scale(timestepSqV, changeLimitBeforeAnimSqV);
|
|
const ScalarV timestepTimesLimitSqV = Max(animatedChangeMagSqV, changeLimitTimesTimeStepBeforeAnimSqV);
|
|
|
|
const ScalarV scaleV = Sqrt(InvScale(timestepTimesLimitSqV, changeMagSqV));
|
|
|
|
Vec3V changeVXY = changeInV;
|
|
changeVXY = Scale(changeVXY, scaleV);
|
|
changeV.SetXf(changeVXY.GetXf());
|
|
changeV.SetYf(changeVXY.GetYf());
|
|
}
|
|
}
|
|
}
|
|
|
|
return changeV;
|
|
}
|
|
|
|
float CTaskMotionBase::GetActualMoveSpeed(const float * fMoveSpeeds, float fMoveBlendRatio)
|
|
{
|
|
Assert(fMoveBlendRatio >= MOVEBLENDRATIO_STILL && fMoveBlendRatio <= MOVEBLENDRATIO_SPRINT);
|
|
fMoveBlendRatio = Clamp(fMoveBlendRatio, MOVEBLENDRATIO_STILL, MOVEBLENDRATIO_SPRINT);
|
|
|
|
float i;
|
|
const float fRemainderY = rage::Modf(fMoveBlendRatio, &i);
|
|
|
|
const int index = (int)fMoveBlendRatio;
|
|
|
|
// DLH TODO
|
|
// Explicitly changed this to branch here as Selectf has no modern
|
|
// intrinsic implementation in the codebase so provided no speed up
|
|
// and resulted in evaluating fMoveSpeeds[index+1] which is an illegal
|
|
// address read when fMoveBlendRatio==3.0f (max speed)
|
|
if ((fRemainderY - 0.01f) > 0)
|
|
{
|
|
const float spd1 = fMoveSpeeds[index];
|
|
const float spd2 = fMoveSpeeds[index+1];
|
|
const float fVal1 = (1.0f - fRemainderY) * spd1;
|
|
const float fVal2 = fRemainderY * spd2;
|
|
return fVal1 + fVal2;
|
|
}
|
|
else
|
|
{
|
|
return fMoveSpeeds[index];
|
|
}
|
|
}
|
|
|
|
void CTaskMotionBase::RetrieveMoveSpeeds(fwClipSet& clipSet, CMoveBlendRatioSpeeds& speeds, const fwMvClipId* moveSpeedClipNames)
|
|
{
|
|
// Query the appropriate clips in the given set and cache the results for later use.
|
|
USE_MEMBUCKET(MEMBUCKET_GAMEPLAY);
|
|
|
|
float* cachedSpeed;
|
|
float speed;
|
|
|
|
cachedSpeed = clipSet.GetProperties().Access(moveSpeedClipNames[0]);
|
|
speed = 0.0f;
|
|
|
|
if (cachedSpeed)
|
|
{
|
|
speeds.SetWalkSpeed(*cachedSpeed);
|
|
}
|
|
else
|
|
{
|
|
if (clipSet.IsStreamedIn_DEPRECATED())
|
|
{
|
|
const crClip* pClip = clipSet.GetClip(moveSpeedClipNames[0]);
|
|
if (pClip)
|
|
{
|
|
speed = fwAnimHelpers::GetMoverTrackVelocity(*pClip).Mag();
|
|
}
|
|
clipSet.GetProperties().Insert(moveSpeedClipNames[0], speed);
|
|
speeds.SetWalkSpeed(speed);
|
|
}
|
|
}
|
|
|
|
cachedSpeed = clipSet.GetProperties().Access(moveSpeedClipNames[1]);
|
|
speed = 0.0f;
|
|
|
|
if (cachedSpeed)
|
|
{
|
|
speeds.SetRunSpeed(*cachedSpeed);
|
|
}
|
|
else
|
|
{
|
|
if (clipSet.IsStreamedIn_DEPRECATED())
|
|
{
|
|
const crClip* pClip = clipSet.GetClip(moveSpeedClipNames[1]);
|
|
if (pClip)
|
|
{
|
|
speed = fwAnimHelpers::GetMoverTrackVelocity(*pClip).Mag();
|
|
}
|
|
clipSet.GetProperties().Insert(moveSpeedClipNames[1], speed);
|
|
speeds.SetRunSpeed(speed);
|
|
}
|
|
}
|
|
|
|
cachedSpeed = clipSet.GetProperties().Access(moveSpeedClipNames[2]);
|
|
speed = 0.0f;
|
|
|
|
if (cachedSpeed)
|
|
{
|
|
speeds.SetSprintSpeed(*cachedSpeed);
|
|
}
|
|
else
|
|
{
|
|
if (clipSet.IsStreamedIn_DEPRECATED())
|
|
{
|
|
const crClip* pClip = clipSet.GetClip(moveSpeedClipNames[2]);
|
|
if (pClip)
|
|
{
|
|
speed = fwAnimHelpers::GetMoverTrackVelocity(*pClip).Mag();
|
|
}
|
|
clipSet.GetProperties().Insert(moveSpeedClipNames[2], speed);
|
|
speeds.SetSprintSpeed(speed);
|
|
}
|
|
}
|
|
}
|
|
|
|
float CTaskMotionBase::GetCameraHeading()
|
|
{
|
|
return GetPed()->GetTransform().GetHeading();
|
|
}
|
|
|
|
// GetMoveBlendRatioForDesiredSpeed()
|
|
// pMoveSpeeds : 4 floats describing the speed for idle,walk,run,sprint for the ped
|
|
// returns the move-blend-ratio to use to get the desired speed
|
|
float CTaskMotionBase::GetMoveBlendRatioForDesiredSpeed(const float * fMoveSpeeds, float fDesiredSpeed)
|
|
{
|
|
float maxSpeed = fMoveSpeeds[0];
|
|
if(fDesiredSpeed < maxSpeed)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
float baseSpeed = 0.0f;
|
|
for(u32 i=0; i<3; i++)
|
|
{
|
|
const float minSpeed = maxSpeed;
|
|
maxSpeed = fMoveSpeeds[i+1];
|
|
if(fDesiredSpeed <= maxSpeed)
|
|
{
|
|
float speed = Range(fDesiredSpeed, minSpeed, maxSpeed);
|
|
Assert(speed >= 0.0f && speed <= 1.0f);
|
|
float ratio = baseSpeed + speed;
|
|
return ratio;
|
|
}
|
|
baseSpeed += 1.0f; // This is just (float)i, to avoid having to do an int->float conversion at the end.
|
|
}
|
|
|
|
return 3.0f;
|
|
}
|
|
|
|
float CTaskMotionBase::CalcDesiredTurnVelocityFromHeadingDelta(const float fHeadingChangeRate, const float fHeadingApproachTime, float fHeadingDelta)
|
|
{
|
|
// Clamp desired turn velocity to a maximum of +/- 180 degrees
|
|
fHeadingDelta = fwAngle::LimitRadianAngleFast(fHeadingDelta);
|
|
|
|
//**********************************************************************************
|
|
// Want some control over how quickly we approach target heading, so divide by the
|
|
// time we want to take to reach desired heading to get speed
|
|
|
|
float fDesiredTurnVelocity = fHeadingDelta / fHeadingApproachTime;
|
|
|
|
if(fDesiredTurnVelocity > fHeadingChangeRate)
|
|
fDesiredTurnVelocity = fHeadingChangeRate;
|
|
else if(fDesiredTurnVelocity < -fHeadingChangeRate)
|
|
fDesiredTurnVelocity = -fHeadingChangeRate;
|
|
|
|
return fDesiredTurnVelocity;
|
|
}
|
|
|
|
|
|
void
|
|
CTaskMotionBase::ApproachCurrentTurnVelocityTowardsDesired(const float fDesiredTurnVelocity, const float fTurnRateAccel, const float fTurnRateDecel, const bool bLessenHdgChangeForSmallAngles, const bool bLessenHdgChangeBySquaring)
|
|
{
|
|
float fScale;
|
|
if(bLessenHdgChangeForSmallAngles)
|
|
{
|
|
// Expecting fDesiredTurnVelocity to be in range -PI to PI.
|
|
// This will produce a value from 1.0 at heading delta of PI, to 0.0 at heading delta of zero
|
|
fScale = (Abs(fDesiredTurnVelocity) / TWO_PI) + 0.5f;
|
|
fScale = Clamp(fScale, 0.0f, 1.0f);
|
|
|
|
if(bLessenHdgChangeBySquaring)
|
|
{
|
|
fScale *= fScale; // Try squaring it ? I really want to stop player controls feeling so jittery
|
|
fScale = Clamp(fScale, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fScale = 1.0f;
|
|
}
|
|
|
|
//************************************************************************
|
|
// Approach the current turn velocity towards the desired turn velocity
|
|
//
|
|
float fCurrentTurnVelocity = GetMotionData().GetCurrentTurnVelocity();
|
|
|
|
// trying to accelerate heading rate in +ve direction
|
|
if(fDesiredTurnVelocity > fCurrentTurnVelocity && fCurrentTurnVelocity > -0.01f)
|
|
{
|
|
fCurrentTurnVelocity += (fTurnRateAccel * fScale) * fwTimer::GetTimeStep();
|
|
// make sure we don't overshoot
|
|
if(fCurrentTurnVelocity > fDesiredTurnVelocity)
|
|
fCurrentTurnVelocity = fDesiredTurnVelocity;
|
|
}
|
|
// trying to accelerate heading rate in -ve direction
|
|
else if(fDesiredTurnVelocity < fCurrentTurnVelocity && fCurrentTurnVelocity < 0.01f)
|
|
{
|
|
fCurrentTurnVelocity -= (fTurnRateAccel * fScale) * fwTimer::GetTimeStep();
|
|
if(fCurrentTurnVelocity < fDesiredTurnVelocity)
|
|
fCurrentTurnVelocity = fDesiredTurnVelocity;
|
|
}
|
|
// trying to slow heading rate
|
|
else if(fDesiredTurnVelocity > fCurrentTurnVelocity)
|
|
{
|
|
fCurrentTurnVelocity += (fTurnRateDecel * fScale) * fwTimer::GetTimeStep();
|
|
if(fCurrentTurnVelocity > fDesiredTurnVelocity)
|
|
fCurrentTurnVelocity = fDesiredTurnVelocity;
|
|
}
|
|
// also trying to slow it
|
|
else
|
|
{
|
|
fCurrentTurnVelocity -= (fTurnRateDecel * fScale) * fwTimer::GetTimeStep();
|
|
if(fCurrentTurnVelocity < fDesiredTurnVelocity)
|
|
fCurrentTurnVelocity = fDesiredTurnVelocity;
|
|
}
|
|
|
|
// Apply
|
|
GetMotionData().SetCurrentTurnVelocity(fCurrentTurnVelocity);
|
|
}
|
|
|
|
float CTaskMotionBase::CalcDesiredDirection() const
|
|
{
|
|
const CPed * pPed = GetPed();
|
|
const float fCurrentHeading = fwAngle::LimitRadianAngleSafe(pPed->GetCurrentHeading());
|
|
const float fDesiredHeading = fwAngle::LimitRadianAngleSafe(pPed->GetDesiredHeading());
|
|
const float fHeadingDelta = SubtractAngleShorter(fDesiredHeading, fCurrentHeading);
|
|
return fHeadingDelta;
|
|
}
|
|
|
|
void CTaskMotionBase::ApproachCurrentMBRTowardsDesired(const float fMoveAccel, const float fMoveDecel, const float fMaxMBR)
|
|
{
|
|
//***************************************
|
|
// Normal (non-strafing) movement
|
|
//
|
|
|
|
CPedMotionData& data = GetMotionData();
|
|
|
|
const float fMinMBR = data.GetIsStrafing() ? -fMaxMBR : 0.0f;
|
|
|
|
const float fDesiredMBR_Y = data.GetDesiredMbrY();
|
|
float fCurrentMBR_Y = data.GetCurrentMbrY();
|
|
if(fDesiredMBR_Y > fCurrentMBR_Y)
|
|
{
|
|
fCurrentMBR_Y += fMoveAccel * fwTimer::GetTimeStep();
|
|
fCurrentMBR_Y = MIN(fCurrentMBR_Y, MIN(fDesiredMBR_Y, fMaxMBR));
|
|
}
|
|
else if(fDesiredMBR_Y < fCurrentMBR_Y)
|
|
{
|
|
fCurrentMBR_Y -= fMoveDecel * fwTimer::GetTimeStep();
|
|
fCurrentMBR_Y = MAX(fCurrentMBR_Y, MAX(fDesiredMBR_Y, fMinMBR));
|
|
}
|
|
else
|
|
{
|
|
fCurrentMBR_Y = fDesiredMBR_Y;
|
|
}
|
|
|
|
//*************************************************
|
|
// If strafing, then approach the X axis as well
|
|
//
|
|
|
|
const float fDesiredMBR_X = data.GetDesiredMbrX();
|
|
float fCurrentMBR_X = data.GetCurrentMbrX();
|
|
if(data.GetIsStrafing())
|
|
{
|
|
if(fDesiredMBR_X > fCurrentMBR_X)
|
|
{
|
|
fCurrentMBR_X += fMoveAccel * fwTimer::GetTimeStep();
|
|
fCurrentMBR_X = MIN(fCurrentMBR_X, MIN(fDesiredMBR_X, fMaxMBR));
|
|
}
|
|
else if(fDesiredMBR_X < fCurrentMBR_X)
|
|
{
|
|
fCurrentMBR_X -= fMoveDecel * fwTimer::GetTimeStep();
|
|
fCurrentMBR_X = MAX(fCurrentMBR_X, MAX(fDesiredMBR_X, fMinMBR));
|
|
}
|
|
else
|
|
{
|
|
fCurrentMBR_X = fDesiredMBR_X;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fCurrentMBR_X = 0.0f;
|
|
}
|
|
|
|
// Set the new move blend ratio
|
|
data.SetCurrentMoveBlendRatio(fCurrentMBR_Y, fCurrentMBR_X);
|
|
}
|
|
|
|
void CTaskMotionBase::ResetGaitReduction()
|
|
{
|
|
GetMotionData().SetGaitReduction(0);
|
|
GetMotionData().SetGaitReductionHeading(0);
|
|
GetMotionData().ResetGaitReductionFlags();
|
|
}
|
|
|
|
void CTaskMotionBase::UpdateGaitReduction()
|
|
{
|
|
m_UseVelocityOverride=false; //clear this here?
|
|
|
|
CPed* pPed = GetPed();
|
|
CPedMotionData* pMotionData = &GetMotionData();
|
|
CTaskMotionBase* pMotionTask = pPed->GetCurrentMotionTask();
|
|
|
|
if(pPed->IsNetworkClone() || pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableGaitReduction) || pMotionTask==NULL ||
|
|
pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodPhysics) || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_StairsDetected)) //disabling on stairs for now, speeds don't match animation going downstairs
|
|
{
|
|
pMotionData->SetGaitReduction(0);
|
|
pMotionData->SetGaitReducedMaxMoveBlendRatio(MOVEBLENDRATIO_SPRINT);
|
|
return; //Low LOD AI don't update their velocity so this is no good.
|
|
}
|
|
|
|
float fMaxMBR = MOVEBLENDRATIO_SPRINT;
|
|
float speed = pPed->GetVelocity().Mag();
|
|
|
|
float clipSpeed = pPed->GetPreviousAnimatedVelocity().Mag();
|
|
float speedRatio = (clipSpeed !=0) ? speed/clipSpeed : 1.0f;
|
|
|
|
CMoveBlendRatioSpeeds moveSpeeds;
|
|
pMotionTask->GetMoveSpeeds(moveSpeeds);
|
|
Vector2 vDesiredMBR;
|
|
pMotionData->GetDesiredMoveBlendRatio(vDesiredMBR.x, vDesiredMBR.y);
|
|
float fWorkingMBR = Max(fabs(vDesiredMBR.x), fabs(vDesiredMBR.y));
|
|
|
|
float currHeading = pMotionData->GetCurrentHeading();
|
|
float headingDelta = fabs(SubtractAngleShorter(pMotionData->GetDesiredHeading(), currHeading));
|
|
|
|
bool bStrafing = pPed->IsStrafing();
|
|
if ( bStrafing &&
|
|
(this->GetTaskType()==CTaskTypes::TASK_MOTION_AIMING
|
|
FPS_MODE_SUPPORTED_ONLY(|| pPed->IsFirstPersonShooterModeEnabledForPlayer(true, true))) )
|
|
{
|
|
if(vDesiredMBR.Mag2() > 0.f)
|
|
{
|
|
vDesiredMBR.Normalize();
|
|
currHeading = fwAngle::LimitRadianAngleSafe(rage::Atan2f(vDesiredMBR.y, vDesiredMBR.x)-HALF_PI);
|
|
}
|
|
}
|
|
|
|
|
|
float fGaitReduction = pMotionData->GetGaitReduction();
|
|
float fGaitReductionHeading = pMotionData->GetGaitReductionHeading();
|
|
//if (pPed->IsLocalPlayer())
|
|
// Displayf("Desired MBR: %f, GaitReduction = %f, spd/aspd: %f, heading Delta: %f", vDesiredMBR.y, fGaitReduction, speedRatio, headingDelta );
|
|
|
|
static float minimumRatio = 0.75f;
|
|
static float minimumHeadingDelta = 1.0f;
|
|
static float totalHeadingDelta = PI/8.0f;
|
|
static u32 blockTime = 1000;
|
|
|
|
bool bNoStand = false;
|
|
bool bReduceWalkRatio = false;
|
|
bool bDidPhysicsCheck = false;
|
|
|
|
bool blockGaitReduction = (speedRatio>minimumRatio && fGaitReduction == 0.0f)
|
|
|| headingDelta > minimumHeadingDelta //Current change
|
|
|| (fGaitReduction && fabs(currHeading - fGaitReductionHeading) > totalHeadingDelta)
|
|
|| pPed->GetPedResetFlag(CPED_RESET_FLAG_PreserveAnimatedAngularVelocity); //total change
|
|
|
|
if (blockGaitReduction)
|
|
{
|
|
if (fGaitReduction != 0.0f) //only when breaking a gait reduction in place
|
|
pMotionData->SetGaitReductionBlockTime();
|
|
}
|
|
else if((pMotionData->GetGaitReductionBlockTime() + blockTime) >= fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
blockGaitReduction = true;
|
|
}
|
|
|
|
//If in a stuck state, see if there is still something in the way
|
|
if (!blockGaitReduction && (fGaitReduction || m_GaitReductionBlockedOnCollision))
|
|
{
|
|
pMotionData->SetGaitReductionFlag(CPedMotionData::GR_HitIncline, false);
|
|
|
|
if(m_CapsuleResults.GetResultsReady())
|
|
{
|
|
bDidPhysicsCheck = true;
|
|
|
|
if (m_CapsuleResults.IsEmpty())
|
|
{
|
|
blockGaitReduction = true; //unlock
|
|
m_GaitReductionBlockedOnCollision = false;
|
|
}
|
|
else
|
|
{
|
|
if(m_CapsuleResults[0].IsAHit())
|
|
{
|
|
const phInst* pHitInst = m_CapsuleResults[0].GetHitInst();
|
|
const CEntity* pHitEntity = m_CapsuleResults[0].GetHitEntity();
|
|
|
|
if (pHitInst && !pHitInst->GetArchetype()->GetTypeFlag(ArchetypeFlags::GTA_PED_TYPE)) //peds get full gait reduce B* 484431
|
|
{
|
|
if (!PHLEVEL->IsFixed(pHitInst->GetLevelIndex()) && (!pHitEntity || !pHitEntity->GetIsFixedFlagSet()))
|
|
{ //pushing things around
|
|
const u32 typeFlags = CPhysics::GetLevel()->GetInstanceTypeFlags(pHitInst->GetLevelIndex());
|
|
if ((typeFlags & ArchetypeFlags::GTA_GLASS_TYPE) == 0)
|
|
{
|
|
float fMassOfCollision = pHitInst->GetArchetype()->GetMass();
|
|
//Displayf("Mass of collision: %f", fMassOfCollision);
|
|
if (fMassOfCollision < 500.0f)
|
|
{
|
|
bReduceWalkRatio = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// B*2239914: Block gait reduction if we're walking into an unlocked door which isn't fully opened.
|
|
if(pHitEntity && pHitEntity->GetIsTypeObject())
|
|
{
|
|
const CObject *pObject = static_cast<const CObject*>(pHitEntity);
|
|
if (pObject && pObject->IsADoor())
|
|
{
|
|
const CDoor *pDoor = static_cast<const CDoor*>(pObject);
|
|
static dev_float sfRatioEpsilon = 0.02f;
|
|
if (pDoor && !pDoor->IsDoorFullyOpen(sfRatioEpsilon) && pDoor->GetDoorSystemData() && pDoor->GetDoorSystemData()->GetState() == DOORSTATE_UNLOCKED)
|
|
{
|
|
blockGaitReduction = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!blockGaitReduction && ! bNoStand)
|
|
{
|
|
Vector3 vHitNormal = m_CapsuleResults[0].GetHitPolyNormal();
|
|
if (vHitNormal.z > 0.4f )
|
|
{
|
|
pMotionData->SetGaitReductionFlag(CPedMotionData::GR_HitIncline, true);
|
|
}
|
|
|
|
//check angle to collision to try walking B* 466133
|
|
if (vHitNormal.z < 0.75f) //ignore up-bounds (B* 653880)
|
|
{
|
|
vHitNormal.z=0; vHitNormal.Normalize();
|
|
Vector3 vPedNormal = VEC3V_TO_VECTOR3(pPed->GetTransform().GetForward());
|
|
vPedNormal.z=0; vPedNormal.Normalize();
|
|
if (bStrafing)
|
|
{
|
|
vPedNormal.RotateZ(currHeading);
|
|
}
|
|
|
|
float dot = vHitNormal.Dot(vPedNormal);
|
|
//Displayf("Dot = %f", dot);
|
|
static dev_float sfMIN_STAND_ANGLE = -0.7f;
|
|
if (dot > sfMIN_STAND_ANGLE)
|
|
bNoStand = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_CapsuleResults.Reset();
|
|
}
|
|
|
|
if(!m_CapsuleResults.GetWaitingOnResults())
|
|
{
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleTest;
|
|
|
|
Vector3 vStart = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
vStart.z-=0.5f;
|
|
Vector3 vEnd(vStart);
|
|
Vector3 vTestDirection = VEC3V_TO_VECTOR3(pPed->GetTransform().GetB());
|
|
if (bStrafing )
|
|
{
|
|
vTestDirection.z=0; vTestDirection.Normalize();
|
|
vTestDirection.RotateZ(currHeading);
|
|
}
|
|
|
|
static dev_float sfCapsuleLength = 0.5f;
|
|
vEnd += vTestDirection * sfCapsuleLength;
|
|
vStart += vTestDirection * -0.1f; //pull it back behind the ped a little for swept sphere
|
|
static dev_float sfCapsuleRadius = 0.2f;
|
|
// Test flags
|
|
static const int iBoundFlags = ArchetypeFlags::GTA_PED_TYPE | ArchetypeFlags::GTA_MAP_TYPE_MOVER | ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_GLASS_TYPE | ArchetypeFlags::GTA_OBJECT_TYPE;
|
|
//static const int iExcludeFlags = ArchetypeFlags::GTA_PED_TYPE;
|
|
capsuleTest.SetCapsule(vStart, vEnd, sfCapsuleRadius);
|
|
capsuleTest.SetResultsStructure(&m_CapsuleResults);
|
|
capsuleTest.SetIsDirected(true);
|
|
capsuleTest.SetIncludeFlags(iBoundFlags);
|
|
capsuleTest.SetExcludeInstance(pPed->GetAnimatedInst());
|
|
//capsuleTest.SetExcludeTypeFlags(iExcludeFlags);
|
|
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(capsuleTest, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
}
|
|
|
|
// Always allow walk when near doors
|
|
// if( pPed->GetPedResetFlag( CPED_RESET_FLAG_IsNearDoor ) )
|
|
// {
|
|
// bNoStand = true;
|
|
// }
|
|
|
|
if (!blockGaitReduction)
|
|
{
|
|
bool downgrade = false;
|
|
static float sprintSpeedScale = 0.9f;
|
|
static float runSpeedScale = 0.5f;
|
|
static float sfWalkSpeedScale = 0.5f;
|
|
static float sfReducedWalkSpeedScale = 0.1f;
|
|
float walkSpeedScale = bReduceWalkRatio ? sfReducedWalkSpeedScale : sfWalkSpeedScale;
|
|
if (fWorkingMBR>= MOVEBLENDRATIO_SPRINT)
|
|
{
|
|
bool bTooSlow = (speed < (moveSpeeds.GetSprintSpeed()*sprintSpeedScale));
|
|
if (bTooSlow)
|
|
{
|
|
downgrade = true;
|
|
fMaxMBR=MOVEBLENDRATIO_RUN;
|
|
if (!fGaitReduction)
|
|
{
|
|
fGaitReduction = MOVEBLENDRATIO_SPRINT;
|
|
fGaitReductionHeading = currHeading;
|
|
}
|
|
//set speed override if we reduced the gait
|
|
SetVelocityOverride(Vector3(0,moveSpeeds.GetSprintSpeed(),0));
|
|
} else
|
|
{
|
|
downgrade = false;
|
|
fGaitReduction=0;
|
|
}
|
|
}
|
|
if (downgrade || fWorkingMBR>= MOVEBLENDRATIO_RUN)
|
|
{
|
|
bool bTooSlow = (speed < (moveSpeeds.GetRunSpeed()*runSpeedScale));
|
|
if (bTooSlow)
|
|
{
|
|
downgrade = true;
|
|
fMaxMBR=MOVEBLENDRATIO_WALK;
|
|
if (!fGaitReduction)
|
|
{
|
|
fGaitReduction = MOVEBLENDRATIO_RUN;
|
|
fGaitReductionHeading = currHeading;
|
|
}
|
|
if (fGaitReduction==MOVEBLENDRATIO_RUN)
|
|
{
|
|
//set speed override if we reduced the gait
|
|
SetVelocityOverride(Vector3(0,moveSpeeds.GetRunSpeed(),0));
|
|
}
|
|
} else
|
|
{
|
|
if (fGaitReduction<=MOVEBLENDRATIO_RUN)
|
|
fGaitReduction=0;
|
|
downgrade = false;
|
|
}
|
|
}
|
|
if (downgrade || fWorkingMBR> MOVEBLENDRATIO_STILL) //anything >0 is walk (animals)
|
|
{
|
|
bool bTooSlow = (speed < (moveSpeeds.GetWalkSpeed()*walkSpeedScale)); //usually want to walk at least
|
|
if (bTooSlow && pPed->IsLocalPlayer() && !bNoStand && pPed->GetMotionData()->GetPlayerHasControlOfPedThisFrame()) //only player peds can be reduced to a stand-still B* 456890
|
|
{
|
|
if (!fGaitReduction)
|
|
{
|
|
fGaitReduction = MOVEBLENDRATIO_WALK;
|
|
fGaitReductionHeading = currHeading;
|
|
}
|
|
|
|
if (!bDidPhysicsCheck)
|
|
{ //if we didn't do a physics check earlier, do one now to make sure we cans stand still here
|
|
pMotionData->SetGaitReduction(fGaitReduction);
|
|
pMotionData->SetGaitReductionHeading(fGaitReductionHeading);
|
|
if (pMotionData->GetGaitReducedMaxMoveBlendRatio() == MOVEBLENDRATIO_STILL)
|
|
{
|
|
fMaxMBR=MOVEBLENDRATIO_STILL;
|
|
SetVelocityOverride(VEC3_ZERO);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fMaxMBR=MOVEBLENDRATIO_STILL;
|
|
SetVelocityOverride(VEC3_ZERO);
|
|
}
|
|
|
|
} else
|
|
{
|
|
if (fGaitReduction && downgrade)
|
|
{
|
|
fMaxMBR=MOVEBLENDRATIO_WALK; //Try to walk
|
|
SetVelocityOverride(Vector3(0,moveSpeeds.GetRunSpeed(),0));
|
|
}
|
|
downgrade = false;
|
|
}
|
|
}
|
|
if (fGaitReduction && fWorkingMBR==MOVEBLENDRATIO_STILL) //I really want to stand
|
|
fGaitReduction=0;
|
|
}
|
|
else
|
|
{
|
|
fGaitReduction=0;
|
|
}
|
|
|
|
pMotionData->SetGaitReductionHeading(fGaitReductionHeading);
|
|
pMotionData->SetGaitReduction(fGaitReduction);
|
|
pMotionData->SetGaitReducedMaxMoveBlendRatio(fMaxMBR);
|
|
}
|
|
|
|
Vec3V_Out CTaskMotionBase::CalcDesiredAngularVelocity(Mat34V_ConstRef updatedPedMatrixIn, float fTimestep)
|
|
{
|
|
const Matrix34& updatedPedMatrix = RCC_MATRIX34(updatedPedMatrixIn);
|
|
|
|
Vector3 vAngVel = VEC3_ZERO;
|
|
CPed* pPed = GetPed();
|
|
|
|
const CPedMotionData& data = GetMotionData();
|
|
const float fGameTimeStep = fwTimer::GetTimeStep();
|
|
|
|
//*******************
|
|
// Apply heading
|
|
|
|
// Heading change is primarily driven from clip
|
|
// But also has additional components (extra heading change this frame or an independent mover transition)
|
|
|
|
//m_fLastFrameAnimatedAngularVelocity = pPed->GetAnimatedAngularVelocity();
|
|
|
|
float fExtractedAngularVel = pPed->GetAnimatedAngularVelocity() * pPed->m_PedResetFlags.GetAnimRotationModifier();
|
|
|
|
// Apply the independent mover angular velocity if active
|
|
if(data.GetIndependentMoverTransition() == 1)
|
|
{
|
|
float independentMoverDofWeight = GetIndependentMoverDofWeight();
|
|
fExtractedAngularVel += ((data.GetIndependentMoverHeadingDelta()*independentMoverDofWeight)/ fGameTimeStep);
|
|
animTaskDebugf(this, "Adding exta heading for independent mover fixup: headingDelta:%.4f, angularVel: %.4f, dofWeight: %.4f, timestep: %.4f", data.GetIndependentMoverHeadingDelta(), ((data.GetIndependentMoverHeadingDelta()*independentMoverDofWeight)/ fGameTimeStep), independentMoverDofWeight, fGameTimeStep);
|
|
}
|
|
|
|
float fAdditionalHeadingChange = pPed->ms_bApplyExtraHeadingChangeToPeds ? data.GetExtraHeadingChangeThisFrame() : 0.0f;
|
|
float fAdditionalPitchChange = data.GetExtraPitchChangeThisFrame();
|
|
|
|
// Extra heading change is not a velocity, so divide by time
|
|
fAdditionalHeadingChange /= fGameTimeStep; // don't use physics timestep because then we'll get 2x the intended turn
|
|
|
|
// If any clips are controlling the ped, then stop any extra heading change
|
|
// and slave the desired heading to the current which is coming from the clips
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsHigherPriorityClipControllingPed))
|
|
{
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_SyncDesiredHeadingToCurrentHeading))
|
|
{
|
|
fAdditionalHeadingChange = 0.0f;
|
|
fAdditionalPitchChange = 0.0f;
|
|
float fDesiredHeading = pPed->GetCurrentHeading();
|
|
|
|
// Add on the heading change we are about to apply so that our desired will be up to date after the physics update
|
|
fDesiredHeading += fExtractedAngularVel*fTimestep;
|
|
fDesiredHeading = fwAngle::LimitRadianAngleSafe(fDesiredHeading);
|
|
|
|
pPed->SetDesiredHeading(fDesiredHeading);
|
|
pPed->SetDesiredPitch(pPed->GetCurrentPitch());
|
|
}
|
|
}
|
|
|
|
float fTotalHeadingVel = fAdditionalHeadingChange + fExtractedAngularVel;
|
|
|
|
// Rotation boost: arbitrary increase in angular velocity to rotate to camera heading faster.
|
|
// TODO: do we need this?
|
|
////if( pPed->IsFirstPersonShooterModeEnabledForPlayer() )
|
|
////{
|
|
//// const bool bEnterExitVehicle = pPed->GetIsEnteringVehicle(true) || pPed->IsExitingVehicle() || pPed->IsBeingJackedFromVehicle();
|
|
//// if(!pPed->GetMotionData()->GetIsStrafing() && !bEnterExitVehicle)
|
|
//// {
|
|
//// float fCurrentHeading = fwAngle::LimitRadianAngle( pPed->GetCurrentHeading() + fTotalHeadingVel * fwTimer::GetTimeStep() );
|
|
//// float fDesiredHeading = camInterface::GetPlayerControlCamHeading(*pPed);
|
|
|
|
//// float fHeadingDiff = SubtractAngleShorter(fDesiredHeading, fCurrentHeading);
|
|
|
|
//// // TODO: expose scale to metadata?
|
|
//// fTotalHeadingVel = fHeadingDiff * 2.0f;
|
|
//// }
|
|
////}
|
|
|
|
// FPS Swim: Don't modify angular velocity based on peds pitch
|
|
Vector3 vFPSSwimAngularVelocityScaler(0.0f, 0.0f, 1.0f);
|
|
Vector3 fVelocityScaler = pPed->GetIsFPSSwimming() ? vFPSSwimAngularVelocityScaler : updatedPedMatrix.c;
|
|
vAngVel += fVelocityScaler * (fTotalHeadingVel);
|
|
|
|
//*******************
|
|
// Apply pitch.
|
|
|
|
// There is currently no pitch change extracted from clip
|
|
// So it is purely driven by the 'extra pitch change this frame'
|
|
|
|
// Use game timestep here! not physics time step or it will be time slice dependent
|
|
float fPitchVel = fAdditionalPitchChange / fGameTimeStep;
|
|
|
|
// Limit pitch in similar way to heading
|
|
// Prevent overshoot
|
|
|
|
const float fAngDiff = fwAngle::LimitRadianAngle(pPed->GetDesiredPitch() - pPed->GetCurrentPitch());
|
|
|
|
// First off make sure ped is turning in the correct direction altogether
|
|
if(Sign(fAngDiff) != Sign(fPitchVel))
|
|
{
|
|
// Ped is turning in wrong direction to reach target
|
|
fPitchVel = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
// Ped is turning in correct direction
|
|
// Make sure ped doesn't overshoot
|
|
float fMaxVel = Abs(fAngDiff / fTimestep);
|
|
fPitchVel = Min(fPitchVel,fMaxVel);
|
|
}
|
|
|
|
vAngVel += fPitchVel * updatedPedMatrix.a;
|
|
|
|
return VECTOR3_TO_VEC3V(vAngVel);
|
|
}
|
|
|
|
void CTaskMotionBase::GetPitchConstraintLimits(float& fMinOut, float& fMaxOut)
|
|
{
|
|
// By default don't allow any pitching
|
|
fMinOut = 0.0f;
|
|
fMaxOut = 0.0f;
|
|
}
|
|
|
|
float CTaskMotionBase::GetStoppingDistance(const float UNUSED_PARAM(fStopDirection), bool* UNUSED_PARAM(bOutIsClipActive))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Purpose: returns the turn rates at walk, run, and sprint move-blend ratios
|
|
void CTaskMotionBase::GetTurnRates(CMoveBlendRatioTurnRates& rates)
|
|
{
|
|
const CMotionTaskDataSet* pSet = GetPed()->GetMotionTaskDataSet();
|
|
|
|
if (Verifyf(pSet, "Invalid motion task dataset!"))
|
|
{
|
|
const sMotionTaskData* pOnFoot = pSet->GetOnFootData();
|
|
|
|
if (Verifyf(pOnFoot, "Invalid on foot motion task dataset!"))
|
|
{
|
|
rates.SetWalkTurnRate(pOnFoot->m_WalkTurnRate);
|
|
rates.SetRunTurnRate(pOnFoot->m_RunTurnRate);
|
|
rates.SetSprintTurnRate(pOnFoot->m_SprintTurnRate);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __DEV
|
|
void CTaskMotionBase::SetDefaultOnFootClipSet(const fwMvClipSetId &clipSetId)
|
|
{
|
|
animAssert(clipSetId != CLIP_SET_ID_INVALID); m_defaultOnFootClipSet = clipSetId;
|
|
}
|
|
#endif // __DEV
|
|
|
|
const crClip* CTaskMotionBase::GetBaseIdleClip()
|
|
{
|
|
static fwMvClipId clipHash("idle",0x71C21326);
|
|
if (m_defaultOnFootClipSet.GetHash()!=0)
|
|
{
|
|
fwClipSet *currentSet = fwClipSetManager::GetClipSet(m_defaultOnFootClipSet);
|
|
if (currentSet)
|
|
{
|
|
// Request_DEPRECATED the clipset is streamed in
|
|
// Surely this should have already been streamed in? (JA)
|
|
currentSet->Request_DEPRECATED();
|
|
|
|
return currentSet->GetClip(clipHash, CRO_FALLBACK | CRO_FALLBACK_IF_NOT_STREAMED);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CTaskMotionBase::SetOnFootClipSet(const fwMvClipSetId &clipSetId, const float fBlendDuration)
|
|
{
|
|
animAssert(clipSetId != CLIP_SET_ID_INVALID);
|
|
|
|
bool isInjured = false;
|
|
|
|
if (GetEntity())
|
|
{
|
|
isInjured = GetPed()->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInjured);
|
|
}
|
|
|
|
if (clipSetId != CLIP_SET_ID_INVALID && ((m_defaultOnFootClipSet != clipSetId) || (isInjured != m_isUsingInjuredMovementOverride)))
|
|
{
|
|
SetDefaultOnFootClipSet(clipSetId);
|
|
m_isUsingInjuredMovementOverride = isInjured;
|
|
m_onFootBlendDuration = fBlendDuration;
|
|
}
|
|
}
|
|
|
|
void CTaskMotionBase::SetInVehicleContextHash(u32 inVehicleContext, bool bRestartState)
|
|
{
|
|
m_overrideInVehicleContext = inVehicleContext;
|
|
|
|
if (bRestartState)
|
|
{
|
|
InVehicleContextChanged(inVehicleContext);
|
|
}
|
|
}
|
|
|
|
void CTaskMotionBase::ClearOverrideWeaponClipSet()
|
|
{
|
|
m_overrideWeaponClipSet.Clear();
|
|
m_overrideWeaponClipSetFilter.Clear();
|
|
m_overrideUsesUpperBodyShadowExpression = false;
|
|
|
|
//Note that the override weapon clip set has been cleared.
|
|
OverrideWeaponClipSetCleared();
|
|
}
|
|
|
|
void CTaskMotionBase::ResetSwimmingClipSet(const CPed& ped)
|
|
{
|
|
taskAssert(ped.GetMotionTaskDataSet());
|
|
const sMotionTaskData* pInWaterData = ped.GetMotionTaskDataSet()->GetInWaterData();
|
|
if ( pInWaterData == NULL )
|
|
{
|
|
SetDefaultSwimmingBaseClipSet( CLIP_SET_ID_INVALID );
|
|
SetDefaultSwimmingClipSet( CLIP_SET_ID_INVALID );
|
|
return;
|
|
}
|
|
|
|
fwMvClipSetId baseClipSet ( pInWaterData->m_SwimmingClipSetBaseHash.GetHash() );
|
|
SetDefaultSwimmingBaseClipSet( baseClipSet );
|
|
fwMvClipSetId clipSet ( pInWaterData->m_SwimmingClipSetHash.GetHash() );
|
|
SetDefaultSwimmingClipSet( clipSet );
|
|
}
|
|
|
|
void CTaskMotionBase::ResetOnFootClipSet(bool bRestartState, float fBlendDuration)
|
|
{
|
|
fwMvClipSetId clipSet = GetModelInfoOnFootClipSet(*GetPed());
|
|
|
|
if (bRestartState)
|
|
{
|
|
SetOnFootClipSet(clipSet, fBlendDuration);
|
|
}
|
|
else
|
|
{
|
|
SetDefaultOnFootClipSet(clipSet);
|
|
}
|
|
}
|
|
|
|
void CTaskMotionBase::ResetOnFootClipSet(const CPed& ped)
|
|
{
|
|
fwMvClipSetId clipSet = GetModelInfoOnFootClipSet(ped);
|
|
SetDefaultOnFootClipSet(clipSet);
|
|
}
|
|
|
|
void CTaskMotionBase::ResetInVehicleContextHash(bool bRestartState)
|
|
{
|
|
SetInVehicleContextHash(0, bRestartState);
|
|
}
|
|
|
|
void CTaskMotionBase::ResetStrafeClipSet()
|
|
{
|
|
SetOverrideStrafingClipSet(CLIP_SET_ID_INVALID);
|
|
SetOverrideCrouchedStrafingClipSet(CLIP_SET_ID_INVALID);
|
|
}
|
|
|
|
void CTaskMotionBase::SetDefaultClipSets(const CPed& ped)
|
|
{
|
|
static fwMvClipSetId s_defaultDivingSet("move_ped_diving",0xA2CA7C1A);
|
|
|
|
SetDefaultDivingClipSet(s_defaultDivingSet);
|
|
|
|
ResetSwimmingClipSet(ped);
|
|
ResetOnFootClipSet(ped);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskMotionBase::SetAlternateClip(AlternateClipType type, const sAlternateClip& data, bool bLooping)
|
|
{
|
|
//copy the data
|
|
m_alternateClips[type] = data;
|
|
SetAlternateClipLooping(type, bLooping);
|
|
|
|
//Note that the alternate walk clip changed.
|
|
AlternateClipChanged(type);
|
|
}
|
|
|
|
void CTaskMotionBase::StopAlternateClip(AlternateClipType type, float fBlendDuration)
|
|
{
|
|
sAlternateClip data;
|
|
data.fBlendDuration = fBlendDuration;
|
|
|
|
SetAlternateClip(type, data, false);
|
|
}
|
|
|
|
void CTaskMotionBase::SetAlternateClipLooping(AlternateClipType type, bool bLooping)
|
|
{
|
|
if(bLooping)
|
|
{
|
|
m_alternateClipsLooping |= (1 << type);
|
|
}
|
|
else
|
|
{
|
|
m_alternateClipsLooping &= ~(1 << type);
|
|
}
|
|
}
|
|
|
|
bool CTaskMotionBase::GetAlternateClipLooping(AlternateClipType type) const
|
|
{
|
|
return (m_alternateClipsLooping & (1<<type)) != 0;
|
|
}
|
|
|
|
void CTaskMotionBase::SetIndependentMoverFrame(const float fHeadingDelta, const fwMvRequestId& requestId, const bool bActuallyApply)
|
|
{
|
|
//taskAssertf(GetMotionData().GetIndependentMoverTransition() != 1, "Independent Mover Set when value is 1 - will cause glitch");
|
|
|
|
#if __DEV
|
|
if(GetMotionData().GetIndependentMoverTransition() == 1)
|
|
{
|
|
taskDebugf3("%d: ped:0x%p, task:0x%p Independent Mover Set when value is 1 - will cause glitch", fwTimer::GetTimeInMilliseconds(), GetEntity(), this);
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(INDEPENDENT_MOVER_TUNE, DISABLE_INDEPENDENT_MOVER, false);
|
|
if (DISABLE_INDEPENDENT_MOVER)
|
|
{
|
|
return;
|
|
}
|
|
#endif // __DEV
|
|
|
|
if(!GetPed()->GetPedResetFlag(CPED_RESET_FLAG_DisableIndependentMoverFrame))
|
|
{
|
|
// Hack - so we setup the motiondata variables without paying the cost of the allocation
|
|
// Set this up so I can send the data in PostMovement, but query if we are doing an independent mover in ProcessControl
|
|
if(bActuallyApply)
|
|
{
|
|
static fwMvFrameId s_independentMoverFrame("IndependentMoverFrame",0x2CA73BFC);
|
|
|
|
// Fill in the independent mover frame
|
|
Quaternion desiredRotation;
|
|
desiredRotation.FromEulers(Vector3(0.0f, 0.0f, fHeadingDelta));
|
|
|
|
crFrame* pMoverFrame = GetPed()->GetIndependentMoverFrame();
|
|
Assert(pMoverFrame);
|
|
|
|
pMoverFrame->SetQuaternion(kTrackIndependentMover, 0, QUATERNION_TO_QUATV(desiredRotation));
|
|
|
|
GetMoveNetworkHelper()->SetFrame(pMoverFrame, s_independentMoverFrame);
|
|
GetMoveNetworkHelper()->SendRequest(requestId);
|
|
}
|
|
|
|
// Track that a transition is occurring
|
|
static dev_u32 FRAME_COUNT = 2;
|
|
GetMotionData().SetIndependentMoverTransition(FRAME_COUNT);
|
|
GetMotionData().SetIndependentMoverHeadingDelta(fHeadingDelta);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CTaskMotionBase::GetIndependentMoverDofWeight() const
|
|
{
|
|
float independentMoverDofWeight = 0.0f;
|
|
|
|
const CPed* pPed = GetPed();
|
|
const crCreatureComponentExtraDofs* pExtraDofs = pPed->GetAnimDirector()->GetCreature()->FindComponent<crCreatureComponentExtraDofs>();
|
|
if(pExtraDofs)
|
|
{
|
|
const crFrame& poseFrame = pExtraDofs->GetPoseFrame();
|
|
poseFrame.GetFloat(kTrackIndependentMoverWeight, BONETAG_ROOT, independentMoverDofWeight);
|
|
}
|
|
|
|
return independentMoverDofWeight;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskMotionBase::CPersistentData::CPersistentData()
|
|
: m_onFootClipSet(u32(0))
|
|
, m_strafingClipSet(u32(0))
|
|
, m_weaponOverrideClipSet(u32(0))
|
|
, m_inVehicleOverrideContext(0)
|
|
{
|
|
memset(m_alternateClipsLooping, 0, sizeof(m_alternateClipsLooping));
|
|
}
|
|
|
|
CTaskMotionBase::CPersistentData::~CPersistentData()
|
|
{
|
|
}
|
|
|
|
void CTaskMotionBase::CPersistentData::Init(CTaskMotionBase& oldTask)
|
|
{
|
|
m_onFootClipSet = oldTask.GetDefaultOnFootClipSet(true);
|
|
m_strafingClipSet = oldTask.GetOverrideStrafingClipSet();
|
|
m_weaponOverrideClipSet = oldTask.GetOverrideWeaponClipSet();
|
|
m_inVehicleOverrideContext = oldTask.GetOverrideInVehicleContextHash();
|
|
|
|
for (s32 i = 0; i < ACT_MAX; i++)
|
|
{
|
|
const sAlternateClip& alt = oldTask.GetAlternateClip(static_cast<AlternateClipType>(i));
|
|
if (alt.IsValid())
|
|
{
|
|
m_alternateClips[i] = alt;
|
|
m_alternateClipsLooping[i] = oldTask.GetAlternateClipLooping(static_cast<AlternateClipType>(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTaskMotionBase::CPersistentData::Apply(CTaskMotionBase& newTask)
|
|
{
|
|
if (m_onFootClipSet != CLIP_SET_ID_INVALID)
|
|
{
|
|
newTask.SetOnFootClipSet(m_onFootClipSet);
|
|
}
|
|
if (m_strafingClipSet != CLIP_SET_ID_INVALID)
|
|
{
|
|
newTask.SetOverrideStrafingClipSet(m_strafingClipSet);
|
|
}
|
|
if (m_weaponOverrideClipSet != CLIP_SET_ID_INVALID)
|
|
{
|
|
newTask.SetOverrideWeaponClipSet(m_weaponOverrideClipSet);
|
|
}
|
|
if (m_inVehicleOverrideContext != 0)
|
|
{
|
|
newTask.SetInVehicleContextHash(m_inVehicleOverrideContext, false);
|
|
}
|
|
|
|
for (s32 i = 0; i < ACT_MAX; i++)
|
|
{
|
|
if (m_alternateClips[i].IsValid())
|
|
{
|
|
newTask.SetAlternateClip(static_cast<AlternateClipType>(i), m_alternateClips[i], m_alternateClipsLooping[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
float CTaskMotionBase::InterpValue(float& current, float target, float interpRate, bool angular)
|
|
{
|
|
return InterpValue(current, target, interpRate, angular, fwTimer::GetTimeStep());
|
|
}
|
|
|
|
float CTaskMotionBase::InterpValue(float& current, float target, float interpRate, bool angular, float fDt)
|
|
{
|
|
#if USE_LINEAR_INTERPOLATION
|
|
float initialCurrent = current;
|
|
|
|
if (angular)
|
|
{
|
|
float amount = SubtractAngleShorter(target, current);
|
|
target = current+amount;
|
|
}
|
|
|
|
if (target<current)
|
|
{
|
|
current-=(interpRate*fDt);
|
|
current = Max(current, target);
|
|
}
|
|
else
|
|
{
|
|
current+=(interpRate*fDt);
|
|
current = Min(current, target);
|
|
}
|
|
|
|
return (current-initialCurrent);
|
|
#else //USE_LINEAR_INTERPOLATION
|
|
float amount;
|
|
if (angular)
|
|
{
|
|
amount = SubtractAngleShorter(target, current);
|
|
}
|
|
else
|
|
{
|
|
amount = target-current;
|
|
}
|
|
amount = (amount * fDt * interpRate);
|
|
current+=amount;
|
|
return amount;
|
|
#endif //USE_LINEAR_INTERPOLATION
|
|
}
|
|
|
|
fwMvClipSetId CTaskMotionBase::GetDefaultOnFootClipSet(const bool bReturnMemberVariable) const
|
|
{
|
|
if(bReturnMemberVariable)
|
|
{
|
|
return m_defaultOnFootClipSet;
|
|
}
|
|
|
|
const CPed* pPed = GetPed();
|
|
|
|
// Only use weapon clipset if m_defaultOnFootClipSet hasn't been overridden
|
|
if (m_defaultOnFootClipSet == GetModelInfoOnFootClipSet(*pPed))
|
|
{
|
|
const CWeaponInfo *pWeaponInfo = pPed->GetWeaponManager() ? pPed->GetWeaponManager()->GetEquippedWeaponInfo() : NULL;
|
|
if(pWeaponInfo)
|
|
{
|
|
fwMvClipSetId movementWeaponOverride = pWeaponInfo->GetMovementClipSetOverride(*GetPed());
|
|
if(movementWeaponOverride != CLIP_SET_ID_INVALID)
|
|
{
|
|
return movementWeaponOverride;
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_isUsingInjuredMovementOverride ? (GetPed()->IsMale() ? CLIP_SET_MOVE_INJURED : CLIP_SET_MOVE_INJURED_FEMALE) : m_defaultOnFootClipSet;
|
|
}
|
|
|
|
fwMvClipSetId CTaskMotionBase::GetDefaultAimingStrafingClipSet(bool bCrouched) const
|
|
{
|
|
static const fwMvClipSetId s_StrafingClipSetId("move_ped_strafing",0x92EC5666);
|
|
static const fwMvClipSetId s_CrouchedStrafingClipSetId("move_ped_crouched_strafing",0x92B35B93);
|
|
static const fwMvClipSetId s_CrouchedCoverStrafingClipSetId("cover@weapon@core",0xD8BD0F2E);
|
|
static const fwMvClipSetId s_StealthStrafingClipSetId("move_ped_strafing_stealth",0x9EECB6A8);
|
|
static const fwMvClipSetId s_StrafingGrenadeClipSetId("move_ped_strafing_grenade",0x8A1403C8);
|
|
#if FPS_MODE_SUPPORTED
|
|
static const fwMvClipSetId s_StrafingFirstPersonClipSetId("move_ped_strafing_firstperson",0xbfbcbae5);
|
|
static const fwMvClipSetId s_StealthStrafingFirstPersonClipSetId("move_ped_strafing_stealth_firstperson",0x41aecd89);
|
|
static const fwMvClipSetId s_CloneStrafingFirstPersonClipSetId("move_ped_strafing_clone_firstperson",0x2657bf43);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
// Get the ped
|
|
const CPed* pPed = GetPed();
|
|
|
|
fwMvClipSetId strafeClipSet;
|
|
|
|
if (pPed->GetIsFPSSwimming())
|
|
{
|
|
if (pPed->GetIsFPSSwimmingUnderwater())
|
|
{
|
|
strafeClipSet = CLIP_SET_FPS_DIVING;
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = CLIP_SET_FPS_SWIMMING;
|
|
}
|
|
}
|
|
else if (bCrouched && pPed->GetIsInCover())
|
|
{
|
|
strafeClipSet = s_CrouchedCoverStrafingClipSetId;
|
|
}
|
|
else if (pPed->GetIsInCover() && !pPed->IsAPlayerPed())
|
|
{
|
|
strafeClipSet = s_StrafingClipSetId;
|
|
}
|
|
else if (pPed->GetMotionData()->GetUsingStealth())
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false, true))
|
|
{
|
|
strafeClipSet = s_StealthStrafingFirstPersonClipSetId;
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
strafeClipSet = s_StealthStrafingClipSetId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = bCrouched ? GetOverrideCrouchedStrafingClipSet() : GetOverrideStrafingClipSet();
|
|
}
|
|
|
|
// For projectiles, override strafing clipset
|
|
CTaskAimAndThrowProjectile* pProjectileTask = static_cast<CTaskAimAndThrowProjectile*>(pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AIM_AND_THROW_PROJECTILE));
|
|
if (pProjectileTask && pProjectileTask->ShouldUseProjectileStrafeClipSet())
|
|
{
|
|
strafeClipSet = s_StrafingGrenadeClipSetId;
|
|
}
|
|
|
|
if(strafeClipSet == CLIP_SET_ID_INVALID)
|
|
{
|
|
if(bCrouched)
|
|
{
|
|
strafeClipSet = s_CrouchedStrafingClipSetId;
|
|
}
|
|
else
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false, true))
|
|
{
|
|
bool bIsFPSIdle = pPed->GetMotionData()->GetIsFPSIdle();
|
|
|
|
TUNE_GROUP_BOOL(PED_MOVEMENT, USE_FPS_CLONE_STRAFE_ANIMS, false);
|
|
const bool bIsClone = pPed->IsNetworkClone();
|
|
if(bIsClone)
|
|
{
|
|
bIsFPSIdle = static_cast<CNetObjPlayer*>(pPed->GetNetworkObject())->IsInFirstPersonIdle();
|
|
}
|
|
|
|
const CPedWeaponManager* pWeapMgr = pPed->GetWeaponManager();
|
|
const bool bUseAimingClipSet = pWeapMgr->GetEquippedWeaponHash() != pPed->GetDefaultUnarmedWeaponHash() && pWeapMgr->GetEquippedWeaponInfo() && !pWeapMgr->GetEquippedWeaponInfo()->GetIsMeleeFist() && !bIsFPSIdle;
|
|
|
|
if(!bUseAimingClipSet && (bIsClone || USE_FPS_CLONE_STRAFE_ANIMS) && fwClipSetManager::GetClipSet(s_CloneStrafingFirstPersonClipSetId))
|
|
{
|
|
strafeClipSet = s_CloneStrafingFirstPersonClipSetId;
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = fwMvClipSetId(CWeaponAnimationsManager::GetInstance().GetFPSStrafeClipSetHash(*pPed));
|
|
|
|
if(strafeClipSet == CLIP_SET_ID_INVALID)
|
|
{
|
|
if(bUseAimingClipSet)
|
|
{
|
|
strafeClipSet = ms_AimingStrafingFirstPersonClipSetId;
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = s_StrafingFirstPersonClipSetId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
const CPedModelInfo* pModelInfo = pPed->GetPedModelInfo();
|
|
if(pModelInfo)
|
|
{
|
|
fwMvClipSetId modelClipSetId = pModelInfo->GetAppropriateStrafeClipSet(pPed);
|
|
if(modelClipSetId != CLIP_SET_ID_INVALID)
|
|
{
|
|
strafeClipSet = modelClipSetId;
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = s_StrafingClipSetId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strafeClipSet = s_StrafingClipSetId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return strafeClipSet;
|
|
}
|
|
|
|
float CTaskMotionBase::GetFloatPropertyFromClip(const fwMvClipSetId& clipSetId, const fwMvClipId& clipId, const char* propertyName) const
|
|
{
|
|
const crClip* pClip = fwClipSetManager::GetClip(clipSetId, clipId);
|
|
if(pClip && pClip->GetProperties())
|
|
{
|
|
const crProperty* pProperty = pClip->GetProperties()->FindProperty(propertyName);
|
|
if(pProperty)
|
|
{
|
|
const crPropertyAttribute* pPropertyAttribute = pProperty->GetAttribute(propertyName);
|
|
if(pPropertyAttribute && pPropertyAttribute->GetType() == crPropertyAttribute::kTypeFloat)
|
|
{
|
|
return static_cast<const crPropertyAttributeFloat*>(pPropertyAttribute)->GetFloat();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default to 1.f
|
|
return 1.f;
|
|
}
|
|
|
|
void CTaskMotionBase::UpdateHeadIK()
|
|
{
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
bool bDoHeadIK = false;
|
|
CPed* pPed = GetPed();
|
|
|
|
if(pPed && pPed->IsNetworkClone() && pPed->IsPlayer())
|
|
{
|
|
bDoHeadIK = true;
|
|
}
|
|
|
|
if (bDoHeadIK)
|
|
{
|
|
CNetObjPlayer *netObjPlayer = SafeCast(CNetObjPlayer, pPed->GetNetworkObject());
|
|
|
|
grcViewport *pViewport = netObjPlayer?netObjPlayer->GetViewport():NULL;
|
|
|
|
if(pViewport)
|
|
{
|
|
if(!pPed->GetIKSettings().IsIKFlagDisabled(IKManagerSolverTypes::ikSolverTypeBodyLook))
|
|
{
|
|
Matrix34 headMatrix;
|
|
pPed->GetBoneMatrix(headMatrix, BONETAG_HEAD);
|
|
|
|
bool bInFirstPerson = false;
|
|
if (pPed->GetNetworkObject())
|
|
bInFirstPerson = static_cast<CNetObjPlayer*>(pPed->GetNetworkObject())->IsUsingFirstPersonCamera() || static_cast<CNetObjPlayer*>(pPed->GetNetworkObject())->IsUsingFirstPersonVehicleCamera();
|
|
|
|
if (bInFirstPerson)
|
|
{
|
|
Matrix34 camMatrix = RCC_MATRIX34(pViewport->GetCameraMtx());
|
|
|
|
Vector3 vCamFace = -camMatrix.c;
|
|
Vector3 vLookAtPos = camMatrix.d + vCamFace* 2.0f; //As camera is inside players head set look at offset distance ahead of camera facing direction,
|
|
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL(VEHICLE_HEAD_IK, bRenderHeadIKForClones, false);
|
|
if (bRenderHeadIKForClones)
|
|
{
|
|
Vector3 vCameraPos = camMatrix.d;
|
|
grcDebugDraw::Line(vCameraPos,vLookAtPos,Color_red,1);
|
|
}
|
|
#endif
|
|
|
|
pPed->GetIkManager().LookAt(0, NULL, 100, BONETAG_INVALID, &vLookAtPos, LF_WHILE_NOT_IN_FOV,
|
|
500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|