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.

284 lines
7.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "grenade_spit.h"
  11. #include "soundent.h"
  12. #include "decals.h"
  13. #include "smoke_trail.h"
  14. #include "hl2_shareddefs.h"
  15. #include "vstdlib/random.h"
  16. #include "engine/IEngineSound.h"
  17. #include "particle_parse.h"
  18. #include "particle_system.h"
  19. #include "soundenvelope.h"
  20. #include "ai_utils.h"
  21. #include "te_effect_dispatch.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. ConVar sk_antlion_worker_spit_grenade_dmg ( "sk_antlion_worker_spit_grenade_dmg", "20", FCVAR_NONE, "Total damage done by an individual antlion worker loogie.");
  25. ConVar sk_antlion_worker_spit_grenade_radius ( "sk_antlion_worker_spit_grenade_radius","40", FCVAR_NONE, "Radius of effect for an antlion worker spit grenade.");
  26. ConVar sk_antlion_worker_spit_grenade_poison_ratio ( "sk_antlion_worker_spit_grenade_poison_ratio","0.3", FCVAR_NONE, "Percentage of an antlion worker's spit damage done as poison (which regenerates)");
  27. LINK_ENTITY_TO_CLASS( grenade_spit, CGrenadeSpit );
  28. BEGIN_DATADESC( CGrenadeSpit )
  29. DEFINE_FIELD( m_bPlaySound, FIELD_BOOLEAN ),
  30. // Function pointers
  31. DEFINE_ENTITYFUNC( GrenadeSpitTouch ),
  32. END_DATADESC()
  33. CGrenadeSpit::CGrenadeSpit( void ) : m_bPlaySound( true ), m_pHissSound( NULL )
  34. {
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose:
  38. //-----------------------------------------------------------------------------
  39. void CGrenadeSpit::Spawn( void )
  40. {
  41. Precache( );
  42. SetSolid( SOLID_BBOX );
  43. SetMoveType( MOVETYPE_FLYGRAVITY );
  44. SetSolidFlags( FSOLID_NOT_STANDABLE );
  45. SetModel( "models/spitball_large.mdl" );
  46. UTIL_SetSize( this, vec3_origin, vec3_origin );
  47. SetUse( &CBaseGrenade::DetonateUse );
  48. SetTouch( &CGrenadeSpit::GrenadeSpitTouch );
  49. SetNextThink( gpGlobals->curtime + 0.1f );
  50. m_flDamage = sk_antlion_worker_spit_grenade_dmg.GetFloat();
  51. m_DmgRadius = sk_antlion_worker_spit_grenade_radius.GetFloat();
  52. m_takedamage = DAMAGE_NO;
  53. m_iHealth = 1;
  54. SetGravity( UTIL_ScaleForGravity( SPIT_GRAVITY ) );
  55. SetFriction( 0.8f );
  56. SetCollisionGroup( HL2COLLISION_GROUP_SPIT );
  57. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  58. // We're self-illuminating, so we don't take or give shadows
  59. AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
  60. // Create the dust effect in place
  61. m_hSpitEffect = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
  62. if ( m_hSpitEffect != NULL )
  63. {
  64. // Setup our basic parameters
  65. m_hSpitEffect->KeyValue( "start_active", "1" );
  66. m_hSpitEffect->KeyValue( "effect_name", "antlion_spit_trail" );
  67. m_hSpitEffect->SetParent( this );
  68. m_hSpitEffect->SetLocalOrigin( vec3_origin );
  69. DispatchSpawn( m_hSpitEffect );
  70. if ( gpGlobals->curtime > 0.5f )
  71. m_hSpitEffect->Activate();
  72. }
  73. }
  74. void CGrenadeSpit::SetSpitSize( int nSize )
  75. {
  76. switch (nSize)
  77. {
  78. case SPIT_LARGE:
  79. {
  80. m_bPlaySound = true;
  81. SetModel( "models/spitball_large.mdl" );
  82. break;
  83. }
  84. case SPIT_MEDIUM:
  85. {
  86. m_bPlaySound = true;
  87. m_flDamage *= 0.5f;
  88. SetModel( "models/spitball_medium.mdl" );
  89. break;
  90. }
  91. case SPIT_SMALL:
  92. {
  93. m_bPlaySound = false;
  94. m_flDamage *= 0.25f;
  95. SetModel( "models/spitball_small.mdl" );
  96. break;
  97. }
  98. }
  99. }
  100. void CGrenadeSpit::Event_Killed( const CTakeDamageInfo &info )
  101. {
  102. Detonate( );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Handle spitting
  106. //-----------------------------------------------------------------------------
  107. void CGrenadeSpit::GrenadeSpitTouch( CBaseEntity *pOther )
  108. {
  109. if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) )
  110. {
  111. // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
  112. if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
  113. return;
  114. }
  115. // Don't hit other spit
  116. if ( pOther->GetCollisionGroup() == HL2COLLISION_GROUP_SPIT )
  117. return;
  118. // We want to collide with water
  119. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  120. // copy out some important things about this trace, because the first TakeDamage
  121. // call below may cause another trace that overwrites the one global pTrace points
  122. // at.
  123. bool bHitWater = ( ( pTrace->contents & CONTENTS_WATER ) != 0 );
  124. CBaseEntity *const pTraceEnt = pTrace->m_pEnt;
  125. const Vector tracePlaneNormal = pTrace->plane.normal;
  126. if ( bHitWater )
  127. {
  128. // Splash!
  129. CEffectData data;
  130. data.m_fFlags = 0;
  131. data.m_vOrigin = pTrace->endpos;
  132. data.m_vNormal = Vector( 0, 0, 1 );
  133. data.m_flScale = 8.0f;
  134. DispatchEffect( "watersplash", data );
  135. }
  136. else
  137. {
  138. // Make a splat decal
  139. trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
  140. UTIL_DecalTrace( pNewTrace, "BeerSplash" );
  141. }
  142. // Part normal damage, part poison damage
  143. float poisonratio = sk_antlion_worker_spit_grenade_poison_ratio.GetFloat();
  144. // Take direct damage if hit
  145. // NOTE: assume that pTrace is invalidated from this line forward!
  146. if ( pTraceEnt )
  147. {
  148. pTraceEnt->TakeDamage( CTakeDamageInfo( this, GetThrower(), m_flDamage * (1.0f-poisonratio), DMG_ACID ) );
  149. pTraceEnt->TakeDamage( CTakeDamageInfo( this, GetThrower(), m_flDamage * poisonratio, DMG_POISON ) );
  150. }
  151. CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), m_DmgRadius * 2.0f, 0.5f, GetThrower() );
  152. QAngle vecAngles;
  153. VectorAngles( tracePlaneNormal, vecAngles );
  154. if ( pOther->IsPlayer() || bHitWater )
  155. {
  156. // Do a lighter-weight effect if we just hit a player
  157. DispatchParticleEffect( "antlion_spit_player", GetAbsOrigin(), vecAngles );
  158. }
  159. else
  160. {
  161. DispatchParticleEffect( "antlion_spit", GetAbsOrigin(), vecAngles );
  162. }
  163. Detonate();
  164. }
  165. void CGrenadeSpit::Detonate(void)
  166. {
  167. m_takedamage = DAMAGE_NO;
  168. EmitSound( "GrenadeSpit.Hit" );
  169. // Stop our hissing sound
  170. if ( m_pHissSound != NULL )
  171. {
  172. CSoundEnvelopeController::GetController().SoundDestroy( m_pHissSound );
  173. m_pHissSound = NULL;
  174. }
  175. if ( m_hSpitEffect )
  176. {
  177. UTIL_Remove( m_hSpitEffect );
  178. }
  179. UTIL_Remove( this );
  180. }
  181. void CGrenadeSpit::InitHissSound( void )
  182. {
  183. if ( m_bPlaySound == false )
  184. return;
  185. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  186. if ( m_pHissSound == NULL )
  187. {
  188. CPASAttenuationFilter filter( this );
  189. m_pHissSound = controller.SoundCreate( filter, entindex(), "NPC_Antlion.PoisonBall" );
  190. controller.Play( m_pHissSound, 1.0f, 100 );
  191. }
  192. }
  193. void CGrenadeSpit::Think( void )
  194. {
  195. InitHissSound();
  196. if ( m_pHissSound == NULL )
  197. return;
  198. // Add a doppler effect to the balls as they travel
  199. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  200. if ( pPlayer != NULL )
  201. {
  202. Vector dir;
  203. VectorSubtract( pPlayer->GetAbsOrigin(), GetAbsOrigin(), dir );
  204. VectorNormalize(dir);
  205. float velReceiver = DotProduct( pPlayer->GetAbsVelocity(), dir );
  206. float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
  207. // speed of sound == 13049in/s
  208. int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
  209. // clamp pitch shifts
  210. if ( iPitch > 250 )
  211. {
  212. iPitch = 250;
  213. }
  214. if ( iPitch < 50 )
  215. {
  216. iPitch = 50;
  217. }
  218. // Set the pitch we've calculated
  219. CSoundEnvelopeController::GetController().SoundChangePitch( m_pHissSound, iPitch, 0.1f );
  220. }
  221. // Set us up to think again shortly
  222. SetNextThink( gpGlobals->curtime + 0.05f );
  223. }
  224. void CGrenadeSpit::Precache( void )
  225. {
  226. // m_nSquidSpitSprite = PrecacheModel("sprites/greenglow1.vmt");// client side spittle.
  227. PrecacheModel( "models/spitball_large.mdl" );
  228. PrecacheModel("models/spitball_medium.mdl");
  229. PrecacheModel("models/spitball_small.mdl");
  230. PrecacheScriptSound( "GrenadeSpit.Hit" );
  231. PrecacheParticleSystem( "antlion_spit_player" );
  232. PrecacheParticleSystem( "antlion_spit" );
  233. }