// Half-Life Tau Cannon. Can be configurable to use Multiplayer rules or Single Player rules. Also have some custom sounds and will be using all animations.
// Special Thanks to Nero...
// Tau Cannon Mk2 from Half-Life: Insecure.
// Credits to Nevermore2790

//#include "sven_scripted_utils"

namespace SVEN_GAUSS_MK2
{
	enum GAUSS_MK2_ANIMATIONS
	{
		GAUSS_MK2_IDLE,
		GAUSS_MK2_IDLE2,
		GAUSS_MK2_FIDGET,
		GAUSS_MK2_SPINUP,
		GAUSS_MK2_SPIN,
		GAUSS_MK2_FIRE1,
		GAUSS_MK2_FIRE2,
		GAUSS_MK2_HOLSTER,
		GAUSS_MK2_DRAW
	};
	
	// Models
	const string P_MODEL				= "models/sc_insecure/p_gauss.mdl";
	const string V_MODEL				= "models/sc_insecure/v_gauss.mdl";
	const string W_MODEL				= "models/sc_insecure/w_gauss.mdl";

	const string P_MODEL_CLASSIC		= "models/sc_insecure/hlclassic/p_gauss.mdl";
	const string V_MODEL_CLASSIC		= "models/sc_insecure/hlclassic/v_gauss.mdl";
	const string W_MODEL_CLASSIC		= "models/sc_insecure/hlclassic/w_gauss.mdl";
	
	// Sprites
	const string SPR_GLOW		= "sprites/hotglow.spr";
	const string SPR_BALLS		= SPR_GLOW;
	const string SPR_BEAM		= "sprites/zbeam2.spr";
	const string SPR_BEAM_02	= "sprites/xenobeam.spr";
	const string SPR_BEAM_03	= "sprites/zbeam1.spr";
	const string SPR_FIREBALL	= "sprites/zerogxplode.spr";
	const string SPR_DIR			= "insecure/";

	// Sounds

	const string FIRE_SND		= "insecure/weapons/gauss2.wav";
	const string CHARGE_SND		= "ambience/pulsemachine.wav";
	
	array<string> GaussMk2ElectroEvents = {
		"weapons/electro4.wav", //0
		"weapons/electro5.wav", //1
		"weapons/electro6.wav" //2
	};
	
	array<string> GaussMk2ExplodeEvents = { 
		"weapons/explode3.wav",				//0
		"weapons/explode4.wav",				//1
		"weapons/explode5.wav"				//2
	};

//	const string SND_GAUSS_MK2_BEAM			= "debris/beamstart4.wav";
//	const string SND_GAUSS_MK2_CHARGED 		= "weapons/gauss_mk2/gauss_mk2_charged.wav";
	const string SND_GAUSS_MK2_BEEP 			= "buttons/blip2.wav";
//	const string SND_GAUSS_MK2_OVERCHARGED	= "weapons/gauss_overcharged.wav";
//	const string SND_GAUSS_MK2_LOWAMMO 		= "fvox/ammowarning.wav";
	const string SND_GAUSS_MK2_EMPTY 		= "weapons/357_cock1.wav";
	
	// Weapon information
	const int MAX_CARRY    						= 100;
	const int DEFAULT_GIVE 						= 20;
	const int WEIGHT       						= 20;
	const int FLAGS								= 0;
	const float DAMAGE_PRIMARY 					= g_EngineFuncs.CVarGetFloat( "sk_plr_gauss" ) * 4.0;	//100.0;
	const float DAMAGE_CHARGED 					= g_EngineFuncs.CVarGetFloat( "sk_plr_secondarygauss" ) * 2.0;	//400.0;
	const float DAMAGE_OVERCHARGE 				= 500.0;	//Insecure increases Overcharge damage from 50 to 500.
	const float DAMAGE_OVERCHARGE_SPLASH		= 1000.0;
	const float DAMAGE_OVERCHARGE_RADIUS		= 256.0;
	uint SLOT									= 3;
	uint POSITION								= 6;
	const int GAUSS_MK2_PRIMARY_CHARGE_VOLUME		= 256;// how loud gauss is while charging
	const int GAUSS_MK2_PRIMARY_FIRE_VOLUME			= 450;// how loud gauss is when discharged
	
	class CGaussMk2 : ScriptBasePlayerWeaponEntity
	{
		private int m_iSoundState, m_fInAttack, m_iExplodeFireball;
		private float m_flNextAmmoBurn, m_flStartCharge, m_flAmmoStartCharge, m_flPlayAftershock;
		private bool m_bFullyCharged;

		float WeaponTimeBase() { return g_Engine.time; }
		
		protected EHandle m_hGlow;
		protected CSprite@ m_pGlow
		{
			get const { return cast<CSprite@>(m_hGlow.GetEntity()); }
			set { m_hGlow = EHandle(@value); }
		}

		private int iGlowBr;
		
		private CBasePlayer@ m_pPlayer
		{
			get const { return cast<CBasePlayer>(self.m_hPlayer.GetEntity()); }
			set       { self.m_hPlayer = EHandle(@value); }
		}
		
		float GetFullChargeTime()
		{
			return 2.5f;
		}
		
		void Spawn()
		{
			self.Precache();
			
			if ( g_ClassicMode.IsEnabled() )
				g_EntityFuncs.SetModel( self, W_MODEL_CLASSIC );
			else
				g_EntityFuncs.SetModel( self, W_MODEL );

			self.m_iDefaultAmmo = DEFAULT_GIVE;
			m_bFullyCharged = false;
			self.FallInit();
		}
		
		void Precache()
		{
			self.PrecacheCustomModels();
			g_Game.PrecacheModel( V_MODEL );
			g_Game.PrecacheModel( W_MODEL );
			g_Game.PrecacheModel( P_MODEL );

			g_Game.PrecacheModel( V_MODEL_CLASSIC );
			g_Game.PrecacheModel( W_MODEL_CLASSIC );
			g_Game.PrecacheModel( P_MODEL_CLASSIC );
			
			g_Game.PrecacheModel( SPR_GLOW );
			g_Game.PrecacheModel( SPR_BALLS );
			g_Game.PrecacheModel( SPR_BEAM );
			g_Game.PrecacheModel( SPR_BEAM_02 );
			g_Game.PrecacheModel( SPR_BEAM_03 );
			g_Game.PrecacheModel( SPR_FIREBALL );
			
			m_iExplodeFireball = g_EngineFuncs.ModelIndex( SPR_FIREBALL );
			
			for( uint i = 0; i < GaussMk2ElectroEvents.length(); i++ )
			{
				g_SoundSystem.PrecacheSound( GaussMk2ElectroEvents[i] );
				g_Game.PrecacheGeneric( "sound/" + GaussMk2ElectroEvents[i] );
			}
			
			for( uint i = 0; i < GaussMk2ExplodeEvents.length(); i++ )
			{
				g_SoundSystem.PrecacheSound( GaussMk2ExplodeEvents[i] );
				g_Game.PrecacheGeneric( "sound/" + GaussMk2ExplodeEvents[i] );
			}
			
			g_SoundSystem.PrecacheSound( SND_GAUSS_MK2_BEEP );
			//g_SoundSystem.PrecacheSound( SND_GAUSS_MK2_OVERCHARGED );
			//g_SoundSystem.PrecacheSound( SND_GAUSS_MK2_BEAM );
			//g_SoundSystem.PrecacheSound( SND_GAUSS_MK2_LOWAMMO );
			g_SoundSystem.PrecacheSound( SND_GAUSS_MK2_EMPTY );

			g_SoundSystem.PrecacheSound( CHARGE_SND );
			g_SoundSystem.PrecacheSound( FIRE_SND );
			
			g_Game.PrecacheGeneric( "sound/" + SND_GAUSS_MK2_BEEP );
			//g_Game.PrecacheGeneric( "sound/" + SND_GAUSS_MK2_OVERCHARGED );
			//g_Game.PrecacheGeneric( "sound/" + SND_GAUSS_MK2_BEAM );
			//g_Game.PrecacheGeneric( "sound/" + SND_GAUSS_MK2_LOWAMMO );
			g_Game.PrecacheGeneric( "sound/" + SND_GAUSS_MK2_EMPTY );
			
			g_Game.PrecacheGeneric( "sprites/insecure/weapon_gaussmk2.spr" );
			g_Game.PrecacheGeneric( "sprites/insecure/weapon_gaussmk2_s.spr" );
			g_Game.PrecacheGeneric( "sprites/insecure/weapon_gaussmk2.txt" );
		}
		
		bool GetItemInfo( ItemInfo& out info )
		{
			info.iMaxAmmo1		= MAX_CARRY;
			info.iMaxAmmo2		= -1;
			info.iAmmo1Drop 	= DEFAULT_GIVE;
		//	info.iAmmo2Drop 	= -1;
			info.iMaxClip		= WEAPON_NOCLIP;
			info.iSlot			= SLOT;
			info.iPosition		= POSITION;
			info.iWeight		= WEIGHT;
			info.iFlags			= FLAGS;
			info.iId 			= g_ItemRegistry.GetIdForName(self.pev.classname);

			return true;
		}
		
		bool AddToPlayer(CBasePlayer@ pPlayer)
		{
			if (!BaseClass.AddToPlayer(pPlayer))
				return false;

			NetworkMessage message(MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict());
				message.WriteLong(g_ItemRegistry.GetIdForName(self.pev.classname));
			message.End();

			return true;
		}
		
		bool PlayEmptySound()
		{
			if ( self.m_bPlayEmptySound )
			{
				self.m_bPlayEmptySound = false;
				g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, SND_GAUSS_MK2_EMPTY, 0.8, ATTN_NORM, 0, PITCH_NORM );
			}
			
			return false;
		}
		
		bool Deploy()
		{
			m_flPlayAftershock = 0.0;
			m_bFullyCharged = false;
			
			bool bResult;
			
			if ( g_ClassicMode.IsEnabled() )
				bResult = self.DefaultDeploy( self.GetV_Model( V_MODEL_CLASSIC ), self.GetP_Model( P_MODEL_CLASSIC ), GAUSS_MK2_DRAW, "gauss" );
			else
				bResult = self.DefaultDeploy( self.GetV_Model( V_MODEL ), self.GetP_Model( P_MODEL ), GAUSS_MK2_DRAW, "gauss" );
			
			self.m_flTimeWeaponIdle = WeaponTimeBase() + 1.0;
			
			return bResult;
		}
		
		void Holster( int skipLocal )
		{	
			if ( m_fInAttack != 0 )
			{
				g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND );
				g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEEP );
				StartFire();
			}
			
			m_pPlayer.m_flNextAttack = 0.5;
			m_bFullyCharged = false;
			self.SendWeaponAnim( GAUSS_MK2_HOLSTER );
			m_fInAttack = 0;
			BaseClass.Holster( skipLocal );
		}
		
		void PrimaryAttack()
		{
			// don't fire underwater
			if ( m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD )
			{
				if ( m_fInAttack != 0 )
				{
					g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND );
					g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEEP );
					
					g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_ITEM, GaussMk2ElectroEvents[0], 1.0, ATTN_NORM, 0, 80 + Math.RandomLong( 0, 0x3f ) );
					//g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_WEAPON, SND_GAUSS_MK2_OVERCHARGED, 1.0, ATTN_NORM, 0, 97 + Math.RandomLong( 0, 0x3f ) );
					self.SendWeaponAnim( GAUSS_MK2_FIRE1 );
					m_fInAttack = 0;
				}
				else
				{
					self.PlayEmptySound();
				}

				self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = 1.0f;
				
				return;
			}
			
			if ( m_fInAttack == 0 )
			{
				if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
				{
					self.PlayEmptySound();
					m_pPlayer.SetSuitUpdate("!HEV_AMO0", false, 0);
					m_pPlayer.m_flNextAttack = 0.5;
					return;
				}

				//Low Ammo Warning
				//if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) == 20 )
				//{
				//	g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_LOWAMMO, 1.0, ATTN_NORM );
				//}

				m_flNextAmmoBurn = WeaponTimeBase();

				// spin up
				m_pPlayer.m_iWeaponVolume = GAUSS_MK2_PRIMARY_CHARGE_VOLUME;

				self.SendWeaponAnim( GAUSS_MK2_SPINUP );
				m_fInAttack = 1;
				self.m_flTimeWeaponIdle = WeaponTimeBase() + 0.5;
				m_flStartCharge = g_Engine.time;
				m_flAmmoStartCharge = WeaponTimeBase() + GetFullChargeTime();

				g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND, 1.0, ATTN_NORM, 0, 110 );
				m_iSoundState = SND_CHANGE_PITCH;
			}
			else if ( m_fInAttack == 1 )
			{
				if ( self.m_flTimeWeaponIdle < WeaponTimeBase() )
				{
					self.SendWeaponAnim( GAUSS_MK2_SPIN );
					m_fInAttack = 2;
				}
			}
			else
			{
				// during the charging process, eat one bit of ammo every once in a while
				if ( WeaponTimeBase() >= m_flNextAmmoBurn && m_flNextAmmoBurn != 1000 )
				{
					m_flNextAmmoBurn = WeaponTimeBase() + 0.1;
				}
			
				if ( WeaponTimeBase() >= m_flAmmoStartCharge )
				{
					// don't eat any more ammo after gun is fully charged.
					m_flNextAmmoBurn = 1000;
				}
			
				float pitch = ( g_Engine.time - m_flStartCharge ) * ( 150 / GetFullChargeTime() ) + 100;
				
				if ( pitch > 250 )	//250
					pitch = 250;
				
				if ( !m_bFullyCharged && WeaponTimeBase() >= m_flAmmoStartCharge )
				{
					//g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_ITEM, SND_GAUSS_MK2_CHARGED, 1.0, ATTN_NORM, m_iSoundState, PITCH_NORM );
					m_bFullyCharged = true;
				}
			
				g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND, 1.0, ATTN_NORM, m_iSoundState, int( pitch ) );
				
				m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
				
				m_pPlayer.m_iWeaponVolume = GAUSS_MK2_PRIMARY_CHARGE_VOLUME;
				
				// self.m_flTimeWeaponIdle = WeaponTimeBase() + 0.1;
				if ( m_flStartCharge < g_Engine.time - 2.75 )
					g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEEP, 0.7, ATTN_NORM, m_iSoundState, 110 );
				if ( m_flStartCharge < g_Engine.time - 5 )
				{
					g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND );
					g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEEP );
					
					g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_ITEM, GaussMk2ElectroEvents[0], 1.0, ATTN_NORM, 0, 80 + Math.RandomLong( 0, 0x3f ) );
					//g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_WEAPON, SND_GAUSS_MK2_OVERCHARGED, 1.0, ATTN_NORM, 0, 97 + Math.RandomLong( 0, 0x3f ) );

					m_fInAttack = 0;

					// Insecure -- The sequence at insecure04e explodes the security guard as well as
					// his partner around him. so make it explode the player and it's surroundings. 
					Math.MakeVectors( m_pPlayer.pev.v_angle + m_pPlayer.pev.punchangle );
					m_pPlayer.pev.punchangle.x = -2.0;
					m_pPlayer.TakeDamage( g_EntityFuncs.Instance(0).pev, g_EntityFuncs.Instance(0).pev, DAMAGE_OVERCHARGE, DMG_SHOCK | DMG_BLAST | DMG_ALWAYSGIB );
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 20 );
					g_PlayerFuncs.ScreenFade( m_pPlayer, Vector( 255, 128, 0 ), 2, 0.5f, 128, FFADE_IN );
					
					int iScale = 50;
					
					NetworkMessage GaussMk2Explode( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
						GaussMk2Explode.WriteByte(TE_EXPLOSION);
						GaussMk2Explode.WriteCoord(pev.origin.x);
						GaussMk2Explode.WriteCoord(pev.origin.y);
						GaussMk2Explode.WriteCoord(pev.origin.z);
						GaussMk2Explode.WriteShort( m_iExplodeFireball );
						GaussMk2Explode.WriteByte( iScale );	// New Scale
						GaussMk2Explode.WriteByte( 15 );
						GaussMk2Explode.WriteByte( TE_EXPLFLAG_NOSOUND ); // No explosion sound, will be handled by a switch-case-default block below due to Stereo Sample issues.
					GaussMk2Explode.End();

					switch ( Math.RandomLong (0, 2) )
					{
						case 0: g_SoundSystem.PlaySound( self.edict(), CHAN_AUTO, GaussMk2ExplodeEvents[0], 1.0, 0.1, 0, PITCH_NORM, 0, false, pev.origin ); break;
						case 1: g_SoundSystem.PlaySound( self.edict(), CHAN_AUTO, GaussMk2ExplodeEvents[1], 1.0, 0.1, 0, PITCH_NORM, 0, false, pev.origin ); break;
						case 2: g_SoundSystem.PlaySound( self.edict(), CHAN_AUTO, GaussMk2ExplodeEvents[2], 1.0, 0.1, 0, PITCH_NORM, 0, false, pev.origin ); break;
					}
					
					GetSoundEntInstance().InsertSound( bits_SOUND_COMBAT, pev.origin, NORMAL_EXPLOSION_VOLUME, 3.0f, self ); 
		
					entvars_t@ pevOwner;

					if ( pev.owner !is null )
						@pevOwner = pev.owner.vars;
					else
						@pevOwner = null;

					// Kill everything around the player.
					g_WeaponFuncs.RadiusDamage( m_pPlayer.pev.origin, self.pev, pevOwner, DAMAGE_OVERCHARGE_SPLASH, DAMAGE_OVERCHARGE_RADIUS, CLASS_NONE, DMG_BLAST );
					
					@pevOwner = null; // can't traceline attack owner if this is set
					
					self.m_flTimeWeaponIdle = WeaponTimeBase() + 1.0;
					m_pPlayer.m_flNextAttack = 1.0;
					
					self.SendWeaponAnim( GAUSS_MK2_FIRE1 );
					// Player may have been killed and this weapon dropped, don't execute any more code after this!
					return;
				}
			}
		}
		
		void SecondaryAttack()
		{
			PrimaryAttack();
		}
		
		void StartFire()
		{
			float flDamage;

			Math.MakeVectors( m_pPlayer.pev.v_angle + m_pPlayer.pev.punchangle );
			Vector vecAiming = g_Engine.v_forward;
			Vector vecSrc = m_pPlayer.GetGunPosition();

			if ( g_Engine.time - m_flStartCharge >= GetFullChargeTime() )
			{
				// full damage, needs 20 uranium ammo
				flDamage = DAMAGE_CHARGED;
				
				if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 20 )
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, 0 );
				else
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 20 );
			}
			else if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 10 )
			{
				// don't deal more than 100 damage if you have less 20 uranium ammo.
				flDamage = DAMAGE_PRIMARY;
				
				if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 10 )
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, 0 );
				else
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 10 );
			}
			else
			{
				// small charge, calculate damage and take 10 uranium ammo.
				flDamage = DAMAGE_CHARGED * ( ( g_Engine.time - m_flStartCharge ) / GetFullChargeTime() );
				
				if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 10 )
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, 0 );
				else
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 10 );
			}
		
			float flZVel = m_pPlayer.pev.velocity.z;
			m_pPlayer.pev.velocity = m_pPlayer.pev.velocity - g_Engine.v_forward * flDamage * 1.75;	//1;	//Added half velocity so you can do some exploits for fun.

			if ( g_EngineFuncs.CVarGetFloat( "mp_disablegaussjump" ) != 0 )
				m_pPlayer.pev.velocity.z = flZVel;

			// player "shoot" animation
			m_pPlayer.SetAnimation( PLAYER_ATTACK1 );

			m_pPlayer.pev.punchangle.x = -2.0;
			
			if ( m_flStartCharge > g_Engine.time - ( GetFullChargeTime() * 0.5 ) )
				self.SendWeaponAnim( GAUSS_MK2_FIRE1 );
			else
				self.SendWeaponAnim( GAUSS_MK2_FIRE2 );
			
			// time until aftershock 'static discharge' sound
			m_flPlayAftershock = g_Engine.time + g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 0.3, 0.8 );

			self.m_flNextPrimaryAttack = WeaponTimeBase() + .5;
			self.m_flNextSecondaryAttack = WeaponTimeBase() + .5;

			Fire( vecSrc, vecAiming, flDamage );
		}
		
		void Fire( Vector vecOrigSrc, Vector vecDir, float flDamage )
		{
			m_pPlayer.m_iWeaponVolume = GAUSS_MK2_PRIMARY_FIRE_VOLUME;
	
			Vector vecSrc = vecOrigSrc;
			Vector vecDest = vecSrc + vecDir * 8192;
			edict_t@ pentIgnore;
			TraceResult tr, beam_tr;
			float flMaxFrac = 1.0f;
			int	nTotal = 0;
			int fHasPunched = 0;
			int fFirstBeam = 1;
			int	nMaxHits = 10;
			
			@pentIgnore = m_pPlayer.edict();
			
			g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_WEAPON, CHARGE_SND );
			g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEEP );
			
			if ( m_flStartCharge > g_Engine.time - ( GetFullChargeTime() * 0.5 ) )
			{
				float flRand = g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 0, 1 );
				
				if ( flRand >= 0.66 )
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 98 + Math.RandomLong(0, 4) );
				}
				else if ( flRand >= 0.33 )
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(0, 5) );
				}
				else
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 95 + Math.RandomLong(0, 10) );
				}
			}
			else
			{
				float flRand = g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 0, 1 );
				
				if ( flRand >= 0.66 )
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(0, 5) );
				}
				else if ( flRand >= 0.33 )
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(0, 5) );
				}
				else
				{
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, FIRE_SND, 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(0, 5) );
				}
			}
			
			//g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_STATIC, SND_GAUSS_MK2_BEAM, 0.9, ATTN_NORM, 0, 100 + Math.RandomLong(0, 2) );
			
			while ( flDamage > 10 && nMaxHits > 0 )
			{
				nMaxHits--;
				
				g_Utility.TraceLine(vecSrc, vecDest, dont_ignore_monsters, pentIgnore, tr);
				
				if ( tr.fAllSolid != 0 )
					break;
				
				if ( fFirstBeam != 0 )
				{
					m_pPlayer.pev.effects |= EF_MUZZLEFLASH;
					fFirstBeam = 0;
					
					NetworkMessage lightGaussMk2Fired( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
						lightGaussMk2Fired.WriteByte( TE_DLIGHT );
						lightGaussMk2Fired.WriteCoord( vecSrc.x );
						lightGaussMk2Fired.WriteCoord( vecSrc.y );
						lightGaussMk2Fired.WriteCoord( vecSrc.z );
						lightGaussMk2Fired.WriteByte( 22 );//radius
						lightGaussMk2Fired.WriteByte( 250 ); //R
						lightGaussMk2Fired.WriteByte( 128 ); //G
						lightGaussMk2Fired.WriteByte( 40 ); //B
						lightGaussMk2Fired.WriteByte( 6 );//life
						lightGaussMk2Fired.WriteByte( 128 );//decay
					lightGaussMk2Fired.End();
						
					if ( m_flStartCharge > g_Engine.time - ( GetFullChargeTime() * 0.5 ) )
					{
						NetworkMessage mainGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, tr.vecEndPos );
							mainGaussMk2Beam.WriteByte( TE_BEAMENTPOINT );
							mainGaussMk2Beam.WriteShort( m_pPlayer.entindex() + 0x1000 );//start entity
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							mainGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_02 ) );
							mainGaussMk2Beam.WriteByte( 0 );//starting frame
							mainGaussMk2Beam.WriteByte( 0 );//frame rate
							mainGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							mainGaussMk2Beam.WriteByte( 70 );//line width in 0.1's	//25
							mainGaussMk2Beam.WriteByte( 0 );//noise amplitude in 0.1's
							mainGaussMk2Beam.WriteByte( 255 );//red
							mainGaussMk2Beam.WriteByte( 255 );//green
							mainGaussMk2Beam.WriteByte( 255 );//blue
							mainGaussMk2Beam.WriteByte( 230 );//brightness
							mainGaussMk2Beam.WriteByte( 0 );//scroll speed
						mainGaussMk2Beam.End();
						
						NetworkMessage boltGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, tr.vecEndPos );
							boltGaussMk2Beam.WriteByte( TE_BEAMENTPOINT );
							boltGaussMk2Beam.WriteShort( m_pPlayer.entindex() + 0x1000 );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							boltGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_03 ) );
							boltGaussMk2Beam.WriteByte( 0 );//starting frame
							boltGaussMk2Beam.WriteByte( 60 );//frame rate
							boltGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							boltGaussMk2Beam.WriteByte( 30 );//line width in 0.1's
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 7, 15 ) );//noise amplitude in 0.1's
							boltGaussMk2Beam.WriteByte( 255 );//red
							boltGaussMk2Beam.WriteByte( 200 );//green
							boltGaussMk2Beam.WriteByte( 170 );//blue
							boltGaussMk2Beam.WriteByte( 255 );//brightness
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 70, 110 ) );//scroll speed
						boltGaussMk2Beam.End();
					}
					else
					{
						NetworkMessage lightGaussMk2Fired( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
							lightGaussMk2Fired.WriteByte( TE_DLIGHT );
							lightGaussMk2Fired.WriteCoord( vecSrc.x );
							lightGaussMk2Fired.WriteCoord( vecSrc.y );
							lightGaussMk2Fired.WriteCoord( vecSrc.z );
							lightGaussMk2Fired.WriteByte( 22 );//radius
							lightGaussMk2Fired.WriteByte( 250 ); //R
							lightGaussMk2Fired.WriteByte( 128 ); //G
							lightGaussMk2Fired.WriteByte( 40 ); //B
							lightGaussMk2Fired.WriteByte( 6 );//life
							lightGaussMk2Fired.WriteByte( 128 );//decay
						lightGaussMk2Fired.End();
						
						NetworkMessage mainGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, tr.vecEndPos );
							mainGaussMk2Beam.WriteByte( TE_BEAMENTPOINT );
							mainGaussMk2Beam.WriteShort( m_pPlayer.entindex() + 0x1000 );//start entity
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							mainGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM ) );
							mainGaussMk2Beam.WriteByte( 0 );//starting frame
							mainGaussMk2Beam.WriteByte( 0 );//frame rate
							mainGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							mainGaussMk2Beam.WriteByte( 50 );//line width in 0.1's	//25
							mainGaussMk2Beam.WriteByte( 0 );//noise amplitude in 0.1's
							mainGaussMk2Beam.WriteByte( 255 );//red
							mainGaussMk2Beam.WriteByte( 230 );//green
							mainGaussMk2Beam.WriteByte( 100 );//blue
							mainGaussMk2Beam.WriteByte( 255 );//brightness
							mainGaussMk2Beam.WriteByte( 0 );//scroll speed
						mainGaussMk2Beam.End();
						
						NetworkMessage boltGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, tr.vecEndPos );
							boltGaussMk2Beam.WriteByte( TE_BEAMENTPOINT );
							boltGaussMk2Beam.WriteShort( m_pPlayer.entindex() + 0x1000 );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							boltGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_03 ) );
							boltGaussMk2Beam.WriteByte( 0 );//starting frame
							boltGaussMk2Beam.WriteByte( 60 );//frame rate
							boltGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							boltGaussMk2Beam.WriteByte( 40 );//line width in 0.1's
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 7, 15 ) );//noise amplitude in 0.1's
							boltGaussMk2Beam.WriteByte( 255 );//red
							boltGaussMk2Beam.WriteByte( 200 );//green
							boltGaussMk2Beam.WriteByte( 170 );//blue
							boltGaussMk2Beam.WriteByte( 255 );//brightness
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 70, 110 ) );//scroll speed
						boltGaussMk2Beam.End();
					}

					nTotal += 26;
				}
				else
				{
					if ( m_flStartCharge > g_Engine.time - ( GetFullChargeTime() * 0.5 ) )
					{
						NetworkMessage mainGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
							mainGaussMk2Beam.WriteByte( TE_BEAMPOINTS );
							mainGaussMk2Beam.WriteCoord( vecSrc.x );//start position
							mainGaussMk2Beam.WriteCoord( vecSrc.y );
							mainGaussMk2Beam.WriteCoord( vecSrc.z );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							mainGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_02 ) );
							mainGaussMk2Beam.WriteByte( 0 );//starting frame
							mainGaussMk2Beam.WriteByte( 0 );//framerate in 0.1's
							mainGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							mainGaussMk2Beam.WriteByte( 70 );//width in 0.1's	//25
							mainGaussMk2Beam.WriteByte( 0 );//noise amplitude in 0.1's
							mainGaussMk2Beam.WriteByte( 255 );//red
							mainGaussMk2Beam.WriteByte( 255 );//green
							mainGaussMk2Beam.WriteByte( 255 );//blue
							mainGaussMk2Beam.WriteByte( 230 );//brightness
							mainGaussMk2Beam.WriteByte( 0 );//scroll speed in 0.1's
						mainGaussMk2Beam.End();
						
						NetworkMessage boltGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
							boltGaussMk2Beam.WriteByte( TE_BEAMPOINTS );
							boltGaussMk2Beam.WriteCoord( vecSrc.x );//start position
							boltGaussMk2Beam.WriteCoord( vecSrc.y );
							boltGaussMk2Beam.WriteCoord( vecSrc.z );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							boltGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_03 ) );
							boltGaussMk2Beam.WriteByte( 0 );//starting frame
							boltGaussMk2Beam.WriteByte( 60 );//framerate in 0.1's
							boltGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							boltGaussMk2Beam.WriteByte( 30 );//width in 0.1's
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 7, 15 ) );//noise amplitude in 0.1's
							boltGaussMk2Beam.WriteByte( 255 );//red
							boltGaussMk2Beam.WriteByte( 200 );//green
							boltGaussMk2Beam.WriteByte( 170 );//blue
							boltGaussMk2Beam.WriteByte( 255 );//brightness
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 70, 110 ) );//scroll speed
						boltGaussMk2Beam.End();
					}
					else
					{
						NetworkMessage mainGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
							mainGaussMk2Beam.WriteByte( TE_BEAMPOINTS );
							mainGaussMk2Beam.WriteCoord( vecSrc.x );//start position
							mainGaussMk2Beam.WriteCoord( vecSrc.y );
							mainGaussMk2Beam.WriteCoord( vecSrc.z );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							mainGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							mainGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM ) );
							mainGaussMk2Beam.WriteByte( 0 );//starting frame
							mainGaussMk2Beam.WriteByte( 0 );//framerate in 0.1's
							mainGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							mainGaussMk2Beam.WriteByte( 50 );//width in 0.1's	//25
							mainGaussMk2Beam.WriteByte( 0 );//noise amplitude in 0.1's
							mainGaussMk2Beam.WriteByte( 255 );//red
							mainGaussMk2Beam.WriteByte( 230 );//green
							mainGaussMk2Beam.WriteByte( 100 );//blue
							mainGaussMk2Beam.WriteByte( 255 );//brightness
							mainGaussMk2Beam.WriteByte( 0 );//scroll speed in 0.1's
						mainGaussMk2Beam.End();
						
						NetworkMessage boltGaussMk2Beam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
							boltGaussMk2Beam.WriteByte( TE_BEAMPOINTS );
							boltGaussMk2Beam.WriteCoord( vecSrc.x );//start position
							boltGaussMk2Beam.WriteCoord( vecSrc.y );
							boltGaussMk2Beam.WriteCoord( vecSrc.z );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.x );//end position
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.y );
							boltGaussMk2Beam.WriteCoord( tr.vecEndPos.z );
							boltGaussMk2Beam.WriteShort( g_Game.PrecacheModel( SPR_BEAM_03 ) );
							boltGaussMk2Beam.WriteByte( 0 );//starting frame
							boltGaussMk2Beam.WriteByte( 60 );//framerate in 0.1's
							boltGaussMk2Beam.WriteByte( 1 );//life in 0.1's
							boltGaussMk2Beam.WriteByte( 40 );//width in 0.1's
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 7, 15 ) );//noise amplitude in 0.1's
							boltGaussMk2Beam.WriteByte( 255 );//red
							boltGaussMk2Beam.WriteByte( 200 );//green
							boltGaussMk2Beam.WriteByte( 170 );//blue
							boltGaussMk2Beam.WriteByte( 255 );//brightness
							boltGaussMk2Beam.WriteByte( Math.RandomLong( 70, 110 ) );//scroll speed
						boltGaussMk2Beam.End();
					}
				}
			
				CBaseEntity@ pEntity = g_EntityFuncs.Instance( tr.pHit );
			
				if ( pEntity is null )
					break;
				
				if ( pEntity.IsBSPModel() == true || pEntity.pev.solid == SOLID_BSP )
				{
					g_WeaponFuncs.DecalGunshot( tr, BULLET_MONSTER_12MM );
					
					NetworkMessage DynLightImpact( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
						DynLightImpact.WriteByte( TE_DLIGHT );
						DynLightImpact.WriteCoord( tr.vecEndPos.x );
						DynLightImpact.WriteCoord( tr.vecEndPos.y );
						DynLightImpact.WriteCoord( tr.vecEndPos.z );
						DynLightImpact.WriteByte( 20 ); //radius
						DynLightImpact.WriteByte( 255 );
						DynLightImpact.WriteByte( 250 );
						DynLightImpact.WriteByte( 140 );
						DynLightImpact.WriteByte( 10 ); //life
						DynLightImpact.WriteByte( 128 ); //decay
					DynLightImpact.End();
					
					NetworkMessage LightGlowSpr( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
						LightGlowSpr.WriteByte( TE_GLOWSPRITE );
						LightGlowSpr.WriteCoord(tr.vecEndPos.x);
						LightGlowSpr.WriteCoord(tr.vecEndPos.y);
						LightGlowSpr.WriteCoord(tr.vecEndPos.z);
						LightGlowSpr.WriteShort(g_EngineFuncs.ModelIndex( SPR_GLOW ));	//Sprite
						LightGlowSpr.WriteByte( int( flDamage / 255.0f ) * 2 );	//Life
						LightGlowSpr.WriteByte( 10 );	//Scale
						LightGlowSpr.WriteByte( iGlowBr );	//Alpha
					LightGlowSpr.End();
				}
				
				if ( pEntity.pev.takedamage != DAMAGE_NO )
				{
					g_WeaponFuncs.ClearMultiDamage();
					
					pEntity.TraceAttack( m_pPlayer.pev, flDamage, vecDest, tr, DMG_ENERGYBEAM );
					g_WeaponFuncs.ApplyMultiDamage( m_pPlayer.pev, m_pPlayer.pev );
				}
				
				if ( pEntity.ReflectGauss() || pEntity.pev.solid == SOLID_BSP )
				{
					float n;
					@pentIgnore = null;
					
					n = -DotProduct( tr.vecPlaneNormal, vecDir );
					
					if ( n < 0.5f ) // 60 degrees
					{
						//g_Game.AlertMessage( at_console, "reflect %1\n", n );
						// reflect
						Vector r;

						r = 2.0f * tr.vecPlaneNormal * n + vecDir;
						flMaxFrac = flMaxFrac - tr.flFraction;
						vecDir = r;
						vecSrc = tr.vecEndPos + vecDir * 8;
						vecDest = vecSrc + vecDir * 8192;

						// explode a bit
						iGlowBr = 255;//int( flDamage * n / 255 );

						@m_pGlow = g_EntityFuncs.CreateSprite( SPR_GLOW, tr.vecEndPos, false ); 
						m_pGlow.SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation ); //int( flDamage / 255.0f )
						m_pGlow.SetScale( 0.5f );
						m_pGlow.SUB_StartFadeOut();

						g_WeaponFuncs.RadiusDamage( tr.vecEndPos, self.pev, m_pPlayer.pev, ( flDamage * 0.5 ) * n, ( ( flDamage * 0.5 ) * n ) * 2.5f, CLASS_PLAYER, DMG_BLAST );	//Maybe to stop user from taking self damage? // Added 0.5 damage multiplier for balance sake. This weapon's capable of nuking.

						GaussMk2Balls( tr, ( tr.vecEndPos + tr.vecPlaneNormal ), 16, 100, 100 );

						nTotal += 34;

						// lose energy
						if ( n == 0 )
							n = 0.1f;

						flDamage = flDamage * (1 - n);
					}
					else
					{
						// tunnel
						g_WeaponFuncs.DecalGunshot( tr, BULLET_MONSTER_12MM );
						
						iGlowBr = 255;//int( flDamage / 255 );
						@m_pGlow = g_EntityFuncs.CreateSprite( SPR_GLOW, tr.vecEndPos, false ); 
						m_pGlow.SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation ); //int(flDamage / 255.0f)
						m_pGlow.SetScale( 1.0f );
						m_pGlow.SUB_StartFadeOut();
						
						nTotal += 13;
						
						// limit it to one hole punch
						if ( fHasPunched != 0 )
							break;
						
						fHasPunched = 1;
						
						// try punching through wall if secondary attack (primary is incapable of breaking through)
						g_Utility.TraceLine( tr.vecEndPos + vecDir * 8, vecDest, dont_ignore_monsters, pentIgnore, beam_tr );
							
						if ( beam_tr.fAllSolid == 0 )
						{
							// trace backwards to find exit point
							g_Utility.TraceLine( beam_tr.vecEndPos, tr.vecEndPos, dont_ignore_monsters, pentIgnore, beam_tr );
								
							n = (beam_tr.vecEndPos - tr.vecEndPos).Length();
								
							if ( n < flDamage )
							{
								if ( n == 0 )
									n = 1;

								flDamage -= n;

								// absorption balls
								GaussMk2Balls( tr, ( tr.vecEndPos - vecDir ), 16, 100, 100 );

								//g_Game.AlertMessage( at_console, "punch %1\n", n );
								nTotal += 21;

								// exit blast damage
								float flDamageRadius;
									
								flDamageRadius = flDamage * 0.25;	//2.0;	//Reduced radius. Capable of nuking.
								
								g_WeaponFuncs.RadiusDamage( beam_tr.vecEndPos + vecDir * 8, self.pev, m_pPlayer.pev, ( flDamage * 0.5 ), flDamageRadius, CLASS_PLAYER, DMG_BLAST );	// Added 0.5 damage multiplier for balance sake. This weapon's capable of nuking.

								GetSoundEntInstance().InsertSound( bits_SOUND_COMBAT, self.pev.origin, NORMAL_EXPLOSION_VOLUME, 3.0f, m_pPlayer ); 
									
								g_WeaponFuncs.DecalGunshot( beam_tr, BULLET_MONSTER_12MM );

								iGlowBr = 255; //int( flDamage / 255 );
								@m_pGlow = g_EntityFuncs.CreateSprite( SPR_GLOW, beam_tr.vecEndPos, false ); 
								m_pGlow.SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation ); //int(flDamage / 255.0f)
								m_pGlow.SetScale( 1.0f );
								m_pGlow.SUB_StartFadeOut();
								GaussMk2Balls( beam_tr, (beam_tr.vecEndPos - vecDir), 20, 200, 100 );	//int(flDamage * 0.3f)

								nTotal += 53;

								vecSrc = beam_tr.vecEndPos + vecDir;
							}
						}
						else
						{
							flDamage = 0;
						}
					}
				}
				else
				{
					vecSrc = tr.vecEndPos + vecDir;
					@pentIgnore = pEntity.edict();
				}
			}
		}
		
		void GlowFadeout()
		{
			if ( m_pGlow is null )
				return;

			m_pGlow.SetBrightness( iGlowBr );

			if ( iGlowBr > 0 )
				iGlowBr -= 1;
		}

		void KillGlow()
		{
			if ( m_pGlow is null )
				return;

			g_EntityFuncs.Remove( m_pGlow );
			@m_pGlow = null;
			
		}

		void ItemPreFrame()
		{
			if ( m_pGlow !is null )
				GlowFadeout();
			
			BaseClass.ItemPreFrame();
		}
	
		void GaussMk2Balls( TraceResult &in tr, Vector vecEnd, int count, int amplitude, int velocity )
		{
			NetworkMessage GaussMk2Balls( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
				GaussMk2Balls.WriteByte( TE_SPRITETRAIL );
				GaussMk2Balls.WriteCoord( tr.vecEndPos.x );//start position
				GaussMk2Balls.WriteCoord( tr.vecEndPos.y );
				GaussMk2Balls.WriteCoord( tr.vecEndPos.z );
				GaussMk2Balls.WriteCoord( vecEnd.x );//end position
				GaussMk2Balls.WriteCoord( vecEnd.y );
				GaussMk2Balls.WriteCoord( vecEnd.z );
				GaussMk2Balls.WriteShort( g_EngineFuncs.ModelIndex( SPR_BALLS ) );//sprite index
				GaussMk2Balls.WriteByte( count );//count
				GaussMk2Balls.WriteByte( 1 );//life in 0.1's
				GaussMk2Balls.WriteByte( Math.RandomLong(1, 2) );//scale in 0.1's
				GaussMk2Balls.WriteByte( velocity / 25 );//velocity along vector in 10's
				GaussMk2Balls.WriteByte( amplitude / 25 );//randomness of velocity in 10's
			GaussMk2Balls.End();
		}
		
		void WeaponIdle()
		{
			if ( !self.m_bFireOnEmpty )
				self.ResetEmptySound();

			m_bFullyCharged = false;
			
			// play aftershock static discharge
			if ( m_flPlayAftershock != 0 && m_flPlayAftershock < g_Engine.time )
			{
				switch( Math.RandomLong( 0, 3 ) )
				{
				case 0:
					g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_ITEM, GaussMk2ElectroEvents[0], Math.RandomFloat( 0.7f, 0.8f ), ATTN_NORM );
					break;
				case 1:
					g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_ITEM, GaussMk2ElectroEvents[1], Math.RandomFloat( 0.7f, 0.8f ), ATTN_NORM );
					break;
				case 2:
					g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_ITEM, GaussMk2ElectroEvents[2], Math.RandomFloat( 0.7f, 0.8f ), ATTN_NORM );
					break;
				case 3:
					break; // no sound
				}
				
				m_flPlayAftershock = 0.0;
			}
			
			if ( self.m_flTimeWeaponIdle > WeaponTimeBase() )
				return;

			if ( m_fInAttack != 0 )
			{
				StartFire();
				m_fInAttack = 0;
				self.m_flTimeWeaponIdle = WeaponTimeBase() + 2.0;
			}
			else
			{	
				int iAnim;
				float flRand = g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 0, 1 );
				
				if ( flRand <= 0.5 )
				{
					iAnim = GAUSS_MK2_IDLE;
					self.m_flTimeWeaponIdle = WeaponTimeBase() + g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 10, 15 );
				}
				else if (flRand <= 0.75)
				{
					iAnim = GAUSS_MK2_IDLE2;
					self.m_flTimeWeaponIdle = WeaponTimeBase() + g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 10, 15 );
				}
				else
				{
					iAnim = GAUSS_MK2_FIDGET;
					self.m_flTimeWeaponIdle = WeaponTimeBase() + 3;
				}
				
				self.SendWeaponAnim( iAnim );
			}
		}
	}

	string GetAmmoName()
	{
		return "ammo_gaussclip";
	}

	string GetName()
	{
		return "weapon_gaussmk2";
	}

	void Register()
	{
		if ( !g_CustomEntityFuncs.IsCustomEntity( GetName() ) )
		{
			g_CustomEntityFuncs.RegisterCustomEntity( "SVEN_GAUSS_MK2::CGaussMk2", GetName() );
			g_ItemRegistry.RegisterWeapon( GetName(), SPR_DIR, "uranium", "", GetAmmoName() );
		}
	}
} // End of namespace