//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: The CS game stats header // // $NoKeywords: $ //=============================================================================// #ifndef CS_GAMESTATS_H #define CS_GAMESTATS_H #ifdef _WIN32 #pragma once #endif #include "cs_blackmarket.h" #include "gamestats.h" #include "cs_gamestats_shared.h" #include "GameEventListener.h" #include "steamworks_gamestats.h" #include "weapon_csbase.h" // forward declares class CBreakableProp; #define CS_STATS_BLOB_VERSION 3 const float cDisseminationTimeHigh = 0.25f; // Time interval for high priority stats sent to the player const float cDisseminationTimeLow = 2.5f; // Time interval for medium priority stats sent to the player int GetCSLevelIndex( const char *pLevelName ); typedef struct { char szGameName[8]; byte iVersion; char szMapName[32]; char ipAddr[4]; short port; int serverid; } gamestats_header_t; typedef struct { gamestats_header_t header; short iMinutesPlayed; short iTerroristVictories[CS_NUM_LEVELS]; short iCounterTVictories[CS_NUM_LEVELS]; short iBlackMarketPurchases[WEAPON_MAX]; short iAutoBuyPurchases; short iReBuyPurchases; short iAutoBuyM4A1Purchases; short iAutoBuyAK47Purchases; short iAutoBuyFamasPurchases; short iAutoBuyGalilPurchases; short iAutoBuyVestHelmPurchases; short iAutoBuyVestPurchases; } cs_gamestats_t; extern short g_iWeaponPurchases[WEAPON_MAX]; extern float g_flGameStatsUpdateTime; extern short g_iTerroristVictories[CS_NUM_LEVELS]; extern short g_iCounterTVictories[CS_NUM_LEVELS]; extern short g_iWeaponPurchases[WEAPON_MAX]; extern short g_iAutoBuyPurchases; extern short g_iReBuyPurchases; extern short g_iAutoBuyM4A1Purchases; extern short g_iAutoBuyAK47Purchases; extern short g_iAutoBuyFamasPurchases; extern short g_iAutoBuyGalilPurchases; extern short g_iAutoBuyVestHelmPurchases; extern short g_iAutoBuyVestPurchases; struct sHappyCamperSnipePosition { sHappyCamperSnipePosition( int userid, Vector pos ) : m_iUserID(userid), m_vPos(pos) {} int m_iUserID; Vector m_vPos; }; struct SMarketPurchases : public BaseStatData { SMarketPurchases( uint64 ulPlayerID, int iPrice, const char *pName ) : m_nPlayerID(ulPlayerID), ItemCost(iPrice) { if ( pName ) { Q_strncpy( ItemID, pName, ARRAYSIZE(ItemID) ); } else { Q_strncpy( ItemID, "unknown", ARRAYSIZE(ItemID) ); } } uint64 m_nPlayerID; int ItemCost; char ItemID[64]; BEGIN_STAT_TABLE( "CSSMarketPurchase" ) REGISTER_STAT( m_nPlayerID ) REGISTER_STAT( ItemCost ) REGISTER_STAT_STRING( ItemID ) END_STAT_TABLE() }; typedef CUtlVector< SMarketPurchases* > VectorMarketPurchaseData; struct WeaponStats { int shots; int hits; int kills; int damage; }; struct SCSSWeaponData : public BaseStatData { SCSSWeaponData( const char *pWeaponName, const WeaponStats &wpnData ) { if ( pWeaponName ) { Q_strncpy( WeaponID, pWeaponName, ARRAYSIZE(WeaponID) ); } else { Q_strncpy( WeaponID, "unknown", ARRAYSIZE(WeaponID) ); } Shots = wpnData.shots; Hits = wpnData.hits; Kills = wpnData.kills; Damage = wpnData.damage; } char WeaponID[64]; int Shots; int Hits; int Kills; int Damage; BEGIN_STAT_TABLE( "CSSWeaponData" ) REGISTER_STAT_STRING( WeaponID ) REGISTER_STAT( Shots ) REGISTER_STAT( Hits ) REGISTER_STAT( Kills ) REGISTER_STAT( Damage ) END_STAT_TABLE() }; typedef CUtlVector< SCSSWeaponData* > CSSWeaponData; struct SCSSDeathData : public BaseStatData { SCSSDeathData( CBasePlayer *pVictim, const CTakeDamageInfo &info ) { m_bUseGlobalData = false; m_DeathPos = info.GetDamagePosition(); m_iVictimTeam = pVictim->GetTeamNumber(); CCSPlayer *pCSPlayer = ToCSPlayer( info.GetAttacker() ); if ( pCSPlayer ) { m_iKillerTeam = pCSPlayer->GetTeamNumber(); } else { m_iKillerTeam = -1; } const char *pszWeaponName = info.GetInflictor() ? info.GetInflictor()->GetClassname() : "unknown"; if ( pszWeaponName ) { if ( V_strcmp(pszWeaponName, "player") == 0 ) { // get the player's weapon if ( pCSPlayer && pCSPlayer->GetActiveCSWeapon() ) { pszWeaponName = pCSPlayer->GetActiveCSWeapon()->GetClassname(); } } } m_uiDeathParam = WEAPON_NONE; if ( (m_uiDeathParam = AliasToWeaponID( pszWeaponName )) == WEAPON_NONE ) { m_uiDeathParam = AliasToWeaponID( pszWeaponName ); } const char *pszMapName = gpGlobals->mapname.ToCStr() ? gpGlobals->mapname.ToCStr() : "unknown"; Q_strncpy( m_szMapName, pszMapName, ARRAYSIZE(m_szMapName) ); } Vector m_DeathPos; int m_iVictimTeam; int m_iKillerTeam; int m_iDamageType; uint64 m_uiDeathParam; char m_szMapName[64]; BEGIN_STAT_TABLE( "Deaths" ) REGISTER_STAT_NAMED( m_DeathPos.x, "XCoord" ) REGISTER_STAT_NAMED( m_DeathPos.y, "YCoord" ) REGISTER_STAT_NAMED( m_DeathPos.z, "ZCoord" ) REGISTER_STAT_NAMED( m_iVictimTeam, "Team" ) REGISTER_STAT_NAMED( m_iKillerTeam, "DeathCause" ) REGISTER_STAT_NAMED( m_uiDeathParam, "DeathParam" ) REGISTER_STAT_NAMED( m_szMapName, "DeathMap" ) END_STAT_TABLE() }; typedef CUtlVector< SCSSDeathData* > CSSDeathData; //============================================================================= // // CS Game Stats Class // class CCSGameStats : public CBaseGameStats, public CGameEventListener, public CAutoGameSystemPerFrame, public IGameStatTracker { public: // Constructor/Destructor. CCSGameStats( void ); ~CCSGameStats( void ); virtual void Clear( void ); virtual bool Init(); virtual void PreClientUpdate(); virtual void PostInit(); virtual void LevelShutdownPreClearSteamAPIContext(); void UploadRoundStats( void ); // Overridden events virtual void Event_LevelInit( void ); virtual void Event_LevelShutdown( float flElapsed ); virtual void Event_ShotFired( CBasePlayer *pPlayer, CBaseCombatWeapon* pWeapon ); virtual void Event_ShotHit( CBasePlayer *pPlayer, const CTakeDamageInfo &info ); virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ); virtual void Event_PlayerKilled_PreWeaponDrop( CBasePlayer *pPlayer, const CTakeDamageInfo &info ); void UpdatePlayerRoundStats(int winner); virtual void Event_PlayerConnected( CBasePlayer *pPlayer ); virtual void Event_PlayerDisconnected( CBasePlayer *pPlayer ); virtual void Event_WindowShattered( CBasePlayer *pPlayer ); virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info ); // CSS specific events void Event_BombPlanted( CCSPlayer *pPlayer ); void Event_BombDefused( CCSPlayer *pPlayer ); void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info ); void Event_BombExploded( CCSPlayer *pPlayer ); void Event_MoneyEarned( CCSPlayer *pPlayer, int moneyEarned ); void Event_MoneySpent( CCSPlayer *pPlayer, int moneySpent, const char *pItemName ); void Event_HostageRescued( CCSPlayer *pPlayer ); void Event_PlayerSprayedDecal( CCSPlayer*pPlayer ); void Event_AllHostagesRescued(); void Event_BreakProp( CCSPlayer *pPlayer, CBreakableProp *pProp ); void Event_PlayerDonatedWeapon (CCSPlayer* pPlayer); void Event_PlayerDominatedOther( CCSPlayer* pAttacker, CCSPlayer* pVictim); void Event_PlayerRevenge( CCSPlayer* pAttacker ); void Event_PlayerAvengedTeammate( CCSPlayer* pAttacker, CCSPlayer* pAvengedPlayer ); void Event_MVPEarned( CCSPlayer* pPlayer ); void Event_KnifeUse( CCSPlayer* pPlayer, bool bStab, int iDamage ); virtual void FireGameEvent( IGameEvent *event ); void DumpMatchWeaponMetrics(); const PlayerStats_t& FindPlayerStats( CBasePlayer *pPlayer ) const; void ResetPlayerStats( CBasePlayer *pPlayer ); void ResetKillHistory( CBasePlayer *pPlayer ); void ResetRoundStats(); void ResetPlayerClassMatchStats(); const StatsCollection_t& GetTeamStats( int iTeamIndex ) const; void ResetAllTeamStats(); void ResetAllStats(); void ResetWeaponStats(); void IncrementTeamStat( int iTeamIndex, int iStatIndex, int iAmount ); void CalcDominationAndRevenge( CCSPlayer *pAttacker, CCSPlayer *pVictim, int *piDeathFlags ); void CalculateOverkill( CCSPlayer* pAttacker, CCSPlayer* pVictim ); void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ); void IncrementStat( CCSPlayer* pPlayer, CSStatType_t statId, int iValue, bool bPlayerOnly = false ); // Steamworks Gamestats virtual void SubmitGameStats( KeyValues *pKV ); virtual StatContainerList_t* GetStatContainerList( void ) { return s_StatLists; } protected: void SetStat( CCSPlayer *pPlayer, CSStatType_t statId, int iValue ); void TrackKillStats( CCSPlayer *pAttacker, CCSPlayer *pVictim ); void ComputeRollingStatAverages(); void ComputeDirectStatAverages(); void SendRollingStatsAveragesToAllPlayers(); void SendDirectStatsAveragesToAllPlayers(); void SendStatsToPlayer( CCSPlayer * pPlayer, int iMinStatPriority ); private: PlayerStats_t m_aPlayerStats[MAX_PLAYERS+1]; // List of stats for each player for current life - reset after each death StatsCollection_t m_aTeamStats[TEAM_MAXCOUNT - FIRST_GAME_TEAM]; RoundStatsRollingAverage_t m_rollingCTStatAverages; RoundStatsRollingAverage_t m_rollingTStatAverages; RoundStatsRollingAverage_t m_rollingPlayerStatAverages; RoundStatsDirectAverage_t m_directCTStatAverages; RoundStatsDirectAverage_t m_directTStatAverages; RoundStatsDirectAverage_t m_directPlayerStatAverages; float m_fDisseminationTimerLow; // how long since last medium priority stat update float m_fDisseminationTimerHigh; // how long since last high priority stat update int m_numberOfRoundsForDirectAverages; int m_numberOfTerroristEntriesForDirectAverages; int m_numberOfCounterTerroristEntriesForDirectAverages; CUtlDict< CSStatType_t, short > m_PropStatTable; CUtlLinkedList m_PlayerSnipedPosition; WeaponStats m_weaponStats[WEAPON_MAX][2]; // Steamworks Gamestats VectorMarketPurchaseData m_MarketPurchases; CSSWeaponData m_WeaponData; CSSDeathData m_DeathData; // A static list of all the stat containers, one for each data structure being tracked static StatContainerList_t * s_StatLists; bool m_bInRound; }; extern CCSGameStats CCS_GameStats; #endif // CS_GAMESTATS_H