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

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "molotov_projectile.h"
#include "keyvalues.h"
#include "weapon_csbase.h"
#include "particle_parse.h"
#if defined( CLIENT_DLL )
#include "particle_parse.h"
#include "c_cs_player.h"
#else
#include "cs_player.h"
#include "smoke_trail.h"
#include "Effects/inferno.h"
#include "bot_manager.h"
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
ConVar molotov_throw_detonate_time( "molotov_throw_detonate_time", "2.0", FCVAR_CHEAT | FCVAR_REPLICATED );
#if defined( CLIENT_DLL )
IMPLEMENT_CLIENTCLASS_DT( C_MolotovProjectile, DT_MolotovProjectile, CMolotovProjectile )
RecvPropBool( RECVINFO(m_bIsIncGrenade) ),
END_RECV_TABLE()
//--------------------------------------------------------------------------------------------------------
void C_MolotovProjectile::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
{
if ( FStrEq( pszParticleName, "weapon_molotov_thrown" ) || FStrEq( pszParticleName, "incgrenade_thrown_trail" ) )
{
m_molotovParticleEffect = pNewParticleEffect;
}
}
//--------------------------------------------------------------------------------------------------------
void C_MolotovProjectile::OnParticleEffectDeleted( CNewParticleEffect *pParticleEffect )
{
if ( m_molotovParticleEffect == pParticleEffect )
{
m_molotovParticleEffect = NULL;
}
}
//--------------------------------------------------------------------------------------------------------
bool C_MolotovProjectile::Simulate( void )
{
if ( !m_molotovParticleEffect.IsValid() )
{
if ( m_bIsIncGrenade )
// todo: make this come from an attachment
DispatchParticleEffect( "incgrenade_thrown_trail", PATTACH_POINT_FOLLOW, this, "trail" );
else
DispatchParticleEffect( "weapon_molotov_thrown", PATTACH_POINT_FOLLOW, this, "Wick" );
}
else
{
m_molotovParticleEffect->SetSortOrigin( GetAbsOrigin() );
m_molotovParticleEffect->SetNeedsBBoxUpdate( true );
}
BaseClass::Simulate();
return true;
}
#else // GAME_DLL
ConVar weapon_molotov_maxdetonateslope(
"weapon_molotov_maxdetonateslope",
"30.0",
FCVAR_REPLICATED,
"Maximum angle of slope on which the molotov will detonate",
true, 0.0,
true, 90.0 );
#define MOLOTOV_MODEL "models/Weapons/w_eq_molotov_dropped.mdl"
#define INCGREN_MODEL "models/Weapons/w_eq_incendiarygrenade_dropped.mdl"
LINK_ENTITY_TO_CLASS( molotov_projectile, CMolotovProjectile );
PRECACHE_REGISTER( molotov_projectile );
BEGIN_DATADESC( CMolotovProjectile )
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "InitializeSpawnFromWorld", InitializeSpawnFromWorld ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CMolotovProjectile, DT_MolotovProjectile )
SendPropBool( SENDINFO(m_bIsIncGrenade) ),
END_SEND_TABLE()
CMolotovProjectile *CMolotovProjectile::Create( const Vector &position, const QAngle &angles,
const Vector &velocity, const AngularImpulse &angVelocity,
CBaseCombatCharacter *owner, const CCSWeaponInfo& weaponInfo )
{
CMolotovProjectile *molotov = (CMolotovProjectile *)CBaseEntity::Create( "molotov_projectile", position, angles, owner );
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 );
molotov->SetDetonateTimerLength( molotov_throw_detonate_time.GetFloat() );
molotov->SetAbsVelocity( velocity );
molotov->SetupInitialTransmittedGrenadeVelocity( velocity );
molotov->SetThrower( owner );
molotov->m_pWeaponInfo = &weaponInfo;
molotov->SetIsIncGrenade( weaponInfo.m_weaponId == WEAPON_INCGRENADE );
if ( molotov->m_bIsIncGrenade )
molotov->SetModel( INCGREN_MODEL );
else
molotov->SetModel( MOLOTOV_MODEL );
molotov->SetGravity( BaseClass::GetGrenadeGravity() );
molotov->SetFriction( BaseClass::GetGrenadeFriction() );
molotov->SetElasticity( BaseClass::GetGrenadeElasticity() );
molotov->SetTouch( &CMolotovProjectile::BounceTouch );
molotov->SetThink( &CMolotovProjectile::DetonateThink );
molotov->SetNextThink( gpGlobals->curtime + 2.0f );
molotov->m_flDamage = 200.0f;
molotov->m_DmgRadius = 300.0f;
molotov->ChangeTeam( owner->GetTeamNumber() );
molotov->ApplyLocalAngularVelocityImpulse( angVelocity );
// make NPCs afaid of it while in the air
molotov->SetThink( &CMolotovProjectile::DangerSoundThink );
molotov->SetNextThink( gpGlobals->curtime );
molotov->EmitSound( "Molotov.Throw" );
molotov->EmitSound( "Molotov.Loop" );
molotov->SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
// we have to reset these here because we set the model late and it resets the collision
Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
molotov->SetSize( min, max );
if ( molotov->CollisionProp() )
molotov->CollisionProp()->SetCollisionBounds( min, max );
return molotov;
}
void CMolotovProjectile::InitializeSpawnFromWorld( inputdata_t &inputdata )
{
SetDetonateTimerLength( molotov_throw_detonate_time.GetFloat() );
SetGravity( GetGrenadeGravity() );
SetFriction( GetGrenadeFriction() );
SetElasticity( GetGrenadeElasticity() );
SetIsIncGrenade( false );
SetTouch( &CMolotovProjectile::BounceTouch );
SetThink( &CMolotovProjectile::DetonateThink );
SetNextThink( gpGlobals->curtime + 2.0f );
//pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
ApplyLocalAngularVelocityImpulse( AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ) );
// make NPCs afaid of it while in the air
SetThink( &CMolotovProjectile::DangerSoundThink );
SetNextThink( gpGlobals->curtime );
EmitSound( "Molotov.Throw" );
EmitSound( "Molotov.Loop" );
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
// we have to reset these here because we set the model late and it resets the collision
Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
SetSize( min, max );
if ( CollisionProp() )
CollisionProp()->SetCollisionBounds( min, max );
m_pWeaponInfo = GetWeaponInfo( WEAPON_MOLOTOV );
}
void CMolotovProjectile::Spawn( void )
{
m_stillTimer.Invalidate();
m_throwDetTimer.Invalidate();
BaseClass::Spawn();
if ( this->m_bIsIncGrenade )
{
SetModel( INCGREN_MODEL );
SetBodygroupPreset( "thrown" );
}
else
{
SetModel( MOLOTOV_MODEL );
}
}
void CMolotovProjectile::Precache( void )
{
PrecacheModel( MOLOTOV_MODEL );
PrecacheModel( INCGREN_MODEL );
PrecacheScriptSound( "Molotov.Throw" );
PrecacheScriptSound( "Molotov.Loop" );
PrecacheParticleSystem( "weapon_molotov_thrown" );
PrecacheParticleSystem( "weapon_molotov_held" );
PrecacheParticleSystem( "incgrenade_thrown_trail" );
BaseClass::Precache();
}
void CMolotovProjectile::BounceTouch( CBaseEntity *other )
{
if ( other->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) )
return;
// don't hit the guy that launched this grenade
if ( other == GetThrower() )
return;
if ( FClassnameIs( other, "func_breakable" ) )
{
return;
}
if ( FClassnameIs( other, "func_breakable_surf" ) )
{
return;
}
// don't detonate on ladders
if ( FClassnameIs( other, "func_ladder" ) )
{
return;
}
// Deal car alarms direct damage to set them off - flames won't do so
if ( FClassnameIs( other, "prop_car_alarm" ) || FClassnameIs( other, "prop_car_glass" ) )
{
CTakeDamageInfo info( this, GetThrower(), 10, DMG_GENERIC );
other->OnTakeDamage( info );
}
const trace_t &hitTrace = GetTouchTrace();
if ( hitTrace.m_pEnt && hitTrace.m_pEnt->MyCombatCharacterPointer() )
{
// don't break if we hit an actor - wait until we hit the environment
return;
}
else
{
// only detonate on surfaces less steep than this
const float kMinCos = cosf(DEG2RAD(weapon_molotov_maxdetonateslope.GetFloat()));
if ( hitTrace.plane.normal.z >= kMinCos )
{
Detonate();
}
}
}
void CMolotovProjectile::BounceSound( void )
{
if ( m_bIsIncGrenade )
EmitSound( "IncGrenade.Bounce" );
else
EmitSound( "GlassBottle.ImpactHard" );
}
void CMolotovProjectile::DetonateThink( void )
{
// if( gpGlobals->curtime > m_flDetonateTime )
// {
// Detonate();
// return;
// }
if ( GetAbsVelocity().IsLengthGreaterThan( 5.0f ) )
{
m_stillTimer.Invalidate();
}
else if ( !m_stillTimer.HasStarted() )
{
m_stillTimer.Start();
}
const float StillDetonateTime = 0.5f;
if ( m_stillTimer.HasStarted() && m_stillTimer.GetElapsedTime() > StillDetonateTime )
{
Detonate();
}
else
{
SetNextThink( gpGlobals->curtime + 0.1f );
}
TheBots->SetGrenadeRadius( this, 0.0f );
}
void CMolotovProjectile::Detonate( void )
{
//BaseClass::Detonate();
const trace_t &hitTrace = GetTouchTrace();
if ( hitTrace.surface.flags & SURF_SKY )
return;
// tell the bots an HE grenade has exploded
CCSPlayer *player = ToCSPlayer(GetThrower());
if ( player )
{
IGameEvent * event = gameeventmanager->CreateEvent( "molotov_detonate" );
if ( event )
{
event->SetInt( "userid", player->GetUserID() );
event->SetFloat( "x", GetAbsOrigin().x );
event->SetFloat( "y", GetAbsOrigin().y );
event->SetFloat( "z", GetAbsOrigin().z );
gameeventmanager->FireEvent( event );
}
}
Vector burnPos, splashNormal;
if ( hitTrace.DidHitWorld() )
{
// hit the world, just explode at that position
burnPos = hitTrace.endpos;
splashNormal = hitTrace.plane.normal;
}
else
{
// exploded in the air, or hit an object or player.
// find the world normal under them (if close enough) and explode there
trace_t tr;
UTIL_TraceLine( GetAbsOrigin() + Vector( 0, 0, 10 ), GetAbsOrigin() + Vector( 0, 0, -128.0f ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction == 1 )
{
// Too high, just play explosion effect and don't start a fire
if ( m_bIsIncGrenade )
EmitSound( "Inferno.Start_IncGrenade" );
else
EmitSound( "Inferno.Start" );
TheBots->SetGrenadeRadius( this, MolotovGrenadeRadius );
StopSound( "Molotov.Loop" );
DispatchParticleEffect( "explosion_molotov_air", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
Vector vecAbsOrigin = GetAbsOrigin();
CPASFilter filter( vecAbsOrigin );
te->Explosion( filter, -1.0, // don't apply cl_interp delay
vecAbsOrigin,
0,
32,
25,
TE_EXPLFLAG_NOSOUND | TE_EXPLFLAG_NOFIREBALL | TE_EXPLFLAG_NOPARTICLES,
152,
50 );
UTIL_Remove( this );
return;
}
else if( tr.surface.flags & SURF_SKY )
{
// just bounce
return;
}
// otherwise explode normally
burnPos = tr.endpos;
splashNormal = tr.plane.normal;
}
TheBots->SetGrenadeRadius( this, MolotovGrenadeRadius );
CInferno *inferno = (CInferno *)CBaseEntity::Create( "inferno", burnPos, QAngle( 0, 0, 0 ), GetThrower() );
Vector vBurnDir = m_vInitialVelocity;
vBurnDir.NormalizeInPlace();
vBurnDir *= GetAbsVelocity().Length();
inferno->SetSourceWeaponInfo( m_pWeaponInfo );
if ( m_bIsIncGrenade )
inferno->SetInfernoType( INFERNO_TYPE_INCGREN_FIRE );
else
inferno->SetInfernoType( INFERNO_TYPE_FIRE );
inferno->StartBurning( burnPos, splashNormal, vBurnDir );
if ( inferno->WasCreatedInSmoke() )
{
// we extinguished ourselves with this throw.
m_unOGSExtraFlags |= GRENADE_EXTINGUISHED_INFERNO;
}
// We override the base class detonate and don't chain down--
RecordDetonation();
StopSound( "Molotov.Loop" );
UTIL_Remove( this );
}
#endif