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.

580 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_matchmaking_panel.h"
  8. #include "ienginevgui.h"
  9. #include "c_tf_gamestats.h"
  10. #include "clientmode_tf.h"
  11. #include "tf_hud_mainmenuoverride.h"
  12. #include "vgui_int.h"
  13. #include "IGameUIFuncs.h" // for key bindings
  14. #include <vgui_controls/AnimationController.h>
  15. #include "vgui/IInput.h"
  16. #include "tf_gc_client.h"
  17. #include "tf_party.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include <tier0/memdbgon.h>
  20. void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  21. extern void ShowEconRequirementDialog( const char *pTitle, const char *pText, const char *pItemDefName );
  22. CMatchMakingPanel *GetMatchMakingPanel()
  23. {
  24. CMatchMakingPanel *pMatchMakingPanel = (CMatchMakingPanel*)gViewPortInterface->FindPanelByName( PANEL_MATCHMAKING );
  25. return pMatchMakingPanel;
  26. }
  27. //-----------------------------------------------------------------------------
  28. // Purpose:
  29. //-----------------------------------------------------------------------------
  30. CMatchMakingPanel::CMatchMakingPanel( IViewPort *pViewPort ) : EditablePanel( NULL, PANEL_MATCHMAKING ), m_iMMPanelKey( BUTTON_CODE_INVALID )
  31. {
  32. vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
  33. SetScheme(scheme);
  34. SetProportional( true );
  35. ListenForGameEvent( "gameui_hidden" );
  36. ListenForGameEvent( "client_beginconnect" );
  37. EditablePanel *m_pMainContainer = new EditablePanel( this, "MainContainer" );
  38. Assert( m_pMainContainer );
  39. m_pCompetitiveModeGroupPanel = new vgui::EditablePanel( m_pMainContainer, "CompetitiveModeGroupBox" );
  40. m_pModeLabel = new vgui::Label( m_pCompetitiveModeGroupPanel, "LadderLabel", "" );
  41. m_pModeComboBox = new vgui::ComboBox( m_pCompetitiveModeGroupPanel, "ModeComboBox", 3, false );
  42. m_pModeComboBox->AddActionSignalTarget( this );
  43. m_pStopSearchButton = new vgui::Button( m_pCompetitiveModeGroupPanel, "StopSearchButton", "" );
  44. m_pStopSearchButton->AddActionSignalTarget( this );
  45. m_pSearchButton = new vgui::Button( m_pCompetitiveModeGroupPanel, "SearchButton", "" );
  46. m_pSearchButton->AddActionSignalTarget( this );
  47. m_pSearchActiveGroupBox = new vgui::EditablePanel( m_pMainContainer, "SearchActiveGroupBox" );
  48. m_pSearchActiveTitleLabel = new vgui::Label( m_pSearchActiveGroupBox, "SearchActiveTitle", "" );
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose:
  52. //-----------------------------------------------------------------------------
  53. CMatchMakingPanel::~CMatchMakingPanel()
  54. {
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. //-----------------------------------------------------------------------------
  59. void CMatchMakingPanel::AttachToGameUI( void )
  60. {
  61. C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_open", "tf_matchmaking_panel" );
  62. if ( GetClientModeTFNormal()->GameUI() )
  63. {
  64. GetClientModeTFNormal()->GameUI()->SetMainMenuOverride( GetVPanel() );
  65. }
  66. SetKeyBoardInputEnabled( true );
  67. SetMouseInputEnabled( true );
  68. SetCursor(dc_arrow);
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose:
  72. //-----------------------------------------------------------------------------
  73. const char *CMatchMakingPanel::GetName( void )
  74. {
  75. return PANEL_MATCHMAKING;
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose:
  79. //-----------------------------------------------------------------------------
  80. void CMatchMakingPanel::ApplySchemeSettings( IScheme *pScheme )
  81. {
  82. BaseClass::ApplySchemeSettings( pScheme );
  83. LoadControlSettings( "Resource/UI/MatchMakingPanel.res" );
  84. // The outer dim / close button
  85. {
  86. Button *pButton = FindControl< Button >( "OutsideCloseButton" );
  87. if ( pButton )
  88. {
  89. pButton->AddActionSignalTarget( this );
  90. pButton->SetPaintBackgroundEnabled( false );
  91. }
  92. }
  93. if ( m_pModeComboBox && m_pModeComboBox->IsVisible() )
  94. {
  95. // Placeholder
  96. m_pModeComboBox->RemoveAll();
  97. vgui::HFont hFont = pScheme->GetFont( "HudFontSmallestBold", true );
  98. m_pModeComboBox->SetFont( hFont );
  99. KeyValues *pKeyValues = new KeyValues( "data" );
  100. pKeyValues->SetInt( "bracket", k_nMatchGroup_Ladder_6v6 );
  101. m_pModeComboBox->AddItem( "6v6", pKeyValues );
  102. pKeyValues->SetInt( "bracket", k_nMatchGroup_Ladder_9v9 );
  103. m_pModeComboBox->AddItem( "9v9", pKeyValues );
  104. pKeyValues->deleteThis();
  105. m_pModeComboBox->ActivateItemByRow( 0 );
  106. }
  107. if ( m_pSearchButton )
  108. {
  109. m_pSearchButton->AddActionSignalTarget( this );
  110. }
  111. if ( m_pStopSearchButton )
  112. {
  113. m_pStopSearchButton->AddActionSignalTarget( this );
  114. }
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose:
  118. //-----------------------------------------------------------------------------
  119. void CMatchMakingPanel::PerformLayout()
  120. {
  121. BaseClass::PerformLayout();
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose:
  125. //-----------------------------------------------------------------------------
  126. void CMatchMakingPanel::OnCommand( const char *pCommand )
  127. {
  128. if ( FStrEq( pCommand, "close" ) )
  129. {
  130. if ( enginevgui->IsGameUIVisible() )
  131. {
  132. ShowPanel( false );
  133. }
  134. else
  135. {
  136. IViewPortPanel *pMMPanel = ( gViewPortInterface->FindPanelByName( PANEL_MATCHMAKING ) );
  137. if ( pMMPanel )
  138. {
  139. gViewPortInterface->ShowPanel( pMMPanel, false );
  140. }
  141. }
  142. }
  143. else if ( FStrEq( pCommand, "search" ) )
  144. {
  145. StartSearch();
  146. }
  147. else if ( FStrEq( pCommand, "stopsearch" ) )
  148. {
  149. StopSearch();
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CMatchMakingPanel::OnTextChanged( KeyValues *data )
  156. {
  157. if ( !IsVisible() )
  158. return;
  159. if ( !GCClientSystem()->BConnectedtoGC() )
  160. return;
  161. Panel *pPanel = reinterpret_cast< vgui::Panel* >( data->GetPtr( "panel" ) );
  162. vgui::ComboBox *pComboBox = dynamic_cast< vgui::ComboBox* >( pPanel );
  163. if ( pComboBox )
  164. {
  165. if ( pComboBox == m_pModeComboBox )
  166. {
  167. KeyValues *pUserData = m_pModeComboBox->GetActiveItemUserData();
  168. if ( !pUserData )
  169. return;
  170. uint32 unModeType = pUserData->GetInt( "bracket", (uint32)-1 );
  171. if ( unModeType >= k_nMatchGroup_Ladder_First && unModeType <= k_nMatchGroup_Ladder_Last )
  172. {
  173. GTFGCClientSystem()->SetLadderType( unModeType );
  174. }
  175. // else if ( unModeType >= k_nMatchGroup_Quickplay_First && unModeType <= k_nMatchGroup_Quickplay_Last )
  176. // {
  177. // GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_QUICKPLAY );
  178. // }
  179. return;
  180. }
  181. }
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose:
  185. //-----------------------------------------------------------------------------
  186. void CMatchMakingPanel::FireGameEvent( IGameEvent *event )
  187. {
  188. if ( FStrEq( event->GetName(), "gameui_hidden" ) )
  189. {
  190. ShowPanel( false );
  191. return;
  192. }
  193. else if ( FStrEq( event->GetName(), "client_beginconnect" ) )
  194. {
  195. ShowPanel( false );
  196. return;
  197. }
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. //-----------------------------------------------------------------------------
  202. void CMatchMakingPanel::ShowPanel( bool bShow )
  203. {
  204. // Snag this so we know what to listen for
  205. m_iMMPanelKey = gameuifuncs->GetButtonCodeForBind( "show_matchmaking" );
  206. if ( bShow
  207. && ( !steamapicontext
  208. || !steamapicontext->SteamUtils()
  209. || !steamapicontext->SteamMatchmakingServers()
  210. || !steamapicontext->SteamUser()
  211. || !steamapicontext->SteamUser()->BLoggedOn() ) )
  212. {
  213. Warning( "Steam not properly initialized or connected.\n" );
  214. ShowMessageBox( "#TF_MM_GenericFailure_Title", "#TF_MM_GenericFailure", "#GameUI_OK" );
  215. ShowPanel( false );
  216. }
  217. if ( bShow && !GCClientSystem()->BConnectedtoGC() )
  218. {
  219. Warning( "Not connected to GC.\n" );
  220. ShowMessageBox( "#TF_MM_NoGC_Title", "#TF_MM_NoGC", "#GameUI_OK" );
  221. ShowPanel( false );
  222. }
  223. bool bInMMGame = GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_InGame &&
  224. GTFGCClientSystem()->GetAssignedMatchAbandonStatus() == k_EAbandonGameStatus_AbandonWithPenalty;
  225. bool bSearching = GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_InQueue;
  226. // DevMsg( "CMatchmakingPanel shown, bInMMGame %d bSearching %d\n", bInMMGame, bSearching );
  227. if ( m_pModeLabel )
  228. {
  229. m_pModeLabel->SetVisible( true );
  230. }
  231. if ( m_pModeComboBox )
  232. {
  233. m_pModeComboBox->SetEnabled( !bSearching && !bInMMGame );
  234. m_pModeComboBox->SetVisible( !bSearching );
  235. }
  236. if ( m_pSearchButton )
  237. {
  238. m_pSearchButton->SetEnabled( !bSearching && !bInMMGame );
  239. m_pSearchButton->SetVisible( !bSearching && !bInMMGame );
  240. }
  241. if ( m_pStopSearchButton )
  242. {
  243. m_pStopSearchButton->SetEnabled( bSearching && !bInMMGame );
  244. m_pStopSearchButton->SetVisible( bSearching && !bInMMGame );
  245. }
  246. if ( m_pSearchActiveGroupBox )
  247. {
  248. m_pSearchActiveGroupBox->SetVisible( bSearching && !bInMMGame );
  249. }
  250. if ( bShow )
  251. {
  252. KeyValues *pUserData = m_pModeComboBox->GetActiveItemUserData();
  253. if ( pUserData )
  254. {
  255. uint32 unModeType = pUserData->GetInt( "bracket", (uint32)-1 );
  256. if ( unModeType >= k_nMatchGroup_Ladder_First && unModeType <= k_nMatchGroup_Ladder_Last )
  257. {
  258. GTFGCClientSystem()->SetLadderType( unModeType );
  259. }
  260. }
  261. }
  262. SetVisible( bShow );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CMatchMakingPanel::OnKeyCodePressed( KeyCode code )
  268. {
  269. if ( code == m_iMMPanelKey )
  270. {
  271. ShowPanel( false );
  272. return;
  273. }
  274. BaseClass::OnKeyCodePressed( code );
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose:
  278. //-----------------------------------------------------------------------------
  279. void CMatchMakingPanel::OnKeyCodeTyped( KeyCode code )
  280. {
  281. if ( code == KEY_ESCAPE )
  282. {
  283. if ( IsVisible() )
  284. {
  285. SetVisible( false );
  286. }
  287. }
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. //-----------------------------------------------------------------------------
  292. void CMatchMakingPanel::OnThink()
  293. {
  294. if ( GTFGCClientSystem()->GetWizardStep() == TF_Matchmaking_WizardStep_SEARCHING )
  295. {
  296. const CMsgMatchmakingProgress &progress = GTFGCClientSystem()->m_msgMatchmakingProgress;
  297. wchar_t wszCount[32];
  298. CUtlVector< vgui::Label* > vecNearbyFields;
  299. vgui::Label *pNearbyColumnHead = dynamic_cast< vgui::Label* >( FindChildByName( "NearbyColumnHead", true ) );
  300. Assert( pNearbyColumnHead );
  301. vecNearbyFields.AddToTail( pNearbyColumnHead );
  302. #define DO_FIELD( protobufname, labelname, bNearby ) \
  303. vgui::Label *p##labelname = dynamic_cast< vgui::Label* >( FindChildByName( #labelname, true ) ); \
  304. Assert( p##labelname ); \
  305. if ( p##labelname ) \
  306. { \
  307. if ( progress.has_##protobufname() ) \
  308. { \
  309. _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", progress.protobufname() ); \
  310. p##labelname->SetText( wszCount ); \
  311. if ( bNearby ) bHasAnyNearbyData = true; \
  312. } \
  313. else \
  314. { \
  315. p##labelname->SetText( "#TF_Matchmaking_NoData" ); \
  316. } \
  317. if ( bNearby ) vecNearbyFields.AddToTail( p##labelname ); \
  318. }
  319. bool bHasAnyNearbyData = false;
  320. DO_FIELD( matching_worldwide_searching_players, PlayersSearchingMatchingWorldwideValue, false )
  321. DO_FIELD( matching_near_you_searching_players, PlayersSearchingMatchingNearbyValue, true )
  322. DO_FIELD( matching_worldwide_active_players, PlayersInGameMatchingWorldwideValue, false )
  323. DO_FIELD( matching_near_you_active_players, PlayersInGameMatchingNearbyValue, true )
  324. DO_FIELD( matching_worldwide_empty_gameservers, EmptyGameserversMatchingWorldwideValue, false )
  325. DO_FIELD( matching_near_you_empty_gameservers, EmptyGameserversMatchingNearbyValue, true )
  326. DO_FIELD( total_worldwide_searching_players, PlayersSearchingTotalWorldwideValue, false )
  327. DO_FIELD( total_near_you_searching_players, PlayersSearchingTotalNearbyValue, true )
  328. DO_FIELD( total_worldwide_active_players, PlayersInGameTotalWorldwideValue, false )
  329. DO_FIELD( total_near_you_active_players, PlayersInGameTotalNearbyValue, true )
  330. FOR_EACH_VEC( vecNearbyFields, i )
  331. {
  332. vecNearbyFields[i]->SetVisible( bHasAnyNearbyData );
  333. }
  334. // HOLY CHEESEBALL BUSY INDICATOR
  335. const wchar_t *pwszEllipses = &L"....."[4 - ( (unsigned)Plat_FloatTime() % 5U )];
  336. wchar_t wszLocalized[512];
  337. g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_Searching" ), 1, pwszEllipses );
  338. m_pSearchActiveTitleLabel->SetText( wszLocalized );
  339. }
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. void CMatchMakingPanel::SetVisible( bool bState )
  345. {
  346. if ( bState == true )
  347. {
  348. // IGameEvent *event = gameeventmanager->CreateEvent( "questlog_opened" );
  349. // if ( event )
  350. // {
  351. // gameeventmanager->FireEventClientSide( event );
  352. // }
  353. if ( enginevgui->IsGameUIVisible() )
  354. {
  355. AttachToGameUI();
  356. }
  357. else
  358. {
  359. ipanel()->SetParent( GetVPanel(), VGui_GetClientDLLRootPanel() );
  360. MakePopup( false, true );
  361. SetKeyBoardInputEnabled( true );
  362. SetMouseInputEnabled( true );
  363. MoveToFront();
  364. }
  365. engine->ClientCmd_Unrestricted( "gameui_preventescapetoshow\n" );
  366. vgui::surface()->PlaySound( "ui/panel_open.wav" );
  367. }
  368. else if ( IsVisible() )
  369. {
  370. // Detach from the GameUI when we hide
  371. IViewPortPanel *pMMOverride = gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE );
  372. if ( pMMOverride )
  373. {
  374. ((CHudMainMenuOverride*)pMMOverride)->AttachToGameUI();
  375. }
  376. engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" );
  377. vgui::surface()->PlaySound( "ui/panel_close.wav" );
  378. }
  379. BaseClass::SetVisible( bState );
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. //-----------------------------------------------------------------------------
  384. void CMatchMakingPanel::StartSearch( void )
  385. {
  386. #ifdef STAGING_ONLY
  387. GTFGCClientSystem()->EndMatchmaking();
  388. bool bCompetitive = GTFGCClientSystem()->GetWizardStep() == TF_Matchmaking_WizardStep_LADDER;
  389. // Solo
  390. CTFParty *pParty = GTFGCClientSystem()->GetParty();
  391. if ( !pParty || pParty->GetNumMembers() <= 1 )
  392. {
  393. if ( bCompetitive && !GTFGCClientSystem()->BHasCompetitiveAccess() )
  394. {
  395. ShowEconRequirementDialog( "#TF_Competitive_RequiresPass_Title", "#TF_Competitive_RequiresPass", CTFItemSchema::k_rchLadderPassItemDefName );
  396. return;
  397. }
  398. else if ( bCompetitive && !CheckCompetitiveConvars() )
  399. {
  400. ShowMessageBox( "#TF_Competitive_Convars_CantProceed_Title", "#TF_Competitive_Convars_CantProceed", "#GameUI_OK" );
  401. return;
  402. }
  403. }
  404. // Group
  405. else
  406. {
  407. wchar_t wszLocalized[512];
  408. char szLocalized[512];
  409. wchar_t wszCharPlayerName[128];
  410. bool bAnyMembersWithoutAuth = false;
  411. for ( int i = 0; i < pParty->GetNumMembers(); ++i )
  412. {
  413. if ( bCompetitive )
  414. {
  415. if ( !pParty->Obj().members( i ).competitive_access() )
  416. {
  417. bAnyMembersWithoutAuth = true;
  418. V_UTF8ToUnicode( steamapicontext->SteamFriends()->GetFriendPersonaName( pParty->GetMember( i ) ), wszCharPlayerName, sizeof( wszCharPlayerName ) );
  419. g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_MissingPass" ), 1, wszCharPlayerName );
  420. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof( szLocalized ) );
  421. GTFGCClientSystem()->SendSteamLobbyChat( CTFGCClientSystem::k_eLobbyMsg_SystemMsgFromLeader, szLocalized );
  422. }
  423. }
  424. }
  425. if ( bAnyMembersWithoutAuth )
  426. {
  427. ShowMessageBox( "#TF_Competitive_RequiresPass_Title", "#TF_Competitive_RequiresPass", "#GameUI_OK" );
  428. return;
  429. }
  430. }
  431. {
  432. KeyValues *pUserData = m_pModeComboBox->GetActiveItemUserData();
  433. if ( !pUserData )
  434. return;
  435. uint32 unModeType = pUserData->GetInt( "bracket", (uint32)-1 );
  436. if ( unModeType >= k_nMatchGroup_Ladder_First && unModeType <= k_nMatchGroup_Ladder_Last )
  437. {
  438. GTFGCClientSystem()->BeginMatchmaking( TF_Matchmaking_LADDER );
  439. }
  440. else if ( unModeType >= k_nMatchGroup_Quickplay_First && unModeType <= k_nMatchGroup_Quickplay_Last )
  441. {
  442. // No longer support quickplay
  443. Assert( false );
  444. }
  445. }
  446. GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_SEARCHING );
  447. if ( m_pModeLabel )
  448. {
  449. m_pModeLabel->SetVisible( false );
  450. }
  451. if ( m_pModeComboBox )
  452. {
  453. m_pModeComboBox->SetEnabled( false );
  454. m_pModeComboBox->SetVisible( false );
  455. }
  456. if ( m_pSearchButton )
  457. {
  458. m_pSearchButton->SetEnabled( false );
  459. m_pSearchButton->SetVisible( false );
  460. }
  461. if ( m_pStopSearchButton )
  462. {
  463. m_pStopSearchButton->SetEnabled( true );
  464. m_pStopSearchButton->SetVisible( true );
  465. }
  466. if ( m_pSearchActiveGroupBox )
  467. {
  468. m_pSearchActiveGroupBox->SetVisible( true );
  469. }
  470. #endif // STAGING_ONLY
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose:
  474. //-----------------------------------------------------------------------------
  475. void CMatchMakingPanel::StopSearch( void )
  476. {
  477. if ( m_pModeLabel )
  478. {
  479. m_pModeLabel->SetVisible( true );
  480. }
  481. if ( m_pModeComboBox )
  482. {
  483. m_pModeComboBox->SetEnabled( true );
  484. m_pModeComboBox->SetVisible( true );
  485. }
  486. if ( m_pSearchButton )
  487. {
  488. m_pSearchButton->SetEnabled( true );
  489. m_pSearchButton->SetVisible( true );
  490. }
  491. if ( m_pStopSearchButton )
  492. {
  493. m_pStopSearchButton->SetEnabled( false );
  494. m_pStopSearchButton->SetVisible( false );
  495. }
  496. if ( m_pSearchActiveGroupBox )
  497. {
  498. m_pSearchActiveGroupBox->SetVisible( false );
  499. }
  500. GTFGCClientSystem()->EndMatchmaking();
  501. }
  502. #ifdef STAGING_ONLY
  503. // Just VGUI things...
  504. static void cc_tf_mm_panel_reload()
  505. {
  506. CMatchMakingPanel *pPanel = GetMatchMakingPanel();
  507. if ( pPanel )
  508. {
  509. pPanel->InvalidateLayout( true, true );
  510. gViewPortInterface->ShowPanel( pPanel, true );
  511. }
  512. }
  513. ConCommand tf_mm_panel_reload( "tf_mm_panel_reload", cc_tf_mm_panel_reload );
  514. #endif