10520 lines
359 KiB
C++
10520 lines
359 KiB
C++
// Rage headers
|
|
#include "Math/AngMath.h"
|
|
#include "crskeleton/skeleton.h"
|
|
// Framework headers
|
|
#include "ai/task/taskchannel.h"
|
|
#include "fwanimation/animmanager.h"
|
|
#include "grcore/debugdraw.h"
|
|
#include "fwmaths/angle.h"
|
|
#include "fwmaths/vector.h"
|
|
#include "phbound/boundcylinder.h"
|
|
|
|
//Game headers
|
|
#include "ai/debug/system/AIDebugLogManager.h"
|
|
#include "AI/Ambient/ConditionalAnimManager.h"
|
|
#include "animation/AnimDefines.h"
|
|
#include "animation/move.h"
|
|
#include "animation/MovePed.h"
|
|
#include "audio/northaudioengine.h"
|
|
#include "audio/superconductor.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/debug/DebugDirector.h"
|
|
#include "camera/gameplay/GameplayDirector.h"
|
|
#include "camera/gameplay/aim/FirstPersonShooterCamera.h"
|
|
#include "Camera/gameplay/aim/ThirdPersonAimCamera.h"
|
|
#include "Control/Gamelogic.h"
|
|
#include "Control/Route.h"
|
|
#include "Debug/VectorMap.h"
|
|
#include "Debug/DebugScene.h"
|
|
#include "Event/EventDamage.h"
|
|
#include "Event/Events.h"
|
|
#include "Event/EventReactions.h"
|
|
#include "Event/EventScript.h"
|
|
#include "Event/EventShocking.h"
|
|
#include "Event/EventWeapon.h"
|
|
#include "Event/ShockingEvents.h"
|
|
#include "frontend/MobilePhone.h"
|
|
#include "frontend/NewHud.h"
|
|
#include "Game/Localisation.h"
|
|
#include "ModelInfo/VehicleModelInfo.h"
|
|
#include "ModelInfo/WeaponModelInfo.h"
|
|
#include "PedGroup/PedGroup.h"
|
|
#include "Peds/AssistedMovement.h"
|
|
#include "Peds/PedDebugVisualiser.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "Peds/PedHelmetComponent.h"
|
|
#include "Peds/PedMoveBlend/PedMoveBlendInWater.h"
|
|
#include "Peds/PedMoveBlend/PedMoveBlendManager.h"
|
|
#include "Peds/PedMoveBlend/PedMoveBlendBase.h"
|
|
#include "Peds/PedMoveBlend/PedMoveBlendOnFoot.h"
|
|
#include "Peds/PedMoveBlend/PedMoveBlendOnSkis.h"
|
|
#include "Peds/PedPlacement.h"
|
|
#include "Peds/PedWeapons/PedTargetEvaluator.h"
|
|
#include "Peds/Ped.h"
|
|
#include "Physics/Physics.h"
|
|
#include "physics/WorldProbe/worldprobe.h"
|
|
#include "renderer/River.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "scene/EntityIterator.h"
|
|
#include "Streaming/Streaming.h"
|
|
#include "System/Pad.h"
|
|
#include "Task/Combat/Cover/Cover.h"
|
|
#include "Task/Combat/Cover/TaskCover.h"
|
|
#include "Task/Combat/Cover/TaskSeekCover.h"
|
|
#include "Task/Combat/TaskCombat.h"
|
|
#include "Task/Combat/CombatMeleeDebug.h"
|
|
#include "Task/Combat/TaskCombatMelee.h"
|
|
#include "task/combat/TaskThreatResponse.h"
|
|
#include "Task/Default/TaskAmbient.h"
|
|
#include "Task/Default/TaskArrest.h"
|
|
#include "Task/Default/TaskCuffed.h"
|
|
#include "task/Default/TaskIncapacitated.h"
|
|
#include "Task/Default/TaskPlayer.h"
|
|
#include "Task/Default/TaskSlopeScramble.h"
|
|
#include "Task/General/TaskBasic.h"
|
|
#include "Task/General/TaskSecondary.h"
|
|
#include "Task/Motion/Locomotion/TaskBirdLocomotion.h"
|
|
#include "Task/Motion/Locomotion/TaskFishLocomotion.h"
|
|
#include "Task/Motion/Locomotion/TaskMotionAiming.h"
|
|
#include "Task/Motion/Locomotion/TaskMotionPed.h"
|
|
#include "Task/Motion/Locomotion/TaskInWater.h"
|
|
#include "Task/Motion/Locomotion/TaskCombatRoll.h"
|
|
#include "Task/Motion/Locomotion/TaskQuadLocomotion.h"
|
|
#include "task/Movement/TaskParachute.h"
|
|
#include "task/Movement/TaskTakeOffPedVariation.h"
|
|
#include "Task/Movement/Climbing/TaskLadderClimb.h"
|
|
#include "Task/Movement/Climbing/TaskGoToAndClimbLadder.h"
|
|
#include "Task/Movement/Climbing/TaskVault.h"
|
|
#include "Task/Movement/Jumping/TaskJump.h"
|
|
#include "Task/Movement/Jumping/TaskInAir.h"
|
|
#include "Task/Movement/TaskJetpack.h"
|
|
#include "Task/Movement/Climbing/TaskDropDown.h"
|
|
#include "Task/Movement/TaskCollisionResponse.h"
|
|
#include "Task/Physics/TaskNMHighFall.h"
|
|
#include "Task/Physics/TaskNMBalance.h"
|
|
#include "Task/Response/TaskAgitated.h"
|
|
#include "Task/Response/TaskDuckAndCover.h"
|
|
#include "Task/Scenario/Info/ScenarioInfo.h"
|
|
#include "task/Scenario/ScenarioFinder.h"
|
|
#include "Task/Scenario/ScenarioManager.h"
|
|
#include "Task/Scenario/TaskScenario.h"
|
|
#include "Task/Scenario/Types/TaskUseScenario.h"
|
|
#include "Task/System/MotionTaskData.h"
|
|
#include "Task/System/TaskHelpers.h"
|
|
#include "Task/Animation/TaskAnims.h"
|
|
#include "Task/Vehicle/TaskCar.h"
|
|
#include "Task/Vehicle/TaskCarUtils.h"
|
|
#include "Task/Vehicle/TaskInVehicle.h"
|
|
#include "Task/General/Phone/TaskMobilePhone.h"
|
|
|
|
#include "Task/Vehicle/TaskEnterVehicle.h"
|
|
#include "Task/Vehicle/TaskMountAnimal.h"
|
|
#include "Task/Vehicle/TaskPlayerOnHorse.h"
|
|
#include "Task/Weapons/Gun/TaskReloadGun.h"
|
|
#include "Task/Weapons/TaskPlayerWeapon.h"
|
|
#include "Task/Weapons/TaskProjectile.h"
|
|
#include "Task/Weapons/TaskSwapWeapon.h"
|
|
#include "Task/Weapons/TaskWeaponBlocked.h"
|
|
|
|
#include "text/messages.h"
|
|
#include "game/modelindices.h"
|
|
#include "vehicles/automobile.h"
|
|
#include "vehicles/train.h"
|
|
#include "Vehicles/Metadata/VehicleDebug.h"
|
|
#include "Vehicles/Metadata/VehicleLayoutInfo.h"
|
|
#include "Vehicles/Metadata/VehicleEntryPointInfo.h"
|
|
#include "vehicles/Metadata/VehicleMetadataManager.h"
|
|
#include "vehicles/Metadata/VehicleSeatInfo.h"
|
|
#include "Vehicles/vehicle.h"
|
|
#include "vehicleai/pathfind.h"
|
|
#include "Weapons/ThrownWeaponInfo.h"
|
|
#include "Weapons/inventory/PedWeaponManager.h"
|
|
#include "Weapons/projectiles/Projectile.h"
|
|
#include "Weapons/projectiles/ProjectileManager.h"
|
|
#include "system/PadGestures.h"
|
|
|
|
#include "Task/Movement/TaskNavMesh.h"
|
|
#include "Task/Vehicle/TaskCarAccessories.h"
|
|
#include "control/restart.h"
|
|
#include "Objects/Object.h"
|
|
#include "debug/debug.h"
|
|
|
|
#include "frontend/MobilePhone.h"
|
|
#include "Pickups/Pickup.h"
|
|
|
|
// network includes
|
|
#include "network/NetworkInterface.h"
|
|
#include "Network/Events/NetworkEventTypes.h"
|
|
#include "Peds/PedFlagsMeta.h"
|
|
|
|
#define USE_PLAYER_MOVE_TASK 0
|
|
#define __ASSISTED_MOVEMENT_ON_SKIS 1
|
|
#define LADDER_HACKS 1
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_COVER_OPTIMISATIONS()
|
|
AI_MOTION_OPTIMISATIONS()
|
|
AI_MOVEMENT_OPTIMISATIONS()
|
|
NETWORK_OPTIMISATIONS()
|
|
|
|
bank_u32 CTaskPlayerOnFoot::ms_iQuickSwitchWeaponPickUpTime = 3000;
|
|
dev_float CTaskPlayerOnFoot::ms_fMeleeStartDistance = 8.0f;
|
|
dev_float CTaskPlayerOnFoot::ms_fMinTargetCheckDistSq = 4.25f;
|
|
dev_u32 CTaskPlayerOnFoot::ms_nRestartMeleeDelayInMS = 10000;
|
|
dev_u32 CTaskPlayerOnFoot::ms_nIntroAnimDelayInMS = 2000;
|
|
bank_bool CTaskPlayerOnFoot::ms_bAllowStrafingWhenUnarmed = false;
|
|
bank_float CTaskPlayerOnFoot::ms_fMaxSnowDepthRatioForJump = 0.4f;
|
|
dev_float CTaskPlayerOnFoot::ms_fStreamClosestLadderMinDistSqr = 10.f * 10.f;
|
|
bank_bool CTaskPlayerOnFoot::ms_bAllowLadderClimbing = true;
|
|
bank_bool CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch = false;
|
|
bank_bool CTaskPlayerOnFoot::ms_bAnalogueLockonAimControl = true;
|
|
bank_bool CTaskPlayerOnFoot::ms_bDisableTalkingThePlayerDown = false;
|
|
bank_bool CTaskPlayerOnFoot::ms_bEnableDuckAndCover = false;
|
|
bank_bool CTaskPlayerOnFoot::ms_bEnableSlopeScramble = true;
|
|
|
|
CTaskPlayerOnFoot::Tunables CTaskPlayerOnFoot::sm_Tunables;
|
|
|
|
IMPLEMENT_DEFAULT_TASK_TUNABLES(CTaskPlayerOnFoot, 0x1122edd9);
|
|
|
|
dev_float MAX_STICK_INPUT_RECORD_TIME = 10.0f;
|
|
dev_float TIME_TO_CONSIDER_OLD_INPUT_VALID = 1.0f;
|
|
|
|
bool CTaskPlayerOnFoot::Tunables::GetUseThreatWeighting(const CPed& FPS_MODE_SUPPORTED_ONLY(rPed)) const
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
return rPed.IsFirstPersonShooterModeEnabledForPlayer(false) ? m_UseThreatWeightingFPS : m_UseThreatWeighting;
|
|
#else // FPS_MODE_SUPPORTED
|
|
return m_UseThreatWeighting;
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::InitTunables()
|
|
{
|
|
sm_Tunables.m_ArrestVehicleSpeed = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_ARREST_CROOK_MAX_SPEED", 0xBFBEA130), sm_Tunables.m_ArrestVehicleSpeed);
|
|
sm_Tunables.m_ArrestDistance = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_ARREST_INTERACTION_RADIUS", 0x546DB1F8), sm_Tunables.m_ArrestDistance);
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::IsAPlayerCopInMP(const CPed& UNUSED_PARAM(ped))
|
|
{
|
|
// url:bugstar:2557284 - eTEAM_COP isn't used any more, script use teams 0-3 freely with assuming certain team types. Removing below code.
|
|
//if (NetworkInterface::IsGameInProgress())
|
|
//{
|
|
// if (ped.IsAPlayerPed() && AssertVerify(ped.GetNetworkObject()))
|
|
// {
|
|
// CNetGamePlayer* pPlayer = ped.GetNetworkObject()->GetPlayerOwner();
|
|
// if (pPlayer)
|
|
// {
|
|
// if (AssertVerify(pPlayer->GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX))
|
|
// {
|
|
// if (pPlayer->GetTeam() == eTEAM_COP)
|
|
// {
|
|
// return true;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CTaskPlayerOnFoot::CheckForUseMobilePhone(const CPed& ped)
|
|
{
|
|
if ( ped.IsLocalPlayer() )
|
|
{
|
|
if ( (CPhoneMgr::CamGetState() || CPhoneMgr::IsDisplayed()) && !CPhoneMgr::GetGoingOffScreen() )
|
|
{
|
|
CTaskMobilePhone::PhoneMode phoneMode = CPhoneMgr::CamGetState() ? CTaskMobilePhone::Mode_ToCamera : CTaskMobilePhone::Mode_ToText;
|
|
return CTaskMobilePhone::CanUseMobilePhoneTask( ped, phoneMode );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::TestIfRouteToCoverPointIsClear(CCoverPoint& rCoverPoint, CPed& rPed, const Vector3& vDesiredProtectionDir)
|
|
{
|
|
// Make sure there's nothing blocking the route to cover
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, ROUTE_CLEAR_CAPSULE_RADIUS, 0.175f, 0.0f, 1.0f, 0.01f);
|
|
|
|
Vector3 vCoverPos(Vector3::ZeroType);
|
|
// Get the cover position, and adjust it to be 1m off the ground
|
|
rCoverPoint.ReserveCoverPointForPed(&rPed);
|
|
CCover::FindCoordinatesCoverPoint(&rCoverPoint, &rPed, vDesiredProtectionDir, vCoverPos );
|
|
rCoverPoint.ReleaseCoverPointForPed(&rPed);
|
|
vCoverPos.z += 1.0f;
|
|
|
|
// Update this just in case the player's position has changed since the start of the function
|
|
Vector3 vPlayerPedPos = VEC3V_TO_VECTOR3(rPed.GetTransform().GetPosition());
|
|
Vector3 vPedToPos = vCoverPos - vPlayerPedPos;
|
|
|
|
// Only test if far enough away from the wall
|
|
if( vPedToPos.Mag2() > rage::square(0.5f) )
|
|
{
|
|
// Bring the end test position away from the wall so the end capsule hemisphere doesn't intersect
|
|
vPedToPos.Normalize();
|
|
vCoverPos -= (vPedToPos*ROUTE_CLEAR_CAPSULE_RADIUS);
|
|
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, SECOND_CAPSULE_TEST_HEIGHT_OFFSET, 0.35f, 0.0f, 2.0f, 0.01f);
|
|
|
|
// Test the route is clear
|
|
if (DidCapsuleTestHitSomething(rPed, vPlayerPedPos, vCoverPos, ROUTE_CLEAR_CAPSULE_RADIUS))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vPlayerPedPos.z -= SECOND_CAPSULE_TEST_HEIGHT_OFFSET;
|
|
vCoverPos.z -= SECOND_CAPSULE_TEST_HEIGHT_OFFSET;
|
|
|
|
if (DidCapsuleTestHitSomething(rPed, vPlayerPedPos, vCoverPos, ROUTE_CLEAR_CAPSULE_RADIUS))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, MIN_COVER_HEIGHT_DIFF_TO_TEST_FOR_STEPS, 0.3f, 0.0f, 2.0f, 0.01f);
|
|
TUNE_GROUP_BOOL(PLAYER_COVER_TUNE, ENABLE_STEP_DETECTION_PROBES, true);
|
|
if (ENABLE_STEP_DETECTION_PROBES)
|
|
{
|
|
const float fHeightDiff = vCoverPos.z - vPlayerPedPos.z;
|
|
if (fHeightDiff > MIN_COVER_HEIGHT_DIFF_TO_TEST_FOR_STEPS)
|
|
{
|
|
// Test parallel to ground from player to cover
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, STEP_TEST_Z_OFFSET, 0.3f, 0.0f, 2.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, MIN_COVER_HEIGHT_DIFF_TO_TEST_FOR_STEPS, 0.3f, 0.0f, 2.0f, 0.01f);
|
|
Vector3 vStartPos = vPlayerPedPos;
|
|
vStartPos.z -= STEP_TEST_Z_OFFSET;
|
|
Vector3 vEndPos = vCoverPos;
|
|
vEndPos.z = vStartPos.z;
|
|
Vector3 vFirstIntersectionPos;
|
|
Vector3 vFirstIntersectionNorm;
|
|
if (DidProbeTestHitSomething(vStartPos, vEndPos, vFirstIntersectionPos, vFirstIntersectionNorm))
|
|
{
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, MAX_STEP_NORMAL_DOT, 0.3f, -1.0f, 1.0f, 0.01f);
|
|
const float fFirstIntersectDot = vFirstIntersectionNorm.Dot(ZAXIS);
|
|
if (fFirstIntersectDot < MAX_STEP_NORMAL_DOT)
|
|
{
|
|
// Test down to ground at cover
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, COVER_DOWN_PROBE_LENGTH, 2.0f, 0.0f, 3.0f, 0.01f);
|
|
Vector3 vStartPos = vCoverPos;
|
|
Vector3 vEndPos = vCoverPos - Vector3(0.0f, 0.0f, COVER_DOWN_PROBE_LENGTH);
|
|
Vector3 vSecondIntersectionPos;
|
|
Vector3 vSecondIntersectionNorm;
|
|
TUNE_GROUP_FLOAT(PLAYER_COVER_TUNE, MIN_GROUND_NORMAL_DOT, 0.7f, -1.0f, 1.0f, 0.01f);
|
|
if (DidProbeTestHitSomething(vStartPos, vEndPos, vSecondIntersectionPos, vSecondIntersectionNorm))
|
|
{
|
|
// If norm 1 is horizontal and norm 2 is vertical rule out cover
|
|
const float fSecondIntersectDot = vSecondIntersectionNorm.Dot(ZAXIS);
|
|
if (fSecondIntersectDot > MIN_GROUND_NORMAL_DOT)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::DidCapsuleTestHitSomething(const CPed& rPed, const Vector3& vStartPos, const Vector3& vEndPos, const float fCapsuleRadius)
|
|
{
|
|
static const int iTestFlags = ArchetypeFlags::GTA_ALL_MAP_TYPES | ArchetypeFlags::GTA_OBJECT_TYPE | ArchetypeFlags::GTA_PED_TYPE | ArchetypeFlags::GTA_VEHICLE_TYPE;
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
|
|
const s32 MAX_INTERSECTIONS = 4;
|
|
WorldProbe::CShapeTestFixedResults<MAX_INTERSECTIONS> capsuleResults;
|
|
capsuleDesc.SetResultsStructure(&capsuleResults);
|
|
capsuleDesc.SetExcludeEntity(&rPed);
|
|
capsuleDesc.SetCapsule(vStartPos, vEndPos, fCapsuleRadius);
|
|
capsuleDesc.SetIncludeFlags(iTestFlags);
|
|
|
|
bool bHitSomething = WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDesc);
|
|
|
|
// Only count peds in cover as obstructions as pushing them out of the way looks bad
|
|
for (s32 i=0; i < capsuleResults.GetNumHits(); ++i)
|
|
{
|
|
bHitSomething = true;
|
|
|
|
const CEntity* pHitEntity = CPhysics::GetEntityFromInst(capsuleResults[i].GetHitInst());
|
|
if (pHitEntity && pHitEntity->GetIsTypePed())
|
|
{
|
|
const CPed* pHitPed = static_cast<const CPed*>(pHitEntity);
|
|
if (!pHitPed->GetIsInCover())
|
|
{
|
|
bHitSomething = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (capsuleResults[i].GetHitInst() && CTaskCover::HitInstIsFoliage(*capsuleResults[i].GetHitInst(), capsuleResults[i].GetHitComponent()))
|
|
{
|
|
bHitSomething = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Early out, hit something which isn't a ped not in cover
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::sDebugCapsuleParams capsuleParams;
|
|
capsuleParams.vStart = RCC_VEC3V(vStartPos);
|
|
capsuleParams.vEnd = RCC_VEC3V(vEndPos);
|
|
capsuleParams.fRadius = fCapsuleRadius;
|
|
capsuleParams.color = bHitSomething ? Color_red : Color_green;
|
|
capsuleParams.uContextHash = CCoverDebug::CAPSULE_CLEARANCE_TESTS;
|
|
CCoverDebug::AddDebugCapsule(capsuleParams);
|
|
CTask::ms_debugDraw.AddCapsule(RCC_VEC3V(vStartPos), RCC_VEC3V(vEndPos), fCapsuleRadius, bHitSomething ? Color_red : Color_green, 5000, 0, false);
|
|
#endif // DEBUG_DRAW
|
|
return bHitSomething;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::DidProbeTestHitSomething(const Vector3& vStart, const Vector3& vEnd, Vector3& vIntersectionPos, Vector3& vIntersectionNormal)
|
|
{
|
|
WorldProbe::CShapeTestFixedResults<> probeResult;
|
|
s32 iTypeFlags = ArchetypeFlags::GTA_ALL_MAP_TYPES | ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_OBJECT_TYPE;
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
probeDesc.SetResultsStructure(&probeResult);
|
|
probeDesc.SetStartAndEnd(vStart, vEnd);
|
|
probeDesc.SetIncludeFlags(iTypeFlags);
|
|
probeDesc.SetTypeFlags(ArchetypeFlags::GTA_AI_TEST);
|
|
probeDesc.SetIsDirected(true);
|
|
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc);
|
|
|
|
if (!probeResult[0].GetHitDetected())
|
|
{
|
|
#if DEBUG_DRAW
|
|
CTask::ms_debugDraw.AddLine(RCC_VEC3V(vStart), RCC_VEC3V(vEnd), Color_brown, 2500, 0);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
|
|
bool bHitSomething = WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc);
|
|
for (s32 i=0; i < probeResult.GetNumHits(); ++i)
|
|
{
|
|
bHitSomething = true;
|
|
|
|
const CEntity* pHitEntity = CPhysics::GetEntityFromInst(probeResult[i].GetHitInst());
|
|
if (pHitEntity && pHitEntity->GetIsTypeObject() && pHitEntity->GetIsDynamic())
|
|
{
|
|
bHitSomething = false;
|
|
continue;
|
|
}
|
|
else if (PGTAMATERIALMGR->GetPolyFlagStairs(probeResult[i].GetHitMaterialId()))
|
|
{
|
|
bHitSomething = false;
|
|
continue;
|
|
}
|
|
else if (probeResult[i].GetHitInst() && CTaskCover::HitInstIsFoliage(*probeResult[i].GetHitInst(), probeResult[i].GetHitComponent()))
|
|
{
|
|
bHitSomething = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Early out, hit something valid
|
|
vIntersectionPos = probeResult[i].GetHitPosition();
|
|
vIntersectionNormal = probeResult[i].GetHitNormal();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
CTask::ms_debugDraw.AddLine(RCC_VEC3V(vStart), bHitSomething ? RCC_VEC3V(vIntersectionPos) : RCC_VEC3V(vEnd), bHitSomething ? Color_purple : Color_cyan, 2500, 0);
|
|
if (bHitSomething)
|
|
{
|
|
CTask::ms_debugDraw.AddArrow(RCC_VEC3V(vIntersectionPos), VECTOR3_TO_VEC3V(vIntersectionPos + vIntersectionNormal), 0.25f, Color_purple, 2500, 0);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
return bHitSomething;
|
|
}
|
|
|
|
u32 CTaskPlayerOnFoot::m_uLastTimeJumpPressed = 0;
|
|
u32 CTaskPlayerOnFoot::m_uLastTimeSprintDown = 0;
|
|
u32 CTaskPlayerOnFoot::m_uReducingGaitTimeMS = 0;
|
|
u32 CTaskPlayerOnFoot::m_nLastFrameProcessedClimbDetector = 0;
|
|
//
|
|
//
|
|
//
|
|
CTaskPlayerOnFoot::CTaskPlayerOnFoot()
|
|
: m_vLastCoverDir(Vector3::ZeroType)
|
|
, m_vLastStickInput(Vector3::ZeroType)
|
|
, m_vBlockPos(Vector3::ZeroType)
|
|
, m_fBlockHeading(FLT_MAX)
|
|
, m_pLadderClipRequestHelper(NULL)
|
|
, m_chosenOptionalVehicleSet(CLIP_SET_ID_INVALID)
|
|
#if FPS_MODE_SUPPORTED
|
|
, m_pFirstPersonIkHelper(NULL)
|
|
, m_WasInFirstPerson(false)
|
|
#endif // FPS_MODE_SUPPORTED
|
|
, m_pMeleeRequestTask(NULL)
|
|
, m_pMeleeReactionTask(NULL)
|
|
, m_pScriptedTask(NULL)
|
|
, m_pTargetVehicle(NULL)
|
|
, m_pTargetAnimal(NULL)
|
|
, m_pTargetLadder(NULL)
|
|
, m_pPreviousTargetLadder(NULL)
|
|
, m_bCanReachLadder(false)
|
|
, m_bLadderVerticalClear(false)
|
|
, m_bUseSmallerVerticalProbeCheck(false)
|
|
, m_pTargetUncuffPed(NULL)
|
|
, m_pTargetArrestPed(NULL)
|
|
, m_iLadderIndex(0)
|
|
, m_uLastTimeInCoverTask(0)
|
|
, m_uLastTimeInMeleeTask(0xFFFFFFFF)
|
|
, m_uLastTimeEnterVehiclePressed(0)
|
|
, m_uLastTimeEnterCoverPressed(0)
|
|
, m_bWasMeleeStrafing(false)
|
|
, m_fIdleTime(0.0f)
|
|
, m_pPedPlayerIntimidating(NULL)
|
|
, m_fTimeIntimidating(0.0f)
|
|
, m_fTimeBetweenIntimidations(0.0f)
|
|
, m_fTimeSinceLastStickInput(MAX_STICK_INPUT_RECORD_TIME)
|
|
, m_fTimeSinceLastValidSlopeScramble(0.f)
|
|
, m_fTimeSinceLastPlayerEventOpportunity(LARGE_FLOAT)
|
|
, m_fTimeSinceBirdProjectile(LARGE_FLOAT)
|
|
, m_nFramesOfSlopeScramble(0)
|
|
, m_bOptionVehicleGroupLoaded(false)
|
|
, m_bScriptedToGoIntoCover(false)
|
|
, m_bShouldStartGunTask(false)
|
|
, m_bCanStealthKill(false)
|
|
, m_bReloadFromIdle(false)
|
|
, m_bUsingStealthClips(false)
|
|
, m_bJumpedFromCover(false)
|
|
, m_bCoverSprintReleased(true)
|
|
, m_bPlayerExitedCoverThisUpdate(false)
|
|
, m_bGettingOnBottom(false)
|
|
, m_eArrestType(eAT_None)
|
|
, m_bWaitingForPedArrestUp(false)
|
|
, m_iJumpFlags(0)
|
|
, m_b1stTimeSeenWhenWanted(false)
|
|
, m_bResumeGetInVehicle(false)
|
|
, m_bCanBreakOutToMovement(false)
|
|
, m_bIdleWeaponBlocked(false)
|
|
, m_bCheckTimeInStateForCover(false)
|
|
, m_bWaitingForLadder(false)
|
|
, m_bEasyLadderEntry(false)
|
|
, m_bHasAttemptedToSpawnBirdProjectile(false)
|
|
, m_bNeedToSpawnBirdProjectile(false)
|
|
, m_nCachedBreakOutToMovementState(-1)
|
|
, m_bGetInVehicleAfterCombatRolling(false)
|
|
, m_fStandAutoStepUpTimer(0.0f)
|
|
#if FPS_MODE_SUPPORTED
|
|
, m_fGetInVehicleFPSIkZOffset(0.f)
|
|
, m_fTimeSinceLastSwimSprinted(0.0f)
|
|
#endif // FPS_MODE_SUPPORTED
|
|
, m_bForceInstantIkBlendInThisFrame(false)
|
|
, m_pTargetPed(NULL)
|
|
, m_pLastTargetPed(NULL)
|
|
, m_uArrestFinishTime(0)
|
|
, m_uArrestSprintBreakoutTime(0)
|
|
, m_bArrestTimerActive(false)
|
|
{
|
|
m_optionalVehicleHelper.ReleaseClips();
|
|
SetInternalTaskType(CTaskTypes::TASK_PLAYER_ON_FOOT);
|
|
|
|
#if __BANK
|
|
AI_LOG_WITH_ARGS("[Player] - TASK_PLAYER_ON_FOOT constructed at 0x%p, see console log for callstack\n", this);
|
|
if ((Channel_ai.FileLevel >= DIAG_SEVERITY_DISPLAY) || (Channel_ai.TtyLevel >= DIAG_SEVERITY_DISPLAY))
|
|
{
|
|
aiDisplayf("Frame %i, TASK_PLAYER_ON_FOOT constructed at 0x%p", fwTimer::GetFrameCount(), this);
|
|
sysStack::PrintStackTrace();
|
|
}
|
|
#endif // __BANK
|
|
|
|
m_uLastTimeJumpPressed = 0;
|
|
m_uLastTimeSprintDown = 0;
|
|
m_uReducingGaitTimeMS = 0;
|
|
m_nLastFrameProcessedClimbDetector = 0;
|
|
}
|
|
|
|
CTaskPlayerOnFoot::~CTaskPlayerOnFoot()
|
|
{
|
|
m_optionalVehicleHelper.ReleaseClips();
|
|
m_chosenOptionalVehicleSet = CLIP_SET_ID_INVALID;
|
|
m_bOptionVehicleGroupLoaded = false;
|
|
if (m_pLadderClipRequestHelper)
|
|
{
|
|
CLadderClipSetRequestHelperPool::ReleaseClipSetRequestHelper(m_pLadderClipRequestHelper);
|
|
m_pLadderClipRequestHelper = NULL;
|
|
}
|
|
|
|
if( m_pMeleeReactionTask )
|
|
{
|
|
delete m_pMeleeReactionTask;
|
|
m_pMeleeReactionTask = NULL;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if (m_pFirstPersonIkHelper)
|
|
{
|
|
delete m_pFirstPersonIkHelper;
|
|
m_pFirstPersonIkHelper = NULL;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
|
|
|
|
aiTask* CTaskPlayerOnFoot::Copy() const
|
|
{
|
|
CTaskPlayerOnFoot* pTask = rage_new CTaskPlayerOnFoot();
|
|
pTask->SetScriptedToGoIntoCover(m_bScriptedToGoIntoCover);
|
|
return pTask;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::ProcessPostPreRender()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
CheckForArmedMeleeAction(pPed);
|
|
|
|
// Check if we have a pending bird projectile to send off.
|
|
if (m_bNeedToSpawnBirdProjectile)
|
|
{
|
|
SpawnBirdProjectile(pPed);
|
|
}
|
|
|
|
return CTask::ProcessPostPreRender();
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ProcessPreRender2()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
if (IsStateValidForIK())
|
|
{
|
|
float fOverrideBlendInDuration = -1.0f;
|
|
float fOverrideBlendOutDuration = -1.0f;
|
|
if (pPed->IsUsingActionMode())
|
|
{
|
|
fOverrideBlendOutDuration = 0.3f;
|
|
}
|
|
pPed->ProcessLeftHandGripIk(true, false, fOverrideBlendInDuration, fOverrideBlendOutDuration);
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if (IsStateValidForFPSIK())
|
|
{
|
|
if (m_pFirstPersonIkHelper == NULL)
|
|
{
|
|
m_pFirstPersonIkHelper = rage_new CFirstPersonIkHelper();
|
|
taskAssert(m_pFirstPersonIkHelper);
|
|
}
|
|
|
|
if (m_pFirstPersonIkHelper)
|
|
{
|
|
const CWeaponInfo* pWeaponInfo = CTaskMotionPed::GetWeaponInfo(pPed);
|
|
m_pFirstPersonIkHelper->SetUseSecondaryMotion(pWeaponInfo && pWeaponInfo->GetUseFPSSecondaryMotion());
|
|
|
|
const bool bUsingMobilePhone = CheckForUseMobilePhone(*pPed);
|
|
const ScalarV vThreshold(10.0f);
|
|
m_pFirstPersonIkHelper->SetUseLook(!bUsingMobilePhone && pPed->IsNearMirror(vThreshold));
|
|
|
|
const bool bUseAnimBlockingTags = pPed->GetPedResetFlag(CPED_RESET_FLAG_PlayFPSIdleFidgets) && pPed->GetMotionData()->GetIsFPSIdle();
|
|
m_pFirstPersonIkHelper->SetUseAnimBlockingTags(bUseAnimBlockingTags);
|
|
|
|
const bool bInstant = (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InFPSUnholsterTransition) && !(pWeaponInfo->GetIsThrownWeapon() && pPed->GetMotionData()->GetIsFPSIdle())) || camInterface::GetGameplayDirector().GetJustSwitchedFromSniperScope();
|
|
ArmIkBlendRate blendInRate = (m_bForceInstantIkBlendInThisFrame || bInstant || (pPed->GetPlayerInfo() && pPed->GetPlayerInfo()->GetSwitchedToOrFromFirstPersonCount() == 1)) ? ARMIK_BLEND_RATE_INSTANT : ARMIK_BLEND_RATE_FAST;
|
|
ArmIkBlendRate blendOutRate = (bInstant || (pPed->GetPlayerInfo() && pPed->GetPlayerInfo()->GetSwitchedToOrFromFirstPersonCount() == 2)) ? ARMIK_BLEND_RATE_INSTANT : ARMIK_BLEND_RATE_FASTEST;
|
|
|
|
TUNE_GROUP_BOOL(REAR_COVER_AIM_BUGS, DISABLE_NEW_BLEND_RATES, false);
|
|
if (!DISABLE_NEW_BLEND_RATES && !CTaskCover::IsPlayerAimingDirectlyInFirstPerson(*pPed))
|
|
{
|
|
TUNE_GROUP_INT(FIRST_PERSON_COVER_BLEND_TUNE, iCoverIntroRightIKBlendIn, 4, 0, 5, 1);
|
|
TUNE_GROUP_INT(FIRST_PERSON_COVER_BLEND_TUNE, iCoverOutroRightIKBlendOut, 4, 0, 5, 1);
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingCoverAimIntro))
|
|
{
|
|
blendInRate = (ArmIkBlendRate) iCoverIntroRightIKBlendIn;
|
|
|
|
}
|
|
else if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingCoverAimOutro))
|
|
{
|
|
blendOutRate = (ArmIkBlendRate) iCoverOutroRightIKBlendOut;
|
|
}
|
|
}
|
|
|
|
m_pFirstPersonIkHelper->SetBlendInRate(blendInRate);
|
|
m_pFirstPersonIkHelper->SetBlendOutRate(blendOutRate);
|
|
|
|
m_pFirstPersonIkHelper->Process(pPed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_pFirstPersonIkHelper)
|
|
{
|
|
const bool bCameraCut = (camInterface::GetGameplayDirector().GetFirstPersonShooterCamera() == NULL) &&
|
|
((camInterface::GetFrame().GetFlags() & (camFrame::Flag_HasCutPosition | camFrame::Flag_HasCutOrientation)) != 0);
|
|
|
|
// Reset arm solver on camera cut
|
|
m_pFirstPersonIkHelper->Reset(pPed, bCameraCut);
|
|
}
|
|
|
|
// First person look when helper is not running
|
|
if (IsStateValidForFPSIK(true))
|
|
{
|
|
const bool bUsingMobilePhone = CheckForUseMobilePhone(*pPed);
|
|
const ScalarV vThreshold(10.0f);
|
|
|
|
if (!bUsingMobilePhone && pPed->IsNearMirror(vThreshold))
|
|
{
|
|
Mat34V mtxEntity(pPed->GetMatrix());
|
|
CFirstPersonIkHelper::ProcessLook(pPed, mtxEntity);
|
|
}
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ProcessPlayerEvents()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
m_fTimeSinceLastPlayerEventOpportunity += fwTimer::GetTimeStep();
|
|
|
|
if (m_fTimeSinceLastPlayerEventOpportunity >= sm_Tunables.m_TimeBetweenPlayerEvents)
|
|
{
|
|
m_fTimeSinceLastPlayerEventOpportunity = 0.0f;
|
|
if (pPed->GetPedModelInfo()->GetPersonalitySettings().GetIsWeird() || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PlayerIsWeird))
|
|
{
|
|
CEventShockingWeirdPed ev(*pPed);
|
|
CShockingEventsManager::Add(ev);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::IsStateValidForIK() const
|
|
{
|
|
const CPed* pPed = GetPed();
|
|
|
|
if (pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_RELOAD_GUN))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_SWAP_WEAPON))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Cover handles grip IK itself.
|
|
if (pPed->GetIsInCover())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bCheckDistance = true;
|
|
#if FPS_MODE_SUPPORTED
|
|
if (pPed->GetIsFPSSwimming() && pPed->GetEquippedWeaponInfo() && pPed->GetEquippedWeaponInfo()->GetIsMelee())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//We do not need left hand IK
|
|
const bool bFirstPerson = pPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
|
|
if(bFirstPerson)
|
|
{
|
|
const CWeapon* pWeapon = pPed->GetWeaponManager() ? pPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
|
|
if(pWeapon && pWeapon->GetWeaponInfo()->GetIsUnarmed())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(bFirstPerson && pPed->GetPedResetFlag(CPED_RESET_FLAG_IsThrowingProjectileWhileAiming))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(bFirstPerson && pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ForceFPSIKWithUpperBodyAnim))
|
|
{
|
|
bCheckDistance = false;
|
|
}
|
|
#endif
|
|
|
|
bool bUsingTwoHandedWeapon = false;
|
|
const CWeaponInfo* pWeaponInfo = NULL;
|
|
if( pPed->GetWeaponManager() )
|
|
{
|
|
const CObject* pWeaponObject = pPed->GetWeaponManager()->GetEquippedWeaponObject();
|
|
if (pWeaponObject)
|
|
{
|
|
const CWeapon* pWeapon = pWeaponObject->GetWeapon();
|
|
if (pWeapon)
|
|
{
|
|
pWeaponInfo = pWeapon->GetWeaponInfo();
|
|
taskAssert(pWeaponInfo);
|
|
|
|
bUsingTwoHandedWeapon = pWeaponInfo->GetIsGun2Handed() || pWeaponInfo->GetIsMelee2Handed();
|
|
if (!pWeaponInfo->GetIsGun1Handed() && !bUsingTwoHandedWeapon && !(bFirstPerson && pWeaponInfo->GetAttachFPSLeftHandIKToRight()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (bCheckDistance)
|
|
{
|
|
// Check distance of left hand to weapon grip in animation and disable IK if distance is too great
|
|
const CWeaponModelInfo* pWeaponModelInfo = pWeapon->GetWeaponModelInfo();
|
|
const crSkeleton* pWeaponSkeleton = pWeapon->GetEntity() ? pWeapon->GetEntity()->GetSkeleton() : NULL;
|
|
const crSkeleton* pPlayerSkeleton = pPed->GetSkeleton();
|
|
|
|
if (pWeaponModelInfo && pWeaponSkeleton && pPlayerSkeleton)
|
|
{
|
|
// Animators requested R grip be used for 1-handed guns
|
|
s32 boneIdxGrip = pWeaponModelInfo->GetBoneIndex(bUsingTwoHandedWeapon ? WEAPON_GRIP_L : WEAPON_GRIP_R);
|
|
s32 boneIdxHandLeft = -1;
|
|
|
|
pPlayerSkeleton->GetSkeletonData().ConvertBoneIdToIndex((u16)BONETAG_L_IK_HAND, boneIdxHandLeft);
|
|
|
|
if ((boneIdxGrip >= 0) && (boneIdxHandLeft >= 0))
|
|
{
|
|
Mat34V mtxGrip;
|
|
pWeaponSkeleton->GetGlobalMtx(boneIdxGrip, mtxGrip);
|
|
|
|
Mat34V mtxHandLeft;
|
|
pPlayerSkeleton->GetGlobalMtx(boneIdxHandLeft, mtxHandLeft);
|
|
|
|
Vec3V vGripPos = mtxGrip.GetCol3();
|
|
Vec3V vHandPos = mtxHandLeft.GetCol3();
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// B*2248699: Update grip position based on change in left hand position from CTaskHumanLocomotion::ProcessPostCamera before we update the peds heading.
|
|
if (bFirstPerson && pPed->GetPlayerInfo())
|
|
{
|
|
Vec3V vCachedHandPos = pPed->GetPlayerInfo()->GetLeftHandGripPositionFPS();
|
|
if (!IsZeroAll(vCachedHandPos))
|
|
{
|
|
Vec3V vHandOffset = vHandPos - vCachedHandPos;
|
|
vGripPos = vGripPos + vHandOffset;
|
|
}
|
|
// Reset the cached position.
|
|
Vec3V vZero(V_ZERO);
|
|
pPed->GetPlayerInfo()->SetLeftHandPositionFPS(vZero);
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
const ScalarV sDistSquared = DistSquared(vGripPos, vHandPos);
|
|
static dev_float fHandToGripRange = 0.15f;
|
|
static dev_float fExpandedHandToGripRange = 0.2f;
|
|
float fGripRange = pPed->IsUsingActionMode() || pPed->GetMotionData()->GetIsDesiredSprinting(true) ? fExpandedHandToGripRange : fHandToGripRange;
|
|
|
|
if(pPed->GetPlayerInfo() && pPed->GetPlayerInfo()->IsPlayerStateValidForFPSManipulation())
|
|
{
|
|
static const float s_fOneOverThirty = 1.f/30.f;
|
|
const float fTimeScaler = Clamp(fwTimer::GetTimeStep() / s_fOneOverThirty, 1.f, 2.f);
|
|
|
|
// Modify grip range test by variable time step
|
|
fGripRange *= fTimeScaler;
|
|
}
|
|
|
|
if (sDistSquared.Getf() > rage::square(fGripRange))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static const fwMvBooleanId s_EnableLeftHandIkId("EnableLeftHandIk",0xebf13a3d);
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bFirstPerson && pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ForceFPSIKWithUpperBodyAnim))
|
|
{
|
|
return true;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
switch(GetState())
|
|
{
|
|
case(STATE_INITIAL):
|
|
case(STATE_PLAYER_GUN):
|
|
case(STATE_USE_COVER):
|
|
case(STATE_MELEE_REACTION):
|
|
if (pPed->GetIkManager().IsActive(IKManagerSolverTypes::ikSolverTypeTorsoReact))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(pPed->IsUsingActionMode())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
case(STATE_MELEE):
|
|
{
|
|
if(pPed->IsUsingActionMode())
|
|
{
|
|
CTaskMeleeActionResult *pMeleeActionResult = pPed->GetPedIntelligence()->GetTaskMeleeActionResult();
|
|
if( pMeleeActionResult && pMeleeActionResult->GetMoveNetworkHelper()->GetBoolean(s_EnableLeftHandIkId) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case(STATE_JUMP):
|
|
{
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping) || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsLanding))
|
|
{
|
|
CTaskJump* pTaskJump = (CTaskJump*)( pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JUMP) );
|
|
if(pTaskJump && !pTaskJump->GetIsStateFall() && !pTaskJump->GetIsDivingLaunch() && !pTaskJump->GetIsSkydivingLaunch())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case(STATE_PLAYER_IDLES):
|
|
{
|
|
if(pPed->GetMotionData()->GetUsingStealth() || pPed->IsUsingActionMode())
|
|
{
|
|
if(pPed->GetMovementModeData().m_UseLeftHandIk)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(pWeaponInfo && pWeaponInfo->GetFlag(CWeaponInfoFlags::DisableLeftHandIkWhenOnFoot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pPed->GetIkManager().IsActive(IKManagerSolverTypes::ikSolverTypeTorsoReact))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Disable Ik in transitions
|
|
CTaskMotionBase* pMotionTask = pPed->GetCurrentMotionTask(false);
|
|
if(pMotionTask)
|
|
{
|
|
CTaskHumanLocomotion* pLocoTask = pMotionTask->GetTaskType() == CTaskTypes::TASK_HUMAN_LOCOMOTION ? static_cast<CTaskHumanLocomotion*>(pMotionTask) : NULL;
|
|
if(pLocoTask)
|
|
{
|
|
//Since cover->Idles doesn't qualify for IsTransitionToIdleIntroActive, we need to block IK some other way, B* 1384768
|
|
static dev_float sf_CoverIdleIKBlockTime = 0.2f;
|
|
if (!bUsingTwoHandedWeapon && GetPreviousState() == STATE_USE_COVER && GetTimeInState() < sf_CoverIdleIKBlockTime)
|
|
return false;
|
|
|
|
if(!pLocoTask->IsTransitionToIdleIntroActive() || pLocoTask->IsFullyInIdle())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(pLocoTask->GetMoveNetworkHelper() && pLocoTask->GetMoveNetworkHelper()->GetBoolean(s_EnableLeftHandIkId))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if(pMotionTask->GetTaskType() == CTaskTypes::TASK_MOTION_DIVING || pMotionTask->GetTaskType() == CTaskTypes::TASK_MOTION_SWIMMING)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#if FPS_MODE_SUPPORTED
|
|
case STATE_GET_IN_VEHICLE:
|
|
if(bFirstPerson && pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_GO_TO_CAR_DOOR_AND_STAND_STILL))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
#endif // FPS_MODE_SUPPORTED
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool CTaskPlayerOnFoot::IsStateValidForFPSIK(bool bExcludeUnarmed) const
|
|
{
|
|
const CPed* pPed = GetPed();
|
|
|
|
if (!pPed->IsFirstPersonShooterModeEnabledForPlayer(false, false, false, true, false))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CWeaponInfo* pWeaponInfo = CTaskMotionPed::GetWeaponInfo(pPed);
|
|
if (!pWeaponInfo)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableFPSArmIK))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_WasPlayingFPSGetup) || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_WasPlayingFPSMeleeActionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// MN: If FPSAllowAimIKForThrownProjectile is set, we want to disregard the weapon info check (as we might switch to unarmed while throwing)
|
|
// We already check the weapon flag before setting FPSAllowAimIKForThrownProjectile so it should be good
|
|
if(!pWeaponInfo->GetUseFPSAimIK() && (!pWeaponInfo->GetIsUnarmed() || !pPed->GetMotionData()->GetIsFPSLT()) && !pPed->GetPedResetFlag(CPED_RESET_FLAG_FPSAllowAimIKForThrownProjectile))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CPedMotionData* pMotionData = pPed->GetMotionData();
|
|
if (!pMotionData->GetIsFPSRNG() && !pMotionData->GetIsFPSLT() && !pMotionData->GetIsFPSScope() && !pMotionData->GetIsFPSIdle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_SWAP_WEAPON) FPS_MODE_SUPPORTED_ONLY( && !pPed->IsFirstPersonShooterModeEnabledForPlayer(false)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_TAKE_OFF_HELMET))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch(GetState())
|
|
{
|
|
case STATE_MELEE:
|
|
{
|
|
CTaskMelee* pTaskMelee = static_cast<CTaskMelee*>(FindSubTaskOfType(CTaskTypes::TASK_MELEE));
|
|
if (pTaskMelee && pTaskMelee->IsBlocking())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Intentional fall-through as we also want to use IK when aiming/sprinting in the melee state
|
|
}
|
|
case STATE_PLAYER_IDLES:
|
|
case STATE_PLAYER_GUN:
|
|
{
|
|
if (!bExcludeUnarmed && (pWeaponInfo->GetIsUnarmed() || pWeaponInfo->GetIsMeleeFist()))
|
|
{
|
|
if(!CTaskMotionAiming::IsValidForUnarmedIK(*pPed))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (pPed->GetPedResetFlag(CPED_RESET_FLAG_IsAiming))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(FPS_IK, ENABLE_WHILE_SPRINTING, true);
|
|
if (ENABLE_WHILE_SPRINTING && pPed->GetMotionData()->GetIsDesiredSprinting(true))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case STATE_USE_COVER:
|
|
{
|
|
if (pPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingCoverAimIntro) || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsAimingFromCover))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (pPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingCoverAimOutro))
|
|
{
|
|
if(!pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableRightArmIKInCoverOutroFPS))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case STATE_GET_IN_VEHICLE:
|
|
if(pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_GO_TO_CAR_DOOR_AND_STAND_STILL))
|
|
{
|
|
return true;
|
|
}
|
|
case STATE_INITIAL:
|
|
case STATE_JUMP:
|
|
case STATE_SWAP_WEAPON:
|
|
case STATE_CLIMB_LADDER:
|
|
case STATE_TAKE_OFF_HELMET:
|
|
case STATE_MELEE_REACTION:
|
|
case STATE_ARREST:
|
|
case STATE_UNCUFF:
|
|
case STATE_MOUNT_ANIMAL:
|
|
case STATE_DUCK_AND_COVER:
|
|
case STATE_SCRIPTED_TASK:
|
|
case STATE_DROPDOWN:
|
|
case STATE_TAKE_OFF_PARACHUTE_PACK:
|
|
case STATE_TAKE_OFF_JETPACK:
|
|
case STATE_TAKE_OFF_SCUBA_GEAR:
|
|
case STATE_RIDE_TRAIN:
|
|
case STATE_JETPACK:
|
|
case STATE_BIRD_PROJECTILE:
|
|
default:
|
|
{
|
|
// Allow IK if we are playing FPS upper body anims for stumbling.
|
|
if(pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ForceFPSIKWithUpperBodyAnim))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
bool CTaskPlayerOnFoot::HasPlayerSelectedGunTask(CPed* FPS_MODE_SUPPORTED_ONLY(pPlayerPed), s32 nState)
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPlayerPed->GetPlayerInfo() && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) && pPlayerPed->GetMotionData()->GetIsFPSIdle())
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return nState == STATE_PLAYER_GUN;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::UpdateTalk(CPed* pPlayerPed, bool bCanBeFriendly, bool bCanBeUnfriendly)
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl->GetPedTalk().IsPressed())
|
|
{
|
|
//Talk.
|
|
fwFlags8 uFlags = 0;
|
|
if(bCanBeFriendly)
|
|
{
|
|
uFlags.SetFlag(CTalkHelper::Options::CanBeFriendly);
|
|
}
|
|
if(bCanBeUnfriendly)
|
|
{
|
|
uFlags.SetFlag(CTalkHelper::Options::CanBeUnfriendly);
|
|
}
|
|
CTalkHelper::Options options(uFlags, sm_Tunables.m_MaxDistanceToTalk, sm_Tunables.m_MinDotToTalk);
|
|
CTalkHelper::Talk(*pPlayerPed, options);
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::IsPlayerStickInputValid(const CPed& rPlayerPed)
|
|
{
|
|
const CControl* pControl = rPlayerPed.GetControlFromPlayer();
|
|
if (!pControl)
|
|
return false;
|
|
|
|
bool bStickInputValid = false;
|
|
Vector2 vStickInput;
|
|
const CTaskPlayerOnFoot* pPlayerOnFootTask = static_cast<const CTaskPlayerOnFoot*>(rPlayerPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_PLAYER_ON_FOOT));
|
|
if (pPlayerOnFootTask)
|
|
{
|
|
bStickInputValid = pPlayerOnFootTask->GetLastStickInputIfValid(vStickInput);
|
|
}
|
|
|
|
if (!bStickInputValid)
|
|
{
|
|
vStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
|
|
vStickInput.y = pControl->GetPedWalkUpDown().GetNorm();
|
|
}
|
|
|
|
if (Abs(vStickInput.x) < sm_Tunables.m_DeadZoneStickNorm && Abs(vStickInput.y) < sm_Tunables.m_DeadZoneStickNorm)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
float CTaskPlayerOnFoot::StaticCoverpointScoringCB(const CCoverPoint* pCoverPoint, const Vector3& UNUSED_PARAM(vCoverPointCoords), void* pData)
|
|
{
|
|
PlayerCoverSearchData* pCoverSearchData = (PlayerCoverSearchData*)pData;
|
|
const CPed& rPlayerPed = *pCoverSearchData->m_pPlayerPed;
|
|
|
|
s32 iNoTexts = 0;
|
|
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(rPlayerPed.GetTransform().GetPosition());
|
|
camGameplayDirector& rGameplayDirector = camInterface::GetGameplayDirector();
|
|
const Vector3 vCamFront = rGameplayDirector.GetFrame().GetFront();
|
|
const bool bStickInputValid = IsPlayerStickInputValid(rPlayerPed);
|
|
const bool bHasThreat = pCoverSearchData->m_HasThreat;
|
|
|
|
// Then score it
|
|
const float fScore = ScoreCoverPoint(vPedPos, vCamFront, rPlayerPed, *pCoverPoint, false, pCoverSearchData->m_CoverProtectionDir, bStickInputValid, bHasThreat, &iNoTexts);
|
|
return fScore;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::StaticCoverpointValidityCallback (const CCoverPoint* pCoverPoint, const Vector3& UNUSED_PARAM(vCoverPointCoords), void* pData)
|
|
{
|
|
PlayerCoverSearchData* pCoverSearchData = (PlayerCoverSearchData*)pData;
|
|
const CPed& rPlayerPed = *pCoverSearchData->m_pPlayerPed;
|
|
|
|
s32 iNoTexts = 0;
|
|
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(rPlayerPed.GetTransform().GetPosition());
|
|
camGameplayDirector& rGameplayDirector = camInterface::GetGameplayDirector();
|
|
const Vector3 vCamFront = rGameplayDirector.GetFrame().GetFront();
|
|
|
|
// Validate the static cover point first
|
|
if (!IsCoverValid(vPedPos, vCamFront, rPlayerPed, *pCoverPoint, true, &iNoTexts))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::IsCoverValid(const Vector3& vPedPos, const Vector3& vCamFwd, const CPed& rPed, const CCoverPoint& rCover, bool bIsStaticCover, s32* DEBUG_DRAW_ONLY(piNoTexts))
|
|
{
|
|
taskAssertf(rPed.IsLocalPlayer(), "Don't call this on non local player peds");
|
|
|
|
Vector3 vCoverPos;
|
|
if (!rCover.GetCoverPointPosition(vCoverPos))
|
|
{
|
|
return false;
|
|
}
|
|
vCoverPos.z += 1.0f;
|
|
|
|
#if DEBUG_DRAW
|
|
char szText[128];
|
|
s32 iLocalNoTexts = 1;
|
|
s32& iNoTexts = piNoTexts ? *piNoTexts : iLocalNoTexts;
|
|
const s32 iContext = bIsStaticCover ? CCoverDebug::STATIC_COVER_EVALUATION : CCoverDebug::INITIAL_SEARCH_SIMPLE;
|
|
#endif // DEBUG_DRAW
|
|
|
|
const bool bStickInputValid = IsPlayerStickInputValid(rPed);
|
|
|
|
Vector3 vSearchDirection = vCamFwd;
|
|
Vector3 vThreatDir = vCamFwd;
|
|
Vector3 vDesired(0.0f,1.0f,0.0f);
|
|
bool bGotStickInput = false;
|
|
const CTaskPlayerOnFoot* pPlayerOnFootTask = static_cast<const CTaskPlayerOnFoot*>(rPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_PLAYER_ON_FOOT));
|
|
if (pPlayerOnFootTask && pPlayerOnFootTask->GetState() != CTaskPlayerOnFoot::STATE_PLAYER_GUN && bStickInputValid)
|
|
{
|
|
bGotStickInput = true;
|
|
vDesired.RotateZ(rPed.GetDesiredHeading());
|
|
vSearchDirection = vDesired;
|
|
vThreatDir = vDesired;
|
|
}
|
|
|
|
bool bHasThreat = false;
|
|
if (sm_Tunables.m_EvaluateThreatFromCoverPoints)
|
|
{
|
|
// Compute the desired protection direction from this cover's position
|
|
|
|
if (CTaskCover::ComputeDesiredProtectionDirection(rPed, vCoverPos, vThreatDir))
|
|
{
|
|
bHasThreat = true;
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(RCC_VEC3V(vCoverPos), RCC_VEC3V(vThreatDir), Color_red, "THREAT", CCoverDebug::ms_Tunables.m_TextOffset * -1, iContext, false);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
|
|
Vector3 vPedToCover = vCoverPos - vPedPos;
|
|
vPedToCover.Normalize();
|
|
const float fDistSqd = vPedPos.Dist2(vCoverPos);
|
|
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MIN_DIST_TO_CHECK_BEHIND_TEST, 1.0f, 0.0f, 10.0f, 0.01f)
|
|
aiDebugf2("BEHIND CHECK cover point %p, fDist = %.2f", &rCover, Sqrtf(fDistSqd));
|
|
if (NetworkInterface::IsGameInProgress() && fDistSqd > square(MIN_DIST_TO_CHECK_BEHIND_TEST))
|
|
{
|
|
aiDebugf2("BEHIND CHECK vPedToCover.Dot(vCamFwd) = %.2f", vPedToCover.Dot(vCamFwd));
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MIN_CAM_ANGLE_TO_CONSIDER_BEHIND, 0.5f, -1.0f, 1.0f, 0.01f)
|
|
if (vPedToCover.Dot(vCamFwd) < MIN_CAM_ANGLE_TO_CONSIDER_BEHIND)
|
|
{
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MIN_PED_ANGLE_TO_CONSIDER_BEHIND, 0.35f, -1.0f, 1.0f, 0.01f)
|
|
aiDebugf2("BEHIND CHECK vPedToCover.Dot(vDesired) = %.2f", vPedToCover.Dot(vDesired));
|
|
if (!bGotStickInput || vPedToCover.Dot(vDesired) < MIN_PED_ANGLE_TO_CONSIDER_BEHIND)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
const Vector3 vCoverDirection = VEC3V_TO_VECTOR3(rCover.GetCoverDirectionVector());
|
|
|
|
// We disallow cover points too far and at an acute angle from the camera (unless the cover provides protection against the threat)
|
|
Vector3 vCoverDir = VEC3V_TO_VECTOR3(rCover.GetCoverDirectionVector());
|
|
const bool bUseThreatWeight = sm_Tunables.GetUseThreatWeighting(rPed);
|
|
const bool bProvidesCoverFromThreat = bUseThreatWeight && bHasThreat && CTaskCover::DoesCoverPointProvideCoverFromThreat(vCoverDir, vThreatDir);
|
|
|
|
if (!bProvidesCoverFromThreat)
|
|
{
|
|
if (CTaskCover::IsCoverPointPositionTooFarAndAcute(rPed, vCoverPos))
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: COVER TOO FAR AND ACUTE");
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const Vector3 vToCoverDirection = fDistSqd < 1.0f ? vCoverDirection : vPedToCover;
|
|
const float fSearchDot = vSearchDirection.Dot(vToCoverDirection);
|
|
const float fThreatDot = vThreatDir.Dot(vToCoverDirection);
|
|
if (fSearchDot < sm_Tunables.m_SearchThreatMaxDot && fThreatDot < sm_Tunables.m_SearchThreatMaxDot)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: S(%.2f)T(%.2f)", fSearchDot, fThreatDot);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CTaskCover::IsCoverValid(rCover, rPed))
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: COVER TOO CLOSE TO ANOTHER PED");
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
|
|
// Disallow taking cover on the static cover on cars we're stood on (B*1173036)
|
|
if (rCover.GetEntity() && rCover.GetEntity()->GetIsTypeVehicle() &&
|
|
static_cast<const CVehicle*>(rCover.GetEntity())->GetVehicleType() == VEHICLE_TYPE_CAR &&
|
|
rPed.GetGroundPhysical() == rCover.GetEntity())
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: STOOD ON CAR");
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
|
|
// Rule out cover not within a certain angle of either the desired or camera heading
|
|
bool bValidForDesiredHeading = true;
|
|
bool bValidForCameraHeading = true;
|
|
float fDesiredAngleToCoverDot = -1.0f;
|
|
float fCameraAngleToCoverDot = -1.0f;
|
|
|
|
const float fVeryCloseIgnoreDist = rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun) ? sm_Tunables.m_VeryCloseIgnoreDesAndCamToleranceDistAimGun : sm_Tunables.m_VeryCloseIgnoreDesAndCamToleranceDist;
|
|
if (fDistSqd > square(fVeryCloseIgnoreDist))
|
|
{
|
|
const float fDesiredHeading = rPed.GetDesiredHeading();
|
|
Vector3 vDesiredDirection(0.0f, 1.0f, 0.0f);
|
|
vDesiredDirection.RotateZ(fDesiredHeading);
|
|
fDesiredAngleToCoverDot = vDesiredDirection.Dot(vPedToCover);
|
|
if (fDesiredAngleToCoverDot < sm_Tunables.m_DesiredDirToCoverMinDot)
|
|
{
|
|
bValidForDesiredHeading = false;
|
|
}
|
|
|
|
fCameraAngleToCoverDot = vCamFwd.Dot(vPedToCover);
|
|
if (fCameraAngleToCoverDot < sm_Tunables.m_CameraDirToCoverMinDot)
|
|
{
|
|
bValidForCameraHeading = false;
|
|
}
|
|
}
|
|
|
|
if (!bValidForDesiredHeading && !bValidForCameraHeading && !bProvidesCoverFromThreat)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: D(%.2f), DES(%.2f)/CAM(%.2f) DIR", sqrtf(fDistSqd), fDesiredAngleToCoverDot, fCameraAngleToCoverDot);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
|
|
// If we're very close, invalid the cover we're on if moving away from its protection vector
|
|
if (fDistSqd <= 1.0f)
|
|
{
|
|
const CControl *pControl = rPed.GetControlFromPlayer();
|
|
Vector2 vecStick(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
|
|
vecStick *= 128.0f;
|
|
vecStick.Rotate(camInterface::GetHeading());
|
|
Vector3 vCoverDir = vCoverDirection;
|
|
vecStick.Rotate(-rage::Atan2f(-vCoverDir.x, vCoverDir.y));
|
|
const bool bNotValidForStickHeading = vecStick.y < CTaskInCover::ms_Tunables.m_InputYAxisQuitValue;
|
|
if (bNotValidForStickHeading)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: StickHeading(%.2f)", vecStick.y);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const bool bIsVehicleCover = rCover.m_pEntity && rCover.m_pEntity->GetIsTypeVehicle();
|
|
|
|
// Check to see if there is collision high enough for it to be used by the player
|
|
// Dynamic tests should have already validated this so we only need to do this for static cover
|
|
// These tests need to be simple as we could be potentially testing many cover points
|
|
if (bIsStaticCover)
|
|
{
|
|
// Extend the LOS check to go beyond the cover position by the peds radius and a bit to make sure there is space.
|
|
Vector3 vLosEndCheck = vCoverPos - vPedPos;
|
|
|
|
//Check height delta
|
|
static dev_float sf_MinAcceptableStaticHeightDelta = 1.5f;
|
|
if (fabs(vLosEndCheck.z) > sf_MinAcceptableStaticHeightDelta)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: height delta too great(%.2f)", fabs(vLosEndCheck.z));
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
|
|
if (!bIsVehicleCover)
|
|
{
|
|
float fLength = vLosEndCheck.Mag();
|
|
vLosEndCheck.Normalize();
|
|
vLosEndCheck.Scale(fLength);// + PED_RADIUS + 0.01f );
|
|
}
|
|
vLosEndCheck += vPedPos;
|
|
|
|
// Do two probes one high, one low (can't do capsule test here since we could be testing loads of cover points)
|
|
Vector3 vStart = vPedPos - Vector3(0.0f, 0.0f, sm_Tunables.m_StaticLosTest1Offset);
|
|
Vector3 vEnd = vLosEndCheck - Vector3(0.0f, 0.0f, sm_Tunables.m_StaticLosTest1Offset);
|
|
|
|
Vector3 vIntersectionPos;
|
|
s32 iHitComponent;
|
|
const phInst* pHitInst = CDynamicCoverHelper::DoLineProbeTest(vStart, vEnd, vIntersectionPos, &iHitComponent);
|
|
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::sDebugLineParams lineParams;
|
|
lineParams.vStart = RCC_VEC3V(vStart);
|
|
lineParams.vEnd = pHitInst ? RCC_VEC3V(vIntersectionPos) : RCC_VEC3V(vEnd);
|
|
lineParams.color = pHitInst ? Color_black : Color_green;
|
|
lineParams.uContextHash = CCoverDebug::STATIC_COVER_EVALUATION;
|
|
CCoverDebug::AddDebugLine(lineParams);
|
|
#endif // DEBUG_DRAW
|
|
|
|
if (!pHitInst)
|
|
{
|
|
vStart = vPedPos - Vector3(0.0f, 0.0f, sm_Tunables.m_StaticLosTest2Offset);
|
|
vEnd = vLosEndCheck - Vector3(0.0f, 0.0f, sm_Tunables.m_StaticLosTest2Offset);
|
|
pHitInst = CDynamicCoverHelper::DoLineProbeTest(vStart, vEnd, vIntersectionPos, &iHitComponent);
|
|
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::sDebugLineParams lineParams;
|
|
lineParams.vStart = RCC_VEC3V(vStart);
|
|
lineParams.vEnd = pHitInst ? RCC_VEC3V(vIntersectionPos) : RCC_VEC3V(vEnd);
|
|
lineParams.color = pHitInst ? Color_black : Color_green;
|
|
lineParams.uContextHash = CCoverDebug::STATIC_COVER_EVALUATION;
|
|
CCoverDebug::AddDebugLine(lineParams);
|
|
#endif // DEBUG_DRAW
|
|
|
|
}
|
|
|
|
if (pHitInst)
|
|
{
|
|
bool bCoverValid = false;
|
|
|
|
// Allowed to hit the object for Object types
|
|
if (rCover.GetType() == CCoverPoint::COVTYPE_OBJECT)
|
|
{
|
|
const CEntity* pEntity = CPhysics::GetEntityFromInst(pHitInst);
|
|
if (pEntity && pEntity == rCover.m_pEntity)
|
|
{
|
|
bCoverValid = true;
|
|
}
|
|
}
|
|
// Allowed to hit foliage
|
|
else if (CTaskCover::HitInstIsFoliage(*pHitInst, static_cast<u16>(iHitComponent)))
|
|
{
|
|
bCoverValid = true;
|
|
}
|
|
|
|
// Otherwise cover is invalid
|
|
if (!bCoverValid)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: OBSTRUCTED");
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_black, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Ignore the height condition for vehicles, assume its valid cover
|
|
if (!bIsVehicleCover)
|
|
{
|
|
Vector3 vStart, vEnd;
|
|
vStart = vCoverPos;
|
|
vStart.z += sm_Tunables.m_CollisionLosHeightOffset;
|
|
const Vector3 vCoverDirection = VEC3V_TO_VECTOR3(rCover.GetCoverDirectionVector());
|
|
vEnd = vStart + (vCoverDirection*0.7f);
|
|
const phInst* pHitInst = CDynamicCoverHelper::DoLineProbeTest(vStart, vEnd, vIntersectionPos);
|
|
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::sDebugLineParams lineParams;
|
|
lineParams.vStart = RCC_VEC3V(vStart);
|
|
lineParams.vEnd = pHitInst ? RCC_VEC3V(vIntersectionPos) : RCC_VEC3V(vEnd);
|
|
lineParams.color = pHitInst ? Color_red : Color_brown;
|
|
lineParams.uContextHash = CCoverDebug::STATIC_COVER_EVALUATION;
|
|
CCoverDebug::AddDebugLine(lineParams);
|
|
#endif // DEBUG_DRAW
|
|
|
|
if (!pHitInst)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "INVALID: NO COLLISION FOUND");
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), Color_brown, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
float CTaskPlayerOnFoot::ScoreCoverPoint(const Vector3& vPedPos, const Vector3& vCamFwd, const CPed& rPed, const CCoverPoint& rCover, bool bIgnoreValidityCheck, const Vector3& vDesiredCoverProtectionDir, bool bStickInputValid, bool UNUSED_PARAM(bHasThreat), s32* piNoTexts)
|
|
{
|
|
if (!rPed.IsLocalPlayer())
|
|
return CTaskCover::INVALID_COVER_SCORE;
|
|
|
|
const bool bIsStaticCover = rPed.GetCoverPoint() != &rCover;
|
|
float fScore = 0.0f;
|
|
|
|
if (!bIgnoreValidityCheck)
|
|
{
|
|
if (!IsCoverValid(vPedPos, vCamFwd, rPed, rCover, bIsStaticCover, piNoTexts))
|
|
{
|
|
return CTaskCover::INVALID_COVER_SCORE;
|
|
}
|
|
}
|
|
|
|
Vector3 vCoverPos;
|
|
if (!rCover.GetCoverPointPosition(vCoverPos))
|
|
{
|
|
return fScore;
|
|
}
|
|
vCoverPos.z += 1.0f;
|
|
|
|
#if DEBUG_DRAW
|
|
char szText[128];
|
|
Color32 color = bIsStaticCover ? Color_red : Color_orange;
|
|
s32 iLocalNoTexts = (bIsStaticCover && !bIgnoreValidityCheck) ? 0 : 1;
|
|
s32& iNoTexts = piNoTexts ? *piNoTexts : iLocalNoTexts;
|
|
const s32 iContext = bIsStaticCover ? bIgnoreValidityCheck ? CCoverDebug::INITIAL_SEARCH_SIMPLE : CCoverDebug::STATIC_COVER_EVALUATION : CCoverDebug::INITIAL_SEARCH_SIMPLE;
|
|
if (bIsStaticCover)
|
|
{
|
|
formatf(szText, "%p SCORING (Actual/Max)", &rCover);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
// Scripted PRIORITY bonus
|
|
if (rCover.GetFlag(CCoverPoint::COVFLAGS_IsPriority))
|
|
{
|
|
fScore += sm_Tunables.m_PriorityCoverWeight;
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)","PRIORITY", sm_Tunables.m_PriorityCoverWeight, sm_Tunables.m_PriorityCoverWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
|
|
// Removed for B*1399215
|
|
// // Capsule object weighting
|
|
// if (rCover.m_pEntity && rCover.m_pEntity->GetIsTypeObject())
|
|
// {
|
|
// const phInst* pInst = rCover.m_pEntity->GetCurrentPhysicsInst();
|
|
// if (pInst)
|
|
// {
|
|
// const phBound* pBound = pInst->GetArchetype()->GetBound();
|
|
// if (pBound)
|
|
// {
|
|
// float fRadius = -1.0f;
|
|
//
|
|
// if (pBound->GetType()==phBound::CAPSULE)
|
|
// {
|
|
// fRadius = pBound->GetRadiusAroundCentroid();
|
|
// }
|
|
// else if (pBound->GetType()==phBound::COMPOSITE)
|
|
// {
|
|
// // Find the first capsule or cylinder bound and use that radius (a bit dodgy for objects that comprise of multiple of these I guess)
|
|
// const phBoundComposite* pBoundComposite = static_cast<const phBoundComposite*>(pBound);
|
|
// const s32 iNumBounds = pBoundComposite->GetNumBounds();
|
|
// for (s32 i=0; i<iNumBounds; ++i)
|
|
// {
|
|
// const phBound* pTestBound = pBoundComposite->GetBound(i);
|
|
// if (pTestBound)
|
|
// {
|
|
// if (pTestBound->GetType()==phBound::CAPSULE)
|
|
// {
|
|
// const phBoundCapsule* pBndCapsule = (const phBoundCapsule*)pTestBound;
|
|
// fRadius = pBndCapsule->GetRadius();
|
|
// break;
|
|
// }
|
|
// else if (pTestBound->GetType()==phBound::CYLINDER)
|
|
// {
|
|
// const phBoundCylinder* pBndCylinder = (const phBoundCylinder*)pTestBound;
|
|
// fRadius = pBndCylinder->GetRadius();
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (fRadius > 0.0f && fRadius < sm_Tunables.m_SmallCapsuleCoverRadius)
|
|
// {
|
|
// fScore -= sm_Tunables.m_SmallCapsuleCoverPenalty;
|
|
// #if DEBUG_DRAW
|
|
// formatf(szText, "%-10s (%.2f/%.2f)","-CAPSULE", sm_Tunables.m_SmallCapsuleCoverPenalty, sm_Tunables.m_SmallCapsuleCoverPenalty);
|
|
// CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
// #endif // DEBUG_DRAW
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
const Vector3 vCoverDirection = VEC3V_TO_VECTOR3(rCover.GetCoverDirectionVector());
|
|
Vector3 vPedToCover = vCoverPos - vPedPos;
|
|
vPedToCover.Normalize();
|
|
|
|
// EDGE bonus
|
|
// Prefer edge cover if its high
|
|
const bool bIsEdgeCover = rCover.IsEdgeOrDoubleEdgeCoverPoint();
|
|
const bool bIsLowCover = rCover.GetHeight() != CCoverPoint::COVHEIGHT_TOOHIGH;
|
|
if (bIsEdgeCover)
|
|
{
|
|
bool bPreferEdgeCover = false;
|
|
|
|
if (!bIsLowCover)
|
|
{
|
|
const bool bIsLeftEdge = rCover.GetUsage() == CCoverPoint::COVUSE_WALLTORIGHT;
|
|
const bool bIsRightEdge = rCover.GetUsage() == CCoverPoint::COVUSE_WALLTOLEFT;
|
|
if (bIsLeftEdge || bIsRightEdge)
|
|
{
|
|
bPreferEdgeCover = true;
|
|
}
|
|
}
|
|
|
|
if (bPreferEdgeCover)
|
|
{
|
|
fScore += sm_Tunables.m_EdgeCoverWeight;
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)","EDGE", sm_Tunables.m_EdgeCoverWeight, sm_Tunables.m_EdgeCoverWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
|
|
// VERY CLOSE TO COVER bonus
|
|
bool bHasThreat = false;
|
|
const float fMaxVeryCloseDistSqd = square(sm_Tunables.m_VeryCloseToCoverDist);
|
|
const float fPedToCoverDistSqd = vPedPos.Dist2(vCoverPos);
|
|
const bool bVeryCloseToCover = fPedToCoverDistSqd < fMaxVeryCloseDistSqd;
|
|
if (bVeryCloseToCover)
|
|
{
|
|
const float fVeryCloseScale = 1.0f - Clamp(fPedToCoverDistSqd / fMaxVeryCloseDistSqd, 0.0f, 1.0f);
|
|
const float fVeryCloseDistScore = fVeryCloseScale * sm_Tunables.m_VeryCloseToCoverWeight;
|
|
fScore += fVeryCloseDistScore;
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)","CLOSE DIST", fVeryCloseDistScore, sm_Tunables.m_VeryCloseToCoverWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
|
|
// THREAT WEIGHTING bonus
|
|
const bool bUseThreatWeight = sm_Tunables.GetUseThreatWeighting(rPed);
|
|
if (bUseThreatWeight || rPed.GetPlayerResetFlag(CPlayerResetFlags::PRF_USE_COVER_THREAT_WEIGHTING))
|
|
{
|
|
Vector3 vThreatDir = vDesiredCoverProtectionDir;
|
|
if (sm_Tunables.m_EvaluateThreatFromCoverPoints)
|
|
{
|
|
// Compute the desired protection direction from this cover's position
|
|
if (CTaskCover::ComputeDesiredProtectionDirection(rPed, vCoverPos, vThreatDir))
|
|
{
|
|
bHasThreat = true;
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(RCC_VEC3V(vCoverPos), RCC_VEC3V(vThreatDir), Color_yellow, "THREAT", CCoverDebug::ms_Tunables.m_TextOffset * -1, iContext, false);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
|
|
if (bHasThreat)
|
|
{
|
|
const float fThreatWeightingScale = Clamp(vCoverDirection.Dot(vThreatDir), 0.0f, 1.0f);
|
|
const float fThreatProtectionScore = fThreatWeightingScale * sm_Tunables.m_ThreatDirWeight;
|
|
fScore += fThreatProtectionScore;
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)", "THREAT", fThreatProtectionScore, sm_Tunables.m_ThreatDirWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
|
|
if (!bIsLowCover && bIsEdgeCover && !rPed.GetPedResetFlag(CPED_RESET_FLAG_IgnoreThreatEngagePlayerCoverBonus))
|
|
{
|
|
const bool bThreatLeftOfCover = vCoverDirection.CrossZ(vThreatDir) > 0.0f ? true : false;
|
|
const bool bIsLeftEdgeCover = rCover.GetUsage() == CCoverPoint::COVUSE_WALLTORIGHT;
|
|
if ((bThreatLeftOfCover && bIsLeftEdgeCover) || (!bThreatLeftOfCover && !bIsLeftEdgeCover))
|
|
{
|
|
const float fThreatEngageScore = sm_Tunables.m_ThreatEngageDirWeight;
|
|
fScore += fThreatEngageScore;
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)", "ENGAGE", fThreatEngageScore, sm_Tunables.m_ThreatEngageDirWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// DIST TO COVER bonus
|
|
const float fMaxDistSqd = square(CTaskEnterCover::ms_Tunables.m_CoverEntryMaxDistance);
|
|
const float fDistToCoverScale = 1.0f - Clamp(fPedToCoverDistSqd / fMaxDistSqd, 0.0f, 1.0f);
|
|
float fDistToCoverWeight = bHasThreat ? sm_Tunables.m_DistToCoverWeightThreat : sm_Tunables.m_DistToCoverWeight;
|
|
if (!bStickInputValid)
|
|
{
|
|
fDistToCoverWeight += sm_Tunables.m_DistToCoverWeightNoStickBonus;
|
|
}
|
|
const float fDistScore = fDistToCoverScale * fDistToCoverWeight;
|
|
fScore += fDistScore;
|
|
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)","DIST", fDistScore, fDistToCoverWeight + sm_Tunables.m_DistToCoverWeightNoStickBonus);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
|
|
const float fDesiredHeading = rPed.GetDesiredHeading();
|
|
Vector3 vDesiredDirection(0.0f, 1.0f, 0.0f);
|
|
vDesiredDirection.RotateZ(fDesiredHeading);
|
|
|
|
// If not pushing the stick, take the direction the camera is pointing as an indication of the cover we want to be at
|
|
if (!bStickInputValid)
|
|
{
|
|
vDesiredDirection = vCamFwd;
|
|
}
|
|
else
|
|
{
|
|
TUNE_GROUP_BOOL(COVER_TUNE, USE_CAMERA_HEADING_IF_COVER_FAR_AND_ACUTE, true);
|
|
if (USE_CAMERA_HEADING_IF_COVER_FAR_AND_ACUTE)
|
|
{
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, FAR_FROM_COVER, 4.0f, 0.0f, 10.0f, 0.01f);
|
|
if (fMaxDistSqd > FAR_FROM_COVER)
|
|
{
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, ANGLE_FROM_CAM, 1.0f, 0.0f, 3.0f, 0.01f);
|
|
const float fCamHeading = camInterface::GetHeading();
|
|
const float fHeadingDelta = fwAngle::LimitRadianAngle(fCamHeading - fDesiredHeading);
|
|
if (fHeadingDelta > ANGLE_FROM_CAM)
|
|
{
|
|
vDesiredDirection = vCamFwd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool bAimingGun = rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun);
|
|
|
|
// DESIRED HEADING TO COVER ANGLE bonus
|
|
// When aiming we want to prefer cover in the direction of the camera, so weigh the dir to cover less when aiming
|
|
float fDesiredDirToCoverMaxWeight = bAimingGun ? sm_Tunables.m_DesiredDirToCoverAimingWeight : sm_Tunables.m_DesiredDirToCoverWeight;
|
|
const float fDesiredAngleToCoverScale = Clamp(vDesiredDirection.Dot(vPedToCover), 0.0f, 1.0f);
|
|
const float fDesiredAngleScore = fDesiredAngleToCoverScale * fDesiredDirToCoverMaxWeight;
|
|
fScore += fDesiredAngleScore;
|
|
|
|
#if DEBUG_DRAW
|
|
if (!bStickInputValid)
|
|
{
|
|
formatf(szText, "%-10s (%.2f/%.2f)","NOSTICK", fDesiredAngleScore, fDesiredDirToCoverMaxWeight);
|
|
}
|
|
else
|
|
{
|
|
formatf(szText, "%-10s (%.2f/%.2f)","DES ANG", fDesiredAngleScore, fDesiredDirToCoverMaxWeight);
|
|
}
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
|
|
|
|
// Scale the max desired angle score down based on distance so if we're close to cover
|
|
// this has less of an effect, don't do this when aiming and close to the cover
|
|
const float fMaxCoverDirToCamWeight = bAimingGun && bVeryCloseToCover ? sm_Tunables.m_CoverDirToCameraWeightMaxAimGun : sm_Tunables.m_CoverDirToCameraWeightMax;
|
|
const float fCoverDirMaxDistSqd = square(sm_Tunables.m_CoverDirToCameraWeightMaxScaleDist);
|
|
const float fCoverDirToCameraWeightDistScale = bAimingGun ? 1.0f : Clamp(fPedToCoverDistSqd / fCoverDirMaxDistSqd, 0.0f, 1.0f);
|
|
const float fTotalCoverDirToCameraWeight = (1.0f - fCoverDirToCameraWeightDistScale) * sm_Tunables.m_CoverDirToCameraWeightMin + fCoverDirToCameraWeightDistScale * fMaxCoverDirToCamWeight;
|
|
|
|
const float fDesiredCoverDirToCameraScale = Clamp(vCamFwd.Dot(vCoverDirection), 0.0f, 1.0f);
|
|
const float fDesiredCoverDirScore = fDesiredCoverDirToCameraScale * fTotalCoverDirToCameraWeight;
|
|
fScore += fDesiredCoverDirScore;
|
|
float fHighestScore = sm_Tunables.m_PriorityCoverWeight + sm_Tunables.m_EdgeCoverWeight + sm_Tunables.m_VeryCloseToCoverWeight + sm_Tunables.m_DistToCoverWeightNoStickBonus + fDistToCoverWeight + fDesiredDirToCoverMaxWeight + fMaxCoverDirToCamWeight;
|
|
|
|
if (sm_Tunables.GetUseThreatWeighting(rPed))
|
|
{
|
|
fHighestScore += sm_Tunables.m_ThreatDirWeight;
|
|
fHighestScore += sm_Tunables.m_ThreatEngageDirWeight;
|
|
}
|
|
|
|
// CAMERA HEADING TO COVER ANGLE bonus
|
|
if (bHasThreat || bStickInputValid)
|
|
{
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "%-10s (%.2f/%.2f)","CAM ANG", fDesiredCoverDirScore, fMaxCoverDirToCamWeight);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
formatf(szText, "%-8s (%.2f/%.2f)","TOTAL", fScore, fHighestScore);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), color, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
|
|
fScore = 1.0f - Clamp(fScore / fHighestScore, 0.0f, 1.0f);
|
|
#if DEBUG_DRAW
|
|
formatf(szText, "NORM SCORE (Lower is better) : %.2f", fScore);
|
|
Color32 iColor = Color32(0.0f, 1.0f - fScore, 0.0f);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vCoverPos), iColor, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, iContext);
|
|
#endif // DEBUG_DRAW
|
|
|
|
return fScore;
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
void CTaskPlayerOnFoot::DrawInitialCoverDebug(const CPed& rPed, s32& iNumPlayerTexts, s32 iTextOffset, const Vector3& vDesiredCoverProtectionDir)
|
|
{
|
|
if (CCoverDebug::ms_Tunables.m_EnableNewCoverDebugRendering)
|
|
{
|
|
if (!CCoverDebug::ms_bInitializedDebugDrawStores)
|
|
{
|
|
CCoverDebug::ms_Tunables.m_DefaultCoverDebugContext.Init();
|
|
CCoverDebug::ms_bInitializedDebugDrawStores = true;
|
|
}
|
|
}
|
|
|
|
const Vec3V vPlayerPos = rPed.GetTransform().GetPosition();
|
|
const Vec3V vPlayerDir = rPed.GetTransform().GetB();
|
|
const Vec3V vDesiredDir = RotateAboutZAxis(Vec3V(V_Y_AXIS_WZERO), ScalarVFromF32(rPed.GetDesiredHeading()));
|
|
const Vec3V vCameraPos = CCoverDebug::GetGamePlayCamPosition();
|
|
const Vec3V vCameraDir = CCoverDebug::GetGamePlayCamDirection();
|
|
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(vCameraPos, vCameraDir, Color_blue, "CAMERA", 0, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(vPlayerPos, RCC_VEC3V(vDesiredCoverProtectionDir), Color_red, "THREAT", iTextOffset * iNumPlayerTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(vPlayerPos, vPlayerDir, Color_green, "PLAYER", iTextOffset * iNumPlayerTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
|
|
CCoverDebug::sDebugArrowParams arrowParams;
|
|
arrowParams.vPos = vPlayerPos;
|
|
arrowParams.vDir = vDesiredDir;
|
|
arrowParams.color = Color_yellow;
|
|
arrowParams.szName = "DESIRED HEADING";
|
|
arrowParams.bAddText = true;
|
|
arrowParams.iYTextOffset = iTextOffset * iNumPlayerTexts++;
|
|
arrowParams.uContextHash = CCoverDebug::INITIAL_SEARCH_SIMPLE;
|
|
CCoverDebug::AddDebugDirectionArrow(arrowParams);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool CTaskPlayerOnFoot::UseAnalogRun(const CPed* pPlayerPed) const
|
|
{
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_TUNE, DisableAnalogStickRun, false);
|
|
if(DisableAnalogStickRun)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!pPlayerPed->GetMotionData()->GetUsingStealth() && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false, false, true))
|
|
{
|
|
const CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
bool bSprintDisabled = pControl && pControl->IsInputDisabled(INPUT_SPRINT) && !CPlayerSpecialAbilityManager::HasSpecialAbilityDisabledSprint();
|
|
if( !bSprintDisabled &&
|
|
(sm_Tunables.m_AllowFPSAnalogStickRunInInteriors || !pPlayerPed->GetIsInInterior() || pPlayerPed->GetPortalTracker()->IsAllowedToRunInInterior() || pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IgnoreInteriorCheckForSprinting)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ProcessViewModeSwitch(CPed* pPlayerPed)
|
|
{
|
|
bool bSwitchedIntoFirstPersonMode = false;
|
|
bool bIsInFirstPersonMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false, false, true);
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_VIEW_MODE_TUNE, DONT_SWITCH_ON_FIRST_FRAME, true);
|
|
if (bIsInFirstPersonMode && !m_WasInFirstPerson && ((DONT_SWITCH_ON_FIRST_FRAME && GetState() != STATE_INITIAL) || pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_SwitchingCharactersInFirstPerson)))
|
|
{
|
|
bSwitchedIntoFirstPersonMode = true;
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_SwitchingCharactersInFirstPerson))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_SwitchingCharactersInFirstPerson, false);
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_VIEW_MODE_TUNE, FORCE_INSTANT_IK_BLEND_IN_ON_SWITCH, true);
|
|
if (FORCE_INSTANT_IK_BLEND_IN_ON_SWITCH)
|
|
{
|
|
m_bForceInstantIkBlendInThisFrame = true;
|
|
}
|
|
}
|
|
m_WasInFirstPerson = bIsInFirstPersonMode;
|
|
|
|
if((pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON) || bSwitchedIntoFirstPersonMode) && !pPlayerPed->GetIsSwimming())
|
|
{
|
|
if(!pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_FIRST_PERSON) && !bSwitchedIntoFirstPersonMode)
|
|
{
|
|
if(!pPlayerPed->GetMotionData()->GetIsFPSIdle() && pPlayerPed->GetEquippedWeaponInfo() && !pPlayerPed->GetEquippedWeaponInfo()->GetIsUnarmed() && !pPlayerPed->GetEquippedWeaponInfo()->GetIsMeleeFist())
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Aiming, true);
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_InstantBlendToAimNoSettle, true);
|
|
}
|
|
else
|
|
{
|
|
float fMBR = pPlayerPed->GetMotionData()->GetCurrentMoveBlendRatio().Mag();
|
|
|
|
Vector3 vDesiredMBR(pPlayerPed->GetMotionData()->GetDesiredMoveBlendRatio().x, pPlayerPed->GetMotionData()->GetDesiredMoveBlendRatio().y, 0.f);
|
|
if(vDesiredMBR.Mag2() > 0.01f && !pPlayerPed->GetUsingRagdoll())
|
|
{
|
|
vDesiredMBR.Normalize();
|
|
float fDesiredHeading = rage::Atan2f(-vDesiredMBR.x, vDesiredMBR.y);
|
|
fDesiredHeading = fDesiredHeading + pPlayerPed->GetCurrentHeading();
|
|
fDesiredHeading = fwAngle::LimitRadianAngle(fDesiredHeading);
|
|
// Set the heading to the direction of motion
|
|
pPlayerPed->SetHeading(fDesiredHeading);
|
|
}
|
|
|
|
if(pPlayerPed->GetMotionData()->GetUsingStealth())
|
|
{
|
|
if(!pPlayerPed->GetMotionData()->GetIsStill(fMBR))
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Stealth_Walk, true);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Stealth_Idle, true);
|
|
}
|
|
}
|
|
else if(pPlayerPed->WantsToUseActionMode())
|
|
{
|
|
if(!pPlayerPed->IsUsingActionMode())
|
|
{
|
|
pPlayerPed->UpdateMovementMode();
|
|
}
|
|
|
|
if(!pPlayerPed->GetMotionData()->GetIsStill(fMBR))
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_ActionMode_Walk, true);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_ActionMode_Idle, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pPlayerPed->GetMotionData()->GetIsSprinting(fMBR))
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Sprint, true);
|
|
}
|
|
else if(!pPlayerPed->GetMotionData()->GetIsStill(fMBR))
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Walk, true);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Idle, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!pPlayerPed->GetIsInCover() && (pPlayerPed->GetAttachParent()==NULL))
|
|
{
|
|
// When going into FPS when climbing a ladder we want to keep the heading of the
|
|
// ped as otherwise we would end up rotating the ped away from the ladder
|
|
const CTask* pClimbLadderTask = pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_CLIMB_LADDER);
|
|
if( pClimbLadderTask == NULL )
|
|
{
|
|
float fCamOrient = fwAngle::LimitRadianAngle(camInterface::GetPlayerControlCamHeading(*pPlayerPed));
|
|
if (!pPlayerPed->GetUsingRagdoll())
|
|
{
|
|
pPlayerPed->SetHeading(fCamOrient);
|
|
}
|
|
|
|
if(!pPlayerPed->GetMotionData()->GetIsFPSIdle())
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_InstantBlendToAimNoSettle, true);
|
|
}
|
|
|
|
pPlayerPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Aiming, true);
|
|
|
|
// Update anims before camera, so the camera can be positioned correctly, based on the new ped pose
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_ForcePreCameraAnimUpdate, true);
|
|
}
|
|
}
|
|
|
|
pPlayerPed->SetClothForcePin(1);
|
|
|
|
// If we are wanting to aim, ensure the gun task is also restarted
|
|
if(pPlayerPed->GetMotionData()->GetForcedMotionStateThisFrame() == CPedMotionStates::MotionState_Aiming)
|
|
{
|
|
CTaskAimGunOnFoot* pAimGunTask = static_cast<CTaskAimGunOnFoot*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AIM_GUN_ON_FOOT));
|
|
if(pAimGunTask)
|
|
{
|
|
pAimGunTask->SetForceRestart(true);
|
|
pPlayerPed->GetPedAiLod().SetForceNoTimesliceIntelligenceUpdate(true);
|
|
pPlayerPed->GetIkManager().SetTorsoYawDeltaLimits(PI, 100.f, 0.f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::ProcessPreFSM()
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
m_bForceInstantIkBlendInThisFrame = false;
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
FPS_MODE_SUPPORTED_ONLY(ProcessViewModeSwitch(pPed));
|
|
|
|
// Flags
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_ForceProcessPedStandingUpdateEachSimStep, true); // Make sure to do ProcessPedStanding() each physics update step, probably safest.
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_ProcessPreRender2, true);
|
|
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_InWaterTaskQuitToClimbLadder, false );
|
|
|
|
Assert(pPed->IsPlayer());
|
|
CControl *pControl = pPed->GetControlFromPlayer();
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// Set flag to transition ped to swimming/diving motion tasks if left stick is pushed forwards (as opposed to aiming which is used for underwater strafing)
|
|
|
|
if(pPed->GetMotionData() && pPed->GetMotionData()->GetCombatRoll() && pControl && HasValidEnterVehicleInput(*pControl))
|
|
{
|
|
m_bGetInVehicleAfterCombatRolling = true;
|
|
}
|
|
|
|
if (pPed->GetIsSwimming() && pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
if (pControl)
|
|
{
|
|
Vector2 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
|
|
|
|
static dev_float fDiveStrafeThresholdX = 0.75f;
|
|
static dev_float fDiveStrafeThresholdY = 0.0f;
|
|
|
|
// Force swim task if we want to enter a vehicle
|
|
bool bIsEnteringVehicle = (GetState() == STATE_GET_IN_VEHICLE);
|
|
|
|
// B*2038392: Keep ped in the swim/dive task if sprint button is down
|
|
bool bSprintButtonDown = pControl->GetPedSprintIsDown();
|
|
//B*2065912: Stay in swim motion task if we're still sprinting (despite letting go of sprint button), unless we're trying to strafe
|
|
bool bIsSprinting = pPed->GetMotionData()->GetIsDesiredSprinting(pPed->GetMotionData()->GetFPSCheckRunInsteadOfSprint());
|
|
|
|
// If player tries to run/sprint, reset the swim-sprint timer that is used to keep us in the swim task when script clamp the max MBR
|
|
if (bSprintButtonDown || bIsSprinting)
|
|
{
|
|
m_fTimeSinceLastSwimSprinted = 0.0f;
|
|
}
|
|
|
|
// Script have clamped the max MBR, so use a timer from when sprint button was last pressed to keep us in the swim motion task
|
|
bool bKeepSwimming = false;
|
|
if(pPed->GetMotionData()->GetScriptedMaxMoveBlendRatio() < MOVEBLENDRATIO_SPRINT)
|
|
{
|
|
static dev_float fStayInSwimTimer = 1.0f;
|
|
if (m_fTimeSinceLastSwimSprinted < fStayInSwimTimer)
|
|
{
|
|
bKeepSwimming = true;
|
|
m_fTimeSinceLastSwimSprinted += fwTimer::GetTimeStep();
|
|
}
|
|
}
|
|
|
|
// If left stick is within the dive threshold, set the flag to force us into TaskMotionDiving, else use TaskMotionAiming for swim-strafing
|
|
bool PushingForwardsToSwim = (Abs(vStickInput.x) < fDiveStrafeThresholdX && vStickInput.y > fDiveStrafeThresholdY);
|
|
|
|
// B*2184371: Fix for jittering between strafe/swim tasks due to low energy causing desired MBR to drop to (0,2).
|
|
// Remain in swim state if we recently had the sprint button down and our sprint bar is replenishing.
|
|
static dev_u32 fSprintWasDownTime = 150;
|
|
bool bTryingToSprintButNoEnergy = !bSprintButtonDown && !bIsSprinting && pPed->GetPlayerInfo() && pPed->GetPlayerInfo()->GetPlayerDataSprintReplenishing() && pControl->GetPedSprintHistoryHeldDown(fSprintWasDownTime);
|
|
|
|
if (PushingForwardsToSwim || bIsEnteringVehicle || bSprintButtonDown || bIsSprinting || bKeepSwimming || bTryingToSprintButNoEnergy)
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_FPSSwimUseSwimMotionTask, true);
|
|
}
|
|
else
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_FPSSwimUseAimingMotionTask, true);
|
|
// Force into strafing
|
|
pPed->SetIsStrafing(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// B*2042421: Creating the mobile phone is delayed by a frame so the "SetIsStrafing" call in CTaskMobilePhone::ProcessPreFSM is delayed by 1 frame
|
|
// so we were swapping to CTaskMotionPed::State_OnFoot causing us to spin around before switching back to strafing again. This is due to the code that calls
|
|
// CreateOrResumeMobilePhoneTask in CTaskPlayerIdles::MovementAndAmbientClips_OnUpdate waiting for the sub-tasks sub-task to be created! Too scary to change at this point.
|
|
if (pPed->IsFirstPersonShooterModeEnabledForPlayer(false) && CTaskPlayerOnFoot::CheckForUseMobilePhone(*pPed) && GetState() != STATE_MELEE)
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_ForcePedToStrafe, true);
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_IsAiming, true);
|
|
}
|
|
|
|
// B*2276638: Block weapon switching and cover entry when aiming homing launcher. These inputs are used for swapping homing targets.
|
|
if (pPed->GetPlayerInfo() && pPed->GetEquippedWeaponInfo() && pPed->GetEquippedWeaponInfo()->GetIsOnFootHoming() && pPed->GetPlayerInfo()->IsHardAiming() &&
|
|
pPed->GetPedIntelligence() && pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_AIM_GUN_ON_FOOT))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_TemporarilyBlockWeaponSwitching, true);
|
|
pPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_DISABLE_CAN_USE_COVER);
|
|
}
|
|
|
|
// B*2047112: Do a weapon blocked check (as this is only done from TASK_AIM_GUN_ON_FOOT which is not run when sprinting).
|
|
// If blocked, set CPED_RESET_FLAG_WeaponBlockedInFPSMode which will force us into the gun state to use the blocked anims.
|
|
if (GetState() != STATE_PLAYER_GUN && pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
Vector3 vTargetPos(Vector3::ZeroType);
|
|
CWeapon* pEquippedWeapon = pPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if (pEquippedWeapon)
|
|
{
|
|
const CObject* pWeaponObject = pPed->GetWeaponManager()->GetEquippedWeaponObject();
|
|
if (pWeaponObject)
|
|
{
|
|
const Matrix34& mWeapon = RCC_MATRIX34(pWeaponObject->GetMatrixRef());
|
|
Vector3 vStart( Vector3::ZeroType );
|
|
pEquippedWeapon->CalcFireVecFromAimCamera( pPed, mWeapon, vStart, vTargetPos );
|
|
|
|
static dev_float fProbeLength = 1.0f;
|
|
static dev_float fProbeLengthMult = 1.0f;
|
|
if(CTaskWeaponBlocked::IsWeaponBlocked(pPed, vTargetPos, NULL, fProbeLengthMult, fProbeLength))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_WeaponBlockedInFPSMode, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pPed->GetPedResetFlag(CPED_RESET_FLAG_WeaponBlockedInFPSMode) && pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_WEAPON_BLOCKED))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_WeaponBlockedInFPSMode, true);
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
// Update the lockon status (Switching targets etc...)
|
|
UpdatePlayerLockon( pPed );
|
|
|
|
// Keeps a history of the last recored stick input
|
|
UpdateMovementStickHistory(pControl);
|
|
|
|
ProcessPlayerEvents();
|
|
|
|
if(pPed->IsLocalPlayer())
|
|
{
|
|
// CNC: Cop players should enter action mode when they spot a crook (within range).
|
|
if (CNC_PlayerCopShouldTriggerActionMode(pPed))
|
|
{
|
|
TUNE_GROUP_FLOAT(CNC_RESPONSIVENESS, fCopTimeToStayInActionModeAfterSeeingCrook, 10.0f, 0.0f, 60.0f, 1);
|
|
pPed->SetUsingActionMode(true, CPed::AME_CopSeenCrookCNC, fCopTimeToStayInActionModeAfterSeeingCrook);
|
|
}
|
|
|
|
const CWanted &Wanted = pPed->GetPlayerInfo()->GetWanted();
|
|
|
|
bool bSetActionModeDueToWanted = false;
|
|
if(Wanted.GetWantedLevel() >= WANTED_LEVEL1)
|
|
{
|
|
if(!Wanted.CopsAreSearching() && (Wanted.m_iTimeFirstSpotted>0) )
|
|
{
|
|
m_b1stTimeSeenWhenWanted = true;
|
|
}
|
|
|
|
// CNC: Always go into action mode when wanted.
|
|
if (CPlayerInfo::AreCNCResponsivenessChangesEnabled(pPed))
|
|
{
|
|
bSetActionModeDueToWanted = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_b1stTimeSeenWhenWanted = false;
|
|
}
|
|
|
|
pPed->SetUsingActionMode(m_b1stTimeSeenWhenWanted || bSetActionModeDueToWanted, CPed::AME_Wanted);
|
|
|
|
if(pPed->GetPedIntelligence()->IsBattleAware())
|
|
{
|
|
float fMinTimer = CPedIntelligence::BATTLE_AWARENESS_MIN_TIME;
|
|
pPed->SetUsingActionMode(true, CPed::AME_Combat, fMinTimer, pPed->GetPedIntelligence()->IsBattleAwareForcingRun());
|
|
}
|
|
}
|
|
|
|
if(NetworkInterface::IsInCopsAndCrooks())
|
|
{
|
|
bool bStopArrest = false;
|
|
|
|
//Arrest timers
|
|
if(m_bArrestTimerActive)
|
|
{
|
|
if(CNetwork::GetNetworkTime() > m_uArrestSprintBreakoutTime)
|
|
{
|
|
if(pPed->GetControlFromPlayer()->GetPedSprintIsDown())
|
|
{
|
|
bStopArrest = true;
|
|
m_bArrestTimerActive = false;
|
|
}
|
|
}
|
|
|
|
if(CNetwork::GetNetworkTime() > m_uArrestFinishTime)
|
|
{
|
|
bStopArrest = true;
|
|
m_bArrestTimerActive = false;
|
|
}
|
|
else if (CNetwork::GetNetworkTime() < m_uArrestFinishTime)
|
|
{
|
|
//Block a bunch of things we don't want to happen while arresting
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_TemporarilyBlockWeaponSwitching, true);
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePlayerJumping, true);
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePlayerVaulting, true);
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePlayerCombatRoll, true);
|
|
|
|
if(m_pTargetPed)
|
|
{
|
|
if(CPlayerInfo *pPlayerInfo = pPed->GetPlayerInfo())
|
|
{
|
|
// Set cop to lockon to crook
|
|
pPlayerInfo->GetTargeting().SetLockOnTarget(m_pTargetPed);
|
|
}
|
|
|
|
if(!m_pTargetPed->IsVisible())
|
|
{
|
|
bStopArrest = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bStopArrest)
|
|
{
|
|
if (CPlayerInfo *pPlayerInfo = pPed->GetPlayerInfo())
|
|
{
|
|
// Remove locked target
|
|
pPlayerInfo->GetTargeting().SetLockOnTarget(nullptr);
|
|
}
|
|
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_ForcedAimFromArrest, false);
|
|
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting, false);
|
|
|
|
m_pTargetPed = nullptr;
|
|
|
|
CPedWeaponManager* pWeaponManager = GetPed()->GetWeaponManager();
|
|
if (pWeaponManager )
|
|
{
|
|
if(pWeaponManager->GetBackupWeapon() != 0)
|
|
{
|
|
pWeaponManager->RestoreBackupWeapon();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// m_pTargetVehicle is explicitly NULLed before reuse so the target vehicle is available after abort so we can resume
|
|
m_pTargetAnimal = NULL;
|
|
//Ladder
|
|
Vector2 vDesiredMoveRatio;
|
|
pPed->GetMotionData()->GetDesiredMoveBlendRatio(vDesiredMoveRatio);
|
|
|
|
if (!m_pLadderClipRequestHelper)
|
|
m_pLadderClipRequestHelper = CLadderClipSetRequestHelperPool::GetNextFreeHelper(pPed);
|
|
|
|
// Only if we move, otherwise we might as well just be close to a ladder standing still
|
|
if (!m_bWaitingForLadder && vDesiredMoveRatio.Mag2() != MOVEBLENDRATIO_STILL && GetState() != STATE_CLIMB_LADDER)
|
|
{
|
|
m_pTargetLadder = NULL;
|
|
m_iLadderIndex = 0;
|
|
|
|
if (m_pLadderClipRequestHelper)
|
|
m_pLadderClipRequestHelper->m_fLastClosestLadder = 0.f;
|
|
}
|
|
|
|
m_pTargetUncuffPed = NULL;
|
|
m_pTargetArrestPed = NULL;
|
|
|
|
CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
|
|
if (pWeaponManager)
|
|
{
|
|
fwMvClipSetId weaponHoldingClipsetId = CTaskCover::GetWeaponHoldingClipSetForArmament(pPed);
|
|
CTaskCover::RequestCoverClipSet(weaponHoldingClipsetId, SP_High);
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// Store the result
|
|
pPed->GetMotionData()->SetUsingAnalogStickRun(UseAnalogRun(pPed));
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
dev_float TIME_TO_INTIMIDATE_PED = 3.0f;
|
|
dev_float DIST_TO_INTIMIDATE_PED = 1.5f;
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::ProcessPostFSM()
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
const float fTimeStep = GetTimeStep();
|
|
|
|
if (m_pLadderClipRequestHelper && (!m_pTargetLadder || m_pLadderClipRequestHelper->m_fLastClosestLadder >= ms_fStreamClosestLadderMinDistSqr))
|
|
m_pLadderClipRequestHelper->ReleaseAllClipSets();
|
|
|
|
// Reset the flag that prevents the player from entering cover
|
|
m_bPlayerExitedCoverThisUpdate = false;
|
|
|
|
if(pPed->GetMotionData() && !pPed->GetMotionData()->GetCombatRoll())
|
|
{
|
|
m_bGetInVehicleAfterCombatRolling = false;
|
|
}
|
|
|
|
// Find a ped in front of us to intimidate
|
|
CPed* pPedToIndimidate = NULL;
|
|
if( GetState() == STATE_PLAYER_IDLES && m_fTimeBetweenIntimidations <= 0.0f )
|
|
{
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
Vector3 vPedToIntimidatePos(Vector3::ZeroType);
|
|
CEntityScannerIterator entityList = pPed->GetPedIntelligence()->GetNearbyPeds();
|
|
for( CEntity* pEnt = entityList.GetFirst(); pEnt; pEnt = entityList.GetNext() )
|
|
{
|
|
CPed* pOtherPed = static_cast<CPed*>(pEnt);
|
|
if( pOtherPed->PopTypeIsRandom() )
|
|
{
|
|
Vector3 vToPed = VEC3V_TO_VECTOR3(pOtherPed->GetTransform().GetPosition()) - vPedPos;
|
|
if( DotProduct(vToPed, VEC3V_TO_VECTOR3(pPed->GetTransform().GetB())) > 0.0f &&
|
|
vToPed.Mag2() < rage::square(DIST_TO_INTIMIDATE_PED) )
|
|
{
|
|
pPedToIndimidate = pOtherPed;
|
|
vPedToIntimidatePos = VEC3V_TO_VECTOR3(pPedToIndimidate->GetTransform().GetPosition());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( m_fTimeBetweenIntimidations > 0.0f )
|
|
m_fTimeBetweenIntimidations -= fTimeStep;
|
|
|
|
// Update the intimidate timers.
|
|
if( pPedToIndimidate != m_pPedPlayerIntimidating )
|
|
{
|
|
m_pPedPlayerIntimidating = pPedToIndimidate;
|
|
m_fTimeIntimidating = 0.0f;
|
|
}
|
|
else if( m_pPedPlayerIntimidating )
|
|
{
|
|
m_fTimeIntimidating += fTimeStep;
|
|
|
|
if( m_fTimeIntimidating > TIME_TO_INTIMIDATE_PED )
|
|
{
|
|
CTaskAmbientClips* pTaskAmbientClips = static_cast<CTaskAmbientClips*>(m_pPedPlayerIntimidating->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AMBIENT_CLIPS));
|
|
|
|
if( m_pPedPlayerIntimidating->GetPedIntelligence()->GetTaskActive() == m_pPedPlayerIntimidating->GetPedIntelligence()->GetTaskDefault() &&
|
|
!m_pPedPlayerIntimidating->GetPedIntelligence()->IsFriendlyWith(*pPed) &&
|
|
CAmbientLookAt::GetLookAtBlockingState(pPed, pTaskAmbientClips, false) == CAmbientLookAt::LBS_NoBlocking)
|
|
{
|
|
CPlayerInfo *pPlayer = CGameWorld::GetMainPlayerInfo();
|
|
|
|
s32 scenario = m_pPedPlayerIntimidating->GetPedIntelligence()->GetQueriableInterface()->GetRunningScenarioType();
|
|
bool bTurnToFaceScenario = false;
|
|
if( scenario != Scenario_Invalid)
|
|
{
|
|
const CScenarioInfo* pScenarioInfo = CScenarioManager::GetScenarioInfo(scenario);
|
|
bTurnToFaceScenario = pScenarioInfo->GetIsFlagSet(CScenarioInfoFlags::TurnToFacePlayerStanding);
|
|
}
|
|
|
|
if( m_pPedPlayerIntimidating->GetPedType() == PEDTYPE_COP )
|
|
{
|
|
m_pPedPlayerIntimidating->NewSay("INTIMIDATE", 0 , false, false, -1, pPed, ATSTRINGHASH("INTIMIDATE_RESP", 0x0080ce82f));
|
|
m_pPedPlayerIntimidating->GetIkManager().LookAt(0, pPed, 1000, BONETAG_HEAD, NULL, 0, 500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
}
|
|
else if( m_pPedPlayerIntimidating->IsGangPed() && pPlayer && pPlayer && pPlayer->bCanBeHassledByGangs )
|
|
{
|
|
// Add an event causing this ped to dislike the other chap
|
|
CEventAcquaintancePedDislike event(pPed);
|
|
m_pPedPlayerIntimidating->GetPedIntelligence()->AddEvent(event);
|
|
}
|
|
else if( scenario != Scenario_Invalid && pPed->CheckBraveryFlags(BF_INTIMIDATE_PLAYER) )
|
|
{
|
|
if( bTurnToFaceScenario )
|
|
{
|
|
const char * pPhrase = (pPed->GetSpeechAudioEntity() && pPed->GetSpeechAudioEntity()->DoesContextExistForThisPed("GENERIC_FUCK_OFF")) ? "GENERIC_FUCK_OFF" : "GENERIC_DEJECTED";
|
|
CTaskSayAudio* pResponse = rage_new CTaskSayAudio(pPhrase, 0.3f, 1.0f, pPed);
|
|
CEventGivePedTask event(PED_TASK_PRIORITY_EVENT_RESPONSE_NONTEMP, pResponse);
|
|
m_pPedPlayerIntimidating->GetPedIntelligence()->AddEvent(event);
|
|
}
|
|
else
|
|
{
|
|
m_pPedPlayerIntimidating->GetIkManager().LookAt(0, pPed, 1000, BONETAG_HEAD, NULL, 0, 500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
if(!m_pPedPlayerIntimidating->NewSay("GENERIC_FUCK_OFF"))
|
|
m_pPedPlayerIntimidating->NewSay("GENERIC_DEJECTED");
|
|
}
|
|
}
|
|
else if( m_pPedPlayerIntimidating->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_WANDER) )
|
|
{
|
|
m_pPedPlayerIntimidating->GetIkManager().LookAt(0, pPed, 1000, BONETAG_HEAD, NULL, 0, 500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
m_pPedPlayerIntimidating->NewSay("FOLLOWED");
|
|
}
|
|
else if( !pPed->CheckBraveryFlags(BF_INTIMIDATE_PLAYER) )
|
|
{
|
|
m_pPedPlayerIntimidating->GetIkManager().LookAt(0, pPed, 1000, BONETAG_HEAD, NULL, 0, 500, 500, CIkManager::IK_LOOKAT_HIGH);
|
|
m_pPedPlayerIntimidating->NewSay("GENERIC_DEJECTED");
|
|
}
|
|
}
|
|
m_fTimeIntimidating = 0.0f;
|
|
m_fTimeBetweenIntimidations = 5.0f;
|
|
}
|
|
}
|
|
|
|
if(GetState()==STATE_JUMP || GetState()==STATE_DROPDOWN)
|
|
{
|
|
CControl *pControl = pPed->GetControlFromPlayer();
|
|
if(pPed->GetClimbPhysical(true) && pPed->GetClimbPhysical(true)->GetIsTypeVehicle())
|
|
{
|
|
CVehicle *pVehicle = static_cast<CVehicle*>(pPed->GetClimbPhysical(true));
|
|
if(pVehicle->InheritsFromBoat())
|
|
{
|
|
if(pControl->GetPedEnter().IsPressed())
|
|
{
|
|
m_nCachedBreakOutToMovementState = STATE_GET_IN_VEHICLE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//! If landing, cache controls for breakout.
|
|
if(pPed->IsOnGround() || pPed->GetPedResetFlag(CPED_RESET_FLAG_IsLanding))
|
|
{
|
|
if(pControl->GetPedCover().IsPressed())
|
|
{
|
|
m_nCachedBreakOutToMovementState = STATE_USE_COVER;
|
|
}
|
|
else if(pControl->GetPedEnter().IsPressed())
|
|
{
|
|
m_nCachedBreakOutToMovementState = STATE_GET_IN_VEHICLE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nCachedBreakOutToMovementState=-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(m_pFirstPersonIkHelper && pPed->IsFirstPersonShooterModeEnabledForPlayer(false, false, true))
|
|
{
|
|
TUNE_GROUP_FLOAT(GET_IN_VEHICLE, FPS_IK_OFFSET, -0.35f, -1.f, 1.f, 0.01f);
|
|
TUNE_GROUP_FLOAT(GET_IN_VEHICLE, FPS_IK_BLEND_RATE, 1.f, 0.f, 10.f, 0.01f);
|
|
|
|
const float fTargetOffset = (GetState() == STATE_GET_IN_VEHICLE) ? FPS_IK_OFFSET : 0.f;
|
|
Approach(m_fGetInVehicleFPSIkZOffset, fTargetOffset, FPS_IK_BLEND_RATE, fTimeStep);
|
|
|
|
const Vec3V vGetInVehicleOffset(0.f, 0.f, m_fGetInVehicleFPSIkZOffset);
|
|
m_pFirstPersonIkHelper->SetOffset(vGetInVehicleOffset);
|
|
}
|
|
else
|
|
{
|
|
if(m_pFirstPersonIkHelper)
|
|
{
|
|
const Vec3V vZero(V_ZERO);
|
|
m_pFirstPersonIkHelper->SetOffset(vZero);
|
|
}
|
|
|
|
m_fGetInVehicleFPSIkZOffset = 0.f;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::ProcessSpecialAbilities()
|
|
{
|
|
CPed& playerPed = *GetPed();
|
|
CPlayerSpecialAbility* pSpecialAbility = playerPed.GetSpecialAbility();
|
|
if (pSpecialAbility)
|
|
{
|
|
CPlayerSpecialAbilityManager::ProcessControl(&playerPed);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if !__FINAL
|
|
const char * CTaskPlayerOnFoot::GetStaticStateName( s32 iState )
|
|
{
|
|
static const char* sStateNames[] =
|
|
{
|
|
"State_Initial",
|
|
"State_Player_Idles",
|
|
"State_Jump",
|
|
"State_Melee",
|
|
"State_Swap_Weapon",
|
|
"State_Use_Cover",
|
|
"State_Climb_Ladder",
|
|
"State_Player_Gun",
|
|
"State_Get_In_Vehicle",
|
|
"State_Take_Off_Helmet",
|
|
"State_Melee_Reaction",
|
|
"State_Arrest",
|
|
"State_Uncuff",
|
|
"State_MountAnimal",
|
|
"State_DuckAndCover",
|
|
"State_SlopeScramble",
|
|
"State_ScriptedTask",
|
|
"State_DropDown",
|
|
"State_TakeOffParachutePack",
|
|
"State_TakeOffJetpackPack",
|
|
"State_TakeOffScubaGear",
|
|
"State_RideTrain",
|
|
"State_Jetpack",
|
|
"State_BirdProjectile",
|
|
};
|
|
|
|
return sStateNames[iState];
|
|
}
|
|
#endif
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
CPed* pPlayerPed = GetPed();
|
|
|
|
if(m_pScriptedTask && iEvent == OnUpdate && GetState() != STATE_SCRIPTED_TASK)
|
|
{
|
|
SetState(STATE_SCRIPTED_TASK);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
FSM_Begin
|
|
FSM_State(STATE_INITIAL)
|
|
FSM_OnUpdate
|
|
return Initial_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_PLAYER_IDLES)
|
|
FSM_OnEnter
|
|
PlayerIdles_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return PlayerIdles_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
PlayerIdles_OnExit(pPlayerPed);
|
|
|
|
FSM_State(STATE_JUMP)
|
|
FSM_OnEnter
|
|
Jump_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return Jump_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_MELEE)
|
|
FSM_OnEnter
|
|
Melee_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return Melee_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
Melee_OnExit();
|
|
|
|
FSM_State(STATE_SWAP_WEAPON)
|
|
FSM_OnEnter
|
|
SwapWeapon_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return SwapWeapon_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_USE_COVER)
|
|
FSM_OnEnter
|
|
UseCover_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return UseCover_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
return UseCover_OnExit();
|
|
|
|
FSM_State(STATE_CLIMB_LADDER)
|
|
FSM_OnEnter
|
|
ClimbLadder_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return ClimbLadder_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
return ClimbLadder_OnExit(pPlayerPed);
|
|
|
|
FSM_State(STATE_PLAYER_GUN)
|
|
FSM_OnEnter
|
|
PlayerGun_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return PlayerGun_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
return PlayerGun_OnExit(pPlayerPed);
|
|
|
|
FSM_State(STATE_GET_IN_VEHICLE)
|
|
FSM_OnEnter
|
|
GetInVehicle_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return GetInVehicle_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_MOUNT_ANIMAL)
|
|
FSM_OnEnter
|
|
GetMountAnimal_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return GetMountAnimal_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_TAKE_OFF_HELMET)
|
|
FSM_OnEnter
|
|
TakeOffHelmet_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return TakeOffHelmet_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_MELEE_REACTION)
|
|
FSM_OnEnter
|
|
MeleeReaction_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return MeleeReaction_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_ARREST)
|
|
FSM_OnEnter
|
|
Arrest_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return Arrest_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_UNCUFF)
|
|
FSM_OnEnter
|
|
Uncuff_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return Uncuff_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_DUCK_AND_COVER)
|
|
FSM_OnEnter
|
|
DuckAndCover_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return DuckAndCover_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_SLOPE_SCRAMBLE)
|
|
FSM_OnEnter
|
|
SlopeScramble_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return SlopeScramble_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_SCRIPTED_TASK)
|
|
FSM_OnEnter
|
|
ScriptedTask_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return ScriptedTask_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_DROPDOWN)
|
|
FSM_OnEnter
|
|
DropDown_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return DropDown_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_TAKE_OFF_PARACHUTE_PACK)
|
|
FSM_OnEnter
|
|
TakeOffParachutePack_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return TakeOffParachutePack_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_TAKE_OFF_JETPACK)
|
|
FSM_OnEnter
|
|
TakeOffJetpack_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return TakeOffJetpack_OnUpdate(pPlayerPed);
|
|
FSM_OnExit
|
|
TakeOffJetpack_OnExit(pPlayerPed);
|
|
|
|
FSM_State(STATE_TAKE_OFF_SCUBA_GEAR)
|
|
FSM_OnEnter
|
|
TakeOffScubaGear_OnEnter(pPlayerPed);
|
|
FSM_OnUpdate
|
|
return TakeOffScubaGear_OnUpdate(pPlayerPed);
|
|
|
|
FSM_State(STATE_RIDE_TRAIN)
|
|
FSM_OnEnter
|
|
RideTrain_OnEnter();
|
|
FSM_OnUpdate
|
|
return RideTrain_OnUpdate();
|
|
|
|
FSM_State(STATE_JETPACK)
|
|
FSM_OnEnter
|
|
Jetpack_OnEnter();
|
|
FSM_OnUpdate
|
|
return Jetpack_OnUpdate();
|
|
|
|
FSM_State(STATE_BIRD_PROJECTILE)
|
|
FSM_OnEnter
|
|
BirdProjectile_OnEnter();
|
|
FSM_OnUpdate
|
|
return BirdProjectile_OnUpdate();
|
|
FSM_OnExit
|
|
BirdProjectile_OnExit();
|
|
|
|
|
|
FSM_End
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::MeleeReaction_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
Assert(m_pMeleeReactionTask);
|
|
SetNewTask(m_pMeleeReactionTask);
|
|
m_pMeleeReactionTask = NULL;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::MeleeReaction_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Initial_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
AI_LOG_WITH_ARGS("[Player] - Ped %s starting CTaskPlayerOnFoot - 0x%p\n", AILogging::GetDynamicEntityNameSafe(pPlayerPed), this);
|
|
|
|
if (m_bScriptedToGoIntoCover)
|
|
{
|
|
pPlayerPed->SetPlayerResetFlag(CPlayerResetFlags::PRF_FORCE_PLAYER_INTO_COVER);
|
|
}
|
|
|
|
if (pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_FORCE_PLAYER_INTO_COVER))
|
|
{
|
|
SetState(STATE_USE_COVER);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// If flagged to enter cover straight after leaving a vehicle, do that now
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterCover) && CheckForNearbyCover(pPlayerPed, CSF_ConsiderCloseCover|CSF_ConsiderDynamicCover))
|
|
{
|
|
SetState(STATE_USE_COVER);
|
|
return FSM_Continue;
|
|
}
|
|
else if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterVehicleFromCover) && pPlayerPed->GetMyVehicle())
|
|
{
|
|
SetState(STATE_GET_IN_VEHICLE);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::PlayerIdles_OnEnter( CPed* pPlayerPed )
|
|
{
|
|
// reset idle timer
|
|
m_fIdleTime = 0.0f;
|
|
CTaskPlayerIdles* pIdleTask = rage_new CTaskPlayerIdles();
|
|
SetNewTask(pIdleTask);
|
|
|
|
// Set the open door arm IK flag
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_OpenDoorArmIK, true );
|
|
|
|
// For safety, reset in case it gets left on.
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_UseReserveParachute, false );
|
|
|
|
if (!pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_VaultFromCover))
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().ResetPendingResults();
|
|
|
|
pPlayerPed->GetPedIntelligence()->GetDropDownDetector().ResetPendingResults();
|
|
|
|
m_bWaitingForLadder = false;
|
|
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl)
|
|
{
|
|
switch(GetPreviousState())
|
|
{
|
|
case STATE_JUMP:
|
|
case STATE_PLAYER_GUN:
|
|
case STATE_SWAP_WEAPON:
|
|
break;
|
|
default:
|
|
pControl->ClearToggleRun();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::PlayerIdles_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
// Used to check for armed melee actions
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_ProcessPostPreRender, true );
|
|
|
|
// talk
|
|
UpdateTalk(pPlayerPed, true, true);
|
|
|
|
// Check to see if we have a melee task stored off from the previous frame
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
pPlayerPed->SetIsCrouching( false, -1, false );
|
|
taskDebugf1("STATE_MELEE: CTaskPlayerOnFoot::PlayerIdles_OnUpdate: m_pMeleeRequestTask");
|
|
SetState(STATE_MELEE);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
ProcessSpecialAbilities();
|
|
|
|
if(CheckShouldRideTrain())
|
|
{
|
|
SetState(STATE_RIDE_TRAIN);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
s32 iDesiredState = GetDesiredStateFromPlayerInput(pPlayerPed);
|
|
|
|
if(m_bWaitingForLadder)
|
|
{
|
|
// Ped has given up trying to get on ladder....
|
|
// Ped has given up trying to get on ladder....
|
|
if(!KeepWaitingForLadder(pPlayerPed, m_fBlockHeading))
|
|
{
|
|
m_bWaitingForLadder = false;
|
|
iDesiredState = STATE_PLAYER_IDLES;
|
|
}
|
|
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_LadderBlockingMovement, true);
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(0.0f, 0.0f);
|
|
pPlayerPed->SetVelocity(VEC3_ZERO);
|
|
pPlayerPed->SetDesiredVelocity(VEC3_ZERO);
|
|
|
|
pPlayerPed->SetHeading(m_fBlockHeading);
|
|
pPlayerPed->SetPosition(m_vBlockPos, true, true);
|
|
|
|
if(CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, CTaskClimbLadderFully::BF_CLIMBING_DOWN)||
|
|
CTaskGoToAndClimbLadder::IsLadderTopBlockedMP(m_pTargetLadder, *pPlayerPed, m_iLadderIndex))
|
|
{
|
|
if(iDesiredState == STATE_CLIMB_LADDER || iDesiredState == STATE_DROPDOWN)
|
|
iDesiredState = STATE_PLAYER_IDLES;
|
|
}
|
|
else
|
|
{
|
|
iDesiredState = STATE_CLIMB_LADDER;
|
|
}
|
|
}
|
|
else if (iDesiredState == STATE_CLIMB_LADDER)
|
|
{
|
|
if (!m_bGettingOnBottom &&
|
|
NetworkInterface::IsGameInProgress() &&
|
|
(CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, CTaskClimbLadderFully::BF_CLIMBING_DOWN)||
|
|
CTaskGoToAndClimbLadder::IsLadderTopBlockedMP(m_pTargetLadder, *pPlayerPed, m_iLadderIndex)))
|
|
{
|
|
m_vBlockPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
m_fBlockHeading = pPlayerPed->GetTransform().GetHeading();
|
|
m_bWaitingForLadder = true;
|
|
iDesiredState = STATE_PLAYER_IDLES;
|
|
}
|
|
}
|
|
|
|
if (iDesiredState == STATE_PLAYER_IDLES
|
|
#if FPS_MODE_SUPPORTED
|
|
|| (iDesiredState == STATE_PLAYER_GUN && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
#endif
|
|
)
|
|
{
|
|
// Might want to take off helmet
|
|
if(pPlayerPed->GetHelmetComponent() && pPlayerPed->GetHelmetComponent()->IsHelmetEnabled() && !pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_DontTakeOffHelmet )
|
|
&& !CTaskMobilePhone::IsRunningMobilePhoneTask(*pPlayerPed) && !pPlayerPed->GetPlayerInfo()->IsControlsScriptDisabled() && !pPlayerPed->GetIsInWater())
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
// B*2055903: Don't take off helmet unless we're in FPS_IDLE. If we're not idle, reset the timer
|
|
if (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
if (!pPlayerPed->GetMotionData()->GetIsFPSIdle())
|
|
{
|
|
m_fIdleTime = 0.0f;
|
|
}
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
m_fIdleTime += GetTimeStep();
|
|
|
|
// If no action has been taken by the player within a time, take off the helmet (B*2146344: unless we're playing a scripted anim).
|
|
if((m_fIdleTime > 4.0f || pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_RemoveHelmet)) &&
|
|
!pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsSwitchingHelmetVisor) &&
|
|
!pPlayerPed->GetPedIntelligence()->FindTaskActiveByTreeAndType(PED_TASK_TREE_SECONDARY, CTaskTypes::TASK_SCRIPTED_ANIMATION))
|
|
{
|
|
SetState(STATE_TAKE_OFF_HELMET);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(iDesiredState == STATE_PLAYER_IDLES)
|
|
{
|
|
// No state change requested
|
|
#if FPS_MODE_SUPPORTED && 0
|
|
// This is only required if calling PlayerIdles_OnUpdate from PlayerGun_OnUpdate.
|
|
// TODO: remove this if not needed.
|
|
if( pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) )
|
|
{
|
|
if( iDesiredState != GetState() )
|
|
{
|
|
SetState(iDesiredState);
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
// Enable scanning flags
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_SearchingForDoors, true );
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_GestureAnimsAllowed, true );
|
|
|
|
//-------------------------------------------------------------------------
|
|
// DEBUG STRAFING
|
|
//-------------------------------------------------------------------------
|
|
if(ms_bAllowStrafingWhenUnarmed)
|
|
{
|
|
if(pPlayerPed->GetWeaponManager())
|
|
{
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if(CPlayerInfo::IsAiming() && pWeapon && pWeapon->GetWeaponInfo()->GetIsMelee())
|
|
{
|
|
pPlayerPed->SetIsStrafing(true);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->SetIsStrafing(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->SetIsStrafing(false);
|
|
}
|
|
}
|
|
|
|
if (CheckShouldPerformArrest())
|
|
{
|
|
ArrestTargetPed(pPlayerPed);
|
|
return FSM_Continue;
|
|
}
|
|
//************************************************************************
|
|
// If we are running into a low obstacle then maybe try to hop up onto it
|
|
|
|
ePlayerOnFootState autoState = CheckForAutoCover(pPlayerPed);
|
|
if( autoState != STATE_INVALID )
|
|
{
|
|
SetState(autoState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//************************************************************************
|
|
// Idle weapon block for heavy weapon minigun.
|
|
if(pPlayerPed->GetWeaponManager())
|
|
{
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
const CWeaponInfo* pWeaponInfo = pWeapon ? pWeapon->GetWeaponInfo() : NULL;
|
|
if (pWeaponInfo && pWeaponInfo->GetIsHeavy())
|
|
{
|
|
//Expose these to rag if we need to handle more weapons etc.
|
|
const float fForwardOffset = 0.7f;
|
|
const float fUpOffset = -0.44f;
|
|
|
|
float fProbeLength = m_bIdleWeaponBlocked ? 1.1f : 1.0f;
|
|
Vector3 vTargetPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()) + (VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetForward()) * fForwardOffset) + (VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetUp()) * fUpOffset);
|
|
if(CTaskWeaponBlocked::IsWeaponBlocked(pPlayerPed, vTargetPos, NULL, 1.0f, fProbeLength))
|
|
{
|
|
const fwMvClipId ms_WeaponBlockedClipId(ATSTRINGHASH("wall_block_idle", 0x0f9b74953));
|
|
const fwMvClipId ms_WeaponBlockedNewClipId(ATSTRINGHASH("wall_block", 0x0ea90630e));
|
|
|
|
fwMvClipId wallBlockClipId = CLIP_ID_INVALID;
|
|
const fwMvClipSetId appropriateWeaponClipSetId = pWeaponInfo->GetAppropriateWeaponClipSetId(pPlayerPed);
|
|
if(fwAnimManager::GetClipIfExistsBySetId(appropriateWeaponClipSetId, ms_WeaponBlockedNewClipId))
|
|
{
|
|
wallBlockClipId = ms_WeaponBlockedNewClipId;
|
|
}
|
|
else if(fwAnimManager::GetClipIfExistsBySetId(appropriateWeaponClipSetId, ms_WeaponBlockedClipId))
|
|
{
|
|
wallBlockClipId = ms_WeaponBlockedClipId;
|
|
}
|
|
|
|
if(wallBlockClipId != CLIP_ID_INVALID && !m_bIdleWeaponBlocked)
|
|
{
|
|
m_bIdleWeaponBlocked = true;
|
|
StartClipBySetAndClip(pPlayerPed, appropriateWeaponClipSetId, wallBlockClipId, SLOW_BLEND_IN_DELTA, CClipHelper::TerminationType_OnDelete);
|
|
Assertf(GetClipHelper(), "Blocked weapon clip failed to start");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(m_bIdleWeaponBlocked)
|
|
{
|
|
m_bIdleWeaponBlocked = false;
|
|
StopClip(SLOW_BLEND_OUT_DELTA);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(m_bIdleWeaponBlocked)
|
|
{
|
|
m_bIdleWeaponBlocked = false;
|
|
StopClip(SLOW_BLEND_OUT_DELTA);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
else
|
|
{
|
|
if(iDesiredState != GetState())
|
|
SetState(iDesiredState);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::PlayerIdles_OnExit( CPed* pPlayerPed )
|
|
{
|
|
// Reset the open door arm IK flag
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_OpenDoorArmIK, false );
|
|
|
|
// Stop stealh prep clips if we are currently running
|
|
if( m_bUsingStealthClips )
|
|
{
|
|
m_bUsingStealthClips = false;
|
|
if( GetClipHelper() )
|
|
{
|
|
StopClip( SLOW_BLEND_OUT_DELTA );
|
|
}
|
|
}
|
|
|
|
if(m_bIdleWeaponBlocked)
|
|
{
|
|
m_bIdleWeaponBlocked = false;
|
|
StopClip(NORMAL_BLEND_OUT_DELTA);
|
|
}
|
|
}
|
|
|
|
|
|
fwFlags32 CTaskPlayerOnFoot::MakeJumpFlags(CPed* pPlayerPed, const sPedTestResults &pedTestResults)
|
|
{
|
|
// There is some 2nd surface depth beyond which the player cannot jump
|
|
// For now we'll assume all 2nd surfaces are 'snow' - but eventually we might
|
|
// need to test which material we're standing on to decide specific depths..
|
|
s32 iFlags = 0;
|
|
#if HACK_GTA4_BOUND_GEOM_SECOND_SURFACE
|
|
if(pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping ) || pPlayerPed->GetSecondSurfaceDepth() > ms_fMaxSnowDepthRatioForJump)
|
|
#else
|
|
if(pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping ) || 0.0f > ms_fMaxSnowDepthRatioForJump)
|
|
#endif // HACK_GTA4_BOUND_GEOM_SECOND_SURFACE
|
|
{
|
|
iFlags |= JF_DisableJumpingIfNoClimb;
|
|
}
|
|
|
|
if(pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerVaulting ) && !m_bJumpedFromCover)
|
|
{
|
|
iFlags |= JF_DisableVault;
|
|
}
|
|
else if (m_bJumpedFromCover)
|
|
{
|
|
iFlags |= JF_ForceVault;
|
|
}
|
|
|
|
if(pedTestResults.GetCanAutoJumpVault())
|
|
{
|
|
iFlags |= JF_AutoJump;
|
|
}
|
|
|
|
iFlags |= JF_UseCachedHandhold;
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_VaultFromCover))
|
|
{
|
|
iFlags |= JF_FromCover;
|
|
iFlags |= JF_DisableJumpingIfNoClimb;
|
|
}
|
|
|
|
if(pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_SUPER_JUMP_ON))
|
|
{
|
|
iFlags |= JF_SuperJump;
|
|
}
|
|
|
|
if(pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_BEAST_JUMP_ON))
|
|
{
|
|
iFlags |= JF_BeastJump;
|
|
}
|
|
|
|
return iFlags;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::Jump_OnEnter(CPed* pPlayerPed)
|
|
{
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_VaultFromCover, false );
|
|
pPlayerPed->SetIsCrouching(false,-1,false);
|
|
|
|
// There is one frame where the player gets no input to the moveblender, between where the CTaskMovePlayer is active
|
|
// and where CTaskComplexJump::ProcessPlayerInput() is passing input to the moveblender. Ensure that we get stick input
|
|
// to prevent the ped initiating a stop-clip here.
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(pPlayerPed->GetMotionData()->GetCurrentMbrY(), 0.0f);
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DontChangeMbrInSimpleMoveDoNothing, true );
|
|
|
|
//@@: location CTASKPLAYERONFOOT_JUMP_ONENTER_GET_CURRENT_MOTIONTASK
|
|
CTaskMotionBase* pMotionTask = pPlayerPed->GetCurrentMotionTask(false);
|
|
if(pMotionTask && pMotionTask->GetSubTask() && pMotionTask->GetSubTask()->GetTaskType() == CTaskTypes::TASK_MOTION_AIMING_TRANSITION)
|
|
{
|
|
// Quit any active transition task, so the independent mover is triggered
|
|
CTaskMotionAimingTransition* pTransitionTask = static_cast<CTaskMotionAimingTransition*>(pMotionTask->GetSubTask());
|
|
pTransitionTask->ForceQuit();
|
|
}
|
|
|
|
// B*1900737 - Interrupt reload task and resume if jumping / climbing / vaulting to prevent instant reload.
|
|
CTaskReloadGun* pReloadTask = static_cast<CTaskReloadGun*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_RELOAD_GUN));
|
|
if(!pReloadTask)
|
|
{
|
|
CTask* pSecondaryTask = pPlayerPed->GetPedIntelligence()->GetTaskSecondaryActive();
|
|
if(pSecondaryTask && pSecondaryTask->GetTaskType() == CTaskTypes::TASK_RELOAD_GUN)
|
|
{
|
|
pReloadTask = static_cast<CTaskReloadGun*>(pSecondaryTask);
|
|
}
|
|
}
|
|
if(pReloadTask)
|
|
{
|
|
pReloadTask->RequestInterrupt();
|
|
}
|
|
|
|
// There is some 2nd surface depth beyond which the player cannot jump
|
|
// For now we'll assume all 2nd surfaces are 'snow' - but eventually we might
|
|
// need to test which material we're standing on to decide specific depths..
|
|
|
|
SetNewTask(rage_new CTaskJumpVault(m_iJumpFlags));
|
|
|
|
m_iJumpFlags = 0;
|
|
m_bCanBreakOutToMovement = false;
|
|
m_nCachedBreakOutToMovementState = -1;
|
|
|
|
//! Reset jump pressed button, which forces us to press again before we can jump/climb again.
|
|
m_uLastTimeJumpPressed = 0;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Jump_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
s32 iContinueToState = STATE_INVALID;
|
|
if(GetPreviousState() == STATE_GET_IN_VEHICLE)
|
|
iContinueToState = STATE_GET_IN_VEHICLE;
|
|
|
|
const CTaskJumpVault* pJumpVaultTask = static_cast<const CTaskJumpVault*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_JUMPVAULT));
|
|
if(pJumpVaultTask)
|
|
{
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl && pControl->GetPedJump().IsPressed())
|
|
{
|
|
m_uLastTimeJumpPressed = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
if(pJumpVaultTask->CanBreakoutToMovementTask())
|
|
{
|
|
//! Need to reset pending results before we start querying again.
|
|
if(!m_bCanBreakOutToMovement)
|
|
{
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().ResetPendingResults();
|
|
pPlayerPed->GetPedIntelligence()->GetDropDownDetector().ResetPendingResults();
|
|
m_bCanBreakOutToMovement = true;
|
|
}
|
|
|
|
if(iContinueToState != STATE_INVALID)
|
|
{
|
|
SetState(iContinueToState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
s32 nState = GetBreakOutToMovementState(pPlayerPed);
|
|
|
|
//! Reset after processing.
|
|
m_nCachedBreakOutToMovementState = -1;
|
|
|
|
if(nState != STATE_INVALID)
|
|
{
|
|
if(nState == GetState())
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
else
|
|
{
|
|
SetState(nState);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
if(iContinueToState != STATE_INVALID)
|
|
SetState(iContinueToState);
|
|
else
|
|
SetState(STATE_PLAYER_IDLES);
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
if(sm_Tunables.m_CanMountFromInAir)
|
|
{
|
|
//mounting from above
|
|
if (GetSubTask() && static_cast<const CTaskJumpVault*>(GetSubTask())->GetIsJumping())
|
|
{
|
|
if (CheckForAirMount(pPlayerPed))
|
|
{
|
|
SetState(STATE_MOUNT_ANIMAL);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::DropDown_OnEnter(CPed* pPlayerPed)
|
|
{
|
|
pPlayerPed->SetIsCrouching(false,-1,false);
|
|
|
|
// There is one frame where the player gets no input to the moveblender, between where the CTaskMovePlayer is active
|
|
// and where CTaskComplexJump::ProcessPlayerInput() is passing input to the moveblender. Ensure that we get stick input
|
|
// to prevent the ped initiating a stop-clip here.
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(pPlayerPed->GetMotionData()->GetCurrentMbrY(), 0.0f);
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DontChangeMbrInSimpleMoveDoNothing, true );
|
|
|
|
const CDropDown &dropDown = pPlayerPed->GetPedIntelligence()->GetDropDownDetector().GetDetectedDropDown();
|
|
|
|
if (dropDown.m_eDropType==eRagdollDrop)
|
|
{
|
|
CTaskNMBehaviour* pFallTask = NULL;
|
|
if (CTaskNMBehaviour::sm_Tunables.m_UseBalanceForEdgeActivation)
|
|
{
|
|
pFallTask = rage_new CTaskNMBalance(500, 10000, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
pFallTask = rage_new CTaskNMHighFall(500, NULL, CTaskNMHighFall::HIGHFALL_TEETER_EDGE, NULL, false);
|
|
}
|
|
|
|
CEventSwitch2NM event(10000, pFallTask, false, 500);
|
|
pPlayerPed->SwitchToRagdoll(event);
|
|
}
|
|
else
|
|
{
|
|
SetNewTask(rage_new CTaskDropDown(dropDown));
|
|
}
|
|
|
|
m_bCanBreakOutToMovement = false;
|
|
m_nCachedBreakOutToMovementState = -1;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::DropDown_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
const CTaskDropDown* pDropDownTask = static_cast<const CTaskDropDown*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_DROP_DOWN));
|
|
if(pDropDownTask)
|
|
{
|
|
if(pDropDownTask->CanBreakoutToMovementTask())
|
|
{
|
|
//! Need to reset pending results before we start querying again.
|
|
if(!m_bCanBreakOutToMovement)
|
|
{
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().ResetPendingResults();
|
|
pPlayerPed->GetPedIntelligence()->GetDropDownDetector().ResetPendingResults();
|
|
m_bCanBreakOutToMovement = true;
|
|
}
|
|
|
|
s32 nState = GetBreakOutToMovementState(pPlayerPed);
|
|
|
|
//! Reset after processing.
|
|
m_nCachedBreakOutToMovementState = -1;
|
|
|
|
if(nState != STATE_INVALID)
|
|
{
|
|
if(nState == GetState())
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
else
|
|
{
|
|
SetState(nState);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//mounting from above
|
|
if(sm_Tunables.m_CanMountFromInAir)
|
|
{
|
|
if (pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedMount())
|
|
{
|
|
if (CheckForAirMount(pPlayerPed))
|
|
{
|
|
SetState(STATE_MOUNT_ANIMAL);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
void CTaskPlayerOnFoot::Melee_OnEnter(CPed* UNUSED_PARAM(pPlayerPed) )
|
|
{
|
|
Assert(m_pMeleeRequestTask);
|
|
SetNewTask(m_pMeleeRequestTask);
|
|
m_pMeleeRequestTask = NULL;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Melee_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
// talk
|
|
UpdateTalk(pPlayerPed, false, true);
|
|
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
if (m_bShouldStartGunTask)
|
|
{
|
|
SetState(STATE_PLAYER_GUN);
|
|
return FSM_Continue;
|
|
}
|
|
taskDebugf1("STATE_PLAYER_IDLES: CTaskPlayerOnFoot::Melee_OnUpdate: SubTaskFinished");
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
else if(CheckShouldRideTrain())
|
|
{
|
|
SetState(STATE_RIDE_TRAIN);
|
|
}
|
|
else if (CheckShouldPerformArrest())
|
|
{
|
|
ArrestTargetPed(pPlayerPed);
|
|
}
|
|
else if(CheckShouldPerformUncuff())
|
|
{
|
|
SetState(STATE_UNCUFF);
|
|
}
|
|
else if(pPlayerPed->GetPlayerInfo() && pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_SUPER_JUMP_ON)
|
|
&& DoJumpCheck(pPlayerPed, pPlayerPed->GetControlFromPlayer(), false))
|
|
{
|
|
// Only allow jumping if we're in State_WaitForMeleeAction
|
|
CTaskMelee *pMeleeSubTask = static_cast<CTaskMelee*>(GetSubTask());
|
|
if (pMeleeSubTask && pMeleeSubTask->GetState() == CTaskMelee::State_WaitForMeleeAction)
|
|
{
|
|
SetState(STATE_JUMP);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::Melee_OnExit()
|
|
{
|
|
if (GetPed())
|
|
GetPed()->SetPedConfigFlag( CPED_CONFIG_FLAG_UsingCoverPoint, false );
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::SwapWeapon_OnEnter(CPed* pPlayerPed)
|
|
{
|
|
s32 iFlags = SWAP_HOLSTER;
|
|
// static dev_bool FORCE_NETWORK_SELECTION = false;
|
|
// if(NetworkInterface::IsGameInProgress() || FORCE_NETWORK_SELECTION)
|
|
// {
|
|
// iFlags |= SWAP_INSTANTLY;
|
|
// }
|
|
|
|
if(pPlayerPed->GetWeaponManager())
|
|
{
|
|
const CWeaponInfo* pInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(pPlayerPed->GetWeaponManager()->GetEquippedWeaponHash());
|
|
if(pInfo && pInfo->GetIsCarriedInHand())
|
|
{
|
|
iFlags |= SWAP_DRAW;
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
iFlags |= SWAP_TO_AIM;
|
|
}
|
|
#endif
|
|
//@@: location CTASKPLAYERONFOOT_SWAPWEAPONONENTER
|
|
SetNewTask(rage_new CTaskComplexControlMovement( rage_new CTaskMovePlayer(), rage_new CTaskSwapWeapon(iFlags), CTaskComplexControlMovement::TerminateOnSubtask ));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::SwapWeapon_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished) || GetSubTask() == NULL)
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
else
|
|
{
|
|
weaponAssert(pPlayerPed->GetWeaponManager());
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if(DoJumpCheck(pPlayerPed, pPlayerPed->GetControlFromPlayer(), pWeapon && pWeapon->GetWeaponInfo()->GetIsHeavy()))
|
|
{
|
|
SetState(STATE_JUMP);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::UseCover_OnEnter(CPed* pPlayerPed)
|
|
{
|
|
CTaskCover* pCoverTask = NULL;
|
|
s32 iCoverFlags = 0;
|
|
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
taskAssert(pControl);
|
|
m_bCoverSprintReleased = !pControl->GetPedSprintIsDown();
|
|
|
|
if (m_bScriptedToGoIntoCover || pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_FORCE_PLAYER_INTO_COVER))
|
|
{
|
|
pPlayerPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_FORCE_PLAYER_INTO_COVER);
|
|
|
|
CCoverPoint* pCoverPoint = pPlayerPed->GetCoverPoint();
|
|
if (pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_SKIP_COVER_ENTRY_ANIM))
|
|
{
|
|
// Skip the idle transition if close to the cover (we lose this flag if being put directly into cover as the player when cover task is first run)
|
|
if (pCoverPoint)
|
|
{
|
|
Vector3 vCoverPosition(Vector3::ZeroType);
|
|
if (pCoverPoint->GetCoverPointPosition(vCoverPosition))
|
|
{
|
|
if ((vCoverPosition - VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition())).XYMag2() <= rage::square(CTaskEnterCover::ms_Tunables.m_CoverEntryMaxDirectDistance))
|
|
{
|
|
iCoverFlags = CTaskCover::CF_SkipIdleCoverTransition;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_SPECIFY_INITIAL_COVER_HEADING))
|
|
{
|
|
iCoverFlags |= CTaskCover::CF_SpecifyInitialHeading;
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_InCoverFacingLeft))
|
|
{
|
|
iCoverFlags |= CTaskCover::CF_FacingLeft;
|
|
}
|
|
if (pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_FACING_LEFT_IN_COVER))
|
|
{
|
|
iCoverFlags |= CTaskCover::CF_FacingLeft;
|
|
}
|
|
}
|
|
|
|
pPlayerPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_FACING_LEFT_IN_COVER);
|
|
pPlayerPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_SKIP_COVER_ENTRY_ANIM);
|
|
pPlayerPed->ClearPlayerResetFlag(CPlayerResetFlags::PRF_SPECIFY_INITIAL_COVER_HEADING);
|
|
pCoverTask = rage_new CTaskCover(CAITarget(), iCoverFlags);
|
|
pCoverTask->SetBlendInDuration(pPlayerPed->GetPlayerInfo()->m_fPutPedDirectlyIntoCoverNetworkBlendInDuration);
|
|
m_bScriptedToGoIntoCover = false;
|
|
}
|
|
else
|
|
{
|
|
pCoverTask = rage_new CTaskCover(CAITarget(), iCoverFlags);
|
|
}
|
|
|
|
SetNewTask(pCoverTask);
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.message = PlayerGotIntoCover;
|
|
CONDUCTORS_ONLY(SUPERCONDUCTOR.SendConductorMessage(messageData));
|
|
|
|
CTaskReloadGun* pReloadTask = static_cast<CTaskReloadGun*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_RELOAD_GUN));
|
|
if(!pReloadTask)
|
|
{
|
|
CTask* pSecondaryTask = pPlayerPed->GetPedIntelligence()->GetTaskSecondaryActive();
|
|
if(pSecondaryTask && pSecondaryTask->GetTaskType() == CTaskTypes::TASK_RELOAD_GUN)
|
|
{
|
|
pReloadTask = static_cast<CTaskReloadGun*>(pSecondaryTask);
|
|
}
|
|
}
|
|
|
|
if(pReloadTask)
|
|
{
|
|
pReloadTask->RequestInterrupt();
|
|
}
|
|
|
|
//Reset player sprint counter so we don't run after cover is done if we came in with a run
|
|
if (pPlayerPed->GetPlayerInfo())
|
|
pPlayerPed->GetPlayerInfo()->SetPlayerDataSprintControlCounter(0.0f);
|
|
|
|
m_bCheckTimeInStateForCover = false;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::UseCover_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
// Check to see if we have a melee task stored off from the previous frame
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
pPlayerPed->SetIsCrouching( false, -1, false );
|
|
SetState(STATE_MELEE);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
ProcessSpecialAbilities();
|
|
|
|
if(CheckShouldRideTrain())
|
|
{
|
|
SetState(STATE_RIDE_TRAIN);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_KeepCoverPoint, true);
|
|
|
|
if (GetSubTask() && GetSubTask()->GetSubTask() && GetSubTask()->GetSubTask()->GetTaskType() == CTaskTypes::TASK_IN_COVER)
|
|
{
|
|
if( pPlayerPed->GetCoverPoint() )
|
|
{
|
|
m_vLastCoverDir = VEC3V_TO_VECTOR3(pPlayerPed->GetCoverPoint()->GetCoverDirectionVector());
|
|
}
|
|
}
|
|
|
|
// Player use of cover can be disabled using
|
|
if (!pPlayerPed->GetPlayerInfo()->CanUseCover())
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.message = PlayerExitCover;
|
|
CONDUCTORS_ONLY(SUPERCONDUCTOR.SendConductorMessage(messageData));
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
taskAssert(pControl);
|
|
bool bEnterPressed = HasValidEnterVehicleInput(*pControl);
|
|
CVehicle * pTargetVehicle = NULL;
|
|
if (bEnterPressed)
|
|
{
|
|
m_pTargetVehicle = NULL;
|
|
pTargetVehicle = CPlayerInfo::ScanForVehicleToEnter(pPlayerPed, VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB()), false);
|
|
}
|
|
|
|
if (!m_bCoverSprintReleased)
|
|
m_bCoverSprintReleased = pControl->GetPedSprintIsReleased();
|
|
|
|
// ENTERING CARS, search for vehicles when enter pressed for picking up objects
|
|
const bool bCanEnterVehicle = CheckCanEnterVehicle(pTargetVehicle);
|
|
if (bCanEnterVehicle)
|
|
{
|
|
pPlayerPed->SetIsInCover(1);
|
|
SetState(STATE_GET_IN_VEHICLE);
|
|
m_pTargetVehicle = const_cast<CVehicle*>(pTargetVehicle);
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.message = PlayerExitCover;
|
|
CONDUCTORS_ONLY(SUPERCONDUCTOR.SendConductorMessage(messageData));
|
|
return FSM_Continue;
|
|
}
|
|
#if __BANK
|
|
else if (pTargetVehicle)
|
|
{
|
|
AI_LOG_WITH_ARGS("Cannot enter target vehicle %s", AILogging::GetDynamicEntityNameSafe(pTargetVehicle));
|
|
}
|
|
else if (CheckShouldPerformArrest())
|
|
{
|
|
ArrestTargetPed(pPlayerPed);
|
|
}
|
|
|
|
#endif // __BANK
|
|
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
if (GetSubTask() && GetSubTask()->GetTaskType() == CTaskTypes::TASK_COVER)
|
|
{
|
|
m_bCheckTimeInStateForCover = GetSubTask()->GetPreviousState() != CTaskCover::State_ExitCover;
|
|
}
|
|
SetState(STATE_PLAYER_IDLES);
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.message = PlayerExitCover;
|
|
CONDUCTORS_ONLY(SUPERCONDUCTOR.SendConductorMessage(messageData));
|
|
return FSM_Continue;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::UseCover_OnExit()
|
|
{
|
|
SetLastTimeInCoverTask(fwTimer::GetTimeInMilliseconds());
|
|
m_bPlayerExitedCoverThisUpdate = true;
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ClimbLadder_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
m_bWaitingForLadder = false;
|
|
|
|
Assert(m_pTargetLadder);
|
|
SetNewTask(rage_new CTaskGoToAndClimbLadder(m_pTargetLadder,m_iLadderIndex,CTaskGoToAndClimbLadder::DontAutoClimb,CTaskGoToAndClimbLadder::InputState_Nothing, m_bEasyLadderEntry));
|
|
|
|
// Let this get handled by CTaskClimbLadder::ProcessLadderBlocking when a network game is running as its done in a different fashion...
|
|
if(!NetworkInterface::IsGameInProgress())
|
|
{
|
|
u32 LadderBlockFlag = (m_bGettingOnBottom ? CTaskClimbLadderFully::BF_CLIMBING_UP : CTaskClimbLadderFully::BF_CLIMBING_DOWN);
|
|
CTaskClimbLadderFully::BlockLadder(m_pTargetLadder, m_iLadderIndex, fwTimer::GetTimeInMilliseconds(), LadderBlockFlag); // Block ladder for AI and other players
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::ClimbLadder_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
// Let the movement system know that the player is in direct control of this ped
|
|
pPlayerPed->GetMotionData()->SetPlayerHasControlOfPedThisFrame(true);
|
|
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::ClimbLadder_OnExit(CPed* pPlayerPed)
|
|
{
|
|
CPedWeaponManager* pWepMgr = pPlayerPed->GetWeaponManager();
|
|
if (pWepMgr)
|
|
{
|
|
pWepMgr->RestoreBackupWeapon();
|
|
}
|
|
|
|
// Let this get handled by CTaskClimbLadder::ProcessLadderBlocking when a network game is running as its done in a different fashion...
|
|
if(!NetworkInterface::IsGameInProgress())
|
|
{
|
|
CTaskClimbLadderFully::ReleaseLadder(m_pTargetLadder, m_iLadderIndex);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TriggerArrestForPed(CPed* CNC_MODE_ENABLED_ONLY(pPed))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
if (pPed && pPed->IsNetworkClone())
|
|
{
|
|
// Start arrest task remotely
|
|
CStartNetworkPedArrestEvent::Trigger(GetPed(), pPed, 0);
|
|
}
|
|
else if (pPed)
|
|
{
|
|
CTaskIncapacitated* task = (CTaskIncapacitated*)pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_INCAPACITATED);
|
|
if (task)
|
|
{
|
|
task->SetIsArrested(true);
|
|
task->SetPedArrester(GetPed());
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ArrestTargetPed(CPed* CNC_MODE_ENABLED_ONLY(pPed))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
Assert(m_pTargetPed);
|
|
if (!m_pTargetPed)
|
|
{
|
|
return;
|
|
}
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting, true);
|
|
|
|
CPedWeaponManager* pWeaponManager = GetPed()->GetWeaponManager();
|
|
if (pWeaponManager )
|
|
{
|
|
//Cache of currently equip weapon if its unarmed or melee then equip a new weapon for the arrest
|
|
CWeapon* pEquippedWeapon = pWeaponManager->GetEquippedWeapon();
|
|
if (pEquippedWeapon && !pEquippedWeapon->GetWeaponInfo()->GetIsGun())
|
|
{
|
|
pWeaponManager->ClearBackupWeapon();
|
|
pWeaponManager->SetBackupWeapon(pEquippedWeapon->GetWeaponHash());
|
|
|
|
//Try to equip a pistol for the arrest
|
|
if(pPed->GetInventory()->GetWeapon(WEAPONTYPE_PISTOL))
|
|
{
|
|
pWeaponManager->EquipWeapon(WEAPONTYPE_PISTOL);
|
|
}
|
|
else
|
|
{
|
|
pWeaponManager->EquipBestWeapon();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!m_bArrestTimerActive)
|
|
{
|
|
TUNE_GROUP_FLOAT(CNC_ARREST, fArrestTime, 2000.0f, 0.0f, 10000.0f, 0.01f);
|
|
m_uArrestFinishTime = CNetwork::GetNetworkTime() + (u32)fArrestTime;
|
|
TUNE_GROUP_FLOAT(CNC_ARREST, fBreakoutTime, 1500.0f, 0.0f, 10000.0f, 0.01f);
|
|
m_uArrestSprintBreakoutTime = CNetwork::GetNetworkTime() + (u32)fBreakoutTime;
|
|
m_bArrestTimerActive = true;
|
|
|
|
//Force aim on target
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_ForcedAimFromArrest, true);
|
|
}
|
|
|
|
CVehicle* vCrookVehicle = m_pTargetPed->GetVehiclePedInside();
|
|
|
|
// Arrest whole vehicle if Ped is in vehicle
|
|
if (vCrookVehicle && vCrookVehicle->GetSeatManager()->GetNumPlayers() > 1)
|
|
{
|
|
for (int seatIndex = 0; seatIndex < MAX_VEHICLE_SEATS; seatIndex++)
|
|
{
|
|
CPed* pSeatedPed = vCrookVehicle->GetSeatManager()->GetPedInSeat(seatIndex);
|
|
|
|
if (!pSeatedPed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pSeatedPed->IsPlayer() && pSeatedPed->GetPlayerWanted()->GetWantedLevel() == WANTED_CLEAN)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pSeatedPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanBeArrested) && !pSeatedPed->IsDead())
|
|
{
|
|
CTaskIncapacitated* incapTask = (CTaskIncapacitated*)pSeatedPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_INCAPACITATED);
|
|
|
|
|
|
if (incapTask && incapTask->GetState() == CTaskIncapacitated::State_Incapacitated && !incapTask->GetIsArrested())
|
|
{
|
|
TriggerArrestForPed(pSeatedPed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(!m_pTargetPed->GetIsArrested())
|
|
{
|
|
TriggerArrestForPed(m_pLastTargetPed);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckShouldPerformArrest(bool CNC_MODE_ENABLED_ONLY(bIgnoreInput), bool CNC_MODE_ENABLED_ONLY(bIsEnteringVehicle))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
if (!NetworkInterface::IsGameInProgress())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CPed* pPed = GetPed();
|
|
if (!pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanPerformArrest) || pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Remove last target if they are no longer incapacitated
|
|
if(m_pLastTargetPed)
|
|
{
|
|
CTaskIncapacitated* incapTask = (CTaskIncapacitated*)m_pLastTargetPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_INCAPACITATED);
|
|
if (!incapTask)
|
|
{
|
|
m_pLastTargetPed = nullptr;
|
|
}
|
|
}
|
|
|
|
if(bIsEnteringVehicle)
|
|
{
|
|
if(m_pTargetVehicle)
|
|
{
|
|
const CTaskEnterVehicle* pEnterVehicleSubTask = static_cast<const CTaskEnterVehicle*>(FindSubTaskOfType(CTaskTypes::TASK_ENTER_VEHICLE));
|
|
if(pEnterVehicleSubTask && pEnterVehicleSubTask->GetTargetEntryPoint() > -1)
|
|
{
|
|
//Check if we are within the arrest range
|
|
Vector3 vCarDoorPosition;
|
|
m_pTargetVehicle->GetEntryExitPoint(pEnterVehicleSubTask->GetTargetEntryPoint())->GetEntryPointPosition(m_pTargetVehicle, NULL, vCarDoorPosition);
|
|
if(vCarDoorPosition.Dist2(pPed->GetGroundPos()) <= sm_Tunables.m_ArrestDistance * sm_Tunables.m_ArrestDistance)
|
|
{
|
|
if(m_pTargetVehicle->GetSeatManager()->GetNumPlayers() > 0)
|
|
{
|
|
//Trigger arrest if we find a ped that is arrestable in the vehicle
|
|
for (int seatIndex = 0; seatIndex < MAX_VEHICLE_SEATS; seatIndex++)
|
|
{
|
|
CPed* pSeatedPed = m_pTargetVehicle->GetSeatManager()->GetPedInSeat(seatIndex);
|
|
if(CheckIfPedIsSuitableForArrest(bIgnoreInput, pSeatedPed))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Find any nearby network players
|
|
const netPlayer *closestPlayers[MAX_NUM_PHYSICAL_PLAYERS];
|
|
const bool bSortResults = true;
|
|
|
|
u32 numPlayersInRange = NetworkInterface::GetClosestRemotePlayers(pPed->GetGroundPos(), sm_Tunables.m_ArrestDistance, closestPlayers, bSortResults);
|
|
if (numPlayersInRange == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (u32 playerIndex = 0; playerIndex < numPlayersInRange; playerIndex++)
|
|
{
|
|
CPed* pTargetPed;
|
|
const CNetGamePlayer* pNetGamePlayer = SafeCast(const CNetGamePlayer, closestPlayers[playerIndex]);
|
|
if (pNetGamePlayer)
|
|
{
|
|
pTargetPed = pNetGamePlayer->GetPlayerPed();
|
|
if(CheckIfPedIsSuitableForArrest(bIgnoreInput, pTargetPed))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif //CNC_MODE_ENABLED
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckIfPedIsSuitableForArrest(bool CNC_MODE_ENABLED_ONLY(bIgnoreInput), CPed* CNC_MODE_ENABLED_ONLY(pTargetPed))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
CPed* pPed = GetPed();
|
|
|
|
// Confirm player is an arrestable type
|
|
if (!pTargetPed || !pTargetPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanBeArrested) || pTargetPed->IsDead())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CTaskIncapacitated* incapTask = (CTaskIncapacitated*)pTargetPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_INCAPACITATED);
|
|
|
|
// Confirm player is incapacitated
|
|
if (incapTask && incapTask->IsArrestableByPed(pPed))
|
|
{
|
|
// Prevent sending duplicate arrests
|
|
if (m_pLastTargetPed && m_pLastTargetPed == pTargetPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Check if ped we are trying to arrest is fading away or not visible
|
|
CNetObjPed* pPedObj = SafeCast(CNetObjPed, pTargetPed->GetNetworkObject());
|
|
if(pPedObj)
|
|
{
|
|
TUNE_GROUP_FLOAT(CNC_ARREST, fTimeToStopArrestDuringCrookFade, 1.0f, 0.0f, 100.0f, 0.01f);
|
|
if((pPedObj->IsFadingOut() && pPedObj->GetAlphaRampingTimeRemaining() < fTimeToStopArrestDuringCrookFade) || !pTargetPed->IsVisible())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CControl& rControl = CControlMgr::GetMainPlayerControl();
|
|
rControl.SetInputExclusive(INPUT_ARREST);
|
|
|
|
if (bIgnoreInput || pPed->GetControlFromPlayer()->GetPedArrest().IsPressed())
|
|
{
|
|
m_pTargetPed = pTargetPed;
|
|
m_pLastTargetPed = pTargetPed;
|
|
return true;
|
|
}
|
|
}
|
|
else if (m_pLastTargetPed && m_pLastTargetPed == pTargetPed)
|
|
{
|
|
// If Ped is not running Incapacitated task in the Incapacitated stage but was the last target then clear that target
|
|
// This will allow them to be arrestable again if they enter a suitable state
|
|
m_pLastTargetPed = NULL;
|
|
return false;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CNC_PlayerCopShouldTriggerActionMode(const CPed* pPlayerPed)
|
|
{
|
|
if (CPlayerInfo::AreCNCResponsivenessChangesEnabled(pPlayerPed) && pPlayerPed->GetPlayerInfo()->GetArcadeInformation().GetTeam() == eArcadeTeam::AT_CNC_COP)
|
|
{
|
|
const netPlayer * const *allPhysicalPlayers = netInterface::GetAllPhysicalPlayers();
|
|
for(unsigned index = 0; index < netInterface::GetNumPhysicalPlayers(); index++)
|
|
{
|
|
const CNetGamePlayer* pNetGamePlayer = SafeCast(const CNetGamePlayer, allPhysicalPlayers[index]);
|
|
CPed* pTargetPlayer = pNetGamePlayer->GetPlayerPed();
|
|
if (pTargetPlayer && pTargetPlayer != pPlayerPed)
|
|
{
|
|
const CPlayerInfo* pTargetPlayerInfo = pTargetPlayer->GetPlayerInfo();
|
|
if (pTargetPlayerInfo && pTargetPlayerInfo->GetArcadeInformation().GetTeam() == eArcadeTeam::AT_CNC_CROOK)
|
|
{
|
|
const CWanted& rTargetPlayerWanted = pTargetPlayerInfo->GetWanted();
|
|
if (rTargetPlayerWanted.m_WantedLevel >= WANTED_LEVEL1)
|
|
{
|
|
TUNE_GROUP_FLOAT(CNC_RESPONSIVENESS, fCopActionModeMinDistanceToCrook, 80.0f, 0.0f, 300.0f, 1);
|
|
const float fDistanceToTarget = Mag(pTargetPlayer->GetTransform().GetPosition() - pPlayerPed->GetTransform().GetPosition()).Getf();
|
|
if (fDistanceToTarget < fCopActionModeMinDistanceToCrook)
|
|
{
|
|
if (pTargetPlayer->IsPedVisible())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::Arrest_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
Assert(m_pTargetArrestPed);
|
|
|
|
#if ENABLE_TASKS_ARREST_CUFFED
|
|
switch(m_eArrestType)
|
|
{
|
|
case(eAT_Cuff):
|
|
{
|
|
CTaskArrest* pArrestTask = rage_new CTaskArrest(m_pTargetArrestPed);
|
|
SetNewTask(pArrestTask);
|
|
}
|
|
break;
|
|
case(eAT_PutInCustody):
|
|
{
|
|
CTaskArrest* pArrestTask = rage_new CTaskArrest(m_pTargetArrestPed, AF_TakeCustody);
|
|
SetNewTask(pArrestTask);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif // ENABLE_TASKS_ARREST_CUFFED
|
|
|
|
m_bWaitingForPedArrestUp = true;
|
|
|
|
m_eArrestType = eAT_None;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Arrest_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CanPerformUncuff()
|
|
{
|
|
CPed *pPlayerPed = GetPed();
|
|
|
|
//! Script set this flag when we give the ped cuff keys.
|
|
if (!pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanPerformUncuff))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//! Don't uncuff is we ourselves are cuffed!
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsHandCuffed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CPed* pPedToUncuff = NULL;
|
|
|
|
CEntity* pTargetEntity = pPlayerPed->GetPlayerInfo()->GetTargeting().GetTarget();
|
|
if (pTargetEntity && pTargetEntity->GetIsTypePed())
|
|
{
|
|
CPed* pTargetPed = static_cast<CPed*>(pTargetEntity);
|
|
|
|
float fScore = 0.0f;
|
|
if(CanUncuffPed(pTargetPed, fScore))
|
|
{
|
|
pPedToUncuff = pTargetPed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPed* pBestTargetPed = NULL;
|
|
float fBestScore = 0.0f;
|
|
float fScore = 0.0f;
|
|
|
|
CEntityScannerIterator pedList = pPlayerPed->GetPedIntelligence()->GetNearbyPeds();
|
|
for(CEntity* pEntity = pedList.GetFirst(); pEntity; pEntity = pedList.GetNext())
|
|
{
|
|
CPed *pOtherPed = static_cast<CPed *>(pEntity);
|
|
|
|
if(!CanUncuffPed(pOtherPed, fScore))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(fScore > fBestScore)
|
|
{
|
|
pBestTargetPed = pOtherPed;
|
|
fBestScore = fScore;
|
|
}
|
|
}
|
|
|
|
pPedToUncuff = pBestTargetPed;
|
|
}
|
|
|
|
if(pPedToUncuff)
|
|
{
|
|
m_pTargetUncuffPed = pPedToUncuff;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CanUncuffPed(const CPed *pOtherPed, float &fOutScore) const
|
|
{
|
|
const CPed *pPlayerPed = GetPed();
|
|
|
|
Vector3 vPlayerForward = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB());
|
|
|
|
const float fMaxUncuffDistance = sm_Tunables.m_ArrestDistance;
|
|
const float fMaxUncuffDot = sm_Tunables.m_ArrestDot;
|
|
|
|
if(pOtherPed->IsDead())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Must be handcuffed.
|
|
if(!pOtherPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsHandCuffed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Must not be ragdolled.
|
|
if(pOtherPed->GetUsingRagdoll())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Don't uncuff in vehicles.
|
|
if(pOtherPed->GetIsInVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Must be friends to uncuff. Note: Script only want this check for players.
|
|
if(pOtherPed->IsAPlayerPed() && !pPlayerPed->GetPedIntelligence()->IsFriendlyWith(*pOtherPed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Must be within distance tolerance.
|
|
float fDistance = Dist(pOtherPed->GetTransform().GetPosition(), pPlayerPed->GetTransform().GetPosition()).Getf();
|
|
if(fDistance > fMaxUncuffDistance)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Must be within angle tolerance.
|
|
Vector3 vDir = VEC3V_TO_VECTOR3(pOtherPed->GetTransform().GetPosition() - GetPed()->GetTransform().GetPosition());
|
|
vDir.NormalizeFast();
|
|
|
|
float fDot = vDir.Dot(vPlayerForward);
|
|
if(fDot <= fMaxUncuffDot)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// LOS check.
|
|
const u32 includeFlags = ArchetypeFlags::GTA_MAP_TYPE_MOVER | ArchetypeFlags::GTA_OBJECT_TYPE | ArchetypeFlags::GTA_VEHICLE_TYPE;
|
|
static const s32 iNumExceptions = 2;
|
|
const CEntity* ppExceptions[iNumExceptions] = { NULL };
|
|
ppExceptions[0] = pPlayerPed;
|
|
ppExceptions[1] = pOtherPed;
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
probeDesc.SetStartAndEnd(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()), VEC3V_TO_VECTOR3(pOtherPed->GetTransform().GetPosition()));
|
|
probeDesc.SetIncludeFlags(includeFlags);
|
|
probeDesc.SetExcludeEntities(ppExceptions,iNumExceptions);
|
|
probeDesc.SetExcludeTypeFlags(ArchetypeFlags::GTA_PICKUP_TYPE);
|
|
probeDesc.SetContext(WorldProbe::LOS_GeneralAI);
|
|
if(WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static dev_float s_fDotFactor = 1.0f;
|
|
static dev_float s_fDistanceFactor = 1.0f;
|
|
|
|
fOutScore = (fDot * s_fDotFactor) + (s_fDistanceFactor * (1.0f - (fDistance/fMaxUncuffDistance)));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckForAirMount(CPed* pPlayerPed)
|
|
{
|
|
//mounting from above
|
|
Vector3 vSearchDirection = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB());
|
|
CPed* pMountableAnimal = const_cast<CPed*>(CPlayerInfo::ScanForAnimalToRide(pPlayerPed, vSearchDirection, false, true));
|
|
if (pMountableAnimal)
|
|
{
|
|
Vector3 vHorsePosition = VEC3V_TO_VECTOR3(pMountableAnimal->GetTransform().GetPosition());
|
|
static dev_float sf_DropFlatHorseRange = 0.75f * 0.75f;
|
|
static dev_float sf_JumpFlatHorseRange = 2.0f * 2.0f;
|
|
static dev_float sf_DropZHorseRange = 1.75f;
|
|
static dev_float sf_JumpZHorseRange = 2.5f;
|
|
static dev_float sf_MinJumpZHorseRange = 1.0f;
|
|
float fFlatHorseRange = GetState() == STATE_DROPDOWN ? sf_DropFlatHorseRange : sf_JumpFlatHorseRange;
|
|
float fZHorseRange = GetState() == STATE_DROPDOWN ? sf_DropZHorseRange : sf_JumpZHorseRange;
|
|
vHorsePosition.Subtract(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()));
|
|
if (vHorsePosition.z >= -fZHorseRange && vHorsePosition.z <= -sf_MinJumpZHorseRange)
|
|
{
|
|
vHorsePosition.z=0;
|
|
if (vHorsePosition.Mag2() <= fFlatHorseRange )
|
|
{
|
|
m_pTargetAnimal = pMountableAnimal;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckShouldPerformUncuff()
|
|
{
|
|
if(GetPed()->GetControlFromPlayer()->GetPedArrest().IsUp())
|
|
{
|
|
m_bWaitingForPedArrestUp = false;
|
|
}
|
|
|
|
if(!m_bWaitingForPedArrestUp)
|
|
{
|
|
bool bCanUncuff = CanPerformUncuff();
|
|
if (bCanUncuff && GetPed()->GetControlFromPlayer()->GetPedArrest().IsDown())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s8 CTaskPlayerOnFoot::CheckShouldEnterVehicle(CPed* pPlayerPed,CControl *pControl, bool bForceControl)
|
|
{
|
|
m_pTargetVehicle = NULL;
|
|
const bool bEnterPressed = HasValidEnterVehicleInput(*pControl) || bForceControl;
|
|
|
|
bool bEnterTapped = false;
|
|
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
if (pPlayerInfo && pPlayerInfo->GetLastTimeWarpedOutOfVehicle() + CTaskEnterVehicle::TIME_TO_DETECT_ENTER_INPUT_TAP < fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
bEnterTapped = pControl && pControl->GetPedEnter().HistoryPressed(CTaskEnterVehicle::TIME_TO_DETECT_ENTER_INPUT_TAP) && pControl->GetPedEnter().IsReleased();
|
|
}
|
|
|
|
|
|
if(pPlayerPed->GetMotionData() && pPlayerPed->GetMotionData()->GetCombatRoll())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
const bool bInputAllowsVehicleSearch = bEnterPressed || (bEnterTapped && pPlayerPed->IsGoonPed());
|
|
if (pPlayerPed->IsGoonPed() && !bEnterTapped)
|
|
{
|
|
AI_LOG_WITH_ARGS("[BossGoonVehicle] Not allowing Goon player %s[%p] to search for vehicles because he's holding enter/exit button\n",
|
|
AILogging::GetDynamicEntityNameSafe(pPlayerPed), pPlayerPed);
|
|
}
|
|
|
|
if( (!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_PedExitedVehicleThisFrame) && bInputAllowsVehicleSearch) || CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch )
|
|
{
|
|
if(!pPlayerPed->GetIsSkiing())
|
|
{
|
|
// Use the most recent stick input to search for a vehicle to enter (if within TIME_TO_CONSIDER_OLD_INPUT_VALID seconds)
|
|
Vector3 vCarSearchDirection = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB());
|
|
bool bUsingStickInput = HasPlayerSelectedGunTask(pPlayerPed, GetState()) ? false : m_fTimeSinceLastStickInput < TIME_TO_CONSIDER_OLD_INPUT_VALID;
|
|
if( bUsingStickInput )
|
|
vCarSearchDirection = m_vLastStickInput;
|
|
|
|
#if DEBUG_DRAW
|
|
if( CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch )
|
|
{
|
|
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()), VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()) + m_vLastStickInput, bUsingStickInput?Color_green:Color_red, bUsingStickInput?Color_green:Color_red);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
|
|
m_pTargetVehicle = const_cast<CVehicle*>(CPlayerInfo::ScanForVehicleToEnter(pPlayerPed, vCarSearchDirection, bUsingStickInput));
|
|
|
|
if ((m_pTargetVehicle && m_pTargetVehicle->m_bSpecialEnterExit) && pPlayerPed->IsGoonPed() && !bEnterTapped)
|
|
{
|
|
AI_LOG("[BossGoonVehicle] - Nulling vehicle as player is goon and enter is not tapped");
|
|
m_pTargetVehicle = NULL;
|
|
return -1;
|
|
}
|
|
else if (pPlayerPed->IsGoonPed() && m_pTargetVehicle)
|
|
{
|
|
AI_LOG_WITH_ARGS("[BossGoonVehicle] Goon player %s[%p] will enter vehicle %s: IsSpecialVeh - %s, bTapped - %s\n",
|
|
AILogging::GetDynamicEntityNameSafe(pPlayerPed), pPlayerPed, m_pTargetVehicle->GetModelName(),
|
|
AILogging::GetBooleanAsString(m_pTargetVehicle->m_bSpecialEnterExit),AILogging::GetBooleanAsString(bEnterTapped));
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
DebugVehicleGetIns();
|
|
#endif
|
|
|
|
#if DEBUG_DRAW
|
|
if( CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch && m_pTargetVehicle )
|
|
{
|
|
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_pTargetVehicle->GetTransform().GetPosition())+ZAXIS, 0.5f, Color_green);
|
|
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()), VEC3V_TO_VECTOR3(m_pTargetVehicle->GetTransform().GetPosition()), Color_blue);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
//-------------------------------------------------------------------------
|
|
// ENTERING CARS, search for vehicles when enter pressed for picking up objects
|
|
//
|
|
// Currently disabled when swimming.
|
|
//-------------------------------------------------------------------------
|
|
|
|
//Ridable animal check
|
|
if( bEnterPressed || (bEnterTapped && pPlayerPed->IsGoonPed()))
|
|
{
|
|
Vector3 vSearchDirection = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB());
|
|
bool bUsingStickInput = m_fTimeSinceLastStickInput < TIME_TO_CONSIDER_OLD_INPUT_VALID;
|
|
m_pTargetAnimal = const_cast<CPed*>(CPlayerInfo::ScanForAnimalToRide(pPlayerPed, vSearchDirection, bUsingStickInput));;
|
|
|
|
if ( m_pTargetVehicle && CheckCanEnterVehicle(m_pTargetVehicle))
|
|
{
|
|
if ( m_pTargetAnimal ) {
|
|
Vector3 vPedPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
Vector3 vFlatDist = VEC3V_TO_VECTOR3(m_pTargetAnimal->GetTransform().GetPosition()) - vPedPos;
|
|
vFlatDist.z=0;
|
|
const float animalDist =vFlatDist.Mag2();
|
|
|
|
vFlatDist = VEC3V_TO_VECTOR3(m_pTargetVehicle->GetTransform().GetPosition()) - vPedPos;
|
|
vFlatDist.z=0;
|
|
const float vehDist =vFlatDist.Mag2();
|
|
if ( animalDist < vehDist)
|
|
return STATE_MOUNT_ANIMAL;
|
|
}
|
|
return STATE_GET_IN_VEHICLE;
|
|
}
|
|
}
|
|
|
|
if (m_pTargetAnimal)
|
|
return STATE_MOUNT_ANIMAL;
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckShouldRideTrain() const
|
|
{
|
|
//Ensure the player is inside a moving train.
|
|
if(!GetPed()->GetPlayerInfo()->IsInsideMovingTrain())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure control is not allowed inside the moving train.
|
|
if(GetPed()->GetPlayerInfo()->AllowControlInsideMovingTrain())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckShouldSlopeScramble()
|
|
{
|
|
CPed* pPlayerPed = GetPed();
|
|
|
|
if( !pPlayerPed->GetIsSwimming() && CTaskSlopeScramble::CanDoSlopeScramble(pPlayerPed) )
|
|
{
|
|
//Do the same checks whilst aiming as not aiming to keep the scramble consistent
|
|
m_fTimeSinceLastValidSlopeScramble += GetTimeStep();
|
|
++m_nFramesOfSlopeScramble;
|
|
float TimeBeforeScramble = CTaskSlopeScramble::GetTimeBeforeScramble(pPlayerPed->GetGroundNormal(), pPlayerPed->GetVelocity(), ZAXIS);
|
|
|
|
TUNE_GROUP_FLOAT (SLOPE_SCRAMBLE, fDisabledTimerGroundZGeneral, 0.6f, 0.0f, 1.0f, 0.01f);
|
|
|
|
// Don't consider time if we are not moving
|
|
if ((m_fTimeSinceLastValidSlopeScramble > TimeBeforeScramble || abs(pPlayerPed->GetGroundNormal().z) > fDisabledTimerGroundZGeneral) && m_nFramesOfSlopeScramble > 4 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we're no longer on a slope, reset.
|
|
m_fTimeSinceLastValidSlopeScramble = 0.0f;
|
|
m_nFramesOfSlopeScramble = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CanReachLadder(const CPed* pPed, const CEntity* pLadder, const Vector3& vLadderEmbarkPos)
|
|
{
|
|
TUNE_GROUP_BOOL( LADDER_DEBUG, bShowReachLadderProbes, false );
|
|
TUNE_GROUP_FLOAT( LADDER_DEBUG, fReachLadderProbeOffset, 0.720f, 0.0f, 10.0f, 0.1f );
|
|
TUNE_GROUP_FLOAT( LADDER_DEBUG, fReachLadderProbeRadius, 0.140f, 0.0f, 10.0f, 0.1f );
|
|
|
|
if( m_LadderProbeResults.GetResultsReady() )
|
|
{
|
|
// Get the results of the test, we can only reach the door if no
|
|
// collisions were found
|
|
m_bCanReachLadder = m_LadderProbeResults.GetNumHits() == 0;
|
|
// Reset the probe state for the next try
|
|
m_LadderProbeResults.Reset();
|
|
}
|
|
else if( !m_LadderProbeResults.GetWaitingOnResults() )
|
|
{
|
|
// Get the feet position of the ped as we will be offsetting the probes from that point
|
|
Vector3 vPedFeetPos = VEC3V_TO_VECTOR3( pPed->GetTransform().GetPosition() );
|
|
vPedFeetPos.z -= PED_HUMAN_GROUNDTOROOTOFFSET;
|
|
|
|
// The capsule will lie in the xy plane (we're not really interested in height)
|
|
Vector3 vStart = vPedFeetPos;
|
|
vStart.z += fReachLadderProbeOffset;
|
|
Vector3 vEnd = vLadderEmbarkPos;
|
|
vEnd.z = vStart.z;
|
|
|
|
// Set the values of the capsule descriptor to do the probe
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
|
|
// Set the results structure
|
|
capsuleDesc.SetResultsStructure(&m_LadderProbeResults);
|
|
// Set the capsule dimensions
|
|
capsuleDesc.SetCapsule(vStart, vEnd, fReachLadderProbeRadius);
|
|
// Set collision flags
|
|
u32 uCollisionFlags = (u32)ArchetypeFlags::GTA_FOLIAGE_TYPE | (u32)ArchetypeFlags::GTA_PICKUP_TYPE;
|
|
capsuleDesc.SetIncludeFlags( ~uCollisionFlags );
|
|
// Set the test as a sweep test and make an initial overlap test
|
|
capsuleDesc.SetIsDirected(true);
|
|
capsuleDesc.SetDoInitialSphereCheck(true);
|
|
// We are not interested in the ped or the ladder
|
|
const u8 nbExcludedEntities = 2;
|
|
const CEntity* excludedEntitiesArray[nbExcludedEntities] = { pPed, pLadder };
|
|
capsuleDesc.SetExcludeEntities(excludedEntitiesArray, nbExcludedEntities);
|
|
// Which part of the code is doing this probe? (for debugging)
|
|
capsuleDesc.SetContext(WorldProbe::LOS_GeneralAI);
|
|
// If there is something in the way, we shouldn't be able to reach the ladder
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
|
|
bool bDoVerticalProbe = false;
|
|
if (pPed->GetIsSwimming())
|
|
{
|
|
bDoVerticalProbe = true;
|
|
}
|
|
else
|
|
{
|
|
m_bLadderVerticalClear = true;
|
|
}
|
|
|
|
if (bDoVerticalProbe)
|
|
{
|
|
if( m_LadderVerticalProbeResults.GetResultsReady() )
|
|
{
|
|
// Get the results of the test, we can only reach the door if no
|
|
// collisions were found
|
|
m_bLadderVerticalClear = m_LadderVerticalProbeResults.GetNumHits() == 0;
|
|
|
|
//url:bugstar:6434652 Check if this hit a ladder on the yacht so we can reduce the probe width on the next check
|
|
if(!m_bLadderVerticalClear)
|
|
{
|
|
for(auto result = m_LadderVerticalProbeResults.begin(); result != m_LadderVerticalProbeResults.end(); ++result)
|
|
{
|
|
if(result->GetHitEntity())
|
|
{
|
|
//url:bugstar:6554278 Check if this is a yatch ladder we are trying to climb on. This will allow us to climb on from half way up the ladder
|
|
u32 uResultHash = result->GetHitEntity()->GetModelNameHash();
|
|
if(uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o1_rail_a", 0xABCF4AAA)
|
|
|| uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o2_rail_a", 0x75F724B2) || uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o3_rail_a", 0x620CF1E2)
|
|
|| uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o1_rail_b", 0xA599BE3F) || uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o2_rail_b", 0x840EC0E1)
|
|
|| uResultHash == ATSTRINGHASH("apa_mp_apa_yacht_o3_rail_b", 0x54C3574F))
|
|
{
|
|
m_bUseSmallerVerticalProbeCheck = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Reset the probe state for the next try
|
|
m_LadderVerticalProbeResults.Reset();
|
|
}
|
|
else if( !m_LadderVerticalProbeResults.GetWaitingOnResults() )
|
|
{
|
|
//If ped in water, do a shapetest vertically along the ladder against vehicles to see if it's blocked
|
|
const CEntity* pParent = (CEntity*)pLadder->GetAttachParent();
|
|
Vector3 vUp = pParent ? VEC3V_TO_VECTOR3(pParent->GetTransform().GetUp()) : Vector3(0.0f, 0.0f, 1.0f);
|
|
vUp.Normalize();
|
|
Vector3 vEnd = vLadderEmbarkPos + vUp * pPed->GetCapsuleInfo()->GetMaxSolidHeight() * 2;
|
|
|
|
// Set the values of the capsule descriptor to do the probe
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
|
|
// Set the results structure
|
|
capsuleDesc.SetResultsStructure(&m_LadderVerticalProbeResults);
|
|
|
|
//Reduce the size of the probe for the yacht ladder
|
|
float fReduceProbeWidthAmount = 0;
|
|
if(m_bUseSmallerVerticalProbeCheck)
|
|
{
|
|
TUNE_GROUP_FLOAT( LADDER_DEBUG, fLadderVerticalProbecheckReduceWidth, 0.050f, 0.0f, 1000.0f, 0.1f );
|
|
fReduceProbeWidthAmount = fLadderVerticalProbecheckReduceWidth;
|
|
m_bUseSmallerVerticalProbeCheck = false;
|
|
}
|
|
|
|
// Set the capsule dimensions
|
|
capsuleDesc.SetCapsule(vLadderEmbarkPos, vEnd, (pPed->GetCapsuleInfo()->GetHalfWidth() - fReduceProbeWidthAmount));
|
|
// Set collision flags
|
|
s32 uCollisionFlags = ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_OBJECT_TYPE;
|
|
capsuleDesc.SetIncludeFlags( uCollisionFlags );
|
|
// Set the test as a sweep test and make an initial overlap test
|
|
capsuleDesc.SetIsDirected(true);
|
|
capsuleDesc.SetDoInitialSphereCheck(true);
|
|
// We are not interested in the ped or the ladder
|
|
const u8 nbExcludedEntities = 3;
|
|
// Exclude the entity that ladder is attached to in case it's a boat to avoid false positives
|
|
const CEntity* excludedEntitiesArray[nbExcludedEntities] = { pPed, pLadder, pParent };
|
|
capsuleDesc.SetExcludeEntities(excludedEntitiesArray, nbExcludedEntities);
|
|
// Which part of the code is doing this probe? (for debugging)
|
|
capsuleDesc.SetContext(WorldProbe::LOS_GeneralAI);
|
|
// If there is something in the way, we shouldn't be able to reach the ladder
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
Color32 capsuleColor = m_bCanReachLadder ? Color_green : Color_red;
|
|
Color32 verticalCapsuleColor = m_bLadderVerticalClear ? Color_green : Color_red;
|
|
if(bShowReachLadderProbes)
|
|
{
|
|
// Get the feet position of the ped as we will be offsetting the probes from that point
|
|
Vector3 vPedFeetPos = VEC3V_TO_VECTOR3( pPed->GetTransform().GetPosition() );
|
|
vPedFeetPos.z -= PED_HUMAN_GROUNDTOROOTOFFSET;
|
|
|
|
// The capsule will lie in the xy plane (we're not really interested in height)
|
|
Vector3 vStart = vPedFeetPos;
|
|
vStart.z += fReachLadderProbeOffset;
|
|
Vector3 vEnd = vLadderEmbarkPos;
|
|
vEnd.z = vStart.z;
|
|
|
|
// Draw the capsule test
|
|
Vec3V vDrawStart = VECTOR3_TO_VEC3V(vStart);
|
|
Vec3V vDrawEnd = VECTOR3_TO_VEC3V(vEnd);
|
|
grcDebugDraw::Capsule( vDrawStart, vDrawEnd, fReachLadderProbeRadius, capsuleColor, false, -1 );
|
|
|
|
if (bDoVerticalProbe)
|
|
{
|
|
const CEntity* pParent = (CEntity*)pLadder->GetAttachParent();
|
|
Vector3 vUp = pParent ? VEC3V_TO_VECTOR3(pParent->GetTransform().GetUp()) : Vector3(0.0f, 0.0f, 1.0f);
|
|
vUp.Normalize();
|
|
Vector3 vEnd = vLadderEmbarkPos + vUp * pPed->GetCapsuleInfo()->GetMaxSolidHeight() * 2;
|
|
|
|
Vec3V vDrawStart = VECTOR3_TO_VEC3V(vLadderEmbarkPos);
|
|
Vec3V vDrawEnd = VECTOR3_TO_VEC3V(vEnd);
|
|
grcDebugDraw::Capsule( vDrawStart, vDrawEnd, pPed->GetCapsuleInfo()->GetHalfWidth(), verticalCapsuleColor, false, -1 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return m_bCanReachLadder && m_bLadderVerticalClear;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::LaddersAreClose(CEntity* pTargetLadder, s32 iLadderIndex, const Vector2& vHackedLadderPos, float fSearchRadius)
|
|
{
|
|
bool canGetOffAtTop = false;
|
|
Vector3 vTop(VEC3_ZERO), vBottom(VEC3_ZERO), vLadderForward(VEC3_ZERO);
|
|
if( CTaskGoToAndClimbLadder::FindTopAndBottomOfAllLadderSections(pTargetLadder, iLadderIndex, vTop, vBottom, vLadderForward, canGetOffAtTop) )
|
|
{
|
|
// Get the position of the detected ladder and compare it with the position of
|
|
// the dreaded ladder (flatten the vectors, we are not interested in the vertical
|
|
// component)
|
|
const Vector2 vDetectedLadder( vBottom.x, vBottom.y );
|
|
|
|
// Get the distance to the bad ladder
|
|
const Vector2 vDetectedLadderToHackedLadder = vHackedLadderPos - vDetectedLadder;
|
|
const float fSqrdDistToHackLadder = vDetectedLadderToHackedLadder.Mag2();
|
|
|
|
// If the found ladder is the dreaded ladder, return false (we'll consider a ladder to
|
|
// be the bad ladder if the found ladder is closer to half a meter to the known position)
|
|
if( fSqrdDistToHackLadder < fSearchRadius * fSearchRadius )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckShouldClimbLadder(CPed* pPlayerPed)
|
|
{
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL( LADDER_DEBUG, bLogCheckShouldClimbLadder, false );
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf( "START OF - CTaskPlayerOnFoot::CheckShouldClimbLadder" );
|
|
}
|
|
#endif
|
|
|
|
//******************************************************************
|
|
//Ladder
|
|
Vector2 vDesiredMoveRatio;
|
|
pPlayerPed->GetMotionData()->GetDesiredMoveBlendRatio(vDesiredMoveRatio);
|
|
|
|
bool bPassedActionModeCondition = false;
|
|
const CTaskReloadGun* pReloadTask = static_cast<const CTaskReloadGun*>(FindSubTaskOfType(CTaskTypes::TASK_RELOAD_GUN));
|
|
const bool bIsReloading = pReloadTask && !pReloadTask->IsReloadFlagSet(RELOAD_IDLE);
|
|
const bool bUseEasyLadderConditions = NetworkInterface::IsGameInProgress() && CTaskGoToAndClimbLadder::ms_bUseEasyLadderConditionsInMP && !pPlayerPed->GetIsSwimming() && (pPlayerPed->IsUsingActionMode() || pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun) || bIsReloading);
|
|
if (bUseEasyLadderConditions)
|
|
{
|
|
Vector2 vCurrentMoveBlendRatio;
|
|
pPlayerPed->GetMotionData()->GetCurrentMoveBlendRatio(vCurrentMoveBlendRatio);
|
|
bPassedActionModeCondition = vCurrentMoveBlendRatio.Mag2() != MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf( "RETURN FALSE: CPED_CONFIG_FLAG_PedIsArresting == true" );
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
// Animals have no business climbing ladders.
|
|
if (pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf( "RETURN FALSE: pPlayerPed->GetPedType() == PEDTYPE_ANIMAL" );
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
const bool bDisableLadderClimbingFlag = pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisableLadderClimbing);
|
|
const bool bPedWantsToMove = vDesiredMoveRatio.Mag2() != MOVEBLENDRATIO_STILL;
|
|
if( (bPedWantsToMove || bPassedActionModeCondition) && !bDisableLadderClimbingFlag ) //check for pushing forward
|
|
{
|
|
Vector3 vGetOnPosition(VEC3_ZERO);
|
|
Vector3 vLadderBottomPos(VEC3_ZERO);
|
|
|
|
m_pTargetLadder = CTaskGoToAndClimbLadder::ScanForLadderToClimb(*pPlayerPed, m_iLadderIndex, vGetOnPosition, NetworkInterface::IsGameInProgress());
|
|
|
|
#if LADDER_HACKS
|
|
// There is a couple of ladders in game that shouldn't be climbed
|
|
// since art can't out these ladders at this stage, we'll just
|
|
// ignore them from code.
|
|
if(m_pTargetLadder != NULL)
|
|
{
|
|
const Vector2 vHackedLadderPos1(2362.1f, 4938.9f), vHackedLadderPos2(934.0f, -3032.6f);
|
|
const float fSearchRadius = 0.5f;
|
|
|
|
// Checks for sp and mp ladders
|
|
if( LaddersAreClose(m_pTargetLadder, m_iLadderIndex, vHackedLadderPos1, fSearchRadius) )
|
|
{
|
|
return false;
|
|
}
|
|
// Checks for mp ladders
|
|
else if( NetworkInterface::IsGameInProgress() )
|
|
{
|
|
if( LaddersAreClose(m_pTargetLadder, m_iLadderIndex, vHackedLadderPos2, fSearchRadius) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(m_pPreviousTargetLadder != m_pTargetLadder)
|
|
{
|
|
m_pPreviousTargetLadder = m_pTargetLadder;
|
|
m_LadderProbeResults.Reset();
|
|
m_LadderVerticalProbeResults.Reset();
|
|
m_bCanReachLadder = false;
|
|
m_bLadderVerticalClear = false;
|
|
}
|
|
m_bGettingOnBottom = CTaskGoToAndClimbLadder::IsGettingOnBottom(m_pTargetLadder, *pPlayerPed, m_iLadderIndex, &vLadderBottomPos);
|
|
|
|
// We also do this check in CTaskGoToAndClimbLadder which will cause an inf loop if that fail
|
|
if (vLadderBottomPos.z - pPlayerPed->GetTransform().GetPosition().GetZf() > 0.25f)
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("vLadderBottomPos.z = %f", vLadderBottomPos.z);
|
|
Displayf("pPlayerPed->GetTransform().GetPosition().GetZf() = %f", pPlayerPed->GetTransform().GetPosition().GetZf());
|
|
Displayf("RETURN FALSE: vLadderBottomPos.z - pPlayerPed->GetTransform().GetPosition().GetZf() > 0.25f returned true");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
if (pPlayerPed->GetWeaponManager() && pPlayerPed->GetWeaponManager()->GetEquippedWeapon() && pPlayerPed->GetWeaponManager()->GetEquippedWeapon()->GetIsCooking())
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: pPlayerPed->GetWeaponManager()->GetEquippedWeapon()->GetIsCooking() returned true");
|
|
}
|
|
#endif
|
|
|
|
return false; // B* 1536944 [7/5/2013 musson]
|
|
}
|
|
|
|
if(m_pTargetLadder != NULL)
|
|
{
|
|
// Can we physically reach the ladder?
|
|
bool canGetOffAtTop = false;
|
|
Vector3 vTop(VEC3_ZERO), vBottom(VEC3_ZERO), vLadderForward(VEC3_ZERO);
|
|
if( CTaskGoToAndClimbLadder::FindTopAndBottomOfAllLadderSections(m_pTargetLadder, m_iLadderIndex, vTop, vBottom, vLadderForward, canGetOffAtTop) )
|
|
{
|
|
TUNE_GROUP_FLOAT( LADDER_DEBUG, fLadderBottomProbeOffset, 0.280f, 0.0f, 10.0f, 0.1f );
|
|
TUNE_GROUP_FLOAT( LADDER_DEBUG, fLadderTopProbeOffset, -0.330f, -10.0f, 0.0f, 0.1f );
|
|
|
|
// Get the ladder pos
|
|
Vector3 vLadderPos = m_bGettingOnBottom ? vBottom : vTop;
|
|
// Set the offset for the probe
|
|
const float fLadderOffset = m_bGettingOnBottom ? fLadderBottomProbeOffset : fLadderTopProbeOffset;
|
|
// Ladder bottom offset
|
|
vLadderPos += vLadderForward * fLadderOffset;
|
|
// If the ladder is physically blocked (cars, peds...) abort
|
|
if( !CanReachLadder(pPlayerPed, m_pTargetLadder, vLadderPos) )
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: CanReachLadder(pPlayerPed, m_pTargetLadder, vLadderPos) returned false");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 LadderBlockFlag = (m_bGettingOnBottom ? CTaskClimbLadderFully::BF_CLIMBING_UP : CTaskClimbLadderFully::BF_CLIMBING_DOWN);
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
if(m_pTargetLadder)
|
|
{
|
|
// use a different system involving freezing the ped is he's getting on at the top to prevent the ped running over an edge and killing himself.
|
|
if(m_bGettingOnBottom)
|
|
{
|
|
if(CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, LadderBlockFlag))
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP returned true");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
if(CTaskClimbLadderFully::IsLadderPhysicallyBlockedAtBaseMP(pPlayerPed, m_pTargetLadder, m_iLadderIndex))
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: CTaskClimbLadderFully::IsLadderPhysicallyBlockedAtBaseMP returned true");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CTaskClimbLadderFully::IsLadderBlocked(m_pTargetLadder, m_iLadderIndex, fwTimer::GetTimeInMilliseconds(), LadderBlockFlag))
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: CTaskClimbLadderFully::IsLadderBlocked returned true");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Vector3 vToLadder = vGetOnPosition - VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
|
|
// B* 1431309, prevent mounting ladders on top when too far below the top
|
|
if (!m_bGettingOnBottom && vToLadder.z > 0.f)
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf( "m_bGettingOnBottom = %s", m_bGettingOnBottom ? "true" : "false" );
|
|
Displayf( "vToLadder.z = %f", vToLadder.z );
|
|
Displayf( "RETURN FALSE: !m_bGettingOnBottom && vToLadder.z > 0.f" );
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
if(CClimbLadderDebug::ms_bRenderDebug)
|
|
{
|
|
grcDebugDraw::Sphere(vGetOnPosition, 0.05f, Color_green);
|
|
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()),vGetOnPosition, Color_blue);
|
|
}
|
|
#endif
|
|
|
|
float fLadderDist = vToLadder.XYMag2();
|
|
|
|
//Near top of Ladder
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, NearLadderRadius, 1.5f, 0.0f, 10.0f, 0.01f);
|
|
if ( !m_bGettingOnBottom && fLadderDist < (NearLadderRadius*NearLadderRadius))
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_IsNearLaddder, true);
|
|
|
|
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, DistanceToStartClimbingModifier, 0.1f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, DistanceToStartClimbingModifierBack, 0.2f, -10.0f, 10.0f, 0.01f);
|
|
|
|
float fStartDistance = 0.0f;
|
|
|
|
//static dev_float DISTANCE_TO_START_CLIMBING = 0.8f;
|
|
if(m_pTargetLadder)
|
|
{
|
|
Assertf(NetworkInterface::IsGameInProgress() || m_pLadderClipRequestHelper, "Player got ladder but no m_pLadderClipRequestHelper");
|
|
|
|
if (m_pLadderClipRequestHelper)
|
|
{
|
|
m_pLadderClipRequestHelper->m_fLastClosestLadder = fLadderDist;
|
|
if (fLadderDist < ms_fStreamClosestLadderMinDistSqr)
|
|
{
|
|
int iStreamStates = 0;
|
|
if (pPlayerPed->GetIsSwimming())
|
|
iStreamStates |= CTaskClimbLadder::NEXT_STATE_WATERMOUNT;
|
|
|
|
iStreamStates |= CTaskClimbLadder::NEXT_STATE_CLIMBING;
|
|
CTaskClimbLadder::StreamReqResourcesIn(m_pLadderClipRequestHelper, pPlayerPed, iStreamStates);
|
|
}
|
|
}
|
|
|
|
/* Matrix34 mat = MAT34V_TO_MATRIX34 (pPlayerPed->GetTransform().GetMatrix());
|
|
|
|
mat.MakeRotateZ(fwAngle::LimitRadianAngle(pPlayerPed->GetDesiredHeading()));
|
|
|
|
grcDebugDraw::Axis(mat, 0.5, true);*/
|
|
|
|
// Should dig out this information from the animations that we don't yet know which ones...
|
|
// Not sure how we should deal with this but with the assumption that we don't really change the run get ons
|
|
// We don't need to bother with this just yet.
|
|
// But when/if we fix it, we might as well deal with foot sync also so we consider that "blend distance"
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunRangeNear, 2.5f, 0.25f, 5.f, 0.1f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunRangeFar, 2.75f, 0.25f, 5.5f, 0.1f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunRangeNearTop, 2.0f, 0.25f, 5.f, 0.1f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunRangeFarTop, 2.25f, 0.25f, 5.5f, 0.1f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, WalkStartDistanceBase, 0.8f, 0.f, 1.5f, 0.1f);
|
|
|
|
if (m_bGettingOnBottom)
|
|
{
|
|
fStartDistance = WalkStartDistanceBase + (vDesiredMoveRatio.y / 10.0f);
|
|
if(pPlayerPed->GetIsInWater())
|
|
{
|
|
float fBoundPitch = abs(pPlayerPed->GetBoundPitch());
|
|
|
|
//If the player is swimming need to add extra to the distance check, because the ped capsule keeps him further
|
|
//from the ladder as it is pitched forward.
|
|
const float PitchBasedCapsuleOffset = 0.5f;
|
|
|
|
float AdjustedForPedCapsule = PitchBasedCapsuleOffset * fBoundPitch;
|
|
|
|
//clamp the max offset to 0.5m ahead of the ped.
|
|
AdjustedForPedCapsule = Clamp(AdjustedForPedCapsule, 0.0f, 0.7f);
|
|
|
|
fStartDistance += AdjustedForPedCapsule + 0.1f;
|
|
|
|
vDesiredMoveRatio.y = Min(vDesiredMoveRatio.y, MOVEBLENDRATIO_WALK); // Clamp to walk speed
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fStartDistance = 0.5f + DistanceToStartClimbingModifier + (vDesiredMoveRatio.y / 10.0f);
|
|
|
|
|
|
// If we dont face the ladder we must be closer before mounting
|
|
Vector3 vToLadderDir = vLadderBottomPos - VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
vToLadderDir.z = 0.f;
|
|
vToLadderDir.Normalize();
|
|
|
|
Vector3 vPlayerDir = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetForward());
|
|
|
|
if (vPlayerDir.Dot(vToLadderDir) < -0.25f)
|
|
fStartDistance -= DistanceToStartClimbingModifierBack;
|
|
}
|
|
|
|
bool bIdealMountTopRunDist = (!m_bGettingOnBottom && !CPedMotionData::GetIsLessThanRun(vDesiredMoveRatio.y) && square(RunRangeNearTop) < fLadderDist && square(RunRangeFarTop) > fLadderDist);
|
|
bool bIdealMountBottomRunDist = (m_bGettingOnBottom && !CPedMotionData::GetIsLessThanRun(vDesiredMoveRatio.y) && square(RunRangeNear) < fLadderDist && square(RunRangeFar) > fLadderDist);
|
|
|
|
if (!CTaskClimbLadder::IsReqResourcesLoaded(pPlayerPed, CTaskClimbLadder::NEXT_STATE_HIGHLOD))
|
|
{
|
|
// We only got these anims in "highlod", this is to prevent mounting the ladder from a great distance but
|
|
// only playing the normal get on, not the run one
|
|
bIdealMountTopRunDist = false;
|
|
bIdealMountBottomRunDist = false;
|
|
}
|
|
|
|
if (bIdealMountTopRunDist || bIdealMountBottomRunDist)
|
|
{
|
|
CTaskMotionBase* pMotionTask = pPlayerPed->GetCurrentMotionTask();
|
|
if(pMotionTask && !pMotionTask->AllowLadderRunMount())
|
|
{
|
|
bIdealMountTopRunDist = false;
|
|
bIdealMountBottomRunDist = false;
|
|
}
|
|
|
|
if (!m_bGettingOnBottom &&
|
|
NetworkInterface::IsGameInProgress() &&
|
|
(CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, CTaskClimbLadderFully::BF_CLIMBING_DOWN) ||
|
|
CTaskGoToAndClimbLadder::IsLadderTopBlockedMP(m_pTargetLadder, *pPlayerPed, m_iLadderIndex)))
|
|
{
|
|
bIdealMountTopRunDist = false;
|
|
bIdealMountBottomRunDist = false;
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(LADDER_DEBUG, PREVENT_RUN_TOP_MOUNT_WHILST_AIMING, true);
|
|
if(PREVENT_RUN_TOP_MOUNT_WHILST_AIMING &&
|
|
!m_bGettingOnBottom &&
|
|
NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun) || bIsReloading)
|
|
{
|
|
bIdealMountTopRunDist = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
vToLadder.z += pPlayerPed->GetCapsuleInfo()->GetGroundToRootOffset();
|
|
// Disable top mount run distance if it is not in line with us
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunMountTopAboveUsTolerance, 0.15f, 0.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(LADDER_DEBUG, RunMountTopBelowUsTolerance, -0.25f, -1.0f, -0.0f, 0.1f);
|
|
if (vToLadder.z > RunMountTopAboveUsTolerance || vToLadder.z < RunMountTopBelowUsTolerance)
|
|
bIdealMountTopRunDist = false;
|
|
|
|
bool bIdealMountWalkDist = fLadderDist < square(fStartDistance);
|
|
aiDebugf2("bIdealMountWalkDist %s| bIdealMountTopRunDist %s| bIdealMountBottomRunDist %s, fLadderDist = %.2f, fStartDistance = %.2f", bIdealMountWalkDist?"TRUE":"FALSE", bIdealMountTopRunDist?"TRUE":"FALSE", bIdealMountBottomRunDist?"TRUE":"FALSE", fLadderDist, fStartDistance);
|
|
|
|
if (bUseEasyLadderConditions)
|
|
{
|
|
TUNE_GROUP_FLOAT(LADDER_TUNE, LadderDistSqdToAlwaysAcceptStrafing, 0.5f, 0.0f, 1.0f, 0.1f);
|
|
bIdealMountWalkDist = fLadderDist < LadderDistSqdToAlwaysAcceptStrafing;
|
|
aiDebugf2("bIdealMountWalkDist = TRUE");
|
|
}
|
|
|
|
if (bIdealMountWalkDist || bIdealMountTopRunDist || bIdealMountBottomRunDist)
|
|
{
|
|
bool bTempIsEasyEntry = false;
|
|
if (CTaskGoToAndClimbLadder::IsPedOrientatedCorrectlyToLadderToGetOn(m_pTargetLadder, *pPlayerPed, m_iLadderIndex, bIdealMountWalkDist, bTempIsEasyEntry))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("RETURN FALSE: CTaskGoToAndClimbLadder::IsPedOrientatedCorrectlyToLadderToGetOn failed");
|
|
}
|
|
#endif
|
|
aiDebugf2("FAILED CTaskGoToAndClimbLadder::IsPedOrientatedCorrectlyToLadderToGetOn");
|
|
}
|
|
|
|
m_bEasyLadderEntry = bTempIsEasyEntry;
|
|
}
|
|
}
|
|
}
|
|
#if __BANK
|
|
else if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf( "bPedWantsToMove = %s", bPedWantsToMove ? "true" : "false" );
|
|
Displayf( "bPassedActionModeCondition = %s", bPassedActionModeCondition ? "true" : "false" );
|
|
Displayf( "bDisableLadderClimbingFlag = %s", bDisableLadderClimbingFlag ? "true" : "false" );
|
|
Displayf( "RETURN FALSE: ((bPedWantsToMove || bPassedActionModeCondition) && !bDisableLadderClimbingFlag) failed" );
|
|
}
|
|
#endif
|
|
|
|
#if __BANK
|
|
if(bLogCheckShouldClimbLadder)
|
|
{
|
|
Displayf("END OF - CTaskPlayerOnFoot::CheckShouldClimbLadder");
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::Uncuff_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
Assert(m_pTargetUncuffPed);
|
|
|
|
#if ENABLE_TASKS_ARREST_CUFFED
|
|
CTaskArrest* pArrestTask = rage_new CTaskArrest(m_pTargetUncuffPed, AF_UnCuff);
|
|
SetNewTask(pArrestTask);
|
|
#endif // ENABLE_TASKS_ARREST_CUFFED
|
|
|
|
m_bWaitingForPedArrestUp = true;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Uncuff_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::PlayerGun_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
s32 iFlags = 0;
|
|
|
|
if (m_bReloadFromIdle)
|
|
{
|
|
iFlags |= GF_ReloadFromIdle;
|
|
}
|
|
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnEnter: Previous State: %d", GetPreviousState());
|
|
|
|
SetNewTask(rage_new CTaskPlayerWeapon(iFlags));
|
|
|
|
m_bWaitingForLadder = false;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::PlayerGun_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
const bool bFPSMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer();
|
|
TUNE_GROUP_BOOL(PED_DOOR, AllowOpenDoorArmIk, false);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
bool bAllowJumpInGunState = false;
|
|
const CWeaponInfo* pWeaponInfo = pPlayerPed->GetEquippedWeaponInfo();
|
|
#if FPS_MODE_SUPPORTED
|
|
bool bAllowOpenDoorArmIK = false;
|
|
if(bFPSMode)
|
|
{
|
|
const bool bIsAWeapon = pWeaponInfo && !pWeaponInfo->GetIsUnarmed();
|
|
const bool bFPSIdle = pPlayerPed->GetMotionData()->GetIsFPSIdle();
|
|
|
|
bool bUsingKeyboardAndMouse = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
bUsingKeyboardAndMouse = pPlayerPed->GetControlFromPlayer()->WasKeyboardMouseLastKnownSource();
|
|
#endif
|
|
|
|
if(!bFPSIdle && !bUsingKeyboardAndMouse)
|
|
{
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl)
|
|
{
|
|
pControl->ClearToggleRun();
|
|
}
|
|
}
|
|
|
|
|
|
// Allow jump in first person camera when in the fps idle state or we're unarmed
|
|
// Don't allow on the first frame, so we can ensure the MBR is up to date
|
|
bAllowJumpInGunState = (bFPSIdle || !bIsAWeapon) && GetTimeInState() > 0.f;
|
|
|
|
// Allow open door arm IK only when in fps idle and unarmed
|
|
bAllowOpenDoorArmIK = bFPSIdle && !bIsAWeapon && AllowOpenDoorArmIk;
|
|
}
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_OpenDoorArmIK, bAllowOpenDoorArmIK );
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
// Allow jump in gun state if super jump is enabled.
|
|
if (!bAllowJumpInGunState)
|
|
{
|
|
bAllowJumpInGunState = pPlayerPed->GetPlayerInfo() && pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_SUPER_JUMP_ON);
|
|
}
|
|
|
|
if (!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisablePlayerJumping))
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, !bAllowJumpInGunState );
|
|
}
|
|
|
|
CTask* pMotionTask = pPlayerPed->GetCurrentMotionTask(false);
|
|
if(pMotionTask && pMotionTask->GetTaskType() == CTaskTypes::TASK_MOTION_AIMING)
|
|
{
|
|
if(static_cast<CTaskMotionAiming*>(pMotionTask)->GetState() == CTaskMotionAiming::State_Roll)
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerVaulting, true );
|
|
}
|
|
}
|
|
|
|
// Disable simulated aiming if the following input is detected.
|
|
if(pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_SimulatingAiming ))
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl->GetPedWalkLeftRight().GetNorm() != 0.0f || pControl->GetPedWalkUpDown().GetNorm() != 0.0f || pControl->GetPedAimWeaponLeftRight().GetNorm() != 0.0f || pControl->GetPedAimWeaponUpDown().GetNorm() != 0.0f
|
|
|| pControl->GetPedAttack().IsPressed() || pControl->GetPedAttack2().IsPressed() != 0.0f || pControl->GetPedTargetIsDown())
|
|
{
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_SimulatingAiming, false );
|
|
}
|
|
}
|
|
|
|
// Check to see if we have a melee task stored off from the previous frame
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
CTaskReloadGun* pReloadTask = static_cast<CTaskReloadGun*>( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_RELOAD_GUN ) );
|
|
if( pReloadTask )
|
|
{
|
|
pReloadTask->RequestInterrupt();
|
|
}
|
|
|
|
pPlayerPed->SetIsCrouching( false, -1, false );
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: SetState(STATE_MELEE) - m_pMeleeRequestTask. Previous State: %d", GetPreviousState());
|
|
SetState(STATE_MELEE);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
ProcessSpecialAbilities();
|
|
|
|
if(CheckShouldRideTrain())
|
|
{
|
|
SetState(STATE_RIDE_TRAIN);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
weaponAssert(pPlayerPed->GetWeaponManager());
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
const bool bFirstPersonScopeWeapon = pWeapon && pWeapon->GetHasFirstPersonScope();
|
|
const bool bHeavyWeapon = pWeapon && pWeapon->GetWeaponInfo()->GetIsHeavy();
|
|
|
|
if(m_bWaitingForLadder)
|
|
{
|
|
// Ped has given up trying to get on ladder....
|
|
if(!KeepWaitingForLadder(pPlayerPed, m_fBlockHeading))
|
|
{
|
|
m_bWaitingForLadder = false;
|
|
return FSM_Continue;
|
|
}
|
|
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_LadderBlockingMovement, true);
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(0.0f, 0.0f);
|
|
pPlayerPed->SetVelocity(VEC3_ZERO);
|
|
pPlayerPed->SetDesiredVelocity(VEC3_ZERO);
|
|
|
|
pPlayerPed->SetHeading(m_fBlockHeading);
|
|
pPlayerPed->SetPosition(m_vBlockPos, true, true);
|
|
|
|
if(!CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, CTaskClimbLadderFully::BF_CLIMBING_DOWN) &&
|
|
!CTaskGoToAndClimbLadder::IsLadderTopBlockedMP(m_pTargetLadder, *pPlayerPed, m_iLadderIndex))
|
|
{
|
|
SetState(STATE_CLIMB_LADDER);
|
|
}
|
|
}
|
|
else if( !pPlayerPed->GetIsSwimming() && CTaskSlopeScramble::CanDoSlopeScramble(pPlayerPed) )
|
|
{
|
|
//Do the same checks whilst aiming as not aiming to keep the scramble consistent
|
|
m_fTimeSinceLastValidSlopeScramble += fwTimer::GetTimeStep();
|
|
++m_nFramesOfSlopeScramble;
|
|
float TimeBeforeScramble = CTaskSlopeScramble::GetTimeBeforeScramble(pPlayerPed->GetGroundNormal(), pPlayerPed->GetVelocity(), ZAXIS);
|
|
|
|
TUNE_GROUP_FLOAT (SLOPE_SCRAMBLE, fDisabledTimerGroundZMaxAiming, 0.6f, 0.0f, 1.0f, 0.01f);
|
|
TUNE_GROUP_INT (SLOPE_SCRAMBLE, nSlopeScrambleFramesAiming, 4, 0, 1000, 1);
|
|
|
|
// Don't consider time if we are not moving
|
|
if ((m_fTimeSinceLastValidSlopeScramble > TimeBeforeScramble || abs(pPlayerPed->GetGroundNormal().z) > fDisabledTimerGroundZMaxAiming) && m_nFramesOfSlopeScramble > nSlopeScrambleFramesAiming )
|
|
{
|
|
SetState(STATE_SLOPE_SCRAMBLE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we're no longer on a slope, reset.
|
|
m_fTimeSinceLastValidSlopeScramble = 0.0f;
|
|
m_nFramesOfSlopeScramble = 0;
|
|
}
|
|
|
|
bool bUsableUnderwater = false;
|
|
if (pWeaponInfo)
|
|
{
|
|
bUsableUnderwater = pWeaponInfo->GetIsUnderwaterGun();
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool bPhoneOnScreen = CTaskPlayerOnFoot::CheckForUseMobilePhone(*pPlayerPed);
|
|
// If the player isn't texting but the phone is out, switch to running the texting clip
|
|
if(bFPSMode && bPhoneOnScreen && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsReloading))
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: SetState(STATE_PLAYER_IDLES) - bPhoneOnScreen. Previous State: %d", GetPreviousState());
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
#endif
|
|
|
|
// Quit out of gun state if we have just done a viewmode switch and are not forced aiming
|
|
const bool bExitedCoverThisFrame = (fwTimer::GetTimeInMilliseconds() - m_uLastTimeInCoverTask == 0) ? true : false;
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished) || ((pPlayerPed->GetIsSwimming() && !bUsableUnderwater)) ||
|
|
(pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON) && pPlayerPed->GetMotionData()->GetForcedMotionStateThisFrame() != CPedMotionStates::MotionState_Aiming && !bExitedCoverThisFrame && !pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_FIRST_PERSON)))
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->m_uLastTimeStopAiming = fwTimer::GetTimeInMilliseconds();
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pWeaponInfo && GetIsFlagSet(aiTaskFlags::SubTaskFinished) && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(true, false, true, true, false))
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: RestartCurrentState");
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: SetState(STATE_PLAYER_IDLES) - SubTaskFinished. Previous State: %d", GetPreviousState());
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
}
|
|
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(CheckForUsingJetpack(pPlayerPed))
|
|
{
|
|
SetState(STATE_JETPACK);
|
|
}
|
|
else if(!m_bPlayerExitedCoverThisUpdate && !bFirstPersonScopeWeapon && !bHeavyWeapon && CheckForNearbyCover(pPlayerPed))
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: SetState(STATE_USE_COVER) - CheckForNearbyCover. Previous State: %d", GetPreviousState());
|
|
SetState(STATE_USE_COVER);
|
|
}
|
|
else if(CheckShouldPerformArrest())
|
|
{
|
|
ArrestTargetPed(pPlayerPed);
|
|
}
|
|
else if(CheckShouldPerformUncuff())
|
|
{
|
|
SetState(STATE_UNCUFF);
|
|
}
|
|
else if( /*pWeapon && (pWeapon->GetState() == CWeapon::STATE_RELOADING) &&*/ DoJumpCheck(pPlayerPed, pControl, bHeavyWeapon))
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::PlayerGun_OnUpdate: SetState(STATE_JUMP) - DoJumpCheck. Previous State: %d", GetPreviousState());
|
|
SetState(STATE_JUMP);
|
|
}
|
|
else if(CheckShouldClimbLadder(pPlayerPed) && !camInterface::GetGameplayDirector().IsFirstPersonAiming())
|
|
{
|
|
if (!m_bGettingOnBottom &&
|
|
NetworkInterface::IsGameInProgress() &&
|
|
(CTaskClimbLadderFully::IsLadderBaseOrTopBlockedByPedMP(m_pTargetLadder, m_iLadderIndex, CTaskClimbLadderFully::BF_CLIMBING_DOWN)||
|
|
CTaskGoToAndClimbLadder::IsLadderTopBlockedMP(m_pTargetLadder, *pPlayerPed, m_iLadderIndex)))
|
|
{
|
|
m_vBlockPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
m_fBlockHeading = pPlayerPed->GetTransform().GetHeading();
|
|
m_bWaitingForLadder = true;
|
|
}
|
|
else
|
|
{
|
|
SetState(STATE_CLIMB_LADDER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s8 iEnterVehicleState = CheckShouldEnterVehicle(pPlayerPed,pControl, m_bGetInVehicleAfterCombatRolling);
|
|
|
|
if(iEnterVehicleState != -1)
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_WantsToEnterVehicleFromAiming, true);
|
|
SetState((ePlayerOnFootState)iEnterVehicleState);
|
|
}
|
|
}
|
|
|
|
// Enable scanning flags
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_SearchingForDoors, true );
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bFPSMode && GetState() == STATE_PLAYER_GUN)
|
|
{
|
|
PlayerIdles_OnUpdate(pPlayerPed);
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::PlayerGun_OnExit(CPed* pPlayerPed)
|
|
{
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_SimulatingAiming, false );
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_OpenDoorArmIK, false );
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::GetMountAnimal_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
#if ENABLE_HORSE
|
|
if( m_pTargetAnimal )
|
|
{
|
|
VehicleEnterExitFlags vehicleFlags;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::PlayerControlledVehicleEntry);
|
|
if (GetPreviousState() == STATE_DROPDOWN)
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::DropEntry);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::InAirEntry);
|
|
}
|
|
else if ( GetPreviousState() == STATE_JUMP)
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::JumpEntry);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::InAirEntry);
|
|
}
|
|
SeatRequestType iSeatRequestType = SR_Specific;
|
|
s32 iSeat = m_pTargetAnimal->GetPedModelInfo()->GetModelSeatInfo()->GetDriverSeat();
|
|
|
|
CTask* pTask = rage_new CTaskMountAnimal(m_pTargetAnimal, iSeatRequestType, iSeat, vehicleFlags);
|
|
SetNewTask( pTask );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::GetMountAnimal_OnUpdate(CPed* ENABLE_HORSE_ONLY(pPlayerPed) )
|
|
{
|
|
#if ENABLE_HORSE
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
if(pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_OnMount ) )
|
|
{
|
|
// Ped got onto mount, quit the on foot task
|
|
return FSM_Quit;
|
|
}
|
|
else
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
//start processing inputs on the mount
|
|
if (pPlayerPed->GetMyMount())
|
|
CTaskPlayerOnHorse::DriveHorse(pPlayerPed);
|
|
|
|
#else
|
|
taskAssert(0);
|
|
#endif
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::GetInVehicle_OnEnter(CPed* pPlayerPed)
|
|
{
|
|
if( m_pTargetVehicle )//&& bEnterHeld)
|
|
{
|
|
VehicleEnterExitFlags vehicleFlags;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::PlayerControlledVehicleEntry);
|
|
|
|
bool bWantsToEnterVehicleFromAiming = pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterVehicleFromAiming);
|
|
#if FPS_MODE_SUPPORTED
|
|
if (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
bWantsToEnterVehicleFromAiming = pPlayerPed->GetPlayerInfo()->IsAiming();
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
if (pPlayerPed->GetIsInCover() || bWantsToEnterVehicleFromAiming)
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::CombatEntry);
|
|
}
|
|
|
|
if (pPlayerPed->GetIsInCover())
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::FromCover);
|
|
}
|
|
|
|
if( m_pTargetVehicle->GetVehicleType() == VEHICLE_TYPE_TRAIN )
|
|
{
|
|
const CTrain* pTrain = static_cast<const CTrain*>(m_pTargetVehicle.Get());
|
|
Assert( pTrain->m_nTrainFlags.bAtStation );
|
|
if (pTrain->m_nTrainFlags.iStationPlatformSides & CTrainTrack::Right)
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::PreferRightEntry);
|
|
else
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::PreferLeftEntry);
|
|
}
|
|
|
|
if (CTaskVehicleFSM::IsVehicleLockedForPlayer(*m_pTargetVehicle, *pPlayerPed))
|
|
{
|
|
if (m_pTargetVehicle->GetDriver() && pPlayerPed->GetPedIntelligence()->IsThreatenedBy(*m_pTargetVehicle->GetDriver()))
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::JustPullPedOut);
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::UseDirectEntryOnly);
|
|
}
|
|
}
|
|
|
|
SeatRequestType iSeatRequestType = SR_Specific;
|
|
s32 iSeat = m_pTargetVehicle->GetDriverSeat();
|
|
|
|
// Process turreted vehicle initial seat preferences
|
|
int iBestTurretSeat = -1;
|
|
const bool bHasTurret = m_pTargetVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_TURRET_SEAT_ON_VEHICLE);
|
|
const CPed* pDriver = CTaskVehicleFSM::GetPedInOrUsingSeat(*m_pTargetVehicle, iSeat);
|
|
if (pDriver)
|
|
{
|
|
// If theres a friendly driver, see if we want to prefer entering a turret seat
|
|
if (pDriver->GetPedIntelligence()->IsFriendlyWith(*pPlayerPed))
|
|
{
|
|
iBestTurretSeat = CTaskVehicleFSM::FindBestTurretSeatIndexForVehicle(*m_pTargetVehicle, *pPlayerPed, pDriver, iSeat, false, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If theres no driver and we're stood on the turreted vehicle (not heli or plane), prefer the turret seat
|
|
if (pPlayerPed->GetGroundPhysical() == m_pTargetVehicle && !m_pTargetVehicle->InheritsFromHeli() && !m_pTargetVehicle->InheritsFromPlane() && !m_pTargetVehicle->InheritsFromBoat())
|
|
{
|
|
iBestTurretSeat = m_pTargetVehicle->GetFirstTurretSeat();
|
|
}
|
|
}
|
|
|
|
// Prefer the turret seat if found
|
|
if (m_pTargetVehicle->IsTurretSeat(iBestTurretSeat))
|
|
{
|
|
iSeatRequestType = SR_Prefer;
|
|
iSeat = iBestTurretSeat;
|
|
}
|
|
else if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (bHasTurret && pDriver)
|
|
{
|
|
iSeatRequestType = SR_Any;
|
|
}
|
|
else
|
|
{
|
|
iSeatRequestType = SR_Prefer;
|
|
}
|
|
iSeat = m_pTargetVehicle->GetDriverSeat();
|
|
}
|
|
if(taskVerifyf(m_pTargetVehicle->GetLayoutInfo(),"NULL layout info") && !m_pTargetVehicle->GetLayoutInfo()->GetHasDriver())
|
|
{
|
|
iSeatRequestType = SR_Any;
|
|
iSeat = -1;
|
|
}
|
|
else
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::CanWarpOverPassengers);
|
|
}
|
|
|
|
// If player is on skis then switch to on foot
|
|
if(pPlayerPed->GetIsSkiing())
|
|
{
|
|
pPlayerPed->SetIsSkiing(false);
|
|
}
|
|
|
|
if( pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_ForcePlayerToEnterVehicleThroughDirectDoorOnly) )
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::UseDirectEntryOnly);
|
|
}
|
|
|
|
if( pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_WillJackWantedPlayersRatherThanStealCar) )
|
|
{
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::JackWantedPlayersRatherThanStealCar);
|
|
}
|
|
|
|
// Walk unless run is held or was released within the last second
|
|
static dev_s32 RUN_IF_RUN_HELD_WITHIN_TIME = 3000;
|
|
float fMoveBlendRatio = MOVEBLENDRATIO_WALK;
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if( pControl && ( pControl->GetPedSprintIsDown() || pControl->GetPedSprintHistoryReleased(RUN_IF_RUN_HELD_WITHIN_TIME)) )
|
|
{
|
|
fMoveBlendRatio = MOVEBLENDRATIO_RUN;
|
|
}
|
|
|
|
#if __BANK
|
|
if (CVehicleDebug::ms_bForcePlayerToUseSpecificSeat && m_pTargetVehicle && m_pTargetVehicle->IsSeatIndexValid(CVehicleDebug::ms_iSeatRequested))
|
|
{
|
|
iSeat = CVehicleDebug::ms_iSeatRequested;
|
|
iSeatRequestType = SR_Specific;
|
|
vehicleFlags.BitSet().Set(CVehicleEnterExitFlags::JackIfOccupied);
|
|
}
|
|
#endif // __BANK
|
|
|
|
CTask* pTask = rage_new CTaskEnterVehicle(m_pTargetVehicle, iSeatRequestType, iSeat, vehicleFlags, 0.0f, fMoveBlendRatio);
|
|
AI_LOG_WITH_ARGS("[Player] - TASK_ENTER_VEHICLE constructed at 0x%p, target vehicle - %s, iSeat - %i\n", pTask, AILogging::GetDynamicEntityNameSafe(m_pTargetVehicle), iSeat);
|
|
SetNewTask( pTask );
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::GetInVehicle_OnUpdate(CPed* pPlayerPed)
|
|
{
|
|
//Override with an arrest if we can
|
|
if(CheckShouldPerformArrest(true, true))
|
|
{
|
|
ArrestTargetPed(pPlayerPed);
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
|
|
if (m_pTargetVehicle)
|
|
CVehicle::RequestHdForVehicle(m_pTargetVehicle);
|
|
|
|
// Try to vault if we are stuck whilst navigating on a vehicle
|
|
if (m_pTargetVehicle == pPlayerPed->GetGroundPhysical())
|
|
{
|
|
if (pPlayerPed->GetPedIntelligence()->GetEventScanner()->GetStaticMovementScanner().GetStaticCounter() > 10)
|
|
{
|
|
CTaskMove * pTaskSimplestMove = pPlayerPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
|
|
if (pTaskSimplestMove && pTaskSimplestMove->IsTaskMoving() && pTaskSimplestMove->GetMoveInterface()->HasTarget())
|
|
{
|
|
Assert(pTaskSimplestMove->IsMoveTask());
|
|
Vector3 vToTarget = pTaskSimplestMove->GetTarget() - VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
vToTarget.z = 0.f;
|
|
vToTarget.Normalize();
|
|
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if (Dot(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetForward()), vToTarget) > 0.967f && // We must face the dir somewhat in case we are strafing
|
|
DoJumpCheck(pPlayerPed, pPlayerPed->GetControlFromPlayer(), pWeapon && pWeapon->GetWeaponInfo()->GetIsHeavy(), true))
|
|
{
|
|
//! If it is a boat, apply some reasonable limits on how high we can climb. Force climb after static counter gets high enough.
|
|
bool bHandholdOK = true;
|
|
|
|
const CTaskEnterVehicle* pEnterVehicleSubTask = static_cast<const CTaskEnterVehicle*>(FindSubTaskOfType(CTaskTypes::TASK_ENTER_VEHICLE));
|
|
const bool bCanVaultUp = pEnterVehicleSubTask ? pEnterVehicleSubTask->GetState() == CTaskEnterVehicle::State_GoToClimbUpPoint : false;
|
|
if(m_pTargetVehicle->InheritsFromBoat() || bCanVaultUp)
|
|
{
|
|
CClimbHandHoldDetected handHold;
|
|
bool bDetected = pPlayerPed->GetPedIntelligence()->GetClimbDetector().GetDetectedHandHold(handHold);
|
|
if(bDetected)
|
|
{
|
|
static dev_float s_fMaxClimbHeight = 1.5f;
|
|
static dev_u32 s_nForceClimbStaticCounter = 50;
|
|
|
|
if( ((handHold.GetHandHold().GetPoint().GetZ() - pPlayerPed->GetGroundPos().z) > s_fMaxClimbHeight) &&
|
|
pPlayerPed->GetPedIntelligence()->GetEventScanner()->GetStaticMovementScanner().GetStaticCounter() < s_nForceClimbStaticCounter)
|
|
{
|
|
bHandholdOK = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bHandholdOK)
|
|
{
|
|
SetState(STATE_JUMP);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(GetIsSubtaskFinished(CTaskTypes::TASK_ENTER_VEHICLE))
|
|
{
|
|
if(pPlayerPed->GetMyVehicle() && pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
|
|
{
|
|
// Ped got into vehicle sucessfully, quit the on foot task
|
|
return FSM_Quit;
|
|
}
|
|
else if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterCover))
|
|
{
|
|
SetState(STATE_INITIAL);
|
|
return FSM_Continue;
|
|
}
|
|
else if (GetTimeInState() > 0.0f) // Prevent infinite loop when trying to enter a vehicle occupied by a friendly ped
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckCanEnterVehicle(CVehicle* pTargetVehicle)
|
|
{
|
|
bool bReturn = true;
|
|
if( pTargetVehicle )
|
|
{
|
|
const CPed& ped = *GetPed();
|
|
|
|
if (ped.GetWeaponManager() && ped.GetWeaponManager()->GetEquippedWeapon() && ped.GetWeaponManager()->GetEquippedWeapon()->GetIsCooking())
|
|
{
|
|
AI_LOG_WITH_ARGS("[Player] - Unable to enter vehicle %s due to cooking", AILogging::GetDynamicEntityNameSafe(pTargetVehicle));
|
|
return false; // B* 1448761 [6/9/2013 musson]
|
|
}
|
|
|
|
const VehicleType vehType = pTargetVehicle->GetVehicleType();
|
|
const bool bLockedToPlayer = CTaskVehicleFSM::IsVehicleLockedForPlayer(*pTargetVehicle, ped) || pTargetVehicle->GetCarDoorLocks() == CARLOCK_LOCKED;
|
|
if (bLockedToPlayer)
|
|
{
|
|
// Let peds enter vehicles that have a driver (either as a passenger or to pull the driver out)
|
|
if (!pTargetVehicle->GetDriver())
|
|
{
|
|
if (vehType != VEHICLE_TYPE_CAR)
|
|
{
|
|
bReturn = false;
|
|
}
|
|
}
|
|
// Don't even attempt to enter the vehicle if flagged as such
|
|
else if (pTargetVehicle->m_nVehicleFlags.bDontTryToEnterThisVehicleIfLockedForPlayer)
|
|
{
|
|
AI_LOG_WITH_ARGS("[Player] - Unable to enter vehicle %s due to bDontTryToEnterThisVehicleIfLockedForPlayer, pTargetVehicle->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD) ? %s", AILogging::GetDynamicEntityNameSafe(pTargetVehicle), AILogging::GetBooleanAsString(pTargetVehicle->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD)));
|
|
bReturn = false;
|
|
}
|
|
}
|
|
|
|
if( vehType == VEHICLE_TYPE_TRAIN )
|
|
{
|
|
const CTrain* pTrain = static_cast<const CTrain*>(pTargetVehicle);
|
|
if( !pTrain->m_nTrainFlags.bAtStation )
|
|
{
|
|
bReturn = false;
|
|
}
|
|
}
|
|
|
|
if( vehType == VEHICLE_TYPE_BIKE ||
|
|
vehType == VEHICLE_TYPE_BICYCLE ||
|
|
vehType == VEHICLE_TYPE_QUADBIKE ||
|
|
vehType == VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE)
|
|
{
|
|
if (!pTargetVehicle->CanPedEnterCar(&ped))
|
|
{
|
|
bReturn = false;
|
|
}
|
|
}
|
|
|
|
if (!bReturn || bLockedToPlayer)
|
|
{
|
|
GetEventScriptAIGroup()->Add(CEventPlayerUnableToEnterVehicle(*pTargetVehicle));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bReturn = false;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TakeOffHelmet_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
m_bGetInVehicleAfterTakingOffHelmet = false;
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
SetNewTask(rage_new CTaskComplexControlMovement( rage_new CTaskMovePlayer(), rage_new CTaskTakeOffHelmet(), CTaskComplexControlMovement::TerminateOnSubtask));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::TakeOffHelmet_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
CPed *pPed = GetPed();
|
|
CControl* pControl = pPed->GetControlFromPlayer();
|
|
const Vector3 vCarSearchDirection = VEC3V_TO_VECTOR3(pPed->GetTransform().GetB());
|
|
const bool bUsingStickInput = false;
|
|
if( pPed->IsFirstPersonShooterModeEnabledForPlayer(false) &&
|
|
pControl &&
|
|
pControl->GetPedEnter().IsPressed() &&
|
|
CPlayerInfo::ScanForVehicleToEnter(pPed, vCarSearchDirection, bUsingStickInput) != NULL )
|
|
{
|
|
m_bGetInVehicleAfterTakingOffHelmet = true;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
if( GetIsFlagSet(aiTaskFlags::SubTaskFinished) )
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
if( CheckShouldEnterVehicle(pPed, pControl, m_bGetInVehicleAfterTakingOffHelmet) == STATE_GET_IN_VEHICLE )
|
|
SetState(STATE_GET_IN_VEHICLE);
|
|
else
|
|
SetState(STATE_PLAYER_IDLES);
|
|
#else
|
|
SetState(STATE_PLAYER_IDLES);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
void CTaskPlayerOnFoot::DuckAndCover_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
SetNewTask(rage_new CTaskDuckAndCover());
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::DuckAndCover_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::SlopeScramble_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
SetNewTask(rage_new CTaskSlopeScramble());
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::SlopeScramble_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ScriptedTask_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if(taskVerifyf(m_pScriptedTask, "ScriptedTask cannot be NULL!"))
|
|
{
|
|
SetNewTask(m_pScriptedTask);
|
|
m_pScriptedTask = NULL;
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::ScriptedTask_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TakeOffParachutePack_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
//Create the move task.
|
|
CTaskMovePlayer* pMoveTask = rage_new CTaskMovePlayer();
|
|
|
|
//Set the max move/blend ratio.
|
|
pMoveTask->SetMaxMoveBlendRatio(MOVEBLENDRATIO_WALK);
|
|
|
|
//Get the parachute pack variation.
|
|
ePedVarComp nVariationComponent = PV_COMP_INVALID;
|
|
u8 uVariationDrawableId = 0;
|
|
u8 uVariationDrawableAltId = 0;
|
|
u8 uVariationTexId = 0;
|
|
CTaskParachute::GetParachutePackVariationForPed(*GetPed(), nVariationComponent, uVariationDrawableId, uVariationDrawableAltId, uVariationTexId);
|
|
|
|
//Define the parameters.
|
|
static fwMvClipSetId s_ClipSetId("skydive@parachute@pack",0x3587E495);
|
|
static fwMvClipId s_ClipIdForPed("Chute_Off",0xB44359AE);
|
|
static fwMvClipId s_ClipIdForProp("Chute_Off_Bag",0xA13571F8);
|
|
static eAnimBoneTag s_nAttachBone = BONETAG_SPINE3;
|
|
static dev_float s_fForceToApplyAfterInterrupt = 300.0f;
|
|
|
|
atHashWithStringNotFinal hProp = CTaskParachute::GetModelForParachutePack(GetPed());
|
|
|
|
//Create the sub-task.
|
|
CTaskTakeOffPedVariation* pSubTask = rage_new CTaskTakeOffPedVariation(s_ClipSetId, s_ClipIdForPed, s_ClipIdForProp,
|
|
nVariationComponent, uVariationDrawableId, uVariationDrawableAltId, uVariationTexId, hProp, s_nAttachBone);
|
|
|
|
//Set the attach parameters.
|
|
Vec3V vAttachOffset(sm_Tunables.m_ParachutePack.m_AttachOffsetX,
|
|
sm_Tunables.m_ParachutePack.m_AttachOffsetY, sm_Tunables.m_ParachutePack.m_AttachOffsetZ);
|
|
pSubTask->SetAttachOffset(vAttachOffset);
|
|
Vec3V vAttachOrientation(sm_Tunables.m_ParachutePack.m_AttachOrientationX * DtoR,
|
|
sm_Tunables.m_ParachutePack.m_AttachOrientationY * DtoR, sm_Tunables.m_ParachutePack.m_AttachOrientationZ * DtoR);
|
|
QuatV qAttachOrientation = QuatVFromEulersXYZ(vAttachOrientation);
|
|
pSubTask->SetAttachOrientation(qAttachOrientation);
|
|
|
|
//Set the blend in parameters.
|
|
pSubTask->SetBlendInDeltaForPed(sm_Tunables.m_ParachutePack.m_BlendInDeltaForPed);
|
|
pSubTask->SetBlendInDeltaForProp(sm_Tunables.m_ParachutePack.m_BlendInDeltaForProp);
|
|
|
|
//Set the blend out parameters.
|
|
pSubTask->SetPhaseToBlendOut(sm_Tunables.m_ParachutePack.m_PhaseToBlendOut);
|
|
pSubTask->SetBlendOutDelta(sm_Tunables.m_ParachutePack.m_BlendOutDelta);
|
|
|
|
//Set the force.
|
|
pSubTask->SetForceToApplyAfterInterrupt(s_fForceToApplyAfterInterrupt);
|
|
|
|
//Set the velocity inheritance.
|
|
if(sm_Tunables.m_ParachutePack.m_VelocityInheritance.m_Enabled)
|
|
{
|
|
pSubTask->SetVelocityInheritance(Vec3V(sm_Tunables.m_ParachutePack.m_VelocityInheritance.m_X,
|
|
sm_Tunables.m_ParachutePack.m_VelocityInheritance.m_Y,
|
|
sm_Tunables.m_ParachutePack.m_VelocityInheritance.m_Z));
|
|
}
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskComplexControlMovement(pMoveTask, pSubTask, CTaskComplexControlMovement::TerminateOnSubtask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::TakeOffParachutePack_OnUpdate(CPed* BANK_ONLY(pPlayerPed))
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
#if __BANK
|
|
if(CTaskParachute::IsParachutePackVariationActiveForPed(*pPlayerPed) && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableTakeOffParachutePack))
|
|
{
|
|
Assertf(0, "Player hasn't taken off parachute pack successfully - it is still on & we are going to get into an infinite loop! Please add a bug with AI Display: Parachute Info!");
|
|
}
|
|
#endif
|
|
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
//Check if we should slope scramble.
|
|
else if(CheckShouldSlopeScramble())
|
|
{
|
|
SetState(STATE_SLOPE_SCRAMBLE);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TakeOffJetpack_OnEnter(CPed* JETPACK_ONLY(pPlayerPed))
|
|
{
|
|
#if ENABLE_JETPACK
|
|
//Create the move task.
|
|
CTaskMovePlayer* pMoveTask = rage_new CTaskMovePlayer();
|
|
|
|
taskAssert(pPlayerPed->GetJetpack()->GetObject());
|
|
|
|
//Set the max move/blend ratio.
|
|
pMoveTask->SetMaxMoveBlendRatio(MOVEBLENDRATIO_WALK);
|
|
|
|
//Define the parameters. TO DO - change to jetpack anims?
|
|
fwMvClipSetId s_ClipSetId(sm_Tunables.m_JetpackData.m_ClipSetHash);
|
|
fwMvClipId s_ClipIdForPed(sm_Tunables.m_JetpackData.m_PedClipHash);
|
|
fwMvClipId s_ClipIdForProp(sm_Tunables.m_JetpackData.m_PropClipHash);
|
|
|
|
atHashWithStringNotFinal jetPackProp = CTaskJetpack::sm_Tunables.m_JetpackModelData.m_JetpackModelName;
|
|
|
|
//Create the sub-task.
|
|
CTaskTakeOffPedVariation* pSubTask = rage_new CTaskTakeOffPedVariation(s_ClipSetId, s_ClipIdForPed, s_ClipIdForProp,
|
|
PV_COMP_INVALID, 0, 0, 0, jetPackProp, BONETAG_INVALID, static_cast<CObject*>(pPlayerPed->GetJetpack()->GetObject()), true);
|
|
|
|
//Set the blend out parameters.
|
|
pSubTask->SetPhaseToBlendOut(sm_Tunables.m_JetpackData.m_PhaseToBlendOut);
|
|
pSubTask->SetBlendOutDelta(sm_Tunables.m_JetpackData.m_BlendOutDelta);
|
|
|
|
//Set the force.
|
|
pSubTask->SetForceToApplyAfterInterrupt(sm_Tunables.m_JetpackData.m_ForceToApplyAfterInterrupt);
|
|
|
|
//Set the velocity inheritance.
|
|
if(sm_Tunables.m_JetpackData.m_VelocityInheritance.m_Enabled)
|
|
{
|
|
pSubTask->SetVelocityInheritance(Vec3V(sm_Tunables.m_JetpackData.m_VelocityInheritance.m_X,
|
|
sm_Tunables.m_JetpackData.m_VelocityInheritance.m_Y,
|
|
sm_Tunables.m_JetpackData.m_VelocityInheritance.m_Z));
|
|
}
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskComplexControlMovement(pMoveTask, pSubTask, CTaskComplexControlMovement::TerminateOnSubtask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
#endif
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::TakeOffJetpack_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
//Check if we should slope scramble.
|
|
else if(CheckShouldSlopeScramble())
|
|
{
|
|
SetState(STATE_SLOPE_SCRAMBLE);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TakeOffJetpack_OnExit(CPed *JETPACK_ONLY(pPlayerPed))
|
|
{
|
|
#if ENABLE_JETPACK
|
|
pPlayerPed->DropJetpackObject();
|
|
#endif
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::TakeOffScubaGear_OnEnter(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
//Clear the scuba mask prop.
|
|
CTaskMotionSwimming::ClearScubaMaskPropForPed(*GetPed());
|
|
|
|
//Create the move task.
|
|
CTaskMovePlayer* pMoveTask = rage_new CTaskMovePlayer();
|
|
|
|
//Set the max move/blend ratio.
|
|
pMoveTask->SetMaxMoveBlendRatio(MOVEBLENDRATIO_WALK);
|
|
|
|
//Get the scuba gear variation.
|
|
ePedVarComp nVariationComponent = PV_COMP_INVALID;
|
|
u8 uVariationDrawableId = 0;
|
|
u8 uVariationDrawableAltId = 0;
|
|
u8 uVariationTexId = 0;
|
|
CTaskMotionSwimming::GetScubaGearVariationForPed(*GetPed(), nVariationComponent, uVariationDrawableId, uVariationDrawableAltId, uVariationTexId);
|
|
|
|
//Define the parameters.
|
|
static fwMvClipSetId s_ClipSetId("swimming@scuba@tank",0x1AC58541);
|
|
static fwMvClipId s_ClipIdForPed("scuba_tank_off",0x47BD6C2E);
|
|
static fwMvClipId s_ClipIdForProp("scuba_tank_off_tank",0xA14A0E53);
|
|
static eAnimBoneTag s_nAttachBone = BONETAG_SPINE3;
|
|
static dev_float s_fForceToApplyAfterInterrupt = 300.0f;
|
|
|
|
atHashWithStringNotFinal hProp = CTaskMotionSwimming::GetModelForScubaGear(*GetPed());
|
|
|
|
//Create the sub-task.
|
|
CTaskTakeOffPedVariation* pSubTask = rage_new CTaskTakeOffPedVariation(s_ClipSetId, s_ClipIdForPed, s_ClipIdForProp,
|
|
nVariationComponent, uVariationDrawableId, uVariationDrawableAltId, uVariationTexId, hProp, s_nAttachBone);
|
|
|
|
//Set the attach parameters.
|
|
Vec3V vAttachOffset(sm_Tunables.m_ScubaGear.m_AttachOffsetX,
|
|
sm_Tunables.m_ScubaGear.m_AttachOffsetY, sm_Tunables.m_ScubaGear.m_AttachOffsetZ);
|
|
pSubTask->SetAttachOffset(vAttachOffset);
|
|
Vec3V vAttachOrientation(sm_Tunables.m_ScubaGear.m_AttachOrientationX * DtoR,
|
|
sm_Tunables.m_ScubaGear.m_AttachOrientationY * DtoR, sm_Tunables.m_ScubaGear.m_AttachOrientationZ * DtoR);
|
|
QuatV qAttachOrientation = QuatVFromEulersXYZ(vAttachOrientation);
|
|
pSubTask->SetAttachOrientation(qAttachOrientation);
|
|
|
|
//Set the blend out parameters.
|
|
pSubTask->SetPhaseToBlendOut(sm_Tunables.m_ScubaGear.m_PhaseToBlendOut);
|
|
pSubTask->SetBlendOutDelta(sm_Tunables.m_ScubaGear.m_BlendOutDelta);
|
|
|
|
//Set the force.
|
|
pSubTask->SetForceToApplyAfterInterrupt(s_fForceToApplyAfterInterrupt);
|
|
|
|
//Set the velocity inheritance.
|
|
if(sm_Tunables.m_ScubaGear.m_VelocityInheritance.m_Enabled)
|
|
{
|
|
pSubTask->SetVelocityInheritance(Vec3V(sm_Tunables.m_ScubaGear.m_VelocityInheritance.m_X,
|
|
sm_Tunables.m_ScubaGear.m_VelocityInheritance.m_Y,
|
|
sm_Tunables.m_ScubaGear.m_VelocityInheritance.m_Z));
|
|
}
|
|
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskComplexControlMovement(pMoveTask, pSubTask, CTaskComplexControlMovement::TerminateOnSubtask);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::TakeOffScubaGear_OnUpdate(CPed* UNUSED_PARAM(pPlayerPed))
|
|
{
|
|
//Check if the sub-task has finished.
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
//Check if we should slope scramble.
|
|
else if(CheckShouldSlopeScramble())
|
|
{
|
|
SetState(STATE_SLOPE_SCRAMBLE);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::RideTrain_OnEnter()
|
|
{
|
|
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::RideTrain_OnUpdate()
|
|
{
|
|
//Check if we should continue riding the train.
|
|
if(CheckShouldRideTrain())
|
|
{
|
|
//Disable first person camera while on the tram
|
|
if (GetPed()->IsLocalPlayer())
|
|
{
|
|
camInterface::GetGameplayDirector().DisableFirstPersonThisUpdate(GetPed());
|
|
}
|
|
|
|
//Block weapon switching.
|
|
GetPed()->SetPedResetFlag(CPED_RESET_FLAG_TemporarilyBlockWeaponSwitching, true);
|
|
|
|
//Check if we are not using a scenario, and the cinematic camera is active.
|
|
if(!GetSubTask() /*&& camInterface::GetCinematicDirector().IsRendering()*/)
|
|
{
|
|
//Create the options.
|
|
CScenarioFinder::FindOptions opts;
|
|
opts.m_pRequiredAttachEntity = GetPed()->GetPlayerInfo()->GetTrainPedIsInside();
|
|
|
|
//Find a scenario attached to the train.
|
|
int iRealScenarioPointType = -1;
|
|
CScenarioPoint* pScenarioPoint = CScenarioFinder::FindNewScenario(GetPed(), VEC3V_TO_VECTOR3(GetPed()->GetTransform().GetPosition()),
|
|
50.0f, opts, iRealScenarioPointType);
|
|
if(pScenarioPoint)
|
|
{
|
|
//Create the task.
|
|
CTask* pTask = rage_new CTaskUseScenario(iRealScenarioPointType, pScenarioPoint,
|
|
CTaskUseScenario::SF_Warp | CTaskUseScenario::SF_SkipEnterClip | CTaskUseScenario::SF_IdleForever);
|
|
|
|
//Start the task.
|
|
SetNewTask(pTask);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Check if we are using a scenario.
|
|
if(GetSubTask() && !GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
//Exit the scenario.
|
|
GetSubTask()->MakeAbortable(ABORT_PRIORITY_URGENT, NULL);
|
|
}
|
|
else
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::Jetpack_OnEnter()
|
|
{
|
|
#if ENABLE_JETPACK
|
|
SetNewTask(rage_new CTaskJetpack());
|
|
#endif
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::Jetpack_OnUpdate()
|
|
{
|
|
if(GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::BirdProjectile_OnEnter()
|
|
{
|
|
// Note that we have tried to fire a projectile.
|
|
m_fTimeSinceBirdProjectile = 0.0f;
|
|
|
|
// Reset the flags for spawning a bird projectile.
|
|
m_bHasAttemptedToSpawnBirdProjectile = false;
|
|
m_bNeedToSpawnBirdProjectile = false;
|
|
|
|
CPed* pPed = GetPed();
|
|
|
|
// Look up the bird crap weapon.
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(sm_Tunables.m_BirdWeaponHash);
|
|
|
|
if (pWeaponInfo && pPed->GetInventory() && pPed->GetWeaponManager())
|
|
{
|
|
// Give the bird one projectile.
|
|
pPed->GetInventory()->AddWeaponAndAmmo(pWeaponInfo->GetHash(), 1);
|
|
|
|
// Equip it.
|
|
pPed->GetWeaponManager()->EquipWeapon(pWeaponInfo->GetHash());
|
|
}
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerOnFoot::BirdProjectile_OnUpdate()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
// We want the velocity of the bird to be taken into consideration when the projectile gets its initial velocity.
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_IncludePedReferenceVelocityWhenFiringProjectiles, true);
|
|
|
|
// The projectile is created in PostPreRender, so we must always run that logic.
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_ProcessPostPreRender, true );
|
|
|
|
if (m_bHasAttemptedToSpawnBirdProjectile)
|
|
{
|
|
// This will be set by the spawning of the bird projectile in ProcessPreRender.
|
|
// Go back to regular movement.
|
|
SetState(STATE_PLAYER_IDLES);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// Look up the bird crap weapon.
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(sm_Tunables.m_BirdWeaponHash);
|
|
|
|
if (Verifyf(pWeaponInfo, "Expected a valid weapon info for bird crapping."))
|
|
{
|
|
if (pPed->GetEquippedWeaponInfo() == pWeaponInfo)
|
|
{
|
|
// Make sure the projectile model is streamed in.
|
|
if (pPed->GetInventory() && pPed->GetInventory()->GetIsStreamedIn())
|
|
{
|
|
// Crap!
|
|
m_bNeedToSpawnBirdProjectile = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Something happened to the equipped weapon, bail out.
|
|
SetState(STATE_PLAYER_IDLES);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::BirdProjectile_OnExit()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
// Look up the bird crap weapon.
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(sm_Tunables.m_BirdWeaponHash);
|
|
Assert(pWeaponInfo);
|
|
|
|
if (pWeaponInfo && pPed->GetInventory() && pPed->GetWeaponManager())
|
|
{
|
|
// Remove the bird crap weapon if it exists from the ped's inventory.
|
|
pPed->GetInventory()->RemoveWeaponAndAmmo(pWeaponInfo->GetHash());
|
|
|
|
// Force the weapon back to unarmed so we don't go through the swap weapon state.
|
|
pPed->GetWeaponManager()->EquipWeapon(pPed->GetDefaultUnarmedWeaponHash());
|
|
}
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::SetScriptedTask(aiTask* pScriptedTask)
|
|
{
|
|
m_pScriptedTask = pScriptedTask;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::ClearScriptedTask()
|
|
{
|
|
if(m_pScriptedTask)
|
|
{
|
|
delete m_pScriptedTask; m_pScriptedTask = NULL;
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckForDuckAndCover(CPed* pPlayerPed)
|
|
{
|
|
|
|
if( CTaskPlayerOnFoot::ms_bEnableDuckAndCover )
|
|
{
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl && pControl->GetPedDuckAndCover().IsPressed())
|
|
{
|
|
// See if there are any recent explosions (or similar events to duck and cover from)
|
|
// that we can perceive
|
|
|
|
CEventGroupGlobal* list = GetEventGlobalGroup();
|
|
const int num = list->GetNumEvents();
|
|
|
|
for(int i=0; i<num; i++ )
|
|
{
|
|
fwEvent* ev = list->GetEventByIndex(i);
|
|
if(!ev || !static_cast<const CEvent*>(ev)->IsShockingEvent())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// First, check if the event can ever trigger duck-and-cover reactions.
|
|
CEventShocking* pEvent = static_cast<CEventShocking*>(ev);
|
|
const CEventShocking::Tunables& tunables = pEvent->GetTunables();
|
|
if(tunables.m_DuckAndCoverCanTriggerForPlayerTime > 0.0f)
|
|
{
|
|
// Check if the event was sensed.
|
|
if(pEvent->CanBeSensedBy(*pPlayerPed, 1.0f, true))
|
|
{
|
|
// Check the time.
|
|
u32 timeSinceRegistration = fwTimer::GetTimeInMilliseconds() - pEvent->GetStartTime();
|
|
if(timeSinceRegistration < (int)(tunables.m_DuckAndCoverCanTriggerForPlayerTime*1000.0f))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
s32 CTaskPlayerOnFoot::GetDesiredStateFromPlayerInput(CPed* pPlayerPed)
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
//--------------------------------------------------------------------
|
|
// Switch helmet visor
|
|
//--------------------------------------------------------------------
|
|
if (CTaskMotionInAutomobile::CheckForHelmetVisorSwitch(*pPlayerPed))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_IsSwitchingHelmetVisor,true);
|
|
return STATE_TAKE_OFF_HELMET;
|
|
}
|
|
|
|
if(!pPlayerPed->GetIsSwimming() &&
|
|
pPlayerPed->GetIsStanding() && !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir))
|
|
{
|
|
//--------------------------------------------------------------------------
|
|
// TAKE OFF PARACHUTE PACK
|
|
//--------------------------------------------------------------------------
|
|
|
|
//Check if the parachute pack variation is active.
|
|
if(CTaskParachute::IsParachutePackVariationActiveForPed(*pPlayerPed) &&
|
|
!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableTakeOffParachutePack) &&
|
|
(GetTimeInState() > 0.1f))
|
|
{
|
|
return STATE_TAKE_OFF_PARACHUTE_PACK;
|
|
}
|
|
|
|
#if ENABLE_JETPACK
|
|
//--------------------------------------------------------------------------
|
|
// TAKE OFF JETPACK
|
|
//--------------------------------------------------------------------------
|
|
if(pPlayerPed->GetHasJetpackEquipped() && HasValidEnterVehicleInput(*pControl) && (GetTimeInState() > 0.1f))
|
|
{
|
|
return STATE_TAKE_OFF_JETPACK;
|
|
}
|
|
#endif //ENABLE_JETPACK
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// TAKE OFF SCUBA GEAR
|
|
//--------------------------------------------------------------------------
|
|
|
|
//Check if the scuba gear variation is active.
|
|
if(CTaskMotionSwimming::IsScubaGearVariationActiveForPed(*pPlayerPed) && !pPlayerPed->GetIsSwimming() &&
|
|
pPlayerPed->GetIsStanding() && !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir) &&
|
|
!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableTakeOffScubaGear) &&
|
|
!pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisableTakeOffScubaGear) &&
|
|
(GetTimeInState() > 0.1f) && !pPlayerPed->GetGroundPhysical())
|
|
{
|
|
return STATE_TAKE_OFF_SCUBA_GEAR;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// SWITCH WEAPON
|
|
//--------------------------------------------------------------------------
|
|
const bool bEnterPressed = HasValidEnterVehicleInput(*pControl) && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_ExitVehicleTaskFinishedThisFrame);
|
|
|
|
const bool bUseMobile = CTaskMobilePhone::IsRunningMobilePhoneTask(*pPlayerPed);
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_KeepWeaponHolsteredUnlessFired))
|
|
{
|
|
const bool bShouldUnholsterWeapon =
|
|
pControl->GetMeleeAttackLight().IsPressed() ||
|
|
pPlayerPed->GetPlayerInfo()->IsAiming() ||
|
|
pPlayerPed->GetPlayerInfo()->IsFiring();
|
|
if(bShouldUnholsterWeapon)
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_KeepWeaponHolsteredUnlessFired, false);
|
|
}
|
|
}
|
|
|
|
CPedWeaponManager *pWeapMgr = pPlayerPed->GetWeaponManager();
|
|
bool bNeedsWeaponSwitch;
|
|
if ( pWeapMgr && !bEnterPressed )
|
|
{
|
|
bNeedsWeaponSwitch = pWeapMgr->GetRequiresWeaponSwitch();
|
|
}
|
|
else
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_ExitVehicleTaskFinishedThisFrame) && pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAnimUpdate))
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
|
|
//! Defer weapon swap until next frame, or we get tag syncing issues.
|
|
if(GetPreviousState() == STATE_JUMP && GetTimeInState() == 0.0f)
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
|
|
// Harpoon: Don't swap weapons if we're in the SwimIdleIntro state
|
|
if (pPlayerPed->GetIsSwimming())
|
|
{
|
|
const CTaskMotionBase* pMotionTask = pPlayerPed->GetCurrentMotionTask(false);
|
|
if(pMotionTask && pMotionTask->GetTaskType() == CTaskTypes::TASK_MOTION_AIMING && pMotionTask->GetState() == CTaskMotionAiming::State_SwimIdleIntro)
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const bool bInFpsMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
if(GetState() == STATE_PLAYER_GUN)
|
|
{
|
|
bool bFPSThrownWeapon = false;
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode && pWeapMgr->GetEquippedWeaponInfo() && pWeapMgr->GetEquippedWeaponInfo()->GetIsThrownWeapon())
|
|
{
|
|
bFPSThrownWeapon = true;
|
|
const CTask *pProjectileTask = pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AIM_AND_THROW_PROJECTILE);
|
|
if(pProjectileTask && (pProjectileTask->GetState() == CTaskAimAndThrowProjectile::State_ThrowProjectile || pProjectileTask->GetState() == CTaskAimAndThrowProjectile::State_PlaceProjectile))
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
}
|
|
#endif
|
|
if(!bFPSThrownWeapon)
|
|
{
|
|
bNeedsWeaponSwitch = false;
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// Not swimming, not when switching to or from unarmed
|
|
const bool bShouldReloadAsPartOfGunTask = bInFpsMode && bNeedsWeaponSwitch && !pPlayerPed->GetIsSwimming() &&
|
|
pWeapMgr && pWeapMgr->GetEquippedWeaponHash() != pPlayerPed->GetDefaultUnarmedWeaponHash() && pWeapMgr->GetEquippedWeaponObjectHash() != 0 &&
|
|
pWeapMgr->GetEquippedWeaponInfo() && !pWeapMgr->GetEquippedWeaponInfo()->GetIsProjectile();
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
if (!bUseMobile && pWeapMgr
|
|
#if ENABLE_DRUNK
|
|
&& !pPlayerPed->IsDrunk()
|
|
#endif // ENABLE_DRUNK
|
|
)
|
|
{
|
|
if (bNeedsWeaponSwitch && !CControlMgr::IsDisabledControl(pPlayerPed->GetControlFromPlayer()))
|
|
{
|
|
const CWeaponInfo* pInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(pWeapMgr->GetEquippedWeaponHash());
|
|
if(pInfo)
|
|
{
|
|
if (!pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_SwimmingTasksRunning) || pInfo->GetUsableUnderwater())
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
if(!bShouldReloadAsPartOfGunTask)
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
// Ensure weapon is streamed in
|
|
if(pPlayerPed->GetInventory()->GetIsStreamedIn(pInfo->GetHash()))
|
|
{
|
|
if(pInfo->GetIsCarriedInHand() || pWeapMgr->GetEquippedWeaponObject() != NULL)
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::GetDesiredStateFromPlayerInput: STATE_SWAP_WEAPON - ln 5665. Current State: %d, Previous State: %d", GetState(), GetPreviousState());
|
|
return STATE_SWAP_WEAPON;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// MELEE RANDOM_AMBIENT MOVES
|
|
// This is always handled.
|
|
//
|
|
// Note that SHOOTING is after this since we may wish to do a
|
|
// rifle butts, pistol whips, or executions instead of a normal weapon fire.
|
|
//
|
|
// We use the action manager to test the current conditions (some of which
|
|
// may use weapons or a complex input state such as a button being released
|
|
// and not just pressed down) to check to see if there is a request to do a
|
|
// rifle butt, pistol whip, execution, a running attack, or a stun attack
|
|
// (like a sucker punch or garot move), etc.
|
|
//
|
|
// Currently disabled for swimming.
|
|
//-------------------------------------------------------------------------
|
|
if(pWeapMgr && !pPlayerPed->GetIsSkiing() && !pPlayerPed->GetIsMeleeDisabled() &&
|
|
#if ENABLE_DRUNK
|
|
!pPlayerPed->IsDrunk() &&
|
|
#endif // ENABLE_DRUNK
|
|
!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_InAirDefenceSphere))
|
|
{
|
|
//! Don't allow melee if we don't have the correct weapon equipped yet.
|
|
const CWeapon* pEquippedWeapon = pPlayerPed->GetWeaponManager() ? pPlayerPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
|
|
u32 uEquippedWeaponHash = pEquippedWeapon ? pEquippedWeapon->GetWeaponHash() : -1;
|
|
u32 uSelectedWeaponHash = pPlayerPed->GetWeaponManager() ? pPlayerPed->GetWeaponManager()->GetSelectedWeaponHash() : -1;
|
|
if (uSelectedWeaponHash == uEquippedWeaponHash)
|
|
{
|
|
bool bGoIntoMeleeEvenWhenNoSpecificMoveFound = false;
|
|
bool bAllowStrafeMode = false;
|
|
|
|
// Get the weapon (non melee weapons are handled in CTaskPlayerOnFoot::CheckForArmedMeleeAction)
|
|
const CWeaponInfo* pWeaponInfo = pWeapMgr->GetEquippedWeaponInfo();
|
|
if( pWeaponInfo && pWeaponInfo->GetIsMelee() && pWeaponInfo->GetAllowCloseQuarterKills() )
|
|
{
|
|
// See if we already have or can find a target.
|
|
CEntity* pTargetEntity = pPlayerPed->GetPlayerInfo()->GetTargeting().GetLockOnTarget();
|
|
bool bHasLockOnTarget = pTargetEntity != NULL;
|
|
|
|
// Make sure the target is a ped and not in a vehicle and is holding a melee weapon
|
|
CPed* pTargetPed = NULL;
|
|
if( pTargetEntity && pTargetEntity->GetIsTypePed() )
|
|
{
|
|
pTargetPed = static_cast<CPed*>(pTargetEntity);
|
|
|
|
// NULL out the target ped as they are in the process of being killed
|
|
if( pTargetPed->GetPedConfigFlag( CPED_CONFIG_FLAG_Knockedout ) ||
|
|
pTargetPed->GetPedConfigFlag( CPED_CONFIG_FLAG_KilledByStealth ) ||
|
|
pTargetPed->GetPedConfigFlag( CPED_CONFIG_FLAG_KilledByTakedown ) )
|
|
{
|
|
pTargetPed = NULL;
|
|
}
|
|
}
|
|
|
|
bool bTargetIsFriendlyPed = pTargetPed && pTargetPed->GetPedConfigFlag(CPED_CONFIG_FLAG_AllowPlayerLockOnIfFriendly);
|
|
bool bDontRaiseFistsWhenLockedOn = pTargetPed ? pTargetPed->GetPedResetFlag( CPED_RESET_FLAG_DontRaiseFistsWhenLockedOn ) : false;
|
|
|
|
// Allow targeting players on bikes with melee weapons when in a multiplayer game
|
|
bool bAllowMeleeTargetingInVehicle = NetworkInterface::IsGameInProgress() && pTargetPed && pTargetPed->IsPlayer() && pTargetPed->GetVehiclePedInside()
|
|
&& (pTargetPed->GetVehiclePedInside()->InheritsFromBike() || pTargetPed->GetVehiclePedInside()->InheritsFromQuadBike() || pTargetPed->GetVehiclePedInside()->InheritsFromAmphibiousQuadBike());
|
|
|
|
if( pTargetPed && (!pTargetPed->GetIsInVehicle() || bAllowMeleeTargetingInVehicle) )
|
|
{
|
|
Vector3 vPlayerToTarget = VEC3V_TO_VECTOR3( pTargetEntity->GetTransform().GetPosition() - pPlayerPed->GetTransform().GetPosition() );
|
|
if( vPlayerToTarget.Mag2() < rage::square( CTaskPlayerOnFoot::ms_fMeleeStartDistance ) &&
|
|
( !pPlayerPed->GetIsCrouching() || pPlayerPed->CanPedStandUp() ) && !pPlayerPed->GetIsFPSSwimming())
|
|
{
|
|
bGoIntoMeleeEvenWhenNoSpecificMoveFound = true;
|
|
|
|
// Check to see if we should go directly into strafe mode
|
|
if( !bDontRaiseFistsWhenLockedOn || ( GetWasMeleeStrafing() && ( GetLastTimeInMeleeTask() + ms_nRestartMeleeDelayInMS ) > fwTimer::GetTimeInMilliseconds() ) )
|
|
bAllowStrafeMode = true;
|
|
}
|
|
}
|
|
|
|
bool bAttemptTaunt = false;
|
|
if( !bTargetIsFriendlyPed && (CTaskMelee::ShouldCheckForMeleeAmbientMove( pPlayerPed, bAttemptTaunt, false ) || bGoIntoMeleeEvenWhenNoSpecificMoveFound) )
|
|
{
|
|
// Initialize the melee intro bool
|
|
bool bPerformMeleeIntroAnim = false;
|
|
if( !bDontRaiseFistsWhenLockedOn && ( ( GetLastTimeInMeleeTask() + ms_nIntroAnimDelayInMS ) < fwTimer::GetTimeInMilliseconds() ) )
|
|
bPerformMeleeIntroAnim = pWeaponInfo ? pWeaponInfo->GetAllowMeleeIntroAnim() : false;
|
|
|
|
//! Note: The target selection will cache the selected action for the target. Just re-use this
|
|
//! CheckForAndGetMeleeAmbientMove() to prevent recalculating it.
|
|
CTaskMelee::ResetLastFoundActionDefinition();
|
|
|
|
// Try to find a suitable ambient target.
|
|
if( !pTargetEntity && pPlayerPed->IsLocalPlayer() )
|
|
{
|
|
CPlayerPedTargeting& rTargeting = pPlayerPed->GetPlayerInfo()->GetTargeting();
|
|
|
|
pTargetEntity = rTargeting.FindMeleeTarget(pPlayerPed, pWeaponInfo, true);
|
|
}
|
|
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed,
|
|
pTargetEntity,
|
|
bHasLockOnTarget,
|
|
bGoIntoMeleeEvenWhenNoSpecificMoveFound,
|
|
bPerformMeleeIntroAnim,
|
|
bAllowStrafeMode,
|
|
pTargetEntity != NULL );
|
|
|
|
// One last ditch effort to find a no target melee action
|
|
if( !m_pMeleeRequestTask )
|
|
{
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed,
|
|
NULL,
|
|
false,
|
|
bGoIntoMeleeEvenWhenNoSpecificMoveFound,
|
|
bPerformMeleeIntroAnim,
|
|
bAllowStrafeMode,
|
|
false );
|
|
}
|
|
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
pPlayerPed->SetIsCrouching( false, -1, false );
|
|
taskDebugf1("STATE_MELEE: CTaskPlayerOnFoot::GetDesiredStateFromPlayerInput: m_pMeleeRequestTask");
|
|
return STATE_MELEE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bSprinting = false;
|
|
const Vector2 &desiredMBR = pPlayerPed->GetMotionData()->GetDesiredMoveBlendRatio();
|
|
if(abs(desiredMBR.x) > 0.01f)
|
|
{
|
|
bSprinting = pPlayerPed->GetMotionData()->GetIsSprinting(desiredMBR.Mag());
|
|
}
|
|
else
|
|
{
|
|
bSprinting = pPlayerPed->GetMotionData()->GetIsSprinting(desiredMBR.y);
|
|
}
|
|
|
|
// Cache off whether or not this is a heavy weapon
|
|
bool bHeavyWeapon = false;
|
|
if( pWeapMgr )
|
|
{
|
|
const CWeapon* pWeaponUsable = pWeapMgr->GetEquippedWeapon();
|
|
const CWeaponInfo* pWeaponInfo = pWeaponUsable ? pWeaponUsable->GetWeaponInfo() : NULL;
|
|
bHeavyWeapon = pWeaponInfo ? pWeaponInfo->GetIsHeavy() : false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// COVER
|
|
//-------------------------------------------------------------------------
|
|
bool bWantsToEnterCover = false;
|
|
if(!bHeavyWeapon && !m_bPlayerExitedCoverThisUpdate && !pPlayerPed->GetIsSwimming() && !pPlayerPed->GetIsSkiing() &&
|
|
#if ENABLE_DRUNK
|
|
!pPlayerPed->IsDrunk() &&
|
|
#endif // ENABLE_DRUNK
|
|
pPlayerPed->GetWeaponManager() && CTaskEnterCover::AreCoreCoverClipsetsLoaded(pPlayerPed))
|
|
{
|
|
if(CheckForNearbyCover(pPlayerPed))
|
|
{
|
|
bWantsToEnterCover = true;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// ENTER VEHICLE
|
|
//-------------------------------------------------------------------------
|
|
|
|
s8 iEnterVehicleState = CheckShouldEnterVehicle(pPlayerPed,pControl, m_bGetInVehicleAfterCombatRolling);
|
|
#if __BANK
|
|
if (iEnterVehicleState == STATE_GET_IN_VEHICLE)
|
|
{
|
|
AI_LOG("[Player] - iEnterVehicleState = STATE_GET_IN_VEHICLE\n");
|
|
}
|
|
#endif // __BANK
|
|
|
|
//-------------------------------------------------------------------------
|
|
// SHOOTING
|
|
//-------------------------------------------------------------------------
|
|
const CWeaponInfo* pWeaponInfo = pPlayerPed->GetEquippedWeaponInfo();
|
|
bool bUsableUnderwater = false;
|
|
if (pWeaponInfo)
|
|
{
|
|
bUsableUnderwater = pWeaponInfo->GetUsableUnderwater();
|
|
}
|
|
if (pWeapMgr && (!pPlayerPed->GetIsSwimming() || bUsableUnderwater) && (!bNeedsWeaponSwitch || bShouldReloadAsPartOfGunTask) &&
|
|
#if ENABLE_DRUNK
|
|
!pPlayerPed->IsDrunk() &&
|
|
#endif // ENABLE_DRUNK
|
|
!CNewHud::IsShowingHUDMenu() && !pPlayerPed->GetPedIntelligence()->FindTaskActiveByTreeAndType(PED_TASK_TREE_SECONDARY, CTaskTypes::TASK_RELOAD_GUN))
|
|
{
|
|
#if 0 // CS
|
|
const bool bCarryingThrowableObject = pPlayerPed->GetWeaponMgr()->GetChosenWeaponType()==WEAPONTYPE_OBJECT &&
|
|
pPlayerPed->GetWeaponMgr()->GetWeaponObject() &&
|
|
pPlayerPed->GetWeaponMgr()->GetWeaponObject()->GetModelIndex() != CPedPhoneComponent::GetPhoneModelIndexSafe(pPlayerPed);
|
|
#else
|
|
const bool bCarryingThrowableObject = false;
|
|
#endif // 0
|
|
const CWeapon* pWeaponUsable = pWeapMgr->GetEquippedWeapon();
|
|
const CWeaponInfo* pWeaponInfo = pWeaponUsable ? pWeaponUsable->GetWeaponInfo() : NULL;
|
|
|
|
bool bIsUsingThrownWeapon = pWeaponInfo && pWeaponInfo->GetIsThrownWeapon();
|
|
bool bIsAWeapon = !pWeaponInfo || !pWeaponInfo->GetIsNotAWeapon();
|
|
|
|
// Should the player reload or has the player hit the input for reloading?
|
|
bool bWantsToReload = false;
|
|
bool bNeedsToReload = false;
|
|
bool bCanReload = false;
|
|
if(pWeaponUsable)
|
|
{
|
|
bCanReload = pWeaponUsable->GetCanReload() && !pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON);
|
|
bWantsToReload = (pControl->GetPedReload().IsPressed() || pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ForceReload)) && bCanReload;
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_RELOAD) && !bWantsToReload)
|
|
{
|
|
CPadGesture* gesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(gesture && gesture->GetHasReloaded())
|
|
{
|
|
bWantsToReload = true;
|
|
}
|
|
}
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
bNeedsToReload = pWeaponUsable->GetNeedsToReload(true) || ( pWeaponInfo->GetCreateVisibleOrdnance() && pPlayerPed->GetWeaponManager() && pPlayerPed->GetWeaponManager()->GetPedEquippedWeapon()->GetProjectileOrdnance() == NULL );
|
|
}
|
|
|
|
bool bFirstPersonAimOrFire = false;
|
|
bool bFPSToggleRunReload = false;
|
|
#if FPS_MODE_SUPPORTED
|
|
bFirstPersonAimOrFire = (pPlayerPed->GetPlayerInfo()->IsAiming() || pPlayerPed->GetPlayerInfo()->IsFiring()) && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
bFPSToggleRunReload = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) && camFirstPersonShooterCamera::WhenSprintingUseRightStick(pPlayerPed, *pControl) && (bWantsToReload || bNeedsToReload) && bCanReload;
|
|
#endif
|
|
bool bKeyboardMouseAimOrFire = false;
|
|
bool bKeyboardMouseReload = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
bKeyboardMouseAimOrFire = pControl->WasKeyboardMouseLastKnownSource() && (pPlayerPed->GetPlayerInfo()->IsAiming() || pPlayerPed->GetPlayerInfo()->IsFiring());
|
|
bKeyboardMouseReload = pControl->WasKeyboardMouseLastKnownSource() && (bWantsToReload || bNeedsToReload) && bCanReload;
|
|
#endif
|
|
bool bCanEnterGunTaskFromSprint = bFirstPersonAimOrFire || bKeyboardMouseAimOrFire || bKeyboardMouseReload || bFPSToggleRunReload || (!bSprinting && pPlayerPed->GetPlayerInfo()->IsSprintAimBreakOutOver()) || bShouldReloadAsPartOfGunTask;
|
|
|
|
// Allow aiming if swimming and sprinting
|
|
if(bIsAWeapon && (bIsUsingThrownWeapon || (bCanEnterGunTaskFromSprint || CPlayerInfo::IsCombatRolling() || pPlayerPed->GetIsSwimming())))
|
|
{
|
|
// Can the player fire or has the player hit the input for firing?
|
|
bool bCanPlayerFire = ( (pWeapMgr->GetIsArmed() && !pWeapMgr->GetIsArmedMelee()) || bCarryingThrowableObject) && !pWeapMgr->GetIsNewEquippableWeaponSelected();
|
|
bool bConsiderAttackTriggerAiming = !bCarryingThrowableObject && !bIsUsingThrownWeapon && !pPlayerPed->GetIsInCover() && !pPlayerPed->GetIsSwimming();
|
|
bool bCanLockonOnFoot = pWeaponUsable ? pWeaponUsable->GetCanLockonOnFoot() : false;
|
|
bool bAimPressed = (pPlayerPed->GetPlayerInfo()->IsAiming(bConsiderAttackTriggerAiming) && (!pWeaponInfo || bCanLockonOnFoot || pWeaponInfo->GetCanFreeAim()));
|
|
bool bFirePressed = pPlayerPed->GetPlayerInfo()->IsFiring();
|
|
bool bCanBeAimedLikeGun = pWeaponInfo && pWeaponInfo->GetCanBeAimedLikeGunWithoutFiring();
|
|
|
|
// Force player to aim when trying to fire while swimming
|
|
bool bMustAimToFire = pPlayerPed->GetIsSwimming() ? bAimPressed : true;
|
|
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_ForcePlayerFiring))
|
|
{
|
|
bFirePressed = true;
|
|
}
|
|
|
|
// B*2592749: Don't allow fire-only/thrown weapons to enter gun tasks if in an air defence sphere (ie jerry can etc).
|
|
if (bCanPlayerFire && pWeaponInfo && (pWeaponInfo->GetOnlyAllowFiring() || pWeaponInfo->GetIsThrownWeapon()) && pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_InAirDefenceSphere))
|
|
{
|
|
bCanPlayerFire = false;
|
|
}
|
|
|
|
// For drop-when-cooked weapons, only go to player gun state when fire trigger is pressed, not held
|
|
if(pWeaponInfo && pWeaponInfo->GetIsThrownWeapon() && pWeaponInfo->GetDropWhenCooked())
|
|
{
|
|
bool bPreviouslySwapping = GetPreviousState() == STATE_SWAP_WEAPON;
|
|
#if FPS_MODE_SUPPORTED
|
|
bPreviouslySwapping |= (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) && pPlayerPed->GetMotionData()->GetWasFPSUnholster());
|
|
#endif
|
|
if(!bPreviouslySwapping)
|
|
{
|
|
bFirePressed = CThrowProjectileHelper::IsPlayerFirePressed(pPlayerPed);
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_StartProjectileTaskWithPrimingDisabled, !bFirePressed);
|
|
}
|
|
}
|
|
|
|
// Reset reload from idle flag
|
|
m_bReloadFromIdle = false;
|
|
|
|
//! If projectile is cooking, but we aren't in projectile throw state, just go back to it, to force it to drop.
|
|
bool bCooking = pWeaponUsable && pWeaponUsable->GetIsCooking();
|
|
|
|
// Start the task if the player wants to reload and can, or if they want to fire and can
|
|
if((bCanPlayerFire && (bAimPressed || bFirePressed || bCooking)) || ((bWantsToReload || bNeedsToReload) && bCanReload) || bShouldReloadAsPartOfGunTask)
|
|
{
|
|
if (!bAimPressed && (bWantsToReload || bNeedsToReload)
|
|
#if FPS_MODE_SUPPORTED
|
|
&& !pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
#else
|
|
)
|
|
#endif
|
|
{
|
|
m_bReloadFromIdle = true;
|
|
}
|
|
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_KeepWeaponHolsteredUnlessFired))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_KeepWeaponHolsteredUnlessFired, false);
|
|
|
|
bNeedsWeaponSwitch = pWeapMgr->GetRequiresWeaponSwitch();
|
|
|
|
if (bNeedsWeaponSwitch)
|
|
{
|
|
const CWeaponInfo* pInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(pWeapMgr->GetEquippedWeaponHash());
|
|
if(pInfo)
|
|
{
|
|
if (!pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_SwimmingTasksRunning) || pInfo->GetUsableUnderwater())
|
|
{
|
|
// Ensure weapon is streamed in
|
|
if(pPlayerPed->GetInventory()->GetIsStreamedIn(pInfo->GetHash()))
|
|
{
|
|
if(pInfo->GetIsCarriedInHand() || pWeapMgr->GetEquippedWeaponObject() != NULL)
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::GetDesiredStateFromPlayerInput: STATE_SWAP_WEAPON - ln 5966. Current State: %d, Previous State: %d", GetState(), GetPreviousState());
|
|
return STATE_SWAP_WEAPON;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//thrown weapons must be swapped 1st
|
|
if (!pWeaponUsable)
|
|
{
|
|
const CWeaponInfo* pEquippedWeaponInfo = pWeapMgr->GetEquippedWeaponInfo();
|
|
if(!pEquippedWeaponInfo || pEquippedWeaponInfo->GetIsThrownWeapon())
|
|
bNeedsWeaponSwitch = true;
|
|
}
|
|
|
|
if(!bNeedsWeaponSwitch || bShouldReloadAsPartOfGunTask)
|
|
{
|
|
const bool bSniperWeapon = pWeaponUsable && pWeaponUsable->GetWeaponInfo()->GetGroup() == WEAPONGROUP_SNIPER;
|
|
bool bActivateSniperScope = bSniperWeapon && (CNewHud::IsShowingHUDMenu());
|
|
|
|
//Allow transition to gun state from diving down (after a min time fTimeBeforeAllowAim to ensure anim transition looks ok)
|
|
bool bIsDivingDown = false;
|
|
CTask *pActiveMotionTask = pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_DIVING);
|
|
if (pActiveMotionTask)
|
|
{
|
|
TUNE_GROUP_FLOAT(DIVING_DOWN_AIMING_TUNE, fTimeBeforeAllowAim, 1.25f, 0.0f, 4.0f, 0.01f);
|
|
CTaskMotionDiving *pDivingTask = static_cast<CTaskMotionDiving*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_DIVING));
|
|
if (pDivingTask && pDivingTask->GetState() == CTaskMotionDiving::State_DivingDown)
|
|
{
|
|
if (pDivingTask->GetTimeInState() < fTimeBeforeAllowAim)
|
|
{
|
|
bIsDivingDown = true;
|
|
}
|
|
else
|
|
{
|
|
pDivingTask->GetParent()->GetMoveNetworkHelper()->SetBoolean(CTaskMotionPed::ms_TransitionClipFinishedId, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Harpoon: Only enter gun state if SwimIdleOutro isn't playing
|
|
bool bIsPlayingSwimIdleOutro = false;
|
|
if (pPlayerPed->GetIsSwimming())
|
|
{
|
|
const CTaskMotionBase* pMotionTask = pPlayerPed->GetCurrentMotionTask(false);
|
|
if(pMotionTask && pMotionTask->GetTaskType() == CTaskTypes::TASK_MOTION_AIMING && pMotionTask->GetState() == CTaskMotionAiming::State_SwimIdleOutro)
|
|
{
|
|
bIsPlayingSwimIdleOutro = true;
|
|
}
|
|
}
|
|
|
|
if(!bActivateSniperScope && !bIsDivingDown && bMustAimToFire && !bIsPlayingSwimIdleOutro && !bWantsToEnterCover && iEnterVehicleState == -1
|
|
#if FPS_MODE_SUPPORTED
|
|
// Don't go in gun state if holding a phone
|
|
&&!(pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer() && CTaskPlayerOnFoot::CheckForUseMobilePhone(*pPlayerPed))
|
|
#endif
|
|
)
|
|
{
|
|
taskDebugf1("CTaskPlayerOnFoot::GetDesiredStateFromPlayerInput: STATE_PLAYER_GUN - !bActivateSniperScope etc. Current State: %d, Previous State: %d", GetState(), GetPreviousState());
|
|
return STATE_PLAYER_GUN;
|
|
}
|
|
}
|
|
}
|
|
// Special case for weapons that can be aimed like a gun but do not fire
|
|
else if( bAimPressed && bCanBeAimedLikeGun )
|
|
{
|
|
return STATE_PLAYER_GUN;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Slope Scramble
|
|
//-------------------------------------------------------------------------
|
|
if( !pPlayerPed->GetIsSwimming() && CTaskSlopeScramble::CanDoSlopeScramble(pPlayerPed) )
|
|
{
|
|
m_fTimeSinceLastValidSlopeScramble += fwTimer::GetTimeStep();
|
|
++m_nFramesOfSlopeScramble;
|
|
float TimeBeforeScramble = CTaskSlopeScramble::GetTimeBeforeScramble(pPlayerPed->GetGroundNormal(), pPlayerPed->GetVelocity(), ZAXIS);
|
|
|
|
//Displayf("SlopeScrambleTimer: %f, TimeBeforeScramble: %f/n", m_fTimeSinceLastValidSlopeScramble, TimeBeforeScramble);
|
|
|
|
TUNE_GROUP_FLOAT (SLOPE_SCRAMBLE, fDisabledTimerGroundZMax, 0.6f, 0.0f, 1.0f, 0.01f);
|
|
TUNE_GROUP_INT (SLOPE_SCRAMBLE, nSlopeScrambleFrames, 4, 0, 1000, 1);
|
|
|
|
// Don't consider time if we are not moving
|
|
if ((m_fTimeSinceLastValidSlopeScramble > TimeBeforeScramble || abs(pPlayerPed->GetGroundNormal().z) > fDisabledTimerGroundZMax) && m_nFramesOfSlopeScramble > nSlopeScrambleFrames )
|
|
{
|
|
if(DoJumpCheck(pPlayerPed, NULL, bHeavyWeapon, true))
|
|
{
|
|
return STATE_JUMP;
|
|
}
|
|
return STATE_SLOPE_SCRAMBLE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we're no longer on a slope, reset.
|
|
m_fTimeSinceLastValidSlopeScramble = 0.0f;
|
|
m_nFramesOfSlopeScramble = 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// DuckAndCover
|
|
//-------------------------------------------------------------------------
|
|
if( !pPlayerPed->GetIsSwimming() && CheckForDuckAndCover(pPlayerPed) )
|
|
{
|
|
return STATE_DUCK_AND_COVER;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// UNCUFF
|
|
//-------------------------------------------------------------------------
|
|
if(CheckShouldPerformUncuff())
|
|
{
|
|
return STATE_UNCUFF;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// SCAN FOR LADDERS, MOUNTS, AND CARS
|
|
// We will normally choose to climb the ladder over entering
|
|
// the car, except where the ladder is behind us.
|
|
//-------------------------------------------------------------------------
|
|
if(iEnterVehicleState != -1)
|
|
{
|
|
return (ePlayerOnFootState)iEnterVehicleState;
|
|
}
|
|
|
|
//******************************************************************
|
|
//Ladder
|
|
if (CheckShouldClimbLadder(pPlayerPed))
|
|
{
|
|
return STATE_CLIMB_LADDER;
|
|
}
|
|
|
|
|
|
// Don't allow player to go into cover whilst using the phone
|
|
if (GetState() == STATE_PLAYER_IDLES)
|
|
{
|
|
if (GetSubTask() && GetSubTask()->GetTaskType() == CTaskTypes::TASK_PLAYER_IDLES)
|
|
{
|
|
if ( CTaskMobilePhone::IsRunningMobilePhoneTask(*pPlayerPed) && !CPhoneMgr::GetGoingOffScreen())
|
|
{
|
|
return STATE_PLAYER_IDLES;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// JETPACK
|
|
//-------------------------------------------------------------------------
|
|
if(CheckForUsingJetpack(pPlayerPed))
|
|
{
|
|
return STATE_JETPACK;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// JUMPING INTO COVER
|
|
//-------------------------------------------------------------------------
|
|
if(bWantsToEnterCover)
|
|
{
|
|
return STATE_USE_COVER;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Jump/Vault.
|
|
//-------------------------------------------------------------------------
|
|
if(DoJumpCheck(pPlayerPed, pControl, bHeavyWeapon))
|
|
return STATE_JUMP;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Drop Downs.
|
|
//-------------------------------------------------------------------------
|
|
if(!bHeavyWeapon && CheckForDropDown(pPlayerPed, pControl))
|
|
{
|
|
if(pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedParachuteDrop() || pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedDive())
|
|
{
|
|
return STATE_JUMP;
|
|
}
|
|
else
|
|
{
|
|
return STATE_DROPDOWN;
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool bUsingMobilePhone = CTaskMobilePhone::IsRunningMobilePhoneTask(*pPlayerPed) || CTaskPlayerOnFoot::CheckForUseMobilePhone(*pPlayerPed);
|
|
if(pWeaponInfo && (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(true, false, true) && (!pPlayerPed->GetIsSwimming() && !bUsingMobilePhone)))
|
|
{
|
|
return STATE_PLAYER_GUN;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
//--------------------------------------------------------------------
|
|
// Bird crapping
|
|
//--------------------------------------------------------------------
|
|
if (CheckForBirdProjectile(pPlayerPed))
|
|
{
|
|
return STATE_BIRD_PROJECTILE;
|
|
}
|
|
else
|
|
{
|
|
m_fTimeSinceBirdProjectile += GetTimeStep();
|
|
}
|
|
|
|
return GetState();
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::GetLastStickInputIfValid(Vector2& vStickInput) const
|
|
{
|
|
if (m_fTimeSinceLastStickInput < TIME_TO_CONSIDER_OLD_INPUT_VALID)
|
|
{
|
|
vStickInput = Vector2(m_vLastStickInput.x, m_vLastStickInput.y);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::UpdatePlayerLockon(CPed* pPlayerPed)
|
|
{
|
|
// If the player is entering the car or jumping, disable lock on
|
|
if( pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_JUMPVAULT ) ||
|
|
pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_CLIMB_LADDER ) ||
|
|
pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_ENTER_VEHICLE ) ||
|
|
pPlayerPed->GetUsingRagdoll() )
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerLockon, true );
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::IsValidForMotionTask(CTaskMotionBase& task) const
|
|
{
|
|
bool isValid = false;
|
|
|
|
if (task.IsOnFoot() || task.IsInWater())
|
|
isValid = true;
|
|
|
|
if(!isValid && GetSubTask())
|
|
{
|
|
//This task is not valid, but an active subtask might be.
|
|
isValid = GetSubTask()->IsValidForMotionTask(task);
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
#if !__FINAL
|
|
|
|
void CTaskPlayerOnFoot::Debug() const
|
|
{
|
|
if(GetSubTask())
|
|
{
|
|
GetSubTask()->Debug();
|
|
}
|
|
|
|
const CPed* pPed = GetPed();
|
|
#if __BANK
|
|
if (pPed->IsLocalPlayer())
|
|
{
|
|
pPed->GetPlayerInfo()->GetVehicleClipRequestHelper().Debug();
|
|
}
|
|
#endif
|
|
const CControl *pControl = pPed->GetControlFromPlayer();
|
|
bool bPlayerWantsToEnterCover = pControl->GetPedCover().IsPressed();//pControl->GetPedEnter().HistoryPressed(iTimePeriod, &iLastTimeTransitioned) && pControl->GetPedEnter().HistoryReleased(fwTimer::GetTimeInMilliseconds() - iLastTimeTransitioned);
|
|
|
|
#if __BANK
|
|
Color32 iColor = ((fwTimer::GetTimeInMilliseconds() < pPed->GetPlayerInfo()->GetTimeLastWeaponPickedUp()+ms_iQuickSwitchWeaponPickUpTime)) ? Color_green : Color_red;
|
|
grcDebugDraw::AddDebugOutput(iColor, "Last Weapon Hash : %u, Time Since Last Weapon Pickup: %u, Current Time %u", pPed->GetPlayerInfo()->GetLastWeaponHashPickedUp(), pPed->GetPlayerInfo()->GetTimeLastWeaponPickedUp(), fwTimer::GetTimeInMilliseconds());
|
|
#endif
|
|
|
|
// If the cover search isn't being rendered, don't search if the button isn't pressed
|
|
#if __DEV
|
|
if( !CCoverDebug::ms_Tunables.m_RenderPlayerCoverSearch )
|
|
#endif
|
|
{
|
|
if( !bPlayerWantsToEnterCover )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if __BANK
|
|
void CTaskPlayerOnFoot::DebugVehicleGetIns()
|
|
{
|
|
#if DEBUG_DRAW
|
|
CEntity* pEnt = CDebugScene::FocusEntities_Get(0);
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,debugVehicleGetOnClips, false);
|
|
if(debugVehicleGetOnClips && pEnt && pEnt->GetIsTypeVehicle() )
|
|
{
|
|
CVehicle* pTrailer = static_cast<CVehicle*>(pEnt);
|
|
|
|
const s32 iNumberEntryExitPoints = pTrailer->GetNumberEntryExitPoints();
|
|
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,debugSpecificEntryPoint, false);
|
|
TUNE_GROUP_INT(VEHICLE_GET_ONS,specificEntryPoint, 0, 0, 10, 1);
|
|
|
|
// Go through entry exit point and see if it can
|
|
for( s32 i = 0; i < iNumberEntryExitPoints; i++ )
|
|
{
|
|
SeatAccess seatAccess = SA_directAccessSeat;
|
|
const CEntryExitPoint* pEntryPoint = pTrailer->GetEntryExitPoint(i);
|
|
Vector3 vOpenDoorPos;
|
|
{
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,renderMyStuff, true);
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,renderText, true);
|
|
|
|
Matrix34 carMat;
|
|
pTrailer->GetMatrixCopy(carMat);
|
|
if (renderMyStuff)
|
|
grcDebugDraw::Sphere(carMat.d, 0.025f, Color_white);
|
|
|
|
Matrix34 localSeatMatrix;
|
|
localSeatMatrix.Identity();
|
|
|
|
s32 iSeatIndex = pEntryPoint->GetSeat(seatAccess);
|
|
|
|
CVehicleModelInfo* pModeInfo = pTrailer->GetVehicleModelInfo();
|
|
int iBoneIndex = pModeInfo->GetModelSeatInfo()->GetBoneIndexFromSeat(iSeatIndex);
|
|
Assertf(iBoneIndex!=-1, "%s:GetEnterCarMatrix - Bone does not exist", pModeInfo->GetModelName());
|
|
if(iBoneIndex!=-1)
|
|
{
|
|
localSeatMatrix = pTrailer->GetLocalMtx(iBoneIndex);
|
|
Assertf(pTrailer->GetSkeletonData().GetParentIndex(iBoneIndex) == 0,"%s GetEnterCarMatrix expects the seat to be a child of the root",pModeInfo->GetModelName());
|
|
}
|
|
Matrix34 matResult;
|
|
matResult.Identity();
|
|
matResult.Dot(localSeatMatrix, carMat);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,arrowlength, 0.5f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,arrowsize, 0.1f, -10.0f, 10.0f, 0.01f);
|
|
if (renderMyStuff)
|
|
{
|
|
if (!debugSpecificEntryPoint || (debugSpecificEntryPoint && i == specificEntryPoint))
|
|
{
|
|
grcDebugDraw::Line(carMat.d, matResult.d, Color_white, Color_blue);
|
|
grcDebugDraw::Sphere(matResult.d, 0.05f, Color_blue);
|
|
Vector3 vSeatDir = matResult.b;
|
|
vSeatDir.Normalize();
|
|
grcDebugDraw::Arrow(RCC_VEC3V(matResult.d), VECTOR3_TO_VEC3V(matResult.d + vSeatDir * arrowlength), arrowsize, Color_blue);
|
|
|
|
if (renderText)
|
|
{
|
|
char szText[64];
|
|
formatf(szText, "Direct Access Seat For Entry Point %i", i);
|
|
grcDebugDraw::Text(matResult.d, Color_blue, szText);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 vResult;
|
|
//s32 iEntryPointIndex = pTrailer->GetEntryExitPointIndex(pEntryPoint);
|
|
//pTrailer->GetOpenDoorOffset(vResult, iEntryPointIndex , pPlayerPed);
|
|
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,xe, 7.59f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,ye, 0.91f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,ze, 2.50f, -10.0f, 10.0f, 0.01f);
|
|
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,xs, 7.59f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,ys, 2.43f, -10.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VEHICLE_GET_ONS,zs, 1.0f, -10.0f, 10.0f, 0.01f);
|
|
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,useForcedOffset, false);
|
|
Vector3 vStart;
|
|
Vector3 vEnd;
|
|
|
|
// Get the local translation of get on from start to end
|
|
if (useForcedOffset)
|
|
{
|
|
vStart = Vector3(xs,ys,zs);
|
|
vEnd = Vector3(xe,ye,ze);
|
|
|
|
vResult = vEnd - vStart;
|
|
vResult *= -1.0f;
|
|
}
|
|
// else // Does the same as get opendoor offset, rewritten here so we can debug
|
|
// {
|
|
// const CVehicleEntryPointAnimInfo* pClipInfo = pTrailer->GetEntryAnimInfo(iEntryPointIndex);
|
|
// if(pClipInfo)
|
|
// {
|
|
// fwClipGroupId nClipGroupId = CLIP_SET_ID_INVALID;
|
|
// fwMvClipId nClipId = CLIP_ID_INVALID;
|
|
//
|
|
// if(pClipInfo->FindClip(VehicleEntryExitClip::GET_IN,nClipGroupId,nClipId))
|
|
// {
|
|
// vStart = CClipBlender::GetMoverTrackTranslation(0.0f,nClipGroupId,nClipId);
|
|
// vEnd = CClipBlender::GetMoverTrackTranslation(1.0f,nClipGroupId,nClipId);
|
|
//
|
|
// vResult = vEnd - vStart;
|
|
// vResult *= -1.0f;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// Make relative to seat orientation
|
|
vResult.RotateZ(rage::Atan2f(-localSeatMatrix.b.x, localSeatMatrix.b.y));
|
|
localSeatMatrix.d += vResult;
|
|
|
|
Matrix34 matResult2;
|
|
matResult2.Identity();
|
|
// Transform the entry position into world space
|
|
matResult2.Dot(localSeatMatrix, carMat);
|
|
|
|
if (renderMyStuff)
|
|
{
|
|
if (!debugSpecificEntryPoint || (debugSpecificEntryPoint && i == specificEntryPoint))
|
|
{
|
|
grcDebugDraw::Line(matResult.d, matResult2.d, Color_blue, Color_green);
|
|
grcDebugDraw::Sphere(matResult2.d, 0.025f, Color_green);
|
|
char szText[64];
|
|
formatf(szText, "Entry Point Position For Entry Point %i", i);
|
|
grcDebugDraw::Text(matResult2.d, Color_green, szText);
|
|
|
|
if (renderText)
|
|
{
|
|
if (useForcedOffset)
|
|
{
|
|
formatf(szText, "Forced Mover Start Translation, (%.2f,%.2f,%.2f)", vStart.x, vStart.y, vStart.z);
|
|
grcDebugDraw::Text(matResult2.d, Color_green, 0, 10, szText);
|
|
formatf(szText, "Forced Mover End Translation, (%.2f,%.2f,%.2f)", vEnd.x, vEnd.y, vEnd.z);
|
|
grcDebugDraw::Text(matResult.d, Color_blue, 0, 10, szText);
|
|
}
|
|
else
|
|
{
|
|
formatf(szText, "Clip Mover Start Translation, (%.2f,%.2f,%.2f)", vStart.x, vStart.y, vStart.z);
|
|
grcDebugDraw::Text(matResult2.d, Color_green, 0, 10, szText);
|
|
formatf(szText, "Clip Mover End Translation, (%.2f,%.2f,%.2f)", vEnd.x, vEnd.y, vEnd.z);
|
|
grcDebugDraw::Text(matResult.d, Color_blue, 0, 10, szText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(VEHICLE_GET_ONS,renderEnterCarMatrixStuff, false);
|
|
if(renderEnterCarMatrixStuff && pEntryPoint->GetEntryPointPosition(pTrailer, NULL, vOpenDoorPos ))
|
|
{
|
|
grcDebugDraw::Line(matResult.d, vOpenDoorPos, Color_blue, Color_yellow);
|
|
grcDebugDraw::Sphere(vOpenDoorPos, 0.025f, Color_yellow);
|
|
char szText[64];
|
|
formatf(szText, "Entry Point Position For Entry Point %i", i);
|
|
grcDebugDraw::Text(vOpenDoorPos, Color_yellow, szText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void CTaskPlayerOnFoot::CleanUp()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
if (pPed->GetPlayerInfo())
|
|
{
|
|
CPlayerPedTargeting & targeting = pPed->GetPlayerInfo()->GetTargeting();
|
|
|
|
if(targeting.GetLockOnTarget())
|
|
{
|
|
targeting.ClearLockOnTarget();
|
|
}
|
|
pPed->GetPlayerInfo()->SetPlayerDataFreeAiming( false );
|
|
}
|
|
|
|
// Clear any arrest values
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_ForcedAimFromArrest, false);
|
|
m_pTargetPed = nullptr;
|
|
m_pLastTargetPed = nullptr;
|
|
m_uArrestFinishTime = 0;
|
|
m_uArrestSprintBreakoutTime = 0;
|
|
m_bArrestTimerActive = false;
|
|
|
|
m_optionalVehicleHelper.ReleaseClips();
|
|
m_chosenOptionalVehicleSet = CLIP_SET_ID_INVALID;
|
|
m_bOptionVehicleGroupLoaded = false;
|
|
m_bWaitingForLadder = false;
|
|
|
|
if( m_pMeleeReactionTask )
|
|
{
|
|
delete m_pMeleeReactionTask;
|
|
m_pMeleeReactionTask = NULL;
|
|
}
|
|
|
|
m_pPedPlayerIntimidating = NULL;
|
|
|
|
m_fTimeSinceLastStickInput = MAX_STICK_INPUT_RECORD_TIME;
|
|
|
|
if(m_pMeleeRequestTask)
|
|
{
|
|
delete m_pMeleeRequestTask;
|
|
m_pMeleeRequestTask = NULL;
|
|
}
|
|
|
|
if (m_pLadderClipRequestHelper)
|
|
{
|
|
CLadderClipSetRequestHelperPool::ReleaseClipSetRequestHelper(m_pLadderClipRequestHelper);
|
|
m_pLadderClipRequestHelper = NULL;
|
|
}
|
|
|
|
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting, false);
|
|
|
|
ClearScriptedTask();
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::ShouldAbort(const AbortPriority iPriority, const aiEvent* pEvent)
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
// Hack. If this is the in-water event, but we have just purposefully quit the CTaskComplexInWater
|
|
// due to wanting to climb a ladder or enter a vehicle - then don't allow this task to be aborted.
|
|
if(pEvent && ((CEvent*)pEvent)->GetEventType()==EVENT_IN_WATER)
|
|
{
|
|
if(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InWaterTaskQuitToClimbLadder ))
|
|
return false;
|
|
}
|
|
|
|
// If made abortable by a task thats giving the player a melee reaction
|
|
// start the reaction next update
|
|
if( pEvent &&
|
|
m_pMeleeReactionTask == NULL &&
|
|
iPriority != CTask::ABORT_PRIORITY_IMMEDIATE &&
|
|
((CEvent*)pEvent)->GetEventType() == EVENT_DAMAGE &&
|
|
pPed->GetPedIntelligence()->GetPhysicalEventResponseTask() &&
|
|
pPed->GetPedIntelligence()->GetPhysicalEventResponseTask()->GetTaskType() == CTaskTypes::TASK_MELEE &&
|
|
pPed->GetPedIntelligence()->GetEventResponseTask() == NULL )
|
|
{
|
|
m_pMeleeReactionTask = (CTask*)pPed->GetPedIntelligence()->GetPhysicalEventResponseTask()->Copy();
|
|
static_cast<CEvent*>(const_cast<aiEvent*>(pEvent))->Tick();
|
|
|
|
// Need to update this bool to stop asserts since we aren't in a FSM update
|
|
// In future would be better if this was set for us in CTask::MakeAbortable
|
|
// but until all tasks are FSM tasks this is not a good idea
|
|
ASSERT_ONLY(SetCanChangeState(true));
|
|
|
|
// Switch to the melee response state
|
|
// First exit the current state
|
|
UpdateFSM( GetState(), OnExit );
|
|
SetState(STATE_MELEE_REACTION);
|
|
SetFlag(aiTaskFlags::RestartStateOnResume);
|
|
//UpdateFSM( STATE_MELEE_REACTION, OnEnter);
|
|
|
|
ASSERT_ONLY(SetCanChangeState(false));
|
|
|
|
return false;
|
|
}
|
|
|
|
return CTask::ShouldAbort(iPriority, pEvent);
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::DoAbort(const AbortPriority UNUSED_PARAM(priority), const aiEvent* pEvent)
|
|
{
|
|
if (pEvent && pEvent->GetEventType() == EVENT_IN_AIR && GetState() == STATE_GET_IN_VEHICLE)
|
|
{
|
|
// Allow the fall task to interrupt early, Phil gave his blessing on the const_cast
|
|
aiEvent* pNonConstAiEvent = const_cast<aiEvent*>(pEvent);
|
|
static_cast<CEventInAir*>(pNonConstAiEvent)->SetForceInterrupt(true);
|
|
m_bResumeGetInVehicle = true;
|
|
}
|
|
}
|
|
|
|
float CTaskPlayerOnFoot::ComputeNewCoverSearchDirection(CPed* pPlayerPed)
|
|
{
|
|
if (pPlayerPed->IsLocalPlayer())
|
|
{
|
|
const CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
Vector2 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
|
|
float fCamOrient = camInterface::GetHeading();
|
|
if (vStickInput.Mag2() > 0.0f)
|
|
{
|
|
return fwAngle::LimitRadianAngle(rage::Atan2f(-vStickInput.x, vStickInput.y) + fCamOrient);
|
|
}
|
|
else
|
|
{
|
|
return fwAngle::LimitRadianAngle(fCamOrient);
|
|
}
|
|
}
|
|
return -999.0f;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::HasValidEnterVehicleInput(const CControl& rControl) const
|
|
{
|
|
if (CPhoneMgr::CamGetState())
|
|
return false;
|
|
|
|
// Check to see if player has warped out of vehicle recently and block exit (button mash prevention)
|
|
TUNE_GROUP_INT(VEHICLE_ENTRY_TUNE, TIME_AFTER_WARP_TO_BLOCK_ENTRY, 400, 0, 1000, 1);
|
|
const CPed* pPed = GetPed();
|
|
if (pPed)
|
|
{
|
|
CPlayerInfo* pPlayerInfo = GetPed()->GetPlayerInfo();
|
|
if (pPlayerInfo->GetLastTimeWarpedOutOfVehicle() + TIME_AFTER_WARP_TO_BLOCK_ENTRY > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (rControl.GetPedEnter().IsPressed())
|
|
return true;
|
|
|
|
TUNE_GROUP_INT(ENTER_VEHICLE_TUNE, MAX_TIME_BUTTON_PRESSED_TO_ALLOW_ENTER_VEHICLE, 500, 0, 2000, 100);
|
|
if ((fwTimer::GetTimeInMilliseconds() - m_uLastTimeEnterVehiclePressed) < MAX_TIME_BUTTON_PRESSED_TO_ALLOW_ENTER_VEHICLE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::HasValidEnterCoverInput(const CControl& rControl) const
|
|
{
|
|
if (rControl.GetPedCover().IsPressed())
|
|
return true;
|
|
|
|
TUNE_GROUP_INT(ENTER_COVER_TUNE, MAX_TIME_BUTTON_PRESSED_TO_ALLOW_ENTER_COVER, 500, 0, 2000, 100);
|
|
if ((fwTimer::GetTimeInMilliseconds() - m_uLastTimeEnterCoverPressed) < MAX_TIME_BUTTON_PRESSED_TO_ALLOW_ENTER_COVER)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Checks if player can fly jetpack.
|
|
//-------------------------------------------------------------------------
|
|
bool CTaskPlayerOnFoot::CheckForUsingJetpack(CPed *pPlayerPed)
|
|
{
|
|
if(pPlayerPed->GetIsSwimming())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(pPlayerPed->GetCurrentMotionTask() && pPlayerPed->GetCurrentMotionTask()->IsUnderWater())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if ENABLE_JETPACK
|
|
//! Scan for jetpack task. TO DO - move somewhere more appropriate?
|
|
if(pPlayerPed->GetHasJetpackEquipped() &&
|
|
CTaskJetpack::DoesPedHaveJetpack(pPlayerPed) &&
|
|
!pPlayerPed->IsUsingJetpack() &&
|
|
pPlayerPed->GetControlFromPlayer() &&
|
|
pPlayerPed->GetControlFromPlayer()->GetVehicleFlyYawRight().GetNorm() > 0.0f)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckForBirdProjectile(CPed* pPlayerPed)
|
|
{
|
|
// Check if you are an animal.
|
|
if (pPlayerPed->GetPedType() != PEDTYPE_ANIMAL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if you are a flying bird.
|
|
CTask* pMotionTask = pPlayerPed->GetPedIntelligence()->GetMotionTaskActiveSimplest();
|
|
if (!pMotionTask || pMotionTask->GetTaskType() != CTaskTypes::TASK_ON_FOOT_BIRD)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if you are in the right bird locomotion state.
|
|
if (pMotionTask->GetState() != CTaskBirdLocomotion::State_Fly && pMotionTask->GetState() != CTaskBirdLocomotion::State_Glide)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(sm_Tunables.m_BirdWeaponHash);
|
|
Assert(pWeaponInfo);
|
|
|
|
if (!pWeaponInfo)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if it's been long enough since crapping.
|
|
if (m_fTimeSinceBirdProjectile < pWeaponInfo->GetTimeBetweenShots())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Grab the input.
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
// Check if the input is down.
|
|
if (!pControl || !pControl->GetPedAttack().IsDown())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Bombs away!
|
|
return true;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::SpawnBirdProjectile(CPed* pPlayerPed)
|
|
{
|
|
// Clear the request flag.
|
|
m_bNeedToSpawnBirdProjectile = false;
|
|
|
|
// Note that we have attempted to spawn a projectile flag.
|
|
m_bHasAttemptedToSpawnBirdProjectile = true;
|
|
|
|
const CWeaponInfo* pWeaponInfo = pPlayerPed->GetEquippedWeaponInfo();
|
|
|
|
if (pWeaponInfo && pWeaponInfo->GetAmmoInfo())
|
|
{
|
|
Matrix34 mSpawn = MAT34V_TO_MATRIX34(pPlayerPed->GetTransform().GetMatrix());
|
|
CProjectile* pProjectile = CProjectileManager::CreateProjectile(pWeaponInfo->GetAmmoInfo()->GetHash(), pWeaponInfo->GetHash(), pPlayerPed, mSpawn, pWeaponInfo->GetDamage(), pWeaponInfo->GetDamageType(), pWeaponInfo->GetEffectGroup());
|
|
|
|
if(pProjectile)
|
|
{
|
|
pProjectile->SetIgnoreDamageEntity(pPlayerPed);
|
|
|
|
if (sm_Tunables.m_HideBirdProjectile)
|
|
{
|
|
pProjectile->SetIsVisibleForModule(SETISVISIBLE_MODULE_GAMEPLAY, false);
|
|
}
|
|
|
|
// There's nothing illegal about bird crap.
|
|
pProjectile->SetDisableExplosionCrimes(true);
|
|
|
|
Vector3 vDirection(0.0f, 0.0f, -1.0f);
|
|
pProjectile->Fire(vDirection, -1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckForNearbyCover(CPed *pPlayerPed, u32 iFlags, bool bForceControl )
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
if (pControl->GetPedSpecialAbility().IsDown())
|
|
return false;
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MIN_TIM_AFTER_EXIT_BEFORE_ALLOW_REENTER, 0.3f, 0.0f, 1.0f, 0.01f);
|
|
if (m_bCheckTimeInStateForCover)
|
|
{
|
|
bool bCheckTimeInState = false;
|
|
if (GetPreviousState() == STATE_USE_COVER)
|
|
{
|
|
bCheckTimeInState = true;
|
|
}
|
|
else if (CTaskCover::CanUseThirdPersonCoverInFirstPerson(*pPlayerPed))
|
|
{
|
|
bCheckTimeInState = true;
|
|
}
|
|
|
|
if (bCheckTimeInState)
|
|
{
|
|
if (GetTimeInState() < MIN_TIM_AFTER_EXIT_BEFORE_ALLOW_REENTER)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
m_bCheckTimeInStateForCover = false;
|
|
}
|
|
|
|
Vector3 vDir = camInterface::GetFront();
|
|
Vector3 vPlayerPedPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
//Vector3 vInFront = ( vDir * 10.0f ) + vPlayerPedPos;
|
|
Vector3 vCoverCoors;
|
|
|
|
// Only when on foot
|
|
if (!pPlayerPed->GetCurrentMotionTask()->IsOnFoot() && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterCover))
|
|
return false;
|
|
|
|
const bool bEnterPressed = HasValidEnterCoverInput(*pControl) || pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_WantsToEnterCover) || bForceControl;
|
|
|
|
bool bPlayerWantsToEnterCover = bEnterPressed || (iFlags&CSF_DontCheckButtonPress);
|
|
|
|
// If the cover search isn't being rendered, don't search if the button isn't pressed
|
|
#if __DEV
|
|
if( !CCoverDebug::ms_Tunables.m_RenderPlayerCoverSearch )
|
|
#endif
|
|
{
|
|
if( !bPlayerWantsToEnterCover )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Player use of cover can be disabled using
|
|
if( !pPlayerPed->GetPlayerInfo()->CanUseCover() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PlayerCoverSearchData coverSearchData;
|
|
coverSearchData.m_pPlayerPed = pPlayerPed;
|
|
coverSearchData.m_iSearchFlags = iFlags;
|
|
coverSearchData.m_vPreviousCoverDir = m_vLastCoverDir;
|
|
|
|
if( !CTaskCover::CalculateCoverSearchVariables(coverSearchData) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
s32 iNumPlayerTexts = 0;
|
|
const Vec3V vPlayerPos = pPlayerPed->GetTransform().GetPosition();
|
|
DrawInitialCoverDebug(*pPlayerPed, iNumPlayerTexts, CCoverDebug::ms_Tunables.m_TextOffset, coverSearchData.m_CoverProtectionDir);
|
|
#endif // DEBUG_DRAW
|
|
|
|
// I already had a coverpoint as I came out of cover but never moved reuse it
|
|
if (pPlayerPed->GetCoverPoint())
|
|
{
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::AddDebugText(vPlayerPos, Color_blue, "REUSING OLD COVERPOINT", -1, CCoverDebug::ms_Tunables.m_TextOffset * iNumPlayerTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
#endif // DEBUG_DRAW
|
|
return true;
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
bool bUsingDynamicCover = false;
|
|
s32 iNoTexts = 0;
|
|
#endif // DEBUG_DRAW
|
|
|
|
// Store whether the using cover point flag is already set
|
|
const bool bUsingCoverPoint = pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_UsingCoverPoint);
|
|
|
|
// Find the nearest cover point using the dynamic cover test
|
|
if( iFlags&CSF_ConsiderDynamicCover )
|
|
{
|
|
Vector3 vTestPos = vPlayerPedPos;
|
|
// Use the collision capsule because it isn't affected by leg ik
|
|
if( pPlayerPed->GetCurrentPhysicsInst() )
|
|
vTestPos.z = pPlayerPed->GetCurrentPhysicsInst()->GetMatrix().GetM23f();
|
|
|
|
// If we fail with the first tests, change the search direction
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::ms_eInitialCheckType = CCoverDebug::FIRST_DYNAMIC_CHECK;
|
|
#endif // DEBUG_DRAW
|
|
CCoverPoint newCoverPoint;
|
|
|
|
// Test in stick direction first
|
|
bool bFoundNewCover = false;
|
|
const float fNewSearchDir = ComputeNewCoverSearchDirection(pPlayerPed);
|
|
if (fNewSearchDir > -999.0f)
|
|
{
|
|
bFoundNewCover = CPlayerInfo::ms_DynamicCoverHelper.FindNewCoverPoint(&newCoverPoint, pPlayerPed, vTestPos, fNewSearchDir);
|
|
}
|
|
|
|
// If nothing is found, test in camera direction
|
|
if (!bFoundNewCover)
|
|
{
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::ms_eInitialCheckType = CCoverDebug::SECOND_DYNAMIC_CHECK;
|
|
#endif // DEBUG_DRAW
|
|
bFoundNewCover = CPlayerInfo::ms_DynamicCoverHelper.FindNewCoverPoint(&newCoverPoint, pPlayerPed, vTestPos);
|
|
}
|
|
|
|
//check if player is pulling away from this position if it's close
|
|
const float fDistToCoverSqd = (vTestPos - vPlayerPedPos).XYMag2();
|
|
if (bFoundNewCover && fDistToCoverSqd <= 1.0f)
|
|
{
|
|
Vector2 vecStick(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm());
|
|
vecStick *= 128.0f;
|
|
vecStick.Rotate(camInterface::GetHeading());
|
|
Vector3 vCoverDirection = VEC3V_TO_VECTOR3(newCoverPoint.GetCoverDirectionVector());
|
|
vecStick.Rotate(-rage::Atan2f(-vCoverDirection.x, vCoverDirection.y));
|
|
const bool bNotValidForStickHeading = vecStick.y < CTaskInCover::ms_Tunables.m_InputYAxisQuitValue;
|
|
if (bNotValidForStickHeading)
|
|
{
|
|
#if DEBUG_DRAW
|
|
Vector3 vDebugCoverPos;
|
|
if (newCoverPoint.GetCoverPointPosition(vDebugCoverPos))
|
|
{
|
|
char szText[128];
|
|
formatf(szText, "DYNAMIC INVALID: StickHeading(%.2f)", vecStick.y);
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vDebugCoverPos), Color_blue, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
bFoundNewCover = false;
|
|
}
|
|
}
|
|
|
|
if (bFoundNewCover)
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint()->Copy(newCoverPoint);
|
|
pPlayerPed->SetCoverPoint(pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint());
|
|
pPlayerPed->GetCoverPoint()->ReserveCoverPointForPed(pPlayerPed);
|
|
#if DEBUG_DRAW
|
|
bUsingDynamicCover = true;
|
|
Vector3 vDebugCoverPos;
|
|
if (pPlayerPed->GetCoverPoint()->GetCoverPointPosition(vDebugCoverPos))
|
|
{
|
|
vDebugCoverPos.z += 1.0f;
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(RCC_VEC3V(vDebugCoverPos), pPlayerPed->GetCoverPoint()->GetCoverDirectionVector(), Color_orange, "DYNAMIC COVER FOUND", CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
|
|
bool bDoneRouteTest = false;
|
|
if( iFlags&CSF_ConsiderStaticCover )
|
|
{
|
|
CCoverPoint* pCoverPoint = CCover::FindClosestCoverPointWithCB( pPlayerPed, vPlayerPedPos, CTaskCover::ms_Tunables.GetMaxPlayerToCoverDist(*pPlayerPed), NULL, CCover::CS_ANY, StaticCoverpointScoringCB, StaticCoverpointValidityCallback, (void*) &coverSearchData );
|
|
if( pCoverPoint )
|
|
{
|
|
#if DEBUG_DRAW
|
|
Vector3 vDebugCoverPos;
|
|
if (pCoverPoint->GetCoverPointPosition(vDebugCoverPos))
|
|
{
|
|
vDebugCoverPos.z += 1.0f;
|
|
CCoverDebug::AddDebugPositionSphereAndDirection(RCC_VEC3V(vDebugCoverPos), pCoverPoint->GetCoverDirectionVector(), Color_red, "BEST STATIC COVER FOUND", 0, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
CCoverPoint* pDynamicCoverPoint = pPlayerPed->GetCoverPoint();
|
|
// Default to using the dynamic point if it exists
|
|
bool bStaticCoverPointHasPrecedenceOverDynamicPoint = false;
|
|
bool bHadPreviousPoint = false;
|
|
CCoverPoint backupCoverPoint;
|
|
|
|
// If we couldn't find a dynamic cover point we must use the static one
|
|
if (!pDynamicCoverPoint)
|
|
{
|
|
Vector3 vStaticCoverDir = VEC3V_TO_VECTOR3(pCoverPoint->GetCoverDirectionVector());
|
|
coverSearchData.m_CoverProtectionDir = vStaticCoverDir;
|
|
bStaticCoverPointHasPrecedenceOverDynamicPoint = true;
|
|
bHadPreviousPoint = true;
|
|
backupCoverPoint = *pCoverPoint;
|
|
}
|
|
// If we found a dynamic cover point aswell, we need to score them against each other to determine which to use
|
|
else
|
|
{
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
camGameplayDirector& rGameplayDirector = camInterface::GetGameplayDirector();
|
|
const Vector3 vCamFront = rGameplayDirector.GetFrame().GetFront();
|
|
|
|
#if DEBUG_DRAW
|
|
char szText[128];
|
|
formatf(szText, "%p SCORING (Actual/Max)", pDynamicCoverPoint);
|
|
Vector3 vDynamicCoverPos;
|
|
pDynamicCoverPoint->GetCoverPointPosition(vDynamicCoverPos);
|
|
vDynamicCoverPos.z += 1.0f;
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vDynamicCoverPos), Color_orange, szText, -1, CCoverDebug::ms_Tunables.m_TextOffset * iNoTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
#endif // DEBUG_DRAW
|
|
|
|
const bool bStickInputValid = IsPlayerStickInputValid(*pPlayerPed);
|
|
const float fDynamicCoverPointScore = ScoreCoverPoint(vPedPos, vCamFront, *pPlayerPed, *pDynamicCoverPoint, false, coverSearchData.m_CoverProtectionDir, bStickInputValid, coverSearchData.m_HasThreat
|
|
#if DEBUG_DRAW
|
|
, &iNoTexts
|
|
#endif // DEBUG_DRAW
|
|
);
|
|
const float fStaticCoverPointScore = coverSearchData.m_BestScore;
|
|
#if DEBUG_DRAW
|
|
#if __ASSERT
|
|
// Call this again so we get the debug rendering on the simple debug and also to verify the results are the same
|
|
const float fDebugDrawScore = ScoreCoverPoint(vPedPos, vCamFront, *pPlayerPed, *pCoverPoint, true, coverSearchData.m_CoverProtectionDir, bStickInputValid, coverSearchData.m_HasThreat);
|
|
taskAssert(fDebugDrawScore == fStaticCoverPointScore);
|
|
#endif // __ASSERT
|
|
#endif // DEBUG_DRAW
|
|
|
|
// Use the cover point with the best (lowest score)
|
|
if (fStaticCoverPointScore < fDynamicCoverPointScore)
|
|
{
|
|
backupCoverPoint = *pDynamicCoverPoint;
|
|
bHadPreviousPoint = true;
|
|
bStaticCoverPointHasPrecedenceOverDynamicPoint = true;
|
|
}
|
|
}
|
|
|
|
if (bStaticCoverPointHasPrecedenceOverDynamicPoint)
|
|
{
|
|
pPlayerPed->SetCoverPoint(pCoverPoint);
|
|
// Position
|
|
Vector3 vPos;
|
|
pPlayerPed->GetCoverPoint()->ReserveCoverPointForPed(pPlayerPed);
|
|
CCover::FindCoordinatesCoverPoint(pPlayerPed->GetCoverPoint(), pPlayerPed, coverSearchData.m_CoverProtectionDir, vPos );
|
|
pPlayerPed->GetCoverPoint()->ReleaseCoverPointForPed(pPlayerPed);
|
|
Vector3 vTestPos = vPos;
|
|
|
|
const float fSecondSurfaceInterp=0.0f;
|
|
|
|
if( CPedPlacement::FindZCoorForPed(fSecondSurfaceInterp, &vTestPos, NULL) )
|
|
{
|
|
vPos = vTestPos;
|
|
}
|
|
|
|
// Heading
|
|
Vector3 vDir = VEC3V_TO_VECTOR3(pPlayerPed->GetCoverPoint()->GetCoverDirectionVector(&RCC_VEC3V(coverSearchData.m_CoverProtectionDir)));
|
|
float fDir = rage::Atan2f(-vDir.x, vDir.y);
|
|
fDir = fwAngle::LimitRadianAngle(fDir);
|
|
|
|
// Keep note of the original cover usage so it can be restored (sometimes the dynamic checks are too ruthless when detecting corners).
|
|
const bool bIsPriority = pPlayerPed->GetCoverPoint()->GetFlag(CCoverPoint::COVFLAGS_IsPriority);
|
|
CCoverPoint newCoverPoint;
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::ms_eInitialCheckType = CCoverDebug::FIRST_STATIC_CHECK;
|
|
#endif // DEBUG_DRAW
|
|
if( !CPlayerInfo::ms_DynamicCoverHelper.FindNewCoverPoint(&newCoverPoint, pPlayerPed, vPos, fDir) )
|
|
{
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::AddDebugText(RCC_VEC3V(vDebugCoverPos), Color_red, "FAILED DYNAMIC CHECKS", -1, CCoverDebug::ms_Tunables.m_TextOffset * -2, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
#endif // DEBUG_DRAW
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
|
|
if( bHadPreviousPoint )
|
|
{
|
|
// Need to set the correct cover entity or we may think we're taking cover on a vehicle when we're not!
|
|
CPlayerInfo::ms_DynamicCoverHelper.SetCoverEntryEntity(backupCoverPoint.GetEntity());
|
|
pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint()->Copy(backupCoverPoint);
|
|
pPlayerPed->SetCoverPoint(pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint());
|
|
pPlayerPed->GetCoverPoint()->ReserveCoverPointForPed(pPlayerPed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Need to set the correct cover entity or we may think we're taking cover on a vehicle when we're not!
|
|
CPlayerInfo::ms_DynamicCoverHelper.SetCoverEntryEntity(pCoverPoint->GetEntity());
|
|
|
|
// We do the obstruction test as part of FindNewCoverPoint
|
|
bDoneRouteTest = true;
|
|
// Make sure the new dynamic cover is near to the static one (dynamic test seem to be returning really bad results on vehicles)
|
|
Vector3 vStaticCoverPos;
|
|
pCoverPoint->GetCoverPointPosition(vStaticCoverPos);
|
|
Vector3 vDynamicCoverPos;
|
|
newCoverPoint.GetCoverPointPosition(vDynamicCoverPos);
|
|
static dev_float STATIC_TO_DYNAMIC_DISTANCE = 1.0f;
|
|
if (vStaticCoverPos.Dist2(vDynamicCoverPos) < rage::square(STATIC_TO_DYNAMIC_DISTANCE))
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint()->Copy(newCoverPoint);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint()->Copy(*pCoverPoint);
|
|
}
|
|
pPlayerPed->SetCoverPoint(pPlayerPed->GetPlayerInfo()->GetDynamicCoverPoint());
|
|
pPlayerPed->GetCoverPoint()->ReserveCoverPointForPed(pPlayerPed);
|
|
|
|
if (bIsPriority)
|
|
{
|
|
pPlayerPed->GetCoverPoint()->SetFlag(CCoverPoint::COVFLAGS_IsPriority);
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
if (pPlayerPed->GetCoverPoint())
|
|
{
|
|
bUsingDynamicCover = false;
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Ignore far cover
|
|
if( pPlayerPed->GetCoverPoint() )
|
|
{
|
|
// If there is a coverpoint, make sure it isn't too close
|
|
Vector3 vCoverPos;
|
|
pPlayerPed->GetCoverPoint()->GetCoverPointPosition(vCoverPos);
|
|
vCoverPos.z += 1.0f;
|
|
|
|
vPlayerPedPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
if( !(iFlags&CSF_ConsiderFarCover) && vCoverPos.Dist2(vPlayerPedPos) > CTaskCover::ms_Tunables.m_MinDistToCoverAnyDir )
|
|
{
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
}
|
|
else if( !(iFlags&CSF_ConsiderCloseCover) && vCoverPos.Dist2(vPlayerPedPos) < CTaskCover::ms_Tunables.m_MinDistToCoverAnyDir )
|
|
{
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
}
|
|
}
|
|
|
|
//Ignore cover underwater or too close to an ai ped entering cover there
|
|
if (pPlayerPed->GetCoverPoint())
|
|
{
|
|
// See if there are nearby peds with a certain distance from the cover point
|
|
Vector3 vPos;
|
|
pPlayerPed->GetCoverPoint()->GetCoverPointPosition(vPos);
|
|
Vector3 vPedCoverPos(vPos.x, vPos.y, vPos.z + 1.0f); // Add one to z height as cover position is on the ground
|
|
const CPed* pPedBlockingCover = CTaskEnterCover::GetPedBlockingMyCover(*pPlayerPed, vPedCoverPos);
|
|
if (pPedBlockingCover)
|
|
{
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
}
|
|
else
|
|
{
|
|
//Is cover point underwater?
|
|
float waterZ = 0;
|
|
if(pPlayerPed->m_Buoyancy.GetWaterLevelIncludingRivers(vPos, &waterZ, true, POOL_DEPTH, REJECTIONABOVEWATER, NULL, pPlayerPed))
|
|
{
|
|
static dev_float s_fAcceptableCoverOffset = 0.5f;
|
|
if (waterZ > (vPos.z + s_fAcceptableCoverOffset))
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bDoneRouteTest && pPlayerPed->GetCoverPoint() && !TestIfRouteToCoverPointIsClear(*pPlayerPed->GetCoverPoint(), *pPlayerPed, coverSearchData.m_CoverProtectionDir))
|
|
{
|
|
pPlayerPed->SetCoverPoint(NULL);
|
|
}
|
|
|
|
if( pPlayerPed->GetCoverPoint() )
|
|
{
|
|
pPlayerPed->GetCoverPoint()->ReserveCoverPointForPed(pPlayerPed);
|
|
|
|
// Don't check for cover if the player hasn't requested cover (must be testing cover rendering)
|
|
if( !bPlayerWantsToEnterCover )
|
|
{
|
|
pPlayerPed->ReleaseCoverPoint();
|
|
|
|
// Making sure flag is cleared if set during the search (ReserveCoverPointForPed would have set the flag)
|
|
if (!bUsingCoverPoint && pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_UsingCoverPoint))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_UsingCoverPoint, false);
|
|
}
|
|
return false;
|
|
}
|
|
#if DEBUG_DRAW
|
|
CCoverDebug::AddDebugText(vPlayerPos, bUsingDynamicCover ? Color_orange : Color_red, bUsingDynamicCover ? "USING DYNAMIC COVERPOINT" : "USING STATIC COVERPOINT", -1, CCoverDebug::ms_Tunables.m_TextOffset * iNumPlayerTexts++, CCoverDebug::INITIAL_SEARCH_SIMPLE);
|
|
#endif // DEBUG_DRAW
|
|
|
|
// If the player has pressed the enter button, go into the cover
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Making sure flag is cleared if set during the search (ReserveCoverPointForPed would have set the flag)
|
|
if (!bUsingCoverPoint && pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_UsingCoverPoint))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_UsingCoverPoint, false);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CTaskPlayerOnFoot::ePlayerOnFootState CTaskPlayerOnFoot::CheckForAutoCover(CPed* pPed)
|
|
{
|
|
Assert(pPed->IsAPlayerPed());
|
|
// Only when on foot
|
|
if (!pPed->GetCurrentMotionTask()->IsOnFoot())
|
|
return STATE_INVALID;
|
|
|
|
// Never when following an 'assisted-movement' route
|
|
CTaskMove * pMoveTask = pPed->GetPedIntelligence()->GetActiveSimplestMovementTask();
|
|
if(pMoveTask && pMoveTask->GetTaskType()==CTaskTypes::TASK_MOVE_PLAYER)
|
|
{
|
|
if(((CTaskMovePlayer*)pMoveTask)->GetAssistedMovementControl()->GetIsUsingRoute())
|
|
return STATE_INVALID;
|
|
}
|
|
|
|
// Never when heavy weapon is equipped
|
|
const CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
|
|
const CWeaponInfo* pWeaponInfo = pWeaponManager ? pWeaponManager->GetEquippedWeaponInfo() : false;
|
|
if( pWeaponInfo && pWeaponInfo->GetIsHeavy() )
|
|
{
|
|
return STATE_INVALID;
|
|
}
|
|
|
|
// Check for cover first:
|
|
if(!m_bPlayerExitedCoverThisUpdate && CheckForNearbyCover(pPed, CSF_ConsiderCloseCover|CSF_ConsiderDynamicCover/*|CSF_DontCheckButtonPress*/))
|
|
{
|
|
return STATE_USE_COVER;
|
|
}
|
|
|
|
return STATE_INVALID;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::DoJumpCheck(CPed* pPlayerPed, CControl *UNUSED_PARAM(pControl), bool bHeavyWeapon, bool bForceVaultCheck)
|
|
{
|
|
#if __BANK
|
|
|
|
TUNE_GROUP_BOOL(PED_JUMPING, bConstantTerrainAnalysis ,false);
|
|
if(bConstantTerrainAnalysis)
|
|
{
|
|
CTaskJump::DoJumpGapTest(pPlayerPed, g_JumpHelper);
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(PED_JUMPING, bTestSuperJump, false);
|
|
if(bTestSuperJump)
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_SUPER_JUMP_ON);
|
|
}
|
|
TUNE_GROUP_BOOL(PED_JUMPING, bTestBeastJump, false);
|
|
if(bTestBeastJump)
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_BEAST_JUMP_ON);
|
|
}
|
|
|
|
|
|
#endif //__BANK
|
|
|
|
m_bJumpedFromCover = (GetPreviousState() == STATE_USE_COVER);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fStandAutoStepUpVelThresholdSq, 1.0f, 0.0f, 10.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fStandAutoStepUpTime, 0.1f, 0.0f, 10.0f, 0.01f);
|
|
float fVelMag2 = pPlayerPed->GetVelocity().XYMag2();
|
|
if(fVelMag2 < rage::square(fStandAutoStepUpVelThresholdSq))
|
|
{
|
|
m_fStandAutoStepUpTimer+=fwTimer::GetTimeStep();
|
|
}
|
|
else
|
|
{
|
|
m_fStandAutoStepUpTimer = 0.0f;
|
|
}
|
|
|
|
bool bCanStandAutoVault = m_fStandAutoStepUpTimer > fStandAutoStepUpTime;
|
|
|
|
m_VaultTestResults.Reset();
|
|
if(CanJumpOrVault(pPlayerPed, m_VaultTestResults, bHeavyWeapon, GetState(), bForceVaultCheck, bCanStandAutoVault))
|
|
{
|
|
m_iJumpFlags = MakeJumpFlags(pPlayerPed, m_VaultTestResults);
|
|
|
|
// Are we going to jump?
|
|
|
|
bool bVault = CTaskJumpVault::WillVault(pPlayerPed, m_iJumpFlags);
|
|
if(bVault)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( !m_iJumpFlags.IsFlagSet(JF_DisableJumpingIfNoClimb) && !bForceVaultCheck )
|
|
{
|
|
CTaskMotionBase *pMotionTask = pPlayerPed->GetCurrentMotionTask();
|
|
float fCurrentMbrYCheck = 0.f;
|
|
#if FPS_MODE_SUPPORTED
|
|
const bool bInFpsMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
if(bInFpsMode && pPlayerPed->IsInMotion())
|
|
{
|
|
TUNE_GROUP_BOOL(BUG_2048930, NO_JUMP_WHEN_ROLLING, true);
|
|
const CTaskCombatRoll* pCombatRollTask = static_cast<const CTaskCombatRoll*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_COMBAT_ROLL));
|
|
if(NO_JUMP_WHEN_ROLLING)
|
|
{
|
|
if (pCombatRollTask)
|
|
return false;
|
|
|
|
const CTaskMotionAiming* pMotionAimingTask = static_cast<const CTaskMotionAiming*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_AIMING));
|
|
if (pMotionAimingTask && pMotionAimingTask->GetPreviousState() == CTaskMotionAiming::State_Roll)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(PED_JUMPING, FPS_MbrYCheck, -1.5f, -MOVEBLENDRATIO_SPRINT, MOVEBLENDRATIO_SPRINT, 0.01f);
|
|
fCurrentMbrYCheck = FPS_MbrYCheck;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
const bool bReducedGaitMoving = pMotionTask && pMotionTask->IsGaitReduced() && (pPlayerPed->GetMotionData()->GetDesiredMbrY() > 0.0f);
|
|
const bool bSpeedCheck = bInFpsMode ? (pPlayerPed->GetMotionData()->GetDesiredMbrY() > fCurrentMbrYCheck) : (pPlayerPed->GetMotionData()->GetCurrentMbrY() > fCurrentMbrYCheck);
|
|
const bool bForcedJump = pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_FORCED_JUMP);
|
|
if(bForcedJump || bSpeedCheck || bReducedGaitMoving)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_VaultFromCover, false );
|
|
return false;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::GetClimbDetectorParam(s32 state,
|
|
CPed* pPlayerPed,
|
|
sPedTestParams &pedTestParams,
|
|
bool bDoManualClimbCheck,
|
|
bool bAutoVaultOnly,
|
|
bool bAttemptingToVaultFromSwimming,
|
|
bool bHeavyWeapon,
|
|
bool bCanDoStandAutoStepUp)
|
|
{
|
|
|
|
Assertf(pPlayerPed,"Expect valid ped here");
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultParallelDot, 0.3f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultForwardDot, 0.3f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultStickDot, 0.3f, -1.0f, 1.0f, 0.1f);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fAimingVaultParallelDot, 0.3f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAimingVaultForwardDot, 0.3f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAimingVaultStickDot, 0.8f, -1.0f, 1.0f, 0.1f);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultParallelDot, 0.8f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultForwardDot, 0.8f, -1.0f, 1.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultStickDot, 0.8f, -1.0f, 1.0f, 0.1f);
|
|
|
|
// DMKH. Different angles for auto-vault vs manual vault.
|
|
float fParallelDot;
|
|
float fForwardDot;
|
|
float fStickDot;
|
|
if(bDoManualClimbCheck)
|
|
{
|
|
if(HasPlayerSelectedGunTask(pPlayerPed, state))
|
|
{
|
|
fParallelDot = fAimingVaultParallelDot;
|
|
fForwardDot = fAimingVaultForwardDot;
|
|
fStickDot = fAimingVaultStickDot;
|
|
|
|
//! Always take stick into account - don't do standing vault if aiming.
|
|
pedTestParams.bDoStandingVault = false;
|
|
}
|
|
else
|
|
{
|
|
fParallelDot = fVaultParallelDot;
|
|
fForwardDot = fVaultForwardDot;
|
|
fStickDot = fVaultStickDot;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fParallelDot = fAutoVaultParallelDot;
|
|
fForwardDot = fAutoVaultForwardDot;
|
|
fStickDot = fAutoVaultStickDot;
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultHeightThresholdMin, 0.0f, 0.0f, 2.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultHeightThresholdMax, 0.8f, 0.0f, 2.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultHeightAllowanceOnSlope, 0.85f, 0.0f, 2.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultDepthThresholdMin, 0.0f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultDepthThresholdMax, 1000.0f, 0.0f, 1000.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultMinHorizontalClearance, 0.75f, 0.0f, 100.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultMBRThreshold, 1.5f, 0.0f, 3.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultDistanceMin, 1.8f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultDistanceMax, 2.0f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultDistanceCutoff, 0.45f, 0.0f, 5.0f, 0.1f)
|
|
TUNE_GROUP_FLOAT(VAULTING, fStepUpDistanceCutoffClose, 0.2f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fStepUpDistanceCutoff, 0.225f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fStealthStepUpDistanceCutoffClose, 0.2f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fStealthStepUpDistanceCutoff, 0.375f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoStepUpHeight, 0.375f, 0.0f, 5.0f, 0.05f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoStepUpPedHeight, 0.25f, 0.0f, 5.0f, 0.05f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fJumpVaultSubmergeLevelThreshold, 0.2f, 0.0f, 5.0f, 0.1f);
|
|
|
|
TUNE_GROUP_BOOL(VAULTING, bDoJumpVault, true);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultDistMBRMin, 0.75f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultDistMBRMax, 1.5f, 0.0f, 5.0f, 0.1f);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultDistanceManual, 1.5f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultDistanceStanding, 1.5f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultDistanceStandingVehicle, 1.5f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fFirstPersonVaultDistance, 2.5f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultDistanceInWater, 1.5f, 0.0f, 5.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fVaultDistanceStepUp, 1.0f, 0.0f, 5.0f, 0.1f);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fHorizontalTestDistance, 2.5f, 0.0f, 10.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fLargeHorizontalTestDistance, 5.0f, 0.0f, 10.0f, 0.1f);
|
|
TUNE_GROUP_FLOAT(VAULTING, fFirstPersonHorizontalTestDistance, 5.0f, 0.0f, 10.0f, 0.1f);
|
|
|
|
TUNE_GROUP_INT(VAULTING, uStepUpReduceGaitTime, 100, 0, 1000, 100);
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fZClearanceAdjustment, 0.05f, 0.0f, 10.0f, 0.1f);
|
|
|
|
float fCurrentMBR = pPlayerPed->GetMotionData()->GetCurrentMbrY();
|
|
|
|
float fVaultDistance;
|
|
float fSelectedVaultDistanceStanding = fVaultDistanceStanding;
|
|
float fSelectedVaultDistanceStandingVehicle = fVaultDistanceStandingVehicle;
|
|
if(bDoManualClimbCheck)
|
|
{
|
|
fVaultDistance = bAttemptingToVaultFromSwimming ? fVaultDistanceInWater : fVaultDistanceManual;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if( pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) )
|
|
{
|
|
fVaultDistance = fFirstPersonVaultDistance;
|
|
fSelectedVaultDistanceStanding = fFirstPersonVaultDistance;
|
|
fSelectedVaultDistanceStandingVehicle = fFirstPersonVaultDistance;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if(state == STATE_JUMP)
|
|
{
|
|
const CTaskVault* pVaultTask = static_cast<const CTaskVault*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VAULT));
|
|
if(pVaultTask && pVaultTask->GetVaultSpeed() == CTaskVault::VaultSpeed_Run)
|
|
{
|
|
fCurrentMBR = MOVEBLENDRATIO_RUN;
|
|
}
|
|
}
|
|
if(state == STATE_DROPDOWN)
|
|
{
|
|
const CTaskDropDown* pDropDownTask = static_cast<const CTaskDropDown*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_DROP_DOWN));
|
|
if(pDropDownTask && pDropDownTask->GetDropDownSpeed() == CTaskDropDown::DropDownSpeed_Run)
|
|
{
|
|
fCurrentMBR = MOVEBLENDRATIO_RUN;
|
|
}
|
|
}
|
|
|
|
float fCurrentSpeed = fCurrentMBR/MOVEBLENDRATIO_SPRINT;
|
|
fVaultDistance = fAutoVaultDistMBRMin + ((fAutoVaultDistMBRMax-fAutoVaultDistMBRMin)*fCurrentSpeed);
|
|
}
|
|
|
|
bool bSubmerged = pPlayerPed->m_Buoyancy.GetBuoyancyInfoUpdatedThisFrame() &&
|
|
(pPlayerPed->GetStatus() != NOT_IN_WATER) &&
|
|
(pPlayerPed->m_Buoyancy.GetSubmergedLevel() > fJumpVaultSubmergeLevelThreshold);
|
|
|
|
bool bInWater = pPlayerPed->GetIsSwimming() || pPlayerPed->GetWasSwimming() || bSubmerged;
|
|
|
|
bool bReducingGaitForStepUp = m_uReducingGaitTimeMS != 0 &&
|
|
(fwTimer::GetTimeInMilliseconds() > (m_uReducingGaitTimeMS + uStepUpReduceGaitTime));
|
|
|
|
//! Capsule using stealth can ride up easier than default. In this case, don't step up unless if we are close to handhold, as it can look pretty horrible.
|
|
float fStepUpCutoff;
|
|
if(pPlayerPed->GetMotionData()->GetUsingStealth())
|
|
{
|
|
fStepUpCutoff = bReducingGaitForStepUp ? fStealthStepUpDistanceCutoffClose : fStealthStepUpDistanceCutoff;
|
|
}
|
|
else
|
|
{
|
|
fStepUpCutoff = bReducingGaitForStepUp ? fStepUpDistanceCutoffClose : fStepUpDistanceCutoff;
|
|
}
|
|
|
|
pedTestParams.fParallelDot = fParallelDot;
|
|
pedTestParams.fForwardDot = fForwardDot;
|
|
pedTestParams.fStickDot = fStickDot;
|
|
pedTestParams.fVaultDistStanding = bAutoVaultOnly ? -1.0f : fSelectedVaultDistanceStanding;
|
|
pedTestParams.fVaultDistStandingVehicle = bAutoVaultOnly ? -1.0f : fSelectedVaultDistanceStandingVehicle;
|
|
pedTestParams.fVaultDistStepUp = fVaultDistanceStepUp;
|
|
pedTestParams.fJumpVaultHeightThresholdMin = fJumpVaultHeightThresholdMin;
|
|
pedTestParams.fJumpVaultHeightThresholdMax = fJumpVaultHeightThresholdMax;
|
|
pedTestParams.fJumpVaultHeightAllowanceOnSlope = fJumpVaultHeightAllowanceOnSlope;
|
|
pedTestParams.fJumpVaultDepthThresholdMin = fJumpVaultDepthThresholdMin;
|
|
pedTestParams.fJumpVaultDepthThresholdMax = fJumpVaultDepthThresholdMax;
|
|
pedTestParams.fJumpVaultMinHorizontalClearance = fJumpVaultMinHorizontalClearance;
|
|
pedTestParams.bDepthThresholdTestInsideRange = true;
|
|
pedTestParams.fJumpVaultMBRThreshold = fJumpVaultMBRThreshold;
|
|
pedTestParams.fAutoJumpVaultDistMin = fJumpVaultDistanceMin;
|
|
pedTestParams.fAutoJumpVaultDistMax = fJumpVaultDistanceMax;
|
|
pedTestParams.fAutoJumpVaultDistCutoff = fJumpVaultDistanceCutoff;
|
|
pedTestParams.fMaxVaultDistance = fVaultDistance;
|
|
pedTestParams.bDoJumpVault = bDoJumpVault && !bInWater && !bHeavyWeapon && !pPlayerPed->GetMotionData()->GetUsingStealth();
|
|
pedTestParams.bShowStickIntent = true;
|
|
pedTestParams.fUseTestOrientationHeight = CTaskVault::ms_fStepUpHeight;
|
|
pedTestParams.fCurrentMBR = fCurrentMBR;
|
|
pedTestParams.fStepUpDistanceCutoff = fStepUpCutoff;
|
|
pedTestParams.fZClearanceAdjustment = fZClearanceAdjustment;
|
|
pedTestParams.bCanDoStandingAutoStepUp = bCanDoStandAutoStepUp;
|
|
|
|
if(pPlayerPed->GetPlayerInfo() && pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_INCREASE_JUMP_SUPPRESSION_RANGE))
|
|
{
|
|
pedTestParams.fHorizontalTestDistanceOverride = fLargeHorizontalTestDistance;
|
|
}
|
|
#if FPS_MODE_SUPPORTED
|
|
else if( pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
pedTestParams.fHorizontalTestDistanceOverride = fFirstPersonHorizontalTestDistance;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
pedTestParams.fHorizontalTestDistanceOverride = fHorizontalTestDistance;
|
|
}
|
|
|
|
//! DMKH. Disallow vaulting/jumping large height when arrested.
|
|
if(pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_IsHandCuffed))
|
|
{
|
|
pedTestParams.fOverrideMaxClimbHeight = sm_Tunables.m_MaxEncumberedClimbHeight;
|
|
pedTestParams.bDo2SidedHeightTest = false;
|
|
}
|
|
else if(pPlayerPed->GetGroundPhysical() && pPlayerPed->GetGroundPhysical()->GetIsTypeVehicle())
|
|
{
|
|
if(((CVehicle*)pPlayerPed->GetGroundPhysical())->GetVehicleType()==VEHICLE_TYPE_TRAIN)
|
|
{
|
|
pedTestParams.fOverrideMaxClimbHeight = sm_Tunables.m_MaxTrainClimbHeight;
|
|
pedTestParams.bDo2SidedHeightTest = false;
|
|
}
|
|
}
|
|
|
|
if(bAutoVaultOnly && CTaskVault::GetUsingAutoStepUp())
|
|
{
|
|
if( (pedTestParams.fOverrideMaxClimbHeight < 0.0f) || (pedTestParams.fOverrideMaxClimbHeight > CTaskVault::ms_fStepUpHeight) )
|
|
{
|
|
pedTestParams.fOverrideMinClimbHeight = fAutoStepUpHeight;
|
|
pedTestParams.fOverrideMinClimbPedHeight = fAutoStepUpPedHeight;
|
|
pedTestParams.fOverrideMaxClimbHeight = CTaskVault::ms_fStepUpHeight;
|
|
pedTestParams.bDo2SidedHeightTest = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CanJumpOrVault(CPed* pPlayerPed,
|
|
sPedTestResults &pedTestResults,
|
|
bool bHeavyWeapon,
|
|
s32 nState,
|
|
bool bForceVaultCheck,
|
|
bool bCanDoStandAutoStepUp)
|
|
{
|
|
bool bForcedJump = pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_FORCED_JUMP);
|
|
|
|
#if ENABLE_DRUNK
|
|
if (pPlayerPed->IsDrunk())
|
|
return false;
|
|
#endif // ENABLE_DRUNK
|
|
|
|
//! DMKH. Not when skiing.
|
|
if(pPlayerPed->GetIsSkiing())
|
|
return false;
|
|
|
|
//! And not when crouching and can't stand up.
|
|
if(pPlayerPed->GetIsCrouching() && !pPlayerPed->CanPedStandUp())
|
|
return false;
|
|
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(!pControl)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//! DMKH. Disallow vaulting from underwater. If necessary, we can modify this check later.
|
|
CTaskMotionBase *pMotionTask = pPlayerPed->GetCurrentMotionTask();
|
|
if(pMotionTask && pMotionTask->IsUnderWater())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (CPedType::IsAnimalType(pPlayerPed->GetPedType()))
|
|
{
|
|
if (CPedModelInfo* pModelInfo = pPlayerPed->GetPedModelInfo())
|
|
{
|
|
if (const CMotionTaskDataSet* pMotionTaskDataSet = CMotionTaskDataManager::GetDataSet(pModelInfo->GetMotionTaskDataSetHash().GetHash()))
|
|
{
|
|
if (pMotionTaskDataSet->GetOnFootData()->m_JumpClipSetHash.IsNull())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Special case material that just forces the stumble behavior
|
|
if (ThePaths.bStreamHeistIslandNodes && pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_TooSteepForPlayer))
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, true );
|
|
}
|
|
|
|
bool bAttemptingToVaultFromSwimming = false;
|
|
if(pPlayerPed->GetIsSwimming())
|
|
{
|
|
bAttemptingToVaultFromSwimming = true;
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, true );
|
|
}
|
|
|
|
if(bHeavyWeapon)
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, true );
|
|
}
|
|
|
|
bool bJumpPressed = false;
|
|
if(bForcedJump || pControl->GetPedJump().IsPressed())
|
|
{
|
|
bJumpPressed = true;
|
|
m_uLastTimeJumpPressed = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
static dev_u32 SPRINT_JUMP_BREAKOUT_COOLDOWN = 300;
|
|
if(nState==STATE_JUMP)
|
|
{
|
|
if( fwTimer::GetTimeInMilliseconds() < (m_uLastTimeJumpPressed + SPRINT_JUMP_BREAKOUT_COOLDOWN))
|
|
{
|
|
bJumpPressed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bAutoVaultPressed = false;
|
|
static dev_u32 SPRINT_AUTO_VAULT_COOLDOWN = 200;
|
|
if(pControl->GetPedSprintIsDown())
|
|
{
|
|
m_uLastTimeSprintDown = fwTimer::GetTimeInMilliseconds();
|
|
bAutoVaultPressed = true;
|
|
}
|
|
else if( (fwTimer::GetTimeInMilliseconds() - SPRINT_AUTO_VAULT_COOLDOWN) < m_uLastTimeSprintDown )
|
|
{
|
|
bAutoVaultPressed = true;
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(VAULTING, fAutoVaultMinMBR, 0.95f, 0.0f, MOVEBLENDRATIO_SPRINT, 0.1f);
|
|
bool bReducingGait = pMotionTask && pMotionTask->IsGaitReduced() && (pPlayerPed->GetMotionData()->GetDesiredMbrY() > fAutoVaultMinMBR);
|
|
|
|
if(bReducingGait)
|
|
{
|
|
if(m_uReducingGaitTimeMS == 0)
|
|
{
|
|
m_uReducingGaitTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_uReducingGaitTimeMS = 0;
|
|
}
|
|
|
|
if(!bAutoVaultPressed)
|
|
{
|
|
bAutoVaultPressed = bReducingGait || (pPlayerPed->GetPedIntelligence()->GetEventScanner()->GetStaticMovementScanner().GetStaticCounter() > 0);
|
|
}
|
|
|
|
bool bAutoVaultEnabled = CTaskVault::GetUsingAutoVault() || ((CTaskVault::GetUsingAutoVaultOnPress() && bAutoVaultPressed) && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisablePlayerAutoVaulting));
|
|
|
|
static dev_u32 TIME_ELAPSED = 600;
|
|
u32 nTestTime = HasPlayerSelectedGunTask(pPlayerPed, nState) ? 0 : TIME_ELAPSED;
|
|
|
|
bool bDoManualClimbCheck = bForceVaultCheck || ((fwTimer::GetTimeInMilliseconds() - nTestTime) <= m_uLastTimeJumpPressed);
|
|
|
|
bool bAutoVaultOnly = bAutoVaultEnabled && !bDoManualClimbCheck && !bForceVaultCheck;
|
|
|
|
//! Do ground test.
|
|
if(!pPlayerPed->GetIsSwimming())
|
|
{
|
|
//! If purely an auto-vault test, make sure ped is properly grounded.
|
|
if(bAutoVaultOnly && !pPlayerPed->IsOnGround())
|
|
{
|
|
return false;
|
|
}
|
|
//! If in manual climb, be a bit more relaxed and climb if we have detected any valid ground z.
|
|
else if(bDoManualClimbCheck && pPlayerPed->GetGroundPos().z <= PED_GROUNDPOS_RESET_Z)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool bClimbDisabled = (pPlayerPed->GetPedType() == PEDTYPE_ANIMAL) || pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerVaulting);
|
|
|
|
if(!bClimbDisabled && (bAutoVaultEnabled || bDoManualClimbCheck) )
|
|
{
|
|
if(bAutoVaultOnly)
|
|
{
|
|
Vector3 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm(), 0.0f);
|
|
if( (vStickInput.Mag2() > 0.0f) && ((pPlayerPed->GetMotionData()->GetCurrentMbrY() > fAutoVaultMinMBR) || bReducingGait || (nState == STATE_JUMP)) )
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForAutoVaultClimb, true);
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForClimb, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForClimb, true);
|
|
}
|
|
|
|
sPedTestParams pedTestParams;
|
|
GetClimbDetectorParam( nState,
|
|
pPlayerPed,
|
|
pedTestParams,
|
|
bDoManualClimbCheck,
|
|
bAutoVaultOnly,
|
|
bAttemptingToVaultFromSwimming,
|
|
bHeavyWeapon,
|
|
bCanDoStandAutoStepUp);
|
|
|
|
// If we aren't running climb detection on consecutive frames, reset pending results as they will be stale. Need
|
|
// to re-run. This can happen if script disable climbing/inputs while we have a process in flight.
|
|
if( (fwTimer::GetFrameCount() - m_nLastFrameProcessedClimbDetector) > 1)
|
|
{
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().ResetPendingResults();
|
|
}
|
|
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().Process(NULL, &pedTestParams, &pedTestResults, nState != STATE_JUMP);
|
|
m_nLastFrameProcessedClimbDetector = fwTimer::GetFrameCount();
|
|
|
|
CClimbHandHoldDetected handHold;
|
|
bool bDetected = pPlayerPed->GetPedIntelligence()->GetClimbDetector().GetDetectedHandHold(handHold);
|
|
|
|
// Disable jumping if going to vault soon. Or if we have detected something that
|
|
// should block the jump (i.e. a wall). ... unless we have super jump enabled!
|
|
bool bSuperJumpEnabled = pPlayerPed->GetPlayerInfo() ? pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_SUPER_JUMP_ON) : false;
|
|
if( (bDetected && pedTestResults.GetGoingToVaultSoon()) || (pPlayerPed->GetPedIntelligence()->GetClimbDetector().IsJumpBlocked() && !bSuperJumpEnabled) )
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().ResetAutoVaultDelayTimers();
|
|
}
|
|
|
|
if((!pedTestResults.GetGoingToVaultSoon() && (bForcedJump || bJumpPressed || pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_VaultFromCover ) || bForceVaultCheck)) ||
|
|
(pedTestResults.GetCanVault() || pedTestResults.GetCanAutoJumpVault()) )
|
|
{
|
|
if(!pedTestResults.GetCanVault() && !pedTestResults.GetCanAutoJumpVault())
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerVaulting, true );
|
|
}
|
|
|
|
if(!pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping) ||
|
|
!pPlayerPed->GetPedResetFlag( CPED_RESET_FLAG_DisablePlayerVaulting))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::CheckForDropDown(CPed* pPlayerPed, CControl *pControl, bool bBiasRightSide)
|
|
{
|
|
if(!pControl)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
//! Don't drop down if we are going to vault soon.
|
|
if(m_VaultTestResults.GetGoingToVaultSoon() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!pPlayerPed->GetIsStanding())
|
|
return false;
|
|
|
|
//! And not when crouching and can't stand up.
|
|
if(pPlayerPed->GetIsCrouching() && !pPlayerPed->CanPedStandUp())
|
|
return false;
|
|
|
|
//! DMKH. Disallow from underwater.
|
|
CTaskMotionBase *pMotionTask = pPlayerPed->GetCurrentMotionTask();
|
|
if(pPlayerPed->GetIsSwimming() || (pMotionTask && pMotionTask->IsUnderWater()) || pPlayerPed->m_Buoyancy.GetStatus() != NOT_IN_WATER)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//! Allow dropdowns to be disabled by external systems (i.e. script).
|
|
if(pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableDropDowns))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//! Don't drop down if we have a jetpack on our back.
|
|
if(pPlayerPed->GetHasJetpackEquipped())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bDropDownEnabled = CTaskDropDown::GetUsingDropDowns();
|
|
if(bDropDownEnabled)
|
|
{
|
|
TUNE_GROUP_FLOAT(DROPDOWN, fDropDownMinMBR, 0.1f, 0.0f, MOVEBLENDRATIO_SPRINT, 0.1f);
|
|
|
|
bool bVaulting = false;
|
|
|
|
float fSpeed = GetPed()->GetMotionData()->GetCurrentMbrY();
|
|
if(GetState() == STATE_JUMP)
|
|
{
|
|
const CTaskVault* pVaultTask = static_cast<const CTaskVault*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VAULT));
|
|
if(pVaultTask)
|
|
{
|
|
bVaulting = true;
|
|
|
|
if(pVaultTask->GetVaultSpeed() == CTaskVault::VaultSpeed_Run)
|
|
{
|
|
fSpeed = MOVEBLENDRATIO_RUN;
|
|
}
|
|
else if(pVaultTask->GetVaultSpeed() == CTaskVault::VaultSpeed_Walk)
|
|
{
|
|
fSpeed = MOVEBLENDRATIO_WALK;
|
|
}
|
|
}
|
|
}
|
|
else if(GetState()==STATE_DROPDOWN)
|
|
{
|
|
const CTaskDropDown* pDropDownTask = static_cast<const CTaskDropDown*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_DROP_DOWN));
|
|
if(pDropDownTask)
|
|
{
|
|
bVaulting = true;
|
|
|
|
if(pDropDownTask->GetDropDownSpeed() == CTaskDropDown::DropDownSpeed_Run)
|
|
{
|
|
fSpeed = MOVEBLENDRATIO_RUN;
|
|
}
|
|
else if(pDropDownTask->GetDropDownSpeed() == CTaskDropDown::DropDownSpeed_Walk)
|
|
{
|
|
fSpeed = MOVEBLENDRATIO_WALK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// No need to test if not moving.
|
|
if (fSpeed<=MOVEBLENDRATIO_STILL)
|
|
return false;
|
|
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForDropDown, true);
|
|
|
|
TUNE_GROUP_BOOL(DROPDOWN, bDropDownAsync, true);
|
|
|
|
u8 nFlags = bDropDownAsync ? CDropDownDetector::DSF_AsyncTests : 0;
|
|
|
|
//! Need to be pressing run/sprint to auto-dive/parachute.
|
|
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
if(pPlayerInfo->ControlButtonSprint(CPlayerInfo::SPRINT_ON_FOOT, true) >= 1.0f)
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_TestForDive;
|
|
|
|
if(CTaskParachute::IsPedInStateToParachute(*pPlayerPed))
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_TestForPararachuteJump;
|
|
}
|
|
}
|
|
|
|
if(sm_Tunables.m_CanMountFromInAir)
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_TestForMount;
|
|
}
|
|
|
|
if(fSpeed >= MOVEBLENDRATIO_RUN)
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_PedRunning;
|
|
}
|
|
if(bVaulting)
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_FromVault;
|
|
}
|
|
if(bBiasRightSide)
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_BiasRightSide;
|
|
}
|
|
|
|
if(!pPlayerInfo->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_BEAST_JUMP_ON))
|
|
{
|
|
nFlags |= CDropDownDetector::DSF_TestForRagdollTeeter;
|
|
}
|
|
|
|
pPlayerPed->GetPedIntelligence()->GetDropDownDetector().Process(NULL, NULL, nFlags);
|
|
|
|
if(pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedDropDown())
|
|
{
|
|
bool bRagdollReaction = pPlayerPed->GetPedIntelligence()->GetDropDownDetector().GetDetectedDropDown().m_eDropType==eRagdollDrop;
|
|
|
|
if (!bRagdollReaction)
|
|
{
|
|
if(fSpeed <= fDropDownMinMBR)
|
|
return false;
|
|
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_StairsDetected))
|
|
return false;
|
|
|
|
#if ENABLE_DRUNK
|
|
if (pPlayerPed->IsDrunk())
|
|
return false;
|
|
#endif // ENABLE_DRUNK
|
|
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsHandCuffed))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_iJumpFlags = 0;
|
|
m_iJumpFlags.SetFlag(JF_DisableVault);
|
|
|
|
if(pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedDive())
|
|
{
|
|
m_iJumpFlags.SetFlag(JF_ForceDiveJump);
|
|
m_iJumpFlags.SetFlag(JF_AutoJump);
|
|
}
|
|
else if(pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedParachuteDrop())
|
|
{
|
|
m_iJumpFlags.SetFlag(JF_ForceParachuteJump);
|
|
m_iJumpFlags.SetFlag(JF_AutoJump);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Keeps a history of the last recored stick input
|
|
void CTaskPlayerOnFoot::UpdateMovementStickHistory( CControl * pControl )
|
|
{
|
|
if( m_fTimeSinceLastStickInput < MAX_STICK_INPUT_RECORD_TIME)
|
|
m_fTimeSinceLastStickInput += fwTimer::GetTimeStep();
|
|
|
|
CPed& rPed = *GetPed();
|
|
bool bShouldTryToKeepCoverPoint = true;
|
|
|
|
Vector2 vecStickInput;
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm();
|
|
float fCamOrient = camInterface::GetHeading(); // careful.. this looks dodgy to me - and could be ANY camera - DW
|
|
|
|
// GetNorm() with no parameters automatically applies default deadzoning.
|
|
if(vecStickInput.Mag2())
|
|
{
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
m_vLastStickInput = Vector3(-rage::Sinf(fStickAngle), rage::Cosf(fStickAngle), 0.0f);
|
|
m_vLastStickInput.Normalize();
|
|
m_fTimeSinceLastStickInput = 0.0f;
|
|
bShouldTryToKeepCoverPoint = false;
|
|
}
|
|
|
|
// Don't keep existing cover point if we moved the camera B*1316985
|
|
if (bShouldTryToKeepCoverPoint)
|
|
{
|
|
Vector2 vecCamInput;
|
|
vecCamInput.x = pControl->GetPedAimWeaponLeftRight().GetNorm();
|
|
vecCamInput.y = -pControl->GetPedAimWeaponLeftRight().GetNorm();
|
|
if (vecCamInput.Mag2())
|
|
{
|
|
bShouldTryToKeepCoverPoint = false;
|
|
}
|
|
}
|
|
|
|
// Moved the camera in cover away
|
|
if (bShouldTryToKeepCoverPoint)
|
|
{
|
|
if (rPed.GetCoverPoint())
|
|
{
|
|
const Vector3 vCamDir = camInterface::GetFront();
|
|
const Vector3 vCovDir = VEC3V_TO_VECTOR3(rPed.GetCoverPoint()->GetCoverDirectionVector());
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MAX_COV_DIR_DOT_CAM_DIR_TO_CONSIDER_OLD_POINT_VALID, 0.85f, -1.0f, 1.0f, 0.01f);
|
|
if (vCovDir.Dot(vCamDir) < MAX_COV_DIR_DOT_CAM_DIR_TO_CONSIDER_OLD_POINT_VALID)
|
|
{
|
|
bShouldTryToKeepCoverPoint = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if the vehicle we're taking cover against is moving
|
|
if (rPed.GetCoverPoint() && rPed.GetCoverPoint()->GetEntity() && rPed.GetCoverPoint()->GetEntity()->GetIsTypeVehicle())
|
|
{
|
|
if (!rPed.GetGroundPhysical() || rPed.GetGroundPhysical() != rPed.GetCoverPoint()->GetEntity())
|
|
{
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MAX_VEHICLE_VELOCITY_TO_CONSIDER_VALID_COVER, 0.1f, 0.0f, 0.5f, 0.01f);
|
|
if (static_cast<CVehicle*>(rPed.GetCoverPoint()->GetEntity())->GetVelocity().Mag2() > MAX_VEHICLE_VELOCITY_TO_CONSIDER_VALID_COVER)
|
|
{
|
|
bShouldTryToKeepCoverPoint = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
rPed.SetPedResetFlag(CPED_RESET_FLAG_KeepCoverPoint, bShouldTryToKeepCoverPoint);
|
|
}
|
|
|
|
s32 CTaskPlayerOnFoot::GetBreakOutToMovementState(CPed* pPlayerPed)
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
weaponAssert(pPlayerPed->GetWeaponManager());
|
|
CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
const bool bHeavyWeapon = pWeapon && pWeapon->GetWeaponInfo()->GetIsHeavy();
|
|
|
|
if(pPlayerPed->IsOnGround())
|
|
{
|
|
//! Don't allow jumping breakout (unless during beast mode).
|
|
if(!pPlayerPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_BEAST_JUMP_ON))
|
|
{
|
|
pPlayerPed->SetPedResetFlag( CPED_RESET_FLAG_DisablePlayerJumping, true );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// JETPACK
|
|
//-------------------------------------------------------------------------
|
|
if(CheckForUsingJetpack(pPlayerPed))
|
|
{
|
|
return STATE_JETPACK;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// JUMPING INTO COVER
|
|
//-------------------------------------------------------------------------
|
|
if(!bHeavyWeapon && !m_bPlayerExitedCoverThisUpdate && !pPlayerPed->GetIsSwimming() && !pPlayerPed->GetIsSkiing() &&
|
|
#if ENABLE_DRUNK
|
|
!pPlayerPed->IsDrunk() &&
|
|
#endif // ENABLE_DRUNK
|
|
pPlayerPed->GetWeaponManager() && CTaskEnterCover::AreCoreCoverClipsetsLoaded(pPlayerPed))
|
|
{
|
|
if(CheckForNearbyCover(pPlayerPed, CSF_DefaultFlags, m_nCachedBreakOutToMovementState == STATE_USE_COVER))
|
|
{
|
|
return STATE_USE_COVER;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CLIMBING
|
|
//-------------------------------------------------------------------------
|
|
if( DoJumpCheck(pPlayerPed, pControl, bHeavyWeapon))
|
|
{
|
|
return STATE_JUMP;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// DROPDOWNS
|
|
//-------------------------------------------------------------------------
|
|
if(!bHeavyWeapon && CheckForDropDown(pPlayerPed, pControl, true))
|
|
{
|
|
if(pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedParachuteDrop() || pPlayerPed->GetPedIntelligence()->GetDropDownDetector().HasDetectedDive())
|
|
{
|
|
return STATE_JUMP;
|
|
}
|
|
else
|
|
{
|
|
return STATE_DROPDOWN;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// ENTERING VEHICLES.
|
|
//-------------------------------------------------------------------------
|
|
s8 iEnterVehicleState = CheckShouldEnterVehicle(pPlayerPed,pControl, m_nCachedBreakOutToMovementState == STATE_GET_IN_VEHICLE);
|
|
if(iEnterVehicleState != -1)
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_WantsToEnterVehicleFromAiming, true);
|
|
return (ePlayerOnFootState)iEnterVehicleState;
|
|
}
|
|
}
|
|
|
|
return STATE_INVALID;
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::KeepWaitingForLadder(CPed* pPed, float fBlockHeading)
|
|
{
|
|
CControl* pControl = pPed->GetControlFromPlayer();
|
|
if(pControl)
|
|
{
|
|
Vector2 vecStickInput;
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm(CTaskMovePlayer::ms_fDeadZone);
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm(CTaskMovePlayer::ms_fDeadZone);
|
|
|
|
if(vecStickInput.Mag() > CTaskMovePlayer::ms_fStickInCentreMag)
|
|
{
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(*pPed);
|
|
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
|
|
if(fabs(fStickAngle - fBlockHeading) > 1.57f)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CTaskPlayerOnFoot::CheckForArmedMeleeAction( CPed* pPlayerPed )
|
|
{
|
|
// We should have never gotten here but clean up if we did
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
// This is possible when a takedown was created on the same frame as the player was hit... so I guess let it happen normally
|
|
delete m_pMeleeRequestTask;
|
|
m_pMeleeRequestTask = NULL;
|
|
}
|
|
|
|
// Do not allow melee actions while HUD is up
|
|
if( CNewHud::IsShowingHUDMenu() )
|
|
return;
|
|
|
|
if( GetState() == STATE_SWAP_WEAPON )
|
|
return;
|
|
|
|
if( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_SWAP_WEAPON ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't allow melee actions in air defence spheres.
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_InAirDefenceSphere))
|
|
{
|
|
return;
|
|
}
|
|
|
|
CTaskAimGunOnFoot* pAimGunTask = static_cast<CTaskAimGunOnFoot*>( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_AIM_GUN_ON_FOOT ) );
|
|
if( pAimGunTask && pAimGunTask->GetWaitingToSwitchWeapon() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Assert( pPlayerPed );
|
|
Assert( pPlayerPed->GetPedIntelligence() );
|
|
|
|
|
|
CWeapon* pEquippedWeapon = pPlayerPed->GetWeaponManager() ? pPlayerPed->GetWeaponManager()->GetEquippedWeapon() : NULL;
|
|
const CWeaponInfo* pEquippedWeaponInfo = pEquippedWeapon ? pEquippedWeapon->GetWeaponInfo() : NULL;
|
|
if( pEquippedWeaponInfo && (!pEquippedWeaponInfo->GetIsMelee() || pEquippedWeaponInfo->GetCanBeAimedLikeGunWithoutFiring()) && pEquippedWeaponInfo->GetAllowCloseQuarterKills() )
|
|
{
|
|
bool bLightAttackPressed = false, bAlternateAttackPressed = false;
|
|
CPlayerInfo::GetCachedMeleeInputs( bLightAttackPressed, bAlternateAttackPressed );
|
|
|
|
// Find the best applicable target without running the melee target evaluation
|
|
CPlayerPedTargeting& rTargeting = pPlayerPed->GetPlayerInfo()->GetTargeting();
|
|
CEntity* pTargetEntity = NULL;
|
|
CPed* pClosestPed = pPlayerPed->GetPedIntelligence()->GetClosestPedInRange();
|
|
if( pClosestPed && !CActionManager::ArePedsFriendlyWithEachOther( pPlayerPed, pClosestPed ))
|
|
pTargetEntity = pClosestPed;
|
|
else if( rTargeting.GetIsFineAiming() )
|
|
pTargetEntity = rTargeting.GetFreeAimTarget();
|
|
else
|
|
pTargetEntity = rTargeting.GetTarget();
|
|
|
|
//! if we are trying to shoot our target, don't allow melee move against a different ped.
|
|
if(pPlayerPed->IsLocalPlayer() && CPlayerInfo::IsAiming(false) && bAlternateAttackPressed && pClosestPed)
|
|
{
|
|
CEntity *pLockOnTarget = rTargeting.GetLockOnTarget();
|
|
if(pLockOnTarget && (pLockOnTarget != pClosestPed))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//! If we have lined up a shot with a non friendly ped, let us shoot instead.
|
|
CEntity *pFreeAimTarget = rTargeting.GetFreeAimTargetRagdoll() ? rTargeting.GetFreeAimTargetRagdoll() : rTargeting.GetFreeAimTarget();
|
|
if(pFreeAimTarget && (pFreeAimTarget !=pClosestPed))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Only if we have a target should we check for an armed takedown
|
|
if( pTargetEntity && pTargetEntity->GetIsTypePed() &&
|
|
DistSquared( pTargetEntity->GetTransform().GetPosition(), pPlayerPed->GetTransform().GetPosition() ).Getf() < ms_fMinTargetCheckDistSq )
|
|
{
|
|
// don't allow melee attacks on ghosted players
|
|
if (pTargetEntity && NetworkInterface::IsGameInProgress() && NetworkInterface::AreInteractionsDisabledInMP(*pPlayerPed, *pTargetEntity))
|
|
{
|
|
return;
|
|
}
|
|
|
|
CPed* pTargetPed = static_cast<CPed*>(pTargetEntity);
|
|
|
|
// If the weapon is blocked by the target just test for a takedown
|
|
CTaskWeaponBlocked* pWeaponBlockedTask = static_cast<CTaskWeaponBlocked*>( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_WEAPON_BLOCKED ) );
|
|
if( pWeaponBlockedTask )
|
|
{
|
|
// Peds marked as lower priority melee targets should be avoided here
|
|
if( !pTargetEntity ||
|
|
!pTargetEntity->GetIsTypePed() ||
|
|
!static_cast<CPed*>(pTargetEntity)->GetPedResetFlag(CPED_RESET_FLAG_IsLowerPriorityMeleeTarget) )
|
|
{
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed, pTargetEntity, true, false, false, true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check to see if the player is attempting to fire
|
|
const bool bIsLowerPriorityMeleeTarget = pTargetPed->GetPedResetFlag(CPED_RESET_FLAG_IsLowerPriorityMeleeTarget);
|
|
CTaskAimGunOnFoot* pAimGunTask = static_cast<CTaskAimGunOnFoot*>( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_AIM_GUN_ON_FOOT ) );
|
|
if( pAimGunTask && pAimGunTask->GetWillFire() && !bIsLowerPriorityMeleeTarget )
|
|
{
|
|
// Reset the will fire bool in the case that we found a melee action and meant to fire
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed, pTargetEntity, true, false, false, true );
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
pAimGunTask->ResetWillFire();
|
|
}
|
|
}
|
|
else if( ShouldMeleeTargetPed(pTargetPed, pEquippedWeapon, bIsLowerPriorityMeleeTarget) )
|
|
{
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed, pTargetEntity, true, false, false, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we do not have a melee action nor target, check to see if there is a melee target around
|
|
if( !m_pMeleeRequestTask && (bLightAttackPressed || (bAlternateAttackPressed && !pTargetEntity)) )
|
|
{
|
|
// CNC: Ignore this min distance threshold check for cops as we want to trigger the long barge melee actions.
|
|
// FindMeleeTarget is already clamped to a max range of 10m.
|
|
bool bIgnoreMinDistanceThreshold = false;
|
|
if (NetworkInterface::IsInCopsAndCrooks() && !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsAiming))
|
|
{
|
|
const CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
if (pPlayerInfo && pPlayerInfo->GetArcadeInformation().GetTeam() == eArcadeTeam::AT_CNC_COP)
|
|
{
|
|
bIgnoreMinDistanceThreshold = true;
|
|
}
|
|
}
|
|
|
|
pTargetEntity = rTargeting.FindMeleeTarget( pPlayerPed, pEquippedWeaponInfo, false, false );
|
|
if( pTargetEntity && pTargetEntity->GetIsTypePed() &&
|
|
(bIgnoreMinDistanceThreshold || DistSquared( pTargetEntity->GetTransform().GetPosition(), pPlayerPed->GetTransform().GetPosition() ).Getf() < ms_fMinTargetCheckDistSq) )
|
|
{
|
|
CPed* pTargetPed = static_cast<CPed*>(pTargetEntity);
|
|
if( ShouldMeleeTargetPed(pTargetPed, pEquippedWeapon, pTargetPed->GetPedResetFlag(CPED_RESET_FLAG_IsLowerPriorityMeleeTarget)) )
|
|
{
|
|
// Reset the will fire bool in the case that we found a melee action and meant to fire
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed, pTargetEntity, true, false, false, true );
|
|
if( m_pMeleeRequestTask )
|
|
{
|
|
// Check to see if the player is attempting to fire
|
|
CTaskAimGunOnFoot* pAimGunTask = static_cast<CTaskAimGunOnFoot*>( pPlayerPed->GetPedIntelligence()->FindTaskActiveByType( CTaskTypes::TASK_AIM_GUN_ON_FOOT ) );
|
|
if( pAimGunTask && pAimGunTask->GetWillFire() )
|
|
{
|
|
pAimGunTask->ResetWillFire();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Last ditch effort to find a move without a target and light attack was pressed (or alternate attack if weapon can't fire)
|
|
if( !m_pMeleeRequestTask && (bLightAttackPressed || (bAlternateAttackPressed && pEquippedWeaponInfo->GetCanBeAimedLikeGunWithoutFiring())) )
|
|
{
|
|
if( !pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_RELOAD_GUN ) ||
|
|
!pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_COVER ) )
|
|
{
|
|
if( !pEquippedWeapon->GetCanReload() || pEquippedWeapon->GetAmmoTotal() == 0 )
|
|
m_pMeleeRequestTask = CTaskMelee::CheckForAndGetMeleeAmbientMove( pPlayerPed, NULL, false, false, false, true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskPlayerOnFoot::ShouldMeleeTargetPed(const CPed* pTargetPed, const CWeapon* pEquippedWeapon, bool bIsLowerPriorityMeleeTarget)
|
|
{
|
|
bool bLightAttackPressed = false, bAlternateAttackPressed = false;
|
|
CPlayerInfo::GetCachedMeleeInputs( bLightAttackPressed, bAlternateAttackPressed );
|
|
|
|
// We only want to hit low priority targets if we can't reload and we have hit the light attack button
|
|
if( bLightAttackPressed && (!bIsLowerPriorityMeleeTarget || (!pEquippedWeapon->GetCanReload() && !pTargetPed->GetPedResetFlag(CPED_RESET_FLAG_IsReloading))) )
|
|
{
|
|
return true;
|
|
}
|
|
// Otherwise should we check the based on the light attack input?
|
|
else if( bAlternateAttackPressed && !bIsLowerPriorityMeleeTarget )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CTaskPlayerIdles::CTaskPlayerIdles()
|
|
: m_bHasSpaceToPlayIdleAnims(true)
|
|
, m_bIsMoving(false)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_PLAYER_IDLES);
|
|
}
|
|
|
|
CTaskPlayerIdles::~CTaskPlayerIdles()
|
|
{
|
|
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerIdles::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
FSM_Begin
|
|
|
|
FSM_State(State_Start)
|
|
FSM_OnUpdate
|
|
Start_OnUpdate();
|
|
|
|
FSM_State(State_MovementAndAmbientClips)
|
|
FSM_OnEnter
|
|
MovementAndAmbientClips_OnEnter();
|
|
FSM_OnUpdate
|
|
return MovementAndAmbientClips_OnUpdate();
|
|
FSM_OnExit
|
|
return MovementAndAmbientClips_OnExit();
|
|
|
|
FSM_State(State_Finish)
|
|
FSM_OnUpdate
|
|
return FSM_Quit;
|
|
|
|
FSM_End
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerIdles::Start_OnUpdate()
|
|
{
|
|
SetState(State_MovementAndAmbientClips);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
void CTaskPlayerIdles::MovementAndAmbientClips_OnEnter()
|
|
{
|
|
CTaskMovePlayer* pMovePlayerTask = rage_new CTaskMovePlayer();
|
|
SetNewTask(rage_new CTaskComplexControlMovement(pMovePlayerTask, rage_new CTaskAmbientClips(CTaskAmbientClips::Flag_PlayIdleClips, CONDITIONALANIMSMGR.GetConditionalAnimsGroup("PLAYER_IDLES"))));
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerIdles::MovementAndAmbientClips_OnUpdate()
|
|
{
|
|
CPed* pPed = GetPed();
|
|
|
|
if (GetIsFlagSet(aiTaskFlags::SubTaskFinished))
|
|
{
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
if( GetSubTask()->GetTaskType() == CTaskTypes::TASK_COMPLEX_CONTROL_MOVEMENT &&
|
|
GetSubTask()->GetSubTask() )
|
|
{
|
|
bool bPhoneOnScreen = CTaskPlayerOnFoot::CheckForUseMobilePhone(*pPed);
|
|
// If the player isn't texting but the phone is out, switch to running the texting clip
|
|
if( GetSubTask()->GetSubTask()->GetTaskType() == CTaskTypes::TASK_AMBIENT_CLIPS &&
|
|
bPhoneOnScreen)
|
|
{
|
|
Vector2 vMBR;
|
|
pPed->GetMotionData()->GetCurrentMoveBlendRatio(vMBR);
|
|
|
|
if(!pPed->GetIsCrouching() || vMBR.Mag2() < 0.1f)
|
|
{
|
|
CTaskMobilePhone::CreateOrResumeMobilePhoneTask(*GetPed());
|
|
}
|
|
}
|
|
|
|
if (pPed->IsInMotion() != m_bIsMoving)
|
|
{
|
|
m_bIsMoving = pPed->IsInMotion();
|
|
|
|
if (!m_bIsMoving)
|
|
{
|
|
Vector3 v3CapsuleStart = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
Vector3 v3CapsuleEnd = v3CapsuleStart;
|
|
const float fCapsuleRadius = 0.56f; // This is the maximum width for Idle motion. [11/29/2012 mdawe]
|
|
|
|
const CBaseCapsuleInfo* pPedCapsule = pPed->GetCapsuleInfo();
|
|
if (pPedCapsule)
|
|
{
|
|
const float fHeight = pPedCapsule->GetMaxSolidHeight();
|
|
v3CapsuleStart.z -= fHeight / 2.0f;
|
|
v3CapsuleEnd.z += fHeight / 2.0f;
|
|
}
|
|
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleTest;
|
|
|
|
capsuleTest.SetIncludeFlags(ArchetypeFlags::GTA_VEHICLE_TYPE | ArchetypeFlags::GTA_OBJECT_INCLUDE_TYPES);
|
|
capsuleTest.SetContext(WorldProbe::LOS_GeneralAI);
|
|
capsuleTest.SetTypeFlags(ArchetypeFlags::GTA_AI_TEST);
|
|
capsuleTest.SetIsDirected(false);
|
|
capsuleTest.SetExcludeEntity(pPed);
|
|
capsuleTest.SetCapsule(v3CapsuleStart, v3CapsuleEnd, fCapsuleRadius);
|
|
|
|
m_bHasSpaceToPlayIdleAnims = !WorldProbe::GetShapeTestManager()->SubmitTest(capsuleTest);
|
|
#if DEBUG_DRAW
|
|
const static bool bDrawCapsule = false;
|
|
if (bDrawCapsule)
|
|
{
|
|
CTask::ms_debugDraw.AddCapsule(RCC_VEC3V(v3CapsuleStart), RCC_VEC3V(v3CapsuleEnd), fCapsuleRadius, Color_DarkOrange, 10000, 0, false);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskPlayerIdles::MovementAndAmbientClips_OnExit()
|
|
{
|
|
// Stop looking at my vehicle.
|
|
CPed* pPed = GetPed();
|
|
if(pPed)
|
|
{
|
|
static const u32 uLookAtMyVehicleHash = ATSTRINGHASH("LookAtMyVehicle", 0x79037202);
|
|
pPed->GetIkManager().AbortLookAt(500, uLookAtMyVehicleHash);
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
#if !__FINAL
|
|
const char * CTaskPlayerIdles::GetStaticStateName( s32 iState )
|
|
{
|
|
taskAssert(iState >= State_Start && iState <= State_Finish);
|
|
|
|
switch (iState)
|
|
{
|
|
case State_Start: return "State_Start";
|
|
case State_MovementAndAmbientClips: return "State_MovementAndAmbientClips";
|
|
case State_Finish: return "State_Finish";
|
|
default: taskAssert(0);
|
|
}
|
|
|
|
return "State_Invalid";
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
float CTaskMovePlayer::ms_fWalkBlend = 1.0f;
|
|
float CTaskMovePlayer::ms_fRunBlend = 2.0f;
|
|
float CTaskMovePlayer::ms_fSprintBlend = 3.0f;
|
|
float CTaskMovePlayer::ms_fStrafeMinMoveBlendRatio = 0.15f;
|
|
float CTaskMovePlayer::ms_fSmallMoveBlendRatioToDoIdleTurn = 0.35f;
|
|
float CTaskMovePlayer::ms_fTimeStickHeldBeforeDoingIdleTurn = 0.5f;
|
|
bool CTaskMovePlayer::ms_bStickyRunButton = false;
|
|
bank_float CTaskMovePlayer::ms_fStickInCentreMag = 0.45f;
|
|
bool CTaskMovePlayer::ms_bAllowRunningWhilstStrafing = true;
|
|
bool CTaskMovePlayer::ms_bDefaultNoSprintingInInteriors = true;
|
|
bool CTaskMovePlayer::ms_bScriptOverrideNoRunningOnPhone = false;
|
|
bool CTaskMovePlayer::ms_bUseMultiPlayerControlInSinglePlayer = false;
|
|
bank_bool CTaskMovePlayer::ms_bFlipSkiControlsWhenBackwards = true;
|
|
float CTaskMovePlayer::ms_fUnderwaterIdleAutoLevelTime = 6.0f;
|
|
dev_float CTaskMovePlayer::ms_fDeadZone = 0.33f;
|
|
dev_float CTaskMovePlayer::ms_fMinDepthForPlayerFish = -180.0f;
|
|
|
|
//
|
|
CTaskMovePlayer::CTaskMovePlayer()
|
|
: CTaskMove(MOVEBLENDRATIO_STILL),
|
|
m_bStickHasReturnedToCentre(true),
|
|
m_fUnderwaterGlideTimer(0.0f),
|
|
m_fSwitchStrokeTimerOnSurface(0.0f),
|
|
m_iStroke(0),
|
|
m_fUnderwaterIdleTimer(0.0f),
|
|
m_pAssistedMovementControl(NULL),
|
|
m_fMaxMoveBlendRatio(MOVEBLENDRATIO_SPRINT),
|
|
m_fLockedStickHeading(-1.0f),
|
|
m_fLockedHeading(0.0f),
|
|
m_fStdPreviousDesiredHeading(FLT_MAX),
|
|
m_fStickAngle(0.0f),
|
|
m_bStickAngleTweaked(false)
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
, m_bWasSprintingOnPCMouseAndKeyboard(false)
|
|
#endif // FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
{
|
|
#if __PLAYER_ASSISTED_MOVEMENT
|
|
m_pAssistedMovementControl = rage_new CPlayerAssistedMovementControl();
|
|
#endif
|
|
|
|
SetInternalTaskType(CTaskTypes::TASK_MOVE_PLAYER);
|
|
}
|
|
|
|
CTaskMovePlayer::~CTaskMovePlayer()
|
|
{
|
|
#if __PLAYER_ASSISTED_MOVEMENT
|
|
delete m_pAssistedMovementControl;
|
|
#endif
|
|
}
|
|
|
|
aiTask* CTaskMovePlayer::Copy() const
|
|
{
|
|
//Create the task.
|
|
CTaskMovePlayer* pTask = rage_new CTaskMovePlayer();
|
|
|
|
//Copy the max move/blend ratio.
|
|
pTask->SetMaxMoveBlendRatio(m_fMaxMoveBlendRatio);
|
|
|
|
return pTask;
|
|
}
|
|
|
|
CTask::FSM_Return CTaskMovePlayer::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
FSM_Begin
|
|
FSM_State(Running)
|
|
FSM_OnUpdate
|
|
return StateRunning_OnUpdate(pPed);
|
|
FSM_End
|
|
}
|
|
|
|
CTask::FSM_Return CTaskMovePlayer::StateRunning_OnUpdate(CPed* pPed)
|
|
{
|
|
if(!pPed->IsPlayer())
|
|
{
|
|
Assertf(false, "CTaskMovePlayer given to non-player ped");
|
|
return FSM_Quit;
|
|
}
|
|
|
|
if (CPhoneMgr::CamGetState() && pPed->IsLocalPlayer())
|
|
{
|
|
pPed->GetMotionData()->SetCurrentMoveBlendRatio(0.0f, 0.0f);
|
|
pPed->GetMotionData()->SetDesiredMoveBlendRatio(0.0f, 0.0f);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
if( pPed->GetPedResetFlag( CPED_RESET_FLAG_PlacingCharge ) )
|
|
{
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// Let the movement system know that the player is in direct control of this ped
|
|
pPed->GetMotionData()->SetPlayerHasControlOfPedThisFrame(true);
|
|
|
|
if(pPed->IsStrafing() || pPed->GetPedResetFlag(CPED_RESET_FLAG_ForcePedToStrafe))
|
|
//|| pControl->GetPedTarget().IsDown())
|
|
{
|
|
m_fStdPreviousDesiredHeading = FLT_MAX;
|
|
ProcessStrafeMove(pPed);
|
|
}
|
|
else
|
|
{
|
|
CTaskMotionBase* pCurrentMotionTask = pPed->GetCurrentMotionTask();
|
|
if (pCurrentMotionTask->GetTaskType() == CTaskTypes::TASK_ON_FOOT_FISH)
|
|
{
|
|
pPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(false);
|
|
m_fStdPreviousDesiredHeading = FLT_MAX;
|
|
ProcessFishMove(*pPed);
|
|
}
|
|
else if (pCurrentMotionTask->GetTaskType() == CTaskTypes::TASK_ON_FOOT_BIRD)
|
|
{
|
|
pPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(false);
|
|
m_fStdPreviousDesiredHeading = FLT_MAX;
|
|
ProcessBirdMove(*pPed);
|
|
}
|
|
else if (pCurrentMotionTask->IsInWater())
|
|
{
|
|
pPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(false);
|
|
m_fStdPreviousDesiredHeading = FLT_MAX;
|
|
ProcessSwimMove(pPed);
|
|
}
|
|
else
|
|
{
|
|
// Prevent player input if running c4 task
|
|
if (!pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_BOMB))
|
|
{
|
|
ProcessStdMove(pPed);
|
|
}
|
|
else
|
|
{
|
|
pPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't allow the crouch status to be changed whilst transitioning
|
|
s32 iMotionState = pPed->GetPrimaryMotionTask()->GetState();
|
|
const bool bDisableCrouchToggle = pPed->GetIsSwimming() || ((pPed->GetPrimaryMotionTask()->GetTaskType() == CTaskTypes::TASK_MOTION_PED) && (iMotionState == CTaskMotionPed::State_CrouchToStand || iMotionState == CTaskMotionPed::State_StandToCrouch));
|
|
|
|
if (!bDisableCrouchToggle)
|
|
{
|
|
pPed->GetPlayerInfo()->ControlButtonDuck();
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CTaskMovePlayer::Debug() const
|
|
{
|
|
if(GetSubTask())
|
|
GetSubTask()->Debug();
|
|
}
|
|
#endif
|
|
|
|
void CTaskMovePlayer::DoAbort(const AbortPriority UNUSED_PARAM(iPriority), const aiEvent* UNUSED_PARAM(pEvent))
|
|
{
|
|
}
|
|
|
|
void CTaskMovePlayer::ProcessStdMove(CPed* pPlayerPed)
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
Vector2 vecStickInput;
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm(ms_fDeadZone);
|
|
// y stick results are positive for down (pulling back) and negative for up, so reverse to match world y direction
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm(ms_fDeadZone);
|
|
|
|
#if RSG_ORBIS
|
|
TUNE_GROUP_FLOAT(PLAYER_STICK_TUNE, STRAFE_POW, 0.75f, 0.f, 10.f, 0.01f);
|
|
vecStickInput.x = powf(Abs(vecStickInput.x), STRAFE_POW) * Sign(vecStickInput.x);
|
|
vecStickInput.x = Clamp(vecStickInput.x, -1.f, 1.f);
|
|
vecStickInput.y = powf(Abs(vecStickInput.y), STRAFE_POW) * Sign(vecStickInput.y);
|
|
vecStickInput.y = Clamp(vecStickInput.y, -1.f, 1.f);
|
|
#endif // RSG_ORBIS
|
|
|
|
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
// B*2325743: Reset toggle run value when sprinting so we will drop back into run if we stop.
|
|
if (m_bWasSprintingOnPCMouseAndKeyboard && pControl->WasKeyboardMouseLastKnownSource() && pControl->GetPedSprint().IsDown())
|
|
{
|
|
pPlayerPed->GetControlFromPlayer()->ClearToggleRun();
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// No longer disabling left stick input
|
|
// TODO: remove if no longer needed
|
|
const bool bInFpsMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
bool bIsFpsModeSprinting = false;
|
|
if(bInFpsMode && !pPlayerInfo->GetSimulateGaitInputOn())
|
|
{
|
|
bool bSprinting = pPlayerPed->GetMotionData()->GetIsDesiredSprinting(pPlayerPed->GetMotionData()->GetFPSCheckRunInsteadOfSprint());
|
|
if(bSprinting)
|
|
{
|
|
if(!camInterface::GetGameplayDirector().IsFirstPersonShooterLeftStickControl() || vecStickInput.Mag2() == 0.0f)
|
|
{
|
|
// Disable left stick input when sprinting using first person shooter camera.
|
|
vecStickInput.Set(0.0f, 1.0f);
|
|
}
|
|
bIsFpsModeSprinting = true;
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
float moveBlendRatio = 0.0f;
|
|
// don't let the player walk when attached... unless they are attached to the ground.
|
|
if(!pPlayerPed->GetIsAttached() || pPlayerPed->GetIsAttachedToGround())
|
|
moveBlendRatio = vecStickInput.Mag();// / ms_fStickRange;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// B*2099984: CTaskMovePlayer::StateRunning_OnUpdate gets in here for a frame when transitioning between aim/swim motion tasks.
|
|
// This was causing MBR to be reset to 0 if no stick input. Safest fix for now just update the MBR based on the sprint button.
|
|
if (pPlayerPed->GetIsFPSSwimming() && pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_FPSSwimUseSwimMotionTask))
|
|
{
|
|
float fSprintResult = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_UNDER_WATER, true);
|
|
moveBlendRatio = fSprintResult;
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
if(moveBlendRatio > 1.0f)
|
|
moveBlendRatio = 1.0f;
|
|
|
|
//! Increase to run speed when in action mode.
|
|
// if(pPlayerPed->IsBeingForcedToRun())
|
|
// {
|
|
// static dev_float s_fRescalePower = 3.0f;
|
|
// moveBlendRatio = powf(moveBlendRatio, s_fRescalePower);
|
|
// }
|
|
|
|
if(moveBlendRatio < ms_fStickInCentreMag)
|
|
m_bStickHasReturnedToCentre = true;
|
|
|
|
// get the orientation of the FIRST follow ped camera, for player controls
|
|
float fCamOrient = fwAngle::LimitRadianAngle(camInterface::GetPlayerControlCamHeading(*pPlayerPed));
|
|
|
|
//Clone the camera pitch for the ped pitch if the move blender allows it.
|
|
float fCamPitch = 0.0f;
|
|
// if(pPlayerPed->GetMotionData() && pPlayerPed->GetMotionData()->ShouldCameraControlPitch())
|
|
// {
|
|
// fCamPitch = camInterface::GetPlayerControlCamPitch(*pPlayerPed);
|
|
// }
|
|
|
|
if((moveBlendRatio > 0.0f) || (fCamPitch == 0.0f)) //Always allow the pitch to be reset.
|
|
{
|
|
pPlayerPed->SetDesiredPitch(fCamPitch);
|
|
}
|
|
|
|
if(moveBlendRatio > 0.0f)
|
|
{
|
|
// reset simulated gait.
|
|
if(pPlayerInfo->GetSimulateGaitInputOn())
|
|
{
|
|
if(!pPlayerInfo->GetSimulateGaitNoInputInterruption() || pPlayerInfo->GetSimulateGaitTimerCount() > pPlayerInfo->GetSimulateGaitDuration())
|
|
{
|
|
pPlayerInfo->ResetSimulateGaitInput();
|
|
}
|
|
else
|
|
{
|
|
// Update the timer
|
|
pPlayerInfo->SetSimulateGaitTimerCount(pPlayerInfo->GetSimulateGaitTimerCount() + fwTimer::GetTimeStep());
|
|
}
|
|
}
|
|
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);
|
|
bool bPlayeReversingAsQuadAnimal = false;
|
|
if (pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
if (const CTaskQuadLocomotion* pQuadMoveTask = static_cast<const CTaskQuadLocomotion*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByTreeAndType(PED_TASK_TREE_MOTION, CTaskTypes::TASK_ON_FOOT_QUAD)))
|
|
{
|
|
bPlayeReversingAsQuadAnimal = pQuadMoveTask->IsReversing();
|
|
}
|
|
}
|
|
|
|
if (bPlayeReversingAsQuadAnimal)
|
|
{
|
|
float fStickThreshold = DtoR * 90.f;
|
|
if (abs(fStickAngle) > fStickThreshold)
|
|
{
|
|
float fPlusMinus = fStickAngle > 0 ? -1.0f : 1.0f;
|
|
float fDelta = abs(fStickAngle) - fStickThreshold;
|
|
fStickAngle = Clamp(fStickAngle, -fStickThreshold, fStickThreshold);
|
|
fStickAngle += (fPlusMinus * fDelta);
|
|
}
|
|
|
|
fStickAngle = fStickAngle + fwAngle::LimitRadianAngle(pPlayerPed->GetCurrentHeading());;
|
|
}
|
|
else
|
|
{
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
}
|
|
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
|
|
if(m_fStdPreviousDesiredHeading == FLT_MAX)
|
|
pPlayerPed->SetDesiredHeading(fStickAngle);
|
|
else
|
|
{
|
|
float fAngleDiff = SubtractAngleShorter(m_fStdPreviousDesiredHeading, fStickAngle);
|
|
if(Abs(fAngleDiff) < PI * 0.9f)
|
|
{
|
|
pPlayerPed->SetDesiredHeading(fStickAngle);
|
|
}
|
|
}
|
|
m_fStdPreviousDesiredHeading = fStickAngle;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if( bIsFpsModeSprinting )
|
|
{
|
|
// Turn to camera heading when sprinting using first person shooter camera.
|
|
pPlayerPed->SetDesiredHeading(fCamOrient);
|
|
m_fStdPreviousDesiredHeading = fCamOrient;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
else if(pPlayerInfo->GetSimulateGaitInputOn()) // if we want to simulate player's movement
|
|
{
|
|
float fDuration = pPlayerInfo->GetSimulateGaitDuration();
|
|
float fTimerCount = pPlayerInfo->GetSimulateGaitTimerCount();
|
|
if(fDuration < 0.0f || fDuration >= fTimerCount )
|
|
{
|
|
vecStickInput.x = 0.0f;
|
|
vecStickInput.y = 1.0f;
|
|
|
|
float fHeading = pPlayerInfo->GetSimulateGaitHeading();
|
|
if(pPlayerInfo->GetSimulateGaitUseRelativeHeading())
|
|
{
|
|
fHeading = fwAngle::LimitRadianAngle(fHeading + pPlayerInfo->GetSimulateGaitStartHeading());
|
|
pPlayerPed->SetDesiredHeading(fHeading);
|
|
}
|
|
else
|
|
{
|
|
fHeading = fwAngle::LimitRadianAngle(fHeading);
|
|
pPlayerPed->SetDesiredHeading(fHeading);
|
|
}
|
|
|
|
fTimerCount += fwTimer::GetTimeStep();
|
|
pPlayerInfo->SetSimulateGaitTimerCount(fTimerCount);
|
|
}
|
|
else
|
|
{
|
|
pPlayerInfo->ResetSimulateGaitInput();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fStdPreviousDesiredHeading = FLT_MAX;
|
|
|
|
// JR - for the most part we are taking out the idle turns for animals as it feels better.
|
|
if (fabs(moveBlendRatio) < SMALL_FLOAT && pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
pPlayerPed->SetDesiredHeading(pPlayerPed->GetCurrentHeading());
|
|
}
|
|
}
|
|
|
|
const bool bUseButtonBashing = true;
|
|
|
|
// if we want to simulate player's movement
|
|
if(pPlayerInfo->GetSimulateGaitInputOn())
|
|
{
|
|
moveBlendRatio = pPlayerInfo->GetSimulateGaitMoveBlendRatio();
|
|
}
|
|
else
|
|
{
|
|
float fSprintResult = pPlayerInfo->ControlButtonSprint(CPlayerInfo::SPRINT_ON_FOOT, bUseButtonBashing);
|
|
|
|
//DEBUG
|
|
TUNE_GROUP_BOOL(PED_MOVEMENT, bConstantSprint, false);
|
|
if( bConstantSprint )
|
|
{
|
|
moveBlendRatio = MOVEBLENDRATIO_SPRINT;
|
|
fSprintResult = 2.0f;
|
|
}
|
|
//DEBUG
|
|
|
|
// Script-specified disabling of sprinting gives a max movespeed of 1.0f (running)
|
|
if(pPlayerInfo->GetPlayerDataPlayerSprintDisabled())
|
|
fSprintResult = Min(fSprintResult, 1.0f);
|
|
|
|
if(fSprintResult > 0.0f)
|
|
m_bStickHasReturnedToCentre = false;
|
|
|
|
if(ms_bStickyRunButton && !m_bStickHasReturnedToCentre)
|
|
fSprintResult = Max(fSprintResult, 1.0f);
|
|
|
|
if(fSprintResult > 1.0f)
|
|
{
|
|
moveBlendRatio = moveBlendRatio > 0.f ? ms_fSprintBlend : 0.f;
|
|
|
|
static const float fMaxCrouchedSprintVal = 1.25f;
|
|
if(fSprintResult >= fMaxCrouchedSprintVal)
|
|
{
|
|
if(pPlayerPed->GetIsCrouching())
|
|
pPlayerPed->SetIsCrouching(false);
|
|
|
|
// JB : If L3 *doesn't* toggle stealth, then don't let the player
|
|
// exit stealth mode once the script/gamelogic has set him in it.
|
|
if(pPlayerPed->GetMotionData()->GetUsingStealth())
|
|
pPlayerPed->GetMotionData()->SetUsingStealth(false);
|
|
}
|
|
}
|
|
else if(fSprintResult > 0.0f)
|
|
{
|
|
moveBlendRatio = moveBlendRatio > 0.f ? ms_fRunBlend : 0.f;
|
|
}
|
|
else if(pPlayerPed->IsBeingForcedToRun())
|
|
{
|
|
#if __XENON
|
|
static dev_float RUN_SPEED_BOUNDARY = 0.75f;
|
|
#else
|
|
static dev_float RUN_SPEED_BOUNDARY = 0.9f;
|
|
#endif // __XENON
|
|
if(moveBlendRatio >= RUN_SPEED_BOUNDARY)
|
|
moveBlendRatio = MOVEBLENDRATIO_RUN;
|
|
}
|
|
else
|
|
{
|
|
moveBlendRatio *= ms_fWalkBlend;
|
|
moveBlendRatio = rage::Min(moveBlendRatio, ms_fWalkBlend);
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// In first person shooter camera, check for analog stick run, if we are currently not running
|
|
if(ShouldBeRunningDueToAnalogRun(pPlayerPed, moveBlendRatio, vecStickInput))
|
|
{
|
|
moveBlendRatio = MOVEBLENDRATIO_RUN;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
|
|
moveBlendRatio = Clamp(moveBlendRatio, MOVEBLENDRATIO_STILL, m_fMaxMoveBlendRatio);
|
|
|
|
//***************************************************************************************
|
|
// If the stick input is just a slight nudge, then allow the player to do an idle-turn
|
|
// on the spot, but zero the moveBlendRatio so that they don't move forwards.
|
|
// The main reason for this is that now we have elaborate left/right walk-starts they
|
|
// conflict with the old method of doing an idle turn (ie. does the player want to start
|
|
// moving, or do they just want to turn on the spot?)
|
|
|
|
Vector2 vCurrMoveRatio;
|
|
pPlayerPed->GetMotionData()->GetCurrentMoveBlendRatio(vCurrMoveRatio);
|
|
|
|
if(moveBlendRatio > 0.0f && moveBlendRatio < ms_fSmallMoveBlendRatioToDoIdleTurn && vCurrMoveRatio.x == 0.0f && vCurrMoveRatio.y == 0.0f)
|
|
{
|
|
// JR - this doesn't look great on the animals, see B* 2039798
|
|
if(!pPlayerPed->IsBeingForcedToRun() && pPlayerPed->GetPedType() != PEDTYPE_ANIMAL)
|
|
{
|
|
moveBlendRatio = 0.0f;
|
|
}
|
|
}
|
|
|
|
const float fPrevMbrSqr = pPlayerPed->GetMotionData()->GetDesiredMoveBlendRatio().Mag2();
|
|
|
|
if( FPS_MODE_SUPPORTED_ONLY(!bInFpsMode &&) CNewHud::IsWeaponWheelActive() && moveBlendRatio > 0.f && fPrevMbrSqr > 0.f )
|
|
{
|
|
moveBlendRatio = Min(sqrt(fPrevMbrSqr), MOVEBLENDRATIO_RUN);
|
|
}
|
|
|
|
if(pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_LadderBlockingMovement))
|
|
{
|
|
moveBlendRatio = MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
pPlayerPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(ShouldCheckRunInsteadOfSprint(pPlayerPed, moveBlendRatio));
|
|
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
moveBlendRatio = MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
// Set the desired moveblendratios for the player ped
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(moveBlendRatio, 0.0f);
|
|
m_fMoveBlendRatio = moveBlendRatio;
|
|
|
|
#if __PLAYER_ASSISTED_MOVEMENT
|
|
if(CPlayerAssistedMovementControl::ms_bAssistedMovementEnabled)
|
|
{
|
|
const bool bForceScan = (moveBlendRatio > 0.0f) && (fPrevMbrSqr==0.0f);
|
|
m_pAssistedMovementControl->ScanForSnapToRoute(pPlayerPed, bForceScan);
|
|
m_pAssistedMovementControl->Process(pPlayerPed, vecStickInput);
|
|
}
|
|
#endif
|
|
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
// B*2325743: Flag if we are using mouse and keyboard and sprinting. Used in CTaskMovePlayer::ProcessStrafeMove.
|
|
m_bWasSprintingOnPCMouseAndKeyboard = false;
|
|
if (pPlayerPed->GetControlFromPlayer() && pPlayerPed->GetControlFromPlayer()->WasKeyboardMouseLastKnownSource() && pPlayerPed->GetControlFromPlayer()->GetPedSprint().IsDown()
|
|
&& pPlayerPed->GetMotionData() && pPlayerPed->GetMotionData()->GetCurrentMoveBlendRatio().Mag() >= 2.0f)
|
|
{
|
|
m_bWasSprintingOnPCMouseAndKeyboard = true;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
}
|
|
|
|
void CTaskMovePlayer::ProcessStrafeMove(CPed* pPlayerPed)
|
|
{
|
|
pPlayerPed->SetIsStrafing(true);
|
|
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
// B*2325743: Just started strafing from sprint whilst using keyboard and mouse (and sprint input is still down).
|
|
// CControl::GetPedSprintIsDown was returning false due to the toggle run value being true, causing us to walk due to CPlayerInfo::ControlButtonSprint returning 0.0f instead of 1.0f.
|
|
// ...So force the toggle run value to false while we're still holding sprint down.
|
|
if (m_bWasSprintingOnPCMouseAndKeyboard && pPlayerPed->GetControlFromPlayer() && pPlayerPed->GetControlFromPlayer()->WasKeyboardMouseLastKnownSource() && pPlayerPed->GetControlFromPlayer()->GetPedSprint().IsDown())
|
|
{
|
|
pPlayerPed->GetControlFromPlayer()->SetToggleRunValue(false);
|
|
}
|
|
// ...If we let go of sprint, reset the toggle run value and clear the "was sprinting" flag.
|
|
else if (m_bWasSprintingOnPCMouseAndKeyboard && pPlayerPed->GetControlFromPlayer() && !pPlayerPed->GetControlFromPlayer()->GetPedSprint().IsDown())
|
|
{
|
|
m_bWasSprintingOnPCMouseAndKeyboard = false;
|
|
pPlayerPed->GetControlFromPlayer()->ClearToggleRun();
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
|
|
// get the orientation of the FIRST follow ped camera, for player controls.
|
|
const float fCamOrient = fwAngle::LimitRadianAngleSafe(camInterface::GetPlayerControlCamHeading(*pPlayerPed));
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const bool bInFpsMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
float fMoveRatio = 0.0f;
|
|
Vector2 vecStickInput(0.0f, 0.0f);
|
|
const CEntity* pLockOnTarget = pPlayerPed->GetPlayerInfo()->GetTargeting().GetLockOnTarget();
|
|
if( pLockOnTarget && pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GUN))
|
|
{
|
|
static dev_float s_gunStrafingMinDistance = 1.2f;
|
|
vecStickInput = ProcessStrafeInputWithRestrictions( pPlayerPed, pLockOnTarget, s_gunStrafingMinDistance, m_fStickAngle, m_bStickAngleTweaked );
|
|
fMoveRatio = vecStickInput.Mag();
|
|
}
|
|
else
|
|
{
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm(ioValue::NO_DEAD_ZONE);
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm(ioValue::NO_DEAD_ZONE);
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm();
|
|
}
|
|
|
|
#if RSG_ORBIS
|
|
TUNE_GROUP_FLOAT(PLAYER_STICK_TUNE, STRAFE_POW, 0.75f, 0.f, 10.f, 0.01f);
|
|
vecStickInput.x = powf(Abs(vecStickInput.x), STRAFE_POW) * Sign(vecStickInput.x);
|
|
vecStickInput.x = Clamp(vecStickInput.x, -1.f, 1.f);
|
|
vecStickInput.y = powf(Abs(vecStickInput.y), STRAFE_POW) * Sign(vecStickInput.y);
|
|
vecStickInput.y = Clamp(vecStickInput.y, -1.f, 1.f);
|
|
#endif // RSG_ORBIS
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
fMoveRatio = rage::ioAddRoundDeadZone(vecStickInput.x, vecStickInput.y, ms_fDeadZone);
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
fMoveRatio = vecStickInput.Mag();
|
|
}
|
|
|
|
// Normalise if > 1.0. Don't want to normalise below this as it'll kill subtle, half stick movement.
|
|
if(fMoveRatio > 1.0f)
|
|
{
|
|
vecStickInput.Normalize();
|
|
fMoveRatio = 1.0f;
|
|
}
|
|
|
|
weaponAssert(pPlayerPed->GetWeaponManager());
|
|
if(CTaskPlayerOnFoot::ms_bAllowStrafingWhenUnarmed && CPlayerInfo::IsAiming()
|
|
&& pPlayerPed->GetWeaponManager()->GetEquippedWeapon() && pPlayerPed->GetWeaponManager()->GetEquippedWeapon()->GetWeaponInfo()->GetIsMelee())
|
|
{
|
|
pPlayerPed->SetDesiredHeading(fCamOrient);
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// FPS Swim Strafing: Rotate based on camera heading and pitch
|
|
if (bInFpsMode && pPlayerPed->GetIsSwimming())
|
|
{
|
|
pPlayerPed->SetDesiredHeading(fCamOrient);
|
|
|
|
// Only use pitch if we are under the surface
|
|
CTaskMotionBase* pPrimaryTask = pPlayerPed->GetPrimaryMotionTask();
|
|
if( pPrimaryTask && pPrimaryTask->GetTaskType() == CTaskTypes::TASK_MOTION_PED )
|
|
{
|
|
CTaskMotionPed* pTask = static_cast<CTaskMotionPed*>(pPrimaryTask);
|
|
if (pTask && pTask->CheckForDiving())
|
|
{
|
|
float fPitch = camInterface::GetPlayerControlCamPitch(*pPlayerPed);
|
|
fPitch = Clamp(fPitch, -1.0f, 1.0f) * CTaskMotionDiving::ms_fMaxPitch;
|
|
pPlayerPed->GetMotionData()->SetCurrentPitch(fPitch);
|
|
}
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
if( fMoveRatio > 0.0f )
|
|
{
|
|
if(fMoveRatio < ms_fStickInCentreMag)
|
|
m_bStickHasReturnedToCentre = true;
|
|
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);//fwAngle::GetRadianAngleBetweenPoints(0.0f, 0.0f, -vMoveBlendRatio.x, vMoveBlendRatio.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngleSafe(fStickAngle);
|
|
|
|
Vector3 vecDesiredDirection(-rage::Sinf(fStickAngle), rage::Cosf(fStickAngle), 0.0f);
|
|
|
|
vecStickInput.x = fMoveRatio*DotProduct(vecDesiredDirection, VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetA()));
|
|
vecStickInput.y = fMoveRatio*DotProduct(vecDesiredDirection, VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB()));
|
|
|
|
vecStickInput.x = Clamp(vecStickInput.x, -1.0f, 1.0f);
|
|
vecStickInput.y = Clamp(vecStickInput.y, -1.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
if( fMoveRatio > 0.0f && !pPlayerInfo->GetSimulateGaitInputOn() )
|
|
{
|
|
float fBlendMult = ms_fWalkBlend;
|
|
float fRescalePow = 3.0f;
|
|
bool bNormaliseStickInput = false;
|
|
|
|
float fSprintResult = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_ON_FOOT);
|
|
|
|
// Script-specified disabling of sprinting gives a max movespeed of 1.0f (running)
|
|
if(pPlayerPed->GetPlayerInfo()->GetPlayerDataPlayerSprintDisabled())
|
|
fSprintResult = Min(fSprintResult, 1.0f);
|
|
|
|
if(fSprintResult > 0.0f)
|
|
m_bStickHasReturnedToCentre = false;
|
|
|
|
if(ms_bStickyRunButton && !m_bStickHasReturnedToCentre)
|
|
fSprintResult = Max(fSprintResult, 1.0f);
|
|
|
|
// Don't auto run when NoAutoRunWhenFiring is set, e.g., pouring petrol can.
|
|
bool bNoAutoRunWhenFiring = false;
|
|
const CWeapon* pWeapon = pPlayerPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if (pWeapon && (pWeapon->GetWeaponInfo()->GetNoAutoRunWhenFiring() || pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_NoAutoRunWhenFiring)) FPS_MODE_SUPPORTED_ONLY(&& (!pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) || !pPlayerPed->GetMotionData()->GetIsFPSIdle())))
|
|
{
|
|
bNoAutoRunWhenFiring = true;
|
|
}
|
|
|
|
if(!bNoAutoRunWhenFiring)
|
|
{
|
|
if(fSprintResult > 1.0f)
|
|
{
|
|
fBlendMult = ms_fSprintBlend;
|
|
bNormaliseStickInput = true;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
pPlayerPed->GetMotionData()->SetUsingStealth(false);
|
|
pPlayerPed->GetMotionData()->SetIsUsingStealthInFPS(false);
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
else if(!pPlayerPed->GetMotionData()->GetUsingStealth() && pWeapon && pWeapon->GetWeaponInfo()->GetIsGunOrCanBeFiredLikeGun() && (pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON) || pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_RUN_AND_GUN)) && (!pPlayerPed->GetIsInInterior() || pPlayerPed->GetPortalTracker()->IsAllowedToRunInInterior() || pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IgnoreInteriorCheckForSprinting)) )
|
|
{
|
|
fBlendMult = ms_fRunBlend;
|
|
static dev_float s_fRescalePower = 3.0f;
|
|
fRescalePow = s_fRescalePower;
|
|
}
|
|
else if(fSprintResult > 0.0)
|
|
{
|
|
fBlendMult = ms_fRunBlend;
|
|
bNormaliseStickInput = true;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// In first person shooter camera, check for analog stick run, if we are currently not running
|
|
if(ShouldBeRunningDueToAnalogRun(pPlayerPed, fBlendMult, vecStickInput))
|
|
{
|
|
// When armed and strafing, force run when stick is fully deflected.
|
|
fBlendMult = ms_fRunBlend;
|
|
bNormaliseStickInput = true;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
}
|
|
|
|
bool isAccuratelyAiming = false;
|
|
camThirdPersonAimCamera* pAimCamera = camInterface::GetGameplayDirector().GetThirdPersonAimCamera();
|
|
if(pAimCamera)
|
|
{
|
|
isAccuratelyAiming = pAimCamera->IsInAccurateMode();
|
|
}
|
|
|
|
if(isAccuratelyAiming)
|
|
{
|
|
// Clamp fBlendMult to walking speed
|
|
fBlendMult = ms_fWalkBlend;
|
|
}
|
|
|
|
// Scale the stick input
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
vecStickInput *= fBlendMult;
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
float fMag = vecStickInput.Mag();
|
|
if(fMag > 0.f)
|
|
{
|
|
if(bNormaliseStickInput)
|
|
{
|
|
vecStickInput.Normalize();
|
|
fMag = fBlendMult;
|
|
}
|
|
else if(fRescalePow > 1.0f && fMag < 1.0f)
|
|
{
|
|
float fRescaledMag = powf(fMag, fRescalePow);
|
|
vecStickInput.Normalize();
|
|
vecStickInput *= fRescaledMag;
|
|
fMag = fRescaledMag;
|
|
}
|
|
|
|
vecStickInput *= fBlendMult;
|
|
}
|
|
else
|
|
{
|
|
vecStickInput.x = 0.f;
|
|
vecStickInput.y = 0.f;
|
|
}
|
|
|
|
// Clamp the moveblend-ratio to some minimum value.
|
|
// This is because strafing a very slow speeds looks silly, as with current move code
|
|
// we end up with a blend which is very close to idle.
|
|
|
|
bool bAdjustStickInput = fMag > 0.0f && fMag < ms_fStrafeMinMoveBlendRatio;
|
|
if(bAdjustStickInput)
|
|
{
|
|
vecStickInput *= (ms_fStrafeMinMoveBlendRatio/fMag);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process simulate gait.
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
if(fMoveRatio > 0.0f || pControl->GetPedTargetIsDown() || pControl->GetPedAttack().IsPressed() || pControl->GetPedAttack2().IsPressed())
|
|
{
|
|
// Disable simulate gait if there is an input detected.
|
|
if(pPlayerInfo->GetSimulateGaitInputOn() )
|
|
{
|
|
if(!pPlayerInfo->GetSimulateGaitNoInputInterruption() || pPlayerInfo->GetSimulateGaitTimerCount() > pPlayerInfo->GetSimulateGaitDuration())
|
|
{
|
|
pPlayerInfo->ResetSimulateGaitInput();
|
|
}
|
|
else
|
|
{
|
|
// Update the timer
|
|
pPlayerInfo->SetSimulateGaitTimerCount(pPlayerInfo->GetSimulateGaitTimerCount() + fwTimer::GetTimeStep());
|
|
}
|
|
}
|
|
}
|
|
else if(pPlayerInfo->GetSimulateGaitInputOn())
|
|
{
|
|
float fDuration = pPlayerInfo->GetSimulateGaitDuration();
|
|
float fTimerCount = pPlayerInfo->GetSimulateGaitTimerCount();
|
|
if(fDuration < 0.0f || fDuration >= fTimerCount )
|
|
{
|
|
vecStickInput.x = 0.0f;
|
|
vecStickInput.y = pPlayerInfo->GetSimulateGaitMoveBlendRatio();
|
|
|
|
float fStickAngle = pPlayerInfo->GetSimulateGaitHeading();
|
|
if(pPlayerInfo->GetSimulateGaitUseRelativeHeading())
|
|
{
|
|
fStickAngle = fStickAngle + pPlayerInfo->GetSimulateGaitStartHeading();
|
|
}
|
|
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
fStickAngle -= pPlayerPed->GetCurrentHeading();
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
fStickAngle = fwAngle::LimitRadianAngleSafe(fStickAngle);
|
|
|
|
Vector3 vecDesiredDirection(-rage::Sinf(fStickAngle), rage::Cosf(fStickAngle), 0.0f);
|
|
|
|
vecStickInput.x = DotProduct(vecDesiredDirection, VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetA()));
|
|
vecStickInput.y = DotProduct(vecDesiredDirection, VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetB()));
|
|
|
|
vecStickInput.x = Clamp(vecStickInput.x, -1.0f, 1.0f);
|
|
vecStickInput.y = Clamp(vecStickInput.y, -1.0f, 1.0f);
|
|
|
|
fTimerCount += fwTimer::GetTimeStep();
|
|
pPlayerInfo->SetSimulateGaitTimerCount(fTimerCount);
|
|
}
|
|
else
|
|
{
|
|
pPlayerInfo->ResetSimulateGaitInput();
|
|
}
|
|
}
|
|
|
|
// if we want to simulate player's movement
|
|
if(pPlayerInfo->GetSimulateGaitInputOn())
|
|
{
|
|
m_fMoveBlendRatio = pPlayerInfo->GetSimulateGaitMoveBlendRatio();
|
|
#if FPS_MODE_SUPPORTED
|
|
if(bInFpsMode)
|
|
{
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(vecStickInput.y * m_fMoveBlendRatio, vecStickInput.x * m_fMoveBlendRatio);
|
|
}
|
|
else
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(vecStickInput.y, vecStickInput.x);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(CNewHud::IsWeaponWheelActive())
|
|
{
|
|
const float fStickInputSqr = vecStickInput.Mag2();
|
|
if(fStickInputSqr > 0.f)
|
|
{
|
|
float fStickInputMag = sqrt(fStickInputSqr);
|
|
vecStickInput.Scale(1.f/fStickInputMag);
|
|
float moveBlendRatio = Min(fStickInputMag, MOVEBLENDRATIO_RUN);
|
|
vecStickInput *= moveBlendRatio;
|
|
}
|
|
}
|
|
|
|
if(pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_LadderBlockingMovement))
|
|
{
|
|
vecStickInput *= MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
vecStickInput *= MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
pPlayerPed->GetMotionData()->SetFPSCheckRunInsteadOfSprint(ShouldCheckRunInsteadOfSprint(pPlayerPed, vecStickInput.Mag()));
|
|
|
|
// Set the desired move blend (which is smoothed internally) and force
|
|
// the momentum (hit) based movement (which won't be smoothed internally).
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(vecStickInput.y, vecStickInput.x);
|
|
m_fMoveBlendRatio = vecStickInput.Mag();
|
|
m_fMoveBlendRatio = Clamp(m_fMoveBlendRatio, MOVEBLENDRATIO_STILL, MOVEBLENDRATIO_SPRINT);
|
|
#if __PLAYER_ASSISTED_MOVEMENT
|
|
if(CPlayerAssistedMovementControl::ms_bAssistedMovementEnabled)
|
|
{
|
|
m_pAssistedMovementControl->ScanForSnapToRoute(pPlayerPed);
|
|
m_pAssistedMovementControl->Process(pPlayerPed, vecStickInput);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void CTaskMovePlayer::ProcessSwimMove(CPed* pPlayerPed)
|
|
{
|
|
CTaskMotionBase* pBaseTask = pPlayerPed->GetCurrentMotionTask();
|
|
|
|
const bool bUnderwater = pBaseTask->IsUnderWater();
|
|
|
|
m_fUnderwaterGlideTimer -= fwTimer::GetTimeStep();
|
|
m_fUnderwaterGlideTimer = Max(m_fUnderwaterGlideTimer, 0.0f);
|
|
|
|
m_fSwitchStrokeTimerOnSurface -= fwTimer::GetTimeStep();
|
|
m_fSwitchStrokeTimerOnSurface = Max(m_fSwitchStrokeTimerOnSurface, 0.0f);
|
|
|
|
//***********************
|
|
// Underwater swimming
|
|
|
|
//! DMKH. Do not process dive if we are already climbing. Or we have a valid handhold to grab.
|
|
CClimbHandHoldDetected handHold;
|
|
bool bHandHoldDetected = pPlayerPed->GetPedIntelligence()->IsPedClimbing();
|
|
|
|
//! TO DO - Create a dive control?
|
|
CControl * pControl = pPlayerPed->GetControlFromPlayer();
|
|
bool bDivePressed = pControl && pControl->GetPedDive().IsPressed() && !pPlayerPed->GetIsFPSSwimming();
|
|
|
|
bool bFPSMode = false;
|
|
#if FPS_MODE_SUPPORTED
|
|
if (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
bFPSMode = true;
|
|
|
|
// Go to dive-down state if aiming down and pressing forwards on stick
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_FPSSwimUseSwimMotionTask))
|
|
{
|
|
float fCamPitch = camInterface::GetPlayerControlCamPitch(*pPlayerPed);
|
|
TUNE_GROUP_FLOAT(FPS_SWIMMING, fDivePitchThresholdFromSwimming, -0.75f, -1.0f, 1.0f, 0.01f);
|
|
if (fCamPitch <= fDivePitchThresholdFromSwimming)
|
|
{
|
|
bDivePressed = true;
|
|
}
|
|
}
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
if(!bHandHoldDetected && bDivePressed)
|
|
{
|
|
pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForClimb, true);
|
|
pPlayerPed->GetPedIntelligence()->GetClimbDetector().Process();
|
|
bHandHoldDetected = pPlayerPed->GetPedIntelligence()->GetClimbDetector().GetDetectedHandHold(handHold);
|
|
}
|
|
|
|
if(bUnderwater)
|
|
{
|
|
CTaskMotionDiving* pTaskMotionDiving = pBaseTask && pBaseTask->GetTaskType() == CTaskTypes::TASK_MOTION_DIVING ? static_cast<CTaskMotionDiving*>(pPlayerPed->GetCurrentMotionTask()) : NULL;
|
|
|
|
// Handle diving under
|
|
if(bDivePressed && !bHandHoldDetected)
|
|
{
|
|
pPlayerPed->GetPrimaryMotionTask()->SetWantsToDiveUnderwater();
|
|
}
|
|
|
|
CControl * pControl = pPlayerPed->GetControlFromPlayer();
|
|
const float fStickLeftRight = -pControl->GetPedWalkLeftRight().GetNorm();
|
|
float fPitch = pControl->GetPedWalkUpDown().GetNorm();
|
|
|
|
// Increase the idle timer if swim-state is idling, and there's no stick input
|
|
// After a certain period we will auto-level the ped to help him orientate.
|
|
if(pTaskMotionDiving && pTaskMotionDiving->GetIsIdle() && Abs(fStickLeftRight) < 0.01f && Abs(fPitch) < 0.01f)
|
|
{
|
|
m_fUnderwaterIdleTimer += fwTimer::GetTimeStep();
|
|
}
|
|
else
|
|
{
|
|
m_fUnderwaterIdleTimer = 0.0f;
|
|
}
|
|
|
|
const bool bAutoLevel = (m_fUnderwaterIdleTimer >= ms_fUnderwaterIdleAutoLevelTime);
|
|
|
|
// get the orientation of the FIRST follow ped camera, for player controls
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(*pPlayerPed);
|
|
float fCamPitch = camInterface::GetPlayerControlCamPitch(*pPlayerPed);
|
|
|
|
// B*2015587: PC Mouse/Keyboard controls - Always pitch based on camera pitch if using mouse/keyboard controls, don't reset it to 0.
|
|
bool bUsingPCControlsUnderwater = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
if(pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
bUsingPCControlsUnderwater = true;
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
if (pControl->GetPedLookUpDown().GetNorm() == 0 && !bUsingPCControlsUnderwater)
|
|
fCamPitch = 0;
|
|
|
|
if(camInterface::GetDebugDirector().IsFreeCamActive())
|
|
{
|
|
fCamOrient = pPlayerPed->GetTransform().GetHeading();
|
|
}
|
|
|
|
//*********
|
|
// Pitch
|
|
fPitch = Clamp(fPitch, -1.0f, 1.0f) * CTaskMotionDiving::ms_fMaxPitch;
|
|
|
|
if(pTaskMotionDiving && pTaskMotionDiving->IsDivingDown())
|
|
fPitch = -CTaskMotionDiving::ms_fDiveUnderPitch;
|
|
|
|
if(bAutoLevel)
|
|
{
|
|
fPitch = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
if (bFPSMode)
|
|
{
|
|
// Just use camera pitch in FPS mode.
|
|
const camFrame& aimCameraFrame = camInterface::GetPlayerControlCamAimFrame();
|
|
float fCamPitch = aimCameraFrame.ComputePitch();
|
|
fPitch = fwAngle::LimitRadianAngle(fCamPitch);
|
|
fPitch = Clamp(fPitch, -CTaskMotionDiving::ms_fMaxPitch, CTaskMotionDiving::ms_fMaxPitch);
|
|
}
|
|
else
|
|
{
|
|
fPitch = fwAngle::LimitRadianAngle(fPitch + fCamPitch);
|
|
fPitch = Clamp(fPitch, -CTaskMotionDiving::ms_fMaxPitch, CTaskMotionDiving::ms_fMaxPitch);
|
|
}
|
|
}
|
|
|
|
pPlayerPed->SetDesiredPitch(fPitch);
|
|
|
|
//*********
|
|
// Accel
|
|
|
|
float fSprintResult = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_UNDER_WATER, true);
|
|
// In FPS mode: use left stick to enter swim (run) state
|
|
if (bFPSMode)
|
|
{
|
|
float fLeftStickPitch = -pControl->GetPedWalkUpDown().GetNorm();
|
|
if (fLeftStickPitch > 0.0f)
|
|
{
|
|
fSprintResult += 1.0f;
|
|
}
|
|
}
|
|
|
|
if (fSprintResult >= 1.0f)
|
|
{
|
|
m_fMoveBlendRatio = (fSprintResult > 1.0f) ? MOVEBLENDRATIO_SPRINT : MOVEBLENDRATIO_RUN;
|
|
|
|
m_fUnderwaterGlideTimer = 1.0f;
|
|
}
|
|
//if Idle but pushing forward, apply a forward drift B* 1427969
|
|
else if (pTaskMotionDiving && fPitch < -1.0f && pTaskMotionDiving->GetIsIdle())
|
|
{
|
|
pTaskMotionDiving->SetApplyForwardDrift();
|
|
}
|
|
else if(m_fUnderwaterGlideTimer <= 0.0f)
|
|
{
|
|
m_fMoveBlendRatio = MOVEBLENDRATIO_STILL;
|
|
}
|
|
|
|
// Restrict speed to breast-stroke when underwater & inside an interior (unless tagged as 'may run')
|
|
if(pPlayerPed->GetPortalTracker()->IsInsideInterior() && !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IgnoreInteriorCheckForSprinting) && !pPlayerPed->GetPortalTracker()->IsAllowedToRunInInterior())
|
|
{
|
|
m_fMoveBlendRatio = Min(m_fMoveBlendRatio, MOVEBLENDRATIO_WALK);
|
|
}
|
|
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(m_fMoveBlendRatio, 0.0f);
|
|
|
|
//*****************
|
|
// Desired heading
|
|
|
|
static const float fMaxTurn = HALF_PI * 0.9f;
|
|
|
|
float fLeftRightVal = Clamp(fStickLeftRight, -1.0f, 1.0f);
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= Sign(fStickLeftRight);
|
|
fLeftRightVal *= fMaxTurn;
|
|
|
|
if (bFPSMode)
|
|
{
|
|
// Ignore left/right inputs in FPS mode. Just want to use camera orientation.
|
|
fLeftRightVal = 0.0f;
|
|
}
|
|
|
|
//if(fLeftRightVal != 0.0f)
|
|
float fHdg;
|
|
|
|
fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + fCamOrient);
|
|
|
|
if(pTaskMotionDiving && !pTaskMotionDiving->CanRotateCamRelative())
|
|
{
|
|
fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + fCamOrient);
|
|
}
|
|
else
|
|
{
|
|
fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + pPlayerPed->GetCurrentHeading());
|
|
}
|
|
|
|
pPlayerPed->SetDesiredHeading(fHdg);
|
|
|
|
}
|
|
|
|
//*************************
|
|
// On-surface swimming
|
|
|
|
else
|
|
{
|
|
//taskAssert(pBaseTask && pBaseTask->GetTaskType()==CTaskTypes::TASK_MOTION_SWIMMING);
|
|
float moveBlendRatio = MOVEBLENDRATIO_STILL;
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
Vector2 vecStickInput;
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
|
|
// y stick results are positive for down (pulling back) and negative for up, so reverse to match world y direction
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm();
|
|
|
|
// don't let the player walk when attached
|
|
if(!pPlayerPed->GetIsAttached())
|
|
moveBlendRatio = vecStickInput.Mag();// / ms_fStickRange;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if (bFPSMode)
|
|
{
|
|
moveBlendRatio = vecStickInput.Mag() * 2.0f;
|
|
|
|
// B*2038392: Allow just pressing/tapping sprint button to sprint in FPS mode (without any stick input)
|
|
if (moveBlendRatio == MOVEBLENDRATIO_STILL)
|
|
{
|
|
moveBlendRatio = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_ON_WATER, true);
|
|
}
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
if(moveBlendRatio > 1.0f && !bFPSMode)
|
|
moveBlendRatio = 1.0f;
|
|
|
|
if(moveBlendRatio < ms_fStickInCentreMag)
|
|
m_bStickHasReturnedToCentre = true;
|
|
|
|
// get the orientation of the FIRST follow ped camera, for player controls
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(*pPlayerPed);
|
|
|
|
float fCamPitch = 0.0f;
|
|
|
|
if((moveBlendRatio > 0.0f) || (fCamPitch == 0.0f)) //Always allow the pitch to be reset.
|
|
{
|
|
pPlayerPed->SetDesiredPitch(fCamPitch);
|
|
}
|
|
|
|
if(moveBlendRatio > 0.0f && !bFPSMode)
|
|
{
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
|
|
if (m_fLockedStickHeading != -1.0f)
|
|
{
|
|
if (fabs(fStickAngle - m_fLockedStickHeading) > EIGHTH_PI)
|
|
{
|
|
m_fLockedStickHeading = -1.0f;
|
|
}
|
|
else
|
|
fStickAngle = m_fLockedHeading;
|
|
}
|
|
|
|
pPlayerPed->SetDesiredHeading(fStickAngle);
|
|
}
|
|
else if (bFPSMode)
|
|
{
|
|
// Just use camera heading in FPS mode.
|
|
const camFrame& aimCameraFrame = camInterface::GetPlayerControlCamAimFrame();
|
|
float fCamOrient = aimCameraFrame.ComputeHeading();
|
|
pPlayerPed->SetDesiredHeading(fCamOrient);
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->SetDesiredHeading(pPlayerPed->GetCurrentHeading());
|
|
}
|
|
|
|
//end of new
|
|
|
|
// Handle diving under
|
|
if (!bHandHoldDetected)
|
|
{
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_EnablePressAndReleaseDives))
|
|
{
|
|
static dev_u32 s_uMinHoldTimeForDive = 200;
|
|
if (pControl && !pControl->GetPedDive().IsReleasedAfterHistoryHeldDown(s_uMinHoldTimeForDive) && pControl->GetPedDive().IsReleased())
|
|
{
|
|
pPlayerPed->GetPrimaryMotionTask()->SetWantsToDiveUnderwater();
|
|
}
|
|
}
|
|
else if(bDivePressed)
|
|
{
|
|
//Check for dive space
|
|
static dev_float sfMinDiveClearance = 2.0f;
|
|
float fGroundClearance = CTaskMotionDiving::TestGroundClearance(pPlayerPed, sfMinDiveClearance, 0.5f);
|
|
if (fGroundClearance<0)
|
|
{
|
|
pPlayerPed->GetPrimaryMotionTask()->SetWantsToDiveUnderwater();
|
|
}
|
|
}
|
|
}
|
|
|
|
//************************
|
|
// Forwards speed (cross)
|
|
|
|
const bool bUseButtonBashing = true;
|
|
|
|
float fSprintResult = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_ON_WATER, bUseButtonBashing);
|
|
|
|
// Script-specified disabling of sprinting gives a max movespeed of 1.0f (running)
|
|
if(pPlayerPed->GetPlayerInfo()->GetPlayerDataPlayerSprintDisabled())
|
|
fSprintResult = Min(fSprintResult, 1.0f);
|
|
|
|
if(fSprintResult > 1.0f)
|
|
{
|
|
moveBlendRatio = moveBlendRatio > 0.f ? (ms_fSprintBlend-ms_fRunBlend)*moveBlendRatio+ms_fRunBlend : 0.f;
|
|
moveBlendRatio = rage::Min(moveBlendRatio, ms_fSprintBlend);
|
|
}
|
|
else if(fSprintResult > 0.0f)
|
|
{
|
|
moveBlendRatio = moveBlendRatio > 0.f ? (ms_fRunBlend-ms_fWalkBlend)*moveBlendRatio+ms_fWalkBlend : 0.f;
|
|
moveBlendRatio = rage::Min(moveBlendRatio, ms_fRunBlend);
|
|
}
|
|
else if (!bFPSMode)
|
|
{
|
|
moveBlendRatio *= ms_fWalkBlend;
|
|
moveBlendRatio = rage::Min(moveBlendRatio, ms_fWalkBlend);
|
|
}
|
|
|
|
moveBlendRatio = Clamp(moveBlendRatio, MOVEBLENDRATIO_STILL, MOVEBLENDRATIO_SPRINT);
|
|
|
|
if(moveBlendRatio==MOVEBLENDRATIO_STILL)
|
|
{
|
|
m_fSwitchStrokeTimerOnSurface = 0.0f;
|
|
}
|
|
|
|
Vector2 vCurrMoveRatio;
|
|
pPlayerPed->GetMotionData()->GetCurrentMoveBlendRatio(vCurrMoveRatio);
|
|
|
|
// Set the desired move blend ratios for the player ped
|
|
pPlayerPed->GetMotionData()->SetDesiredMoveBlendRatio(moveBlendRatio, 0.0f);
|
|
m_fMoveBlendRatio = moveBlendRatio;
|
|
}
|
|
|
|
#if __PLAYER_ASSISTED_MOVEMENT
|
|
if(CPlayerAssistedMovementControl::ms_bAssistedMovementEnabled)
|
|
{
|
|
Vector2 vecStickInput(
|
|
pPlayerPed->GetControlFromPlayer()->GetPedWalkLeftRight().GetNorm(),
|
|
-pPlayerPed->GetControlFromPlayer()->GetPedWalkUpDown().GetNorm()
|
|
);
|
|
m_pAssistedMovementControl->ScanForSnapToRoute(pPlayerPed);
|
|
m_pAssistedMovementControl->Process(pPlayerPed, vecStickInput);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CTaskMovePlayer::ProcessBirdMove(CPed& playerPed)
|
|
{
|
|
if (playerPed.GetMotionData()->GetIsFlying())
|
|
{
|
|
CTask* pTaskLocomotion = playerPed.GetPedIntelligence()->GetMotionTaskActiveSimplest();
|
|
CTaskBirdLocomotion* pTaskBirdLocomotion = pTaskLocomotion && pTaskLocomotion->GetTaskType() == CTaskTypes::TASK_ON_FOOT_BIRD ? static_cast<CTaskBirdLocomotion*>(pTaskLocomotion) : NULL;
|
|
|
|
if (!pTaskBirdLocomotion)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool bFlapping = pTaskBirdLocomotion->IsFlapping();
|
|
bool bGliding = pTaskBirdLocomotion->IsGliding();
|
|
|
|
// No control if you aren't flapping or gliding.
|
|
if (!bFlapping && !bGliding)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CControl* pControl = playerPed.GetControlFromPlayer();
|
|
Assert(pControl);
|
|
const float fStickLeftRight = -pControl->GetPedWalkLeftRight().GetNorm();
|
|
float fPitch = pControl->GetPedWalkUpDown().GetNorm();
|
|
|
|
// Pitch
|
|
fPitch = Clamp(fPitch, -1.0f, 1.0f) * CTaskBirdLocomotion::ms_fMaxPitch;
|
|
fPitch = fwAngle::LimitRadianAngle(fPitch);
|
|
|
|
float fMinPitch = -CTaskBirdLocomotion::ms_fMaxPitchPlayer;
|
|
float fMaxPitch = CTaskBirdLocomotion::ms_fMaxPitchPlayer;
|
|
|
|
// If you're gliding, then you are limited in your ascension rate.
|
|
static const float sf_GlidingMaxPitch = 15.0f * DtoR;
|
|
|
|
// If you're above a certain height then you can no longer ascend.
|
|
static const float sf_MaxHeightForUpwardMovement = 1200.0f;
|
|
|
|
if (playerPed.GetTransform().GetPosition().GetZf() >= sf_MaxHeightForUpwardMovement)
|
|
{
|
|
fMaxPitch = 0.0f;
|
|
}
|
|
else if (bGliding)
|
|
{
|
|
fMaxPitch = sf_GlidingMaxPitch;
|
|
}
|
|
|
|
fPitch = Clamp(fPitch, fMinPitch, fMaxPitch);
|
|
|
|
playerPed.SetDesiredPitch(fPitch);
|
|
|
|
// Desired heading
|
|
static const float fMaxTurn = HALF_PI * 0.9f;
|
|
|
|
float fLeftRightVal = Clamp(fStickLeftRight, -1.0f, 1.0f);
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= Sign(fStickLeftRight);
|
|
fLeftRightVal *= fMaxTurn;
|
|
|
|
float fHdg = fwAngle::LimitRadianAngle(fLeftRightVal);
|
|
fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + playerPed.GetCurrentHeading());
|
|
|
|
playerPed.SetDesiredHeading(fHdg);
|
|
|
|
m_fMoveBlendRatio = MOVEBLENDRATIO_WALK;
|
|
playerPed.GetMotionData()->SetDesiredMoveBlendRatio(m_fMoveBlendRatio, 0.0f);
|
|
|
|
// Check for flapping input (x).
|
|
if (pControl->GetPedSprintIsDown())
|
|
{
|
|
pTaskBirdLocomotion->RequestFlap();
|
|
}
|
|
|
|
// Check for landing input (square).
|
|
if (pControl->GetPedJump().IsPressed() && pTaskBirdLocomotion->CanLandNow())
|
|
{
|
|
pTaskBirdLocomotion->RequestPlayerLand();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Treat birds on the ground as walking peds.
|
|
ProcessStdMove(&playerPed);
|
|
|
|
// However, query if the player hits the sprint button.
|
|
// If they do, then start flying.
|
|
CControl* pControl = playerPed.GetControlFromPlayer();
|
|
if (pControl)
|
|
{
|
|
if (pControl->GetPedSprintIsPressed())
|
|
{
|
|
playerPed.GetMotionData()->SetIsFlyingForwards();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTaskMovePlayer::ProcessFishMove(CPed& playerPed)
|
|
{
|
|
CTask* pTaskLocomotion = playerPed.GetPedIntelligence()->GetMotionTaskActiveSimplest();
|
|
CTaskFishLocomotion* pTaskFishLocomotion = pTaskLocomotion && pTaskLocomotion->GetTaskType() == CTaskTypes::TASK_ON_FOOT_FISH ? static_cast<CTaskFishLocomotion*>(pTaskLocomotion) : NULL;
|
|
|
|
if (!pTaskFishLocomotion)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CControl* pControl = playerPed.GetControlFromPlayer();
|
|
Assert(pControl);
|
|
const float fStickLeftRight = -pControl->GetPedWalkLeftRight().GetNorm();
|
|
float fPitch = pControl->GetPedWalkUpDown().GetNorm();
|
|
|
|
bool bMovingStick = fabs(fStickLeftRight) > 0.0f || fabs(fPitch) > 0.0f;
|
|
|
|
|
|
// Speed
|
|
float fMBR = MOVEBLENDRATIO_STILL;
|
|
|
|
// No idle turns.
|
|
if (bMovingStick)
|
|
{
|
|
fMBR = MOVEBLENDRATIO_WALK;
|
|
}
|
|
|
|
// If holding sprint button, swim fast.
|
|
float fSprintResult = playerPed.GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_UNDER_WATER, true);
|
|
if (fSprintResult > 0.0f)
|
|
{
|
|
fMBR = MOVEBLENDRATIO_RUN;
|
|
}
|
|
|
|
// Store off the MBR.
|
|
m_fMoveBlendRatio = fMBR;
|
|
|
|
// Reset the gaitless rate boost.
|
|
pTaskFishLocomotion->SetUseGaitlessRateBoostThisFrame(false);
|
|
|
|
// Clamp their movement to be species appropriate.
|
|
// Small fish don't look good when swimming fast.
|
|
if (playerPed.GetTaskData().GetIsFlagSet(CTaskFlags::ForceSlowSwimWhenUnderPlayerControl) && m_fMoveBlendRatio > MOVEBLENDRATIO_WALK)
|
|
{
|
|
m_fMoveBlendRatio = Min(MOVEBLENDRATIO_WALK, m_fMoveBlendRatio);
|
|
pTaskFishLocomotion->SetUseGaitlessRateBoostThisFrame(true);
|
|
}
|
|
|
|
playerPed.GetMotionData()->SetDesiredMoveBlendRatio(m_fMoveBlendRatio, 0.0f);
|
|
|
|
// Orientation
|
|
// get the orientation of the FIRST follow ped camera, for player controls
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(playerPed);
|
|
float fCamPitch = camInterface::GetPlayerControlCamPitch(playerPed);
|
|
if (pControl->GetPedLookUpDown().GetNorm() == 0)
|
|
{
|
|
fCamPitch = 0;
|
|
}
|
|
|
|
if (camInterface::GetDebugDirector().IsFreeCamActive())
|
|
{
|
|
fCamOrient = playerPed.GetTransform().GetHeading();
|
|
}
|
|
|
|
// Pitch
|
|
fPitch = Clamp(fPitch, -1.0f, 1.0f) * CTaskMotionDiving::ms_fMaxPitch;
|
|
fPitch = fwAngle::LimitRadianAngle(fPitch + fCamPitch);
|
|
|
|
// Limit the minimum that a fish's pitch can be based on their depth so they don't go under the world.
|
|
float fMinPitch = GetFishMinPitch(playerPed);
|
|
|
|
// If out of the water limit pitching upwards so we don't pop up and down while skimming it.
|
|
float fMaxPitch = GetFishMaxPitch(playerPed);
|
|
|
|
if (fMaxPitch < CTaskMotionDiving::ms_fMaxPitch && m_fMoveBlendRatio > MOVEBLENDRATIO_WALK)
|
|
{
|
|
pTaskFishLocomotion->SetForceFastPitching(true);
|
|
}
|
|
else if (fMinPitch > -CTaskMotionDiving::ms_fMaxPitch && m_fMoveBlendRatio > MOVEBLENDRATIO_WALK)
|
|
{
|
|
pTaskFishLocomotion->SetForceFastPitching(true);
|
|
}
|
|
else
|
|
{
|
|
pTaskFishLocomotion->SetForceFastPitching(false);
|
|
}
|
|
|
|
fPitch = Clamp(fPitch, fMinPitch, fMaxPitch);
|
|
|
|
playerPed.SetDesiredPitch(fPitch);
|
|
|
|
// Desired heading
|
|
static const float fMaxTurn = HALF_PI * 0.9f;
|
|
|
|
float fLeftRightVal = Clamp(fStickLeftRight, -1.0f, 1.0f);
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= fLeftRightVal;
|
|
fLeftRightVal *= Sign(fStickLeftRight);
|
|
fLeftRightVal *= fMaxTurn;
|
|
|
|
float fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + fCamOrient);
|
|
fHdg = fwAngle::LimitRadianAngle(fLeftRightVal + playerPed.GetCurrentHeading());
|
|
|
|
playerPed.SetDesiredHeading(fHdg);
|
|
}
|
|
|
|
float CTaskMovePlayer::GetFishMaxPitch(const CPed& playerPed)
|
|
{
|
|
float fWaterZ = 0.0f;
|
|
Vec3V vPlayer = playerPed.GetTransform().GetPosition();
|
|
if (!CWaterTestHelper::GetWaterHeightAtPositionIncludingRivers(VEC3V_TO_VECTOR3(vPlayer), &fWaterZ, false))
|
|
{
|
|
// No water at all, so this is clearly bad.
|
|
return 0.0f;
|
|
}
|
|
|
|
// Too close to the surface - tend towards flat.
|
|
float fSurfaceSwimmingOffset = playerPed.GetTaskData().GetSurfaceSwimmingDepthOffset();
|
|
if (fWaterZ - vPlayer.GetZf() <= fSurfaceSwimmingOffset)
|
|
{
|
|
// Can't pitch up at all.
|
|
return 0.0f;
|
|
}
|
|
else if (fWaterZ - vPlayer.GetZf() <= fSurfaceSwimmingOffset * 3.0f) // magic
|
|
{
|
|
|
|
// Can only pitch up a little bit.
|
|
return 5.0f * DtoR;
|
|
}
|
|
|
|
// Can pitch to your heart's content.
|
|
return CTaskMotionDiving::ms_fMaxPitch;
|
|
}
|
|
|
|
float CTaskMovePlayer::GetFishMinPitch(const CPed& playerPed)
|
|
{
|
|
// We are nearing the depth where the map is going to teleport us back on land.
|
|
if (playerPed.GetTransform().GetPosition().GetZf() < ms_fMinDepthForPlayerFish)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
// Fine to pitch down as much as you want.
|
|
return -CTaskMotionDiving::ms_fMaxPitch;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool CTaskMovePlayer::ShouldBeRunningDueToAnalogRun(const CPed* pPlayerPed, const float fCurrentMBR, const Vector2& /*vecStickInput*/) const
|
|
{
|
|
bool bUsingKeyboardAndMouse = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
// B*2312353: PC - Don't auto run; allow player to toggle between walk/run in FPS mode when using keyboard/mouse.
|
|
if (pPlayerPed->GetControlFromPlayer() && pPlayerPed->GetControlFromPlayer()->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
bUsingKeyboardAndMouse = true;
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
// In first person shooter camera, check for analog stick run, if we are currently not running
|
|
if( fCurrentMBR > 0.f && fCurrentMBR < ms_fRunBlend && pPlayerPed->GetMotionData()->GetUsingAnalogStickRun() && !bUsingKeyboardAndMouse)
|
|
{
|
|
// TUNE_GROUP_FLOAT(FIRST_PERSON_TUNE, s_fStickRunThreshold, 0.7f, 0.f, 1.f, 0.01f);
|
|
// float x = vecStickInput.x;
|
|
// float y = vecStickInput.y;
|
|
// float fFpsMoveRatio = rage::ioAddRoundDeadZone(x, y, ioValue::DEFAULT_DEAD_ZONE_VALUE);
|
|
// Assertf(fFpsMoveRatio <= 1.0f, "ioAddRoundDeadZone saturate failed: %f", fFpsMoveRatio);
|
|
//
|
|
// if( fFpsMoveRatio > s_fStickRunThreshold )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool CTaskMovePlayer::ShouldCheckRunInsteadOfSprint(CPed* pPlayerPed, float fMoveBlendRatio) const
|
|
{
|
|
const CPedMotionData* pMotionData = pPlayerPed->GetMotionData();
|
|
if(pMotionData->GetUsingStealth())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!pMotionData->GetUsingAnalogStickRun() || (CPedMotionData::GetIsSprinting(fMoveBlendRatio) && pMotionData->GetScriptedMaxMoveBlendRatio() < MOVEBLENDRATIO_SPRINT))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
|
|
if(pPlayerInfo->GetPlayerDataPlayerSprintDisabled() && pPlayerInfo->ProcessCanSprint(*pPlayerPed, false))
|
|
{
|
|
if(pPlayerInfo->ProcessSprintControl(*pPlayerPed->GetControlFromPlayer(), *pPlayerPed, CPlayerInfo::SPRINT_ON_FOOT, true) > 1.f)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
void CTaskMovePlayer::LockStickHeading(bool bUseCurrentCamera)
|
|
{
|
|
CPed* pPlayerPed = GetPed();
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
Vector2 vecStickInput;
|
|
vecStickInput.x = pControl->GetPedWalkLeftRight().GetNorm();
|
|
// y stick results are positive for down (pulling back) and negative for up, so reverse to match world y direction
|
|
vecStickInput.y = -pControl->GetPedWalkUpDown().GetNorm();
|
|
float fCamOrient = camInterface::GetPlayerControlCamHeading(*pPlayerPed);
|
|
float fStickAngle = rage::Atan2f(-vecStickInput.x, vecStickInput.y);
|
|
fStickAngle = fStickAngle + fCamOrient;
|
|
fStickAngle = fwAngle::LimitRadianAngle(fStickAngle);
|
|
m_fLockedStickHeading = fStickAngle;
|
|
m_fLockedHeading = bUseCurrentCamera ? fCamOrient : m_fLockedStickHeading;
|
|
}
|
|
|
|
CTaskMoveInAir::CTaskMoveInAir() :
|
|
CTaskMove(MOVEBLENDRATIO_STILL),
|
|
m_bWantsToUseParachute(false)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_MOVE_IN_AIR);
|
|
}
|
|
|
|
CTask::FSM_Return CTaskMoveInAir::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
FSM_Begin
|
|
FSM_State(Running)
|
|
FSM_OnUpdate
|
|
return StateRunning_OnUpdate(pPed);
|
|
FSM_End
|
|
}
|
|
|
|
CTask::FSM_Return CTaskMoveInAir::StateRunning_OnUpdate(CPed* pPed)
|
|
{
|
|
if(pPed->IsLocalPlayer())
|
|
{
|
|
ProcessPlayerInput(pPed);
|
|
}
|
|
else if(!pPed->GetPedResetFlag( CPED_RESET_FLAG_IsClimbing))
|
|
{
|
|
ProcessAiInput(pPed);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
// This just checks to see if there is a goto task sequenced after this.
|
|
// If so then the goto target of this task is used to apply a desired
|
|
// heading & moveblendratio to the ped.
|
|
void CTaskMoveInAir::ProcessAiInput(CPed* pPed)
|
|
{
|
|
CPedIntelligence * pPedAi = pPed->GetPedIntelligence();
|
|
CTask * pActiveSeqTask = pPedAi->FindTaskActiveByType(CTaskTypes::TASK_USE_SEQUENCE);
|
|
if(!pActiveSeqTask)
|
|
return;
|
|
|
|
CTaskUseSequence * pTaskUseSequence = (CTaskUseSequence*)pActiveSeqTask;
|
|
Assert(pTaskUseSequence->GetSequenceID()!=-1);
|
|
CTaskSequenceList* pTaskSequence = (CTaskSequenceList*) &CTaskSequences::ms_TaskSequenceLists[pTaskUseSequence->GetSequenceID()];
|
|
|
|
// Check that the task at the current position in the sequence is a navmesh route task
|
|
int iProgress = pTaskUseSequence->GetPrimarySequenceProgress();
|
|
const aiTask * pSeqTask = pTaskSequence->GetTask(iProgress);
|
|
|
|
if(pSeqTask->GetTaskType() != CTaskTypes::TASK_JUMPVAULT)
|
|
return;
|
|
|
|
if(iProgress+1 < CTaskList::MAX_LIST_SIZE)
|
|
{
|
|
const aiTask * pTask = pTaskSequence->GetTask(iProgress+1);
|
|
|
|
if(pTask && pTask->GetTaskType()==CTaskTypes::TASK_COMPLEX_CONTROL_MOVEMENT)
|
|
{
|
|
CTaskTypes::eTaskType iNextTaskType = (CTaskTypes::eTaskType) ((CTaskComplexControlMovement*)pTask)->GetMoveTaskType();
|
|
if(iNextTaskType == CTaskTypes::TASK_MOVE_GO_TO_POINT || iNextTaskType == CTaskTypes::TASK_MOVE_GO_TO_POINT_AND_STAND_STILL)
|
|
{
|
|
const Vector3 vTarget = ((CTaskComplexControlMovement*)pTask)->GetTarget();
|
|
const float fMBR = ((CTaskComplexControlMovement*)pTask)->GetMoveBlendRatio();
|
|
|
|
pPed->SetDesiredHeading(vTarget);
|
|
pPed->GetMotionData()->SetDesiredMoveBlendRatio(fMBR, 0.0f);
|
|
// Prevent the possibility of CTaskSimpleMoveDoNothing resetting this to zero this frame
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_DontChangeMbrInSimpleMoveDoNothing, true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTaskMoveInAir::ProcessPlayerInput(CPed* pPed)
|
|
{
|
|
if (pPed->GetPedResetFlag( CPED_RESET_FLAG_IsLanding ))
|
|
{
|
|
pPed->GetPlayerInfo()->ControlButtonDuck();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// eof
|