Counter Strike : Global Offensive Source Code
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.

451 lines
13 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "flashbang_projectile.h"
  8. #include "shake.h"
  9. #include "engine/IEngineSound.h"
  10. #include "cs_player.h"
  11. #include "dlight.h"
  12. #include "keyvalues.h"
  13. #include "weapon_csbase.h"
  14. #include "cs_gamerules.h"
  15. #include "animation.h"
  16. #define GRENADE_MODEL "models/Weapons/w_eq_flashbang_dropped.mdl"
  17. LINK_ENTITY_TO_CLASS( flashbang_projectile, CFlashbangProjectile );
  18. PRECACHE_REGISTER( flashbang_projectile );
  19. #if !defined( CLIENT_DLL )
  20. BEGIN_DATADESC( CFlashbangProjectile )
  21. // Fields
  22. //DEFINE_KEYFIELD( m_flTimeToDetonate, FIELD_FLOAT, "TimeToDetonate" ),
  23. // Inputs
  24. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTimer", InputSetTimer ),
  25. END_DATADESC()
  26. #endif
  27. // hack to allow de_nuke vents to occlude flashbangs when closed
  28. class CTraceFilterNoPlayersAndFlashbangPassableAnims : public CTraceFilterNoPlayers
  29. {
  30. public:
  31. CTraceFilterNoPlayersAndFlashbangPassableAnims( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE )
  32. : CTraceFilterNoPlayers( passentity, collisionGroup )
  33. {
  34. }
  35. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  36. {
  37. CBaseEntity *pEnt = EntityFromEntityHandle(pHandleEntity);
  38. if ( pEnt )
  39. {
  40. CBaseAnimating* pAnimating = dynamic_cast< CBaseAnimating* >( pEnt );
  41. if ( pAnimating )
  42. {
  43. // look for the flashbang passable animtag
  44. float flFlashbangPassable = pAnimating->GetAnySequenceAnimTag( pAnimating->GetSequence(), ANIMTAG_FLASHBANG_PASSABLE, -1 );
  45. if ( flFlashbangPassable != -1 )
  46. return false; // model animation is tagged to allow flashbangs through
  47. }
  48. // Weapons don't block flashbangs
  49. CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pEnt );
  50. CBaseGrenade* pGrenade = dynamic_cast< CBaseGrenade* > ( pEnt );
  51. if ( pWeapon || pGrenade )
  52. return false;
  53. }
  54. return CTraceFilterNoPlayers::ShouldHitEntity( pHandleEntity, contentsMask );
  55. }
  56. };
  57. float PercentageOfFlashForPlayer(CBaseEntity *player, Vector flashPos, CBaseEntity *pevInflictor)
  58. {
  59. if (!(player->IsPlayer()))
  60. {
  61. // if this entity isn't a player, it's a hostage or some other entity, then don't bother with the expensive checks
  62. // that come below.
  63. return 0.0f;
  64. }
  65. const float FLASH_FRACTION = 0.167f;
  66. const float SIDE_OFFSET = 75.0f;
  67. Vector pos = player->EyePosition();
  68. Vector vecRight, vecUp;
  69. QAngle tempAngle;
  70. VectorAngles(player->EyePosition() - flashPos, tempAngle);
  71. AngleVectors(tempAngle, NULL, &vecRight, &vecUp);
  72. vecRight.NormalizeInPlace();
  73. vecUp.NormalizeInPlace();
  74. // Set up all the ray stuff.
  75. // We don't want to let other players block the flash bang so we use this custom filter.
  76. Ray_t ray;
  77. trace_t tr;
  78. CTraceFilterNoPlayersAndFlashbangPassableAnims traceFilter( pevInflictor, COLLISION_GROUP_NONE );
  79. unsigned int FLASH_MASK = MASK_OPAQUE_AND_NPCS | CONTENTS_DEBRIS;
  80. // According to comment in IsNoDrawBrush in cmodel.cpp, CONTENTS_OPAQUE is ONLY used for block light surfaces,
  81. // and we want flashbang traces to pass through those, since the block light surface is only used for blocking
  82. // lightmap light rays during map compilation.
  83. FLASH_MASK &= ~CONTENTS_OPAQUE;
  84. ray.Init( flashPos, pos );
  85. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  86. if ((tr.fraction == 1.0f) || (tr.m_pEnt == player))
  87. {
  88. return 1.0f;
  89. }
  90. float retval = 0.0f;
  91. // check the point straight up.
  92. pos = flashPos + vecUp*50.0f;
  93. ray.Init( flashPos, pos );
  94. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  95. // Now shoot it to the player's eye.
  96. pos = player->EyePosition();
  97. ray.Init( tr.endpos, pos );
  98. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  99. if ((tr.fraction == 1.0f) || (tr.m_pEnt == player))
  100. {
  101. retval += FLASH_FRACTION;
  102. }
  103. // check the point up and right.
  104. pos = flashPos + vecRight*SIDE_OFFSET + vecUp*10.0f;
  105. ray.Init( flashPos, pos );
  106. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  107. // Now shoot it to the player's eye.
  108. pos = player->EyePosition();
  109. ray.Init( tr.endpos, pos );
  110. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  111. if ((tr.fraction == 1.0f) || (tr.m_pEnt == player))
  112. {
  113. retval += FLASH_FRACTION;
  114. }
  115. // Check the point up and left.
  116. pos = flashPos - vecRight*SIDE_OFFSET + vecUp*10.0f;
  117. ray.Init( flashPos, pos );
  118. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  119. // Now shoot it to the player's eye.
  120. pos = player->EyePosition();
  121. ray.Init( tr.endpos, pos );
  122. enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
  123. if ((tr.fraction == 1.0f) || (tr.m_pEnt == player))
  124. {
  125. retval += FLASH_FRACTION;
  126. }
  127. return retval;
  128. }
  129. // --------------------------------------------------------------------------------------------------- //
  130. //
  131. // RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range.
  132. //
  133. // only damage ents that can clearly be seen by the explosion!
  134. // --------------------------------------------------------------------------------------------------- //
  135. void RadiusFlash(
  136. Vector vecSrc,
  137. CBaseEntity *pevInflictor,
  138. CBaseEntity *pevAttacker,
  139. float flDamage,
  140. int iClassIgnore,
  141. int bitsDamageType,
  142. uint8 *pOutNumOpponentsEffected = NULL,
  143. uint8 *pOutNumTeammatesEffected = NULL )
  144. {
  145. vecSrc.z += 1;// in case grenade is lying on the ground
  146. if ( !pevAttacker )
  147. pevAttacker = pevInflictor;
  148. if ( pOutNumOpponentsEffected )
  149. *pOutNumOpponentsEffected = 0;
  150. if ( pOutNumTeammatesEffected )
  151. *pOutNumTeammatesEffected = 0;
  152. trace_t tr;
  153. float flAdjustedDamage;
  154. variant_t var;
  155. Vector vecEyePos;
  156. float fadeTime, fadeHold;
  157. Vector vForward;
  158. Vector vecLOS;
  159. float flDot;
  160. CBaseEntity *pEntity = NULL;
  161. static float flRadius = 3000;
  162. float falloff = flDamage / flRadius;
  163. //bool bInWater = (UTIL_PointContents( vecSrc, MASK_WATER ) == CONTENTS_WATER);
  164. // iterate on all entities in the vicinity.
  165. while ((pEntity = gEntList.FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL)
  166. {
  167. bool bPlayer = pEntity->IsPlayer();
  168. if( !bPlayer )
  169. continue;
  170. vecEyePos = pEntity->EyePosition();
  171. //// blasts used to not travel into or out of water, users assumed it was a bug. Fix is not to run this check -wills
  172. //if ( bInWater && pEntity->GetWaterLevel() == WL_NotInWater)
  173. // continue;
  174. //if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes)
  175. // continue;
  176. float percentageOfFlash = PercentageOfFlashForPlayer(pEntity, vecSrc, pevInflictor);
  177. if ( percentageOfFlash > 0.0 )
  178. {
  179. if ( pOutNumOpponentsEffected && pEntity->GetTeamNumber() != pevAttacker->GetTeamNumber() )
  180. (*pOutNumOpponentsEffected)++;
  181. if ( pOutNumTeammatesEffected && pEntity->GetTeamNumber() == pevAttacker->GetTeamNumber() )
  182. (*pOutNumTeammatesEffected)++;
  183. // decrease damage for an ent that's farther from the grenade
  184. flAdjustedDamage = flDamage - ( vecSrc - pEntity->EyePosition() ).Length() * falloff;
  185. if ( flAdjustedDamage > 0 )
  186. {
  187. // See if we were facing the flash
  188. AngleVectors( pEntity->EyeAngles(), &vForward );
  189. vecLOS = ( vecSrc - vecEyePos );
  190. float flDistance = vecLOS.Length();
  191. //DebugDrawLine( vecEyePos, vecEyePos + (100.0 * vecLOS), 0, 255, 0, true, 10.0 );
  192. //DebugDrawLine( vecEyePos, vecEyePos + (100.0 * vForward), 0, 0, 255, true, 10.0 );
  193. // Normalize both vectors so the dotproduct is in the range -1.0 <= x <= 1.0
  194. vecLOS.NormalizeInPlace();
  195. flDot = DotProduct (vecLOS, vForward);
  196. float startingAlpha = 255;
  197. // if target is facing the bomb, the effect lasts longer
  198. if( flDot >= 0.6 )
  199. {
  200. // looking at the flashbang
  201. fadeTime = flAdjustedDamage * 2.5f;
  202. fadeHold = flAdjustedDamage * 1.25f;
  203. }
  204. else if( flDot >= 0.3 )
  205. {
  206. // looking to the side
  207. fadeTime = flAdjustedDamage * 1.75f;
  208. fadeHold = flAdjustedDamage * 0.8f;
  209. }
  210. else if( flDot >= -0.2 )
  211. {
  212. // looking to the side
  213. fadeTime = flAdjustedDamage * 1.00f;
  214. fadeHold = flAdjustedDamage * 0.5f;
  215. }
  216. else
  217. {
  218. // facing away
  219. fadeTime = flAdjustedDamage * 0.5f;
  220. fadeHold = flAdjustedDamage * 0.25f;
  221. // startingAlpha = 200;
  222. }
  223. fadeTime *= percentageOfFlash;
  224. fadeHold *= percentageOfFlash;
  225. if ( bPlayer )
  226. {
  227. // blind players and bots
  228. CCSPlayer *player = static_cast< CCSPlayer * >( pEntity );
  229. // [tj] Store who was responsible for the most recent flashbang blinding.
  230. CCSPlayer *attacker = ToCSPlayer (pevAttacker);
  231. if ( attacker && player && player->IsAlive() )
  232. {
  233. player->SetLastFlashbangAttacker(attacker);
  234. // score points/penalties for blinding players
  235. if ( flDot >= 0.0f )
  236. {
  237. if ( attacker->GetTeamNumber() == player->GetTeamNumber() )
  238. CSGameRules()->ScoreBlindFriendly( attacker );
  239. else
  240. CSGameRules()->ScoreBlindEnemy( attacker );
  241. }
  242. }
  243. player->Blind( fadeHold, fadeTime, startingAlpha );
  244. // fire an event when a player has been sufficiently blinded as to not
  245. // be able to perform the training map flashbang range test
  246. if ( CSGameRules()->IsPlayingTraining() && fadeHold > 1.9f )
  247. {
  248. IGameEvent * event = gameeventmanager->CreateEvent( "tr_player_flashbanged" );
  249. if ( event )
  250. {
  251. event->SetInt( "userid", player->GetUserID() );
  252. gameeventmanager->FireEvent( event );
  253. }
  254. }
  255. IGameEvent * event = gameeventmanager->CreateEvent( "player_blind" );
  256. if ( event )
  257. {
  258. event->SetInt( "userid", player->GetUserID() );
  259. event->SetInt( "attacker", attacker ? attacker->GetUserID() : 0 );
  260. event->SetInt( "entityid", pevInflictor ? pevInflictor->entindex() : 0 );
  261. event->SetFloat( "blind_duration", player->m_flFlashDuration );
  262. gameeventmanager->FireEvent( event );
  263. }
  264. // deafen players and bots
  265. player->Deafen( flDistance );
  266. }
  267. }
  268. }
  269. }
  270. CPVSFilter filter(vecSrc);
  271. te->DynamicLight( filter, 0.0, &vecSrc, 255, 255, 255, 2, 400, 0.1, 768 );
  272. }
  273. // --------------------------------------------------------------------------------------------------- //
  274. // CFlashbangProjectile implementation.
  275. // --------------------------------------------------------------------------------------------------- //
  276. CFlashbangProjectile* CFlashbangProjectile::Create(
  277. const Vector &position,
  278. const QAngle &angles,
  279. const Vector &velocity,
  280. const AngularImpulse &angVelocity,
  281. CBaseCombatCharacter *pOwner,
  282. const CCSWeaponInfo& weaponInfo )
  283. {
  284. CFlashbangProjectile *pGrenade = (CFlashbangProjectile*)CBaseEntity::Create( "flashbang_projectile", position, angles, pOwner );
  285. // Set the timer for 1 second less than requested. We're going to issue a SOUND_DANGER
  286. // one second before detonation.
  287. pGrenade->SetAbsVelocity( velocity );
  288. pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity );
  289. pGrenade->SetThrower( pOwner );
  290. pGrenade->m_pWeaponInfo = &weaponInfo;
  291. pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
  292. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  293. pGrenade->SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
  294. return pGrenade;
  295. }
  296. CFlashbangProjectile::CFlashbangProjectile()
  297. {
  298. m_flDamage = 100;
  299. // default timer value for when a player throws it
  300. // can be overridden when spawned from an entity maker
  301. m_flTimeToDetonate = 1.5;
  302. m_numOpponentsHit = m_numTeammatesHit = 0;
  303. }
  304. void CFlashbangProjectile::Spawn()
  305. {
  306. SetModel( GRENADE_MODEL );
  307. SetDetonateTimerLength( m_flTimeToDetonate );
  308. SetTouch( &CBaseGrenade::BounceTouch );
  309. SetThink( &CBaseCSGrenadeProjectile::DangerSoundThink );
  310. SetNextThink( gpGlobals->curtime );
  311. SetGravity( BaseClass::GetGrenadeGravity() );
  312. SetFriction( BaseClass::GetGrenadeFriction() );
  313. SetElasticity( BaseClass::GetGrenadeElasticity() );
  314. m_pWeaponInfo = GetWeaponInfo( WEAPON_FLASHBANG );
  315. BaseClass::Spawn();
  316. SetBodygroupPreset( "thrown" );
  317. }
  318. void CFlashbangProjectile::Precache()
  319. {
  320. PrecacheModel( GRENADE_MODEL );
  321. PrecacheScriptSound( "Flashbang.Explode" );
  322. PrecacheScriptSound( "Flashbang.Bounce" );
  323. BaseClass::Precache();
  324. }
  325. ConVar sv_flashbang_strength( "sv_flashbang_strength", "3.55", FCVAR_REPLICATED, "Flashbang strength", true, 2.0, true, 8.0 );
  326. void CFlashbangProjectile::Detonate()
  327. {
  328. // tell the bots a flashbang grenade has exploded (and record log events)
  329. CCSPlayer *player = ToCSPlayer( GetThrower() );
  330. if ( player )
  331. {
  332. IGameEvent * event = gameeventmanager->CreateEvent( "flashbang_detonate" );
  333. if ( event )
  334. {
  335. event->SetInt( "userid", player->GetUserID() );
  336. event->SetInt( "entityid", this->entindex() );
  337. event->SetFloat( "x", GetAbsOrigin().x );
  338. event->SetFloat( "y", GetAbsOrigin().y );
  339. event->SetFloat( "z", GetAbsOrigin().z );
  340. gameeventmanager->FireEvent( event );
  341. }
  342. }
  343. RadiusFlash ( GetAbsOrigin(), this, GetThrower(), sv_flashbang_strength.GetInt(), CLASS_NONE, DMG_BLAST, &m_numOpponentsHit, &m_numTeammatesHit );
  344. EmitSound( "Flashbang.Explode" );
  345. trace_t tr;
  346. Vector vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 2 );
  347. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -64 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr);
  348. UTIL_DecalTrace( &tr, "Scorch" );
  349. // Because we don't chain to base, tell ogs to record this detonation here
  350. RecordDetonation();
  351. UTIL_Remove( this );
  352. }
  353. //TODO: Let physics handle the sound!
  354. void CFlashbangProjectile::BounceSound( void )
  355. {
  356. EmitSound( "Flashbang.Bounce" );
  357. }
  358. void CFlashbangProjectile::InputSetTimer( inputdata_t &inputdata )
  359. {
  360. m_flTimeToDetonate = inputdata.value.Float();
  361. SetDetonateTimerLength( m_flTimeToDetonate );
  362. }