//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "tf_party.h" #include "vgui_controls/PropertySheet.h" #include "vgui_controls/ScrollableEditablePanel.h" #include "tf_lobbypanel_comp.h" #include "tf_lobby_container_frame_comp.h" #include "vgui/ISystem.h" #include "tf_streams.h" #include "tf_badge_panel.h" // memdbgon must be the last include file in a .cpp file!!! #include 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." ); 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." ); class CLobbyPanel_Comp; #include "iclientmode.h" #include class CMatchHistoryEntryPanel : public CExpandablePanel { public: DECLARE_CLASS_SIMPLE( CMatchHistoryEntryPanel, CExpandablePanel ); CMatchHistoryEntryPanel( Panel* pParent, const char *pszPanelname ) : BaseClass( pParent, pszPanelname ) {} virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/ui/MatchHistoryEntryPanel.res" ); } void SetMatchData( const CSOTFMatchResultPlayerStats& stats ) { EditablePanel* pContainer = FindControl< EditablePanel >( "Container" ); if ( !pContainer ) return; // Match date CRTime matchdate( stats.endtime() ); char rtime_buf[k_RTimeRenderBufferSize]; matchdate.Render( rtime_buf ); pContainer->SetDialogVariable( "match_date", rtime_buf ); // Map name const MapDef_t* pMapDef = GetItemSchema()->GetMasterMapDefByIndex( stats.map_index() ); const char* pszMapToken = "#TF_Map_Unknown"; if ( pMapDef ) { pszMapToken = pMapDef->pszMapNameLocKey; } pContainer->SetDialogVariable( "map_name", g_pVGuiLocalize->Find( pszMapToken ) ); // KD ratio float flKDRatio = stats.kills(); if ( stats.deaths() > 0 ) { flKDRatio /= (float)stats.deaths(); } pContainer->SetDialogVariable( "kd_ratio", CFmtStr( "%.1f", flKDRatio ) ); pContainer->SetControlVisible( "WinLabel", stats.display_rating_change() > 0 ); pContainer->SetControlVisible( "LossLabel", stats.display_rating_change() < 0 ); EditablePanel* pStatsContainer = FindControl< EditablePanel >( "SlidingStatsContainer", true ); if ( pStatsContainer ) { pStatsContainer->SetDialogVariable( "stat_kills", LocalizeNumberWithToken( "TF_Competitive_Kills", stats.kills() ) ); pStatsContainer->SetDialogVariable( "stat_deaths", LocalizeNumberWithToken( "TF_Competitive_Deaths", stats.deaths() ) ); pStatsContainer->SetDialogVariable( "stat_damage", LocalizeNumberWithToken( "TF_Competitive_Damage", stats.damage() ) ); pStatsContainer->SetDialogVariable( "stat_healing", LocalizeNumberWithToken( "TF_Competitive_Healing", stats.healing() ) ); pStatsContainer->SetDialogVariable( "stat_support", LocalizeNumberWithToken( "TF_Competitive_Support", stats.support() ) ); pStatsContainer->SetDialogVariable( "stat_score", LocalizeNumberWithToken( "TF_Competitive_Score", stats.score() ) ); ScalableImagePanel* pMapImage = pStatsContainer->FindControl< ScalableImagePanel >( "BGImage", true ); if ( pMapImage && pMapDef ) { char imagename[ 512 ]; Q_snprintf( imagename, sizeof( imagename ), "..\\vgui\\maps\\menu_thumb_%s", pMapDef->pszMapName ); pMapImage->SetImage( imagename ); } // Lambdas, wherefore art thou... SetupClassIcon( pStatsContainer, "scout", TF_CLASS_SCOUT, stats ); SetupClassIcon( pStatsContainer, "soldier", TF_CLASS_SOLDIER, stats ); SetupClassIcon( pStatsContainer, "pyro", TF_CLASS_PYRO, stats ); SetupClassIcon( pStatsContainer, "demo", TF_CLASS_DEMOMAN, stats ); SetupClassIcon( pStatsContainer, "heavy", TF_CLASS_HEAVYWEAPONS, stats ); SetupClassIcon( pStatsContainer, "engineer", TF_CLASS_ENGINEER, stats ); SetupClassIcon( pStatsContainer, "medic", TF_CLASS_MEDIC, stats ); SetupClassIcon( pStatsContainer, "sniper", TF_CLASS_SNIPER, stats ); SetupClassIcon( pStatsContainer, "spy", TF_CLASS_SPY, stats ); SetupMedalForStat( pStatsContainer, stats.kills_medal(), "kills" ); SetupMedalForStat( pStatsContainer, stats.damage_medal(), "damage" ); SetupMedalForStat( pStatsContainer, stats.healing_medal(), "healing" ); SetupMedalForStat( pStatsContainer, stats.support_medal(), "support" ); SetupMedalForStat( pStatsContainer, stats.score_medal(), "score" ); } } private: void SetupMedalForStat( EditablePanel* pParent, int nStat, const char* pszStatName ) { ScalableImagePanel* pMedalImage = pParent->FindControl< ScalableImagePanel >( CFmtStr( "%sMedal", pszStatName ) ); if ( pMedalImage ) { if ( nStat != StatMedal_None ) { pMedalImage->SetImage( g_pszCompetitiveMedalImages[ nStat ] ); } pMedalImage->SetVisible( nStat != StatMedal_None ); } } void SetupClassIcon( EditablePanel* pParent, const char* pszClassName, int nBit, const CSOTFMatchResultPlayerStats& stats ) { ScalableImagePanel* pClassIcon = pParent->FindControl< ScalableImagePanel >( CFmtStr( "%sIcon", pszClassName ), true ); if( pClassIcon ) { pClassIcon->SetImage( stats.classes_played() & (1<AddActionSignalTarget( this ); m_pszLeaderboardName = "tf2_ladder_6v6"; for ( int i = 0; i < 100; ++i ) { vgui::EditablePanel *pEntryUI = new vgui::EditablePanel( m_pScoreList, "LeaderboardEntry" ); m_vecLeaderboardEntries.AddToTail( pEntryUI ); } m_pToolTip = new CTFTextToolTip( this ); m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" ); m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false ); m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false ); m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel ); m_pToolTip->SetTooltipDelay( 0 ); m_bIsDataValid = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLadderLobbyLeaderboard::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "Resource/UI/econ/LobbyLeaderboard.res" ); FOR_EACH_VEC( m_vecLeaderboardEntries, i ) { m_vecLeaderboardEntries[i]->ApplySchemeSettings( pScheme ); m_vecLeaderboardEntries[i]->LoadControlSettings( "Resource/UI/LeaderboardEntryRank.res" ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLadderLobbyLeaderboard::PerformLayout() { BaseClass::PerformLayout(); UpdateLeaderboards(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLadderLobbyLeaderboard::OnCommand( const char *command ) { if ( Q_strnicmp( command, "stream", 6 ) == 0 ) { vgui::system()->ShellExecute( "open", command + 7 ); return; } else if ( FStrEq( "global", command ) ) { if ( m_bGlobal != true ) { m_bGlobal = true; UpdateLeaderboards(); } return; } else if ( FStrEq( "local", command ) ) { if ( m_bGlobal == true ) { m_bGlobal = false; UpdateLeaderboards(); } } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CLadderLobbyLeaderboard::GetLeaderboardData( CUtlVector< LeaderboardEntry_t* >& scores ) { CUtlVector< LeaderboardEntry_t* > vecLadderScores; if ( Leaderboards_GetLadderLeaderboard( vecLadderScores, m_pszLeaderboardName, m_bGlobal ) ) { scores.AddVectorToTail( vecLadderScores ); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CLadderLobbyLeaderboard::UpdateLeaderboards() { CUtlVector< LeaderboardEntry_t* > scores; m_bIsDataValid = GetLeaderboardData( scores ); if ( !m_bIsDataValid ) return false; int nScoreListHeight = scores.Count() * m_yEntryStep; int nScrollerWidth, nScrollerHeight; m_pScoreListScroller->GetSize( nScrollerWidth, nScrollerHeight ); m_pScoreList->SetSize( nScrollerWidth, Max( nScoreListHeight, nScrollerHeight ) ); m_pScoreList->InvalidateLayout( true ); m_pScoreListScroller->InvalidateLayout( true ); m_pScoreListScroller->GetScrollbar()->InvalidateLayout( true ); static int nScrollWidth = m_pScoreListScroller->GetScrollbar()->GetWide(); m_pScoreListScroller->GetScrollbar()->SetWide( nScrollWidth>>1 ); if ( m_pScoreListScroller->GetScrollbar()->GetButton( 0 ) && m_pScoreListScroller->GetScrollbar()->GetButton( 1 ) ) { m_pScoreListScroller->GetScrollbar()->GetButton( 0 )->SetVisible( false ); m_pScoreListScroller->GetScrollbar()->GetButton( 1 )->SetVisible( false ); } FOR_EACH_VEC( m_vecLeaderboardEntries, i ) { EditablePanel *pContainer = dynamic_cast< EditablePanel* >( m_vecLeaderboardEntries[i] ); if ( !pContainer ) continue; Color colorToUse = i % 2 == 1 ? m_OddTextColor : m_EvenTextColor; bool bIsEntryVisible = i < scores.Count(); pContainer->SetVisible( bIsEntryVisible ); pContainer->SetPos( 0, i * m_yEntryStep ); if ( bIsEntryVisible ) { const LeaderboardEntry_t *pLeaderboardEntry = scores[i]; const CSteamID &steamID = pLeaderboardEntry->m_steamIDUser; #ifdef TWITCH_LEADERBOARD TwitchTvAccountInfo_t *pTwitchInfo = StreamManager()->GetTwitchTvAccountInfo( steamID.ConvertToUint64() ); ETwitchTvState_t twitchState = pTwitchInfo ? pTwitchInfo->m_eTwitchTvState : k_ETwitchTvState_Error; // still waiting for twitch info to load if ( twitchState <= k_ETwitchTvState_Loading ) return false; #endif // TWITCH_LEADERBOARD bool bIsLocalPlayer = steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamUser()->GetSteamID() == steamID; pContainer->SetDialogVariable( "position", m_bGlobal ? CFmtStr( "%d.", pLeaderboardEntry->m_nGlobalRank ) : "" ); pContainer->SetDialogVariable( "username", InventoryManager()->PersonaName_Get( steamID.GetAccountID() ) ); CExLabel *pNameText = dynamic_cast< CExLabel* >( pContainer->FindChildByName( "UserName" ) ); if ( pNameText ) { pNameText->SetColorStr( bIsLocalPlayer ? m_LocalPlayerTextColor : colorToUse ); } CAvatarImagePanel *pAvatar = dynamic_cast< CAvatarImagePanel* >( pContainer->FindChildByName( "AvatarImage" ) ); if ( pAvatar ) { pAvatar->SetShouldDrawFriendIcon( false ); pAvatar->SetPlayer( steamID, k_EAvatarSize32x32 ); } CTFBadgePanel *pRankImage = dynamic_cast< CTFBadgePanel* >( pContainer->FindChildByName( "RankImage" ) ); if ( pRankImage ) { const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( k_nMatchGroup_Ladder_6v6 ); const LevelInfo_t& levelInfo = pMatchDesc->m_pProgressionDesc->GetLevelForExperience( pLeaderboardEntry->m_nScore ); pRankImage->SetupBadge( pMatchDesc->m_pProgressionDesc, levelInfo ); wchar_t wszOutString[ 128 ]; char szLocalized[512]; wchar_t wszCount[ 16 ]; _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", levelInfo.m_nLevelNum ); const wchar_t *wpszFormat = g_pVGuiLocalize->Find( pMatchDesc->m_pProgressionDesc->m_pszLevelToken ); g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 2, wszCount, g_pVGuiLocalize->Find( levelInfo.m_pszLevelTitle ) ); g_pVGuiLocalize->ConvertUnicodeToANSI( wszOutString, szLocalized, sizeof( szLocalized ) ); pRankImage->SetMouseInputEnabled( true ); pRankImage->SetVisible( true ); pRankImage->SetTooltip( m_pToolTip, szLocalized ); } CExImageButton *pStreamImage = dynamic_cast< CExImageButton* >( pContainer->FindChildByName( "StreamImageButton" ) ); if ( pStreamImage ) { #ifdef TWITCH_LEADERBOARD if ( twitchState == k_ETwitchTvState_Linked ) { pStreamImage->SetVisible( true ); pStreamImage->SetCommand( CFmtStr( "stream %s", pTwitchInfo->m_sTwitchTvChannel.String() ) ); } else #endif // TWITCH_LEADERBOARD { pStreamImage->SetVisible( false ); pStreamImage->SetCommand( "" ); } } } } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLadderLobbyLeaderboard::SetLeaderboard( const char *pszLeaderboardName, bool bGlobal ) { m_pszLeaderboardName = pszLeaderboardName; m_bGlobal = bGlobal; UpdateLeaderboards(); } static void GetPlayerNameForSteamID( wchar_t *wCharPlayerName, int nBufSizeBytes, const CSteamID &steamID ) { const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID ); V_UTF8ToUnicode( pszName, wCharPlayerName, nBufSizeBytes ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CLobbyPanel_Comp::CLobbyPanel_Comp( vgui::Panel *pParent, CBaseLobbyContainerFrame* pLobbyContainer ) : CBaseLobbyPanel( pParent, pLobbyContainer ) , m_pCompetitiveModeLeaderboard( NULL ) , m_pMatchHistoryScroller( NULL ) , m_eMatchSortMethod( SORT_BY_DATE ) , m_bDescendingMatchHistorySort( true ) { // Comp m_fontMedalsCount = 0; m_flCompetitiveRankProgress = -1.f; m_flCompetitiveRankPrevProgress = -1.f; m_flRefreshPlayerListTime = -1.f; m_bCompetitiveRankChangePlayedSound = false; m_bMatchHistoryLoaded = false; m_bMatchDataForLocalPlayerDirty = true; ListenForGameEvent( "gc_new_session" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CLobbyPanel_Comp::~CLobbyPanel_Comp() { delete m_pImageList; m_pImageList = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::OnCommand( const char *command ) { if ( FStrEq( command, "medals_help" ) ) { CExplanationPopup *pPopup = dynamic_cast< CExplanationPopup* >( GetParent()->FindChildByName( "MedalsHelp" ) ); if ( pPopup ) { pPopup->Popup(); } return; } else if ( FStrEq( command, "show_leaderboards" ) ) { if ( m_pCompetitiveModeLeaderboard ) { m_pCompetitiveModeLeaderboard->SetVisible( true ); } SetControlVisible( "MatchHistoryCategories", false, true ); SetControlVisible( "MatchHistoryContainer", false, true ); return; } else if ( FStrEq( command, "show_match_history" ) ) { m_bMatchDataForLocalPlayerDirty = true; if ( m_pCompetitiveModeLeaderboard ) { m_pCompetitiveModeLeaderboard->SetVisible( false ); } SetControlVisible( "MatchHistoryCategories", true, true ); SetControlVisible( "MatchHistoryContainer", true, true ); return; } else if ( !Q_strncmp( "sort", command, 4 ) ) { EMatchHistorySortMethods_t eNewMethod = (EMatchHistorySortMethods_t)atoi( command + 4 ); if ( eNewMethod == m_eMatchSortMethod ) { m_bDescendingMatchHistorySort = !m_bDescendingMatchHistorySort; } else { m_eMatchSortMethod = eNewMethod; m_bDescendingMatchHistorySort = true; } m_bMatchDataForLocalPlayerDirty = true; return; } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CLobbyPanel_Comp::ShouldShowLateJoin() const { return false; // For now } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::ApplyChatUserSettings( const CBaseLobbyPanel::LobbyPlayerInfo &player, KeyValues *pKV ) const { pKV->SetInt( "has_ticket", 0 ); pKV->SetInt( "squad_surplus", 0 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::WriteGameSettingsControls() { BaseClass::WriteGameSettingsControls(); // Make sure we want to be in matchmaking. (If we don't, the frame should hide us pretty quickly.) // We might get an event or something right at the transition point occasionally when the UI should // not be visible if ( GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_Inactive ) { return; } ++m_iWritingPanel; m_pContainer->SetNextButtonEnabled( true ); SetControlVisible( "ScrollableContainer", GTFGCClientSystem()->GetWizardStep()== TF_Matchmaking_WizardStep_LADDER ); --m_iWritingPanel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CLobbyPanel_Comp::GetMedalCountForStat( EMatchGroup unLadderType, RankStatType_t nStatType, int nMedalLevel ) { CSOTFLadderData *pData = GetLocalPlayerLadderData( unLadderType ); if ( !pData ) return 0; switch ( nStatType ) { case RankStat_Score: if ( nMedalLevel == StatMedal_Bronze ) { return pData->Obj().score_bronze(); } else if ( nMedalLevel == StatMedal_Silver ) { return pData->Obj().score_silver(); } else if ( nMedalLevel == StatMedal_Gold ) { return pData->Obj().score_gold(); } break; case RankStat_Kills: if ( nMedalLevel == StatMedal_Bronze ) { return pData->Obj().kills_bronze(); } else if ( nMedalLevel == StatMedal_Silver ) { return pData->Obj().kills_silver(); } else if ( nMedalLevel == StatMedal_Gold ) { return pData->Obj().kills_gold(); } break; case RankStat_Damage: if ( nMedalLevel == StatMedal_Bronze ) { return pData->Obj().damage_bronze(); } else if ( nMedalLevel == StatMedal_Silver ) { return pData->Obj().damage_silver(); } else if ( nMedalLevel == StatMedal_Gold ) { return pData->Obj().damage_gold(); } break; case RankStat_Healing: if ( nMedalLevel == StatMedal_Bronze ) { return pData->Obj().healing_bronze(); } else if ( nMedalLevel == StatMedal_Silver ) { return pData->Obj().healing_silver(); } else if ( nMedalLevel == StatMedal_Gold ) { return pData->Obj().healing_gold(); } break; case RankStat_Support: if ( nMedalLevel == StatMedal_Bronze ) { return pData->Obj().support_bronze(); } else if ( nMedalLevel == StatMedal_Silver ) { return pData->Obj().support_silver(); } else if ( nMedalLevel == StatMedal_Gold ) { return pData->Obj().support_gold(); } break; case RankStat_Deaths: // Not supported break; default: Assert( 0 ); } return 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::OnThink() { BaseClass::OnThink(); if ( m_bMatchDataForLocalPlayerDirty ) { UpdateMatchDataForLocalPlayer(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::FireGameEvent( IGameEvent *event ) { const char *pszEventname = event->GetName(); if ( !Q_stricmp( pszEventname, "gc_new_session" ) ) { // This is loaded on demand by the GC - if we have a new session, we need to re-request if ( m_bMatchHistoryLoaded ) { m_bMatchHistoryLoaded = false; m_bMatchDataForLocalPlayerDirty = true; } } BaseClass::FireGameEvent( event ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- EMatchGroup CLobbyPanel_Comp::GetMatchGroup( void ) const { return k_nMatchGroup_Ladder_6v6; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) { if ( pObject->GetTypeID() != CSOTFMatchResultPlayerInfo::k_nTypeID ) return; m_bMatchDataForLocalPlayerDirty = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) { if ( pObject->GetTypeID() != CSOTFMatchResultPlayerInfo::k_nTypeID ) return; m_bMatchDataForLocalPlayerDirty = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::PerformLayout() { BaseClass::PerformLayout(); m_bMatchDataForLocalPlayerDirty = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLobbyPanel_Comp::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); EditablePanel* pPlaylistBGPanel = FindControl< EditablePanel >( "PlaylistBGPanel", true ); m_pCompetitiveModeLeaderboard = pPlaylistBGPanel->FindControl< CLadderLobbyLeaderboard >( "Leaderboard", true ); m_pMatchHistoryScroller = pPlaylistBGPanel->FindControl< CScrollableList >( "MatchHistoryContainer" ); int nAvatarWidth = ( ( m_iAvatarWidth * 5 / 4 ) + 1 ); int nExtraWidth = ( m_pChatPlayerList->GetWide() - ( 2 * nAvatarWidth ) - m_iPlayerNameWidth - m_iBannedWidth - m_iHasPassWidth ); m_pChatPlayerList->AddColumnToSection( 0, "avatar", "#TF_Players", vgui::SectionedListPanel::COLUMN_IMAGE, nAvatarWidth ); m_pChatPlayerList->AddColumnToSection( 0, "name", "", 0, m_iPlayerNameWidth + nExtraWidth ); m_pChatPlayerList->AddColumnToSection( 0, "is_banned", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, m_iBannedWidth ); m_pChatPlayerList->AddColumnToSection( 0, "has_competitive_access", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, m_iHasPassWidth ); m_pChatPlayerList->AddColumnToSection( 0, "rank", "", vgui::SectionedListPanel::COLUMN_IMAGE | vgui::SectionedListPanel::COLUMN_CENTER, nAvatarWidth ); m_pChatPlayerList->SetDrawHeaders( false ); m_fontMedalsCount = pScheme->GetFont( "HudFontSmallestBold", true ); } static int SortResult( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b ) { return a->display_rating_change() < b->display_rating_change(); } static int SortDate( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b ) { return a->endtime() < b->endtime(); } static int SortMap( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b ) { const MapDef_t* pMapDef = GetItemSchema()->GetMasterMapDefByIndex( a->map_index() ); const wchar_t* pszAMapName = g_pVGuiLocalize->Find( pMapDef ? pMapDef->pszMapNameLocKey : "#TF_Map_Unknown" ); pMapDef = GetItemSchema()->GetMasterMapDefByIndex( b->map_index() ); const wchar_t* pszBMapName = g_pVGuiLocalize->Find( pMapDef ? pMapDef->pszMapNameLocKey : "#TF_Map_Unknown" ); return wcscoll( pszAMapName, pszBMapName ); } static int SortKDR( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b ) { float flAKDRatio = a->kills(); if ( a->deaths() > 0 ) { flAKDRatio /= (float)a->deaths(); } float flBKDRatio = b->kills(); if ( b->deaths() > 0 ) { flBKDRatio /= (float)b->deaths(); } return ( flBKDRatio - flAKDRatio ) * 1000.f; } void CLobbyPanel_Comp::UpdateMatchDataForLocalPlayer() { m_bMatchDataForLocalPlayerDirty = false; CUtlVector < CSOTFMatchResultPlayerStats > vecMatches; GetLocalPlayerMatchHistory( GetMatchGroup(), vecMatches ); if ( !m_bMatchHistoryLoaded ) { GCSDK::CProtoBufMsg< CMsgGCMatchHistoryLoad > msg( k_EMsgGCMatchHistoryLoad ); GCClientSystem()->BSendMessage( msg ); m_bMatchHistoryLoaded = true; } if ( m_pMatchHistoryScroller ) { m_pMatchHistoryScroller->ClearAutoLayoutPanels(); typedef int (*MatchSortFunc)( const CSOTFMatchResultPlayerStats * a, const CSOTFMatchResultPlayerStats * b ); struct SortMethodData_t { MatchSortFunc m_pfnSort; CExButton* m_pSortButton; }; EditablePanel* pPlaylistBGPanel = FindControl< EditablePanel >( "PlaylistBGPanel", true ); Assert( pPlaylistBGPanel ); if ( !pPlaylistBGPanel ) return; SortMethodData_t sortMethods[ NUM_SORT_METHODS ] = { { &SortResult, pPlaylistBGPanel->FindControl< CExButton >( "ResultButton", true ) } , { &SortDate, pPlaylistBGPanel->FindControl< CExButton >( "DateButton", true ) } , { &SortMap, pPlaylistBGPanel->FindControl< CExButton >( "MapButton", true ) } , { &SortKDR, pPlaylistBGPanel->FindControl< CExButton >( "KDRButton", true ) } }; // Sort vecMatches.Sort( sortMethods[ m_eMatchSortMethod ].m_pfnSort ); Label* pSortArrow = pPlaylistBGPanel->FindControl< Label >( "SortArrow", true ); Assert( pSortArrow ); if ( !pSortArrow ) return; // Update controls for( int i=0; iSetSelected( bSelected ); if ( bSelected ) { int nDummy, nX; sortMethods[ i ].m_pSortButton->GetContentSize( nX, nDummy ); // Move the sort arrow to the right edge of the selected panel pSortArrow->SetPos( sortMethods[ i ].m_pSortButton->GetXPos() + nX , pSortArrow->GetYPos() ); // Fixup the label to be an up or down arrow pSortArrow->SetText( m_bDescendingMatchHistorySort ? L"6" : L"5" ); } } } // Potentially go backwards if ( m_bDescendingMatchHistorySort ) { FOR_EACH_VEC( vecMatches, i ) { CMatchHistoryEntryPanel* pMatchEntryPanel = new CMatchHistoryEntryPanel( m_pMatchHistoryScroller, "MatchEntry" ); pMatchEntryPanel->MakeReadyForUse(); pMatchEntryPanel->SetMatchData( vecMatches[ i ] ); m_pMatchHistoryScroller->AddPanel( pMatchEntryPanel, 5 ); } } else { FOR_EACH_VEC_BACK( vecMatches, i ) { CMatchHistoryEntryPanel* pMatchEntryPanel = new CMatchHistoryEntryPanel( m_pMatchHistoryScroller, "MatchEntry" ); pMatchEntryPanel->MakeReadyForUse(); pMatchEntryPanel->SetMatchData( vecMatches[ i ] ); m_pMatchHistoryScroller->AddPanel( pMatchEntryPanel, 5 ); } } m_pMatchHistoryScroller->MakeReadyForUse(); } }