473 lines
11 KiB
C++
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;
|
|
}
|