summaryrefslogtreecommitdiff
path: root/gameui/matchmaking
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gameui/matchmaking
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'gameui/matchmaking')
-rw-r--r--gameui/matchmaking/achievementsdialog.cpp1075
-rw-r--r--gameui/matchmaking/achievementsdialog.h182
-rw-r--r--gameui/matchmaking/basedialog.cpp295
-rw-r--r--gameui/matchmaking/basedialog.h120
-rw-r--r--gameui/matchmaking/dialogmenu.cpp1524
-rw-r--r--gameui/matchmaking/dialogmenu.h367
-rw-r--r--gameui/matchmaking/leaderboarddialog.cpp563
-rw-r--r--gameui/matchmaking/leaderboarddialog.h59
-rw-r--r--gameui/matchmaking/matchmakingbasepanel.cpp999
-rw-r--r--gameui/matchmaking/matchmakingbasepanel.h114
-rw-r--r--gameui/matchmaking/pausedialog.cpp47
-rw-r--r--gameui/matchmaking/pausedialog.h30
-rw-r--r--gameui/matchmaking/sessionbrowserdialog.cpp323
-rw-r--r--gameui/matchmaking/sessionbrowserdialog.h46
-rw-r--r--gameui/matchmaking/sessionlobbydialog.cpp739
-rw-r--r--gameui/matchmaking/sessionlobbydialog.h85
-rw-r--r--gameui/matchmaking/sessionoptionsdialog.cpp420
-rw-r--r--gameui/matchmaking/sessionoptionsdialog.h61
-rw-r--r--gameui/matchmaking/welcomedialog.cpp58
-rw-r--r--gameui/matchmaking/welcomedialog.h34
20 files changed, 7141 insertions, 0 deletions
diff --git a/gameui/matchmaking/achievementsdialog.cpp b/gameui/matchmaking/achievementsdialog.cpp
new file mode 100644
index 0000000..82931b2
--- /dev/null
+++ b/gameui/matchmaking/achievementsdialog.cpp
@@ -0,0 +1,1075 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Display a list of achievements for the current game
+//
+//=============================================================================//
+
+#include "achievementsdialog.h"
+#include "vgui_controls/Button.h"
+#include "vgui/ILocalize.h"
+#include "ixboxsystem.h"
+#include "iachievementmgr.h"
+#include "EngineInterface.h"
+#include "GameUI_Interface.h"
+#include "MouseMessageForwardingPanel.h"
+#include "filesystem.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/ScrollBar.h"
+#include "BasePanel.h"
+#include "fmtstr.h"
+#include "UtlSortVector.h"
+
+using namespace vgui;
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+KeyValues *g_pPreloadedAchievementItemLayout = NULL;
+
+#define NUM_COMBO_BOX_LINES_DEFAULT 10
+#define NUM_COMBO_BOX_LINES_MAX 16
+
+// Shared helper functions so xbox and pc can share as much code as possible while coming from different bases.
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file.
+//-----------------------------------------------------------------------------
+bool LoadAchievementIcon( vgui::ImagePanel* pIconPanel, IAchievement *pAchievement, const char *pszExt /*= NULL*/ )
+{
+ char imagePath[_MAX_PATH];
+ Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) );
+ Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS );
+ if ( pszExt )
+ {
+ Q_strncat( imagePath, pszExt, sizeof(imagePath), COPY_ALL_CHARACTERS );
+ }
+ Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS );
+
+ char checkFile[_MAX_PATH];
+ Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath );
+ if ( !g_pFullFileSystem->FileExists( checkFile ) )
+ {
+ Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" );
+ }
+
+ pIconPanel->SetShouldScaleImage( true );
+ pIconPanel->SetImage( imagePath );
+ pIconPanel->SetVisible( true );
+
+ return pIconPanel->IsVisible();
+}
+
+//-----------------------------------------------------------------------------
+// The bias is to ensure the percentage bar gets plenty orange before it reaches the text,
+// as the white-on-grey is hard to read.
+//-----------------------------------------------------------------------------
+Color LerpColors ( Color cStart, Color cEnd, float flPercent )
+{
+ float r = (float)((float)(cStart.r()) + (float)(cEnd.r() - cStart.r()) * Bias( flPercent, 0.75 ) );
+ float g = (float)((float)(cStart.g()) + (float)(cEnd.g() - cStart.g()) * Bias( flPercent, 0.75 ) );
+ float b = (float)((float)(cStart.b()) + (float)(cEnd.b() - cStart.b()) * Bias( flPercent, 0.75 ) );
+ float a = (float)((float)(cStart.a()) + (float)(cEnd.a() - cStart.a()) * Bias( flPercent, 0.75 ) );
+ return Color( r, g, b, a );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shares common percentage bar calculations/color settings between xbox and pc.
+// Not really intended for robustness or reuse across many panels.
+// Input : pFrame - assumed to have certain child panels (see below)
+// *pAchievement - source achievement to poll for progress. Non progress achievements will not show a percentage bar.
+//-----------------------------------------------------------------------------
+void UpdateProgressBar( vgui::EditablePanel* pPanel, IAchievement *pAchievement, Color clrProgressBar )
+{
+ if ( pAchievement->GetGoal() > 1 )
+ {
+ bool bShowProgress = true;
+
+ // if this achievement gets saved with game and we're not in a level and have not achieved it, then we do not have any state
+ // for this achievement, don't show progress
+ if ( ( pAchievement->GetFlags() & ACH_SAVE_WITH_GAME ) && !GameUI().IsInLevel() && !pAchievement->IsAchieved() )
+ {
+ bShowProgress = false;
+ }
+
+ float flCompletion = 0.0f;
+
+ // Once achieved, we can't rely on count. If they've completed the achievement just set to 100%.
+ int iCount = pAchievement->GetCount();
+ if ( pAchievement->IsAchieved() )
+ {
+ flCompletion = 1.0f;
+ iCount = pAchievement->GetGoal();
+ }
+ else if ( bShowProgress )
+ {
+ flCompletion = ( ((float)pAchievement->GetCount()) / ((float)pAchievement->GetGoal()) );
+ // In rare cases count can exceed goal and not be achieved (switch local storage on X360, take saved game from different user on PC).
+ // These will self-correct with continued play, but if we're in that state don't show more than 100% achieved.
+ flCompletion = min( flCompletion, 1.f );
+ }
+
+ char szPercentageText[ 256 ] = "";
+ if ( bShowProgress )
+ {
+ Q_snprintf( szPercentageText, 256, "%d/%d", iCount, pAchievement->GetGoal() );
+ }
+
+ pPanel->SetControlString( "PercentageText", szPercentageText );
+ pPanel->SetControlVisible( "PercentageText", true );
+ pPanel->SetControlVisible( "CompletionText", true );
+
+ vgui::ImagePanel *pPercentageBar = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBar" );
+ vgui::ImagePanel *pPercentageBarBkg = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBarBackground" );
+
+ if ( pPercentageBar && pPercentageBarBkg )
+ {
+ pPercentageBar->SetFillColor( clrProgressBar );
+ pPercentageBar->SetWide( pPercentageBarBkg->GetWide() * flCompletion );
+
+ pPanel->SetControlVisible( "PercentageBarBackground", IsX360() ? bShowProgress : true );
+ pPanel->SetControlVisible( "PercentageBar", true );
+ }
+ }
+}
+
+// TODO: revisit this once other games are rebuilt using the updated IAchievement interface
+bool GameSupportsAchievementTracker()
+{
+ const char *pGame = Q_UnqualifiedFileName( engine->GetGameDirectory() );
+ if ( ( Q_stricmp( pGame, "tf" ) == 0 ) || ( Q_stricmp( pGame, "tf_beta" ) == 0 ) )
+ return true;
+
+ return false;
+}
+
+
+//----------------------------------------------------------
+// CAchievementsDialog_XBox
+//----------------------------------------------------------
+
+CAchievementsDialog_XBox::CAchievementsDialog_XBox( vgui::Panel *pParent ) : BaseClass( pParent, "AchievementsDialog" )
+{
+ m_nTotalAchievements = 0;
+ m_nUnlocked = 0;
+ m_iSelection = 0;
+ m_iScroll = 0;
+ m_iNumItems = 0;
+ m_bCenterOnScreen = true;
+
+ m_pProgressBg = new vgui::Panel( this, "ProgressBg" );
+ m_pProgressBar = new vgui::Panel( this, "ProgressBar" );
+ m_pProgressPercent = new vgui::Label( this, "ProgressPercent", "" );
+ m_pNumbering = new vgui::Label( this, "Numbering", "" );
+ m_pUpArrow = new vgui::Label( this, "UpArrow", "" );
+ m_pDownArrow = new vgui::Label( this, "DownArrow", "" );
+
+ SetDeleteSelfOnClose(true);
+ SetSizeable( false );
+
+ m_pFooter = new CFooterPanel( pParent, "AchievementsFooter" );
+}
+
+CAchievementsDialog_XBox::~CAchievementsDialog_XBox()
+{
+ delete m_pProgressBg;
+ delete m_pProgressBar;
+ delete m_pProgressPercent;
+ delete m_pNumbering;
+ delete m_pUpArrow;
+ delete m_pDownArrow;
+
+ delete m_pFooter;
+}
+
+//----------------------------------------------------------
+// Position the dialogs elements
+//----------------------------------------------------------
+void CAchievementsDialog_XBox::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ // Avoid division by zero if the achievements haven't been downloaded yet
+ if ( m_nTotalAchievements )
+ {
+ int x, y, wide, tall;
+ m_pProgressBg->GetBounds( x, y, wide, tall );
+ int iBarX = GetWide() - wide - m_nBorderWidth;
+ int iBarWide = wide * m_nUnlocked / m_nTotalAchievements;
+ m_pProgressBg->SetBounds( iBarX, y, wide, tall );
+ m_pProgressBar->SetBounds( iBarX, y, iBarWide, tall );
+
+ wchar_t wszProgressText[64];
+#ifdef WIN32
+ V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %s", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) );
+#else
+ V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %S", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) );
+#endif
+ m_pProgressPercent->SetText( wszProgressText );
+ m_pProgressPercent->SizeToContents();
+ m_pProgressPercent->SetPos( GetWide() - m_pProgressPercent->GetWide() - m_nBorderWidth, y + tall + 3 );
+
+ int menux, menuy;
+ m_Menu.GetPos( menux, menuy );
+
+ // Do a perform layout on the menu so we get the correct height now
+ m_Menu.InvalidateLayout( true, false );
+
+ int iBottomOfTable = menuy + m_Menu.GetTall() + 3;
+
+ m_pNumbering->SizeToContents();
+ m_pNumbering->SetPos( m_nBorderWidth, iBottomOfTable );
+
+ m_pUpArrow->GetPos( x, y );
+ m_pUpArrow->SetPos( x, iBottomOfTable );
+ m_pDownArrow->GetPos( x, y );
+ m_pDownArrow->SetPos( x, iBottomOfTable );
+
+ wchar_t wszNumbering[64];
+ wchar_t *wzNumberingFmt = g_pVGuiLocalize->Find( "#GameUI_Achievement_Menu_Range" );
+ wchar_t wzActiveItem[8];
+ wchar_t wzTotal[8];
+ V_snwprintf( wzActiveItem, ARRAYSIZE( wzActiveItem ), L"%d", m_Menu.GetActiveItemIndex()+1 );
+ V_snwprintf( wzTotal, ARRAYSIZE( wzTotal ), L"%d", m_nTotalAchievements );
+ g_pVGuiLocalize->ConstructString( wszNumbering, sizeof( wszNumbering ), wzNumberingFmt, 2, wzActiveItem, wzTotal );
+ m_pNumbering->SetText( wszNumbering );
+ m_pNumbering->SetWide( GetWide() );
+ }
+
+ if ( m_bCenterOnScreen )
+ {
+ MoveToCenterOfScreen();
+ }
+
+ // Set the footer
+ m_pFooter->ClearButtons();
+
+ if ( m_pFooterInfo && ( m_pFooterInfo->GetInt( "hide_regular_footer", 0 ) > 0 ) == false )
+ {
+ for ( KeyValues *pButton = m_pFooterInfo->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() )
+ {
+ if ( !Q_stricmp( pButton->GetName(), "button" ) )
+ {
+ // Add a button to the footer
+ const char *pText = pButton->GetString( "text", "NULL" );
+ const char *pIcon = pButton->GetString( "icon", "NULL" );
+ m_pFooter->AddNewButtonLabel( pText, pIcon );
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------
+// Query the system interface for this game's list of achievements
+//----------------------------------------------------------
+void CAchievementsDialog_XBox::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ m_bCenterOnScreen = pResourceData->GetInt( "center", 1 ) == 1;
+
+ if ( !achievementmgr )
+ {
+ AssertOnce( 0 && "No achievement manager interface in GameUI.dll" );
+ return;
+ }
+
+ achievementmgr->EnsureGlobalStateLoaded();
+
+ int iAllAchievements = achievementmgr->GetAchievementCount();
+
+ for ( int i = 0; i < iAllAchievements; ++i )
+ {
+ IAchievement* pCurAchievement = (IAchievement*)achievementmgr->GetAchievementByIndex( i );
+ Assert ( pCurAchievement );
+
+ if ( pCurAchievement->IsAchieved() )
+ ++m_nUnlocked;
+
+ // don't show hidden achievements if not achieved
+ if ( pCurAchievement->ShouldHideUntilAchieved() && !pCurAchievement->IsAchieved() )
+ continue;
+
+ CAchievementItem *pItem = m_Menu.AddAchievementItem( ACHIEVEMENT_LOCALIZED_NAME( pCurAchievement ),
+ ACHIEVEMENT_LOCALIZED_DESC( pCurAchievement ),
+ pCurAchievement->GetPointValue(),
+ pCurAchievement->IsAchieved(),
+ pCurAchievement
+ );
+
+ SETUP_PANEL( pItem );
+ }
+
+ m_nTotalAchievements = m_Menu.GetItemCount();
+}
+
+//----------------------------------------------------------
+// Add the progress bar and update the dialog layout
+//----------------------------------------------------------
+void CAchievementsDialog_XBox::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pProgressBg->SetBgColor( Color( 200, 184, 151, 255 ) );
+ m_pProgressBar->SetBgColor( Color( 179, 82, 22, 255 ) );
+ m_pProgressPercent->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
+ m_pNumbering->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolling up and down with up/down, exit to main menu with back button
+//-----------------------------------------------------------------------------
+void CAchievementsDialog_XBox::OnKeyCodePressed( vgui::KeyCode code )
+{
+ // Handle close here, CBasePanel parent doesn't support "DialogClosing" command
+ if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
+ {
+ OnCommand( "AchievementsDialogClosing" );
+ SetDeleteSelfOnClose( true );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: catch key repeats
+//-----------------------------------------------------------------------------
+void CAchievementsDialog_XBox::HandleKeyRepeated( vgui::KeyCode code )
+{
+ BaseClass::HandleKeyRepeated( code );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fade main menu back in when this dialog closes.
+//-----------------------------------------------------------------------------
+void CAchievementsDialog_XBox::OnClose()
+{
+ BasePanel()->RunCloseAnimation( "CloseAchievementsDialog_OpenMainMenu" );
+ BaseClass::OnClose();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PC Implementation
+//////////////////////////////////////////////////////////////////////////
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: creates child panels, passes down name to pick up any settings from res files.
+//-----------------------------------------------------------------------------
+CAchievementsDialog::CAchievementsDialog(vgui::Panel *parent) : BaseClass(parent, "AchievementsDialog")
+{
+ SetDeleteSelfOnClose(true);
+ SetBounds(0, 0, 512, 384);
+ SetMinimumSize( 256, 300 );
+ SetSizeable( true );
+
+ m_nOldScrollItem = -1;
+ m_nScrollItem = -1;
+
+ m_nUnlocked = 0;
+ m_nTotalAchievements = 0;
+
+ m_pAchievementsList = new vgui::PanelListPanel( this, "listpanel_achievements" );
+ m_pAchievementsList->SetFirstColumnWidth( 0 );
+
+ m_pListBG = new vgui::ImagePanel( this, "listpanel_background" );
+
+ m_pPercentageBarBackground = SETUP_PANEL( new ImagePanel( this, "PercentageBarBackground" ) );
+ m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) );
+
+ m_pSelectionHighlight = new ImagePanel( m_pAchievementsList->FindChildByName( "PanelListEmbedded" ), "SelectionHighlight" );
+ m_pSelectionHighlight->SetVisible( false );
+ m_pSelectionHighlight->SetZPos( 100 );
+
+ m_pAchievementPackCombo = new ComboBox(this, "achievement_pack_combo", NUM_COMBO_BOX_LINES_DEFAULT, false);
+ m_pHideAchievedCheck = new vgui::CheckButton( this, "HideAchieved", "" );
+
+ // int that holds the highest number achievement id we've found
+ int iHighestAchievementIDSeen = -1;
+ int iNextGroupBoundary = 1000;
+
+ Q_memset( m_AchievementGroups, 0, sizeof(m_AchievementGroups) );
+ m_iNumAchievementGroups = 0;
+
+ // Base groups
+// CreateNewAchievementGroup( 0, 16000 );
+ CreateNewAchievementGroup( 0, 999 );
+
+ Assert ( achievementmgr );
+ if ( achievementmgr )
+ {
+ int iCount = achievementmgr->GetAchievementCount();
+ for ( int i = 0; i < iCount; ++i )
+ {
+ IAchievement* pCur = achievementmgr->GetAchievementByIndex( i );
+
+ if ( !pCur )
+ continue;
+
+ int iAchievementID = pCur->GetAchievementID();
+
+ if ( iAchievementID > iHighestAchievementIDSeen )
+ {
+ // if its crossed the next group boundary, create a new group
+ if ( iAchievementID >= iNextGroupBoundary )
+ {
+ int iNewGroupBoundary = 100 * ( (int)( (float)iAchievementID / 100 ) );
+ CreateNewAchievementGroup( iNewGroupBoundary, iNewGroupBoundary+99 );
+
+ iNextGroupBoundary = iNewGroupBoundary + 100;
+ }
+
+ iHighestAchievementIDSeen = iAchievementID;
+ }
+
+ // don't show hidden achievements if not achieved
+ if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() )
+ continue;
+
+ m_nTotalAchievements++;
+
+ if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() )
+ continue;
+
+ bool bAchieved = pCur->IsAchieved();
+
+ if ( bAchieved )
+ {
+ ++m_nUnlocked;
+ }
+
+ for ( int j=0;j<m_iNumAchievementGroups;j++ )
+ {
+ if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange &&
+ iAchievementID <= m_AchievementGroups[j].m_iMaxRange )
+ {
+ if ( bAchieved )
+ {
+ m_AchievementGroups[j].m_iNumUnlocked++;
+ }
+
+ m_AchievementGroups[j].m_iNumAchievements++;
+ }
+ }
+ }
+ }
+
+ CreateOrUpdateComboItems( true );
+
+ m_pAchievementPackCombo->ActivateItemByRow( 0 );
+}
+
+CAchievementsDialog::~CAchievementsDialog()
+{
+ if ( achievementmgr )
+ {
+ achievementmgr->SaveGlobalStateIfDirty( false ); // check for saving here to store achievements we want pinned to HUD
+ }
+
+ m_pAchievementsList->DeleteAllItems();
+ delete m_pAchievementsList;
+ delete m_pPercentageBarBackground;
+ delete m_pPercentageBar;
+}
+
+void CAchievementsDialog::OnCheckButtonChecked(Panel *panel)
+{
+ if ( panel == m_pHideAchievedCheck )
+ {
+ UpdateAchievementList();
+ }
+}
+
+void CAchievementsDialog::CreateNewAchievementGroup( int iMinRange, int iMaxRange )
+{
+ if ( m_iNumAchievementGroups < MAX_ACHIEVEMENT_GROUPS )
+ {
+ m_AchievementGroups[m_iNumAchievementGroups].m_iMinRange = iMinRange;
+ m_AchievementGroups[m_iNumAchievementGroups].m_iMaxRange = iMaxRange;
+ m_iNumAchievementGroups++;
+ }
+ else
+ {
+ AssertMsg( false, "CAchievementsDialog: Too many achievement groups" );
+ }
+}
+
+//----------------------------------------------------------
+// Get the width we're going to lock at
+//----------------------------------------------------------
+void CAchievementsDialog::ApplySettings( KeyValues *pResourceData )
+{
+ m_iFixedWidth = pResourceData->GetInt( "wide", 512 );
+
+ BaseClass::ApplySettings( pResourceData );
+}
+
+//----------------------------------------------------------
+// Preserve our width to the one in the .res file
+//----------------------------------------------------------
+void CAchievementsDialog::OnSizeChanged(int newWide, int newTall)
+{
+ // Lock the width, but allow height scaling
+ if ( newWide != m_iFixedWidth )
+ {
+ SetSize( m_iFixedWidth, newTall );
+ return;
+ }
+
+ BaseClass::OnSizeChanged(newWide, newTall);
+}
+
+//----------------------------------------------------------
+// New group was selected in the dropdown, recalc what achievements to show
+//----------------------------------------------------------
+void CAchievementsDialog::OnTextChanged(KeyValues *data)
+{
+ Panel *pPanel = (Panel *)data->GetPtr( "panel", NULL );
+
+ // first check which control had its text changed!
+ if ( pPanel == m_pAchievementPackCombo )
+ {
+ UpdateAchievementList();
+ }
+}
+
+class CAchievementsLess
+{
+public:
+ bool Less( const IAchievement* src1, const IAchievement* src2, void *pCtx )
+ {
+ IAchievement* _src1 = const_cast<IAchievement*>(src1);
+ IAchievement* _src2 = const_cast<IAchievement*>(src2);
+
+ if ( _src1->IsAchieved() && !_src2->IsAchieved() )
+ return false;
+ if ( !_src1->IsAchieved() && _src2->IsAchieved() )
+ return true;
+
+ const char* name1 = _src1->GetName();
+ const char* name2 = _src2->GetName();
+ if ( wcscoll( ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name1 ), ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name2 ) ) < 0 )
+ return true;
+ return false;
+ }
+};
+
+//----------------------------------------------------------
+// Re-populate the achievement list with the selected group
+//----------------------------------------------------------
+void CAchievementsDialog::UpdateAchievementList()
+{
+ m_pAchievementsList->DeleteAllItems();
+
+ KeyValues *pData = m_pAchievementPackCombo->GetActiveItemUserData();
+
+ if ( pData )
+ {
+ int iMinRange = pData->GetInt( "minrange" );
+ int iMaxRange = pData->GetInt( "maxrange" );
+
+ if ( achievementmgr )
+ {
+ CUtlSortVector<IAchievement*, CAchievementsLess> achievements;
+
+ int iCount = achievementmgr->GetAchievementCount();
+ for ( int i = 0; i < iCount; ++i )
+ {
+ IAchievement* pCur = achievementmgr->GetAchievementByIndex( i );
+
+ if ( !pCur )
+ continue;
+
+ int iAchievementID = pCur->GetAchievementID();
+
+ if ( iAchievementID < iMinRange || iAchievementID > iMaxRange )
+ continue;
+
+ // don't show hidden achievements if not achieved
+ if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() )
+ continue;
+
+ if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() )
+ continue;
+
+ // if we don't have a localized name, don't add it to the list
+ if ( pCur->GetName() == NULL || ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( pCur->GetName() ) == NULL )
+ continue;
+
+ achievements.Insert( pCur );
+ }
+
+ for ( int i=0; i<achievements.Count(); i++ )
+ {
+ CAchievementDialogItemPanel *achievementItemPanel = new CAchievementDialogItemPanel( m_pAchievementsList, "AchievementDialogItemPanel", i );
+ achievementItemPanel->SetAchievementInfo( achievements[i] );
+ m_pAchievementsList->AddItem( NULL, achievementItemPanel );
+ }
+ }
+ }
+
+ if ( m_pAchievementsList->GetItemCount() > 0 )
+ {
+ m_nOldScrollItem = -1;
+ m_nScrollItem = -1;
+ m_pSelectionHighlight->SetVisible( false );
+ m_pAchievementsList->ScrollToItem( 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads settings from achievementsdialog.res in hl2/resource/ui/
+// Sets up progress bar displaying total achievement unlocking progress by the user.
+//-----------------------------------------------------------------------------
+void CAchievementsDialog::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ LoadControlSettings("resource/ui/AchievementsDialog.res");
+
+ if ( !achievementmgr )
+ {
+ AssertOnce( 0 && "No achievement manager interface in GameUI.dll" );
+ return;
+ }
+
+ // Set up total completion percentage bar
+ float flCompletion = 0.0f;
+ if ( achievementmgr->GetAchievementCount() > 0 )
+ {
+ flCompletion = (((float)m_nUnlocked) / ((float)m_nTotalAchievements));
+ }
+
+ char szPercentageText[64];
+ V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )",
+ m_nUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) );
+
+ SetControlString( "PercentageText", szPercentageText );
+ SetControlVisible( "PercentageText", true );
+ SetControlVisible( "CompletionText", true );
+
+ Color clrHighlight = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) );
+ Color clrWhite(255, 255, 255, 255);
+
+ Color cProgressBar = Color( static_cast<float>( clrHighlight.r() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.r() ) * flCompletion,
+ static_cast<float>( clrHighlight.g() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.g() ) * flCompletion,
+ static_cast<float>( clrHighlight.b() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.b() ) * flCompletion,
+ static_cast<float>( clrHighlight.a() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.a() ) * flCompletion );
+
+ m_pPercentageBar->SetFgColor( cProgressBar );
+ m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion );
+
+ SetControlVisible( "PercentageBarBackground", true );
+ SetControlVisible( "PercentageBar", true );
+
+ m_pSelectionHighlight->SetFillColor( Color( clrHighlight.r(), clrHighlight.g(), clrHighlight.b(), 32 ) );
+
+ if ( m_iNumAchievementGroups <= 2 )
+ {
+ // we have no achievement packs. Hide the combo and bump the achievement list up a bit
+ m_pAchievementPackCombo->SetVisible( false );
+
+ // do some work to preserve the pincorner and resizing
+
+ int comboX, comboY;
+ m_pAchievementPackCombo->GetPos( comboX, comboY );
+
+ int x, y, w, h;
+ m_pAchievementsList->GetBounds( x, y, w, h );
+
+ PinCorner_e corner = m_pAchievementsList->GetPinCorner();
+ int pinX, pinY;
+ m_pAchievementsList->GetPinOffset( pinX, pinY );
+
+ int resizeOffsetX, resizeOffsetY;
+ m_pAchievementsList->GetResizeOffset( resizeOffsetX, resizeOffsetY );
+
+ m_pAchievementsList->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY );
+
+ m_pAchievementsList->SetBounds( x, comboY, w, h + ( y - comboY ) );
+
+ m_pListBG->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY );
+ m_pListBG->SetBounds( x, comboY, w, h + ( y - comboY ) );
+ }
+
+ MoveToCenterOfScreen();
+}
+
+void CAchievementsDialog::ScrollToItem( int nDirection )
+{
+ if ( m_pAchievementsList->GetItemCount() > 0 )
+ {
+ m_nScrollItem = clamp( m_nScrollItem + nDirection, 0, m_pAchievementsList->GetItemCount() - 1 );
+
+ if ( m_nOldScrollItem != m_nScrollItem )
+ {
+ m_nOldScrollItem = m_nScrollItem;
+ m_pAchievementsList->ScrollToItem( m_nScrollItem );
+
+ Panel *pItem = m_pAchievementsList->GetItemPanel( m_nScrollItem );
+ if ( pItem )
+ {
+ int nX, nY, nWide, nTall;
+ pItem->GetBounds( nX, nY, nWide, nTall );
+
+ m_pSelectionHighlight->SetVisible( true );
+ m_pSelectionHighlight->SetBounds( nX, nY, nWide, nTall );
+ }
+ }
+ }
+}
+
+void CAchievementsDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ // Handle close here, CBasePanel parent doesn't support "DialogClosing" command
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B )
+ {
+ OnCommand( "Close" );
+ }
+ else if ( nButtonCode == KEY_XBUTTON_X || nButtonCode == STEAMCONTROLLER_X )
+ {
+ if ( m_pHideAchievedCheck && m_pHideAchievedCheck->IsVisible() )
+ {
+ m_pHideAchievedCheck->SetSelected( !m_pHideAchievedCheck->IsSelected() );
+ }
+ }
+ else if ( nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
+ {
+ if ( GameSupportsAchievementTracker() && m_nScrollItem >= 0 && m_nScrollItem < m_pAchievementsList->GetItemCount() )
+ {
+ CAchievementDialogItemPanel *pItem = dynamic_cast< CAchievementDialogItemPanel* >( m_pAchievementsList->GetItemPanel( m_nScrollItem) );
+ if ( pItem && pItem->IsVisible() )
+ {
+ pItem->ToggleShowOnHUD();
+ }
+ }
+ }
+ else if ( nButtonCode == KEY_XBUTTON_UP ||
+ nButtonCode == KEY_XSTICK1_UP ||
+ nButtonCode == KEY_XSTICK2_UP ||
+ nButtonCode == STEAMCONTROLLER_DPAD_UP ||
+ nButtonCode == KEY_UP )
+ {
+ ScrollToItem( -1 );
+ }
+ else if ( nButtonCode == KEY_XBUTTON_DOWN ||
+ nButtonCode == KEY_XSTICK1_DOWN ||
+ nButtonCode == KEY_XSTICK2_DOWN ||
+ nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
+ nButtonCode == KEY_DOWN )
+ {
+ ScrollToItem( 1 );
+ }
+ else if ( nButtonCode == KEY_XBUTTON_LEFT ||
+ nButtonCode == KEY_XSTICK1_LEFT ||
+ nButtonCode == KEY_XSTICK2_LEFT ||
+ nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
+ nButtonCode == KEY_LEFT )
+ {
+ m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() - 1 );
+ }
+ else if ( nButtonCode == KEY_XBUTTON_RIGHT ||
+ nButtonCode == KEY_XSTICK1_RIGHT ||
+ nButtonCode == KEY_XSTICK2_RIGHT ||
+ nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
+ nButtonCode == KEY_RIGHT )
+ {
+ m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() + 1 );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Each sub-panel gets its data updated
+//-----------------------------------------------------------------------------
+void CAchievementsDialog::UpdateAchievementDialogInfo( void )
+{
+ int iCount = m_pAchievementsList->GetItemCount();
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CAchievementDialogItemPanel *pPanel = (CAchievementDialogItemPanel*)m_pAchievementsList->GetItemPanel(i);
+ if ( pPanel )
+ {
+ pPanel->UpdateAchievementInfo( pScheme );
+ }
+ }
+
+ // update the groups and overall progress bar
+ if ( achievementmgr )
+ {
+ int i;
+
+ // reset group completed counts
+ for ( i=0;i<m_iNumAchievementGroups;i++ )
+ {
+ m_AchievementGroups[i].m_iNumUnlocked = 0;
+ }
+
+ int iAchievementCount = achievementmgr->GetAchievementCount();
+
+ // update counts for each achieved achievement
+ for ( i=0;i<iAchievementCount;i++ )
+ {
+ IAchievement* pCurAchievement = achievementmgr->GetAchievementByIndex( i );
+
+ if ( !pCurAchievement || !pCurAchievement->IsAchieved() )
+ continue;
+
+ int iAchievementID = pCurAchievement->GetAchievementID();
+
+ for ( int j=0;j<m_iNumAchievementGroups;j++ )
+ {
+ if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange &&
+ iAchievementID <= m_AchievementGroups[j].m_iMaxRange )
+ {
+ m_AchievementGroups[j].m_iNumUnlocked++;
+ }
+ }
+ }
+
+ // update the global progress bar label
+ char szPercentageText[64];
+ float flCompletion = (((float)m_AchievementGroups[0].m_iNumUnlocked) / ((float)iAchievementCount));
+ V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )",
+ m_AchievementGroups[0].m_iNumUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) );
+
+ // and the progress bar
+ m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion );
+
+ SetControlString( "PercentageText", szPercentageText );
+ }
+
+ CreateOrUpdateComboItems( false ); // update them with new achieved counts
+
+ m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() );
+}
+
+void CAchievementsDialog::CreateOrUpdateComboItems( bool bCreate )
+{
+ for ( int i=0;i<m_iNumAchievementGroups;i++ )
+ {
+ char buf[128];
+
+ Q_snprintf( buf, sizeof(buf), "#Achievement_Group_%d", m_AchievementGroups[i].m_iMinRange );
+
+ const wchar_t *wzGroupName = g_pVGuiLocalize->Find( buf );
+
+ if ( !wzGroupName )
+ {
+ wzGroupName = L"Need Title ( %s1 of %s2 )";
+ }
+
+ wchar_t wzGroupTitle[128];
+
+ if ( wzGroupName )
+ {
+ wchar_t wzNumUnlocked[8];
+ V_snwprintf( wzNumUnlocked, ARRAYSIZE( wzNumUnlocked ), L"%d", m_AchievementGroups[i].m_iNumUnlocked );
+
+ wchar_t wzNumAchievements[8];
+ V_snwprintf( wzNumAchievements, ARRAYSIZE( wzNumAchievements ), L"%d", m_AchievementGroups[i].m_iNumAchievements );
+
+ g_pVGuiLocalize->ConstructString( wzGroupTitle, sizeof( wzGroupTitle ), wzGroupName, 2, wzNumUnlocked, wzNumAchievements );
+ }
+
+ KeyValues *pKV = new KeyValues( "grp" );
+ pKV->SetInt( "minrange", m_AchievementGroups[i].m_iMinRange );
+ pKV->SetInt( "maxrange", m_AchievementGroups[i].m_iMaxRange );
+
+ if ( bCreate )
+ m_AchievementGroups[i].m_iDropDownGroupID = m_pAchievementPackCombo->AddItem( wzGroupTitle, pKV );
+ else
+ m_pAchievementPackCombo->UpdateItem( m_AchievementGroups[i].m_iDropDownGroupID, wzGroupTitle, pKV );
+ }
+
+ if ( bCreate && ( m_iNumAchievementGroups > NUM_COMBO_BOX_LINES_DEFAULT ) )
+ {
+ if ( m_pAchievementPackCombo )
+ {
+ m_pAchievementPackCombo->SetNumberOfEditLines( ( m_iNumAchievementGroups <= NUM_COMBO_BOX_LINES_MAX ) ? m_iNumAchievementGroups : NUM_COMBO_BOX_LINES_MAX );
+ }
+ }
+}
+
+void CAchievementsDialog::OnCommand( const char *command )
+{
+ if ( !Q_strcasecmp( command, "ongameuiactivated" ) )
+ {
+ UpdateAchievementDialogInfo();
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates child panels, passes down name to pick up any settings from res files.
+//-----------------------------------------------------------------------------
+CAchievementDialogItemPanel::CAchievementDialogItemPanel( vgui::PanelListPanel *parent, const char* name, int iListItemID ) : BaseClass( parent, name )
+{
+ m_iListItemID = iListItemID;
+ m_pParent = parent;
+ m_pSchemeSettings = NULL;
+
+ SetMouseInputEnabled( true );
+ parent->SetMouseInputEnabled( true );
+
+ //CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL);
+ //panel->SetZPos(2);
+}
+
+CAchievementDialogItemPanel::~CAchievementDialogItemPanel()
+{
+ /*
+ delete m_pAchievementIcon;
+ delete m_pAchievementNameLabel;
+ delete m_pAchievementDescLabel;
+ delete m_pPercentageBar;
+ delete m_pPercentageText;
+ delete m_pShowOnHUDCheck;
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates displayed achievement data. In applyschemesettings, and when gameui activates.
+//-----------------------------------------------------------------------------
+void CAchievementDialogItemPanel::UpdateAchievementInfo( IScheme* pScheme )
+{
+ if ( m_pSourceAchievement && m_pSchemeSettings )
+ {
+ // Set name, description and unlocked state text
+ m_pAchievementNameLabel->SetText( ACHIEVEMENT_LOCALIZED_NAME( m_pSourceAchievement ) );
+ m_pAchievementDescLabel->SetText( ACHIEVEMENT_LOCALIZED_DESC( m_pSourceAchievement ) );
+
+ // Setup icon
+ // get the vtfFilename from the path.
+
+ // Display percentage completion for progressive achievements
+ // Set up total completion percentage bar. Goal > 1 means its a progress achievement.
+ UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar );
+
+ if ( m_pSourceAchievement->IsAchieved() )
+ {
+ LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement );
+
+ SetBgColor( pScheme->GetColor( "AchievementsLightGrey", Color(255, 0, 0, 255) ) );
+
+ m_pAchievementNameLabel->SetFgColor( pScheme->GetColor( "SteamLightGreen", Color(255, 255, 255, 255) ) );
+
+ Color fgColor = pScheme->GetColor( "Label.TextBrightColor", Color(255, 255, 255, 255) );
+ m_pAchievementDescLabel->SetFgColor( fgColor );
+ m_pPercentageText->SetFgColor( fgColor );
+ m_pShowOnHUDCheck->SetVisible( false );
+ m_pShowOnHUDCheck->SetSelected( false );
+ }
+ else
+ {
+ LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement, "_bw" );
+
+ SetBgColor( pScheme->GetColor( "AchievementsDarkGrey", Color(255, 0, 0, 255) ) );
+
+ Color fgColor = pScheme->GetColor( "AchievementsInactiveFG", Color(255, 255, 255, 255) );
+ m_pAchievementNameLabel->SetFgColor( fgColor );
+ m_pAchievementDescLabel->SetFgColor( fgColor );
+ m_pPercentageText->SetFgColor( fgColor );
+ if ( GameSupportsAchievementTracker() )
+ {
+ m_pShowOnHUDCheck->SetVisible( !m_pSourceAchievement->ShouldHideUntilAchieved() ); // m_pSourceAchievement->GetGoal() > 1 &&
+ m_pShowOnHUDCheck->SetSelected( m_pSourceAchievement->ShouldShowOnHUD() );
+ }
+ else
+ {
+ m_pShowOnHUDCheck->SetVisible( false );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Makes a local copy of a pointer to the achievement entity stored on the client.
+//-----------------------------------------------------------------------------
+void CAchievementDialogItemPanel::SetAchievementInfo( IAchievement* pAchievement )
+{
+ if ( !pAchievement )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ m_pSourceAchievement = pAchievement;
+ m_iSourceAchievementIndex = pAchievement->GetAchievementID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAchievementDialogItemPanel::PreloadResourceFile( void )
+{
+ const char *controlResourceName = "resource/ui/AchievementItem.res";
+
+ g_pPreloadedAchievementItemLayout = new KeyValues(controlResourceName);
+ g_pPreloadedAchievementItemLayout->LoadFromFile(g_pFullFileSystem, controlResourceName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads settings from hl2/resource/ui/achievementitem.res
+// Sets display info for this achievement item.
+//-----------------------------------------------------------------------------
+void CAchievementDialogItemPanel::ApplySchemeSettings( IScheme* pScheme )
+{
+ if ( !g_pPreloadedAchievementItemLayout )
+ {
+ PreloadResourceFile();
+ }
+
+ LoadControlSettings( "", NULL, g_pPreloadedAchievementItemLayout );
+
+ m_pSchemeSettings = pScheme;
+
+ if ( !m_pSourceAchievement )
+ {
+ return;
+ }
+
+ m_pAchievementIcon = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("AchievementIcon")));
+ m_pAchievementNameLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementName"));
+ m_pAchievementDescLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementDesc"));
+ m_pPercentageBar = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("PercentageBar")));
+ m_pPercentageText = dynamic_cast<vgui::Label*>(FindChildByName("PercentageText"));
+ m_pShowOnHUDCheck = dynamic_cast<vgui::CheckButton*>(FindChildByName("ShowOnHUD"));
+ m_pShowOnHUDCheck->SetMouseInputEnabled( true );
+ m_pShowOnHUDCheck->SetEnabled( true );
+ m_pShowOnHUDCheck->SetCheckButtonCheckable( true );
+ m_pShowOnHUDCheck->AddActionSignalTarget( this );
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // m_pSchemeSettings must be set for this.
+ UpdateAchievementInfo( pScheme );
+}
+
+void CAchievementDialogItemPanel::ToggleShowOnHUD( void )
+{
+ m_pShowOnHUDCheck->SetSelected( !m_pShowOnHUDCheck->IsSelected() );
+}
+
+void CAchievementDialogItemPanel::OnCheckButtonChecked(Panel *panel)
+{
+ if ( GameSupportsAchievementTracker() && panel == m_pShowOnHUDCheck && m_pSourceAchievement )
+ {
+ m_pSourceAchievement->SetShowOnHUD( m_pShowOnHUDCheck->IsSelected() );
+ }
+}
diff --git a/gameui/matchmaking/achievementsdialog.h b/gameui/matchmaking/achievementsdialog.h
new file mode 100644
index 0000000..b16d876
--- /dev/null
+++ b/gameui/matchmaking/achievementsdialog.h
@@ -0,0 +1,182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ACHIEVEMENTSDIALOG_H
+#define ACHIEVEMENTSDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+#include "vgui_controls/PanelListPanel.h"
+#include "vgui_controls/Label.h"
+#include "tier1/KeyValues.h"
+#include "TGAImagePanel.h"
+
+#define MAX_ACHIEVEMENT_GROUPS 25
+
+class IAchievement;
+
+#define ACHIEVED_ICON_PATH "hud/icon_check.vtf"
+#define LOCK_ICON_PATH "hud/icon_locked.vtf"
+
+// Loads an achievement's icon into a specified image panel, or turns the panel off if no achievement icon was found.
+bool LoadAchievementIcon( vgui::ImagePanel* pIconPanel, IAchievement *pAchievement, const char *pszExt = NULL );
+
+// Updates a listed achievement item's progress bar.
+void UpdateProgressBar( vgui::EditablePanel* pPanel, IAchievement *pAchievement, Color clrProgressBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Simple menu to choose a matchmaking session type
+//-----------------------------------------------------------------------------
+class CAchievementsDialog_XBox : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CAchievementsDialog_XBox, CBaseDialog );
+
+public:
+ CAchievementsDialog_XBox(vgui::Panel *parent);
+ ~CAchievementsDialog_XBox();
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void PerformLayout();
+
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void HandleKeyRepeated( vgui::KeyCode code );
+
+ virtual void OnClose();
+
+
+private:
+
+ vgui::Panel *m_pProgressBg;
+
+ vgui::Panel *m_pProgressBar;
+ vgui::Label *m_pProgressPercent;
+ vgui::Label *m_pNumbering;
+ vgui::Label *m_pUpArrow;
+ vgui::Label *m_pDownArrow;
+
+ KeyValues* m_pResourceData;
+
+ CFooterPanel *m_pFooter;
+
+ bool m_bCenterOnScreen;
+ int m_iNumItems;
+ int m_nTotalAchievements; // Total achievements for this title
+ int m_nUnlocked;
+ int m_iSelection;
+ int m_iScroll;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// PC version
+//////////////////////////////////////////////////////////////////////////
+class CAchievementsDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE ( CAchievementsDialog, vgui::Frame );
+
+public:
+ CAchievementsDialog( vgui::Panel *parent );
+ ~CAchievementsDialog();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ void ScrollToItem( int nDirection );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void UpdateAchievementDialogInfo( void );
+ virtual void OnCommand( const char* command );
+
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void OnSizeChanged( int newWide, int newTall );
+
+ MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel );
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+
+ void CreateNewAchievementGroup( int iMinRange, int iMaxRange );
+ void CreateOrUpdateComboItems( bool bCreate );
+ void UpdateAchievementList();
+
+ vgui::PanelListPanel *m_pAchievementsList;
+ vgui::ImagePanel *m_pListBG;
+
+ vgui::ImagePanel *m_pPercentageBarBackground;
+ vgui::ImagePanel *m_pPercentageBar;
+
+ vgui::ImagePanel *m_pSelectionHighlight;
+
+ vgui::ComboBox *m_pAchievementPackCombo;
+ vgui::CheckButton *m_pHideAchievedCheck;
+
+ int m_nUnlocked;
+ int m_nTotalAchievements;
+
+ int m_iFixedWidth;
+
+ typedef struct
+ {
+ int m_iMinRange;
+ int m_iMaxRange;
+ int m_iNumAchievements;
+ int m_iNumUnlocked;
+ int m_iDropDownGroupID;
+ } achievement_group_t;
+
+ int m_iNumAchievementGroups;
+
+ achievement_group_t m_AchievementGroups[ MAX_ACHIEVEMENT_GROUPS ];
+
+ int m_nScrollItem;
+ int m_nOldScrollItem;
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Individual item panel, displaying stats for one achievement
+class CAchievementDialogItemPanel : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CAchievementDialogItemPanel, vgui::EditablePanel );
+
+public:
+ CAchievementDialogItemPanel( vgui::PanelListPanel *parent, const char* name, int iListItemID );
+ ~CAchievementDialogItemPanel();
+
+ void SetAchievementInfo ( IAchievement* pAchievement );
+ IAchievement* GetAchievementInfo( void ) { return m_pSourceAchievement; }
+ void UpdateAchievementInfo( IScheme *pScheme );
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ void ToggleShowOnHUD( void );
+
+ MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel );
+
+private:
+ void PreloadResourceFile( void );
+
+ IAchievement* m_pSourceAchievement;
+ int m_iSourceAchievementIndex;
+
+ vgui::PanelListPanel *m_pParent;
+
+ vgui::Label *m_pAchievementNameLabel;
+ vgui::Label *m_pAchievementDescLabel;
+ vgui::Label *m_pPercentageText;
+
+ vgui::ImagePanel *m_pLockedIcon;
+ vgui::ImagePanel *m_pAchievementIcon;
+
+ vgui::ImagePanel *m_pPercentageBarBackground;
+ vgui::ImagePanel *m_pPercentageBar;
+
+ vgui::CheckButton *m_pShowOnHUDCheck;
+
+ vgui::IScheme *m_pSchemeSettings;
+
+ CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" );
+
+ int m_iListItemID;
+};
+
+#endif // ACHIEVEMENTSDIALOG_H
diff --git a/gameui/matchmaking/basedialog.cpp b/gameui/matchmaking/basedialog.cpp
new file mode 100644
index 0000000..be65718
--- /dev/null
+++ b/gameui/matchmaking/basedialog.cpp
@@ -0,0 +1,295 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: All matchmaking dialogs inherit from this
+//
+//=============================================================================//
+
+#include "vgui_controls/Label.h"
+#include "GameUI_Interface.h"
+#include "KeyValues.h"
+#include "basedialog.h"
+#include "BasePanel.h"
+#include "matchmakingbasepanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//---------------------------------------------------------
+// CBaseDialog
+//---------------------------------------------------------
+CBaseDialog::CBaseDialog( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
+{
+ SetTitleBarVisible( false );
+ SetCloseButtonVisible( false );
+ SetSizeable( false );
+
+ m_pParent = pParent;
+ m_Menu.SetParent( this );
+
+ m_pTitle = new vgui::Label( this, "DialogTitle", "" );
+ m_pFooterInfo = NULL;
+
+ m_nBorderWidth = 0;
+ m_nButtonGap = -1;
+}
+
+CBaseDialog::~CBaseDialog()
+{
+ delete m_pTitle;
+
+ if ( m_pFooterInfo )
+ {
+ m_pFooterInfo->deleteThis();
+ }
+}
+
+//---------------------------------------------------------
+// Purpose: Activate the dialog
+//---------------------------------------------------------
+void CBaseDialog::Activate( void )
+{
+ BaseClass::Activate();
+
+ InvalidateLayout( false, false );
+}
+
+//---------------------------------------------------------
+// Purpose: Set the title and menu positions
+//---------------------------------------------------------
+void CBaseDialog::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ m_pTitle->SizeToContents();
+
+ int menux, menuy;
+ m_Menu.GetPos( menux, menuy );
+
+ int autoWide = m_Menu.GetWide() + m_nBorderWidth * 2;
+ int autoTall = menuy + m_Menu.GetTall() + m_nBorderWidth;
+ autoWide = max( autoWide, GetWide() );
+ autoTall = max( autoTall, GetTall() );
+
+ SetSize( autoWide, autoTall );
+
+ if ( m_pFooterInfo && m_pParent )
+ {
+ CMatchmakingBasePanel *pBasePanel = dynamic_cast< CMatchmakingBasePanel* >( m_pParent );
+ if ( pBasePanel )
+ {
+ // the base panel is our parent
+ pBasePanel->SetFooterButtons( this, m_pFooterInfo, m_nButtonGap );
+ }
+ }
+
+ if ( m_Menu.GetActiveItemIndex() == -1 )
+ {
+ m_Menu.SetFocus( 0 );
+ }
+}
+
+//---------------------------------------------------------
+// Purpose: Setup sizes and positions
+//---------------------------------------------------------
+void CBaseDialog::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ m_nBorderWidth = inResourceData->GetInt( "borderwidth", 0 );
+
+ KeyValues *pFooter = inResourceData->FindKey( "Footer" );
+ if ( pFooter )
+ {
+ m_pFooterInfo = pFooter->MakeCopy();
+ }
+
+ m_nButtonGap = inResourceData->GetInt( "footer_buttongap", -1 );
+}
+
+//---------------------------------------------------------
+// Purpose: Setup colors and fonts
+//---------------------------------------------------------
+void CBaseDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetPaintBackgroundType( 2 );
+
+ m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 200, 184, 151, 255 ) ) );
+
+ char szResourceName[MAX_PATH];
+ Q_snprintf( szResourceName, sizeof( szResourceName ), "%s.res", GetName() );
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( szResourceName );
+ LoadControlSettings( "NULL", NULL, pKeys );
+}
+
+//-----------------------------------------------------------------
+// Purpose: Set the resource file to load this dialog's settings
+//-----------------------------------------------------------------
+void CBaseDialog::OnClose()
+{
+ // Hide the rather ugly fade out
+ SetAlpha( 0 );
+ BaseClass::OnClose();
+}
+
+//-----------------------------------------------------------------
+// Purpose: Change properties of a menu item
+//-----------------------------------------------------------------
+void CBaseDialog::OverrideMenuItem( KeyValues *pKeys )
+{
+ // Do nothing
+}
+
+//-----------------------------------------------------------------
+// Purpose: Swap the order of two menu items
+//-----------------------------------------------------------------
+void CBaseDialog::SwapMenuItems( int iOne, int iTwo )
+{
+ // Do nothing
+}
+
+//-----------------------------------------------------------------
+// Purpose: Send key presses to the dialog's menu
+//-----------------------------------------------------------------
+void CBaseDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( code == KEY_XBUTTON_START )
+ {
+ m_KeyRepeat.Reset();
+ if ( GameUI().IsInLevel() )
+ {
+ m_pParent->OnCommand( "ResumeGame" );
+ }
+ return;
+ }
+
+ m_KeyRepeat.KeyDown( code );
+
+ // Send down to the menu
+ if ( !m_Menu.HandleKeyCode( code ) )
+ {
+ if ( code == KEY_XBUTTON_B )
+ {
+ OnCommand( "DialogClosing" );
+ SetDeleteSelfOnClose( true );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDialog::OnKeyCodeReleased( vgui::KeyCode code )
+{
+ m_KeyRepeat.KeyUp( code );
+
+ BaseClass::OnKeyCodeReleased( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDialog::HandleKeyRepeated( vgui::KeyCode code )
+{
+ m_Menu.HandleKeyCode( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDialog::OnThink()
+{
+ vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
+ if ( code )
+ {
+ if ( HasFocus() )
+ {
+ HandleKeyRepeated( code );
+ }
+ else
+ {
+ // This can happen because of the slight delay after selecting a
+ // menu option and the resulting action. The selection caused the
+ // key repeater to be reset, but the player can press a movement
+ // key before the action occurs, leaving us with a key repeating
+ // on this dialog even though it no longer has focus.
+ m_KeyRepeat.Reset();
+ }
+ }
+
+ BaseClass::OnThink();
+}
+
+//-----------------------------------------------------------------
+// Purpose: Forward commands to the matchmaking base panel
+//-----------------------------------------------------------------
+void CBaseDialog::OnCommand( const char *pCommand )
+{
+ m_KeyRepeat.Reset();
+ m_pParent->OnCommand( pCommand );
+}
+
+
+//---------------------------------------------------------------------
+// Helper object to display the map picture and descriptive text
+//---------------------------------------------------------------------
+CScenarioInfoPanel::CScenarioInfoPanel( vgui::Panel *parent, const char *pName ) : BaseClass( parent, pName )
+{
+ m_pMapImage = new vgui::ImagePanel( this, "MapImage" );
+ m_pTitle = new CPropertyLabel( this, "Title", "" );
+ m_pSubtitle = new CPropertyLabel( this, "Subtitle", "" );
+
+ m_pDescOne = new CPropertyLabel( this, "DescOne", "" );
+ m_pDescTwo = new CPropertyLabel( this, "DescTwo", "" );
+ m_pDescThree = new CPropertyLabel( this, "DescThree", "" );
+
+ m_pValueTwo = new CPropertyLabel( this, "ValueTwo", "" );
+ m_pValueThree = new CPropertyLabel( this, "ValueThree", "" );
+}
+CScenarioInfoPanel::~CScenarioInfoPanel()
+{
+ delete m_pMapImage;
+ delete m_pTitle;
+ delete m_pSubtitle;
+ delete m_pDescOne;
+ delete m_pDescTwo;
+ delete m_pDescThree;
+ delete m_pValueTwo;
+ delete m_pValueThree;
+}
+
+void CScenarioInfoPanel::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+}
+
+void CScenarioInfoPanel::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+}
+
+void CScenarioInfoPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ Color fontColor = pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 0, 0, 0, 255 ) );
+ m_pTitle->SetFgColor( fontColor );
+ m_pSubtitle->SetFgColor( fontColor );
+ m_pDescOne->SetFgColor( fontColor );
+ m_pDescTwo->SetFgColor( fontColor );
+ m_pDescThree->SetFgColor( fontColor );
+ m_pValueTwo->SetFgColor( fontColor );
+ m_pValueThree->SetFgColor( fontColor );
+
+ SetPaintBackgroundType( 2 );
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "ScenarioInfoPanel.res" );
+ ApplySettings( pKeys );
+}
+
+DECLARE_BUILD_FACTORY( CScenarioInfoPanel );
diff --git a/gameui/matchmaking/basedialog.h b/gameui/matchmaking/basedialog.h
new file mode 100644
index 0000000..1501e1f
--- /dev/null
+++ b/gameui/matchmaking/basedialog.h
@@ -0,0 +1,120 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: All matchmaking dialogs inherit from this
+//
+//=============================================================================//
+
+#ifndef BASEDIALOG_H
+#define BASEDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dialogmenu.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/KeyRepeat.h"
+#include "KeyValues.h"
+#include "BasePanel.h"
+
+#if !defined( _X360 )
+#include "xbox/xboxstubs.h"
+#endif
+
+class CFooterPanel;
+
+//-----------------------------------------------------------------------------
+// Purpose: A Label with an extra string to hold a session property lookup key
+//-----------------------------------------------------------------------------
+class CPropertyLabel : public vgui::Label
+{
+ DECLARE_CLASS_SIMPLE( CPropertyLabel, vgui::Label );
+
+public:
+ CPropertyLabel( Panel *parent, const char *panelName, const char *text ) : BaseClass( parent, panelName, text )
+ {
+ }
+
+ virtual void ApplySettings( KeyValues *pResourceData )
+ {
+ BaseClass::ApplySettings( pResourceData );
+
+ m_szPropertyString[0] = 0;
+ const char *pString = pResourceData->GetString( "PropertyString", NULL );
+ if ( pString )
+ {
+ Q_strncpy( m_szPropertyString, pString, sizeof( m_szPropertyString ) );
+ }
+ }
+
+ char m_szPropertyString[ MAX_PATH ];
+};
+
+//--------------------------------
+// CBaseDialog
+//--------------------------------
+class CBaseDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CBaseDialog, vgui::Frame );
+
+public:
+ CBaseDialog( vgui::Panel *parent, const char *pName );
+ ~CBaseDialog();
+
+ // IPanel interface
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void PerformLayout();
+ virtual void Activate();
+
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void OnKeyCodeReleased( vgui::KeyCode code);
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnClose();
+ virtual void OnThink();
+
+ virtual void OverrideMenuItem( KeyValues *pKeys );
+ virtual void SwapMenuItems( int iOne, int iTwo );
+
+ virtual void HandleKeyRepeated( vgui::KeyCode code );
+
+protected:
+ int m_nBorderWidth;
+ int m_nMinWide;
+
+ CDialogMenu m_Menu;
+ vgui::Label *m_pTitle;
+ vgui::Panel *m_pParent;
+
+ KeyValues *m_pFooterInfo;
+ int m_nButtonGap;
+
+ vgui::CKeyRepeatHandler m_KeyRepeat;
+};
+
+
+//---------------------------------------------------------------------
+// Helper object to display the map picture and descriptive text
+//---------------------------------------------------------------------
+class CScenarioInfoPanel : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CScenarioInfoPanel, vgui::EditablePanel );
+
+public:
+ CScenarioInfoPanel( vgui::Panel *parent, const char *pName );
+ ~CScenarioInfoPanel();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ vgui::ImagePanel *m_pMapImage;
+ CPropertyLabel *m_pTitle;
+ CPropertyLabel *m_pSubtitle;
+ CPropertyLabel *m_pDescOne;
+ CPropertyLabel *m_pDescTwo;
+ CPropertyLabel *m_pDescThree;
+ CPropertyLabel *m_pValueTwo;
+ CPropertyLabel *m_pValueThree;
+};
+
+#endif // BASEDIALOG_H \ No newline at end of file
diff --git a/gameui/matchmaking/dialogmenu.cpp b/gameui/matchmaking/dialogmenu.cpp
new file mode 100644
index 0000000..e46888b
--- /dev/null
+++ b/gameui/matchmaking/dialogmenu.cpp
@@ -0,0 +1,1524 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Multi-purpose menu for matchmaking dialogs, navigable with the xbox controller.
+//
+//=============================================================================//
+
+#include "engine/imatchmaking.h"
+#include "GameUI_Interface.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui/ILocalize.h"
+#include "vgui/ISurface.h"
+#include "KeyValues.h"
+#include "dialogmenu.h"
+#include "BasePanel.h"
+#include "vgui_controls/ImagePanel.h"
+#include "iachievementmgr.h" // for iachievement abstract class in CAchievementItem
+#include "achievementsdialog.h" // for helper functions used by both pc and xbox achievements
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------
+// Base class representing a generic menu item. Supports two text labels,
+// where the first label is the "action" text and the second is an optional
+// description of the action.
+//-----------------------------------------------------------------------
+CMenuItem::CMenuItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription )
+ : BaseClass( pParent, "MenuItem" )
+{
+ // Quiet "parent not sized yet" spew
+ SetSize( 10, 10 );
+
+ m_pParent = pParent;
+
+ m_bEnabled = true;
+ m_nDisabledAlpha = 30;
+
+ m_pTitle = new vgui::Label( this, "MenuItemText", pTitle );
+ m_pDescription = NULL;
+ if ( pDescription )
+ {
+ m_pDescription = new vgui::Label( this, "MenuItemDesc", pDescription );
+ }
+}
+
+CMenuItem::~CMenuItem()
+{
+ delete m_pTitle;
+ delete m_pDescription;
+}
+
+//-----------------------------------------------------------------------
+// Update colors according to enabled/disabled state
+//-----------------------------------------------------------------------
+void CMenuItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------
+// Setup margins and calculate the total menu item size
+//-----------------------------------------------------------------------
+void CMenuItem::ApplySettings( KeyValues *pSettings )
+{
+ BaseClass::ApplySettings( pSettings );
+
+ m_nBottomMargin = pSettings->GetInt( "bottommargin", 0 );
+ m_nRightMargin = pSettings->GetInt( "rightmargin", 0 );
+
+ int x, y;
+ m_pTitle->GetPos( x, y );
+ m_pTitle->SizeToContents();
+
+ int bgTall = y + m_pTitle->GetTall() + m_nBottomMargin;
+ int textWide = m_pTitle->GetWide();
+
+ if ( m_pDescription )
+ {
+ m_pDescription->SizeToContents();
+ m_pDescription->GetPos( x, y );
+ bgTall = y + m_pDescription->GetTall() + m_nBottomMargin;
+ textWide = max( textWide, m_pDescription->GetWide() );
+ }
+
+ int bgWide = x + textWide + m_nRightMargin;
+
+ SetSize( bgWide, bgTall );
+}
+
+//-----------------------------------------------------------------------
+// Setup colors and fonts
+//-----------------------------------------------------------------------
+void CMenuItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetPaintBackgroundType( 2 );
+
+ m_BgColor = pScheme->GetColor( "MatchmakingMenuItemBackground", Color( 46, 43, 42, 255 ) );
+ m_BgColorActive = pScheme->GetColor( "MatchmakingMenuItemBackgroundActive", Color( 150, 71, 0, 255 ) );
+
+ m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 0, 0, 0, 255 ) ) );
+
+ if ( m_pDescription )
+ {
+ m_pDescription->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 0, 0, 0, 255 ) ) );
+ }
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "MenuItem.res" );
+ ApplySettings( pKeys );
+}
+
+//-----------------------------------------------------------------------
+// Set an item as having input focus
+//-----------------------------------------------------------------------
+void CMenuItem::SetFocus( const bool bActive )
+{
+ if ( bActive )
+ {
+ SetBgColor( m_BgColorActive );
+ }
+ else
+ {
+ SetBgColor( m_BgColor );
+ }
+}
+
+//-----------------------------------------------------------------------
+// Set an item as having input focus
+//-----------------------------------------------------------------------
+void CMenuItem::SetEnabled( bool bEnabled )
+{
+ if ( bEnabled )
+ {
+ SetAlpha( 255 );
+ }
+ else
+ {
+ SetAlpha( m_nDisabledAlpha );
+ }
+ m_bEnabled = bEnabled;
+}
+
+//-----------------------------------------------------------------------
+// Set a column as having focus
+//-----------------------------------------------------------------------
+void CMenuItem::SetActiveColumn( int col )
+{
+ // do nothing
+}
+
+//-----------------------------------------------------------------------
+// Set an item as having input focus
+//-----------------------------------------------------------------------
+bool CMenuItem::IsEnabled()
+{
+ return m_bEnabled;
+}
+
+//-----------------------------------------------------------------------
+// Perform any special actions when an item is "clicked"
+//-----------------------------------------------------------------------
+void CMenuItem::OnClick()
+{
+ // do nothing - derived classes implement this
+}
+
+
+//-----------------------------------------------------------------------
+// CCommandItem
+//
+// Menu item that issues a command when clicked.
+//-----------------------------------------------------------------------
+CCommandItem::CCommandItem( CDialogMenu *pParent, const char *pTitleLabel, const char *pDescLabel, const char *pCommand )
+ : BaseClass( pParent, pTitleLabel, pDescLabel )
+{
+ Q_strncpy( m_szCommand, pCommand, MAX_COMMAND_LEN );
+}
+
+CCommandItem::~CCommandItem()
+{
+ // do nothing
+}
+
+void CCommandItem::OnClick()
+{
+ GetParent()->OnCommand( m_szCommand );
+
+ vgui::surface()->PlaySound( "UI/buttonclick.wav" );
+}
+
+void CCommandItem::SetFocus(const bool bActive )
+{
+ BaseClass::SetFocus( bActive );
+
+ if ( bActive == true && m_bHasFocus == false )
+ {
+ vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
+ }
+
+ m_bHasFocus = bActive;
+}
+
+
+//-----------------------------------------------------------------------
+// CPlayerItem
+//
+// Menu item to display a player in the lobby.
+//-----------------------------------------------------------------------
+CPlayerItem::CPlayerItem( CDialogMenu *pParent, const char *pTitleLabel, int64 nId, byte bVoice, bool bReady )
+: BaseClass( pParent, pTitleLabel, NULL, "ShowGamerCard" )
+{
+ m_pVoiceIcon = new vgui::Label( this, "voiceicon", "" );
+ m_pReadyIcon = new vgui::Label( this, "readyicon", "" );
+
+ m_nId = nId;
+ m_bVoice = bVoice;
+ m_bReady = bReady;
+}
+
+CPlayerItem::~CPlayerItem()
+{
+ delete m_pVoiceIcon;
+ delete m_pReadyIcon;
+}
+
+void CPlayerItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ const char *pVoice = "";
+
+ if ( m_bVoice == 2 )
+ {
+ pVoice = "#TF_Icon_Voice";
+ }
+ else if ( m_bVoice == 1 )
+ {
+ pVoice = "#TF_Icon_Voice_Idle";
+ }
+
+ m_pVoiceIcon->SetText( pVoice );
+ m_pReadyIcon->SetText( m_bReady ? "#TF_Icon_Ready" : "#TF_Icon_NotReady" );
+
+ int x, y;
+ m_pReadyIcon->GetPos( x, y );
+ m_pReadyIcon->SetPos( GetWide() - m_pReadyIcon->GetWide() - m_nRightMargin, y );
+}
+
+void CPlayerItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "PlayerItem.res" );
+ ApplySettings( pKeys );
+}
+
+void CPlayerItem::OnClick()
+{
+ BaseClass::OnClick();
+}
+
+//-----------------------------------------------------------------------
+// CBrowserItem
+//
+// Menu item used to display session search results.
+//-----------------------------------------------------------------------
+CBrowserItem::CBrowserItem( CDialogMenu *pParent, const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing )
+ : BaseClass( pParent, pHost, NULL, "SelectSession" )
+{
+ m_pPlayers = new vgui::Label( this, "players", pPlayers );
+ m_pScenario = new vgui::Label( this, "scenario", pScenario );
+ m_pPing = new vgui::Label( this, "ping", pPing );
+}
+
+CBrowserItem::~CBrowserItem()
+{
+ delete m_pPlayers;
+ delete m_pScenario;
+ delete m_pPing;
+}
+
+void CBrowserItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, wide, tall;
+ m_pPing->GetBounds( x, y, wide, tall );
+
+ m_pScenario->SizeToContents();
+ int sx, sy;
+ m_pScenario->GetPos( sx, sy );
+ m_pScenario->SetPos( x - m_pScenario->GetWide() - m_nRightMargin, sy );
+
+ SetSize( x + wide, GetTall() );
+}
+
+void CBrowserItem::ApplySettings( KeyValues *pSettings )
+{
+ BaseClass::ApplySettings( pSettings );
+}
+
+void CBrowserItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ Color fgcolor = pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) );
+ m_pPlayers->SetFgColor( fgcolor );
+ m_pScenario->SetFgColor( fgcolor );
+
+ m_pPing->SetContentAlignment( vgui::Label::a_center );
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "BrowserItem.res" );
+ ApplySettings( pKeys );
+
+ SetFocus( false );
+}
+
+//-----------------------------------------------------------------------
+// COptionsItem
+//
+// Menu item used to present a list of options for the player to select
+// from, such as "choose a map" or "number of rounds".
+//-----------------------------------------------------------------------
+COptionsItem::COptionsItem( CDialogMenu *pParent, const char *pLabel )
+ : BaseClass( pParent, pLabel, NULL )
+{
+ m_nActiveOption = m_Options.InvalidIndex();
+ m_nOptionsXPos = 0;
+ m_nMaxOptionWidth = 0;
+
+ m_szOptionsFont[0] = '\0';
+ m_hOptionsFont = vgui::INVALID_FONT;
+
+ m_pLeftArrow = new vgui::Label( this, "LeftArrow", "" );
+ m_pRightArrow = new vgui::Label( this, "RightArrow", "" );
+}
+
+COptionsItem::~COptionsItem()
+{
+ m_OptionLabels.PurgeAndDeleteElements();
+
+ delete m_pLeftArrow;
+ delete m_pRightArrow;
+}
+
+void COptionsItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int optionWide = max( m_nOptionsMinWide, GetWide() - m_nOptionsXPos - m_pRightArrow->GetWide() - m_nOptionsLeftMargin );
+ int optionTall = GetTall();
+
+ for ( int i = 0; i < m_OptionLabels.Count(); ++i )
+ {
+ vgui::Label *pOption = m_OptionLabels[i];
+
+ pOption->SetBounds( m_nOptionsXPos, 0, optionWide, optionTall );
+ }
+
+ int lx, ly;
+ m_pLeftArrow->GetPos( lx, ly );
+ m_pLeftArrow->SetPos( m_nOptionsXPos - m_nArrowGap - m_pLeftArrow->GetWide(), ly );
+
+ int rx, ry;
+ m_pRightArrow->GetPos( rx, ry );
+ m_pRightArrow->SetPos( m_nOptionsXPos + optionWide + m_nArrowGap, ry );
+
+ m_pLeftArrow->SetAlpha( 255 );
+ m_pRightArrow->SetAlpha( 255 );
+
+ if ( m_nActiveOption == 0 )
+ {
+ m_pLeftArrow->SetAlpha( 32 );
+ }
+ else if ( m_nActiveOption == m_OptionLabels.Count() - 1 )
+ {
+ m_pRightArrow->SetAlpha( 32 );
+ }
+}
+
+void COptionsItem::ApplySettings( KeyValues *pSettings )
+{
+ BaseClass::ApplySettings( pSettings );
+
+ m_nOptionsXPos = pSettings->GetInt( "optionsxpos", 0 );
+ m_nOptionsMinWide = pSettings->GetInt( "optionsminwide", 0 );
+ m_nOptionsLeftMargin = pSettings->GetInt( "optionsleftmargin", 0 );
+ m_nArrowGap = pSettings->GetInt( "arrowgap", 0 );
+
+ Q_strncpy( m_szOptionsFont, pSettings->GetString( "optionsfont", "Default" ), sizeof( m_szOptionsFont ) );
+}
+
+void COptionsItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ SetPaintBackgroundEnabled( false );
+
+ m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 200, 184, 151, 255 ) ) );
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsItem.res" );
+ ApplySettings( pKeys );
+
+ m_hOptionsFont = pScheme->GetFont( m_szOptionsFont );
+}
+
+void COptionsItem::SetFocus( const bool bActive )
+{
+ if ( bActive )
+ {
+ for ( int i = 0; i < m_OptionLabels.Count(); ++i )
+ {
+ m_OptionLabels[i]->SetBgColor( m_BgColorActive );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < m_OptionLabels.Count(); ++i )
+ {
+ m_OptionLabels[i]->SetBgColor( m_BgColor );
+ }
+ }
+}
+
+void COptionsItem::AddOption( const char *pLabelText, const sessionProperty_t& option )
+{
+ // Add a new option to this item's list of options
+ m_Options.AddToTail( option );
+
+ int idx = m_OptionLabels.AddToTail( new vgui::Label( this, "Option Value", pLabelText ) );
+ vgui::Label *pOption = m_OptionLabels[idx];
+
+ // Check for a format string
+ if ( Q_stristr( pLabelText, "Fmt" ) )
+ {
+ wchar_t wszString[64];
+ wchar_t wzNumber[8];
+ wchar_t *wzFmt = g_pVGuiLocalize->Find( pLabelText );
+ g_pVGuiLocalize->ConvertANSIToUnicode( option.szValue, wzNumber, sizeof( wzNumber ) );
+ g_pVGuiLocalize->ConstructString( wszString, sizeof( wszString ), wzFmt, 1, wzNumber );
+ pOption->SetText( wszString );
+ }
+
+ SETUP_PANEL( pOption );
+ pOption->SetPaintBackgroundType( 2 );
+
+ pOption->SetFont( m_hOptionsFont );
+ pOption->SetBgColor( Color( 46, 43, 42, 255 ) );
+ pOption->SetFgColor( m_pTitle ? m_pTitle->GetFgColor() : Color( 200, 184, 151, 255 ) );
+ pOption->SetTextInset( m_nOptionsLeftMargin, 0 );
+ pOption->SetContentAlignment( vgui::Label::a_southwest );
+ pOption->SizeToContents();
+
+ int wide = max( m_nOptionsMinWide, pOption->GetWide() );
+ pOption->SetBounds( m_nOptionsXPos, 0, wide, GetTall() );
+ m_nMaxOptionWidth = max( wide, m_nMaxOptionWidth );
+
+ SetWide( m_nOptionsXPos + m_nMaxOptionWidth + m_nOptionsLeftMargin * 2 + m_nArrowGap * 2 + m_pRightArrow->GetWide() );
+}
+
+//-----------------------------------------------------------------------
+// Return the session property associated with the current active option
+//-----------------------------------------------------------------------
+const sessionProperty_t &COptionsItem::GetActiveOption()
+{
+ return m_Options[m_nActiveOption];
+}
+
+//-----------------------------------------------------------------------
+// Return the index of the current active option
+//-----------------------------------------------------------------------
+int COptionsItem::GetActiveOptionIndex()
+{
+ return m_nActiveOption;
+}
+
+//-----------------------------------------------------------------------
+// Sets which option currently has focus
+//-----------------------------------------------------------------------
+void COptionsItem::SetOptionFocus( unsigned int idx )
+{
+ unsigned int itemCt = (unsigned int)m_OptionLabels.Count();
+ if ( idx > itemCt )
+ return;
+
+ m_nActiveOption = idx;
+
+ for ( unsigned int i = 0; i < itemCt; ++i )
+ {
+ vgui::Label *pLabel = m_OptionLabels[i];
+
+ const bool bVisible = ( i == idx );
+ pLabel->SetVisible( bVisible );
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------
+// Move focus to the next option - does not wrap
+//-----------------------------------------------------------------------
+void COptionsItem::SetOptionFocusNext()
+{
+ if ( m_nActiveOption + 1 < m_OptionLabels.Count() )
+ {
+ SetOptionFocus( m_nActiveOption + 1 );
+ }
+ else
+ {
+ vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
+ }
+}
+
+//-----------------------------------------------------------------------
+// Move focus to the previous option - does not wrap
+//-----------------------------------------------------------------------
+void COptionsItem::SetOptionFocusPrev()
+{
+ if ( m_nActiveOption > 0 )
+ {
+ SetOptionFocus( m_nActiveOption - 1 );
+ }
+ else
+ {
+ vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// CAchievementItem
+//
+// Menu item used to present an achievement - including image, title,
+// description, points and unlock date. Clicking the item opens another
+// dialog with additional information about the achievement.
+//-----------------------------------------------------------------------
+CAchievementItem::CAchievementItem( CDialogMenu *pParent, const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement )
+ : BaseClass( pParent, "", "" )
+{
+ // Title and description were returned as results of a system query,
+ // and are therefore already localized.
+ m_pTitle->SetText( pName );
+
+ if ( IsX360() )
+ {
+ wchar_t buf[120];
+
+ // Get the screen size
+ int wide, tall;
+ vgui::surface()->GetScreenSize(wide, tall);
+
+ unsigned int iWrapLen;
+
+ if ( tall <= 480 )
+ {
+ iWrapLen = 50;
+ }
+ else
+ {
+ iWrapLen = 65;
+ }
+
+ // let's do some wrapping on this label
+ wcsncpy( buf, pDesc, sizeof(buf) / sizeof( wchar_t ) );
+
+ if ( wcslen(buf) > iWrapLen )
+ {
+ int iPos = iWrapLen;
+
+ while ( iPos > 0 && buf[iPos] != L' ' )
+ {
+ iPos--;
+ }
+
+ if ( iPos > 0 && buf[iPos] == L' ' )
+ {
+ buf[iPos] = L'\n';
+ }
+ }
+
+ m_pDescription->SetText( buf );
+ }
+ else
+ {
+ m_pDescription->SetText( pDesc );
+ }
+
+ m_pSourceAchievement = pSourceAchievement;
+
+ m_pPercentageBarBackground = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBarBackground" ) );
+ m_pPercentageBar = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBar" ) );
+ m_pPercentageText = SETUP_PANEL( new vgui::Label( this, "PercentageText", "" ) );
+
+ // Set the status icons
+ m_pLockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "lockedicon" ) );
+ m_pUnlockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "unlockedicon" ) );
+
+ // Gamerscore number
+ if ( IsX360() )
+ {
+ wchar_t *wzFormat = g_pVGuiLocalize->Find( "#GameUI_Achievement_Points" ); // "%s1G"
+ wchar_t wzPoints[10];
+ V_snwprintf( wzPoints, ARRAYSIZE( wzPoints ), L"%d", points );
+ wchar_t wzPointsLayout[10];
+ g_pVGuiLocalize->ConstructString( wzPointsLayout, sizeof( wzPointsLayout ), wzFormat, 1, wzPoints );
+ m_pPoints = new vgui::Label( this, "Points", wzPointsLayout );
+ }
+
+ // Achievement image
+ m_pImage = new vgui::ImagePanel( this, "icon" );
+}
+
+CAchievementItem::~CAchievementItem()
+{
+ delete m_pImage;
+ delete m_pPoints;
+ delete m_pLockedIcon;
+ delete m_pUnlockedIcon;
+ delete m_pPercentageBarBackground;
+ delete m_pPercentageBar;
+ delete m_pPercentageText;
+}
+
+void CAchievementItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y;
+
+ m_pPoints->SizeToContents();
+ m_pPoints->GetPos( x, y );
+ x = GetWide() - m_pPoints->GetWide() - m_nRightMargin;
+ m_pPoints->SetPos( x, y );
+
+}
+
+void CAchievementItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ KeyValues*pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "AchievementItem.res" );
+ ApplySettings( pKeys );
+
+ m_pImage->SetBgColor( Color( 32, 32, 32, 255 ) );
+ m_pImage->SetFgColor( Color( 32, 32, 32, 255 ) );
+ m_pImage->SetPaintBackgroundEnabled( true );
+
+ m_pPoints->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
+
+ // Set icon image
+ LoadAchievementIcon( m_pImage, m_pSourceAchievement );
+
+ // Percentage completion bar (for progressive achievements)
+ UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar );
+
+ if ( m_pSourceAchievement && m_pSourceAchievement->IsAchieved() )
+ {
+ m_pLockedIcon->SetVisible( false );
+ m_pUnlockedIcon->SetVisible ( true );
+ m_pImage->SetVisible( true );
+ }
+ else
+ {
+ m_pLockedIcon->SetVisible( true );
+ m_pUnlockedIcon->SetVisible( false );
+ m_pImage->SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------
+// CSectionedItem
+//
+// Menu item used to display some number of data entries, which are arranged
+// into columns. Supports scrolling through columns horizontally with the
+// ability to "lock" columns so they don't scroll
+//-----------------------------------------------------------------------
+CSectionedItem::CSectionedItem( CDialogMenu *pParent, const char **ppEntries, int ct )
+ : BaseClass( pParent, "", NULL, "SelectSession" )
+{
+ m_bHeader = false;
+ for ( int i = 0; i < ct; ++i )
+ {
+ AddSection( ppEntries[i], m_pParent->GetColumnAlignment( i ) );
+ }
+}
+
+CSectionedItem::~CSectionedItem()
+{
+ ClearSections();
+}
+
+void CSectionedItem::ClearSections()
+{
+ for ( int i = 0; i < m_Sections.Count(); ++i )
+ {
+ section_s &sec = m_Sections[i];
+ delete sec.pLabel;
+ }
+}
+
+void CSectionedItem::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int tall = GetTall();
+ for ( int i = 0; i < m_Sections.Count(); ++i )
+ {
+ vgui::Label *pLabel = m_Sections[i].pLabel;
+ if ( !m_bHeader )
+ {
+ pLabel->SetFont( m_pParent->GetColumnFont(i) );
+ pLabel->SetFgColor( m_pParent->GetColumnColor(i) );
+ }
+ pLabel->SetBounds( m_pParent->GetColumnXPos(i), 0, m_pParent->GetColumnWide(i), tall );
+ pLabel->SetTextInset( 10, m_bHeader ? 5 : m_pParent->GetColumnYPos(i) ); // only use ypos for the y-inset if we're not a header
+ }
+}
+
+void CSectionedItem::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+}
+
+void CSectionedItem::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SectionedItem.res" );
+ ApplySettings( pKeys );
+
+ int iLast = m_Sections.Count() -1;
+ SetWide( m_pParent->GetColumnXPos(iLast) + m_pParent->GetColumnWide(iLast) );
+}
+
+void CSectionedItem::AddSection( const char *pText, int align )
+{
+ section_s sec;
+ sec.pLabel = new vgui::Label( this, "Section", pText );
+ SETUP_PANEL( sec.pLabel );
+ sec.pLabel->SetContentAlignment( (vgui::Label::Alignment)align );
+ sec.pLabel->SetTextInset( 10, 0 );
+ sec.pLabel->SetBgColor( Color( 209, 112, 52, 128 ) );
+ m_Sections.AddToTail( sec );
+}
+
+void CSectionedItem::SetActiveColumn( int col )
+{
+ for ( int i = 0; i < m_Sections.Count(); ++i )
+ {
+ m_Sections[i].pLabel->SetPaintBackgroundEnabled( i == col );
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Generic menu for Xbox 360 matchmaking dialogs. Contains a list of CMenuItems arranged
+// vertically. The user can navigate the list using the controller and click on any
+// item. A clicked item may send a command to the dialog and the dialog responds accordingly.
+//--------------------------------------------------------------------------------------
+CDialogMenu::CDialogMenu() : BaseClass( NULL, "DialogMenu" )
+{
+ // Quiet "parent not sized yet" spew
+ SetSize( 100, 100 );
+
+ m_pParent = NULL;
+ m_pHeader = NULL;
+ m_bUseFilter = false;
+ m_bHasHeader = false;
+ m_nItemSpacing = 0;
+ m_nMinWide = 0;
+ m_nActive = -1;
+ m_nActiveColumn = -1;
+ m_nBaseRowIdx = 0;
+ m_nBaseColumnIdx = 0;
+ m_iUnlocked = 0;
+ m_nMaxVisibleItems = 1000; // arbitrarily large
+ m_nMaxVisibleColumns = 1000;// arbitrarily large
+}
+
+CDialogMenu::~CDialogMenu()
+{
+ m_MenuItems.PurgeAndDeleteElements();
+ delete m_pHeader;
+}
+
+void CDialogMenu::SetParent( CBaseDialog *pParent )
+{
+ BaseClass::SetParent( pParent );
+ m_pParent = pParent;
+}
+
+//--------------------------------------------------------------------------------------
+// Set a filter to use when reading in menu item keyvalues
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetFilter( const char *pFilter )
+{
+ if ( pFilter )
+ {
+ Q_strncpy( m_szFilter, pFilter, sizeof( m_szFilter ) );
+ m_bUseFilter = true;
+ }
+ else
+ {
+ m_bUseFilter = false;
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Add a new menu item to the item array
+//--------------------------------------------------------------------------------------
+CMenuItem *CDialogMenu::AddItemInternal( CMenuItem *pItem )
+{
+ int idx = m_MenuItems.AddToTail( pItem );
+
+ SETUP_PANEL( pItem );
+
+ return m_MenuItems[idx];
+}
+
+//--------------------------------------------------------------------------------------
+// Add a new menu item of some type that derives from CMenuItem
+//--------------------------------------------------------------------------------------
+CCommandItem *CDialogMenu::AddCommandItem( const char *pTitleLabel, const char *pDescLabel, const char *pCommand )
+{
+ return (CCommandItem*)AddItemInternal( new CCommandItem( this, pTitleLabel, pDescLabel, pCommand ) );
+}
+
+CBrowserItem *CDialogMenu::AddBrowserItem( const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing )
+{
+ // Results are added to the menu at runtime, so the layout needs to be updated after each addition.
+ CBrowserItem *pItem = (CBrowserItem*)AddItemInternal( new CBrowserItem( this, pHost, pPlayers, pScenario, pPing ) );
+ PerformLayout();
+ return pItem;
+}
+
+COptionsItem *CDialogMenu::AddOptionsItem( const char *pLabel )
+{
+ return (COptionsItem*)AddItemInternal( new COptionsItem( this, pLabel ) );
+}
+
+CAchievementItem *CDialogMenu::AddAchievementItem( const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement )
+{
+ return (CAchievementItem*)AddItemInternal( new CAchievementItem( this, pName, pDesc, points, bUnlocked, pSourceAchievement ) );
+}
+
+CSectionedItem *CDialogMenu::AddSectionedItem( const char **ppEntries, int ct )
+{
+ CSectionedItem *pItem = (CSectionedItem*)AddItemInternal( new CSectionedItem( this, ppEntries, ct ) );
+ PerformLayout();
+ return pItem;
+}
+
+CPlayerItem *CDialogMenu::AddPlayerItem( const char *pTitleLabel, int64 nId, byte bVoice, bool bReady )
+{
+ // Players are added to the lobby at runtime, so the layout needs to be updated after each addition.
+ CPlayerItem *pItem = (CPlayerItem*)AddItemInternal( new CPlayerItem( this, pTitleLabel, nId, bVoice, bReady ) );
+ PerformLayout();
+ return pItem;
+}
+
+void CDialogMenu::RemovePlayerItem( int idx )
+{
+ delete m_MenuItems[idx];
+ m_MenuItems.Remove( idx );
+ PerformLayout();
+}
+
+void CDialogMenu::ClearItems()
+{
+ m_MenuItems.PurgeAndDeleteElements();
+ InvalidateLayout();
+}
+
+//--------------------------------------------------------------------------------------
+// Set the size an position of all the menu items
+//--------------------------------------------------------------------------------------
+void CDialogMenu::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // Position the menu items and set their width
+ int yPos = 0;
+ int wide = GetWide();
+
+ if ( m_bHasHeader )
+ {
+ yPos = 40;
+ m_pHeader->SetPos( 0, 0 );
+ m_pHeader->SetWide( wide );
+ m_pHeader->PerformLayout();
+ }
+
+ for ( int i = 0; i < m_MenuItems.Count(); ++i )
+ {
+ CMenuItem *pItem = m_MenuItems[i];
+
+ pItem->SetPos( 0, yPos );
+ pItem->SetWide( wide );
+ pItem->SetActiveColumn( m_nActiveColumn );
+ pItem->PerformLayout();
+
+ if ( i < m_nBaseRowIdx || i > m_nBaseRowIdx + m_nMaxVisibleItems - 1 )
+ {
+ pItem->SetVisible( false );
+ }
+ else
+ {
+ pItem->SetVisible( true );
+ yPos += pItem->GetTall() + m_nItemSpacing;
+ }
+ }
+
+ // Reset the focus to update background colors of all menu items
+ SetFocus( m_nActive );
+
+
+ SetTall( yPos );
+}
+
+//--------------------------------------------------------------------------------------
+// Parse the res file for menu items to build out the dialog menu.
+//--------------------------------------------------------------------------------------
+void CDialogMenu::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ m_nItemSpacing = pResourceData->GetInt( "itemspacing", 2 );
+ m_nMinWide = pResourceData->GetInt( "minwide", 0 );
+ m_nActiveColumn = pResourceData->GetInt( "activecolumn", -1 );
+ m_nMaxVisibleItems = pResourceData->GetInt( "maxvisibleitems", 1000 ); // arbitrarily large
+ m_nMaxVisibleColumns = pResourceData->GetInt( "maxvisiblecolumns", 1000 ); // arbitrarily large
+
+ KeyValues *pColumnData = pResourceData->FindKey( "Columns" );
+ if ( pColumnData )
+ {
+ int xPos = 0;
+ int idx = 0;
+ const char *ppHeader[MAX_COLUMNS];
+ for ( KeyValues *pColumn = pColumnData->GetFirstSubKey(); pColumn != NULL; pColumn = pColumn->GetNextKey() )
+ {
+ if ( !Q_stricmp( pColumn->GetName(), "Column" ) )
+ {
+ columninfo_s col;
+ col.bSortDown = true;
+ col.xpos = pColumn->GetInt( "xpos", xPos );
+ col.ypos = pColumn->GetInt( "ypos", 0 );
+ col.wide = pColumn->GetInt( "wide", 0 );
+ col.align = pColumn->GetInt( "align", 3 ); // west by default
+ col.bLocked = pColumn->GetInt( "locked", 0 );
+ col.hFont = m_pScheme->GetFont( pColumn->GetString( "font", "default" ) );
+ col.color = m_pScheme->GetColor( pColumn->GetString( "fgcolor" ), Color( 0, 0, 0, 255 ) );
+
+ ppHeader[idx++] = pColumn->GetString( "header", "" );
+
+ xPos = col.xpos + col.wide;
+ m_Columns.AddToTail( col );
+
+ if ( col.bLocked )
+ {
+ m_nBaseColumnIdx = idx;
+ m_iUnlocked = idx;
+ }
+ }
+ }
+ m_bHasHeader = true;
+ m_pHeader = new CSectionedItem( this, ppHeader, idx );
+ m_pHeader->m_bHeader = true;
+ SETUP_PANEL( m_pHeader );
+
+ m_pHeader->SetPaintBackgroundEnabled( false );
+ vgui::HFont headerFont = m_pScheme->GetFont( pColumnData->GetString( "headerfont", "default" ) );
+ Color headerColor = m_pScheme->GetColor( pColumnData->GetString( "headerfgcolor" ), Color( 0, 0, 0, 255 ) );
+ for ( int i = 0; i < idx; ++i )
+ {
+ vgui::Label *pLabel = m_pHeader->m_Sections[i].pLabel;
+ pLabel->SetFont( headerFont );
+ pLabel->SetFgColor( headerColor );
+ pLabel->SetPaintBackgroundEnabled( false );
+ }
+ }
+
+ for ( KeyValues *pMenuData = pResourceData->GetFirstSubKey(); pMenuData != NULL; pMenuData = pMenuData->GetNextKey() )
+ {
+ // See if we should skip over this block
+ if ( m_bUseFilter )
+ {
+ if ( pMenuData->GetInt( m_szFilter, 0 ) == 0 )
+ continue;
+ }
+
+ // Give our parent a chance to change the properties of this item
+ m_pParent->OverrideMenuItem( pMenuData );
+
+ if ( !Q_stricmp( pMenuData->GetName(), "CommandItem" ) )
+ {
+ // New Command Item
+ const char *label = pMenuData->GetString( "label", "<unknown>" );
+ const char *description = pMenuData->GetString( "description", NULL );
+ const char *command = pMenuData->GetString( "command", "<unknown>" );
+
+ AddCommandItem( label, description, command );
+ }
+ else if ( !Q_stricmp( pMenuData->GetName(), "OptionsItem" ) )
+ {
+ // New Options Item
+ COptionsItem *pItem = AddOptionsItem( pMenuData->GetString( "label", "<unknown>" ) );
+
+ // ID and ValueType and the same for all option values
+ const char *pID = pMenuData->GetString( "id", "NULL" );
+ const char *pValueType = pMenuData->GetString( "valuetype", NULL );
+
+ // Add all the options
+ for ( KeyValues *pValue = pMenuData->GetFirstSubKey(); pValue != NULL; pValue = pValue->GetNextKey() )
+ {
+ if ( !Q_stricmp( pValue->GetName(), "Option" ) )
+ {
+ sessionProperty_t prop;
+ prop.nType = SESSION_CONTEXT;
+ Q_strncpy( prop.szID, pID, sizeof( prop.szID ) );
+ Q_strncpy( prop.szValue, pValue->GetString( "value", "NULL" ), sizeof( prop.szValue ) );
+
+ if ( pValueType )
+ {
+ // Only session properties have a type
+ prop.nType = SESSION_PROPERTY;
+ Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) );
+ }
+
+ const char *pLabel = pValue->GetString( "label", "<unknown>" );
+ pItem->AddOption( pLabel, prop );
+ }
+ }
+
+ // Add range items after the specified items
+ if ( pMenuData->GetInt( "userange" ) )
+ {
+ // Options are an implicit range of integers
+ int nStart = pMenuData->GetInt( "rangelow" );
+ int nEnd = pMenuData->GetInt( "rangehigh" );
+ int nInterval = pMenuData->GetInt( "interval", 1 );
+
+ // Prevent total destruction from a bad resource file
+ if ( nEnd < nStart )
+ {
+ nEnd = nStart;
+ }
+
+ for ( int i = nStart; i <= nEnd; i += nInterval )
+ {
+ sessionProperty_t prop;
+ prop.nType = SESSION_PROPERTY;
+ Q_strncpy( prop.szID, pID, sizeof( prop.szID ) );
+ Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) );
+ Q_snprintf( prop.szValue, sizeof(prop.szValue), "%d", i );
+
+ pItem->AddOption( prop.szValue, prop );
+ }
+ }
+
+ // Set the default active option
+ int active = pMenuData->GetInt( "activeoption", 0 );
+ pItem->SetOptionFocus( active );
+
+ // Notify our parent that each option has been set to its current setting
+ KeyValues *kv = new KeyValues( "MenuItemChanged", "item", GetItemCount() - 1 );
+ PostActionSignal( kv );
+ }
+ }
+
+ // Calculate the final menu size according to the widest menu item
+ int wide = m_nMinWide;
+ for ( int i = 0; i < m_MenuItems.Count(); ++i )
+ {
+ wide = max( wide, m_MenuItems[i]->GetWide() );
+ }
+ SetWide( wide );
+}
+
+//--------------------------------------------------------------------------------------
+// Cache off the scheme
+//--------------------------------------------------------------------------------------
+void CDialogMenu::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pScheme = pScheme;
+}
+
+//--------------------------------------------------------------------------------------
+// Give focus (highlights) a particular menu item by index
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetFocus( int idx )
+{
+ int itemCt = (unsigned int)m_MenuItems.Count();
+ if ( idx >= itemCt )
+ return;
+
+ for ( int i = 0; i < itemCt; ++i )
+ {
+ m_MenuItems[i]->SetFocus( i == idx );
+ }
+ m_nActive = idx;
+
+ if ( m_nActive >= 0 && m_nActive < m_nBaseRowIdx )
+ {
+ m_nBaseRowIdx = m_nActive;
+ }
+ else if ( m_nActive > m_nBaseRowIdx + m_nMaxVisibleItems - 1 )
+ {
+ m_nBaseRowIdx = m_nActive - ( m_nMaxVisibleItems - 1 );
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Sort the menu items according to the selected column
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SortMenuItems()
+{
+ if ( !m_bHasHeader )
+ return;
+
+ // Simple bubble sort
+ char szBufferOne[32];
+ char szBufferTwo[32];
+ bool bSortDown = GetColumnSortType( m_nActiveColumn );
+ for ( int i = 1; i <= m_MenuItems.Count(); ++i )
+ {
+ for ( int j = 0; j < m_MenuItems.Count() - i; ++j )
+ {
+ ((CSectionedItem*)m_MenuItems[j])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferOne, sizeof( szBufferOne ) );
+ ((CSectionedItem*)m_MenuItems[j+1])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferTwo, sizeof( szBufferTwo ) );
+
+ int diff = Q_stricmp( szBufferOne, szBufferTwo );
+ bool bSwap = bSortDown ? diff > 0 : diff < 0;
+ if ( bSwap )
+ {
+ CMenuItem *pTemp = m_MenuItems[j+1];
+ m_MenuItems[j+1] = m_MenuItems[j];
+ m_MenuItems[j] = pTemp;
+
+ m_pParent->SwapMenuItems( j, j+1 );
+ }
+ }
+ }
+ InvalidateLayout();
+}
+
+//--------------------------------------------------------------------------------------
+// Move item focus to the next item in the menu - supports wrapping
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetFocusNext()
+{
+ if ( m_MenuItems.Count() )
+ {
+ int iNewIndex = ( m_nActive + 1 ) % m_MenuItems.Count();
+
+ int i = 0;
+ bool bSet = false;
+ while ( i < m_MenuItems.Count() )
+ {
+ if ( m_MenuItems[iNewIndex]->IsEnabled() )
+ {
+ SetFocus( iNewIndex );
+ bSet = true;
+ break;
+ }
+
+ iNewIndex = ( iNewIndex + 1 ) % m_MenuItems.Count();
+ i++;
+ }
+
+ InvalidateLayout();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// Move item focus to the previous item in the menu - supports wrapping
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetFocusPrev()
+{
+ if ( m_MenuItems.Count() )
+ {
+ int iNewIndex = m_nActive - 1;
+ if ( iNewIndex < 0 )
+ iNewIndex = m_MenuItems.Count() - 1;
+
+ int i = 0;
+ bool bSet = false;
+ while ( i < m_MenuItems.Count() )
+ {
+ if ( m_MenuItems[iNewIndex]->IsEnabled() )
+ {
+ SetFocus( iNewIndex );
+ bSet = true;
+ break;
+ }
+
+ if ( --iNewIndex < 0 )
+ iNewIndex = m_MenuItems.Count() - 1;
+
+ i++;
+ }
+
+ InvalidateLayout();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For OptionsItems: Move focus to the next option in the menu item - does not wrap
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetOptionFocusNext()
+{
+ COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) );
+ if ( pItem )
+ {
+ pItem->SetOptionFocusNext();
+
+ KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive );
+ PostActionSignal( kv );
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For OptionsItems: Move focus to the previous option in the menu item - does not wrap
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetOptionFocusPrev()
+{
+ COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) );
+ if ( pItem )
+ {
+ pItem->SetOptionFocusPrev();
+
+ KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive );
+ PostActionSignal( kv );
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For OptionsItems: Update the base index for the columns
+//--------------------------------------------------------------------------------------
+void CDialogMenu::UpdateBaseColumnIndex()
+{
+ if ( m_iUnlocked + m_nActiveColumn - m_nBaseColumnIdx >= m_nMaxVisibleColumns )
+ {
+ m_nBaseColumnIdx = m_iUnlocked + m_nActiveColumn - m_nMaxVisibleColumns + 1;
+ }
+ else if ( m_nActiveColumn - m_nBaseColumnIdx < 0 )
+ {
+ m_nBaseColumnIdx = m_nActiveColumn;
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For menus with sectioned columns - move focus to the next column
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetColumnFocusNext()
+{
+ if ( m_nActiveColumn == -1 )
+ return;
+
+ if ( m_Columns.Count() )
+ {
+ int iNewColumn = m_nActiveColumn + 1;
+ if ( iNewColumn >= m_Columns.Count() )
+ return;
+
+ m_nActiveColumn = iNewColumn;
+ UpdateBaseColumnIndex();
+ InvalidateLayout();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For menus with sectioned columns - move focus to the next column
+//--------------------------------------------------------------------------------------
+void CDialogMenu::SetColumnFocusPrev()
+{
+ if ( m_nActiveColumn == -1 )
+ return;
+
+ if ( m_Columns.Count() )
+ {
+ int iNewColumn = m_nActiveColumn - 1;
+ if ( iNewColumn < 0 || m_Columns[iNewColumn].bLocked )
+ return;
+
+ m_nActiveColumn = iNewColumn;
+ UpdateBaseColumnIndex();
+ InvalidateLayout();
+ }
+}
+
+//--------------------------------------------------------------------------------------
+// For OptionsItems: Lets the dialog find out which option is currently selected
+//--------------------------------------------------------------------------------------
+int CDialogMenu::GetActiveOptionIndex( int nMenuItemIdx )
+{
+ int retval = -1;
+ COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( nMenuItemIdx ) );
+ if ( pItem )
+ {
+ retval = pItem->GetActiveOptionIndex();
+ }
+ return retval;
+}
+
+//-----------------------------------------------------------------------
+// Return the index of the current active menu item
+//-----------------------------------------------------------------------
+int CDialogMenu::GetActiveItemIndex()
+{
+ return m_nActive;
+}
+
+//-----------------------------------------------------------------------
+// Return the index of the current active menu column
+//-----------------------------------------------------------------------
+int CDialogMenu::GetActiveColumnIndex()
+{
+ return m_nActiveColumn;
+}
+
+//-----------------------------------------------------------------------
+// Return the number of menu items
+//-----------------------------------------------------------------------
+int CDialogMenu::GetItemCount()
+{
+ return m_MenuItems.Count();
+}
+
+//-----------------------------------------------------------------------
+// Return the number of visible menu items
+//-----------------------------------------------------------------------
+int CDialogMenu::GetVisibleItemCount()
+{
+ return min( GetItemCount(), m_nMaxVisibleItems );
+}
+
+//-----------------------------------------------------------------------
+// Return the number of visible menu columns
+//-----------------------------------------------------------------------
+int CDialogMenu::GetVisibleColumnCount()
+{
+ return m_nMaxVisibleColumns;
+}
+
+//-----------------------------------------------------------------------
+// Return the index of the first unlocked column
+//-----------------------------------------------------------------------
+int CDialogMenu::GetFirstUnlockedColumnIndex()
+{
+ return m_iUnlocked;
+}
+
+//-----------------------------------------------------------------------
+// Return the first visible index in the menu
+//-----------------------------------------------------------------------
+int CDialogMenu::GetBaseRowIndex()
+{
+ return m_nBaseRowIdx;
+}
+
+//-----------------------------------------------------------------------
+// Set the first visible index in the menu
+//-----------------------------------------------------------------------
+void CDialogMenu::SetBaseRowIndex( int idx )
+{
+ m_nBaseRowIdx = idx;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified menu item
+//-----------------------------------------------------------------------
+CMenuItem *CDialogMenu::GetItem( int idx )
+{
+ if ( m_MenuItems.IsValidIndex( idx ) )
+ {
+ return m_MenuItems[idx];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column xpos
+//-----------------------------------------------------------------------
+int CDialogMenu::GetColumnXPos( int idx )
+{
+ // Compensate for scrolling offsets
+ columninfo_s &col = m_Columns[idx];
+
+ int xpos;
+ if ( col.bLocked )
+ {
+ xpos = m_Columns[idx].xpos;
+ }
+ else
+ {
+ int trueIdx = m_iUnlocked + idx - m_nBaseColumnIdx;
+ if ( trueIdx < m_iUnlocked )
+ {
+ // Put it offscreen
+ xpos = -100 - col.wide;
+ }
+ else
+ {
+ xpos = m_Columns[trueIdx].xpos;
+ }
+ }
+ return xpos;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column ypos
+//-----------------------------------------------------------------------
+int CDialogMenu::GetColumnYPos( int idx )
+{
+ return m_Columns[idx].ypos;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column width
+//-----------------------------------------------------------------------
+int CDialogMenu::GetColumnWide( int idx )
+{
+ return m_Columns[idx].wide;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column alignment
+//-----------------------------------------------------------------------
+int CDialogMenu::GetColumnAlignment( int idx )
+{
+ return m_Columns[idx].align;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column font
+//-----------------------------------------------------------------------
+HFont CDialogMenu::GetColumnFont( int idx )
+{
+ return m_Columns[idx].hFont;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column fgcolor
+//-----------------------------------------------------------------------
+Color CDialogMenu::GetColumnColor( int idx )
+{
+ return m_Columns[idx].color;
+}
+
+//-----------------------------------------------------------------------
+// Return the specified column fgcolor
+//-----------------------------------------------------------------------
+bool CDialogMenu::GetColumnSortType( int idx )
+{
+ bool bSortDown = m_Columns[idx].bSortDown;
+ m_Columns[idx].bSortDown = !bSortDown;
+ return bSortDown;
+}
+
+//--------------------------------------------------------------------------------------
+// Receive the command from a clicked menu item and forwards it to the parent dialog
+//--------------------------------------------------------------------------------------
+void CDialogMenu::OnCommand( const char *pCommand )
+{
+ GetParent()->OnCommand( pCommand );
+}
+
+//--------------------------------------------------------------------------------------
+// Update the menu state according to controller input.
+// Returns whether or not the keycode was handled by the menu.
+//--------------------------------------------------------------------------------------
+bool CDialogMenu::HandleKeyCode( vgui::KeyCode code )
+{
+ switch( code )
+ {
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case STEAMCONTROLLER_DPAD_DOWN:
+ SetFocusNext();
+ break;
+
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case STEAMCONTROLLER_DPAD_UP:
+ SetFocusPrev();
+ break;
+
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case STEAMCONTROLLER_DPAD_LEFT:
+ SetOptionFocusPrev();
+ SetColumnFocusPrev();
+ break;
+
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case STEAMCONTROLLER_DPAD_RIGHT:
+ SetOptionFocusNext();
+ SetColumnFocusNext();
+ break;
+
+ case KEY_XBUTTON_A:
+ case STEAMCONTROLLER_A:
+ if ( m_MenuItems.Count() && m_nActive >= 0 )
+ {
+ m_MenuItems[m_nActive]->OnClick();
+ }
+ break;
+
+ case KEY_XBUTTON_Y:
+ case STEAMCONTROLLER_Y:
+ SortMenuItems();
+ break;
+
+ default:
+ // Not handled
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/gameui/matchmaking/dialogmenu.h b/gameui/matchmaking/dialogmenu.h
new file mode 100644
index 0000000..83940d9
--- /dev/null
+++ b/gameui/matchmaking/dialogmenu.h
@@ -0,0 +1,367 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DIALOGMENU_H
+#define DIALOGMENU_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined(_WIN32) && !defined(_X360)
+#include "winlite.h" // FILETIME
+#endif
+
+#include "vgui_controls/Panel.h"
+#include "vgui_controls/Frame.h"
+
+class IAchievement;
+
+#define MAX_COMMAND_LEN 256
+#define MAX_COLUMNS 32
+
+class CDialogMenu;
+class CBaseDialog;
+
+struct sessionProperty_t
+{
+ static const int MAX_KEY_LEN = 64;
+ byte nType;
+ char szID[MAX_KEY_LEN];
+ char szValue[MAX_KEY_LEN];
+ char szValueType[MAX_KEY_LEN];
+};
+
+//-----------------------------------------------------------------------
+// Base class representing a generic menu item. Supports two text labels,
+// where the first label is the "action" text and the second is an optional
+// description of the action.
+//-----------------------------------------------------------------------
+class CMenuItem : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CMenuItem, vgui::EditablePanel );
+
+public:
+ CMenuItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription );
+ virtual ~CMenuItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *pSettings );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void SetFocus( const bool bActive );
+ virtual void SetEnabled( bool bEnabled );
+ virtual void SetActiveColumn( int col );
+ virtual bool IsEnabled();
+ virtual void OnClick();
+
+protected:
+ CDialogMenu *m_pParent;
+
+ vgui::Label *m_pTitle;
+ vgui::Label *m_pDescription;
+
+ Color m_BgColor;
+ Color m_BgColorActive;
+
+ int m_nDisabledAlpha;
+ int m_nBottomMargin;
+ int m_nRightMargin;
+
+ bool m_bEnabled;
+};
+
+//-----------------------------------------------------------------------
+// CCommandItem
+//
+// Menu item that issues a command when clicked.
+//-----------------------------------------------------------------------
+class CCommandItem : public CMenuItem
+{
+ DECLARE_CLASS_SIMPLE( CCommandItem, CMenuItem );
+
+public:
+ CCommandItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription, const char *pCommand );
+ virtual ~CCommandItem();
+
+ virtual void OnClick();
+ virtual void SetFocus( const bool bActive );
+
+ bool m_bHasFocus;
+
+ char m_szCommand[MAX_PATH];
+};
+
+//-----------------------------------------------------------------------
+// CPlayerItem
+//
+// Menu item to display a player in the lobby.
+//-----------------------------------------------------------------------
+class CPlayerItem : public CCommandItem
+{
+ DECLARE_CLASS_SIMPLE( CMenuItem, CCommandItem );
+
+public:
+ CPlayerItem( CDialogMenu *pParent, const char *pTitle, int64 nId, byte bVoice, bool bReady );
+ virtual ~CPlayerItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void OnClick();
+
+ vgui::Label *m_pVoiceIcon;
+ vgui::Label *m_pReadyIcon;
+
+ byte m_bVoice;
+ bool m_bReady;
+ uint64 m_nId;
+};
+
+//-----------------------------------------------------------------------
+// CBrowserItem
+//
+// Menu item used to display session search results, etc.
+//-----------------------------------------------------------------------
+class CBrowserItem : public CCommandItem
+{
+ DECLARE_CLASS_SIMPLE( CBrowserItem, CCommandItem );
+
+public:
+ CBrowserItem( CDialogMenu *pParent, const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing );
+ virtual ~CBrowserItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *pSettings );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+private:
+ vgui::Label *m_pPlayers;
+ vgui::Label *m_pScenario;
+ vgui::Label *m_pPing;
+};
+
+//-----------------------------------------------------------------------
+// COptionsItem
+//
+// Menu item used to present a list of options for the player to select
+// from, such as "choose a map" or "number of rounds".
+//-----------------------------------------------------------------------
+class COptionsItem : public CMenuItem
+{
+ DECLARE_CLASS_SIMPLE( COptionsItem, CMenuItem );
+
+public:
+ COptionsItem( CDialogMenu *pParent, const char *pLabel );
+ virtual ~COptionsItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *pSettings );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void SetFocus( const bool bActive );
+
+ void SetOptionFocus( unsigned int idx );
+ void SetOptionFocusNext();
+ void SetOptionFocusPrev();
+
+ void AddOption( const char *pLabelText, const sessionProperty_t &option );
+ int GetActiveOptionIndex();
+ const sessionProperty_t &GetActiveOption();
+
+ void DeleteAllOptions()
+ {
+ m_Options.RemoveAll();
+ m_OptionLabels.PurgeAndDeleteElements();
+ m_nActiveOption = m_Options.InvalidIndex();
+ }
+private:
+ int m_nActiveOption;
+ int m_nOptionsXPos;
+ int m_nOptionsMinWide;
+ int m_nOptionsLeftMargin;
+ int m_nMaxOptionWidth;
+ int m_nArrowGap;
+
+ CUtlVector< vgui::Label* > m_OptionLabels;
+ CUtlVector< sessionProperty_t > m_Options;
+
+ char m_szOptionsFont[64];
+ vgui::HFont m_hOptionsFont;
+
+ vgui::Label *m_pLeftArrow;
+ vgui::Label *m_pRightArrow;
+};
+
+//-----------------------------------------------------------------------
+// CAchievementItem
+//
+// Menu item used to present an achievement - including image, title,
+// description, points and unlock date. Clicking the item opens another
+// dialog with additional information about the achievement.
+//-----------------------------------------------------------------------
+class CAchievementItem : public CMenuItem
+{
+ DECLARE_CLASS_SIMPLE( CAchievementItem, CMenuItem );
+
+public:
+ CAchievementItem( CDialogMenu *pParent, const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement );
+ virtual ~CAchievementItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+private:
+ vgui::Label *m_pPoints;
+ vgui::ImagePanel *m_pLockedIcon;
+ vgui::ImagePanel *m_pUnlockedIcon;
+ vgui::ImagePanel *m_pImage;
+
+ vgui::ImagePanel *m_pPercentageBarBackground;
+ vgui::ImagePanel *m_pPercentageBar;
+ vgui::Label *m_pPercentageText;
+
+ IAchievement *m_pSourceAchievement;
+
+ Color m_AchievedBGColor;
+ Color m_UnachievedBGColor;
+
+ CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" );
+};
+
+//-----------------------------------------------------------------------
+// CSectionedItem
+//
+// Menu item used to display some number of data entries, which are arranged
+// into columns. Supports scrolling through columns horizontally with the
+// ability to "lock" columns so they don't scroll
+//-----------------------------------------------------------------------
+class CSectionedItem : public CCommandItem
+{
+ DECLARE_CLASS_SIMPLE( CSectionedItem, CCommandItem );
+
+public:
+ CSectionedItem( CDialogMenu *pParent, const char **ppEntries, int ct );
+ virtual ~CSectionedItem();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *pSettings );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void SetActiveColumn( int col );
+
+ void ClearSections();
+ void AddSection( const char *pText, int wide );
+
+ struct section_s
+ {
+ int wide;
+ vgui::Label *pLabel;
+ };
+ CUtlVector< section_s >m_Sections;
+
+ bool m_bHeader;
+};
+
+//--------------------------------------------------------------------------------------
+// Generic menu for Xbox 360 matchmaking dialogs. Contains a list of CMenuItems arranged
+// vertically. The user can navigate the list using the controller and click on any
+// item. A clicked item may send a command to the dialog and the dialog responds accordingly.
+//--------------------------------------------------------------------------------------
+class CDialogMenu : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CDialogMenu, vgui::Panel );
+
+public:
+ CDialogMenu();
+ ~CDialogMenu();
+
+ virtual void OnCommand( const char *pCommand );
+ virtual void ApplySettings( KeyValues *inResourceData );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void PerformLayout();
+ void SetFilter( const char *pFilter );
+ virtual bool HandleKeyCode( vgui::KeyCode code );
+ void SetMaxVisibleItems( uint nMaxVisibleItems );
+ void SetParent( CBaseDialog *pParent );
+
+ // Menu items
+ CCommandItem *AddCommandItem( const char *pTitleLabel, const char *pDescLabel, const char *pCommand );
+ CPlayerItem *AddPlayerItem( const char *pTitleLabel, int64 nId, byte bVoice, bool bReady );
+ CBrowserItem *AddBrowserItem( const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing );
+ COptionsItem *AddOptionsItem( const char *pLabel );
+ CSectionedItem *AddSectionedItem( const char **ppEntries, int ct );
+ CAchievementItem *AddAchievementItem( const wchar_t *pName, const wchar_t *pDesc, uint cred, bool bUnlocked, IAchievement* pSourceAchievement );
+ CMenuItem *AddItemInternal( CMenuItem *pItem );
+
+ void RemovePlayerItem( int idx );
+ void SortMenuItems();
+ void ClearItems();
+
+ // Navigation
+ void SetFocus( int idx );
+ void SetFocusNext();
+ void SetFocusPrev();
+ void SetOptionFocusNext();
+ void SetOptionFocusPrev();
+ void SetColumnFocusNext();
+ void SetColumnFocusPrev();
+ void UpdateBaseColumnIndex();
+
+ // Accessors
+ CMenuItem *GetItem( int idx);
+ int GetItemCount();
+ int GetActiveItemIndex();
+ int GetActiveColumnIndex();
+ int GetActiveOptionIndex( int idx );
+ int GetVisibleItemCount();
+ int GetVisibleColumnCount();
+ int GetFirstUnlockedColumnIndex();
+ int GetBaseRowIndex();
+ void SetBaseRowIndex( int idx );
+ int GetColumnXPos( int idx );
+ int GetColumnYPos( int idx );
+ int GetColumnWide( int idx );
+ int GetColumnAlignment( int idx );
+ vgui::HFont GetColumnFont( int idx );
+ Color GetColumnColor( int idx );
+ bool GetColumnSortType( int idx );
+
+private:
+ struct columninfo_s
+ {
+ int xpos;
+ int ypos;
+ int wide;
+ int align;
+ bool bLocked;
+ Color color;
+ vgui::HFont hFont;
+ bool bSortDown;
+ };
+ CUtlVector< columninfo_s >m_Columns;
+ CUtlVector< CMenuItem* > m_MenuItems;
+
+ CBaseDialog *m_pParent;
+ CSectionedItem *m_pHeader;
+ vgui::IScheme *m_pScheme;
+
+ char m_szFilter[MAX_COMMAND_LEN]; // string to use as a keyvalues filter when reading in menu items
+
+ int m_nItemSpacing; // gap between menu items
+ int m_nMinWide; // minimum width - final menu width will always be >= m_nMinWide
+
+ bool m_bInitialized;
+ bool m_bUseFilter;
+ bool m_bHasHeader;
+ int m_nMaxVisibleItems; // max number of items to display in the menu
+ int m_nMaxVisibleColumns; // max number of columns to display in the menu
+ int m_nActiveColumn; // index of the current active column
+ int m_nBaseColumnIdx; // array index of the first non-static column
+ int m_nBaseRowIdx; // array index of the first visible row
+ int m_nActive; // index of the current active item
+ int m_iUnlocked; // first unlocked column in the menu
+};
+
+#endif // DIALOGMENU_H
diff --git a/gameui/matchmaking/leaderboarddialog.cpp b/gameui/matchmaking/leaderboarddialog.cpp
new file mode 100644
index 0000000..93885db
--- /dev/null
+++ b/gameui/matchmaking/leaderboarddialog.cpp
@@ -0,0 +1,563 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Displays a leaderboard
+//
+//=============================================================================//
+
+#include "leaderboarddialog.h"
+#include "vgui_controls/Label.h"
+#include "vgui/ILocalize.h"
+#include "hl2orange.spa.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define NUM_ROWS_PER_QUERY 100
+
+CLeaderboardDialog *g_pLeaderboardDialog;
+
+//----------------------------------------------------------
+// CLeaderboardDialog
+//----------------------------------------------------------
+CLeaderboardDialog::CLeaderboardDialog( vgui::Panel *pParent ) : BaseClass( pParent, "LeaderboardDialog" )
+{
+ g_pLeaderboardDialog = this;
+ m_iBaseRank = 0;
+ m_iActiveRank = 0;
+ m_iMaxRank = 0;
+ m_cColumns = 0;
+ m_iRangeBase = 0;
+#if defined( _X360 )
+ m_pStats = NULL;
+#endif
+
+ m_pProgressBg = new vgui::Panel( this, "ProgressBg" );
+ m_pProgressBar = new vgui::Panel( this, "ProgressBar" );
+ m_pProgressPercent = new vgui::Label( this, "ProgressPercent", "" );
+ m_pNumbering = new vgui::Label( this, "Numbering", "" );
+ m_pUpArrow = new vgui::Label( this, "UpArrow", "" );
+ m_pDownArrow = new vgui::Label( this, "DownArrow", "" );
+ m_pBestMoments = new vgui::Label( this, "BestMoments", "" );
+}
+
+CLeaderboardDialog::~CLeaderboardDialog()
+{
+ CleanupStats();
+
+ delete m_pProgressBg;
+ delete m_pProgressBar;
+ delete m_pProgressPercent;
+ delete m_pNumbering;
+ delete m_pUpArrow;
+ delete m_pDownArrow;
+}
+
+//----------------------------------------------------------
+// Clean up the stats array
+//----------------------------------------------------------
+void CLeaderboardDialog::CleanupStats()
+{
+#if defined( _X360 )
+ if ( m_pStats )
+ {
+ delete [] m_pStats;
+ m_pStats = NULL;
+ }
+#endif
+}
+
+//----------------------------------------------------------
+// Position the dialogs elements
+//----------------------------------------------------------
+void CLeaderboardDialog::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ if ( m_cColumns )
+ {
+ int x, y, wide, tall;
+ m_pProgressBg->GetBounds( x, y, wide, tall );
+
+ int columnWide = wide / m_cColumns;
+ int lockedColumns = m_Menu.GetFirstUnlockedColumnIndex();
+ int visibleColumns = m_Menu.GetVisibleColumnCount() - lockedColumns;
+ int iColumn = m_Menu.GetActiveColumnIndex() - lockedColumns;
+
+ if ( iColumn < 0 )
+ {
+ iColumn = 0;
+ }
+ else if ( iColumn < m_iRangeBase )
+ {
+ m_iRangeBase = iColumn;
+ }
+ else if ( iColumn >= m_iRangeBase + visibleColumns )
+ {
+ m_iRangeBase = iColumn - visibleColumns + 1;
+ }
+
+ m_pProgressBg->SetBounds( x, y, columnWide * m_cColumns, tall );
+ m_pProgressBar->SetBounds( x + columnWide * m_iRangeBase, y, columnWide * visibleColumns, tall );
+ }
+ else
+ {
+ m_pProgressBg->SetVisible( false );
+ m_pProgressBar->SetVisible( false );
+ }
+
+ int menux, menuy;
+ m_Menu.GetPos( menux, menuy );
+
+ // Do a perform layout on the menu so we get the correct height now
+ m_Menu.InvalidateLayout( true, false );
+
+ m_pNumbering->SizeToContents();
+
+ wchar_t wszNumbering[64];
+ wchar_t *wzNumberingFmt = g_pVGuiLocalize->Find( "#GameUI_Achievement_Menu_Range" );
+ wchar_t wzActiveItem[8];
+ wchar_t wzTotal[8];
+
+ int iActive = m_iBaseRank + m_Menu.GetActiveItemIndex();
+ if ( iActive < 0 )
+ {
+ iActive = 0;
+ }
+ V_snwprintf( wzActiveItem, ARRAYSIZE( wzActiveItem ), L"%d", iActive );
+ V_snwprintf( wzTotal, ARRAYSIZE( wzTotal ), L"%d", m_iMaxRank );
+ g_pVGuiLocalize->ConstructString( wszNumbering, sizeof( wszNumbering ), wzNumberingFmt, 2, wzActiveItem, wzTotal );
+ m_pNumbering->SetText( wszNumbering );
+ m_pNumbering->SetWide( GetWide() );
+
+ MoveToCenterOfScreen();
+}
+
+//----------------------------------------------------------
+//
+//----------------------------------------------------------
+void CLeaderboardDialog::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_DOWN, 0.05f );
+ m_KeyRepeat.SetKeyRepeatTime( KEY_XSTICK1_DOWN, 0.05f );
+ m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_UP, 0.05f );
+ m_KeyRepeat.SetKeyRepeatTime( KEY_XSTICK1_UP, 0.05f );
+}
+
+//----------------------------------------------------------
+//
+//----------------------------------------------------------
+void CLeaderboardDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pProgressBg->SetBgColor( Color( 200, 184, 151, 255 ) );
+ m_pProgressBar->SetBgColor( Color( 179, 82, 22, 255 ) );
+ m_pNumbering->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
+ m_pBestMoments->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
+}
+
+//----------------------------------------------------------
+//
+//----------------------------------------------------------
+void CLeaderboardDialog::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "CenterOnPlayer" ) )
+ {
+ if ( GetPlayerStats( -1 ) == 0 )
+ {
+ // Player isn't on the board, just start at rank 1
+ GetPlayerStats( 1 );
+ }
+ }
+ else if ( !Q_stricmp( pCommand, "Friends" ) )
+ {
+ GetPlayerStats( -1, true );
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+//----------------------------------------------------------
+//
+//----------------------------------------------------------
+void CLeaderboardDialog::AddLeaderboardEntry( const char **ppEntries, int ct )
+{
+ m_Menu.AddSectionedItem( ppEntries, ct );
+}
+
+//----------------------------------------------------------
+// Get some portion of the leaderboard. This should ideally live
+// in the client, since it's very mod-specific
+//----------------------------------------------------------
+bool CLeaderboardDialog::GetPlayerStats( int rank, bool bFriends )
+{
+#if defined _X360
+ HANDLE handle;
+
+ // Retrieve the necessary buffer size
+ DWORD cbResults = 0;
+
+ bool bRanked = false;
+ const char *pName = GetName();
+ if ( !Q_stricmp( pName, "LeaderboardDialog_Ranked" ) )
+ {
+ bRanked = true;
+ }
+
+ XUSER_STATS_SPEC spec;
+ if ( !bRanked )
+ {
+ spec.dwViewId = STATS_VIEW_PLAYER_MAX_UNRANKED;
+ spec.dwNumColumnIds = 15;
+ spec.rgwColumnIds[0] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINTS_SCORED;
+ spec.rgwColumnIds[1] = STATS_COLUMN_PLAYER_MAX_UNRANKED_KILLS;
+ spec.rgwColumnIds[2] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINTS_CAPPED;
+ spec.rgwColumnIds[3] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINT_DEFENSES;
+ spec.rgwColumnIds[4] = STATS_COLUMN_PLAYER_MAX_UNRANKED_DOMINATIONS;
+ spec.rgwColumnIds[5] = STATS_COLUMN_PLAYER_MAX_UNRANKED_REVENGE;
+ spec.rgwColumnIds[6] = STATS_COLUMN_PLAYER_MAX_UNRANKED_BUILDINGS_DESTROYED;
+ spec.rgwColumnIds[7] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEADSHOTS;
+ spec.rgwColumnIds[8] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEALTH_POINTS_HEALED;
+ spec.rgwColumnIds[9] = STATS_COLUMN_PLAYER_MAX_UNRANKED_INVULNS;
+ spec.rgwColumnIds[10] = STATS_COLUMN_PLAYER_MAX_UNRANKED_KILL_ASSISTS;
+ spec.rgwColumnIds[11] = STATS_COLUMN_PLAYER_MAX_UNRANKED_BACKSTABS;
+ spec.rgwColumnIds[12] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEALTH_POINTS_LEACHED;
+ spec.rgwColumnIds[13] = STATS_COLUMN_PLAYER_MAX_UNRANKED_SENTRY_KILLS;
+ spec.rgwColumnIds[14] = STATS_COLUMN_PLAYER_MAX_UNRANKED_TELEPORTS;
+ m_cColumns = 15;
+ }
+ else
+ {
+ spec.dwViewId = STATS_VIEW_PLAYER_MAX_RANKED;
+ spec.dwNumColumnIds = 1;
+ spec.rgwColumnIds[ 0 ] = STATS_COLUMN_PLAYER_MAX_RANKED_POINTS_SCORED;
+
+ // set to zero to hide the progress bar
+ m_cColumns = 0;
+ }
+
+ DWORD ret;
+ XUID xuid = 0u;
+ XUID xuidFriends[NUM_ROWS_PER_QUERY];
+ int xuidCount = 1;
+
+ if ( !bFriends )
+ {
+ if ( rank == -1 )
+ {
+ // Center on the player's xuid
+ XUserGetXUID( XBX_GetPrimaryUserId(), &xuid );
+
+ ret = XUserCreateStatsEnumeratorByXuid(
+ 0,
+ xuid,
+ NUM_ROWS_PER_QUERY,
+ 1,
+ &spec,
+ &cbResults,
+ &handle );
+ }
+ else
+ {
+ // Start at the requested rank
+ ret = XUserCreateStatsEnumeratorByRank(
+ 0,
+ rank,
+ NUM_ROWS_PER_QUERY,
+ 1,
+ &spec,
+ &cbResults,
+ &handle );
+ }
+
+ if( ret != ERROR_SUCCESS )
+ {
+ Warning( "Error getting stats\n" );
+ return false;
+ }
+
+ // Allocate the buffer
+ CleanupStats();
+ m_pStats = ( XUSER_STATS_READ_RESULTS* ) new char[cbResults];
+
+ DWORD cpReturned;
+ ret = XEnumerate( handle, m_pStats, cbResults, &cpReturned, NULL );
+ }
+ else
+ {
+ // Get Friends leaderboard
+ int id = XBX_GetPrimaryUserId();
+ ret = XFriendsCreateEnumerator( id, 0, 5, &cbResults, &handle );
+
+ if ( ret != ERROR_SUCCESS )
+ {
+ Warning( "Error getting friends list\n" );
+ return false;
+ }
+
+ // Allocate the buffer
+ XONLINE_FRIEND *pFriends = ( XONLINE_FRIEND* ) new char[cbResults];
+
+ DWORD cpReturned;
+ ret = XEnumerate( handle, pFriends, cbResults, &cpReturned, NULL );
+ if( ret != ERROR_SUCCESS )
+ {
+ delete pFriends;
+ return false;
+ }
+
+ for ( uint i = 0; i < cpReturned; ++i )
+ {
+ xuidFriends[i] = pFriends[i].xuid;
+ }
+
+ // Allocate the buffer
+ CleanupStats();
+ m_pStats = ( XUSER_STATS_READ_RESULTS* ) new char[cbResults];
+
+ ret = XUserReadStats( 0, xuidCount, xuidFriends, 1, &spec, &cbResults, m_pStats, NULL );
+ }
+
+ if( ret == ERROR_SUCCESS )
+ {
+ const char *pEntries[32];
+ char pRowBuffer[MAX_PATH];
+ char pBuffers[32][MAX_PATH];
+
+ m_Menu.ClearItems();
+ m_iMaxRank = m_pStats->pViews[0].dwTotalViewRows;
+
+ // Did this search return any rows?
+ if ( m_pStats->pViews[0].dwNumRows == 0 )
+ return false;
+
+ for ( uint i = 0; i < m_pStats->pViews[0].dwNumRows; ++i )
+ {
+ XUSER_STATS_ROW &row = m_pStats->pViews[0].pRows[i];
+
+ // Save off the first rank in this set of entries
+ if ( i == 0 && m_iBaseRank == 0 )
+ {
+ m_iBaseRank = row.dwRank;
+ }
+
+ pEntries[0] = itoa( row.dwRank, pRowBuffer, 10 );
+ pEntries[1] = row.szGamertag;
+ for ( uint j = 0; j < row.dwNumColumns; ++j )
+ {
+ XUSER_STATS_COLUMN &col = m_pStats->pViews[0].pRows[i].pColumns[j];
+ pEntries[j+2] = itoa( col.Value.nData, pBuffers[j], 10 );
+ }
+
+ AddLeaderboardEntry( pEntries, row.dwNumColumns + 2 );
+
+ if ( rank == -1 && row.xuid == xuid )
+ {
+ m_Menu.SetFocus( i );
+ m_iActiveRank = row.dwRank;
+ }
+ }
+ }
+ else
+ {
+ Warning( "Error getting leaderboard stats\n" );
+ return false;
+ }
+
+ CloseHandle( handle );
+
+ return true;
+#endif
+
+ return false;
+}
+
+//----------------------------------------------------------
+// Determine if a new set of stats needs to be downloaded
+// Return true if the update has been handled, false otherwise
+//----------------------------------------------------------
+void CLeaderboardDialog::UpdateLeaderboard( int iNewRank )
+{
+ // Clamp the input
+ if ( iNewRank < 1 )
+ {
+ iNewRank = 1;
+ }
+ else if ( iNewRank > m_iMaxRank )
+ {
+ iNewRank = m_iMaxRank;
+ }
+
+ // No action necessary?
+ if ( iNewRank == m_iActiveRank )
+ return;
+
+ int nInterval = iNewRank - m_iActiveRank;
+ int iNewActiveItemIndex = m_Menu.GetActiveItemIndex() + nInterval;
+
+ // Set these "new" values to the current values - they will be conditionally updated.
+ int iNewBaseRank = m_iBaseRank;
+ int iNewBaseItemIndex = m_Menu.GetBaseRowIndex();
+ int nVisibleItems = m_Menu.GetVisibleItemCount();
+ int nHiddenItems = NUM_ROWS_PER_QUERY - nVisibleItems;
+
+ // Are we outside the visible range of the menu?
+ if ( iNewActiveItemIndex < iNewBaseItemIndex )
+ {
+ // Do we need to grab another set of columns?
+ if ( iNewRank < m_iBaseRank )
+ {
+ iNewBaseRank = iNewRank - nHiddenItems;
+ if ( iNewBaseRank < 1 )
+ {
+ iNewBaseRank = 1;
+ }
+
+ if ( !GetPlayerStats( iNewBaseRank ) )
+ {
+ // Failed to load player stats, don't change the current index
+ return;
+ }
+
+ m_iBaseRank = iNewBaseRank;
+ }
+
+ int nBaseToActiveInterval = iNewRank - m_iBaseRank;
+
+ // Since we shifted the menu down, both base and active item are at the first visible menu item
+ iNewActiveItemIndex = nBaseToActiveInterval;
+ iNewBaseItemIndex = nBaseToActiveInterval;
+ }
+ else if ( iNewActiveItemIndex >= m_Menu.GetBaseRowIndex() + nVisibleItems )
+ {
+ int nHiddenItems = NUM_ROWS_PER_QUERY - nVisibleItems;
+ int iTopRank = iNewRank + nHiddenItems;
+ if ( iTopRank > m_iMaxRank )
+ {
+ iTopRank = m_iMaxRank;
+ }
+
+
+ // Do we need to grab another set of columns?
+ if ( iNewRank >= m_iBaseRank + NUM_ROWS_PER_QUERY )
+ {
+ iNewBaseRank = iTopRank - NUM_ROWS_PER_QUERY + 1;
+ if ( !GetPlayerStats( iNewBaseRank ) )
+ {
+ // Failed to load player stats, don't change the current index
+ return;
+ }
+ m_iBaseRank = iNewBaseRank;
+ }
+
+ int nBaseToActiveInterval = iNewRank - m_iBaseRank;
+
+ iNewActiveItemIndex = nBaseToActiveInterval;
+ iNewBaseItemIndex = iNewActiveItemIndex - nVisibleItems + 1;
+ }
+
+ // Set all the new variables - must set base index before active index.
+ m_iActiveRank = iNewRank;
+ m_Menu.SetBaseRowIndex( iNewBaseItemIndex );
+ m_Menu.SetFocus( iNewActiveItemIndex );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLeaderboardDialog::HandleKeyRepeated( vgui::KeyCode code )
+{
+ OnKeyCodePressed( code );
+}
+
+//-----------------------------------------------------------------
+// Purpose: Send key presses to the dialog's menu
+//-----------------------------------------------------------------
+void CLeaderboardDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+
+ switch( code )
+ {
+ case KEY_XBUTTON_A:
+ case STEAMCONTROLLER_A:
+#ifdef _X360
+ {
+ int idx = m_Menu.GetActiveItemIndex();
+ if ( m_pStats && idx < (int)m_pStats->pViews[0].dwNumRows )
+ {
+ XUSER_STATS_ROW &row = m_pStats->pViews[0].pRows[idx];
+ XShowGamerCardUI( XBX_GetPrimaryUserId(), row.xuid );
+ }
+ }
+#endif
+ break;
+
+ case KEY_XBUTTON_Y:
+ case STEAMCONTROLLER_Y:
+ break;
+
+ case KEY_XSTICK1_DOWN:
+ case KEY_XBUTTON_DOWN:
+ case STEAMCONTROLLER_DPAD_DOWN:
+ m_KeyRepeat.KeyDown( code );
+ UpdateLeaderboard( m_iActiveRank + 1 );
+ break;
+
+ case KEY_XSTICK1_UP:
+ case KEY_XBUTTON_UP:
+ case STEAMCONTROLLER_DPAD_UP:
+ m_KeyRepeat.KeyDown( code );
+ UpdateLeaderboard( m_iActiveRank - 1 );
+ break;
+
+ case KEY_XBUTTON_LEFT_SHOULDER:
+ UpdateLeaderboard( 1 );
+ break;
+
+ case KEY_XBUTTON_RIGHT_SHOULDER:
+ OnCommand( "CenterOnPlayer" );
+ break;
+
+ // Disabled until friends enumeration works
+// case KEY_XBUTTON_RIGHT_SHOULDER:
+// OnCommand( "Friends" );
+// break;
+
+ default:
+ m_KeyRepeat.KeyDown( code );
+ BaseClass::OnKeyCodePressed( code );
+ break;
+ }
+
+ // Invalidate layout when scrolling through columns
+ switch( code )
+ {
+ case KEY_XSTICK1_LEFT:
+ case KEY_XBUTTON_LEFT:
+ case STEAMCONTROLLER_DPAD_LEFT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XBUTTON_RIGHT:
+ case STEAMCONTROLLER_DPAD_RIGHT:
+ InvalidateLayout();
+ break;
+ }
+}
+
+
+CON_COMMAND( mm_add_item, "Add a stats item" )
+{
+ if ( args.ArgC() > 1 )
+ {
+ int ct = atoi( args[1] );
+ const char *pEntries[32];
+ for ( int i = 0; i < ct; ++i )
+ {
+ pEntries[i] = args[i+2];
+ }
+ g_pLeaderboardDialog->AddLeaderboardEntry( pEntries, ct );
+ }
+}
diff --git a/gameui/matchmaking/leaderboarddialog.h b/gameui/matchmaking/leaderboarddialog.h
new file mode 100644
index 0000000..6fd646f
--- /dev/null
+++ b/gameui/matchmaking/leaderboarddialog.h
@@ -0,0 +1,59 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Displays a leaderboard
+//
+//=============================================================================//
+
+#ifndef LEADERBOARDDIALOG_H
+#define LEADERBOARDDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Display player leaderboards
+//-----------------------------------------------------------------------------
+class CLeaderboardDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CLeaderboardDialog, CBaseDialog );
+
+public:
+ CLeaderboardDialog(vgui::Panel *parent);
+ ~CLeaderboardDialog();
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void PerformLayout( void );
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void HandleKeyRepeated( vgui::KeyCode code );
+
+ bool GetPlayerStats( int rank, bool bFriends = false );
+ void UpdateLeaderboard( int iNewRank );
+ void AddLeaderboardEntry( const char **pEntries, int ct );
+ void CleanupStats();
+
+private:
+ vgui::Panel *m_pProgressBg;
+ vgui::Panel *m_pProgressBar;
+ vgui::Label *m_pProgressPercent;
+ vgui::Label *m_pNumbering;
+ vgui::Label *m_pUpArrow;
+ vgui::Label *m_pDownArrow;
+ vgui::Label *m_pBestMoments;
+
+ int m_iBaseRank;
+ int m_iActiveRank;
+ int m_iMaxRank;
+ int m_cColumns;
+ int m_iRangeBase;
+
+#if defined( _X360 )
+ XUSER_STATS_READ_RESULTS *m_pStats;
+#endif
+};
+
+
+#endif // LEADERBOARDDIALOG_H
diff --git a/gameui/matchmaking/matchmakingbasepanel.cpp b/gameui/matchmaking/matchmakingbasepanel.cpp
new file mode 100644
index 0000000..403e476
--- /dev/null
+++ b/gameui/matchmaking/matchmakingbasepanel.cpp
@@ -0,0 +1,999 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Serves as the base panel for the entire matchmaking UI
+//
+//=============================================================================//
+
+#include "matchmakingbasepanel.h"
+#include "welcomedialog.h"
+#include "pausedialog.h"
+#include "leaderboarddialog.h"
+#include "achievementsdialog.h"
+#include "sessionoptionsdialog.h"
+#include "sessionlobbydialog.h"
+#include "sessionbrowserdialog.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/MessageDialog.h"
+#include "vgui/ISurface.h"
+#include "EngineInterface.h"
+#include "game/client/IGameClientExports.h"
+#include "GameUI_Interface.h"
+#include "engine/imatchmaking.h"
+#include "KeyValues.h"
+#include "vstdlib/jobthread.h"
+#include "BasePanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------
+// CMatchmakingBasePanel
+//--------------------------------
+CMatchmakingBasePanel::CMatchmakingBasePanel( vgui::Panel *pParent ) : BaseClass( pParent, "MatchmakingBasePanel" )
+{
+ SetDeleteSelfOnClose( true );
+ SetPaintBackgroundEnabled( false );
+
+ vgui::scheme()->LoadSchemeFromFile( "Resource/ClientScheme.res", "ClientScheme" );
+ SetScheme( "ClientScheme" );
+
+ m_pFooter = new CFooterPanel( this, "MatchmakingFooterPanel" );
+
+ m_nGameType = GAMETYPE_STANDARD_MATCH;
+}
+
+CMatchmakingBasePanel::~CMatchmakingBasePanel()
+{
+ if ( m_pFooter )
+ {
+ delete m_pFooter;
+ m_pFooter = NULL;
+ }
+}
+
+void CMatchmakingBasePanel::SetFooterButtons( CBaseDialog *pOwner, KeyValues *pKeyValues, int nButtonGap /* = -1 */ )
+{
+ // Don't lay out the buttons if the dialog is not at the top of the stack
+ if ( m_DialogStack.Count() )
+ {
+ CBaseDialog *pDlg = m_DialogStack.Top();
+ if ( pDlg != pOwner )
+ return;
+ }
+
+ if ( m_pFooter )
+ {
+ m_pFooter->ClearButtons();
+
+ if ( pKeyValues )
+ {
+ for ( KeyValues *pButton = pKeyValues->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() )
+ {
+ if ( !Q_stricmp( pButton->GetName(), "button" ) )
+ {
+ // Add a button to the footer
+ const char *pText = pButton->GetString( "text", NULL );
+ const char *pIcon = pButton->GetString( "icon", NULL );
+
+ if ( pText && pIcon )
+ {
+ m_pFooter->AddNewButtonLabel( pText, pIcon );
+ }
+ }
+ }
+ }
+ else
+ {
+ // no data was passed so just setup the standard footer buttons
+ m_pFooter->SetStandardDialogButtons();
+ }
+
+ if ( nButtonGap > 0 )
+ {
+ m_pFooter->SetButtonGap( nButtonGap );
+ }
+ else
+ {
+ m_pFooter->UseDefaultButtonGap();
+ }
+ }
+}
+
+void CMatchmakingBasePanel::ShowFooter( bool bShown )
+{
+ m_pFooter->SetVisible( bShown );
+}
+
+void CMatchmakingBasePanel::SetFooterButtonVisible( const char *pszText, bool bVisible )
+{
+ if ( m_pFooter )
+ {
+ m_pFooter->ShowButtonLabel( pszText, bVisible );
+ }
+}
+
+void CMatchmakingBasePanel::Activate( void )
+{
+ BaseClass::Activate();
+
+ // Close animation may have set this to zero
+ SetAlpha( 255 );
+
+ if ( !GameUI().IsInLevel() )
+ {
+ OnOpenWelcomeDialog();
+ }
+ else
+ {
+ OnOpenPauseDialog();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle commands from all matchmaking dialogs
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( "OpenWelcomeDialog", pCommand ) )
+ {
+ OnOpenWelcomeDialog();
+ }
+ if ( !Q_stricmp( "OpenPauseDialog", pCommand ) )
+ {
+ OnOpenPauseDialog();
+ }
+ if ( !Q_stricmp( "OpenRankingsDialog", pCommand ) )
+ {
+ OnOpenRankingsDialog();
+ }
+ else if ( !Q_stricmp( "OpenSystemLinkDialog", pCommand ) )
+ {
+ OnOpenSystemLinkDialog();
+ }
+ else if ( !Q_stricmp( "OpenPlayerMatchDialog", pCommand ) )
+ {
+ OnOpenPlayerMatchDialog();
+ }
+ else if ( !Q_stricmp( "OpenRankedMatchDialog", pCommand ) )
+ {
+ OnOpenRankedMatchDialog();
+ }
+ else if ( !Q_stricmp( "OpenAchievementsDialog", pCommand ) )
+ {
+ OnOpenAchievementsDialog();
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Specific code for CS Achievements Display
+ //=============================================================================
+
+ else if ( !Q_stricmp( "OpenCSAchievementsDialog", pCommand ) )
+ {
+ OnOpenCSAchievementsDialog();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ else if ( !Q_stricmp( "LevelLoadingStarted", pCommand ) )
+ {
+ OnLevelLoadingStarted();
+ }
+ else if ( !Q_stricmp( "LevelLoadingFinished", pCommand ) )
+ {
+ OnLevelLoadingFinished();
+ }
+ else if ( !Q_stricmp( "SessionOptions_Modify", pCommand ) )
+ {
+ OnOpenSessionOptionsDialog( pCommand );
+ }
+ else if ( !Q_stricmp( "ModifySession", pCommand ) )
+ {
+ matchmaking->ModifySession();
+ }
+ else if ( !Q_stricmp( "ChangeClass", pCommand ) )
+ {
+ engine->ClientCmd_Unrestricted( "changeclass" );
+ OnCommand( "ResumeGame" );
+ }
+ else if ( !Q_stricmp( "ChangeTeam", pCommand ) )
+ {
+ engine->ClientCmd_Unrestricted( "changeteam" );
+ OnCommand( "ResumeGame" );
+ }
+ else if ( !Q_stricmp( "ShowMapInfo", pCommand ) )
+ {
+ engine->ClientCmd_Unrestricted( "showmapinfo" );
+ OnCommand( "ResumeGame" );
+ }
+ else if ( !Q_stricmp( "StartHost", pCommand ) )
+ {
+ // Show progress dialog
+ GameUI().ShowMessageDialog( MD_CREATING_GAME, this );
+
+ // Send the host start command
+ matchmaking->StartHost();
+ }
+ else if ( !Q_stricmp( "StartSystemLinkHost", pCommand ) )
+ {
+ // Show progress dialog
+ GameUI().ShowMessageDialog( MD_CREATING_GAME, this );
+
+ m_nGameType = GAMETYPE_SYSTEMLINK_MATCH;
+ matchmaking->StartHost( true );
+ }
+ else if ( !Q_stricmp( "StartClient", pCommand ) )
+ {
+ // Show progress dialog
+ GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this );
+
+ // Tell matchmaking to start a client and search for games
+ matchmaking->StartClient( false );
+ }
+ else if ( !Q_stricmp( "StartSystemLinkClient", pCommand ) )
+ {
+ // Show progress dialog
+ GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this );
+
+ // Set the system link flag
+ matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_SYSTEMLINK", NULL, NULL );
+
+ // Tell matchmaking to start a client and search for games
+ m_nGameType = GAMETYPE_SYSTEMLINK_MATCH;
+ matchmaking->StartClient( true );
+ }
+ else if ( Q_stristr( pCommand, "StartQuickMatchClient_" ) )
+ {
+ // Show progress dialog
+ GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this );
+
+ if ( Q_stristr( pCommand, "_Ranked" ) )
+ {
+ // Set the basic flags
+ matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_MODE", "CONTEXT_GAME_MODE_MULTIPLAYER", NULL );
+ matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_TYPE", "CONTEXT_GAME_TYPE_RANKED", NULL );
+ matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_LIVE_MULTIPLAYER_RANKED", NULL, NULL );
+ m_nGameType = GAMETYPE_RANKED_MATCH;
+ }
+ else
+ {
+ // Set the standard match flag
+ matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_MODE", "CONTEXT_GAME_MODE_MULTIPLAYER", NULL );
+ matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_TYPE", "CONTEXT_GAME_TYPE_STANDARD", NULL );
+ matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_LIVE_MULTIPLAYER_STANDARD", NULL, NULL );
+ m_nGameType = GAMETYPE_STANDARD_MATCH;
+ }
+
+ // Tell matchmaking to start a client and search for games
+ matchmaking->StartClient( false );
+ }
+ else if ( !Q_stricmp( "StartGame", pCommand ) )
+ {
+ // Tell matchmaking the host wants to start the game
+ matchmaking->StartGame();
+ }
+ else if ( Q_stristr( pCommand, "LeaderboardDialog_" ) )
+ {
+ // This covers LeaderboardDialog_[Ranked|Stats]
+ OnOpenLeaderboardDialog( pCommand );
+ }
+ else if ( Q_stristr( pCommand, "SessionOptions_" ) )
+ {
+ // This covers six command strings: *_Host[Standard|Ranked|Systemlink], *_Client[Standard|Ranked|Systemlink]
+ // Each command has a unique options menu - the command string is used as the name of the .res file.
+ OnOpenSessionOptionsDialog( pCommand );
+ }
+ else if ( !Q_stricmp( pCommand, "DialogClosing" ) )
+ {
+ PopDialog();
+ }
+ else if ( !Q_stricmp( pCommand, "AchievementsDialogClosing" ) )
+ {
+ PopDialog();
+ }
+ else if ( !Q_stricmp( pCommand, "show_achievements_dialog" ) )
+ {
+ OnOpenAchievementsDialog();
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Specific code for CS Achievements Display
+ //=============================================================================
+
+ else if ( !Q_stricmp( pCommand, "show_csachievements_dialog" ) )
+ {
+ OnOpenCSAchievementsDialog();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ else if ( !Q_stricmp( pCommand, "ShowSessionOptionsDialog" ) )
+ {
+ // Need to close the client options dialog and open the host options equivalent
+ PopDialog();
+
+ switch( m_nGameType )
+ {
+ case GAMETYPE_STANDARD_MATCH:
+ OnOpenSessionOptionsDialog( "SessionOptions_HostStandard" );
+ break;
+
+ case GAMETYPE_RANKED_MATCH:
+ OnOpenSessionOptionsDialog( "SessionOptions_HostRanked" );
+ break;
+
+ case GAMETYPE_SYSTEMLINK_MATCH:
+ OnOpenSessionOptionsDialog( "SessionOptions_SystemLink" );
+ break;
+ }
+ }
+ else if ( !Q_stricmp( pCommand, "ReturnToMainMenu" ) )
+ {
+ CloseAllDialogs();
+ Activate();
+ }
+ else if ( !Q_stricmp( pCommand, "CancelOperation" ) )
+ {
+ GameUI().CloseMessageDialog();
+ PopDialog();
+ matchmaking->CancelCurrentOperation();
+ }
+ else if ( !Q_stricmp( pCommand, "StorageDeviceDenied" ) )
+ {
+ // Set us as declined
+ XBX_SetStorageDeviceId( XBX_STORAGE_DECLINED );
+ }
+ else
+ {
+ if ( !Q_stricmp( "ResumeGame", pCommand ) )
+ {
+ CloseAllDialogs();
+ }
+
+ CallParentFunction( new KeyValues( "Command", "command", pCommand ) );
+ }
+
+ // We should handle the case when user launched the game via invite,
+ // was prompted for a storage device and cancelled the picker.
+ // In this case whenever any command gets selected from the main menu
+ // we should cancel the wait for storage device selection.
+ BasePanel()->ValidateStorageDevice( NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle notifications from matchmaking in the engine.
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::SessionNotification( const int notification, const int param )
+{
+ switch( notification )
+ {
+ case SESSION_NOTIFY_FAIL_SEARCH:
+ GameUI().CloseMessageDialog();
+ GameUI().ShowMessageDialog( MD_SESSION_SEARCH_FAILED, this );
+ break;
+
+ case SESSION_NOTIFY_CONNECT_NOTAVAILABLE:
+ CloseAllDialogs();
+ GameUI().ShowMessageDialog( MD_SESSION_CONNECT_NOTAVAILABLE, this );
+ break;
+
+ case SESSION_NOTIFY_CONNECT_SESSIONFULL:
+ CloseAllDialogs();
+ GameUI().ShowMessageDialog( MD_SESSION_CONNECT_SESSIONFULL, this );
+ break;
+
+ case SESSION_NOTIFY_CONNECT_FAILED:
+ CloseAllDialogs();
+ GameUI().ShowMessageDialog( MD_SESSION_CONNECT_FAILED, this );
+ break;
+
+ case SESSION_NOTIFY_FAIL_CREATE:
+ CloseAllDialogs();
+ GameUI().ShowMessageDialog( MD_SESSION_CREATE_FAILED, this );
+ break;
+
+ case SESSION_NOTIFY_CLIENT_KICKED:
+ CloseAllDialogs();
+ GameUI().ShowMessageDialog( MD_CLIENT_KICKED, this );
+ break;
+
+ case SESSION_NOTIFY_LOST_HOST:
+ CloseBaseDialogs();
+ GameUI().ShowMessageDialog( MD_LOST_HOST, this );
+ break;
+
+ case SESSION_NOTIFY_LOST_SERVER:
+ CloseBaseDialogs();
+ GameUI().ShowMessageDialog( MD_LOST_SERVER, this );
+ break;
+
+ case SESSION_NOFIFY_MODIFYING_SESSION:
+ GameUI().ShowMessageDialog( MD_MODIFYING_SESSION, this );
+ break;
+
+ case SESSION_NOTIFY_SEARCH_COMPLETED:
+ GameUI().CloseMessageDialog();
+
+ LoadSessionProperties();
+
+ // Switch to the session browser
+ switch( m_nGameType )
+ {
+ case GAMETYPE_STANDARD_MATCH:
+ case GAMETYPE_RANKED_MATCH:
+ OnOpenSessionBrowserDialog( "SessionBrowser_Live" );
+ break;
+
+ case GAMETYPE_SYSTEMLINK_MATCH:
+ OnOpenSessionBrowserDialog( "SessionBrowser_SystemLink" );
+ break;
+ }
+ break;
+
+ case SESSION_NOTIFY_CREATED_HOST:
+ case SESSION_NOTIFY_MODIFYING_COMPLETED_HOST:
+ GameUI().CloseMessageDialog();
+
+ LoadSessionProperties();
+
+ // Switch to the Lobby
+ switch( m_nGameType )
+ {
+ case GAMETYPE_STANDARD_MATCH:
+ case GAMETYPE_RANKED_MATCH:
+ case GAMETYPE_SYSTEMLINK_MATCH:
+ OnOpenSessionLobbyDialog( "SessionLobby_Host" );
+ break;
+ }
+ break;
+
+ case SESSION_NOTIFY_CREATED_CLIENT:
+ GameUI().ShowMessageDialog( MD_SESSION_CONNECTING, this );
+ break;
+
+ case SESSION_NOTIFY_CONNECTED_TOSESSION:
+ case SESSION_NOTIFY_MODIFYING_COMPLETED_CLIENT:
+ GameUI().CloseMessageDialog();
+
+ LoadSessionProperties();
+
+ // Switch to the Lobby
+ switch( m_nGameType )
+ {
+ case GAMETYPE_STANDARD_MATCH:
+ case GAMETYPE_RANKED_MATCH:
+ case GAMETYPE_SYSTEMLINK_MATCH:
+ OnOpenSessionLobbyDialog( "SessionLobby_Client" );
+ break;
+ }
+ break;
+
+ case SESSION_NOTIFY_CONNECTED_TOSERVER:
+ CloseAllDialogs( false );
+ break;
+
+ case SESSION_NOTIFY_ENDGAME_RANKED:
+ // Return to the main menu
+ CloseAllDialogs();
+ break;
+
+ case SESSION_NOTIFY_ENDGAME_HOST:
+ CloseBaseDialogs();
+ OnOpenSessionLobbyDialog( "SessionLobby_Host" );
+ break;
+
+ case SESSION_NOTIFY_ENDGAME_CLIENT:
+ CloseBaseDialogs();
+ OnOpenSessionLobbyDialog( "SessionLobby_Client" );
+ break;
+
+ case SESSION_NOTIFY_COUNTDOWN:
+ {
+ CSessionLobbyDialog *pDlg = (CSessionLobbyDialog*)m_hSessionLobbyDialog.Get();
+ if ( pDlg )
+ {
+ pDlg->UpdateCountdown( param );
+ }
+
+ if ( param == 0 )
+ {
+ BasePanel()->RunAnimationWithCallback( this, "CloseMatchmakingUI", new KeyValues( "LoadMap" ) );
+ }
+ }
+ break;
+
+ case SESSION_NOTIFY_DUMPSTATS:
+ Msg( "[MM] %d open dialogs\n", m_DialogStack.Count() );
+ for ( int i = 0; i < m_DialogStack.Count(); ++i )
+ {
+ const char *pString = "NULL";
+ bool bVisible = false;
+ float fAlpha = 0.f;
+ CBaseDialog *pDlg = m_DialogStack[i];
+ if ( pDlg )
+ {
+ pString = pDlg->GetName();
+ bVisible = pDlg->IsVisible();
+ fAlpha = pDlg->GetAlpha();
+ }
+ const char *pVisible = bVisible ? "YES" : "NO";
+ Msg( "[MM] Dialog %d: %s, Visible %s, Alpha %f\n", i, pString, pVisible, fAlpha );
+ }
+ break;
+
+ case SESSION_NOTIFY_WELCOME:
+ CloseGameDialogs( false );
+ Activate();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: System Notification
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::SystemNotification( const int notification )
+{
+ switch( notification )
+ {
+ case SYSTEMNOTIFY_USER_SIGNEDOUT:
+ // See if this was us
+#if defined( _X360 )
+ uint state = XUserGetSigninState( XBX_GetPrimaryUserId() );
+ if ( state == eXUserSigninState_NotSignedIn )
+ {
+ matchmaking->KickPlayerFromSession( 0 );
+ CloseAllDialogs();
+ }
+ else if ( state != eXUserSigninState_SignedInToLive )
+ {
+ // User was signed out of live
+ if ( m_bPlayingOnline )
+ {
+ matchmaking->KickPlayerFromSession( 0 );
+ CloseAllDialogs();
+ }
+ }
+#endif
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check whether a player meets the signin requirements for a multiplayer game
+//-----------------------------------------------------------------------------
+bool CMatchmakingBasePanel::ValidateSigninAndStorage( bool bOnlineRequired, const char *pIssuingCommand )
+{
+ // Check the signin state of the primary user
+ bool bSignedIn = false;
+ bool bOnlineEnabled = false;
+ bool bOnlineSignedIn = false;
+
+#if defined( _X360 )
+ int userIdx = XBX_GetPrimaryUserId();
+ if ( userIdx != INVALID_USER_ID )
+ {
+ XUSER_SIGNIN_INFO info;
+ uint ret = XUserGetSigninInfo( userIdx, 0, &info );
+ if ( ret == ERROR_SUCCESS )
+ {
+ bSignedIn = true;
+ if ( info.dwInfoFlags & XUSER_INFO_FLAG_LIVE_ENABLED )
+ {
+ bOnlineEnabled = true;
+ uint state = XUserGetSigninState( XBX_GetPrimaryUserId() );
+ if ( state == eXUserSigninState_SignedInToLive )
+ {
+ bOnlineSignedIn = true;
+
+ // Check privileges
+ BOOL bPrivCheck = false;
+ DWORD dwPrivCheck = XUserCheckPrivilege( userIdx, XPRIVILEGE_MULTIPLAYER_SESSIONS, &bPrivCheck );
+ if ( ERROR_SUCCESS != dwPrivCheck ||
+ !bPrivCheck )
+ {
+ bOnlineEnabled = false;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if ( bOnlineRequired && !bOnlineEnabled )
+ {
+ // Player must sign in an online account
+ GameUI().ShowMessageDialog( MD_NOT_ONLINE_ENABLED );
+ return false;
+ }
+ else if ( bOnlineRequired && !bOnlineSignedIn )
+ {
+ // Player's live account isn't signed in to live
+ GameUI().ShowMessageDialog( MD_NOT_ONLINE_SIGNEDIN );
+ return false;
+ }
+ else if ( !bSignedIn )
+ {
+ // Eat the input and make the user sign in
+ xboxsystem->ShowSigninUI( 1, 0 ); // One user, no special flags
+ return false;
+ }
+
+ // Handle the storage device selection
+ if ( !BasePanel()->HandleStorageDeviceRequest( pIssuingCommand ) )
+ return false;
+
+ // If we succeeded, clear the command out
+ BasePanel()->ClearPostPromptCommand( pIssuingCommand );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update player information in the lobby
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost )
+{
+ CSessionLobbyDialog *pLobby = dynamic_cast< CSessionLobbyDialog* >( m_hSessionLobbyDialog.Get() );
+ if ( pLobby )
+ {
+ pLobby->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a search result to the browser dialog
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping )
+{
+ CSessionBrowserDialog *pBrowser = dynamic_cast< CSessionBrowserDialog* >( m_hSessionBrowserDialog.Get() );
+ if ( pBrowser )
+ {
+ pBrowser->SessionSearchResult( searchIdx, pHostData, pResult, ping );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pre level load ops
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::OnLevelLoadingStarted()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Post level load ops
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::OnLevelLoadingFinished()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Hide the current dialog, add a new one to the stack and activate it.
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::PushDialog( vgui::DHANDLE< CBaseDialog > &hDialog )
+{
+ if ( m_DialogStack.Count() )
+ {
+ if ( m_DialogStack.Top() )
+ {
+ m_DialogStack.Top()->Close();
+ }
+ else
+ {
+ m_DialogStack.Pop();
+ }
+ }
+ hDialog->Activate();
+ m_DialogStack.Push( hDialog );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Close the current dialog, pop it from the top of the stack, and activate the next one.
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::PopDialog( bool bActivateNext )
+{
+ if ( m_DialogStack.Count() > 1 )
+ {
+ if ( m_DialogStack.Top() )
+ {
+ m_DialogStack.Top()->SetDeleteSelfOnClose( true );
+ m_DialogStack.Top()->Close();
+ m_DialogStack.Pop();
+ }
+
+ // Drop down to the next available dialog
+ while ( m_DialogStack.Count() && !m_DialogStack.Top() )
+ {
+ m_DialogStack.Pop();
+ }
+
+ if ( bActivateNext && m_DialogStack.Count() && m_DialogStack.Top() )
+ {
+ m_DialogStack.Top()->Activate();
+ }
+ }
+
+ if ( m_DialogStack.Count() <= 1 )
+ {
+ // Back at the welcome menu
+ m_bPlayingOnline = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Close all open dialogs down to the main menu
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::CloseGameDialogs( bool bActivateNext )
+{
+ CloseBaseDialogs();
+ while ( m_DialogStack.Count() > 1 )
+ {
+ PopDialog( bActivateNext );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Close all open dialogs down to the main menu
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::CloseAllDialogs( bool bActivateNext )
+{
+ GameUI().CloseMessageDialog();
+ CloseGameDialogs( bActivateNext );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::CloseBaseDialogs( void )
+{
+ if ( BasePanel() )
+ {
+ BasePanel()->CloseBaseDialogs();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get session property keyvalues from base panel and matchmaking
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::LoadSessionProperties()
+{
+ // Grab the session property keys from XboxDialogs.res and from matchmaking
+ m_pSessionKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "PropertyDisplayKeys" );
+ if ( m_pSessionKeys )
+ {
+ m_pSessionKeys->ChainKeyValue( matchmaking->GetSessionProperties() );
+ }
+
+ // Cache off the map name
+ const char *pDiskName = NULL;
+ KeyValues *pName = m_pSessionKeys->FindKey( "MapDiskNames" );
+ if ( pName )
+ {
+ KeyValues *pScenario = m_pSessionKeys->FindKey( "CONTEXT_SCENARIO" );
+ if ( pScenario )
+ {
+ pDiskName = pName->GetString( pScenario->GetString( "displaystring" ), NULL );
+ }
+ }
+
+ if ( pDiskName )
+ {
+ Q_strncpy( m_szMapLoadName, pDiskName, sizeof( m_szMapLoadName ) );
+ Msg( "Storing mapname %s\n", m_szMapLoadName );
+ if ( Q_strlen( m_szMapLoadName ) < 5 )
+ {
+ Warning( "Bad map name!\n" );
+ }
+ }
+ else
+ {
+ // X360TBD: Generate a create error
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Open dialog functions.
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::OnOpenWelcomeDialog()
+{
+ if ( !m_hWelcomeDialog.Get() )
+ {
+ m_hWelcomeDialog = new CWelcomeDialog( this );
+ m_DialogStack.Push( m_hWelcomeDialog );
+ }
+
+ m_hWelcomeDialog->Activate();
+ m_bPlayingOnline = false;
+}
+
+void CMatchmakingBasePanel::OnOpenPauseDialog()
+{
+ if ( !m_hPauseDialog.Get() )
+ {
+ m_hPauseDialog = new CPauseDialog( this );
+ }
+ PushDialog( m_hPauseDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenRankingsDialog()
+{
+ if ( !ValidateSigninAndStorage( true, "OpenRankingDialog" ) )
+ return;
+
+ if ( !m_hRankingsDialog.Get() )
+ {
+ m_hRankingsDialog = new CBaseDialog( this, "RankingsDialog" );
+ }
+ PushDialog( m_hRankingsDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenSystemLinkDialog()
+{
+ if ( !ValidateSigninAndStorage( false, "OpenSystemLinkDialog" ) )
+ return;
+
+ if ( !m_hSystemLinkDialog.Get() )
+ {
+ m_hSystemLinkDialog = new CBaseDialog( this, "SystemLinkDialog" );
+ }
+ PushDialog( m_hSystemLinkDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenPlayerMatchDialog()
+{
+ if ( !ValidateSigninAndStorage( true, "OpenPlayerMatchDialog" ) )
+ return;
+
+ if ( !m_hPlayerMatchDialog.Get() )
+ {
+ m_hPlayerMatchDialog = new CBaseDialog( this, "PlayerMatchDialog" );
+ }
+ PushDialog( m_hPlayerMatchDialog );
+ m_bPlayingOnline = true;
+}
+
+void CMatchmakingBasePanel::OnOpenRankedMatchDialog()
+{
+ if ( !ValidateSigninAndStorage( true, "OpenRankedMatchDialog" ) )
+ return;
+
+ if ( !m_hRankedMatchDialog.Get() )
+ {
+ m_hRankedMatchDialog = new CBaseDialog( this, "RankedMatchDialog" );
+ }
+ PushDialog( m_hRankedMatchDialog );
+ m_bPlayingOnline = true;
+}
+
+void CMatchmakingBasePanel::OnOpenAchievementsDialog()
+{
+ if ( !ValidateSigninAndStorage( false, "OpenAchievementsDialog" ) )
+ return;
+
+ if ( !m_hAchievementsDialog.Get() )
+ {
+ m_hAchievementsDialog = new CAchievementsDialog_XBox( this );
+ }
+ PushDialog( m_hAchievementsDialog );
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [dwenger] Specific code for CS Achievements Display
+//=============================================================================
+
+void CMatchmakingBasePanel::OnOpenCSAchievementsDialog()
+{
+ if ( !ValidateSigninAndStorage( false, "OpenCSAchievementsDialog" ) )
+ return;
+
+ if ( !m_hAchievementsDialog.Get() )
+ {
+ // $TODO(HPE): m_hAchievementsDialog = new CAchievementsDialog_XBox( this );
+ }
+ PushDialog( m_hAchievementsDialog );
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+void CMatchmakingBasePanel::OnOpenSessionOptionsDialog( const char *pResourceName )
+{
+ if ( !m_hSessionOptionsDialog.Get() )
+ {
+ m_hSessionOptionsDialog = new CSessionOptionsDialog( this );
+ }
+
+ if ( Q_stristr( pResourceName, "Ranked" ) )
+ {
+ m_nGameType = GAMETYPE_RANKED_MATCH;
+ }
+ else if ( Q_stristr( pResourceName, "Standard" ) )
+ {
+ m_nGameType = GAMETYPE_STANDARD_MATCH;
+ }
+ else if ( Q_stristr( pResourceName, "SystemLink" ) )
+ {
+ m_nGameType = GAMETYPE_SYSTEMLINK_MATCH;
+ }
+
+ LoadSessionProperties();
+
+ CSessionOptionsDialog* pDlg = ((CSessionOptionsDialog*)m_hSessionOptionsDialog.Get());
+ pDlg->SetGameType( pResourceName );
+ pDlg->SetDialogKeys( m_pSessionKeys );
+
+ PushDialog( m_hSessionOptionsDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenSessionLobbyDialog( const char *pResourceName )
+{
+ if ( !m_hSessionLobbyDialog.Get() )
+ {
+ m_hSessionLobbyDialog = new CSessionLobbyDialog( this );
+ }
+ CSessionLobbyDialog *pDlg = (CSessionLobbyDialog*)m_hSessionLobbyDialog.Get();
+ pDlg->SetDialogKeys( m_pSessionKeys );
+
+ m_hSessionLobbyDialog->SetName( pResourceName );
+ PushDialog( m_hSessionLobbyDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenSessionBrowserDialog( const char *pResourceName )
+{
+ if ( !m_hSessionBrowserDialog.Get() )
+ {
+ m_hSessionBrowserDialog = new CSessionBrowserDialog( this, m_pSessionKeys );
+ m_hSessionBrowserDialog->SetName( pResourceName );
+
+ // Matchmaking will start adding results immediately, so prepare the dialog
+ SETUP_PANEL( m_hSessionBrowserDialog.Get() );
+ }
+ PushDialog( m_hSessionBrowserDialog );
+}
+
+void CMatchmakingBasePanel::OnOpenLeaderboardDialog( const char *pResourceName )
+{
+ if ( !m_hLeaderboardDialog.Get() )
+ {
+ m_hLeaderboardDialog = new CLeaderboardDialog( this );
+ m_hLeaderboardDialog->SetName( pResourceName );
+ SETUP_PANEL( m_hLeaderboardDialog.Get() );
+ }
+ PushDialog( m_hLeaderboardDialog );
+ m_hLeaderboardDialog->OnCommand( "CenterOnPlayer" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Callback function to start map load after ui fades out.
+//-----------------------------------------------------------------------------
+void CMatchmakingBasePanel::LoadMap( const char *mapname )
+{
+ CloseAllDialogs( false );
+
+ char cmd[MAX_PATH];
+ Q_snprintf( cmd, sizeof( cmd ), "map %s", m_szMapLoadName );
+ BasePanel()->FadeToBlackAndRunEngineCommand( cmd );
+}
+
+//-------------------------------------------------------
+// Keyboard input
+//-------------------------------------------------------
+void CMatchmakingBasePanel::OnKeyCodePressed( vgui::KeyCode code )
+{
+ switch( code )
+ {
+ case KEY_XBUTTON_B:
+ // Can't close the matchmaking base panel
+ break;
+
+ default:
+ BaseClass::OnKeyCodePressed( code );
+ break;
+ }
+}
diff --git a/gameui/matchmaking/matchmakingbasepanel.h b/gameui/matchmaking/matchmakingbasepanel.h
new file mode 100644
index 0000000..0be40ed
--- /dev/null
+++ b/gameui/matchmaking/matchmakingbasepanel.h
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Serves as the base panel for the entire matchmaking UI
+//
+//=============================================================================//
+
+#ifndef MATCHMAKINGBASEPANEL_H
+#define MATCHMAKINGBASEPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+#include "utlstack.h"
+#include "const.h"
+
+enum EGameType
+{
+ GAMETYPE_RANKED_MATCH,
+ GAMETYPE_STANDARD_MATCH,
+ GAMETYPE_SYSTEMLINK_MATCH,
+};
+
+//----------------------------
+// CMatchmakingBasePanel
+//----------------------------
+class CMatchmakingBasePanel : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CMatchmakingBasePanel, CBaseDialog );
+
+public:
+ CMatchmakingBasePanel(vgui::Panel *parent);
+ ~CMatchmakingBasePanel();
+
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void Activate();
+
+ void SessionNotification( const int notification, const int param = 0 );
+ void SystemNotification( const int notification );
+ void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost );
+ void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping );
+
+ void OnLevelLoadingStarted();
+ void OnLevelLoadingFinished();
+ void CloseGameDialogs( bool bActivateNext = true );
+ void CloseAllDialogs( bool bActivateNext = true );
+ void CloseBaseDialogs( void );
+
+ void SetFooterButtons( CBaseDialog *pOwner, KeyValues *pData, int nButtonGap = -1 );
+ void ShowFooter( bool bShown );
+ void SetFooterButtonVisible( const char *pszText, bool bVisible );
+
+ uint GetGameType( void ) { return m_nGameType; }
+
+ MESSAGE_FUNC_CHARPTR( LoadMap, "LoadMap", mapname );
+private:
+ void OnOpenWelcomeDialog();
+ void OnOpenPauseDialog();
+ void OnOpenRankingsDialog();
+ void OnOpenSystemLinkDialog();
+ void OnOpenPlayerMatchDialog();
+ void OnOpenRankedMatchDialog();
+ void OnOpenAchievementsDialog();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Specific code for CS Achievements Display
+ //=============================================================================
+
+ // $TODO(HPE): Move this to a game-specific location
+ void OnOpenCSAchievementsDialog();
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ void OnOpenLeaderboardDialog( const char *pResourceName );
+ void OnOpenSessionOptionsDialog( const char *pResourceName );
+ void OnOpenSessionLobbyDialog( const char *pResourceName );
+ void OnOpenSessionBrowserDialog( const char *pResourceName );
+
+ void LoadSessionProperties();
+ bool ValidateSigninAndStorage( bool bOnlineRequired, const char *pIssuingCommand );
+ void CenterDialog( vgui::PHandle dlg );
+ void PushDialog( vgui::DHANDLE< CBaseDialog > &hDialog );
+ void PopDialog( bool bActivateNext = true );
+
+ vgui::DHANDLE< CBaseDialog > m_hWelcomeDialog;
+ vgui::DHANDLE< CBaseDialog > m_hPauseDialog;
+ vgui::DHANDLE< CBaseDialog > m_hStatsDialog;
+ vgui::DHANDLE< CBaseDialog > m_hRankingsDialog;
+ vgui::DHANDLE< CBaseDialog > m_hLeaderboardDialog;
+ vgui::DHANDLE< CBaseDialog > m_hSystemLinkDialog;
+ vgui::DHANDLE< CBaseDialog > m_hPlayerMatchDialog;
+ vgui::DHANDLE< CBaseDialog > m_hRankedMatchDialog;
+ vgui::DHANDLE< CBaseDialog > m_hAchievementsDialog;
+ vgui::DHANDLE< CBaseDialog > m_hSessionOptionsDialog;
+ vgui::DHANDLE< CBaseDialog > m_hSessionLobbyDialog;
+ vgui::DHANDLE< CBaseDialog > m_hSessionBrowserDialog;
+
+ CUtlStack< vgui::DHANDLE< CBaseDialog > > m_DialogStack;
+
+ uint m_nSessionType;
+ uint m_nGameType;
+ bool m_bPlayingOnline;
+ char m_szMapLoadName[MAX_MAP_NAME];
+ KeyValues *m_pSessionKeys;
+
+ CFooterPanel *m_pFooter;
+};
+
+
+#endif // MATCHMAKINGBASEPANEL_H
diff --git a/gameui/matchmaking/pausedialog.cpp b/gameui/matchmaking/pausedialog.cpp
new file mode 100644
index 0000000..f158583
--- /dev/null
+++ b/gameui/matchmaking/pausedialog.cpp
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Multiplayer pause menu
+//
+//=============================================================================//
+
+#include "pausedialog.h"
+#include "GameUI_Interface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------
+// CPauseDialog
+//--------------------------------
+CPauseDialog::CPauseDialog( vgui::Panel *pParent ) : BaseClass( pParent, "PauseDialog" )
+{
+ // do nothing
+}
+
+void CPauseDialog::Activate( void )
+{
+ BaseClass::Activate();
+
+ SetDeleteSelfOnClose( false );
+ m_Menu.SetFocus( 0 );
+}
+
+//-------------------------------------------------------
+// Keyboard input
+//-------------------------------------------------------
+void CPauseDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ switch( code )
+ {
+ case KEY_XBUTTON_B:
+ if ( GameUI().IsInLevel() )
+ {
+ m_pParent->OnCommand( "ResumeGame" );
+ }
+ break;
+
+ default:
+ BaseClass::OnKeyCodePressed( code );
+ break;
+ }
+} \ No newline at end of file
diff --git a/gameui/matchmaking/pausedialog.h b/gameui/matchmaking/pausedialog.h
new file mode 100644
index 0000000..3d580d8
--- /dev/null
+++ b/gameui/matchmaking/pausedialog.h
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Multiplayer pause menu
+//
+//=============================================================================//
+
+#ifndef PAUSEDIALOG_H
+#define PAUSEDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Multiplayer pause menu
+//-----------------------------------------------------------------------------
+class CPauseDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CPauseDialog, CBaseDialog );
+
+public:
+ CPauseDialog( vgui::Panel *parent );
+
+ virtual void Activate( void );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+};
+
+
+#endif // PAUSEDIALOG_H
diff --git a/gameui/matchmaking/sessionbrowserdialog.cpp b/gameui/matchmaking/sessionbrowserdialog.cpp
new file mode 100644
index 0000000..f29b177
--- /dev/null
+++ b/gameui/matchmaking/sessionbrowserdialog.cpp
@@ -0,0 +1,323 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Present a list of sessions from which the player can choose a game to join.
+//
+//=============================================================================//
+
+#include "sessionbrowserdialog.h"
+#include "engine/imatchmaking.h"
+#include "EngineInterface.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/Label.h"
+#include "KeyValues.h"
+#include "vgui/ISurface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+CSessionBrowserDialog *g_pBrowserDialog;
+
+//--------------------------------
+// CSessionBrowserDialog
+//--------------------------------
+CSessionBrowserDialog::CSessionBrowserDialog( vgui::Panel *pParent, KeyValues *pDialogKeys ) : BaseClass( pParent, "" )
+{
+ g_pBrowserDialog = this;
+ m_pDialogKeys = pDialogKeys;
+
+ SetDeleteSelfOnClose( true );
+}
+
+CSessionBrowserDialog::~CSessionBrowserDialog()
+{
+ m_pScenarioInfos.PurgeAndDeleteElements();
+}
+
+//---------------------------------------------------------------------
+// Purpose: Center the dialog on the screen
+//---------------------------------------------------------------------
+void CSessionBrowserDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ MoveToCenterOfScreen();
+ UpdateScenarioDisplay();
+}
+
+//---------------------------------------------------------------------
+// Purpose: Parse session properties and contexts from the resource file
+//---------------------------------------------------------------------
+void CSessionBrowserDialog::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ KeyValues *pScenarios = pResourceData->FindKey( "ScenarioInfoPanels" );
+ if ( pScenarios )
+ {
+ for ( KeyValues *pScenario = pScenarios->GetFirstSubKey(); pScenario != NULL; pScenario = pScenario->GetNextKey() )
+ {
+ CScenarioInfoPanel *pScenarioInfo = new CScenarioInfoPanel( this, "ScenarioInfoPanel" );
+ SETUP_PANEL( pScenarioInfo );
+ pScenarioInfo->m_pTitle->SetText( pScenario->GetString( "title" ) );
+ pScenarioInfo->m_pSubtitle->SetText( pScenario->GetString( "subtitle" ) );
+ pScenarioInfo->m_pMapImage->SetImage( pScenario->GetString( "image" ) );
+
+ int nTall = pScenario->GetInt( "tall", -1 );
+ if ( nTall > 0 )
+ {
+ pScenarioInfo->SetTall( nTall );
+ }
+
+ int nXPos = pScenario->GetInt( "xpos", -1 );
+ if ( nXPos >= 0 )
+ {
+ int x, y;
+ pScenarioInfo->GetPos( x, y );
+ pScenarioInfo->SetPos( nXPos, y );
+ }
+
+ int nDescOneYpos = pScenario->GetInt( "descOneY", -1 );
+ if ( nDescOneYpos > 0 )
+ {
+ int x, y;
+ pScenarioInfo->m_pDescOne->GetPos( x, y );
+ pScenarioInfo->m_pDescOne->SetPos( x, nDescOneYpos );
+ }
+
+ int nDescTwoYpos = pScenario->GetInt( "descTwoY", -1 );
+ if ( nDescTwoYpos > 0 )
+ {
+ int x, y;
+ pScenarioInfo->m_pDescTwo->GetPos( x, y );
+ pScenarioInfo->m_pDescTwo->SetPos( x, nDescTwoYpos );
+ }
+
+ m_pScenarioInfos.AddToTail( pScenarioInfo );
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Set up colors and other such stuff
+//---------------------------------------------------------------------
+void CSessionBrowserDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ for ( int i = 0; i < m_pScenarioInfos.Count(); ++i )
+ {
+ m_pScenarioInfos[i]->SetBgColor( pScheme->GetColor( "TanDark", Color( 0, 0, 0, 255 ) ) );
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Info about a session, sent from matchmaking
+//---------------------------------------------------------------------
+void CSessionBrowserDialog::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping )
+{
+ char *pPing;
+ switch ( ping )
+ {
+ case 0:
+ pPing = "#TF_Icon_Ping_Green";
+ break;
+ case 1:
+ pPing = "#TF_Icon_Ping_Yellow";
+ break;
+ case 2:
+ default:
+ pPing = "#TF_Icon_Ping_Red";
+ break;
+ }
+
+ int nScenarioId = 0;
+ uint nContextId = m_pDialogKeys->GetInt( "scenario", -1 );
+ for ( uint i = 0; i < pResult->cContexts; ++i )
+ {
+ if ( pResult->pContexts[i].dwContextId == nContextId )
+ {
+ nScenarioId = pResult->pContexts[i].dwValue;
+ break;
+ }
+ }
+
+ hostData_s *pData = (hostData_s*)pHostData;
+
+ int filledSlots = pResult->dwFilledPublicSlots + pResult->dwFilledPrivateSlots;
+ int totalSlots = filledSlots + pResult->dwOpenPublicSlots + pResult->dwOpenPrivateSlots;
+
+ m_GameStates.AddToTail( pData->gameState );
+ m_GameTimes.AddToTail( pData->gameTime );
+ m_XUIDs.AddToTail( pData->xuid );
+
+ char szSlots[16] = {0};
+ Q_snprintf( szSlots, sizeof( szSlots ), "%d/%d", filledSlots, totalSlots );
+
+ int ct = 0;
+ const char *ppStrings[4];
+ ppStrings[ct++] = pData->hostName;
+ ppStrings[ct++] = szSlots;
+ ppStrings[ct++] = pData->scenario;
+ if ( ping != -1 )
+ {
+ ppStrings[ct++] = pPing;
+ }
+ m_Menu.AddSectionedItem( ppStrings, ct );
+
+ m_ScenarioIndices.AddToTail( nScenarioId );
+ m_SearchIndices.AddToTail( searchIdx );
+
+ if ( m_Menu.GetItemCount() == 1 )
+ {
+ m_Menu.SetFocus( 0 );
+ }
+
+ UpdateScenarioDisplay();
+}
+
+//-----------------------------------------------------------------
+// Purpose: Show the correct scenario image and text
+//-----------------------------------------------------------------
+void CSessionBrowserDialog::UpdateScenarioDisplay( void )
+{
+ if ( !m_ScenarioIndices.Count() )
+ return;
+
+ // Check if the selected map has changed (first menu item)
+ int idx = m_Menu.GetActiveItemIndex();
+ for ( int i = 0; i < m_pScenarioInfos.Count(); ++i )
+ {
+ m_pScenarioInfos[i]->SetVisible( i == m_ScenarioIndices[idx] );
+ }
+
+ // Get the screen size
+ int wide, tall;
+ vgui::surface()->GetScreenSize(wide, tall);
+ bool bLodef = ( tall <= 480 );
+
+ const char *pState = "";
+ switch( m_GameStates[idx] )
+ {
+ case 0:
+ if ( bLodef )
+ {
+ pState = "#TF_GameState_InLobby_lodef";
+ }
+ else
+ {
+ pState = "#TF_GameState_InLobby";
+ }
+ break;
+
+ case 1:
+ if ( bLodef )
+ {
+ pState = "#TF_GameState_GameInProgress_lodef";
+ }
+ else
+ {
+ pState = "#TF_GameState_GameInProgress";
+ }
+ break;
+ }
+
+ char szTime[32] = {0};
+ if ( m_GameTimes[idx] >= NO_TIME_LIMIT )
+ {
+ Q_strncpy( szTime, "#TF_NoTimeLimit", sizeof( szTime) );
+ }
+ else
+ {
+ Q_snprintf( szTime, sizeof( szTime), "%d:00", m_GameTimes[idx] );
+ }
+
+ if ( !m_pScenarioInfos.IsValidIndex( m_ScenarioIndices[idx] ) )
+ return;
+
+ CScenarioInfoPanel *pPanel = m_pScenarioInfos[ m_ScenarioIndices[idx] ];
+ pPanel->m_pDescOne->SetText( pState );
+ pPanel->m_pDescTwo->SetText( szTime );
+}
+
+
+//-----------------------------------------------------------------
+// helper to swap two ints in a utlvector
+//-----------------------------------------------------------------
+static void Swap( CUtlVector< int > &vec, int iOne, int iTwo )
+{
+ int temp = vec[iOne];
+ vec[iOne] = vec[iTwo];
+ vec[iTwo] = temp;
+}
+
+//-----------------------------------------------------------------
+// Purpose: Swap the order of two menu items
+//-----------------------------------------------------------------
+void CSessionBrowserDialog::SwapMenuItems( int iOne, int iTwo )
+{
+ Swap( m_ScenarioIndices, iOne, iTwo );
+ Swap( m_SearchIndices, iOne, iTwo );
+ Swap( m_GameStates, iOne, iTwo );
+ Swap( m_GameTimes, iOne, iTwo );
+
+ // swap the XUIDs, too
+ XUID temp = m_XUIDs[iOne];
+ m_XUIDs[iOne] = m_XUIDs[iTwo];
+ m_XUIDs[iTwo] = temp;
+}
+
+//-----------------------------------------------------------------
+// Purpose: Handle commands from the dialog menu
+//-----------------------------------------------------------------
+void CSessionBrowserDialog::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "SelectSession" ) )
+ {
+ int idx = m_SearchIndices[ m_Menu.GetActiveItemIndex() ];
+ matchmaking->SelectSession( idx );
+ }
+
+ m_pParent->OnCommand( pCommand );
+}
+
+//-----------------------------------------------------------------
+// Purpose: Send key presses to the dialog's menu
+//-----------------------------------------------------------------
+void CSessionBrowserDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ switch( code )
+ {
+ case KEY_XBUTTON_B:
+ matchmaking->KickPlayerFromSession( 0 );
+ break;
+ case KEY_XBUTTON_X:
+#ifdef _X360
+ int idx = m_Menu.GetActiveItemIndex();
+ if ( m_XUIDs.IsValidIndex( idx ) )
+ {
+ XShowGamerCardUI( XBX_GetPrimaryUserId(), m_XUIDs[idx] );
+ }
+#endif
+ break;
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+
+ // Selected session may have been updated
+ UpdateScenarioDisplay();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSessionBrowserDialog::OnThink()
+{
+ vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
+ if ( code )
+ {
+ m_Menu.HandleKeyCode( code );
+ UpdateScenarioDisplay();
+ }
+
+ BaseClass::OnThink();
+}
diff --git a/gameui/matchmaking/sessionbrowserdialog.h b/gameui/matchmaking/sessionbrowserdialog.h
new file mode 100644
index 0000000..ea77b9b
--- /dev/null
+++ b/gameui/matchmaking/sessionbrowserdialog.h
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Present a list of sessions from which the player can choose a game to join.
+//
+//=====================================================================================//
+
+#ifndef SESSIONBROWSERDIALOG_H
+#define SESSIONBROWSERDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+class CSessionBrowserDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CSessionBrowserDialog, CBaseDialog );
+
+public:
+ CSessionBrowserDialog( vgui::Panel *parent, KeyValues *pDialogKeys );
+ ~CSessionBrowserDialog();
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *inResourceData );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnThink();
+
+ virtual void SwapMenuItems( int iOne, int iTwo );
+
+ void UpdateScenarioDisplay( void );
+ void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping );
+
+ KeyValues *m_pDialogKeys;
+
+ CUtlVector< CScenarioInfoPanel* > m_pScenarioInfos;
+ CUtlVector< int > m_ScenarioIndices;
+ CUtlVector< int > m_SearchIndices;
+ CUtlVector< int > m_GameStates;
+ CUtlVector< int > m_GameTimes;
+ CUtlVector< XUID > m_XUIDs;
+};
+
+
+#endif // SESSIONBROWSERDIALOG_H
diff --git a/gameui/matchmaking/sessionlobbydialog.cpp b/gameui/matchmaking/sessionlobbydialog.cpp
new file mode 100644
index 0000000..6f4e808
--- /dev/null
+++ b/gameui/matchmaking/sessionlobbydialog.cpp
@@ -0,0 +1,739 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "sessionlobbydialog.h"
+#include "engine/imatchmaking.h"
+#include "GameUI_Interface.h"
+#include "EngineInterface.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui/ILocalize.h"
+#include "BasePanel.h"
+#include "matchmakingbasepanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+CSessionLobbyDialog *g_pLobbyDialog;
+
+//--------------------------------
+// CSessionLobbyDialog
+//--------------------------------
+CSessionLobbyDialog::CSessionLobbyDialog( vgui::Panel *pParent ) : BaseClass( pParent, "SessionLobbyDialog" )
+{
+ m_Menus[0].SetParent( this );
+ m_Menus[0].SetName( "BluePlayers" );
+ m_Menus[1].SetParent( this );
+ m_Menus[1].SetName( "RedPlayers" );
+
+ m_iLocalTeam = -1;
+ m_iActiveMenu = -1;
+ m_nHostId = 0;
+ m_bHostLobby = false;
+ m_bCenterOnScreen = true;
+
+ m_pLobbyStateBg = new vgui::Panel( this, "LobbyStateBg" );
+ m_pLobbyStateLabel = new CPropertyLabel( this, "LobbyStateLabel", "" );
+ m_pLobbyStateIcon = new CPropertyLabel( this, "LobbyStateIcon", "" );
+ m_pHostLabel = new CPropertyLabel( this, "HostLabel", "" );
+ m_pHostOptionsPanel = new vgui::EditablePanel( this, "HostOptions" );
+
+ m_pScenarioInfo = new CScenarioInfoPanel( this, "GameScenario" );
+ m_pTeamInfos[BLUE_TEAM_LOBBY] = new CScenarioInfoPanel( this, "BlueTeamDescription" );
+ m_pTeamInfos[RED_TEAM_LOBBY] = new CScenarioInfoPanel( this, "RedTeamDescription" );
+
+ m_pDialogKeys = NULL;
+
+ g_pLobbyDialog = this;
+
+ m_bStartingGame = false;
+ m_nLastPlayersNeeded = 0;
+
+ m_nMinInfoHeight[BLUE_TEAM_LOBBY] = 0;
+ m_nMinInfoHeight[RED_TEAM_LOBBY] = 0;
+}
+
+CSessionLobbyDialog::~CSessionLobbyDialog()
+{
+ delete m_pLobbyStateBg;
+ delete m_pLobbyStateLabel;
+ delete m_pLobbyStateIcon;
+ delete m_pHostLabel;
+ delete m_pHostOptionsPanel;
+
+ delete m_pScenarioInfo;
+ for ( int i = 0; i < TOTAL_LOBBY_TEAMS; ++i )
+ {
+ delete m_pTeamInfos[i];
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Dialog keys contain session contexts and properties
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::SetDialogKeys( KeyValues *pKeys )
+{
+ m_pDialogKeys = pKeys;
+ InvalidateLayout();
+}
+
+//---------------------------------------------------------------------
+// Purpose: Helper to set label text from keyvalues
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::SetTextFromKeyvalues( CPropertyLabel *pLabel )
+{
+ KeyValues *pKey = m_pDialogKeys->FindKey( pLabel->m_szPropertyString );
+ if ( pKey )
+ {
+ const char *pString = pKey->GetString( "displaystring", NULL );
+ if ( pString )
+ {
+ pLabel->SetText( pString );
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Center the dialog on the screen
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( !m_pDialogKeys )
+ return;
+
+ // Set the label strings according to the keyvalues passed in
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pTitle );
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pDescOne );
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pDescTwo );
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pDescThree );
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pValueTwo );
+ SetTextFromKeyvalues( m_pScenarioInfo->m_pValueThree );
+
+ const char *pDiskName = "unknown";
+ KeyValues *pName = m_pDialogKeys->FindKey( "MapDiskNames" );
+ if ( pName )
+ {
+ KeyValues *pScenario = m_pDialogKeys->FindKey( "CONTEXT_SCENARIO" );
+ if ( pScenario )
+ {
+ pDiskName = pName->GetString( pScenario->GetString( "displaystring" ), "unknown" );
+ }
+ }
+
+ // find the scenario type
+ KeyValues *pType = m_pDialogKeys->FindKey( "ScenarioTypes" );
+ if ( pType )
+ {
+ const char *pString = pType->GetString( pDiskName, NULL );
+ if ( pString )
+ {
+ m_pScenarioInfo->m_pSubtitle->SetText( pString );
+ }
+ }
+
+ // Set the team goals
+ KeyValues *pGoals = m_pDialogKeys->FindKey( "TeamGoals" );
+ if ( pGoals )
+ {
+ KeyValues *pTeam = pGoals->FindKey( "Blue" );
+ if ( pTeam )
+ {
+ m_pTeamInfos[BLUE_TEAM_LOBBY]->m_pDescOne->SetText( pTeam->GetString( pDiskName, "" ) );
+ }
+ pTeam = pGoals->FindKey( "Red" );
+ if ( pTeam )
+ {
+ m_pTeamInfos[RED_TEAM_LOBBY]->m_pDescOne->SetText( pTeam->GetString( pDiskName, "" ) );
+ }
+ }
+
+ for ( int i = 0; i < TOTAL_LOBBY_TEAMS; ++i )
+ {
+ UpdatePlayerCountDisplay( i );
+ }
+
+ if ( m_bCenterOnScreen )
+ {
+ MoveToCenterOfScreen();
+ }
+
+ // Don't allow player reviews in system link games
+ CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent );
+ if ( pBase )
+ {
+ pBase->SetFooterButtonVisible( "#GameUI_PlayerReview", pBase->GetGameType() != GAMETYPE_SYSTEMLINK_MATCH );
+
+ // hide the settings changing if we're in a ranked game
+ if ( m_pHostOptionsPanel )
+ {
+ bool bVisible = pBase->GetGameType() != GAMETYPE_RANKED_MATCH;
+ vgui::Label *pSettingsLabel = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("ChangeSettingsButton",true);
+ if ( pSettingsLabel )
+ {
+ pSettingsLabel->SetVisible( bVisible );
+ }
+ pSettingsLabel = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("ChangeSettingsText",true);
+ if ( pSettingsLabel )
+ {
+ pSettingsLabel->SetVisible( bVisible );
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Parse session properties and contexts from the resource file
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ m_nImageBorderWidth = pResourceData->GetInt( "imageborderwidth", 15 );
+ m_nTeamspacing = pResourceData->GetInt( "teamspacing", 0 );
+ m_bHostLobby = pResourceData->GetInt( "hostlobby", 0 ) != 0;
+ m_bCenterOnScreen = pResourceData->GetInt( "center", 1 ) == 1;
+
+ Q_strncpy( m_szCommand, pResourceData->GetString( "commandstring", "NULL" ), sizeof( m_szCommand ) );
+}
+
+//---------------------------------------------------------------------
+// Purpose: Set up colors and other such stuff
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ Color cLabelColor = pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 0, 0, 0, 255 ) );
+
+ m_pLobbyStateLabel->SetFgColor( cLabelColor );
+ m_pHostLabel->SetFgColor( cLabelColor );
+
+ m_pLobbyStateBg->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) );
+ m_pLobbyStateBg->SetPaintBackgroundType( 2 );
+
+ m_pHostOptionsPanel->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) );
+ m_pHostOptionsPanel->SetPaintBackgroundType( 2 );
+
+ m_pScenarioInfo->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) );
+ m_pTeamInfos[BLUE_TEAM_LOBBY]->SetBgColor( pScheme->GetColor( "HudBlueTeam", Color( 0, 0, 0, 255 ) ) );
+ m_pTeamInfos[RED_TEAM_LOBBY]->SetBgColor( pScheme->GetColor( "HudRedTeam", Color( 0, 0, 0, 255 ) ) );
+
+ // Cache of these heights so we never go below them when resizing
+ m_nMinInfoHeight[BLUE_TEAM_LOBBY] = m_pTeamInfos[BLUE_TEAM_LOBBY]->GetTall();
+ m_nMinInfoHeight[RED_TEAM_LOBBY] = m_pTeamInfos[RED_TEAM_LOBBY]->GetTall();
+
+ //Lets set all the labels this panel owns to be the right fgcolor. hooray vgui!
+ int iChildren = m_pHostOptionsPanel->GetChildCount();
+
+ for ( int i=0;i<iChildren;i++ )
+ {
+ vgui::Label *pLabel = dynamic_cast< vgui::Label * >( m_pHostOptionsPanel->GetChild(i) );
+ if ( pLabel )
+ {
+ SETUP_PANEL( pLabel );
+ pLabel->SetFgColor( cLabelColor );
+ }
+ }
+
+ vgui::Label *pPlayerReviewLabel = (vgui::Label *)FindChildByName("PlayerReviewLabel" );
+ if ( pPlayerReviewLabel )
+ {
+ SETUP_PANEL( pPlayerReviewLabel );
+ pPlayerReviewLabel->SetFgColor( cLabelColor );
+ }
+
+ SetLobbyReadyState( m_nLastPlayersNeeded );
+}
+
+void CSessionLobbyDialog::PositionTeamInfos()
+{
+ // Line up the team info panels and menus
+ int x, y;
+ int menux, menuy;
+ m_pTeamInfos[0]->GetPos( x, y );
+ m_Menus[0].GetPos( menux, menuy );
+
+ for ( int i = 1; i < TOTAL_LOBBY_TEAMS; ++i )
+ {
+ y += m_pTeamInfos[i - 1]->GetTall() + m_nTeamspacing;
+ m_pTeamInfos[i]->SetPos( x, y );
+ m_Menus[i].SetPos( menux, y );
+ }
+}
+
+void CSessionLobbyDialog::ActivateNextMenu()
+{
+ int startIndex = m_iActiveMenu;
+ m_Menus[m_iActiveMenu].SetFocus( -1 );
+ do
+ {
+ m_iActiveMenu = (m_iActiveMenu + 1) % TOTAL_LOBBY_TEAMS;
+ } while ( m_Menus[m_iActiveMenu].GetItemCount() == 0 && m_iActiveMenu != startIndex );
+
+ m_Menus[m_iActiveMenu].SetFocus( 0 );
+}
+
+void CSessionLobbyDialog::ActivatePreviousMenu()
+{
+ int startIndex = m_iActiveMenu;
+ m_Menus[m_iActiveMenu].SetFocus( -1 );
+ do
+ {
+ m_iActiveMenu = m_iActiveMenu ? m_iActiveMenu - 1 : TOTAL_LOBBY_TEAMS - 1;
+ } while ( m_Menus[m_iActiveMenu].GetItemCount() == 0 && m_iActiveMenu != startIndex );
+
+ m_Menus[m_iActiveMenu].SetFocus( m_Menus[m_iActiveMenu].GetItemCount() - 1 );
+}
+
+void CSessionLobbyDialog::UpdatePlayerCountDisplay( int iTeam )
+{
+ int ct = m_Menus[iTeam].GetItemCount();
+ wchar_t wszString[32];
+ wchar_t *wzPlayersFmt = g_pVGuiLocalize->Find( ct != 1 ? "#TF_ScoreBoard_Players" : "#TF_ScoreBoard_Player" );
+ wchar_t wzPlayerCt[8];
+ V_snwprintf( wzPlayerCt, ARRAYSIZE( wzPlayerCt ), L"%d", ct );
+ g_pVGuiLocalize->ConstructString( wszString, sizeof( wszString ), wzPlayersFmt, 1, wzPlayerCt );
+
+ m_pTeamInfos[iTeam]->m_pSubtitle->SetText( wszString );
+
+ if ( m_nMinInfoHeight[iTeam] == 0 )
+ {
+ m_nMinInfoHeight[iTeam] = m_pTeamInfos[iTeam]->GetTall();
+ }
+
+ int height = max( m_nMinInfoHeight[iTeam], m_Menus[iTeam].GetTall() );
+ m_pTeamInfos[iTeam]->SetTall( height );
+
+ PositionTeamInfos();
+}
+
+void CSessionLobbyDialog::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int iTeam, byte cVoiceState, int nPlayersNeeded, bool bHost )
+{
+ if ( m_iLocalTeam == -1 )
+ {
+ m_iLocalTeam = iTeam;
+ }
+
+ bool bReady = ( nPlayersNeeded == 0 );
+
+ // Look for the player
+ int iFoundTeam = -1;
+ int iFoundItem = -1;
+ CPlayerItem *pFound = NULL;
+ for ( int iMenu = 0; iMenu < TOTAL_LOBBY_TEAMS && !pFound; ++iMenu )
+ {
+ CDialogMenu &menu = m_Menus[iMenu];
+
+ if ( menu.GetItemCount() == 0 )
+ continue;
+
+ for ( int idx = 0; idx < menu.GetItemCount(); ++idx )
+ {
+ CPlayerItem *pPlayerItem = dynamic_cast< CPlayerItem* >( menu.GetItem( idx ) );
+ if ( pPlayerItem && pPlayerItem->m_nId == nPlayerId )
+ {
+ pFound = pPlayerItem;
+ iFoundTeam = iMenu;
+ iFoundItem = idx;
+ break;
+ }
+ }
+ }
+
+ // Update menu and item focus if the player changed teams
+ if ( iFoundTeam != iTeam )
+ {
+ if ( pFound )
+ {
+ // Remove the player from the current team
+ m_Menus[iFoundTeam].RemovePlayerItem( iFoundItem );
+ UpdatePlayerCountDisplay( iFoundTeam );
+ }
+
+ if ( 0 <= iTeam && iTeam < TOTAL_LOBBY_TEAMS )
+ {
+ // Add the player to the new team
+ m_Menus[iTeam].AddPlayerItem( pName, nPlayerId, cVoiceState, bReady );
+ UpdatePlayerCountDisplay( iTeam );
+ }
+
+ // if the player joined an empty lobby, set the active team
+ if ( m_iActiveMenu == -1 )
+ {
+ m_iActiveMenu = iTeam;
+ m_Menus[m_iActiveMenu].SetFocus( 0 );
+ }
+
+ // update the highlight position
+ if ( iFoundTeam == m_iActiveMenu )
+ {
+ CDialogMenu &activeMenu = m_Menus[m_iActiveMenu];
+ int iActive = activeMenu.GetActiveItemIndex();
+
+ if ( iActive == iFoundItem )
+ {
+ // The changed player was also the highlighted player
+ if ( iTeam >= 0 )
+ {
+ // Move the highlight to the player's new position
+ activeMenu.SetFocus( -1 );
+ m_iActiveMenu = iTeam;
+ m_Menus[m_iActiveMenu].SetFocus( m_Menus[m_iActiveMenu].GetItemCount() - 1 );
+ }
+ else
+ {
+ // player left the game, move the highlight to the next filled slot
+ if ( iActive >= activeMenu.GetItemCount() )
+ {
+ ActivateNextMenu();
+ }
+ }
+ }
+ else if ( iActive > iFoundItem )
+ {
+ // Need to drop the highlighted index one slot
+ m_Menus[m_iActiveMenu].SetFocus( iActive - 1 );
+ }
+ }
+ }
+ else
+ {
+ if ( pFound )
+ {
+ if ( pFound->m_bVoice != cVoiceState )
+ {
+ pFound->m_bVoice = cVoiceState;
+ pFound->InvalidateLayout();
+ }
+ }
+ }
+
+ if ( bHost )
+ {
+ wchar_t wszString[MAX_PATH];
+ wchar_t wszHostname[MAX_PATH];
+ wchar_t *wzHostFmt = g_pVGuiLocalize->Find( "#TF_Lobby_Host" );
+ g_pVGuiLocalize->ConvertANSIToUnicode( pName, wszHostname, sizeof( wszHostname ) );
+
+ V_snwprintf( wszString, ARRAYSIZE(wszString), L"%s\n%s", wzHostFmt, wszHostname );
+
+ m_pHostLabel->SetText( wszString );
+ m_nHostId = nPlayerId;
+ }
+
+ int iPlayersNeeded = ( bReady ) ? 0 : nPlayersNeeded;
+ SetLobbyReadyState( iPlayersNeeded );
+ m_nLastPlayersNeeded = iPlayersNeeded;
+
+ InvalidateLayout( true, false );
+}
+
+void CSessionLobbyDialog::SetLobbyReadyState( int nPlayersNeeded )
+{
+ // check if the host is allowed to start the game
+ if ( nPlayersNeeded <= 0 )
+ {
+ if ( m_bHostLobby )
+ {
+ m_pLobbyStateLabel->SetText( "#TF_PressStart" );
+ m_pLobbyStateIcon->SetText( "#GameUI_Icons_START" );
+ }
+ else
+ {
+ m_pLobbyStateLabel->SetText( "#TF_WaitingForHost" );
+ m_pLobbyStateIcon->SetText( "#TF_Icon_Alert" );
+
+ m_bStartingGame = false; // client guesses that they can change teams
+ }
+ }
+ else
+ {
+ wchar_t wszWaiting[64];
+ wchar_t *wzWaitingFmt;
+ if ( nPlayersNeeded == 1 )
+ {
+ wzWaitingFmt = g_pVGuiLocalize->Find( "#TF_WaitingForPlayerFmt" );
+ }
+ else
+ {
+ wzWaitingFmt = g_pVGuiLocalize->Find( "#TF_WaitingForPlayersFmt" );
+ }
+ wchar_t wzPlayers[8];
+ V_snwprintf( wzPlayers, ARRAYSIZE( wzPlayers ), L"%d", nPlayersNeeded );
+ g_pVGuiLocalize->ConstructString( wszWaiting, sizeof( wszWaiting ), wzWaitingFmt, 1, wzPlayers );
+ m_pLobbyStateLabel->SetText( wszWaiting );
+ m_pLobbyStateIcon->SetText( "#TF_Icon_Alert" );
+
+ // If we were starting and dropped below min players, cancel
+ SetStartGame( false );
+ }
+}
+
+void CSessionLobbyDialog::UpdateCountdown( int seconds )
+{
+ if ( seconds == -1 )
+ {
+ // countdown was canceled
+ SetLobbyReadyState( 0 );
+ return;
+ }
+
+ // Set the text in the countdown label
+ wchar_t wszCountdown[MAX_PATH];
+ wchar_t wszSeconds[MAX_PATH];
+ wchar_t *wzCountdownFmt;
+ if ( seconds != 1 )
+ {
+ wzCountdownFmt = g_pVGuiLocalize->Find( "#TF_StartingInSecs" );
+ }
+ else
+ {
+ wzCountdownFmt = g_pVGuiLocalize->Find( "#TF_StartingInSec" );
+ }
+ V_snwprintf( wszSeconds, ARRAYSIZE( wszSeconds ), L"%d", seconds );
+ g_pVGuiLocalize->ConstructString( wszCountdown, sizeof( wszCountdown ), wzCountdownFmt, 1, wszSeconds );
+
+ m_pLobbyStateLabel->SetText( wszCountdown );
+
+ if ( !m_bHostLobby )
+ {
+ m_bStartingGame = true; // client guesses that they can't change teams
+ }
+}
+
+//-----------------------------------------------------------------
+// Purpose: Send key presses to the dialog's menu
+//-----------------------------------------------------------------
+void CSessionLobbyDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ CDialogMenu *pMenu = &m_Menus[m_iActiveMenu];
+ if ( !pMenu )
+ return;
+
+ int idx = pMenu->GetActiveItemIndex();
+ int itemCt = pMenu->GetItemCount();
+
+ CPlayerItem *pItem = dynamic_cast< CPlayerItem* >( pMenu->GetItem( idx ) );
+ if ( !pItem )
+ return;
+
+ SetDeleteSelfOnClose( true );
+
+ switch( code )
+ {
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case STEAMCONTROLLER_DPAD_DOWN:
+ if ( idx >= itemCt - 1 )
+ {
+ ActivateNextMenu();
+ }
+ else
+ {
+ pMenu->HandleKeyCode( code );
+ }
+ break;
+
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case STEAMCONTROLLER_DPAD_UP:
+ if ( idx <= 0 )
+ {
+ ActivatePreviousMenu();
+ }
+ else
+ {
+ pMenu->HandleKeyCode( code );
+ }
+ break;
+
+ case KEY_XBUTTON_A:
+ case STEAMCONTROLLER_A:
+#ifdef _X360
+ XShowGamerCardUI( XBX_GetPrimaryUserId(), pItem->m_nId );
+#endif
+ break;
+
+ case KEY_XBUTTON_RIGHT_SHOULDER:
+#ifdef _X360
+ {
+ // Don't allow player reviews in system link games
+ CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent );
+ if ( pBase && pBase->GetGameType() == GAMETYPE_SYSTEMLINK_MATCH )
+ break;
+ }
+
+ XShowPlayerReviewUI( XBX_GetPrimaryUserId(), pItem->m_nId );
+#endif
+ break;
+
+ case KEY_XBUTTON_LEFT_SHOULDER:
+ {
+ // Don't kick ourselves
+ if ( m_bHostLobby )
+ {
+ if ( pItem && ((CPlayerItem*)pItem)->m_nId != m_nHostId )
+ {
+ GameUI().ShowMessageDialog( MD_KICK_CONFIRMATION, this );
+ }
+ else
+ {
+ vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
+ }
+ }
+ }
+ break;
+
+ case KEY_XBUTTON_B:
+ case STEAMCONTROLLER_B:
+ GameUI().ShowMessageDialog( MD_EXIT_SESSION_CONFIRMATION, this );
+
+ if ( m_bHostLobby )
+ {
+ SetStartGame( false );
+ }
+ break;
+
+ case KEY_XBUTTON_X:
+ case STEAMCONTROLLER_X:
+ if ( m_bStartingGame )
+ {
+ // We think we're loading the game, so play deny sound
+ vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
+ }
+ else
+ {
+ matchmaking->ChangeTeam( NULL );
+ }
+ break;
+
+ case KEY_XBUTTON_Y:
+ case STEAMCONTROLLER_Y:
+ if ( m_bHostLobby )
+ {
+ // Don't allow settings changes in ranked games
+ CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent );
+ if ( pBase && pBase->GetGameType() == GAMETYPE_RANKED_MATCH )
+ break;
+
+ SetDeleteSelfOnClose( false );
+ OnCommand( "SessionOptions_Modify" );
+ SetStartGame( false );
+ }
+ break;
+
+ case KEY_XBUTTON_START:
+ SetStartGame( !m_bStartingGame );
+ break;
+
+ default:
+ pMenu->HandleKeyCode( code );
+ break;
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: start and stop the countdown
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::SetStartGame( bool bStartGame )
+{
+ if ( !m_bHostLobby )
+ return;
+
+ bool bCanStart = true;
+ if ( m_bStartingGame != bStartGame )
+ {
+ if ( bStartGame )
+ {
+ m_bStartingGame = matchmaking->StartGame();
+ bCanStart = m_bStartingGame;
+ }
+ else
+ {
+ if ( matchmaking->CancelStartGame() )
+ {
+ m_bStartingGame = false;
+ }
+ }
+
+ // If we can start the game but haven't started yet, show the "Start Game" label
+ bool bShowStartGame = bCanStart && !m_bStartingGame;
+
+ // show/hide the "start game" and countdown hint label based on state
+ vgui::Label *pStartGame = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("StartGameText" );
+ if ( pStartGame )
+ {
+ pStartGame->SetVisible( m_bStartingGame == false );
+ }
+
+ vgui::Label *pCancelCountdown = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("CancelGameText" );
+ if ( pCancelCountdown )
+ {
+ pCancelCountdown->SetVisible( m_bStartingGame == true );
+ }
+
+ if ( bShowStartGame )
+ {
+ m_pLobbyStateLabel->SetText( "#TF_PressStart" );
+ m_pLobbyStateIcon->SetText( "#GameUI_Icons_START" );
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Handle menu commands
+//---------------------------------------------------------------------
+void CSessionLobbyDialog::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "ReturnToMainMenu" ) )
+ {
+ matchmaking->KickPlayerFromSession( 0 );
+ }
+ else if ( !Q_stricmp( pCommand, "KickPlayer" ) )
+ {
+ CDialogMenu *pMenu = &m_Menus[m_iActiveMenu];
+ CPlayerItem *pItem = (CPlayerItem*)pMenu->GetItem( pMenu->GetActiveItemIndex() );
+ if ( pItem )
+ {
+ matchmaking->KickPlayerFromSession( pItem->m_nId );
+ }
+ }
+
+ GetParent()->OnCommand( pCommand );
+}
+
+static int pnum = 1;
+
+CON_COMMAND( mm_add_player, "Add a player" )
+{
+ if ( args.ArgC() >= 5 )
+ {
+ int team = atoi( args[1] );
+ const char *pName = args[2];
+ uint32 id = atoi( args[3] );
+ byte cVoiceState = atoi( args[4] ) != 0;
+ int nPlayersNeeded = atoi( args[5] );
+ g_pLobbyDialog->UpdatePlayerInfo( id, pName, team, cVoiceState, nPlayersNeeded, false );
+ }
+ else if ( args.ArgC() >= 1 )
+ {
+ char name[ 32 ];
+ int team = pnum & 0x1;
+ int id = pnum++;
+ if ( args.ArgC() >= 2 )
+ {
+ team = atoi( args[ 1 ] );
+ if ( args.ArgC() >= 3 )
+ {
+ id = atoi( args[2] );
+ }
+ }
+ Q_snprintf( name, sizeof( name ), "Player%d", pnum );
+ g_pLobbyDialog->UpdatePlayerInfo( id, name, team, true, 0, false );
+ }
+} \ No newline at end of file
diff --git a/gameui/matchmaking/sessionlobbydialog.h b/gameui/matchmaking/sessionlobbydialog.h
new file mode 100644
index 0000000..9049333
--- /dev/null
+++ b/gameui/matchmaking/sessionlobbydialog.h
@@ -0,0 +1,85 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef SESSIONLOBBYDIALOG_H
+#define SESSIONLOBBYDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Simple menu to choose a matchmaking session type
+//-----------------------------------------------------------------------------
+class CSessionLobbyDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CSessionLobbyDialog, CBaseDialog );
+
+public:
+ CSessionLobbyDialog( vgui::Panel *parent );
+ ~CSessionLobbyDialog();
+
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+
+ virtual void PerformLayout();
+ virtual void ApplySettings( KeyValues *inResourceData );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ void SetDialogKeys( KeyValues *pKeys );
+ void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost );
+ void UpdateCountdown( int seconds );
+
+private:
+ void ActivateNextMenu();
+ void ActivatePreviousMenu();
+ void UpdatePlayerCountDisplay( int iTeam );
+ void SetLobbyReadyState( int nPlayersNeeded );
+ void PositionTeamInfos();
+
+ void SetTextFromKeyvalues( CPropertyLabel *pLabel );
+
+ void SetStartGame( bool bStartGame );
+
+ enum
+ {
+ BLUE_TEAM_LOBBY,
+ RED_TEAM_LOBBY,
+ TOTAL_LOBBY_TEAMS,
+ };
+
+ CDialogMenu m_Menus[TOTAL_LOBBY_TEAMS];
+
+ vgui::Panel *m_pLobbyStateBg;
+ CPropertyLabel *m_pLobbyStateLabel;
+ CPropertyLabel *m_pLobbyStateIcon;
+ CPropertyLabel *m_pHostLabel;
+ vgui::EditablePanel *m_pHostOptionsPanel;
+
+ KeyValues *m_pDialogKeys;
+
+ CScenarioInfoPanel *m_pScenarioInfo;
+ CScenarioInfoPanel *m_pTeamInfos[TOTAL_LOBBY_TEAMS];
+
+ int m_nMinInfoHeight[TOTAL_LOBBY_TEAMS];
+
+ uint64 m_nHostId;
+ bool m_bReady;
+ bool m_bHostLobby;
+ bool m_bCenterOnScreen;
+ int m_iLocalTeam;
+ int m_iActiveMenu;
+ int m_nImageBorderWidth;
+ int m_nTeamspacing;
+ char m_szCommand[MAX_COMMAND_LEN];
+
+ bool m_bStartingGame;
+ int m_nLastPlayersNeeded;
+};
+
+
+#endif // SESSIONLOBBYDIALOG_H
diff --git a/gameui/matchmaking/sessionoptionsdialog.cpp b/gameui/matchmaking/sessionoptionsdialog.cpp
new file mode 100644
index 0000000..fba64d8
--- /dev/null
+++ b/gameui/matchmaking/sessionoptionsdialog.cpp
@@ -0,0 +1,420 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Dialog for setting customizable game options
+//
+//=============================================================================//
+
+#include "sessionoptionsdialog.h"
+#include "engine/imatchmaking.h"
+#include "EngineInterface.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/Label.h"
+#include "KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//---------------------------------------------------------------------
+// CSessionOptionsDialog
+//---------------------------------------------------------------------
+CSessionOptionsDialog::CSessionOptionsDialog( vgui::Panel *pParent ) : BaseClass( pParent, "SessionOptions" )
+{
+ SetDeleteSelfOnClose( true );
+
+ m_pDialogKeys = NULL;
+ m_bModifySession = false;
+}
+
+CSessionOptionsDialog::~CSessionOptionsDialog()
+{
+ m_pScenarioInfos.PurgeAndDeleteElements();
+}
+
+//---------------------------------------------------------------------
+// Purpose: Dialog keys contain session contexts and properties
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::SetDialogKeys( KeyValues *pKeys )
+{
+ m_pDialogKeys = pKeys;
+}
+
+
+//---------------------------------------------------------------------
+// Purpose: Strip off the game type from the resource name
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::SetGameType( const char *pString )
+{
+ // Get the full gametype from the resource name
+ const char *pGametype = Q_stristr( pString, "_" );
+ if ( !pGametype )
+ return;
+
+ Q_strncpy( m_szGametype, pGametype + 1, sizeof( m_szGametype ) );
+
+ // set the menu filter
+ m_Menu.SetFilter( m_szGametype );
+}
+
+//---------------------------------------------------------------------
+// Purpose: Center the dialog on the screen
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ UpdateScenarioDisplay();
+
+ MoveToCenterOfScreen();
+
+ if ( m_pRecommendedLabel )
+ {
+ bool bHosting = ( Q_stristr( m_szGametype, "Host" ) );
+ m_pRecommendedLabel->SetVisible( bHosting );
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Apply common properties and contexts
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::ApplyCommonProperties( KeyValues *pKeys )
+{
+ for ( KeyValues *pProperty = pKeys->GetFirstSubKey(); pProperty != NULL; pProperty = pProperty->GetNextKey() )
+ {
+ const char *pName = pProperty->GetName();
+
+ if ( !Q_stricmp( pName, "SessionContext" ) )
+ {
+ // Create a new session context
+ sessionProperty_t ctx;
+ ctx.nType = SESSION_CONTEXT;
+ Q_strncpy( ctx.szID, pProperty->GetString( "id", "NULL" ), sizeof( ctx.szID ) );
+ Q_strncpy( ctx.szValue, pProperty->GetString( "value", "NULL" ), sizeof( ctx.szValue ) );
+ // ctx.szValueType not used
+
+ m_SessionProperties.AddToTail( ctx );
+ }
+ else if ( !Q_stricmp( pName, "SessionProperty" ) )
+ {
+ // Create a new session property
+ sessionProperty_t prop;
+ prop.nType = SESSION_PROPERTY;
+ Q_strncpy( prop.szID, pProperty->GetString( "id", "NULL" ), sizeof( prop.szID ) );
+ Q_strncpy( prop.szValue, pProperty->GetString( "value", "NULL" ), sizeof( prop.szValue ) );
+ Q_strncpy( prop.szValueType, pProperty->GetString( "valuetype", "NULL" ), sizeof( prop.szValueType ) );
+
+ m_SessionProperties.AddToTail( prop );
+ }
+ else if ( !Q_stricmp( pName, "SessionFlag" ) )
+ {
+ sessionProperty_t flag;
+ flag.nType = SESSION_FLAG;
+ Q_strncpy( flag.szID, pProperty->GetString(), sizeof( flag.szID ) );
+
+ m_SessionProperties.AddToTail( flag );
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Purpose: Parse session properties and contexts from the resource file
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::ApplySettings( KeyValues *pResourceData )
+{
+ BaseClass::ApplySettings( pResourceData );
+
+ // Apply settings common to all game types
+ ApplyCommonProperties( pResourceData );
+
+ // Apply settings specific to this game type
+ KeyValues *pSettings = pResourceData->FindKey( m_szGametype );
+ if ( pSettings )
+ {
+ Q_strncpy( m_szCommand, pSettings->GetString( "commandstring", "NULL" ), sizeof( m_szCommand ) );
+ m_pTitle->SetText( pSettings->GetString( "title", "Unknown" ) );
+
+ ApplyCommonProperties( pSettings );
+ }
+
+ KeyValues *pScenarios = pResourceData->FindKey( "ScenarioInfoPanels" );
+ if ( pScenarios )
+ {
+ for ( KeyValues *pScenario = pScenarios->GetFirstSubKey(); pScenario != NULL; pScenario = pScenario->GetNextKey() )
+ {
+ CScenarioInfoPanel *pScenarioInfo = new CScenarioInfoPanel( this, "ScenarioInfoPanel" );
+ SETUP_PANEL( pScenarioInfo );
+ pScenarioInfo->m_pTitle->SetText( pScenario->GetString( "title" ) );
+ pScenarioInfo->m_pSubtitle->SetText( pScenario->GetString( "subtitle" ) );
+ pScenarioInfo->m_pMapImage->SetImage( pScenario->GetString( "image" ) );
+
+ int nTall = pScenario->GetInt( "tall", -1 );
+ if ( nTall > 0 )
+ {
+ pScenarioInfo->SetTall( nTall );
+ }
+
+ m_pScenarioInfos.AddToTail( pScenarioInfo );
+ }
+ }
+
+ if ( Q_stristr( m_szGametype, "Modify" ) )
+ {
+ m_bModifySession = true;
+ }
+}
+
+int CSessionOptionsDialog::GetMaxPlayersRecommendedOption( void )
+{
+ MM_QOS_t qos = matchmaking->GetQosWithLIVE();
+
+ // Conservatively assume that every player needs ~ 7 kBytes/s
+ // plus one for the hosting player.
+ int numPlayersCanService = 1 + int( qos.flBwUpKbs / 7.0f );
+
+ // Determine the option that suits our B/W bests
+ int options[] = { 8, 12, 16 };
+
+ for ( int k = 1; k < ARRAYSIZE( options ); ++ k )
+ {
+ if ( options[k] > numPlayersCanService )
+ {
+ Msg( "[SessionOptionsDialog] Defaulting number of players to %d (upstream b/w = %.1f kB/s ~ %d players).\n",
+ options[k - 1], qos.flBwUpKbs, numPlayersCanService );
+ return k - 1;
+ }
+ }
+
+ return ARRAYSIZE( options ) - 1;
+}
+
+//---------------------------------------------------------------------
+// Purpose: Set up colors and other such stuff
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ for ( int i = 0; i < m_pScenarioInfos.Count(); ++i )
+ {
+ m_pScenarioInfos[i]->SetBgColor( pScheme->GetColor( "TanDark", Color( 0, 0, 0, 255 ) ) );
+ }
+
+ m_pRecommendedLabel = dynamic_cast<vgui::Label *>(FindChildByName( "RecommendedLabel" ));
+}
+
+//---------------------------------------------------------------------
+// Purpose: Send all properties and contexts to the matchmaking session
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::SetupSession( void )
+{
+ KeyValues *pKeys = new KeyValues( "SessionKeys" );
+
+ // Send user-selected properties and contexts
+ for ( int i = 0; i < m_Menu.GetItemCount(); ++i )
+ {
+ COptionsItem *pItem = dynamic_cast< COptionsItem* >( m_Menu.GetItem( i ) );
+ if ( !pItem )
+ {
+ continue;
+ }
+
+ const sessionProperty_t &prop = pItem->GetActiveOption();
+
+ KeyValues *pProperty = pKeys->CreateNewKey();
+ pProperty->SetName( prop.szID );
+ pProperty->SetInt( "type", prop.nType );
+ pProperty->SetString( "valuestring", prop.szValue );
+ pProperty->SetString( "valuetype", prop.szValueType );
+ pProperty->SetInt( "optionindex", pItem->GetActiveOptionIndex() );
+ }
+
+ // Send contexts and properties parsed from the resource file
+ for ( int i = 0; i < m_SessionProperties.Count(); ++i )
+ {
+ const sessionProperty_t &prop = m_SessionProperties[i];
+
+ KeyValues *pProperty = pKeys->CreateNewKey();
+ pProperty->SetName( prop.szID );
+ pProperty->SetInt( "type", prop.nType );
+ pProperty->SetString( "valuestring", prop.szValue );
+ pProperty->SetString( "valuetype", prop.szValueType );
+ }
+
+ // Matchmaking will make a copy of these keys
+ matchmaking->SetSessionProperties( pKeys );
+ pKeys->deleteThis();
+}
+
+//-----------------------------------------------------------------
+// Purpose: Show the correct scenario image and text
+//-----------------------------------------------------------------
+void CSessionOptionsDialog::UpdateScenarioDisplay( void )
+{
+ // Check if the selected map has changed (first menu item)
+ int idx = m_Menu.GetActiveOptionIndex( 0 );
+ for ( int i = 0; i < m_pScenarioInfos.Count(); ++i )
+ {
+ m_pScenarioInfos[i]->SetVisible( i == idx );
+ }
+}
+
+//-----------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------
+void CSessionOptionsDialog::OnMenuItemChanged( KeyValues *pData )
+{
+ // which item changed
+ int iItem = pData->GetInt( "item", -1 );
+
+ if ( iItem >= 0 && iItem < m_Menu.GetItemCount() )
+ {
+ COptionsItem *pActiveOption = dynamic_cast< COptionsItem* >( m_Menu.GetItem( iItem ) );
+ if ( pActiveOption )
+ {
+ const sessionProperty_t &activeOption = pActiveOption->GetActiveOption();
+
+ if ( !Q_strncmp( activeOption.szID, "PROPERTY_GAME_SIZE", sessionProperty_t::MAX_KEY_LEN ) )
+ {
+ // make sure the private slots is less than prop.szValue
+
+ int iMaxPlayers = atoi(activeOption.szValue);
+ bool bShouldWarnMaxPlayers = ( pActiveOption->GetActiveOptionIndex() > GetMaxPlayersRecommendedOption() );
+ m_pRecommendedLabel->SetVisible( bShouldWarnMaxPlayers );
+
+ // find the private slots option and repopulate it
+ for ( int iMenu = 0; iMenu < m_Menu.GetItemCount(); ++iMenu )
+ {
+ COptionsItem *pItem = dynamic_cast< COptionsItem* >( m_Menu.GetItem( iMenu ) );
+ if ( !pItem )
+ {
+ continue;
+ }
+
+ const sessionProperty_t &prop = pItem->GetActiveOption();
+
+ if ( !Q_strncmp( prop.szID, "PROPERTY_PRIVATE_SLOTS", sessionProperty_t::MAX_KEY_LEN ) )
+ {
+ const sessionProperty_t baseProp = pItem->GetActiveOption();
+
+ // preserve the selection
+ int iActiveItem = pItem->GetActiveOptionIndex();
+
+ // clear all options
+ pItem->DeleteAllOptions();
+
+ // re-add the items 0 - maxplayers
+ int nStart = 0;
+ int nEnd = iMaxPlayers;
+ int nInterval = 1;
+
+ for ( int i = nStart; i <= nEnd; i += nInterval )
+ {
+ sessionProperty_t propNew;
+ propNew.nType = SESSION_PROPERTY;
+ Q_strncpy( propNew.szID, baseProp.szID, sizeof( propNew.szID ) );
+ Q_strncpy( propNew.szValueType, baseProp.szValueType, sizeof( propNew.szValueType ) );
+ Q_snprintf( propNew.szValue, sizeof( propNew.szValue), "%d", i );
+ pItem->AddOption( propNew.szValue, propNew );
+ }
+
+ // re-set the focus
+ pItem->SetOptionFocus( min( iActiveItem, iMaxPlayers ) );
+
+ // fixup the option sizes
+ m_Menu.InvalidateLayout();
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------
+// Purpose: Change properties of a menu item
+//-----------------------------------------------------------------
+void CSessionOptionsDialog::OverrideMenuItem( KeyValues *pItemKeys )
+{
+ if ( m_bModifySession && m_pDialogKeys )
+ {
+ if ( !Q_stricmp( pItemKeys->GetName(), "OptionsItem" ) )
+ {
+ const char *pID = pItemKeys->GetString( "id", "NULL" );
+
+ KeyValues *pKey = m_pDialogKeys->FindKey( pID );
+ if ( pKey )
+ {
+ pItemKeys->SetInt( "activeoption", pKey->GetInt( "optionindex" ) );
+ }
+ }
+ }
+
+ //
+ // When hosting a new session on LIVE:
+ // - restrict max number of players to bandwidth allowed
+ //
+ if ( !m_bModifySession &&
+ ( !Q_stricmp( m_szGametype, "hoststandard" ) || !Q_stricmp( m_szGametype, "hostranked" ) )
+ )
+ {
+ if ( !Q_stricmp( pItemKeys->GetName(), "OptionsItem" ) )
+ {
+ const char *pID = pItemKeys->GetString( "id", "NULL" );
+ if ( !Q_stricmp( pID, "PROPERTY_GAME_SIZE" ) )
+ {
+ pItemKeys->SetInt( "activeoption", GetMaxPlayersRecommendedOption() );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------
+// Purpose: Send key presses to the dialog's menu
+//-----------------------------------------------------------------
+void CSessionOptionsDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
+ {
+ OnCommand( m_szCommand );
+ }
+ else if ( (code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B) && m_bModifySession )
+ {
+ // Return to the session lobby without making any changes
+ OnCommand( "DialogClosing" );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+
+ UpdateScenarioDisplay();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSessionOptionsDialog::OnThink()
+{
+ vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
+ if ( code )
+ {
+ m_Menu.HandleKeyCode( code );
+ UpdateScenarioDisplay();
+ }
+
+ BaseClass::OnThink();
+}
+
+//---------------------------------------------------------------------
+// Purpose: Handle menu commands
+//---------------------------------------------------------------------
+void CSessionOptionsDialog::OnCommand( const char *pCommand )
+{
+ // Don't set up the session if the dialog is just closing
+ if ( Q_stricmp( pCommand, "DialogClosing" ) )
+ {
+ SetupSession();
+ OnClose();
+ }
+
+ GetParent()->OnCommand( pCommand );
+}
+
diff --git a/gameui/matchmaking/sessionoptionsdialog.h b/gameui/matchmaking/sessionoptionsdialog.h
new file mode 100644
index 0000000..69de2b3
--- /dev/null
+++ b/gameui/matchmaking/sessionoptionsdialog.h
@@ -0,0 +1,61 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Dialog for setting customizable game options
+//
+//=============================================================================//
+
+#ifndef SESSIONOPTIONSDIALOG_H
+#define SESSIONOPTIONSDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Simple menu to choose a matchmaking session type
+//-----------------------------------------------------------------------------
+class CSessionOptionsDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CSessionOptionsDialog, CBaseDialog );
+
+public:
+ CSessionOptionsDialog( vgui::Panel *parent );
+ ~CSessionOptionsDialog();
+
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+ virtual void OnThink();
+
+ virtual void PerformLayout( void );
+ virtual void ApplySettings( KeyValues *pResourceData );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void OverrideMenuItem( KeyValues *pKeys );
+
+ void SetGameType( const char *pGametype );
+ void SetDialogKeys( KeyValues *pKeys );
+ void ApplyCommonProperties( KeyValues *pKeys );
+
+ MESSAGE_FUNC_PARAMS( OnMenuItemChanged, "MenuItemChanged", data );
+
+private:
+ void SetupSession( void );
+ void UpdateScenarioDisplay( void );
+
+ int GetMaxPlayersRecommendedOption( void );
+
+ char m_szCommand[MAX_COMMAND_LEN];
+ char m_szGametype[MAX_COMMAND_LEN];
+ bool m_bModifySession;
+
+ KeyValues *m_pDialogKeys;
+
+ CUtlVector< sessionProperty_t > m_SessionProperties;
+ CUtlVector< CScenarioInfoPanel* > m_pScenarioInfos;
+
+ vgui::Label *m_pRecommendedLabel;
+};
+
+
+#endif // SESSIONOPTIONSDIALOG_H
diff --git a/gameui/matchmaking/welcomedialog.cpp b/gameui/matchmaking/welcomedialog.cpp
new file mode 100644
index 0000000..7249c70
--- /dev/null
+++ b/gameui/matchmaking/welcomedialog.cpp
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Matchmaking's "main menu"
+//
+//=============================================================================//
+
+#include "welcomedialog.h"
+#include "GameUI_Interface.h"
+#include "vgui_controls/MessageDialog.h"
+#include "ixboxsystem.h"
+#include "EngineInterface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------
+// CPlayerMatchDialog
+//--------------------------------
+CWelcomeDialog::CWelcomeDialog( vgui::Panel *pParent ) : BaseClass( pParent, "WelcomeDialog" )
+{
+ // do nothing
+}
+
+//---------------------------------------------------------
+// Purpose: Set the title and menu positions
+//---------------------------------------------------------
+void CWelcomeDialog::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------
+// Purpose: Forward commands to the matchmaking base panel
+//-----------------------------------------------------------------
+void CWelcomeDialog::OnCommand( const char *pCommand )
+{
+ BaseClass::OnCommand( pCommand );
+}
+
+//-------------------------------------------------------
+// Keyboard input
+//-------------------------------------------------------
+void CWelcomeDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ switch( code )
+ {
+ case KEY_XBUTTON_B:
+ if ( GameUI().IsInLevel() )
+ {
+ m_pParent->OnCommand( "ResumeGame" );
+ }
+ break;
+
+ default:
+ BaseClass::OnKeyCodePressed( code );
+ break;
+ }
+} \ No newline at end of file
diff --git a/gameui/matchmaking/welcomedialog.h b/gameui/matchmaking/welcomedialog.h
new file mode 100644
index 0000000..dbef44d
--- /dev/null
+++ b/gameui/matchmaking/welcomedialog.h
@@ -0,0 +1,34 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Matchmaking's "main menu"
+//
+//=============================================================================//
+
+#ifndef WELCOMEDIALOG_H
+#define WELCOMEDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialog.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Main matchmaking menu
+//-----------------------------------------------------------------------------
+class CWelcomeDialog : public CBaseDialog
+{
+ DECLARE_CLASS_SIMPLE( CWelcomeDialog, CBaseDialog );
+
+public:
+ CWelcomeDialog(vgui::Panel *parent);
+
+ virtual void PerformLayout( void );
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+
+private:
+ bool m_bOnlineEnabled;
+};
+
+
+#endif // WELCOMEDIALOG_H