//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A slow-moving, once-human headcrab victim with only melee attacks.
//
// UNDONE: Make head take 100% damage, body take 30% damage.
// UNDONE: Don't flinch every time you get hit.
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "game.h"
#include "ai_default.h"
#include "ai_schedule.h"
#include "ai_hull.h"
#include "ai_route.h"
#include "npcevent.h"
#include "hl1_npc_zombie.h"
#include "gib.h"
//#include "AI_Interactions.h"
#include "ndebugoverlay.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"

ConVar	sk_zombie_health( "sk_zombie_health","50");
ConVar  sk_zombie_dmg_one_slash( "sk_zombie_dmg_one_slash", "20" );
ConVar  sk_zombie_dmg_both_slash( "sk_zombie_dmg_both_slash", "40" );



LINK_ENTITY_TO_CLASS( monster_zombie, CNPC_Zombie );


//=========================================================
// Spawn
//=========================================================
void CNPC_Zombie::Spawn()
{
	Precache( );

	SetModel( "models/zombie.mdl" );
	
	SetRenderColor( 255, 255, 255, 255 );
	
	SetHullType(HULL_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	m_bloodColor		= BLOOD_COLOR_GREEN;
    m_iHealth			= sk_zombie_health.GetFloat();
	//pev->view_ofs		= VEC_VIEW;// position of the eyes relative to monster's origin.
	m_flFieldOfView		= 0.5;
	m_NPCState			= NPC_STATE_NONE;
	CapabilitiesClear();
	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP );

	NPCInit();
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Zombie::Precache()
{
	PrecacheModel( "models/zombie.mdl" );

	PrecacheScriptSound( "Zombie.AttackHit" );
	PrecacheScriptSound( "Zombie.AttackMiss" );
	PrecacheScriptSound( "Zombie.Pain" );
	PrecacheScriptSound( "Zombie.Idle" );
	PrecacheScriptSound( "Zombie.Alert" );
	PrecacheScriptSound( "Zombie.Attack" );

	BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Purpose: Returns this monster's place in the relationship table.
//-----------------------------------------------------------------------------
Class_T	CNPC_Zombie::Classify( void )
{
	return CLASS_ALIEN_MONSTER; 
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Zombie::HandleAnimEvent( animevent_t *pEvent )
{
	Vector v_forward, v_right;
	switch( pEvent->event )
	{
		case ZOMBIE_AE_ATTACK_RIGHT:
		{
			// do stuff for this event.
	//		ALERT( at_console, "Slash right!\n" );

			Vector vecMins = GetHullMins();
			Vector vecMaxs = GetHullMaxs();
			vecMins.z = vecMins.x;
			vecMaxs.z = vecMaxs.x;

			CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
			CPASAttenuationFilter filter( this );
			if ( pHurt )
			{
				if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
				{
					pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
					
					GetVectors( &v_forward, &v_right, NULL );

					pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
				}
				// Play a random attack hit sound
				EmitSound( filter, entindex(), "Zombie.AttackHit" );
			}
			else // Play a random attack miss sound
				EmitSound( filter, entindex(), "Zombie.AttackMiss" );

			if ( random->RandomInt( 0, 1 ) )
				 AttackSound();
		}
		break;

		case ZOMBIE_AE_ATTACK_LEFT:
		{
			// do stuff for this event.
	//		ALERT( at_console, "Slash left!\n" );
			Vector vecMins = GetHullMins();
			Vector vecMaxs = GetHullMaxs();
			vecMins.z = vecMins.x;
			vecMaxs.z = vecMaxs.x;

			CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
			
			CPASAttenuationFilter filter2( this );
			if ( pHurt )
			{
				if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
				{
					pHurt->ViewPunch( QAngle ( 5, 0, -18 ) );
					
					GetVectors( &v_forward, &v_right, NULL );

					pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
				}
				EmitSound( filter2, entindex(), "Zombie.AttackHit" );
			}
			else
			{
				EmitSound( filter2, entindex(), "Zombie.AttackMiss" );
			}

			if ( random->RandomInt( 0,1 ) ) 
				 AttackSound();
		}
		break;

		case ZOMBIE_AE_ATTACK_BOTH:
		{
			// do stuff for this event.
			Vector vecMins = GetHullMins();
			Vector vecMaxs = GetHullMaxs();
			vecMins.z = vecMins.x;
			vecMaxs.z = vecMaxs.x;

			CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_both_slash.GetFloat(), DMG_SLASH );
			

			CPASAttenuationFilter filter3( this );
			if ( pHurt )
			{
				if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
				{
					pHurt->ViewPunch( QAngle ( 5, 0, 0 ) );
					
					GetVectors( &v_forward, &v_right, NULL );
					pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
				}
				EmitSound( filter3, entindex(), "Zombie.AttackHit" );
			}
			else
				EmitSound( filter3, entindex(),"Zombie.AttackMiss" );

			if ( random->RandomInt( 0,1 ) )
				 AttackSound();
		}
		break;

		default:
			BaseClass::HandleAnimEvent( pEvent );
			break;
	}
}


static float DamageForce( const Vector &size, float damage )
{ 
	float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5;
	
	if ( force > 1000.0) 
	{
		force = 1000.0;
	}

	return force;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pInflictor - 
//			pAttacker - 
//			flDamage - 
//			bitsDamageType - 
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Zombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
	CTakeDamageInfo info = inputInfo;

	// Take 30% damage from bullets
	if ( info.GetDamageType() == DMG_BULLET )
	{
		Vector vecDir = GetAbsOrigin() - info.GetInflictor()->WorldSpaceCenter();
		VectorNormalize( vecDir );
		float flForce = DamageForce( WorldAlignSize(), info.GetDamage() );
		SetAbsVelocity( GetAbsVelocity() + vecDir * flForce );
		info.ScaleDamage( 0.3f );
	}

	// HACK HACK -- until we fix this.
	if ( IsAlive() )
		 PainSound( info );
	
	return BaseClass::OnTakeDamage_Alive( info );
}

void CNPC_Zombie::PainSound( const CTakeDamageInfo &info )
{
	if ( random->RandomInt(0,5) < 2)
	{
		CPASAttenuationFilter filter( this );
		EmitSound( filter, entindex(), "Zombie.Pain" );
	}
}

void CNPC_Zombie::AlertSound( void )
{
	CPASAttenuationFilter filter( this );
	EmitSound( filter, entindex(), "Zombie.Alert" );
}

void CNPC_Zombie::IdleSound( void )
{
	// Play a random idle sound
	CPASAttenuationFilter filter( this );
	EmitSound( filter, entindex(), "Zombie.Idle" );
}

void CNPC_Zombie::AttackSound( void )
{
	// Play a random attack sound
	CPASAttenuationFilter filter( this );
	EmitSound( filter, entindex(), "Zombie.Attack" );
}

int CNPC_Zombie::MeleeAttack1Conditions ( float flDot, float flDist )
{
	if ( flDist > 64)
	{
		return COND_TOO_FAR_TO_ATTACK;
	}
	else if (flDot < 0.7)
	{
		return 0;
	}
	else if (GetEnemy() == NULL)
	{
		return 0;
	}

	return COND_CAN_MELEE_ATTACK1;
}

void CNPC_Zombie::RemoveIgnoredConditions ( void )
{
	if ( GetActivity() == ACT_MELEE_ATTACK1 )
	{
		// Nothing stops an attacking zombie.
		ClearCondition( COND_LIGHT_DAMAGE );
		ClearCondition( COND_HEAVY_DAMAGE );
	}

	if (( GetActivity() == ACT_SMALL_FLINCH ) || ( GetActivity() == ACT_BIG_FLINCH ))
	{
		if (m_flNextFlinch < gpGlobals->curtime)
			m_flNextFlinch = gpGlobals->curtime + ZOMBIE_FLINCH_DELAY;
	}

	BaseClass::RemoveIgnoredConditions();
}