//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Combine Zombie... Zombie Combine... its like a... Zombine... get it?
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "ai_basenpc.h"
#include "ai_default.h"
#include "ai_schedule.h"
#include "ai_hull.h"
#include "ai_motor.h"
#include "ai_memory.h"
#include "ai_route.h"
#include "ai_squad.h"
#include "soundent.h"
#include "game.h"
#include "npcevent.h"
#include "entitylist.h"
#include "ai_task.h"
#include "activitylist.h"
#include "engine/IEngineSound.h"
#include "npc_BaseZombie.h"
#include "movevars_shared.h"
#include "IEffects.h"
#include "props.h"
#include "physics_npc_solver.h"
#include "hl2_player.h"
#include "hl2_gamerules.h"

#include "basecombatweapon.h"
#include "basegrenade_shared.h"
#include "grenade_frag.h"

#include "ai_interactions.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

enum
{	
	SQUAD_SLOT_ZOMBINE_SPRINT1 = LAST_SHARED_SQUADSLOT,
	SQUAD_SLOT_ZOMBINE_SPRINT2,
};

#define MIN_SPRINT_TIME 3.5f
#define MAX_SPRINT_TIME 5.5f

#define MIN_SPRINT_DISTANCE 64.0f
#define MAX_SPRINT_DISTANCE 1024.0f

#define SPRINT_CHANCE_VALUE 10
#define SPRINT_CHANCE_VALUE_DARKNESS 50

#define GRENADE_PULL_MAX_DISTANCE 256.0f

#define ZOMBINE_MAX_GRENADES 1

int ACT_ZOMBINE_GRENADE_PULL;
int ACT_ZOMBINE_GRENADE_WALK;
int ACT_ZOMBINE_GRENADE_RUN;
int ACT_ZOMBINE_GRENADE_IDLE;
int ACT_ZOMBINE_ATTACK_FAST;
int ACT_ZOMBINE_GRENADE_FLINCH_BACK;
int ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
int ACT_ZOMBINE_GRENADE_FLINCH_WEST;
int ACT_ZOMBINE_GRENADE_FLINCH_EAST;

int AE_ZOMBINE_PULLPIN;

extern bool IsAlyxInDarknessMode();

ConVar	sk_zombie_soldier_health( "sk_zombie_soldier_health","0");

float g_flZombineGrenadeTimes = 0;

class CNPC_Zombine : public CAI_BlendingHost<CNPC_BaseZombie>, public CDefaultPlayerPickupVPhysics
{
	DECLARE_DATADESC();
	DECLARE_CLASS( CNPC_Zombine, CAI_BlendingHost<CNPC_BaseZombie> );

public:

	void Spawn( void );
	void Precache( void );

	void SetZombieModel( void );

	virtual void PrescheduleThink( void );
	virtual int SelectSchedule( void );
	virtual void BuildScheduleTestBits( void );

	virtual void HandleAnimEvent( animevent_t *pEvent );

	virtual const char *GetLegsModel( void );
	virtual const char *GetTorsoModel( void );
	virtual const char *GetHeadcrabClassname( void );
	virtual const char *GetHeadcrabModel( void );

	virtual void PainSound( const CTakeDamageInfo &info );
	virtual void DeathSound( const CTakeDamageInfo &info );
	virtual void AlertSound( void );
	virtual void IdleSound( void );
	virtual void AttackSound( void );
	virtual void AttackHitSound( void );
	virtual void AttackMissSound( void );
	virtual void FootstepSound( bool fRightFoot );
	virtual void FootscuffSound( bool fRightFoot );
	virtual void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize );

	virtual void Event_Killed( const CTakeDamageInfo &info );
	virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
	virtual void RunTask( const Task_t *pTask );
	virtual int  MeleeAttack1Conditions ( float flDot, float flDist );

	virtual bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold );

	virtual void OnScheduleChange ( void );
	virtual bool CanRunAScriptedNPCInteraction( bool bForced );

	void GatherGrenadeConditions( void );

	virtual Activity NPC_TranslateActivity( Activity baseAct );

	const char *GetMoanSound( int nSound );

	bool AllowedToSprint( void );
	void Sprint( bool bMadSprint = false );
	void StopSprint( void );

	void DropGrenade( Vector vDir );

	bool IsSprinting( void ) { return m_flSprintTime > gpGlobals->curtime;	}
	bool HasGrenade( void ) { return m_hGrenade != NULL; }

	int TranslateSchedule( int scheduleType );

	void InputStartSprint ( inputdata_t &inputdata );
	void InputPullGrenade ( inputdata_t &inputdata );

	virtual CBaseEntity *OnFailedPhysGunPickup ( Vector vPhysgunPos );

	//Called when we want to let go of a grenade and let the physcannon pick it up.
	void ReleaseGrenade( Vector vPhysgunPos );

	virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt );

	enum
	{
		COND_ZOMBINE_GRENADE = LAST_BASE_ZOMBIE_CONDITION,
	};

	enum
	{
		SCHED_ZOMBINE_PULL_GRENADE = LAST_BASE_ZOMBIE_SCHEDULE,
	};

public:
	DEFINE_CUSTOM_AI;

private:

	float	m_flSprintTime;
	float	m_flSprintRestTime;

	float	m_flSuperFastAttackTime;
	float   m_flGrenadePullTime;
	
	int		m_iGrenadeCount;

	EHANDLE	m_hGrenade;

protected:
	static const char *pMoanSounds[];

};

LINK_ENTITY_TO_CLASS( npc_zombine, CNPC_Zombine );

BEGIN_DATADESC( CNPC_Zombine )
	DEFINE_FIELD( m_flSprintTime, FIELD_TIME ),
	DEFINE_FIELD( m_flSprintRestTime, FIELD_TIME ),
	DEFINE_FIELD( m_flSuperFastAttackTime, FIELD_TIME ),
	DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ),
	DEFINE_FIELD( m_flGrenadePullTime, FIELD_TIME ),
	DEFINE_FIELD( m_iGrenadeCount, FIELD_INTEGER ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"StartSprint", InputStartSprint ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"PullGrenade", InputPullGrenade ),
END_DATADESC()

//---------------------------------------------------------
//---------------------------------------------------------
const char *CNPC_Zombine::pMoanSounds[] =
{
	"ATV_engine_null",
};

void CNPC_Zombine::Spawn( void )
{
	Precache();

	m_fIsTorso = false;
	m_fIsHeadless = false;
	
#ifdef HL2_EPISODIC
	SetBloodColor( BLOOD_COLOR_ZOMBIE );
#else
	SetBloodColor( BLOOD_COLOR_GREEN );
#endif // HL2_EPISODIC

	m_iHealth			= sk_zombie_soldier_health.GetFloat();
	SetMaxHealth( m_iHealth );

	m_flFieldOfView		= 0.2;

	CapabilitiesClear();

	BaseClass::Spawn();

	m_flSprintTime = 0.0f;
	m_flSprintRestTime = 0.0f;

	m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 4.0 );

	g_flZombineGrenadeTimes = gpGlobals->curtime;
	m_flGrenadePullTime = gpGlobals->curtime;

	m_iGrenadeCount = ZOMBINE_MAX_GRENADES;
}

void CNPC_Zombine::Precache( void )
{
	BaseClass::Precache();

	PrecacheModel( "models/zombie/zombie_soldier.mdl" );

	PrecacheScriptSound( "Zombie.FootstepRight" );
	PrecacheScriptSound( "Zombie.FootstepLeft" );
	PrecacheScriptSound( "Zombine.ScuffRight" );
	PrecacheScriptSound( "Zombine.ScuffLeft" );
	PrecacheScriptSound( "Zombie.AttackHit" );
	PrecacheScriptSound( "Zombie.AttackMiss" );
	PrecacheScriptSound( "Zombine.Pain" );
	PrecacheScriptSound( "Zombine.Die" );
	PrecacheScriptSound( "Zombine.Alert" );
	PrecacheScriptSound( "Zombine.Idle" );
	PrecacheScriptSound( "Zombine.ReadyGrenade" );

	PrecacheScriptSound( "ATV_engine_null" );
	PrecacheScriptSound( "Zombine.Charge" );
	PrecacheScriptSound( "Zombie.Attack" );
}

void CNPC_Zombine::SetZombieModel( void )
{
	SetModel( "models/zombie/zombie_soldier.mdl" );
	SetHullType( HULL_HUMAN );

	SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );

	SetHullSizeNormal( true );
	SetDefaultEyeOffset();
	SetActivity( ACT_IDLE );
}

void CNPC_Zombine::PrescheduleThink( void )
{
	GatherGrenadeConditions();

	if( gpGlobals->curtime > m_flNextMoanSound )
	{
		if( CanPlayMoanSound() )
		{
			// Classic guy idles instead of moans.
			IdleSound();

			m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 10.0, 15.0 );
		}
		else
		{
			m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.5, 5.0 );
		}
	}

	if ( HasGrenade () )
	{
		CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetSmoothedVelocity() * 0.5f , 256, 0.1, this, SOUNDENT_CHANNEL_ZOMBINE_GRENADE );

		if( IsSprinting() && GetEnemy() && GetEnemy()->Classify() == CLASS_PLAYER_ALLY_VITAL && HasCondition( COND_SEE_ENEMY ) )
		{
			if( GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin()) < Square( 144 ) )
			{
				StopSprint();
			}
		}
	}

	BaseClass::PrescheduleThink();
}

void CNPC_Zombine::OnScheduleChange( void )
{
	if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) && IsSprinting() == true )
	{
		m_flSuperFastAttackTime = gpGlobals->curtime + 1.0f;
	}

	BaseClass::OnScheduleChange();
}
bool CNPC_Zombine::CanRunAScriptedNPCInteraction( bool bForced )
{
	if ( HasGrenade() == true )
		return false;

	return BaseClass::CanRunAScriptedNPCInteraction( bForced );
}

int CNPC_Zombine::SelectSchedule( void )
{
	if ( GetHealth() <= 0 )
		return BaseClass::SelectSchedule();

	if ( HasCondition( COND_ZOMBINE_GRENADE ) )
	{
		ClearCondition( COND_ZOMBINE_GRENADE );
		
		return SCHED_ZOMBINE_PULL_GRENADE;
	}

	return BaseClass::SelectSchedule();
}

void CNPC_Zombine::BuildScheduleTestBits( void )
{
	BaseClass::BuildScheduleTestBits();

	SetCustomInterruptCondition( COND_ZOMBINE_GRENADE );
}

Activity CNPC_Zombine::NPC_TranslateActivity( Activity baseAct )
{
	if ( baseAct == ACT_MELEE_ATTACK1 )
	{
		if ( m_flSuperFastAttackTime > gpGlobals->curtime || HasGrenade() )
		{
			return (Activity)ACT_ZOMBINE_ATTACK_FAST;
		}
	}

	if ( baseAct == ACT_IDLE )
	{
		if ( HasGrenade() )
		{
			return (Activity)ACT_ZOMBINE_GRENADE_IDLE;
		}
	}

	return BaseClass::NPC_TranslateActivity( baseAct );
}

int CNPC_Zombine::MeleeAttack1Conditions ( float flDot, float flDist )
{
	int iBase = BaseClass::MeleeAttack1Conditions( flDot, flDist );

	if( HasGrenade() )
	{
		//Adrian: stop spriting if we get close enough to melee and we have a grenade
		//this gives NPCs time to move away from you (before it was almost impossible cause of the high sprint speed)
		if ( iBase == COND_CAN_MELEE_ATTACK1 )
		{
			StopSprint();
		}
	}

	return iBase;
}

void CNPC_Zombine::GatherGrenadeConditions( void )
{
	if ( m_iGrenadeCount <= 0 )
		return;

	if ( g_flZombineGrenadeTimes > gpGlobals->curtime )
		return;

	if ( m_flGrenadePullTime > gpGlobals->curtime )
		return;

	if ( m_flSuperFastAttackTime >= gpGlobals->curtime )
		return;
	
	if ( HasGrenade() )
		return;

	if ( GetEnemy() == NULL )
		return;

	if ( FVisible( GetEnemy() ) == false )
		return;

	if ( IsSprinting() )
		return;

	if ( IsOnFire() )
		return;
	
	if ( IsRunningDynamicInteraction() == true )
		return;

	if ( m_ActBusyBehavior.IsActive() )
		return;

	CBasePlayer *pPlayer = AI_GetSinglePlayer();

	if ( pPlayer && pPlayer->FVisible( this ) )
	{
		float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length();
		float flLengthToEnemy = flLengthToPlayer;

		if ( pPlayer != GetEnemy() )
		{
			flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length();
		}

		if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE )
		{
			float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE );
			m_flGrenadePullTime = gpGlobals->curtime + 0.5f;

			if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) )
			{
				g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f;
				SetCondition( COND_ZOMBINE_GRENADE );
			}
		}
	}
}

int CNPC_Zombine::TranslateSchedule( int scheduleType ) 
{
	return BaseClass::TranslateSchedule( scheduleType );
}

void CNPC_Zombine::DropGrenade( Vector vDir )
{
	if ( m_hGrenade == NULL )
		 return;

	m_hGrenade->SetParent( NULL );
	m_hGrenade->SetOwnerEntity( NULL );

	Vector vGunPos;
	QAngle angles;
	GetAttachment( "grenade_attachment", vGunPos, angles );

	IPhysicsObject *pPhysObj = m_hGrenade->VPhysicsGetObject();

	if ( pPhysObj == NULL )
	{
		m_hGrenade->SetMoveType( MOVETYPE_VPHYSICS );
		m_hGrenade->SetSolid( SOLID_VPHYSICS );
		m_hGrenade->SetCollisionGroup( COLLISION_GROUP_WEAPON );

		m_hGrenade->CreateVPhysics();
	}

	if ( pPhysObj )
	{
		pPhysObj->Wake();
		pPhysObj->SetPosition( vGunPos, angles, true );
		pPhysObj->ApplyForceCenter( vDir * 0.2f );

		pPhysObj->RecheckCollisionFilter();
	}

	m_hGrenade = NULL;
}

void CNPC_Zombine::Event_Killed( const CTakeDamageInfo &info )
{
	BaseClass::Event_Killed( info );

	if ( HasGrenade() )
	{
		DropGrenade( vec3_origin );
	}
}

//-----------------------------------------------------------------------------
// Purpose:  This is a generic function (to be implemented by sub-classes) to
//			 handle specific interactions between different types of characters
//			 (For example the barnacle grabbing an NPC)
// Input  :  Constant for the type of interaction
// Output :	 true  - if sub-class has a response for the interaction
//			 false - if sub-class has no response
//-----------------------------------------------------------------------------
bool CNPC_Zombine::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
{
	if ( interactionType == g_interactionBarnacleVictimGrab )
	{
		if ( HasGrenade() )
		{
			DropGrenade( vec3_origin );
		}
	}

	return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
}

void CNPC_Zombine::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
{
	BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );

	//Only knock grenades off their hands if it's a player doing the damage.
	if ( info.GetAttacker() && info.GetAttacker()->IsNPC() )
		return;

	if ( info.GetDamageType() & ( DMG_BULLET | DMG_CLUB ) )
	{
		if ( ptr->hitgroup == HITGROUP_LEFTARM )
		{
			if ( HasGrenade() )
			{
				DropGrenade( info.GetDamageForce() );
				StopSprint();
			}
		}
	}
}

void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_ZOMBINE_PULLPIN )
	{
		Vector vecStart;
		QAngle angles;
		GetAttachment( "grenade_attachment", vecStart, angles );

		CBaseGrenade *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vec3_origin, AngularImpulse( 0, 0, 0 ), this, 3.5f, true );

		if ( pGrenade )
		{
			// Move physobject to shadow
			IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();

			if ( pPhysicsObject )
			{
				pGrenade->VPhysicsDestroyObject();

				int iAttachment = LookupAttachment( "grenade_attachment");

				pGrenade->SetMoveType( MOVETYPE_NONE );
				pGrenade->SetSolid( SOLID_NONE );
				pGrenade->SetCollisionGroup( COLLISION_GROUP_DEBRIS );

				pGrenade->SetAbsOrigin( vecStart );
				pGrenade->SetAbsAngles( angles );

				pGrenade->SetParent( this, iAttachment );

				pGrenade->SetDamage( 200.0f );
				m_hGrenade = pGrenade;
				
				EmitSound( "Zombine.ReadyGrenade" );

				// Tell player allies nearby to regard me!
				CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
				CAI_BaseNPC *pNPC;
				for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
				{
					pNPC = ppAIs[i];

					if( pNPC->Classify() == CLASS_PLAYER_ALLY || pNPC->Classify() == CLASS_PLAYER_ALLY_VITAL && pNPC->FVisible(this) )
					{
						int priority;
						Disposition_t disposition;

						priority = pNPC->IRelationPriority(this);
						disposition = pNPC->IRelationType(this);

						pNPC->AddEntityRelationship( this, disposition, priority + 1 );
					}
				}
			}

			m_iGrenadeCount--;
		}

		return;
	}

	if ( pEvent->event == AE_NPC_ATTACK_BROADCAST )
	{
		if ( HasGrenade() )
			return;
	}

	BaseClass::HandleAnimEvent( pEvent );
}

bool CNPC_Zombine::AllowedToSprint( void )
{
	if ( IsOnFire() )
		return false;
	
	//If you're sprinting then there's no reason to sprint again.
	if ( IsSprinting() )
		return false;

	int iChance = SPRINT_CHANCE_VALUE;

	CHL2_Player *pPlayer = dynamic_cast <CHL2_Player*> ( AI_GetSinglePlayer() );

	if ( pPlayer )
	{
		if ( HL2GameRules()->IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false )
		{
			iChance = SPRINT_CHANCE_VALUE_DARKNESS;
		}

		//Bigger chance of this happening if the player is not looking at the zombie
		if ( pPlayer->FInViewCone( this ) == false )
		{
			iChance *= 2;
		}
	}

	if ( HasGrenade() ) 
	{
		iChance *= 4;
	}

	//Below 25% health they'll always sprint
	if ( ( GetHealth() > GetMaxHealth() * 0.5f ) )
	{
		if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ) == true )
			return false;
		
		if ( random->RandomInt( 0, 100 ) > iChance )
			return false;
		
		if ( m_flSprintRestTime > gpGlobals->curtime )
			return false;
	}

	float flLength = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length();

	if ( flLength > MAX_SPRINT_DISTANCE )
		return false;

	return true;
}

void CNPC_Zombine::StopSprint( void )
{
	GetNavigator()->SetMovementActivity( ACT_WALK );

	m_flSprintTime = gpGlobals->curtime;
	m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );
}

void CNPC_Zombine::Sprint( bool bMadSprint )
{
	if ( IsSprinting() )
		return;

	OccupyStrategySlotRange( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 );
	GetNavigator()->SetMovementActivity( ACT_RUN );

	float flSprintTime = random->RandomFloat( MIN_SPRINT_TIME, MAX_SPRINT_TIME );

	//If holding a grenade then sprint until it blows up.
	if ( HasGrenade() || bMadSprint == true )
	{
		flSprintTime = 9999;
	}

	m_flSprintTime = gpGlobals->curtime + flSprintTime;

	//Don't sprint for this long after I'm done with this sprint run.
	m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );

	EmitSound( "Zombine.Charge" );
}

void CNPC_Zombine::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
		case TASK_WAIT_FOR_MOVEMENT_STEP:
		case TASK_WAIT_FOR_MOVEMENT:
		{
			BaseClass::RunTask( pTask );

			if ( IsOnFire() && IsSprinting() )
			{
				StopSprint();
			}

			//Only do this if I have an enemy
			if ( GetEnemy() )
			{
				if ( AllowedToSprint() == true )
				{
					Sprint( ( GetHealth() <= GetMaxHealth() * 0.5f ) );
					return;
				}

				if ( HasGrenade() )
				{
					if ( IsSprinting() )
					{
						GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_RUN );
					}
					else
					{
						GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_WALK );
					}

					return;
				}

				if ( GetNavigator()->GetMovementActivity() != ACT_WALK )
				{
					if ( IsSprinting() == false )
					{
						GetNavigator()->SetMovementActivity( ACT_WALK );
					}
				}
			}
			else
			{
				GetNavigator()->SetMovementActivity( ACT_WALK );
			}
		
			break;
		}
		default:
		{
			BaseClass::RunTask( pTask );
			break;
		}
	}
}

void CNPC_Zombine::InputStartSprint ( inputdata_t &inputdata )
{
	Sprint();
}

void CNPC_Zombine::InputPullGrenade ( inputdata_t &inputdata )
{
	g_flZombineGrenadeTimes = gpGlobals->curtime + 5.0f;
	SetCondition( COND_ZOMBINE_GRENADE );
}

//-----------------------------------------------------------------------------
// Purpose: Returns a moan sound for this class of zombie.
//-----------------------------------------------------------------------------
const char *CNPC_Zombine::GetMoanSound( int nSound )
{
	return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ];
}

//-----------------------------------------------------------------------------
// Purpose: Sound of a footstep
//-----------------------------------------------------------------------------
void CNPC_Zombine::FootstepSound( bool fRightFoot )
{
	if( fRightFoot )
	{
		EmitSound( "Zombie.FootstepRight" );
	}
	else
	{
		EmitSound( "Zombie.FootstepLeft" );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Overloaded so that explosions don't split the zombine in twain.
//-----------------------------------------------------------------------------
bool CNPC_Zombine::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
{
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Sound of a foot sliding/scraping
//-----------------------------------------------------------------------------
void CNPC_Zombine::FootscuffSound( bool fRightFoot )
{
	if( fRightFoot )
	{
		EmitSound( "Zombine.ScuffRight" );
	}
	else
	{
		EmitSound( "Zombine.ScuffLeft" );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Play a random attack hit sound
//-----------------------------------------------------------------------------
void CNPC_Zombine::AttackHitSound( void )
{
	EmitSound( "Zombie.AttackHit" );
}

//-----------------------------------------------------------------------------
// Purpose: Play a random attack miss sound
//-----------------------------------------------------------------------------
void CNPC_Zombine::AttackMissSound( void )
{
	// Play a random attack miss sound
	EmitSound( "Zombie.AttackMiss" );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Zombine::PainSound( const CTakeDamageInfo &info )
{
	// We're constantly taking damage when we are on fire. Don't make all those noises!
	if ( IsOnFire() )
	{
		return;
	}

	EmitSound( "Zombine.Pain" );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CNPC_Zombine::DeathSound( const CTakeDamageInfo &info ) 
{
	EmitSound( "Zombine.Die" );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Zombine::AlertSound( void )
{
	EmitSound( "Zombine.Alert" );

	// Don't let a moan sound cut off the alert sound.
	m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 );
}

//-----------------------------------------------------------------------------
// Purpose: Play a random idle sound.
//-----------------------------------------------------------------------------
void CNPC_Zombine::IdleSound( void )
{
	if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 )
	{
		// Moan infrequently in IDLE state.
		return;
	}

	if( IsSlumped() )
	{
		// Sleeping zombies are quiet.
		return;
	}

	EmitSound( "Zombine.Idle" );
	MakeAISpookySound( 360.0f );
}

//-----------------------------------------------------------------------------
// Purpose: Play a random attack sound.
//-----------------------------------------------------------------------------
void CNPC_Zombine::AttackSound( void )
{
	
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char *CNPC_Zombine::GetHeadcrabModel( void )
{
	return "models/headcrabclassic.mdl";
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *CNPC_Zombine::GetLegsModel( void )
{
	return "models/zombie/zombie_soldier_legs.mdl";
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char *CNPC_Zombine::GetTorsoModel( void )
{
	return "models/zombie/zombie_soldier_torso.mdl";
}

//---------------------------------------------------------
// Classic zombie only uses moan sound if on fire.
//---------------------------------------------------------
void CNPC_Zombine::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize )
{
	if( IsOnFire() )
	{
		BaseClass::MoanSound( pEnvelope, iEnvelopeSize );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails.
//-----------------------------------------------------------------------------
const char *CNPC_Zombine::GetHeadcrabClassname( void )
{
	return "npc_headcrab";
}

void CNPC_Zombine::ReleaseGrenade( Vector vPhysgunPos )
{
	if ( HasGrenade() == false )
		return;

	Vector vDir = vPhysgunPos - m_hGrenade->GetAbsOrigin();
	VectorNormalize( vDir );

	Activity aActivity;

	Vector vForward, vRight;
	GetVectors( &vForward, &vRight, NULL );

	float flDotForward	= DotProduct( vForward, vDir );
	float flDotRight	= DotProduct( vRight, vDir );

	bool bNegativeForward = false;
	bool bNegativeRight = false;

	if ( flDotForward < 0.0f )
	{
		bNegativeForward = true;
		flDotForward = flDotForward * -1;
	}

	if ( flDotRight < 0.0f )
	{
		bNegativeRight = true;
		flDotRight = flDotRight * -1;
	}

	if ( flDotRight > flDotForward )
	{
		if ( bNegativeRight == true )
			aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_WEST;
		else 
			aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_EAST;
	}
	else
	{
		if ( bNegativeForward == true )
			aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_BACK;
		else 
			aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
	}

	AddGesture( aActivity );

	DropGrenade( vec3_origin );

	if ( IsSprinting() )
	{
		StopSprint();
	}
	else
	{
		Sprint();
	}
}

CBaseEntity *CNPC_Zombine::OnFailedPhysGunPickup( Vector vPhysgunPos )
{
	CBaseEntity *pGrenade = m_hGrenade;
	ReleaseGrenade( vPhysgunPos );
	return pGrenade;
}

//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------

AI_BEGIN_CUSTOM_NPC( npc_zombine, CNPC_Zombine )

	//Squad slots
	DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT1 )
	DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT2 )

	DECLARE_CONDITION( COND_ZOMBINE_GRENADE )

	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_PULL )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_WALK )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_RUN )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_IDLE )
	DECLARE_ACTIVITY( ACT_ZOMBINE_ATTACK_FAST )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_BACK )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_FRONT )
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_WEST)
	DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_EAST )

	DECLARE_ANIMEVENT( AE_ZOMBINE_PULLPIN )


	DEFINE_SCHEDULE
	(
	SCHED_ZOMBINE_PULL_GRENADE,

	"	Tasks"
	"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_ZOMBINE_GRENADE_PULL"


	"	Interrupts"

	)

AI_END_CUSTOM_NPC()



