//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements d0g, the loving and caring head crushing Alyx companion.
//
//=============================================================================//

#include "cbase.h"
#include "npcevent.h"
#include "ai_basenpc.h"
#include "ai_network.h"
#include "ai_navigator.h"
#include "ai_motor.h"
#include "ai_hull.h"
#include "beam_shared.h"
#include "ai_baseactor.h"
#include "npc_rollermine.h"
#include "saverestore_utlvector.h"
#include "physics_bone_follower.h"
#include "Sprite.h"
#include "ai_behavior_follow.h"
#include "collisionutils.h"

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

#define EFFECT_COUNT 4

extern ConVar ai_debug_avoidancebounds;

class CNPC_Dog : public CAI_BaseActor
{
public:
	DECLARE_DATADESC();
	DECLARE_CLASS( CNPC_Dog, CAI_BaseActor );
	Class_T Classify ( void );
	void Spawn( void );
	void Precache( void );
	void StartTask( const Task_t *pTask );
	void HandleAnimEvent( animevent_t *pEvent );
	int	 SelectSchedule( void );

	bool FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore = NULL );
	void RunTask( const Task_t *pTask );
	void CreateBeams( void );
	void ClearBeams( void );

	void PrescheduleThink( void );

	bool CanTargetSeeMe( void );

	Vector	FacingPosition( void ) { return WorldSpaceCenter(); }
	float	GetHeadDebounce( void ) { return 0.8; } // how much of previous head turn to use

	void InputSetPickupTarget( inputdata_t &inputdata );
	void InputStartCatchThrowBehavior( inputdata_t &inputdata );
	void InputStopCatchThrowBehavior( inputdata_t &inputdata );
	void InputPlayerPickupObject( inputdata_t &inputdata );

	void InputStartWaitAndCatch( inputdata_t &inputdata );
	void InputStopWaitAndCatch( inputdata_t &inputdata );
	void InputSetThrowArcModifier( inputdata_t &inputdata );
	void InputSetThrowTarget( inputdata_t &inputdata );
	
	void InputTurnBoneFollowersOff( inputdata_t &inputdata );
	void InputTurnBoneFollowersOn( inputdata_t &inputdata );

	void CleanCatchAndThrow( bool bClearTimers = true );
	void SetTurnActivity ( void );
	void ThrowObject( const char *pAttachmentName );
	void PickupOrCatchObject( const char *pAttachmentName );
	void PullObject( bool bMantain );
	void SetupThrowTarget( void );

	void GatherConditions( void );

	Disposition_t IRelationType( CBaseEntity *pTarget );

	int OnTakeDamage_Alive( const CTakeDamageInfo &info );

	void	MantainBoneFollowerCollisionGroups( int CollisionGroup );
	virtual void SetPlayerAvoidState( void );

protected:
	enum
	{
		COND_DOG_LOST_PHYSICS_ENTITY = BaseClass::NEXT_CONDITION,

		NEXT_CONDITION,
	};

protected:
	float m_flNextSwat;
	float m_flTimeToCatch;
	float m_flTimeToPull;
	EHANDLE m_hPhysicsEnt;
	EHANDLE m_hThrowTarget;

	int	  m_iPhysGunAttachment;
	bool  m_bDoCatchThrowBehavior;
	bool  m_bDoWaitforObjectBehavior;
	string_t m_sObjectName;

	COutputEvent	m_OnThrow;
	COutputEvent	m_OnCatch;
	COutputEvent	m_OnPickup;

	float			m_flThrowArcModifier;
	int				m_iContainerMoveType;
	float			m_flNextRouteTime;

	bool			m_bHasObject;
	bool			m_bBeamEffects;
	
	CUtlVector< CHandle <CBaseEntity> > m_hUnreachableObjects;

	// Contained Bone Follower manager
	CBoneFollowerManager m_BoneFollowerManager;

	bool CreateVPhysics( void );
	void UpdateOnRemove( void );
	void NPCThink( void );
	void Event_Killed( const CTakeDamageInfo &info );

	void CreateSprites( void );
	void ClearSprites( void );
	CHandle<CSprite> m_hGlowSprites[EFFECT_COUNT];
	CHandle<CBeam>  m_hBeams[EFFECT_COUNT]; //This is temp.

	virtual bool CreateBehaviors( void );
	CAI_FollowBehavior		m_FollowBehavior;

	bool	m_bBoneFollowersActive;


protected:
	
	DEFINE_CUSTOM_AI;
};

LINK_ENTITY_TO_CLASS( npc_dog, CNPC_Dog );

BEGIN_DATADESC( CNPC_Dog )
	DEFINE_EMBEDDED( m_BoneFollowerManager ),
//	m_FollowBehavior
	DEFINE_FIELD( m_flNextSwat, FIELD_TIME ),
	DEFINE_FIELD( m_flTimeToCatch, FIELD_TIME ),
	DEFINE_FIELD( m_flTimeToPull, FIELD_TIME ),
	DEFINE_FIELD( m_hPhysicsEnt, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hThrowTarget, FIELD_EHANDLE ),
	DEFINE_FIELD( m_iPhysGunAttachment, FIELD_INTEGER ),
	DEFINE_FIELD( m_bDoCatchThrowBehavior, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bDoWaitforObjectBehavior, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_sObjectName, FIELD_STRING ),
	DEFINE_FIELD( m_flThrowArcModifier, FIELD_FLOAT ),
	DEFINE_FIELD( m_flNextRouteTime, FIELD_TIME ),
	DEFINE_FIELD( m_bHasObject, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_iContainerMoveType, FIELD_INTEGER ),
	DEFINE_FIELD( m_bBeamEffects, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bBoneFollowersActive, FIELD_BOOLEAN ),
	DEFINE_UTLVECTOR( m_hUnreachableObjects, FIELD_EHANDLE ),
	DEFINE_AUTO_ARRAY( m_hGlowSprites, FIELD_EHANDLE ),
	DEFINE_AUTO_ARRAY( m_hBeams, FIELD_EHANDLE ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"SetPickupTarget", InputSetPickupTarget ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"StartCatchThrowBehavior", InputStartCatchThrowBehavior ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"StopCatchThrowBehavior", InputStopCatchThrowBehavior ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"PlayerPickupObject", InputPlayerPickupObject ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"StartWaitAndCatch", InputStartWaitAndCatch ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"StopWaitAndCatch", InputStopWaitAndCatch ),
	DEFINE_INPUTFUNC( FIELD_FLOAT,	"SetThrowArcModifier", InputSetThrowArcModifier ),
	DEFINE_INPUTFUNC( FIELD_STRING,	"SetThrowTarget", InputSetThrowTarget ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"TurnBoneFollowersOff", InputTurnBoneFollowersOff ),
	DEFINE_INPUTFUNC( FIELD_VOID,	"TurnBoneFollowersOn", InputTurnBoneFollowersOn ),
	DEFINE_OUTPUT( m_OnThrow, "OnDogThrow"),
	DEFINE_OUTPUT( m_OnCatch, "OnDogCatch"),
	DEFINE_OUTPUT( m_OnPickup, "OnDogPickup"),
	
END_DATADESC()

#define DOG_PHYSOBJ_MOVE_TO_DIST	96
#define DOG_PULL_DISTANCE			200
#define DOG_CATCH_DISTANCE			48
#define DOG_PULL_VELOCITY_MOD		0.1f
#define DOG_PULL_ANGULARIMP_MOD		0.8f
#define DOG_PULL_TO_GUN_VEL_MOD		2.0f
#define DOG_MAX_THROW_MASS			250.0f
#define DOG_PHYSGUN_ATTACHMENT_NAME "physgun"

// These bones have physics shadows
static const char *pFollowerBoneNames[] =
{
	// head
	"Dog_Model.Eye",
	"Dog_Model.Pelvis",
};

enum
{
	SCHED_DOG_FIND_OBJECT = LAST_SHARED_SCHEDULE,
	SCHED_DOG_CATCH_OBJECT,
	SCHED_DOG_WAIT_THROW_OBJECT,
};

//=========================================================
// tasks
//=========================================================
enum 
{
	TASK_DOG_DELAY_SWAT = LAST_SHARED_TASK,
	TASK_DOG_GET_PATH_TO_PHYSOBJ,
	TASK_DOG_PICKUP_ITEM,
	TASK_DOG_LAUNCH_ITEM,
	TASK_DOG_FACE_OBJECT,
	TASK_DOG_WAIT_FOR_OBJECT,
	TASK_DOG_CATCH_OBJECT,
	TASK_DOG_WAIT_FOR_TARGET_TO_FACE,
	TASK_DOG_SETUP_THROW_TARGET,
};

int ACT_DOG_THROW;
int ACT_DOG_PICKUP;
int ACT_DOG_WAITING;
int ACT_DOG_CATCH;

int AE_DOG_THROW;
int AE_DOG_PICKUP;
int AE_DOG_CATCH;
int AE_DOG_PICKUP_NOEFFECT;

ConVar dog_max_wait_time( "dog_max_wait_time", "7" );
ConVar dog_debug( "dog_debug", "0" );

//-----------------------------------------------------------------------------
// Classify - indicates this NPC's place in the 
// relationship table.
//-----------------------------------------------------------------------------
Class_T	CNPC_Dog::Classify ( void )
{
	return	CLASS_PLAYER_ALLY_VITAL;
}

bool CNPC_Dog::CreateBehaviors( void )
{
	AddBehavior( &m_FollowBehavior );

	return BaseClass::CreateBehaviors();
}

Disposition_t CNPC_Dog::IRelationType( CBaseEntity *pTarget )
{
	if ( NPC_Rollermine_IsRollermine( pTarget ) )
	{
		if ( pTarget->HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
			 return D_LI;
	}

	return BaseClass::IRelationType( pTarget );
}

//---------------------------------------------------------
//---------------------------------------------------------
bool CNPC_Dog::CreateVPhysics( void )
{
	BaseClass::CreateVPhysics();

	if ( m_bBoneFollowersActive == true && !m_BoneFollowerManager.GetNumBoneFollowers() )
	{
		m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pFollowerBoneNames), pFollowerBoneNames );
	}
	return true;
}

//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::UpdateOnRemove( void )
{
	m_BoneFollowerManager.DestroyBoneFollowers();
	BaseClass::UpdateOnRemove();
}

void CNPC_Dog::GatherConditions( void )
{
	if ( IsInAScript() )
	{
		ClearSenseConditions();
		return;
	}

	BaseClass::GatherConditions();
}

int	CNPC_Dog::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	if ( IsInAScript() )
		 return 0;

	return BaseClass::OnTakeDamage_Alive( info );
}

//-----------------------------------------------------------------------------
// This function checks if Dog's collision group doesn't match his bone follower's and fixes them up.
//-----------------------------------------------------------------------------
void CNPC_Dog::MantainBoneFollowerCollisionGroups( int iCollisionGroup )
{
	if ( m_bBoneFollowersActive == false )
		return;

	physfollower_t* pBone = m_BoneFollowerManager.GetBoneFollower( 0 );

	if ( pBone && pBone->hFollower && pBone->hFollower->GetCollisionGroup() != iCollisionGroup )
	{
		for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
		{
			pBone = m_BoneFollowerManager.GetBoneFollower( i );

			if ( pBone && pBone->hFollower )
			{
				pBone->hFollower->SetCollisionGroup( iCollisionGroup );
			}
		}
	}
}

void CNPC_Dog::SetPlayerAvoidState( void )
{
	bool bIntersectingBoneFollowers = false;
	bool bIntersectingNPCBox = false;

	Vector vNothing;

	GetSequenceLinearMotion( GetSequence(), &vNothing );
	bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) );

	//If we are coming out of a script, check if we are stuck inside the player.
	if ( m_bPerformAvoidance || ( ShouldPlayerAvoid() && bIsMoving ) )
	{
		trace_t trace;
		Vector vMins, vMaxs;
		Vector vWorldMins, vWorldMaxs;
		Vector vPlayerMins, vPlayerMaxs;
		physfollower_t *pBone;
		int i;

		CBasePlayer *pLocalPlayer = AI_GetSinglePlayer();

		if ( pLocalPlayer )
		{
			vWorldMins = WorldAlignMins();
			vWorldMaxs = WorldAlignMaxs();

			vPlayerMins = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins();
			vPlayerMaxs = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs();

			// check if the player intersects the bounds of any of the bone followers
			for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
			{
				pBone = m_BoneFollowerManager.GetBoneFollower( i );
				if ( pBone && pBone->hFollower )
				{
					pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
					if ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) )
					{
						bIntersectingBoneFollowers = true;
						break;
					}
				}
			}

			bIntersectingNPCBox = IsBoxIntersectingBox( GetAbsOrigin() + vWorldMins, GetAbsOrigin() + vWorldMaxs, vPlayerMins, vPlayerMaxs );

			if ( ai_debug_avoidancebounds.GetBool() )
			{
				int iRed = ( bIntersectingNPCBox == true ) ? 255 : 0;

				NDebugOverlay::Box( GetAbsOrigin(), vWorldMins, vWorldMaxs, iRed, 0, 255, 64, 0.1 );

				// draw the bounds of the bone followers
				for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
				{
					pBone = m_BoneFollowerManager.GetBoneFollower( i );
					if ( pBone && pBone->hFollower )
					{
						pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
						iRed = ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) ) ? 255 : 0;

						NDebugOverlay::Box( vec3_origin, vMins, vMaxs, iRed, 0, 255, 64, 0.1 );
					}
				}
			}
		}
	}

	m_bPlayerAvoidState = ShouldPlayerAvoid();
	m_bPerformAvoidance = bIntersectingNPCBox || bIntersectingBoneFollowers;

	if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR )
	{
		if ( bIntersectingNPCBox == true )
		{
			SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR );
		}
		else
		{
			SetCollisionGroup( COLLISION_GROUP_NPC );
		}

		if ( bIntersectingBoneFollowers == true )
		{
			MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC_ACTOR );
		}
		else
		{
			MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC );
		}
	}
}
//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::NPCThink( void )
{
	BaseClass::NPCThink();

	if ( m_hPhysicsEnt == NULL )
	{
		ClearBeams();
		m_bHasObject = false;
	}
	
	if ( m_bHasObject == true )
	{
		 RelaxAim();
		 PullObject( true );
	}
	
	
	// update follower bones
	m_BoneFollowerManager.UpdateBoneFollowers(this);
}

//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::Event_Killed( const CTakeDamageInfo &info )
{
	m_BoneFollowerManager.DestroyBoneFollowers();
	BaseClass::Event_Killed( info );
}

//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CNPC_Dog::Spawn( void )
{
	m_bBoneFollowersActive = true;

	Precache();

	BaseClass::Spawn();

	SetModel( "models/dog.mdl" );

	SetHullType( HULL_WIDE_HUMAN );
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	SetBloodColor( BLOOD_COLOR_MECH );

	m_iHealth			= 999;
	m_flFieldOfView		= 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;

	m_takedamage		= DAMAGE_NO;
	
	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
	CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE );

	NPCInit();

	m_iPhysGunAttachment = LookupAttachment( DOG_PHYSGUN_ATTACHMENT_NAME );

	m_bDoCatchThrowBehavior = false;
	m_bDoWaitforObjectBehavior = false;
	m_bHasObject = false;
	m_bBeamEffects = true;

	m_flThrowArcModifier = 1.0f;

	m_flNextSwat = gpGlobals->curtime;
	m_flNextRouteTime = gpGlobals->curtime;
}


void CNPC_Dog::PrescheduleThink( void )
{
	BaseClass::PrescheduleThink();

	if ( m_hPhysicsEnt )
	{
		IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();

		if ( pPhysObj && pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
		{
			m_hPhysicsEnt->SetOwnerEntity( NULL );
		}
	}

	if ( m_flTimeToCatch < gpGlobals->curtime ) 
		 m_flTimeToCatch = 0.0f;

	
	if ( GetIdealActivity() == ACT_IDLE )
	{
		if ( m_hPhysicsEnt && m_bHasObject == true )
		{
			 SetIdealActivity( (Activity)ACT_DOG_WAITING );
		}
	}
}

int CNPC_Dog::SelectSchedule ( void )
{
	ClearCondition( COND_DOG_LOST_PHYSICS_ENTITY );

	if ( GetState() == NPC_STATE_SCRIPT || IsInAScript() )
		 return BaseClass::SelectSchedule();

	if ( BehaviorSelectSchedule() )
		return BaseClass::SelectSchedule();

	if ( m_bDoWaitforObjectBehavior == true )
	{
		if ( m_hPhysicsEnt )
			 return SCHED_DOG_CATCH_OBJECT;
	}
	
	if ( m_bDoCatchThrowBehavior == true )
	{
		if ( m_flTimeToCatch < 0.1 && m_flNextSwat <= gpGlobals->curtime )
		{
			 return SCHED_DOG_FIND_OBJECT;
		}

		if ( m_flTimeToCatch > gpGlobals->curtime && m_hPhysicsEnt )
			 return SCHED_DOG_CATCH_OBJECT;
	}
	else
	{
		if ( m_hPhysicsEnt )
		{
			if ( m_bHasObject == true )
			{
				return SCHED_DOG_WAIT_THROW_OBJECT;
			}
		}
	}

	return BaseClass::SelectSchedule();
}

void CNPC_Dog::PullObject( bool bMantain )
{
	if ( m_hPhysicsEnt == NULL )
	{
		TaskFail( "Ack! No Phys Object!");
		return;
	}

	IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();

	if ( pPhysObj == NULL )
	{
		TaskFail( "Pulling object with no Phys Object?!" );
		return;
	}

	if( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
	{
		m_bHasObject = false;
		ClearBeams();
		TaskFail("Player Grabbed Ball");
		return;
	}

	CreateBeams();

	Vector vGunPos;
	GetAttachment( m_iPhysGunAttachment, vGunPos );
	float flDistance = ( vGunPos - m_hPhysicsEnt->WorldSpaceCenter() ).Length();

	if ( bMantain == false )
	{
		if ( flDistance <= DOG_CATCH_DISTANCE )
		{
			m_hPhysicsEnt->SetOwnerEntity( this );

			GetNavigator()->StopMoving();

			//Fire Output!
			m_OnPickup.FireOutput( this, this );

			m_bHasObject = true;
			ClearBeams();
			TaskComplete();
			return;
		}
	}

	Vector vDir = ( vGunPos -  m_hPhysicsEnt->WorldSpaceCenter() );

	Vector vCurrentVel;
	float flCurrentVel;
	AngularImpulse vCurrentAI;

	pPhysObj->GetVelocity( &vCurrentVel, &vCurrentAI );
	flCurrentVel = vCurrentVel.Length();

	VectorNormalize( vCurrentVel );
	VectorNormalize( vDir );

	float flVelMod = DOG_PULL_VELOCITY_MOD;

	if ( bMantain == true )
		 flVelMod *= 2;

	vCurrentVel = vCurrentVel * flCurrentVel * flVelMod;

	vCurrentAI = vCurrentAI * DOG_PULL_ANGULARIMP_MOD;
	pPhysObj->SetVelocity( &vCurrentVel, &vCurrentAI );

	vDir = vDir * flDistance * (DOG_PULL_TO_GUN_VEL_MOD * 2);

	Vector vAngle( 0, 0, 0 );
	pPhysObj->AddVelocity( &vDir, &vAngle );
}

//-----------------------------------------------------------------------------
// Precache - precaches all resources this NPC needs
//-----------------------------------------------------------------------------
void CNPC_Dog::Precache( void )
{
	PrecacheModel( "models/dog.mdl" );
	
	PrecacheScriptSound( "Weapon_PhysCannon.Launch" );

	PrecacheModel( "sprites/orangelight1.vmt" );
	PrecacheModel( "sprites/physcannon_bluelight2.vmt" );
	PrecacheModel( "sprites/glow04_noz.vmt" );

	BaseClass::Precache();
}

void CNPC_Dog::CleanCatchAndThrow( bool bClearTimers )
{
	if ( m_hPhysicsEnt )
	{
		if ( m_bHasObject == true )
		{
			IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();

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

			Vector vGunPos;
			QAngle angGunAngles;
			GetAttachment( m_iPhysGunAttachment, vGunPos, angGunAngles );

			if ( pPhysObj )
			{
				pPhysObj->Wake();
				pPhysObj->RemoveShadowController();
				pPhysObj->SetPosition( vGunPos, angGunAngles, true );
			}
			else
			{
				Warning( "CleanCatchAndThrow:   m_hPhysicsEnt->VPhysicsGetObject == NULL!\n" );
			}

			m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType );

			if ( pPhysObj )
			{
				pPhysObj->RecheckCollisionFilter();
			}
	
			ClearBeams();
		}
		
		m_hPhysicsEnt = NULL;
	}

	if ( bClearTimers == true )
	{
		 m_bDoCatchThrowBehavior = false;
		 m_bDoWaitforObjectBehavior = false;
		 m_flTimeToCatch = 0.0f;
		 m_flNextSwat = 0.0f;

		 SetCondition( COND_DOG_LOST_PHYSICS_ENTITY );
	}
}

void CNPC_Dog::InputPlayerPickupObject ( inputdata_t &inputdata )
{
	if ( m_bDoWaitforObjectBehavior == true )
	{
		if ( m_hPhysicsEnt != inputdata.pCaller )
		{
			if ( m_hPhysicsEnt != NULL )
				 CleanCatchAndThrow( false );

			//Reset this cause CleanCatchAndThrow clears it.
			m_bDoWaitforObjectBehavior = true;
			m_hPhysicsEnt = inputdata.pCaller;
		}
	}
	else if ( m_bDoCatchThrowBehavior == true )
	{
		if ( m_sObjectName != NULL_STRING )
		{
			if ( m_hPhysicsEnt != inputdata.pCaller )
			{
				if ( m_hPhysicsEnt != NULL )
					 CleanCatchAndThrow( false );

				//Reset this cause CleanCatchAndThrow clears it.
				m_bDoCatchThrowBehavior = true;
				m_hPhysicsEnt = inputdata.pCaller;
			}
		}
	}
}

void CNPC_Dog::InputSetThrowArcModifier( inputdata_t &inputdata )
{
	m_flThrowArcModifier = inputdata.value.Float();
}

void CNPC_Dog::InputSetPickupTarget( inputdata_t &inputdata )
{
	CleanCatchAndThrow( false );
	FindPhysicsObject( inputdata.value.String() );
}

void CNPC_Dog::InputStartWaitAndCatch( inputdata_t &inputdata )
{
	CleanCatchAndThrow();
	m_bDoWaitforObjectBehavior = true;
}

void CNPC_Dog::InputStopWaitAndCatch( inputdata_t &inputdata )
{
	CleanCatchAndThrow();
}

void CNPC_Dog::InputStartCatchThrowBehavior( inputdata_t &inputdata )
{
	CleanCatchAndThrow();

	m_sObjectName = MAKE_STRING( inputdata.value.String() );
	m_bDoCatchThrowBehavior = true;

	m_flTimeToCatch = 0.0f;
	m_flNextSwat = 0.0f;

	FindPhysicsObject( inputdata.value.String() );
}

void CNPC_Dog::InputStopCatchThrowBehavior( inputdata_t &inputdata )
{
	m_bDoCatchThrowBehavior = false;

	m_flTimeToCatch = 0.0f;
	m_flNextSwat = 0.0f;
	m_sObjectName = NULL_STRING;

	CleanCatchAndThrow();
}

void CNPC_Dog::InputSetThrowTarget( inputdata_t &inputdata )
{
	m_hThrowTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller );
}

void CNPC_Dog::SetTurnActivity( void )
{
	BaseClass::SetTurnActivity();

	if ( GetIdealActivity() == ACT_IDLE )
	{
		if ( m_hPhysicsEnt && m_bHasObject == true )
			 SetIdealActivity( (Activity)ACT_DOG_WAITING );
	}
}

void CNPC_Dog::ThrowObject( const char *pAttachmentName )
{
	if ( m_hPhysicsEnt )
	{
		m_bHasObject = false;

		IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();

		if ( pPhysObj )
		{
			Vector vGunPos;
			QAngle angGunAngles;

			AngularImpulse angVelocity = RandomAngularImpulse( -250 , -250 ) / pPhysObj->GetMass();

			InvalidateBoneCache();

			int iAttachment = LookupAttachment( pAttachmentName );

			if ( iAttachment == 0 )
				 iAttachment = m_iPhysGunAttachment;
			
			GetAttachment( iAttachment, vGunPos, angGunAngles );

			pPhysObj->Wake();

			if ( pPhysObj->GetShadowController() )
			{
				m_hPhysicsEnt->SetParent( NULL );
				m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType );
				m_hPhysicsEnt->SetOwnerEntity( this );

				pPhysObj->RemoveShadowController();
				pPhysObj->SetPosition( m_hPhysicsEnt->GetLocalOrigin(), m_hPhysicsEnt->GetLocalAngles(), true );

				pPhysObj->RecheckCollisionFilter();
				pPhysObj->RecheckContactPoints();
			}
				
			if ( m_hThrowTarget == NULL )
				 m_hThrowTarget = AI_GetSinglePlayer();

			Vector vThrowDirection;

			if ( m_hThrowTarget )
			{
				Vector vThrowOrigin = m_hThrowTarget->GetAbsOrigin();
				
				if ( m_hThrowTarget->IsPlayer() )
					 vThrowOrigin = vThrowOrigin + Vector( random->RandomFloat( -128, 128 ), random->RandomFloat( -128, 128 ), 0 );

				Vector vecToss = VecCheckToss( this, vGunPos, vThrowOrigin, m_flThrowArcModifier, 1.0f, true );

				if( vecToss == vec3_origin )
				{
					// Fix up an impossible throw so dog will at least toss the box in the target's general direction instead of dropping it.
					// Also toss it up in the air so it will fall down and break. (Just throw the box up at a 45 degree angle)
					Vector forward, up;
					GetVectors( &forward, NULL, &up );

					vecToss = forward + up;
					VectorNormalize( vecToss );

					vecToss *= pPhysObj->GetMass() * 30.0f;
				}

				vThrowDirection = vecToss + ( m_hThrowTarget->GetSmoothedVelocity() / 2 );
							
				Vector vLinearDrag;

				Vector unitVel = vThrowDirection;
				VectorNormalize( unitVel );

				float flTest = 1000 / vThrowDirection.Length();

				float flDrag = pPhysObj->CalculateLinearDrag( vThrowDirection );
				vThrowDirection = vThrowDirection + ( unitVel * ( flDrag * flDrag ) ) / flTest;
			
				pPhysObj->SetVelocity( &vThrowDirection, &angVelocity );
				
				m_flTimeToCatch = gpGlobals->curtime + dog_max_wait_time.GetFloat();

				//Don't start pulling until the object is away from me.
				//We base the time on the throw velocity.
				m_flTimeToPull = gpGlobals->curtime + ( 1000 / vThrowDirection.Length() );
			}

			//Fire Output!
			m_OnThrow.FireOutput( this, this );

			ClearBeams();
			
			if ( m_bBeamEffects == true )
			{
				EmitSound( "Weapon_PhysCannon.Launch" );
				
				CBeam *pBeam = CBeam::BeamCreate(  "sprites/orangelight1.vmt", 1.8 );

				if ( pBeam != NULL )
				{
					pBeam->PointEntInit( m_hPhysicsEnt->WorldSpaceCenter(), this );
					pBeam->SetEndAttachment( m_iPhysGunAttachment );
					pBeam->SetWidth( 6.4 );
					pBeam->SetEndWidth( 12.8 );					
					pBeam->SetBrightness( 255 );
					pBeam->SetColor( 255, 255, 255 );
					pBeam->LiveForTime( 0.2f );
					pBeam->RelinkBeam();
					pBeam->SetNoise( 2 );
				}
			
				Vector	shotDir = ( m_hPhysicsEnt->WorldSpaceCenter() - vGunPos );
				VectorNormalize( shotDir );

				CPVSFilter filter( m_hPhysicsEnt->WorldSpaceCenter() );
				te->GaussExplosion( filter, 0.0f, m_hPhysicsEnt->WorldSpaceCenter() - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
			}
		}
	}
}

void CNPC_Dog::PickupOrCatchObject( const char *pAttachmentName )
{
	if ( m_hPhysicsEnt )
	{
		InvalidateBoneCache();

		int iAttachment = LookupAttachment( pAttachmentName );

		if ( iAttachment == 0 )
			 iAttachment = m_iPhysGunAttachment;
		
		// Move physobject to shadow
		IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
		if ( pPhysicsObject )
		{
			pPhysicsObject->SetShadow( 1e4, 1e4, false, false );
			pPhysicsObject->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, 0 );
		}
		
		m_iContainerMoveType = m_hPhysicsEnt->GetMoveType();
		m_hPhysicsEnt->SetMoveType( MOVETYPE_NONE );
		
		m_hPhysicsEnt->SetParent( this, iAttachment );
	
		m_hPhysicsEnt->SetLocalOrigin( vec3_origin );
		m_hPhysicsEnt->SetLocalAngles( vec3_angle );

		m_hPhysicsEnt->SetGroundEntity( NULL );
		

		if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
			 m_hPhysicsEnt->SetOwnerEntity( this );

		if ( pPhysicsObject )
			 pPhysicsObject->RecheckCollisionFilter();

		m_bHasObject = true;

		//Fire Output!
		m_OnPickup.FireOutput( this, this );
	}
}

//-----------------------------------------------------------------------------
// HandleAnimEvent - catches the NPC-specific messages
// that occur when tagged animation frames are played.
//-----------------------------------------------------------------------------
void CNPC_Dog::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_DOG_THROW )
	{
		ThrowObject( pEvent->options );
		return;
	}

	if ( pEvent->event == AE_DOG_PICKUP || pEvent->event == AE_DOG_CATCH || pEvent->event == AE_DOG_PICKUP_NOEFFECT )
	{
		if ( pEvent->event == AE_DOG_PICKUP_NOEFFECT )
			 m_bBeamEffects = false;
		else
			 m_bBeamEffects = true;

		PickupOrCatchObject( pEvent->options );
		return;
	}

	BaseClass::HandleAnimEvent( pEvent );
}

void CNPC_Dog::ClearBeams( void )
{
	ClearSprites();
	
	// Turn off sprites
	for ( int i = 0; i < EFFECT_COUNT; i++ )
	{
		if ( m_hBeams[i] != NULL )
		{
			UTIL_Remove( m_hBeams[i] );
			m_hBeams[i] = NULL;
		}
	}
}

void CNPC_Dog::ClearSprites( void )
{
	// Turn off sprites
	for ( int i = 0; i < EFFECT_COUNT; i++ )
	{
		if ( m_hGlowSprites[i] != NULL )
		{
			UTIL_Remove( m_hGlowSprites[i] );
			m_hGlowSprites[i] = NULL;
		}
	}
}

void CNPC_Dog::CreateSprites( void )
{
	//Create the glow sprites
	for ( int i = 0; i < EFFECT_COUNT; i++ )
	{
		if ( m_hGlowSprites[i] )
			continue;

		const char *attachNames[] = 
		{
			"physgun",
			"thumb",
			"pinky",
			"index",
		};

		m_hGlowSprites[i] = CSprite::SpriteCreate( "sprites/glow04_noz.vmt", GetAbsOrigin(), false );

		m_hGlowSprites[i]->SetAttachment( this, LookupAttachment( attachNames[i] ) );
		m_hGlowSprites[i]->SetTransparency( kRenderGlow, 255, 128, 0, 64, kRenderFxNoDissipation );
		m_hGlowSprites[i]->SetBrightness( 255, 0.2f );
		m_hGlowSprites[i]->SetScale( 0.55f, 0.2f );
	}
}

void CNPC_Dog::CreateBeams( void )
{
	if ( m_bBeamEffects == false )
	{
		ClearBeams();
		return;
	}

	CreateSprites();

	for ( int i = 0; i < EFFECT_COUNT; i++ )
	{
		if ( m_hBeams[i] )
			continue;

		const char *attachNames[] = 
		{
			"physgun",
			"thumb",
			"pinky",
			"index",
		};

		m_hBeams[i] = CBeam::BeamCreate( "sprites/physcannon_bluelight2.vmt", 5.0 );

		m_hBeams[i]->EntsInit( m_hPhysicsEnt, this );
		m_hBeams[i]->SetEndAttachment( LookupAttachment( attachNames[i] ) );
		m_hBeams[i]->SetBrightness( 255 );
		m_hBeams[i]->SetColor( 255, 255, 255 );
		m_hBeams[i]->SetNoise( 5.5 );
		m_hBeams[i]->SetRenderMode( kRenderTransAdd );
	}

}

bool CNPC_Dog::FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore )
{
	CBaseEntity		*pEnt = NULL;
	CBaseEntity		*pNearest = NULL;
	float			flDist;
	IPhysicsObject	*pPhysObj = NULL;
	float			flNearestDist = 99999;

	if ( pPickupName != NULL && strlen( pPickupName ) > 0 )
	{
		pEnt = gEntList.FindEntityByName( NULL, pPickupName );
		
		if ( m_hUnreachableObjects.Find( pEnt ) == -1  )
		{
			m_bHasObject = false;
			m_hPhysicsEnt = pEnt;
			return true;
		}
	}
	
	while ( ( pEnt = gEntList.FindEntityByClassname( pEnt, "prop_physics" ) ) != NULL )
	{
		//We don't want this one.
		if ( pEnt == pIgnore )
			 continue;

		if ( m_hUnreachableObjects.Find( pEnt ) != -1 )
			 continue;

		pPhysObj = pEnt->VPhysicsGetObject();

		if( pPhysObj == NULL )
			continue;

		if ( pPhysObj->GetMass() > DOG_MAX_THROW_MASS )
			 continue;
		
		Vector center = pEnt->WorldSpaceCenter();
		flDist = UTIL_DistApprox2D( GetAbsOrigin(), center );

		vcollide_t *pCollide = modelinfo->GetVCollide( pEnt->GetModelIndex() );

		if ( pCollide == NULL )
			 continue;

		if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
			 continue;

		if ( pPhysObj->IsMoveable() == false )
			 continue;

		if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_DEBRIS || 
			 pEnt->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
			 continue;

		if ( center.z > EyePosition().z )
			 continue;

		if ( flDist >= flNearestDist )
			 continue;

		if ( FVisible( pEnt ) == false )
			 continue;
		
		pNearest = pEnt;
		flNearestDist = flDist;
	}

	m_bHasObject = false;
	m_hPhysicsEnt = pNearest;

	if ( dog_debug.GetBool() == true )
	{
		if ( pNearest )
			 NDebugOverlay::Box( pNearest->WorldSpaceCenter(), pNearest->CollisionProp()->OBBMins(), pNearest->CollisionProp()->OBBMaxs(), 255, 0, 255, true, 3 );
	}

	if( m_hPhysicsEnt == NULL )
	{
		return false;
	}
	else
	{
		return true;
	}
}

//-----------------------------------------------------------------------------
// Can me enemy see me? 
//-----------------------------------------------------------------------------
bool CNPC_Dog::CanTargetSeeMe( void )
{
	CBaseEntity *pEntity = m_hThrowTarget;

	if ( pEntity )
	{
		if ( pEntity->IsPlayer() == false )
			return true;

		CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( pEntity );

		if ( pPlayer )
		{
			if ( m_hPhysicsEnt )
			{
				if ( pPlayer->FVisible( m_hPhysicsEnt ) == false )
					return false;
			}
			
			if ( pPlayer->FInViewCone( this ) )
			{
				return true;
			}
		}
	}

	return false;
}


//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::RunTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{

	case TASK_DOG_PICKUP_ITEM:
	{
		 PullObject( false );
	}
	break;

	case TASK_DOG_GET_PATH_TO_PHYSOBJ:
		{
			//Check this cause our object might have been deleted.
			if ( m_hPhysicsEnt == NULL )
				 FindPhysicsObject( NULL );

			//And if we still can't find anything, then just go away.
			if ( m_hPhysicsEnt == NULL )
			{
				TaskFail( "Can't find an object I like!" );
				return;
			}
	
			IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
			
			Vector vecGoalPos;
			Vector vecDir;

			vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
			VectorNormalize(vecDir);
			vecDir.z = 0;
		
			if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
				 m_hPhysicsEnt->SetOwnerEntity( this );
		
			if ( pPhysicsObject )
				 pPhysicsObject->RecheckCollisionFilter();

			vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );

			bool bBuiltRoute = false;

			//If I'm near my goal, then just walk to it.
			Activity aActivity = ACT_RUN;

			if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
				 aActivity = ACT_WALK;

			bBuiltRoute = GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL );

			if ( bBuiltRoute == true )
				 TaskComplete();
			else
			{
				m_flTimeToCatch = gpGlobals->curtime + 0.1;
				m_flNextRouteTime = gpGlobals->curtime + 0.3;
				m_flNextSwat = gpGlobals->curtime + 0.1;

				if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 )
					 m_hUnreachableObjects.AddToTail( m_hPhysicsEnt );
								
				m_hPhysicsEnt = NULL;

				GetNavigator()->ClearGoal();
			}
		}
		break;

	case TASK_WAIT:
	{
		if ( IsWaitFinished() )
		{
			TaskComplete();
		}

		if ( m_hPhysicsEnt )
		{
			if ( m_bHasObject == false )
			{
				GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() );
				GetMotor()->UpdateYaw();
			}
		}

		break;
	}

	case TASK_DOG_LAUNCH_ITEM:
		if( IsActivityFinished() )
		{
			if ( m_hPhysicsEnt )
			{
				m_hPhysicsEnt->SetOwnerEntity( NULL );
			}

			TaskComplete();
		}
		break;

	case TASK_DOG_WAIT_FOR_TARGET_TO_FACE:
	{
		if ( CanTargetSeeMe() )
			 TaskComplete();
	}
		break;

	case TASK_WAIT_FOR_MOVEMENT:
		{
			if ( GetState() == NPC_STATE_SCRIPT || IsInAScript() )
			{
			  	 BaseClass::RunTask( pTask );
				 return;
			}

			if ( m_hPhysicsEnt != NULL )
			{
				IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
					
				if ( !pPhysObj )
				{
					Warning( "npc_dog TASK_WAIT_FOR_MOVEMENT with NULL m_hPhysicsEnt->VPhysicsGetObject\n" );
				}

				if ( pPhysObj && pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
					 TaskFail( "Player picked it up!" );

				//If the object is moving then my old goal might not be valid
				//cancel the schedule and make it restart again in a bit.
				if ( pPhysObj && pPhysObj->IsAsleep() == false && GetNavigator()->IsGoalActive() == false )
				{
					Vector vecGoalPos;
					Vector vecDir;
				
					vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
					VectorNormalize(vecDir);
					vecDir.z = 0;
									
					vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );

					GetNavigator()->ClearGoal();

					float flDistance = (vecGoalPos - GetLocalOrigin()).Length();

					//If I'm near my goal, then just walk to it.
					Activity aActivity = ACT_RUN;

					if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
						 aActivity = ACT_WALK;

				    GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL );

					if ( flDistance <= DOG_PHYSOBJ_MOVE_TO_DIST )
					{
						TaskComplete();
						GetNavigator()->StopMoving();
					}
				}
			}
			
			BaseClass::RunTask( pTask );
		}
		break;

	case TASK_DOG_WAIT_FOR_OBJECT:
		{
			if ( m_hPhysicsEnt != NULL )
			{
				if ( FVisible( m_hPhysicsEnt ) == false )
				{
					m_flTimeToCatch = 0.0f;
					ClearBeams();
					TaskFail( "Lost sight of the object!" );
					m_hPhysicsEnt->SetOwnerEntity( NULL );
					return;
				}

				m_hPhysicsEnt->SetOwnerEntity( this );

				Vector vForward;
				AngleVectors( GetAbsAngles(), &vForward );


				Vector vGunPos;
				GetAttachment( m_iPhysGunAttachment, vGunPos );

				Vector vToObject = m_hPhysicsEnt->WorldSpaceCenter() - vGunPos;
				float flDistance = vToObject.Length();

				VectorNormalize( vToObject );

				SetAim( m_hPhysicsEnt->WorldSpaceCenter() - GetAbsOrigin() );

				CBasePlayer *pPlayer = AI_GetSinglePlayer();

				float flDistanceToPlayer = flDistance;

				if ( pPlayer )
				{
					flDistanceToPlayer = (pPlayer->GetAbsOrigin() - m_hPhysicsEnt->WorldSpaceCenter()).Length();
				}
			
				IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
				if ( !pPhysObj )
				{
					Warning( "npc_dog:  TASK_DOG_WAIT_FOR_OBJECT with m_hPhysicsEnt->VPhysicsGetObject == NULL\n" );
				}
					
				if ( pPhysObj && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) && flDistanceToPlayer > ( flDistance * 2 ) )
				{
					if ( m_flTimeToPull <= gpGlobals->curtime )
					{
						Vector vCurrentVel;
						float flCurrentVel;
						AngularImpulse vCurrentAI;

						pPhysObj->GetVelocity( &vCurrentVel, &vCurrentAI );

						flCurrentVel = vCurrentVel.Length();
						VectorNormalize( vCurrentVel );

						if ( pPhysObj && flDistance <= DOG_PULL_DISTANCE )
						{
							Vector vDir = ( vGunPos -  m_hPhysicsEnt->WorldSpaceCenter() );
								
							VectorNormalize( vDir );

							vCurrentVel = vCurrentVel * ( flCurrentVel * DOG_PULL_VELOCITY_MOD );

							vCurrentAI = vCurrentAI * DOG_PULL_ANGULARIMP_MOD;
							pPhysObj->SetVelocity( &vCurrentVel, &vCurrentAI );

							vDir = vDir * flDistance * DOG_PULL_TO_GUN_VEL_MOD;

							Vector vAngle( 0, 0, 0 );
							pPhysObj->AddVelocity( &vDir, &vAngle );
							
							CreateBeams();
						}
					
						float flDot = DotProduct( vCurrentVel, vForward );

						if ( flDistance >= DOG_PULL_DISTANCE && flDistance <= ( DOG_PULL_DISTANCE * 2 ) && flDot > -0.3 )
						{
							if ( pPhysObj->IsAsleep() == false && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
							{
								Vector vecGoalPos;
								Vector vecDir;

								vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
								VectorNormalize(vecDir);
								vecDir.z = 0;
												
								vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );

								GetNavigator()->ClearGoal();

								//If I'm near my goal, then just walk to it.
								Activity aActivity = ACT_RUN;

								if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
									 aActivity = ACT_WALK;
									 
								GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ),  AIN_NO_PATH_TASK_FAIL );
							}
						}
					}
				}


				float flDirDot = DotProduct( vToObject, vForward );

				if ( flDirDot < 0.2 )
				{
					GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() );
					GetMotor()->UpdateYaw();
				}

				if ( m_flTimeToCatch < gpGlobals->curtime && m_bDoWaitforObjectBehavior == false ) 
				{
					m_hPhysicsEnt->SetOwnerEntity( NULL );
					m_flTimeToCatch = 0.0f;
					ClearBeams();
					TaskFail( "Done waiting!" );
				}
				else if ( pPhysObj && ( flDistance <= DOG_CATCH_DISTANCE && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) )
				{
					AngularImpulse vZero( 0, 0, 0 );
					pPhysObj->SetVelocity( &vec3_origin, &vZero );

					GetNavigator()->StopMoving();

					//Fire Output!
					m_OnCatch.FireOutput( this, this );
					m_bHasObject = true;
					ClearBeams();
					TaskComplete();
				}
			}
			else
			{
				GetNavigator()->StopMoving();

				ClearBeams();
				TaskFail("No Physics Object!");
			}
			
		}
		break;

	case TASK_DOG_CATCH_OBJECT:
		if( IsActivityFinished() )
		{
			m_flTimeToCatch = 0.0f;
			TaskComplete();
		}
		break;
	default:
		BaseClass::RunTask( pTask );
		break;
	}
}

void CNPC_Dog::SetupThrowTarget( void )
{
	if ( m_hThrowTarget == NULL )
	{
		m_hThrowTarget = AI_GetSinglePlayer();
	}

	SetTarget( m_hThrowTarget );
}

//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::StartTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{

	case TASK_DOG_SETUP_THROW_TARGET:
		{
			SetupThrowTarget();
			TaskComplete();
		}
		break;
	case TASK_DOG_GET_PATH_TO_PHYSOBJ:
		{
			FindPhysicsObject( STRING( m_sObjectName ) );

			if ( m_hPhysicsEnt == NULL )
			{
				 FindPhysicsObject( NULL );
				 return;
			}

			IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
			
			Vector vecGoalPos;
			Vector vecDir;

			vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
			VectorNormalize(vecDir);
			vecDir.z = 0;
		
			if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
				 m_hPhysicsEnt->SetOwnerEntity( this );
		
			if ( pPhysicsObject )
				 pPhysicsObject->RecheckCollisionFilter();

			vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );

			//If I'm near my goal, then just walk to it.
			Activity aActivity = ACT_RUN;

			if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
				 aActivity = ACT_WALK;

			if ( GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ) == false )
			{
				 if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 )
					  m_hUnreachableObjects.AddToTail( m_hPhysicsEnt );
					
				 FindPhysicsObject( NULL, m_hPhysicsEnt );

				 m_flTimeToCatch = gpGlobals->curtime + 0.1;
				 m_flNextRouteTime = gpGlobals->curtime + 0.3;
				 m_flNextSwat = gpGlobals->curtime + 0.1;

				 GetNavigator()->ClearGoal();
			}
			else
			{
				TaskComplete();
			}
		}
		break;

	case TASK_DOG_FACE_OBJECT:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}

			Vector vecDir;

			vecDir = m_hPhysicsEnt->WorldSpaceCenter() - GetLocalOrigin();
			VectorNormalize(vecDir);

			GetMotor()->SetIdealYaw( UTIL_VecToYaw( vecDir ) );
			TaskComplete();
		}
		break;
		
	case TASK_DOG_PICKUP_ITEM:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}
			else
			{
				SetIdealActivity( (Activity)ACT_DOG_PICKUP );
			}
		}

		break;
		
	case TASK_DOG_LAUNCH_ITEM:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}
			else
			{
				if ( m_hPhysicsEnt == NULL || m_bHasObject == false )
				{
					 TaskFail( "Don't have the item!" );
					 return;
				}

				SetIdealActivity( (Activity)ACT_DOG_THROW );
			}
		}

		break;

	case TASK_DOG_WAIT_FOR_TARGET_TO_FACE:
		{
			if ( CanTargetSeeMe() )
				 TaskComplete();
		}
		break;

	case TASK_DOG_WAIT_FOR_OBJECT:
		{
			SetIdealActivity( (Activity)ACT_DOG_WAITING );
		}
		break;

	case TASK_DOG_CATCH_OBJECT:
	{
		SetIdealActivity( (Activity)ACT_DOG_CATCH  );
	}
	break;
			
	case TASK_DOG_DELAY_SWAT:
		m_flNextSwat = gpGlobals->curtime + pTask->flTaskData;

		if ( m_hThrowTarget == NULL )
			m_hThrowTarget = AI_GetSinglePlayer();

		TaskComplete();
		break;

	default:
		BaseClass::StartTask( pTask );
	}
}

void CNPC_Dog::InputTurnBoneFollowersOff( inputdata_t &inputdata )
{
	if ( m_bBoneFollowersActive )
	{
		m_bBoneFollowersActive = false;
		m_BoneFollowerManager.DestroyBoneFollowers();
	}

}

void CNPC_Dog::InputTurnBoneFollowersOn( inputdata_t &inputdata )
{
	if ( !m_bBoneFollowersActive )
	{
		m_bBoneFollowersActive = true;
		m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pFollowerBoneNames), pFollowerBoneNames );
	}
}

AI_BEGIN_CUSTOM_NPC( npc_dog, CNPC_Dog )

	DECLARE_USES_SCHEDULE_PROVIDER( CAI_FollowBehavior )

	DECLARE_ACTIVITY( ACT_DOG_THROW )
	DECLARE_ACTIVITY( ACT_DOG_PICKUP )
	DECLARE_ACTIVITY( ACT_DOG_WAITING )
	DECLARE_ACTIVITY( ACT_DOG_CATCH )
	
	DECLARE_CONDITION( COND_DOG_LOST_PHYSICS_ENTITY )

	DECLARE_TASK( TASK_DOG_DELAY_SWAT )
	DECLARE_TASK( TASK_DOG_GET_PATH_TO_PHYSOBJ )
	DECLARE_TASK( TASK_DOG_LAUNCH_ITEM )
	DECLARE_TASK( TASK_DOG_PICKUP_ITEM )
	DECLARE_TASK( TASK_DOG_FACE_OBJECT )
	DECLARE_TASK( TASK_DOG_WAIT_FOR_OBJECT )
	DECLARE_TASK( TASK_DOG_CATCH_OBJECT )

	DECLARE_TASK( TASK_DOG_WAIT_FOR_TARGET_TO_FACE )
	DECLARE_TASK( TASK_DOG_SETUP_THROW_TARGET )
		
	DECLARE_ANIMEVENT( AE_DOG_THROW )
	DECLARE_ANIMEVENT( AE_DOG_PICKUP )
	DECLARE_ANIMEVENT( AE_DOG_CATCH )
	DECLARE_ANIMEVENT( AE_DOG_PICKUP_NOEFFECT )
	

	DEFINE_SCHEDULE
	(
		SCHED_DOG_FIND_OBJECT,

		"	Tasks"
		"		TASK_DOG_DELAY_SWAT					3"
		"		TASK_DOG_GET_PATH_TO_PHYSOBJ		0"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		"		TASK_DOG_FACE_OBJECT				0"
		"		TASK_FACE_IDEAL						0"
		"		TASK_DOG_PICKUP_ITEM				0"
		"		TASK_DOG_SETUP_THROW_TARGET			0"
		"		TASK_FACE_TARGET					0.5"
		"		TASK_DOG_WAIT_FOR_TARGET_TO_FACE	0"
		"		TASK_DOG_LAUNCH_ITEM				0"
		""
		"	Interrupts"
		"		COND_DOG_LOST_PHYSICS_ENTITY"
	)

	DEFINE_SCHEDULE
	(
		SCHED_DOG_WAIT_THROW_OBJECT,
		"	Tasks"
		"		TASK_DOG_SETUP_THROW_TARGET			0"
		"		TASK_FACE_TARGET					0.5"
		"		TASK_DOG_WAIT_FOR_TARGET_TO_FACE	0"
		"		TASK_DOG_LAUNCH_ITEM				0"
		""
		"	Interrupts"
		"		COND_DOG_LOST_PHYSICS_ENTITY"
	)

	DEFINE_SCHEDULE
	(
		SCHED_DOG_CATCH_OBJECT,

		"	Tasks"
		"		TASK_DOG_WAIT_FOR_OBJECT			0"
		"		TASK_DOG_CATCH_OBJECT				0"
		"		TASK_FACE_PLAYER					0.5"
		"		TASK_DOG_WAIT_FOR_TARGET_TO_FACE	0"
		"		TASK_DOG_LAUNCH_ITEM				0"
		"		TASK_SET_FAIL_SCHEDULE				SCHEDULE:SCHED_IDLE_STAND"
		""
		"	Interrupts"
		"		COND_DOG_LOST_PHYSICS_ENTITY"
	)

AI_END_CUSTOM_NPC()
