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

249 lines
7.7 KiB
C++

// Filename : TaskNMRelax.h
// Description: Natural Motion relax class (FSM version)
// --- Include Files ------------------------------------------------------------
// C headers
// Rage headers
#include "crskeleton\Skeleton.h"
#include "fragment\Cache.h"
#include "fragment\Instance.h"
#include "fragment\Type.h"
#include "fragment\TypeChild.h"
#include "fragmentnm\messageparams.h"
#include "pharticulated/articulatedcollider.h"
#include "physics/shapetest.h"
// Framework headers
#include "fwanimation/animmanager.h"
#include "fwanimation/pointcloud.h"
#include "grcore/debugdraw.h"
#include "fwmaths\Angle.h"
// Game headers
#include "camera/CamInterface.h"
#include "Event\EventDamage.h"
#include "Network\NetworkInterface.h"
#include "Peds\Ped.h"
#include "Peds\PedIntelligence.h"
#include "Peds\PedPlacement.h"
#include "PedGroup\PedGroup.h"
#include "Physics\GtaInst.h"
#include "Physics\Physics.h"
#include "physics/WorldProbe/worldprobe.h"
#include "scene/world/GameWorld.h"
#include "Task\General\TaskBasic.h"
#include "Task\Movement\TaskAnimatedFallback.h"
#include "Task\Movement\Jumping\TaskFallGetUp.h"
#include "Task\Movement\Jumping\TaskInAir.h"
#include "Task/Physics/TaskNMRelax.h"
#include "vehicles/vehicle.h"
#include "Vfx\Misc\Fire.h"
AI_OPTIMISATIONS()
//////////////////////////////////////////////////////////////////////////
// CClonedNMRelaxInfo
//////////////////////////////////////////////////////////////////////////
CClonedNMRelaxInfo::CClonedNMRelaxInfo(float fStiffness, float fDamping, bool bHoldPose)
: m_fStiffness(fStiffness)
, m_fDamping(fDamping)
, m_bHasStiffness(fStiffness > 0.0f)
, m_bHasDamping(fDamping > 0.0f)
, m_bHoldPose(bHoldPose)
{
}
CClonedNMRelaxInfo::CClonedNMRelaxInfo()
: m_fStiffness(-1.0f)
, m_fDamping(-1.0f)
, m_bHasStiffness(false)
, m_bHasDamping(false)
, m_bHoldPose(false)
{
}
CTaskFSMClone *CClonedNMRelaxInfo::CreateCloneFSMTask()
{
return rage_new CTaskNMRelax(10000, 10000, m_fStiffness, m_fDamping, m_bHoldPose);
}
//////////////////////////////////////////////////////////////////////////
// CTaskNMRelax
//////////////////////////////////////////////////////////////////////////
CTaskNMRelax::CTaskNMRelax(u32 nMinTime, u32 nMaxTime, float fStiffness, float fDamping, bool bHoldPose)
: CTaskNMBehaviour(nMinTime, nMaxTime),
m_fStiffness(fStiffness), m_fDamping(fDamping), m_bHoldPose(bHoldPose)
{
SetInternalTaskType(CTaskTypes::TASK_NM_RELAX);
}
CTaskNMRelax::~CTaskNMRelax()
{
}
dev_float DEFAULT_RELAXATION = 80.0f;
//
void CTaskNMRelax::SendRelaxMessage(CPed* pPed, bool bStart)
{
// Fix for peds getting stuck in "fatally injured" state with zero health.
// GS - 26/07/2012 Had to remove this as it was stopping the dead ped scanner from
// kicking off the dead task, which caused an unmanaged ragdoll.
//if(pPed->ShouldBeDead() && pPed->GetDeathState()==DeathState_Alive)
//{
// pPed->SetDeathState(DeathState_Dead);
//}
ART::MessageParams msg;
if(bStart)
msg.addBool(NMSTR_PARAM(NM_START), true);
msg.addBool(NMSTR_PARAM(NM_RELAX_HOLD_POSE), m_bHoldPose);
float fRelaxation = (m_fStiffness >= 0.0f ? rage::Max(10.0f, 100.0f - m_fStiffness) : DEFAULT_RELAXATION);
msg.addFloat(NMSTR_PARAM(NM_RELAX_RELAXATION), fRelaxation);
msg.addInt("priority", 100);
if(m_fDamping >= 0.0f && m_fDamping <= 1.0f)
msg.addFloat(NMSTR_PARAM(NM_RELAX_DAMPING), m_fDamping);
pPed->GetRagdollInst()->PostARTMessage(NMSTR_MSG(NM_RELAX_MSG), &msg);
}
void CTaskNMRelax::StartBehaviour(CPed* pPed)
{
if (pPed->GetRagdollInst() && pPed->GetRagdollInst()->GetCurrentPhysicsLOD()==fragInstNM::RAGDOLL_LOD_HIGH)
{
// Set a minimum stiffness on the ankles to increase stability
static float ankleStiffness = 0.8f;
((phArticulatedCollider*)pPed->GetCollider())->GetBody()->GetJoint(RAGDOLL_ANKLE_LEFT_JOINT).SetMinStiffness(ankleStiffness);
((phArticulatedCollider*)pPed->GetCollider())->GetBody()->GetJoint(RAGDOLL_ANKLE_RIGHT_JOINT).SetMinStiffness(ankleStiffness);
((phArticulatedCollider*)pPed->GetCollider())->GetBody()->GetJoint(RAGDOLL_WRIST_LEFT_JOINT).SetMinStiffness(ankleStiffness);
((phArticulatedCollider*)pPed->GetCollider())->GetBody()->GetJoint(RAGDOLL_WRIST_RIGHT_JOINT).SetMinStiffness(ankleStiffness);
}
SendRelaxMessage(pPed, true);
}
void CTaskNMRelax::ControlBehaviour(CPed* UNUSED_PARAM(pPed))
{
}
bool CTaskNMRelax::FinishConditions(CPed* pPed)
{
return ProcessFinishConditionsBase(pPed, MONITOR_RELAX, FLAG_VEL_CHECK|FLAG_SKIP_DEAD_CHECK);
}
void CTaskNMRelax::StartAnimatedFallback(CPed* pPed)
{
fwMvClipSetId clipSetId = CLIP_SET_ID_INVALID;
fwMvClipId clipId = CLIP_ID_INVALID;
Vector3 vecHeadPos(0.0f,0.0f,0.0f);
pPed->GetBonePosition(vecHeadPos, BONETAG_HEAD);
// If the ped is already prone then use an on-ground damage reaction
if (vecHeadPos.z < pPed->GetTransform().GetPosition().GetZf())
{
const EstimatedPose pose = pPed->EstimatePose();
if (pose == POSE_ON_BACK)
{
clipId = CLIP_DAM_FLOOR_BACK;
}
else
{
clipId = CLIP_DAM_FLOOR_FRONT;
}
Matrix34 rootMatrix;
if (pPed->GetBoneMatrix(rootMatrix, BONETAG_ROOT))
{
float fAngle = AngleZ(pPed->GetTransform().GetForward(), RCC_VEC3V(rootMatrix.c)).Getf();
if (fAngle < -QUARTER_PI)
{
clipId = CLIP_DAM_FLOOR_LEFT;
}
else if (fAngle > QUARTER_PI)
{
clipId = CLIP_DAM_FLOOR_RIGHT;
}
}
clipSetId = pPed->GetPedModelInfo()->GetFullBodyDamageClipSet();
SetNewTask(rage_new CTaskAnimatedFallback(clipSetId, clipId, 0.0f, 0.0f));
}
else
{
float fStartPhase = 0.0f;
// If this task is starting as the result of a migration then use the suggested clip and start phase
CTaskNMControl* pControlTask = GetControlTask();
if (pControlTask != NULL && (pControlTask->GetFlags() & CTaskNMControl::ALREADY_RUNNING) != 0 &&
m_suggestedClipSetId != CLIP_SET_ID_INVALID && m_suggestedClipId != CLIP_SET_ID_INVALID)
{
clipSetId = m_suggestedClipSetId;
clipId = m_suggestedClipId;
fStartPhase = m_suggestedClipPhase;
}
else
{
CTaskFallOver::eFallDirection dir = CTaskFallOver::kDirFront;
CTaskFallOver::eFallContext context = CTaskFallOver::kContextShotPistol;
CTaskFallOver::PickFallAnimation(pPed, context, dir, clipSetId, clipId);
}
CTaskFallOver* pNewTask = rage_new CTaskFallOver(clipSetId, clipId, 1.0f, fStartPhase);
pNewTask->SetRunningLocally(true);
SetNewTask(pNewTask);
}
// Pretend that the balancer failed at this point (if it hasn't already been set) so that clones will know that the owner has 'fallen'
CTaskNMControl* pTaskNMControl = GetControlTask();
if (!pPed->IsNetworkClone() && (pTaskNMControl == NULL || !pTaskNMControl->IsFeedbackFlagSet(CTaskNMControl::BALANCE_FAILURE)))
{
ARTFeedbackInterfaceGta feedbackInterface;
feedbackInterface.SetParentInst(pPed->GetRagdollInst());
strncpy(feedbackInterface.m_behaviourName, NMSTR_PARAM(NM_BALANCE_FB), ART_FEEDBACK_BHNAME_LENGTH);
feedbackInterface.onBehaviourFailure();
}
}
bool CTaskNMRelax::ControlAnimatedFallback(CPed* pPed)
{
nmDebugf3("LOCAL: Controlling animated fallback task %s (0x%p) Ped ragdolling : %s\n", GetSubTask() ? GetSubTask()->GetTaskName() : "None", this, pPed->GetUsingRagdoll() ? "true" : "false");
// disable ped capsule control so the blender can set the velocity directly on the ped
if (pPed->IsNetworkClone())
{
NetworkInterface::UseAnimatedRagdollFallbackBlending(*pPed);
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisablePedCapsuleControl, true);
}
if (!GetSubTask() || GetIsFlagSet(aiTaskFlags::SubTaskFinished))
{
m_nSuggestedNextTask = CTaskTypes::TASK_FINISHED;
if (!pPed->GetIsDeadOrDying())
{
if (pPed->ShouldBeDead())
{
CEventDeath deathEvent(false, fwTimer::GetTimeInMilliseconds(), true);
pPed->GetPedIntelligence()->AddEvent(deathEvent);
}
else
{
m_nSuggestedNextTask = CTaskTypes::TASK_BLEND_FROM_NM;
}
}
return false;
}
return true;
}