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.

3123 lines
82 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tier1/utllinkedlist.h"
  8. #include "bitstring.h"
  9. #include "utlvector.h"
  10. #include "ai_navigator.h"
  11. #include "scripted.h"
  12. #include "ai_hint.h"
  13. #include "ai_behavior_follow.h"
  14. #include "ai_memory.h"
  15. #include "ai_squad.h"
  16. #include "ai_tacticalservices.h"
  17. #include "ndebugoverlay.h"
  18. #include "ai_senses.h"
  19. #ifdef HL2_EPISODIC
  20. #include "info_darknessmode_lightsource.h"
  21. #endif
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. ConVar ai_debug_follow( "ai_debug_follow", "0" );
  25. ConVar ai_follow_use_points( "ai_follow_use_points", "1" );
  26. ConVar ai_follow_use_points_when_moving( "ai_follow_use_points_when_moving", "1" );
  27. #define FollowMsg(s) if ( !GetOuter() || !ai_debug_follow.GetBool() ) ; else DevMsg( GetOuter(), "Follow: " s )
  28. #define WAIT_HINT_MIN_DIST (16*16) // Was: Square(GetHullWidth())
  29. //-----------------------------------------------------------------------------
  30. //
  31. // Purpose: Formation management
  32. //
  33. // Right now, this is in a very preliminary sketch state. (toml 03-03-03)
  34. //-----------------------------------------------------------------------------
  35. struct AI_FollowSlot_t;
  36. struct AI_FollowFormation_t;
  37. struct AI_FollowGroup_t;
  38. struct AI_Follower_t
  39. {
  40. AI_Follower_t()
  41. {
  42. slot = -1;
  43. memset( &navInfo, 0, sizeof(navInfo) );
  44. pGroup = NULL;
  45. }
  46. AIHANDLE hFollower;
  47. int slot;
  48. AI_FollowNavInfo_t navInfo;
  49. AI_FollowGroup_t * pGroup; // backpointer for efficiency
  50. };
  51. struct AI_FollowGroup_t
  52. {
  53. AI_FollowFormation_t * pFormation;
  54. EHANDLE hFollowTarget;
  55. CUtlFixedLinkedList<AI_Follower_t> followers;
  56. CVarBitVec slotUsage;
  57. };
  58. //-------------------------------------
  59. class CAI_FollowManager
  60. {
  61. public:
  62. ~CAI_FollowManager()
  63. {
  64. for ( int i = 0; i < m_groups.Count(); i++ )
  65. delete m_groups[i];
  66. }
  67. bool AddFollower( CBaseEntity *pTarget, CAI_BaseNPC *pFollower, AI_Formations_t formation, AI_FollowManagerInfoHandle_t *pHandle );
  68. void ChangeFormation( AI_FollowManagerInfoHandle_t &handle, AI_Formations_t formation );
  69. void RemoveFollower( AI_FollowManagerInfoHandle_t &handle );
  70. bool CalcFollowPosition( AI_FollowManagerInfoHandle_t &handle, AI_FollowNavInfo_t *pNavInfo );
  71. int CountFollowersInGroup( CAI_BaseNPC *pMember )
  72. {
  73. AI_FollowGroup_t *pGroup = FindFollowerGroup( pMember );
  74. if( !pGroup )
  75. {
  76. return 0;
  77. }
  78. return pGroup->followers.Count();
  79. }
  80. int CountFollowers( CBaseEntity *pFollowTarget, string_t iszClassname )
  81. {
  82. AI_FollowGroup_t *pGroup = FindGroup( pFollowTarget );
  83. if( !pGroup )
  84. {
  85. return 0;
  86. }
  87. if ( iszClassname == NULL_STRING )
  88. {
  89. return pGroup->followers.Count();
  90. }
  91. else
  92. {
  93. int result = 0;
  94. for ( int i = pGroup->followers.Head(); i != pGroup->followers.InvalidIndex(); i = pGroup->followers.Next( i ) )
  95. {
  96. if ( pGroup->followers[i].hFollower && pGroup->followers[i].hFollower->ClassMatches( iszClassname ) )
  97. {
  98. result++;
  99. }
  100. }
  101. return result;
  102. }
  103. }
  104. int GetFollowerSlot( CAI_BaseNPC *pFollower )
  105. {
  106. AI_FollowGroup_t *pGroup = FindFollowerGroup( pFollower );
  107. if( !pGroup )
  108. {
  109. return 0;
  110. }
  111. int h = pGroup->followers.Head();
  112. while( h != pGroup->followers.InvalidIndex() )
  113. {
  114. AI_Follower_t *it = &pGroup->followers[h];
  115. if ( it->hFollower.Get() == pFollower )
  116. {
  117. return it->slot;
  118. }
  119. h = pGroup->followers.Next( h );
  120. }
  121. return 0;
  122. }
  123. private:
  124. bool RedistributeSlots( AI_FollowGroup_t *pGroup );
  125. int FindBestSlot( AI_FollowGroup_t *pGroup );
  126. void CalculateFieldsFromSlot( AI_FollowSlot_t *pSlot, AI_FollowNavInfo_t *pFollowerInfo );
  127. AI_FollowGroup_t *FindCreateGroup( CBaseEntity *pTarget, AI_Formations_t formation );
  128. AI_FollowGroup_t *FindGroup( CBaseEntity *pTarget );
  129. AI_FollowGroup_t *FindFollowerGroup( CBaseEntity *pFollower );
  130. void RemoveGroup( AI_FollowGroup_t * );
  131. //---------------------------------
  132. CUtlVector<AI_FollowGroup_t *> m_groups;
  133. };
  134. //-------------------------------------
  135. CAI_FollowManager g_AIFollowManager;
  136. //-----------------------------------------------------------------------------
  137. int AIGetNumFollowers( CBaseEntity *pEntity, string_t iszClassname )
  138. {
  139. return g_AIFollowManager.CountFollowers( pEntity, iszClassname );
  140. }
  141. //-----------------------------------------------------------------------------
  142. //
  143. // CAI_FollowBehavior
  144. //
  145. //-----------------------------------------------------------------------------
  146. BEGIN_SIMPLE_DATADESC( AI_FollowNavInfo_t )
  147. DEFINE_FIELD( flags, FIELD_INTEGER ),
  148. DEFINE_FIELD( position, FIELD_POSITION_VECTOR ),
  149. DEFINE_FIELD( range, FIELD_FLOAT ),
  150. DEFINE_FIELD( Zrange, FIELD_FLOAT ),
  151. DEFINE_FIELD( tolerance, FIELD_FLOAT ),
  152. DEFINE_FIELD( followPointTolerance, FIELD_FLOAT ),
  153. DEFINE_FIELD( targetMoveTolerance, FIELD_FLOAT ),
  154. DEFINE_FIELD( repathOnRouteTolerance, FIELD_FLOAT ),
  155. DEFINE_FIELD( walkTolerance, FIELD_FLOAT ),
  156. DEFINE_FIELD( coverTolerance, FIELD_FLOAT ),
  157. DEFINE_FIELD( enemyLOSTolerance, FIELD_FLOAT ),
  158. DEFINE_FIELD( chaseEnemyTolerance, FIELD_FLOAT ),
  159. END_DATADESC();
  160. BEGIN_SIMPLE_DATADESC( AI_FollowParams_t )
  161. DEFINE_FIELD( formation, FIELD_INTEGER ),
  162. DEFINE_FIELD( bNormalMemoryDiscard, FIELD_BOOLEAN ),
  163. END_DATADESC();
  164. BEGIN_DATADESC( CAI_FollowBehavior )
  165. DEFINE_FIELD( m_hFollowTarget, FIELD_EHANDLE ),
  166. DEFINE_EMBEDDED( m_FollowNavGoal ),
  167. DEFINE_FIELD( m_flTimeUpdatedFollowPosition, FIELD_TIME ),
  168. DEFINE_FIELD( m_bFirstFacing, FIELD_BOOLEAN ),
  169. DEFINE_FIELD( m_flTimeFollowTargetVisible, FIELD_TIME ),
  170. DEFINE_EMBEDDED( m_TargetMonitor ),
  171. DEFINE_FIELD( m_bTargetUnreachable, FIELD_BOOLEAN ),
  172. DEFINE_FIELD( m_bFollowNavFailed, FIELD_BOOLEAN ),
  173. DEFINE_FIELD( m_bMovingToCover, FIELD_BOOLEAN ),
  174. DEFINE_FIELD( m_flOriginalEnemyDiscardTime, FIELD_FLOAT ),
  175. DEFINE_FIELD( m_SavedDistTooFar, FIELD_FLOAT ),
  176. DEFINE_EMBEDDED( m_FollowDelay ),
  177. DEFINE_EMBEDDED( m_RepathOnFollowTimer ),
  178. DEFINE_CUSTOM_FIELD( m_CurrentFollowActivity, ActivityDataOps() ),
  179. DEFINE_EMBEDDED( m_TimeBlockUseWaitPoint ),
  180. DEFINE_EMBEDDED( m_TimeCheckForWaitPoint ),
  181. DEFINE_FIELD( m_pInterruptWaitPoint, FIELD_CLASSPTR ),
  182. DEFINE_EMBEDDED( m_TimeBeforeSpreadFacing ),
  183. DEFINE_EMBEDDED( m_TimeNextSpreadFacing ),
  184. // m_hFollowManagerInfo (reset on load)
  185. DEFINE_EMBEDDED( m_params ),
  186. DEFINE_FIELD( m_hFollowGoalEnt, FIELD_EHANDLE ),
  187. DEFINE_FIELD( m_nFailedFollowAttempts, FIELD_INTEGER ),
  188. DEFINE_FIELD( m_flTimeFailFollowStarted, FIELD_TIME ),
  189. DEFINE_FIELD( m_vFollowMoveAnchor, FIELD_POSITION_VECTOR ),
  190. END_DATADESC();
  191. //-------------------------------------
  192. CAI_FollowBehavior::CAI_FollowBehavior( const AI_FollowParams_t &params )
  193. {
  194. memset( &m_FollowNavGoal, 0, sizeof( m_FollowNavGoal ) );
  195. m_FollowDelay.Set( 1.0, 3.0 );
  196. m_hFollowManagerInfo.m_pGroup = NULL;
  197. m_hFollowManagerInfo.m_hFollower = 0;
  198. m_TimeBlockUseWaitPoint.Set( 0.5, 1.5 );
  199. m_TimeCheckForWaitPoint.Set( 1.0 );
  200. m_pInterruptWaitPoint = NULL;
  201. m_TimeBeforeSpreadFacing.Set( 2.0, 4.0 );
  202. m_TimeNextSpreadFacing.Set( 3.0, 12.0 );
  203. m_params = params;
  204. NoteSuccessfulFollow();
  205. }
  206. //-------------------------------------
  207. CAI_FollowBehavior::~CAI_FollowBehavior()
  208. {
  209. Assert( !m_hFollowManagerInfo.m_pGroup );
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Draw any text overlays
  213. // Input : Previous text offset from the top
  214. // Output : Current text offset from the top
  215. //-----------------------------------------------------------------------------
  216. int CAI_FollowBehavior::DrawDebugTextOverlays( int text_offset )
  217. {
  218. char tempstr[ 512 ];
  219. int offset;
  220. CBaseEntity * followEnt;
  221. offset = BaseClass::DrawDebugTextOverlays( text_offset );
  222. if ( GetOuter()->m_debugOverlays & OVERLAY_TEXT_BIT )
  223. {
  224. followEnt = GetFollowTarget();
  225. if ( followEnt != NULL )
  226. {
  227. Q_snprintf( tempstr, sizeof(tempstr), "Follow: (%d) %s (%s)", followEnt->entindex(), followEnt->GetDebugName(), followEnt->GetClassname() );
  228. }
  229. else
  230. {
  231. Q_snprintf( tempstr, sizeof(tempstr), "Follow: NULL" );
  232. }
  233. GetOuter()->EntityText( offset, tempstr, 0 );
  234. offset++;
  235. }
  236. return offset;
  237. }
  238. void CAI_FollowBehavior::DrawDebugGeometryOverlays()
  239. {
  240. if ( GetFollowTarget() )
  241. {
  242. Vector vecFollowPos = GetGoalPosition();
  243. NDebugOverlay::HorzArrow( GetOuter()->GetAbsOrigin(), vecFollowPos, 16.0f, 0, 255, 0, 0, true, 0 );
  244. }
  245. }
  246. //-------------------------------------
  247. void CAI_FollowBehavior::SetParameters( const AI_FollowParams_t &params )
  248. {
  249. m_params = params;
  250. if ( m_hFollowManagerInfo.m_pGroup )
  251. {
  252. g_AIFollowManager.ChangeFormation( m_hFollowManagerInfo, params.formation );
  253. m_flTimeUpdatedFollowPosition = 0;
  254. }
  255. }
  256. //-------------------------------------
  257. CBaseEntity * CAI_FollowBehavior::GetFollowTarget()
  258. {
  259. return m_hFollowTarget;
  260. }
  261. //-------------------------------------
  262. // Returns true if the NPC is actively following a target.
  263. bool CAI_FollowBehavior::IsActive( void )
  264. {
  265. if ( IsRunning() && GetFollowTarget() )
  266. {
  267. // Only true if we're running a follow schedule
  268. return IsCurScheduleFollowSchedule();
  269. }
  270. return false;
  271. }
  272. //-------------------------------------
  273. void CAI_FollowBehavior::SetFollowTarget( CBaseEntity *pLeader, bool fFinishCurSchedule )
  274. {
  275. if ( pLeader == m_hFollowTarget )
  276. return;
  277. if ( !GetOuter()->IsAlive() )
  278. {
  279. return;
  280. }
  281. m_flTimeUpdatedFollowPosition = 0;
  282. if ( m_hFollowTarget )
  283. {
  284. g_AIFollowManager.RemoveFollower( m_hFollowManagerInfo );
  285. m_hFollowTarget = NULL;
  286. m_hFollowManagerInfo.m_pGroup = NULL;
  287. if ( IsRunning() )
  288. {
  289. if ( GetNavigator()->GetGoalType() == GOALTYPE_TARGETENT )
  290. {
  291. GetNavigator()->StopMoving(); // Stop him from walking toward the player
  292. }
  293. if ( GetEnemy() != NULL )
  294. {
  295. GetOuter()->SetIdealState( NPC_STATE_COMBAT );
  296. }
  297. }
  298. }
  299. if ( pLeader )
  300. {
  301. if ( g_AIFollowManager.AddFollower( pLeader, GetOuter(), m_params.formation, &m_hFollowManagerInfo ) )
  302. {
  303. m_hFollowTarget = pLeader;
  304. m_bFirstFacing = true;
  305. m_flTimeFollowTargetVisible = 0;
  306. SetCondition( COND_TARGET_MOVED_FROM_MARK );
  307. m_TargetMonitor.ClearMark();
  308. NoteSuccessfulFollow();
  309. }
  310. }
  311. NotifyChangeBehaviorStatus(fFinishCurSchedule);
  312. }
  313. //-------------------------------------
  314. void CAI_FollowBehavior::SetFollowGoalDirect( CAI_FollowGoal *pGoal )
  315. {
  316. m_hFollowGoalEnt = pGoal;
  317. m_flTimeUpdatedFollowPosition = 0;
  318. }
  319. //-------------------------------------
  320. bool CAI_FollowBehavior::SetFollowGoal( CAI_FollowGoal *pGoal, bool fFinishCurSchedule )
  321. {
  322. if ( GetOuter()->ShouldAcceptGoal( this, pGoal ) )
  323. {
  324. GetOuter()->ClearCommandGoal();
  325. if( hl2_episodic.GetBool() )
  326. {
  327. // Poke the NPC to interrupt any stubborn schedules
  328. GetOuter()->SetCondition(COND_PROVOKED);
  329. }
  330. SetFollowTarget( pGoal->GetGoalEntity() );
  331. Assert( pGoal->m_iFormation == AIF_SIMPLE || pGoal->m_iFormation == AIF_WIDE || pGoal->m_iFormation == AIF_MEDIUM || pGoal->m_iFormation == AIF_SIDEKICK || pGoal->m_iFormation == AIF_VORTIGAUNT );
  332. SetParameters( AI_FollowParams_t( (AI_Formations_t)pGoal->m_iFormation ) );
  333. m_hFollowGoalEnt = pGoal;
  334. m_flTimeUpdatedFollowPosition = 0;
  335. return true;
  336. }
  337. return false;
  338. }
  339. //-------------------------------------
  340. void CAI_FollowBehavior::ClearFollowGoal( CAI_FollowGoal *pGoal )
  341. {
  342. GetOuter()->OnClearGoal( this, pGoal );
  343. if ( pGoal == m_hFollowGoalEnt )
  344. {
  345. SetFollowTarget( NULL );
  346. m_hFollowGoalEnt = NULL;
  347. m_flTimeUpdatedFollowPosition = 0;
  348. }
  349. }
  350. //-------------------------------------
  351. bool CAI_FollowBehavior::UpdateFollowPosition()
  352. {
  353. AI_PROFILE_SCOPE( CAI_FollowBehavior_UpdateFollowPosition );
  354. if ( m_flTimeUpdatedFollowPosition == gpGlobals->curtime )
  355. {
  356. return true;
  357. }
  358. if (m_hFollowTarget == NULL)
  359. return false;
  360. if ( !g_AIFollowManager.CalcFollowPosition( m_hFollowManagerInfo, &m_FollowNavGoal ) )
  361. {
  362. return false;
  363. }
  364. CBaseEntity *pFollowTarget = GetFollowTarget();
  365. if ( pFollowTarget->GetParent() )
  366. {
  367. if ( pFollowTarget->GetParent()->GetServerVehicle() )
  368. {
  369. m_FollowNavGoal.targetMoveTolerance *= 1.5;
  370. m_FollowNavGoal.range += pFollowTarget->GetParent()->BoundingRadius() * 0.333;
  371. }
  372. }
  373. #if TODO
  374. // @TODO (toml 07-27-03): this is too simplistic. fails when the new point is an inappropriate target
  375. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(m_hFollowTarget.Get());
  376. Vector targetVelocity = pPlayer->GetSmoothedVelocity();
  377. m_FollowNavGoal.position += targetVelocity * 0.5;
  378. #endif
  379. m_flTimeUpdatedFollowPosition = gpGlobals->curtime;
  380. return true;
  381. }
  382. //-------------------------------------
  383. bool CAI_FollowBehavior::IsMovingToFollowTarget()
  384. {
  385. return ( IsRunning() && ( IsCurSchedule(SCHED_FOLLOW, false) || IsCurSchedule(SCHED_FOLLOWER_GO_TO_WAIT_POINT, false) ) );
  386. }
  387. //-------------------------------------
  388. bool CAI_FollowBehavior::CanSelectSchedule()
  389. {
  390. if ( !GetOuter()->IsInterruptable() )
  391. return false;
  392. if ( !ShouldFollow() )
  393. {
  394. return false;
  395. }
  396. return true;
  397. }
  398. //-------------------------------------
  399. bool CAI_FollowBehavior::PlayerIsPushing()
  400. {
  401. return (m_hFollowTarget && m_hFollowTarget->IsPlayer() && HasCondition( COND_PLAYER_PUSHING ) );
  402. }
  403. //-------------------------------------
  404. bool CAI_FollowBehavior::IsFollowTargetInRange( float rangeMultiplier )
  405. {
  406. if ( !GetFollowTarget()->IsPlayer() && HasCondition( COND_RECEIVED_ORDERS ) )
  407. return false;
  408. if( GetNpcState() == NPC_STATE_COMBAT )
  409. {
  410. if( IsFollowGoalInRange( MAX( m_FollowNavGoal.coverTolerance, m_FollowNavGoal.enemyLOSTolerance ) * rangeMultiplier, GetGoalZRange(), GetGoalFlags() ) )
  411. {
  412. return true;
  413. }
  414. }
  415. else
  416. {
  417. if( IsFollowGoalInRange( MAX( m_FollowNavGoal.tolerance, GetGoalRange() ) * rangeMultiplier, GetGoalZRange(), GetGoalFlags() ) )
  418. {
  419. if ( m_FollowNavGoal.flags & AIFF_REQUIRE_LOS_OUTSIDE_COMBAT )
  420. {
  421. //trace_t tr;
  422. //AI_TraceLOS( vecStart, vecStart + vecDir * 8192, m_hFollowTarget, &tr );
  423. //if ( AI_TraceLOS m_FollowNavGoal.position
  424. if ( !HasCondition(COND_SEE_PLAYER) )
  425. return false;
  426. }
  427. return true;
  428. }
  429. }
  430. return false;
  431. }
  432. //-------------------------------------
  433. bool CAI_FollowBehavior::IsFollowGoalInRange( float tolerance, float zTolerance, int flags )
  434. {
  435. const Vector &origin = WorldSpaceCenter();
  436. const Vector &goal = GetGoalPosition();
  437. if ( zTolerance == -1 )
  438. zTolerance = GetHullHeight();
  439. float distanceSq = ( goal.AsVector2D() - origin.AsVector2D() ).LengthSqr();
  440. tolerance += 0.1;
  441. // Increase Z tolerance slightly as XY distance decreases
  442. float flToleranceSq = (tolerance*tolerance);
  443. float flIncreaseRange = flToleranceSq * 0.25;
  444. zTolerance += zTolerance * clamp((distanceSq / flIncreaseRange), 0.f, 1.f );
  445. if ( fabs( origin.z - goal.z ) > zTolerance )
  446. return false;
  447. if ( distanceSq > flToleranceSq )
  448. return false;
  449. if ( flags & AIFF_REQUIRE_LOS_OUTSIDE_COMBAT && m_hFollowTarget.Get() )
  450. {
  451. if ( !GetOuter()->GetSenses()->DidSeeEntity( m_hFollowTarget ) )
  452. return false;
  453. }
  454. return true;
  455. }
  456. //-------------------------------------
  457. bool CAI_FollowBehavior::IsChaseGoalInRange()
  458. {
  459. if ( GetEnemy() && ( GetEnemy()->WorldSpaceCenter() - m_FollowNavGoal.position ).LengthSqr() > Square( m_FollowNavGoal.chaseEnemyTolerance ) )
  460. return false;
  461. return true;
  462. }
  463. //-------------------------------------
  464. void CAI_FollowBehavior::NoteFailedFollow()
  465. {
  466. m_nFailedFollowAttempts++;
  467. if ( m_flTimeFailFollowStarted == FLT_MAX )
  468. m_flTimeFailFollowStarted = gpGlobals->curtime;
  469. if ( GetOuter() && ai_debug_follow.GetBool() )
  470. DevMsg( GetOuter(), "Follow: NoteFailedFollow() (%d, %f)\n", m_nFailedFollowAttempts, m_flTimeFailFollowStarted );
  471. }
  472. //-------------------------------------
  473. void CAI_FollowBehavior::NoteSuccessfulFollow()
  474. {
  475. m_nFailedFollowAttempts = 0;
  476. m_flTimeFailFollowStarted = FLT_MAX;
  477. FollowMsg( "NoteSuccessfulFollow()\n" );
  478. }
  479. //-------------------------------------
  480. void CAI_FollowBehavior::BeginScheduleSelection()
  481. {
  482. if ( GetOuter()->m_hCine )
  483. GetOuter()->m_hCine->CancelScript();
  484. m_TimeBeforeSpreadFacing.Reset();
  485. SetCondition( COND_TARGET_MOVED_FROM_MARK );
  486. m_TargetMonitor.ClearMark();
  487. NoteSuccessfulFollow();
  488. if ( !m_params.bNormalMemoryDiscard )
  489. {
  490. // Forget about enemies that I haven't seen for >5 seconds
  491. m_flOriginalEnemyDiscardTime = GetOuter()->GetEnemies()->GetEnemyDiscardTime();
  492. GetOuter()->GetEnemies()->SetEnemyDiscardTime( 5.0f );
  493. }
  494. m_SavedDistTooFar = GetOuter()->m_flDistTooFar;
  495. if ( GetFollowTarget() && GetFollowTarget()->IsPlayer() )
  496. {
  497. GetOuter()->m_flDistTooFar = FLT_MAX;
  498. }
  499. BaseClass::BeginScheduleSelection();
  500. }
  501. //-------------------------------------
  502. void CAI_FollowBehavior::EndScheduleSelection()
  503. {
  504. if ( !m_params.bNormalMemoryDiscard )
  505. {
  506. // Restore our original enemy discard time
  507. GetOuter()->GetEnemies()->SetEnemyDiscardTime( m_flOriginalEnemyDiscardTime );
  508. }
  509. if ( m_SavedDistTooFar > 0.1 ) // backward savefile compatability
  510. {
  511. GetOuter()->m_flDistTooFar = m_SavedDistTooFar;
  512. }
  513. BaseClass::EndScheduleSelection();
  514. }
  515. //-------------------------------------
  516. void CAI_FollowBehavior::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput )
  517. {
  518. if ( m_hFollowManagerInfo.m_pGroup )
  519. {
  520. g_AIFollowManager.RemoveFollower( m_hFollowManagerInfo );
  521. m_hFollowManagerInfo.m_pGroup = NULL;
  522. m_hFollowTarget = NULL;
  523. }
  524. BaseClass::CleanupOnDeath( pCulprit, bFireDeathOutput );
  525. }
  526. //-------------------------------------
  527. void CAI_FollowBehavior::Precache()
  528. {
  529. if ( m_hFollowTarget != NULL && m_hFollowManagerInfo.m_pGroup == NULL )
  530. {
  531. // Post load fixup
  532. if ( !g_AIFollowManager.AddFollower( m_hFollowTarget, GetOuter(), m_params.formation, &m_hFollowManagerInfo ) )
  533. {
  534. m_hFollowTarget = NULL;
  535. }
  536. }
  537. }
  538. //-------------------------------------
  539. void CAI_FollowBehavior::GatherConditions( void )
  540. {
  541. BaseClass::GatherConditions();
  542. if ( !GetFollowTarget() )
  543. {
  544. ClearCondition( COND_FOLLOW_PLAYER_IS_LIT );
  545. ClearCondition( COND_FOLLOW_PLAYER_IS_NOT_LIT );
  546. ClearCondition( COND_FOLLOW_TARGET_VISIBLE );
  547. ClearCondition( COND_FOLLOW_TARGET_NOT_VISIBLE );
  548. ClearCondition( COND_FOLLOW_DELAY_EXPIRED );
  549. ClearCondition( COND_TARGET_MOVED_FROM_MARK );
  550. ClearFollowPoint();
  551. m_pInterruptWaitPoint = NULL;
  552. m_bTargetUnreachable = false;
  553. m_flTimeFollowTargetVisible = 0;
  554. if ( IsRunning() )
  555. {
  556. GetOuter()->ClearSchedule( "Follow target gone" );
  557. }
  558. return;
  559. }
  560. if ( !m_TargetMonitor.IsMarkSet() )
  561. {
  562. FollowMsg( "No mark set\n" );
  563. }
  564. if ( m_FollowDelay.IsRunning() && m_FollowDelay.Expired())
  565. {
  566. SetCondition( COND_FOLLOW_DELAY_EXPIRED );
  567. m_FollowDelay.Stop();
  568. }
  569. if ( m_TargetMonitor.TargetMoved2D( GetFollowTarget() ) )
  570. {
  571. FollowMsg( "Target moved\n" );
  572. m_TargetMonitor.ClearMark();
  573. SetCondition( COND_TARGET_MOVED_FROM_MARK );
  574. m_bTargetUnreachable = false;
  575. }
  576. if ( !m_TargetMonitor.IsMarkSet() )
  577. m_bTargetUnreachable = false;
  578. m_pInterruptWaitPoint = NULL;
  579. if ( GetHintNode() == NULL )
  580. {
  581. if ( ShouldUseFollowPoints() && m_TimeBlockUseWaitPoint.Expired() && m_TimeCheckForWaitPoint.Expired() )
  582. {
  583. m_TimeCheckForWaitPoint.Reset();
  584. m_pInterruptWaitPoint = FindFollowPoint();
  585. if ( m_pInterruptWaitPoint )
  586. SetCondition( COND_FOUND_WAIT_POINT );
  587. }
  588. }
  589. if ( m_flTimeUpdatedFollowPosition == 0 || gpGlobals->curtime - m_flTimeUpdatedFollowPosition > 2.0 )
  590. UpdateFollowPosition();
  591. if ( IsFollowTargetInRange() )
  592. {
  593. NoteSuccessfulFollow();
  594. }
  595. else if ( GetOuter()->GetTask() && !IsCurScheduleFollowSchedule() )
  596. {
  597. if ( !m_FollowDelay.IsRunning() || m_FollowDelay.Expired() )
  598. {
  599. switch ( GetOuter()->GetTask()->iTask )
  600. {
  601. case TASK_WAIT_RANDOM:
  602. case TASK_WAIT_INDEFINITE:
  603. case TASK_WAIT:
  604. case TASK_WAIT_FACE_ENEMY:
  605. case TASK_WAIT_FACE_ENEMY_RANDOM:
  606. {
  607. m_TargetMonitor.ClearMark();
  608. if ( !HasCondition(COND_FOLLOW_PLAYER_IS_NOT_LIT) )
  609. {
  610. SetCondition( COND_TARGET_MOVED_FROM_MARK );
  611. }
  612. }
  613. }
  614. }
  615. }
  616. #if 0
  617. else if ( !IsFollowPointInRange() )
  618. {
  619. GetHintNode()->Unlock();
  620. SetHintNode( NULL );
  621. }
  622. #endif
  623. #ifdef HL2_EPISODIC
  624. // Let followers know if the player is lit in the darkness
  625. if ( GetFollowTarget()->IsPlayer() && HL2GameRules()->IsAlyxInDarknessMode() )
  626. {
  627. if ( LookerCouldSeeTargetInDarkness( GetOuter(), GetFollowTarget() ) )
  628. {
  629. SetCondition( COND_FOLLOW_PLAYER_IS_LIT );
  630. ClearCondition( COND_FOLLOW_PLAYER_IS_NOT_LIT );
  631. }
  632. else
  633. {
  634. SetCondition( COND_FOLLOW_PLAYER_IS_NOT_LIT );
  635. ClearCondition( COND_FOLLOW_PLAYER_IS_LIT );
  636. }
  637. }
  638. #endif
  639. // Set our follow target visibility state
  640. if ( (GetFollowTarget()->IsPlayer() && HasCondition( COND_SEE_PLAYER )) || GetOuter()->FVisible( GetFollowTarget()) )
  641. {
  642. SetCondition( COND_FOLLOW_TARGET_VISIBLE );
  643. ClearCondition( COND_FOLLOW_TARGET_NOT_VISIBLE );
  644. m_flTimeFollowTargetVisible = gpGlobals->curtime;
  645. }
  646. else
  647. {
  648. ClearCondition( COND_FOLLOW_TARGET_VISIBLE );
  649. SetCondition( COND_FOLLOW_TARGET_NOT_VISIBLE );
  650. }
  651. if ( HasFollowPoint() && ( m_flTimeFollowTargetVisible != 0 && gpGlobals->curtime - m_flTimeFollowTargetVisible > 5.0 ) )
  652. SetCondition( COND_FOLLOW_WAIT_POINT_INVALID );
  653. else
  654. ClearCondition( COND_FOLLOW_WAIT_POINT_INVALID );
  655. }
  656. //-------------------------------------
  657. int CAI_FollowBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  658. {
  659. if ( failedTask == TASK_MOVE_TO_FOLLOW_POSITION || failedTask == TASK_GET_PATH_TO_FOLLOW_POSITION )
  660. {
  661. if ( m_hFollowTarget )
  662. {
  663. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance * 0.5 );
  664. m_FollowDelay.Start();
  665. NoteFailedFollow();
  666. }
  667. }
  668. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  669. }
  670. //-------------------------------------
  671. bool CAI_FollowBehavior::ShouldFollow()
  672. {
  673. if ( !GetFollowTarget() )
  674. return false;
  675. if ( GetFollowTarget()->GetFlags() & FL_NOTARGET )
  676. return false;
  677. // If we recently failed to build a follow path, wait a while to
  678. // give other schedules a chance to run.
  679. if ( m_bFollowNavFailed && m_FollowDelay.IsRunning() && !m_FollowDelay.Expired() )
  680. {
  681. return false;
  682. }
  683. m_bFollowNavFailed = false;
  684. return true;
  685. }
  686. //-------------------------------------
  687. bool CAI_FollowBehavior::ShouldMoveToFollowTarget()
  688. {
  689. if ( GetFollowTarget() == NULL )
  690. return false;
  691. if( m_bTargetUnreachable )
  692. return false;
  693. #ifdef HL2_EPISODIC
  694. if ( HL2GameRules()->IsAlyxInDarknessMode() )
  695. {
  696. // If we're in darkness mode, the player needs to be lit by
  697. // darkness, but we don't need line of sight to him.
  698. if ( HasCondition(COND_FOLLOW_PLAYER_IS_NOT_LIT) )
  699. return false;
  700. }
  701. #endif
  702. if ( HasFollowPoint() )
  703. {
  704. if ( IsFollowPointInRange() )
  705. return false;
  706. }
  707. else if ( IsFollowTargetInRange() )
  708. return false;
  709. if( m_FollowDelay.IsRunning() && !m_FollowDelay.Expired() && !HasCondition( COND_TARGET_MOVED_FROM_MARK ) )
  710. return false;
  711. return true;
  712. }
  713. //-------------------------------------
  714. int CAI_FollowBehavior::SelectScheduleManagePosition()
  715. {
  716. if ( PlayerIsPushing() )
  717. return SCHED_MOVE_AWAY;
  718. if ( !UpdateFollowPosition() )
  719. return SCHED_FAIL;
  720. return SCHED_NONE;
  721. }
  722. //-------------------------------------
  723. bool CAI_FollowBehavior::ShouldUseFollowPoints()
  724. {
  725. if ( !ai_follow_use_points.GetBool() || GetEnemy() != NULL )
  726. return false;
  727. return true;
  728. }
  729. //-------------------------------------
  730. bool CAI_FollowBehavior::HasFollowPoint()
  731. {
  732. return ( GetHintNode() && GetHintNode()->HintType() == HINT_FOLLOW_WAIT_POINT );
  733. }
  734. //-------------------------------------
  735. void CAI_FollowBehavior::ClearFollowPoint()
  736. {
  737. if ( GetHintNode() && GetHintNode()->HintType() == HINT_FOLLOW_WAIT_POINT )
  738. {
  739. GetHintNode()->Unlock();
  740. SetHintNode( NULL );
  741. }
  742. }
  743. //-------------------------------------
  744. const Vector &CAI_FollowBehavior::GetFollowPoint()
  745. {
  746. static Vector invalid = vec3_invalid;
  747. if ( GetHintNode() && GetHintNode()->HintType() == HINT_FOLLOW_WAIT_POINT )
  748. return GetHintNode()->GetAbsOrigin();
  749. return invalid;
  750. }
  751. //-------------------------------------
  752. CAI_Hint *CAI_FollowBehavior::FindFollowPoint()
  753. {
  754. if ( !m_TimeBlockUseWaitPoint.Expired() )
  755. return NULL;
  756. CHintCriteria hintCriteria;
  757. hintCriteria.SetHintType( HINT_FOLLOW_WAIT_POINT );
  758. hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_NEAREST );
  759. // Add the search position
  760. hintCriteria.AddIncludePosition( GetGoalPosition(), MAX( m_FollowNavGoal.followPointTolerance, GetGoalRange() ) );
  761. hintCriteria.AddExcludePosition( GetGoalPosition(), (GetFollowTarget()->WorldAlignMins().AsVector2D() - GetFollowTarget()->WorldAlignMaxs().AsVector2D()).Length());
  762. return CAI_HintManager::FindHint( GetOuter(), hintCriteria );
  763. }
  764. //-------------------------------------
  765. bool CAI_FollowBehavior::IsFollowPointInRange()
  766. {
  767. return ( GetHintNode() &&
  768. GetHintNode()->HintType() == HINT_FOLLOW_WAIT_POINT &&
  769. (GetHintNode()->GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin()).LengthSqr() < Square(MAX(m_FollowNavGoal.followPointTolerance, GetGoalRange())) );
  770. }
  771. //-------------------------------------
  772. bool CAI_FollowBehavior::ShouldIgnoreFollowPointFacing()
  773. {
  774. if ( !GetHintNode() )
  775. return true;
  776. HintIgnoreFacing_t hintSetting = GetHintNode()->GetIgnoreFacing();
  777. if ( hintSetting == HIF_DEFAULT )
  778. return ( GetHintNode()->HintActivityName() == NULL_STRING );
  779. return ( hintSetting == HIF_YES );
  780. }
  781. //-------------------------------------
  782. void CAI_FollowBehavior::SetFollowPoint( CAI_Hint *pHintNode )
  783. {
  784. if ( !pHintNode )
  785. return;
  786. Assert( pHintNode->HintType() == HINT_FOLLOW_WAIT_POINT );
  787. if ( GetHintNode() == pHintNode )
  788. return;
  789. if ( GetHintNode() )
  790. GetHintNode()->Unlock();
  791. if ( !pHintNode->Lock( GetOuter() ) )
  792. {
  793. SetHintNode( NULL );
  794. m_TimeBlockUseWaitPoint.Reset();
  795. }
  796. else
  797. SetHintNode( pHintNode );
  798. }
  799. //-------------------------------------
  800. int CAI_FollowBehavior::SelectScheduleFollowPoints()
  801. {
  802. bool bShouldUseFollowPoints = ( ShouldUseFollowPoints() && IsFollowGoalInRange( m_FollowNavGoal.followPointTolerance + 0.1, GetGoalZRange(), GetGoalFlags() ) );
  803. float distSqToPoint = FLT_MAX;
  804. bool bHasFollowPoint = HasFollowPoint();
  805. if ( bHasFollowPoint )
  806. {
  807. distSqToPoint = (GetHintNode()->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
  808. if ( !bShouldUseFollowPoints ||
  809. distSqToPoint > Square(2.0 * GetHullWidth()) ||
  810. HasCondition( COND_FOLLOW_WAIT_POINT_INVALID ) )
  811. {
  812. GetHintNode()->Unlock();
  813. SetHintNode( NULL );
  814. m_TimeBlockUseWaitPoint.Reset();
  815. bShouldUseFollowPoints = false;
  816. }
  817. }
  818. if ( bShouldUseFollowPoints )
  819. {
  820. bool bNewHint = false;
  821. if ( GetHintNode() && !bHasFollowPoint )
  822. {
  823. GetHintNode()->Unlock();
  824. SetHintNode( NULL );
  825. }
  826. if (!GetHintNode())
  827. {
  828. bNewHint = true;
  829. SetFollowPoint( ( m_pInterruptWaitPoint ) ? m_pInterruptWaitPoint : FindFollowPoint() );
  830. if ( GetHintNode() )
  831. distSqToPoint = (GetHintNode()->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
  832. }
  833. if ( GetHintNode() )
  834. {
  835. if ( bNewHint || distSqToPoint > WAIT_HINT_MIN_DIST )
  836. return SCHED_FOLLOWER_GO_TO_WAIT_POINT;
  837. if ( !ShouldIgnoreFollowPointFacing() )
  838. return SCHED_FOLLOWER_STAND_AT_WAIT_POINT;
  839. }
  840. }
  841. else
  842. ClearFollowPoint();
  843. return SCHED_NONE;
  844. }
  845. //-------------------------------------
  846. int CAI_FollowBehavior::SelectScheduleMoveToFormation()
  847. {
  848. if( ( GetNpcState() != NPC_STATE_COMBAT && !( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))) ||
  849. !IsFollowGoalInRange( GetGoalRange(), GetGoalZRange(), GetGoalFlags() ) )
  850. {
  851. AISquadIter_t iter;
  852. CAI_Squad *pSquad = GetOuter()->GetSquad();
  853. if ( pSquad )
  854. {
  855. for ( CAI_BaseNPC *pSquadMember = pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = pSquad->GetNextMember( &iter ) )
  856. {
  857. if ( pSquadMember->HasCondition( COND_PLAYER_PUSHING ) )
  858. {
  859. return SCHED_NONE;
  860. }
  861. }
  862. }
  863. if ( ShouldMoveToFollowTarget() || m_bFirstFacing )
  864. {
  865. return SCHED_TARGET_FACE; // Code for "SCHED_MOVE_TO_FACE_FOLLOW_TARGET". Used by Talker clients to interject comment
  866. }
  867. }
  868. return SCHED_NONE;
  869. }
  870. //-------------------------------------
  871. int CAI_FollowBehavior::SelectSchedule()
  872. {
  873. // Allow a range attack if we need to do it
  874. if ( hl2_episodic.GetBool() )
  875. {
  876. // Range attack
  877. if ( GetOuter()->ShouldMoveAndShoot() == false && HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  878. return SCHED_RANGE_ATTACK1;
  879. }
  880. if ( GetFollowTarget() )
  881. {
  882. if ( !GetFollowTarget()->IsAlive() )
  883. {
  884. // UNDONE: Comment about the recently dead player here?
  885. SetFollowTarget( NULL );
  886. }
  887. else if ( ShouldFollow() )
  888. {
  889. int result = SCHED_NONE;
  890. result = SelectScheduleManagePosition();
  891. if ( result != SCHED_NONE )
  892. return result;
  893. result = SelectScheduleFollowPoints();
  894. if ( result != SCHED_NONE )
  895. return result;
  896. result = SelectScheduleMoveToFormation();
  897. if ( result != SCHED_NONE )
  898. return result;
  899. if ( HasCondition ( COND_NO_PRIMARY_AMMO ) && HaveSequenceForActivity( GetOuter()->TranslateActivity( ACT_RUN_AIM ) ) )
  900. return SCHED_HIDE_AND_RELOAD;
  901. }
  902. if ( PlayerIsPushing() )
  903. return SCHED_MOVE_AWAY;
  904. }
  905. else
  906. {
  907. // Should not have landed here. Follow target ent must have been destroyed
  908. NotifyChangeBehaviorStatus();
  909. }
  910. if ( HasCondition( COND_TARGET_MOVED_FROM_MARK ) )
  911. {
  912. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance * 0.5 );
  913. }
  914. return FollowCallBaseSelectSchedule();
  915. }
  916. //-------------------------------------
  917. int CAI_FollowBehavior::TranslateSchedule( int scheduleType )
  918. {
  919. switch( scheduleType )
  920. {
  921. case SCHED_FOLLOWER_IDLE_STAND:
  922. // If we have an enemy, at least face them!
  923. if ( GetEnemy() )
  924. return SCHED_FOLLOWER_COMBAT_FACE;
  925. break;
  926. case SCHED_IDLE_STAND:
  927. {
  928. if ( ShouldMoveToFollowTarget() && !IsFollowGoalInRange( GetGoalRange(), GetGoalZRange(), GetGoalFlags() ) )
  929. {
  930. return SCHED_MOVE_TO_FACE_FOLLOW_TARGET;
  931. }
  932. if ( HasFollowPoint() && !ShouldIgnoreFollowPointFacing() )
  933. return SCHED_FOLLOWER_GO_TO_WAIT_POINT;
  934. // If we have an enemy, at least face them!
  935. if ( GetEnemy() )
  936. return SCHED_FOLLOWER_COMBAT_FACE;
  937. return SCHED_FOLLOWER_IDLE_STAND;
  938. }
  939. case SCHED_COMBAT_STAND:
  940. case SCHED_ALERT_STAND:
  941. {
  942. if ( ShouldMoveToFollowTarget() && !IsFollowGoalInRange( GetGoalRange(), GetGoalZRange(), GetGoalFlags() ) )
  943. {
  944. return SCHED_MOVE_TO_FACE_FOLLOW_TARGET;
  945. }
  946. break;
  947. }
  948. case SCHED_TARGET_FACE:
  949. {
  950. if ( ( ShouldMoveToFollowTarget() || m_bFirstFacing ) && !IsFollowGoalInRange( GetGoalRange(), GetGoalZRange(), GetGoalFlags() ) )
  951. {
  952. return SCHED_MOVE_TO_FACE_FOLLOW_TARGET;
  953. }
  954. if ( HasFollowPoint() && !ShouldIgnoreFollowPointFacing() )
  955. return SCHED_FOLLOWER_GO_TO_WAIT_POINT;
  956. if ( !m_TargetMonitor.IsMarkSet() )
  957. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance );
  958. return SCHED_FACE_FOLLOW_TARGET; // @TODO (toml 03-03-03): should select a facing sched
  959. }
  960. case SCHED_TARGET_CHASE:
  961. {
  962. return SCHED_FOLLOW;
  963. }
  964. // SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK just tells the NPC to chase their enemy, so
  965. // forbid this unless the destination is acceptable within the parameters of the follow behavior.
  966. case SCHED_CHASE_ENEMY:
  967. case SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK:
  968. {
  969. if ( IsChaseGoalInRange() == false )
  970. return SCHED_FOLLOWER_IDLE_STAND;
  971. break;
  972. }
  973. case SCHED_RANGE_ATTACK1:
  974. {
  975. if ( GetOuter()->GetShotRegulator()->IsInRestInterval() )
  976. {
  977. if ( GetEnemy() )
  978. return SCHED_FOLLOWER_COMBAT_FACE;
  979. return SCHED_FOLLOWER_IDLE_STAND; // @TODO (toml 07-02-03): Should do something more tactically sensible
  980. }
  981. break;
  982. }
  983. case SCHED_CHASE_ENEMY_FAILED:
  984. {
  985. if (HasMemory(bits_MEMORY_INCOVER))
  986. {
  987. // Make sure I don't get too far from the player
  988. if ( GetFollowTarget() )
  989. {
  990. float fDist = (GetLocalOrigin() - GetFollowTarget()->GetAbsOrigin()).Length();
  991. if (fDist > 500)
  992. {
  993. return SCHED_FOLLOW;
  994. }
  995. }
  996. }
  997. break;
  998. }
  999. case SCHED_MOVE_AWAY_FAIL:
  1000. {
  1001. return SCHED_FOLLOWER_MOVE_AWAY_FAIL;
  1002. }
  1003. case SCHED_MOVE_AWAY_END:
  1004. {
  1005. return SCHED_FOLLOWER_MOVE_AWAY_END;
  1006. }
  1007. }
  1008. return BaseClass::TranslateSchedule( scheduleType );
  1009. }
  1010. //-------------------------------------
  1011. void CAI_FollowBehavior::OnStartSchedule( int scheduleType )
  1012. {
  1013. if ( !IsRunning() && HasFollowPoint() )
  1014. {
  1015. ClearHintNode( 0.5 );
  1016. }
  1017. if ( !m_TargetMonitor.IsMarkSet() && !IsCurScheduleFollowSchedule() )
  1018. {
  1019. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance );
  1020. }
  1021. }
  1022. //-------------------------------------
  1023. void CAI_FollowBehavior::GetFollowTargetViewLoc( Vector *pResult )
  1024. {
  1025. if ( !dynamic_cast<CPointEntity *>(m_hFollowTarget.Get()) )
  1026. {
  1027. trace_t tr;
  1028. Vector vecStart, vecDir;
  1029. ASSERT( m_hFollowTarget != NULL );
  1030. vecStart = m_hFollowTarget->EyePosition();
  1031. CBasePlayer *pPlayer;
  1032. pPlayer = dynamic_cast<CBasePlayer *>(m_hFollowTarget.Get());
  1033. if( pPlayer )
  1034. {
  1035. // Follow target is a player.
  1036. pPlayer->EyeVectors( &vecDir, NULL, NULL );
  1037. }
  1038. else
  1039. {
  1040. // Not a player.
  1041. m_hFollowTarget->GetVectors( &vecDir, NULL, NULL );
  1042. }
  1043. AI_TraceLOS( vecStart, vecStart + vecDir * 8192, m_hFollowTarget, &tr );
  1044. *pResult = tr.endpos;
  1045. }
  1046. else
  1047. *pResult = m_hFollowTarget->GetAbsOrigin();
  1048. }
  1049. //-------------------------------------
  1050. bool CAI_FollowBehavior::ValidateFaceTarget( Vector *pFaceTarget )
  1051. {
  1052. if ( *pFaceTarget == vec3_invalid )
  1053. {
  1054. if ( m_hFollowTarget != NULL )
  1055. {
  1056. *pFaceTarget = m_hFollowTarget->GetAbsOrigin();
  1057. }
  1058. return false;
  1059. }
  1060. Vector testPoint = *pFaceTarget - GetAbsOrigin();
  1061. testPoint.z = 0;
  1062. VectorNormalize( testPoint );
  1063. testPoint *= 48;
  1064. testPoint += GetOuter()->EyePosition();
  1065. trace_t tr;
  1066. AI_TraceLine( GetOuter()->EyePosition(), testPoint, MASK_BLOCKLOS, m_hFollowTarget, COLLISION_GROUP_NONE, &tr );
  1067. if ( tr.fraction < 1.0 )
  1068. {
  1069. *pFaceTarget = m_hFollowTarget->GetAbsOrigin();
  1070. return false;
  1071. }
  1072. return true;
  1073. }
  1074. //-------------------------------------
  1075. bool CAI_FollowBehavior::FindCoverFromEnemyAtFollowTarget( float coverRadius, Vector *pResult )
  1076. {
  1077. CBaseEntity *pEntity = GetEnemy();
  1078. return GetOuter()->FindCoverPosInRadius( pEntity, m_FollowNavGoal.position, coverRadius, pResult );
  1079. }
  1080. //-------------------------------------
  1081. void CAI_FollowBehavior::StartTask( const Task_t *pTask )
  1082. {
  1083. AI_PROFILE_SCOPE( CAI_FollowBehavior_StartTask );
  1084. switch ( pTask->iTask )
  1085. {
  1086. case TASK_RANGE_ATTACK1:
  1087. BaseClass::StartTask( pTask );
  1088. break;
  1089. case TASK_GET_PATH_TO_FOLLOW_POSITION:
  1090. {
  1091. if ( !UpdateFollowPosition() )
  1092. {
  1093. TaskFail(FAIL_NO_TARGET);
  1094. }
  1095. else
  1096. {
  1097. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance );
  1098. m_bMovingToCover = false;
  1099. GetOuter()->m_vInterruptSavePosition = vec3_invalid;
  1100. }
  1101. break;
  1102. }
  1103. case TASK_CANT_FOLLOW:
  1104. {
  1105. SetFollowTarget( NULL, true );
  1106. TaskComplete();
  1107. break;
  1108. }
  1109. case TASK_FOLLOWER_FACE_TACTICAL:
  1110. case TASK_FACE_FOLLOW_TARGET:
  1111. {
  1112. if ( !m_TimeBeforeSpreadFacing.Expired() )
  1113. {
  1114. m_TimeNextSpreadFacing.Reset();
  1115. }
  1116. Vector faceTarget = vec3_invalid;
  1117. bool bFollowingPoint = ( dynamic_cast<CPointEntity *>(m_hFollowTarget.Get()) != NULL );
  1118. if ( GetNpcState() == NPC_STATE_COMBAT )
  1119. {
  1120. if( gpGlobals->curtime - GetOuter()->GetEnemyLastTimeSeen() < 5.0 )
  1121. {
  1122. faceTarget = GetEnemyLKP();
  1123. }
  1124. else if ( !bFollowingPoint )
  1125. {
  1126. GetFollowTargetViewLoc( &faceTarget );
  1127. }
  1128. }
  1129. else if ( m_hFollowTarget && !bFollowingPoint )
  1130. {
  1131. if ( m_bFirstFacing && m_hFollowTarget->IsPlayer() )
  1132. {
  1133. faceTarget = m_hFollowTarget->GetAbsOrigin();
  1134. }
  1135. else if ( m_TimeNextSpreadFacing.Expired() )
  1136. {
  1137. m_TimeNextSpreadFacing.Reset();
  1138. bool bIsEpisodicVitalAlly;
  1139. #ifdef HL2_DLL
  1140. bIsEpisodicVitalAlly = (hl2_episodic.GetBool() && GetOuter()->Classify() == CLASS_PLAYER_ALLY_VITAL);
  1141. #else
  1142. bIsEpisodicVitalAlly = false;
  1143. #endif//HL2_DLL
  1144. if( bIsEpisodicVitalAlly )
  1145. {
  1146. faceTarget = m_hFollowTarget->GetAbsOrigin();
  1147. }
  1148. else
  1149. {
  1150. int roll = random->RandomInt(1, 4);
  1151. if ( roll == 1 )
  1152. {
  1153. GetFollowTargetViewLoc( &faceTarget );
  1154. }
  1155. else if ( roll == 2 )
  1156. {
  1157. faceTarget = m_hFollowTarget->GetAbsOrigin();
  1158. }
  1159. else
  1160. {
  1161. // Fan out and face to cover all directions.
  1162. int count = g_AIFollowManager.CountFollowersInGroup( GetOuter() );
  1163. if( count > 0 )
  1164. {
  1165. // Slice up the directions among followers and leader. ( +1 because we count the leader!)
  1166. float flSlice = 360.0 / (count + 1);
  1167. // Add one to slots so then are 1 to N instead of 0 to N - 1.
  1168. int slot = random->RandomInt( 0, count );
  1169. QAngle angle = m_hFollowTarget->GetAbsAngles();
  1170. // split up the remaining angles among followers in my group.
  1171. angle.y = UTIL_AngleMod( angle.y + ( flSlice * slot ) );
  1172. Vector vecDir;
  1173. AngleVectors( angle, &vecDir );
  1174. faceTarget = GetOuter()->GetAbsOrigin() + vecDir * 128;
  1175. }
  1176. }
  1177. }
  1178. }
  1179. else
  1180. {
  1181. // Stay where we are
  1182. TaskComplete();
  1183. break;
  1184. }
  1185. }
  1186. m_bFirstFacing = false;
  1187. if ( ValidateFaceTarget( &faceTarget ) )
  1188. {
  1189. Assert( faceTarget != vec3_invalid );
  1190. if ( !GetOuter()->FInAimCone( faceTarget ) )
  1191. {
  1192. GetMotor()->SetIdealYawToTarget( faceTarget, 30 );
  1193. GetOuter()->SetTurnActivity();
  1194. }
  1195. else
  1196. TaskComplete();
  1197. }
  1198. else
  1199. ChainStartTask( TASK_FACE_REASONABLE );
  1200. break;
  1201. }
  1202. case TASK_MOVE_TO_FOLLOW_POSITION:
  1203. {
  1204. if ( m_hFollowTarget == NULL)
  1205. {
  1206. TaskFail(FAIL_NO_TARGET);
  1207. }
  1208. else if ( (m_hFollowTarget->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 )
  1209. {
  1210. TaskComplete();
  1211. }
  1212. else if ( !GetNavigator()->IsGoalActive() )
  1213. {
  1214. TaskFail(FAIL_NO_ROUTE);
  1215. }
  1216. else
  1217. {
  1218. m_vFollowMoveAnchor = GetAbsOrigin();
  1219. m_CurrentFollowActivity = ACT_INVALID;
  1220. m_RepathOnFollowTimer.Force();
  1221. }
  1222. break;
  1223. }
  1224. case TASK_SET_FOLLOW_TARGET_MARK:
  1225. {
  1226. if ( m_hFollowTarget == NULL)
  1227. {
  1228. TaskFail(FAIL_NO_TARGET);
  1229. }
  1230. else
  1231. {
  1232. FollowMsg( "TASK_SET_FOLLOW_TARGET_MARK\n" );
  1233. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance );
  1234. TaskComplete();
  1235. }
  1236. break;
  1237. }
  1238. case TASK_SET_FOLLOW_DELAY:
  1239. {
  1240. m_FollowDelay.Start( pTask->flTaskData );
  1241. TaskComplete();
  1242. break;
  1243. }
  1244. case TASK_FIND_COVER_FROM_ENEMY:
  1245. {
  1246. CBaseEntity *pLeader = GetFollowTarget();
  1247. if ( pLeader )
  1248. {
  1249. Vector coverPos = vec3_invalid;
  1250. float coverRadius = MIN( GetOuter()->CoverRadius(), m_FollowNavGoal.coverTolerance );
  1251. if ( FindCoverFromEnemyAtFollowTarget( coverRadius, &coverPos ) )
  1252. {
  1253. AI_NavGoal_t goal(GOALTYPE_COVER, coverPos, ACT_RUN, AIN_HULL_TOLERANCE, AIN_DEF_FLAGS);
  1254. GetNavigator()->SetGoal( goal );
  1255. GetOuter()->m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1256. TaskComplete();
  1257. }
  1258. else
  1259. TaskFail(FAIL_NO_COVER);
  1260. }
  1261. else
  1262. BaseClass::StartTask( pTask );
  1263. break;
  1264. }
  1265. case TASK_GET_PATH_TO_FOLLOW_POINT:
  1266. {
  1267. ChainStartTask( TASK_GET_PATH_TO_HINTNODE, ShouldIgnoreFollowPointFacing() );
  1268. break;
  1269. }
  1270. case TASK_ARRIVE_AT_FOLLOW_POINT:
  1271. {
  1272. if ( GetHintNode() && !ShouldIgnoreFollowPointFacing() )
  1273. ChainStartTask( TASK_FACE_HINTNODE, 0 );
  1274. else
  1275. TaskComplete();
  1276. break;
  1277. }
  1278. case TASK_SET_FOLLOW_POINT_STAND_SCHEDULE:
  1279. {
  1280. if ( GetHintNode() && !ShouldIgnoreFollowPointFacing() )
  1281. {
  1282. float distSqToPoint = (GetHintNode()->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
  1283. if ( distSqToPoint < WAIT_HINT_MIN_DIST )
  1284. {
  1285. GetOuter()->SetSchedule( SCHED_FOLLOWER_STAND_AT_WAIT_POINT );
  1286. }
  1287. else
  1288. {
  1289. GetHintNode()->Unlock();
  1290. SetHintNode( NULL );
  1291. m_TimeBlockUseWaitPoint.Reset();
  1292. TaskFail("Couldn't get to wait node." );
  1293. }
  1294. }
  1295. else
  1296. {
  1297. GetOuter()->SetSchedule( SCHED_FACE_FOLLOW_TARGET );
  1298. }
  1299. break;
  1300. }
  1301. case TASK_BEGIN_STAND_AT_WAIT_POINT:
  1302. {
  1303. if ( !m_TargetMonitor.IsMarkSet() && IsFollowPointInRange() )
  1304. m_TargetMonitor.SetMark( m_hFollowTarget, m_FollowNavGoal.targetMoveTolerance );
  1305. if ( GetHintNode() && !ShouldIgnoreFollowPointFacing() )
  1306. ChainStartTask( TASK_FACE_HINTNODE, 0 );
  1307. else
  1308. TaskComplete();
  1309. break;
  1310. }
  1311. default:
  1312. BaseClass::StartTask( pTask );
  1313. }
  1314. }
  1315. //-------------------------------------
  1316. void CAI_FollowBehavior::RunTask( const Task_t *pTask )
  1317. {
  1318. switch( pTask->iTask )
  1319. {
  1320. case TASK_GET_PATH_TO_FOLLOW_POSITION:
  1321. {
  1322. switch( GetOuter()->GetTaskInterrupt() )
  1323. {
  1324. case 0:
  1325. {
  1326. if ( GetEnemy() )
  1327. {
  1328. Assert( GetOuter()->m_vInterruptSavePosition == vec3_invalid );
  1329. Vector coverPos = vec3_invalid;
  1330. float coverRadius = MIN( (float)12*12, m_FollowNavGoal.coverTolerance );
  1331. if ( FindCoverFromEnemyAtFollowTarget( coverRadius, &coverPos ) )
  1332. {
  1333. GetOuter()->m_vInterruptSavePosition = coverPos;
  1334. }
  1335. GetOuter()->TaskInterrupt();
  1336. break;
  1337. }
  1338. }
  1339. // Fall through...
  1340. case 1:
  1341. {
  1342. if ( GetOuter()->m_vInterruptSavePosition != vec3_invalid )
  1343. {
  1344. AI_NavGoal_t goal(GOALTYPE_COVER, GetOuter()->m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE, AIN_DEF_FLAGS);
  1345. if ( GetNavigator()->SetGoal( goal, AIN_NO_PATH_TASK_FAIL ) )
  1346. {
  1347. TaskComplete();
  1348. m_bMovingToCover = true;
  1349. }
  1350. else
  1351. {
  1352. GetOuter()->TaskInterrupt();
  1353. }
  1354. break;
  1355. }
  1356. // Fall through...
  1357. }
  1358. case 2:
  1359. {
  1360. Assert( !m_bMovingToCover );
  1361. Vector vGoalPosition;
  1362. if ( HasFollowPoint() && IsFollowPointInRange() )
  1363. vGoalPosition = GetFollowPoint();
  1364. else
  1365. vGoalPosition = GetGoalPosition();
  1366. AI_NavGoal_t goal( vGoalPosition, AIN_DEF_ACTIVITY, GetGoalTolerance() );
  1367. if ( !m_hFollowTarget->GetParent() || !m_hFollowTarget->GetParent()->GetServerVehicle() )
  1368. {
  1369. goal.pTarget = m_hFollowTarget;
  1370. }
  1371. else
  1372. {
  1373. goal.pTarget = m_hFollowTarget->GetParent();
  1374. }
  1375. bool bSuccess = true;
  1376. if ( !GetNavigator()->SetGoal( goal, AIN_NO_PATH_TASK_FAIL ) )
  1377. {
  1378. const Vector &vTarget = GetFollowTarget()->WorldSpaceCenter();
  1379. Vector vToGoal = vGoalPosition - vTarget;
  1380. if ( vToGoal.Length2DSqr() > 6*12 )
  1381. {
  1382. goal.dest = vTarget + vToGoal * 0.5;
  1383. if ( !GetNavigator()->SetGoal( goal, AIN_NO_PATH_TASK_FAIL ) )
  1384. {
  1385. bSuccess = false;
  1386. m_FollowDelay.Start( 2.0, 5.0 );
  1387. }
  1388. }
  1389. else
  1390. {
  1391. bSuccess = false;
  1392. m_FollowDelay.Start( 2.0, 5.0 );
  1393. }
  1394. }
  1395. if ( !bSuccess )
  1396. {
  1397. m_bFollowNavFailed = true;
  1398. TaskFail( FAIL_NO_ROUTE );
  1399. }
  1400. else
  1401. {
  1402. TaskComplete();
  1403. }
  1404. }
  1405. }
  1406. break;
  1407. }
  1408. case TASK_FOLLOWER_FACE_TACTICAL:
  1409. case TASK_FACE_FOLLOW_TARGET:
  1410. {
  1411. ChainRunTask( TASK_FACE_REASONABLE );
  1412. break;
  1413. }
  1414. case TASK_MOVE_TO_FOLLOW_POSITION:
  1415. {
  1416. if ( m_hFollowTarget == NULL )
  1417. {
  1418. TaskFail(FAIL_NO_TARGET);
  1419. }
  1420. else
  1421. {
  1422. if ( m_bMovingToCover )
  1423. {
  1424. ChainRunTask( TASK_WAIT_FOR_MOVEMENT );
  1425. NoteSuccessfulFollow();
  1426. return;
  1427. }
  1428. // Re-evaluate when you think your finished, or the target has moved too far
  1429. if ( !UpdateFollowPosition() )
  1430. {
  1431. TaskFail(FAIL_NO_TARGET);
  1432. break;
  1433. }
  1434. if ( ShouldUseFollowPoints() && ai_follow_use_points_when_moving.GetBool() )
  1435. {
  1436. if ( HasFollowPoint() )
  1437. {
  1438. if ( !IsFollowPointInRange() )
  1439. {
  1440. ClearFollowPoint();
  1441. GetNavigator()->SetArrivalDirection( vec3_origin );
  1442. GetNavigator()->SetArrivalActivity( ACT_INVALID );
  1443. m_TimeBlockUseWaitPoint.Reset();
  1444. m_TimeCheckForWaitPoint.Reset();
  1445. }
  1446. }
  1447. if ( GetNavigator()->GetNavType() != NAV_JUMP && !HasFollowPoint() && m_pInterruptWaitPoint )
  1448. {
  1449. SetFollowPoint( m_pInterruptWaitPoint );
  1450. }
  1451. }
  1452. else
  1453. {
  1454. ClearFollowPoint();
  1455. if ( GetNavigator()->IsGoalActive() )
  1456. {
  1457. GetNavigator()->SetArrivalDirection( vec3_origin );
  1458. GetNavigator()->SetArrivalActivity( ACT_INVALID );
  1459. }
  1460. }
  1461. if ( !GetNavigator()->IsGoalActive() )
  1462. {
  1463. // What this probably means is that the navigation failed but within tolerance
  1464. // So for now, just call it good and block another attempt for a bit
  1465. TaskComplete();
  1466. if ( !IsFollowPointInRange() )
  1467. ClearFollowPoint();
  1468. if ( !IsFollowGoalInRange( m_FollowNavGoal.tolerance, GetGoalZRange(), GetGoalFlags() ) )
  1469. m_FollowDelay.Start( 0.25, 0.75 );
  1470. else
  1471. {
  1472. m_TargetMonitor.SetMark( GetFollowTarget(), m_FollowNavGoal.targetMoveTolerance );
  1473. m_bTargetUnreachable = false;
  1474. }
  1475. break;
  1476. }
  1477. if ( !HasFollowPoint() )
  1478. {
  1479. float range = GetGoalRange();
  1480. Vector vVelocity =- GetFollowTarget()->GetSmoothedVelocity();
  1481. bool bDoSlowdown = ( vVelocity.LengthSqr() < Square(4*12) );
  1482. if ( bDoSlowdown )
  1483. {
  1484. range += GetMotor()->MinStoppingDist(12) - 12;
  1485. }
  1486. if ( IsFollowGoalInRange( range, GetGoalZRange(), GetGoalFlags() ) )
  1487. {
  1488. m_TimeBeforeSpreadFacing.Reset();
  1489. TaskComplete();
  1490. GetNavigator()->StopMoving( !bDoSlowdown ); // Stop moving
  1491. m_TargetMonitor.SetMark( GetFollowTarget(), m_FollowNavGoal.targetMoveTolerance );
  1492. break;
  1493. }
  1494. // Update the nav goal if needed
  1495. if ( m_RepathOnFollowTimer.Expired() )
  1496. {
  1497. if ( (GetNavigator()->GetGoalPos() - GetGoalPosition()).LengthSqr() > Square( m_FollowNavGoal.repathOnRouteTolerance ) )
  1498. {
  1499. if ( GetNavigator()->GetNavType() != NAV_JUMP )
  1500. {
  1501. m_RepathOnFollowTimer.Set( .5 );
  1502. if ( !GetNavigator()->UpdateGoalPos( GetGoalPosition() ) )
  1503. {
  1504. bool bSuccess = false;
  1505. const Vector &vTarget = GetFollowTarget()->WorldSpaceCenter();
  1506. Vector vToGoal = GetGoalPosition() - vTarget;
  1507. if ( vToGoal.Length2DSqr() > 6*12 )
  1508. {
  1509. if ( GetNavigator()->UpdateGoalPos( vTarget + vToGoal * 0.5 ) )
  1510. {
  1511. bSuccess = true;
  1512. }
  1513. }
  1514. if ( !bSuccess )
  1515. {
  1516. TaskFail(FAIL_NO_ROUTE);
  1517. m_bTargetUnreachable = true;
  1518. }
  1519. break;
  1520. }
  1521. NoteSuccessfulFollow();
  1522. }
  1523. }
  1524. }
  1525. }
  1526. else
  1527. {
  1528. const Vector &vFollowPoint = GetFollowPoint();
  1529. if ( GetNavigator()->GetGoalPos() != vFollowPoint )
  1530. {
  1531. if ( !GetNavigator()->UpdateGoalPos( vFollowPoint ) )
  1532. {
  1533. TaskFail(FAIL_NO_ROUTE);
  1534. m_bTargetUnreachable = true;
  1535. break;
  1536. }
  1537. NoteSuccessfulFollow();
  1538. if ( !ShouldIgnoreFollowPointFacing() )
  1539. GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() );
  1540. if ( GetHintNode()->HintActivityName() != NULL_STRING )
  1541. {
  1542. Activity hintActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(GetHintNode()->HintActivityName()) );
  1543. if ( hintActivity != ACT_INVALID )
  1544. {
  1545. GetNavigator()->SetArrivalActivity( GetOuter()->GetHintActivity(GetHintNode()->HintType(), hintActivity ) );
  1546. }
  1547. else
  1548. {
  1549. int iSequence = GetOuter()->LookupSequence(STRING(GetHintNode()->HintActivityName()));
  1550. if ( iSequence != ACT_INVALID )
  1551. {
  1552. GetNavigator()->SetArrivalSequence( iSequence );
  1553. }
  1554. }
  1555. }
  1556. }
  1557. }
  1558. // Set the appropriate activity based on an overlapping range
  1559. // overlap the range to prevent oscillation
  1560. // BUGBUG: this is checking linear distance (ie. through walls) and not path distance or even visibility
  1561. // Never stop running once started
  1562. if ( m_CurrentFollowActivity != ACT_RUN )
  1563. {
  1564. float distToTargetSq = ( GetNavigator()->GetGoalPos() - GetLocalOrigin() ).Length2DSqr();
  1565. // Pick the right movement activity.
  1566. Activity followActivity = ( distToTargetSq < Square(m_FollowNavGoal.walkTolerance) && GetOuter()->GetState() != NPC_STATE_COMBAT ) ? ACT_WALK : ACT_RUN;
  1567. // If we're supposed to have LOS, run to catch up
  1568. if ( m_FollowNavGoal.flags & AIFF_REQUIRE_LOS_OUTSIDE_COMBAT )
  1569. {
  1570. if ( !GetOuter()->GetSenses()->DidSeeEntity( m_hFollowTarget ) )
  1571. {
  1572. followActivity = ACT_RUN;
  1573. }
  1574. }
  1575. if ( followActivity != m_CurrentFollowActivity )
  1576. {
  1577. m_CurrentFollowActivity = followActivity;
  1578. GetNavigator()->SetMovementActivity(followActivity);
  1579. }
  1580. }
  1581. if ( ( m_vFollowMoveAnchor - GetAbsOrigin() ).LengthSqr() > Square( 15.0 * 12.0 ) )
  1582. {
  1583. m_vFollowMoveAnchor = GetAbsOrigin();
  1584. NoteSuccessfulFollow();
  1585. }
  1586. }
  1587. break;
  1588. }
  1589. case TASK_ARRIVE_AT_FOLLOW_POINT:
  1590. {
  1591. ChainRunTask( TASK_FACE_HINTNODE, 0 );
  1592. break;
  1593. }
  1594. case TASK_BEGIN_STAND_AT_WAIT_POINT:
  1595. {
  1596. ChainRunTask( TASK_FACE_HINTNODE, 0 );
  1597. break;
  1598. }
  1599. default:
  1600. BaseClass::RunTask( pTask );
  1601. }
  1602. }
  1603. //-------------------------------------
  1604. void CAI_FollowBehavior::TaskComplete( bool fIgnoreSetFailedCondition )
  1605. {
  1606. const Task_t *pTask = GetCurTask();
  1607. if ( pTask->iTask == TASK_MOVE_TO_FOLLOW_POSITION || pTask->iTask == TASK_GET_PATH_TO_FOLLOW_POSITION )
  1608. NoteSuccessfulFollow();
  1609. BaseClass::TaskComplete( fIgnoreSetFailedCondition );
  1610. }
  1611. //-------------------------------------
  1612. void CAI_FollowBehavior::BuildScheduleTestBits()
  1613. {
  1614. BaseClass::BuildScheduleTestBits();
  1615. bool bIsTakeCover = false;
  1616. bool bIsHideAndReload = false;
  1617. bool bIsReload = false;
  1618. bool bIgnoreMovedMark = false;
  1619. if ( ( GetOuter()->ConditionInterruptsCurSchedule( COND_GIVE_WAY ) ||
  1620. GetOuter()->ConditionInterruptsCurSchedule( COND_IDLE_INTERRUPT ) ||
  1621. ( bIsHideAndReload = IsCurSchedule(SCHED_HIDE_AND_RELOAD ) ) == true ||
  1622. ( bIsReload = IsCurSchedule(SCHED_RELOAD ) ) == true ||
  1623. IsCurSchedule(SCHED_STANDOFF ) ||
  1624. ( bIsTakeCover = IsCurSchedule(SCHED_TAKE_COVER_FROM_ENEMY ) ) == true ||
  1625. IsCurSchedule(SCHED_COMBAT_FACE ) ||
  1626. IsCurSchedule(SCHED_ALERT_FACE ) ||
  1627. IsCurSchedule(SCHED_COMBAT_STAND ) ||
  1628. IsCurSchedule(SCHED_ALERT_STAND) ) ||
  1629. IsCurSchedule(SCHED_ALERT_FACE_BESTSOUND ) )
  1630. {
  1631. #ifdef HL2_EPISODIC
  1632. if( IsCurSchedule(SCHED_RELOAD, false) && GetOuter()->Classify() == CLASS_PLAYER_ALLY_VITAL )
  1633. {
  1634. // Alyx and Barney do not stop reloading because the player has moved.
  1635. // Citizens and other regular allies do.
  1636. bIgnoreMovedMark = true;
  1637. }
  1638. #endif//HL2_EPISODIC
  1639. if( !bIgnoreMovedMark )
  1640. {
  1641. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_TARGET_MOVED_FROM_MARK ) );
  1642. }
  1643. if ( !bIsTakeCover && !bIsHideAndReload && !bIsReload )
  1644. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_FOLLOW_DELAY_EXPIRED) );
  1645. }
  1646. // Add logic for NPCs not able to move and shoot
  1647. if ( hl2_episodic.GetBool() )
  1648. {
  1649. if ( IsCurScheduleFollowSchedule() && GetOuter()->ShouldMoveAndShoot() == false )
  1650. {
  1651. GetOuter()->SetCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 );
  1652. }
  1653. #ifdef HL2_EPISODIC
  1654. // In Alyx darkness mode, break on the player turning their flashlight off
  1655. if ( HL2GameRules()->IsAlyxInDarknessMode() )
  1656. {
  1657. if ( IsCurSchedule(SCHED_FOLLOW, false) || IsCurSchedule(SCHED_MOVE_TO_FACE_FOLLOW_TARGET, false) ||
  1658. IsCurSchedule(SCHED_FACE_FOLLOW_TARGET, false) )
  1659. {
  1660. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_FOLLOW_PLAYER_IS_NOT_LIT ) );
  1661. }
  1662. }
  1663. #endif // HL2_EPISODIC
  1664. }
  1665. if ( GetNpcState() == NPC_STATE_COMBAT && IsCurScheduleFollowSchedule() )
  1666. {
  1667. GetOuter()->ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  1668. }
  1669. }
  1670. //-------------------------------------
  1671. Activity CAI_FollowBehavior::NPC_TranslateActivity( Activity activity )
  1672. {
  1673. if ( activity == ACT_IDLE && HasFollowPoint() && GetHintNode()->HintActivityName() != NULL_STRING )
  1674. {
  1675. return GetOuter()->GetHintActivity(GetHintNode()->HintType(), (Activity)CAI_BaseNPC::GetActivityID( STRING(GetHintNode()->HintActivityName()) ) );
  1676. }
  1677. return BaseClass::NPC_TranslateActivity( activity );
  1678. }
  1679. //-------------------------------------
  1680. bool CAI_FollowBehavior::IsCurScheduleFollowSchedule()
  1681. {
  1682. int curScheduleId = ( GetOuter()->GetCurSchedule() ) ? GetOuter()->GetCurSchedule()->GetId() : SCHED_NONE;
  1683. if ( curScheduleId >= GetClassScheduleIdSpace()->ScheduleLocalToGlobal( SCHED_FOLLOWER_MOVE_AWAY_FAIL ) &&
  1684. curScheduleId <= GetClassScheduleIdSpace()->ScheduleLocalToGlobal( SCHED_FOLLOWER_STAND_AT_WAIT_POINT ) )
  1685. {
  1686. return true;
  1687. }
  1688. return false;
  1689. }
  1690. //-------------------------------------
  1691. bool CAI_FollowBehavior::IsCurTaskContinuousMove()
  1692. {
  1693. const Task_t *pCurTask = GetCurTask();
  1694. if ( pCurTask && pCurTask->iTask == TASK_MOVE_TO_FOLLOW_POSITION )
  1695. return true;
  1696. return BaseClass::IsCurTaskContinuousMove();
  1697. }
  1698. //-------------------------------------
  1699. void CAI_FollowBehavior::OnMovementFailed()
  1700. {
  1701. float acceptDist = m_FollowNavGoal.range;
  1702. if ( m_FollowNavGoal.tolerance > acceptDist )
  1703. acceptDist = m_FollowNavGoal.tolerance;
  1704. if ( GetNpcState() == NPC_STATE_COMBAT )
  1705. {
  1706. if ( m_FollowNavGoal.coverTolerance > acceptDist )
  1707. acceptDist = m_FollowNavGoal.coverTolerance;
  1708. if (m_FollowNavGoal.enemyLOSTolerance > acceptDist )
  1709. acceptDist = m_FollowNavGoal.enemyLOSTolerance;
  1710. }
  1711. float flZRange = GetGoalZRange();
  1712. if ( GetGoalZRange() == -1 )
  1713. {
  1714. flZRange = GetHullHeight() * 2;
  1715. }
  1716. if ( IsFollowGoalInRange( acceptDist * 1.5, flZRange, GetGoalFlags() ) )
  1717. m_bTargetUnreachable = true;
  1718. else
  1719. m_FollowDelay.Start();
  1720. }
  1721. //-------------------------------------
  1722. void CAI_FollowBehavior::OnMovementComplete()
  1723. {
  1724. if ( !IsCurSchedule(SCHED_FOLLOWER_GO_TO_WAIT_POINT) )
  1725. m_TimeBeforeSpreadFacing.Reset();
  1726. else
  1727. {
  1728. m_TimeBeforeSpreadFacing.Force();
  1729. m_TimeNextSpreadFacing.Force();
  1730. }
  1731. }
  1732. //-------------------------------------
  1733. bool CAI_FollowBehavior::FValidateHintType( CAI_Hint *pHint )
  1734. {
  1735. if ( pHint->HintType() == HINT_FOLLOW_WAIT_POINT )
  1736. {
  1737. if ( GetFollowTarget() && GetFollowTarget()->FVisible( pHint->GetAbsOrigin() + Vector( 0, 0, 0.1 ) ) )
  1738. return true;
  1739. else
  1740. return false;
  1741. }
  1742. return BaseClass::FValidateHintType( pHint );
  1743. }
  1744. //-------------------------------------
  1745. bool CAI_FollowBehavior::IsValidCover( const Vector &vLocation, CAI_Hint const *pHint )
  1746. {
  1747. if ( (vLocation - m_FollowNavGoal.position).LengthSqr() > Square( m_FollowNavGoal.coverTolerance + 0.1 ) )
  1748. return false;
  1749. return BaseClass::IsValidCover( vLocation, pHint );
  1750. }
  1751. //-------------------------------------
  1752. bool CAI_FollowBehavior::IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint )
  1753. {
  1754. if ( (vLocation - m_FollowNavGoal.position).LengthSqr() > Square( m_FollowNavGoal.enemyLOSTolerance + 0.1 ) )
  1755. return false;
  1756. return BaseClass::IsValidShootPosition( vLocation, pNode, pHint );
  1757. }
  1758. //-------------------------------------
  1759. bool CAI_FollowBehavior::ShouldAlwaysThink()
  1760. {
  1761. return ( m_hFollowTarget && m_hFollowTarget->IsPlayer() );
  1762. }
  1763. //-----------------------------------------------------------------------------
  1764. //
  1765. // CAI_FollowGoal
  1766. //
  1767. // Purpose: A level tool to control the follow behavior. Use is not required
  1768. // in order to use behavior.
  1769. //
  1770. //-----------------------------------------------------------------------------
  1771. BEGIN_DATADESC( CAI_FollowGoal )
  1772. DEFINE_KEYFIELD( m_iFormation, FIELD_INTEGER, "Formation" ),
  1773. #ifdef HL2_EPISODIC
  1774. DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ),
  1775. #endif
  1776. END_DATADESC()
  1777. //-------------------------------------
  1778. LINK_ENTITY_TO_CLASS( ai_goal_follow, CAI_FollowGoal );
  1779. //-------------------------------------
  1780. void CAI_FollowGoal::EnableGoal( CAI_BaseNPC *pAI )
  1781. {
  1782. CAI_FollowBehavior *pBehavior;
  1783. if ( !pAI->GetBehavior( &pBehavior ) )
  1784. return;
  1785. CBaseEntity *pGoalEntity = GetGoalEntity();
  1786. if ( !pGoalEntity && AI_IsSinglePlayer() )
  1787. {
  1788. if ( pAI->IRelationType(UTIL_GetLocalPlayer()) == D_LI )
  1789. {
  1790. pGoalEntity = UTIL_GetLocalPlayer();
  1791. SetGoalEntity( pGoalEntity );
  1792. }
  1793. }
  1794. if ( pGoalEntity )
  1795. pBehavior->SetFollowGoal( this );
  1796. }
  1797. //-------------------------------------
  1798. void CAI_FollowGoal::DisableGoal( CAI_BaseNPC *pAI )
  1799. {
  1800. CAI_FollowBehavior *pBehavior;
  1801. if ( !pAI || !pAI->GetBehavior( &pBehavior ) )
  1802. return;
  1803. pBehavior->ClearFollowGoal( this );
  1804. }
  1805. //-------------------------------------
  1806. #ifdef HL2_EPISODIC
  1807. void CAI_FollowGoal::InputOutsideTransition( inputdata_t &inputdata )
  1808. {
  1809. EnterDormant();
  1810. }
  1811. #endif
  1812. //-----------------------------------------------------------------------------
  1813. //
  1814. // CAI_FollowManager
  1815. //
  1816. //-----------------------------------------------------------------------------
  1817. //-------------------------------------
  1818. //
  1819. // Purpose: Formation definitions
  1820. //
  1821. // @TODO (toml 11-21-03): rework follow so we don't have to have class specifc formations in this file
  1822. struct AI_FollowSlot_t
  1823. {
  1824. int priority;
  1825. TableVector position;
  1826. float positionVariability;
  1827. float rangeMin;
  1828. float rangeMax;
  1829. float Zrange;
  1830. float tolerance;
  1831. // @Q (toml 02-28-03): facing?
  1832. };
  1833. struct AI_FollowFormation_t
  1834. {
  1835. const char * pszName;
  1836. unsigned flags;
  1837. int nSlots;
  1838. // Range within which can exit formation to seek a follow point
  1839. float followPointTolerance;
  1840. // Distance target must move to reset formation
  1841. float targetMoveTolerance;
  1842. // Distance from current move goal target must move to force a repathfind
  1843. float repathOnRouteTolerance;
  1844. // Distance from target within which should walk, not run to formation
  1845. float walkTolerance;
  1846. // Distance within which can exit formation to seek cover
  1847. float coverTolerance;
  1848. // Distance within which can exit formation to seek LOS to enemy
  1849. float enemyLOSTolerance;
  1850. // Distance within which can exit formation to chase enemy
  1851. float chaseEnemyTolerance;
  1852. AI_FollowSlot_t * pSlots;
  1853. };
  1854. //-------------------------------------
  1855. static AI_FollowSlot_t g_SimpleFollowFormationSlots[] =
  1856. {
  1857. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1858. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1859. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1860. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1861. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1862. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1863. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1864. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1865. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1866. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1867. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1868. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1869. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1870. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1871. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1872. { 1, { 0, 0, 0 }, 0, 96, 120, -1, 128 },
  1873. };
  1874. static AI_FollowFormation_t g_SimpleFollowFormation =
  1875. {
  1876. "Simple",
  1877. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  1878. ARRAYSIZE(g_SimpleFollowFormationSlots),
  1879. 168, // followPointTolerance
  1880. 36, // targetMoveTolerance
  1881. 60, // repathOnRouteTolerance
  1882. 190, // walkTolerance
  1883. 300, // coverTolerance
  1884. 300, // enemyLOSTolerance
  1885. 300, // chaseEnemyTolerance
  1886. g_SimpleFollowFormationSlots,
  1887. };
  1888. //-------------------------------------
  1889. static AI_FollowSlot_t g_WideFollowFormationSlots[] =
  1890. {
  1891. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1892. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1893. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1894. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1895. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1896. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1897. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1898. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1899. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1900. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1901. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1902. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1903. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1904. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1905. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1906. { 1, { 0, 0, 0 }, 0, 120, 240, -1, 128 },
  1907. };
  1908. static AI_FollowFormation_t g_WideFollowFormation =
  1909. {
  1910. "Wide",
  1911. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  1912. ARRAYSIZE(g_WideFollowFormationSlots),
  1913. 168, // followPointTolerance
  1914. 72, // targetMoveTolerance
  1915. 60, // repathOnRouteTolerance
  1916. 190, // walkTolerance
  1917. 600, // coverTolerance
  1918. 600, // enemyLOSTolerance
  1919. 600, // chaseEnemyTolerance
  1920. g_WideFollowFormationSlots,
  1921. };
  1922. //---------------------------------------------
  1923. // Antlion use very loose following criteria
  1924. static AI_FollowSlot_t g_AntlionFollowFormationSlots[] =
  1925. {
  1926. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1927. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1928. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1929. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1930. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1931. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1932. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1933. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1934. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1935. { 1, { 0, 0, 0 }, 0, 150, 250, -1, 128 },
  1936. };
  1937. static AI_FollowFormation_t g_AntlionFollowFormation =
  1938. {
  1939. "Antlion",
  1940. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  1941. ARRAYSIZE(g_AntlionFollowFormationSlots),
  1942. 168, // followPointTolerance
  1943. 36, // targetMoveTolerance
  1944. 60, // repathOnRouteTolerance
  1945. 190, // walkTolerance
  1946. 1024, // coverTolerance
  1947. 1024, // enemyLOSTolerance
  1948. 1024, // chaseEnemyTolerance
  1949. g_AntlionFollowFormationSlots,
  1950. };
  1951. //-------------------------------------
  1952. #define COMMANDER_TOLERANCE (13.0 * 1.415)
  1953. static AI_FollowSlot_t g_CommanderFollowFormationSlots[] =
  1954. {
  1955. { 2, { 0, 0, 0 }, 0, COMMANDER_TOLERANCE, COMMANDER_TOLERANCE, -1, 48 },
  1956. { 1, { 0, 0, 0 }, 0, COMMANDER_TOLERANCE, COMMANDER_TOLERANCE, -1, 48 },
  1957. { 1, { 0, 0, 0 }, 0, COMMANDER_TOLERANCE, COMMANDER_TOLERANCE, -1, 48 },
  1958. { 1, { 0, 0, 0 }, 0, COMMANDER_TOLERANCE, COMMANDER_TOLERANCE, -1, 48 },
  1959. };
  1960. static AI_FollowFormation_t g_CommanderFollowFormation =
  1961. {
  1962. "Commander",
  1963. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  1964. ARRAYSIZE(g_CommanderFollowFormationSlots),
  1965. 168, // followPointTolerance
  1966. 6, // targetMoveTolerance
  1967. 60, // repathOnRouteTolerance
  1968. 12, // walkTolerance
  1969. 300, // coverTolerance
  1970. 300, // enemyLOSTolerance
  1971. 300, // chaseEnemyTolerance
  1972. g_CommanderFollowFormationSlots,
  1973. };
  1974. //-------------------------------------
  1975. static AI_FollowSlot_t g_TightFollowFormationSlots[] =
  1976. {
  1977. { 1, { 0, 0, 0 }, 0, 0, 0, -1, 48 },
  1978. { 1, { 0, 0, 0 }, 0, 0, 0, -1, 48 },
  1979. { 1, { 0, 0, 0 }, 0, 0, 0, -1, 48 },
  1980. { 1, { 0, 0, 0 }, 0, 0, 0, -1, 48 },
  1981. };
  1982. static AI_FollowFormation_t g_TightFollowFormation =
  1983. {
  1984. "Tight",
  1985. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  1986. ARRAYSIZE(g_CommanderFollowFormationSlots),
  1987. 48, // followPointTolerance
  1988. 6, // targetMoveTolerance
  1989. 60, // repathOnRouteTolerance
  1990. 12, // walkTolerance
  1991. 300, // coverTolerance
  1992. 32, // enemyLOSTolerance
  1993. 32, // chaseEnemyTolerance
  1994. g_TightFollowFormationSlots,
  1995. };
  1996. //-------------------------------------
  1997. static AI_FollowSlot_t g_MediumFollowFormationSlots[] =
  1998. {
  1999. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2000. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2001. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2002. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2003. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2004. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2005. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2006. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2007. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2008. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2009. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2010. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2011. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2012. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2013. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2014. { 1, { 0, 0, 0 }, 0, 156, 156, -1, 128 },
  2015. };
  2016. static AI_FollowFormation_t g_MediumFollowFormation =
  2017. {
  2018. "Medium",
  2019. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  2020. ARRAYSIZE(g_MediumFollowFormationSlots),
  2021. 168, // followPointTolerance
  2022. 36, // targetMoveTolerance
  2023. 60, // repathOnRouteTolerance
  2024. 190, // walkTolerance
  2025. 300, // coverTolerance
  2026. 300, // enemyLOSTolerance
  2027. 300, // chaseEnemyTolerance
  2028. g_MediumFollowFormationSlots,
  2029. };
  2030. //-------------------------------------
  2031. static AI_FollowSlot_t g_SidekickFollowFormationSlots[] =
  2032. {
  2033. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2034. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2035. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2036. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2037. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2038. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2039. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2040. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2041. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2042. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2043. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2044. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2045. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2046. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2047. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2048. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2049. };
  2050. static AI_FollowFormation_t g_SidekickFollowFormation =
  2051. {
  2052. "Sidekick",
  2053. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS | AIFF_REQUIRE_LOS_OUTSIDE_COMBAT,
  2054. ARRAYSIZE(g_SidekickFollowFormationSlots),
  2055. 168, // followPointTolerance
  2056. 36, // targetMoveTolerance
  2057. 60, // repathOnRouteTolerance
  2058. 190, // walkTolerance
  2059. 300, // coverTolerance
  2060. 300, // enemyLOSTolerance
  2061. 300, // chaseEnemyTolerance
  2062. g_SidekickFollowFormationSlots,
  2063. };
  2064. //-------------------------------------
  2065. // Used for hunters following striders
  2066. //-------------------------------------
  2067. static AI_FollowSlot_t g_HunterFollowFormationSlots[] =
  2068. {
  2069. { 3, { 480, -240, -400 }, 0, 48, 64, 1000, 60 },
  2070. { 3, { 480, 240, -400 }, 0, 48, 64, 1000, 60 },
  2071. { 2, { 480, 0, -400 }, 0, 48, 64, 1000, 60 },
  2072. { 1, { -240, 0, -400 }, 0, 48, 64, 1000, 60 },
  2073. };
  2074. static AI_FollowFormation_t g_HunterFollowFormation =
  2075. {
  2076. "Hunter",
  2077. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS,
  2078. ARRAYSIZE(g_HunterFollowFormationSlots),
  2079. 48, // followPointTolerance
  2080. 48, // targetMoveTolerance
  2081. 60,//180, // repathOnRouteTolerance
  2082. 0, // walkTolerance
  2083. 960, // coverTolerance
  2084. 960, // enemyLOSTolerance
  2085. 1920, // chaseEnemyTolerance
  2086. g_HunterFollowFormationSlots,
  2087. };
  2088. //-------------------------------------
  2089. static AI_FollowSlot_t g_VortigauntFollowFormationSlots[] =
  2090. {
  2091. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2092. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2093. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2094. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2095. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2096. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2097. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2098. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2099. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2100. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2101. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2102. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2103. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2104. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2105. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2106. { 1, { 0, 0, 0 }, 0, 120, 160, 256, 128 },
  2107. };
  2108. static AI_FollowFormation_t g_VortigauntFollowFormation =
  2109. {
  2110. "Vortigaunt",
  2111. AIFF_DEFAULT | AIFF_USE_FOLLOW_POINTS | AIFF_REQUIRE_LOS_OUTSIDE_COMBAT,
  2112. ARRAYSIZE(g_VortigauntFollowFormationSlots),
  2113. 168, // followPointTolerance
  2114. 36, // targetMoveTolerance
  2115. 60, // repathOnRouteTolerance
  2116. 190, // walkTolerance
  2117. 300, // coverTolerance
  2118. (50*12), // enemyLOSTolerance
  2119. (50*12), // chaseEnemyTolerance
  2120. g_VortigauntFollowFormationSlots,
  2121. };
  2122. //-----------------------------------------------------------------------------
  2123. // NOTE: these must correspond with the AI_Formations_t enumeration in AI_Behavior_Follow.h!!
  2124. //-----------------------------------------------------------------------------
  2125. AI_FollowFormation_t *g_AI_Formations[] =
  2126. {
  2127. &g_SimpleFollowFormation,
  2128. &g_WideFollowFormation,
  2129. &g_AntlionFollowFormation,
  2130. &g_CommanderFollowFormation,
  2131. &g_TightFollowFormation,
  2132. &g_MediumFollowFormation,
  2133. &g_SidekickFollowFormation,
  2134. &g_HunterFollowFormation,
  2135. &g_VortigauntFollowFormation,
  2136. };
  2137. AI_FollowFormation_t *AIGetFormation( AI_Formations_t formation )
  2138. {
  2139. if ( formation < 0 )
  2140. formation = (AI_Formations_t)0;
  2141. else if ( formation >= ARRAYSIZE( g_AI_Formations ) )
  2142. formation = (AI_Formations_t)(ARRAYSIZE( g_AI_Formations ) - 1 );
  2143. return g_AI_Formations[formation];
  2144. }
  2145. //---------------------------------------------------------
  2146. bool CAI_FollowManager::AddFollower( CBaseEntity *pTarget, CAI_BaseNPC *pFollower, AI_Formations_t formation, AI_FollowManagerInfoHandle_t *pHandle )
  2147. {
  2148. AI_FollowGroup_t *pGroup = FindCreateGroup( pTarget, formation );
  2149. int slot = FindBestSlot( pGroup );
  2150. if ( slot != -1 )
  2151. {
  2152. MEM_ALLOC_CREDIT();
  2153. AI_FollowSlot_t *pSlot = &pGroup->pFormation->pSlots[slot];
  2154. int i = pGroup->followers.AddToTail( );
  2155. AI_Follower_t *iterNode = &pGroup->followers[i];
  2156. iterNode->hFollower = pFollower;
  2157. iterNode->slot = slot;
  2158. iterNode->pGroup = pGroup;
  2159. pGroup->slotUsage.Set( slot );
  2160. CalculateFieldsFromSlot( pSlot, &iterNode->navInfo );
  2161. pHandle->m_hFollower = i;
  2162. pHandle->m_pGroup = pGroup;
  2163. return true;
  2164. }
  2165. pHandle->m_hFollower = 0;
  2166. pHandle->m_pGroup = NULL;
  2167. return false;
  2168. }
  2169. //-------------------------------------
  2170. bool CAI_FollowManager::CalcFollowPosition( AI_FollowManagerInfoHandle_t& hInfo, AI_FollowNavInfo_t *pNavInfo )
  2171. {
  2172. if ( hInfo.m_pGroup && hInfo.m_hFollower )
  2173. {
  2174. AI_FollowGroup_t *pGroup = hInfo.m_pGroup;
  2175. Assert( pGroup->hFollowTarget.Get() );
  2176. CBaseEntity *pTarget = pGroup->hFollowTarget;
  2177. AI_Follower_t *iterNode = &pGroup->followers[hInfo.m_hFollower];
  2178. if ( iterNode->navInfo.position != vec3_origin )
  2179. {
  2180. QAngle angles = pTarget->GetLocalAngles();
  2181. angles.x = angles.z = 0;
  2182. matrix3x4_t fRotateMatrix;
  2183. AngleMatrix(angles, fRotateMatrix);
  2184. VectorRotate( iterNode->navInfo.position, fRotateMatrix, pNavInfo->position);
  2185. pNavInfo->position += pTarget->WorldSpaceCenter();
  2186. }
  2187. else
  2188. {
  2189. pNavInfo->position = iterNode->navInfo.position + pTarget->WorldSpaceCenter();
  2190. }
  2191. pNavInfo->tolerance = iterNode->navInfo.tolerance;
  2192. pNavInfo->range = iterNode->navInfo.range;
  2193. pNavInfo->Zrange = iterNode->navInfo.Zrange;
  2194. pNavInfo->flags = pGroup->pFormation->flags;
  2195. pNavInfo->followPointTolerance = pGroup->pFormation->followPointTolerance;
  2196. pNavInfo->targetMoveTolerance = pGroup->pFormation->targetMoveTolerance;
  2197. pNavInfo->repathOnRouteTolerance = pGroup->pFormation->repathOnRouteTolerance;
  2198. pNavInfo->walkTolerance = pGroup->pFormation->walkTolerance;
  2199. pNavInfo->coverTolerance = pGroup->pFormation->coverTolerance;
  2200. pNavInfo->enemyLOSTolerance = pGroup->pFormation->enemyLOSTolerance;
  2201. pNavInfo->chaseEnemyTolerance = pGroup->pFormation->chaseEnemyTolerance;
  2202. return true;
  2203. }
  2204. return false;
  2205. }
  2206. //-------------------------------------
  2207. bool CAI_FollowManager::RedistributeSlots( AI_FollowGroup_t *pGroup )
  2208. {
  2209. bool result = false;
  2210. CUtlRBTree<CBaseEntity *> movedFollowers;
  2211. SetDefLessFunc( movedFollowers );
  2212. const Vector &originFollowed = pGroup->hFollowTarget->GetAbsOrigin();
  2213. int bestSlot;
  2214. while ( ( bestSlot = FindBestSlot( pGroup ) ) != -1 && ((int)movedFollowers.Count() < pGroup->followers.Count()) )
  2215. {
  2216. AI_FollowSlot_t * pSlot = &pGroup->pFormation->pSlots[bestSlot];
  2217. Vector slotPos = originFollowed + pSlot->position;
  2218. int h = pGroup->followers.Head();
  2219. int hBest = pGroup->followers.InvalidIndex();
  2220. float distSqBest = FLT_MAX;
  2221. while ( h != pGroup->followers.InvalidIndex() )
  2222. {
  2223. AI_Follower_t *p = &pGroup->followers[h];
  2224. if ( movedFollowers.Find( p->hFollower ) == movedFollowers.InvalidIndex() &&
  2225. ( p->slot == -1 || pSlot->priority > pGroup->pFormation->pSlots[p->slot].priority ) )
  2226. {
  2227. float distSqCur = ( p->hFollower->GetAbsOrigin() - slotPos ).LengthSqr();
  2228. if ( distSqCur < distSqBest )
  2229. {
  2230. hBest = h;
  2231. }
  2232. }
  2233. h = pGroup->followers.Next( h );
  2234. }
  2235. if ( hBest == pGroup->followers.InvalidIndex() )
  2236. break;
  2237. AI_Follower_t *pBest = &pGroup->followers[hBest];
  2238. if ( pBest->slot != -1 )
  2239. {
  2240. pGroup->slotUsage.Clear( pBest->slot );
  2241. }
  2242. pBest->slot = bestSlot;
  2243. CalculateFieldsFromSlot( pSlot, &pBest->navInfo );
  2244. pGroup->slotUsage.Set( bestSlot );
  2245. movedFollowers.Insert( pBest->hFollower );
  2246. result = true;
  2247. }
  2248. return result;
  2249. }
  2250. //-------------------------------------
  2251. void CAI_FollowManager::ChangeFormation( AI_FollowManagerInfoHandle_t& hInfo, AI_Formations_t formation )
  2252. {
  2253. if ( !hInfo.m_pGroup || !hInfo.m_hFollower )
  2254. return;
  2255. AI_FollowGroup_t *pGroup = hInfo.m_pGroup;
  2256. AI_FollowFormation_t *pNewFormation = AIGetFormation( formation );
  2257. if ( pNewFormation == pGroup->pFormation )
  2258. return;
  2259. int h = pGroup->followers.Head();
  2260. while ( h != pGroup->followers.InvalidIndex() )
  2261. {
  2262. CAI_FollowBehavior *pFollowBehavior;
  2263. AI_Follower_t *p = &pGroup->followers[h];
  2264. p->slot = -1;
  2265. p->hFollower->GetBehavior( &pFollowBehavior );
  2266. Assert( pFollowBehavior );
  2267. if ( pFollowBehavior )
  2268. {
  2269. pFollowBehavior->m_params.formation = formation;
  2270. pFollowBehavior->m_TargetMonitor.ClearMark();
  2271. pFollowBehavior->SetCondition( CAI_FollowBehavior::COND_TARGET_MOVED_FROM_MARK );
  2272. pFollowBehavior->m_bTargetUnreachable = false;
  2273. }
  2274. h = pGroup->followers.Next( h );
  2275. }
  2276. pGroup->slotUsage.ClearAll();
  2277. pGroup->pFormation = pNewFormation;
  2278. pGroup->slotUsage.Resize( pGroup->pFormation->nSlots );
  2279. RedistributeSlots( pGroup );
  2280. #ifdef DEBUG
  2281. h = pGroup->followers.Head();
  2282. while ( h != pGroup->followers.InvalidIndex() )
  2283. {
  2284. AI_Follower_t *p = &pGroup->followers[h];
  2285. Assert( p->slot != -1 );
  2286. h = pGroup->followers.Next( h );
  2287. }
  2288. #endif
  2289. }
  2290. //-------------------------------------
  2291. void CAI_FollowManager::RemoveFollower( AI_FollowManagerInfoHandle_t& hInfo )
  2292. {
  2293. if ( hInfo.m_pGroup && hInfo.m_hFollower )
  2294. {
  2295. AI_FollowGroup_t *pGroup = hInfo.m_pGroup;
  2296. AI_Follower_t* iterNode = &pGroup->followers[hInfo.m_hFollower];
  2297. int slot = iterNode->slot;
  2298. pGroup->slotUsage.Clear( slot );
  2299. pGroup->followers.Remove( hInfo.m_hFollower );
  2300. if ( pGroup->followers.Count() == 0 )
  2301. {
  2302. RemoveGroup( pGroup );
  2303. }
  2304. else
  2305. {
  2306. if ( pGroup->hFollowTarget != NULL ) // NULL on level unload
  2307. {
  2308. RedistributeSlots( pGroup );
  2309. }
  2310. }
  2311. }
  2312. }
  2313. //-------------------------------------
  2314. int CAI_FollowManager::FindBestSlot( AI_FollowGroup_t *pGroup )
  2315. {
  2316. // @TODO (toml 02-28-03): crude placeholder
  2317. int nSlots = pGroup->pFormation->nSlots;
  2318. int best = -1;
  2319. int bestPriority = -1;
  2320. for ( int i = 0; i < nSlots; i++ )
  2321. {
  2322. if ( !pGroup->slotUsage.IsBitSet( i ) && pGroup->pFormation->pSlots[i].priority > bestPriority )
  2323. {
  2324. bestPriority = pGroup->pFormation->pSlots[i].priority;
  2325. best = i;
  2326. }
  2327. }
  2328. return best;
  2329. }
  2330. //-------------------------------------
  2331. void CAI_FollowManager::CalculateFieldsFromSlot( AI_FollowSlot_t *pSlot, AI_FollowNavInfo_t *pFollowerInfo )
  2332. {
  2333. // @TODO (toml 02-28-03): placeholder. Force break if someone tries to actually use
  2334. Assert( pSlot->positionVariability == 0.0 );
  2335. //Assert( pSlot->tolerance == AIN_DEF_TOLERANCE );
  2336. pFollowerInfo->position = pSlot->position;
  2337. pFollowerInfo->range = random->RandomFloat( pSlot->rangeMin, pSlot->rangeMax );
  2338. pFollowerInfo->Zrange = pSlot->Zrange;
  2339. pFollowerInfo->tolerance = pSlot->tolerance;
  2340. }
  2341. //-------------------------------------
  2342. AI_FollowGroup_t *CAI_FollowManager::FindCreateGroup( CBaseEntity *pTarget, AI_Formations_t formation )
  2343. {
  2344. AI_FollowGroup_t *pGroup = FindGroup( pTarget );
  2345. if ( !pGroup )
  2346. {
  2347. {
  2348. MEM_ALLOC_CREDIT();
  2349. pGroup = new AI_FollowGroup_t;
  2350. }
  2351. pGroup->pFormation = AIGetFormation( formation );
  2352. pGroup->slotUsage.Resize( pGroup->pFormation->nSlots );
  2353. pGroup->hFollowTarget = pTarget;
  2354. m_groups.AddToHead( pGroup );
  2355. }
  2356. return pGroup;
  2357. }
  2358. //-------------------------------------
  2359. void CAI_FollowManager::RemoveGroup( AI_FollowGroup_t *pGroup )
  2360. {
  2361. for ( int i = 0; i < m_groups.Count(); i++ )
  2362. {
  2363. if ( m_groups[i] == pGroup )
  2364. {
  2365. delete m_groups[i];
  2366. m_groups.FastRemove(i);
  2367. return;
  2368. }
  2369. }
  2370. }
  2371. //-------------------------------------
  2372. AI_FollowGroup_t *CAI_FollowManager::FindGroup( CBaseEntity *pTarget )
  2373. {
  2374. for ( int i = 0; i < m_groups.Count(); i++ )
  2375. {
  2376. if ( m_groups[i]->hFollowTarget == pTarget )
  2377. return m_groups[i];
  2378. }
  2379. return NULL;
  2380. }
  2381. //-------------------------------------
  2382. AI_FollowGroup_t *CAI_FollowManager::FindFollowerGroup( CBaseEntity *pFollower )
  2383. {
  2384. for ( int i = 0; i < m_groups.Count(); i++ )
  2385. {
  2386. int h = m_groups[i]->followers.Head();
  2387. while( h != m_groups[i]->followers.InvalidIndex() )
  2388. {
  2389. AI_Follower_t *p = &m_groups[i]->followers[h];
  2390. if ( p->hFollower.Get() == pFollower )
  2391. return m_groups[i];
  2392. h = m_groups[i]->followers.Next( h );
  2393. }
  2394. }
  2395. return NULL;
  2396. }
  2397. //-----------------------------------------------------------------------------
  2398. AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER(CAI_FollowBehavior)
  2399. DECLARE_TASK(TASK_CANT_FOLLOW)
  2400. DECLARE_TASK(TASK_FACE_FOLLOW_TARGET)
  2401. DECLARE_TASK(TASK_MOVE_TO_FOLLOW_POSITION)
  2402. DECLARE_TASK(TASK_GET_PATH_TO_FOLLOW_POSITION)
  2403. DECLARE_TASK(TASK_SET_FOLLOW_TARGET_MARK)
  2404. DECLARE_TASK(TASK_FOLLOWER_FACE_TACTICAL)
  2405. DECLARE_TASK(TASK_SET_FOLLOW_DELAY)
  2406. DECLARE_TASK(TASK_GET_PATH_TO_FOLLOW_POINT)
  2407. DECLARE_TASK(TASK_ARRIVE_AT_FOLLOW_POINT)
  2408. DECLARE_TASK(TASK_BEGIN_STAND_AT_WAIT_POINT)
  2409. DECLARE_TASK(TASK_SET_FOLLOW_POINT_STAND_SCHEDULE)
  2410. DECLARE_CONDITION(COND_TARGET_MOVED_FROM_MARK)
  2411. DECLARE_CONDITION(COND_FOUND_WAIT_POINT)
  2412. DECLARE_CONDITION(COND_FOLLOW_DELAY_EXPIRED)
  2413. DECLARE_CONDITION(COND_FOLLOW_TARGET_VISIBLE)
  2414. DECLARE_CONDITION(COND_FOLLOW_TARGET_NOT_VISIBLE)
  2415. DECLARE_CONDITION(COND_FOLLOW_WAIT_POINT_INVALID)
  2416. DECLARE_CONDITION(COND_FOLLOW_PLAYER_IS_LIT)
  2417. DECLARE_CONDITION(COND_FOLLOW_PLAYER_IS_NOT_LIT)
  2418. //=========================================================
  2419. // > SCHED_FOLLOWER_MOVE_AWAY_END
  2420. //=========================================================
  2421. DEFINE_SCHEDULE
  2422. (
  2423. SCHED_FOLLOWER_MOVE_AWAY_END,
  2424. " Tasks"
  2425. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FOLLOWER_MOVE_AWAY_FAIL "
  2426. " TASK_STOP_MOVING 0"
  2427. " TASK_FACE_FOLLOW_TARGET 0"
  2428. " TASK_SET_FOLLOW_DELAY 2"
  2429. ""
  2430. " Interrupts"
  2431. " COND_PLAYER_PUSHING"
  2432. )
  2433. //=========================================================
  2434. // > SCHED_FOLLOWER_MOVE_AWAY_FAIL
  2435. //=========================================================
  2436. DEFINE_SCHEDULE
  2437. (
  2438. SCHED_FOLLOWER_MOVE_AWAY_FAIL,
  2439. " Tasks"
  2440. " TASK_STOP_MOVING 0"
  2441. " TASK_FACE_FOLLOW_TARGET 0"
  2442. " TASK_SET_FOLLOW_DELAY 2"
  2443. ""
  2444. " Interrupts"
  2445. " COND_PLAYER_PUSHING"
  2446. )
  2447. //=========================================================
  2448. // > SCHED_FOLLOW
  2449. //=========================================================
  2450. DEFINE_SCHEDULE
  2451. (
  2452. SCHED_FOLLOW,
  2453. " Tasks"
  2454. " TASK_GET_PATH_TO_FOLLOW_POSITION 0"
  2455. " TASK_MOVE_TO_FOLLOW_POSITION 0"
  2456. " TASK_WAIT_FOR_MOVEMENT 0"
  2457. " TASK_SET_SCHEDULE SCHEDULE:SCHED_TARGET_FACE "
  2458. ""
  2459. " Interrupts"
  2460. " COND_NEW_ENEMY"
  2461. " COND_LIGHT_DAMAGE"
  2462. " COND_HEAVY_DAMAGE"
  2463. " COND_HEAR_DANGER"
  2464. " COND_PROVOKED"
  2465. " COND_PLAYER_PUSHING"
  2466. " COND_BETTER_WEAPON_AVAILABLE"
  2467. );
  2468. //=========================================================
  2469. // > SCHED_MOVE_TO_FACE_FOLLOW_TARGET
  2470. //=========================================================
  2471. DEFINE_SCHEDULE
  2472. (
  2473. SCHED_MOVE_TO_FACE_FOLLOW_TARGET,
  2474. " Tasks"
  2475. // " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2476. // " TASK_FACE_FOLLOW_TARGET 0"
  2477. // " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2478. " TASK_SET_SCHEDULE SCHEDULE:SCHED_FOLLOW"
  2479. ""
  2480. " Interrupts"
  2481. " COND_NEW_ENEMY"
  2482. " COND_LIGHT_DAMAGE"
  2483. " COND_HEAVY_DAMAGE"
  2484. " COND_HEAR_DANGER"
  2485. " COND_PROVOKED"
  2486. " COND_PLAYER_PUSHING"
  2487. )
  2488. //=========================================================
  2489. // > SCHED_FACE_FOLLOW_TARGET
  2490. //=========================================================
  2491. DEFINE_SCHEDULE
  2492. (
  2493. SCHED_FACE_FOLLOW_TARGET,
  2494. " Tasks"
  2495. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2496. " TASK_FACE_FOLLOW_TARGET 0"
  2497. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2498. " TASK_SET_SCHEDULE SCHEDULE:SCHED_FOLLOWER_IDLE_STAND "
  2499. ""
  2500. " Interrupts"
  2501. " COND_NEW_ENEMY"
  2502. " COND_LIGHT_DAMAGE"
  2503. " COND_HEAVY_DAMAGE"
  2504. " COND_HEAR_DANGER"
  2505. " COND_PROVOKED"
  2506. " COND_PLAYER_PUSHING"
  2507. " COND_GIVE_WAY"
  2508. )
  2509. //=========================================================
  2510. // > SCHED_FOLLOWER_GO_TO_WAIT_POINT
  2511. //=========================================================
  2512. DEFINE_SCHEDULE
  2513. (
  2514. SCHED_FOLLOWER_GO_TO_WAIT_POINT,
  2515. " Tasks"
  2516. " TASK_LOCK_HINTNODE 0 " // this will fail the schedule if no hint node or not already lockable
  2517. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_FOLLOWER_GO_TO_WAIT_POINT_FAIL"
  2518. " TASK_SET_TOLERANCE_DISTANCE 4"
  2519. " TASK_GET_PATH_TO_FOLLOW_POINT 0"
  2520. " TASK_SET_FOLLOW_TARGET_MARK 0"
  2521. " TASK_WALK_PATH 0"
  2522. " TASK_WAIT_FOR_MOVEMENT 0"
  2523. " TASK_ARRIVE_AT_FOLLOW_POINT 0"
  2524. " TASK_SET_FOLLOW_POINT_STAND_SCHEDULE 0"
  2525. ""
  2526. " Interrupts"
  2527. " COND_NEW_ENEMY"
  2528. " COND_LIGHT_DAMAGE"
  2529. " COND_HEAVY_DAMAGE"
  2530. " COND_HEAR_DANGER"
  2531. " COND_PROVOKED"
  2532. " COND_PLAYER_PUSHING"
  2533. " COND_TARGET_MOVED_FROM_MARK"
  2534. )
  2535. //=========================================================
  2536. // > SCHED_FOLLOWER_GO_TO_WAIT_POINT_FAIL
  2537. //=========================================================
  2538. DEFINE_SCHEDULE
  2539. (
  2540. SCHED_FOLLOWER_GO_TO_WAIT_POINT_FAIL,
  2541. " Tasks"
  2542. " TASK_CLEAR_HINTNODE .5"
  2543. " TASK_SET_FOLLOW_DELAY 1"
  2544. ""
  2545. " Interrupts"
  2546. )
  2547. //=========================================================
  2548. // > SCHED_FOLLOWER_STAND_AT_WAIT_POINT
  2549. //=========================================================
  2550. DEFINE_SCHEDULE
  2551. (
  2552. SCHED_FOLLOWER_STAND_AT_WAIT_POINT,
  2553. " Tasks"
  2554. " TASK_BEGIN_STAND_AT_WAIT_POINT 0"
  2555. " TASK_PLAY_HINT_ACTIVITY 0"
  2556. " TASK_SET_SCHEDULE SCHEDULE:SCHED_FOLLOWER_STAND_AT_WAIT_POINT "
  2557. ""
  2558. " Interrupts"
  2559. " COND_NEW_ENEMY"
  2560. " COND_LIGHT_DAMAGE"
  2561. " COND_HEAVY_DAMAGE"
  2562. " COND_HEAR_DANGER"
  2563. " COND_PROVOKED"
  2564. " COND_PLAYER_PUSHING"
  2565. " COND_TARGET_MOVED_FROM_MARK"
  2566. " COND_GIVE_WAY"
  2567. " COND_FOLLOW_WAIT_POINT_INVALID"
  2568. // " COND_IDLE_INTERRUPT"
  2569. )
  2570. DEFINE_SCHEDULE
  2571. (
  2572. SCHED_FOLLOWER_IDLE_STAND,
  2573. " Tasks"
  2574. " TASK_STOP_MOVING 0"
  2575. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2576. // " TASK_SET_FOLLOW_TARGET_MARK 0"
  2577. " TASK_WAIT 2.5"
  2578. " TASK_FACE_FOLLOW_TARGET 0"
  2579. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2580. " TASK_WAIT 3"
  2581. ""
  2582. " Interrupts"
  2583. " COND_NEW_ENEMY"
  2584. " COND_SEE_FEAR"
  2585. " COND_CAN_RANGE_ATTACK1"
  2586. " COND_NO_PRIMARY_AMMO"
  2587. " COND_LIGHT_DAMAGE"
  2588. " COND_HEAVY_DAMAGE"
  2589. " COND_SMELL"
  2590. " COND_PROVOKED"
  2591. " COND_GIVE_WAY"
  2592. " COND_HEAR_DANGER"
  2593. " COND_HEAR_COMBAT"
  2594. " COND_HEAR_BULLET_IMPACT"
  2595. " COND_PLAYER_PUSHING"
  2596. " COND_TARGET_MOVED_FROM_MARK"
  2597. " COND_FOLLOW_DELAY_EXPIRED"
  2598. " COND_FOUND_WAIT_POINT"
  2599. " COND_IDLE_INTERRUPT"
  2600. " COND_BETTER_WEAPON_AVAILABLE"
  2601. )
  2602. DEFINE_SCHEDULE
  2603. (
  2604. SCHED_FOLLOWER_COMBAT_FACE,
  2605. " Tasks"
  2606. " TASK_STOP_MOVING 0"
  2607. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2608. " TASK_FACE_ENEMY 0"
  2609. ""
  2610. " Interrupts"
  2611. " COND_NEW_ENEMY"
  2612. " COND_SEE_FEAR"
  2613. " COND_CAN_RANGE_ATTACK1"
  2614. " COND_CAN_RANGE_ATTACK2"
  2615. " COND_CAN_MELEE_ATTACK1"
  2616. " COND_CAN_MELEE_ATTACK2"
  2617. " COND_NO_PRIMARY_AMMO"
  2618. " COND_LIGHT_DAMAGE"
  2619. " COND_HEAVY_DAMAGE"
  2620. " COND_SMELL"
  2621. " COND_PROVOKED"
  2622. " COND_GIVE_WAY"
  2623. " COND_HEAR_DANGER"
  2624. " COND_HEAR_COMBAT"
  2625. " COND_HEAR_BULLET_IMPACT"
  2626. " COND_PLAYER_PUSHING"
  2627. " COND_TARGET_MOVED_FROM_MARK"
  2628. " COND_FOLLOW_DELAY_EXPIRED"
  2629. " COND_FOUND_WAIT_POINT"
  2630. " COND_BETTER_WEAPON_AVAILABLE"
  2631. )
  2632. AI_END_CUSTOM_SCHEDULE_PROVIDER()
  2633. //=============================================================================