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

758 lines
21 KiB
C++

// Framework headers
#include "ai/task/taskchannel.h" // For channel asserts and verifies
#include "math/angmath.h"
// Game headers
#include "Animation/moveped.h"
#include "Game/ModelIndices.h"
#include "Task/Combat/TaskReact.h"
#include "Event/EventReactions.h" // For adding new reaction events
#include "Peds/Ped.h"
#include "Peds/PedIntelligence.h" // For accessing pedintelligence
#include "Peds/PedWeapons/PedTargeting.h" // For LOS targetting of ped targer
#include "Peds/Rendering/PedVariationPack.h"
#include "streaming/streaming.h" // For CStreaming::HasObjectLoaded(), etc.
#include "Task/Combat/TaskThreatResponse.h"
#include "Task/Default/TaskChat.h"
#include "Task/Default/Patrol/TaskPatrol.h"
#include "Task/Weapons/Gun/TaskGun.h"
#include "Task/Weapons/WeaponController.h"
#include "Weapons/Info/WeaponInfoManager.h"
#include "Task/General/TaskBasic.h"
#include "Task/Motion/Locomotion/TaskMotionPed.h"
#include "Task/Motion/TaskMotionBase.h"
#include "Task/Movement/Jumping/TaskFallGetUp.h"
#include "Task/Movement/TaskCollisionResponse.h"
#if __BANK
#include "camera/CamInterface.h"
#include "Peds/Ped.h"
#include "Peds/PedDebugVisualiser.h"
#include "Peds/PedFactory.h"
#include "script/script.h"
#include "fwsys/timer.h"
#include "Scene/World/GameWorld.h"
#include "Task/Combat/TaskNewCombat.h"
#include "Task/Movement/TaskNavMesh.h"
#include "Task/Physics/TaskBlendFromNM.h"
#include "Physics/Physics.h"
#include "debug/DebugScene.h"
#endif // __BANK
AI_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
//////////////////////////////////////////////////////////////////////////
// CTaskReactAimWeapon
//////////////////////////////////////////////////////////////////////////
fwMvClipSetVarId CTaskReactAimWeapon::sm_AdditionalAnimsClipSet("AimClipSet",0xF15A6053);
fwMvRequestId CTaskReactAimWeapon::ms_ReactBlendRequest("ReactBlend",0x59F3BE88);
fwMvRequestId CTaskReactAimWeapon::ms_AimRequest("Aim",0xB01E2F36);
fwMvBooleanId CTaskReactAimWeapon::ms_OnEnterReactBlend("OnEnterReactBlend",0x63124675);
fwMvBooleanId CTaskReactAimWeapon::ms_ReactBlendFinished("ReactBlendFinished",0xBE6492F4);
fwMvFloatId CTaskReactAimWeapon::ms_ReactBlendDirection("ReactBlendDirection",0xD489B765);
fwMvFloatId CTaskReactAimWeapon::ms_PitchId("Pitch",0x3F4BB8CC);
fwMvFloatId CTaskReactAimWeapon::ms_ReactRateId("ReactRate",0xCF3F86FD);
fwMvFlagId CTaskReactAimWeapon::ms_HasSixDirectionsId("HasSixDirections",0x5F9E7626);
CTaskReactAimWeapon::Tunables CTaskReactAimWeapon::sm_Tunables;
IMPLEMENT_COMBAT_TASK_TUNABLES(CTaskReactAimWeapon, 0x24f8cd8e);
//--------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------
CTaskFSMClone* CClonedReactAimWeaponInfo::CreateCloneFSMTask(void)
{
CAITarget target;
m_weaponTargetHelper.UpdateTarget(target);
return rage_new CTaskReactAimWeapon(target, m_flags);
}
//--------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------
CTaskReactAimWeapon::CTaskReactAimWeapon(const CPed* pTarget, fwFlags8 uConfigFlags)
: m_target(pTarget)
, m_fReactBlendCurrentHeading(0.0f)
, m_fReactBlendReferenceHeading(0.0f)
, m_fCurrentPitch(-1.0f)
, m_BaseClipSetId(CLIP_SET_ID_INVALID)
, m_WeaponClipSetId(CLIP_SET_ID_INVALID)
, m_fRate(1.0f)
, m_uConfigFlags(uConfigFlags)
, m_bHasSixDirections(false)
, m_bHasCreateWeaponTag(false)
, m_bHasInterruptTag(false)
, m_bUsingOverrideClipSet(false)
, m_bNoLongerRunningOnOwner(false)
{
SetInternalTaskType(CTaskTypes::TASK_REACT_AIM_WEAPON);
}
CTaskReactAimWeapon::CTaskReactAimWeapon(const CWeaponTarget& target, fwFlags8 uConfigFlags)
: m_target(target)
, m_fReactBlendCurrentHeading(0.0f)
, m_fReactBlendReferenceHeading(0.0f)
, m_fCurrentPitch(-1.0f)
, m_BaseClipSetId(CLIP_SET_ID_INVALID)
, m_WeaponClipSetId(CLIP_SET_ID_INVALID)
, m_fRate(1.0f)
, m_uConfigFlags(uConfigFlags)
, m_bHasSixDirections(false)
, m_bHasCreateWeaponTag(false)
, m_bHasInterruptTag(false)
, m_bNoLongerRunningOnOwner(false)
{
SetInternalTaskType(CTaskTypes::TASK_REACT_AIM_WEAPON);
}
CTaskReactAimWeapon::~CTaskReactAimWeapon()
{
}
void CTaskReactAimWeapon::CleanUp()
{
CPed* pPed = GetPed();
if(pPed && m_moveNetworkHelper.IsNetworkActive())
{
TUNE_GROUP_FLOAT(AIM_REACT_TUNE, BLEND_OUT_DURATION, NORMAL_BLEND_DURATION, 0.0f, 1.0f, 0.01f);
pPed->GetMovePed().ClearTaskNetwork(m_moveNetworkHelper, BLEND_OUT_DURATION);
}
if (pPed->IsNetworkClone() && GetState() == State_PlayReaction)
{
pPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Aiming);
pPed->SetPedResetFlag(CPED_RESET_FLAG_AimWeaponReactionRunning, true);
pPed->SetPedResetFlag(CPED_RESET_FLAG_SkipAimingIdleIntro, true);
}
}
CTask::FSM_Return CTaskReactAimWeapon::ProcessPreFSM()
{
if(!m_target.GetIsValid())
{
return FSM_Quit;
}
if(GetPed()->GetIsInVehicle())
{
return FSM_Quit;
}
return FSM_Continue;
}
CTask::FSM_Return CTaskReactAimWeapon::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_StreamAnims)
FSM_OnUpdate
return StreamAnims_OnUpdate();
FSM_State(State_PlayReaction)
FSM_OnEnter
return PlayReaction_OnEnter();
FSM_OnUpdate
return PlayReaction_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
bool CTaskReactAimWeapon::ProcessMoveSignals()
{
//Check the state.
switch(GetState())
{
case State_PlayReaction:
{
return PlayReaction_OnProcessMoveSignals();
}
default:
{
break;
}
}
return false;
}
void CTaskReactAimWeapon::Start_OnEnter()
{
//Ensure we have a gun equipped.
bool bIsGun2Handed = false;
const CWeaponInfo* pEquippedGunInfo = GetEquippedGunInfo(bIsGun2Handed);
if(!pEquippedGunInfo)
{
return;
}
//Choose the ability.
Tunables::Ability* pAbility = (GetPed()->GetPedIntelligence()->GetCombatBehaviour().GetCombatAbility() == CCombatData::CA_Professional) ?
&sm_Tunables.m_Professional : &sm_Tunables.m_NotProfessional;
//Choose the situation.
Tunables::Ability::Situation* pSituation = &pAbility->m_None;
if(!GetPed()->GetPedIntelligence()->GetCombatBehaviour().IsFlagSet(CCombatData::BF_DisableEntryReactions))
{
if(m_uConfigFlags.IsFlagSet(CF_Flinch))
{
pSituation = &pAbility->m_Flinch;
}
else if(m_uConfigFlags.IsFlagSet(CF_Surprised))
{
pSituation = &pAbility->m_Surprised;
}
else if(m_uConfigFlags.IsFlagSet(CF_Sniper))
{
pSituation = &pAbility->m_Sniper;
}
}
//Choose the weapon.
Tunables::Ability::Situation::Weapon* pWeapon = NULL;
if(!bIsGun2Handed)
{
if(pEquippedGunInfo->GetGroup() == WEAPONGROUP_SMG)
{
pWeapon = &pSituation->m_MicroSMG;
}
else
{
pWeapon = &pSituation->m_Pistol;
}
}
else
{
pWeapon = &pSituation->m_Rifle;
}
float fRate = 1.0f;
fwMvClipSetId overrideReactionClipSet = pEquippedGunInfo->GetCombatReactionOverride(*GetPed());
if(overrideReactionClipSet != CLIP_SET_ID_INVALID)
{
m_BaseClipSetId = fwMvClipSetId(overrideReactionClipSet);
m_bHasSixDirections = false;
m_bHasCreateWeaponTag = true;
m_bHasInterruptTag = true;
m_bUsingOverrideClipSet = true;
}
else
{
m_BaseClipSetId = fwMvClipSetId(pWeapon->m_ClipSetId.GetHash());
//Check if the clip has six directions.
m_bHasSixDirections = pWeapon->m_HasSixDirections;
//Calculate the rate.
fRate = sm_Tunables.m_Rate * pWeapon->m_Rate;
//Check if the clip has a create weapon tag.
m_bHasCreateWeaponTag = pWeapon->m_HasCreateWeaponTag;
//Check if the clip has an interrupt tag.
m_bHasInterruptTag = pWeapon->m_HasInterruptTag;
}
if(!m_bHasCreateWeaponTag)
{
//Create the weapon.
CreateWeapon();
}
//Apply the rate variance.
float fMaxRateVariance = sm_Tunables.m_MaxRateVariance;
float fMinRateMultiplier = 1.0f - fMaxRateVariance;
float fMaxRateMultiplier = 1.0f + fMaxRateVariance;
float fRateMultiplier = fwRandom::GetRandomNumberInRange(fMinRateMultiplier, fMaxRateMultiplier);
fRate *= fRateMultiplier;
//Set the rate.
m_fRate = fRate;
//If we can see our target then play the draw_gun line
if(m_target.GetEntity())
{
CPedTargetting* pPedTargetting = GetPed()->GetPedIntelligence()->GetTargetting( false );
if(pPedTargetting)
{
LosStatus losStatus = pPedTargetting->GetLosStatus(m_target.GetEntity(), false);
if(losStatus == Los_clear || losStatus == Los_blockedByFriendly)
{
GetPed()->NewSay("DRAW_GUN");
}
}
}
}
CTask::FSM_Return CTaskReactAimWeapon::Start_OnUpdate()
{
// If for some reason we didn't get a valid clip set then quit the task
if(m_BaseClipSetId == CLIP_SET_ID_INVALID)
{
return FSM_Quit;
}
// Find our weapon clip set and check if it is loaded
const CWeaponInfo* pWeaponInfo = GetPed()->GetWeaponManager()->GetEquippedWeaponInfo();
if(!pWeaponInfo)
{
return FSM_Quit;
}
// If our weapon doesn't have a valid clip set then we need to return
m_WeaponClipSetId = pWeaponInfo->GetAppropriateWeaponClipSetId(GetPed());
if(m_WeaponClipSetId == CLIP_SET_ID_INVALID)
{
return FSM_Quit;
}
SetState(State_StreamAnims);
return FSM_Continue;
}
CTask::FSM_Return CTaskReactAimWeapon::StreamAnims_OnUpdate()
{
Assert(m_BaseClipSetId != CLIP_SET_ID_INVALID);
Assert(m_WeaponClipSetId != CLIP_SET_ID_INVALID);
if(GetTimeInState() >= 0.5f)
{
return FSM_Quit;
}
// Check if our base clip set Id is loaded
bool bBaseClipSetLoaded = m_clipRequestHelper.Request(m_BaseClipSetId);
bool bWeaponClipSetLoaded = m_weaponClipRequestHelper.Request(m_WeaponClipSetId);;
// Don't play a reaction unless both clip sets are loaded
if(bBaseClipSetLoaded && bWeaponClipSetLoaded)
{
SetState(State_PlayReaction);
}
return FSM_Continue;
}
CTask::FSM_Return CTaskReactAimWeapon::PlayReaction_OnEnter()
{
CPed* pPed = GetPed();
if (pPed->IsNetworkClone())
{
CObject* pWeapon = pPed->GetWeaponManager()->GetEquippedWeaponObject();
if (pWeapon && !pWeapon->GetIsVisible())
{
pWeapon->SetIsVisibleForModule(SETISVISIBLE_MODULE_GAMEPLAY, true, true);
}
}
if (!m_moveNetworkHelper.IsNetworkActive())
{
taskAssertf(fwAnimDirector::RequestNetworkDef(CClipNetworkMoveInfo::ms_NetworkTaskReactAimWeapon), "React aim weapon network is not streamed in!");
m_moveNetworkHelper.CreateNetworkPlayer(pPed, CClipNetworkMoveInfo::ms_NetworkTaskReactAimWeapon);
TUNE_GROUP_FLOAT(AIM_REACT_TUNE, BLEND_IN_DURATION, NORMAL_BLEND_DURATION, 0.0f, 1.0f, 0.01f);
pPed->GetMovePed().SetTaskNetwork(m_moveNetworkHelper.GetNetworkPlayer(), BLEND_IN_DURATION);
}
//Set the directions flag.
m_moveNetworkHelper.SetFlag(m_bHasSixDirections, ms_HasSixDirectionsId);
// Transition to the correct state
m_moveNetworkHelper.SendRequest(ms_ReactBlendRequest);
m_moveNetworkHelper.WaitForTargetState(ms_OnEnterReactBlend);
// Set the correct anim set to use for the get up blend
m_moveNetworkHelper.SetClipSet(m_BaseClipSetId);
//Set the rate.
m_moveNetworkHelper.SetFloat(ms_ReactRateId, m_fRate);
// Set the aim clip set based on our weapon
const CWeaponInfo* pWeaponInfo = GetPed()->GetWeaponManager()->GetEquippedWeaponInfo();
if(pWeaponInfo && m_WeaponClipSetId != CLIP_SET_ID_INVALID)
{
m_moveNetworkHelper.SetClipSet(m_WeaponClipSetId, sm_AdditionalAnimsClipSet);
}
else
{
return FSM_Quit;
}
// Set our initial react blend heading
m_fReactBlendReferenceHeading = pPed->GetCurrentHeading();
m_fReactBlendCurrentHeading = CalculateDirection();
RequestProcessMoveSignalCalls();
m_bMoveCreateWeapon = false;
m_bMoveInterrupt = false;
m_bMoveReactBlendFinished = false;
return FSM_Continue;
}
CTask::FSM_Return CTaskReactAimWeapon::PlayReaction_OnUpdate()
{
CPed* pPed = GetPed();
pPed->GetMotionData()->SetExtraHeadingChangeThisFrame(0.0f);
float fTemp = CalculateDirection();
// Don't wrap
float fDiff = m_fReactBlendCurrentHeading - fTemp;
if(Abs(fDiff) > 0.5f)
{
if(fDiff < 0.0f)
{
fTemp = 0.0f;
}
else
{
fTemp = 1.0f;
}
}
float fCurrentReactBlend = m_fReactBlendCurrentHeading;
CTaskMotionBase::InterpValue(fCurrentReactBlend, fTemp, 1.5f);
// Ensure we don't flip between -0 / 0 anims
fCurrentReactBlend = m_fReactBlendCurrentHeading <= 0.5f ? rage::Clamp(fCurrentReactBlend, 0.0f, 0.5f) : rage::Clamp(fCurrentReactBlend, 0.500001f, 1.0f);
m_fReactBlendCurrentHeading = fCurrentReactBlend;
m_moveNetworkHelper.SetFloat(ms_ReactBlendDirection, m_fReactBlendCurrentHeading);
// Calculate the pitch we want to be aiming at
const CWeaponInfo* pWeaponInfo = GetPed()->GetWeaponManager()->GetEquippedWeaponInfo();
if (pWeaponInfo)
{
const CAimingInfo* pAimingInfo = pWeaponInfo->GetAimingInfo();
if(!pAimingInfo)
{
return FSM_Quit;
}
// Get the sweep ranges from the weapon info
const float fSweepPitchMin = pAimingInfo->GetSweepPitchMin() * DtoR;
const float fSweepPitchMax = pAimingInfo->GetSweepPitchMax() * DtoR;
// Calculate the aim vector (this determines the heading and pitch angles to point the clips in the correct direction)
Vector3 vStart(pPed->GetBonePositionCached((eAnimBoneTag)BONETAG_L_HAND));
Vector3 vEnd;
m_target.GetPosition(vEnd);
// Compute desired pitch angle value
float fDesiredPitch = CTaskAimGun::CalculateDesiredPitch(pPed, vStart, vEnd);
// Map the angle to the range 0.0->1.0
m_fCurrentPitch = CTaskAimGun::ConvertRadianToSignal(fDesiredPitch, fSweepPitchMin, fSweepPitchMax, false);
m_moveNetworkHelper.SetFloat(ms_PitchId, m_fCurrentPitch);
}
if (!m_moveNetworkHelper.IsInTargetState())
return FSM_Continue;
//Check if we have a create weapon tag.
if(m_bHasCreateWeaponTag && m_bMoveCreateWeapon && (!GetPed()->IsNetworkClone()))
{
CreateWeapon();
}
//Check if we have an interrupt tag.
if(m_bHasInterruptTag && m_bMoveInterrupt)
{
m_moveNetworkHelper.SendRequest(ms_AimRequest);
}
else if(!m_bUsingOverrideClipSet)
{
float fTimeToAim = 1.15f * m_fRate;
if (GetTimeInState() >= fTimeToAim)
{
m_moveNetworkHelper.SendRequest(ms_AimRequest);
}
}
if(m_bMoveReactBlendFinished && (!GetPed()->IsNetworkClone() || m_bNoLongerRunningOnOwner))
{
pPed->ForceMotionStateThisFrame(CPedMotionStates::MotionState_Aiming);
pPed->SetPedResetFlag(CPED_RESET_FLAG_AimWeaponReactionRunning, true);
pPed->SetPedResetFlag(CPED_RESET_FLAG_SkipAimingIdleIntro, true);
SetState(State_Finish);
}
if(m_target.GetEntity() && m_target.GetEntity()->GetIsTypePed())
{
CIkRequestBodyLook lookRequest( m_target.GetEntity(), BONETAG_HEAD );
lookRequest.SetRefDirHead( LOOKIK_REF_DIR_FACING );
lookRequest.SetRefDirNeck( LOOKIK_REF_DIR_FACING );
lookRequest.SetRotLimHead( LOOKIK_ROT_ANGLE_YAW, LOOKIK_ROT_LIM_OFF );
lookRequest.SetRotLimHead( LOOKIK_ROT_ANGLE_PITCH, LOOKIK_ROT_LIM_WIDE );
lookRequest.SetRotLimNeck( LOOKIK_ROT_ANGLE_YAW, LOOKIK_ROT_LIM_OFF );
lookRequest.SetRotLimNeck( LOOKIK_ROT_ANGLE_PITCH, LOOKIK_ROT_LIM_WIDE );
lookRequest.SetFlags( LF_WHILE_NOT_IN_FOV );
if(pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodTimesliceIntelligenceUpdate))
{
lookRequest.SetHoldTime( 1000 );
}
else
{
lookRequest.SetHoldTime( 100 );
}
pPed->GetIkManager().Request( lookRequest );
}
return FSM_Continue;
}
bool CTaskReactAimWeapon::PlayReaction_OnProcessMoveSignals()
{
static fwMvBooleanId s_CreateWeaponId("CreateWeapon",0x68C5D794);
m_bMoveCreateWeapon |= m_moveNetworkHelper.GetBoolean(s_CreateWeaponId);
static fwMvBooleanId s_InterruptId("Interrupt",0x6D6BC7B7);
m_bMoveInterrupt |= m_moveNetworkHelper.GetBoolean(s_InterruptId);
m_bMoveReactBlendFinished |= m_moveNetworkHelper.GetBoolean(ms_ReactBlendFinished);
return true;
}
float CTaskReactAimWeapon::CalculateDirection()
{
CPed* pPed = GetPed();
Vector3 vTargetPos;
m_target.GetPosition(vTargetPos);
if(!pPed->IsNetworkClone())
{
pPed->SetDesiredHeading(vTargetPos);
}
float fOriginalHeading = fwAngle::LimitRadianAngleSafe(m_fReactBlendReferenceHeading);
float fDesiredHeading = fwAngle::LimitRadianAngleSafe(pPed->GetDesiredHeading());
TUNE_GROUP_BOOL(ALTERNATE_WEAPONS_ANIMS, bUseSameHeading, false);
if(bUseSameHeading)
{
fDesiredHeading=fOriginalHeading;
}
float fHeadingDelta = SubtractAngleShorter(fDesiredHeading, fOriginalHeading);
fHeadingDelta = Clamp( fHeadingDelta/PI, -1.0f, 1.0f);
return ((-fHeadingDelta * 0.5f) + 0.5f);
}
void CTaskReactAimWeapon::CreateWeapon()
{
//Ensure the weapon manager is valid.
CPedWeaponManager* pWeaponManager = GetPed()->GetWeaponManager();
if(!pWeaponManager)
{
return;
}
//Create the weapon.
pWeaponManager->CreateEquippedWeaponObject();
}
const CWeaponInfo* CTaskReactAimWeapon::GetEquippedGunInfo(bool& bIs2Handed) const
{
//Ensure the weapon manager is valid.
const CPedWeaponManager* pWeaponManager = GetPed()->GetWeaponManager();
if(!pWeaponManager)
{
return NULL;
}
//Ensure the weapon info is valid.
const CWeaponInfo* pWeaponInfo = pWeaponManager->GetEquippedWeaponInfo();
if(!pWeaponInfo)
{
return NULL;
}
//Ensure the weapon is a gun.
if(!pWeaponInfo->GetIsGun())
{
return NULL;
}
//Set the 2-handed state.
bIs2Handed = pWeaponInfo->GetIsGun2Handed();
return pWeaponInfo;
}
// Clone task implementation
void CTaskReactAimWeapon::OnCloneTaskNoLongerRunningOnOwner()
{
m_bNoLongerRunningOnOwner = true;
}
// Since we're playing a really quick anim, don't allow migration....
bool CTaskReactAimWeapon::ControlPassingAllowed(CPed *pPed, const netPlayer& UNUSED_PARAM(player), eMigrationType UNUSED_PARAM(migrationType))
{
if (pPed && pPed->IsNetworkClone() && !IsInScope(pPed))
{
return true;
}
return false;
}
bool CTaskReactAimWeapon::IsInScope(const CPed* pPed)
{
bool inScope = CTaskFSMClone::IsInScope(pPed);
if (inScope)
{
const CWeaponInfo* pWeaponInfo = pPed->GetWeaponManager()->GetEquippedWeaponInfo();
if (pWeaponInfo)
{
// The weapon info could not be synchronized yet. We wait until we have the right one
inScope = (pWeaponInfo->GetAimingInfo() != NULL);
}
}
return inScope;
}
CTaskInfo* CTaskReactAimWeapon::CreateQueriableState() const
{
CClonedReactAimWeaponInfo::InitParams params(GetState(), m_BaseClipSetId, m_WeaponClipSetId, m_uConfigFlags.GetAllFlags(), &m_target, m_fRate, m_bHasSixDirections, m_bHasCreateWeaponTag, m_bHasInterruptTag);
return rage_new CClonedReactAimWeaponInfo(params);
}
void CTaskReactAimWeapon::ReadQueriableState(CClonedFSMTaskInfo* pTaskInfo)
{
Assert( pTaskInfo->GetTaskInfoType() == CTaskInfo::INFO_TYPE_REACT_AIM_WEAPON );
CClonedReactAimWeaponInfo *reactAimWeaponInfo = static_cast<CClonedReactAimWeaponInfo*>(pTaskInfo);
reactAimWeaponInfo->GetWeaponTargetHelper().ReadQueriableState(m_target);
m_BaseClipSetId = reactAimWeaponInfo->GetClipSetId();
m_WeaponClipSetId = reactAimWeaponInfo->GetWeaponClipSetId();
m_fRate = reactAimWeaponInfo->GetRate();
m_bHasSixDirections = reactAimWeaponInfo->GetSixDirections();
m_bHasCreateWeaponTag = reactAimWeaponInfo->GetCreateWeapon();
m_bHasInterruptTag = reactAimWeaponInfo->GetInterrupt();
m_uConfigFlags.SetAllFlags(reactAimWeaponInfo->GetFlags());
CTaskFSMClone::ReadQueriableState(pTaskInfo);
}
CTask::FSM_Return CTaskReactAimWeapon::UpdateClonedFSM(const s32 iState, const FSM_Event iEvent)
{
if(State_Finish != iState)
{
if(iEvent == OnUpdate)
{
if(GetStateFromNetwork() != -1)
{
// We only want to move onto streaming the anims when we know the info
// has been set on the owner.
if((GetStateFromNetwork() > State_Start) && (GetState() == State_Start))
{
SetState(State_StreamAnims);
}
}
}
}
FSM_Begin
FSM_State(State_StreamAnims)
FSM_OnUpdate
return StreamAnims_OnUpdate();
FSM_State(State_PlayReaction)
FSM_OnEnter
return PlayReaction_OnEnter();
FSM_OnUpdate
return PlayReaction_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_State(State_Start)
FSM_End
}
CTaskFSMClone* CTaskReactAimWeapon::CreateTaskForClonePed(CPed* UNUSED_PARAM(pPed))
{
return rage_new CTaskReactAimWeapon(m_target, m_uConfigFlags);
}
CTaskFSMClone* CTaskReactAimWeapon::CreateTaskForLocalPed(CPed* UNUSED_PARAM(pPed))
{
return rage_new CTaskReactAimWeapon(m_target, m_uConfigFlags);
}
#if DEBUG_DRAW
void CTaskReactAimWeapon::DebugRenderClonedInfo(void)
{
if(GetPed())
{
char buffer[256];
sprintf(buffer, "clipSetId %s\nweaponClipSetId %s\nflags %d\nrate %f\nsixDirections %d\ncreateWeaponTag %d\nhasInterruptTag %d",
m_BaseClipSetId.GetCStr(),
m_WeaponClipSetId.GetCStr(),
m_uConfigFlags.GetAllFlags(),
m_fRate,
m_bHasSixDirections,
m_bHasCreateWeaponTag,
m_bHasInterruptTag);
Vector3 pos = VEC3V_TO_VECTOR3(GetPed()->GetTransform().GetPosition());
grcDebugDraw::Text(pos, Color_white, buffer, false);
}
}
#endif /* DEBUG_DRAW */
#if !__FINAL
const char * CTaskReactAimWeapon::GetStaticStateName( s32 iState )
{
taskAssert(iState >= State_Start && iState <= State_Finish);
switch (iState)
{
case State_Start: return "State_Start";
case State_StreamAnims: return "State_StreamAnims";
case State_PlayReaction: return "State_PlayReaction";
case State_Finish: return "State_Finish";
default: taskAssert(0);
}
return "State_Invalid";
}
#endif // !__FINAL