diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gameui/matchmaking | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'gameui/matchmaking')
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 |