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.

419 lines
11 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "molotov_projectile.h"
  8. #include "keyvalues.h"
  9. #include "weapon_csbase.h"
  10. #include "particle_parse.h"
  11. #if defined( CLIENT_DLL )
  12. #include "particle_parse.h"
  13. #include "c_cs_player.h"
  14. #else
  15. #include "cs_player.h"
  16. #include "smoke_trail.h"
  17. #include "Effects/inferno.h"
  18. #include "bot_manager.h"
  19. #endif
  20. // NOTE: This has to be the last file included!
  21. #include "tier0/memdbgon.h"
  22. ConVar molotov_throw_detonate_time( "molotov_throw_detonate_time", "2.0", FCVAR_CHEAT | FCVAR_REPLICATED );
  23. #if defined( CLIENT_DLL )
  24. IMPLEMENT_CLIENTCLASS_DT( C_MolotovProjectile, DT_MolotovProjectile, CMolotovProjectile )
  25. RecvPropBool( RECVINFO(m_bIsIncGrenade) ),
  26. END_RECV_TABLE()
  27. //--------------------------------------------------------------------------------------------------------
  28. void C_MolotovProjectile::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
  29. {
  30. if ( FStrEq( pszParticleName, "weapon_molotov_thrown" ) || FStrEq( pszParticleName, "incgrenade_thrown_trail" ) )
  31. {
  32. m_molotovParticleEffect = pNewParticleEffect;
  33. }
  34. }
  35. //--------------------------------------------------------------------------------------------------------
  36. void C_MolotovProjectile::OnParticleEffectDeleted( CNewParticleEffect *pParticleEffect )
  37. {
  38. if ( m_molotovParticleEffect == pParticleEffect )
  39. {
  40. m_molotovParticleEffect = NULL;
  41. }
  42. }
  43. //--------------------------------------------------------------------------------------------------------
  44. bool C_MolotovProjectile::Simulate( void )
  45. {
  46. if ( !m_molotovParticleEffect.IsValid() )
  47. {
  48. if ( m_bIsIncGrenade )
  49. // todo: make this come from an attachment
  50. DispatchParticleEffect( "incgrenade_thrown_trail", PATTACH_POINT_FOLLOW, this, "trail" );
  51. else
  52. DispatchParticleEffect( "weapon_molotov_thrown", PATTACH_POINT_FOLLOW, this, "Wick" );
  53. }
  54. else
  55. {
  56. m_molotovParticleEffect->SetSortOrigin( GetAbsOrigin() );
  57. m_molotovParticleEffect->SetNeedsBBoxUpdate( true );
  58. }
  59. BaseClass::Simulate();
  60. return true;
  61. }
  62. #else // GAME_DLL
  63. ConVar weapon_molotov_maxdetonateslope(
  64. "weapon_molotov_maxdetonateslope",
  65. "30.0",
  66. FCVAR_REPLICATED,
  67. "Maximum angle of slope on which the molotov will detonate",
  68. true, 0.0,
  69. true, 90.0 );
  70. #define MOLOTOV_MODEL "models/Weapons/w_eq_molotov_dropped.mdl"
  71. #define INCGREN_MODEL "models/Weapons/w_eq_incendiarygrenade_dropped.mdl"
  72. LINK_ENTITY_TO_CLASS( molotov_projectile, CMolotovProjectile );
  73. PRECACHE_REGISTER( molotov_projectile );
  74. BEGIN_DATADESC( CMolotovProjectile )
  75. // Inputs
  76. DEFINE_INPUTFUNC( FIELD_VOID, "InitializeSpawnFromWorld", InitializeSpawnFromWorld ),
  77. END_DATADESC()
  78. IMPLEMENT_SERVERCLASS_ST( CMolotovProjectile, DT_MolotovProjectile )
  79. SendPropBool( SENDINFO(m_bIsIncGrenade) ),
  80. END_SEND_TABLE()
  81. CMolotovProjectile *CMolotovProjectile::Create( const Vector &position, const QAngle &angles,
  82. const Vector &velocity, const AngularImpulse &angVelocity,
  83. CBaseCombatCharacter *owner, const CCSWeaponInfo& weaponInfo )
  84. {
  85. CMolotovProjectile *molotov = (CMolotovProjectile *)CBaseEntity::Create( "molotov_projectile", position, angles, owner );
  86. UTIL_LogPrintf( "Molotov projectile spawned at %f %f %f, velocity %f %f %f\n", position.x, position.y, position.z, velocity.x, velocity.y, velocity.z );
  87. molotov->SetDetonateTimerLength( molotov_throw_detonate_time.GetFloat() );
  88. molotov->SetAbsVelocity( velocity );
  89. molotov->SetupInitialTransmittedGrenadeVelocity( velocity );
  90. molotov->SetThrower( owner );
  91. molotov->m_pWeaponInfo = &weaponInfo;
  92. molotov->SetIsIncGrenade( weaponInfo.m_weaponId == WEAPON_INCGRENADE );
  93. if ( molotov->m_bIsIncGrenade )
  94. molotov->SetModel( INCGREN_MODEL );
  95. else
  96. molotov->SetModel( MOLOTOV_MODEL );
  97. molotov->SetGravity( BaseClass::GetGrenadeGravity() );
  98. molotov->SetFriction( BaseClass::GetGrenadeFriction() );
  99. molotov->SetElasticity( BaseClass::GetGrenadeElasticity() );
  100. molotov->SetTouch( &CMolotovProjectile::BounceTouch );
  101. molotov->SetThink( &CMolotovProjectile::DetonateThink );
  102. molotov->SetNextThink( gpGlobals->curtime + 2.0f );
  103. molotov->m_flDamage = 200.0f;
  104. molotov->m_DmgRadius = 300.0f;
  105. molotov->ChangeTeam( owner->GetTeamNumber() );
  106. molotov->ApplyLocalAngularVelocityImpulse( angVelocity );
  107. // make NPCs afaid of it while in the air
  108. molotov->SetThink( &CMolotovProjectile::DangerSoundThink );
  109. molotov->SetNextThink( gpGlobals->curtime );
  110. molotov->EmitSound( "Molotov.Throw" );
  111. molotov->EmitSound( "Molotov.Loop" );
  112. molotov->SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
  113. // we have to reset these here because we set the model late and it resets the collision
  114. Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
  115. Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
  116. molotov->SetSize( min, max );
  117. if ( molotov->CollisionProp() )
  118. molotov->CollisionProp()->SetCollisionBounds( min, max );
  119. return molotov;
  120. }
  121. void CMolotovProjectile::InitializeSpawnFromWorld( inputdata_t &inputdata )
  122. {
  123. SetDetonateTimerLength( molotov_throw_detonate_time.GetFloat() );
  124. SetGravity( GetGrenadeGravity() );
  125. SetFriction( GetGrenadeFriction() );
  126. SetElasticity( GetGrenadeElasticity() );
  127. SetIsIncGrenade( false );
  128. SetTouch( &CMolotovProjectile::BounceTouch );
  129. SetThink( &CMolotovProjectile::DetonateThink );
  130. SetNextThink( gpGlobals->curtime + 2.0f );
  131. //pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
  132. ApplyLocalAngularVelocityImpulse( AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ) );
  133. // make NPCs afaid of it while in the air
  134. SetThink( &CMolotovProjectile::DangerSoundThink );
  135. SetNextThink( gpGlobals->curtime );
  136. EmitSound( "Molotov.Throw" );
  137. EmitSound( "Molotov.Loop" );
  138. SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
  139. // we have to reset these here because we set the model late and it resets the collision
  140. Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
  141. Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
  142. SetSize( min, max );
  143. if ( CollisionProp() )
  144. CollisionProp()->SetCollisionBounds( min, max );
  145. m_pWeaponInfo = GetWeaponInfo( WEAPON_MOLOTOV );
  146. }
  147. void CMolotovProjectile::Spawn( void )
  148. {
  149. m_stillTimer.Invalidate();
  150. m_throwDetTimer.Invalidate();
  151. BaseClass::Spawn();
  152. if ( this->m_bIsIncGrenade )
  153. {
  154. SetModel( INCGREN_MODEL );
  155. SetBodygroupPreset( "thrown" );
  156. }
  157. else
  158. {
  159. SetModel( MOLOTOV_MODEL );
  160. }
  161. }
  162. void CMolotovProjectile::Precache( void )
  163. {
  164. PrecacheModel( MOLOTOV_MODEL );
  165. PrecacheModel( INCGREN_MODEL );
  166. PrecacheScriptSound( "Molotov.Throw" );
  167. PrecacheScriptSound( "Molotov.Loop" );
  168. PrecacheParticleSystem( "weapon_molotov_thrown" );
  169. PrecacheParticleSystem( "weapon_molotov_held" );
  170. PrecacheParticleSystem( "incgrenade_thrown_trail" );
  171. BaseClass::Precache();
  172. }
  173. void CMolotovProjectile::BounceTouch( CBaseEntity *other )
  174. {
  175. if ( other->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) )
  176. return;
  177. // don't hit the guy that launched this grenade
  178. if ( other == GetThrower() )
  179. return;
  180. if ( FClassnameIs( other, "func_breakable" ) )
  181. {
  182. return;
  183. }
  184. if ( FClassnameIs( other, "func_breakable_surf" ) )
  185. {
  186. return;
  187. }
  188. // don't detonate on ladders
  189. if ( FClassnameIs( other, "func_ladder" ) )
  190. {
  191. return;
  192. }
  193. // Deal car alarms direct damage to set them off - flames won't do so
  194. if ( FClassnameIs( other, "prop_car_alarm" ) || FClassnameIs( other, "prop_car_glass" ) )
  195. {
  196. CTakeDamageInfo info( this, GetThrower(), 10, DMG_GENERIC );
  197. other->OnTakeDamage( info );
  198. }
  199. const trace_t &hitTrace = GetTouchTrace();
  200. if ( hitTrace.m_pEnt && hitTrace.m_pEnt->MyCombatCharacterPointer() )
  201. {
  202. // don't break if we hit an actor - wait until we hit the environment
  203. return;
  204. }
  205. else
  206. {
  207. // only detonate on surfaces less steep than this
  208. const float kMinCos = cosf(DEG2RAD(weapon_molotov_maxdetonateslope.GetFloat()));
  209. if ( hitTrace.plane.normal.z >= kMinCos )
  210. {
  211. Detonate();
  212. }
  213. }
  214. }
  215. void CMolotovProjectile::BounceSound( void )
  216. {
  217. if ( m_bIsIncGrenade )
  218. EmitSound( "IncGrenade.Bounce" );
  219. else
  220. EmitSound( "GlassBottle.ImpactHard" );
  221. }
  222. void CMolotovProjectile::DetonateThink( void )
  223. {
  224. // if( gpGlobals->curtime > m_flDetonateTime )
  225. // {
  226. // Detonate();
  227. // return;
  228. // }
  229. if ( GetAbsVelocity().IsLengthGreaterThan( 5.0f ) )
  230. {
  231. m_stillTimer.Invalidate();
  232. }
  233. else if ( !m_stillTimer.HasStarted() )
  234. {
  235. m_stillTimer.Start();
  236. }
  237. const float StillDetonateTime = 0.5f;
  238. if ( m_stillTimer.HasStarted() && m_stillTimer.GetElapsedTime() > StillDetonateTime )
  239. {
  240. Detonate();
  241. }
  242. else
  243. {
  244. SetNextThink( gpGlobals->curtime + 0.1f );
  245. }
  246. TheBots->SetGrenadeRadius( this, 0.0f );
  247. }
  248. void CMolotovProjectile::Detonate( void )
  249. {
  250. //BaseClass::Detonate();
  251. const trace_t &hitTrace = GetTouchTrace();
  252. if ( hitTrace.surface.flags & SURF_SKY )
  253. return;
  254. // tell the bots an HE grenade has exploded
  255. CCSPlayer *player = ToCSPlayer(GetThrower());
  256. if ( player )
  257. {
  258. IGameEvent * event = gameeventmanager->CreateEvent( "molotov_detonate" );
  259. if ( event )
  260. {
  261. event->SetInt( "userid", player->GetUserID() );
  262. event->SetFloat( "x", GetAbsOrigin().x );
  263. event->SetFloat( "y", GetAbsOrigin().y );
  264. event->SetFloat( "z", GetAbsOrigin().z );
  265. gameeventmanager->FireEvent( event );
  266. }
  267. }
  268. Vector burnPos, splashNormal;
  269. if ( hitTrace.DidHitWorld() )
  270. {
  271. // hit the world, just explode at that position
  272. burnPos = hitTrace.endpos;
  273. splashNormal = hitTrace.plane.normal;
  274. }
  275. else
  276. {
  277. // exploded in the air, or hit an object or player.
  278. // find the world normal under them (if close enough) and explode there
  279. trace_t tr;
  280. UTIL_TraceLine( GetAbsOrigin() + Vector( 0, 0, 10 ), GetAbsOrigin() + Vector( 0, 0, -128.0f ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  281. if ( tr.fraction == 1 )
  282. {
  283. // Too high, just play explosion effect and don't start a fire
  284. if ( m_bIsIncGrenade )
  285. EmitSound( "Inferno.Start_IncGrenade" );
  286. else
  287. EmitSound( "Inferno.Start" );
  288. TheBots->SetGrenadeRadius( this, MolotovGrenadeRadius );
  289. StopSound( "Molotov.Loop" );
  290. DispatchParticleEffect( "explosion_molotov_air", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
  291. Vector vecAbsOrigin = GetAbsOrigin();
  292. CPASFilter filter( vecAbsOrigin );
  293. te->Explosion( filter, -1.0, // don't apply cl_interp delay
  294. vecAbsOrigin,
  295. 0,
  296. 32,
  297. 25,
  298. TE_EXPLFLAG_NOSOUND | TE_EXPLFLAG_NOFIREBALL | TE_EXPLFLAG_NOPARTICLES,
  299. 152,
  300. 50 );
  301. UTIL_Remove( this );
  302. return;
  303. }
  304. else if( tr.surface.flags & SURF_SKY )
  305. {
  306. // just bounce
  307. return;
  308. }
  309. // otherwise explode normally
  310. burnPos = tr.endpos;
  311. splashNormal = tr.plane.normal;
  312. }
  313. TheBots->SetGrenadeRadius( this, MolotovGrenadeRadius );
  314. CInferno *inferno = (CInferno *)CBaseEntity::Create( "inferno", burnPos, QAngle( 0, 0, 0 ), GetThrower() );
  315. Vector vBurnDir = m_vInitialVelocity;
  316. vBurnDir.NormalizeInPlace();
  317. vBurnDir *= GetAbsVelocity().Length();
  318. inferno->SetSourceWeaponInfo( m_pWeaponInfo );
  319. if ( m_bIsIncGrenade )
  320. inferno->SetInfernoType( INFERNO_TYPE_INCGREN_FIRE );
  321. else
  322. inferno->SetInfernoType( INFERNO_TYPE_FIRE );
  323. inferno->StartBurning( burnPos, splashNormal, vBurnDir );
  324. if ( inferno->WasCreatedInSmoke() )
  325. {
  326. // we extinguished ourselves with this throw.
  327. m_unOGSExtraFlags |= GRENADE_EXTINGUISHED_INFERNO;
  328. }
  329. // We override the base class detonate and don't chain down--
  330. RecordDetonation();
  331. StopSound( "Molotov.Loop" );
  332. UTIL_Remove( this );
  333. }
  334. #endif