summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/tf_statsummary.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/tf_statsummary.cpp')
-rw-r--r--game/client/tf/vgui/tf_statsummary.cpp1506
1 files changed, 1506 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_statsummary.cpp b/game/client/tf/vgui/tf_statsummary.cpp
new file mode 100644
index 0000000..8e96829
--- /dev/null
+++ b/game/client/tf/vgui/tf_statsummary.cpp
@@ -0,0 +1,1506 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "cbase.h"
+
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/RichText.h>
+#include <vgui_controls/Frame.h>
+#include <vgui_controls/QueryBox.h>
+#include <vgui/IScheme.h>
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include "ienginevgui.h"
+#include <game/client/iviewport.h>
+#include "tf_tips.h"
+#include "tf_mapinfo.h"
+#include "vgui_avatarimage.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+
+#include "tf_statsummary.h"
+#include <convar.h>
+#include "fmtstr.h"
+#include "tf_gamerules.h"
+#include "tf_gc_client.h"
+
+using namespace vgui;
+
+#if defined( REPLAY_ENABLED )
+extern bool g_bIsReplayRewinding;
+#else
+bool g_bIsReplayRewinding = false;
+#endif
+
+const char *g_pszTipsClassImages[] =
+{
+ "", // TF_CLASS_UNDEFINED = 0,
+ "class_portraits/scout", // TF_CLASS_SCOUT,
+ "class_portraits/sniper",// TF_CLASS_SNIPER,
+ "class_portraits/soldier", // TF_CLASS_SOLDIER,
+ "class_portraits/demoman", // TF_CLASS_DEMOMAN,
+ "class_portraits/medic", // TF_CLASS_MEDIC,
+ "class_portraits/heavy", // TF_CLASS_HEAVYWEAPONS,
+ "class_portraits/pyro", // TF_CLASS_PYRO,
+ "class_portraits/spy", // TF_CLASS_SPY,
+ "class_portraits/engineer", // TF_CLASS_ENGINEER,
+};
+
+ClassDetails_t g_PerClassStatDetails[15] =
+{
+ { TFSTAT_POINTSSCORED, ALL_CLASSES, "#TF_ClassRecord_MostPoints", "#TF_ClassRecord_Alt_MostPoints" },
+ { TFSTAT_KILLS, ALL_CLASSES, "#TF_ClassRecord_MostKills", "#TF_ClassRecord_Alt_MostKills" },
+ { TFSTAT_KILLASSISTS, ALL_CLASSES, "#TF_ClassRecord_MostAssists", "#TF_ClassRecord_Alt_MostAssists" },
+ { TFSTAT_CAPTURES, ALL_CLASSES, "#TF_ClassRecord_MostCaptures", "#TF_ClassRecord_Alt_MostCaptures" },
+ { TFSTAT_DEFENSES, ALL_CLASSES, "#TF_ClassRecord_MostDefenses", "#TF_ClassRecord_Alt_MostDefenses" },
+ { TFSTAT_DAMAGE, ALL_CLASSES, "#TF_ClassRecord_MostDamage", "#TF_ClassRecord_Alt_MostDamage" },
+ { TFSTAT_BUILDINGSDESTROYED, ALL_CLASSES, "#TF_ClassRecord_MostDestruction", "#TF_ClassRecord_Alt_MostDestruction" },
+ { TFSTAT_DOMINATIONS, ALL_CLASSES, "#TF_ClassRecord_MostDominations", "#TF_ClassRecord_Alt_MostDominations" },
+ { TFSTAT_PLAYTIME, ALL_CLASSES, "#TF_ClassRecord_LongestLife", "#TF_ClassRecord_Alt_LongestLife" },
+ { TFSTAT_HEALING, MAKESTATFLAG(TF_CLASS_MEDIC) | MAKESTATFLAG(TF_CLASS_ENGINEER) | MAKESTATFLAG(TF_CLASS_HEAVYWEAPONS), "#TF_ClassRecord_MostHealing", "#TF_ClassRecord_Alt_MostHealing" },
+ { TFSTAT_INVULNS, MAKESTATFLAG(TF_CLASS_MEDIC), "#TF_ClassRecord_MostInvulns", "#TF_ClassRecord_Alt_MostInvulns" },
+ { TFSTAT_MAXSENTRYKILLS, MAKESTATFLAG(TF_CLASS_ENGINEER), "#TF_ClassRecord_MostSentryKills", "#TF_ClassRecord_Alt_MostSentryKills" },
+ { TFSTAT_TELEPORTS, MAKESTATFLAG(TF_CLASS_ENGINEER), "#TF_ClassRecord_MostTeleports", "#TF_ClassRecord_Alt_MostTeleports" },
+ { TFSTAT_HEADSHOTS, MAKESTATFLAG(TF_CLASS_SNIPER) | MAKESTATFLAG(TF_CLASS_SPY), "#TF_ClassRecord_MostHeadshots", "#TF_ClassRecord_Alt_MostHeadshots" },
+ { TFSTAT_BACKSTABS, MAKESTATFLAG(TF_CLASS_SPY), "#TF_ClassRecord_MostBackstabs", "#TF_ClassRecord_Alt_MostBackstabs" },
+};
+
+ClassDetails_t g_PerClassMVMStatDetails[12] =
+{
+ { TFSTAT_POINTSSCORED, ALL_CLASSES, "#TF_ClassRecord_MostPoints", "#TF_ClassRecord_Alt_MostPoints" },
+ { TFSTAT_KILLS, ALL_CLASSES, "#TF_ClassRecord_MostKills", "#TF_ClassRecord_Alt_MostKills" },
+ { TFSTAT_KILLASSISTS, ALL_CLASSES, "#TF_ClassRecord_MostAssists", "#TF_ClassRecord_Alt_MostAssists" },
+ { TFSTAT_DEFENSES, ALL_CLASSES, "#TF_ClassRecord_MostDefenses", "#TF_ClassRecord_Alt_MostDefenses" },
+ { TFSTAT_DAMAGE, ALL_CLASSES, "#TF_ClassRecord_MostDamage", "#TF_ClassRecord_Alt_MostDamage" },
+ { TFSTAT_PLAYTIME, ALL_CLASSES, "#TF_ClassRecord_LongestLife", "#TF_ClassRecord_Alt_LongestLife" },
+ { TFSTAT_HEALING, MAKESTATFLAG(TF_CLASS_MEDIC) | MAKESTATFLAG(TF_CLASS_ENGINEER) | MAKESTATFLAG(TF_CLASS_HEAVYWEAPONS), "#TF_ClassRecord_MostHealing", "#TF_ClassRecord_Alt_MostHealing" },
+ { TFSTAT_INVULNS, MAKESTATFLAG(TF_CLASS_MEDIC), "#TF_ClassRecord_MostInvulns", "#TF_ClassRecord_Alt_MostInvulns" },
+ { TFSTAT_MAXSENTRYKILLS, MAKESTATFLAG(TF_CLASS_ENGINEER), "#TF_ClassRecord_MostSentryKills", "#TF_ClassRecord_Alt_MostSentryKills" },
+ { TFSTAT_TELEPORTS, MAKESTATFLAG(TF_CLASS_ENGINEER), "#TF_ClassRecord_MostTeleports", "#TF_ClassRecord_Alt_MostTeleports" },
+ { TFSTAT_HEADSHOTS, MAKESTATFLAG(TF_CLASS_SNIPER) | MAKESTATFLAG(TF_CLASS_SPY), "#TF_ClassRecord_MostHeadshots", "#TF_ClassRecord_Alt_MostHeadshots" },
+ { TFSTAT_BACKSTABS, MAKESTATFLAG(TF_CLASS_SPY), "#TF_ClassRecord_MostBackstabs", "#TF_ClassRecord_Alt_MostBackstabs" },
+};
+
+CTFStatsSummaryPanel *g_pTFStatsSummaryPanel = NULL;
+
+CUtlVector<CTFStatsSummaryPanel *> g_vecStatPanels;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void UpdateStatSummaryPanels( CUtlVector<ClassStats_t> &vecClassStats )
+{
+ for ( int i = 0; i < g_vecStatPanels.Count(); i++ )
+ {
+ g_vecStatPanels[i]->SetStats( vecClassStats );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the global stats summary panel
+//-----------------------------------------------------------------------------
+CTFStatsSummaryPanel *GStatsSummaryPanel()
+{
+ if ( NULL == g_pTFStatsSummaryPanel )
+ {
+ g_pTFStatsSummaryPanel = new CTFStatsSummaryPanel();
+ }
+ return g_pTFStatsSummaryPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destroys the global stats summary panel
+//-----------------------------------------------------------------------------
+void DestroyStatsSummaryPanel()
+{
+ if ( NULL != g_pTFStatsSummaryPanel )
+ {
+ g_pTFStatsSummaryPanel->MarkForDeletion();
+ g_pTFStatsSummaryPanel = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CTFStatsSummaryPanel::CTFStatsSummaryPanel()
+ : BaseClass( NULL, "TFStatsSummary", vgui::scheme()->LoadSchemeFromFile( "Resource/ClientScheme.res", "ClientScheme" ) )
+ , m_bShowingLeaderboard( false )
+ , m_bLoadingCommunityMap( false )
+{
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::Init( void )
+{
+ m_bControlsLoaded = false;
+ m_bInteractive = false;
+ m_bEmbedded = false;
+ m_xStartLHBar = 0;
+ m_xStartRHBar = 0;
+ m_iBarHeight = 1;
+ m_iBarMaxWidth = 1;
+
+ m_pPlayerData = new vgui::EditablePanel( this, "statdata" );
+ m_pInteractiveHeaders = new vgui::EditablePanel( m_pPlayerData, "InteractiveHeaders" );
+ m_pNonInteractiveHeaders = new vgui::EditablePanel( m_pPlayerData, "NonInteractiveHeaders" );
+ m_pBarChartComboBoxA = new vgui::ComboBox( m_pInteractiveHeaders, "BarChartComboA", 10, false );
+ m_pBarChartComboBoxB = new vgui::ComboBox( m_pInteractiveHeaders, "BarChartComboB", 10, false );
+ m_pClassComboBox = new vgui::ComboBox( m_pInteractiveHeaders, "ClassCombo", 10, false );
+ m_pTipImage = new CTFImagePanel( this, "TipImage" );
+ m_pTipText = new vgui::Label( this, "TipText", "" );
+ m_pMapInfoPanel = NULL;
+ m_pMainBackground = NULL;
+ m_pLeaderboardTitle = NULL;
+ m_pContributedPanel = NULL;
+
+#ifdef _X360
+ m_pFooter = new CTFFooter( this, "Footer" );
+ m_bShowBackButton = false;
+#else
+ m_pNextTipButton = new vgui::Button( this, "NextTipButton", "" );
+ m_pResetStatsButton = new vgui::Button( this, "ResetStatsButton", "" );
+ m_pCloseButton = new vgui::Button( this, "CloseButton", "" );
+#endif
+
+ m_pBarChartComboBoxA->AddActionSignalTarget( this );
+ m_pBarChartComboBoxB->AddActionSignalTarget( this );
+ m_pClassComboBox->AddActionSignalTarget( this );
+
+ ListenForGameEvent( "server_spawn" );
+
+ Reset();
+
+ g_vecStatPanels.AddToTail( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CTFStatsSummaryPanel::CTFStatsSummaryPanel( vgui::Panel *parent ) : BaseClass( parent, "TFStatsSummary",
+ vgui::scheme()->LoadSchemeFromFile( "Resource/ClientScheme.res", "ClientScheme" ) )
+{
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFStatsSummaryPanel::~CTFStatsSummaryPanel()
+{
+ g_vecStatPanels.FindAndRemove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shows this dialog as a modal dialog
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::ShowModal()
+{
+#ifdef _X360
+ m_bInteractive = false;
+ m_bShowBackButton = true;
+#else
+ // we are in interactive mode, enable controls
+ m_bInteractive = true;
+#endif
+
+ SetParent( enginevgui->GetPanel( PANEL_GAMEUIDLL ) );
+ UpdateDialog();
+ SetVisible( true );
+ MoveToFront();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::SetupForEmbedded( void )
+{
+ m_bInteractive = true;
+ m_bEmbedded = true;
+
+ UpdateDialog();
+
+ InvalidateLayout( true, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+#ifndef _X360
+ if ( m_pTipImage && m_pTipText )
+ {
+ int iX,iY;
+ m_pTipImage->GetPos(iX,iY);
+ int iTX, iTY;
+ m_pTipText->GetPos(iTX, iTY);
+ m_pTipText->SetPos( iX + m_pTipImage->GetWide() + XRES(8), iTY );
+ }
+
+ if ( m_pNextTipButton )
+ {
+ m_pNextTipButton->SizeToContents();
+ }
+
+ if ( m_pResetStatsButton )
+ {
+ m_pResetStatsButton->SizeToContents();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnThink()
+{
+ BaseClass::OnThink();
+
+ if ( m_bShowingLeaderboard )
+ {
+ UpdateLeaderboard();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Command handler
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnCommand( const char *command )
+{
+ if ( 0 == Q_stricmp( command, "vguicancel" ) )
+ {
+ m_bInteractive = false;
+ UpdateDialog();
+ SetVisible( false );
+ SetParent( (VPANEL) NULL );
+
+#ifdef _X360
+ SetDefaultSelections();
+ m_bShowBackButton = true;
+#endif
+ }
+#ifndef _X360
+ else if ( 0 == Q_stricmp( command, "resetstatsbutton" ) )
+ {
+ QueryBox *qb = new QueryBox( "#GameUI_Confirm", "#TF_ConfirmResetStats" );
+ if (qb != NULL)
+ {
+ qb->SetOKCommand(new KeyValues("DoResetStats") );
+ qb->AddActionSignalTarget(this);
+ qb->MoveToFront();
+ qb->DoModal();
+ }
+ }
+#endif
+ else if ( 0 == Q_stricmp( command, "nexttip" ) )
+ {
+ UpdateTip();
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the dialog
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::Reset()
+{
+ m_aClassStats.RemoveAll();
+
+ SetDefaultSelections();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets all user-controllable dialog settings to default values
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::SetDefaultSelections()
+{
+ m_iSelectedClass = TF_CLASS_UNDEFINED;
+ m_statBarGraph[0] = TFSTAT_POINTSSCORED;
+ m_displayBarGraph[0]= SHOW_MAX;
+ m_statBarGraph[1] = TFSTAT_PLAYTIME;
+ m_displayBarGraph[1] = SHOW_TOTAL;
+
+ m_pBarChartComboBoxA->ActivateItemByRow( 0 );
+ m_pBarChartComboBoxB->ActivateItemByRow( 10 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the background image based on the current mode
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateMainBackground( void )
+{
+ if ( IsPC() )
+ {
+ m_pMainBackground = dynamic_cast<ImagePanel *>( FindChildByName( "MainBackground" ) );
+ if ( m_pMainBackground )
+ {
+ const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
+
+ // determine if we're in widescreen or not and select the appropriate image
+ int screenWide, screenTall;
+ surface()->GetScreenSize( screenWide, screenTall );
+ float aspectRatio = (float)screenWide/(float)screenTall;
+ bool bIsWidescreen = aspectRatio >= 1.5999f;
+
+ if ( g_bIsReplayRewinding )
+ {
+ m_pMainBackground->SetImage( bIsWidescreen ? "../console/rewind_background_widescreen" : "../console/rewind_background" );
+ }
+ else if ( engine->IsLoadingDemo() || engine->IsPlayingDemo() || engine->IsSkippingPlayback() )
+ {
+ m_pMainBackground->SetImage( bIsWidescreen ? "../console/replay_loading_widescreen" : "../console/replay_loading" );
+ }
+ else if ( pMatchDesc && pMatchDesc->GetMapLoadBackgroundOverride( bIsWidescreen ) ) // Use match override if we have one
+ {
+ m_pMainBackground->SetImage( pMatchDesc->GetMapLoadBackgroundOverride( bIsWidescreen ) );
+ }
+ else
+ {
+ m_pMainBackground->SetImage( bIsWidescreen ? "../console/background01_widescreen" : "../console/background01" );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies scheme settings
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::ApplySchemeSettings(vgui::IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetProportional( true );
+
+ if ( m_bEmbedded )
+ {
+ LoadControlSettings( "Resource/UI/StatSummary_Embedded.res" );
+ }
+ else
+ {
+ LoadControlSettings( "Resource/UI/StatSummary.res" );
+ }
+ m_bControlsLoaded = true;
+
+ // set the background image
+ UpdateMainBackground();
+
+ m_pMapInfoPanel = dynamic_cast< EditablePanel *>( FindChildByName( "MapInfo" ) );
+ m_vecLeaderboardEntries.RemoveAll();
+ if ( m_pMapInfoPanel )
+ {
+ for ( int i = 0; i < 10; ++ i )
+ {
+ vgui::EditablePanel *pEntryUI = new vgui::EditablePanel( m_pMapInfoPanel, "LeaderboardEntry" );
+ pEntryUI->ApplySchemeSettings( pScheme );
+ pEntryUI->LoadControlSettings( "Resource/UI/LeaderboardEntry.res" );
+ m_vecLeaderboardEntries.AddToTail( pEntryUI );
+ }
+ }
+
+ // get the dimensions and position of a left-hand bar and a right-hand bar so we can do bar sizing later
+ Panel *pLHBar = m_pPlayerData->FindChildByName( "ClassBar1A" );
+ Panel *pRHBar = m_pPlayerData->FindChildByName( "ClassBar1B" );
+ if ( pLHBar && pRHBar )
+ {
+ int y;
+ pLHBar->GetBounds( m_xStartLHBar, y, m_iBarMaxWidth, m_iBarHeight );
+ pRHBar->GetBounds( m_xStartRHBar, y, m_iBarMaxWidth, m_iBarHeight );
+ }
+
+ // fill the combo box selections appropriately
+ InitBarChartComboBox( m_pBarChartComboBoxA );
+ InitBarChartComboBox( m_pBarChartComboBoxB );
+
+ // fill the class names in the class combo box
+ HFont hFont = scheme()->GetIScheme( GetScheme() )->GetFont( "ScoreboardSmall", true );
+ m_pClassComboBox->SetFont( hFont );
+ m_pClassComboBox->RemoveAll();
+ KeyValues *pKeyValues = new KeyValues( "data" );
+ pKeyValues->SetInt( "class", TF_CLASS_UNDEFINED );
+ m_pClassComboBox->AddItem( "#StatSummary_Label_AsAnyClass", pKeyValues );
+ for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass <= TF_LAST_NORMAL_CLASS; iClass++ )
+ {
+ if ( iClass == TF_CLASS_CIVILIAN )
+ continue;
+ pKeyValues = new KeyValues( "data" );
+ pKeyValues->SetInt( "class", iClass );
+ m_pClassComboBox->AddItem( g_aPlayerClassNames[iClass], pKeyValues );
+ }
+ m_pClassComboBox->ActivateItemByRow( 0 );
+
+ if ( m_pMapInfoPanel )
+ {
+ m_pContributedPanel = dynamic_cast< vgui::EditablePanel* >( m_pMapInfoPanel->FindChildByName( "ContributedLabel" ) );
+ }
+
+ SetDefaultSelections();
+ UpdateDialog();
+
+ if ( !m_bEmbedded )
+ {
+ SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnKeyCodePressed( KeyCode code )
+{
+ if ( IsX360() )
+ {
+ if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
+ {
+ OnCommand( "nexttip" ) ;
+ }
+ else if ( code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B )
+ {
+ OnCommand( "vguicancel" );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets stats to use
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::SetStats( CUtlVector<ClassStats_t> &vecClassStats )
+{
+ m_aClassStats = vecClassStats;
+ if ( m_bControlsLoaded )
+ {
+ UpdateDialog();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the dialog
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::ClearMapLabel()
+{
+ SetDialogVariable( "maplabel", "" );
+ SetDialogVariable( "maptype", "" );
+
+ vgui::Label *pLabel = dynamic_cast<Label *>( FindChildByName( "OnYourWayLabel" ) );
+ if ( pLabel && pLabel->IsVisible() )
+ {
+ pLabel->SetVisible( false );
+ }
+
+ pLabel = dynamic_cast<Label *>( FindChildByName( "MapType" ) );
+ if ( pLabel && pLabel->IsVisible() )
+ {
+ pLabel->SetVisible( false );
+ }
+
+ if ( m_pContributedPanel )
+ {
+ m_pContributedPanel->SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::ShowMapInfo( bool bShowMapInfo, bool bIsMVM /*= false*/, bool bBackgroundOverride /*= false*/ )
+{
+ if ( m_pMainBackground )
+ {
+ m_pMainBackground->SetVisible( !bShowMapInfo );
+ }
+ m_pPlayerData->SetVisible( bIsMVM || !bShowMapInfo );
+ m_pNextTipButton->SetVisible( m_bInteractive && !bShowMapInfo );
+ m_pResetStatsButton->SetVisible( m_bInteractive && !bShowMapInfo );
+
+ if ( m_pMapInfoPanel )
+ {
+ m_pMapInfoPanel->SetVisible( bShowMapInfo );
+ vgui::Panel* pInfoBG = m_pMapInfoPanel->FindChildByName( "InfoBG" );
+ if ( pInfoBG )
+ {
+ pInfoBG->SetVisible( bShowMapInfo && !bIsMVM && !bBackgroundOverride );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnMapLoad( const char *pMapName )
+{
+ if ( g_bIsReplayRewinding || engine->IsLoadingDemo() || engine->IsPlayingDemo() || engine->IsSkippingPlayback() )
+ return;
+
+ bool bWidescreenBackground = false;
+
+ bool bIsMVM = ( pMapName && !Q_strncmp( pMapName, "mvm_", 4 ) );
+ const char *pszBackgroundOverride = NULL;
+ const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
+ if ( pMatchDesc )
+ {
+ int screenWide, screenTall;
+ surface()->GetScreenSize( screenWide, screenTall );
+ float aspectRatio = (float)screenWide/(float)screenTall;
+ bool bWideScreen = aspectRatio >= 1.5999f;
+
+ // Check if there's a widescreen override
+ if( bWideScreen )
+ {
+ pszBackgroundOverride = pMatchDesc->GetMapLoadBackgroundOverride( true );
+ if ( pszBackgroundOverride )
+ {
+ // Success! We're done
+ bWidescreenBackground = true;
+ }
+ }
+
+ if ( !bWideScreen && !pszBackgroundOverride )
+ {
+ pszBackgroundOverride = pMatchDesc->GetMapLoadBackgroundOverride( false );
+ }
+
+ }
+ else if ( bIsMVM )
+ {
+ // this will preserve the current behavior for non-matchmaking servers
+ pszBackgroundOverride = "mvm_background_map";
+ }
+
+ bool bIsCommunityMap = false;
+ const char *pAuthors = NULL;
+
+ const MapDef_t *pMapInfo = GetItemSchema()->GetMasterMapDefByName( pMapName );
+ if ( pMapInfo )
+ {
+ bIsCommunityMap = pMapInfo->IsCommunityMap();
+ pAuthors = pMapInfo->pszAuthorsLocKey;
+ }
+
+ ShowMapInfo( true, bIsMVM, ( pszBackgroundOverride != NULL ) );
+
+ m_xStartLeaderboard = 0;
+ m_yStartLeaderboard = 0;
+
+ // If we're loading a background map, don't display anything
+ // HACK: Client doesn't get gpGlobals->eLoadType, so just do string compare for now.
+ if ( Q_stristr( pMapName, "background") )
+ {
+ ClearMapLabel();
+ }
+ else
+ {
+ // set the map name in the UI
+ wchar_t wzMapName[255]=L"";
+ g_pVGuiLocalize->ConvertANSIToUnicode( GetMapDisplayName( pMapName ), wzMapName, sizeof( wzMapName ) );
+
+ SetDialogVariable( "maplabel", wzMapName );
+ SetDialogVariable( "maptype", g_pVGuiLocalize->Find( GetMapType( pMapName ) ) );
+
+ vgui::Label *pLabel = dynamic_cast<Label *>( FindChildByName( "OnYourWayLabel" ) );
+ if ( pLabel && !pLabel->IsVisible() )
+ {
+ pLabel->SetVisible( true );
+ }
+
+ pLabel = dynamic_cast<Label *>( FindChildByName( "MapType" ) );
+ if ( pLabel && !pLabel->IsVisible() )
+ {
+ pLabel->SetVisible( true );
+ }
+
+ ImagePanel *pMapImage = m_pMapInfoPanel ? dynamic_cast< ImagePanel *>( m_pMapInfoPanel->FindChildByName( "MapImage" ) ) : NULL;
+ if ( pMapImage )
+ {
+ // load the map image (if it exists for the current map)
+ char szMapImage[ MAX_PATH ];
+ Q_snprintf( szMapImage, sizeof( szMapImage ), "VGUI/maps/menu_photos_%s", pMapName );
+ Q_strlower( szMapImage );
+
+ IMaterial *pMapMaterial = materials->FindMaterial( szMapImage, TEXTURE_GROUP_VGUI, false );
+ if ( pMapMaterial && !IsErrorMaterial( pMapMaterial ) && !pszBackgroundOverride )
+ {
+ // take off the vgui/ at the beginning when we set the image
+ Q_snprintf( szMapImage, sizeof( szMapImage ), "maps/menu_photos_%s", pMapName );
+ Q_strlower( szMapImage );
+ pMapImage->SetImage( szMapImage );
+ pMapImage->SetVisible( true );
+ }
+ else
+ {
+ pMapImage->SetVisible( false );
+ }
+ }
+
+ ImagePanel *pBackgroundImage = m_pMapInfoPanel ? dynamic_cast< ImagePanel *>( m_pMapInfoPanel->FindChildByName( "Background" ) ) : NULL;
+ if ( pBackgroundImage )
+ {
+ const char* pszBackgroundImage = pszBackgroundOverride ? pszBackgroundOverride : "stamp_background_map";
+
+ pBackgroundImage->SetImage( pszBackgroundImage );
+
+ // Resize to accomodate the background image coming in
+ if ( bWidescreenBackground )
+ {
+ pBackgroundImage->SetWide( GetWide() );
+ }
+ else
+ {
+ pBackgroundImage->SetWide( GetTall() * ( 4.f / 3.f ) );
+ }
+
+ }
+
+ if ( bIsMVM )
+ {
+ UpdateClassDetails( true );
+ m_pMapInfoPanel->SetDialogVariable( "map_leaderboard_title", "" );
+ m_pMapInfoPanel->SetDialogVariable( "title", "" );
+ m_pMapInfoPanel->SetDialogVariable( "authors", "" );
+
+ FOR_EACH_VEC( m_vecLeaderboardEntries, i )
+ {
+ EditablePanel *pContainer = dynamic_cast< EditablePanel* >( m_vecLeaderboardEntries[i] );
+ if ( pContainer )
+ {
+ pContainer->SetVisible( false );
+ }
+ }
+ }
+ else
+ {
+ m_pLeaderboardTitle = NULL;
+ // add authors
+ if ( m_pMapInfoPanel )
+ {
+ if ( bIsCommunityMap )
+ {
+ m_pMapInfoPanel->SetDialogVariable( "title", g_pVGuiLocalize->Find( "#TF_MapAuthors_Community_Title" ) );
+ m_pMapInfoPanel->SetDialogVariable( "map_leaderboard_title", "" );
+ m_pMapInfoPanel->SetDialogVariable( "authors", g_pVGuiLocalize->Find( pAuthors ) );
+ m_pLeaderboardTitle = m_pMapInfoPanel->FindChildByName( "MapLeaderboardTitle" );
+ }
+ else
+ {
+ m_pMapInfoPanel->SetDialogVariable( "title", g_pVGuiLocalize->Find( "#TF_DuelLeaderboard_Title" ) );
+ m_pMapInfoPanel->SetDialogVariable( "map_leaderboard_title", "" );
+ m_pMapInfoPanel->SetDialogVariable( "authors", "" );
+ m_pLeaderboardTitle = m_pMapInfoPanel->FindChildByName( "Title" );
+ }
+ }
+ if ( m_pLeaderboardTitle )
+ {
+ m_pLeaderboardTitle->GetPos( m_xStartLeaderboard, m_yStartLeaderboard );
+ m_yStartLeaderboard += m_pLeaderboardTitle->GetTall();
+ }
+
+ // request leaderboard data
+ m_bShowingLeaderboard = true;
+ if ( bIsCommunityMap )
+ {
+ MapInfo_RefreshLeaderboard( pMapName );
+ }
+ else
+ {
+ Leaderboards_Refresh();
+ }
+ m_bLoadingCommunityMap = bIsCommunityMap;
+
+ if ( m_pContributedPanel && steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamFriends() )
+ {
+ int iDonationAmount = MapInfo_GetDonationAmount( steamapicontext->SteamUser()->GetSteamID().GetAccountID(), pMapName );
+ m_pContributedPanel->SetVisible( iDonationAmount != 0 );
+ if ( iDonationAmount != 0 )
+ {
+ m_pContributedPanel->SetDialogVariable( "playername", steamapicontext->SteamFriends()->GetPersonaName() );
+ }
+ }
+
+ UpdateLeaderboard();
+ }
+ }
+}
+
+void CTFStatsSummaryPanel::UpdateLeaderboard()
+{
+ if ( m_pMapInfoPanel == NULL || steamapicontext == NULL || steamapicontext->SteamUserStats() == NULL || steamapicontext->SteamUser() == NULL )
+ return;
+
+ const int kMaxVisible_Supporters = 5;
+ const int kIdeallyNumVisible_Supporters = 3;
+ const int kMaxVisible_DuelWins = 10;
+ const int kIdeallyNumVisible_DuelWins = 5;
+
+ // retrieve scores
+ CUtlVector< LeaderboardEntry_t* > scores;
+ bool bVisible = true;
+ int iNumLeaderboardEntries = 0;
+ if ( m_bLoadingCommunityMap )
+ {
+ bVisible = MapInfo_GetLeaderboardInfo( engine->GetLevelName(), scores, iNumLeaderboardEntries, kIdeallyNumVisible_Supporters );
+ wchar_t wzNumEntriesString[256];
+ _snwprintf( wzNumEntriesString, ARRAYSIZE( wzNumEntriesString ), L"%i", iNumLeaderboardEntries );
+ wchar_t wzTitle[256];
+ g_pVGuiLocalize->ConstructString_safe( wzTitle, g_pVGuiLocalize->Find( "#TF_MapDonators_Title" ), 1, wzNumEntriesString );
+ m_pMapInfoPanel->SetDialogVariable( "map_leaderboard_title", wzTitle );
+ }
+ else
+ {
+ bVisible = Leaderboards_GetDuelWins( scores, false );
+ if ( bVisible && scores.Count() < kIdeallyNumVisible_DuelWins )
+ {
+ bVisible = Leaderboards_GetDuelWins( scores, true ) && scores.Count() > 0;
+ }
+ // show old stats
+ m_pPlayerData->SetVisible( bVisible == false );
+ if ( m_pMapInfoPanel )
+ {
+ vgui::Panel* pInfoBG = m_pMapInfoPanel->FindChildByName( "InfoBG" );
+ if ( pInfoBG )
+ {
+ pInfoBG->SetVisible( bVisible );
+ }
+ }
+ }
+
+ const int kMaxVisible = m_bLoadingCommunityMap ? kMaxVisible_Supporters : kMaxVisible_DuelWins;
+
+ // try to show local player in relation to the people in the list
+ if ( bVisible && scores.Count() > 0 && steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamUserStats() )
+ {
+ int iLocalPlayerIdx = -1;
+ CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
+ FOR_EACH_VEC( scores, i )
+ {
+ const LeaderboardEntry_t *leaderboardEntry = scores[i];
+ if ( leaderboardEntry->m_steamIDUser == localSteamID )
+ {
+ iLocalPlayerIdx = i;
+ break;
+ }
+ }
+ // local player is in the list, but is outside the visible range
+ // so we want to move them to the last spot
+ // and move the closest person above them as well
+ if ( iLocalPlayerIdx >= kMaxVisible )
+ {
+ LeaderboardEntry_t *entryLocalPlayer = scores[iLocalPlayerIdx];
+ LeaderboardEntry_t *closestPlayer = scores[iLocalPlayerIdx - 1];
+ scores[kMaxVisible - 1] = entryLocalPlayer;
+ scores[kMaxVisible - 2] = closestPlayer;
+ }
+ }
+
+ // set avatars and names
+ int x = m_xStartLeaderboard;
+ int y = m_yStartLeaderboard;
+ FOR_EACH_VEC( m_vecLeaderboardEntries, i )
+ {
+ EditablePanel *pContainer = dynamic_cast< EditablePanel* >( m_vecLeaderboardEntries[i] );
+ if ( pContainer )
+ {
+ bool bIsEntryVisible = bVisible && i < scores.Count() && i < kMaxVisible;
+ pContainer->SetVisible( bIsEntryVisible );
+ pContainer->SetPos( x, y );
+ y += pContainer->GetTall();
+ if ( bIsEntryVisible )
+ {
+ const LeaderboardEntry_t *leaderboardEntry = scores[i];
+ const CSteamID &steamID = leaderboardEntry->m_steamIDUser;
+ pContainer->SetDialogVariable( "username", CFmtStr( "%d. %s - %d", leaderboardEntry->m_nGlobalRank, InventoryManager()->PersonaName_Get( steamID.GetAccountID() ), leaderboardEntry->m_nScore ) );
+ CAvatarImagePanel *pAvatar = dynamic_cast< CAvatarImagePanel* >( pContainer->FindChildByName( "AvatarImage" ) );
+ if ( pAvatar )
+ {
+ pAvatar->SetShouldDrawFriendIcon( false );
+ pAvatar->SetPlayer( steamID, k_EAvatarSize32x32 );
+ }
+ }
+ }
+ }
+
+ if ( m_pLeaderboardTitle )
+ {
+ bool bShowTitle = bVisible && scores.Count() > 0;
+ if ( m_pLeaderboardTitle->IsVisible() != bShowTitle )
+ {
+ m_pLeaderboardTitle->SetVisible( bShowTitle );
+ }
+ }
+
+ m_bShowingLeaderboard = bVisible;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the dialog
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateDialog()
+{
+ UpdateMainBackground();
+
+ if ( g_bIsReplayRewinding || engine->IsLoadingDemo() || engine->IsPlayingDemo() || engine->IsSkippingPlayback() )
+ {
+ // hide all of the various panels for the other loadscreen modes
+ if ( IsPC() )
+ {
+ ClearMapLabel();
+
+ m_pPlayerData->SetVisible( false );
+ m_pNextTipButton->SetVisible( false );
+ m_pResetStatsButton->SetVisible( false );
+ m_pInteractiveHeaders->SetVisible( false );
+ m_pNonInteractiveHeaders->SetVisible( false );
+ m_pTipText->SetVisible( false );
+ m_pTipImage->SetVisible( false );
+
+ if ( m_pMapInfoPanel )
+ {
+ m_pMapInfoPanel->SetVisible( false );
+
+ vgui::Panel* pInfoBG = m_pMapInfoPanel->FindChildByName( "InfoBG" );
+ if ( pInfoBG )
+ {
+ pInfoBG->SetVisible( false );
+ }
+ }
+ }
+
+ return;
+ }
+
+ RandomSeed( Plat_MSTime() );
+
+ m_iTotalSpawns = 0;
+
+ // if we don't have stats for any class, add empty stat entries for them
+ for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass <= TF_LAST_NORMAL_CLASS; iClass++ )
+ {
+ if ( iClass == TF_CLASS_CIVILIAN )
+ continue; // Ignore the civilian.
+
+ int j;
+ for ( j = 0; j < m_aClassStats.Count(); j++ )
+ {
+ if ( m_aClassStats[j].iPlayerClass == iClass )
+ {
+ m_iTotalSpawns += m_aClassStats[j].iNumberOfRounds;
+ break;
+ }
+ }
+ if ( j == m_aClassStats.Count() )
+ {
+ ClassStats_t stats;
+ stats.iPlayerClass = iClass;
+ m_aClassStats.AddToTail( stats );
+ }
+ }
+
+ ClearMapLabel();
+
+#ifdef _X360
+ if ( m_pFooter )
+ {
+ m_pFooter->ShowButtonLabel( "nexttip", m_bShowBackButton );
+ m_pFooter->ShowButtonLabel( "back", m_bShowBackButton );
+ }
+#endif
+
+ // fill out bar charts
+ UpdateBarCharts();
+ // fill out class details
+ UpdateClassDetails();
+ // update the tip
+ UpdateTip();
+ // show or hide controls depending on if we're interactive or not
+ UpdateControls();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates bar charts
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateBarCharts()
+{
+ // sort the class stats by the selected stat for right-hand bar chart
+ m_aClassStats.Sort( &CTFStatsSummaryPanel::CompareClassStats );
+
+ // loop for left & right hand charts
+ for ( int iChart = 0; iChart < 2; iChart++ )
+ {
+ float flMax = 0;
+ for ( int i = 0; i < m_aClassStats.Count(); i++ )
+ {
+ // get max value of stat being charted so we know how to scale the graph
+ float flVal = GetDisplayValue( m_aClassStats[i], m_statBarGraph[iChart], m_displayBarGraph[iChart] );
+ flMax = MAX( flVal, flMax );
+ }
+
+ // draw the bar chart value for each player class
+ // TODO: Fix up after the civilian becomes playable.
+ int iChartBar = 0;
+ for ( int i = 0; i < m_aClassStats.Count(); i++ )
+ {
+ int iClass = m_aClassStats[i].iPlayerClass;
+ if ( iClass == TF_CLASS_CIVILIAN )
+ {
+ continue;
+ }
+ if ( 0 == iChart )
+ {
+ // if this is the first chart, set the class label for each class
+ m_pPlayerData->SetDialogVariable( CFmtStr( "class%d", iChartBar+1 ), g_pVGuiLocalize->Find( g_aPlayerClassNames[iClass] ) );
+ }
+ // draw the bar for this class
+ DisplayBarValue( iChart, iChartBar++, m_aClassStats[i], m_statBarGraph[iChart], m_displayBarGraph[iChart], flMax );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates class details
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateClassDetails( bool bIsMVM )
+{
+ vgui::Label *pTitle = assert_cast< vgui::Label* >( FindChildByName( "RecordsLabel1", true ) );
+ if ( pTitle )
+ {
+ pTitle->SetText( bIsMVM ? "#StatSummary_Label_BestMVMMoments" : "#StatSummary_Label_BestMoments" );
+ }
+
+ const wchar_t *wzWithClassFmt = g_pVGuiLocalize->Find( "#StatSummary_ScoreAsClassFmt" );
+ const wchar_t *wzWithoutClassFmt = L"%s1";
+
+ ClassDetails_t *pStatDetails = ( bIsMVM ? g_PerClassMVMStatDetails : g_PerClassStatDetails );
+ int nArraySize = ( bIsMVM ? ARRAYSIZE( g_PerClassMVMStatDetails ) : ARRAYSIZE( g_PerClassStatDetails ) );
+
+ // display the record for each stat
+ int iRow = 0;
+ for ( int i = 0; i < nArraySize; i++ )
+ {
+ TFStatType_t statType = pStatDetails[i].statType;
+
+ int iClass = TF_CLASS_UNDEFINED;
+ int iMaxVal = 0;
+
+ // if there is a selected class, and if this stat should not be shown for this class, skip this stat
+ if ( m_iSelectedClass != TF_CLASS_UNDEFINED && ( 0 == ( pStatDetails[i].iFlagsClass & MAKESTATFLAG( m_iSelectedClass ) ) ) )
+ continue;
+
+ if ( m_iSelectedClass == TF_CLASS_UNDEFINED )
+ {
+ // if showing best from any class, look through all player classes to determine the max value of this stat
+ for ( int j = 0; j < m_aClassStats.Count(); j++ )
+ {
+ RoundStats_t *pRoundStats = &( bIsMVM ? m_aClassStats[j].maxMVM : m_aClassStats[j].max );
+ if ( pRoundStats->m_iStat[statType] > iMaxVal )
+ {
+ // remember max value and class that has max value
+ iMaxVal = pRoundStats->m_iStat[statType];
+ iClass = m_aClassStats[j].iPlayerClass;
+ }
+ }
+ }
+ else
+ {
+ // show best from selected class
+ iClass = m_iSelectedClass;
+ for ( int j = 0; j < m_aClassStats.Count(); j++ )
+ {
+ if ( m_aClassStats[j].iPlayerClass == iClass )
+ {
+ RoundStats_t *pRoundStats = &( bIsMVM ? m_aClassStats[j].maxMVM : m_aClassStats[j].max );
+ iMaxVal = pRoundStats->m_iStat[statType];
+ break;
+ }
+ }
+ }
+
+ wchar_t wzStatNum[32];
+ wchar_t wzStatVal[128];
+ if ( TFSTAT_PLAYTIME == statType )
+ {
+ // playtime gets displayed as a time string
+ g_pVGuiLocalize->ConvertANSIToUnicode( FormatSeconds( iMaxVal ), wzStatNum, sizeof( wzStatNum ) );
+ }
+ else
+ {
+ // all other stats are just shown as a #
+ swprintf_s( wzStatNum, ARRAYSIZE( wzStatNum ), L"%d", iMaxVal );
+ }
+
+ if ( TF_CLASS_UNDEFINED == m_iSelectedClass && iMaxVal > 0 )
+ {
+ // if we are doing a cross-class view (no single selected class) and the max value is non-zero, show "# (as <class>)"
+ wchar_t *wzLocalizedClassName = g_pVGuiLocalize->Find( g_aPlayerClassNames[iClass] );
+ g_pVGuiLocalize->ConstructString_safe( wzStatVal, wzWithClassFmt, 2, wzStatNum, wzLocalizedClassName );
+ }
+ else
+ {
+ // just show the value
+ g_pVGuiLocalize->ConstructString_safe( wzStatVal, wzWithoutClassFmt, 1, wzStatNum );
+ }
+
+ // set the label
+ m_pPlayerData->SetDialogVariable( CFmtStr( "classrecord%dlabel", iRow+1 ), g_pVGuiLocalize->Find( pStatDetails[i].szResourceName ) );
+ // set the value
+ m_pPlayerData->SetDialogVariable( CFmtStr( "classrecord%dvalue", iRow+1 ), wzStatVal );
+
+ iRow++;
+ }
+
+ // if there are any leftover rows for the selected class, fill out the remaining rows with blank labels and values
+ for ( ; iRow < 15; iRow ++ )
+ {
+ m_pPlayerData->SetDialogVariable( CFmtStr( "classrecord%dlabel", iRow+1 ), "" );
+ m_pPlayerData->SetDialogVariable( CFmtStr( "classrecord%dvalue", iRow+1 ), "" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the tip
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateTip()
+{
+ int iTipClass = TF_CLASS_UNDEFINED;
+
+ SetDialogVariable( "tiptext", g_TFTips.GetRandomTip( iTipClass ) );
+
+ if ( m_pTipImage )
+ {
+ if ( iTipClass > TF_CLASS_UNDEFINED && iTipClass <= TF_CLASS_ENGINEER )
+ {
+ m_pTipImage->SetVisible( true );
+ m_pTipImage->SetImage( g_pszTipsClassImages[iTipClass] );
+ }
+ else
+ {
+ m_pTipImage->SetVisible( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shows or hides controls
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::UpdateControls()
+{
+ // show or hide controls depending on what mode we're in
+#ifndef _X360
+ bool bShowPlayerData = ( m_bInteractive || m_iTotalSpawns > 0 );
+#else
+ bool bShowPlayerData = ( m_bInteractive || m_bShowBackButton || m_iTotalSpawns > 0 );
+#endif
+ m_pPlayerData->SetVisible( bShowPlayerData );
+ m_pInteractiveHeaders->SetVisible( m_bInteractive );
+ m_pNonInteractiveHeaders->SetVisible( !m_bInteractive );
+ m_pTipText->SetVisible( bShowPlayerData );
+ m_pTipImage->SetVisible( bShowPlayerData );
+
+ if ( !IsX360() )
+ {
+ if ( !m_bInteractive )
+ {
+ char szTemp[128];
+
+ // update our non-interactive headers to match the current combo box selections
+ Label *pLabel = dynamic_cast<Label *>( m_pNonInteractiveHeaders->FindChildByName( "BarChartLabelA" ) );
+ if ( pLabel && m_pBarChartComboBoxA )
+ {
+ m_pBarChartComboBoxA->GetItemText( m_pBarChartComboBoxA->GetActiveItem(), szTemp, sizeof( szTemp ) );
+ pLabel->SetText( szTemp );
+ }
+
+ pLabel = dynamic_cast<Label *>( m_pNonInteractiveHeaders->FindChildByName( "BarChartLabelB" ) );
+ if ( pLabel && m_pBarChartComboBoxB )
+ {
+ m_pBarChartComboBoxB->GetItemText( m_pBarChartComboBoxB->GetActiveItem(), szTemp, sizeof( szTemp ) );
+ pLabel->SetText( szTemp );
+ }
+
+ pLabel = dynamic_cast<Label *>( m_pNonInteractiveHeaders->FindChildByName( "OverallRecordLabel" ) );
+ if ( pLabel && m_pClassComboBox )
+ {
+ m_pClassComboBox->GetItemText( m_pClassComboBox->GetActiveItem(), szTemp, sizeof( szTemp ) );
+ pLabel->SetText( szTemp );
+ }
+ }
+ }
+
+#ifndef _X360
+ m_pNextTipButton->SetVisible( m_bInteractive );
+ m_pResetStatsButton->SetVisible( m_bInteractive );
+ m_pCloseButton->SetVisible( m_bInteractive && !m_bEmbedded );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes a bar chart combo box
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::InitBarChartComboBox( ComboBox *pComboBox )
+{
+ struct BarChartComboInit_t
+ {
+ TFStatType_t statType;
+ StatDisplay_t statDisplay;
+ const char *szName;
+ };
+
+ BarChartComboInit_t initData[] =
+ {
+ { TFSTAT_POINTSSCORED, SHOW_MAX, "#StatSummary_StatTitle_MostPoints" },
+ { TFSTAT_POINTSSCORED, SHOW_AVG, "#StatSummary_StatTitle_AvgPoints" },
+ { TFSTAT_KILLS, SHOW_MAX, "#StatSummary_StatTitle_MostKills" },
+ { TFSTAT_KILLS, SHOW_AVG, "#StatSummary_StatTitle_AvgKills" },
+ { TFSTAT_CAPTURES, SHOW_MAX, "#StatSummary_StatTitle_MostCaptures" },
+ { TFSTAT_CAPTURES, SHOW_AVG, "#StatSummary_StatTitle_AvgCaptures" },
+ { TFSTAT_KILLASSISTS, SHOW_MAX, "#StatSummary_StatTitle_MostAssists" },
+ { TFSTAT_KILLASSISTS, SHOW_AVG, "#StatSummary_StatTitle_AvgAssists" },
+ { TFSTAT_DAMAGE, SHOW_MAX, "#StatSummary_StatTitle_MostDamage" },
+ { TFSTAT_DAMAGE, SHOW_AVG, "#StatSummary_StatTitle_AvgDamage" },
+ { TFSTAT_PLAYTIME, SHOW_TOTAL, "#StatSummary_StatTitle_TotalPlaytime" },
+ { TFSTAT_PLAYTIME, SHOW_MAX, "#StatSummary_StatTitle_LongestLife" },
+ };
+
+ // set the font
+ HFont hFont = scheme()->GetIScheme( GetScheme() )->GetFont( "ScoreboardVerySmall", true );
+ pComboBox->SetFont( hFont );
+ pComboBox->RemoveAll();
+ // add all the options to the combo box
+ for ( int i=0; i < ARRAYSIZE( initData ); i++ )
+ {
+ KeyValues *pKeyValues = new KeyValues( "data" );
+ pKeyValues->SetInt( "stattype", initData[i].statType );
+ pKeyValues->SetInt( "statdisplay", initData[i].statDisplay );
+ pComboBox->AddItem( g_pVGuiLocalize->Find( initData[i].szName ), pKeyValues );
+ }
+ pComboBox->SetNumberOfEditLines( ARRAYSIZE( initData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper function that sets the specified dialog variable to
+// "<value> (as <localized class name>)"
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::SetValueAsClass( const char *pDialogVariable, int iValue, int iPlayerClass )
+{
+ if ( iValue > 0 )
+ {
+ wchar_t *wzScoreAsClassFmt = g_pVGuiLocalize->Find( "#StatSummary_ScoreAsClassFmt" );
+ wchar_t *wzLocalizedClassName = g_pVGuiLocalize->Find( g_aPlayerClassNames[iPlayerClass] );
+ wchar_t wzVal[16];
+ wchar_t wzMsg[128];
+ swprintf( wzVal, ARRAYSIZE( wzVal ), L"%d", iValue );
+ g_pVGuiLocalize->ConstructString_safe( wzMsg, wzScoreAsClassFmt, 2, wzVal, wzLocalizedClassName );
+ m_pPlayerData->SetDialogVariable( pDialogVariable, wzMsg );
+ }
+ else
+ {
+ m_pPlayerData->SetDialogVariable( pDialogVariable, "0" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the specified bar chart item to the specified value, in range 0->1
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::DisplayBarValue( int iChart, int iBar, ClassStats_t &stats, TFStatType_t statType, StatDisplay_t statDisplay, float flMaxValue )
+{
+ const char *szControlSuffix = ( 0 == iChart ? "A" : "B" );
+ Panel *pBar = m_pPlayerData->FindChildByName( CFmtStr( "ClassBar%d%s", iBar+1, szControlSuffix ) );
+ Label *pLabel = dynamic_cast<Label*>( m_pPlayerData->FindChildByName( CFmtStr( "classbarlabel%d%s", iBar+1, szControlSuffix ) ) );
+ if ( !pBar || !pLabel )
+ return;
+
+ // get the stat value
+ float flValue = GetDisplayValue( stats, statType, statDisplay );
+ // calculate the bar size to draw, in the range of 0.0->1.0
+ float flBarRange = SafeCalcFraction( flValue, flMaxValue );
+ // calculate the # of pixels of bar width to draw
+ int iBarWidth = MAX( (int) ( flBarRange * (float) m_iBarMaxWidth ), 1 );
+
+ // Get the text label to draw for this bar. For values of 0, draw nothing, to minimize clutter
+ const char *szLabel = ( flValue > 0 ? RenderValue( flValue, statType, statDisplay ) : "" );
+ // draw the label outside the bar if there's room
+ bool bLabelOutsideBar = true;
+ const int iLabelSpacing = 4;
+ HFont hFont = pLabel->GetFont();
+ int iLabelWidth = UTIL_ComputeStringWidth( hFont, szLabel );
+ if ( iBarWidth + iLabelWidth + iLabelSpacing > m_iBarMaxWidth )
+ {
+ // if there's not room outside the bar for the label, draw it inside the bar
+ bLabelOutsideBar = false;
+ }
+
+ int xBar,yBar,xLabel,yLabel;
+ pBar->GetPos( xBar,yBar );
+ pLabel->GetPos( xLabel,yLabel );
+
+ m_pPlayerData->SetDialogVariable( CFmtStr( "classbarlabel%d%s", iBar+1, szControlSuffix ), szLabel );
+ if ( 1 == iChart )
+ {
+ // drawing code for RH bar chart
+ xBar = m_xStartRHBar;
+ pBar->SetBounds( xBar, yBar, iBarWidth, m_iBarHeight );
+ if ( bLabelOutsideBar )
+ {
+ pLabel->SetPos( xBar + iBarWidth + iLabelSpacing, yLabel );
+ }
+ else
+ {
+ pLabel->SetPos( xBar + iBarWidth - ( iLabelWidth + iLabelSpacing ), yLabel );
+ }
+ }
+ else
+ {
+ // drawing code for LH bar chart
+ xBar = m_xStartLHBar + m_iBarMaxWidth - iBarWidth;
+ pBar->SetBounds( xBar, yBar, iBarWidth, m_iBarHeight );
+ if ( bLabelOutsideBar )
+ {
+ pLabel->SetPos( xBar - ( iLabelWidth + iLabelSpacing ), yLabel );
+ }
+ else
+ {
+ pLabel->SetPos( xBar + iLabelSpacing, yLabel );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculates a fraction and guards from divide by 0. (Returns 0 if
+// denominator is 0.)
+//-----------------------------------------------------------------------------
+float CTFStatsSummaryPanel::SafeCalcFraction( float flNumerator, float flDemoninator )
+{
+ if ( 0 == flDemoninator )
+ return 0;
+ return flNumerator / flDemoninator;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Formats # of seconds into a string
+//-----------------------------------------------------------------------------
+const char *FormatSeconds( int seconds )
+{
+ static char string[64];
+
+ int hours = 0;
+ int minutes = seconds / 60;
+
+ if ( minutes > 0 )
+ {
+ seconds -= (minutes * 60);
+ hours = minutes / 60;
+
+ if ( hours > 0 )
+ {
+ minutes -= (hours * 60);
+ }
+ }
+
+ if ( hours > 0 )
+ {
+ Q_snprintf( string, sizeof(string), "%2i:%02i:%02i", hours, minutes, seconds );
+ }
+ else
+ {
+ Q_snprintf( string, sizeof(string), "%02i:%02i", minutes, seconds );
+ }
+
+ return string;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static sort function that sorts in descending order by play time
+//-----------------------------------------------------------------------------
+int __cdecl CTFStatsSummaryPanel::CompareClassStats( const ClassStats_t *pStats0, const ClassStats_t *pStats1 )
+{
+ // sort stats first by right-hand bar graph
+ TFStatType_t statTypePrimary = GStatsSummaryPanel()->m_statBarGraph[1];
+ StatDisplay_t statDisplayPrimary = GStatsSummaryPanel()->m_displayBarGraph[1];
+ // then by left-hand bar graph
+ TFStatType_t statTypeSecondary = GStatsSummaryPanel()->m_statBarGraph[0];
+ StatDisplay_t statDisplaySecondary = GStatsSummaryPanel()->m_displayBarGraph[0];
+
+ float flValPrimary0 = GetDisplayValue( (ClassStats_t &) *pStats0, statTypePrimary, statDisplayPrimary );
+ float flValPrimary1 = GetDisplayValue( (ClassStats_t &) *pStats1, statTypePrimary, statDisplayPrimary );
+ float flValSecondary0 = GetDisplayValue( (ClassStats_t &) *pStats0, statTypeSecondary, statDisplaySecondary );
+ float flValSecondary1 = GetDisplayValue( (ClassStats_t &) *pStats1, statTypeSecondary, statDisplaySecondary );
+
+ // sort in descending order by primary stat value
+ if ( flValPrimary1 > flValPrimary0 )
+ return 1;
+ if ( flValPrimary1 < flValPrimary0 )
+ return -1;
+
+ // if primary stat values are equal, sort in descending order by secondary stat value
+ if ( flValSecondary1 > flValSecondary0 )
+ return 1;
+ if ( flValSecondary1 < flValSecondary0 )
+ return -1;
+
+ // if primary & secondary stats are equal, sort by class for consistent sort order
+ return ( pStats1->iPlayerClass - pStats0->iPlayerClass );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when text changes in combo box
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
+ vgui::ComboBox *pComboBox = dynamic_cast<vgui::ComboBox *>( pPanel );
+
+ if ( m_pBarChartComboBoxA == pComboBox || m_pBarChartComboBoxB == pComboBox )
+ {
+ // a bar chart combo box changed, update the bar charts
+
+ KeyValues *pUserDataA = m_pBarChartComboBoxA->GetActiveItemUserData();
+ KeyValues *pUserDataB = m_pBarChartComboBoxB->GetActiveItemUserData();
+ if ( !pUserDataA || !pUserDataB )
+ return;
+ m_statBarGraph[0] = (TFStatType_t) pUserDataA->GetInt( "stattype" );
+ m_displayBarGraph[0] = (StatDisplay_t) pUserDataA->GetInt( "statdisplay" );
+ m_statBarGraph[1] = (TFStatType_t) pUserDataB->GetInt( "stattype" );
+ m_displayBarGraph[1] = (StatDisplay_t) pUserDataB->GetInt( "statdisplay" );
+ UpdateBarCharts();
+ }
+ else if ( m_pClassComboBox == pComboBox )
+ {
+ // the class selection combo box changed, update class details
+
+ KeyValues *pUserData = m_pClassComboBox->GetActiveItemUserData();
+ if ( !pUserData )
+ return;
+
+ m_iSelectedClass = pUserData->GetInt( "class", TF_CLASS_UNDEFINED );
+
+ UpdateClassDetails();
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Command target handler called from reset stats confirmation query box
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::DoResetStats()
+{
+#ifndef _X360
+ // reset the stats
+ engine->ClientCmd( "resetplayerstats" );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the stat value for specified display type
+//-----------------------------------------------------------------------------
+float CTFStatsSummaryPanel::GetDisplayValue( ClassStats_t &stats, TFStatType_t statType, StatDisplay_t statDisplay )
+{
+ switch ( statDisplay )
+ {
+ case SHOW_MAX:
+ return stats.max.m_iStat[statType];
+ break;
+ case SHOW_TOTAL:
+ return stats.accumulated.m_iStat[statType];
+ break;
+ case SHOW_AVG:
+ return SafeCalcFraction( stats.accumulated.m_iStat[statType], stats.iNumberOfRounds );
+ break;
+ default:
+ AssertOnce( false );
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the text representation of this value
+//-----------------------------------------------------------------------------
+const char *CTFStatsSummaryPanel::RenderValue( float flValue, TFStatType_t statType, StatDisplay_t statDisplay )
+{
+ static char szValue[64];
+ if ( TFSTAT_PLAYTIME == statType )
+ {
+ // the playtime stat is shown in seconds
+ return FormatSeconds( (int) flValue );
+ }
+ else if ( SHOW_AVG == statDisplay )
+ {
+ // if it's an average, render as a float w/2 decimal places
+ Q_snprintf( szValue, ARRAYSIZE( szValue ), "%.2f", flValue );
+ }
+ else
+ {
+ // otherwise, render as an integer
+ Q_snprintf( szValue, ARRAYSIZE( szValue ), "%d", (int) flValue );
+ }
+
+ return szValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Event handler
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::FireGameEvent( IGameEvent *event )
+{
+ const char *pEventName = event->GetName();
+
+ // when we are changing levels and
+ if ( 0 == Q_strcmp( pEventName, "server_spawn" ) )
+ {
+ if ( !m_bInteractive )
+ {
+ const char *pMapName = event->GetString( "mapname" );
+ if ( pMapName )
+ {
+ OnMapLoad( pMapName );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when we are activated during level load
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnActivate()
+{
+ ClearMapLabel();
+
+ m_bShowingLeaderboard = false;
+ m_bLoadingCommunityMap = false;
+ ShowMapInfo( false );
+
+#ifdef _X360
+ m_bShowBackButton = false;
+#endif
+
+ UpdateDialog();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when we are deactivated at end of level load
+//-----------------------------------------------------------------------------
+void CTFStatsSummaryPanel::OnDeactivate()
+{
+ ClearMapLabel();
+}
+
+CON_COMMAND( showstatsdlg, "Shows the player stats dialog" )
+{
+#ifdef _DEBUG
+ GStatsSummaryPanel()->InvalidateLayout( false, true );
+#endif
+ GStatsSummaryPanel()->ShowModal();
+#ifdef _DEBUG
+ GStatsSummaryPanel()->OnMapLoad( "cp_coldfront" );
+#endif
+}