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.

967 lines
28 KiB

  1. //====== Copyright Valve Corporation, All rights reserved. =================
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "cs_player_rank_mgr.h"
  8. #include "achievementmgr.h"
  9. #include "achievements_cs.h"
  10. #include "cs_client_gamestats.h"
  11. #include "keyvalues.h"
  12. #include "c_playerresource.h"
  13. #include "cs_hud_chat.h"
  14. #ifdef _PS3
  15. #include "fmtstr.h"
  16. #endif
  17. #if defined ( _GAMECONSOLE )
  18. #include "usermessages.h"
  19. #include "inputsystem/iinputsystem.h"
  20. #endif
  21. ConVar cl_player_rank_debug( "cl_player_rank_debug", "0", FCVAR_DEVELOPMENTONLY );
  22. ConVar cl_force_progress_notice_every_change( "cl_force_progress_notice_every_change", "0", FCVAR_DEVELOPMENTONLY );
  23. ConVar cl_medal_progress_shown_fraction( "cl_medal_progress_shown_fraction", "5", FCVAR_DEVELOPMENTONLY, "Show progress on the win panel every GOAL/X increments for stat based achievements.", true, 1, true, 10 );
  24. CPlayerRankManager g_PlayerRankManager;
  25. MedalCategory_t GetAchievementCategory( int id );
  26. static CFmtStr s_MedalCategoryNameActiveSeason( "SFUI_MedalCategory_Season%d_CAPHTML", MEDAL_SEASON_ACCESS_VALUE + 1 );
  27. const char *s_MedalCategoryNames[] =
  28. {
  29. "SFUI_MedalCategory_TeamAndObjective_CAPHTML",
  30. "SFUI_MedalCategory_Combat_CAPHTML",
  31. "SFUI_MedalCategory_Weapon_CAPHTML",
  32. "SFUI_MedalCategory_Map_CAPHTML",
  33. "SFUI_MedalCategory_GunGame_CAPHTML",
  34. s_MedalCategoryNameActiveSeason.Access()
  35. };
  36. const char *s_MedalCategoryRankNames[] =
  37. {
  38. "SFUI_Medal_RankName_0",
  39. "SFUI_Medal_RankName_1",
  40. "SFUI_Medal_RankName_2",
  41. "SFUI_Medal_RankName_3"
  42. };
  43. CPlayerRankManager::CPlayerRankManager()
  44. : m_bMedalCategoriesBuilt( false ),
  45. m_iEloBracketDelta( 0 ),
  46. m_iNewEloBracket( -1 )
  47. {
  48. COMPILE_TIME_ASSERT( ARRAYSIZE( s_MedalCategoryRankNames ) == MEDAL_RANK_COUNT );
  49. COMPILE_TIME_ASSERT( ARRAYSIZE( s_MedalCategoryNames ) == MEDAL_CATEGORY_COUNT );
  50. Q_memset( m_rank, 0, sizeof( m_rank ) );
  51. }
  52. CPlayerRankManager::~CPlayerRankManager()
  53. {
  54. }
  55. bool CPlayerRankManager::Init()
  56. {
  57. ListenForGameEvent( "achievement_earned_local" );
  58. ListenForGameEvent( "round_start" );
  59. ListenForGameEvent( "achievement_info_loaded" );
  60. ListenForGameEvent( "seasoncoin_levelup" );
  61. g_pMatchFramework->GetEventsSubscription()->Subscribe( this );
  62. return true;
  63. }
  64. void CPlayerRankManager::Shutdown()
  65. {
  66. g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this );
  67. StopListeningForAllEvents();
  68. }
  69. void CPlayerRankManager::LevelInitPostEntity()
  70. {
  71. // Intention here is to send this once we finish connecting to a server.
  72. SendRankDataToServer();
  73. ResetRecordedEloBracketChange();
  74. }
  75. void CPlayerRankManager::FireGameEvent( IGameEvent *event )
  76. {
  77. const char *name = event->GetName();
  78. if ( 0 == Q_strcmp( name, "round_start" ) )
  79. {
  80. OnRoundStart();
  81. }
  82. else if ( 0 == Q_strcmp( name, "achievement_earned_local" ) )
  83. {
  84. OnAchievementEarned( event->GetInt( "achievement", CSInvalidAchievement ) );
  85. }
  86. else if ( 0 == Q_strcmp( name, "achievement_info_loaded" ) )
  87. {
  88. // After we retrieve achievement info from steam or titledata,
  89. // get lists of our achievements in each category and determine the 'rank' for the player.
  90. BuildMedalCategories();
  91. }
  92. else if ( 0 == Q_strcmp( name, "seasoncoin_levelup" ) )
  93. {
  94. int iPlayerIndex = event->GetInt( "player" );
  95. int iCategory = event->GetInt( "category" );
  96. int iRank = event->GetInt( "rank" );
  97. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
  98. if ( C_BasePlayer::GetLocalPlayer() && pPlayer && C_BasePlayer::GetLocalPlayer() == pPlayer )
  99. {
  100. OnSeasonCoinLeveledUp( iCategory, iRank );
  101. }
  102. }
  103. }
  104. // Use CommandKeyValues to send our rank data to the server so it can
  105. // network to other clients and display in the scoreboard.
  106. void CPlayerRankManager::SendRankDataToServer( void )
  107. {
  108. #if 0
  109. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  110. if ( !pLocalPlayer || !engine->IsConnected() )
  111. return;
  112. KeyValues *kv = new KeyValues("player_medal_ranking");
  113. for ( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
  114. {
  115. kv->SetInt( CFmtStr("rank%d",i), m_rank[i] );
  116. }
  117. // Base_CmdKeyValues handles the kv deletion.
  118. engine->ServerCmdKeyValues( kv );
  119. #endif
  120. }
  121. // Once we have achievements loaded, sort them into category lists and build our internal
  122. // category info structures.
  123. void CPlayerRankManager::BuildMedalCategories( void )
  124. {
  125. // Note: we cut splitscreen, so achievements should all be at idx 0.
  126. CUtlMap<int, CBaseAchievement *> &achievements = g_AchievementMgrCS.GetAchievements( 0 );
  127. // clear these out before we rebuild them
  128. for ( int i=0; i<MEDAL_CATEGORY_COUNT; i++)
  129. {
  130. m_medalCategoryList[i].RemoveAll();
  131. }
  132. m_StatBasedAchievements.RemoveAll();
  133. if ( achievements.Count() == 0 )
  134. {
  135. //TODO: need disable this whole system when this happens.
  136. Warning( "PlayerRankManager: No achievements loaded!\n" );
  137. m_bMedalCategoriesBuilt = false;
  138. return;
  139. }
  140. int count[MEDAL_CATEGORY_COUNT];
  141. memset( count, 0, sizeof( count ) );
  142. FOR_EACH_MAP( achievements, idx )
  143. {
  144. CBaseAchievement *pAchievement = achievements[idx];
  145. MedalCategory_t c = GetAchievementCategory( pAchievement->GetAchievementID() );
  146. if ( c != MEDAL_CATEGORY_NONE )
  147. {
  148. count[c]++;
  149. m_medalCategoryList[c].AddToTail( pAchievement );
  150. // Keep a separate list of achievements withs stat goals so we can
  151. // check for relevant stat changes then report those for display.
  152. if ( !pAchievement->IsAchieved() && pAchievement->GetGoal() > 1 )
  153. {
  154. CAchievement_StatGoal *pStatGoal = dynamic_cast<CAchievement_StatGoal*>(pAchievement);
  155. Assert( pStatGoal );
  156. if ( pStatGoal )
  157. {
  158. m_StatBasedAchievements.AddToTail( pStatGoal );
  159. }
  160. }
  161. }
  162. }
  163. // This may be temp... First guess at how to distribute the rank requirements
  164. // This also assumes the medal rank enum never changes...
  165. for ( int c = MEDAL_CATEGORY_START; c < MEDAL_CATEGORY_COUNT; ++c )
  166. {
  167. m_categoryInfo[c].m_totalInCategory = count[c];
  168. m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_NONE] = 0;
  169. m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_BRONZE] = (int)((float)(count[c])/3.0f); // ~1/3rd for bronze;
  170. m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_SILVER] = (int)(2.0f*(float)(count[c])/3.0f); // ~2/3rd for silver;
  171. m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_GOLD] = count[c]; // all for gold
  172. }
  173. #if defined ( DEBUG )
  174. int dbgCatCount = 0;
  175. FOR_EACH_MAP( achievements, idx )
  176. {
  177. CBaseAchievement *pAchievement = achievements[idx];
  178. if ( MEDAL_CATEGORY_NONE != GetAchievementCategory( pAchievement->GetAchievementID() ) )
  179. {
  180. dbgCatCount++;
  181. }
  182. }
  183. // If you implement a new achievement, give it a category.
  184. Assert( dbgCatCount == g_AchievementMgrCS.GetAchievementCount() );
  185. #endif
  186. m_bMedalCategoriesBuilt = true;
  187. RecalculateRanks();
  188. }
  189. void CPlayerRankManager::RecalculateRanks( void )
  190. {
  191. for ( int j = MEDAL_CATEGORY_START; j < MEDAL_CATEGORY_COUNT; ++j )
  192. {
  193. MedalRank_t newRank = CalculateRankForCategory( (MedalCategory_t)j );
  194. if ( newRank > m_rank[j] )
  195. m_rank[j] = newRank;
  196. }
  197. }
  198. MedalRank_t CPlayerRankManager::CalculateRankForCategory( MedalCategory_t category ) const
  199. {
  200. if ( m_bMedalCategoriesBuilt == false )
  201. return MEDAL_RANK_NONE;
  202. int count = CountAchievedInCategory( category );
  203. const MedalCategoryInfo_t& info = m_categoryInfo[category];
  204. int rank = MEDAL_RANK_NONE;
  205. for ( int i = 0; i < MEDAL_RANK_COUNT; i++ )
  206. {
  207. // increase rank until we no longer meet the reqirements for the rank
  208. if ( count >= info.m_minCountForRank[i] )
  209. rank = i;
  210. }
  211. if ( cl_player_rank_debug.GetBool() )
  212. Msg( "PlayerRankManager: Calculating rank for category %d: %d (%d achieved, %d needed)\n", category, rank, count, info.m_minCountForRank[rank] );
  213. return (MedalRank_t)rank;
  214. }
  215. int CPlayerRankManager::CountAchievedInCategory( MedalCategory_t category ) const
  216. {
  217. Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
  218. if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
  219. return 0;
  220. int achievedCount = 0;
  221. FOR_EACH_VEC( m_medalCategoryList[category], iter )
  222. {
  223. CBaseAchievement *pAchievement = m_medalCategoryList[category][iter];
  224. if ( pAchievement->IsAchieved() )
  225. achievedCount++;
  226. }
  227. return achievedCount;
  228. }
  229. void CPlayerRankManager::OnRoundStart()
  230. {
  231. if ( cl_player_rank_debug.GetBool() )
  232. Msg( "PlayerRankManager: clearing all rank progress events at round start\n" );
  233. m_RankIncreases.RemoveAll();
  234. m_MedalsEarned.RemoveAll();
  235. }
  236. void CPlayerRankManager::OnAchievementEarned( int achievementId )
  237. {
  238. CBaseAchievement *pAchievement = g_AchievementMgrCS.GetAchievementByID( achievementId, 0 );
  239. MedalCategory_t category = GetAchievementCategory( achievementId );
  240. if ( !pAchievement || category == MEDAL_CATEGORY_NONE )
  241. return;
  242. if ( cl_player_rank_debug.GetBool() )
  243. Msg( "PlayerRankManager: Adding achievement event '%s'\n", pAchievement->GetName() );
  244. MedalEarnedEvent_t event( pAchievement, category );
  245. m_MedalsEarned.AddToTail( event );
  246. MedalRank_t oldRank = m_rank[category];
  247. MedalRank_t newRank = CalculateRankForCategory( category );
  248. if ( newRank > oldRank )
  249. {
  250. if ( engine->IsConnected() )
  251. {
  252. RankIncreasedEvent_t event( pAchievement, category );
  253. m_RankIncreases.AddToTail( event );
  254. }
  255. m_rank[category] = newRank;
  256. SendRankDataToServer();
  257. if ( cl_player_rank_debug.GetBool() )
  258. Msg( "PlayerRankManager: Increasing medal rank category %d to %d\n", category, m_rank[category] );
  259. }
  260. }
  261. void CPlayerRankManager::OnSeasonCoinLeveledUp( int iCategory, int iRank )
  262. {
  263. // figure out which coin we leveled up
  264. if ( iCategory <= MEDAL_CATEGORY_NONE )
  265. {
  266. for ( int i = MEDAL_CATEGORY_SEASON_COIN; i < MEDAL_CATEGORY_COUNT; i++ )
  267. {
  268. CheckCategoryRankLevelUp( i, -1 );
  269. }
  270. }
  271. else
  272. {
  273. CheckCategoryRankLevelUp( iCategory, iRank );
  274. }
  275. }
  276. bool CPlayerRankManager::CheckCategoryRankLevelUp( int iCategory, int iRank )
  277. {
  278. if ( iCategory != MEDAL_CATEGORY_SEASON_COIN )
  279. return false;
  280. bool bLeveledUp = false;
  281. MedalCategory_t category = (MedalCategory_t)iCategory;
  282. MedalRank_t oldRank = m_rank[category];
  283. MedalRank_t newRank = (iRank > -1) ? (MedalRank_t)iRank : CalculateRankForCategory( category );
  284. if ( newRank > oldRank )
  285. {
  286. if ( engine->IsConnected() && oldRank > MEDAL_RANK_NONE )
  287. {
  288. RankIncreasedEvent_t event( NULL, category );
  289. m_RankIncreases.AddToTail( event );
  290. }
  291. m_rank[category] = newRank;
  292. SendRankDataToServer();
  293. if ( cl_player_rank_debug.GetBool() )
  294. Msg( "PlayerRankManager: Increasing medal rank category %d to %d\n", category, m_rank[category] );
  295. bLeveledUp = true;
  296. }
  297. return bLeveledUp;
  298. }
  299. int CPlayerRankManager::GetTotalMedalsInCategory( MedalCategory_t category ) const
  300. {
  301. Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
  302. if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
  303. return 0;
  304. return m_categoryInfo[category].m_totalInCategory;
  305. }
  306. int CPlayerRankManager::GetMinMedalsForRank( MedalCategory_t category, MedalRank_t rank ) const
  307. {
  308. Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
  309. if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
  310. return 0;
  311. Assert( rank >= MEDAL_RANK_NONE && rank < MEDAL_RANK_COUNT );
  312. if( rank < MEDAL_RANK_NONE || rank >= MEDAL_RANK_COUNT )
  313. return 0;
  314. return m_categoryInfo[category].m_minCountForRank[rank];
  315. }
  316. void CPlayerRankManager::GetMedalStatsEarnedThisRound( CUtlVector<MedalStatEvent_t>& outVec ) const
  317. {
  318. const StatsCollection_t &roundStats = g_CSClientGameStats.GetRoundStats(0);
  319. FOR_EACH_VEC( m_StatBasedAchievements, iter )
  320. {
  321. CAchievement_StatGoal *pStatGoal = m_StatBasedAchievements[iter];
  322. if ( roundStats[pStatGoal->GetStatId()] > 0 && !pStatGoal->IsAchieved() )
  323. {
  324. MedalCategory_t cat = GetAchievementCategory( pStatGoal->GetAchievementID() );
  325. if ( cat == MEDAL_CATEGORY_NONE )
  326. {
  327. Assert( 0 );
  328. continue;
  329. }
  330. // Hackish: Logic for culling which stat goal achievements is going here.
  331. // Current idea is any achievemnt that just reached or crossed a 20% border of the total
  332. // gets sent to UI code for possible display.
  333. int showProgresCount = MAX( 1, pStatGoal->GetGoal() / cl_medal_progress_shown_fraction.GetInt() ); // Show a progress notification in the win panel every X stats.
  334. int cur = pStatGoal->GetCount();
  335. int prev = cur - roundStats[pStatGoal->GetStatId()];
  336. bool bCrossedBorder = false;
  337. while ( cur != prev )
  338. {
  339. if ( cur%showProgresCount == 0 )
  340. {
  341. bCrossedBorder = true;
  342. break;
  343. }
  344. cur--;
  345. }
  346. if ( cl_force_progress_notice_every_change.GetBool() )
  347. bCrossedBorder = true;
  348. if ( bCrossedBorder )
  349. {
  350. MedalStatEvent_t event( pStatGoal, cat, pStatGoal->GetStatId() );
  351. outVec.AddToTail( event );
  352. if ( cl_player_rank_debug.GetBool() )
  353. Msg( "PlayerRankManager: Adding stat progress for achievement '%s' in stat %d\n", pStatGoal->GetName(), pStatGoal->GetStatId() );
  354. }
  355. }
  356. }
  357. }
  358. const CUtlVector<RankIncreasedEvent_t>& CPlayerRankManager::GetRankIncreasesThisRound( void ) const
  359. {
  360. return m_RankIncreases;
  361. }
  362. const CUtlVector<MedalEarnedEvent_t>& CPlayerRankManager::GetMedalsEarnedThisRound( void ) const
  363. {
  364. return m_MedalsEarned;
  365. }
  366. const char *CPlayerRankManager::GetMedalCatagoryName( MedalCategory_t category )
  367. {
  368. Assert( category >= 0 && category < ARRAYSIZE( s_MedalCategoryNames ) );
  369. return s_MedalCategoryNames[category];
  370. }
  371. const char *CPlayerRankManager::GetMedalCatagoryRankName( int nRank )
  372. {
  373. Assert( nRank >= 0 && nRank < ARRAYSIZE( s_MedalCategoryRankNames ) );
  374. return s_MedalCategoryRankNames[nRank];
  375. }
  376. void CPlayerRankManager::PrintRankProgressThisRound() const
  377. {
  378. Msg( "Rank Increases:\n" );
  379. FOR_EACH_VEC( m_RankIncreases, iter )
  380. {
  381. Msg( "PlayerRankManager: Rank increase because of medal '%s' in category %d\n", m_RankIncreases[iter].m_pAchievement->GetName(), m_RankIncreases[iter].m_category );
  382. }
  383. Msg( "Current Ranks: " );
  384. for ( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
  385. {
  386. Msg( "%d", m_rank[i] );
  387. }
  388. Msg( "\n\n" );
  389. Msg( "Medals Earned:\n" );
  390. FOR_EACH_VEC( m_MedalsEarned, iter )
  391. {
  392. Msg( "PlayerRankManager: Earned medal '%s' in category %d\n", m_MedalsEarned[iter].m_pAchievement->GetName(), m_MedalsEarned[iter].m_category );
  393. }
  394. Msg( "\n\n" );
  395. Msg( "Medal Progress made:\n" );
  396. CUtlVector<MedalStatEvent_t> outVec;
  397. GetMedalStatsEarnedThisRound( outVec );
  398. FOR_EACH_VEC( outVec, iter )
  399. {
  400. Msg( "PlayerRankManager: Earned statid %d towards achievement '%s' in category %d\n", outVec[iter].m_StatType, outVec[iter].m_pAchievement->GetName(), outVec[iter].m_category );
  401. }
  402. }
  403. void CPlayerRankManager::OnEvent( KeyValues *pEvent )
  404. {
  405. /* Removed for partner depot */
  406. }
  407. void CPlayerRankManager::NoteEloBracketChanged( int iOldBracket, int iNewBracket )
  408. {
  409. m_iEloBracketDelta += (iNewBracket - iOldBracket);
  410. m_iNewEloBracket = iNewBracket;
  411. }
  412. // Returns the delta between the elo at the start of the map and now.
  413. // Should be reset explictly when we've displayed the 'elo changed' update to the player.
  414. int CPlayerRankManager::GetEloBracketChange( int &iOutNewEloBracket )
  415. {
  416. if ( m_iEloBracketDelta != 0 )
  417. {
  418. Assert( m_iNewEloBracket >= 0 );
  419. iOutNewEloBracket = m_iNewEloBracket;
  420. }
  421. return m_iEloBracketDelta;
  422. }
  423. // Call this after we're sure we've shown the user their new rank.
  424. void CPlayerRankManager::ResetRecordedEloBracketChange( void )
  425. {
  426. m_iEloBracketDelta = 0;
  427. m_iNewEloBracket = -1;
  428. }
  429. CON_COMMAND_F( cl_player_rank_events_spew, "Spews the contents of all events this round that could be displayed to the player, as well as the player's current ranks.", FCVAR_DEVELOPMENTONLY )
  430. {
  431. g_PlayerRankManager.PrintRankProgressThisRound();
  432. }
  433. CON_COMMAND_F( print_achievement_categories, "Spews achievements for each category", FCVAR_DEVELOPMENTONLY )
  434. {
  435. CUtlMap<int, CBaseAchievement *> &achievements = g_AchievementMgrCS.GetAchievements( 0 );
  436. for( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
  437. {
  438. Msg( "Category %d:\n", i );
  439. int count = 0;
  440. int countAchieved = 0;
  441. FOR_EACH_MAP( achievements, idx )
  442. {
  443. CBaseAchievement *pAchievement = achievements[idx];
  444. if ( i == GetAchievementCategory( pAchievement->GetAchievementID() ) )
  445. {
  446. Msg( "%s %s\n", pAchievement->GetName(), pAchievement->IsAchieved() ? "*achieved*" : " " );
  447. count++;
  448. if ( pAchievement->IsAchieved() )
  449. countAchieved++;
  450. }
  451. if ( GetAchievementCategory( pAchievement->GetAchievementID() ) == MEDAL_CATEGORY_NONE )
  452. {
  453. Msg( "***%s Uncategorized!\n", pAchievement->GetName() );
  454. }
  455. }
  456. Msg( "Total: %d\n\n", count );
  457. }
  458. }
  459. // Pretty ugly... Fragile hand-built categorization using the achievement excel sheet. May need to revisit how
  460. // we tag an achievement with a category more formally.
  461. MedalCategory_t GetAchievementCategory( int id )
  462. {
  463. switch( id )
  464. {
  465. // Team and Objective
  466. case CSMedalist:
  467. case CSWinBombPlant:
  468. case CSWinBombDefuse:
  469. case CSBombDefuseCloseCall:
  470. case CSKilledDefuser:
  471. case CSPlantBombWithin25Seconds:
  472. case CSKillBombPickup:
  473. case CSBombMultikill:
  474. case CSGooseChase:
  475. case CSWinBombPlantAfterRecovery:
  476. case CSDefuseDefense:
  477. case CSPlantBombsLow:
  478. case CSDefuseBombsLow:
  479. case CSRescueAllHostagesInARound:
  480. case CSKilledRescuer:
  481. case CSFastHostageRescue:
  482. case CSRescueHostagesLow:
  483. case CSRescueHostagesMid:
  484. case CSWinRoundsLow:
  485. case CSWinRoundsMed:
  486. case CSWinRoundsHigh:
  487. case CSFastRoundWin:
  488. case CSLosslessExtermination:
  489. case CSFlawlessVictory:
  490. case CSDonateWeapons:
  491. case CSBloodlessVictory:
  492. case CSMoneyEarnedLow:
  493. case CSMoneyEarnedMed:
  494. case CSMoneyEarnedHigh:
  495. case CSKillEnemyTeam:
  496. case CSLastPlayerAlive:
  497. case CSWinPistolRoundsLow:
  498. case CSWinPistolRoundsMed:
  499. case CSWinPistolRoundsHigh:
  500. case CSSilentWin:
  501. case CSWinRoundsWithoutBuying:
  502. case CSAvengeFriend:
  503. return MEDAL_CATEGORY_TEAM_AND_OBJECTIVE;
  504. // Combat
  505. case CSEnemyKillsLow:
  506. case CSEnemyKillsMed:
  507. case CSEnemyKillsHigh:
  508. case CSKillEnemyReloading:
  509. case CSKillingSpree:
  510. case CSKillsWithMultipleGuns:
  511. case CSHeadshots:
  512. case CSSurviveGrenade:
  513. case CSKillEnemyBlinded:
  514. case CSKillEnemiesWhileBlind:
  515. case CSKillEnemiesWhileBlindHard:
  516. case CSKillsEnemyWeapon:
  517. case CSWinKnifeFightsLow:
  518. case CSWinKnifeFightsHigh:
  519. case CSKilledDefuserWithGrenade:
  520. case CSKillSniperWithSniper:
  521. case CSKillSniperWithKnife:
  522. case CSHipShot:
  523. case CSKillSnipers:
  524. case CSKillWhenAtLowHealth:
  525. case CSPistolRoundKnifeKill:
  526. case CSWinDualDuel:
  527. case CSGrenadeMultikill:
  528. case CSKillWhileInAir:
  529. case CSKillEnemyInAir:
  530. case CSKillerAndEnemyInAir:
  531. case CSKillEnemyWithFormerGun:
  532. case CSKillTwoWithOneShot:
  533. case CSGiveDamageLow:
  534. case CSGiveDamageMed:
  535. case CSGiveDamageHigh:
  536. case CSKillEnemyLastBullet:
  537. case CSKillingSpreeEnder:
  538. case CSSurviveManyAttacks:
  539. case CSDamageNoKill:
  540. case CSKillLowDamage:
  541. case CSUnstoppableForce:
  542. case CSImmovableObject:
  543. case CSHeadshotsInRound:
  544. case CSCauseFriendlyFireWithFlashbang:
  545. return MEDAL_CATEGORY_COMBAT;
  546. // Weapon
  547. case CSEnemyKillsDeagle:
  548. case CSEnemyKillsHKP2000:
  549. case CSEnemyKillsGlock:
  550. case CSEnemyKillsP250:
  551. case CSEnemyKillsElite:
  552. case CSEnemyKillsFiveSeven:
  553. case CSEnemyKillsAWP:
  554. case CSEnemyKillsAK47:
  555. case CSEnemyKillsM4A1:
  556. case CSEnemyKillsAUG:
  557. case CSEnemyKillsSG556:
  558. case CSEnemyKillsSCAR20:
  559. case CSEnemyKillsGALILAR:
  560. case CSEnemyKillsFAMAS:
  561. case CSEnemyKillsSSG08:
  562. case CSEnemyKillsG3SG1:
  563. case CSEnemyKillsP90:
  564. case CSEnemyKillsMP7:
  565. case CSEnemyKillsMP9:
  566. case CSEnemyKillsMAC10:
  567. case CSEnemyKillsUMP45:
  568. case CSEnemyKillsNova:
  569. case CSEnemyKillsXM1014:
  570. case CSEnemyKillsMag7:
  571. case CSEnemyKillsM249:
  572. case CSEnemyKillsNegev:
  573. case CSEnemyKillsTec9:
  574. case CSEnemyKillsSawedoff:
  575. case CSEnemyKillsBizon:
  576. case CSEnemyKillsKnife:
  577. case CSEnemyKillsHEGrenade:
  578. case CSEnemyKillsMolotov:
  579. case CSPosthumousGrenadeKill:
  580. case CSMetaPistol:
  581. case CSMetaRifle:
  582. case CSMetaSMG:
  583. case CSMetaShotgun:
  584. case CSMetaWeaponMaster:
  585. case CSEnemyKillsTaser:
  586. case CSKillWithEveryWeapon:
  587. return MEDAL_CATEGORY_WEAPON;
  588. // Map
  589. case CSWinMapCS_ITALY:
  590. case CSWinMapCS_OFFICE:
  591. case CSWinMapDE_AZTEC:
  592. case CSWinMapDE_DUST:
  593. case CSWinMapDE_DUST2:
  594. case CSWinMapDE_INFERNO:
  595. case CSWinMapDE_NUKE:
  596. case CSWinMapDE_TRAIN:
  597. case CSWinMatchAR_SHOOTS:
  598. case CSWinMatchAR_BAGGAGE:
  599. case CSWinMatchDE_LAKE:
  600. case CSWinMatchDE_SAFEHOUSE:
  601. case CSWinMatchDE_SUGARCANE:
  602. case CSWinMatchDE_STMARC:
  603. case CSWinMatchDE_BANK:
  604. case CSWinMatchDE_SHORTTRAIN:
  605. case CSBreakWindows:
  606. //case CSBreakProps:
  607. return MEDAL_CATEGORY_MAP;
  608. // Arsenal
  609. case CSGunGameKillKnifer:
  610. case CSWinEveryGGMap:
  611. case CSGunGameProgressiveRampage:
  612. case CSGunGameFirstKill:
  613. case CSFirstBulletKills:
  614. case CSGunGameConservationist:
  615. case CSPlantBombsTRLow:
  616. case CSDefuseBombsTRLow:
  617. case CSKillEnemyTerrTeamBeforeBombPlant:
  618. case CSKillEnemyCTTeamBeforeBombPlant:
  619. case CSBornReady:
  620. case CSSpawnCamper:
  621. case CSGunGameKnifeKillKnifer:
  622. case CSGunGameSMGKillKnifer:
  623. case CSStillAlive:
  624. case CSGGRoundsLow:
  625. case CSGGRoundsMed:
  626. case CSGGRoundsHigh:
  627. case CSGGWinRoundsLow:
  628. case CSGGWinRoundsMed:
  629. case CSGGWinRoundsHigh:
  630. case CSGGWinRoundsExtreme:
  631. case CSGGWinRoundsUltimate:
  632. case CSPlayEveryGGMap:
  633. case CSDominationsLow:
  634. case CSDominationsHigh:
  635. case CSRevengesLow:
  636. case CSRevengesHigh:
  637. case CSDominationOverkillsLow:
  638. case CSDominationOverkillsHigh:
  639. case CSDominationOverkillsMatch:
  640. case CSExtendedDomination:
  641. case CSConcurrentDominations:
  642. return MEDAL_CATEGORY_ARSENAL;
  643. default:
  644. break;
  645. }
  646. // Disabling assert for now, there are some stale achievements with no category that need to be removed first.
  647. //AssertMsg( 0, "Found achievement with no known category." );
  648. return MEDAL_CATEGORY_NONE;
  649. }
  650. // Find the loc string with the correct phrasing for this stat based achievement's progress
  651. // ie, "You've killed X out of Y enemies" vs "You've planted X out of Y bombs".
  652. // Returns NULL if no string token mapped to stat id.
  653. const char* GetLocTokenForStatId( const CSStatType_t id )
  654. {
  655. switch ( id )
  656. {
  657. case CSSTAT_KILLS:
  658. case CSSTAT_KILLS_DEAGLE:
  659. case CSSTAT_KILLS_GLOCK:
  660. case CSSTAT_KILLS_ELITE:
  661. case CSSTAT_KILLS_FIVESEVEN:
  662. case CSSTAT_KILLS_BIZON:
  663. case CSSTAT_KILLS_TEC9:
  664. case CSSTAT_KILLS_TASER:
  665. case CSSTAT_KILLS_HKP2000:
  666. case CSSTAT_KILLS_P250:
  667. case CSSTAT_KILLS_AWP:
  668. case CSSTAT_KILLS_AK47:
  669. case CSSTAT_KILLS_M4A1:
  670. case CSSTAT_KILLS_AUG:
  671. case CSSTAT_KILLS_GALILAR:
  672. case CSSTAT_KILLS_FAMAS:
  673. case CSSTAT_KILLS_G3SG1:
  674. case CSSTAT_KILLS_SCAR20:
  675. case CSSTAT_KILLS_SG556:
  676. case CSSTAT_KILLS_SSG08:
  677. case CSSTAT_KILLS_P90:
  678. case CSSTAT_KILLS_MAC10:
  679. case CSSTAT_KILLS_UMP45:
  680. case CSSTAT_KILLS_MP7:
  681. case CSSTAT_KILLS_MP9:
  682. case CSSTAT_KILLS_XM1014:
  683. case CSSTAT_KILLS_MAG7:
  684. case CSSTAT_KILLS_SAWEDOFF:
  685. case CSSTAT_KILLS_NOVA:
  686. case CSSTAT_KILLS_M249:
  687. case CSSTAT_KILLS_NEGEV:
  688. case CSSTAT_KILLS_KNIFE:
  689. case CSSTAT_KILLS_HEGRENADE:
  690. case CSSTAT_KILLS_MOLOTOV:
  691. case CSSTAT_KILLS_ENEMY_WEAPON:
  692. case CSSTAT_KILLS_ENEMY_BLINDED:
  693. case CSSTAT_KILLS_KNIFE_FIGHT:
  694. case CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER:
  695. return "SFUI_WinPanelProg_stat_progress_kills";
  696. case CSSTAT_MAP_WINS_CS_ITALY:
  697. case CSSTAT_MAP_WINS_CS_OFFICE:
  698. case CSSTAT_MAP_WINS_DE_AZTEC:
  699. case CSSTAT_MAP_WINS_DE_DUST2:
  700. case CSSTAT_MAP_WINS_DE_DUST:
  701. case CSSTAT_MAP_WINS_DE_INFERNO:
  702. case CSSTAT_MAP_WINS_DE_NUKE:
  703. case CSSTAT_MAP_WINS_DE_TRAIN:
  704. case CSSTAT_MAP_MATCHES_WON_BANK:
  705. case CSSTAT_MAP_MATCHES_WON_SHOOTS:
  706. case CSSTAT_MAP_MATCHES_WON_STMARC:
  707. case CSSTAT_MAP_MATCHES_WON_SUGARCANE:
  708. case CSSTAT_MAP_MATCHES_WON_SAFEHOUSE:
  709. case CSSTAT_MAP_MATCHES_WON_LAKE:
  710. case CSSTAT_MAP_MATCHES_WON_BAGGAGE:
  711. case CSSTAT_MAP_MATCHES_WON_SHORTTRAIN:
  712. case CSSTAT_ROUNDS_WON:
  713. case CSSTAT_PISTOLROUNDS_WON:
  714. case CSSTAT_GUN_GAME_MATCHES_WON:
  715. return "SFUI_WinPanelProg_stat_progress_wins";
  716. case CSSTAT_GUN_GAME_MATCHES_PLAYED:
  717. return "SFUI_WinPanelProg_stat_progress_played";
  718. case CSSTAT_MONEY_EARNED:
  719. return "SFUI_WinPanelProg_stat_progress_money";
  720. case CSSTAT_DAMAGE:
  721. return "SFUI_WinPanelProg_stat_progress_damage";
  722. case CSSTAT_KILLS_HEADSHOT:
  723. return "SFUI_WinPanelProg_stat_progress_headshots";
  724. case CSSTAT_NUM_BOMBS_PLANTED:
  725. return "SFUI_WinPanelProg_stat_progress_bomb_plant";
  726. case CSSTAT_NUM_BOMBS_DEFUSED:
  727. return "SFUI_WinPanelProg_stat_progress_bomb_diffuse";
  728. case CSSTAT_NUM_HOSTAGES_RESCUED:
  729. return "SFUI_WinPanelProg_stat_progress_rescue";
  730. case CSSTAT_WEAPONS_DONATED:
  731. return "SFUI_WinPanelProg_stat_progress_donate";
  732. case CSSTAT_DOMINATIONS:
  733. return "SFUI_WinPanelProg_stat_progress_dominate";
  734. case CSSTAT_DOMINATION_OVERKILLS:
  735. return "SFUI_WinPanelProg_stat_progress_overkill";
  736. case CSSTAT_REVENGES:
  737. return "SFUI_WinPanelProg_stat_progress_revenge";
  738. default:
  739. break;
  740. }
  741. return NULL;
  742. }
  743. bool CPlayerRankManager::HasBuiltMedalCategories( void ) const
  744. {
  745. return m_bMedalCategoriesBuilt;
  746. }
  747. #if defined ( _GAMECONSOLE )
  748. // Official servers request elo bracket info when a player connects.
  749. bool MsgFunc_RequestEloBracketInfo( const CCSUsrMsg_RequestEloBracketInfo &msg )
  750. {
  751. ELOGameType_t game_mode = (ELOGameType_t) msg.bracket();
  752. g_PlayerRankManager.ServerRequestBracketInfo( game_mode );
  753. return true;
  754. }
  755. // We want all the bracket calculation logic to happen on the server (because we expect to change it post ship)
  756. // but all the storage for console must happen on the client. Thus this horribleness.
  757. void CPlayerRankManager::ServerRequestBracketInfo( ELOGameType_t game_mode )
  758. {
  759. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  760. if ( !pLocalPlayer || !engine->IsConnected() )
  761. return;
  762. // Restrict bracket changes to gamempads for console
  763. InputDevice_t device = g_pInputSystem->GetCurrentInputDevice();
  764. if ( device != INPUT_DEVICE_GAMEPAD )
  765. return;
  766. unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
  767. if ( idx == m_PlayerBracketInfos.InvalidIndex() )
  768. return;
  769. PlayerELOBracketInfo_t& eloBracketInfo = m_PlayerBracketInfos[idx];
  770. KeyValues *kv = new KeyValues("player_elo_bracket_info");
  771. kv->SetInt( "game_mode", game_mode );
  772. kv->SetInt( "display_bracket", eloBracketInfo.m_DisplayBracket );
  773. kv->SetInt( "previous_bracket", eloBracketInfo.m_PreviousBracket );
  774. kv->SetInt( "games_in_bracket", eloBracketInfo.m_NumGamesInBracket );
  775. // Base_CmdKeyValues handles the kv deletion.
  776. engine->ServerCmdKeyValues( kv );
  777. }
  778. // official servers send this after rounds end so clients can handle storage.
  779. bool MsgFunc_SetEloBracketInfo( const CCSUsrMsg_SetEloBracketInfo &msg )
  780. {
  781. ELOGameType_t game_mode = (ELOGameType_t) msg.game_mode();
  782. int8 display_bracket = msg.display_bracket();
  783. int8 prev_bracket = msg.prev_bracket();
  784. int8 games_in_bracket = msg.num_games_in_bracket();
  785. //Msg( "SetEloBracket %d %d %d %d\n", game_mode, display_bracket, prev_bracket, games_in_bracket );
  786. g_PlayerRankManager.Console_SetEloBracket( game_mode, display_bracket, prev_bracket, games_in_bracket );
  787. return true;
  788. }
  789. bool CPlayerRankManager::Console_SetEloBracket( ELOGameType_t game_mode, uint8 display_bracket, uint8 prev_bracket, uint8 num_games_in_bracket )
  790. {
  791. // make sure they'll fit in four bits.
  792. Assert( (display_bracket & 0xF0) == 0 );
  793. Assert( (prev_bracket & 0xF0) == 0 );
  794. //Msg( " %d %d %d %d \n", game_mode, display_bracket, prev_bracket, num_games_in_bracket );
  795. unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
  796. if ( idx == m_PlayerBracketInfos.InvalidIndex() )
  797. {
  798. if ( game_mode > ELOGameType::INVALID && game_mode < ELOGameType::COUNT )
  799. {
  800. idx = m_PlayerBracketInfos.Insert( game_mode );
  801. }
  802. else
  803. {
  804. return false;
  805. }
  806. }
  807. PlayerELOBracketInfo_t& eloBracketInfo = m_PlayerBracketInfos[idx];
  808. eloBracketInfo.m_DisplayBracket = display_bracket;
  809. eloBracketInfo.m_PreviousBracket = prev_bracket;
  810. eloBracketInfo.m_NumGamesInBracket = num_games_in_bracket;
  811. return true;
  812. }
  813. bool CPlayerRankManager::Console_SetEloBracket( ELOGameType_t game_mode, const PlayerELOBracketInfo_t& bracket )
  814. {
  815. return Console_SetEloBracket( game_mode, bracket.m_DisplayBracket, bracket.m_PreviousBracket, bracket.m_NumGamesInBracket );
  816. }
  817. // Returns the display bracket, -1 on failure (no recorded bracket info for specified game mode).
  818. // Optional pointer to elo bracket struct can be filled out.
  819. int CPlayerRankManager::Console_GetEloBracket( ELOGameType_t game_mode, PlayerELOBracketInfo_t *pOutBracketInfo /*=NULL*/ )
  820. {
  821. unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
  822. if ( idx != m_PlayerBracketInfos.InvalidIndex() )
  823. {
  824. PlayerELOBracketInfo_t &info = m_PlayerBracketInfos[idx];
  825. if ( pOutBracketInfo )
  826. *pOutBracketInfo = info;
  827. return info.m_DisplayBracket;
  828. }
  829. return -1;
  830. }
  831. #endif // _GAMECONSOLE