diff options
Diffstat (limited to 'game/server/hl2/npc_dog.cpp')
| -rw-r--r-- | game/server/hl2/npc_dog.cpp | 1784 |
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() |