You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1506 lines
50 KiB
1506 lines
50 KiB
//========= 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 |
|
}
|
|
|