Files
GTASource/game/task/Combat/Subtasks/TaskShellShocked.cpp
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

473 lines
11 KiB
C++

// FILE : TaskShellShocked.cpp
// PURPOSE : Combat subtask in control of being shell-shocked
// CREATED : 3/12/2012
// File header
#include "Task/Combat/Subtasks/TaskShellShocked.h"
// Game headers
#include "animation/EventTags.h"
#include "animation/Move.h"
#include "animation/MovePed.h"
#include "event/EventDamage.h"
#include "Peds/ped.h"
#include "Peds/PedIntelligence.h"
#include "weapons/inventory/PedWeaponManager.h"
AI_OPTIMISATIONS()
//=========================================================================
// CTaskShellShocked
//=========================================================================
//Static Initialization -- MoVE
fwMvBooleanId CTaskShellShocked::ms_StaggerClipEndedId("StaggerClipEnded",0xD7D657B9);
fwMvClipId CTaskShellShocked::ms_StaggerClipId("StaggerClip",0xFEBBD768);
fwMvFlagId CTaskShellShocked::ms_ShortId("Short",0x9D43FEE8);
fwMvFlagId CTaskShellShocked::ms_LongId("Long",0xDAA2D5EC);
fwMvFlagId CTaskShellShocked::ms_FrontId("Front",0x89F230A2);
fwMvFlagId CTaskShellShocked::ms_BackId("Back",0x37418674);
fwMvFlagId CTaskShellShocked::ms_LeftId("Left",0x6FA34840);
fwMvFlagId CTaskShellShocked::ms_RightId("Right",0xB8CCC339);
fwMvFloatId CTaskShellShocked::ms_RateId("Rate",0x7E68C088);
fwMvFloatId CTaskShellShocked::ms_PhaseId("Phase",0xA27F482B);
fwMvRequestId CTaskShellShocked::ms_StaggerId("Stagger",0xEE57898E);
CTaskShellShocked::CTaskShellShocked(Vec3V_In vPosition, Duration nDuration, fwFlags8 uConfigFlags)
: m_ClipSetRequestHelper()
, m_MoveNetworkHelper()
, m_vPosition(vPosition)
, m_nDuration(nDuration)
, m_nDirection(CDirectionHelper::D_Invalid)
, m_ClipSetId(CLIP_SET_ID_INVALID)
, m_uConfigFlags(uConfigFlags)
{
SetInternalTaskType(CTaskTypes::TASK_SHELL_SHOCKED);
}
CTaskShellShocked::~CTaskShellShocked()
{
}
void CTaskShellShocked::CleanUp()
{
//Grab the ped.
CPed* pPed = GetPed();
//Ensure the ped is valid.
if(pPed)
{
//Check if the move network is active.
if(m_MoveNetworkHelper.IsNetworkActive())
{
//Clear the move network.
pPed->GetMovePed().ClearTaskNetwork(m_MoveNetworkHelper, NORMAL_BLEND_DURATION);
}
}
}
aiTask* CTaskShellShocked::Copy() const
{
return rage_new CTaskShellShocked(m_vPosition, m_nDuration, m_uConfigFlags);
}
bool CTaskShellShocked::IsValid(const CPed& rPed, Vec3V_In vPosition)
{
//Ensure we are not running normally.
if(!rPed.GetMotionData()->GetIsLessThanRun() && !rPed.IsStrafing())
{
return false;
}
//Ensure we are not fleeing.
if(rPed.GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_SMART_FLEE, true) ||
rPed.GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_REACT_AND_FLEE, true))
{
return false;
}
//Ensure the clip set is valid.
if(ChooseClipSet(rPed) == CLIP_SET_ID_INVALID)
{
return false;
}
//Ensure the direction is valid.
if(ChooseDirection(rPed, vPosition, false) == CDirectionHelper::D_Invalid)
{
return false;
}
return true;
}
#if !__FINAL
const char* CTaskShellShocked::GetStaticStateName(s32 iState)
{
Assert(iState >= State_Start && iState <= State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_Stream",
"State_Stagger",
"State_Finish",
};
return aStateNames[iState];
}
#endif
CTask::FSM_Return CTaskShellShocked::ProcessPreFSM()
{
//Process the streaming.
ProcessStreaming();
return FSM_Continue;
}
CTask::FSM_Return CTaskShellShocked::UpdateFSM(const s32 iState, const FSM_Event iEvent)
{
FSM_Begin
FSM_State(State_Start)
FSM_OnEnter
Start_OnEnter();
FSM_OnUpdate
return Start_OnUpdate();
FSM_State(State_Stream)
FSM_OnUpdate
return Stream_OnUpdate();
FSM_State(State_Stagger)
FSM_OnEnter
Stagger_OnEnter();
FSM_OnUpdate
return Stagger_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
void CTaskShellShocked::Start_OnEnter()
{
//Choose the clip set.
m_ClipSetId = ChooseClipSet(*GetPed());
//Choose the direction.
m_nDirection = ChooseDirection(*GetPed(), m_vPosition, true);
}
CTask::FSM_Return CTaskShellShocked::Start_OnUpdate()
{
//Ensure the clip set is valid.
if(m_ClipSetId == CLIP_SET_ID_INVALID)
{
return FSM_Quit;
}
//Ensure the direction is valid.
if(m_nDirection == CDirectionHelper::D_Invalid)
{
return FSM_Quit;
}
//Move to the stream state.
SetState(State_Stream);
return FSM_Continue;
}
CTask::FSM_Return CTaskShellShocked::Stream_OnUpdate()
{
//Stream the MoVE network.
bool bNetworkIsStreamedIn = fwAnimDirector::RequestNetworkDef(CClipNetworkMoveInfo::ms_NetworkTaskShellShocked);
//Stream the clip set.
bool bClipSetIsStreamedIn = m_ClipSetRequestHelper.IsLoaded();
//Check if everything is streamed in.
if(bNetworkIsStreamedIn && bClipSetIsStreamedIn)
{
//Grab the ped.
CPed* pPed = GetPed();
//Set up the move network on the ped.
m_MoveNetworkHelper.CreateNetworkPlayer(pPed, CClipNetworkMoveInfo::ms_NetworkTaskShellShocked);
pPed->GetMovePed().SetTaskNetwork(m_MoveNetworkHelper.GetNetworkPlayer(), NORMAL_BLEND_DURATION);
//Set the clip set.
m_MoveNetworkHelper.SetClipSet(m_ClipSetId);
//Move to the stagger state.
SetState(State_Stagger);
}
return FSM_Continue;
}
void CTaskShellShocked::Stagger_OnEnter()
{
//Check if we should make the ped fatigued.
if(ShouldMakeFatigued())
{
//Make the ped fatigued.
MakeFatigued();
}
//Set the duration.
switch(m_nDuration)
{
case Duration_Short:
{
m_MoveNetworkHelper.SetFlag(true, ms_ShortId);
break;
}
case Duration_Long:
{
m_MoveNetworkHelper.SetFlag(true, ms_LongId);
break;
}
default:
{
taskAssertf(false, "Unknown duration: %d.", m_nDuration);
break;
}
}
//Set the rate.
static dev_float s_fMinRate = 0.8f;
static dev_float s_fMaxRate = 1.2f;
fwRandom::SetRandomSeed(GetPed()->GetRandomSeed());
m_MoveNetworkHelper.SetFloat(ms_RateId, fwRandom::GetRandomNumberInRange(s_fMinRate, s_fMaxRate));
//Check the direction.
switch(m_nDirection)
{
case CDirectionHelper::D_FrontLeft:
case CDirectionHelper::D_FrontRight:
{
m_MoveNetworkHelper.SetFlag(true, ms_FrontId);
break;
}
case CDirectionHelper::D_BackLeft:
case CDirectionHelper::D_BackRight:
{
m_MoveNetworkHelper.SetFlag(true, ms_BackId);
break;
}
case CDirectionHelper::D_Left:
{
m_MoveNetworkHelper.SetFlag(true, ms_LeftId);
break;
}
case CDirectionHelper::D_Right:
{
m_MoveNetworkHelper.SetFlag(true, ms_RightId);
break;
}
default:
{
taskAssertf(false, "Unknown direction: %d.", m_nDirection);
break;
}
}
//Send the stagger request.
m_MoveNetworkHelper.SendRequest(ms_StaggerId);
//Wait for the stagger clip to end.
m_MoveNetworkHelper.WaitForTargetState(ms_StaggerClipEndedId);
}
CTask::FSM_Return CTaskShellShocked::Stagger_OnUpdate()
{
//Check if the target state has been reached, or we should interrupt.
if(m_MoveNetworkHelper.IsInTargetState() || ShouldInterrupt())
{
//Finish the task.
SetState(State_Finish);
}
return FSM_Continue;
}
void CTaskShellShocked::MakeFatigued()
{
taskAssert(ShouldMakeFatigued());
//Ensure the ped is not already fatigued.
float fHealth = GetPed()->GetHealth();
float fThreshold = GetPed()->GetFatiguedHealthThreshold();
float fDifference = fHealth - fThreshold;
if(fDifference <= 0.0f)
{
return;
}
//Create the damage event.
CEventDamage tempDamageEvent(NULL, fwTimer::GetTimeInMilliseconds(), WEAPONTYPE_FALL);
//Apply the damage.
float fDamage = fDifference + 1.0f;
CPedDamageCalculator damageCalculator(NULL, fDamage, WEAPONTYPE_FALL, RAGDOLL_HEAD, false);
damageCalculator.ApplyDamageAndComputeResponse(GetPed(), tempDamageEvent.GetDamageResponseData(), CPedDamageCalculator::DF_None);
}
void CTaskShellShocked::ProcessStreaming()
{
//Stream the clip set.
m_ClipSetRequestHelper.Request(m_ClipSetId);
}
bool CTaskShellShocked::ShouldInterrupt() const
{
//Ensure the flag is set.
if(!m_uConfigFlags.IsFlagSet(CF_Interrupt))
{
return false;
}
//Ensure the clip is valid.
const crClip* pClip = m_MoveNetworkHelper.GetClip(ms_StaggerClipId);
if(!pClip)
{
return false;
}
//Find the event phase.
float fInterruptiblePhase;
if(!CClipEventTags::FindEventPhase(pClip, CClipEventTags::Interruptible, fInterruptiblePhase))
{
return false;
}
//Ensure the phase has exceeded the interruptible phase.
float fPhase = m_MoveNetworkHelper.GetFloat(ms_PhaseId);
if(fPhase < fInterruptiblePhase)
{
return false;
}
return true;
}
bool CTaskShellShocked::ShouldMakeFatigued() const
{
//Ensure the config flag is not set.
if(m_uConfigFlags.IsFlagSet(CF_DisableMakeFatigued))
{
return false;
}
//Ensure the ped is random.
if(!GetPed()->PopTypeIsRandom())
{
return false;
}
return true;
}
fwMvClipSetId CTaskShellShocked::ChooseClipSet(const CPed& rPed)
{
//Ensure the weapon manager is valid.
const CPedWeaponManager* pWeaponManager = rPed.GetWeaponManager();
if(!pWeaponManager)
{
return CLIP_SET_ID_INVALID;
}
//Ensure the equipped weapon info is valid.
const CWeaponInfo* pWeaponInfo = pWeaponManager->GetEquippedWeaponInfo();
if(!pWeaponInfo)
{
return CLIP_SET_ID_INVALID;
}
return pWeaponInfo->GetShellShockedClipSetId(rPed);
}
CDirectionHelper::Direction CTaskShellShocked::ChooseDirection(const CPed& rPed, Vec3V_In vPosition, bool bUpdateTime)
{
//Calculate the closest direction.
CDirectionHelper::Direction nDirection = CDirectionHelper::CalculateClosestDirection(
rPed.GetTransform().GetPosition(), rPed.GetCurrentHeading(), vPosition);
taskAssert(nDirection != CDirectionHelper::D_Invalid);
//Ensure the ped is on-screen.
if(!rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_VisibleOnScreen))
{
return nDirection;
}
//Get the left/right directions.
CDirectionHelper::Direction nDirectionToLeft = CDirectionHelper::MoveDirectionToLeft(nDirection);
CDirectionHelper::Direction nDirectionToRight = CDirectionHelper::MoveDirectionToRight(nDirection);
taskAssert(nDirectionToLeft != CDirectionHelper::D_Invalid);
taskAssert(nDirectionToRight != CDirectionHelper::D_Invalid);
//Choose the direction that was used furthest in the past.
static u32 s_uTimes[CDirectionHelper::D_Max];
static bool s_bInitialized = false;
if(!s_bInitialized)
{
s_bInitialized = true;
for(int i = 0; i < CDirectionHelper::D_Max; ++i)
{
s_uTimes[i] = 0;
}
}
//Choose the best time.
u32 uTime = s_uTimes[nDirection];
u32 uTimeToLeft = s_uTimes[nDirectionToLeft];
u32 uTimeToRight = s_uTimes[nDirectionToRight];
u32 uTimeBest = Min(Min(uTime, uTimeToLeft), uTimeToRight);
//Choose a direction.
static const int s_iMaxDirections = 3;
CDirectionHelper::Direction aDirections[s_iMaxDirections];
int iNumDirections = 0;
//Check the directions.
if(uTimeBest == uTime)
{
aDirections[iNumDirections++] = nDirection;
}
else if(uTimeBest == uTimeToLeft)
{
aDirections[iNumDirections++] = nDirectionToLeft;
}
else if(uTimeBest == uTimeToRight)
{
aDirections[iNumDirections++] = nDirectionToRight;
}
//Ensure the directions are valid.
if(iNumDirections <= 0)
{
return nDirection;
}
//Choose a direction.
fwRandom::SetRandomSeed(rPed.GetRandomSeed());
int iIndex = fwRandom::GetRandomNumberInRange(0, iNumDirections);
CDirectionHelper::Direction nDirectionToUse = aDirections[iIndex];
//Set the time.
if(bUpdateTime && (nDirectionToUse != CDirectionHelper::D_Invalid))
{
s_uTimes[nDirectionToUse] = fwTimer::GetTimeInMilliseconds();
}
return nDirectionToUse;
}