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.

319 lines
9.5 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Rich Presence support
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "tf_tips.h"
  8. #include "tier3/tier3.h"
  9. #include <vgui/ILocalize.h>
  10. #include "cdll_util.h"
  11. #include "fmtstr.h"
  12. #include "tf_gamerules.h"
  13. #include "tf_hud_statpanel.h"
  14. #include "filesystem.h"
  15. //-----------------------------------------------------------------------------
  16. // Purpose: constructor
  17. //-----------------------------------------------------------------------------
  18. CTFTips::CTFTips() : CAutoGameSystem( "CTFTips" )
  19. {
  20. Q_memset( m_iTipCount, 0, sizeof( m_iTipCount ) );
  21. m_iTipCountAll = 0;
  22. m_iCurrentClassTip = 0;
  23. m_bInited = false;
  24. }
  25. //-----------------------------------------------------------------------------
  26. // Purpose: Initializer
  27. //-----------------------------------------------------------------------------
  28. bool CTFTips::Init()
  29. {
  30. if ( !m_bInited )
  31. {
  32. // count how many tips there are for each class and in total
  33. m_iTipCountAll = 0;
  34. for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass <= TF_LAST_NORMAL_CLASS; iClass++ )
  35. {
  36. // tip count per class is stored in resource file
  37. wchar_t *wzTipCount = g_pVGuiLocalize->Find( CFmtStr( "Tip_%d_Count", iClass ) );
  38. int iClassTipCount = wzTipCount ? _wtoi( wzTipCount ) : 0;
  39. m_iTipCount[iClass] = iClassTipCount;
  40. m_iTipCountAll += iClassTipCount;
  41. m_iArenaTipCount = g_pVGuiLocalize->Find( "Tip_arena_Count" ) ? _wtoi( g_pVGuiLocalize->Find( "Tip_arena_Count" ) ) : 0;
  42. }
  43. // Captain Canteen mascot parts!
  44. m_CaptainCanteenBody.RemoveAll();
  45. m_CaptainCanteenMisc.RemoveAll();
  46. m_CaptainCanteenHat.RemoveAll();
  47. KeyValues *kv = new KeyValues( "cpncntn" );
  48. KeyValues::AutoDelete autodelete_key( kv );
  49. if ( kv->LoadFromFile( g_pFullFileSystem, "scripts/cpncntn.txt", "MOD" ) )
  50. {
  51. for ( KeyValues *pSubKey = kv->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
  52. {
  53. // Figure out which bucket it goes in
  54. CUtlVector< CaptainCanteenAsset_t > *pAssetBucket = NULL;
  55. if ( V_stricmp( pSubKey->GetName(), "body" ) == 0 )
  56. {
  57. pAssetBucket = &m_CaptainCanteenBody;
  58. }
  59. else if ( V_stricmp( pSubKey->GetName(), "misc" ) == 0 )
  60. {
  61. pAssetBucket = &m_CaptainCanteenMisc;
  62. }
  63. else if ( V_stricmp( pSubKey->GetName(), "hat" ) == 0 )
  64. {
  65. pAssetBucket = &m_CaptainCanteenHat;
  66. }
  67. if ( pAssetBucket )
  68. {
  69. // Added more assets to that bucket
  70. for ( KeyValues *pAssetKey = pSubKey->GetFirstSubKey(); pAssetKey; pAssetKey = pAssetKey->GetNextKey() )
  71. {
  72. int nNewAsset = pAssetBucket->AddToTail();
  73. V_strncpy( (*pAssetBucket)[ nNewAsset ].szImage, pAssetKey->GetName(), sizeof( (*pAssetBucket)[ nNewAsset ].szImage ) );
  74. (*pAssetBucket)[ nNewAsset ].flRarity = ( 1.0f / pAssetKey->GetFloat() );
  75. }
  76. }
  77. }
  78. }
  79. m_bInited = true;
  80. }
  81. return true;
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Returns a random tip, selected from tips for all classes,
  85. // fills in iClassUsed with the class the tip is for
  86. //-----------------------------------------------------------------------------
  87. const wchar_t *CTFTips::GetRandomTip( int &iClassUsed )
  88. {
  89. Init();
  90. // Chance of reminding players about the Abuse Reporter.
  91. // The chance is very high for the first 20 hours of play since newbies get picked on a lot.
  92. int abuseHintChance = 3;
  93. if ( CTFStatPanel::GetTotalHoursPlayed() < 20.0f )
  94. {
  95. abuseHintChance = 33;
  96. }
  97. if ( RandomInt( 1, 100 ) <= abuseHintChance )
  98. {
  99. iClassUsed = RandomInt( TF_FIRST_NORMAL_CLASS, TF_LAST_NORMAL_CLASS );
  100. return GetAbuseReportTip();
  101. }
  102. // pick a random tip
  103. int iTip = RandomInt( 0, m_iTipCountAll-1 );
  104. // walk through each class until we find the class this tip lands in
  105. for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass <= TF_LAST_NORMAL_CLASS; iClass++ )
  106. {
  107. Assert( iTip >= 0 );
  108. int iClassTipCount = m_iTipCount[iClass];
  109. if ( iTip < iClassTipCount )
  110. {
  111. iClassUsed = iClass;
  112. // return the tip
  113. return GetTip( iClass, iTip+1 );
  114. }
  115. iTip -= iClassTipCount;
  116. }
  117. Assert( false ); // shouldn't hit this
  118. iClassUsed = TF_CLASS_UNDEFINED;
  119. return L"";
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Returns the next tip for specified class
  123. //-----------------------------------------------------------------------------
  124. const wchar_t *CTFTips::GetNextClassTip( int iClass )
  125. {
  126. int iTipClass = TF_CLASS_UNDEFINED;
  127. // OK to call this function with TF_CLASS_UNDEFINED or TF_CLASS_RANDOM, just return a random tip for any class in that case
  128. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass > TF_LAST_NORMAL_CLASS )
  129. return GetRandomTip( iTipClass );
  130. if ( TFGameRules() && TFGameRules()->IsInArenaMode() == true )
  131. {
  132. return GetArenaTip();
  133. }
  134. int iClassTipCount = m_iTipCount[iClass];
  135. Assert( 0 != iClassTipCount );
  136. if ( 0 == iClassTipCount )
  137. return L"";
  138. // wrap the tip index to the valid range for this class
  139. if ( m_iCurrentClassTip >= iClassTipCount )
  140. {
  141. m_iCurrentClassTip %= iClassTipCount;
  142. }
  143. // return the tip
  144. const wchar_t *wzTip = GetTip( iClass, m_iCurrentClassTip+1 );
  145. m_iCurrentClassTip++;
  146. return wzTip;
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: Returns specified tip index for arena
  150. //-----------------------------------------------------------------------------
  151. const wchar_t *CTFTips::GetArenaTip( void )
  152. {
  153. int iClassTipCount = m_iArenaTipCount;
  154. Assert( 0 != iClassTipCount );
  155. if ( 0 == iClassTipCount )
  156. return L"";
  157. // wrap the tip index to the valid range for this class
  158. if ( m_iCurrentClassTip >= iClassTipCount )
  159. {
  160. m_iCurrentClassTip %= iClassTipCount;
  161. }
  162. // return the tip
  163. const wchar_t *wzFmt = g_pVGuiLocalize->Find( CFmtStr( "#Tip_arena_%d", m_iCurrentClassTip+1 ) );
  164. static wchar_t wzTip[512] = L"";
  165. // replace any commands with their bound keys
  166. UTIL_ReplaceKeyBindings( wzFmt, 0, wzTip, sizeof( wzTip ) );
  167. m_iCurrentClassTip++;
  168. return wzTip;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Returns specified tip index for specified class
  172. //-----------------------------------------------------------------------------
  173. const wchar_t *CTFTips::GetTip( int iClass, int iTip )
  174. {
  175. static wchar_t wzTip[512] = L"";
  176. // get the tip
  177. const wchar_t *wzFmt = g_pVGuiLocalize->Find( CFmtStr( "#Tip_%d_%d", iClass, iTip ) );
  178. // replace any commands with their bound keys
  179. UTIL_ReplaceKeyBindings( wzFmt, 0, wzTip, sizeof( wzTip ) );
  180. return wzTip;
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose: Returns tip about using the Abuse Reporter
  184. //-----------------------------------------------------------------------------
  185. const wchar_t *CTFTips::GetAbuseReportTip( void )
  186. {
  187. static wchar_t wzAbuseTip[512] = L"";
  188. // replace any commands with their bound keys
  189. const wchar_t *wzFmt = g_pVGuiLocalize->Find( "Tip_Abuse_Report" );
  190. UTIL_ReplaceKeyBindings( wzFmt, 0, wzAbuseTip, sizeof( wzAbuseTip ) );
  191. return wzAbuseTip;
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose: Returns tip related to MvM
  195. //-----------------------------------------------------------------------------
  196. const wchar_t *CTFTips::GetRandomMvMTip( int &iClassUsed )
  197. {
  198. Init();
  199. static wchar_t wzMvMTip[512] = L"";
  200. static int iPrevMvMClass = -1;
  201. iClassUsed = RandomInt( TF_FIRST_NORMAL_CLASS, TF_LAST_NORMAL_CLASS );
  202. if ( iClassUsed == iPrevMvMClass )
  203. {
  204. iClassUsed = ( iClassUsed + 1 ) % TF_LAST_NORMAL_CLASS;
  205. }
  206. iPrevMvMClass = iClassUsed;
  207. // Get Tip Count
  208. CFmtStr fmtTipCount("Tip_MvM_%d_Count", iClassUsed );
  209. int iMvMTipCount = g_pVGuiLocalize->Find( fmtTipCount ) ? _wtoi( g_pVGuiLocalize->Find( fmtTipCount ) ) : 1;
  210. int iTip = RandomInt( 1, iMvMTipCount );
  211. // replace any commands with their bound keys
  212. const wchar_t *wzFmt = g_pVGuiLocalize->Find( CFmtStr( "#Tip_MvM_%d_%d", iClassUsed, iTip ) );
  213. UTIL_ReplaceKeyBindings( wzFmt, 0, wzMvMTip, sizeof( wzMvMTip ) );
  214. iPrevMvMClass = iClassUsed;
  215. return wzMvMTip;
  216. }
  217. void CTFTips::GetRandomCaptainCanteenImages( const char **ppchBody, const char **ppchMisc, const char **ppchHat )
  218. {
  219. // Select and copy over all 3 parts
  220. *ppchBody = GetRandomCaptainCanteenAsset( &m_CaptainCanteenBody );
  221. *ppchMisc = GetRandomCaptainCanteenAsset( &m_CaptainCanteenMisc );
  222. *ppchHat = GetRandomCaptainCanteenAsset( &m_CaptainCanteenHat );
  223. }
  224. const char *CTFTips::GetRandomCaptainCanteenAsset( CUtlVector< CaptainCanteenAsset_t > *pAssetBucket )
  225. {
  226. // If there's nothing in the bucket, bail out
  227. if ( pAssetBucket->Count() <= 0 )
  228. {
  229. return "";
  230. }
  231. if ( pAssetBucket->Count() == 1 )
  232. {
  233. // Easy out if there's only 1 choice
  234. return (*pAssetBucket)[ 0 ].szImage;
  235. }
  236. // Get the total scale of selection possibilities
  237. float flSelectionTotal = 0.0f;
  238. for ( int i = 0; i < pAssetBucket->Count(); ++i )
  239. {
  240. flSelectionTotal += (*pAssetBucket)[ i ].flRarity;
  241. }
  242. if ( flSelectionTotal > 0.0f )
  243. {
  244. // Pick a number from 0 to 1
  245. float flRand = RandomFloat();
  246. // Loop through to figure out which asset we've chosen
  247. float flCurrentPosition = 0.0f;
  248. for ( int i = 0; i < pAssetBucket->Count(); ++i )
  249. {
  250. flCurrentPosition += (*pAssetBucket)[ i ].flRarity / flSelectionTotal;
  251. if ( flRand <= flCurrentPosition )
  252. {
  253. // We landed on this random asset
  254. return (*pAssetBucket)[ i ].szImage;
  255. }
  256. }
  257. }
  258. // Something went wrong... just return the first choice
  259. return (*pAssetBucket)[ 0 ].szImage;
  260. }
  261. // global instance
  262. CTFTips g_TFTips;