diff options
Diffstat (limited to 'game/shared/tf2/weapon_repairgun.cpp')
| -rw-r--r-- | game/shared/tf2/weapon_repairgun.cpp | 740 |
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 |