summaryrefslogtreecommitdiff
path: root/game/shared/tf2/weapon_repairgun.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/tf2/weapon_repairgun.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf2/weapon_repairgun.cpp')
-rw-r--r--game/shared/tf2/weapon_repairgun.cpp740
1 files changed, 740 insertions, 0 deletions
diff --git a/game/shared/tf2/weapon_repairgun.cpp b/game/shared/tf2/weapon_repairgun.cpp
new file mode 100644
index 0000000..b2e626d
--- /dev/null
+++ b/game/shared/tf2/weapon_repairgun.cpp
@@ -0,0 +1,740 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Medic's Medikit weapon
+//
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "weapon_combatshield.h"
+#include "engine/IEngineSound.h"
+#include "grenade_base_empable.h"
+#include "basetfvehicle.h"
+#include "tf_gamerules.h"
+
+//#define REPAIR_GUN_DISABLES_GRENADES // Uncomment if you want the repair gun to disable grenades.
+
+#if defined( CLIENT_DLL )
+#include "particles_simple.h"
+#else
+#include "ndebugoverlay.h"
+#endif
+
+#include "weapon_repairgun.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Buff ranges
+ConVar weapon_repairgun_target_range( "weapon_repairgun_target_range", "450", FCVAR_REPLICATED, "The farthest away you can be for the repair gun to initially lock onto a player." );
+ConVar weapon_repairgun_stick_range( "weapon_repairgun_stick_range", "512", FCVAR_REPLICATED, "How far away the repair gun can stay locked onto someone." );
+ConVar weapon_repairgun_rate( "weapon_repairgun_rate", "12", FCVAR_REPLICATED, "Health healed per second by the repair gun." );
+ConVar weapon_repairgun_damage_modifier( "weapon_repairgun_damage_modifier", "1.5", FCVAR_REPLICATED, "Scales the damage a player does while being healed with the repair gun." );
+ConVar weapon_repairgun_debug( "weapon_repairgun_debug", "0", FCVAR_REPLICATED, "Show debugging info for the repair gun." );
+ConVar weapon_repairgun_construction_rate( "weapon_repairgun_construction_rate", "10", FCVAR_REPLICATED, "Constructing object health healed per second by the repair gun." );
+
+LINK_ENTITY_TO_CLASS( weapon_repairgun, CWeaponRepairGun );
+
+PRECACHE_WEAPON_REGISTER( weapon_repairgun );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRepairGun, DT_WeaponRepairGun )
+
+BEGIN_NETWORK_TABLE( CWeaponRepairGun, DT_WeaponRepairGun )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_bHealing ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bAttacking ), 1, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO( m_hHealingTarget ) ),
+#else
+ RecvPropInt( RECVINFO( m_bAttacking ) ),
+ RecvPropInt( RECVINFO( m_bHealing ) ),
+ RecvPropEHandle( RECVINFO(m_hHealingTarget) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponRepairGun )
+
+ DEFINE_PRED_FIELD( m_bHealing, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAttacking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_hHealingTarget, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_FIELD( m_flHealEffectLifetime, FIELD_FLOAT ),
+
+// DEFINE_PRED_FIELD( m_pEmitter, FIELD_POINTER ),
+// DEFINE_PRED_FIELD( m_hParticleMaterial, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_PathParticleEvent, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
+
+END_PREDICTION_DATA()
+
+#define PARTICLE_PATH_VEL 140.0
+#define NUM_PATH_PARTICLES_PER_SEC 600.0f
+#define NUM_TARGET_PARTICLES_PER_SEC 720.0f
+
+#define NUM_REPAIRGUN_PATH_POINTS 8
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRepairGun::CWeaponRepairGun( void )
+{
+ m_flHealEffectLifetime = 0;
+
+ m_bHealing = false;
+ m_bAttacking = false;
+
+ m_flNextBuzzTime = 0;
+
+#if defined( CLIENT_DLL )
+ m_pEmitter = NULL;
+ m_hParticleMaterial = INVALID_MATERIAL_HANDLE;
+ m_PathParticleEvent.Init( NUM_PATH_PARTICLES_PER_SEC );
+ m_bPlayingSound = false;
+#endif
+
+ SetPredictionEligible( true );
+}
+
+
+void CWeaponRepairGun::Precache()
+{
+ BaseClass::Precache();
+ PrecacheScriptSound( "WeaponRepairGun.NoTarget" );
+ PrecacheScriptSound( "WeaponRepairGun.Healing" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRepairGun::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ RemoveHealingTarget();
+ m_bAttacking = false;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetTargetRange( void )
+{
+ return weapon_repairgun_target_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetStickRange( void )
+{
+ return weapon_repairgun_stick_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetHealRate( void )
+{
+ return weapon_repairgun_rate.GetFloat();
+}
+
+// Now make sure there isn't something other than team players in the way.
+class CRepairFilter : public CTraceFilterSimple
+{
+public:
+ CRepairFilter( CBaseEntity *pShooter ) : CTraceFilterSimple( pShooter, TFCOLLISION_GROUP_WEAPON )
+ {
+ m_pShooter = pShooter;
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ // If it hit an edict the isn't the target and is on our team, then the ray is blocked.
+ CBaseEntity *pEnt = static_cast<CBaseEntity*>(pHandleEntity);
+
+ // Ignore collisions with the shooter
+ if ( pEnt == m_pShooter )
+ return false;
+
+ // You can't heal a vehicle you are sitting in.
+
+ if( ((CBaseTFPlayer*)m_pShooter)->IsInAVehicle() )
+ {
+
+ CBaseEntity* pVehicle = ((CBaseTFPlayer*)m_pShooter)->GetVehicle()->GetVehicleEnt();
+ if( pVehicle == pEnt )
+ {
+ return false;
+ }
+ }
+
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ // Repairgun can also disable enemy grenades
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pEnt);
+
+ // Ignore collisions with teammates, or friendly grenades
+ if ( !pGrenade )
+ {
+ if ( pEnt->GetTeam() == m_pShooter->GetTeam() )
+ return false;
+ }
+#else
+ if ( pEnt->GetTeam() == m_pShooter->GetTeam() )
+ return false;
+#endif
+
+ return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
+ }
+
+ CBaseEntity *m_pShooter;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Vehicle checking to see if we should switch targets from a player
+// to a vehicle, or vice versa.
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponRepairGun::CheckVehicleTargets( CBaseEntity *pCurHealing )
+{
+ // Unable to switch to/from players?
+ if ( !TargetsPlayers() )
+ return pCurHealing;
+
+ CBaseTFVehicle *pTargetVehicle = NULL;
+
+ // If we're a fully healed player sitting in a vehicle, see if the vehicle needs healing instead
+ if ( pCurHealing->IsPlayer() && pCurHealing->GetHealth() >= pCurHealing->GetMaxHealth() )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)pCurHealing;
+ if ( !pPlayer->IsInAVehicle() )
+ return pCurHealing;
+
+ pTargetVehicle = (CBaseTFVehicle*)(pPlayer->GetVehicle()->GetVehicleEnt());
+ if ( pTargetVehicle->GetHealth() < pTargetVehicle->GetMaxHealth() )
+ return pTargetVehicle;
+ }
+ else
+ {
+ // If the entity is a vehicle, and it's fully healed, heal any players in it instead
+ pTargetVehicle = dynamic_cast<CBaseTFVehicle *>(pCurHealing);
+ }
+
+ // Is the vehicle fully healed?
+ if ( pTargetVehicle && pTargetVehicle->GetHealth() >= pTargetVehicle->GetMaxHealth() )
+ {
+ CBaseTFPlayer *pUnhurtPlayer = NULL;
+ CBaseTFPlayer *pHurtPlayer = NULL;
+
+ // Go through all players in the vehicle
+ int iMax = pTargetVehicle->GetMaxPassengerCount();
+ for ( int i = 0; i < iMax; i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pTargetVehicle->GetPassenger(i);
+ if ( pPlayer )
+ {
+ if ( pPlayer->GetHealth() < pPlayer->GetMaxHealth() )
+ {
+ pUnhurtPlayer = pPlayer;
+ }
+ else
+ {
+ pHurtPlayer = pPlayer;
+ }
+ }
+ }
+
+ // Heal hurt players first
+ if ( pHurtPlayer )
+ return pHurtPlayer;
+
+ if ( pUnhurtPlayer )
+ return pUnhurtPlayer;
+ }
+
+ return pCurHealing;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to a healable target
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponRepairGun::GetTargetToHeal( CBaseEntity *pCurHealing )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return NULL;
+
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+
+
+ // If we're already healing someone, stick onto them as long as possible.
+ // Even if we can't heal them at the moment, lock onto them until they release the buttom.
+ CBaseEntity* pTarget = pCurHealing;
+ CBaseEntity* pVehicle = NULL; // Vehicle the owner is in, or NULL.
+
+ // You can't heal a vehicle you are sitting in, make sure we aren't healing a vehicle right after we've gotten in.
+ if( ((CBaseTFPlayer*)pOwner)->IsInAVehicle() )
+ {
+
+ pVehicle = ((CBaseTFPlayer*)pOwner)->GetVehicle()->GetVehicleEnt();
+ if( pVehicle == pTarget )
+ {
+ pTarget = NULL;
+ }
+ }
+
+ if ( pTarget && pTarget->IsAlive() && (pTarget->GetTeam() == pOwner->GetTeam()) )
+ {
+ // Make sure the guy didn't go out of range.
+ Vector vecTargetPoint = pTarget->WorldSpaceCenter();
+ Vector vecPoint;
+
+ // If it's brush built, use absmins/absmaxs
+ pTarget->CollisionProp()->CalcNearestPoint( vecSrc, &vecPoint );
+
+#ifndef CLIENT_DLL
+ //NDebugOverlay::Box( vecPoint, Vector(-2,-2,-2), Vector(2,2,2), 255,0,0, 8, 0.1 );
+#endif
+
+ float flDistance = (vecPoint - vecSrc).Length();
+ if ( flDistance < GetStickRange() )
+ {
+ trace_t tr;
+ CRepairFilter drainFilter( pOwner );
+ UTIL_TraceLine( vecSrc, vecTargetPoint, MASK_SHOT, &drainFilter, &tr );
+
+ if (( tr.fraction == 1.0f) || (tr.m_pEnt == pTarget))
+ return CheckVehicleTargets( pTarget );
+ }
+
+ // Return null so we can't heal this player but m_hHealingPlayer stays set to them.
+ return NULL;
+ }
+ else
+ {
+ // Ok, try to find a new player to heal.
+ // Get the target point and location
+ Vector vecAiming;
+ pOwner->EyeVectors( &vecAiming );
+
+ // Find a player in range of this player, and make sure they're healable.
+ Vector vecEnd = vecSrc + vecAiming * GetTargetRange();
+ trace_t tr;
+
+ // Use WeaponTraceLine so shields are tested...
+ TFGameRules()->WeaponTraceLine( vecSrc, vecEnd, (MASK_SHOT & ~CONTENTS_HITBOX), pOwner, DMG_PROBE, &tr );
+
+#ifndef CLIENT_DLL
+ //NDebugOverlay::Box( vecSrc, Vector(-2,-2,-2), Vector(2,2,2), 192,192,0, 8, 10 );
+ //NDebugOverlay::Box( vecEnd, Vector(-2,-2,-2), Vector(2,2,2), 0,255,0, 8, 10 );
+ //NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255, 8, 10 );
+#endif
+
+ if ( tr.fraction != 1.0 )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( pEntity )
+ {
+ // Repairgun can also disable enemy grenades
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pEntity);
+ if ( pGrenade && !pGrenade->InSameTeam( pOwner ) )
+ return pGrenade;
+#endif
+
+ // Only target players if I'm allowed to
+ if ( !TargetsPlayers() && pEntity->IsPlayer() )
+ return NULL;
+
+ // You can't heal a vehicle you are sitting in.
+ if ( pVehicle && ( pVehicle == pEntity ) )
+ return NULL;
+
+ if ( (pEntity != pOwner) && pEntity->IsAlive() && pEntity->CanBePoweredUp() )
+ {
+ // Target needs to be on the same team
+ if ( pEntity->InSameTeam( pOwner ) )
+ return CheckVehicleTargets( pEntity );
+ }
+ }
+ }
+
+ if ( weapon_repairgun_debug.GetBool() )
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "REPAIRGUN: no target found\n" );
+ }
+
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Overloaded to handle the hold-down healing
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+#if !defined( CLIENT_DLL )
+ if ( AppliesModifier() )
+ {
+ m_DamageModifier.SetModifier( weapon_repairgun_damage_modifier.GetFloat() );
+ }
+#endif
+
+ // Try to start healing
+ m_bAttacking = false;
+ if ( (pOwner->m_nButtons & IN_ATTACK) && GetShieldState() == SS_DOWN )
+ {
+ PrimaryAttack();
+ m_bAttacking = true;
+ }
+ else if ( GetCurHealingTarget() )
+ {
+ // Detach from the player if they release the attack button.
+ RemoveHealingTarget();
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're healing
+ AllowShieldPostFrame( !m_bAttacking );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::RemoveHealingTarget()
+{
+ // Stop the welding animation
+ if ( m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_END );
+ }
+
+ m_hHealingTarget = NULL;
+#if !defined( CLIENT_DLL )
+ m_DamageModifier.RemoveModifier();
+#endif
+ m_bHealing = false;
+
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( pOwner )
+ {
+ pOwner->SetIDEnt( NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRepairGun::ComputeEMPFireState( void )
+{
+ if ( IsOwnerEMPed() )
+ {
+ // If we've just been EMPed, remove the heal target
+ if ( GetCurHealingTarget() )
+ {
+ RemoveHealingTarget();
+ }
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to heal any player within range of the medikit
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Can't fire if we've been EMPed.
+ if ( !ComputeEMPFireState() )
+ return;
+
+#if !defined( CLIENT_DLL )
+ // Find a Player to buff with hitscan
+ CBaseEntity *pCurHealing = GetCurHealingTarget();
+ CBaseEntity *pTarget = GetTargetToHeal( pCurHealing );
+ if ( pTarget )
+ {
+ // Start the welding animation
+ if ( !m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_START );
+ }
+
+ // Tell the client who we're trying to heal.
+ m_bHealing = true;
+ m_hHealingTarget = pTarget;
+
+ // Repairgun needs to EMP grenades, heal everything else
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pTarget);
+ if ( pGrenade )
+ {
+ pTarget->TakeEMPDamage( 1.0 );
+ }
+ else
+#endif
+ {
+ // Can't bring things back from the dead
+ if ( pTarget->IsAlive() )
+ {
+ float flBoostAmount = GetHealRate() * gpGlobals->frametime;
+ // If it's an object, and it's constructing, use the construction heal rate
+ if ( pTarget->Classify() == CLASS_MILITARY )
+ {
+ CBaseObject *pObject = dynamic_cast<CBaseObject*>(pTarget);
+ if ( pObject && pObject->IsBuilding() )
+ {
+ flBoostAmount = weapon_repairgun_construction_rate.GetFloat() * gpGlobals->frametime;
+ }
+ if ( pObject && pObject->IsDying() )
+ {
+ flBoostAmount = 0;
+ }
+ }
+
+ // If we're not succeeding, remove the damage modifier
+ if ( !pTarget->AttemptToPowerup( POWERUP_BOOST, 1.0, flBoostAmount, pOwner, AppliesModifier() ? &m_DamageModifier : NULL ) )
+ {
+ m_DamageModifier.RemoveModifier();
+ }
+
+ // Force the player's ID target to the heal target
+ pOwner->SetIDEnt( pTarget );
+ }
+ }
+ }
+ else
+ {
+ RemoveHealingTarget();
+ }
+#endif
+
+ CheckRemoveDisguise();
+}
+
+void CWeaponRepairGun::PlayAttackAnimation( int activity )
+{
+ SendWeaponAnim( activity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle tests to see if we're facing a valid target for the medikit
+// If so, move into the "heal-able" animation.
+// Otherwise, move into the "not-heal-able" animation.
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ // Loop the welding animation
+ if ( m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_LOOP );
+ }
+ else
+ {
+ // select an idle animation
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+// Check to see if it affects the medikit
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+}
+
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::StopRepairSound( bool bStopHealingSound, bool bStopNoTargetSound )
+{
+ if ( bStopHealingSound )
+ {
+ StopSound( "WeaponRepairGun.Healing" );
+ }
+
+ if ( bStopNoTargetSound )
+ {
+ StopSound( "WeaponRepairGun.NoTarget" );
+ }
+}
+
+
+void C_WeaponRepairGun::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ // Stop emitting particles if we're going dormant.
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ }
+
+ BaseClass::NotifyShouldTransmit( state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_WeaponRepairGun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ m_pEmitter = CSimpleEmitter::Create( "C_WeaponRepairGun" );
+
+ m_hParticleMaterial = m_pEmitter->GetPMaterial( "sprites/chargeball" );
+ }
+
+ // Think?
+ if ( m_bHealing && m_hHealingTarget.Get())
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
+ }
+ else
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ m_bPlayingSound = false;
+ StopRepairSound( true, false );
+
+ // Are they holding the attack button but not healing anyone? Give feedback.
+ if ( IsActiveByLocalPlayer() && GetOwner() && GetOwner()->IsAlive() && m_bAttacking && GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( gpGlobals->curtime >= m_flNextBuzzTime )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponRepairGun.NoTarget" );
+ m_flNextBuzzTime = gpGlobals->curtime + 0.5f; // only buzz every so often.
+ }
+ }
+ else
+ {
+ StopRepairSound( false, true ); // Stop the "no target" sound.
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponRepairGun::ClientThink()
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer )
+ return;
+
+ if ( m_hHealingTarget == NULL )
+ {
+ return;
+ }
+
+ // Don't show it while the player is dead. Ideally, we'd respond to m_bHealing in OnDataChanged,
+ // but it stops sending the weapon when it's holstered, and it gets holstered when the player dies.
+ C_BasePlayer *pFiringPlayer = dynamic_cast< C_BasePlayer* >( GetOwner() );
+ if ( !pFiringPlayer || pFiringPlayer->IsPlayerDead() )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ m_bPlayingSound = false;
+ StopRepairSound();
+ return;
+ }
+
+ if ( !m_bPlayingSound )
+ {
+ m_bPlayingSound = true;
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponRepairGun.Healing" );
+ }
+
+ Vector points[NUM_REPAIRGUN_PATH_POINTS];
+
+ // First generate a sequence of points so we can parameterize the (curvy) path from the
+ // tip of the gun to the target.
+ Vector vForward, vOrigin;
+ QAngle vAngles;
+ GetShootPosition( vOrigin, vAngles );
+
+ AngleVectors( vAngles, &vForward );
+
+ Vector vecTargetOrg = m_hHealingTarget->WorldSpaceCenter();
+
+ Vector vDirTo = vecTargetOrg - vOrigin;
+ float flDistanceTo = vDirTo.Length();
+ vDirTo /= flDistanceTo;
+
+ float flBendDist = flDistanceTo * 3;
+ const Vector A = vOrigin - vForward * flBendDist;
+ const Vector &B = vOrigin;
+ const Vector &C = vecTargetOrg;
+ const Vector D = vecTargetOrg - vForward * flBendDist;
+
+ for ( int i=0; i < NUM_REPAIRGUN_PATH_POINTS; i++ )
+ {
+ Catmull_Rom_Spline( A, B, C, D, (float)i / (NUM_REPAIRGUN_PATH_POINTS-1), points[i] );
+ }
+
+ // Add random short-lived particles from the gun tip to the target.
+ m_pEmitter->SetSortOrigin( (vecTargetOrg + pPlayer->GetAbsOrigin()) * 0.5f );
+
+ float flCur = gpGlobals->frametime;
+ while ( m_PathParticleEvent.NextEvent( flCur ) )
+ {
+ float t = RandomFloat( 0, 1 );
+ int iPrev = (int)( t * (NUM_REPAIRGUN_PATH_POINTS - 1.001) );
+ float tPrev = (float)iPrev / (NUM_REPAIRGUN_PATH_POINTS - 1);
+ float tNext = (float)(iPrev+1) / (NUM_REPAIRGUN_PATH_POINTS - 1);
+ Assert( tNext <= NUM_REPAIRGUN_PATH_POINTS-1 );
+
+ Vector vPos;
+ VectorLerp( points[iPrev], points[iPrev+1], (t-tPrev) / (tNext - tPrev), vPos );
+ vPos += RandomVector( -3, 3 );
+
+ SimpleParticle *pParticle = m_pEmitter->AddSimpleParticle( m_hParticleMaterial, vPos );
+ if ( pParticle )
+ {
+ // Move the points along the path.
+ pParticle->m_vecVelocity = points[iPrev+1] - points[iPrev];
+ VectorNormalize( pParticle->m_vecVelocity );
+ pParticle->m_vecVelocity *= PARTICLE_PATH_VEL;
+
+ pParticle->m_flRoll = 0;
+ pParticle->m_flRollDelta = 0;
+ pParticle->m_flDieTime = 0.2f;
+ pParticle->m_flLifetime = 0;
+ pParticle->m_uchColor[1] = 200;
+ pParticle->m_uchColor[0] = pParticle->m_uchColor[2] = RandomInt( 0, 128 );
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_uchStartSize = 5;
+ pParticle->m_uchEndSize = 3;
+ pParticle->m_iFlags = 0;
+ }
+ }
+}
+
+#endif \ No newline at end of file