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.

729 lines
21 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: TF Base Rockets.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_weaponbase_rocket.h"
  8. // Server specific.
  9. #ifdef GAME_DLL
  10. #include "soundent.h"
  11. #include "te_effect_dispatch.h"
  12. #include "tf_fx.h"
  13. #include "iscorer.h"
  14. #include "tf_gamerules.h"
  15. #include "func_nogrenades.h"
  16. #include "tf_obj_sentrygun.h"
  17. extern void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID );
  18. extern void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID );
  19. #endif
  20. #ifdef CLIENT_DLL
  21. #include "props_shared.h"
  22. #endif
  23. //w_rocket_airstrike\w_rocket_airstrike.mdl
  24. #define MINI_ROCKETS_MODEL "models/weapons/w_models/w_rocket_airstrike/w_rocket_airstrike.mdl"
  25. //=============================================================================
  26. //
  27. // TF Base Rocket tables.
  28. //
  29. IMPLEMENT_NETWORKCLASS_ALIASED( TFBaseRocket, DT_TFBaseRocket )
  30. BEGIN_NETWORK_TABLE( CTFBaseRocket, DT_TFBaseRocket )
  31. // Client specific.
  32. #ifdef CLIENT_DLL
  33. RecvPropVector( RECVINFO( m_vInitialVelocity ) ),
  34. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  35. RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ),
  36. RecvPropInt( RECVINFO( m_iDeflected ) ),
  37. RecvPropEHandle( RECVINFO( m_hLauncher ) ),
  38. // Server specific.
  39. #else
  40. SendPropVector( SENDINFO( m_vInitialVelocity ), 12 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ),
  41. SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
  42. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  43. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD_MP_INTEGRAL|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  44. SendPropQAngles (SENDINFO(m_angRotation), 6, SPROP_CHANGES_OFTEN, SendProxy_Angles ),
  45. SendPropInt( SENDINFO( m_iDeflected ), 4, SPROP_UNSIGNED ),
  46. SendPropEHandle( SENDINFO( m_hLauncher ) ),
  47. #endif
  48. END_NETWORK_TABLE()
  49. // Server specific.
  50. #ifdef GAME_DLL
  51. BEGIN_DATADESC( CTFBaseRocket )
  52. END_DATADESC()
  53. #endif
  54. #ifdef _DEBUG
  55. ConVar tf_rocket_show_radius( "tf_rocket_show_radius", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Render rocket radius." );
  56. #endif
  57. //=============================================================================
  58. //
  59. // Shared (client/server) functions.
  60. //
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Constructor.
  63. //-----------------------------------------------------------------------------
  64. CTFBaseRocket::CTFBaseRocket()
  65. {
  66. m_vInitialVelocity.Init();
  67. m_iDeflected = 0;
  68. // Client specific.
  69. #ifdef CLIENT_DLL
  70. m_flSpawnTime = 0.0f;
  71. m_iCachedDeflect = false;
  72. // Server specific.
  73. #else
  74. m_flDamage = 0.0f;
  75. m_flDestroyableTime = 0.0f;
  76. m_bStunOnImpact = false;
  77. m_flDamageForceScale = 1.0f;
  78. #endif
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose: Destructor.
  82. //-----------------------------------------------------------------------------
  83. CTFBaseRocket::~CTFBaseRocket()
  84. {
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. void CTFBaseRocket::Precache( void )
  90. {
  91. BaseClass::Precache();
  92. PrecacheParticleSystem( "Explosion_ShockWave_01" );
  93. PrecacheModel( MINI_ROCKETS_MODEL );
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. //-----------------------------------------------------------------------------
  98. void CTFBaseRocket::Spawn( void )
  99. {
  100. BaseClass::Spawn();
  101. // Precache.
  102. Precache();
  103. UseClientSideAnimation();
  104. if ( GetLauncher() )
  105. {
  106. int iMiniRocket = 0;
  107. CALL_ATTRIB_HOOK_INT_ON_OTHER( GetLauncher(), iMiniRocket, mini_rockets );
  108. if ( iMiniRocket )
  109. {
  110. SetModel( MINI_ROCKETS_MODEL );
  111. }
  112. }
  113. // Client specific.
  114. #ifdef CLIENT_DLL
  115. m_flSpawnTime = gpGlobals->curtime;
  116. // Server specific.
  117. #else
  118. //Derived classes must have set model.
  119. Assert( GetModel() );
  120. SetSolid( SOLID_BBOX );
  121. SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM );
  122. AddEFlags( EFL_NO_WATER_VELOCITY_CHANGE );
  123. AddEffects( EF_NOSHADOW );
  124. SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS );
  125. UTIL_SetSize( this, -Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
  126. ResetSequence( LookupSequence("idle") );
  127. // Setup attributes.
  128. m_takedamage = DAMAGE_NO;
  129. SetGravity( 0.0f );
  130. // Setup the touch and think functions.
  131. SetTouch( &CTFBaseRocket::RocketTouch );
  132. SetNextThink( gpGlobals->curtime );
  133. AddFlag( FL_GRENADE );
  134. m_flDestroyableTime = gpGlobals->curtime + TF_ROCKET_DESTROYABLE_TIMER;
  135. m_bCritical = false;
  136. #endif
  137. }
  138. //=============================================================================
  139. //
  140. // Client specific functions.
  141. //
  142. #ifdef CLIENT_DLL
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. //-----------------------------------------------------------------------------
  146. void CTFBaseRocket::PostDataUpdate( DataUpdateType_t type )
  147. {
  148. // Pass through to the base class.
  149. BaseClass::PostDataUpdate( type );
  150. if ( type == DATA_UPDATE_CREATED )
  151. {
  152. // Now stick our initial velocity and angles into the interpolation history.
  153. CInterpolatedVar<Vector> &interpolator = GetOriginInterpolator();
  154. interpolator.ClearHistory();
  155. CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator();
  156. rotInterpolator.ClearHistory();
  157. float flChangeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
  158. // Add a sample 1 second back.
  159. Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
  160. interpolator.AddToHead( flChangeTime - 1.0f, &vCurOrigin, false );
  161. QAngle vCurAngles = GetLocalAngles();
  162. rotInterpolator.AddToHead( flChangeTime - 1.0f, &vCurAngles, false );
  163. // Add the current sample.
  164. vCurOrigin = GetLocalOrigin();
  165. interpolator.AddToHead( flChangeTime, &vCurOrigin, false );
  166. rotInterpolator.AddToHead( flChangeTime - 1.0, &vCurAngles, false );
  167. }
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose:
  171. //-----------------------------------------------------------------------------
  172. void CTFBaseRocket::OnDataChanged(DataUpdateType_t updateType)
  173. {
  174. BaseClass::OnDataChanged(updateType);
  175. if ( updateType == DATA_UPDATE_CREATED || m_iCachedDeflect != GetDeflected() )
  176. {
  177. CreateTrails();
  178. }
  179. m_iCachedDeflect = GetDeflected();
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. int CTFBaseRocket::DrawModel( int flags )
  185. {
  186. // During the first 0.2 seconds of our life, don't draw ourselves.
  187. if ( gpGlobals->curtime - m_flSpawnTime < 0.2f )
  188. return 0;
  189. return BaseClass::DrawModel( flags );
  190. }
  191. //=============================================================================
  192. //
  193. // Server specific functions.
  194. //
  195. #else
  196. //-----------------------------------------------------------------------------
  197. // Purpose:
  198. //-----------------------------------------------------------------------------
  199. CTFBaseRocket *CTFBaseRocket::Create( CBaseEntity *pLauncher, const char *pszClassname, const Vector &vecOrigin,
  200. const QAngle &vecAngles, CBaseEntity *pOwner )
  201. {
  202. CTFBaseRocket *pRocket = static_cast<CTFBaseRocket*>( CBaseEntity::Create( pszClassname, vecOrigin, vecAngles, pOwner ) );
  203. if ( !pRocket )
  204. return NULL;
  205. pRocket->SetLauncher( pLauncher );
  206. // Initialize the owner.
  207. pRocket->SetOwnerEntity( pOwner );
  208. // Spawn.
  209. pRocket->Spawn();
  210. // Setup the initial velocity.
  211. Vector vecForward, vecRight, vecUp;
  212. AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
  213. float flLaunchSpeed = 1100.0f;
  214. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, flLaunchSpeed, mult_projectile_speed );
  215. // Hack: This attribute represents a bucket of attributes - one of which is projectile speed.
  216. // If the concept works we'll make the "bucket" system directly modify the attributes instead.
  217. if ( pOwner )
  218. {
  219. // if the owner is a Sentry, Check its owner
  220. int iRocketSpecialist = 0;
  221. CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pOwner );
  222. if ( pSentry )
  223. {
  224. CALL_ATTRIB_HOOK_INT_ON_OTHER( pSentry->GetOwner(), iRocketSpecialist, rocket_specialist );
  225. }
  226. else
  227. {
  228. CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRocketSpecialist, rocket_specialist );
  229. }
  230. if ( iRocketSpecialist )
  231. {
  232. flLaunchSpeed *= RemapValClamped( iRocketSpecialist, 1.f, 4.f, 1.15f, 1.6f );
  233. flLaunchSpeed = Min( flLaunchSpeed, 3000.f );
  234. }
  235. }
  236. CTFPlayer *pTFOwner = ToTFPlayer( pRocket->GetOwnerPlayer() );
  237. if ( pTFOwner )
  238. {
  239. pRocket->SetTruceValidForEnt( pTFOwner->IsTruceValidForEnt() );
  240. if ( pTFOwner->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  241. {
  242. flLaunchSpeed = 3000.f;
  243. }
  244. }
  245. Vector vecVelocity = vecForward * flLaunchSpeed;
  246. pRocket->SetAbsVelocity( vecVelocity );
  247. pRocket->SetupInitialTransmittedGrenadeVelocity( vecVelocity );
  248. // Setup the initial angles.
  249. QAngle angles;
  250. VectorAngles( vecVelocity, angles );
  251. pRocket->SetAbsAngles( angles );
  252. // Set team.
  253. pRocket->ChangeTeam( pOwner->GetTeamNumber() );
  254. return pRocket;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. void CTFBaseRocket::RocketTouch( CBaseEntity *pOther )
  260. {
  261. // Verify a correct "other."
  262. Assert( pOther );
  263. bool bShield = pOther->IsCombatItem() && !InSameTeam( pOther );
  264. if ( pOther->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) && !bShield )
  265. return;
  266. // Handle hitting skybox (disappear).
  267. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  268. if( pTrace->surface.flags & SURF_SKY )
  269. {
  270. UTIL_Remove( this );
  271. return;
  272. }
  273. trace_t trace;
  274. memcpy( &trace, pTrace, sizeof( trace_t ) );
  275. Explode( &trace, pOther );
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. unsigned int CTFBaseRocket::PhysicsSolidMaskForEntity( void ) const
  281. {
  282. int teamContents = 0;
  283. if ( !CanCollideWithTeammates() )
  284. {
  285. // Only collide with the other team
  286. teamContents = ( GetTeamNumber() == TF_TEAM_RED ) ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
  287. }
  288. else
  289. {
  290. // Collide with both teams
  291. teamContents = CONTENTS_REDTEAM | CONTENTS_BLUETEAM;
  292. }
  293. return BaseClass::PhysicsSolidMaskForEntity() | teamContents;
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose:
  297. //-----------------------------------------------------------------------------
  298. bool CTFBaseRocket::ShouldNotDetonate( void )
  299. {
  300. return InNoGrenadeZone( this );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose:
  304. //-----------------------------------------------------------------------------
  305. void CTFBaseRocket::Destroy( bool bBlinkOut, bool bBreakRocket )
  306. {
  307. if ( bBreakRocket )
  308. {
  309. CPVSFilter filter( GetAbsOrigin() );
  310. UserMessageBegin( filter, "BreakModelRocketDud" );
  311. WRITE_SHORT( GetModelIndex() );
  312. WRITE_VEC3COORD( GetAbsOrigin() );
  313. WRITE_ANGLES( GetAbsAngles() );
  314. MessageEnd();
  315. }
  316. // Kill it
  317. SetThink( &BaseClass::SUB_Remove );
  318. SetNextThink( gpGlobals->curtime );
  319. SetTouch( NULL );
  320. AddEffects( EF_NODRAW );
  321. if ( bBlinkOut )
  322. {
  323. // Sprite flash
  324. CSprite *pGlowSprite = CSprite::SpriteCreate( NOGRENADE_SPRITE, GetAbsOrigin(), false );
  325. if ( pGlowSprite )
  326. {
  327. pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxFadeFast );
  328. pGlowSprite->SetThink( &CSprite::SUB_Remove );
  329. pGlowSprite->SetNextThink( gpGlobals->curtime + 1.0 );
  330. }
  331. }
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. //-----------------------------------------------------------------------------
  336. void CTFBaseRocket::Explode( trace_t *pTrace, CBaseEntity *pOther )
  337. {
  338. if ( ShouldNotDetonate() )
  339. {
  340. Destroy( true );
  341. return;
  342. }
  343. // Save this entity as enemy, they will take 100% damage.
  344. m_hEnemy = pOther;
  345. // Invisible.
  346. SetModelName( NULL_STRING );
  347. AddSolidFlags( FSOLID_NOT_SOLID );
  348. m_takedamage = DAMAGE_NO;
  349. // Pull out a bit.
  350. if ( pTrace->fraction != 1.0 )
  351. {
  352. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  353. }
  354. // Play explosion sound and effect.
  355. Vector vecOrigin = GetAbsOrigin();
  356. CPVSFilter filter( vecOrigin );
  357. // Halloween Spell Effect Check
  358. int iHalloweenSpell = 0;
  359. int iCustomParticleIndex = INVALID_STRING_INDEX;
  360. item_definition_index_t ownerWeaponDefIndex = INVALID_ITEM_DEF_INDEX;
  361. // if the owner is a Sentry, Check its owner
  362. CBaseEntity *pPlayerOwner = GetOwnerPlayer();
  363. if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  364. {
  365. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayerOwner, iHalloweenSpell, halloween_pumpkin_explosions );
  366. if ( iHalloweenSpell > 0 )
  367. {
  368. iCustomParticleIndex = GetParticleSystemIndex( "halloween_explosion" );
  369. }
  370. }
  371. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( GetOriginalLauncher() );
  372. if ( pWeapon )
  373. {
  374. ownerWeaponDefIndex = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex();
  375. }
  376. int iLargeExplosion = 0;
  377. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayerOwner, iLargeExplosion, use_large_smoke_explosion );
  378. if ( iLargeExplosion > 0 )
  379. {
  380. DispatchParticleEffect( "explosionTrail_seeds_mvm", GetAbsOrigin(), GetAbsAngles() );
  381. DispatchParticleEffect( "fluidSmokeExpl_ring_mvm", GetAbsOrigin(), GetAbsAngles() );
  382. }
  383. TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), pOther->entindex(), ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex );
  384. CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 );
  385. // Damage.
  386. CBaseEntity *pAttacker = GetOwnerEntity();
  387. IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker );
  388. if ( pScorerInterface )
  389. {
  390. pAttacker = pScorerInterface->GetScorer();
  391. }
  392. else if ( pAttacker && pAttacker->GetOwnerEntity() )
  393. {
  394. pAttacker = pAttacker->GetOwnerEntity();
  395. }
  396. float flRadius = GetRadius();
  397. if ( pAttacker ) // No attacker, deal no damage. Otherwise we could potentially kill teammates.
  398. {
  399. CTFPlayer *pTarget = ToTFPlayer( GetEnemy() );
  400. if ( pTarget )
  401. {
  402. // Rocket Specialist
  403. CheckForStunOnImpact( pTarget );
  404. if ( pTarget->GetTeamNumber() != pAttacker->GetTeamNumber() )
  405. {
  406. IGameEvent *event = gameeventmanager->CreateEvent( "projectile_direct_hit" );
  407. if ( event )
  408. {
  409. event->SetInt( "attacker", pAttacker->entindex() );
  410. event->SetInt( "victim", pTarget->entindex() );
  411. event->SetInt( "weapon_def_index", ownerWeaponDefIndex );
  412. gameeventmanager->FireEvent( event, true );
  413. }
  414. }
  415. }
  416. CTakeDamageInfo info( this, pAttacker, GetOriginalLauncher(), vec3_origin, vecOrigin, GetDamage(), GetDamageType(), GetDamageCustom() );
  417. CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, TF_ROCKET_RADIUS_FOR_RJS, GetDamageForceScale() );
  418. TFGameRules()->RadiusDamage( radiusinfo );
  419. }
  420. #if defined( _DEBUG ) && defined( STAGING_ONLY )
  421. // Debug!
  422. if ( tf_rocket_show_radius.GetBool() )
  423. {
  424. DrawRadius( flRadius );
  425. }
  426. #endif
  427. // Don't decal players with scorch.
  428. if ( !pOther->IsPlayer() )
  429. {
  430. UTIL_DecalTrace( pTrace, "Scorch" );
  431. }
  432. // Remove the rocket.
  433. UTIL_Remove( this );
  434. return;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void CTFBaseRocket::CheckForStunOnImpact( CTFPlayer* pTarget )
  440. {
  441. if ( !m_bStunOnImpact )
  442. return;
  443. CTFPlayer *pAttacker = ToTFPlayer( GetOwnerPlayer() );
  444. if ( !pAttacker )
  445. return;
  446. int iRocketSpecialist = GetStunLevel();
  447. if ( !iRocketSpecialist )
  448. return;
  449. // Stun
  450. float flStunAmount = pTarget->IsMiniBoss() ? 0.85f : 1.f;
  451. float flStunTime = RemapValClamped( iRocketSpecialist, 1.f, 4.f, 0.5f, 0.75f );
  452. pTarget->SetAbsVelocity( vec3_origin );
  453. pTarget->m_Shared.StunPlayer( flStunTime, flStunAmount, TF_STUN_MOVEMENT | TF_STUN_NO_EFFECTS, pAttacker );
  454. if ( TFGameRules()->IsMannVsMachineMode() && pTarget->IsBot() && ( pAttacker->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) )
  455. {
  456. pAttacker->AwardAchievement( ACHIEVEMENT_TF_MVM_ROCKET_SPECIALIST_STUN_GRIND );
  457. }
  458. // Effect
  459. CPVSFilter filter( GetAbsOrigin() );
  460. TE_TFParticleEffect( filter, 0.0, "mvm_soldier_shockwave", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose:
  464. //-----------------------------------------------------------------------------
  465. int CTFBaseRocket::GetStunLevel( void )
  466. {
  467. CTFPlayer *pAttacker = ToTFPlayer( GetOwnerPlayer() );
  468. if ( !pAttacker )
  469. return 0;
  470. int iRocketSpecialist = 0;
  471. CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRocketSpecialist, rocket_specialist );
  472. return iRocketSpecialist;
  473. }
  474. #ifdef STAGING_ONLY
  475. //-----------------------------------------------------------------------------
  476. // Purpose:
  477. //-----------------------------------------------------------------------------
  478. void CTFBaseRocket::DrawRadius( float flRadius )
  479. {
  480. Vector pos = GetAbsOrigin();
  481. int r = 255;
  482. int g = 0, b = 0;
  483. float flLifetime = 10.0f;
  484. bool bDepthTest = true;
  485. Vector edge, lastEdge;
  486. NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, !bDepthTest, flLifetime );
  487. lastEdge = Vector( flRadius + pos.x, pos.y, pos.z );
  488. float angle;
  489. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  490. {
  491. edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x;
  492. edge.y = pos.y;
  493. edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z;
  494. NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime );
  495. lastEdge = edge;
  496. }
  497. lastEdge = Vector( pos.x, flRadius + pos.y, pos.z );
  498. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  499. {
  500. edge.x = pos.x;
  501. edge.y = flRadius * cos( DEG2RAD( angle ) ) + pos.y;
  502. edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z;
  503. NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime );
  504. lastEdge = edge;
  505. }
  506. lastEdge = Vector( pos.x, flRadius + pos.y, pos.z );
  507. for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
  508. {
  509. edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x;
  510. edge.y = flRadius * sin( DEG2RAD( angle ) ) + pos.y;
  511. edge.z = pos.z;
  512. NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime );
  513. lastEdge = edge;
  514. }
  515. }
  516. #endif
  517. //-----------------------------------------------------------------------------
  518. // Purpose:
  519. //-----------------------------------------------------------------------------
  520. float CTFBaseRocket::GetRadius()
  521. {
  522. float flRadius = TF_ROCKET_RADIUS;
  523. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flRadius, mult_explosion_radius );
  524. CBaseEntity *pAttacker = GetOwnerPlayer();
  525. if ( pAttacker )
  526. {
  527. int iRocketSpecialist = 0;
  528. CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRocketSpecialist, rocket_specialist );
  529. if ( iRocketSpecialist )
  530. {
  531. bool bDirectHit = ( GetEnemy() && GetEnemy()->GetTeamNumber() != pAttacker->GetTeamNumber() &&
  532. ( GetEnemy()->IsPlayer() || GetEnemy()->MyCombatCharacterPointer() ) );
  533. // If we have the Rocket Specialist attribute and hit an enemy combatant directly...
  534. if ( bDirectHit )
  535. {
  536. // Increased blast radius
  537. flRadius *= RemapValClamped( iRocketSpecialist, 1.f, 4.f, 1.15f, 1.6f );
  538. m_bStunOnImpact = true;
  539. }
  540. }
  541. CTFPlayer *pTFPlayer = ToTFPlayer( pAttacker );
  542. // Airstrike gets a small blast radius penalty while Rjing
  543. if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
  544. {
  545. // Using this attr to key in the AirStrike
  546. float flRocketJumpAttackBonus = 1.0f;
  547. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAttacker, flRocketJumpAttackBonus, rocketjump_attackrate_bonus );
  548. if ( flRocketJumpAttackBonus != 1.0f )
  549. {
  550. flRadius *= 0.80;
  551. }
  552. }
  553. }
  554. return flRadius;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Checks if the owner is a sentry gun, if so returns the sentry guns owner
  558. //-----------------------------------------------------------------------------
  559. CBaseEntity *CTFBaseRocket::GetOwnerPlayer( void ) const
  560. {
  561. // if the owner is a Sentry, Check its owner
  562. CBaseEntity *pOwner = GetOwnerEntity();
  563. CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pOwner );
  564. if ( pSentry )
  565. {
  566. return pSentry->GetOwner();
  567. }
  568. return pOwner;
  569. }
  570. #endif
  571. #if defined( CLIENT_DLL )
  572. //-----------------------------------------------------------------------------
  573. // Receive the BreakModelRocketDud user message
  574. //-----------------------------------------------------------------------------
  575. void __MsgFunc_BreakModelRocketDud( bf_read &msg )
  576. {
  577. int nModelIndex = (int)msg.ReadShort();
  578. CUtlVector<breakmodel_t> aGibs;
  579. BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
  580. if ( !aGibs.Count() )
  581. return;
  582. // Get the origin & angles
  583. Vector vecOrigin, vecForward;
  584. QAngle vecAngles;
  585. msg.ReadBitVec3Coord( vecOrigin );
  586. msg.ReadBitAngles( vecAngles );
  587. AngleVectors( vecAngles, &vecForward );
  588. Vector vecBreakVelocity = Vector(0,0,300) + vecForward*-400;
  589. AngularImpulse angularImpulse( 0, RandomFloat( -500, -3000 ), 0 );
  590. breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse );
  591. breakParams.impactEnergyScale = 1.0f;
  592. CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true );
  593. }
  594. #endif