Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1175 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // An ingenious device. We call it "The Magnusson Device". Not my chosen label,
  4. // you understand, but it seemed to please the personnel.
  5. //
  6. // From your point of view you simply throw it at a strider and then blow it up.
  7. //
  8. //=============================================================================
  9. #include "cbase.h"
  10. #include "props.h"
  11. #include "vphysics/constraints.h"
  12. #include "physics_saverestore.h"
  13. #include "model_types.h"
  14. #include "ai_utils.h"
  15. #include "particle_system.h"
  16. #include "Sprite.h"
  17. #include "citadel_effects_shared.h"
  18. #include "soundent.h"
  19. #include "SpriteTrail.h"
  20. #include "te_effect_dispatch.h"
  21. #include "beam_shared.h"
  22. #include "npc_strider.h"
  23. #include "npc_hunter.h"
  24. #include "particle_parse.h"
  25. #include "gameweaponmanager.h"
  26. #include "gamestats.h"
  27. extern ConVar hunter_hate_held_striderbusters;
  28. extern ConVar hunter_hate_thrown_striderbusters;
  29. extern ConVar hunter_hate_attached_striderbusters;
  30. ConVar striderbuster_health( "striderbuster_health", "14" );
  31. ConVar striderbuster_autoaim_radius( "striderbuster_autoaim_radius", "64.0f" );
  32. ConVar striderbuster_shot_velocity( "striderbuster_shot_velocity", "2500.0", FCVAR_NONE, "Speed at which launch the bomb from the physcannon" );
  33. ConVar striderbuster_allow_all_damage( "striderbuster_allow_all_damage", "0", FCVAR_NONE, "If set to '1' the bomb will detonate on any damage taken. Otherwise only the player may trigger it." );
  34. //ConVar striderbuster_magnetic_radius("striderbuster_magnetic_radius","400.0f", FCVAR_NONE,"Maximum distance at which magnade experiences attraction to a target. Set to 0 to disable magnetism.");
  35. ConVar striderbuster_magnetic_force_strider("striderbuster_magnetic_force_strider", "750000.0f", FCVAR_NONE,"Intensity of magnade's attraction to a strider.");
  36. ConVar striderbuster_magnetic_force_hunter("striderbuster_magnetic_force_hunter","1750000.0f",FCVAR_NONE,"Intensity of magnade's attraction to a hunter.");
  37. ConVar striderbuster_falloff_power("striderbuster_falloff_power","4",FCVAR_NONE,"Order of the distance falloff. 1 = linear 2 = quadratic");
  38. ConVar striderbuster_leg_stick_dist( "striderbuster_leg_stick_dist", "80.0", FCVAR_NONE, "If the buster hits a strider's leg, the max distance from the head at which it sticks anyway." );
  39. ConVar striderbuster_debugseek( "striderbuster_debugseek", "0" );
  40. ConVar sk_striderbuster_magnet_multiplier( "sk_striderbuster_magnet_multiplier", "2.25" );
  41. ConVar striderbuster_die_detach( "striderbuster_die_detach", "1" ); // Drop off the strider if a hunter shoots me. (Instead of exploding)
  42. ConVar striderbuster_dive_force( "striderbuster_dive_force", "-200" ); // How much force to apply to a nosediving (dead in the air) striderbuster
  43. ConVar striderbuster_use_particle_flare( "striderbuster_use_particle_flare", "1" );
  44. #define STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER 0x00000001 // We were knocked off of a strider after the player attached me.
  45. #define SF_DONT_WEAPON_MANAGE 0x800000
  46. #define STRIDERBUSTER_SPRITE_TRAIL "sprites/bluelaser1.vmt"
  47. string_t g_iszVehicle;
  48. #define BUSTER_PING_SOUND_FREQ 3.0f // How often (seconds) to issue the ping sound to remind players we are attached
  49. static const char *s_pBusterPingThinkContext = "BusterPing";
  50. class CWeaponStriderBuster : public CPhysicsProp
  51. {
  52. DECLARE_CLASS( CWeaponStriderBuster, CPhysicsProp );
  53. DECLARE_DATADESC();
  54. public:
  55. CWeaponStriderBuster( void );
  56. virtual void Precache( void );
  57. virtual void Spawn( void );
  58. virtual void Activate( void );
  59. // Treat as a live target so hunters can attack us
  60. virtual bool IsAlive() { return true; }
  61. virtual void OnRestore( void );
  62. virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
  63. virtual void UpdateOnRemove( void );
  64. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  65. virtual bool ShouldPuntUseLaunchForces( PhysGunForce_t reason ) { return ( reason == PHYSGUN_FORCE_LAUNCHED ); }
  66. virtual QAngle PreferredCarryAngles( void ) { return m_CarryAngles; }
  67. virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; }
  68. virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
  69. virtual void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason );
  70. virtual Vector PhysGunLaunchVelocity( const Vector &forward, float flMass );
  71. virtual float GetAutoAimRadius( void ) { return striderbuster_autoaim_radius.GetFloat(); }
  72. virtual void BusterTouch( CBaseEntity *pOther );
  73. virtual bool ShouldAttractAutoAim( CBaseEntity *pAimingEnt ) { return IsAttachedToStrider(); }
  74. void InputConstraintBroken( inputdata_t &inputdata );
  75. void BusterFlyThink();
  76. void BusterDetachThink();
  77. void BusterPingThink();
  78. void OnAddToCargoHold();
  79. void OnFlechetteAttach( Vector &vecForceDir );
  80. int NumFlechettesAttached() { return m_nAttachedFlechettes; }
  81. float GetPickupTime() { return m_PickupTime; }
  82. int GetStriderBusterFlags() { return m_iBusterFlags; } // I added a flags field so we don't have to keep added bools for all of these contingencies (sjb)
  83. private:
  84. void Launch( CBasePlayer *pPhysGunUser );
  85. void Detonate( void );
  86. void Shatter( CBaseEntity *pAttacker );
  87. bool StickToEntity( CBaseEntity *pOther );
  88. bool CreateConstraintToObject( CBaseEntity *pObject );
  89. void DestroyConstraint( void );
  90. bool ShouldStickToEntity( CBaseEntity *pEntity );
  91. void CreateDestroyedEffect( void );
  92. inline bool IsAttachedToStrider( void ) const;
  93. bool m_bDud;
  94. bool m_bLaunched;
  95. bool m_bNoseDiving; // No magnetism, nosedive and break. Hunter flechettes set this.
  96. int m_nAttachedFlechettes;
  97. float m_flCollisionSpeedSqr;
  98. int m_nAttachedBoneFollowerIndex;
  99. float m_PickupTime;
  100. IPhysicsConstraint *m_pConstraint;
  101. EHANDLE m_hConstrainedEntity;
  102. CHandle<CSprite> m_hGlowSprite;
  103. CHandle<CSprite> m_hMainGlow;
  104. //CHandle<CParticleSystem> m_hGlowTrail;
  105. EHANDLE m_hParticleEffect;
  106. int m_nRingTexture;
  107. QAngle m_CarryAngles;
  108. int m_iBusterFlags;
  109. COutputEvent m_OnAttachToStrider;
  110. COutputEvent m_OnDetonate;
  111. COutputEvent m_OnShatter;
  112. COutputEvent m_OnShotDown;
  113. friend bool StriderBuster_IsAttachedStriderBuster( CBaseEntity *pEntity, CBaseEntity * );
  114. };
  115. LINK_ENTITY_TO_CLASS( prop_stickybomb, CWeaponStriderBuster );
  116. LINK_ENTITY_TO_CLASS( weapon_striderbuster, CWeaponStriderBuster );
  117. BEGIN_DATADESC( CWeaponStriderBuster )
  118. DEFINE_KEYFIELD( m_bDud, FIELD_BOOLEAN, "dud" ),
  119. DEFINE_FIELD( m_bLaunched, FIELD_BOOLEAN ),
  120. DEFINE_FIELD( m_bNoseDiving, FIELD_BOOLEAN ),
  121. DEFINE_FIELD( m_nAttachedFlechettes, FIELD_INTEGER ),
  122. DEFINE_FIELD( m_flCollisionSpeedSqr, FIELD_FLOAT ),
  123. DEFINE_FIELD( m_hConstrainedEntity, FIELD_EHANDLE ),
  124. DEFINE_FIELD( m_hGlowSprite, FIELD_EHANDLE ),
  125. DEFINE_FIELD( m_hMainGlow, FIELD_EHANDLE ),
  126. //DEFINE_FIELD( m_hGlowTrail, FIELD_EHANDLE ),
  127. DEFINE_FIELD( m_nRingTexture, FIELD_INTEGER ),
  128. DEFINE_FIELD( m_nAttachedBoneFollowerIndex, FIELD_INTEGER ),
  129. DEFINE_FIELD( m_PickupTime, FIELD_TIME ),
  130. DEFINE_FIELD( m_hParticleEffect, FIELD_EHANDLE ),
  131. DEFINE_FIELD( m_CarryAngles, FIELD_VECTOR ),
  132. DEFINE_FIELD( m_iBusterFlags, FIELD_INTEGER ),
  133. DEFINE_PHYSPTR( m_pConstraint ),
  134. DEFINE_INPUTFUNC( FIELD_VOID, "ConstraintBroken", InputConstraintBroken ),
  135. DEFINE_OUTPUT( m_OnAttachToStrider, "OnAttachToStrider" ),
  136. DEFINE_OUTPUT( m_OnDetonate, "OnDetonate" ),
  137. DEFINE_OUTPUT( m_OnShatter, "OnShatter" ),
  138. DEFINE_OUTPUT( m_OnShotDown, "OnShotDown" ),
  139. DEFINE_ENTITYFUNC( BusterTouch ),
  140. DEFINE_THINKFUNC( BusterFlyThink ),
  141. DEFINE_THINKFUNC( BusterDetachThink ),
  142. DEFINE_THINKFUNC( BusterPingThink ),
  143. END_DATADESC()
  144. CWeaponStriderBuster::CWeaponStriderBuster( void ) :
  145. m_pConstraint( NULL ),
  146. m_flCollisionSpeedSqr( -1.0f ),
  147. m_hConstrainedEntity( NULL ),
  148. m_nAttachedBoneFollowerIndex( -1 )
  149. {
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose:
  153. //-----------------------------------------------------------------------------
  154. void CWeaponStriderBuster::Precache( void )
  155. {
  156. PrecacheScriptSound( "Weapon_StriderBuster.StickToEntity" );
  157. PrecacheScriptSound( "Weapon_StriderBuster.Detonate" );
  158. PrecacheScriptSound( "Weapon_StriderBuster.Dud_Detonate" );
  159. PrecacheScriptSound( "Weapon_StriderBuster.Ping" );
  160. PrecacheModel("sprites/orangeflare1.vmt");
  161. UTIL_PrecacheOther( "env_citadel_energy_core" );
  162. UTIL_PrecacheOther( "sparktrail" );
  163. m_nRingTexture = PrecacheModel( "sprites/lgtning.vmt" );
  164. PrecacheParticleSystem( "striderbuster_attach" );
  165. PrecacheParticleSystem( "striderbuster_attached_pulse" );
  166. PrecacheParticleSystem( "striderbuster_explode_core" );
  167. PrecacheParticleSystem( "striderbuster_explode_dummy_core" );
  168. PrecacheParticleSystem( "striderbuster_break_flechette" );
  169. PrecacheParticleSystem( "striderbuster_trail" );
  170. PrecacheParticleSystem( "striderbuster_shotdown_trail" );
  171. PrecacheParticleSystem( "striderbuster_break" );
  172. PrecacheParticleSystem( "striderbuster_flechette_attached" );
  173. SetModelName( AllocPooledString("models/magnusson_device.mdl") );
  174. BaseClass::Precache();
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose:
  178. //-----------------------------------------------------------------------------
  179. void CWeaponStriderBuster::Spawn( void )
  180. {
  181. SetModelName( AllocPooledString("models/magnusson_device.mdl") );
  182. BaseClass::Spawn();
  183. // Setup for being shot by the player
  184. m_takedamage = DAMAGE_EVENTS_ONLY;
  185. // Ignore touches until launched.
  186. SetTouch ( NULL );
  187. AddFlag( FL_AIMTARGET|FL_OBJECT );
  188. m_hParticleEffect = CreateEntityByName( "info_particle_system" );
  189. if ( m_hParticleEffect )
  190. {
  191. m_hParticleEffect->KeyValue( "start_active", "1" );
  192. m_hParticleEffect->KeyValue( "effect_name", "striderbuster_smoke" );
  193. DispatchSpawn( m_hParticleEffect );
  194. if ( gpGlobals->curtime > 0.2f )
  195. {
  196. m_hParticleEffect->Activate();
  197. }
  198. m_hParticleEffect->SetAbsOrigin( GetAbsOrigin() );
  199. m_hParticleEffect->SetParent( this );
  200. }
  201. SetHealth( striderbuster_health.GetFloat() );
  202. SetNextThink(gpGlobals->curtime + 0.01f);
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. void CWeaponStriderBuster::Activate( void )
  208. {
  209. g_iszVehicle = AllocPooledString( "prop_vehicle_jeep" );
  210. BaseClass::Activate();
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CWeaponStriderBuster::OnRestore( void )
  216. {
  217. BaseClass::OnRestore();
  218. // If we have an entity we're attached to, attempt to reconstruct our bone follower setup
  219. if ( m_hConstrainedEntity != NULL )
  220. {
  221. CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(m_hConstrainedEntity.Get());
  222. if ( pStrider != NULL )
  223. {
  224. // Make sure we've done this step or we'll have no controller to attach to
  225. pStrider->InitBoneFollowers();
  226. // Attempt to make a connection to the same bone follower we attached to previously
  227. CBoneFollower *pBoneFollower = pStrider->GetBoneFollowerByIndex( m_nAttachedBoneFollowerIndex );
  228. if ( CreateConstraintToObject( pBoneFollower ) == false )
  229. {
  230. Msg( "Failed to reattach to bone follower %d\n", m_nAttachedBoneFollowerIndex );
  231. }
  232. }
  233. }
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose:
  237. //-----------------------------------------------------------------------------
  238. void CWeaponStriderBuster::DestroyConstraint( void )
  239. {
  240. // Destroy the constraint
  241. if ( m_pConstraint != NULL )
  242. {
  243. physenv->DestroyConstraint( m_pConstraint );
  244. m_pConstraint = NULL;
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose: Create a constraint between this object and another
  249. // Input : *pObject - Object to constrain ourselves to
  250. // Output : Returns true on success, false on failure.
  251. //-----------------------------------------------------------------------------
  252. bool CWeaponStriderBuster::CreateConstraintToObject( CBaseEntity *pObject )
  253. {
  254. if ( m_pConstraint != NULL )
  255. {
  256. // Should we destroy the constraint and make a new one at this point?
  257. Assert( 0 );
  258. return false;
  259. }
  260. if ( pObject == NULL )
  261. return false;
  262. IPhysicsObject *pPhysObject = pObject->VPhysicsGetObject();
  263. if ( pPhysObject == NULL )
  264. return false;
  265. IPhysicsObject *pMyPhysObject = VPhysicsGetObject();
  266. if ( pPhysObject == NULL )
  267. return false;
  268. // Create the fixed constraint
  269. constraint_fixedparams_t fixedConstraint;
  270. fixedConstraint.Defaults();
  271. fixedConstraint.InitWithCurrentObjectState( pPhysObject, pMyPhysObject );
  272. IPhysicsConstraint *pConstraint = physenv->CreateFixedConstraint( pPhysObject, pMyPhysObject, NULL, fixedConstraint );
  273. if ( pConstraint == NULL )
  274. return false;
  275. // Hold on to us
  276. m_pConstraint = pConstraint;
  277. pConstraint->SetGameData( (void *)this );
  278. m_hConstrainedEntity = pObject->GetOwnerEntity();;
  279. // Disable collisions between the two ents
  280. PhysDisableObjectCollisions( pPhysObject, pMyPhysObject );
  281. return true;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Physics system has just told us our constraint has been broken
  285. //-----------------------------------------------------------------------------
  286. void CWeaponStriderBuster::InputConstraintBroken( inputdata_t &inputdata )
  287. {
  288. // Shatter with no real explosion effect
  289. Shatter( NULL );
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. void CWeaponStriderBuster::UpdateOnRemove( void )
  295. {
  296. DestroyConstraint();
  297. if ( m_hGlowSprite != NULL )
  298. {
  299. m_hGlowSprite->FadeAndDie( 0.5f );
  300. m_hGlowSprite = NULL;
  301. }
  302. if ( m_hParticleEffect )
  303. {
  304. UTIL_Remove( m_hParticleEffect );
  305. }
  306. BaseClass::UpdateOnRemove();
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose:
  310. // Input : *pEntity -
  311. // Output : Returns true on success, false on failure.
  312. //-----------------------------------------------------------------------------
  313. bool CWeaponStriderBuster::ShouldStickToEntity( CBaseEntity *pEntity )
  314. {
  315. if ( pEntity == NULL )
  316. return false;
  317. // Must have a follow parent
  318. CBaseEntity *pFollowParent = pEntity->GetOwnerEntity();
  319. if ( pFollowParent == NULL )
  320. return false;
  321. // Must be a strider
  322. CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent);
  323. if ( pStrider == NULL )
  324. return false;
  325. if( m_bNoseDiving )
  326. return false;
  327. // Don't attach to legs
  328. CBoneFollower *pFollower = static_cast<CBoneFollower *>(pEntity);
  329. if ( pStrider->IsLegBoneFollower( pFollower ) )
  330. {
  331. Vector vecDelta = pStrider->GetAdjustedOrigin() - GetAbsOrigin();
  332. if ( vecDelta.Length() > striderbuster_leg_stick_dist.GetFloat() )
  333. {
  334. return false;
  335. }
  336. }
  337. // Ick, this is kind of ugly, but it's also ugly having to pass pointer into this to avoid multiple castings!
  338. // Save this to patch up save/restore later
  339. m_nAttachedBoneFollowerIndex = pStrider->GetBoneFollowerIndex( pFollower );
  340. return true;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose: Stick to an entity (using hierarchy if we can)
  344. // Output : Returns true on success, false on failure.
  345. //-----------------------------------------------------------------------------
  346. bool CWeaponStriderBuster::StickToEntity( CBaseEntity *pOther )
  347. {
  348. // Make sure the object is travelling fast enough to stick
  349. if ( m_flCollisionSpeedSqr > 50 && !m_bNoseDiving )
  350. {
  351. // See if this is a valid strider bit
  352. if ( ShouldStickToEntity( pOther ) )
  353. {
  354. // Attempt to constraint to it
  355. if ( CreateConstraintToObject( pOther ) )
  356. {
  357. // Only works for striders, at the moment
  358. CBaseEntity *pFollowParent = pOther->GetOwnerEntity();
  359. if ( pFollowParent == NULL )
  360. return false;
  361. // Allows us to identify our constrained object later
  362. SetOwnerEntity( pFollowParent );
  363. // Make a sound
  364. EmitSound( "Weapon_StriderBuster.StickToEntity" );
  365. DispatchParticleEffect( "striderbuster_attach", GetAbsOrigin(), GetAbsAngles(), NULL );
  366. if( striderbuster_use_particle_flare.GetBool() )
  367. {
  368. // We don't have to save any pointers or handles to this because it's parented to the buster.
  369. // So it will die when the buster dies. Yay.
  370. CParticleSystem *pFlare = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
  371. if ( pFlare != NULL )
  372. {
  373. pFlare->KeyValue( "start_active", "1" );
  374. pFlare->KeyValue( "effect_name", "striderbuster_attached_pulse" );
  375. pFlare->SetParent( this );
  376. pFlare->SetLocalOrigin( vec3_origin );
  377. DispatchSpawn( pFlare );
  378. pFlare->Activate();
  379. }
  380. }
  381. else
  382. {
  383. // Create a glow sprite
  384. m_hGlowSprite = CSprite::SpriteCreate( "sprites/orangeflare1.vmt", GetLocalOrigin(), false );
  385. Assert( m_hGlowSprite );
  386. if ( m_hGlowSprite != NULL )
  387. {
  388. m_hGlowSprite->TurnOn();
  389. m_hGlowSprite->SetTransparency( kRenderWorldGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
  390. m_hGlowSprite->SetAbsOrigin( GetAbsOrigin() );
  391. m_hGlowSprite->SetScale( 5.0f );
  392. m_hGlowSprite->m_nRenderFX = kRenderFxStrobeFaster;
  393. m_hGlowSprite->SetGlowProxySize( 16.0f );
  394. m_hGlowSprite->SetParent( this );
  395. }
  396. }
  397. // Stop touching things
  398. SetTouch( NULL );
  399. // Must be a strider
  400. CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent);
  401. if ( pStrider == NULL )
  402. return false;
  403. // Notify the strider we're attaching to him
  404. pStrider->StriderBusterAttached( this );
  405. m_OnAttachToStrider.FireOutput( this, this );
  406. // Start the ping sound.
  407. SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext );
  408. // Don't autodelete this one!
  409. WeaponManager_RemoveManaged( this );
  410. return true;
  411. }
  412. return false;
  413. }
  414. }
  415. return false;
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose: Create the explosion effect for the final big boom
  419. //-----------------------------------------------------------------------------
  420. void CWeaponStriderBuster::CreateDestroyedEffect( void )
  421. {
  422. CBaseEntity *pTrail;
  423. StopParticleEffects( this );
  424. for ( int i = 0; i < 3; i++ )
  425. {
  426. pTrail = CreateEntityByName( "sparktrail" );
  427. pTrail->SetOwnerEntity( this );
  428. DispatchSpawn( pTrail );
  429. }
  430. DispatchParticleEffect( "striderbuster_explode_core", GetAbsOrigin(), GetAbsAngles() );
  431. // Create liquid fountain gushtacular effect here!
  432. CEffectData data;
  433. int nNumSteps = 6;
  434. float flRadStep = (2*M_PI) / nNumSteps;
  435. for ( int i = 0; i < nNumSteps; i++ )
  436. {
  437. data.m_vOrigin = GetAbsOrigin() + RandomVector( -32.0f, 32.0f );
  438. data.m_vNormal.x = cos( flRadStep*i );
  439. data.m_vNormal.y = sin( flRadStep*i );
  440. data.m_vNormal.z = 0.0f;
  441. data.m_flScale = ( random->RandomInt( 0, 5 ) == 0 ) ? 1 : 2;
  442. DispatchEffect( "StriderBlood", data );
  443. }
  444. // More effects
  445. UTIL_ScreenShake( GetAbsOrigin(), 20.0f, 150.0, 1.0, 1250.0f, SHAKE_START );
  446. data.m_vOrigin = GetAbsOrigin();
  447. DispatchEffect( "cball_explode", data );
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Purpose: Handle a collision using our special behavior
  451. //-----------------------------------------------------------------------------
  452. void CWeaponStriderBuster::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  453. {
  454. // Find out what we hit.
  455. // Don't do anything special if we're already attached to a strider.
  456. CBaseEntity *pVictim = pEvent->pEntities[!index];
  457. if ( pVictim == NULL || m_pConstraint != NULL )
  458. {
  459. BaseClass::VPhysicsCollision( index, pEvent );
  460. return;
  461. }
  462. // Don't attach if we're being held by the player
  463. if ( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  464. {
  465. BaseClass::VPhysicsCollision( index, pEvent );
  466. return;
  467. }
  468. // Save off the speed of the object
  469. m_flCollisionSpeedSqr = ( pEvent->preVelocity[ index ] ).LengthSqr();
  470. // Break if we hit the world while going fast enough.
  471. // Launched duds detonate if they hit the world at any speed.
  472. if ( pVictim->IsWorld() && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) )
  473. {
  474. m_OnShatter.FireOutput( this, this );
  475. Shatter( pVictim );
  476. return;
  477. }
  478. // We'll handle this later in our touch call
  479. if ( ShouldStickToEntity( pVictim ) )
  480. return;
  481. // Determine if we should shatter
  482. CBaseEntity *pOwnerEntity = pVictim->GetOwnerEntity();
  483. bool bVictimIsStrider = ( ( pOwnerEntity != NULL ) && FClassnameIs( pOwnerEntity, "npc_strider" ) );
  484. // Break if we hit anything other than a strider while going fast enough.
  485. // Launched duds detonate if they hit anything other than a strider any speed.
  486. if ( ( bVictimIsStrider == false ) && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) )
  487. {
  488. m_OnShatter.FireOutput( this, this );
  489. Shatter( pVictim );
  490. return;
  491. }
  492. // Just bounce
  493. BaseClass::VPhysicsCollision( index, pEvent );
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose: Called to see if we should attach to the victim
  497. // Input : *pOther - the victim
  498. //-----------------------------------------------------------------------------
  499. void CWeaponStriderBuster::BusterTouch( CBaseEntity *pOther )
  500. {
  501. // Attempt to stick to the entity
  502. StickToEntity( pOther );
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Purpose:
  506. //-----------------------------------------------------------------------------
  507. inline bool CWeaponStriderBuster::IsAttachedToStrider( void ) const
  508. {
  509. CBaseEntity *pAttachedEnt = GetOwnerEntity();
  510. if ( pAttachedEnt && FClassnameIs( pAttachedEnt, "npc_strider" ) )
  511. return true;
  512. return false;
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose:
  516. //-----------------------------------------------------------------------------
  517. void CWeaponStriderBuster::Detonate( void )
  518. {
  519. CBaseEntity *pVictim = GetOwnerEntity();
  520. if ( !m_bDud && pVictim )
  521. {
  522. // Kill the strider (with magic effect)
  523. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  524. CTakeDamageInfo info( pPlayer, this, RandomVector( -100.0f, 100.0f ), GetAbsOrigin(), pVictim->GetHealth(), DMG_GENERIC );
  525. pVictim->TakeDamage( info );
  526. gamestats->Event_WeaponHit( ToBasePlayer( pPlayer ), true, GetClassname(), info );
  527. // Tracker 62293: There's a bug where the inflictor/attacker are reversed when calling TakeDamage above so the player never gets
  528. // credit for the strider buster kills. The code has a bunch of assumptions lower level, so it's safer to just fix it here by
  529. // crediting a kill to the player directly.
  530. gamestats->Event_PlayerKilledOther( pPlayer, pVictim, info );
  531. }
  532. m_OnDetonate.FireOutput( this, this );
  533. // Explode
  534. if ( !m_bDud )
  535. {
  536. CreateDestroyedEffect();
  537. EmitSound( "Weapon_StriderBuster.Detonate" );
  538. }
  539. else
  540. {
  541. DispatchParticleEffect( "striderbuster_explode_dummy_core", GetAbsOrigin(), GetAbsAngles() );
  542. EmitSound( "Weapon_StriderBuster.Dud_Detonate" );
  543. }
  544. // Go to bits!
  545. Shatter( pVictim );
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Purpose: Intercept damage and decide whether or not we want to trigger
  549. // Input : &info -
  550. //-----------------------------------------------------------------------------
  551. int CWeaponStriderBuster::OnTakeDamage( const CTakeDamageInfo &info )
  552. {
  553. // If we're attached, any damage from the player makes us trigger
  554. CBaseEntity *pInflictor = info.GetInflictor();
  555. CBaseEntity *pAttacker = info.GetAttacker();
  556. bool bInflictorIsPlayer = ( pInflictor != NULL && pInflictor->IsPlayer() );
  557. bool bAttackerIsPlayer = ( pAttacker != NULL && pAttacker->IsPlayer() );
  558. if ( GetParent() && GetParent()->ClassMatches( g_iszVehicle ) )
  559. {
  560. return 0;
  561. }
  562. // Only take damage from a player, for the moment
  563. if ( striderbuster_allow_all_damage.GetBool() || ( IsAttachedToStrider() && ( bAttackerIsPlayer || bInflictorIsPlayer ) ) )
  564. {
  565. Detonate();
  566. return 0;
  567. }
  568. if ( pAttacker && ( pAttacker->Classify() == CLASS_COMBINE || pAttacker->Classify() == CLASS_COMBINE_HUNTER ) )
  569. {
  570. if ( VPhysicsGetObject() && !VPhysicsGetObject()->IsMoveable() )
  571. {
  572. return 0;
  573. }
  574. }
  575. // Hunters are able to destroy strider busters
  576. if ( hunter_hate_held_striderbusters.GetBool() || hunter_hate_thrown_striderbusters.GetBool() || hunter_hate_attached_striderbusters.GetBool() )
  577. {
  578. if ( ( GetHealth() > 0 ) && ( pInflictor != NULL ) && FClassnameIs( pInflictor, "hunter_flechette" ) )
  579. {
  580. //
  581. // Flechette impacts don't hurt the striderbuster unless it's attached to a strider,
  582. // but the explosions always do. This is so that held or thrown striderbusters fly
  583. // awry because of the flechette, but attached striderbusters break instantly to make
  584. // the hunters more effective at defending the strider.
  585. //
  586. if ( IsAttachedToStrider() || !( info.GetDamageType() & DMG_NEVERGIB ) )
  587. {
  588. if( striderbuster_die_detach.GetBool() && IsAttachedToStrider() )
  589. {
  590. // Make the buster fall off and break.
  591. m_takedamage = DAMAGE_NO;
  592. CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(GetOwnerEntity());
  593. Assert( pStrider != NULL );
  594. pStrider->StriderBusterDetached( this );
  595. DestroyConstraint();
  596. // Amplify some lateral force.
  597. Vector vecForce = info.GetDamageForce();
  598. vecForce.z = 0.0f;
  599. VPhysicsGetObject()->ApplyForceCenter( vecForce * 5.0f );
  600. SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext );
  601. SetThink( &CWeaponStriderBuster::BusterDetachThink );
  602. SetNextThink( gpGlobals->curtime );
  603. m_iBusterFlags |= STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER;
  604. return 0;
  605. }
  606. else
  607. {
  608. // Destroy the buster in place
  609. // Make sure they know it blew up prematurely.
  610. EmitSound( "Weapon_StriderBuster.Dud_Detonate" );
  611. DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() );
  612. SetHealth( 0 );
  613. Shatter( info.GetAttacker() );
  614. return 0;
  615. }
  616. }
  617. if ( info.GetDamage() < 5 )
  618. {
  619. bool bFirst = ( m_CarryAngles.x == 45 && m_CarryAngles.y == 0 && m_CarryAngles.z == 0);
  620. float sinTime = sin( gpGlobals->curtime );
  621. bool bSubtractX = ( bFirst ) ? ( sinTime < 0 ) : ( m_CarryAngles.x < 45 );
  622. m_CarryAngles.x += ( 10.0 + 10.0 * fabsf( sinTime ) + random->RandomFloat( -2.5, 2.5 ) + random->RandomFloat( -2.5, 2.5 ) ) * ( ( bSubtractX ) ? -1.0 : 1.0 );
  623. m_CarryAngles.y = 15 * ( sin( gpGlobals->curtime ) + cos( gpGlobals->curtime * 0.5 ) ) * .5 + random->RandomFloat( -15, 15 );
  624. m_CarryAngles.z = 7.5 * ( sin( gpGlobals->curtime ) + sin( gpGlobals->curtime * 2.0 ) ) * .5 + random->RandomFloat( -7.5, 7.5 );
  625. }
  626. return 1;
  627. }
  628. }
  629. // Allow crushing damage
  630. if ( info.GetDamageType() & DMG_CRUSH )
  631. return BaseClass::OnTakeDamage( info );
  632. return 0;
  633. }
  634. //-----------------------------------------------------------------------------
  635. //-----------------------------------------------------------------------------
  636. void CWeaponStriderBuster::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  637. {
  638. m_PickupTime = gpGlobals->curtime;
  639. m_CarryAngles.Init( 45, 0, 0 );
  640. if ( ( reason == PICKED_UP_BY_CANNON ) && ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) )
  641. {
  642. WeaponManager_RemoveManaged( this );
  643. }
  644. else if ( reason == PUNTED_BY_CANNON )
  645. {
  646. Launch( pPhysGunUser );
  647. }
  648. BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
  649. }
  650. //-----------------------------------------------------------------------------
  651. //-----------------------------------------------------------------------------
  652. void CWeaponStriderBuster::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  653. {
  654. if ( Reason == LAUNCHED_BY_CANNON )
  655. {
  656. Launch( pPhysGunUser );
  657. }
  658. else if ( ( Reason == DROPPED_BY_CANNON ) && ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) )
  659. {
  660. // This striderbuster is now fair game for autodeletion.
  661. WeaponManager_AddManaged( this );
  662. }
  663. BaseClass::OnPhysGunDrop( pPhysGunUser, Reason );
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Fling the buster with the physcannon either via punt or launch.
  667. //-----------------------------------------------------------------------------
  668. void CWeaponStriderBuster::Launch( CBasePlayer *pPhysGunUser )
  669. {
  670. if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) )
  671. {
  672. WeaponManager_RemoveManaged( this );
  673. }
  674. m_bLaunched = true;
  675. // Notify all nearby hunters that we were launched.
  676. Hunter_StriderBusterLaunched( this );
  677. // Start up the eye glow
  678. m_hMainGlow = CSprite::SpriteCreate( "sprites/blueglow1.vmt", GetLocalOrigin(), false );
  679. if ( m_hMainGlow != NULL )
  680. {
  681. m_hMainGlow->FollowEntity( this );
  682. m_hMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 140, kRenderFxNoDissipation );
  683. m_hMainGlow->SetScale( 2.0f );
  684. m_hMainGlow->SetGlowProxySize( 8.0f );
  685. }
  686. if ( !m_bNoseDiving )
  687. {
  688. DispatchParticleEffect( "striderbuster_trail", PATTACH_ABSORIGIN_FOLLOW, this );
  689. }
  690. else
  691. {
  692. DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this );
  693. }
  694. // We get our touch function from the physics system
  695. SetTouch ( &CWeaponStriderBuster::BusterTouch );
  696. SetThink( &CWeaponStriderBuster::BusterFlyThink );
  697. SetNextThink( gpGlobals->curtime );
  698. gamestats->Event_WeaponFired( pPhysGunUser, true, GetClassname() );
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose:
  702. // Input : &forward -
  703. // flMass -
  704. // Output : Vector
  705. //-----------------------------------------------------------------------------
  706. Vector CWeaponStriderBuster::PhysGunLaunchVelocity( const Vector &forward, float flMass )
  707. {
  708. return ( striderbuster_shot_velocity.GetFloat() * forward );
  709. }
  710. //-----------------------------------------------------------------------------
  711. // Purpose:
  712. //-----------------------------------------------------------------------------
  713. void CWeaponStriderBuster::Shatter( CBaseEntity *pAttacker )
  714. {
  715. if( m_bNoseDiving )
  716. m_OnShotDown.FireOutput( this, this );
  717. m_takedamage = DAMAGE_YES;
  718. if( !IsAttachedToStrider() )
  719. {
  720. // Don't display this particular effect if we're attached to a strider. This effect just gets lost
  721. // in the big strider explosion anyway, so let's recover some perf.
  722. DispatchParticleEffect( "striderbuster_break", GetAbsOrigin(), GetAbsAngles() );
  723. }
  724. // Buster is useless now. Stop thinking, touching.
  725. SetThink( NULL );
  726. SetTouch( NULL );
  727. SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext );
  728. // Deal deadly damage to ourselves (DMG_CRUSH is allowed, others are blocked)
  729. CTakeDamageInfo info( pAttacker, pAttacker, RandomVector( -100, 100 ), GetAbsOrigin(), 100.0f, DMG_CRUSH );
  730. TakeDamage( info );
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Purpose: Give the buster a slight attraction to striders.
  734. // Ported back from the magnade.
  735. //-----------------------------------------------------------------------------
  736. void CWeaponStriderBuster::BusterFlyThink()
  737. {
  738. if (IsAttachedToStrider())
  739. return; // early out. Think no more.
  740. // If we're nosediving, forget about magnetism.
  741. if ( m_bNoseDiving )
  742. {
  743. if ( VPhysicsGetObject() )
  744. VPhysicsGetObject()->ApplyForceCenter( Vector( 0, 0, striderbuster_dive_force.GetFloat() ) );
  745. SetNextThink(gpGlobals->curtime + 0.01f);
  746. return;
  747. }
  748. // seek?
  749. const float magradius = 38.0 * sk_striderbuster_magnet_multiplier.GetFloat(); // radius of strider hull times multiplier
  750. if (magradius > 0 &&
  751. GetMoveType() == MOVETYPE_VPHYSICS &&
  752. VPhysicsGetObject()
  753. )
  754. {
  755. // find the nearest enemy.
  756. CBaseEntity *pList[16];
  757. Vector origin = GetAbsOrigin();
  758. // do a find in box ( a little faster than sphere )
  759. int count;
  760. {
  761. Vector mins,maxs;
  762. mins = origin;
  763. mins -= magradius;
  764. maxs = origin;
  765. maxs += magradius;
  766. count = UTIL_EntitiesInBox(pList, 16, mins, maxs, FL_NPC);
  767. }
  768. float magradiusSq = Square( magradius );
  769. float nearestDistSq = magradiusSq + 1;
  770. int bestFit = -1;
  771. Vector toTarget; // will be garbage unless something good is found
  772. CNPC_Strider *pBestStrider = NULL;
  773. for ( int ii = 0 ; ii < count ; ++ii )
  774. {
  775. CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pList[ii]);
  776. if ( pStrider && !pStrider->CarriedByDropship() ) // ShouldStickToEntity() doesn't work because the strider NPC isn't what we glue to
  777. {
  778. // get distance squared
  779. VectorSubtract( pStrider->GetAdjustedOrigin(), GetAbsOrigin(), toTarget );
  780. //NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + toTarget, 128, 0, 128, false, 0.1 );
  781. float dSq = toTarget.LengthSqr();
  782. if (dSq < nearestDistSq)
  783. {
  784. bestFit = ii; nearestDistSq = dSq;
  785. pBestStrider = pStrider;
  786. }
  787. }
  788. }
  789. if (bestFit >= 0) // we found something and should attract towards it. (hysterisis later?)
  790. {
  791. if ( striderbuster_debugseek.GetBool() )
  792. {
  793. NDebugOverlay::Circle( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, 255, true, .1 );
  794. NDebugOverlay::Cross3D( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, true, .1 );
  795. }
  796. // force magnitude.
  797. float magnitude = GetMass() * striderbuster_magnetic_force_strider.GetFloat();
  798. int falloff = striderbuster_falloff_power.GetInt();
  799. switch (falloff)
  800. {
  801. case 1:
  802. VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / nearestDistSq) ); // dividing through by distance squared normalizes toTarget and gives a linear falloff
  803. break;
  804. case 2:
  805. VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * sqrtf(nearestDistSq))) ); // dividing through by distance cubed normalizes toTarget and gives a quadratic falloff
  806. break;
  807. case 3:
  808. VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * nearestDistSq)) ); // dividing through by distance fourth normalizes toTarget and gives a cubic falloff
  809. break;
  810. case 4:
  811. {
  812. Vector toTarget;
  813. pBestStrider->GetAttachment( "buster_target", toTarget );
  814. if ( striderbuster_debugseek.GetBool() )
  815. {
  816. NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 );
  817. NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 );
  818. }
  819. toTarget -= GetAbsOrigin();
  820. toTarget.NormalizeInPlace();
  821. VPhysicsGetObject()->ApplyForceCenter( toTarget * magnitude );
  822. }
  823. break;
  824. default: // arbitrary powers
  825. VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude * powf(nearestDistSq,(falloff+1.0f)/2)) ); // square root for distance instead of squared, add one to normalize toTarget
  826. break;
  827. }
  828. }
  829. SetNextThink(gpGlobals->curtime + 0.01f);
  830. }
  831. }
  832. //-----------------------------------------------------------------------------
  833. //-----------------------------------------------------------------------------
  834. void CWeaponStriderBuster::BusterDetachThink()
  835. {
  836. SetNextThink( gpGlobals->curtime + 0.1f );
  837. trace_t tr;
  838. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1200), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  839. if( fabs(tr.startpos.z - tr.endpos.z) < 240.0f )
  840. {
  841. SetThink(NULL);
  842. EmitSound( "Weapon_StriderBuster.Dud_Detonate" );
  843. DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() );
  844. SetHealth( 0 );
  845. CTakeDamageInfo info;
  846. info.SetDamage( 1.0f );
  847. info.SetAttacker( this );
  848. info.SetInflictor( this );
  849. Shatter(this);
  850. }
  851. }
  852. //-----------------------------------------------------------------------------
  853. //-----------------------------------------------------------------------------
  854. void CWeaponStriderBuster::BusterPingThink()
  855. {
  856. EmitSound( "Weapon_StriderBuster.Ping" );
  857. SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext );
  858. }
  859. //-----------------------------------------------------------------------------
  860. //-----------------------------------------------------------------------------
  861. void CWeaponStriderBuster::OnAddToCargoHold()
  862. {
  863. if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) )
  864. {
  865. WeaponManager_RemoveManaged( this );
  866. }
  867. }
  868. //-----------------------------------------------------------------------------
  869. //-----------------------------------------------------------------------------
  870. void CWeaponStriderBuster::OnFlechetteAttach( Vector &vecFlechetteVelocity )
  871. {
  872. if ( m_bLaunched )
  873. {
  874. Vector vecForce = vecFlechetteVelocity;
  875. VectorNormalize( vecForce );
  876. vecForce *= 1000;
  877. vecForce.z = -5000;
  878. VPhysicsGetObject()->ApplyForceCenter( vecForce );
  879. }
  880. if ( !GetParent() || !GetParent()->ClassMatches( g_iszVehicle ) )
  881. {
  882. if ( !m_bNoseDiving )
  883. {
  884. //m_hGlowTrail->StopParticleSystem();
  885. StopParticleEffects( this );
  886. if( m_iBusterFlags & STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER )
  887. {
  888. DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this );
  889. }
  890. else
  891. {
  892. DispatchParticleEffect( "striderbuster_flechette_attached", PATTACH_ABSORIGIN_FOLLOW, this );
  893. }
  894. }
  895. m_bNoseDiving = true;
  896. }
  897. m_nAttachedFlechettes++;
  898. }
  899. //-----------------------------------------------------------------------------
  900. //-----------------------------------------------------------------------------
  901. bool StriderBuster_IsAttachedStriderBuster( CBaseEntity *pEntity, CBaseEntity *pAttachedTo )
  902. {
  903. Assert(dynamic_cast<CWeaponStriderBuster *>(pEntity));
  904. if ( !pAttachedTo )
  905. return static_cast<CWeaponStriderBuster *>(pEntity)->m_hConstrainedEntity != NULL;
  906. else
  907. return static_cast<CWeaponStriderBuster *>(pEntity)->m_hConstrainedEntity == pAttachedTo;
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Called when the striderbuster is placed in the jalopy's cargo container.
  911. //-----------------------------------------------------------------------------
  912. void StriderBuster_OnAddToCargoHold( CBaseEntity *pEntity )
  913. {
  914. CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity );
  915. if ( pBuster )
  916. {
  917. pBuster->OnAddToCargoHold();
  918. }
  919. }
  920. //-----------------------------------------------------------------------------
  921. //-----------------------------------------------------------------------------
  922. bool StriderBuster_OnFlechetteAttach( CBaseEntity *pEntity, Vector &vecFlechetteVelocity )
  923. {
  924. CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity );
  925. if ( pBuster )
  926. {
  927. pBuster->OnFlechetteAttach( vecFlechetteVelocity );
  928. return true;
  929. }
  930. return false;
  931. }
  932. //-----------------------------------------------------------------------------
  933. //-----------------------------------------------------------------------------
  934. int StriderBuster_NumFlechettesAttached( CBaseEntity *pEntity )
  935. {
  936. CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity );
  937. if ( pBuster )
  938. {
  939. return pBuster->NumFlechettesAttached();
  940. }
  941. return 0;
  942. }
  943. //-----------------------------------------------------------------------------
  944. //-----------------------------------------------------------------------------
  945. float StriderBuster_GetPickupTime( CBaseEntity *pEntity )
  946. {
  947. CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity );
  948. if ( pBuster )
  949. {
  950. return pBuster->GetPickupTime();
  951. }
  952. return 0;
  953. }
  954. //-----------------------------------------------------------------------------
  955. //-----------------------------------------------------------------------------
  956. bool StriderBuster_WasKnockedOffStrider( CBaseEntity *pEntity )
  957. {
  958. CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity );
  959. if ( pBuster )
  960. {
  961. return ((pBuster->GetStriderBusterFlags() & STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER) != 0);
  962. }
  963. return false;
  964. }