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.

477 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "basemultiplayerplayer.h"
  9. #include "ai_baseactor.h"
  10. #include "ai_speech.h"
  11. #include "flex_expresser.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include <tier0/memdbgon.h>
  14. extern ConVar ai_debug_speech;
  15. #define DebuggingSpeech() ai_debug_speech.GetBool()
  16. extern ConVar rr_debugresponses;
  17. ConVar rr_followup_maxdist( "rr_followup_maxdist", "1800", FCVAR_CHEAT, "'then ANY' or 'then ALL' response followups will be dispatched only to characters within this distance." );
  18. ///////////////////////////////////////////////////////////////////////////////
  19. // RESPONSE QUEUE DATA STRUCTURE
  20. ///////////////////////////////////////////////////////////////////////////////
  21. CResponseQueue::CResponseQueue( int queueSize ) : m_Queue(queueSize), m_ExpresserTargets(8,8)
  22. {};
  23. /// Add a deferred response.
  24. void CResponseQueue::Add( const AIConcept_t &concept, ///< concept to dispatch
  25. const AI_CriteriaSet * RESTRICT contexts,
  26. float time, ///< when to dispatch it. You can specify a time of zero to mean "immediately."
  27. const CFollowupTargetSpec_t &targetspec,
  28. CBaseEntity *pIssuer
  29. )
  30. {
  31. // Add a response.
  32. AssertMsg( m_Queue.Count() < AI_RESPONSE_QUEUE_SIZE, "AI Response queue overfilled." );
  33. QueueType_t::IndexLocalType_t idx = m_Queue.AddToTail();
  34. m_Queue[idx].Init( concept, contexts, time, targetspec, pIssuer );
  35. }
  36. /// Remove a deferred response matching the concept and issuer.
  37. void CResponseQueue::Remove( const AIConcept_t &concept, ///< concept to dispatch
  38. CBaseEntity * const RESTRICT pIssuer ///< the entity issuing the response, if one exists.
  39. ) RESTRICT
  40. {
  41. // walk through the queue until we find a response matching the concept and issuer, then strike it.
  42. QueueType_t::IndexLocalType_t idx = m_Queue.Head();
  43. while (idx != m_Queue.InvalidIndex())
  44. {
  45. CDeferredResponse &response = m_Queue[idx];
  46. QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element
  47. idx = m_Queue.Next(idx); // is now the next index
  48. if ( CompareConcepts( response.m_concept, concept ) && // if concepts match and
  49. ( !pIssuer || ( response.m_hIssuer.Get() == pIssuer ) ) // issuer is null, or matches the one in the response
  50. )
  51. {
  52. m_Queue.Remove(previdx);
  53. }
  54. }
  55. }
  56. void CResponseQueue::RemoveSpeechQueuedFor( const CBaseEntity *pSpeaker )
  57. {
  58. // walk through the queue until we find a response matching the speaker, then strike it.
  59. // because responses are dispatched from inside a loop that is already walking through the
  60. // queue, it's not safe to actually remove the elements. Instead, quash it by replacing it
  61. // with a null event.
  62. for ( QueueType_t::IndexLocalType_t idx = m_Queue.Head() ;
  63. idx != m_Queue.InvalidIndex() ;
  64. idx = m_Queue.Next(idx) ) // is now the next index
  65. {
  66. CDeferredResponse &response = m_Queue[idx];
  67. if ( response.m_Target.m_hHandle.Get() == pSpeaker )
  68. {
  69. response.Quash();
  70. }
  71. }
  72. }
  73. // TODO: use a more compact representation.
  74. void CResponseQueue::DeferContextsFromCriteriaSet( DeferredContexts_t &contextsOut, const AI_CriteriaSet * RESTRICT criteriaIn )
  75. {
  76. contextsOut.Reset();
  77. if (criteriaIn)
  78. {
  79. contextsOut.Merge(criteriaIn);
  80. }
  81. }
  82. void CResponseQueue::PerFrameDispatch()
  83. {
  84. failsafe:
  85. // Walk through the list, find any messages whose time has come, and dispatch them. Then remove them.
  86. QueueType_t::IndexLocalType_t idx = m_Queue.Head();
  87. while (idx != m_Queue.InvalidIndex())
  88. {
  89. // do we need to dispatch this concept?
  90. CDeferredResponse &response = m_Queue[idx];
  91. QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element
  92. if ( response.IsQuashed() )
  93. {
  94. idx = m_Queue.Next(idx); // is now the next index
  95. // we can delete this entry now
  96. m_Queue.Remove(previdx);
  97. }
  98. else if ( response.m_fDispatchTime <= gpGlobals->curtime )
  99. {
  100. // dispatch. we've had bugs where dispatches removed things from inside the queue;
  101. // so, as a failsafe, if the queue length changes as a result, start over.
  102. int oldLength = m_Queue.Count();
  103. DispatchOneResponse(response);
  104. if ( m_Queue.Count() < oldLength )
  105. {
  106. AssertMsg( false, "Response queue length changed in non-reentrant way! FAILSAFE TRIGGERED" );
  107. goto failsafe; // ick
  108. }
  109. idx = m_Queue.Next(idx); // is now the next index
  110. // we can delete this entry now
  111. m_Queue.Remove(previdx);
  112. }
  113. else
  114. {
  115. idx = m_Queue.Next(idx); // is now the next index
  116. }
  117. }
  118. }
  119. /// Add an expressor owner to this queue.
  120. void CResponseQueue::AddExpresserHost(CBaseEntity *host)
  121. {
  122. EHANDLE ehost = host;
  123. // see if it's in there already
  124. if (m_ExpresserTargets.HasElement(ehost))
  125. {
  126. AssertMsg1(false, "Tried to add %s to response queue when it was already in there.", host->GetDebugName());
  127. }
  128. else
  129. {
  130. // zip through the queue front to back, first see if there's any invalid handles to replace
  131. int count = m_ExpresserTargets.Count();
  132. for (int i = 0 ; i < count ; ++i )
  133. {
  134. if ( !m_ExpresserTargets[i].Get() )
  135. {
  136. m_ExpresserTargets[i] = ehost;
  137. return;
  138. }
  139. }
  140. // if we're down here we didn't find one to replace, so append the host to the end.
  141. m_ExpresserTargets.AddToTail(ehost);
  142. }
  143. }
  144. /// Remove an expresser host from this queue.
  145. void CResponseQueue::RemoveExpresserHost(CBaseEntity *host)
  146. {
  147. int idx = m_ExpresserTargets.Find( host );
  148. if (idx == -1)
  149. {
  150. // AssertMsg1(false, "Tried to remove %s from response queue, but it's not in there to begin with!", host->GetDebugName() );
  151. }
  152. else
  153. {
  154. m_ExpresserTargets.FastRemove(idx);
  155. }
  156. }
  157. /// Get the expresser for a base entity.
  158. /// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
  159. static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
  160. {
  161. if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
  162. {
  163. return pPlayer->GetExpresser();
  164. }
  165. else if ( CAI_BaseActor *pActor = dynamic_cast<CAI_BaseActor *>(pEnt) )
  166. {
  167. return pActor->GetExpresser();
  168. }
  169. else if ( CFlexExpresser *pFlex = dynamic_cast<CFlexExpresser *>(pEnt) )
  170. {
  171. return pFlex->GetExpresser();
  172. }
  173. else
  174. {
  175. return NULL;
  176. }
  177. }
  178. void CResponseQueue::CDeferredResponse::Quash()
  179. {
  180. m_Target = CFollowupTargetSpec_t();
  181. m_fDispatchTime = 0;
  182. }
  183. bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
  184. {
  185. // find the target.
  186. CBaseEntity * RESTRICT pTarget = NULL;
  187. AI_CriteriaSet &deferredCriteria = response.m_contexts;
  188. CAI_Expresser * RESTRICT pEx = NULL;
  189. CBaseEntity * RESTRICT pIssuer = response.m_hIssuer.Get(); // MAY BE NULL
  190. float followupMaxDistSq;
  191. {
  192. CFlexExpresser * RESTRICT pOrator = CFlexExpresser::AsFlexExpresser( pIssuer );
  193. if ( pOrator )
  194. {
  195. // max dist is overridden. "0" means infinite distance (for orators only),
  196. // anything else is a finite distance.
  197. if ( pOrator->m_flThenAnyMaxDist > 0 )
  198. {
  199. followupMaxDistSq = pOrator->m_flThenAnyMaxDist * pOrator->m_flThenAnyMaxDist;
  200. }
  201. else
  202. {
  203. followupMaxDistSq = FLT_MAX;
  204. }
  205. }
  206. else
  207. {
  208. followupMaxDistSq = rr_followup_maxdist.GetFloat(); // square of max audibility distance
  209. followupMaxDistSq *= followupMaxDistSq;
  210. }
  211. }
  212. switch (response.m_Target.m_iTargetType)
  213. {
  214. case kDRT_SPECIFIC:
  215. {
  216. pTarget = response.m_Target.m_hHandle.Get();
  217. }
  218. break;
  219. case kDRT_ANY:
  220. {
  221. return DispatchOneResponse_ThenANY( response, &deferredCriteria, pIssuer, followupMaxDistSq );
  222. }
  223. break;
  224. case kDRT_ALL:
  225. {
  226. bool bSaidAnything = false;
  227. Vector issuerLocation;
  228. if ( pIssuer )
  229. {
  230. issuerLocation = pIssuer->GetAbsOrigin();
  231. }
  232. // find all characters
  233. int numExprs = GetNumExpresserTargets();
  234. for ( int i = 0 ; i < numExprs; ++i )
  235. {
  236. pTarget = GetExpresserHost(i);
  237. float distIssuerToTargetSq = 0.0f;
  238. if ( pIssuer )
  239. {
  240. distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr();
  241. if ( distIssuerToTargetSq > followupMaxDistSq )
  242. continue; // too far
  243. }
  244. pEx = InferExpresserFromBaseEntity(pTarget);
  245. if ( !pEx || pTarget == pIssuer )
  246. continue;
  247. AI_CriteriaSet characterCriteria;
  248. pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
  249. characterCriteria.Merge(&deferredCriteria);
  250. if ( pIssuer )
  251. {
  252. characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
  253. }
  254. AI_Response prospectiveResponse;
  255. if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
  256. {
  257. // dispatch it
  258. bSaidAnything = pEx->SpeakDispatchResponse(response.m_concept, &prospectiveResponse, &deferredCriteria) || bSaidAnything ;
  259. }
  260. }
  261. return bSaidAnything;
  262. }
  263. break;
  264. default:
  265. // WTF?
  266. AssertMsg1( false, "Unknown deferred response type %d\n", response.m_Target.m_iTargetType );
  267. return false;
  268. }
  269. if (!pTarget)
  270. return false; // we're done right here.
  271. // Get the expresser for the target.
  272. pEx = InferExpresserFromBaseEntity(pTarget);
  273. if (!pEx)
  274. return false;
  275. AI_CriteriaSet characterCriteria;
  276. pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
  277. characterCriteria.Merge(&deferredCriteria);
  278. pEx->Speak( response.m_concept, &characterCriteria );
  279. return true;
  280. }
  281. //
  282. ConVar rr_thenany_score_slop( "rr_thenany_score_slop", "0.0", FCVAR_CHEAT, "When computing respondents for a 'THEN ANY' rule, all rule-matching scores within this much of the best score will be considered." );
  283. #define EXARRAYMAX 32 // maximum number of prospective expressers in the array (hardcoded for simplicity)
  284. bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, AI_CriteriaSet * RESTRICT pDeferredCriteria, CBaseEntity * const RESTRICT pIssuer, float followupMaxDistSq )
  285. {
  286. CBaseEntity * RESTRICT pTarget = NULL;
  287. CAI_Expresser * RESTRICT pEx = NULL;
  288. float bestScore = 0;
  289. float slop = rr_thenany_score_slop.GetFloat();
  290. Vector issuerLocation;
  291. if ( pIssuer )
  292. {
  293. issuerLocation = pIssuer->GetAbsOrigin();
  294. }
  295. // this is an array of prospective respondents.
  296. CAI_Expresser * RESTRICT pBestEx[EXARRAYMAX];
  297. AI_Response responseToSay[EXARRAYMAX];
  298. int numExFound = 0; // and this is the high water mark for the array.
  299. // Here's the algorithm: we're going to walk through all the characters, finding the
  300. // highest scoring ones for this rule. Let the highest score be called k.
  301. // Because there may be (n) many characters all scoring k, we store an array of
  302. // all characters with score k, then choose randomly from that array at return.
  303. // We also define an allowable error for k in the global cvar
  304. // rr_thenany_score_slop , which may be zero.
  305. // find all characters (except the issuer)
  306. int numExprs = GetNumExpresserTargets();
  307. AssertMsg1( numExprs <= EXARRAYMAX, "Response queue has %d possible expresser targets, please increase EXARRAYMAX ", numExprs );
  308. for ( int i = 0 ; i < numExprs; ++i )
  309. {
  310. pTarget = GetExpresserHost(i);
  311. if ( pTarget == pIssuer )
  312. continue; // don't dispatch to myself
  313. if ( !pTarget->IsAlive() )
  314. continue; // dead men tell no tales
  315. float distIssuerToTargetSq = 0.0f;
  316. if ( pIssuer )
  317. {
  318. distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr();
  319. if ( distIssuerToTargetSq > followupMaxDistSq )
  320. continue; // too far
  321. }
  322. pEx = InferExpresserFromBaseEntity(pTarget);
  323. if ( !pEx )
  324. continue;
  325. AI_CriteriaSet characterCriteria;
  326. pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
  327. characterCriteria.Merge( pDeferredCriteria );
  328. pTarget->ModifyOrAppendDerivedCriteria( characterCriteria );
  329. if ( pIssuer )
  330. {
  331. characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
  332. }
  333. AI_Response prospectiveResponse;
  334. if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
  335. {
  336. float score = prospectiveResponse.GetMatchScore();
  337. if ( score > 0 && !prospectiveResponse.IsEmpty() ) // ignore scores that are zero, regardless of slop
  338. {
  339. // if this score is better than all we've seen (outside the slop), then replace the array with
  340. // an entry just to this expresser
  341. if ( score > bestScore + slop )
  342. {
  343. responseToSay[0] = prospectiveResponse;
  344. pBestEx[0] = pEx;
  345. bestScore = score;
  346. numExFound = 1;
  347. }
  348. else if ( score >= bestScore - slop ) // if this score is at least as good as the best we've seen, but not better than all
  349. {
  350. if ( numExFound >= EXARRAYMAX )
  351. continue; // SAFETY: don't overflow the array
  352. responseToSay[numExFound] = prospectiveResponse;
  353. pBestEx[numExFound] = pEx;
  354. bestScore = fpmax( score, bestScore );
  355. numExFound += 1;
  356. }
  357. }
  358. }
  359. }
  360. // if I have a response, dispatch it.
  361. if ( numExFound > 0 )
  362. {
  363. // get a random number between 0 and the responses found
  364. int iSelect = numExFound > 1 ? RandomInt( 0, numExFound - 1 ) : 0;
  365. if ( pBestEx[iSelect] != NULL )
  366. {
  367. return pBestEx[iSelect]->SpeakDispatchResponse( response.m_concept, responseToSay + iSelect, pDeferredCriteria );
  368. }
  369. else
  370. {
  371. AssertMsg( false, "Response queue somehow found a response, but no expresser for it.\n" );
  372. return false;
  373. }
  374. }
  375. else
  376. { // I did not find a response.
  377. return false;
  378. }
  379. return false; // just in case
  380. }
  381. void CResponseQueue::Evacuate()
  382. {
  383. m_Queue.RemoveAll();
  384. }
  385. #undef EXARRAYMAX
  386. ///////////////////////////////////////////////////////////////////////////////
  387. // RESPONSE QUEUE MANAGER
  388. ///////////////////////////////////////////////////////////////////////////////
  389. void CResponseQueueManager::LevelInitPreEntity( void )
  390. {
  391. if (m_pQueue == NULL)
  392. {
  393. m_pQueue = new CResponseQueue(AI_RESPONSE_QUEUE_SIZE);
  394. }
  395. }
  396. CResponseQueueManager::~CResponseQueueManager()
  397. {
  398. if (m_pQueue != NULL)
  399. {
  400. delete m_pQueue;
  401. m_pQueue = NULL;
  402. }
  403. }
  404. void CResponseQueueManager::Shutdown()
  405. {
  406. if (m_pQueue != NULL)
  407. {
  408. delete m_pQueue;
  409. m_pQueue = NULL;
  410. }
  411. }
  412. void CResponseQueueManager::FrameUpdatePostEntityThink()
  413. {
  414. Assert(m_pQueue);
  415. m_pQueue->PerFrameDispatch();
  416. }
  417. CResponseQueueManager g_ResponseQueueManager( "CResponseQueueManager" );