summaryrefslogtreecommitdiff
path: root/game/server/cstrike/cs_gamestats.h
blob: 4a24efb342727d3cc56b4f72c444ada9e3429c7d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//========= 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