diff options
Diffstat (limited to 'game/server/episodic/ep2_gamestats.cpp')
| -rw-r--r-- | game/server/episodic/ep2_gamestats.cpp | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/game/server/episodic/ep2_gamestats.cpp b/game/server/episodic/ep2_gamestats.cpp new file mode 100644 index 0000000..af6d5e9 --- /dev/null +++ b/game/server/episodic/ep2_gamestats.cpp @@ -0,0 +1,585 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#if defined( GAME_DLL ) +#include "cbase.h" +#endif +#include "ep2_gamestats.h" +#include "tier1/utlbuffer.h" +#include "vehicle_base.h" +#include "tier1/utlstring.h" +#include "filesystem.h" +#include "icommandline.h" + +static CEP2GameStats s_CEP2GameStats_Singleton; +CBaseGameStats *g_pEP2GameStats = &s_CEP2GameStats_Singleton; + + +CEP2GameStats::CEP2GameStats( void ) +{ + Q_memset( m_flInchesRemainder, 0, sizeof( m_flInchesRemainder ) ); + m_pCurrentMap = NULL; + m_dictMapStats.Purge(); +} + +const char *CEP2GameStats::GetStatSaveFileName( void ) +{ + //overriding the default for backwards compatibility with release stat tracking code + return "ep2_gamestats.dat"; +} + +const char *CEP2GameStats::GetStatUploadRegistryKeyName( void ) +{ + //overriding the default for backwards compatibility with release stat tracking code + return "GameStatsUpload_Ep2"; +} + + +static char const *ep2Maps[] = +{ + "ep2_outland_01", + "ep2_outland_02", + "ep2_outland_03", + "ep2_outland_04", + "ep2_outland_05", + "ep2_outland_06", + "ep2_outland_06a", + "ep2_outland_07", + "ep2_outland_08", + "ep2_outland_09", + "ep2_outland_10", + "ep2_outland_10a", + "ep2_outland_11", + "ep2_outland_11a", + "ep2_outland_12", + "ep2_outland_12a" +}; + + +bool CEP2GameStats::UserPlayedAllTheMaps( void ) +{ + int c = ARRAYSIZE( ep2Maps ); + for ( int i = 0; i < c; ++i ) + { + int idx = m_BasicStats.m_MapTotals.Find( ep2Maps[ i ] ); + if( idx == m_BasicStats.m_MapTotals.InvalidIndex() ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +// Input : - +//----------------------------------------------------------------------------- +CEP2GameStats::~CEP2GameStats() +{ + m_pCurrentMap = NULL; + m_dictMapStats.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &SaveBuffer - +//----------------------------------------------------------------------------- +void CEP2GameStats::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer ) +{ + // Save data per map. + for ( int iMap = m_dictMapStats.First(); iMap != m_dictMapStats.InvalidIndex(); iMap = m_dictMapStats.Next( iMap ) ) + { + // Get the current map. + Ep2LevelStats_t *pCurrentMap = &m_dictMapStats[iMap]; + Assert( pCurrentMap ); + pCurrentMap->AppendToBuffer( SaveBuffer ); + } +} + +void CEP2GameStats::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) +{ + Ep2LevelStats_t::LoadData( m_dictMapStats, LoadBuffer ); +} + +//----------------------------------------------------------------------------- +// 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 ); +} + +Ep2LevelStats_t::EntityDeathsLump_t *CEP2GameStats::FindDeathsLump( char const *npcName ) +{ + if ( !m_pCurrentMap ) + return NULL; + + char const *name = npcName; + // Hack to fixup name + if ( !Q_stricmp( name, "npc_ministrider" ) ) + { + name = "npc_hunter"; + } + + if ( Q_strnicmp( name, "npc_", 4 ) ) + return NULL; + + int idx = m_pCurrentMap->m_dictEntityDeaths.Find( name ); + if ( idx == m_pCurrentMap->m_dictEntityDeaths.InvalidIndex() ) + { + idx = m_pCurrentMap->m_dictEntityDeaths.Insert( name ); + } + + return &m_pCurrentMap->m_dictEntityDeaths[ idx ]; +} + +Ep2LevelStats_t::WeaponLump_t *CEP2GameStats::FindWeaponsLump( char const *pchWeaponName, bool bPrimary ) +{ + if ( !m_pCurrentMap ) + return NULL; + + if ( !pchWeaponName ) + { + AssertOnce( !"FindWeaponsLump pchWeaponName == NULL" ); + return NULL; + } + + char lookup[ 512 ]; + Q_snprintf( lookup, sizeof( lookup ), "%s_%s", pchWeaponName, bPrimary ? "primary" : "secondary" ); + int idx = m_pCurrentMap->m_dictWeapons.Find( lookup ); + if ( idx == m_pCurrentMap->m_dictWeapons.InvalidIndex() ) + { + idx = m_pCurrentMap->m_dictWeapons.Insert( lookup ); + } + + return &m_pCurrentMap->m_dictWeapons[ idx ]; +} + +// Finds the generic stats lump +Ep2LevelStats_t::GenericStatsLump_t *CEP2GameStats::FindGenericLump( char const *pchStatName ) +{ + if ( !m_pCurrentMap ) + return NULL; + if ( !pchStatName || !*pchStatName ) + return NULL; + + int idx = m_pCurrentMap->m_dictGeneric.Find( pchStatName ); + if ( idx == m_pCurrentMap->m_dictGeneric.InvalidIndex() ) + { + idx = m_pCurrentMap->m_dictGeneric.Insert( pchStatName ); + } + + return &m_pCurrentMap->m_dictGeneric[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szMapName - +// Output : Ep2LevelStats_t +//----------------------------------------------------------------------------- +Ep2LevelStats_t *CEP2GameStats::FindOrAddMapStats( const char *szMapName ) +{ + int iMap = m_dictMapStats.Find( szMapName ); + if( iMap == m_dictMapStats.InvalidIndex() ) + { + iMap = m_dictMapStats.Insert( szMapName ); + } + + return &m_dictMapStats[iMap]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEP2GameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info ) +{ + BaseClass::Event_PlayerDamage( pBasePlayer, info ); + + m_pCurrentMap->m_FloatCounters[ Ep2LevelStats_t::COUNTER_DAMAGETAKEN ] += info.GetDamage(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEP2GameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + BaseClass::Event_PlayerKilledOther( pAttacker, pVictim, info ); + + if ( pAttacker ) + { + StatsLog( "Attacker: %s\n", pAttacker->GetClassname() ); + } + + if ( !pVictim ) + { + return; + } + + char const *pchVictim = pVictim->GetClassname(); + Ep2LevelStats_t::EntityDeathsLump_t *lump = FindDeathsLump( pchVictim ); + if ( lump ) + { + ++lump->m_nBodyCount; + StatsLog( "Player has killed %d %s's\n", lump->m_nBodyCount, pchVictim ); + + CPropVehicleDriveable *veh = dynamic_cast< CPropVehicleDriveable * >( pAttacker ); + if ( !veh ) + veh = dynamic_cast< CPropVehicleDriveable * >( info.GetInflictor() ); + if ( veh ) + { + CBaseEntity *driver = veh->GetDriver(); + if ( driver && driver->IsPlayer() ) + { + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES ]; + StatsLog( " Vehicular homicide [%I64d] of %s's\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES ], pchVictim ); + } + } + } + else + { + StatsLog( "Player killed %s (not tracked)\n", pchVictim ); + } +} + +void CEP2GameStats::Event_Punted( CBaseEntity *pObject ) +{ + BaseClass::Event_Punted( pObject ); + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_OBJECTSPUNTED ]; +} + +//----------------------------------------------------------------------------- +// 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 ); + } +} + +void CEP2GameStats::Event_CrateSmashed() +{ + BaseClass::Event_CrateSmashed(); + + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_CRATESSMASHED ]; +} + +void CEP2GameStats::Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting ) +{ + BaseClass::Event_PlayerTraveled( pBasePlayer, distanceInInches, bInVehicle, bSprinting ); + + int iIndex = INVEHICLE; + if ( !bInVehicle ) + { + iIndex = bSprinting ? ONFOOTSPRINTING : ONFOOT; + } + + m_flInchesRemainder[ iIndex ] += distanceInInches; + uint64 intPart = (uint64)m_flInchesRemainder[ iIndex ]; + m_flInchesRemainder[ iIndex ] -= intPart; + if ( intPart > 0 ) + { + if ( bInVehicle ) + { + m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_INVEHICLE ] += intPart; + } + else + { + if ( bSprinting ) + { + m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOTSPRINTING ] += intPart; + } + else + { + m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOT ] += intPart; + } + } + } + + Ep2LevelStats_t *map = m_pCurrentMap; + if ( !map ) + return; + + Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord; + + if ( rec && + rec->m_nSaveHealth == -1 ) + { + Vector pos = pBasePlayer->GetAbsOrigin(); + rec->m_nSavePos[ 0 ] = (short)pos.x; + rec->m_nSavePos[ 1 ] = (short)pos.y; + rec->m_nSavePos[ 2 ] = (short)pos.z; + rec->m_nSaveHealth = clamp( pBasePlayer->GetHealth(), 0, 100 ); + } +} + +void CEP2GameStats::Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName ) +{ + BaseClass::Event_WeaponFired( pShooter, bPrimary, pchWeaponName ); + + Ep2LevelStats_t::WeaponLump_t *lump = FindWeaponsLump( pchWeaponName, bPrimary ); + if ( lump ) + { + ++lump->m_nShots; + } +} + +void CEP2GameStats::Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info ) +{ + BaseClass::Event_WeaponHit( pShooter, bPrimary, pchWeaponName, info ); + Ep2LevelStats_t::WeaponLump_t *lump = FindWeaponsLump( pchWeaponName, bPrimary ); + if ( lump ) + { + ++lump->m_nHits; + lump->m_flDamageInflicted += info.GetDamage(); + } +} + +void CEP2GameStats::Event_SaveGame( void ) +{ + BaseClass::Event_SaveGame(); + + Ep2LevelStats_t *map = m_pCurrentMap; + if ( !map ) + return; + + ++map->m_IntCounters[ Ep2LevelStats_t::COUNTER_SAVES ]; + StatsLog( " %I64uth save on this map\n", map->m_IntCounters[ Ep2LevelStats_t::COUNTER_SAVES ] ); + + char const *pchSaveFile = engine->GetSaveFileName(); + if ( !pchSaveFile || !pchSaveFile[ 0 ] ) + return; + + char name[ 512 ]; + Q_strncpy( name, pchSaveFile, sizeof( name ) ); + Q_strlower( name ); + Q_FixSlashes( name ); + + unsigned int uFileTime = filesystem->GetFileTime( name, "GAME" ); + // Latch off previous + map->m_SaveGameInfo.Latch( name, uFileTime ); + + Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord; + + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) + { + Vector pos = pPlayer->GetAbsOrigin(); + rec->m_nSavePos[ 0 ] = (short)pos.x; + rec->m_nSavePos[ 1 ] = (short)pos.y; + rec->m_nSavePos[ 2 ] = (short)pos.z; + rec->m_nSaveHealth = clamp( pPlayer->GetHealth(), 0, 100 ); + rec->m_SaveType = Q_stristr( pchSaveFile, "autosave" ) ? + Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_AUTOSAVE : Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_USERSAVE; + + StatsLog( "save pos %i %i %i w/ health %d\n", + rec->m_nSavePos[ 0 ], + rec->m_nSavePos[ 1 ], + rec->m_nSavePos[ 2 ], + rec->m_nSaveHealth ); + + } +} + +void CEP2GameStats::Event_LoadGame( void ) +{ + BaseClass::Event_LoadGame(); + + Ep2LevelStats_t *map = m_pCurrentMap; + if ( !map ) + return; + + ++map->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADS ]; + StatsLog( " %I64uth load on this map\n", map->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADS ] ); + + char const *pchSaveFile = engine->GetMostRecentlyLoadedFileName(); + if ( !pchSaveFile || !pchSaveFile[ 0 ] ) + return; + + char name[ 512 ]; + Q_snprintf( name, sizeof( name ), "save/%s", pchSaveFile ); + Q_DefaultExtension( name, IsX360() ? ".360.sav" : ".sav", sizeof( name ) ); + Q_FixSlashes( name ); + Q_strlower( name ); + + Ep2LevelStats_t::SaveGameInfo_t *pSaveGameInfo = &map->m_SaveGameInfo; + + if ( pSaveGameInfo->m_nCurrentSaveFileTime == 0 || + pSaveGameInfo->m_sCurrentSaveFile != name ) + { + unsigned int uFileTime = filesystem->GetFileTime( name, "GAME" ); + + // Latch off previous + StatsLog( "Relatching save game file due to time or filename change (%s : %u)\n", name, uFileTime ); + pSaveGameInfo->Latch( name, uFileTime ); + } +} + +void CEP2GameStats::Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle ) +{ + BaseClass::Event_FlippedVehicle( pDriver, pVehicle ); + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED ]; + StatsLog( "%I64u time vehicle overturned\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED ] ); +} + +void CEP2GameStats::Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame ) +{ + BaseClass::Event_PreSaveGameLoaded( pSaveName, bInGame ); + + // Not currently in a level + if ( !bInGame ) + return; + + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( !pPlayer ) + return; + + // We're loading a saved game while the player is still alive (are they stuck?) + if ( pPlayer->IsAlive() ) + { + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE ]; + StatsLog( "%I64u game loaded with living player\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE ] ); + } +} + +void CEP2GameStats::Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer ) +{ + BaseClass::Event_PlayerEnteredGodMode( pBasePlayer ); + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_GODMODES ]; + StatsLog( "%I64u time entering godmode\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_GODMODES ] ); +} + +void CEP2GameStats::Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer ) +{ + BaseClass::Event_PlayerEnteredNoClip( pBasePlayer ); + ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ]; + StatsLog( "%I64u time entering NOCLIP\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] ); +} + +void CEP2GameStats::Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer ) +{ + BaseClass::Event_DecrementPlayerEnteredNoClip( pBasePlayer ); + if ( m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] > 0 ) + { + --m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ]; + } + StatsLog( "%I64u decrement entering NOCLIP (entering vehicle doesn't count)\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] ); +} + +// Generic statistics lump +void CEP2GameStats::Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount ) +{ + BaseClass::Event_IncrementCountedStatistic( vecAbsOrigin, pchStatisticName, flIncrementAmount ); + + // Find the generic lump + Ep2LevelStats_t::GenericStatsLump_t *lump = FindGenericLump( pchStatisticName ); + if ( lump ) + { + lump->m_Pos[ 0 ] = (short)vecAbsOrigin.x; + lump->m_Pos[ 1 ] = (short)vecAbsOrigin.y; + lump->m_Pos[ 2 ] = (short)vecAbsOrigin.z; + lump->m_flCurrentValue += (double)flIncrementAmount; + ++lump->m_unCount; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void CC_ListDeaths( const CCommand &args ) +{ + Ep2LevelStats_t *map = s_CEP2GameStats_Singleton.FindOrAddMapStats( STRING( gpGlobals->mapname ) ); + if ( !map ) + return; + + int nRendered = 0; + for ( int i = map->m_aPlayerDeaths.Count() - 1; i >= 0 ; --i, ++nRendered ) + { + Vector org( map->m_aPlayerDeaths[ i ].nPosition[ 0 ], + map->m_aPlayerDeaths[ i ].nPosition[ 1 ], + map->m_aPlayerDeaths[ i ].nPosition[ 2 ] + 36.0f ); + + // FIXME: This might overflow + NDebugOverlay::Box( org, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ), 0, 255, 0, 128, 10.0f ); + + /* + Msg( "%s killed %s with %s at (%d,%d,%d)\n", + g_aClassNames[ map->m_aPlayerDeaths[ i ].iAttackClass ], + g_aClassNames[ map->m_aPlayerDeaths[ i ].iTargetClass ], + WeaponIdToAlias( map->m_aPlayerDeaths[ i ].iWeapon ), + map->m_aPlayerDeaths[ i ].nPosition[ 0 ], + map->m_aPlayerDeaths[ i ].nPosition[ 1 ], + map->m_aPlayerDeaths[ i ].nPosition[ 2 ] ); + */ + + if ( nRendered > 150 ) + break; + } + Msg( "\nlisted %d deaths\n", map->m_aPlayerDeaths.Count() ); +} + +static ConCommand listDeaths("listdeaths", CC_ListDeaths, "lists player deaths", 0 );
\ No newline at end of file |