namespace cnpc_agrunt
{

const uint g_MaxAmmoPri		= 999;
const string sWeaponName	= "weapon_agrunt";
const float CD_HORNET		= 1.9;
const float CD_MELEE		= 1.0;

const float DMG_MELEE		= 100.0;
const float MELEE_RANGE		= 90.0;

const float HORNET_REFIRE	= 0.2;
const float CNPC_IDLESOUND	= 10.0;

const float SPEED_WALK		= (15.501198 * CNPC::flModelToGameSpeedModifier); //35.501198
const float SPEED_RUN		= (170.424637 * CNPC::flModelToGameSpeedModifier); //270.424637
const Vector ED_COLOR		= Vector( 255, 0, 0 );

const array<string> pSqknestSounds = 
{
	"debris/flesh1.wav",
	"debris/flesh2.wav",
	"debris/flesh3.wav",
	"debris/flesh4.wav",
	"debris/flesh5.wav"
};

const array<string> pAttackHitSounds = 
{
	"zombie/claw_strike1.wav",
	"zombie/claw_strike2.wav",
	"zombie/claw_strike3.wav"
};

const array<string> pAttackMissSounds = 
{
	"zombie/claw_miss1.wav",
	"zombie/claw_miss2.wav"
};

const array<string> pPainSounds = 
{
	"agrunt/ag_pain1.wav",
	"agrunt/ag_pain2.wav",
	"agrunt/ag_pain3.wav",
	"agrunt/ag_pain4.wav",
	"agrunt/ag_pain5.wav"
};

const array<string> pDieSounds = 
{
	"agrunt/ag_die1.wav",
	"agrunt/ag_die4.wav",
	"agrunt/ag_die5.wav"
};

const array<string> pIdleSounds = 
{
	"agrunt/ag_idle1.wav",
	"agrunt/ag_idle2.wav",
	"agrunt/ag_idle3.wav",
	"agrunt/ag_idle4.wav"
};

const array<string> pAttackSounds = 
{
	"agrunt/ag_attack1.wav",
	"agrunt/ag_attack2.wav",
	"agrunt/ag_attack3.wav"
};

enum states_e
{
	STATE_IDLE = 0,
	STATE_WALK,
	STATE_RUN,
	STATE_DEATH,
	STATE_ATTACK_MELEE,
	STATE_ATTACK_HORNET,
	STATE_THROW_SNARK
};

enum anim_e
{
	ANIM_IDLE = 0,
	ANIM_WALK = 2,
	ANIM_RUN,
	ANIM_MELEE_R = 8,
	ANIM_MELEE_L,
	ANIM_SHOOT = 19,
	ANIM_QUICKSHOOT,
	ANIM_LONGSHOOT,
	ANIM_DEATH1,
	ANIM_DEATH2,
	ANIM_DEATH3,
	ANIM_DEATH4,
	ANIM_DEATH5,
	ANIM_FLOAT,
	ANIM_LAND = 31,
	ANIM_THROW
};

class weapon_agrunt : CBaseDriveWeapon
{
	float m_flRechargeTime;
	float m_flNextIdleSound;
	float m_fNextRegenTick;
	float m_fNextHealthCheck = 0.0;
	float m_flNextShortBurst1;
	float m_flNextShortBurst2;
	private bool m_bShotBlocked;
	private int m_iLastWord;
	private int m_iShootStage;
	private int m_iShootStage2;
	//private int m_iState;
	private int m_iAutoDeploy;
	private int m_iRandomAttack;
	private float m_flNextFragCheck = 0.0;
	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 float m_flStopHornetAttack;
	private float m_flStopHornetAttack2;
	private float m_flStopHornetAttack3;
	private float m_flStopHornetAttack4;
	private float m_flStopHornetAttack5;
	private float m_flStopSnarkThrow;
	private float m_flNextSpeakTime;
	private float m_flNextWordTime;
	private float m_flNextAlertGrowl;
	private float flDistToEnemy;
	private float flDistToEnemy2;

	protected EHandle m_hHornet;
	protected CBaseEntity@ m_pTrackHornet
	{
		get const { return m_hHornet.GetEntity(); }
		set { m_hHornet = EHandle(@value); }
	}

	void Spawn()
	{
		Precache();

		self.m_iDefaultAmmo = 9999;
		self.m_iDefaultSecAmmo = 0;

		g_EntityFuncs.SetModel( self, "models/npcspawner/npc_agrunt.mdl" );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderamt", 128 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "rendermode", 2 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderfx", 15 );

		self.pev.sequence = self.LookupSequence("idle1");
		self.pev.frame = 0;
		self.ResetSequenceInfo();

		self.pev.set_controller( 0,  127 );
		self.pev.set_controller( 1,  127 );

		m_iState = STATE_IDLE;
		m_iRandomAttack = 0;
		m_iAutoDeploy = 1;
		m_iLastWord = 0;
		m_iShootStage = 0;
		m_iShootStage2 = 0;
		m_flNextShortBurst1 = 0;
		m_flNextShortBurst2 = 0;
		m_flNextSpeakTime = m_flNextWordTime = g_Engine.time + 10 + Math.RandomFloat(0, 10);
		m_flNextIdleSound = g_Engine.time + CNPC_IDLESOUND;

		self.FallInit();
	}

	void Precache()
	{
		BaseClass.Precache();
		self.PrecacheCustomModels();
		g_Game.PrecacheGeneric( "sprites/controlnpc/agrunt/weapon_agrunt.txt" );
		g_Game.PrecacheModel( "sprites/controlnpc/crosshairs.spr" );
		g_Game.PrecacheModel( "models/npcspawner/npc_agrunt.mdl" );

		for( uint i = 0; i < pAttackHitSounds.length(); i++ )
			g_SoundSystem.PrecacheSound( pSqknestSounds[i] );
			g_SoundSystem.PrecacheSound( "debris/bustflesh1.wav" );
			g_SoundSystem.PrecacheSound( "debris/bustflesh2.wav" );
			g_SoundSystem.PrecacheSound( "debris/flesh6.wav" );
			g_SoundSystem.PrecacheSound( "debris/flesh7.wav" );
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1		= 9999;
		info.iMaxAmmo2		= 10;
		info.iMaxClip		= WEAPON_NOCLIP;
		info.iSlot		= 1 - 1;
		info.iPosition		= 1 - 1;
		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;

		/*NetworkMessage m1( MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict() );
			m1.WriteLong( g_ItemRegistry.GetIdForName(sWeaponName) );
		m1.End();*/

		//m_pPlayer.SwitchWeapon(self);

		return true;
	}

	bool Deploy()
	{
		spawn_driveent();
		self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;

		return self.DefaultDeploy( "", "", 0, "" );
	}

	bool CanHolster()
	{
		return false;
	}

	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()
	{
		TraceResult tr;
		Math.MakeVectors( m_pPlayer.pev.v_angle );
		g_Utility.TraceHull( m_pPlayer.pev.origin, m_pPlayer.pev.origin + g_Engine.v_forward * 42, dont_ignore_monsters, head_hull, m_pPlayer.edict(), tr );

		/*if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			m_pDriveEnt.pev.gaitsequence = m_pDriveEnt.LookupSequence("run");
			m_pDriveEnt.ResetGaitSequenceInfo();
			//self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
		}*/
		
		/*if( m_pDriveEnt !is null and tr.flFraction != 1.0 and m_pPlayer.pev.velocity.Length2D() == 0.0 and (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
		{
			self.m_flNextPrimaryAttack = g_Engine.time + 1.2; //self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
			//SecondaryAttack();
			//g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Can't shoot here.\n Something's in the way.\n" );
			//DoIdleAnimation();
			m_bShotBlocked = true;
			//return;
		}
		else if( m_pDriveEnt !is null and tr.flFraction == 1.0 and m_pPlayer.pev.velocity.Length2D() == 0.0 and (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
		{
			m_bShotBlocked = false;
		}*/

		if( m_pDriveEnt !is null )
		{
			/*if( m_iState == STATE_RUN )
			{
				m_pDriveEnt.pev.gaitsequence = m_pDriveEnt.LookupSequence("run");
				m_pDriveEnt.ResetGaitSequenceInfo();
			}*/

			if( m_iState != STATE_DEATH ) //!m_bShotBlocked or m_iState >= STATE_RUN )
			{
				switch( Math.RandomLong(1, 3) )
				{
					case 1:
					if( m_flNextShortBurst1 < g_Engine.time )
					{
						m_flNextShortBurst1 = g_Engine.time + 2.0;
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("quickshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.45;
						m_flStopHornetAttack = g_Engine.time + 0.75;

						self.m_flNextPrimaryAttack = g_Engine.time + 0.9;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.0;
					}
					break;
					/*case 2:
					if( m_flNextShortBurst2 < g_Engine.time )
					{
						m_flNextShortBurst2 = g_Engine.time + 3.0;
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("quickshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.45;
						m_flStopHornetAttack = g_Engine.time + 0.75;

						self.m_flNextPrimaryAttack = g_Engine.time + 0.9;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.0;
					}
					break;*/
					case 2:
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("hornetattack");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						//SetThink( ThinkFunction(this.HornetBurstThink) );
						//pev.nextthink = g_Engine.time + 0.01;

						SetThink( ThinkFunction(this.HornetAttackThink2) );
						pev.nextthink = g_Engine.time + 0.3;
						//m_flStopHornetAttack = g_Engine.time + 1.2;
						m_flStopHornetAttack2 = g_Engine.time + 0.4;

						self.m_flNextPrimaryAttack = g_Engine.time + 1.5;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.5;
					break;
					case 3:
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("longshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						//SetThink( ThinkFunction(this.HornetLongBurstThink) );
						//pev.nextthink = g_Engine.time + 0.01;

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.3;
						m_flStopHornetAttack = g_Engine.time + 1.6;

						self.m_flNextPrimaryAttack = g_Engine.time + CD_HORNET;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + CD_HORNET;
					break;
				}
			}
		}
		else
		{
			spawn_driveent();
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle = g_Engine.time + 0.5;
			m_flNextAlertGrowl = g_Engine.time + 0.0;

			return;
		}
	}

	void SecondaryAttack()
	{
		/*if( m_iState == STATE_RUN )
		{
			//self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
			return;
		}*/

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH )
		{
			m_iState = STATE_ATTACK_MELEE;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_iRandomAttack = Math.RandomLong(2, 3);
			PlayMeleeAttackSound();

			switch( m_iRandomAttack )
			{
				case 2:	m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack2"); pev.nextthink = g_Engine.time + 0.5; break;
				case 3:	m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack3"); pev.nextthink = g_Engine.time + 0.4; break;
			}

			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.MeleeAttackThink) );
		}

		self.m_flNextPrimaryAttack = g_Engine.time + 1.0;
		self.m_flNextSecondaryAttack = g_Engine.time + CD_MELEE;
		self.m_flTimeWeaponIdle = g_Engine.time + CD_MELEE;
	}

	void TertiaryAttack()
	{
		Vector vecOrigin, vecOrigin2, vecOrigin3, vecOrigin4, vecOrigin5; 
		//m_pDriveEnt.GetAttachment( 0, vecOrigin, void );

		entvars_t@ pevOwner;
		if( self.pev.owner !is null )
			@pevOwner = @self.pev.owner.vars;
		else
			@pevOwner = self.pev;

		vecOrigin = m_pPlayer.pev.origin + g_Engine.v_right * -24;

		if( m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) != 0 )
		{
			DoIdleAnimation();
			self.m_flNextTertiaryAttack = g_Engine.time + 0.1;
			self.m_flTimeWeaponIdle = 0.1;
			return;
		}

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_ATTACK_MELEE )
		{
			m_iState = STATE_THROW_SNARK;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack3");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.ThrowSnarkThink) );
			pev.nextthink = g_Engine.time + 0.1;
			m_flStopSnarkThrow = g_Engine.time + 0.75;

			//CBaseEntity@ pSqueak = g_EntityFuncs.Create( "monster_hlsnark", vecOrigin, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			//pSqueak.pev.velocity = g_Engine.v_forward * 200 + g_Engine.v_up * 300;
			//CBaseEntity@ pOwner = g_EntityFuncs.Instance( pevOwner );
			//@pSqueak.pev.owner = @pOwner.edict();
			//pSqueak.SetPlayerAlly( !self.IsPlayerAlly() );

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 1.0;
			self.m_flNextSecondaryAttack = g_Engine.time + CD_MELEE;
			self.m_flNextTertiaryAttack = g_Engine.time + 9.5;
			self.m_flTimeWeaponIdle = g_Engine.time + CD_MELEE;
			m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) + 10 );
		}
	}

	void Reload()
	{
	}

	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, 50 );

			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 )
			{
				//DoEnemyDetection();
				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 and m_iState != STATE_THROW_SNARK )
			{
				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.0 and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_ATTACK_MELEE and m_iState != STATE_THROW_SNARK )
			{
				//m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
				DoMovementAnimation();
				self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
			}

			if( m_pPlayer.pev.velocity.Length() <= 0 and m_iState != STATE_ATTACK_MELEE and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_THROW_SNARK )
				DoIdleAnimation();

			if( m_iState == STATE_ATTACK_HORNET )
			{
				Vector angDir = m_pPlayer.pev.v_angle;

				if( angDir.x > 180 )
					angDir.x = angDir.x - 360;

				m_pDriveEnt.SetBlending( 0, -angDir.x );
			}
		}
	}

	void InactiveItemPostFrame()
	{
		m_pPlayer.SwitchWeapon(self);
	}

	void DoEnemyDetection()
	{
		CBaseEntity@ pTarget = null;
		while( (@pTarget = g_EntityFuncs.FindEntityInSphere(pTarget, m_pDriveEnt.pev.origin, 8192, "*", "classname")) !is null )
		{
			int rel = m_pPlayer.IRelationship(pTarget);
			bool isFriendly = rel == R_AL or rel == R_NO;

			if( !pTarget.pev.FlagBitSet(FL_MONSTER) or !pTarget.IsAlive() or isFriendly )
				continue;

			NetworkMessage m2( MSG_ONE, NetworkMessages::SVC_TEMPENTITY, m_pPlayer.edict() );
				m2.WriteByte( TE_ELIGHT );
				m2.WriteShort( pTarget.entindex() );
				m2.WriteCoord( pTarget.Center().x );		// origin
				m2.WriteCoord( pTarget.Center().y );
				m2.WriteCoord( pTarget.Center().z );
				m2.WriteCoord( 1024 );	// radius
				m2.WriteByte( int(ED_COLOR.x) );	// R
				m2.WriteByte( int(ED_COLOR.y) );	// G
				m2.WriteByte( int(ED_COLOR.z) );	// B
				m2.WriteByte( 16 );	// life * 10
				m2.WriteCoord( 2000 ); // decay
			m2.End(); 
		}
	}

	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_pEnemy = pEnemy;
				m_flNextSpeakTime = g_Engine.time + 6.0;
					
				if( m_flNextAlertGrowl < g_Engine.time )
				{
					AlertSound();
					m_flNextAlertGrowl = g_Engine.time + 9999.0;
				}
			}
		}
		else if( !m_pEnemy.IsAlive() or !m_pPlayer.FVisible(m_pEnemy, true) or !m_pPlayer.FInViewCone(m_pEnemy) )
		{
			@m_pEnemy = null;
			m_flNextAlertGrowl = g_Engine.time + 0.0;
		}*/

		if( !m_pPlayer.m_hEnemy.IsValid() )
		{
			m_pPlayer.Look( 4056 );
			m_pPlayer.m_hEnemy = m_pPlayer.BestVisibleEnemy();
		}

		if ( m_pPlayer.m_hEnemy.IsValid() && m_pPlayer.FInViewCone( m_pPlayer.m_hEnemy.GetEntity() ) && m_pPlayer.FVisible( m_pPlayer.m_hEnemy.GetEntity(), true ) )
		{
			m_pPlayer.m_hEnemy = m_pPlayer.m_hEnemy.GetEntity();
			m_flNextSpeakTime = g_Engine.time + 10.0;
					
			if( m_flNextAlertGrowl < g_Engine.time )
			{
				AlertSound();
				m_flNextAlertGrowl = g_Engine.time + 9999.0;
			}
		}
		else
		{
			m_pPlayer.m_hEnemy = null;
			m_flNextAlertGrowl = g_Engine.time + 0.0;
		}
	}

	void AlertSound()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			switch ( Math.RandomLong( 1, 4 ) )
			{
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, 1.0 ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert3.wav", 1.0, 1.0 ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert4.wav", 1.0, 1.0 ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert5.wav", 1.0, 1.0 ); break;
			}
		}
	}

	void AlertGrowl()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			switch ( Math.RandomLong( 1, 4 ) )
			{
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, 1.0 ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert3.wav", 1.0, 1.0 ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert4.wav", 1.0, 1.0 ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert5.wav", 1.0, 1.0 ); break;
			}
		}
	}

	/*void ItemPostFrame()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_bShotBlocked )
		{
			m_iShootStage = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") )
		{
			switch( GetFrame(16) )
			{
			    case 3: { if( m_iShootStage == 0 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 5: { if( m_iShootStage == 1 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 7: { if( m_iShootStage == 2 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 8: { if( m_iShootStage == 3 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 11: { if( m_iShootStage == 4 ) { ShootHornet(); m_iShootStage++; } break; }
			}
		}

		BaseClass.ItemPostFrame();
	}*/

	void HornetBurstThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_bShotBlocked )
		{
			SetThink( null );
			m_iShootStage = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") )
		{
			switch( GetFrame(16) )
			{
			    case 3: { if( m_iShootStage == 0 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 5: { if( m_iShootStage == 1 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 7: { if( m_iShootStage == 2 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 8: { if( m_iShootStage == 3 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 11: { if( m_iShootStage == 4 ) { ShootHornet(); m_iShootStage++; } break; }
			}

			pev.nextthink = g_Engine.time;
		}
	}
	
	void HornetLongBurstThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET )
		{
			SetThink( null );
			m_iShootStage2 = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("longshoot") )
		{
			switch( GetFrame(21) )
			{
			    case 3: { if( m_iShootStage2 == 0 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 5: { if( m_iShootStage2 == 1 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 7: { if( m_iShootStage2 == 2 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 9: { if( m_iShootStage2 == 3 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 11: { if( m_iShootStage2 == 4 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 13: { if( m_iShootStage2 == 5 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 15: { if( m_iShootStage2 == 6 ) { ShootHornet(); m_iShootStage2++; } break; }
			}
			
			pev.nextthink = g_Engine.time;
		}
	}

	void RegenerateHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_fNextRegenTick < g_Engine.time and m_pPlayer.pev.health < 300 )
			{
				m_pPlayer.pev.health += 50;
				m_fNextRegenTick = g_Engine.time + 1.0;
			}
		}
	}

	void CheckMaxHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_pPlayer.pev.max_health > 900 || m_pPlayer.pev.max_health < 900 )
			{
				m_pPlayer.pev.max_health = 900;
			}

			if( m_fNextHealthCheck < g_Engine.time and m_pPlayer.pev.health > 900 )
			{
				m_pPlayer.pev.health = 900;
				m_fNextHealthCheck = 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, "agrunt/ag_pain1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain3.wav", VOL_NORM, ATTN_NORM ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain4.wav", VOL_NORM, ATTN_NORM ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain5.wav", VOL_NORM, ATTN_NORM ); break;
			}

			m_flNextIdleSound = g_Engine.time + 4.0;
		}
	}

	void IdleSound()
	{
		if( m_pDriveEnt is null ) return;
		if( m_flNextSpeakTime > g_Engine.time ) return;

		if( m_flNextWordTime < g_Engine.time )
		{
			int num = -1;

			do
			{
				num = Math.RandomLong( 0,3 );
			} 
			
			while( num == m_iLastWord );

			m_iLastWord = num;

			g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, pIdleSounds[num], VOL_NORM, ATTN_NORM );

			if( Math.RandomLong(1, 10) <= 1 )
				m_flNextWordTime = m_flNextSpeakTime = g_Engine.time + ( 10 + Math.RandomFloat(0.0, 10.0) );
			else
				m_flNextWordTime = g_Engine.time + Math.RandomFloat( 0.5, 1.0 );
		}
	}

	void PlayMeleeAttackSound ()
	{
		if( m_pDriveEnt !is null )
		{
			switch( Math.RandomLong(0, 2) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack3.wav", VOL_NORM, ATTN_NORM ); break;
			}
		}
	}

	void HornetAttackThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_flStopHornetAttack < g_Engine.time or m_bShotBlocked )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + HORNET_REFIRE;
	}

	void HornetAttackThink2()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack2 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink3) );
			pev.nextthink = g_Engine.time + 0.09;
			m_flStopHornetAttack3 = g_Engine.time + 0.095;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + HORNET_REFIRE;
	}

	void HornetAttackThink3()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack3 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink4) );
			pev.nextthink = g_Engine.time + 0.1;
			m_flStopHornetAttack4 = g_Engine.time + 0.1;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.09;
	}

	void HornetAttackThink4()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack4 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink5) );
			pev.nextthink = g_Engine.time + 0.09;
			m_flStopHornetAttack5 = g_Engine.time + 0.095;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.3;
	}

	void HornetAttackThink5()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_flStopHornetAttack5 < g_Engine.time or m_bShotBlocked )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.09;
	}

	void ThrowSnarkThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_THROW_SNARK or m_flStopSnarkThrow < g_Engine.time )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ThrowSnark();
		pev.nextthink = g_Engine.time + 0.15;
	}

	void ShootHornet()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() and !m_bShotBlocked )
		{
			Vector vecAngle, vecOrigin, vecMuzzle;
			vecAngle = m_pPlayer.pev.v_angle;

			if( vecAngle.x < -54.5 ) vecAngle.x = -54.5;
			if( vecAngle.x > 32.0 ) vecAngle.x = 32.0;

			Math.MakeVectors( vecAngle );
			m_pDriveEnt.GetAttachment( 0, vecOrigin, void );

			m_pPlayer.SetMaxSpeedOverride( 0 );

			vecMuzzle = vecOrigin + g_Engine.v_forward * 32;

			m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;

			NetworkMessage m1( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, vecMuzzle );
				m1.WriteByte( TE_SPRITE );
				m1.WriteCoord( vecMuzzle.x );
				m1.WriteCoord( vecMuzzle.y );
				m1.WriteCoord( vecMuzzle.z );
				m1.WriteShort( m_iAgruntMuzzleFlash );
				m1.WriteByte( 6 ); // size * 10
				m1.WriteByte( 128 ); // brightness
			m1.End();

			CBaseEntity@ pHornet = g_EntityFuncs.Create( "hivehornet", vecMuzzle, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			//CBaseEntity@ pHornet = g_EntityFuncs.Create( "playerhornet", vecMuzzle, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			pHornet.pev.velocity = g_Engine.v_forward * 300;
			pHornet.pev.angles = Math.VecToAngles( pHornet.pev.velocity );

			CBaseMonster@ pHornetMonster = pHornet.MyMonsterPointer();

			if ( pHornetMonster !is null )
			{
				pHornetMonster.m_hEnemy = m_pPlayer.m_hEnemy.GetEntity();
			}

			switch( Math.RandomLong(0, 2) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire3.wav", VOL_NORM, ATTN_NORM ); break;
			}

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
		}
	}

	void ThrowSnark()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() )
		{
			g_EngineFuncs.MakeVectors( m_pPlayer.pev.v_angle );
			Vector vecGrenadeOrigin = m_pPlayer.pev.origin + m_pPlayer.pev.view_ofs + g_Engine.v_forward * 64; //= m_pPlayer.GetGunPosition() + g_Engine.v_forward * 1.0;

			CBaseEntity@ pSqueak = g_EntityFuncs.Create( "monster_hlsnark", vecGrenadeOrigin, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			pSqueak.pev.velocity = g_Engine.v_forward * 300 + g_Engine.v_up * 300;
			pSqueak.pev.angles = Math.VecToAngles( pSqueak.pev.velocity );
			cast<CBaseMonster@>(pSqueak).m_flFieldOfView = -1.0;
			cast<CBaseMonster@>(pSqueak).m_flDistLook = 4048.0;
			pSqueak.SetClassification(11);
			pSqueak.SetPlayerAlly(true);

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
		}
	}

	void MeleeAttackThink()
	{
		m_flNextIdleSound = g_Engine.time + 4.0;
		m_fNextRegenTick = g_Engine.time + 6.0;

		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_MELEE )
		{
			SetThink( null );
			return;
		}

		CBaseEntity@ pHurt = CheckTraceHullAttack( MELEE_RANGE, DMG_MELEE, DMG_ENERGYBEAM | DMG_NEVERGIB );
		
		if( pHurt !is null )
		{
			pHurt.pev.punchangle.y = (m_iRandomAttack == 3) ? -25.0 : 25.0;
			pHurt.pev.punchangle.x = 8.0;

			if( (pHurt.pev.flags & (FL_CLIENT)) == 1 )
				pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * ((m_iRandomAttack == 3) ? 250.0 : -250.0);

			g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, pAttackHitSounds[Math.RandomLong(0,(pAttackHitSounds.length() - 1))], VOL_NORM, ATTN_NORM, 0, 100 + Math.RandomLong(-5, 5) );

			Vector vecArmPos, vecArmAng;
			m_pDriveEnt.GetAttachment( 0, vecArmPos, vecArmAng );
			g_WeaponFuncs.SpawnBlood( vecArmPos, pHurt.BloodColor(), 25 );
		}
		else
			g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, pAttackMissSounds[Math.RandomLong(0,(pAttackMissSounds.length() - 1))], VOL_NORM, ATTN_NORM, 0, 100 + Math.RandomLong(-5, 5) );

		SetThink( null );
	}

	void DoMovementAnimation()
	{
		float flMinWalkVelocity = -150.0;
		float flMaxWalkVelocity = 150.0;
		m_bShotBlocked = false;

		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_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("walk") )
			{
				m_iState = STATE_WALK;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("walk");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				//m_pPlayer.SetMaxSpeedOverride( int(SPEED_WALK) );
			}
			else
			{
				if( GetFrame(20) == 9 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				else if( GetFrame(20) == 19 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
			}

			m_fNextRegenTick = g_Engine.time + 6.0;
			
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
			{
				//nada
			}
			else
			{
				self.m_flNextPrimaryAttack = g_Engine.time + 0.25;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
			}
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pPlayer.pev.velocity.Length2D() > 0.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 );
			}
			else
			{
				/*if( GetFrame(26) == 10 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, 80 );
				}*/
				if( GetFrame(26) == 11 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				/*else if( GetFrame(26) == 22 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
				}*/
				else if( GetFrame(26) == 23 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}

				m_fNextRegenTick = g_Engine.time + 6.0;
				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) );
			}
			else
			{
				/*if( GetFrame(26) == 10 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, 80 );
				}*/
				if( GetFrame(26) == 11 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				/*else if( GetFrame(26) == 22 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
				}*/
				else if( GetFrame(26) == 23 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}

				m_fNextRegenTick = g_Engine.time + 6.0;
				self.m_flNextPrimaryAttack = g_Engine.time + 0.25;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
				m_pPlayer.pev.flTimeStepSound = 9999; //prevents normal footsteps from playing, kinda? :hehe:
			}
		}
	}

	void DoIdleAnimation()
	{
		if( m_pDriveEnt is null ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("quickshoot") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("longshoot") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_MELEE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("mattack2") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_MELEE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("mattack3") and !m_pDriveEnt.m_fSequenceFinished ) return;

		if( m_iState != STATE_IDLE )
		{
			m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			m_iState = STATE_IDLE;
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("idle1");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();
			m_iShootStage = 0;
			m_iShootStage2 = 0;
			m_bShotBlocked = false;
		}
	}

	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_agrunt", 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
		m_pPlayer.m_bloodColor = BLOOD_COLOR_YELLOW;
		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 = 900 * ( m_pPlayer.pev.health / m_pPlayer.pev.max_health );
			m_pPlayer.pev.max_health = 900;
		}
		else
		{
			m_pPlayer.pev.health = 900;
			m_pPlayer.pev.max_health = 900;
		}

		NetworkMessage m1( MSG_ONE_UNRELIABLE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cam_idealdist 128;cam_idealpitch 15;cl_movespeedkey 0.175;\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, CNPC::CNPC_AGRUNT );
	}

	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 weapon_agrunt2 : CBaseDriveWeapon
{
	float m_flRechargeTime;
	float m_flNextIdleSound;
	float m_fNextRegenTick;
	float m_fNextHealthCheck = 0.0;
	float m_flNextShortBurst1;
	float m_flNextShortBurst2;
	private bool m_bShotBlocked;
	private int m_iLastWord;
	private int m_iShootStage;
	private int m_iShootStage2;
	//private int m_iState;
	private int m_iAutoDeploy;
	private int m_iRandomAttack;
	private float m_flNextFragCheck = 0.0;
	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 float m_flStopHornetAttack;
	private float m_flStopHornetAttack2;
	private float m_flStopHornetAttack3;
	private float m_flStopHornetAttack4;
	private float m_flStopHornetAttack5;
	private float m_flStopSnarkThrow;
	private float m_flNextSpeakTime;
	private float m_flNextWordTime;
	private float m_flNextAlertGrowl;
	private float flDistToEnemy;
	private float flDistToEnemy2;

	protected EHandle m_hHornet;
	protected CBaseEntity@ m_pTrackHornet
	{
		get const { return m_hHornet.GetEntity(); }
		set { m_hHornet = EHandle(@value); }
	}

	void Spawn()
	{
		Precache();

		self.m_iDefaultAmmo = 9999;
		self.m_iDefaultSecAmmo = 0;

		g_EntityFuncs.SetModel( self, "models/cnpc/agrunt.mdl" );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderamt", 128 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "rendermode", 2 );
		g_EntityFuncs.DispatchKeyValue( self.edict(), "renderfx", 15 );

		self.pev.sequence = self.LookupSequence("idle1");
		self.pev.frame = 0;
		self.ResetSequenceInfo();

		self.pev.set_controller( 0,  127 );
		self.pev.set_controller( 1,  127 );

		m_iState = STATE_IDLE;
		m_iRandomAttack = 0;
		m_iAutoDeploy = 1;
		m_iLastWord = 0;
		m_iShootStage = 0;
		m_iShootStage2 = 0;
		m_flNextShortBurst1 = 0;
		m_flNextShortBurst2 = 0;
		m_flNextSpeakTime = m_flNextWordTime = g_Engine.time + 10 + Math.RandomFloat(0, 10);
		m_flNextIdleSound = g_Engine.time + CNPC_IDLESOUND;

		self.FallInit();
	}

	void Precache()
	{
		BaseClass.Precache();
		self.PrecacheCustomModels();
		g_Game.PrecacheGeneric( "sprites/controlnpc/agrunt/weapon_agrunt.txt" );
		g_Game.PrecacheModel( "sprites/controlnpc/crosshairs.spr" );
		g_Game.PrecacheModel( "models/cnpc/agrunt.mdl" );

		for( uint i = 0; i < pAttackHitSounds.length(); i++ )
			g_SoundSystem.PrecacheSound( pSqknestSounds[i] );
			g_SoundSystem.PrecacheSound( "debris/bustflesh1.wav" );
			g_SoundSystem.PrecacheSound( "debris/bustflesh2.wav" );
			g_SoundSystem.PrecacheSound( "debris/flesh6.wav" );
			g_SoundSystem.PrecacheSound( "debris/flesh7.wav" );
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1		= 9999;
		info.iMaxAmmo2		= 10;
		info.iMaxClip		= WEAPON_NOCLIP;
		info.iSlot		= 1 - 1;
		info.iPosition		= 1 - 1;
		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;

		/*NetworkMessage m1( MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict() );
			m1.WriteLong( g_ItemRegistry.GetIdForName(sWeaponName) );
		m1.End();*/

		m_pPlayer.SwitchWeapon(self);

		return true;
	}

	bool Deploy()
	{
		spawn_driveent();
		self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.1;

		return self.DefaultDeploy( "", "", 0, "" );
	}

	bool CanHolster()
	{
		return false;
	}

	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()
	{
		TraceResult tr;
		Math.MakeVectors( m_pPlayer.pev.v_angle );
		g_Utility.TraceHull( m_pPlayer.pev.origin, m_pPlayer.pev.origin + g_Engine.v_forward * 42, dont_ignore_monsters, head_hull, m_pPlayer.edict(), tr );

		/*if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			m_pDriveEnt.pev.gaitsequence = m_pDriveEnt.LookupSequence("run");
			m_pDriveEnt.ResetGaitSequenceInfo();
			//self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
		}*/
		
		/*if( m_pDriveEnt !is null and tr.flFraction != 1.0 and m_pPlayer.pev.velocity.Length2D() == 0.0 and (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
		{
			self.m_flNextPrimaryAttack = g_Engine.time + 1.2; //self.m_flNextPrimaryAttack = g_Engine.time + 0.1;
			//SecondaryAttack();
			//g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Can't shoot here.\n Something's in the way.\n" );
			//DoIdleAnimation();
			m_bShotBlocked = true;
			//return;
		}
		else if( m_pDriveEnt !is null and tr.flFraction == 1.0 and m_pPlayer.pev.velocity.Length2D() == 0.0 and (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 )
		{
			m_bShotBlocked = false;
		}*/

		if( m_pDriveEnt !is null )
		{
			/*if( m_iState == STATE_RUN )
			{
				m_pDriveEnt.pev.gaitsequence = m_pDriveEnt.LookupSequence("run");
				m_pDriveEnt.ResetGaitSequenceInfo();
			}*/

			if( m_iState != STATE_DEATH ) //!m_bShotBlocked or m_iState >= STATE_RUN )
			{
				switch( Math.RandomLong(1, 3) )
				{
					case 1:
					if( m_flNextShortBurst1 < g_Engine.time )
					{
						m_flNextShortBurst1 = g_Engine.time + 2.0;
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("quickshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.45;
						m_flStopHornetAttack = g_Engine.time + 0.75;

						self.m_flNextPrimaryAttack = g_Engine.time + 0.9;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.0;
					}
					break;
					/*case 2:
					if( m_flNextShortBurst2 < g_Engine.time )
					{
						m_flNextShortBurst2 = g_Engine.time + 3.0;
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("quickshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.45;
						m_flStopHornetAttack = g_Engine.time + 0.75;

						self.m_flNextPrimaryAttack = g_Engine.time + 0.9;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.0;
					}
					break;*/
					case 2:
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("hornetattack");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						//SetThink( ThinkFunction(this.HornetBurstThink) );
						//pev.nextthink = g_Engine.time + 0.01;

						SetThink( ThinkFunction(this.HornetAttackThink2) );
						pev.nextthink = g_Engine.time + 0.3;
						//m_flStopHornetAttack = g_Engine.time + 1.2;
						m_flStopHornetAttack2 = g_Engine.time + 0.4;

						self.m_flNextPrimaryAttack = g_Engine.time + 1.5;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + 1.5;
					break;
					case 3:
						m_iShootStage = 0;
						m_iShootStage2 = 0;
						m_iState = STATE_ATTACK_HORNET;
						m_pPlayer.SetMaxSpeedOverride( 0 );
						m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("longshoot");
						m_pDriveEnt.pev.frame = 0;
						m_pDriveEnt.ResetSequenceInfo();

						//SetThink( ThinkFunction(this.HornetLongBurstThink) );
						//pev.nextthink = g_Engine.time + 0.01;

						SetThink( ThinkFunction(this.HornetAttackThink) );
						pev.nextthink = g_Engine.time + 0.3;
						m_flStopHornetAttack = g_Engine.time + 1.6;

						self.m_flNextPrimaryAttack = g_Engine.time + CD_HORNET;
						self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
						self.m_flTimeWeaponIdle = g_Engine.time + CD_HORNET;
					break;
				}
			}
		}
		else
		{
			spawn_driveent();
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle = g_Engine.time + 0.5;
			m_flNextAlertGrowl = g_Engine.time + 0.0;

			return;
		}
	}

	void SecondaryAttack()
	{
		/*if( m_iState == STATE_RUN )
		{
			//self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
			return;
		}*/

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH )
		{
			m_iState = STATE_ATTACK_MELEE;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_iRandomAttack = Math.RandomLong(2, 3);
			PlayMeleeAttackSound();

			switch( m_iRandomAttack )
			{
				case 2:	m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack2"); pev.nextthink = g_Engine.time + 0.5; break;
				case 3:	m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack3"); pev.nextthink = g_Engine.time + 0.4; break;
			}

			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.MeleeAttackThink) );
		}

		self.m_flNextPrimaryAttack = g_Engine.time + 1.0;
		self.m_flNextSecondaryAttack = g_Engine.time + CD_MELEE;
		self.m_flTimeWeaponIdle = g_Engine.time + CD_MELEE;
	}

	void TertiaryAttack()
	{
		Vector vecOrigin, vecOrigin2, vecOrigin3, vecOrigin4, vecOrigin5; 
		//m_pDriveEnt.GetAttachment( 0, vecOrigin, void );

		entvars_t@ pevOwner;
		if( self.pev.owner !is null )
			@pevOwner = @self.pev.owner.vars;
		else
			@pevOwner = self.pev;

		vecOrigin = m_pPlayer.pev.origin + g_Engine.v_right * -24;

		if( m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) != 0 )
		{
			DoIdleAnimation();
			self.m_flNextTertiaryAttack = g_Engine.time + 0.1;
			self.m_flTimeWeaponIdle = 0.1;
			return;
		}

		if( m_pDriveEnt !is null and m_iState != STATE_DEATH and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_ATTACK_MELEE )
		{
			m_iState = STATE_THROW_SNARK;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("mattack3");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.ThrowSnarkThink) );
			pev.nextthink = g_Engine.time + 0.1;
			m_flStopSnarkThrow = g_Engine.time + 0.75;

			//CBaseEntity@ pSqueak = g_EntityFuncs.Create( "monster_hlsnark", vecOrigin, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			//pSqueak.pev.velocity = g_Engine.v_forward * 200 + g_Engine.v_up * 300;
			//CBaseEntity@ pOwner = g_EntityFuncs.Instance( pevOwner );
			//@pSqueak.pev.owner = @pOwner.edict();
			//pSqueak.SetPlayerAlly( !self.IsPlayerAlly() );

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 1.0;
			self.m_flNextSecondaryAttack = g_Engine.time + CD_MELEE;
			self.m_flNextTertiaryAttack = g_Engine.time + 4.0;
			self.m_flTimeWeaponIdle = g_Engine.time + CD_MELEE;
			m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) + 5 );
		}
	}

	void Reload()
	{
	}

	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, 50 );

			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_flHealthRegenTime < g_Engine.time and m_pDriveEnt.pev.health < m_pDriveEnt.pev.max_health && m_pDriveEnt.pev.deadflag != DEAD_DEAD )
			{
				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_pDriveEnt.pev.health += 20;
				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_flNextHealthCheck < g_Engine.time and m_pDriveEnt.pev.health > m_pDriveEnt.pev.max_health && m_pDriveEnt.pev.deadflag != DEAD_DEAD )
			{
				m_pDriveEnt.pev.health = m_pDriveEnt.pev.max_health;
				m_flNextHealthCheck = g_Engine.time + 0.5;
			}

			if( m_flRemoveGlowTime < g_Engine.time )
			{
				m_pDriveEnt.pev.rendercolor = Vector(0, 0, 0);
				m_flRemoveGlowTime = g_Engine.time + 99999;
			}

			if( m_pDriveEnt.pev.health <= 0 || m_pDriveEnt.pev.deadflag == DEAD_DEAD )
			{
				m_pPlayer.Killed( null, GIB_NOPENALTY );
			}

			if( m_pDriveEnt.pev.health > 0 )
			{
				m_pPlayer.pev.health = m_pDriveEnt.pev.health;
			}

			if( m_flNextFragCheck < g_Engine.time and m_pDriveEnt.pev.frags > 0 )
			{
				m_pDriveEnt.pev.frags -= 1;
				m_pPlayer.pev.frags += 1;

				m_flNextFragCheck = g_Engine.time + 1.0;
			}

			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 and m_iState != STATE_THROW_SNARK )
			{
				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.0 and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_ATTACK_MELEE and m_iState != STATE_THROW_SNARK )
			{
				//m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
				DoMovementAnimation();
				self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
			}

			if( m_pPlayer.pev.velocity.Length() <= 0 and m_iState != STATE_ATTACK_MELEE and m_iState != STATE_ATTACK_HORNET and m_iState != STATE_THROW_SNARK )
				DoIdleAnimation();

			if( m_iState == STATE_ATTACK_HORNET )
			{
				Vector angDir = m_pPlayer.pev.v_angle;

				if( angDir.x > 180 )
					angDir.x = angDir.x - 360;

				m_pDriveEnt.SetBlending( 0, -angDir.x );
			}
		}
	}

	void DoEnemyDetection()
	{
		CBaseEntity@ pTarget = null;
		while( (@pTarget = g_EntityFuncs.FindEntityInSphere(pTarget, m_pDriveEnt.pev.origin, 8192, "*", "classname")) !is null )
		{
			int rel = m_pPlayer.IRelationship(pTarget);
			bool isFriendly = rel == R_AL or rel == R_NO;

			if( !pTarget.pev.FlagBitSet(FL_MONSTER) or !pTarget.IsAlive() or isFriendly )
				continue;

			NetworkMessage m2( MSG_ONE, NetworkMessages::SVC_TEMPENTITY, m_pPlayer.edict() );
				m2.WriteByte( TE_ELIGHT );
				m2.WriteShort( pTarget.entindex() );
				m2.WriteCoord( pTarget.Center().x );		// origin
				m2.WriteCoord( pTarget.Center().y );
				m2.WriteCoord( pTarget.Center().z );
				m2.WriteCoord( 1024 );	// radius
				m2.WriteByte( int(ED_COLOR.x) );	// R
				m2.WriteByte( int(ED_COLOR.y) );	// G
				m2.WriteByte( int(ED_COLOR.z) );	// B
				m2.WriteByte( 16 );	// life * 10
				m2.WriteCoord( 2000 ); // decay
			m2.End(); 
		}
	}

	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_pEnemy = pEnemy;
				m_flNextSpeakTime = g_Engine.time + 6.0;
					
				if( m_flNextAlertGrowl < g_Engine.time )
				{
					AlertSound();
					m_flNextAlertGrowl = g_Engine.time + 9999.0;
				}
			}
		}
		else if( !m_pEnemy.IsAlive() or !m_pPlayer.FVisible(m_pEnemy, true) or !m_pPlayer.FInViewCone(m_pEnemy) )
		{
			@m_pEnemy = null;
			m_flNextAlertGrowl = g_Engine.time + 0.0;
		}*/

		if( !m_pPlayer.m_hEnemy.IsValid() )
		{
			m_pPlayer.Look( 4056 );
			m_pPlayer.m_hEnemy = m_pPlayer.BestVisibleEnemy();
		}

		if ( m_pPlayer.m_hEnemy.IsValid() && m_pPlayer.FInViewCone( m_pPlayer.m_hEnemy.GetEntity() ) && m_pPlayer.FVisible( m_pPlayer.m_hEnemy.GetEntity(), true ) )
		{
			m_pPlayer.m_hEnemy = m_pPlayer.m_hEnemy.GetEntity();
			m_flNextSpeakTime = g_Engine.time + 10.0;
					
			if( m_flNextAlertGrowl < g_Engine.time )
			{
				AlertSound();
				m_flNextAlertGrowl = g_Engine.time + 9999.0;
			}
		}
		else
		{
			m_pPlayer.m_hEnemy = null;
			m_flNextAlertGrowl = g_Engine.time + 0.0;
		}
	}

	void AlertSound()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			switch ( Math.RandomLong( 1, 4 ) )
			{
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, 1.0 ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert3.wav", 1.0, 1.0 ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert4.wav", 1.0, 1.0 ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert5.wav", 1.0, 1.0 ); break;
			}
		}
	}

	void AlertGrowl()
	{
		if( m_pPlayer !is null or m_pDriveEnt !is null )
		{
			switch ( Math.RandomLong( 1, 4 ) )
			{
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, 1.0 ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert3.wav", 1.0, 1.0 ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert4.wav", 1.0, 1.0 ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert5.wav", 1.0, 1.0 ); break;
			}
		}
	}

	/*void ItemPostFrame()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_bShotBlocked )
		{
			m_iShootStage = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") )
		{
			switch( GetFrame(16) )
			{
			    case 3: { if( m_iShootStage == 0 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 5: { if( m_iShootStage == 1 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 7: { if( m_iShootStage == 2 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 8: { if( m_iShootStage == 3 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 11: { if( m_iShootStage == 4 ) { ShootHornet(); m_iShootStage++; } break; }
			}
		}

		BaseClass.ItemPostFrame();
	}*/

	void HornetBurstThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_bShotBlocked )
		{
			SetThink( null );
			m_iShootStage = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") )
		{
			switch( GetFrame(16) )
			{
			    case 3: { if( m_iShootStage == 0 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 5: { if( m_iShootStage == 1 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 7: { if( m_iShootStage == 2 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 8: { if( m_iShootStage == 3 ) { ShootHornet(); m_iShootStage++; } break; }
			    case 11: { if( m_iShootStage == 4 ) { ShootHornet(); m_iShootStage++; } break; }
			}

			pev.nextthink = g_Engine.time;
		}
	}
	
	void HornetLongBurstThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET )
		{
			SetThink( null );
			m_iShootStage2 = 0;
			return;
		}

		if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("longshoot") )
		{
			switch( GetFrame(21) )
			{
			    case 3: { if( m_iShootStage2 == 0 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 5: { if( m_iShootStage2 == 1 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 7: { if( m_iShootStage2 == 2 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 9: { if( m_iShootStage2 == 3 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 11: { if( m_iShootStage2 == 4 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 13: { if( m_iShootStage2 == 5 ) { ShootHornet(); m_iShootStage2++; } break; }
			    case 15: { if( m_iShootStage2 == 6 ) { ShootHornet(); m_iShootStage2++; } break; }
			}
			
			pev.nextthink = g_Engine.time;
		}
	}

	void RegenerateHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_fNextRegenTick < g_Engine.time and m_pPlayer.pev.health < 300 )
			{
				m_pPlayer.pev.health += 50;
				m_fNextRegenTick = g_Engine.time + 1.0;
			}
		}
	}

	void CheckMaxHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_pPlayer.pev.max_health > 900 || m_pPlayer.pev.max_health < 900 )
			{
				m_pPlayer.pev.max_health = 900;
			}

			if( m_fNextHealthCheck < g_Engine.time and m_pPlayer.pev.health > 900 )
			{
				m_pPlayer.pev.health = 900;
				m_fNextHealthCheck = 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, "agrunt/ag_pain1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain3.wav", VOL_NORM, ATTN_NORM ); break;
				case 3: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain4.wav", VOL_NORM, ATTN_NORM ); break;
				case 4: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_pain5.wav", VOL_NORM, ATTN_NORM ); break;
			}

			m_flNextIdleSound = g_Engine.time + 4.0;
		}
	}

	void IdleSound()
	{
		if( m_pDriveEnt is null ) return;
		if( m_flNextSpeakTime > g_Engine.time ) return;

		if( m_flNextWordTime < g_Engine.time )
		{
			int num = -1;

			do
			{
				num = Math.RandomLong( 0,3 );
			} 
			
			while( num == m_iLastWord );

			m_iLastWord = num;

			g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, pIdleSounds[num], VOL_NORM, ATTN_NORM );

			if( Math.RandomLong(1, 10) <= 1 )
				m_flNextWordTime = m_flNextSpeakTime = g_Engine.time + ( 10 + Math.RandomFloat(0.0, 10.0) );
			else
				m_flNextWordTime = g_Engine.time + Math.RandomFloat( 0.5, 1.0 );
		}
	}

	void PlayMeleeAttackSound ()
	{
		if( m_pDriveEnt !is null )
		{
			switch( Math.RandomLong(0, 2) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_attack3.wav", VOL_NORM, ATTN_NORM ); break;
			}
		}
	}

	void HornetAttackThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_flStopHornetAttack < g_Engine.time or m_bShotBlocked )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + HORNET_REFIRE;
	}

	void HornetAttackThink2()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack2 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink3) );
			pev.nextthink = g_Engine.time + 0.09;
			m_flStopHornetAttack3 = g_Engine.time + 0.095;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + HORNET_REFIRE;
	}

	void HornetAttackThink3()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack3 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink4) );
			pev.nextthink = g_Engine.time + 0.1;
			m_flStopHornetAttack4 = g_Engine.time + 0.1;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.09;
	}

	void HornetAttackThink4()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET ) //if( m_pDriveEnt !is null and m_iState == STATE_RUN )
		{
			SetThink( null );
			return;
		}

		if( m_flStopHornetAttack4 < g_Engine.time )
		{
			SetThink( ThinkFunction(this.HornetAttackThink5) );
			pev.nextthink = g_Engine.time + 0.09;
			m_flStopHornetAttack5 = g_Engine.time + 0.095;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.3;
	}

	void HornetAttackThink5()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_HORNET or m_flStopHornetAttack5 < g_Engine.time or m_bShotBlocked )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ShootHornet();
		pev.nextthink = g_Engine.time + 0.09;
	}

	void ThrowSnarkThink()
	{
		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_THROW_SNARK or m_flStopSnarkThrow < g_Engine.time )
		{
			SetThink( null );
			return;
		}

		m_pPlayer.SetMaxSpeedOverride( 0 );

		ThrowSnark();
		pev.nextthink = g_Engine.time + 0.15;
	}

	void ShootHornet()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() and !m_bShotBlocked )
		{
			Vector vecAngle, vecOrigin, vecMuzzle;
			vecAngle = m_pPlayer.pev.v_angle;

			if( vecAngle.x < -54.5 ) vecAngle.x = -54.5;
			if( vecAngle.x > 32.0 ) vecAngle.x = 32.0;

			Math.MakeVectors( vecAngle );
			m_pDriveEnt.GetAttachment( 0, vecOrigin, void );

			m_pPlayer.SetMaxSpeedOverride( 0 );

			vecMuzzle = vecOrigin + g_Engine.v_forward * 32;

			m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;

			NetworkMessage m1( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, vecMuzzle );
				m1.WriteByte( TE_SPRITE );
				m1.WriteCoord( vecMuzzle.x );
				m1.WriteCoord( vecMuzzle.y );
				m1.WriteCoord( vecMuzzle.z );
				m1.WriteShort( m_iAgruntMuzzleFlash );
				m1.WriteByte( 6 ); // size * 10
				m1.WriteByte( 128 ); // brightness
			m1.End();

			CBaseEntity@ pHornet = g_EntityFuncs.Create( "hivehornet", vecMuzzle, m_pPlayer.pev.v_angle, false, m_pDriveEnt.edict() );
			//CBaseEntity@ pHornet = g_EntityFuncs.Create( "playerhornet", vecMuzzle, m_pPlayer.pev.v_angle, false, m_pPlayer.edict() );
			pHornet.pev.velocity = g_Engine.v_forward * 300;
			pHornet.pev.angles = Math.VecToAngles( pHornet.pev.velocity );

			CBaseMonster@ pHornetMonster = pHornet.MyMonsterPointer();

			if ( pHornetMonster !is null )
			{
				pHornetMonster.m_hEnemy = m_pPlayer.m_hEnemy.GetEntity();
			}

			switch( Math.RandomLong(0, 2) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_WEAPON, "agrunt/ag_fire3.wav", VOL_NORM, ATTN_NORM ); break;
			}

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
		}
	}

	void ThrowSnark()
	{
		if( m_pDriveEnt !is null and m_pPlayer.IsAlive() )
		{
			g_EngineFuncs.MakeVectors( m_pPlayer.pev.v_angle );
			Vector vecGrenadeOrigin = m_pPlayer.pev.origin + m_pPlayer.pev.view_ofs + g_Engine.v_forward * 64; //= m_pPlayer.GetGunPosition() + g_Engine.v_forward * 1.0;

			CBaseEntity@ pSqueak = g_EntityFuncs.Create( "monster_hlsnark", vecGrenadeOrigin, m_pPlayer.pev.v_angle, false, m_pDriveEnt.edict() );
			pSqueak.pev.velocity = g_Engine.v_forward * 300 + g_Engine.v_up * 300;
			pSqueak.pev.angles = Math.VecToAngles( pSqueak.pev.velocity );
			cast<CBaseMonster@>(pSqueak).m_flFieldOfView = -1.0;
			cast<CBaseMonster@>(pSqueak).m_flDistLook = 4048.0;
			pSqueak.SetClassification(11);
			pSqueak.SetPlayerAlly(true);

			m_flNextIdleSound = g_Engine.time + 4.0;
			m_fNextRegenTick = g_Engine.time + 6.0;
		}
	}

	void MeleeAttackThink()
	{
		m_flNextIdleSound = g_Engine.time + 4.0;
		m_fNextRegenTick = g_Engine.time + 6.0;

		if( m_pPlayer is null or m_pDriveEnt is null or m_iState != STATE_ATTACK_MELEE )
		{
			SetThink( null );
			return;
		}

		CBaseEntity@ pHurt = CheckTraceHullAttack( MELEE_RANGE, DMG_MELEE, DMG_ENERGYBEAM | DMG_NEVERGIB );
		
		if( pHurt !is null )
		{
			pHurt.pev.punchangle.y = (m_iRandomAttack == 3) ? -25.0 : 25.0;
			pHurt.pev.punchangle.x = 8.0;

			if( (pHurt.pev.flags & (FL_CLIENT)) == 1 )
				pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * ((m_iRandomAttack == 3) ? 250.0 : -250.0);

			g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, pAttackHitSounds[Math.RandomLong(0,(pAttackHitSounds.length() - 1))], VOL_NORM, ATTN_NORM, 0, 100 + Math.RandomLong(-5, 5) );

			Vector vecArmPos, vecArmAng;
			m_pDriveEnt.GetAttachment( 0, vecArmPos, vecArmAng );
			g_WeaponFuncs.SpawnBlood( vecArmPos, pHurt.BloodColor(), 25 );
		}
		else
			g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, pAttackMissSounds[Math.RandomLong(0,(pAttackMissSounds.length() - 1))], VOL_NORM, ATTN_NORM, 0, 100 + Math.RandomLong(-5, 5) );

		SetThink( null );
	}

	void DoMovementAnimation()
	{
		float flMinWalkVelocity = -150.0;
		float flMaxWalkVelocity = 150.0;
		m_bShotBlocked = false;

		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_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("walk") )
			{
				m_iState = STATE_WALK;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("walk");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				//m_pPlayer.SetMaxSpeedOverride( int(SPEED_WALK) );
			}
			else
			{
				if( GetFrame(20) == 9 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				else if( GetFrame(20) == 19 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
			}

			m_fNextRegenTick = g_Engine.time + 6.0;
			
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
			{
				//nada
			}
			else
			{
				self.m_flNextPrimaryAttack = g_Engine.time + 0.25;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
			}
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pPlayer.pev.velocity.Length2D() > 0.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 );
			}
			else
			{
				/*if( GetFrame(26) == 10 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, 80 );
				}*/
				if( GetFrame(26) == 11 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				/*else if( GetFrame(26) == 22 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
				}*/
				else if( GetFrame(26) == 23 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}

				m_fNextRegenTick = g_Engine.time + 6.0;
				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) );
			}
			else
			{
				/*if( GetFrame(26) == 10 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, 80 );
				}*/
				if( GetFrame(26) == 11 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder2.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder4.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}
				/*else if( GetFrame(26) == 22 )
				{
					g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_VOICE, "agrunt/ag_alert1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
				}*/
				else if( GetFrame(26) == 23 and m_pPlayer.pev.flags & FL_ONGROUND != 0 )
				{
					switch( Math.RandomLong(0, 1) )
					{
						case 0:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder1.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
						case 1:	g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "player/pl_ladder3.wav", VOL_NORM, ATTN_NORM, 0, 70 ); break;
					}
				}

				m_fNextRegenTick = g_Engine.time + 6.0;
				self.m_flNextPrimaryAttack = g_Engine.time + 0.25;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.15;
				m_pPlayer.pev.flTimeStepSound = 9999; //prevents normal footsteps from playing, kinda? :hehe:
			}
		}
	}

	void DoIdleAnimation()
	{
		if( m_pDriveEnt is null ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("quickshoot") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("hornetattack") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_HORNET and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("longshoot") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_MELEE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("mattack2") and !m_pDriveEnt.m_fSequenceFinished ) return;
		//if( m_iState == STATE_ATTACK_MELEE and m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("mattack3") and !m_pDriveEnt.m_fSequenceFinished ) return;

		if( m_iState != STATE_IDLE )
		{
			m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN) );
			m_iState = STATE_IDLE;
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("idle1");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();
			m_iShootStage = 0;
			m_iShootStage2 = 0;
			m_bShotBlocked = false;
		}
	}

	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_agrunt2", 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.solid = SOLID_NOT;
		m_pPlayer.pev.flags |= FL_NOTARGET;
		m_pPlayer.pev.takedamage = DAMAGE_NO;
		m_pPlayer.pev.iuser3 = 1;
		m_pPlayer.pev.fuser4 = 1; //disable jump
		m_pPlayer.m_bloodColor = BLOOD_COLOR_YELLOW;
		self.m_bExclusiveHold = true;
		g_EntityFuncs.DispatchKeyValue( m_pDriveEnt.edict(), "max_health", "900" );
		m_pPlayer.pev.armorvalue = 0.0;
		m_pPlayer.pev.armortype = 0.0;

		NetworkMessage m1( MSG_ONE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cam_idealdist 128;cam_idealpitch 15;cl_movespeedkey 0.175\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, CNPC::CNPC_AGRUNT );
	}

	void ResetPlayer()
	{
		m_pPlayer.pev.iuser3 = 0;
		m_pPlayer.pev.fuser4 = 0; //enable jump
		m_pPlayer.SetMaxSpeedOverride( -1 );

		NetworkMessage m1( MSG_ONE, 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_agrunt2 : ScriptBaseMonsterEntity //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;
	private float m_flHealthRegenTime = 0.0;
	private float m_flNextHealthCheck = 0.0;
	private float m_flRemoveGlowTime = 0.0;

	void Spawn()
	{
		g_EntityFuncs.SetModel( self, "models/cnpc/agrunt.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() );

		self.pev.solid = SOLID_BBOX;
		self.pev.movetype = MOVETYPE_BOUNCEMISSILE;
		self.pev.flags |= FL_MONSTER;
		self.pev.takedamage = DAMAGE_AIM;
		self.pev.armorvalue = 50.0;

		if( (m_pOwner.pev.flags & FL_FAKECLIENT) == 0 )
		{
			if( m_pOwner.pev.health < 100 )
			{
				self.pev.health = 900 * ( m_pOwner.pev.health / m_pOwner.pev.max_health );
				self.pev.max_health = 900.0f;
				m_pOwner.pev.max_health = 900.0f;
			}
			else
			{
				self.pev.health = 900;
				self.pev.max_health = 900.0f;
				m_pOwner.pev.max_health = 900.0f;
			}
		}
		else
		{
			self.pev.health = 900;
			self.pev.max_health = 900.0f;
		}

		self.pev.deadflag = DEAD_NO;
		self.m_bloodColor = BLOOD_COLOR_GREEN;
		self.m_FormattedName = "Alien Grunt";
		self.m_afCapability = 0;

		self.pev.sequence = self.LookupSequence("idle1");
		self.pev.frame = 0;
		self.ResetSequenceInfo();

		m_flNextOriginUpdate = g_Engine.time;

		self.MonsterInit();

		SetTouch( TouchFunction( TrackTouch ) );
		SetThink( ThinkFunction(this.DriveThink) );
		pev.nextthink = g_Engine.time;
	}

	int Classify( void )
	{
		if( m_pOwner !is null and m_pOwner.Classify() == CLASS_PLAYER_ALLY )
		{
			return CLASS_PLAYER_ALLY;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_HUMAN_PASSIVE )
		{
			return CLASS_PLAYER_ALLY;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_TEAM1 )
		{
			return CLASS_TEAM1;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_TEAM2 )
		{
			return CLASS_TEAM2;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_TEAM3 )
		{
			return CLASS_TEAM3;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_TEAM4 )
		{
			return CLASS_TEAM4;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_ALIEN_MILITARY )
		{
			return CLASS_ALIEN_MILITARY;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_HUMAN_MILITARY )
		{
			return CLASS_HUMAN_MILITARY;
		}
		else if( m_pOwner !is null and m_pOwner.Classify() == CLASS_XRACE_SHOCK )
		{
			return CLASS_XRACE_SHOCK;
		}
		else
		{
			return CLASS_PLAYER_ALLY;
		}
	}

	int TakeDamage( entvars_t@ pevInflictor, entvars_t@ pevAttacker, float flDamage, int bitsDamageType ) 
	{
		if( pevAttacker is null )
			return 0;

		CBaseEntity@ pAttacker = g_EntityFuncs.Instance( pevAttacker );

		if( self.CheckAttacker( pAttacker ) )
			return 0;


		if( m_pOwner !is null and m_pOwner.IsConnected() and pev.health > 0 )
		{
			m_pOwner.pev.health = pev.health;
		}

		if( self.IsAlive() )
		{
			cnpc_agrunt::weapon_agrunt@ pWeapon = cast<cnpc_agrunt::weapon_agrunt@>( CastToScriptClass(m_pOwner.m_hActiveItem.GetEntity()) );

			if( m_flNextPainSound < g_Engine.time )
			{
				pWeapon.PainSound();

				m_flNextPainSound = g_Engine.time + 1.0;
			}

			pevAttacker.frags += self.GetPointsForDamage( flDamage );
		}

		if ( self.pev.health <= 0 ) 
		{
			self.pev.velocity = g_vecZero;
			self.Killed(null, GIB_NORMAL);
			return 0;
		}

		return BaseClass.TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
	}

	float GetPointsForDamage( float flDamage )
	{
		return ( flDamage/self.pev.max_health ) * ( 10 );
	}

	void TraceAttack( entvars_t@ pevAttacker, float flDamage, const Vector& in vecDir, TraceResult& in ptr, int bitsDamageType )
	{
		if( !self.IsAlive() )
		{
			BaseClass.TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
			return;
		}

		if ( ptr.iHitgroup == HITGROUP_HEAD )
		{
			ptr.iHitgroup = HITGROUP_CHEST;
		}
		
		if ( ptr.iHitgroup == 10 && bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB) != 0 )
		{
			// hit armor
			if ( pev.dmgtime != g_Engine.time || (Math.RandomLong(0,10) < 1) )
			{
				g_Utility.Ricochet( ptr.vecEndPos, Math.RandomFloat( 1, 2) );
				pev.dmgtime = g_Engine.time;
			}

			if ( Math.RandomLong( 0, 1 ) == 0 )
			{
				Vector vecTracerDir = vecDir;

				vecTracerDir.x += Math.RandomFloat( -0.3, 0.3 );
				vecTracerDir.y += Math.RandomFloat( -0.3, 0.3 );
				vecTracerDir.z += Math.RandomFloat( -0.3, 0.3 );

				vecTracerDir = vecTracerDir * -512;
				
				NetworkMessage message( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, ptr.vecEndPos );
					message.WriteByte(TE_TRACER);
					message.WriteCoord(ptr.vecEndPos.x);
					message.WriteCoord(ptr.vecEndPos.y);
					message.WriteCoord(ptr.vecEndPos.z);
					message.WriteCoord(vecTracerDir.x);
					message.WriteCoord(vecTracerDir.y);
					message.WriteCoord(vecTracerDir.z);
				message.End();
			}

			flDamage -= 20;
			if (flDamage <= 0)
				flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
		}
		if ( ptr.iHitgroup == 11 && bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB) != 0 )
		{
			// hit armor
			if ( pev.dmgtime != g_Engine.time || (Math.RandomLong(0,10) < 1) )
			{
				g_Utility.Ricochet( ptr.vecEndPos, Math.RandomFloat( 1, 2) );
				pev.dmgtime = g_Engine.time;
			}

			if ( Math.RandomLong( 0, 1 ) == 0 )
			{
				Vector vecTracerDir = vecDir;

				vecTracerDir.x += Math.RandomFloat( -0.3, 0.3 );
				vecTracerDir.y += Math.RandomFloat( -0.3, 0.3 );
				vecTracerDir.z += Math.RandomFloat( -0.3, 0.3 );

				vecTracerDir = vecTracerDir * -512;
				
				NetworkMessage message( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, ptr.vecEndPos );
					message.WriteByte(TE_TRACER);
					message.WriteCoord(ptr.vecEndPos.x);
					message.WriteCoord(ptr.vecEndPos.y);
					message.WriteCoord(ptr.vecEndPos.z);
					message.WriteCoord(vecTracerDir.x);
					message.WriteCoord(vecTracerDir.y);
					message.WriteCoord(vecTracerDir.z);
				message.End();
			}

			flDamage -= 20;
			if (flDamage <= 0)
				flDamage = 0.5;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
		}

		BaseClass.TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
	}

	void Killed( entvars_t@ pevAttacker, int iGib )
	{
		DeathSound();

		array<string> arrsDeathAnims = 
		{
			"dieforward",
			"diebackward",
			"diegut",
			"diesimple"
		};

		pev.sequence = self.LookupSequence( arrsDeathAnims[Math.RandomLong(0, arrsDeathAnims.length()-1)] );
		pev.frame = 0;
		self.ResetSequenceInfo();

		self.pev.velocity = g_vecZero;
		self.pev.health = 0;
		self.pev.takedamage = DAMAGE_NO;
		self.pev.deadflag = DEAD_DEAD;
		self.pev.solid = SOLID_NOT;
		self.pev.movetype = MOVETYPE_NONE;
		g_EntityFuncs.DispatchKeyValue(self.edict(), "rendercolor", "0 0 0");
		SetThink( ThinkFunction(this.SUB_StartFadeOut) );
		pev.nextthink = g_Engine.time + 1.0;
	}

	void DeathSound()
	{
		switch ( Math.RandomLong( 0, 2 ) )
		{
		case 0:	
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "agrunt/ag_die1.wav", 1.0, 1.0, 0, PITCH_NORM );
			break;
		case 1:
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "agrunt/ag_die4.wav", 1.0, 1.0, 0, PITCH_NORM );
			break;
		case 2:
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "agrunt/ag_die5.wav", 1.0, 1.0, 0, PITCH_NORM );
			break;
		}
	}

	void TrackTouch( CBaseEntity@ pOther )
	{
		if ( pOther.IsPlayer() && pOther.edict() !is pev.owner )
		{
			pev.movetype = MOVETYPE_STEP;
			return;
		}
	}

	void DieThink()
	{
		array<string> arrsDeathAnims = 
		{
			"dieforward",
			"diebackward",
			"diegut",
			"diesimple"
		};

		pev.sequence = self.LookupSequence( arrsDeathAnims[Math.RandomLong(0, arrsDeathAnims.length()-1)] );
		pev.frame = 0;
		pev.solid = SOLID_NOT;
		self.ResetSequenceInfo();

		g_EngineFuncs.DropToFloor( self.edict() );
		self.pev.rendercolor = Vector(0, 0, 0);
		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 DriveThink()
	{
		if( m_pOwner is null or !m_pOwner.IsConnected() or m_pOwner.pev.deadflag != DEAD_NO )
		{
			pev.velocity = g_vecZero;
			self.Killed(null, GIB_NORMAL);
			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 == ANIM_RUN or pev.sequence == ANIM_WALK) )
			pev.angles.y = Math.VecToAngles( pev.velocity ).y;
		else
			pev.angles.y = m_pOwner.pev.angles.y;

		pev.angles.z = 0;

		self.StudioFrameAdvance();

		if( pev.movetype != MOVETYPE_BOUNCEMISSILE )
		{
			pev.movetype = MOVETYPE_BOUNCEMISSILE;
		}

		pev.nextthink = g_Engine.time + 0.01;
	}

	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);
	}
}

class cnpc_agrunt : 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;
	private float m_flHealthRegenTime = 0.0;
	private float m_flNextHealthCheck = 0.0;
	private float m_flRemoveGlowTime = 0.0;

	void Spawn()
	{
		g_EntityFuncs.SetModel( self, "models/npcspawner/npc_agrunt.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 DieThink()
	{
		array<string> arrsDeathAnims = 
		{
			"dieforward",
			"diebackward",
			"diegut",
			"diesimple"
		};

		pev.sequence = self.LookupSequence( arrsDeathAnims[Math.RandomLong(0, arrsDeathAnims.length()-1)] );
		pev.frame = 0;
		pev.solid = SOLID_NOT;
		self.ResetSequenceInfo();

		g_EngineFuncs.DropToFloor( self.edict() );
		self.pev.rendercolor = Vector(0, 0, 0);
		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 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 == ANIM_RUN or pev.sequence == ANIM_WALK) )
			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 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_agrunt::cnpc_agrunt", "cnpc_agrunt" );
	g_Game.PrecacheOther( "cnpc_agrunt" );

	g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_agrunt::weapon_agrunt", sWeaponName );
	g_ItemRegistry.RegisterWeapon( sWeaponName, "controlnpc/agrunt", "agrunt", "snarks" );
	g_Game.PrecacheOther( sWeaponName );

	//g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_agrunt::cnpc_agrunt2", "cnpc_agrunt2" );
	//g_Game.PrecacheOther( "cnpc_agrunt2" );

	//g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_agrunt::weapon_agrunt2", "weapon_agrunt2" );
	//g_ItemRegistry.RegisterWeapon( "weapon_agrunt2", "controlnpc/agrunt", "agrunt", "snarks" );
	//g_Game.PrecacheOther( "weapon_agrunt2" );
}

} //namespace cnpc_agrunt END

/* FIXME
Holding any button after attacking will cause the animation to freeze at the last frame until released
*/

/* TODO
Use turning animations 
*/
