#include "weapon_hlhandgrenade"

namespace cnpc_ofgrunt
{
const string sWeaponName		= "weapon_ofgrunt";
const float OFGRUNT_MELEE_CD		= 1.0;

const float OFGRUNT_MELEE_DMG		= 50.0;
const float OFGRUNT_MELEE_DMG_RANGE	= 50.0;
const float SPEED_WALK			= (35.501198 * CNPC::flModelToGameSpeedModifier); //45.501198
const float SPEED_RUN			= (150.424637 * CNPC::flModelToGameSpeedModifier); //150.424637
const int REVIVE_RADIUS			= 80;
const int EAGLE_DAMAGE			= 44; //g_EngineFuncs.CVarGetFloat('sk_plr_357_bullet') * 0.66;
const int BULLET_DMG_SAW		= 15; //g_EngineFuncs.CVarGetFloat('sk_556_bullet');

const array<string> pAttackMissSounds = 
{
	"zombie/claw_miss1.wav",
	"zombie/claw_miss2.wav"
};

const array<string> pPainSounds = 
{
	"fgrunt/gr_pain1.wav",
	"fgrunt/gr_pain2.wav",
	"fgrunt/gr_pain3.wav",
	"fgrunt/gr_pain4.wav",
	"fgrunt/gr_pain5.wav"
};

const array<string> pDieSounds = 
{
	"fgrunt/death1.wav",
	"fgrunt/death2.wav",
	"fgrunt/death3.wav"
};

const array<string> pIdleSounds = 
{
	"hgrunt/gr_idle1.wav",
	"hgrunt/gr_idle2.wav",
	"hgrunt/gr_idle3.wav"
};

enum states_e
{
	STATE_IDLE = 0,
	STATE_IDLE_COMBAT,
	STATE_WALK,
	STATE_RUN,
	STATE_DEATH,
	STATE_OFGRUNT_ATTACK_MELEE,
	STATE_OFGRUNT_ATTACK_MGUN,
	STATE_RELOAD,
	STATE_THROW_GRENADE,
	STATE_CHARGE_SET,
	STATE_DEPLOY_NEEDLE,
	STATE_MEDIC_REVIVE
};

enum m_iWeaponModeOFGrunt
{
	MODE_NONE,
	MODE_SHOTGUN,
	MODE_MP5,
	MODE_M16,
	MODE_SAW,
	MODE_TORCH,
	MODE_MEDIC
};

class weapon_ofgrunt : CBaseDriveWeapon
{
	int m_iVoicePitch;
	int m_iMaxAmmoOFGrunt;
	int m_iWeaponModeOFGrunt;
	float m_flRechargeTime;
	float m_flNextIdleSound;
	float m_fNextCombatSound;
	float m_fNextRegenTick;
	float m_fNextHealthCheck;
	float m_flStopReviving;
	//private int m_iState;
	private int m_iBitchSlap;
	private int m_iAutoDeploy;
	private bool m_bIsInCombat;
	private bool m_bSyringeOut;
	private int m_iWeaponStage;
	private float m_flStopHornetAttack;
	private float m_flHealthRegenTime = 0.0;
	private float m_flRemoveGlowTime = 0.0;
	private float m_flNextHealthCheck = 0.0;
	private float m_flNextAmmoRegen = 0.0;
	private int m_iBurstCount = 0, m_iBurstLeft = 0;
	private float m_flNextBurstFireTime = 0;
	private float m_reviveChargedTime;
	private float m_flNextAlertGrowl;
	private float m_flNextCharge;
	
	protected EHandle m_hEnemy;
	protected CBaseEntity@ m_pEnemy
	{
		get const { return m_hEnemy.GetEntity(); }
		set { m_hEnemy = EHandle(@value); }
	}

	array<string> arrsBombTargetNames = { "asbomb1", "asbomb2", "asbomb3", "asbomb4", "asbomb5", "asbomb6", "asbomb7", "asbomb8", "asbomb9" };
	private EHandle m_hBombTarget;
	CBaseEntity@ pBestTarget = null;
	private CBaseEntity@ m_pBombTarget
	{
		get const { return m_hBombTarget.GetEntity(); }
		set { m_hBombTarget = EHandle(@value); }
	}

	void Spawn()
	{
		Precache();

		g_EntityFuncs.SetModel( self, "models/cnpc/hgrunt_opforf.mdl" );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderamt", 128 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "rendermode", 2 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderfx", 15 );

		m_iWeaponModeOFGrunt = MODE_SHOTGUN;
		self.pev.sequence = self.LookupSequence("idle1");
		self.pev.frame = 0;
		self.ResetSequenceInfo();

		self.pev.set_controller( 0,  127 );
		self.pev.set_controller( 1,  127 );
		//pev.targetname = "nosave";

		self.SetBodygroup( 1, 3 );
		self.SetBodygroup( 2, 1 );
		self.SetBodygroup( 3, 2 );

		m_iVoicePitch = 100;
		self.m_iDefaultSecAmmo = 0;

		if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			m_iMaxAmmoOFGrunt = 8;
		else if( m_iWeaponModeOFGrunt == MODE_MP5 )
			m_iMaxAmmoOFGrunt = 36;
		else if( m_iWeaponModeOFGrunt == MODE_M16 )
			m_iMaxAmmoOFGrunt = 36;
		else if( m_iWeaponModeOFGrunt == MODE_SAW )
			m_iMaxAmmoOFGrunt = 200;
		else if( m_iWeaponModeOFGrunt == MODE_TORCH || m_iWeaponModeOFGrunt == MODE_MEDIC )
			m_iMaxAmmoOFGrunt = 7;
		else
			m_iMaxAmmoOFGrunt = 36;

		self.m_iDefaultAmmo = m_iMaxAmmoOFGrunt;
		m_iAutoDeploy = 1;
		m_iState = STATE_IDLE;
		m_iBitchSlap = 0;
		m_bSyringeOut = false;

		self.FallInit();
	}

	void Precache()
	{
		BaseClass.Precache();
		self.PrecacheCustomModels();
		g_Game.PrecacheGeneric( "sprites/controlnpc/hgrunts/weapon_ofgrunt.txt" );
		g_Game.PrecacheGeneric( "sprites/controlnpc/crosshairs.spr" );
		g_Game.PrecacheModel( "models/cnpc/hgrunt_opforf.mdl" );
		g_Game.PrecacheGeneric( "models/cnpc/hgrunt_opforfT.mdl" );
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1		= m_iMaxAmmoOFGrunt; //= 999;
		info.iMaxAmmo2		= 5;
		info.iMaxClip		= WEAPON_NOCLIP;
		info.iSlot		= 0;
		info.iPosition		= 0;
		info.iFlags 		= ITEM_FLAG_SELECTONEMPTY; //to prevent monster from being despawned if out of ammo TODO
		info.iWeight		= 0; //-1 ??

		return true;
	}

	bool AddToPlayer( CBasePlayer@ pPlayer )
	{
		if( !BaseClass.AddToPlayer(pPlayer) )
			return false;

		@m_pPlayer = pPlayer;

		m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, m_iMaxAmmoOFGrunt);
		m_pPlayer.SwitchWeapon(self);

		return true;
	}

	bool Deploy()
	{
		if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
		{
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Shotgun Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
		}

		if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
		{
			switch( Math.RandomLong(1, 3) )
			{
				case 1:
				{
					m_iWeaponModeOFGrunt = MODE_MP5;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
					m_iVoicePitch = Math.RandomLong(95, 100);
					spawn_driveent();
					self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
				}
				break;
				case 2:
				{
					m_iWeaponModeOFGrunt = MODE_SHOTGUN;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 8);
					m_iVoicePitch = 95;
					spawn_driveent();
					self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
				}
				break;
				case 3:
				{
					m_iWeaponModeOFGrunt = MODE_SAW;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 200);
					m_iVoicePitch = Math.RandomLong(95, 100);
					spawn_driveent();
					self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
				}
				break;
				/*case 5:
				{
					m_iWeaponModeOFGrunt = MODE_M16;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
					m_iVoicePitch = Math.RandomLong(95, 100);
					spawn_driveent();
					self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
				}
				break;*/
			}
		}

		return self.DefaultDeploy( "", "", 0, "" );
	}

	bool CanHolster()
	{
		return false;
	}

	void CreatePelletDecals( const Vector& in vecSrc, const Vector& in vecAiming, const Vector& in vecSpread, const uint uiPelletCount )
	{
		TraceResult tr;
		
		float x, y;
		
		for( uint uiPellet = 0; uiPellet < uiPelletCount; ++uiPellet )
		{
			g_Utility.GetCircularGaussianSpread( x, y );
			
			Vector vecDir = vecAiming 
							+ x * vecSpread.x * g_Engine.v_right 
							+ y * vecSpread.y * g_Engine.v_up;

			Vector vecEnd	= vecSrc + vecDir * 9048;
			
			g_Utility.TraceLine( vecSrc, vecEnd, dont_ignore_monsters, m_pPlayer.edict(), tr );

			//Gets the barrel attachment
			Vector vecAttachOrigin;
			Vector vecAttachAngles;
			g_EngineFuncs.GetAttachment( m_pDriveEnt.edict(), 0, vecAttachOrigin, vecAttachAngles );

			WW2DynamicTracer( vecAttachOrigin, tr.vecEndPos );

			if( tr.flFraction < 1.0 )
			{
				if( tr.pHit !is null )
				{
					CBaseEntity@ pHit = g_EntityFuncs.Instance( tr.pHit );
					
					if( pHit is null || pHit.IsBSPModel() )
						g_WeaponFuncs.DecalGunshot( tr, BULLET_PLAYER_BUCKSHOT );
				}
			}
		}
	}

	void DoIdleSound()
	{
		//if( m_iState != STATE_IDLE and m_iState != STATE_WALK ) return;
		if( m_flNextIdleSound > g_Engine.time ) return;

		if( Math.RandomLong(0, 99) == 0 )
			IdleSound();
	}

	void Holster( int skipLocal = 0 )
	{
		if( m_pDriveEnt !is null )
			@m_pDriveEnt.pev.owner = null;

		ResetPlayer();

		BaseClass.Holster( skipLocal );
	}

	void PrimaryAttack()
	{
		float flTime = 1.0;

		/*if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
		}*/

		if( m_pDriveEnt !is null and m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 && !m_bSyringeOut )
		{
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle = g_Engine.time + 0.08;

			if( m_bIsInCombat )
			{
				DoCombatIdleAnimation();
			}
			else
			{
				DoIdleAnimation();
			}
			
			return;
		}

		if( m_pDriveEnt !is null )
		{
			if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_MP5 )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				m_iState = STATE_OFGRUNT_ATTACK_MGUN;

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_mp5");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

				m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
				m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME;
				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/hks1.wav", VOL_NORM, 0.6 );

				Vector vecSrc = m_pPlayer.GetGunPosition();
				Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
				Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES );
				Math.MakeVectors( m_pPlayer.pev.v_angle );
				Math.MakeAimVectors( m_pPlayer.pev.v_angle );
				Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
				g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
				m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 2, g_EngineFuncs.CVarGetFloat('sk_plr_9mmAR_bullet'), m_pPlayer.pev );

				WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );

				m_flNextIdleSound = g_Engine.time + 4.0;
				m_fNextRegenTick = g_Engine.time + 6.0;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.095;
				flTime = 0.1;
			}
			else if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_M16 )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}
				else
				{
					m_iState = STATE_OFGRUNT_ATTACK_MGUN;

					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_m16a2");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					m_pPlayer.SetMaxSpeedOverride( 0 );

					g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/m16_3round.wav", VOL_NORM, 0.6 );

					//Fire at most 3 bullets.
					m_iBurstCount = Math.min( 3, self.m_iPrimaryAmmoType );
					m_iBurstLeft = m_iBurstCount - 1;

					m_flNextBurstFireTime = g_Engine.time + 0.085;

					flTime = 0.6;
					self.m_flNextSecondaryAttack = g_Engine.time + 0.65;
					self.m_flTimeWeaponIdle = g_Engine.time + 0.5;
				}

				FirstAttackCommon();
			}
			else if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				m_iState = STATE_OFGRUNT_ATTACK_MGUN;

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_shotgun");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

				m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
				m_pPlayer.m_iWeaponVolume = LOUD_GUN_VOLUME;
				m_pPlayer.m_iWeaponFlash = NORMAL_GUN_FLASH;

				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/sbarrel1.wav", VOL_NORM, 0.6 );
				Vector vecSrc = m_pPlayer.GetGunPosition();
				Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
				Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES );
				Math.MakeVectors( m_pPlayer.pev.v_angle );
				Math.MakeAimVectors( m_pPlayer.pev.v_angle );
				Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
				g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL );
				m_pDriveEnt.FireBullets( 7, vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 8192.0, BULLET_PLAYER_BUCKSHOT, 5, BULLET_PLAYER_BUCKSHOT, m_pPlayer.pev );

				WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );

				m_flNextIdleSound = g_Engine.time + 4.0;
				m_fNextRegenTick = g_Engine.time + 6.0;

				flTime = 0.3;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.35;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.25;
			}
			if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_SAW )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				m_iState = STATE_OFGRUNT_ATTACK_MGUN;

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_saw");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

				m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
				m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME;

				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/saw_fire1.wav", VOL_NORM, 0.6 );

				Vector vecSrc = m_pPlayer.GetGunPosition();
				Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
				Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES );
				Math.MakeVectors( m_pPlayer.pev.v_angle );
				Math.MakeAimVectors( m_pPlayer.pev.v_angle );
				Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
				g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellSAW, TE_BOUNCE_SHELL );
				m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 2, g_EngineFuncs.CVarGetFloat('sk_556_bullet'), m_pPlayer.pev );

				WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );

				m_flNextIdleSound = g_Engine.time + 4.0;
				m_fNextRegenTick = g_Engine.time + 6.0;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.095;
				flTime = 0.1;
			}
			else if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_TORCH )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				if( m_iState == STATE_CHARGE_SET )
				{
					flTime = 0.5;
					return;
				}

				m_iState = STATE_OFGRUNT_ATTACK_MGUN;

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_mp5");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

				m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
				m_pPlayer.m_iWeaponVolume = LOUD_GUN_VOLUME;

				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", VOL_NORM, 0.6 );

				Vector vecSrc = m_pPlayer.GetGunPosition();
				Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
				Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_2DEGREES );
				Math.MakeVectors( m_pPlayer.pev.v_angle );
				Math.MakeAimVectors( m_pPlayer.pev.v_angle );
				Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
				g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
				m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 1, g_EngineFuncs.CVarGetFloat('sk_plr_357_bullet') * 0.66, m_pPlayer.pev );

				WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );
				m_flNextIdleSound = g_Engine.time + 4.0;
				m_fNextRegenTick = g_Engine.time + 6.0;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.55;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.5;
				flTime = 0.5;
			}
			else if( m_iState != STATE_DEATH and m_iWeaponModeOFGrunt == MODE_MEDIC )
			{
				if( m_iState == STATE_MEDIC_REVIVE )
				{
					flTime = 0.4;
					return;
				}

				m_iState = STATE_OFGRUNT_ATTACK_MGUN;

				if ( !m_bSyringeOut )
				{
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("standing_mp5");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					m_pPlayer.SetMaxSpeedOverride( 0 );

					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

					m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
					m_pPlayer.m_iWeaponVolume = LOUD_GUN_VOLUME;

					g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", VOL_NORM, 0.6 );

					Vector vecSrc = m_pPlayer.GetGunPosition();
					Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
					Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_2DEGREES );
					Math.MakeVectors( m_pPlayer.pev.v_angle );
					Math.MakeAimVectors( m_pPlayer.pev.v_angle );
					Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
					g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
					m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 1, g_EngineFuncs.CVarGetFloat('sk_plr_357_bullet') * 0.66, m_pPlayer.pev );

					WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );

					m_flNextIdleSound = g_Engine.time + 4.0;
					m_fNextRegenTick = g_Engine.time + 6.0;

					self.m_flNextSecondaryAttack = g_Engine.time + 0.45;
					self.m_flTimeWeaponIdle = g_Engine.time + 0.35;
					flTime = 0.4;
				}
				else
				{
					Math.MakeVectors( m_pPlayer.pev.v_angle );
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("give_shot");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					m_pPlayer.SetMaxSpeedOverride( 0 );
					float flHealing = 10;

					CBaseEntity@ pHurt = CheckTraceHullAttack( OFGRUNT_MELEE_DMG_RANGE, 0, DMG_MEDKITHEAL | DMG_NEVERGIB );

					if( pHurt !is null )
					{
						if ( pHurt.IsPlayer() && pHurt.IsAlive() && pHurt.pev.health < pHurt.pev.max_health )
						{
							pHurt.TakeHealth( flHealing, DMG_MEDKITHEAL );
							m_pPlayer.pev.frags += 1;
						}
						else if ( pHurt.IsMonster() && !pHurt.IsMachine() )
						{
							CBaseMonster@ pMonster = cast< CBaseMonster@ >( pHurt );
							if ( pMonster.IsAlive() && pMonster.IsPlayerAlly() && pMonster.pev.health < pMonster.pev.max_health )
							{
								pMonster.TakeHealth( flHealing, DMG_MEDKITHEAL );
								m_pPlayer.pev.frags += 1;
							}
						}
					}

					self.m_flNextSecondaryAttack = g_Engine.time + 0.45;
					self.m_flTimeWeaponIdle = g_Engine.time + 0.35;
					flTime = 0.6;
				}
			}
		}
		else
		{
			spawn_driveent();
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle = g_Engine.time + 0.5;

			return;
		}

		self.m_flNextPrimaryAttack = g_Engine.time + flTime;
	}

	void SecondaryAttack()
	{
		if( m_iState == STATE_RUN )
		{
			self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
		}

		if( m_iState == STATE_CHARGE_SET )
		{
			self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
			return;
		}

		if( m_iState == STATE_DEPLOY_NEEDLE )
		{
			self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
			return;
		}

		if( m_iState == STATE_MEDIC_REVIVE )
		{
			self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
			return;
		}

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH )
		{
			m_iState = STATE_OFGRUNT_ATTACK_MELEE;
			//m_bIsInCombat = true;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_iBitchSlap = 0;
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("frontkick");
			pev.nextthink = g_Engine.time + 0.4;

			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.MeleeAttackThink) );

			self.m_flNextSecondaryAttack = g_Engine.time + OFGRUNT_MELEE_CD;
		}
		if( m_pDriveEnt is null )
		{
			switch( m_iWeaponModeOFGrunt )
			{
				case MODE_SHOTGUN:
				{
					m_iWeaponModeOFGrunt = MODE_MP5;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
					m_iVoicePitch = Math.RandomLong(95, 100);
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: MP5 Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					break;
				}

				case MODE_MP5:
				{
					m_iWeaponModeOFGrunt = MODE_M16;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
					m_iVoicePitch = Math.RandomLong(95, 100);
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: M16 Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					break;
				}

				case MODE_M16:
				{
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: M249 SAW Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 200);
					m_iVoicePitch = Math.RandomLong(95, 100);
					m_iWeaponModeOFGrunt = MODE_SAW;
					break;
				}

				case MODE_SAW:
				{
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Torch Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
					m_iVoicePitch = Math.RandomLong(95, 100);
					m_iWeaponModeOFGrunt = MODE_TORCH;
					break;
				}

				case MODE_TORCH:
				{
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Medic Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
					m_iVoicePitch = Math.RandomLong(95, 100);
					m_iWeaponModeOFGrunt = MODE_MEDIC;
					m_bSyringeOut = false;
					break;
				}

				case MODE_MEDIC:
				{
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Shotgun Grunt\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 8);
					m_iVoicePitch = 95;
					m_iWeaponModeOFGrunt = MODE_SHOTGUN;
					break;
				}

			}

			self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
		}

		m_flNextIdleSound = g_Engine.time + 4.0;
		self.m_flNextPrimaryAttack = g_Engine.time + 1.0;
		self.m_flTimeWeaponIdle = g_Engine.time + OFGRUNT_MELEE_CD;
	}

	void TertiaryAttack()
	{
		float nextTertiary = 1.0;

		if( m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) != 0 and ( m_iWeaponModeOFGrunt != MODE_MEDIC || m_iWeaponModeOFGrunt != MODE_TORCH ) )
		{
			self.m_flNextTertiaryAttack = g_Engine.time + 0.1;
			self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
			return;
		}

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH )
		{
			if( m_iState == STATE_MEDIC_REVIVE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("heal_crouch") and !m_pDriveEnt.m_fSequenceFinished ) return;

			if( m_iWeaponModeOFGrunt == MODE_M16 )
			{
				m_iState = STATE_THROW_GRENADE;
				//m_bIsInCombat = true;
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("launchgrenade");
				pev.nextthink = g_Engine.time + 0.8;
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();

				SetThink( ThinkFunction(this.GrenadeLaunchThink) );
				m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) + 5 );
				nextTertiary = 4.0;
			}
			else if( m_iWeaponModeOFGrunt == MODE_MEDIC )
			{
				m_iState = STATE_DEPLOY_NEEDLE;
				//m_bIsInCombat = false;
				m_pPlayer.SetMaxSpeedOverride( 0 );

				if( !m_bSyringeOut )
				{
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("pull_needle");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					m_pDriveEnt.pev.framerate = 1.5;

					pev.nextthink = g_Engine.time + 0.4;
					SetThink( ThinkFunction(this.SyringeDeployThink) );
				}
				if( m_bSyringeOut )
				{
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("store_needle");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					m_pDriveEnt.pev.framerate = 1.5;

					pev.nextthink = g_Engine.time + 0.725; //1.25;
					SetThink( ThinkFunction(this.SyringeStoreThink) );
				}

				nextTertiary = 2.0;
			}
			else if( m_iWeaponModeOFGrunt == MODE_TORCH )
			{
				self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
				nextTertiary = 0.0;
				return;

			}
			else
			{
				m_iState = STATE_THROW_GRENADE;
				//m_bIsInCombat = true;
				m_pPlayer.SetMaxSpeedOverride( 0 );

				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("throwgrenade");
				pev.nextthink = g_Engine.time + 1.25;
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();

				SetThink( ThinkFunction(this.GrenadeThrowThink) );
				g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_THROW", 1.0, ATTN_NORM, 0, m_iVoicePitch );
				m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) + 5 );
				nextTertiary = 4.0;
			}
		}

		m_flNextIdleSound = g_Engine.time + 4.0;
		m_fNextRegenTick = g_Engine.time + 6.0;
		self.m_flNextPrimaryAttack = g_Engine.time + 1.35;
		self.m_flNextSecondaryAttack = g_Engine.time + 1.35;
		self.m_flNextTertiaryAttack = g_Engine.time + nextTertiary;
		self.m_flTimeWeaponIdle = g_Engine.time + 1.35;
	}

	void Reload()
	{
	}

	void SyringeDeployThink()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			m_bSyringeOut = true;
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Healing\n Press TERTIARY FIRE to change\n" );
			m_pDriveEnt.SetBodygroup( 3, 2 );
			SetThink( null );
		}

		pev.nextthink = g_Engine.time + 0.3;
	}

	void SyringeStoreThink()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			m_bSyringeOut = false;
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Offense\n Press TERTIARY FIRE to change\n" );
			m_pDriveEnt.SetBodygroup( 3, 0 );
			SetThink( null );
		}

		pev.nextthink = g_Engine.time + 0.3;
	}

	void WeaponIdle()
	{
		if( self.m_flTimeWeaponIdle > g_Engine.time )
			return;

		DoIdleAnimation();

		self.m_flTimeWeaponIdle = g_Engine.time + 1.0;
	}

	void ItemPreFrame()
	{
		if( m_pDriveEnt !is null )
		{
			m_pPlayer.pev.friction = 2;
			m_pPlayer.pev.punchangle.x = 0;
                        m_pPlayer.pev.punchangle.z = 0;
                        m_pPlayer.pev.punchangle.y = 0;

			m_pPlayer.SetViewMode( ViewMode_ThirdPerson );
			//m_pPlayer.pev.view_ofs = Vector( 0, 0, 40 );
			
			DoIdleSound();
			CheckMaxHealth();

			if( m_flHealthRegenTime < g_Engine.time and m_pPlayer.pev.health < m_pPlayer.pev.max_health )
			{
				m_pDriveEnt.pev.rendermode = 0.0;
				m_pDriveEnt.pev.renderfx = kRenderFxGlowShell;
				m_pDriveEnt.pev.renderamt = 1.0;
				m_pDriveEnt.pev.rendercolor = Vector(0, 100, 0);

				m_pPlayer.pev.health += 20;
				if( m_pPlayer.pev.health > m_pPlayer.pev.max_health )
				{
					m_pPlayer.pev.health = m_pPlayer.pev.max_health;
				}

				m_flHealthRegenTime = g_Engine.time + 3.0;
				m_flRemoveGlowTime = g_Engine.time + 1.0;
				g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_ITEM, "misc/regen.wav", 0.5, ATTN_NORM, 0, PITCH_NORM );
			}

			if( m_flRemoveGlowTime < g_Engine.time )
			{
				m_pDriveEnt.pev.rendercolor = Vector(0, 0, 0);
				m_flRemoveGlowTime = g_Engine.time + 99999;
			}

			if( m_flNextThink <= g_Engine.time )
			{
				LookForEnemies();
				m_flNextThink = g_Engine.time + 0.5;
			}

			if( m_flNextAmmoRegen <= g_Engine.time && m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) != 0 )
			{
				m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) - 1 );
				m_flNextAmmoRegen = g_Engine.time + 1.0;
			}

			if( m_pPlayer.pev.button & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT) != 0 )
			{
				if( m_iState != STATE_THROW_GRENADE and m_iState != STATE_RELOAD and m_iState != STATE_DEPLOY_NEEDLE and m_iState != STATE_CHARGE_SET and m_iState != STATE_MEDIC_REVIVE )
				{
					m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
					DoMovementAnimation();
					self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
				}
			}
			else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pPlayer.pev.velocity.Length2D() > 0 )
			{
				if( m_iState != STATE_OFGRUNT_ATTACK_MGUN and m_iState != STATE_OFGRUNT_ATTACK_MELEE and m_iState != STATE_RELOAD and m_iState != STATE_CHARGE_SET and m_iState != STATE_THROW_GRENADE )
				{
					m_pPlayer.SetMaxSpeedOverride( 270 );
					DoMovementAnimation();
					self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
				}
			}

			if( m_pPlayer.pev.velocity.Length() <= 0 and m_iState != STATE_OFGRUNT_ATTACK_MELEE and m_iState != STATE_OFGRUNT_ATTACK_MGUN and m_iState != STATE_THROW_GRENADE and m_iState != STATE_RELOAD 
			and m_iState != STATE_DEPLOY_NEEDLE and m_iState != STATE_CHARGE_SET and m_iState != STATE_MEDIC_REVIVE )
			{
				DoIdleAnimation();	
			}		

			if( m_iState == STATE_OFGRUNT_ATTACK_MGUN )
			{
				m_pPlayer.SetMaxSpeedOverride( 0 );

				Vector angDir = m_pPlayer.pev.v_angle;

				if( angDir.x > 360 )
					angDir.x = angDir.x - 360;

				m_pDriveEnt.SetBlending( 0, -angDir.x );
			}
			
			DoReload();
			DoRevive();
			CheckChargePlantingInput();
			DoChargePlanting();
		}
	}

	void InactiveItemPostFrame()
	{
		m_pPlayer.SwitchWeapon(self);
	}
	
	CBaseEntity@ BestVisibleEnemy()
	{
		CBaseEntity@ pReturn = null;
		
		while( (@pReturn = g_EntityFuncs.FindEntityInSphere(pReturn, m_pPlayer.pev.origin, 8192, "*", "classname")) !is null )
		{
			if( pReturn.pev.FlagBitSet(FL_MONSTER | FL_CLIENT) and pReturn.IsAlive() and m_pPlayer.IRelationship(pReturn) > R_NO and pReturn.IsAlive() and pReturn.edict() !is m_pPlayer.edict() 
			and !pReturn.pev.FlagBitSet(FL_NOTARGET) and ( m_pPlayer.FInViewCone(pReturn) and m_pPlayer.FVisible(pReturn, true) ) and pReturn.pev.classname != "playerhornet" and pReturn.pev.sequence != 0 )
			{
				return pReturn;
			}
		}

		return null;
	}

	void LookForEnemies()
	{
		if( m_pEnemy is null )
		{
			m_pPlayer.Look( 8192 );
			CBaseEntity@ pEnemy = m_pPlayer.BestVisibleEnemy();
			if( pEnemy !is null and m_pPlayer.FVisible(pEnemy, true) and m_pPlayer.FInViewCone(pEnemy) )
			{
				m_bIsInCombat = true;
				@m_pEnemy = pEnemy;
				m_flNextIdleSound = g_Engine.time + 6.0;

				if( m_flNextAlertGrowl < g_Engine.time )
				{
					AlertGrowl();
					m_flNextAlertGrowl = g_Engine.time + 9999;
				}
			}
			/*else if( pEnemy !is null and !m_pPlayer.FVisible(pEnemy, true) and m_flNextIdleSound < g_Engine.time )
			{
					m_bIsInCombat = true;
					@m_pEnemy = pEnemy;
					m_flNextIdleSound = g_Engine.time + 2.0;
			}*/
		}
		else if( !m_pEnemy.IsAlive() or !m_pPlayer.FVisible(m_pEnemy, true) or !m_pPlayer.FInViewCone(m_pEnemy) )
		{
			@m_pEnemy = null;
			m_bIsInCombat = false;
			m_flNextAlertGrowl = g_Engine.time + 0.0;
		}
	}

	void AlertGrowl()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_ALERT", 1.0, ATTN_NORM, 0, PITCH_NORM );
		}
	}

	float WeaponTimeBase() // map time
	{
		return g_Engine.time;
	}

	void ItemPostFrame()
	{
		if( m_iWeaponModeOFGrunt == MODE_M16 )
		{
			if( m_iBurstLeft > 0 )
			{
				if( m_flNextBurstFireTime < WeaponTimeBase() )
				{
					if( self.m_iPrimaryAmmoType <= 0 )
					{
						m_iBurstLeft = 0;
						return;
					}
					else
						--m_iBurstLeft;

					FirstAttackCommon();

					if( m_iBurstLeft > 0 )
						m_flNextBurstFireTime = g_Engine.time + 0.085;
					else
						m_flNextBurstFireTime = 0;
				}

				//While firing a burst, don't allow reload or any other weapon actions. Might be best to let some things run though.
				return;
			}
		}

		BaseClass.ItemPostFrame();
	}

	void FirstAttackCommon()
	{
		if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
		{
			DoIdleAnimation();
			self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
			return;
		}

		m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

		m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;
		m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME;
		m_pPlayer.m_iWeaponFlash = NORMAL_GUN_FLASH;

		Vector vecSrc = m_pPlayer.GetGunPosition();
		Vector vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
		Vector vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_2DEGREES );
		Math.MakeVectors( m_pPlayer.pev.v_angle );
		Math.MakeAimVectors( m_pPlayer.pev.v_angle );
		Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
		g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 12, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
		m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 2, g_EngineFuncs.CVarGetFloat('sk_556_bullet'), m_pDriveEnt.pev );

		WW2DynamicLight( m_pPlayer.pev.origin, 8, 240, 180, 0, 8, 50 );
		m_flNextIdleSound = g_Engine.time + 4.0;
		m_fNextRegenTick = g_Engine.time + 6.0;
	}

	void RegenerateHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if ( m_fNextRegenTick < g_Engine.time and m_pPlayer.pev.health < 100 )
			{
				m_pPlayer.pev.health += 10;
				m_fNextRegenTick = g_Engine.time + 1.0;
			}
		}
	}

	void CheckMaxHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_flNextHealthCheck < g_Engine.time and m_pPlayer.pev.health > 500 )
			{
				m_pPlayer.pev.health = 500;

				if( m_pPlayer.pev.max_health > 500 || m_pPlayer.pev.max_health < 500)
				{
					m_pPlayer.pev.max_health = 500;
				}

				m_flNextHealthCheck = g_Engine.time + 0.1;
			}
		}
	}

	void PainSound()
	{
		if( m_pDriveEnt !is null )
		{
			switch( Math.RandomLong(0, 4) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "fgrunt/gr_pain1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "fgrunt/gr_pain2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "fgrunt/gr_pain3.wav", VOL_NORM, ATTN_NORM ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "fgrunt/gr_pain4.wav", VOL_NORM, ATTN_NORM ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "fgrunt/gr_pain5.wav", VOL_NORM, ATTN_NORM ); break;
			}

			m_flNextIdleSound = g_Engine.time + 4.0;
		}
	}

	void IdleSound ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_bIsInCombat and m_flNextIdleSound < g_Engine.time )
			{
				g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_CHARGE", 1.0, ATTN_NORM, 0, PITCH_NORM );
				m_flNextIdleSound = g_Engine.time + 2.0;
			}
			else if( !m_bIsInCombat and m_flNextIdleSound < g_Engine.time )
			{
				m_flNextIdleSound = g_Engine.time + 5.0;
				m_fNextCombatSound = 0.0;
				m_bIsInCombat = false;

				switch( Math.RandomLong(0, 4) )
				{
					case 0: g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_IDLE", 1.0, ATTN_NORM, 0, m_iVoicePitch ); break;
					case 1: g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_QUEST", 1.0, ATTN_NORM, 0, m_iVoicePitch ); break;
					case 2: g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_ANSWER", 1.0, ATTN_NORM, 0, m_iVoicePitch ); break;
					case 3: g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_CLEAR", 1.0, ATTN_NORM, 0, m_iVoicePitch ); break;
					case 4: g_SoundSystem.PlaySentenceGroup( m_pDriveEnt.edict(), "FG_CHECK", 1.0, ATTN_NORM, 0, m_iVoicePitch ); break;
				}

			}
		}
	}

	void DoReload()
	{
		if( ( m_iWeaponModeOFGrunt == MODE_TORCH or m_iWeaponModeOFGrunt == MODE_MEDIC ) && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 7 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( ( m_iWeaponModeOFGrunt == MODE_MP5 or m_iWeaponModeOFGrunt == MODE_M16 ) && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 36 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( m_iWeaponModeOFGrunt == MODE_SHOTGUN && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 8 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( m_iWeaponModeOFGrunt == MODE_SAW && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 200 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( m_iWeaponModeOFGrunt == MODE_MEDIC && m_bSyringeOut ) return;

		if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pDriveEnt !is null and m_pPlayer.IsAlive() and m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
		{
			m_iState = STATE_RELOAD;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			{
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload_shotgun");
			}
			else
			{
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload_mp5");
			}

			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			if( m_iWeaponModeOFGrunt == MODE_TORCH or m_iWeaponModeOFGrunt == MODE_MEDIC )
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
			}
			else if( m_iWeaponModeOFGrunt == MODE_SAW )
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 200);
			}
			else if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 8);
			}
			else
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
			}

			SetThink( ThinkFunction(this.ReloadThink) );
			pev.nextthink = g_Engine.time + 1.35;

			m_fNextRegenTick = g_Engine.time + 6.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 2.0;
			self.m_flNextSecondaryAttack = g_Engine.time + 2.0;
			self.m_flTimeWeaponIdle = g_Engine.time + 2.0;
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 and (m_pPlayer.pev.button & IN_RELOAD) != 0 
		and m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("reload_mp5") and m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("reload_shotgun") and m_pDriveEnt !is null )
		{
			m_iState = STATE_RELOAD;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			{
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload_shotgun");
			}
			else
			{
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload_mp5");
			}

			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.ReloadThink) );
			pev.nextthink = g_Engine.time + 1.35;

			m_fNextRegenTick = g_Engine.time + 6.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 2.0;
			self.m_flNextSecondaryAttack = g_Engine.time + 2.0;
			self.m_flTimeWeaponIdle = g_Engine.time + 2.0;
		}
	}

	void ReloadThink()
	{
		if( m_pDriveEnt !is null and m_pPlayer.pev.velocity.Length() <= 0 )
		{
			m_fNextCombatSound = 0.0;

			if( m_iWeaponModeOFGrunt == MODE_TORCH or m_iWeaponModeOFGrunt == MODE_MEDIC )
			{
				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "hgrunt/gr_reload1.wav", VOL_NORM, 1.0 );
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
			}
			else if( m_iWeaponModeOFGrunt == MODE_SAW )
			{
				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/saw_reload.wav", VOL_NORM, 1.0 );
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 200);
			}
			else if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
			{
				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "hgrunt/gr_reload1.wav", VOL_NORM, 1.0 );
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 8);
			}
			else
			{
				g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "hgrunt/gr_reload1.wav", VOL_NORM, 1.0 );
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 36);
			}

			DoIdleAnimation();
			SetThink( null );
		}

		pev.nextthink = g_Engine.time + 0.1;
	}

	void ReloadSoundThink()
	{
		if( m_pDriveEnt is null ) return;
		if( m_iState != STATE_RELOAD ) return;
		if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("reload_mp5") and m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("reload_shotgun") ) return;

		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload_mp5") )
		{
			switch( GetFrame(61) )
			{
				case 11: { if( m_iWeaponStage == 0 ) { g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_ITEM, "items/cliprelease1.wav", VOL_NORM, ATTN_NORM ); m_iWeaponStage++; } break; }
				case 39: { if( m_iWeaponStage == 1 ) { g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_ITEM, "items/clipinsert1.wav", VOL_NORM, ATTN_NORM ); m_iWeaponStage++; } break; }
			}
		}
		else if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload_shotgun") )
		{
			switch( GetFrame(61) )
			{
				case 11: { if( m_iWeaponStage == 0 ) { g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_ITEM, "weapons/reload1.wav", VOL_NORM, ATTN_NORM ); m_iWeaponStage++; } break; }
				case 29: { if( m_iWeaponStage == 1 ) { g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_ITEM, "weapons/reload3.wav", VOL_NORM, ATTN_NORM ); m_iWeaponStage++; } break; }
				case 39: { if( m_iWeaponStage == 2 ) { g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_ITEM, "weapons/reload1.wav", VOL_NORM, ATTN_NORM ); m_iWeaponStage++; } break; }
			}
		}

		pev.nextthink = g_Engine.time + 0.01;
	}

	void MeleeAttackThink()
	{
		m_flNextIdleSound = g_Engine.time + 6.0;
		m_fNextRegenTick = g_Engine.time + 6.0;

		if( m_pPlayer is null or !m_pPlayer.IsAlive() or m_pDriveEnt is null )
		{
			SetThink( null );
			return;
		}

		if( m_iBitchSlap == 0 ) //left slap
		{
			g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, pAttackMissSounds[Math.RandomLong(0,(pAttackMissSounds.length() - 1))], VOL_NORM, ATTN_NORM, 0, Math.RandomLong(95, 105) );
			CBaseEntity@ pHurt = CheckTraceHullAttack( OFGRUNT_MELEE_DMG_RANGE, OFGRUNT_MELEE_DMG, DMG_ENERGYBEAM | DMG_NEVERGIB );

			if( pHurt !is null )
			{
				int rel = m_pPlayer.IRelationship(pHurt);
				bool isFriendly = rel == R_AL or rel == R_NO;

				if( pHurt.pev.flags & (FL_MONSTER | FL_CLIENT) != 0 ) //and !isFriendly )
				{
					pHurt.pev.punchangle.x = 15;
					pHurt.pev.velocity = pHurt.pev.velocity + g_Engine.v_forward * 100 + g_Engine.v_up * 50;
				}
			}

		}

		SetThink( null );
	}

	void GrenadeThrowThink()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() )
		{
			if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("throwgrenade") and m_pDriveEnt.m_fSequenceFinished )
			{
				if( m_bIsInCombat )
				{
					DoCombatIdleAnimation();
				}
				else
				{
					DoIdleAnimation();
				}

				if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
				{
					m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
				}
				else
				{
					m_pPlayer.SetMaxSpeedOverride( 270 );
				}
			}

			Vector angThrow = m_pPlayer.pev.v_angle + m_pPlayer.pev.punchangle;
			if ( angThrow.x < 0 )
				angThrow.x = -10 + angThrow.x * ( ( 90 - 10 ) / 90.0 );
			else
				angThrow.x = -10 + angThrow.x * ( ( 90 + 10 ) / 90.0 );
			float flVel = ( 90 - angThrow.x ) * 4;
			if ( flVel > 500 )
				flVel = 500;
			g_EngineFuncs.MakeVectors( angThrow );
			Vector vecSrc = m_pPlayer.pev.origin + m_pPlayer.pev.view_ofs + g_Engine.v_forward * 16;
			Vector vecThrowBot = g_Engine.v_forward * 800 +  g_Engine.v_up * 100;

			//HLSDK_CGrenade@ pGrenadeBot = ShootTimed( m_pPlayer.pev, vecSrc, vecThrowBot, 4.0 );
			g_EntityFuncs.ShootTimed( m_pPlayer.pev, m_pDriveEnt.pev.origin + g_Engine.v_forward * 34 + Vector (0, 0, 32), vecThrowBot, 3.5 );
		}

		SetThink( null );
	}

	void GrenadeLaunchThink()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() )
		{
			if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("launchgrenade") and m_pDriveEnt.m_fSequenceFinished )
			{
				if( m_bIsInCombat )
				{
					DoCombatIdleAnimation();
				}
				else
				{
					DoIdleAnimation();
				}


				if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
				{
					m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
				}
				else
				{
					m_pPlayer.SetMaxSpeedOverride( 270 );
				}
			}

			g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM );

			Math.MakeVectors( m_pPlayer.pev.v_angle );

			Vector vecGrenadeOrigin;
			Vector vecGrenadeVelocity = m_pPlayer.pev.velocity + g_Engine.v_forward * 800 + g_Engine.v_up * 50;
			m_pDriveEnt.GetAttachment( 0, vecGrenadeOrigin, void );
			g_EntityFuncs.ShootContact( m_pPlayer.pev, vecGrenadeOrigin, vecGrenadeVelocity );

			if( m_bIsInCombat )
			{
				DoCombatIdleAnimation();
			}
			else
			{
				DoIdleAnimation();
			}
		}

		SetThink( null );
	}

	Vector VecCheckSplatToss( Vector vecSpot1, Vector vecSpot2, float maxHeight )
	{
		TraceResult		tr;
		Vector			vecMidPoint;// halfway point between Spot1 and Spot2
		Vector			vecApex;// highest point 
		Vector			vecScale;
		Vector			vecGrenadeVel;
		Vector			vecTemp;
		float				flGravity = g_EngineFuncs.CVarGetFloat( "sv_gravity" );

		// calculate the midpoint and apex of the 'triangle'
		vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
		g_Utility.TraceLine( vecMidPoint, vecMidPoint + Vector(0, 0, maxHeight), ignore_monsters, m_pDriveEnt.edict(), tr );
		vecApex = tr.vecEndPos;

		g_Utility.TraceLine( vecSpot1, vecApex, dont_ignore_monsters, m_pDriveEnt.edict(), tr );
		if( tr.flFraction != 1.0 )
		{
			// fail!
			return g_vecZero;
		}

		// Don't worry about actually hitting the target, this won't hurt us!

		// How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)?
		float height = (vecApex.z - vecSpot1.z) - 15;
		// How fast does the grenade need to travel to reach that height given gravity?
		float speed = sqrt( 2 * flGravity * height );

		// How much time does it take to get there?
		float time = speed / flGravity;
		vecGrenadeVel = (vecSpot2 - vecSpot1);
		vecGrenadeVel.z = 0;
		float distance = vecGrenadeVel.Length();

		// Travel half the distance to the target in that time (apex is at the midpoint)
		vecGrenadeVel = vecGrenadeVel * ( 0.5 / time );
		// Speed to offset gravity at the desired height
		vecGrenadeVel.z = speed;

		return vecGrenadeVel;
	}

	void DoMovementAnimation()
	{
		float flMinWalkVelocity = -150.0;
		float flMaxWalkVelocity = 150.0;
		//if( m_iState == STATE_DEPLOY_NEEDLE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("pull_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_DEPLOY_NEEDLE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;

		if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
		{
			m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
		}

		if( (m_pPlayer.pev.button & IN_USE) != 0 or IsBetween(m_pPlayer.pev.velocity.Length(), flMinWalkVelocity, flMaxWalkVelocity) )
		{
			if( m_iWeaponModeOFGrunt == MODE_TORCH || m_iWeaponModeOFGrunt == MODE_MEDIC )
			{
				if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("walk1") )
				{
					m_iState = STATE_WALK;
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("walk1");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					//m_pPlayer.SetMaxSpeedOverride( int(SPEED_WALK) );
				}
			}
			else
			{
				if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("walk_new") )
				{
					m_iState = STATE_WALK;
					m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("walk_new");
					m_pDriveEnt.pev.frame = 0;
					m_pDriveEnt.ResetSequenceInfo();
					//m_pPlayer.SetMaxSpeedOverride( int(SPEED_WALK) );
				}
			}

			m_fNextRegenTick = g_Engine.time + 6.0;
			
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
			{
				//nada
			}
			else
			{
				self.m_flNextPrimaryAttack = g_Engine.time + 0.35;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.35;
			}
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
		{
			if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("run") )
			{
				m_iState = STATE_RUN;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("run");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( 270 );
			}

			m_pPlayer.pev.flTimeStepSound = 9999; //prevents normal footsteps from playing, kinda? :hehe:
		}
		else
		{
			if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("run") )
			{
				m_iState = STATE_RUN;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("run");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			}

			m_fNextRegenTick = g_Engine.time + 6.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 0.35;
			self.m_flNextSecondaryAttack = g_Engine.time + 0.35;
			m_pPlayer.pev.flTimeStepSound = 9999; //prevents normal footsteps from playing, kinda? :hehe:
		}
	}

	void DoCombatIdleAnimation()
	{
		if( m_pDriveEnt is null ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("launchgrenade") and !m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload_mp5") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("pull_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_needle") and m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_iState == STATE_CHARGE_SET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_torch") and !m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_iState == STATE_MEDIC_REVIVE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("heal_crouch") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_DEPLOY_NEEDLE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("pull_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_DEPLOY_NEEDLE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;

		if( m_iState != STATE_IDLE_COMBAT )
		{
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
			{
				m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			}
			else
			{
				m_pPlayer.SetMaxSpeedOverride( 270 );
			}

			m_iState = STATE_IDLE_COMBAT;
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("combatidle");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();
		}
	}

	void DoIdleAnimation( bool bOverrideState = false )
	{
		if( m_pDriveEnt is null ) return;
		//if( m_bIsInCombat and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("combatidle") and !m_pDriveEnt.m_fSequenceFinished ) return; //m_iState == STATE_IDLE_COMBAT ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("launchgrenade") and !m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload_mp5") and !m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_iState == STATE_CHARGE_SET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_torch") and !m_pDriveEnt.m_fSequenceFinished ) return;
		if( m_iState == STATE_MEDIC_REVIVE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("heal_crouch") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("pull_needle") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_needle") and m_pDriveEnt.m_fSequenceFinished ) return;

		if( m_iState != STATE_IDLE and !m_bIsInCombat )
		{
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
			{
				m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			}
			else
			{
				m_pPlayer.SetMaxSpeedOverride( 270 );
			}
			
			//if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("combatidle") and m_pDriveEnt.m_fSequenceFinished )
			//{
				m_iState = STATE_IDLE;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("idle1");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
			//}
		}
		else if( m_iState != STATE_IDLE_COMBAT and m_bIsInCombat )
		{
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
			{
				m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			}
			else
			{
				m_pPlayer.SetMaxSpeedOverride( 270 );
			}
			
			//if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("idle") and m_pDriveEnt.m_fSequenceFinished )
			//{
				m_iState = STATE_IDLE_COMBAT;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("combatidle");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
			//}
		}
	}

	void CheckChargePlantingInput()
	{
		if( m_pDriveEnt is null ) return;
		if( m_iState > STATE_RUN ) return;

		if( (m_pPlayer.m_afButtonPressed & IN_USE) != 0 and m_iWeaponModeOFGrunt == MODE_TORCH )
		{
			float flDist;

			CBaseEntity@ pTarget = null;
			while( (@pTarget = g_EntityFuncs.FindEntityByClassname(pTarget, "scripted_sequence")) !is null )
			{
				if( arrsBombTargetNames.find(pTarget.pev.targetname) < 0 ) continue;
				flDist = (m_pPlayer.pev.origin - pTarget.pev.origin).Length();
				if( flDist > 50 ) continue;

				//g_Game.AlertMessage( at_notice, "Targetname %1 found!\n", pTarget.pev.targetname );

				string sNewTargetName = string(pTarget.pev.targetname) + m_pPlayer.entindex();
				pTarget.pev.targetname = sNewTargetName;

				//g_Game.AlertMessage( at_notice, "Targetname set to %1\n", pTarget.pev.targetname );
				@m_pBombTarget = pTarget;
				break;
			}

			if( pTarget !is null )
			{
				m_pDriveEnt.pev.angles.y = pTarget.pev.angles.y;
				m_iState = STATE_CHARGE_SET;
				m_pPlayer.SetMaxSpeedOverride( 0 );
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("store_torch");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.pev.framerate = 1.0;
				m_pDriveEnt.ResetSequenceInfo();
				m_iWeaponStage = 0;
			}
		}

		if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_iWeaponModeOFGrunt == MODE_TORCH )
		{
			float flDist;

			CBaseEntity@ pTarget = null;
			while( (@pTarget = g_EntityFuncs.FindEntityByClassname(pTarget, "scripted_sequence")) !is null )
			{
				if( arrsBombTargetNames.find(pTarget.pev.targetname) < 0 ) continue;
				flDist = (m_pPlayer.pev.origin - pTarget.pev.origin).Length();
				if( flDist > 50 ) continue;

				//g_Game.AlertMessage( at_notice, "Targetname %1 found!\n", pTarget.pev.targetname );

				string sNewTargetName = string(pTarget.pev.targetname) + m_pPlayer.entindex();
				pTarget.pev.targetname = sNewTargetName;

				//g_Game.AlertMessage( at_notice, "Targetname set to %1\n", pTarget.pev.targetname );
				@m_pBombTarget = pTarget;
				break;
			}

			if( pTarget !is null )
			{
				m_pDriveEnt.pev.angles.y = pTarget.pev.angles.y;
				m_iState = STATE_CHARGE_SET;
				m_pPlayer.SetMaxSpeedOverride( 0 );
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("store_torch");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				//m_pDriveEnt.pev.framerate = 1.0;
				m_iWeaponStage = 0;
			}
		}
	}

	void DoChargePlanting()
	{
		if( m_pDriveEnt is null or m_pBombTarget is null ) return;
		if( m_iState != STATE_CHARGE_SET ) return;

		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("store_torch") and m_pDriveEnt.m_fSequenceFinished )
		{
			m_pBombTarget.SUB_UseTargets( m_pPlayer, USE_ON, 0 );
			m_pPlayer.pev.frags += 30;

			DoIdleAnimation();
		}
	}

	void DoRevive()
	{
		if( m_pDriveEnt is null ) return;
		if( m_iWeaponModeOFGrunt != MODE_MEDIC ) return;
		if( !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;

		if( (m_pPlayer.pev.button & IN_DUCK != 0) && (m_pPlayer.pev.button & IN_USE != 0) and m_iWeaponModeOFGrunt == MODE_MEDIC )
		{
			if( m_bSyringeOut )
			{
				g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Offense\n Press TERTIARY FIRE to change\n Reviving in progress...\n" );
			}

			m_bSyringeOut = false;
			m_iState = STATE_MEDIC_REVIVE;
			m_pPlayer.SetMaxSpeedOverride( 0 );
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("heal_crouch");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();
			m_pDriveEnt.SetBodygroup( 3, 0 );
			ReviveFallenAlly( m_pPlayer, m_pPlayer.pev.origin, 100 );

			SetThink( ThinkFunction(this.StartReviveThink) );
			pev.nextthink = g_Engine.time + 0.01;
			m_iWeaponStage = 0;
		}
	}

	void StartReviveThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_MEDIC_REVIVE or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) )
		{
			SetThink( null );
			m_iWeaponStage = 0;
			return;
		}

		if( m_iState == STATE_MEDIC_REVIVE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("heal_crouch") )
		{
			switch( GetFrame(121) )
			{
			    case 10: { if( m_iWeaponStage == 0 ) { m_pDriveEnt.SetBodygroup( 3, 3 ); m_iWeaponStage++; } break; }
			    case 27: { if( m_iWeaponStage == 1 ) { m_pDriveEnt.SetBodygroup( 3, 2 ); m_iWeaponStage++; } break; }
			    case 100: { if( m_iWeaponStage == 2 ) { m_pDriveEnt.SetBodygroup( 3, 3 ); m_iWeaponStage++; } break; }
			    case 110: { if( m_iWeaponStage == 3 ) { m_pDriveEnt.SetBodygroup( 3, 0 ); m_iWeaponStage++; } break; }
			    case 119: { if( m_iWeaponStage == 4 ) { ReviveFallenAlly2( m_pPlayer, m_pPlayer.pev.origin, 100 ); m_iWeaponStage++; } break; }
			    case 120: { if( m_iWeaponStage == 5 ) { DoIdleAnimation(); SetThink( null ); m_iWeaponStage++; } break; }
			}

			pev.nextthink = g_Engine.time + 0.01;
		}
	}

	void ReviveFallenAlly(CBaseEntity@ pWho, Vector vecWhere, float flRadius) 
	{
		array <CBaseEntity@> aEnts(32);
		int iNum = g_EntityFuncs.MonstersInSphere(aEnts, vecWhere, flRadius);
		for (int i = 0; i < iNum; ++i) 
		{
			CBaseMonster@ pEnt = cast<CBaseMonster@>( aEnts[i] );
			if ( !pEnt.IsAlive() && pEnt.IsMonster() && pEnt.IsPlayerAlly() && pEnt !is pWho ) 
			{
				if( pEnt.FVisible(vecWhere) && ( pWho.pev.origin - pEnt.pev.origin ).Length() <= 80)
				{
					pEnt.pev.renderamt = 255;
					pEnt.pev.nextthink = g_Engine.time + 8.0f;
				}
			}
		}
	}

	void ReviveFallenAlly2(CBaseEntity@ pWho, Vector vecWhere, float flRadius) 
	{
		array <CBaseEntity@> aEnts(32);
		int iNum = g_EntityFuncs.MonstersInSphere(aEnts, vecWhere, flRadius);
		for (int i = 0; i < iNum; ++i) 
		{
			CBaseMonster@ pEnt = cast<CBaseMonster@>( aEnts[i] );
			if ( !pEnt.IsAlive() && pEnt.IsMonster() && pEnt.IsPlayerAlly() || ( pEnt.IsPlayer() && pEnt !is pWho ) ) 
			{
				if( pEnt.FVisible(vecWhere) && ( pWho.pev.origin - pEnt.pev.origin ).Length() <= 80)
				{
					pEnt.Revive();
					pEnt.EndRevive( 0.5 );
					m_pPlayer.pev.frags += 5;
				}
			}
		}
	}

	/*void ReviveFallenAlly()
	{
		CBaseEntity@ pBestTarget = null;
		float flBestDist = 80;

		CBaseEntity@ pEntity;
		while ((@pEntity = g_EntityFuncs.FindEntityInSphere(pEntity, m_pPlayer.GetOrigin(), REVIVE_RADIUS, "*", "classname")) !is null)
		{
			if (pEntity is null || !IsValidReviveTarget(pEntity))
				continue;

			float flDist = (pEntity.pev.origin - m_pPlayer.pev.origin).Length();

			if (pBestTarget is null)
			{
				flBestDist = flDist;
				@pBestTarget = pEntity;
				continue;
			}

			if (IsBetterReviveTarget(pEntity, pBestTarget, flDist, flBestDist))
			{
				flBestDist = flDist;
				@pBestTarget = pEntity;
			}
		}

		if (pBestTarget.m_fCanFadeStart) 
		{
			pBestTarget.pev.renderamt = 255;
			pBestTarget.pev.nextthink = g_Engine.time + 2.0f;
		}

		if (m_reviveChargedTime < g_Engine.time)
		{
			m_reviveChargedTime = 0;
			CBaseMonster@ pMonster = (pBestTarget.GetClassname() == "monster_") ? cast<CBaseMonster@>(g_EntityFuncs.Instance(int(pBestTarget.pev.renderamt))) : pBestTarget.MyMonsterPointer();
			pMonster.Revive();
			pMonster.pev.health = (pMonster.pev.max_health / 2);
		}
	}

	bool IsValidReviveTarget(CBaseEntity@ pEntity)
        {
		return (pEntity.IsPlayer() && (pEntity.pev.deadflag != DEAD_NO && cast<CBasePlayer@>(pEntity).GetObserver().HasCorpse()) && pEntity.pev.iuser1 == 0) ||
			(pEntity.GetClassname() == "deadplayer") ||
			(pEntity.IsMonster() && !pEntity.IsPlayer());
	}

        bool IsBetterReviveTarget(CBaseEntity@ pEntity, CBaseEntity@ pBestTarget, float flDist, float flBestDist)
        {
		bool isBetterClass = pEntity.IsPlayer() && !pBestTarget.IsPlayer();
		bool isWorseClass = !pEntity.IsPlayer() && pBestTarget.IsPlayer();

		return (flDist < flBestDist && !isWorseClass) || isBetterClass;
        }*/

	void spawn_driveent()
	{
		if( !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) )
		{
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Unable to deploy\nwhile in the air" );
			return;
		}

		m_pPlayer.pev.velocity = g_vecZero;
		Vector vecOrigin = m_pPlayer.pev.origin;
		vecOrigin.z -= 36.0;
		@m_pDriveEnt = cast<CBaseAnimating@>( g_EntityFuncs.Create("cnpc_ofgrunt", vecOrigin, Vector(0, m_pPlayer.pev.angles.y, 0), false, m_pPlayer.edict()) );
		m_pDriveEnt.pev.set_controller( 0,  127 );
		m_pDriveEnt.pev.set_controller( 1,  127 );
		m_pPlayer.m_flFieldOfView = 0.25;
		m_pPlayer.m_flDistLook = 8192.0;
		m_pPlayer.pev.effects |= EF_NODRAW;
		m_pPlayer.pev.iuser3 = 1;
		m_pPlayer.pev.fuser4 = 1; //disable jump
		self.m_bExclusiveHold = true;
		m_pPlayer.pev.armorvalue = 10.0;
		m_pPlayer.pev.armortype = 10.0;
		if( m_pPlayer.pev.health < 100 )
		{
			m_pPlayer.pev.health = 500 * ( m_pPlayer.pev.health / m_pPlayer.pev.max_health );
			m_pPlayer.pev.max_health = 500;
		}
		else
		{
			m_pPlayer.pev.health = 500;
			m_pPlayer.pev.max_health = 500;
		}

		NetworkMessage m1( MSG_ONE_UNRELIABLE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cam_idealdist 128;cam_idealpitch 10;cl_movespeedkey 0.175;\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, CNPC::CNPC_OFGRUNT );

		if( m_iWeaponModeOFGrunt == MODE_SHOTGUN )
		{
			m_pDriveEnt.SetBodygroup( 3, 1 );
			m_pDriveEnt.SetBodygroup( 2, 3 );

			switch( Math.RandomLong(1, 2) )
			{
				case 1: m_pDriveEnt.SetBodygroup( 1, 1 ); break;
				case 2: m_pDriveEnt.SetBodygroup( 1, 2 ); break;
			}
		}
		else if( m_iWeaponModeOFGrunt == MODE_SAW )
		{
			m_pDriveEnt.SetBodygroup( 3, 2 );
			m_pDriveEnt.SetBodygroup( 2, 1 );

			switch( Math.RandomLong(1, 2) )
			{
				case 1: m_pDriveEnt.SetBodygroup( 1, 1 ); break;
				case 2: m_pDriveEnt.SetBodygroup( 1, 3 ); break;
			}
		}
		else if( m_iWeaponModeOFGrunt == MODE_MP5 )
		{
			m_pDriveEnt.SetBodygroup( 3, 4 );

			switch( Math.RandomLong(1, 2) )
			{
				case 1: m_pDriveEnt.SetBodygroup( 1, 0 ); break;
				case 2: m_pDriveEnt.SetBodygroup( 1, 1 ); break;
			}
		}
		else if( m_iWeaponModeOFGrunt == MODE_M16 )
		{
			m_pDriveEnt.SetBodygroup( 3, 0 );

			switch( Math.RandomLong(1, 2) )
			{
				case 1: m_pDriveEnt.SetBodygroup( 1, 0 ); break;
				case 2: m_pDriveEnt.SetBodygroup( 1, 1 ); break;
			}
		}
		else if( m_iWeaponModeOFGrunt == MODE_TORCH )
		{
			string szMapName = string(g_Engine.mapname);
			if( szMapName == "sandstone" )
			{
				g_EntityFuncs.SetModel( m_pDriveEnt, "models/sandstone/engineer.mdl" );
			}
			else
			{
				g_EntityFuncs.SetModel( m_pDriveEnt, "models/cnpc/hgrunt_torchf.mdl" );
			}
			g_EntityFuncs.SetSize( m_pDriveEnt.pev, Vector(-16, -16, 0), Vector(16, 16, 72) );
			g_EntityFuncs.SetOrigin( m_pDriveEnt, pev.origin );
			m_pDriveEnt.pev.solid = SOLID_NOT;
			m_pDriveEnt.pev.movetype = MOVETYPE_NOCLIP;
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Ability: Plant C4\n Press USE key to plant\n (sandstone map only)\n" );

			m_pDriveEnt.SetBodygroup( 2, 0 );
		}
		else if( m_iWeaponModeOFGrunt == MODE_MEDIC )
		{
			g_EntityFuncs.SetModel( m_pDriveEnt, "models/cnpc/hgrunt_medicf.mdl" );
			g_EntityFuncs.SetSize( m_pDriveEnt.pev, Vector(-16, -16, 0), Vector(16, 16, 72) );
			g_EntityFuncs.SetOrigin( m_pDriveEnt, pev.origin );
			m_pDriveEnt.pev.solid = SOLID_NOT;
			m_pDriveEnt.pev.movetype = MOVETYPE_NOCLIP;
			m_bSyringeOut = false;
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Offense\n Press TERTIARY FIRE to change\n Press DUCK and USE to revive\n" );

			m_pDriveEnt.SetBodygroup( 2, 0 );
		}
	}

	void ResetPlayer()
	{
		m_pPlayer.pev.iuser3 = 0;
		m_pPlayer.pev.fuser4 = 0; //enable jump
		m_pPlayer.SetMaxSpeedOverride( -1 );

		NetworkMessage m1( MSG_ONE_UNRELIABLE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cl_movespeedkey 0.3;\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, 0 );
	}
}

class cnpc_ofgrunt : ScriptBaseAnimating
{
	protected CBasePlayer@ m_pOwner
	{
		get { return cast<CBasePlayer@>( g_EntityFuncs.Instance(pev.owner) ); }
	}

	private float m_flNextOriginUpdate; //hopefully fixes hacky movement on other players
	private float m_flNextPainSound = 0.0;

	void Spawn()
	{
		if( !self.SetupModel() )
		{
			g_EntityFuncs.SetModel( self, "models/cnpc/hgrunt_opforf.mdl" );
		}
		g_EntityFuncs.SetSize( self.pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
		g_EntityFuncs.SetOrigin( self, pev.origin );
		g_EngineFuncs.DropToFloor( self.edict() );

		pev.solid = SOLID_NOT;
		pev.movetype = MOVETYPE_NOCLIP;

		self.pev.sequence = self.LookupSequence("idle1");
		self.pev.frame = 0;
		self.ResetSequenceInfo();

		m_flNextOriginUpdate = g_Engine.time;

		SetThink( ThinkFunction(this.DriveThink) );
		pev.nextthink = g_Engine.time;
	}

	void DriveThink()
	{
		if( m_pOwner is null or !m_pOwner.IsConnected() or m_pOwner.pev.deadflag != DEAD_NO )
		{
			pev.velocity = g_vecZero;
			SetThink( ThinkFunction(this.DieThink) );
			pev.nextthink = g_Engine.time;

			return;
		}

		if( m_flNextOriginUpdate < g_Engine.time )
		{
			Vector vecOrigin = m_pOwner.pev.origin;
			vecOrigin.z -= 36.0;
			g_EntityFuncs.SetOrigin( self, vecOrigin );
			m_flNextOriginUpdate = g_Engine.time + 0.1;
		}

		pev.velocity = m_pOwner.pev.velocity;

		pev.angles.x = 0;

		if( pev.velocity.Length2D() > 0.0 and (pev.sequence == self.LookupSequence("walk1") or pev.sequence == self.LookupSequence("walk_new") or pev.sequence == self.LookupSequence("run")) )
			pev.angles.y = Math.VecToAngles( pev.velocity ).y;
		else
			pev.angles.y = m_pOwner.pev.angles.y;

		pev.angles.z = 0;

		self.StudioFrameAdvance();

		pev.nextthink = g_Engine.time + 0.01;
	}

	void DieThink()
	{
		array<string> arrsDeathAnims = 
		{
			"dieback1",
			"diebackwards",
			"diegutshot",
			"diesimple",
			"dieheadshot"
		};

		pev.sequence = self.LookupSequence( arrsDeathAnims[Math.RandomLong(0, arrsDeathAnims.length()-1)] );
		pev.frame = 0;
		self.ResetSequenceInfo();
		g_EngineFuncs.DropToFloor( self.edict() );

		g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, pDieSounds[Math.RandomLong(0, pDieSounds.length()-1)], VOL_NORM, ATTN_NORM );

		SetThink( ThinkFunction(this.SUB_StartFadeOut) );
		pev.nextthink = g_Engine.time + 1.0;
	}

	void SUB_StartFadeOut()
	{
		if( pev.rendermode == kRenderNormal )
		{
			pev.renderamt = 255;
			pev.rendermode = kRenderTransTexture;
		}

		pev.solid = SOLID_NOT;
		pev.avelocity = g_vecZero;

		pev.nextthink = g_Engine.time + 0.1;
		SetThink( ThinkFunction(this.SUB_FadeOut) );
	}

	void SUB_FadeOut()
	{
		if( pev.renderamt > 7 )
		{
			pev.renderamt -= 7;
			pev.nextthink = g_Engine.time + 0.1;
		}
		else 
		{
			pev.renderamt = 0;
			pev.nextthink = g_Engine.time + 0.2;
			SetThink( ThinkFunction(this.SUB_Remove) );
		}
	}

	void SUB_Remove()
	{
		self.UpdateOnRemove();

		if( pev.health > 0 )
			pev.health = 0;

		NetworkMessage killbeam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
			killbeam.WriteByte(TE_KILLBEAM);
			killbeam.WriteShort(self.entindex());
		killbeam.End();

		g_EntityFuncs.Remove(self);
	}
}

void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_ofgrunt::cnpc_ofgrunt", "cnpc_ofgrunt" );
	g_Game.PrecacheOther( "cnpc_ofgrunt" );

	g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_ofgrunt::weapon_ofgrunt", sWeaponName );
	g_ItemRegistry.RegisterWeapon( sWeaponName, "controlnpc/hgrunts", "ofgrunt", "ofgrenades" );
	g_Game.PrecacheOther( sWeaponName );
}

} //namespace cnpc_ofgrunt END

/* FIXME
Holding any button after attacking will cause the animation to freeze at the last frame until released
*/

/* TODO
Use turning animations 
*/
