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.
638 lines
20 KiB
638 lines
20 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#ifdef GAME_DLL
|
||
|
#include "gamestats.h"
|
||
|
#else
|
||
|
#include "tf_hud_statpanel.h"
|
||
|
#endif
|
||
|
#include "tf_gamestats_shared.h"
|
||
|
|
||
|
#ifndef NO_STEAM
|
||
|
#include "steamworks_gamestats.h"
|
||
|
#endif
|
||
|
|
||
|
int TF_Gamestats_RoundStats_t::m_iNumRounds = 0;
|
||
|
time_t TF_Gamestats_RoundStats_t::m_iRoundStartTime = 0;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
const char *s_pStatStrings[ TFSTAT_TOTAL ] =
|
||
|
{
|
||
|
"TFSTAT_UNDEFINED",
|
||
|
"TFSTAT_SHOTS_HIT",
|
||
|
"TFSTAT_SHOTS_FIRED",
|
||
|
"TFSTAT_KILLS",
|
||
|
"TFSTAT_DEATHS",
|
||
|
"TFSTAT_DAMAGE",
|
||
|
"TFSTAT_CAPTURES",
|
||
|
"TFSTAT_DEFENSES",
|
||
|
"TFSTAT_DOMINATIONS",
|
||
|
"TFSTAT_REVENGE",
|
||
|
"TFSTAT_POINTSSCORED",
|
||
|
"TFSTAT_BUILDINGSDESTROYED",
|
||
|
"TFSTAT_HEADSHOTS",
|
||
|
"TFSTAT_PLAYTIME",
|
||
|
"TFSTAT_HEALING",
|
||
|
"TFSTAT_INVULNS",
|
||
|
"TFSTAT_KILLASSISTS",
|
||
|
"TFSTAT_BACKSTABS",
|
||
|
"TFSTAT_HEALTHLEACHED",
|
||
|
"TFSTAT_BUILDINGSBUILT",
|
||
|
"TFSTAT_MAXSENTRYKILLS",
|
||
|
"TFSTAT_TELEPORTS",
|
||
|
"TFSTAT_FIREDAMAGE",
|
||
|
"TFSTAT_BONUS_POINTS",
|
||
|
"TFSTAT_BLASTDAMAGE",
|
||
|
"TFSTAT_DAMAGETAKEN",
|
||
|
"TFSTAT_HEALTHKITS",
|
||
|
"TFSTAT_AMMOKITS",
|
||
|
"TFSTAT_CLASSCHANGES",
|
||
|
"TFSTAT_CRITS",
|
||
|
"TFSTAT_SUICIDES",
|
||
|
"TFSTAT_CURRENCY_COLLECTED",
|
||
|
"TFSTAT_DAMAGE_ASSIST",
|
||
|
"TFSTAT_HEALING_ASSIST",
|
||
|
"TFSTAT_DAMAGE_BOSS",
|
||
|
"TFSTAT_DAMAGE_BLOCKED",
|
||
|
"TFSTAT_DAMAGE_RANGED",
|
||
|
"TFSTAT_DAMAGE_RANGED_CRIT_RANDOM",
|
||
|
"TFSTAT_DAMAGE_RANGED_CRIT_BOOSTED",
|
||
|
"TFSTAT_REVIVED",
|
||
|
};
|
||
|
|
||
|
const char *s_pMapStatStrings[ TFMAPSTAT_TOTAL ] =
|
||
|
{
|
||
|
"TFSTAT_UNDEFINED",
|
||
|
"TFSTAT_PLAYTIME",
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Constructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_LevelStats_t::TF_Gamestats_LevelStats_t()
|
||
|
{
|
||
|
m_bInitialized = false;
|
||
|
m_iRoundStartTime = 0;
|
||
|
m_flRoundStartTime = 0;
|
||
|
m_Header.m_iRoundsPlayed = 0;
|
||
|
m_Header.m_iTotalTime = 0;
|
||
|
m_Header.m_iBlueWins = 0;
|
||
|
m_Header.m_iRedWins = 0;
|
||
|
m_Header.m_iStalemates = 0;
|
||
|
m_Header.m_iBlueSuddenDeathWins = 0;
|
||
|
m_Header.m_iRedSuddenDeathWins = 0;
|
||
|
Q_memset( m_aClassStats, 0, sizeof( m_aClassStats ) );
|
||
|
Q_memset( m_aWeaponStats, 0, sizeof( m_aWeaponStats ) );
|
||
|
Q_memset( m_iPeakPlayerCount, 0, sizeof( m_iPeakPlayerCount ) );
|
||
|
|
||
|
for ( int i = 0; i <= MAX_CONTROL_POINTS; i++ )
|
||
|
{
|
||
|
m_Header.m_iLastCapChangedInRound[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Destructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_LevelStats_t::~TF_Gamestats_LevelStats_t()
|
||
|
{
|
||
|
//m_aPlayerDeaths.Purge();
|
||
|
//m_aPlayerDamage.Purge();
|
||
|
m_bIsRealServer = false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Copy constructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_LevelStats_t::TF_Gamestats_LevelStats_t( const TF_Gamestats_LevelStats_t &stats )
|
||
|
{
|
||
|
m_bInitialized = stats.m_bInitialized;
|
||
|
m_iRoundStartTime = stats.m_iRoundStartTime;
|
||
|
m_flRoundStartTime = stats.m_flRoundStartTime;
|
||
|
m_iMapStartTime = stats.m_iMapStartTime;
|
||
|
m_Header = stats.m_Header;
|
||
|
m_bIsRealServer = stats.m_bIsRealServer;
|
||
|
//m_aPlayerDeaths = stats.m_aPlayerDeaths;
|
||
|
//m_aPlayerDamage = stats.m_aPlayerDamage;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pszMapName -
|
||
|
// nIPAddr -
|
||
|
// nPort -
|
||
|
// flStartTime -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TF_Gamestats_LevelStats_t::Init( const char *pszMapName, int nMapRevision, int nIPAddr, short nPort, float flStartTime )
|
||
|
{
|
||
|
Q_memset( &m_Header, 0, sizeof( m_Header ) ); // TODO: This is correct for steamworks stats, but probably breaks old stats!!!
|
||
|
|
||
|
V_FileBase( pszMapName, m_Header.m_szMapName, sizeof( m_Header.m_szMapName ) );
|
||
|
|
||
|
m_Header.m_nMapRevision = nMapRevision;
|
||
|
m_Header.m_nIPAddr = nIPAddr;
|
||
|
m_Header.m_nPort = nPort;
|
||
|
|
||
|
#ifndef NO_STEAM
|
||
|
// Start the level timer.
|
||
|
m_iMapStartTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
|
||
|
m_iRoundStartTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
|
||
|
m_flRoundStartTime = gpGlobals->curtime;
|
||
|
#endif
|
||
|
|
||
|
m_bIsRealServer = false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : flEndTime -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TF_Gamestats_LevelStats_t::Shutdown( float flEndTime )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Constructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_RoundStats_t::TF_Gamestats_RoundStats_t()
|
||
|
{
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Destructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_RoundStats_t::~TF_Gamestats_RoundStats_t()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: resets the state of stat tracking
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TF_Gamestats_RoundStats_t::Reset()
|
||
|
{
|
||
|
ResetSummary();
|
||
|
m_iRoundStartTime = 0.f;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TF_Gamestats_RoundStats_t::ResetSummary()
|
||
|
{
|
||
|
Q_memset( &m_Summary, 0, sizeof( m_Summary ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Constructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_KillStats_t::TF_Gamestats_KillStats_t()
|
||
|
{
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Destructor
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_KillStats_t::~TF_Gamestats_KillStats_t()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: resets the state of stat tracking
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TF_Gamestats_KillStats_t::Reset()
|
||
|
{
|
||
|
// Q_memset( &m_Summary, 0, sizeof( m_Summary ) );
|
||
|
// m_flRoundStartTime = 0.f;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: constructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TFReportedStats_t::TFReportedStats_t()
|
||
|
{
|
||
|
Clear();
|
||
|
m_bValidData = false;
|
||
|
m_pCurrentGame = NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: destructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TFReportedStats_t::~TFReportedStats_t()
|
||
|
{
|
||
|
if ( m_pCurrentGame )
|
||
|
{
|
||
|
delete m_pCurrentGame;
|
||
|
m_pCurrentGame = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Clears data
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TFReportedStats_t::Clear()
|
||
|
{
|
||
|
m_pCurrentGame = NULL;
|
||
|
m_dictMapStats.Purge();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *szMapName -
|
||
|
// Output : TF_Gamestats_LevelStats_t
|
||
|
//-----------------------------------------------------------------------------
|
||
|
TF_Gamestats_LevelStats_t *TFReportedStats_t::FindOrAddMapStats( const char *szMapName )
|
||
|
{
|
||
|
int iMap = m_dictMapStats.Find( szMapName );
|
||
|
if( iMap == m_dictMapStats.InvalidIndex() )
|
||
|
{
|
||
|
iMap = m_dictMapStats.Insert( szMapName );
|
||
|
}
|
||
|
|
||
|
return &m_dictMapStats[iMap];
|
||
|
}
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Saves data to buffer
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void TFReportedStats_t::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer )
|
||
|
{
|
||
|
// save a version lump at beginning of file
|
||
|
TF_Gamestats_Version_t versionLump;
|
||
|
versionLump.m_iMagic = TF_GAMESTATS_MAGIC;
|
||
|
versionLump.m_iVersion = TF_GAMESTATS_FILE_VERSION;
|
||
|
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_VERSION, 1, sizeof( versionLump ), &versionLump );
|
||
|
|
||
|
// Save data per map.
|
||
|
for ( int iMap = m_dictMapStats.First(); iMap != m_dictMapStats.InvalidIndex(); iMap = m_dictMapStats.Next( iMap ) )
|
||
|
{
|
||
|
// Get the current map.
|
||
|
TF_Gamestats_LevelStats_t *pCurrentMap = &m_dictMapStats[iMap];
|
||
|
Assert( pCurrentMap );
|
||
|
|
||
|
// Write out the lumps.
|
||
|
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPHEADER, 1, sizeof( TF_Gamestats_LevelStats_t::LevelHeader_t ), static_cast<void*>( &pCurrentMap->m_Header ) );
|
||
|
//CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPDEATH, pCurrentMap->m_aPlayerDeaths.Count(), sizeof( TF_Gamestats_LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( pCurrentMap->m_aPlayerDeaths.Base() ) );
|
||
|
//CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPDAMAGE, pCurrentMap->m_aPlayerDamage.Count(), sizeof( TF_Gamestats_LevelStats_t::PlayerDamageLump_t ), static_cast<void*>( pCurrentMap->m_aPlayerDamage.Base() ) );
|
||
|
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_CLASS, ARRAYSIZE( pCurrentMap->m_aClassStats ), sizeof( pCurrentMap->m_aClassStats[0] ),
|
||
|
static_cast<void*>( pCurrentMap->m_aClassStats ) );
|
||
|
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_WEAPON, ARRAYSIZE( pCurrentMap->m_aWeaponStats ), sizeof( pCurrentMap->m_aWeaponStats[0] ),
|
||
|
static_cast<void*>( pCurrentMap->m_aWeaponStats ) );
|
||
|
}
|
||
|
|
||
|
// Append an end tag to verify we've reached end of file and data was sane. (Sometimes we receive stat files that start sane but become filled
|
||
|
// with garbage partway through.)
|
||
|
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_ENDTAG, 1, sizeof( versionLump ), &versionLump );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Loads data from buffer
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool TFReportedStats_t::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer )
|
||
|
{
|
||
|
// read the version lump of beginning of file and verify version
|
||
|
bool bGotEndTag = false;
|
||
|
unsigned short iLump = 0;
|
||
|
unsigned short iLumpCount = 0;
|
||
|
if ( !CBaseGameStats::GetLumpHeader( MAX_LUMP_COUNT, LoadBuffer, iLump, iLumpCount ) )
|
||
|
return false;
|
||
|
if ( iLump != TFSTATS_LUMP_VERSION )
|
||
|
{
|
||
|
Msg( "Didn't find version header. Expected lump type TFSTATS_LUMP_VERSION, got lump type %d. Skipping file.\n", iLump );
|
||
|
return false;
|
||
|
}
|
||
|
TF_Gamestats_Version_t versionLump;
|
||
|
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( versionLump ), &versionLump );
|
||
|
if ( versionLump.m_iMagic != TF_GAMESTATS_MAGIC )
|
||
|
{
|
||
|
Msg( "Incorrect magic # in version header. Expected %x, got %x. Skipping file.\n", TF_GAMESTATS_MAGIC, versionLump.m_iMagic );
|
||
|
return false;
|
||
|
}
|
||
|
if ( versionLump.m_iVersion != TF_GAMESTATS_FILE_VERSION )
|
||
|
{
|
||
|
Msg( "Mismatched file version. Expected file version %d, got %d. Skipping file.\n", TF_GAMESTATS_FILE_VERSION, versionLump.m_iVersion );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
TF_Gamestats_LevelStats_t *pCurrentGame = NULL;
|
||
|
|
||
|
// read all the lumps in the file
|
||
|
while( CBaseGameStats::GetLumpHeader( MAX_LUMP_COUNT, LoadBuffer, iLump, iLumpCount ) )
|
||
|
{
|
||
|
switch ( iLump )
|
||
|
{
|
||
|
case TFSTATS_LUMP_MAPHEADER:
|
||
|
{
|
||
|
TF_Gamestats_LevelStats_t::LevelHeader_t header;
|
||
|
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::LevelHeader_t ), &header );
|
||
|
|
||
|
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||
|
if ( ( header.m_iRoundsPlayed < 0 ) || ( header.m_iTotalTime < 0 ) || ( header.m_iRoundsPlayed > 1000 ) )
|
||
|
return false;
|
||
|
|
||
|
// if there's no interesting data, skip this file. (Need to have server not send it in this case.)
|
||
|
if ( header.m_iTotalTime == 0 )
|
||
|
return false;
|
||
|
|
||
|
pCurrentGame = FindOrAddMapStats( header.m_szMapName );
|
||
|
if ( pCurrentGame )
|
||
|
{
|
||
|
pCurrentGame->m_Header = header;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TFSTATS_LUMP_MAPDEATH:
|
||
|
{
|
||
|
//CUtlVector<TF_Gamestats_LevelStats_t::PlayerDeathsLump_t> playerDeaths;
|
||
|
|
||
|
//playerDeaths.SetCount( iLumpCount );
|
||
|
//CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( playerDeaths.Base() ) );
|
||
|
//if ( pCurrentGame )
|
||
|
//{
|
||
|
// pCurrentGame->m_aPlayerDeaths = playerDeaths;
|
||
|
//}
|
||
|
break;
|
||
|
}
|
||
|
case TFSTATS_LUMP_MAPDAMAGE:
|
||
|
{
|
||
|
//CUtlVector<TF_Gamestats_LevelStats_t::PlayerDamageLump_t> playerDamage;
|
||
|
|
||
|
//playerDamage.SetCount( iLumpCount );
|
||
|
//CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::PlayerDamageLump_t ), static_cast<void*>( playerDamage.Base() ) );
|
||
|
//if ( pCurrentGame )
|
||
|
//{
|
||
|
// pCurrentGame->m_aPlayerDamage = playerDamage;
|
||
|
//}
|
||
|
break;
|
||
|
}
|
||
|
case TFSTATS_LUMP_CLASS:
|
||
|
{
|
||
|
Assert( pCurrentGame );
|
||
|
if ( !pCurrentGame )
|
||
|
return false;
|
||
|
Assert ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aClassStats ) );
|
||
|
if ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aClassStats ) )
|
||
|
{
|
||
|
CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( pCurrentGame->m_aClassStats ), sizeof( pCurrentGame->m_aClassStats[0] ),
|
||
|
pCurrentGame->m_aClassStats );
|
||
|
|
||
|
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||
|
for ( int i = 0; i < ARRAYSIZE( pCurrentGame->m_aClassStats ); i++ )
|
||
|
{
|
||
|
TF_Gamestats_ClassStats_t &classStats = pCurrentGame->m_aClassStats[i];
|
||
|
if ( ( classStats.iSpawns < 0 ) || ( classStats.iSpawns > 10000 ) || ( classStats.iTotalTime < 0 ) || ( classStats.iTotalTime > 36000 * 20 ) ||
|
||
|
( classStats.iKills < 0 ) || ( classStats.iKills > 10000 ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// mismatched lump size, possibly from different build, don't know how it interpret it, just skip over it
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TFSTATS_LUMP_WEAPON:
|
||
|
{
|
||
|
Assert( pCurrentGame );
|
||
|
if ( !pCurrentGame )
|
||
|
return false;
|
||
|
Assert ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aWeaponStats ) );
|
||
|
if ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aWeaponStats ) )
|
||
|
{
|
||
|
CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( pCurrentGame->m_aWeaponStats ), sizeof( pCurrentGame->m_aWeaponStats[0] ),
|
||
|
pCurrentGame->m_aWeaponStats );
|
||
|
|
||
|
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||
|
if ( ( pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired < 0 ) || ( pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired > 100000 )
|
||
|
|| ( pCurrentGame->m_aWeaponStats[TF_WEAPON_FLAMETHROWER_ROCKET].iShotsFired != 0 ) ) // check that unused weapon has 0 shots
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// mismatched lump size, possibly from different build, don't know how it interpret it, just skip over it
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TFSTATS_LUMP_ENDTAG:
|
||
|
{
|
||
|
// check that end tag is valid -- should be version lump again
|
||
|
TF_Gamestats_Version_t versionLump;
|
||
|
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( versionLump ), &versionLump );
|
||
|
if ( versionLump.m_iMagic != TF_GAMESTATS_MAGIC )
|
||
|
{
|
||
|
Msg( "Incorrect magic # in version header. Expected %x, got %x. Skipping file.\n", TF_GAMESTATS_MAGIC, versionLump.m_iMagic );
|
||
|
return false;
|
||
|
}
|
||
|
if ( versionLump.m_iVersion != TF_GAMESTATS_FILE_VERSION )
|
||
|
{
|
||
|
Msg( "Mismatched file version. Expected file version %d, got %d. Skipping file.\n", TF_GAMESTATS_FILE_VERSION, versionLump.m_iVersion );
|
||
|
return false;
|
||
|
}
|
||
|
bGotEndTag = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bGotEndTag;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// TF2 Beta Maps
|
||
|
// Robot Destruction
|
||
|
//-----------------------------------------------------------------------------
|
||
|
RobotDestructionStats_t::RobotDestructionStats_t()
|
||
|
{
|
||
|
Clear();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void RobotDestructionStats_t::Clear()
|
||
|
{
|
||
|
V_memset( &iRobotInteraction, 0, sizeof( iRobotInteraction ) );
|
||
|
V_memset( &iRobotCoreInteraction, 0, sizeof( iRobotCoreInteraction ) );
|
||
|
V_memset( &iFlagInteraction, 0, sizeof( iFlagInteraction ) );
|
||
|
|
||
|
V_memset( &iCoresCollectedByTeam, 0, sizeof( iCoresCollectedByTeam ) );
|
||
|
V_memset( &iCoreCollectedByClass, 0, sizeof( iCoreCollectedByClass ) );
|
||
|
|
||
|
V_memset( &iBlueRobotsKilledByType, 0, sizeof( iBlueRobotsKilledByType ) );
|
||
|
V_memset( &iRedRobotsKilledByType, 0, sizeof( iRedRobotsKilledByType ) );
|
||
|
V_memset( &iRobotsDamageFromClass, 0, sizeof( iRobotsDamageFromClass ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int RobotDestructionStats_t::GetRobotInteractionCount()
|
||
|
{
|
||
|
int iCount = 0;
|
||
|
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||
|
{
|
||
|
if ( iRobotInteraction[i] )
|
||
|
{
|
||
|
iCount++;
|
||
|
}
|
||
|
}
|
||
|
return iCount;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int RobotDestructionStats_t::GetRobotCoreInteractionCount()
|
||
|
{
|
||
|
int iCount = 0;
|
||
|
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||
|
{
|
||
|
if ( iRobotCoreInteraction[i] )
|
||
|
{
|
||
|
iCount++;
|
||
|
}
|
||
|
}
|
||
|
return iCount;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int RobotDestructionStats_t::GetFlagInteractionCount()
|
||
|
{
|
||
|
int iCount = 0;
|
||
|
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||
|
{
|
||
|
if ( iFlagInteraction[i] )
|
||
|
{
|
||
|
iCount++;
|
||
|
}
|
||
|
}
|
||
|
return iCount;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char* g_aRoundEndReasons[] =
|
||
|
{
|
||
|
"round_end",
|
||
|
"client_disconnect",
|
||
|
"client_quit",
|
||
|
"server_map_change",
|
||
|
"server_shutdown",
|
||
|
"time_limit_reached",
|
||
|
"win_limit_reached",
|
||
|
"win_diff_limit_reached",
|
||
|
"round_limit_reached",
|
||
|
"next_level_cvar",
|
||
|
};
|
||
|
|
||
|
// Get a string describing the current game type.
|
||
|
const char* GetGameTypeID()
|
||
|
{
|
||
|
ConVarRef tf_gamemode_arena( "tf_gamemode_arena" );
|
||
|
ConVarRef tf_gamemode_cp( "tf_gamemode_cp" );
|
||
|
ConVarRef tf_gamemode_ctf( "tf_gamemode_ctf" );
|
||
|
ConVarRef tf_gamemode_sd( "tf_gamemode_sd" );
|
||
|
ConVarRef tf_gamemode_payload( "tf_gamemode_payload" );
|
||
|
ConVarRef tf_gamemode_mvm( "tf_gamemode_mvm" );
|
||
|
ConVarRef tf_powerup_mode( "tf_powerup_mode" );
|
||
|
ConVarRef tf_gamemode_passtime( "tf_gamemode_passtime" );
|
||
|
|
||
|
const char* pszGameTypeID = NULL;
|
||
|
if ( tf_gamemode_arena.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "arena";
|
||
|
}
|
||
|
else if ( tf_gamemode_cp.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "cp";
|
||
|
}
|
||
|
else if ( tf_gamemode_ctf.GetBool() )
|
||
|
{
|
||
|
if ( tf_powerup_mode.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "ctf_mannpower";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszGameTypeID = "ctf";
|
||
|
}
|
||
|
}
|
||
|
else if ( tf_gamemode_sd.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "sd";
|
||
|
}
|
||
|
else if ( tf_gamemode_payload.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "payload";
|
||
|
}
|
||
|
else if ( tf_gamemode_mvm.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "mvm";
|
||
|
}
|
||
|
else if ( tf_gamemode_passtime.GetBool() )
|
||
|
{
|
||
|
pszGameTypeID = "pass"; // intentionally not "passtime"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszGameTypeID = "custom";
|
||
|
}
|
||
|
|
||
|
return pszGameTypeID;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// TF2 Beta Maps
|
||
|
// Passtime
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void PasstimeStats_t::Clear()
|
||
|
{
|
||
|
memset( &summary, 0, sizeof(summary) );
|
||
|
memset( &classes, 0, sizeof(classes) );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void PasstimeStats_t::AddBallFracSample( float f )
|
||
|
{
|
||
|
Assert( f >= 0 && f <= 1.0f );
|
||
|
int iBin = (uint8) Floor2Int( f * 255 );
|
||
|
summary.nBallFracHistSum += iBin;
|
||
|
++summary.arrBallFracHist[ iBin ];
|
||
|
++summary.nBallFracSampleCount;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void PasstimeStats_t::AddPassTravelDistSample( float f )
|
||
|
{
|
||
|
if ( summary.nPassTravelDistSampleCount >= summary.k_nMaxPassTravelDistSamples )
|
||
|
return;
|
||
|
Assert( f >= 0 );
|
||
|
summary.arrPassTravelDistSamples[ summary.nPassTravelDistSampleCount ] = (uint16) Float2Int( f );
|
||
|
++summary.nPassTravelDistSampleCount;
|
||
|
}
|
||
|
|
||
|
#ifdef CLIENT_DLL
|
||
|
MapStats_t &GetMapStats( map_identifier_t iMapID )
|
||
|
{
|
||
|
return CTFStatPanel::GetMapStats( iMapID );
|
||
|
}
|
||
|
#endif
|