namespace cnpc_securityguard
{
	
const uint g_MaxAmmoPrimary		= 17;
const string sWeaponName		= "weapon_securityguard";
const float SPEED_RUN_OTIS		= (155.426682 * CNPC::flModelToGameSpeedModifier) * 0.8;

const array<string> pPainSounds = 
{
	"barney/ba_pain1.wav",
	"barney/ba_pain2.wav",
	"barney/ba_pain3.wav"
};

const array<string> pDieSounds = 
{
	"barney/ba_die1.wav",
	"barney/ba_die2.wav",
	"barney/ba_die3.wav"
};

enum m_iWeaponModeGuard
{
	MODE_NONE,
	MODE_BARNEY,
	MODE_OTIS
};

enum states_e
{
	STATE_IDLE = 0,
	STATE_WALK,
	STATE_RUN,
	STATE_DEATH,
	STATE_ATTACK_HANDGUN,
	STATE_RELOAD
};

class weapon_securityguard : CBaseDriveWeapon
{
	float m_fNextRegenTick;
	float m_fNextHealthCheck;
	int m_iMaxAmmoGuard;
	int m_iWeaponModeGuard;
	//private int m_iState;
	private int m_iAutoDeploy;
	private float m_flHealthRegenTime = 0.0;
	private float m_flRemoveGlowTime = 0.0;
	private float m_flNextHealthCheck = 0.0;

	void Spawn()
	{
		Precache();

		g_EntityFuncs.SetModel( self, "models/cnpc/otis.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 );
		pev.targetname = "nosave";

		m_iWeaponModeGuard = MODE_BARNEY;
		if( m_iWeaponModeGuard == MODE_BARNEY )
			m_iMaxAmmoGuard = 17;
		else
			m_iMaxAmmoGuard = 7;

		self.m_iDefaultAmmo = m_iMaxAmmoGuard;

		m_iState = STATE_IDLE;

		self.FallInit();
	}

	void Precache()
	{
		BaseClass.Precache();
		self.PrecacheCustomModels();
		g_Game.PrecacheGeneric( "sprites/controlnpc/weapon_securityguard.txt" );
		g_Game.PrecacheModel( "models/cnpc/otis.mdl" );

		for( uint i = 0; i < pPainSounds.length(); i++ )
			g_SoundSystem.PrecacheSound( pPainSounds[i] );

		for( uint i = 0; i < pDieSounds.length(); i++ )
			g_SoundSystem.PrecacheSound( pDieSounds[i] );
			g_SoundSystem.PrecacheSound( "common/npc_step2.wav" );
			g_SoundSystem.PrecacheSound( "common/npc_step4.wav" );
			g_SoundSystem.PrecacheSound( "weapons/desert_eagle_fire.wav" );
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1		= m_iMaxAmmoGuard;
		info.iMaxClip		= WEAPON_NOCLIP;
		info.iSlot		= 0;
		info.iPosition		= 0;
		info.iFlags 		= ITEM_FLAG_SELECTONEMPTY; //to prevent monster from being despawned if out of ammo TODO
		info.iWeight		= 0; //-1 ??

		return true;
	}

	bool AddToPlayer( CBasePlayer@ pPlayer )
	{
		if( !BaseClass.AddToPlayer(pPlayer) )
			return false;

		@m_pPlayer = pPlayer;

		m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, m_iMaxAmmoGuard);
		m_pPlayer.SwitchWeapon(self);

		return true;
	}

	bool Deploy()
	{
		if( m_iWeaponModeGuard == MODE_BARNEY )
		{
			g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Barney\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
		}

		return self.DefaultDeploy( "", "", 0, "" );
	}

	bool CanHolster()
	{
		return false;
	}

	void Holster( int skipLocal = 0 )
	{
		if( m_pDriveEnt !is null )
			@m_pDriveEnt.pev.owner = null;

		ResetPlayer();

		BaseClass.Holster( skipLocal );
	}

	void PrimaryAttack()
	{
		if( m_iState == STATE_RUN )
		{
			self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
		}

		if( m_pDriveEnt !is null )
		{
			if( m_iState != STATE_DEATH and m_iWeaponModeGuard == MODE_OTIS )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				m_iState = STATE_ATTACK_HANDGUN;

				m_pPlayer.SetMaxSpeedOverride( 0 );
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("shootgun");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				
				ShootEagle();

				self.m_flNextPrimaryAttack = g_Engine.time + 0.4;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.4;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.4;
			}
			else if( m_iState != STATE_DEATH and m_iWeaponModeGuard == MODE_BARNEY )
			{
				if( m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
				{
					DoIdleAnimation();
					self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
					return;
				}

				m_iState = STATE_ATTACK_HANDGUN;

				m_pPlayer.SetMaxSpeedOverride( 0 );
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("shootgun");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
				
				Shoot();

				self.m_flNextPrimaryAttack = g_Engine.time + 0.4;
				self.m_flNextSecondaryAttack = g_Engine.time + 0.4;
				self.m_flTimeWeaponIdle = g_Engine.time + 0.4;
			}
		}
		else
		{
			spawn_driveent();
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle = g_Engine.time + 0.5;

			return;
		}
	}

	void SecondaryAttack()
	{
		if( m_pDriveEnt is null )
		{
			switch( m_iWeaponModeGuard )
			{
				case MODE_BARNEY:
				{
					m_iWeaponModeGuard = MODE_OTIS;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Otis\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					break;
				}

				case MODE_OTIS:
				{
					m_iWeaponModeGuard = MODE_BARNEY;
					m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 17);
					g_EngineFuncs.ClientPrintf( m_pPlayer, print_center, "Mode: Barney\n Press PRIMARY FIRE to select\n Press SECONDARY FIRE to change\n" );
					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "buttons/button3.wav", 0.4, ATTN_NONE, SND_SKIP_ORIGIN_USE_ENT, PITCH_NORM, m_pPlayer.entindex() );
					break;
				}
			}

			self.m_flNextSecondaryAttack = g_Engine.time + 0.25;
		}

	}

	void TertiaryAttack()
	{
	}

	void Reload()
	{
	}

	void Shoot()
	{
		Vector vecShootOrigin, vecShootDir;

		vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
		vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES ); //g_Engine.v_forward;
		Math.MakeVectors( m_pPlayer.pev.v_angle );
		Math.MakeAimVectors( m_pPlayer.pev.v_angle );
		Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
		g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 24 + g_Engine.v_right * 8, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
		m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024.0, BULLET_PLAYER_CUSTOMDAMAGE, 2, g_EngineFuncs.CVarGetFloat('sk_plr_9mm_bullet'), m_pPlayer.pev );

		m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;

		m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

		//Vector angDir = Math.VecToAngles( vecShootDir );
		Vector angDir = m_pPlayer.pev.v_angle;

		if( angDir.x > 360 )
			angDir.x = angDir.x - 360;

		m_pDriveEnt.SetBlending( 0, -angDir.x );

		int iPitchShift = Math.RandomLong( 0, 20 );

		// Only shift about half the time
		if( iPitchShift > 10 )
			iPitchShift = 0;
		else
			iPitchShift -= 5;

		g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/pl_gun3.wav", VOL_NORM, ATTN_NORM, 0, 100 + iPitchShift );
		GetSoundEntInstance().InsertSound ( bits_SOUND_COMBAT, m_pDriveEnt.pev.origin, 384, 0.3, m_pPlayer ); 
	}

	void ShootEagle()
	{
		Vector vecShootOrigin, vecShootDir;

		vecShootOrigin = m_pDriveEnt.pev.origin + Vector(0, 0, 60);
		vecShootDir = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES ); //g_Engine.v_forward;
		Math.MakeVectors( m_pPlayer.pev.v_angle );
		Math.MakeAimVectors( m_pPlayer.pev.v_angle );
		Vector vecShellVelocity = g_Engine.v_right * Math.RandomFloat(40, 90) + g_Engine.v_up * Math.RandomFloat(75, 200) + g_Engine.v_forward * Math.RandomFloat(-40, 40);
		g_EntityFuncs.EjectBrass( vecShootOrigin + vecShootDir * 24 + g_Engine.v_right * 8, vecShellVelocity, m_pDriveEnt.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL );
		m_pDriveEnt.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024.0, BULLET_PLAYER_CUSTOMDAMAGE, 2, g_EngineFuncs.CVarGetFloat('sk_plr_357_bullet') * 0.66, m_pPlayer.pev );

		m_pDriveEnt.pev.effects |= EF_MUZZLEFLASH;

		m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1 );

		//Vector angDir = Math.VecToAngles( vecShootDir );
		Vector angDir = m_pPlayer.pev.v_angle;

		if( angDir.x > 360 )
			angDir.x = angDir.x - 360;

		m_pDriveEnt.SetBlending( 0, -angDir.x );

		int iPitchShift = Math.RandomLong( 0, 20 );

		// Only shift about half the time
		if( iPitchShift > 10 )
			iPitchShift = 0;
		else
			iPitchShift -= 5;

		g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", VOL_NORM, ATTN_NORM, 0, 100 + iPitchShift );
		GetSoundEntInstance().InsertSound ( bits_SOUND_COMBAT, m_pDriveEnt.pev.origin, 384, 0.3, m_pPlayer ); 
	}

	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.SetViewMode( ViewMode_ThirdPerson );

			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_pPlayer.pev.button & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT) != 0 )
			{
				if( m_iState != STATE_RELOAD )
				{
					m_pPlayer.SetMaxSpeedOverride( -1 );
					DoMovementAnimation();
					self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
				}
			}
			else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pPlayer.pev.velocity.Length2D() > 0 )
			{
				if( m_iState != STATE_ATTACK_HANDGUN and m_iState != STATE_RELOAD )
				{
					m_pPlayer.SetMaxSpeedOverride( -1 );
					DoMovementAnimation();
					self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
				}
			}

			if( m_pPlayer.pev.velocity.Length() <= 0 and m_iState != STATE_ATTACK_HANDGUN and m_iState != STATE_RELOAD )
			{
				DoIdleAnimation();
			}
			
			DoReload();
		}
	}

	void InactiveItemPostFrame()
	{
		m_pPlayer.SwitchWeapon(self);
	}

	void PainSound()
	{
		if( m_pDriveEnt !is null )
		{
			switch( Math.RandomLong(0, 4) )
			{
				case 0: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "barney/ba_pain1.wav", VOL_NORM, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "barney/ba_pain2.wav", VOL_NORM, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( m_pDriveEnt.edict(), CHAN_VOICE, "barney/ba_pain3.wav", VOL_NORM, ATTN_NORM ); break;
			}
		}
	}

	void RegenerateHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if ( m_fNextRegenTick < g_Engine.time and m_pPlayer.pev.health < 100 )
			{
				m_pPlayer.pev.health += 5;
				m_fNextRegenTick = g_Engine.time + 1.0;
			}
		}
	}

	void CheckMaxHealth ()
	{
		if( m_pDriveEnt !is null )
		{
			if( m_flNextHealthCheck < g_Engine.time and m_pPlayer.pev.health > 400 )
			{
				m_pPlayer.pev.health = 400;

				m_flNextHealthCheck = g_Engine.time + 0.1;

				if( m_pPlayer.pev.max_health > 400 || m_pPlayer.pev.max_health < 400)
				{
					m_pPlayer.pev.max_health = 400;
				}
			}
		}
	}

	void DoReload()
	{
		if( m_iWeaponModeGuard == MODE_BARNEY && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 17 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( m_iWeaponModeGuard == MODE_OTIS && m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) >= 7 or !m_pPlayer.pev.FlagBitSet(FL_ONGROUND) ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload") ) return;

		if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 and m_pDriveEnt !is null and m_pPlayer.IsAlive() and m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 )
		{
			m_iState == STATE_RELOAD;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.ReloadThink) );
			pev.nextthink = g_Engine.time + 2.0;

			m_fNextRegenTick = g_Engine.time + 5.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 2.0;
			self.m_flNextSecondaryAttack = g_Engine.time + 2.0;
			self.m_flTimeWeaponIdle = g_Engine.time + 2.0;
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) == 0 and (m_pPlayer.pev.button & IN_RELOAD) != 0 and m_pDriveEnt !is null )
		{
			m_iState == STATE_RELOAD;
			m_pPlayer.SetMaxSpeedOverride( 0 );

			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("reload");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();

			SetThink( ThinkFunction(this.ReloadThink) );
			pev.nextthink = g_Engine.time + 2.0;

			m_fNextRegenTick = g_Engine.time + 5.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 2.0;
			self.m_flNextSecondaryAttack = g_Engine.time + 2.0;
			self.m_flTimeWeaponIdle = g_Engine.time + 2.0;
		}
	}

	void ReloadThink()
	{
		if( m_pDriveEnt !is null and m_pPlayer.pev.velocity.Length() <= 0 )
		{
			if( m_iWeaponModeGuard == MODE_BARNEY )
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 17);
			}
			else
			{
				m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, 7);
			}

			DoIdleAnimation();
			SetThink( null );
			m_pPlayer.SetMaxSpeedOverride( -1 );
		}

		pev.nextthink = g_Engine.time + 0.1;
	}

	void DoMovementAnimation()
	{
		float flMinWalkVelocity = -150.0;
		float flMaxWalkVelocity = 150.0;

		if( m_iWeaponModeGuard == MODE_OTIS )
		{
			m_pPlayer.SetMaxSpeedOverride( int(SPEED_RUN_OTIS) );
		}
		else
		{
			m_pPlayer.SetMaxSpeedOverride( 270 );
		}

		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();
			}
			else
			{
				if( GetFrame(32) == 1 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, 100 ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, 100 ); break;
					}
				}
				else if( GetFrame(32) == 19 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, 100 ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, 100 ); break;
					}
				}
			}

			m_fNextRegenTick = g_Engine.time + 3.0;
			
			if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
			{
				//nada
			}
			else
			{
				self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
			}
		}
		else if( (m_pPlayer.pev.flags & FL_FAKECLIENT) != 0 )
		{
			if( m_pDriveEnt.pev.sequence != m_pDriveEnt.LookupSequence("run") )
			{
				m_iState = STATE_RUN;
				m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("run");
				m_pDriveEnt.pev.frame = 0;
				m_pDriveEnt.ResetSequenceInfo();
			}
			else
			{
				if( GetFrame(23) == 4 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
					}
				}
				else if( GetFrame(23) == 15 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
					}
				}
			}

			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();
			}
			else
			{
				if( GetFrame(23) == 4 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
					}
				}
				else if( GetFrame(23) == 15 )
				{
					switch( Math.RandomLong(1, 2) )
					{
						case 1: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
						case 2: g_SoundSystem.EmitSoundDyn( m_pDriveEnt.edict(), CHAN_BODY, "common/npc_step4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); break;
					}
				}
			}

			m_fNextRegenTick = g_Engine.time + 3.0;
			self.m_flNextPrimaryAttack = g_Engine.time + 0.3;
			m_pPlayer.pev.flTimeStepSound = 9999; //prevents normal footsteps from playing, kinda? :hehe:
		}
	}

	void DoIdleAnimation()
	{
		if( m_pDriveEnt is null ) return;
		if( m_pDriveEnt.pev.sequence == m_pDriveEnt.LookupSequence("reload") and !m_pDriveEnt.m_fSequenceFinished ) return;

		if( m_iState != STATE_IDLE )
		{
			m_pPlayer.SetMaxSpeedOverride( 270 );
			m_iState = STATE_IDLE;
			m_pDriveEnt.pev.sequence = m_pDriveEnt.LookupSequence("idle1");
			m_pDriveEnt.pev.frame = 0;
			m_pDriveEnt.ResetSequenceInfo();
		}
	}

	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_securityguard", vecOrigin, Vector(0, m_pPlayer.pev.angles.y, 0), false, m_pPlayer.edict()) );
		m_pDriveEnt.pev.set_controller( 0,  127 );
		m_pDriveEnt.pev.set_controller( 1,  127 );
		m_pPlayer.m_flFieldOfView = 0.25;
		m_pPlayer.m_flDistLook = 8192.0;
		m_pPlayer.pev.effects |= EF_NODRAW;
		m_pPlayer.pev.iuser3 = 1;
		m_pPlayer.pev.fuser4 = 1; //disable jump
		self.m_bExclusiveHold = true;
		m_pPlayer.pev.armorvalue = 10.0;
		m_pPlayer.pev.armortype = 10.0;
		if( m_pPlayer.pev.health < 100 )
		{
			m_pPlayer.pev.health = 400 * ( m_pPlayer.pev.health / m_pPlayer.pev.max_health );
			m_pPlayer.pev.max_health = 400;
		}
		else
		{
			m_pPlayer.pev.health = 400;
			m_pPlayer.pev.max_health = 400;
		}

		NetworkMessage m1( MSG_ONE_UNRELIABLE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cam_idealdist 128;cam_idealpitch 10;cl_movespeedkey 0.2;\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, CNPC::CNPC_SECURITYGUARD );

		if( m_iWeaponModeGuard == MODE_OTIS )
		{
			g_EntityFuncs.SetModel( m_pDriveEnt, "models/cnpc/otis.mdl" );
			g_EntityFuncs.SetSize( m_pDriveEnt.pev, Vector(-16, -16, 0), Vector(16, 16, 72) );
			g_EntityFuncs.SetOrigin( m_pDriveEnt, pev.origin );
			m_pDriveEnt.pev.solid = SOLID_NOT;
			m_pDriveEnt.pev.movetype = MOVETYPE_NOCLIP;
		}

	}

	void ResetPlayer()
	{
		m_pPlayer.pev.iuser3 = 0;
		m_pPlayer.pev.fuser4 = 0; //enable jump
		m_pPlayer.SetMaxSpeedOverride( -1 );

		NetworkMessage m1( MSG_ONE_UNRELIABLE, NetworkMessages::SVC_STUFFTEXT, m_pPlayer.edict() );
			m1.WriteString( "cl_movespeedkey 0.3;\n" );
		m1.End();

		CustomKeyvalues@ pCustom = m_pPlayer.GetCustomKeyvalues();
		pCustom.SetKeyvalue( CNPC::sCNPCKV, 0 );
	}
}

class cnpc_securityguard : ScriptBaseMonsterEntity
{
	protected CBasePlayer@ m_pOwner
	{
		get { return cast<CBasePlayer@>( g_EntityFuncs.Instance(pev.owner) ); }
	}

	private float m_flNextOriginUpdate; //hopefully fixes hacky movement on other players
	private float m_flNextPainSound = 0.0;

	void Spawn()
	{
		if( !self.SetupModel() )
		{
			g_EntityFuncs.SetModel( self, "models/npcspawner/barney.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();

		self.SetBodygroup( 1, 1 );

		m_flNextOriginUpdate = g_Engine.time;

		SetThink( ThinkFunction(this.DriveThink) );
		pev.nextthink = g_Engine.time;
	}

	void DriveThink()
	{
		if( m_pOwner is null or !m_pOwner.IsConnected() or m_pOwner.pev.deadflag != DEAD_NO )
		{
			pev.velocity = g_vecZero;
			SetThink( ThinkFunction(this.DieThink) );
			pev.nextthink = g_Engine.time;

			return;
		}

		if( m_flNextOriginUpdate < g_Engine.time )
		{
			Vector vecOrigin = m_pOwner.pev.origin;
			vecOrigin.z -= 36.0;
			g_EntityFuncs.SetOrigin( self, vecOrigin );
			m_flNextOriginUpdate = g_Engine.time + 0.1;
		}

		pev.velocity = m_pOwner.pev.velocity;

		pev.angles.x = 0;

		if( pev.velocity.Length2D() > 0.0 and (pev.sequence == self.LookupSequence("walk") or pev.sequence == self.LookupSequence("run")) )
			pev.angles.y = Math.VecToAngles( pev.velocity ).y;
		else
			pev.angles.y = m_pOwner.pev.angles.y;

		pev.angles.z = 0;

		self.StudioFrameAdvance();

		pev.nextthink = g_Engine.time + 0.01;
	}

	void DieThink()
	{
		array<string> arrsDeathAnims = 
		{
			"diecrump",
			"diebackward",
			"dieviolent",
			"diegutshot",
			"diesimple",
			"dieforward"
		};

		pev.sequence = self.LookupSequence( arrsDeathAnims[Math.RandomLong(0, arrsDeathAnims.length()-1)] );
		pev.frame = 0;
		self.ResetSequenceInfo();
		g_EngineFuncs.DropToFloor( self.edict() );

		g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, pDieSounds[Math.RandomLong(0, pDieSounds.length()-1)], VOL_NORM, ATTN_NORM );

		SetThink( ThinkFunction(this.SUB_StartFadeOut) );
		pev.nextthink = g_Engine.time + 1.0;
	}

	void SUB_StartFadeOut()
	{
		if( pev.rendermode == kRenderNormal )
		{
			pev.renderamt = 255;
			pev.rendermode = kRenderTransTexture;
		}

		pev.solid = SOLID_NOT;
		pev.avelocity = g_vecZero;

		pev.nextthink = g_Engine.time + 0.1;
		SetThink( ThinkFunction(this.SUB_FadeOut) );
	}

	void SUB_FadeOut()
	{
		if( pev.renderamt > 7 )
		{
			pev.renderamt -= 7;
			pev.nextthink = g_Engine.time + 0.1;
		}
		else 
		{
			pev.renderamt = 0;
			pev.nextthink = g_Engine.time + 0.2;
			SetThink( ThinkFunction(this.SUB_Remove) );
		}
	}

	void SUB_Remove()
	{
		self.UpdateOnRemove();

		if( pev.health > 0 )
			pev.health = 0;

		NetworkMessage killbeam( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY, null );
			killbeam.WriteByte(TE_KILLBEAM);
			killbeam.WriteShort(self.entindex());
		killbeam.End();

		g_EntityFuncs.Remove(self);
	}
}

void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_securityguard::cnpc_securityguard", "cnpc_securityguard" );
	g_Game.PrecacheOther( "cnpc_securityguard" );

	g_CustomEntityFuncs.RegisterCustomEntity( "cnpc_securityguard::weapon_securityguard", sWeaponName );
	g_ItemRegistry.RegisterWeapon( sWeaponName, "controlnpc", "securityguard" );
	g_Game.PrecacheOther( sWeaponName );
}

} //namespace cnpc_securityguard END

/* TODO
Use turning animations 
*/
