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.

2707 lines
74 KiB

  1. //========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Core types for the response rules -- criteria, responses, rules, and matchers.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "rrbase.h"
  8. #include "vstdlib/random.h"
  9. #include "utlbuffer.h"
  10. #include "tier2/interval.h"
  11. #include "fmtstr.h"
  12. #include "generichash.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. // #pragma optimize( "", off )
  16. using namespace ResponseRules;
  17. #if RESPONSE_RULES_DEBUG_ENABLED // this is an exploit to crash game servers, it is now disabled
  18. static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args );
  19. static ConCommand rr_debug_responseconcept_exclude( "rr_debugresponseconcept_exclude", CC_RR_Debug_ResponseConcept_Exclude, "Set a list of concepts to exclude from rr_debugresponseconcept. Separate multiple concepts with spaces. Call with no arguments to see current list. Call 'rr_debug_responseconcept_exclude !' to reset.");
  20. #endif
  21. static void CC_RR_DumpHashInfo( const CCommand &args );
  22. namespace ResponseRules
  23. {
  24. // ick
  25. // Wrap string lookup with a hash on the string so that all of the repetitive playerxxx type strings get bucketed out better
  26. #define STRING_BUCKETS_COUNT 64 // Must be power of two
  27. #define STRING_BUCKETS_MASK ( STRING_BUCKETS_COUNT - 1 )
  28. CUtlRBTree<const char *> g_ResponseStrings[ STRING_BUCKETS_COUNT ];
  29. class CResponseStringBuckets
  30. {
  31. public:
  32. CResponseStringBuckets()
  33. {
  34. for ( int i = 0; i < ARRAYSIZE( g_ResponseStrings ); ++i )
  35. {
  36. g_ResponseStrings[ i ].SetLessFunc( &StringLessThan );
  37. }
  38. }
  39. } g_ReponseStringBucketInitializer;
  40. const char *ResponseCopyString( const char *in )
  41. {
  42. if ( !in )
  43. return NULL;
  44. if ( !*in )
  45. return "";
  46. int bucket = ( RR_HASH( in ) & STRING_BUCKETS_MASK );
  47. CUtlRBTree<const char *> &list = g_ResponseStrings[ bucket ];
  48. int i = list.Find( in );
  49. if ( i != list.InvalidIndex() )
  50. {
  51. return list[i];
  52. }
  53. int len = Q_strlen( in );
  54. char *out = new char[ len + 1 ];
  55. Q_memcpy( out, in, len );
  56. out[ len ] = 0;
  57. list.Insert( out );
  58. return out;
  59. }
  60. }
  61. IEngineEmulator *IEngineEmulator::Get()
  62. {
  63. AssertMsg( IEngineEmulator::s_pSingleton, "Response rules fail: no IEngineEmulator" );
  64. return IEngineEmulator::s_pSingleton;
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Convars
  68. //-----------------------------------------------------------------------------
  69. ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring, 3 for noisy). If set to 4, it will only show response success/failure for npc_selected NPCs." );
  70. ConVar rr_debugrule( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system.");
  71. ConVar rr_dumpresponses( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" );
  72. ConVar rr_debugresponseconcept( "rr_debugresponseconcept", "", FCVAR_NONE, "If set, rr_debugresponses will print only responses testing for the specified concept" );
  73. #define RR_DEBUGRESPONSES_SPECIALCASE 4
  74. //-----------------------------------------------------------------------------
  75. // Copied from SoundParametersInternal.cpp
  76. //-----------------------------------------------------------------------------
  77. #define SNDLVL_PREFIX "SNDLVL_"
  78. struct SoundLevelLookup
  79. {
  80. soundlevel_t level;
  81. char const *name;
  82. };
  83. // NOTE: Needs to reflect the soundlevel_t enum defined in soundflags.h
  84. static SoundLevelLookup g_pSoundLevels[] =
  85. {
  86. { SNDLVL_NONE, "SNDLVL_NONE" },
  87. { SNDLVL_20dB, "SNDLVL_20dB" },
  88. { SNDLVL_25dB, "SNDLVL_25dB" },
  89. { SNDLVL_30dB, "SNDLVL_30dB" },
  90. { SNDLVL_35dB, "SNDLVL_35dB" },
  91. { SNDLVL_40dB, "SNDLVL_40dB" },
  92. { SNDLVL_45dB, "SNDLVL_45dB" },
  93. { SNDLVL_50dB, "SNDLVL_50dB" },
  94. { SNDLVL_55dB, "SNDLVL_55dB" },
  95. { SNDLVL_IDLE, "SNDLVL_IDLE" },
  96. { SNDLVL_TALKING, "SNDLVL_TALKING" },
  97. { SNDLVL_60dB, "SNDLVL_60dB" },
  98. { SNDLVL_65dB, "SNDLVL_65dB" },
  99. { SNDLVL_STATIC, "SNDLVL_STATIC" },
  100. { SNDLVL_70dB, "SNDLVL_70dB" },
  101. { SNDLVL_NORM, "SNDLVL_NORM" },
  102. { SNDLVL_75dB, "SNDLVL_75dB" },
  103. { SNDLVL_80dB, "SNDLVL_80dB" },
  104. { SNDLVL_85dB, "SNDLVL_85dB" },
  105. { SNDLVL_90dB, "SNDLVL_90dB" },
  106. { SNDLVL_95dB, "SNDLVL_95dB" },
  107. { SNDLVL_100dB, "SNDLVL_100dB" },
  108. { SNDLVL_105dB, "SNDLVL_105dB" },
  109. { SNDLVL_110dB, "SNDLVL_110dB" },
  110. { SNDLVL_120dB, "SNDLVL_120dB" },
  111. { SNDLVL_130dB, "SNDLVL_130dB" },
  112. { SNDLVL_GUNFIRE, "SNDLVL_GUNFIRE" },
  113. { SNDLVL_140dB, "SNDLVL_140dB" },
  114. { SNDLVL_150dB, "SNDLVL_150dB" },
  115. { SNDLVL_180dB, "SNDLVL_180dB" },
  116. };
  117. static soundlevel_t TextToSoundLevel( const char *key )
  118. {
  119. if ( !key )
  120. {
  121. Assert( 0 );
  122. return SNDLVL_NORM;
  123. }
  124. int c = ARRAYSIZE( g_pSoundLevels );
  125. int i;
  126. for ( i = 0; i < c; i++ )
  127. {
  128. SoundLevelLookup *entry = &g_pSoundLevels[ i ];
  129. if ( !Q_strcasecmp( key, entry->name ) )
  130. return entry->level;
  131. }
  132. if ( !Q_strnicmp( key, SNDLVL_PREFIX, Q_strlen( SNDLVL_PREFIX ) ) )
  133. {
  134. char const *val = key + Q_strlen( SNDLVL_PREFIX );
  135. int sndlvl = atoi( val );
  136. if ( sndlvl > 0 && sndlvl <= 180 )
  137. {
  138. return ( soundlevel_t )sndlvl;
  139. }
  140. }
  141. DevMsg( "CSoundEmitterSystem: Unknown sound level %s\n", key );
  142. return SNDLVL_NORM;
  143. }
  144. CResponseSystem::ExcludeList_t CResponseSystem::m_DebugExcludeList( 4, 0 );
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. //-----------------------------------------------------------------------------
  148. CResponseSystem::CResponseSystem() :
  149. m_RootCommandHashes( 0, 0, DefLessFunc( unsigned int ) ),
  150. m_FileDispatch( 0, 0, DefLessFunc( unsigned int ) ),
  151. m_RuleDispatch( 0, 0, DefLessFunc( unsigned int ) ),
  152. m_ResponseDispatch( 0, 0, DefLessFunc( unsigned int ) ),
  153. m_ResponseGroupDispatch( 0, 0, DefLessFunc( unsigned int ) )
  154. {
  155. token[0] = 0;
  156. m_bUnget = false;
  157. m_bCustomManagable = false;
  158. BuildDispatchTables();
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose:
  162. //-----------------------------------------------------------------------------
  163. CResponseSystem::~CResponseSystem()
  164. {
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. // Output : char const
  169. //-----------------------------------------------------------------------------
  170. void CResponseSystem::GetCurrentScript( char *buf, size_t buflen )
  171. {
  172. Assert( buf );
  173. buf[ 0 ] = 0;
  174. if ( m_ScriptStack.Count() <= 0 )
  175. return;
  176. if ( IEngineEmulator::Get()->GetFilesystem()->String( m_ScriptStack[ 0 ].name, buf, buflen ) )
  177. {
  178. return;
  179. }
  180. buf[ 0 ] = 0;
  181. }
  182. void CResponseSystem::PushScript( const char *scriptfile, unsigned char *buffer )
  183. {
  184. ScriptEntry e;
  185. e.name = IEngineEmulator::Get()->GetFilesystem()->FindOrAddFileName( scriptfile );
  186. e.buffer = buffer;
  187. e.currenttoken = (char *)e.buffer;
  188. e.tokencount = 0;
  189. m_ScriptStack.AddToHead( e );
  190. }
  191. void CResponseSystem::PopScript(void)
  192. {
  193. Assert( m_ScriptStack.Count() >= 1 );
  194. if ( m_ScriptStack.Count() <= 0 )
  195. return;
  196. m_ScriptStack.Remove( 0 );
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. //-----------------------------------------------------------------------------
  201. void CResponseSystem::Clear()
  202. {
  203. m_Responses.RemoveAll();
  204. m_Criteria.RemoveAll();
  205. m_RulePartitions.RemoveAll();
  206. m_Enumerations.RemoveAll();
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. // Input : *name -
  211. // found -
  212. // Output : float
  213. //-----------------------------------------------------------------------------
  214. float CResponseSystem::LookupEnumeration( const char *name, bool& found )
  215. {
  216. int idx = m_Enumerations.Find( name );
  217. if ( idx == m_Enumerations.InvalidIndex() )
  218. {
  219. found = false;
  220. return 0.0f;
  221. }
  222. found = true;
  223. return m_Enumerations[ idx ].value;
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. // Input : matcher -
  228. //-----------------------------------------------------------------------------
  229. void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken )
  230. {
  231. if ( rawtoken[0] != '[' )
  232. {
  233. Q_strncpy( token, rawtoken, bufsize );
  234. return;
  235. }
  236. // Now lookup enumeration
  237. bool found = false;
  238. float f = LookupEnumeration( rawtoken, found );
  239. if ( !found )
  240. {
  241. Q_strncpy( token, rawtoken, bufsize );
  242. ResponseWarning( "No such enumeration '%s'\n", token );
  243. return;
  244. }
  245. Q_snprintf( token, bufsize, "%f", f );
  246. }
  247. static bool AppearsToBeANumber( char const *token )
  248. {
  249. if ( atof( token ) != 0.0f )
  250. return true;
  251. char const *p = token;
  252. while ( *p )
  253. {
  254. if ( *p != '0' )
  255. return false;
  256. p++;
  257. }
  258. return true;
  259. }
  260. void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher )
  261. {
  262. const char *s = c->value;
  263. if ( !s )
  264. {
  265. matcher.valid = false;
  266. return;
  267. }
  268. const char *in = s;
  269. char token[ 128 ];
  270. char rawtoken[ 128 ];
  271. token[ 0 ] = 0;
  272. rawtoken[ 0 ] = 0;
  273. int n = 0;
  274. bool gt = false;
  275. bool lt = false;
  276. bool eq = false;
  277. bool nt = false;
  278. bool done = false;
  279. while ( !done )
  280. {
  281. switch( *in )
  282. {
  283. case '>':
  284. {
  285. gt = true;
  286. Assert( !lt ); // Can't be both
  287. }
  288. break;
  289. case '<':
  290. {
  291. lt = true;
  292. Assert( !gt ); // Can't be both
  293. }
  294. break;
  295. case '=':
  296. {
  297. eq = true;
  298. }
  299. break;
  300. case ',':
  301. case '\0':
  302. {
  303. rawtoken[ n ] = 0;
  304. n = 0;
  305. // Convert raw token to real token in case token is an enumerated type specifier
  306. ResolveToken( matcher, token, sizeof( token ), rawtoken );
  307. // Fill in first data set
  308. if ( gt )
  309. {
  310. matcher.usemin = true;
  311. matcher.minequals = eq;
  312. matcher.minval = (float)atof( token );
  313. matcher.isnumeric = true;
  314. }
  315. else if ( lt )
  316. {
  317. matcher.usemax = true;
  318. matcher.maxequals = eq;
  319. matcher.maxval = (float)atof( token );
  320. matcher.isnumeric = true;
  321. }
  322. else
  323. {
  324. if ( *in == ',' )
  325. {
  326. // If there's a comma, this better have been a less than or a gt key
  327. Assert( 0 );
  328. }
  329. matcher.notequal = nt;
  330. matcher.isnumeric = AppearsToBeANumber( token );
  331. }
  332. gt = lt = eq = nt = false;
  333. if ( !(*in) )
  334. {
  335. done = true;
  336. }
  337. }
  338. break;
  339. case '!':
  340. nt = true;
  341. break;
  342. default:
  343. rawtoken[ n++ ] = *in;
  344. break;
  345. }
  346. in++;
  347. }
  348. matcher.SetToken( token );
  349. matcher.SetRaw( rawtoken );
  350. matcher.valid = true;
  351. }
  352. bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose /*=false*/ )
  353. {
  354. if ( !m.valid )
  355. return false;
  356. float v = (float)atof( setValue );
  357. if ( setValue[0] == '[' )
  358. {
  359. bool found = false;
  360. v = LookupEnumeration( setValue, found );
  361. }
  362. int minmaxcount = 0;
  363. if ( m.usemin )
  364. {
  365. if ( m.minequals )
  366. {
  367. if ( v < m.minval )
  368. return false;
  369. }
  370. else
  371. {
  372. if ( v <= m.minval )
  373. return false;
  374. }
  375. ++minmaxcount;
  376. }
  377. if ( m.usemax )
  378. {
  379. if ( m.maxequals )
  380. {
  381. if ( v > m.maxval )
  382. return false;
  383. }
  384. else
  385. {
  386. if ( v >= m.maxval )
  387. return false;
  388. }
  389. ++minmaxcount;
  390. }
  391. // Had one or both criteria and met them
  392. if ( minmaxcount >= 1 )
  393. {
  394. return true;
  395. }
  396. if ( m.notequal )
  397. {
  398. if ( m.isnumeric )
  399. {
  400. if ( v == (float)atof( m.GetToken() ) )
  401. return false;
  402. }
  403. else
  404. {
  405. if ( !Q_stricmp( setValue, m.GetToken() ) )
  406. return false;
  407. }
  408. return true;
  409. }
  410. if ( m.isnumeric )
  411. {
  412. // If the setValue is "", the NPC doesn't have the key at all,
  413. // in which case we shouldn't match "0".
  414. if ( !setValue || !setValue[0] )
  415. return false;
  416. return v == (float)atof( m.GetToken() );
  417. }
  418. return !Q_stricmp( setValue, m.GetToken() ) ? true : false;
  419. }
  420. bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ )
  421. {
  422. Assert( c );
  423. Assert( setValue );
  424. bool bret = CompareUsingMatcher( setValue, c->matcher, verbose );
  425. if ( verbose )
  426. {
  427. DevMsg( "'%20s' vs. '%20s' = ", setValue, c->value );
  428. {
  429. //DevMsg( "\n" );
  430. //m.Describe();
  431. }
  432. }
  433. return bret;
  434. }
  435. float CResponseSystem::RecursiveScoreSubcriteriaAgainstRule( const CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ )
  436. {
  437. float score = 0.0f;
  438. int subcount = parent->subcriteria.Count();
  439. for ( int i = 0; i < subcount; i++ )
  440. {
  441. int icriterion = parent->subcriteria[ i ];
  442. bool excludesubrule = false;
  443. if (verbose)
  444. {
  445. DevMsg( "\n" );
  446. }
  447. score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, excludesubrule, verbose );
  448. }
  449. exclude = ( parent->required && score == 0.0f ) ? true : false;
  450. return score * parent->weight.GetFloat();
  451. }
  452. float CResponseSystem::RecursiveLookForCriteria( const CriteriaSet &criteriaSet, Criteria *pParent )
  453. {
  454. float flScore = 0.0f;
  455. int nSubCount = pParent->subcriteria.Count();
  456. for ( int iSub = 0; iSub < nSubCount; ++iSub )
  457. {
  458. int iCriteria = pParent->subcriteria[iSub];
  459. flScore += LookForCriteria( criteriaSet, iCriteria );
  460. }
  461. return flScore;
  462. }
  463. float CResponseSystem::LookForCriteria( const CriteriaSet &criteriaSet, int iCriteria )
  464. {
  465. Criteria *pCriteria = &m_Criteria[iCriteria];
  466. if ( pCriteria->IsSubCriteriaType() )
  467. {
  468. return RecursiveLookForCriteria( criteriaSet, pCriteria );
  469. }
  470. int iIndex = criteriaSet.FindCriterionIndex( pCriteria->nameSym );
  471. if ( iIndex == -1 )
  472. return 0.0f;
  473. Assert( criteriaSet.GetValue( iIndex ) );
  474. if ( Q_stricmp( criteriaSet.GetValue( iIndex ), pCriteria->value ) )
  475. return 0.0f;
  476. return 1.0f;
  477. }
  478. float CResponseSystem::ScoreCriteriaAgainstRuleCriteria( const CriteriaSet& set, int icriterion, bool& exclude, bool verbose /*=false*/ )
  479. {
  480. Criteria *c = &m_Criteria[ icriterion ];
  481. if ( c->IsSubCriteriaType() )
  482. {
  483. return RecursiveScoreSubcriteriaAgainstRule( set, c, exclude, verbose );
  484. }
  485. if ( verbose )
  486. {
  487. DevMsg( " criterion '%25s':'%15s' ", m_Criteria.GetElementName( icriterion ), CriteriaSet::SymbolToStr(c->nameSym) );
  488. }
  489. exclude = false;
  490. float score = 0.0f;
  491. const char *actualValue = "";
  492. /*
  493. const char * RESTRICT critname = c->name;
  494. CUtlSymbol sym(critname);
  495. const char * nameDoubleCheck = sym.String();
  496. */
  497. int found = set.FindCriterionIndex( c->nameSym );
  498. if ( found != -1 )
  499. {
  500. actualValue = set.GetValue( found );
  501. if ( !actualValue )
  502. {
  503. Assert( 0 );
  504. return score;
  505. }
  506. }
  507. Assert( actualValue );
  508. if ( Compare( actualValue, c, verbose ) )
  509. {
  510. float w = set.GetWeight( found );
  511. score = w * c->weight.GetFloat();
  512. if ( verbose )
  513. {
  514. DevMsg( "matched, weight %4.2f (s %4.2f x c %4.2f)",
  515. score, w, c->weight.GetFloat() );
  516. }
  517. }
  518. else
  519. {
  520. if ( c->required )
  521. {
  522. exclude = true;
  523. if ( verbose )
  524. {
  525. DevMsg( "failed (+exclude rule)" );
  526. }
  527. }
  528. else
  529. {
  530. if ( verbose )
  531. {
  532. DevMsg( "failed" );
  533. }
  534. }
  535. }
  536. return score;
  537. }
  538. float CResponseSystem::ScoreCriteriaAgainstRule( const CriteriaSet& set, ResponseRulePartition::tRuleDict &dict, int irule, bool verbose /*=false*/ )
  539. {
  540. Rule * RESTRICT rule = dict[ irule ];
  541. float score = 0.0f;
  542. bool bBeingWatched = false;
  543. // See if we're trying to debug this rule
  544. const char *pszText = rr_debugrule.GetString();
  545. if ( pszText && pszText[0] && !Q_stricmp( pszText, dict.GetElementName( irule ) ) )
  546. {
  547. bBeingWatched = true;
  548. }
  549. if ( !rule->IsEnabled() )
  550. {
  551. if ( bBeingWatched )
  552. {
  553. DevMsg("Rule is disabled.\n" );
  554. }
  555. return 0.0f;
  556. }
  557. if ( bBeingWatched )
  558. {
  559. verbose = true;
  560. }
  561. if ( verbose )
  562. {
  563. DevMsg( "Scoring rule '%s' (%i)\n{\n", dict.GetElementName( irule ), irule+1 );
  564. }
  565. // Iterate set criteria
  566. int count = rule->m_Criteria.Count();
  567. int i;
  568. for ( i = 0; i < count; i++ )
  569. {
  570. int icriterion = rule->m_Criteria[ i ];
  571. bool exclude = false;
  572. score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, exclude, verbose );
  573. if ( verbose )
  574. {
  575. DevMsg( ", score %4.2f\n", score );
  576. }
  577. if ( exclude )
  578. {
  579. score = 0.0f;
  580. break;
  581. }
  582. }
  583. if ( verbose )
  584. {
  585. DevMsg( "}\n" );
  586. }
  587. if ( rule->m_nForceWeight > 0 )
  588. { // this means override the cumulative weight of criteria and just force the rule's total score,
  589. // assuming it matched at all.
  590. return fsel( score - FLT_MIN, rule->m_nForceWeight, 0 );
  591. }
  592. else
  593. {
  594. return score;
  595. }
  596. }
  597. void CResponseSystem::DebugPrint( int depth, const char *fmt, ... )
  598. {
  599. int indentchars = 3 * depth;
  600. char *indent = (char *) stackalloc( indentchars + 1);
  601. indent[ indentchars ] = 0;
  602. while ( --indentchars >= 0 )
  603. {
  604. indent[ indentchars ] = ' ';
  605. }
  606. // Dump text to debugging console.
  607. va_list argptr;
  608. char szText[1024];
  609. va_start (argptr, fmt);
  610. Q_vsnprintf (szText, sizeof( szText ), fmt, argptr);
  611. va_end (argptr);
  612. DevMsg( "%s%s", indent, szText );
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Purpose:
  616. //-----------------------------------------------------------------------------
  617. void CResponseSystem::ResetResponseGroups()
  618. {
  619. int i;
  620. int c = m_Responses.Count();
  621. for ( i = 0; i < c; i++ )
  622. {
  623. m_Responses[ i ].Reset();
  624. }
  625. }
  626. //-----------------------------------------------------------------------------
  627. // Purpose: Make certain responses unavailable by marking them as depleted
  628. //-----------------------------------------------------------------------------
  629. void CResponseSystem::FakeDepletes( ResponseGroup *g, IResponseFilter *pFilter )
  630. {
  631. m_FakedDepletes.RemoveAll();
  632. // Fake depletion of unavailable choices
  633. int c = g->group.Count();
  634. if ( pFilter && g->ShouldCheckRepeats() )
  635. {
  636. for ( int i = 0; i < c; i++ )
  637. {
  638. ParserResponse *r = &g->group[ i ];
  639. if ( r->depletioncount != g->GetDepletionCount() && !pFilter->IsValidResponse( r->GetType(), r->value ) )
  640. {
  641. m_FakedDepletes.AddToTail( i );
  642. g->MarkResponseUsed( i );
  643. }
  644. }
  645. }
  646. // Fake depletion of choices that fail the odds check
  647. for ( int i = 0; i < c; i++ )
  648. {
  649. ParserResponse *r = &g->group[ i ];
  650. if ( RandomInt( 1, 100 ) > r->params.odds )
  651. {
  652. m_FakedDepletes.AddToTail( i );
  653. g->MarkResponseUsed( i );
  654. }
  655. }
  656. }
  657. //-----------------------------------------------------------------------------
  658. // Purpose: Restore responses that were faked as being depleted
  659. //-----------------------------------------------------------------------------
  660. void CResponseSystem::RevertFakedDepletes( ResponseGroup *g )
  661. {
  662. for ( int i = 0; i < m_FakedDepletes.Count(); i++ )
  663. {
  664. g->group[ m_FakedDepletes[ i ] ].depletioncount = 0;
  665. }
  666. m_FakedDepletes.RemoveAll();
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Purpose:
  670. // Input : *g -
  671. // Output : int
  672. //-----------------------------------------------------------------------------
  673. int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter )
  674. {
  675. int c = g->group.Count();
  676. if ( !c )
  677. {
  678. Assert( !"Expecting response group with >= 1 elements" );
  679. return -1;
  680. }
  681. FakeDepletes( g, pFilter );
  682. if ( !g->HasUndepletedChoices() )
  683. {
  684. g->ResetDepletionCount();
  685. FakeDepletes( g, pFilter );
  686. if ( !g->HasUndepletedChoices() )
  687. return -1;
  688. // Disable the group if we looped through all the way
  689. if ( g->IsNoRepeat() )
  690. {
  691. g->SetEnabled( false );
  692. return -1;
  693. }
  694. }
  695. bool checkrepeats = g->ShouldCheckRepeats();
  696. int depletioncount = g->GetDepletionCount();
  697. float totalweight = 0.0f;
  698. int slot = -1;
  699. if ( checkrepeats )
  700. {
  701. int check= -1;
  702. // Snag the first slot right away
  703. if ( g->HasUndepletedFirst( check ) && check != -1 )
  704. {
  705. slot = check;
  706. }
  707. if ( slot == -1 && g->HasUndepletedLast( check ) && check != -1 )
  708. {
  709. // If this is the only undepleted one, use it now
  710. int i;
  711. for ( i = 0; i < c; i++ )
  712. {
  713. ParserResponse *r = &g->group[ i ];
  714. if ( checkrepeats &&
  715. ( r->depletioncount == depletioncount ) )
  716. {
  717. continue;
  718. }
  719. if ( r->last )
  720. {
  721. Assert( i == check );
  722. continue;
  723. }
  724. // There's still another undepleted entry
  725. break;
  726. }
  727. // No more undepleted so use the r->last slot
  728. if ( i >= c )
  729. {
  730. slot = check;
  731. }
  732. }
  733. }
  734. if ( slot == -1 )
  735. {
  736. for ( int i = 0; i < c; i++ )
  737. {
  738. ParserResponse *r = &g->group[ i ];
  739. if ( checkrepeats &&
  740. ( r->depletioncount == depletioncount ) )
  741. {
  742. continue;
  743. }
  744. // Always skip last entry here since we will deal with it above
  745. if ( checkrepeats && r->last )
  746. continue;
  747. int prevSlot = slot;
  748. if ( !totalweight )
  749. {
  750. slot = i;
  751. }
  752. // Always assume very first slot will match
  753. totalweight += r->weight.GetFloat();
  754. if ( !totalweight || IEngineEmulator::Get()->GetRandomStream()->RandomFloat(0,totalweight) < r->weight.GetFloat() )
  755. {
  756. slot = i;
  757. }
  758. if ( !checkrepeats && slot != prevSlot && pFilter && !pFilter->IsValidResponse( r->GetType(), r->value ) )
  759. {
  760. slot = prevSlot;
  761. totalweight -= r->weight.GetFloat();
  762. }
  763. }
  764. }
  765. if ( slot != -1 )
  766. g->MarkResponseUsed( slot );
  767. // Revert fake depletion of unavailable choices
  768. RevertFakedDepletes( g );
  769. return slot;
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose:
  773. // Input : searchResult -
  774. // depth -
  775. // *name -
  776. // verbose -
  777. // Output : Returns true on success, false on failure.
  778. //-----------------------------------------------------------------------------
  779. bool CResponseSystem::ResolveResponse( ResponseSearchResult& searchResult, int depth, const char *name, bool verbose /*= false*/, IResponseFilter *pFilter )
  780. {
  781. int responseIndex = m_Responses.Find( name );
  782. if ( responseIndex == m_Responses.InvalidIndex() )
  783. return false;
  784. ResponseGroup *g = &m_Responses[ responseIndex ];
  785. // Group has been disabled
  786. if ( !g->IsEnabled() )
  787. return false;
  788. int c = g->group.Count();
  789. if ( !c )
  790. return false;
  791. int idx = 0;
  792. if ( g->IsSequential() )
  793. {
  794. // See if next index is valid
  795. int initialIndex = g->GetCurrentIndex();
  796. bool bFoundValid = false;
  797. do
  798. {
  799. idx = g->GetCurrentIndex();
  800. g->SetCurrentIndex( idx + 1 );
  801. if ( idx >= c )
  802. {
  803. if ( g->IsNoRepeat() )
  804. {
  805. g->SetEnabled( false );
  806. return false;
  807. }
  808. idx = 0;
  809. g->SetCurrentIndex( 0 );
  810. }
  811. if ( !pFilter || pFilter->IsValidResponse( g->group[idx].GetType(), g->group[idx].value ) )
  812. {
  813. bFoundValid = true;
  814. break;
  815. }
  816. } while ( g->GetCurrentIndex() != initialIndex );
  817. if ( !bFoundValid )
  818. return false;
  819. }
  820. else
  821. {
  822. idx = SelectWeightedResponseFromResponseGroup( g, pFilter );
  823. if ( idx < 0 )
  824. return false;
  825. }
  826. if ( verbose )
  827. {
  828. DebugPrint( depth, "%s\n", m_Responses.GetElementName( responseIndex ) );
  829. DebugPrint( depth, "{\n" );
  830. DescribeResponseGroup( g, idx, depth );
  831. }
  832. bool bret = true;
  833. ParserResponse *result = &g->group[ idx ];
  834. if ( result->type == RESPONSE_RESPONSE )
  835. {
  836. // Recurse
  837. bret = ResolveResponse( searchResult, depth + 1, result->value, verbose, pFilter );
  838. }
  839. else
  840. {
  841. searchResult.action = result;
  842. searchResult.group = g;
  843. }
  844. if( verbose )
  845. {
  846. DebugPrint( depth, "}\n" );
  847. }
  848. return bret;
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Purpose:
  852. // Input : *group -
  853. // selected -
  854. // depth -
  855. //-----------------------------------------------------------------------------
  856. void CResponseSystem::DescribeResponseGroup( ResponseGroup *group, int selected, int depth )
  857. {
  858. int c = group->group.Count();
  859. for ( int i = 0; i < c ; i++ )
  860. {
  861. ParserResponse *r = &group->group[ i ];
  862. DebugPrint( depth + 1, "%s%20s : %40s %5.3f\n",
  863. i == selected ? "-> " : " ",
  864. CRR_Response::DescribeResponse( r->GetType() ),
  865. r->value,
  866. r->weight.GetFloat() );
  867. }
  868. }
  869. //-----------------------------------------------------------------------------
  870. // Purpose:
  871. // Input : *rule -
  872. // Output : CResponseSystem::Response
  873. //-----------------------------------------------------------------------------
  874. bool CResponseSystem::GetBestResponse( ResponseSearchResult& searchResult, Rule *rule, bool verbose /*=false*/, IResponseFilter *pFilter )
  875. {
  876. int c = rule->m_Responses.Count();
  877. if ( !c )
  878. return false;
  879. int index = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, c - 1 );
  880. int groupIndex = rule->m_Responses[ index ];
  881. ResponseGroup *g = &m_Responses[ groupIndex ];
  882. // Group has been disabled
  883. if ( !g->IsEnabled() )
  884. return false;
  885. int count = g->group.Count();
  886. if ( !count )
  887. return false;
  888. int responseIndex = 0;
  889. if ( g->IsSequential() )
  890. {
  891. // See if next index is valid
  892. int initialIndex = g->GetCurrentIndex();
  893. bool bFoundValid = false;
  894. do
  895. {
  896. responseIndex = g->GetCurrentIndex();
  897. g->SetCurrentIndex( responseIndex + 1 );
  898. if ( responseIndex >= count )
  899. {
  900. if ( g->IsNoRepeat() )
  901. {
  902. g->SetEnabled( false );
  903. return false;
  904. }
  905. responseIndex = 0;
  906. g->SetCurrentIndex( 0 );
  907. }
  908. if ( !pFilter || pFilter->IsValidResponse( g->group[responseIndex].GetType(), g->group[responseIndex].value ) )
  909. {
  910. bFoundValid = true;
  911. break;
  912. }
  913. } while ( g->GetCurrentIndex() != initialIndex );
  914. if ( !bFoundValid )
  915. return false;
  916. }
  917. else
  918. {
  919. responseIndex = SelectWeightedResponseFromResponseGroup( g, pFilter );
  920. if ( responseIndex < 0 )
  921. return false;
  922. }
  923. ParserResponse *r = &g->group[ responseIndex ];
  924. int depth = 0;
  925. if ( verbose )
  926. {
  927. DebugPrint( depth, "%s\n", m_Responses.GetElementName( groupIndex ) );
  928. DebugPrint( depth, "{\n" );
  929. DescribeResponseGroup( g, responseIndex, depth );
  930. }
  931. bool bret = true;
  932. if ( r->type == RESPONSE_RESPONSE )
  933. {
  934. bret = ResolveResponse( searchResult, depth + 1, r->value, verbose, pFilter );
  935. }
  936. else
  937. {
  938. searchResult.action = r;
  939. searchResult.group = g;
  940. }
  941. if ( verbose )
  942. {
  943. DebugPrint( depth, "}\n" );
  944. }
  945. return bret;
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Purpose:
  949. // Input : set -
  950. // verbose -
  951. // Output : int
  952. // Warning: If you change this, be sure to also change
  953. // ResponseSystemImplementationCLI::FindAllRulesMatchingCriteria().
  954. //-----------------------------------------------------------------------------
  955. ResponseRulePartition::tIndex CResponseSystem::FindBestMatchingRule( const CriteriaSet& set, bool verbose, float &scoreOfBestMatchingRule )
  956. {
  957. CUtlVector< ResponseRulePartition::tIndex > bestrules(16,4);
  958. float bestscore = 0.001f;
  959. scoreOfBestMatchingRule = 0;
  960. CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > buckets( 0, 2 );
  961. m_RulePartitions.GetDictsForCriteria( &buckets, set );
  962. for ( int b = 0 ; b < buckets.Count() ; ++b )
  963. {
  964. ResponseRulePartition::tRuleDict *prules = buckets[b];
  965. int c = prules->Count();
  966. int i;
  967. for ( i = 0; i < c; i++ )
  968. {
  969. float score = ScoreCriteriaAgainstRule( set, *prules, i, verbose );
  970. // Check equals so that we keep track of all matching rules
  971. if ( score >= bestscore )
  972. {
  973. // Reset bucket
  974. if( score != bestscore )
  975. {
  976. bestscore = score;
  977. bestrules.RemoveAll();
  978. }
  979. // Add to bucket
  980. bestrules.AddToTail( m_RulePartitions.IndexFromDictElem( prules, i ) );
  981. }
  982. }
  983. }
  984. int bestCount = bestrules.Count();
  985. if ( bestCount <= 0 )
  986. return m_RulePartitions.InvalidIdx();
  987. scoreOfBestMatchingRule = bestscore ;
  988. if ( bestCount == 1 )
  989. {
  990. return bestrules[ 0 ] ;
  991. }
  992. else
  993. {
  994. // Randomly pick one of the tied matching rules
  995. int idx = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, bestCount - 1 );
  996. if ( verbose )
  997. {
  998. DevMsg( "Found %i matching rules, selecting slot %i\n", bestCount, idx );
  999. }
  1000. return bestrules[ idx ] ;
  1001. }
  1002. }
  1003. //-----------------------------------------------------------------------------
  1004. // Purpose:
  1005. // Input : set -
  1006. // Output : CRR_Response
  1007. //-----------------------------------------------------------------------------
  1008. bool CResponseSystem::FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter )
  1009. {
  1010. bool valid = false;
  1011. int iDbgResponse = rr_debugresponses.GetInt();
  1012. bool showRules = ( iDbgResponse >= 2 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE );
  1013. bool showResult = ( iDbgResponse >= 1 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE );
  1014. // Look for match. verbose mode used to be at level 2, but disabled because the writers don't actually care for that info.
  1015. float scoreOfBestRule;
  1016. ResponseRulePartition::tIndex bestRule = FindBestMatchingRule( set,
  1017. ( iDbgResponse >= 3 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ),
  1018. scoreOfBestRule );
  1019. ResponseType_t responseType = RESPONSE_NONE;
  1020. ResponseParams rp;
  1021. char ruleName[ 128 ];
  1022. char responseName[ 128 ];
  1023. const char *context;
  1024. bool bcontexttoworld;
  1025. ruleName[ 0 ] = 0;
  1026. responseName[ 0 ] = 0;
  1027. context = NULL;
  1028. bcontexttoworld = false;
  1029. if ( m_RulePartitions.IsValid( bestRule ) )
  1030. {
  1031. Rule * RESTRICT r = &m_RulePartitions[ bestRule ];
  1032. ResponseSearchResult result;
  1033. if ( GetBestResponse( result, r, showResult, pFilter ) )
  1034. {
  1035. Q_strncpy( responseName, result.action->value, sizeof( responseName ) );
  1036. responseType = result.action->GetType();
  1037. rp = result.action->params;
  1038. rp.m_pFollowup = &result.action->m_followup;
  1039. }
  1040. Q_strncpy( ruleName, m_RulePartitions.GetElementName( bestRule ), sizeof( ruleName ) );
  1041. // Disable the rule if it only allows for matching one time
  1042. if ( r->IsMatchOnce() )
  1043. {
  1044. r->Disable();
  1045. }
  1046. context = r->GetContext();
  1047. bcontexttoworld = r->IsApplyContextToWorld();
  1048. response.SetMatchScore(scoreOfBestRule);
  1049. valid = true;
  1050. }
  1051. response.Init( responseType, responseName, rp, ruleName, context, bcontexttoworld );
  1052. if ( showResult )
  1053. {
  1054. /*
  1055. // clipped -- chet doesn't really want this info
  1056. if ( valid )
  1057. {
  1058. // Rescore the winner and dump to console
  1059. ScoreCriteriaAgainstRule( set, bestRule, true );
  1060. }
  1061. */
  1062. if ( valid || showRules )
  1063. {
  1064. const char *pConceptFilter = rr_debugresponseconcept.GetString();
  1065. // Describe the response, too
  1066. if ( V_strlen(pConceptFilter) > 0 && !rr_debugresponseconcept.GetBool() )
  1067. { // filter for only one concept
  1068. if ( V_stricmp(pConceptFilter, set.GetValue(set.FindCriterionIndex("concept")) ) == 0 )
  1069. {
  1070. response.Describe(&set);
  1071. } // else don't print
  1072. }
  1073. else
  1074. {
  1075. // maybe we need to filter *out* some concepts
  1076. if ( m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Head() ) )
  1077. {
  1078. // we are excluding at least one concept
  1079. CRR_Concept test( set.GetValue(set.FindCriterionIndex("concept")) );
  1080. if ( ! m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Find( test ) ) )
  1081. { // if not found in exclude list, then print
  1082. response.Describe(&set);
  1083. }
  1084. }
  1085. else
  1086. {
  1087. // describe everything
  1088. response.Describe(&set);
  1089. }
  1090. }
  1091. }
  1092. }
  1093. return valid;
  1094. }
  1095. //-----------------------------------------------------------------------------
  1096. //-----------------------------------------------------------------------------
  1097. void CResponseSystem::GetAllResponses( CUtlVector<CRR_Response> *pResponses )
  1098. {
  1099. for ( int i = 0; i < (int)m_Responses.Count(); i++ )
  1100. {
  1101. ResponseGroup &group = m_Responses[i];
  1102. for ( int j = 0; j < group.group.Count(); j++)
  1103. {
  1104. ParserResponse &response = group.group[j];
  1105. if ( response.type != RESPONSE_RESPONSE )
  1106. {
  1107. /*
  1108. CRR_Response *pResponse = new CRR_Response;
  1109. pResponse->Init( response.GetType(), response.value, CriteriaSet(), response.params, NULL, NULL, false );
  1110. pResponses->AddToTail(pResponse);
  1111. */
  1112. pResponses->Element(pResponses->AddToTail()).Init( response.GetType(), response.value, response.params, NULL, NULL, false );
  1113. }
  1114. }
  1115. }
  1116. }
  1117. void CResponseSystem::ParseInclude()
  1118. {
  1119. char includefile[ 256 ];
  1120. ParseToken();
  1121. Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token );
  1122. // check if the file is already included
  1123. if ( m_IncludedFiles.Find( includefile ) != NULL )
  1124. {
  1125. return;
  1126. }
  1127. MEM_ALLOC_CREDIT();
  1128. // Try and load it
  1129. CUtlBuffer buf;
  1130. if ( !IEngineEmulator::Get()->GetFilesystem()->ReadFile( includefile, "GAME", buf ) )
  1131. {
  1132. DevMsg( "Unable to load #included script %s\n", includefile );
  1133. return;
  1134. }
  1135. LoadFromBuffer( includefile, (const char *)buf.PeekGet() );
  1136. }
  1137. void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer )
  1138. {
  1139. COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Start", scriptfile );
  1140. m_IncludedFiles.Allocate( scriptfile );
  1141. PushScript( scriptfile, (unsigned char * )buffer );
  1142. if( rr_dumpresponses.GetBool() )
  1143. {
  1144. DevMsg("Reading: %s\n", scriptfile );
  1145. }
  1146. while ( 1 )
  1147. {
  1148. ParseToken();
  1149. if ( !token[0] )
  1150. {
  1151. break;
  1152. }
  1153. unsigned int hash = RR_HASH( token );
  1154. bool bSuccess = Dispatch( token, hash, m_FileDispatch );
  1155. if ( !bSuccess )
  1156. {
  1157. int byteoffset = m_ScriptStack[ 0 ].currenttoken - (const char *)m_ScriptStack[ 0 ].buffer;
  1158. Error( "CResponseSystem::LoadFromBuffer: Unknown entry type '%s', expecting 'response', 'criterion', 'enumeration' or 'rules' in file %s(offset:%i)\n",
  1159. token, scriptfile, byteoffset );
  1160. break;
  1161. }
  1162. }
  1163. if ( m_ScriptStack.Count() == 1 )
  1164. {
  1165. char cur[ 256 ];
  1166. GetCurrentScript( cur, sizeof( cur ) );
  1167. DevMsg( 1, "CResponseSystem: %s (%i rules, %i criteria, and %i responses)\n",
  1168. cur, m_RulePartitions.Count(), m_Criteria.Count(), m_Responses.Count() );
  1169. if( rr_dumpresponses.GetBool() )
  1170. {
  1171. DumpRules();
  1172. }
  1173. }
  1174. PopScript();
  1175. COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Finish", scriptfile );
  1176. }
  1177. //-----------------------------------------------------------------------------
  1178. // Purpose:
  1179. //-----------------------------------------------------------------------------
  1180. void CResponseSystem::LoadRuleSet( const char *basescript )
  1181. {
  1182. float flStart = Plat_FloatTime();
  1183. int length = 0;
  1184. unsigned char *buffer = (unsigned char *)IEngineEmulator::Get()->LoadFileForMe( basescript, &length );
  1185. if ( length <= 0 || !buffer )
  1186. {
  1187. DevMsg( 1, "CResponseSystem: failed to load %s\n", basescript );
  1188. return;
  1189. }
  1190. m_IncludedFiles.FreeAll();
  1191. LoadFromBuffer( basescript, (const char *)buffer );
  1192. IEngineEmulator::Get()->FreeFile( buffer );
  1193. Assert( m_ScriptStack.Count() == 0 );
  1194. float flEnd = Plat_FloatTime();
  1195. COM_TimestampedLog( "CResponseSystem::LoadRuleSet took %f msec", 1000.0f * ( flEnd - flStart ) );
  1196. }
  1197. inline ResponseType_t ComputeResponseType( const char *s )
  1198. {
  1199. switch ( s[ 0 ] )
  1200. {
  1201. default:
  1202. break;
  1203. case 's':
  1204. switch ( s[ 1 ] )
  1205. {
  1206. default:
  1207. break;
  1208. case 'c':
  1209. return RESPONSE_SCENE;
  1210. case 'e':
  1211. return RESPONSE_SENTENCE;
  1212. case 'p':
  1213. return RESPONSE_SPEAK;
  1214. }
  1215. break;
  1216. case 'r':
  1217. return RESPONSE_RESPONSE;
  1218. case 'p':
  1219. return RESPONSE_PRINT;
  1220. case 'e':
  1221. return RESPONSE_ENTITYIO;
  1222. }
  1223. return RESPONSE_NONE;
  1224. }
  1225. void CResponseSystem::ParseResponse_Weight( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1226. {
  1227. ParseToken();
  1228. newResponse.weight.SetFloat( (float)atof( token ) );
  1229. }
  1230. void CResponseSystem::ParseResponse_PreDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1231. {
  1232. ParseToken();
  1233. rp->flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK;
  1234. rp->predelay.FromInterval( ReadInterval( token ) );
  1235. }
  1236. void CResponseSystem::ParseResponse_NoDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1237. {
  1238. ParseToken();
  1239. rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1240. rp->delay.start = 0;
  1241. rp->delay.range = 0;
  1242. }
  1243. void CResponseSystem::ParseResponse_DefaultDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1244. {
  1245. rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1246. rp->delay.start = AIS_DEF_MIN_DELAY;
  1247. rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY );
  1248. }
  1249. void CResponseSystem::ParseResponse_Delay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1250. {
  1251. ParseToken();
  1252. rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1253. rp->delay.FromInterval( ReadInterval( token ) );
  1254. }
  1255. void CResponseSystem::ParseResponse_SpeakOnce( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1256. {
  1257. rp->flags |= AI_ResponseParams::RG_SPEAKONCE;
  1258. }
  1259. void CResponseSystem::ParseResponse_NoScene( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1260. {
  1261. rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE;
  1262. }
  1263. void CResponseSystem::ParseResponse_StopOnNonIdle( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1264. {
  1265. rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE;
  1266. }
  1267. void CResponseSystem::ParseResponse_Odds( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1268. {
  1269. ParseToken();
  1270. rp->flags |= AI_ResponseParams::RG_ODDS;
  1271. rp->odds = clamp( atoi( token ), 0, 100 );
  1272. }
  1273. void CResponseSystem::ParseResponse_RespeakDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1274. {
  1275. ParseToken();
  1276. rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY;
  1277. rp->respeakdelay.FromInterval( ReadInterval( token ) );
  1278. }
  1279. void CResponseSystem::ParseResponse_WeaponDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1280. {
  1281. ParseToken();
  1282. rp->flags |= AI_ResponseParams::RG_WEAPONDELAY;
  1283. rp->weapondelay.FromInterval( ReadInterval( token ) );
  1284. }
  1285. void CResponseSystem::ParseResponse_Soundlevel( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1286. {
  1287. ParseToken();
  1288. rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL;
  1289. rp->soundlevel = (soundlevel_t)TextToSoundLevel( token );
  1290. }
  1291. void CResponseSystem::ParseResponse_DisplayFirst( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1292. {
  1293. newResponse.first = true;
  1294. group.m_bHasFirst = true;
  1295. }
  1296. void CResponseSystem::ParseResponse_DisplayLast( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1297. {
  1298. newResponse.last = true;
  1299. group.m_bHasLast= true;
  1300. }
  1301. void CResponseSystem::ParseResponse_Fire( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1302. {
  1303. // get target name
  1304. bool bSuc = ParseToken();
  1305. if (!bSuc)
  1306. {
  1307. ResponseWarning( "FIRE token in response needs exactly three parameters." );
  1308. return;
  1309. }
  1310. newResponse.m_followup.followup_entityiotarget = ResponseCopyString(token);
  1311. bSuc = ParseToken();
  1312. if (!bSuc)
  1313. {
  1314. ResponseWarning( "FIRE token in response needs exactly three parameters." );
  1315. return;
  1316. }
  1317. newResponse.m_followup.followup_entityioinput = ResponseCopyString(token);
  1318. bSuc = ParseToken();
  1319. if (!bSuc)
  1320. {
  1321. ResponseWarning( "FIRE token in response needs exactly three parameters." );
  1322. return;
  1323. }
  1324. newResponse.m_followup.followup_entityiodelay = atof( token );
  1325. /*
  1326. m_followup.followup_entityioinput = ResponseCopyString(src.m_followup.followup_entityioinput);
  1327. m_followup.followup_entityiotarget = ResponseCopyString(src.m_followup.followup_entityiotarget);
  1328. */
  1329. }
  1330. void CResponseSystem::ParseResponse_Then( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  1331. {
  1332. // eg, "subject TALK_ANSWER saidunplant:1 3"
  1333. bool bSuc = ParseToken();
  1334. if (!bSuc)
  1335. {
  1336. AssertMsg(false, "THEN token in response lacked any further info.\n");
  1337. ResponseWarning( "THEN token in response lacked any further info.\n" );
  1338. return;
  1339. }
  1340. newResponse.m_followup.followup_target = ResponseCopyString(token);
  1341. bSuc = ParseToken(); // get another token
  1342. if (!bSuc)
  1343. {
  1344. AssertMsg1(false, "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target );
  1345. ResponseWarning( "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target );
  1346. return;
  1347. }
  1348. newResponse.m_followup.followup_concept = ResponseCopyString( token );
  1349. // Okay, this is totally asinine.
  1350. // Because the ParseToken() function will split foo:bar into three tokens
  1351. // (which is reasonable), but we have no safe way to parse the file otherwise
  1352. // because it's all behind an engine interface, it's necessary to parse all
  1353. // the tokens to the end of the line and catenate them, except for the last one
  1354. // which is the delay. That's crap.
  1355. bSuc = ParseToken();
  1356. if (!bSuc)
  1357. {
  1358. AssertMsg(false, "THEN token in response lacked contexts.\n");
  1359. ResponseWarning( "THEN token in response lacked contexts.\n" );
  1360. return;
  1361. }
  1362. // okay, as long as there is at least one more token, catenate the ones we
  1363. // see onto a temporary buffer. When we're down to the last token, that is
  1364. // the delay.
  1365. char buf[4096];
  1366. buf[0] = '\0';
  1367. while ( TokenWaiting() )
  1368. {
  1369. Q_strncat( buf, token, 4096 );
  1370. bSuc = ParseToken();
  1371. AssertMsg(bSuc, "Token parsing mysteriously failed.");
  1372. }
  1373. // down here, token is the last token, and buf is everything up to there.
  1374. newResponse.m_followup.followup_contexts = ResponseCopyString( buf );
  1375. newResponse.m_followup.followup_delay = atof( token );
  1376. }
  1377. void CResponseSystem::ParseOneResponse( const char *responseGroupName, ResponseGroup& group, ResponseParams *defaultParams )
  1378. {
  1379. ParserResponse &newResponse = group.group[ group.group.AddToTail() ];
  1380. newResponse.weight.SetFloat( 1.0f );
  1381. // inherit from group if appropriate
  1382. if (defaultParams)
  1383. {
  1384. newResponse.params = *defaultParams;
  1385. }
  1386. ResponseParams *rp = &newResponse.params;
  1387. newResponse.type = ComputeResponseType( token );
  1388. if ( RESPONSE_NONE == newResponse.type )
  1389. {
  1390. ResponseWarning( "response entry '%s' with unknown response type '%s'\n", responseGroupName, token );
  1391. return;
  1392. }
  1393. ParseToken();
  1394. newResponse.value = ResponseCopyString( token );
  1395. while ( TokenWaiting() )
  1396. {
  1397. ParseToken();
  1398. unsigned int hash = RR_HASH( token );
  1399. if ( DispatchParseResponse( token, hash, m_ResponseDispatch, newResponse, group, rp ) )
  1400. {
  1401. continue;
  1402. }
  1403. ResponseWarning( "response entry '%s' with unknown command '%s'\n", responseGroupName, token );
  1404. }
  1405. }
  1406. void CResponseSystem::ParseResponseGroup_Start( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1407. {
  1408. while ( 1 )
  1409. {
  1410. ParseToken();
  1411. if ( !Q_stricmp( token, "}" ) )
  1412. break;
  1413. if ( !Q_stricmp( token, "permitrepeats" ) )
  1414. {
  1415. newGroup.m_bDepleteBeforeRepeat = false;
  1416. continue;
  1417. }
  1418. else if ( !Q_stricmp( token, "sequential" ) )
  1419. {
  1420. newGroup.SetSequential( true );
  1421. continue;
  1422. }
  1423. else if ( !Q_stricmp( token, "norepeat" ) )
  1424. {
  1425. newGroup.SetNoRepeat( true );
  1426. continue;
  1427. }
  1428. ParseOneResponse( responseGroupName, newGroup );
  1429. }
  1430. }
  1431. void CResponseSystem::ParseResponseGroup_PreDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1432. {
  1433. ParseToken();
  1434. groupResponseParams.flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK;
  1435. groupResponseParams.predelay.FromInterval( ReadInterval( token ) );
  1436. }
  1437. void CResponseSystem::ParseResponseGroup_NoDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1438. {
  1439. ParseToken();
  1440. groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1441. groupResponseParams.delay.start = 0;
  1442. groupResponseParams.delay.range = 0;
  1443. }
  1444. void CResponseSystem::ParseResponseGroup_DefaultDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1445. {
  1446. groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1447. groupResponseParams.delay.start = AIS_DEF_MIN_DELAY;
  1448. groupResponseParams.delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY );
  1449. }
  1450. void CResponseSystem::ParseResponseGroup_Delay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1451. {
  1452. ParseToken();
  1453. groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK;
  1454. groupResponseParams.delay.FromInterval( ReadInterval( token ) );
  1455. }
  1456. void CResponseSystem::ParseResponseGroup_SpeakOnce( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1457. {
  1458. groupResponseParams.flags |= AI_ResponseParams::RG_SPEAKONCE;
  1459. }
  1460. void CResponseSystem::ParseResponseGroup_NoScene( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1461. {
  1462. groupResponseParams.flags |= AI_ResponseParams::RG_DONT_USE_SCENE;
  1463. }
  1464. void CResponseSystem::ParseResponseGroup_StopOnNonIdle( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1465. {
  1466. groupResponseParams.flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE;
  1467. }
  1468. void CResponseSystem::ParseResponseGroup_Odds( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1469. {
  1470. ParseToken();
  1471. groupResponseParams.flags |= AI_ResponseParams::RG_ODDS;
  1472. groupResponseParams.odds = clamp( atoi( token ), 0, 100 );
  1473. }
  1474. void CResponseSystem::ParseResponseGroup_RespeakDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1475. {
  1476. ParseToken();
  1477. groupResponseParams.flags |= AI_ResponseParams::RG_RESPEAKDELAY;
  1478. groupResponseParams.respeakdelay.FromInterval( ReadInterval( token ) );
  1479. }
  1480. void CResponseSystem::ParseResponseGroup_WeaponDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1481. {
  1482. ParseToken();
  1483. groupResponseParams.flags |= AI_ResponseParams::RG_WEAPONDELAY;
  1484. groupResponseParams.weapondelay.FromInterval( ReadInterval( token ) );
  1485. }
  1486. void CResponseSystem::ParseResponseGroup_Soundlevel( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams )
  1487. {
  1488. ParseToken();
  1489. groupResponseParams.flags |= AI_ResponseParams::RG_SOUNDLEVEL;
  1490. groupResponseParams.soundlevel = (soundlevel_t)TextToSoundLevel( token );
  1491. }
  1492. //-----------------------------------------------------------------------------
  1493. // Purpose:
  1494. //-----------------------------------------------------------------------------
  1495. void CResponseSystem::ParseResponse( void )
  1496. {
  1497. AI_ResponseParams groupResponseParams; // default response parameters inherited from single line format for group
  1498. // Should have groupname at start
  1499. ParseToken();
  1500. char responseGroupName[ 128 ];
  1501. Q_strncpy( responseGroupName, token, sizeof( responseGroupName ) );
  1502. int slot = m_Responses.Insert( responseGroupName );
  1503. ResponseGroup &newGroup = m_Responses[ slot ];
  1504. while ( 1 )
  1505. {
  1506. ParseToken();
  1507. unsigned int hash = RR_HASH( token );
  1508. // Oops, part of next definition
  1509. if( IsRootCommand( hash ) )
  1510. {
  1511. Unget();
  1512. break;
  1513. }
  1514. if ( DispatchParseResponseGroup( token, hash, m_ResponseGroupDispatch, responseGroupName, newGroup, groupResponseParams ) )
  1515. {
  1516. continue;
  1517. }
  1518. ParseOneResponse( responseGroupName, newGroup );
  1519. }
  1520. }
  1521. //-----------------------------------------------------------------------------
  1522. // Purpose:
  1523. // Input : *criterion -
  1524. //-----------------------------------------------------------------------------
  1525. int CResponseSystem::ParseOneCriterion( const char *criterionName )
  1526. {
  1527. char key[ 128 ];
  1528. char value[ 128 ];
  1529. Criteria *pNewCriterion = NULL;
  1530. int idx;
  1531. if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() )
  1532. {
  1533. static Criteria dummy;
  1534. pNewCriterion = &dummy;
  1535. ResponseWarning( "Multiple definitions for criteria '%s' [%d]\n", criterionName, RR_HASH( criterionName ) );
  1536. idx = m_Criteria.InvalidIndex();
  1537. }
  1538. else
  1539. {
  1540. idx = m_Criteria.Insert( criterionName );
  1541. pNewCriterion = &m_Criteria[ idx ];
  1542. }
  1543. bool gotbody = false;
  1544. while ( TokenWaiting() || !gotbody )
  1545. {
  1546. ParseToken();
  1547. // Oops, part of next definition
  1548. if( IsRootCommand() )
  1549. {
  1550. Unget();
  1551. break;
  1552. }
  1553. if ( !Q_stricmp( token, "{" ) )
  1554. {
  1555. gotbody = true;
  1556. while ( 1 )
  1557. {
  1558. ParseToken();
  1559. if ( !Q_stricmp( token, "}" ) )
  1560. break;
  1561. // Look up subcriteria index
  1562. int idx = m_Criteria.Find( token );
  1563. if ( idx != m_Criteria.InvalidIndex() )
  1564. {
  1565. pNewCriterion->subcriteria.AddToTail( idx );
  1566. }
  1567. else
  1568. {
  1569. ResponseWarning( "Skipping unrecongized subcriterion '%s' in '%s'\n", token, criterionName );
  1570. }
  1571. }
  1572. continue;
  1573. }
  1574. else if ( !Q_stricmp( token, "required" ) )
  1575. {
  1576. pNewCriterion->required = true;
  1577. }
  1578. else if ( !Q_stricmp( token, "weight" ) )
  1579. {
  1580. ParseToken();
  1581. pNewCriterion->weight.SetFloat( (float)atof( token ) );
  1582. }
  1583. else
  1584. {
  1585. Assert( pNewCriterion->subcriteria.Count() == 0 );
  1586. // Assume it's the math info for a non-subcriteria resposne
  1587. Q_strncpy( key, token, sizeof( key ) );
  1588. ParseToken();
  1589. Q_strncpy( value, token, sizeof( value ) );
  1590. V_strlower( key );
  1591. pNewCriterion->nameSym = CriteriaSet::ComputeCriteriaSymbol( key );
  1592. pNewCriterion->value = ResponseCopyString( value );
  1593. gotbody = true;
  1594. }
  1595. }
  1596. if ( !pNewCriterion->IsSubCriteriaType() )
  1597. {
  1598. ComputeMatcher( pNewCriterion, pNewCriterion->matcher );
  1599. }
  1600. return idx;
  1601. }
  1602. //-----------------------------------------------------------------------------
  1603. // Purpose:
  1604. // Input : *kv -
  1605. //-----------------------------------------------------------------------------
  1606. void CResponseSystem::ParseCriterion( void )
  1607. {
  1608. // Should have groupname at start
  1609. char criterionName[ 128 ];
  1610. ParseToken();
  1611. Q_strncpy( criterionName, token, sizeof( criterionName ) );
  1612. ParseOneCriterion( criterionName );
  1613. }
  1614. //-----------------------------------------------------------------------------
  1615. // Purpose:
  1616. // Input : *kv -
  1617. //-----------------------------------------------------------------------------
  1618. void CResponseSystem::ParseEnumeration( void )
  1619. {
  1620. char enumerationName[ 128 ];
  1621. ParseToken();
  1622. Q_strncpy( enumerationName, token, sizeof( enumerationName ) );
  1623. ParseToken();
  1624. if ( Q_stricmp( token, "{" ) )
  1625. {
  1626. ResponseWarning( "Expecting '{' in enumeration '%s', got '%s'\n", enumerationName, token );
  1627. return;
  1628. }
  1629. while ( 1 )
  1630. {
  1631. ParseToken();
  1632. if ( !Q_stricmp( token, "}" ) )
  1633. break;
  1634. if ( Q_strlen( token ) <= 0 )
  1635. {
  1636. ResponseWarning( "Expecting more tokens in enumeration '%s'\n", enumerationName );
  1637. break;
  1638. }
  1639. char key[ 128 ];
  1640. Q_strncpy( key, token, sizeof( key ) );
  1641. ParseToken();
  1642. float value = (float)atof( token );
  1643. char sz[ 128 ];
  1644. Q_snprintf( sz, sizeof( sz ), "[%s::%s]", enumerationName, key );
  1645. Q_strlower( sz );
  1646. Enumeration newEnum;
  1647. newEnum.value = value;
  1648. if ( m_Enumerations.Find( sz ) == m_Enumerations.InvalidIndex() )
  1649. {
  1650. m_Enumerations.Insert( sz, newEnum );
  1651. }
  1652. /*
  1653. else
  1654. {
  1655. ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz );
  1656. }
  1657. */
  1658. }
  1659. }
  1660. void CResponseSystem::ParseRule_MatchOnce( Rule &newRule )
  1661. {
  1662. newRule.m_bMatchOnce = true;
  1663. }
  1664. void CResponseSystem::ParseRule_ApplyContextToWorld( Rule &newRule )
  1665. {
  1666. newRule.m_bApplyContextToWorld = true;
  1667. }
  1668. void CResponseSystem::ParseRule_ApplyContext( Rule &newRule )
  1669. {
  1670. ParseToken();
  1671. if ( newRule.GetContext() == NULL )
  1672. {
  1673. newRule.SetContext( token );
  1674. }
  1675. else
  1676. {
  1677. CFmtStrN<1024> newContext( "%s,%s", newRule.GetContext(), token );
  1678. newRule.SetContext( newContext );
  1679. }
  1680. }
  1681. void CResponseSystem::ParseRule_Response( Rule &newRule )
  1682. {
  1683. // Read them until we run out.
  1684. while ( TokenWaiting() )
  1685. {
  1686. ParseToken();
  1687. int idx = m_Responses.Find( token );
  1688. if ( idx != m_Responses.InvalidIndex() )
  1689. {
  1690. MEM_ALLOC_CREDIT();
  1691. newRule.m_Responses.AddToTail( idx );
  1692. }
  1693. else
  1694. {
  1695. m_bParseRuleValid = false;
  1696. ResponseWarning( "No such response '%s' for rule '%s'\n", token, m_pParseRuleName );
  1697. }
  1698. }
  1699. }
  1700. /*
  1701. void CResponseSystem::ParseRule_ForceWeight( Rule &newRule )
  1702. {
  1703. ParseToken();
  1704. if ( token[0] == 0 )
  1705. {
  1706. // no token followed forceweight?
  1707. ResponseWarning( "Forceweight token in rule '%s' did not specify a numerical weight! Ignoring.\n", m_pParseRuleName );
  1708. }
  1709. else
  1710. {
  1711. newRule.m_nForceWeight = atoi(token);
  1712. if ( newRule.m_nForceWeight == 0 )
  1713. {
  1714. ResponseWarning( "Rule '%s' had forceweight '%s', which doesn't work out to a nonzero number. Ignoring.\n",
  1715. m_pParseRuleName, token );
  1716. }
  1717. }
  1718. }
  1719. */
  1720. void CResponseSystem::ParseRule_Criteria( Rule &newRule )
  1721. {
  1722. // Read them until we run out.
  1723. while ( TokenWaiting() )
  1724. {
  1725. ParseToken();
  1726. int idx = m_Criteria.Find( token );
  1727. if ( idx != m_Criteria.InvalidIndex() )
  1728. {
  1729. MEM_ALLOC_CREDIT();
  1730. newRule.m_Criteria.AddToTail( idx );
  1731. }
  1732. else
  1733. {
  1734. m_bParseRuleValid = false;
  1735. ResponseWarning( "No such criterion '%s' for rule '%s'\n", token, m_pParseRuleName );
  1736. }
  1737. }
  1738. }
  1739. //-----------------------------------------------------------------------------
  1740. // Purpose:
  1741. // Input : *kv -
  1742. //-----------------------------------------------------------------------------
  1743. void CResponseSystem::ParseRule( void )
  1744. {
  1745. static int instancedCriteria = 0;
  1746. char ruleName[ 128 ];
  1747. ParseToken();
  1748. Q_strncpy( ruleName, token, sizeof( ruleName ) );
  1749. ParseToken();
  1750. if ( Q_stricmp( token, "{" ) )
  1751. {
  1752. ResponseWarning( "Expecting '{' in rule '%s', got '%s'\n", ruleName, token );
  1753. return;
  1754. }
  1755. // entries are "criteria", "response" or an in-line criteria to instance
  1756. Rule *newRule = new Rule;
  1757. char sz[ 128 ];
  1758. m_bParseRuleValid = true;
  1759. m_pParseRuleName = ruleName;
  1760. while ( 1 )
  1761. {
  1762. ParseToken();
  1763. if ( !Q_stricmp( token, "}" ) )
  1764. {
  1765. break;
  1766. }
  1767. if ( Q_strlen( token ) <= 0 )
  1768. {
  1769. ResponseWarning( "Expecting more tokens in rule '%s'\n", ruleName );
  1770. break;
  1771. }
  1772. unsigned int hash = RR_HASH( token );
  1773. if ( DispatchParseRule( token, hash, m_RuleDispatch, *newRule ) )
  1774. continue;
  1775. // It's an inline criteria, generate a name and parse it in
  1776. Q_snprintf( sz, sizeof( sz ), "[%s%03i]", ruleName, ++instancedCriteria );
  1777. Unget();
  1778. int idx = ParseOneCriterion( sz );
  1779. if ( idx != m_Criteria.InvalidIndex() )
  1780. {
  1781. newRule->m_Criteria.AddToTail( idx );
  1782. }
  1783. }
  1784. if ( m_bParseRuleValid )
  1785. {
  1786. m_RulePartitions.GetDictForRule( this, newRule ).Insert( ruleName, newRule );
  1787. }
  1788. else
  1789. {
  1790. DevMsg( "Discarded rule %s\n", ruleName );
  1791. delete newRule;
  1792. }
  1793. }
  1794. //-----------------------------------------------------------------------------
  1795. // Purpose:
  1796. //-----------------------------------------------------------------------------
  1797. int CResponseSystem::GetCurrentToken() const
  1798. {
  1799. if ( m_ScriptStack.Count() <= 0 )
  1800. return -1;
  1801. return m_ScriptStack[ 0 ].tokencount;
  1802. }
  1803. void CResponseSystem::ResponseWarning( const char *fmt, ... )
  1804. {
  1805. va_list argptr;
  1806. char string[1024];
  1807. va_start (argptr, fmt);
  1808. Q_vsnprintf(string, sizeof(string), fmt,argptr);
  1809. va_end (argptr);
  1810. char cur[ 256 ];
  1811. GetCurrentScript( cur, sizeof( cur ) );
  1812. DevMsg( 1, "%s(token %i) : %s", cur, GetCurrentToken(), string );
  1813. }
  1814. //-----------------------------------------------------------------------------
  1815. // Purpose:
  1816. //-----------------------------------------------------------------------------
  1817. void CResponseSystem::CopyCriteriaFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem )
  1818. {
  1819. // Add criteria from this rule to global list in custom response system.
  1820. int nCriteriaCount = pSrcRule->m_Criteria.Count();
  1821. for ( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria )
  1822. {
  1823. int iSrcIndex = pSrcRule->m_Criteria[iCriteria];
  1824. Criteria *pSrcCriteria = &m_Criteria[iSrcIndex];
  1825. if ( pSrcCriteria )
  1826. {
  1827. int iIndex = pCustomSystem->m_Criteria.Find( m_Criteria.GetElementName( iSrcIndex ) );
  1828. if ( iIndex != pCustomSystem->m_Criteria.InvalidIndex() )
  1829. {
  1830. pDstRule->m_Criteria.AddToTail( iIndex );
  1831. continue;
  1832. }
  1833. // Add the criteria.
  1834. Criteria dstCriteria;
  1835. dstCriteria.nameSym = pSrcCriteria->nameSym ;
  1836. dstCriteria.value = ResponseCopyString( pSrcCriteria->value );
  1837. dstCriteria.weight = pSrcCriteria->weight;
  1838. dstCriteria.required = pSrcCriteria->required;
  1839. dstCriteria.matcher = pSrcCriteria->matcher;
  1840. int nSubCriteriaCount = pSrcCriteria->subcriteria.Count();
  1841. for ( int iSubCriteria = 0; iSubCriteria < nSubCriteriaCount; ++iSubCriteria )
  1842. {
  1843. int iSrcSubIndex = pSrcCriteria->subcriteria[iSubCriteria];
  1844. Criteria *pSrcSubCriteria = &m_Criteria[iSrcSubIndex];
  1845. if ( pSrcCriteria )
  1846. {
  1847. int iSubIndex = pCustomSystem->m_Criteria.Find( pSrcSubCriteria->value );
  1848. if ( iSubIndex != pCustomSystem->m_Criteria.InvalidIndex() )
  1849. continue;
  1850. // Add the criteria.
  1851. Criteria dstSubCriteria;
  1852. dstSubCriteria.nameSym = pSrcSubCriteria->nameSym ;
  1853. dstSubCriteria.value = ResponseCopyString( pSrcSubCriteria->value );
  1854. dstSubCriteria.weight = pSrcSubCriteria->weight;
  1855. dstSubCriteria.required = pSrcSubCriteria->required;
  1856. dstSubCriteria.matcher = pSrcSubCriteria->matcher;
  1857. int iSubInsertIndex = pCustomSystem->m_Criteria.Insert( pSrcSubCriteria->value, dstSubCriteria );
  1858. dstCriteria.subcriteria.AddToTail( iSubInsertIndex );
  1859. }
  1860. }
  1861. int iInsertIndex = pCustomSystem->m_Criteria.Insert( m_Criteria.GetElementName( iSrcIndex ), dstCriteria );
  1862. pDstRule->m_Criteria.AddToTail( iInsertIndex );
  1863. }
  1864. }
  1865. }
  1866. //-----------------------------------------------------------------------------
  1867. // Purpose:
  1868. //-----------------------------------------------------------------------------
  1869. void CResponseSystem::CopyResponsesFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem )
  1870. {
  1871. // Add responses from this rule to global list in custom response system.
  1872. int nResponseGroupCount = pSrcRule->m_Responses.Count();
  1873. for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup )
  1874. {
  1875. int iSrcResponseGroup = pSrcRule->m_Responses[iResponseGroup];
  1876. ResponseGroup *pSrcResponseGroup = &m_Responses[iSrcResponseGroup];
  1877. if ( pSrcResponseGroup )
  1878. {
  1879. // Add response group.
  1880. ResponseGroup dstResponseGroup;
  1881. dstResponseGroup.m_bDepleteBeforeRepeat = pSrcResponseGroup->m_bDepleteBeforeRepeat;
  1882. dstResponseGroup.m_nDepletionCount = pSrcResponseGroup->m_nDepletionCount;
  1883. dstResponseGroup.m_bHasFirst = pSrcResponseGroup->m_bHasFirst;
  1884. dstResponseGroup.m_bHasLast = pSrcResponseGroup->m_bHasLast;
  1885. dstResponseGroup.m_bSequential = pSrcResponseGroup->m_bSequential;
  1886. dstResponseGroup.m_bNoRepeat = pSrcResponseGroup->m_bNoRepeat;
  1887. dstResponseGroup.m_bEnabled = pSrcResponseGroup->m_bEnabled;
  1888. dstResponseGroup.m_nCurrentIndex = pSrcResponseGroup->m_nCurrentIndex;
  1889. int nSrcResponseCount = pSrcResponseGroup->group.Count();
  1890. for ( int iResponse = 0; iResponse < nSrcResponseCount; ++iResponse )
  1891. {
  1892. ParserResponse *pSrcResponse = &pSrcResponseGroup->group[iResponse];
  1893. if ( pSrcResponse )
  1894. {
  1895. // Add Response
  1896. ParserResponse dstResponse;
  1897. dstResponse.weight = pSrcResponse->weight;
  1898. dstResponse.type = pSrcResponse->type;
  1899. dstResponse.value = ResponseCopyString( pSrcResponse->value );
  1900. dstResponse.depletioncount = pSrcResponse->depletioncount;
  1901. dstResponse.first = pSrcResponse->first;
  1902. dstResponse.last = pSrcResponse->last;
  1903. dstResponseGroup.group.AddToTail( dstResponse );
  1904. }
  1905. }
  1906. int iInsertIndex = pCustomSystem->m_Responses.Insert( m_Responses.GetElementName( iSrcResponseGroup ), dstResponseGroup );
  1907. pDstRule->m_Responses.AddToTail( iInsertIndex );
  1908. }
  1909. }
  1910. }
  1911. //-----------------------------------------------------------------------------
  1912. // Purpose:
  1913. //-----------------------------------------------------------------------------
  1914. void CResponseSystem::CopyEnumerationsFrom( CResponseSystem *pCustomSystem )
  1915. {
  1916. int nEnumerationCount = m_Enumerations.Count();
  1917. for ( int iEnumeration = 0; iEnumeration < nEnumerationCount; ++iEnumeration )
  1918. {
  1919. Enumeration *pSrcEnumeration = &m_Enumerations[iEnumeration];
  1920. if ( pSrcEnumeration )
  1921. {
  1922. Enumeration dstEnumeration;
  1923. dstEnumeration.value = pSrcEnumeration->value;
  1924. pCustomSystem->m_Enumerations.Insert( m_Enumerations.GetElementName( iEnumeration ), dstEnumeration );
  1925. }
  1926. }
  1927. }
  1928. //-----------------------------------------------------------------------------
  1929. // Purpose:
  1930. //-----------------------------------------------------------------------------
  1931. void CResponseSystem::CopyRuleFrom( Rule *pSrcRule, ResponseRulePartition::tIndex iRule, CResponseSystem *pCustomSystem )
  1932. {
  1933. // Verify data.
  1934. Assert( pSrcRule );
  1935. Assert( pCustomSystem );
  1936. if ( !pSrcRule || !pCustomSystem )
  1937. return;
  1938. // New rule
  1939. Rule *dstRule = new Rule;
  1940. dstRule->SetContext( pSrcRule->GetContext() );
  1941. dstRule->m_bMatchOnce = pSrcRule->m_bMatchOnce;
  1942. dstRule->m_bEnabled = pSrcRule->m_bEnabled;
  1943. dstRule->m_bApplyContextToWorld = pSrcRule->m_bApplyContextToWorld;
  1944. // Copy off criteria.
  1945. CopyCriteriaFrom( pSrcRule, dstRule, pCustomSystem );
  1946. // Copy off responses.
  1947. CopyResponsesFrom( pSrcRule, dstRule, pCustomSystem );
  1948. // Copy off enumerations - Don't think we use these.
  1949. // CopyEnumerationsFrom( pCustomSystem );
  1950. // Add rule.
  1951. pCustomSystem->m_RulePartitions.GetDictForRule( this, dstRule ).Insert( m_RulePartitions.GetElementName( iRule ), dstRule );
  1952. }
  1953. //-----------------------------------------------------------------------------
  1954. //-----------------------------------------------------------------------------
  1955. void CResponseSystem::DumpRules()
  1956. {
  1957. for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ;
  1958. m_RulePartitions.IsValid(idx) ;
  1959. idx = m_RulePartitions.Next(idx) )
  1960. {
  1961. Msg("%s\n", m_RulePartitions.GetElementName( idx ) );
  1962. }
  1963. }
  1964. //-----------------------------------------------------------------------------
  1965. //-----------------------------------------------------------------------------
  1966. void CResponseSystem::DumpDictionary( const char *pszName )
  1967. {
  1968. Msg( "\nDictionary: %s\n", pszName );
  1969. // int nRuleCount = m_Rules.Count();
  1970. // for ( int iRule = 0; iRule < nRuleCount; ++iRule )
  1971. for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ;
  1972. m_RulePartitions.IsValid(idx) ;
  1973. idx = m_RulePartitions.Next(idx) )
  1974. {
  1975. Msg(" Rule %d/%d: %s\n", m_RulePartitions.BucketFromIdx(idx), m_RulePartitions.PartFromIdx( idx ), m_RulePartitions.GetElementName( idx ) );
  1976. Rule *pRule = &m_RulePartitions[idx];
  1977. int nCriteriaCount = pRule->m_Criteria.Count();
  1978. for( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria )
  1979. {
  1980. int iRuleCriteria = pRule->m_Criteria[iCriteria];
  1981. Criteria *pCriteria = &m_Criteria[iRuleCriteria];
  1982. Msg( " Criteria %d: %s %s\n", iCriteria, CriteriaSet::SymbolToStr(pCriteria->nameSym), pCriteria->value );
  1983. }
  1984. int nResponseGroupCount = pRule->m_Responses.Count();
  1985. for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup )
  1986. {
  1987. int iRuleResponse = pRule->m_Responses[iResponseGroup];
  1988. ResponseGroup *pResponseGroup = &m_Responses[iRuleResponse];
  1989. Msg( " ResponseGroup %d: %s\n", iResponseGroup, m_Responses.GetElementName( iRuleResponse ) );
  1990. int nResponseCount = pResponseGroup->group.Count();
  1991. for ( int iResponse = 0; iResponse < nResponseCount; ++iResponse )
  1992. {
  1993. ParserResponse *pResponse = &pResponseGroup->group[iResponse];
  1994. Msg( " Response %d: %s\n", iResponse, pResponse->value );
  1995. }
  1996. }
  1997. }
  1998. }
  1999. void CResponseSystem::BuildDispatchTables()
  2000. {
  2001. m_RootCommandHashes.Insert( RR_HASH( "#include" ) );
  2002. m_RootCommandHashes.Insert( RR_HASH( "response" ) );
  2003. m_RootCommandHashes.Insert( RR_HASH( "enumeration" ) );
  2004. m_RootCommandHashes.Insert( RR_HASH( "criterion" ) );
  2005. m_RootCommandHashes.Insert( RR_HASH( "criteria" ) );
  2006. m_RootCommandHashes.Insert( RR_HASH( "rule" ) );
  2007. m_FileDispatch.Insert( RR_HASH( "#include" ), &CResponseSystem::ParseInclude );
  2008. m_FileDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseResponse );
  2009. m_FileDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseCriterion );
  2010. m_FileDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseCriterion );
  2011. m_FileDispatch.Insert( RR_HASH( "rule" ), &CResponseSystem::ParseRule );
  2012. m_FileDispatch.Insert( RR_HASH( "enumeration" ), &CResponseSystem::ParseEnumeration );
  2013. m_RuleDispatch.Insert( RR_HASH( "matchonce" ), &CResponseSystem::ParseRule_MatchOnce );
  2014. m_RuleDispatch.Insert( RR_HASH( "applycontexttoworld" ), &CResponseSystem::ParseRule_ApplyContextToWorld );
  2015. m_RuleDispatch.Insert( RR_HASH( "applycontext" ), &CResponseSystem::ParseRule_ApplyContext );
  2016. m_RuleDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseRule_Response );
  2017. // m_RuleDispatch.Insert( RR_HASH( "forceweight" ), &CResponseSystem::ParseRule_ForceWeight );
  2018. m_RuleDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseRule_Criteria );
  2019. m_RuleDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseRule_Criteria );
  2020. m_ResponseDispatch.Insert( RR_HASH( "weight" ), &CResponseSystem::ParseResponse_Weight );
  2021. m_ResponseDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponse_PreDelay );
  2022. m_ResponseDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponse_NoDelay );
  2023. m_ResponseDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponse_DefaultDelay );
  2024. m_ResponseDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponse_Delay );
  2025. m_ResponseDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponse_SpeakOnce );
  2026. m_ResponseDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponse_NoScene );
  2027. m_ResponseDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponse_StopOnNonIdle );
  2028. m_ResponseDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponse_Odds );
  2029. m_ResponseDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponse_RespeakDelay );
  2030. m_ResponseDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponse_WeaponDelay );
  2031. m_ResponseDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponse_Soundlevel );
  2032. m_ResponseDispatch.Insert( RR_HASH( "displayfirst" ), &CResponseSystem::ParseResponse_DisplayFirst );
  2033. m_ResponseDispatch.Insert( RR_HASH( "displaylast" ), &CResponseSystem::ParseResponse_DisplayLast );
  2034. m_ResponseDispatch.Insert( RR_HASH( "fire" ), &CResponseSystem::ParseResponse_Fire );
  2035. m_ResponseDispatch.Insert( RR_HASH( "then" ), &CResponseSystem::ParseResponse_Then );
  2036. m_ResponseGroupDispatch.Insert( RR_HASH( "{" ), &CResponseSystem::ParseResponseGroup_Start );
  2037. m_ResponseGroupDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponseGroup_PreDelay );
  2038. m_ResponseGroupDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponseGroup_NoDelay );
  2039. m_ResponseGroupDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponseGroup_DefaultDelay );
  2040. m_ResponseGroupDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponseGroup_Delay );
  2041. m_ResponseGroupDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponseGroup_SpeakOnce );
  2042. m_ResponseGroupDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponseGroup_NoScene );
  2043. m_ResponseGroupDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponseGroup_StopOnNonIdle );
  2044. m_ResponseGroupDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponseGroup_Odds );
  2045. m_ResponseGroupDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponseGroup_RespeakDelay );
  2046. m_ResponseGroupDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponseGroup_WeaponDelay );
  2047. m_ResponseGroupDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponseGroup_Soundlevel );
  2048. }
  2049. bool CResponseSystem::Dispatch( char const *pToken, unsigned int uiHash, CResponseSystem::DispatchMap_t &rMap )
  2050. {
  2051. int slot = rMap.Find( uiHash );
  2052. if ( slot != rMap.InvalidIndex() )
  2053. {
  2054. CResponseSystem::pfnResponseDispatch dispatch = rMap[ slot ];
  2055. (this->*dispatch)();
  2056. return true;
  2057. }
  2058. return false;
  2059. }
  2060. bool CResponseSystem::DispatchParseRule( char const *pToken, unsigned int uiHash, ParseRuleDispatchMap_t &rMap, Rule &newRule )
  2061. {
  2062. int slot = rMap.Find( uiHash );
  2063. if ( slot != rMap.InvalidIndex() )
  2064. {
  2065. CResponseSystem::pfnParseRuleDispatch dispatch = rMap[ slot ];
  2066. (this->*dispatch)( newRule );
  2067. return true;
  2068. }
  2069. return false;
  2070. }
  2071. bool CResponseSystem::DispatchParseResponse( char const *pToken, unsigned int uiHash, ParseResponseDispatchMap_t &rMap, ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp )
  2072. {
  2073. int slot = rMap.Find( uiHash );
  2074. if ( slot != rMap.InvalidIndex() )
  2075. {
  2076. CResponseSystem::pfnParseResponseDispatch dispatch = rMap[ slot ];
  2077. (this->*dispatch)( newResponse, group, rp );
  2078. return true;
  2079. }
  2080. return false;
  2081. }
  2082. bool CResponseSystem::DispatchParseResponseGroup( char const *pToken, unsigned int uiHash, ParseResponseGroupDispatchMap_t &rMap, char const *responseGroupName, ResponseGroup& newGroup, AI_ResponseParams &groupResponseParams )
  2083. {
  2084. int slot = rMap.Find( uiHash );
  2085. if ( slot != rMap.InvalidIndex() )
  2086. {
  2087. CResponseSystem::pfnParseResponseGroupDispatch dispatch = rMap[ slot ];
  2088. (this->*dispatch)( responseGroupName, newGroup, groupResponseParams );
  2089. return true;
  2090. }
  2091. return false;
  2092. }
  2093. unsigned int ResponseRulePartition::GetBucketForSpeakerAndConcept( const char *pszSpeaker, const char *pszConcept, const char *pszSubject )
  2094. {
  2095. // make sure is a power of two
  2096. COMPILE_TIME_ASSERT( ( N_RESPONSE_PARTITIONS & ( N_RESPONSE_PARTITIONS - 1 ) ) == 0 );
  2097. // hash together the speaker and concept strings, and mask off by the bucket mask
  2098. unsigned hashSpeaker = 0; // pszSpeaker ? HashStringCaseless( pszSpeaker ) : 0;
  2099. unsigned hashConcept = pszConcept ? HashStringCaseless( pszConcept ) : 0;
  2100. unsigned hashSubject = pszSubject ? HashStringCaseless( pszSubject ) : 0;
  2101. unsigned hashBrowns = ( ( hashSubject >> 3 ) ^ (hashSpeaker >> 1) ^ hashConcept ) & ( N_RESPONSE_PARTITIONS - 1 );
  2102. return hashBrowns;
  2103. }
  2104. const char *Rule::GetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem, const CUtlSymbol &pCritNameSym )
  2105. {
  2106. const char * retval = NULL;
  2107. // for each rule criterion...
  2108. for ( int i = 0 ; i < m_Criteria.Count() ; ++i )
  2109. {
  2110. retval = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym );
  2111. if ( retval != NULL )
  2112. {
  2113. // we found a result, early out
  2114. break;
  2115. }
  2116. }
  2117. return retval;
  2118. }
  2119. const Criteria *Rule::GetPointerForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym )
  2120. {
  2121. const Criteria * retval = NULL;
  2122. // for each rule criterion...
  2123. for ( int i = 0 ; i < m_Criteria.Count() ; ++i )
  2124. {
  2125. retval = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym );
  2126. if ( retval != NULL )
  2127. {
  2128. // we found a result, early out
  2129. break;
  2130. }
  2131. }
  2132. return retval;
  2133. }
  2134. const char *Rule::RecursiveGetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem,
  2135. const Criteria * RESTRICT pCrit, const CUtlSymbol &pCritNameSym )
  2136. {
  2137. Assert( pCrit );
  2138. if ( !pCrit ) return NULL;
  2139. if ( pCrit->IsSubCriteriaType() )
  2140. {
  2141. // test each of the children (depth first)
  2142. const char *pRet = NULL;
  2143. for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i )
  2144. {
  2145. pRet = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym );
  2146. if ( pRet ) // if found something, early out
  2147. return pRet;
  2148. }
  2149. }
  2150. else // leaf criterion
  2151. {
  2152. if ( pCrit->nameSym == pCritNameSym )
  2153. {
  2154. return pCrit->value;
  2155. }
  2156. else
  2157. {
  2158. return NULL;
  2159. }
  2160. }
  2161. return NULL;
  2162. }
  2163. const Criteria *Rule::RecursiveGetPointerForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym )
  2164. {
  2165. Assert( pCrit );
  2166. if ( !pCrit ) return NULL;
  2167. if ( pCrit->IsSubCriteriaType() )
  2168. {
  2169. // test each of the children (depth first)
  2170. const Criteria *pRet = NULL;
  2171. for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i )
  2172. {
  2173. pRet = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym );
  2174. if ( pRet ) // if found something, early out
  2175. return pRet;
  2176. }
  2177. }
  2178. else // leaf criterion
  2179. {
  2180. if ( pCrit->nameSym == pCritNameSym )
  2181. {
  2182. return pCrit;
  2183. }
  2184. else
  2185. {
  2186. return NULL;
  2187. }
  2188. }
  2189. return NULL;
  2190. }
  2191. #if RESPONSE_RULES_DEBUG_ENABLED
  2192. static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args )
  2193. {
  2194. // shouldn't use this extern elsewhere -- it's meant to be a hidden
  2195. // implementation detail
  2196. extern CRR_ConceptSymbolTable *g_pRRConceptTable;
  2197. Assert( g_pRRConceptTable );
  2198. if ( !g_pRRConceptTable ) return;
  2199. // different things for different argument lengths
  2200. switch ( args.ArgC() )
  2201. {
  2202. case 0:
  2203. {
  2204. AssertMsg( args.ArgC() > 0, "WTF error in ccommand parsing: zero arguments!\n" );
  2205. return;
  2206. }
  2207. case 1:
  2208. {
  2209. // print usage info
  2210. Msg("Usage: rr_debugresponseconcept_exclude Concept1 Concept2 Concept3...\n");
  2211. Msg("\tseparate multiple concepts with spaces.\n");
  2212. Msg("\tcall with no arguments to see this message and a list of current excludes.\n");
  2213. Msg("\tto reset the exclude list, type \"rr_debugresponseconcept_exclude !\"\n");
  2214. // print current excludes
  2215. Msg("\nCurrent exclude list:\n");
  2216. if ( !CResponseSystem::m_DebugExcludeList.IsValidIndex( CResponseSystem::m_DebugExcludeList.Head() ) )
  2217. {
  2218. Msg("\t<none>\n");
  2219. }
  2220. else
  2221. {
  2222. CResponseSystem::ExcludeList_t::IndexLocalType_t i;
  2223. for ( i = CResponseSystem::m_DebugExcludeList.Head() ;
  2224. CResponseSystem::m_DebugExcludeList.IsValidIndex(i) ;
  2225. i = CResponseSystem::m_DebugExcludeList.Next(i) )
  2226. {
  2227. Msg( "\t%s\n", CResponseSystem::m_DebugExcludeList[i].GetStringConcept() );
  2228. }
  2229. }
  2230. return;
  2231. }
  2232. case 2:
  2233. // deal with the erase operator
  2234. if ( args[1][0] == '!' )
  2235. {
  2236. CResponseSystem::m_DebugExcludeList.Purge();
  2237. Msg( "Exclude list emptied.\n" );
  2238. return;
  2239. }
  2240. // else, FALL THROUGH:
  2241. default:
  2242. // add each arg to the exclude list
  2243. for ( int i = 1 ; i < args.ArgC() ; ++i )
  2244. {
  2245. if ( !g_pRRConceptTable->Find(args[i]).IsValid() )
  2246. {
  2247. Msg( "\t'%s' is not a known concept (adding it anyway)\n", args[i] );
  2248. }
  2249. CRR_Concept concept( args[i] );
  2250. CResponseSystem::m_DebugExcludeList.AddToTail( concept );
  2251. }
  2252. }
  2253. }
  2254. #endif
  2255. #if RR_DUMPHASHINFO_ENABLED
  2256. static void CC_RR_DumpHashInfo( const CCommand &args )
  2257. {
  2258. defaultresponsesytem.m_InstancedSystems[0]->m_RulePartitions.PrintBucketInfo( defaultresponsesytem.m_InstancedSystems[0] );
  2259. }
  2260. static ConCommand rr_dumphashinfo( "rr_dumphashinfo", CC_RR_DumpHashInfo, "Statistics on primary hash bucketing of response rule partitions");
  2261. void ResponseRulePartition::PrintBucketInfo( CResponseSystem *pSys )
  2262. {
  2263. struct bucktuple_t
  2264. {
  2265. int nBucket;
  2266. int nCount;
  2267. bucktuple_t() : nBucket(-1), nCount(-1) {};
  2268. bucktuple_t( int bucket, int count ) : nBucket(bucket), nCount(count) {};
  2269. static int __cdecl SortCompare( const bucktuple_t * a, const bucktuple_t * b )
  2270. {
  2271. return a->nCount - b->nCount;
  2272. }
  2273. };
  2274. CUtlVector<bucktuple_t> infos( N_RESPONSE_PARTITIONS, N_RESPONSE_PARTITIONS );
  2275. float nAverage = 0;
  2276. for ( int i = 0 ; i < N_RESPONSE_PARTITIONS ; ++i )
  2277. {
  2278. int count = m_RuleParts[i].Count();
  2279. infos.AddToTail( bucktuple_t( i, count ) );
  2280. nAverage += count;
  2281. }
  2282. nAverage /= N_RESPONSE_PARTITIONS;
  2283. infos.Sort( bucktuple_t::SortCompare );
  2284. Msg( "%d buckets, %d total, %.2f average size\n", N_RESPONSE_PARTITIONS, Count(), nAverage );
  2285. Msg( "8 shortest buckets:\n" );
  2286. for ( int i = 0 ; i < 8 ; ++i )
  2287. {
  2288. Msg("\t%d: %d\n", infos[i].nBucket, infos[i].nCount );
  2289. }
  2290. Msg( "8 longest buckets:\n" );
  2291. for ( int i = infos.Count() - 1 ; i >= infos.Count() - 9 ; --i )
  2292. {
  2293. Msg("\t%d: %d\n", infos[i].nBucket, infos[i].nCount );
  2294. }
  2295. int nempty = 0;
  2296. for ( nempty = 0 ; nempty < infos.Count() ; ++nempty )
  2297. {
  2298. if ( infos[nempty].nCount != 0 )
  2299. break;
  2300. }
  2301. Msg( "%d empty buckets\n", nempty );
  2302. /*
  2303. Msg( " Contents of longest bucket\nwho\tconcept\n" );
  2304. tRuleDict &bucket = m_RuleParts[infos[infos.Count()-1].nBucket];
  2305. for ( tRuleDict::IndexType_t i = bucket.FirstInorder(); bucket.IsValidIndex(i); i = bucket.NextInorder(i) )
  2306. {
  2307. Rule &rule = bucket.Element(i) ;
  2308. Msg("%s\t%s\n", rule.GetValueForRuleCriterionByName( pSys, "who" ), rule.GetValueForRuleCriterionByName( pSys, CriteriaSet::ComputeCriteriaSymbol("concept") ) );
  2309. }
  2310. */
  2311. }
  2312. #endif