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

//====== Copyright Valve Corporation, All rights reserved. =================
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "cs_player_rank_mgr.h"
#include "achievementmgr.h"
#include "achievements_cs.h"
#include "cs_client_gamestats.h"
#include "keyvalues.h"
#include "c_playerresource.h"
#include "cs_hud_chat.h"
#ifdef _PS3
#include "fmtstr.h"
#endif
#if defined ( _GAMECONSOLE )
#include "usermessages.h"
#include "inputsystem/iinputsystem.h"
#endif
ConVar cl_player_rank_debug( "cl_player_rank_debug", "0", FCVAR_DEVELOPMENTONLY );
ConVar cl_force_progress_notice_every_change( "cl_force_progress_notice_every_change", "0", FCVAR_DEVELOPMENTONLY );
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 );
CPlayerRankManager g_PlayerRankManager;
MedalCategory_t GetAchievementCategory( int id );
static CFmtStr s_MedalCategoryNameActiveSeason( "SFUI_MedalCategory_Season%d_CAPHTML", MEDAL_SEASON_ACCESS_VALUE + 1 );
const char *s_MedalCategoryNames[] =
{
"SFUI_MedalCategory_TeamAndObjective_CAPHTML",
"SFUI_MedalCategory_Combat_CAPHTML",
"SFUI_MedalCategory_Weapon_CAPHTML",
"SFUI_MedalCategory_Map_CAPHTML",
"SFUI_MedalCategory_GunGame_CAPHTML",
s_MedalCategoryNameActiveSeason.Access()
};
const char *s_MedalCategoryRankNames[] =
{
"SFUI_Medal_RankName_0",
"SFUI_Medal_RankName_1",
"SFUI_Medal_RankName_2",
"SFUI_Medal_RankName_3"
};
CPlayerRankManager::CPlayerRankManager()
: m_bMedalCategoriesBuilt( false ),
m_iEloBracketDelta( 0 ),
m_iNewEloBracket( -1 )
{
COMPILE_TIME_ASSERT( ARRAYSIZE( s_MedalCategoryRankNames ) == MEDAL_RANK_COUNT );
COMPILE_TIME_ASSERT( ARRAYSIZE( s_MedalCategoryNames ) == MEDAL_CATEGORY_COUNT );
Q_memset( m_rank, 0, sizeof( m_rank ) );
}
CPlayerRankManager::~CPlayerRankManager()
{
}
bool CPlayerRankManager::Init()
{
ListenForGameEvent( "achievement_earned_local" );
ListenForGameEvent( "round_start" );
ListenForGameEvent( "achievement_info_loaded" );
ListenForGameEvent( "seasoncoin_levelup" );
g_pMatchFramework->GetEventsSubscription()->Subscribe( this );
return true;
}
void CPlayerRankManager::Shutdown()
{
g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this );
StopListeningForAllEvents();
}
void CPlayerRankManager::LevelInitPostEntity()
{
// Intention here is to send this once we finish connecting to a server.
SendRankDataToServer();
ResetRecordedEloBracketChange();
}
void CPlayerRankManager::FireGameEvent( IGameEvent *event )
{
const char *name = event->GetName();
if ( 0 == Q_strcmp( name, "round_start" ) )
{
OnRoundStart();
}
else if ( 0 == Q_strcmp( name, "achievement_earned_local" ) )
{
OnAchievementEarned( event->GetInt( "achievement", CSInvalidAchievement ) );
}
else if ( 0 == Q_strcmp( name, "achievement_info_loaded" ) )
{
// After we retrieve achievement info from steam or titledata,
// get lists of our achievements in each category and determine the 'rank' for the player.
BuildMedalCategories();
}
else if ( 0 == Q_strcmp( name, "seasoncoin_levelup" ) )
{
int iPlayerIndex = event->GetInt( "player" );
int iCategory = event->GetInt( "category" );
int iRank = event->GetInt( "rank" );
C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
if ( C_BasePlayer::GetLocalPlayer() && pPlayer && C_BasePlayer::GetLocalPlayer() == pPlayer )
{
OnSeasonCoinLeveledUp( iCategory, iRank );
}
}
}
// Use CommandKeyValues to send our rank data to the server so it can
// network to other clients and display in the scoreboard.
void CPlayerRankManager::SendRankDataToServer( void )
{
#if 0
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pLocalPlayer || !engine->IsConnected() )
return;
KeyValues *kv = new KeyValues("player_medal_ranking");
for ( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
{
kv->SetInt( CFmtStr("rank%d",i), m_rank[i] );
}
// Base_CmdKeyValues handles the kv deletion.
engine->ServerCmdKeyValues( kv );
#endif
}
// Once we have achievements loaded, sort them into category lists and build our internal
// category info structures.
void CPlayerRankManager::BuildMedalCategories( void )
{
// Note: we cut splitscreen, so achievements should all be at idx 0.
CUtlMap<int, CBaseAchievement *> &achievements = g_AchievementMgrCS.GetAchievements( 0 );
// clear these out before we rebuild them
for ( int i=0; i<MEDAL_CATEGORY_COUNT; i++)
{
m_medalCategoryList[i].RemoveAll();
}
m_StatBasedAchievements.RemoveAll();
if ( achievements.Count() == 0 )
{
//TODO: need disable this whole system when this happens.
Warning( "PlayerRankManager: No achievements loaded!\n" );
m_bMedalCategoriesBuilt = false;
return;
}
int count[MEDAL_CATEGORY_COUNT];
memset( count, 0, sizeof( count ) );
FOR_EACH_MAP( achievements, idx )
{
CBaseAchievement *pAchievement = achievements[idx];
MedalCategory_t c = GetAchievementCategory( pAchievement->GetAchievementID() );
if ( c != MEDAL_CATEGORY_NONE )
{
count[c]++;
m_medalCategoryList[c].AddToTail( pAchievement );
// Keep a separate list of achievements withs stat goals so we can
// check for relevant stat changes then report those for display.
if ( !pAchievement->IsAchieved() && pAchievement->GetGoal() > 1 )
{
CAchievement_StatGoal *pStatGoal = dynamic_cast<CAchievement_StatGoal*>(pAchievement);
Assert( pStatGoal );
if ( pStatGoal )
{
m_StatBasedAchievements.AddToTail( pStatGoal );
}
}
}
}
// This may be temp... First guess at how to distribute the rank requirements
// This also assumes the medal rank enum never changes...
for ( int c = MEDAL_CATEGORY_START; c < MEDAL_CATEGORY_COUNT; ++c )
{
m_categoryInfo[c].m_totalInCategory = count[c];
m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_NONE] = 0;
m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_BRONZE] = (int)((float)(count[c])/3.0f); // ~1/3rd for bronze;
m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_SILVER] = (int)(2.0f*(float)(count[c])/3.0f); // ~2/3rd for silver;
m_categoryInfo[c].m_minCountForRank[MEDAL_RANK_GOLD] = count[c]; // all for gold
}
#if defined ( DEBUG )
int dbgCatCount = 0;
FOR_EACH_MAP( achievements, idx )
{
CBaseAchievement *pAchievement = achievements[idx];
if ( MEDAL_CATEGORY_NONE != GetAchievementCategory( pAchievement->GetAchievementID() ) )
{
dbgCatCount++;
}
}
// If you implement a new achievement, give it a category.
Assert( dbgCatCount == g_AchievementMgrCS.GetAchievementCount() );
#endif
m_bMedalCategoriesBuilt = true;
RecalculateRanks();
}
void CPlayerRankManager::RecalculateRanks( void )
{
for ( int j = MEDAL_CATEGORY_START; j < MEDAL_CATEGORY_COUNT; ++j )
{
MedalRank_t newRank = CalculateRankForCategory( (MedalCategory_t)j );
if ( newRank > m_rank[j] )
m_rank[j] = newRank;
}
}
MedalRank_t CPlayerRankManager::CalculateRankForCategory( MedalCategory_t category ) const
{
if ( m_bMedalCategoriesBuilt == false )
return MEDAL_RANK_NONE;
int count = CountAchievedInCategory( category );
const MedalCategoryInfo_t& info = m_categoryInfo[category];
int rank = MEDAL_RANK_NONE;
for ( int i = 0; i < MEDAL_RANK_COUNT; i++ )
{
// increase rank until we no longer meet the reqirements for the rank
if ( count >= info.m_minCountForRank[i] )
rank = i;
}
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: Calculating rank for category %d: %d (%d achieved, %d needed)\n", category, rank, count, info.m_minCountForRank[rank] );
return (MedalRank_t)rank;
}
int CPlayerRankManager::CountAchievedInCategory( MedalCategory_t category ) const
{
Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
return 0;
int achievedCount = 0;
FOR_EACH_VEC( m_medalCategoryList[category], iter )
{
CBaseAchievement *pAchievement = m_medalCategoryList[category][iter];
if ( pAchievement->IsAchieved() )
achievedCount++;
}
return achievedCount;
}
void CPlayerRankManager::OnRoundStart()
{
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: clearing all rank progress events at round start\n" );
m_RankIncreases.RemoveAll();
m_MedalsEarned.RemoveAll();
}
void CPlayerRankManager::OnAchievementEarned( int achievementId )
{
CBaseAchievement *pAchievement = g_AchievementMgrCS.GetAchievementByID( achievementId, 0 );
MedalCategory_t category = GetAchievementCategory( achievementId );
if ( !pAchievement || category == MEDAL_CATEGORY_NONE )
return;
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: Adding achievement event '%s'\n", pAchievement->GetName() );
MedalEarnedEvent_t event( pAchievement, category );
m_MedalsEarned.AddToTail( event );
MedalRank_t oldRank = m_rank[category];
MedalRank_t newRank = CalculateRankForCategory( category );
if ( newRank > oldRank )
{
if ( engine->IsConnected() )
{
RankIncreasedEvent_t event( pAchievement, category );
m_RankIncreases.AddToTail( event );
}
m_rank[category] = newRank;
SendRankDataToServer();
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: Increasing medal rank category %d to %d\n", category, m_rank[category] );
}
}
void CPlayerRankManager::OnSeasonCoinLeveledUp( int iCategory, int iRank )
{
// figure out which coin we leveled up
if ( iCategory <= MEDAL_CATEGORY_NONE )
{
for ( int i = MEDAL_CATEGORY_SEASON_COIN; i < MEDAL_CATEGORY_COUNT; i++ )
{
CheckCategoryRankLevelUp( i, -1 );
}
}
else
{
CheckCategoryRankLevelUp( iCategory, iRank );
}
}
bool CPlayerRankManager::CheckCategoryRankLevelUp( int iCategory, int iRank )
{
if ( iCategory != MEDAL_CATEGORY_SEASON_COIN )
return false;
bool bLeveledUp = false;
MedalCategory_t category = (MedalCategory_t)iCategory;
MedalRank_t oldRank = m_rank[category];
MedalRank_t newRank = (iRank > -1) ? (MedalRank_t)iRank : CalculateRankForCategory( category );
if ( newRank > oldRank )
{
if ( engine->IsConnected() && oldRank > MEDAL_RANK_NONE )
{
RankIncreasedEvent_t event( NULL, category );
m_RankIncreases.AddToTail( event );
}
m_rank[category] = newRank;
SendRankDataToServer();
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: Increasing medal rank category %d to %d\n", category, m_rank[category] );
bLeveledUp = true;
}
return bLeveledUp;
}
int CPlayerRankManager::GetTotalMedalsInCategory( MedalCategory_t category ) const
{
Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
return 0;
return m_categoryInfo[category].m_totalInCategory;
}
int CPlayerRankManager::GetMinMedalsForRank( MedalCategory_t category, MedalRank_t rank ) const
{
Assert( category >= MEDAL_CATEGORY_START && category < MEDAL_CATEGORY_COUNT );
if( category < MEDAL_CATEGORY_START || category >= MEDAL_CATEGORY_COUNT )
return 0;
Assert( rank >= MEDAL_RANK_NONE && rank < MEDAL_RANK_COUNT );
if( rank < MEDAL_RANK_NONE || rank >= MEDAL_RANK_COUNT )
return 0;
return m_categoryInfo[category].m_minCountForRank[rank];
}
void CPlayerRankManager::GetMedalStatsEarnedThisRound( CUtlVector<MedalStatEvent_t>& outVec ) const
{
const StatsCollection_t &roundStats = g_CSClientGameStats.GetRoundStats(0);
FOR_EACH_VEC( m_StatBasedAchievements, iter )
{
CAchievement_StatGoal *pStatGoal = m_StatBasedAchievements[iter];
if ( roundStats[pStatGoal->GetStatId()] > 0 && !pStatGoal->IsAchieved() )
{
MedalCategory_t cat = GetAchievementCategory( pStatGoal->GetAchievementID() );
if ( cat == MEDAL_CATEGORY_NONE )
{
Assert( 0 );
continue;
}
// Hackish: Logic for culling which stat goal achievements is going here.
// Current idea is any achievemnt that just reached or crossed a 20% border of the total
// gets sent to UI code for possible display.
int showProgresCount = MAX( 1, pStatGoal->GetGoal() / cl_medal_progress_shown_fraction.GetInt() ); // Show a progress notification in the win panel every X stats.
int cur = pStatGoal->GetCount();
int prev = cur - roundStats[pStatGoal->GetStatId()];
bool bCrossedBorder = false;
while ( cur != prev )
{
if ( cur%showProgresCount == 0 )
{
bCrossedBorder = true;
break;
}
cur--;
}
if ( cl_force_progress_notice_every_change.GetBool() )
bCrossedBorder = true;
if ( bCrossedBorder )
{
MedalStatEvent_t event( pStatGoal, cat, pStatGoal->GetStatId() );
outVec.AddToTail( event );
if ( cl_player_rank_debug.GetBool() )
Msg( "PlayerRankManager: Adding stat progress for achievement '%s' in stat %d\n", pStatGoal->GetName(), pStatGoal->GetStatId() );
}
}
}
}
const CUtlVector<RankIncreasedEvent_t>& CPlayerRankManager::GetRankIncreasesThisRound( void ) const
{
return m_RankIncreases;
}
const CUtlVector<MedalEarnedEvent_t>& CPlayerRankManager::GetMedalsEarnedThisRound( void ) const
{
return m_MedalsEarned;
}
const char *CPlayerRankManager::GetMedalCatagoryName( MedalCategory_t category )
{
Assert( category >= 0 && category < ARRAYSIZE( s_MedalCategoryNames ) );
return s_MedalCategoryNames[category];
}
const char *CPlayerRankManager::GetMedalCatagoryRankName( int nRank )
{
Assert( nRank >= 0 && nRank < ARRAYSIZE( s_MedalCategoryRankNames ) );
return s_MedalCategoryRankNames[nRank];
}
void CPlayerRankManager::PrintRankProgressThisRound() const
{
Msg( "Rank Increases:\n" );
FOR_EACH_VEC( m_RankIncreases, iter )
{
Msg( "PlayerRankManager: Rank increase because of medal '%s' in category %d\n", m_RankIncreases[iter].m_pAchievement->GetName(), m_RankIncreases[iter].m_category );
}
Msg( "Current Ranks: " );
for ( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
{
Msg( "%d", m_rank[i] );
}
Msg( "\n\n" );
Msg( "Medals Earned:\n" );
FOR_EACH_VEC( m_MedalsEarned, iter )
{
Msg( "PlayerRankManager: Earned medal '%s' in category %d\n", m_MedalsEarned[iter].m_pAchievement->GetName(), m_MedalsEarned[iter].m_category );
}
Msg( "\n\n" );
Msg( "Medal Progress made:\n" );
CUtlVector<MedalStatEvent_t> outVec;
GetMedalStatsEarnedThisRound( outVec );
FOR_EACH_VEC( outVec, iter )
{
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 );
}
}
void CPlayerRankManager::OnEvent( KeyValues *pEvent )
{
/* Removed for partner depot */
}
void CPlayerRankManager::NoteEloBracketChanged( int iOldBracket, int iNewBracket )
{
m_iEloBracketDelta += (iNewBracket - iOldBracket);
m_iNewEloBracket = iNewBracket;
}
// Returns the delta between the elo at the start of the map and now.
// Should be reset explictly when we've displayed the 'elo changed' update to the player.
int CPlayerRankManager::GetEloBracketChange( int &iOutNewEloBracket )
{
if ( m_iEloBracketDelta != 0 )
{
Assert( m_iNewEloBracket >= 0 );
iOutNewEloBracket = m_iNewEloBracket;
}
return m_iEloBracketDelta;
}
// Call this after we're sure we've shown the user their new rank.
void CPlayerRankManager::ResetRecordedEloBracketChange( void )
{
m_iEloBracketDelta = 0;
m_iNewEloBracket = -1;
}
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 )
{
g_PlayerRankManager.PrintRankProgressThisRound();
}
CON_COMMAND_F( print_achievement_categories, "Spews achievements for each category", FCVAR_DEVELOPMENTONLY )
{
CUtlMap<int, CBaseAchievement *> &achievements = g_AchievementMgrCS.GetAchievements( 0 );
for( int i = MEDAL_CATEGORY_START; i < MEDAL_CATEGORY_COUNT; ++i )
{
Msg( "Category %d:\n", i );
int count = 0;
int countAchieved = 0;
FOR_EACH_MAP( achievements, idx )
{
CBaseAchievement *pAchievement = achievements[idx];
if ( i == GetAchievementCategory( pAchievement->GetAchievementID() ) )
{
Msg( "%s %s\n", pAchievement->GetName(), pAchievement->IsAchieved() ? "*achieved*" : " " );
count++;
if ( pAchievement->IsAchieved() )
countAchieved++;
}
if ( GetAchievementCategory( pAchievement->GetAchievementID() ) == MEDAL_CATEGORY_NONE )
{
Msg( "***%s Uncategorized!\n", pAchievement->GetName() );
}
}
Msg( "Total: %d\n\n", count );
}
}
// Pretty ugly... Fragile hand-built categorization using the achievement excel sheet. May need to revisit how
// we tag an achievement with a category more formally.
MedalCategory_t GetAchievementCategory( int id )
{
switch( id )
{
// Team and Objective
case CSMedalist:
case CSWinBombPlant:
case CSWinBombDefuse:
case CSBombDefuseCloseCall:
case CSKilledDefuser:
case CSPlantBombWithin25Seconds:
case CSKillBombPickup:
case CSBombMultikill:
case CSGooseChase:
case CSWinBombPlantAfterRecovery:
case CSDefuseDefense:
case CSPlantBombsLow:
case CSDefuseBombsLow:
case CSRescueAllHostagesInARound:
case CSKilledRescuer:
case CSFastHostageRescue:
case CSRescueHostagesLow:
case CSRescueHostagesMid:
case CSWinRoundsLow:
case CSWinRoundsMed:
case CSWinRoundsHigh:
case CSFastRoundWin:
case CSLosslessExtermination:
case CSFlawlessVictory:
case CSDonateWeapons:
case CSBloodlessVictory:
case CSMoneyEarnedLow:
case CSMoneyEarnedMed:
case CSMoneyEarnedHigh:
case CSKillEnemyTeam:
case CSLastPlayerAlive:
case CSWinPistolRoundsLow:
case CSWinPistolRoundsMed:
case CSWinPistolRoundsHigh:
case CSSilentWin:
case CSWinRoundsWithoutBuying:
case CSAvengeFriend:
return MEDAL_CATEGORY_TEAM_AND_OBJECTIVE;
// Combat
case CSEnemyKillsLow:
case CSEnemyKillsMed:
case CSEnemyKillsHigh:
case CSKillEnemyReloading:
case CSKillingSpree:
case CSKillsWithMultipleGuns:
case CSHeadshots:
case CSSurviveGrenade:
case CSKillEnemyBlinded:
case CSKillEnemiesWhileBlind:
case CSKillEnemiesWhileBlindHard:
case CSKillsEnemyWeapon:
case CSWinKnifeFightsLow:
case CSWinKnifeFightsHigh:
case CSKilledDefuserWithGrenade:
case CSKillSniperWithSniper:
case CSKillSniperWithKnife:
case CSHipShot:
case CSKillSnipers:
case CSKillWhenAtLowHealth:
case CSPistolRoundKnifeKill:
case CSWinDualDuel:
case CSGrenadeMultikill:
case CSKillWhileInAir:
case CSKillEnemyInAir:
case CSKillerAndEnemyInAir:
case CSKillEnemyWithFormerGun:
case CSKillTwoWithOneShot:
case CSGiveDamageLow:
case CSGiveDamageMed:
case CSGiveDamageHigh:
case CSKillEnemyLastBullet:
case CSKillingSpreeEnder:
case CSSurviveManyAttacks:
case CSDamageNoKill:
case CSKillLowDamage:
case CSUnstoppableForce:
case CSImmovableObject:
case CSHeadshotsInRound:
case CSCauseFriendlyFireWithFlashbang:
return MEDAL_CATEGORY_COMBAT;
// Weapon
case CSEnemyKillsDeagle:
case CSEnemyKillsHKP2000:
case CSEnemyKillsGlock:
case CSEnemyKillsP250:
case CSEnemyKillsElite:
case CSEnemyKillsFiveSeven:
case CSEnemyKillsAWP:
case CSEnemyKillsAK47:
case CSEnemyKillsM4A1:
case CSEnemyKillsAUG:
case CSEnemyKillsSG556:
case CSEnemyKillsSCAR20:
case CSEnemyKillsGALILAR:
case CSEnemyKillsFAMAS:
case CSEnemyKillsSSG08:
case CSEnemyKillsG3SG1:
case CSEnemyKillsP90:
case CSEnemyKillsMP7:
case CSEnemyKillsMP9:
case CSEnemyKillsMAC10:
case CSEnemyKillsUMP45:
case CSEnemyKillsNova:
case CSEnemyKillsXM1014:
case CSEnemyKillsMag7:
case CSEnemyKillsM249:
case CSEnemyKillsNegev:
case CSEnemyKillsTec9:
case CSEnemyKillsSawedoff:
case CSEnemyKillsBizon:
case CSEnemyKillsKnife:
case CSEnemyKillsHEGrenade:
case CSEnemyKillsMolotov:
case CSPosthumousGrenadeKill:
case CSMetaPistol:
case CSMetaRifle:
case CSMetaSMG:
case CSMetaShotgun:
case CSMetaWeaponMaster:
case CSEnemyKillsTaser:
case CSKillWithEveryWeapon:
return MEDAL_CATEGORY_WEAPON;
// Map
case CSWinMapCS_ITALY:
case CSWinMapCS_OFFICE:
case CSWinMapDE_AZTEC:
case CSWinMapDE_DUST:
case CSWinMapDE_DUST2:
case CSWinMapDE_INFERNO:
case CSWinMapDE_NUKE:
case CSWinMapDE_TRAIN:
case CSWinMatchAR_SHOOTS:
case CSWinMatchAR_BAGGAGE:
case CSWinMatchDE_LAKE:
case CSWinMatchDE_SAFEHOUSE:
case CSWinMatchDE_SUGARCANE:
case CSWinMatchDE_STMARC:
case CSWinMatchDE_BANK:
case CSWinMatchDE_SHORTTRAIN:
case CSBreakWindows:
//case CSBreakProps:
return MEDAL_CATEGORY_MAP;
// Arsenal
case CSGunGameKillKnifer:
case CSWinEveryGGMap:
case CSGunGameProgressiveRampage:
case CSGunGameFirstKill:
case CSFirstBulletKills:
case CSGunGameConservationist:
case CSPlantBombsTRLow:
case CSDefuseBombsTRLow:
case CSKillEnemyTerrTeamBeforeBombPlant:
case CSKillEnemyCTTeamBeforeBombPlant:
case CSBornReady:
case CSSpawnCamper:
case CSGunGameKnifeKillKnifer:
case CSGunGameSMGKillKnifer:
case CSStillAlive:
case CSGGRoundsLow:
case CSGGRoundsMed:
case CSGGRoundsHigh:
case CSGGWinRoundsLow:
case CSGGWinRoundsMed:
case CSGGWinRoundsHigh:
case CSGGWinRoundsExtreme:
case CSGGWinRoundsUltimate:
case CSPlayEveryGGMap:
case CSDominationsLow:
case CSDominationsHigh:
case CSRevengesLow:
case CSRevengesHigh:
case CSDominationOverkillsLow:
case CSDominationOverkillsHigh:
case CSDominationOverkillsMatch:
case CSExtendedDomination:
case CSConcurrentDominations:
return MEDAL_CATEGORY_ARSENAL;
default:
break;
}
// Disabling assert for now, there are some stale achievements with no category that need to be removed first.
//AssertMsg( 0, "Found achievement with no known category." );
return MEDAL_CATEGORY_NONE;
}
// Find the loc string with the correct phrasing for this stat based achievement's progress
// ie, "You've killed X out of Y enemies" vs "You've planted X out of Y bombs".
// Returns NULL if no string token mapped to stat id.
const char* GetLocTokenForStatId( const CSStatType_t id )
{
switch ( id )
{
case CSSTAT_KILLS:
case CSSTAT_KILLS_DEAGLE:
case CSSTAT_KILLS_GLOCK:
case CSSTAT_KILLS_ELITE:
case CSSTAT_KILLS_FIVESEVEN:
case CSSTAT_KILLS_BIZON:
case CSSTAT_KILLS_TEC9:
case CSSTAT_KILLS_TASER:
case CSSTAT_KILLS_HKP2000:
case CSSTAT_KILLS_P250:
case CSSTAT_KILLS_AWP:
case CSSTAT_KILLS_AK47:
case CSSTAT_KILLS_M4A1:
case CSSTAT_KILLS_AUG:
case CSSTAT_KILLS_GALILAR:
case CSSTAT_KILLS_FAMAS:
case CSSTAT_KILLS_G3SG1:
case CSSTAT_KILLS_SCAR20:
case CSSTAT_KILLS_SG556:
case CSSTAT_KILLS_SSG08:
case CSSTAT_KILLS_P90:
case CSSTAT_KILLS_MAC10:
case CSSTAT_KILLS_UMP45:
case CSSTAT_KILLS_MP7:
case CSSTAT_KILLS_MP9:
case CSSTAT_KILLS_XM1014:
case CSSTAT_KILLS_MAG7:
case CSSTAT_KILLS_SAWEDOFF:
case CSSTAT_KILLS_NOVA:
case CSSTAT_KILLS_M249:
case CSSTAT_KILLS_NEGEV:
case CSSTAT_KILLS_KNIFE:
case CSSTAT_KILLS_HEGRENADE:
case CSSTAT_KILLS_MOLOTOV:
case CSSTAT_KILLS_ENEMY_WEAPON:
case CSSTAT_KILLS_ENEMY_BLINDED:
case CSSTAT_KILLS_KNIFE_FIGHT:
case CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER:
return "SFUI_WinPanelProg_stat_progress_kills";
case CSSTAT_MAP_WINS_CS_ITALY:
case CSSTAT_MAP_WINS_CS_OFFICE:
case CSSTAT_MAP_WINS_DE_AZTEC:
case CSSTAT_MAP_WINS_DE_DUST2:
case CSSTAT_MAP_WINS_DE_DUST:
case CSSTAT_MAP_WINS_DE_INFERNO:
case CSSTAT_MAP_WINS_DE_NUKE:
case CSSTAT_MAP_WINS_DE_TRAIN:
case CSSTAT_MAP_MATCHES_WON_BANK:
case CSSTAT_MAP_MATCHES_WON_SHOOTS:
case CSSTAT_MAP_MATCHES_WON_STMARC:
case CSSTAT_MAP_MATCHES_WON_SUGARCANE:
case CSSTAT_MAP_MATCHES_WON_SAFEHOUSE:
case CSSTAT_MAP_MATCHES_WON_LAKE:
case CSSTAT_MAP_MATCHES_WON_BAGGAGE:
case CSSTAT_MAP_MATCHES_WON_SHORTTRAIN:
case CSSTAT_ROUNDS_WON:
case CSSTAT_PISTOLROUNDS_WON:
case CSSTAT_GUN_GAME_MATCHES_WON:
return "SFUI_WinPanelProg_stat_progress_wins";
case CSSTAT_GUN_GAME_MATCHES_PLAYED:
return "SFUI_WinPanelProg_stat_progress_played";
case CSSTAT_MONEY_EARNED:
return "SFUI_WinPanelProg_stat_progress_money";
case CSSTAT_DAMAGE:
return "SFUI_WinPanelProg_stat_progress_damage";
case CSSTAT_KILLS_HEADSHOT:
return "SFUI_WinPanelProg_stat_progress_headshots";
case CSSTAT_NUM_BOMBS_PLANTED:
return "SFUI_WinPanelProg_stat_progress_bomb_plant";
case CSSTAT_NUM_BOMBS_DEFUSED:
return "SFUI_WinPanelProg_stat_progress_bomb_diffuse";
case CSSTAT_NUM_HOSTAGES_RESCUED:
return "SFUI_WinPanelProg_stat_progress_rescue";
case CSSTAT_WEAPONS_DONATED:
return "SFUI_WinPanelProg_stat_progress_donate";
case CSSTAT_DOMINATIONS:
return "SFUI_WinPanelProg_stat_progress_dominate";
case CSSTAT_DOMINATION_OVERKILLS:
return "SFUI_WinPanelProg_stat_progress_overkill";
case CSSTAT_REVENGES:
return "SFUI_WinPanelProg_stat_progress_revenge";
default:
break;
}
return NULL;
}
bool CPlayerRankManager::HasBuiltMedalCategories( void ) const
{
return m_bMedalCategoriesBuilt;
}
#if defined ( _GAMECONSOLE )
// Official servers request elo bracket info when a player connects.
bool MsgFunc_RequestEloBracketInfo( const CCSUsrMsg_RequestEloBracketInfo &msg )
{
ELOGameType_t game_mode = (ELOGameType_t) msg.bracket();
g_PlayerRankManager.ServerRequestBracketInfo( game_mode );
return true;
}
// We want all the bracket calculation logic to happen on the server (because we expect to change it post ship)
// but all the storage for console must happen on the client. Thus this horribleness.
void CPlayerRankManager::ServerRequestBracketInfo( ELOGameType_t game_mode )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pLocalPlayer || !engine->IsConnected() )
return;
// Restrict bracket changes to gamempads for console
InputDevice_t device = g_pInputSystem->GetCurrentInputDevice();
if ( device != INPUT_DEVICE_GAMEPAD )
return;
unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
if ( idx == m_PlayerBracketInfos.InvalidIndex() )
return;
PlayerELOBracketInfo_t& eloBracketInfo = m_PlayerBracketInfos[idx];
KeyValues *kv = new KeyValues("player_elo_bracket_info");
kv->SetInt( "game_mode", game_mode );
kv->SetInt( "display_bracket", eloBracketInfo.m_DisplayBracket );
kv->SetInt( "previous_bracket", eloBracketInfo.m_PreviousBracket );
kv->SetInt( "games_in_bracket", eloBracketInfo.m_NumGamesInBracket );
// Base_CmdKeyValues handles the kv deletion.
engine->ServerCmdKeyValues( kv );
}
// official servers send this after rounds end so clients can handle storage.
bool MsgFunc_SetEloBracketInfo( const CCSUsrMsg_SetEloBracketInfo &msg )
{
ELOGameType_t game_mode = (ELOGameType_t) msg.game_mode();
int8 display_bracket = msg.display_bracket();
int8 prev_bracket = msg.prev_bracket();
int8 games_in_bracket = msg.num_games_in_bracket();
//Msg( "SetEloBracket %d %d %d %d\n", game_mode, display_bracket, prev_bracket, games_in_bracket );
g_PlayerRankManager.Console_SetEloBracket( game_mode, display_bracket, prev_bracket, games_in_bracket );
return true;
}
bool CPlayerRankManager::Console_SetEloBracket( ELOGameType_t game_mode, uint8 display_bracket, uint8 prev_bracket, uint8 num_games_in_bracket )
{
// make sure they'll fit in four bits.
Assert( (display_bracket & 0xF0) == 0 );
Assert( (prev_bracket & 0xF0) == 0 );
//Msg( " %d %d %d %d \n", game_mode, display_bracket, prev_bracket, num_games_in_bracket );
unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
if ( idx == m_PlayerBracketInfos.InvalidIndex() )
{
if ( game_mode > ELOGameType::INVALID && game_mode < ELOGameType::COUNT )
{
idx = m_PlayerBracketInfos.Insert( game_mode );
}
else
{
return false;
}
}
PlayerELOBracketInfo_t& eloBracketInfo = m_PlayerBracketInfos[idx];
eloBracketInfo.m_DisplayBracket = display_bracket;
eloBracketInfo.m_PreviousBracket = prev_bracket;
eloBracketInfo.m_NumGamesInBracket = num_games_in_bracket;
return true;
}
bool CPlayerRankManager::Console_SetEloBracket( ELOGameType_t game_mode, const PlayerELOBracketInfo_t& bracket )
{
return Console_SetEloBracket( game_mode, bracket.m_DisplayBracket, bracket.m_PreviousBracket, bracket.m_NumGamesInBracket );
}
// Returns the display bracket, -1 on failure (no recorded bracket info for specified game mode).
// Optional pointer to elo bracket struct can be filled out.
int CPlayerRankManager::Console_GetEloBracket( ELOGameType_t game_mode, PlayerELOBracketInfo_t *pOutBracketInfo /*=NULL*/ )
{
unsigned short idx = m_PlayerBracketInfos.Find( game_mode );
if ( idx != m_PlayerBracketInfos.InvalidIndex() )
{
PlayerELOBracketInfo_t &info = m_PlayerBracketInfos[idx];
if ( pOutBracketInfo )
*pOutBracketInfo = info;
return info.m_DisplayBracket;
}
return -1;
}
#endif // _GAMECONSOLE