hl2_src-leak-2017/src/game/server/hl2/npc_dog.cpp

1785 lines
43 KiB
C++

//========= 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()