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.

402 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // TF Energy Ball
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_projectile_energy_ball.h"
  8. #include "soundent.h"
  9. #include "tf_fx.h"
  10. #include "props.h"
  11. #include "baseobject_shared.h"
  12. #include "SpriteTrail.h"
  13. #include "IEffects.h"
  14. #include "te_effect_dispatch.h"
  15. #include "collisionutils.h"
  16. #include "bone_setup.h"
  17. #include "decals.h"
  18. #include "tf_player.h"
  19. #include "te_effect_dispatch.h"
  20. #include "tf_gamerules.h"
  21. #include "tf_weapon_rocketlauncher.h"
  22. //=============================================================================
  23. //
  24. // TF Energy Ball Projectile functions (Server specific).
  25. //
  26. #define ENERGY_BALL_MODEL "models/weapons/w_models/w_drg_ball.mdl"
  27. #define ARROW_GRAVITY 0.3f
  28. #define ENERGY_BALL_THINK_CONTEXT "CTFProjectile_EnergyBallThink"
  29. //-----------------------------------------------------------------------------
  30. LINK_ENTITY_TO_CLASS( tf_projectile_energy_ball, CTFProjectile_EnergyBall );
  31. PRECACHE_WEAPON_REGISTER( tf_projectile_energy_ball );
  32. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_EnergyBall, DT_TFProjectile_EnergyBall )
  33. BEGIN_NETWORK_TABLE( CTFProjectile_EnergyBall, DT_TFProjectile_EnergyBall )
  34. SendPropBool( SENDINFO( m_bChargedShot ) ),
  35. SendPropVector( SENDINFO( m_vColor1 ), 8, 0, 0, 1 ),
  36. SendPropVector( SENDINFO( m_vColor2 ), 8, 0, 0, 1 )
  37. END_NETWORK_TABLE()
  38. BEGIN_DATADESC( CTFProjectile_EnergyBall )
  39. //DEFINE_THINKFUNC( ImpactThink ),
  40. END_DATADESC()
  41. //-----------------------------------------------------------------------------
  42. // Purpose:
  43. //-----------------------------------------------------------------------------
  44. CTFProjectile_EnergyBall::CTFProjectile_EnergyBall()
  45. {
  46. m_bChargedShot = false;
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. CTFProjectile_EnergyBall::~CTFProjectile_EnergyBall()
  52. {
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose:
  56. //-----------------------------------------------------------------------------
  57. CTFProjectile_EnergyBall *CTFProjectile_EnergyBall::Create( const Vector &vecOrigin, const QAngle &vecAngles, const float fSpeed, const float fGravity, CBaseEntity *pOwner, CBaseEntity *pScorer )
  58. {
  59. CTFProjectile_EnergyBall *pBall = static_cast<CTFProjectile_EnergyBall*>( CBaseEntity::Create( "tf_projectile_energy_ball", vecOrigin, vecAngles, pOwner ) );
  60. if ( pBall )
  61. {
  62. pBall->InitEnergyBall( vecOrigin, vecAngles, fSpeed, fGravity, pOwner, pScorer );
  63. }
  64. return pBall;
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. void CTFProjectile_EnergyBall::InitEnergyBall( const Vector &vecOrigin, const QAngle &vecAngles, const float fSpeed, const float fGravity, CBaseEntity *pOwner, CBaseEntity *pScorer )
  70. {
  71. // Initialize the owner.
  72. SetOwnerEntity( pOwner );
  73. // Set team.
  74. ChangeTeam( pOwner->GetTeamNumber() );
  75. // Spawn.
  76. Spawn();
  77. SetGravity( fGravity );
  78. SetCritical( true );
  79. // Setup the initial velocity.
  80. Vector vecForward, vecRight, vecUp;
  81. AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
  82. Vector vecVelocity = vecForward * fSpeed;
  83. SetAbsVelocity( vecVelocity );
  84. SetupInitialTransmittedGrenadeVelocity( vecVelocity );
  85. // Setup the initial angles.
  86. QAngle angles;
  87. VectorAngles( vecVelocity, angles );
  88. SetAbsAngles( angles );
  89. // Save the scoring player.
  90. SetScorer( pScorer );
  91. if ( pScorer )
  92. {
  93. SetTruceValidForEnt( pScorer->IsTruceValidForEnt() );
  94. }
  95. m_flInitTime = gpGlobals->curtime;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose:
  99. //-----------------------------------------------------------------------------
  100. void CTFProjectile_EnergyBall::Spawn()
  101. {
  102. SetModel( ENERGY_BALL_MODEL );
  103. BaseClass::Spawn();
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose:
  107. //-----------------------------------------------------------------------------
  108. void CTFProjectile_EnergyBall::Precache()
  109. {
  110. PrecacheParticleSystem( "drg_cow_rockettrail_charged" );
  111. PrecacheParticleSystem( "drg_cow_rockettrail_charged_blue" );
  112. PrecacheParticleSystem( "drg_cow_rockettrail_normal" );
  113. PrecacheParticleSystem( "drg_cow_rockettrail_normal_blue" );
  114. PrecacheModel( ENERGY_BALL_MODEL );
  115. PrecacheScriptSound( "Weapon_CowMangler.Explode" );
  116. BaseClass::Precache();
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Purpose:
  120. //-----------------------------------------------------------------------------
  121. void CTFProjectile_EnergyBall::SetScorer( CBaseEntity *pScorer )
  122. {
  123. m_Scorer = pScorer;
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose:
  127. //-----------------------------------------------------------------------------
  128. CBasePlayer *CTFProjectile_EnergyBall::GetScorer( void )
  129. {
  130. return dynamic_cast<CBasePlayer *>( m_Scorer.Get() );
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose: Plays an impact sound. Louder for the attacker.
  134. //-----------------------------------------------------------------------------
  135. void CTFProjectile_EnergyBall::ImpactSound( const char *pszSoundName, bool bLoudForAttacker )
  136. {
  137. CTFPlayer *pAttacker = ToTFPlayer( GetScorer() );
  138. if ( !pAttacker )
  139. return;
  140. if ( bLoudForAttacker )
  141. {
  142. float soundlen = 0;
  143. EmitSound_t params;
  144. params.m_flSoundTime = 0;
  145. params.m_pSoundName = pszSoundName;
  146. params.m_pflSoundDuration = &soundlen;
  147. CPASFilter filter( GetAbsOrigin() );
  148. filter.RemoveRecipient( ToTFPlayer(pAttacker) );
  149. EmitSound( filter, entindex(), params );
  150. CSingleUserRecipientFilter attackerFilter( ToTFPlayer(pAttacker) );
  151. EmitSound( attackerFilter, pAttacker->entindex(), params );
  152. }
  153. else
  154. {
  155. EmitSound( pszSoundName );
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. //-----------------------------------------------------------------------------
  161. void CTFProjectile_EnergyBall::FadeOut( int iTime )
  162. {
  163. SetMoveType( MOVETYPE_NONE );
  164. SetAbsVelocity( vec3_origin );
  165. AddSolidFlags( FSOLID_NOT_SOLID );
  166. AddEffects( EF_NODRAW );
  167. // Start remove timer.
  168. SetContextThink( &CTFProjectile_EnergyBall::RemoveThink, gpGlobals->curtime + iTime, "ENERGY_BALL_REMOVE_THINK" );
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. void CTFProjectile_EnergyBall::RemoveThink( void )
  174. {
  175. UTIL_Remove( this );
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: Arrow was deflected.
  179. //-----------------------------------------------------------------------------
  180. void CTFProjectile_EnergyBall::Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir )
  181. {
  182. CTFPlayer *pTFDeflector = ToTFPlayer( pDeflectedBy );
  183. if ( !pTFDeflector )
  184. return;
  185. ChangeTeam( pTFDeflector->GetTeamNumber() );
  186. SetLauncher( pTFDeflector->GetActiveWeapon() );
  187. CTFPlayer* pOldOwner = ToTFPlayer( GetOwnerEntity() );
  188. SetOwnerEntity( pTFDeflector );
  189. if ( pOldOwner )
  190. {
  191. pOldOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:1,victim:1" );
  192. }
  193. if ( pTFDeflector->m_Shared.IsCritBoosted() )
  194. {
  195. SetCritical( true );
  196. }
  197. CTFWeaponBase::SendObjectDeflectedEvent( pTFDeflector, pOldOwner, GetWeaponID(), this );
  198. IncrementDeflected();
  199. SetScorer( pTFDeflector );
  200. // Change particle color data.
  201. if ( GetTeamNumber() == TF_TEAM_BLUE )
  202. {
  203. m_vColor1 = TF_PARTICLE_WEAPON_BLUE_1;
  204. m_vColor2 = TF_PARTICLE_WEAPON_BLUE_2;
  205. }
  206. else
  207. {
  208. m_vColor1 = TF_PARTICLE_WEAPON_RED_1;
  209. m_vColor2 = TF_PARTICLE_WEAPON_RED_2;
  210. }
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CTFProjectile_EnergyBall::Explode( trace_t *pTrace, CBaseEntity *pOther )
  216. {
  217. if ( ShouldNotDetonate() )
  218. {
  219. Destroy( true );
  220. return;
  221. }
  222. // Save this entity as enemy, they will take 100% damage.
  223. m_hEnemy = pOther;
  224. // Invisible.
  225. SetModelName( NULL_STRING );
  226. AddSolidFlags( FSOLID_NOT_SOLID );
  227. m_takedamage = DAMAGE_NO;
  228. // Pull out a bit.
  229. if ( pTrace->fraction != 1.0 )
  230. {
  231. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  232. }
  233. // Particle (oriented)
  234. Vector vecOrigin = GetAbsOrigin();
  235. CPVSFilter filter( vecOrigin );
  236. QAngle angExplosion( 0.f, 0.f, 0.f );
  237. VectorAngles( pTrace->plane.normal, angExplosion );
  238. TE_TFParticleEffect( filter, 0.f, GetExplosionParticleName(), vecOrigin, pTrace->plane.normal, angExplosion, NULL );
  239. // Screenshake
  240. if ( m_bChargedShot )
  241. {
  242. UTIL_ScreenShake( WorldSpaceCenter(), 25.0, 150.0, 1.0, 750, SHAKE_START );
  243. }
  244. // Sound
  245. ImpactSound( "Weapon_CowMangler.Explode" );
  246. CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 );
  247. // Damage.
  248. CBaseEntity *pAttacker = GetOwnerEntity();
  249. IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker );
  250. if ( pScorerInterface )
  251. {
  252. pAttacker = pScorerInterface->GetScorer();
  253. }
  254. float flRadius = GetRadius();
  255. if ( pAttacker ) // No attacker, deal no damage. Otherwise we could potentially kill teammates.
  256. {
  257. CTFPlayer *pTarget = ToTFPlayer( GetEnemy() );
  258. if ( pTarget )
  259. {
  260. // Rocket Specialist
  261. CheckForStunOnImpact( pTarget );
  262. if ( pTarget->GetTeamNumber() != pAttacker->GetTeamNumber() )
  263. {
  264. IGameEvent *event = gameeventmanager->CreateEvent( "projectile_direct_hit" );
  265. if ( event )
  266. {
  267. event->SetInt( "attacker", pAttacker->entindex() );
  268. event->SetInt( "victim", pTarget->entindex() );
  269. item_definition_index_t ownerWeaponDefIndex = INVALID_ITEM_DEF_INDEX;
  270. CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( GetOriginalLauncher() );
  271. if ( pWeapon )
  272. {
  273. ownerWeaponDefIndex = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex();
  274. }
  275. event->SetInt( "weapon_def_index", ownerWeaponDefIndex );
  276. gameeventmanager->FireEvent( event, true );
  277. }
  278. }
  279. }
  280. CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), GetDamageType(), GetDamageCustom() );
  281. CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, m_bChargedShot ? TF_ROCKET_RADIUS_FOR_RJS*1.33 : TF_ROCKET_RADIUS_FOR_RJS );
  282. TFGameRules()->RadiusDamage( radiusinfo );
  283. }
  284. // Don't decal players with scorch.
  285. if ( !pOther->IsPlayer() )
  286. {
  287. UTIL_DecalTrace( pTrace, "Scorch" );
  288. }
  289. // Remove the rocket.
  290. UTIL_Remove( this );
  291. return;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. //-----------------------------------------------------------------------------
  296. const char *CTFProjectile_EnergyBall::GetExplosionParticleName( void )
  297. {
  298. if ( m_bChargedShot )
  299. {
  300. return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_explosioncore_charged" : "drg_cow_explosioncore_charged_blue";
  301. }
  302. else
  303. {
  304. return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_explosioncore_normal" : "drg_cow_explosioncore_normal_blue";
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. float CTFProjectile_EnergyBall::GetDamage()
  311. {
  312. return m_flDamage;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. int CTFProjectile_EnergyBall::GetDamageType()
  318. {
  319. int iDamageType;
  320. if ( m_bChargedShot )
  321. {
  322. iDamageType = DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD | DMG_IGNITE;
  323. }
  324. else
  325. {
  326. iDamageType = DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD;
  327. }
  328. return iDamageType;
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. //-----------------------------------------------------------------------------
  333. int CTFProjectile_EnergyBall::GetDamageCustom()
  334. {
  335. return m_bChargedShot ? TF_DMG_CUSTOM_PLASMA_CHARGED : TF_DMG_CUSTOM_PLASMA;
  336. }