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

4764 lines
118 KiB

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