Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2675 lines
74 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "replayperformanceeditor.h"
  9. #include "replay/replay.h"
  10. #include "replay/ireplayperformanceeditor.h"
  11. #include "replay/ireplayperformancecontroller.h"
  12. #include "replay/performance.h"
  13. #include "ienginevgui.h"
  14. #include "iclientmode.h"
  15. #include "vgui_controls/ImagePanel.h"
  16. #include "vgui_controls/TextImage.h"
  17. #include "vgui_controls/Slider.h"
  18. #include "vgui_controls/Menu.h"
  19. #include "vgui/ILocalize.h"
  20. #include "vgui/IImage.h"
  21. #include "c_team.h"
  22. #include "vgui_avatarimage.h"
  23. #include "vgui/ISurface.h"
  24. #include "vgui/IInput.h"
  25. #include "replay/replaycamera.h"
  26. #include "replay/ireplaymanager.h"
  27. #include "replay/iclientreplaycontext.h"
  28. #include "confirm_dialog.h"
  29. #include "replayperformancesavedlg.h"
  30. #include "replay/irecordingsessionmanager.h"
  31. #include "achievementmgr.h"
  32. #include "c_playerresource.h"
  33. #include "replay/gamedefs.h"
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include <tier0/memdbgon.h>
  36. extern CAchievementMgr g_AchievementMgrTF;
  37. //-----------------------------------------------------------------------------
  38. using namespace vgui;
  39. //-----------------------------------------------------------------------------
  40. extern IReplayPerformanceController *g_pReplayPerformanceController;
  41. //-----------------------------------------------------------------------------
  42. // Hack-y global bool to communicate when we are rewinding for map load screens.
  43. // Order of operations issues preclude the use of engine->IsPlayingDemo().
  44. bool g_bIsReplayRewinding = false;
  45. //-----------------------------------------------------------------------------
  46. // TODO: Make these archive? Right now, the tips are reset every time the game starts
  47. ConVar replay_perftip_count_enter( "replay_perftip_count_enter", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
  48. ConVar replay_perftip_count_exit( "replay_perftip_count_exit", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
  49. ConVar replay_perftip_count_freecam_enter( "replay_perftip_count_freecam_enter", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
  50. ConVar replay_perftip_count_freecam_exit( "replay_perftip_count_freecam_exit", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
  51. ConVar replay_perftip_count_freecam_exit2( "replay_perftip_count_freecam_exit2", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
  52. ConVar replay_editor_fov_mousewheel_multiplier( "replay_editor_fov_mousewheel_multiplier", "5", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "The multiplier on mousewheel input for adjusting camera FOV in the replay editor." );
  53. ConVar replay_editor_fov_mousewheel_invert( "replay_editor_fov_mousewheel_invert", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Invert FOV zoom/unzoom on mousewheel in the replay editor." );
  54. ConVar replay_replayeditor_rewindmsgcounter( "replay_replayeditor_rewindmsgcounter", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "" );
  55. //-----------------------------------------------------------------------------
  56. #define MAX_TIP_DISPLAYS 1
  57. //-----------------------------------------------------------------------------
  58. #define TIMESCALE_MIN 0.01f
  59. #define TIMESCALE_MAX 3.0f
  60. //-----------------------------------------------------------------------------
  61. #define SLIDER_RANGE_MAX 10000.0f
  62. //-----------------------------------------------------------------------------
  63. #define REPLAY_SOUND_DIALOG_POPUP "replay\\replaydialog_warn.wav"
  64. //-----------------------------------------------------------------------------
  65. static const char *gs_pCamNames[ NCAMS ] =
  66. {
  67. "free",
  68. "third",
  69. "first",
  70. "timescale",
  71. };
  72. static const char *gs_pBaseComponentNames[ NCAMS ] =
  73. {
  74. "replay/replay_camera_%s%s",
  75. "replay/replay_camera_%s%s",
  76. "replay/replay_camera_%s%s",
  77. "replay/replay_%s%s",
  78. };
  79. //-----------------------------------------------------------------------------
  80. void PlayDemo()
  81. {
  82. engine->ClientCmd_Unrestricted( "demo_resume" );
  83. }
  84. void PauseDemo()
  85. {
  86. engine->ClientCmd_Unrestricted( "demo_pause" );
  87. }
  88. //-----------------------------------------------------------------------------
  89. inline float SCurve( float t )
  90. {
  91. t = clamp( t, 0.0f, 1.0f );
  92. return t * t * (3 - 2*t);
  93. }
  94. inline float CubicEaseIn( float t )
  95. {
  96. t = clamp( t, 0.0f, 1.0f );
  97. return t * t * t;
  98. }
  99. inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax )
  100. {
  101. float flDenom = flInMax - flInMin;
  102. if ( flDenom == 0.0f )
  103. return 0.0f;
  104. float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f );
  105. return Lerp( t, flOutMin, flOutMax );
  106. }
  107. //-----------------------------------------------------------------------------
  108. void HighlightTipWords( Label *pLabel )
  109. {
  110. // Setup coloring - get # of words that should be highlighted
  111. wchar_t *pwNumWords = g_pVGuiLocalize->Find( "#Replay_PerfTip_Highlight_NumWords" );
  112. if ( !pwNumWords )
  113. return;
  114. // Get the current label text
  115. wchar_t wszLabelText[512];
  116. pLabel->GetText( wszLabelText, sizeof( wszLabelText ) );
  117. pLabel->GetTextImage()->ClearColorChangeStream();
  118. pLabel->GetTextImage()->AddColorChange( pLabel->GetFgColor(), 0 );
  119. int nNumWords = _wtoi( pwNumWords );
  120. for ( int i = 0; i < nNumWords; ++i )
  121. {
  122. char szWordFindStr[64];
  123. V_snprintf( szWordFindStr, sizeof( szWordFindStr ), "#Replay_PerfTip_Highlight_Word%i", i );
  124. wchar_t *pwWord = g_pVGuiLocalize->Find( szWordFindStr );
  125. if ( !pwWord )
  126. continue;
  127. const int nWordLen = wcslen( pwWord );
  128. // Find any instance of the word in the label text and highlight it in red
  129. const wchar_t *p = wszLabelText;
  130. do
  131. {
  132. const wchar_t *pInst = wcsstr( p, pwWord );
  133. if ( !pInst )
  134. break;
  135. // Highlight the text
  136. int nStartPos = pInst - wszLabelText;
  137. int nEndPos = nStartPos + nWordLen;
  138. // If start pos is non-zero, clear color changes
  139. bool bChangeColor = true;
  140. if ( nStartPos == 0 )
  141. {
  142. pLabel->GetTextImage()->ClearColorChangeStream();
  143. }
  144. else if ( iswalpha( wszLabelText[ nStartPos - 1 ] ) )
  145. {
  146. // If this is not the beginning of the string, check the previous character. If it's
  147. // not whitespace, etc, we found an instance of a keyword within another word. Skip.
  148. bChangeColor = false;
  149. }
  150. if ( bChangeColor )
  151. {
  152. pLabel->GetTextImage()->AddColorChange( Color(200,80,60,255), nStartPos );
  153. pLabel->GetTextImage()->AddColorChange( pLabel->GetFgColor(), nEndPos );
  154. }
  155. p = pInst + nWordLen;
  156. } while ( 1 );
  157. }
  158. }
  159. //-----------------------------------------------------------------------------
  160. class CSavingDialog : public CGenericWaitingDialog
  161. {
  162. DECLARE_CLASS_SIMPLE( CSavingDialog, CGenericWaitingDialog );
  163. public:
  164. CSavingDialog( CReplayPerformanceEditorPanel *pEditorPanel )
  165. : CGenericWaitingDialog( pEditorPanel )
  166. {
  167. m_pEditorPanel = pEditorPanel;
  168. }
  169. virtual void OnTick()
  170. {
  171. BaseClass::OnTick();
  172. if ( !g_pReplayPerformanceController )
  173. return;
  174. // Update async save
  175. if ( g_pReplayPerformanceController->IsSaving() )
  176. {
  177. g_pReplayPerformanceController->SaveThink();
  178. }
  179. else
  180. {
  181. if ( m_pEditorPanel.Get() )
  182. {
  183. m_pEditorPanel->OnSaveComplete();
  184. }
  185. Close();
  186. }
  187. }
  188. private:
  189. CConfirmDialog *m_pLoginDialog;
  190. vgui::DHANDLE< CReplayPerformanceEditorPanel > m_pEditorPanel;
  191. };
  192. //-----------------------------------------------------------------------------
  193. class CReplayTipLabel : public Label
  194. {
  195. DECLARE_CLASS_SIMPLE( CReplayTipLabel, Label );
  196. public:
  197. CReplayTipLabel( Panel *pParent, const char *pName, const char *pText )
  198. : BaseClass( pParent, pName, pText )
  199. {
  200. }
  201. virtual void ApplySchemeSettings( IScheme *pScheme )
  202. {
  203. BaseClass::ApplySchemeSettings( pScheme );
  204. HighlightTipWords( this );
  205. }
  206. };
  207. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayTipLabel, Label );
  208. //-----------------------------------------------------------------------------
  209. class CPerformanceTip : public EditablePanel
  210. {
  211. DECLARE_CLASS_SIMPLE( CPerformanceTip, EditablePanel );
  212. public:
  213. static DHANDLE< CPerformanceTip > s_pTip;
  214. static CPerformanceTip *CreateInstance( const char *pText )
  215. {
  216. if ( s_pTip )
  217. {
  218. s_pTip->SetVisible( false );
  219. s_pTip->MarkForDeletion();
  220. s_pTip = NULL;
  221. }
  222. s_pTip = SETUP_PANEL( new CPerformanceTip( pText ) );
  223. return s_pTip;
  224. }
  225. CPerformanceTip( const char *pText )
  226. : BaseClass( g_pClientMode->GetViewport(), "Tip" ),
  227. m_flBornTime( gpGlobals->realtime ),
  228. m_flAge( 0.0f ),
  229. m_flShowDuration( 15.0f )
  230. {
  231. m_pTextLabel = new CReplayTipLabel( this, "TextLabel", pText );
  232. }
  233. virtual void OnThink()
  234. {
  235. // Delete the panel if life exceeded
  236. const float flEndTime = m_flBornTime + m_flShowDuration;
  237. if ( gpGlobals->realtime >= flEndTime )
  238. {
  239. SetVisible( false );
  240. MarkForDeletion();
  241. s_pTip = NULL;
  242. return;
  243. }
  244. SetVisible( true );
  245. const float flFadeDuration = .4f;
  246. float flAlpha;
  247. // Fade out?
  248. if ( gpGlobals->realtime >= flEndTime - flFadeDuration )
  249. {
  250. flAlpha = LerpScale( gpGlobals->realtime, flEndTime - flFadeDuration, flEndTime, 1.0f, 0.0f );
  251. }
  252. // Fade in?
  253. else if ( gpGlobals->realtime <= m_flBornTime + flFadeDuration )
  254. {
  255. flAlpha = LerpScale( gpGlobals->realtime, m_flBornTime, m_flBornTime + flFadeDuration, 0.0f, 1.0f );
  256. }
  257. // Otherwise, we must be in between fade in/fade out
  258. else
  259. {
  260. flAlpha = 1.0f;
  261. }
  262. SetAlpha( 255 * SCurve( flAlpha ) );
  263. }
  264. virtual void ApplySchemeSettings( IScheme *pScheme )
  265. {
  266. BaseClass::ApplySchemeSettings( pScheme );
  267. LoadControlSettings( "resource/ui/replayperformanceeditor/tip.res", "GAME" );
  268. // Center relative to parent
  269. const int nScreenW = ScreenWidth();
  270. const int nScreenH = ScreenHeight();
  271. int aContentSize[2];
  272. m_pTextLabel->GetContentSize( aContentSize[0], aContentSize[1] );
  273. const int nLabelHeight = aContentSize[1];
  274. SetBounds(
  275. 0,
  276. 3 * nScreenH / 4 - nLabelHeight / 2,
  277. nScreenW,
  278. nLabelHeight + 2 * m_nTopBottomMargin
  279. );
  280. m_pTextLabel->SetBounds(
  281. m_nLeftRightMarginWidth,
  282. m_nTopBottomMargin,
  283. nScreenW - 2 * m_nLeftRightMarginWidth,
  284. nLabelHeight
  285. );
  286. }
  287. static void Cleanup()
  288. {
  289. if ( s_pTip )
  290. {
  291. s_pTip->MarkForDeletion();
  292. s_pTip = NULL;
  293. }
  294. }
  295. CPanelAnimationVarAliasType( int, m_nLeftRightMarginWidth, "left_right_margin", "0", "proportional_xpos" );
  296. CPanelAnimationVarAliasType( int, m_nTopBottomMargin , "top_bottom_margin", "0", "proportional_ypos" );
  297. CReplayTipLabel *m_pTextLabel;
  298. float m_flBornTime;
  299. float m_flAge;
  300. float m_flShowDuration;
  301. };
  302. DHANDLE< CPerformanceTip > CPerformanceTip::s_pTip;
  303. // Display the performance tip if we haven't already displayed it nMaxTimesToDisplay times or more
  304. inline void DisplayPerformanceTip( const char *pText, ConVar* pCountCv = NULL, int nMaxTimesToDisplay = -1 )
  305. {
  306. // Already displayed too many times? Get out.
  307. if ( pCountCv && nMaxTimesToDisplay >= 0 )
  308. {
  309. int nCount = pCountCv->GetInt();
  310. if ( nCount >= nMaxTimesToDisplay )
  311. return;
  312. // Incremement count cvar
  313. pCountCv->SetValue( nCount + 1 );
  314. }
  315. // Display the tip
  316. CPerformanceTip::CreateInstance( pText );
  317. }
  318. //-----------------------------------------------------------------------------
  319. inline float GetPlaybackTime()
  320. {
  321. CReplay *pPlayingReplay = g_pReplayManager->GetPlayingReplay();
  322. return gpGlobals->curtime - TICKS_TO_TIME( pPlayingReplay->m_nSpawnTick );
  323. }
  324. //-----------------------------------------------------------------------------
  325. class CPlayerCell : public CExImageButton
  326. {
  327. DECLARE_CLASS_SIMPLE( CPlayerCell, CExImageButton );
  328. public:
  329. CPlayerCell( Panel *pParent, const char *pName, int *pCurTargetPlayerIndex )
  330. : CExImageButton( pParent, pName, "" ),
  331. m_iPlayerIndex( -1 ),
  332. m_pCurTargetPlayerIndex( pCurTargetPlayerIndex )
  333. {
  334. }
  335. virtual void ApplySchemeSettings( IScheme *pScheme )
  336. {
  337. BaseClass::ApplySchemeSettings( pScheme );
  338. GetImage()->SetImage( "" );
  339. SetFont( pScheme->GetFont( "ReplaySmall" ) );
  340. SetContentAlignment( Label::a_center );
  341. }
  342. MESSAGE_FUNC( DoClick, "PressButton" )
  343. {
  344. ReplayCamera()->SetPrimaryTarget( m_iPlayerIndex );
  345. *m_pCurTargetPlayerIndex = m_iPlayerIndex;
  346. float flCurTime = GetPlaybackTime();
  347. extern IReplayPerformanceController *g_pReplayPerformanceController;
  348. g_pReplayPerformanceController->AddEvent_Camera_ChangePlayer( flCurTime, m_iPlayerIndex );
  349. }
  350. int m_iPlayerIndex;
  351. int *m_pCurTargetPlayerIndex; // Allow the button to write current target in outer class when pressed
  352. };
  353. //-----------------------------------------------------------------------------
  354. /*
  355. class CReplayEditorSlider : public Slider
  356. {
  357. DECLARE_CLASS_SIMPLE( CReplayEditorSlider, Slider );
  358. public:
  359. CReplayEditorSlider( Panel *pParent, const char *pName )
  360. : Slider( pParent, pName )
  361. {
  362. }
  363. virtual void SetDefault( float flDefault ) { m_flDefault = flDefault; }
  364. ON_MESSAGE( Reset, OnReset )
  365. {
  366. SetValue(
  367. }
  368. private:
  369. float m_flDefault;
  370. };
  371. */
  372. //-----------------------------------------------------------------------------
  373. class CCameraOptionsPanel : public EditablePanel
  374. {
  375. DECLARE_CLASS_SIMPLE( CCameraOptionsPanel, EditablePanel );
  376. public:
  377. CCameraOptionsPanel( Panel *pParent, const char *pName, const char *pTitle )
  378. : EditablePanel( pParent, pName ),
  379. m_bControlsAdded( false )
  380. {
  381. m_pTitleLabel = new CExLabel( this, "TitleLabel", pTitle );
  382. AddControlToLayout( m_pTitleLabel );
  383. }
  384. ~CCameraOptionsPanel()
  385. {
  386. m_lstSliderInfos.PurgeAndDeleteElements();
  387. }
  388. void AddControlToLayout( Panel *pControl )
  389. {
  390. if ( pControl )
  391. {
  392. m_lstControls.AddToTail( pControl );
  393. pControl->SetMouseInputEnabled( true );
  394. }
  395. }
  396. // NOTE: Default value is assumed to be stored in flOut
  397. void AddSliderToLayout( int nId, Slider *pSlider, const char *pLabelText,
  398. float flMinValue, float flMaxValue, float &flOut )
  399. {
  400. SliderInfo_t *pNewSliderInfo = new SliderInfo_t;
  401. pNewSliderInfo->m_nId = nId;
  402. pNewSliderInfo->m_pSlider = pSlider;
  403. pNewSliderInfo->m_flRange[ 0 ] = flMinValue;
  404. pNewSliderInfo->m_flRange[ 1 ] = flMaxValue;
  405. pNewSliderInfo->m_flDefault = flOut;
  406. pNewSliderInfo->m_pValueOut = &flOut;
  407. m_lstSliderInfos.AddToTail( pNewSliderInfo );
  408. AddControlToLayout( new EditablePanel( this, "Buffer" ) );
  409. AddControlToLayout( NewLabel( pLabelText ) );
  410. AddControlToLayout( NewSetDefaultButton( nId ) );
  411. AddControlToLayout( pSlider );
  412. pSlider->AddActionSignalTarget( this );
  413. }
  414. void ResetSlider( int nId )
  415. {
  416. const SliderInfo_t *pSliderInfo = FindSliderInfoFromId( nId );
  417. if ( !pSliderInfo )
  418. return;
  419. SetValue( pSliderInfo, pSliderInfo->m_flDefault );
  420. }
  421. void SetValue( int nId, float flValue )
  422. {
  423. const SliderInfo_t *pSliderInfo = FindSliderInfoFromId( nId );
  424. if ( !pSliderInfo )
  425. return;
  426. SetValue( pSliderInfo, flValue );
  427. }
  428. virtual void ApplySchemeSettings( IScheme *pScheme )
  429. {
  430. BaseClass::ApplySchemeSettings( pScheme );
  431. // Setup border
  432. SetBorder( pScheme->GetBorder( "ButtonBorder" ) );
  433. HFont hFont = pScheme->GetFont( "ReplayBrowserSmallest", true );
  434. m_pTitleLabel->SetFont( hFont );
  435. m_pTitleLabel->SizeToContents();
  436. m_pTitleLabel->SetTall( YRES( 20 ) );
  437. m_pTitleLabel->SetColorStr( "235 235 235 255" );
  438. if ( !m_bControlsAdded )
  439. {
  440. const char *pResFile = GetResFile();
  441. if ( pResFile )
  442. {
  443. LoadControlSettings( pResFile, "GAME" );
  444. }
  445. AddControls();
  446. m_bControlsAdded = true;
  447. }
  448. FOR_EACH_LL( m_lstSliderInfos, it )
  449. {
  450. SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
  451. Slider *pSlider = pInfo->m_pSlider;
  452. pSlider->SetRange( 0, SLIDER_RANGE_MAX );
  453. pSlider->SetNumTicks( 10 );
  454. float flDenom = fabs( pInfo->m_flRange[1] - pInfo->m_flRange[0] );
  455. pSlider->SetValue( SLIDER_RANGE_MAX * fabs( pInfo->m_flDefault - pInfo->m_flRange[0] ) / flDenom );
  456. }
  457. }
  458. virtual void PerformLayout()
  459. {
  460. BaseClass::PerformLayout();
  461. int nWidth = XRES( 140 );
  462. int nMargins[2] = { (int)XRES( 5 ), (int)YRES( 5 ) };
  463. int nVBuf = YRES( 0 );
  464. int nLastY = -1;
  465. int nY = nMargins[1];
  466. Panel *pPrevPanel = NULL;
  467. int nLastCtrlHeight = 0;
  468. FOR_EACH_LL( m_lstControls, i )
  469. {
  470. Panel *pPanel = m_lstControls[ i ];
  471. if ( !pPanel->IsVisible() )
  472. continue;
  473. int aPos[2];
  474. pPanel->GetPos( aPos[0], aPos[1] );
  475. if ( pPrevPanel && aPos[1] >= 0 )
  476. {
  477. nY += pPrevPanel->GetTall() + nVBuf;
  478. }
  479. // Gross hack to see if the control is a default button
  480. if ( dynamic_cast< CExButton * >( pPanel ) )
  481. {
  482. pPanel->SetWide( XRES( 36 ) );
  483. pPanel->SetPos( pPrevPanel ? ( GetWide() - nMargins[0] - pPanel->GetWide() ) : 0, nLastY );
  484. }
  485. else
  486. {
  487. pPanel->SetWide( nWidth - 2 * nMargins[0] );
  488. pPanel->SetPos( nMargins[0], nY );
  489. }
  490. nLastY = nY;
  491. pPrevPanel = pPanel;
  492. nLastCtrlHeight = MAX( nLastCtrlHeight, pPanel->GetTall() );
  493. }
  494. SetSize( nWidth, nY + nLastCtrlHeight + 2 * YRES( 3 ) );
  495. }
  496. virtual void OnCommand( const char *pCommand )
  497. {
  498. if ( !V_strnicmp( pCommand, "reset_", 6 ) )
  499. {
  500. const int nSliderInfoId = atoi( pCommand + 6 );
  501. ResetSlider( nSliderInfoId );
  502. }
  503. else
  504. {
  505. BaseClass::OnCommand( pCommand );
  506. }
  507. }
  508. Label *NewLabel( const char *pText )
  509. {
  510. Label *pLabel = new Label( this, "Label", pText );
  511. pLabel->SetTall( YRES( 9 ) );
  512. pLabel->SetPos( -1, 0 ); // Use default x and accumulated y
  513. // Set font
  514. IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  515. HFont hFont = pScheme->GetFont( "DefaultVerySmall", true );
  516. pLabel->SetFont( hFont );
  517. return pLabel;
  518. }
  519. CExButton *NewSetDefaultButton( int nSliderInfoId )
  520. {
  521. CExButton *pButton = new CExButton( this, "DefaultButton", "#Replay_SetDefaultSetting" );
  522. pButton->SetTall( YRES( 11 ) );
  523. pButton->SetPos( XRES( 30 ), -1 ); // Set y to -1 so it will stay on the same line
  524. pButton->SetContentAlignment( Label::a_center );
  525. CFmtStr fmtResetCommand( "reset_%i", nSliderInfoId );
  526. pButton->SetCommand( fmtResetCommand.Access() );
  527. pButton->AddActionSignalTarget( this );
  528. // Set font
  529. IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  530. HFont hFont = pScheme->GetFont( "DefaultVerySmall", true );
  531. pButton->SetFont( hFont );
  532. return pButton;
  533. }
  534. protected:
  535. MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pParams )
  536. {
  537. Panel *pSlider = (Panel *)pParams->GetPtr( "panel" );
  538. float flPercent = pParams->GetInt( "position" ) / SLIDER_RANGE_MAX;
  539. FOR_EACH_LL( m_lstSliderInfos, it )
  540. {
  541. SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
  542. if ( pSlider == pInfo->m_pSlider )
  543. {
  544. *pInfo->m_pValueOut = Lerp( flPercent, pInfo->m_flRange[0], pInfo->m_flRange[1] );
  545. }
  546. }
  547. }
  548. virtual const char *GetResFile() { return NULL; }
  549. virtual void AddControls()
  550. {
  551. }
  552. struct SliderInfo_t
  553. {
  554. Slider *m_pSlider;
  555. float m_flRange[2];
  556. float m_flDefault;
  557. int m_nId;
  558. float *m_pValueOut;
  559. };
  560. const SliderInfo_t *FindSliderInfoFromId( int nId )
  561. {
  562. FOR_EACH_LL( m_lstSliderInfos, it )
  563. {
  564. SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
  565. if ( pInfo->m_nId == nId )
  566. return pInfo;
  567. }
  568. AssertMsg( 0, "Should always find a slider here." );
  569. return NULL;
  570. }
  571. void SetValue( const SliderInfo_t *pSliderInfo, float flValue )
  572. {
  573. if ( !pSliderInfo )
  574. {
  575. AssertMsg( 0, "This should not happen." );
  576. return;
  577. }
  578. // Calculate the range
  579. const float flRange = fabs( pSliderInfo->m_flRange[1] - pSliderInfo->m_flRange[0] );
  580. AssertMsg( flRange > 0, "Bad slider range!" );
  581. // Calculate the percentile based on the specified value and the range.
  582. const float flPercent = fabs( flValue - pSliderInfo->m_flRange[0] ) / flRange;
  583. pSliderInfo->m_pSlider->SetValue( flPercent * SLIDER_RANGE_MAX, true );
  584. }
  585. CUtlLinkedList< Panel * > m_lstControls;
  586. CUtlLinkedList< SliderInfo_t *, int > m_lstSliderInfos;
  587. CExLabel *m_pTitleLabel;
  588. bool m_bControlsAdded;
  589. };
  590. //-----------------------------------------------------------------------------
  591. class CTimeScaleOptionsPanel : public CCameraOptionsPanel
  592. {
  593. DECLARE_CLASS_SIMPLE( CTimeScaleOptionsPanel, CCameraOptionsPanel );
  594. public:
  595. CTimeScaleOptionsPanel( Panel *pParent, float *pTimeScaleProxy )
  596. : BaseClass( pParent, "TimeScaleSettings", "#Replay_TimeScale" ),
  597. m_pTimeScaleSlider( NULL ),
  598. m_pTimeScaleProxy( pTimeScaleProxy )
  599. {
  600. }
  601. virtual const char *GetResFile()
  602. {
  603. return "resource/ui/replayperformanceeditor/settings_timescale.res";
  604. }
  605. virtual void AddControls()
  606. {
  607. m_pTimeScaleSlider = dynamic_cast< Slider * >( FindChildByName( "TimeScaleSlider" ) );
  608. AddSliderToLayout( SLIDER_TIMESCALE, m_pTimeScaleSlider, "#Replay_Scale", TIMESCALE_MIN, TIMESCALE_MAX, *m_pTimeScaleProxy );
  609. }
  610. enum FreeCamSliders_t
  611. {
  612. SLIDER_TIMESCALE,
  613. };
  614. Slider *m_pTimeScaleSlider;
  615. float *m_pTimeScaleProxy;
  616. };
  617. //-----------------------------------------------------------------------------
  618. class CCameraOptionsPanel_Free : public CCameraOptionsPanel
  619. {
  620. DECLARE_CLASS_SIMPLE( CCameraOptionsPanel_Free, CCameraOptionsPanel );
  621. public:
  622. CCameraOptionsPanel_Free( Panel *pParent )
  623. : BaseClass( pParent, "FreeCameraSettings", "#Replay_FreeCam" ),
  624. m_pAccelSlider( NULL ),
  625. m_pSpeedSlider( NULL ),
  626. m_pFovSlider( NULL ),
  627. m_pRotFilterSlider( NULL ),
  628. m_pShakeSpeedSlider( NULL ),
  629. m_pShakeAmountSlider( NULL )
  630. {
  631. }
  632. virtual const char *GetResFile()
  633. {
  634. return "resource/ui/replayperformanceeditor/camsettings_free.res";
  635. }
  636. virtual void AddControls()
  637. {
  638. m_pAccelSlider = dynamic_cast< Slider * >( FindChildByName( "AccelSlider" ) );
  639. m_pSpeedSlider = dynamic_cast< Slider * >( FindChildByName( "SpeedSlider" ) );
  640. m_pFovSlider = dynamic_cast< Slider * >( FindChildByName( "FovSlider" ) );
  641. m_pRotFilterSlider = dynamic_cast< Slider * >( FindChildByName( "RotFilterSlider" ) );
  642. m_pShakeSpeedSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeSpeedSlider" ) );
  643. m_pShakeAmountSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeAmountSlider" ) );
  644. m_pShakeDirSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeDirSlider" ) );
  645. AddSliderToLayout( SLIDER_ACCEL, m_pAccelSlider, "#Replay_Accel", FREE_CAM_ACCEL_MIN, FREE_CAM_ACCEL_MAX, ReplayCamera()->m_flRoamingAccel );
  646. AddSliderToLayout( SLIDER_SPEED, m_pSpeedSlider, "#Replay_Speed", FREE_CAM_SPEED_MIN, FREE_CAM_SPEED_MAX, ReplayCamera()->m_flRoamingSpeed );
  647. AddSliderToLayout( SLIDER_FOV, m_pFovSlider, "#Replay_Fov", FREE_CAM_FOV_MIN, FREE_CAM_FOV_MAX, ReplayCamera()->m_flRoamingFov[1] );
  648. AddSliderToLayout( SLIDER_ROTFILTER, m_pRotFilterSlider, "#Replay_RotFilter", FREE_CAM_ROT_FILTER_MIN, FREE_CAM_ROT_FILTER_MAX, ReplayCamera()->m_flRoamingRotFilterFactor );
  649. AddSliderToLayout( SLIDER_SHAKE_SPEED, m_pShakeSpeedSlider, "#Replay_ShakeSpeed", FREE_CAM_SHAKE_SPEED_MIN, FREE_CAM_SHAKE_SPEED_MAX, ReplayCamera()->m_flRoamingShakeSpeed );
  650. AddSliderToLayout( SLIDER_SHAKE_AMOUNT, m_pShakeAmountSlider, "#Replay_ShakeAmount", FREE_CAM_SHAKE_AMOUNT_MIN, FREE_CAM_SHAKE_AMOUNT_MAX, ReplayCamera()->m_flRoamingShakeAmount );
  651. AddSliderToLayout( SLIDER_SHAKE_DIR, m_pShakeDirSlider, "#Replay_ShakeDir", FREE_CAM_SHAKE_DIR_MIN, FREE_CAM_SHAKE_DIR_MAX, ReplayCamera()->m_flRoamingShakeDir );
  652. }
  653. enum FreeCamSliders_t
  654. {
  655. SLIDER_ACCEL,
  656. SLIDER_SPEED,
  657. SLIDER_FOV,
  658. SLIDER_ROTFILTER,
  659. SLIDER_SHAKE_SPEED,
  660. SLIDER_SHAKE_AMOUNT,
  661. SLIDER_SHAKE_DIR,
  662. };
  663. Slider *m_pAccelSlider;
  664. Slider *m_pSpeedSlider;
  665. Slider *m_pFovSlider;
  666. Slider *m_pRotFilterSlider;
  667. Slider *m_pShakeSpeedSlider;
  668. Slider *m_pShakeAmountSlider;
  669. Slider *m_pShakeDirSlider;
  670. };
  671. //-----------------------------------------------------------------------------
  672. class CReplayButton : public CExImageButton
  673. {
  674. DECLARE_CLASS_SIMPLE( CReplayButton, CExImageButton );
  675. public:
  676. CReplayButton( Panel *pParent, const char *pName, const char *pText )
  677. : BaseClass( pParent, pName, pText ),
  678. m_pTipText( NULL )
  679. {
  680. }
  681. virtual void ApplySettings( KeyValues *pInResourceData )
  682. {
  683. BaseClass::ApplySettings( pInResourceData );
  684. const char *pTipName = pInResourceData->GetString( "tipname" );
  685. if ( pTipName && pTipName[0] )
  686. {
  687. const wchar_t *pTipText = g_pVGuiLocalize->Find( pTipName );
  688. if ( pTipText && pTipText[0] )
  689. {
  690. const int nTipLength = V_wcslen( pTipText );
  691. m_pTipText = new wchar_t[ nTipLength + 1 ];
  692. V_wcsncpy( m_pTipText, pTipText, sizeof(wchar_t) * ( nTipLength + 1 ) );
  693. m_pTipText[ nTipLength ] = L'\0';
  694. }
  695. }
  696. }
  697. virtual void OnCursorEntered()
  698. {
  699. BaseClass::OnCursorEntered();
  700. CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
  701. if ( pEditor && m_pTipText )
  702. {
  703. pEditor->SetButtonTip( m_pTipText, this );
  704. pEditor->ShowButtonTip( true );
  705. }
  706. }
  707. virtual void OnCursorExited()
  708. {
  709. BaseClass::OnCursorExited();
  710. CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
  711. if ( pEditor && m_pTipText )
  712. {
  713. pEditor->ShowButtonTip( false );
  714. }
  715. }
  716. private:
  717. wchar_t *m_pTipText;
  718. };
  719. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayButton, CExImageButton );
  720. //-----------------------------------------------------------------------------
  721. #define MAX_FF_RAMP_TIME 8.0f // The amount of time until we ramp to max scale value.
  722. class CReplayEditorFastForwardButton : public CReplayButton
  723. {
  724. DECLARE_CLASS_SIMPLE( CReplayEditorFastForwardButton, CReplayButton );
  725. public:
  726. CReplayEditorFastForwardButton( Panel *pParent, const char *pName, const char *pText )
  727. : BaseClass( pParent, pName, pText ),
  728. m_flPressTime( 0.0f )
  729. {
  730. m_pHostTimescale = cvar->FindVar( "host_timescale" );
  731. AssertMsg( m_pHostTimescale, "host_timescale lookup failed!" );
  732. ivgui()->AddTickSignal( GetVPanel(), 10 );
  733. }
  734. ~CReplayEditorFastForwardButton()
  735. {
  736. ivgui()->RemoveTickSignal( GetVPanel() );
  737. // Avoid a non-1.0 host_timescale after replay edit, which can happen if
  738. // the user is still holding downt he FF button at the end of the replay.
  739. if ( m_pHostTimescale )
  740. {
  741. m_pHostTimescale->SetValue( 1.0f );
  742. }
  743. // Resume demo playback so that any demo played later won't start paused.
  744. PlayDemo();
  745. }
  746. virtual void OnMousePressed( MouseCode code )
  747. {
  748. m_flPressTime = gpGlobals->realtime;
  749. PlayDemo();
  750. BaseClass::OnMousePressed( code );
  751. }
  752. virtual void OnMouseReleased( MouseCode code )
  753. {
  754. m_flPressTime = 0.0f;
  755. PauseDemo();
  756. BaseClass::OnMouseReleased( code );
  757. }
  758. void OnTick()
  759. {
  760. float flScale;
  761. if ( m_flPressTime == 0.0f )
  762. {
  763. flScale = 1.0f;
  764. }
  765. else
  766. {
  767. const float flElapsed = clamp( gpGlobals->realtime - m_flPressTime, 0.0f, MAX_FF_RAMP_TIME );
  768. const float t = CubicEaseIn( flElapsed / MAX_FF_RAMP_TIME );
  769. // If a shift key is down...
  770. if ( input()->IsKeyDown( KEY_LSHIFT ) || input()->IsKeyDown( KEY_RSHIFT ) )
  771. {
  772. // ...slow down host_timescale.
  773. flScale = .1f + .4f * t;
  774. }
  775. // If alt key down...
  776. else if ( input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ) )
  777. {
  778. // ...FF very quickly, ramp from 5 to 10.
  779. flScale = 5.0f + 5.0f * t;
  780. }
  781. else
  782. {
  783. // Otherwise, start at 1.5 and ramp upwards over time.
  784. flScale = 1.5f + 3.5f * t;
  785. }
  786. }
  787. // Set host_timescale.
  788. if ( m_pHostTimescale )
  789. {
  790. m_pHostTimescale->SetValue( flScale );
  791. }
  792. }
  793. private:
  794. float m_flPressTime;
  795. ConVar *m_pHostTimescale;
  796. };
  797. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayEditorFastForwardButton, CExImageButton );
  798. //-----------------------------------------------------------------------------
  799. class CRecLightPanel : public EditablePanel
  800. {
  801. DECLARE_CLASS_SIMPLE( CRecLightPanel, vgui::EditablePanel );
  802. public:
  803. CRecLightPanel( Panel *pParent )
  804. : EditablePanel( pParent, "RecLightPanel" ),
  805. m_flPlayPauseTime( 0.0f ),
  806. m_bPaused( false ),
  807. m_bPerforming( false )
  808. {
  809. m_pRecLights[ 0 ] = NULL;
  810. m_pRecLights[ 1 ] = NULL;
  811. m_pPlayPause[ 0 ] = NULL;
  812. m_pPlayPause[ 1 ] = NULL;
  813. }
  814. virtual void ApplySchemeSettings( IScheme *pScheme )
  815. {
  816. BaseClass::ApplySchemeSettings( pScheme );
  817. LoadControlSettings( "resource/ui/replayperformanceeditor/reclight.res", "GAME" );
  818. m_pRecLights[ 0 ] = dynamic_cast< ImagePanel * >( FindChildByName( "RecLightOffImg" ) );
  819. m_pRecLights[ 1 ] = dynamic_cast< ImagePanel * >( FindChildByName( "RecLightOnImg" ) );
  820. m_pPlayPause[ 0 ] = dynamic_cast< ImagePanel * >( FindChildByName( "PlayImg" ) );
  821. m_pPlayPause[ 1 ] = dynamic_cast< ImagePanel * >( FindChildByName( "PauseImg" ) );
  822. m_pCameraFringe = dynamic_cast< ImagePanel *>( FindChildByName( "CameraFringe" ) );
  823. m_pCameraCrosshair = dynamic_cast< ImagePanel *>( FindChildByName( "CameraCrosshair" ) );
  824. }
  825. virtual void PerformLayout()
  826. {
  827. BaseClass::PerformLayout();
  828. SetVisible( m_bPerforming );
  829. const int nScreenWidth = ScreenWidth();
  830. const int nRecLightW = m_pRecLights[ 0 ]->GetWide();
  831. int nXPos = nScreenWidth - nRecLightW + XRES( 6 );
  832. int nYPos = -YRES( 8 );
  833. m_pRecLights[ 0 ]->SetPos( nXPos, nYPos );
  834. m_pRecLights[ 1 ]->SetPos( nXPos, nYPos );
  835. const int nWidth = GetWide();
  836. const int nHeight = GetTall();
  837. // Setup camera fringe height
  838. if ( m_pCameraFringe )
  839. {
  840. m_pCameraFringe->SetSize( nWidth, nHeight );
  841. m_pCameraFringe->InstallMouseHandler( this );
  842. }
  843. // Setup camera cross hair height
  844. if ( m_pCameraCrosshair )
  845. {
  846. int aImageSize[2];
  847. IImage *pImage = m_pCameraCrosshair->GetImage();
  848. pImage->GetSize( aImageSize[0], aImageSize[1] );
  849. aImageSize[0] = m_pCameraCrosshair->GetWide();
  850. aImageSize[1] = m_pCameraCrosshair->GetTall();
  851. const int nStartY = YRES( 13 );
  852. m_pCameraCrosshair->SetBounds(
  853. nStartY + ( nWidth - aImageSize[0] ) / 2,
  854. nStartY + ( nHeight - aImageSize[1] ) / 2,
  855. aImageSize[0] - 2 * nStartY,
  856. aImageSize[1] - 2 * nStartY
  857. );
  858. m_pCameraCrosshair->InstallMouseHandler( this );
  859. }
  860. }
  861. void UpdateBackgroundVisibility()
  862. {
  863. m_pCameraCrosshair->SetVisible( m_bPaused );
  864. m_pCameraFringe->SetVisible( m_bPaused );
  865. }
  866. virtual void OnThink()
  867. {
  868. const float flTime = gpGlobals->realtime;
  869. bool bPauseAnimating = m_flPlayPauseTime > 0.0f &&
  870. flTime >= m_flPlayPauseTime &&
  871. flTime < ( m_flPlayPauseTime + m_flAnimTime );
  872. // Setup light visibility
  873. int nOnOff = fmod( flTime * 2.0f, 2.0f );
  874. bool bOnLightVisible = (bool)nOnOff;
  875. bool bRecording = g_pReplayPerformanceController->IsRecording();
  876. m_pRecLights[ 0 ]->SetVisible( m_bPaused || ( bRecording && !bOnLightVisible ) );
  877. m_pRecLights[ 1 ]->SetVisible( bRecording && ( !m_bPaused && bOnLightVisible ) );
  878. // Deal with fringe and crosshair vis
  879. UpdateBackgroundVisibility();
  880. int iPlayPauseActive = (int)m_bPaused;
  881. // Animate the pause icon
  882. if ( bPauseAnimating )
  883. {
  884. const float t = clamp( ( flTime - m_flPlayPauseTime ) / m_flAnimTime, 0.0f, 1.0f );
  885. const float s = SCurve( t );
  886. const int nSize = (int)Lerp( s, 60.0f, 60.0f * m_nAnimScale );
  887. int aCrossHairPos[2];
  888. m_pCameraCrosshair->GetPos( aCrossHairPos[0], aCrossHairPos[1] );
  889. const int nScreenXCenter = aCrossHairPos[0] + m_pCameraCrosshair->GetWide() / 2;
  890. const int nScreenYCenter = aCrossHairPos[1] + m_pCameraCrosshair->GetTall() / 2;
  891. m_pPlayPause[ iPlayPauseActive ]->SetBounds(
  892. nScreenXCenter - nSize / 2,
  893. nScreenYCenter - nSize / 2,
  894. nSize,
  895. nSize
  896. );
  897. m_pPlayPause[ iPlayPauseActive ]->SetAlpha( (int)( MIN( 0.5f, 1.0f - s ) * 255) );
  898. }
  899. m_pPlayPause[ iPlayPauseActive ]->SetVisible( bPauseAnimating );
  900. m_pPlayPause[ !iPlayPauseActive ]->SetVisible( false );
  901. }
  902. void UpdatePauseState( bool bPaused )
  903. {
  904. if ( bPaused == m_bPaused )
  905. return;
  906. m_bPaused = bPaused;
  907. m_flPlayPauseTime = gpGlobals->realtime;
  908. }
  909. void SetPerforming( bool bPerforming )
  910. {
  911. if ( bPerforming == m_bPerforming )
  912. return;
  913. m_bPerforming = bPerforming;
  914. InvalidateLayout( true, false );
  915. }
  916. float m_flPlayPauseTime;
  917. bool m_bPaused;
  918. bool m_bPerforming;
  919. ImagePanel *m_pPlayPause[2]; // 0=play, 1=pause
  920. ImagePanel *m_pRecLights[2]; // 0=off, 1=on
  921. ImagePanel *m_pCameraFringe;
  922. ImagePanel *m_pCameraCrosshair;
  923. CPanelAnimationVar( int, m_nAnimScale, "anim_scale", "4" );
  924. CPanelAnimationVar( float, m_flAnimTime, "anim_time", "1.5" );
  925. };
  926. //-----------------------------------------------------------------------------
  927. CReplayPerformanceEditorPanel::CReplayPerformanceEditorPanel( Panel *parent, ReplayHandle_t hReplay )
  928. : EditablePanel( parent, "ReplayPerformanceEditor" ),
  929. m_hReplay( hReplay ),
  930. m_flLastTime( -1 ),
  931. m_nRedBlueLabelRightX( 0 ),
  932. m_nBottomPanelStartY( 0 ),
  933. m_nBottomPanelHeight( 0 ),
  934. m_nLastRoundedTime( -1 ),
  935. m_flSpaceDownStart( 0.0f ),
  936. m_flOldFps( -1.0f ),
  937. m_flLastTimeSpaceBarPressed( 0.0f ),
  938. m_flActiveTimeInEditor( 0.0f ),
  939. m_flTimeScaleProxy( 1.0f ),
  940. m_iCameraSelection( CAM_FIRST ),
  941. m_bMousePressed( false ),
  942. m_bMouseDown( false ),
  943. m_nMouseClickedOverCameraSettingsPanel( CAM_INVALID ),
  944. m_bShownAtLeastOnce( false ),
  945. m_bAchievementAwarded( false ),
  946. m_pImageList( NULL ),
  947. m_pCurTimeLabel( NULL ),
  948. m_pTotalTimeLabel( NULL ),
  949. m_pPlayerNameLabel( NULL ),
  950. m_pMouseTargetPanel( NULL ),
  951. m_pSlowMoButton( NULL ),
  952. m_pRecLightPanel( NULL ),
  953. m_pPlayerCellData( NULL ),
  954. m_pBottom( NULL ),
  955. m_pMenuButton( NULL ),
  956. m_pMenu( NULL ),
  957. m_pPlayerCellsPanel( NULL ),
  958. m_pButtonTip( NULL ),
  959. m_pSavingDlg( NULL )
  960. {
  961. V_memset( m_pCameraButtons, 0, sizeof( m_pCameraButtons ) );
  962. V_memset( m_pCtrlButtons, 0, sizeof( m_pCtrlButtons ) );
  963. V_memset( m_pCameraOptionsPanels, NULL, sizeof( m_pCameraOptionsPanels ) );
  964. m_pCameraOptionsPanels[ CAM_FREE ] = new CCameraOptionsPanel_Free( this );
  965. m_pCameraOptionsPanels[ COMPONENT_TIMESCALE ] = new CTimeScaleOptionsPanel( this, &m_flTimeScaleProxy );
  966. m_nRedBlueSigns[0] = -1;
  967. m_nRedBlueSigns[1] = 1;
  968. m_iCurPlayerTarget = -1;
  969. m_bCurrentTargetNeedsVisibilityUpdate = false;
  970. m_pImageList = new ImageList( false );
  971. SetParent( g_pClientMode->GetViewport() );
  972. HScheme hScheme = scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
  973. SetScheme( hScheme );
  974. ivgui()->AddTickSignal( GetVPanel(), 16 ); // Roughly 60hz
  975. MakePopup( true );
  976. SetMouseInputEnabled( true );
  977. // Create bottom
  978. m_pBottom = new EditablePanel( this, "BottomPanel" );
  979. // Add player cells
  980. m_pPlayerCellsPanel = new EditablePanel( m_pBottom, "PlayerCellsPanel" );
  981. for ( int i = 0; i < 2; ++i )
  982. {
  983. for ( int j = 0; j <= MAX_PLAYERS; ++j )
  984. {
  985. m_pPlayerCells[i][j] = new CPlayerCell( m_pPlayerCellsPanel, "PlayerCell", &m_iCurPlayerTarget );
  986. m_pPlayerCells[i][j]->SetVisible( false );
  987. AddPanelKeyboardInputDisableList( m_pPlayerCells[i][j] );
  988. }
  989. }
  990. // Create rec light panel
  991. m_pRecLightPanel = SETUP_PANEL( new CRecLightPanel( g_pClientMode->GetViewport() ) );
  992. // Display "enter performance mode" tip
  993. DisplayPerformanceTip( "#Replay_PerfTip_EnterPerfMode", &replay_perftip_count_enter, MAX_TIP_DISPLAYS );
  994. // Create menu
  995. m_pMenu = new Menu( this, "Menu" );
  996. m_aMenuItemIds[ MENU_SAVE ] = m_pMenu->AddMenuItem( "#Replay_Save", "menu_save", this );
  997. m_aMenuItemIds[ MENU_SAVEAS ] = m_pMenu->AddMenuItem( "#Replay_SaveAs", "menu_saveas", this );
  998. m_pMenu->AddSeparator();
  999. m_aMenuItemIds[ MENU_EXIT ] = m_pMenu->AddMenuItem( "#Replay_Exit", "menu_exit", this );
  1000. m_pMenu->EnableUseMenuManager( false ); // The menu manager doesn't play nice with the menu button
  1001. }
  1002. CReplayPerformanceEditorPanel::~CReplayPerformanceEditorPanel()
  1003. {
  1004. m_pRecLightPanel->MarkForDeletion();
  1005. m_pRecLightPanel = NULL;
  1006. m_pButtonTip->MarkForDeletion();
  1007. m_pButtonTip = NULL;
  1008. g_bIsReplayRewinding = false;
  1009. surface()->PlaySound( "replay\\performanceeditorclosed.wav" );
  1010. CPerformanceTip::Cleanup();
  1011. ClearPlayerCellData();
  1012. }
  1013. void CReplayPerformanceEditorPanel::ClearPlayerCellData()
  1014. {
  1015. if ( m_pPlayerCellData )
  1016. {
  1017. m_pPlayerCellData->deleteThis();
  1018. m_pPlayerCellData = NULL;
  1019. }
  1020. }
  1021. void CReplayPerformanceEditorPanel::AddPanelKeyboardInputDisableList( Panel *pPanel )
  1022. {
  1023. m_lstDisableKeyboardInputPanels.AddToTail( pPanel );
  1024. }
  1025. void CReplayPerformanceEditorPanel::ApplySchemeSettings( IScheme *pScheme )
  1026. {
  1027. BaseClass::ApplySchemeSettings( pScheme );
  1028. LoadControlSettings( "resource/ui/replayperformanceeditor/main.res", "GAME" );
  1029. m_lstDisableKeyboardInputPanels.RemoveAll();
  1030. int nParentWidth = GetParent()->GetWide();
  1031. int nParentHeight = GetParent()->GetTall();
  1032. // Set size of this panel
  1033. SetSize( nParentWidth, nParentHeight );
  1034. // Layout bottom
  1035. if ( m_pBottom )
  1036. {
  1037. m_nBottomPanelHeight = m_pBottom->GetTall(); // Get from .res
  1038. m_nBottomPanelStartY = nParentHeight - m_nBottomPanelHeight;
  1039. m_pBottom->SetBounds( 0, m_nBottomPanelStartY, nParentWidth, m_nBottomPanelHeight );
  1040. }
  1041. // Layout rec light panel - don't overlap bottom panel
  1042. m_pRecLightPanel->SetBounds( 0, 0, ScreenWidth(), m_nBottomPanelStartY );
  1043. // Setup camera buttons
  1044. const int nNumCameraButtons = NCAMS;
  1045. const char *pCameraButtonNames[nNumCameraButtons] = { "CameraFree", "CameraThird", "CameraFirst", "TimeScaleButton" };
  1046. int nCurButtonX = nParentWidth - m_nRightMarginWidth;
  1047. int nLeftmostCameraButtonX = 0;
  1048. for ( int i = 0; i < nNumCameraButtons; ++i )
  1049. {
  1050. m_pCameraButtons[i] = dynamic_cast< CExImageButton * >( FindChildByName( pCameraButtonNames[ i ] ) );
  1051. if ( m_pCameraButtons[i] )
  1052. {
  1053. CExImageButton *pCurButton = m_pCameraButtons[ i ];
  1054. if ( !pCurButton )
  1055. continue;
  1056. nCurButtonX -= pCurButton->GetWide();
  1057. int nX, nY;
  1058. pCurButton->GetPos( nX, nY );
  1059. pCurButton->SetPos( nCurButtonX, nY );
  1060. pCurButton->SetParent( m_pBottom );
  1061. pCurButton->AddActionSignalTarget( this );
  1062. #if !defined( TF_CLIENT_DLL )
  1063. pCurButton->SetPaintBorderEnabled( false );
  1064. #endif
  1065. AddPanelKeyboardInputDisableList( pCurButton );
  1066. }
  1067. }
  1068. nLeftmostCameraButtonX = nCurButtonX;
  1069. static const char *s_pControlButtonNames[NUM_CTRLBUTTONS] = {
  1070. "InButton", "GotoBeginningButton", "RewindButton",
  1071. "PlayButton",
  1072. "FastForwardButton", "GotoEndButton", "OutButton"
  1073. };
  1074. for ( int i = 0; i < NUM_CTRLBUTTONS; ++i )
  1075. {
  1076. CExImageButton *pCurButton = dynamic_cast< CExImageButton * >( FindChildByName( s_pControlButtonNames[ i ] ) ); Assert( pCurButton );
  1077. if ( !pCurButton )
  1078. continue;
  1079. pCurButton->SetParent( m_pBottom );
  1080. pCurButton->AddActionSignalTarget( this );
  1081. AddPanelKeyboardInputDisableList( pCurButton );
  1082. #if !defined( TF_CLIENT_DLL )
  1083. pCurButton->SetPaintBorderEnabled( false );
  1084. #endif
  1085. m_pCtrlButtons[ i ] = pCurButton;
  1086. }
  1087. // If the performance in tick is set, highlight the in point button
  1088. {
  1089. CReplayPerformance *pSavedPerformance = GetSavedPerformance();
  1090. m_pCtrlButtons[ CTRLBUTTON_IN ]->SetSelected( pSavedPerformance && pSavedPerformance->HasInTick() );
  1091. m_pCtrlButtons[ CTRLBUTTON_OUT ]->SetSelected( pSavedPerformance && pSavedPerformance->HasOutTick() );
  1092. }
  1093. // Select first-person camera by default.
  1094. UpdateCameraSelectionPosition( CAM_FIRST );
  1095. // Position time label
  1096. m_pCurTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "CurTimeLabel" ) );
  1097. m_pTotalTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "TotalTimeLabel" ) );
  1098. m_pCurTimeLabel->SetParent( m_pBottom );
  1099. m_pTotalTimeLabel->SetParent( m_pBottom );
  1100. // Get player name label
  1101. m_pPlayerNameLabel = dynamic_cast< CExLabel * >( FindChildByName( "PlayerNameLabel" ) );
  1102. // Get mouse target panel
  1103. m_pMouseTargetPanel = dynamic_cast< EditablePanel * >( FindChildByName( "MouseTargetPanel" ) );
  1104. for ( int i = 0; i < 2; ++i )
  1105. {
  1106. for ( int j = 0; j <= MAX_PLAYERS; ++j )
  1107. {
  1108. m_pPlayerCells[i][j]->SetMouseInputEnabled( true );
  1109. }
  1110. }
  1111. // Get menu button
  1112. m_pMenuButton = dynamic_cast< CExImageButton * >( FindChildByName( "MenuButton" ) );
  1113. AddPanelKeyboardInputDisableList( m_pMenuButton );
  1114. m_pMenuButton->SetMouseInputEnabled( true );
  1115. #if !defined( TF_CLIENT_DLL )
  1116. m_pMenuButton->SetPaintBorderEnabled( false );
  1117. #endif
  1118. // Get button tip
  1119. m_pButtonTip = dynamic_cast< CReplayTipLabel * >( FindChildByName( "ButtonTip" ) );
  1120. m_pButtonTip->SetParent( g_pClientMode->GetViewport() );
  1121. }
  1122. static void Replay_GotoTick( bool bConfirmed, void *pContext )
  1123. {
  1124. if ( bConfirmed )
  1125. {
  1126. int nGotoTick = (int)pContext;
  1127. CFmtStr fmtCmd( "demo_gototick %i\ndemo_pause\n", nGotoTick );
  1128. engine->ClientCmd_Unrestricted( fmtCmd.Access() );
  1129. }
  1130. }
  1131. void CReplayPerformanceEditorPanel::OnSliderMoved( KeyValues *pParams )
  1132. {
  1133. }
  1134. void CReplayPerformanceEditorPanel::OnInGameMouseWheelEvent( int nDelta )
  1135. {
  1136. HandleMouseWheel( nDelta );
  1137. }
  1138. void CReplayPerformanceEditorPanel::HandleMouseWheel( int nDelta )
  1139. {
  1140. if ( ReplayCamera()->GetMode() == OBS_MODE_ROAMING )
  1141. {
  1142. // Invert mousewheel input if necessary
  1143. if ( replay_editor_fov_mousewheel_invert.GetBool() )
  1144. {
  1145. nDelta *= -1;
  1146. }
  1147. float &flFov = ReplayCamera()->m_flRoamingFov[1];
  1148. flFov = clamp( flFov - nDelta * replay_editor_fov_mousewheel_multiplier.GetFloat(), FREE_CAM_FOV_MIN, FREE_CAM_FOV_MAX );
  1149. // Update FOV slider in free camera settings
  1150. CCameraOptionsPanel_Free *pFreeCamOptions = static_cast< CCameraOptionsPanel_Free * >( m_pCameraOptionsPanels[ CAM_FREE ] );
  1151. pFreeCamOptions->m_pFovSlider->SetValue( flFov - FREE_CAM_FOV_MIN, false );
  1152. }
  1153. }
  1154. void CReplayPerformanceEditorPanel::ApplySettings( KeyValues *pInResourceData )
  1155. {
  1156. BaseClass::ApplySettings( pInResourceData );
  1157. ClearPlayerCellData();
  1158. KeyValues *pPlayerCellData = pInResourceData->FindKey( "PlayerCell" );
  1159. if ( pPlayerCellData )
  1160. {
  1161. m_pPlayerCellData = new KeyValues( "PlayerCell" );
  1162. pPlayerCellData->CopySubkeys( m_pPlayerCellData );
  1163. }
  1164. }
  1165. CameraMode_t CReplayPerformanceEditorPanel::IsMouseOverActiveCameraOptionsPanel( int nMouseX, int nMouseY )
  1166. {
  1167. // In one of the camera options panels?
  1168. for ( int i = 0; i < NCAMS; ++i )
  1169. {
  1170. CCameraOptionsPanel *pCurPanel = m_pCameraOptionsPanels[ i ];
  1171. if ( pCurPanel && pCurPanel->IsVisible() && pCurPanel->IsWithin( nMouseX, nMouseY ) )
  1172. return (CameraMode_t)i;
  1173. }
  1174. return CAM_INVALID;
  1175. }
  1176. void CReplayPerformanceEditorPanel::OnMouseWheeled( int nDelta )
  1177. {
  1178. HandleMouseWheel( nDelta );
  1179. }
  1180. void CReplayPerformanceEditorPanel::OnTick()
  1181. {
  1182. BaseClass::OnTick();
  1183. // engine->Con_NPrintf( 0, "timescale: %f", g_pReplayPerformanceController->GetPlaybackTimeScale() );
  1184. C_ReplayCamera *pCamera = ReplayCamera();
  1185. if ( !pCamera )
  1186. return;
  1187. // Calc elapsed time
  1188. float flElapsed = gpGlobals->realtime - m_flLastTime;
  1189. m_flLastTime = gpGlobals->realtime;
  1190. // If this is the first time we're running and camera is valid, get primary target
  1191. if ( m_iCurPlayerTarget < 0 )
  1192. {
  1193. m_iCurPlayerTarget = pCamera->GetPrimaryTargetIndex();
  1194. }
  1195. // NOTE: Third-person is not "controllable" yet
  1196. int nCameraMode = pCamera->GetMode();
  1197. bool bInAControllableCameraMode = nCameraMode == OBS_MODE_ROAMING || nCameraMode == OBS_MODE_CHASE;
  1198. // Get mouse cursor pos
  1199. int nMouseX, nMouseY;
  1200. input()->GetCursorPos( nMouseX, nMouseY );
  1201. // Toggle in and out of camera control if appropriate
  1202. // Mouse pressed?
  1203. bool bMouseDown = input()->IsMouseDown( MOUSE_LEFT );
  1204. m_bMousePressed = bMouseDown && !m_bMouseDown;
  1205. m_bMouseDown = bMouseDown;
  1206. // Reset this flag if mouse is no longer down
  1207. if ( !m_bMouseDown )
  1208. {
  1209. m_nMouseClickedOverCameraSettingsPanel = CAM_INVALID;
  1210. }
  1211. bool bNoDialogsUp = TFModalStack()->IsEmpty();
  1212. bool bMouseCursorOverPerfEditor = nMouseY >= m_nBottomPanelStartY;
  1213. bool bMouseOverMenuButton = m_pMenuButton->IsWithin( nMouseX, nMouseY );
  1214. bool bMouseOverMenu = m_pMenu->IsWithin( nMouseX, nMouseY );
  1215. bool bRecording = g_pReplayPerformanceController->IsRecording();
  1216. if ( IsVisible() && m_bMousePressed )
  1217. {
  1218. CameraMode_t nActiveOptionsPanel = IsMouseOverActiveCameraOptionsPanel( nMouseX, nMouseY );
  1219. if ( nActiveOptionsPanel != CAM_INVALID )
  1220. {
  1221. m_nMouseClickedOverCameraSettingsPanel = nActiveOptionsPanel;
  1222. }
  1223. else if ( m_pMenu->IsVisible() && !m_pMenu->IsWithin( nMouseX, nMouseY ) )
  1224. {
  1225. ToggleMenu();
  1226. }
  1227. else if ( bInAControllableCameraMode && !bMouseCursorOverPerfEditor && !bMouseOverMenuButton &&
  1228. !bMouseOverMenu && bNoDialogsUp )
  1229. {
  1230. if ( bRecording )
  1231. {
  1232. bool bMouseInputEnabled = IsMouseInputEnabled();
  1233. // Already in a controllable camera mode?
  1234. if ( bMouseInputEnabled )
  1235. {
  1236. DisplayPerformanceTip( "#Replay_PerfTip_ExitFreeCam", &replay_perftip_count_freecam_exit, MAX_TIP_DISPLAYS );
  1237. surface()->PlaySound( "replay\\cameracontrolmodeentered.wav" );
  1238. }
  1239. else
  1240. {
  1241. DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam", &replay_perftip_count_freecam_enter, MAX_TIP_DISPLAYS );
  1242. surface()->PlaySound( "replay\\cameracontrolmodeexited.wav" );
  1243. }
  1244. SetMouseInputEnabled( !bMouseInputEnabled );
  1245. }
  1246. else
  1247. {
  1248. // Play an error sound
  1249. surface()->PlaySound( "replay\\cameracontrolerror.wav" );
  1250. }
  1251. }
  1252. }
  1253. // Show panel if space key bar is down
  1254. bool bSpaceDown = bNoDialogsUp && !enginevgui->IsGameUIVisible() && input()->IsKeyDown( KEY_SPACE );
  1255. m_bSpacePressed = bSpaceDown && !m_bSpaceDown;
  1256. m_bSpaceDown = bSpaceDown;
  1257. // Modify visibility?
  1258. bool bShow = IsVisible();
  1259. if ( m_bSpacePressed )
  1260. {
  1261. bShow = !IsVisible();
  1262. }
  1263. // Set visibility?
  1264. if ( IsVisible() != bShow )
  1265. {
  1266. ShowPanel( bShow );
  1267. m_bShownAtLeastOnce = true;
  1268. // For achievements:
  1269. Achievements_OnSpaceBarPressed();
  1270. }
  1271. // Factor in host_timescale.
  1272. float flScaledElapsed = flElapsed;
  1273. ConVarRef host_timescale( "host_timescale" );
  1274. if ( host_timescale.GetFloat() > 0 )
  1275. {
  1276. flScaledElapsed *= host_timescale.GetFloat();
  1277. }
  1278. // Do FOV smoothing
  1279. ReplayCamera()->SmoothFov( flScaledElapsed );
  1280. // Don't do any more processing if not needed
  1281. if ( !m_bShownAtLeastOnce )
  1282. return;
  1283. // Update time text if necessary
  1284. UpdateTimeLabels();
  1285. // Make all player cells invisible
  1286. int nTeamCounts[2] = {0,0};
  1287. int nCurTeam = 0;
  1288. for ( int i = 0; i < 2; ++i )
  1289. for ( int j = 0; j <= MAX_PLAYERS; ++j )
  1290. {
  1291. m_pPlayerCells[i][j]->SetVisible( false );
  1292. }
  1293. int iMouseOverPlayerIndex = -1;
  1294. CPlayerCell *pMouseOverCell = NULL;
  1295. // Update player cells
  1296. bool bLayoutPlayerCells = true; // TODO: only layout when necessary
  1297. C_ReplayGame_PlayerResource_t *pGamePlayerResource = dynamic_cast< C_ReplayGame_PlayerResource_t * >( g_PR );
  1298. for ( int iPlayer = 1; iPlayer <= MAX_PLAYERS; ++iPlayer )
  1299. {
  1300. IGameResources *pGR = GameResources();
  1301. if ( !pGR || !pGR->IsConnected( iPlayer ) )
  1302. continue;
  1303. // Which team?
  1304. int iTeam = pGR->GetTeam( iPlayer );
  1305. switch ( iTeam )
  1306. {
  1307. case REPLAY_TEAM_TEAM0:
  1308. ++nTeamCounts[0];
  1309. nCurTeam = 0;
  1310. break;
  1311. case REPLAY_TEAM_TEAM1:
  1312. ++nTeamCounts[1];
  1313. nCurTeam = 1;
  1314. break;
  1315. default:
  1316. nCurTeam = -1;
  1317. break;
  1318. }
  1319. if ( nCurTeam < 0 )
  1320. continue;
  1321. #if !defined( CSTRIKE_DLL )
  1322. int iPlayerClass = pGamePlayerResource->GetPlayerClass( iPlayer );
  1323. if ( iPlayerClass == REPLAY_CLASS_UNDEFINED )
  1324. continue;
  1325. #endif
  1326. int nCurTeamCount = nTeamCounts[ nCurTeam ];
  1327. CPlayerCell* pCell = m_pPlayerCells[ nCurTeam ][ nCurTeamCount-1 ];
  1328. // Cache the player index
  1329. pCell->m_iPlayerIndex = iPlayer;
  1330. // Make visible
  1331. pCell->SetVisible( true );
  1332. // Show leaderboard icon
  1333. #if defined( TF_CLIENT_DLL )
  1334. char szClassImg[64];
  1335. extern const char *g_aPlayerClassNames_NonLocalized[ REPLAY_NUM_CLASSES ];
  1336. char const *pClassName = iPlayerClass == TF_CLASS_DEMOMAN
  1337. ? "demo"
  1338. : g_aPlayerClassNames_NonLocalized[ iPlayerClass ];
  1339. V_snprintf( szClassImg, sizeof( szClassImg ), "../HUD/leaderboard_class_%s", pClassName );
  1340. // Show dead icon instead?
  1341. if ( !pGamePlayerResource->IsAlive( iPlayer ) )
  1342. {
  1343. V_strcat( szClassImg, "_d", sizeof( szClassImg ) );
  1344. }
  1345. IImage *pImage = scheme()->GetImage( szClassImg, true );
  1346. if ( pImage )
  1347. {
  1348. pImage->SetSize( 32, 32 );
  1349. pCell->GetImage()->SetImage( pImage );
  1350. }
  1351. #elif defined( CSTRIKE_DLL )
  1352. // TODO - create and use class icons
  1353. char szText[16];
  1354. V_snprintf( szText, sizeof( szText ), "%i", nTeamCounts[ nCurTeam ] );
  1355. pCell->SetText( szText );
  1356. #endif
  1357. // Display player name if mouse is over the current cell
  1358. if ( pCell->IsWithin( nMouseX, nMouseY ) )
  1359. {
  1360. iMouseOverPlayerIndex = iPlayer;
  1361. pMouseOverCell = pCell;
  1362. }
  1363. }
  1364. // Check to see if we're hovering over a camera-mode, and if so, display its options panel if it has one
  1365. if ( bRecording )
  1366. {
  1367. for ( int i = 0; i < NCAMS; ++i )
  1368. {
  1369. CCameraOptionsPanel *pCurOptionsPanel = m_pCameraOptionsPanels[ i ];
  1370. if ( !pCurOptionsPanel )
  1371. continue;
  1372. bool bMouseOverButton = m_pCameraButtons[ i ]->IsWithin( nMouseX, nMouseY );
  1373. bool bMouseOverOptionsPanel = pCurOptionsPanel->IsWithin( nMouseX, nMouseY );
  1374. bool bInCameraModeThatMouseIsOver = ReplayCamera()->GetMode() == GetCameraModeFromButtonIndex( (CameraMode_t)i );
  1375. bool bDontCareAboutCameraMode = i == COMPONENT_TIMESCALE;
  1376. bool bActivate = ( i == m_nMouseClickedOverCameraSettingsPanel ) ||
  1377. ( ( ( bInCameraModeThatMouseIsOver || bDontCareAboutCameraMode ) && bMouseOverButton ) || ( bMouseOverOptionsPanel && pCurOptionsPanel->IsVisible() ) );
  1378. pCurOptionsPanel->SetVisible( bActivate );
  1379. }
  1380. }
  1381. if ( bLayoutPlayerCells )
  1382. {
  1383. LayoutPlayerCells();
  1384. }
  1385. // Setup player name label and temporary camera view
  1386. if ( m_pPlayerNameLabel && pGamePlayerResource && pMouseOverCell )
  1387. {
  1388. m_pPlayerNameLabel->SetText( pGamePlayerResource->GetPlayerName( iMouseOverPlayerIndex ) );
  1389. m_pPlayerNameLabel->SizeToContents();
  1390. int nCellPos[2];
  1391. pMouseOverCell->GetPos( nCellPos[0], nCellPos[1] );
  1392. int nLabelX = MAX(
  1393. nCellPos[0],
  1394. m_nRedBlueLabelRightX
  1395. );
  1396. int nLabelY = m_nBottomPanelStartY + ( m_nBottomPanelHeight - m_pPlayerNameLabel->GetTall() ) / 2;
  1397. m_pPlayerNameLabel->SetPos( nLabelX, nLabelY );
  1398. m_pPlayerNameLabel->SetVisible( true );
  1399. // Setup camera
  1400. pCamera->SetPrimaryTarget( iMouseOverPlayerIndex );
  1401. }
  1402. else
  1403. {
  1404. m_pPlayerNameLabel->SetVisible( false );
  1405. // Set camera to last valid target
  1406. Assert( m_iCurPlayerTarget >= 0 );
  1407. pCamera->SetPrimaryTarget( m_iCurPlayerTarget );
  1408. }
  1409. // If user clicked, assume it was the selected cell and set primary target in camera
  1410. if ( iMouseOverPlayerIndex >= 0 )
  1411. {
  1412. pCamera->SetPrimaryTarget( iMouseOverPlayerIndex );
  1413. }
  1414. else
  1415. {
  1416. pCamera->SetPrimaryTarget( m_iCurPlayerTarget );
  1417. }
  1418. // fixes a case where the replay would be paused and the player would cycle cameras but the
  1419. // target's visibility wouldn't be updated until the replay was unpaused (they would be invisible)
  1420. if ( m_bCurrentTargetNeedsVisibilityUpdate )
  1421. {
  1422. C_BaseEntity *pTarget = ClientEntityList().GetEnt( pCamera->GetPrimaryTargetIndex() );
  1423. if ( pTarget )
  1424. {
  1425. pTarget->UpdateVisibility();
  1426. }
  1427. m_bCurrentTargetNeedsVisibilityUpdate = false;
  1428. }
  1429. // If in free-cam mode, add set view event if we're not paused
  1430. if ( bInAControllableCameraMode && m_bShownAtLeastOnce && bRecording )
  1431. {
  1432. AddSetViewEvent();
  1433. AddTimeScaleEvent( m_flTimeScaleProxy );
  1434. }
  1435. // Set paused state in rec light
  1436. const bool bPaused = IsPaused();
  1437. m_pRecLightPanel->UpdatePauseState( bPaused );
  1438. Achievements_Think( flElapsed );
  1439. }
  1440. void CReplayPerformanceEditorPanel::Achievements_OnSpaceBarPressed()
  1441. {
  1442. m_flLastTimeSpaceBarPressed = gpGlobals->realtime;
  1443. }
  1444. void CReplayPerformanceEditorPanel::Achievements_Think( float flElapsed )
  1445. {
  1446. // engine->Con_NPrintf( 10, "total time: %f", m_flActiveTimeInEditor );
  1447. // engine->Con_NPrintf( 11, "last time space bar pressed: %f", m_flLastTimeSpaceBarPressed );
  1448. // Already awarded one this editing session?
  1449. if ( m_bAchievementAwarded )
  1450. return;
  1451. // Too much idle time since last activity?
  1452. if ( gpGlobals->realtime - m_flLastTimeSpaceBarPressed > 60.0f )
  1453. {
  1454. m_flActiveTimeInEditor = 0.0f;
  1455. return;
  1456. }
  1457. // Accumulate active time
  1458. m_flActiveTimeInEditor += flElapsed;
  1459. // Award now if three-minutes of non-idle time has passed
  1460. const float flMinutes = 60.0f * 3.0f;
  1461. if ( m_flActiveTimeInEditor < flMinutes )
  1462. return;
  1463. Achievements_Grant();
  1464. }
  1465. void CReplayPerformanceEditorPanel::Achievements_Grant()
  1466. {
  1467. #if defined( TF_CLIENT_DLL )
  1468. g_AchievementMgrTF.AwardAchievement( ACHIEVEMENT_TF_REPLAY_EDIT_TIME );
  1469. #endif
  1470. // Awarded
  1471. m_bAchievementAwarded = true;
  1472. }
  1473. bool CReplayPerformanceEditorPanel::IsPaused()
  1474. {
  1475. return IsVisible();
  1476. }
  1477. CReplayPerformance *CReplayPerformanceEditorPanel::GetPerformance() const
  1478. {
  1479. return g_pReplayPerformanceController->GetPerformance();
  1480. }
  1481. CReplayPerformance *CReplayPerformanceEditorPanel::GetSavedPerformance() const
  1482. {
  1483. return g_pReplayPerformanceController->GetSavedPerformance();
  1484. }
  1485. int CReplayPerformanceEditorPanel::GetCameraModeFromButtonIndex( CameraMode_t iCamera )
  1486. {
  1487. switch ( iCamera )
  1488. {
  1489. case CAM_FREE: return OBS_MODE_ROAMING;
  1490. case CAM_THIRD: return OBS_MODE_CHASE;
  1491. case CAM_FIRST: return OBS_MODE_IN_EYE;
  1492. }
  1493. return CAM_INVALID;
  1494. }
  1495. void CReplayPerformanceEditorPanel::UpdateTimeLabels()
  1496. {
  1497. CReplay *pPlayingReplay = g_pReplayManager->GetPlayingReplay();
  1498. if ( !pPlayingReplay || !m_pCurTimeLabel || !m_pTotalTimeLabel )
  1499. return;
  1500. float flCurTime, flTotalTime;
  1501. g_pClientReplayContext->GetPlaybackTimes( flCurTime, flTotalTime, pPlayingReplay, GetPerformance() );
  1502. int nCurRoundedTime = (int)flCurTime; // Essentially floor'd
  1503. if ( nCurRoundedTime == m_nLastRoundedTime )
  1504. return;
  1505. m_nLastRoundedTime = nCurRoundedTime;
  1506. // Set current time text
  1507. char szTimeText[64];
  1508. V_snprintf( szTimeText, sizeof( szTimeText ), "%s", CReplayTime::FormatTimeString( nCurRoundedTime ) );
  1509. m_pCurTimeLabel->SetText( szTimeText );
  1510. // Set total time text
  1511. V_snprintf( szTimeText, sizeof( szTimeText ), "%s", CReplayTime::FormatTimeString( (int)flTotalTime ) );
  1512. m_pTotalTimeLabel->SetText( szTimeText );
  1513. // Center between left-most camera button and play/pause button
  1514. m_pCurTimeLabel->SizeToContents();
  1515. m_pTotalTimeLabel->SizeToContents();
  1516. }
  1517. void CReplayPerformanceEditorPanel::UpdateCameraSelectionPosition( CameraMode_t nCameraMode )
  1518. {
  1519. Assert( nCameraMode >= 0 && nCameraMode < NCAMS );
  1520. m_iCameraSelection = nCameraMode;
  1521. UpdateCameraButtonImages();
  1522. }
  1523. void CReplayPerformanceEditorPanel::UpdateFreeCamSettings( const SetViewParams_t &params )
  1524. {
  1525. CCameraOptionsPanel_Free *pSettingsPanel = dynamic_cast< CCameraOptionsPanel_Free * >( m_pCameraOptionsPanels[ CAM_FREE ] );
  1526. if ( !pSettingsPanel )
  1527. return;
  1528. pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_ACCEL, params.m_flAccel );
  1529. pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_SPEED, params.m_flSpeed );
  1530. pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_FOV, params.m_flFov );
  1531. pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_ROTFILTER, params.m_flRotationFilter );
  1532. }
  1533. void CReplayPerformanceEditorPanel::UpdateTimeScale( float flScale )
  1534. {
  1535. CTimeScaleOptionsPanel *pSettingsPanel = dynamic_cast< CTimeScaleOptionsPanel * >( m_pCameraOptionsPanels[ COMPONENT_TIMESCALE ] );
  1536. if ( !pSettingsPanel )
  1537. return;
  1538. pSettingsPanel->SetValue( CTimeScaleOptionsPanel::SLIDER_TIMESCALE, flScale );
  1539. }
  1540. void CReplayPerformanceEditorPanel::LayoutPlayerCells()
  1541. {
  1542. int nPanelHeight = m_pPlayerCellsPanel->GetTall();
  1543. int nCellBuffer = XRES(1);
  1544. for ( int i = 0; i < 2; ++i )
  1545. {
  1546. int nCurX = m_nRedBlueLabelRightX;
  1547. for ( int j = 0; j <= MAX_PLAYERS; ++j )
  1548. {
  1549. CPlayerCell *pCurCell = m_pPlayerCells[i][j];
  1550. if ( !pCurCell->IsVisible() )
  1551. continue;
  1552. // Apply cached settings from .res file
  1553. if ( m_pPlayerCellData )
  1554. {
  1555. pCurCell->ApplySettings( m_pPlayerCellData );
  1556. }
  1557. const int nY = nPanelHeight/2 + m_nRedBlueSigns[i] * nPanelHeight/4 - pCurCell->GetTall()/2;
  1558. pCurCell->SetPos(
  1559. nCurX,
  1560. nY
  1561. );
  1562. nCurX += pCurCell->GetWide() + nCellBuffer;
  1563. }
  1564. }
  1565. }
  1566. void CReplayPerformanceEditorPanel::PerformLayout()
  1567. {
  1568. int w = ScreenWidth(), h = ScreenHeight();
  1569. SetBounds(0,0,w,h);
  1570. // Layout camera options panels
  1571. for ( int i = 0; i < NCAMS; ++i )
  1572. {
  1573. CCameraOptionsPanel *pCurOptionsPanel = m_pCameraOptionsPanels[ i ];
  1574. if ( !pCurOptionsPanel )
  1575. continue;
  1576. CExImageButton *pCurCameraButton = m_pCameraButtons[ i ];
  1577. if ( !pCurCameraButton )
  1578. continue;
  1579. // Get camera button position
  1580. int aCameraButtonPos[2];
  1581. int aBottomPos[2];
  1582. pCurCameraButton->GetPos( aCameraButtonPos[ 0 ], aCameraButtonPos[ 1 ] );
  1583. m_pBottom->GetPos( aBottomPos[ 0 ], aBottomPos[ 1 ] );
  1584. // Layout the panel now - it should set its own size, which we need to know to position it properly
  1585. pCurOptionsPanel->InvalidateLayout( true, true );
  1586. // Position it
  1587. pCurOptionsPanel->SetPos(
  1588. aBottomPos[ 0 ] + aCameraButtonPos[ 0 ] + pCurCameraButton->GetWide() - pCurOptionsPanel->GetWide() - XRES( 3 ),
  1589. aBottomPos[ 1 ] + aCameraButtonPos[ 1 ] - pCurOptionsPanel->GetTall()
  1590. );
  1591. }
  1592. // Setup menu position relative to menu button
  1593. int aMenuButtonPos[2];
  1594. m_pMenuButton->GetPos( aMenuButtonPos[0], aMenuButtonPos[1] );
  1595. m_pMenu->SetPos( aMenuButtonPos[0], aMenuButtonPos[1] + m_pMenuButton->GetTall() );
  1596. // Set player cell panel to be the size of half the bottom panel
  1597. int aBottomSize[2];
  1598. m_pBottom->GetSize( aBottomSize[0], aBottomSize[1] );
  1599. m_pPlayerCellsPanel->SetBounds( 0, 0, aBottomSize[0] / 2, m_pPlayerCellsPanel->GetTall() );
  1600. CExLabel *pRedBlueLabels[2] = {
  1601. dynamic_cast< CExLabel * >( m_pPlayerCellsPanel->FindChildByName( "RedLabel" ) ),
  1602. dynamic_cast< CExLabel * >( m_pPlayerCellsPanel->FindChildByName( "BlueLabel" ) )
  1603. };
  1604. int nMargins[2] = { (int)XRES( 5 ), (int)YRES( 2 ) };
  1605. for ( int i = 0; i < 2; ++i )
  1606. {
  1607. pRedBlueLabels[i]->SizeToContents();
  1608. const int nY = m_pPlayerCellsPanel->GetTall()/2 + m_nRedBlueSigns[i] * m_pPlayerCellsPanel->GetTall()/4 - pRedBlueLabels[i]->GetTall()/2;
  1609. pRedBlueLabels[i]->SetPos( nMargins[0], nY );
  1610. m_nRedBlueLabelRightX = MAX( m_nRedBlueLabelRightX, nMargins[0] + pRedBlueLabels[i]->GetWide() + nMargins[0] );
  1611. }
  1612. // Position player cells
  1613. LayoutPlayerCells();
  1614. BaseClass::PerformLayout();
  1615. }
  1616. bool CReplayPerformanceEditorPanel::OnStateChangeRequested( const char *pEventStr )
  1617. {
  1618. // If we're already recording, allow the change.
  1619. if ( g_pReplayPerformanceController->IsRecording() )
  1620. return true;
  1621. // If we aren't recording and there is no forthcoming data in the playback stream, allow the change.
  1622. if ( !g_pReplayPerformanceController->IsPlaybackDataLeft() )
  1623. return true;
  1624. // Otherwise, record the event string and show a dialog asking the user if they're sure they want to nuke.
  1625. V_strncpy( m_szSuspendedEvent, pEventStr, sizeof( m_szSuspendedEvent ) );
  1626. ShowConfirmDialog( "#Replay_Warning", "#Replay_NukePerformanceChanges", "#GameUI_Confirm", "#GameUI_CancelBold", OnConfirmDestroyChanges, this, this, REPLAY_SOUND_DIALOG_POPUP );
  1627. return false;
  1628. }
  1629. void CReplayPerformanceEditorPanel::SetButtonTip( wchar_t *pTipText, Panel *pContextPanel )
  1630. {
  1631. // Set the text
  1632. m_pButtonTip->SetText( pTipText );
  1633. m_pButtonTip->InvalidateLayout( true, true );
  1634. // Center relative to context panel
  1635. int aPos[2];
  1636. ipanel()->GetAbsPos( pContextPanel->GetVPanel(), aPos[0], aPos[1] );
  1637. const int nX = clamp(
  1638. aPos[0] - m_pButtonTip->GetWide() / 2,
  1639. 0,
  1640. ScreenWidth() - m_pButtonTip->GetWide() - (int) XRES( 40 )
  1641. );
  1642. const int nY = m_nBottomPanelStartY - m_pButtonTip->GetTall() - (int) YRES( 2 );
  1643. m_pButtonTip->SetPos( nX, nY );
  1644. }
  1645. void CReplayPerformanceEditorPanel::ShowButtonTip( bool bShow )
  1646. {
  1647. m_pButtonTip->SetVisible( bShow );
  1648. }
  1649. void CReplayPerformanceEditorPanel::ShowSavingDialog()
  1650. {
  1651. Assert( !m_pSavingDlg );
  1652. m_pSavingDlg = new CSavingDialog( ReplayUI_GetPerformanceEditor() );
  1653. ShowWaitingDialog( m_pSavingDlg, "#Replay_Saving", true, false, -1 );
  1654. }
  1655. void CReplayPerformanceEditorPanel::ShowPanel( bool bShow )
  1656. {
  1657. if ( bShow == IsVisible() )
  1658. return;
  1659. if ( bShow )
  1660. {
  1661. // We are now performing.
  1662. m_pRecLightPanel->SetPerforming( true );
  1663. // Disable keyboard input on all panels added to the list
  1664. FOR_EACH_LL( m_lstDisableKeyboardInputPanels, it )
  1665. {
  1666. m_lstDisableKeyboardInputPanels[ it ]->SetKeyBoardInputEnabled( false );
  1667. }
  1668. DisplayPerformanceTip( "#Replay_PerfTip_ExitPerfMode", &replay_perftip_count_exit, MAX_TIP_DISPLAYS );
  1669. // Fire a message the game DLL can intercept (for achievements, etc).
  1670. IGameEvent *event = gameeventmanager->CreateEvent( "entered_performance_mode" );
  1671. if ( event )
  1672. {
  1673. gameeventmanager->FireEventClientSide( event );
  1674. }
  1675. // Play a sound
  1676. surface()->PlaySound( "replay\\enterperformancemode.wav" );
  1677. }
  1678. else
  1679. {
  1680. // Display a tip
  1681. DisplayPerformanceTip( "#Replay_PerfTip_EnterPerfMode", &replay_perftip_count_enter, MAX_TIP_DISPLAYS );
  1682. // Play a sound
  1683. surface()->PlaySound( "replay\\exitperformancemode.wav" );
  1684. }
  1685. // Show mouse cursor
  1686. SetMouseInputEnabled( bShow );
  1687. SetVisible( bShow );
  1688. MakePopup( bShow );
  1689. // Avoid waiting for next OnThink() to hide background images
  1690. m_pRecLightPanel->UpdatePauseState( bShow );
  1691. m_pRecLightPanel->UpdateBackgroundVisibility();
  1692. // Play or pause
  1693. if ( bShow )
  1694. {
  1695. PauseDemo();
  1696. }
  1697. else
  1698. {
  1699. PlayDemo();
  1700. }
  1701. // Keep controller informed about pause state so that it can throw away unimportant events during pause if it's recording.
  1702. g_pReplayPerformanceController->NotifyPauseState( bShow );
  1703. }
  1704. bool CReplayPerformanceEditorPanel::OnEndOfReplayReached()
  1705. {
  1706. if ( m_bShownAtLeastOnce )
  1707. {
  1708. ShowPanel( true );
  1709. DisplayPerformanceTip( "#Replay_PerfTip_EndOfReplayReached" );
  1710. // Don't end demo playback yet.
  1711. return true;
  1712. }
  1713. // Let the demo player end demo playback
  1714. return false;
  1715. }
  1716. void CReplayPerformanceEditorPanel::AddSetViewEvent()
  1717. {
  1718. if ( !g_pReplayManager->GetPlayingReplay() )
  1719. return;
  1720. if ( !g_pReplayPerformanceController )
  1721. return;
  1722. Vector pos;
  1723. QAngle angles;
  1724. float fov;
  1725. ReplayCamera()->GetCachedView( pos, angles, fov );
  1726. SetViewParams_t params;
  1727. params.m_flTime = GetPlaybackTime();
  1728. params.m_flFov = fov;
  1729. params.m_pOrigin = &pos;
  1730. params.m_pAngles = &angles;
  1731. params.m_flAccel = ReplayCamera()->m_flRoamingAccel;
  1732. params.m_flSpeed = ReplayCamera()->m_flRoamingSpeed;
  1733. params.m_flRotationFilter = ReplayCamera()->m_flRoamingRotFilterFactor;
  1734. g_pReplayPerformanceController->AddEvent_Camera_SetView( params );
  1735. }
  1736. // Input should be in [0,1]
  1737. void CReplayPerformanceEditorPanel::AddTimeScaleEvent( float flTimeScale )
  1738. {
  1739. if ( !g_pReplayManager->GetPlayingReplay() )
  1740. return;
  1741. if ( !g_pReplayPerformanceController )
  1742. return;
  1743. g_pReplayPerformanceController->AddEvent_TimeScale( GetPlaybackTime(), flTimeScale );
  1744. }
  1745. void CReplayPerformanceEditorPanel::UpdateCameraButtonImages( bool bForceUnselected/*=false*/ )
  1746. {
  1747. CReplayPerformance *pPerformance = GetPerformance();
  1748. for ( int i = 0; i < NCAMS; ++i )
  1749. {
  1750. CFmtStr fmtFile(
  1751. gs_pBaseComponentNames[i],
  1752. gs_pCamNames[i],
  1753. ( !bForceUnselected && ( !pPerformance || g_pReplayPerformanceController->IsRecording() ) && i == m_iCameraSelection ) ? "_selected" : ""
  1754. );
  1755. if ( m_pCameraButtons[ i ] )
  1756. {
  1757. m_pCameraButtons[ i ]->SetSubImage( fmtFile.Access() );
  1758. }
  1759. }
  1760. }
  1761. void CReplayPerformanceEditorPanel::EnsureRecording( bool bShouldSnip )
  1762. {
  1763. // Not recording?
  1764. if ( !g_pReplayPerformanceController->IsRecording() )
  1765. {
  1766. // Start recording - snip if needed.
  1767. g_pReplayPerformanceController->StartRecording( GetReplay(), bShouldSnip );
  1768. }
  1769. }
  1770. void CReplayPerformanceEditorPanel::ToggleMenu()
  1771. {
  1772. if ( !m_pMenu )
  1773. return;
  1774. // Show/hide
  1775. const bool bShow = !m_pMenu->IsVisible();
  1776. m_pMenu->SetVisible( bShow );
  1777. }
  1778. void CReplayPerformanceEditorPanel::SaveAs( const wchar_t *pTitle )
  1779. {
  1780. if ( !g_pReplayPerformanceController->SaveAsAsync( pTitle ) )
  1781. {
  1782. DisplaySavedTip( false );
  1783. }
  1784. ShowSavingDialog();
  1785. }
  1786. /*static*/ void CReplayPerformanceEditorPanel::OnConfirmSaveAs( bool bShouldSave, wchar_t *pTitle, void *pContext )
  1787. {
  1788. // NOTE: Assumes that overwriting has already been confirmed by the user.
  1789. if ( !bShouldSave )
  1790. return;
  1791. CReplayPerformanceEditorPanel *pThis = (CReplayPerformanceEditorPanel *)pContext;
  1792. pThis->SaveAs( pTitle );
  1793. surface()->PlaySound( "replay\\saved_take.wav" );
  1794. }
  1795. void CReplayPerformanceEditorPanel::ShowRewindConfirmMessage()
  1796. {
  1797. ShowMessageBox( "#Replay_RewindWarningTitle", "#Replay_RewindWarningMsg", "#GameUI_OK", OnConfirmRewind, NULL, (void *)this );
  1798. surface()->PlaySound( "replay\\replaydialog_warn.wav" );
  1799. }
  1800. /*static*/ void CReplayPerformanceEditorPanel::OnConfirmRewind( bool bConfirmed, void *pContext )
  1801. {
  1802. if ( bConfirmed )
  1803. {
  1804. if ( pContext )
  1805. {
  1806. CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
  1807. pEditor->OnCommand( "goto_back" );
  1808. }
  1809. }
  1810. }
  1811. void CReplayPerformanceEditorPanel::OnMenuCommand_Save( bool bExitEditorWhenDone/*=false*/ )
  1812. {
  1813. // If this is the first time we're saving this performance, do a save-as.
  1814. if ( !g_pReplayPerformanceController->HasSavedPerformance() )
  1815. {
  1816. OnMenuCommand_SaveAs( bExitEditorWhenDone );
  1817. return;
  1818. }
  1819. // Regular save
  1820. if ( !g_pReplayPerformanceController->SaveAsync() )
  1821. {
  1822. DisplaySavedTip( false );
  1823. }
  1824. // Show saving dialog
  1825. ShowSavingDialog();
  1826. // Exit editor?
  1827. if ( bExitEditorWhenDone )
  1828. {
  1829. OnMenuCommand_Exit();
  1830. }
  1831. }
  1832. void CReplayPerformanceEditorPanel::OnMenuCommand_SaveAs( bool bExitEditorWhenDone/*=false*/ )
  1833. {
  1834. ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveAs, this, GetReplay(), bExitEditorWhenDone );
  1835. }
  1836. void CReplayPerformanceEditorPanel::DisplaySavedTip( bool bSucceess )
  1837. {
  1838. DisplayPerformanceTip( bSucceess ? "#Replay_PerfTip_Saved" : "#Replay_PerfTip_SaveFailed" );
  1839. }
  1840. void CReplayPerformanceEditorPanel::OnSaveComplete()
  1841. {
  1842. DisplaySavedTip( g_pReplayPerformanceController->GetLastSaveStatus() );
  1843. m_pSavingDlg = NULL;
  1844. }
  1845. void CReplayPerformanceEditorPanel::HandleUiToggle()
  1846. {
  1847. if ( !TFModalStack()->IsEmpty() )
  1848. return;
  1849. PauseDemo();
  1850. Exit_ShowDialogs();
  1851. }
  1852. void CReplayPerformanceEditorPanel::Exit()
  1853. {
  1854. engine->ClientCmd_Unrestricted( "disconnect" );
  1855. }
  1856. void CReplayPerformanceEditorPanel::Exit_ShowDialogs()
  1857. {
  1858. if ( g_pReplayPerformanceController->IsDirty() )
  1859. {
  1860. ShowConfirmDialog( "#Replay_DiscardTitle", "#Replay_DiscardChanges", "#Replay_Discard", "#Replay_Cancel", OnConfirmDiscard, NULL, this, REPLAY_SOUND_DIALOG_POPUP );
  1861. }
  1862. else
  1863. {
  1864. ShowConfirmDialog( "#Replay_ExitEditorTitle", "#Replay_BackToReplays", "#GameUI_Confirm", "#Replay_Cancel", OnConfirmExit, NULL, this, REPLAY_SOUND_DIALOG_POPUP );
  1865. }
  1866. }
  1867. void CReplayPerformanceEditorPanel::OnMenuCommand_Exit()
  1868. {
  1869. Exit_ShowDialogs();
  1870. }
  1871. void CReplayPerformanceEditorPanel::OnCommand( const char *command )
  1872. {
  1873. float flCurTime = GetPlaybackTime();
  1874. g_bIsReplayRewinding = false;
  1875. if ( !V_stricmp( command, "toggle_menu" ) )
  1876. {
  1877. ToggleMenu();
  1878. }
  1879. else if ( !V_strnicmp( command, "menu_", 5 ) )
  1880. {
  1881. const char *pMenuCommand = command + 5;
  1882. if ( !V_stricmp( pMenuCommand, "save" ) )
  1883. {
  1884. OnMenuCommand_Save();
  1885. }
  1886. else if ( !V_stricmp( pMenuCommand, "saveas" ) )
  1887. {
  1888. OnMenuCommand_SaveAs();
  1889. }
  1890. else if ( !V_stricmp( pMenuCommand, "exit" ) )
  1891. {
  1892. OnMenuCommand_Exit();
  1893. }
  1894. }
  1895. else if ( !V_stricmp( command, "close" ) )
  1896. {
  1897. ShowPanel( false );
  1898. MarkForDeletion();
  1899. return;
  1900. }
  1901. else if ( !V_stricmp( command, "play" ) )
  1902. {
  1903. ShowPanel( false );
  1904. return;
  1905. }
  1906. else if ( !V_stricmp( command, "pause" ) )
  1907. {
  1908. ShowPanel( true );
  1909. return;
  1910. }
  1911. else if ( !V_strnicmp( command, "timescale_", 10 ) )
  1912. {
  1913. const char *pTimeScaleCmd = command + 10;
  1914. if ( !V_stricmp( pTimeScaleCmd, "showpanel" ) )
  1915. {
  1916. // If we're playing back, pop up a dialog asking if the user is sure they want to nuke the
  1917. // rest of whatever is playing back.
  1918. if ( !OnStateChangeRequested( command ) )
  1919. return;
  1920. EnsureRecording();
  1921. }
  1922. }
  1923. else if ( !V_strnicmp( command, "settick_", 8 ) )
  1924. {
  1925. const char *pSetType = command + 8;
  1926. const int nCurTick = engine->GetDemoPlaybackTick();
  1927. if ( !V_stricmp( pSetType, "in" ) )
  1928. {
  1929. SetOrRemoveInTick( nCurTick, true );
  1930. }
  1931. else if ( !V_stricmp( pSetType, "out" ) )
  1932. {
  1933. SetOrRemoveOutTick( nCurTick, true );
  1934. }
  1935. // Save the replay
  1936. CReplay *pReplay = GetReplay();
  1937. if ( pReplay )
  1938. {
  1939. g_pReplayManager->FlagReplayForFlush( pReplay, true );
  1940. }
  1941. return;
  1942. }
  1943. else if ( !V_strnicmp( command, "goto_", 5 ) )
  1944. {
  1945. const char *pGotoType = command + 5;
  1946. CReplay *pReplay = GetReplay();
  1947. if ( pReplay )
  1948. {
  1949. const CReplayPerformance *pScratchPerformance = g_pReplayPerformanceController->GetPerformance();
  1950. const CReplayPerformance *pSavedPerformance = g_pReplayPerformanceController->GetSavedPerformance();
  1951. const CReplayPerformance *pPerformance = pScratchPerformance ? pScratchPerformance : pSavedPerformance;
  1952. const int nCurTick = engine->GetDemoPlaybackTick();
  1953. // If in or out ticks are set in the performance, use those for the 'full' rewind/fast-forward
  1954. const int nStartTick = MAX( 0, ( pPerformance && pPerformance->HasInTick() ) ? pPerformance->m_nTickIn : pReplay->m_nSpawnTick );
  1955. const int nEndTick = MAX( // The MAX() here will keep us from going back in time if we're already past the "end" tick
  1956. nCurTick,
  1957. ( ( pPerformance && pPerformance->HasOutTick() ) ?
  1958. pPerformance->m_nTickOut :
  1959. ( nStartTick + TIME_TO_TICKS( pReplay->m_flLength ) ) )
  1960. - TIME_TO_TICKS( 0.1f )
  1961. );
  1962. int nGotoTick = 0;
  1963. bool bGoingBack = false;
  1964. if ( !V_stricmp( pGotoType, "start" ) )
  1965. {
  1966. bGoingBack = true;
  1967. nGotoTick = nStartTick;
  1968. }
  1969. else if ( !V_stricmp( pGotoType, "back" ) )
  1970. {
  1971. // If this is the first time rewinding, display a message
  1972. if ( !replay_replayeditor_rewindmsgcounter.GetBool() )
  1973. {
  1974. replay_replayeditor_rewindmsgcounter.SetValue( 1 );
  1975. ShowRewindConfirmMessage();
  1976. return;
  1977. }
  1978. bGoingBack = true;
  1979. nGotoTick = nCurTick - TIME_TO_TICKS( 10.0f );
  1980. }
  1981. else if ( !V_stricmp( pGotoType, "end" ) )
  1982. {
  1983. nGotoTick = nEndTick; // Don't go back in time
  1984. }
  1985. // Clamp it
  1986. nGotoTick = clamp( nGotoTick, nStartTick, nEndTick );
  1987. // If going back...
  1988. if ( bGoingBack )
  1989. {
  1990. // ...and notify the recorder that we're skipping, which we only need to do if we're going backwards
  1991. g_pReplayPerformanceController->NotifyRewinding();
  1992. g_bIsReplayRewinding = true;
  1993. }
  1994. // Go to the given tick and pause
  1995. CFmtStr fmtCmd( "demo_gototick %i\ndemo_pause\n", nGotoTick );
  1996. engine->ClientCmd_Unrestricted( fmtCmd.Access() );
  1997. }
  1998. return;
  1999. }
  2000. else if ( !V_strnicmp( command, "setcamera_", 10 ) )
  2001. {
  2002. const char *pCamType = command + 10;
  2003. int nEntIndex = ReplayCamera()->GetPrimaryTargetIndex();
  2004. // If we're playing back, pop up a dialog asking if the user is sure they want to nuke the
  2005. // rest of whatever is playing back.
  2006. if ( !OnStateChangeRequested( command ) )
  2007. return;
  2008. EnsureRecording();
  2009. if ( !V_stricmp( pCamType, "first" ) )
  2010. {
  2011. ReplayCamera()->SetMode( OBS_MODE_IN_EYE );
  2012. UpdateCameraSelectionPosition( CAM_FIRST );
  2013. m_bCurrentTargetNeedsVisibilityUpdate = true;
  2014. g_pReplayPerformanceController->AddEvent_Camera_Change_FirstPerson( flCurTime, nEntIndex );
  2015. }
  2016. else if ( !V_stricmp( pCamType, "third" ) )
  2017. {
  2018. ReplayCamera()->SetMode( OBS_MODE_CHASE );
  2019. UpdateCameraSelectionPosition( CAM_THIRD );
  2020. m_bCurrentTargetNeedsVisibilityUpdate = true;
  2021. g_pReplayPerformanceController->AddEvent_Camera_Change_ThirdPerson( flCurTime, nEntIndex );
  2022. AddSetViewEvent();
  2023. }
  2024. else if ( !V_stricmp( pCamType, "free" ) )
  2025. {
  2026. ReplayCamera()->SetMode( OBS_MODE_ROAMING );
  2027. UpdateCameraSelectionPosition( CAM_FREE );
  2028. m_bCurrentTargetNeedsVisibilityUpdate = true;
  2029. g_pReplayPerformanceController->AddEvent_Camera_Change_Free( flCurTime );
  2030. AddSetViewEvent();
  2031. DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam", &replay_perftip_count_freecam_enter, MAX_TIP_DISPLAYS );
  2032. }
  2033. return;
  2034. }
  2035. else
  2036. {
  2037. engine->ClientCmd( const_cast<char *>( command ) );
  2038. return;
  2039. }
  2040. BaseClass::OnCommand( command );
  2041. }
  2042. void CReplayPerformanceEditorPanel::OnConfirmDestroyChanges( bool bConfirmed, void *pContext )
  2043. {
  2044. AssertMsg( pContext, "Should have a context! Fix me!" );
  2045. if ( pContext && bConfirmed )
  2046. {
  2047. CReplayPerformanceEditorPanel *pEditorPanel = (CReplayPerformanceEditorPanel *)pContext;
  2048. if ( bConfirmed )
  2049. {
  2050. CReplay *pReplay = pEditorPanel->GetReplay();
  2051. g_pReplayPerformanceController->StartRecording( pReplay, true );
  2052. // Reissue the command.
  2053. pEditorPanel->OnCommand( pEditorPanel->m_szSuspendedEvent );
  2054. // Play a sound
  2055. surface()->PlaySound( "replay\\snip.wav" );
  2056. }
  2057. // Clear suspended event
  2058. pEditorPanel->m_szSuspendedEvent[ 0 ] = '\0';
  2059. // Make sure mouse is free
  2060. pEditorPanel->SetMouseInputEnabled( true );
  2061. DisplayPerformanceTip( "#Replay_PerfTip_Snip" );
  2062. }
  2063. }
  2064. /*static*/ void CReplayPerformanceEditorPanel::OnConfirmDiscard( bool bConfirmed, void *pContext )
  2065. {
  2066. CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
  2067. if ( bConfirmed )
  2068. {
  2069. pEditor->Exit();
  2070. }
  2071. else
  2072. {
  2073. if ( !pEditor->IsVisible() )
  2074. {
  2075. PlayDemo();
  2076. }
  2077. }
  2078. }
  2079. /*static*/ void CReplayPerformanceEditorPanel::OnConfirmExit( bool bConfirmed, void *pContext )
  2080. {
  2081. CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
  2082. if ( bConfirmed )
  2083. {
  2084. pEditor->Exit();
  2085. }
  2086. else
  2087. {
  2088. if ( !pEditor->IsVisible() )
  2089. {
  2090. PlayDemo();
  2091. }
  2092. }
  2093. }
  2094. void CReplayPerformanceEditorPanel::SetOrRemoveInTick( int nTick, bool bRemoveIfSet )
  2095. {
  2096. SetOrRemoveTick( nTick, true, bRemoveIfSet );
  2097. }
  2098. void CReplayPerformanceEditorPanel::SetOrRemoveOutTick( int nTick, bool bRemoveIfSet )
  2099. {
  2100. SetOrRemoveTick( nTick, false, bRemoveIfSet );
  2101. }
  2102. void CReplayPerformanceEditorPanel::SetOrRemoveTick( int nTick, bool bUseInTick, bool bRemoveIfSet )
  2103. {
  2104. CReplayPerformance *pPerformance = GetPerformance();
  2105. AssertMsg( pPerformance, "Performance should always be valid by this point." );
  2106. ControlButtons_t iButton;
  2107. int *pResultTick;
  2108. const char *pSetTickKey;
  2109. const char *pUnsetTickKey;
  2110. if ( bUseInTick )
  2111. {
  2112. pResultTick = &pPerformance->m_nTickIn;
  2113. iButton = CTRLBUTTON_IN;
  2114. pSetTickKey = "#Replay_PerfTip_InPointSet";
  2115. pUnsetTickKey = "#Replay_PerfTip_InPointRemoved";
  2116. }
  2117. else
  2118. {
  2119. pResultTick = &pPerformance->m_nTickOut;
  2120. iButton = CTRLBUTTON_OUT;
  2121. pSetTickKey = "#Replay_PerfTip_OutPointSet";
  2122. pUnsetTickKey = "#Replay_PerfTip_OutPointRemoved";
  2123. }
  2124. // Tick explicitly being removed? Caller passing in -1?
  2125. const bool bRemoving = nTick < 0;
  2126. // If tick already exists and we want to remove, remove it
  2127. bool bSetting;
  2128. if ( ( *pResultTick >= 0 && bRemoveIfSet ) || bRemoving )
  2129. {
  2130. *pResultTick = -1;
  2131. bSetting = false;
  2132. }
  2133. else
  2134. {
  2135. *pResultTick = nTick;
  2136. bSetting = true;
  2137. }
  2138. // Display the appropriate tip
  2139. DisplayPerformanceTip( bSetting ? pSetTickKey : pUnsetTickKey );
  2140. // Select/unselect button
  2141. CExImageButton *pButton = m_pCtrlButtons[ iButton ];
  2142. pButton->SetSelected( bSetting );
  2143. pButton->InvalidateLayout( true, true ); // Without this, buttons don't update immediately
  2144. // Mark the performance as dirty
  2145. g_pReplayPerformanceController->NotifyDirty();
  2146. }
  2147. CReplay *CReplayPerformanceEditorPanel::GetReplay()
  2148. {
  2149. return g_pReplayManager->GetReplay( m_hReplay );
  2150. }
  2151. void CReplayPerformanceEditorPanel::OnRewindComplete()
  2152. {
  2153. // Get rid of any "selected" icon - this will happen as soon as we actually start playing back
  2154. // events, but if we aren't playing back events yet we need to explicitly tell the icons not
  2155. // to display their "selected" versions.
  2156. UpdateCameraButtonImages( true );
  2157. }
  2158. //-----------------------------------------------------------------------------
  2159. static DHANDLE<CReplayPerformanceEditorPanel> g_ReplayPerformanceEditorPanel;
  2160. //-----------------------------------------------------------------------------
  2161. CReplayPerformanceEditorPanel *ReplayUI_InitPerformanceEditor( ReplayHandle_t hReplay )
  2162. {
  2163. if ( !g_ReplayPerformanceEditorPanel.Get() )
  2164. {
  2165. g_ReplayPerformanceEditorPanel = SETUP_PANEL( new CReplayPerformanceEditorPanel( NULL, hReplay ) );
  2166. g_ReplayPerformanceEditorPanel->InvalidateLayout( false, true );
  2167. }
  2168. // Notify recorder of editor
  2169. g_pReplayPerformanceController->SetEditor( g_ReplayPerformanceEditorPanel.Get() );
  2170. return g_ReplayPerformanceEditorPanel;
  2171. }
  2172. void ReplayUI_ClosePerformanceEditor()
  2173. {
  2174. if ( g_ReplayPerformanceEditorPanel )
  2175. {
  2176. g_ReplayPerformanceEditorPanel->MarkForDeletion();
  2177. g_ReplayPerformanceEditorPanel = NULL;
  2178. }
  2179. }
  2180. CReplayPerformanceEditorPanel *ReplayUI_GetPerformanceEditor()
  2181. {
  2182. return g_ReplayPerformanceEditorPanel;
  2183. }
  2184. #if _DEBUG
  2185. CON_COMMAND_F( replay_showperfeditor, "Show performance editor", FCVAR_CLIENTDLL )
  2186. {
  2187. ReplayUI_ClosePerformanceEditor();
  2188. ReplayUI_InitPerformanceEditor( REPLAY_HANDLE_INVALID );
  2189. }
  2190. CON_COMMAND_F( replay_tiptest, "", FCVAR_CLIENTDLL )
  2191. {
  2192. DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam" );
  2193. }
  2194. #endif
  2195. #endif