//========= 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 > 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 m_hGlowSprites[EFFECT_COUNT]; CHandle 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( 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()