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.

188 lines
4.9 KiB

  1. //====== Copyright � Valve Corporation, All rights reserved. =================
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "cs_gameplay_hints.h"
  8. #include "keyvalues.h"
  9. #include "fmtstr.h"
  10. #include "filesystem.h"
  11. #include "vgui/ILocalize.h"
  12. #include "gametypes/igametypes.h"
  13. #include "cs_gamerules.h"
  14. struct CSGameplayHint_t
  15. {
  16. CSGameplayHint_t( const char* pszToken, uint32 flags ):
  17. m_pszLocToken( pszToken ), m_nRequiredContextFlags( flags ), m_nDisplayCount( 0 ) {}
  18. const char* m_pszLocToken;
  19. uint32 m_nRequiredContextFlags;
  20. uint32 m_nDisplayCount;
  21. };
  22. CCSGameplayHints::CCSGameplayHints()
  23. {
  24. m_pHintKV = NULL;
  25. }
  26. CCSGameplayHints::~CCSGameplayHints()
  27. {
  28. Cleanup();
  29. }
  30. CCSGameplayHints g_CSGameplayHints;
  31. uint32 CCSGameplayHints::GetCurrentContextFlags( void )
  32. {
  33. uint32 flags = 0;
  34. if ( CSGameRules() && CSGameRules()->IsBombDefuseMap() )
  35. flags |= HINT_CONTEXT_BOMB_MAP;
  36. if ( CSGameRules() && CSGameRules()->IsHostageRescueMap() )
  37. flags |= HINT_CONTEXT_HOSTAGE_MAP;
  38. if ( CSGameRules() && CSGameRules()->IsPlayingGunGame() )
  39. flags |= HINT_CONTEXT_GUNGAME;
  40. return flags;
  41. }
  42. uint32 ContextEntryToBitFlag( const char* szName )
  43. {
  44. if ( V_stricmp( szName, "bomb_map_only" ) == 0 )
  45. {
  46. return CCSGameplayHints::HINT_CONTEXT_BOMB_MAP;
  47. }
  48. else if ( V_stricmp( szName, "hostage_map_only" ) == 0 )
  49. {
  50. return CCSGameplayHints::HINT_CONTEXT_HOSTAGE_MAP;
  51. }
  52. else if ( V_stricmp( szName, "gungame_map_only" ) == 0 )
  53. {
  54. return CCSGameplayHints::HINT_CONTEXT_GUNGAME;
  55. }
  56. else
  57. {
  58. Warning( "HintConfig.txt contains invalid context setting: %s\n", szName );
  59. Assert( 0 );
  60. }
  61. // Show it as a failsafe.
  62. return CCSGameplayHints::HINT_CONTEXT_ALWAYS_SHOW;
  63. }
  64. uint32 BuildContextFlags( KeyValues *pContextKeys )
  65. {
  66. if ( pContextKeys == NULL || pContextKeys->GetFirstSubKey() == NULL )
  67. return CCSGameplayHints::HINT_CONTEXT_ALWAYS_SHOW;
  68. uint32 nFlags = 0;
  69. for ( KeyValues *entry = pContextKeys->GetFirstSubKey(); entry != NULL; entry = entry->GetNextKey() )
  70. {
  71. if ( entry->GetInt() > 0 )
  72. {
  73. nFlags |= ContextEntryToBitFlag( entry->GetName() );
  74. }
  75. }
  76. return nFlags;
  77. }
  78. void CCSGameplayHints::PostInit()
  79. {
  80. KeyValues *m_pHintKV = new KeyValues( "HintConfig.txt" );
  81. if ( m_pHintKV->LoadFromFile( g_pFullFileSystem, "resource/HintConfig.txt" ) )
  82. {
  83. KeyValues *hints = m_pHintKV->FindKey( "hints" );
  84. if ( hints )
  85. {
  86. for ( KeyValues *entry = hints->GetFirstSubKey(); entry != NULL; entry = entry->GetNextKey() )
  87. {
  88. const char* szLocToken = entry->GetString( "locToken", NULL );
  89. if ( szLocToken )
  90. {
  91. const wchar_t *wszText = g_pVGuiLocalize->Find( szLocToken );
  92. Assert( wszText );
  93. // Sanity check length here
  94. if ( wszText )
  95. {
  96. uint32 nContextFlags = BuildContextFlags( entry->FindKey( "context", NULL ) );
  97. m_HintList.AddToTail( new CSGameplayHint_t( szLocToken, nContextFlags ) );
  98. }
  99. else
  100. {
  101. Warning( "Bad localization token in resource/HintConfig.txt: '%s'\n", szLocToken );
  102. }
  103. }
  104. }
  105. }
  106. }
  107. Assert( m_HintList.Count() > 0 );
  108. }
  109. void CCSGameplayHints::Cleanup( void )
  110. {
  111. m_HintList.PurgeAndDeleteElements();
  112. if ( m_pHintKV )
  113. {
  114. m_pHintKV->deleteThis();
  115. m_pHintKV = NULL;
  116. }
  117. }
  118. void CCSGameplayHints::Shutdown()
  119. {
  120. Cleanup();
  121. }
  122. int CompareHintsByDisplayCount( CSGameplayHint_t* const* lhs, CSGameplayHint_t* const* rhs )
  123. {
  124. if ( (*lhs)->m_nDisplayCount > (*rhs)->m_nDisplayCount )
  125. return 1;
  126. else if ( (*lhs)->m_nDisplayCount < (*rhs)->m_nDisplayCount )
  127. return -1;
  128. return 0;
  129. }
  130. const char* CCSGameplayHints::GetRandomLeastPlayedHint( void )
  131. {
  132. if ( m_HintList.Count() == 0 )
  133. return NULL;
  134. // Sort by times 'used' and pick among the lowest counts. Counts aren't stored and will
  135. // re-zero every time this the CCSGameplayHints class is created (so, every app launch).
  136. m_HintList.InPlaceQuickSort( CompareHintsByDisplayCount );
  137. uint32 contextFlags = GetCurrentContextFlags();
  138. CUtlVector<CSGameplayHint_t*> validHints;
  139. // Filter out based on current context flags
  140. FOR_EACH_VEC( m_HintList, i )
  141. {
  142. CSGameplayHint_t* hint = m_HintList[i];
  143. // If bits are set for required context, make sure all of them are currently met.
  144. if ( hint->m_nRequiredContextFlags == HINT_CONTEXT_ALWAYS_SHOW ||
  145. ( contextFlags & hint->m_nRequiredContextFlags ) == hint->m_nRequiredContextFlags )
  146. {
  147. // Early out if we found at least one valid hint and we've moved
  148. // on to checking hints that have been displayed more often.
  149. // After the sort, the first found valid hint should have the lowest display count.
  150. if ( validHints.Count() > 0 && validHints[0]->m_nDisplayCount < hint->m_nDisplayCount )
  151. break;
  152. validHints.AddToTail( hint );
  153. }
  154. }
  155. int pick = RandomInt( 0, validHints.Count()-1 );
  156. CSGameplayHint_t *hint = validHints[pick];
  157. hint->m_nDisplayCount++;
  158. return hint->m_pszLocToken;
  159. }
  160. /*
  161. CON_COMMAND( pick_hint, "" )
  162. {
  163. Msg( "Hint: '%s'\n", g_CSGameplayHints.GetRandomLeastPlayedHint() );
  164. }
  165. */