This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+210
View File
@@ -0,0 +1,210 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replay/cdll_replay.h"
#include "replay/replaycamera.h"
#include "replay/replay_ragdoll.h"
#include "replay/iclientreplay.h"
#include "replay/ireplayscreenshotsystem.h"
#include "replay/ireplaysystem.h"
#include "replay/ienginereplay.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/vgui/replayconfirmquitdlg.h"
#include "replay/vgui/replaybrowsermainpanel.h"
#include "replay/vgui/replayinputpanel.h"
#include "replay/vgui/replayperformanceeditor.h"
#include "replayperformanceplaybackhandler.h"
#include "vgui/ILocalize.h"
#include "vgui/ISurface.h"
#include "clientmode.h"
#include "iviewrender.h"
#include "igameevents.h"
#include "replaycamera.h"
#if defined( TF_CLIENT_DLL )
#include "c_tf_gamestats.h"
#endif
#include "steamworks_gamestats.h"
#include "replay_gamestats_shared.h"
//----------------------------------------------------------------------------------------
class IReplayScreenshotSystem;
//----------------------------------------------------------------------------------------
extern void ReplayUI_OpenReplayRenderOverlay();
extern void ReplayUI_HideRenderOverlay();
//----------------------------------------------------------------------------------------
extern IReplayMovieManager *g_pReplayMovieManager;
//----------------------------------------------------------------------------------------
class CClientReplayImp : public IClientReplay
{
public:
virtual uint64 GetServerSessionId()
{
return GetSteamWorksSGameStatsUploader().GetServerSessionID();
}
virtual IReplayScreenshotSystem *GetReplayScreenshotSystem()
{
if ( g_pEngineReplay->IsSupportedModAndPlatform() )
return view->GetReplayScreenshotSystem();
return NULL;
}
virtual IReplayPerformancePlaybackHandler *GetPerformancePlaybackHandler()
{
return g_pReplayPerformancePlaybackHandler;
}
virtual bool CacheReplayRagdolls( const char* pFilename, int nStartTick )
{
return Replay_CacheRagdolls( pFilename, nStartTick );
}
virtual void OnSaveReplay( ReplayHandle_t hNewReplay, bool bShowInputDlg )
{
if ( bShowInputDlg )
{
// Get a name for the replay, saves to disk, add thumbnail to replay browser
ShowReplayInputPanel( hNewReplay );
}
else
{
// Just add the thumbnail if the replay browser exists
CReplayBrowserPanel* pReplayBrowser = ReplayUI_GetBrowserPanel();
if ( pReplayBrowser )
{
pReplayBrowser->OnSaveReplay( hNewReplay );
}
}
// Fire a message the game DLL can intercept (for achievements, etc).
IGameEvent *event = gameeventmanager->CreateEvent( "replay_saved" );
if ( event )
{
gameeventmanager->FireEventClientSide( event );
}
}
virtual void OnDeleteReplay( ReplayHandle_t hReplay )
{
CReplayBrowserPanel* pReplayBrowser = ReplayUI_GetBrowserPanel();
if ( pReplayBrowser )
{
pReplayBrowser->OnDeleteReplay( hReplay );
}
}
virtual void DisplayReplayMessage( const char *pLocalizeStr, bool bUrgent, bool bDlg, const char *pSound )
{
// Display a message?
if ( !pLocalizeStr || !pLocalizeStr[0] )
return;
g_pClientMode->DisplayReplayMessage( pLocalizeStr, -1.0f, bUrgent, pSound, bDlg );
}
virtual void DisplayReplayMessage( const wchar_t *pText, bool bUrgent, bool bDlg, const char *pSound )
{
if ( !pText || !pText[0] )
return;
const int nLen = wcslen( pText ) + 1;
char *pAnsi = new char[ nLen ];
g_pVGuiLocalize->ConvertUnicodeToANSI( pText, pAnsi, nLen );
g_pClientMode->DisplayReplayMessage( pAnsi, -1.0f, bUrgent, pSound, bDlg );
}
virtual bool OnConfirmQuit()
{
return ReplayUI_ShowConfirmQuitDlg();
}
virtual void OnRenderStart()
{
ReplayUI_OpenReplayRenderOverlay();
}
virtual void OnRenderComplete( const RenderMovieParams_t &RenderParams, bool bCancelled, bool bSuccess, bool bShowBrowser )
{
ReplayUI_HideRenderOverlay();
if ( bShowBrowser )
{
ReplayUI_ReloadBrowser();
}
// Upload a row to the OGS now that rendering has completed
GetReplayGameStatsHelper().SW_ReplayStats_WriteRenderDataEnd( RenderParams, bCancelled ? "cancelled" : bSuccess ? "success" : "failed" );
}
virtual void InitPerformanceEditor( ReplayHandle_t hReplay )
{
ReplayUI_InitPerformanceEditor( hReplay );
}
virtual void HidePerformanceEditor()
{
ReplayUI_ClosePerformanceEditor();
}
virtual bool ShouldRender()
{
extern ConVar replay_enablerenderpreview;
return !g_pReplayMovieManager->IsRendering() || replay_enablerenderpreview.GetBool();
}
virtual void PlaySound( const char *pSound )
{
if ( g_pVGuiSurface )
{
g_pVGuiSurface->PlaySound( pSound );
}
}
virtual void UploadOgsData( KeyValues *pData, bool bIncludeTimeField )
{
GetReplayGameStatsHelper().UploadError( pData, bIncludeTimeField );
}
virtual bool ShouldCompletePendingReplay( IGameEvent *pEvent )
{
#if defined( TF_CLIENT_DLL )
return !( pEvent->GetInt( "death_flags" ) & TF_DEATH_FEIGN_DEATH );
#else
return true;
#endif
}
virtual void OnPlaybackComplete( ReplayHandle_t hReplay, int iPerformance )
{
ReplayUI_ReloadBrowser( hReplay, iPerformance );
}
virtual IReplayCamera *GetReplayCamera()
{
return ReplayCamera();
}
virtual bool OnEndOfReplayReached()
{
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( !pEditor )
return false;
return pEditor->OnEndOfReplayReached();
}
};
static CClientReplayImp s_ClientReplayImp;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CClientReplayImp, IClientReplay, CLIENT_REPLAY_INTERFACE_VERSION, s_ClientReplayImp );
#endif // #if defined( REPLAY_ENABLED )
+16
View File
@@ -0,0 +1,16 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//=======================================================================================//
#ifndef CDLL_REPLAY_H
#define CDLL_REPLAY_H
#ifdef _WIN32
#pragma once
#endif
//----------------------------------------------------------------------------------------
class IReplayEngineClient;
//----------------------------------------------------------------------------------------
#endif
+47
View File
@@ -0,0 +1,47 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//----------------------------------------------------------------------------------------
#include "cbase.h"
#include "gamedefs.h"
//----------------------------------------------------------------------------------------
StatInfo_t g_pReplayDisplayGameStats[REPLAY_MAX_DISPLAY_GAMESTATS] =
{
#if defined( TF_CLIENT_DLL )
{ TFSTAT_SHOTS_HIT, "#Stat_ShotsHit", },
{ TFSTAT_SHOTS_FIRED, "#Stat_ShotsFired" },
{ TFSTAT_DAMAGE, "#Stat_Damage" },
{ TFSTAT_CAPTURES, "#Stat_Captures" },
{ TFSTAT_DEFENSES, "#Stat_Defenses" },
{ TFSTAT_REVENGE, "#Stat_Revenge" },
{ TFSTAT_POINTSSCORED, "#Stat_PointsScored" },
{ TFSTAT_BUILDINGSDESTROYED,"#Stat_BuildingsDestroyed" },
{ TFSTAT_HEADSHOTS, "#Stat_Headshots" },
{ TFSTAT_PLAYTIME, "#Stat_PlayTime" },
{ TFSTAT_HEALING, "#Stat_Healing" },
{ TFSTAT_INVULNS, "#Stat_Invulns" },
{ TFSTAT_KILLASSISTS, "#Stat_KillAssists" },
{ TFSTAT_BACKSTABS, "#Stat_BackStabs" },
{ TFSTAT_HEALTHLEACHED, "#Stat_HealthLeached" },
{ TFSTAT_BUILDINGSBUILT, "#Stat_BuildingsBuilt" },
{ TFSTAT_MAXSENTRYKILLS, "#Stat_MaxSentryKills" },
{ TFSTAT_TELEPORTS, "#Stat_Teleports" },
{ TFSTAT_FIREDAMAGE, "#Stat_FiredDamage" },
{ TFSTAT_BONUS_POINTS, "#Stat_BonusPoints" },
{ TFSTAT_BLASTDAMAGE, "#Stat_BlastDamage" },
{ TFSTAT_DAMAGETAKEN, "#Stat_DamageTaken" },
{ TFSTAT_CRITS, "#Stat_Crits" },
#elif defined( CSTRIKE_DLL )
{ CSSTAT_SHOTS_HIT, "#Stat_ShotsHit" },
{ CSSTAT_SHOTS_FIRED, "#Stat_ShotsFired" },
{ CSSTAT_DAMAGE, "#Stat_Damage" },
#endif
};
+79
View File
@@ -0,0 +1,79 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//----------------------------------------------------------------------------------------
#ifndef GAMEDEFS_H
#define GAMEDEFS_H
#ifdef _WIN32
#pragma once
#endif
#if defined( TF_CLIENT_DLL )
# include "tf_gamestats_shared.h"
# define REPLAY_GAMESTATS_UNDEFINED TFSTAT_UNDEFINED
# define REPLAY_GAMESTATS_REVENGE TFSTAT_REVENGE
# define REPLAY_GAMESTATS_DEATHS TFSTAT_DEATHS
# define REPLAY_GAMESTATS_DOMINATIONS TFSTAT_DOMINATIONS
# define REPLAY_GAMESTATS_POINTSSCORED TFSTAT_POINTSSCORED
# define REPLAY_GAMESTATS_MAX TFSTAT_TOTAL
# define REPLAY_TEAM_TEAM0 TF_TEAM_RED
# define REPLAY_TEAM_TEAM1 TF_TEAM_BLUE
# define REPLAY_CLASS_UNDEFINED TF_CLASS_UNDEFINED
# define REPLAY_NUM_CLASSES TF_CLASS_MENU_BUTTONS
# define REPLAY_DEATH_DOMINATION TF_DEATH_DOMINATION
# define REPLAY_DEATH_ASSISTER_DOMINATION TF_DEATH_ASSISTER_DOMINATION
# define REPLAY_DEATH_REVENGE TF_DEATH_REVENGE
# define REPLAY_DEATH_ASSISTER_REVENGE TF_DEATH_ASSISTER_REVENGE
# define C_ReplayGame_PlayerResource
# include "c_tf_playerresource.h"
typedef C_TF_PlayerResource C_ReplayGame_PlayerResource_t;
# define ReplayStatType_t TFStatType_t
# define REPLAY_MAX_DISPLAY_GAMESTATS 23
#elif defined( CSTRIKE_DLL )
# include "cs_gamestats_shared.h"
# define REPLAY_GAMESTATS_UNDEFINED CSSTAT_UNDEFINED
# define REPLAY_GAMESTATS_REVENGE CSSTAT_REVENGES
# define REPLAY_GAMESTATS_DEATHS CSSTAT_DEATHS
# define REPLAY_GAMESTATS_DOMINATIONS CSSTAT_DOMINATIONS
# define REPLAY_GAMESTATS_POINTSSCORED CSSTAT_UNDEFINED // Sheeeeeeeeeit
# define REPLAY_GAMESTATS_MAX CSSTAT_MAX
# define REPLAY_TEAM_TEAM0 TEAM_TERRORIST
# define REPLAY_TEAM_TEAM1 TEAM_CT
# define REPLAY_CLASS_UNDEFINED CS_CLASS_NONE
# define REPLAY_NUM_CLASSES CS_NUM_CLASSES
# define REPLAY_DEATH_DOMINATION CS_DEATH_DOMINATION
# define REPLAY_DEATH_REVENGE CS_DEATH_REVENGE
# include "c_cs_playerresource.h"
typedef C_CS_PlayerResource C_ReplayGame_PlayerResource_t;
# define ReplayStatType_t CSStatType_t
# define RoundStats_t StatsCollection_t
# define REPLAY_MAX_DISPLAY_GAMESTATS 3
#endif
struct StatInfo_t
{
ReplayStatType_t m_nStat;
const char *m_pStatLocalizationToken;
};
extern StatInfo_t g_pReplayDisplayGameStats[REPLAY_MAX_DISPLAY_GAMESTATS];
#endif // GAMEDEFS_H
@@ -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
@@ -0,0 +1,178 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//=======================================================================================//
#if defined( REPLAY_ENABLED )
#ifndef GENERICCLASSBASED_REPLAY_H
#define GENERICCLASSBASED_REPLAY_H
#ifdef _WIN32
#pragma once
#endif
//----------------------------------------------------------------------------------------
#include "replay/replay.h"
#include "replay/iclientreplaycontext.h"
#include "GameEventListener.h"
// For RoundStats_t
#include "replay/gamedefs.h"
//----------------------------------------------------------------------------------------
extern IClientReplayContext *g_pClientReplayContext;
//----------------------------------------------------------------------------------------
class CGenericClassBasedReplay : public CReplay,
public CGameEventListener
{
typedef CReplay BaseClass;
public:
CGenericClassBasedReplay();
~CGenericClassBasedReplay();
virtual void OnBeginRecording();
virtual void OnEndRecording();
virtual void OnComplete();
virtual bool ShouldAllowDelete() const;
virtual void OnDelete();
virtual void FireGameEvent( IGameEvent *pEvent );
virtual bool Read( KeyValues *pIn );
virtual void Write( KeyValues *pOut );
virtual void DumpGameSpecificData() const;
void SetPlayerClass( int nPlayerClass );
void SetPlayerTeam( int nPlayerTeam );
void RecordPlayerDeath( const char *pKillerName, int nKillerClass );
// Add a new kill to the list
void AddKill( const char *pPlayerName, int nPlayerClass );
// Get the player class as a string
virtual const char *GetPlayerClass() const;
// Get the player team as a string
virtual const char *GetPlayerTeam() const = 0;
// Utility to get the material-friendly player class (demoman->demo, heavyweapons->heavy)
virtual const char *GetMaterialFriendlyPlayerClass() const;
// Was there a killer?
inline bool WasKilled() const { return m_szKillerName[0] != 0; }
// Get killer name
const char *GetKillerName() const;
// Get the killer class, if there was a killer
const char *GetKillerClass() const;
int GetDownloadStatus() const;
// Kill info
struct KillData_t
{
char m_szPlayerName[MAX_OSPATH];
int m_nPlayerClass;
};
inline int GetKillCount() const { return m_vecKills.Count(); }
inline const KillData_t *GetKill( int nKillIndex ) { return m_vecKills[ nKillIndex ]; }
// A generic info struct used for dominations, assisted dominations, revenges, assisted revenged...
// Not all data members are necessarily used
struct GenericStatInfo_t
{
GenericStatInfo_t() : m_nVictimFriendId( 0 ), m_nAssisterFriendId( 0 ) {}
uint32 m_nVictimFriendId;
uint32 m_nAssisterFriendId;
};
inline int GetDominationCount() const { return m_vecDominations.Count(); }
inline const GenericStatInfo_t *GetDomination( int nIndex ) const { return m_vecDominations[ nIndex ]; }
inline int GetAssisterDominationCount() const { return m_vecAssisterDominations.Count(); }
inline const GenericStatInfo_t *GetAssisterDomination( int nIndex ) const { return m_vecAssisterDominations[ nIndex ]; }
inline int GetRevengeCount() const { return m_vecRevenges.Count(); }
inline const GenericStatInfo_t *GetRevenge( int nIndex ) const { return m_vecRevenges[ nIndex ]; }
inline int GetAssisterRevengeCount() const { return m_vecAssisterRevenges.Count(); }
inline const GenericStatInfo_t *GetAssisterRevenge( int nIndex ) const { return m_vecAssisterRevenges[ nIndex ]; }
RoundStats_t const &GetStats() const { return m_lifeStats; }
protected:
int m_nPlayerClass;
int m_nPlayerTeam;
int m_nStatUndefined;
char m_szKillerName[ MAX_OSPATH ];
int m_nKillerClass;
virtual bool IsValidClass( int nClass ) const = 0;
virtual bool IsValidTeam( int iTeam ) const = 0;
virtual bool GetCurrentStats( RoundStats_t &out ) = 0;
virtual const char *GetStatString( int iStat ) const = 0;
virtual const char *GetPlayerClass( int iClass ) const = 0;
virtual void Update();
// Domination
void AddDomination( int nVictimID );
void AddAssisterDomination( int nVictimID, int nAssiterID );
void AddRevenge( int nVictimID );
void AddAssisterRevenge( int nVictimID, int nAssiterID );
float GetKillScreenshotDelay();
RoundStats_t m_refStats; // Reference stats, used to compute current stats
RoundStats_t m_lifeStats; // Stats for this life, based on reference stats (m_refStats)
private:
void MedicUpdate();
bool GetFriendIdFromUserId( int nPlayerIndex, uint32 &nFriendIdOut ) const; // Get a friend ID based on player index. Returns true on success
void AddKillStatFromUserIds( CUtlVector< GenericStatInfo_t * > &vec, int nVictimId, int nAssisterId = 0 );
void AddKillStatFromFriendIds( CUtlVector< GenericStatInfo_t * > &vec, uint32 nVictimFriendId, uint32 nAssisterFriendId = 0 );
void WriteKillStatVector( CUtlVector< GenericStatInfo_t * > const &vec, const char *pSubKeyName, const char *pElementKeyName,
KeyValues *pRootKey, int nNumMembersToWrite ) const;
void AddKillStats( CUtlVector< GenericStatInfo_t * > &vecKillStats, KeyValues *pIn, const char *pSubKeyName, int iStatIndex );
void RecordUpdatedStats();
CUtlVector< KillData_t * > m_vecKills;
CUtlVector< GenericStatInfo_t * > m_vecDominations;
CUtlVector< GenericStatInfo_t * > m_vecAssisterDominations;
CUtlVector< GenericStatInfo_t * > m_vecRevenges;
CUtlVector< GenericStatInfo_t * > m_vecAssisterRevenges;
// TODO... dominations, achievements, etc.
};
//----------------------------------------------------------------------------------------
inline CGenericClassBasedReplay *ToGenericClassBasedReplay( CReplay *pClientReplay )
{
return static_cast< CGenericClassBasedReplay * >( pClientReplay );
}
inline const CGenericClassBasedReplay *ToGenericClassBasedReplay( const CReplay *pClientReplay )
{
return static_cast< const CGenericClassBasedReplay * >( pClientReplay );
}
inline CGenericClassBasedReplay *GetGenericClassBasedReplay( ReplayHandle_t hReplay )
{
return ToGenericClassBasedReplay( g_pClientReplayContext->GetReplay( hReplay ) );
}
//----------------------------------------------------------------------------------------
#endif // GENERICCLASSBASED_REPLAY_H
#endif
+750
View File
@@ -0,0 +1,750 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// TODO:
// - Use a mempool
// - Need to be able to gracefully turn replay ragdolls on/off
//
//----------------------------------------------------------------------------------------
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replay_ragdoll.h"
#include "tier1/mempool.h"
#include "debugoverlay_shared.h"
#include "filesystem.h"
//--------------------------------------------------------------------------------
static matrix3x4_t gs_BoneCache[ MAXSTUDIOBONES ];
static int gs_nBytesAllocated = 0;
//--------------------------------------------------------------------------------
void OnReplayCacheClientRagdollsCvarChange( IConVar *pVar, const char *pOldValue, float flOldValue )
{
// TODO: need to be able to gracefully turn replay ragdolls on/off
}
//--------------------------------------------------------------------------------
static ConVar replay_ragdoll_dbg( "replay_ragdoll_dbg", "0", FCVAR_CLIENTDLL, "Display replay ragdoll debugging information." );
static ConVar replay_cache_client_ragdolls( "replay_cache_client_ragdolls", "0", FCVAR_CLIENTDLL, "Record ragdolls on the client during.", OnReplayCacheClientRagdollsCvarChange );
//--------------------------------------------------------------------------------
void DrawBones( matrix3x4_t const* pBones, int nNumBones, ragdoll_t const* pRagdoll,
int nRed, int nGreen, int nBlue, C_BaseAnimating* pBaseAnimating )
{
Assert( pBones );
Assert( pRagdoll );
Assert( pBaseAnimating );
Vector from, to;
for ( int i = 0; i < nNumBones; ++i )
{
// debugoverlay->AddCoordFrameOverlay( pBones[ i ], 3.0f );
int const iRagdollParentIndex = pRagdoll->list[ i ].parentIndex;
if ( iRagdollParentIndex < 0 )
continue;
int iBoneIndex = pRagdoll->boneIndex[ i ];
int iParentIndex = pRagdoll->boneIndex[ iRagdollParentIndex ];
MatrixPosition( pBones[ iParentIndex ], from );
MatrixPosition( pBones[ iBoneIndex ], to );
if ( debugoverlay )
{
debugoverlay->AddLineOverlay( from, to, nRed, nGreen, nBlue, true, 0.0f );
}
}
}
//--------------------------------------------------------------------------------
inline int GetServerTickCount()
{
int nTick = TIME_TO_TICKS( engine->GetLastTimeStamp() );
return nTick;
}
//--------------------------------------------------------------------------------
/*static*/ RagdollSimulationFrame_t* RagdollSimulationFrame_t::Alloc( int nNumBones )
{
// TODO: use a mempool
RagdollSimulationFrame_t* pNew = new RagdollSimulationFrame_t();
pNew->pPositions = new Vector[ nNumBones ];
pNew->pAngles = new QAngle[ nNumBones ];
gs_nBytesAllocated += sizeof( pNew ) + nNumBones * ( sizeof( Vector ) + sizeof( QAngle ) );
return pNew;
}
//--------------------------------------------------------------------------------
RagdollSimulationData_t::RagdollSimulationData_t( C_BaseAnimating* pEntity, int nStartTick, int nNumBones )
: m_pEntity( pEntity ),
m_nEntityIndex( -1 ),
m_nStartTick( nStartTick ),
m_nNumBones( nNumBones ),
m_nDuration( -1 )
{
if ( pEntity )
{
m_nEntityIndex = pEntity->entindex();
}
Assert( nNumBones >= 0 && nNumBones < MAXSTUDIOBONES );
}
bool _ComputeRagdollBones( const ragdoll_t *pRagdoll, matrix3x4_t &parentTransform, matrix3x4_t *pBones, Vector *pPositions, QAngle *pAngles )
{
matrix3x4_t inverted, output;
#ifdef _DEBUG
CBitVec<MAXSTUDIOBONES> vBonesComputed;
vBonesComputed.ClearAll();
#endif
for ( int i = 0; i < pRagdoll->listCount; ++i )
{
const ragdollelement_t& element = pRagdoll->list[ i ];
// during restore if a model has changed since the file was saved, this could be NULL
if ( !element.pObject )
return false;
int const boneIndex = pRagdoll->boneIndex[ i ];
if ( boneIndex < 0 )
{
AssertMsg( 0, "Replay: No mapping for ragdoll bone\n" );
return false;
}
// Get global transform and put it into the bone cache
element.pObject->GetPositionMatrix( &pBones[ boneIndex ] );
// Ensure a fixed translation from the parent (no stretching)
if ( element.parentIndex >= 0 && !pRagdoll->allowStretch )
{
int parentIndex = pRagdoll->boneIndex[ element.parentIndex ];
#ifdef _DEBUG
// Make sure we computed the parent already
Assert( vBonesComputed.IsBitSet(parentIndex) );
#endif
// overwrite the position from physics to force rigid attachment
// NOTE: On the client we actually override this with the proper parent bone in each LOD
Vector out;
VectorTransform( element.originParentSpace, pBones[ parentIndex ], out );
MatrixSetColumn( out, 3, pBones[ boneIndex ] );
MatrixInvert( pBones[ parentIndex ], inverted );
}
else if ( element.parentIndex == - 1 )
{
// Decompose into parent space
MatrixInvert( parentTransform, inverted );
}
#ifdef _DEBUG
vBonesComputed.Set( boneIndex, true );
#endif
// Compute local transform and put into 'output'
ConcatTransforms( inverted, pBones[ boneIndex ], output );
// Cache as Euler/position
MatrixAngles( output, pAngles[ i ], pPositions[ i ] );
}
return true;
}
void RagdollSimulationData_t::Record()
{
Assert( m_pEntity->m_pRagdoll );
// Allocate a frame
RagdollSimulationFrame_t* pNewFrame = RagdollSimulationFrame_t::Alloc( m_nNumBones );
if ( !pNewFrame )
return;
// Set the current tick
pNewFrame->nTick = GetServerTickCount();
// Add new frame to list of frames
m_lstFrames.AddToTail( pNewFrame );
// Compute parent transform
matrix3x4_t parentTransform;
Vector vRootPosition = m_pEntity->GetRenderOrigin();
QAngle angRootAngles = m_pEntity->GetRenderAngles();
AngleMatrix( angRootAngles, vRootPosition, parentTransform );
// debugoverlay->AddCoordFrameOverlay( parentTransform, 100 );
// Cache off root position/orientation
pNewFrame->vRootPosition = vRootPosition;
pNewFrame->angRootAngles = angRootAngles;
// Compute actual ragdoll bones
matrix3x4_t* pBones = gs_BoneCache;
_ComputeRagdollBones( m_pEntity->m_pRagdoll->GetRagdoll(), parentTransform, pBones, pNewFrame->pPositions, pNewFrame->pAngles );
// Draw bones
if ( replay_ragdoll_dbg.GetBool() )
{
DrawBones( pBones, m_pEntity->m_pRagdoll->RagdollBoneCount(), m_pEntity->m_pRagdoll->GetRagdoll(), 255, 0, 0, m_pEntity );
}
}
//--------------------------------------------------------------------------------
CReplayRagdollRecorder::CReplayRagdollRecorder()
: m_bIsRecording(false)
{
}
CReplayRagdollRecorder::~CReplayRagdollRecorder()
{
}
/*static*/ CReplayRagdollRecorder& CReplayRagdollRecorder::Instance()
{
static CReplayRagdollRecorder s_instance;
return s_instance;
}
void CReplayRagdollRecorder::Init()
{
Assert( !m_bIsRecording );
m_bIsRecording = true;
gs_nBytesAllocated = 0;
}
void CReplayRagdollRecorder::Shutdown()
{
if ( !m_bIsRecording )
return;
m_lstRagdolls.PurgeAndDeleteElements();
gs_nBytesAllocated = 0;
// RemoveAll() purges, and there is no UnlinkAll() - is there an easier way to do this?
Iterator_t i = m_lstRagdollsToRecord.Head();
while ( i != m_lstRagdollsToRecord.InvalidIndex() )
{
m_lstRagdollsToRecord.Unlink( i );
i = m_lstRagdollsToRecord.Head();
}
Assert( m_bIsRecording );
m_bIsRecording = false;
}
void CReplayRagdollRecorder::AddEntry( C_BaseAnimating* pEntity, int nStartTick, int nNumBones )
{
DevMsg( "Replay: Processing Ragdoll at time %d\n", nStartTick );
Assert( pEntity );
RagdollSimulationData_t* pNewEntry = new RagdollSimulationData_t( pEntity, nStartTick, nNumBones );
gs_nBytesAllocated += sizeof( RagdollSimulationData_t );
m_lstRagdolls.AddToTail( pNewEntry );
// Also add to list of ragdolls to record
m_lstRagdollsToRecord.AddToTail( pNewEntry );
}
void CReplayRagdollRecorder::StopRecordingRagdoll( C_BaseAnimating* pEntity )
{
Assert( pEntity );
// Find the entry in the recording list
Iterator_t nIndex;
if ( !FindEntryInRecordingList( pEntity, nIndex ) )
return;
StopRecordingRagdollAtIndex( nIndex );
}
void CReplayRagdollRecorder::StopRecordingRagdollAtIndex( Iterator_t nIndex )
{
// No longer recording - compute duration
RagdollSimulationData_t* pData = m_lstRagdollsToRecord[ nIndex ];
// Does duration need to be set?
if ( pData->m_nDuration < 0 )
{
pData->m_nDuration = GetServerTickCount() - pData->m_nStartTick; Assert( pData->m_nDuration > 0 );
}
// Remove it from the recording list
m_lstRagdollsToRecord.Unlink( nIndex );
}
void CReplayRagdollRecorder::StopRecordingSleepingRagdolls()
{
Iterator_t i = m_lstRagdollsToRecord.Head();
while ( i != m_lstRagdollsToRecord.InvalidIndex() )
{
if ( RagdollIsAsleep( *m_lstRagdollsToRecord[ i ]->m_pEntity->m_pRagdoll->GetRagdoll() ) )
{
DevMsg( "entity %d: Removing sleeping ragdoll\n", m_lstRagdollsToRecord[ i ]->m_nEntityIndex );
StopRecordingRagdollAtIndex( i );
i = m_lstRagdollsToRecord.Head();
}
else
{
i = m_lstRagdollsToRecord.Next( i );
}
}
}
bool CReplayRagdollRecorder::FindEntryInRecordingList( C_BaseAnimating* pEntity,
CReplayRagdollRecorder::Iterator_t& nOutIndex )
{
// Find the entry
FOR_EACH_LL( m_lstRagdollsToRecord, i )
{
if ( m_lstRagdollsToRecord[ i ]->m_pEntity == pEntity )
{
nOutIndex = i;
return true;
}
}
nOutIndex = m_lstRagdollsToRecord.InvalidIndex();
return false;
}
void CReplayRagdollRecorder::Record()
{
static ConVar* pReplayEnable = NULL;
static bool bLookedForConvar = false;
if ( bLookedForConvar )
{
pReplayEnable = (ConVar*)cvar->FindVar( "replay_enable" );
bLookedForConvar = true;
}
if ( !pReplayEnable || !pReplayEnable->GetInt() )
return;
if ( !replay_cache_client_ragdolls.GetInt() )
return;
FOR_EACH_LL( m_lstRagdollsToRecord, i )
{
Assert( m_lstRagdollsToRecord[ i ]->m_pEntity->IsRagdoll() );
m_lstRagdollsToRecord[ i ]->Record();
}
}
void CReplayRagdollRecorder::Think()
{
if ( !IsRecording() )
return;
StopRecordingSleepingRagdolls();
Record();
PrintDebug();
}
void CReplayRagdollRecorder::PrintDebug()
{
if ( !replay_ragdoll_dbg.GetInt() )
return;
int nLine = 0;
// Print memory usage
engine->Con_NPrintf( nLine++, "ragdolls: %.2f MB", gs_nBytesAllocated / 1048576.0f );
// Print server time
engine->Con_NPrintf( nLine++, "server time: %d", GetServerTickCount() );
++nLine; // Blank line
// Print info about each ragdoll
FOR_EACH_LL( m_lstRagdolls, i )
{
engine->Con_NPrintf( nLine++, "entity %d: start time=%d duration=%d num bones=%d", m_lstRagdolls[i]->m_nEntityIndex, m_lstRagdolls[i]->m_nStartTick, m_lstRagdolls[i]->m_nDuration, m_lstRagdolls[i]->m_nNumBones );
}
}
void CReplayRagdollRecorder::CleanupStartupTicksAndDurations( int nStartTick )
{
FOR_EACH_LL( m_lstRagdolls, i )
{
RagdollSimulationData_t* pRagdollData = m_lstRagdolls[ i ];
// Offset start tick with start tick, sent over from server
pRagdollData->m_nStartTick -= nStartTick; Assert( pRagdollData->m_nStartTick >= 0 );
// Setup duration
pRagdollData->m_nDuration = GetServerTickCount() - nStartTick; Assert( pRagdollData->m_nDuration > 0 );
// Go through all frames and subtract the start tick
FOR_EACH_LL( pRagdollData->m_lstFrames, j )
{
pRagdollData->m_lstFrames[ j ]->nTick -= nStartTick;
}
}
}
BEGIN_DMXELEMENT_UNPACK( RagdollSimulationData_t )
DMXELEMENT_UNPACK_FIELD( "nEntityIndex", "0", int, m_nEntityIndex )
DMXELEMENT_UNPACK_FIELD( "nStartTick", "0", int, m_nStartTick )
DMXELEMENT_UNPACK_FIELD( "nDuration", "0", int, m_nDuration )
DMXELEMENT_UNPACK_FIELD( "nNumBones", "0", int, m_nNumBones )
END_DMXELEMENT_UNPACK( RagdollSimulationData_t, s_RagdollSimulationDataUnpack )
bool CReplayRagdollRecorder::DumpRagdollsToDisk( char const* pFilename ) const
{
MEM_ALLOC_CREDIT();
DECLARE_DMX_CONTEXT();
CDmxElement* pSimulations = CreateDmxElement( "Simulations" );
CDmxElementModifyScope modify( pSimulations );
int const nNumRagdolls = m_lstRagdolls.Count();
pSimulations->SetValue( "iNumRagdolls", nNumRagdolls );
CDmxAttribute* pRagdolls = pSimulations->AddAttribute( "ragdolls" );
CUtlVector< CDmxElement* >& ragdolls = pRagdolls->GetArrayForEdit< CDmxElement* >();
modify.Release();
char name[32];
FOR_EACH_LL( m_lstRagdolls, i )
{
RagdollSimulationData_t const* pData = m_lstRagdolls[ i ];
// Make sure we've setup all durations properly
Assert( pData->m_nDuration >= 0 );
CDmxElement* pRagdoll = CreateDmxElement( "ragdoll" );
ragdolls.AddToTail( pRagdoll );
V_snprintf( name, sizeof(name), "ragdoll %d", i );
pRagdoll->SetValue( "name", name );
CDmxElementModifyScope modifyClass( pRagdoll );
pRagdoll->AddAttributesFromStructure( pData, s_RagdollSimulationDataUnpack );
CDmxAttribute* pFrames = pRagdoll->AddAttribute( "frames" );
CUtlVector< CDmxElement* >& frames = pFrames->GetArrayForEdit< CDmxElement* >();
FOR_EACH_LL( pData->m_lstFrames, j )
{
CDmxElement* pFrame = CreateDmxElement( "frame" );
frames.AddToTail( pFrame );
V_snprintf( name, sizeof(name), "frame %d", j );
pFrame->SetValue( "name", name );
// Store tick
pFrame->SetValue( "tick", pData->m_lstFrames[ j ]->nTick );
// Store root pos/orientation
pFrame->SetValue( "root_pos" , pData->m_lstFrames[ j ]->vRootPosition );
pFrame->SetValue( "root_angles", pData->m_lstFrames[ j ]->angRootAngles );
for ( int k = 0; k < pData->m_nNumBones; ++k )
{
CDmxAttribute* pPositions = pFrame->AddAttribute( "positions" );
CUtlVector< Vector >& positions = pPositions->GetArrayForEdit< Vector >();
CDmxAttribute* pAngles = pFrame->AddAttribute( "angles" );
CUtlVector< QAngle >& angles = pAngles->GetArrayForEdit< QAngle >();
positions.AddToTail( pData->m_lstFrames[ j ]->pPositions[ k ] );
angles.AddToTail( pData->m_lstFrames[ j ]->pAngles[ k ] );
}
}
}
{
MEM_ALLOC_CREDIT();
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
if ( !SerializeDMX( buf, pSimulations, pFilename ) )
{
Warning( "Replay: Failed to write ragdoll cache, %s.\n", pFilename );
return false;
}
// Write the file
filesystem->WriteFile( pFilename, "MOD", buf );
}
CleanupDMX( pSimulations );
Msg( "Replay: Cached ragdoll data.\n" );
return true;
}
//--------------------------------------------------------------------------------
CReplayRagdollCache::CReplayRagdollCache()
: m_bInit( false )
{
}
/*static*/ CReplayRagdollCache& CReplayRagdollCache::Instance()
{
static CReplayRagdollCache s_instance;
return s_instance;
}
bool CReplayRagdollCache::Init( char const* pFilename )
{
Assert( !m_bInit );
// Make sure valid filename
if ( !pFilename || pFilename[0] == 0 )
return false;
DECLARE_DMX_CONTEXT();
// Attempt to read from disk
CDmxElement* pRagdolls = NULL;
if ( !UnserializeDMX( pFilename, "MOD", true, &pRagdolls ) )
// if ( !UnserializeDMX( pFilename, "GAME", false, &pRagdolls ) )
return false;
CUtlVector< CDmxElement* > const& ragdolls = pRagdolls->GetArray< CDmxElement* >( "ragdolls" );
for ( int i = 0; i < ragdolls.Count(); ++i )
{
CDmxElement* pCurRagdollInput = ragdolls[ i ];
// Create a new ragdoll entry and add to list
RagdollSimulationData_t* pNewSimData = new RagdollSimulationData_t();
m_lstRagdolls.AddToTail( pNewSimData );
// Read
pCurRagdollInput->UnpackIntoStructure( pNewSimData, sizeof( *pNewSimData ), s_RagdollSimulationDataUnpack );
// NOTE: Entity ptr doesn't get linked up here because it doesn't necessarily exist at this point
// Read frames
CUtlVector< CDmxElement* > const& frames = pCurRagdollInput->GetArray< CDmxElement* >( "frames" );
for ( int j = 0; j < frames.Count(); ++j )
{
CDmxElement* pCurFrameInput = frames[ j ];
// Create a new frame and add it to list of frames
RagdollSimulationFrame_t* pNewFrame = RagdollSimulationFrame_t::Alloc( pNewSimData->m_nNumBones );
pNewSimData->m_lstFrames.AddToTail( pNewFrame );
// Read tick
pNewFrame->nTick = pCurFrameInput->GetValue( "tick", -1 ); Assert( pNewFrame->nTick != -1 );
// Read root pos/orientation
pNewFrame->vRootPosition = pCurFrameInput->GetValue( "root_pos" , vec3_origin );
pNewFrame->angRootAngles = pCurFrameInput->GetValue( "root_angles", vec3_angle );
CUtlVector< Vector > const& positions = pCurFrameInput->GetArray< Vector >( "positions" );
CUtlVector< QAngle > const& angles = pCurFrameInput->GetArray< QAngle >( "angles" );
for ( int k = 0; k < pNewSimData->m_nNumBones; ++k )
{
pNewFrame->pPositions[ k ] = positions[ k ];
pNewFrame->pAngles[ k ] = angles[ k ];
}
}
}
// Cleanup
CleanupDMX( pRagdolls );
m_bInit = true;
return true;
}
void CReplayRagdollCache::Shutdown()
{
if ( !m_bInit )
return;
m_lstRagdolls.PurgeAndDeleteElements();
m_bInit = false;
}
ConVar replay_ragdoll_blending( "replay_ragdoll_blending", "1", FCVAR_DEVELOPMENTONLY );
ConVar replay_ragdoll_tickoffset( "replay_ragdoll_tickoffset", "0", FCVAR_DEVELOPMENTONLY );
bool CReplayRagdollCache::GetFrame( C_BaseAnimating* pEntity, int nTick, bool* pBoneSimulated, CBoneAccessor* pBoneAccessor ) const
{
nTick += replay_ragdoll_tickoffset.GetInt();
Assert( pEntity );
Assert( pBoneSimulated );
Assert( pEntity->m_pRagdoll );
// Find ragdoll for the given entity - will return NULL if nTick is out of the entry's time window
const RagdollSimulationData_t* pRagdollEntry = FindRagdollEntry( pEntity, nTick );
if ( !pRagdollEntry )
return false;
// Find frame for the given tick
RagdollSimulationFrame_t* pFrame;
RagdollSimulationFrame_t* pNextFrame;
if ( !FindFrame( pFrame, pNextFrame, pRagdollEntry, nTick ) )
return false;
// Compute root transform
matrix3x4_t rootTransform;
float flInterpAmount = gpGlobals->interpolation_amount;
if ( pNextFrame )
{
AngleMatrix(
(const QAngle &)Lerp( flInterpAmount, pFrame->angRootAngles, pNextFrame->angRootAngles ), // Actually does a slerp
Lerp( flInterpAmount, pFrame->vRootPosition, pNextFrame->vRootPosition ),
rootTransform
);
}
else
{
AngleMatrix( pFrame->angRootAngles, pFrame->vRootPosition, rootTransform );
}
// Compute each bone
ragdoll_t* pRagdoll = pEntity->m_pRagdoll->GetRagdoll(); Assert( pRagdoll );
for ( int k = 0; k < pRagdoll->listCount; ++k )
{
int objectIndex = k;
const ragdollelement_t& element = pRagdoll->list[ objectIndex ];
int const boneIndex = pRagdoll->boneIndex[ objectIndex ]; Assert( boneIndex >= 0 );
// Compute blended transform if possible
matrix3x4_t localTransform;
if ( pNextFrame && replay_ragdoll_blending.GetInt() )
{
// Get blended Eular angles - NOTE: The Lerp() here actually calls Lerp<QAngle>() which converts to quats and back
flInterpAmount = gpGlobals->interpolation_amount; Assert( flInterpAmount >= 0.0f && flInterpAmount <= 1.0f );
AngleMatrix(
(const QAngle &)Lerp( flInterpAmount, pFrame->pAngles [ objectIndex ], pNextFrame->pAngles [ objectIndex ] ),
Lerp( flInterpAmount, pFrame->pPositions[ objectIndex ], pNextFrame->pPositions[ objectIndex ] ),
localTransform
);
}
else
{
// Last frame
AngleMatrix( pFrame->pAngles[ objectIndex ], pFrame->pPositions[ objectIndex ], localTransform );
}
matrix3x4_t& boneMatrix = pBoneAccessor->GetBoneForWrite( boneIndex );
if ( element.parentIndex < 0 )
{
ConcatTransforms( rootTransform, localTransform, boneMatrix );
}
else
{
int parentBoneIndex = pRagdoll->boneIndex[ element.parentIndex ]; Assert( parentBoneIndex >= 0 );
Assert( pBoneSimulated[ parentBoneIndex ] );
matrix3x4_t const& parentMatrix = pBoneAccessor->GetBone( parentBoneIndex );
ConcatTransforms( parentMatrix, localTransform, boneMatrix );
}
// Simulated this bone
pBoneSimulated[ boneIndex ] = true;
}
if ( replay_ragdoll_dbg.GetBool() )
{
DrawBones( pBoneAccessor->GetBoneArrayForWrite(), pRagdollEntry->m_nNumBones, pRagdoll, 0, 0, 255, pEntity );
}
return true;
}
RagdollSimulationData_t* CReplayRagdollCache::FindRagdollEntry( C_BaseAnimating* pEntity, int nTick )
{
Assert( pEntity );
int const nEntIndex = pEntity->entindex();
FOR_EACH_LL( m_lstRagdolls, i )
{
RagdollSimulationData_t* pRagdollData = m_lstRagdolls[ i ];
// If not the right entity or the tick is out range, continue.
if ( pRagdollData->m_nEntityIndex != nEntIndex )
continue;
// We've got the ragdoll, but only return it if nTick is in the window
if ( nTick < pRagdollData->m_nStartTick ||
nTick > pRagdollData->m_nStartTick + pRagdollData->m_nDuration )
return NULL;
return pRagdollData;
}
return NULL;
}
bool CReplayRagdollCache::FindFrame( RagdollSimulationFrame_t*& pFrameOut, RagdollSimulationFrame_t*& pNextFrameOut,
const RagdollSimulationData_t* pRagdollEntry, int nTick )
{
// Look for the appropriate frame
FOR_EACH_LL( pRagdollEntry->m_lstFrames, j )
{
RagdollSimulationFrame_t* pFrame = pRagdollEntry->m_lstFrames[ j ];
// Get next frame if possible
int const nNext = pRagdollEntry->m_lstFrames.Next( j );
RagdollSimulationFrame_t* pNextFrame =
nNext == pRagdollEntry->m_lstFrames.InvalidIndex() ? NULL : pRagdollEntry->m_lstFrames[ nNext ];
// Use this frame?
if ( nTick >= pFrame->nTick &&
( (pNextFrame && nTick <= pNextFrame->nTick) || !pNextFrame ) ) // Use the last frame if the tick is past the range of frames -
{ // this is the "sleeping" ragdoll frame
pFrameOut = pFrame;
pNextFrameOut = pNextFrame;
return true;
}
}
pFrameOut = NULL;
pNextFrameOut = NULL;
return false;
}
void CReplayRagdollCache::Think()
{
// TODO: Add IsPlayingReplayDemo() to engine interface
/*
engine->Con_NPrintf( 8, "time: %d", engine->GetDemoPlaybackTick() );
FOR_EACH_LL( m_lstRagdolls, i )
{
engine->Con_NPrintf( 10 + i, "entity %d: start time=%d duration=%d num bones=%d", m_lstRagdolls[i]->m_nEntityIndex, m_lstRagdolls[i]->m_nStartTick, m_lstRagdolls[i]->m_nDuration, m_lstRagdolls[i]->m_nNumBones );
}
*/
}
//--------------------------------------------------------------------------------
bool Replay_CacheRagdolls( const char* pFilename, int nStartTick )
{
CReplayRagdollRecorder::Instance().CleanupStartupTicksAndDurations( nStartTick );
return CReplayRagdollRecorder::Instance().DumpRagdollsToDisk( pFilename );
}
#endif
+150
View File
@@ -0,0 +1,150 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//----------------------------------------------------------------------------------------
#ifndef REPLAY_RAGDOLL_H
#define REPLAY_RAGDOLL_H
#ifdef _WIN32
#pragma once
#endif
//--------------------------------------------------------------------------------
class C_BaseAnimating;
//--------------------------------------------------------------------------------
struct RagdollSimulationFrame_t
{
RagdollSimulationFrame_t() : pPositions(NULL), pAngles(NULL), nTick(-1) {}
static RagdollSimulationFrame_t* Alloc( int nNumBones );
int nTick;
Vector* pPositions;
QAngle* pAngles;
Vector vRootPosition;
QAngle angRootAngles;
};
//--------------------------------------------------------------------------------
struct RagdollSimulationData_t
{
RagdollSimulationData_t( C_BaseAnimating* pEntity = NULL, int nStartTick = 0, int nNumBones = 0 );
void Record();
int m_nEntityIndex;
int m_nStartTick;
int m_nDuration;
int m_nNumBones;
typedef unsigned short Iterator_t;
CUtlLinkedList< RagdollSimulationFrame_t*, Iterator_t > m_lstFrames;
C_BaseAnimating* m_pEntity;
};
//--------------------------------------------------------------------------------
// TODO: Refactor this into an interface and hide implementation in cpp file
class CReplayRagdollRecorder
{
private:
CReplayRagdollRecorder();
~CReplayRagdollRecorder();
public:
static CReplayRagdollRecorder& Instance();
void Init();
void Shutdown();
void Think();
void AddEntry( C_BaseAnimating* pEntity, int nStartTick, int nNumBones );
void StopRecordingRagdoll( C_BaseAnimating* pEntity );
void CleanupStartupTicksAndDurations( int nStartTick );
bool DumpRagdollsToDisk( char const* pszFilename ) const;
bool IsRecording() const { return m_bIsRecording; }
private:
typedef unsigned short Iterator_t;
void StopRecordingRagdollAtIndex( Iterator_t nIndex );
void StopRecordingSleepingRagdolls();
void Record();
bool FindEntryInRecordingList( C_BaseAnimating* pEntity, Iterator_t& nOutIndex );
void PrintDebug();
CUtlLinkedList< RagdollSimulationData_t*, Iterator_t > m_lstRagdolls;
CUtlLinkedList< RagdollSimulationData_t*, Iterator_t > m_lstRagdollsToRecord; // Contains some of the elements from m_lstRagdolls -
// the ones which are still recording
bool m_bIsRecording;
};
//--------------------------------------------------------------------------------
class CReplayRagdollCache
{
private:
CReplayRagdollCache();
public:
static CReplayRagdollCache& Instance();
bool Init( char const* pszFilename );
void Shutdown();
void Think();
bool IsInitialized() const { return m_bInit; }
//
// Returns false is no frame exists for the given entity at the given tick.
// Otherwise, returns a
//
bool GetFrame( C_BaseAnimating* pEntity, int nTick, bool* pBoneSimulated, CBoneAccessor* pBoneAccessor ) const;
private:
RagdollSimulationData_t* FindRagdollEntry( C_BaseAnimating* pEntity, int nTick );
const RagdollSimulationData_t* FindRagdollEntry( C_BaseAnimating* pEntity, int nTick ) const;
bool FindFrame( RagdollSimulationFrame_t*& pFrameOut, RagdollSimulationFrame_t*& pNextFrameOut,
const RagdollSimulationData_t* pRagdollEntry, int nTick );
bool FindFrame( RagdollSimulationFrame_t*& pFrameOut, RagdollSimulationFrame_t*& pNextFrameOut,
const RagdollSimulationData_t* pRagdollEntry, int nTick ) const;
typedef unsigned short Iterator_t;
bool m_bInit;
CUtlLinkedList< RagdollSimulationData_t*, Iterator_t > m_lstRagdolls;
};
//--------------------------------------------------------------------------------
bool Replay_CacheRagdolls( const char* pFilename, int nStartTick );
//--------------------------------------------------------------------------------
inline const RagdollSimulationData_t* CReplayRagdollCache::FindRagdollEntry( C_BaseAnimating* pEntity, int nTick ) const
{
return const_cast< CReplayRagdollCache* >( this )->FindRagdollEntry( pEntity, nTick );
}
inline bool CReplayRagdollCache::FindFrame( RagdollSimulationFrame_t*& pFrameOut, RagdollSimulationFrame_t*& pNextFrameOut,
const RagdollSimulationData_t* pRagdollEntry, int nTick ) const
{
return const_cast< CReplayRagdollCache* >( this )->FindFrame( pFrameOut, pNextFrameOut, pRagdollEntry, nTick );
}
//--------------------------------------------------------------------------------
#endif // REPLAY_RAGDOLL_H
+246
View File
@@ -0,0 +1,246 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "materialsystem/itexture.h"
#include "materialsystem/imaterialsystem.h"
#include "replay/iclientreplaycontext.h"
#include "replay/ireplayscreenshotmanager.h"
#include "replay_screenshot.h"
#include "view.h"
#include "filesystem.h"
#include "view_shared.h"
#include "iviewrender.h"
#include "fasttimer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
extern IClientReplayContext *g_pClientReplayContext;
ITexture *CReplayScreenshotTaker::m_pScreenshotTarget;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayScreenshotTaker::CReplayScreenshotTaker( IViewRender *pViewRender, CViewSetup &viewSetup )
: m_pViewRender( pViewRender ),
m_View( viewSetup )
{
m_pUnpaddedPixels = NULL;
m_pPaddedPixels = NULL;
m_pVTFPixels = NULL;
m_pVTFTexture = NULL;
m_pBuffer = NULL;
if ( !m_pScreenshotTarget )
return;
m_aPaddedDims[ 0 ] = m_pScreenshotTarget->GetActualWidth();
m_aPaddedDims[ 1 ] = m_pScreenshotTarget->GetActualHeight();
g_pClientReplayContext->GetScreenshotManager()->GetUnpaddedScreenshotSize( m_aUnpaddedDims[ 0 ], m_aUnpaddedDims[ 1 ] );
// Calculate sizes
int nUnpaddedSize = 3 * m_aUnpaddedDims[ 0 ] * m_aUnpaddedDims[ 1 ];
int nPaddedSize = 3 * m_aPaddedDims[ 0 ] * m_aPaddedDims[ 1 ];
// Allocate for padded & unpadded pixel data
m_pUnpaddedPixels = new uint8[ nUnpaddedSize ];
m_pPaddedPixels = new uint8[ nPaddedSize ];
// White out the entire padded image
V_memset( m_pPaddedPixels, 255, nPaddedSize );
// Create the VTF
#ifndef _X360
IVTFTexture *pVTFTexture = CreateVTFTexture();
const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB;
if ( !pVTFTexture->Init( m_aPaddedDims[ 0 ], m_aPaddedDims[ 1 ], 1, IMAGE_FORMAT_RGB888, nFlags, 1, 1 ) )
return;
m_pVTFTexture = pVTFTexture;
#else
m_pVTFTexture = NULL;
#endif // _X360
// Allocate pixels for the output buffer
int nVTFSize = 1024 + ( 3 * m_aPaddedDims[ 0 ] * m_aPaddedDims[ 1 ] );
m_pVTFPixels = new uint8[ nVTFSize ];
m_pBuffer = new CUtlBuffer( m_pVTFPixels, nVTFSize );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayScreenshotTaker::~CReplayScreenshotTaker()
{
delete [] m_pUnpaddedPixels;
delete [] m_pPaddedPixels;
delete [] m_pVTFPixels;
#ifndef _X360
DestroyVTFTexture( m_pVTFTexture );
#endif // _X360
delete m_pBuffer;
}
//-----------------------------------------------------------------------------
// Purpose: takes a screenshot for the replay system
//-----------------------------------------------------------------------------
void CReplayScreenshotTaker::TakeScreenshot( WriteReplayScreenshotParams_t &params )
{
if ( !m_pViewRender )
return;
CFastTimer timer;
ConVarRef replay_debug( "replay_debug" );
bool bDbg = replay_debug.IsValid() && replay_debug.GetBool();
int width = params.m_nWidth;
int height = params.m_nHeight;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
extern bool g_bRenderingScreenshot;
g_bRenderingScreenshot = true;
// Push back buffer on the stack with small viewport
pRenderContext->PushRenderTargetAndViewport( m_pScreenshotTarget, 0, 0, width, height );
// setup the view to render
CViewSetup viewSetup = m_View;
viewSetup.x = 0;
viewSetup.y = 0;
viewSetup.width = width;
viewSetup.height = height;
viewSetup.fov = ScaleFOVByWidthRatio( m_View.fov, ( (float)width / (float)height ) / ( 4.0f / 3.0f ) );
viewSetup.m_bRenderToSubrectOfLargerScreen = true;
// Setup view origin/angles
if ( params.m_pOrigin )
{
viewSetup.origin = *params.m_pOrigin;
}
if ( params.m_pAngles )
{
viewSetup.angles = *params.m_pAngles;
}
timer.Start();
// draw out the scene - don't draw the HUD or the viewmodel
m_pViewRender->RenderView( viewSetup, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, 0 );
timer.End();
if ( bDbg ) Warning( "Screenshot RenderView(): %.4f s\n", timer.GetDuration().GetSeconds() );
timer.Start();
// Get Bits from the material system
pRenderContext->ReadPixels( 0, 0, width, height, m_pUnpaddedPixels, IMAGE_FORMAT_RGB888 );
timer.End();
if ( bDbg ) Warning( "Screenshot ReadPixels(): %.4f s\n", timer.GetDuration().GetSeconds() );
// Some stuff to be setup dependent on padded vs. not padded
int nSrcWidth, nSrcHeight;
unsigned char *pSrcImage;
// Setup dimensions as needed
int nPaddedWidth = m_aPaddedDims[0];
int nPaddedHeight = m_aPaddedDims[1];
// Allocate
unsigned char *pUnpaddedImage = m_pUnpaddedPixels;
unsigned char *pPaddedImage = m_pPaddedPixels;
timer.Start();
// Copy over each row individually
for ( int nRow = 0; nRow < height; ++nRow )
{
unsigned char *pDst = pPaddedImage + 3 * ( nRow * nPaddedWidth );
const unsigned char *pSrc = pUnpaddedImage + 3 * ( nRow * width );
V_memcpy( pDst, pSrc, 3 * width );
}
timer.End();
if ( bDbg ) Warning( "Screenshot copy image: %.4f s\n", timer.GetDuration().GetSeconds() );
// Setup source data
nSrcWidth = nPaddedWidth;
nSrcHeight = nPaddedHeight;
pSrcImage = pPaddedImage;
if ( !m_pVTFTexture )
return;
// Copy the image data over to the VTF
unsigned char *pDestBits = m_pVTFTexture->ImageData();
int nDstSize = nSrcWidth * nSrcHeight * 3;
V_memcpy( pDestBits, pSrcImage, nDstSize );
bool bWriteResult = true;
// Reset put
m_pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
timer.Start();
// Serialize to the buffer
bWriteResult = m_pVTFTexture->Serialize( *m_pBuffer );
timer.End();
if ( bDbg ) Warning( "Screenshot VTF->Serialize(): %.4f s\n", timer.GetDuration().GetSeconds() );
if ( !bWriteResult )
{
Warning( "Couldn't write Replay screenshot.\n" );
bWriteResult = false;
return;
}
// async write to disk (this will take ownership of the memory)
char szPathedFileName[_MAX_PATH];
Q_snprintf( szPathedFileName, sizeof(szPathedFileName), "//MOD/%s", params.m_pFilename );
timer.Start();
filesystem->AsyncWrite( szPathedFileName, m_pBuffer->Base(), m_pBuffer->TellPut(), false );
timer.End();
if ( bDbg ) Warning( "Screenshot AsyncWrite(): %.4f s\n", timer.GetDuration().GetSeconds() );
// restore our previous state
pRenderContext->PopRenderTargetAndViewport();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
g_bRenderingScreenshot = false;
}
void CReplayScreenshotTaker::CreateRenderTarget( IMaterialSystem *pMaterialSystem )
{
m_pScreenshotTarget = pMaterialSystem->CreateNamedRenderTargetTextureEx2( "rt_ReplayScreenshot", 0, 0, RT_SIZE_REPLAY_SCREENSHOT, IMAGE_FORMAT_RGB888, MATERIAL_RT_DEPTH_SEPARATE );
m_pScreenshotTarget->AddRef(); // we will leak this ref, but only at shutdown of the app, which will be cleaned up then
}
#endif
+54
View File
@@ -0,0 +1,54 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#if !defined( REPLAY_SCREENSHOT_H )
#define REPLAY_SCREENSHOT_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "replay/screenshot.h"
//-----------------------------------------------------------------------------
class IViewRender;
//-----------------------------------------------------------------------------
//
// Takes screenshots as VTF's for the replay system - allocates enough
// memory up-front as opposed to every frame. Should be destroyed and recreated
// any time the cvar "replay_screenshotresolution" changes OR the actual screen
// resolution.
//
class CReplayScreenshotTaker
{
public:
CReplayScreenshotTaker( IViewRender *pViewRender, CViewSetup &view );
~CReplayScreenshotTaker();
void TakeScreenshot( WriteReplayScreenshotParams_t &params );
static void CreateRenderTarget( IMaterialSystem *pMaterialSystem );
private:
IViewRender *m_pViewRender;
CViewSetup &m_View;
uint8 *m_pUnpaddedPixels;
uint8 *m_pPaddedPixels;
IVTFTexture *m_pVTFTexture;
uint8 *m_pVTFPixels;
int m_aUnpaddedDims[2]; // Width & height of m_pUnpaddedPixels
int m_aPaddedDims[2]; // Width & height of m_pPaddedPixels
CUtlBuffer *m_pBuffer;
static ITexture *m_pScreenshotTarget;
};
#endif // REPLAY_SCREENSHOT_H
+929
View File
@@ -0,0 +1,929 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replay/ireplaysystem.h"
#include "replay/ienginereplay.h"
#include "replay/ireplaymanager.h"
#include "replay/replay.h"
#include "replay/replaycamera.h"
#include "cdll_client_int.h"
#include "util_shared.h"
#include "prediction.h"
#include "movevars_shared.h"
#include "in_buttons.h"
#include "text_message.h"
#include "vgui_controls/Controls.h"
#include "vgui/ILocalize.h"
#include "vguicenterprint.h"
#include "game/client/iviewport.h"
#include "vgui/IInput.h"
#include <KeyValues.h>
#include "iinput.h"
#include "iclientmode.h"
#include "ienginevgui.h"
#include "vgui/IInput.h"
#include "mathlib/noise.h"
#ifdef CSTRIKE_DLL
#include "c_cs_player.h"
#endif
ConVar replay_editor_camera_length( "replay_editor_camera_length", "15", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "This is the camera length used to simulate camera shake in the replay editor. The larger this number, the more the actual position will change. It can also be set to negative values." );
//ConVar spec_autodirector( "spec_autodirector", "1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director chooses best view modes while spectating" );
extern ConVar spec_autodirector;
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define CHASE_CAM_DISTANCE_MAX 96.0f
#define WALL_OFFSET 6.0f
#define DEFAULT_ROAMING_ACCEL 5.0f
#define DEFAULT_ROAMING_SPEED 3.0f
static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// converts all '\r' characters to '\n', so that the engine can deal with the properly
// returns a pointer to str
static wchar_t* ConvertCRtoNL( wchar_t *str )
{
for ( wchar_t *ch = str; *ch != 0; ch++ )
if ( *ch == L'\r' )
*ch = L'\n';
return str;
}
static C_ReplayCamera s_ReplayCamera;
C_ReplayCamera *ReplayCamera()
{
return &s_ReplayCamera;
}
C_ReplayCamera::C_ReplayCamera()
{
Reset();
m_nNumSpectators = 0;
m_szTitleText[0] = 0;
}
C_ReplayCamera::~C_ReplayCamera()
{
}
void C_ReplayCamera::Init()
{
ListenForGameEvent( "game_newmap" );
Reset();
m_nNumSpectators = 0;
m_szTitleText[0] = 0;
}
void C_ReplayCamera::Reset()
{
m_nCameraMode = OBS_MODE_FIXED;
m_iTarget1 = m_iTarget2 = 0;
m_flFOV = 90.0f;
m_flDistance = m_flLastDistance = CHASE_CAM_DISTANCE_MAX;
m_flInertia = 3.0f;
m_flPhi = 0;
m_flTheta = 0;
m_flOffset = 0;
m_bEntityPacketReceived = false;
m_bOverrideView = false;
m_flOldTime = 0.0f;
m_bInputEnabled = true;
m_flRoamingAccel = DEFAULT_ROAMING_ACCEL;
m_flRoamingSpeed = DEFAULT_ROAMING_SPEED;
m_flRoamingFov[0] = m_flRoamingFov[1] = 90.0f;
m_flRoamingRotFilterFactor = 10.0f;
m_flRoamingShakeAmount = 0.0f;
m_flRoamingShakeSpeed = 0.0f;
m_flNoiseSample = 0.0f;
m_flRoamingShakeDir = Lerp( 0.5f, FREE_CAM_SHAKE_DIR_MIN, FREE_CAM_SHAKE_DIR_MAX );
m_vCamOrigin.Init();
m_aCamAngle.Init();
m_aSmoothedRoamingAngles.Init();
m_OverrideViewData.origin.Init();
m_OverrideViewData.angles.Init();
m_OverrideViewData.fov = 90;
m_LastCmd.Reset();
m_vecVelocity.Init();
InitRoamingKeys();
}
void C_ReplayCamera::InitRoamingKeys()
{
m_aMovementButtons[DIR_FWD ] = KEY_W;
m_aMovementButtons[DIR_BACK ] = KEY_S;
m_aMovementButtons[DIR_LEFT ] = KEY_A;
m_aMovementButtons[DIR_RIGHT] = KEY_D;
m_aMovementButtons[DIR_DOWN ] = KEY_X;
m_aMovementButtons[DIR_UP ] = KEY_Z;
}
bool C_ReplayCamera::ShouldUseDefaultRoamingSettings() const
{
return vgui::input()->IsKeyDown( KEY_LSHIFT );
}
void C_ReplayCamera::CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
{
bool bManual = true;
Vector targetOrigin1, targetOrigin2, cameraOrigin, forward;
if ( m_iTarget1 == 0 )
return;
// get primary target, also translates to ragdoll
C_BaseEntity *target1 = GetPrimaryTarget();
if ( !target1 )
return;
if ( target1->IsAlive() && target1->IsDormant() )
return;
targetOrigin1 = target1->GetRenderOrigin();
if ( !target1->IsAlive() )
{
targetOrigin1 += VEC_DEAD_VIEWHEIGHT;
}
else if ( target1->GetFlags() & FL_DUCKING )
{
targetOrigin1 += VEC_DUCK_VIEW;
}
else
{
targetOrigin1 += VEC_VIEW;
}
// get secondary target if set
C_BaseEntity *target2 = NULL;
if ( m_iTarget2 > 0 && (m_iTarget2 != m_iTarget1) && !bManual )
{
target2 = ClientEntityList().GetBaseEntity( m_iTarget2 );
// if target is out PVS and not dead, it's not valid
if ( target2 && target2->IsDormant() && target2->IsAlive() )
target2 = NULL;
if ( target2 )
{
targetOrigin2 = target2->GetRenderOrigin();
if ( !target2->IsAlive() )
{
targetOrigin2 += VEC_DEAD_VIEWHEIGHT;
}
else if ( target2->GetFlags() & FL_DUCKING )
{
targetOrigin2 += VEC_DUCK_VIEW;
}
else
{
targetOrigin2 += VEC_VIEW;
}
}
}
// apply angle offset & smoothing
QAngle angleOffset( m_flPhi, m_flTheta, 0 );
QAngle cameraAngles = m_aCamAngle;
if ( bManual )
{
// let spectator choose the view angles
engine->GetViewAngles( cameraAngles );
}
else if ( target2 )
{
// look into direction of second target
forward = targetOrigin2 - targetOrigin1;
VectorAngles( forward, cameraAngles );
cameraAngles.z = 0; // no ROLL
}
else if ( m_iTarget2 == 0 || m_iTarget2 == m_iTarget1)
{
// look into direction where primary target is looking
cameraAngles = target1->EyeAngles();
cameraAngles.x = 0; // no PITCH
cameraAngles.z = 0; // no ROLL
}
else
{
// target2 is missing, just keep angelsm, reset offset
angleOffset.Init();
}
if ( !bManual )
{
if ( !target1->IsAlive() )
{
angleOffset.x = 15;
}
cameraAngles += angleOffset;
}
AngleVectors( cameraAngles, &forward );
VectorNormalize( forward );
// calc optimal camera position
VectorMA(targetOrigin1, -m_flDistance, forward, cameraOrigin );
targetOrigin1.z += m_flOffset; // add offset
// clip against walls
trace_t trace;
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( targetOrigin1, cameraOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, target1, COLLISION_GROUP_NONE, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
float dist = VectorLength( trace.endpos - targetOrigin1 );
// grow distance by 32 unit a second
m_flLastDistance += flDelta * 32.0f;
if ( dist > m_flLastDistance )
{
VectorMA(targetOrigin1, -m_flLastDistance, forward, cameraOrigin );
}
else
{
cameraOrigin = trace.endpos;
m_flLastDistance = dist;
}
if ( target2 )
{
// if we have 2 targets look at point between them
forward = (targetOrigin1+targetOrigin2)/2 - cameraOrigin;
QAngle angle;
VectorAngles( forward, angle );
cameraAngles.y = angle.y;
NormalizeAngles( cameraAngles );
cameraAngles.x = clamp( cameraAngles.x, -60.f, 60.f );
SmoothCameraAngle( cameraAngles );
}
else
{
SetCameraAngle( cameraAngles );
}
VectorCopy( cameraOrigin, m_vCamOrigin );
VectorCopy( m_aCamAngle, eyeAngles );
VectorCopy( m_vCamOrigin, eyeOrigin );
fov = m_flFOV;
}
int C_ReplayCamera::GetMode()
{
return m_nCameraMode;
}
C_BaseEntity* C_ReplayCamera::GetPrimaryTarget()
{
if ( m_iTarget1 <= 0 )
return NULL;
C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
return target;
}
void C_ReplayCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
{
C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iTarget1 );
if ( !pPlayer )
return;
if ( !pPlayer->IsAlive() )
{
// if dead, show from 3rd person
CalcChaseCamView( eyeOrigin, eyeAngles, fov, flDelta );
return;
}
m_aCamAngle = pPlayer->EyeAngles();
m_vCamOrigin = pPlayer->GetAbsOrigin();
m_flFOV = pPlayer->GetFOV();
if ( pPlayer->GetFlags() & FL_DUCKING )
{
m_vCamOrigin += VEC_DUCK_VIEW;
}
else
{
m_vCamOrigin += VEC_VIEW;
}
eyeOrigin = m_vCamOrigin;
eyeAngles = m_aCamAngle;
fov = m_flFOV;
pPlayer->CalcViewModelView( eyeOrigin, eyeAngles);
C_BaseViewModel *pViewModel = pPlayer->GetViewModel( 0 );
if ( pViewModel )
{
Assert( pViewModel->GetOwner() == pPlayer );
pViewModel->UpdateVisibility();
}
// This fixes the bug where going from third or first person to free cam defaults to some arbitrary angle,
// because free cam uses engine->GetViewAngles().
engine->SetViewAngles( m_aCamAngle );
}
void C_ReplayCamera::Accelerate( Vector& wishdir, float wishspeed, float accel, float flDelta )
{
float addspeed, accelspeed, currentspeed;
// See if we are changing direction a bit
currentspeed =m_vecVelocity.Dot(wishdir);
// Reduce wishspeed by the amount of veer.
addspeed = wishspeed - currentspeed;
// If not going to add any speed, done.
if (addspeed <= 0)
return;
// Determine amount of acceleration.
accelspeed = accel * flDelta * wishspeed;
// Cap at addspeed
if (accelspeed > addspeed)
accelspeed = addspeed;
// Adjust velocity.
for (int i=0 ; i<3 ; i++)
{
m_vecVelocity[i] += accelspeed * wishdir[i];
}
}
bool C_ReplayCamera::ShouldOverrideView( Vector& origin, QAngle& angles, float& fov )
{
if ( !m_bOverrideView )
return false;
origin = m_OverrideViewData.origin;
angles = m_OverrideViewData.angles;
fov = m_OverrideViewData.fov;
return true;
}
// movement code is a copy of CGameMovement::FullNoClipMove()
void C_ReplayCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta)
{
// only if PVS isn't locked by auto-director
if ( !IsPVSLocked() )
{
Vector wishvel;
Vector forward, right, up;
Vector wishdir;
float wishspeed;
float factor = ShouldUseDefaultRoamingSettings() ? DEFAULT_ROAMING_SPEED : m_flRoamingSpeed;
float maxspeed = sv_maxspeed.GetFloat() * factor;
AngleVectors ( m_aCamAngle, &forward, &right, &up ); // Determine movement angles
if ( m_LastCmd.buttons & IN_SPEED )
{
factor /= 2.0f;
}
// Check for movement
float fmove = 0.0f;
float smove = 0.0f;
float vmove = 0.0f;
if ( !enginevgui->IsGameUIVisible() && m_bInputEnabled )
{
// Forward/backward movement
if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_FWD] ) )
{
fmove = factor * maxspeed;
}
else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_BACK] ) )
{
fmove = -factor * maxspeed;
}
// Lateral movement
if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_LEFT] ) )
{
smove = -factor * maxspeed;
}
else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_RIGHT] ) )
{
smove = factor * maxspeed;
}
// Vertical movement
if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_UP] ) )
{
vmove = factor * maxspeed;
}
else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_DOWN] ) )
{
vmove = -factor * maxspeed;
}
}
// Normalize remainder of vectors
VectorNormalize(forward);
VectorNormalize(right);
VectorNormalize(up);
for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove + up[i]*vmove;
wishvel[2] += m_LastCmd.upmove * factor;
VectorCopy (wishvel, wishdir); // Determine magnitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// Clamp to server defined max speed
//
if (wishspeed > maxspeed )
{
VectorScale (wishvel, maxspeed/wishspeed, wishvel);
wishspeed = maxspeed;
}
const float flRoamingAccel = ShouldUseDefaultRoamingSettings() ?
DEFAULT_ROAMING_ACCEL : m_flRoamingAccel;
if ( flRoamingAccel > 0.0 )
{
// Set move velocity
Accelerate ( wishdir, wishspeed, flRoamingAccel, flDelta );
float spd = VectorLength( m_vecVelocity );
if ( CloseEnough( spd, 0.0f ) )
{
m_vecVelocity.Init();
}
else
{
// Bleed off some speed, but if we have less than the bleed
// threshold, bleed the threshold amount.
float control = spd;
float friction = sv_friction.GetFloat();
// Add the amount to the drop amount.
float drop = control * friction * flDelta;
// scale the velocity
float newspeed = spd - drop;
if (newspeed < 0)
newspeed = 0;
// Determine proportion of old speed we are using.
newspeed /= spd;
VectorScale( m_vecVelocity, newspeed, m_vecVelocity );
}
}
else
{
VectorCopy( wishvel, m_vecVelocity );
}
// Just move ( don't clip or anything )
VectorMA( m_vCamOrigin, flDelta, m_vecVelocity, m_vCamOrigin );
// get camera angle directly from engine
engine->GetViewAngles( m_aCamAngle );
// Zero out velocity if in noaccel mode
if ( sv_specaccelerate.GetFloat() < 0.0f )
{
m_vecVelocity.Init();
}
}
// Smooth the angles
float flPercent = clamp( flDelta * m_flRoamingRotFilterFactor, 0.0f, 1.0f );
m_aSmoothedRoamingAngles = Lerp( flPercent, m_aSmoothedRoamingAngles, m_aCamAngle );
Vector vCameraShakeOffset;
vCameraShakeOffset.Init();
// Add in camera shake
if ( !ShouldUseDefaultRoamingSettings() && m_flRoamingShakeAmount > 0.0f )
{
QAngle angShake( 0.0f, 0.0f, 0.0f );
m_flNoiseSample += m_flRoamingShakeSpeed * flDelta;
float flNoiseX = Lerp( FractalNoise( Vector( m_flNoiseSample, 0.0f, 0.0f ), 1 ), -1.0f, 1.0f );
float flNoiseY = Lerp( FractalNoise( Vector( 0.0f, 1000 + m_flNoiseSample, 0.0f ), 1 ), -1.0f, 1.0f );
// Vertical shake
const float flAmplitudeX = m_flRoamingShakeAmount * ( m_flRoamingShakeDir < 0.0f ? ( 1.0f + m_flRoamingShakeDir ) : 1.0f );
angShake.x = flAmplitudeX * flNoiseX;
// Lateral shake
const float flAmplitudeY = m_flRoamingShakeAmount * ( m_flRoamingShakeDir > 0.0f ? ( 1.0f - m_flRoamingShakeDir ) : 1.0f );
angShake.y = flAmplitudeY * flNoiseY;
// The math below simulates a camera with length "replay_editor_camera_length," so that the camera position bounces around
// as if it were on someone's shoulder. If we were to just use angShake at this point with no translation, we would get a
// camera that looks around but is anchored and doesn't feel quite right. With the code below, the camera will translate,
// but be centered around the same point as when camera shake is off completely, rather than actually offsetting that point
// by the camera length.
// Get the forward vector from the shake transform/angles
Vector vShakeForward;
AngleVectors( angShake, &vShakeForward );
// Calculate an offset, simulating a camera length
Vector vCameraOffset = vShakeForward * replay_editor_camera_length.GetFloat();
// Get the global matrix without any shake
matrix3x4_t mGlobal;
AngleMatrix( m_aSmoothedRoamingAngles, m_vCamOrigin, mGlobal );
// Convert local shake angles and offset to a matrix
matrix3x4_t mShake;
AngleMatrix( angShake, mShake );
// Setup a translation matrix using the offset
matrix3x4_t mOffset;
SetIdentityMatrix( mOffset );
PositionMatrix( vCameraOffset, mOffset );
matrix3x4_t mOffsetInv;
MatrixInvert( mOffset, mOffsetInv );
// The meat
matrix3x4_t mFinal = mGlobal;
MatrixMultiply( mFinal, mOffsetInv, mFinal );
MatrixMultiply( mFinal, mShake, mFinal );
MatrixMultiply( mFinal, mOffset, mFinal );
// Convert back to Vector / QAngle
MatrixAngles( mFinal, eyeAngles, eyeOrigin );
}
else
{
// No shake
eyeOrigin = m_vCamOrigin;
eyeAngles = m_aSmoothedRoamingAngles;
}
fov = m_flRoamingFov[0];
}
void C_ReplayCamera::CalcFixedView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
{
eyeOrigin = m_vCamOrigin;
eyeAngles = m_aCamAngle;
fov = m_flFOV;
if ( m_iTarget1 == 0 )
return;
C_BaseEntity * target = ClientEntityList().GetBaseEntity( m_iTarget1 );
if ( target && target->IsAlive() )
{
// if we're chasing a target, change viewangles
QAngle angle;
VectorAngles( (target->GetAbsOrigin()+VEC_VIEW) - m_vCamOrigin, angle );
SmoothCameraAngle( angle );
}
}
void C_ReplayCamera::PostEntityPacketReceived()
{
m_bEntityPacketReceived = true;
}
void C_ReplayCamera::SmoothFov( float flDelta )
{
m_flRoamingFov[0] = clamp(
Lerp( 7 * flDelta, m_flRoamingFov[0], m_flRoamingFov[1] ),
// Approach( m_flRoamingFov[1], m_flRoamingFov[0], 40 * m_flFrameTime ),
FREE_CAM_FOV_MIN,
FREE_CAM_FOV_MAX
);
}
void C_ReplayCamera::FixupMovmentParents()
{
// Find resource zone
for ( ClientEntityHandle_t e = ClientEntityList().FirstHandle();
e != ClientEntityList().InvalidHandle(); e = ClientEntityList().NextHandle( e ) )
{
C_BaseEntity *ent = C_BaseEntity::Instance( e );
if ( !ent )
continue;
ent->HierarchyUpdateMoveParent();
}
}
void C_ReplayCamera::EnableInput( bool bEnable )
{
m_bInputEnabled = bEnable;
}
void C_ReplayCamera::ClearOverrideView()
{
if ( m_bOverrideView )
{
m_vCamOrigin = m_OverrideViewData.origin;
m_aCamAngle = m_aSmoothedRoamingAngles = m_OverrideViewData.angles;
m_flRoamingFov[0] = m_flRoamingFov[1] = m_OverrideViewData.fov;
}
m_bOverrideView = false;
// Set view angles in engine so that CalcRoamingView() won't pop to some stupid angle
engine->SetViewAngles( m_aCamAngle );
}
void C_ReplayCamera::OverrideView( const Vector *pOrigin, const QAngle *pAngles, float flFov )
{
m_bOverrideView = true;
m_OverrideViewData.origin = *pOrigin;
m_OverrideViewData.angles = *pAngles;
m_OverrideViewData.fov = flFov;
}
void C_ReplayCamera::CalcView(Vector &origin, QAngle &angles, float &fov )
{
// NOTE ABOUT CLOCKS: 'realtime' is used, because otherwise we can't move the camera round while
// the game is paused.
// Calculate elapsed time since last call to CalcView()
if ( m_flOldTime == 0.0f )
{
m_flOldTime = gpGlobals->realtime;
}
const float flDelta = gpGlobals->realtime - m_flOldTime;
m_flOldTime = gpGlobals->realtime;
if ( m_bEntityPacketReceived )
{
// try to fixup movment parents
FixupMovmentParents();
m_bEntityPacketReceived = false;
}
// Completely override?
if ( ShouldOverrideView( origin, angles, fov ) )
return;
switch ( m_nCameraMode )
{
case OBS_MODE_ROAMING : CalcRoamingView( origin, angles, fov, flDelta );
break;
case OBS_MODE_FIXED : CalcFixedView( origin, angles, fov, flDelta );
break;
case OBS_MODE_IN_EYE : CalcInEyeCamView( origin, angles, fov, flDelta );
break;
case OBS_MODE_CHASE : CalcChaseCamView( origin, angles, fov, flDelta );
break;
}
// Cache in case we want to access this data later in the frame
m_CachedView.origin = origin;
m_CachedView.angles = angles;
m_CachedView.fov = fov;
}
void C_ReplayCamera::GetCachedView( Vector &origin, QAngle &angles, float &fov )
{
origin = m_CachedView.origin;
angles = m_CachedView.angles;
fov = m_CachedView.fov;
}
void C_ReplayCamera::SetMode(int iMode)
{
if ( m_nCameraMode == iMode )
return;
Assert( iMode > OBS_MODE_NONE && iMode <= LAST_PLAYER_OBSERVERMODE );
m_nCameraMode = iMode;
if ( m_nCameraMode != OBS_MODE_ROAMING && m_nCameraMode != OBS_MODE_CHASE )
{
ClearOverrideView();
}
}
void C_ReplayCamera::SetPrimaryTarget( int nEntity )
{
if ( m_iTarget1 == nEntity )
return;
m_iTarget1 = nEntity;
if ( GetMode() == OBS_MODE_ROAMING )
{
Vector vOrigin;
QAngle aAngles;
float flFov;
CalcChaseCamView( vOrigin, aAngles, flFov, 0.015f );
}
else if ( GetMode() == OBS_MODE_CHASE )
{
C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
if ( target )
{
QAngle eyeAngle = target->EyeAngles();
prediction->SetViewAngles( eyeAngle );
}
}
m_flLastDistance = m_flDistance;
m_flLastAngleUpdateTime = -1;
}
void C_ReplayCamera::SpecNextPlayer( bool bInverse )
{
int start = 1;
if ( m_iTarget1 > 0 && m_iTarget1 <= gpGlobals->maxClients )
start = m_iTarget1;
int index = start;
while ( true )
{
// got next/prev player
if ( bInverse )
index--;
else
index++;
// check bounds
if ( index < 1 )
index = gpGlobals->maxClients;
else if ( index > gpGlobals->maxClients )
index = 1;
if ( index == start )
break; // couldn't find a new valid player
C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
if ( !pPlayer )
continue;
// only follow living players
if ( pPlayer->IsObserver() )
continue;
break; // found a new player
}
SetPrimaryTarget( index );
// turn off auto director once user tried to change view settings
SetAutoDirector( false );
}
void C_ReplayCamera::SpecPlayerByPredicate( const char *szSearch )
{
CBasePlayer *pPlayer = UTIL_PlayerByCommandArg( szSearch );
if ( !pPlayer )
return;
// only follow living players or dedicated spectators
if ( pPlayer->IsObserver() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
return;
SetPrimaryTarget( pPlayer->entindex() );
return;
}
void C_ReplayCamera::FireGameEvent( IGameEvent * event)
{
if ( !g_pEngineClientReplay->IsPlayingReplayDemo() )
return; // not in Replay mode
const char *type = event->GetName();
if ( Q_strcmp( "game_newmap", type ) == 0 )
{
// Do not reset the camera, since we reload the map when "rewinding"
// and want to keep our camera settings intact.
// Reset(); // reset all camera settings
// show spectator UI
if ( !gViewPortInterface )
return;
if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
{
SetMode( OBS_MODE_IN_EYE );
CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
SetPrimaryTarget( ( pReplay && pReplay->m_nPlayerSlot >= 0 ) ? pReplay->m_nPlayerSlot : 0 );
}
else
{
// during live broadcast only show black bars
gViewPortInterface->ShowPanel( PANEL_SPECMENU, true );
}
return;
}
// after this only auto-director commands follow
// don't execute them is autodirector is off and PVS is unlocked
if ( !spec_autodirector.GetBool() && !IsPVSLocked() )
return;
}
// this is a cheap version of FullNoClipMove():
void C_ReplayCamera::CreateMove( CUserCmd *cmd)
{
if ( cmd )
{
m_LastCmd = *cmd;
}
}
void C_ReplayCamera::SetCameraAngle( QAngle& targetAngle )
{
m_aCamAngle = targetAngle;
NormalizeAngles( m_aCamAngle );
m_flLastAngleUpdateTime = gpGlobals->realtime;
}
void C_ReplayCamera::SmoothCameraAngle( QAngle& targetAngle )
{
if ( m_flLastAngleUpdateTime > 0 )
{
float deltaTime = gpGlobals->realtime - m_flLastAngleUpdateTime;
deltaTime = clamp( deltaTime*m_flInertia, 0.01f, 1.f);
InterpolateAngles( m_aCamAngle, targetAngle, m_aCamAngle, deltaTime );
}
else
{
m_aCamAngle = targetAngle;
}
m_flLastAngleUpdateTime = gpGlobals->realtime;
}
bool C_ReplayCamera::IsPVSLocked()
{
return false;
}
void C_ReplayCamera::SetAutoDirector( bool bActive )
{
spec_autodirector.SetValue( bActive?1:0 );
}
#endif
+159
View File
@@ -0,0 +1,159 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#if defined( REPLAY_ENABLED )
#ifndef REPLAYCAMERA_H
#define REPLAYCAMERA_H
#ifdef _WIN32
#pragma once
#endif
#include "replay/ireplaycamera.h"
#include "GameEventListener.h"
class C_ReplayCamera : public CGameEventListener,
public IReplayCamera
{
public:
C_ReplayCamera();
virtual ~C_ReplayCamera();
void Init();
void Reset();
//
// IReplayCamera:
//
virtual void ClearOverrideView();
void EnableInput( bool bEnable );
void OverrideView( const Vector *pOrigin, const QAngle *pAngles, float flFov );
void GetCachedView( Vector &origin, QAngle &angles, float &fov );
void CalcView(Vector& origin, QAngle& angles, float& fov);
void FireGameEvent( IGameEvent *event );
void SetMode(int iMode);
void SetChaseCamParams( float flOffset, float flDistance, float flTheta, float flPhi );
void SpecNextPlayer( bool bInverse );
// See UTIL_PlayerByCommandArg for what all might go in here.
void SpecPlayerByPredicate( const char *szPlayerSearch );
bool IsPVSLocked();
void SetAutoDirector( bool bActive );
int GetMode(); // returns current camera mode
C_BaseEntity *GetPrimaryTarget(); // return primary target
inline int GetPrimaryTargetIndex() { return m_iTarget1; }
void SetPrimaryTarget( int nEntity); // set the primary obs target
void CreateMove(CUserCmd *cmd);
void FixupMovmentParents();
void PostEntityPacketReceived();
const char* GetTitleText() { return m_szTitleText; }
int GetNumSpectators() { return m_nNumSpectators; }
void SmoothFov( float flDelta );
float m_flRoamingAccel;
float m_flRoamingSpeed;
float m_flRoamingFov[2]; // FOV for roaming only - current and target - smoothing done by replay editor
float m_flRoamingRotFilterFactor;
float m_flRoamingShakeAmount;
float m_flRoamingShakeSpeed;
float m_flRoamingShakeDir;
protected:
void InitRoamingKeys();
bool ShouldUseDefaultRoamingSettings() const;
void CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta );
void CalcFixedView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta );
void CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta );
void CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta);
void SmoothCameraAngle( QAngle& targetAngle );
void SetCameraAngle( QAngle& targetAngle );
void Accelerate( Vector& wishdir, float wishspeed, float accel, float flDelta );
bool ShouldOverrideView( Vector& origin, QAngle& angles, float& fov ); // Fills with override data if m_bOverrideView is set
struct View_t
{
Vector origin;
QAngle angles;
float fov;
};
bool m_bInputEnabled;
bool m_bOverrideView;
View_t m_OverrideViewData;
View_t m_CachedView;
float m_flOldTime; // Time of last CalcView() (uses gpGlobals->realtime)
int m_nCameraMode; // current camera mode
Vector m_vCamOrigin; //current camera origin
QAngle m_aCamAngle; //current camera angle
QAngle m_aSmoothedRoamingAngles;
int m_iTarget1; // first tracked target or 0
int m_iTarget2; // second tracked target or 0
float m_flFOV; // current FOV
float m_flOffset; // z-offset from target origin
float m_flDistance; // distance to traget origin+offset
float m_flLastDistance; // too smooth distance
float m_flTheta; // view angle horizontal
float m_flPhi; // view angle vertical
float m_flInertia; // camera inertia 0..100
float m_flLastAngleUpdateTime;
bool m_bEntityPacketReceived; // true after a new packet was received
int m_nNumSpectators;
char m_szTitleText[64];
CUserCmd m_LastCmd;
Vector m_vecVelocity;
enum Dir_t
{
DIR_FWD,
DIR_BACK,
DIR_LEFT,
DIR_RIGHT,
DIR_UP,
DIR_DOWN,
NUM_DIRS
};
ButtonCode_t m_aMovementButtons[NUM_DIRS];
float m_flNoiseSample;
};
//-----------------------------------------------------------------------------
C_ReplayCamera *ReplayCamera();
//-----------------------------------------------------------------------------
#define FREE_CAM_ACCEL_MIN 1.1f
#define FREE_CAM_ACCEL_MAX 10.0f
#define FREE_CAM_SPEED_MIN 0.1f
#define FREE_CAM_SPEED_MAX 20.0f
#define FREE_CAM_FOV_MIN 10.0f
#define FREE_CAM_FOV_MAX 130.0f
#define FREE_CAM_ROT_FILTER_MIN 30.0f
#define FREE_CAM_ROT_FILTER_MAX 5.0f
#define FREE_CAM_SHAKE_SPEED_MIN 0.1f
#define FREE_CAM_SHAKE_SPEED_MAX 15.0f
#define FREE_CAM_SHAKE_AMOUNT_MIN 0.0f
#define FREE_CAM_SHAKE_AMOUNT_MAX 35.0f
#define FREE_CAM_SHAKE_DIR_MIN -1.0f
#define FREE_CAM_SHAKE_DIR_MAX 1.0f
//-----------------------------------------------------------------------------
#endif // REPLAYCAMERA_H
#endif
@@ -0,0 +1,99 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayperformanceplaybackhandler.h"
#include "replay/replaycamera.h"
#include "replay/vgui/replayperformanceeditor.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
class CReplayPerformancePlaybackHandler : public IReplayPerformancePlaybackHandler
{
public:
CReplayPerformancePlaybackHandler() {}
private:
//
// IReplayPerformancePlaybackController
//
virtual void OnEvent_Camera_Change_FirstPerson( float flTime, int nEntityIndex )
{
ReplayCamera()->SetMode( OBS_MODE_IN_EYE );
Editor_UpdateCameraModeIcon( CAM_FIRST );
}
virtual void OnEvent_Camera_Change_ThirdPerson( float flTime, int nEntityIndex )
{
ReplayCamera()->SetMode( OBS_MODE_CHASE );
Editor_UpdateCameraModeIcon( CAM_THIRD );
}
virtual void OnEvent_Camera_Change_Free( float flTime )
{
ReplayCamera()->SetMode( OBS_MODE_ROAMING );
Editor_UpdateCameraModeIcon( CAM_FREE );
}
virtual void OnEvent_Camera_ChangePlayer( float flTime, int nEntIndex )
{
ReplayCamera()->SetPrimaryTarget( nEntIndex );
}
virtual void OnEvent_Camera_SetView( const SetViewParams_t &params )
{
ReplayCamera()->OverrideView( params.m_pOrigin, params.m_pAngles, params.m_flFov );
Editor_UpdateFreeCamSettings( params );
}
virtual void OnEvent_TimeScale( float flTime, float flScale )
{
// Update the slider position
Editor_UpdateTimeScale( flScale );
}
// ---
void Editor_UpdateCameraModeIcon( CameraMode_t nMode )
{
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( !pEditor )
return;
pEditor->UpdateCameraSelectionPosition( nMode );
}
void Editor_UpdateFreeCamSettings( const SetViewParams_t &params )
{
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( !pEditor )
return;
pEditor->UpdateFreeCamSettings( params );
}
void Editor_UpdateTimeScale( float flScale )
{
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( !pEditor )
return;
pEditor->UpdateTimeScale( flScale );
}
};
//-----------------------------------------------------------------------------
CReplayPerformancePlaybackHandler s_ReplayPerformancePlaybackHandler;
IReplayPerformancePlaybackHandler *g_pReplayPerformancePlaybackHandler = &s_ReplayPerformancePlaybackHandler;
//-----------------------------------------------------------------------------
#endif
@@ -0,0 +1,21 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYPERFORMANCEPLAYER_H
#define REPLAYPERFORMANCEPLAYER_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "replay/ireplayperformanceplaybackhandler.h"
//-----------------------------------------------------------------------------
extern IReplayPerformancePlaybackHandler *g_pReplayPerformancePlaybackHandler;
//-----------------------------------------------------------------------------
#endif // REPLAYPERFORMANCEPLAYER_H
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYRENDERER_H
#define REPLAYRENDERER_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "replay/ireplaymovierenderer.h"
#include "replay/rendermovieparams.h"
#include "movieobjects/timeutils.h"
#include "fmtstr.h"
//-----------------------------------------------------------------------------
class CReplay;
class CReplayPerformance;
class IQuickTimeMovieMaker;
class CReplayRenderOverlay;
class IVideoRecorder;
//-----------------------------------------------------------------------------
class CReplayRenderer : public IReplayMovieRenderer
{
public:
CReplayRenderer( CReplayRenderOverlay *pOverlay );
~CReplayRenderer();
const CReplayPerformance *GetPerformance() const;
const char *GetMovieFilename() const;
static int GetNumMotionBlurTimeSteps( int nQuality );
//
// IReplayMovieRenderer interface
//
virtual bool SetupRenderer( RenderMovieParams_t &params, IReplayMovie *pMovie );
virtual void ShutdownRenderer();
virtual void RenderVideo();
virtual void RenderAudio( unsigned char *pBuffer, int nSize, int nNumSamples );
virtual void SetAudioSyncFrame( bool isSync = false );
virtual bool IsAudioSyncFrame();
virtual float GetRecordingFrameDuration();
private:
bool IsDepthOfFieldEnabled() const;
bool IsAntialiasingEnabled() const;
bool IsHDR() const;
bool IsMotionBlurEnabled() const;
int GetMotionBlurQuality() const;
int GetDepthOfFieldQuality() const;
int NumMotionBlurTimeSteps() const;
float GetFramerate() const;
void ComputeSampleCounts( int *pNSamplesPerTimeStep, int *pNTotalSamples ) const;
float GetViewModelFOVOffset();
void SetupDOFMatrixSkewView( const Vector &pos, const QAngle &angles, int nSample, CViewSetup& viewSetup );
void BeginRenderingSample( int nSample, int x, int y, int nWidth, int nHeight, float fTonemapScale );
void EndRendering();
void SetupSampleView( int x, int y, int w, int h, int nSample, CViewSetup& viewSetup );
void InitBuffers( const RenderMovieParams_t &params );
void DrawResolvingQuad( int nWidth, int nHeight );
float GetRenderLength( const CReplay *pReplay );
void CompositeAndLayoffFrame( int nFrame );
void RenderLayoffFrame( DmeTime_t time, int nCurSample, int nNumTotalSamples );
void ResolveSamples( int nSample, DmeTime_t frametime, int x, int y, int nWidth, int nHeight, bool bShowUser, float flBloomScale );
void LayoffFrame( int nFrame );
double GetShutterSpeed() const;
void ClearToBlack( CTextureReference &buf, int x, int y, int nWidth, int nHeight );
bool SetupJitterTable();
void GetViewSetup( CViewSetup &viewsetup );
bool m_bIsAudioSyncFrame;
const Vector2D *m_pJitterTable;
int m_nNumJitterSamples;
IVideoRecorder *m_pMovieMaker;
bool m_bCacheFullSceneState;
BGRA8888_t *m_pLayoffBuf;
IReplayMovie *m_pMovie;
RenderMovieParams_t m_RenderParams;
CTextureReference m_AccumBuffSample;
CTextureReference m_LayoffResult;
CTextureReference m_AccumBuffPingPong[2]; // Buffers and materials for ping-pong accumulation buffer
CMaterialReference m_FourSampleResolveMatRef;
bool m_bForceCheapDoF;
bool m_bShutterClosed;
int m_nCurrentPingPong;
int m_nFrame;
ConVar *m_pViewmodelFov;
ConVar *m_pDefaultFov;
int m_nTimeStep;
int m_nCurSample;
DmeTime_t m_curSampleTime;
int m_iTgaFrame;
CFmtStr m_fmtTgaRenderDirName;
CReplayRenderOverlay *m_pRenderOverlay;
};
//-----------------------------------------------------------------------------
#endif // REPLAYRENDERER_H
+142
View File
@@ -0,0 +1,142 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayvideo.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
static ReplayVideoMode_t s_VideoModes[] =
{
{ 720, 480, 24, true, "#Replay_Res_480p" },
{ 1280, 720, 24, true, "#Replay_Res_720p" },
{ 1920, 1080, 24, true, "#Replay_Res_1080p" },
{ 320, 240, 15, false, "#Replay_Res_Web" },
{ 960, 640, 24, true, "#Replay_Res_iPhone_Horizontal" },
{ 640, 960, 24, true, "#Replay_Res_iPhone_Vertical" },
};
#define VIDEO_MODE_COUNT ( sizeof( s_VideoModes ) / sizeof( s_VideoModes[0] ) )
//-----------------------------------------------------------------------------
#ifdef USE_WEBM_FOR_REPLAY
static ReplayCodec_t s_Codecs[] =
{
{ VideoEncodeCodec::WEBM_CODEC, "#Replay_Codec_WEBM" },
};
static int s_nNumCodecs = ARRAYSIZE( s_Codecs );
//-----------------------------------------------------------------------------
static ReplayQualityPreset_t s_QualityPresets[] =
{
{ "#Replay_RenderSetting_Low", VideoEncodeCodec::WEBM_CODEC, 0, false, 0 },
{ "#Replay_RenderSetting_Medium", VideoEncodeCodec::WEBM_CODEC, 50, false, 1 },
{ "#Replay_RenderSetting_High", VideoEncodeCodec::WEBM_CODEC, 100, true, 2 },
{ "#Replay_RenderSetting_Max", VideoEncodeCodec::WEBM_CODEC, 100, true, 3 },
};
static int s_NumQualityPresets = ARRAYSIZE( s_QualityPresets );
static int s_DefaultQualityPreset = 1;
#else
static ReplayCodec_t s_Codecs[] =
{
{ VideoEncodeCodec::MJPEG_A_CODEC, "#Replay_Codec_MJPEGA" },
{ VideoEncodeCodec::H264_CODEC, "#Replay_Codec_H264" },
};
static int s_nNumCodecs = ARRAYSIZE( s_Codecs );
//-----------------------------------------------------------------------------
static ReplayQualityPreset_t s_QualityPresets[] =
{
{ "#Replay_RenderSetting_Low", VideoEncodeCodec::MJPEG_A_CODEC, 0, false, 0 },
{ "#Replay_RenderSetting_Medium", VideoEncodeCodec::MJPEG_A_CODEC, 50, false, 1 },
{ "#Replay_RenderSetting_High", VideoEncodeCodec::MJPEG_A_CODEC, 100, true, 2 },
{ "#Replay_RenderSetting_Max", VideoEncodeCodec::H264_CODEC, 100, true, 3 },
};
static int s_NumQualityPresets = ARRAYSIZE( s_QualityPresets );
static int s_DefaultQualityPreset = 1;
#endif
//-----------------------------------------------------------------------------
static const int s_QualityRange = 4;
static const int s_QualityInterval = 100 / s_QualityRange;
//-----------------------------------------------------------------------------
int ReplayVideo_GetVideoModeCount()
{
return VIDEO_MODE_COUNT;
}
const ReplayVideoMode_t &ReplayVideo_GetVideoMode( int i )
{
AssertMsg( i >= 0 && i < VIDEO_MODE_COUNT, "Replay video mode out of range!" );
return s_VideoModes[ i ];
}
int ReplayVideo_GetDefaultQualityPreset()
{
return s_DefaultQualityPreset;
}
int ReplayVideo_GetQualityInterval()
{
return s_QualityInterval;
}
int ReplayVideo_GetQualityRange()
{
return s_QualityRange;
}
int ReplayVideo_GetQualityPresetCount()
{
return s_NumQualityPresets;
}
const ReplayQualityPreset_t &ReplayVideo_GetQualityPreset( int i )
{
return s_QualityPresets[ i ];
}
int ReplayVideo_GetCodecCount()
{
return s_nNumCodecs;
}
const ReplayCodec_t &ReplayVideo_GetCodec( int i )
{
AssertMsg( i >= 0 && i < s_nNumCodecs, "Replay codec out of range!" );
return s_Codecs[ i ];
}
int ReplayVideo_FindCodecPresetFromCodec( VideoEncodeCodec_t nCodecId )
{
AssertMsg( nCodecId < VideoEncodeCodec::CODEC_COUNT, "Codec ID out of range!" );
for ( int i = 0; i < VideoEncodeCodec::CODEC_COUNT; ++i )
{
if ( s_Codecs[ i ].m_nCodecId == nCodecId )
return i;
}
AssertMsg( 0, "Codec not found! This should never happen!" );
return 0;
}
//-----------------------------------------------------------------------------
#endif
+59
View File
@@ -0,0 +1,59 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYVIDEO_H
#define REPLAYVIDEO_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "video/ivideoservices.h"
//-----------------------------------------------------------------------------
struct ReplayVideoMode_t
{
int m_nWidth;
int m_nHeight;
int m_nBaseFPS;
bool m_bNTSCRate;
const char *m_pName; // Can be a localization token, e.g. "#Replay_Blah"
};
struct ReplayQualityPreset_t
{
const char *m_pName;
VideoEncodeCodec::EVideoEncodeCodec_t m_nCodecId;
int m_iQuality;
bool m_bMotionBlurEnabled;
int m_iMotionBlurQuality;
};
struct ReplayCodec_t
{
VideoEncodeCodec::EVideoEncodeCodec_t m_nCodecId;
const char *m_pName;
};
//-----------------------------------------------------------------------------
int ReplayVideo_GetVideoModeCount();
const ReplayVideoMode_t &ReplayVideo_GetVideoMode( int i );
int ReplayVideo_GetDefaultQualityPreset();
int ReplayVideo_GetQualityInterval(); // TODO: Wtf is this?
int ReplayVideo_GetQualityRange();
int ReplayVideo_GetQualityPresetCount();
const ReplayQualityPreset_t &ReplayVideo_GetQualityPreset( int i );
int ReplayVideo_GetCodecCount();
const ReplayCodec_t &ReplayVideo_GetCodec( int i );
int ReplayVideo_FindCodecPresetFromCodec( VideoEncodeCodec_t nCodec );
//-----------------------------------------------------------------------------
#endif // REPLAYVIDEO_H
+617
View File
@@ -0,0 +1,617 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayyoutubeapi.h"
#include "tier1/KeyValues.h"
#include "replay/ireplaymovie.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/genericclassbased_replay.h"
#include "vgui/ISurface.h"
#include "vgui/ILocalize.h"
#include "vgui_controls/ProgressBar.h"
#include "vgui_controls/TextEntry.h"
#include "vgui_controls/ScrollBar.h"
#include "confirm_dialog.h"
#include "replay/vgui/replaybrowserdetailspanel.h"
#include "base_gcmessages.pb.h"
#include "youtubeapi.h"
#include "steamworks_gamestats.h"
#include "replayvideo.h"
#include "gc_clientsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
using namespace vgui;
static ConVar youtube_username( "youtube_username", "", FCVAR_ARCHIVE, "Username for YouTube." );
//-----------------------------------------------------------------------------
extern IReplayMovieManager *g_pReplayMovieManager;
extern const char *COM_GetModDirectory();
extern void GetYouTubeAPIKey( const char *pGameDir, bool bOnSteamPublic, const char **ppSource, const char **ppDeveloperTag, const char **ppDeveloperKey );
//-----------------------------------------------------------------------------
static bool HasInvalidCharacters( const char *pString )
{
while ( *pString != 0 )
{
switch ( *pString )
{
case '<': return true;
case '>': return true;
case '&': return true;
}
++pString;
}
return false;
}
//-----------------------------------------------------------------------------
void UploadOgsData( IReplayMovie *pMovie, bool bEnded = false, const char *pEndReason = NULL )
{
static int s_nUploadCounter = 0;
KeyValues* pKVData = new KeyValues( "TF2ReplayUploads" );
pKVData->SetInt( "UploadCounter", s_nUploadCounter++ );
pKVData->SetInt( "ReplayHandle", (int)pMovie->GetReplayHandle() );
const ReplayRenderSettings_t &RenderSettings = pMovie->GetRenderSettings();
CFmtStr fmtResolution( "%ix%i", RenderSettings.m_nWidth, RenderSettings.m_nHeight );
pKVData->SetString( "ResolutionID", fmtResolution.Access() );
pKVData->SetString( "CodecID", ReplayVideo_GetCodec( ReplayVideo_FindCodecPresetFromCodec( RenderSettings.m_Codec ) ).m_pName );
pKVData->SetInt( "MotionBlurQuality", RenderSettings.m_nMotionBlurQuality );
pKVData->SetInt( "RenderQuality", RenderSettings.m_nEncodingQuality );
pKVData->SetInt( "FPSUPF", RenderSettings.m_FPS.GetUnitsPerFrame() );
pKVData->SetInt( "FPSUPS", RenderSettings.m_FPS.GetUnitsPerSecond() );
if ( bEnded )
{
pKVData->SetInt( "EndUploadTime", GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch() );
pKVData->SetString( "EndUploadReasonID", pEndReason );
}
else
{
pKVData->SetInt( "StartUploadTime", GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch() );
}
GetSteamWorksSGameStatsUploader().AddStatsForUpload( pKVData );
}
//-----------------------------------------------------------------------------
class CYouTubeLoginWaitDialog : public CGenericWaitingDialog
{
DECLARE_CLASS_SIMPLE( CYouTubeLoginWaitDialog, CGenericWaitingDialog );
public:
CYouTubeLoginWaitDialog( IReplayMovie *pMovie, CConfirmDialog *pLoginDialog )
: CGenericWaitingDialog( pLoginDialog->GetParent() )
, m_pMovie( pMovie )
, m_pLoginDialog( pLoginDialog )
{
}
virtual void OnUserClose()
{
BaseClass::OnUserClose();
YouTube_LoginCancel();
ShowMessageBox( "#YouTube_LoginResults_Title", "#YouTube_LoginResults_Cancel", "#GameUI_OK" );
}
virtual void OnTick()
{
BaseClass::OnTick();
eYouTubeLoginStatus loginStatus = YouTube_GetLoginStatus();
switch ( loginStatus )
{
case kYouTubeLogin_NotLoggedIn:
break;
case kYouTubeLogin_LoggedIn:
Close();
PostMessage( m_pLoginDialog, new KeyValues("Command", "command", "cancel" ), NULL);
YouTube_ShowUploadDialog( m_pMovie, m_pLoginDialog->GetParent() );
break;
case kYouTubeLogin_CouldNotConnect:
Close();
ShowMessageBox( "#YouTube_LoginResults_Title", "#YouTube_LoginResults_CouldNotConnect", "#GameUI_OK" );
break;
case kYouTubeLogin_Forbidden:
Close();
ShowMessageBox( "#YouTube_LoginResults_Title", "#YouTube_LoginResults_Forbidden", "#GameUI_OK" );
break;
case kYouTubeLogin_GenericFailure:
default:
Close();
ShowMessageBox( "#YouTube_LoginResults_Title", "#YouTube_LoginResults_Failure", "#GameUI_OK" );
break;
}
}
private:
IReplayMovie *m_pMovie;
CConfirmDialog *m_pLoginDialog;
};
class CYouTubeUploadWaitDialog : public CGenericWaitingDialog
{
DECLARE_CLASS_SIMPLE( CYouTubeUploadWaitDialog, CGenericWaitingDialog );
public:
CYouTubeUploadWaitDialog( IReplayMovie *pMovie, const char *pTitle, const char *pDescription, YouTubeUploadHandle_t handle, vgui::Panel *pParent )
: CGenericWaitingDialog( pParent )
, m_pMovie( pMovie )
, m_strTitle( pTitle )
, m_strDescription( pDescription )
, m_uploadHandle( handle )
, m_iTick( 0 )
{
}
virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_pProgressBar = dynamic_cast< ProgressBar * >( FindChildByName( "Progress" ) );
}
virtual void PerformLayout()
{
BaseClass::PerformLayout();
if ( m_pProgressBar )
{
m_pProgressBar->SetVisible( true );
m_pProgressBar->SetSegmentInfo( XRES(1), XRES(5) );
}
}
virtual const char* GetResFile() const { return "resource/UI/YouTubeUploadWaitingDialog.res"; }
virtual const char *GetResFilePathId() const { return "GAME"; }
virtual void OnUserClose()
{
BaseClass::OnUserClose();
YouTube_CancelUpload( m_uploadHandle );
UploadOgsData( m_pMovie, true, "cancelled" );
}
virtual void OnTick()
{
BaseClass::OnTick();
double ultotal = 0.0;
double ulnow = 0.0;
if ( YouTube_GetUploadProgress( m_uploadHandle, ultotal, ulnow ) == false )
{
Close();
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_Failure", "#GameUI_OK" );
}
else if ( YouTube_IsUploadFinished( m_uploadHandle ) )
{
bool bSuccess = true;
CUtlString strURLToVideo;
CUtlString strURLToVideoStats;
if ( YouTube_GetUploadResults( m_uploadHandle, bSuccess, strURLToVideo, strURLToVideoStats ) && bSuccess )
{
// mark movie uploaded
m_pMovie->SetUploaded( true );
m_pMovie->SetUploadURL( strURLToVideoStats.Get() );
g_pReplayMovieManager->FlagMovieForFlush( m_pMovie, true );
// update the UI
CReplayDetailsPanel *pDetailsPanel = dynamic_cast< CReplayDetailsPanel *>( GetParent() );
if ( pDetailsPanel )
{
pDetailsPanel->InvalidateLayout( true, false );
}
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_Success", "#GameUI_OK", NULL, GetParent() );
// notify the GC
uint64 uSessionId = g_pClientReplayContext->GetServerSessionId( m_pMovie->GetReplayHandle() );
if ( uSessionId != 0 )
{
GCSDK::CProtoBufMsg< CMsgReplayUploadedToYouTube > msg( k_EMsgGCReplay_UploadedToYouTube );
msg.Body().set_youtube_url( strURLToVideoStats.Get() );
msg.Body().set_youtube_account_name( YouTube_GetLoginName() );
msg.Body().set_session_id( uSessionId );
GCClientSystem()->BSendMessage( msg );
}
surface()->PlaySound( "replay\\youtube_uploadfinished.wav" );
UploadOgsData( m_pMovie, true, "completed" );
// share on Steam Community
if ( steamapicontext && steamapicontext->SteamRemoteStorage() )
{
CUtlString strPreviewFileName;
AppId_t nConsumerAppId = steamapicontext->SteamUtils()->GetAppID();
ERemoteStoragePublishedFileVisibility eVisibility = k_ERemoteStoragePublishedFileVisibilityPublic;
SteamParamStringArray_t tags;
tags.m_ppStrings = NULL;
tags.m_nNumStrings = 0;
// don't bother waiting for result
SteamAPICall_t hSteamAPICall = steamapicontext->SteamRemoteStorage()->PublishVideo(
k_EWorkshopVideoProviderNone, "",
strURLToVideo.Get(),
strPreviewFileName.Get(),
nConsumerAppId,
m_strTitle.Get(),
m_strDescription.Get(),
eVisibility,
&tags
);
hSteamAPICall;
}
}
else
{
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_Failure", "#GameUI_OK" );
surface()->PlaySound( "replay\\youtube_uploadfailed.wav" );
UploadOgsData( m_pMovie, true, "failed" );
}
// close wait dialog
YouTube_ClearUploadResults( m_uploadHandle );
Close();
}
else
{
float flProgress = MIN( ulnow / MAX( ultotal, 1.0 ), 1.0f );
int iProgress = int( 100.0 * flProgress );
int ulnow_kb = uint32( ulnow / 1024 );
int ultotal_kb = uint32( ultotal / 1024 );
if ( ulnow_kb == ultotal_kb )
{
// we tick at 500 ms, so this should be ok
m_iTick = ( m_iTick + 1 ) % 4;
switch ( m_iTick )
{
case 0: SetDialogVariable( "duration", g_pVGuiLocalize->Find( "YouTube_UploadFinishing1" ) ); break;
case 1: SetDialogVariable( "duration", g_pVGuiLocalize->Find( "YouTube_UploadFinishing2" ) ); break;
case 2: SetDialogVariable( "duration", g_pVGuiLocalize->Find( "YouTube_UploadFinishing3" ) ); break;
case 3: SetDialogVariable( "duration", g_pVGuiLocalize->Find( "YouTube_UploadFinishing4" ) ); break;
}
}
else
{
wchar_t wszProgress[1024];
wchar_t wszPercentage[32];
wchar_t wszNow[32];
wchar_t wszTotal[32];
_snwprintf( wszPercentage, ARRAYSIZE( wszPercentage ), L"%u", iProgress );
_snwprintf( wszNow, ARRAYSIZE( wszNow ), L"%u", ulnow_kb );
_snwprintf( wszTotal, ARRAYSIZE( wszTotal ), L"%u", ultotal_kb );
g_pVGuiLocalize->ConstructString_safe( wszProgress, g_pVGuiLocalize->Find( "#YouTube_UploadProgress" ), 3,
wszPercentage,
wszNow,
wszTotal );
SetDialogVariable( "duration", wszProgress );
}
if ( m_pProgressBar )
{
m_pProgressBar->SetProgress( flProgress );
}
}
}
private:
IReplayMovie *m_pMovie;
YouTubeUploadHandle_t m_uploadHandle;
CUtlString m_strTitle;
CUtlString m_strDescription;
ProgressBar *m_pProgressBar;
int m_iTick;
};
//-----------------------------------------------------------------------------
// Purpose: Dialog for logging into YouTube
//-----------------------------------------------------------------------------
class CYouTubeLoginDialog : public CConfirmDialog
{
DECLARE_CLASS_SIMPLE( CYouTubeLoginDialog, CConfirmDialog );
public:
CYouTubeLoginDialog( IReplayMovie *pMovie, Panel *pParent ) : BaseClass( pParent ), m_pMovie( pMovie ) {}
const wchar_t *GetText() { return NULL; }
virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
TextEntry *pTextEntryUserName = dynamic_cast< TextEntry * >( FindChildByName( "UserNameTextEntry" ) );
if ( pTextEntryUserName )
{
pTextEntryUserName->SetText( "" );
pTextEntryUserName->InsertString( youtube_username.GetString() );
}
}
virtual void OnCommand( const char *command )
{
if ( !Q_strnicmp( command, "register", 8 ) )
{
if ( steamapicontext && steamapicontext->SteamFriends() )
{
steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.youtube.com/create_account?next=/" );
}
}
else if ( !Q_strnicmp( command, "confirm", 7 ) )
{
TextEntry *pTextEntryUserName = dynamic_cast< TextEntry * >( FindChildByName( "UserNameTextEntry" ) );
TextEntry *pTextEntryPassword = dynamic_cast< TextEntry * >( FindChildByName( "PasswordTextEntry" ) );
if ( pTextEntryUserName && pTextEntryPassword )
{
char szUserName[256];
pTextEntryUserName->GetText( szUserName, sizeof( szUserName ) );
char szPassword[256];
pTextEntryPassword->GetText( szPassword, sizeof( szPassword ) );
youtube_username.SetValue( szUserName );
Login( szUserName, szPassword );
}
return;
}
BaseClass::OnCommand( command );
}
protected:
virtual const char *GetResFile() { return "Resource/UI/YouTubeLoginDialog.res"; }
virtual const char *GetResFilePathId() { return "GAME"; }
void Login( const char* pUserName, const char *pPassword )
{
const bool bOnSteamPublic = GetUniverse() == k_EUniversePublic;
const char *pGameDir = COM_GetModDirectory();
const char *pSource = NULL;
const char *pDeveloperTag = NULL;
const char *pDeveloperKey = NULL;
GetYouTubeAPIKey( pGameDir, bOnSteamPublic, &pSource, &pDeveloperTag, &pDeveloperKey );
Assert( pSource );
Assert( pDeveloperTag );
Assert( pDeveloperKey );
YouTube_SetDeveloperSettings( pDeveloperKey, pDeveloperTag );
// try to log in
YouTube_Login( pUserName, pPassword, pSource );
CYouTubeLoginWaitDialog *pDialog = new CYouTubeLoginWaitDialog( m_pMovie, this );
ShowWaitingDialog( pDialog, "#YouTube_LoggingIn", true, true, -1 );
}
private:
IReplayMovie *m_pMovie;
};
//-----------------------------------------------------------------------------
// Purpose: Dialog for uploading a file to YouTube
//-----------------------------------------------------------------------------
class CYouTubeUploadDialog : public CConfirmDialog
{
DECLARE_CLASS_SIMPLE( CYouTubeUploadDialog, CConfirmDialog );
public:
CYouTubeUploadDialog( IReplayMovie *pMovie, Panel *pParent ) : BaseClass( pParent ), m_pMovie( pMovie ) {}
const wchar_t *GetText() { return NULL; }
virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_pTextEntryMovieTitle = dynamic_cast< TextEntry * >( FindChildByName( "MovieTitleTextEntry" ) );
m_pTextEntryMovieDesc = dynamic_cast< TextEntry * >( FindChildByName( "MovieDescTextEntry" ) );
// use insert, so that max characters is obeyed
m_pTextEntryMovieTitle->InsertString( m_pMovie->GetItemTitle() );
// @todo add steam profile to description
m_pTextEntryMovieDesc->SetText( "" );
m_pTextEntryMovieDesc->SetMultiline( true );
m_pTextEntryMovieDesc->SetCatchEnterKey( true );
m_pTextEntryMovieDesc->SetVerticalScrollbar( true );
ScrollBar *pScrollbar = dynamic_cast< ScrollBar* >( m_pTextEntryMovieDesc->FindChildByName( "Scrollbar" ) );
if ( pScrollbar )
{
pScrollbar->SetAutohideButtons( false );
pScrollbar->SetScrollbarButtonsVisible( true );
}
m_pUnlistedCheckbox = dynamic_cast< CheckButton * >( FindChildByName( "UnlistedCheckbox" ) );
if ( m_pUnlistedCheckbox )
{
m_pUnlistedCheckbox->SetMouseInputEnabled( true );
}
}
void GetGameNameStrings( const char **ppShortGameName, const char **ppFullGameName )
{
const char *pGameDir = COM_GetModDirectory();
// Team Fortress 2?
if ( FStrEq( pGameDir, "tf" ) )
{
*ppShortGameName = "TF2";
*ppFullGameName = "Team Fortress 2";
}
// Team Fortress 2 Beta?
else if ( FStrEq( pGameDir, "tf_beta" ) )
{
*ppShortGameName = "TF2";
*ppFullGameName = "Team Fortress 2 Beta";
}
// Counter-Strike: Source?
else if ( FStrEq( pGameDir, "cstrike" ) )
{
*ppShortGameName = "CSS";
*ppFullGameName = "Counter-Strike: Source";
}
// Counter-Strike: Source Beta?
else if ( FStrEq( pGameDir, "cstrike_beta" ) )
{
*ppShortGameName = "CSS";
*ppFullGameName = "Counter-Strike: Source Beta";
}
else
{
AssertMsg( 0, "Unknown game" );
*ppShortGameName = "";
*ppFullGameName = "";
}
}
virtual void OnCommand( const char *command )
{
if ( !Q_strnicmp( command, "termsofservice", 14 ) )
{
if ( steamapicontext && steamapicontext->SteamFriends() )
{
steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.youtube.com/t/terms" );
}
}
else if ( !Q_strnicmp( command, "confirm", 7 ) )
{
char szTitle[256];
m_pTextEntryMovieTitle->GetText( szTitle, sizeof( szTitle ) );
char szDesc[2048];
m_pTextEntryMovieDesc->GetText( szDesc, sizeof( szDesc ) );
if ( HasInvalidCharacters( szTitle ) )
{
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_InvalidChars_Title", "#GameUI_OK" );
return;
}
if ( HasInvalidCharacters( szDesc ) )
{
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_InvalidChars_Desc", "#GameUI_OK" );
return;
}
CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( m_pMovie->GetItemReplay() );
CFmtStr fmtMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), m_pMovie->GetMovieFilename() );
const char *pMimeType = "video/mp4";
const char *pTitle = szTitle;
const char *pCategory = "Games";
// add steam profile to the description for verification if necessary
EUniverse eSteamUniverse = GetUniverse();
CUtlString description( szDesc );
if ( steamapicontext && steamapicontext->SteamUser() )
{
const char *pchCommunityURL = "http://steamcommunity.com/";
switch ( eSteamUniverse )
{
case k_EUniverseDev:
pchCommunityURL = "http://localhost/community/";
break;
case k_EUniverseBeta:
pchCommunityURL = "http://beta.steamcommunity.com/";
break;
case k_EUniversePublic:
default:
pchCommunityURL = "http://steamcommunity.com/";
}
description.Format( "%s\n\n%sprofiles/%llu", szDesc, pchCommunityURL, steamapicontext->SteamUser()->GetSteamID().ConvertToUint64() );
}
const char *pShortGameName = NULL;
const char *pFullGameName = NULL;
GetGameNameStrings( &pShortGameName, &pFullGameName );
CFmtStr1024 keywords( "%s Replay, %s, %s, Replay, Valve, %s", pShortGameName, pShortGameName, pFullGameName, pReplay->GetPlayerClass() );
bool bUnlisted = m_pUnlistedCheckbox->IsSelected();
uint64 uSessionId = g_pClientReplayContext->GetServerSessionId( pReplay->GetHandle() );
if ( uSessionId != 0 )
{
char szSessionId[32];
// Write session ID as hex (this code taken from KeyValues.cpp, modified).
#ifdef WIN32
Q_snprintf( szSessionId, sizeof( szSessionId ), "%I64X", uSessionId );
#else
Q_snprintf( szSessionId, sizeof( szSessionId ), "%llX", uSessionId );
#endif
// Add the match tag to the list of keywords.
keywords.AppendFormat( ", match_%s", szSessionId );
}
UploadOgsData( m_pMovie );
YouTubeUploadHandle_t handle = YouTube_Upload( fmtMovieFullFilename.Access(), pMimeType, pTitle, description.Get(), pCategory, keywords.Access(), bUnlisted ? kYouTubeAccessControl_Unlisted : kYouTubeAccessControl_Public );
if ( handle != NULL )
{
// Play a sound
surface()->PlaySound( "replay\\youtube_startingupload.wav" );
CYouTubeUploadWaitDialog *pDialog = new CYouTubeUploadWaitDialog( m_pMovie, pTitle, description.Get(), handle, GetParent() );
ShowWaitingDialog( pDialog, "#YouTube_Uploading", true, true, -1 );
// get rid of this dialog
FinishUp();
}
else
{
ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_Failure", "#GameUI_OK" );
}
return;
}
BaseClass::OnCommand( command );
}
protected:
virtual const char *GetResFile() { return "Resource/UI/YouTubeUploadDialog.res"; }
virtual const char *GetResFilePathId() { return "GAME"; }
private:
IReplayMovie *m_pMovie;
TextEntry *m_pTextEntryMovieTitle;
TextEntry *m_pTextEntryMovieDesc;
CheckButton *m_pUnlistedCheckbox;
};
//-----------------------------------------------------------------------------
void YouTube_ShowLoginDialog( IReplayMovie *pMovie, vgui::Panel *pParent )
{
CYouTubeLoginDialog *pDialog = vgui::SETUP_PANEL( new CYouTubeLoginDialog( pMovie, pParent ) );
pDialog->Show( false );
}
//-----------------------------------------------------------------------------
void YouTube_ShowUploadDialog( IReplayMovie *pMovie, vgui::Panel *pParent )
{
CYouTubeUploadDialog *pDialog = vgui::SETUP_PANEL( new CYouTubeUploadDialog( pMovie, pParent ) );
pDialog->Show( false );
}
//-----------------------------------------------------------------------------
#endif // REPLAY_ENABLED
+32
View File
@@ -0,0 +1,32 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYYOUTUBEAPI_H
#define REPLAYYOUTUBEAPI_H
#ifdef _WIN32
#pragma once
#endif
class IReplayMovie;
namespace vgui { class Panel; };
/**
* Show the YouTube login dialog and attempt to upload the movie if login was successful
* @param pMovie
* @param pParent
*/
void YouTube_ShowLoginDialog( IReplayMovie *pMovie, vgui::Panel *pParent );
/**
*
* Show the YouTube upload dialog and upload the movie
* @param pMovie
* @param pParent
*/
void YouTube_ShowUploadDialog( IReplayMovie *pMovie, vgui::Panel *pParent );
#endif // REPLAYYOUTUBEAPI_H
@@ -0,0 +1,48 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
void GetYouTubeAPIKey( const char *pGameDir, bool bOnSteamPublic, const char **ppSource, const char **ppDeveloperTag, const char **ppDeveloperKey )
{
// Team Fortress 2?
if ( FStrEq( pGameDir, "tf" ) )
{
*ppSource = "Team Fortress 2";
*ppDeveloperTag = "TF2";
*ppDeveloperKey = bOnSteamPublic ?
"AI39si6dQGxX2TWkOT9V9ihFpKmokaDhqIw3mgJcnnRFjX5f00HMRXqj69Fg1ulTdYF9Aw4wIt5NYHCxQAXHPPEcbQ89rEaCeg" :
"AI39si5q3V-l7DhNbdSn-F2P3l0sUOIOnHBqJC5kvdGsuwpIinmcOH5GFC1PGG0olcZM2ID0Fvbsodj6g0pOUkKhuRNxcEErLQ";
}
// Team Fortress 2 Beta?
else if ( FStrEq( pGameDir, "tf_beta" ) )
{
*ppSource = "Team Fortress 2 Beta";
*ppDeveloperTag = "TF2";
*ppDeveloperKey = bOnSteamPublic ?
"AI39si7XuLuXg3-2T06aVUaM-45HSXYFqzXfyPR6y5K4XotWKf78lfCByXnD1T8Kj9jeIR85NelpjGYGsH8pR3RO4k3TrwlTbw" :
"AI39si79TOshUv9FcIT6cjEH0Q9KK_eEOH1q6-_lIdNIsmHrKcal1R8Uf0TzMhdq-rQ7iUEZ3uqSKlLXa2J-lQvuKwNq5SSnMA";
}
// Counter-Strike: Source?
else if ( FStrEq( pGameDir, "cstrike" ) )
{
*ppSource = "Counter-Strike: Source";
*ppDeveloperTag = "CSS";
*ppDeveloperKey = bOnSteamPublic ?
"AI39si7JIn2nj67YoxWPzmsGHO2R-WSpN0su1f3-BF9lVC5jWz9DEOPbOxFz-MiXuaMtoCZnS3nSPjwGfqHenXC6RKGcICI5HQ" :
"AI39si4bpW1q3D0gcWWNWFNHjHgsM7YL3RGCdEAIKV2k_mH5Cwj-YwXinVv933tFhy-6583HcuZ8NWTrvfbB8XTWEI-hYidEjA";
}
// Counter-Strike: Source Beta?
else if ( FStrEq( pGameDir, "cstrike_beta" ) )
{
*ppSource = "Counter-Strike: Source Beta";
*ppDeveloperTag = "CSS";
*ppDeveloperKey = bOnSteamPublic ?
"AI39si5JUyCvGdavFg02OusWk9lkSxkpEX99KnJCjTwOzLJH7N9MS40YMFk-o8dTwyO0w2Qi2CSU8qrB4bdTohHj35mAa0iMDQ" :
"AI39si4Oq2O35MHD5qahqODCKQfsssq5STE6ISolJKsvFuxtPpqhG4sQbDF8pGdZ02c-_s5KRB5nhTjqZMLB4h0lKKHh8I52Tg";
}
}
@@ -0,0 +1,16 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
void GetYouTubeAPIKey( const char *pGameDir, bool bOnSteamPublic, const char **ppSource, const char **ppDeveloperTag, const char **ppDeveloperKey )
{
// Put your YouTube Developer key here if you're going to upload videos to YouTube
*ppSource = "your Mod Name";
*ppDeveloperTag = "Your mod tag";
*ppDeveloperKey = "Your mod's developer key";
}
@@ -0,0 +1,216 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "vgui/IInput.h"
#include "vgui/ISurface.h"
#include "vgui_controls/TextEntry.h"
#include "replaybrowserbasepage.h"
#include "replaybrowserdetailspanel.h"
#include "replaybrowsermainpanel.h"
#include "replaybrowserlistpanel.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/ireplaymanager.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
extern IReplayMovieManager *g_pReplayMovieManager;
//-----------------------------------------------------------------------------
CReplayBrowserBasePage::CReplayBrowserBasePage( Panel *pParent )
: BaseClass( pParent, "BasePage" )
{
m_pReplayList = new CReplayListPanel( this, "ReplayList" );
m_pReplayList->SetFirstColumnWidth( 0 );
m_pSearchTextEntry = new vgui::TextEntry( this, "SearchTextEntry" );
m_pSearchTextEntry->SelectAllOnFocusAlways( true );
m_pSearchTextEntry->AddActionSignalTarget( this );
m_pSearchTextEntry->SetCatchEnterKey( true );
InvalidateLayout( true, true );
m_pReplayList->AddReplaysToList();
ivgui()->AddTickSignal( GetVPanel(), 100 );
}
CReplayBrowserBasePage::~CReplayBrowserBasePage()
{
ivgui()->RemoveTickSignal( GetVPanel() );
}
void CReplayBrowserBasePage::OnTick()
{
if ( !IsVisible() )
return;
int nCursorX, nCursorY;
input()->GetCursorPos( nCursorX, nCursorY );
if ( input()->IsMouseDown( MOUSE_LEFT ) &&
!m_pSearchTextEntry->IsWithin( nCursorX, nCursorY ) &&
m_pSearchTextEntry->HasFocus() )
{
RequestFocus();
}
}
void CReplayBrowserBasePage::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replaybrowser/basepage.res", "GAME" );
m_pSearchTextEntry->SetText( "#Replay_SearchText" );
}
void CReplayBrowserBasePage::OnPageShow()
{
BaseClass::OnPageShow();
m_pSearchTextEntry->SetText( "#Replay_SearchText" );
}
void CReplayBrowserBasePage::OnSelectionStarted()
{
PostActionSignal( new KeyValues("SelectionUpdate", "open", 1 ) );
}
void CReplayBrowserBasePage::OnSelectionEnded()
{
PostActionSignal( new KeyValues("SelectionUpdate", "open", 0 ) );
}
void CReplayBrowserBasePage::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
{
m_pReplayList->CleanupUIForReplayItem( hReplayItem );
}
void CReplayBrowserBasePage::AddReplay( ReplayHandle_t hReplay )
{
m_pReplayList->AddReplayItem( hReplay );
}
void CReplayBrowserBasePage::DeleteReplay( ReplayHandle_t hReplayItem )
{
IReplayItemManager *pItemManager;
if ( FindReplayItem( hReplayItem, &pItemManager ) )
{
ReplayUI_GetBrowserPanel()->AttemptToDeleteReplayItem( this, hReplayItem, pItemManager, -1 );
}
}
void CReplayBrowserBasePage::OnCancelSelection()
{
}
void CReplayBrowserBasePage::GoBack()
{
DeleteDetailsPanelAndShowReplayList();
}
void CReplayBrowserBasePage::OnReplayItemDeleted( KeyValues *pParams )
{
GoBack();
}
void CReplayBrowserBasePage::OnTextChanged( KeyValues *data )
{
wchar_t wszText[256];
m_pSearchTextEntry->GetText( wszText, ARRAYSIZE( wszText ) );
m_pReplayList->ApplyFilter( wszText );
InvalidateLayout();
}
void CReplayBrowserBasePage::OnCommand( const char *pCommand )
{
// User wants details on a replay?
if ( !V_strnicmp( pCommand, "details", 7 ) )
{
// Get rid of preview panel
m_pReplayList->ClearPreviewPanel();
QueryableReplayItemHandle_t hReplayItem = (QueryableReplayItemHandle_t)atoi( pCommand + 7 );
IReplayItemManager *pItemManager;
IQueryableReplayItem *pReplayItem = FindReplayItem( hReplayItem, &pItemManager ); Assert( pReplayItem );
if ( pReplayItem )
{
// Get performance
int iPerformance = -1;
const char *pPerformanceStr = V_strstr( pCommand + 8, "_" );
if ( pPerformanceStr )
{
iPerformance = atoi( pPerformanceStr + 1 );
}
m_hReplayDetailsPanel = vgui::SETUP_PANEL( new CReplayDetailsPanel( this, hReplayItem, iPerformance, pItemManager ) );
m_hReplayDetailsPanel->SetVisible( true );
m_hReplayDetailsPanel->MoveToFront();
m_pReplayList->SetVisible( false );
surface()->PlaySound( "replay\\showdetails.wav" );
}
}
// "back" button was hit in details panel?
else if ( FStrEq( pCommand, "back" ) )
{
GoBack();
}
BaseClass::OnCommand( pCommand );
}
void CReplayBrowserBasePage::DeleteDetailsPanelAndShowReplayList()
{
// Delete the panel
if ( m_hReplayDetailsPanel )
{
m_hReplayDetailsPanel->MarkForDeletion();
m_hReplayDetailsPanel = NULL;
}
m_pReplayList->SetVisible( true );
}
void CReplayBrowserBasePage::PerformLayout()
{
BaseClass::PerformLayout();
if ( m_pSearchTextEntry )
{
const bool bHasReplays = g_pReplayManager && g_pReplayManager->GetReplayCount();
const bool bHasMovies = g_pReplayMovieManager && g_pReplayMovieManager->GetMovieCount();
int aListPos[2];
m_pReplayList->GetPos( aListPos[0], aListPos[1] );
m_pSearchTextEntry->SetPos( aListPos[0] + m_pReplayList->GetWide() - m_pSearchTextEntry->GetWide(), YRES( 5 ) );
m_pSearchTextEntry->SetVisible( bHasReplays || bHasMovies );
}
// Invalidate the list too, because we might be laying out due to a replay being removed from the list.
m_pReplayList->InvalidateLayout();
}
void CReplayBrowserBasePage::FreeDetailsPanelMovieLock()
{
m_hReplayDetailsPanel->FreeMovieFileLock();
}
bool CReplayBrowserBasePage::IsDetailsViewOpen()
{
return m_hReplayDetailsPanel.Get() != NULL && m_hReplayDetailsPanel->IsVisible();
}
#endif
@@ -0,0 +1,67 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYBROWSER_BASEPAGE_H
#define REPLAYBROWSER_BASEPAGE_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/PropertyPage.h"
#include "replaybrowseritemmanager.h"
#include "replay/genericclassbased_replay.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CReplayListPanel;
class CExLabel;
class CReplayDetailsPanel;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayBrowserBasePage : public PropertyPage
{
DECLARE_CLASS_SIMPLE( CReplayBrowserBasePage, PropertyPage );
public:
CReplayBrowserBasePage( Panel *pParent );
virtual ~CReplayBrowserBasePage();
void DeleteDetailsPanelAndShowReplayList();
bool IsDetailsViewOpen();
void GoBack();
// Movie-only stuff
void FreeDetailsPanelMovieLock();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void OnCommand( const char *pCommand );
virtual void PerformLayout();
MESSAGE_FUNC( OnPageShow, "PageShow" );
MESSAGE_FUNC( OnSelectionStarted, "SelectionStarted" );
MESSAGE_FUNC( OnSelectionEnded, "SelectionEnded" );
MESSAGE_FUNC( OnCancelSelection, "CancelSelection" );
MESSAGE_FUNC_PARAMS( OnReplayItemDeleted, "ReplayItemDeleted", pParams );
MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
void AddReplay( ReplayHandle_t hReplay );
void DeleteReplay( ReplayHandle_t hReplay );
void OnTick();
virtual void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
vgui::TextEntry *m_pSearchTextEntry;
CReplayListPanel *m_pReplayList;
DHANDLE< CReplayDetailsPanel > m_hReplayDetailsPanel;
};
#endif // REPLAYBROWSER_BASEPAGE_H
@@ -0,0 +1,39 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowserbasepanel.h"
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
CReplayBasePanel::CReplayBasePanel( Panel *pParent, const char *pName )
: BaseClass( pParent, pName )
{
}
void CReplayBasePanel::GetPosRelativeToAncestor( Panel *pAncestor, int &nXOut, int &nYOut )
{
nXOut = nYOut = 0;
Panel *pCurrent = this;
while ( pCurrent && pCurrent != pAncestor )
{
int x,y;
pCurrent->GetPos( x, y );
nXOut += x;
nYOut += y;
pCurrent = pCurrent->GetParent();
}
Assert( pAncestor == pCurrent );
}
#endif
@@ -0,0 +1,25 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYBASEPANEL_H
#define REPLAYBASEPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/EditablePanel.h"
//-----------------------------------------------------------------------------
// Purpose: Base panel for replay panels
//-----------------------------------------------------------------------------
class CReplayBasePanel : public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayBasePanel, vgui::EditablePanel );
public:
CReplayBasePanel( Panel *pParent, const char *pName );
void GetPosRelativeToAncestor( Panel *pAncestor, int &nXOut, int &nYOut );
};
#endif // REPLAYBASEPANEL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,462 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYBROWSER_DETAILSPANEL_H
#define REPLAYBROWSER_DETAILSPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include <game/client/iviewport.h>
#include "vgui_controls/EditablePanel.h"
#include "vgui_controls/ScrollableEditablePanel.h"
#include "replay/iqueryablereplayitem.h"
#include "replay/ireplaymovie.h"
#include "replay/replayhandle.h"
#include "replay/gamedefs.h"
#include "econ/econ_controls.h"
using namespace vgui;
//-----------------------------------------------------------------------------
#define NUM_CLASSES_IN_LOADOUT_PANEL (TF_LAST_NORMAL_CLASS-1) // We don't allow unlockables for the civilian
//-----------------------------------------------------------------------------
// Purpose: Forward declarations
//-----------------------------------------------------------------------------
class CExLabel;
class CExButton;
class CTFReplay;
class CReplayPerformance;
class IReplayItemManager;
//-----------------------------------------------------------------------------
// Purpose: A panel containing 2 labels: one key, one value
//-----------------------------------------------------------------------------
class CKeyValueLabelPanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CKeyValueLabelPanel, EditablePanel );
public:
CKeyValueLabelPanel( Panel *pParent, const char *pKey, const char *pValue );
CKeyValueLabelPanel( Panel *pParent, const char *pKey, const wchar_t *pValue );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
int GetHeight() const;
int GetValueHeight() const;
void SetValue( const wchar_t *pValue );
private:
CExLabel *m_pLabels[2];
};
//-----------------------------------------------------------------------------
// Purpose: Base details panel with left/top padding and black border
//-----------------------------------------------------------------------------
class CBaseDetailsPanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CBaseDetailsPanel, EditablePanel );
public:
CBaseDetailsPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
int GetMarginSize() const { return XRES(6); }
bool ShouldShow() const { return m_bShouldShow; }
protected:
EditablePanel *GetInset() { return m_pInsetPanel; }
ReplayHandle_t m_hReplay;
bool m_bShouldShow;
private:
EditablePanel *m_pInsetPanel; // padding on left/top
};
//-----------------------------------------------------------------------------
// Purpose: Score panel - contains score & any records from the round
//-----------------------------------------------------------------------------
class CRecordsPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CRecordsPanel, CBaseDetailsPanel );
public:
CRecordsPanel( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
private:
ImagePanel *m_pClassImage;
};
//-----------------------------------------------------------------------------
// Purpose: Stats panel
//-----------------------------------------------------------------------------
class CStatsPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CStatsPanel, CBaseDetailsPanel );
public:
CStatsPanel( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
private:
CKeyValueLabelPanel *m_paStatLabels[ REPLAY_MAX_DISPLAY_GAMESTATS ];
};
//-----------------------------------------------------------------------------
// Purpose: Dominations panel
//-----------------------------------------------------------------------------
class CDominationsPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CDominationsPanel, CBaseDetailsPanel );
public:
CDominationsPanel( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
ImagePanel *m_pNumDominationsImage;
CUtlVector< ImagePanel * > m_vecDominationImages;
};
//-----------------------------------------------------------------------------
// Purpose: Kills panel
//-----------------------------------------------------------------------------
class CKillsPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CKillsPanel, CBaseDetailsPanel );
public:
CKillsPanel( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
CKeyValueLabelPanel *m_pKillLabels;
CUtlVector< ImagePanel * > m_vecKillImages;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CBasicLifeInfoPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CBasicLifeInfoPanel, CBaseDetailsPanel );
public:
CBasicLifeInfoPanel( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
private:
CKeyValueLabelPanel *m_pKilledByLabels;
CKeyValueLabelPanel *m_pMapLabels;
CKeyValueLabelPanel *m_pLifeLabels;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CMovieInfoPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CMovieInfoPanel, CBaseDetailsPanel );
public:
CMovieInfoPanel( Panel *pParent, ReplayHandle_t hReplay, QueryableReplayItemHandle_t hMovie,
IReplayItemManager *pItemManager );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
private:
CKeyValueLabelPanel *m_pRenderTimeLabels;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CYouTubeInfoPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CYouTubeInfoPanel, CBaseDetailsPanel );
public:
CYouTubeInfoPanel( Panel *pParent );
virtual void PerformLayout();
void SetInfo( const wchar_t *pInfo );
private:
CKeyValueLabelPanel *m_pLabels;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CTitleEditPanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CTitleEditPanel, EditablePanel );
public:
CTitleEditPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager );
~CTitleEditPanel();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void PaintBackground();
virtual void OnKeyCodeTyped(vgui::KeyCode code);
virtual void OnTick();
bool m_bMouseOver;
TextEntry *m_pTitleEntry;
ImagePanel *m_pHeaderLine;
CExLabel *m_pClickToEditLabel;
CExLabel *m_pCaratLabel;
QueryableReplayItemHandle_t m_hReplayItem;
IReplayItemManager *m_pItemManager;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayScreenshotSlideshowPanel;
class CPlaybackPanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CPlaybackPanel, EditablePanel );
public:
CPlaybackPanel( Panel *pParent );
~CPlaybackPanel();
virtual void FreeMovieMaterial() {}
protected:
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
inline int GetMarginSize() { return 9; }
inline int GetViewWidth() { return GetWide() - 2 * GetMarginSize(); }
inline int GetViewHeight() { return GetTall() - 2 * GetMarginSize(); }
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CPlaybackPanelSlideshow : public CPlaybackPanel
{
DECLARE_CLASS_SIMPLE( CPlaybackPanelSlideshow, CPlaybackPanel );
public:
CPlaybackPanelSlideshow( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
private:
ReplayHandle_t m_hReplay;
CExLabel *m_pNoScreenshotLabel;
CReplayScreenshotSlideshowPanel *m_pScreenshotImage;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CMoviePlayerPanel;
class CPlaybackPanelMovie : public CPlaybackPanel
{
DECLARE_CLASS_SIMPLE( CPlaybackPanelMovie, CPlaybackPanel );
public:
CPlaybackPanelMovie( Panel *pParent, ReplayHandle_t hReplay );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void FreeMovieMaterial();
private:
CExLabel *m_pLoadingLabel;
CMoviePlayerPanel *m_pMoviePlayerPanel;
ReplayHandle_t m_hMovie;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CCutImagePanel : public CExImageButton
{
DECLARE_CLASS_SIMPLE( CCutImagePanel, CExImageButton );
public:
CCutImagePanel( Panel *pParent, const char *pName );
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void SetSelected( bool bState );
private:
virtual IBorder *GetBorder( bool bDepressed, bool bArmed, bool bSelected, bool bKeyFocus );
IBorder *m_pSelectedBorder;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayDetailsPanel;
class CCutsPanel : public CBaseDetailsPanel
{
DECLARE_CLASS_SIMPLE( CCutsPanel, CBaseDetailsPanel );
public:
CCutsPanel( Panel *pParent, ReplayHandle_t hReplay, int iPerformance );
~CCutsPanel();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnCommand( const char *pCommand );
virtual void ApplySettings( KeyValues *pInResourceData );
void OnPerformanceDeleted( int iPerformance );
CPanelAnimationVarAliasType( int, m_nCutButtonWidth, "cut_button_width", "0", "proportional_xpos" );
CPanelAnimationVarAliasType( int, m_nCutButtonHeight, "cut_button_height", "0", "proportional_ypos" );
CPanelAnimationVarAliasType( int, m_nCutButtonBuffer, "cut_button_buffer", "0", "proportional_xpos" );
CPanelAnimationVarAliasType( int, m_nCutButtonSpace, "cut_button_space", "0", "proportional_xpos" );
CPanelAnimationVarAliasType( int, m_nCutButtonSpaceWide, "cut_button_space_wide", "0", "proportional_xpos" );
CPanelAnimationVarAliasType( int, m_nTopMarginHeight, "top_margin_height", "0", "proportional_ypos" );
CPanelAnimationVarAliasType( int, m_nNameLabelTopMargin, "name_label_top_margin", "0", "proportional_ypos" );
CPanelAnimationVarAliasType( int, m_nButtonStartY, "button_start_y", "0", "proportional_ypos" );
void UpdateNameLabel( int iPerformance );
private:
void SelectButtonFromPerformance( int iPerformance );
void SetPage( int iPage, int iButtonToSelect = 0 );
int ButtonToPerformance( int iButton ) const;
int PerformanceToButton( int iPerformance ) const;
const CReplayPerformance *GetPerformance( int iPerformance ) const;
virtual void OnTick();
struct ButtonInfo_t
{
CExImageButton *m_pButton;
CExButton *m_pAddToRenderQueueButton;
int m_iPerformance;
};
enum Consts_t
{
BUTTONS_PER_PAGE = 4
};
ButtonInfo_t m_aButtons[ BUTTONS_PER_PAGE ];
EditablePanel *m_pVerticalLine;
CExLabel *m_pNoCutsLabel;
CExLabel *m_pOriginalLabel;
CExLabel *m_pCutsLabel;
CExLabel *m_pNameLabel;
CExButton *m_pPrevButton;
CExButton *m_pNextButton;
int m_iPage;
int m_nVisibleButtons;
vgui::DHANDLE< CReplayDetailsPanel > m_hDetailsPanel;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class IReplayItemManager;
class CConfirmDialog;
class CYouTubeGetStatsHandler;
class CReplayDetailsPanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayDetailsPanel, EditablePanel );
public:
CReplayDetailsPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, int iPerformance, IReplayItemManager *pItemManager );
~CReplayDetailsPanel();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnMousePressed( MouseCode code );
virtual void OnKeyCodeTyped( KeyCode code );
virtual void OnCommand( const char *pCommand );
virtual void OnMessage( const KeyValues* pParams, VPANEL hFromPanel );
EditablePanel *GetInset() { return m_pInsetPanel; }
void ShowRenderDialog();
void FreeMovieFileLock();
void ShowExportDialog();
static void OnPlayerWarningDlgConfirm( bool bConfirmed, void *pContext );
enum eYouTubeStatus
{
kYouTubeStatus_Private,
kYouTubeStatus_RetrievingInfo,
kYouTubeStatus_RetrievedInfo,
kYouTubeStatus_CouldNotRetrieveInfo,
kYouTubeStatus_NotUploaded
};
void SetYouTubeStatus( eYouTubeStatus status );
EditablePanel *m_pInsetPanel; // Parent to most child panels listed here - narrower than screen width
EditablePanel *m_pInfoPanel; // Container for info panels
ScrollableEditablePanel *m_pScrollPanel;
CPlaybackPanel *m_pPlaybackPanel; // Contains screenshot, playback button
CRecordsPanel *m_pRecordsPanel; // Contains score, records
CStatsPanel *m_pStatsPanel; // Contains stats
CDominationsPanel *m_pDominationsPanel; // Dominations
CBasicLifeInfoPanel *m_pBasicInfoPanel; // Killed by, map, life
CKillsPanel *m_pKillsPanel; // # kills, kill class icons
CYouTubeInfoPanel *m_pYouTubeInfoPanel; // YouTube Info
CCutsPanel *m_pCutsPanel; // Buttons for performances
CUtlVector< CBaseDetailsPanel* > m_vecInfoPanels; // List of panels on the right
CTitleEditPanel *m_pTitleEditPanel;
CExButton *m_pBackButton;
CExButton *m_pDeleteButton;
CExButton *m_pRenderButton;
CExButton *m_pPlayButton;
CExButton *m_pExportMovie;
CExButton *m_pYouTubeUpload;
CExButton *m_pYouTubeView;
CExButton *m_pYouTubeShareURL;
CExImageButton *m_pShowRenderInfoButton;
QueryableReplayItemHandle_t m_hReplayItem;
ReplayHandle_t m_hReplay;
IReplayItemManager *m_pItemManager;
int m_iSelectedPerformance; // Which performance to play/render/delete
CYouTubeGetStatsHandler *m_pYouTubeResponseHandler;
vgui::FileOpenDialog *m_hExportMovieDialog;
private:
void ShowRenderInfo();
MESSAGE_FUNC_PARAMS( OnConfirmDisconnect, "ConfirmDlgResult", data );
MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
CPanelAnimationVarAliasType( int, m_nMarginWidth, "margin_width", "0", "proportional_xpos" );
void GoBack();
void ShowPlayConfirmationDialog();
};
#endif // REPLAYBROWSER_DETAILSPANEL_H
@@ -0,0 +1,133 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowseritemmanager.h"
#include "replaybrowserbasepage.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/ireplaymanager.h"
#include "replay/ireplaymovie.h"
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
extern IClientReplayContext *g_pClientReplayContext;
extern IReplayMovieManager *g_pReplayMovieManager;
//-----------------------------------------------------------------------------
class CReplayItemManager : public IReplayItemManager
{
public:
virtual int GetItemCount()
{
return g_pReplayManager->GetReplayCount();
}
virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items )
{
g_pReplayManager->GetReplaysAsQueryableItems( items );
}
virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem )
{
return static_cast< CReplay * >( g_pReplayManager->GetReplay( (ReplayHandle_t)hItem ) );
}
virtual bool AreItemsMovies()
{
return false;
}
virtual void DeleteItem( Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI )
{
g_pReplayManager->DeleteReplay( (ReplayHandle_t)hItem, bNotifyUI );
}
};
//-----------------------------------------------------------------------------
class CMovieItemManager : public IReplayItemManager
{
public:
virtual int GetItemCount()
{
return g_pReplayMovieManager->GetMovieCount();
}
virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items )
{
g_pReplayMovieManager->GetMoviesAsQueryableItems( items );
}
virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem )
{
return g_pReplayMovieManager->GetMovie( (ReplayHandle_t)hItem );
}
virtual bool AreItemsMovies()
{
return true;
}
virtual void DeleteItem( Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI )
{
CReplayBrowserBasePage *pBasePage = static_cast< CReplayBrowserBasePage * >( pPage );
// Free the lock so the file is deletable
pBasePage->FreeDetailsPanelMovieLock();
// Delete the entry & the file
g_pReplayMovieManager->DeleteMovie( hItem );
}
};
//-----------------------------------------------------------------------------
static CReplayItemManager s_ReplayItemManager;
static CMovieItemManager s_MovieItemManager;
//-----------------------------------------------------------------------------
IReplayItemManager *GetReplayItemManager()
{
return &s_ReplayItemManager;
}
IReplayItemManager *GetReplayMovieItemManager()
{
return &s_MovieItemManager;
}
IQueryableReplayItem *FindReplayItem( ReplayItemHandle_t hItem, IReplayItemManager **ppItemManager )
{
static IReplayItemManager *s_pItemManagers[] = { &s_ReplayItemManager, &s_MovieItemManager };
if ( ppItemManager )
{
*ppItemManager = NULL;
}
for ( int i = 0; i < 2; ++i )
{
IQueryableReplayItem *pItem = s_pItemManagers[ i ]->GetItem( hItem );
if ( pItem )
{
if ( ppItemManager )
{
*ppItemManager = s_pItemManagers[ i ];
}
return pItem;
}
}
return NULL;
}
#endif
@@ -0,0 +1,40 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYBROWSER_ITEMMANAGER_H
#define REPLAYBROWSER_ITEMMANAGER_H
#ifdef _WIN32
#pragma once
#endif
#include "utllinkedlist.h"
#include <vgui_controls/Panel.h>
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
typedef int ReplayItemHandle_t;
//-----------------------------------------------------------------------------
// Purpose: Layer of abstraction between UI and replay demos or rendered movies
//-----------------------------------------------------------------------------
class IQueryableReplayItem;
abstract_class IReplayItemManager : public IBaseInterface
{
public:
virtual int GetItemCount() = 0;
virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items ) = 0;
virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem ) = 0;
virtual bool AreItemsMovies() = 0;
virtual void DeleteItem( vgui::Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI ) = 0;
};
IReplayItemManager *GetReplayItemManager();
IReplayItemManager *GetReplayMovieItemManager();
// Find an item and put the item manager in ppItemManager
IQueryableReplayItem *FindReplayItem( ReplayItemHandle_t hItem, IReplayItemManager **ppItemManager );
#endif // REPLAYBROWSER_ITEMMANAGER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,239 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYLISTITEMPANEL_H
#define REPLAYLISTITEMPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include "replaybrowserbasepanel.h"
#include "replaybrowseritemmanager.h"
#include "replay/genericclassbased_replay.h"
#include "game_controls/slideshowpanel.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: Slideshow panel that adds all screenshots associated
// with a given replay.
//-----------------------------------------------------------------------------
class CReplayScreenshotSlideshowPanel : public CSlideshowPanel
{
DECLARE_CLASS_SIMPLE( CReplayScreenshotSlideshowPanel, CSlideshowPanel );
public:
CReplayScreenshotSlideshowPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
virtual void PerformLayout();
private:
ReplayHandle_t m_hReplay;
};
//-----------------------------------------------------------------------------
// Purpose: An individual Replay thumbnail, with download button, title, etc.
//-----------------------------------------------------------------------------
class CExButton;
class CExLabel;
class IReplayItemManager;
class CMoviePlayerPanel;
class CReplayBrowserThumbnail : public CReplayBasePanel
{
DECLARE_CLASS_SIMPLE( CReplayBrowserThumbnail, CReplayBasePanel );
public:
CReplayBrowserThumbnail( Panel *pParent, const char *pName, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pReplayItemManager );
~CReplayBrowserThumbnail();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnMousePressed( MouseCode code );
virtual void OnTick();
virtual void OnCommand( const char *pCommand );
void UpdateTitleText();
void SetReplayItem( QueryableReplayItemHandle_t hReplayItem );
CGenericClassBasedReplay *GetReplay();
IQueryableReplayItem *GetReplayItem();
MESSAGE_FUNC_PARAMS( OnDownloadClicked, "Download", pParams );
MESSAGE_FUNC_PARAMS( OnDeleteReplay, "delete_replayitem", pParams );
CCrossfadableImagePanel *m_pScreenshotThumb;
QueryableReplayItemHandle_t m_hReplayItem;
private:
void SetupReplayItemUserData( void *pUserData );
void UpdateProgress( bool bDownloadPhase, const CReplay *pReplay );
Label *m_pTitle;
Label *m_pDownloadLabel;
Label *m_pRecordingInProgressLabel;
ProgressBar *m_pDownloadProgress;
CExButton *m_pDownloadButton;
CExButton *m_pDeleteButton;
Label *m_pErrorLabel;
CMoviePlayerPanel *m_pMoviePlayer;
Panel *m_pDownloadOverlay;
EditablePanel *m_pBorderPanel;
Color m_clrHighlight;
Color m_clrDefaultBg;
bool m_bMouseOver;
IReplayItemManager *m_pReplayItemManager;
float m_flLastMovieScrubTime;
float m_flHoverStartTime;
float m_flLastProgressChangeTime;
};
//-----------------------------------------------------------------------------
// Purpose: A row of Replay thumbnails (CReplayBrowserThumbnail's)
//-----------------------------------------------------------------------------
class CReplayBrowserThumbnailRow : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayBrowserThumbnailRow, EditablePanel );
public:
CReplayBrowserThumbnailRow( Panel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
void AddReplayThumbnail( const IQueryableReplayItem *pReplay );
void AddReplayThumbnail( QueryableReplayItemHandle_t hReplayItem );
void DeleteReplayItemThumbnail( const IQueryableReplayItem *pReplayItem );
int GetNumReplayItems() const { return m_vecThumbnails.Count(); }
int GetNumVisibleReplayItems() const;
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
CReplayBrowserThumbnail *FindThumbnail( const IQueryableReplayItem *pReplay );
CUtlVector< CReplayBrowserThumbnail * > m_vecThumbnails;
IReplayItemManager *m_pReplayItemManager;
};
//-----------------------------------------------------------------------------
// Purpose: A collection of CReplayBrowserThumbnailRows containing replays
// recorded on a given day.
//-----------------------------------------------------------------------------
class CExLabel;
class CExButton;
class CReplayListPanel;
class CBaseThumbnailCollection : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CBaseThumbnailCollection, EditablePanel );
public:
CBaseThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
void AddReplay( const IQueryableReplayItem *pItem );
virtual bool IsMovieCollection() const = 0;
void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
virtual void PerformLayout();
virtual void ApplySchemeSettings( IScheme *pScheme );
void RemoveEmptyRows();
void RemoveAll();
void OnUpdated();
void OnCommand( const char *pCommand );
CReplayBrowserThumbnailRow *FindReplayItemThumbnailRow( const IQueryableReplayItem *pReplayItem );
inline int GetNumRows() const { return m_vecRows.Count(); }
typedef CUtlVector< CReplayBrowserThumbnailRow * > RowContainer_t;
RowContainer_t m_vecRows;
protected:
// Called from PerformLayout() - layout any panels that should appear at the top (vertically)-most position
virtual void LayoutUpperPanels( int nStartY, int nBgWidth ) = 0;
virtual void LayoutBackgroundPanel( int nWide, int nTall ) {}
virtual Panel *GetLowestPanel( int &nVerticalBuffer ) = 0;
void UpdateViewingPage( void );
int m_nStartX;
protected:
CExLabel *m_pNoReplayItemsLabel;
IReplayItemManager *m_pReplayItemManager;
CExButton *m_pShowNextButton;
CExButton *m_pShowPrevButton;
CUtlVector<ReplayItemHandle_t> m_vecReplays;
int m_iViewingPage;
int m_nReplayThumbnailsPerRow;
int m_nMaxRows;
CExLabel *m_pCaratLabel;
CExLabel *m_pTitleLabel;
CExButton *m_pRenderAllButton;
private:
int GetRowStartY();
CReplayListPanel *m_pParentListPanel; // Parent gets altered so we keep this cached ptr around
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayThumbnailCollection : public CBaseThumbnailCollection
{
DECLARE_CLASS_SIMPLE( CReplayThumbnailCollection, CBaseThumbnailCollection );
public:
CReplayThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
virtual bool IsMovieCollection() const;
virtual void PerformLayout();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void LayoutUpperPanels( int nStartY, int nBgWidth );
virtual void LayoutBackgroundPanel( int nWide, int nTall );
virtual Panel *GetLowestPanel( int &nVerticalBuffer );
Panel *m_pLinePanel;
CExLabel *m_pWarningLabel;
Panel *m_pUnconvertedBg;
};
#define OLDER_MOVIES_COLLECTION -2
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CMovieThumbnailCollection : public CBaseThumbnailCollection
{
DECLARE_CLASS_SIMPLE( CMovieThumbnailCollection, CBaseThumbnailCollection );
public:
CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager,
int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel );
CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager,
bool bShowSavedMoviesLabel );
bool DoesDateMatch( int nDay, int nMonth, int nYear );
virtual bool IsMovieCollection() const;
private:
void Init( int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel );
virtual void PerformLayout();
virtual void ApplySchemeSettings( IScheme *pScheme );
Panel *GetLowestPanel( int &nVerticalBuffer );
void LayoutUpperPanels( int nStartY, int nBgWidth );
int m_nDay, m_nMonth, m_nYear;
CExLabel *m_pDateLabel;
bool m_bShowSavedMoviesLabel;
};
#endif // REPLAYLISTITEMPANEL_H
@@ -0,0 +1,484 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowserlistpanel.h"
#include "ienginevgui.h"
#include "vgui/ISurface.h"
#include "vgui/IInput.h"
#include "vgui/IVGui.h"
#include "vgui_controls/ScrollBar.h"
#include "vgui_controls/ScrollBarSlider.h"
#include "replaybrowserlistitempanel.h"
#include "replaybrowserpreviewpanel.h"
#include "replaybrowserbasepage.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/ireplaymanager.h"
#include "replaybrowsermainpanel.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
extern IClientReplayContext *g_pClientReplayContext;
extern IReplayMovieManager *g_pReplayMovieManager;
extern const char *GetMapDisplayName( const char *mapName );
//-----------------------------------------------------------------------------
DECLARE_BUILD_FACTORY( CReplayListPanel );
//-----------------------------------------------------------------------------
#define MAX_MOVIE_THUMBNAILS 12 // The remaining movies will be put into a single collection
//-----------------------------------------------------------------------------
CReplayListPanel::CReplayListPanel( Panel *pParent, const char *pName )
: BaseClass( pParent, pName ),
m_pPrevHoverPanel( NULL ),
m_pPreviewPanel( NULL )
{
ivgui()->AddTickSignal( GetVPanel(), 10 );
m_pBorderArrowImg = new ImagePanel( this, "ArrowImage" );
// Add replays and movies collections, which will contain all replays & movies.
m_pReplaysCollection = new CReplayThumbnailCollection( this, "ReplayThumbnailCollection", GetReplayItemManager() );
m_pMoviesCollection = new CMovieThumbnailCollection( this, "MovieThumbnailCollection", GetReplayMovieItemManager(), true );
m_vecCollections.AddToTail( m_pReplaysCollection );
m_vecCollections.AddToTail( m_pMoviesCollection );
m_wszFilter[0] = L' ';
m_wszFilter[1] = NULL;
}
CReplayListPanel::~CReplayListPanel()
{
ivgui()->RemoveTickSignal( GetVPanel() );
}
void CReplayListPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replaybrowser/replaylistpanel.res", "GAME" );
#if !defined( TF_CLIENT_DLL )
SetPaintBorderEnabled( false );
#endif
MoveScrollBarToTop();
vgui::ScrollBar *pScrollBar = dynamic_cast< vgui::ScrollBar * >( FindChildByName( "PanelListPanelVScroll" ) );
pScrollBar->SetScrollbarButtonsVisible( false );
Color clrButtonColor = GetSchemeColor( "Yellow", Color( 255, 255, 255, 255 ), pScheme );
Color clrBgColor = GetSchemeColor( "TanDark", Color( 255, 255, 255, 255 ), pScheme );
const int nWidth = XRES( 5 );
pScrollBar->SetSize( nWidth, GetTall() );
pScrollBar->GetSlider()->SetSize( nWidth, GetTall() );
}
void CReplayListPanel::PerformLayout()
{
BaseClass::PerformLayout();
}
void CReplayListPanel::OnMouseWheeled(int delta)
{
if ( !GetScrollbar()->IsVisible() )
return;
BaseClass::OnMouseWheeled( delta );
}
void CReplayListPanel::SetupBorderArrow( bool bLeft )
{
m_pBorderArrowImg->SetVisible( true );
m_pBorderArrowImg->SetImage( bLeft ? "replay/replay_balloon_arrow_left" : "replay/replay_balloon_arrow_right" );
m_pBorderArrowImg->SetZPos( 1000 );
m_pBorderArrowImg->GetImage()->GetContentSize( m_aBorderArrowDims[0], m_aBorderArrowDims[1] );
m_pBorderArrowImg->SetSize( m_aBorderArrowDims[0], m_aBorderArrowDims[1] );
}
void CReplayListPanel::ClearPreviewPanel()
{
if ( m_pPreviewPanel )
{
m_pPreviewPanel->MarkForDeletion();
m_pPreviewPanel = NULL;
}
}
void CReplayListPanel::ApplyFilter( const wchar_t *pFilterText )
{
Q_wcsncpy( m_wszFilter, pFilterText, sizeof( m_wszFilter ) );
V_wcslower( m_wszFilter );
m_pReplaysCollection->RemoveAll();
m_pMoviesCollection->RemoveAll();
m_pPrevHoverPanel = NULL;
ClearPreviewPanel();
RemoveAll();
AddReplaysToList();
FOR_EACH_VEC( m_vecCollections, i )
{
m_vecCollections[i]->OnUpdated();
}
InvalidateLayout();
}
void CReplayListPanel::OnTick()
{
if ( !enginevgui->IsGameUIVisible() )
return;
CReplayBrowserPanel *pReplayBrowser = ReplayUI_GetBrowserPanel();
if ( !pReplayBrowser || !pReplayBrowser->IsVisible() )
return;
int x,y;
vgui::input()->GetCursorPos(x, y);
// If the deletion confirmation dialog is up
if ( vgui::input()->GetAppModalSurface() )
{
ClearPreviewPanel();
// Hide the preview arrow
m_pBorderArrowImg->SetVisible( false );
return;
}
CReplayBrowserThumbnail *pOverPanel = FindThumbnailAtCursor( x, y );
if ( m_pPrevHoverPanel != pOverPanel )
{
if ( m_pPrevHoverPanel )
{
OnItemPanelExited( m_pPrevHoverPanel );
}
m_pPrevHoverPanel = pOverPanel;
if ( m_pPrevHoverPanel )
{
OnItemPanelEntered( m_pPrevHoverPanel );
}
}
}
void CReplayListPanel::OnItemPanelEntered( vgui::Panel *pPanel )
{
CReplayBrowserThumbnail *pThumbnail = dynamic_cast< CReplayBrowserThumbnail * >( pPanel );
if ( IsVisible() && pThumbnail && pThumbnail->IsVisible() )
{
ClearPreviewPanel();
// Determine which type of preview panel to display
IReplayItemManager *pItemManager;
IQueryableReplayItem *pReplayItem = FindReplayItem( pThumbnail->m_hReplayItem, &pItemManager );
AssertMsg( pReplayItem, "Why is this happening?" );
if ( !pReplayItem )
return;
if ( pReplayItem->IsItemAMovie() )
{
m_pPreviewPanel = new CReplayPreviewPanelBase( this, pReplayItem->GetItemHandle(), pItemManager );
}
else
{
m_pPreviewPanel = new CReplayPreviewPanelSlideshow( this, pReplayItem->GetItemHandle(), pItemManager );
}
m_pPreviewPanel->InvalidateLayout( true, true );
int x,y;
pThumbnail->GetPosRelativeToAncestor( this, x, y );
int nXPos, nYPos;
int nOffset = XRES( 1 );
nXPos = ( x > GetWide()/2 ) ? ( x - m_pPreviewPanel->GetWide() - nOffset ) : ( x + pThumbnail->GetWide() + nOffset );
nYPos = y + ( pThumbnail->GetTall() - m_pPreviewPanel->GetTall() ) / 2;
// Make sure the popup stays onscreen.
if ( nXPos < 0 )
{
nXPos = 0;
}
else if ( (nXPos + m_pPreviewPanel->GetWide()) > GetWide() )
{
nXPos = GetWide() - m_pPreviewPanel->GetWide();
}
if ( nYPos < 0 )
{
nYPos = 0;
}
else if ( (nYPos + m_pPreviewPanel->GetTall()) > GetTall() )
{
// Move it up as much as we can without it going below the bottom
nYPos = GetTall() - m_pPreviewPanel->GetTall();
}
// Setup the balloon's arrow
bool bLeftArrow = x < (GetWide() / 2);
SetupBorderArrow( bLeftArrow ); // Sets proper image and caches image dims in m_aBorderArrowDims
int nArrowXPos, nArrowYPos;
const int nPreviewBorderWidth = 2; // Should be just big enough to cover the preview's border width
if ( bLeftArrow )
{
// Setup the arrow along the left-hand side
nArrowXPos = nXPos - m_aBorderArrowDims[0] + nPreviewBorderWidth;
}
else
{
nArrowXPos = nXPos + m_pPreviewPanel->GetWide() - nPreviewBorderWidth;
}
nArrowYPos = MIN( nYPos + m_pPreviewPanel->GetTall() - m_pBorderArrowImg->GetTall() * 2, y + ( pThumbnail->m_pScreenshotThumb->GetTall() - m_aBorderArrowDims[1] ) / 2 );
m_pBorderArrowImg->SetPos( nArrowXPos, nArrowYPos );
m_pPreviewPanel->SetPos( nXPos, nYPos );
m_pPreviewPanel->SetVisible( true );
surface()->PlaySound( "replay\\replaypreviewpopup.wav" );
}
}
void CReplayListPanel::OnItemPanelExited( vgui::Panel *pPanel )
{
CReplayBrowserThumbnail *pThumbnail = dynamic_cast < CReplayBrowserThumbnail * > ( pPanel );
if ( pThumbnail && IsVisible() && m_pPreviewPanel )
{
m_pBorderArrowImg->SetVisible( false );
ClearPreviewPanel();
}
}
CBaseThumbnailCollection *CReplayListPanel::FindOrAddReplayThumbnailCollection( const IQueryableReplayItem *pItem, IReplayItemManager *pItemManager )
{
Assert( pItem );
if ( pItem->IsItemAMovie() )
{
return m_pMoviesCollection;
}
return m_pReplaysCollection;
}
void CReplayListPanel::AddReplaysToList()
{
// Cache off list item pointers into a temp list for processing
CUtlLinkedList< IQueryableReplayItem *, int > lstMovies;
CUtlLinkedList< IQueryableReplayItem *, int > lstReplays;
// Add all replays to a replays list
g_pReplayManager->GetReplaysAsQueryableItems( lstReplays );
// Add all movies to a movies list
g_pReplayMovieManager->GetMoviesAsQueryableItems( lstMovies );
// Go through all movies, and add them to the proper collection, based on date
FOR_EACH_LL( lstMovies, i )
{
if ( PassesFilter( lstMovies[ i ] ) )
{
AddReplayItem( lstMovies[ i ]->GetItemHandle() );
}
}
// Add any replays to the "temporary replays" collection
FOR_EACH_LL( lstReplays, i )
{
if ( PassesFilter( lstReplays[ i ] ) )
{
m_pReplaysCollection->AddReplay( lstReplays[ i ] );
}
}
// Add all collection panels to the list panel
FOR_EACH_VEC( m_vecCollections, i )
{
AddItem( NULL, m_vecCollections[ i ] );
}
}
void CReplayListPanel::RemoveCollection( CBaseThumbnailCollection *pCollection )
{
// Never remove our two base collections. If they have no entries, they display messages instead.
if ( pCollection == m_pMoviesCollection || pCollection == m_pReplaysCollection )
return;
// Find the item and remove it
int i = FirstItem();
while ( i != InvalidItemID() )
{
if ( GetItemPanel( i ) == pCollection )
{
int nNextI = NextItem( i );
RemoveItem( i );
i = nNextI;
}
else
{
i = NextItem( i );
}
}
// Remove our own cached ptr
i = m_vecCollections.Find( pCollection );
if ( i != m_vecCollections.InvalidIndex() )
{
m_vecCollections.Remove( i );
}
}
CReplayBrowserThumbnail *CReplayListPanel::FindThumbnailAtCursor( int x, int y )
{
// Is the cursor hovering over any of the thumbnails?
FOR_EACH_VEC( m_vecCollections, i )
{
CBaseThumbnailCollection *pCollection = m_vecCollections[ i ];
if ( pCollection->IsWithin( x, y ) )
{
FOR_EACH_VEC( pCollection->m_vecRows, j )
{
CReplayBrowserThumbnailRow *pRow = pCollection->m_vecRows[ j ];
if ( pRow->IsWithin( x, y ) )
{
FOR_EACH_VEC( pRow->m_vecThumbnails, k )
{
CReplayBrowserThumbnail *pThumbnail = pRow->m_vecThumbnails[ k ];
if ( pThumbnail->IsWithin( x, y ) )
{
return pThumbnail;
}
}
}
}
}
}
return NULL;
}
#if defined( WIN32 )
#define Q_wcstok( text, delimiters, context ) wcstok( text, delimiters ); context;
#elif defined( OSX ) || defined( LINUX )
#define Q_wcstok( text, delimiters, context ) wcstok( text, delimiters, context )
#endif
bool CReplayListPanel::PassesFilter( IQueryableReplayItem *pItem )
{
CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( pItem->GetItemReplay() );
if ( !pReplay )
return false;
wchar_t wszSearchableText[1024] = L"";
wchar_t wszTemp[256];
// title
const wchar_t *pTitle = pItem->GetItemTitle();
V_wcscat_safe( wszSearchableText, pTitle );
V_wcscat_safe( wszSearchableText, L" " );
// map
const char *pMapName = GetMapDisplayName( pReplay->m_szMapName );
g_pVGuiLocalize->ConvertANSIToUnicode( pMapName, wszTemp, sizeof( wszTemp ) );
V_wcscat_safe( wszSearchableText, wszTemp );
V_wcscat_safe( wszSearchableText, L" " );
// player class
g_pVGuiLocalize->ConvertANSIToUnicode( pReplay->GetPlayerClass(), wszTemp, sizeof( wszTemp ) );
V_wcscat_safe( wszSearchableText, wszTemp );
V_wcscat_safe( wszSearchableText, L" " );
// killer name
if ( pReplay->WasKilled() )
{
g_pVGuiLocalize->ConvertANSIToUnicode( pReplay->GetKillerName(), wszTemp, sizeof( wszTemp ) );
V_wcscat_safe( wszSearchableText, wszTemp );
V_wcscat_safe( wszSearchableText, L" " );
}
// lower case
V_wcslower( wszSearchableText );
wchar_t wszFilter[256];
Q_wcsncpy( wszFilter, m_wszFilter, sizeof( wszFilter ) );
bool bPasses = true;
wchar_t seps[] = L" ";
wchar_t *last = NULL;
wchar_t *token = Q_wcstok( wszFilter, seps, &last );
while ( token && bPasses )
{
bPasses &= wcsstr( wszSearchableText, token ) != NULL;
token = Q_wcstok( NULL, seps, &last );
}
return bPasses;
}
void CReplayListPanel::AddReplayItem( ReplayItemHandle_t hItem )
{
IReplayItemManager *pItemManager;
const IQueryableReplayItem *pItem = FindReplayItem( hItem, &pItemManager );
if ( !pItem )
return;
// Find or add the collection
CBaseThumbnailCollection *pCollection = FindOrAddReplayThumbnailCollection( pItem, pItemManager );
// Add the replay
pCollection->AddReplay( pItem );
}
void CReplayListPanel::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
{
IReplayItemManager *pItemManager;
const IQueryableReplayItem *pReplayItem = FindReplayItem( hReplayItem, &pItemManager ); AssertValidReadPtr( pReplayItem );
CBaseThumbnailCollection *pCollection = NULL;
FOR_EACH_VEC( m_vecCollections, i )
{
CBaseThumbnailCollection *pCurCollection = m_vecCollections[ i ];
if ( pCurCollection->FindReplayItemThumbnailRow( pReplayItem ) )
{
pCollection = pCurCollection;
break;
}
}
// Find the collection associated with the given replay - NOTE: we pass false here for the "bAddIfNotFound" param
if ( !pCollection )
{
AssertMsg( 0, "REPLAY: Should have found collection while attempting to delete a replay from the browser." );
return;
}
// Clear the previous hover pointer to avoid a potential crash where we've got a stale ptr
m_pPrevHoverPanel = NULL;
// Clear out the preview panel if it exists and is for the given replay necessary
if ( m_pPreviewPanel && m_pPreviewPanel->GetReplayHandle() == pReplayItem->GetItemReplayHandle() )
{
ClearPreviewPanel();
m_pBorderArrowImg->SetVisible( false );
}
pCollection->CleanupUIForReplayItem( hReplayItem );
}
#endif
@@ -0,0 +1,80 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYBROWSER_LISTPANEL_H
#define REPLAYBROWSER_LISTPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include <game/client/iviewport.h>
#include "vgui_controls/PropertyPage.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/PanelListPanel.h"
#include "vgui_controls/EditablePanel.h"
#include "replaybrowseritemmanager.h"
#include "replay/genericclassbased_replay.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CBaseThumbnailCollection;
class CReplayPreviewPanelBase;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayBrowserThumbnail;
class CExLabel;
class CReplayListPanel : public PanelListPanel
{
DECLARE_CLASS_SIMPLE( CReplayListPanel, PanelListPanel );
public:
CReplayListPanel( Panel *pParent, const char *pName );
~CReplayListPanel();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
void AddReplayItem( ReplayItemHandle_t hItem );
void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
void AddReplaysToList();
void RemoveCollection( CBaseThumbnailCollection *pCollection );
virtual void OnTick();
void OnItemPanelEntered( Panel *pPanel );
void OnItemPanelExited( Panel *pPanel );
void SetupBorderArrow( bool bLeft );
void ClearPreviewPanel();
void ApplyFilter( const wchar_t *pFilterText );
protected:
virtual void OnMouseWheeled(int delta);
private:
const IQueryableReplayItem *FindItem( ReplayItemHandle_t hItem, int *pItemManagerIndex );
CBaseThumbnailCollection *FindOrAddReplayThumbnailCollection( const IQueryableReplayItem *pItem, IReplayItemManager *pItemManager );
CReplayBrowserThumbnail *FindThumbnailAtCursor( int x, int y );
bool PassesFilter( IQueryableReplayItem *pItem );
CBaseThumbnailCollection *m_pReplaysCollection;
CBaseThumbnailCollection *m_pMoviesCollection;
CUtlVector< CBaseThumbnailCollection * > m_vecCollections;
CReplayPreviewPanelBase *m_pPreviewPanel;
Panel *m_pPrevHoverPanel;
ImagePanel *m_pBorderArrowImg;
int m_aBorderArrowDims[2];
wchar_t m_wszFilter[256];
};
#endif // REPLAYBROWSER_LISTPANEL_H
@@ -0,0 +1,449 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowsermainpanel.h"
#include "replaybrowserbasepage.h"
#include "confirm_delete_dialog.h"
#include "vgui_controls/PropertySheet.h"
#include "vgui_controls/TextImage.h"
#include "vgui/IInput.h"
#include "vgui/ISurface.h"
#include "ienginevgui.h"
#include "replay/ireplaymanager.h"
#include "replay/ireplaymoviemanager.h"
#include "econ/econ_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Replay deletion confirmation dialog
//-----------------------------------------------------------------------------
class CConfirmDeleteReplayDialog : public CConfirmDeleteDialog
{
DECLARE_CLASS_SIMPLE( CConfirmDeleteReplayDialog, CConfirmDeleteDialog );
public:
CConfirmDeleteReplayDialog( Panel *pParent, IReplayItemManager *pItemManager, int iPerformance )
: BaseClass( pParent )
{
m_pTextId = iPerformance >= 0 ? "#Replay_DeleteEditConfirm" : pItemManager->AreItemsMovies() ? "#Replay_DeleteMovieConfirm" : "#Replay_DeleteReplayConfirm";
}
const wchar_t *GetText()
{
return g_pVGuiLocalize->Find( m_pTextId );
}
const char *m_pTextId;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayBrowserPanel::CReplayBrowserPanel( Panel *parent )
: PropertyDialog(parent, "ReplayBrowser"),
m_pConfirmDeleteDialog( NULL )
{
// Clear out delete info
V_memset( &m_DeleteInfo, 0, sizeof( m_DeleteInfo ) );
// Replay browser is parented to the game UI panel
vgui::VPANEL gameuiPanel = enginevgui->GetPanel( PANEL_GAMEUIDLL );
SetParent( gameuiPanel );
SetMoveable( false );
SetSizeable( false );
vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
SetScheme(scheme);
SetProportional( true );
// Setup page
m_pReplaysPage = new CReplayBrowserBasePage( this );
m_pReplaysPage->AddActionSignalTarget( this );
AddPage( m_pReplaysPage, "#Replay_MyReplays" );
m_pReplaysPage->SetVisible( true );
ListenForGameEvent( "gameui_hidden" );
// Create this now, so that it can be the default button (if created in .res file, it fights with PropertyDialog's OkButton & generates asserts)
CExButton *pCloseButton = new CExButton( this, "BackButton", "" );
GetFocusNavGroup().SetDefaultButton(pCloseButton);
m_flTimeOpened = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayBrowserPanel::~CReplayBrowserPanel()
{
if ( m_pConfirmDeleteDialog )
{
m_pConfirmDeleteDialog->MarkForDeletion();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replaybrowser/mainpanel.res", "GAME" );
SetOKButtonVisible(false);
SetCancelButtonVisible(false);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::PerformLayout( void )
{
if ( GetVParent() )
{
int w,h;
vgui::ipanel()->GetSize( GetVParent(), w, h );
SetBounds(0,0,w,h);
}
BaseClass::PerformLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::ShowPanel(bool bShow, ReplayHandle_t hReplayDetails/*=REPLAY_HANDLE_INVALID*/,
int iPerformance/*=-1*/ )
{
if ( bShow )
{
GetPropertySheet()->SetActivePage( m_pReplaysPage );
InvalidateLayout( false, true );
Activate();
m_flTimeOpened = gpGlobals->realtime;
}
else
{
PostMessage( m_pReplaysPage, new KeyValues("CancelSelection") );
}
SetVisible( bShow );
m_pReplaysPage->SetVisible( bShow );
if ( hReplayDetails != REPLAY_HANDLE_INVALID )
{
char szDetails[32];
V_snprintf( szDetails, sizeof( szDetails ), "details%i_%i", (int)hReplayDetails, iPerformance );
m_pReplaysPage->OnCommand( szDetails );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::FireGameEvent( IGameEvent *event )
{
const char * type = event->GetName();
if ( Q_strcmp(type, "gameui_hidden") == 0 )
{
ShowPanel( false );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::OnCommand( const char *command )
{
if ( !Q_stricmp( command, "back" ) )
{
if ( m_pReplaysPage->IsDetailsViewOpen() )
{
m_pReplaysPage->DeleteDetailsPanelAndShowReplayList();
}
else
{
// Close the main panel
ShowPanel( false );
// TODO: Properly manage the browser so that we don't have to recreate it ever time its opened
MarkForDeletion();
// If we're connected to a game server, we also close the game UI.
if ( engine->IsInGame() )
{
engine->ClientCmd_Unrestricted( "gameui_hide" );
}
}
}
BaseClass::OnCommand( command );
}
void CReplayBrowserPanel::OnKeyCodeTyped(vgui::KeyCode code)
{
if ( code == KEY_ESCAPE )
{
ShowPanel( false );
}
else
{
BaseClass::OnKeyCodeTyped( code );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::OnKeyCodePressed(vgui::KeyCode code)
{
if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
{
ShowPanel( false );
}
else if ( code == KEY_ENTER )
{
// do nothing, the default is to close the panel!
}
else
{
BaseClass::OnKeyCodePressed( code );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::ShowDeleteReplayDenialDlg()
{
ShowMessageBox( "#Replay_DeleteDenialTitle", "#Replay_DeleteDenialText", "#GameUI_OK" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::AttemptToDeleteReplayItem( Panel *pHandler, ReplayItemHandle_t hReplayItem,
IReplayItemManager *pItemManager, int iPerformance )
{
IQueryableReplayItem *pItem = pItemManager->GetItem( hReplayItem );
CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( pItem->GetItemReplay() );
// If this is an actual replay the user is trying to delete, only allow it
// if the replay says it's OK. Don't execute this code for performances.
if ( !pItemManager->AreItemsMovies() && iPerformance < 0 && !pReplay->ShouldAllowDelete() )
{
ShowDeleteReplayDenialDlg();
return;
}
// Otherwise, show the confirm delete dlg
vgui::surface()->PlaySound( "replay\\replaydialog_warn.wav" );
ConfirmReplayItemDelete( pHandler, hReplayItem, pItemManager, iPerformance );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::ConfirmReplayItemDelete( Panel *pHandler, ReplayItemHandle_t hReplayItem,
IReplayItemManager *pItemManager, int iPerformance )
{
CConfirmDeleteReplayDialog *pConfirm = vgui::SETUP_PANEL( new CConfirmDeleteReplayDialog( this, pItemManager, iPerformance ) );
if ( pConfirm )
{
// Cache replay and handler for later
m_DeleteInfo.m_hReplayItem = hReplayItem;
m_DeleteInfo.m_pItemManager = pItemManager;
m_DeleteInfo.m_hHandler = pHandler->GetVPanel();
m_DeleteInfo.m_iPerformance = iPerformance;
// Display the panel!
pConfirm->Show();
// Cache confirm dialog ptr
m_pConfirmDeleteDialog = pConfirm;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::OnConfirmDelete( KeyValues *data )
{
// Clear confirm ptr
m_pConfirmDeleteDialog = NULL;
// User confirmed delete?
int nConfirmed = data->GetInt( "confirmed", 0 );
if ( !nConfirmed )
return;
// Get the replay from the dialog
ReplayItemHandle_t hReplayItem = m_DeleteInfo.m_hReplayItem;
// Post actions signal to the handler
KeyValues *pMsg = new KeyValues( "ReplayItemDeleted" );
pMsg->SetInt( "replayitem", (int)hReplayItem );
pMsg->SetInt( "perf", m_DeleteInfo.m_iPerformance );
PostMessage( m_DeleteInfo.m_hHandler, pMsg );
// Delete actual replay item
if ( m_DeleteInfo.m_iPerformance < 0 )
{
// Cleanup UI related to the replay/movie
CleanupUIForReplayItem( hReplayItem );
// Delete the replay/movie
m_DeleteInfo.m_pItemManager->DeleteItem( GetActivePage(), hReplayItem, false );
}
vgui::surface()->PlaySound( "replay\\deleted_take.wav" );
// Clear delete info
V_memset( &m_DeleteInfo, 0, sizeof( m_DeleteInfo ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::OnSaveReplay( ReplayHandle_t hNewReplay )
{
// Verify that the handle is valid
Assert( g_pReplayManager->GetReplay( hNewReplay ) );
m_pReplaysPage->AddReplay( hNewReplay );
m_pReplaysPage->Repaint();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::OnDeleteReplay( ReplayHandle_t hDeletedReplay )
{
// Verify that the handle is valid
Assert( g_pReplayManager->GetReplay( hDeletedReplay ) );
DeleteReplay( hDeletedReplay );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::DeleteReplay( ReplayHandle_t hReplay )
{
m_pReplaysPage->DeleteReplay( hReplay );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayBrowserPanel::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
{
if ( GetActivePage() == m_pReplaysPage )
{
m_pReplaysPage->CleanupUIForReplayItem( hReplayItem );
}
}
static vgui::DHANDLE<CReplayBrowserPanel> g_ReplayBrowserPanel;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayBrowserPanel *ReplayUI_OpenReplayBrowserPanel( ReplayHandle_t hReplayDetails,
int iPerformance )
{
if ( !g_ReplayBrowserPanel.Get() )
{
g_ReplayBrowserPanel = vgui::SETUP_PANEL( new CReplayBrowserPanel( NULL ) );
g_ReplayBrowserPanel->InvalidateLayout( false, true );
}
engine->ClientCmd_Unrestricted( "gameui_activate" );
g_ReplayBrowserPanel->ShowPanel( true, hReplayDetails, iPerformance );
extern IReplayMovieManager *g_pReplayMovieManager;
if ( g_pReplayMovieManager->GetMovieCount() > 0 )
{
// Fire a message the game DLL can intercept (for achievements, etc).
IGameEvent *event = gameeventmanager->CreateEvent( "browse_replays" );
if ( event )
{
gameeventmanager->FireEventClientSide( event );
}
}
return g_ReplayBrowserPanel;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CReplayBrowserPanel *ReplayUI_GetBrowserPanel( void )
{
return g_ReplayBrowserPanel.Get();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ReplayUI_CloseReplayBrowser()
{
if ( g_ReplayBrowserPanel )
{
g_ReplayBrowserPanel->MarkForDeletion();
g_ReplayBrowserPanel = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ReplayUI_ReloadBrowser( ReplayHandle_t hReplay/*=REPLAY_HANDLE_INVALID*/,
int iPerformance/*=-1*/ )
{
delete g_ReplayBrowserPanel.Get();
g_ReplayBrowserPanel = NULL;
ReplayUI_OpenReplayBrowserPanel( hReplay, iPerformance );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CON_COMMAND_F( open_replaybrowser, "Open the replay browser.", FCVAR_CLIENTDLL )
{
ReplayUI_OpenReplayBrowserPanel( REPLAY_HANDLE_INVALID, -1 );
g_ReplayBrowserPanel->InvalidateLayout( false, true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CON_COMMAND_F( replay_reloadbrowser, "Reloads replay data and display replay browser", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE )
{
ReplayUI_ReloadBrowser();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CON_COMMAND_F( replay_hidebrowser, "Hides replay browser", FCVAR_CLIENTDLL )
{
ReplayUI_CloseReplayBrowser();
}
#endif
@@ -0,0 +1,89 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#if defined( REPLAY_ENABLED )
#ifndef REPLAYBROWSER_MAIN_PANEL_H
#define REPLAYBROWSER_MAIN_PANEL_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/PropertyDialog.h"
#include "replay/replayhandle.h"
#include "GameEventListener.h"
#include "replaybrowseritemmanager.h"
//-----------------------------------------------------------------------------
class CReplayBrowserBasePage;
class CConfirmDeleteDialog;
class CExButton;
//-----------------------------------------------------------------------------
class CReplayBrowserPanel : public vgui::PropertyDialog,
public CGameEventListener
{
DECLARE_CLASS_SIMPLE( CReplayBrowserPanel, vgui::PropertyDialog );
public:
CReplayBrowserPanel( Panel *parent );
virtual ~CReplayBrowserPanel();
void OnSaveReplay( ReplayHandle_t hNewReplay );
void OnDeleteReplay( ReplayHandle_t hDeletedReplay );
void DeleteReplay( ReplayHandle_t hReplay );
virtual void CleanupUIForReplayItem( ReplayItemHandle_t hReplay ); // After a replay has been deleted - deletes all UI (thumbnail, but maybe also row and/or collection as well)
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void PerformLayout( void );
virtual void OnCommand( const char *command );
virtual void ShowPanel( bool bShow, ReplayHandle_t hReplayDetails = REPLAY_HANDLE_INVALID, int iPerformance = -1 );
virtual void OnKeyCodeTyped(vgui::KeyCode code);
virtual void OnKeyCodePressed(vgui::KeyCode code);
virtual void FireGameEvent( IGameEvent *event );
MESSAGE_FUNC_PARAMS( OnConfirmDelete, "ConfirmDlgResult", data );
void AttemptToDeleteReplayItem( Panel *pHandler, ReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager, int iPerformance );
CReplayBrowserBasePage *m_pReplaysPage;
CConfirmDeleteDialog *m_pConfirmDeleteDialog;
struct DeleteInfo_t
{
ReplayItemHandle_t m_hReplayItem;
IReplayItemManager *m_pItemManager;
vgui::VPANEL m_hHandler;
int m_iPerformance;
};
DeleteInfo_t m_DeleteInfo;
float GetTimeOpened( void ){ return m_flTimeOpened; }
private:
void ShowDeleteReplayDenialDlg();
void ConfirmReplayItemDelete( Panel *pHandler, ReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager, int iPerformance );
float m_flTimeOpened;
};
//-----------------------------------------------------------------------------
CReplayBrowserPanel *ReplayUI_GetBrowserPanel();
void ReplayUI_ReloadBrowser( ReplayHandle_t hReplay = REPLAY_HANDLE_INVALID, int iPerformance = -1 );
void ReplayUI_CloseReplayBrowser();
//-----------------------------------------------------------------------------
#endif // REPLAYBROWSER_MAIN_PANEL_H
#endif
@@ -0,0 +1,220 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowsermovieplayerpanel.h"
#include "vgui/IVGui.h"
#include "vgui/IInput.h"
#include "engine/IEngineSound.h"
#include "iclientmode.h"
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
CMoviePlayerPanel::CMoviePlayerPanel( Panel *pParent, const char *pName, const char *pMovieFilename )
: CReplayBasePanel( pParent, pName ),
m_flCurFrame( 0.0f ),
m_flLastTime( 0.0f ),
m_nLastMouseXPos( 0 ),
m_bPlaying( false ),
m_bLooping( false ),
m_bFullscreen( false ),
m_bMouseOverScrub( false ),
m_pOldParent( NULL ),
m_pVideoMaterial( NULL )
{
if ( g_pVideo )
{
m_pVideoMaterial = g_pVideo->CreateVideoMaterial( pMovieFilename, pMovieFilename, "GAME" );
if ( m_pVideoMaterial )
{
m_pMaterial = m_pVideoMaterial->GetMaterial();
m_pMaterial->AddRef();
m_nNumFrames = m_pVideoMaterial->GetFrameCount();
}
}
ivgui()->AddTickSignal( GetVPanel(), 0 );
}
CMoviePlayerPanel::~CMoviePlayerPanel()
{
FreeMaterial();
ivgui()->RemoveTickSignal( GetVPanel() );
}
void CMoviePlayerPanel::PerformLayout()
{
BaseClass::PerformLayout();
GetPosRelativeToAncestor( NULL, m_nGlobalPos[0], m_nGlobalPos[1] );
if ( m_bFullscreen )
{
// Cache parent
m_pOldParent = GetParent();
GetBounds( m_aOldBounds[0], m_aOldBounds[1], m_aOldBounds[2], m_aOldBounds[3] );
// Adjust parent for fullscreen mode
SetParent( g_pClientMode->GetViewport() );
// Adjust bounds for fullscreen
SetBounds( 0, 0, ScreenWidth(), ScreenHeight() );
}
else if ( m_pOldParent )
{
// Restore old parent/bounds
SetParent( m_pOldParent );
SetBounds( m_aOldBounds[0], m_aOldBounds[1], m_aOldBounds[2], m_aOldBounds[3] );
}
}
void CMoviePlayerPanel::OnMousePressed( MouseCode code )
{
// ToggleFullscreen();
}
void CMoviePlayerPanel::SetScrubOnMouseOverMode( bool bOn )
{
if ( bOn )
{
m_bPlaying = false;
}
m_bMouseOverScrub = bOn;
}
void CMoviePlayerPanel::Play()
{
m_bPlaying = true;
m_flLastTime = gpGlobals->realtime;
enginesound->NotifyBeginMoviePlayback();
}
void CMoviePlayerPanel::FreeMaterial()
{
if ( m_pVideoMaterial )
{
if ( g_pVideo )
{
g_pVideo->DestroyVideoMaterial( m_pVideoMaterial );
}
m_pVideoMaterial = NULL;
}
if ( m_pMaterial )
{
m_pMaterial->Release();
m_pMaterial = NULL;
}
}
void CMoviePlayerPanel::OnTick()
{
if ( !IsEnabled() )
return;
if ( m_bMouseOverScrub )
{
int nMouseX, nMouseY;
input()->GetCursorPos( nMouseX, nMouseY );
if ( IsWithin( nMouseX, nMouseY ) &&
nMouseX != m_nLastMouseXPos )
{
float flPercent = (float)( nMouseX - m_nGlobalPos[0] ) / GetWide();
m_flCurFrame = flPercent * ( m_nNumFrames - 1 );
m_nLastMouseXPos = nMouseX;
}
}
else if ( m_bPlaying )
{
float flElapsed = gpGlobals->realtime - m_flLastTime;
m_flLastTime = gpGlobals->realtime;
m_flCurFrame += flElapsed * m_pVideoMaterial->GetVideoFrameRate().GetFPS();
// Loop if necessary
if ( m_flCurFrame >= m_nNumFrames )
{
if ( m_bLooping )
{
m_flCurFrame = m_flCurFrame - m_nNumFrames;
}
else
{
// Don't go past last frame
m_flCurFrame = m_nNumFrames - 1;
}
}
}
m_pVideoMaterial->SetFrame( m_flCurFrame );
// Msg( "frame: %f / %i\n", m_flCurFrame, m_nNumFrames );
}
void CMoviePlayerPanel::Paint()
{
if ( m_pVideoMaterial == NULL )
return;
// Get panel position/dimensions
int x,y;
int w,h;
GetPosRelativeToAncestor( NULL, x, y );
GetSize( w,h );
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( m_pMaterial );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
float flMinU = 0.0f, flMinV = 0.0f;
float flMaxU, flMaxV;
m_pVideoMaterial->GetVideoTexCoordRange( &flMaxU, &flMaxV );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0.0f );
meshBuilder.TexCoord2f( 0, flMinU, flMinV );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + w, y, 0.0f );
meshBuilder.TexCoord2f( 0, flMaxU, flMinV );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + w, y + h, 0.0f );
meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x, y + h, 0.0f );
meshBuilder.TexCoord2f( 0, flMinU, flMaxV );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
void CMoviePlayerPanel::ToggleFullscreen()
{
m_bFullscreen = !m_bFullscreen;
InvalidateLayout( false, false );
}
#endif
@@ -0,0 +1,57 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYBROWSERMOVIEPLAYERPANEL_H
#define REPLAYBROWSERMOVIEPLAYERPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include "replaybrowserbasepanel.h"
#include "video/ivideoservices.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: A panel that plays AVI's
//-----------------------------------------------------------------------------
class CMoviePlayerPanel : public CReplayBasePanel
{
DECLARE_CLASS_SIMPLE( CMoviePlayerPanel, CReplayBasePanel );
public:
CMoviePlayerPanel( Panel *pParent, const char *pName, const char *pMovieFilename );
~CMoviePlayerPanel();
virtual void Paint();
void Play();
void SetLooping( bool bLooping ) { m_bLooping = bLooping; }
bool IsPlaying() { return m_bPlaying; }
void SetScrubOnMouseOverMode( bool bOn );
void FreeMaterial();
void ToggleFullscreen();
private:
virtual void PerformLayout();
virtual void OnMousePressed( MouseCode code );
virtual void OnTick();
IVideoMaterial *m_pVideoMaterial;
IMaterial *m_pMaterial;
float m_flCurFrame;
int m_nNumFrames;
bool m_bPlaying;
bool m_bLooping;
float m_flLastTime;
int m_nGlobalPos[2];
int m_nLastMouseXPos;
bool m_bFullscreen;
Panel *m_pOldParent;
int m_aOldBounds[4];
bool m_bMouseOverScrub; // In this mode, we don't playback, only scrub on mouse over
};
#endif // REPLAYBROWSERMOVIEPLAYERPANEL_H
@@ -0,0 +1,301 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowserpreviewpanel.h"
#include "replaybrowsermainpanel.h"
#include "replaybrowsermovieplayerpanel.h"
#include "replaybrowserlistitempanel.h"
#include "replay/ireplaymovie.h"
#include "replay/screenshot.h"
#include "vgui/ISurface.h"
#include "econ/econ_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
CReplayPreviewPanelBase::CReplayPreviewPanelBase( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager )
: EditablePanel( pParent, "PreviewPanel" ),
m_hItem( hItem ),
m_pItemManager( pItemManager )
{
CGenericClassBasedReplay *pReplay = GetReplay();
IQueryableReplayItem *pItem = pItemManager->GetItem( hItem );
// Setup class image
char szImage[MAX_OSPATH];
m_pClassImage = new ImagePanel( this, "ClassImage" );
V_snprintf( szImage, sizeof( szImage ), "class_sel_sm_%s_%s", pReplay->GetMaterialFriendlyPlayerClass(), pReplay->GetPlayerTeam() ); // Cause default image to display
m_pClassImage->SetImage( szImage );
m_pInfoPanel = new vgui::EditablePanel( this, "InfoPanel" );
// Setup map label
const char *pMapName = pReplay->m_szMapName;
const char *pUnderscore = V_strstr( pMapName, "_" );
if ( pUnderscore )
{
pMapName = pUnderscore + 1;
}
m_pMapLabel = new CExLabel( m_pInfoPanel, "MapLabel", pMapName );
// Setup record date/time
const CReplayTime &RecordTime = pItem->GetItemDate();
int nDay, nMonth, nYear;
RecordTime.GetDate( nDay, nMonth, nYear );
int nHour, nMin, nSec;
RecordTime.GetTime( nHour, nMin, nSec );
const wchar_t *pDateAndTime = CReplayTime::GetLocalizedDate( g_pVGuiLocalize, nDay, nMonth, nYear, &nHour, &nMin, &nSec );
// Setup date / time label
m_pDateTimeLabel = new CExLabel( m_pInfoPanel, "DateTimeLabel", pDateAndTime );
// Setup info labels
for ( int i = 0; i < NUM_INFO_LABELS; ++i )
{
for ( int j = 0; j < 2; ++j )
{
m_pReplayInfoLabels[i][j] = new CExLabel( m_pInfoPanel, VarArgs("Label%d_%d", i, j), "" );
}
}
m_pReplayInfoLabels[ LABEL_PLAYED_AS ][1]->SetText( pReplay->GetPlayerClass() );
m_pReplayInfoLabels[ LABEL_KILLED_BY ][1]->SetText( pReplay->WasKilled() ? pReplay->GetKillerName() : "#Replay_NoKiller" );
m_pReplayInfoLabels[ LABEL_LIFE_LENGTH ][1]->SetText( CReplayTime::FormatTimeString( (int)pItem->GetItemLength() ) );
}
CReplayPreviewPanelBase::~CReplayPreviewPanelBase()
{
}
void CReplayPreviewPanelBase::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replaybrowser/previewpanel.res", "GAME" );
#if !defined( TF_CLIENT_DLL )
m_pClassImage->SetVisible( false );
#endif
}
void CReplayPreviewPanelBase::PerformLayout()
{
BaseClass::PerformLayout();
CGenericClassBasedReplay *pReplay = GetReplay();
if ( !pReplay )
return;
int nWide = XRES(18);
int nTall = YRES(18);
int nScreenshotH = 0; // Represents the height of the screenshot OR the "no screenshot" label
LayoutView( nWide, nTall, nScreenshotH );
int iInfoHeight = m_pInfoPanel->GetTall();
nTall += iInfoHeight;
if ( m_pClassImage )
{
int w, h;
m_pClassImage->GetImage()->GetContentSize( w, h );
float s = ShoudlUseLargeClassImage() ? 1.25f : 1.0f;
int nClassTall = s*h;
m_pClassImage->SetSize( s*w, nClassTall );
m_pClassImage->SetShouldScaleImage( true );
m_pClassImage->SetScaleAmount( s );
// The panel should be at least as tall as the height of the screenshot
// (or "no screenshot" label) and the class image.
if ( nTall < nClassTall )
{
nTall = nClassTall;
}
m_pClassImage->SetPos( XRES(9), nTall - nClassTall );
}
const int nLabelX = m_pClassImage->GetWide() + XRES( 18 );
int iInfoWidth = nWide - nLabelX;
m_pMapLabel->SetSize( iInfoWidth, m_pMapLabel->GetTall() );
m_pDateTimeLabel->SetSize( iInfoWidth, m_pMapLabel->GetTall() );
m_pInfoPanel->SetBounds( nLabelX, nTall - iInfoHeight, iInfoWidth, iInfoHeight );
nTall += YRES(9);
SetSize( nWide, nTall );
}
void CReplayPreviewPanelBase::LayoutView( int &nWide, int &nTall, int &nCurY )
{
nWide = XRES( 188 );
nTall = YRES(9);
nCurY = nTall;
}
CGenericClassBasedReplay *CReplayPreviewPanelBase::GetReplay()
{
return ToGenericClassBasedReplay( m_pItemManager->GetItem( m_hItem )->GetItemReplay() );
}
ReplayHandle_t CReplayPreviewPanelBase::GetReplayHandle()
{
return GetReplay()->GetHandle();
}
//-----------------------------------------------------------------------------
CReplayPreviewPanelSlideshow::CReplayPreviewPanelSlideshow( Panel *pParent, QueryableReplayItemHandle_t hReplay, IReplayItemManager *pItemManager )
: BaseClass( pParent, hReplay, pItemManager ),
m_pScreenshotPanel( NULL )
{
// Setup screenshot slideshow panel
CGenericClassBasedReplay *pReplay = GetReplay();
const int nScreenshotCount = pReplay->GetScreenshotCount();
if ( nScreenshotCount )
{
m_pScreenshotPanel = new CReplayScreenshotSlideshowPanel( this, "ScreenshotSlideshowPanel", hReplay );
// Set pretty quick transition times based on the screenshot count
m_pScreenshotPanel->SetInterval( ( nScreenshotCount == 2 ) ? 3.0f : 2.0f );
m_pScreenshotPanel->SetTransitionTime( 0.5f );
}
// Setup the no screenshot label
m_pNoScreenshotLabel = new CExLabel( this, "NoScreenshotLabel", "#Replay_NoScreenshot" );
m_pNoScreenshotLabel->SetVisible( false );
}
void CReplayPreviewPanelSlideshow::PerformLayout()
{
BaseClass::PerformLayout();
m_pNoScreenshotLabel->SizeToContents();
m_pNoScreenshotLabel->SetWide( GetWide() );
}
void CReplayPreviewPanelSlideshow::LayoutView( int &nWide, int &nTall, int &nCurY )
{
if ( m_pScreenshotPanel )
{
// Use the dimensions from the first screenshot to figure out the scale, even though the dimensions
// may vary if the user changed resolutions during gameplay
CGenericClassBasedReplay *pReplay = GetReplay();
const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( 0 );
int nScreenshotW = pScreenshot->m_nWidth;
int nScreenshotH = pScreenshot->m_nHeight;
// Scale the screenshot if it's too big for the current resolution
float flScreenshotScale = 1.0f;
int nMaxScreenshotWidth = ScreenWidth() / 3;
if ( nScreenshotW > nMaxScreenshotWidth )
{
flScreenshotScale = (float)nMaxScreenshotWidth / pScreenshot->m_nWidth;
nScreenshotW = nMaxScreenshotWidth;
}
nCurY = nScreenshotH * flScreenshotScale;
m_pScreenshotPanel->GetImagePanel()->SetShouldScaleImage( true );
m_pScreenshotPanel->GetImagePanel()->SetScaleAmount( flScreenshotScale );
nWide += nScreenshotW;
nTall += nCurY;
m_pScreenshotPanel->SetBounds( (nWide - nScreenshotW) * 0.5, YRES(9), nScreenshotW, nCurY );
}
else
{
int w, h;
m_pNoScreenshotLabel->SetContentAlignment( Label::a_center );
m_pNoScreenshotLabel->GetContentSize( w, h );
nTall += YRES( 20 );
m_pNoScreenshotLabel->SetBounds( 0, nTall, w, h );
nTall += YRES( 20 );
m_pNoScreenshotLabel->SetVisible( true );
nWide += XRES( 213 ); // Default width (maps to 640 on 1920x1200)
nTall += h;
nCurY = nTall;
}
}
//-----------------------------------------------------------------------------
CReplayPreviewPanelMovie::CReplayPreviewPanelMovie( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager )
: BaseClass( pParent, hItem, pItemManager ),
m_pMoviePlayerPanel( NULL )
{
m_flCreateTime = gpGlobals->realtime;
ivgui()->AddTickSignal( GetVPanel(), 10 );
}
CReplayPreviewPanelMovie::~CReplayPreviewPanelMovie()
{
ivgui()->RemoveTickSignal( GetVPanel() );
}
void CReplayPreviewPanelMovie::OnTick()
{
if ( gpGlobals->realtime >= m_flCreateTime + 0.5f )
{
if ( !m_pMoviePlayerPanel )
{
m_pMoviePlayerPanel = new CMoviePlayerPanel( this, "MoviePlayer", GetReplayMovie()->GetMovieFilename() );
InvalidateLayout( true, false );
}
if ( !m_pMoviePlayerPanel->IsPlaying() )
{
m_pMoviePlayerPanel->SetLooping( true );
m_pMoviePlayerPanel->Play();
}
}
}
IReplayMovie* CReplayPreviewPanelMovie::GetReplayMovie()
{
return static_cast< IReplayMovie * >( m_pItemManager->GetItem( m_hItem ) );
}
void CReplayPreviewPanelMovie::LayoutView( int &nWide, int &nTall, int &nCurY )
{
// Get frame dimensions
int nFrameWidth, nFrameHeight;
IReplayMovie* pReplayMovie = GetReplayMovie();
pReplayMovie->GetFrameDimensions( nFrameWidth, nFrameHeight );
int nScaledWidth = nFrameWidth;
int nScaledHeight = nFrameHeight;
// Scale the screenshot if it's too big for the current resolution
float flScale = 1.0f;
int nMaxWidth = ScreenWidth() / 3;
if ( nFrameWidth > nMaxWidth )
{
flScale = (float)nMaxWidth / nFrameWidth;
nScaledWidth = nMaxWidth;
nScaledHeight = nFrameHeight * flScale;
}
nWide += nScaledWidth;
nTall += nScaledHeight;
nCurY = nTall;
// Layout movie player panel if it's ready
if ( m_pMoviePlayerPanel )
{
m_pMoviePlayerPanel->SetBounds( 9, 9, nScaledWidth, nScaledHeight );
m_pMoviePlayerPanel->SetEnabled( true );
m_pMoviePlayerPanel->SetVisible( true );
m_pMoviePlayerPanel->SetZPos( 101 );
}
}
#endif
@@ -0,0 +1,120 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYBROWSER_PREVIEWPANEL_H
#define REPLAYBROWSER_PREVIEWPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include <game/client/iviewport.h>
#include "vgui_controls/PropertyPage.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/PanelListPanel.h"
#include "vgui_controls/EditablePanel.h"
#include "replaybrowseritemmanager.h"
#include "replay/genericclassbased_replay.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CExLabel;
class CBaseThumbnailCollection;
class CReplayDetailsPanel;
class CReplayScreenshotSlideshowPanel;
//-----------------------------------------------------------------------------
// Purpose: Preview balloon
//-----------------------------------------------------------------------------
class CGenericClassBasedReplay;
class CCrossfadableImagePanel;
class CSlideshowPanel;
class CReplayPreviewPanelBase : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayPreviewPanelBase, EditablePanel );
public:
CReplayPreviewPanelBase( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
~CReplayPreviewPanelBase();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
ReplayHandle_t GetReplayHandle();
protected:
CGenericClassBasedReplay *GetReplay();
virtual bool ShoudlUseLargeClassImage() { return false; }
virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
protected:
IReplayItemManager *m_pItemManager;
QueryableReplayItemHandle_t m_hItem;
private:
ImagePanel *m_pClassImage;
vgui::EditablePanel *m_pInfoPanel;
CExLabel *m_pMapLabel;
CExLabel *m_pDateTimeLabel;
enum ELabels
{
LABEL_PLAYED_AS,
LABEL_KILLED_BY,
LABEL_LIFE_LENGTH,
NUM_INFO_LABELS
};
CExLabel *m_pReplayInfoLabels[NUM_INFO_LABELS][2];
};
//-----------------------------------------------------------------------------
// Purpose: Preview balloon for slideshows (actual replays)
//-----------------------------------------------------------------------------
class CReplayPreviewPanelSlideshow : public CReplayPreviewPanelBase
{
DECLARE_CLASS_SIMPLE( CReplayPreviewPanelSlideshow, CReplayPreviewPanelBase );
public:
CReplayPreviewPanelSlideshow( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
private:
virtual void PerformLayout();
virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
CReplayScreenshotSlideshowPanel *m_pScreenshotPanel;
CExLabel *m_pNoScreenshotLabel;
};
//-----------------------------------------------------------------------------
// Purpose: Preview balloon for movies (rendered replays)
//-----------------------------------------------------------------------------
class CMoviePlayerPanel;
class IReplayMovie;
class CReplayPreviewPanelMovie : public CReplayPreviewPanelBase
{
DECLARE_CLASS_SIMPLE( CReplayPreviewPanelMovie, CReplayPreviewPanelBase );
public:
CReplayPreviewPanelMovie( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
~CReplayPreviewPanelMovie();
private:
virtual void OnTick();
virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
virtual IReplayMovie *GetReplayMovie();
CMoviePlayerPanel *m_pMoviePlayerPanel;
float m_flCreateTime;
};
#endif // REPLAYBROWSER_PREVIEWPANEL_H
@@ -0,0 +1,639 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaybrowserrenderdialog.h"
#include "vgui_controls/TextImage.h"
#include "vgui_controls/CheckButton.h"
#include "vgui_controls/TextEntry.h"
#include "vgui/IInput.h"
#include "replay/genericclassbased_replay.h"
#include "ienginevgui.h"
#include "replayrenderoverlay.h"
#include "replay/ireplaymanager.h"
#include "replay/ireplaymoviemanager.h"
#include "video/ivideoservices.h"
#include "confirm_dialog.h"
#include "replay/replayrenderer.h"
#include "replay/performance.h"
#include "replay/replayvideo.h"
#include "replay_gamestats_shared.h"
#include "econ/econ_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
extern IReplayMovieManager *g_pReplayMovieManager;
//-----------------------------------------------------------------------------
ConVar replay_rendersetting_quitwhendone( "replay_rendersetting_quitwhendone", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Quit after rendering is completed.", true, 0.0f, true, 1.0f );
ConVar replay_rendersetting_exportraw( "replay_rendersetting_exportraw", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Export raw TGA frames and a .wav file, instead of encoding a movie file.", true, 0.0f, true, 1.0f );
ConVar replay_rendersetting_motionblurquality( "replay_rendersetting_motionblurquality", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Motion blur quality.", true, 0, true, MAX_MOTION_BLUR_QUALITY );
ConVar replay_rendersetting_motionblurenabled( "replay_rendersetting_motionblurenabled", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Motion blur enabled/disabled.", true, 0.0f, true, 1.0f );
ConVar replay_rendersetting_encodingquality( "replay_rendersetting_encodingquality", "100", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Render quality: the higher the quality, the larger the resulting movie file size.", true, 0, true, 100 );
ConVar replay_rendersetting_motionblur_can_toggle( "replay_rendersetting_motionblur_can_toggle", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "" );
ConVar replay_rendersetting_renderglow( "replay_rendersetting_renderglow", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Glow effect enabled/disabled.", true, 0.0f, true, 1.0f );
//-----------------------------------------------------------------------------
CReplayRenderDialog::CReplayRenderDialog( Panel *pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance )
: BaseClass( pParent, "RenderDialog" ),
m_bShowAdvancedOptions( false ),
m_hReplay( hReplay ),
m_bSetQuit( bSetQuit ),
m_iPerformance( iPerformance ),
m_pVideoModesCombo( NULL ),
m_pCodecCombo( NULL ),
m_pPlayVoiceCheck( NULL ),
m_pShowAdvancedOptionsCheck( NULL ),
m_pQuitWhenDoneCheck( NULL ),
m_pExportRawCheck( NULL ),
m_pTitleText( NULL ),
m_pResolutionNoteLabel( NULL ),
m_pEnterANameLabel( NULL ),
m_pVideoModeLabel( NULL ),
m_pCodecLabel( NULL ),
m_pMotionBlurLabel( NULL ),
m_pMotionBlurSlider( NULL ),
m_pQualityLabel( NULL ),
m_pQualitySlider( NULL ),
m_pTitleLabel( NULL ),
m_pCancelButton( NULL ),
m_pRenderButton( NULL ),
m_pBgPanel( NULL ),
m_pMotionBlurCheck( NULL ),
m_pQualityPresetLabel( NULL ),
m_pQualityPresetCombo( NULL ),
m_pSeparator( NULL ),
m_pGlowEnabledCheck( NULL )
{
m_iQualityPreset = ReplayVideo_GetDefaultQualityPreset();
}
void CReplayRenderDialog::UpdateControlsValues()
{
ConVarRef replay_voice_during_playback( "replay_voice_during_playback" );
m_pQuitWhenDoneCheck->SetSelected( replay_rendersetting_quitwhendone.GetBool() );
m_pExportRawCheck->SetSelected( replay_rendersetting_exportraw.GetBool() );
m_pShowAdvancedOptionsCheck->SetSelected( m_bShowAdvancedOptions );
m_pMotionBlurSlider->SetValue( replay_rendersetting_motionblurquality.GetInt() );
m_pMotionBlurCheck->SetSelected( replay_rendersetting_motionblurenabled.GetBool() );
m_pQualitySlider->SetValue( replay_rendersetting_encodingquality.GetInt() / ReplayVideo_GetQualityInterval() );
if ( m_pGlowEnabledCheck )
{
m_pGlowEnabledCheck->SetSelected( replay_rendersetting_renderglow.GetBool() );
}
if ( replay_voice_during_playback.IsValid() )
{
m_pPlayVoiceCheck->SetSelected( replay_voice_during_playback.GetBool() );
}
else
{
m_pPlayVoiceCheck->SetEnabled( false );
}
}
void CReplayRenderDialog::AddControlToAutoLayout( Panel *pPanel, bool bAdvanced )
{
LayoutInfo_t *pNewLayoutInfo = new LayoutInfo_t;
pNewLayoutInfo->pPanel = pPanel;
// Use the positions from the .res file as relative positions for auto-layout
pPanel->GetPos( pNewLayoutInfo->nOffsetX, pNewLayoutInfo->nOffsetY );
pNewLayoutInfo->bAdvanced = bAdvanced;
// Add to the list
m_lstControls.AddToTail( pNewLayoutInfo );
}
void CReplayRenderDialog::SetValuesFromQualityPreset()
{
const ReplayQualityPreset_t &preset = ReplayVideo_GetQualityPreset( m_iQualityPreset );
replay_rendersetting_motionblurquality.SetValue( preset.m_iMotionBlurQuality );
replay_rendersetting_motionblurenabled.SetValue( (int)preset.m_bMotionBlurEnabled );
replay_rendersetting_encodingquality.SetValue( preset.m_iQuality );
for ( int i = 0; i < ReplayVideo_GetCodecCount(); ++i )
{
const ReplayCodec_t &CurCodec = ReplayVideo_GetCodec( i );
if ( CurCodec.m_nCodecId == preset.m_nCodecId )
{
m_pCodecCombo->ActivateItem( m_pCodecCombo->GetItemIDFromRow( i ) );
break;
}
}
UpdateControlsValues();
InvalidateLayout();
}
void CReplayRenderDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
{
int i;
// Link in TF scheme
extern IEngineVGui *enginevgui;
vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
SetScheme( pTFScheme );
SetProportional( true );
BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
LoadControlSettings( "Resource/UI/replaybrowser/renderdialog.res", "GAME" );
// retrieve controls
m_pPlayVoiceCheck = dynamic_cast< CheckButton * >( FindChildByName( "PlayVoice" ) );
m_pShowAdvancedOptionsCheck = dynamic_cast< CheckButton * >( FindChildByName( "ShowAdvancedOptions" ) );
m_pQuitWhenDoneCheck = dynamic_cast< CheckButton * >( FindChildByName( "QuitWhenDone" ) );
m_pExportRawCheck = dynamic_cast< CheckButton * >( FindChildByName( "ExportRaw" ) );
m_pTitleText = dynamic_cast< TextEntry * >( FindChildByName( "TitleInput" ) );
m_pResolutionNoteLabel = dynamic_cast< CExLabel * >( FindChildByName( "ResolutionNoteLabel" ) );
m_pEnterANameLabel = dynamic_cast< CExLabel * >( FindChildByName( "EnterANameLabel" ) );
m_pVideoModeLabel = dynamic_cast< CExLabel * >( FindChildByName( "VideoModeLabel" ) );
m_pCodecLabel = dynamic_cast< CExLabel * >( FindChildByName( "CodecLabel" ) );
m_pMotionBlurLabel = dynamic_cast< CExLabel * >( FindChildByName( "MotionBlurLabel" ) );
m_pMotionBlurSlider = dynamic_cast< Slider * >( FindChildByName( "MotionBlurSlider" ) );
m_pQualityLabel = dynamic_cast< CExLabel * >( FindChildByName( "QualityLabel" ) );
m_pQualitySlider = dynamic_cast< Slider * >( FindChildByName( "QualitySlider" ) );
m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) );
m_pRenderButton = dynamic_cast< CExButton * >( FindChildByName( "RenderButton" ) );
m_pCancelButton = dynamic_cast< CExButton * >( FindChildByName( "CancelButton" ) );
m_pBgPanel = dynamic_cast< EditablePanel * >( FindChildByName( "BGPanel" ) );
m_pMotionBlurCheck = dynamic_cast< CheckButton * >( FindChildByName( "MotionBlurEnabled" ) );
m_pQualityPresetLabel = dynamic_cast< CExLabel * >( FindChildByName( "QualityPresetLabel" ) );
m_pQualityPresetCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "QualityPresetCombo" ) );
m_pCodecCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "CodecCombo" ) );
m_pVideoModesCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "VideoModeCombo" ) );
m_pEstimateTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "EstimateTimeLabel" ) );
m_pEstimateFileLabel = dynamic_cast< CExLabel * >( FindChildByName( "EstimateFileLabel" ) );
m_pSeparator = FindChildByName( "SeparatorLine" );
m_pGlowEnabledCheck = dynamic_cast< CheckButton * >( FindChildByName( "GlowEnabled" ) );
m_pLockWarningLabel = dynamic_cast< CExLabel * >( FindChildByName( "LockWarningLabel" ) );
#if defined( TF_CLIENT_DLL )
if ( m_pBgPanel )
{
m_pBgPanel->SetPaintBackgroundType( 2 ); // Rounded.
}
#endif
AddControlToAutoLayout( m_pTitleLabel, false );
// The replay may be REPLAY_HANDLE_INVALID in the case that we are about to render all unrendered replays
if ( m_hReplay != REPLAY_HANDLE_INVALID )
{
CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
m_pTitleText->SetText( pReplay->m_wszTitle );
m_pTitleText->SetVisible( true );
m_pTitleLabel->SetText( "#Replay_RenderReplay" );
m_pEnterANameLabel->SetVisible( true );
AddControlToAutoLayout( m_pEnterANameLabel, false );
}
else
{
m_pTitleLabel->SetText( "#Replay_RenderReplays" );
}
m_pTitleText->SelectAllOnFocusAlways( true );
AddControlToAutoLayout( m_pTitleText, false );
// Update controls based on preset
SetValuesFromQualityPreset();
// Set quit button if necessary
if ( m_bSetQuit )
{
m_pQuitWhenDoneCheck->SetSelected( true );
}
m_pPlayVoiceCheck->SetProportional( false );
m_pQuitWhenDoneCheck->SetProportional( false );
m_pShowAdvancedOptionsCheck->SetProportional( false );
m_pMotionBlurCheck->SetProportional( false );
m_pMotionBlurSlider->InvalidateLayout( false, true ); // Without this, the range labels show up with "..." because of an invalid font in TextImage::ApplySchemeSettings().
m_pExportRawCheck->SetProportional( false );
m_pQualitySlider->InvalidateLayout( false, true ); // Without this, the range labels show up with "..." because of an invalid font in TextImage::ApplySchemeSettings().
if ( m_pGlowEnabledCheck )
{
m_pGlowEnabledCheck->SetProportional( false );
}
// Fill in combo box with preset quality levels
const int nQualityPresetCount = ReplayVideo_GetQualityPresetCount();
m_pQualityPresetCombo->SetNumberOfEditLines( nQualityPresetCount );
for ( i = 0; i < nQualityPresetCount; ++i )
{
const ReplayQualityPreset_t &CurQualityPreset = ReplayVideo_GetQualityPreset( i );
m_pQualityPresetCombo->AddItem( CurQualityPreset.m_pName, NULL );
m_pQualityPresetCombo->SetItemEnabled( i, true );
}
m_pQualityPresetCombo->ActivateItem( m_pQualityPresetCombo->GetItemIDFromRow( m_iQualityPreset ) );
// Fill in combo box with video modes
int nScreenW = ScreenWidth();
int nScreenH = ScreenHeight();
const int nVidModeCount = ReplayVideo_GetVideoModeCount();
m_pVideoModesCombo->SetNumberOfEditLines( nVidModeCount );
bool bAtLeastOneVideoModeAdded = false;
bool bEnable = false;
bool bSkipped = false;
for ( i = 0; i < nVidModeCount; ++i )
{
// Only offer display modes less than the current window size
const ReplayVideoMode_t &CurVideoMode = ReplayVideo_GetVideoMode( i );
int nMw = CurVideoMode.m_nWidth;
int nMh = CurVideoMode.m_nHeight;
// Only display modes that fit in the current window
bEnable = ( nMw <= nScreenW && nMh <= nScreenH );
if (!bEnable)
bSkipped = true;
m_pVideoModesCombo->AddItem( CurVideoMode.m_pName, NULL );
m_pVideoModesCombo->SetItemEnabled( i, bEnable );
if (bEnable)
bAtLeastOneVideoModeAdded = true;
}
if ( bAtLeastOneVideoModeAdded )
{
m_pVideoModesCombo->ActivateItem( m_pVideoModesCombo->GetItemIDFromRow( 0 ) );
}
// fill in the combo box with codecs
const int nNumCodecs = ReplayVideo_GetCodecCount();
m_pCodecCombo->SetNumberOfEditLines( nNumCodecs );
for ( i = 0; i < nNumCodecs; ++i )
{
const ReplayCodec_t &CurCodec = ReplayVideo_GetCodec( i );
m_pCodecCombo->AddItem( CurCodec.m_pName, NULL );
m_pCodecCombo->SetItemEnabled( i, true );
}
m_pCodecCombo->ActivateItem( m_pCodecCombo->GetItemIDFromRow( 0 ) );
// now layout
// simplified options
AddControlToAutoLayout( m_pVideoModeLabel, false );
AddControlToAutoLayout( m_pVideoModesCombo, false );
// Show the note about "not all resolutions are available?"
if ( bSkipped && m_pResolutionNoteLabel )
{
m_pResolutionNoteLabel->SetVisible( true );
AddControlToAutoLayout( m_pResolutionNoteLabel, false );
}
// other simplified options
AddControlToAutoLayout( m_pQualityPresetLabel, false );
AddControlToAutoLayout( m_pQualityPresetCombo, false );
AddControlToAutoLayout( m_pEstimateTimeLabel, false );
AddControlToAutoLayout( m_pEstimateFileLabel, false );
AddControlToAutoLayout( m_pPlayVoiceCheck, false );
AddControlToAutoLayout( m_pShowAdvancedOptionsCheck, false );
AddControlToAutoLayout( m_pQuitWhenDoneCheck, false );
AddControlToAutoLayout( m_pLockWarningLabel, false );
// now advanced options
AddControlToAutoLayout( m_pSeparator, true );
AddControlToAutoLayout( m_pCodecLabel, true );
AddControlToAutoLayout( m_pCodecCombo, true );
if ( replay_rendersetting_motionblur_can_toggle.GetBool() )
{
AddControlToAutoLayout( m_pMotionBlurCheck, true );
}
else
{
m_pMotionBlurCheck->SetVisible( false );
}
AddControlToAutoLayout( m_pMotionBlurLabel, true );
AddControlToAutoLayout( m_pMotionBlurSlider, true );
AddControlToAutoLayout( m_pQualityLabel, true );
AddControlToAutoLayout( m_pQualitySlider, true );
AddControlToAutoLayout( m_pExportRawCheck, true );
if ( m_pGlowEnabledCheck )
{
AddControlToAutoLayout( m_pGlowEnabledCheck, true );
}
// these buttons always show up
AddControlToAutoLayout( m_pRenderButton, false );
AddControlToAutoLayout( m_pCancelButton, false );
}
void CReplayRenderDialog::PerformLayout()
{
BaseClass::PerformLayout();
m_pResolutionNoteLabel->SizeToContents(); // Get the proper height
int nY = m_nStartY;
Panel *pPrevPanel = NULL;
int nLastCtrlHeight = 0;
FOR_EACH_LL( m_lstControls, i )
{
LayoutInfo_t *pLayoutInfo = m_lstControls[ i ];
Panel *pPanel = pLayoutInfo->pPanel;
// should an advanced option be shown?
if ( pLayoutInfo->bAdvanced )
{
if ( pPanel->IsVisible() != m_bShowAdvancedOptions )
{
pPanel->SetVisible( m_bShowAdvancedOptions );
}
}
if ( !pPanel->IsVisible() )
continue;
if ( pPrevPanel && pLayoutInfo->nOffsetY >= 0 )
{
nY += pPrevPanel->GetTall() + pLayoutInfo->nOffsetY + m_nVerticalBuffer;
}
pPanel->SetPos( pLayoutInfo->nOffsetX ? pLayoutInfo->nOffsetX : m_nDefaultX, nY );
pPrevPanel = pPanel;
nLastCtrlHeight = pPanel->GetTall();
}
m_pBgPanel->SetTall( nY + nLastCtrlHeight + 2 * m_nVerticalBuffer );
}
void CReplayRenderDialog::Close()
{
SetVisible( false );
MarkForDeletion();
TFModalStack()->PopModal( this );
}
void CReplayRenderDialog::OnCommand( const char *pCommand )
{
if ( FStrEq( pCommand, "cancel" ) )
{
Close();
}
else if ( FStrEq( pCommand, "render" ) )
{
Close();
Render();
}
else
{
engine->ClientCmd( const_cast<char *>( pCommand ) );
}
BaseClass::OnCommand( pCommand );
}
void CReplayRenderDialog::Render()
{
// Only complain about QuickTime if we aren't exporting raw TGA's/WAV
if ( !m_pExportRawCheck->IsSelected() )
{
#ifndef USE_WEBM_FOR_REPLAY
if ( !g_pVideo || !g_pVideo->IsVideoSystemAvailable( VideoSystem::QUICKTIME ) )
{
ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_NeedQuicktime", "#GameUI_OK" );
return;
}
if ( g_pVideo->GetVideoSystemStatus( VideoSystem::QUICKTIME ) != VideoSystemStatus::OK )
{
if ( g_pVideo->GetVideoSystemStatus( VideoSystem::QUICKTIME ) == VideoSystemStatus::NOT_CURRENT_VERSION )
{
ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_NeedQuicktimeNewer", "#GameUI_OK" );
return;
}
ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_Err_QT_FailedToLoad", "#GameUI_OK" );
return;
}
#endif
}
// Update convars from settings
const int nMotionBlurQuality = clamp( m_pMotionBlurSlider->GetValue(), 0, MAX_MOTION_BLUR_QUALITY );
replay_rendersetting_quitwhendone.SetValue( (int)m_pQuitWhenDoneCheck->IsSelected() );
replay_rendersetting_exportraw.SetValue( (int)m_pExportRawCheck->IsSelected() );
replay_rendersetting_motionblurquality.SetValue( nMotionBlurQuality );
replay_rendersetting_motionblurenabled.SetValue( replay_rendersetting_motionblur_can_toggle.GetBool() ? (int)m_pMotionBlurCheck->IsSelected() : 1 );
replay_rendersetting_encodingquality.SetValue( clamp( m_pQualitySlider->GetValue() * ReplayVideo_GetQualityInterval(), 0, 100 ) );
if ( m_pGlowEnabledCheck )
{
replay_rendersetting_renderglow.SetValue( m_pGlowEnabledCheck->IsSelected() );
}
ConVarRef replay_voice_during_playback( "replay_voice_during_playback" );
if ( replay_voice_during_playback.IsValid() && m_pPlayVoiceCheck->IsEnabled() )
{
replay_voice_during_playback.SetValue( (int)m_pPlayVoiceCheck->IsSelected() );
}
// Setup parameters for render
RenderMovieParams_t params;
params.m_hReplay = m_hReplay;
params.m_iPerformance = m_iPerformance; // Use performance passed in from details panel
params.m_bQuitWhenFinished = m_pQuitWhenDoneCheck->IsSelected();
params.m_bExportRaw = m_pExportRawCheck->IsSelected();
m_pTitleText->GetText( params.m_wszTitle, sizeof( params.m_wszTitle ) );
#ifdef USE_WEBM_FOR_REPLAY
V_strcpy_safe( params.m_szExtension, ".webm" ); // Use .webm
#else
V_strcpy_safe( params.m_szExtension, ".mov" ); // Use .mov for Quicktime
#endif
const int iRes = m_pVideoModesCombo->GetActiveItem();
const ReplayVideoMode_t &VideoMode = ReplayVideo_GetVideoMode( iRes );
params.m_Settings.m_bMotionBlurEnabled = replay_rendersetting_motionblurenabled.GetBool();
params.m_Settings.m_bAAEnabled = replay_rendersetting_motionblurenabled.GetBool();
params.m_Settings.m_nMotionBlurQuality = nMotionBlurQuality;
params.m_Settings.m_nWidth = VideoMode.m_nWidth;
params.m_Settings.m_nHeight = VideoMode.m_nHeight;
params.m_Settings.m_FPS.SetFPS( VideoMode.m_nBaseFPS, VideoMode.m_bNTSCRate );
params.m_Settings.m_Codec = ReplayVideo_GetCodec( m_pCodecCombo->GetActiveItem() ).m_nCodecId;
params.m_Settings.m_nEncodingQuality = replay_rendersetting_encodingquality.GetInt();
params.m_Settings.m_bRaw = m_pExportRawCheck->IsSelected();
// Calculate the framerate for the engine - for each engine frame, we need the # of motion blur timesteps,
// x 2, since the shutter is open for nNumMotionBlurTimeSteps and closed for nNumMotionBlurTimeSteps,
// with the engine frame centered in the shutter open state (ie when we're half way through the motion blur
// timesteps). Antialiasing does not factor in here because it doesn't require extra frames - the AA jitter
// is interwoven in with the motion sub-frames.
const int nNumMotionBlurTimeSteps = ( params.m_Settings.m_bMotionBlurEnabled ) ? CReplayRenderer::GetNumMotionBlurTimeSteps( params.m_Settings.m_nMotionBlurQuality ) : 1;
if ( params.m_Settings.m_bMotionBlurEnabled )
{
params.m_flEngineFps = 2 * nNumMotionBlurTimeSteps * params.m_Settings.m_FPS.GetFPS();
}
else
{
Assert( nNumMotionBlurTimeSteps == 1 );
params.m_flEngineFps = params.m_Settings.m_FPS.GetFPS();
}
// Close the browser
extern void ReplayUI_CloseReplayBrowser();
ReplayUI_CloseReplayBrowser();
// Hide the console
engine->ExecuteClientCmd( "hideconsole" );
// Stats tracking.
GetReplayGameStatsHelper().SW_ReplayStats_WriteRenderDataStart( params, this );
// Render the movie
g_pReplayMovieManager->RenderMovie( params );
}
void CReplayRenderDialog::OnKeyCodeTyped( vgui::KeyCode code )
{
if( code == KEY_ENTER )
{
OnCommand( "render" );
}
else if ( code == KEY_ESCAPE )
{
MarkForDeletion();
}
else
{
BaseClass::OnKeyCodeTyped( code );
}
}
void CReplayRenderDialog::OnThink()
{
if ( m_pEstimateTimeLabel == NULL || m_pEstimateFileLabel == NULL )
return;
// The replay may be NULL if this dialog is created by 'save all' from the quit confirmation dialog. In this
// case, we don't want to a replay-specific time estimate anyway, so we can just early out here.
CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( g_pReplayManager->GetReplay( m_hReplay) );
if ( !pReplay )
return;
const int nMotionBlurQuality = clamp( m_pMotionBlurSlider->GetValue(), 0, MAX_MOTION_BLUR_QUALITY );
const int nCodecQuality = clamp( m_pQualitySlider->GetValue(), 0, ReplayVideo_GetQualityRange() );
VideoEncodeCodec::EVideoEncodeCodec_t eCodec = ReplayVideo_GetCodec( m_pCodecCombo->GetActiveItem() ).m_nCodecId;
// fFrameSize is the scale factor based on the size of the rendered frame.
const int iRes = m_pVideoModesCombo->GetActiveItem();
const ReplayVideoMode_t &VideoMode = ReplayVideo_GetVideoMode( iRes );
float fFrameSize = (float)(VideoMode.m_nWidth * VideoMode.m_nHeight)/(float)(640*480);
float flEstimatedFileSize = 0;
float flEstimatedRenderTime_Min = 0;
static float mjpegToMotionBlurMultiplierTable[] = { 2.0f, 3.0f, 5.5f, 12.0f };
static float h264ToMotionBlurMultiplierTable[] = { 2.8f, 4.2f, 6.4f, 13.0f };
static float webmToMotionBlurMultiplierTable[] = { 2.8f, 4.2f, 6.4f, 13.0f };
static float mjpegToQualityMultiplierTable[] = { 620.0f, 736.0f, 1284.0f, 2115.0f, 3028.0f };
static float h264ToQualityMultiplierTable[] = { 276.0f, 384.0f, 595.0f, 1026.0f, 1873.0f };
static float webmToQualityMultiplierTable[] = { 125.0f, 250.0f, 312.0f, 673.0f, 1048.0f };
switch ( eCodec )
{
case VideoEncodeCodec::WEBM_CODEC:
flEstimatedFileSize = pReplay->m_flLength * webmToQualityMultiplierTable[nCodecQuality]*fFrameSize;
flEstimatedRenderTime_Min = pReplay->m_flLength * webmToMotionBlurMultiplierTable[nMotionBlurQuality];
break;
case VideoEncodeCodec::H264_CODEC:
flEstimatedFileSize = pReplay->m_flLength * h264ToQualityMultiplierTable[nCodecQuality];
flEstimatedRenderTime_Min = pReplay->m_flLength * h264ToMotionBlurMultiplierTable[nMotionBlurQuality];
break;
case VideoEncodeCodec::MJPEG_A_CODEC:
flEstimatedFileSize = pReplay->m_flLength * mjpegToQualityMultiplierTable[nCodecQuality];
flEstimatedRenderTime_Min = pReplay->m_flLength * mjpegToMotionBlurMultiplierTable[nMotionBlurQuality];
break;
}
float flEstimatedRenderTime_Max = flEstimatedRenderTime_Min * 3.0f;
// @todo Tom Bui: if this goes into hours, we are in trouble...
wchar_t wzFileSize[64];
_snwprintf( wzFileSize, ARRAYSIZE( wzFileSize ), L"%d", (int)flEstimatedFileSize );
wchar_t wzTimeMin[64];
wchar_t wzTimeMax[64];
g_pVGuiLocalize->ConvertANSIToUnicode( CReplayTime::FormatTimeString( flEstimatedRenderTime_Min ), wzTimeMin, sizeof( wzTimeMin ) );
g_pVGuiLocalize->ConvertANSIToUnicode( CReplayTime::FormatTimeString( flEstimatedRenderTime_Max ), wzTimeMax, sizeof( wzTimeMax ) );
wchar_t wzText[256] = L"";
g_pVGuiLocalize->ConstructString_safe( wzText, g_pVGuiLocalize->Find( "#Replay_RenderEstimate_File" ), 1,
wzFileSize,
wzTimeMin,
wzTimeMax );
m_pEstimateFileLabel->SetText( wzText );
g_pVGuiLocalize->ConstructString_safe( wzText, g_pVGuiLocalize->Find( "#Replay_RenderEstimate_Time" ), 2,
wzTimeMin,
wzTimeMax );
m_pEstimateTimeLabel->SetText( wzText );
}
void CReplayRenderDialog::OnTextChanged( KeyValues *data )
{
Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
vgui::ComboBox *pComboBox = dynamic_cast<vgui::ComboBox *>( pPanel );
if ( pComboBox == m_pQualityPresetCombo )
{
m_iQualityPreset = m_pQualityPresetCombo->GetActiveItem();
SetValuesFromQualityPreset();
}
}
void CReplayRenderDialog::OnCheckButtonChecked( vgui::Panel *panel )
{
if ( panel == m_pShowAdvancedOptionsCheck )
{
m_bShowAdvancedOptions = m_pShowAdvancedOptionsCheck->IsSelected();
InvalidateLayout( true, false );
}
}
void CReplayRenderDialog::OnSetFocus()
{
m_pTitleText->RequestFocus();
}
void ReplayUI_ShowRenderDialog( Panel* pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance )
{
CReplayRenderDialog *pRenderDialog = vgui::SETUP_PANEL( new CReplayRenderDialog( pParent, hReplay, bSetQuit, iPerformance ) );
pRenderDialog->SetVisible( true );
pRenderDialog->MakePopup();
pRenderDialog->MoveToFront();
pRenderDialog->SetKeyBoardInputEnabled( true );
pRenderDialog->SetMouseInputEnabled( true );
TFModalStack()->PushModal( pRenderDialog );
}
#endif
@@ -0,0 +1,109 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYBROWSER_RENDERDIALOG_H
#define REPLAYBROWSER_RENDERDIALOG_H
#ifdef _WIN32
#pragma once
#endif
#include "replaybrowserbasepanel.h"
#include "vgui/IScheme.h"
#include "vgui_controls/CheckButton.h"
#include "vgui_controls/ComboBox.h"
#include "vgui_controls/Slider.h"
#include "replay/replayhandle.h"
using namespace vgui;
class CExLabel;
class CExButton;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CReplayRenderDialog : public CReplayBasePanel
{
DECLARE_CLASS_SIMPLE( CReplayRenderDialog, CReplayBasePanel );
public:
CReplayRenderDialog( Panel *pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnCommand( const char *pCommand );
virtual void OnKeyCodeTyped( vgui::KeyCode code );
virtual void OnThink();
MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel );
private:
MESSAGE_FUNC( OnSetFocus, "SetFocus" );
void Close();
void Render();
void ValidateRenderData();
void UpdateControlsValues();
void AddControlToAutoLayout( Panel *pPanel, bool bAdvanced );
void SetValuesFromQualityPreset();
bool m_bShowAdvancedOptions;
int m_iQualityPreset;
ReplayHandle_t m_hReplay;
bool m_bSetQuit;
int m_iPerformance;
CheckButton *m_pPlayVoiceCheck;
CheckButton *m_pShowAdvancedOptionsCheck;
CheckButton *m_pQuitWhenDoneCheck;
CheckButton *m_pExportRawCheck;
CExButton *m_pCancelButton;
CExButton *m_pRenderButton;
TextEntry *m_pTitleText;
ComboBox *m_pVideoModesCombo;
ComboBox *m_pCodecCombo;
CExLabel *m_pQualityPresetLabel;
ComboBox *m_pQualityPresetCombo;
CExLabel *m_pResolutionNoteLabel;
CExLabel *m_pEnterANameLabel;
CExLabel *m_pVideoModeLabel;
CExLabel *m_pTitleLabel;
CExLabel *m_pLockWarningLabel;
CExLabel *m_pCodecLabel;
CExLabel *m_pEstimateTimeLabel;
CExLabel *m_pEstimateFileLabel;
CheckButton *m_pMotionBlurCheck;
CExLabel *m_pMotionBlurLabel;
Slider *m_pMotionBlurSlider;
CExLabel *m_pQualityLabel;
Slider *m_pQualitySlider;
EditablePanel *m_pBgPanel;
Panel *m_pSeparator;
CheckButton *m_pGlowEnabledCheck;
struct LayoutInfo_t
{
Panel *pPanel;
int nOffsetX;
int nOffsetY;
bool bAdvanced;
};
CUtlLinkedList< LayoutInfo_t * > m_lstControls;
CPanelAnimationVarAliasType( int, m_nStartY, "start_y", "0", "proportional_ypos" );
CPanelAnimationVarAliasType( int, m_nVerticalBuffer, "vertical_buffer", "0", "proportional_ypos" );
CPanelAnimationVarAliasType( int, m_nDefaultX, "default_x", "0", "proportional_xpos" );
friend class CReplayGameStatsHelper;
};
//-----------------------------------------------------------------------------
void ReplayUI_ShowRenderDialog( Panel* pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance );
//-----------------------------------------------------------------------------
#endif // REPLAYBROWSER_RENDERDIALOG_H
@@ -0,0 +1,167 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayconfirmquitdlg.h"
#include "vgui_controls/TextImage.h"
#include "vgui_controls/CheckButton.h"
#include "vgui_controls/TextEntry.h"
#include "vgui/IInput.h"
#include "vgui/ISurface.h"
#include "ienginevgui.h"
#include "replay/genericclassbased_replay.h"
#include "replaybrowserrenderdialog.h"
#include "econ/econ_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
using namespace vgui;
//-----------------------------------------------------------------------------
ConVar replay_quitmsg_dontaskagain( "replay_quitmsg_dontaskagain", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "The replay system will ask you to render your replays on quit, unless this cvar is 1.", true, 0, true, 1 );
//-----------------------------------------------------------------------------
CReplayConfirmQuitDialog::CReplayConfirmQuitDialog( Panel *pParent )
: BaseClass( pParent, "confirmquitdlg" ),
m_pDontShowAgain( NULL ),
m_pQuitButton( NULL )
{
SetScheme( "ClientScheme" );
InvalidateLayout( true, true );
}
void CReplayConfirmQuitDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
{
// Link in TF scheme
extern IEngineVGui *enginevgui;
vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
SetScheme( pTFScheme );
SetProportional( true );
BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
LoadControlSettings( "Resource/UI/replaybrowser/confirmquitdlg.res", "GAME" );
m_pDontShowAgain = dynamic_cast< CheckButton * >( FindChildByName( "DontShowThisAgainCheckbox" ) );
m_pQuitButton = dynamic_cast< CExButton * >( FindChildByName( "QuitButton" ) );
if ( m_pQuitButton )
{
m_pQuitButton->GetTextImage()->ClearColorChangeStream();
m_pQuitButton->GetTextImage()->AddColorChange( Color(200,80,60,255), 0 );
}
}
void CReplayConfirmQuitDialog::OnCommand( const char *pCommand )
{
// Store the setting of our "never show this again" checkbox if the user picked anything
// except cancel.
if ( !FStrEq( pCommand, "cancel" ) && m_pDontShowAgain && m_pDontShowAgain->IsSelected() )
{
replay_quitmsg_dontaskagain.SetValue( 1 );
}
if ( FStrEq( pCommand, "rendernow_delay" ) )
{
// Delete this
SetVisible( false );
MarkForDeletion();
// Render all unrendered replays now
ReplayUI_ShowRenderDialog( NULL, REPLAY_HANDLE_INVALID, true, -1 );
}
else if ( FStrEq( pCommand, "rendernow" ) )
{
// Sometimes this message comes in just before input is processed when using a controller
// Refire after a delay
PostMessage( this, new KeyValues( "Command", "command", "rendernow_delay" ), 0.001f );
}
else if ( FStrEq( pCommand, "quit" ) )
{
MarkForDeletion();
engine->ClientCmd_Unrestricted( "quit\n" );
}
else if ( FStrEq( pCommand, "cancel" ) )
{
MarkForDeletion();
}
else if ( FStrEq( pCommand, "gotoreplays"))
{
// "Go to replays"
MarkForDeletion();
engine->ClientCmd( "replay_reloadbrowser" );
}
}
void CReplayConfirmQuitDialog::OnKeyCodeTyped( vgui::KeyCode code )
{
if ( code == KEY_ESCAPE )
{
OnCommand( "cancel" );
}
else
{
BaseClass::OnKeyCodeTyped( code );
}
}
void CReplayConfirmQuitDialog::OnKeyCodePressed( vgui::KeyCode code )
{
if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B || GetBaseButtonCode( code ) == STEAMCONTROLLER_B )
{
OnCommand( "cancel" );
}
else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_A || GetBaseButtonCode( code ) == STEAMCONTROLLER_A )
{
OnCommand( "quit" );
}
else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_X || GetBaseButtonCode( code ) == STEAMCONTROLLER_X )
{
if ( m_pDontShowAgain )
{
m_pDontShowAgain->SetSelected( !m_pDontShowAgain->IsSelected() );
}
}
else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_Y || GetBaseButtonCode( code ) == STEAMCONTROLLER_Y )
{
OnCommand( "gotoreplays" );
}
else
{
BaseClass::OnKeyCodePressed( code );
}
}
bool ReplayUI_ShowConfirmQuitDlg()
{
if ( replay_quitmsg_dontaskagain.GetBool() )
return false;
CReplayConfirmQuitDialog *pConfirmQuitDlg = vgui::SETUP_PANEL( new CReplayConfirmQuitDialog( NULL ) );
if ( pConfirmQuitDlg )
{
vgui::surface()->PlaySound( "replay\\replaydialog_warn.wav" );
// Display the panel!
pConfirmQuitDlg->SetVisible( true );
pConfirmQuitDlg->MakePopup();
pConfirmQuitDlg->MoveToFront();
pConfirmQuitDlg->SetKeyBoardInputEnabled( true );
pConfirmQuitDlg->SetMouseInputEnabled( true );
TFModalStack()->PushModal( pConfirmQuitDlg );
}
return true;
}
#endif
@@ -0,0 +1,45 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef REPLAYBROWSER_CONFIRMQUITDLG_H
#define REPLAYBROWSER_CONFIRMQUITDLG_H
#ifdef _WIN32
#pragma once
#endif
#include "replaybrowserbasepanel.h"
#include "vgui/IScheme.h"
#include "vgui_controls/CheckButton.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CExButton;
class CReplayConfirmQuitDialog : public CReplayBasePanel
{
DECLARE_CLASS_SIMPLE( CReplayConfirmQuitDialog, CReplayBasePanel );
public:
CReplayConfirmQuitDialog( Panel *pParent );
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void OnCommand( const char *pCommand );
virtual void OnKeyCodeTyped( vgui::KeyCode code );
virtual void OnKeyCodePressed( vgui::KeyCode code );
private:
vgui::CheckButton *m_pDontShowAgain;
CExButton *m_pQuitButton;
};
//-----------------------------------------------------------------------------
bool ReplayUI_ShowConfirmQuitDlg();
//-----------------------------------------------------------------------------
#endif // REPLAYBROWSER_CONFIRMQUITDLG_H
@@ -0,0 +1,245 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayinputpanel.h"
#include "replaybrowsermainpanel.h"
#include "replay/replay.h"
#include "vgui_controls/EditablePanel.h"
#include "vgui_controls/TextEntry.h"
#include "vgui/IInput.h"
#include "vgui/ILocalize.h"
#include "ienginevgui.h"
#include "vgui_int.h"
#include "vgui/ISurface.h"
#include "iclientmode.h"
#include "replay/ireplaymanager.h"
#include "econ/econ_controls.h"
#if defined( TF_CLIENT_DLL )
#include "tf_item_inventory.h"
#endif
using namespace vgui;
//-----------------------------------------------------------------------------
static bool s_bPanelVisible = false;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Player input dialog for a replay
//-----------------------------------------------------------------------------
class CReplayInputPanel : public EditablePanel
{
private:
DECLARE_CLASS_SIMPLE( CReplayInputPanel, EditablePanel );
public:
CReplayInputPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
~CReplayInputPanel();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnCommand( const char *command );
virtual void OnKeyCodePressed( KeyCode code );
virtual void OnKeyCodeTyped( KeyCode code );
MESSAGE_FUNC( OnSetFocus, "SetFocus" );
private:
Panel *m_pDlg;
TextEntry *m_pTitleEntry;
ReplayHandle_t m_hReplay;
};
//-----------------------------------------------------------------------------
// Purpose: CReplayInputPanel implementation
//-----------------------------------------------------------------------------
CReplayInputPanel::CReplayInputPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay )
: BaseClass( pParent, pName ),
m_hReplay( hReplay ),
m_pDlg( NULL ),
m_pTitleEntry( NULL )
{
SetScheme( "ClientScheme" );
SetProportional( true );
}
CReplayInputPanel::~CReplayInputPanel()
{
}
void CReplayInputPanel::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replayinputpanel.res", "GAME" );
// Cache off the dlg pointer
m_pDlg = FindChildByName( "Dlg" );
// Setup some action sigsies
m_pDlg->FindChildByName( "SaveButton" )->AddActionSignalTarget( this );
m_pDlg->FindChildByName( "CancelButton" )->AddActionSignalTarget( this );
m_pTitleEntry = static_cast< TextEntry * >( m_pDlg->FindChildByName( "TitleInput" ) );
m_pTitleEntry->SelectAllOnFocusAlways( true );
m_pTitleEntry->SetSelectionBgColor( GetSchemeColor( "Yellow", Color( 255, 255, 255, 255), pScheme ) );
m_pTitleEntry->SetSelectionTextColor( Color( 255, 255, 255, 255 ) );
if ( m_hReplay != REPLAY_HANDLE_INVALID )
{
CReplay *pReplay = g_pReplayManager->GetReplay( m_hReplay );
m_pTitleEntry->SetText( pReplay->m_wszTitle );
}
}
void CReplayInputPanel::PerformLayout()
{
BaseClass::PerformLayout();
SetWide( ScreenWidth() );
SetTall( ScreenHeight() );
// Center
m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
}
void CReplayInputPanel::OnKeyCodeTyped( KeyCode code )
{
if ( code == KEY_ESCAPE )
{
OnCommand( "cancel" );
}
BaseClass::OnKeyCodeTyped( code );
}
void CReplayInputPanel::OnKeyCodePressed( KeyCode code )
{
if ( code == KEY_ENTER )
{
OnCommand( "save" );
}
BaseClass::OnKeyCodePressed( code );
}
void CReplayInputPanel::OnSetFocus()
{
m_pTitleEntry->RequestFocus();
}
void CReplayInputPanel::OnCommand( const char *command )
{
bool bCloseWindow = false;
bool bLocalPlayerDead = false;
if ( !Q_strnicmp( command, "save", 4 ) )
{
if ( m_hReplay != REPLAY_HANDLE_INVALID )
{
// Store the title
CReplay *pReplay = g_pReplayManager->GetReplay( m_hReplay );
if ( pReplay )
{
m_pTitleEntry->GetText( pReplay->m_wszTitle, sizeof( pReplay->m_wszTitle ) );
}
// Cache to disk
g_pReplayManager->FlagReplayForFlush( pReplay, false );
// Add the replay to the browser
CReplayBrowserPanel* pReplayBrowser = ReplayUI_GetBrowserPanel();
if ( pReplayBrowser )
{
pReplayBrowser->OnSaveReplay( m_hReplay );
}
// Display a message - if we somehow disconnect, we can crash here if local player isn't checked
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
g_pClientMode->DisplayReplayMessage( pLocalPlayer->IsAlive() ? "#Replay_ReplaySavedAlive" : "#Replay_ReplaySavedDead", -1.0f, false, "replay\\saved.wav", false );
// Check to see if player's dead - used later to determine if we should show items window
bLocalPlayerDead = !pLocalPlayer->IsAlive();
}
}
bCloseWindow = true;
}
else if ( !Q_strnicmp( command, "cancel", 6 ) )
{
bCloseWindow = true;
}
// Close the window?
if ( bCloseWindow )
{
s_bPanelVisible = false;
SetVisible( false );
TFModalStack()->PopModal( this );
MarkForDeletion();
// This logic is perhaps a smidge of a hack. We have to be careful about executing "gameui_hide"
// since it will hide the item pickup panel. If there are no items to be picked up, we can safely
// hide the gameui panel, but we have to call CheckForRoomAndForceDiscard() (as ShowItemsPickedUp()
// does if no items are picked up). Otherwise, skip the "gameui_hide" call and show the item pickup
// panel.
#if defined( TF_CLIENT_DLL )
if ( TFInventoryManager()->GetNumItemPickedUpItems() == 0 )
{
TFInventoryManager()->CheckForRoomAndForceDiscard();
engine->ClientCmd_Unrestricted( "gameui_hide" );
}
else if ( bLocalPlayerDead )
{
// Now show the items pickup screen if player's dead
TFInventoryManager()->ShowItemsPickedUp();
}
#endif
}
BaseClass::OnCommand( command );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool IsReplayInputPanelVisible()
{
return s_bPanelVisible;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ShowReplayInputPanel( ReplayHandle_t hReplay )
{
vgui::DHANDLE< CReplayInputPanel > hReplayInputPanel;
hReplayInputPanel = vgui::SETUP_PANEL( new CReplayInputPanel( NULL, "ReplayInputPanel", hReplay ) );
hReplayInputPanel->SetVisible( true );
hReplayInputPanel->MakePopup();
hReplayInputPanel->MoveToFront();
hReplayInputPanel->SetKeyBoardInputEnabled(true);
hReplayInputPanel->SetMouseInputEnabled(true);
TFModalStack()->PushModal( hReplayInputPanel );
engine->ClientCmd_Unrestricted( "gameui_hide" );
s_bPanelVisible = true;
}
//-----------------------------------------------------------------------------
// Purpose: Test the replay input dialog
//-----------------------------------------------------------------------------
CON_COMMAND_F( open_replayinputpanel, "Open replay input panel test", FCVAR_NONE )
{
ShowReplayInputPanel( NULL );
}
#endif
@@ -0,0 +1,25 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAY_INPUT_PANEL_H
#define REPLAY_INPUT_PANEL_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "replay/replayhandle.h"
//-----------------------------------------------------------------------------
// Purpose: Show Replay input panel for entering a title, etc.
//-----------------------------------------------------------------------------
void ShowReplayInputPanel( ReplayHandle_t hReplay );
//-----------------------------------------------------------------------------
// Purpose: Is the panel visible?
//-----------------------------------------------------------------------------
bool IsReplayInputPanelVisible();
#endif // REPLAY_INPUT_PANEL_H
@@ -0,0 +1,375 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//----------------------------------------------------------------------------------------
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replaymessagepanel.h"
#include "vgui_controls/CheckButton.h"
#include "ienginevgui.h"
#include "vgui_controls/PHandle.h"
#include "econ/econ_controls.h"
#if defined( CSTRIKE_DLL )
# include "cstrike/clientmode_csnormal.h"
#elif defined( TF_CLIENT_DLL )
# include "tf/clientmode_tf.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
#if _DEBUG
CON_COMMAND( testreplaymessagepanel, "" )
{
CReplayMessagePanel *pPanel = new CReplayMessagePanel( "#Replay_StartRecord", replay_msgduration_misc.GetFloat(), rand()%2==0);
pPanel->Show();
}
CON_COMMAND( testreplaymessagedlg, "" )
{
CReplayMessageDlg *pPanel = SETUP_PANEL( new CReplayMessageDlg( "text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text." ) );
pPanel->SetVisible( true );
pPanel->MakePopup();
pPanel->MoveToFront();
pPanel->SetKeyBoardInputEnabled( true );
pPanel->SetMouseInputEnabled( true );
pPanel->RequestFocus();
engine->ClientCmd_Unrestricted( "gameui_hide" );
}
#endif
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
typedef vgui::DHANDLE< CReplayMessagePanel > ReplayMessagePanelHandle_t;
static CUtlVector< ReplayMessagePanelHandle_t > g_vecReplayMessagePanels;
//-----------------------------------------------------------------------------
ConVar replay_msgduration_startrecord( "replay_msgduration_startrecord", "6", FCVAR_DONTRECORD, "Duration for start record message.", true, 0.0f, true, 10.0f );
ConVar replay_msgduration_stoprecord( "replay_msgduration_stoprecord", "6", FCVAR_DONTRECORD, "Duration for stop record message.", true, 0.0f, true, 10.0f );
ConVar replay_msgduration_replaysavailable( "replay_msgduration_replaysavailable", "6", FCVAR_DONTRECORD, "Duration for replays available message.", true, 0.0f, true, 10.0f );
ConVar replay_msgduration_error( "replay_msgduration_error", "6", FCVAR_DONTRECORD, "Duration for replays available message.", true, 0.0f, true, 10.0f );
ConVar replay_msgduration_misc( "replay_msgduration_misc", "5", FCVAR_DONTRECORD, "Duration for misc replays messages (server errors and such).", true, 0.0f, true, 10.0f );
ConVar replay_msgduration_connectrecording( "replay_msgduration_connectrecording", "8", FCVAR_DONTRECORD, "Duration for the message that pops up when you connect to a server already recording replays.", true, 0.0f, true, 15.0f );
//-----------------------------------------------------------------------------
CReplayMessageDlg::CReplayMessageDlg( const char *pText )
: BaseClass( NULL, "ReplayMessageDlg" ),
m_pOKButton( NULL ),
m_pDlg( NULL ),
m_pMsgLabel( NULL )
{
InvalidateLayout( true, true );
m_pMsgLabel->SetText( pText );
}
CReplayMessageDlg::~CReplayMessageDlg()
{
}
void CReplayMessageDlg::ApplySchemeSettings( IScheme *pScheme )
{
// Link in TF scheme
extern IEngineVGui *enginevgui;
vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
SetScheme( pTFScheme );
SetProportional( true );
BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
LoadControlSettings( "resource/ui/replaymessagedlg.res", "GAME" );
m_pDlg = FindChildByName( "Dlg" );
m_pOKButton = dynamic_cast< CExButton * >( m_pDlg->FindChildByName( "OKButton" ) );
m_pMsgLabel = dynamic_cast< CExLabel * >( m_pDlg->FindChildByName( "TextLabel") );
m_pOKButton->AddActionSignalTarget( this );
}
void CReplayMessageDlg::PerformLayout()
{
BaseClass::PerformLayout();
SetWide( ScreenWidth() );
SetTall( ScreenHeight() );
// Center dlg on screen
m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
// Position OK below text label, centered horizontally
int nButtonX = XRES(13);
int nButtonY = m_pDlg->GetTall() - m_pOKButton->GetTall() - YRES( 10 );
m_pOKButton->SetPos( nButtonX, nButtonY );
}
void CReplayMessageDlg::Close()
{
// Hide / delete / hide game UI
SetVisible( false );
MarkForDeletion();
engine->ClientCmd_Unrestricted( "gameui_hide" );
}
void CReplayMessageDlg::OnCommand( const char *pCommand )
{
if ( FStrEq( pCommand, "close" ) )
{
Close();
}
BaseClass::OnCommand( pCommand );
}
void CReplayMessageDlg::OnKeyCodeTyped( KeyCode nCode )
{
switch ( nCode )
{
case KEY_ESCAPE:
case KEY_SPACE:
case KEY_ENTER:
Close();
return;
}
BaseClass::OnKeyCodeTyped( nCode );
}
//-----------------------------------------------------------------------------
int CReplayMessagePanel::InstanceCount()
{
return g_vecReplayMessagePanels.Count();
}
void CReplayMessagePanel::RemoveAll()
{
FOR_EACH_VEC( g_vecReplayMessagePanels, i )
{
CReplayMessagePanel *pCurPanel = g_vecReplayMessagePanels[ i ];
pCurPanel->MarkForDeletion();
}
g_vecReplayMessagePanels.RemoveAll();
}
//-----------------------------------------------------------------------------
ReplayMessagePanelHandle_t GetReplayMessagePanelHandle( CReplayMessagePanel *pPanel )
{
ReplayMessagePanelHandle_t hThis;
hThis = pPanel;
return hThis;
}
CReplayMessagePanel::CReplayMessagePanel( const char *pLocalizeName, float flDuration, bool bUrgent )
: EditablePanel( g_pClientMode->GetViewport(), "ReplayMessagePanel" ),
m_bUrgent( bUrgent )
{
m_flShowStartTime = 0;
m_flShowDuration = flDuration;
m_pMessageLabel = new CExLabel( this, "MessageLabel", pLocalizeName );
m_pReplayLabel = new CExLabel( this, "ReplayLabel", "" );
m_pIcon = new ImagePanel( this, "Icon" );
#if defined( TF_CLIENT_DLL )
const char *pBorderName = bUrgent ? "ReplayFatLineBorderRedBGOpaque" : "ReplayFatLineBorderOpaque";
V_strncpy( m_szBorderName, pBorderName, sizeof( m_szBorderName ) );
#endif
g_vecReplayMessagePanels.AddToTail( GetReplayMessagePanelHandle( const_cast< CReplayMessagePanel * >( this ) ) );
InvalidateLayout( true, true );
ivgui()->AddTickSignal( GetVPanel(), 10 );
}
CReplayMessagePanel::~CReplayMessagePanel()
{
// CUtlVector<>::Find() vomits.
int iFind = g_vecReplayMessagePanels.InvalidIndex();
FOR_EACH_VEC( g_vecReplayMessagePanels, i )
{
if ( g_vecReplayMessagePanels[ i ].Get() == this )
{
iFind = i;
}
}
// Remove, if found.
if ( iFind != g_vecReplayMessagePanels.InvalidIndex() )
{
g_vecReplayMessagePanels.FastRemove( iFind );
}
ivgui()->RemoveTickSignal( GetVPanel() );
}
void CReplayMessagePanel::Show()
{
m_pMessageLabel->SetVisible( true );
// Setup start time
m_flShowStartTime = gpGlobals->curtime;
m_pMessageLabel->MoveToFront();
SetAlpha( 0 );
}
inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax )
{
float flDenom = flInMax - flInMin;
if ( flDenom == 0.0f )
return 0.0f;
float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f );
return Lerp( t, flOutMin, flOutMax );
}
inline float SCurve( float t )
{
t = clamp( t, 0.0f, 1.0f );
return t * t * (3 - 2*t);
}
void CReplayMessagePanel::OnTick()
{
// Hide if taking screenshot
extern ConVar hud_freezecamhide;
extern bool IsTakingAFreezecamScreenshot();
if ( hud_freezecamhide.GetBool() && IsTakingAFreezecamScreenshot() )
{
SetVisible( false );
return;
}
// Delete the panel if life exceeded
const float flEndTime = m_flShowStartTime + m_flShowDuration;
if ( gpGlobals->curtime >= flEndTime )
{
SetVisible( false );
MarkForDeletion();
return;
}
SetVisible( true );
const float flFadeDuration = .4f;
float flAlpha;
// Fade out?
if ( gpGlobals->curtime >= flEndTime - flFadeDuration )
{
flAlpha = LerpScale( gpGlobals->curtime, flEndTime - flFadeDuration, flEndTime, 1.0f, 0.0f );
}
// Fade in?
else if ( gpGlobals->curtime <= m_flShowStartTime + flFadeDuration )
{
flAlpha = LerpScale( gpGlobals->curtime, m_flShowStartTime, m_flShowStartTime + flFadeDuration, 0.0f, 1.0f );
}
// Otherwise, we must be in between fade in/fade out
else
{
flAlpha = 1.0f;
}
SetAlpha( 255 * SCurve( flAlpha ) );
}
void CReplayMessagePanel::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replaymessage.res", "GAME" );
#if defined( CSTRIKE_DLL )
SetPaintBackgroundEnabled( true );
SetPaintBorderEnabled( false );
SetPaintBackgroundType( 0 );
SetBgColor( pScheme->GetColor( m_bUrgent ? "DarkRed" : "DarkGray", Color( 255, 255, 255, 255 ) ) );
#endif
}
void CReplayMessagePanel::PerformLayout()
{
BaseClass::PerformLayout();
#if defined( TF_CLIENT_DLL )
// Set the border if one was specified
if ( m_szBorderName[0] )
{
SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( m_szBorderName ) );
}
#endif
// Adjust overall panel size depending on min-mode
#if defined( TF_CLIENT_DLL )
extern ConVar cl_hud_minmode;
bool bMinMode = cl_hud_minmode.GetBool();
#else
bool bMinMode = false;
#endif
int nVerticalBuffer = bMinMode ? YRES(3) : YRES(5);
int nMessageLabelY = nVerticalBuffer;
int nVerticalOffsetBetweenPanels = YRES(6);
// Only display replay icon and "replay" label if this is the top-most (vertically) panel
// and we're not in min-mode
Assert( InstanceCount() > 0 );
if ( !InstanceCount() || bMinMode || g_vecReplayMessagePanels[ 0 ].Get() != this )
{
m_pIcon->SetTall( 0 );
m_pReplayLabel->SetTall( 0 );
nVerticalOffsetBetweenPanels = YRES(1);
}
else
{
m_pReplayLabel->SizeToContents();
nMessageLabelY += m_pReplayLabel->GetTall();
nVerticalOffsetBetweenPanels = YRES(6);
}
// Resize the message label to fit the text
m_pMessageLabel->SizeToContents();
// Adjust this panel's height to fit the label size
SetTall( nMessageLabelY + m_pMessageLabel->GetTall() + nVerticalBuffer );
// Set the message label's position
m_pMessageLabel->SetPos( XRES(8), nMessageLabelY );
// Get the bottom of the bottom-most message panel
int nMaxY = 0;
FOR_EACH_VEC( g_vecReplayMessagePanels, it )
{
CReplayMessagePanel *pPanel = g_vecReplayMessagePanels[ it ];
if ( pPanel == this )
continue;
int nX, nY;
pPanel->GetPos( nX, nY );
nMaxY = MAX( nMaxY, pPanel->GetTall() + nY );
}
// Adjust this panel's position to be below bottom-most panel
// NOTE: Intentionally using YRES() for xpos, since we want to match offsets in both x & y margins
SetPos( YRES(6), nMaxY + nVerticalOffsetBetweenPanels );
}
//-----------------------------------------------------------------------------
#endif
@@ -0,0 +1,85 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//----------------------------------------------------------------------------------------
#ifndef REPLAYMESSAGEPANEL_H
#define REPLAYMESSAGEPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/EditablePanel.h"
using namespace vgui;
//----------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------
extern ConVar replay_msgduration_startrecord;
extern ConVar replay_msgduration_stoprecord;
extern ConVar replay_msgduration_replaysavailable;
extern ConVar replay_msgduration_error;
extern ConVar replay_msgduration_misc;
extern ConVar replay_msgduration_connectrecording;
//----------------------------------------------------------------------------------------
// Purpose: Forward declarations
//----------------------------------------------------------------------------------------
class CExLabel;
class CExButton;
class CReplayMessageDlg : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayMessageDlg, EditablePanel );
public:
CReplayMessageDlg( const char *pText );
~CReplayMessageDlg();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnKeyCodeTyped( KeyCode nCode );
virtual void OnCommand( const char *pCommand );
private:
void Close();
Panel *m_pDlg;
CExLabel *m_pMsgLabel;
CExButton *m_pOKButton;
};
//----------------------------------------------------------------------------------------
// Purpose: A panel for display messages from the replay system during gameplay
//----------------------------------------------------------------------------------------
class CReplayMessagePanel : public EditablePanel
{
DECLARE_CLASS_SIMPLE( CReplayMessagePanel, EditablePanel );
public:
CReplayMessagePanel( const char *pLocalizeName, float flDuration, bool bUrgent );
virtual ~CReplayMessagePanel();
void Show();
virtual void OnTick();
static int InstanceCount();
static void RemoveAll();
private:
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
CExLabel *m_pMessageLabel;
CExLabel *m_pReplayLabel;
ImagePanel *m_pIcon;
float m_flShowStartTime;
float m_flShowDuration;
bool m_bUrgent;
#if defined( TF_CLIENT_DLL )
char m_szBorderName[ 64 ];
#endif
};
#endif // REPLAYMESSAGEPANEL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,241 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#if defined( REPLAY_ENABLED )
#ifndef REPLAYPERFORMANCEEDITOR_H
#define REPLAYPERFORMANCEEDITOR_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/EditablePanel.h"
#include "vgui_controls/ImagePanel.h"
#include "vgui_controls/ImageList.h"
#include "tf/vgui/tf_controls.h"
#include "replay/replayhandle.h"
#include "replay/ireplayperformanceeditor.h"
#include "replay/ireplayperformancecontroller.h"
//-----------------------------------------------------------------------------
class CPlayerCell;
class CCameraOptionsPanel;
class CRecLightPanel;
class CReplay;
class CReplayPerformance;
class CReplayTipLabel;
class CSavingDialog;
//-----------------------------------------------------------------------------
// NOTE: Should not change order here - if you do, you need to modify g_pCamNames.
enum CameraMode_t
{
CAM_INVALID = -1,
CAM_FREE,
CAM_THIRD,
CAM_FIRST,
COMPONENT_TIMESCALE,
NCAMS
};
//-----------------------------------------------------------------------------
class CReplayPerformanceEditorPanel : public vgui::EditablePanel,
public IReplayPerformanceEditor
{
DECLARE_CLASS_SIMPLE( CReplayPerformanceEditorPanel, vgui::EditablePanel );
public:
CReplayPerformanceEditorPanel( Panel *parent, ReplayHandle_t hReplay );
virtual ~CReplayPerformanceEditorPanel();
virtual void ShowPanel( bool bShow );
bool OnEndOfReplayReached();
void OnInGameMouseWheelEvent( int nDelta );
void UpdateCameraSelectionPosition( CameraMode_t nCameraMode );
void UpdateFreeCamSettings( const SetViewParams_t &params );
void UpdateTimeScale( float flScale );
void HandleUiToggle();
void Exit();
void Exit_ShowDialogs();
private:
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void ApplySettings( KeyValues *pInResourceData );
virtual void PerformLayout();
virtual void OnCommand( const char *command );
virtual void OnMouseWheeled( int nDelta );
virtual void OnTick();
void Achievements_Think( float flElapsed );
void Achievements_OnSpaceBarPressed();
void Achievements_Grant();
friend class CReplayButton;
friend class CSavingDialog;
void SetButtonTip( wchar_t *pTipText, Panel *pContextPanel );
void ShowButtonTip( bool bShow );
void ShowSavingDialog();
//
// IReplayPerformanceEditor:
//
virtual CReplay *GetReplay();
virtual void OnRewindComplete();
// Called when the user attempts to change to a different camera, etc.
// Returns true if request is immediately granted - false means the event
// was queued and the user has been asked if they are OK with nuking any
// changes after the current time.
bool OnStateChangeRequested( const char *pEventStr );
void EnsureRecording( bool bShouldSnip = true ); // Start recording now if not already doing so
bool IsPaused();
void UpdateCameraButtonImages( bool bForceUseUnselected = false );
void LayoutPlayerCells();
void SetupHighlightPanel( EditablePanel *pPanel, CPlayerCell *pPlayerCell );
void UpdateTimeLabels();
void ClearPlayerCellData();
void HandleMouseWheel( int nDelta );
private:
enum ControlButtons_t
{
CTRLBUTTON_IN,
CTRLBUTTON_GOTOBEGINNING,
CTRLBUTTON_REWIND,
CTRLBUTTON_PLAY,
CTRLBUTTON_FF,
CTRLBUTTON_GOTOEND,
CTRLBUTTON_OUT,
NUM_CTRLBUTTONS
};
CReplayPerformance *GetPerformance() const;
CReplayPerformance *GetSavedPerformance() const;
int GetCameraModeFromButtonIndex( CameraMode_t iCamera );
void AddSetViewEvent();
void AddTimeScaleEvent( float flTimeScale );
void AddPanelKeyboardInputDisableList( vgui::Panel *pPanel );
CameraMode_t IsMouseOverActiveCameraOptionsPanel( int nMouseX, int nMouseY );
void SetOrRemoveInTick( int nTick, bool bRemoveIfSet );
void SetOrRemoveOutTick( int nTick, bool bRemoveIfSet );
void SetOrRemoveTick( int nTick, bool bUseInTick, bool bRemoveIfSet );
void ToggleMenu();
void OnMenuCommand_Save( bool bExitEditorWhenDone = false );
void OnMenuCommand_SaveAs( bool bExitEditorWhenDone = false );
void OnMenuCommand_Exit();
void DisplaySavedTip( bool bSucceess );
void OnSaveComplete();
void SaveAs( const wchar_t *pTitle );
void ShowRewindConfirmMessage();
static void OnConfirmSaveAs( bool bShouldSave, wchar_t *pTitle, void *pContext );
static void OnConfirmDestroyChanges( bool bConfirmed, void *pContext );
static void OnConfirmDiscard( bool bConfirmed, void *pContext );
static void OnConfirmExit( bool bConfirmed, void *pContext );
static void OnConfirmRewind( bool bConfirmed, void *pContext );
MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pParams );
ReplayHandle_t m_hReplay;
float m_flLastTime; // Can't use gpGlobals->frametime when playback is paused
float m_flOldFps;
CExLabel *m_pCurTimeLabel;
CExLabel *m_pTotalTimeLabel;
CExLabel *m_pPlayerNameLabel;
KeyValues *m_pPlayerCellData;
CPlayerCell *m_pPlayerCells[2][MAX_PLAYERS+1];
vgui::ImageList *m_pImageList;
EditablePanel *m_pMouseTargetPanel;
EditablePanel *m_pBottom;
CPlayerCell *m_pCurTargetCell;
CExImageButton *m_pCameraButtons[NCAMS];
CExImageButton *m_pCtrlButtons[NUM_CTRLBUTTONS];
float m_flTimeScaleProxy;
EditablePanel *m_pPlayerCellsPanel;
vgui::ImagePanel *m_pCameraSelection;
CameraMode_t m_iCameraSelection; // NOTE: Indexes into some arrays
CReplayTipLabel *m_pButtonTip;
CSavingDialog *m_pSavingDlg;
enum MenuItems_t
{
MENU_SAVE,
MENU_SAVEAS,
MENU_EXIT,
NUM_MENUITEMS
};
CExImageButton *m_pMenuButton;
vgui::Menu *m_pMenu;
int m_aMenuItemIds[ NUM_MENUITEMS ];
CExButton *m_pSlowMoButton;
CCameraOptionsPanel *m_pCameraOptionsPanels[NCAMS];
CUtlLinkedList< vgui::Panel *, int > m_lstDisableKeyboardInputPanels;
int m_nRedBlueLabelRightX;
int m_nBottomPanelStartY;
int m_nBottomPanelHeight;
int m_nRedBlueSigns[2];
int m_iCurPlayerTarget;
float m_flSpaceDownStart; // The time at which user started holding down space bar
bool m_bSpaceDown;
bool m_bSpacePressed;
int m_nLastRoundedTime;
bool m_bMousePressed;
bool m_bMouseDown;
float m_flDefaultFramerate; // host_framerate before perf editor started mucking about with it
CameraMode_t m_nMouseClickedOverCameraSettingsPanel; // Allows user to drag slider outside of camera settings panel w/o the panel disappearing
CRecLightPanel *m_pRecLightPanel;
bool m_bShownAtLeastOnce; // Has the replay editor shown at least once? In other words, has the user hit the space bar at all yet?
char m_szSuspendedEvent[128];
bool m_bAchievementAwarded; // Was an achievement awarded during this editing session?
float m_flLastTimeSpaceBarPressed;
float m_flActiveTimeInEditor; // Will be zero'd out if user is idle (ie if they don't press space bar often enough)
CPanelAnimationVarAliasType( int, m_nRightMarginWidth, "right_margin_width", "0", "proportional_xpos" );
bool m_bCurrentTargetNeedsVisibilityUpdate;
};
//-----------------------------------------------------------------------------
CReplayPerformanceEditorPanel *ReplayUI_InitPerformanceEditor( ReplayHandle_t hReplay );
CReplayPerformanceEditorPanel *ReplayUI_GetPerformanceEditor();
void ReplayUI_ClosePerformanceEditor();
//-----------------------------------------------------------------------------
#endif // REPLAYPERFORMANCEEDITOR_H
#endif
@@ -0,0 +1,261 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayperformancesavedlg.h"
#include "replay/performance.h"
#include "replay/ireplaymanager.h"
#include "replay/ireplayperformancecontroller.h"
#include "replay/replay.h"
#include "econ/confirm_dialog.h"
#include "vgui_controls/EditablePanel.h"
#include "vgui_controls/TextEntry.h"
#include "vgui_controls/TextImage.h"
#include "vgui/ISurface.h"
#include "replay/replaycamera.h"
#include "replayperformanceeditor.h"
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: Player input dialog for a replay
//-----------------------------------------------------------------------------
class CReplayPerformanceSaveDlg : public EditablePanel
{
private:
DECLARE_CLASS_SIMPLE( CReplayPerformanceSaveDlg, EditablePanel );
public:
CReplayPerformanceSaveDlg( Panel *pParent, const char *pName,
OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay, bool bExitEditorWhenDone );
~CReplayPerformanceSaveDlg();
static void Show( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
bool bExitEditorWhenDone );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual void OnCommand( const char *command );
virtual void OnKeyCodePressed( KeyCode code );
virtual void OnKeyCodeTyped( KeyCode code );
bool ConfirmOverwriteOrSaveNow();
void CloseWindow();
static void OnConfirmOverwrite( bool bConfirm, void *pContext );
MESSAGE_FUNC( OnSetFocus, "SetFocus" );
static vgui::DHANDLE< CReplayPerformanceSaveDlg > ms_hDlg;
private:
OnConfirmSaveCallback m_pfnCallback;
void *m_pContext;
Panel *m_pDlg;
CReplay *m_pReplay;
TextEntry *m_pTitleEntry;
bool m_bExitEditorWhenDone;
wchar_t m_wszTitle[ MAX_TAKE_TITLE_LENGTH ];
};
vgui::DHANDLE< CReplayPerformanceSaveDlg > CReplayPerformanceSaveDlg::ms_hDlg;
//-----------------------------------------------------------------------------
// Purpose: CReplayPerformanceSaveDlg implementation
//-----------------------------------------------------------------------------
CReplayPerformanceSaveDlg::CReplayPerformanceSaveDlg( Panel *pParent, const char *pName,
OnConfirmSaveCallback pfnCallback, void *pContext,
CReplay *pReplay, bool bExitEditorWhenDone )
: BaseClass( pParent, pName ),
m_pfnCallback( pfnCallback ),
m_pContext( pContext ),
m_pReplay( pReplay ),
m_bExitEditorWhenDone( bExitEditorWhenDone ),
m_pDlg( NULL ),
m_pTitleEntry( NULL )
{
Assert( m_pContext );
SetScheme( "ClientScheme" );
SetProportional( true );
}
CReplayPerformanceSaveDlg::~CReplayPerformanceSaveDlg()
{
ms_hDlg = NULL;
}
/*static*/ void CReplayPerformanceSaveDlg::Show( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
bool bExitEditorWhenDone )
{
Assert( !ms_hDlg.Get() );
ms_hDlg = vgui::SETUP_PANEL( new CReplayPerformanceSaveDlg( NULL, "ReplayInputPanel", pfnCallback, pContext, pReplay, bExitEditorWhenDone ) );
ms_hDlg->SetVisible( true );
ms_hDlg->MakePopup();
ms_hDlg->MoveToFront();
ms_hDlg->SetKeyBoardInputEnabled(true);
ms_hDlg->SetMouseInputEnabled(true);
TFModalStack()->PushModal( ms_hDlg );
engine->ClientCmd_Unrestricted( "gameui_hide" );
ReplayCamera()->EnableInput( false );
}
void CReplayPerformanceSaveDlg::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/ui/replayperformanceeditor/savedlg.res", "GAME" );
// Cache off the dlg pointer
m_pDlg = FindChildByName( "Dlg" );
CExButton *pDiscardButton;
pDiscardButton = dynamic_cast< CExButton * >( m_pDlg->FindChildByName( "DiscardButton" ) );
SetXToRed( pDiscardButton );
// Setup some action sigs
m_pDlg->FindChildByName( "SaveButton" )->AddActionSignalTarget( this );
m_pDlg->FindChildByName( "CancelButton" )->AddActionSignalTarget( this );
pDiscardButton->AddActionSignalTarget( this );
m_pTitleEntry = static_cast< TextEntry * >( m_pDlg->FindChildByName( "TitleInput" ) );
m_pTitleEntry->SelectAllOnFocusAlways( true );
m_pTitleEntry->SetSelectionBgColor( GetSchemeColor( "Yellow", Color( 255, 255, 255, 255), pScheme ) );
m_pTitleEntry->SetSelectionTextColor( Color( 255, 255, 255, 255 ) );
m_pTitleEntry->SetText( L"" );
}
void CReplayPerformanceSaveDlg::PerformLayout()
{
BaseClass::PerformLayout();
SetWide( ScreenWidth() );
SetTall( ScreenHeight() );
// Center
m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
}
void CReplayPerformanceSaveDlg::OnKeyCodeTyped( KeyCode code )
{
if ( code == KEY_ESCAPE )
{
surface()->PlaySound( "replay\\record_fail.wav" );
return;
}
BaseClass::OnKeyCodeTyped( code );
}
void CReplayPerformanceSaveDlg::OnKeyCodePressed( KeyCode code )
{
if ( code == KEY_ENTER )
{
OnCommand( "save" );
}
BaseClass::OnKeyCodePressed( code );
}
void CReplayPerformanceSaveDlg::OnSetFocus()
{
m_pTitleEntry->RequestFocus();
}
/*static*/ void CReplayPerformanceSaveDlg::OnConfirmOverwrite( bool bConfirm, void *pContext )
{
CReplayPerformanceSaveDlg *pThis = (CReplayPerformanceSaveDlg *)pContext;
pThis->m_pfnCallback( bConfirm, pThis->m_wszTitle, pThis->m_pContext );
pThis->CloseWindow();
}
bool CReplayPerformanceSaveDlg::ConfirmOverwriteOrSaveNow()
{
// Using the same title as an existing performance?
CReplayPerformance *pExistingPerformance = m_pReplay->GetPerformanceWithTitle( m_wszTitle );
if ( pExistingPerformance )
{
ShowConfirmDialog( "#Replay_OverwriteDlgTitle", "#Replay_OverwriteDlgText",
"#Replay_ConfirmOverwrite", "#Replay_Cancel", OnConfirmOverwrite, NULL, this );
return false;
}
m_pfnCallback( true, m_wszTitle, m_pContext );
return true;
}
void CReplayPerformanceSaveDlg::OnCommand( const char *command )
{
bool bCloseWindow = false;
extern IReplayPerformanceController *g_pReplayPerformanceController;
if ( !Q_strnicmp( command, "save", 4 ) )
{
// Get the text and save the replay/performance immediately
m_pTitleEntry->GetText( m_wszTitle, MAX_TAKE_TITLE_LENGTH );
// If we aren't overwriting an existing performance, this func will return true.
bCloseWindow = ConfirmOverwriteOrSaveNow();
}
else if ( !Q_strnicmp( command, "cancel", 6 ) )
{
bCloseWindow = true;
}
// Close the window?
if ( bCloseWindow )
{
CloseWindow();
}
BaseClass::OnCommand( command );
}
void CReplayPerformanceSaveDlg::CloseWindow()
{
SetVisible( false );
MarkForDeletion();
TFModalStack()->PopModal( ms_hDlg.Get() );
ReplayCamera()->EnableInput( true );
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( m_bExitEditorWhenDone && pEditor )
{
pEditor->Exit();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveCallback pfnCallback,
void *pContext, CReplay *pReplay,
bool bExitEditorWhenDone )
{
CReplayPerformanceSaveDlg::Show( pfnCallback, pContext, pReplay, bExitEditorWhenDone );
}
bool ReplayUI_IsPerformanceSaveDlgOpen()
{
return CReplayPerformanceSaveDlg::ms_hDlg.Get() != NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Test the replay input dialog
//-----------------------------------------------------------------------------
CON_COMMAND_F( replay_test_take_save_dlg, "Open replay save take dlg", FCVAR_NONE )
{
ReplayUI_ShowPerformanceSaveDlg( NULL, NULL, NULL, false );
}
#endif
@@ -0,0 +1,27 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAYPERFORMANCESAVEDLG_H
#define REPLAYPERFORMANCESAVEDLG_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
class CReplay;
//-----------------------------------------------------------------------------
typedef void (*OnConfirmSaveCallback)( bool bConfirmed, wchar_t *pTitle, void *pContext );
//-----------------------------------------------------------------------------
void ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
bool bExitEditorWhenDone );
bool ReplayUI_IsPerformanceSaveDlgOpen();
//-----------------------------------------------------------------------------
#endif // REPLAYPERFORMANCESAVEDLG_H
@@ -0,0 +1,163 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayreminderpanel.h"
#include "replay/ireplaysystem.h"
#include "replay/replay.h"
#include "replay/ireplayscreenshotmanager.h"
#include "replay/ireplaymanager.h"
#include "replay/screenshot.h"
#include "iclientmode.h"
#include "vgui_controls/AnimationController.h"
//-----------------------------------------------------------------------------
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
DECLARE_HUDELEMENT( CReplayReminderPanel );
//-----------------------------------------------------------------------------
CReplayReminderPanel::CReplayReminderPanel( const char *pElementName )
: EditablePanel( g_pClientMode->GetViewport(), "ReplayReminder" ),
CHudElement( pElementName )
{
SetScheme( "ClientScheme" );
m_flShowTime = 0;
m_bShouldDraw = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::SetupText()
{
// Get current key binding, if any.
const char *pBoundKey = engine->Key_LookupBinding( "save_replay" );
if ( !pBoundKey || FStrEq( pBoundKey, "(null)" ) )
{
pBoundKey = " ";
}
char szKey[16];
Q_snprintf( szKey, sizeof(szKey), "%s", pBoundKey );
wchar_t wKey[16];
wchar_t wLabel[256];
g_pVGuiLocalize->ConvertANSIToUnicode( szKey, wKey, sizeof( wKey ) );
g_pVGuiLocalize->ConstructString_safe( wLabel, g_pVGuiLocalize->Find("#Replay_freezecam_replay" ), 1, wKey );
// Set the text
SetDialogVariable( "text", wLabel );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::ApplySchemeSettings( IScheme *pScheme )
{
LoadControlSettings("Resource/UI/ReplayReminder.res", "GAME");
BaseClass::ApplySchemeSettings( pScheme );
SetupText();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::Show()
{
m_flShowTime = gpGlobals->curtime;
SetVisible( true );
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( GetParent(), "HudReplayReminderIn" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::Hide()
{
SetVisible( false );
m_flShowTime = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CReplayReminderPanel::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
{
if ( ShouldDraw() && pszCurrentBinding )
{
if ( FStrEq (pszCurrentBinding, "save_replay" ) )
{
SetVisible( false );
}
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::OnThink()
{
BaseClass::OnThink();
if ( !IsVisible() )
return;
// If we're displaying the element for some specific duration...
if ( m_flShowTime )
{
// Get maximum duration
ConVarRef replay_postwinreminderduration( "replay_postwinreminderduration" );
float flShowLength = replay_postwinreminderduration.IsValid() ? replay_postwinreminderduration.GetFloat() : 5.0f;
if ( gpGlobals->curtime >= m_flShowTime + flShowLength )
{
m_flShowTime = 0;
SetVisible( false );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CReplayReminderPanel::SetVisible( bool bState )
{
if ( bState )
{
SetupText();
}
// Store this state for ShouldDraw()
m_bShouldDraw = bState;
BaseClass::SetVisible( bState );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CReplayReminderPanel::ShouldDraw()
{
return m_bShouldDraw;
}
#endif // #if defined( REPLAY_ENABLED )
@@ -0,0 +1,50 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef REPLAYREMINDERPANEL_H
#define REPLAYREMINDERPANEL_H
#ifdef _WIN32
#pragma once
#endif
#include <vgui_controls/EditablePanel.h>
#include <game/client/iviewport.h>
#include <vgui/IScheme.h>
#include "hud.h"
#include "hudelement.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: Replay reminder panel
//-----------------------------------------------------------------------------
class CReplayReminderPanel : public EditablePanel, public CHudElement
{
DECLARE_CLASS_SIMPLE( CReplayReminderPanel, vgui::EditablePanel );
public:
CReplayReminderPanel( const char *pElementName );
void Hide(); // To be used by HUD only
void Show(); // To be used by HUD only
// CHudElement overrides
virtual bool ShouldDraw();
virtual void OnThink();
virtual int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
// EditablePanel overrides
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void SetVisible( bool bState );
private:
void SetupText();
float m_flShowTime; // Used by the HUD only, to display the panel only for a certain period of time
bool m_bShouldDraw; // Store this state for ShouldDraw(), which allows us to use a single panel for
// both the post-win reminder and the freezepanel reminder.
};
#endif // REPLAYREMINDERPANEL_H
@@ -0,0 +1,380 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cbase.h"
#if defined( REPLAY_ENABLED )
#include "replayrenderoverlay.h"
#include "vgui_controls/TextImage.h"
#include "replay/genericclassbased_replay.h"
#include "iclientmode.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "ienginevgui.h"
#include "vgui/IVGui.h"
#include "econ/confirm_dialog.h"
#include "replay/ireplaymanager.h"
#include "replay/irecordingsessionmanager.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/replayrenderer.h"
#include "econ/econ_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
extern IReplayMovieManager *g_pReplayMovieManager;
//-----------------------------------------------------------------------------
using namespace vgui;
//-----------------------------------------------------------------------------
#define TMP_ENCODED_AUDIO ".tmp.aac"
#ifdef USE_WEBM_FOR_REPLAY
#define TMP_ENCODED_VIDEO ".tmp.webm"
#else
#define TMP_ENCODED_VIDEO ".tmp.mov"
#endif
//-----------------------------------------------------------------------------
ConVar replay_enablerenderpreview( "replay_enablerenderpreview", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Enable preview during replay render." );
//-----------------------------------------------------------------------------
void OnRenderCancelDialogButtonPressed( bool bConfirm, void *pContext )
{
if ( bConfirm )
{
g_pReplayMovieManager->CancelRender();
}
}
//-----------------------------------------------------------------------------
CReplayRenderOverlay::CReplayRenderOverlay( Panel *pParent )
: BaseClass( pParent, "ReplayRenderOverlay" ),
m_pBottom( NULL ),
m_pCancelButton( NULL ),
m_pTitleLabel( NULL ),
m_pProgressLabel( NULL ),
m_pFilenameLabel( NULL ),
m_pRenderProgress( NULL ),
m_pRenderer( NULL ),
m_pPreviewCheckButton( NULL ),
m_unNumFrames( 0 ),
m_flStartTime( 0.0f ),
m_flPreviousTimeLeft( 0.0f )
{
if ( pParent == NULL )
{
vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
SetScheme(scheme);
SetProportional( true );
}
ivgui()->AddTickSignal( GetVPanel(), 10 );
m_pRenderer = new CReplayRenderer( this );
}
CReplayRenderOverlay::~CReplayRenderOverlay()
{
ivgui()->RemoveTickSignal( GetVPanel() );
delete m_pRenderer;
}
void CReplayRenderOverlay::Show()
{
// Setup panel
SetVisible( true );
SetMouseInputEnabled( true );
SetKeyBoardInputEnabled( true );
MakePopup( true );
MoveToFront();
TFModalStack()->PushModal( this );
// Make sure game UI is hidden
engine->ClientCmd_Unrestricted( "gameui_hide" );
InvalidateLayout( false, true );
}
void CReplayRenderOverlay::Hide()
{
SetVisible( false );
TFModalStack()->PopModal( this );
MarkForDeletion();
}
void CReplayRenderOverlay::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
// Load controls
LoadControlSettings( "Resource/UI/replayrenderoverlay.res", "GAME" );
// Layout bottom
m_pBottom = dynamic_cast< EditablePanel * >( FindChildByName( "BottomPanel" ) );
if ( !m_pBottom )
return;
// Find some controls
m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) );
m_pProgressLabel = dynamic_cast< CExLabel * >( FindChildByName( "ProgressLabel" ) );
m_pRenderProgress = dynamic_cast< ProgressBar * >( FindChildByName( "RenderProgress" ) );
m_pCancelButton = dynamic_cast< CExButton * >( FindChildByName( "CancelButton" ) );
m_pFilenameLabel = dynamic_cast< CExLabel * >( FindChildByName( "FilenameLabel" ) );
m_pPreviewCheckButton = dynamic_cast< CheckButton * >( FindChildByName( "PreviewCheckButton" ) );
m_pPreviewCheckButton->SetProportional( false );
m_pPreviewCheckButton->SetSelected( replay_enablerenderpreview.GetBool() );
m_pPreviewCheckButton->AddActionSignalTarget( this );
const char *pMovieFilename = m_pRenderer->GetMovieFilename();
if ( m_pFilenameLabel && pMovieFilename )
{
const char *pFilename = V_UnqualifiedFileName( pMovieFilename );
m_pFilenameLabel->SetText( pFilename );
}
}
void CReplayRenderOverlay::PerformLayout()
{
BaseClass::PerformLayout();
if ( !m_pBottom )
return;
int sw, sh;
vgui::surface()->GetScreenSize( sw, sh );
SetBounds( 0, 0, sw, sh );
int nBottomPanelHeight = sh * .13f;
int nBottomPanelStartY = sh - nBottomPanelHeight;
m_pBottom->SetBounds( 0, nBottomPanelStartY, sw, nBottomPanelHeight );
int nBottomW = sw;
int nBottomH = nBottomPanelHeight;
// Setup progress bar
if ( !m_pRenderProgress )
return;
int nProgHeight = YRES(20);
int nMargin = nBottomW/5;
int nProgX = nMargin;
int nProgY = nBottomPanelStartY + ( nBottomH - nProgHeight ) / 2;
int nProgW = nBottomW - 2*nMargin;
// Only show progress bar if replay is valid and length of render is non-zero, and the record start tick exists
CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
if ( pReplay )
{
const float flTotalTime = pReplay->m_flLength;
const int nServerRecordStartTick = g_pClientReplayContext->GetRecordingSessionManager()->GetServerStartTickForSession( pReplay->m_hSession ); // NOTE: Returns -1 on fail
if ( flTotalTime > 0.0f && nServerRecordStartTick >= 0 )
{
m_pRenderProgress->SetVisible( true );
m_pRenderProgress->SetBounds( nProgX, nProgY, nProgW, nProgHeight );
m_pRenderProgress->SetSegmentInfo( XRES(1), XRES(8) );
}
}
// Layout title label
const int nTitleLabelY = nBottomPanelStartY + ( m_pBottom->GetTall() - m_pTitleLabel->GetTall() ) / 2;
if ( m_pTitleLabel )
{
m_pTitleLabel->SizeToContents();
m_pTitleLabel->SetPos( ( nProgX - m_pTitleLabel->GetWide() ) / 2, nTitleLabelY );
}
// Layout preview check button
if ( m_pPreviewCheckButton )
{
m_pPreviewCheckButton->SizeToContents();
m_pPreviewCheckButton->SetPos( ( nProgX - m_pPreviewCheckButton->GetWide() ) / 2, nTitleLabelY + m_pTitleLabel->GetTall() + YRES(3) );
}
// Layout filename label
if ( m_pFilenameLabel )
{
int nProgBottomY = nProgY + nProgHeight;
m_pFilenameLabel->SizeToContents();
m_pFilenameLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pFilenameLabel->GetTall() ) / 2 );
}
// Layout progress label
if ( m_pProgressLabel )
{
int nProgBottomY = nProgY + nProgHeight;
m_pProgressLabel->SizeToContents();
m_pProgressLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pProgressLabel->GetTall() ) / 2 );
m_pProgressLabel->SetWide( nProgW );
}
// Layout cancel button
if ( !m_pCancelButton )
return;
// Put cancel button half way in between progress bar and screen right
int nProgRightX = nProgX + nProgW;
m_pCancelButton->SetPos(
nProgRightX + ( m_pBottom->GetWide() - nProgRightX - m_pCancelButton->GetWide() ) / 2,
nBottomPanelStartY + ( m_pBottom->GetTall() - m_pCancelButton->GetTall() ) / 2
);
SetXToRed( m_pCancelButton );
m_pCancelButton->RequestFocus();
}
void CReplayRenderOverlay::OnTick()
{
#if _DEBUG
if ( m_bReloadScheme )
{
InvalidateLayout( true, true );
m_bReloadScheme = false;
}
#endif
// Update progress
if ( m_pRenderProgress )
{
CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
if ( pReplay && m_pRenderProgress->IsVisible() )
{
float flCurTime, flTotalTime;
g_pClientReplayContext->GetPlaybackTimes( flCurTime, flTotalTime, pReplay, m_pRenderer->GetPerformance() );
const float flProgress = ( flTotalTime == 0.0f ) ? 1.0f : ( flCurTime / flTotalTime );
Assert( flTotalTime > 0.0f ); // NOTE: Progress bar will always be invisible if total time is 0, but check anyway to be safe.
m_pRenderProgress->SetProgress( MAX( m_pRenderProgress->GetProgress(), flProgress ) ); // The MAX() here keeps the progress bar from thrashing
if ( m_pProgressLabel )
{
// @note Tom Bui: this is a horribly ugly hack, but the first couple of frames take a really freaking long time, so that
// really blows out the estimate
float flTimePassed = 0.0f;
++m_unNumFrames;
const uint32 kNumFramesToWait = 10;
if ( m_unNumFrames < kNumFramesToWait )
{
m_flStartTime = gpGlobals->realtime;
}
else if ( m_unNumFrames > kNumFramesToWait )
{
flTimePassed = gpGlobals->realtime - m_flStartTime;
float flEstimatedTimeLeft = flProgress > 0.0f ? ( flTimePassed / flProgress ) - flTimePassed : 0.0f;
// exponential moving average FIR filter
// S(t) = smoothing_factor * Y(t) + (1 - smoothing_factor)* Y(t-1)
// previous value is essentially 90% of the current value
const float kSmoothingFactor = 0.1f;
if ( m_flPreviousTimeLeft == 0.0f )
{
m_flPreviousTimeLeft = flEstimatedTimeLeft;
}
else
{
m_flPreviousTimeLeft = kSmoothingFactor * flEstimatedTimeLeft + ( 1 - kSmoothingFactor ) * m_flPreviousTimeLeft;
}
}
wchar_t wszTimeLeft[256];
wchar_t wszTime[256];
{
const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( m_flPreviousTimeLeft ) );
g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTimeLeft, sizeof( wszTimeLeft ) );
}
{
const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( flTimePassed ) );
g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTime, sizeof( wszTime ) );
}
wchar_t wszText[256];
g_pVGuiLocalize->ConstructString_safe( wszText, g_pVGuiLocalize->Find( "#Replay_RenderOverlay_TimeLeft" ), 2, wszTime, wszTimeLeft );
m_pProgressLabel->SetText( wszText );
}
}
}
}
void CReplayRenderOverlay::OnMousePressed( MouseCode nCode )
{
#if _DEBUG
m_bReloadScheme = true;
#endif
BaseClass::OnMousePressed( nCode );
}
void CReplayRenderOverlay::OnKeyCodeTyped( vgui::KeyCode nCode )
{
if ( nCode == KEY_ESCAPE )
{
if ( TFModalStack()->Top() == GetVPanel() )
{
OnCommand( "confirmcancel" );
return;
}
}
BaseClass::OnKeyCodeTyped( nCode );
}
void CReplayRenderOverlay::OnCommand( const char *pCommand )
{
if ( !V_stricmp( pCommand, "confirmcancel" ) )
{
ShowConfirmDialog( "#Replay_CancelRenderTitle", "#Replay_ConfirmCancelRender", "#Replay_YesCancel", "#Replay_No", OnRenderCancelDialogButtonPressed, this, NULL, "replay\\replaydialog_warn.wav" );
return;
}
BaseClass::OnCommand( pCommand );
}
void CReplayRenderOverlay::OnCheckButtonChecked( Panel *pPanel )
{
replay_enablerenderpreview.SetValue( (int)m_pPreviewCheckButton->IsSelected() );
}
//-----------------------------------------------------------------------------
static CReplayRenderOverlay *s_pRenderOverlay = NULL;
void ReplayUI_OpenReplayRenderOverlay()
{
if ( !g_pReplayMovieManager->IsRendering() )
return;
// Delete any existing panel
if ( s_pRenderOverlay )
{
s_pRenderOverlay->MarkForDeletion();
}
// Create the panel - get the render resolution from the settings
s_pRenderOverlay = SETUP_PANEL( new CReplayRenderOverlay( NULL ) ); // Parenting to NULL allows us to turn off world rendering in engine/view.cpp (V_RenderView())
// Set the panel as the movie renderer, so it can receive begin/end render calls from the engine
g_pClientReplayContext->SetMovieRenderer( s_pRenderOverlay->m_pRenderer );
}
void ReplayUI_HideRenderOverlay()
{
if ( s_pRenderOverlay )
{
s_pRenderOverlay->MarkForDeletion();
s_pRenderOverlay = NULL;
}
g_pClientReplayContext->SetMovieRenderer( NULL );
}
//-----------------------------------------------------------------------------
#endif
@@ -0,0 +1,73 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#ifndef REPLAY_RENDEROVERLAY_H
#define REPLAY_RENDEROVERLAY_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
#include "vgui_controls/Frame.h"
#include "vgui_controls/ProgressBar.h"
#include "replay/rendermovieparams.h"
//-----------------------------------------------------------------------------
class CExButton;
class CExLabel;
class IQuickTimeMovieMaker;
class CReplay;
class CReplayRenderer;
//-----------------------------------------------------------------------------
class CReplayRenderOverlay : public vgui::Frame
{
DECLARE_CLASS_SIMPLE( CReplayRenderOverlay, vgui::Frame );
public:
CReplayRenderOverlay( Panel *pParent );
~CReplayRenderOverlay();
void Show();
void Hide();
CReplayRenderer *m_pRenderer;
private:
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void PerformLayout();
virtual void OnTick();
virtual void OnMousePressed( vgui::MouseCode nCode );
virtual void OnKeyCodeTyped( vgui::KeyCode nCode );
virtual void OnCommand( const char *pCommand );
private:
MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", pPanel );
#if _DEBUG
bool m_bReloadScheme;
#endif
int m_unNumFrames;
float m_flStartTime;
float m_flPreviousTimeLeft;
EditablePanel *m_pBottom;
vgui::ProgressBar *m_pRenderProgress;
vgui::CheckButton *m_pPreviewCheckButton;
CExButton *m_pCancelButton;
CExLabel *m_pTitleLabel;
CExLabel *m_pFilenameLabel;
CExLabel *m_pProgressLabel;
};
//-----------------------------------------------------------------------------
void ReplayUI_OpenReplayRenderOverlay();
void ReplayUI_HideRenderOverlay();
//-----------------------------------------------------------------------------
#endif // REPLAY_RENDEROVERLAY_H