677 lines
22 KiB
C++
677 lines
22 KiB
C++
// File header
|
|
#include "Task/Weapons/TaskBomb.h"
|
|
|
|
// Game headers
|
|
#include "Network/General/NetworkPendingProjectiles.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "Physics/Physics.h"
|
|
#include "System/control.h"
|
|
#include "Task/General/TaskBasic.h"
|
|
#include "Task/Movement/TaskGoToPoint.h"
|
|
#include "Task/Movement/TaskSlideToCoord.h"
|
|
#include "Task/Weapons/TaskSwapWeapon.h"
|
|
#include "Weapons/Weapon.h"
|
|
#include "weapons/projectiles/ProjectileManager.h"
|
|
#include "weapons/projectiles/Projectile.h"
|
|
|
|
// Macro to disable optimisations if set
|
|
AI_OPTIMISATIONS()
|
|
AI_WEAPON_OPTIMISATIONS()
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CTaskBomb
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
fwMvClipId CTaskBomb::ms_PlantInFrontClipId(ATSTRINGHASH("plant_vertical", 0x03009a70e));
|
|
fwMvClipId CTaskBomb::ms_PlantOnGroundClipId(ATSTRINGHASH("plant_floor", 0x0f968b075));
|
|
dev_float CTaskBomb::ms_fVerticalProbeDistance = 1.0f;
|
|
dev_float CTaskBomb::ms_fMaxPlantGroundNormalZ = 0.95f;
|
|
dev_float CTaskBomb::ms_fUphillMaxPlantGroundNormalZ = 0.998f;
|
|
|
|
CTaskBomb::CTaskBomb()
|
|
: m_weaponControllerType(CWeaponController::WCT_Fire)
|
|
, m_fDuration(-1)
|
|
, m_pTargetEntity(NULL)
|
|
, m_vTargetPosition(Vector3::ZeroType)
|
|
, m_vTargetNormal(Vector3::ZeroType)
|
|
, m_bHasToBeAttached(false)
|
|
, m_bClonePlantInFront(false)
|
|
, m_bHasPriorPosition(false)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_BOMB);
|
|
}
|
|
|
|
CTaskBomb::CTaskBomb(const CWeaponController::WeaponControllerType weaponControllerType, float fDuration, bool bHasToBeAttached)
|
|
: m_weaponControllerType(weaponControllerType)
|
|
, m_fDuration(fDuration)
|
|
, m_vTargetPosition(Vector3::ZeroType)
|
|
, m_vTargetNormal(Vector3::ZeroType)
|
|
, m_bHasToBeAttached(bHasToBeAttached)
|
|
, m_bClonePlantInFront(false)
|
|
, m_bHasPriorPosition(false)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_BOMB);
|
|
}
|
|
|
|
CTaskBomb::~CTaskBomb()
|
|
{
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::ProcessPreFSM()
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_TemporarilyBlockWeaponSwitching, true );
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_DisableDynamicCapsuleRadius, true );
|
|
|
|
if (pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_DisableMotionBaseVelocityOverride, true);
|
|
|
|
if(m_ClipId == ms_PlantOnGroundClipId)
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_FPSPlantingBombOnFloor, true);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::UpdateFSM(const s32 iState, const FSM_Event iEvent)
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
weaponAssert( pPed->GetWeaponManager() );
|
|
|
|
FSM_Begin
|
|
FSM_State( State_Init )
|
|
FSM_OnUpdate
|
|
return StateInit_OnUpdate( pPed );
|
|
FSM_State( State_SlideAndPlant )
|
|
FSM_OnEnter
|
|
return StateSlideAndPlant_OnEnter( pPed );
|
|
FSM_OnUpdate
|
|
return StateSlideAndPlant_OnUpdate( pPed );
|
|
FSM_OnExit
|
|
return StateSlideAndPlant_OnExit( pPed );
|
|
FSM_State( State_Swap )
|
|
FSM_OnEnter
|
|
return StateSwap_OnEnter( pPed );
|
|
FSM_OnUpdate
|
|
return StateSwap_OnUpdate( pPed );
|
|
FSM_State( State_Quit )
|
|
FSM_OnUpdate
|
|
return FSM_Quit;
|
|
FSM_End
|
|
}
|
|
|
|
CTaskInfo* CTaskBomb::CreateQueriableState() const
|
|
{
|
|
return rage_new CClonedBombInfo( GetState(), m_vTargetPosition, m_vTargetNormal, m_bClonePlantInFront );
|
|
}
|
|
|
|
CTask::FSM_Return CTaskBomb::UpdateClonedFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CPed *pPed = GetPed(); //Get the ped ptr.
|
|
|
|
FSM_Begin
|
|
FSM_State( State_Init )
|
|
FSM_OnUpdate
|
|
return StateInit_OnUpdateClone( pPed );
|
|
FSM_State( State_SlideAndPlant )
|
|
FSM_OnEnter
|
|
return StateSlideAndPlant_OnEnter( pPed );
|
|
FSM_OnUpdate
|
|
return StateSlideAndPlant_OnUpdate( pPed );
|
|
FSM_OnExit
|
|
return StateSlideAndPlant_OnExit( pPed );
|
|
FSM_State( State_Swap )
|
|
FSM_End
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CTaskBomb::Debug() const
|
|
{
|
|
#if DEBUG_DRAW
|
|
grcDebugDraw::Sphere( m_vTargetPosition, 0.1f, Color_white, false );
|
|
grcDebugDraw::Line( m_vTargetPosition, m_vTargetPosition + m_vTargetNormal, Color_blue );
|
|
#endif // DEBUG_DRAW
|
|
|
|
// Base class
|
|
CTask::Debug();
|
|
}
|
|
|
|
const char * CTaskBomb::GetStaticStateName( s32 iState )
|
|
{
|
|
static const char* aStateNames[] =
|
|
{
|
|
"Init",
|
|
"SlideAndPlant",
|
|
"Swap",
|
|
"Quit",
|
|
};
|
|
|
|
return aStateNames[ iState ];
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateInit_OnUpdate( CPed* pPed )
|
|
{
|
|
if( pPed->GetWeaponManager()->GetRequiresWeaponSwitch() )
|
|
{
|
|
// Swap weapon, as we have the incorrect (or holstered) weapon equipped
|
|
SetState( State_Swap );
|
|
return FSM_Continue;
|
|
}
|
|
else
|
|
{
|
|
static dev_float sfProbeRadius = 0.1f;
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
|
|
WorldProbe::CShapeTestSingleResult capsuleResult;
|
|
capsuleDesc.SetResultsStructure( &capsuleResult );
|
|
capsuleDesc.SetIncludeFlags( (ArchetypeFlags::GTA_ALL_TYPES_WEAPON & ~ArchetypeFlags::GTA_PROJECTILE_TYPE & ~ArchetypeFlags::GTA_PED_TYPE) );
|
|
capsuleDesc.SetTypeFlags( ArchetypeFlags::GTA_WEAPON_TEST );
|
|
capsuleDesc.SetExcludeEntity( pPed );
|
|
capsuleDesc.SetIsDirected( true ); // Only interested in first contact.
|
|
|
|
Vec3V vStart = pPed->GetTransform().GetPosition();
|
|
vStart.SetZf( vStart.GetZf() );
|
|
|
|
float fDesiredHeading = pPed->GetDesiredHeading();
|
|
Vec3V vPedForward( -rage::Sinf( fDesiredHeading ), rage::Cosf( fDesiredHeading ), 0.0f );
|
|
vPedForward = NormalizeSafe( vPedForward, pPed->GetTransform().GetB() );
|
|
Vec3V vEnd = vStart + Scale( vPedForward, ScalarV( ms_fVerticalProbeDistance ) );
|
|
|
|
capsuleDesc.SetCapsule( VEC3V_TO_VECTOR3( vStart ), VEC3V_TO_VECTOR3( vEnd ), sfProbeRadius );
|
|
|
|
// Clear any previous resets
|
|
if( WorldProbe::GetShapeTestManager()->SubmitTest( capsuleDesc ) )
|
|
{
|
|
// Store the intersection position and normal.
|
|
m_pTargetEntity = CPhysics::GetEntityFromInst( capsuleResult[0].GetHitInst() );
|
|
m_vTargetPosition = capsuleResult[0].GetHitPosition();
|
|
m_vTargetNormal = capsuleResult[0].GetHitNormal();
|
|
|
|
m_ClipId = ms_PlantInFrontClipId;
|
|
m_bClonePlantInFront = true; //set this to sync remote
|
|
m_bHasPriorPosition = false;
|
|
|
|
// Switch to move to state
|
|
SetState( State_SlideAndPlant );
|
|
return FSM_Continue;
|
|
}
|
|
|
|
// If this needs to be force planted
|
|
if( !m_bHasToBeAttached )
|
|
{
|
|
m_vTargetPosition = VEC3V_TO_VECTOR3( pPed->GetTransform().GetPosition() );
|
|
m_vTargetNormal.Zero();
|
|
|
|
// Plant on the ground
|
|
m_ClipId = ms_PlantOnGroundClipId;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
pPed->SetPedResetFlag(CPED_RESET_FLAG_FPSPlantingBombOnFloor, true);
|
|
}
|
|
#endif
|
|
static dev_float sf_ActionModeTime = 3.0f;
|
|
if (!pPed->IsUsingStealthMode())
|
|
pPed->SetUsingActionMode(true, CPed::AME_Combat, sf_ActionModeTime);
|
|
|
|
SetState( State_SlideAndPlant );
|
|
return FSM_Continue;
|
|
}
|
|
}
|
|
|
|
// Can't be placed
|
|
return FSM_Quit;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateInit_OnUpdateClone( CPed* pPed )
|
|
{
|
|
const CWeaponInfo* pWeaponInfo = pPed->GetWeaponManager()->GetEquippedWeaponInfo();
|
|
if (!pWeaponInfo || !pWeaponInfo->GetIsProjectile())
|
|
{
|
|
//wait until clone has equipped the bomb
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//Check if clone has been blocked from getting to placement position used on the remote
|
|
Vector3 vCloneExpectedPosition = NetworkInterface::GetLastPosReceivedOverNetwork(pPed);
|
|
Vector3 vCloneActualPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
float fCloneDistanceFromExpectedSq = (vCloneExpectedPosition - vCloneActualPosition).Mag2();
|
|
const float MAX_CLONE_DISTANCE_FROM_EXPECTED_PLACEMENT_POSITION = 0.5f;
|
|
|
|
if(fCloneDistanceFromExpectedSq > rage::square(MAX_CLONE_DISTANCE_FROM_EXPECTED_PLACEMENT_POSITION) )
|
|
{
|
|
NetworkInterface::GoStraightToTargetPosition(pPed);
|
|
}
|
|
|
|
if( GetStateFromNetwork() == State_SlideAndPlant )
|
|
{
|
|
CTaskFSMClone* pMainTask = this;
|
|
|
|
if (GetParent() && GetParent()->IsClonedFSMTask())
|
|
{
|
|
pMainTask = static_cast<CTaskFSMClone*>(GetParent());
|
|
}
|
|
|
|
ProjectileInfo* pProjectileInfo = pMainTask ? CNetworkPendingProjectiles::GetProjectile(pPed, pMainTask->GetNetSequenceID(), false) : NULL;
|
|
|
|
if (pProjectileInfo)
|
|
{
|
|
CTaskInfo* pInfo = pPed->GetPedIntelligence()->GetQueriableInterface()->GetTaskInfoForTaskType(CTaskTypes::TASK_BOMB, PED_TASK_PRIORITY_MAX, false);
|
|
m_vTargetNormal.Zero();
|
|
|
|
if (pInfo)
|
|
{
|
|
CClonedBombInfo* pTaskBombInfo = static_cast<CClonedBombInfo*>(pInfo);
|
|
|
|
m_vTargetPosition = pTaskBombInfo->GetTargetPosition();
|
|
|
|
if(pTaskBombInfo->GetPlantInFront())
|
|
{
|
|
// Plant in front
|
|
m_ClipId = ms_PlantInFrontClipId;
|
|
m_vTargetNormal = pTaskBombInfo->GetTargetNormal();
|
|
m_vTargetNormal.Normalize();
|
|
}
|
|
else
|
|
{
|
|
// Plant on the ground
|
|
m_ClipId = ms_PlantOnGroundClipId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vTargetPosition = VEC3V_TO_VECTOR3( pPed->GetTransform().GetPosition() );
|
|
// Plant on the ground
|
|
m_ClipId = ms_PlantOnGroundClipId;
|
|
}
|
|
|
|
SetState(State_SlideAndPlant);
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateSlideAndPlant_OnEnter( CPed* pPed )
|
|
{
|
|
const CWeapon* pWeapon = pPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if( !pWeapon )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
const CWeaponInfo* pWeaponInfo = pWeapon->GetWeaponInfo();
|
|
if( !pWeaponInfo )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
// when parent task TaskThrowAndAimProjectile starts, it blocks weapon syncing on the clone
|
|
// if we swap weapon to stickybomb and immediately start TaskThrowAndAimProjectile we can
|
|
// block changing weapons before the inventory has changed the weapon to stickybombs.
|
|
// if we catch that and our inventory says we are waiting for the weapon to be applied,
|
|
// force it...
|
|
if( pPed->IsNetworkClone() )
|
|
{
|
|
if(pWeaponInfo->GetHash() != WEAPONTYPE_STICKYBOMB)
|
|
{
|
|
CNetObjPed* netObjPed = static_cast<CNetObjPed*>(pPed->GetNetworkObject());
|
|
if(netObjPed && netObjPed->CacheWeaponInfo())
|
|
{
|
|
if(netObjPed->GetCachedWeaponInfo().m_weapon == WEAPONTYPE_STICKYBOMB)
|
|
{
|
|
netObjPed->UpdateCachedWeaponInfo(true);
|
|
|
|
const CWeapon* pWeapon = pPed->GetWeaponManager()->GetEquippedWeapon();
|
|
if( !pWeapon )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
pWeaponInfo = pWeapon->GetWeaponInfo();
|
|
if( !pWeaponInfo )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert( !m_vTargetPosition.IsZero() );
|
|
|
|
Vector3 vSlideToPosition;
|
|
GetTargetPosition( vSlideToPosition );
|
|
|
|
float fDesiredHeading = m_vTargetNormal.IsZero() ? pPed->GetCurrentHeading() : GetTargetHeading();
|
|
|
|
static dev_float SLIDE_SPEED = 1000.0f;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
FPSStreamingFlags fpsStreamingFlag = FPS_StreamDefault;
|
|
if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
fpsStreamingFlag = FPS_StreamRNG;
|
|
}
|
|
#endif
|
|
|
|
CTaskSlideToCoord* pSlideToCoordTask = rage_new CTaskSlideToCoord( vSlideToPosition, fDesiredHeading, SLIDE_SPEED, m_ClipId, pWeaponInfo->GetAppropriateWeaponClipSetId(pPed FPS_MODE_SUPPORTED_ONLY(, fpsStreamingFlag)), NORMAL_BLEND_IN_DELTA );
|
|
|
|
static dev_float HEADING_SPEED = PI * 2.0f;
|
|
pSlideToCoordTask->SetHeadingIncrement( HEADING_SPEED );
|
|
|
|
static dev_float POS_ACCURACY = 0.045f;
|
|
float fAccuracy = POS_ACCURACY;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if (pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
// Need more precise target position in FPS mode so we don't clip hand through the wall
|
|
static dev_float POS_ACCURACY_FPS = 0.01f;
|
|
fAccuracy = POS_ACCURACY_FPS;
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
pSlideToCoordTask->SetPosAccuracy( fAccuracy );
|
|
|
|
// Just use zero sliding if the ground physical is moving
|
|
pSlideToCoordTask->SetUseZeroVelocityIfGroundPhsyicalIsMoving( true );
|
|
|
|
SetNewTask( pSlideToCoordTask );
|
|
|
|
m_bHasPriorPosition = false;
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateSlideAndPlant_OnUpdate( CPed* pPed )
|
|
{
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_PlacingCharge, true );
|
|
|
|
// Have we finished the task?
|
|
if( GetIsFlagSet( aiTaskFlags::SubTaskFinished ) )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
|
|
bool bStillHasPriorPosition = false;
|
|
CTask* pSubTask = GetSubTask();
|
|
if( pSubTask && pSubTask->GetTaskType() == CTaskTypes::TASK_SLIDE_TO_COORD )
|
|
{
|
|
const CClipHelper* pClipHelper = ((CTaskSlideToCoord*)pSubTask)->GetSubTaskClip();
|
|
if( pClipHelper )
|
|
{
|
|
bool bSimulatedAttach = false;
|
|
|
|
CObject* pWeaponObject = pPed->GetWeaponManager()->GetEquippedWeaponObject();
|
|
if( pWeaponObject )
|
|
{
|
|
CWeapon* pWeapon = pWeaponObject ? pWeaponObject->GetWeapon() : NULL;
|
|
const CWeaponInfo* pWeaponInfo = pWeapon ? pWeapon->GetWeaponInfo() : NULL;
|
|
|
|
// Should we check to see if we should attach to something?
|
|
if( pWeaponInfo && pClipHelper->IsEvent( CClipEventTags::On ) )
|
|
{
|
|
const float fProjectileWidth = pWeaponObject->GetBoundingBoxMax().GetZ() - pWeaponObject->GetBoundingBoxMin().GetZ();
|
|
Vec3V vDirection;
|
|
Vec3V vStart;
|
|
Vec3V vEnd;
|
|
if (m_bHasPriorPosition)
|
|
{
|
|
vStart = m_vPriorBombPosition;
|
|
Vec3V vPosition = pWeaponObject->GetTransform().GetPosition();
|
|
vDirection = Normalize(vPosition - vStart);
|
|
vEnd = vPosition + Scale( vDirection, ScalarV( fProjectileWidth*2.0f ) );
|
|
}
|
|
else
|
|
{
|
|
vDirection = Negate( pWeaponObject->GetTransform().GetC() );
|
|
vStart = pWeaponObject->GetTransform().GetPosition() - Scale( vDirection, ScalarV( fProjectileWidth ) );
|
|
vEnd = pWeaponObject->GetTransform().GetPosition() + Scale( vDirection, ScalarV( fProjectileWidth*2.0f ) );
|
|
}
|
|
#if __BANK
|
|
CTask::ms_debugDraw.AddSphere( vStart, fProjectileWidth, Color_green, 250, atStringHash( "STICKY_WEAPON_PROBE_START_SPHERE" ), false );
|
|
CTask::ms_debugDraw.AddSphere( vEnd, fProjectileWidth, Color_green4, 250, atStringHash( "STICKY_WEAPON_PROBE_END_SPHERE" ), false );
|
|
CTask::ms_debugDraw.AddLine( vStart, vEnd, Color_pink, 250, atStringHash( "STICKY_WEAPON_PROBE_LINE" ) );
|
|
#endif
|
|
DR_PROJECTILE_ONLY(debugPlayback::AddSimpleLine("probe", vStart, vEnd, Color_pink, Color32(0,0,0)));
|
|
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
|
|
WorldProbe::CShapeTestFixedResults<> capsuleResults;
|
|
capsuleDesc.SetResultsStructure( &capsuleResults );
|
|
capsuleDesc.SetCapsule( VEC3V_TO_VECTOR3( vStart ), VEC3V_TO_VECTOR3( vEnd ), fProjectileWidth );
|
|
capsuleDesc.SetIncludeFlags( ArchetypeFlags::GTA_ALL_TYPES_WEAPON & ~ArchetypeFlags::GTA_PROJECTILE_TYPE & ~ArchetypeFlags::GTA_PED_TYPE );
|
|
capsuleDesc.SetTypeFlags( ArchetypeFlags::GTA_WEAPON_TEST );
|
|
capsuleDesc.SetExcludeEntity( pWeaponObject );
|
|
capsuleDesc.SetIsDirected( true );
|
|
if( WorldProbe::GetShapeTestManager()->SubmitTest( capsuleDesc ) )
|
|
{
|
|
CEntity* pHitEntity = NULL;
|
|
int nNumHitResults = capsuleResults.GetNumHits();
|
|
int nBestHitIdx = -1;
|
|
float fDot = -1.0f;
|
|
float fBestHitDot = FLT_MAX;
|
|
for( int i = 0; i < nNumHitResults; ++i )
|
|
{
|
|
pHitEntity = CPhysics::GetEntityFromInst( capsuleResults[i].GetHitInst() );
|
|
if( pHitEntity && CProjectile::ShouldStickToEntity( pHitEntity, pPed, fProjectileWidth, capsuleResults[i].GetHitPositionV(), capsuleResults[i].GetHitComponent(), capsuleResults[i].GetHitMaterialId() ) )
|
|
{
|
|
fDot = Dot( capsuleResults[i].GetHitPositionV(), vDirection ).Getf();
|
|
if( fDot < fBestHitDot )
|
|
{
|
|
nBestHitIdx = i;
|
|
fBestHitDot = fDot;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if we found a collision and use the best candidate
|
|
if( nBestHitIdx > -1 )
|
|
{
|
|
CTaskFSMClone* pParentTask = (GetParent() && GetParent()->IsClonedFSMTask()) ? static_cast<CTaskFSMClone*>(GetParent()) : NULL;
|
|
|
|
if (pPed->IsNetworkClone() && pParentTask)
|
|
{
|
|
if (!CProjectileManager::FireOrPlacePendingProjectile(pPed, pParentTask->GetNetSequenceID()))
|
|
{
|
|
return FSM_Quit; // No longer on pending list so likely detonated or removed by other process while animating slide so let the clone quit
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Instead of going through fire which needs to wait to collect an impact, simply set the impact information here
|
|
Matrix34 weaponMtx = MAT34V_TO_MATRIX34( pWeaponObject->GetMatrix() );
|
|
CProjectile* pNewProjectile = CProjectileManager::CreateProjectile( pWeaponInfo->GetAmmoInfo()->GetHash(), pWeaponInfo->GetHash(), pPed, weaponMtx, pWeaponInfo->GetDamage(), pWeaponInfo->GetDamageType(), pWeaponInfo->GetEffectGroup() );
|
|
if( pNewProjectile )
|
|
{
|
|
// Project the new hit position onto the contact plane
|
|
Vec3V vHitNormal = capsuleResults[nBestHitIdx].GetHitNormalV();
|
|
if (pHitEntity)
|
|
{
|
|
CProjectile::FindAveragedStickySurfaceNormal( GetPed(), *pHitEntity, capsuleResults[nBestHitIdx].GetPosition(), vHitNormal, fProjectileWidth );
|
|
}
|
|
|
|
// Vec3V vHitDirection = pWeaponObject->GetTransform().GetPosition() - capsuleResults[nBestHitIdx].GetHitPositionV(); // Unnecessary due to commented-out part on next line.
|
|
Vec3V vHitPosition = capsuleResults[nBestHitIdx].GetPosition();//pWeaponObject->GetTransform().GetPosition() - Scale( vHitNormal, Dot( vHitDirection, vHitNormal ) );
|
|
|
|
|
|
// Push back the position slightly to match up with the projectile mesh (since all objects use the root position)
|
|
vHitPosition = vHitPosition + Scale( vHitNormal, ScalarV( fProjectileWidth * 0.5f ) );
|
|
#if __BANK
|
|
CTask::ms_debugDraw.AddSphere( vHitPosition, 0.05f, Color_red, 250, atStringHash( "STICKY_WEAPON_PROBE_HIT_POSITION" ), false );
|
|
CTask::ms_debugDraw.AddArrow( vHitPosition, vHitPosition + capsuleResults[nBestHitIdx].GetHitNormalV(), 0.25f, Color_red, 250, atStringHash( "STICKY_WEAPON_PROBE_HIT_DIRECTION" ) );
|
|
#endif
|
|
pNewProjectile->InitLifetimeValues();
|
|
pNewProjectile->StickToEntity( pHitEntity, vHitPosition, vHitNormal, capsuleResults[nBestHitIdx].GetHitComponent(), capsuleResults[nBestHitIdx].GetHitMaterialId() );
|
|
pWeapon->UpdateAmmoAfterFiring(pPed, pWeaponInfo, 1);
|
|
|
|
// Mark that we have attached
|
|
bSimulatedAttach = true;
|
|
}
|
|
}
|
|
|
|
// Ensure the equipped object is destroyed
|
|
pPed->GetWeaponManager()->DestroyEquippedWeaponObject();
|
|
}
|
|
}
|
|
|
|
bStillHasPriorPosition = true;
|
|
m_vPriorBombPosition = pWeaponObject->GetTransform().GetPosition();
|
|
}
|
|
|
|
// Otherwise lets check to see if we should fire
|
|
if( !bSimulatedAttach && pClipHelper->IsEvent( CClipEventTags::Fire ) )
|
|
{
|
|
Vec3V vDirection = Negate(pWeaponObject->GetTransform().GetC());
|
|
Vec3V vStart = pWeaponObject->GetTransform().GetPosition();
|
|
Vec3V vEnd = vStart + Scale( vDirection, ScalarV( 0.1f ) );
|
|
|
|
Assertf(IsFiniteAll(vDirection) && IsFiniteAll(vStart) && IsFiniteAll(vEnd),
|
|
"Invalid bullet parameters initialised in CTaskBomb::StateSlideAndPlant_OnUpdate:"
|
|
"\tDirection: <%5.2f, %5.2f, %5.2f>"
|
|
"\tStart: <%5.2f, %5.2f, %5.2f>"
|
|
"\tEnd: <%5.2f, %5.2f, %5.2f>",
|
|
VEC3V_ARGS(vDirection),VEC3V_ARGS(vStart),VEC3V_ARGS(vEnd));
|
|
|
|
if (IsFiniteAll(vStart) && IsFiniteAll(vEnd))
|
|
{
|
|
if( pWeapon->Fire( CWeapon::sFireParams( pPed, MAT34V_TO_MATRIX34( pWeaponObject->GetMatrix()), &RCC_VECTOR3(vStart), &RCC_VECTOR3(vEnd) ) ) )
|
|
pPed->GetWeaponManager()->DestroyEquippedWeaponObject();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !pPed->IsNetworkClone() && pClipHelper->IsEvent<crPropertyAttributeBool>( CClipEventTags::Object, CClipEventTags::Create, true ) )
|
|
{
|
|
// When the reload anim event happens, create the projectile
|
|
if( !pPed->GetInventory()->GetIsWeaponOutOfAmmo( pPed->GetWeaponManager()->GetEquippedWeaponInfo() ) )
|
|
{
|
|
pPed->GetWeaponManager()->CreateEquippedWeaponObject();
|
|
}
|
|
}
|
|
else if( pClipHelper->IsEvent( CClipEventTags::Interruptible ) && CheckForPlayerMovement(*pPed) )
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_bHasPriorPosition = bStillHasPriorPosition;
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateSlideAndPlant_OnExit( CPed* pPed )
|
|
{
|
|
Assert(pPed);
|
|
|
|
if(pPed->IsNetworkClone())
|
|
{
|
|
return FSM_Continue;
|
|
}
|
|
|
|
const CWeaponInfo* pWeaponInfo = pPed->GetWeaponManager()->GetEquippedWeaponInfo();
|
|
if (pWeaponInfo && !pWeaponInfo->GetIsUnarmed()) //don't remove the unarmed weapon if it is the equipped weapon here (lavalley) - fixes url:bugstar:1920233
|
|
{
|
|
s32 iAmmo = pPed->GetInventory()->GetWeaponAmmo( pWeaponInfo );
|
|
if( iAmmo == 0 )
|
|
{
|
|
// Out of ammo - swap weapon
|
|
pPed->GetInventory()->RemoveWeapon(pWeaponInfo->GetHash());
|
|
pPed->GetWeaponManager()->EquipBestWeapon();
|
|
}
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateSwap_OnEnter( CPed* UNUSED_PARAM( pPed ) )
|
|
{
|
|
s32 iFlags = SWAP_HOLSTER | SWAP_DRAW | SWAP_TO_AIM;
|
|
SetNewTask( rage_new CTaskSwapWeapon( iFlags ) );
|
|
return FSM_Continue;
|
|
}
|
|
|
|
CTaskBomb::FSM_Return CTaskBomb::StateSwap_OnUpdate( CPed* UNUSED_PARAM( pPed ) )
|
|
{
|
|
if( GetIsFlagSet( aiTaskFlags::SubTaskFinished ) )
|
|
{
|
|
// Set state to init to decide what to do next
|
|
SetState( State_Init );
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
bool CTaskBomb::CheckForPlayerMovement(CPed& ped)
|
|
{
|
|
if (ped.IsLocalPlayer())
|
|
{
|
|
CControl* pControl = ped.GetControlFromPlayer();
|
|
|
|
if (pControl->GetPedJump().IsPressed())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Vector2 vecStick(pControl->GetPedWalkLeftRight().GetNorm(), - pControl->GetPedWalkUpDown().GetNorm());
|
|
float fInputMag = vecStick.Mag();
|
|
|
|
if (fInputMag > 0.1f)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static dev_float OFFSET_FROM_TARGET = 0.4367f;
|
|
void CTaskBomb::GetTargetPosition( Vector3& vMoveTarget ) const
|
|
{
|
|
float fOffset = OFFSET_FROM_TARGET;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// FPS anims are slightly different and require a larger offset value
|
|
const CPed *pPed = GetPed();
|
|
if (pPed && pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
static dev_float fFPSOffset = 0.6f;
|
|
fOffset = fFPSOffset;
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
// Work out the point we want to move to
|
|
vMoveTarget = m_vTargetPosition;
|
|
vMoveTarget += m_vTargetNormal * fOffset;
|
|
}
|
|
|
|
float CTaskBomb::GetTargetHeading() const
|
|
{
|
|
Vector3 vNormal( m_vTargetNormal.x, m_vTargetNormal.y, 0.0f );
|
|
vNormal.NormalizeFast();
|
|
return rage::Atan2f( vNormal.x, -vNormal.y );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CClonedBombInfo
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CClonedBombInfo::CClonedBombInfo( s32 state, const Vector3& targetPosition, const Vector3& targetNormal, bool bPlantInFront )
|
|
: m_targetPosition(targetPosition)
|
|
, m_bPlantInFront(bPlantInFront)
|
|
, m_targetNormal(Vector3(0.0f, 0.0f, 0.0f))
|
|
{
|
|
if(bPlantInFront)
|
|
{
|
|
m_targetNormal = targetNormal;
|
|
}
|
|
SetStatusFromMainTaskState( state );
|
|
}
|
|
|
|
CTaskFSMClone* CClonedBombInfo::CreateCloneFSMTask()
|
|
{
|
|
return rage_new CTaskBomb();
|
|
}
|