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.

826 lines
27 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_party.h"
  8. #include "vgui_controls/PropertySheet.h"
  9. #include "vgui_controls/ScrollableEditablePanel.h"
  10. #include "tf_lobbypanel_comp.h"
  11. #include "tf_lobby_container_frame_comp.h"
  12. #include "vgui/ISystem.h"
  13. #include "tf_streams.h"
  14. #include "tf_badge_panel.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include <tier0/memdbgon.h>
  17. ConVar tf_mm_ladder_ui_last_rating_change( "tf_mm_ladder_ui_last_rating_change", "0", FCVAR_HIDDEN | FCVAR_ARCHIVE, "Track last match skillrating change for UI." );
  18. ConVar tf_mm_ladder_ui_last_rating_time( "tf_mm_ladder_ui_last_rating_time", "-1", FCVAR_HIDDEN | FCVAR_ARCHIVE, "Track last match skillrating change time for UI." );
  19. class CLobbyPanel_Comp;
  20. #include "iclientmode.h"
  21. #include <vgui_controls/AnimationController.h>
  22. class CMatchHistoryEntryPanel : public CExpandablePanel
  23. {
  24. public:
  25. DECLARE_CLASS_SIMPLE( CMatchHistoryEntryPanel, CExpandablePanel );
  26. CMatchHistoryEntryPanel( Panel* pParent, const char *pszPanelname )
  27. : BaseClass( pParent, pszPanelname )
  28. {}
  29. virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE
  30. {
  31. BaseClass::ApplySchemeSettings( pScheme );
  32. LoadControlSettings( "resource/ui/MatchHistoryEntryPanel.res" );
  33. }
  34. void SetMatchData( const CSOTFMatchResultPlayerStats& stats )
  35. {
  36. EditablePanel* pContainer = FindControl< EditablePanel >( "Container" );
  37. if ( !pContainer )
  38. return;
  39. // Match date
  40. CRTime matchdate( stats.endtime() );
  41. char rtime_buf[k_RTimeRenderBufferSize];
  42. matchdate.Render( rtime_buf );
  43. pContainer->SetDialogVariable( "match_date", rtime_buf );
  44. // Map name
  45. const MapDef_t* pMapDef = GetItemSchema()->GetMasterMapDefByIndex( stats.map_index() );
  46. const char* pszMapToken = "#TF_Map_Unknown";
  47. if ( pMapDef )
  48. {
  49. pszMapToken = pMapDef->pszMapNameLocKey;
  50. }
  51. pContainer->SetDialogVariable( "map_name", g_pVGuiLocalize->Find( pszMapToken ) );
  52. // KD ratio
  53. float flKDRatio = stats.kills();
  54. if ( stats.deaths() > 0 )
  55. {
  56. flKDRatio /= (float)stats.deaths();
  57. }
  58. pContainer->SetDialogVariable( "kd_ratio", CFmtStr( "%.1f", flKDRatio ) );
  59. pContainer->SetControlVisible( "WinLabel", stats.display_rating_change() > 0 );
  60. pContainer->SetControlVisible( "LossLabel", stats.display_rating_change() < 0 );
  61. EditablePanel* pStatsContainer = FindControl< EditablePanel >( "SlidingStatsContainer", true );
  62. if ( pStatsContainer )
  63. {
  64. pStatsContainer->SetDialogVariable( "stat_kills", LocalizeNumberWithToken( "TF_Competitive_Kills", stats.kills() ) );
  65. pStatsContainer->SetDialogVariable( "stat_deaths", LocalizeNumberWithToken( "TF_Competitive_Deaths", stats.deaths() ) );
  66. pStatsContainer->SetDialogVariable( "stat_damage", LocalizeNumberWithToken( "TF_Competitive_Damage", stats.damage() ) );
  67. pStatsContainer->SetDialogVariable( "stat_healing", LocalizeNumberWithToken( "TF_Competitive_Healing", stats.healing() ) );
  68. pStatsContainer->SetDialogVariable( "stat_support", LocalizeNumberWithToken( "TF_Competitive_Support", stats.support() ) );
  69. pStatsContainer->SetDialogVariable( "stat_score", LocalizeNumberWithToken( "TF_Competitive_Score", stats.score() ) );
  70. ScalableImagePanel* pMapImage = pStatsContainer->FindControl< ScalableImagePanel >( "BGImage", true );
  71. if ( pMapImage && pMapDef )
  72. {
  73. char imagename[ 512 ];
  74. Q_snprintf( imagename, sizeof( imagename ), "..\\vgui\\maps\\menu_thumb_%s", pMapDef->pszMapName );
  75. pMapImage->SetImage( imagename );
  76. }
  77. // Lambdas, wherefore art thou...
  78. SetupClassIcon( pStatsContainer, "scout", TF_CLASS_SCOUT, stats );
  79. SetupClassIcon( pStatsContainer, "soldier", TF_CLASS_SOLDIER, stats );
  80. SetupClassIcon( pStatsContainer, "pyro", TF_CLASS_PYRO, stats );
  81. SetupClassIcon( pStatsContainer, "demo", TF_CLASS_DEMOMAN, stats );
  82. SetupClassIcon( pStatsContainer, "heavy", TF_CLASS_HEAVYWEAPONS, stats );
  83. SetupClassIcon( pStatsContainer, "engineer", TF_CLASS_ENGINEER, stats );
  84. SetupClassIcon( pStatsContainer, "medic", TF_CLASS_MEDIC, stats );
  85. SetupClassIcon( pStatsContainer, "sniper", TF_CLASS_SNIPER, stats );
  86. SetupClassIcon( pStatsContainer, "spy", TF_CLASS_SPY, stats );
  87. SetupMedalForStat( pStatsContainer, stats.kills_medal(), "kills" );
  88. SetupMedalForStat( pStatsContainer, stats.damage_medal(), "damage" );
  89. SetupMedalForStat( pStatsContainer, stats.healing_medal(), "healing" );
  90. SetupMedalForStat( pStatsContainer, stats.support_medal(), "support" );
  91. SetupMedalForStat( pStatsContainer, stats.score_medal(), "score" );
  92. }
  93. }
  94. private:
  95. void SetupMedalForStat( EditablePanel* pParent, int nStat, const char* pszStatName )
  96. {
  97. ScalableImagePanel* pMedalImage = pParent->FindControl< ScalableImagePanel >( CFmtStr( "%sMedal", pszStatName ) );
  98. if ( pMedalImage )
  99. {
  100. if ( nStat != StatMedal_None )
  101. {
  102. pMedalImage->SetImage( g_pszCompetitiveMedalImages[ nStat ] );
  103. }
  104. pMedalImage->SetVisible( nStat != StatMedal_None );
  105. }
  106. }
  107. void SetupClassIcon( EditablePanel* pParent, const char* pszClassName, int nBit, const CSOTFMatchResultPlayerStats& stats )
  108. {
  109. ScalableImagePanel* pClassIcon = pParent->FindControl< ScalableImagePanel >( CFmtStr( "%sIcon", pszClassName ), true );
  110. if( pClassIcon )
  111. {
  112. pClassIcon->SetImage( stats.classes_played() & (1<<nBit) ? CFmtStr( "class_icons/filter_%s_on", pszClassName ) : CFmtStr( "class_icons/filter_%s", pszClassName ) );
  113. }
  114. }
  115. };
  116. DECLARE_BUILD_FACTORY( CMatchHistoryEntryPanel );
  117. extern Color s_colorChallengeHeader;
  118. DECLARE_BUILD_FACTORY( CLadderLobbyLeaderboard );
  119. //-----------------------------------------------------------------------------
  120. // Purpose:
  121. //-----------------------------------------------------------------------------
  122. CLadderLobbyLeaderboard::CLadderLobbyLeaderboard( Panel *pParent, const char *pszPanelName )
  123. : CTFLeaderboardPanel( pParent, pszPanelName )
  124. {
  125. m_pScoreList = new vgui::EditablePanel( this, "ScoreList" );
  126. m_pScoreListScroller = new vgui::ScrollableEditablePanel( this, m_pScoreList, "ScoreListScroller" );
  127. m_pScoreListScroller->AddActionSignalTarget( this );
  128. m_pszLeaderboardName = "tf2_ladder_6v6";
  129. for ( int i = 0; i < 100; ++i )
  130. {
  131. vgui::EditablePanel *pEntryUI = new vgui::EditablePanel( m_pScoreList, "LeaderboardEntry" );
  132. m_vecLeaderboardEntries.AddToTail( pEntryUI );
  133. }
  134. m_pToolTip = new CTFTextToolTip( this );
  135. m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" );
  136. m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false );
  137. m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false );
  138. m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel );
  139. m_pToolTip->SetTooltipDelay( 0 );
  140. m_bIsDataValid = false;
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose:
  144. //-----------------------------------------------------------------------------
  145. void CLadderLobbyLeaderboard::ApplySchemeSettings( vgui::IScheme *pScheme )
  146. {
  147. BaseClass::ApplySchemeSettings( pScheme );
  148. LoadControlSettings( "Resource/UI/econ/LobbyLeaderboard.res" );
  149. FOR_EACH_VEC( m_vecLeaderboardEntries, i )
  150. {
  151. m_vecLeaderboardEntries[i]->ApplySchemeSettings( pScheme );
  152. m_vecLeaderboardEntries[i]->LoadControlSettings( "Resource/UI/LeaderboardEntryRank.res" );
  153. }
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void CLadderLobbyLeaderboard::PerformLayout()
  159. {
  160. BaseClass::PerformLayout();
  161. UpdateLeaderboards();
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. //-----------------------------------------------------------------------------
  166. void CLadderLobbyLeaderboard::OnCommand( const char *command )
  167. {
  168. if ( Q_strnicmp( command, "stream", 6 ) == 0 )
  169. {
  170. vgui::system()->ShellExecute( "open", command + 7 );
  171. return;
  172. }
  173. else if ( FStrEq( "global", command ) )
  174. {
  175. if ( m_bGlobal != true )
  176. {
  177. m_bGlobal = true;
  178. UpdateLeaderboards();
  179. }
  180. return;
  181. }
  182. else if ( FStrEq( "local", command ) )
  183. {
  184. if ( m_bGlobal == true )
  185. {
  186. m_bGlobal = false;
  187. UpdateLeaderboards();
  188. }
  189. }
  190. BaseClass::OnCommand( command );
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose:
  194. //-----------------------------------------------------------------------------
  195. bool CLadderLobbyLeaderboard::GetLeaderboardData( CUtlVector< LeaderboardEntry_t* >& scores )
  196. {
  197. CUtlVector< LeaderboardEntry_t* > vecLadderScores;
  198. if ( Leaderboards_GetLadderLeaderboard( vecLadderScores, m_pszLeaderboardName, m_bGlobal ) )
  199. {
  200. scores.AddVectorToTail( vecLadderScores );
  201. return true;
  202. }
  203. return false;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose:
  207. //-----------------------------------------------------------------------------
  208. bool CLadderLobbyLeaderboard::UpdateLeaderboards()
  209. {
  210. CUtlVector< LeaderboardEntry_t* > scores;
  211. m_bIsDataValid = GetLeaderboardData( scores );
  212. if ( !m_bIsDataValid )
  213. return false;
  214. int nScoreListHeight = scores.Count() * m_yEntryStep;
  215. int nScrollerWidth, nScrollerHeight;
  216. m_pScoreListScroller->GetSize( nScrollerWidth, nScrollerHeight );
  217. m_pScoreList->SetSize( nScrollerWidth, Max( nScoreListHeight, nScrollerHeight ) );
  218. m_pScoreList->InvalidateLayout( true );
  219. m_pScoreListScroller->InvalidateLayout( true );
  220. m_pScoreListScroller->GetScrollbar()->InvalidateLayout( true );
  221. static int nScrollWidth = m_pScoreListScroller->GetScrollbar()->GetWide();
  222. m_pScoreListScroller->GetScrollbar()->SetWide( nScrollWidth>>1 );
  223. if ( m_pScoreListScroller->GetScrollbar()->GetButton( 0 ) &&
  224. m_pScoreListScroller->GetScrollbar()->GetButton( 1 ) )
  225. {
  226. m_pScoreListScroller->GetScrollbar()->GetButton( 0 )->SetVisible( false );
  227. m_pScoreListScroller->GetScrollbar()->GetButton( 1 )->SetVisible( false );
  228. }
  229. FOR_EACH_VEC( m_vecLeaderboardEntries, i )
  230. {
  231. EditablePanel *pContainer = dynamic_cast< EditablePanel* >( m_vecLeaderboardEntries[i] );
  232. if ( !pContainer )
  233. continue;
  234. Color colorToUse = i % 2 == 1 ? m_OddTextColor : m_EvenTextColor;
  235. bool bIsEntryVisible = i < scores.Count();
  236. pContainer->SetVisible( bIsEntryVisible );
  237. pContainer->SetPos( 0, i * m_yEntryStep );
  238. if ( bIsEntryVisible )
  239. {
  240. const LeaderboardEntry_t *pLeaderboardEntry = scores[i];
  241. const CSteamID &steamID = pLeaderboardEntry->m_steamIDUser;
  242. #ifdef TWITCH_LEADERBOARD
  243. TwitchTvAccountInfo_t *pTwitchInfo = StreamManager()->GetTwitchTvAccountInfo( steamID.ConvertToUint64() );
  244. ETwitchTvState_t twitchState = pTwitchInfo ? pTwitchInfo->m_eTwitchTvState : k_ETwitchTvState_Error;
  245. // still waiting for twitch info to load
  246. if ( twitchState <= k_ETwitchTvState_Loading )
  247. return false;
  248. #endif // TWITCH_LEADERBOARD
  249. bool bIsLocalPlayer = steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamUser()->GetSteamID() == steamID;
  250. pContainer->SetDialogVariable( "position", m_bGlobal ? CFmtStr( "%d.", pLeaderboardEntry->m_nGlobalRank ) : "" );
  251. pContainer->SetDialogVariable( "username", InventoryManager()->PersonaName_Get( steamID.GetAccountID() ) );
  252. CExLabel *pNameText = dynamic_cast< CExLabel* >( pContainer->FindChildByName( "UserName" ) );
  253. if ( pNameText )
  254. {
  255. pNameText->SetColorStr( bIsLocalPlayer ? m_LocalPlayerTextColor : colorToUse );
  256. }
  257. CAvatarImagePanel *pAvatar = dynamic_cast< CAvatarImagePanel* >( pContainer->FindChildByName( "AvatarImage" ) );
  258. if ( pAvatar )
  259. {
  260. pAvatar->SetShouldDrawFriendIcon( false );
  261. pAvatar->SetPlayer( steamID, k_EAvatarSize32x32 );
  262. }
  263. CTFBadgePanel *pRankImage = dynamic_cast< CTFBadgePanel* >( pContainer->FindChildByName( "RankImage" ) );
  264. if ( pRankImage )
  265. {
  266. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( k_nMatchGroup_Ladder_6v6 );
  267. const LevelInfo_t& levelInfo = pMatchDesc->m_pProgressionDesc->GetLevelForExperience( pLeaderboardEntry->m_nScore );
  268. pRankImage->SetupBadge( pMatchDesc->m_pProgressionDesc, levelInfo );
  269. wchar_t wszOutString[ 128 ];
  270. char szLocalized[512];
  271. wchar_t wszCount[ 16 ];
  272. _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", levelInfo.m_nLevelNum );
  273. const wchar_t *wpszFormat = g_pVGuiLocalize->Find( pMatchDesc->m_pProgressionDesc->m_pszLevelToken );
  274. g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 2, wszCount, g_pVGuiLocalize->Find( levelInfo.m_pszLevelTitle ) );
  275. g_pVGuiLocalize->ConvertUnicodeToANSI( wszOutString, szLocalized, sizeof( szLocalized ) );
  276. pRankImage->SetMouseInputEnabled( true );
  277. pRankImage->SetVisible( true );
  278. pRankImage->SetTooltip( m_pToolTip, szLocalized );
  279. }
  280. CExImageButton *pStreamImage = dynamic_cast< CExImageButton* >( pContainer->FindChildByName( "StreamImageButton" ) );
  281. if ( pStreamImage )
  282. {
  283. #ifdef TWITCH_LEADERBOARD
  284. if ( twitchState == k_ETwitchTvState_Linked )
  285. {
  286. pStreamImage->SetVisible( true );
  287. pStreamImage->SetCommand( CFmtStr( "stream %s", pTwitchInfo->m_sTwitchTvChannel.String() ) );
  288. }
  289. else
  290. #endif // TWITCH_LEADERBOARD
  291. {
  292. pStreamImage->SetVisible( false );
  293. pStreamImage->SetCommand( "" );
  294. }
  295. }
  296. }
  297. }
  298. return true;
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose:
  302. //-----------------------------------------------------------------------------
  303. void CLadderLobbyLeaderboard::SetLeaderboard( const char *pszLeaderboardName, bool bGlobal )
  304. {
  305. m_pszLeaderboardName = pszLeaderboardName;
  306. m_bGlobal = bGlobal;
  307. UpdateLeaderboards();
  308. }
  309. static void GetPlayerNameForSteamID( wchar_t *wCharPlayerName, int nBufSizeBytes, const CSteamID &steamID )
  310. {
  311. const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
  312. V_UTF8ToUnicode( pszName, wCharPlayerName, nBufSizeBytes );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. CLobbyPanel_Comp::CLobbyPanel_Comp( vgui::Panel *pParent, CBaseLobbyContainerFrame* pLobbyContainer )
  318. : CBaseLobbyPanel( pParent, pLobbyContainer )
  319. , m_pCompetitiveModeLeaderboard( NULL )
  320. , m_pMatchHistoryScroller( NULL )
  321. , m_eMatchSortMethod( SORT_BY_DATE )
  322. , m_bDescendingMatchHistorySort( true )
  323. {
  324. // Comp
  325. m_fontMedalsCount = 0;
  326. m_flCompetitiveRankProgress = -1.f;
  327. m_flCompetitiveRankPrevProgress = -1.f;
  328. m_flRefreshPlayerListTime = -1.f;
  329. m_bCompetitiveRankChangePlayedSound = false;
  330. m_bMatchHistoryLoaded = false;
  331. m_bMatchDataForLocalPlayerDirty = true;
  332. ListenForGameEvent( "gc_new_session" );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. //-----------------------------------------------------------------------------
  337. CLobbyPanel_Comp::~CLobbyPanel_Comp()
  338. {
  339. delete m_pImageList;
  340. m_pImageList = NULL;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. //-----------------------------------------------------------------------------
  345. void CLobbyPanel_Comp::OnCommand( const char *command )
  346. {
  347. if ( FStrEq( command, "medals_help" ) )
  348. {
  349. CExplanationPopup *pPopup = dynamic_cast< CExplanationPopup* >( GetParent()->FindChildByName( "MedalsHelp" ) );
  350. if ( pPopup )
  351. {
  352. pPopup->Popup();
  353. }
  354. return;
  355. }
  356. else if ( FStrEq( command, "show_leaderboards" ) )
  357. {
  358. if ( m_pCompetitiveModeLeaderboard )
  359. {
  360. m_pCompetitiveModeLeaderboard->SetVisible( true );
  361. }
  362. SetControlVisible( "MatchHistoryCategories", false, true );
  363. SetControlVisible( "MatchHistoryContainer", false, true );
  364. return;
  365. }
  366. else if ( FStrEq( command, "show_match_history" ) )
  367. {
  368. m_bMatchDataForLocalPlayerDirty = true;
  369. if ( m_pCompetitiveModeLeaderboard )
  370. {
  371. m_pCompetitiveModeLeaderboard->SetVisible( false );
  372. }
  373. SetControlVisible( "MatchHistoryCategories", true, true );
  374. SetControlVisible( "MatchHistoryContainer", true, true );
  375. return;
  376. }
  377. else if ( !Q_strncmp( "sort", command, 4 ) )
  378. {
  379. EMatchHistorySortMethods_t eNewMethod = (EMatchHistorySortMethods_t)atoi( command + 4 );
  380. if ( eNewMethod == m_eMatchSortMethod )
  381. {
  382. m_bDescendingMatchHistorySort = !m_bDescendingMatchHistorySort;
  383. }
  384. else
  385. {
  386. m_eMatchSortMethod = eNewMethod;
  387. m_bDescendingMatchHistorySort = true;
  388. }
  389. m_bMatchDataForLocalPlayerDirty = true;
  390. return;
  391. }
  392. BaseClass::OnCommand( command );
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose:
  396. //-----------------------------------------------------------------------------
  397. bool CLobbyPanel_Comp::ShouldShowLateJoin() const
  398. {
  399. return false; // For now
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose:
  403. //-----------------------------------------------------------------------------
  404. void CLobbyPanel_Comp::ApplyChatUserSettings( const CBaseLobbyPanel::LobbyPlayerInfo &player, KeyValues *pKV ) const
  405. {
  406. pKV->SetInt( "has_ticket", 0 );
  407. pKV->SetInt( "squad_surplus", 0 );
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose:
  411. //-----------------------------------------------------------------------------
  412. void CLobbyPanel_Comp::WriteGameSettingsControls()
  413. {
  414. BaseClass::WriteGameSettingsControls();
  415. // Make sure we want to be in matchmaking. (If we don't, the frame should hide us pretty quickly.)
  416. // We might get an event or something right at the transition point occasionally when the UI should
  417. // not be visible
  418. if ( GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_Inactive )
  419. {
  420. return;
  421. }
  422. ++m_iWritingPanel;
  423. m_pContainer->SetNextButtonEnabled( true );
  424. SetControlVisible( "ScrollableContainer", GTFGCClientSystem()->GetWizardStep()== TF_Matchmaking_WizardStep_LADDER );
  425. --m_iWritingPanel;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose:
  429. //-----------------------------------------------------------------------------
  430. int CLobbyPanel_Comp::GetMedalCountForStat( EMatchGroup unLadderType, RankStatType_t nStatType, int nMedalLevel )
  431. {
  432. CSOTFLadderData *pData = GetLocalPlayerLadderData( unLadderType );
  433. if ( !pData )
  434. return 0;
  435. switch ( nStatType )
  436. {
  437. case RankStat_Score:
  438. if ( nMedalLevel == StatMedal_Bronze )
  439. {
  440. return pData->Obj().score_bronze();
  441. }
  442. else if ( nMedalLevel == StatMedal_Silver )
  443. {
  444. return pData->Obj().score_silver();
  445. }
  446. else if ( nMedalLevel == StatMedal_Gold )
  447. {
  448. return pData->Obj().score_gold();
  449. }
  450. break;
  451. case RankStat_Kills:
  452. if ( nMedalLevel == StatMedal_Bronze )
  453. {
  454. return pData->Obj().kills_bronze();
  455. }
  456. else if ( nMedalLevel == StatMedal_Silver )
  457. {
  458. return pData->Obj().kills_silver();
  459. }
  460. else if ( nMedalLevel == StatMedal_Gold )
  461. {
  462. return pData->Obj().kills_gold();
  463. }
  464. break;
  465. case RankStat_Damage:
  466. if ( nMedalLevel == StatMedal_Bronze )
  467. {
  468. return pData->Obj().damage_bronze();
  469. }
  470. else if ( nMedalLevel == StatMedal_Silver )
  471. {
  472. return pData->Obj().damage_silver();
  473. }
  474. else if ( nMedalLevel == StatMedal_Gold )
  475. {
  476. return pData->Obj().damage_gold();
  477. }
  478. break;
  479. case RankStat_Healing:
  480. if ( nMedalLevel == StatMedal_Bronze )
  481. {
  482. return pData->Obj().healing_bronze();
  483. }
  484. else if ( nMedalLevel == StatMedal_Silver )
  485. {
  486. return pData->Obj().healing_silver();
  487. }
  488. else if ( nMedalLevel == StatMedal_Gold )
  489. {
  490. return pData->Obj().healing_gold();
  491. }
  492. break;
  493. case RankStat_Support:
  494. if ( nMedalLevel == StatMedal_Bronze )
  495. {
  496. return pData->Obj().support_bronze();
  497. }
  498. else if ( nMedalLevel == StatMedal_Silver )
  499. {
  500. return pData->Obj().support_silver();
  501. }
  502. else if ( nMedalLevel == StatMedal_Gold )
  503. {
  504. return pData->Obj().support_gold();
  505. }
  506. break;
  507. case RankStat_Deaths:
  508. // Not supported
  509. break;
  510. default:
  511. Assert( 0 );
  512. }
  513. return 0;
  514. }
  515. //-----------------------------------------------------------------------------
  516. // Purpose:
  517. //-----------------------------------------------------------------------------
  518. void CLobbyPanel_Comp::OnThink()
  519. {
  520. BaseClass::OnThink();
  521. if ( m_bMatchDataForLocalPlayerDirty )
  522. {
  523. UpdateMatchDataForLocalPlayer();
  524. }
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Purpose:
  528. //-----------------------------------------------------------------------------
  529. void CLobbyPanel_Comp::FireGameEvent( IGameEvent *event )
  530. {
  531. const char *pszEventname = event->GetName();
  532. if ( !Q_stricmp( pszEventname, "gc_new_session" ) )
  533. {
  534. // This is loaded on demand by the GC - if we have a new session, we need to re-request
  535. if ( m_bMatchHistoryLoaded )
  536. {
  537. m_bMatchHistoryLoaded = false;
  538. m_bMatchDataForLocalPlayerDirty = true;
  539. }
  540. }
  541. BaseClass::FireGameEvent( event );
  542. }
  543. //-----------------------------------------------------------------------------
  544. // Purpose:
  545. //-----------------------------------------------------------------------------
  546. EMatchGroup CLobbyPanel_Comp::GetMatchGroup( void ) const
  547. {
  548. return k_nMatchGroup_Ladder_6v6;
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose:
  552. //-----------------------------------------------------------------------------
  553. void CLobbyPanel_Comp::SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent )
  554. {
  555. if ( pObject->GetTypeID() != CSOTFMatchResultPlayerInfo::k_nTypeID )
  556. return;
  557. m_bMatchDataForLocalPlayerDirty = true;
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Purpose:
  561. //-----------------------------------------------------------------------------
  562. void CLobbyPanel_Comp::SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent )
  563. {
  564. if ( pObject->GetTypeID() != CSOTFMatchResultPlayerInfo::k_nTypeID )
  565. return;
  566. m_bMatchDataForLocalPlayerDirty = true;
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose:
  570. //-----------------------------------------------------------------------------
  571. void CLobbyPanel_Comp::PerformLayout()
  572. {
  573. BaseClass::PerformLayout();
  574. m_bMatchDataForLocalPlayerDirty = true;
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose:
  578. //-----------------------------------------------------------------------------
  579. void CLobbyPanel_Comp::ApplySchemeSettings( vgui::IScheme *pScheme )
  580. {
  581. BaseClass::ApplySchemeSettings( pScheme );
  582. EditablePanel* pPlaylistBGPanel = FindControl< EditablePanel >( "PlaylistBGPanel", true );
  583. m_pCompetitiveModeLeaderboard = pPlaylistBGPanel->FindControl< CLadderLobbyLeaderboard >( "Leaderboard", true );
  584. m_pMatchHistoryScroller = pPlaylistBGPanel->FindControl< CScrollableList >( "MatchHistoryContainer" );
  585. int nAvatarWidth = ( ( m_iAvatarWidth * 5 / 4 ) + 1 );
  586. int nExtraWidth = ( m_pChatPlayerList->GetWide() - ( 2 * nAvatarWidth ) - m_iPlayerNameWidth - m_iBannedWidth - m_iHasPassWidth );
  587. m_pChatPlayerList->AddColumnToSection( 0, "avatar", "#TF_Players", vgui::SectionedListPanel::COLUMN_IMAGE, nAvatarWidth );
  588. m_pChatPlayerList->AddColumnToSection( 0, "name", "", 0, m_iPlayerNameWidth + nExtraWidth );
  589. m_pChatPlayerList->AddColumnToSection( 0, "is_banned", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, m_iBannedWidth );
  590. m_pChatPlayerList->AddColumnToSection( 0, "has_competitive_access", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, m_iHasPassWidth );
  591. m_pChatPlayerList->AddColumnToSection( 0, "rank", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, nAvatarWidth );
  592. m_pChatPlayerList->SetDrawHeaders( false );
  593. m_fontMedalsCount = pScheme->GetFont( "HudFontSmallestBold", true );
  594. }
  595. static int SortResult( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b )
  596. {
  597. return a->display_rating_change() < b->display_rating_change();
  598. }
  599. static int SortDate( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b )
  600. {
  601. return a->endtime() < b->endtime();
  602. }
  603. static int SortMap( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b )
  604. {
  605. const MapDef_t* pMapDef = GetItemSchema()->GetMasterMapDefByIndex( a->map_index() );
  606. const wchar_t* pszAMapName = g_pVGuiLocalize->Find( pMapDef ? pMapDef->pszMapNameLocKey : "#TF_Map_Unknown" );
  607. pMapDef = GetItemSchema()->GetMasterMapDefByIndex( b->map_index() );
  608. const wchar_t* pszBMapName = g_pVGuiLocalize->Find( pMapDef ? pMapDef->pszMapNameLocKey : "#TF_Map_Unknown" );
  609. return wcscoll( pszAMapName, pszBMapName );
  610. }
  611. static int SortKDR( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b )
  612. {
  613. float flAKDRatio = a->kills();
  614. if ( a->deaths() > 0 )
  615. {
  616. flAKDRatio /= (float)a->deaths();
  617. }
  618. float flBKDRatio = b->kills();
  619. if ( b->deaths() > 0 )
  620. {
  621. flBKDRatio /= (float)b->deaths();
  622. }
  623. return ( flBKDRatio - flAKDRatio ) * 1000.f;
  624. }
  625. void CLobbyPanel_Comp::UpdateMatchDataForLocalPlayer()
  626. {
  627. m_bMatchDataForLocalPlayerDirty = false;
  628. CUtlVector < CSOTFMatchResultPlayerStats > vecMatches;
  629. GetLocalPlayerMatchHistory( GetMatchGroup(), vecMatches );
  630. if ( !m_bMatchHistoryLoaded )
  631. {
  632. GCSDK::CProtoBufMsg< CMsgGCMatchHistoryLoad > msg( k_EMsgGCMatchHistoryLoad );
  633. GCClientSystem()->BSendMessage( msg );
  634. m_bMatchHistoryLoaded = true;
  635. }
  636. if ( m_pMatchHistoryScroller )
  637. {
  638. m_pMatchHistoryScroller->ClearAutoLayoutPanels();
  639. typedef int (*MatchSortFunc)( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b );
  640. struct SortMethodData_t
  641. {
  642. MatchSortFunc m_pfnSort;
  643. CExButton* m_pSortButton;
  644. };
  645. EditablePanel* pPlaylistBGPanel = FindControl< EditablePanel >( "PlaylistBGPanel", true );
  646. Assert( pPlaylistBGPanel );
  647. if ( !pPlaylistBGPanel )
  648. return;
  649. SortMethodData_t sortMethods[ NUM_SORT_METHODS ] = { { &SortResult, pPlaylistBGPanel->FindControl< CExButton >( "ResultButton", true ) }
  650. , { &SortDate, pPlaylistBGPanel->FindControl< CExButton >( "DateButton", true ) }
  651. , { &SortMap, pPlaylistBGPanel->FindControl< CExButton >( "MapButton", true ) }
  652. , { &SortKDR, pPlaylistBGPanel->FindControl< CExButton >( "KDRButton", true ) } };
  653. // Sort
  654. vecMatches.Sort( sortMethods[ m_eMatchSortMethod ].m_pfnSort );
  655. Label* pSortArrow = pPlaylistBGPanel->FindControl< Label >( "SortArrow", true );
  656. Assert( pSortArrow );
  657. if ( !pSortArrow )
  658. return;
  659. // Update controls
  660. for( int i=0; i<ARRAYSIZE( sortMethods ); ++i )
  661. {
  662. if( sortMethods[ i ].m_pSortButton )
  663. {
  664. bool bSelected = i == m_eMatchSortMethod;
  665. sortMethods[ i ].m_pSortButton->SetSelected( bSelected );
  666. if ( bSelected )
  667. {
  668. int nDummy, nX;
  669. sortMethods[ i ].m_pSortButton->GetContentSize( nX, nDummy );
  670. // Move the sort arrow to the right edge of the selected panel
  671. pSortArrow->SetPos( sortMethods[ i ].m_pSortButton->GetXPos() + nX , pSortArrow->GetYPos() );
  672. // Fixup the label to be an up or down arrow
  673. pSortArrow->SetText( m_bDescendingMatchHistorySort ? L"6" : L"5" );
  674. }
  675. }
  676. }
  677. // Potentially go backwards
  678. if ( m_bDescendingMatchHistorySort )
  679. {
  680. FOR_EACH_VEC( vecMatches, i )
  681. {
  682. CMatchHistoryEntryPanel* pMatchEntryPanel = new CMatchHistoryEntryPanel( m_pMatchHistoryScroller, "MatchEntry" );
  683. pMatchEntryPanel->MakeReadyForUse();
  684. pMatchEntryPanel->SetMatchData( vecMatches[ i ] );
  685. m_pMatchHistoryScroller->AddPanel( pMatchEntryPanel, 5 );
  686. }
  687. }
  688. else
  689. {
  690. FOR_EACH_VEC_BACK( vecMatches, i )
  691. {
  692. CMatchHistoryEntryPanel* pMatchEntryPanel = new CMatchHistoryEntryPanel( m_pMatchHistoryScroller, "MatchEntry" );
  693. pMatchEntryPanel->MakeReadyForUse();
  694. pMatchEntryPanel->SetMatchData( vecMatches[ i ] );
  695. m_pMatchHistoryScroller->AddPanel( pMatchEntryPanel, 5 );
  696. }
  697. }
  698. m_pMatchHistoryScroller->MakeReadyForUse();
  699. }
  700. }