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.

624 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // TF Nail
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_projectile_flare.h"
  8. #include "soundent.h"
  9. #include "tf_fx.h"
  10. #include "tf_player.h"
  11. #include "tf_weapon_flaregun.h"
  12. #include "tf_gamerules.h"
  13. //=============================================================================
  14. //
  15. // TF Flare Projectile functions (Server specific).
  16. //
  17. #define FLARE_MODEL "models/weapons/w_models/w_flaregun_shell.mdl"
  18. #define FLARE_GRAVITY 0.3f
  19. #define FLARE_SPEED 2000.0f
  20. #define FLARE_THINK_CONTEXT "CTFProjectile_FlareThink"
  21. LINK_ENTITY_TO_CLASS( tf_projectile_flare, CTFProjectile_Flare );
  22. PRECACHE_WEAPON_REGISTER( tf_projectile_flare );
  23. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Flare, DT_TFProjectile_Flare )
  24. BEGIN_NETWORK_TABLE( CTFProjectile_Flare, DT_TFProjectile_Flare )
  25. SendPropBool( SENDINFO( m_bCritical ) ),
  26. END_NETWORK_TABLE()
  27. BEGIN_DATADESC( CTFProjectile_Flare )
  28. DEFINE_THINKFUNC( ImpactThink ),
  29. END_DATADESC()
  30. //-----------------------------------------------------------------------------
  31. // Purpose:
  32. //-----------------------------------------------------------------------------
  33. CTFProjectile_Flare::CTFProjectile_Flare()
  34. {
  35. m_bIsFromTaunt = false;
  36. m_bCritical = false;
  37. m_bImpact = false;
  38. m_flImpactTime = 0.0f;
  39. m_flNextSeekUpdate = 0.0f;
  40. }
  41. //-----------------------------------------------------------------------------
  42. // Purpose:
  43. //-----------------------------------------------------------------------------
  44. CTFProjectile_Flare::~CTFProjectile_Flare()
  45. {
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Purpose:
  49. //-----------------------------------------------------------------------------
  50. CTFProjectile_Flare *CTFProjectile_Flare::Create( CBaseEntity *pLauncher, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, CBaseEntity *pScorer )
  51. {
  52. CTFProjectile_Flare *pFlare = static_cast<CTFProjectile_Flare*>( CBaseEntity::CreateNoSpawn( "tf_projectile_flare", vecOrigin, vecAngles, pOwner ) );
  53. if ( !pFlare )
  54. return NULL;
  55. pFlare->SetLauncher( pLauncher );
  56. // Initialize the owner.
  57. pFlare->SetOwnerEntity( pOwner );
  58. // Set team.
  59. pFlare->ChangeTeam( pOwner->GetTeamNumber() );
  60. // Save the scoring player.
  61. pFlare->SetScorer( pScorer );
  62. // Spawn.
  63. DispatchSpawn( pFlare );
  64. // Setup the initial velocity.
  65. Vector vecForward, vecRight, vecUp;
  66. AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
  67. float flLaunchSpeed = pFlare->GetProjectileSpeed();
  68. Vector vecVelocity = vecForward * flLaunchSpeed;
  69. pFlare->SetAbsVelocity( vecVelocity );
  70. pFlare->SetupInitialTransmittedGrenadeVelocity( vecVelocity );
  71. float flGravity = FLARE_GRAVITY;
  72. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, flGravity, mult_projectile_speed );
  73. pFlare->SetGravity( flGravity );
  74. // Setup the initial angles.
  75. QAngle angles;
  76. VectorAngles( vecVelocity, angles );
  77. pFlare->SetAbsAngles( angles );
  78. return pFlare;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose:
  82. //-----------------------------------------------------------------------------
  83. void CTFProjectile_Flare::Spawn()
  84. {
  85. SetModel( FLARE_MODEL );
  86. BaseClass::Spawn();
  87. float flHeatSeekPower = GetHeatSeekPower();
  88. if ( flHeatSeekPower > 0.0f )
  89. {
  90. SetMoveType( MOVETYPE_CUSTOM, MOVECOLLIDE_DEFAULT );
  91. SetGravity( FLARE_GRAVITY );
  92. }
  93. else
  94. {
  95. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
  96. SetGravity( FLARE_GRAVITY );
  97. }
  98. // Set team.
  99. m_nSkin = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0;
  100. m_flCreationTime = gpGlobals->curtime;
  101. CTFPlayer *pScorer = ToTFPlayer( GetScorer() );
  102. if ( pScorer && pScorer->IsTaunting() && m_flCreationTime >= pScorer->GetTauntAttackTime() )
  103. {
  104. m_bIsFromTaunt = true;
  105. }
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //-----------------------------------------------------------------------------
  110. void CTFProjectile_Flare::Precache()
  111. {
  112. PrecacheModel( FLARE_MODEL );
  113. PrecacheParticleSystem( "flaregun_trail_red" );
  114. PrecacheParticleSystem( "flaregun_trail_crit_red" );
  115. PrecacheParticleSystem( "flaregun_trail_blue" );
  116. PrecacheParticleSystem( "flaregun_trail_crit_blue" );
  117. PrecacheParticleSystem( "drg_manmelter_projectile" );
  118. PrecacheParticleSystem( "scorchshot_trail_red" );
  119. PrecacheParticleSystem( "scorchshot_trail_crit_red" );
  120. PrecacheParticleSystem( "scorchshot_trail_blue" );
  121. PrecacheParticleSystem( "scorchshot_trail_crit_blue" );
  122. PrecacheParticleSystem( "Explosions_MA_FlyingEmbers" );
  123. PrecacheParticleSystem( "pyrovision_flaregun_trail_blue" );
  124. PrecacheParticleSystem( "pyrovision_flaregun_trail_red" );
  125. PrecacheParticleSystem( "pyrovision_flaregun_trail_crit_blue" );
  126. PrecacheParticleSystem( "pyrovision_flaregun_trail_crit_red" );
  127. PrecacheParticleSystem( "pyrovision_scorchshot_trail_blue" );
  128. PrecacheParticleSystem( "pyrovision_scorchshot_trail_red" );
  129. PrecacheParticleSystem( "pyrovision_scorchshot_trail_crit_blue" );
  130. PrecacheParticleSystem( "pyrovision_scorchshot_trail_crit_red" );
  131. BaseClass::Precache();
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose:
  135. //-----------------------------------------------------------------------------
  136. void CTFProjectile_Flare::SetScorer( CBaseEntity *pScorer )
  137. {
  138. m_Scorer = pScorer;
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. //-----------------------------------------------------------------------------
  143. CBasePlayer *CTFProjectile_Flare::GetScorer( void )
  144. {
  145. return dynamic_cast<CBasePlayer *>( m_Scorer.Get() );
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose:
  149. //-----------------------------------------------------------------------------
  150. int CTFProjectile_Flare::GetDamageType()
  151. {
  152. int iDmgType = BaseClass::GetDamageType();
  153. if ( m_bCritical )
  154. {
  155. iDmgType |= DMG_CRITICAL;
  156. }
  157. return iDmgType;
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther )
  163. {
  164. Vector vecOrigin = GetAbsOrigin();
  165. CBaseEntity *pAttacker = GetOwnerEntity();
  166. IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker );
  167. if ( pScorerInterface )
  168. {
  169. pAttacker = pScorerInterface->GetScorer();
  170. }
  171. CTFPlayer *pTFVictim = ToTFPlayer( pOther );
  172. CTFFlareGun *pFlareGun = dynamic_cast< CTFFlareGun* >( GetLauncher() );
  173. if ( pFlareGun )
  174. {
  175. if ( pFlareGun->GetFlareGunType() == FLAREGUN_SCORCHSHOT )
  176. {
  177. // When the scorch shot hits a player...
  178. if ( pTFVictim )
  179. {
  180. // Now it only collides with the world
  181. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  182. Vector vVelocity = GetAbsVelocity();
  183. // Check if burning before damage
  184. bool bIsBurningVictim = pTFVictim->m_Shared.InCond( TF_COND_BURNING );
  185. int iDamageType = GetDamageType();
  186. // Prevent the normal push force cause we are going to add it
  187. iDamageType |= DMG_PREVENT_PHYSICS_FORCE;
  188. // Damage the player to push them back
  189. CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), iDamageType, m_bIsFromTaunt ? TF_DMG_CUSTOM_FLARE_PELLET : 0 );
  190. pTFVictim->TakeDamage( info );
  191. bool bIsEnemy = pAttacker && pTFVictim->GetTeamNumber() != pAttacker->GetTeamNumber();
  192. // Quick Fix Uber and teammates are immune to the force
  193. if ( !pTFVictim->m_Shared.InCond( TF_COND_MEGAHEAL ) && bIsEnemy )
  194. {
  195. Vector vecToTarget;
  196. vecToTarget = vVelocity;
  197. VectorNormalize( vecToTarget );
  198. vecToTarget.z = 1.0;
  199. // apply airblast - Apply stun if they are effectively grounded so we can knock them up
  200. if ( !pTFVictim->m_Shared.InCond( TF_COND_KNOCKED_INTO_AIR ) )
  201. {
  202. pTFVictim->m_Shared.StunPlayer( 0.5, 1.f, TF_STUN_MOVEMENT, ToTFPlayer( pAttacker ) );
  203. }
  204. float flForce = bIsBurningVictim ? 400.0f : 100.0f;
  205. pTFVictim->ApplyAirBlastImpulse( vecToTarget * flForce );
  206. }
  207. // It loses almost all of its speed and pops into the air
  208. vVelocity.x *= 0.07f;
  209. vVelocity.y *= 0.07f;
  210. vVelocity.z = 100.0f;
  211. SetAbsVelocity( vVelocity + RandomVector( -2.0f, 2.0f ) );
  212. // Point the new direction and randomly flip
  213. QAngle angForward;
  214. VectorAngles( vVelocity, angForward );
  215. SetAbsAngles( angForward );
  216. QAngle angRotation = RandomAngle( 180.0f, 720.0f );
  217. angRotation.x *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 );
  218. angRotation.y *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 );
  219. angRotation.z *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 );
  220. SetLocalAngularVelocity( angRotation );
  221. CPVSFilter filter( vecOrigin );
  222. EmitSound( filter, entindex(), "Rubber.BulletImpact" );
  223. // Save this entity as enemy, they will take 100% damage.
  224. if ( m_hEnemy.Get() == NULL )
  225. {
  226. m_hEnemy = pTFVictim;
  227. }
  228. return;
  229. }
  230. }
  231. }
  232. // If we've already got an impact time, don't impact again.
  233. if ( m_flImpactTime > 0.0 )
  234. return;
  235. // Save this entity as enemy, they will take 100% damage.
  236. if ( m_hEnemy.Get() == NULL )
  237. {
  238. m_hEnemy = pOther;
  239. }
  240. if ( !pTFVictim )
  241. {
  242. m_bImpact = true;
  243. }
  244. // Invisible.
  245. AddSolidFlags( FSOLID_NOT_SOLID );
  246. m_takedamage = DAMAGE_NO;
  247. bool bDetonate = false;
  248. bool bNoRandomCrit = false;
  249. if ( pFlareGun )
  250. {
  251. switch ( pFlareGun->GetFlareGunType() )
  252. {
  253. case FLAREGUN_DETONATE:
  254. bDetonate = true;
  255. break;
  256. case FLAREGUN_GRORDBORT:
  257. bNoRandomCrit = true;
  258. break;
  259. case FLAREGUN_SCORCHSHOT:
  260. bDetonate = true;
  261. bNoRandomCrit = true;
  262. break;
  263. }
  264. }
  265. // Flares that hit a burning player crit, unless it's a detonate flare - they mini-crit
  266. if ( pTFVictim && pTFVictim->m_Shared.InCond( TF_COND_BURNING ) && !bDetonate && !bNoRandomCrit )
  267. {
  268. m_bCritical = true;
  269. }
  270. CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), GetDamageType(), TF_DMG_CUSTOM_BURNING_FLARE );
  271. pOther->TakeDamage( info );
  272. // Remove the flare.
  273. if ( m_bImpact )
  274. {
  275. SetMoveType( MOVETYPE_FLY );
  276. SetAbsVelocity( vec3_origin );
  277. m_vecImpactNormal = pTrace->plane.normal;
  278. m_flImpactTime = gpGlobals->curtime + 0.1f;
  279. // Stick into and object and fizzle a little while.
  280. SetContextThink( &CTFProjectile_Flare::ImpactThink, gpGlobals->curtime, FLARE_THINK_CONTEXT );
  281. // Only do this for the Detonator
  282. if ( bDetonate )
  283. {
  284. // Scorch Shot can still light others in this case
  285. Detonate( pFlareGun->GetFlareGunType() != FLAREGUN_SCORCHSHOT );
  286. }
  287. }
  288. else
  289. {
  290. // Impact player sound.
  291. CPVSFilter filter( vecOrigin );
  292. EmitSound( filter, pOther->entindex(), "TFPlayer.FlareImpact" );
  293. SendDeathNotice();
  294. UTIL_Remove( this );
  295. }
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose: Custom explode for air burst flare
  299. //-----------------------------------------------------------------------------
  300. void CTFProjectile_Flare::Explode_Air( trace_t *pTrace, int bitsDamageType, bool bSelfOnly )
  301. {
  302. // Invisible.
  303. AddSolidFlags( FSOLID_NOT_SOLID );
  304. m_takedamage = DAMAGE_NO;
  305. // Play explosion sound and effect.
  306. Vector vecOrigin = GetAbsOrigin();
  307. CPVSFilter filter( vecOrigin );
  308. CBaseEntity *pAttacker = GetOwnerEntity();
  309. int nDefID = -1;
  310. WeaponSound_t nSound = SPECIAL1;
  311. if ( pAttacker )
  312. {
  313. CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun*>( ToTFPlayer( pAttacker )->GetActiveWeapon() );
  314. if ( pFlareGun )
  315. {
  316. CEconItemView *pItem = pFlareGun->GetAttributeContainer()->GetItem();
  317. nDefID = pItem->GetItemDefIndex();
  318. }
  319. // Damage.
  320. IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker );
  321. if ( pScorerInterface )
  322. {
  323. pAttacker = pScorerInterface->GetScorer();
  324. }
  325. float flRadius = bSelfOnly ? 0.f : GetRadius();
  326. if ( bSelfOnly )
  327. {
  328. bitsDamageType |= DMG_BLAST;
  329. nSound = SPECIAL2;
  330. }
  331. #if defined( _DEBUG ) && defined( STAGING_ONLY )
  332. // Debug!
  333. ConVarRef tf_rocket_show_radius( "tf_rocket_show_radius" );
  334. if ( tf_rocket_show_radius.GetBool() )
  335. {
  336. DrawRadius( flRadius );
  337. }
  338. #endif
  339. CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), bitsDamageType | DMG_HALF_FALLOFF, TF_DMG_CUSTOM_FLARE_EXPLOSION );
  340. CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, TF_FLARE_RADIUS_FOR_FJS );
  341. TFGameRules()->RadiusDamage( radiusinfo );
  342. }
  343. const char *pszParticle = bSelfOnly ? "Explosions_MA_FlyingEmbers" : "ExplosionCore_MidAir_Flare";
  344. TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), entindex(), nDefID, nSound );
  345. TE_TFParticleEffect( filter, 0.0f, pszParticle, vecOrigin, pTrace->plane.normal, vec3_angle );
  346. CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 );
  347. SendDeathNotice();
  348. UTIL_Remove( this );
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: Alt-fire air burst flare
  352. //-----------------------------------------------------------------------------
  353. void CTFProjectile_Flare::Detonate( bool bSelfOnly )
  354. {
  355. trace_t tr;
  356. Vector vecSpot;
  357. SetThink( NULL );
  358. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );
  359. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr);
  360. Explode_Air( &tr, GetDamageType(), bSelfOnly );
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose:
  364. //-----------------------------------------------------------------------------
  365. float CTFProjectile_Flare::GetRadius( void )
  366. {
  367. float flRadius = TF_FLARE_DET_RADIUS;
  368. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flRadius, mult_explosion_radius );
  369. return flRadius;
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Let the flaregun know we're gone
  373. //-----------------------------------------------------------------------------
  374. void CTFProjectile_Flare::SendDeathNotice( void )
  375. {
  376. CBaseEntity *pAttacker = GetOwnerEntity();
  377. if ( !pAttacker )
  378. return;
  379. CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun*>( ToTFPlayer( pAttacker )->GetActiveWeapon() );
  380. if ( pFlareGun && pFlareGun->GetFlareGunType() == FLAREGUN_DETONATE )
  381. {
  382. pFlareGun->DeathNotice( this );
  383. }
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose:
  387. //-----------------------------------------------------------------------------
  388. void CTFProjectile_Flare::ImpactThink( void )
  389. {
  390. if ( gpGlobals->curtime > m_flImpactTime )
  391. {
  392. // If we hit anything other than the player create an impact - effect, sound, etc.
  393. if ( m_hEnemy.Get() )
  394. {
  395. Vector vecOrigin = GetAbsOrigin();
  396. CPVSFilter filter( vecOrigin );
  397. TE_TFExplosion( filter, 0.0f, vecOrigin, m_vecImpactNormal, GetWeaponID(), m_hEnemy.Get()->entindex() );
  398. }
  399. SendDeathNotice();
  400. UTIL_Remove( this );
  401. SetContextThink( NULL, 0, FLARE_THINK_CONTEXT );
  402. }
  403. else
  404. {
  405. SetContextThink( &CTFProjectile_Flare::ImpactThink, gpGlobals->curtime + 0.1f, FLARE_THINK_CONTEXT );
  406. }
  407. }
  408. void CTFProjectile_Flare::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
  409. {
  410. if ( m_flNextSeekUpdate < gpGlobals->curtime )
  411. {
  412. CTFPlayer *pBestTarget = NULL;
  413. const float flMaxSeekDistanceSqr = ( 1024.0f * 1024.0f );
  414. float flBestDistance = flMaxSeekDistanceSqr;
  415. // Loop through players and attempt to find a seek target
  416. int i;
  417. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  418. {
  419. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  420. if ( !pPlayer )
  421. continue;
  422. bool bBurning = pPlayer->m_Shared.InCond( TF_COND_BURNING );
  423. if ( !bBurning ||
  424. pPlayer->InSameTeam( this ) ||
  425. ( pPlayer->m_Shared.GetDisguiseTeam() == GetTeamNumber() && !bBurning ) ||
  426. ( pPlayer->m_Shared.IsStealthed() && !bBurning ) ||
  427. pPlayer->GetTeamNumber() == TEAM_SPECTATOR ||
  428. !pPlayer->IsAlive() )
  429. {
  430. continue;
  431. }
  432. Vector vToTarget = pPlayer->WorldSpaceCenter() - GetAbsOrigin();
  433. VectorNormalize( vToTarget );
  434. Vector vForward = *pNewVelocity;
  435. VectorNormalize( vForward );
  436. if ( vToTarget.Dot( vForward ) < -0.25f )
  437. continue;
  438. float flDistanceSqr = pPlayer->WorldSpaceCenter().DistToSqr( GetAbsOrigin() );
  439. if ( flBestDistance > flDistanceSqr )
  440. {
  441. trace_t tr;
  442. UTIL_TraceLine( pPlayer->WorldSpaceCenter(), GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr );
  443. if ( !tr.DidHit() || tr.m_pEnt == this )
  444. {
  445. flBestDistance = flDistanceSqr;
  446. pBestTarget = pPlayer;
  447. }
  448. }
  449. }
  450. float flHeatSeekPower = GetHeatSeekPower();
  451. QAngle angToTarget = *pNewAngles;
  452. if ( pBestTarget )
  453. {
  454. Vector vToTarget = pBestTarget->WorldSpaceCenter() - GetAbsOrigin();
  455. VectorAngles( vToTarget, angToTarget );
  456. }
  457. const float flUpdatesPerSecond = 4.0f;
  458. if ( angToTarget != *pNewAngles )
  459. {
  460. pNewAngVelocity->x = Approach( UTIL_AngleDiff( angToTarget.x, pNewAngles->x ) * flUpdatesPerSecond, pNewAngVelocity->x, flHeatSeekPower );
  461. pNewAngVelocity->y = Approach( UTIL_AngleDiff( angToTarget.y, pNewAngles->y ) * flUpdatesPerSecond, pNewAngVelocity->y, flHeatSeekPower );
  462. pNewAngVelocity->z = Approach( UTIL_AngleDiff( angToTarget.z, pNewAngles->z ) * flUpdatesPerSecond, pNewAngVelocity->z, flHeatSeekPower );
  463. const float flMaxAngularVelocity = 360.0f;
  464. pNewAngVelocity->x = clamp( pNewAngVelocity->x, -flMaxAngularVelocity, flMaxAngularVelocity );
  465. pNewAngVelocity->y = clamp( pNewAngVelocity->y, -flMaxAngularVelocity, flMaxAngularVelocity );
  466. pNewAngVelocity->z = clamp( pNewAngVelocity->z, -flMaxAngularVelocity, flMaxAngularVelocity );
  467. }
  468. m_flNextSeekUpdate = gpGlobals->curtime + ( 1.0f / flUpdatesPerSecond );
  469. }
  470. *pNewAngles += *pNewAngVelocity * gpGlobals->frametime;
  471. Vector vForward;
  472. AngleVectors( *pNewAngles, &vForward );
  473. *pNewVelocity = vForward * GetProjectileSpeed();
  474. *pNewPosition += *pNewVelocity * gpGlobals->frametime;
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose: Flare was deflected.
  478. //-----------------------------------------------------------------------------
  479. void CTFProjectile_Flare::Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir )
  480. {
  481. CTFPlayer *pTFDeflector = ToTFPlayer( pDeflectedBy );
  482. if ( !pTFDeflector )
  483. return;
  484. ChangeTeam( pTFDeflector->GetTeamNumber() );
  485. SetLauncher( pTFDeflector->GetActiveWeapon() );
  486. CTFPlayer* pOldOwner = ToTFPlayer( GetOwnerEntity() );
  487. SetOwnerEntity( pTFDeflector );
  488. if ( pOldOwner )
  489. {
  490. pOldOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:1,victim:1" );
  491. }
  492. if ( pTFDeflector->m_Shared.IsCritBoosted() )
  493. {
  494. SetCritical( true );
  495. }
  496. CTFWeaponBase::SendObjectDeflectedEvent( pTFDeflector, pOldOwner, GetWeaponID(), this );
  497. IncrementDeflected();
  498. SetScorer( pTFDeflector );
  499. }
  500. float CTFProjectile_Flare::GetProjectileSpeed( void ) const
  501. {
  502. float flLaunchSpeed = FLARE_SPEED;
  503. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flLaunchSpeed, mult_projectile_speed );
  504. return flLaunchSpeed;
  505. }
  506. float CTFProjectile_Flare::GetHeatSeekPower( void ) const
  507. {
  508. float flHeatSeekPower = 0.0;
  509. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flHeatSeekPower, mod_projectile_heat_seek_power );
  510. return flHeatSeekPower;
  511. }