/*

*/

#include "data_classlevels_data"

enum AISTATE 
{
  STATE_IDLE = 0,
  STATE_WALK,
  STATE_RUN,
  STATE_ATTACK,
  STATE_PAIN,
  STATE_DEAD
};

namespace MonsterSlime
{
	AISTATE m_iAIState = STATE_IDLE;
	EHandle m_hEnemy = null;
	Activity m_Activity;           // what the monster is doing (animation)
	Activity m_IdealActivity;      // monster should switch to this activity
	
	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> pAttackSounds =
	{
		"zombie/zo_attack1.wav",
		"zombie/zo_attack2.wav",
	};
	const array<string> pIdleSounds =
	{
		"zombie/zo_idle1.wav",
		"zombie/zo_idle2.wav",
		"zombie/zo_idle3.wav",
		"zombie/zo_idle4.wav",
	};
	const array<string> pAlertSounds =
	{
		"zombie/zo_alert10.wav",
		"zombie/zo_alert20.wav",
		"zombie/zo_alert30.wav",
	};
	const array<string> pPainSounds =
	{
		"zombie/zo_pain1.wav",
		"zombie/zo_pain2.wav",
	};

	CBaseEntity@ CheckTraceHullAttack( CBaseMonster@ pThis, float flDist, int iDamage, int iDmgType ) {
		TraceResult tr;

		if (pThis.IsPlayer()) {
			Math.MakeVectors( pThis.pev.angles );
		} else {
			Math.MakeAimVectors( pThis.pev.angles );
		}

		Vector vecStart = pThis.pev.origin;
		vecStart.z += pThis.pev.size.z * 0.5;
		Vector vecEnd = vecStart + (g_Engine.v_forward * flDist );

		g_Utility.TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, pThis.edict(), tr );
		
		if ( tr.pHit !is null ) {
			CBaseEntity@ pEntity = g_EntityFuncs.Instance( tr.pHit );
			if ( iDamage > 0 ) {
				pEntity.TakeDamage( pThis.pev, pThis.pev, iDamage, iDmgType );
			}
			return pEntity;
		}
		return null;
	}
	
/*
class CZombie : public CBaseMonster
{
public:
	void HandleAnimEvent( MonsterEvent_t *pEvent );
	// No range attacks
*/
	const string ZOMBIE_MODEL = "models/sc_psyko/lightgreenslime.mdl";
    const int iHealth = 100;
	const float YAW_SPEED = 120;
	const float ZOMBIE_CLIMB_MAX_HEIGHT = 130 + VEC_HUMAN_HULL_MAX.z; // z = 72
	const float ZOMBIE_CLIMB_FORWARD_STEP = 40;
	const int ZOMBIE_FLINCH_DELAY = 2;
	const int ZOMBIE_AE_ATTACK_RIGHT = 1;
	const int ZOMBIE_AE_ATTACK_LEFT = 2;
	const int ZOMBIE_AE_ATTACK_BOTH = 3;
	const int g_iHealth = int(g_EngineFuncs.CVarGetFloat( "sk_zombie_health" ));
	const int g_iOneSlash = int(g_EngineFuncs.CVarGetFloat( "sk_zombie_dmg_one_slash" ));
	const int g_iBothSlash = int(g_EngineFuncs.CVarGetFloat( "sk_zombie_dmg_both_slash" ));

	class CMonsterSlime : ScriptBaseMonsterEntity
	{
	
		private int m_iSoundVolume = 1;
		private	int m_iVoicePitch = PITCH_NORM;	
		private float m_flNextFlinch;	
		private float m_flClimbProgress = 0;
		private Vector m_vecClimbPos;
		private string m_sCLimbSequence;
		
		//
		private int m_saved_movetype;
		private int m_saved_solid;
		private int m_saved_effects;
		
		
		void Spawn()
		{
		
			Precache( );
			
			g_EntityFuncs.SetModel(self, ZOMBIE_MODEL);
			g_EntityFuncs.SetSize(pev, Vector (VEC_HUMAN_HULL_MIN), Vector (32,32,72));
			if (self.pev.scale == 4) g_EntityFuncs.SetSize(pev, Vector (VEC_HUMAN_HULL_MIN), Vector (80,80,72));
			if (self.pev.scale == 3) g_EntityFuncs.SetSize(pev, Vector (VEC_HUMAN_HULL_MIN), Vector (64,64,72));
			if (self.pev.scale == 2) g_EntityFuncs.SetSize(pev, Vector (VEC_HUMAN_HULL_MIN), Vector (48,48,72));
						
			pev.solid			        = SOLID_SLIDEBOX;
			pev.movetype		        = MOVETYPE_STEP;
			self.m_bloodColor	        = BLOOD_COLOR_GREEN;
			if(pev.health<=0)
			{
				self.pev.health  = iHealth;
				if (self.pev.scale == 4) self.pev.health  = 300;
				if (self.pev.scale == 3) self.pev.health  = 200;
				if (self.pev.scale == 2) self.pev.health  = 150;
			}
			pev.view_ofs		        = VEC_VIEW;
			
			self.m_flFieldOfView        = 0.5;
			self.m_MonsterState		    = MONSTERSTATE_NONE;
			self.m_afCapability			= bits_CAP_DOORS_GROUP;
			self.m_FormattedName		= "Zombie";
			m_flClimbProgress		= 0;

			self.MonsterInit();
			
		}
		string RANDOM_SOUND_ARRAY(array<string> ary)
		{
			int i = Math.RandomLong(0,ary.length() - 1);
			return ary[i];
		}
		void PainSound( )
		{
			int pitch = 95 + Math.RandomLong(0,9);

			if (Math.RandomLong(0,5) < 2)
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), m_iSoundVolume, ATTN_NORM, 0, pitch );
		
		}
		void AlertSound( )
		{
			int pitch = 95 + Math.RandomLong(0,9);
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), m_iSoundVolume, ATTN_NORM, 0, pitch );
		}		
		void IdleSound( )
		{
			int pitch = 100 + Math.RandomLong(-5,5);
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), m_iSoundVolume, ATTN_NORM, 0, pitch );
		}		
		void AttackSound( )
		{
			int pitch = 100 + Math.RandomLong(-5,5);
			g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), m_iSoundVolume, ATTN_NORM, 0, pitch );
		}

		void SetYawSpeed ( )
		{
			pev.yaw_speed = YAW_SPEED;
		}
		int	Classify ()
		{
			return	CLASS_ALIEN_MONSTER;
		}
		void Precache()
		{
			//BaseClass.Precache();
			g_Game.PrecacheModel(ZOMBIE_MODEL);
			for(uint i = 0; i < pAttackHitSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pAttackHitSounds[i]);}	
			for(uint i = 0; i < pAttackMissSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pAttackMissSounds[i]);}			
			for(uint i = 0; i < pAttackSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pAttackSounds[i]);}			
			for(uint i = 0; i < pIdleSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pIdleSounds[i]);}			
			for(uint i = 0; i < pAlertSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pAlertSounds[i]);}			
			for(uint i = 0; i < pPainSounds.length();i++)
			{g_SoundSystem.PrecacheSound(pPainSounds[i]);}
			
		}	
		int IgnoreConditions ( )
		{
			int iIgnore = 0;
			
			if ((self.m_Activity == ACT_MELEE_ATTACK1) || (self.m_Activity == ACT_MELEE_ATTACK1))
			{	
				if (m_flNextFlinch >= g_Engine.time)
					iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE);
			}

			if ((self.m_Activity == ACT_SMALL_FLINCH) || (self.m_Activity == ACT_BIG_FLINCH))
			{
				if (m_flNextFlinch < g_Engine.time)
					m_flNextFlinch = g_Engine.time + ZOMBIE_FLINCH_DELAY;
			}

			return iIgnore;
			
		}
		
		bool CheckRangeAttack1 ( float flDot, float flDist )
		{ 
		return false;
		}
		bool CheckRangeAttack2 ( float flDot, float flDist )
		{ 
		return false;
		}
		int TakeDamage( entvars_t@ pevInflictor, entvars_t@ pevAttacker, float flDamage, int bitsDamageType )
		{
			// Take 60% damage from bullets
			if ( bitsDamageType == DMG_BULLET )
			{
				
				Vector vecDir = pev.origin - (pevInflictor.absmin + pevInflictor.absmax) * 0.5;
				vecDir = vecDir.Normalize();
				float flForce = self.DamageForce( flDamage );
				pev.velocity = pev.velocity + vecDir * flForce;
				flDamage *= 0.6;
			}
			
			// HACK HACK -- until we fix this.
			if ( self.IsAlive() )
				self.PainSound();
				
			if (self.pev.health <= 0) 
			{
			self.Killed(pevAttacker, GIB_NORMAL);
			return 0;
			}
						
			/*if (self.pev.health <= 0) 
			{
				if (self.pev.scale == 2) 
				{
				CBaseEntity@ newslime = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime.pev.origin = self.pev.origin;
				newslime.pev.targetname = "slime1";
				CBaseEntity@ newslime2 = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime2.pev.origin = self.pev.origin + Vector (0 , 0 , 30);
				newslime2.pev.targetname = "slime2";
				}
			}*/
			return BaseClass.TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
		}
		
		void Killed(entvars_t@ pevAttacker, int iGibbed)
		{
			//MonsterKilled(pevAttacker, iGibbed);
			
			CBaseEntity@ pAttacker = g_EntityFuncs.Instance(pevAttacker);
			if (pAttacker !is null) pAttacker.AddPoints(1, false);

			m_iAIState = STATE_DEAD;

			self.pev.takedamage = DAMAGE_NO;
			self.pev.deadflag = DEAD_DEAD;
			self.pev.solid = SOLID_NOT;
			
			classlevels_file_array[0] = classlevels_file_array[0] + 3;
			ClasslevelSet(classlevels_file_array[0]);
			ClasslevelWriteLineToFile();

			if (!m_hEnemy) m_hEnemy = pAttacker;
			MonsterDeathUse(m_hEnemy, self, USE_TOGGLE, 0.0);
			
			if (self.pev.health <= 0) 
			{
				if (self.pev.scale == 4) 
				{
				g_EntityFuncs.Remove( self );
				CBaseEntity@ newslime = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime.pev.origin = self.pev.origin;
				newslime.pev.size = Vector (80,80,72);
				newslime.pev.targetname = "slime1";
				newslime.pev.scale = 3;
				g_EntityFuncs.DispatchSpawn( newslime.edict() );
				CBaseEntity@ newslime2 = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime2.pev.origin = self.pev.origin + Vector (0 , 0 , 80);
				newslime2.pev.size = Vector (80,80,72);
				newslime2.pev.targetname = "slime2";
				newslime2.pev.scale = 3;
				g_EntityFuncs.DispatchSpawn( newslime2.edict() );
				}
				if (self.pev.scale == 3) 
				{
				g_EntityFuncs.Remove( self );
				CBaseEntity@ newslime = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime.pev.origin = self.pev.origin;
				newslime.pev.size = Vector (64,64,72);
				newslime.pev.targetname = "slime1";
				newslime.pev.scale = 2;
				g_EntityFuncs.DispatchSpawn( newslime.edict() );
				CBaseEntity@ newslime2 = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime2.pev.origin = self.pev.origin + Vector (0 , 0 , 80);
				newslime2.pev.size = Vector (64,64,72);
				newslime2.pev.targetname = "slime2";
				newslime2.pev.scale = 2;
				g_EntityFuncs.DispatchSpawn( newslime2.edict() );
				}
				if (self.pev.scale == 2) 
				{
				g_EntityFuncs.Remove( self );
				CBaseEntity@ newslime = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime.pev.origin = self.pev.origin;
				newslime.pev.size = Vector (48,48,72);
				newslime.pev.targetname = "slime1";
				g_EntityFuncs.DispatchSpawn( newslime.edict() );
				CBaseEntity@ newslime2 = g_EntityFuncs.CreateEntity( "monster_slime", null,  false);
				newslime2.pev.origin = self.pev.origin + Vector (0 , 0 , 80);
				newslime2.pev.size = Vector (48,48,72);
				newslime2.pev.targetname = "slime2";
				g_EntityFuncs.DispatchSpawn( newslime2.edict() );
				}
				
				if (self.pev.scale == 1) 
				{
				SetActivity(ACT_DIESIMPLE);
				g_EntityFuncs.Remove( self );
				//SetThink(ThinkFunction(this.DeadThink));
				}
			}
		}	
		
		/*void DeadThink()
		{
			if (self.m_fSequenceFinished){g_EntityFuncs.Remove( self );}
			self.pev.nextthink = g_Engine.time + 10.0;
		}*/
		
		void SetActivity(Activity newActivity) 
		{
			int iSeq = self.LookupActivity(newActivity);
			if (iSeq > -1) {
			if (self.pev.sequence != iSeq || !self.m_fSequenceLoops)
			if (!(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(newActivity == ACT_WALK || newActivity == ACT_RUN))
			self.pev.frame = 0;
			self.pev.sequence = iSeq;
			self.ResetSequenceInfo();
			} 
			else 
		{
			self.pev.sequence = 0;
		}
			m_Activity = newActivity;
			m_IdealActivity = newActivity;
		}
		
		void MonsterDeathUse(CBaseEntity@ pActivator, CBaseEntity@ pCaller, USE_TYPE useType, float value)
		{
		self.pev.flags &= ~(FL_FLY | FL_SWIM);
		if (self.pev.target == "") return;
		self.SUB_UseTargets(pActivator, useType, value);
		}
		
		//void MonsterKilled(entvars_t@ pevAttacker, int iGib) {}
		
		void HandleAnimEvent( MonsterEvent@ pEvent )
		{
			switch( pEvent.event )
			{
				case ZOMBIE_AE_ATTACK_RIGHT:
				{
					CBaseEntity@ pHurt = CheckTraceHullAttack(self, 70, g_iOneSlash, DMG_SLASH );
					
					if ( pHurt !is null )
					{
					
						if ( pHurt.pev.flags & ( FL_MONSTER | FL_CLIENT ) != 0 )
						{
							pHurt.pev.punchangle.z = -18;
							pHurt.pev.punchangle.x = 5;
							pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * 100;
						}
						// Play a random attack hit sound
						g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					else
					{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					
					if(Math.RandomLong(0,1)>0)
					{
					AttackSound();
					}
				}
				break;				
				case ZOMBIE_AE_ATTACK_LEFT:
				{
					CBaseEntity@ pHurt = CheckTraceHullAttack(self, 70, g_iOneSlash, DMG_SLASH );
					
					if ( pHurt !is null )
					{
					
						if ( pHurt.pev.flags & ( FL_MONSTER | FL_CLIENT ) != 0 )
						{
							pHurt.pev.punchangle.z = 18;
							pHurt.pev.punchangle.x = 5;
							pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * 100;
						}
						// Play a random attack hit sound
						g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					else
					{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					
					if(Math.RandomLong(0,1)>0)
					{
					AttackSound();
					}
				}
				break;
				case ZOMBIE_AE_ATTACK_BOTH:
				{
					CBaseEntity@ pHurt = CheckTraceHullAttack(self, 70, g_iBothSlash, DMG_SLASH );
					
					if ( pHurt !is null )
					{
					
						if ( pHurt.pev.flags & ( FL_MONSTER | FL_CLIENT ) != 0 )
						{
							pHurt.pev.punchangle.x = 5;
							pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * 100;
						}
						// Play a random attack hit sound
						g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					else
					{
					g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), m_iSoundVolume, ATTN_NORM, 0,  100 + Math.RandomLong(-5,5) );
					}
					
					if(Math.RandomLong(0,1)>0)
					{
					AttackSound();
					}
				}
				break;
				
				default:
					BaseClass.HandleAnimEvent( pEvent );
					break;
			}
		}
		void RouteNew()
		{
			self.m_Route(0).iType		= 0;
			self.m_iRouteIndex	= 0;
		}
		void MoveToLocation(Activity movementAct, float waitTime, Vector goal){
			self.m_movementActivity = movementAct;
			self.m_moveWaitTime = waitTime;
			
			//self.m_movementGoal = bits_MF_TO_LOCATION;
			self.m_vecMoveGoal = goal;
			self.m_vecEnemyLKP = goal;
			
			RouteNew();
		}
		bool WalkCheck(Vector pos,Vector dir,float flDist){
			Vector lastPos = pev.origin;
			g_EntityFuncs.SetOrigin (self, pos);
			int oldType = pev.movetype;
			int result = g_EngineFuncs.WalkMove(self.edict(), g_EngineFuncs.VecToYaw(dir), flDist, WALKMOVE_CHECKONLY);
			g_EntityFuncs.SetOrigin (self, lastPos);
			return result == 0 ? true : false;
		}

		/*
		Fire a Vector to target, try to reach edge - goodman3
		*/
		void MoveToEdge(bool detectEnemy){
			Vector dir;
			if(detectEnemy && self.m_hEnemy.IsValid()){
				dir = self.m_hEnemy.GetEntity().pev.origin;
				dir.z = pev.origin.z;
			} else {	
				dir =  pev.origin + g_Engine.v_forward * 6000;
			}
			TraceResult tr;
			g_Utility.TraceLine( pev.origin, dir, ignore_monsters, self.edict(), tr );
			Vector vecEndPos;
			if( tr.flFraction < 1.0 )
			{
				if( tr.pHit !is null )
				{
					CBaseEntity@ pHit = g_EntityFuncs.Instance( tr.pHit );
					vecEndPos = pHit.pev.origin;
					if( pHit is null || pHit.IsBSPModel() )
						vecEndPos = tr.vecEndPos;
				}
			} else {
				vecEndPos =  dir;
			}
			vecEndPos = vecEndPos - g_Engine.v_forward * ZOMBIE_CLIMB_FORWARD_STEP/2;
			MoveToLocation(ACT_WALK, 0, vecEndPos);
		}
	}
		
	void Registermonster_slime()
	{
		g_CustomEntityFuncs.RegisterCustomEntity( "MonsterSlime::CMonsterSlime", "monster_slime" );
	}
}