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