Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1746 lines
48 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_agent.h"
  8. #include "datacache/imdlcache.h"
  9. #include "isaverestore.h"
  10. #include "game.h"
  11. #include "env_debughistory.h"
  12. #include "checksum_crc.h"
  13. #include "IEffects.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. //-----------------------------------------------------------------------------
  17. //
  18. // Crude frame timings
  19. //
  20. extern CFastTimer g_AIRunTimer;
  21. extern CFastTimer g_AIPostRunTimer;
  22. extern CFastTimer g_AIConditionsTimer;
  23. extern CFastTimer g_AIPrescheduleThinkTimer;
  24. extern CFastTimer g_AIMaintainScheduleTimer;
  25. //-----------------------------------------------------------------------------
  26. //-----------------------------------------------------------------------------
  27. // ================================================================
  28. // Init static data
  29. // ================================================================
  30. CAI_ClassScheduleIdSpace CAI_Agent::gm_ClassScheduleIdSpace( true );
  31. CAI_GlobalScheduleNamespace CAI_Agent::gm_SchedulingSymbols;
  32. // ================================================================
  33. // Class Methods
  34. // ================================================================
  35. //---------------------------------------------------------
  36. //---------------------------------------------------------
  37. #define InterruptFromCondition( iCondition ) \
  38. AI_RemapFromGlobal( ( AI_IdIsLocal( iCondition ) ? GetClassScheduleIdSpace()->ConditionLocalToGlobal( iCondition ) : iCondition ) )
  39. void CAI_Agent::SetCondition( int iCondition )
  40. {
  41. int interrupt = InterruptFromCondition( iCondition );
  42. if ( interrupt == -1 )
  43. {
  44. Assert(0);
  45. return;
  46. }
  47. m_Conditions.Set( interrupt );
  48. }
  49. //---------------------------------------------------------
  50. //---------------------------------------------------------
  51. bool CAI_Agent::HasCondition( int iCondition )
  52. {
  53. int interrupt = InterruptFromCondition( iCondition );
  54. if ( interrupt == -1 )
  55. {
  56. Assert(0);
  57. return false;
  58. }
  59. bool bReturn = m_Conditions.IsBitSet(interrupt);
  60. return (bReturn);
  61. }
  62. //---------------------------------------------------------
  63. //---------------------------------------------------------
  64. bool CAI_Agent::HasCondition( int iCondition, bool bUseIgnoreConditions )
  65. {
  66. if ( bUseIgnoreConditions )
  67. return HasCondition( iCondition );
  68. int interrupt = InterruptFromCondition( iCondition );
  69. if ( interrupt == -1 )
  70. {
  71. Assert(0);
  72. return false;
  73. }
  74. bool bReturn = m_ConditionsPreIgnore.IsBitSet(interrupt);
  75. return (bReturn);
  76. }
  77. //---------------------------------------------------------
  78. //---------------------------------------------------------
  79. void CAI_Agent::ClearCondition( int iCondition )
  80. {
  81. int interrupt = InterruptFromCondition( iCondition );
  82. if ( interrupt == -1 )
  83. {
  84. Assert(0);
  85. return;
  86. }
  87. m_Conditions.Clear(interrupt);
  88. }
  89. //---------------------------------------------------------
  90. //---------------------------------------------------------
  91. void CAI_Agent::ClearConditions( int *pConditions, int nConditions )
  92. {
  93. for ( int i = 0; i < nConditions; ++i )
  94. {
  95. int iCondition = pConditions[i];
  96. int interrupt = InterruptFromCondition( iCondition );
  97. if ( interrupt == -1 )
  98. {
  99. Assert(0);
  100. continue;
  101. }
  102. m_Conditions.Clear( interrupt );
  103. }
  104. }
  105. //---------------------------------------------------------
  106. //---------------------------------------------------------
  107. void CAI_Agent::SetIgnoreConditions( int *pConditions, int nConditions )
  108. {
  109. for ( int i = 0; i < nConditions; ++i )
  110. {
  111. int iCondition = pConditions[i];
  112. int interrupt = InterruptFromCondition( iCondition );
  113. if ( interrupt == -1 )
  114. {
  115. Assert(0);
  116. continue;
  117. }
  118. m_InverseIgnoreConditions.Clear( interrupt ); // clear means ignore
  119. }
  120. }
  121. void CAI_Agent::ClearIgnoreConditions( int *pConditions, int nConditions )
  122. {
  123. for ( int i = 0; i < nConditions; ++i )
  124. {
  125. int iCondition = pConditions[i];
  126. int interrupt = InterruptFromCondition( iCondition );
  127. if ( interrupt == -1 )
  128. {
  129. Assert(0);
  130. continue;
  131. }
  132. m_InverseIgnoreConditions.Set( interrupt ); // set means don't ignore
  133. }
  134. }
  135. //---------------------------------------------------------
  136. //---------------------------------------------------------
  137. bool CAI_Agent::HasInterruptCondition( int iCondition )
  138. {
  139. if( !GetCurSchedule() )
  140. {
  141. return false;
  142. }
  143. int interrupt = InterruptFromCondition( iCondition );
  144. if ( interrupt == -1 )
  145. {
  146. Assert(0);
  147. return false;
  148. }
  149. return ( m_Conditions.IsBitSet( interrupt ) && GetCurSchedule()->HasInterrupt( interrupt ) );
  150. }
  151. //---------------------------------------------------------
  152. //---------------------------------------------------------
  153. bool CAI_Agent::ConditionInterruptsCurSchedule( int iCondition )
  154. {
  155. if( !GetCurSchedule() )
  156. {
  157. return false;
  158. }
  159. int interrupt = InterruptFromCondition( iCondition );
  160. if ( interrupt == -1 )
  161. {
  162. Assert(0);
  163. return false;
  164. }
  165. return ( GetCurSchedule()->HasInterrupt( interrupt ) );
  166. }
  167. //---------------------------------------------------------
  168. //---------------------------------------------------------
  169. bool CAI_Agent::ConditionInterruptsSchedule( int localScheduleID, int iCondition )
  170. {
  171. CAI_Schedule *pSchedule = GetSchedule( localScheduleID );
  172. if ( !pSchedule )
  173. return false;
  174. int interrupt = InterruptFromCondition( iCondition );
  175. if ( interrupt == -1 )
  176. {
  177. Assert(0);
  178. return false;
  179. }
  180. return ( pSchedule->HasInterrupt( interrupt ) );
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Returns whether we currently have any interrupt conditions that would
  184. // interrupt the given schedule.
  185. //-----------------------------------------------------------------------------
  186. bool CAI_Agent::HasConditionsToInterruptSchedule( int nLocalScheduleID )
  187. {
  188. CAI_Schedule *pSchedule = GetSchedule( nLocalScheduleID );
  189. if ( !pSchedule )
  190. return false;
  191. CAI_ScheduleBits bitsMask;
  192. pSchedule->GetInterruptMask( &bitsMask );
  193. CAI_ScheduleBits bitsOut;
  194. AccessConditionBits().And( bitsMask, &bitsOut );
  195. return !bitsOut.IsAllClear();
  196. }
  197. //-----------------------------------------------------------------------------
  198. //-----------------------------------------------------------------------------
  199. bool CAI_Agent::IsCustomInterruptConditionSet( int nCondition )
  200. {
  201. int interrupt = InterruptFromCondition( nCondition );
  202. if ( interrupt == -1 )
  203. {
  204. Assert(0);
  205. return false;
  206. }
  207. return m_CustomInterruptConditions.IsBitSet( interrupt );
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Sets a flag in the custom interrupt flags, translating the condition
  211. // to the proper global space, if necessary
  212. //-----------------------------------------------------------------------------
  213. void CAI_Agent::SetCustomInterruptCondition( int nCondition )
  214. {
  215. int interrupt = InterruptFromCondition( nCondition );
  216. if ( interrupt == -1 )
  217. {
  218. Assert(0);
  219. return;
  220. }
  221. m_CustomInterruptConditions.Set( interrupt );
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose: Clears a flag in the custom interrupt flags, translating the condition
  225. // to the proper global space, if necessary
  226. //-----------------------------------------------------------------------------
  227. void CAI_Agent::ClearCustomInterruptCondition( int nCondition )
  228. {
  229. int interrupt = InterruptFromCondition( nCondition );
  230. if ( interrupt == -1 )
  231. {
  232. Assert(0);
  233. return;
  234. }
  235. m_CustomInterruptConditions.Clear( interrupt );
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: Clears all the custom interrupt flags.
  239. //-----------------------------------------------------------------------------
  240. void CAI_Agent::ClearCustomInterruptConditions()
  241. {
  242. m_CustomInterruptConditions.ClearAll();
  243. }
  244. //-----------------------------------------------------------------------------
  245. bool CAI_Agent::PreThink( void )
  246. {
  247. return true;
  248. }
  249. //-----------------------------------------------------------------------------
  250. // NPC Think - calls out to core AI functions and handles this
  251. // npc's specific animation events
  252. //
  253. void CAI_Agent::Think( void )
  254. {
  255. if ( PreThink() )
  256. {
  257. RunAI();
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose: Virtual function that allows us to have any npc ignore a set of
  262. // shared conditions.
  263. //
  264. //-----------------------------------------------------------------------------
  265. void CAI_Agent::RemoveIgnoredConditions( void )
  266. {
  267. m_ConditionsPreIgnore = m_Conditions;
  268. m_Conditions.And( m_InverseIgnoreConditions, &m_Conditions );
  269. }
  270. //-----------------------------------------------------------------------------
  271. void CAI_Agent::GatherConditions( void )
  272. {
  273. m_bConditionsGathered = true;
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose:
  277. //-----------------------------------------------------------------------------
  278. void CAI_Agent::PrescheduleThink( void )
  279. {
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Main entry point for processing AI
  283. //-----------------------------------------------------------------------------
  284. void CAI_Agent::RunAI( void )
  285. {
  286. AI_PROFILE_SCOPE(CAI_Agent_RunAI);
  287. g_AIRunTimer.Start();
  288. m_bConditionsGathered = false;
  289. AI_PROFILE_SCOPE_BEGIN(CAI_Agent_RunAI_GatherConditions);
  290. GatherConditions();
  291. RemoveIgnoredConditions();
  292. AI_PROFILE_SCOPE_END();
  293. if ( !m_bConditionsGathered )
  294. m_bConditionsGathered = true; // derived class didn't call to base
  295. g_AIPrescheduleThinkTimer.Start();
  296. AI_PROFILE_SCOPE_BEGIN(CAI_RunAI_PrescheduleThink);
  297. PrescheduleThink();
  298. AI_PROFILE_SCOPE_END();
  299. g_AIPrescheduleThinkTimer.End();
  300. MaintainSchedule();
  301. PostscheduleThink();
  302. ClearTransientConditions();
  303. g_AIRunTimer.End();
  304. }
  305. //-----------------------------------------------------------------------------
  306. void CAI_Agent::ClearTransientConditions()
  307. {
  308. }
  309. //=========================================================
  310. // NPCInit - after a npc is spawned, it needs to
  311. // be dropped into the world, checked for mobility problems,
  312. // and put on the proper path, if any. This function does
  313. // all of those things after the npc spawns. Any
  314. // initialization that should take place for all npcs
  315. // goes here.
  316. //=========================================================
  317. void CAI_Agent::Init( void )
  318. {
  319. // Clear conditions
  320. m_Conditions.ClearAll();
  321. // NOTE: Can't call NPC Init Think directly... logic changed about
  322. // what time it is when worldspawn happens..
  323. // We must put off the rest of our initialization
  324. // until we're sure everything else has had a chance to spawn. Otherwise
  325. // we may try to reference entities that haven't spawned yet.(sjb)
  326. ForceGatherConditions();
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. //-----------------------------------------------------------------------------
  331. void CAI_Agent::TaskComplete( bool fIgnoreSetFailedCondition )
  332. {
  333. // EndTaskOverlay();
  334. // Handy thing to use for debugging
  335. //if (IsCurSchedule(SCHED_PUT_HERE) &&
  336. // GetTask()->iTask == TASK_PUT_HERE)
  337. //{
  338. // int put_breakpoint_here = 5;
  339. //}
  340. if ( fIgnoreSetFailedCondition || !HasCondition(COND_TASK_FAILED) )
  341. {
  342. SetTaskStatus( TASKSTATUS_COMPLETE );
  343. }
  344. }
  345. void CAI_Agent::TaskMovementComplete( void )
  346. {
  347. switch( GetTaskStatus() )
  348. {
  349. case TASKSTATUS_NEW:
  350. case TASKSTATUS_RUN_MOVE_AND_TASK:
  351. SetTaskStatus( TASKSTATUS_RUN_TASK );
  352. break;
  353. case TASKSTATUS_RUN_MOVE:
  354. TaskComplete();
  355. break;
  356. case TASKSTATUS_RUN_TASK:
  357. // FIXME: find out how to safely restart movement
  358. //Warning( "Movement completed twice!\n" );
  359. //Assert( 0 );
  360. break;
  361. case TASKSTATUS_COMPLETE:
  362. break;
  363. }
  364. }
  365. int CAI_Agent::TaskIsRunning( void )
  366. {
  367. if ( GetTaskStatus() != TASKSTATUS_COMPLETE &&
  368. GetTaskStatus() != TASKSTATUS_RUN_MOVE )
  369. return 1;
  370. return 0;
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. // Input :
  375. // Output :
  376. //-----------------------------------------------------------------------------
  377. void CAI_Agent::TaskFail( AI_TaskFailureCode_t code )
  378. {
  379. // EndTaskOverlay();
  380. // Handy tool for debugging
  381. //if (IsCurSchedule(SCHED_PUT_NAME_HERE))
  382. //{
  383. // int put_breakpoint_here = 5;
  384. //}
  385. // If in developer mode save the fail text for debug output
  386. if (g_pDeveloper->GetInt())
  387. {
  388. m_failText = TaskFailureToString( code );
  389. m_interuptSchedule = NULL;
  390. m_failedSchedule = GetCurSchedule();
  391. if (GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  392. {
  393. DevMsg(this, AIMF_IGNORE_SELECTED, " TaskFail -> %s\n", m_failText );
  394. }
  395. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): TaskFail -> %s\n", GetDebugName(), entindex(), m_failText ) );
  396. //AddTimedOverlay( fail_text, 5);
  397. }
  398. m_ScheduleState.taskFailureCode = code;
  399. SetCondition(COND_TASK_FAILED);
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose: Draw any debug text overlays
  403. // Input :
  404. // Output : Current text offset from the top
  405. //-----------------------------------------------------------------------------
  406. int CAI_Agent::DrawDebugTextOverlays( int text_offset )
  407. {
  408. if (GetDebugOverlayFlags() & OVERLAY_TEXT_BIT)
  409. {
  410. char tempstr[512];
  411. // --------------
  412. // Print Schedule
  413. // --------------
  414. if ( GetCurSchedule() )
  415. {
  416. const char *pName = NULL;
  417. pName = GetCurSchedule()->GetName();
  418. if ( !pName )
  419. {
  420. pName = "Unknown";
  421. }
  422. Q_snprintf(tempstr,sizeof(tempstr),"Schd: %s, ", pName );
  423. EntityText(text_offset,tempstr,0);
  424. text_offset++;
  425. if (GetDebugOverlayFlags() & OVERLAY_NPC_TASK_BIT)
  426. {
  427. for (int i = 0 ; i < GetCurSchedule()->NumTasks(); i++)
  428. {
  429. Q_snprintf(tempstr,sizeof(tempstr),"%s%s%s%s",
  430. ((i==0) ? "Task:":" "),
  431. ((i==GetScheduleCurTaskIndex()) ? "->" :" "),
  432. TaskName(GetCurSchedule()->GetTaskList()[i].iTask),
  433. ((i==GetScheduleCurTaskIndex()) ? "<-" :""));
  434. EntityText(text_offset,tempstr,0);
  435. text_offset++;
  436. }
  437. }
  438. else
  439. {
  440. const Task_t *pTask = GetTask();
  441. if ( pTask )
  442. {
  443. Q_snprintf(tempstr,sizeof(tempstr),"Task: %s (#%d), ", TaskName(pTask->iTask), GetScheduleCurTaskIndex() );
  444. }
  445. else
  446. {
  447. Q_strncpy(tempstr,"Task: None",sizeof(tempstr));
  448. }
  449. EntityText(text_offset,tempstr,0);
  450. text_offset++;
  451. }
  452. }
  453. //
  454. // Print all the current conditions.
  455. //
  456. if (GetDebugOverlayFlags() & OVERLAY_NPC_CONDITIONS_BIT)
  457. {
  458. bool bHasConditions = false;
  459. for (int i = 0; i < MAX_CONDITIONS; i++)
  460. {
  461. if (m_Conditions.IsBitSet(i))
  462. {
  463. Q_snprintf(tempstr, sizeof(tempstr), "Cond: %s\n", ConditionName(AI_RemapToGlobal(i)));
  464. EntityText(text_offset, tempstr, 0);
  465. text_offset++;
  466. bHasConditions = true;
  467. }
  468. }
  469. if (!bHasConditions)
  470. {
  471. Q_snprintf(tempstr,sizeof(tempstr),"(no conditions)");
  472. EntityText(text_offset,tempstr,0);
  473. text_offset++;
  474. }
  475. }
  476. // --------------
  477. // Print Interrupte
  478. // --------------
  479. if (m_interuptSchedule)
  480. {
  481. const char *pName = NULL;
  482. pName = m_interuptSchedule->GetName();
  483. if ( !pName )
  484. {
  485. pName = "Unknown";
  486. }
  487. Q_snprintf(tempstr,sizeof(tempstr),"Intr: %s (%s)\n", pName, m_interruptText );
  488. EntityText(text_offset,tempstr,0);
  489. text_offset++;
  490. }
  491. // --------------
  492. // Print Failure
  493. // --------------
  494. if (m_failedSchedule)
  495. {
  496. const char *pName = NULL;
  497. pName = m_failedSchedule->GetName();
  498. if ( !pName )
  499. {
  500. pName = "Unknown";
  501. }
  502. Q_snprintf(tempstr,sizeof(tempstr),"Fail: %s (%s)\n", pName,m_failText );
  503. EntityText(text_offset,tempstr,0);
  504. text_offset++;
  505. }
  506. }
  507. return text_offset;
  508. }
  509. //------------------------------------------------------------------------------
  510. // Purpose : Add new entity positioned overlay text
  511. // Input : How many lines to offset text from origin
  512. // The text to print
  513. // How long to display text
  514. // The color of the text
  515. // Output :
  516. //------------------------------------------------------------------------------
  517. void CAI_Agent::EntityText( int text_offset, const char *text, float duration, int r, int g, int b, int a )
  518. {
  519. NDebugOverlay::EntityTextAtPosition( m_vecAgentDebugOverlaysPos, text_offset, text, duration, r, g, b, a );
  520. }
  521. //=========================================================
  522. //=========================================================
  523. void CAI_Agent::OnScheduleChange ( void )
  524. {
  525. // EndTaskOverlay();
  526. }
  527. // Global Savedata for npc
  528. //
  529. // This should be an exact copy of the var's in the header. Fields
  530. // that aren't save/restored are commented out
  531. BEGIN_SIMPLE_DATADESC( CAI_Agent )
  532. // m_pSchedule (reacquired on restore)
  533. DEFINE_EMBEDDED( m_ScheduleState ),
  534. DEFINE_FIELD( m_IdealSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules
  535. DEFINE_FIELD( m_failSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules
  536. // m_Conditions (custom save)
  537. // m_CustomInterruptConditions (custom save)
  538. // m_ConditionsPreIgnore (custom save)
  539. // m_InverseIgnoreConditions (custom save)
  540. DEFINE_FIELD( m_bForceConditionsGather, FIELD_BOOLEAN ),
  541. DEFINE_FIELD( m_bConditionsGathered, FIELD_BOOLEAN ),
  542. // m_fIsUsingSmallHull TODO -- This needs more consideration than simple save/load
  543. // m_failText DEBUG
  544. // m_interruptText DEBUG
  545. // m_failedSchedule DEBUG
  546. // m_interuptSchedule DEBUG
  547. // m_nDebugCurIndex DEBUG
  548. // m_LastShootAccuracy DEBUG
  549. // m_RecentShotAccuracy DEBUG
  550. // m_TotalShots DEBUG
  551. // m_TotalHits DEBUG
  552. // m_bSelected DEBUG
  553. // m_TimeLastShotMark DEBUG
  554. // m_bDeferredNavigation
  555. END_DATADESC()
  556. BEGIN_SIMPLE_DATADESC( AIAgentScheduleState_t )
  557. DEFINE_FIELD( iCurTask, FIELD_INTEGER ),
  558. DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ),
  559. DEFINE_FIELD( timeStarted, FIELD_TIME ),
  560. DEFINE_FIELD( timeCurTaskStarted, FIELD_TIME ),
  561. DEFINE_FIELD( taskFailureCode, FIELD_INTEGER ),
  562. DEFINE_FIELD( iTaskInterrupt, FIELD_INTEGER ),
  563. DEFINE_FIELD( bScheduleWasInterrupted, FIELD_BOOLEAN ),
  564. END_DATADESC()
  565. //-----------------------------------------------------------------------------
  566. const short AI_EXTENDED_SAVE_HEADER_VERSION = 5;
  567. const short AI_EXTENDED_SAVE_HEADER_RESET_VERSION = 3;
  568. const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_CONDITIONS = 2;
  569. const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP = 3;
  570. const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SEQUENCE = 4;
  571. const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_NAVIGATOR_SAVE = 5;
  572. struct AIAgentSaveHeader_t
  573. {
  574. AIAgentSaveHeader_t()
  575. : version(AI_EXTENDED_SAVE_HEADER_VERSION),
  576. flags(0),
  577. scheduleCrc(0)
  578. {
  579. szSchedule[0] = 0;
  580. szIdealSchedule[0] = 0;
  581. szFailSchedule[0] = 0;
  582. szSequence[0] = 0;
  583. }
  584. short version;
  585. unsigned flags;
  586. char szSchedule[128];
  587. CRC32_t scheduleCrc;
  588. char szIdealSchedule[128];
  589. char szFailSchedule[128];
  590. char szSequence[128];
  591. DECLARE_SIMPLE_DATADESC();
  592. };
  593. //-------------------------------------
  594. BEGIN_SIMPLE_DATADESC( AIAgentSaveHeader_t )
  595. DEFINE_FIELD( version, FIELD_SHORT ),
  596. DEFINE_FIELD( flags, FIELD_INTEGER ),
  597. DEFINE_AUTO_ARRAY( szSchedule, FIELD_CHARACTER ),
  598. DEFINE_FIELD( scheduleCrc, FIELD_INTEGER ),
  599. DEFINE_AUTO_ARRAY( szIdealSchedule, FIELD_CHARACTER ),
  600. DEFINE_AUTO_ARRAY( szFailSchedule, FIELD_CHARACTER ),
  601. DEFINE_AUTO_ARRAY( szSequence, FIELD_CHARACTER ),
  602. END_DATADESC()
  603. //-------------------------------------
  604. int CAI_Agent::Save( ISave &save )
  605. {
  606. AIAgentSaveHeader_t saveHeader;
  607. if ( m_pSchedule )
  608. {
  609. const char *pszSchedule = m_pSchedule->GetName();
  610. Assert( Q_strlen( pszSchedule ) < sizeof( saveHeader.szSchedule ) - 1 );
  611. Q_strncpy( saveHeader.szSchedule, pszSchedule, sizeof( saveHeader.szSchedule ) );
  612. CRC32_Init( &saveHeader.scheduleCrc );
  613. CRC32_ProcessBuffer( &saveHeader.scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) );
  614. CRC32_Final( &saveHeader.scheduleCrc );
  615. }
  616. else
  617. {
  618. saveHeader.szSchedule[0] = 0;
  619. saveHeader.scheduleCrc = 0;
  620. }
  621. int idealSchedule = GetGlobalScheduleId( m_IdealSchedule );
  622. if ( idealSchedule != -1 && idealSchedule != AI_RemapToGlobal( SCHED_NONE ) )
  623. {
  624. CAI_Schedule *pIdealSchedule = GetSchedule( m_IdealSchedule );
  625. if ( pIdealSchedule )
  626. {
  627. const char *pszIdealSchedule = pIdealSchedule->GetName();
  628. Assert( Q_strlen( pszIdealSchedule ) < sizeof( saveHeader.szIdealSchedule ) - 1 );
  629. Q_strncpy( saveHeader.szIdealSchedule, pszIdealSchedule, sizeof( saveHeader.szIdealSchedule ) );
  630. }
  631. }
  632. int failSchedule = GetGlobalScheduleId( m_failSchedule );
  633. if ( failSchedule != -1 && failSchedule != AI_RemapToGlobal( SCHED_NONE ) )
  634. {
  635. CAI_Schedule *pFailSchedule = GetSchedule( m_failSchedule );
  636. if ( pFailSchedule )
  637. {
  638. const char *pszFailSchedule = pFailSchedule->GetName();
  639. Assert( Q_strlen( pszFailSchedule ) < sizeof( saveHeader.szFailSchedule ) - 1 );
  640. Q_strncpy( saveHeader.szFailSchedule, pszFailSchedule, sizeof( saveHeader.szFailSchedule ) );
  641. }
  642. }
  643. save.WriteAll( &saveHeader );
  644. save.StartBlock();
  645. SaveConditions( save, m_Conditions );
  646. SaveConditions( save, m_CustomInterruptConditions );
  647. SaveConditions( save, m_ConditionsPreIgnore );
  648. CAI_ScheduleBits ignoreConditions;
  649. m_InverseIgnoreConditions.Not( &ignoreConditions );
  650. SaveConditions( save, ignoreConditions );
  651. save.EndBlock();
  652. return 1;
  653. }
  654. //-------------------------------------
  655. void CAI_Agent::DiscardScheduleState()
  656. {
  657. // We don't save/restore schedules yet
  658. ClearSchedule( "Restoring NPC" );
  659. m_Conditions.ClearAll();
  660. }
  661. //-------------------------------------
  662. int CAI_Agent::Restore( IRestore &restore )
  663. {
  664. AIAgentSaveHeader_t saveHeader;
  665. restore.ReadAll( &saveHeader );
  666. restore.StartBlock();
  667. RestoreConditions( restore, &m_Conditions );
  668. RestoreConditions( restore, &m_CustomInterruptConditions );
  669. RestoreConditions( restore, &m_ConditionsPreIgnore );
  670. CAI_ScheduleBits ignoreConditions;
  671. RestoreConditions( restore, &ignoreConditions );
  672. ignoreConditions.Not( &m_InverseIgnoreConditions );
  673. restore.EndBlock();
  674. #ifdef TODO
  675. // do a normal restore
  676. int status = BaseClass::Restore(restore);
  677. if ( !status )
  678. return 0;
  679. #else
  680. int status = 1;
  681. #endif
  682. // Do schedule fix-up
  683. if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP )
  684. {
  685. if ( saveHeader.szIdealSchedule[0] )
  686. {
  687. CAI_Schedule *pIdealSchedule = g_AI_AgentSchedulesManager.GetScheduleByName( saveHeader.szIdealSchedule );
  688. m_IdealSchedule = ( pIdealSchedule ) ? pIdealSchedule->GetId() : SCHED_NONE;
  689. }
  690. if ( saveHeader.szFailSchedule[0] )
  691. {
  692. CAI_Schedule *pFailSchedule = g_AI_AgentSchedulesManager.GetScheduleByName( saveHeader.szFailSchedule );
  693. m_failSchedule = ( pFailSchedule ) ? pFailSchedule->GetId() : SCHED_NONE;
  694. }
  695. }
  696. bool bDiscardScheduleState = ( saveHeader.szSchedule[0] == 0 ||
  697. saveHeader.version < AI_EXTENDED_SAVE_HEADER_RESET_VERSION );
  698. if ( m_ScheduleState.taskFailureCode >= NUM_FAIL_CODES )
  699. m_ScheduleState.taskFailureCode = FAIL_NO_TARGET; // must have been a string, gotta punt
  700. if ( !bDiscardScheduleState )
  701. {
  702. m_pSchedule = g_AI_AgentSchedulesManager.GetScheduleByName( saveHeader.szSchedule );
  703. if ( m_pSchedule )
  704. {
  705. CRC32_t scheduleCrc;
  706. CRC32_Init( &scheduleCrc );
  707. CRC32_ProcessBuffer( &scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) );
  708. CRC32_Final( &scheduleCrc );
  709. if ( scheduleCrc != saveHeader.scheduleCrc )
  710. {
  711. m_pSchedule = NULL;
  712. }
  713. }
  714. }
  715. if ( !m_pSchedule )
  716. bDiscardScheduleState = true;
  717. if ( bDiscardScheduleState )
  718. {
  719. DiscardScheduleState();
  720. }
  721. return status;
  722. }
  723. //-------------------------------------
  724. void CAI_Agent::SaveConditions( ISave &save, const CAI_ScheduleBits &conditions )
  725. {
  726. for (int i = 0; i < MAX_CONDITIONS; i++)
  727. {
  728. if (conditions.IsBitSet(i))
  729. {
  730. const char *pszConditionName = ConditionName(AI_RemapToGlobal(i));
  731. if ( !pszConditionName )
  732. break;
  733. save.WriteString( pszConditionName );
  734. }
  735. }
  736. save.WriteString( "" );
  737. }
  738. //-------------------------------------
  739. void CAI_Agent::RestoreConditions( IRestore &restore, CAI_ScheduleBits *pConditions )
  740. {
  741. pConditions->ClearAll();
  742. char szCondition[256];
  743. for (;;)
  744. {
  745. restore.ReadString( szCondition, sizeof(szCondition), 0 );
  746. if ( !szCondition[0] )
  747. break;
  748. int iCondition = GetSchedulingSymbols()->ConditionSymbolToId( szCondition );
  749. if ( iCondition != -1 )
  750. pConditions->Set( AI_RemapFromGlobal( iCondition ) );
  751. }
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Purpose: Written by subclasses macro to load schedules
  755. // Input :
  756. // Output :
  757. //-----------------------------------------------------------------------------
  758. bool CAI_Agent::LoadSchedules(void)
  759. {
  760. return true;
  761. }
  762. //-----------------------------------------------------------------------------
  763. bool CAI_Agent::LoadedSchedules(void)
  764. {
  765. return true;
  766. }
  767. //-----------------------------------------------------------------------------
  768. // Purpose: Constructor
  769. // Input :
  770. // Output :
  771. //-----------------------------------------------------------------------------
  772. CAI_Agent::CAI_Agent(void)
  773. {
  774. m_pSchedule = NULL;
  775. m_IdealSchedule = SCHED_NONE;
  776. // ----------------------------
  777. // Debugging fields
  778. // ----------------------------
  779. m_interruptText = NULL;
  780. m_failText = NULL;
  781. m_failedSchedule = NULL;
  782. m_interuptSchedule = NULL;
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose: Destructor
  786. // Input :
  787. // Output :
  788. //-----------------------------------------------------------------------------
  789. CAI_Agent::~CAI_Agent(void)
  790. {
  791. }
  792. //-----------------------------------------------------------------------------
  793. #ifndef DBGFLAG_STRINGS_STRIP
  794. static void AIMsgGuts( CAI_Agent *pAI, unsigned flags, const char *pszMsg )
  795. {
  796. // int len = strlen( pszMsg );
  797. // const char *pszFmt2 = NULL;
  798. //
  799. // if ( len && pszMsg[len-1] == '\n' )
  800. // {
  801. // (const_cast<char *>(pszMsg))[len-1] = 0;
  802. // pszFmt2 = "%s (%s: %d/%s) [%d]\n";
  803. // }
  804. // else
  805. // pszFmt2 = "%s (%s: %d/%s) [%d]";
  806. //
  807. // DevMsg( pszFmt2,
  808. // pszMsg,
  809. // pAI->GetClassname(),
  810. // pAI->entindex(),
  811. // ( pAI->GetEntityName() == NULL_STRING ) ? "<unnamed>" : STRING(pAI->GetEntityName()),
  812. // gpGlobals->tickcount );
  813. }
  814. void DevMsg( CAI_Agent *pAI, unsigned flags, const char *pszFormat, ... )
  815. {
  816. if ( (flags & AIMF_IGNORE_SELECTED) || (pAI->GetDebugOverlayFlags() & OVERLAY_NPC_SELECTED_BIT) )
  817. {
  818. AIMsgGuts( pAI, flags, CFmtStr( &pszFormat ) );
  819. }
  820. }
  821. //-----------------------------------------------------------------------------
  822. void DevMsg( CAI_Agent *pAI, const char *pszFormat, ... )
  823. {
  824. if ( (pAI->GetDebugOverlayFlags() & OVERLAY_NPC_SELECTED_BIT) )
  825. {
  826. AIMsgGuts( pAI, 0, CFmtStr( &pszFormat ) );
  827. }
  828. }
  829. #endif
  830. //=========================================================
  831. // GetScheduleOfType - returns a pointer to one of the
  832. // NPC's available schedules of the indicated type.
  833. //=========================================================
  834. CAI_Schedule *CAI_Agent::GetScheduleOfType( int scheduleType )
  835. {
  836. // allow the derived classes to pick an appropriate version of this schedule or override
  837. // base schedule types.
  838. AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_TranslateSchedule);
  839. scheduleType = TranslateSchedule( scheduleType );
  840. AI_PROFILE_SCOPE_END();
  841. // Get a pointer to that schedule
  842. CAI_Schedule *schedule = GetSchedule(scheduleType);
  843. if (!schedule)
  844. {
  845. //DevMsg( "GetScheduleOfType(): No CASE for Schedule Type %d!\n", scheduleType );
  846. return GetSchedule(SCHED_NONE);
  847. }
  848. return schedule;
  849. }
  850. CAI_Schedule *CAI_Agent::GetSchedule(int schedule)
  851. {
  852. if ( schedule < NEXT_SCHEDULE )
  853. {
  854. return NULL;
  855. }
  856. if (!GetClassScheduleIdSpace()->IsGlobalBaseSet())
  857. {
  858. Warning("ERROR: %s missing schedule!\n", GetSchedulingErrorName());
  859. return g_AI_AgentSchedulesManager.GetScheduleFromID(SCHED_NONE);
  860. }
  861. if ( AI_IdIsLocal( schedule ) )
  862. {
  863. schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
  864. }
  865. return g_AI_AgentSchedulesManager.GetScheduleFromID( schedule );
  866. }
  867. bool CAI_Agent::IsCurSchedule( int schedId, bool fIdeal )
  868. {
  869. if ( !m_pSchedule )
  870. return ( schedId == SCHED_NONE || schedId == AI_RemapToGlobal(SCHED_NONE) );
  871. schedId = ( AI_IdIsLocal( schedId ) ) ? GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedId) : schedId;
  872. if ( fIdeal )
  873. return ( schedId == m_IdealSchedule );
  874. return ( m_pSchedule->GetId() == schedId );
  875. }
  876. const char* CAI_Agent::ConditionName(int conditionID)
  877. {
  878. if ( AI_IdIsLocal( conditionID ) )
  879. conditionID = GetClassScheduleIdSpace()->ConditionLocalToGlobal(conditionID);
  880. return GetSchedulingSymbols()->ConditionIdToSymbol(conditionID);
  881. }
  882. const char *CAI_Agent::TaskName(int taskID)
  883. {
  884. if ( AI_IdIsLocal( taskID ) )
  885. taskID = GetClassScheduleIdSpace()->TaskLocalToGlobal(taskID);
  886. return GetSchedulingSymbols()->TaskIdToSymbol( taskID );
  887. }
  888. extern ConVar ai_task_pre_script;
  889. extern ConVar ai_use_efficiency;
  890. extern ConVar ai_use_think_optimizations;
  891. #define ShouldUseEfficiency() ( ai_use_think_optimizations.GetBool() && ai_use_efficiency.GetBool() )
  892. extern ConVar ai_simulate_task_overtime;
  893. #define MAX_TASKS_RUN 10
  894. struct AgentTaskTimings
  895. {
  896. const char *pszTask;
  897. CFastTimer selectSchedule;
  898. CFastTimer startTimer;
  899. CFastTimer runTimer;
  900. };
  901. AgentTaskTimings g_AIAgentTaskTimings[MAX_TASKS_RUN];
  902. int g_nAIAgentTasksRun;
  903. void CAI_Agent::DumpTaskTimings()
  904. {
  905. DevMsg(" Tasks timings:\n" );
  906. for ( int i = 0; i < g_nAIAgentTasksRun; ++i )
  907. {
  908. DevMsg( " %32s -- select %5.2f, start %5.2f, run %5.2f\n", g_AIAgentTaskTimings[i].pszTask,
  909. g_AIAgentTaskTimings[i].selectSchedule.GetDuration().GetMillisecondsF(),
  910. g_AIAgentTaskTimings[i].startTimer.GetDuration().GetMillisecondsF(),
  911. g_AIAgentTaskTimings[i].runTimer.GetDuration().GetMillisecondsF() );
  912. }
  913. }
  914. //=========================================================
  915. // FHaveSchedule - Returns true if NPC's GetCurSchedule()
  916. // is anything other than NULL.
  917. //=========================================================
  918. bool CAI_Agent::FHaveSchedule( void )
  919. {
  920. if ( GetCurSchedule() == NULL )
  921. {
  922. return false;
  923. }
  924. return true;
  925. }
  926. //=========================================================
  927. // ClearSchedule - blanks out the caller's schedule pointer
  928. // and index.
  929. //=========================================================
  930. void CAI_Agent::ClearSchedule( const char *szReason )
  931. {
  932. if (szReason && GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  933. {
  934. DevMsg( this, AIMF_IGNORE_SELECTED, " Schedule cleared: %s\n", szReason );
  935. }
  936. if ( szReason )
  937. {
  938. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): Schedule cleared: %s\n", GetDebugName(), entindex(), szReason ) );
  939. }
  940. m_ScheduleState.timeCurTaskStarted = m_ScheduleState.timeStarted = 0;
  941. m_ScheduleState.bScheduleWasInterrupted = true;
  942. SetTaskStatus( TASKSTATUS_NEW );
  943. m_IdealSchedule = SCHED_NONE;
  944. m_pSchedule = NULL;
  945. ResetScheduleCurTaskIndex();
  946. m_InverseIgnoreConditions.SetAll();
  947. }
  948. //=========================================================
  949. // FScheduleDone - Returns true if the caller is on the
  950. // last task in the schedule
  951. //=========================================================
  952. bool CAI_Agent::FScheduleDone ( void )
  953. {
  954. Assert( GetCurSchedule() != NULL );
  955. if ( GetScheduleCurTaskIndex() == GetCurSchedule()->NumTasks() )
  956. {
  957. return true;
  958. }
  959. return false;
  960. }
  961. //=========================================================
  962. bool CAI_Agent::SetSchedule( int localScheduleID )
  963. {
  964. CAI_Schedule *pNewSchedule = GetScheduleOfType( localScheduleID );
  965. if ( pNewSchedule )
  966. {
  967. m_IdealSchedule = GetGlobalScheduleId( localScheduleID );
  968. SetSchedule( pNewSchedule );
  969. return true;
  970. }
  971. return false;
  972. }
  973. //=========================================================
  974. // SetSchedule - replaces the NPC's schedule pointer
  975. // with the passed pointer, and sets the ScheduleIndex back
  976. // to 0
  977. //=========================================================
  978. #define SCHEDULE_HISTORY_SIZE 10
  979. void CAI_Agent::SetSchedule( CAI_Schedule *pNewSchedule )
  980. {
  981. //Assert( pNewSchedule != NULL );
  982. m_ScheduleState.timeCurTaskStarted = m_ScheduleState.timeStarted = gpGlobals->curtime;
  983. m_ScheduleState.bScheduleWasInterrupted = false;
  984. m_pSchedule = pNewSchedule ;
  985. ResetScheduleCurTaskIndex();
  986. SetTaskStatus( TASKSTATUS_NEW );
  987. m_failSchedule = SCHED_NONE;
  988. m_Conditions.ClearAll();
  989. m_bConditionsGathered = false;
  990. m_InverseIgnoreConditions.SetAll();
  991. // this is very useful code if you can isolate a test case in a level with a single NPC. It will notify
  992. // you of every schedule selection the NPC makes.
  993. if( pNewSchedule != NULL )
  994. {
  995. if (GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  996. {
  997. DevMsg(this, AIMF_IGNORE_SELECTED, "Schedule: %s (time: %.2f)\n", pNewSchedule->GetName(), gpGlobals->curtime );
  998. }
  999. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Schedule: %s (time: %.2f)\n", GetDebugName(), entindex(), pNewSchedule->GetName(), gpGlobals->curtime ) );
  1000. }
  1001. }
  1002. //=========================================================
  1003. // NextScheduledTask - increments the ScheduleIndex
  1004. //=========================================================
  1005. void CAI_Agent::NextScheduledTask ( void )
  1006. {
  1007. Assert( GetCurSchedule() != NULL );
  1008. SetTaskStatus( TASKSTATUS_NEW );
  1009. IncScheduleCurTaskIndex();
  1010. if ( FScheduleDone() )
  1011. {
  1012. // Reset memory of failed schedule
  1013. m_failedSchedule = NULL;
  1014. m_interuptSchedule = NULL;
  1015. // just completed last task in schedule, so make it invalid by clearing it.
  1016. SetCondition( COND_SCHEDULE_DONE );
  1017. }
  1018. }
  1019. //-----------------------------------------------------------------------------
  1020. // Purpose: This function allows NPCs to modify the interrupt mask for the
  1021. // current schedule. This enables them to use base schedules but with
  1022. // different interrupt conditions. Implement this function in your
  1023. // derived class, and Set or Clear condition bits as you please.
  1024. //
  1025. // NOTE: Always call the base class in your implementation, but be
  1026. // aware of the difference between changing the bits before vs.
  1027. // changing the bits after calling the base implementation.
  1028. //
  1029. // Input : pBitString - Receives the updated interrupt mask.
  1030. //-----------------------------------------------------------------------------
  1031. void CAI_Agent::BuildScheduleTestBits( void )
  1032. {
  1033. //NOTENOTE: Always defined in the leaf classes
  1034. }
  1035. //=========================================================
  1036. // IsScheduleValid - returns true as long as the current
  1037. // schedule is still the proper schedule to be executing,
  1038. // taking into account all conditions
  1039. //=========================================================
  1040. bool CAI_Agent::IsScheduleValid()
  1041. {
  1042. if ( GetCurSchedule() == NULL || GetCurSchedule()->NumTasks() == 0 )
  1043. {
  1044. return false;
  1045. }
  1046. //Start out with the base schedule's set interrupt conditions
  1047. GetCurSchedule()->GetInterruptMask( &m_CustomInterruptConditions );
  1048. if ( !m_CustomInterruptConditions.IsBitSet( COND_NO_CUSTOM_INTERRUPTS ) )
  1049. {
  1050. BuildScheduleTestBits();
  1051. }
  1052. // This is like: m_CustomInterruptConditions &= m_Conditions;
  1053. CAI_ScheduleBits testBits;
  1054. m_CustomInterruptConditions.And( m_Conditions, &testBits );
  1055. if (!testBits.IsAllClear())
  1056. {
  1057. // If in developer mode save the interrupt text for debug output
  1058. if (g_pDeveloper->GetInt())
  1059. {
  1060. // Reset memory of failed schedule
  1061. m_failedSchedule = NULL;
  1062. m_interuptSchedule = GetCurSchedule();
  1063. // Find the first non-zero bit
  1064. for (int i=0;i<MAX_CONDITIONS;i++)
  1065. {
  1066. if (testBits.IsBitSet(i))
  1067. {
  1068. m_interruptText = ConditionName( AI_RemapToGlobal( i ) );
  1069. if (!m_interruptText)
  1070. {
  1071. m_interruptText = "(UNKNOWN CONDITION)";
  1072. /*
  1073. static const char *pError = "ERROR: Unknown condition!";
  1074. DevMsg("%s (%s)\n", pError, GetDebugName());
  1075. m_interruptText = pError;
  1076. */
  1077. }
  1078. if (GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  1079. {
  1080. DevMsg( this, AIMF_IGNORE_SELECTED, " Break condition -> %s\n", m_interruptText );
  1081. }
  1082. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Break condition -> %s\n", GetDebugName(), entindex(), m_interruptText ) );
  1083. break;
  1084. }
  1085. }
  1086. }
  1087. return false;
  1088. }
  1089. if ( HasCondition(COND_SCHEDULE_DONE) ||
  1090. HasCondition(COND_TASK_FAILED) )
  1091. {
  1092. // some condition has interrupted the schedule, or the schedule is done
  1093. return false;
  1094. }
  1095. return true;
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose: Determines whether or not SelectIdealState() should be called before
  1099. // a NPC selects a new schedule.
  1100. //
  1101. // NOTE: This logic was a source of pure, distilled trouble in Half-Life.
  1102. // If you change this function, please supply good comments.
  1103. //
  1104. // Output : Returns true if yes, false if no
  1105. //-----------------------------------------------------------------------------
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose: Returns a new schedule based on current condition bits.
  1108. //-----------------------------------------------------------------------------
  1109. CAI_Schedule *CAI_Agent::GetNewSchedule( void )
  1110. {
  1111. int scheduleType;
  1112. //
  1113. // Schedule selection code here overrides all leaf schedule selection.
  1114. //
  1115. scheduleType = SelectSchedule();
  1116. m_IdealSchedule = GetGlobalScheduleId( scheduleType );
  1117. return GetScheduleOfType( scheduleType );
  1118. }
  1119. //-----------------------------------------------------------------------------
  1120. //-----------------------------------------------------------------------------
  1121. CAI_Schedule *CAI_Agent::GetFailSchedule( void )
  1122. {
  1123. int prevSchedule;
  1124. int failedTask;
  1125. if ( GetCurSchedule() )
  1126. prevSchedule = GetLocalScheduleId( GetCurSchedule()->GetId() );
  1127. else
  1128. prevSchedule = SCHED_NONE;
  1129. const Task_t *pTask = GetTask();
  1130. if ( pTask )
  1131. failedTask = pTask->iTask;
  1132. else
  1133. failedTask = TASK_INVALID;
  1134. Assert( AI_IdIsLocal( prevSchedule ) );
  1135. Assert( AI_IdIsLocal( failedTask ) );
  1136. int scheduleType = SelectFailSchedule( prevSchedule, failedTask, m_ScheduleState.taskFailureCode );
  1137. return GetScheduleOfType( scheduleType );
  1138. }
  1139. //=========================================================
  1140. // MaintainSchedule - does all the per-think schedule maintenance.
  1141. // ensures that the NPC leaves this function with a valid
  1142. // schedule!
  1143. //=========================================================
  1144. static bool ShouldStopProcessingTasks( CAI_Agent *pNPC, int taskTime, int timeLimit )
  1145. {
  1146. #ifdef DEBUG
  1147. if( ai_simulate_task_overtime.GetBool() )
  1148. return true;
  1149. #endif
  1150. return false;
  1151. }
  1152. //-------------------------------------
  1153. void CAI_Agent::MaintainSchedule ( void )
  1154. {
  1155. AI_PROFILE_SCOPE(CAI_Agent_RunAI_MaintainSchedule);
  1156. extern CFastTimer g_AIMaintainScheduleTimer;
  1157. CTimeScope timeScope(&g_AIMaintainScheduleTimer);
  1158. //---------------------------------
  1159. CAI_Schedule *pNewSchedule;
  1160. int i;
  1161. bool runTask = true;
  1162. #if defined( VPROF_ENABLED )
  1163. #if defined(DISABLE_DEBUG_HISTORY)
  1164. bool bDebugTaskNames = ( developer.GetBool() || ( VProfAI() && g_VProfCurrentProfile.IsEnabled() ) );
  1165. #else
  1166. bool bDebugTaskNames = true;
  1167. #endif
  1168. #else
  1169. bool bDebugTaskNames = false;
  1170. #endif
  1171. memset( g_AIAgentTaskTimings, 0, sizeof(g_AIAgentTaskTimings) );
  1172. g_nAIAgentTasksRun = 0;
  1173. const int timeLimit = ( IsDebug() ) ? 16 : 8;
  1174. int taskTime = Plat_MSTime();
  1175. // // Reset this at the beginning of the frame
  1176. // Forget( bits_MEMORY_TASK_EXPENSIVE );
  1177. // UNDONE: Tune/fix this MAX_TASKS_RUN... This is just here so infinite loops are impossible
  1178. bool bStopProcessing = false;
  1179. for ( i = 0; i < MAX_TASKS_RUN && !bStopProcessing; i++ )
  1180. {
  1181. if ( GetCurSchedule() != NULL && TaskIsComplete() )
  1182. {
  1183. // Schedule is valid, so advance to the next task if the current is complete.
  1184. NextScheduledTask();
  1185. // If we finished the current schedule, clear our ignored conditions so they
  1186. // aren't applied to the next schedule selection.
  1187. if ( HasCondition( COND_SCHEDULE_DONE ) )
  1188. {
  1189. // Put our conditions back the way they were after GatherConditions,
  1190. // but add in COND_SCHEDULE_DONE.
  1191. m_Conditions = m_ConditionsPreIgnore;
  1192. SetCondition( COND_SCHEDULE_DONE );
  1193. m_InverseIgnoreConditions.SetAll();
  1194. }
  1195. }
  1196. int curTiming = g_nAIAgentTasksRun;
  1197. g_nAIAgentTasksRun++;
  1198. // validate existing schedule
  1199. if ( !IsScheduleValid() /* || m_NPCState != m_IdealNPCState */ )
  1200. {
  1201. // Notify the NPC that his schedule is changing
  1202. m_ScheduleState.bScheduleWasInterrupted = true;
  1203. OnScheduleChange();
  1204. if ( !m_bConditionsGathered )
  1205. {
  1206. // occurs if a schedule is exhausted within one think
  1207. GatherConditions();
  1208. }
  1209. //
  1210. // if ( ShouldSelectIdealState() )
  1211. // {
  1212. // NPC_STATE eIdealState = SelectIdealState();
  1213. // SetIdealState( eIdealState );
  1214. // }
  1215. if ( HasCondition( COND_TASK_FAILED ) /*&& m_NPCState == m_IdealNPCState*/ )
  1216. {
  1217. // Get a fail schedule if the previous schedule failed during execution and
  1218. // the NPC is still in its ideal state. Otherwise, the NPC would immediately
  1219. // select the same schedule again and fail again.
  1220. if (GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  1221. {
  1222. DevMsg( this, AIMF_IGNORE_SELECTED, " (failed)\n" );
  1223. }
  1224. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): (failed)\n", GetDebugName(), entindex() ) );
  1225. pNewSchedule = GetFailSchedule();
  1226. m_IdealSchedule = pNewSchedule->GetId();
  1227. DevWarning( 2, "(%s) Schedule (%s) Failed at %d!\n", STRING( GetEntityName() ), GetCurSchedule() ? GetCurSchedule()->GetName() : "GetCurSchedule() == NULL", GetScheduleCurTaskIndex() );
  1228. SetSchedule( pNewSchedule );
  1229. }
  1230. else
  1231. {
  1232. // // If the NPC is supposed to change state, it doesn't matter if the previous
  1233. // // schedule failed or completed. Changing state means selecting an entirely new schedule.
  1234. // SetState( m_IdealNPCState );
  1235. //
  1236. g_AIAgentTaskTimings[curTiming].selectSchedule.Start();
  1237. pNewSchedule = GetNewSchedule();
  1238. g_AIAgentTaskTimings[curTiming].selectSchedule.End();
  1239. SetSchedule( pNewSchedule );
  1240. }
  1241. }
  1242. if (!GetCurSchedule())
  1243. {
  1244. g_AIAgentTaskTimings[curTiming].selectSchedule.Start();
  1245. pNewSchedule = GetNewSchedule();
  1246. g_AIAgentTaskTimings[curTiming].selectSchedule.End();
  1247. if (pNewSchedule)
  1248. {
  1249. SetSchedule( pNewSchedule );
  1250. }
  1251. }
  1252. if ( !GetCurSchedule() || GetCurSchedule()->NumTasks() == 0 )
  1253. {
  1254. return;
  1255. }
  1256. AI_PROFILE_SCOPE_BEGIN_( CAI_Agent::GetSchedulingSymbols()->ScheduleIdToSymbol( GetCurSchedule()->GetId() ) );
  1257. if ( GetTaskStatus() == TASKSTATUS_NEW )
  1258. {
  1259. if ( GetScheduleCurTaskIndex() == 0 )
  1260. {
  1261. int globalId = GetCurSchedule()->GetId();
  1262. int localId = GetLocalScheduleId( globalId ); // if localId == -1, then it came from a behavior
  1263. OnStartSchedule( (localId != -1)? localId : globalId );
  1264. }
  1265. g_AIAgentTaskTimings[curTiming].startTimer.Start();
  1266. const Task_t *pTask = GetTask();
  1267. const char *pszTaskName = ( bDebugTaskNames ) ? TaskName( pTask->iTask ) : "ai_task";
  1268. Assert( pTask != NULL );
  1269. g_AIAgentTaskTimings[i].pszTask = pszTaskName;
  1270. if (GetDebugOverlayFlags() & OVERLAY_TASK_TEXT_BIT)
  1271. {
  1272. DevMsg(this, AIMF_IGNORE_SELECTED, " Task: %s\n", pszTaskName );
  1273. }
  1274. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Task: %s\n", GetDebugName(), entindex(), pszTaskName ) );
  1275. OnStartTask();
  1276. m_ScheduleState.taskFailureCode = NO_TASK_FAILURE;
  1277. m_ScheduleState.timeCurTaskStarted = gpGlobals->curtime;
  1278. AI_PROFILE_SCOPE_BEGIN_( pszTaskName );
  1279. AI_PROFILE_SCOPE_BEGIN(CAI_Agent_StartTask);
  1280. StartTask( pTask );
  1281. AI_PROFILE_SCOPE_END();
  1282. AI_PROFILE_SCOPE_END();
  1283. // if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) )
  1284. // StartTaskOverlay();
  1285. g_AIAgentTaskTimings[curTiming].startTimer.End();
  1286. // DevMsg( "%.2f StartTask( %s )\n", gpGlobals->curtime, m_pTaskSR->GetStringText( pTask->iTask ) );
  1287. }
  1288. AI_PROFILE_SCOPE_END();
  1289. // // UNDONE: Twice?!!!
  1290. // MaintainActivity();
  1291. AI_PROFILE_SCOPE_BEGIN_( CAI_Agent::GetSchedulingSymbols()->ScheduleIdToSymbol( GetCurSchedule()->GetId() ) );
  1292. if ( !TaskIsComplete() && GetTaskStatus() != TASKSTATUS_NEW )
  1293. {
  1294. if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) && runTask )
  1295. {
  1296. const Task_t *pTask = GetTask();
  1297. const char *pszTaskName = ( bDebugTaskNames ) ? TaskName( pTask->iTask ) : "ai_task";
  1298. Assert( pTask != NULL );
  1299. g_AIAgentTaskTimings[i].pszTask = pszTaskName;
  1300. // DevMsg( "%.2f RunTask( %s )\n", gpGlobals->curtime, m_pTaskSR->GetStringText( pTask->iTask ) );
  1301. g_AIAgentTaskTimings[curTiming].runTimer.Start();
  1302. AI_PROFILE_SCOPE_BEGIN_( pszTaskName );
  1303. AI_PROFILE_SCOPE_BEGIN(CAI_Agent_RunTask);
  1304. int j;
  1305. for (j = 0; j < 8; j++)
  1306. {
  1307. RunTask( pTask );
  1308. if ( GetTaskInterrupt() == 0 || TaskIsComplete() || HasCondition(COND_TASK_FAILED) )
  1309. break;
  1310. if ( ShouldUseEfficiency() && ShouldStopProcessingTasks( this, Plat_MSTime() - taskTime, timeLimit ) )
  1311. {
  1312. bStopProcessing = true;
  1313. break;
  1314. }
  1315. }
  1316. AssertMsg( j < 8, "Runaway task interrupt\n" );
  1317. AI_PROFILE_SCOPE_END();
  1318. AI_PROFILE_SCOPE_END();
  1319. if ( TaskIsRunning() && !HasCondition(COND_TASK_FAILED) )
  1320. {
  1321. // EndTaskOverlay();
  1322. //
  1323. }
  1324. g_AIAgentTaskTimings[curTiming].runTimer.End();
  1325. // don't do this again this frame
  1326. // FIXME: RunTask() should eat some of the clock, depending on what it has done
  1327. // runTask = false;
  1328. if ( !TaskIsComplete() )
  1329. {
  1330. bStopProcessing = true;
  1331. }
  1332. }
  1333. else
  1334. {
  1335. bStopProcessing = true;
  1336. }
  1337. }
  1338. AI_PROFILE_SCOPE_END();
  1339. // Decide if we should continue on this frame
  1340. if ( !bStopProcessing && ShouldStopProcessingTasks( this, Plat_MSTime() - taskTime, timeLimit ) )
  1341. bStopProcessing = true;
  1342. }
  1343. }
  1344. //-----------------------------------------------------------------------------
  1345. // Start task!
  1346. //-----------------------------------------------------------------------------
  1347. void CAI_Agent::StartTask( const Task_t *pTask )
  1348. {
  1349. switch( pTask->iTask )
  1350. {
  1351. case TASK_SET_SCHEDULE:
  1352. if ( !SetSchedule( pTask->flTaskData ) )
  1353. TaskFail(FAIL_SCHEDULE_NOT_FOUND);
  1354. break;
  1355. default:
  1356. DevMsg( "No StartTask entry for %s\n", TaskName( pTask->iTask ) );
  1357. };
  1358. }
  1359. //=========================================================
  1360. // RunTask
  1361. //=========================================================
  1362. void CAI_Agent::RunTask( const Task_t *pTask )
  1363. {
  1364. DevMsg( "No RunTask entry for %s\n", TaskName( pTask->iTask ) );
  1365. TaskComplete();
  1366. }
  1367. //=========================================================
  1368. // GetTask - returns a pointer to the current
  1369. // scheduled task. NULL if there's a problem.
  1370. //=========================================================
  1371. const Task_t *CAI_Agent::GetTask( void )
  1372. {
  1373. int iScheduleIndex = GetScheduleCurTaskIndex();
  1374. if ( !GetCurSchedule() || iScheduleIndex < 0 || iScheduleIndex >= GetCurSchedule()->NumTasks() )
  1375. // iScheduleIndex is not within valid range for the NPC's current schedule.
  1376. return NULL;
  1377. return &GetCurSchedule()->GetTaskList()[ iScheduleIndex ];
  1378. }
  1379. //-----------------------------------------------------------------------------
  1380. // Purpose: Decides which type of schedule best suits the NPC's current
  1381. // state and conditions. Then calls NPC's member function to get a pointer
  1382. // to a schedule of the proper type.
  1383. //-----------------------------------------------------------------------------
  1384. int CAI_Agent::SelectSchedule( void )
  1385. {
  1386. return SCHED_FAIL;
  1387. }
  1388. //-----------------------------------------------------------------------------
  1389. int CAI_Agent::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1390. {
  1391. return ( m_failSchedule != SCHED_NONE ) ? m_failSchedule : SCHED_FAIL;
  1392. }
  1393. void CAI_Agent::InitDefaultTaskSR(void)
  1394. {
  1395. #define ADD_DEF_TASK( name ) idSpace.AddTask(#name, name, "CAI_Agent" )
  1396. CAI_ClassScheduleIdSpace &idSpace = CAI_Agent::AccessClassScheduleIdSpaceDirect();
  1397. ADD_DEF_TASK( TASK_INVALID );
  1398. ADD_DEF_TASK( TASK_SET_SCHEDULE );
  1399. }
  1400. void CAI_Agent::InitDefaultConditionSR(void)
  1401. {
  1402. #define ADD_CONDITION_TO_SR( _n ) idSpace.AddCondition( #_n, _n, "CAI_Agent" )
  1403. CAI_ClassScheduleIdSpace &idSpace = CAI_Agent::AccessClassScheduleIdSpaceDirect();
  1404. ADD_CONDITION_TO_SR( COND_NONE );
  1405. ADD_CONDITION_TO_SR( COND_TASK_FAILED );
  1406. ADD_CONDITION_TO_SR( COND_SCHEDULE_DONE );
  1407. ADD_CONDITION_TO_SR( COND_NO_CUSTOM_INTERRUPTS ); // Don't call BuildScheduleTestBits for this schedule. Used for schedules that must strictly control their interruptibility.
  1408. }
  1409. void CAI_Agent::InitDefaultScheduleSR(void)
  1410. {
  1411. #define ADD_DEF_SCHEDULE( name, localId ) idSpace.AddSchedule(name, localId, "CAI_Agent" )
  1412. CAI_ClassScheduleIdSpace &idSpace = CAI_Agent::AccessClassScheduleIdSpaceDirect();
  1413. ADD_DEF_SCHEDULE( "SCHED_NONE", SCHED_NONE);
  1414. ADD_DEF_SCHEDULE( "SCHED_FAIL", SCHED_FAIL );
  1415. }
  1416. bool CAI_Agent::LoadDefaultSchedules(void)
  1417. {
  1418. // AI_LOAD_DEF_SCHEDULE( CAI_Agent, SCHED_NONE);
  1419. //AI_LOAD_DEF_SCHEDULE( CAI_Agent, SCHED_FAIL);
  1420. return true;
  1421. }