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.

1071 lines
30 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "isaverestore.h"
  9. #include "ai_behavior.h"
  10. #include "scripted.h"
  11. #include "env_debughistory.h"
  12. #include "saverestore_utlvector.h"
  13. #include "checksum_crc.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. bool g_bBehaviorHost_PreventBaseClassGatherConditions;
  17. //-----------------------------------------------------------------------------
  18. BEGIN_SIMPLE_DATADESC( AIChannelScheduleState_t )
  19. DEFINE_FIELD( bActive, FIELD_BOOLEAN ),
  20. // pSchedule
  21. // idealSchedule
  22. // failSchedule
  23. DEFINE_FIELD( iCurTask, FIELD_INTEGER ),
  24. DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ),
  25. DEFINE_FIELD( timeStarted, FIELD_TIME ),
  26. DEFINE_FIELD( timeCurTaskStarted, FIELD_TIME ),
  27. DEFINE_FIELD( taskFailureCode, FIELD_INTEGER ),
  28. DEFINE_FIELD( bScheduleWasInterrupted, FIELD_BOOLEAN ),
  29. END_DATADESC()
  30. //-----------------------------------------------------------------------------
  31. // CAI_BehaviorBase
  32. //-----------------------------------------------------------------------------
  33. BEGIN_DATADESC_NO_BASE( CAI_BehaviorBase )
  34. DEFINE_UTLVECTOR( m_ScheduleChannels, FIELD_EMBEDDED ),
  35. END_DATADESC()
  36. //-------------------------------------
  37. CAI_ClassScheduleIdSpace *CAI_BehaviorBase::GetClassScheduleIdSpace()
  38. {
  39. return GetOuter()->GetClassScheduleIdSpace();
  40. }
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Expose classmap of CAI_BehaviorBase that gets statically initialized
  43. // in time for use regardless of compiler whimsy. This also exposes
  44. // g_pBehaviorClasses, which allows us to inspect the classmap
  45. // in a debug session
  46. //-----------------------------------------------------------------------------
  47. static CGenericClassmap< CAI_BehaviorBase > *g_pBehaviorClasses = NULL;
  48. CGenericClassmap< CAI_BehaviorBase > *CAI_BehaviorBase::GetBehaviorClasses()
  49. {
  50. if( !g_pBehaviorClasses )
  51. {
  52. g_pBehaviorClasses = GetBehaviorClassesInternal();
  53. }
  54. return g_pBehaviorClasses;
  55. }
  56. CGenericClassmap< CAI_BehaviorBase > *CAI_BehaviorBase::GetBehaviorClassesInternal()
  57. {
  58. static CGenericClassmap< CAI_BehaviorBase > s_BehaviorClasses;
  59. return &s_BehaviorClasses;
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Draw any text overlays (override in subclass to add additional text)
  63. // Input : Previous text offset from the top
  64. // Output : Current text offset from the top
  65. //-----------------------------------------------------------------------------
  66. int CAI_BehaviorBase::DrawDebugTextOverlays( int text_offset )
  67. {
  68. char tempstr[ 512 ];
  69. if ( GetOuter()->m_debugOverlays & OVERLAY_TEXT_BIT )
  70. {
  71. Q_snprintf( tempstr, sizeof( tempstr ), "Behv: %s, ", GetName() );
  72. GetOuter()->EntityText( text_offset, tempstr, 0 );
  73. text_offset++;
  74. for ( int i = 0; i < m_ScheduleChannels.Count(); i++ )
  75. {
  76. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[i];
  77. if ( pScheduleState->bActive && pScheduleState->pSchedule )
  78. {
  79. const char *pName = NULL;
  80. pName = pScheduleState->pSchedule->GetName();
  81. if ( !pName )
  82. {
  83. pName = "Unknown";
  84. }
  85. Q_snprintf(tempstr,sizeof(tempstr)," Schd [%d]: %s, ", i, pName );
  86. GetOuter()->EntityText(text_offset,tempstr,0);
  87. text_offset++;
  88. const Task_t *pTask = GetCurTask( i );
  89. if ( pTask )
  90. {
  91. Q_snprintf(tempstr,sizeof(tempstr)," Task [%d]: %s (#%d), ", i, GetSchedulingSymbols()->TaskIdToSymbol( GetClassScheduleIdSpace()->TaskLocalToGlobal( pTask->iTask ) ), pScheduleState->iCurTask );
  92. }
  93. else
  94. {
  95. Q_snprintf(tempstr,sizeof(tempstr)," Task [%d]: None",i);
  96. }
  97. GetOuter()->EntityText(text_offset,tempstr,0);
  98. text_offset++;
  99. }
  100. }
  101. }
  102. return text_offset;
  103. }
  104. //-------------------------------------
  105. void CAI_BehaviorBase::GatherConditions()
  106. {
  107. Assert( m_pBackBridge != NULL );
  108. m_pBackBridge->BehaviorBridge_GatherConditions();
  109. }
  110. //-------------------------------------
  111. void CAI_BehaviorBase::OnStartSchedule( int scheduleType )
  112. {
  113. }
  114. //-------------------------------------
  115. int CAI_BehaviorBase::SelectSchedule()
  116. {
  117. Assert( m_pBackBridge != NULL );
  118. return m_pBackBridge->BehaviorBridge_SelectSchedule();
  119. }
  120. //-------------------------------------
  121. int CAI_BehaviorBase::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  122. {
  123. m_fOverrode = false;
  124. return SCHED_NONE;
  125. }
  126. //-------------------------------------
  127. void CAI_BehaviorBase::StartTask( const Task_t *pTask )
  128. {
  129. m_fOverrode = false;
  130. }
  131. //-------------------------------------
  132. void CAI_BehaviorBase::RunTask( const Task_t *pTask )
  133. {
  134. m_fOverrode = false;
  135. }
  136. //-------------------------------------
  137. int CAI_BehaviorBase::TranslateSchedule( int scheduleType )
  138. {
  139. Assert( m_pBackBridge != NULL );
  140. return m_pBackBridge->BehaviorBridge_TranslateSchedule( scheduleType );
  141. }
  142. //-------------------------------------
  143. CAI_Schedule *CAI_BehaviorBase::GetNewSchedule( int channel )
  144. {
  145. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  146. {
  147. AssertMsg( 0, "Bad schedule channel" );
  148. return NULL;
  149. }
  150. int scheduleType;
  151. //
  152. // Schedule selection code here overrides all leaf schedule selection.
  153. //
  154. scheduleType = SelectSchedule( channel );
  155. CAI_Schedule *pSchedule = GetSchedule( scheduleType );
  156. if( pSchedule != NULL )
  157. m_ScheduleChannels[channel].idealSchedule = pSchedule->GetId();
  158. return pSchedule;
  159. }
  160. //-------------------------------------
  161. CAI_Schedule *CAI_BehaviorBase::GetFailSchedule( AIChannelScheduleState_t *pScheduleState )
  162. {
  163. int prevSchedule;
  164. int failedTask;
  165. if ( pScheduleState->pSchedule )
  166. prevSchedule = GetClassScheduleIdSpace()->ScheduleGlobalToLocal( pScheduleState->pSchedule->GetId() );
  167. else
  168. prevSchedule = SCHED_NONE;
  169. const Task_t *pTask = GetTask( pScheduleState );
  170. if ( pTask )
  171. failedTask = pTask->iTask;
  172. else
  173. failedTask = TASK_INVALID;
  174. Assert( AI_IdIsLocal( prevSchedule ) );
  175. Assert( AI_IdIsLocal( failedTask ) );
  176. int scheduleType = SelectFailSchedule( prevSchedule, failedTask, pScheduleState->taskFailureCode );
  177. return GetSchedule( scheduleType );
  178. }
  179. //-------------------------------------
  180. CAI_Schedule *CAI_BehaviorBase::GetSchedule(int schedule)
  181. {
  182. if (!GetClassScheduleIdSpace()->IsGlobalBaseSet())
  183. {
  184. Warning("ERROR: %s missing schedule!\n", GetSchedulingErrorName());
  185. return g_AI_SchedulesManager.GetScheduleFromID(SCHED_IDLE_STAND);
  186. }
  187. if( schedule == SCHED_NONE )
  188. return NULL;
  189. if ( AI_IdIsLocal( schedule ) )
  190. {
  191. schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
  192. }
  193. if ( schedule == -1 )
  194. return NULL;
  195. return g_AI_SchedulesManager.GetScheduleFromID( schedule );
  196. }
  197. //-------------------------------------
  198. const Task_t *CAI_BehaviorBase::GetTask( AIChannelScheduleState_t *pScheduleState )
  199. {
  200. int iScheduleIndex = pScheduleState->iCurTask;
  201. if ( !pScheduleState->pSchedule || iScheduleIndex < 0 || iScheduleIndex >= pScheduleState->pSchedule->NumTasks() )
  202. // iScheduleIndex is not within valid range for the NPC's current schedule.
  203. return NULL;
  204. return &pScheduleState->pSchedule->GetTaskList()[ iScheduleIndex ];
  205. }
  206. //-------------------------------------
  207. bool CAI_BehaviorBase::IsCurSchedule( int schedule, bool fIdeal )
  208. {
  209. if ( AI_IdIsLocal( schedule ) )
  210. schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
  211. return GetOuter()->IsCurSchedule( schedule, fIdeal );
  212. }
  213. //-------------------------------------
  214. const char *CAI_BehaviorBase::GetSchedulingErrorName()
  215. {
  216. return "CAI_Behavior";
  217. }
  218. //-------------------------------------
  219. void CAI_BehaviorBase::StartChannel( int channel )
  220. {
  221. if ( channel < 0 )
  222. {
  223. AssertMsg( 0, "Bad schedule channel" );
  224. return;
  225. }
  226. m_ScheduleChannels.EnsureCount( channel + 1 );
  227. m_ScheduleChannels[channel].bActive = true;
  228. }
  229. //-------------------------------------
  230. void CAI_BehaviorBase::StopChannel( int channel )
  231. {
  232. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  233. {
  234. AssertMsg( 0, "Bad schedule channel" );
  235. return;
  236. }
  237. m_ScheduleChannels[channel].bActive = false;
  238. }
  239. //-------------------------------------
  240. void CAI_BehaviorBase::MaintainChannelSchedules()
  241. {
  242. for ( int i = 0; i < m_ScheduleChannels.Count(); i++ )
  243. {
  244. MaintainSchedule( i );
  245. }
  246. }
  247. //-------------------------------------
  248. #define MAX_TASKS_RUN 10
  249. void CAI_BehaviorBase::MaintainSchedule( int channel )
  250. {
  251. CAI_Schedule *pNewSchedule;
  252. bool runTask = true;
  253. AssertMsg( m_ScheduleChannels.IsValidIndex( channel ), "Bad schedule channel" );
  254. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  255. if ( !pScheduleState->bActive )
  256. {
  257. return;
  258. }
  259. int i;
  260. bool bStopProcessing = false;
  261. for ( i = 0; i < MAX_TASKS_RUN && !bStopProcessing; i++ )
  262. {
  263. if ( pScheduleState->pSchedule != NULL && pScheduleState->fTaskStatus == TASKSTATUS_COMPLETE )
  264. {
  265. // Schedule is valid, so advance to the next task if the current is complete.
  266. pScheduleState->fTaskStatus = TASKSTATUS_NEW;
  267. pScheduleState->iCurTask++;
  268. }
  269. // validate existing schedule
  270. if ( !IsScheduleValid( pScheduleState ) )
  271. {
  272. // Notify the NPC that his schedule is changing
  273. pScheduleState->bScheduleWasInterrupted = true;
  274. OnScheduleChange( channel );
  275. // if ( !m_bConditionsGathered )
  276. // {
  277. // // occurs if a schedule is exhausted within one think
  278. // GatherConditions();
  279. // }
  280. if ( pScheduleState->taskFailureCode != NO_TASK_FAILURE )
  281. {
  282. // Get a fail schedule if the previous schedule failed during execution and
  283. // the NPC is still in its ideal state. Otherwise, the NPC would immediately
  284. // select the same schedule again and fail again.
  285. if ( GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
  286. {
  287. DevMsg( GetOuter(), AIMF_IGNORE_SELECTED, " Behavior %s channel %d (failed)\n", GetName(), channel );
  288. }
  289. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Behavior %s channel %d (failed)\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel ) );
  290. pNewSchedule = GetFailSchedule( pScheduleState );
  291. pScheduleState->idealSchedule = pNewSchedule->GetId();
  292. DevWarning( 2, "(%s) Schedule (%s) Failed at %d!\n", STRING( GetOuter()->GetEntityName() ), pScheduleState->pSchedule ? pScheduleState->pSchedule->GetName() : "GetCurSchedule() == NULL", pScheduleState->iCurTask );
  293. SetSchedule( channel, pNewSchedule );
  294. }
  295. else
  296. {
  297. pNewSchedule = GetNewSchedule( channel );
  298. SetSchedule( channel, pNewSchedule );
  299. }
  300. }
  301. if ( !pScheduleState->pSchedule )
  302. {
  303. pNewSchedule = GetNewSchedule( channel );
  304. if (pNewSchedule)
  305. {
  306. SetSchedule( channel, pNewSchedule );
  307. }
  308. }
  309. if ( !pScheduleState->pSchedule || pScheduleState->pSchedule->NumTasks() == 0 )
  310. {
  311. return;
  312. }
  313. if ( pScheduleState->fTaskStatus == TASKSTATUS_NEW )
  314. {
  315. if ( pScheduleState->iCurTask == 0 )
  316. {
  317. int globalId = pScheduleState->pSchedule->GetId();
  318. int localId = GetClassScheduleIdSpace()->ScheduleGlobalToLocal( globalId );
  319. OnStartSchedule( channel, (localId != -1)? localId : globalId );
  320. }
  321. const Task_t *pTask = GetCurTask( channel );
  322. Assert( pTask != NULL );
  323. if ( GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
  324. {
  325. DevMsg( GetOuter(), AIMF_IGNORE_SELECTED, " Behavior %s channel %d Task: %s\n", GetName(), channel, GetSchedulingSymbols()->TaskIdToSymbol( GetClassScheduleIdSpace()->TaskLocalToGlobal( pTask->iTask ) ) );
  326. }
  327. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Behavior %s channel %d Task: %s\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel, GetSchedulingSymbols()->TaskIdToSymbol( GetClassScheduleIdSpace()->TaskLocalToGlobal( pTask->iTask ) ) ) );
  328. pScheduleState->fTaskStatus = TASKSTATUS_RUN_MOVE_AND_TASK;
  329. pScheduleState->taskFailureCode = NO_TASK_FAILURE;
  330. pScheduleState->timeCurTaskStarted = gpGlobals->curtime;
  331. StartTask( channel, pTask );
  332. }
  333. if ( !TaskIsComplete( channel ) && pScheduleState->fTaskStatus != TASKSTATUS_NEW )
  334. {
  335. if ( pScheduleState->fTaskStatus != TASKSTATUS_COMPLETE && pScheduleState->fTaskStatus != TASKSTATUS_RUN_MOVE && pScheduleState->taskFailureCode == NO_TASK_FAILURE && runTask )
  336. {
  337. const Task_t *pTask = GetCurTask( channel );
  338. Assert( pTask != NULL );
  339. RunTask( channel, pTask );
  340. if ( !TaskIsComplete( channel ) )
  341. {
  342. bStopProcessing = true;
  343. }
  344. }
  345. else
  346. {
  347. bStopProcessing = true;
  348. }
  349. }
  350. }
  351. }
  352. //-------------------------------------
  353. bool CAI_BehaviorBase::IsScheduleValid( AIChannelScheduleState_t *pScheduleState )
  354. {
  355. CAI_Schedule *pSchedule = pScheduleState->pSchedule;
  356. if ( !pSchedule || pSchedule->NumTasks() == 0 )
  357. {
  358. return false;
  359. }
  360. if ( pScheduleState->iCurTask == pSchedule->NumTasks() )
  361. {
  362. return false;
  363. }
  364. if ( pScheduleState->taskFailureCode != NO_TASK_FAILURE )
  365. {
  366. return false;
  367. }
  368. //Start out with the base schedule's set interrupt conditions
  369. CAI_ScheduleBits testBits;
  370. pSchedule->GetInterruptMask( &testBits );
  371. testBits.And( GetOuter()->AccessConditionBits(), &testBits );
  372. if (!testBits.IsAllClear())
  373. {
  374. // If in developer mode save the interrupt text for debug output
  375. if (developer.GetInt())
  376. {
  377. // Find the first non-zero bit
  378. for (int i=0;i<MAX_CONDITIONS;i++)
  379. {
  380. if (testBits.IsBitSet(i))
  381. {
  382. int channel = pScheduleState - m_ScheduleChannels.Base();
  383. const char *pszInterruptText = GetOuter()->ConditionName( AI_RemapToGlobal( i ) );
  384. if (!pszInterruptText)
  385. {
  386. pszInterruptText = "(UNKNOWN CONDITION)";
  387. }
  388. if (GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  389. {
  390. DevMsg(GetOuter(), AIMF_IGNORE_SELECTED, " Behavior %s channel %d Break condition -> %s\n", GetName(), channel, pszInterruptText );
  391. }
  392. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Behavior %s channel %d Break condition -> %s\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel, pszInterruptText ) );
  393. break;
  394. }
  395. }
  396. }
  397. return false;
  398. }
  399. return true;
  400. }
  401. //-------------------------------------
  402. void CAI_BehaviorBase::SetSchedule( int channel, CAI_Schedule *pNewSchedule )
  403. {
  404. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  405. {
  406. AssertMsg( 0, "Bad schedule channel" );
  407. return;
  408. }
  409. //Assert( pNewSchedule != NULL );
  410. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  411. pScheduleState->timeCurTaskStarted = pScheduleState->timeStarted = gpGlobals->curtime;
  412. pScheduleState->bScheduleWasInterrupted = false;
  413. pScheduleState->pSchedule = pNewSchedule ;
  414. pScheduleState->iCurTask = 0;
  415. pScheduleState->fTaskStatus = TASKSTATUS_NEW;
  416. pScheduleState->failSchedule = SCHED_NONE;
  417. // this is very useful code if you can isolate a test case in a level with a single NPC. It will notify
  418. // you of every schedule selection the NPC makes.
  419. if( pNewSchedule )
  420. {
  421. if (GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  422. {
  423. DevMsg(GetOuter(), AIMF_IGNORE_SELECTED, "Behavior %s channel %d Schedule: %s (time: %.2f)\n", GetName(), channel, pNewSchedule->GetName(), gpGlobals->curtime );
  424. }
  425. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Behavior %s channel %d Schedule: %s (time: %.2f)\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel, pNewSchedule->GetName(), gpGlobals->curtime ) );
  426. }
  427. }
  428. //-------------------------------------
  429. bool CAI_BehaviorBase::SetSchedule( int channel, int localScheduleID )
  430. {
  431. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  432. {
  433. AssertMsg( 0, "Bad schedule channel" );
  434. return false;
  435. }
  436. int translatedSchedule = TranslateSchedule( channel, localScheduleID );
  437. CAI_Schedule *pNewSchedule = GetSchedule( translatedSchedule );
  438. if ( pNewSchedule )
  439. {
  440. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  441. if ( AI_IdIsLocal( localScheduleID ) )
  442. {
  443. pScheduleState->idealSchedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal( localScheduleID );
  444. }
  445. else
  446. {
  447. pScheduleState->idealSchedule = localScheduleID;
  448. }
  449. SetSchedule( channel, pNewSchedule );
  450. return true;
  451. }
  452. return false;
  453. }
  454. //-------------------------------------
  455. void CAI_BehaviorBase::ClearSchedule( int channel, const char *szReason )
  456. {
  457. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  458. {
  459. AssertMsg( 0, "Bad schedule channel" );
  460. return;
  461. }
  462. if (szReason && GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  463. {
  464. DevMsg( GetOuter(), AIMF_IGNORE_SELECTED, " Behavior %s channel %d Schedule cleared: %s\n", GetName(), channel, szReason );
  465. }
  466. if ( szReason )
  467. {
  468. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): Behavior %s channel %d Schedule cleared: %s\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel, szReason ) );
  469. }
  470. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  471. pScheduleState->timeCurTaskStarted = pScheduleState->timeStarted = 0;
  472. pScheduleState->bScheduleWasInterrupted = true;
  473. pScheduleState->fTaskStatus = TASKSTATUS_NEW;
  474. pScheduleState->idealSchedule = AI_RemapToGlobal( SCHED_NONE );
  475. pScheduleState->pSchedule = NULL;
  476. pScheduleState->iCurTask = 0;
  477. }
  478. //-------------------------------------
  479. CAI_Schedule *CAI_BehaviorBase::GetCurSchedule( int channel )
  480. {
  481. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  482. {
  483. AssertMsg( 0, "Bad schedule channel" );
  484. return NULL;
  485. }
  486. return m_ScheduleChannels[channel].pSchedule;
  487. }
  488. //-------------------------------------
  489. bool CAI_BehaviorBase::IsCurSchedule( int channel, int schedId, bool fIdeal )
  490. {
  491. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  492. {
  493. AssertMsg( 0, "Bad schedule channel" );
  494. return false;
  495. }
  496. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  497. if ( !pScheduleState->pSchedule )
  498. return ( schedId == SCHED_NONE || schedId == AI_RemapToGlobal(SCHED_NONE) );
  499. schedId = ( AI_IdIsLocal( schedId ) ) ? GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedId) : schedId;
  500. if ( fIdeal )
  501. return ( schedId == pScheduleState->idealSchedule );
  502. return ( pScheduleState->pSchedule->GetId() == schedId );
  503. }
  504. //-------------------------------------
  505. void CAI_BehaviorBase::OnScheduleChange( int channel )
  506. {
  507. }
  508. //-------------------------------------
  509. void CAI_BehaviorBase::OnStartSchedule( int channel, int scheduleType )
  510. {
  511. }
  512. //-------------------------------------
  513. int CAI_BehaviorBase::SelectSchedule( int channel )
  514. {
  515. return SCHED_NONE;
  516. }
  517. //-------------------------------------
  518. int CAI_BehaviorBase::SelectFailSchedule( int channel, int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  519. {
  520. return SCHED_NONE;
  521. }
  522. //-------------------------------------
  523. void CAI_BehaviorBase::TaskComplete( int channel, bool fIgnoreSetFailedCondition )
  524. {
  525. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  526. {
  527. AssertMsg( 0, "Bad schedule channel" );
  528. return;
  529. }
  530. if ( fIgnoreSetFailedCondition || m_ScheduleChannels[channel].taskFailureCode == NO_TASK_FAILURE )
  531. {
  532. m_ScheduleChannels[channel].taskFailureCode = NO_TASK_FAILURE;
  533. m_ScheduleChannels[channel].fTaskStatus = TASKSTATUS_COMPLETE;
  534. }
  535. }
  536. //-------------------------------------
  537. void CAI_BehaviorBase::TaskFail( int channel, AI_TaskFailureCode_t code )
  538. {
  539. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  540. {
  541. AssertMsg( 0, "Bad schedule channel" );
  542. return;
  543. }
  544. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  545. if ( GetOuter()->m_debugOverlays & OVERLAY_TASK_TEXT_BIT)
  546. {
  547. DevMsg( GetOuter(), AIMF_IGNORE_SELECTED, " Behavior %s channel %d: TaskFail -> %s\n", GetName(), channel, TaskFailureToString( code ) );
  548. }
  549. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): Behavior %s channel %d: TaskFail -> %s\n", GetOuter()->GetDebugName(), GetOuter()->entindex(), GetName(), channel, TaskFailureToString( code ) ) );
  550. pScheduleState->taskFailureCode = code;
  551. }
  552. //-------------------------------------
  553. const Task_t *CAI_BehaviorBase::GetCurTask( int channel )
  554. {
  555. if ( !m_ScheduleChannels.IsValidIndex( channel ) )
  556. {
  557. AssertMsg( 0, "Bad schedule channel" );
  558. return NULL;
  559. }
  560. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[channel];
  561. int iScheduleIndex = pScheduleState->iCurTask;
  562. if ( !pScheduleState->pSchedule || iScheduleIndex < 0 || iScheduleIndex >= pScheduleState->pSchedule->NumTasks() )
  563. // iScheduleIndex is not within valid range for the NPC's current schedule.
  564. return NULL;
  565. return &pScheduleState->pSchedule->GetTaskList()[ iScheduleIndex ];
  566. }
  567. //-------------------------------------
  568. void CAI_BehaviorBase::StartTask( int channel, const Task_t *pTask )
  569. {
  570. Assert( 0 );
  571. }
  572. //-------------------------------------
  573. void CAI_BehaviorBase::RunTask( int channel, const Task_t *pTask )
  574. {
  575. Assert( 0 );
  576. }
  577. //-------------------------------------
  578. float CAI_BehaviorBase::GetJumpGravity() const
  579. {
  580. Assert( m_pBackBridge != NULL );
  581. return m_pBackBridge->BehaviorBridge_GetJumpGravity();
  582. }
  583. //-------------------------------------
  584. bool CAI_BehaviorBase::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const
  585. {
  586. Assert( m_pBackBridge != NULL );
  587. return m_pBackBridge->BehaviorBridge_IsJumpLegal( startPos, apex, endPos, maxUp, maxDown, maxDist );
  588. }
  589. //-------------------------------------
  590. bool CAI_BehaviorBase::MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost )
  591. {
  592. Assert( m_pBackBridge != NULL );
  593. return m_pBackBridge->BehaviorBridge_MovementCost( moveType, vecStart, vecEnd, pCost );
  594. }
  595. //-------------------------------------
  596. bool CAI_BehaviorBase::NotifyChangeBehaviorStatus( bool fCanFinishSchedule )
  597. {
  598. bool fInterrupt = GetOuter()->OnBehaviorChangeStatus( this, fCanFinishSchedule );
  599. if ( !GetOuter()->IsInterruptable())
  600. return false;
  601. if ( fInterrupt )
  602. {
  603. if ( GetOuter()->m_hCine )
  604. {
  605. if( GetOuter()->m_hCine->PlayedSequence() )
  606. {
  607. DevWarning( "NPC: %s canceled running script %s due to behavior change\n", GetOuter()->GetDebugName(), GetOuter()->m_hCine->GetDebugName() );
  608. }
  609. else
  610. {
  611. DevWarning( "NPC: %s canceled script %s without playing, due to behavior change\n", GetOuter()->GetDebugName(), GetOuter()->m_hCine->GetDebugName() );
  612. }
  613. GetOuter()->m_hCine->CancelScript();
  614. }
  615. //!!!HACKHACK
  616. // this is dirty, but it forces NPC to pick a new schedule next time through.
  617. GetOuter()->ClearSchedule( "Changed behavior status" );
  618. }
  619. return fInterrupt;
  620. }
  621. //-------------------------------------
  622. int CAI_BehaviorBase::Save( ISave &save )
  623. {
  624. if ( save.WriteAll( this, GetDataDescMap() ) )
  625. {
  626. SaveChannels( save );
  627. return 1;
  628. }
  629. return 0;
  630. }
  631. //-------------------------------------
  632. int CAI_BehaviorBase::Restore( IRestore &restore )
  633. {
  634. if ( restore.ReadAll( this, GetDataDescMap() ) )
  635. {
  636. RestoreChannels( restore );
  637. return 1;
  638. }
  639. return 0;
  640. }
  641. //-------------------------------------
  642. //-----------------------------------------------------------------------------
  643. const short AI_BEHAVIOR_CHANNEL_SAVE_HEADER_VERSION = 1;
  644. struct AIBehaviorChannelSaveHeader_t
  645. {
  646. AIBehaviorChannelSaveHeader_t()
  647. : flags(0),
  648. scheduleCrc(0)
  649. {
  650. szSchedule[0] = 0;
  651. szIdealSchedule[0] = 0;
  652. szFailSchedule[0] = 0;
  653. }
  654. unsigned flags;
  655. char szSchedule[128];
  656. CRC32_t scheduleCrc;
  657. char szIdealSchedule[128];
  658. char szFailSchedule[128];
  659. DECLARE_SIMPLE_DATADESC();
  660. };
  661. //-------------------------------------
  662. BEGIN_SIMPLE_DATADESC( AIBehaviorChannelSaveHeader_t )
  663. DEFINE_FIELD( flags, FIELD_INTEGER ),
  664. DEFINE_AUTO_ARRAY( szSchedule, FIELD_CHARACTER ),
  665. DEFINE_FIELD( scheduleCrc, FIELD_INTEGER ),
  666. DEFINE_AUTO_ARRAY( szIdealSchedule, FIELD_CHARACTER ),
  667. DEFINE_AUTO_ARRAY( szFailSchedule, FIELD_CHARACTER ),
  668. END_DATADESC()
  669. void CAI_BehaviorBase::SaveChannels( ISave &save )
  670. {
  671. AIBehaviorChannelSaveHeader_t saveHeader;
  672. if ( m_ScheduleChannels.Count() )
  673. {
  674. save.StartBlock();
  675. int version = AI_BEHAVIOR_CHANNEL_SAVE_HEADER_VERSION;
  676. save.WriteInt( &version );
  677. for ( int i = 0; i < m_ScheduleChannels.Count(); i++ )
  678. {
  679. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[i];
  680. if ( pScheduleState->pSchedule )
  681. {
  682. const char *pszSchedule = pScheduleState->pSchedule->GetName();
  683. Assert( Q_strlen( pszSchedule ) < sizeof( saveHeader.szSchedule ) - 1 );
  684. Q_strncpy( saveHeader.szSchedule, pszSchedule, sizeof( saveHeader.szSchedule ) );
  685. CRC32_Init( &saveHeader.scheduleCrc );
  686. CRC32_ProcessBuffer( &saveHeader.scheduleCrc, (void *)pScheduleState->pSchedule->GetTaskList(), pScheduleState->pSchedule->NumTasks() * sizeof(Task_t) );
  687. CRC32_Final( &saveHeader.scheduleCrc );
  688. }
  689. else
  690. {
  691. saveHeader.szSchedule[0] = 0;
  692. saveHeader.scheduleCrc = 0;
  693. }
  694. const int SCHED_NONE_GLOBAL = AI_RemapToGlobal( SCHED_NONE );
  695. int idealSchedule = ( pScheduleState->idealSchedule == SCHED_NONE ) ? SCHED_NONE_GLOBAL : pScheduleState->idealSchedule;
  696. Assert( !AI_IdIsLocal( idealSchedule ) );
  697. if ( idealSchedule != -1 && idealSchedule != SCHED_NONE_GLOBAL )
  698. {
  699. CAI_Schedule *pIdealSchedule = GetSchedule( pScheduleState->idealSchedule );
  700. if ( pIdealSchedule )
  701. {
  702. const char *pszIdealSchedule = pIdealSchedule->GetName();
  703. Assert( Q_strlen( pszIdealSchedule ) < sizeof( saveHeader.szIdealSchedule ) - 1 );
  704. Q_strncpy( saveHeader.szIdealSchedule, pszIdealSchedule, sizeof( saveHeader.szIdealSchedule ) );
  705. }
  706. }
  707. int failSchedule = ( pScheduleState->failSchedule == SCHED_NONE ) ? SCHED_NONE_GLOBAL : pScheduleState->failSchedule;
  708. Assert( !AI_IdIsLocal( failSchedule ) );
  709. if ( failSchedule != -1 && failSchedule != SCHED_NONE_GLOBAL )
  710. {
  711. CAI_Schedule *pFailSchedule = GetSchedule( pScheduleState->failSchedule );
  712. if ( pFailSchedule )
  713. {
  714. const char *pszFailSchedule = pFailSchedule->GetName();
  715. Assert( Q_strlen( pszFailSchedule ) < sizeof( saveHeader.szFailSchedule ) - 1 );
  716. Q_strncpy( saveHeader.szFailSchedule, pszFailSchedule, sizeof( saveHeader.szFailSchedule ) );
  717. }
  718. }
  719. save.WriteAll( &saveHeader );
  720. }
  721. save.EndBlock();
  722. }
  723. }
  724. //-------------------------------------
  725. void CAI_BehaviorBase::RestoreChannels( IRestore &restore )
  726. {
  727. if ( m_ScheduleChannels.Count() )
  728. {
  729. restore.StartBlock();
  730. int version;
  731. restore.ReadInt( &version );
  732. AIBehaviorChannelSaveHeader_t saveHeader;
  733. for ( int i = 0; i < m_ScheduleChannels.Count(); i++ )
  734. {
  735. restore.ReadAll( &saveHeader );
  736. AIChannelScheduleState_t *pScheduleState = &m_ScheduleChannels[i];
  737. // Do schedule fix-up
  738. if ( saveHeader.szIdealSchedule[0] )
  739. {
  740. CAI_Schedule *pIdealSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szIdealSchedule );
  741. pScheduleState->idealSchedule = ( pIdealSchedule ) ? pIdealSchedule->GetId() : SCHED_NONE;
  742. }
  743. if ( saveHeader.szFailSchedule[0] )
  744. {
  745. CAI_Schedule *pFailSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szFailSchedule );
  746. pScheduleState->failSchedule = ( pFailSchedule ) ? pFailSchedule->GetId() : SCHED_NONE;
  747. }
  748. bool bDiscardScheduleState = ( saveHeader.szSchedule[0] == 0 );
  749. if ( pScheduleState->taskFailureCode >= NUM_FAIL_CODES )
  750. pScheduleState->taskFailureCode = FAIL_NO_TARGET; // must have been a string, gotta punt
  751. if ( !bDiscardScheduleState )
  752. {
  753. pScheduleState->pSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szSchedule );
  754. if ( pScheduleState->pSchedule )
  755. {
  756. CRC32_t scheduleCrc;
  757. CRC32_Init( &scheduleCrc );
  758. CRC32_ProcessBuffer( &scheduleCrc, (void *)pScheduleState->pSchedule->GetTaskList(), pScheduleState->pSchedule->NumTasks() * sizeof(Task_t) );
  759. CRC32_Final( &scheduleCrc );
  760. if ( scheduleCrc != saveHeader.scheduleCrc )
  761. {
  762. pScheduleState->pSchedule = NULL;
  763. }
  764. }
  765. }
  766. if ( !pScheduleState->pSchedule )
  767. bDiscardScheduleState = true;
  768. if ( bDiscardScheduleState )
  769. {
  770. ClearSchedule( i, "Restoring NPC" );
  771. }
  772. }
  773. restore.EndBlock();
  774. }
  775. }
  776. //-------------------------------------
  777. #define BEHAVIOR_SAVE_BLOCKNAME "AI_Behaviors"
  778. #define BEHAVIOR_SAVE_VERSION 2
  779. void CAI_BehaviorBase::SaveBehaviors(ISave &save, CAI_BehaviorBase *pCurrentBehavior, CAI_BehaviorBase **ppBehavior, int nBehaviors, bool bTestIfNPCSave )
  780. {
  781. save.StartBlock( BEHAVIOR_SAVE_BLOCKNAME );
  782. short temp = BEHAVIOR_SAVE_VERSION;
  783. save.WriteShort( &temp );
  784. temp = (short)nBehaviors;
  785. save.WriteShort( &temp );
  786. for ( int i = 0; i < nBehaviors; i++ )
  787. {
  788. if ( ( !bTestIfNPCSave || ppBehavior[i]->ShouldNPCSave() ) )
  789. {
  790. bool bHasDatadesc = ( strcmp( ppBehavior[i]->GetDataDescMap()->dataClassName, CAI_BehaviorBase::m_DataMap.dataClassName ) != 0 );
  791. if ( bHasDatadesc )
  792. {
  793. save.StartBlock();
  794. save.WriteString( ppBehavior[i]->GetDataDescMap()->dataClassName );
  795. bool bIsCurrent = ( pCurrentBehavior == ppBehavior[i] );
  796. save.WriteBool( &bIsCurrent );
  797. ppBehavior[i]->Save( save );
  798. save.EndBlock();
  799. }
  800. else
  801. {
  802. DevMsg( "Note: behavior \"%s\" lacks a datadesc and probably won't save/restore correctly\n", ppBehavior[i]->GetName() ); // You need at least an empty datadesc to allow for correct binding on load
  803. }
  804. }
  805. }
  806. save.EndBlock();
  807. }
  808. //-------------------------------------
  809. int CAI_BehaviorBase::RestoreBehaviors(IRestore &restore, CAI_BehaviorBase **ppBehavior, int nBehaviors, bool bTestIfNPCSave )
  810. {
  811. int iCurrent = -1;
  812. char szBlockName[SIZE_BLOCK_NAME_BUF];
  813. restore.StartBlock( szBlockName );
  814. if ( strcmp( szBlockName, BEHAVIOR_SAVE_BLOCKNAME ) == 0 )
  815. {
  816. short version;
  817. restore.ReadShort( &version );
  818. if ( version == BEHAVIOR_SAVE_VERSION )
  819. {
  820. short nToRestore;
  821. char szClassNameCurrent[256];
  822. restore.ReadShort( &nToRestore );
  823. for ( int i = 0; i < nToRestore; i++ )
  824. {
  825. restore.StartBlock();
  826. restore.ReadString( szClassNameCurrent, sizeof( szClassNameCurrent ), 0 );
  827. bool bIsCurrent;
  828. restore.ReadBool( &bIsCurrent );
  829. for ( int j = 0; j < nBehaviors; j++ )
  830. {
  831. if ( ( !bTestIfNPCSave || ppBehavior[j]->ShouldNPCSave() ) && strcmp( ppBehavior[j]->GetDataDescMap()->dataClassName, szClassNameCurrent ) == 0 )
  832. {
  833. if ( bIsCurrent )
  834. iCurrent = j;
  835. ppBehavior[j]->Restore( restore );
  836. }
  837. }
  838. restore.EndBlock();
  839. }
  840. }
  841. }
  842. restore.EndBlock();
  843. return iCurrent;
  844. }
  845. //-----------------------------------------------------------------------------