Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1784 lines
43 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements d0g, the loving and caring head crushing Alyx companion.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "npcevent.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_network.h"
  10. #include "ai_navigator.h"
  11. #include "ai_motor.h"
  12. #include "ai_hull.h"
  13. #include "beam_shared.h"
  14. #include "ai_baseactor.h"
  15. #include "npc_rollermine.h"
  16. #include "saverestore_utlvector.h"
  17. #include "physics_bone_follower.h"
  18. #include "Sprite.h"
  19. #include "ai_behavior_follow.h"
  20. #include "collisionutils.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. #define EFFECT_COUNT 4
  24. extern ConVar ai_debug_avoidancebounds;
  25. class CNPC_Dog : public CAI_BaseActor
  26. {
  27. public:
  28. DECLARE_DATADESC();
  29. DECLARE_CLASS( CNPC_Dog, CAI_BaseActor );
  30. Class_T Classify ( void );
  31. void Spawn( void );
  32. void Precache( void );
  33. void StartTask( const Task_t *pTask );
  34. void HandleAnimEvent( animevent_t *pEvent );
  35. int SelectSchedule( void );
  36. bool FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore = NULL );
  37. void RunTask( const Task_t *pTask );
  38. void CreateBeams( void );
  39. void ClearBeams( void );
  40. void PrescheduleThink( void );
  41. bool CanTargetSeeMe( void );
  42. Vector FacingPosition( void ) { return WorldSpaceCenter(); }
  43. float GetHeadDebounce( void ) { return 0.8; } // how much of previous head turn to use
  44. void InputSetPickupTarget( inputdata_t &inputdata );
  45. void InputStartCatchThrowBehavior( inputdata_t &inputdata );
  46. void InputStopCatchThrowBehavior( inputdata_t &inputdata );
  47. void InputPlayerPickupObject( inputdata_t &inputdata );
  48. void InputStartWaitAndCatch( inputdata_t &inputdata );
  49. void InputStopWaitAndCatch( inputdata_t &inputdata );
  50. void InputSetThrowArcModifier( inputdata_t &inputdata );
  51. void InputSetThrowTarget( inputdata_t &inputdata );
  52. void InputTurnBoneFollowersOff( inputdata_t &inputdata );
  53. void InputTurnBoneFollowersOn( inputdata_t &inputdata );
  54. void CleanCatchAndThrow( bool bClearTimers = true );
  55. void SetTurnActivity ( void );
  56. void ThrowObject( const char *pAttachmentName );
  57. void PickupOrCatchObject( const char *pAttachmentName );
  58. void PullObject( bool bMantain );
  59. void SetupThrowTarget( void );
  60. void GatherConditions( void );
  61. Disposition_t IRelationType( CBaseEntity *pTarget );
  62. int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  63. void MantainBoneFollowerCollisionGroups( int CollisionGroup );
  64. virtual void SetPlayerAvoidState( void );
  65. protected:
  66. enum
  67. {
  68. COND_DOG_LOST_PHYSICS_ENTITY = BaseClass::NEXT_CONDITION,
  69. NEXT_CONDITION,
  70. };
  71. protected:
  72. float m_flNextSwat;
  73. float m_flTimeToCatch;
  74. float m_flTimeToPull;
  75. EHANDLE m_hPhysicsEnt;
  76. EHANDLE m_hThrowTarget;
  77. int m_iPhysGunAttachment;
  78. bool m_bDoCatchThrowBehavior;
  79. bool m_bDoWaitforObjectBehavior;
  80. string_t m_sObjectName;
  81. COutputEvent m_OnThrow;
  82. COutputEvent m_OnCatch;
  83. COutputEvent m_OnPickup;
  84. float m_flThrowArcModifier;
  85. int m_iContainerMoveType;
  86. float m_flNextRouteTime;
  87. bool m_bHasObject;
  88. bool m_bBeamEffects;
  89. CUtlVector< CHandle <CBaseEntity> > m_hUnreachableObjects;
  90. // Contained Bone Follower manager
  91. CBoneFollowerManager m_BoneFollowerManager;
  92. bool CreateVPhysics( void );
  93. void UpdateOnRemove( void );
  94. void NPCThink( void );
  95. void Event_Killed( const CTakeDamageInfo &info );
  96. void CreateSprites( void );
  97. void ClearSprites( void );
  98. CHandle<CSprite> m_hGlowSprites[EFFECT_COUNT];
  99. CHandle<CBeam> m_hBeams[EFFECT_COUNT]; //This is temp.
  100. virtual bool CreateBehaviors( void );
  101. CAI_FollowBehavior m_FollowBehavior;
  102. bool m_bBoneFollowersActive;
  103. protected:
  104. DEFINE_CUSTOM_AI;
  105. };
  106. LINK_ENTITY_TO_CLASS( npc_dog, CNPC_Dog );
  107. BEGIN_DATADESC( CNPC_Dog )
  108. DEFINE_EMBEDDED( m_BoneFollowerManager ),
  109. // m_FollowBehavior
  110. DEFINE_FIELD( m_flNextSwat, FIELD_TIME ),
  111. DEFINE_FIELD( m_flTimeToCatch, FIELD_TIME ),
  112. DEFINE_FIELD( m_flTimeToPull, FIELD_TIME ),
  113. DEFINE_FIELD( m_hPhysicsEnt, FIELD_EHANDLE ),
  114. DEFINE_FIELD( m_hThrowTarget, FIELD_EHANDLE ),
  115. DEFINE_FIELD( m_iPhysGunAttachment, FIELD_INTEGER ),
  116. DEFINE_FIELD( m_bDoCatchThrowBehavior, FIELD_BOOLEAN ),
  117. DEFINE_FIELD( m_bDoWaitforObjectBehavior, FIELD_BOOLEAN ),
  118. DEFINE_FIELD( m_sObjectName, FIELD_STRING ),
  119. DEFINE_FIELD( m_flThrowArcModifier, FIELD_FLOAT ),
  120. DEFINE_FIELD( m_flNextRouteTime, FIELD_TIME ),
  121. DEFINE_FIELD( m_bHasObject, FIELD_BOOLEAN ),
  122. DEFINE_FIELD( m_iContainerMoveType, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_bBeamEffects, FIELD_BOOLEAN ),
  124. DEFINE_FIELD( m_bBoneFollowersActive, FIELD_BOOLEAN ),
  125. DEFINE_UTLVECTOR( m_hUnreachableObjects, FIELD_EHANDLE ),
  126. DEFINE_AUTO_ARRAY( m_hGlowSprites, FIELD_EHANDLE ),
  127. DEFINE_AUTO_ARRAY( m_hBeams, FIELD_EHANDLE ),
  128. DEFINE_INPUTFUNC( FIELD_STRING, "SetPickupTarget", InputSetPickupTarget ),
  129. DEFINE_INPUTFUNC( FIELD_STRING, "StartCatchThrowBehavior", InputStartCatchThrowBehavior ),
  130. DEFINE_INPUTFUNC( FIELD_STRING, "StopCatchThrowBehavior", InputStopCatchThrowBehavior ),
  131. DEFINE_INPUTFUNC( FIELD_VOID, "PlayerPickupObject", InputPlayerPickupObject ),
  132. DEFINE_INPUTFUNC( FIELD_VOID, "StartWaitAndCatch", InputStartWaitAndCatch ),
  133. DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitAndCatch", InputStopWaitAndCatch ),
  134. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetThrowArcModifier", InputSetThrowArcModifier ),
  135. DEFINE_INPUTFUNC( FIELD_STRING, "SetThrowTarget", InputSetThrowTarget ),
  136. DEFINE_INPUTFUNC( FIELD_VOID, "TurnBoneFollowersOff", InputTurnBoneFollowersOff ),
  137. DEFINE_INPUTFUNC( FIELD_VOID, "TurnBoneFollowersOn", InputTurnBoneFollowersOn ),
  138. DEFINE_OUTPUT( m_OnThrow, "OnDogThrow"),
  139. DEFINE_OUTPUT( m_OnCatch, "OnDogCatch"),
  140. DEFINE_OUTPUT( m_OnPickup, "OnDogPickup"),
  141. END_DATADESC()
  142. #define DOG_PHYSOBJ_MOVE_TO_DIST 96
  143. #define DOG_PULL_DISTANCE 200
  144. #define DOG_CATCH_DISTANCE 48
  145. #define DOG_PULL_VELOCITY_MOD 0.1f
  146. #define DOG_PULL_ANGULARIMP_MOD 0.8f
  147. #define DOG_PULL_TO_GUN_VEL_MOD 2.0f
  148. #define DOG_MAX_THROW_MASS 250.0f
  149. #define DOG_PHYSGUN_ATTACHMENT_NAME "physgun"
  150. // These bones have physics shadows
  151. static const char *pFollowerBoneNames[] =
  152. {
  153. // head
  154. "Dog_Model.Eye",
  155. "Dog_Model.Pelvis",
  156. };
  157. enum
  158. {
  159. SCHED_DOG_FIND_OBJECT = LAST_SHARED_SCHEDULE,
  160. SCHED_DOG_CATCH_OBJECT,
  161. SCHED_DOG_WAIT_THROW_OBJECT,
  162. };
  163. //=========================================================
  164. // tasks
  165. //=========================================================
  166. enum
  167. {
  168. TASK_DOG_DELAY_SWAT = LAST_SHARED_TASK,
  169. TASK_DOG_GET_PATH_TO_PHYSOBJ,
  170. TASK_DOG_PICKUP_ITEM,
  171. TASK_DOG_LAUNCH_ITEM,
  172. TASK_DOG_FACE_OBJECT,
  173. TASK_DOG_WAIT_FOR_OBJECT,
  174. TASK_DOG_CATCH_OBJECT,
  175. TASK_DOG_WAIT_FOR_TARGET_TO_FACE,
  176. TASK_DOG_SETUP_THROW_TARGET,
  177. };
  178. int ACT_DOG_THROW;
  179. int ACT_DOG_PICKUP;
  180. int ACT_DOG_WAITING;
  181. int ACT_DOG_CATCH;
  182. int AE_DOG_THROW;
  183. int AE_DOG_PICKUP;
  184. int AE_DOG_CATCH;
  185. int AE_DOG_PICKUP_NOEFFECT;
  186. ConVar dog_max_wait_time( "dog_max_wait_time", "7" );
  187. ConVar dog_debug( "dog_debug", "0" );
  188. //-----------------------------------------------------------------------------
  189. // Classify - indicates this NPC's place in the
  190. // relationship table.
  191. //-----------------------------------------------------------------------------
  192. Class_T CNPC_Dog::Classify ( void )
  193. {
  194. return CLASS_PLAYER_ALLY_VITAL;
  195. }
  196. bool CNPC_Dog::CreateBehaviors( void )
  197. {
  198. AddBehavior( &m_FollowBehavior );
  199. return BaseClass::CreateBehaviors();
  200. }
  201. Disposition_t CNPC_Dog::IRelationType( CBaseEntity *pTarget )
  202. {
  203. if ( NPC_Rollermine_IsRollermine( pTarget ) )
  204. {
  205. if ( pTarget->HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
  206. return D_LI;
  207. }
  208. return BaseClass::IRelationType( pTarget );
  209. }
  210. //---------------------------------------------------------
  211. //---------------------------------------------------------
  212. bool CNPC_Dog::CreateVPhysics( void )
  213. {
  214. BaseClass::CreateVPhysics();
  215. if ( m_bBoneFollowersActive == true && !m_BoneFollowerManager.GetNumBoneFollowers() )
  216. {
  217. m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pFollowerBoneNames), pFollowerBoneNames );
  218. }
  219. return true;
  220. }
  221. //---------------------------------------------------------
  222. //---------------------------------------------------------
  223. void CNPC_Dog::UpdateOnRemove( void )
  224. {
  225. m_BoneFollowerManager.DestroyBoneFollowers();
  226. BaseClass::UpdateOnRemove();
  227. }
  228. void CNPC_Dog::GatherConditions( void )
  229. {
  230. if ( IsInAScript() )
  231. {
  232. ClearSenseConditions();
  233. return;
  234. }
  235. BaseClass::GatherConditions();
  236. }
  237. int CNPC_Dog::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  238. {
  239. if ( IsInAScript() )
  240. return 0;
  241. return BaseClass::OnTakeDamage_Alive( info );
  242. }
  243. //-----------------------------------------------------------------------------
  244. // This function checks if Dog's collision group doesn't match his bone follower's and fixes them up.
  245. //-----------------------------------------------------------------------------
  246. void CNPC_Dog::MantainBoneFollowerCollisionGroups( int iCollisionGroup )
  247. {
  248. if ( m_bBoneFollowersActive == false )
  249. return;
  250. physfollower_t* pBone = m_BoneFollowerManager.GetBoneFollower( 0 );
  251. if ( pBone && pBone->hFollower && pBone->hFollower->GetCollisionGroup() != iCollisionGroup )
  252. {
  253. for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
  254. {
  255. pBone = m_BoneFollowerManager.GetBoneFollower( i );
  256. if ( pBone && pBone->hFollower )
  257. {
  258. pBone->hFollower->SetCollisionGroup( iCollisionGroup );
  259. }
  260. }
  261. }
  262. }
  263. void CNPC_Dog::SetPlayerAvoidState( void )
  264. {
  265. bool bIntersectingBoneFollowers = false;
  266. bool bIntersectingNPCBox = false;
  267. Vector vNothing;
  268. GetSequenceLinearMotion( GetSequence(), &vNothing );
  269. bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) );
  270. //If we are coming out of a script, check if we are stuck inside the player.
  271. if ( m_bPerformAvoidance || ( ShouldPlayerAvoid() && bIsMoving ) )
  272. {
  273. trace_t trace;
  274. Vector vMins, vMaxs;
  275. Vector vWorldMins, vWorldMaxs;
  276. Vector vPlayerMins, vPlayerMaxs;
  277. physfollower_t *pBone;
  278. int i;
  279. CBasePlayer *pLocalPlayer = AI_GetSinglePlayer();
  280. if ( pLocalPlayer )
  281. {
  282. vWorldMins = WorldAlignMins();
  283. vWorldMaxs = WorldAlignMaxs();
  284. vPlayerMins = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins();
  285. vPlayerMaxs = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs();
  286. // check if the player intersects the bounds of any of the bone followers
  287. for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
  288. {
  289. pBone = m_BoneFollowerManager.GetBoneFollower( i );
  290. if ( pBone && pBone->hFollower )
  291. {
  292. pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
  293. if ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) )
  294. {
  295. bIntersectingBoneFollowers = true;
  296. break;
  297. }
  298. }
  299. }
  300. bIntersectingNPCBox = IsBoxIntersectingBox( GetAbsOrigin() + vWorldMins, GetAbsOrigin() + vWorldMaxs, vPlayerMins, vPlayerMaxs );
  301. if ( ai_debug_avoidancebounds.GetBool() )
  302. {
  303. int iRed = ( bIntersectingNPCBox == true ) ? 255 : 0;
  304. NDebugOverlay::Box( GetAbsOrigin(), vWorldMins, vWorldMaxs, iRed, 0, 255, 64, 0.1 );
  305. // draw the bounds of the bone followers
  306. for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
  307. {
  308. pBone = m_BoneFollowerManager.GetBoneFollower( i );
  309. if ( pBone && pBone->hFollower )
  310. {
  311. pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs );
  312. iRed = ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) ) ? 255 : 0;
  313. NDebugOverlay::Box( vec3_origin, vMins, vMaxs, iRed, 0, 255, 64, 0.1 );
  314. }
  315. }
  316. }
  317. }
  318. }
  319. m_bPlayerAvoidState = ShouldPlayerAvoid();
  320. m_bPerformAvoidance = bIntersectingNPCBox || bIntersectingBoneFollowers;
  321. if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR )
  322. {
  323. if ( bIntersectingNPCBox == true )
  324. {
  325. SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR );
  326. }
  327. else
  328. {
  329. SetCollisionGroup( COLLISION_GROUP_NPC );
  330. }
  331. if ( bIntersectingBoneFollowers == true )
  332. {
  333. MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC_ACTOR );
  334. }
  335. else
  336. {
  337. MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC );
  338. }
  339. }
  340. }
  341. //---------------------------------------------------------
  342. //---------------------------------------------------------
  343. void CNPC_Dog::NPCThink( void )
  344. {
  345. BaseClass::NPCThink();
  346. if ( m_hPhysicsEnt == NULL )
  347. {
  348. ClearBeams();
  349. m_bHasObject = false;
  350. }
  351. if ( m_bHasObject == true )
  352. {
  353. RelaxAim();
  354. PullObject( true );
  355. }
  356. // update follower bones
  357. m_BoneFollowerManager.UpdateBoneFollowers(this);
  358. }
  359. //---------------------------------------------------------
  360. //---------------------------------------------------------
  361. void CNPC_Dog::Event_Killed( const CTakeDamageInfo &info )
  362. {
  363. m_BoneFollowerManager.DestroyBoneFollowers();
  364. BaseClass::Event_Killed( info );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Spawn
  368. //-----------------------------------------------------------------------------
  369. void CNPC_Dog::Spawn( void )
  370. {
  371. m_bBoneFollowersActive = true;
  372. Precache();
  373. BaseClass::Spawn();
  374. SetModel( "models/dog.mdl" );
  375. SetHullType( HULL_WIDE_HUMAN );
  376. SetHullSizeNormal();
  377. SetSolid( SOLID_BBOX );
  378. AddSolidFlags( FSOLID_NOT_STANDABLE );
  379. SetMoveType( MOVETYPE_STEP );
  380. SetBloodColor( BLOOD_COLOR_MECH );
  381. m_iHealth = 999;
  382. m_flFieldOfView = 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
  383. m_NPCState = NPC_STATE_NONE;
  384. m_takedamage = DAMAGE_NO;
  385. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
  386. CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE );
  387. NPCInit();
  388. m_iPhysGunAttachment = LookupAttachment( DOG_PHYSGUN_ATTACHMENT_NAME );
  389. m_bDoCatchThrowBehavior = false;
  390. m_bDoWaitforObjectBehavior = false;
  391. m_bHasObject = false;
  392. m_bBeamEffects = true;
  393. m_flThrowArcModifier = 1.0f;
  394. m_flNextSwat = gpGlobals->curtime;
  395. m_flNextRouteTime = gpGlobals->curtime;
  396. }
  397. void CNPC_Dog::PrescheduleThink( void )
  398. {
  399. BaseClass::PrescheduleThink();
  400. if ( m_hPhysicsEnt )
  401. {
  402. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  403. if ( pPhysObj && pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  404. {
  405. m_hPhysicsEnt->SetOwnerEntity( NULL );
  406. }
  407. }
  408. if ( m_flTimeToCatch < gpGlobals->curtime )
  409. m_flTimeToCatch = 0.0f;
  410. if ( GetIdealActivity() == ACT_IDLE )
  411. {
  412. if ( m_hPhysicsEnt && m_bHasObject == true )
  413. {
  414. SetIdealActivity( (Activity)ACT_DOG_WAITING );
  415. }
  416. }
  417. }
  418. int CNPC_Dog::SelectSchedule ( void )
  419. {
  420. ClearCondition( COND_DOG_LOST_PHYSICS_ENTITY );
  421. if ( GetState() == NPC_STATE_SCRIPT || IsInAScript() )
  422. return BaseClass::SelectSchedule();
  423. if ( BehaviorSelectSchedule() )
  424. return BaseClass::SelectSchedule();
  425. if ( m_bDoWaitforObjectBehavior == true )
  426. {
  427. if ( m_hPhysicsEnt )
  428. return SCHED_DOG_CATCH_OBJECT;
  429. }
  430. if ( m_bDoCatchThrowBehavior == true )
  431. {
  432. if ( m_flTimeToCatch < 0.1 && m_flNextSwat <= gpGlobals->curtime )
  433. {
  434. return SCHED_DOG_FIND_OBJECT;
  435. }
  436. if ( m_flTimeToCatch > gpGlobals->curtime && m_hPhysicsEnt )
  437. return SCHED_DOG_CATCH_OBJECT;
  438. }
  439. else
  440. {
  441. if ( m_hPhysicsEnt )
  442. {
  443. if ( m_bHasObject == true )
  444. {
  445. return SCHED_DOG_WAIT_THROW_OBJECT;
  446. }
  447. }
  448. }
  449. return BaseClass::SelectSchedule();
  450. }
  451. void CNPC_Dog::PullObject( bool bMantain )
  452. {
  453. if ( m_hPhysicsEnt == NULL )
  454. {
  455. TaskFail( "Ack! No Phys Object!");
  456. return;
  457. }
  458. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  459. if ( pPhysObj == NULL )
  460. {
  461. TaskFail( "Pulling object with no Phys Object?!" );
  462. return;
  463. }
  464. if( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  465. {
  466. m_bHasObject = false;
  467. ClearBeams();
  468. TaskFail("Player Grabbed Ball");
  469. return;
  470. }
  471. CreateBeams();
  472. Vector vGunPos;
  473. GetAttachment( m_iPhysGunAttachment, vGunPos );
  474. float flDistance = ( vGunPos - m_hPhysicsEnt->WorldSpaceCenter() ).Length();
  475. if ( bMantain == false )
  476. {
  477. if ( flDistance <= DOG_CATCH_DISTANCE )
  478. {
  479. m_hPhysicsEnt->SetOwnerEntity( this );
  480. GetNavigator()->StopMoving();
  481. //Fire Output!
  482. m_OnPickup.FireOutput( this, this );
  483. m_bHasObject = true;
  484. ClearBeams();
  485. TaskComplete();
  486. return;
  487. }
  488. }
  489. Vector vDir = ( vGunPos - m_hPhysicsEnt->WorldSpaceCenter() );
  490. Vector vCurrentVel;
  491. float flCurrentVel;
  492. AngularImpulse vCurrentAI;
  493. pPhysObj->GetVelocity( &vCurrentVel, &vCurrentAI );
  494. flCurrentVel = vCurrentVel.Length();
  495. VectorNormalize( vCurrentVel );
  496. VectorNormalize( vDir );
  497. float flVelMod = DOG_PULL_VELOCITY_MOD;
  498. if ( bMantain == true )
  499. flVelMod *= 2;
  500. vCurrentVel = vCurrentVel * flCurrentVel * flVelMod;
  501. vCurrentAI = vCurrentAI * DOG_PULL_ANGULARIMP_MOD;
  502. pPhysObj->SetVelocity( &vCurrentVel, &vCurrentAI );
  503. vDir = vDir * flDistance * (DOG_PULL_TO_GUN_VEL_MOD * 2);
  504. Vector vAngle( 0, 0, 0 );
  505. pPhysObj->AddVelocity( &vDir, &vAngle );
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Precache - precaches all resources this NPC needs
  509. //-----------------------------------------------------------------------------
  510. void CNPC_Dog::Precache( void )
  511. {
  512. PrecacheModel( "models/dog.mdl" );
  513. PrecacheScriptSound( "Weapon_PhysCannon.Launch" );
  514. PrecacheModel( "sprites/orangelight1.vmt" );
  515. PrecacheModel( "sprites/physcannon_bluelight2.vmt" );
  516. PrecacheModel( "sprites/glow04_noz.vmt" );
  517. BaseClass::Precache();
  518. }
  519. void CNPC_Dog::CleanCatchAndThrow( bool bClearTimers )
  520. {
  521. if ( m_hPhysicsEnt )
  522. {
  523. if ( m_bHasObject == true )
  524. {
  525. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  526. m_hPhysicsEnt->SetParent( NULL );
  527. m_hPhysicsEnt->SetOwnerEntity( NULL );
  528. Vector vGunPos;
  529. QAngle angGunAngles;
  530. GetAttachment( m_iPhysGunAttachment, vGunPos, angGunAngles );
  531. if ( pPhysObj )
  532. {
  533. pPhysObj->Wake();
  534. pPhysObj->RemoveShadowController();
  535. pPhysObj->SetPosition( vGunPos, angGunAngles, true );
  536. }
  537. else
  538. {
  539. Warning( "CleanCatchAndThrow: m_hPhysicsEnt->VPhysicsGetObject == NULL!\n" );
  540. }
  541. m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType );
  542. if ( pPhysObj )
  543. {
  544. pPhysObj->RecheckCollisionFilter();
  545. }
  546. ClearBeams();
  547. }
  548. m_hPhysicsEnt = NULL;
  549. }
  550. if ( bClearTimers == true )
  551. {
  552. m_bDoCatchThrowBehavior = false;
  553. m_bDoWaitforObjectBehavior = false;
  554. m_flTimeToCatch = 0.0f;
  555. m_flNextSwat = 0.0f;
  556. SetCondition( COND_DOG_LOST_PHYSICS_ENTITY );
  557. }
  558. }
  559. void CNPC_Dog::InputPlayerPickupObject ( inputdata_t &inputdata )
  560. {
  561. if ( m_bDoWaitforObjectBehavior == true )
  562. {
  563. if ( m_hPhysicsEnt != inputdata.pCaller )
  564. {
  565. if ( m_hPhysicsEnt != NULL )
  566. CleanCatchAndThrow( false );
  567. //Reset this cause CleanCatchAndThrow clears it.
  568. m_bDoWaitforObjectBehavior = true;
  569. m_hPhysicsEnt = inputdata.pCaller;
  570. }
  571. }
  572. else if ( m_bDoCatchThrowBehavior == true )
  573. {
  574. if ( m_sObjectName != NULL_STRING )
  575. {
  576. if ( m_hPhysicsEnt != inputdata.pCaller )
  577. {
  578. if ( m_hPhysicsEnt != NULL )
  579. CleanCatchAndThrow( false );
  580. //Reset this cause CleanCatchAndThrow clears it.
  581. m_bDoCatchThrowBehavior = true;
  582. m_hPhysicsEnt = inputdata.pCaller;
  583. }
  584. }
  585. }
  586. }
  587. void CNPC_Dog::InputSetThrowArcModifier( inputdata_t &inputdata )
  588. {
  589. m_flThrowArcModifier = inputdata.value.Float();
  590. }
  591. void CNPC_Dog::InputSetPickupTarget( inputdata_t &inputdata )
  592. {
  593. CleanCatchAndThrow( false );
  594. FindPhysicsObject( inputdata.value.String() );
  595. }
  596. void CNPC_Dog::InputStartWaitAndCatch( inputdata_t &inputdata )
  597. {
  598. CleanCatchAndThrow();
  599. m_bDoWaitforObjectBehavior = true;
  600. }
  601. void CNPC_Dog::InputStopWaitAndCatch( inputdata_t &inputdata )
  602. {
  603. CleanCatchAndThrow();
  604. }
  605. void CNPC_Dog::InputStartCatchThrowBehavior( inputdata_t &inputdata )
  606. {
  607. CleanCatchAndThrow();
  608. m_sObjectName = MAKE_STRING( inputdata.value.String() );
  609. m_bDoCatchThrowBehavior = true;
  610. m_flTimeToCatch = 0.0f;
  611. m_flNextSwat = 0.0f;
  612. FindPhysicsObject( inputdata.value.String() );
  613. }
  614. void CNPC_Dog::InputStopCatchThrowBehavior( inputdata_t &inputdata )
  615. {
  616. m_bDoCatchThrowBehavior = false;
  617. m_flTimeToCatch = 0.0f;
  618. m_flNextSwat = 0.0f;
  619. m_sObjectName = NULL_STRING;
  620. CleanCatchAndThrow();
  621. }
  622. void CNPC_Dog::InputSetThrowTarget( inputdata_t &inputdata )
  623. {
  624. m_hThrowTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller );
  625. }
  626. void CNPC_Dog::SetTurnActivity( void )
  627. {
  628. BaseClass::SetTurnActivity();
  629. if ( GetIdealActivity() == ACT_IDLE )
  630. {
  631. if ( m_hPhysicsEnt && m_bHasObject == true )
  632. SetIdealActivity( (Activity)ACT_DOG_WAITING );
  633. }
  634. }
  635. void CNPC_Dog::ThrowObject( const char *pAttachmentName )
  636. {
  637. if ( m_hPhysicsEnt )
  638. {
  639. m_bHasObject = false;
  640. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  641. if ( pPhysObj )
  642. {
  643. Vector vGunPos;
  644. QAngle angGunAngles;
  645. AngularImpulse angVelocity = RandomAngularImpulse( -250 , -250 ) / pPhysObj->GetMass();
  646. InvalidateBoneCache();
  647. int iAttachment = LookupAttachment( pAttachmentName );
  648. if ( iAttachment == 0 )
  649. iAttachment = m_iPhysGunAttachment;
  650. GetAttachment( iAttachment, vGunPos, angGunAngles );
  651. pPhysObj->Wake();
  652. if ( pPhysObj->GetShadowController() )
  653. {
  654. m_hPhysicsEnt->SetParent( NULL );
  655. m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType );
  656. m_hPhysicsEnt->SetOwnerEntity( this );
  657. pPhysObj->RemoveShadowController();
  658. pPhysObj->SetPosition( m_hPhysicsEnt->GetLocalOrigin(), m_hPhysicsEnt->GetLocalAngles(), true );
  659. pPhysObj->RecheckCollisionFilter();
  660. pPhysObj->RecheckContactPoints();
  661. }
  662. if ( m_hThrowTarget == NULL )
  663. m_hThrowTarget = AI_GetSinglePlayer();
  664. Vector vThrowDirection;
  665. if ( m_hThrowTarget )
  666. {
  667. Vector vThrowOrigin = m_hThrowTarget->GetAbsOrigin();
  668. if ( m_hThrowTarget->IsPlayer() )
  669. vThrowOrigin = vThrowOrigin + Vector( random->RandomFloat( -128, 128 ), random->RandomFloat( -128, 128 ), 0 );
  670. Vector vecToss = VecCheckToss( this, vGunPos, vThrowOrigin, m_flThrowArcModifier, 1.0f, true );
  671. if( vecToss == vec3_origin )
  672. {
  673. // Fix up an impossible throw so dog will at least toss the box in the target's general direction instead of dropping it.
  674. // Also toss it up in the air so it will fall down and break. (Just throw the box up at a 45 degree angle)
  675. Vector forward, up;
  676. GetVectors( &forward, NULL, &up );
  677. vecToss = forward + up;
  678. VectorNormalize( vecToss );
  679. vecToss *= pPhysObj->GetMass() * 30.0f;
  680. }
  681. vThrowDirection = vecToss + ( m_hThrowTarget->GetSmoothedVelocity() / 2 );
  682. Vector vLinearDrag;
  683. Vector unitVel = vThrowDirection;
  684. VectorNormalize( unitVel );
  685. float flTest = 1000 / vThrowDirection.Length();
  686. float flDrag = pPhysObj->CalculateLinearDrag( vThrowDirection );
  687. vThrowDirection = vThrowDirection + ( unitVel * ( flDrag * flDrag ) ) / flTest;
  688. pPhysObj->SetVelocity( &vThrowDirection, &angVelocity );
  689. m_flTimeToCatch = gpGlobals->curtime + dog_max_wait_time.GetFloat();
  690. //Don't start pulling until the object is away from me.
  691. //We base the time on the throw velocity.
  692. m_flTimeToPull = gpGlobals->curtime + ( 1000 / vThrowDirection.Length() );
  693. }
  694. //Fire Output!
  695. m_OnThrow.FireOutput( this, this );
  696. ClearBeams();
  697. if ( m_bBeamEffects == true )
  698. {
  699. EmitSound( "Weapon_PhysCannon.Launch" );
  700. CBeam *pBeam = CBeam::BeamCreate( "sprites/orangelight1.vmt", 1.8 );
  701. if ( pBeam != NULL )
  702. {
  703. pBeam->PointEntInit( m_hPhysicsEnt->WorldSpaceCenter(), this );
  704. pBeam->SetEndAttachment( m_iPhysGunAttachment );
  705. pBeam->SetWidth( 6.4 );
  706. pBeam->SetEndWidth( 12.8 );
  707. pBeam->SetBrightness( 255 );
  708. pBeam->SetColor( 255, 255, 255 );
  709. pBeam->LiveForTime( 0.2f );
  710. pBeam->RelinkBeam();
  711. pBeam->SetNoise( 2 );
  712. }
  713. Vector shotDir = ( m_hPhysicsEnt->WorldSpaceCenter() - vGunPos );
  714. VectorNormalize( shotDir );
  715. CPVSFilter filter( m_hPhysicsEnt->WorldSpaceCenter() );
  716. te->GaussExplosion( filter, 0.0f, m_hPhysicsEnt->WorldSpaceCenter() - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
  717. }
  718. }
  719. }
  720. }
  721. void CNPC_Dog::PickupOrCatchObject( const char *pAttachmentName )
  722. {
  723. if ( m_hPhysicsEnt )
  724. {
  725. InvalidateBoneCache();
  726. int iAttachment = LookupAttachment( pAttachmentName );
  727. if ( iAttachment == 0 )
  728. iAttachment = m_iPhysGunAttachment;
  729. // Move physobject to shadow
  730. IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
  731. if ( pPhysicsObject )
  732. {
  733. pPhysicsObject->SetShadow( 1e4, 1e4, false, false );
  734. pPhysicsObject->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, 0 );
  735. }
  736. m_iContainerMoveType = m_hPhysicsEnt->GetMoveType();
  737. m_hPhysicsEnt->SetMoveType( MOVETYPE_NONE );
  738. m_hPhysicsEnt->SetParent( this, iAttachment );
  739. m_hPhysicsEnt->SetLocalOrigin( vec3_origin );
  740. m_hPhysicsEnt->SetLocalAngles( vec3_angle );
  741. m_hPhysicsEnt->SetGroundEntity( NULL );
  742. if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
  743. m_hPhysicsEnt->SetOwnerEntity( this );
  744. if ( pPhysicsObject )
  745. pPhysicsObject->RecheckCollisionFilter();
  746. m_bHasObject = true;
  747. //Fire Output!
  748. m_OnPickup.FireOutput( this, this );
  749. }
  750. }
  751. //-----------------------------------------------------------------------------
  752. // HandleAnimEvent - catches the NPC-specific messages
  753. // that occur when tagged animation frames are played.
  754. //-----------------------------------------------------------------------------
  755. void CNPC_Dog::HandleAnimEvent( animevent_t *pEvent )
  756. {
  757. if ( pEvent->event == AE_DOG_THROW )
  758. {
  759. ThrowObject( pEvent->options );
  760. return;
  761. }
  762. if ( pEvent->event == AE_DOG_PICKUP || pEvent->event == AE_DOG_CATCH || pEvent->event == AE_DOG_PICKUP_NOEFFECT )
  763. {
  764. if ( pEvent->event == AE_DOG_PICKUP_NOEFFECT )
  765. m_bBeamEffects = false;
  766. else
  767. m_bBeamEffects = true;
  768. PickupOrCatchObject( pEvent->options );
  769. return;
  770. }
  771. BaseClass::HandleAnimEvent( pEvent );
  772. }
  773. void CNPC_Dog::ClearBeams( void )
  774. {
  775. ClearSprites();
  776. // Turn off sprites
  777. for ( int i = 0; i < EFFECT_COUNT; i++ )
  778. {
  779. if ( m_hBeams[i] != NULL )
  780. {
  781. UTIL_Remove( m_hBeams[i] );
  782. m_hBeams[i] = NULL;
  783. }
  784. }
  785. }
  786. void CNPC_Dog::ClearSprites( void )
  787. {
  788. // Turn off sprites
  789. for ( int i = 0; i < EFFECT_COUNT; i++ )
  790. {
  791. if ( m_hGlowSprites[i] != NULL )
  792. {
  793. UTIL_Remove( m_hGlowSprites[i] );
  794. m_hGlowSprites[i] = NULL;
  795. }
  796. }
  797. }
  798. void CNPC_Dog::CreateSprites( void )
  799. {
  800. //Create the glow sprites
  801. for ( int i = 0; i < EFFECT_COUNT; i++ )
  802. {
  803. if ( m_hGlowSprites[i] )
  804. continue;
  805. const char *attachNames[] =
  806. {
  807. "physgun",
  808. "thumb",
  809. "pinky",
  810. "index",
  811. };
  812. m_hGlowSprites[i] = CSprite::SpriteCreate( "sprites/glow04_noz.vmt", GetAbsOrigin(), false );
  813. m_hGlowSprites[i]->SetAttachment( this, LookupAttachment( attachNames[i] ) );
  814. m_hGlowSprites[i]->SetTransparency( kRenderGlow, 255, 128, 0, 64, kRenderFxNoDissipation );
  815. m_hGlowSprites[i]->SetBrightness( 255, 0.2f );
  816. m_hGlowSprites[i]->SetScale( 0.55f, 0.2f );
  817. }
  818. }
  819. void CNPC_Dog::CreateBeams( void )
  820. {
  821. if ( m_bBeamEffects == false )
  822. {
  823. ClearBeams();
  824. return;
  825. }
  826. CreateSprites();
  827. for ( int i = 0; i < EFFECT_COUNT; i++ )
  828. {
  829. if ( m_hBeams[i] )
  830. continue;
  831. const char *attachNames[] =
  832. {
  833. "physgun",
  834. "thumb",
  835. "pinky",
  836. "index",
  837. };
  838. m_hBeams[i] = CBeam::BeamCreate( "sprites/physcannon_bluelight2.vmt", 5.0 );
  839. m_hBeams[i]->EntsInit( m_hPhysicsEnt, this );
  840. m_hBeams[i]->SetEndAttachment( LookupAttachment( attachNames[i] ) );
  841. m_hBeams[i]->SetBrightness( 255 );
  842. m_hBeams[i]->SetColor( 255, 255, 255 );
  843. m_hBeams[i]->SetNoise( 5.5 );
  844. m_hBeams[i]->SetRenderMode( kRenderTransAdd );
  845. }
  846. }
  847. bool CNPC_Dog::FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore )
  848. {
  849. CBaseEntity *pEnt = NULL;
  850. CBaseEntity *pNearest = NULL;
  851. float flDist;
  852. IPhysicsObject *pPhysObj = NULL;
  853. float flNearestDist = 99999;
  854. if ( pPickupName != NULL && strlen( pPickupName ) > 0 )
  855. {
  856. pEnt = gEntList.FindEntityByName( NULL, pPickupName );
  857. if ( m_hUnreachableObjects.Find( pEnt ) == -1 )
  858. {
  859. m_bHasObject = false;
  860. m_hPhysicsEnt = pEnt;
  861. return true;
  862. }
  863. }
  864. while ( ( pEnt = gEntList.FindEntityByClassname( pEnt, "prop_physics" ) ) != NULL )
  865. {
  866. //We don't want this one.
  867. if ( pEnt == pIgnore )
  868. continue;
  869. if ( m_hUnreachableObjects.Find( pEnt ) != -1 )
  870. continue;
  871. pPhysObj = pEnt->VPhysicsGetObject();
  872. if( pPhysObj == NULL )
  873. continue;
  874. if ( pPhysObj->GetMass() > DOG_MAX_THROW_MASS )
  875. continue;
  876. Vector center = pEnt->WorldSpaceCenter();
  877. flDist = UTIL_DistApprox2D( GetAbsOrigin(), center );
  878. vcollide_t *pCollide = modelinfo->GetVCollide( pEnt->GetModelIndex() );
  879. if ( pCollide == NULL )
  880. continue;
  881. if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  882. continue;
  883. if ( pPhysObj->IsMoveable() == false )
  884. continue;
  885. if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
  886. pEnt->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  887. continue;
  888. if ( center.z > EyePosition().z )
  889. continue;
  890. if ( flDist >= flNearestDist )
  891. continue;
  892. if ( FVisible( pEnt ) == false )
  893. continue;
  894. pNearest = pEnt;
  895. flNearestDist = flDist;
  896. }
  897. m_bHasObject = false;
  898. m_hPhysicsEnt = pNearest;
  899. if ( dog_debug.GetBool() == true )
  900. {
  901. if ( pNearest )
  902. NDebugOverlay::Box( pNearest->WorldSpaceCenter(), pNearest->CollisionProp()->OBBMins(), pNearest->CollisionProp()->OBBMaxs(), 255, 0, 255, true, 3 );
  903. }
  904. if( m_hPhysicsEnt == NULL )
  905. {
  906. return false;
  907. }
  908. else
  909. {
  910. return true;
  911. }
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Can me enemy see me?
  915. //-----------------------------------------------------------------------------
  916. bool CNPC_Dog::CanTargetSeeMe( void )
  917. {
  918. CBaseEntity *pEntity = m_hThrowTarget;
  919. if ( pEntity )
  920. {
  921. if ( pEntity->IsPlayer() == false )
  922. return true;
  923. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( pEntity );
  924. if ( pPlayer )
  925. {
  926. if ( m_hPhysicsEnt )
  927. {
  928. if ( pPlayer->FVisible( m_hPhysicsEnt ) == false )
  929. return false;
  930. }
  931. if ( pPlayer->FInViewCone( this ) )
  932. {
  933. return true;
  934. }
  935. }
  936. }
  937. return false;
  938. }
  939. //---------------------------------------------------------
  940. //---------------------------------------------------------
  941. void CNPC_Dog::RunTask( const Task_t *pTask )
  942. {
  943. switch( pTask->iTask )
  944. {
  945. case TASK_DOG_PICKUP_ITEM:
  946. {
  947. PullObject( false );
  948. }
  949. break;
  950. case TASK_DOG_GET_PATH_TO_PHYSOBJ:
  951. {
  952. //Check this cause our object might have been deleted.
  953. if ( m_hPhysicsEnt == NULL )
  954. FindPhysicsObject( NULL );
  955. //And if we still can't find anything, then just go away.
  956. if ( m_hPhysicsEnt == NULL )
  957. {
  958. TaskFail( "Can't find an object I like!" );
  959. return;
  960. }
  961. IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
  962. Vector vecGoalPos;
  963. Vector vecDir;
  964. vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
  965. VectorNormalize(vecDir);
  966. vecDir.z = 0;
  967. if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
  968. m_hPhysicsEnt->SetOwnerEntity( this );
  969. if ( pPhysicsObject )
  970. pPhysicsObject->RecheckCollisionFilter();
  971. vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );
  972. bool bBuiltRoute = false;
  973. //If I'm near my goal, then just walk to it.
  974. Activity aActivity = ACT_RUN;
  975. if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
  976. aActivity = ACT_WALK;
  977. bBuiltRoute = GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL );
  978. if ( bBuiltRoute == true )
  979. TaskComplete();
  980. else
  981. {
  982. m_flTimeToCatch = gpGlobals->curtime + 0.1;
  983. m_flNextRouteTime = gpGlobals->curtime + 0.3;
  984. m_flNextSwat = gpGlobals->curtime + 0.1;
  985. if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 )
  986. m_hUnreachableObjects.AddToTail( m_hPhysicsEnt );
  987. m_hPhysicsEnt = NULL;
  988. GetNavigator()->ClearGoal();
  989. }
  990. }
  991. break;
  992. case TASK_WAIT:
  993. {
  994. if ( IsWaitFinished() )
  995. {
  996. TaskComplete();
  997. }
  998. if ( m_hPhysicsEnt )
  999. {
  1000. if ( m_bHasObject == false )
  1001. {
  1002. GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() );
  1003. GetMotor()->UpdateYaw();
  1004. }
  1005. }
  1006. break;
  1007. }
  1008. case TASK_DOG_LAUNCH_ITEM:
  1009. if( IsActivityFinished() )
  1010. {
  1011. if ( m_hPhysicsEnt )
  1012. {
  1013. m_hPhysicsEnt->SetOwnerEntity( NULL );
  1014. }
  1015. TaskComplete();
  1016. }
  1017. break;
  1018. case TASK_DOG_WAIT_FOR_TARGET_TO_FACE:
  1019. {
  1020. if ( CanTargetSeeMe() )
  1021. TaskComplete();
  1022. }
  1023. break;
  1024. case TASK_WAIT_FOR_MOVEMENT:
  1025. {
  1026. if ( GetState() == NPC_STATE_SCRIPT || IsInAScript() )
  1027. {
  1028. BaseClass::RunTask( pTask );
  1029. return;
  1030. }
  1031. if ( m_hPhysicsEnt != NULL )
  1032. {
  1033. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  1034. if ( !pPhysObj )
  1035. {
  1036. Warning( "npc_dog TASK_WAIT_FOR_MOVEMENT with NULL m_hPhysicsEnt->VPhysicsGetObject\n" );
  1037. }
  1038. if ( pPhysObj && pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  1039. TaskFail( "Player picked it up!" );
  1040. //If the object is moving then my old goal might not be valid
  1041. //cancel the schedule and make it restart again in a bit.
  1042. if ( pPhysObj && pPhysObj->IsAsleep() == false && GetNavigator()->IsGoalActive() == false )
  1043. {
  1044. Vector vecGoalPos;
  1045. Vector vecDir;
  1046. vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
  1047. VectorNormalize(vecDir);
  1048. vecDir.z = 0;
  1049. vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );
  1050. GetNavigator()->ClearGoal();
  1051. float flDistance = (vecGoalPos - GetLocalOrigin()).Length();
  1052. //If I'm near my goal, then just walk to it.
  1053. Activity aActivity = ACT_RUN;
  1054. if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
  1055. aActivity = ACT_WALK;
  1056. GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL );
  1057. if ( flDistance <= DOG_PHYSOBJ_MOVE_TO_DIST )
  1058. {
  1059. TaskComplete();
  1060. GetNavigator()->StopMoving();
  1061. }
  1062. }
  1063. }
  1064. BaseClass::RunTask( pTask );
  1065. }
  1066. break;
  1067. case TASK_DOG_WAIT_FOR_OBJECT:
  1068. {
  1069. if ( m_hPhysicsEnt != NULL )
  1070. {
  1071. if ( FVisible( m_hPhysicsEnt ) == false )
  1072. {
  1073. m_flTimeToCatch = 0.0f;
  1074. ClearBeams();
  1075. TaskFail( "Lost sight of the object!" );
  1076. m_hPhysicsEnt->SetOwnerEntity( NULL );
  1077. return;
  1078. }
  1079. m_hPhysicsEnt->SetOwnerEntity( this );
  1080. Vector vForward;
  1081. AngleVectors( GetAbsAngles(), &vForward );
  1082. Vector vGunPos;
  1083. GetAttachment( m_iPhysGunAttachment, vGunPos );
  1084. Vector vToObject = m_hPhysicsEnt->WorldSpaceCenter() - vGunPos;
  1085. float flDistance = vToObject.Length();
  1086. VectorNormalize( vToObject );
  1087. SetAim( m_hPhysicsEnt->WorldSpaceCenter() - GetAbsOrigin() );
  1088. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  1089. float flDistanceToPlayer = flDistance;
  1090. if ( pPlayer )
  1091. {
  1092. flDistanceToPlayer = (pPlayer->GetAbsOrigin() - m_hPhysicsEnt->WorldSpaceCenter()).Length();
  1093. }
  1094. IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();
  1095. if ( !pPhysObj )
  1096. {
  1097. Warning( "npc_dog: TASK_DOG_WAIT_FOR_OBJECT with m_hPhysicsEnt->VPhysicsGetObject == NULL\n" );
  1098. }
  1099. if ( pPhysObj && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) && flDistanceToPlayer > ( flDistance * 2 ) )
  1100. {
  1101. if ( m_flTimeToPull <= gpGlobals->curtime )
  1102. {
  1103. Vector vCurrentVel;
  1104. float flCurrentVel;
  1105. AngularImpulse vCurrentAI;
  1106. pPhysObj->GetVelocity( &vCurrentVel, &vCurrentAI );
  1107. flCurrentVel = vCurrentVel.Length();
  1108. VectorNormalize( vCurrentVel );
  1109. if ( pPhysObj && flDistance <= DOG_PULL_DISTANCE )
  1110. {
  1111. Vector vDir = ( vGunPos - m_hPhysicsEnt->WorldSpaceCenter() );
  1112. VectorNormalize( vDir );
  1113. vCurrentVel = vCurrentVel * ( flCurrentVel * DOG_PULL_VELOCITY_MOD );
  1114. vCurrentAI = vCurrentAI * DOG_PULL_ANGULARIMP_MOD;
  1115. pPhysObj->SetVelocity( &vCurrentVel, &vCurrentAI );
  1116. vDir = vDir * flDistance * DOG_PULL_TO_GUN_VEL_MOD;
  1117. Vector vAngle( 0, 0, 0 );
  1118. pPhysObj->AddVelocity( &vDir, &vAngle );
  1119. CreateBeams();
  1120. }
  1121. float flDot = DotProduct( vCurrentVel, vForward );
  1122. if ( flDistance >= DOG_PULL_DISTANCE && flDistance <= ( DOG_PULL_DISTANCE * 2 ) && flDot > -0.3 )
  1123. {
  1124. if ( pPhysObj->IsAsleep() == false && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
  1125. {
  1126. Vector vecGoalPos;
  1127. Vector vecDir;
  1128. vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
  1129. VectorNormalize(vecDir);
  1130. vecDir.z = 0;
  1131. vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );
  1132. GetNavigator()->ClearGoal();
  1133. //If I'm near my goal, then just walk to it.
  1134. Activity aActivity = ACT_RUN;
  1135. if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
  1136. aActivity = ACT_WALK;
  1137. GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL );
  1138. }
  1139. }
  1140. }
  1141. }
  1142. float flDirDot = DotProduct( vToObject, vForward );
  1143. if ( flDirDot < 0.2 )
  1144. {
  1145. GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() );
  1146. GetMotor()->UpdateYaw();
  1147. }
  1148. if ( m_flTimeToCatch < gpGlobals->curtime && m_bDoWaitforObjectBehavior == false )
  1149. {
  1150. m_hPhysicsEnt->SetOwnerEntity( NULL );
  1151. m_flTimeToCatch = 0.0f;
  1152. ClearBeams();
  1153. TaskFail( "Done waiting!" );
  1154. }
  1155. else if ( pPhysObj && ( flDistance <= DOG_CATCH_DISTANCE && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) )
  1156. {
  1157. AngularImpulse vZero( 0, 0, 0 );
  1158. pPhysObj->SetVelocity( &vec3_origin, &vZero );
  1159. GetNavigator()->StopMoving();
  1160. //Fire Output!
  1161. m_OnCatch.FireOutput( this, this );
  1162. m_bHasObject = true;
  1163. ClearBeams();
  1164. TaskComplete();
  1165. }
  1166. }
  1167. else
  1168. {
  1169. GetNavigator()->StopMoving();
  1170. ClearBeams();
  1171. TaskFail("No Physics Object!");
  1172. }
  1173. }
  1174. break;
  1175. case TASK_DOG_CATCH_OBJECT:
  1176. if( IsActivityFinished() )
  1177. {
  1178. m_flTimeToCatch = 0.0f;
  1179. TaskComplete();
  1180. }
  1181. break;
  1182. default:
  1183. BaseClass::RunTask( pTask );
  1184. break;
  1185. }
  1186. }
  1187. void CNPC_Dog::SetupThrowTarget( void )
  1188. {
  1189. if ( m_hThrowTarget == NULL )
  1190. {
  1191. m_hThrowTarget = AI_GetSinglePlayer();
  1192. }
  1193. SetTarget( m_hThrowTarget );
  1194. }
  1195. //---------------------------------------------------------
  1196. //---------------------------------------------------------
  1197. void CNPC_Dog::StartTask( const Task_t *pTask )
  1198. {
  1199. switch( pTask->iTask )
  1200. {
  1201. case TASK_DOG_SETUP_THROW_TARGET:
  1202. {
  1203. SetupThrowTarget();
  1204. TaskComplete();
  1205. }
  1206. break;
  1207. case TASK_DOG_GET_PATH_TO_PHYSOBJ:
  1208. {
  1209. FindPhysicsObject( STRING( m_sObjectName ) );
  1210. if ( m_hPhysicsEnt == NULL )
  1211. {
  1212. FindPhysicsObject( NULL );
  1213. return;
  1214. }
  1215. IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
  1216. Vector vecGoalPos;
  1217. Vector vecDir;
  1218. vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
  1219. VectorNormalize(vecDir);
  1220. vecDir.z = 0;
  1221. if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
  1222. m_hPhysicsEnt->SetOwnerEntity( this );
  1223. if ( pPhysicsObject )
  1224. pPhysicsObject->RecheckCollisionFilter();
  1225. vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );
  1226. //If I'm near my goal, then just walk to it.
  1227. Activity aActivity = ACT_RUN;
  1228. if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
  1229. aActivity = ACT_WALK;
  1230. if ( GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ) == false )
  1231. {
  1232. if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 )
  1233. m_hUnreachableObjects.AddToTail( m_hPhysicsEnt );
  1234. FindPhysicsObject( NULL, m_hPhysicsEnt );
  1235. m_flTimeToCatch = gpGlobals->curtime + 0.1;
  1236. m_flNextRouteTime = gpGlobals->curtime + 0.3;
  1237. m_flNextSwat = gpGlobals->curtime + 0.1;
  1238. GetNavigator()->ClearGoal();
  1239. }
  1240. else
  1241. {
  1242. TaskComplete();
  1243. }
  1244. }
  1245. break;
  1246. case TASK_DOG_FACE_OBJECT:
  1247. {
  1248. if( m_hPhysicsEnt == NULL )
  1249. {
  1250. // Physics Object is gone! Probably was an explosive
  1251. // or something else broke it.
  1252. TaskFail("Physics ent NULL");
  1253. return;
  1254. }
  1255. Vector vecDir;
  1256. vecDir = m_hPhysicsEnt->WorldSpaceCenter() - GetLocalOrigin();
  1257. VectorNormalize(vecDir);
  1258. GetMotor()->SetIdealYaw( UTIL_VecToYaw( vecDir ) );
  1259. TaskComplete();
  1260. }
  1261. break;
  1262. case TASK_DOG_PICKUP_ITEM:
  1263. {
  1264. if( m_hPhysicsEnt == NULL )
  1265. {
  1266. // Physics Object is gone! Probably was an explosive
  1267. // or something else broke it.
  1268. TaskFail("Physics ent NULL");
  1269. return;
  1270. }
  1271. else
  1272. {
  1273. SetIdealActivity( (Activity)ACT_DOG_PICKUP );
  1274. }
  1275. }
  1276. break;
  1277. case TASK_DOG_LAUNCH_ITEM:
  1278. {
  1279. if( m_hPhysicsEnt == NULL )
  1280. {
  1281. // Physics Object is gone! Probably was an explosive
  1282. // or something else broke it.
  1283. TaskFail("Physics ent NULL");
  1284. return;
  1285. }
  1286. else
  1287. {
  1288. if ( m_hPhysicsEnt == NULL || m_bHasObject == false )
  1289. {
  1290. TaskFail( "Don't have the item!" );
  1291. return;
  1292. }
  1293. SetIdealActivity( (Activity)ACT_DOG_THROW );
  1294. }
  1295. }
  1296. break;
  1297. case TASK_DOG_WAIT_FOR_TARGET_TO_FACE:
  1298. {
  1299. if ( CanTargetSeeMe() )
  1300. TaskComplete();
  1301. }
  1302. break;
  1303. case TASK_DOG_WAIT_FOR_OBJECT:
  1304. {
  1305. SetIdealActivity( (Activity)ACT_DOG_WAITING );
  1306. }
  1307. break;
  1308. case TASK_DOG_CATCH_OBJECT:
  1309. {
  1310. SetIdealActivity( (Activity)ACT_DOG_CATCH );
  1311. }
  1312. break;
  1313. case TASK_DOG_DELAY_SWAT:
  1314. m_flNextSwat = gpGlobals->curtime + pTask->flTaskData;
  1315. if ( m_hThrowTarget == NULL )
  1316. m_hThrowTarget = AI_GetSinglePlayer();
  1317. TaskComplete();
  1318. break;
  1319. default:
  1320. BaseClass::StartTask( pTask );
  1321. }
  1322. }
  1323. void CNPC_Dog::InputTurnBoneFollowersOff( inputdata_t &inputdata )
  1324. {
  1325. if ( m_bBoneFollowersActive )
  1326. {
  1327. m_bBoneFollowersActive = false;
  1328. m_BoneFollowerManager.DestroyBoneFollowers();
  1329. }
  1330. }
  1331. void CNPC_Dog::InputTurnBoneFollowersOn( inputdata_t &inputdata )
  1332. {
  1333. if ( !m_bBoneFollowersActive )
  1334. {
  1335. m_bBoneFollowersActive = true;
  1336. m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pFollowerBoneNames), pFollowerBoneNames );
  1337. }
  1338. }
  1339. AI_BEGIN_CUSTOM_NPC( npc_dog, CNPC_Dog )
  1340. DECLARE_USES_SCHEDULE_PROVIDER( CAI_FollowBehavior )
  1341. DECLARE_ACTIVITY( ACT_DOG_THROW )
  1342. DECLARE_ACTIVITY( ACT_DOG_PICKUP )
  1343. DECLARE_ACTIVITY( ACT_DOG_WAITING )
  1344. DECLARE_ACTIVITY( ACT_DOG_CATCH )
  1345. DECLARE_CONDITION( COND_DOG_LOST_PHYSICS_ENTITY )
  1346. DECLARE_TASK( TASK_DOG_DELAY_SWAT )
  1347. DECLARE_TASK( TASK_DOG_GET_PATH_TO_PHYSOBJ )
  1348. DECLARE_TASK( TASK_DOG_LAUNCH_ITEM )
  1349. DECLARE_TASK( TASK_DOG_PICKUP_ITEM )
  1350. DECLARE_TASK( TASK_DOG_FACE_OBJECT )
  1351. DECLARE_TASK( TASK_DOG_WAIT_FOR_OBJECT )
  1352. DECLARE_TASK( TASK_DOG_CATCH_OBJECT )
  1353. DECLARE_TASK( TASK_DOG_WAIT_FOR_TARGET_TO_FACE )
  1354. DECLARE_TASK( TASK_DOG_SETUP_THROW_TARGET )
  1355. DECLARE_ANIMEVENT( AE_DOG_THROW )
  1356. DECLARE_ANIMEVENT( AE_DOG_PICKUP )
  1357. DECLARE_ANIMEVENT( AE_DOG_CATCH )
  1358. DECLARE_ANIMEVENT( AE_DOG_PICKUP_NOEFFECT )
  1359. DEFINE_SCHEDULE
  1360. (
  1361. SCHED_DOG_FIND_OBJECT,
  1362. " Tasks"
  1363. " TASK_DOG_DELAY_SWAT 3"
  1364. " TASK_DOG_GET_PATH_TO_PHYSOBJ 0"
  1365. " TASK_RUN_PATH 0"
  1366. " TASK_WAIT_FOR_MOVEMENT 0"
  1367. " TASK_DOG_FACE_OBJECT 0"
  1368. " TASK_FACE_IDEAL 0"
  1369. " TASK_DOG_PICKUP_ITEM 0"
  1370. " TASK_DOG_SETUP_THROW_TARGET 0"
  1371. " TASK_FACE_TARGET 0.5"
  1372. " TASK_DOG_WAIT_FOR_TARGET_TO_FACE 0"
  1373. " TASK_DOG_LAUNCH_ITEM 0"
  1374. ""
  1375. " Interrupts"
  1376. " COND_DOG_LOST_PHYSICS_ENTITY"
  1377. )
  1378. DEFINE_SCHEDULE
  1379. (
  1380. SCHED_DOG_WAIT_THROW_OBJECT,
  1381. " Tasks"
  1382. " TASK_DOG_SETUP_THROW_TARGET 0"
  1383. " TASK_FACE_TARGET 0.5"
  1384. " TASK_DOG_WAIT_FOR_TARGET_TO_FACE 0"
  1385. " TASK_DOG_LAUNCH_ITEM 0"
  1386. ""
  1387. " Interrupts"
  1388. " COND_DOG_LOST_PHYSICS_ENTITY"
  1389. )
  1390. DEFINE_SCHEDULE
  1391. (
  1392. SCHED_DOG_CATCH_OBJECT,
  1393. " Tasks"
  1394. " TASK_DOG_WAIT_FOR_OBJECT 0"
  1395. " TASK_DOG_CATCH_OBJECT 0"
  1396. " TASK_FACE_PLAYER 0.5"
  1397. " TASK_DOG_WAIT_FOR_TARGET_TO_FACE 0"
  1398. " TASK_DOG_LAUNCH_ITEM 0"
  1399. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_IDLE_STAND"
  1400. ""
  1401. " Interrupts"
  1402. " COND_DOG_LOST_PHYSICS_ENTITY"
  1403. )
  1404. AI_END_CUSTOM_NPC()