summaryrefslogtreecommitdiff
path: root/gameui/matchmaking/leaderboarddialog.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gameui/matchmaking/leaderboarddialog.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'gameui/matchmaking/leaderboarddialog.cpp')
-rw-r--r--gameui/matchmaking/leaderboarddialog.cpp563
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 );
+ }
+}