source-engine/game/server/cstrike/cs_gamestats.h
2022-03-02 11:45:17 +03:00

341 lines
10 KiB
C++

//========= 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<sHappyCamperSnipePosition, int> 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