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.

1721 lines
56 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: CS game stats
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // Some tricky business here - we don't want to include the precompiled header for the statreader
  8. // and trying to #ifdef it out does funky things like ignoring the #endif. Define our header file
  9. // separately and include it based on the switch
  10. #include "cbase.h"
  11. #include <tier0/platform.h>
  12. #include "cs_gamerules.h"
  13. #include "cs_gamestats.h"
  14. #include "weapon_csbase.h"
  15. #include "props.h"
  16. #include "cs_achievement_constants.h"
  17. #include "../../shared/cstrike/weapon_c4.h"
  18. #include <time.h>
  19. #include "filesystem.h"
  20. #include "bot_util.h"
  21. #include "cdll_int.h"
  22. #include "steamworks_gamestats.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. float g_flGameStatsUpdateTime = 0.0f;
  26. short g_iTerroristVictories[CS_NUM_LEVELS];
  27. short g_iCounterTVictories[CS_NUM_LEVELS];
  28. short g_iWeaponPurchases[WEAPON_MAX];
  29. short g_iAutoBuyPurchases = 0;
  30. short g_iReBuyPurchases = 0;
  31. short g_iAutoBuyM4A1Purchases = 0;
  32. short g_iAutoBuyAK47Purchases = 0;
  33. short g_iAutoBuyFamasPurchases = 0;
  34. short g_iAutoBuyGalilPurchases = 0;
  35. short g_iAutoBuyVestHelmPurchases = 0;
  36. short g_iAutoBuyVestPurchases = 0;
  37. struct PropModelStats_t
  38. {
  39. const char* szPropModelName;
  40. CSStatType_t statType;
  41. } PropModelStatsTableInit[] =
  42. {
  43. { "models/props/cs_office/computer_caseb.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  44. { "models/props/cs_office/computer_monitor.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  45. { "models/props/cs_office/phone.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  46. { "models/props/cs_office/projector.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  47. { "models/props/cs_office/TV_plasma.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  48. { "models/props/cs_office/computer_keyboard.mdl", CSSTAT_PROPSBROKEN_OFFICEELECTRONICS },
  49. { "models/props/cs_office/radio.mdl", CSSTAT_PROPSBROKEN_OFFICERADIO },
  50. { "models/props/cs_office/trash_can.mdl", CSSTAT_PROPSBROKEN_OFFICEJUNK },
  51. { "models/props/cs_office/file_box.mdl", CSSTAT_PROPSBROKEN_OFFICEJUNK },
  52. { "models/props_junk/watermelon01.mdl", CSSTAT_PROPSBROKEN_ITALY_MELON },
  53. // models/props/de_inferno/claypot01.mdl
  54. // models/props/de_inferno/claypot02.mdl
  55. // models/props/de_dust/grainbasket01c.mdl
  56. // models/props_junk/wood_crate001a.mdl
  57. // models/props/cs_office/file_box_p1.mdl
  58. };
  59. struct ServerStats_t
  60. {
  61. int achievementId;
  62. int statId;
  63. int roundRequirement;
  64. int matchRequirement;
  65. const char* mapFilter;
  66. bool IsMet(int roundStat, int matchStat)
  67. {
  68. return roundStat >= roundRequirement && matchStat >= matchRequirement;
  69. }
  70. } ServerStatBasedAchievements[] =
  71. {
  72. { CSBreakWindows, CSSTAT_NUM_BROKEN_WINDOWS, AchievementConsts::BreakWindowsInOfficeRound_Windows, 0, "cs_office" },
  73. { CSBreakProps, CSSTAT_PROPSBROKEN_ALL, AchievementConsts::BreakPropsInRound_Props, 0, NULL },
  74. { CSUnstoppableForce, CSSTAT_KILLS, AchievementConsts::UnstoppableForce_Kills, 0, NULL },
  75. { CSHeadshotsInRound, CSSTAT_KILLS_HEADSHOT, AchievementConsts::HeadshotsInRound_Kills, 0, NULL },
  76. { CSDominationOverkillsMatch, CSSTAT_DOMINATION_OVERKILLS, 0, 10, NULL },
  77. };
  78. //=============================================================================
  79. // HPE_BEGIN:
  80. // [Forrest] Allow nemesis/revenge to be turned off for a server
  81. //=============================================================================
  82. static void SvNoNemesisChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
  83. {
  84. ConVarRef var( pConVar );
  85. if ( var.IsValid() && var.GetBool() )
  86. {
  87. // Clear all nemesis relationships.
  88. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  89. {
  90. CCSPlayer *pTemp = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  91. if ( pTemp )
  92. {
  93. pTemp->RemoveNemesisRelationships();
  94. }
  95. }
  96. }
  97. }
  98. ConVar sv_nonemesis( "sv_nonemesis", "0", 0, "Disable nemesis and revenge.", SvNoNemesisChangeCallback );
  99. //=============================================================================
  100. // HPE_END
  101. //=============================================================================
  102. int GetCSLevelIndex( const char *pLevelName )
  103. {
  104. for ( int i = 0; MapName_StatId_Table[i].statWinsId != CSSTAT_UNDEFINED; i ++ )
  105. {
  106. if ( Q_strcmp( pLevelName, MapName_StatId_Table[i].szMapName ) == 0 )
  107. return i;
  108. }
  109. return -1;
  110. }
  111. ConVar sv_debugroundstats( "sv_debugroundstats", "0", 0, "A temporary variable that will print extra information about stats upload which may be useful in debugging any problems." );
  112. //=============================================================================
  113. // HPE_BEGIN:
  114. // [menglish] Addition of CCS_GameStats class
  115. //=============================================================================
  116. CCSGameStats CCS_GameStats;
  117. CCSGameStats::StatContainerList_t* CCSGameStats::s_StatLists = new CCSGameStats::StatContainerList_t();
  118. //-----------------------------------------------------------------------------
  119. // Purpose: Constructor
  120. // Input : -
  121. //-----------------------------------------------------------------------------
  122. CCSGameStats::CCSGameStats()
  123. {
  124. gamestats = this;
  125. Clear();
  126. m_fDisseminationTimerLow = m_fDisseminationTimerHigh = 0.0f;
  127. // create table for mapping prop models to stats
  128. for ( int i = 0; i < ARRAYSIZE(PropModelStatsTableInit); ++i)
  129. {
  130. m_PropStatTable.Insert(PropModelStatsTableInit[i].szPropModelName, PropModelStatsTableInit[i].statType);
  131. }
  132. m_numberOfRoundsForDirectAverages = 0;
  133. m_numberOfTerroristEntriesForDirectAverages = 0;
  134. m_numberOfCounterTerroristEntriesForDirectAverages = 0;
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Destructor
  138. // Input : -
  139. //-----------------------------------------------------------------------------
  140. CCSGameStats::~CCSGameStats()
  141. {
  142. Clear();
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose: Clear out game stats
  146. // Input : -
  147. //-----------------------------------------------------------------------------
  148. void CCSGameStats::Clear( void )
  149. {
  150. m_PlayerSnipedPosition.Purge();
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. bool CCSGameStats::Init( void )
  156. {
  157. ListenForGameEvent( "round_start" );
  158. ListenForGameEvent( "round_end" );
  159. ListenForGameEvent( "break_prop" );
  160. ListenForGameEvent( "player_decal");
  161. ListenForGameEvent( "hegrenade_detonate");
  162. return true;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. //-----------------------------------------------------------------------------
  167. void CCSGameStats::PostInit( void )
  168. {
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. void CCSGameStats::LevelShutdownPreClearSteamAPIContext( void )
  174. {
  175. // If we have any unsent round stats we better send them now or we'll lose them
  176. UploadRoundStats();
  177. GetSteamWorksSGameStatsUploader().EndSession();
  178. }
  179. extern double g_rowCommitTime;
  180. extern double g_rowWriteTime;
  181. void CCSGameStats::UploadRoundStats( void )
  182. {
  183. CFastTimer totalTimer, purchasesAndDeathsStatTimer, weaponsStatBuildTimer, weaponsStatSubmitTimer, submitTimer, cleanupTimer;
  184. // Prevent uploading empty data
  185. if ( !m_bInRound )
  186. return;
  187. if ( sv_noroundstats.GetBool() )
  188. {
  189. if ( sv_debugroundstats.GetBool() )
  190. {
  191. Msg( "sv_noroundstats is enabled. Not uploading round stats.\n" );
  192. }
  193. m_MarketPurchases.PurgeAndDeleteElements();
  194. m_WeaponData.PurgeAndDeleteElements();
  195. m_DeathData.PurgeAndDeleteElements();
  196. return;
  197. }
  198. m_bInRound = false;
  199. KeyValues *pKV = new KeyValues( "basedata" );
  200. if ( !pKV )
  201. return;
  202. totalTimer.Start();
  203. purchasesAndDeathsStatTimer.Start();
  204. const char *pzMapName = gpGlobals->mapname.ToCStr();
  205. pKV->SetString( "MapID", pzMapName );
  206. for ( int k=0 ; k < m_MarketPurchases.Count() ; ++k )
  207. SubmitStat( m_MarketPurchases[ k ] );
  208. for ( int k=0 ; k < m_DeathData.Count() ; ++k )
  209. SubmitStat( m_DeathData[ k ] );
  210. purchasesAndDeathsStatTimer.End();
  211. weaponsStatBuildTimer.Start();
  212. // Now add the weapon stats that HPE collected
  213. for (int iWeapon = 0; iWeapon < WEAPON_MAX; ++iWeapon)
  214. {
  215. CCSWeaponInfo* pInfo = GetWeaponInfo( (CSWeaponID)iWeapon );
  216. if ( !pInfo )
  217. continue;
  218. const char* pWeaponName = pInfo->szClassName;
  219. if ( !pWeaponName || !pWeaponName[0] || ( m_weaponStats[iWeapon][0].shots == 0 && m_weaponStats[iWeapon][1].shots == 0 ) )
  220. continue;
  221. m_WeaponData.AddToTail( new SCSSWeaponData( pWeaponName, m_weaponStats[iWeapon][0] ) );
  222. // Now add the bot data if we're collecting detailled data
  223. if ( GetSteamWorksSGameStatsUploader().IsCollectingDetails() )
  224. {
  225. char pWeaponNameModified[64];
  226. V_snprintf( pWeaponNameModified, ARRAYSIZE(pWeaponNameModified), "%s_bot", pWeaponName );
  227. m_WeaponData.AddToTail( new SCSSWeaponData( pWeaponNameModified, m_weaponStats[iWeapon][1] ) );
  228. }
  229. }
  230. weaponsStatBuildTimer.End();
  231. weaponsStatSubmitTimer.Start();
  232. for ( int k=0 ; k < m_WeaponData.Count() ; ++k )
  233. SubmitStat( m_WeaponData[ k ] );
  234. weaponsStatSubmitTimer.End();
  235. // Perform the actual submission
  236. submitTimer.Start();
  237. SubmitGameStats( pKV );
  238. submitTimer.End();
  239. int iPurchases, iDeathData, iWeaponData;
  240. int listCount = s_StatLists->Count();
  241. iPurchases = m_MarketPurchases.Count();
  242. iDeathData = m_DeathData.Count();
  243. iWeaponData = m_WeaponData.Count();
  244. // Clear out the per round stats
  245. cleanupTimer.Start();
  246. m_MarketPurchases.Purge();
  247. m_WeaponData.Purge();
  248. m_DeathData.Purge();
  249. pKV->deleteThis();
  250. cleanupTimer.End();
  251. totalTimer.End();
  252. if ( sv_debugroundstats.GetBool() )
  253. {
  254. Msg( "**** ROUND STAT DEBUG ****\n" );
  255. Msg( "UploadRoundStats completed. %.3f msec. Breakdown:\n a: %.3f msec\n b: %.3f msec\n c: %.3f msec\n d: %.3f msec\n e: %.3f msec\n objects: %d %d %d %d\n commit: %.3fms\n write: %.3fms.\n\n",
  256. totalTimer.GetDuration().GetMillisecondsF(),
  257. purchasesAndDeathsStatTimer.GetDuration().GetMillisecondsF(),
  258. weaponsStatBuildTimer.GetDuration().GetMillisecondsF(),
  259. weaponsStatSubmitTimer.GetDuration().GetMillisecondsF(),
  260. submitTimer.GetDuration().GetMillisecondsF(),
  261. cleanupTimer.GetDuration().GetMillisecondsF(),
  262. iPurchases, iDeathData, iWeaponData, listCount,
  263. g_rowCommitTime, g_rowWriteTime);
  264. }
  265. }
  266. #ifdef _DEBUG
  267. CON_COMMAND ( teststats, "Test command" )
  268. {
  269. CFastTimer totalTimer;
  270. double uploadTime = 0.0f;
  271. g_rowCommitTime = 0.0f;
  272. g_rowWriteTime = 0.0f;
  273. for( int i = 0; i < 1000; i++ )
  274. {
  275. KeyValues *pKV = new KeyValues( "basedata" );
  276. if ( !pKV )
  277. return;
  278. pKV->SetName( "foobartest" );
  279. pKV->SetUint64( "test1", 1234 );
  280. pKV->SetUint64( "test2", 1234 );
  281. pKV->SetUint64( "test3", 1234 );
  282. pKV->SetUint64( "test4", 1234 );
  283. pKV->SetString( "test5", "TEST1234567890TEST1234567890TEST!");
  284. /*pKV->SetString( "test6", "TEST1234567890TEST1234567890TEST!");
  285. pKV->SetString( "test7", "TEST1234567890TEST1234567890TEST!");
  286. pKV->SetString( "test8", "TEST1234567890TEST1234567890TEST!");
  287. pKV->SetString( "test9", "TEST1234567890TEST1234567890TEST!");*/
  288. totalTimer.Start();
  289. GetSteamWorksSGameStatsUploader().AddStatsForUpload( pKV, args.ArgC() == 1 );
  290. totalTimer.End();
  291. uploadTime += totalTimer.GetDuration().GetMillisecondsF();
  292. }
  293. Msg( "teststats took %.3f msec commit: %.3fms write: %.3fms.\n", uploadTime, g_rowCommitTime, g_rowWriteTime );
  294. }
  295. #endif
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. //-----------------------------------------------------------------------------
  299. void CCSGameStats::SubmitGameStats( KeyValues *pKV )
  300. {
  301. int listCount = s_StatLists->Count();
  302. for( int i=0; i < listCount; ++i )
  303. {
  304. // Create a master key value that has stats everybody should share (map name, session ID, etc)
  305. (*s_StatLists)[i]->SendData(pKV);
  306. (*s_StatLists)[i]->Clear();
  307. }
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. //-----------------------------------------------------------------------------
  312. void CCSGameStats::Event_ShotFired( CBasePlayer *pPlayer, CBaseCombatWeapon* pWeapon )
  313. {
  314. Assert( pPlayer );
  315. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  316. CWeaponCSBase* pCSWeapon = dynamic_cast< CWeaponCSBase * >(pWeapon);
  317. //=============================================================================
  318. // HPE_BEGIN:
  319. // [dwenger] adding tracking for weapon used fun fact
  320. //=============================================================================
  321. if ( pCSPlayer )
  322. {
  323. // [dwenger] Update the player's tracking of which weapon type they fired
  324. pCSPlayer->PlayerUsedFirearm( pWeapon );
  325. //=============================================================================
  326. // HPE_END
  327. //=============================================================================
  328. IncrementStat( pCSPlayer, CSSTAT_SHOTS_FIRED, 1 );
  329. // Increment the individual weapon
  330. if( pCSWeapon )
  331. {
  332. CSWeaponID weaponId = pCSWeapon->GetWeaponID();
  333. for (int i = 0; WeaponName_StatId_Table[i].shotStatId != CSSTAT_UNDEFINED; ++i)
  334. {
  335. if ( WeaponName_StatId_Table[i].weaponId == weaponId )
  336. {
  337. IncrementStat( pCSPlayer, WeaponName_StatId_Table[i].shotStatId, 1 );
  338. break;
  339. }
  340. }
  341. int iType = pCSPlayer->IsBot();
  342. ++m_weaponStats[weaponId][iType].shots;
  343. }
  344. }
  345. }
  346. void CCSGameStats::Event_ShotHit( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
  347. {
  348. Assert( pPlayer );
  349. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  350. IncrementStat( pCSPlayer, CSSTAT_SHOTS_HIT, 1 );
  351. CBaseEntity *pInflictor = info.GetInflictor();
  352. if ( pInflictor )
  353. {
  354. if ( pInflictor == pPlayer )
  355. {
  356. if ( pPlayer->GetActiveWeapon() )
  357. {
  358. CWeaponCSBase* pCSWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->GetActiveWeapon());
  359. if (pCSWeapon)
  360. {
  361. CSWeaponID weaponId = pCSWeapon->GetWeaponID();
  362. for (int i = 0; WeaponName_StatId_Table[i].shotStatId != CSSTAT_UNDEFINED; ++i)
  363. {
  364. if ( WeaponName_StatId_Table[i].weaponId == weaponId )
  365. {
  366. IncrementStat( pCSPlayer, WeaponName_StatId_Table[i].hitStatId, 1 );
  367. break;
  368. }
  369. }
  370. }
  371. }
  372. }
  373. }
  374. }
  375. void CCSGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
  376. {
  377. Assert( pPlayer );
  378. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  379. IncrementStat( pCSPlayer, CSSTAT_DEATHS, 1 );
  380. }
  381. void CCSGameStats::Event_PlayerSprayedDecal( CCSPlayer* pPlayer )
  382. {
  383. IncrementStat( pPlayer, CSSTAT_DECAL_SPRAYS, 1 );
  384. }
  385. void CCSGameStats::Event_PlayerKilled_PreWeaponDrop( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
  386. {
  387. Assert( pPlayer );
  388. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  389. CCSPlayer *pAttacker = ToCSPlayer( info.GetAttacker() );
  390. bool victimZoomed = ( pCSPlayer->GetFOV() != pCSPlayer->GetDefaultFOV() );
  391. if (victimZoomed)
  392. {
  393. IncrementStat(pAttacker, CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, 1);
  394. }
  395. //Check for knife fight
  396. if (pAttacker && pCSPlayer && pAttacker == info.GetInflictor() && pAttacker->GetTeamNumber() != pCSPlayer->GetTeamNumber())
  397. {
  398. CWeaponCSBase* attackerWeapon = pAttacker->GetActiveCSWeapon();
  399. CWeaponCSBase* victimWeapon = pCSPlayer->GetActiveCSWeapon();
  400. if (attackerWeapon && victimWeapon)
  401. {
  402. CSWeaponID attackerWeaponID = attackerWeapon->GetWeaponID();
  403. CSWeaponID victimWeaponID = victimWeapon->GetWeaponID();
  404. if (attackerWeaponID == WEAPON_KNIFE && victimWeaponID == WEAPON_KNIFE)
  405. {
  406. IncrementStat(pAttacker, CSSTAT_KILLS_KNIFE_FIGHT, 1);
  407. }
  408. }
  409. }
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose:
  413. //-----------------------------------------------------------------------------
  414. void CCSGameStats::Event_BombPlanted( CCSPlayer* pPlayer)
  415. {
  416. IncrementStat( pPlayer, CSSTAT_NUM_BOMBS_PLANTED, 1 );
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose:
  420. //-----------------------------------------------------------------------------
  421. void CCSGameStats::Event_BombDefused( CCSPlayer* pPlayer)
  422. {
  423. IncrementStat( pPlayer, CSSTAT_NUM_BOMBS_DEFUSED, 1 );
  424. IncrementStat( pPlayer, CSSTAT_OBJECTIVES_COMPLETED, 1 );
  425. if( pPlayer && pPlayer->HasDefuser() )
  426. {
  427. IncrementStat( pPlayer, CSSTAT_BOMBS_DEFUSED_WITHKIT, 1 );
  428. }
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose: Increment terrorist team stat
  432. //-----------------------------------------------------------------------------
  433. void CCSGameStats::Event_BombExploded( CCSPlayer* pPlayer )
  434. {
  435. IncrementStat( pPlayer, CSSTAT_OBJECTIVES_COMPLETED, 1 );
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose:
  439. //-----------------------------------------------------------------------------
  440. void CCSGameStats::Event_HostageRescued( CCSPlayer* pPlayer)
  441. {
  442. IncrementStat( pPlayer, CSSTAT_NUM_HOSTAGES_RESCUED, 1 );
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose: Increment counter-terrorist team stat
  446. //-----------------------------------------------------------------------------
  447. void CCSGameStats::Event_AllHostagesRescued()
  448. {
  449. IncrementTeamStat( TEAM_CT, CSSTAT_OBJECTIVES_COMPLETED, 1 );
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. //-----------------------------------------------------------------------------
  454. void CCSGameStats::Event_WindowShattered( CBasePlayer *pPlayer)
  455. {
  456. Assert( pPlayer );
  457. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  458. IncrementStat( pCSPlayer, CSSTAT_NUM_BROKEN_WINDOWS, 1 );
  459. }
  460. void CCSGameStats::Event_BreakProp( CCSPlayer* pPlayer, CBreakableProp *pProp )
  461. {
  462. if (!pPlayer)
  463. return;
  464. DevMsg("Player %s broke a %s (%i)\n", pPlayer->GetPlayerName(), pProp->GetModelName().ToCStr(), pProp->entindex());
  465. int iIndex = m_PropStatTable.Find(pProp->GetModelName().ToCStr());
  466. if (m_PropStatTable.IsValidIndex(iIndex))
  467. {
  468. IncrementStat(pPlayer, m_PropStatTable[iIndex], 1);
  469. }
  470. IncrementStat(pPlayer, CSSTAT_PROPSBROKEN_ALL, 1);
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose:
  474. //-----------------------------------------------------------------------------
  475. void CCSGameStats::UpdatePlayerRoundStats(int winner)
  476. {
  477. int mapIndex = GetCSLevelIndex(gpGlobals->mapname.ToCStr());
  478. CSStatType_t mapStatWinIndex = CSSTAT_UNDEFINED, mapStatRoundIndex = CSSTAT_UNDEFINED;
  479. if ( mapIndex != -1 )
  480. {
  481. mapStatWinIndex = MapName_StatId_Table[mapIndex].statWinsId;
  482. mapStatRoundIndex = MapName_StatId_Table[mapIndex].statRoundsId;
  483. }
  484. // increment the team specific stats
  485. IncrementTeamStat( winner, CSSTAT_ROUNDS_WON, 1 );
  486. if ( mapStatWinIndex != CSSTAT_UNDEFINED )
  487. {
  488. IncrementTeamStat( winner, mapStatWinIndex, 1 );
  489. }
  490. if ( CSGameRules()->IsPistolRound() )
  491. {
  492. IncrementTeamStat( winner, CSSTAT_PISTOLROUNDS_WON, 1 );
  493. }
  494. IncrementTeamStat( TEAM_TERRORIST, CSSTAT_ROUNDS_PLAYED, 1 );
  495. IncrementTeamStat( TEAM_CT, CSSTAT_ROUNDS_PLAYED, 1 );
  496. IncrementTeamStat( TEAM_TERRORIST, mapStatRoundIndex, 1 );
  497. IncrementTeamStat( TEAM_CT, mapStatRoundIndex, 1 );
  498. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  499. {
  500. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  501. if ( pPlayer && pPlayer->IsConnected() )
  502. {
  503. if ( winner == TEAM_CT )
  504. {
  505. IncrementStat( pPlayer, CSSTAT_CT_ROUNDS_WON, 1, true );
  506. }
  507. else if ( winner == TEAM_TERRORIST )
  508. {
  509. IncrementStat( pPlayer, CSSTAT_T_ROUNDS_WON, 1, true );
  510. }
  511. if ( winner == TEAM_CT || winner == TEAM_TERRORIST )
  512. {
  513. // Increment the win stats if this player is on the winning team
  514. if ( pPlayer->GetTeamNumber() == winner )
  515. {
  516. IncrementStat( pPlayer, CSSTAT_ROUNDS_WON, 1, true );
  517. if ( CSGameRules()->IsPistolRound() )
  518. {
  519. IncrementStat( pPlayer, CSSTAT_PISTOLROUNDS_WON, 1, true );
  520. }
  521. if ( mapStatWinIndex != CSSTAT_UNDEFINED )
  522. {
  523. IncrementStat( pPlayer, mapStatWinIndex, 1, true );
  524. }
  525. }
  526. IncrementStat( pPlayer, CSSTAT_ROUNDS_PLAYED, 1 );
  527. if ( mapStatWinIndex != CSSTAT_UNDEFINED )
  528. {
  529. IncrementStat( pPlayer, mapStatRoundIndex, 1, true );
  530. }
  531. // set the play time for the round
  532. IncrementStat( pPlayer, CSSTAT_PLAYTIME, (int)CSGameRules()->GetRoundElapsedTime(), true );
  533. }
  534. }
  535. }
  536. // send a stats update to all players
  537. for ( int iPlayerIndex = 1; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  538. {
  539. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  540. if ( pPlayer && pPlayer->IsConnected())
  541. {
  542. SendStatsToPlayer(pPlayer, CSSTAT_PRIORITY_ENDROUND);
  543. }
  544. }
  545. }
  546. void CCSGameStats::SendRollingStatsAveragesToAllPlayers()
  547. {
  548. //The most stats we can send at once is the max message size minus the header, divided by the total size of each stat.
  549. const int maxStatsPerMessage = (MAX_USER_MSG_DATA - sizeof(CSStatType_t)) / (3 * sizeof(float));
  550. const int numMessagesNeeded = ((CSSTAT_MAX - CSSTAT_FIRST) / maxStatsPerMessage) + 1;
  551. for (int batchIndex = 0 ; batchIndex < numMessagesNeeded ; ++batchIndex)
  552. {
  553. int firstStatInThisBatch = (batchIndex * maxStatsPerMessage) + CSSTAT_FIRST;
  554. int lastMessageInThisBatch = firstStatInThisBatch + maxStatsPerMessage - 1;
  555. CRecipientFilter filter;
  556. filter.AddAllPlayers();
  557. UserMessageBegin( filter, "MatchStatsUpdate" );
  558. WRITE_SHORT(firstStatInThisBatch);
  559. for ( int iStat = firstStatInThisBatch; iStat < CSSTAT_MAX && iStat <= lastMessageInThisBatch; ++iStat )
  560. {
  561. WRITE_FLOAT(m_rollingTStatAverages.m_fStat[iStat]);
  562. WRITE_FLOAT(m_rollingCTStatAverages.m_fStat[iStat]);
  563. WRITE_FLOAT(m_rollingPlayerStatAverages.m_fStat[iStat]);
  564. }
  565. MessageEnd();
  566. }
  567. }
  568. void CCSGameStats::SendDirectStatsAveragesToAllPlayers()
  569. {
  570. //The most stats we can send at once is the max message size minus the header, divided by the total size of each stat.
  571. const int maxStatsPerMessage = (MAX_USER_MSG_DATA - sizeof(CSStatType_t)) / (3 * sizeof(float));
  572. const int numMessagesNeeded = ((CSSTAT_MAX - CSSTAT_FIRST) / maxStatsPerMessage) + 1;
  573. for (int batchIndex = 0 ; batchIndex < numMessagesNeeded ; ++batchIndex)
  574. {
  575. int firstStatInThisBatch = (batchIndex * maxStatsPerMessage) + CSSTAT_FIRST;
  576. int lastMessageInThisBatch = firstStatInThisBatch + maxStatsPerMessage - 1;
  577. CRecipientFilter filter;
  578. filter.AddAllPlayers();
  579. UserMessageBegin( filter, "MatchStatsUpdate" );
  580. WRITE_SHORT(firstStatInThisBatch);
  581. for ( int iStat = firstStatInThisBatch; iStat < CSSTAT_MAX && iStat <= lastMessageInThisBatch; ++iStat )
  582. {
  583. WRITE_FLOAT(m_directTStatAverages.m_fStat[iStat]);
  584. WRITE_FLOAT(m_directCTStatAverages.m_fStat[iStat]);
  585. WRITE_FLOAT(m_directPlayerStatAverages.m_fStat[iStat]);
  586. }
  587. MessageEnd();
  588. }
  589. }
  590. void CCSGameStats::ComputeRollingStatAverages()
  591. {
  592. int numPlayers = 0;
  593. int numCTs = 0;
  594. int numTs = 0;
  595. RoundStatsRollingAverage_t currentRoundTStatsAverage;
  596. RoundStatsRollingAverage_t currentRoundCTStatsAverage;
  597. RoundStatsRollingAverage_t currentRoundPlayerStatsAverage;
  598. currentRoundTStatsAverage.Reset();
  599. currentRoundCTStatsAverage.Reset();
  600. currentRoundPlayerStatsAverage.Reset();
  601. //for ( int iStat = CSSTAT_FIRST; iStat < CSSTAT_MAX; ++iStat )
  602. {
  603. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  604. {
  605. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  606. if ( pPlayer && pPlayer->IsConnected())
  607. {
  608. StatsCollection_t &roundStats = m_aPlayerStats[pPlayer->entindex()].statsCurrentRound;
  609. int teamNumber = pPlayer->GetTeamNumber();
  610. if (teamNumber == TEAM_CT)
  611. {
  612. numCTs++;
  613. numPlayers++;
  614. currentRoundCTStatsAverage += roundStats;
  615. currentRoundPlayerStatsAverage += roundStats;
  616. }
  617. else if (teamNumber == TEAM_TERRORIST)
  618. {
  619. numTs++;
  620. numPlayers++;
  621. currentRoundTStatsAverage += roundStats;
  622. currentRoundPlayerStatsAverage += roundStats;
  623. }
  624. }
  625. }
  626. if (numTs > 0)
  627. {
  628. currentRoundTStatsAverage /= numTs;
  629. }
  630. if (numCTs > 0)
  631. {
  632. currentRoundCTStatsAverage /= numCTs;
  633. }
  634. if (numPlayers > 0)
  635. {
  636. currentRoundPlayerStatsAverage /= numPlayers;
  637. }
  638. m_rollingTStatAverages.RollDataSetIntoAverage(currentRoundTStatsAverage);
  639. m_rollingCTStatAverages.RollDataSetIntoAverage(currentRoundCTStatsAverage);
  640. m_rollingPlayerStatAverages.RollDataSetIntoAverage(currentRoundPlayerStatsAverage);
  641. }
  642. }
  643. void CCSGameStats::ComputeDirectStatAverages()
  644. {
  645. m_numberOfRoundsForDirectAverages++;
  646. m_directCTStatAverages.Reset();
  647. m_directTStatAverages.Reset();
  648. m_directPlayerStatAverages.Reset();
  649. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  650. {
  651. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  652. if ( pPlayer && pPlayer->IsConnected())
  653. {
  654. StatsCollection_t &matchStats = m_aPlayerStats[pPlayer->entindex()].statsCurrentMatch;
  655. int teamNumber = pPlayer->GetTeamNumber();
  656. if (teamNumber == TEAM_CT)
  657. {
  658. m_numberOfCounterTerroristEntriesForDirectAverages++;
  659. m_directCTStatAverages += matchStats;
  660. m_directPlayerStatAverages += matchStats;
  661. }
  662. else if (teamNumber == TEAM_TERRORIST)
  663. {
  664. m_numberOfTerroristEntriesForDirectAverages++;
  665. m_directTStatAverages += matchStats;
  666. m_directPlayerStatAverages += matchStats;
  667. }
  668. }
  669. }
  670. if (m_numberOfTerroristEntriesForDirectAverages > 0)
  671. {
  672. m_directTStatAverages /= m_numberOfTerroristEntriesForDirectAverages;
  673. m_directTStatAverages *= m_numberOfRoundsForDirectAverages;
  674. }
  675. if (m_numberOfCounterTerroristEntriesForDirectAverages > 0)
  676. {
  677. m_directCTStatAverages /= m_numberOfCounterTerroristEntriesForDirectAverages;
  678. m_directCTStatAverages *= m_numberOfRoundsForDirectAverages;
  679. }
  680. int numPlayers = m_numberOfCounterTerroristEntriesForDirectAverages + m_numberOfTerroristEntriesForDirectAverages;
  681. if (numPlayers > 0)
  682. {
  683. m_directPlayerStatAverages /= numPlayers;
  684. m_directPlayerStatAverages *= m_numberOfRoundsForDirectAverages;
  685. }
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose: Log accumulated weapon usage and performance data
  689. //-----------------------------------------------------------------------------
  690. void CCSGameStats::DumpMatchWeaponMetrics()
  691. {
  692. //// generate a filename
  693. //time_t t = time( NULL );
  694. //struct tm *now = localtime( &t );
  695. //if ( !now )
  696. // return;
  697. //int year = now->tm_year + 1900;
  698. //int month = now->tm_mon + 1;
  699. //int day = now->tm_mday;
  700. //int hour = now->tm_hour;
  701. //int minute = now->tm_min;
  702. //int second = now->tm_sec;
  703. //char filename[ 128 ];
  704. //Q_snprintf( filename, sizeof(filename), "wm_%4d%02d%02d_%02d%02d%02d_%s.csv",
  705. // year, month, day, hour, minute, second, gpGlobals->mapname.ToCStr());
  706. //FileHandle_t hLogFile = filesystem->Open( filename, "wt" );
  707. //if ( hLogFile == FILESYSTEM_INVALID_HANDLE )
  708. // return;
  709. //filesystem->FPrintf(hLogFile, "%s\n", "WeaponId, Mode, Cost, Bullets, CycleTime, TotalShots, TotalHits, TotalDamage, TotalKills");
  710. //for (int iWeapon = 0; iWeapon < WEAPON_MAX; ++iWeapon)
  711. //{
  712. // CCSWeaponInfo* pInfo = GetWeaponInfo( (CSWeaponID)iWeapon );
  713. // if ( !pInfo )
  714. // continue;
  715. // const char* pWeaponName = pInfo->szClassName;
  716. // if ( !pWeaponName )
  717. // continue;
  718. // if ( V_strncmp(pWeaponName, "weapon_", 7) == 0 )
  719. // pWeaponName += 7;
  720. // for ( int iMode = 0; iMode < WeaponMode_MAX; ++iMode)
  721. // {
  722. // filesystem->FPrintf(hLogFile, "%s, %d, %d, %d, %f, %d, %d, %d, %d\n",
  723. // pWeaponName,
  724. // iMode,
  725. // pInfo->GetWeaponPrice(),
  726. // pInfo->m_iBullets,
  727. // pInfo->m_flCycleTime,
  728. // m_weaponStats[iWeapon][iMode].shots,
  729. // m_weaponStats[iWeapon][iMode].hits,
  730. // m_weaponStats[iWeapon][iMode].damage,
  731. // m_weaponStats[iWeapon][iMode].kills);
  732. // }
  733. //}
  734. //filesystem->FPrintf(hLogFile, "\n");
  735. //filesystem->FPrintf(hLogFile, "weapon_accuracy_model, %d\n", weapon_accuracy_model.GetInt());
  736. //filesystem->FPrintf(hLogFile, "bot_difficulty, %d\n", cv_bot_difficulty.GetInt());
  737. //g_pFullFileSystem->Close(hLogFile);
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose:
  741. //-----------------------------------------------------------------------------
  742. void CCSGameStats::Event_PlayerConnected( CBasePlayer *pPlayer )
  743. {
  744. ResetPlayerStats( ToCSPlayer( pPlayer ) );
  745. }
  746. //-----------------------------------------------------------------------------
  747. // Purpose:
  748. //-----------------------------------------------------------------------------
  749. void CCSGameStats::Event_PlayerDisconnected( CBasePlayer *pPlayer )
  750. {
  751. CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
  752. if ( !pCSPlayer )
  753. return;
  754. ResetPlayerStats( pCSPlayer );
  755. }
  756. //-----------------------------------------------------------------------------
  757. // Purpose:
  758. //-----------------------------------------------------------------------------
  759. void CCSGameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info )
  760. {
  761. // This also gets called when the victim is a building. That gets tracked separately as building destruction, don't count it here
  762. if ( !pVictim->IsPlayer() )
  763. return;
  764. CBaseEntity *pInflictor = info.GetInflictor();
  765. CCSPlayer *pPlayerAttacker = ToCSPlayer( pAttacker );
  766. CCSPlayer *pPlayerVictim = ToCSPlayer( pVictim );
  767. // keep track of how many times every player kills every other player
  768. TrackKillStats( pPlayerAttacker, pPlayerVictim );
  769. // Skip rest of stat reporting for friendly fire
  770. if ( pPlayerAttacker->GetTeam() == pVictim->GetTeam() )
  771. return;
  772. CSWeaponID weaponId = WEAPON_NONE;
  773. if ( pInflictor )
  774. {
  775. if ( pInflictor == pAttacker )
  776. {
  777. if ( pAttacker->GetActiveWeapon() )
  778. {
  779. CBaseCombatWeapon* weapon = pAttacker->GetActiveWeapon();
  780. if (weapon)
  781. {
  782. CWeaponCSBase* pCSWeapon = static_cast<CWeaponCSBase*>(weapon);
  783. weaponId = pCSWeapon->GetWeaponID();
  784. CCSWeaponInfo *info = GetWeaponInfo(weaponId);
  785. if (info && info->m_iTeam != TEAM_UNASSIGNED && pAttacker->GetTeamNumber() != info->m_iTeam )
  786. {
  787. IncrementStat(pPlayerAttacker, CSSTAT_KILLS_ENEMY_WEAPON, 1);
  788. }
  789. }
  790. }
  791. }
  792. else
  793. {
  794. //In the case of grenades, the inflictor is the spawned grenade entity.
  795. if ( V_strcmp(pInflictor->m_iClassname.ToCStr(), "hegrenade_projectile") == 0 )
  796. weaponId = WEAPON_HEGRENADE;
  797. }
  798. }
  799. // update weapon stats
  800. ++m_weaponStats[weaponId][pPlayerAttacker->IsBot()].kills;
  801. for (int i = 0; WeaponName_StatId_Table[i].killStatId != CSSTAT_UNDEFINED; ++i)
  802. {
  803. if ( WeaponName_StatId_Table[i].weaponId == weaponId )
  804. {
  805. IncrementStat( pPlayerAttacker, WeaponName_StatId_Table[i].killStatId, 1 );
  806. break;
  807. }
  808. }
  809. if (pPlayerVictim && pPlayerVictim->IsBlind())
  810. {
  811. IncrementStat( pPlayerAttacker, CSSTAT_KILLS_ENEMY_BLINDED, 1 );
  812. }
  813. if (pPlayerVictim && pPlayerAttacker && pPlayerAttacker->IsBlindForAchievement())
  814. {
  815. IncrementStat( pPlayerAttacker, CSSTAT_KILLS_WHILE_BLINDED, 1 );
  816. }
  817. // [sbodenbender] check for deaths near planted bomb for funfact
  818. if (pPlayerVictim && pPlayerAttacker && pPlayerAttacker->GetTeamNumber() == TEAM_TERRORIST && CSGameRules()->m_bBombPlanted)
  819. {
  820. float bombCheckDistSq = AchievementConsts::KillEnemyNearBomb_MaxDistance * AchievementConsts::KillEnemyNearBomb_MaxDistance;
  821. for ( int i=0; i < g_PlantedC4s.Count(); i++ )
  822. {
  823. CPlantedC4 *pC4 = g_PlantedC4s[i];
  824. if ( pC4->IsBombActive() )
  825. {
  826. Vector bombPos = pC4->GetAbsOrigin();
  827. Vector victimToBomb = pPlayerVictim->GetAbsOrigin() - bombPos;
  828. Vector attackerToBomb = pPlayerAttacker->GetAbsOrigin() - bombPos;
  829. if (victimToBomb.LengthSqr() < bombCheckDistSq || attackerToBomb.LengthSqr() < bombCheckDistSq)
  830. {
  831. IncrementStat(pPlayerAttacker, CSSTAT_KILLS_WHILE_DEFENDING_BOMB, 1);
  832. break; // you only get credit for one kill even if you happen to be by more than one bomb
  833. }
  834. }
  835. }
  836. }
  837. //Increment stat if this is a headshot.
  838. if (info.GetDamageType() & DMG_HEADSHOT)
  839. {
  840. IncrementStat( pPlayerAttacker, CSSTAT_KILLS_HEADSHOT, 1 );
  841. }
  842. IncrementStat( pPlayerAttacker, CSSTAT_KILLS, 1 );
  843. // we don't have a simple way (yet) to check if the victim actually just achieved The Unstoppable Force, so we
  844. // award this achievement simply if they've met the requirements and would have received it.
  845. PlayerStats_t &victimStats = m_aPlayerStats[pVictim->entindex()];
  846. if (victimStats.statsCurrentRound[CSSTAT_KILLS] >= AchievementConsts::UnstoppableForce_Kills)
  847. {
  848. pPlayerAttacker->AwardAchievement(CSImmovableObject);
  849. }
  850. CCSGameRules::TeamPlayerCounts playerCounts[TEAM_MAXCOUNT];
  851. CSGameRules()->GetPlayerCounts(playerCounts);
  852. int iAttackerTeamNumber = pPlayerAttacker->GetTeamNumber() ;
  853. if (playerCounts[iAttackerTeamNumber].totalAlivePlayers == 1 && playerCounts[iAttackerTeamNumber].killedPlayers >= 2)
  854. {
  855. IncrementStat(pPlayerAttacker, CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE, 1);
  856. }
  857. //if they were damaged by more than one person that must mean that someone else did damage before the killer finished them off.
  858. if (pPlayerVictim->GetNumEnemyDamagers() > 1)
  859. {
  860. IncrementStat(pPlayerAttacker, CSSTAT_KILLS_ENEMY_WOUNDED, 1);
  861. }
  862. // Let's check for the "Happy Camper" achievement where we snipe two players while standing in the same spot.
  863. if ( pPlayerAttacker && !pPlayerAttacker->IsBot() && weaponId != WEAPON_NONE )
  864. {
  865. // Were we using a sniper rifle?
  866. bool bUsingSniper = ( weaponId == WEAPON_AWP ||
  867. weaponId == WEAPON_SCOUT ||
  868. weaponId == WEAPON_SG550 ||
  869. weaponId == WEAPON_G3SG1 );
  870. // If we're zoomed in
  871. if ( bUsingSniper && pPlayerAttacker->GetFOV() != pPlayerAttacker->GetDefaultFOV() )
  872. {
  873. // Get our position
  874. Vector position = pPlayerAttacker->GetAbsOrigin();
  875. int userid = pPlayerAttacker->GetUserID();
  876. bool bFoundPlayerEntry = false;
  877. FOR_EACH_LL( m_PlayerSnipedPosition, iElement )
  878. {
  879. sHappyCamperSnipePosition *pSnipePosition = &m_PlayerSnipedPosition[iElement];
  880. // We've found this player's entry. Next, check to see if they meet the achievement criteria
  881. if ( userid == pSnipePosition->m_iUserID )
  882. {
  883. bFoundPlayerEntry = true;
  884. Vector posDif = position - pSnipePosition->m_vPos;
  885. // Give a small epsilon to account for floating point anomalies
  886. if ( posDif.IsLengthLessThan( .01f) )
  887. {
  888. pPlayerAttacker->AwardAchievement( CSSnipeTwoFromSameSpot );
  889. }
  890. // Update their position
  891. pSnipePosition->m_vPos = position;
  892. break;
  893. }
  894. }
  895. // No entry so add one
  896. if ( !bFoundPlayerEntry )
  897. {
  898. m_PlayerSnipedPosition.AddToTail( sHappyCamperSnipePosition( userid, position ) );
  899. }
  900. }
  901. }
  902. }
  903. void CCSGameStats::CalculateOverkill(CCSPlayer* pAttacker, CCSPlayer* pVictim)
  904. {
  905. //Count domination overkills - Do this before determining domination
  906. if (pAttacker->GetTeam() != pVictim->GetTeam())
  907. {
  908. if (pAttacker->IsPlayerDominated(pVictim->entindex()))
  909. {
  910. IncrementStat( pAttacker, CSSTAT_DOMINATION_OVERKILLS, 1 );
  911. }
  912. }
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Purpose: Steamworks Gamestats death tracking
  916. //-----------------------------------------------------------------------------
  917. void CCSGameStats::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  918. {
  919. if ( !pVictim )
  920. return;
  921. m_DeathData.AddToTail( new SCSSDeathData( pVictim, info ) );
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Purpose: Stats event for giving damage to player
  925. //-----------------------------------------------------------------------------
  926. void CCSGameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info )
  927. {
  928. CCSPlayer *pAttacker = ToCSPlayer( info.GetAttacker() );
  929. if ( pAttacker && pAttacker->GetTeam() != pBasePlayer->GetTeam() )
  930. {
  931. CSWeaponMode weaponMode = Primary_Mode;
  932. IncrementStat( pAttacker, CSSTAT_DAMAGE, info.GetDamage() );
  933. if (pAttacker->m_bNightVisionOn)
  934. {
  935. IncrementStat( pAttacker, CSSTAT_NIGHTVISION_DAMAGE, info.GetDamage() );
  936. }
  937. const char *pWeaponName = NULL;
  938. if ( info.GetInflictor() == info.GetAttacker() )
  939. {
  940. if ( pAttacker->GetActiveWeapon() )
  941. {
  942. CWeaponCSBase* pCSWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon());
  943. if (pCSWeapon)
  944. {
  945. pWeaponName = pCSWeapon->GetClassname();
  946. weaponMode = pCSWeapon->m_weaponMode;
  947. }
  948. }
  949. }
  950. // Need to determine the weapon name
  951. else
  952. {
  953. pWeaponName = info.GetInflictor()->GetClassname();
  954. }
  955. // Unknown weapon?!?
  956. if ( !pWeaponName )
  957. return;
  958. // Now update the damage this weapon has done
  959. CSWeaponID weaponId = AliasToWeaponID( GetTranslatedWeaponAlias( pWeaponName ) );
  960. for (int i = 0; WeaponName_StatId_Table[i].shotStatId != CSSTAT_UNDEFINED; ++i)
  961. {
  962. if ( weaponId == WeaponName_StatId_Table[i].weaponId )
  963. {
  964. int weaponId = WeaponName_StatId_Table[i].weaponId;
  965. int iType = pAttacker->IsBot();
  966. ++m_weaponStats[weaponId][iType].hits;
  967. m_weaponStats[weaponId][iType].damage += info.GetDamage();
  968. break;
  969. }
  970. }
  971. }
  972. }
  973. //-----------------------------------------------------------------------------
  974. // Purpose: Stats event for giving money to player
  975. //-----------------------------------------------------------------------------
  976. void CCSGameStats::Event_MoneyEarned( CCSPlayer* pPlayer, int moneyEarned)
  977. {
  978. if ( pPlayer && moneyEarned > 0)
  979. {
  980. IncrementStat(pPlayer, CSSTAT_MONEY_EARNED, moneyEarned);
  981. }
  982. }
  983. void CCSGameStats::Event_MoneySpent( CCSPlayer* pPlayer, int moneySpent, const char *pItemName )
  984. {
  985. if ( pPlayer && moneySpent > 0)
  986. {
  987. IncrementStat(pPlayer, CSSTAT_MONEY_SPENT, moneySpent);
  988. if ( pItemName && !pPlayer->IsBot() )
  989. {
  990. CSteamID steamIDForBuyer;
  991. pPlayer->GetSteamID( &steamIDForBuyer );
  992. m_MarketPurchases.AddToTail( new SMarketPurchases( steamIDForBuyer.ConvertToUint64(), moneySpent, pItemName ) );
  993. }
  994. }
  995. }
  996. void CCSGameStats::Event_PlayerDonatedWeapon (CCSPlayer* pPlayer)
  997. {
  998. if (pPlayer)
  999. {
  1000. IncrementStat(pPlayer, CSSTAT_WEAPONS_DONATED, 1);
  1001. }
  1002. }
  1003. void CCSGameStats::Event_MVPEarned( CCSPlayer* pPlayer )
  1004. {
  1005. if (pPlayer)
  1006. {
  1007. IncrementStat(pPlayer, CSSTAT_MVPS, 1);
  1008. }
  1009. }
  1010. void CCSGameStats::Event_KnifeUse( CCSPlayer* pPlayer, bool bStab, int iDamage )
  1011. {
  1012. if (pPlayer)
  1013. {
  1014. int weaponId = WEAPON_KNIFE;
  1015. int iType = pPlayer->IsBot();
  1016. IncrementStat( pPlayer, CSSTAT_SHOTS_KNIFE, 1 );
  1017. ++m_weaponStats[weaponId][iType].shots;
  1018. if ( iDamage )
  1019. {
  1020. IncrementStat( pPlayer, CSSTAT_HITS_KNIFE, 1 );
  1021. ++m_weaponStats[weaponId][iType].hits;
  1022. IncrementStat( pPlayer, CSSTAT_DAMAGE_KNIFE, iDamage );
  1023. m_weaponStats[weaponId][iType].damage += iDamage;
  1024. }
  1025. }
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose: Event handler
  1029. //-----------------------------------------------------------------------------
  1030. void CCSGameStats::FireGameEvent( IGameEvent *event )
  1031. {
  1032. const char *pEventName = event->GetName();
  1033. if ( V_strcmp(pEventName, "round_start") == 0 )
  1034. {
  1035. m_PlayerSnipedPosition.Purge();
  1036. m_bInRound = true;
  1037. }
  1038. else if ( V_strcmp(pEventName, "round_end") == 0 )
  1039. {
  1040. const int reason = event->GetInt( "reason" );
  1041. if( reason == Game_Commencing )
  1042. {
  1043. ResetPlayerClassMatchStats();
  1044. }
  1045. else
  1046. {
  1047. UpdatePlayerRoundStats(event->GetInt("winner"));
  1048. ComputeDirectStatAverages();
  1049. SendDirectStatsAveragesToAllPlayers();
  1050. UploadRoundStats();
  1051. ResetWeaponStats();
  1052. }
  1053. return;
  1054. }
  1055. else if ( V_strcmp(pEventName, "break_prop") == 0 )
  1056. {
  1057. int userid = event->GetInt("userid", 0);
  1058. int entindex = event->GetInt("entindex", 0);
  1059. CBreakableProp* pProp = static_cast<CBreakableProp*>(CBaseEntity::Instance(entindex));
  1060. Event_BreakProp(ToCSPlayer(UTIL_PlayerByUserId(userid)), pProp);
  1061. }
  1062. else if ( V_strcmp(pEventName, "player_decal") == 0 )
  1063. {
  1064. int userid = event->GetInt("userid", 0);
  1065. Event_PlayerSprayedDecal(ToCSPlayer(UTIL_PlayerByUserId(userid)));
  1066. }
  1067. else if ( V_strcmp(pEventName, "hegrenade_detonate") == 0 )
  1068. {
  1069. int userid = event->GetInt("userid", 0);
  1070. IncrementStat( ToCSPlayer(UTIL_PlayerByUserId(userid)), CSSTAT_SHOTS_HEGRENADE, 1 );
  1071. }
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Purpose: Return stats for the given player
  1075. //-----------------------------------------------------------------------------
  1076. const PlayerStats_t& CCSGameStats::FindPlayerStats( CBasePlayer *pPlayer ) const
  1077. {
  1078. return m_aPlayerStats[pPlayer->entindex()];
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Purpose: Return stats for the given team
  1082. //-----------------------------------------------------------------------------
  1083. const StatsCollection_t& CCSGameStats::GetTeamStats( int iTeamIndex ) const
  1084. {
  1085. int arrayIndex = iTeamIndex - FIRST_GAME_TEAM;
  1086. Assert( arrayIndex >= 0 && arrayIndex < TEAM_MAXCOUNT - FIRST_GAME_TEAM );
  1087. return m_aTeamStats[arrayIndex];
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose: Resets the stats for each team
  1091. //-----------------------------------------------------------------------------
  1092. void CCSGameStats::ResetAllTeamStats()
  1093. {
  1094. for ( int i = 0; i < ARRAYSIZE(m_aTeamStats); ++i )
  1095. {
  1096. m_aTeamStats[i].Reset();
  1097. }
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Purpose: Resets all stats (including round, match, accumulated and rolling averages
  1101. //-----------------------------------------------------------------------------
  1102. void CCSGameStats::ResetAllStats()
  1103. {
  1104. for ( int i = 0; i < ARRAYSIZE( m_aPlayerStats ); i++ )
  1105. {
  1106. m_aPlayerStats[i].statsDelta.Reset();
  1107. m_aPlayerStats[i].statsCurrentRound.Reset();
  1108. m_aPlayerStats[i].statsCurrentMatch.Reset();
  1109. m_rollingCTStatAverages.Reset();
  1110. m_rollingTStatAverages.Reset();
  1111. m_rollingPlayerStatAverages.Reset();
  1112. m_numberOfRoundsForDirectAverages = 0;
  1113. m_numberOfTerroristEntriesForDirectAverages = 0;
  1114. m_numberOfCounterTerroristEntriesForDirectAverages = 0;
  1115. }
  1116. }
  1117. void CCSGameStats::ResetWeaponStats()
  1118. {
  1119. V_memset(m_weaponStats, 0, sizeof(m_weaponStats));
  1120. }
  1121. void CCSGameStats::IncrementTeamStat( int iTeamIndex, int iStatIndex, int iAmount )
  1122. {
  1123. int arrayIndex = iTeamIndex - TEAM_TERRORIST;
  1124. Assert( iStatIndex >= 0 && iStatIndex < CSSTAT_MAX );
  1125. if( arrayIndex >= 0 && arrayIndex < TEAM_MAXCOUNT - TEAM_TERRORIST )
  1126. {
  1127. m_aTeamStats[arrayIndex][iStatIndex] += iAmount;
  1128. }
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose: Resets all stats for this player
  1132. //-----------------------------------------------------------------------------
  1133. void CCSGameStats::ResetPlayerStats( CBasePlayer* pPlayer )
  1134. {
  1135. PlayerStats_t &stats = m_aPlayerStats[pPlayer->entindex()];
  1136. // reset the stats on this player
  1137. stats.Reset();
  1138. // reset the matrix of who killed whom with respect to this player
  1139. ResetKillHistory( pPlayer );
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose: Resets the kill history for this player
  1143. //-----------------------------------------------------------------------------
  1144. void CCSGameStats::ResetKillHistory( CBasePlayer* pPlayer )
  1145. {
  1146. int iPlayerIndex = pPlayer->entindex();
  1147. PlayerStats_t& statsPlayer = m_aPlayerStats[iPlayerIndex];
  1148. // for every other player, set all all the kills with respect to this player to 0
  1149. for ( int i = 0; i < ARRAYSIZE( m_aPlayerStats ); i++ )
  1150. {
  1151. //reset their record of us.
  1152. PlayerStats_t &statsOther = m_aPlayerStats[i];
  1153. statsOther.statsKills.iNumKilled[iPlayerIndex] = 0;
  1154. statsOther.statsKills.iNumKilledBy[iPlayerIndex] = 0;
  1155. statsOther.statsKills.iNumKilledByUnanswered[iPlayerIndex] = 0;
  1156. //reset our record of them
  1157. statsPlayer.statsKills.iNumKilled[i] = 0;
  1158. statsPlayer.statsKills.iNumKilledBy[i] = 0;
  1159. statsPlayer.statsKills.iNumKilledByUnanswered[i] = 0;
  1160. }
  1161. }
  1162. //-----------------------------------------------------------------------------
  1163. // Purpose: Resets per-round stats for all players
  1164. //-----------------------------------------------------------------------------
  1165. void CCSGameStats::ResetRoundStats()
  1166. {
  1167. for ( int i = 0; i < ARRAYSIZE( m_aPlayerStats ); i++ )
  1168. {
  1169. m_aPlayerStats[i].statsCurrentRound.Reset();
  1170. }
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. // Purpose: Increments specified stat for specified player by specified amount
  1174. //-----------------------------------------------------------------------------
  1175. void CCSGameStats::IncrementStat( CCSPlayer* pPlayer, CSStatType_t statId, int iDelta, bool bPlayerOnly /* = false */ )
  1176. {
  1177. if ( pPlayer )
  1178. {
  1179. PlayerStats_t &stats = m_aPlayerStats[pPlayer->entindex()];
  1180. stats.statsDelta[statId] += iDelta;
  1181. stats.statsCurrentRound[statId] += iDelta;
  1182. stats.statsCurrentMatch[statId] += iDelta;
  1183. // increment team stat
  1184. int teamIndex = pPlayer->GetTeamNumber() - FIRST_GAME_TEAM;
  1185. if ( !bPlayerOnly && teamIndex >= 0 && teamIndex < ARRAYSIZE(m_aTeamStats) )
  1186. {
  1187. m_aTeamStats[teamIndex][statId] += iDelta;
  1188. }
  1189. for (int i = 0; i < ARRAYSIZE(ServerStatBasedAchievements); ++i)
  1190. {
  1191. if (ServerStatBasedAchievements[i].statId == statId)
  1192. {
  1193. // skip this if there is a map filter and it doesn't match
  1194. if (ServerStatBasedAchievements[i].mapFilter != NULL && V_strcmp(gpGlobals->mapname.ToCStr(), ServerStatBasedAchievements[i].mapFilter) != 0)
  1195. continue;
  1196. bool bWasMet = ServerStatBasedAchievements[i].IsMet(stats.statsCurrentRound[statId] - iDelta, stats.statsCurrentMatch[statId] - iDelta);
  1197. bool bIsMet = ServerStatBasedAchievements[i].IsMet(stats.statsCurrentRound[statId], stats.statsCurrentMatch[statId]);
  1198. if (!bWasMet && bIsMet)
  1199. {
  1200. pPlayer->AwardAchievement(ServerStatBasedAchievements[i].achievementId);
  1201. }
  1202. }
  1203. }
  1204. }
  1205. }
  1206. //-----------------------------------------------------------------------------
  1207. // Purpose: Sets the specified stat for specified player to the specified amount
  1208. //-----------------------------------------------------------------------------
  1209. void CCSGameStats::SetStat( CCSPlayer *pPlayer, CSStatType_t statId, int iValue )
  1210. {
  1211. if (pPlayer)
  1212. {
  1213. int oldRoundValue, oldMatchValue;
  1214. PlayerStats_t &stats = m_aPlayerStats[pPlayer->entindex()];
  1215. oldRoundValue = stats.statsCurrentRound[statId];
  1216. oldMatchValue = stats.statsCurrentMatch[statId];
  1217. stats.statsDelta[statId] = iValue;
  1218. stats.statsCurrentRound[statId] = iValue;
  1219. stats.statsCurrentMatch[statId] = iValue;
  1220. for (int i = 0; i < ARRAYSIZE(ServerStatBasedAchievements); ++i)
  1221. {
  1222. if (ServerStatBasedAchievements[i].statId == statId)
  1223. {
  1224. // skip this if there is a map filter and it doesn't match
  1225. if (ServerStatBasedAchievements[i].mapFilter != NULL && V_strcmp(gpGlobals->mapname.ToCStr(), ServerStatBasedAchievements[i].mapFilter) != 0)
  1226. continue;
  1227. bool bWasMet = ServerStatBasedAchievements[i].IsMet(oldRoundValue, oldMatchValue);
  1228. bool bIsMet = ServerStatBasedAchievements[i].IsMet(stats.statsCurrentRound[statId], stats.statsCurrentMatch[statId]);
  1229. if (!bWasMet && bIsMet)
  1230. {
  1231. pPlayer->AwardAchievement(ServerStatBasedAchievements[i].achievementId);
  1232. }
  1233. }
  1234. }
  1235. }
  1236. }
  1237. void CCSGameStats::SendStatsToPlayer( CCSPlayer * pPlayer, int iMinStatPriority )
  1238. {
  1239. ASSERT(CSSTAT_MAX < 255); // if we add more than 255 stats, we'll need to update this protocol
  1240. if ( pPlayer && pPlayer->IsConnected())
  1241. {
  1242. StatsCollection_t &deltaStats = m_aPlayerStats[pPlayer->entindex()].statsDelta;
  1243. // check to see if we have any stats to actually send
  1244. byte iStatsToSend = 0;
  1245. for ( int iStat = CSSTAT_FIRST; iStat < CSSTAT_MAX; ++iStat )
  1246. {
  1247. ASSERT(CSStatProperty_Table[iStat].statId == iStat);
  1248. if ( CSStatProperty_Table[iStat].statId != iStat )
  1249. {
  1250. Warning( "CSStatProperty_Table[iStat].statId != iStat, (%d)", CSStatProperty_Table[iStat].statId );
  1251. }
  1252. int iPriority = CSStatProperty_Table[iStat].flags & CSSTAT_PRIORITY_MASK;
  1253. if (deltaStats[iStat] != 0 && iPriority >= iMinStatPriority)
  1254. {
  1255. ++iStatsToSend;
  1256. }
  1257. }
  1258. // nothing changed - bail out
  1259. if ( !iStatsToSend )
  1260. return;
  1261. CSingleUserRecipientFilter filter( pPlayer );
  1262. filter.MakeReliable();
  1263. UserMessageBegin( filter, "PlayerStatsUpdate" );
  1264. CRC32_t crc;
  1265. CRC32_Init( &crc );
  1266. // begin the CRC with a trivially hidden key value to discourage packet modification
  1267. const uint32 key = 0x82DA9F4C; // this key should match the key in cs_client_gamestats.cpp
  1268. CRC32_ProcessBuffer( &crc, &key, sizeof(key));
  1269. // if we make any change to the ordering of the stats or this message format, update this value
  1270. const byte version = 0x01;
  1271. CRC32_ProcessBuffer( &crc, &version, sizeof(version));
  1272. WRITE_BYTE(version);
  1273. CRC32_ProcessBuffer( &crc, &iStatsToSend, sizeof(iStatsToSend));
  1274. WRITE_BYTE(iStatsToSend);
  1275. for ( byte iStat = CSSTAT_FIRST; iStat < CSSTAT_MAX; ++iStat )
  1276. {
  1277. int iPriority = CSStatProperty_Table[iStat].flags & CSSTAT_PRIORITY_MASK;
  1278. if (deltaStats[iStat] != 0 && iPriority >= iMinStatPriority)
  1279. {
  1280. CRC32_ProcessBuffer( &crc, &iStat, sizeof(iStat));
  1281. WRITE_BYTE(iStat);
  1282. Assert(deltaStats[iStat] <= 0x7FFF && deltaStats[iStat] > 0); // make sure we aren't truncating bits
  1283. short delta = deltaStats[iStat];
  1284. CRC32_ProcessBuffer( &crc, &delta, sizeof(delta));
  1285. WRITE_SHORT( deltaStats[iStat]);
  1286. deltaStats[iStat] = 0;
  1287. --iStatsToSend;
  1288. }
  1289. }
  1290. Assert(iStatsToSend == 0);
  1291. CRC32_Final( &crc );
  1292. WRITE_LONG(crc);
  1293. MessageEnd();
  1294. }
  1295. }
  1296. //-----------------------------------------------------------------------------
  1297. // Purpose: Sends intermittent stats updates for stats that need to be updated during a round and/or life
  1298. //-----------------------------------------------------------------------------
  1299. void CCSGameStats::PreClientUpdate()
  1300. {
  1301. int iMinStatPriority = -1;
  1302. m_fDisseminationTimerHigh += gpGlobals->frametime;
  1303. m_fDisseminationTimerLow += gpGlobals->frametime;
  1304. if ( m_fDisseminationTimerHigh > cDisseminationTimeHigh)
  1305. {
  1306. iMinStatPriority = CSSTAT_PRIORITY_HIGH;
  1307. m_fDisseminationTimerHigh = 0.0f;
  1308. if ( m_fDisseminationTimerLow > cDisseminationTimeLow)
  1309. {
  1310. iMinStatPriority = CSSTAT_PRIORITY_LOW;
  1311. m_fDisseminationTimerLow = 0.0f;
  1312. }
  1313. }
  1314. else
  1315. return;
  1316. //The proper time has elapsed, now send the update to every player
  1317. for ( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  1318. {
  1319. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(iPlayerIndex) );
  1320. SendStatsToPlayer(pPlayer, iMinStatPriority);
  1321. }
  1322. }
  1323. //-----------------------------------------------------------------------------
  1324. // Purpose: Updates the stats of who has killed whom
  1325. //-----------------------------------------------------------------------------
  1326. void CCSGameStats::TrackKillStats( CCSPlayer *pAttacker, CCSPlayer *pVictim )
  1327. {
  1328. int iPlayerIndexAttacker = pAttacker->entindex();
  1329. int iPlayerIndexVictim = pVictim->entindex();
  1330. PlayerStats_t &statsAttacker = m_aPlayerStats[iPlayerIndexAttacker];
  1331. PlayerStats_t &statsVictim = m_aPlayerStats[iPlayerIndexVictim];
  1332. statsVictim.statsKills.iNumKilledBy[iPlayerIndexAttacker]++;
  1333. statsVictim.statsKills.iNumKilledByUnanswered[iPlayerIndexAttacker]++;
  1334. statsAttacker.statsKills.iNumKilled[iPlayerIndexVictim]++;
  1335. statsAttacker.statsKills.iNumKilledByUnanswered[iPlayerIndexVictim] = 0;
  1336. }
  1337. //-----------------------------------------------------------------------------
  1338. // Purpose: Determines if attacker and victim have gotten domination or revenge
  1339. //-----------------------------------------------------------------------------
  1340. void CCSGameStats::CalcDominationAndRevenge( CCSPlayer *pAttacker, CCSPlayer *pVictim, int *piDeathFlags )
  1341. {
  1342. //=============================================================================
  1343. // HPE_BEGIN:
  1344. // [Forrest] Allow nemesis/revenge to be turned off for a server
  1345. //=============================================================================
  1346. if ( sv_nonemesis.GetBool() )
  1347. {
  1348. return;
  1349. }
  1350. //=============================================================================
  1351. // HPE_END
  1352. //=============================================================================
  1353. //If there is no attacker, there is no domination or revenge
  1354. if( !pAttacker || !pVictim )
  1355. {
  1356. return;
  1357. }
  1358. if (pAttacker->GetTeam() == pVictim->GetTeam())
  1359. {
  1360. return;
  1361. }
  1362. int iPlayerIndexVictim = pVictim->entindex();
  1363. PlayerStats_t &statsVictim = m_aPlayerStats[iPlayerIndexVictim];
  1364. // calculate # of unanswered kills between killer & victim
  1365. // This is plus 1 as this function gets called before the stat is updated. That is done so that the domination
  1366. // and revenge will be calculated prior to the death message being sent to the clients
  1367. int attackerEntityIndex = pAttacker->entindex();
  1368. int iKillsUnanswered = statsVictim.statsKills.iNumKilledByUnanswered[attackerEntityIndex] + 1;
  1369. if ( CS_KILLS_FOR_DOMINATION == iKillsUnanswered )
  1370. {
  1371. // this is the Nth unanswered kill between killer and victim, killer is now dominating victim
  1372. *piDeathFlags |= ( CS_DEATH_DOMINATION );
  1373. }
  1374. else if ( pVictim->IsPlayerDominated( pAttacker->entindex() ) )
  1375. {
  1376. // the killer killed someone who was dominating him, gains revenge
  1377. *piDeathFlags |= ( CS_DEATH_REVENGE );
  1378. }
  1379. //Check the overkill on 1 player achievement
  1380. if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION + AchievementConsts::ExtendedDomination_AdditionalKills)
  1381. {
  1382. pAttacker->AwardAchievement(CSExtendedDomination);
  1383. }
  1384. if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION)
  1385. {
  1386. //this is the Nth unanswered kill between killer and victim, killer is now dominating victim
  1387. //set victim to be dominated by killer
  1388. pAttacker->SetPlayerDominated( pVictim, true );
  1389. //Check concurrent dominations achievement
  1390. int numConcurrentDominations = 0;
  1391. for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
  1392. {
  1393. CCSPlayer *pPlayer= ToCSPlayer( UTIL_PlayerByIndex( i ) );
  1394. if (pPlayer && pAttacker->IsPlayerDominated(pPlayer->entindex()))
  1395. {
  1396. numConcurrentDominations++;
  1397. }
  1398. }
  1399. if (numConcurrentDominations >= AchievementConsts::ConcurrentDominations_MinDominations)
  1400. {
  1401. pAttacker->AwardAchievement(CSConcurrentDominations);
  1402. }
  1403. // record stats
  1404. Event_PlayerDominatedOther( pAttacker, pVictim );
  1405. }
  1406. else if ( pVictim->IsPlayerDominated( pAttacker->entindex() ) )
  1407. {
  1408. // the killer killed someone who was dominating him, gains revenge
  1409. // set victim to no longer be dominating the killer
  1410. pVictim->SetPlayerDominated( pAttacker, false );
  1411. // record stats
  1412. Event_PlayerRevenge( pAttacker );
  1413. }
  1414. }
  1415. void CCSGameStats::Event_PlayerDominatedOther( CCSPlayer *pAttacker, CCSPlayer* pVictim )
  1416. {
  1417. IncrementStat( pAttacker, CSSTAT_DOMINATIONS, 1 );
  1418. }
  1419. void CCSGameStats::Event_PlayerRevenge( CCSPlayer *pAttacker )
  1420. {
  1421. IncrementStat( pAttacker, CSSTAT_REVENGES, 1 );
  1422. }
  1423. void CCSGameStats::Event_PlayerAvengedTeammate( CCSPlayer* pAttacker, CCSPlayer* pAvengedPlayer )
  1424. {
  1425. if (pAttacker && pAvengedPlayer)
  1426. {
  1427. IGameEvent *event = gameeventmanager->CreateEvent( "player_avenged_teammate" );
  1428. if ( event )
  1429. {
  1430. event->SetInt( "avenger_id", pAttacker->GetUserID() );
  1431. event->SetInt( "avenged_player_id", pAvengedPlayer->GetUserID() );
  1432. gameeventmanager->FireEvent( event );
  1433. }
  1434. }
  1435. }
  1436. void CCSGameStats::Event_LevelInit()
  1437. {
  1438. ResetAllTeamStats();
  1439. ResetWeaponStats();
  1440. CBaseGameStats::Event_LevelInit();
  1441. GetSteamWorksSGameStatsUploader().StartSession();
  1442. }
  1443. void CCSGameStats::Event_LevelShutdown( float fElapsed )
  1444. {
  1445. DumpMatchWeaponMetrics();
  1446. CBaseGameStats::Event_LevelShutdown(fElapsed);
  1447. }
  1448. // Reset any per match info that resides in the player class
  1449. void CCSGameStats::ResetPlayerClassMatchStats()
  1450. {
  1451. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1452. {
  1453. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
  1454. if ( pPlayer )
  1455. {
  1456. pPlayer->SetNumMVPs( 0 );
  1457. }
  1458. }
  1459. }