mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-29 17:43:01 +00:00
1785 lines
43 KiB
C++
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()
|