summaryrefslogtreecommitdiff
path: root/game/server/hl2/npc_dog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/hl2/npc_dog.cpp')
-rw-r--r--game/server/hl2/npc_dog.cpp1784
1 files changed, 1784 insertions, 0 deletions
diff --git a/game/server/hl2/npc_dog.cpp b/game/server/hl2/npc_dog.cpp
new file mode 100644
index 0000000..6e05cf4
--- /dev/null
+++ b/game/server/hl2/npc_dog.cpp
@@ -0,0 +1,1784 @@
+//========= Copyright 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()