//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEP2GameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ) { BaseClass::Event_PlayerKilled( pPlayer, info ); if ( info.GetDamageType() & DMG_FALL ) { ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_FALLINGDEATHS ]; } Ep2LevelStats_t::PlayerDeathsLump_t death; // set the location where the target died const Vector &org = pPlayer->GetAbsOrigin(); death.nPosition[ 0 ] = static_cast<short>( org.x ); death.nPosition[ 1 ] = static_cast<short>( org.y ); death.nPosition[ 2 ] = static_cast<short>( org.z ); StatsLog( "CEP2GameStats::Event_PlayerKilled at location [%d %d %d]\n", (int)death.nPosition[ 0 ], (int)death.nPosition[ 1 ], (int)death.nPosition[ 2 ] ); // set the class of the attacker CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); if ( pInflictor ) { StatsLog( "Inflictor: %s\n", pInflictor->GetClassname() ); } if ( pKiller ) { char const *pchKiller = pKiller->GetClassname(); Ep2LevelStats_t::EntityDeathsLump_t *lump = FindDeathsLump( pchKiller ); if ( lump ) { ++lump->m_nKilledPlayer; StatsLog( "Player has been killed %d times by %s's\n", lump->m_nKilledPlayer, pchKiller ); } else { StatsLog( "Player killed by %s (not tracked)\n", pchKiller ); } } // add it to the list of deaths Ep2LevelStats_t *map = FindOrAddMapStats( STRING( gpGlobals->mapname ) ); int slot = map->m_aPlayerDeaths.AddToTail( death ); Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord; if ( rec ) { if ( rec->m_nFirstDeathIndex == -1 ) { rec->m_nFirstDeathIndex = slot; } ++rec->m_nNumDeaths; StatsLog( "Player has died %d times since last save/load\n", rec->m_nNumDeaths ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEP2GameStats::Event_LevelInit( void ) { BaseClass::Event_LevelInit(); char const *pchTag = NULL; CommandLine()->CheckParm( "-gamestatstag", &pchTag ); if ( !pchTag ) { pchTag = ""; } m_pCurrentMap = FindOrAddMapStats( STRING( gpGlobals->mapname ) ); m_pCurrentMap->Init( STRING( gpGlobals->mapname ), gpGlobals->curtime, pchTag, gpGlobals->mapversion ); }
//----------------------------------------------------------------------------- // 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; } // 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; m_pCurrentGame = FindOrAddMapStats( header.m_szMapName ); if ( m_pCurrentGame ) { m_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 ( m_pCurrentGame ) { m_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 ( m_pCurrentGame ) { m_pCurrentGame->m_aPlayerDamage = playerDamage; } break; } case TFSTATS_LUMP_CLASS: { Assert( m_pCurrentGame ); Assert ( iLumpCount == ARRAYSIZE( m_pCurrentGame->m_aClassStats ) ); if ( iLumpCount == ARRAYSIZE( m_pCurrentGame->m_aClassStats ) ) { CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( m_pCurrentGame->m_aClassStats ), sizeof( m_pCurrentGame->m_aClassStats[0] ), m_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( m_pCurrentGame->m_aClassStats ); i++ ) { TF_Gamestats_ClassStats_t &classStats = m_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( m_pCurrentGame ); Assert ( iLumpCount == ARRAYSIZE( m_pCurrentGame->m_aWeaponStats ) ); if ( iLumpCount == ARRAYSIZE( m_pCurrentGame->m_aWeaponStats ) ) { CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( m_pCurrentGame->m_aWeaponStats ), sizeof( m_pCurrentGame->m_aWeaponStats[0] ), m_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 ( ( m_pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired < 0 ) || ( m_pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired > 100000 ) || ( m_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; }