//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //============================================================================= #include "cbase.h" #include #include #include #include #include #include #include #include #include #include #include "ienginevgui.h" #include #include "tf_tips.h" #include "tf_mapinfo.h" #include "vgui_avatarimage.h" #include "VGuiMatSurface/IMatSystemSurface.h" #include "tf_statsummary.h" #include #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 g_vecStatPanels; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void UpdateStatSummaryPanels( CUtlVector &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( 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 &vecClassStats ) { m_aClassStats = vecClassStats; if ( m_bControlsLoaded ) { UpdateDialog(); } } //----------------------------------------------------------------------------- // Purpose: Updates the dialog //----------------------------------------------------------------------------- void CTFStatsSummaryPanel::ClearMapLabel() { SetDialogVariable( "maplabel", "" ); SetDialogVariable( "maptype", "" ); vgui::Label *pLabel = dynamic_cast