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.

1234 lines
38 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_throwable.h"
  8. #include "tf_gamerules.h"
  9. #include "in_buttons.h"
  10. #include "basetypes.h"
  11. #include "tf_weaponbase_gun.h"
  12. #include "effect_dispatch_data.h"
  13. // Client specific.
  14. #ifdef CLIENT_DLL
  15. #include "c_tf_player.h"
  16. // Server specific.
  17. #else
  18. #include "tf_player.h"
  19. #include "tf_fx.h"
  20. #include "te_effect_dispatch.h"
  21. #include "bone_setup.h"
  22. #include "tf_target_dummy.h"
  23. #endif
  24. // Base
  25. // Launcher
  26. IMPLEMENT_NETWORKCLASS_ALIASED( TFThrowable, DT_TFWeaponThrowable )
  27. BEGIN_NETWORK_TABLE( CTFThrowable, DT_TFWeaponThrowable )
  28. #ifdef CLIENT_DLL
  29. RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ),
  30. #else
  31. SendPropFloat( SENDINFO( m_flChargeBeginTime ) ),
  32. #endif
  33. END_NETWORK_TABLE()
  34. BEGIN_PREDICTION_DATA( CTFThrowable )
  35. END_PREDICTION_DATA()
  36. //LINK_ENTITY_TO_CLASS( tf_weapon_throwable, CTFThrowable );
  37. //PRECACHE_WEAPON_REGISTER( tf_weapon_throwable );
  38. #ifdef STAGING_ONLY
  39. CREATE_SIMPLE_WEAPON_TABLE( TFThrowablePrimary, tf_weapon_throwable_primary )
  40. CREATE_SIMPLE_WEAPON_TABLE( TFThrowableSecondary, tf_weapon_throwable_secondary )
  41. CREATE_SIMPLE_WEAPON_TABLE( TFThrowableMelee, tf_weapon_throwable_melee )
  42. CREATE_SIMPLE_WEAPON_TABLE( TFThrowableUtility, tf_weapon_throwable_utility )
  43. #endif
  44. // Projectile
  45. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Throwable, DT_TFProjectile_Throwable )
  46. BEGIN_NETWORK_TABLE( CTFProjectile_Throwable, DT_TFProjectile_Throwable )
  47. END_NETWORK_TABLE()
  48. LINK_ENTITY_TO_CLASS( tf_projectile_throwable, CTFProjectile_Throwable );
  49. PRECACHE_WEAPON_REGISTER( tf_projectile_throwable );
  50. // Projectile Repel
  51. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableRepel, DT_TFProjectile_ThrowableRepel )
  52. BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableRepel, DT_TFProjectile_ThrowableRepel )
  53. END_NETWORK_TABLE()
  54. LINK_ENTITY_TO_CLASS( tf_projectile_throwable_repel, CTFProjectile_ThrowableRepel );
  55. PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_repel );
  56. // Projectile Brick
  57. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableBrick, DT_TFProjectile_ThrowableBrick )
  58. BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableBrick, DT_TFProjectile_ThrowableBrick )
  59. END_NETWORK_TABLE()
  60. LINK_ENTITY_TO_CLASS( tf_projectile_throwable_brick, CTFProjectile_ThrowableBrick );
  61. PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_brick );
  62. // Projectile Bread Monster
  63. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableBreadMonster, DT_TFProjectile_ThrowableBreadMonster )
  64. BEGIN_NETWORK_TABLE( CTFProjectile_ThrowableBreadMonster, DT_TFProjectile_ThrowableBreadMonster )
  65. END_NETWORK_TABLE()
  66. LINK_ENTITY_TO_CLASS( tf_projectile_throwable_breadmonster, CTFProjectile_ThrowableBreadMonster );
  67. PRECACHE_WEAPON_REGISTER( tf_projectile_throwable_breadmonster );
  68. #ifdef STAGING_ONLY
  69. // Projectile Target Dummy
  70. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowableTargetDummy, DT_TFProjectile_ThrowableTargetDummy )
  71. BEGIN_NETWORK_TABLE( CTFProjectile_ConcGrenade, DT_TFProjectile_ThrowableTargetDummy )
  72. END_NETWORK_TABLE()
  73. LINK_ENTITY_TO_CLASS( tf_projectile_target_dummy, CTFProjectile_ThrowableTargetDummy );
  74. PRECACHE_WEAPON_REGISTER( tf_projectile_target_dummy );
  75. // Projectile Concussion Grenade
  76. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ConcGrenade, DT_TFProjectile_ConcGrenade )
  77. BEGIN_NETWORK_TABLE( CTFProjectile_ConcGrenade, DT_TFProjectile_ConcGrenade )
  78. END_NETWORK_TABLE()
  79. LINK_ENTITY_TO_CLASS( tf_projectile_grenade_concussion, CTFProjectile_ConcGrenade );
  80. PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_concussion );
  81. // Projectile Teleport Grenade
  82. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_TeleportGrenade, DT_TFProjectile_TeleportGrenade )
  83. BEGIN_NETWORK_TABLE( CTFProjectile_TeleportGrenade, DT_TFProjectile_TeleportGrenade )
  84. END_NETWORK_TABLE()
  85. LINK_ENTITY_TO_CLASS( tf_projectile_grenade_teleport, CTFProjectile_TeleportGrenade );
  86. PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_teleport );
  87. // Projectile Chain Grenade
  88. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_GravityGrenade, DT_TFProjectile_GravityGrenade )
  89. BEGIN_NETWORK_TABLE( CTFProjectile_GravityGrenade, DT_TFProjectile_GravityGrenade )
  90. END_NETWORK_TABLE()
  91. LINK_ENTITY_TO_CLASS( tf_projectile_grenade_gravity, CTFProjectile_GravityGrenade );
  92. PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_gravity );
  93. // Projectile Chain Grenade
  94. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_ThrowingKnife, DT_TFProjectile_ThrowingKnife )
  95. BEGIN_NETWORK_TABLE( CTFProjectile_ThrowingKnife, DT_TFProjectile_ThrowingKnife )
  96. END_NETWORK_TABLE()
  97. LINK_ENTITY_TO_CLASS( tf_projectile_throwing_knife, CTFProjectile_ThrowingKnife );
  98. PRECACHE_WEAPON_REGISTER( tf_projectile_throwing_knife );
  99. // Projectile Smoke Grenade
  100. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SmokeGrenade, DT_TFProjectile_SmokeGrenade )
  101. BEGIN_NETWORK_TABLE( CTFProjectile_SmokeGrenade, DT_TFProjectile_SmokeGrenade )
  102. END_NETWORK_TABLE()
  103. LINK_ENTITY_TO_CLASS( tf_projectile_grenade_smoke, CTFProjectile_SmokeGrenade );
  104. PRECACHE_WEAPON_REGISTER( tf_projectile_grenade_smoke );
  105. #endif // STAGING_ONLY
  106. #define TF_GRENADE_TIMER "Weapon_Grenade.Timer"
  107. #define TF_GRENADE_CHARGE "Weapon_LooseCannon.Charge"
  108. //****************************************************************************
  109. // Throwable Weapon
  110. //****************************************************************************
  111. CTFThrowable::CTFThrowable( void )
  112. {
  113. m_flChargeBeginTime = -1.0f;
  114. }
  115. //-----------------------------------------------------------------------------
  116. void CTFThrowable::Precache()
  117. {
  118. BaseClass::Precache();
  119. PrecacheModel( g_pszArrowModels[MODEL_BREAD_MONSTER] );
  120. PrecacheModel( g_pszArrowModels[MODEL_THROWING_KNIFE] );
  121. #ifdef STAGING_ONLY
  122. PrecacheScriptSound( "Weapon_Grenade_Concussion.Explode" );
  123. PrecacheScriptSound( "Weapon_Grenade_Teleport.Explode" );
  124. PrecacheScriptSound( TF_GRENADE_TIMER );
  125. #endif // STAGING_ONLY
  126. PrecacheScriptSound( TF_GRENADE_CHARGE );
  127. PrecacheScriptSound( "Weapon_bm_throwable.throw" );
  128. PrecacheScriptSound( "Weapon_bm_throwable.smash" );
  129. PrecacheParticleSystem( "grenade_smoke_cycle" );
  130. PrecacheParticleSystem( "blood_bread_biting" );
  131. }
  132. //-----------------------------------------------------------------------------
  133. float CTFThrowable::InternalGetEffectBarRechargeTime( void )
  134. {
  135. float flRechargeTime = 0;
  136. CALL_ATTRIB_HOOK_FLOAT( flRechargeTime, throwable_recharge_time );
  137. if ( flRechargeTime )
  138. return flRechargeTime;
  139. return 10.0f; // default
  140. }
  141. //-----------------------------------------------------------------------------
  142. float CTFThrowable::GetDetonationTime()
  143. {
  144. float flDetonationTime = 0;
  145. CALL_ATTRIB_HOOK_FLOAT( flDetonationTime, throwable_detonation_time );
  146. if ( flDetonationTime )
  147. return flDetonationTime;
  148. return 5.0f; // default
  149. }
  150. //-----------------------------------------------------------------------------
  151. void CTFThrowable::PrimaryAttack( void )
  152. {
  153. if ( !CanCharge() )
  154. {
  155. // Fire
  156. BaseClass::PrimaryAttack();
  157. return;
  158. }
  159. if ( m_flChargeBeginTime > 0 )
  160. return;
  161. // Do all the Checks and start a charged (primed) attack
  162. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  163. if ( !pPlayer )
  164. return;
  165. // Check for ammunition.
  166. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) < 1 )
  167. return;
  168. // Are we capable of firing again?
  169. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  170. return;
  171. if ( pPlayer->GetWaterLevel() == WL_Eyes )
  172. return;
  173. if ( !CanAttack() )
  174. return;
  175. if ( m_flChargeBeginTime <= 0 )
  176. {
  177. // Set the weapon mode.
  178. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  179. SendWeaponAnim( ACT_VM_PULLBACK ); // TODO : Anim!
  180. #ifdef GAME_DLL
  181. // save that we had the attack button down
  182. m_flChargeBeginTime = gpGlobals->curtime;
  183. #endif // GAME_LL
  184. #ifdef CLIENT_DLL
  185. if ( pPlayer == C_BasePlayer::GetLocalPlayer() )
  186. {
  187. int iCanBeCharged = 0;
  188. CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable );
  189. if ( iCanBeCharged )
  190. {
  191. EmitSound( TF_GRENADE_CHARGE );
  192. }
  193. else
  194. {
  195. EmitSound( TF_GRENADE_TIMER );
  196. }
  197. }
  198. #endif // CLIENT_DLL
  199. }
  200. }
  201. //-----------------------------------------------------------------------------
  202. void CTFThrowable::ItemPostFrame( void )
  203. {
  204. // Get the player owning the weapon.
  205. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  206. if ( !pPlayer )
  207. return;
  208. if ( m_flChargeBeginTime > 0.f && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  209. {
  210. bool bFiredWeapon = false;
  211. // If we're not holding down the attack button, launch our grenade
  212. if ( !( pPlayer->m_nButtons & IN_ATTACK ) )
  213. {
  214. FireProjectile( pPlayer );
  215. bFiredWeapon = true;
  216. }
  217. // Misfire
  218. else if ( m_flChargeBeginTime + GetDetonationTime() < gpGlobals->curtime )
  219. {
  220. CTFProjectile_Throwable * pThrowable = dynamic_cast<CTFProjectile_Throwable*>( FireProjectile( pPlayer ) );
  221. if ( pThrowable )
  222. {
  223. #ifdef GAME_DLL
  224. pThrowable->Misfire();
  225. #endif // GAME_DLL
  226. }
  227. bFiredWeapon = true;
  228. }
  229. if ( bFiredWeapon )
  230. {
  231. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  232. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  233. #ifdef GAME_DLL
  234. m_flChargeBeginTime = -1.0f; // reset
  235. #endif // GAME_DLL
  236. // Set next attack times.
  237. float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
  238. m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
  239. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  240. #ifdef CLIENT_DLL
  241. int iCanBeCharged = 0;
  242. CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable );
  243. if ( iCanBeCharged )
  244. {
  245. StopSound( TF_GRENADE_CHARGE );
  246. }
  247. #endif // CLIENT_DLL
  248. }
  249. }
  250. BaseClass::ItemPostFrame();
  251. }
  252. // ITFChargeUpWeapon
  253. //-----------------------------------------------------------------------------
  254. // Primable is for timed explosions
  255. // Charagable is for things like distance or power increases
  256. // Can't really have both but can have neither
  257. bool CTFThrowable::CanCharge()
  258. {
  259. int iCanBePrimed = 0;
  260. CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable );
  261. int iCanBeCharged = 0;
  262. CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable );
  263. return iCanBeCharged || iCanBePrimed ;
  264. }
  265. //-----------------------------------------------------------------------------
  266. float CTFThrowable::GetChargeBeginTime( void )
  267. {
  268. float flDetonateTimeLength = GetDetonationTime();
  269. // float flModDetonateTimeLength = 0;
  270. int iCanBePrimed = 0;
  271. CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable );
  272. // Use reverse logic for primable grenades (Counts down to boom)
  273. // Full charge since we haven't fired
  274. if ( iCanBePrimed )
  275. {
  276. if ( m_flChargeBeginTime < 0 )
  277. {
  278. return gpGlobals->curtime - flDetonateTimeLength;
  279. }
  280. return gpGlobals->curtime - Clamp( m_flChargeBeginTime + flDetonateTimeLength - gpGlobals->curtime, 0.f, flDetonateTimeLength );
  281. }
  282. return m_flChargeBeginTime;
  283. }
  284. //-----------------------------------------------------------------------------
  285. float CTFThrowable::GetChargeMaxTime( void )
  286. {
  287. return GetDetonationTime();
  288. }
  289. //-----------------------------------------------------------------------------
  290. CBaseEntity *CTFThrowable::FireJar( CTFPlayer *pPlayer )
  291. {
  292. #ifdef GAME_DLL
  293. return FireProjectileInternal();
  294. #endif
  295. return NULL;
  296. }
  297. #ifdef GAME_DLL
  298. //-----------------------------------------------------------------------------
  299. void CTFThrowable::TossJarThink( void )
  300. {
  301. FireProjectileInternal();
  302. }
  303. //-----------------------------------------------------------------------------
  304. CTFProjectile_Throwable *CTFThrowable::FireProjectileInternal( void )
  305. {
  306. CTFPlayer *pPlayer = GetTFPlayerOwner();
  307. if ( !pPlayer )
  308. return NULL;
  309. CAttribute_String attrProjectileEntityName;
  310. GetProjectileEntityName( &attrProjectileEntityName );
  311. if ( !attrProjectileEntityName.has_value() )
  312. return NULL;
  313. Vector vecForward, vecRight, vecUp;
  314. AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp );
  315. float fRight = 8.f;
  316. if ( IsViewModelFlipped() )
  317. {
  318. fRight *= -1;
  319. }
  320. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  321. // Make spell toss position at the hand
  322. vecSrc = vecSrc + ( vecUp * -9.0f ) + ( vecRight * 7.0f ) + ( vecForward * 3.0f );
  323. trace_t trace;
  324. Vector vecEye = pPlayer->EyePosition();
  325. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  326. UTIL_TraceHull( vecEye, vecSrc, -Vector( 8, 8, 8 ), Vector( 8, 8, 8 ), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  327. // If we started in solid, don't let them fire at all
  328. if ( trace.startsolid )
  329. return NULL;
  330. CalcIsAttackCritical();
  331. // Create the Grenade and Intialize it appropriately
  332. CTFProjectile_Throwable *pGrenade = static_cast<CTFProjectile_Throwable*>( CBaseEntity::CreateNoSpawn( attrProjectileEntityName.value().c_str(), trace.endpos, pPlayer->EyeAngles(), pPlayer ) );
  333. if ( pGrenade )
  334. {
  335. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  336. pGrenade->SetPipebombMode();
  337. pGrenade->SetLauncher( this );
  338. pGrenade->SetCritical( IsCurrentAttackACrit() );
  339. DispatchSpawn( pGrenade );
  340. // Calculate a charge percentage
  341. // For now Charge just effects exit velocity
  342. int iCanBeCharged = 0;
  343. float flChargePercent = 0;
  344. float flDetonateTime = GetDetonationTime();
  345. CALL_ATTRIB_HOOK_INT( iCanBeCharged, is_throwable_chargeable );
  346. if ( iCanBeCharged )
  347. {
  348. flChargePercent = RemapVal( gpGlobals->curtime, m_flChargeBeginTime, m_flChargeBeginTime + flDetonateTime, 0.0f, 1.0f );
  349. }
  350. Vector vecVelocity = pGrenade->GetVelocityVector( vecForward, vecRight, vecUp, flChargePercent );
  351. AngularImpulse angVelocity = pGrenade->GetAngularImpulse();
  352. pGrenade->InitGrenade( vecVelocity, angVelocity, pPlayer, GetTFWpnData() );
  353. pGrenade->InitThrowable( flChargePercent );
  354. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  355. if ( flDetonateTime > 0 )
  356. {
  357. // Check if this has been primed
  358. int iCanBePrimed = 0;
  359. CALL_ATTRIB_HOOK_INT( iCanBePrimed, is_throwable_primable );
  360. if ( m_flChargeBeginTime > 0 && iCanBePrimed > 0 )
  361. {
  362. flDetonateTime = ( m_flChargeBeginTime + flDetonateTime - gpGlobals->curtime );
  363. }
  364. pGrenade->SetDetonateTimerLength( flDetonateTime );
  365. }
  366. pGrenade->m_flFullDamage = 0;
  367. if ( pGrenade->GetThrowSoundEffect() )
  368. {
  369. pGrenade->EmitSound( pGrenade->GetThrowSoundEffect() );
  370. }
  371. }
  372. StartEffectBarRegen();
  373. return pGrenade;
  374. }
  375. #endif // GAME_DLL
  376. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  377. // Throwable Projectile
  378. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  379. #ifdef GAME_DLL
  380. CTFProjectile_Throwable::CTFProjectile_Throwable( void )
  381. {
  382. m_flChargePercent = 0;
  383. m_bHit = false;
  384. }
  385. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  386. // Get Initial Velocity
  387. Vector CTFProjectile_Throwable::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, float flCharge )
  388. {
  389. // Scale the projectile speed up to a maximum of 3000?
  390. float flSpeed = RemapVal( flCharge, 0, 1.0f, GetProjectileSpeed(), GetProjectileMaxSpeed() );
  391. return ( ( flSpeed * vecForward ) +
  392. ( ( random->RandomFloat( -10.0f, 10.0f ) + 200.0f ) * vecUp ) +
  393. ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) );
  394. }
  395. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  396. void CTFProjectile_Throwable::OnHit( CBaseEntity *pOther )
  397. {
  398. if ( m_bHit )
  399. return;
  400. if ( ExplodesOnHit() )
  401. {
  402. Explode();
  403. }
  404. m_bHit = true;
  405. }
  406. //-----------------------------------------------------------------------------
  407. void CTFProjectile_Throwable::Explode()
  408. {
  409. trace_t tr;
  410. Vector vecSpot;// trace starts here!
  411. SetThink( NULL );
  412. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );
  413. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr);
  414. Explode( &tr, GetDamageType() );
  415. }
  416. //-----------------------------------------------------------------------------
  417. void CTFProjectile_Throwable::Explode( trace_t *pTrace, int bitsDamageType )
  418. {
  419. if ( GetThrower() )
  420. {
  421. InitialExplodeEffects( NULL, pTrace );
  422. // Particle
  423. const char* pszExplodeEffect = GetExplodeEffectParticle();
  424. if ( pszExplodeEffect && pszExplodeEffect[0] != '\0' )
  425. {
  426. CPVSFilter filter( GetAbsOrigin() );
  427. TE_TFParticleEffect( filter, 0.0, pszExplodeEffect, GetAbsOrigin(), vec3_angle );
  428. }
  429. // Sounds
  430. const char* pszSoundEffect = GetExplodeEffectSound();
  431. if ( pszSoundEffect && pszSoundEffect[0] != '\0' )
  432. {
  433. EmitSound( pszSoundEffect );
  434. }
  435. }
  436. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  437. SetTouch( NULL );
  438. AddEffects( EF_NODRAW );
  439. SetAbsVelocity( vec3_origin );
  440. }
  441. //-----------------------------------------------------------------------------
  442. // THROWABLE REPEL
  443. //-----------------------------------------------------------------------------
  444. void CTFProjectile_ThrowableRepel::OnHit( CBaseEntity *pOther )
  445. {
  446. if ( m_bHit )
  447. return;
  448. CTFPlayer *pPlayer = dynamic_cast< CTFPlayer*>( pOther );
  449. if ( pPlayer && !pPlayer->InSameTeam( GetThrower() ) )
  450. {
  451. CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  452. trace_t trace;
  453. UTIL_TraceLine( GetAbsOrigin(), pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace );
  454. // Apply AirBlast Force
  455. Vector vecToTarget;
  456. vecToTarget = pPlayer->GetAbsOrigin() - this->GetAbsOrigin();
  457. vecToTarget.z = 0;
  458. VectorNormalize( vecToTarget );
  459. // Quick Fix Uber is immune
  460. if ( pPlayer->m_Shared.InCond( TF_COND_MEGAHEAL ))
  461. return;
  462. float flForce = 300.0f * m_flChargePercent + 350.0f;
  463. pPlayer->ApplyAirBlastImpulse( vecToTarget * flForce + Vector( 0, 0, flForce ) );
  464. pPlayer->ApplyPunchImpulseX( RandomInt( -50, -30 ) );
  465. // Apply Damage to Victim
  466. CTakeDamageInfo info;
  467. info.SetAttacker( GetThrower() );
  468. info.SetInflictor( this );
  469. info.SetWeapon( GetLauncher() );
  470. info.SetDamage( GetDamage() );
  471. info.SetDamageCustom( GetCustomDamageType() );
  472. info.SetDamagePosition( this->GetAbsOrigin() );
  473. info.SetDamageType( DMG_CLUB | DMG_PREVENT_PHYSICS_FORCE );
  474. //Vector dir;
  475. //AngleVectors( GetAbsAngles(), &dir );
  476. pPlayer->DispatchTraceAttack( info, vecToTarget, &trace );
  477. ApplyMultiDamage();
  478. }
  479. BaseClass::OnHit( pOther );
  480. }
  481. //-----------------------------------------------------------------------------
  482. // THROWABLE BRICK
  483. //-----------------------------------------------------------------------------
  484. void CTFProjectile_ThrowableBrick::OnHit( CBaseEntity *pOther )
  485. {
  486. if ( m_bHit )
  487. return;
  488. CTFPlayer *pPlayer = dynamic_cast< CTFPlayer*>( pOther );
  489. if ( pPlayer && !pPlayer->InSameTeam( GetThrower() ) )
  490. {
  491. CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  492. trace_t trace;
  493. UTIL_TraceLine( GetAbsOrigin(), pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace );
  494. Vector vecToTarget;
  495. vecToTarget = pPlayer->WorldSpaceCenter() - this->WorldSpaceCenter();
  496. VectorNormalize( vecToTarget );
  497. // Apply Damage to Victim
  498. CTakeDamageInfo info;
  499. info.SetAttacker( GetThrower() );
  500. info.SetInflictor( this );
  501. info.SetWeapon( GetLauncher() );
  502. info.SetDamage( GetDamage() );
  503. info.SetDamageCustom( GetCustomDamageType() );
  504. info.SetDamagePosition( GetAbsOrigin() );
  505. info.SetDamageType( DMG_CLUB );
  506. pPlayer->DispatchTraceAttack( info, vecToTarget, &trace );
  507. pPlayer->ApplyPunchImpulseX( RandomInt( 15, 20 ) );
  508. ApplyMultiDamage();
  509. }
  510. BaseClass::OnHit( pOther );
  511. }
  512. //-----------------------------------------------------------------------------
  513. // THROWABLE BREADMONSTER
  514. //-----------------------------------------------------------------------------
  515. void CTFProjectile_ThrowableBreadMonster::OnHit( CBaseEntity *pOther )
  516. {
  517. if ( m_bHit )
  518. return;
  519. CTFPlayer *pVictim = dynamic_cast< CTFPlayer*>( pOther );
  520. CTFPlayer *pOwner = dynamic_cast< CTFPlayer*>( GetThrower() );
  521. if ( pVictim && pOwner && !pVictim->InSameTeam( pOwner ) )
  522. {
  523. m_bHit = true;
  524. CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  525. trace_t trace;
  526. Vector vEndPos = pVictim->WorldSpaceCenter();
  527. vEndPos.z = WorldSpaceCenter().z + 1.0f;
  528. UTIL_TraceLine( WorldSpaceCenter(), vEndPos, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &tracefilter, &trace );
  529. Vector vecToTarget;
  530. vecToTarget = pVictim->WorldSpaceCenter() - this->WorldSpaceCenter();
  531. VectorNormalize( vecToTarget );
  532. // Apply Damage to Victim
  533. CTakeDamageInfo info;
  534. info.SetAttacker( GetThrower() );
  535. info.SetInflictor( this );
  536. info.SetWeapon( GetLauncher() );
  537. info.SetDamage( GetDamage() );
  538. info.SetDamageCustom( GetCustomDamageType() );
  539. info.SetDamagePosition( GetAbsOrigin() );
  540. int iDamageType = DMG_CLUB;
  541. if ( IsCritical() )
  542. {
  543. iDamageType |= DMG_CRITICAL;
  544. }
  545. info.SetDamageType( iDamageType );
  546. pVictim->DispatchTraceAttack( info, vecToTarget, &trace );
  547. pVictim->ApplyPunchImpulseX( RandomInt( 15, 20 ) );
  548. pVictim->m_Shared.MakeBleed( pOwner, dynamic_cast< CTFWeaponBase * >( GetLauncher() ), 5.0f, 1.0f );
  549. ApplyMultiDamage();
  550. // Bread Particle
  551. CPVSFilter filter( vEndPos );
  552. TE_TFParticleEffect( filter, 0.0, "blood_bread_biting", vEndPos, vec3_angle );
  553. // Attach Breadmonster to Victim
  554. CreateStickyAttachmentToTarget( pOwner, pVictim, &trace );
  555. BaseClass::Explode();
  556. return;
  557. }
  558. else // its a dud
  559. {
  560. BaseClass::Explode();
  561. return;
  562. }
  563. BaseClass::OnHit( pOther );
  564. }
  565. //-----------------------------------------------------------------------------
  566. void CTFProjectile_ThrowableBreadMonster::Detonate()
  567. {
  568. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  569. SetTouch( NULL );
  570. AddEffects( EF_NODRAW );
  571. SetAbsVelocity( vec3_origin );
  572. }
  573. //-----------------------------------------------------------------------------
  574. void CTFProjectile_ThrowableBreadMonster::Explode( trace_t *pTrace, int bitsDamageType )
  575. {
  576. if ( !m_bHit )
  577. {
  578. // TODO, Spawn Debris / Flopping BreadInstead
  579. trace_t tr;
  580. Vector velDir = m_vCollisionVelocity;
  581. VectorNormalize( velDir );
  582. Vector vecSpot = GetAbsOrigin() - velDir * 32;
  583. UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &tr );
  584. if ( tr.fraction < 1.0 && tr.surface.flags & SURF_SKY )
  585. {
  586. // We hit the skybox, go away soon.
  587. return;
  588. }
  589. // Create a breadmonster in the world
  590. CEffectData data;
  591. data.m_vOrigin = tr.endpos;
  592. data.m_vNormal = velDir;
  593. data.m_nEntIndex = 0;
  594. data.m_nAttachmentIndex = 0;
  595. data.m_nMaterial = 0;
  596. data.m_fFlags = TF_PROJECTILE_BREAD_MONSTER;
  597. data.m_nColor = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0;
  598. DispatchEffect( "TFBoltImpact", data );
  599. }
  600. BaseClass::Explode( pTrace, bitsDamageType );
  601. }
  602. #endif // GAME_DLL
  603. //
  604. //#ifdef CLIENT_DLL
  605. //
  606. //static CUtlMap< const char*, CUtlString > s_TeamParticleMap;
  607. //static bool s_TeamParticleMapInited = false;
  608. //
  609. ////-----------------------------------------------------------------------------
  610. //const char *CTFProjectile_Throwable::GetTrailParticleName( void )
  611. //{
  612. // // Check for Particles
  613. // int iDynamicParticleEffect = 0;
  614. // CALL_ATTRIB_HOOK_INT_ON_OTHER( GetLauncher(), iDynamicParticleEffect, set_attached_particle );
  615. // if ( iDynamicParticleEffect > 0 )
  616. // {
  617. // // Init Map Once
  618. // if ( !s_TeamParticleMapInited )
  619. // {
  620. // SetDefLessFunc( s_TeamParticleMap );
  621. // s_TeamParticleMapInited = true;
  622. // }
  623. //
  624. // attachedparticlesystem_t *pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iDynamicParticleEffect );
  625. // if ( pParticleSystem )
  626. // {
  627. // // TF Team Color Particles
  628. // const char * pName = pParticleSystem->pszSystemName;
  629. // if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pName, "_teamcolor_red" ))
  630. // {
  631. // int index = s_TeamParticleMap.Find( pName );
  632. // if ( !s_TeamParticleMap.IsValidIndex( index ) )
  633. // {
  634. // char pBlue[256];
  635. // V_StrSubst( pName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 );
  636. // CUtlString pBlueString( pBlue );
  637. // index = s_TeamParticleMap.Insert( pName, pBlueString );
  638. // }
  639. // return s_TeamParticleMap[index].String();
  640. // }
  641. // else if ( GetTeamNumber() == TF_TEAM_RED && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_blue" ))
  642. // {
  643. // // Guard against accidentally giving out the blue team color (support tool)
  644. // int index = s_TeamParticleMap.Find( pName );
  645. // if ( !s_TeamParticleMap.IsValidIndex( index ) )
  646. // {
  647. // char pRed[256];
  648. // V_StrSubst( pName, "_teamcolor_blue", "_teamcolor_red", pRed, 256 );
  649. // CUtlString pRedString( pRed );
  650. // index = s_TeamParticleMap.Insert( pName, pRedString );
  651. // }
  652. // return s_TeamParticleMap[index].String();
  653. // }
  654. //
  655. // return pName;
  656. // }
  657. // }
  658. //
  659. // if ( GetTeamNumber() == TF_TEAM_BLUE )
  660. // {
  661. // return "trail_basic_blue";
  662. // }
  663. // else
  664. // {
  665. // return "trail_basic_red";
  666. // }
  667. //}
  668. //
  669. //#endif // CLIENT_DLL
  670. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  671. #ifdef STAGING_ONLY
  672. #ifdef GAME_DLL
  673. void CTFProjectile_ThrowableTargetDummy::Explode()
  674. {
  675. CTFPlayer *pPlayer = ToTFPlayer( GetThrower() );
  676. if ( !pPlayer )
  677. return;
  678. CTFTargetDummy::Create( GetAbsOrigin(), GetAbsAngles(), pPlayer );
  679. BaseClass::Explode();
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose:
  683. //-----------------------------------------------------------------------------
  684. void CTFProjectile_ConcGrenade::Detonate( )
  685. {
  686. Explode();
  687. }
  688. //-----------------------------------------------------------------------------
  689. void CTFProjectile_ConcGrenade::Misfire( )
  690. {
  691. CTFPlayer *pPlayer = ToTFPlayer( GetThrower() );
  692. if ( pPlayer )
  693. {
  694. SetAbsOrigin( pPlayer->GetAbsOrigin() );
  695. }
  696. Explode();
  697. }
  698. //-----------------------------------------------------------------------------
  699. void CTFProjectile_ConcGrenade::Explode( )
  700. {
  701. // Apply pushback
  702. const float flMaxForce = 900.f;
  703. const float flMaxSelfForce = 800.f;
  704. const int nMaxEnts = MAX_PLAYERS;
  705. CBaseEntity *pObjects[ nMaxEnts ];
  706. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, GetAbsOrigin(), GetDamageRadius(), FL_CLIENT );
  707. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  708. for ( int i = 0; i < nCount; i++ )
  709. {
  710. if ( !pObjects[i] )
  711. continue;
  712. if ( !pObjects[i]->IsAlive() )
  713. continue;
  714. // Only affect the thrower from same team
  715. if ( InSameTeam( pObjects[i] ) && pObjects[i] != pThrower )
  716. continue;
  717. if ( !FVisible( pObjects[i], MASK_OPAQUE ) )
  718. continue;
  719. if ( !pObjects[i]->IsPlayer() )
  720. continue;
  721. CTFPlayer *pTFPlayer = static_cast< CTFPlayer* >( pObjects[i] );
  722. if ( !pTFPlayer )
  723. continue;
  724. // Conc does more force the further away you are from the blast radius
  725. Vector vecPushDir = pTFPlayer->GetAbsOrigin() - GetAbsOrigin();
  726. float flForce = RemapVal( vecPushDir.Length(), 0, GetDamageRadius(), 0, flMaxForce );
  727. if ( flForce < 250.0f && pObjects[i] == pThrower ) // Hold case
  728. {
  729. AngularImpulse ang;
  730. pTFPlayer->GetVelocity( &vecPushDir, &ang );
  731. flForce = flMaxSelfForce;
  732. }
  733. VectorNormalize( vecPushDir );
  734. vecPushDir.z *= 1.5f;
  735. pTFPlayer->ApplyAirBlastImpulse( vecPushDir * flForce );
  736. pTFPlayer->ApplyPunchImpulseX( RandomInt( -50, -30 ) );
  737. // if ( pObjects[i] == pThrower )
  738. // {
  739. // // Apply 'Bonk' lines to make target more visible for 2 seconds
  740. // pThrower->m_Shared.AddCond( TF_COND_SELF_CONC, 2 );
  741. // }
  742. }
  743. BaseClass::Explode();
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose:
  747. //-----------------------------------------------------------------------------
  748. void CTFProjectile_TeleportGrenade::Spawn( void )
  749. {
  750. BaseClass::Spawn();
  751. SetContextThink( &CTFProjectile_TeleportGrenade::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" );
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Purpose:
  755. //-----------------------------------------------------------------------------
  756. void CTFProjectile_TeleportGrenade::RecordPosThink( void )
  757. {
  758. m_vecTrailingPos.AddToTail( GetAbsOrigin() );
  759. // Only retain 5 positions
  760. if ( m_vecTrailingPos.Count() > 5 )
  761. {
  762. m_vecTrailingPos.Remove( 0 );
  763. }
  764. SetContextThink( &CTFProjectile_TeleportGrenade::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" );
  765. }
  766. //-----------------------------------------------------------------------------
  767. // Purpose:
  768. //-----------------------------------------------------------------------------
  769. void CTFProjectile_TeleportGrenade::Explode( trace_t *pTrace, int bitsDamageType )
  770. {
  771. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  772. if ( !pThrower || !pThrower->IsAlive() )
  773. return;
  774. trace_t traceHull;
  775. CTraceFilterIgnoreTeammates traceFilter( this, COLLISION_GROUP_PLAYER_MOVEMENT, GetTeamNumber() );
  776. unsigned int nMask = pThrower->GetTeamNumber() == TF_TEAM_RED ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
  777. nMask |= MASK_PLAYERSOLID;
  778. // Try a few spots
  779. FOR_EACH_VEC_BACK( m_vecTrailingPos, i )
  780. {
  781. // Try positions starting with the current, and moving back in time a bit
  782. Vector vecStart = m_vecTrailingPos[i];
  783. UTIL_TraceHull( vecStart, vecStart, VEC_HULL_MIN, VEC_HULL_MAX, nMask, &traceFilter, &traceHull );
  784. if ( !traceHull.DidHit() )
  785. {
  786. // Place a teleport effect where they came from
  787. const Vector& vecOrigin = pThrower->GetAbsOrigin();
  788. CPVSFilter pvsFilter( vecOrigin );
  789. TE_TFParticleEffect( pvsFilter, 0.f, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  790. // Move 'em!
  791. pThrower->Teleport( &vecStart, &pThrower->GetAbsAngles(), NULL );
  792. // Do a zoom effect
  793. pThrower->SetFOV( pThrower, 0.f, 0.3f, 120.f );
  794. // Screen flash
  795. color32 fadeColor = { 255, 255, 255, 100 };
  796. UTIL_ScreenFade( pThrower, fadeColor, 0.25f, 0.4f, FFADE_IN );
  797. if ( TFGameRules() )
  798. {
  799. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_TELEPORT, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  800. }
  801. }
  802. }
  803. BaseClass::Explode( pTrace, bitsDamageType );
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. //-----------------------------------------------------------------------------
  808. void CTFProjectile_GravityGrenade::Spawn( void )
  809. {
  810. BaseClass::Spawn();
  811. m_flStartTime = -1.f;
  812. m_flNextPulseEffectTime = -1.f;
  813. m_bHitWorld = false;
  814. }
  815. //-----------------------------------------------------------------------------
  816. // Purpose:
  817. //-----------------------------------------------------------------------------
  818. void CTFProjectile_GravityGrenade::TrapThink( void )
  819. {
  820. if ( gpGlobals->curtime > m_flStartTime + 5.f )
  821. {
  822. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  823. SetTouch( NULL );
  824. AddEffects( EF_NODRAW );
  825. SetAbsVelocity( vec3_origin );
  826. return;
  827. }
  828. PulseTrap();
  829. SetContextThink( &CTFProjectile_GravityGrenade::TrapThink, gpGlobals->curtime + 0.15f, "TrapThink" );
  830. }
  831. //-----------------------------------------------------------------------------
  832. // Purpose:
  833. //-----------------------------------------------------------------------------
  834. void CTFProjectile_GravityGrenade::OnHitWorld( void )
  835. {
  836. if ( !m_bHitWorld )
  837. {
  838. SetDetonateTimerLength( FLT_MAX );
  839. m_bHitWorld = true;
  840. m_flStartTime = gpGlobals->curtime;
  841. AddSolidFlags( FSOLID_TRIGGER );
  842. SetTouch( NULL );
  843. SetContextThink( &CTFProjectile_GravityGrenade::TrapThink, gpGlobals->curtime, "TrapThink" );
  844. }
  845. BaseClass::OnHitWorld();
  846. }
  847. //-----------------------------------------------------------------------------
  848. // Purpose:
  849. //-----------------------------------------------------------------------------
  850. void CTFProjectile_GravityGrenade::PulseTrap( void )
  851. {
  852. const int nMaxEnts = 32;
  853. Vector vecPos = GetAbsOrigin();
  854. CBaseEntity *pObjects[ nMaxEnts ];
  855. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecPos, GetDamageRadius(), FL_CLIENT );
  856. // Iterate through sphere's contents
  857. for ( int i = 0; i < nCount; i++ )
  858. {
  859. CBaseCombatCharacter *pEntity = pObjects[i]->MyCombatCharacterPointer();
  860. if ( !pEntity )
  861. continue;
  862. if ( InSameTeam( pEntity ) )
  863. continue;
  864. if ( !FVisible( pEntity, MASK_OPAQUE ) )
  865. continue;
  866. // Draw player toward us
  867. Vector vecSourcePos = pEntity->GetAbsOrigin();
  868. Vector vecTargetPos = GetAbsOrigin();
  869. Vector vecVelocity = ( vecTargetPos - vecSourcePos ) * 2.f;
  870. vecVelocity.z += 50.f;
  871. if ( pEntity->GetFlags() & FL_ONGROUND )
  872. {
  873. vecVelocity.z += 150.f;
  874. pEntity->SetGroundEntity( NULL );
  875. pEntity->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
  876. }
  877. pEntity->Teleport( NULL, NULL, &vecVelocity );
  878. }
  879. // NDebugOverlay::Sphere( vecPos, GetDamageRadius(), 0, 255, 0, false, 0.35f );
  880. PulseEffects();
  881. }
  882. //-----------------------------------------------------------------------------
  883. //
  884. //-----------------------------------------------------------------------------
  885. void CTFProjectile_GravityGrenade::PulseEffects( void )
  886. {
  887. if ( gpGlobals->curtime < m_flNextPulseEffectTime )
  888. return;
  889. Vector vecOrigin = GetAbsOrigin();
  890. CPVSFilter filter( vecOrigin );
  891. TE_TFParticleEffect( filter, 0.f, "Explosion_ShockWave_01", vecOrigin, vec3_angle );
  892. EmitSound( filter, entindex(), "Weapon_Upgrade.ExplosiveHeadshot" );
  893. m_flNextPulseEffectTime = gpGlobals->curtime + 1.f;
  894. }
  895. //-----------------------------------------------------------------------------
  896. // THROWABLE KNIFE
  897. //-----------------------------------------------------------------------------
  898. Vector CTFProjectile_ThrowingKnife::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, float flCharge )
  899. {
  900. // Scale the projectile speed up to a maximum of 3000?
  901. float flSpeed = RemapVal( flCharge, 0, 1.0f, GetProjectileSpeed(), GetProjectileMaxSpeed() );
  902. return ( ( flSpeed * vecForward ) +
  903. ( flSpeed * 0.1 * vecUp ) );
  904. }
  905. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  906. void CTFProjectile_ThrowingKnife::OnHit( CBaseEntity *pOther )
  907. {
  908. if ( m_bHit )
  909. return;
  910. CTFPlayer *pVictim = dynamic_cast< CTFPlayer*>( pOther );
  911. CTFPlayer *pOwner = dynamic_cast< CTFPlayer*>( GetThrower() );
  912. if ( pVictim && pOwner && !pVictim->InSameTeam( pOwner ) )
  913. {
  914. CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  915. trace_t trace;
  916. UTIL_TraceLine( GetAbsOrigin(), pVictim->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace );
  917. Vector entForward;
  918. AngleVectors( pVictim->EyeAngles(), &entForward );
  919. Vector toEnt = pVictim->GetAbsOrigin() - this->GetAbsOrigin();
  920. toEnt.z = 0;
  921. entForward.z = 0;
  922. toEnt.NormalizeInPlace();
  923. entForward.NormalizeInPlace();
  924. // Is from behind?
  925. bool bIsFromBehind = DotProduct( toEnt, entForward ) > 0.7071f;
  926. // Apply Damage to Victim
  927. CTakeDamageInfo info;
  928. info.SetAttacker( GetThrower() );
  929. info.SetInflictor( this );
  930. info.SetWeapon( GetLauncher() );
  931. info.SetDamageCustom( GetCustomDamageType() );
  932. info.SetDamagePosition( GetAbsOrigin() );
  933. int iDamageType = DMG_CLUB;
  934. if ( bIsFromBehind )
  935. {
  936. iDamageType |= DMG_CRITICAL;
  937. }
  938. info.SetDamageType( iDamageType );
  939. info.SetDamage( bIsFromBehind ? GetBackHitDamage() : GetDamage() );
  940. pVictim->DispatchTraceAttack( info, toEnt, &trace );
  941. ApplyMultiDamage();
  942. CreateStickyAttachmentToTarget( pOwner, pVictim, &trace );
  943. Explode();
  944. return;
  945. }
  946. else // its a dud, mark as hit and let it roll around
  947. {
  948. m_bHit = true;
  949. }
  950. BaseClass::OnHit( pOther );
  951. }
  952. //-----------------------------------------------------------------------------
  953. void CTFProjectile_ThrowingKnife::Detonate()
  954. {
  955. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  956. SetTouch( NULL );
  957. AddEffects( EF_NODRAW );
  958. SetAbsVelocity( vec3_origin );
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Purpose:
  962. //-----------------------------------------------------------------------------
  963. void CTFProjectile_SmokeGrenade::Spawn( void )
  964. {
  965. BaseClass::Spawn();
  966. m_flStartTime = -1.f;
  967. m_bHitWorld = false;
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose:
  971. //-----------------------------------------------------------------------------
  972. void CTFProjectile_SmokeGrenade::OnHitWorld( void )
  973. {
  974. if ( !m_bHitWorld )
  975. {
  976. SetDetonateTimerLength( FLT_MAX );
  977. // VPhysicsGetObject()->EnableMotion( false );
  978. m_bHitWorld = true;
  979. m_flStartTime = gpGlobals->curtime;
  980. const char *pszSoundEffect = GetExplodeEffectSound();
  981. if ( pszSoundEffect && pszSoundEffect[0] != '\0' )
  982. {
  983. EmitSound( pszSoundEffect );
  984. }
  985. AddSolidFlags( FSOLID_TRIGGER );
  986. SetTouch( NULL );
  987. SetContextThink( &CTFProjectile_SmokeGrenade::SmokeThink, gpGlobals->curtime, "SmokeThink" );
  988. }
  989. BaseClass::OnHitWorld();
  990. }
  991. //-----------------------------------------------------------------------------
  992. // Purpose:
  993. //-----------------------------------------------------------------------------
  994. void CTFProjectile_SmokeGrenade::SmokeThink( void )
  995. {
  996. if ( gpGlobals->curtime > m_flStartTime + 6.f )
  997. {
  998. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  999. AddEffects( EF_NODRAW );
  1000. SetAbsVelocity( vec3_origin );
  1001. return;
  1002. }
  1003. CPVSFilter filter( GetAbsOrigin() );
  1004. TE_TFParticleEffect( filter, 0.f, "grenade_smoke_cycle", GetAbsOrigin(), vec3_angle );
  1005. const int nMaxEnts = 32;
  1006. Vector vecPos = GetAbsOrigin();
  1007. CBaseEntity *pObjects[ nMaxEnts ];
  1008. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecPos, GetDamageRadius(), FL_CLIENT );
  1009. // Iterate through sphere's contents
  1010. for ( int i = 0; i < nCount; i++ )
  1011. {
  1012. CBaseCombatCharacter *pEntity = pObjects[i]->MyCombatCharacterPointer();
  1013. if ( !pEntity )
  1014. continue;
  1015. if ( !InSameTeam( pEntity ) )
  1016. continue;
  1017. if ( !FVisible( pEntity, MASK_OPAQUE ) )
  1018. continue;
  1019. if ( !pEntity->IsPlayer() )
  1020. continue;
  1021. CTFPlayer *pTFPlayer = ToTFPlayer( pEntity );
  1022. if ( !pTFPlayer )
  1023. continue;
  1024. pTFPlayer->m_Shared.AddCond( TF_COND_OBSCURED_SMOKE, 0.5f );
  1025. }
  1026. // NDebugOverlay::Sphere( vecPos, GetDamageRadius(), 0, 255, 0, false, 0.35f );
  1027. SetContextThink( &CTFProjectile_SmokeGrenade::SmokeThink, gpGlobals->curtime + 0.3f, "SmokeThink" );
  1028. }
  1029. #endif // GAME_DLL
  1030. #endif // STAGING_ONLY