diff options
Diffstat (limited to 'gameui/matchmaking/leaderboarddialog.cpp')
| -rw-r--r-- | gameui/matchmaking/leaderboarddialog.cpp | 563 |
1 files changed, 563 insertions, 0 deletions
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 ); + } +} |