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

501 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "AI_Criteria.h"
  10. #include "ai_speech.h"
  11. #include <KeyValues.h>
  12. #include "engine/IEngineSound.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include <tier0/memdbgon.h>
  15. //-----------------------------------------------------------------------------
  16. // Purpose:
  17. //-----------------------------------------------------------------------------
  18. AI_CriteriaSet::AI_CriteriaSet() : m_Lookup( 0, 0, CritEntry_t::LessFunc )
  19. {
  20. }
  21. //-----------------------------------------------------------------------------
  22. // Purpose:
  23. // Input : src -
  24. //-----------------------------------------------------------------------------
  25. AI_CriteriaSet::AI_CriteriaSet( const AI_CriteriaSet& src ) : m_Lookup( 0, 0, CritEntry_t::LessFunc )
  26. {
  27. // Use fast Copy CUtlRBTree CopyFrom. WARNING: It only handles POD.
  28. m_Lookup.CopyFrom( src.m_Lookup );
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Purpose:
  32. //-----------------------------------------------------------------------------
  33. AI_CriteriaSet::~AI_CriteriaSet()
  34. {
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose:
  38. // Input : *criteria -
  39. // "" -
  40. // 1.0f -
  41. //-----------------------------------------------------------------------------
  42. void AI_CriteriaSet::AppendCriteria( const char *criteria, const char *value /*= ""*/, float weight /*= 1.0f*/ )
  43. {
  44. // Note: value pointer may come from an entry inside m_Lookup!
  45. // that value string must be copied out before any modification
  46. // to the m_Lookup struct which could make the pointer invalid
  47. int idx = FindCriterionIndex( criteria );
  48. if ( idx == -1 )
  49. {
  50. CritEntry_t entry;
  51. entry.criterianame = criteria;
  52. MEM_ALLOC_CREDIT();
  53. entry.SetValue(value);
  54. entry.weight = weight;
  55. m_Lookup.Insert( entry );
  56. }
  57. else
  58. {
  59. CritEntry_t *entry = &m_Lookup[ idx ];
  60. entry->SetValue( value );
  61. entry->weight = weight;
  62. }
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Removes criteria in a set
  66. //-----------------------------------------------------------------------------
  67. void AI_CriteriaSet::RemoveCriteria( const char *criteria )
  68. {
  69. int idx = FindCriterionIndex( criteria );
  70. if ( idx == -1 )
  71. return;
  72. m_Lookup.RemoveAt( idx );
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose:
  76. // Output : int
  77. //-----------------------------------------------------------------------------
  78. int AI_CriteriaSet::GetCount() const
  79. {
  80. return m_Lookup.Count();
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose:
  84. // Input : *name -
  85. // Output : int
  86. //-----------------------------------------------------------------------------
  87. int AI_CriteriaSet::FindCriterionIndex( const char *name ) const
  88. {
  89. CritEntry_t search;
  90. search.criterianame = name;
  91. int idx = m_Lookup.Find( search );
  92. if ( idx == m_Lookup.InvalidIndex() )
  93. return -1;
  94. return idx;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. // Input : index -
  99. // Output : char const
  100. //-----------------------------------------------------------------------------
  101. const char *AI_CriteriaSet::GetName( int index ) const
  102. {
  103. static char namebuf[ 128 ];
  104. if ( index < 0 || index >= (int)m_Lookup.Count() )
  105. return "";
  106. const CritEntry_t *entry = &m_Lookup[ index ];
  107. Q_strncpy( namebuf, entry->criterianame.String(), sizeof( namebuf ) );
  108. return namebuf;
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Purpose:
  112. // Input : index -
  113. // Output : char const
  114. //-----------------------------------------------------------------------------
  115. const char *AI_CriteriaSet::GetValue( int index ) const
  116. {
  117. if ( index < 0 || index >= (int)m_Lookup.Count() )
  118. return "";
  119. const CritEntry_t *entry = &m_Lookup[ index ];
  120. return entry->value ? entry->value : "";
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose:
  124. // Input : index -
  125. // Output : float
  126. //-----------------------------------------------------------------------------
  127. float AI_CriteriaSet::GetWeight( int index ) const
  128. {
  129. if ( index < 0 || index >= (int)m_Lookup.Count() )
  130. return 1.0f;
  131. const CritEntry_t *entry = &m_Lookup[ index ];
  132. return entry->weight;
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose:
  136. //-----------------------------------------------------------------------------
  137. void AI_CriteriaSet::Describe()
  138. {
  139. for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) )
  140. {
  141. CritEntry_t *entry = &m_Lookup[ i ];
  142. if ( entry->weight != 1.0f )
  143. {
  144. DevMsg( " %20s = '%s' (weight %f)\n", entry->criterianame.String(), entry->value ? entry->value : "", entry->weight );
  145. }
  146. else
  147. {
  148. DevMsg( " %20s = '%s'\n", entry->criterianame.String(), entry->value ? entry->value : "" );
  149. }
  150. }
  151. }
  152. BEGIN_SIMPLE_DATADESC( AI_ResponseParams )
  153. DEFINE_FIELD( flags, FIELD_SHORT ),
  154. DEFINE_FIELD( odds, FIELD_SHORT ),
  155. DEFINE_FIELD( soundlevel, FIELD_CHARACTER ),
  156. DEFINE_FIELD( delay, FIELD_INTEGER ), // These are compressed down to two float16s, so treat as an INT for saverestore
  157. DEFINE_FIELD( respeakdelay, FIELD_INTEGER ), //
  158. END_DATADESC()
  159. BEGIN_SIMPLE_DATADESC( AI_Response )
  160. DEFINE_FIELD( m_Type, FIELD_CHARACTER ),
  161. DEFINE_ARRAY( m_szResponseName, FIELD_CHARACTER, AI_Response::MAX_RESPONSE_NAME ),
  162. DEFINE_ARRAY( m_szMatchingRule, FIELD_CHARACTER, AI_Response::MAX_RULE_NAME ),
  163. // DEFINE_FIELD( m_pCriteria, FIELD_??? ), // Don't need to save this probably
  164. DEFINE_EMBEDDED( m_Params ),
  165. END_DATADESC()
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. AI_Response::AI_Response()
  170. {
  171. m_Type = RESPONSE_NONE;
  172. m_szResponseName[0] = 0;
  173. m_szMatchingRule[0] = 0;
  174. m_pCriteria = NULL;
  175. m_bApplyContextToWorld = false;
  176. }
  177. //-----------------------------------------------------------------------------
  178. //-----------------------------------------------------------------------------
  179. AI_Response::AI_Response( const AI_Response &from )
  180. {
  181. m_pCriteria = NULL;
  182. *this = from;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose:
  186. //-----------------------------------------------------------------------------
  187. AI_Response::~AI_Response()
  188. {
  189. delete m_pCriteria;
  190. m_pCriteria = NULL;
  191. }
  192. //-----------------------------------------------------------------------------
  193. AI_Response &AI_Response::operator=( const AI_Response &from )
  194. {
  195. Assert( (void*)(&m_Type) == (void*)this );
  196. if (this == &from)
  197. return *this;
  198. m_Type = from.m_Type;
  199. V_strcpy_safe( m_szResponseName, from.m_szResponseName );
  200. V_strcpy_safe( m_szMatchingRule, from.m_szMatchingRule );
  201. delete m_pCriteria;
  202. m_pCriteria = NULL;
  203. // Copy criteria.
  204. if (from.m_pCriteria)
  205. m_pCriteria = new AI_CriteriaSet(*from.m_pCriteria);
  206. m_Params = from.m_Params;
  207. m_szContext = from.m_szContext;
  208. m_bApplyContextToWorld = from.m_bApplyContextToWorld;
  209. return *this;
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose:
  213. // Input : *response -
  214. // *criteria -
  215. //-----------------------------------------------------------------------------
  216. void AI_Response::Init( ResponseType_t type, const char *responseName, const AI_CriteriaSet& criteria,
  217. const AI_ResponseParams& responseparams, const char *ruleName, const char *applyContext,
  218. bool bApplyContextToWorld )
  219. {
  220. m_Type = type;
  221. V_strcpy_safe( m_szResponseName, responseName );
  222. V_strcpy_safe( m_szMatchingRule, ruleName ? ruleName : "NULL" );
  223. // Copy underlying criteria
  224. Assert( !m_pCriteria );
  225. m_pCriteria = new AI_CriteriaSet( criteria );
  226. m_Params = responseparams;
  227. m_szContext = applyContext;
  228. m_bApplyContextToWorld = bApplyContextToWorld;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. //-----------------------------------------------------------------------------
  233. void AI_Response::Describe()
  234. {
  235. if ( m_pCriteria )
  236. {
  237. DevMsg( "Search criteria:\n" );
  238. m_pCriteria->Describe();
  239. }
  240. if ( m_szMatchingRule[ 0 ] )
  241. DevMsg( "Matched rule '%s', ", m_szMatchingRule );
  242. if ( m_szContext.Length() )
  243. DevMsg( "Contexts to set '%s' on %s, ", m_szContext.Get(), m_bApplyContextToWorld ? "world" : "speaker" );
  244. DevMsg( "response %s = '%s'\n", DescribeResponse( (ResponseType_t)m_Type ), m_szResponseName );
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose:
  248. //-----------------------------------------------------------------------------
  249. const char * AI_Response::GetNamePtr() const
  250. {
  251. return m_szResponseName;
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose:
  255. //-----------------------------------------------------------------------------
  256. const char * AI_Response::GetResponsePtr() const
  257. {
  258. return m_szResponseName;
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose:
  262. // Input : type -
  263. // Output : char const
  264. //-----------------------------------------------------------------------------
  265. const char *AI_Response::DescribeResponse( ResponseType_t type )
  266. {
  267. if ( (int)type < 0 || (int)type >= NUM_RESPONSES )
  268. {
  269. Assert( 0 );
  270. return "???AI_Response bogus index";
  271. }
  272. switch( type )
  273. {
  274. case RESPONSE_NONE: return "RESPONSE_NONE";
  275. case RESPONSE_SPEAK: return "RESPONSE_SPEAK";
  276. case RESPONSE_SENTENCE: return "RESPONSE_SENTENCE";
  277. case RESPONSE_SCENE: return "RESPONSE_SCENE";
  278. case RESPONSE_RESPONSE: return "RESPONSE_RESPONSE";
  279. case RESPONSE_PRINT: return "RESPONSE_PRINT";
  280. }
  281. Assert( 0 );
  282. return "RESPONSE_NONE";
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose:
  286. // Output : const AI_CriteriaSet
  287. //-----------------------------------------------------------------------------
  288. const AI_CriteriaSet *AI_Response::GetCriteria()
  289. {
  290. return m_pCriteria;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose:
  294. //-----------------------------------------------------------------------------
  295. void AI_Response::Release()
  296. {
  297. delete this;
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose:
  301. // Output : soundlevel_t
  302. //-----------------------------------------------------------------------------
  303. soundlevel_t AI_Response::GetSoundLevel() const
  304. {
  305. if ( m_Params.flags & AI_ResponseParams::RG_SOUNDLEVEL )
  306. {
  307. return (soundlevel_t)m_Params.soundlevel;
  308. }
  309. return SNDLVL_TALKING;
  310. }
  311. float AI_Response::GetRespeakDelay( void ) const
  312. {
  313. if ( m_Params.flags & AI_ResponseParams::RG_RESPEAKDELAY )
  314. {
  315. interval_t temp;
  316. m_Params.respeakdelay.ToInterval( temp );
  317. return RandomInterval( temp );
  318. }
  319. return 0.0f;
  320. }
  321. float AI_Response::GetWeaponDelay( void ) const
  322. {
  323. if ( m_Params.flags & AI_ResponseParams::RG_WEAPONDELAY )
  324. {
  325. interval_t temp;
  326. m_Params.weapondelay.ToInterval( temp );
  327. return RandomInterval( temp );
  328. }
  329. return 0.0f;
  330. }
  331. bool AI_Response::GetSpeakOnce( void ) const
  332. {
  333. if ( m_Params.flags & AI_ResponseParams::RG_SPEAKONCE )
  334. {
  335. return true;
  336. }
  337. return false;
  338. }
  339. bool AI_Response::ShouldntUseScene( void ) const
  340. {
  341. return ( m_Params.flags & AI_ResponseParams::RG_DONT_USE_SCENE ) != 0;
  342. }
  343. bool AI_Response::ShouldBreakOnNonIdle( void ) const
  344. {
  345. return ( m_Params.flags & AI_ResponseParams::RG_STOP_ON_NONIDLE ) != 0;
  346. }
  347. int AI_Response::GetOdds( void ) const
  348. {
  349. if ( m_Params.flags & AI_ResponseParams::RG_ODDS )
  350. {
  351. return m_Params.odds;
  352. }
  353. return 100;
  354. }
  355. float AI_Response::GetDelay() const
  356. {
  357. if ( m_Params.flags & AI_ResponseParams::RG_DELAYAFTERSPEAK )
  358. {
  359. interval_t temp;
  360. m_Params.delay.ToInterval( temp );
  361. return RandomInterval( temp );
  362. }
  363. return 0.0f;
  364. }
  365. float AI_Response::GetPreDelay() const
  366. {
  367. if ( m_Params.flags & AI_ResponseParams::RG_DELAYBEFORESPEAK )
  368. {
  369. interval_t temp;
  370. m_Params.predelay.ToInterval( temp );
  371. return RandomInterval( temp );
  372. }
  373. return 0.0f;
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Sets context string
  377. // Output : void
  378. //-----------------------------------------------------------------------------
  379. void AI_Response::SetContext( const char *context )
  380. {
  381. m_szContext = context;
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. // Input : *raw -
  386. // *key -
  387. // keylen -
  388. // *value -
  389. // valuelen -
  390. // *duration -
  391. // Output : static bool
  392. //-----------------------------------------------------------------------------
  393. const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration )
  394. {
  395. char *colon1 = Q_strstr( raw, ":" );
  396. if ( !colon1 )
  397. {
  398. DevMsg( "SplitContext: warning, ignoring context '%s', missing colon separator!\n", raw );
  399. *key = *value = 0;
  400. return NULL;
  401. }
  402. int len = colon1 - raw;
  403. Q_strncpy( key, raw, MIN( len + 1, keylen ) );
  404. key[ MIN( len, keylen - 1 ) ] = 0;
  405. bool last = false;
  406. char *end = Q_strstr( colon1 + 1, "," );
  407. if ( !end )
  408. {
  409. int remaining = Q_strlen( colon1 + 1 );
  410. end = colon1 + 1 + remaining;
  411. last = true;
  412. }
  413. char *colon2 = Q_strstr( colon1 + 1, ":" );
  414. if ( colon2 && ( colon2 < end ) )
  415. {
  416. if ( duration )
  417. *duration = atof( colon2 + 1 );
  418. len = MIN( colon2 - ( colon1 + 1 ), valuelen - 1 );
  419. Q_strncpy( value, colon1 + 1, len + 1 );
  420. value[ len ] = 0;
  421. }
  422. else
  423. {
  424. if ( duration )
  425. *duration = 0.0;
  426. len = MIN( end - ( colon1 + 1 ), valuelen - 1 );
  427. Q_strncpy( value, colon1 + 1, len + 1 );
  428. value[ len ] = 0;
  429. }
  430. return last ? NULL : end + 1;
  431. }