/* 
* Custom Barniel NPC Entity- this was done to get around Barniel using Barney voices
* Call barnielCustom::Register() to register this entity.
* Entity classname: monster_barniel
*/

namespace barnielCustomNew
{

const int barniel_AE_DRAW			= 2;
const int barniel_AE_SHOOT			= 3;
const int barniel_AE_HOLSTER		= 4;
const int barniel_BODY_GUNHOLSTERED	= 0;
const int barniel_BODY_GUNDRAWN		= 1;
const int barniel_BODY_GUNGONE		= 2;

class CMonsterbarnielCustomNew : ScriptBaseMonsterEntity
{
	private bool	m_fGunDrawn;
	private float	m_painTime;
	private int		m_head;
	private int		m_cClipSize;
	private float	m_flNextFearScream;
	private float m_flNextScorePoint = 0.0;
	private	float m_flHealthRegenTime = 0;
	private	float m_flNextHealthCheck = 0;
	private	float m_flRemoveGlowTime = 0;
	private float m_flNextScream = 0.0;
	private float m_flReloadFinished = Math.FLOAT_MAX;
	private float m_flFollowTime = 0;
	private bool m_bIsInReload = false;

	protected CBasePlayer@ m_pOwner
	{
		get { return cast<CBasePlayer@>( g_EntityFuncs.Instance(pev.euser1) ); }
	}

	void Spawn()
	{
		if( !self.SetupModel() )
		{
			g_EntityFuncs.SetModel( self, "models/npcspawner/kate_v2.mdl" );
		}

		g_EntityFuncs.SetSize( self.pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
	
		pev.solid					= SOLID_SLIDEBOX;
		pev.movetype				= MOVETYPE_STEP;
		self.m_bloodColor			= BLOOD_COLOR_RED;
		if( self.pev.health == 0.0f )
			self.pev.health  = 300.0f;
		self.pev.view_ofs			= Vector( 0, 0, 50 );// position of the eyes relative to monster's origin.
		self.m_flFieldOfView		= VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
		self.m_MonsterState			= MONSTERSTATE_NONE;
		self.pev.body				= 1; // gun in holster
		m_fGunDrawn					= true;
		self.m_afCapability			= bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_CLIMB | bits_CAP_SQUAD | bits_CAP_STRAFE | bits_CAP_JUMP | bits_CAP_DUCK;
		self.m_fCanFearCreatures 	= false; // Can attempt to run away from things like zombies
		m_flNextFearScream			= g_Engine.time;
		//self.m_afMoveShootCap()	= bits_MOVESHOOT_RANGE_ATTACK1;

		m_cClipSize					= 17; //17 Shots
		self.m_cAmmoLoaded			= m_cClipSize;

		if( string( self.m_FormattedName ).IsEmpty() )
		{
			self.m_FormattedName = "Kate (Mad)";
		}

		self.SetPlayerAlly( !self.IsPlayerAlly() );
		self.CanPlayerFollow();
		
		if( self.IsPlayerAlly() ){ SetUse( UseFunction( this.FollowerUse ) ); }
		self.MonsterInit();

		SetThink(ThinkFunction(MonsterThink));
		self.pev.nextthink = g_Engine.time + 0.01;
	}

	int	Classify()
	{
		return self.GetClassification( CLASS_PLAYER_ALLY );
	}
	
	void SetYawSpeed()
	{
		int ys = 0;

		/*
		switch ( m_Activity )
		{
		case ACT_TURN_LEFT:
		case ACT_TURN_RIGHT:
			ys = 180;
			break;

		case ACT_IDLE:
		case ACT_WALK: 
			ys = 70;	
			break;
		case ACT_RUN:  
			ys = 90;	
			break;

		default:       
			ys = 70;	
			break;
		}
		*/

		ys = 360; //270 seems to be an ideal speed, which matches most animations

		self.pev.yaw_speed = ys;
	}

	void SetActivity(Activity NewActivity)
	{
		int iSequence = ACTIVITY_NOT_AVAILABLE;

		switch ( NewActivity )
		{
			case ACT_RELOAD:
			{
				iSequence = self.LookupSequence( "reload" );
			}
			break;
			case ACT_RUN:
			{
				iSequence = self.LookupSequence( "run" );
				self.pev.framerate = 2.0f;
			}
			break;
			case ACT_RANGE_ATTACK1:
			{
				iSequence = self.LookupSequence( "shootgun2" );
			}
			break;
			default:
				iSequence = self.LookupActivity ( NewActivity );
				break;
		}

		self.m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present

		// Set to the desired anim, or default anim if the desired is not present
		if ( iSequence > ACTIVITY_NOT_AVAILABLE )
		{
			if ( self.pev.sequence != iSequence || !self.m_fSequenceLoops )
			{
				self.pev.frame = 0;
			}

			self.pev.sequence		= iSequence;	// Set to the reset anim (if it's there)
			self.ResetSequenceInfo( );
			SetYawSpeed();
		}
		else
		{
			// Not available try to get default anim
			self.pev.sequence		= 0;	// Set to the reset anim (if it's there)
		}
	}

	void SetGaitActivity( Activity NewActivity )
	{
		int iSequence = ACTIVITY_NOT_AVAILABLE;

		switch ( NewActivity )
		{
			case ACT_RUN:
			{
				iSequence = self.LookupSequence( "run" );
			}
			break;
			case ACT_WALK:
			{
				iSequence = self.LookupSequence( "walk" );
			}
			break;
			default:
				iSequence = self.LookupActivity ( NewActivity );
				break;
		}

		self.m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present

		// Set to the desired anim, or default anim if the desired is not present
		if ( iSequence > ACTIVITY_NOT_AVAILABLE )
		{
			/*if ( self.pev.sequence != iSequence || !self.m_fSequenceLoops )
			{
				self.pev.frame = 0;
			}*/

			self.pev.gaitsequence		= iSequence;	// Set to the reset anim (if it's there)
			self.ResetGaitSequenceInfo();
			SetYawSpeed();
		}
		else
		{
			// Not available try to get default anim
			self.pev.sequence		= 0;	// Set to the reset anim (if it's there)
		}
	}
	
	bool CheckRangeAttack1( float flDot, float flDist )
	{	
		if ( flDist <= 16192 && flDot >= 0.5 && self.NoFriendlyFire() )
		{
			CBaseEntity@ pEnemy = self.m_hEnemy.GetEntity();
			TraceResult tr;
			Vector shootOrigin = self.pev.origin + Vector( 0, 0, 55 );
			Vector shootTarget = pEnemy.BodyTarget( shootOrigin );
			g_Utility.TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, self.edict(), tr );
						
			if ( tr.flFraction == 1.0 || tr.pHit is pEnemy.edict() )
				return true;
		}

		return false;
	}

	bool CheckMeleeAttack1( float flDot, float flDist )
	{
		return false;
	}

	bool CanCheckAttacks( void )
	{
		if ( !self.HasConditions( bits_COND_ENEMY_TOOFAR ) && self.m_cAmmoLoaded != 0 )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	void barnielFirePistol()
	{
		Math.MakeVectors( self.pev.angles );
		Vector vecShootOrigin = self.pev.origin + Vector( 0, 0, 55 );
		Vector vecShootDir = self.ShootAtEnemy( vecShootOrigin );
		Vector angDir = Math.VecToAngles( vecShootDir );

		//self.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 16192, BULLET_PLAYER_MP5 );
		self.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 1, g_EngineFuncs.CVarGetFloat('sk_9mm_bullet') * 1.25, self.pev );
		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 * -17, vecShellVelocity, self.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL); 

		int pitchShift = Math.RandomLong( 0, 20 );
		if ( pitchShift > 10 )// Only shift about half the time
			pitchShift = 0;
		else
			pitchShift -= 5;
		
		self.SetBlending( 0, angDir.x );
		self.pev.effects = EF_MUZZLEFLASH;
		GetSoundEntInstance().InsertSound ( bits_SOUND_COMBAT, self.pev.origin, NORMAL_GUN_VOLUME, 0.3, self );
		//g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, "ins2/wpn/m1911/shoot.ogg", 1, 0.8, 0, PITCH_NORM + pitchShift );
		g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, "weapons/pl_gun4.wav", 1.0, 0.45, 0, PITCH_NORM );

		WW2DynamicLight( self.pev.origin, 4, 255, 232, 156, 8, 50 );

		if( self.pev.movetype != MOVETYPE_FLY && self.m_MonsterState != MONSTERSTATE_PRONE )
		{
			self.m_flAutomaticAttackTime = 0.2; //g_Engine.time + Math.RandomFloat(0.2, 0.3);
		}

		if( m_flNextScream <= g_Engine.time )
		{
			switch ( Math.RandomLong( 1, 6 ) )
			{
				case 1:	
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_backoff.wav", 1, 0.5, 0, 100 ); break;
				}
				case 2:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die.wav", 1, 0.5, 0, 100 ); break;
				}
				case 3:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die2.wav", 1, 0.5, 0, 100 ); break;
				}
				case 4:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die3.wav", 1, 0.5, 0, 100 ); break;
				}
				case 5:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_takethat.wav", 1, 0.5, 0, 100 ); break;
				}
				case 6:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/infacemf.wav", 1, 0.5, 0, 100 ); break;
				}
			}

			m_flNextScream = Math.FLOAT_MAX;
			m_painTime = g_Engine.time + 2.0;
		}

		// UNDONE: Reload?
		--self.m_cAmmoLoaded;// take away a bullet!
	}
	
	void CheckAmmo()
	{
		if ( self.m_cAmmoLoaded <= 0 )
		{
			self.SetConditions( bits_COND_NO_AMMO_LOADED );
		}
	}

	void HandleAnimEvent( MonsterEvent@ pEvent )
	{
		switch( pEvent.event )
		{
		case barniel_AE_SHOOT:
			barnielFirePistol();
			break;
		case barniel_AE_DRAW:
			// barniel's bodygroup switches here so he can pull gun from holster
			self.pev.body = barniel_BODY_GUNDRAWN;
			m_fGunDrawn = true;
			break;

		case barniel_AE_HOLSTER:
			// change bodygroup to replace gun in holster
			self.pev.body = barniel_BODY_GUNHOLSTERED;
			m_fGunDrawn = false;
			break;

		default:
			BaseClass.HandleAnimEvent( pEvent );
		}
	}

	void MonsterThink()
	{
		//self.Look( 4096 );

		/*if( self.HasConditions( bits_COND_SEE_ENEMY ) and self.m_cAmmoLoaded == 0 and self.IsAlive() )
		{
			self.SetConditions( bits_COND_HEAVY_DAMAGE );
		}*/

		if( self.HasConditions( bits_COND_SEE_ENEMY ) and !m_bIsInReload and self.IsAlive() )
		{
			self.SetConditions( bits_COND_CAN_RANGE_ATTACK1 );
		}

		if( m_bIsInReload and self.IsAlive() )
		{
			if( self.pev.sequence != self.LookupSequence("reload") )
			{
				pev.sequence = self.LookupSequence("reload");
				self.ResetSequenceInfo();
				self.m_flGroundSpeed = 0.01;
			}

			/*if( pev.velocity.Length2D() > 0.0 and self.pev.sequence == self.LookupSequence("reload") )
			{
				SetGaitActivity(ACT_RUN);
				pev.sequence = self.LookupSequence("reload");
				self.ResetSequenceInfo();
			}*/
		}

		if ( self.m_cAmmoLoaded == 0 and !m_bIsInReload and self.IsAlive() )
		{
			//DoReload();
			//self.m_flEffectSpeed = 0.01;
			m_flReloadFinished = g_Engine.time + (27.0/15.0);
			m_bIsInReload = true;
		}

		if ( m_flReloadFinished <= g_Engine.time and m_bIsInReload and self.IsAlive() )
		{
			ReloadDone();
			self.m_flGroundSpeed = -1.0;
			SetActivity(ACT_IDLE);
			self.ClearConditions( bits_COND_HEAVY_DAMAGE );
			self.ClearConditions( bits_COND_SEE_FEAR );
			m_flReloadFinished = Math.FLOAT_MAX;
			m_bIsInReload = false;
			m_flNextScream = 0.0;
		}

		self.StudioFrameAdvance();

		pev.nextthink = g_Engine.time + 0.1;

		BaseClass.Think();
	}
	
	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;

		// make sure friends talk about it if player hurts talkmonsters...
		int ret = BaseClass.TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
		if ( ( !self.IsAlive() || self.pev.deadflag == DEAD_DYING) && (!self.IsPlayerAlly()))	// evils dont alert friends!
			return ret;

		if ( self.m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker.flags & FL_CLIENT) != 0 )
		{
			// This is a heurstic to determine if the player intended to harm me
			// If I have an enemy, we can't establish intent (may just be crossfire)
			if ( !self.m_hEnemy.IsValid() )
			{		
				if( self.pev.deadflag == DEAD_NO )
				{
					// If the player was facing directly at me, or I'm already suspicious, get mad
					if ( (self.m_afMemory & bits_MEMORY_SUSPICIOUS) != 0 || pAttacker.IsFacing( self.pev, 0.96f ) )
					{
						// Alright, now I'm pissed!
						//PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM );

						self.Remember( bits_MEMORY_PROVOKED );
						self.StopPlayerFollowing( true, false );
					}
					else
					{
						// Hey, be careful with that
						//PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
						self.Remember( bits_MEMORY_SUSPICIOUS );
					}
				}
			}
			else if ( (!self.m_hEnemy.GetEntity().IsPlayer()) && self.pev.deadflag == DEAD_NO )
			{
				//PlaySentence( "BN_SHOT", 4, VOL_NORM, ATTN_NORM );
			}
		}

		return ret;
	}

	float GetPointsForDamage( float flDamage )
	{
		return ( flDamage/self.pev.max_health ) * ( 10 );
	}

	/*void AlertSound()
	{
		switch ( Math.RandomLong( 0, 1 ) )
		{
			case 0:	
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/FEMALE_SEEENEMY1.wav", 0.75, 1.25, 0, 90 );
				break;
			case 1:
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/FEMALE_SEEENEMY2.wav", 0.75, 1.25, 0, 90 );
				break;
		}
	}
	
	void FearScream()
	{
		if( m_flNextFearScream < g_Engine.time )
		{
			switch (Math.RandomLong(0,2))
			{
				case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_becarefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
				case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_carefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
				case 2: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_carefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
			}

			m_flNextFearScream = g_Engine.time + Math.RandomLong(2,5);
		}
	}
	
	void PainSound()
	{
		if (g_Engine.time < m_painTime)
			return;
		
		m_painTime = g_Engine.time + Math.RandomFloat(0.5, 0.75);
		switch (Math.RandomLong(0,2))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/HURTPAIN_A_FEMALE_1.wav", 0.5, 1.25, 0, 90 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/HURTPAIN_A_FEMALE_2.wav", 0.5, 1.25, 0, 90 ); break;
			case 2: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/HURTPAIN_A_FEMALE_3.wav", 0.5, 1.25, 0, 90 ); break;
		}
	}
	
	void DeathSound()
	{
		switch (Math.RandomLong(0,1))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/FEMALE_DEATH1.wav", 1.0, ATTN_NORM, 0, 90 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "combatarms2/misc/FEMALE_DEATH2.wav", 1.0, ATTN_NORM, 0, 90 ); break;
		}
	}*/

	void PainSound()
	{
		if (g_Engine.time < m_painTime)
			return;
		
		m_painTime = g_Engine.time + 2.0;
		switch (Math.RandomLong(0,2))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain1.wav", 1.0, 0.5, 0, 100 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain2.wav", 1.0, 0.5, 0, 100 ); break;
			case 2: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain3.wav", 1.0, 0.5, 0, 100 ); break;
			case 3: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain3.wav", 1.0, 0.5, 0, 100 ); break;
		}
	}
	
	void DeathSound()
	{
		switch (Math.RandomLong(0,1))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/death1.wav", 1.0, 0.5, 0, 100 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/death2.wav", 1.0, 0.5, 0, 100 ); break;
		}
	}

	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 == HITGROUP_CHEST || ptr.iHitgroup == HITGROUP_STOMACH ) && ( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_BLAST) ) != 0 )
		{
			if(flDamage >= 2)
				flDamage -= 2;

			flDamage *= 0.5;
		}
		
		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
		}

		BaseClass.TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
	}

	void RunAI( void )
	{
		if( m_flHealthRegenTime < g_Engine.time and self.pev.health < self.pev.max_health and self.IsAlive() )
		{
			g_EntityFuncs.DispatchKeyValue(self.edict(), "rendermode", 0);
			g_EntityFuncs.DispatchKeyValue(self.edict(), "renderfx", 19);
			g_EntityFuncs.DispatchKeyValue(self.edict(), "rendercolor", "0 100 0");

			self.pev.health += 25;
			m_flHealthRegenTime = g_Engine.time + 3.0;
			m_flRemoveGlowTime = g_Engine.time + 1.0;
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_ITEM, "misc/regen.wav", 0.5, ATTN_NORM, 0, PITCH_NORM );
		}

		if( m_flNextHealthCheck < g_Engine.time and self.pev.health > self.pev.max_health and self.IsAlive() )
		{
			self.pev.health = self.pev.max_health;
			m_flNextHealthCheck = g_Engine.time + 0.5;
		}

		if( m_flRemoveGlowTime < g_Engine.time and self.IsAlive() )
		{
			g_EntityFuncs.DispatchKeyValue(self.edict(), "renderfx", 0);
			m_flRemoveGlowTime = g_Engine.time + 99999;
		}

		if( self.pev.frags > 0 && m_pOwner !is null && m_flNextScorePoint < g_Engine.time )
		{
			self.pev.frags -= 1;
			m_pOwner.pev.frags += 1;
			m_flNextScorePoint = g_Engine.time + 1.0;
		}

		if( !self.IsPlayerFollowing() )
		{
			FollowMe();
		}

		if( self.IsAlive() )
		{
			self.ClearConditions(bits_COND_SEE_FEAR|bits_COND_HEAVY_DAMAGE);
		}

		if( self.m_MonsterState == MONSTERSTATE_IDLE and self.HasConditions( bits_COND_ENEMY_DEAD ) )
		{
			m_flNextScream = 0.0;
		}

		if( self.m_MonsterState == MONSTERSTATE_COMBAT and ( self.HasConditions( bits_COND_ENEMY_OCCLUDED ) || self.HasConditions( bits_COND_ENEMY_DEAD ) || self.HasConditions( bits_COND_NEW_ENEMY ) ) )
		{
			m_flNextScream = 0.0;
		}

		BaseClass.RunAI();
	}

	void ReloadDone()
	{
		g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_STATIC, "hgrunt/gr_reload1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
		self.m_cAmmoLoaded = 17;
	}

	void DoReload()
	{
		pev.sequence = self.LookupSequence("reload");
		pev.frame = 0;
		self.ResetSequenceInfo();
	}

	void FollowMe()
	{
		if( self.pev.netname != "nofollow" )
		{
			self.FollowerPlayerUse( m_pOwner, m_pOwner, USE_TOGGLE, 1.0 );
		}
		else
		{
			//nada
		}
	}
	
	void FollowerUse( CBaseEntity@ pActivator, CBaseEntity@ pCaller, USE_TYPE useType, float flValue )
	{
		self.FollowerPlayerUse( pActivator, pCaller, useType, flValue );
		
		CBaseEntity@ pTarget = self.m_hTargetEnt;
		
		if( pTarget is pActivator )
		{
			g_SoundSystem.PlaySentenceGroup( self.edict(), "BN_OK", 1.0, ATTN_NORM, 0, PITCH_NORM );
		}
		else
			g_SoundSystem.PlaySentenceGroup( self.edict(), "BN_WAIT", 1.0, ATTN_NORM, 0, PITCH_NORM );
	}
}

class Kate : ScriptBaseMonsterEntity
{
	private bool	m_fGunDrawn;
	private float	m_painTime;
	private int		m_head;
	private int		m_cClipSize;
	private float	m_flNextFearScream;
	private float m_flNextScorePoint = 0.0;
	private	float m_flHealthRegenTime = 0;
	private	float m_flNextHealthCheck = 0;
	private	float m_flRemoveGlowTime = 0;
	private float m_flNextScream = 0.0;
	private float m_flReloadFinished = Math.FLOAT_MAX;
	private float m_flFollowTime = 0;
	private bool m_bIsInReload = false;

	protected CBasePlayer@ m_pOwner
	{
		get { return cast<CBasePlayer@>( g_EntityFuncs.Instance(pev.euser1) ); }
	}

	void Spawn()
	{
		//Precache();
		
		if( !self.SetupModel() )
		{
			g_EntityFuncs.SetModel( self, "models/npcspawner/kate_v3.mdl" );
		}

		g_EntityFuncs.SetSize( self.pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
	
		pev.solid					= SOLID_SLIDEBOX;
		pev.movetype				= MOVETYPE_STEP;
		self.m_bloodColor			= BLOOD_COLOR_RED;
		if( self.pev.health == 0.0f )
			self.pev.health  = 300.0f;
		self.pev.view_ofs			= Vector( 0, 0, 50 );// position of the eyes relative to monster's origin.
		self.m_flFieldOfView		= VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
		self.m_MonsterState			= MONSTERSTATE_NONE;
		self.pev.body = 1;
		m_fGunDrawn					= true;
		self.m_afCapability			= bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_CLIMB | bits_CAP_SQUAD | bits_CAP_STRAFE | bits_CAP_JUMP | bits_CAP_DUCK;
		self.m_fCanFearCreatures 	= false; // Can attempt to run away from things like zombies
		m_flNextFearScream			= g_Engine.time;
		//self.m_afMoveShootCap()	= bits_MOVESHOOT_RANGE_ATTACK1;
		m_cClipSize					= 17; //17 Shots
		self.m_cAmmoLoaded			= m_cClipSize;

		if( string( self.m_FormattedName ).IsEmpty() )
		{
			self.m_FormattedName = "Cpl. Kate (Mad)";
		}

		self.SetPlayerAlly( !self.IsPlayerAlly() );
		self.CanPlayerFollow();
		
		if( self.IsPlayerAlly() ){ SetUse( UseFunction( this.FollowerUse ) ); }
		self.MonsterInit();

		SetThink(ThinkFunction(MonsterThink));
		self.pev.nextthink = g_Engine.time + 0.01;
	}

	int	Classify()
	{
		return self.GetClassification( CLASS_PLAYER_ALLY );
	}
	
	void SetYawSpeed()
	{
		int ys = 0;

		/*
		switch ( m_Activity )
		{
		case ACT_TURN_LEFT:
		case ACT_TURN_RIGHT:
			ys = 180;
			break;

		case ACT_IDLE:
		case ACT_WALK: 
			ys = 70;	
			break;
		case ACT_RUN:  
			ys = 90;	
			break;

		default:       
			ys = 70;	
			break;
		}
		*/

		ys = 360; //270 seems to be an ideal speed, which matches most animations

		self.pev.yaw_speed = ys;
	}

	void SetActivity(Activity NewActivity)
	{
		int iSequence = ACTIVITY_NOT_AVAILABLE;

		switch ( NewActivity )
		{
			case ACT_RELOAD:
			{
				iSequence = self.LookupSequence( "reload" );
			}
			break;
			case ACT_RUN:
			{
				iSequence = self.LookupSequence( "run" );
				self.pev.framerate = 2.0f;
			}
			break;
			case ACT_RANGE_ATTACK1:
			{
				iSequence = self.LookupSequence( "shootgun2" );
			}
			break;
			default:
				iSequence = self.LookupActivity ( NewActivity );
				break;
		}

		self.m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present

		// Set to the desired anim, or default anim if the desired is not present
		if ( iSequence > ACTIVITY_NOT_AVAILABLE )
		{
			if ( self.pev.sequence != iSequence || !self.m_fSequenceLoops )
			{
				self.pev.frame = 0;
			}

			self.pev.sequence		= iSequence;	// Set to the reset anim (if it's there)
			self.ResetSequenceInfo( );
			SetYawSpeed();
		}
		else
		{
			// Not available try to get default anim
			self.pev.sequence		= 0;	// Set to the reset anim (if it's there)
		}
	}

	void SetGaitActivity( Activity NewActivity )
	{
		int iSequence = ACTIVITY_NOT_AVAILABLE;

		switch ( NewActivity )
		{
			case ACT_RUN:
			{
				iSequence = self.LookupSequence( "run" );
			}
			break;
			case ACT_WALK:
			{
				iSequence = self.LookupSequence( "walk" );
			}
			break;
			case ACT_RANGE_ATTACK1:
			{
				iSequence = self.LookupSequence( "shootgun2" );
			}
			break;
			//default:
				//iSequence = self.LookupActivity ( NewActivity );
				//break;
		}

		self.m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present

		// Set to the desired anim, or default anim if the desired is not present
		if ( iSequence > ACTIVITY_NOT_AVAILABLE )
		{
			/*if ( self.pev.sequence != iSequence || !self.m_fSequenceLoops )
			{
				self.pev.frame = 0;
			}*/

			self.pev.gaitsequence		= iSequence;	// Set to the reset anim (if it's there)
			self.ResetGaitSequenceInfo();
			SetYawSpeed();
		}
		else
		{
			// Not available try to get default anim
			self.pev.sequence		= 0;	// Set to the reset anim (if it's there)
		}
	}
	
	bool CheckRangeAttack1( float flDot, float flDist )
	{	
		if ( flDist <= 16192 && flDot >= 0.5 && self.NoFriendlyFire() )
		{
			CBaseEntity@ pEnemy = self.m_hEnemy.GetEntity();
			TraceResult tr;
			Vector shootOrigin = self.pev.origin + Vector( 0, 0, 55 );
			Vector shootTarget = pEnemy.BodyTarget( shootOrigin );
			g_Utility.TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, self.edict(), tr );
						
			if ( tr.flFraction == 1.0 || tr.pHit is pEnemy.edict() )
				return true;
		}

		return false;
	}

	bool CheckMeleeAttack1( float flDot, float flDist )
	{
		return false;
	}

	bool CanCheckAttacks( void )
	{
		if ( !self.HasConditions( bits_COND_ENEMY_TOOFAR ) && self.m_cAmmoLoaded != 0 )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	void barnielFirePistol()
	{
		Math.MakeVectors( self.pev.angles );
		Vector vecShootOrigin = self.pev.origin + Vector( 0, 0, 55 );
		Vector vecShootDir = self.ShootAtEnemy( vecShootOrigin );
		Vector angDir = Math.VecToAngles( vecShootDir );

		//self.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 16192, BULLET_PLAYER_MP5 );
		self.FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 8192.0, BULLET_PLAYER_CUSTOMDAMAGE, 1, g_EngineFuncs.CVarGetFloat('sk_9mm_bullet') * 1.5, self.pev );
		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 * -17, vecShellVelocity, self.pev.angles.y, m_iShellNew, TE_BOUNCE_SHELL); 

		int pitchShift = Math.RandomLong( 0, 20 );
		if ( pitchShift > 10 )// Only shift about half the time
			pitchShift = 0;
		else
			pitchShift -= 5;
		
		self.SetBlending( 0, angDir.x );
		self.pev.effects = EF_MUZZLEFLASH;
		GetSoundEntInstance().InsertSound ( bits_SOUND_COMBAT, self.pev.origin, LOUD_GUN_VOLUME, 0.3, self );
		//g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, "ins2/wpn/m1911/shoot.ogg", 1, 0.8, 0, PITCH_NORM + pitchShift );
		g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, "weapons/pl_gun4.wav", 1.0, 0.45, 0, PITCH_NORM );

		WW2DynamicLight( self.pev.origin, 4, 255, 232, 156, 8, 50 );

		if( self.pev.movetype != MOVETYPE_FLY && self.m_MonsterState != MONSTERSTATE_PRONE )
		{
			self.m_flAutomaticAttackTime = 0.2; //g_Engine.time + 0.2; //Math.RandomFloat(0.2, 0.2);
		}

		if( m_flNextScream <= g_Engine.time )
		{
			switch ( Math.RandomLong( 1, 6 ) )
			{
				case 1:	
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_backoff.wav", 1, 0.5, 0, 100 ); break;
				}
				case 2:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die.wav", 1, 0.5, 0, 100 ); break;
				}
				case 3:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die2.wav", 1, 0.5, 0, 100 ); break;
				}
				case 4:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die3.wav", 1, 0.5, 0, 100 ); break;
				}
				case 5:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_takethat.wav", 1, 0.5, 0, 100 ); break;
				}
				case 6:
				{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/infacemf.wav", 1, 0.5, 0, 100 ); break;
				}
			}

			m_flNextScream = Math.FLOAT_MAX;
			m_painTime = g_Engine.time + 2.0;
		}

		// UNDONE: Reload?
		--self.m_cAmmoLoaded;// take away a bullet!
	}
	
	void CheckAmmo()
	{
		if ( self.m_cAmmoLoaded <= 0 )
		{
			self.SetConditions( bits_COND_NO_AMMO_LOADED );
		}
	}

	void HandleAnimEvent( MonsterEvent@ pEvent )
	{
		switch( pEvent.event )
		{
		case barniel_AE_SHOOT:
			barnielFirePistol();
			break;
		case barniel_AE_DRAW:
			// barniel's bodygroup switches here so he can pull gun from holster
			self.pev.body = barniel_BODY_GUNDRAWN;
			m_fGunDrawn = true;
			break;

		case barniel_AE_HOLSTER:
			// change bodygroup to replace gun in holster
			self.pev.body = barniel_BODY_GUNHOLSTERED;
			m_fGunDrawn = false;
			break;

		default:
			BaseClass.HandleAnimEvent( pEvent );
		}
	}
	
	void Precache()
	{
		//g_SoundSystem.PrecacheSound("barniel/ba_attack1.wav");
		g_SoundSystem.PrecacheSound("azuresheep/barniel/bn_becarefull.wav");
		g_SoundSystem.PrecacheSound("azuresheep/barniel/bn_carefull.wav");
		g_SoundSystem.PrecacheSound("azuresheep/barniel/bn_pain1.wav");
		g_SoundSystem.PrecacheSound("azuresheep/barniel/bn_pain2.wav");
		g_SoundSystem.PrecacheSound("azuresheep/barniel/bn_die1.wav");
		g_Game.PrecacheGeneric("azuresheep/barniel/bn_becarefull.wav");
		g_Game.PrecacheGeneric("azuresheep/barniel/bn_carefull.wav");
		g_Game.PrecacheGeneric("azuresheep/barniel/bn_pain1.wav");
		g_Game.PrecacheGeneric("azuresheep/barniel/bn_pain2.wav");
		g_Game.PrecacheGeneric("azuresheep/barniel/bn_die1.wav");
	}

	void MonsterThink()
	{
		//self.Look( 4096 );

		/*if( self.HasConditions( bits_COND_SEE_ENEMY ) and self.m_cAmmoLoaded == 0 and self.IsAlive() )
		{
			self.SetConditions( bits_COND_HEAVY_DAMAGE );
		}*/

		if( self.HasConditions( bits_COND_SEE_ENEMY ) and !m_bIsInReload and self.IsAlive() )
		{
			self.SetConditions( bits_COND_CAN_RANGE_ATTACK1 );
		}

		if( m_bIsInReload and self.IsAlive() )
		{
			if( self.pev.sequence != self.LookupSequence("reload") )
			{
				pev.sequence = self.LookupSequence("reload");
				self.ResetSequenceInfo();
				self.m_flGroundSpeed = 0.01;
			}
		}

		if ( self.m_cAmmoLoaded == 0 and !m_bIsInReload and self.IsAlive() )
		{
			//DoReload();
			//self.m_flEffectSpeed = 0.01;
			m_flReloadFinished = g_Engine.time + (27.0/15.0);
			m_bIsInReload = true;
		}

		if ( m_flReloadFinished <= g_Engine.time and m_bIsInReload and self.IsAlive() )
		{
			ReloadDone();
			self.m_flGroundSpeed = -1.0;
			SetActivity(ACT_IDLE);
			self.ClearConditions( bits_COND_HEAVY_DAMAGE );
			self.ClearConditions( bits_COND_SEE_FEAR );
			m_flReloadFinished = Math.FLOAT_MAX;
			m_bIsInReload = false;
			m_flNextScream = 0.0;
		}

		self.StudioFrameAdvance();

		pev.nextthink = g_Engine.time + 0.1;

		BaseClass.Think();
	}
	
	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;

		// make sure friends talk about it if player hurts talkmonsters...
		int ret = BaseClass.TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
		if ( ( !self.IsAlive() || self.pev.deadflag == DEAD_DYING) && (!self.IsPlayerAlly()))	// evils dont alert friends!
			return ret;

		if ( self.m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker.flags & FL_CLIENT) != 0 )
		{
			// This is a heurstic to determine if the player intended to harm me
			// If I have an enemy, we can't establish intent (may just be crossfire)
			if ( !self.m_hEnemy.IsValid() )
			{		
				if( self.pev.deadflag == DEAD_NO )
				{
					// If the player was facing directly at me, or I'm already suspicious, get mad
					if ( (self.m_afMemory & bits_MEMORY_SUSPICIOUS) != 0 || pAttacker.IsFacing( self.pev, 0.96f ) )
					{
						// Alright, now I'm pissed!
						//PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM );

						self.Remember( bits_MEMORY_PROVOKED );
						self.StopPlayerFollowing( true, false );
					}
					else
					{
						// Hey, be careful with that
						//PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
						self.Remember( bits_MEMORY_SUSPICIOUS );
					}
				}
			}
			else if ( (!self.m_hEnemy.GetEntity().IsPlayer()) && self.pev.deadflag == DEAD_NO )
			{
				//PlaySentence( "BN_SHOT", 4, VOL_NORM, ATTN_NORM );
			}
		}

		return ret;
	}

	float GetPointsForDamage( float flDamage )
	{
		return ( flDamage/self.pev.max_health ) * ( 10 );
	}

	/*void AlertSound()
	{
		switch ( Math.RandomLong( 1, 6 ) )
		{
			case 1:	
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_backoff.wav", 1, 0.5, 0, 100 ); break;
			}
			case 2:
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die.wav", 1, 0.5, 0, 100 ); break;
			}
			case 3:
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die2.wav", 1, 0.5, 0, 100 ); break;
			}
			case 4:
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_die3.wav", 1, 0.5, 0, 100 ); break;
			}
			case 5:
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/f_takethat.wav", 1, 0.5, 0, 100 ); break;
			}
			case 6:
			{
				g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/infacemf.wav", 1, 0.5, 0, 100 ); break;
			}
		}

		m_painTime = g_Engine.time + 3.0;
	}*/
	
	void FearScream()
	{
		if( m_flNextFearScream < g_Engine.time )
		{
			switch (Math.RandomLong(0,2))
			{
				case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_becarefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
				case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_carefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
				case 2: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "azuresheep/barniel/bn_carefull.wav", 1, ATTN_NORM, 0, PITCH_NORM ); break;
			}

			m_flNextFearScream = g_Engine.time + Math.RandomLong(2,5);
		}
	}
	
	void PainSound()
	{
		if (g_Engine.time < m_painTime)
			return;
		
		m_painTime = g_Engine.time + 2.0; //Math.RandomFloat(0.5, 0.75);
		switch (Math.RandomLong(0,2))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain1.wav", 1.0, 0.5, 0, 100 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain2.wav", 1.0, 0.5, 0, 100 ); break;
			case 2: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain3.wav", 1.0, 0.5, 0, 100 ); break;
			case 3: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/pain3.wav", 1.0, 0.5, 0, 100 ); break;
		}
	}
	
	void DeathSound()
	{
		switch (Math.RandomLong(0,1))
		{
			case 0: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/death1.wav", 1.0, 0.5, 0, 100 ); break;
			case 1: g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "kingpin_loc/female/death2.wav", 1.0, 0.5, 0, 100 ); break;
		}
	}

	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 == HITGROUP_CHEST || ptr.iHitgroup == HITGROUP_STOMACH ) && ( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_BLAST) ) != 0 )
		{
			if(flDamage >= 2)
				flDamage -= 2;

			flDamage *= 0.5;
		}
		
		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
		}

		BaseClass.TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
	}

	void RunAI( void )
	{
		if( m_flHealthRegenTime < g_Engine.time and self.pev.health < self.pev.max_health and self.IsAlive() )
		{
			g_EntityFuncs.DispatchKeyValue(self.edict(), "rendermode", 0);
			g_EntityFuncs.DispatchKeyValue(self.edict(), "renderfx", 19);
			g_EntityFuncs.DispatchKeyValue(self.edict(), "rendercolor", "0 100 0");

			self.pev.health += 25;
			m_flHealthRegenTime = g_Engine.time + 3.0;
			m_flRemoveGlowTime = g_Engine.time + 1.0;
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_ITEM, "misc/regen.wav", 0.5, ATTN_NORM, 0, PITCH_NORM );
		}

		if( m_flNextHealthCheck < g_Engine.time and self.pev.health > self.pev.max_health and self.IsAlive() )
		{
			self.pev.health = self.pev.max_health;
			m_flNextHealthCheck = g_Engine.time + 0.5;
		}

		if( m_flRemoveGlowTime < g_Engine.time and self.IsAlive() )
		{
			g_EntityFuncs.DispatchKeyValue(self.edict(), "renderfx", 0);
			m_flRemoveGlowTime = g_Engine.time + 99999;
		}

		if( self.pev.frags > 0 && m_pOwner !is null && m_flNextScorePoint < g_Engine.time )
		{
			self.pev.frags -= 1;
			m_pOwner.pev.frags += 1;
			m_flNextScorePoint = g_Engine.time + 1.0;
		}

		if( !self.IsPlayerFollowing() )
		{
			FollowMe();
		}

		if( self.IsAlive() )
		{
			self.ClearConditions(bits_COND_SEE_FEAR|bits_COND_HEAVY_DAMAGE);
		}

		if( self.m_MonsterState == MONSTERSTATE_IDLE and self.HasConditions( bits_COND_ENEMY_DEAD ) )
		{
			m_flNextScream = 0.0;
		}

		if( self.m_MonsterState == MONSTERSTATE_COMBAT and ( self.HasConditions( bits_COND_ENEMY_OCCLUDED ) || self.HasConditions( bits_COND_ENEMY_DEAD ) || self.HasConditions( bits_COND_NEW_ENEMY ) ) )
		{
			m_flNextScream = 0.0;
		}

		BaseClass.RunAI();
	}

	void ReloadDone()
	{
		//g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_STATIC, "hgrunt/gr_reload1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );
		self.m_cAmmoLoaded = m_cClipSize;
	}

	void DoReload()
	{
		pev.sequence = self.LookupSequence("reload");
		pev.frame = 0;
		self.ResetSequenceInfo();
	}

	void FollowMe()
	{
		if( self.pev.netname != "nofollow" )
		{
			self.FollowerPlayerUse( m_pOwner, m_pOwner, USE_TOGGLE, 1.0 );
		}
		else
		{
			//nada
		}
	}
	
	void FollowerUse( CBaseEntity@ pActivator, CBaseEntity@ pCaller, USE_TYPE useType, float flValue )
	{
		self.FollowerPlayerUse( pActivator, pCaller, useType, flValue );
		
		CBaseEntity@ pTarget = self.m_hTargetEnt;
		
		if( pTarget is pActivator )
		{
			g_SoundSystem.PlaySentenceGroup( self.edict(), "BN_OK", 1.0, ATTN_NORM, 0, PITCH_NORM );
		}
		else
			g_SoundSystem.PlaySentenceGroup( self.edict(), "BN_WAIT", 1.0, ATTN_NORM, 0, PITCH_NORM );
	}
}

void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity( "barnielCustomNew::CMonsterbarnielCustomNew", "monster_angrykate" );
	g_CustomEntityFuncs.RegisterCustomEntity( "barnielCustomNew::Kate", "monster_angrykate2" );
}

} // end of namespace