diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/portal/portal_gamestats.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/portal/portal_gamestats.cpp')
| -rw-r--r-- | game/server/portal/portal_gamestats.cpp | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/game/server/portal/portal_gamestats.cpp b/game/server/portal/portal_gamestats.cpp new file mode 100644 index 0000000..452e9da --- /dev/null +++ b/game/server/portal/portal_gamestats.cpp @@ -0,0 +1,683 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "portal_gamestats.h" +#include "tier1/utlbuffer.h" +#include "portal_player.h" + +#define PORTALSTATS_TRIMEVENT( varName, varType )\ + if( varName->Count() > varType::TRIMSIZE )\ + varName->RemoveMultiple( 0, (varName->Count() - varType::TRIMSIZE) ); + +#define PORTALSTATS_PREPCHUNK( structType, bufName, SizePositionVarNameToUse )\ + SaveBuffer.PutUnsignedShort( structType::CHUNKID );\ + int SizePositionVarNameToUse = bufName.TellPut();\ + bufName.PutUnsignedInt( 0 ); + +#define PORTALSTATS_WRITECHUNKSIZE( bufName, SizePositionVariable ) \ +{\ + int SizePositionVariable ## _askdjbhas = bufName.TellPut() - SizePositionVariable;\ + bufName.SeekPut( CUtlBuffer::SEEK_HEAD, SizePositionVariable );\ + bufName.PutUnsignedInt( SizePositionVariable ## _askdjbhas );\ + bufName.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );\ +} + +static Portal_Gamestats_LevelStats_t s_DummyStats; +CPortalGameStats g_PortalGameStats; + +class CPortalGameStatsSingleton //used to remove the constructor destructor from the general class +{ +public: + CPortalGameStatsSingleton( void ) + { + gamestats = (CBaseGameStats *)&g_PortalGameStats; + CreateLevelStatPointers( &s_DummyStats ); + } + + ~CPortalGameStatsSingleton( void ) + { + AssertMsg( (CBaseGameStats::StatTrackingAllowed() == false) || + ((s_DummyStats.m_pDeaths->Count() == 0) && + (s_DummyStats.m_pPlacements->Count() == 0) && + (s_DummyStats.m_pUseEvents->Count() == 0) && + (s_DummyStats.m_pStuckSpots->Count() == 0) && + (s_DummyStats.m_pJumps->Count() == 0) && + (s_DummyStats.m_pTimeSpentInVisLeafs->Count() == 0)), + "Some stats were deferred to the dummy entry." ); + DestroyLevelStatPointers( &s_DummyStats ); + } +}; +CPortalGameStatsSingleton s_CPGSS_ThisJustSitsInMemory; + +CPortalGameStats::CPortalGameStats( void ) +: m_pCurrentMapStats( &s_DummyStats ) +{ + +} + +CPortalGameStats::~CPortalGameStats( void ) +{ + Clear(); +} + + + +void Portal_Gamestats_LevelStats_t::AppendSubChunksToBuffer( CUtlBuffer &SaveBuffer ) +{ + if( m_pDeaths->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pDeaths, PlayerDeaths_t ); + PORTALSTATS_PREPCHUNK( PlayerDeaths_t, SaveBuffer, iSubChunkSizePosition ); + + int iDeathStatsCount = m_pDeaths->Count(); + SaveBuffer.PutUnsignedInt( iDeathStatsCount ); + for( int i = 0; i != iDeathStatsCount; ++i ) + { + PlayerDeaths_t &DeathStat = m_pDeaths->Element( i ); + + SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.x ); + SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.y ); + SaveBuffer.PutFloat( DeathStat.ptPositionOfDeath.z ); + SaveBuffer.PutInt( DeathStat.iDamageType ); + SaveBuffer.PutString( DeathStat.szAttackerClassName ); + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } + + if( m_pPlacements->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pPlacements, PortalPlacement_t ); + PORTALSTATS_PREPCHUNK( PortalPlacement_t, SaveBuffer, iSubChunkSizePosition ); + + int iPlacementStatsCount = m_pPlacements->Count(); + SaveBuffer.PutUnsignedInt( iPlacementStatsCount ); + for( int i = 0; i != iPlacementStatsCount; ++i ) + { + PortalPlacement_t &PortalPlacementStat = m_pPlacements->Element( i ); + + SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.x ); + SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.y ); + SaveBuffer.PutFloat( PortalPlacementStat.ptPlayerFiredFrom.z ); + + SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.x ); + SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.y ); + SaveBuffer.PutFloat( PortalPlacementStat.ptPlacementPosition.z ); + + SaveBuffer.PutChar( PortalPlacementStat.iSuccessCode ); + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } + + if( m_pUseEvents->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pUseEvents, PlayerUse_t ); + PORTALSTATS_PREPCHUNK( PlayerUse_t, SaveBuffer, iSubChunkSizePosition ); + + int iUseEventCount = m_pUseEvents->Count(); + SaveBuffer.PutUnsignedInt( iUseEventCount ); + for( int i = 0; i != iUseEventCount; ++i ) + { + PlayerUse_t &UseEvent = m_pUseEvents->Element( i ); + + SaveBuffer.PutFloat( UseEvent.ptTraceStart.x ); + SaveBuffer.PutFloat( UseEvent.ptTraceStart.y ); + SaveBuffer.PutFloat( UseEvent.ptTraceStart.z ); + + SaveBuffer.PutFloat( UseEvent.vTraceDelta.x ); + SaveBuffer.PutFloat( UseEvent.vTraceDelta.y ); + SaveBuffer.PutFloat( UseEvent.vTraceDelta.z ); + + SaveBuffer.PutString( UseEvent.szUseEntityClassName ); + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } + + if( m_pStuckSpots->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pStuckSpots, StuckEvent_t ); + PORTALSTATS_PREPCHUNK( StuckEvent_t, SaveBuffer, iSubChunkSizePosition ); + + int iStuckStatsCount = m_pStuckSpots->Count(); + SaveBuffer.PutUnsignedInt( iStuckStatsCount ); + for( int i = 0; i != iStuckStatsCount; ++i ) + { + StuckEvent_t &StuckStat = m_pStuckSpots->Element( i ); + + SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.x ); + SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.y ); + SaveBuffer.PutFloat( StuckStat.ptPlayerPosition.z ); + + SaveBuffer.PutFloat( StuckStat.qPlayerAngles.x ); + SaveBuffer.PutFloat( StuckStat.qPlayerAngles.y ); + SaveBuffer.PutFloat( StuckStat.qPlayerAngles.z ); + + unsigned char bitFlags = 0; + if( StuckStat.bNearPortal ) + bitFlags |= (1 << 0); + + if( StuckStat.bDucking ) + bitFlags |= (1 << 1); + + SaveBuffer.PutUnsignedChar( bitFlags ); + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } + + if( m_pJumps->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pJumps, JumpEvent_t ); + PORTALSTATS_PREPCHUNK( JumpEvent_t, SaveBuffer, iSubChunkSizePosition ); + + int iJumpStatsCount = m_pJumps->Count(); + SaveBuffer.PutUnsignedInt( iJumpStatsCount ); + for( int i = 0; i != iJumpStatsCount; ++i ) + { + JumpEvent_t &JumpStat = m_pJumps->Element( i ); + + SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.x ); + SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.y ); + SaveBuffer.PutFloat( JumpStat.ptPlayerPositionAtJumpStart.z ); + + SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.x ); + SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.y ); + SaveBuffer.PutFloat( JumpStat.vPlayerVelocityAtJumpStart.z ); + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } + + if( m_pTimeSpentInVisLeafs->Count() != 0 ) + { + PORTALSTATS_TRIMEVENT( m_pTimeSpentInVisLeafs, LeafTimes_t ); + PORTALSTATS_PREPCHUNK( LeafTimes_t, SaveBuffer, iSubChunkSizePosition ); + + int iLeafTimeStatsCount = m_pTimeSpentInVisLeafs->Count(); + SaveBuffer.PutUnsignedInt( iLeafTimeStatsCount ); + for( int i = 0; i != iLeafTimeStatsCount; ++i ) + { + LeafTimes_t &LeafTimeStat = m_pTimeSpentInVisLeafs->Element( i ); + + SaveBuffer.PutFloat( LeafTimeStat.fTimeSpentInVisLeaf ); //assumes visleafs will be the same when this data is loaded again, or that there will be a way to invalidate the data + } + + PORTALSTATS_WRITECHUNKSIZE( SaveBuffer, iSubChunkSizePosition ); + } +} + +void Portal_Gamestats_LevelStats_t::LoadSubChunksFromBuffer( CUtlBuffer &LoadBuffer, unsigned int iChunkEndPosition ) +{ + Clear(); + + while( ((unsigned int)LoadBuffer.TellGet()) != iChunkEndPosition ) + { + Assert( (iChunkEndPosition - LoadBuffer.TellGet()) > (sizeof( unsigned short ) + sizeof( unsigned int )) ); //at least an empty chunk left + + unsigned short iChunkID = LoadBuffer.GetUnsignedShort(); + unsigned int iChunkSize = LoadBuffer.GetUnsignedInt() - sizeof( unsigned int ); //chunk size includes the chunk size data itself +#ifdef _DEBUG + unsigned int iChunkEndPosition = LoadBuffer.TellGet() + iChunkSize; //used in an assert later +#endif + + + switch( iChunkID ) + { +#ifdef PORTAL_GAMESTATS_VERBOSE //don't bother loading verbose chunks if we're not going to save them back out + case PlayerDeaths_t::CHUNKID: + { + unsigned int iDeathStatCount = LoadBuffer.GetUnsignedInt(); + for( unsigned int i = 0; i != iDeathStatCount; ++i ) + { + int index = m_pDeaths->AddToTail(); + PlayerDeaths_t &DeathStat = m_pDeaths->Element( index ); + + DeathStat.ptPositionOfDeath.x = LoadBuffer.GetFloat(); + DeathStat.ptPositionOfDeath.y = LoadBuffer.GetFloat(); + DeathStat.ptPositionOfDeath.z = LoadBuffer.GetFloat(); + DeathStat.iDamageType = LoadBuffer.GetInt(); + LoadBuffer.GetString( DeathStat.szAttackerClassName ); + } + + break; + } + + case PortalPlacement_t::CHUNKID: + { + unsigned int iPlacementStatCount = LoadBuffer.GetUnsignedInt(); + for( unsigned int i = 0; i != iPlacementStatCount; ++i ) + { + int index = m_pPlacements->AddToTail(); + PortalPlacement_t &PlacementStat = m_pPlacements->Element( index ); + + PlacementStat.ptPlayerFiredFrom.x = LoadBuffer.GetFloat(); + PlacementStat.ptPlayerFiredFrom.y = LoadBuffer.GetFloat(); + PlacementStat.ptPlayerFiredFrom.z = LoadBuffer.GetFloat(); + + PlacementStat.ptPlacementPosition.x = LoadBuffer.GetFloat(); + PlacementStat.ptPlacementPosition.y = LoadBuffer.GetFloat(); + PlacementStat.ptPlacementPosition.z = LoadBuffer.GetFloat(); + + PlacementStat.iSuccessCode = LoadBuffer.GetChar(); + } + + break; + } + + case PlayerUse_t::CHUNKID: + { + int iUseEventCount = LoadBuffer.GetUnsignedInt(); + for( int i = 0; i != iUseEventCount; ++i ) + { + int index = m_pUseEvents->AddToTail(); + PlayerUse_t &UseEvent = m_pUseEvents->Element( index ); + + UseEvent.ptTraceStart.x = LoadBuffer.GetFloat(); + UseEvent.ptTraceStart.y = LoadBuffer.GetFloat(); + UseEvent.ptTraceStart.z = LoadBuffer.GetFloat(); + + UseEvent.vTraceDelta.x = LoadBuffer.GetFloat(); + UseEvent.vTraceDelta.y = LoadBuffer.GetFloat(); + UseEvent.vTraceDelta.z = LoadBuffer.GetFloat(); + + LoadBuffer.GetString( UseEvent.szUseEntityClassName ); + } + + break; + } + + case StuckEvent_t::CHUNKID: + { + unsigned int iStuckEventCount = LoadBuffer.GetUnsignedInt(); + for( unsigned int i = 0; i != iStuckEventCount; ++i ) + { + int index = m_pStuckSpots->AddToTail(); + StuckEvent_t &StuckEvent = m_pStuckSpots->Element( index ); + + StuckEvent.ptPlayerPosition.x = LoadBuffer.GetFloat(); + StuckEvent.ptPlayerPosition.y = LoadBuffer.GetFloat(); + StuckEvent.ptPlayerPosition.z = LoadBuffer.GetFloat(); + + StuckEvent.qPlayerAngles.x = LoadBuffer.GetFloat(); + StuckEvent.qPlayerAngles.y = LoadBuffer.GetFloat(); + StuckEvent.qPlayerAngles.z = LoadBuffer.GetFloat(); + + unsigned char bitFlags = LoadBuffer.GetUnsignedChar(); + + StuckEvent.bNearPortal = ( (bitFlags & (1 << 0)) != 0 ); + StuckEvent.bDucking = ( (bitFlags & (1 << 1)) != 0 ); + } + + break; + } + + case JumpEvent_t::CHUNKID: + { + unsigned int iJumpEventCount = LoadBuffer.GetUnsignedInt(); + for( unsigned int i = 0; i != iJumpEventCount; ++i ) + { + int index = m_pJumps->AddToTail(); + JumpEvent_t &JumpEvent = m_pJumps->Element( index ); + + JumpEvent.ptPlayerPositionAtJumpStart.x = LoadBuffer.GetFloat(); + JumpEvent.ptPlayerPositionAtJumpStart.y = LoadBuffer.GetFloat(); + JumpEvent.ptPlayerPositionAtJumpStart.z = LoadBuffer.GetFloat(); + + JumpEvent.vPlayerVelocityAtJumpStart.x = LoadBuffer.GetFloat(); + JumpEvent.vPlayerVelocityAtJumpStart.y = LoadBuffer.GetFloat(); + JumpEvent.vPlayerVelocityAtJumpStart.z = LoadBuffer.GetFloat(); + } + + break; + } + + case LeafTimes_t::CHUNKID: + { + //IMPORTANT TODO + //TODO: Detect if the leaves have changed and invalidate these counts + + unsigned int iLeafCount = LoadBuffer.GetUnsignedInt(); + for( unsigned int i = 0; i != iLeafCount; ++i ) + { + int index = m_pTimeSpentInVisLeafs->AddToTail(); + LeafTimes_t &LeafTime = m_pTimeSpentInVisLeafs->Element( index ); + + LeafTime.fTimeSpentInVisLeaf = LoadBuffer.GetFloat(); + } + + break; + } +#else + case PlayerDeaths_t::CHUNKID: //warning workaround +#endif + default: + { + //an unknown chunk, skip it + LoadBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, iChunkSize ); + } + }; + + Assert( ((unsigned int)LoadBuffer.TellGet()) == iChunkEndPosition ); + + }; +} + +void Portal_Gamestats_LevelStats_t::Clear( void ) +{ + m_pDeaths->RemoveAll(); + m_pPlacements->RemoveAll(); + m_pStuckSpots->RemoveAll(); + m_pJumps->RemoveAll(); + m_pTimeSpentInVisLeafs->RemoveAll(); +} + + + + + +void CPortalGameStats::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE //we only have verbose chunks for now, so only write custom data if we're verbosely tracking + + SaveBuffer.PutUnsignedShort( PORTAL_GAMESTATS_VERSION ); + + //no headers allowed, chunks were chosen for their flexibility in loading even when parts of the data are unknown + //you can simulate a header by enclosing the entirety of the custom data in a chunk that starts with a header, but you'll kill loading in old versions + + if( m_CustomMapStats.Count() != 0 ) //we have some map stats + { + //put out a map chunk for each map + for ( int i = m_CustomMapStats.First(); i != m_CustomMapStats.InvalidIndex(); i = m_CustomMapStats.Next( i ) ) + { + SaveBuffer.PutShort( Portal_Gamestats_LevelStats_t::CHUNKID ); + + //we can trivially find the chunk size after the chunk is written, but chunk size needs to be at the beginning, reserve the space for chunk size now + int iChunkSizePosition = SaveBuffer.TellPut(); + SaveBuffer.PutUnsignedInt( 0 ); + + char const *szMapName = m_CustomMapStats.GetElementName( i ); + Portal_Gamestats_LevelStats_t &mapStats = m_CustomMapStats[ i ]; + + SaveBuffer.PutString( szMapName ); + mapStats.AppendSubChunksToBuffer( SaveBuffer ); + + + //write out the map stats chunk size + { + int iChunkSize = SaveBuffer.TellPut() - iChunkSizePosition; + Assert( iChunkSize >= sizeof( int ) ); //minimum sizeof( int ) + +#ifdef _DEBUG + int iOldTellPut = SaveBuffer.TellPut(); //needed for an assert below +#endif + + SaveBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, iChunkSizePosition ); + SaveBuffer.PutUnsignedInt( iChunkSize ); + SaveBuffer.SeekPut( CUtlBuffer::SEEK_TAIL, 0 ); + + Assert( iOldTellPut == SaveBuffer.TellPut() ); //writing the chunk size should have overwritten, not inserted + } + } + } + +#endif //#ifdef PORTAL_GAMESTATS_VERBOSE +} + +void CPortalGameStats::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) +{ +#ifdef _DEBUG + unsigned short iSaveStatsVersion = LoadBuffer.GetUnsignedShort(); + AssertOnce( iSaveStatsVersion <= PORTAL_GAMESTATS_VERSION ); //useful to know, but shouldn't be a failure case +#else + LoadBuffer.GetUnsignedShort(); //don't really need the version +#endif + + int iEndPosition = LoadBuffer.TellPut(); + + while( LoadBuffer.TellGet() != iEndPosition ) + { + Assert( (iEndPosition - LoadBuffer.TellGet()) > (sizeof( unsigned short ) + sizeof( unsigned int )) ); //at least an empty chunk left + + unsigned short iChunkID = LoadBuffer.GetUnsignedShort(); + unsigned int iChunkSize = LoadBuffer.GetUnsignedInt() - sizeof( unsigned int ); //chunk size includes the chunk size data itself +#ifdef PORTAL_GAMESTATS_VERBOSE + unsigned int iChunkEndPosition = LoadBuffer.TellGet() + iChunkSize; //used in an assert later +#endif + + switch( iChunkID ) + { +#ifdef PORTAL_GAMESTATS_VERBOSE //levelstats only have verbose data for the time being, so only bother to load verboseness if we're still tracking it + case Portal_Gamestats_LevelStats_t::CHUNKID: + { + //map chunk + char szMapName[256]; + LoadBuffer.GetString( szMapName ); + + Portal_Gamestats_LevelStats_t *mapStats = FindOrAddMapStats( szMapName ); + mapStats->LoadSubChunksFromBuffer( LoadBuffer, iChunkEndPosition ); + + break; + } +#else + case Portal_Gamestats_LevelStats_t::CHUNKID: //warning workaround +#endif + default: + { + //an unknown chunk, skip it + LoadBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, iChunkSize ); + } + }; + +#ifdef PORTAL_GAMESTATS_VERBOSE + Assert( ((unsigned int)LoadBuffer.TellGet()) == iChunkEndPosition ); +#endif + } +} + + +void CPortalGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE + if( CBaseGameStats::StatTrackingAllowed() == false ) + return; + + int index = m_pCurrentMapStats->m_pDeaths->AddToTail(); + Portal_Gamestats_LevelStats_t::PlayerDeaths_t &DeathStat = m_pCurrentMapStats->m_pDeaths->Element( index ); + + DeathStat.ptPositionOfDeath = pPlayer->GetAbsOrigin(); + DeathStat.iDamageType = info.GetDamageType(); + DeathStat.szAttackerClassName[0] = '\0'; + + CBaseEntity *pInflictor = info.GetInflictor(); + if( pInflictor ) + Q_strncpy( DeathStat.szAttackerClassName, pInflictor->GetClassname(), sizeof( DeathStat.szAttackerClassName ) ); +#endif +} + +void CPortalGameStats::Event_PlayerJump( const Vector &ptStartPosition, const Vector &vStartVelocity ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE + if( CBaseGameStats::StatTrackingAllowed() == false ) + return; + + int index = m_pCurrentMapStats->m_pJumps->AddToTail(); + Portal_Gamestats_LevelStats_t::JumpEvent_t &JumpStat = m_pCurrentMapStats->m_pJumps->Element( index ); + + JumpStat.ptPlayerPositionAtJumpStart = ptStartPosition; + JumpStat.vPlayerVelocityAtJumpStart = vStartVelocity; +#endif +} + +void CPortalGameStats::Event_PortalPlacement( const Vector &ptPlayerFiredFrom, const Vector &ptAttemptedPosition, char iSuccessCode ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE + if( CBaseGameStats::StatTrackingAllowed() == false ) + return; + + int index = m_pCurrentMapStats->m_pPlacements->AddToTail(); + Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( index ); + + PlacementStat.ptPlacementPosition = ptAttemptedPosition; + PlacementStat.ptPlayerFiredFrom = ptPlayerFiredFrom; + PlacementStat.iSuccessCode = iSuccessCode; +#endif +} + +void CPortalGameStats::Event_PlayerUsed( const Vector &ptTraceStart, const Vector &vTraceDelta, CBaseEntity *pUsedEntity ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE + if( CBaseGameStats::StatTrackingAllowed() == false ) + return; + + static float fLastUseTime = 0.0f; + + if( fLastUseTime > gpGlobals->curtime ) //I'm not positive, but I think curtime resets between levels, cheap to do this + fLastUseTime = 0.0f; + + if( (gpGlobals->curtime - fLastUseTime) < 0.25f ) //use events cluster + return; + + fLastUseTime = gpGlobals->curtime; + + int index = m_pCurrentMapStats->m_pUseEvents->AddToTail(); + Portal_Gamestats_LevelStats_t::PlayerUse_t &UseEvent = m_pCurrentMapStats->m_pUseEvents->Element( index ); + + UseEvent.ptTraceStart = ptTraceStart; + UseEvent.vTraceDelta = vTraceDelta; + UseEvent.szUseEntityClassName[0] = '\0'; + + if( pUsedEntity ) + Q_strncpy( UseEvent.szUseEntityClassName, pUsedEntity->GetClassname(), sizeof( UseEvent.szUseEntityClassName ) ); +#endif +} + +void CPortalGameStats::Event_PlayerStuck( CPortal_Player *pPlayer ) +{ +#ifdef PORTAL_GAMESTATS_VERBOSE + if( CBaseGameStats::StatTrackingAllowed() == false ) + return; + + static float fLastStuckTime = 0.0f; + + if( fLastStuckTime > gpGlobals->curtime ) //I'm not positive, but I think curtime resets between levels, cheap to do this + fLastStuckTime = 0.0f; + + if( (gpGlobals->curtime - fLastStuckTime) < 10.0f ) //only log one stuck spot per 10 second interval (in case it oscillates) + return; + + fLastStuckTime = gpGlobals->curtime; + + int index = m_pCurrentMapStats->m_pStuckSpots->AddToTail(); + Portal_Gamestats_LevelStats_t::StuckEvent_t &StuckSpot = m_pCurrentMapStats->m_pStuckSpots->Element( index ); + + StuckSpot.ptPlayerPosition = pPlayer->GetAbsOrigin(); + StuckSpot.qPlayerAngles = pPlayer->GetAbsAngles(); + StuckSpot.bNearPortal = (pPlayer->m_hPortalEnvironment.Get() != NULL); + StuckSpot.bDucking = ((pPlayer->m_nButtons & IN_DUCK) != 0); +#endif +} + +void CPortalGameStats::Event_LevelInit( void ) +{ + BaseClass::Event_LevelInit(); + m_pCurrentMapStats = FindOrAddMapStats( STRING( gpGlobals->mapname ) ); +} + +void CPortalGameStats::Event_MapChange( const char *szOldMapName, const char *szNewMapName ) +{ + BaseClass::Event_MapChange( szOldMapName, szNewMapName ); + m_pCurrentMapStats = FindOrAddMapStats( szNewMapName ); +} + + + +void CPortalGameStats::Clear( void ) +{ + for( int i = m_CustomMapStats.First(); i != m_CustomMapStats.InvalidIndex(); i = m_CustomMapStats.Next( i ) ) + { + DestroyLevelStatPointers( &m_CustomMapStats[i] ); + } + + m_CustomMapStats.RemoveAll(); +} + +Portal_Gamestats_LevelStats_t *CPortalGameStats::FindOrAddMapStats( const char *szMapName ) +{ + int idx = m_CustomMapStats.Find( szMapName ); + if( idx == m_CustomMapStats.InvalidIndex() ) + { + idx = m_CustomMapStats.Insert( szMapName ); + + CreateLevelStatPointers( &m_CustomMapStats[idx] ); + } + + return &m_CustomMapStats[ idx ]; +} + + +void CreateLevelStatPointers( Portal_Gamestats_LevelStats_t *pFillIn ) +{ + pFillIn->m_pDeaths = new CUtlVector<Portal_Gamestats_LevelStats_t::PlayerDeaths_t>; + pFillIn->m_pPlacements = new CUtlVector<Portal_Gamestats_LevelStats_t::PortalPlacement_t>; + pFillIn->m_pUseEvents = new CUtlVector<Portal_Gamestats_LevelStats_t::PlayerUse_t>; + pFillIn->m_pStuckSpots = new CUtlVector<Portal_Gamestats_LevelStats_t::StuckEvent_t>; + pFillIn->m_pJumps = new CUtlVector<Portal_Gamestats_LevelStats_t::JumpEvent_t>; + pFillIn->m_pTimeSpentInVisLeafs = new CUtlVector<Portal_Gamestats_LevelStats_t::LeafTimes_t>; +} + +void DestroyLevelStatPointers( Portal_Gamestats_LevelStats_t *pDestroyFrom ) +{ + delete pDestroyFrom->m_pDeaths; + delete pDestroyFrom->m_pPlacements; + delete pDestroyFrom->m_pUseEvents; + delete pDestroyFrom->m_pStuckSpots; + delete pDestroyFrom->m_pJumps; + delete pDestroyFrom->m_pTimeSpentInVisLeafs; +} + + + +static char const *portalMaps[] = +{ + "testchmb_a_00", + "testchmb_a_01", + "testchmb_a_02", + "testchmb_a_03", + "testchmb_a_04", + "testchmb_a_05", + "testchmb_a_06", + "testchmb_a_07", + "testchmb_a_08", + "testchmb_a_09", + "testchmb_a_10", + "testchmb_a_11", + //"testchmb_a_12", //12 got deleted/skipped + "testchmb_a_13", + "testchmb_a_14", + "testchmb_a_15", + "escape_00", + "escape_01", + "escape_02" +}; + + +bool CPortalGameStats::UserPlayedAllTheMaps( void ) +{ + int c = ARRAYSIZE( portalMaps ); + for ( int i = 0; i < c; ++i ) + { + int idx = m_BasicStats.m_MapTotals.Find( portalMaps[ i ] ); + if( idx == m_BasicStats.m_MapTotals.InvalidIndex() ) + return false; + } + + return true; +} + + |