diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/replay/genericclassbased_replay.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/replay/genericclassbased_replay.cpp')
| -rw-r--r-- | mp/src/game/client/replay/genericclassbased_replay.cpp | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/mp/src/game/client/replay/genericclassbased_replay.cpp b/mp/src/game/client/replay/genericclassbased_replay.cpp new file mode 100644 index 00000000..51556d20 --- /dev/null +++ b/mp/src/game/client/replay/genericclassbased_replay.cpp @@ -0,0 +1,426 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "genericclassbased_replay.h"
+#include "clientmode_shared.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplayfactory.h"
+#include "replay/ireplayscreenshotmanager.h"
+#include "replay/screenshot.h"
+#include "replay/gamedefs.h"
+#include <time.h>
+
+//----------------------------------------------------------------------------------------
+
+extern IReplayScreenshotManager *g_pReplayScreenshotManager;
+
+//----------------------------------------------------------------------------------------
+
+CGenericClassBasedReplay::CGenericClassBasedReplay()
+: m_nPlayerTeam( 0 )
+{
+ m_szKillerName[ 0 ] = 0;
+
+ m_nPlayerClass = REPLAY_CLASS_UNDEFINED;
+ m_nKillerClass = REPLAY_CLASS_UNDEFINED;
+}
+
+CGenericClassBasedReplay::~CGenericClassBasedReplay()
+{
+ OnEndRecording();
+
+ m_vecKills.PurgeAndDeleteElements();
+ m_vecDominations.PurgeAndDeleteElements();
+ m_vecAssisterDominations.PurgeAndDeleteElements();
+ m_vecRevenges.PurgeAndDeleteElements();
+ m_vecAssisterRevenges.PurgeAndDeleteElements();
+}
+
+void CGenericClassBasedReplay::OnBeginRecording()
+{
+ BaseClass::OnBeginRecording();
+
+ Assert( gameeventmanager );
+ ListenForGameEvent( "player_death" );
+}
+
+void CGenericClassBasedReplay::OnEndRecording()
+{
+ if ( gameeventmanager )
+ {
+ gameeventmanager->RemoveListener( this );
+ }
+
+ BaseClass::OnEndRecording();
+}
+
+void CGenericClassBasedReplay::OnComplete()
+{
+ BaseClass::OnComplete();
+}
+
+bool CGenericClassBasedReplay::ShouldAllowDelete() const
+{
+ return g_pClientReplayContext->GetMovieManager()->GetNumMoviesDependentOnReplay( this ) == 0;
+}
+
+void CGenericClassBasedReplay::OnDelete()
+{
+ BaseClass::OnDelete();
+}
+
+void CGenericClassBasedReplay::FireGameEvent( IGameEvent *pEvent )
+{
+}
+
+void CGenericClassBasedReplay::Update()
+{
+ BaseClass::Update();
+
+ // Record any new stats
+ RecordUpdatedStats();
+
+ // Setup next update
+ m_flNextUpdateTime = engine->Time() + .1f;
+}
+
+float CGenericClassBasedReplay::GetKillScreenshotDelay()
+{
+ ConVarRef replay_screenshotkilldelay( "replay_screenshotkilldelay" );
+ return replay_screenshotkilldelay.IsValid() ? replay_screenshotkilldelay.GetFloat() : 0.5f;
+}
+
+void CGenericClassBasedReplay::RecordUpdatedStats()
+{
+ // Get current stats
+ static RoundStats_t s_curStats;
+ if ( !GetCurrentStats( s_curStats ) )
+ return;
+
+ // Go through each stat and see if it's changed
+ for ( int i = 0; i < REPLAY_GAMESTATS_MAX; ++i )
+ {
+ const int nCurStat = s_curStats.Get( i );
+ const int nRefStat = m_refStats.Get( i );
+
+ if ( nCurStat != nRefStat )
+ {
+ // Calculate new stat based on reference
+ const int nLifeStat = nCurStat - nRefStat;
+
+ if ( nLifeStat != m_lifeStats.Get( i ) )
+ {
+ ConVarRef replay_debug( "replay_debug" );
+ if ( replay_debug.IsValid() && replay_debug.GetBool() )
+ {
+ Msg( "REPLAY: Player stat \"%s\" changed from %i to %i.\n", GetStatString( i ), nRefStat, nCurStat );
+ }
+
+ // Set the new stat
+ m_lifeStats.Set( i, nLifeStat );
+ }
+ }
+ }
+}
+
+bool CGenericClassBasedReplay::Read( KeyValues *pIn )
+{
+ if ( !BaseClass::Read( pIn ) )
+ return false;
+
+ // Read player class
+ m_nPlayerClass = pIn->GetInt( "player_class" ); Assert( IsValidClass( m_nPlayerClass ) );
+
+ // Read player team
+ m_nPlayerTeam = pIn->GetInt( "player_team" ); Assert( IsValidTeam( m_nPlayerTeam ) );
+
+ // Read killer info
+ m_nKillerClass = pIn->GetInt( "killer_class" );
+ V_strcpy( m_szKillerName, pIn->GetString( "killer_name" ) );
+
+ // Make sure vector is clear
+ Assert( GetKillCount() == 0 );
+
+ // Read all kill data and add the kills vector
+ KeyValues *pKills = pIn->FindKey( "kills" );
+ if ( pKills )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKills, pKill )
+ {
+ // Create the kill data
+ AddKill(
+ pKill->GetString( "victim_name" ),
+ pKill->GetInt( "victim_class" )
+ );
+ }
+ }
+
+ AddKillStats( m_vecDominations , pIn, "dominations", REPLAY_GAMESTATS_DOMINATIONS );
+ AddKillStats( m_vecAssisterDominations, pIn, "assister_dominations", REPLAY_GAMESTATS_UNDEFINED );
+ AddKillStats( m_vecRevenges , pIn, "revenges", REPLAY_GAMESTATS_REVENGE );
+ AddKillStats( m_vecAssisterRevenges , pIn, "assister_revenges", REPLAY_GAMESTATS_UNDEFINED );
+
+ // Read stats by index
+ KeyValues *pStats = pIn->FindKey( "stats" );
+ if ( pStats )
+ {
+ for ( int i = 0; i < REPLAY_GAMESTATS_MAX; ++i )
+ {
+ char szStatKey[ 16 ];
+ V_snprintf( szStatKey, sizeof( szStatKey ), "%i", i );
+ m_lifeStats.Set( i, pStats->GetInt( szStatKey ) );
+ }
+ }
+
+ return true;
+}
+
+void CGenericClassBasedReplay::AddKillStats( CUtlVector< GenericStatInfo_t * > &vecKillStats, KeyValues *pIn, const char *pSubKeyName, int iStatIndex )
+{
+ Assert( vecKillStats.Count() == 0 );
+ KeyValues *pSubKey = pIn->FindKey( pSubKeyName );
+ if ( pSubKey )
+ {
+ FOR_EACH_TRUE_SUBKEY( pSubKey, pCurKillStat )
+ {
+ GenericStatInfo_t *pNewKillStat = new GenericStatInfo_t;
+ pNewKillStat->m_nVictimFriendId = pCurKillStat->GetInt( "victim_friend_id" );
+ pNewKillStat->m_nAssisterFriendId = pCurKillStat->GetInt( "assister_friend_id" );
+ vecKillStats.AddToTail( pNewKillStat );
+ }
+ }
+
+ // Duplicate the data in the life stats
+ if ( iStatIndex > m_nStatUndefined )
+ {
+ m_lifeStats.Set( iStatIndex, vecKillStats.Count() );
+ }
+}
+
+void CGenericClassBasedReplay::Write( KeyValues *pOut )
+{
+ BaseClass::Write( pOut );
+
+ // Write player class
+ pOut->SetInt( "player_class", m_nPlayerClass );
+
+ // Write player team
+ pOut->SetInt( "player_team", m_nPlayerTeam );
+
+ // Write killer info
+ pOut->SetInt( "killer_class", m_nKillerClass );
+ pOut->SetString( "killer_name", m_szKillerName );
+
+ // Write kills
+ KeyValues *pKills = new KeyValues( "kills" );
+ pOut->AddSubKey( pKills );
+
+ for ( int i = 0; i < GetKillCount(); ++i )
+ {
+ KillData_t *pCurKill = m_vecKills[ i ];
+
+ KeyValues *pKillOut = new KeyValues( "kill" );
+ pKills->AddSubKey( pKillOut );
+
+ // Write kill data
+ pKillOut->SetString( "victim_name", pCurKill->m_szPlayerName );
+ pKillOut->SetInt( "victim_class", pCurKill->m_nPlayerClass );
+ }
+
+ WriteKillStatVector( m_vecDominations , "dominations" , "domination" , pOut, 1 );
+ WriteKillStatVector( m_vecAssisterDominations, "assister_dominations", "assister_domination", pOut, 2 );
+ WriteKillStatVector( m_vecRevenges , "revenges" , "revenge" , pOut, 1 );
+ WriteKillStatVector( m_vecAssisterRevenges , "assister_revenges" , "assister_revenge" , pOut, 2 );
+
+ // Write non-zero stats by index
+ KeyValues *pStats = new KeyValues( "stats" );
+ pOut->AddSubKey( pStats );
+
+ for ( int i = 0; i < REPLAY_GAMESTATS_MAX; ++i )
+ {
+ const int nCurStat = m_lifeStats.Get( i );
+ if ( nCurStat )
+ {
+ char szStatKey[ 16 ];
+ V_snprintf( szStatKey, sizeof( szStatKey ), "%i", i );
+ pStats->SetInt( szStatKey, nCurStat );
+ }
+ }
+}
+
+void CGenericClassBasedReplay::WriteKillStatVector( CUtlVector< CGenericClassBasedReplay::GenericStatInfo_t * > const &vec, const char *pSubKeyName,
+ const char *pElementKeyName, KeyValues *pRootKey, int nNumMembersToWrite ) const
+{
+ Assert( nNumMembersToWrite >= 1 );
+
+ // Write dominations
+ KeyValues *pSubKey = new KeyValues( pSubKeyName );
+ pRootKey->AddSubKey( pSubKey );
+
+ for ( int i = 0; i < vec.Count(); ++i )
+ {
+ GenericStatInfo_t *pSrcData = vec[ i ];
+
+ KeyValues *pCurSubKey = new KeyValues( pElementKeyName );
+ pSubKey->AddSubKey( pCurSubKey );
+
+ // Always write
+ pCurSubKey->SetInt( "victim_friend_id", pSrcData->m_nVictimFriendId );
+
+ if ( nNumMembersToWrite > 1 )
+ {
+ pCurSubKey->SetInt( "assister_friend_id", pSrcData->m_nAssisterFriendId );
+ }
+ }
+}
+
+void CGenericClassBasedReplay::AddKill( const char *pPlayerName, int nPlayerClass )
+{
+ KillData_t *pNewKillData = new KillData_t;
+
+ V_strcpy( pNewKillData->m_szPlayerName , pPlayerName );
+ pNewKillData->m_nPlayerClass = nPlayerClass;
+
+ ConVarRef replay_debug( "replay_debug" );
+ if ( replay_debug.IsValid() && replay_debug.GetBool() )
+ {
+ DevMsg( "\n\nRecorded kill: name=%s, class=%s (this=%i)\n\n", pPlayerName, GetPlayerClass( nPlayerClass ), (int)this );
+ }
+
+ m_vecKills.AddToTail( pNewKillData );
+}
+
+const char *CGenericClassBasedReplay::GetPlayerClass() const
+{
+ return GetPlayerClass( m_nPlayerClass );
+}
+
+void CGenericClassBasedReplay::AddDomination( int nVictimID )
+{
+ AddKillStatFromUserIds( m_vecDominations, nVictimID );
+}
+
+void CGenericClassBasedReplay::AddAssisterDomination( int nVictimID, int nAssiterID )
+{
+ AddKillStatFromUserIds( m_vecAssisterDominations, nVictimID, nAssiterID );
+}
+
+void CGenericClassBasedReplay::AddRevenge( int nVictimID )
+{
+ AddKillStatFromUserIds( m_vecRevenges, nVictimID );
+}
+
+void CGenericClassBasedReplay::AddAssisterRevenge( int nVictimID, int nAssiterID )
+{
+ AddKillStatFromUserIds( m_vecAssisterRevenges, nVictimID, nAssiterID );
+}
+
+void CGenericClassBasedReplay::AddKillStatFromUserIds( CUtlVector< GenericStatInfo_t * > &vec, int nVictimId, int nAssisterId/*=0*/ )
+{
+ uint32 nVictimFriendId;
+ if ( !GetFriendIdFromUserId( engine->GetPlayerForUserID( nVictimId ), nVictimFriendId ) )
+ return;
+
+ uint32 nAssisterFriendId = 0;
+ if ( nAssisterId && !GetFriendIdFromUserId( engine->GetPlayerForUserID( nAssisterId ), nAssisterFriendId ) )
+ return;
+
+ AddKillStatFromFriendIds( vec, nVictimFriendId, nAssisterFriendId );
+}
+
+void CGenericClassBasedReplay::AddKillStatFromFriendIds( CUtlVector< GenericStatInfo_t * > &vec, uint32 nVictimFriendId, uint32 nAssisterFriendId/*=0*/ )
+{
+ GenericStatInfo_t *pNewKillStat = new GenericStatInfo_t;
+ pNewKillStat->m_nVictimFriendId = nVictimFriendId;
+ pNewKillStat->m_nAssisterFriendId = nAssisterFriendId;
+ vec.AddToTail( pNewKillStat );
+}
+
+bool CGenericClassBasedReplay::GetFriendIdFromUserId( int nPlayerIndex, uint32 &nFriendIdOut ) const
+{
+ player_info_t pi;
+ if ( !steamapicontext->SteamFriends() ||
+ !steamapicontext->SteamUtils() ||
+ !engine->GetPlayerInfo( nPlayerIndex, &pi ) )
+ {
+ AssertMsg( 0, "REPLAY: Failed to add domination" );
+ nFriendIdOut = 0;
+ return false;
+ }
+
+ nFriendIdOut = pi.friendsID;
+
+ return true;
+}
+
+const char *CGenericClassBasedReplay::GetMaterialFriendlyPlayerClass() const
+{
+ return GetPlayerClass();
+}
+
+const char *CGenericClassBasedReplay::GetKillerName() const
+{
+ Assert( WasKilled() );
+ return m_szKillerName;
+}
+
+const char *CGenericClassBasedReplay::GetKillerClass() const
+{
+ Assert( WasKilled() );
+ return GetPlayerClass( m_nKillerClass );
+}
+
+void CGenericClassBasedReplay::DumpGameSpecificData() const
+{
+ DevMsg( " class: %s\n", GetPlayerClass() );
+
+ // Print kills
+ DevMsg( " %i kills:\n", GetKillCount() );
+ for ( int i = 0; i < GetKillCount(); ++i )
+ {
+ KillData_t *pCurKill = m_vecKills[ i ];
+ Msg( " kill %i: name=%s class=%s\n", i, pCurKill->m_szPlayerName, GetPlayerClass( pCurKill->m_nPlayerClass ) );
+ }
+
+ if ( !WasKilled() )
+ {
+ Msg( " No killer.\n" );
+ return;
+ }
+
+ // Print killer info
+ Msg( " killer: name=%s class=%s\n", m_szKillerName, GetPlayerClass( m_nKillerClass ) );
+}
+
+void CGenericClassBasedReplay::SetPlayerClass( int nPlayerClass )
+{
+ //Assert( IsValidClass( nPlayerClass ) );
+ m_nPlayerClass = nPlayerClass;
+
+ // Setup reference stats if this is a valid class
+ if ( IsValidClass( nPlayerClass ) )
+ {
+ GetCurrentStats( m_refStats );
+ }
+}
+
+void CGenericClassBasedReplay::SetPlayerTeam( int nPlayerTeam )
+{
+ m_nPlayerTeam = nPlayerTeam;
+}
+
+void CGenericClassBasedReplay::RecordPlayerDeath( const char *pKillerName, int nKillerClass )
+{
+ V_strcpy( m_szKillerName, pKillerName );
+ m_nKillerClass = nKillerClass;
+}
+
+
+//----------------------------------------------------------------------------------------
+
+#endif
\ No newline at end of file |