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.

881 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "cbase.h"
  5. #ifdef SAXXYMAINMENU_ENABLED
  6. #include "tf_hud_saxxycontest.h"
  7. #include "econ_item_inventory.h"
  8. #include "econ/confirm_dialog.h"
  9. #include "econ/econ_controls.h"
  10. #include "vgui/IVGui.h"
  11. #include "vgui/ILocalize.h"
  12. #include "vgui/IInput.h"
  13. #include "vgui/ISurface.h"
  14. #include "vgui_controls/TextEntry.h"
  15. #include "vgui_controls/ComboBox.h"
  16. #include "ienginevgui.h"
  17. #include "replaybrowsermainpanel.h"
  18. #include "gc_clientsystem.h"
  19. #include "rtime.h"
  20. #include "tf_gcmessages.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. DECLARE_BUILD_FACTORY( CSaxxyAwardsPanel );
  24. static void SaxxyAwards_ShowSubmitForm();
  25. //-----------------------------------------------------------------------------
  26. int CSaxxyAwardsPanel::sm_nShowCounter = -1;
  27. //-----------------------------------------------------------------------------
  28. #define FOR_EACH_FLASH( i_ ) for ( int i_ = 0; i_ < MAX_FLASHES; ++i_ )
  29. #define FOR_EACH_GLOW( i_ ) for ( int i_ = 0; i_ < MAX_GLOWS; ++i_ )
  30. #define FOR_EACH_CLAP( i_ ) for ( int i_ = 0; i_ < MAX_CLAPS; ++i_ )
  31. //-----------------------------------------------------------------------------
  32. #define NUM_FLASH_SOUNDS 11
  33. #define NUM_FLASH_MATERIALS 3
  34. #define FREAKOUT_THRESHOLD 0.7f
  35. //-----------------------------------------------------------------------------
  36. inline float SCurve( float t )
  37. {
  38. t = clamp( t, 0.0f, 1.0f );
  39. return t * t * (3 - 2*t);
  40. }
  41. inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax )
  42. {
  43. float flDenom = flInMax - flInMin;
  44. if ( flDenom == 0.0f )
  45. return 0.0f;
  46. float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f );
  47. return Lerp( t, flOutMin, flOutMax );
  48. }
  49. //-----------------------------------------------------------------------------
  50. CSaxxyAwardsPanel::CSaxxyAwardsPanel( Panel *pParent, const char *pName )
  51. : EditablePanel( pParent, pName ),
  52. m_pCameraFlashKv( NULL )
  53. {
  54. ivgui()->AddTickSignal( GetVPanel(), 17 );
  55. Init();
  56. ++sm_nShowCounter;
  57. }
  58. CSaxxyAwardsPanel::~CSaxxyAwardsPanel()
  59. {
  60. ivgui()->RemoveTickSignal( GetVPanel() );
  61. }
  62. void CSaxxyAwardsPanel::Init()
  63. {
  64. m_flShowTime = GetCurrentTime();
  65. m_vDialogsParent = (VPANEL)-1;
  66. m_nNumTargetFlashes = 0;
  67. m_flLastTickTime = 0.0f;
  68. m_flEarliestNextFlashTime = 0.0f;
  69. m_flGlowFade = 0.0f;
  70. m_flNextPanelTestTime = 0.0f;
  71. m_pBackgroundPanel = NULL;
  72. m_pSaxxyModelPanel = NULL;
  73. m_pStageBgPanel = NULL;
  74. m_pSubmitButton = NULL;
  75. m_pInfoLabel = NULL;
  76. m_pCurtainPanel = NULL;
  77. m_pContestOverLabel = NULL;
  78. m_pSpotlightPanel = NULL;
  79. m_flCurtainStartAnimTime = -1.0f;
  80. FOR_EACH_CLAP( i )
  81. {
  82. m_aClapPlayTimes[i] = 0.0f;
  83. }
  84. for ( int i = 0; i < 2; ++i )
  85. {
  86. m_aFilteredMousePos[i][0] = ScreenWidth() / 2;
  87. m_aFilteredMousePos[i][1] = ScreenHeight() / 2;
  88. }
  89. FOR_EACH_FLASH( i )
  90. {
  91. CFmtStr fmtPanelName( "FlashPanel_%i", i );
  92. FlashInfo_t *pCurFlashInfo = &m_aFlashes[ i ];
  93. pCurFlashInfo->m_pPanel = new ImagePanel( NULL, fmtPanelName.Access() );
  94. pCurFlashInfo->m_bInUse = false;
  95. }
  96. m_angModelRot.Init();
  97. m_vSaxxyDefaultPos.Init();
  98. }
  99. void CSaxxyAwardsPanel::ApplySettings( KeyValues *pInResourceData )
  100. {
  101. BaseClass::ApplySettings( pInResourceData );
  102. // Delete old KeyValues
  103. if ( m_pCameraFlashKv )
  104. {
  105. m_pCameraFlashKv->deleteThis();
  106. m_pCameraFlashKv = NULL;
  107. }
  108. // Copy camera flash keyvalues for creating instances
  109. KeyValues *pCameraFlash = pInResourceData->FindKey( "CameraFlashSettings" );
  110. Assert( pCameraFlash );
  111. if ( pCameraFlash )
  112. {
  113. m_pCameraFlashKv = pCameraFlash->MakeCopy();
  114. }
  115. }
  116. bool CSaxxyAwardsPanel::CreateFlash()
  117. {
  118. if ( GetActiveFlashCount() == MAX_FLASHES )
  119. return false;
  120. const int iUnusedSlot = GetUnusedFlashSlot(); Assert( iUnusedSlot >= 0 && iUnusedSlot < MAX_FLASHES );
  121. if ( iUnusedSlot < 0 )
  122. return false; // Should never happen, since GetActiveFlashCount() didn't return 0
  123. FlashInfo_t *pFlashInfo = &m_aFlashes[ iUnusedSlot ];
  124. ImagePanel *pPanel = pFlashInfo->m_pPanel;
  125. // Apply base settings
  126. if ( !m_pCameraFlashKv )
  127. return false;
  128. pPanel->ApplySettings( m_pCameraFlashKv );
  129. // Set the image to a random image
  130. const int iImage = 1 + rand() % NUM_FLASH_MATERIALS;
  131. CFmtStr fmtImageName( "../models/effects/camera_flash/camera_flash_%02i", iImage );
  132. pPanel->SetImage( fmtImageName.Access() );
  133. pFlashInfo->m_flStartTime = GetCurrentTime();
  134. pFlashInfo->m_flLifeLength = FRand( m_flFlashLifeLengthMin, m_flFlashLifeLengthMax );
  135. pFlashInfo->m_bInUse = true;
  136. // Put the flash at a random position in the "flash bounds"
  137. pFlashInfo->m_nCenterX = (int)FRand( m_nFlashBoundsX, m_nFlashBoundsX + m_nFlashBoundsW );
  138. pFlashInfo->m_nCenterY = (int)FRand( m_nFlashBoundsY, m_nFlashBoundsY + m_nFlashBoundsH );
  139. pFlashInfo->m_nMinSize = (int)Lerp( FRand( 0.0f, 1.0f ), m_nFlashStartSizeMin, m_nFlashStartSizeMax );
  140. pFlashInfo->m_nMaxSize = pFlashInfo->m_nMinSize * FRand( .5f * m_flFlashMaxScale, m_flFlashMaxScale );
  141. Assert( pFlashInfo->m_nMaxSize > pFlashInfo->m_nMinSize );
  142. pFlashInfo->m_nCurW = pFlashInfo->m_nMinSize;
  143. pFlashInfo->m_nCurH = pFlashInfo->m_nMinSize;
  144. PlaceFlash( pFlashInfo );
  145. // Setup visibility
  146. pPanel->SetAlpha( 0 );
  147. pPanel->SetVisible( true );
  148. // Play a random flash sound
  149. int iSound = rand() % NUM_FLASH_SOUNDS + 1;
  150. Assert( iSound >= 1 && iSound <= NUM_FLASH_SOUNDS );
  151. CFmtStr fmtSoundFilename( "misc/tf_camera_%02i.wav", iSound );
  152. surface()->PlaySound( fmtSoundFilename.Access() );
  153. return true;
  154. }
  155. void CSaxxyAwardsPanel::PlaceFlash( FlashInfo_t *pFlashInfo )
  156. {
  157. pFlashInfo->m_pPanel->SetBounds(
  158. pFlashInfo->m_nCenterX - pFlashInfo->m_nCurW / 2,
  159. pFlashInfo->m_nCenterY - pFlashInfo->m_nCurH / 2,
  160. pFlashInfo->m_nCurW,
  161. pFlashInfo->m_nCurH
  162. );
  163. }
  164. void CSaxxyAwardsPanel::ApplySchemeSettings( IScheme *pScheme )
  165. {
  166. BaseClass::ApplySchemeSettings( pScheme );
  167. LoadControlSettings( "resource/UI/MainMenu_SaxxyAwards.res" );
  168. SetupContestPanels();
  169. m_vDialogsParent = GetDialogsParent();
  170. m_pBackgroundPanel = dynamic_cast< EditablePanel * >( FindChildByName( "Background" ) );
  171. m_pSubmitButton = dynamic_cast< CExButton *>( FindChildByName( "SubmitButton" ) );
  172. m_pInfoLabel = FindChildByName( "InfoLabel" );
  173. m_pContestOverLabel = FindChildByName( "ContestOverLabel" );
  174. m_pSpotlightPanel = dynamic_cast< ImagePanel * >( FindChildByName( "SpotlightPanel" ) );
  175. m_pStageBgPanel = dynamic_cast< ImagePanel * >( FindChildByName( "StageBackground" ) );
  176. m_pCurtainPanel = dynamic_cast< EditablePanel * >( FindChildByName( "CurtainsPanel" ) );
  177. if ( m_pCurtainPanel )
  178. {
  179. m_Curtains[0].m_pPanel = dynamic_cast< ImagePanel * >( m_pCurtainPanel->FindChildByName( "CurtainsLeft" ) );
  180. m_Curtains[1].m_pPanel = dynamic_cast< ImagePanel * >( m_pCurtainPanel->FindChildByName( "CurtainsRight" ) );
  181. // Hide curtains if this isn't the first view
  182. if ( sm_nShowCounter > 0 )
  183. {
  184. for ( int i = 0; i < 2; ++i )
  185. {
  186. if ( !m_Curtains[i].m_pPanel )
  187. continue;
  188. m_Curtains[i].m_pPanel->SetVisible( false );
  189. }
  190. }
  191. // Set all flash parents to background panel
  192. if ( m_pBackgroundPanel )
  193. {
  194. FOR_EACH_FLASH( i )
  195. {
  196. m_aFlashes[ i ].m_pPanel->SetParent( m_pStageBgPanel );
  197. }
  198. }
  199. // Setup Saxxy model
  200. m_pSaxxyModelPanel = dynamic_cast< CBaseModelPanel * >( m_pCurtainPanel->FindChildByName( "SaxxyModelPanel" ) );
  201. if ( m_pSaxxyModelPanel )
  202. {
  203. MDLHandle_t hModel = mdlcache->FindMDL( "models/effects/saxxy_flash/saxxy_flash.mdl" );
  204. m_pSaxxyModelPanel->SetMDL( hModel );
  205. m_pSaxxyModelPanel->SetSequence( ACT_RESET );
  206. mdlcache->Release( hModel ); // counterbalance addref from within FindMDL
  207. m_pSaxxyModelPanel->InvalidateLayout( true, true ); // Updates player position
  208. m_vSaxxyDefaultPos = m_pSaxxyModelPanel->GetPlayerPos();
  209. }
  210. }
  211. }
  212. void CSaxxyAwardsPanel::PerformLayout()
  213. {
  214. BaseClass::PerformLayout();
  215. }
  216. void CSaxxyAwardsPanel::Refresh()
  217. {
  218. Init();
  219. InvalidateLayout( true, true );
  220. }
  221. void CSaxxyAwardsPanel::OnCommand( const char *pCommand )
  222. {
  223. if ( FStrEq( pCommand, "viewdetails" ) )
  224. {
  225. if ( steamapicontext && steamapicontext->SteamFriends() )
  226. {
  227. steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/saxxyawards/" );
  228. }
  229. }
  230. else if ( FStrEq( pCommand, "submit" ) )
  231. {
  232. SaxxyAwards_ShowSubmitForm();
  233. }
  234. else
  235. {
  236. BaseClass::OnCommand( pCommand );
  237. }
  238. }
  239. void CSaxxyAwardsPanel::OnTick()
  240. {
  241. if ( C_BasePlayer::GetLocalPlayer() )
  242. return;
  243. if ( ReplayUI_GetBrowserPanel() && ReplayUI_GetBrowserPanel()->IsVisible() )
  244. return;
  245. if ( !IsVisible() )
  246. return;
  247. // Calculate elapsed
  248. const float flCurTime = GetCurrentTime();
  249. const float flElapsed = clamp( flCurTime - m_flLastTickTime, 0, 0.1f );
  250. m_flLastTickTime = flCurTime;
  251. // Msg( "initial freakout? %s\n", InInitialFreakoutPeriod() ? "yes" : "no" );
  252. const bool bOtherPanelsOpen = AreOtherPanelsOpen( flCurTime );
  253. UpdateMousePos( flElapsed );
  254. CurtainsThink();
  255. RotateModel( flElapsed );
  256. FlashThink( bOtherPanelsOpen );
  257. SpotlightThink();
  258. ClapsThink( flCurTime, flElapsed, bOtherPanelsOpen );
  259. SetupContestPanels();
  260. }
  261. bool CSaxxyAwardsPanel::AreOtherPanelsOpen( float flCurTime )
  262. {
  263. static bool s_bOtherPanelsOpen = false;
  264. if ( flCurTime < m_flNextPanelTestTime )
  265. return s_bOtherPanelsOpen;
  266. // Setup next think time
  267. m_flNextPanelTestTime = flCurTime + 2.0f;
  268. // Do tests to see if other panels are open
  269. bool bOtherPanelsOpen = false;
  270. {
  271. const int nNumGameUICarePanels = 4;
  272. const char *pCarePanels[nNumGameUICarePanels] = { "GameConsole", "character_info", "store_panel", "BugUIPanel" };
  273. bOtherPanelsOpen = bOtherPanelsOpen || AreNonMainMenuPanelsOpen( enginevgui->GetPanel( PANEL_GAMEUIDLL ), pCarePanels, nNumGameUICarePanels );
  274. }
  275. // Msg( "-----\n" );
  276. if ( m_vDialogsParent != (VPANEL)-1 )
  277. {
  278. const int nNumCarePanels = 2;
  279. const char *pCarePanels[nNumCarePanels] = { "CServerBrowserDialog", "OptionsDialog" };
  280. bOtherPanelsOpen = bOtherPanelsOpen || AreNonMainMenuPanelsOpen( m_vDialogsParent, pCarePanels, nNumCarePanels );
  281. }
  282. // Cache so we don't have to think every frame
  283. s_bOtherPanelsOpen = bOtherPanelsOpen;
  284. return bOtherPanelsOpen;
  285. }
  286. void CSaxxyAwardsPanel::PaintBackground()
  287. {
  288. BaseClass::PaintBackground();
  289. }
  290. void CSaxxyAwardsPanel::UpdateMousePos( float flElapsed )
  291. {
  292. // Set target mouse pos
  293. int x, y;
  294. input()->GetCursorPos( x, y );
  295. m_aFilteredMousePos[1][0] = x;
  296. m_aFilteredMousePos[1][1] = y;
  297. // Move towards target
  298. const float t = flElapsed * 1.5f;
  299. m_aFilteredMousePos[0][0] = Lerp( t, m_aFilteredMousePos[0][0], m_aFilteredMousePos[1][0] );
  300. m_aFilteredMousePos[0][1] = Lerp( t, m_aFilteredMousePos[0][1], m_aFilteredMousePos[1][1] );
  301. // Msg( "mouse pos: current=(%f %f) target=(%f %f)\n", m_aFilteredMousePos[0][0], m_aFilteredMousePos[0][1],
  302. // m_aFilteredMousePos[1][0], m_aFilteredMousePos[1][1] );
  303. }
  304. void CSaxxyAwardsPanel::CurtainsThink()
  305. {
  306. if ( !m_Curtains[0].m_pPanel || !m_Curtains[1].m_pPanel )
  307. return;
  308. // Has enough time passed? Only open the curtains m_flOpenCurtainsTime seconds after m_flShowTime.
  309. const float flElapsedSinceShowTime = GetCurrentTime() - m_flShowTime;
  310. if ( flElapsedSinceShowTime < m_flOpenCurtainsTime )
  311. return;
  312. // Start animating now?
  313. if ( CurtainsClosed() )
  314. {
  315. m_flCurtainStartAnimTime = GetCurrentTime();
  316. // Play crowd the first time the menu is shown
  317. if ( sm_nShowCounter == 0 )
  318. {
  319. surface()->PlaySound( "misc/boring_applause_1.wav" );
  320. }
  321. // Init positions to those set in res file
  322. for ( int i = 0; i < 2; ++i )
  323. {
  324. m_Curtains[i].m_pPanel->GetPos( m_Curtains[i].m_aInitialPos[0], m_Curtains[i].m_aInitialPos[1] );
  325. }
  326. }
  327. else // Already animating?
  328. {
  329. // Set updated positions for both curtains
  330. const float flCurTime = GetCurrentTime();
  331. for ( int i = 0; i < 2; ++i )
  332. {
  333. CurtainInfo_t *pCurCurtain = &m_Curtains[i];
  334. Panel *pPanel = pCurCurtain->m_pPanel;
  335. const int nSign = i == 0 ? -1 : 1;
  336. const float t = SCurve( clamp( ( flCurTime - m_flCurtainStartAnimTime ) / m_flCurtainAnimDuration, 0.0f, 1.0f ) );
  337. int aCurPos[2] = {
  338. Lerp( t, pCurCurtain->m_aInitialPos[0], pCurCurtain->m_aInitialPos[0] + nSign * pPanel->GetWide() ),
  339. pCurCurtain->m_aInitialPos[1]
  340. };
  341. m_Curtains[i].m_pPanel->SetPos( aCurPos[0], aCurPos[1] );
  342. }
  343. }
  344. }
  345. void CSaxxyAwardsPanel::RotateModel( float flElapsed )
  346. {
  347. if ( m_pSaxxyModelPanel )
  348. {
  349. const float flOsc = .28f * sinf( .5f * GetCurrentTime() );
  350. const Vector vCurPos(
  351. Lerp( m_flGlowFade, m_vSaxxyDefaultPos.x, m_vSaxxyDefaultPos.x - 8.0f ),
  352. m_vSaxxyDefaultPos.y,
  353. Lerp( m_flGlowFade, m_vSaxxyDefaultPos.z, m_vSaxxyDefaultPos.z - 3.0f ) + flOsc
  354. );
  355. m_angModelRot.y += flElapsed * 45 * 0.5f * Lerp( m_flGlowFade, 0.5f, 1.0f );
  356. m_pSaxxyModelPanel->SetModelAnglesAndPosition( m_angModelRot, vCurPos );
  357. }
  358. }
  359. void CSaxxyAwardsPanel::FlashThink( bool bOtherPanelsOpen )
  360. {
  361. if ( !m_pSaxxyModelPanel )
  362. return;
  363. // Set the target number of flashes based on how far the mouse is from the saxxy
  364. int aModelBounds[4];
  365. m_pSaxxyModelPanel->GetSize( aModelBounds[2], aModelBounds[3] );
  366. ipanel()->GetAbsPos( m_pSaxxyModelPanel->GetVPanel(), aModelBounds[0], aModelBounds[1] );
  367. const float flDistX = aModelBounds[0] + aModelBounds[2]/2.0f - m_aFilteredMousePos[0][0];
  368. const float flDistY = aModelBounds[1] + aModelBounds[3]/2.0f - m_aFilteredMousePos[0][1];
  369. const float flDistanceToSaxxy = sqrtf( flDistX * flDistX + flDistY * flDistY );
  370. // Msg( "xdist: %f ydist: %f distance: %f\n", flDistX, flDistY, flDistanceToSaxxy );
  371. const float flMaxDistance = 750.0f;
  372. m_flGlowFade = clamp( 1.0f - clamp( flDistanceToSaxxy / flMaxDistance, 0.0f, 1.0f ), 0.0f, 1.0f );
  373. // Msg( "glow fade: %f\n", m_flGlowFade );
  374. const float flCurTime = GetCurrentTime();
  375. // Set alpha for active flashes, kill any that are done animating, update positions of actives
  376. FOR_EACH_FLASH( i )
  377. {
  378. FlashInfo_t *pCurFlashInfo = &m_aFlashes[ i ];
  379. if ( !pCurFlashInfo->m_bInUse )
  380. continue;
  381. const float t = ( flCurTime - pCurFlashInfo->m_flStartTime ) / pCurFlashInfo->m_flLifeLength;
  382. if ( t >= 1.0f )
  383. {
  384. ClearFlash( pCurFlashInfo );
  385. }
  386. else
  387. {
  388. // Set opacity
  389. const float flPhase = M_PI * t;
  390. const float flFade = clamp( sinf( flPhase ), 0.0f, 1.0f );
  391. pCurFlashInfo->m_pPanel->SetAlpha( 255 * flFade );
  392. // Update size
  393. pCurFlashInfo->m_nCurW = (int)Lerp( flFade, pCurFlashInfo->m_nMinSize, pCurFlashInfo->m_nMaxSize );
  394. pCurFlashInfo->m_nCurH = pCurFlashInfo->m_nCurW;
  395. // Update position/size
  396. PlaceFlash( pCurFlashInfo );
  397. }
  398. }
  399. // Create new flashes / freak out
  400. const bool bEnoughTimeHasPassed = flCurTime >= m_flEarliestNextFlashTime;
  401. if ( !bOtherPanelsOpen && bEnoughTimeHasPassed && FlashingStartTimePassed() && GetUnusedFlashCount() )
  402. {
  403. CreateFlash();
  404. const bool bFreakout = InInitialFreakoutPeriod() || InFreakoutMode();
  405. const float flOffset = bFreakout ? FRand( .1f, .2f ) : FRand( 2.0f, 5.0f );
  406. m_flEarliestNextFlashTime = flCurTime + flOffset;
  407. }
  408. }
  409. VPANEL CSaxxyAwardsPanel::GetDialogsParent()
  410. {
  411. VPANEL vGameUI = enginevgui->GetPanel( PANEL_GAMEUIDLL );
  412. const int nNumChildren = ipanel()->GetChildCount( vGameUI );
  413. for ( int i = 0; i < nNumChildren; ++i )
  414. {
  415. VPANEL vChild = ipanel()->GetChild( vGameUI, i );
  416. const char *pDlgName = ipanel()->GetName( vChild );
  417. if ( FStrEq( pDlgName, "BaseGameUIPanel" ) )
  418. {
  419. return vChild;
  420. }
  421. }
  422. return (VPANEL)-1;
  423. }
  424. bool CSaxxyAwardsPanel::AreNonMainMenuPanelsOpen( VPANEL vRoot, const char **pCarePanels, int nNumCarePanels )
  425. {
  426. int nNumChildren = ipanel()->GetChildCount( vRoot );
  427. for ( int i = 0; i < nNumChildren; ++i )
  428. {
  429. VPANEL vChild = ipanel()->GetChild( vRoot, i );
  430. const char *pDlgName = ipanel()->GetName( vChild );
  431. bool bCare = false;
  432. for ( int j = 0; j < nNumCarePanels; ++j )
  433. {
  434. if ( FStrEq( pCarePanels[j], pDlgName ) )
  435. {
  436. // Msg( "CARE: %s\n", pDlgName );
  437. bCare = true;
  438. break;
  439. }
  440. }
  441. if ( !bCare )
  442. {
  443. // Msg( "DONT CARE: %s\n", pDlgName );
  444. continue;
  445. }
  446. if ( ipanel()->IsVisible( vChild ) )
  447. return true;
  448. }
  449. return false;
  450. }
  451. void CSaxxyAwardsPanel::SpotlightThink()
  452. {
  453. if ( !m_pSpotlightPanel )
  454. return;
  455. const float flOscillateAmount = 0.10f;
  456. const float flSpeed = 0.7f;
  457. const float flFade = .1f + flOscillateAmount * ( .5f + .5f * cosf( flSpeed * GetCurrentTime() ) );
  458. m_pSpotlightPanel->SetAlpha( 255 * flFade );
  459. }
  460. void CSaxxyAwardsPanel::PlaySomeClaps()
  461. {
  462. // Play each clap offset by a bit
  463. const float flCurTime = GetCurrentTime();
  464. FOR_EACH_CLAP( i )
  465. {
  466. // If this sound is already playing, continue
  467. float &flCurClapPlayTime = m_aClapPlayTimes[i];
  468. const bool bAlreadyPlaying = flCurClapPlayTime != 0.0f;
  469. if ( bAlreadyPlaying )
  470. continue;
  471. // Some chance that not all claps will be played
  472. if ( rand() % 100 < 30 )
  473. {
  474. SetNextPossibleClapTime( &flCurClapPlayTime );
  475. continue;
  476. }
  477. flCurClapPlayTime = flCurTime + LerpScale( FRand( 0.0f, 1.0f ), 0.0f, 1.0f, .3f, 1.5f );
  478. }
  479. }
  480. void CSaxxyAwardsPanel::ClapsThink( float flCurTime, float flElapsed, bool bOtherPanelsOpen )
  481. {
  482. if ( !bOtherPanelsOpen && InFreakoutMode() && !InInitialFreakoutPeriod() )
  483. {
  484. PlaySomeClaps();
  485. }
  486. FOR_EACH_CLAP( i )
  487. {
  488. float &flCurClapTime = m_aClapPlayTimes[i];
  489. // Msg( "clap %i: %f\n", i, flCurClapTime );
  490. if ( flCurClapTime < 0.0f )
  491. {
  492. // Once enough time has elapsed, this slot will be > 0 and available again.
  493. flCurClapTime = MIN( flCurClapTime + flElapsed, 0.0f );
  494. continue;
  495. }
  496. // Start playing now?
  497. if ( flCurClapTime <= flCurTime && flCurClapTime != 0.0f )
  498. {
  499. // Play.
  500. CFmtStr fmtSound( "misc/clap_single_%i.wav", i + 1 );
  501. surface()->PlaySound( fmtSound.Access() );
  502. SetNextPossibleClapTime( &flCurClapTime );
  503. }
  504. }
  505. }
  506. void CSaxxyAwardsPanel::SetNextPossibleClapTime( float *pClapTime )
  507. {
  508. // Set the next possible time to the duration of the sound + some randomn offset
  509. const float flExtraOffset = LerpScale( FRand( 0.0f, 1.0f ), 0.0f, 1.0f, 1.0f, 5.0f );
  510. *pClapTime = -( m_flClapSoundDuration + flExtraOffset );
  511. }
  512. void CSaxxyAwardsPanel::ClearFlash( FlashInfo_t *pFlashInfo )
  513. {
  514. pFlashInfo->m_bInUse = false;
  515. pFlashInfo->m_pPanel->SetAlpha( 0 );
  516. pFlashInfo->m_pPanel->SetVisible( false );
  517. }
  518. void CSaxxyAwardsPanel::ClearFlashes()
  519. {
  520. FOR_EACH_FLASH( i )
  521. {
  522. ClearFlash( &m_aFlashes[i] );
  523. }
  524. }
  525. int CSaxxyAwardsPanel::GetActiveFlashCount() const
  526. {
  527. int nResult = 0;
  528. FOR_EACH_FLASH( i )
  529. {
  530. if ( m_aFlashes[ i ].m_bInUse )
  531. ++nResult;
  532. }
  533. return nResult;
  534. }
  535. int CSaxxyAwardsPanel::GetUnusedFlashCount() const
  536. {
  537. return MAX_FLASHES - GetActiveFlashCount();
  538. }
  539. int CSaxxyAwardsPanel::GetUnusedFlashSlot() const
  540. {
  541. FOR_EACH_FLASH( i )
  542. {
  543. if ( !m_aFlashes[ i ].m_bInUse )
  544. return i;
  545. }
  546. return -1;
  547. }
  548. float CSaxxyAwardsPanel::GetCurrentTime() const
  549. {
  550. return gpGlobals->curtime;
  551. }
  552. bool CSaxxyAwardsPanel::InInitialFreakoutPeriod() const
  553. {
  554. return ( GetCurrentTime() - m_flShowTime ) <= m_flInitialFreakoutDuration;
  555. }
  556. bool CSaxxyAwardsPanel::InFreakoutMode() const
  557. {
  558. return m_flGlowFade > FREAKOUT_THRESHOLD;
  559. }
  560. bool CSaxxyAwardsPanel::CurtainsClosed() const
  561. {
  562. return m_flCurtainStartAnimTime < 0.0f;
  563. }
  564. bool CSaxxyAwardsPanel::FlashingStartTimePassed() const
  565. {
  566. return ( GetCurrentTime() - m_flShowTime ) >= m_flFlashStartTime;
  567. }
  568. void CSaxxyAwardsPanel::SetupContestPanels()
  569. {
  570. CRTime now;
  571. now.SetToCurrentTime();
  572. now.SetToGMT( true );
  573. CRTime deadline = CRTime::RTime32FromString( "2011-05-21 00:00:00" );
  574. deadline.SetToGMT( true );
  575. if ( now > deadline )
  576. {
  577. if ( m_pInfoLabel && m_pInfoLabel->IsVisible() )
  578. {
  579. m_pInfoLabel->SetVisible( false );
  580. }
  581. if ( m_pSubmitButton && m_pSubmitButton->IsVisible() )
  582. {
  583. m_pSubmitButton->SetVisible( false );
  584. }
  585. if ( m_pContestOverLabel && !m_pContestOverLabel->IsVisible() )
  586. {
  587. m_pContestOverLabel->SetVisible( true );
  588. }
  589. }
  590. }
  591. CSaxxyAwardsSubmitForm::CSaxxyAwardsSubmitForm( Panel *pParent )
  592. : BaseClass( pParent, "SaxxySubmitForm" ),
  593. m_pURLInput( NULL ),
  594. m_pCategoryCombo( NULL )
  595. {
  596. }
  597. void CSaxxyAwardsSubmitForm::ApplySchemeSettings( IScheme *pScheme )
  598. {
  599. // Link in TF scheme
  600. extern IEngineVGui *enginevgui;
  601. vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
  602. SetScheme( pTFScheme );
  603. SetProportional( true );
  604. BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
  605. LoadControlSettings( "resource/UI/SaxxyAwards_SubmitForm.res" );
  606. m_pURLInput = dynamic_cast< TextEntry * >( FindChildByName( "URLInput" ) );
  607. if ( m_pURLInput )
  608. {
  609. m_pURLInput->SetText( "#Replay_YouTubeURL" );
  610. }
  611. m_pCategoryCombo = dynamic_cast< ComboBox * >( FindChildByName( "CategoryCombo" ) );
  612. if ( m_pCategoryCombo )
  613. {
  614. // Add "Categories" text
  615. const int nDefaultItemID = m_pCategoryCombo->AddItem( "#Replay_Category", NULL );
  616. m_pCategoryCombo->ActivateItem( nDefaultItemID );
  617. // Add all categories
  618. const wchar_t *pCategoryText = L"";
  619. int i = 0;
  620. while ( true )
  621. {
  622. CFmtStr fmtCategoryToken( "#Replay_Contest_Category%i", i++ );
  623. pCategoryText = g_pVGuiLocalize->Find( fmtCategoryToken.Access() );
  624. if ( !pCategoryText || !pCategoryText[0] )
  625. break;
  626. m_pCategoryCombo->AddItem( pCategoryText, NULL );
  627. }
  628. m_pCategoryCombo->SetNumberOfEditLines( i );
  629. }
  630. }
  631. void CSaxxyAwardsSubmitForm::PerformLayout()
  632. {
  633. BaseClass::PerformLayout();
  634. }
  635. void CSaxxyAwardsSubmitForm::OnCommand( const char *pCommand )
  636. {
  637. if ( FStrEq( pCommand, "submit" ) )
  638. {
  639. // need a category
  640. if ( m_pCategoryCombo->GetActiveItem() == 0 )
  641. {
  642. ShowMessageBox( "#Replay_Contest_StatusTitle", "#Replay_Contest_ChooseCategory", "#GameUI_OK" );
  643. return;
  644. }
  645. // need a non-empty string
  646. char szText[256];
  647. m_pURLInput->GetText( szText, sizeof( szText ) );
  648. if ( Q_strlen( szText ) == 0 )
  649. {
  650. ShowMessageBox( "#Replay_Contest_StatusTitle", "#Replay_Contest_EnterURL", "#GameUI_OK" );
  651. return;
  652. }
  653. // waiting dialog
  654. ShowWaitingDialog( new CGenericWaitingDialog( this ), "#Replay_Contest_Waiting", true, true, 30.0f );
  655. // send the url
  656. GCSDK::CProtoBufMsg< CMsgReplaySubmitContestEntry > msg( k_EMsgGCReplay_SubmitContestEntry );
  657. msg.Body().set_youtube_url( szText );
  658. msg.Body().set_category( m_pCategoryCombo->GetActiveItem() - 1 );
  659. GCClientSystem()->GetGCClient()->BSendMessage( msg );
  660. // close the submit dialog
  661. Close();
  662. }
  663. else if ( FStrEq( pCommand, "cancel" ) )
  664. {
  665. Close();
  666. }
  667. else if ( FStrEq( pCommand, "rules" ) )
  668. {
  669. if ( steamapicontext && steamapicontext->SteamFriends() )
  670. {
  671. steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/saxxyawards/#rules" );
  672. }
  673. }
  674. else
  675. {
  676. BaseClass::OnCommand( pCommand );
  677. }
  678. }
  679. void CSaxxyAwardsSubmitForm::Close()
  680. {
  681. TFModalStack()->PopModal( this );
  682. MarkForDeletion();
  683. }
  684. void CSaxxyAwardsSubmitForm::OnKeyCodeTyped( vgui::KeyCode nCode )
  685. {
  686. if ( nCode == KEY_ESCAPE )
  687. {
  688. Close();
  689. }
  690. else
  691. {
  692. BaseClass::OnKeyCodeTyped( nCode );
  693. }
  694. }
  695. static void SaxxyAwards_ShowSubmitForm()
  696. {
  697. CSaxxyAwardsSubmitForm *pSubmitDialog = vgui::SETUP_PANEL( new CSaxxyAwardsSubmitForm( NULL ) );
  698. pSubmitDialog->SetVisible( true );
  699. pSubmitDialog->MakePopup();
  700. pSubmitDialog->MoveToFront();
  701. pSubmitDialog->SetKeyBoardInputEnabled( true );
  702. pSubmitDialog->SetMouseInputEnabled( true );
  703. TFModalStack()->PushModal( pSubmitDialog );
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose: Refresh the saxxy awards portion of the main menu
  707. //-----------------------------------------------------------------------------
  708. class CGC_Replay_SubmitContestEntryResponse_Status : public GCSDK::CGCClientJob
  709. {
  710. public:
  711. CGC_Replay_SubmitContestEntryResponse_Status( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  712. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  713. {
  714. GCSDK::CProtoBufMsg< CMsgReplaySubmitContestEntryResponse > msg( pNetPacket );
  715. CloseWaitingDialog();
  716. if ( msg.Body().success() )
  717. {
  718. ShowMessageBox( "#Replay_Contest_StatusTitle", "#Replay_Contest_StatusSuccess", "#GameUI_OK" );
  719. }
  720. else
  721. {
  722. ShowMessageBox( "#Replay_Contest_StatusTitle", "#Replay_Contest_StatusFailure", "#GameUI_OK" );
  723. }
  724. return true;
  725. }
  726. protected:
  727. };
  728. GC_REG_JOB( GCSDK::CGCClient, CGC_Replay_SubmitContestEntryResponse_Status, "CGC_Replay_SubmitContestEntryResponse_Status", k_EMsgGCReplay_SubmitContestEntryResponse, GCSDK::k_EServerTypeGCClient );
  729. #endif // #ifdef SAXXYMAINMENU_ENABLED