Counter Strike : Global Offensive Source Code
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.

4875 lines
120 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Functions and data pertaining to the NPCs' AI scheduling system.
  4. // Implements default NPC tasks and schedules.
  5. //
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_default.h"
  9. #include "animation.h"
  10. #include "scripted.h"
  11. #include "soundent.h"
  12. #include "entitylist.h"
  13. #include "basecombatweapon.h"
  14. #include "bitstring.h"
  15. #include "ai_task.h"
  16. #include "ai_network.h"
  17. #include "ai_schedule.h"
  18. #include "ai_hull.h"
  19. #include "ai_node.h"
  20. #include "ai_motor.h"
  21. #include "ai_hint.h"
  22. #include "ai_memory.h"
  23. #include "ai_navigator.h"
  24. #include "ai_tacticalservices.h"
  25. #include "ai_moveprobe.h"
  26. #include "ai_squadslot.h"
  27. #include "ai_squad.h"
  28. #include "ai_speech.h"
  29. #include "game.h"
  30. #include "IEffects.h"
  31. #include "vstdlib/random.h"
  32. #include "ndebugoverlay.h"
  33. #include "env_debughistory.h"
  34. #include "ai_behavior.h"
  35. #include "global_event_log.h"
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. extern ConVar ai_task_pre_script;
  39. extern ConVar ai_use_efficiency;
  40. extern ConVar ai_use_think_optimizations;
  41. #define ShouldUseEfficiency() ( ai_use_think_optimizations.GetBool() && ai_use_efficiency.GetBool() )
  42. ConVar ai_simulate_task_overtime( "ai_simulate_task_overtime", "0" );
  43. #define MAX_TASKS_RUN 10
  44. struct TaskTimings
  45. {
  46. const char *pszTask;
  47. CFastTimer selectSchedule;
  48. CFastTimer startTimer;
  49. CFastTimer runTimer;
  50. };
  51. TaskTimings g_AITaskTimings[MAX_TASKS_RUN];
  52. int g_nAITasksRun;
  53. void CAI_BaseNPC::DumpTaskTimings()
  54. {
  55. DevMsg(" Tasks timings:\n" );
  56. for ( int i = 0; i < g_nAITasksRun; ++i )
  57. {
  58. DevMsg( " %32s -- select %5.2f, start %5.2f, run %5.2f\n", g_AITaskTimings[i].pszTask,
  59. g_AITaskTimings[i].selectSchedule.GetDuration().GetMillisecondsF(),
  60. g_AITaskTimings[i].startTimer.GetDuration().GetMillisecondsF(),
  61. g_AITaskTimings[i].runTimer.GetDuration().GetMillisecondsF() );
  62. }
  63. }
  64. //=========================================================
  65. // FHaveSchedule - Returns true if NPC's GetCurSchedule()
  66. // is anything other than NULL.
  67. //=========================================================
  68. bool CAI_BaseNPC::FHaveSchedule( void )
  69. {
  70. if ( GetCurSchedule() == NULL )
  71. {
  72. return false;
  73. }
  74. return true;
  75. }
  76. //=========================================================
  77. // ClearSchedule - blanks out the caller's schedule pointer
  78. // and index.
  79. //=========================================================
  80. void CAI_BaseNPC::ClearSchedule( const char *szReason )
  81. {
  82. if (szReason && m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  83. {
  84. DevMsg( this, AIMF_IGNORE_SELECTED, " Schedule cleared: %s\n", szReason );
  85. }
  86. if ( szReason )
  87. {
  88. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): Schedule cleared: %s\n", GetDebugName(), entindex(), szReason ) );
  89. }
  90. m_ScheduleState.timeCurTaskStarted = m_ScheduleState.timeStarted = 0;
  91. m_ScheduleState.bScheduleWasInterrupted = true;
  92. SetTaskStatus( TASKSTATUS_NEW );
  93. m_IdealSchedule = SCHED_NONE;
  94. m_pSchedule = NULL;
  95. ResetScheduleCurTaskIndex();
  96. m_InverseIgnoreConditions.SetAll();
  97. }
  98. //=========================================================
  99. // FScheduleDone - Returns true if the caller is on the
  100. // last task in the schedule
  101. //=========================================================
  102. bool CAI_BaseNPC::FScheduleDone ( void )
  103. {
  104. Assert( GetCurSchedule() != NULL );
  105. if ( GetScheduleCurTaskIndex() == GetCurSchedule()->NumTasks() )
  106. {
  107. return true;
  108. }
  109. return false;
  110. }
  111. //=========================================================
  112. bool CAI_BaseNPC::SetSchedule( int localScheduleID )
  113. {
  114. CAI_Schedule *pNewSchedule = GetScheduleOfType( localScheduleID );
  115. if ( pNewSchedule )
  116. {
  117. // ken: I'm don't know of any remaining cases, but if you find one, hunt it down as to why the schedule is getting slammed while they're in the middle of script
  118. if (m_hCine != NULL)
  119. {
  120. if (!(localScheduleID == SCHED_SLEEP || localScheduleID == SCHED_WAIT_FOR_SCRIPT || localScheduleID == SCHED_SCRIPTED_WALK || localScheduleID == SCHED_SCRIPTED_RUN || localScheduleID == SCHED_SCRIPTED_CUSTOM_MOVE || localScheduleID == SCHED_SCRIPTED_WAIT || localScheduleID == SCHED_SCRIPTED_FACE) )
  121. {
  122. Assert( 0 );
  123. // ExitScriptedSequence();
  124. }
  125. }
  126. m_IdealSchedule = GetGlobalScheduleId( localScheduleID );
  127. SetSchedule( pNewSchedule );
  128. return true;
  129. }
  130. return false;
  131. }
  132. //=========================================================
  133. // SetSchedule - replaces the NPC's schedule pointer
  134. // with the passed pointer, and sets the ScheduleIndex back
  135. // to 0
  136. //=========================================================
  137. #define SCHEDULE_HISTORY_SIZE 10
  138. void CAI_BaseNPC::SetSchedule( CAI_Schedule *pNewSchedule )
  139. {
  140. Assert( pNewSchedule != NULL );
  141. OnSetSchedule();
  142. m_ScheduleState.timeCurTaskStarted = m_ScheduleState.timeStarted = gpGlobals->curtime;
  143. m_ScheduleState.bScheduleWasInterrupted = false;
  144. m_pSchedule = pNewSchedule ;
  145. ResetScheduleCurTaskIndex();
  146. SetTaskStatus( TASKSTATUS_NEW );
  147. m_failSchedule = SCHED_NONE;
  148. bool bCondInPVS = HasCondition( COND_IN_PVS );
  149. m_Conditions.ClearAll();
  150. if ( bCondInPVS )
  151. SetCondition( COND_IN_PVS );
  152. m_bConditionsGathered = false;
  153. GetNavigator()->ClearGoal();
  154. m_InverseIgnoreConditions.SetAll();
  155. Forget( bits_MEMORY_TURNING );
  156. /*
  157. #if _DEBUG
  158. if ( !ScheduleFromName( pNewSchedule->GetName() ) )
  159. {
  160. DevMsg( "Schedule %s not in table!!!\n", pNewSchedule->GetName() );
  161. }
  162. #endif
  163. */
  164. // this is very useful code if you can isolate a test case in a level with a single NPC. It will notify
  165. // you of every schedule selection the NPC makes.
  166. if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  167. {
  168. DevMsg(this, AIMF_IGNORE_SELECTED, "Schedule: %s (time: %.2f)\n", pNewSchedule->GetName(), gpGlobals->curtime );
  169. }
  170. if ( m_pEvent != NULL )
  171. {
  172. if ( m_pScheduleEvent != NULL )
  173. {
  174. GlobalEventLog.RemoveEvent( m_pScheduleEvent );
  175. }
  176. m_pScheduleEvent = GlobalEventLog.CreateEvent( "Schedule", false, m_pEvent );
  177. GlobalEventLog.AddKeyValue( m_pScheduleEvent, false, "Schedule", pNewSchedule->GetName() );
  178. }
  179. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Schedule: %s (time: %.2f)\n", GetDebugName(), entindex(), pNewSchedule->GetName(), gpGlobals->curtime ) );
  180. #ifdef AI_MONITOR_FOR_OSCILLATION
  181. if( m_bSelected )
  182. {
  183. AIScheduleChoice_t choice;
  184. choice.m_flTimeSelected = gpGlobals->curtime;
  185. choice.m_pScheduleSelected = pNewSchedule;
  186. m_ScheduleHistory.AddToHead(choice);
  187. if( m_ScheduleHistory.Count() > SCHEDULE_HISTORY_SIZE )
  188. {
  189. m_ScheduleHistory.Remove( SCHEDULE_HISTORY_SIZE );
  190. }
  191. assert( m_ScheduleHistory.Count() <= SCHEDULE_HISTORY_SIZE );
  192. // No analysis until the vector is full!
  193. if( m_ScheduleHistory.Count() == SCHEDULE_HISTORY_SIZE )
  194. {
  195. int iNumSelections = m_ScheduleHistory.Count();
  196. float flTimeSpan = m_ScheduleHistory.Head().m_flTimeSelected - m_ScheduleHistory.Tail().m_flTimeSelected;
  197. float flSelectionsPerSecond = ((float)iNumSelections) / flTimeSpan;
  198. Msg( "%d selections in %f seconds (avg. %f selections per second)\n", iNumSelections, flTimeSpan, flSelectionsPerSecond );
  199. if( flSelectionsPerSecond >= 8.0f )
  200. {
  201. DevMsg("\n\n %s is thrashing schedule selection:\n", GetDebugName() );
  202. for( int i = 0 ; i < m_ScheduleHistory.Count() ; i++ )
  203. {
  204. AIScheduleChoice_t choice = m_ScheduleHistory[i];
  205. Msg("--%s %f\n", choice.m_pScheduleSelected->GetName(), choice.m_flTimeSelected );
  206. }
  207. Msg("\n");
  208. CAI_BaseNPC::m_nDebugBits |= bits_debugDisableAI;
  209. }
  210. }
  211. }
  212. #endif//AI_MONITOR_FOR_OSCILLATION
  213. }
  214. //=========================================================
  215. // NextScheduledTask - increments the ScheduleIndex
  216. //=========================================================
  217. void CAI_BaseNPC::NextScheduledTask ( void )
  218. {
  219. Assert( GetCurSchedule() != NULL );
  220. SetTaskStatus( TASKSTATUS_NEW );
  221. IncScheduleCurTaskIndex();
  222. if ( FScheduleDone() )
  223. {
  224. // Reset memory of failed schedule
  225. m_failedSchedule = NULL;
  226. m_interuptSchedule = NULL;
  227. // just completed last task in schedule, so make it invalid by clearing it.
  228. SetCondition( COND_SCHEDULE_DONE );
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: This function allows NPCs to modify the interrupt mask for the
  233. // current schedule. This enables them to use base schedules but with
  234. // different interrupt conditions. Implement this function in your
  235. // derived class, and Set or Clear condition bits as you please.
  236. //
  237. // NOTE: Always call the base class in your implementation, but be
  238. // aware of the difference between changing the bits before vs.
  239. // changing the bits after calling the base implementation.
  240. //
  241. // Input : pBitString - Receives the updated interrupt mask.
  242. //-----------------------------------------------------------------------------
  243. void CAI_BaseNPC::BuildScheduleTestBits( void )
  244. {
  245. //NOTENOTE: Always defined in the leaf classes
  246. }
  247. //=========================================================
  248. // IsScheduleValid - returns true as long as the current
  249. // schedule is still the proper schedule to be executing,
  250. // taking into account all conditions
  251. //=========================================================
  252. bool CAI_BaseNPC::IsScheduleValid()
  253. {
  254. if ( GetCurSchedule() == NULL || GetCurSchedule()->NumTasks() == 0 )
  255. {
  256. return false;
  257. }
  258. //Start out with the base schedule's set interrupt conditions
  259. GetCurSchedule()->GetInterruptMask( &m_CustomInterruptConditions );
  260. // Let the leaf class modify our interrupt test bits, but:
  261. // - Don't allow any modifications when scripted
  262. // - Don't modify interrupts for Schedules that set the COND_NO_CUSTOM_INTERRUPTS bit.
  263. if ( m_NPCState != NPC_STATE_SCRIPT && !IsInLockedScene() && !m_CustomInterruptConditions.IsBitSet( COND_NO_CUSTOM_INTERRUPTS ) )
  264. {
  265. BuildScheduleTestBits();
  266. }
  267. //Any conditions set here will always be forced on the interrupt conditions
  268. SetCustomInterruptCondition( COND_NPC_FREEZE );
  269. // This is like: m_CustomInterruptConditions &= m_Conditions;
  270. CAI_ScheduleBits testBits;
  271. m_CustomInterruptConditions.And( m_Conditions, &testBits );
  272. if (!testBits.IsAllClear())
  273. {
  274. // If in developer mode save the interrupt text for debug output
  275. if (g_pDeveloper->GetInt())
  276. {
  277. // Reset memory of failed schedule
  278. m_failedSchedule = NULL;
  279. m_interuptSchedule = GetCurSchedule();
  280. // Find the first non-zero bit
  281. for (int i=0;i<MAX_CONDITIONS;i++)
  282. {
  283. if (testBits.IsBitSet(i))
  284. {
  285. m_interruptText = ConditionName( AI_RemapToGlobal( i ) );
  286. if (!m_interruptText)
  287. {
  288. m_interruptText = "(UNKNOWN CONDITION)";
  289. /*
  290. static const char *pError = "ERROR: Unknown condition!";
  291. DevMsg("%s (%s)\n", pError, GetDebugName());
  292. m_interruptText = pError;
  293. */
  294. }
  295. if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  296. {
  297. DevMsg( this, AIMF_IGNORE_SELECTED, " Break condition -> %s\n", m_interruptText );
  298. }
  299. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Break condition -> %s\n", GetDebugName(), entindex(), m_interruptText ) );
  300. break;
  301. }
  302. }
  303. if ( HasCondition( COND_NEW_ENEMY ) )
  304. {
  305. if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  306. {
  307. DevMsg( this, AIMF_IGNORE_SELECTED, " New enemy: %s\n", GetEnemy() ? GetEnemy()->GetDebugName() : "<NULL>" );
  308. }
  309. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): New enemy: %s\n", GetDebugName(), entindex(), GetEnemy() ? GetEnemy()->GetDebugName() : "<NULL>" ) );
  310. }
  311. }
  312. return false;
  313. }
  314. if ( HasCondition(COND_SCHEDULE_DONE) ||
  315. HasCondition(COND_TASK_FAILED) )
  316. {
  317. #ifdef DEBUG
  318. if ( HasCondition ( COND_TASK_FAILED ) && m_failSchedule == SCHED_NONE )
  319. {
  320. // fail! Send a visual indicator.
  321. DevWarning( 2, "Schedule: %s Failed\n", GetCurSchedule()->GetName() );
  322. Vector tmp;
  323. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &tmp );
  324. tmp.z += 16;
  325. g_pEffects->Sparks( tmp );
  326. }
  327. #endif // DEBUG
  328. // some condition has interrupted the schedule, or the schedule is done
  329. return false;
  330. }
  331. return true;
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose: Determines whether or not SelectIdealState() should be called before
  335. // a NPC selects a new schedule.
  336. //
  337. // NOTE: This logic was a source of pure, distilled trouble in Half-Life.
  338. // If you change this function, please supply good comments.
  339. //
  340. // Output : Returns true if yes, false if no
  341. //-----------------------------------------------------------------------------
  342. bool CAI_BaseNPC::ShouldSelectIdealState( void )
  343. {
  344. /*
  345. HERE's the old Half-Life code that used to control this.
  346. if ( m_IdealNPCState != NPC_STATE_DEAD &&
  347. (m_IdealNPCState != NPC_STATE_SCRIPT || m_IdealNPCState == m_NPCState) )
  348. {
  349. if ( (m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) ||
  350. (GetCurSchedule() && (GetCurSchedule()->iInterruptMask & bits_COND_SCHEDULE_DONE)) ||
  351. ((m_NPCState == NPC_STATE_COMBAT) && (GetEnemy() == NULL)) )
  352. {
  353. GetIdealState();
  354. }
  355. }
  356. */
  357. // Don't get ideal state if you are supposed to be dead.
  358. if ( m_IdealNPCState == NPC_STATE_DEAD )
  359. return false;
  360. // If I'm supposed to be in scripted state, but i'm not yet, do not allow
  361. // SelectIdealState() to be called, because it doesn't know how to determine
  362. // that a NPC should be in SCRIPT state and will stomp it with some other
  363. // state. (Most likely ALERT)
  364. if ( (m_IdealNPCState == NPC_STATE_SCRIPT) && (m_NPCState != NPC_STATE_SCRIPT) )
  365. return false;
  366. // If the NPC has any current conditions, and one of those conditions indicates
  367. // that the previous schedule completed successfully, then don't run SelectIdealState().
  368. // Paths between states only exist for interrupted schedules, or when a schedule
  369. // contains a task that suggests that the NPC change state.
  370. if ( !HasCondition(COND_SCHEDULE_DONE) )
  371. return true;
  372. // This seems like some sort of hack...
  373. // Currently no schedule that I can see in the AI uses this feature, but if a schedule
  374. // interrupt mask contains bits_COND_SCHEDULE_DONE, then force a call to SelectIdealState().
  375. // If we want to keep this feature, I suggest we create a new condition with a name that
  376. // indicates exactly what it does.
  377. if ( GetCurSchedule() && GetCurSchedule()->HasInterrupt(COND_SCHEDULE_DONE) )
  378. return true;
  379. // Don't call SelectIdealState if a NPC in combat state has a valid enemy handle. Otherwise,
  380. // we need to change state immediately because something unexpected happened to the enemy
  381. // entity (it was blown apart by someone else, for example), and we need the NPC to change
  382. // state. THE REST OF OUR CODE should be robust enough that this can go away!!
  383. if ( (m_NPCState == NPC_STATE_COMBAT) && (GetEnemy() == NULL) )
  384. return true;
  385. if ( (m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT) && (GetEnemy() != NULL) )
  386. return true;
  387. return false;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Returns a new schedule based on current condition bits.
  391. //-----------------------------------------------------------------------------
  392. CAI_Schedule *CAI_BaseNPC::GetNewSchedule( void )
  393. {
  394. int scheduleType;
  395. //
  396. // Schedule selection code here overrides all leaf schedule selection.
  397. //
  398. if (HasCondition(COND_NPC_FREEZE))
  399. {
  400. scheduleType = SCHED_NPC_FREEZE;
  401. }
  402. else
  403. {
  404. // I dunno how this trend got started, but we need to find the problem.
  405. // You may not be in combat state with no enemy!!! (sjb) 11/4/03
  406. if( m_NPCState == NPC_STATE_COMBAT && !GetEnemy() )
  407. {
  408. DevMsg("**ERROR: Combat State with no enemy! slamming to ALERT\n");
  409. SetState( NPC_STATE_ALERT );
  410. }
  411. AI_PROFILE_SCOPE_BEGIN( CAI_BaseNPC_SelectSchedule);
  412. if ( m_NPCState == NPC_STATE_SCRIPT || m_NPCState == NPC_STATE_DEAD || m_iInteractionState == NPCINT_MOVING_TO_MARK )
  413. {
  414. scheduleType = CAI_BaseNPC::SelectSchedule();
  415. }
  416. else
  417. {
  418. scheduleType = SelectSchedule();
  419. }
  420. m_IdealSchedule = GetGlobalScheduleId( scheduleType );
  421. AI_PROFILE_SCOPE_END();
  422. }
  423. return GetScheduleOfType( scheduleType );
  424. }
  425. //-----------------------------------------------------------------------------
  426. //-----------------------------------------------------------------------------
  427. CAI_Schedule *CAI_BaseNPC::GetFailSchedule( void )
  428. {
  429. int prevSchedule;
  430. int failedTask;
  431. if ( GetCurSchedule() )
  432. prevSchedule = GetLocalScheduleId( GetCurSchedule()->GetId() );
  433. else
  434. prevSchedule = SCHED_NONE;
  435. const Task_t *pTask = GetTask();
  436. if ( pTask )
  437. failedTask = pTask->iTask;
  438. else
  439. failedTask = TASK_INVALID;
  440. Assert( AI_IdIsLocal( prevSchedule ) );
  441. Assert( AI_IdIsLocal( failedTask ) );
  442. int scheduleType = SelectFailSchedule( prevSchedule, failedTask, m_ScheduleState.taskFailureCode );
  443. return GetScheduleOfType( scheduleType );
  444. }
  445. //=========================================================
  446. // MaintainSchedule - does all the per-think schedule maintenance.
  447. // ensures that the NPC leaves this function with a valid
  448. // schedule!
  449. //=========================================================
  450. static bool ShouldStopProcessingTasks( CAI_BaseNPC *pNPC, int taskTime, int timeLimit )
  451. {
  452. #ifdef DEBUG
  453. if( ai_simulate_task_overtime.GetBool() )
  454. return true;
  455. #endif
  456. // Always stop processing if we've queued up a navigation query on the last task
  457. if ( pNPC->IsNavigationDeferred() )
  458. return true;
  459. if ( AIStrongOpt() )
  460. {
  461. bool bInScript = ( pNPC->GetState() == NPC_STATE_SCRIPT || pNPC->IsCurSchedule( SCHED_SCENE_GENERIC, false ) );
  462. // We ran a costly task, don't do it again!
  463. if ( pNPC->HasMemory( bits_MEMORY_TASK_EXPENSIVE ) && bInScript == false )
  464. return true;
  465. }
  466. if ( taskTime > timeLimit )
  467. {
  468. if ( ShouldUseEfficiency() ||
  469. pNPC->IsMoving() ||
  470. ( pNPC->GetIdealActivity() != ACT_RUN && pNPC->GetIdealActivity() != ACT_WALK ) )
  471. {
  472. return true;
  473. }
  474. }
  475. return false;
  476. }
  477. //-------------------------------------
  478. void CAI_BaseNPC::MaintainSchedule ( void )
  479. {
  480. AI_PROFILE_SCOPE(CAI_BaseNPC_RunAI_MaintainSchedule);
  481. extern CFastTimer g_AIMaintainScheduleTimer;
  482. CTimeScope timeScope(&g_AIMaintainScheduleTimer);
  483. //---------------------------------
  484. CAI_Schedule *pNewSchedule;
  485. int i;
  486. bool runTask = true;
  487. #if defined( VPROF_ENABLED )
  488. #if defined(DISABLE_DEBUG_HISTORY)
  489. bool bDebugTaskNames = ( developer.GetBool() || ( VProfAI() && g_VProfCurrentProfile.IsEnabled() ) );
  490. #else
  491. bool bDebugTaskNames = true;
  492. #endif
  493. #else
  494. bool bDebugTaskNames = false;
  495. #endif
  496. memset( g_AITaskTimings, 0, sizeof(g_AITaskTimings) );
  497. g_nAITasksRun = 0;
  498. const int timeLimit = ( IsDebug() ) ? 16 : 8;
  499. int taskTime = Plat_MSTime();
  500. // Reset this at the beginning of the frame
  501. Forget( bits_MEMORY_TASK_EXPENSIVE );
  502. // UNDONE: Tune/fix this MAX_TASKS_RUN... This is just here so infinite loops are impossible
  503. bool bStopProcessing = false;
  504. for ( i = 0; i < MAX_TASKS_RUN && !bStopProcessing; i++ )
  505. {
  506. if ( GetCurSchedule() != NULL && TaskIsComplete() )
  507. {
  508. // Schedule is valid, so advance to the next task if the current is complete.
  509. NextScheduledTask();
  510. // If we finished the current schedule, clear our ignored conditions so they
  511. // aren't applied to the next schedule selection.
  512. if ( HasCondition( COND_SCHEDULE_DONE ) )
  513. {
  514. // Put our conditions back the way they were after GatherConditions,
  515. // but add in COND_SCHEDULE_DONE.
  516. m_Conditions = m_ConditionsPreIgnore;
  517. SetCondition( COND_SCHEDULE_DONE );
  518. m_InverseIgnoreConditions.SetAll();
  519. }
  520. // --------------------------------------------------------
  521. // If debug stepping advance when I complete a task
  522. // --------------------------------------------------------
  523. if (CAI_BaseNPC::m_nDebugBits & bits_debugStepAI)
  524. {
  525. m_nDebugCurIndex++;
  526. return;
  527. }
  528. }
  529. int curTiming = g_nAITasksRun;
  530. g_nAITasksRun++;
  531. // validate existing schedule
  532. if ( !IsScheduleValid() || m_NPCState != m_IdealNPCState )
  533. {
  534. // Notify the NPC that his schedule is changing
  535. m_ScheduleState.bScheduleWasInterrupted = true;
  536. OnScheduleChange();
  537. if ( !HasCondition(COND_NPC_FREEZE) && ( !m_bConditionsGathered || m_bSkippedChooseEnemy ) )
  538. {
  539. // occurs if a schedule is exhausted within one think
  540. GatherConditions();
  541. }
  542. if ( ShouldSelectIdealState() )
  543. {
  544. NPC_STATE eIdealState = SelectIdealState();
  545. SetIdealState( eIdealState );
  546. }
  547. if ( HasCondition( COND_TASK_FAILED ) && m_NPCState == m_IdealNPCState )
  548. {
  549. // Get a fail schedule if the previous schedule failed during execution and
  550. // the NPC is still in its ideal state. Otherwise, the NPC would immediately
  551. // select the same schedule again and fail again.
  552. if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  553. {
  554. DevMsg( this, AIMF_IGNORE_SELECTED, " (failed)\n" );
  555. }
  556. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): (failed)\n", GetDebugName(), entindex() ) );
  557. pNewSchedule = GetFailSchedule();
  558. m_IdealSchedule = pNewSchedule->GetId();
  559. DevWarning( 2, "(%s) Schedule (%s) Failed at %d!\n", STRING( GetEntityName() ), GetCurSchedule() ? GetCurSchedule()->GetName() : "GetCurSchedule() == NULL", GetScheduleCurTaskIndex() );
  560. SetSchedule( pNewSchedule );
  561. }
  562. else
  563. {
  564. // If the NPC is supposed to change state, it doesn't matter if the previous
  565. // schedule failed or completed. Changing state means selecting an entirely new schedule.
  566. SetState( m_IdealNPCState );
  567. g_AITaskTimings[curTiming].selectSchedule.Start();
  568. pNewSchedule = GetNewSchedule();
  569. g_AITaskTimings[curTiming].selectSchedule.End();
  570. SetSchedule( pNewSchedule );
  571. }
  572. }
  573. if (!GetCurSchedule())
  574. {
  575. g_AITaskTimings[curTiming].selectSchedule.Start();
  576. pNewSchedule = GetNewSchedule();
  577. g_AITaskTimings[curTiming].selectSchedule.End();
  578. if (pNewSchedule)
  579. {
  580. SetSchedule( pNewSchedule );
  581. }
  582. }
  583. if ( !GetCurSchedule() || GetCurSchedule()->NumTasks() == 0 )
  584. {
  585. DevMsg("ERROR: Missing or invalid schedule!\n");
  586. SetActivity ( ACT_IDLE );
  587. return;
  588. }
  589. AI_PROFILE_SCOPE_BEGIN_( GetCurSchedule() ? CAI_BaseNPC::GetSchedulingSymbols()->ScheduleIdToSymbol( GetCurSchedule()->GetId() ) : "NULL SCHEDULE" );
  590. if ( GetTaskStatus() == TASKSTATUS_NEW )
  591. {
  592. if ( GetScheduleCurTaskIndex() == 0 )
  593. {
  594. int globalId = GetCurSchedule()->GetId();
  595. int localId = GetLocalScheduleId( globalId ); // if localId == -1, then it came from a behavior
  596. OnStartSchedule( (localId != -1)? localId : globalId );
  597. }
  598. g_AITaskTimings[curTiming].startTimer.Start();
  599. const Task_t *pTask = GetTask();
  600. const char *pszTaskName = ( bDebugTaskNames ) ? TaskName( pTask->iTask ) : "ai_task";
  601. Assert( pTask != NULL );
  602. g_AITaskTimings[i].pszTask = pszTaskName;
  603. if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  604. {
  605. DevMsg(this, AIMF_IGNORE_SELECTED, " Task: %s\n", pszTaskName );
  606. }
  607. if ( m_pScheduleEvent != NULL )
  608. {
  609. CGlobalEvent *pEvent = GlobalEventLog.CreateTempEvent( "New Task", m_pScheduleEvent );
  610. GlobalEventLog.AddKeyValue( pEvent, false, "Task", pszTaskName );
  611. }
  612. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Task: %s\n", GetDebugName(), entindex(), pszTaskName ) );
  613. OnStartTask();
  614. m_ScheduleState.taskFailureCode = NO_TASK_FAILURE;
  615. m_ScheduleState.timeCurTaskStarted = gpGlobals->curtime;
  616. AI_PROFILE_SCOPE_BEGIN_( pszTaskName );
  617. AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_StartTask);
  618. StartTask( pTask );
  619. AI_PROFILE_SCOPE_END();
  620. AI_PROFILE_SCOPE_END();
  621. if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) )
  622. StartTaskOverlay();
  623. g_AITaskTimings[curTiming].startTimer.End();
  624. // DevMsg( "%.2f StartTask( %s )\n", gpGlobals->curtime, m_pTaskSR->GetStringText( pTask->iTask ) );
  625. }
  626. AI_PROFILE_SCOPE_END();
  627. // UNDONE: Twice?!!!
  628. MaintainActivity();
  629. AI_PROFILE_SCOPE_BEGIN_( GetCurSchedule() ? CAI_BaseNPC::GetSchedulingSymbols()->ScheduleIdToSymbol( GetCurSchedule()->GetId() ) : "NULL SCHEDULE" );
  630. if ( !TaskIsComplete() && GetTaskStatus() != TASKSTATUS_NEW )
  631. {
  632. if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) && runTask )
  633. {
  634. const Task_t *pTask = GetTask();
  635. const char *pszTaskName = ( bDebugTaskNames ) ? TaskName( pTask->iTask ) : "ai_task";
  636. Assert( pTask != NULL );
  637. g_AITaskTimings[i].pszTask = pszTaskName;
  638. // DevMsg( "%.2f RunTask( %s )\n", gpGlobals->curtime, m_pTaskSR->GetStringText( pTask->iTask ) );
  639. g_AITaskTimings[curTiming].runTimer.Start();
  640. AI_PROFILE_SCOPE_BEGIN_( pszTaskName );
  641. AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_RunTask);
  642. int j;
  643. for (j = 0; j < 8; j++)
  644. {
  645. RunTask( pTask );
  646. if ( GetTaskInterrupt() == 0 || TaskIsComplete() || HasCondition(COND_TASK_FAILED) )
  647. break;
  648. if ( ShouldUseEfficiency() && ShouldStopProcessingTasks( this, Plat_MSTime() - taskTime, timeLimit ) )
  649. {
  650. bStopProcessing = true;
  651. break;
  652. }
  653. }
  654. AssertMsg( j < 8, "Runaway task interrupt\n" );
  655. AI_PROFILE_SCOPE_END();
  656. AI_PROFILE_SCOPE_END();
  657. if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) )
  658. {
  659. if ( IsCurTaskContinuousMove() )
  660. Remember( bits_MEMORY_MOVED_FROM_SPAWN );
  661. RunTaskOverlay();
  662. }
  663. g_AITaskTimings[curTiming].runTimer.End();
  664. // don't do this again this frame
  665. // FIXME: RunTask() should eat some of the clock, depending on what it has done
  666. // runTask = false;
  667. if ( !TaskIsComplete() )
  668. {
  669. bStopProcessing = true;
  670. }
  671. }
  672. else
  673. {
  674. bStopProcessing = true;
  675. }
  676. }
  677. AI_PROFILE_SCOPE_END();
  678. // Decide if we should continue on this frame
  679. if ( !bStopProcessing && ShouldStopProcessingTasks( this, Plat_MSTime() - taskTime, timeLimit ) )
  680. bStopProcessing = true;
  681. }
  682. for ( i = 0; i < m_Behaviors.Count(); i++ )
  683. {
  684. m_Behaviors[i]->MaintainChannelSchedules();
  685. }
  686. // UNDONE: We have to do this so that we have an animation set to blend to if RunTask changes the animation
  687. // RunTask() will always change animations at the end of a script!
  688. // Don't do this twice
  689. MaintainActivity();
  690. // --------------------------------------------------------
  691. // If I'm stopping to debug step, don't animate unless
  692. // I'm in motion
  693. // --------------------------------------------------------
  694. if (CAI_BaseNPC::m_nDebugBits & bits_debugStepAI)
  695. {
  696. if (!GetNavigator()->IsGoalActive() &&
  697. m_nDebugCurIndex >= CAI_BaseNPC::m_nDebugPauseIndex)
  698. {
  699. m_flPlaybackRate = 0;
  700. }
  701. }
  702. }
  703. //=========================================================
  704. bool CAI_BaseNPC::FindCoverPos( CBaseEntity *pEntity, Vector *pResult )
  705. {
  706. AI_PROFILE_SCOPE(CAI_BaseNPC_FindCoverPos);
  707. if ( !GetTacticalServices()->FindLateralCover( pEntity->EyePosition(), 0, pResult ) )
  708. {
  709. if ( !GetTacticalServices()->FindCoverPos( pEntity->GetAbsOrigin(), pEntity->EyePosition(), 0, CoverRadius(), pResult ) )
  710. {
  711. return false;
  712. }
  713. }
  714. return true;
  715. }
  716. //=========================================================
  717. bool CAI_BaseNPC::FindCoverPosInRadius( CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult )
  718. {
  719. AI_PROFILE_SCOPE(CAI_BaseNPC_FindCoverPosInRadius);
  720. if ( pEntity == NULL )
  721. {
  722. // Find cover from self if no enemy available
  723. pEntity = this;
  724. }
  725. Vector coverPos = vec3_invalid;
  726. CAI_TacticalServices * pTacticalServices = GetTacticalServices();
  727. const Vector & enemyPos = pEntity->GetAbsOrigin();
  728. Vector enemyEyePos = pEntity->EyePosition();
  729. if( ( !GetSquad() || GetSquad()->GetFirstMember() == this ) &&
  730. IsCoverPosition( enemyEyePos, goalPos + GetViewOffset() ) &&
  731. IsValidCover( goalPos, NULL ) )
  732. {
  733. coverPos = goalPos;
  734. }
  735. else if ( !pTacticalServices->FindCoverPos( goalPos, enemyPos, enemyEyePos, 0, coverRadius * 0.5, &coverPos ) )
  736. {
  737. if ( !pTacticalServices->FindLateralCover( goalPos, enemyEyePos, 0, coverRadius * 0.5, 3, &coverPos ) )
  738. {
  739. if ( !pTacticalServices->FindCoverPos( goalPos, enemyPos, enemyEyePos, coverRadius * 0.5 - 0.1, coverRadius, &coverPos ) )
  740. {
  741. pTacticalServices->FindLateralCover( goalPos, enemyEyePos, 0, coverRadius, 5, &coverPos );
  742. }
  743. }
  744. }
  745. if ( coverPos == vec3_invalid )
  746. return false;
  747. *pResult = coverPos;
  748. return true;
  749. }
  750. //=========================================================
  751. bool CAI_BaseNPC::FindCoverPos( CSound *pSound, Vector *pResult )
  752. {
  753. if ( !GetTacticalServices()->FindCoverPos( pSound->GetSoundReactOrigin(),
  754. pSound->GetSoundReactOrigin(),
  755. MIN( pSound->Volume(), 120.0 ),
  756. CoverRadius(),
  757. pResult ) )
  758. {
  759. return GetTacticalServices()->FindLateralCover( pSound->GetSoundReactOrigin(), MIN( pSound->Volume(), 60.0 ), pResult );
  760. }
  761. return true;
  762. }
  763. //=========================================================
  764. // Start task - selects the correct activity and performs
  765. // any necessary calculations to start the next task on the
  766. // schedule.
  767. //=========================================================
  768. //-----------------------------------------------------------------------------
  769. // TASK_TURN_RIGHT / TASK_TURN_LEFT
  770. //-----------------------------------------------------------------------------
  771. void CAI_BaseNPC::StartTurn( float flDeltaYaw )
  772. {
  773. float flCurrentYaw;
  774. flCurrentYaw = UTIL_AngleMod( GetLocalAngles().y );
  775. GetMotor()->SetIdealYaw( UTIL_AngleMod( flCurrentYaw + flDeltaYaw ) );
  776. SetTurnActivity();
  777. }
  778. //-----------------------------------------------------------------------------
  779. // TASK_CLEAR_HINTNODE
  780. //-----------------------------------------------------------------------------
  781. void CAI_BaseNPC::ClearHintNode( float reuseDelay )
  782. {
  783. if ( m_pHintNode )
  784. {
  785. if ( m_pHintNode->IsLockedBy(this) )
  786. m_pHintNode->Unlock(reuseDelay);
  787. m_pHintNode = NULL;
  788. }
  789. }
  790. void CAI_BaseNPC::SetHintNode( CAI_Hint *pHintNode )
  791. {
  792. m_pHintNode = pHintNode;
  793. }
  794. //-----------------------------------------------------------------------------
  795. bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, float flMaxDistance )
  796. {
  797. CBaseEntity *pEntity = GetEnemy();
  798. // Find cover from self if no enemy available
  799. if ( pEntity == NULL )
  800. pEntity = this;
  801. Vector coverPos = vec3_invalid;
  802. ClearHintNode();
  803. if ( bNodesOnly )
  804. {
  805. if ( flMaxDistance == FLT_MAX )
  806. flMaxDistance = CoverRadius();
  807. if ( !GetTacticalServices()->FindCoverPos( pEntity->GetAbsOrigin(), pEntity->EyePosition(), flMinDistance, flMaxDistance, &coverPos ) )
  808. return false;
  809. }
  810. else
  811. {
  812. if ( !FindCoverPos( pEntity, &coverPos ) )
  813. return false;
  814. }
  815. AI_NavGoal_t goal( GOALTYPE_COVER, coverPos, ACT_RUN, AIN_HULL_TOLERANCE );
  816. if ( !GetNavigator()->SetGoal( goal ) )
  817. return false;
  818. // FIXME: add to goal
  819. if (GetHintNode())
  820. {
  821. GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) );
  822. GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() );
  823. }
  824. return true;
  825. }
  826. //-----------------------------------------------------------------------------
  827. // TASK_FIND_COVER_FROM_BEST_SOUND
  828. //-----------------------------------------------------------------------------
  829. bool CAI_BaseNPC::FindCoverFromBestSound( Vector *pCoverPos )
  830. {
  831. CSound *pBestSound;
  832. pBestSound = GetBestSound();
  833. if (pBestSound)
  834. {
  835. // UNDONE: Back away if cover fails? Grunts do this.
  836. return FindCoverPos( pBestSound, pCoverPos );
  837. }
  838. else
  839. {
  840. DevMsg( 2, "Attempting to find cover from best sound, but best sound not founc.\n" );
  841. }
  842. return false;
  843. }
  844. //-----------------------------------------------------------------------------
  845. // TASK_FACE_REASONABLE
  846. //-----------------------------------------------------------------------------
  847. float CAI_BaseNPC::CalcReasonableFacing( bool bIgnoreOriginalFacing )
  848. {
  849. float flReasonableYaw;
  850. if( !bIgnoreOriginalFacing && !HasMemory( bits_MEMORY_MOVED_FROM_SPAWN ) && !HasCondition( COND_SEE_ENEMY) )
  851. {
  852. flReasonableYaw = m_flOriginalYaw;
  853. }
  854. else
  855. {
  856. // If I'm facing a wall, change my original yaw and try to find a good direction to face.
  857. trace_t tr;
  858. Vector forward;
  859. QAngle angles( 0, 0, 0 );
  860. float idealYaw = GetMotor()->GetIdealYaw();
  861. flReasonableYaw = idealYaw;
  862. // Try just using the facing we have
  863. const float MIN_DIST = GetReasonableFacingDist();
  864. float longestTrace = 0;
  865. // Early out if we're overriding reasonable facing
  866. if ( !MIN_DIST )
  867. return flReasonableYaw;
  868. // Otherwise, scan out back and forth until something better is found
  869. const float SLICES = 8.0f;
  870. const float SIZE_SLICE = 360.0 / SLICES;
  871. const int SEARCH_MAX = (int)SLICES / 2;
  872. float zEye = GetAbsOrigin().z + m_vDefaultEyeOffset.z; // always use standing eye so as to not screw with crouch cover
  873. for( int i = 0 ; i <= SEARCH_MAX; i++ )
  874. {
  875. float offset = i * SIZE_SLICE;
  876. for ( int j = -1; j <= 1; j += 2)
  877. {
  878. angles.y = idealYaw + ( offset * j );
  879. AngleVectors( angles, &forward, NULL, NULL );
  880. float curTrace;
  881. if( ( curTrace = LineOfSightDist( forward, zEye ) ) > longestTrace && IsValidReasonableFacing(forward, curTrace) )
  882. {
  883. // Take this one.
  884. flReasonableYaw = angles.y;
  885. longestTrace = curTrace;
  886. }
  887. if ( longestTrace > MIN_DIST) // found one
  888. break;
  889. if ( i == 0 || i == SEARCH_MAX) // if trying forwards or backwards, skip the check of the other side...
  890. break;
  891. }
  892. if ( longestTrace > MIN_DIST ) // found one
  893. break;
  894. }
  895. }
  896. return flReasonableYaw;
  897. }
  898. //-----------------------------------------------------------------------------
  899. //-----------------------------------------------------------------------------
  900. float CAI_BaseNPC::GetReasonableFacingDist( void )
  901. {
  902. if ( GetTask() && GetTask()->iTask == TASK_FACE_ENEMY )
  903. {
  904. const float dist = 3.5*12;
  905. if ( GetEnemy() )
  906. {
  907. float distEnemy = ( GetEnemy()->GetAbsOrigin().AsVector2D() - GetAbsOrigin().AsVector2D() ).Length() - 1.0;
  908. return MIN( distEnemy, dist );
  909. }
  910. return dist;
  911. }
  912. return 5*12;
  913. }
  914. //-----------------------------------------------------------------------------
  915. // TASK_SCRIPT_RUN_TO_TARGET / TASK_SCRIPT_WALK_TO_TARGET / TASK_SCRIPT_CUSTOM_MOVE_TO_TARGET
  916. //-----------------------------------------------------------------------------
  917. void CAI_BaseNPC::StartScriptMoveToTargetTask( int task )
  918. {
  919. Activity newActivity;
  920. if ( m_hTargetEnt == NULL)
  921. {
  922. TaskFail(FAIL_NO_TARGET);
  923. }
  924. else if ( (m_hTargetEnt->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 )
  925. {
  926. TaskComplete();
  927. }
  928. else
  929. {
  930. //
  931. // Select the appropriate activity.
  932. //
  933. if ( task == TASK_SCRIPT_WALK_TO_TARGET )
  934. {
  935. newActivity = ACT_WALK;
  936. }
  937. else if ( task == TASK_SCRIPT_RUN_TO_TARGET )
  938. {
  939. newActivity = ACT_RUN;
  940. }
  941. else
  942. {
  943. newActivity = GetScriptCustomMoveActivity();
  944. }
  945. if ( ( newActivity != ACT_SCRIPT_CUSTOM_MOVE ) && TranslateActivity( newActivity ) == ACT_INVALID )
  946. {
  947. // This NPC can't do this!
  948. Assert( 0 );
  949. }
  950. else
  951. {
  952. if (m_hTargetEnt == NULL)
  953. {
  954. TaskFail(FAIL_NO_TARGET);
  955. }
  956. else
  957. {
  958. AI_NavGoal_t goal( GOALTYPE_TARGETENT, newActivity );
  959. if ( GetState() == NPC_STATE_SCRIPT &&
  960. ( m_ScriptArrivalActivity != AIN_DEF_ACTIVITY ||
  961. m_strScriptArrivalSequence != NULL_STRING ) )
  962. {
  963. if ( m_ScriptArrivalActivity != AIN_DEF_ACTIVITY )
  964. {
  965. goal.arrivalActivity = m_ScriptArrivalActivity;
  966. }
  967. else
  968. {
  969. goal.arrivalSequence = LookupSequence( m_strScriptArrivalSequence.ToCStr() );
  970. }
  971. }
  972. if (!GetNavigator()->SetGoal( goal, AIN_DISCARD_IF_FAIL ))
  973. {
  974. if ( GetNavigator()->GetNavFailCounter() == 0 )
  975. {
  976. // no path was built, but OnNavFailed() did something so that next time it may work
  977. DevWarning("%s %s failed Urgent Movement, retrying\n", GetDebugName(), TaskName( task ) );
  978. return;
  979. }
  980. // FIXME: scripted sequences don't actually know how to handle failure, but we're failing. This is serious
  981. DevWarning("%s %s failed Urgent Movement, abandoning schedule\n", GetDebugName(), TaskName( task ) );
  982. TaskFail(FAIL_NO_ROUTE);
  983. }
  984. else
  985. {
  986. GetNavigator()->SetArrivalDirection( m_hTargetEnt->GetAbsAngles() );
  987. }
  988. }
  989. }
  990. }
  991. m_ScriptArrivalActivity = AIN_DEF_ACTIVITY;
  992. m_strScriptArrivalSequence = NULL_STRING;
  993. TaskComplete();
  994. }
  995. //-----------------------------------------------------------------------------
  996. // Start task!
  997. //-----------------------------------------------------------------------------
  998. void CAI_BaseNPC::StartTask( const Task_t *pTask )
  999. {
  1000. int task = pTask->iTask;
  1001. switch ( pTask->iTask )
  1002. {
  1003. case TASK_RESET_ACTIVITY:
  1004. m_Activity = ACT_RESET;
  1005. TaskComplete();
  1006. break;
  1007. case TASK_CREATE_PENDING_WEAPON:
  1008. Assert( m_iszPendingWeapon != NULL_STRING );
  1009. GiveWeapon( m_iszPendingWeapon );
  1010. m_iszPendingWeapon = NULL_STRING;
  1011. TaskComplete();
  1012. break;
  1013. case TASK_RANDOMIZE_FRAMERATE:
  1014. {
  1015. float newRate = GetPlaybackRate();
  1016. float percent = pTask->flTaskData / 100.0f;
  1017. newRate += ( newRate * random->RandomFloat(-percent, percent) );
  1018. SetPlaybackRate(newRate);
  1019. TaskComplete();
  1020. }
  1021. break;
  1022. case TASK_DEFER_DODGE:
  1023. m_flNextDodgeTime = gpGlobals->curtime + pTask->flTaskData;
  1024. TaskComplete();
  1025. break;
  1026. // Default case just completes. Override in sub-classes
  1027. // to play sound / animation / or pause
  1028. case TASK_ANNOUNCE_ATTACK:
  1029. TaskComplete();
  1030. break;
  1031. case TASK_TURN_RIGHT:
  1032. StartTurn( -pTask->flTaskData );
  1033. break;
  1034. case TASK_TURN_LEFT:
  1035. StartTurn( pTask->flTaskData );
  1036. break;
  1037. case TASK_REMEMBER:
  1038. Remember ( (int)pTask->flTaskData );
  1039. TaskComplete();
  1040. break;
  1041. case TASK_FORGET:
  1042. Forget ( (int)pTask->flTaskData );
  1043. TaskComplete();
  1044. break;
  1045. case TASK_FIND_HINTNODE:
  1046. case TASK_FIND_LOCK_HINTNODE:
  1047. {
  1048. if (!GetHintNode())
  1049. {
  1050. SetHintNode( CAI_HintManager::FindHint( this, HINT_NONE, pTask->flTaskData, 2000 ) );
  1051. }
  1052. if (GetHintNode())
  1053. {
  1054. TaskComplete();
  1055. }
  1056. else
  1057. {
  1058. TaskFail(FAIL_NO_HINT_NODE);
  1059. }
  1060. if ( task == TASK_FIND_HINTNODE )
  1061. break;
  1062. }
  1063. // Fall through on TASK_FIND_LOCK_HINTNODE...
  1064. case TASK_LOCK_HINTNODE:
  1065. {
  1066. if (!GetHintNode())
  1067. {
  1068. TaskFail(FAIL_NO_HINT_NODE);
  1069. }
  1070. else if( GetHintNode()->Lock(this) )
  1071. {
  1072. TaskComplete();
  1073. }
  1074. else
  1075. {
  1076. TaskFail(FAIL_ALREADY_LOCKED);
  1077. SetHintNode( NULL );
  1078. }
  1079. break;
  1080. }
  1081. case TASK_STORE_LASTPOSITION:
  1082. m_vecLastPosition = GetLocalOrigin();
  1083. TaskComplete();
  1084. break;
  1085. case TASK_CLEAR_LASTPOSITION:
  1086. m_vecLastPosition = vec3_origin;
  1087. TaskComplete();
  1088. break;
  1089. case TASK_STORE_POSITION_IN_SAVEPOSITION:
  1090. m_vSavePosition = GetLocalOrigin();
  1091. TaskComplete();
  1092. break;
  1093. case TASK_STORE_BESTSOUND_IN_SAVEPOSITION:
  1094. {
  1095. CSound *pBestSound = GetBestSound();
  1096. if ( pBestSound )
  1097. {
  1098. m_vSavePosition = pBestSound->GetSoundOrigin();
  1099. CBaseEntity *pSoundEnt = pBestSound->m_hOwner;
  1100. if ( pSoundEnt )
  1101. {
  1102. Vector vel;
  1103. pSoundEnt->GetVelocity( &vel, NULL );
  1104. // HACKHACK: run away from cars in the right direction
  1105. m_vSavePosition += vel * 2; // add in 2 seconds of velocity
  1106. }
  1107. TaskComplete();
  1108. }
  1109. else
  1110. {
  1111. TaskFail("No Sound!");
  1112. return;
  1113. }
  1114. }
  1115. break;
  1116. case TASK_STORE_BESTSOUND_REACTORIGIN_IN_SAVEPOSITION:
  1117. {
  1118. CSound *pBestSound = GetBestSound();
  1119. if ( pBestSound )
  1120. {
  1121. m_vSavePosition = pBestSound->GetSoundReactOrigin();
  1122. TaskComplete();
  1123. }
  1124. else
  1125. {
  1126. TaskFail("No Sound!");
  1127. return;
  1128. }
  1129. }
  1130. break;
  1131. case TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION:
  1132. if ( GetEnemy() != NULL )
  1133. {
  1134. m_vSavePosition = GetEnemy()->GetAbsOrigin();
  1135. TaskComplete();
  1136. }
  1137. else
  1138. {
  1139. TaskFail(FAIL_NO_ENEMY);
  1140. }
  1141. break;
  1142. case TASK_CLEAR_HINTNODE:
  1143. ClearHintNode(pTask->flTaskData);
  1144. TaskComplete();
  1145. break;
  1146. case TASK_STOP_MOVING:
  1147. if ( ( GetNavigator()->IsGoalSet() && GetNavigator()->IsGoalActive() ) || GetNavType() == NAV_JUMP )
  1148. {
  1149. DbgNavMsg( this, "Start TASK_STOP_MOVING\n" );
  1150. if ( pTask->flTaskData == 1 )
  1151. {
  1152. DbgNavMsg( this, "Initiating stopping path\n" );
  1153. GetNavigator()->StopMoving( false );
  1154. }
  1155. else
  1156. {
  1157. GetNavigator()->ClearGoal();
  1158. }
  1159. // E3 Hack
  1160. if ( HasPoseMoveYaw() )
  1161. {
  1162. SetPoseParameter( m_poseMove_Yaw, 0 );
  1163. }
  1164. }
  1165. else
  1166. {
  1167. if ( pTask->flTaskData == 1 && GetNavigator()->SetGoalFromStoppingPath() )
  1168. {
  1169. DbgNavMsg( this, "Start TASK_STOP_MOVING\n" );
  1170. DbgNavMsg( this, "Initiating stopping path\n" );
  1171. }
  1172. else
  1173. {
  1174. GetNavigator()->ClearGoal();
  1175. SetIdealActivity( GetStoppedActivity() );
  1176. TaskComplete();
  1177. }
  1178. }
  1179. break;
  1180. case TASK_PLAY_PRIVATE_SEQUENCE:
  1181. case TASK_PLAY_PRIVATE_SEQUENCE_FACE_ENEMY:
  1182. case TASK_PLAY_SEQUENCE_FACE_ENEMY:
  1183. case TASK_PLAY_SEQUENCE_FACE_TARGET:
  1184. case TASK_PLAY_SEQUENCE:
  1185. SetIdealActivity( (Activity)(int)pTask->flTaskData );
  1186. break;
  1187. case TASK_ADD_GESTURE_WAIT:
  1188. {
  1189. int iLayer = AddGesture( (Activity)(int)pTask->flTaskData );
  1190. if (iLayer > 0)
  1191. {
  1192. float flDuration = GetLayerDuration( iLayer );
  1193. SetWait( flDuration );
  1194. }
  1195. else
  1196. {
  1197. TaskFail( "Unable to allocate gesture" );
  1198. }
  1199. break;
  1200. }
  1201. case TASK_ADD_GESTURE:
  1202. {
  1203. AddGesture( (Activity)(int)pTask->flTaskData );
  1204. TaskComplete();
  1205. break;
  1206. }
  1207. case TASK_PLAY_HINT_ACTIVITY:
  1208. if ( GetHintNode()->HintActivityName() != NULL_STRING )
  1209. {
  1210. Activity hintActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(GetHintNode()->HintActivityName()) );
  1211. if ( hintActivity != ACT_INVALID )
  1212. {
  1213. SetIdealActivity( GetHintActivity(GetHintNode()->HintType(), hintActivity) );
  1214. }
  1215. else
  1216. {
  1217. int iSequence = LookupSequence(STRING(GetHintNode()->HintActivityName()));
  1218. if ( iSequence > ACT_INVALID )
  1219. {
  1220. SetSequenceById( iSequence ); // ???
  1221. SetIdealActivity( ACT_DO_NOT_DISTURB );
  1222. }
  1223. else
  1224. SetIdealActivity( ACT_IDLE );
  1225. }
  1226. }
  1227. else
  1228. {
  1229. SetIdealActivity( ACT_IDLE );
  1230. }
  1231. break;
  1232. case TASK_SET_SCHEDULE:
  1233. if ( !SetSchedule( pTask->flTaskData ) )
  1234. TaskFail(FAIL_SCHEDULE_NOT_FOUND);
  1235. break;
  1236. case TASK_FIND_BACKAWAY_FROM_SAVEPOSITION:
  1237. {
  1238. if ( GetEnemy() != NULL )
  1239. {
  1240. Vector backPos;
  1241. if ( !GetTacticalServices()->FindBackAwayPos( m_vSavePosition, &backPos ) )
  1242. {
  1243. // no place to backaway
  1244. TaskFail(FAIL_NO_BACKAWAY_NODE);
  1245. }
  1246. else
  1247. {
  1248. if (GetNavigator()->SetGoal( AI_NavGoal_t( backPos, ACT_RUN ) ) )
  1249. {
  1250. TaskComplete();
  1251. }
  1252. else
  1253. {
  1254. // no place to backaway
  1255. TaskFail(FAIL_NO_ROUTE);
  1256. }
  1257. }
  1258. }
  1259. else
  1260. {
  1261. TaskFail(FAIL_NO_ENEMY);
  1262. }
  1263. }
  1264. break;
  1265. case TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY:
  1266. case TASK_FIND_FAR_NODE_COVER_FROM_ENEMY:
  1267. case TASK_FIND_NODE_COVER_FROM_ENEMY:
  1268. case TASK_FIND_COVER_FROM_ENEMY:
  1269. {
  1270. bool bNodeCover = ( task != TASK_FIND_COVER_FROM_ENEMY );
  1271. float flMinDistance = ( task == TASK_FIND_FAR_NODE_COVER_FROM_ENEMY ) ? pTask->flTaskData : 0.0;
  1272. float flMaxDistance = ( task == TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY ) ? pTask->flTaskData : FLT_MAX;
  1273. if ( FindCoverFromEnemy( bNodeCover, flMinDistance, flMaxDistance ) )
  1274. {
  1275. if ( task == TASK_FIND_COVER_FROM_ENEMY )
  1276. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1277. TaskComplete();
  1278. }
  1279. else
  1280. TaskFail(FAIL_NO_COVER);
  1281. }
  1282. break;
  1283. case TASK_FIND_COVER_FROM_ORIGIN:
  1284. {
  1285. Vector coverPos;
  1286. if ( GetTacticalServices()->FindCoverPos( GetLocalOrigin(), EyePosition(), 0, CoverRadius(), &coverPos ) )
  1287. {
  1288. AI_NavGoal_t goal(coverPos, ACT_RUN, AIN_HULL_TOLERANCE);
  1289. GetNavigator()->SetGoal( goal );
  1290. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1291. }
  1292. else
  1293. {
  1294. // no coverwhatsoever.
  1295. TaskFail(FAIL_NO_COVER);
  1296. }
  1297. }
  1298. break;
  1299. case TASK_FIND_COVER_FROM_BEST_SOUND:
  1300. {
  1301. }
  1302. break;
  1303. case TASK_FACE_HINTNODE:
  1304. // If the yaw is locked, this function will not act correctly
  1305. Assert( GetMotor()->IsYawLocked() == false );
  1306. GetMotor()->SetIdealYaw( GetHintNode()->Yaw() );
  1307. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  1308. if ( FacingIdeal() )
  1309. TaskComplete();
  1310. else
  1311. SetTurnActivity();
  1312. break;
  1313. case TASK_FACE_LASTPOSITION:
  1314. GetMotor()->SetIdealYawToTarget( m_vecLastPosition );
  1315. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  1316. SetTurnActivity();
  1317. break;
  1318. case TASK_FACE_SAVEPOSITION:
  1319. GetMotor()->SetIdealYawToTarget( m_vSavePosition );
  1320. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  1321. SetTurnActivity();
  1322. break;
  1323. case TASK_FACE_AWAY_FROM_SAVEPOSITION:
  1324. GetMotor()->SetIdealYawToTarget( m_vSavePosition, 0, 180.0 );
  1325. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  1326. SetTurnActivity();
  1327. break;
  1328. case TASK_SET_IDEAL_YAW_TO_CURRENT:
  1329. GetMotor()->SetIdealYaw( UTIL_AngleMod( GetLocalAngles().y ) );
  1330. TaskComplete();
  1331. break;
  1332. case TASK_FACE_TARGET:
  1333. if ( m_hTargetEnt != NULL )
  1334. {
  1335. GetMotor()->SetIdealYawToTarget( m_hTargetEnt->GetAbsOrigin() );
  1336. SetTurnActivity();
  1337. }
  1338. else
  1339. {
  1340. TaskFail(FAIL_NO_TARGET);
  1341. }
  1342. break;
  1343. case TASK_FACE_PLAYER:
  1344. // track head to the client for a while.
  1345. SetWait( pTask->flTaskData );
  1346. break;
  1347. case TASK_FACE_ENEMY:
  1348. {
  1349. Vector vecEnemyLKP = GetEnemyLKP();
  1350. if (!FInAimCone( vecEnemyLKP ))
  1351. {
  1352. GetMotor()->SetIdealYawToTarget( vecEnemyLKP );
  1353. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  1354. SetTurnActivity();
  1355. }
  1356. else
  1357. {
  1358. float flReasonableFacing = CalcReasonableFacing( true );
  1359. if ( fabsf( flReasonableFacing - GetMotor()->GetIdealYaw() ) < 1 )
  1360. TaskComplete();
  1361. else
  1362. {
  1363. GetMotor()->SetIdealYaw( flReasonableFacing );
  1364. SetTurnActivity();
  1365. }
  1366. }
  1367. break;
  1368. }
  1369. case TASK_FACE_IDEAL:
  1370. SetTurnActivity();
  1371. break;
  1372. case TASK_FACE_REASONABLE:
  1373. GetMotor()->SetIdealYaw( CalcReasonableFacing() );
  1374. SetTurnActivity();
  1375. break;
  1376. case TASK_FACE_PATH:
  1377. {
  1378. if (!GetNavigator()->IsGoalActive())
  1379. {
  1380. DevWarning( 2, "No route to face!\n");
  1381. TaskFail(FAIL_NO_ROUTE);
  1382. }
  1383. else
  1384. {
  1385. const float NPC_TRIVIAL_TURN = 15; // (Degrees). Turns this small or smaller, don't bother with a transition.
  1386. GetMotor()->SetIdealYawToTarget( GetNavigator()->GetCurWaypointPos());
  1387. if( fabs( GetMotor()->DeltaIdealYaw() ) <= NPC_TRIVIAL_TURN )
  1388. {
  1389. // This character is already facing the path well enough that
  1390. // moving will look fairly natural. Don't bother with a transitional
  1391. // turn animation.
  1392. TaskComplete();
  1393. break;
  1394. }
  1395. SetTurnActivity();
  1396. }
  1397. }
  1398. break;
  1399. // don't do anything.
  1400. case TASK_WAIT_PVS:
  1401. case TASK_WAIT_INDEFINITE:
  1402. break;
  1403. // set a future time that tells us when the wait is over.
  1404. case TASK_WAIT:
  1405. case TASK_WAIT_FACE_ENEMY:
  1406. SetWait( pTask->flTaskData );
  1407. break;
  1408. // set a future time that tells us when the wait is over.
  1409. case TASK_WAIT_RANDOM:
  1410. case TASK_WAIT_FACE_ENEMY_RANDOM:
  1411. SetWait( 0, pTask->flTaskData );
  1412. break;
  1413. case TASK_MOVE_TO_TARGET_RANGE:
  1414. case TASK_MOVE_TO_GOAL_RANGE:
  1415. {
  1416. // Identical tasks, except that target_range uses m_hTargetEnt,
  1417. // and Goal range uses the nav goal
  1418. CBaseEntity *pTarget = NULL;
  1419. if ( task == TASK_MOVE_TO_GOAL_RANGE )
  1420. {
  1421. pTarget = GetNavigator()->GetGoalTarget();
  1422. }
  1423. if ( !pTarget )
  1424. {
  1425. pTarget = m_hTargetEnt.Get();
  1426. }
  1427. if ( pTarget == NULL)
  1428. {
  1429. TaskFail(FAIL_NO_TARGET);
  1430. }
  1431. else if ( (pTarget->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 )
  1432. {
  1433. TaskComplete();
  1434. }
  1435. if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  1436. {
  1437. TaskComplete();
  1438. GetNavigator()->ClearGoal(); // Clear residual state
  1439. }
  1440. else
  1441. {
  1442. // set that we're probably going to stop before the goal
  1443. GetNavigator()->SetArrivalDistance( pTask->flTaskData );
  1444. }
  1445. break;
  1446. }
  1447. case TASK_WAIT_UNTIL_NO_DANGER_SOUND:
  1448. if( !HasCondition( COND_HEAR_DANGER ) )
  1449. {
  1450. TaskComplete();
  1451. }
  1452. break;
  1453. case TASK_TARGET_PLAYER:
  1454. {
  1455. CBaseEntity *pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  1456. if ( pPlayer )
  1457. {
  1458. SetTarget( pPlayer );
  1459. TaskComplete();
  1460. }
  1461. else
  1462. TaskFail( FAIL_NO_PLAYER );
  1463. break;
  1464. }
  1465. case TASK_SCRIPT_RUN_TO_TARGET:
  1466. case TASK_SCRIPT_WALK_TO_TARGET:
  1467. case TASK_SCRIPT_CUSTOM_MOVE_TO_TARGET:
  1468. StartScriptMoveToTargetTask( pTask->iTask );
  1469. break;
  1470. case TASK_CLEAR_MOVE_WAIT:
  1471. m_flMoveWaitFinished = gpGlobals->curtime;
  1472. TaskComplete();
  1473. break;
  1474. case TASK_MELEE_ATTACK1:
  1475. SetLastAttackTime( gpGlobals->curtime );
  1476. ResetIdealActivity( ACT_MELEE_ATTACK1 );
  1477. break;
  1478. case TASK_MELEE_ATTACK2:
  1479. SetLastAttackTime( gpGlobals->curtime );
  1480. ResetIdealActivity( ACT_MELEE_ATTACK2 );
  1481. break;
  1482. case TASK_RANGE_ATTACK1:
  1483. SetLastAttackTime( gpGlobals->curtime );
  1484. ResetIdealActivity( ACT_RANGE_ATTACK1 );
  1485. break;
  1486. case TASK_RANGE_ATTACK2:
  1487. SetLastAttackTime( gpGlobals->curtime );
  1488. ResetIdealActivity( ACT_RANGE_ATTACK2 );
  1489. break;
  1490. case TASK_RELOAD:
  1491. ResetIdealActivity( ACT_RELOAD );
  1492. break;
  1493. case TASK_SPECIAL_ATTACK1:
  1494. ResetIdealActivity( ACT_SPECIAL_ATTACK1 );
  1495. break;
  1496. case TASK_SPECIAL_ATTACK2:
  1497. ResetIdealActivity( ACT_SPECIAL_ATTACK2 );
  1498. break;
  1499. case TASK_SET_ACTIVITY:
  1500. {
  1501. Activity goalActivity = (Activity)((int)pTask->flTaskData);
  1502. if (goalActivity != ACT_RESET)
  1503. {
  1504. SetIdealActivity( goalActivity );
  1505. }
  1506. else
  1507. {
  1508. m_Activity = ACT_RESET;
  1509. }
  1510. break;
  1511. }
  1512. case TASK_GET_CHASE_PATH_TO_ENEMY:
  1513. {
  1514. CBaseEntity *pEnemy = GetEnemy();
  1515. if ( !pEnemy )
  1516. {
  1517. TaskFail(FAIL_NO_ROUTE);
  1518. return;
  1519. }
  1520. if ( ( pEnemy->GetAbsOrigin() - GetEnemyLKP() ).LengthSqr() < Square(pTask->flTaskData) )
  1521. {
  1522. ChainStartTask( TASK_GET_PATH_TO_ENEMY );
  1523. }
  1524. else
  1525. {
  1526. ChainStartTask( TASK_GET_PATH_TO_ENEMY_LKP );
  1527. }
  1528. if ( !TaskIsComplete() && !HasCondition(COND_TASK_FAILED) )
  1529. TaskFail(FAIL_NO_ROUTE);
  1530. break;
  1531. }
  1532. case TASK_GET_PATH_TO_ENEMY_LKP:
  1533. {
  1534. CBaseEntity *pEnemy = GetEnemy();
  1535. if (!pEnemy || IsUnreachable(pEnemy))
  1536. {
  1537. TaskFail(FAIL_NO_ROUTE);
  1538. return;
  1539. }
  1540. AI_NavGoal_t goal( GetEnemyLKP() );
  1541. TranslateNavGoal( pEnemy, goal.dest );
  1542. if ( GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET ) )
  1543. {
  1544. TaskComplete();
  1545. }
  1546. else
  1547. {
  1548. // no way to get there =(
  1549. DevWarning( 2, "GetPathToEnemyLKP failed!!\n" );
  1550. RememberUnreachable(GetEnemy());
  1551. TaskFail(FAIL_NO_ROUTE);
  1552. }
  1553. break;
  1554. }
  1555. case TASK_GET_PATH_TO_INTERACTION_PARTNER:
  1556. {
  1557. if ( !m_hForcedInteractionPartner || IsUnreachable(m_hForcedInteractionPartner) )
  1558. {
  1559. TaskFail(FAIL_NO_ROUTE);
  1560. return;
  1561. }
  1562. // Calculate the position we need to be at to start the interaction.
  1563. CalculateForcedInteractionPosition();
  1564. AI_NavGoal_t goal( m_vecForcedWorldPosition );
  1565. TranslateNavGoal( m_hForcedInteractionPartner, goal.dest );
  1566. if ( GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET ) )
  1567. {
  1568. TaskComplete();
  1569. }
  1570. else
  1571. {
  1572. DevWarning( 2, "GetPathToInteractionPartner failed!!\n" );
  1573. RememberUnreachable(m_hForcedInteractionPartner);
  1574. TaskFail(FAIL_NO_ROUTE);
  1575. }
  1576. break;
  1577. }
  1578. case TASK_GET_PATH_TO_RANGE_ENEMY_LKP_LOS:
  1579. {
  1580. if ( GetEnemy() )
  1581. {
  1582. // Find out which range to use (either innately or a held weapon)
  1583. float flRange = -1.0f;
  1584. if ( CapabilitiesGet() & (bits_CAP_INNATE_RANGE_ATTACK1|bits_CAP_INNATE_RANGE_ATTACK2) )
  1585. {
  1586. flRange = InnateRange1MaxRange();
  1587. }
  1588. else if ( GetActiveWeapon() )
  1589. {
  1590. flRange = MAX( GetActiveWeapon()->m_fMaxRange1, GetActiveWeapon()->m_fMaxRange2 );
  1591. }
  1592. else
  1593. {
  1594. // You can't call this task without either innate range attacks or a weapon!
  1595. Assert( 0 );
  1596. TaskFail( FAIL_NO_ROUTE );
  1597. }
  1598. // Clamp to the specified range, if supplied
  1599. if ( pTask->flTaskData != 0 && pTask->flTaskData < flRange )
  1600. flRange = pTask->flTaskData;
  1601. // For now, just try running straight at enemy
  1602. float dist = EnemyDistance( GetEnemy() );
  1603. if ( dist <= flRange || GetNavigator()->SetVectorGoalFromTarget( GetEnemy()->GetAbsOrigin(), dist - flRange ) )
  1604. {
  1605. TaskComplete();
  1606. break;
  1607. }
  1608. }
  1609. TaskFail( FAIL_NO_ROUTE );
  1610. break;
  1611. }
  1612. case TASK_GET_PATH_TO_ENEMY_LOS:
  1613. case TASK_GET_FLANK_RADIUS_PATH_TO_ENEMY_LOS:
  1614. case TASK_GET_FLANK_ARC_PATH_TO_ENEMY_LOS:
  1615. case TASK_GET_PATH_TO_ENEMY_LKP_LOS:
  1616. {
  1617. if ( GetEnemy() == NULL )
  1618. {
  1619. TaskFail(FAIL_NO_ENEMY);
  1620. return;
  1621. }
  1622. AI_PROFILE_SCOPE(CAI_BaseNPC_FindLosToEnemy);
  1623. float flMaxRange = 2000;
  1624. float flMinRange = 0;
  1625. if ( GetActiveWeapon() )
  1626. {
  1627. flMaxRange = MAX( GetActiveWeapon()->m_fMaxRange1, GetActiveWeapon()->m_fMaxRange2 );
  1628. flMinRange = MIN( GetActiveWeapon()->m_fMinRange1, GetActiveWeapon()->m_fMinRange2 );
  1629. }
  1630. else if ( CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1 )
  1631. {
  1632. flMaxRange = InnateRange1MaxRange();
  1633. flMinRange = InnateRange1MinRange();
  1634. }
  1635. //Check against NPC's MAX range
  1636. if ( flMaxRange > m_flDistTooFar )
  1637. {
  1638. flMaxRange = m_flDistTooFar;
  1639. }
  1640. Vector vecEnemy = ( task != TASK_GET_PATH_TO_ENEMY_LKP ) ? GetEnemy()->GetAbsOrigin() : GetEnemyLKP();
  1641. Vector vecEnemyEye = vecEnemy + GetEnemy()->GetViewOffset();
  1642. Vector posLos;
  1643. bool found = false;
  1644. if ( ( task != TASK_GET_FLANK_RADIUS_PATH_TO_ENEMY_LOS ) && ( task != TASK_GET_FLANK_ARC_PATH_TO_ENEMY_LOS ) )
  1645. {
  1646. if ( GetTacticalServices()->FindLateralLos( vecEnemyEye, &posLos ) )
  1647. {
  1648. float dist = ( posLos - vecEnemyEye ).Length();
  1649. if ( dist < flMaxRange && dist > flMinRange )
  1650. found = true;
  1651. }
  1652. }
  1653. if ( !found )
  1654. {
  1655. FlankType_t eFlankType = FLANKTYPE_NONE;
  1656. Vector vecFlankRefPos = vec3_origin;
  1657. float flFlankParam = 0;
  1658. if ( task == TASK_GET_FLANK_RADIUS_PATH_TO_ENEMY_LOS )
  1659. {
  1660. eFlankType = FLANKTYPE_RADIUS;
  1661. vecFlankRefPos = m_vSavePosition;
  1662. flFlankParam = pTask->flTaskData;
  1663. }
  1664. else if ( task == TASK_GET_FLANK_ARC_PATH_TO_ENEMY_LOS )
  1665. {
  1666. eFlankType = FLANKTYPE_ARC;
  1667. vecFlankRefPos = m_vSavePosition;
  1668. flFlankParam = pTask->flTaskData;
  1669. }
  1670. if ( GetTacticalServices()->FindLos( vecEnemy, vecEnemyEye, flMinRange, flMaxRange, 1.0, eFlankType, vecFlankRefPos, flFlankParam, &posLos ) )
  1671. {
  1672. found = true;
  1673. }
  1674. }
  1675. if ( !found )
  1676. {
  1677. TaskFail( FAIL_NO_SHOOT );
  1678. }
  1679. else
  1680. {
  1681. // else drop into run task to offer an interrupt
  1682. m_vInterruptSavePosition = posLos;
  1683. }
  1684. }
  1685. break;
  1686. //==================================================
  1687. // TASK_SET_GOAL
  1688. //==================================================
  1689. case TASK_SET_GOAL:
  1690. switch ( (int) pTask->flTaskData )
  1691. {
  1692. case GOAL_ENEMY: //Enemy
  1693. if ( GetEnemy() == NULL )
  1694. {
  1695. TaskFail( FAIL_NO_ENEMY );
  1696. return;
  1697. }
  1698. //Setup our stored info
  1699. m_vecStoredPathGoal = GetEnemy()->GetAbsOrigin();
  1700. m_nStoredPathType = GOALTYPE_ENEMY;
  1701. m_fStoredPathFlags = 0;
  1702. m_hStoredPathTarget = GetEnemy();
  1703. GetNavigator()->SetMovementActivity(ACT_RUN);
  1704. break;
  1705. case GOAL_ENEMY_LKP: //Enemy's last known position
  1706. if ( GetEnemy() == NULL )
  1707. {
  1708. TaskFail( FAIL_NO_ENEMY );
  1709. return;
  1710. }
  1711. //Setup our stored info
  1712. m_vecStoredPathGoal = GetEnemyLKP();
  1713. m_nStoredPathType = GOALTYPE_LOCATION;
  1714. m_fStoredPathFlags = 0;
  1715. m_hStoredPathTarget = NULL;
  1716. GetNavigator()->SetMovementActivity(ACT_RUN);
  1717. break;
  1718. case GOAL_TARGET: //Target entity
  1719. if ( m_hTargetEnt == NULL )
  1720. {
  1721. TaskFail( FAIL_NO_TARGET );
  1722. return;
  1723. }
  1724. //Setup our stored info
  1725. m_vecStoredPathGoal = m_hTargetEnt->GetAbsOrigin();
  1726. m_nStoredPathType = GOALTYPE_TARGETENT;
  1727. m_fStoredPathFlags = 0;
  1728. m_hStoredPathTarget = m_hTargetEnt;
  1729. GetNavigator()->SetMovementActivity(ACT_RUN);
  1730. break;
  1731. case GOAL_SAVED_POSITION: //Saved position
  1732. //Setup our stored info
  1733. m_vecStoredPathGoal = m_vSavePosition;
  1734. m_nStoredPathType = GOALTYPE_LOCATION;
  1735. m_fStoredPathFlags = 0;
  1736. m_hStoredPathTarget = NULL;
  1737. GetNavigator()->SetMovementActivity(ACT_RUN);
  1738. break;
  1739. }
  1740. TaskComplete();
  1741. break;
  1742. //==================================================
  1743. // TASK_GET_PATH_TO_GOAL
  1744. //==================================================
  1745. case TASK_GET_PATH_TO_GOAL:
  1746. {
  1747. AI_NavGoal_t goal( m_nStoredPathType,
  1748. AIN_DEF_ACTIVITY,
  1749. AIN_HULL_TOLERANCE,
  1750. AIN_DEF_FLAGS,
  1751. m_hStoredPathTarget );
  1752. bool foundPath = false;
  1753. //Find our path
  1754. switch ( (int) pTask->flTaskData )
  1755. {
  1756. case PATH_TRAVEL: //A land path to our goal
  1757. goal.dest = m_vecStoredPathGoal;
  1758. foundPath = GetNavigator()->SetGoal( goal );
  1759. break;
  1760. case PATH_LOS: //A path to get LOS to our goal
  1761. {
  1762. float flMaxRange = 2000.0f;
  1763. float flMinRange = 0.0f;
  1764. if ( GetActiveWeapon() )
  1765. {
  1766. flMaxRange = MAX( GetActiveWeapon()->m_fMaxRange1, GetActiveWeapon()->m_fMaxRange2 );
  1767. flMinRange = MIN( GetActiveWeapon()->m_fMinRange1, GetActiveWeapon()->m_fMinRange2 );
  1768. }
  1769. else if ( CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1 )
  1770. {
  1771. flMaxRange = InnateRange1MaxRange();
  1772. flMinRange = InnateRange1MinRange();
  1773. }
  1774. // Check against NPC's MAX range
  1775. if ( flMaxRange > m_flDistTooFar )
  1776. {
  1777. flMaxRange = m_flDistTooFar;
  1778. }
  1779. Vector eyePosition = ( m_hStoredPathTarget != NULL ) ? m_hStoredPathTarget->EyePosition() : m_vecStoredPathGoal;
  1780. Vector posLos;
  1781. // See if we've found it
  1782. if ( GetTacticalServices()->FindLos( m_vecStoredPathGoal, eyePosition, flMinRange, flMaxRange, 1.0f, &posLos ) )
  1783. {
  1784. goal.dest = posLos;
  1785. foundPath = GetNavigator()->SetGoal( goal );
  1786. }
  1787. else
  1788. {
  1789. // No LOS to goal
  1790. TaskFail( FAIL_NO_SHOOT );
  1791. return;
  1792. }
  1793. }
  1794. break;
  1795. case PATH_COVER: //Get a path to cover FROM our goal
  1796. {
  1797. CBaseEntity *pEntity = ( m_hStoredPathTarget == nullptr ) ? this : m_hStoredPathTarget.Get();
  1798. //Find later cover first
  1799. Vector coverPos;
  1800. if ( GetTacticalServices()->FindLateralCover( pEntity->EyePosition(), 0, &coverPos ) )
  1801. {
  1802. AI_NavGoal_t goal( coverPos, ACT_RUN );
  1803. GetNavigator()->SetGoal( goal, AIN_CLEAR_PREVIOUS_STATE );
  1804. //FIXME: What exactly is this doing internally?
  1805. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1806. TaskComplete();
  1807. return;
  1808. }
  1809. else
  1810. {
  1811. //Try any cover
  1812. if ( GetTacticalServices()->FindCoverPos( pEntity->GetAbsOrigin(), pEntity->EyePosition(), 0, CoverRadius(), &coverPos ) )
  1813. {
  1814. //If we've found it, find a safe route there
  1815. AI_NavGoal_t coverGoal( GOALTYPE_COVER,
  1816. coverPos,
  1817. ACT_RUN,
  1818. AIN_HULL_TOLERANCE,
  1819. AIN_DEF_FLAGS,
  1820. m_hStoredPathTarget );
  1821. foundPath = GetNavigator()->SetGoal( goal );
  1822. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  1823. }
  1824. else
  1825. {
  1826. TaskFail( FAIL_NO_COVER );
  1827. }
  1828. }
  1829. }
  1830. break;
  1831. }
  1832. //Now validate our try
  1833. if ( foundPath )
  1834. {
  1835. TaskComplete();
  1836. }
  1837. else
  1838. {
  1839. TaskFail( FAIL_NO_ROUTE );
  1840. }
  1841. }
  1842. break;
  1843. case TASK_GET_PATH_TO_ENEMY:
  1844. {
  1845. if (IsUnreachable(GetEnemy()))
  1846. {
  1847. TaskFail(FAIL_NO_ROUTE);
  1848. return;
  1849. }
  1850. CBaseEntity *pEnemy = GetEnemy();
  1851. if ( pEnemy == NULL )
  1852. {
  1853. TaskFail(FAIL_NO_ENEMY);
  1854. return;
  1855. }
  1856. if ( GetNavigator()->SetGoal( GOALTYPE_ENEMY ) )
  1857. {
  1858. TaskComplete();
  1859. }
  1860. else
  1861. {
  1862. // no way to get there =(
  1863. DevWarning( 2, "GetPathToEnemy failed!!\n" );
  1864. RememberUnreachable(GetEnemy());
  1865. TaskFail(FAIL_NO_ROUTE);
  1866. }
  1867. break;
  1868. }
  1869. case TASK_GET_PATH_TO_ENEMY_CORPSE:
  1870. {
  1871. Vector forward;
  1872. AngleVectors( GetLocalAngles(), &forward );
  1873. Vector vecEnemyLKP = GetEnemyLKP();
  1874. GetNavigator()->SetGoal( vecEnemyLKP - forward * 64, AIN_CLEAR_TARGET);
  1875. }
  1876. break;
  1877. case TASK_GET_PATH_TO_PLAYER:
  1878. {
  1879. CBaseEntity *pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  1880. AI_NavGoal_t goal;
  1881. goal.type = GOALTYPE_LOCATION;
  1882. goal.dest = pPlayer->WorldSpaceCenter();
  1883. goal.pTarget = pPlayer;
  1884. GetNavigator()->SetGoal( goal );
  1885. break;
  1886. }
  1887. case TASK_GET_PATH_TO_SAVEPOSITION_LOS:
  1888. {
  1889. if ( GetEnemy() == NULL )
  1890. {
  1891. TaskFail(FAIL_NO_ENEMY);
  1892. return;
  1893. }
  1894. float flMaxRange = 2000;
  1895. float flMinRange = 0;
  1896. if ( GetActiveWeapon() )
  1897. {
  1898. flMaxRange = MAX(GetActiveWeapon()->m_fMaxRange1,GetActiveWeapon()->m_fMaxRange2);
  1899. flMinRange = MIN(GetActiveWeapon()->m_fMinRange1,GetActiveWeapon()->m_fMinRange2);
  1900. }
  1901. else if ( CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1 )
  1902. {
  1903. flMaxRange = InnateRange1MaxRange();
  1904. flMinRange = InnateRange1MinRange();
  1905. }
  1906. // Check against NPC's MAX range
  1907. if (flMaxRange > m_flDistTooFar)
  1908. {
  1909. flMaxRange = m_flDistTooFar;
  1910. }
  1911. Vector posLos;
  1912. if (GetTacticalServices()->FindLos(m_vSavePosition,m_vSavePosition, flMinRange, flMaxRange, 1.0, &posLos))
  1913. {
  1914. GetNavigator()->SetGoal( AI_NavGoal_t( posLos, ACT_RUN, AIN_HULL_TOLERANCE ) );
  1915. }
  1916. else
  1917. {
  1918. // no coverwhatsoever.
  1919. TaskFail(FAIL_NO_SHOOT);
  1920. }
  1921. break;
  1922. }
  1923. case TASK_GET_PATH_TO_TARGET_WEAPON:
  1924. {
  1925. // Finds the nearest node within the leniency distances,
  1926. // whether the node can see the target or not.
  1927. const float XY_LENIENCY = 64.0;
  1928. const float Z_LENIENCY = 72.0;
  1929. if (m_hTargetEnt == NULL)
  1930. {
  1931. TaskFail(FAIL_NO_TARGET);
  1932. }
  1933. else
  1934. {
  1935. // Since this weapon MAY be on a table, we find the nearest node without verifying
  1936. // line-of-sight, since weapons on the table will not be able to see nodes very nearby.
  1937. int node = GetNavigator()->GetNetwork()->NearestNodeToPoint( this, m_hTargetEnt->GetAbsOrigin(), false );
  1938. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( node );
  1939. if( !pNode )
  1940. {
  1941. TaskFail( FAIL_NO_ROUTE );
  1942. break;
  1943. }
  1944. bool bHasPath = true;
  1945. Vector vecNodePos;
  1946. vecNodePos = pNode->GetPosition( GetHullType() );
  1947. float flDistZ;
  1948. flDistZ = fabs( vecNodePos.z - m_hTargetEnt->GetAbsOrigin().z );
  1949. if( flDistZ > Z_LENIENCY )
  1950. {
  1951. // The gun is too far away from its nearest node on the Z axis.
  1952. TaskFail( "Target not within Z_LENIENCY!\n");
  1953. CBaseCombatWeapon *pWeapon = ToBaseCombatWeapon( m_hTargetEnt.Get() );
  1954. if( pWeapon )
  1955. {
  1956. // Lock this weapon for a long time so no one else tries to get it.
  1957. pWeapon->Lock( 30.0f, pWeapon );
  1958. break;
  1959. }
  1960. }
  1961. if( flDistZ >= 16.0 )
  1962. {
  1963. // The gun is higher or lower, but it's within reach. (probably on a table).
  1964. float flDistXY = ( vecNodePos - m_hTargetEnt->GetAbsOrigin() ).Length2D();
  1965. // This means we have to stand on the nearest node and still be able to
  1966. // reach the gun.
  1967. if( flDistXY > XY_LENIENCY )
  1968. {
  1969. TaskFail( "Target not within XY_LENIENCY!\n" );
  1970. CBaseCombatWeapon *pWeapon = ToBaseCombatWeapon( m_hTargetEnt.Get() );
  1971. if( pWeapon )
  1972. {
  1973. // Lock this weapon for a long time so no one else tries to get it.
  1974. pWeapon->Lock( 30.0f, pWeapon );
  1975. break;
  1976. }
  1977. }
  1978. AI_NavGoal_t goal( vecNodePos );
  1979. goal.pTarget = m_hTargetEnt;
  1980. bHasPath = GetNavigator()->SetGoal( goal );
  1981. }
  1982. else
  1983. {
  1984. // The gun is likely just lying on the floor. Just pick it up.
  1985. AI_NavGoal_t goal( m_hTargetEnt->GetAbsOrigin() );
  1986. goal.pTarget = m_hTargetEnt;
  1987. bHasPath = GetNavigator()->SetGoal( goal );
  1988. }
  1989. if( !bHasPath )
  1990. {
  1991. CBaseCombatWeapon *pWeapon = ToBaseCombatWeapon( m_hTargetEnt.Get() );
  1992. if( pWeapon )
  1993. {
  1994. // Lock this weapon for a long time so no one else tries to get it.
  1995. pWeapon->Lock( 15.0f, pWeapon );
  1996. }
  1997. }
  1998. }
  1999. }
  2000. break;
  2001. case TASK_GET_PATH_OFF_OF_NPC:
  2002. {
  2003. Assert( ( GetGroundEntity() && ( GetGroundEntity()->IsPlayer() || ( GetGroundEntity()->IsNPC() && IRelationType( GetGroundEntity() ) == D_LI ) ) ) );
  2004. GetNavigator()->SetAllowBigStep( GetGroundEntity() );
  2005. ChainStartTask( TASK_MOVE_AWAY_PATH, 48 );
  2006. }
  2007. break;
  2008. case TASK_GET_PATH_TO_TARGET:
  2009. {
  2010. if (m_hTargetEnt == NULL)
  2011. {
  2012. TaskFail(FAIL_NO_TARGET);
  2013. }
  2014. else
  2015. {
  2016. AI_NavGoal_t goal( static_cast<const Vector&>(m_hTargetEnt->EyePosition()) );
  2017. goal.pTarget = m_hTargetEnt;
  2018. GetNavigator()->SetGoal( goal );
  2019. }
  2020. break;
  2021. }
  2022. case TASK_GET_PATH_TO_HINTNODE:// for active idles!
  2023. {
  2024. if (!GetHintNode())
  2025. {
  2026. TaskFail(FAIL_NO_HINT_NODE);
  2027. }
  2028. else
  2029. {
  2030. Vector vHintPos;
  2031. GetHintNode()->GetPosition(this, &vHintPos);
  2032. GetNavigator()->SetGoal( AI_NavGoal_t( vHintPos, ACT_RUN ) );
  2033. if ( pTask->flTaskData == 0 )
  2034. GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() );
  2035. if ( GetHintNode()->HintActivityName() != NULL_STRING )
  2036. {
  2037. Activity hintActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(GetHintNode()->HintActivityName()) );
  2038. if ( hintActivity != ACT_INVALID )
  2039. {
  2040. GetNavigator()->SetArrivalActivity( GetHintActivity(GetHintNode()->HintType(), hintActivity) );
  2041. }
  2042. else
  2043. {
  2044. int iSequence = LookupSequence(STRING(GetHintNode()->HintActivityName()));;
  2045. if ( iSequence != ACT_INVALID )
  2046. GetNavigator()->SetArrivalSequence( iSequence );
  2047. }
  2048. }
  2049. }
  2050. break;
  2051. }
  2052. case TASK_GET_PATH_TO_COMMAND_GOAL:
  2053. {
  2054. if (!GetNavigator()->SetGoal( m_vecCommandGoal ))
  2055. {
  2056. OnMoveToCommandGoalFailed();
  2057. TaskFail(FAIL_NO_ROUTE);
  2058. }
  2059. break;
  2060. }
  2061. case TASK_MARK_COMMAND_GOAL_POS:
  2062. // Start watching my position to detect whether another AI process has moved me from my mark.
  2063. m_CommandMoveMonitor.SetMark( this, COMMAND_GOAL_TOLERANCE );
  2064. TaskComplete();
  2065. break;
  2066. case TASK_CLEAR_COMMAND_GOAL:
  2067. m_vecCommandGoal = vec3_invalid;
  2068. TaskComplete();
  2069. break;
  2070. case TASK_GET_PATH_TO_LASTPOSITION:
  2071. {
  2072. if (!GetNavigator()->SetGoal( m_vecLastPosition ))
  2073. {
  2074. TaskFail(FAIL_NO_ROUTE);
  2075. }
  2076. else
  2077. {
  2078. GetNavigator()->SetGoalTolerance( 48 );
  2079. }
  2080. break;
  2081. }
  2082. case TASK_GET_PATH_TO_SAVEPOSITION:
  2083. {
  2084. GetNavigator()->SetGoal( m_vSavePosition );
  2085. break;
  2086. }
  2087. case TASK_GET_PATH_TO_RANDOM_NODE: // Task argument is lenth of path to build
  2088. {
  2089. if ( GetNavigator()->SetRandomGoal( pTask->flTaskData ) )
  2090. TaskComplete();
  2091. else
  2092. TaskFail(FAIL_NO_REACHABLE_NODE);
  2093. break;
  2094. }
  2095. case TASK_GET_PATH_TO_BESTSOUND:
  2096. {
  2097. CSound *pSound = GetBestSound();
  2098. if (!pSound)
  2099. {
  2100. TaskFail(FAIL_NO_SOUND);
  2101. }
  2102. else
  2103. {
  2104. GetNavigator()->SetGoal( pSound->GetSoundReactOrigin() );
  2105. }
  2106. break;
  2107. }
  2108. case TASK_GET_PATH_TO_BESTSCENT:
  2109. {
  2110. CSound *pScent = GetBestScent();
  2111. if (!pScent)
  2112. {
  2113. TaskFail(FAIL_NO_SCENT);
  2114. }
  2115. else
  2116. {
  2117. GetNavigator()->SetGoal( pScent->GetSoundOrigin() );
  2118. }
  2119. break;
  2120. }
  2121. case TASK_GET_PATH_AWAY_FROM_BEST_SOUND:
  2122. {
  2123. CSound *pBestSound = GetBestSound();
  2124. if ( !pBestSound )
  2125. {
  2126. TaskFail("No Sound!");
  2127. break;
  2128. }
  2129. GetMotor()->SetIdealYawToTarget( pBestSound->GetSoundOrigin() );
  2130. ChainStartTask( TASK_MOVE_AWAY_PATH, pTask->flTaskData );
  2131. LockBestSound();
  2132. break;
  2133. }
  2134. case TASK_MOVE_AWAY_PATH:
  2135. {
  2136. // Drop into run task to support interrupt
  2137. DesireStand();
  2138. }
  2139. break;
  2140. case TASK_WEAPON_RUN_PATH:
  2141. case TASK_ITEM_RUN_PATH:
  2142. GetNavigator()->SetMovementActivity(ACT_RUN);
  2143. break;
  2144. case TASK_RUN_PATH:
  2145. {
  2146. // UNDONE: This is in some default AI and some NPCs can't run? -- walk instead?
  2147. if ( TranslateActivity( ACT_RUN ) != ACT_INVALID )
  2148. {
  2149. GetNavigator()->SetMovementActivity( ACT_RUN );
  2150. }
  2151. else
  2152. {
  2153. GetNavigator()->SetMovementActivity(ACT_WALK);
  2154. }
  2155. // Cover is void once I move
  2156. Forget( bits_MEMORY_INCOVER );
  2157. TaskComplete();
  2158. break;
  2159. }
  2160. case TASK_WALK_PATH_FOR_UNITS:
  2161. {
  2162. GetNavigator()->SetMovementActivity(ACT_WALK);
  2163. break;
  2164. }
  2165. case TASK_RUN_PATH_FOR_UNITS:
  2166. {
  2167. GetNavigator()->SetMovementActivity(ACT_RUN);
  2168. break;
  2169. }
  2170. case TASK_WALK_PATH:
  2171. {
  2172. bool bIsFlying = (GetMoveType() == MOVETYPE_FLY) || (GetMoveType() == MOVETYPE_FLYGRAVITY);
  2173. if ( bIsFlying && ( TranslateActivity( ACT_FLY ) != ACT_INVALID) )
  2174. {
  2175. GetNavigator()->SetMovementActivity(ACT_FLY);
  2176. }
  2177. else if ( TranslateActivity( ACT_WALK ) != ACT_INVALID )
  2178. {
  2179. GetNavigator()->SetMovementActivity(ACT_WALK);
  2180. }
  2181. else
  2182. {
  2183. GetNavigator()->SetMovementActivity(ACT_RUN);
  2184. }
  2185. // Cover is void once I move
  2186. Forget( bits_MEMORY_INCOVER );
  2187. TaskComplete();
  2188. break;
  2189. }
  2190. case TASK_WALK_PATH_WITHIN_DIST:
  2191. {
  2192. GetNavigator()->SetMovementActivity(ACT_WALK);
  2193. // set that we're probably going to stop before the goal
  2194. GetNavigator()->SetArrivalDistance( pTask->flTaskData );
  2195. break;
  2196. }
  2197. case TASK_RUN_PATH_WITHIN_DIST:
  2198. {
  2199. GetNavigator()->SetMovementActivity(ACT_RUN);
  2200. // set that we're probably going to stop before the goal
  2201. GetNavigator()->SetArrivalDistance( pTask->flTaskData );
  2202. break;
  2203. }
  2204. case TASK_RUN_PATH_FLEE:
  2205. {
  2206. Vector vecDiff;
  2207. vecDiff = GetLocalOrigin() - GetNavigator()->GetGoalPos();
  2208. if( vecDiff.Length() <= pTask->flTaskData )
  2209. {
  2210. GetNavigator()->StopMoving();
  2211. TaskFail("Flee path shorter than task parameter");
  2212. }
  2213. else
  2214. {
  2215. GetNavigator()->SetMovementActivity(ACT_RUN);
  2216. }
  2217. break;
  2218. }
  2219. case TASK_WALK_PATH_TIMED:
  2220. {
  2221. GetNavigator()->SetMovementActivity(ACT_WALK);
  2222. SetWait( pTask->flTaskData );
  2223. break;
  2224. }
  2225. case TASK_RUN_PATH_TIMED:
  2226. {
  2227. GetNavigator()->SetMovementActivity(ACT_RUN);
  2228. SetWait( pTask->flTaskData );
  2229. break;
  2230. }
  2231. case TASK_STRAFE_PATH:
  2232. {
  2233. Vector2D vec2DirToPoint;
  2234. Vector2D vec2RightSide;
  2235. // to start strafing, we have to first figure out if the target is on the left side or right side
  2236. Vector right;
  2237. AngleVectors( GetLocalAngles(), NULL, &right, NULL );
  2238. vec2DirToPoint = ( GetNavigator()->GetCurWaypointPos() - GetLocalOrigin() ).AsVector2D();
  2239. Vector2DNormalize(vec2DirToPoint);
  2240. vec2RightSide = right.AsVector2D();
  2241. Vector2DNormalize(vec2RightSide);
  2242. if ( DotProduct2D ( vec2DirToPoint, vec2RightSide ) > 0 )
  2243. {
  2244. // strafe right
  2245. GetNavigator()->SetMovementActivity(ACT_STRAFE_RIGHT);
  2246. }
  2247. else
  2248. {
  2249. // strafe left
  2250. GetNavigator()->SetMovementActivity(ACT_STRAFE_LEFT);
  2251. }
  2252. TaskComplete();
  2253. break;
  2254. }
  2255. case TASK_WAIT_FOR_MOVEMENT_STEP:
  2256. {
  2257. if ( IsMovementFrozen() )
  2258. {
  2259. TaskFail(FAIL_FROZEN);
  2260. break;
  2261. }
  2262. if(!GetNavigator()->IsGoalActive())
  2263. {
  2264. TaskComplete();
  2265. return;
  2266. }
  2267. if ( IsActivityFinished() )
  2268. {
  2269. TaskComplete();
  2270. return;
  2271. }
  2272. ValidateNavGoal();
  2273. break;
  2274. }
  2275. case TASK_WAIT_FOR_MOVEMENT:
  2276. {
  2277. if ( IsMovementFrozen() )
  2278. {
  2279. TaskFail(FAIL_FROZEN);
  2280. break;
  2281. }
  2282. if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  2283. {
  2284. TaskComplete();
  2285. GetNavigator()->ClearGoal(); // Clear residual state
  2286. }
  2287. else if (!GetNavigator()->IsGoalActive())
  2288. {
  2289. SetIdealActivity( GetStoppedActivity() );
  2290. }
  2291. else
  2292. {
  2293. // Check validity of goal type
  2294. ValidateNavGoal();
  2295. }
  2296. break;
  2297. }
  2298. case TASK_SMALL_FLINCH:
  2299. {
  2300. Remember(bits_MEMORY_FLINCHED);
  2301. SetIdealActivity( GetFlinchActivity( false, false ) );
  2302. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
  2303. break;
  2304. }
  2305. case TASK_BIG_FLINCH:
  2306. {
  2307. Remember(bits_MEMORY_FLINCHED);
  2308. SetIdealActivity( GetFlinchActivity( true, false ) );
  2309. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
  2310. break;
  2311. }
  2312. case TASK_DIE:
  2313. {
  2314. GetNavigator()->StopMoving();
  2315. SetIdealActivity( GetDeathActivity() );
  2316. m_lifeState = LIFE_DYING;
  2317. break;
  2318. }
  2319. case TASK_SOUND_WAKE:
  2320. {
  2321. AlertSound();
  2322. TaskComplete();
  2323. break;
  2324. }
  2325. case TASK_SOUND_DIE:
  2326. {
  2327. CTakeDamageInfo info;
  2328. DeathSound( info );
  2329. TaskComplete();
  2330. break;
  2331. }
  2332. case TASK_SOUND_IDLE:
  2333. {
  2334. IdleSound();
  2335. TaskComplete();
  2336. break;
  2337. }
  2338. case TASK_SOUND_PAIN:
  2339. {
  2340. CTakeDamageInfo info;
  2341. PainSound( info );
  2342. TaskComplete();
  2343. break;
  2344. }
  2345. case TASK_SOUND_ANGRY:
  2346. {
  2347. // sounds are complete as soon as we get here, cause we've already played them.
  2348. DevMsg( 2, "SOUND\n" );
  2349. TaskComplete();
  2350. break;
  2351. }
  2352. case TASK_SPEAK_SENTENCE:
  2353. {
  2354. SpeakSentence(pTask->flTaskData);
  2355. TaskComplete();
  2356. break;
  2357. }
  2358. case TASK_WAIT_FOR_SPEAK_FINISH:
  2359. {
  2360. if ( !GetExpresser() )
  2361. TaskComplete();
  2362. else
  2363. {
  2364. // Are we waiting for our speech to end? Or for the mutex to be free?
  2365. if ( pTask->flTaskData )
  2366. {
  2367. // Waiting for our speech to end
  2368. if ( GetExpresser()->CanSpeakAfterMyself() )
  2369. {
  2370. TaskComplete();
  2371. }
  2372. }
  2373. else
  2374. {
  2375. // Waiting for the speech & the delay afterwards
  2376. if ( !GetExpresser()->IsSpeaking() )
  2377. {
  2378. TaskComplete();
  2379. }
  2380. }
  2381. break;
  2382. }
  2383. break;
  2384. }
  2385. case TASK_WAIT_FOR_SCRIPT:
  2386. {
  2387. if ( !m_hCine )
  2388. {
  2389. DevMsg( "Scripted sequence destroyed while in use\n" );
  2390. TaskFail( FAIL_SCHEDULE_NOT_FOUND );
  2391. break;
  2392. }
  2393. break;
  2394. }
  2395. case TASK_PUSH_SCRIPT_ARRIVAL_ACTIVITY:
  2396. {
  2397. if ( !m_hCine )
  2398. {
  2399. DevMsg( "Scripted sequence destroyed while in use\n" );
  2400. TaskFail( FAIL_SCHEDULE_NOT_FOUND );
  2401. break;
  2402. }
  2403. string_t iszArrivalText;
  2404. if ( m_hCine->m_iszEntry != NULL_STRING )
  2405. {
  2406. iszArrivalText = m_hCine->m_iszEntry;
  2407. }
  2408. else if ( m_hCine->m_iszPlay != NULL_STRING )
  2409. {
  2410. iszArrivalText = m_hCine->m_iszPlay;
  2411. }
  2412. else if ( m_hCine->m_iszPostIdle != NULL_STRING )
  2413. {
  2414. iszArrivalText = m_hCine->m_iszPostIdle;
  2415. }
  2416. else
  2417. iszArrivalText = NULL_STRING;
  2418. m_ScriptArrivalActivity = AIN_DEF_ACTIVITY;
  2419. m_strScriptArrivalSequence = NULL_STRING;
  2420. if ( iszArrivalText != NULL_STRING )
  2421. {
  2422. m_ScriptArrivalActivity = (Activity)GetActivityID( STRING( iszArrivalText ) );
  2423. if ( m_ScriptArrivalActivity == ACT_INVALID )
  2424. m_strScriptArrivalSequence = iszArrivalText;
  2425. }
  2426. TaskComplete();
  2427. break;
  2428. }
  2429. case TASK_PLAY_SCRIPT:
  2430. {
  2431. // Throw away any stopping paths we have saved, because we
  2432. // won't be able to resume them after the sequence.
  2433. GetNavigator()->IgnoreStoppingPath();
  2434. if ( HasMovement( GetSequence() ) || m_hCine->m_bIgnoreGravity )
  2435. {
  2436. AddFlag( FL_FLY );
  2437. SetGroundEntity( NULL );
  2438. }
  2439. if (m_hCine)
  2440. {
  2441. m_hCine->SynchronizeSequence( this );
  2442. }
  2443. //
  2444. // Start playing a scripted sequence.
  2445. //
  2446. m_scriptState = SCRIPT_PLAYING;
  2447. break;
  2448. }
  2449. case TASK_PLAY_SCRIPT_POST_IDLE:
  2450. {
  2451. //
  2452. // Start playing a scripted post idle.
  2453. //
  2454. m_scriptState = SCRIPT_POST_IDLE;
  2455. break;
  2456. }
  2457. // This is the first task of every schedule driven by a scripted_sequence.
  2458. // Delay starting the sequence until all actors have hit their marks.
  2459. case TASK_PRE_SCRIPT:
  2460. {
  2461. if ( !ai_task_pre_script.GetBool() )
  2462. {
  2463. TaskComplete();
  2464. }
  2465. else if ( !m_hCine )
  2466. {
  2467. TaskComplete();
  2468. //DevMsg( "Scripted sequence destroyed while in use\n" );
  2469. //TaskFail( FAIL_SCHEDULE_NOT_FOUND );
  2470. }
  2471. else
  2472. {
  2473. m_hCine->DelayStart( true );
  2474. TaskComplete();
  2475. }
  2476. break;
  2477. }
  2478. case TASK_ENABLE_SCRIPT:
  2479. {
  2480. //
  2481. // Start waiting to play a script. Play the script's pre idle animation if one
  2482. // is specified, otherwise just go to our default idle activity.
  2483. //
  2484. if ( m_hCine->m_iszPreIdle != NULL_STRING )
  2485. {
  2486. m_hCine->StartSequence( ( CAI_BaseNPC * )this, m_hCine->m_iszPreIdle, false );
  2487. if ( FStrEq( STRING( m_hCine->m_iszPreIdle ), STRING( m_hCine->m_iszPlay ) ) )
  2488. {
  2489. m_flPlaybackRate = 0;
  2490. }
  2491. }
  2492. else if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK )
  2493. {
  2494. // FIXME: too many ss assume its safe to leave the npc is whatever sequence they were in before, so only slam their activity
  2495. // if they're playing a recognizable movement animation
  2496. //
  2497. #ifdef HL2_EPISODIC
  2498. // dvs: Check current activity rather than ideal activity. Since scripted NPCs early out in MaintainActivity,
  2499. // they'll never reach their ideal activity if it's different from their current activity.
  2500. if ( GetActivity() == ACT_WALK ||
  2501. GetActivity() == ACT_RUN ||
  2502. GetActivity() == ACT_WALK_AIM ||
  2503. GetActivity() == ACT_RUN_AIM )
  2504. {
  2505. SetActivity( ACT_IDLE );
  2506. }
  2507. #else
  2508. if ( GetIdealActivity() == ACT_WALK ||
  2509. GetIdealActivity() == ACT_RUN ||
  2510. GetIdealActivity() == ACT_WALK_AIM ||
  2511. GetIdealActivity() == ACT_RUN_AIM )
  2512. {
  2513. SetActivity( ACT_IDLE );
  2514. }
  2515. #endif // HL2_EPISODIC
  2516. }
  2517. break;
  2518. }
  2519. case TASK_PLANT_ON_SCRIPT:
  2520. {
  2521. if ( m_hTargetEnt != NULL )
  2522. {
  2523. SetLocalOrigin( m_hTargetEnt->GetAbsOrigin() ); // Plant on target
  2524. }
  2525. TaskComplete();
  2526. break;
  2527. }
  2528. case TASK_FACE_SCRIPT:
  2529. {
  2530. if ( m_hTargetEnt != NULL )
  2531. {
  2532. GetMotor()->SetIdealYaw( UTIL_AngleMod( m_hTargetEnt->GetLocalAngles().y ) );
  2533. }
  2534. if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK )
  2535. {
  2536. SetTurnActivity();
  2537. // dvs: HACK: MaintainActivity won't do anything while scripted, so go straight there.
  2538. SetActivity( GetIdealActivity() );
  2539. }
  2540. GetNavigator()->StopMoving();
  2541. break;
  2542. }
  2543. case TASK_PLAY_SCENE:
  2544. {
  2545. // inside a scene with movement and sequence commands
  2546. break;
  2547. }
  2548. case TASK_SUGGEST_STATE:
  2549. {
  2550. SetIdealState( (NPC_STATE)(int)pTask->flTaskData );
  2551. TaskComplete();
  2552. break;
  2553. }
  2554. case TASK_SET_FAIL_SCHEDULE:
  2555. m_failSchedule = (int)pTask->flTaskData;
  2556. TaskComplete();
  2557. break;
  2558. case TASK_SET_TOLERANCE_DISTANCE:
  2559. GetNavigator()->SetGoalTolerance( (int)pTask->flTaskData );
  2560. TaskComplete();
  2561. break;
  2562. case TASK_SET_ROUTE_SEARCH_TIME:
  2563. GetNavigator()->SetMaxRouteRebuildTime( (int)pTask->flTaskData );
  2564. TaskComplete();
  2565. break;
  2566. case TASK_CLEAR_FAIL_SCHEDULE:
  2567. m_failSchedule = SCHED_NONE;
  2568. TaskComplete();
  2569. break;
  2570. case TASK_WEAPON_FIND:
  2571. {
  2572. m_hTargetEnt = Weapon_FindUsable( Vector(1000,1000,1000) );
  2573. if (m_hTargetEnt)
  2574. {
  2575. TaskComplete();
  2576. }
  2577. else
  2578. {
  2579. TaskFail(FAIL_ITEM_NO_FIND);
  2580. }
  2581. }
  2582. break;
  2583. case TASK_ITEM_PICKUP:
  2584. {
  2585. SetIdealActivity( ACT_PICKUP_GROUND );
  2586. }
  2587. break;
  2588. case TASK_WEAPON_PICKUP:
  2589. {
  2590. if( GetActiveWeapon() )
  2591. {
  2592. Weapon_Drop( GetActiveWeapon() );
  2593. }
  2594. if( GetTarget() )
  2595. {
  2596. CBaseCombatWeapon *pWeapon = ToBaseCombatWeapon(GetTarget());
  2597. if( pWeapon )
  2598. {
  2599. if( Weapon_IsOnGround( pWeapon ) )
  2600. {
  2601. // Squat down
  2602. SetIdealActivity( ACT_PICKUP_GROUND );
  2603. }
  2604. else
  2605. {
  2606. // Reach and take this weapon from rack or shelf.
  2607. SetIdealActivity( ACT_PICKUP_RACK );
  2608. }
  2609. return;
  2610. }
  2611. }
  2612. TaskFail("Weapon went away!\n");
  2613. }
  2614. break;
  2615. case TASK_WEAPON_CREATE:
  2616. {
  2617. if( !GetActiveWeapon() && GetTarget() )
  2618. {
  2619. // Create a copy of the weapon this NPC is trying to pick up.
  2620. CBaseCombatWeapon *pTargetWeapon = ToBaseCombatWeapon(GetTarget());
  2621. if( pTargetWeapon )
  2622. {
  2623. CBaseCombatWeapon *pWeapon = Weapon_Create( pTargetWeapon->GetClassname() );
  2624. if ( pWeapon )
  2625. {
  2626. Weapon_Equip( pWeapon );
  2627. }
  2628. }
  2629. }
  2630. SetTarget( NULL );
  2631. TaskComplete();
  2632. }
  2633. break;
  2634. case TASK_USE_SMALL_HULL:
  2635. {
  2636. SetHullSizeSmall();
  2637. TaskComplete();
  2638. }
  2639. break;
  2640. case TASK_FALL_TO_GROUND:
  2641. // Set a wait time to try to force a ground ent.
  2642. SetWait(4);
  2643. break;
  2644. case TASK_WANDER:
  2645. {
  2646. // This task really uses 2 parameters, so we have to extract
  2647. // them from a single integer. To send both parameters, the
  2648. // formula is MIN_DIST * 10000 + MAX_DIST
  2649. {
  2650. int iMinDist, iMaxDist, iParameter;
  2651. iParameter = pTask->flTaskData;
  2652. iMinDist = iParameter / 10000;
  2653. iMaxDist = iParameter - (iMinDist * 10000);
  2654. if ( GetNavigator()->SetWanderGoal( iMinDist, iMaxDist ) )
  2655. TaskComplete();
  2656. else
  2657. TaskFail(FAIL_NO_REACHABLE_NODE);
  2658. }
  2659. }
  2660. break;
  2661. case TASK_FREEZE:
  2662. m_flPlaybackRate = 0;
  2663. break;
  2664. case TASK_GATHER_CONDITIONS:
  2665. GatherConditions();
  2666. TaskComplete();
  2667. break;
  2668. case TASK_IGNORE_OLD_ENEMIES:
  2669. m_flAcceptableTimeSeenEnemy = gpGlobals->curtime;
  2670. if ( GetEnemy() && GetEnemyLastTimeSeen() < m_flAcceptableTimeSeenEnemy )
  2671. {
  2672. CBaseEntity *pNewEnemy = BestEnemy();
  2673. Assert( pNewEnemy != GetEnemy() );
  2674. if( pNewEnemy != NULL )
  2675. {
  2676. // New enemy! Clear the timers and set conditions.
  2677. SetEnemy( pNewEnemy );
  2678. SetState( NPC_STATE_COMBAT );
  2679. }
  2680. else
  2681. {
  2682. SetEnemy( NULL );
  2683. ClearAttackConditions();
  2684. }
  2685. }
  2686. TaskComplete();
  2687. break;
  2688. case TASK_ADD_HEALTH:
  2689. TakeHealth( (int)pTask->flTaskData, DMG_GENERIC );
  2690. TaskComplete();
  2691. break;
  2692. default:
  2693. {
  2694. DevMsg( "No StartTask entry for %s\n", TaskName( task ) );
  2695. }
  2696. break;
  2697. }
  2698. }
  2699. void CAI_BaseNPC::StartTaskOverlay()
  2700. {
  2701. if ( IsCurTaskContinuousMove() )
  2702. {
  2703. if ( ShouldMoveAndShoot() )
  2704. {
  2705. m_MoveAndShootOverlay.StartShootWhileMove();
  2706. }
  2707. else
  2708. {
  2709. m_MoveAndShootOverlay.NoShootWhileMove();
  2710. }
  2711. }
  2712. }
  2713. //-----------------------------------------------------------------------------
  2714. // TASK_DIE.
  2715. //-----------------------------------------------------------------------------
  2716. void CAI_BaseNPC::RunDieTask()
  2717. {
  2718. AutoMovement();
  2719. if ( IsActivityFinished() && GetCycle() >= 1.0f )
  2720. {
  2721. m_lifeState = LIFE_DEAD;
  2722. SetThink ( NULL );
  2723. StopAnimation();
  2724. if ( !BBoxFlat() )
  2725. {
  2726. // a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will
  2727. // block the player on a slope or stairs, the corpse is made nonsolid.
  2728. // SetSolid( SOLID_NOT );
  2729. UTIL_SetSize ( this, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) );
  2730. }
  2731. else // !!!HACKHACK - put NPC in a thin, wide bounding box until we fix the solid type/bounding volume problem
  2732. UTIL_SetSize ( this, WorldAlignMins(), Vector ( WorldAlignMaxs().x, WorldAlignMaxs().y, WorldAlignMins().z + 1 ) );
  2733. }
  2734. }
  2735. //-----------------------------------------------------------------------------
  2736. // TASK_RANGE_ATTACK1 / TASK_RANGE_ATTACK2 / etc.
  2737. //-----------------------------------------------------------------------------
  2738. void CAI_BaseNPC::RunAttackTask( int task )
  2739. {
  2740. AutoMovement( );
  2741. Vector vecEnemyLKP = GetEnemyLKP();
  2742. // If our enemy was killed, but I'm not done animating, the last known position comes
  2743. // back as the origin and makes the me face the world origin if my attack schedule
  2744. // doesn't break when my enemy dies. (sjb)
  2745. if( vecEnemyLKP != vec3_origin )
  2746. {
  2747. if ( ( task == TASK_RANGE_ATTACK1 || task == TASK_RELOAD ) &&
  2748. ( CapabilitiesGet() & bits_CAP_AIM_GUN ) &&
  2749. FInAimCone( vecEnemyLKP ) )
  2750. {
  2751. // Arms will aim, so leave body yaw as is
  2752. GetMotor()->SetIdealYawAndUpdate( GetMotor()->GetIdealYaw(), AI_KEEP_YAW_SPEED );
  2753. }
  2754. else
  2755. {
  2756. GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP, AI_KEEP_YAW_SPEED );
  2757. }
  2758. }
  2759. if ( IsActivityFinished() )
  2760. {
  2761. if ( task == TASK_RELOAD && GetShotRegulator() )
  2762. {
  2763. GetShotRegulator()->Reset( false );
  2764. }
  2765. TaskComplete();
  2766. }
  2767. }
  2768. //=========================================================
  2769. // RunTask
  2770. //=========================================================
  2771. void CAI_BaseNPC::RunTask( const Task_t *pTask )
  2772. {
  2773. VPROF_BUDGET( "CAI_BaseNPC::RunTask", VPROF_BUDGETGROUP_NPCS );
  2774. switch ( pTask->iTask )
  2775. {
  2776. case TASK_GET_PATH_TO_RANDOM_NODE:
  2777. {
  2778. break;
  2779. }
  2780. case TASK_TURN_RIGHT:
  2781. case TASK_TURN_LEFT:
  2782. {
  2783. // If the yaw is locked, this function will not act correctly
  2784. Assert( GetMotor()->IsYawLocked() == false );
  2785. GetMotor()->UpdateYaw();
  2786. if ( FacingIdeal() )
  2787. {
  2788. TaskComplete();
  2789. }
  2790. break;
  2791. }
  2792. case TASK_PLAY_PRIVATE_SEQUENCE_FACE_ENEMY:
  2793. case TASK_PLAY_SEQUENCE_FACE_ENEMY:
  2794. case TASK_PLAY_SEQUENCE_FACE_TARGET:
  2795. {
  2796. CBaseEntity *pTarget;
  2797. if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET )
  2798. pTarget = m_hTargetEnt;
  2799. else
  2800. pTarget = GetEnemy();
  2801. if ( pTarget )
  2802. {
  2803. GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetLocalOrigin() , AI_KEEP_YAW_SPEED );
  2804. }
  2805. if ( IsActivityFinished() )
  2806. {
  2807. TaskComplete();
  2808. }
  2809. }
  2810. break;
  2811. case TASK_PLAY_HINT_ACTIVITY:
  2812. {
  2813. if (!GetHintNode())
  2814. {
  2815. TaskFail(FAIL_NO_HINT_NODE);
  2816. }
  2817. // Put a debugging check in here
  2818. if (GetHintNode()->User() != this)
  2819. {
  2820. DevMsg("Hint node (%s) being used by non-owner!\n",GetHintNode()->GetDebugName());
  2821. }
  2822. if ( IsActivityFinished() )
  2823. {
  2824. TaskComplete();
  2825. }
  2826. break;
  2827. }
  2828. case TASK_STOP_MOVING:
  2829. {
  2830. if ( pTask->flTaskData == 1 )
  2831. {
  2832. ChainRunTask( TASK_WAIT_FOR_MOVEMENT );
  2833. if ( GetTaskStatus() == TASKSTATUS_COMPLETE )
  2834. {
  2835. DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" );
  2836. }
  2837. }
  2838. else
  2839. {
  2840. // if they're jumping, wait until they land
  2841. if (GetNavType() == NAV_JUMP)
  2842. {
  2843. if (GetFlags() & FL_ONGROUND)
  2844. {
  2845. DbgNavMsg( this, "Jump landed\n" );
  2846. SetNavType( NAV_GROUND ); // this assumes that NAV_JUMP only happens with npcs that use NAV_GROUND as base movement
  2847. }
  2848. else if (GetSmoothedVelocity().Length() > 0.01) // use an EPSILON damnit!!
  2849. {
  2850. // wait until you land
  2851. break;
  2852. }
  2853. else
  2854. {
  2855. DbgNavMsg( this, "Jump stuck\n" );
  2856. // stopped and stuck!
  2857. SetNavType( NAV_GROUND );
  2858. TaskFail( FAIL_STUCK_ONTOP );
  2859. }
  2860. }
  2861. // @TODO (toml 10-30-02): this is unacceptable, but needed until navigation can handle commencing
  2862. // a navigation while in the middle of a climb
  2863. if (GetNavType() == NAV_CLIMB)
  2864. {
  2865. // wait until you reach the end
  2866. break;
  2867. }
  2868. DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" );
  2869. SetIdealActivity( GetStoppedActivity() );
  2870. TaskComplete();
  2871. }
  2872. break;
  2873. }
  2874. case TASK_PLAY_SEQUENCE:
  2875. case TASK_PLAY_PRIVATE_SEQUENCE:
  2876. {
  2877. AutoMovement( );
  2878. if ( IsActivityFinished() )
  2879. {
  2880. TaskComplete();
  2881. }
  2882. break;
  2883. }
  2884. case TASK_ADD_GESTURE_WAIT:
  2885. {
  2886. if ( IsWaitFinished() )
  2887. {
  2888. TaskComplete();
  2889. }
  2890. break;
  2891. }
  2892. case TASK_SET_ACTIVITY:
  2893. {
  2894. if ( IsActivityStarted() )
  2895. {
  2896. TaskComplete();
  2897. }
  2898. }
  2899. break;
  2900. case TASK_FACE_ENEMY:
  2901. {
  2902. // If the yaw is locked, this function will not act correctly
  2903. Assert( GetMotor()->IsYawLocked() == false );
  2904. Vector vecEnemyLKP = GetEnemyLKP();
  2905. if (!FInAimCone( vecEnemyLKP ))
  2906. {
  2907. GetMotor()->SetIdealYawToTarget( vecEnemyLKP );
  2908. GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
  2909. }
  2910. else
  2911. {
  2912. float flReasonableFacing = CalcReasonableFacing( true );
  2913. if ( fabsf( flReasonableFacing - GetMotor()->GetIdealYaw() ) > 1 )
  2914. GetMotor()->SetIdealYaw( flReasonableFacing );
  2915. }
  2916. GetMotor()->UpdateYaw();
  2917. if ( FacingIdeal( m_flFaceEnemyTolerance ) )
  2918. {
  2919. TaskComplete();
  2920. }
  2921. break;
  2922. }
  2923. case TASK_FACE_PLAYER:
  2924. {
  2925. // Get edict for one player
  2926. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  2927. if ( pPlayer )
  2928. {
  2929. GetMotor()->SetIdealYawToTargetAndUpdate( pPlayer->GetAbsOrigin(), AI_KEEP_YAW_SPEED );
  2930. SetTurnActivity();
  2931. if ( IsWaitFinished() && GetMotor()->DeltaIdealYaw() < 10 )
  2932. {
  2933. TaskComplete();
  2934. }
  2935. }
  2936. else
  2937. {
  2938. TaskFail(FAIL_NO_PLAYER);
  2939. }
  2940. }
  2941. break;
  2942. case TASK_FIND_COVER_FROM_BEST_SOUND:
  2943. {
  2944. switch( GetTaskInterrupt() )
  2945. {
  2946. case 0:
  2947. {
  2948. if ( !FindCoverFromBestSound( &m_vInterruptSavePosition ) )
  2949. TaskFail(FAIL_NO_COVER);
  2950. else
  2951. {
  2952. GetNavigator()->IgnoreStoppingPath();
  2953. LockBestSound();
  2954. TaskInterrupt();
  2955. }
  2956. }
  2957. break;
  2958. case 1:
  2959. {
  2960. AI_NavGoal_t goal(m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE);
  2961. CSound *pBestSound = GetBestSound();
  2962. if ( pBestSound )
  2963. goal.maxInitialSimplificationDist = pBestSound->Volume() * 0.5;
  2964. if ( GetNavigator()->SetGoal( goal ) )
  2965. {
  2966. m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  2967. }
  2968. }
  2969. break;
  2970. }
  2971. }
  2972. break;
  2973. case TASK_FACE_HINTNODE:
  2974. case TASK_FACE_LASTPOSITION:
  2975. case TASK_FACE_SAVEPOSITION:
  2976. case TASK_FACE_AWAY_FROM_SAVEPOSITION:
  2977. case TASK_FACE_TARGET:
  2978. case TASK_FACE_IDEAL:
  2979. case TASK_FACE_SCRIPT:
  2980. case TASK_FACE_PATH:
  2981. {
  2982. // If the yaw is locked, this function will not act correctly
  2983. Assert( GetMotor()->IsYawLocked() == false );
  2984. GetMotor()->UpdateYaw();
  2985. if ( FacingIdeal() )
  2986. {
  2987. TaskComplete();
  2988. }
  2989. break;
  2990. }
  2991. case TASK_FACE_REASONABLE:
  2992. {
  2993. // If the yaw is locked, this function will not act correctly
  2994. Assert( GetMotor()->IsYawLocked() == false );
  2995. GetMotor()->UpdateYaw();
  2996. if ( FacingIdeal() )
  2997. {
  2998. TaskComplete();
  2999. }
  3000. break;
  3001. }
  3002. case TASK_WAIT_PVS:
  3003. {
  3004. if ( ShouldAlwaysThink() ||
  3005. UTIL_FindClientInPVS(edict()) ||
  3006. ( GetState() == NPC_STATE_COMBAT && GetEnemy() && gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ) < 15 ) )
  3007. {
  3008. TaskComplete();
  3009. }
  3010. break;
  3011. }
  3012. case TASK_WAIT_INDEFINITE:
  3013. {
  3014. // don't do anything.
  3015. break;
  3016. }
  3017. case TASK_WAIT:
  3018. case TASK_WAIT_RANDOM:
  3019. {
  3020. if ( IsWaitFinished() )
  3021. {
  3022. TaskComplete();
  3023. }
  3024. break;
  3025. }
  3026. case TASK_WAIT_FACE_ENEMY:
  3027. case TASK_WAIT_FACE_ENEMY_RANDOM:
  3028. {
  3029. Vector vecEnemyLKP = GetEnemyLKP();
  3030. if (!FInAimCone( vecEnemyLKP ))
  3031. {
  3032. GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP , AI_KEEP_YAW_SPEED );
  3033. }
  3034. if ( IsWaitFinished() )
  3035. {
  3036. TaskComplete();
  3037. }
  3038. break;
  3039. }
  3040. case TASK_WAIT_UNTIL_NO_DANGER_SOUND:
  3041. if( !HasCondition( COND_HEAR_DANGER ) )
  3042. {
  3043. TaskComplete();
  3044. }
  3045. break;
  3046. case TASK_MOVE_TO_TARGET_RANGE:
  3047. case TASK_MOVE_TO_GOAL_RANGE:
  3048. {
  3049. // Identical tasks, except that target_range uses m_hTargetEnt,
  3050. // and Goal range uses the nav goal
  3051. CBaseEntity *pTarget = NULL;
  3052. if ( pTask->iTask == TASK_MOVE_TO_GOAL_RANGE )
  3053. {
  3054. pTarget = GetNavigator()->GetGoalTarget();
  3055. }
  3056. if ( !pTarget )
  3057. {
  3058. pTarget = m_hTargetEnt.Get();
  3059. }
  3060. float distance;
  3061. if ( pTarget == NULL )
  3062. {
  3063. TaskFail(FAIL_NO_TARGET);
  3064. }
  3065. else if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  3066. {
  3067. TaskComplete();
  3068. GetNavigator()->ClearGoal(); // Clear residual state
  3069. }
  3070. else
  3071. {
  3072. bool bForceRun = false;
  3073. // Check Z first, and only check 2d if we're within that
  3074. Vector vecGoalPos = GetNavigator()->GetGoalPos();
  3075. distance = fabs(vecGoalPos.z - GetLocalOrigin().z);
  3076. if ( distance < pTask->flTaskData )
  3077. {
  3078. distance = ( vecGoalPos - GetLocalOrigin() ).Length2D();
  3079. }
  3080. else
  3081. {
  3082. // If the target is significantly higher or lower than me, I must run.
  3083. bForceRun = true;
  3084. }
  3085. // If we're jumping, wait until we're finished to update our goal position.
  3086. if ( GetNavigator()->GetNavType() != NAV_JUMP )
  3087. {
  3088. // Re-evaluate when you think your finished, or the target has moved too far
  3089. if ( (distance < pTask->flTaskData) || (vecGoalPos - pTarget->GetAbsOrigin()).Length() > pTask->flTaskData * 0.5 )
  3090. {
  3091. distance = ( pTarget->GetAbsOrigin() - GetLocalOrigin() ).Length2D();
  3092. if ( !GetNavigator()->UpdateGoalPos( pTarget->GetAbsOrigin() ) )
  3093. {
  3094. TaskFail( FAIL_NO_ROUTE );
  3095. break;
  3096. }
  3097. }
  3098. }
  3099. // Set the appropriate activity based on an overlapping range
  3100. // overlap the range to prevent oscillation
  3101. // BUGBUG: this is checking linear distance (ie. through walls) and not path distance or even visibility
  3102. if ( distance < pTask->flTaskData )
  3103. {
  3104. TaskComplete();
  3105. #ifndef HL2_DLL
  3106. // HL2 uses TASK_STOP_MOVING
  3107. GetNavigator()->StopMoving(); // Stop moving
  3108. #endif
  3109. }
  3110. else
  3111. {
  3112. // Pick the right movement activity.
  3113. Activity followActivity;
  3114. if( bForceRun )
  3115. {
  3116. followActivity = ACT_RUN;
  3117. }
  3118. else
  3119. {
  3120. followActivity = ( distance < 190 && m_NPCState != NPC_STATE_COMBAT ) ? ACT_WALK : ACT_RUN;
  3121. }
  3122. // Don't confuse move and shoot by resetting the activity every think
  3123. Activity curActivity = GetNavigator()->GetMovementActivity();
  3124. switch( curActivity )
  3125. {
  3126. case ACT_WALK_AIM: curActivity = ACT_WALK; break;
  3127. case ACT_RUN_AIM: curActivity = ACT_RUN; break;
  3128. }
  3129. if ( curActivity != followActivity )
  3130. {
  3131. GetNavigator()->SetMovementActivity(followActivity);
  3132. }
  3133. GetNavigator()->SetArrivalDirection( pTarget );
  3134. }
  3135. }
  3136. break;
  3137. }
  3138. case TASK_GET_PATH_TO_ENEMY_LOS:
  3139. case TASK_GET_FLANK_RADIUS_PATH_TO_ENEMY_LOS:
  3140. case TASK_GET_FLANK_ARC_PATH_TO_ENEMY_LOS:
  3141. case TASK_GET_PATH_TO_ENEMY_LKP_LOS:
  3142. {
  3143. if ( GetEnemy() == NULL )
  3144. {
  3145. TaskFail(FAIL_NO_ENEMY);
  3146. return;
  3147. }
  3148. if ( GetTaskInterrupt() > 0 )
  3149. {
  3150. ClearTaskInterrupt();
  3151. Vector vecEnemy = ( pTask->iTask == TASK_GET_PATH_TO_ENEMY_LOS ) ? GetEnemy()->GetAbsOrigin() : GetEnemyLKP();
  3152. AI_NavGoal_t goal( m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE );
  3153. GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET );
  3154. GetNavigator()->SetArrivalDirection( vecEnemy - goal.dest );
  3155. }
  3156. else
  3157. TaskInterrupt();
  3158. }
  3159. break;
  3160. case TASK_GET_PATH_AWAY_FROM_BEST_SOUND:
  3161. {
  3162. ChainRunTask( TASK_MOVE_AWAY_PATH, pTask->flTaskData );
  3163. if ( GetNavigator()->IsGoalActive() )
  3164. {
  3165. Vector vecDest = GetNavigator()->GetGoalPos();
  3166. float flDist = ( GetAbsOrigin() - vecDest ).Length();
  3167. if( flDist < 10.0 * 12.0 )
  3168. {
  3169. TaskFail("Path away from best sound too short!\n");
  3170. }
  3171. }
  3172. break;
  3173. }
  3174. case TASK_GET_PATH_OFF_OF_NPC:
  3175. {
  3176. if ( AI_IsSinglePlayer() )
  3177. {
  3178. GetNavigator()->SetAllowBigStep( UTIL_GetLocalPlayer() );
  3179. }
  3180. ChainRunTask( TASK_MOVE_AWAY_PATH, 48 );
  3181. }
  3182. break;
  3183. case TASK_MOVE_AWAY_PATH:
  3184. {
  3185. QAngle ang = GetLocalAngles();
  3186. ang.y = GetMotor()->GetIdealYaw() + 180;
  3187. Vector move;
  3188. switch ( GetTaskInterrupt() )
  3189. {
  3190. case 0:
  3191. {
  3192. if( IsPlayerAlly() )
  3193. {
  3194. // Look for a move away hint node.
  3195. CAI_Hint *pHint;
  3196. CHintCriteria hintCriteria;
  3197. hintCriteria.AddHintType( HINT_PLAYER_ALLY_MOVE_AWAY_DEST );
  3198. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  3199. hintCriteria.AddIncludePosition( GetAbsOrigin(), (20.0f * 12.0f) ); // 20 feet MAX
  3200. hintCriteria.AddExcludePosition( GetAbsOrigin(), 28.0f ); // don't plant on an hint that you start on
  3201. pHint = CAI_HintManager::FindHint( this, hintCriteria );
  3202. if( pHint )
  3203. {
  3204. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  3205. Vector vecGoal = pHint->GetAbsOrigin();
  3206. if( vecGoal.DistToSqr(GetAbsOrigin()) < vecGoal.DistToSqr(pPlayer->GetAbsOrigin()) )
  3207. {
  3208. if( GetNavigator()->SetGoal(vecGoal) )
  3209. {
  3210. pHint->DisableForSeconds( 0.1f ); // Force others to find their own.
  3211. TaskComplete();
  3212. break;
  3213. }
  3214. }
  3215. }
  3216. }
  3217. #ifdef HL2_EPISODIC
  3218. // See if we're moving away from a vehicle
  3219. CSound *pBestSound = GetBestSound( SOUND_MOVE_AWAY );
  3220. if ( pBestSound && pBestSound->m_hOwner && pBestSound->m_hOwner->GetServerVehicle() )
  3221. {
  3222. // Move away from the vehicle's center, regardless of our facing
  3223. move = ( GetAbsOrigin() - pBestSound->m_hOwner->WorldSpaceCenter() );
  3224. VectorNormalize( move );
  3225. }
  3226. else
  3227. {
  3228. // Use the first angles
  3229. AngleVectors( ang, &move );
  3230. }
  3231. #else
  3232. AngleVectors( ang, &move );
  3233. #endif //HL2_EPISODIC
  3234. if ( GetNavigator()->SetVectorGoal( move, (float)pTask->flTaskData, MIN(36,pTask->flTaskData), true ) && IsValidMoveAwayDest( GetNavigator()->GetGoalPos() ))
  3235. {
  3236. TaskComplete();
  3237. }
  3238. else
  3239. {
  3240. ang.y = GetMotor()->GetIdealYaw() + 91;
  3241. AngleVectors( ang, &move );
  3242. if ( GetNavigator()->SetVectorGoal( move, (float)pTask->flTaskData, MIN(24,pTask->flTaskData), true ) && IsValidMoveAwayDest( GetNavigator()->GetGoalPos() ) )
  3243. {
  3244. TaskComplete();
  3245. }
  3246. else
  3247. {
  3248. TaskInterrupt();
  3249. }
  3250. }
  3251. }
  3252. break;
  3253. case 1:
  3254. {
  3255. ang.y = GetMotor()->GetIdealYaw() + 271;
  3256. AngleVectors( ang, &move );
  3257. if ( GetNavigator()->SetVectorGoal( move, (float)pTask->flTaskData, MIN(24,pTask->flTaskData), true ) && IsValidMoveAwayDest( GetNavigator()->GetGoalPos() ) )
  3258. {
  3259. TaskComplete();
  3260. }
  3261. else
  3262. {
  3263. ang.y = GetMotor()->GetIdealYaw() + 180;
  3264. while (ang.y < 0)
  3265. ang.y += 360;
  3266. while (ang.y >= 360)
  3267. ang.y -= 360;
  3268. if ( ang.y < 45 || ang.y >= 315 )
  3269. ang.y = 0;
  3270. else if ( ang.y < 135 )
  3271. ang.y = 90;
  3272. else if ( ang.y < 225 )
  3273. ang.y = 180;
  3274. else
  3275. ang.y = 270;
  3276. AngleVectors( ang, &move );
  3277. if ( GetNavigator()->SetVectorGoal( move, (float)pTask->flTaskData, MIN(6,pTask->flTaskData), false ) && IsValidMoveAwayDest( GetNavigator()->GetGoalPos() ) )
  3278. {
  3279. TaskComplete();
  3280. }
  3281. else
  3282. {
  3283. TaskInterrupt();
  3284. }
  3285. }
  3286. }
  3287. break;
  3288. case 2:
  3289. {
  3290. ClearTaskInterrupt();
  3291. Vector coverPos;
  3292. if ( GetTacticalServices()->FindCoverPos( GetLocalOrigin(), EyePosition(), 0, CoverRadius(), &coverPos ) && IsValidMoveAwayDest( GetNavigator()->GetGoalPos() ) )
  3293. {
  3294. GetNavigator()->SetGoal( AI_NavGoal_t( coverPos, ACT_RUN ) );
  3295. m_flMoveWaitFinished = gpGlobals->curtime + 2;
  3296. }
  3297. else
  3298. {
  3299. // no coverwhatsoever.
  3300. TaskFail(FAIL_NO_ROUTE);
  3301. }
  3302. }
  3303. break;
  3304. }
  3305. }
  3306. break;
  3307. case TASK_WEAPON_RUN_PATH:
  3308. case TASK_ITEM_RUN_PATH:
  3309. {
  3310. CBaseEntity *pTarget = m_hTargetEnt;
  3311. if ( pTarget )
  3312. {
  3313. if ( pTarget->GetOwnerEntity() )
  3314. {
  3315. TaskFail(FAIL_WEAPON_OWNED);
  3316. }
  3317. else if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  3318. {
  3319. TaskComplete();
  3320. }
  3321. }
  3322. else
  3323. {
  3324. TaskFail(FAIL_ITEM_NO_FIND);
  3325. }
  3326. }
  3327. break;
  3328. case TASK_WAIT_FOR_MOVEMENT_STEP:
  3329. case TASK_WAIT_FOR_MOVEMENT:
  3330. {
  3331. if ( IsMovementFrozen() )
  3332. {
  3333. TaskFail(FAIL_FROZEN);
  3334. break;
  3335. }
  3336. bool fTimeExpired = ( pTask->flTaskData != 0 && pTask->flTaskData < gpGlobals->curtime - GetTimeTaskStarted() );
  3337. if (fTimeExpired || GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  3338. {
  3339. TaskComplete();
  3340. GetNavigator()->StopMoving(); // Stop moving
  3341. }
  3342. else if (!GetNavigator()->IsGoalActive())
  3343. {
  3344. SetIdealActivity( GetStoppedActivity() );
  3345. }
  3346. else
  3347. {
  3348. // Check validity of goal type
  3349. ValidateNavGoal();
  3350. }
  3351. break;
  3352. }
  3353. case TASK_DIE:
  3354. RunDieTask();
  3355. break;
  3356. case TASK_WAIT_FOR_SPEAK_FINISH:
  3357. Assert( GetExpresser() );
  3358. if ( GetExpresser() )
  3359. {
  3360. // Are we waiting for our speech to end? Or for the mutex to be free?
  3361. if ( pTask->flTaskData )
  3362. {
  3363. // Waiting for our speech to end
  3364. if ( GetExpresser()->CanSpeakAfterMyself() )
  3365. {
  3366. TaskComplete();
  3367. }
  3368. }
  3369. else
  3370. {
  3371. // Waiting for the speech & the delay afterwards
  3372. if ( !GetExpresser()->IsSpeaking() )
  3373. {
  3374. TaskComplete();
  3375. }
  3376. }
  3377. }
  3378. break;
  3379. case TASK_SCRIPT_RUN_TO_TARGET:
  3380. case TASK_SCRIPT_WALK_TO_TARGET:
  3381. case TASK_SCRIPT_CUSTOM_MOVE_TO_TARGET:
  3382. StartScriptMoveToTargetTask( pTask->iTask );
  3383. break;
  3384. case TASK_RANGE_ATTACK1:
  3385. case TASK_RANGE_ATTACK2:
  3386. case TASK_MELEE_ATTACK1:
  3387. case TASK_MELEE_ATTACK2:
  3388. case TASK_SPECIAL_ATTACK1:
  3389. case TASK_SPECIAL_ATTACK2:
  3390. case TASK_RELOAD:
  3391. RunAttackTask( pTask->iTask );
  3392. break;
  3393. case TASK_SMALL_FLINCH:
  3394. case TASK_BIG_FLINCH:
  3395. {
  3396. if ( IsActivityFinished() )
  3397. {
  3398. TaskComplete();
  3399. }
  3400. }
  3401. break;
  3402. case TASK_WAIT_FOR_SCRIPT:
  3403. {
  3404. //
  3405. // Waiting to play a script. If the script is ready, start playing the sequence.
  3406. //
  3407. if ( m_hCine && m_hCine->IsTimeToStart() )
  3408. {
  3409. TaskComplete();
  3410. m_hCine->OnBeginSequence();
  3411. // If we have an entry, we have to play it first
  3412. if ( m_hCine->m_iszEntry != NULL_STRING )
  3413. {
  3414. m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszEntry, true );
  3415. }
  3416. else
  3417. {
  3418. m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszPlay, true );
  3419. }
  3420. // StartSequence() can call CineCleanup(). If that happened, just exit schedule
  3421. if ( !m_hCine )
  3422. {
  3423. ClearSchedule( "Waiting for script, but lost script!" );
  3424. }
  3425. m_flPlaybackRate = 1.0;
  3426. //DevMsg( 2, "Script %s has begun for %s\n", STRING( m_hCine->m_iszPlay ), GetClassname() );
  3427. }
  3428. else if (!m_hCine)
  3429. {
  3430. DevMsg( "Cine died!\n");
  3431. TaskComplete();
  3432. }
  3433. else if ( IsRunningDynamicInteraction() )
  3434. {
  3435. // If we've lost our partner, abort
  3436. if ( !m_hInteractionPartner )
  3437. {
  3438. CineCleanup();
  3439. }
  3440. }
  3441. break;
  3442. }
  3443. case TASK_PLAY_SCRIPT:
  3444. {
  3445. //
  3446. // Playing a scripted sequence.
  3447. //
  3448. AutoMovement( );
  3449. if ( IsSequenceFinished() )
  3450. {
  3451. // Check to see if we are done with the action sequence.
  3452. if ( m_hCine->FinishedActionSequence( this ) )
  3453. {
  3454. // dvs: This is done in FixScriptNPCSchedule -- doing it here is too early because we still
  3455. // need to play our post-action idle sequence, which might also require FL_FLY.
  3456. //
  3457. // drop to ground if this guy is only marked "fly" because of the auto movement
  3458. /*if ( !(m_hCine->m_savedFlags & FL_FLY) )
  3459. {
  3460. if ( ( GetFlags() & FL_FLY ) && !m_hCine->m_bIgnoreGravity )
  3461. {
  3462. RemoveFlag( FL_FLY );
  3463. }
  3464. }*/
  3465. if (m_hCine)
  3466. {
  3467. m_hCine->SequenceDone( this );
  3468. }
  3469. TaskComplete();
  3470. }
  3471. else if ( m_hCine && m_hCine->m_bForceSynch )
  3472. {
  3473. m_hCine->SynchronizeSequence( this );
  3474. }
  3475. }
  3476. break;
  3477. }
  3478. case TASK_PLAY_SCRIPT_POST_IDLE:
  3479. {
  3480. if ( !m_hCine )
  3481. {
  3482. DevMsg( "Scripted sequence destroyed while in use\n" );
  3483. TaskFail( FAIL_SCHEDULE_NOT_FOUND );
  3484. break;
  3485. }
  3486. //
  3487. // Playing a scripted post idle sequence. Quit early if another sequence has grabbed the NPC.
  3488. //
  3489. if ( IsSequenceFinished() || ( m_hCine->m_hNextCine != NULL ) )
  3490. {
  3491. m_hCine->PostIdleDone( this );
  3492. }
  3493. break;
  3494. }
  3495. case TASK_ENABLE_SCRIPT:
  3496. {
  3497. if ( !m_hCine )
  3498. {
  3499. DevMsg( "Scripted sequence destroyed while in use\n" );
  3500. TaskFail( FAIL_SCHEDULE_NOT_FOUND );
  3501. break;
  3502. }
  3503. if (!m_hCine->IsWaitingForBegin())
  3504. {
  3505. m_hCine->DelayStart( false );
  3506. TaskComplete();
  3507. }
  3508. break;
  3509. }
  3510. case TASK_PLAY_SCENE:
  3511. {
  3512. if (!IsInLockedScene())
  3513. {
  3514. ClearSchedule( "Playing a scene, but not in a scene!" );
  3515. }
  3516. if (GetNavigator()->GetGoalType() != GOALTYPE_NONE)
  3517. {
  3518. TaskComplete();
  3519. }
  3520. break;
  3521. }
  3522. case TASK_RUN_PATH_FOR_UNITS:
  3523. case TASK_WALK_PATH_FOR_UNITS:
  3524. {
  3525. float distance;
  3526. distance = (m_vecLastPosition - GetLocalOrigin()).Length2D();
  3527. // Walk path until far enough away
  3528. if ( distance > pTask->flTaskData ||
  3529. GetNavigator()->GetGoalType() == GOALTYPE_NONE )
  3530. {
  3531. TaskComplete();
  3532. }
  3533. break;
  3534. }
  3535. case TASK_RUN_PATH_FLEE:
  3536. {
  3537. Vector vecDiff;
  3538. vecDiff = GetLocalOrigin() - GetNavigator()->GetGoalPos();
  3539. if( vecDiff.Length() <= pTask->flTaskData )
  3540. {
  3541. TaskComplete();
  3542. }
  3543. break;
  3544. }
  3545. case TASK_WALK_PATH_WITHIN_DIST:
  3546. case TASK_RUN_PATH_WITHIN_DIST:
  3547. {
  3548. Vector vecDiff;
  3549. vecDiff = GetLocalOrigin() - GetNavigator()->GetGoalPos();
  3550. if( vecDiff.Length() <= pTask->flTaskData )
  3551. {
  3552. TaskComplete();
  3553. }
  3554. break;
  3555. }
  3556. case TASK_WALK_PATH_TIMED:
  3557. case TASK_RUN_PATH_TIMED:
  3558. {
  3559. if ( IsWaitFinished() ||
  3560. GetNavigator()->GetGoalType() == GOALTYPE_NONE )
  3561. {
  3562. TaskComplete();
  3563. }
  3564. }
  3565. break;
  3566. case TASK_WEAPON_PICKUP:
  3567. {
  3568. if ( IsActivityFinished() )
  3569. {
  3570. CBaseCombatWeapon *pWeapon = ToBaseCombatWeapon( (CBaseEntity *)m_hTargetEnt);
  3571. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  3572. if ( !pOwner )
  3573. {
  3574. TaskComplete();
  3575. }
  3576. else
  3577. {
  3578. TaskFail(FAIL_WEAPON_OWNED);
  3579. }
  3580. }
  3581. break;
  3582. }
  3583. break;
  3584. case TASK_ITEM_PICKUP:
  3585. {
  3586. if ( IsActivityFinished() )
  3587. {
  3588. TaskComplete();
  3589. }
  3590. break;
  3591. }
  3592. break;
  3593. case TASK_FALL_TO_GROUND:
  3594. if ( GetFlags() & FL_ONGROUND )
  3595. {
  3596. TaskComplete();
  3597. }
  3598. else if( GetFlags() & FL_FLY )
  3599. {
  3600. // We're never going to fall if we're FL_FLY.
  3601. RemoveFlag( FL_FLY );
  3602. }
  3603. else
  3604. {
  3605. if( IsWaitFinished() )
  3606. {
  3607. // After 4 seconds of trying to fall to ground, Assume that we're in a bad case where the NPC
  3608. // isn't actually falling, and make an attempt to slam the ground entity to whatever's under the NPC.
  3609. Vector maxs = WorldAlignMaxs() - Vector( .1, .1, .2 );
  3610. Vector mins = WorldAlignMins() + Vector( .1, .1, 0 );
  3611. Vector vecStart = GetAbsOrigin() + Vector( 0, 0, .1 );
  3612. Vector vecDown = GetAbsOrigin();
  3613. vecDown.z -= 0.2;
  3614. trace_t trace;
  3615. m_pMoveProbe->TraceHull( vecStart, vecDown, mins, maxs, GetAITraceMask(), &trace );
  3616. if( trace.m_pEnt )
  3617. {
  3618. // Found something!
  3619. SetGroundEntity( trace.m_pEnt );
  3620. TaskComplete();
  3621. }
  3622. else
  3623. {
  3624. // Try again in a few seconds.
  3625. SetWait(4);
  3626. }
  3627. }
  3628. }
  3629. break;
  3630. case TASK_WANDER:
  3631. break;
  3632. case TASK_FREEZE:
  3633. if ( m_flFrozen < 1.0f )
  3634. {
  3635. Unfreeze();
  3636. }
  3637. break;
  3638. default:
  3639. {
  3640. DevMsg( "No RunTask entry for %s\n", TaskName( pTask->iTask ) );
  3641. TaskComplete();
  3642. }
  3643. break;
  3644. }
  3645. }
  3646. void CAI_BaseNPC::RunTaskOverlay()
  3647. {
  3648. if ( IsCurTaskContinuousMove() )
  3649. {
  3650. m_MoveAndShootOverlay.RunShootWhileMove();
  3651. }
  3652. }
  3653. void CAI_BaseNPC::EndTaskOverlay()
  3654. {
  3655. m_MoveAndShootOverlay.EndShootWhileMove();
  3656. }
  3657. //=========================================================
  3658. // SetTurnActivity - measures the difference between the way
  3659. // the NPC is facing and determines whether or not to
  3660. // select one of the 180 turn animations.
  3661. //=========================================================
  3662. void CAI_BaseNPC::SetTurnActivity ( void )
  3663. {
  3664. if ( IsCrouching() )
  3665. {
  3666. SetIdealActivity( ACT_IDLE ); // failure case
  3667. return;
  3668. }
  3669. float flYD;
  3670. flYD = GetMotor()->DeltaIdealYaw();
  3671. // FIXME: unknown case, update yaw should catch these
  3672. /*
  3673. if (GetMotor()->AddTurnGesture( flYD ))
  3674. {
  3675. SetIdealActivity( ACT_IDLE );
  3676. Remember( bits_MEMORY_TURNING );
  3677. return;
  3678. }
  3679. */
  3680. if( flYD <= -80 && flYD >= -100 && SelectWeightedSequence( ACT_90_RIGHT ) != ACTIVITY_NOT_AVAILABLE )
  3681. {
  3682. // 90 degree right.
  3683. Remember( bits_MEMORY_TURNING );
  3684. SetIdealActivity( ACT_90_RIGHT );
  3685. return;
  3686. }
  3687. if( flYD >= 80 && flYD <= 100 && SelectWeightedSequence( ACT_90_LEFT ) != ACTIVITY_NOT_AVAILABLE )
  3688. {
  3689. // 90 degree left.
  3690. Remember( bits_MEMORY_TURNING );
  3691. SetIdealActivity( ACT_90_LEFT );
  3692. return;
  3693. }
  3694. if( fabs( flYD ) >= 160 && SelectWeightedSequence ( ACT_180_LEFT ) != ACTIVITY_NOT_AVAILABLE )
  3695. {
  3696. Remember( bits_MEMORY_TURNING );
  3697. SetIdealActivity( ACT_180_LEFT );
  3698. return;
  3699. }
  3700. if ( flYD <= -45 && SelectWeightedSequence ( ACT_TURN_RIGHT ) != ACTIVITY_NOT_AVAILABLE )
  3701. {// big right turn
  3702. SetIdealActivity( ACT_TURN_RIGHT );
  3703. return;
  3704. }
  3705. if ( flYD >= 45 && SelectWeightedSequence ( ACT_TURN_LEFT ) != ACTIVITY_NOT_AVAILABLE )
  3706. {// big left turn
  3707. SetIdealActivity( ACT_TURN_LEFT );
  3708. return;
  3709. }
  3710. SetIdealActivity( ACT_IDLE ); // failure case
  3711. }
  3712. //-----------------------------------------------------------------------------
  3713. // Purpose: For a specific delta, add a turn gesture and set the yaw speed
  3714. // Input : yaw delta
  3715. //-----------------------------------------------------------------------------
  3716. bool CAI_BaseNPC::UpdateTurnGesture( void )
  3717. {
  3718. float flYD = GetMotor()->DeltaIdealYaw();
  3719. return GetMotor()->AddTurnGesture( flYD );
  3720. }
  3721. //-----------------------------------------------------------------------------
  3722. // Purpose: For non-looping animations that may be replayed sequentially (like attacks)
  3723. // Set the activity to ACT_RESET if this is a replay, otherwise just set ideal activity
  3724. // Input : newIdealActivity - desired ideal activity
  3725. //-----------------------------------------------------------------------------
  3726. void CAI_BaseNPC::ResetIdealActivity( Activity newIdealActivity )
  3727. {
  3728. if ( m_Activity == newIdealActivity )
  3729. {
  3730. m_Activity = ACT_RESET;
  3731. }
  3732. SetIdealActivity( newIdealActivity );
  3733. }
  3734. void CAI_BaseNPC::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition )
  3735. {
  3736. if ( GetNavType() == NAV_FLY )
  3737. {
  3738. // UNDONE: Cache these per enemy instead?
  3739. Vector offset = pEnemy->EyePosition() - pEnemy->GetAbsOrigin();
  3740. chasePosition += offset;
  3741. }
  3742. }
  3743. //-----------------------------------------------------------------------------
  3744. // Purpose: Returns the custom movement activity for the script that this NPC
  3745. // is running.
  3746. // Output : Returns the activity, or ACT_INVALID is the sequence is unknown.
  3747. //-----------------------------------------------------------------------------
  3748. Activity CAI_BaseNPC::GetScriptCustomMoveActivity( void )
  3749. {
  3750. Activity eActivity = ACT_WALK;
  3751. if ( ( m_hCine != NULL ) && ( m_hCine->m_iszCustomMove != NULL_STRING ) )
  3752. {
  3753. // We have a valid script. Look up the custom movement activity.
  3754. eActivity = ( Activity )LookupActivity( STRING( m_hCine->m_iszCustomMove ) );
  3755. if ( eActivity == ACT_INVALID )
  3756. {
  3757. // Not an activity, at least make sure it's a valid sequence.
  3758. if ( LookupSequence( STRING( m_hCine->m_iszCustomMove ) ) != ACT_INVALID )
  3759. {
  3760. eActivity = ACT_SCRIPT_CUSTOM_MOVE;
  3761. }
  3762. else
  3763. {
  3764. eActivity = ACT_WALK;
  3765. }
  3766. }
  3767. }
  3768. else if ( m_iszSceneCustomMoveSeq != NULL_STRING )
  3769. {
  3770. eActivity = ACT_SCRIPT_CUSTOM_MOVE;
  3771. }
  3772. return eActivity;
  3773. }
  3774. //-----------------------------------------------------------------------------
  3775. // Purpose:
  3776. // Output : int
  3777. //-----------------------------------------------------------------------------
  3778. int CAI_BaseNPC::GetScriptCustomMoveSequence( void )
  3779. {
  3780. int iSequence = ACTIVITY_NOT_AVAILABLE;
  3781. // If we have a scripted sequence entity, use it's custom move
  3782. if ( m_hCine != NULL )
  3783. {
  3784. iSequence = LookupSequence( STRING( m_hCine->m_iszCustomMove ) );
  3785. if ( iSequence == ACTIVITY_NOT_AVAILABLE )
  3786. {
  3787. DevMsg( "SCRIPT_CUSTOM_MOVE: %s has no sequence:%s\n", GetClassname(), STRING(m_hCine->m_iszCustomMove) );
  3788. }
  3789. }
  3790. else if ( m_iszSceneCustomMoveSeq != NULL_STRING )
  3791. {
  3792. // Otherwise, use the .vcd custom move
  3793. iSequence = LookupSequence( STRING( m_iszSceneCustomMoveSeq ) );
  3794. if ( iSequence == ACTIVITY_NOT_AVAILABLE )
  3795. {
  3796. Warning( "SCRIPT_CUSTOM_MOVE: %s failed scripted custom move. Has no sequence called: %s\n", GetClassname(), STRING(m_iszSceneCustomMoveSeq) );
  3797. }
  3798. }
  3799. // Failed? Use walk.
  3800. if ( iSequence == ACTIVITY_NOT_AVAILABLE )
  3801. {
  3802. iSequence = SelectWeightedSequence( ACT_WALK );
  3803. }
  3804. return iSequence;
  3805. }
  3806. //=========================================================
  3807. // GetTask - returns a pointer to the current
  3808. // scheduled task. NULL if there's a problem.
  3809. //=========================================================
  3810. const Task_t *CAI_BaseNPC::GetTask( void )
  3811. {
  3812. int iScheduleIndex = GetScheduleCurTaskIndex();
  3813. if ( !GetCurSchedule() || iScheduleIndex < 0 || iScheduleIndex >= GetCurSchedule()->NumTasks() )
  3814. // iScheduleIndex is not within valid range for the NPC's current schedule.
  3815. return NULL;
  3816. return &GetCurSchedule()->GetTaskList()[ iScheduleIndex ];
  3817. }
  3818. void CAI_BaseNPC::TranslateAddOnAttachment( char *pchAttachmentName, int iCount )
  3819. {
  3820. #ifdef HL2_DLL
  3821. if( Classify() == CLASS_ZOMBIE || ClassMatches( "npc_combine*" ) )
  3822. {
  3823. if ( Q_strcmp( pchAttachmentName, "addon_rear" ) == 0 ||
  3824. Q_strcmp( pchAttachmentName, "addon_front" ) == 0 ||
  3825. Q_strcmp( pchAttachmentName, "addon_rear_or_front" ) == 0 )
  3826. {
  3827. if ( iCount == 0 )
  3828. {
  3829. Q_strcpy( pchAttachmentName, "eyes" );
  3830. }
  3831. else
  3832. {
  3833. Q_strcpy( pchAttachmentName, "" );
  3834. }
  3835. return;
  3836. }
  3837. }
  3838. #endif
  3839. if( Q_strcmp( pchAttachmentName, "addon_baseshooter" ) == 0 )
  3840. {
  3841. switch ( iCount )
  3842. {
  3843. case 0:
  3844. Q_strcpy( pchAttachmentName, "anim_attachment_lh" );
  3845. break;
  3846. case 1:
  3847. Q_strcpy( pchAttachmentName, "anim_attachment_rh" );
  3848. break;
  3849. default:
  3850. Q_strcpy( pchAttachmentName, "" );
  3851. }
  3852. return;
  3853. }
  3854. Q_strcpy( pchAttachmentName, "" );
  3855. }
  3856. //-----------------------------------------------------------------------------
  3857. bool CAI_BaseNPC::IsInterruptable()
  3858. {
  3859. if ( GetState() == NPC_STATE_SCRIPT )
  3860. {
  3861. if ( m_hCine )
  3862. {
  3863. if (!m_hCine->CanInterrupt() )
  3864. return false;
  3865. // are the in an script FL_FLY state?
  3866. if ((GetFlags() & FL_FLY ) && !(m_hCine->m_savedFlags & FL_FLY))
  3867. {
  3868. return false;
  3869. }
  3870. }
  3871. }
  3872. return IsAlive();
  3873. }
  3874. //-----------------------------------------------------------------------------
  3875. // Purpose:
  3876. //-----------------------------------------------------------------------------
  3877. int CAI_BaseNPC::SelectInteractionSchedule( void )
  3878. {
  3879. SetTarget( m_hForcedInteractionPartner );
  3880. // If we have an interaction, we're the initiator. Move to our interaction point.
  3881. if ( m_iInteractionPlaying != NPCINT_NONE )
  3882. return SCHED_INTERACTION_MOVE_TO_PARTNER;
  3883. // Otherwise, turn towards our partner and wait for him to reach us.
  3884. //m_iInteractionState = NPCINT_MOVING_TO_MARK;
  3885. return SCHED_INTERACTION_WAIT_FOR_PARTNER;
  3886. }
  3887. //-----------------------------------------------------------------------------
  3888. // Idle schedule selection
  3889. //-----------------------------------------------------------------------------
  3890. int CAI_BaseNPC::SelectIdleSchedule()
  3891. {
  3892. if ( m_hForcedInteractionPartner )
  3893. return SelectInteractionSchedule();
  3894. int nSched = SelectFlinchSchedule();
  3895. if ( nSched != SCHED_NONE )
  3896. return nSched;
  3897. if ( HasCondition ( COND_HEAR_DANGER ) ||
  3898. HasCondition ( COND_HEAR_COMBAT ) ||
  3899. HasCondition ( COND_HEAR_WORLD ) ||
  3900. HasCondition ( COND_HEAR_BULLET_IMPACT ) ||
  3901. HasCondition ( COND_HEAR_PLAYER ) )
  3902. {
  3903. return SCHED_ALERT_FACE_BESTSOUND;
  3904. }
  3905. // no valid route!
  3906. if (GetNavigator()->GetGoalType() == GOALTYPE_NONE)
  3907. return SCHED_IDLE_STAND;
  3908. // valid route. Get moving
  3909. return SCHED_IDLE_WALK;
  3910. }
  3911. //-----------------------------------------------------------------------------
  3912. // Alert schedule selection
  3913. //-----------------------------------------------------------------------------
  3914. int CAI_BaseNPC::SelectAlertSchedule()
  3915. {
  3916. if ( m_hForcedInteractionPartner )
  3917. return SelectInteractionSchedule();
  3918. int nSched = SelectFlinchSchedule();
  3919. if ( nSched != SCHED_NONE )
  3920. return nSched;
  3921. // Scan around for new enemies
  3922. if ( HasCondition( COND_ENEMY_DEAD ) && SelectWeightedSequence( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE )
  3923. return SCHED_ALERT_SCAN;
  3924. if( IsPlayerAlly() && HasCondition(COND_HEAR_COMBAT) )
  3925. {
  3926. return SCHED_ALERT_REACT_TO_COMBAT_SOUND;
  3927. }
  3928. if ( HasCondition ( COND_HEAR_DANGER ) ||
  3929. HasCondition ( COND_HEAR_PLAYER ) ||
  3930. HasCondition ( COND_HEAR_WORLD ) ||
  3931. HasCondition ( COND_HEAR_BULLET_IMPACT ) ||
  3932. HasCondition ( COND_HEAR_COMBAT ) )
  3933. {
  3934. return SCHED_ALERT_FACE_BESTSOUND;
  3935. }
  3936. if ( gpGlobals->curtime - GetEnemies()->LastTimeSeen( AI_UNKNOWN_ENEMY ) < TIME_CARE_ABOUT_DAMAGE )
  3937. return SCHED_ALERT_FACE;
  3938. return SCHED_ALERT_STAND;
  3939. }
  3940. //-----------------------------------------------------------------------------
  3941. // Combat schedule selection
  3942. //-----------------------------------------------------------------------------
  3943. int CAI_BaseNPC::SelectCombatSchedule()
  3944. {
  3945. if ( m_hForcedInteractionPartner )
  3946. return SelectInteractionSchedule();
  3947. int nSched = SelectFlinchSchedule();
  3948. if ( nSched != SCHED_NONE )
  3949. return nSched;
  3950. if ( HasCondition(COND_NEW_ENEMY) && gpGlobals->curtime - GetEnemies()->FirstTimeSeen(GetEnemy()) < 2.0 )
  3951. {
  3952. return SCHED_WAKE_ANGRY;
  3953. }
  3954. if ( HasCondition( COND_ENEMY_DEAD ) )
  3955. {
  3956. // clear the current (dead) enemy and try to find another.
  3957. SetEnemy( NULL );
  3958. if ( ChooseEnemy() )
  3959. {
  3960. ClearCondition( COND_ENEMY_DEAD );
  3961. return SelectSchedule();
  3962. }
  3963. SetState( NPC_STATE_ALERT );
  3964. return SelectSchedule();
  3965. }
  3966. // If I'm scared of this enemy run away
  3967. if ( IRelationType( GetEnemy() ) == D_FR )
  3968. {
  3969. if (HasCondition( COND_SEE_ENEMY ) ||
  3970. HasCondition( COND_LIGHT_DAMAGE )||
  3971. HasCondition( COND_HEAVY_DAMAGE ))
  3972. {
  3973. FearSound();
  3974. //ClearCommandGoal();
  3975. return SCHED_RUN_FROM_ENEMY;
  3976. }
  3977. // If I've seen the enemy recently, cower. Ignore the time for unforgettable enemies.
  3978. AI_EnemyInfo_t *pMemory = GetEnemies()->Find( GetEnemy() );
  3979. if ( (pMemory && pMemory->bUnforgettable) || (GetEnemyLastTimeSeen() > (gpGlobals->curtime - 5.0)) )
  3980. {
  3981. // If we're facing him, just look ready. Otherwise, face him.
  3982. if ( FInAimCone( GetEnemy()->EyePosition() ) )
  3983. return SCHED_COMBAT_STAND;
  3984. return SCHED_FEAR_FACE;
  3985. }
  3986. }
  3987. // Check if need to reload
  3988. if ( HasCondition( COND_LOW_PRIMARY_AMMO ) || HasCondition( COND_NO_PRIMARY_AMMO ) )
  3989. {
  3990. return SCHED_HIDE_AND_RELOAD;
  3991. }
  3992. // Can we see the enemy?
  3993. if ( !HasCondition(COND_SEE_ENEMY) )
  3994. {
  3995. // enemy is unseen, but not occluded!
  3996. // turn to face enemy
  3997. if ( !HasCondition(COND_ENEMY_OCCLUDED) )
  3998. return SCHED_COMBAT_FACE;
  3999. // chase!
  4000. if ( GetActiveWeapon() || (CapabilitiesGet() & (bits_CAP_INNATE_RANGE_ATTACK1|bits_CAP_INNATE_RANGE_ATTACK2)))
  4001. return SCHED_ESTABLISH_LINE_OF_FIRE;
  4002. else if ( (CapabilitiesGet() & (bits_CAP_INNATE_MELEE_ATTACK1|bits_CAP_INNATE_MELEE_ATTACK2)))
  4003. return SCHED_CHASE_ENEMY;
  4004. else
  4005. return SCHED_TAKE_COVER_FROM_ENEMY;
  4006. }
  4007. if ( HasCondition(COND_TOO_CLOSE_TO_ATTACK) )
  4008. return SCHED_BACK_AWAY_FROM_ENEMY;
  4009. if ( HasCondition( COND_WEAPON_PLAYER_IN_SPREAD ) ||
  4010. HasCondition( COND_WEAPON_BLOCKED_BY_FRIEND ) ||
  4011. HasCondition( COND_WEAPON_SIGHT_OCCLUDED ) )
  4012. {
  4013. return SCHED_ESTABLISH_LINE_OF_FIRE;
  4014. }
  4015. if ( GetShotRegulator()->IsInRestInterval() )
  4016. {
  4017. if ( HasCondition(COND_CAN_RANGE_ATTACK1) )
  4018. return SCHED_COMBAT_FACE;
  4019. }
  4020. // we can see the enemy
  4021. if ( HasCondition(COND_CAN_RANGE_ATTACK1) )
  4022. {
  4023. if ( !UseAttackSquadSlots() || OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  4024. return SCHED_RANGE_ATTACK1;
  4025. return SCHED_COMBAT_FACE;
  4026. }
  4027. if ( HasCondition(COND_CAN_RANGE_ATTACK2) )
  4028. return SCHED_RANGE_ATTACK2;
  4029. if ( HasCondition(COND_CAN_MELEE_ATTACK1) )
  4030. return SCHED_MELEE_ATTACK1;
  4031. if ( HasCondition(COND_CAN_MELEE_ATTACK2) )
  4032. return SCHED_MELEE_ATTACK2;
  4033. if ( HasCondition(COND_NOT_FACING_ATTACK) )
  4034. return SCHED_COMBAT_FACE;
  4035. if ( !HasCondition(COND_CAN_RANGE_ATTACK1) && !HasCondition(COND_CAN_MELEE_ATTACK1) )
  4036. {
  4037. // if we can see enemy but can't use either attack type, we must need to get closer to enemy
  4038. if ( GetActiveWeapon() )
  4039. return SCHED_MOVE_TO_WEAPON_RANGE;
  4040. // If we have an innate attack and we're too far (or occluded) then get line of sight
  4041. if ( HasCondition( COND_TOO_FAR_TO_ATTACK ) && ( CapabilitiesGet() & (bits_CAP_INNATE_RANGE_ATTACK1|bits_CAP_INNATE_RANGE_ATTACK2)) )
  4042. return SCHED_MOVE_TO_WEAPON_RANGE;
  4043. // if we can see enemy but can't use either attack type, we must need to get closer to enemy
  4044. if ( CapabilitiesGet() & (bits_CAP_INNATE_MELEE_ATTACK1|bits_CAP_INNATE_MELEE_ATTACK2) )
  4045. return SCHED_CHASE_ENEMY;
  4046. else
  4047. return SCHED_TAKE_COVER_FROM_ENEMY;
  4048. }
  4049. DevWarning( 2, "No suitable combat schedule!\n" );
  4050. return SCHED_FAIL;
  4051. }
  4052. //-----------------------------------------------------------------------------
  4053. // Dead schedule selection
  4054. //-----------------------------------------------------------------------------
  4055. int CAI_BaseNPC::SelectDeadSchedule()
  4056. {
  4057. if ( BecomeRagdollOnClient( vec3_origin ) )
  4058. {
  4059. CleanupOnDeath();
  4060. return SCHED_DIE_RAGDOLL;
  4061. }
  4062. // Adrian - Alread dead (by animation event maybe?)
  4063. // Is it safe to set it to SCHED_NONE?
  4064. if ( m_lifeState == LIFE_DEAD )
  4065. return SCHED_NONE;
  4066. CleanupOnDeath();
  4067. return SCHED_DIE;
  4068. }
  4069. //-----------------------------------------------------------------------------
  4070. // Script schedule selection
  4071. //-----------------------------------------------------------------------------
  4072. int CAI_BaseNPC::SelectScriptSchedule()
  4073. {
  4074. Assert( m_hCine != NULL );
  4075. if ( m_hCine )
  4076. return SCHED_AISCRIPT;
  4077. DevWarning( 2, "Script failed for %s\n", GetClassname() );
  4078. CineCleanup();
  4079. return SCHED_IDLE_STAND;
  4080. }
  4081. //-----------------------------------------------------------------------------
  4082. // Purpose: Select a gesture to play in response to damage we've taken
  4083. // Output : int
  4084. //-----------------------------------------------------------------------------
  4085. void CAI_BaseNPC::PlayFlinchGesture()
  4086. {
  4087. if ( !CanFlinch() )
  4088. return;
  4089. Activity iFlinchActivity = ACT_INVALID;
  4090. float flNextFlinch = random->RandomFloat( 0.5f, 1.0f );
  4091. // If I haven't flinched for a while, play the big flinch gesture
  4092. if ( !HasMemory(bits_MEMORY_FLINCHED) )
  4093. {
  4094. iFlinchActivity = GetFlinchActivity( true, true );
  4095. if ( HaveSequenceForActivity( iFlinchActivity ) )
  4096. {
  4097. RestartGesture( iFlinchActivity );
  4098. }
  4099. Remember(bits_MEMORY_FLINCHED);
  4100. }
  4101. else
  4102. {
  4103. iFlinchActivity = GetFlinchActivity( false, true );
  4104. if ( HaveSequenceForActivity( iFlinchActivity ) )
  4105. {
  4106. RestartGesture( iFlinchActivity );
  4107. }
  4108. }
  4109. if ( iFlinchActivity != ACT_INVALID )
  4110. {
  4111. //Get the duration of the flinch and delay the next one by that (plus a bit more)
  4112. int iSequence = GetLayerSequence( FindGestureLayer( iFlinchActivity ) );
  4113. if ( iSequence != ACT_INVALID )
  4114. {
  4115. flNextFlinch += SequenceDuration( iSequence );
  4116. }
  4117. m_flNextFlinchTime = gpGlobals->curtime + flNextFlinch;
  4118. }
  4119. }
  4120. //-----------------------------------------------------------------------------
  4121. // Purpose: See if we should flinch in response to damage we've taken
  4122. // Output : int
  4123. //-----------------------------------------------------------------------------
  4124. int CAI_BaseNPC::SelectFlinchSchedule()
  4125. {
  4126. if ( !HasCondition(COND_HEAVY_DAMAGE) )
  4127. return SCHED_NONE;
  4128. // If we've flinched recently, don't do it again. A gesture flinch will be played instead.
  4129. if ( HasMemory(bits_MEMORY_FLINCHED) )
  4130. return SCHED_NONE;
  4131. if ( !CanFlinch() )
  4132. return SCHED_NONE;
  4133. // Robin: This was in the original HL1 flinch code. Do we still want it?
  4134. //if ( fabs( GetMotor()->DeltaIdealYaw() ) < (1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction
  4135. // return SCHED_TAKE_COVER_FROM_ORIGIN;
  4136. // Heavy damage. Break out of my current schedule and flinch.
  4137. Activity iFlinchActivity = GetFlinchActivity( true, false );
  4138. if ( HaveSequenceForActivity( iFlinchActivity ) )
  4139. return SCHED_BIG_FLINCH;
  4140. /*
  4141. // Not used anymore, because gesture flinches are played instead for heavy damage
  4142. // taken shortly after we've already flinched full.
  4143. //
  4144. iFlinchActivity = GetFlinchActivity( false, false );
  4145. if ( HaveSequenceForActivity( iFlinchActivity ) )
  4146. return SCHED_SMALL_FLINCH;
  4147. */
  4148. return SCHED_NONE;
  4149. }
  4150. //-----------------------------------------------------------------------------
  4151. // Purpose: Decides which type of schedule best suits the NPC's current
  4152. // state and conditions. Then calls NPC's member function to get a pointer
  4153. // to a schedule of the proper type.
  4154. //-----------------------------------------------------------------------------
  4155. int CAI_BaseNPC::SelectSchedule( void )
  4156. {
  4157. if ( HasCondition( COND_FLOATING_OFF_GROUND ) )
  4158. {
  4159. SetGravity( 1.0 );
  4160. SetGroundEntity( NULL );
  4161. return SCHED_FALL_TO_GROUND;
  4162. }
  4163. switch( m_NPCState )
  4164. {
  4165. case NPC_STATE_NONE:
  4166. DevWarning( 2, "NPC_STATE IS NONE!\n" );
  4167. break;
  4168. case NPC_STATE_PRONE:
  4169. return SCHED_IDLE_STAND;
  4170. case NPC_STATE_IDLE:
  4171. AssertMsgOnce( GetEnemy() == NULL, "NPC has enemy but is not in combat state?" );
  4172. return SelectIdleSchedule();
  4173. case NPC_STATE_ALERT:
  4174. AssertMsgOnce( GetEnemy() == NULL, "NPC has enemy but is not in combat state?" );
  4175. return SelectAlertSchedule();
  4176. case NPC_STATE_COMBAT:
  4177. return SelectCombatSchedule();
  4178. case NPC_STATE_DEAD:
  4179. return SelectDeadSchedule();
  4180. case NPC_STATE_SCRIPT:
  4181. return SelectScriptSchedule();
  4182. default:
  4183. DevWarning( 2, "Invalid State for SelectSchedule!\n" );
  4184. break;
  4185. }
  4186. return SCHED_FAIL;
  4187. }
  4188. //-----------------------------------------------------------------------------
  4189. int CAI_BaseNPC::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  4190. {
  4191. return ( m_failSchedule != SCHED_NONE ) ? m_failSchedule : SCHED_FAIL;
  4192. }