diff options
Diffstat (limited to 'game/client/replay/genericclassbased_replay.cpp')
| -rw-r--r-- | game/client/replay/genericclassbased_replay.cpp | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/game/client/replay/genericclassbased_replay.cpp b/game/client/replay/genericclassbased_replay.cpp new file mode 100644 index 0000000..e42667b --- /dev/null +++ b/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_safe( 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_safe( 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_safe( m_szKillerName, pKillerName ); + m_nKillerClass = nKillerClass; +} + + +//---------------------------------------------------------------------------------------- + +#endif
\ No newline at end of file |