//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================
#include "cbase.h"
#include "tf_weapon_minigun.h"
#include "decals.h"
#include "in_buttons.h"
#include "tf_fx_shared.h"
#include "debugoverlay_shared.h"
#include "tf_gamerules.h"

// Client specific.
#ifdef CLIENT_DLL
#include "c_tf_player.h"
#include "soundenvelope.h"
#include "achievementmgr.h"
#include "baseachievement.h"
#include "achievements_tf.h"
#include "prediction.h"
#include "clientmode_tf.h"
#include "bone_setup.h"
// NVNT haptics system interface
#include "haptics/ihaptics.h"
// Server specific.
#else
#include "tf_player.h"
#include "particle_parse.h"
#include "tf_gamestats.h"
#include "baseprojectile.h"
#endif

#define MAX_BARREL_SPIN_VELOCITY	20
#define TF_MINIGUN_SPINUP_TIME 0.75f
#define TF_MINIGUN_PENALTY_PERIOD 1.f

//=============================================================================
//
// Weapon Minigun tables.
//
IMPLEMENT_NETWORKCLASS_ALIASED( TFMinigun, DT_WeaponMinigun )

BEGIN_NETWORK_TABLE( CTFMinigun, DT_WeaponMinigun )
// Client specific.
#ifdef CLIENT_DLL
	RecvPropInt( RECVINFO( m_iWeaponState ) ),
	RecvPropBool( RECVINFO( m_bCritShot ) )
// Server specific.
#else
	SendPropInt( SENDINFO( m_iWeaponState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
	SendPropBool( SENDINFO( m_bCritShot ) )
#endif
END_NETWORK_TABLE()

#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CTFMinigun )
	DEFINE_FIELD(  m_iWeaponState, FIELD_INTEGER ),
END_PREDICTION_DATA()
#endif

LINK_ENTITY_TO_CLASS( tf_weapon_minigun, CTFMinigun );
PRECACHE_WEAPON_REGISTER( tf_weapon_minigun );


// Server specific.
#ifndef CLIENT_DLL
BEGIN_DATADESC( CTFMinigun )
END_DATADESC()
#endif

//=============================================================================
//
// Weapon Minigun functions.
//

//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CTFMinigun::CTFMinigun()
{
#ifdef CLIENT_DLL
	m_pSoundCur = NULL;

	m_hEjectBrassWeapon = NULL;
	m_pEjectBrassEffect = NULL;
	m_iEjectBrassAttachment = -1;

	m_hMuzzleEffectWeapon = NULL;
	m_pMuzzleEffect = NULL;
	m_iMuzzleAttachment = -1;

	m_nShotsFired = 0;

	ListenForGameEvent( "teamplay_round_active" );
	ListenForGameEvent( "localplayer_respawn" );

	m_bRageDraining = false;
	m_bPrevRageDraining = false;
#endif
	m_bAttack3Down = false;

	WeaponReset();
}

//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CTFMinigun::~CTFMinigun()
{
	WeaponReset();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::WeaponReset( void )
{
	BaseClass::WeaponReset();

	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( pPlayer )
	{
		pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
		pPlayer->TeamFortress_SetSpeed();

#ifdef GAME_DLL
		pPlayer->ClearWeaponFireScene();
		m_flAegisCheckTime = 0.0f;
#endif

		m_flNextRingOfFireAttackTime = 0.0f;
		m_flLastAmmoDrainTime = gpGlobals->curtime;
		m_flAccumulatedAmmoDrain = 0.0f;
	}

	SetWeaponState( AC_STATE_IDLE );
	m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
	m_bCritShot = false;
	m_flStartedFiringAt = -1.0f;
	m_flStartedWindUpAt = -1.f;
	m_flNextFiringSpeech = 0.0f;

	m_flBarrelAngle = 0.0f;

	m_flBarrelCurrentVelocity = 0.0f;
	m_flBarrelTargetVelocity = 0.0f;

#ifdef CLIENT_DLL
	if ( m_pSoundCur )
	{
		CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
		m_pSoundCur = NULL;
	}

	m_iMinigunSoundCur = -1;
	m_flMinigunSoundCurrentPitch = 1.0f;

	StopMuzzleEffect();
	StopBrassEffect();
#endif
}

#ifdef GAME_DLL
int CTFMinigun::UpdateTransmitState( void )
{
	// ALWAYS transmit to all clients.
	return SetTransmitState( FL_EDICT_ALWAYS );
}
#endif

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::Precache( void )
{
	PrecacheScriptSound( "Halloween.HeadlessBossAxeHitWorld" );

	// FIXME: Do we still need these??
	PrecacheScriptSound( "MVM.GiantHeavyGunWindUp" );
	PrecacheScriptSound( "MVM.GiantHeavyGunWindDown" );
	PrecacheScriptSound( "MVM.GiantHeavyGunFire" );
	PrecacheScriptSound( "MVM.GiantHeavyGunSpin" );

	BaseClass::Precache();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::ItemPostFrame( void )
{
	// Prevent base code from ever playing empty sounds, minigun handles them manually.
	m_flNextEmptySoundTime = gpGlobals->curtime + 1.0;

#ifdef GAME_DLL
	CBasePlayer *pOwner = GetPlayerOwner();
	if ( pOwner )
	{
		if ( ( pOwner->m_nButtons & IN_ATTACK3 ) && !m_bAttack3Down )
		{
			ActivatePushBackAttackMode();
			m_bAttack3Down = true;
		}
		else if ( !( pOwner->m_nButtons & IN_ATTACK3 ) && m_bAttack3Down )
		{
			m_bAttack3Down = false;
		}
	}
#endif // GAME_DLL

	BaseClass::ItemPostFrame();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::PrimaryAttack()
{
	SharedAttack();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::SharedAttack()
{
	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	if ( !CanAttack() )
	{
		WeaponIdle();
		return;
	}

#ifdef CLIENT_DLL
	m_bRageDraining = pPlayer->m_Shared.IsRageDraining();
#endif // CLIENT_DLL

	if ( pPlayer->m_nButtons & IN_ATTACK )
	{
		m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
	}
	else if ( pPlayer->m_nButtons & IN_ATTACK2 )
	{
		m_iWeaponMode = TF_WEAPON_SECONDARY_MODE;
	}

	switch ( m_iWeaponState )
	{
	default:
	case AC_STATE_IDLE:
		{
			// Removed the need for cells to powerup the AC
			WindUp();

			float flSpinUpTime = TF_MINIGUN_SPINUP_TIME;
			CALL_ATTRIB_HOOK_FLOAT( flSpinUpTime, mult_minigun_spinup_time );

			float flSpinTimeMultiplier = Max( flSpinUpTime, 0.00001f );
			if ( pPlayer->GetViewModel( 0 ) )
			{
				pPlayer->GetViewModel( 0 )->SetPlaybackRate( TF_MINIGUN_SPINUP_TIME / flSpinTimeMultiplier );
			}
			if ( pPlayer->GetViewModel( 1 ) )
			{
				pPlayer->GetViewModel( 1 )->SetPlaybackRate( TF_MINIGUN_SPINUP_TIME / flSpinTimeMultiplier );
			}

			m_flNextPrimaryAttack = gpGlobals->curtime + flSpinUpTime;
			m_flNextSecondaryAttack = gpGlobals->curtime + flSpinUpTime;
			m_flTimeWeaponIdle = gpGlobals->curtime + flSpinUpTime;
			m_flStartedFiringAt = -1.f;
			pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRE );
			break;
		}
	case AC_STATE_STARTFIRING:
		{
			// Start playing the looping fire sound
			if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
			{
				if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
				{
					SetWeaponState( AC_STATE_SPINNING );
				}
				else
				{
					SetWeaponState( AC_STATE_FIRING );
				}

#ifdef GAME_DLL
				if ( m_iWeaponState == AC_STATE_SPINNING )
				{
					pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN );
				}
				else
				{
					pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN );
				}
#endif

				m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1;
			}
			break;
		}
	case AC_STATE_FIRING:
		{
			if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
			{
				SetWeaponState( AC_STATE_SPINNING );
			}

			if ( m_iWeaponState == AC_STATE_SPINNING )
			{
#ifdef GAME_DLL
				pPlayer->ClearWeaponFireScene();
				pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN );
#endif
				m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1;

			}
			else if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
			{
				SetWeaponState( AC_STATE_DRYFIRE );
			}
			else
			{
				if ( m_flStartedFiringAt < 0 )
				{
					m_flStartedFiringAt = gpGlobals->curtime;
				}

#ifdef GAME_DLL
				if ( m_flNextFiringSpeech < gpGlobals->curtime )
				{
					m_flNextFiringSpeech = gpGlobals->curtime + 5.0;
					pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_MINIGUN_FIREWEAPON );
				}
#endif

#ifdef CLIENT_DLL
				int nAmmo = 0;
				if ( prediction->IsFirstTimePredicted() && 
					 C_BasePlayer::GetLocalPlayer() == pPlayer )
				{
					nAmmo = pPlayer->GetAmmoCount( m_iPrimaryAmmoType );
				}
#endif

				// Only fire if we're actually shooting
				BaseClass::PrimaryAttack();		// fire and do timers
				
#ifdef CLIENT_DLL
				if ( prediction->IsFirstTimePredicted() && 
					 C_BasePlayer::GetLocalPlayer() == pPlayer &&
					 nAmmo != pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ) // did PrimaryAttack() fire a shot? (checking our ammo to find out)
				{
					m_nShotsFired++;
					if ( m_nShotsFired == 1000 ) // == and not >= so we don't keep awarding this every shot after it's achieved
					{
						g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_FIRE_LOTS );
					}
					// NVNT the local player fired a shot. notify the haptics system.
					if ( haptics )
						haptics->ProcessHapticEvent(2,"Weapons","minigun_fire");
				}
#endif
				CalcIsAttackCritical();
				m_bCritShot = IsCurrentAttackACrit();
				pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );

#ifdef GAME_DLL

				int iAttackProjectiles = 0;
				CALL_ATTRIB_HOOK_INT( iAttackProjectiles, attack_projectiles );

#ifdef TF_RAID_MODE
				if ( TFGameRules()->IsBossBattleMode() )
				{
					iAttackProjectiles = 1;
				}
#endif // TF_RAID_MODE

				if ( iAttackProjectiles )
				{
					AttackEnemyProjectiles();
				}

#endif // GAME_DLL

				m_flTimeWeaponIdle = gpGlobals->curtime + 0.2;
			}
			break;
		}
	case AC_STATE_DRYFIRE:
		{
			m_flStartedFiringAt = -1.f;
			m_flStartedWindUpAt = -1.f;

			if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 )
			{
				SetWeaponState( AC_STATE_FIRING );
			}
			else if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
			{
				SetWeaponState( AC_STATE_SPINNING );
			}
			SendWeaponAnim( ACT_VM_SECONDARYATTACK );
			break;
		}
	case AC_STATE_SPINNING:
		{
			m_flStartedFiringAt = -1.f;

			if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE )
			{
				if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 )
				{
#ifdef GAME_DLL
					pPlayer->ClearWeaponFireScene();
					pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN );
#endif
					SetWeaponState( AC_STATE_FIRING );
				}
				else
				{
					SetWeaponState( AC_STATE_DRYFIRE );
				}
			}

			SendWeaponAnim( ACT_VM_SECONDARYATTACK );
			break;
		}
	}

	if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
	{
		if ( m_iWeaponState > AC_STATE_STARTFIRING )
		{
			int nRingOfFireWhileAiming = 0;
			CALL_ATTRIB_HOOK_INT( nRingOfFireWhileAiming, ring_of_fire_while_aiming );
			if ( nRingOfFireWhileAiming != 0 )
			{
				RingOfFireAttack( nRingOfFireWhileAiming );
			}
		}

		if ( m_iWeaponState == AC_STATE_SPINNING || m_iWeaponState == AC_STATE_FIRING )
		{
			int nUsesAmmoWhileAiming = 0;
			CALL_ATTRIB_HOOK_INT( nUsesAmmoWhileAiming, uses_ammo_while_aiming );
			if ( nUsesAmmoWhileAiming > 0 )
			{
				m_flAccumulatedAmmoDrain += nUsesAmmoWhileAiming * ( gpGlobals->curtime - m_flLastAmmoDrainTime );
				m_flLastAmmoDrainTime = gpGlobals->curtime;

				if ( m_flAccumulatedAmmoDrain > 1.0f )
				{
					int nAmmoRemoved = m_flAccumulatedAmmoDrain;
					pPlayer->RemoveAmmo( nAmmoRemoved, m_iPrimaryAmmoType );

					m_flAccumulatedAmmoDrain -= nAmmoRemoved;
				}
			}
		}
	}
}

void CTFMinigun::SetWeaponState( MinigunState_t nState )
{
	if ( m_iWeaponState != nState )
	{
		if ( m_iWeaponState == AC_STATE_IDLE || m_iWeaponState == AC_STATE_STARTFIRING || m_iWeaponState == AC_STATE_DRYFIRE )
		{
			// Transitioning from non firing or non fully spinning states resets when our drain start point and when the ring of fire can start
			m_flLastAmmoDrainTime = gpGlobals->curtime;
			m_flNextRingOfFireAttackTime = gpGlobals->curtime + 0.5f;
		}
		
		m_iWeaponState = nState;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Fall through to Primary Attack
//-----------------------------------------------------------------------------
void CTFMinigun::SecondaryAttack( void )
{
	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	SharedAttack();
}

void CTFMinigun::RingOfFireAttack( int nDamage )
{
	if ( m_flNextRingOfFireAttackTime == 0.0f || m_flNextRingOfFireAttackTime > gpGlobals->curtime )
		return;

	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

#ifdef GAME_DLL

	Vector vOrigin = pPlayer->GetAbsOrigin();
	const float flFireRadius = 135.0f;
	const float flFireRadiusSqr = flFireRadius * flFireRadius;

	CBaseEntity *pEntity = NULL;
	for ( CEntitySphereQuery sphere( vOrigin, flFireRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
	{
		// Skip players on the same team or who are invuln
		CTFPlayer *pVictim = ToTFPlayer( pEntity );
		if ( !pVictim || InSameTeam( pVictim ) || pVictim->m_Shared.InCond( TF_COND_INVULNERABLE ) )
			continue;

		// Make sure their bounding box is near our ground plane
		Vector vMins = pVictim->GetPlayerMins();
		Vector vMaxs = pVictim->GetPlayerMaxs();
		if ( !( vOrigin.z > pVictim->GetAbsOrigin().z + vMins.z - 32.0f && vOrigin.z < pVictim->GetAbsOrigin().z + vMaxs.z ) )
		{
			continue;
		}

		// CEntitySphereQuery actually does a box test. So we need to make sure the distance is less than the radius first.
		Vector vecPos;
		pEntity->CollisionProp()->CalcNearestPoint( vOrigin, &vecPos );
		if ( ( vOrigin - vecPos ).LengthSqr() > flFireRadiusSqr )
			continue;

		// Finally LOS test
		trace_t	tr;
		Vector vecSrc = WorldSpaceCenter();
		Vector vecSpot = pEntity->WorldSpaceCenter();
		CTraceFilterSimple filter( this, COLLISION_GROUP_PROJECTILE );
		UTIL_TraceLine( vecSrc, vecSpot, MASK_SOLID_BRUSHONLY, &filter, &tr );

		// If we don't trace the whole way to the target, and we didn't hit the target entity, we're blocked
		if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
			continue;

		pVictim->TakeDamage( CTakeDamageInfo( pPlayer, pPlayer, this, vec3_origin, vOrigin, nDamage, DMG_PLASMA, 0, &vOrigin ) );
	}

	DispatchParticleEffect( "heavy_ring_of_fire", pPlayer->GetAbsOrigin(), vec3_angle );

#else

	DispatchParticleEffect( "heavy_ring_of_fire_fp", pPlayer->GetAbsOrigin(), vec3_angle );

#endif // #ifdef GAME_DLL

	m_flNextRingOfFireAttackTime = gpGlobals->curtime + 0.5f;
}

#ifdef GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:  Scans along a line for rockets and grenades to destroy
//-----------------------------------------------------------------------------
void CTFMinigun::AttackEnemyProjectiles( void )
{
	if ( gpGlobals->curtime < m_flAegisCheckTime )
		return;

	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	// Parameters
	const int nSweepDist = 300;	// How far out
	const int nHitDist = ( pPlayer->IsMiniBoss() ) ? 56 : 38;	// How far from the center line (radial)
	float flRechargeTime = 0.1f;

	// Pos
	const Vector &vecGunPos = ( pPlayer->IsMiniBoss() ) ? pPlayer->Weapon_ShootPosition() : pPlayer->EyePosition();
	Vector vecForward;
	AngleVectors( GetAbsAngles(), &vecForward );
	Vector vecGunAimEnd = vecGunPos + vecForward * (float)nSweepDist;

	bool bDebug = false;
	if ( bDebug )
	{
		// NDebugOverlay::Sphere( vecGunPos + vecForward * nSweepDist, nSweepDist, 0, 255, 0, 40, 5 );
		NDebugOverlay::Box( vecGunPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 0, 40, 5 );
		NDebugOverlay::Box( vecGunAimEnd, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 0, 40, 5 );
		NDebugOverlay::Line( vecGunPos, vecGunAimEnd, 255, 255, 255, true, 5 );
	}

	// Iterate through each grenade/rocket in the sphere
	const int nMaxEnts = 32;
	CBaseEntity	*pObjects[ nMaxEnts ];
	int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecGunPos, nSweepDist, FL_GRENADE );
	for ( int i = 0; i < nCount; i++ )
	{
		if ( InSameTeam( pObjects[i] ) )
			continue;

		// Hit?
		const Vector &vecGrenadePos = pObjects[i]->GetAbsOrigin();
		float flDistToLine = CalcDistanceToLineSegment( vecGrenadePos, vecGunPos, vecGunAimEnd );
		if ( flDistToLine <= nHitDist )
		{
			if ( pPlayer->FVisible( pObjects[i], MASK_SOLID ) == false )
				continue;

			if ( ( pObjects[i]->GetFlags() & FL_ONGROUND ) )
				continue;
				
			if ( !pObjects[i]->IsDeflectable() )
				continue;

			CBaseProjectile *pProjectile = dynamic_cast< CBaseProjectile* >( pObjects[i] );
			if ( pProjectile && pProjectile->IsDestroyable() )
			{
				pProjectile->IncrementDestroyableHitCount();

				if ( bDebug )
				{
					NDebugOverlay::Box( vecGrenadePos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 255, 40, 5 );
				}

				// Did we destroy it?
				int iAttackProjectiles = 0;
				CALL_ATTRIB_HOOK_INT( iAttackProjectiles, attack_projectiles );
				int nHitsRequired = m_bCritShot ? 1 : (int)RemapValClamped( iAttackProjectiles, 1, 2, 2, 1 );
				if ( pProjectile->GetDestroyableHitCount() >= nHitsRequired )
				{
					pProjectile->Destroy( false, true );

					EmitSound( "Halloween.HeadlessBossAxeHitWorld" );

					CTF_GameStats.Event_PlayerAwardBonusPoints( pPlayer, NULL, 2 );

					// Weaker version has a longer cooldown
					if ( iAttackProjectiles < 2 )
					{
						flRechargeTime = 0.3f;
					}
				}
				else
				{
					// Nicked it
					pObjects[i]->EmitSound( "FX_RicochetSound.Ricochet" );
				}
			}
		}
	}

	m_flAegisCheckTime = gpGlobals->curtime + flRechargeTime;
}

//-----------------------------------------------------------------------------
// Purpose:  Reduces damage and adds extra knockback.
//-----------------------------------------------------------------------------
void CTFMinigun::ActivatePushBackAttackMode( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
	if ( !pOwner )
		return;

	int iRage = 0;
	CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRage, generate_rage_on_dmg );
	if ( !iRage )
		return;

	if ( pOwner->m_Shared.IsRageDraining() )
		return;

	if ( pOwner->m_Shared.GetRageMeter() < 100.f )
	{
		pOwner->EmitSound( "Player.DenyWeaponSelection" );
		return;
	}

	pOwner->m_Shared.StartRageDrain();
	EmitSound( "Heavy.Battlecry03" );
}
#endif

//-----------------------------------------------------------------------------
// Purpose: UI Progress (same as GetProgress() without the division by 100.0f)
//-----------------------------------------------------------------------------
bool CTFMinigun::IsRageFull( void )
{
	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if ( !pPlayer )
		return false;

	return ( pPlayer->m_Shared.GetRageMeter() >= 100.0f );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMinigun::EffectMeterShouldFlash( void )
{
	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if ( !pPlayer )
		return false;

	if ( pPlayer && ( IsRageFull() || pPlayer->m_Shared.IsRageDraining() ) )
		return true;
	else
		return false;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMinigun::CanInspect() const
{
	return BaseClass::CanInspect() && CanHolster();
}

//-----------------------------------------------------------------------------
// Purpose: UI Progress
//-----------------------------------------------------------------------------
float CTFMinigun::GetProgress( void )
{
	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if ( !pPlayer )
		return 0.f;

	return pPlayer->m_Shared.GetRageMeter() / 100.0f;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::WindUp( void )
{
	// Get the player owning the weapon.
	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	// Play wind-up animation and sound (SPECIAL1).
	SendWeaponAnim( ACT_MP_ATTACK_STAND_PREFIRE );

	// Set the appropriate firing state.
	SetWeaponState( AC_STATE_STARTFIRING );
	pPlayer->m_Shared.AddCond( TF_COND_AIMING );

#ifndef CLIENT_DLL
	pPlayer->StopRandomExpressions();
#endif

#ifdef CLIENT_DLL 
	WeaponSoundUpdate();
#endif

	// Update player's speed
	pPlayer->TeamFortress_SetSpeed();

	if ( m_flStartedWindUpAt == -1.f )
	{
		m_flStartedWindUpAt = gpGlobals->curtime;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFMinigun::CanHolster( void ) const
{
	bool bCanHolster = CanHolsterWhileSpinning();

	CTFPlayer *pPlayer = GetTFPlayerOwner();
	if( pPlayer )
	{
		// PASSTIME need to be able to immediately holster when you catch the ball
		if ( pPlayer->m_Shared.HasPasstimeBall() )
			return true;

		// TF_COND_MELEE_ONLY need to be able to immediately holster and switch to melee weapon
		if ( pPlayer->m_Shared.InCond( TF_COND_MELEE_ONLY ) )
			return true;
	}

#ifdef STAGING_ONLY
	// Agility powerup allows holstering while spinning
	bCanHolster |= ( pPlayer && pPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY );
#endif //STAGING_ONLY

	if ( bCanHolster )
	{
		if ( m_iWeaponState == AC_STATE_STARTFIRING || m_iWeaponState == AC_STATE_FIRING )
			return false;
	}
	else
	{
		if ( m_iWeaponState > AC_STATE_IDLE )
			return false;

		if ( GetActivity() == ACT_MP_ATTACK_STAND_POSTFIRE || GetActivity() == ACT_PRIMARY_ATTACK_STAND_POSTFIRE )
		{
			if ( !IsViewModelSequenceFinished() )
				return false;
		}
	}

	return BaseClass::CanHolster();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMinigun::Holster( CBaseCombatWeapon *pSwitchingTo )
{
	if ( m_iWeaponState > AC_STATE_IDLE )
	{
		WindDown();
	}

	return BaseClass::Holster( pSwitchingTo );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFMinigun::Lower( void )
{
	if ( m_iWeaponState > AC_STATE_IDLE )
	{
		WindDown();
	}

	return BaseClass::Lower();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::WindDown( void )
{
	// Get the player owning the weapon.
	CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
	if ( !pPlayer )
		return;

	SendWeaponAnim( ACT_MP_ATTACK_STAND_POSTFIRE );

#ifdef CLIENT_DLL
	if ( !HasSpinSounds() && m_iWeaponState == AC_STATE_FIRING )
	{
		PlayStopFiringSound();
	}
#endif

	// Set the appropriate firing state.
	SetWeaponState( AC_STATE_IDLE );
	pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
#ifdef CLIENT_DLL
	WeaponSoundUpdate();
#else
	pPlayer->ClearWeaponFireScene();
#endif

	// Time to weapon idle.
	m_flTimeWeaponIdle = gpGlobals->curtime + 2.0;

	// Update player's speed
	pPlayer->TeamFortress_SetSpeed();

#ifdef CLIENT_DLL
	m_flBarrelTargetVelocity = 0;

	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( pLocalPlayer && GetOwner() == pLocalPlayer )
	{
		IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_winddown" );
		if ( event )
		{
			gameeventmanager->FireEventClientSide( event );
		}
	}
#endif

	m_flStartedWindUpAt = -1.f;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::WeaponIdle()
{
	if ( gpGlobals->curtime < m_flTimeWeaponIdle )
		return;

	// Always wind down if we've hit here, because it only happens when the player has stopped firing/spinning
	if ( m_iWeaponState != AC_STATE_IDLE )
	{
		CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
		if ( pPlayer )
		{
			pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_POST );
		}

		WindDown();
		return;
	}

	BaseClass::WeaponIdle();

	m_flTimeWeaponIdle = gpGlobals->curtime + 12.5;// how long till we do this again.
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::FireGameEvent( IGameEvent * event )
{
#ifdef CLIENT_DLL
	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
	if ( pLocalPlayer && GetOwner() == pLocalPlayer )
	{
		if ( FStrEq( event->GetName(), "teamplay_round_active" ) ||
			 FStrEq( event->GetName(), "localplayer_respawn" ) )
		{
			m_nShotsFired = 0;
		}
	}

	BaseClass::FireGameEvent( event );
#endif
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFMinigun::SendWeaponAnim( int iActivity )
{
#ifdef CLIENT_DLL
	// Client procedurally animates the barrel bone
	if ( iActivity == ACT_MP_ATTACK_STAND_PRIMARYFIRE || iActivity == ACT_MP_ATTACK_STAND_PREFIRE )
	{
		m_flBarrelTargetVelocity = MAX_BARREL_SPIN_VELOCITY;
	}
	else if ( iActivity == ACT_MP_ATTACK_STAND_POSTFIRE )
	{
		m_flBarrelTargetVelocity = 0;
	}

#endif


	// When we start firing, play the startup firing anim first
	if ( iActivity == ACT_VM_PRIMARYATTACK )
	{
		// If we're already playing the fire anim, let it continue. It loops.
		if ( GetActivity() == ACT_VM_PRIMARYATTACK )
			return true;

		// Otherwise, play the start it
		return BaseClass::SendWeaponAnim( ACT_VM_PRIMARYATTACK );		
	}

	return BaseClass::SendWeaponAnim( iActivity );
}

//-----------------------------------------------------------------------------
// Purpose: This will force the minigun to turn off the firing sound and play the spinning sound
//-----------------------------------------------------------------------------
void CTFMinigun::HandleFireOnEmpty( void )
{
	if ( m_iWeaponState == AC_STATE_FIRING || m_iWeaponState == AC_STATE_SPINNING )
	{
		 SetWeaponState( AC_STATE_DRYFIRE );

		 SendWeaponAnim( ACT_VM_SECONDARYATTACK );

		 if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
		 {
			SetWeaponState ( AC_STATE_SPINNING );
		 }
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CTFMinigun::GetProjectileDamage( void )
{
	float flDamage = BaseClass::GetProjectileDamage();

	if ( GetFiringDuration() < TF_MINIGUN_PENALTY_PERIOD )
	{
		float flMod = 1.f;
		flMod = RemapValClamped( GetFiringDuration(), 0.2f, TF_MINIGUN_PENALTY_PERIOD, 0.5f, 1.f );
		flDamage *= flMod;
	}
	
	return flDamage;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CTFMinigun::GetWeaponSpread( void )
{
	float flSpread = BaseClass::GetWeaponSpread();

	// How long have we been spun up - sans the min period required to fire
	float flPreFireWindUp = GetWindUpDuration() - TF_MINIGUN_SPINUP_TIME;
	// DevMsg( "PreFireTime: %.2f\n", flPreFireWindUp );

	if ( GetFiringDuration() < TF_MINIGUN_PENALTY_PERIOD && flPreFireWindUp < 1.f )
	{
		// If we've spun up - prior to pressing fire - reduce accuracy penalty
		float flSpinTime = Max( flPreFireWindUp, GetFiringDuration() );
		const float flMaxSpread = 1.5f;
		float flMod = RemapValClamped( flSpinTime, 0.f, TF_MINIGUN_PENALTY_PERIOD, flMaxSpread, 1.f );
		// DevMsg( "SpreadMod: %.2f\n", flMod );

		flSpread *= flMod;
	}
	
	return flSpread;
}


#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CStudioHdr *CTFMinigun::OnNewModel( void )
{
	CStudioHdr *hdr = BaseClass::OnNewModel();

	m_iBarrelBone = LookupBone( "barrel" );

	// skip resetting this while recording in the tool
	// we change the weapon to the worldmodel and back to the viewmodel when recording
	// which causes the minigun to not spin while recording
	if ( !IsToolRecording() )
	{
		m_flBarrelAngle = 0;

		m_flBarrelCurrentVelocity = 0;
		m_flBarrelTargetVelocity = 0;
	}

	return hdr;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
{
	BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );

	if (m_iBarrelBone != -1)
	{
		UpdateBarrelMovement();

		// Weapon happens to be aligned to (0,0,0)
		// If that changes, use this code block instead to
		// modify the angles

		/*
		RadianEuler a;
		QuaternionAngles( q[iBarrelBone], a );

		a.x = m_flBarrelAngle;

		AngleQuaternion( a, q[iBarrelBone] );
		*/

		AngleQuaternion( RadianEuler( 0, 0, m_flBarrelAngle ), q[m_iBarrelBone] );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Updates the velocity and position of the rotating barrel
//-----------------------------------------------------------------------------
void CTFMinigun::UpdateBarrelMovement()
{
	if ( m_flBarrelCurrentVelocity != m_flBarrelTargetVelocity )
	{
		float flBarrelAcceleration = CanHolsterWhileSpinning() ? 0.5f : 0.1f;

		// update barrel velocity to bring it up to speed or to rest
		m_flBarrelCurrentVelocity = Approach( m_flBarrelTargetVelocity, m_flBarrelCurrentVelocity, flBarrelAcceleration );

		if ( 0 == m_flBarrelCurrentVelocity )
		{	
			// if we've stopped rotating, turn off the wind-down sound
			WeaponSoundUpdate();
		}
	}

	// update the barrel rotation based on current velocity
	m_flBarrelAngle += m_flBarrelCurrentVelocity * gpGlobals->frametime;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::OnDataChanged( DataUpdateType_t updateType )
{
	// Brass ejection and muzzle flash.
	HandleBrassEffect();
	HandleMuzzleEffect();

	BaseClass::OnDataChanged( updateType );

	WeaponSoundUpdate();
	
	// Turn off the firing sound here for the Tomislav
	if( m_iPrevMinigunState == AC_STATE_FIRING && 
		( m_iWeaponState == AC_STATE_SPINNING || m_iWeaponState == AC_STATE_IDLE ) )
	{
		if ( !HasSpinSounds() )
		{
			PlayStopFiringSound();
		}
	}

	m_iPrevMinigunState = m_iWeaponState;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::UpdateOnRemove( void )
{
	if ( m_pSoundCur )
	{
		CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
		m_pSoundCur = NULL;
	}

	// Force the particle system off.
	StopMuzzleEffect();
	StopBrassEffect();

	BaseClass::UpdateOnRemove();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::SetDormant( bool bDormant )
{
	// If I'm going from active to dormant and I'm carried by another player, stop our firing sound.
	if ( !IsCarriedByLocalPlayer() )
	{
		// Am I firing? Stop the firing sound.
		if ( !IsDormant() && bDormant && m_iWeaponState >= AC_STATE_FIRING )
		{
			WeaponSoundUpdate();
		}

		// If firing and going dormant - stop the brass effect.
		if ( !IsDormant() && bDormant && m_iWeaponState != AC_STATE_IDLE )
		{
			StopMuzzleEffect();
			StopBrassEffect();
		}
	}

	// Deliberately skip base combat weapon
	C_BaseEntity::SetDormant( bDormant );
}


//-----------------------------------------------------------------------------
// Purpose: 
// won't be called for w_ version of the model, so this isn't getting updated twice
//-----------------------------------------------------------------------------
void CTFMinigun::ItemPreFrame( void )
{
	UpdateBarrelMovement();
	BaseClass::ItemPreFrame();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::StartBrassEffect()
{
	StopBrassEffect();

	m_hEjectBrassWeapon = GetWeaponForEffect();
	if ( !m_hEjectBrassWeapon )
		return;

	if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
	{
		// Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
		return;
	}

	// Try and setup the attachment point if it doesn't already exist.
	// This caching will mess up if we go third person from first - we only do this in taunts and don't fire so we should
	// be okay for now.
	if ( m_iEjectBrassAttachment == -1 )
	{
		m_iEjectBrassAttachment = m_hEjectBrassWeapon->LookupAttachment( "eject_brass" );
	}

	// Start the brass ejection, if a system hasn't already been started.
	if ( m_iEjectBrassAttachment > 0 && m_pEjectBrassEffect == NULL )
	{
		m_pEjectBrassEffect = m_hEjectBrassWeapon->ParticleProp()->Create( "eject_minigunbrass", PATTACH_POINT_FOLLOW, m_iEjectBrassAttachment );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::StartMuzzleEffect()
{
	StopMuzzleEffect();

	m_hMuzzleEffectWeapon = GetWeaponForEffect();
	if ( !m_hMuzzleEffectWeapon )
		return;

	if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
	{
		// Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
		return;
	}

	// Try and setup the attachment point if it doesn't already exist.
	// This caching will mess up if we go third person from first - we only do this in taunts and don't fire so we should
	// be okay for now.
	if ( m_iMuzzleAttachment <= 0 )
	{
		m_iMuzzleAttachment = m_hMuzzleEffectWeapon->LookupAttachment( "muzzle" );
	}

	// Start the muzzle flash, if a system hasn't already been started.
	if ( m_iMuzzleAttachment > 0 && m_pMuzzleEffect == NULL )
	{
		m_pMuzzleEffect = m_hMuzzleEffectWeapon->ParticleProp()->Create( "muzzle_minigun_constant", PATTACH_POINT_FOLLOW, m_iMuzzleAttachment );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::StopBrassEffect()
{
	if ( !m_hEjectBrassWeapon )
		return;

	// Stop the brass ejection.
	if ( m_pEjectBrassEffect )
	{
		m_hEjectBrassWeapon->ParticleProp()->StopEmission( m_pEjectBrassEffect );
		m_hEjectBrassWeapon = NULL;
		m_pEjectBrassEffect = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::StopMuzzleEffect()
{
	if ( !m_hMuzzleEffectWeapon )
		return;

	// Stop the muzzle flash.
	if ( m_pMuzzleEffect )
	{
		m_hMuzzleEffectWeapon->ParticleProp()->StopEmission( m_pMuzzleEffect );
		m_hMuzzleEffectWeapon = NULL;
		m_pMuzzleEffect		  = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::HandleBrassEffect()
{
	if ( m_iWeaponState == AC_STATE_FIRING && m_pEjectBrassEffect == NULL )
	{
		StartBrassEffect();
	}
	else if ( m_iWeaponState != AC_STATE_FIRING && m_pEjectBrassEffect )
	{
		StopBrassEffect();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::HandleMuzzleEffect()
{
	if ( m_iWeaponState == AC_STATE_FIRING && m_pMuzzleEffect == NULL )
	{
		StartMuzzleEffect();
	}
	else if ( m_iWeaponState != AC_STATE_FIRING && m_pMuzzleEffect )
	{
		StopMuzzleEffect();
	}
}

//-----------------------------------------------------------------------------
// Purpose: View model barrel rotation angle. Calculated here, implemented in 
// tf_viewmodel.cpp
//-----------------------------------------------------------------------------
float CTFMinigun::GetBarrelRotation( void )
{
	return m_flBarrelAngle;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFMinigun::ViewModelAttachmentBlending( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
{
	int iBarrelBone = Studio_BoneIndexByName( hdr, "barrel" );

	// Assert( iBarrelBone != -1 );

	if ( iBarrelBone != -1 )
	{
		if ( hdr->boneFlags( iBarrelBone ) & boneMask )
		{
			RadianEuler a;
			QuaternionAngles( q[iBarrelBone], a );

			a.z = GetBarrelRotation();

			AngleQuaternion( a, q[iBarrelBone] );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMinigun::CreateMove( float flInputSampleTime, CUserCmd *pCmd, const QAngle &vecOldViewAngles )
{
	// Prevent jumping while firing
	if ( m_iWeaponState != AC_STATE_IDLE )
	{
		pCmd->buttons &= ~IN_JUMP;
	}

	BaseClass::CreateMove( flInputSampleTime, pCmd, vecOldViewAngles );
}

//-----------------------------------------------------------------------------
// Purpose: Ensures the correct sound (including silence) is playing for 
//			current weapon state.
//-----------------------------------------------------------------------------
void CTFMinigun::WeaponSoundUpdate()
{
	// determine the desired sound for our current state
	int iSound = -1;
	switch ( m_iWeaponState )
	{
	case AC_STATE_IDLE:
		if ( !HasSpinSounds() && m_iMinigunSoundCur == SPECIAL2 )
		{
			// Don't turn off SPECIAL2 (stop firing sound) for non spinning miniguns.
			// We don't have a wind-down sound.
			return;
		}
		else if ( HasSpinSounds() && m_flBarrelCurrentVelocity > 0 )
		{
			iSound = SPECIAL2;	// wind down sound

			if ( m_flBarrelTargetVelocity > 0 )
			{
				m_flBarrelTargetVelocity = 0;
			}
		}
		else
		{
			iSound = -1;
		}
		break;
	case AC_STATE_STARTFIRING:
			iSound = SPECIAL1;	// wind up sound
		break;
	case AC_STATE_FIRING:
		{
			if ( m_bCritShot == true ) 
			{
				iSound = BURST;	// Crit sound
			}
			else
			{
				iSound = WPN_DOUBLE; // firing sound
			}
		}
		break;
	case AC_STATE_SPINNING:
		if ( HasSpinSounds() )
			iSound = SPECIAL3;	// spinning sound
		else
			return;
		break;
	case AC_STATE_DRYFIRE:
		iSound = EMPTY;		// out of ammo, still trying to fire
		break;
	default:
		Assert( false );
		break;
	}

	// Get the pitch we should play at
	float flPitch = 1.0f;

	float flSpeed = ApplyFireDelay( 1.0f );
	if ( flSpeed != 1.0f )
	{
		flPitch = RemapValClamped( flSpeed, 1.5f, 0.5f, 80.f, 120.f );
	}

	if ( m_bRageDraining )
	{
		flPitch /= 1.65;
	}

	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();

	// if we're already playing the desired sound, nothing to do
	if ( m_iMinigunSoundCur == iSound && m_bPrevRageDraining == m_bRageDraining )
	{
		// If the pitch is different we need to modify it
		if ( m_flMinigunSoundCurrentPitch != flPitch )
		{
			m_flMinigunSoundCurrentPitch = flPitch;

			if ( m_pSoundCur )
			{
				controller.SoundChangePitch( m_pSoundCur, m_flMinigunSoundCurrentPitch, 0.3f );
			}
		}
		return;
	}

	// if we're playing some other sound, stop it
	if ( m_pSoundCur )
	{
		// Stop the previous sound immediately
		CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
		m_pSoundCur = NULL;
	}
	m_iMinigunSoundCur = iSound;
	// if there's no sound to play for current state, we're done
	if ( -1 == iSound )
		return;

	m_flMinigunSoundCurrentPitch = flPitch;

	// play the appropriate sound
	const char *shootsound = GetShootSound( iSound );
	CLocalPlayerFilter filter;
	m_pSoundCur = controller.SoundCreate( filter, entindex(), shootsound );
	controller.Play( m_pSoundCur, 1.0, 100 );
	controller.SoundChangeVolume( m_pSoundCur, 1.0, 0.1 );

	if ( m_flMinigunSoundCurrentPitch != 1.0f )
	{
		controller.SoundChangePitch( m_pSoundCur, m_flMinigunSoundCurrentPitch, 0.0 );
	}

	m_bPrevRageDraining = m_bRageDraining;
}

void CTFMinigun::PlayStopFiringSound()
{
	if ( m_pSoundCur )
	{
		CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
		m_pSoundCur = NULL;
	}

	m_iMinigunSoundCur = SPECIAL2;

	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	const char *shootsound = GetShootSound( SPECIAL2 );
	CLocalPlayerFilter filter;
	m_pSoundCur = controller.SoundCreate( filter, entindex(), shootsound );
	controller.Play( m_pSoundCur, 1.0, 100 );
	controller.SoundChangeVolume( m_pSoundCur, 1.0, 0.1 );
}

#endif