summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_grenade_pipebomb.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weapon_grenade_pipebomb.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_weapon_grenade_pipebomb.cpp')
-rw-r--r--game/shared/tf/tf_weapon_grenade_pipebomb.cpp1402
1 files changed, 1402 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_grenade_pipebomb.cpp b/game/shared/tf/tf_weapon_grenade_pipebomb.cpp
new file mode 100644
index 0000000..a5ed406
--- /dev/null
+++ b/game/shared/tf/tf_weapon_grenade_pipebomb.cpp
@@ -0,0 +1,1402 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: TF Pipebomb Grenade.
+//
+//=============================================================================//
+#include "cbase.h"
+#include "tf_weaponbase.h"
+#include "tf_gamerules.h"
+#include "npcevent.h"
+#include "engine/IEngineSound.h"
+#include "tf_weapon_grenade_pipebomb.h"
+#include "tf_weapon_pipebomblauncher.h"
+#include "tf_weapon_grenadelauncher.h"
+
+// Client specific.
+#ifdef CLIENT_DLL
+#include "c_tf_player.h"
+#include "IEffects.h"
+#include "materialsystem/imaterialvar.h"
+#include "functionproxy.h"
+// Server specific.
+#else
+#include "tf_player.h"
+#include "items.h"
+#include "tf_weaponbase_grenadeproj.h"
+#include "soundent.h"
+#include "KeyValues.h"
+#include "IEffects.h"
+#include "props.h"
+#include "func_respawnroom.h"
+#include "tf_ammo_pack.h"
+#include "takedamageinfo.h"
+#include "tf_team.h"
+#include "physics_collisionevent.h"
+#ifdef TF_RAID_MODE
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#endif // TF_RAID_MODE
+#include "tf_weapon_medigun.h"
+#endif
+
+#define TF_WEAPON_PIPEBOMB_TIMER 3.0f //Seconds
+
+#define TF_WEAPON_PIPEBOMB_GRAVITY 0.5f
+#define TF_WEAPON_PIPEBOMB_FRICTION 0.8f
+#define TF_WEAPON_PIPEBOMB_ELASTICITY 0.45f
+
+#define TF_WEAPON_PIPEBOMB_TIMER_DMG_REDUCTION 0.6
+
+extern ConVar tf_grenadelauncher_max_chargetime;
+ConVar tf_grenadelauncher_chargescale( "tf_grenadelauncher_chargescale", "1.0", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
+ConVar tf_grenadelauncher_livetime( "tf_grenadelauncher_livetime", "0.8", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
+extern ConVar tf_sticky_radius_ramp_time;
+extern ConVar tf_sticky_airdet_radius;
+
+#ifndef CLIENT_DLL
+
+ConVar tf_grenadelauncher_min_contact_speed( "tf_grenadelauncher_min_contact_speed", "100", FCVAR_DEVELOPMENTONLY );
+extern ConVar tf_obj_gib_velocity_min;
+extern ConVar tf_obj_gib_velocity_max;
+extern ConVar tf_obj_gib_maxspeed;
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFGrenadePipebombProjectile, DT_TFProjectile_Pipebomb )
+
+BEGIN_NETWORK_TABLE( CTFGrenadePipebombProjectile, DT_TFProjectile_Pipebomb )
+#ifdef CLIENT_DLL
+RecvPropInt( RECVINFO( m_bTouched ) ),
+RecvPropInt( RECVINFO( m_iType ) ),
+RecvPropEHandle( RECVINFO( m_hLauncher ) ),
+RecvPropBool( RECVINFO( m_bDefensiveBomb ) ),
+#else
+SendPropBool( SENDINFO( m_bTouched ) ),
+SendPropInt( SENDINFO( m_iType ), 3 ),
+SendPropEHandle( SENDINFO( m_hLauncher ) ),
+SendPropBool( SENDINFO( m_bDefensiveBomb ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef GAME_DLL
+static string_t s_iszTrainName;
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+CTFGrenadePipebombProjectile::CTFGrenadePipebombProjectile()
+{
+ m_bTouched = false;
+ m_flChargeTime = 0.0f;
+ m_bDetonateOnPulse = false;
+#ifdef GAME_DLL
+ s_iszTrainName = AllocPooledString( "models/props_vehicles/train_enginecar.mdl" );
+ m_flDeflectedTime = 0.0f;
+ m_bWallShatter = false;
+ m_bDefensiveBomb = false;
+ m_bSendPlayerDestroyedEvent = true;
+ m_bCanTakeDamage = true;
+#else
+ pEffectTrail = NULL;
+ pEffectCrit = NULL;
+ m_iCachedDeflect = 0;
+ m_bHighlight = false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+CTFGrenadePipebombProjectile::~CTFGrenadePipebombProjectile()
+{
+#ifdef CLIENT_DLL
+
+ if ( pEffectTrail )
+ {
+ ParticleProp()->StopEmission( pEffectTrail );
+ }
+ if ( pEffectCrit )
+ {
+ ParticleProp()->StopEmission( pEffectCrit );
+ }
+ if ( m_pGlowEffect )
+ {
+ delete m_pGlowEffect;
+ m_pGlowEffect = NULL;
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFGrenadePipebombProjectile::GetWeaponID( void ) const
+{
+ if ( m_iType == TF_GL_MODE_CANNONBALL )
+ {
+ return TF_WEAPON_CANNON;
+ }
+
+ return ( HasStickyEffects() ? TF_WEAPON_GRENADE_PIPEBOMB : TF_WEAPON_GRENADE_DEMOMAN );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFGrenadePipebombProjectile::GetDamageType( void )
+{
+ int iDmgType = BaseClass::GetDamageType();
+
+ // If we're a pipebomb, we do distance based damage falloff for just the first few seconds of our life
+ if ( m_iType == TF_GL_MODE_REMOTE_DETONATE )
+ {
+ if ( gpGlobals->curtime - m_flCreationTime < 5.0 )
+ {
+ iDmgType |= DMG_USEDISTANCEMOD;
+ }
+ }
+
+ return iDmgType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGrenadePipebombProjectile::ShouldMiniCritOnReflect() const
+{
+ return GetType() == TF_GL_MODE_REGULAR;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::UpdateOnRemove( void )
+{
+ // Tell our launcher that we were removed
+ CTFPipebombLauncher *pLauncher = dynamic_cast<CTFPipebombLauncher*>( m_hLauncher.Get() );
+
+ if ( pLauncher )
+ {
+ pLauncher->DeathNotice( this );
+ }
+
+ BaseClass::UpdateOnRemove();
+}
+
+#ifdef CLIENT_DLL
+//=============================================================================
+//
+// TF Pipebomb Grenade Projectile functions (Client specific).
+//
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CTFGrenadePipebombProjectile::GetTrailParticleName( void )
+{
+ int iTeamNumber = GetTeamNumber();
+
+ if ( GetDeflected() && m_iType != TF_GL_MODE_REMOTE_DETONATE )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetDeflectOwner() );
+
+ if ( pOwner )
+ {
+ iTeamNumber = pOwner->GetTeamNumber();
+ }
+ }
+
+ if ( HasStickyEffects() )
+ {
+ if ( iTeamNumber == TF_TEAM_BLUE )
+ {
+ return "stickybombtrail_blue";
+ }
+ else
+ {
+ return "stickybombtrail_red";
+ }
+ }
+ else
+ {
+ if ( iTeamNumber == TF_TEAM_BLUE )
+ {
+ return "pipebombtrail_blue";
+ }
+ else
+ {
+ return "pipebombtrail_red";
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::OnDataChanged(DataUpdateType_t updateType)
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ m_flCreationTime = gpGlobals->curtime;
+ m_bPulsed = false;
+
+ CTFPipebombLauncher *pLauncher = dynamic_cast<CTFPipebombLauncher*>( m_hLauncher.Get() );
+
+ if ( pLauncher )
+ {
+ pLauncher->AddPipeBomb( this );
+ }
+
+ if ( m_bDefensiveBomb && C_BasePlayer::GetLocalPlayer() == GetThrower() )
+ {
+ if ( GetTeamNumber() == TF_TEAM_RED )
+ {
+ m_pGlowEffect = new CGlowObject( this, Vector( 150, 0, 0 ), 1.0, true );
+ }
+ else
+ {
+ m_pGlowEffect = new CGlowObject( this, Vector( 0, 0, 150 ), 1.0, true );
+ }
+ }
+
+ CreateTrailParticles();
+ }
+ else if ( m_bTouched )
+ {
+ //ParticleProp()->StopEmission();
+ }
+
+ if ( m_iCachedDeflect != GetDeflected() )
+ {
+ CreateTrailParticles();
+ }
+
+ m_iCachedDeflect = GetDeflected();
+}
+
+void CTFGrenadePipebombProjectile::CreateTrailParticles( void )
+{
+ if ( pEffectTrail )
+ {
+ ParticleProp()->StopEmission( pEffectTrail );
+ }
+
+ if ( pEffectCrit )
+ {
+ ParticleProp()->StopEmission( pEffectCrit );
+ }
+
+ pEffectTrail = ParticleProp()->Create( GetTrailParticleName(), PATTACH_ABSORIGIN_FOLLOW );
+
+ int iTeamNumber = GetTeamNumber();
+
+ if ( GetDeflected() && m_iType != TF_GL_MODE_REMOTE_DETONATE )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetDeflectOwner() );
+
+ if ( pOwner )
+ {
+ iTeamNumber = pOwner->GetTeamNumber();
+ }
+ }
+
+ if ( m_bCritical )
+ {
+ switch( iTeamNumber )
+ {
+ case TF_TEAM_BLUE:
+
+ if ( HasStickyEffects() )
+ {
+ pEffectCrit = ParticleProp()->Create( "critical_grenade_blue", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ else
+ {
+ pEffectCrit = ParticleProp()->Create( "critical_pipe_blue", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ break;
+ case TF_TEAM_RED:
+
+ if ( HasStickyEffects() )
+ {
+ pEffectCrit = ParticleProp()->Create( "critical_grenade_red", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ else
+ {
+ pEffectCrit = ParticleProp()->Create( "critical_pipe_red", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+
+extern ConVar tf_grenadelauncher_livetime;
+
+void CTFGrenadePipebombProjectile::Simulate( void )
+{
+ BaseClass::Simulate();
+
+ if ( !HasStickyEffects() )
+ return;
+
+ if ( m_bPulsed == false )
+ {
+ if ( (gpGlobals->curtime - m_flCreationTime) >= GetLiveTime() )
+ {
+ if ( GetTeamNumber() == TF_TEAM_RED )
+ {
+ ParticleProp()->Create( "stickybomb_pulse_red", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ else
+ {
+ ParticleProp()->Create( "stickybomb_pulse_blue", PATTACH_ABSORIGIN_FOLLOW );
+ }
+
+ m_bPulsed = true;
+
+ if ( m_bDetonateOnPulse )
+ {
+ Detonate();
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose: Don't draw if we haven't yet gone past our original spawn point
+// Input : flags -
+//-----------------------------------------------------------------------------
+int CTFGrenadePipebombProjectile::DrawModel( int flags )
+{
+ if ( gpGlobals->curtime < ( m_flCreationTime + 0.1 ) )
+ return 0;
+
+ return BaseClass::DrawModel( flags );
+}
+
+#else
+
+//=============================================================================
+//
+// TF Pipebomb Grenade Projectile functions (Server specific).
+//
+#define TF_WEAPON_PIPEGRENADE_MODEL "models/weapons/w_models/w_grenade_grenadelauncher.mdl"
+#define TF_WEAPON_CANNONBALL_MODEL "models/weapons/w_models/w_cannonball.mdl"
+#define TF_WEAPON_PIPEBOMB_MODEL "models/weapons/w_models/w_stickybomb.mdl"
+#define TF_WEAPON_PIPEBOMB2_MODEL "models/weapons/w_models/w_stickybomb2.mdl"
+#define TF_WEAPON_PIPEBOMBD_MODEL "models/weapons/w_models/w_stickybomb_d.mdl"
+#define TF_WEAPON_PIPEBOMB_BOUNCE_SOUND "Weapon_Grenade_Pipebomb.Bounce"
+#define TF_WEAPON_CANNON_IMPACT_SOUND "Weapon_LooseCannon.BallImpact"
+#define TF_WEAPON_GRENADE_DETONATE_TIME 2.0f
+#define TF_WEAPON_GRENADE_XBOX_DAMAGE 112
+
+BEGIN_DATADESC( CTFGrenadePipebombProjectile )
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( tf_projectile_pipe_remote, CTFGrenadePipebombProjectile );
+PRECACHE_WEAPON_REGISTER( tf_projectile_pipe_remote );
+
+LINK_ENTITY_TO_CLASS( tf_projectile_pipe, CTFGrenadePipebombProjectile );
+PRECACHE_WEAPON_REGISTER( tf_projectile_pipe );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char* CTFGrenadePipebombProjectile::GetPipebombClass( int iPipeBombType )
+{
+ switch ( iPipeBombType )
+ {
+ case TF_GL_MODE_REGULAR:
+ return "tf_projectile_pipe";
+ case TF_GL_MODE_REMOTE_DETONATE:
+ case TF_GL_MODE_REMOTE_DETONATE_PRACTICE:
+ return "tf_projectile_pipe_remote";
+ default:
+ return "tf_projectile_pipe";
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGrenadePipebombProjectile* CTFGrenadePipebombProjectile::Create( const Vector &position, const QAngle &angles,
+ const Vector &velocity, const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo,
+ int iPipeBombType, float flMultDmg )
+{
+ // Translate a projectile type into a pipebomb type.
+ int iPipeBombDetonateType;
+ switch ( iPipeBombType )
+ {
+ case TF_PROJECTILE_PIPEBOMB_REMOTE:
+ {
+ iPipeBombDetonateType = TF_GL_MODE_REMOTE_DETONATE;
+ }
+ break;
+ case TF_PROJECTILE_PIPEBOMB_PRACTICE:
+ {
+ iPipeBombDetonateType = TF_GL_MODE_REMOTE_DETONATE_PRACTICE;
+ }
+ break;
+ case TF_PROJECTILE_CANNONBALL:
+ {
+ iPipeBombDetonateType = TF_GL_MODE_CANNONBALL;
+ }
+ break;
+ default:
+ iPipeBombDetonateType = TF_GL_MODE_REGULAR;
+ }
+
+ const char* pszBombClass = GetPipebombClass( iPipeBombDetonateType );
+ CTFGrenadePipebombProjectile *pGrenade = static_cast<CTFGrenadePipebombProjectile*>( CBaseEntity::CreateNoSpawn( pszBombClass, position, angles, pOwner ) );
+ if ( pGrenade )
+ {
+ // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly
+ pGrenade->SetPipebombMode( iPipeBombDetonateType );
+ DispatchSpawn( pGrenade );
+
+ pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo );
+ pGrenade->SetDamage( pGrenade->GetDamage() * flMultDmg );
+ pGrenade->SetFullDamage( pGrenade->GetDamage() );
+
+ if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE )
+ {
+ // Some hackery here. Reduce the damage, so that if we explode on timeout,
+ // we'll do less damage. If we explode on contact, we'll restore this to full damage.
+ pGrenade->SetDamage( pGrenade->GetDamage() * TF_WEAPON_PIPEBOMB_TIMER_DMG_REDUCTION );
+ }
+
+ pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
+
+ if ( pOwner )
+ {
+ pGrenade->SetTruceValidForEnt( pOwner->IsTruceValidForEnt() );
+ }
+ }
+
+ return pGrenade;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::Spawn()
+{
+ if ( HasStickyEffects() )
+ {
+ // Set this to max, so effectively they do not self-implode.
+
+ if ( m_iType == TF_GL_MODE_REMOTE_DETONATE_PRACTICE )
+ {
+ SetModel( TF_WEAPON_PIPEBOMB2_MODEL );
+ }
+ else
+ {
+ SetModel( TF_WEAPON_PIPEBOMB_MODEL );
+ }
+ SetDetonateTimerLength( FLT_MAX );
+ SetContextThink( &CTFGrenadePipebombProjectile::PreArmThink, gpGlobals->curtime + 0.001f, "PRE_ARM_THINK" ); // Next frame.
+ SetTouch( &CTFGrenadePipebombProjectile::StickybombTouch );
+ }
+ else
+ {
+ if ( m_iType == TF_GL_MODE_CANNONBALL )
+ {
+ SetModel( TF_WEAPON_CANNONBALL_MODEL );
+ }
+ else
+ {
+ SetModel( TF_WEAPON_PIPEGRENADE_MODEL );
+ }
+ SetDetonateTimerLength( TF_WEAPON_GRENADE_DETONATE_TIME );
+ SetTouch( &CTFGrenadePipebombProjectile::PipebombTouch );
+ }
+
+ SetCustomPipebombModel();
+
+ BaseClass::Spawn();
+
+ m_bTouched = false;
+ m_flCreationTime = gpGlobals->curtime;
+
+ // We want to get touch functions called so we can damage enemy players
+ AddSolidFlags( FSOLID_TRIGGER );
+
+ m_flMinSleepTime = 0;
+ AddFlag( FL_GRENADE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::Precache()
+{
+ int iModel = PrecacheModel( TF_WEAPON_PIPEBOMB_MODEL );
+ PrecacheGibsForModel( iModel );
+
+ iModel = PrecacheModel( TF_WEAPON_PIPEBOMB2_MODEL );
+ PrecacheGibsForModel( iModel );
+
+ iModel = PrecacheModel( TF_WEAPON_PIPEBOMBD_MODEL );
+ PrecacheGibsForModel( iModel );
+
+ iModel = PrecacheModel( TF_WEAPON_PIPEGRENADE_MODEL );
+ PrecacheGibsForModel( iModel );
+
+ iModel = PrecacheModel( TF_WEAPON_CANNONBALL_MODEL );
+ PrecacheGibsForModel( iModel );
+
+ // Must add All custom Models here
+ iModel = PrecacheModel( "models/workshop/weapons/c_models/c_kingmaker_sticky/w_kingmaker_stickybomb.mdl" );
+ iModel = PrecacheModel( "models/workshop/weapons/c_models/c_quadball/w_quadball_grenade.mdl" );
+
+ PrecacheParticleSystem( "stickybombtrail_blue" );
+ PrecacheParticleSystem( "stickybombtrail_red" );
+
+ PrecacheScriptSound( TF_WEAPON_PIPEBOMB_BOUNCE_SOUND );
+ PrecacheScriptSound( TF_WEAPON_CANNON_IMPACT_SOUND );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::SetPipebombMode( int iPipebombMode /* = TF_GL_MODE_REGULAR */ )
+{
+ m_iType.Set( iPipebombMode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::BounceSound( void )
+{
+ EmitSound( TF_WEAPON_PIPEBOMB_BOUNCE_SOUND );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::Detonate()
+{
+ if ( gpGlobals->curtime > m_flDetonateTime )
+ {
+ if ( GetLauncher() )
+ {
+ float flFizzle = 0;
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetLauncher(), flFizzle, stickybomb_fizzle_time );
+ if ( flFizzle )
+ {
+ Fizzle();
+ }
+ }
+ }
+
+ if ( m_bFizzle )
+ {
+ g_pEffects->Sparks( GetAbsOrigin(), 1, 2 );
+ Destroy( false );
+
+ if ( HasStickyEffects() )
+ {
+ CreatePipebombGibs();
+ }
+
+ return;
+ }
+
+ BaseClass::Detonate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGrenadePipebombProjectile::DetonateStickies()
+{
+ if ( !GetLauncher() )
+ return false;
+
+ bool bDetonateSticky = false;
+
+ Vector vecOrigin = GetAbsOrigin();
+ const int maxEntities = 64;
+ CBaseEntity *pObjects[ maxEntities ];
+ int count = UTIL_EntitiesInSphere( pObjects, maxEntities, vecOrigin, GetDamageRadius(), FL_GRENADE );
+
+ int iStickiesRemoved = 0;
+
+ trace_t tr;
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( pObjects[i]->GetTeamNumber() == GetLauncher()->GetTeamNumber() )
+ continue;
+
+ CTFGrenadePipebombProjectile *pGrenade = dynamic_cast < CTFGrenadePipebombProjectile*> ( pObjects[i] );
+ if ( !pGrenade )
+ continue;
+
+ if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE )
+ continue;
+
+ if ( pGrenade->m_bFizzle )
+ continue;
+
+ UTIL_TraceLine( vecOrigin, pGrenade->GetAbsOrigin(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction < 1.0 )
+ continue; // No line of sight to the bomb.
+
+ pGrenade->Fizzle();
+ pGrenade->Detonate();
+
+ iStickiesRemoved++;
+
+ bDetonateSticky = true;
+ }
+
+ CTFPlayer *pOwner = ToTFPlayer( GetThrower() );
+ if ( iStickiesRemoved && pOwner )
+ {
+ pOwner->AwardAchievement( ACHIEVEMENT_TF_DEMOMAN_DESTROY_X_STICKYBOMBS, iStickiesRemoved );
+ }
+
+ return bDetonateSticky;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::CreatePipebombGibs( void )
+{
+ CPVSFilter filter( GetAbsOrigin() );
+ UserMessageBegin( filter, "CheapBreakModel" );
+ WRITE_SHORT( GetModelIndex() );
+ WRITE_VEC3COORD( GetAbsOrigin() );
+ MessageEnd();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::Fizzle( void )
+{
+ m_bFizzle = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::StickybombTouch( CBaseEntity *pOther )
+{
+#ifdef GAME_DLL
+#ifdef TF_RAID_MODE
+ if ( TFGameRules()->IsRaidMode() )
+ {
+ if ( dynamic_cast< CBossAlpha * >( pOther ) != NULL )
+ {
+ // stickies stick to the boss
+ m_bTouched = true;
+ VPhysicsGetObject()->EnableMotion( false );
+
+ SetParent( pOther );
+ }
+ }
+#endif
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::PipebombTouch( CBaseEntity *pOther )
+{
+ if ( pOther == GetThrower() )
+ return;
+
+ // Verify a correct "other."
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
+ return;
+
+ // Handle hitting skybox (disappear).
+ trace_t pTrace;
+ Vector velDir = GetAbsVelocity();
+ VectorNormalize( velDir );
+ Vector vOrigin = GetAbsOrigin();
+ Vector vecSpot = vOrigin - velDir * 32;
+ UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
+
+ if ( pTrace.fraction < 1.0 && pTrace.surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // PASSTIME always explode when it hits the ball
+ // fixme find a non-strcmp way to do this
+ if ( !V_strcmp( pOther->GetClassname(), "passtime_ball" ) )
+ {
+ Explode( &pTrace, GetDamageType() );
+ return;
+ }
+
+ //If we already touched a surface then we're not exploding on contact anymore.
+ if ( m_bTouched == true )
+ return;
+
+ bool bExploded = false;
+
+ // Blow up if we hit an enemy we can damage
+ if ( pOther->GetTeamNumber() && pOther->GetTeamNumber() != GetTeamNumber() && pOther->m_takedamage != DAMAGE_NO )
+ {
+ // Check to see if this is a respawn room.
+ if ( !pOther->IsPlayer() )
+ {
+ CFuncRespawnRoom *pRespawnRoom = dynamic_cast<CFuncRespawnRoom*>( pOther );
+ if ( pRespawnRoom )
+ {
+ if ( !pRespawnRoom->PointIsWithin( vOrigin ) )
+ return;
+ }
+ }
+
+ if ( m_iType == TF_GL_MODE_CANNONBALL )
+ {
+ // Damage the player to push them back
+ CBaseEntity *pAttacker = GetThrower();
+ if ( pAttacker && ( pOther->IsPlayer() || pOther->IsBaseObject() ) )
+ {
+ // check if we already penetrate through this victim
+ if ( !m_penetratedEntities.HasElement( pOther ) )
+ {
+ // Impact damage scales with distance
+ float flDistanceSq = (pOther->GetAbsOrigin() - pAttacker->GetAbsOrigin()).LengthSqr();
+ float flImpactDamage = RemapValClamped( flDistanceSq, 512 * 512, 1024 * 1024, 50, 25 );
+
+ CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vOrigin, flImpactDamage, GetDamageType(), TF_DMG_CUSTOM_CANNONBALL_PUSH );
+ pOther->TakeDamage( info );
+
+ CTFPlayer *pVictim = ToTFPlayer( pOther );
+ if ( pVictim )
+ {
+ // apply airblast - Apply stun if they are effectively grounded so we can knock them up
+ if ( !pVictim->m_Shared.InCond( TF_COND_KNOCKED_INTO_AIR ) )
+ {
+ pVictim->m_Shared.StunPlayer( 0.5, 1.f, TF_STUN_MOVEMENT, ToTFPlayer( pAttacker ) );
+ }
+
+ Vector vecToTarget = pVictim->WorldSpaceCenter() - pAttacker->WorldSpaceCenter();
+ VectorNormalize( vecToTarget );
+ vecToTarget *= 400;
+ vecToTarget.z += 350; // Mimic Flamethrower AirBlast
+ pVictim->ApplyAirBlastImpulse( vecToTarget );
+ }
+
+ m_penetratedEntities.AddToTail( pOther );
+
+ EmitSound( TF_WEAPON_CANNON_IMPACT_SOUND );
+
+ // Add this guy to our donk list. If this grenade explodes and hits anyone on our launcher's
+ // donk list, they get minicrit
+ CTFGrenadeLauncher* pLauncher = dynamic_cast<CTFGrenadeLauncher*>( GetLauncher() );
+ if( pLauncher )
+ {
+ pLauncher->AddDonkVictim( pOther );
+ }
+ }
+ return;
+ }
+ }
+
+ // Save this entity as enemy, they will take 100% damage.
+ m_hEnemy = pOther;
+
+ // Restore damage. See comment in CTFGrenadePipebombProjectile::Create() above to understand this.
+ m_flDamage = m_flFullDamage;
+ Explode( &pTrace, GetDamageType() );
+ bExploded = true;
+ }
+
+ // Train hack!
+ if ( !bExploded && pOther->GetModelName() == s_iszTrainName && ( pOther->GetAbsVelocity().LengthSqr() > 1.0f ) )
+ {
+ Explode( &pTrace, GetDamageType() );
+ bExploded = true;
+ }
+
+ // Explode on contact with a Boss, too
+ if ( !bExploded && TFGameRules()->GetActiveBoss() && pOther == TFGameRules()->GetActiveBoss() )
+ {
+ Explode( &pTrace, GetDamageType() );
+ bExploded = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+extern bool PropDynamic_CollidesWithGrenades( CBaseEntity* pBaseEntity );
+
+void CTFGrenadePipebombProjectile::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ BaseClass::VPhysicsCollision( index, pEvent );
+
+ int otherIndex = !index;
+ CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
+
+ if ( !pHitEntity )
+ return;
+
+ if ( m_bWallShatter )
+ {
+ Fizzle();
+ Detonate();
+ return;
+ }
+
+ if ( m_iType == TF_GL_MODE_REGULAR || m_iType == TF_GL_MODE_CANNONBALL )
+ {
+ if ( PropDynamic_CollidesWithGrenades( pHitEntity) )
+ {
+ if ( m_bTouched == false )
+ {
+ SetThink( &CTFGrenadePipebombProjectile::Detonate );
+ SetNextThink( gpGlobals->curtime );
+ }
+ }
+ // Blow up if we hit an enemy we can damage
+ else if ( pHitEntity->GetTeamNumber() && pHitEntity->GetTeamNumber() != GetTeamNumber() && pHitEntity->m_takedamage != DAMAGE_NO )
+ {
+ SetThink( &CTFGrenadePipebombProjectile::Detonate );
+ SetNextThink( gpGlobals->curtime );
+ }
+
+ if ( m_bTouched == false )
+ {
+ SetDamage( GetDamageScaleOnWorldContact() * GetDamage() );
+
+ int iNoBounce = 0;
+ if ( GetLauncher() )
+ {
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( GetLauncher(), iNoBounce, grenade_no_bounce )
+ if ( iNoBounce )
+ {
+ Vector velocity;
+ AngularImpulse angularVelocity;
+ VPhysicsGetObject()->GetVelocity( &velocity, &angularVelocity );
+ velocity *= 0.1f;
+ VPhysicsGetObject()->SetVelocity( &velocity, &angularVelocity );
+ }
+ }
+ }
+
+ m_bTouched = true;
+ return;
+ }
+
+ // Handle hitting skybox (disappear).
+ surfacedata_t *pprops = physprops->GetSurfaceData( pEvent->surfaceProps[otherIndex] );
+ if ( pprops->game.material == 'X' )
+ {
+ // uncomment to destroy grenade upon hitting sky brush
+ //SetThink( &CTFGrenadePipebombProjectile::SUB_Remove );
+ //SetNextThink( gpGlobals->curtime );
+ return;
+ }
+
+ bool bIsDynamicProp = ( NULL != dynamic_cast<CDynamicProp *>( pHitEntity ) );
+
+ // Temp: Don't stick to the saw blades in sawmill.
+ // We should make the saws their own entity type for networking.
+ if ( FStrEq( pHitEntity->m_iParent.ToCStr(), "sawmovelinear01" ) ||
+ FStrEq( pHitEntity->m_iParent.ToCStr(), "sawmovelinear02" ) ||
+ PropDynamic_CollidesWithGrenades( pHitEntity) )
+ {
+ bIsDynamicProp = false;
+ }
+
+ // Pipebombs stick to the world when they touch it
+ if ( pHitEntity && ( pHitEntity->IsWorld() || bIsDynamicProp ) && gpGlobals->curtime > m_flMinSleepTime )
+ {
+ m_bTouched = true;
+
+ g_PostSimulationQueue.QueueCall( VPhysicsGetObject(), &IPhysicsObject::EnableMotion, false );
+
+ // Save impact data for explosions.
+ m_bUseImpactNormal = true;
+ pEvent->pInternalData->GetSurfaceNormal( m_vecImpactNormal );
+ m_vecImpactNormal.Negate();
+ m_flTouchedTime = gpGlobals->curtime;
+
+ float flFizzle = 0;
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetLauncher(), flFizzle, stickybomb_fizzle_time );
+ if ( flFizzle > 0 )
+ {
+ SetDetonateTimerLength( flFizzle );
+ }
+ }
+}
+
+ConVar tf_grenade_forcefrom_bullet( "tf_grenade_forcefrom_bullet", "2.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar tf_grenade_forcefrom_buckshot( "tf_grenade_forcefrom_buckshot", "0.75", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar tf_grenade_forcefrom_blast( "tf_grenade_forcefrom_blast", "0.15", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar tf_grenade_force_sleeptime( "tf_grenade_force_sleeptime", "1.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); // How long after being shot will we re-stick to the world.
+ConVar tf_pipebomb_force_to_move( "tf_pipebomb_force_to_move", "1500.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+ConVar tf_pipebomb_deflect_reset_time( "tf_pipebomb_deflect_reset_time", "10.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
+
+//-----------------------------------------------------------------------------
+// Purpose: If we are shot after being stuck to the world, move a bit
+//-----------------------------------------------------------------------------
+int CTFGrenadePipebombProjectile::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ if ( !info.GetAttacker() )
+ {
+ Assert( !info.GetAttacker() );
+ return 0;
+ }
+
+ bool bSameTeam = ( info.GetAttacker()->GetTeamNumber() == GetTeamNumber() );
+ if ( !bSameTeam && CanTakeDamage() )
+ {
+ if ( m_bTouched && HasStickyEffects() && ( info.GetDamageType() & (DMG_BULLET|DMG_BUCKSHOT|DMG_BLAST|DMG_SONIC|DMG_MELEE) ) )
+ {
+ Vector vecForce = info.GetDamageForce();
+
+ bool bBreakPipes = false;
+
+ if ( info.GetDamageType() & (DMG_BULLET|DMG_MELEE) )
+ {
+ vecForce *= tf_grenade_forcefrom_bullet.GetFloat();
+ bBreakPipes = true;
+ }
+ if ( info.GetDamageType() & DMG_SONIC )
+ {
+ vecForce *= tf_grenade_forcefrom_bullet.GetFloat();
+ }
+ else if ( info.GetDamageType() & DMG_BUCKSHOT )
+ {
+ vecForce *= tf_grenade_forcefrom_buckshot.GetFloat();
+ bBreakPipes = true;
+ }
+ else if ( info.GetDamageType() & DMG_BLAST )
+ {
+ vecForce *= tf_grenade_forcefrom_blast.GetFloat();
+ }
+
+ if ( bBreakPipes == true )
+ {
+ // we might get multiple calls for the same pipe when shooting it with a shotgun,
+ // so make sure it only sends the player_destroyed_pipebomb event once
+ if ( m_bSendPlayerDestroyedEvent )
+ {
+ if ( info.GetAttacker()->IsPlayer() )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( info.GetAttacker() );
+ if ( pPlayer )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_destroyed_pipebomb" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ // If we are near a building, award achievement progress.
+ CTFTeam *pTeam = pPlayer->GetTFTeam();
+ if ( pTeam )
+ {
+ for ( int i=0; i<pTeam->GetNumObjects(); i++ )
+ {
+ CBaseObject *pObject = pTeam->GetObject(i);
+ if ( pObject && pObject->GetAbsOrigin().DistTo( GetAbsOrigin() ) < 100 &&
+ pObject->ObjectType() != OBJ_ATTACHMENT_SAPPER )
+ {
+ pPlayer->AwardAchievement( ACHIEVEMENT_TF_ENGINEER_DESTROY_STICKIES, 1 );
+ break; // Only one award per sticky.
+ }
+ }
+ }
+ }
+ }
+
+ m_bSendPlayerDestroyedEvent = false;
+ }
+ }
+
+ Fizzle();
+ Detonate();
+ }
+
+ // If the force is sufficient, detach & move the pipebomb
+ float flForce = tf_pipebomb_force_to_move.GetFloat();
+ if ( vecForce.LengthSqr() > (flForce*flForce) )
+ {
+ if ( VPhysicsGetObject() )
+ {
+ VPhysicsGetObject()->EnableMotion( true );
+ }
+
+ CTakeDamageInfo newInfo = info;
+ newInfo.SetDamageForce( vecForce );
+
+ VPhysicsTakeDamage( newInfo );
+
+ // The pipebomb will re-stick to the ground after this time expires
+ m_flMinSleepTime = gpGlobals->curtime + tf_grenade_force_sleeptime.GetFloat();
+ m_bTouched = false;
+
+ // It has moved the data is no longer valid.
+ m_bUseImpactNormal = false;
+ m_vecImpactNormal.Init();
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void CTFGrenadePipebombProjectile::IncrementDeflected( void )
+{
+ BaseClass::IncrementDeflected();
+
+ if ( GetDeflected() && HasStickyEffects() )
+ {
+ m_flDeflectedTime = gpGlobals->curtime + tf_pipebomb_deflect_reset_time.GetFloat();
+ }
+
+ int iTeamNumber = GetTeamNumber();
+
+ CTFPlayer *pOwner = ToTFPlayer( GetDeflectOwner() );
+
+ if ( pOwner )
+ {
+ iTeamNumber = pOwner->GetTeamNumber();
+ }
+
+ if ( !HasStickyEffects() )
+ {
+ m_nSkin = ( iTeamNumber == TF_TEAM_BLUE ) ? 1 : 0;
+ }
+}
+
+void CTFGrenadePipebombProjectile::DetonateThink( void )
+{
+ BaseClass::DetonateThink();
+
+ if ( m_flDeflectedTime <= gpGlobals->curtime && HasStickyEffects() )
+ {
+ ResetDeflected();
+ SetDeflectOwner( NULL );
+ }
+
+ // If we received our crit via a medic, make sure they still exist.
+ if ( m_CritMedics.Count() )
+ {
+ if ( TFGameRules() && ( TFGameRules()->InSetup() || TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS ) )
+ {
+ bool bRemove = true;
+
+ FOR_EACH_VEC( m_CritMedics, i )
+ {
+ if ( m_CritMedics[i] && m_CritMedics[i]->GetPlayerClass()->GetClassIndex() == TF_CLASS_MEDIC )
+ {
+ bRemove = false;
+ break;
+ }
+ }
+
+ // No medic(s)
+ if ( bRemove )
+ {
+ Fizzle();
+ Detonate();
+ return;
+ }
+ }
+ else
+ {
+ // Clear the vector when the game starts
+ m_CritMedics.RemoveAll();
+ }
+ }
+}
+
+void CTFGrenadePipebombProjectile::PreArmThink( void )
+{
+ SetContextThink( &CTFGrenadePipebombProjectile::ArmThink, gpGlobals->curtime + GetLiveTime(), "ARM_THINK" );
+}
+
+void CTFGrenadePipebombProjectile::ArmThink( void )
+{
+ // When between waves in MvM, players sometimes switch to medic just so demos can place crit stickies,
+ // and then switch back. This code removes the sticky if the medic switches ( in DetonateThink() )
+ if ( IsCritical() && HasStickyEffects() && TFGameRules() && ( TFGameRules()->InSetup() || TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS ) )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetThrower() );
+ if ( pOwner && pOwner->m_Shared.InCond( TF_COND_CRITBOOSTED ) && !pOwner->m_Shared.InCond( TF_COND_CRITBOOSTED_USER_BUFF ) )
+ {
+ // Find the medic(s)
+ for ( int i = 0; i < pOwner->m_Shared.GetNumHealers(); i++ )
+ {
+ CTFPlayer *pMedic = ToTFPlayer( pOwner->m_Shared.GetHealerByIndex( i ) );
+ if ( !pMedic )
+ continue;
+
+ CWeaponMedigun *pMedigun = dynamic_cast <CWeaponMedigun*>( pMedic->GetActiveTFWeapon() );
+ if ( pMedigun && pMedigun->IsReleasingCharge() && pMedigun->GetChargeType() == MEDIGUN_CHARGE_CRITICALBOOST )
+ {
+ m_CritMedics.AddToTail( pMedic );
+ }
+ }
+
+ // We didn't find the medic. What provided TF_COND_CRITBOOSTED?
+ Assert( m_CritMedics.Count() );
+ }
+ }
+
+ if ( m_bDetonateOnPulse )
+ {
+ Detonate();
+ }
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFGrenadePipebombProjectile::GetLiveTime( void )
+{
+ float flLiveTime = tf_grenadelauncher_livetime.GetFloat();
+
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetLauncher(), flLiveTime, sticky_arm_time );
+
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetThrower() );
+
+ if ( pOwner )
+ {
+ if ( pOwner->m_Shared.GetCarryingRuneType() == RUNE_HASTE )
+ {
+ flLiveTime *= 0.5f;
+ }
+ else if ( pOwner->m_Shared.GetCarryingRuneType() == RUNE_KING || pOwner->m_Shared.InCond( TF_COND_KING_BUFFED ) )
+ {
+ flLiveTime *= 0.75f;
+ }
+ }
+ }
+
+ return flLiveTime;
+}
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Grenade was deflected.
+//-----------------------------------------------------------------------------
+void CTFGrenadePipebombProjectile::Deflected( CBaseEntity *pDeflectedBy, Vector& vecDir )
+{
+ CTFPlayer *pTFDeflector = ToTFPlayer( pDeflectedBy );
+ if ( !pTFDeflector )
+ return;
+
+ CTFPlayer* pOldOwner = NULL;
+ if ( HasStickyEffects() )
+ {
+ CTakeDamageInfo info;
+
+ float flForceMultiplier = 1.0f;
+ ITFChargeUpWeapon *pWeapon = dynamic_cast<ITFChargeUpWeapon*>( pTFDeflector->GetActiveWeapon() );
+ if ( pWeapon )
+ {
+ flForceMultiplier = RemapValClamped( ( gpGlobals->curtime - pWeapon->GetChargeBeginTime() ),
+ 0.0f,
+ pWeapon->GetChargeMaxTime(),
+ 1.0f,
+ 2.0f );
+ }
+ Vector vecForce = vecDir * flForceMultiplier * -CTFWeaponBase::DeflectionForce( WorldAlignSize(), 90, 12.0f );
+
+ pOldOwner = ToTFPlayer( GetThrower() );
+ info.SetAttacker( pDeflectedBy );
+ info.SetDamageForce( vecForce );
+ info.SetDamageType( DMG_SONIC );
+ info.SetWeapon( pTFDeflector->GetActiveTFWeapon() );
+ OnTakeDamage( info );
+ }
+ else
+ {
+ ChangeTeam( pTFDeflector->GetTeamNumber() );
+ SetLauncher( pTFDeflector->GetActiveWeapon() );
+ pOldOwner = ToTFPlayer( GetThrower() );
+ SetThrower( pTFDeflector );
+
+ if ( pTFDeflector->m_Shared.IsCritBoosted() )
+ {
+ SetCritical( true );
+ }
+ }
+
+ if ( pOldOwner )
+ {
+ pOldOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:1,victim:1" );
+ }
+
+ CTFWeaponBase::SendObjectDeflectedEvent( pTFDeflector, pOldOwner, GetWeaponID(), this );
+
+ SetDeflectOwner( pTFDeflector );
+ IncrementDeflected();
+}
+#endif
+
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Highlight FX
+//-----------------------------------------------------------------------------
+class CProxyStickybombGlowColor : public CResultProxy
+{
+public:
+ void OnBind( void *pC_BaseEntity )
+ {
+ Assert( m_pResult );
+
+ if ( !pC_BaseEntity )
+ {
+ m_pResult->SetVecValue( 1, 1, 1 );
+ return;
+ }
+
+ CTFGrenadePipebombProjectile *pGrenade = NULL;
+ C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
+ if ( !pEntity )
+ {
+ m_pResult->SetVecValue( 1, 1, 1 );
+ return;
+ }
+
+ // default to [1 1 1]
+ Vector vResult = Vector( 1, 1, 1 );
+
+ pGrenade = dynamic_cast<CTFGrenadePipebombProjectile*>( pEntity );
+ if ( pGrenade )
+ {
+ if ( pGrenade->IsHighlighted() )
+ {
+ int iTeamNumber = pGrenade->GetTeamNumber();
+ if ( iTeamNumber == TF_TEAM_RED )
+ {
+ vResult = Vector ( 100.f, 0.f, 0.f );
+ if ( pGrenade->m_pGlowEffect )
+ {
+ pGrenade->m_pGlowEffect->SetColor( Vector( 250, 0, 0 ) );
+ }
+ }
+ else
+ {
+ vResult = Vector ( 0.f, 0.f, 100.f );
+ if ( pGrenade->m_pGlowEffect )
+ {
+ pGrenade->m_pGlowEffect->SetColor( Vector( 0, 0, 250 ) );
+ }
+ }
+ }
+ else
+ {
+ int iTeamNumber = pGrenade->GetTeamNumber();
+ if ( iTeamNumber == TF_TEAM_RED )
+ {
+ if ( pGrenade->m_pGlowEffect )
+ {
+ pGrenade->m_pGlowEffect->SetColor( Vector( 200, 100, 100 ) );
+ }
+ }
+ else
+ {
+ if ( pGrenade->m_pGlowEffect )
+ {
+ pGrenade->m_pGlowEffect->SetColor( Vector( 100, 100, 200 ) );
+ }
+ }
+ }
+ }
+ m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
+ }
+};
+EXPOSE_INTERFACE( CProxyStickybombGlowColor, IMaterialProxy, "StickybombGlowColor" IMATERIAL_PROXY_INTERFACE_VERSION );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#if GAME_DLL
+int CTFGrenadePipebombProjectile::GetDamageCustom()
+{
+ if ( m_iType == TF_GL_MODE_REMOTE_DETONATE )
+ {
+ if ( !m_bTouched )
+ {
+ return TF_DMG_CUSTOM_AIR_STICKY_BURST;
+ }
+ else if ( m_bDefensiveBomb )
+ {
+ return TF_DMG_CUSTOM_DEFENSIVE_STICKY;
+ }
+ else
+ {
+ return TF_DMG_CUSTOM_STANDARD_STICKY;
+ }
+ }
+ else if ( m_iType == TF_GL_MODE_REMOTE_DETONATE_PRACTICE )
+ {
+ return TF_DMG_CUSTOM_PRACTICE_STICKY;
+ }
+
+ return BaseClass::GetDamageCustom();
+}
+
+
+float CTFGrenadePipebombProjectile::GetDamageScaleOnWorldContact()
+{
+ float flGrenadeDamageScaleOnWorldContact = 1.f;
+ if ( GetLauncher() )
+ {
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetLauncher(),flGrenadeDamageScaleOnWorldContact, grenade_damage_reduction_on_world_contact );
+ }
+ return flGrenadeDamageScaleOnWorldContact;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+float CTFGrenadePipebombProjectile::GetDamageRadius()
+{
+ float flRadiusMod = 1.0f;
+
+#ifdef GAME_DLL
+ // winbomb prevention.
+ // Air Det
+ if ( m_iType == TF_GL_MODE_REMOTE_DETONATE )
+ {
+ if ( m_bTouched == false )
+ {
+ float flArmTime = tf_grenadelauncher_livetime.GetFloat();
+ flRadiusMod *= RemapValClamped( gpGlobals->curtime - m_flCreationTime, flArmTime, flArmTime + tf_sticky_radius_ramp_time.GetFloat(), tf_sticky_airdet_radius.GetFloat(), 1.0 );
+ }
+ }
+#endif // GAME_DLL
+ return BaseClass::GetDamageRadius() * flRadiusMod;
+}