//========= 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