|
|
//========= 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
|