/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #pragma hdrstop #include "../../idLib/precompiled.h" #include "../Game_local.h" const static int NUM_LEADERBOARD_ITEMS = 16; const int MAX_STAT_LISTINGS = 16; static const int MAX_ROWS_PER_BLOCK = 50; idMenuScreen_Shell_Leaderboards::~idMenuScreen_Shell_Leaderboards() { if ( lbCache != NULL ) { delete lbCache; lbCache = NULL; } } // Helper functions for formatting leaderboard columns static idStr FormatTime( int64 time ) { int minutes = time / ( 1000 * 60 ); int seconds = ( time - ( minutes * 1000 * 60 ) ) / 1000; int mseconds = time - ( ( minutes * 1000 * 60 ) + ( seconds * 1000 ) ); return idStr( va( "%02d:%02d.%03d", minutes, seconds, mseconds ) ); } static idStr FormatCash( int64 cash ) { return idStr::FormatCash( static_cast( cash ) ); } static int32 FormatNumber( int64 number ) { return static_cast( number ); } static idSWFScriptVar FormatColumn( const columnDef_t * columnDef, int64 score ) { switch( columnDef->displayType ) { case STATS_COLUMN_DISPLAY_TIME_MILLISECONDS: return idSWFScriptVar( FormatTime( score ) ); case STATS_COLUMN_DISPLAY_CASH: return idSWFScriptVar( FormatCash( score ) ); default: return idSWFScriptVar( FormatNumber( score ) ); } } /* ======================== idMenuScreen_Shell_Leaderboards::Initialize ======================== */ void idMenuScreen_Shell_Leaderboards::Initialize( idMenuHandler * data ) { idMenuScreen::Initialize( data ); if ( data != NULL ) { menuGUI = data->GetGUI(); } lbCache = new idLBCache(); lbCache->Reset(); SetSpritePath( "menuLeaderboards" ); options = new (TAG_SWF) idMenuWidget_DynamicList(); options->SetNumVisibleOptions( NUM_LEADERBOARD_ITEMS ); options->SetSpritePath( GetSpritePath(), "info", "options" ); options->SetWrappingAllowed( true ); while ( options->GetChildren().Num() < NUM_LEADERBOARD_ITEMS ) { idMenuWidget_Button * const buttonWidget = new (TAG_SWF) idMenuWidget_Button(); buttonWidget->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_PRESS_FOCUSED, options->GetChildren().Num() ); buttonWidget->Initialize( data ); options->AddChild( buttonWidget ); } options->Initialize( data ); AddChild( options ); btnBack = new (TAG_SWF) idMenuWidget_Button(); btnBack->Initialize( data ); btnBack->SetLabel( "#str_swf_party_lobby" ); btnBack->SetSpritePath( GetSpritePath(), "info", "btnBack" ); btnBack->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_GO_BACK ); AddChild( btnBack ); btnNext = new (TAG_SWF) idMenuWidget_Button(); btnNext->Initialize( data ); btnNext->SetLabel( "#str_swf_next" ); btnNext->SetSpritePath( GetSpritePath(), "info", "btnNext" ); btnNext->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) ); AddChild( btnNext ); btnPrev = new (TAG_SWF) idMenuWidget_Button(); btnPrev->Initialize( data ); btnPrev->SetLabel( "#str_swf_prev" ); btnPrev->SetSpritePath( GetSpritePath(), "info", "btnPrevious" ); btnPrev->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) ); AddChild( btnPrev ); btnPageDwn = new (TAG_SWF) idMenuWidget_Button(); btnPageDwn->Initialize( data ); btnPageDwn->SetLabel( "#str_swf_next_page" ); btnPageDwn->SetSpritePath( GetSpritePath(), "info", "btnPageDwn" ); idSWFParmList parms; parms.Append( MAX_STAT_LISTINGS - 1 ); btnPageDwn->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) ); btnPageDwn->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) ); AddChild( btnPageDwn ); btnPageUp = new (TAG_SWF) idMenuWidget_Button(); btnPageUp->Initialize( data ); btnPageUp->SetLabel( "#str_swf_prev_page" ); btnPageUp->SetSpritePath( GetSpritePath(), "info", "btnPageUp" ); parms.Clear(); parms.Append( MAX_STAT_LISTINGS - 1 ); btnPageUp->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) ); btnPageUp->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) ); AddChild( btnPageUp ); AddEventAction( WIDGET_EVENT_SCROLL_DOWN_LSTICK ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_DOWN_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_DOWN_LSTICK ) ); AddEventAction( WIDGET_EVENT_SCROLL_UP_LSTICK ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_UP_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_UP_LSTICK ) ); AddEventAction( WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ) ); AddEventAction( WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ) ); AddEventAction( WIDGET_EVENT_SCROLL_DOWN ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_DOWN_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_DOWN ) ); AddEventAction( WIDGET_EVENT_SCROLL_UP ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_UP_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_UP ) ); AddEventAction( WIDGET_EVENT_SCROLL_DOWN_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_RELEASE ) ); AddEventAction( WIDGET_EVENT_SCROLL_UP_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_RELEASE ) ); AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) ); AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) ); AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) ); AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) ); AddEventAction( WIDGET_EVENT_TAB_NEXT ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) ); AddEventAction( WIDGET_EVENT_TAB_PREV ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) ); leaderboards.Clear(); const idList< mpMap_t > maps = common->GetMapList(); const char ** gameModes = NULL; const char ** gameModesDisplay = NULL; int numModes = game->GetMPGameModes( &gameModes, &gameModesDisplay ); for ( int mapIndex = 0; mapIndex < maps.Num(); ++mapIndex ) { for ( int modeIndex = 0; modeIndex < numModes; ++modeIndex ) { // Check the supported modes on the map. if( maps[ mapIndex ].supportedModes & BIT( modeIndex ) ) { int boardID = LeaderboardLocal_GetID( mapIndex, modeIndex ); const leaderboardDefinition_t * lbDef = Sys_FindLeaderboardDef( boardID ); if ( lbDef != NULL ) { doomLeaderboard_t lb = doomLeaderboard_t( lbDef, lbDef->boardName ); leaderboards.Append( lb ); } } } } } /* ======================== idMenuScreen_Shell_Leaderboards::PumpLBCache ======================== */ void idMenuScreen_Shell_Leaderboards::PumpLBCache() { if ( lbCache == NULL ) { return; } lbCache->Pump(); } /* ======================== idMenuScreen_Shell_Leaderboards::ClearLeaderboard ======================== */ void idMenuScreen_Shell_Leaderboards::ClearLeaderboard() { if ( lbCache == NULL ) { return; } lbCache->Reset(); } /* ======================== idMenuScreen_Shell_Leaderboards::Update ======================== */ void idMenuScreen_Shell_Leaderboards::Update() { if ( menuData != NULL ) { idMenuWidget_CommandBar * cmdBar = menuData->GetCmdBar(); if ( cmdBar != NULL ) { cmdBar->ClearAllButtons(); idMenuWidget_CommandBar::buttonInfo_t * buttonInfo; buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY2 ); if ( menuData->GetPlatform() != 2 ) { buttonInfo->label = "#str_00395"; } buttonInfo->action.Set( WIDGET_ACTION_GO_BACK ); buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY3 ); buttonInfo->label = "#str_online_leaderboards_toggle_filter"; buttonInfo->action.Set( WIDGET_ACTION_JOY3_ON_PRESS ); if ( !lbCache->IsLoadingNewLeaderboard() && !lbCache->IsRequestingRows() && options != NULL && options->GetTotalNumberOfOptions() > 0 ) { buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY1 ); if ( menuData->GetPlatform() != 2 ) { buttonInfo->label = "#str_swf_view_profile"; } buttonInfo->action.Set( WIDGET_ACTION_PRESS_FOCUSED ); } } } idSWFScriptObject & root = GetSWFObject()->GetRootObject(); if ( BindSprite( root ) ) { idSWFTextInstance * heading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtHeading" ); if ( heading != NULL ) { heading->SetText( lbCache->GetFilterStrType() ); heading->SetStrokeInfo( true, 0.75f, 1.75f ); } idSWFSpriteInstance * gradient = GetSprite()->GetScriptObject()->GetNestedSprite( "info", "gradient" ); if ( gradient != NULL && heading != NULL ) { gradient->SetXPos( heading->GetTextLength() ); } } if ( btnBack != NULL ) { btnBack->BindSprite( root ); } idMenuScreen::Update(); } /* ======================== idMenuScreen_Shell_Leaderboards::ShowScreen ======================== */ void idMenuScreen_Shell_Leaderboards::ShowScreen( const mainMenuTransition_t transitionType ) { idMenuScreen::ShowScreen( transitionType ); if ( GetSprite() != NULL ) { lbHeading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtLbType" ); if ( menuData != NULL && menuData->GetGUI() != NULL ) { idSWFScriptObject * const shortcutKeys = menuData->GetGUI()->GetGlobal( "shortcutKeys" ).GetObject(); if ( verify( shortcutKeys != NULL ) ) { // TAB NEXT idSWFScriptObject * const btnTabNext = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnNext" ); if ( btnTabNext != NULL ) { btnTabNext->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_NEXT, 0 ) ); shortcutKeys->Set( "JOY6", btnTabNext ); if ( btnTabNext->GetSprite() != NULL && menuData != NULL ) { btnTabNext->GetSprite()->StopFrame( menuData->GetPlatform() + 1 ); } } // TAB PREV idSWFScriptObject * const btnTabPrev = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPrevious" ); if ( btnTabPrev != NULL ) { btnTabPrev->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_PREV, 0 ) ); shortcutKeys->Set( "JOY5", btnTabPrev ); if ( btnTabPrev->GetSprite() != NULL && menuData != NULL ) { btnTabPrev->GetSprite()->StopFrame( menuData->GetPlatform() + 1 ); } } // TAB NEXT idSWFScriptObject * const btnDwn = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageDwn" ); if ( btnDwn != NULL ) { btnDwn->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEDWN, 0 ) ); shortcutKeys->Set( "JOY_TRIGGER2", btnDwn ); if ( btnDwn->GetSprite() != NULL && menuData != NULL ) { btnDwn->GetSprite()->StopFrame( menuData->GetPlatform() + 1 ); } } // TAB PREV idSWFScriptObject * const btnUp = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageUp" ); if ( btnUp != NULL ) { btnUp->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEUP, 0 ) ); shortcutKeys->Set( "JOY_TRIGGER1", btnUp ); if ( btnUp->GetSprite() != NULL && menuData != NULL ) { btnUp->GetSprite()->StopFrame( menuData->GetPlatform() + 1 ); } } } } } SetLeaderboardIndex(); if ( menuData == NULL ) { return; } int platform = menuData->GetPlatform(); if ( btnNext != NULL && btnNext->GetSprite() != NULL ) { idSWFSpriteInstance * btnImg = btnNext->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" ); if ( btnImg != NULL ) { if ( platform == 2 ) { btnImg->SetVisible( false ); } else { btnImg->SetVisible( true ); btnImg->StopFrame( platform + 1 ); } } } if ( btnPrev != NULL && btnPrev->GetSprite() != NULL ) { idSWFSpriteInstance * btnImg = btnPrev->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" ); if ( btnImg != NULL ) { if ( platform == 2 ) { btnImg->SetVisible( false ); } else { btnImg->SetVisible( true ); btnImg->StopFrame( platform + 1 ); } } } if ( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL ) { idSWFSpriteInstance * btnImg = btnPageDwn->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" ); if ( btnImg != NULL ) { if ( platform == 2 ) { btnImg->SetVisible( false ); } else { btnImg->SetVisible( true ); btnImg->StopFrame( platform + 1 ); } } } if ( btnPageUp != NULL && btnPageUp->GetSprite() != NULL ) { idSWFSpriteInstance * btnImg = btnPageUp->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" ); if ( btnImg != NULL ) { if ( platform == 2 ) { btnImg->SetVisible( false ); } else { btnImg->SetVisible( true ); btnImg->StopFrame( platform + 1 ); } } } } /* ======================== idMenuScreen_Shell_Leaderboards::HideScreen ======================== */ void idMenuScreen_Shell_Leaderboards::HideScreen( const mainMenuTransition_t transitionType ) { idMenuScreen::HideScreen( transitionType ); } /* ======================== idMenuScreen_Shell_Leaderboards::HandleAction ======================== */ bool idMenuScreen_Shell_Leaderboards::HandleAction( idWidgetAction & action, const idWidgetEvent & event, idMenuWidget * widget, bool forceHandled ) { if ( menuData == NULL ) { return true; } if ( menuData->ActiveScreen() != SHELL_AREA_LEADERBOARDS ) { return false; } widgetAction_t actionType = action.GetType(); const idSWFParmList & parms = action.GetParms(); switch ( actionType ) { case WIDGET_ACTION_GO_BACK: { menuData->SetNextScreen( SHELL_AREA_PARTY_LOBBY, MENU_TRANSITION_SIMPLE ); return true; } case WIDGET_ACTION_JOY3_ON_PRESS: { lbCache->CycleFilter(); refreshLeaderboard = true; return true; } case WIDGET_ACTION_PRESS_FOCUSED: { if ( options == NULL ) { return true; } int index = options->GetFocusIndex(); if ( parms.Num() != 0 ) { index = parms[0].ToInteger(); } if ( lbCache->GetEntryIndex() != index ) { lbCache->SetEntryIndex( index ); refreshLeaderboard = true; return true; } const idLeaderboardCallback::row_t * row = lbCache->GetLeaderboardRow( lbCache->GetRowOffset() + lbCache->GetEntryIndex() ); if ( row != NULL ) { lbCache->DisplayGamerCardUI( row ); } return true; } case WIDGET_ACTION_SCROLL_TAB: { int delta = parms[0].ToInteger(); lbIndex += delta; SetLeaderboardIndex(); return true; } case WIDGET_ACTION_SCROLL_VERTICAL_VARIABLE: { if ( parms.Num() == 0 ) { return true; } if ( options == NULL ) { return true; } int dir = parms[ 0 ].ToInteger(); if ( lbCache->Scroll( dir ) ) { // TODO_SPARTY: play scroll sound refreshLeaderboard = true; } return true; } case WIDGET_ACTION_SCROLL_PAGE: { if ( parms.Num() == 0 ) { return true; } if ( options == NULL ) { return true; } int dir = parms[ 0 ].ToInteger(); if ( lbCache->ScrollOffset( dir ) ) { refreshLeaderboard = true; } return true; } } return idMenuWidget::HandleAction( action, event, widget, forceHandled ); } /* ======================== idMenuScreen_Shell_Leaderboards::UpdateLeaderboard ======================== */ void idMenuScreen_Shell_Leaderboards::UpdateLeaderboard( const idLeaderboardCallback * callback ) { lbCache->Update( callback ); if ( callback->GetErrorCode() != LEADERBOARD_ERROR_NONE ) { if ( !session->GetSignInManager().IsMasterLocalUserOnline() ) { refreshWhenMasterIsOnline = true; } } refreshLeaderboard = true; } /* ======================== idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex ======================== */ void idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex() { if ( lbIndex >= leaderboards.Num() ) { lbIndex = 0; } else if ( lbIndex < 0 ) { lbIndex = leaderboards.Num() - 1; } const leaderboardDefinition_t * leaderboardDef = leaderboards[ lbIndex ].lb; for ( int i = 0; i < leaderboardDef->numColumns; i++ ) { /*if ( leaderboardDef->columnDefs[i].displayType != STATS_COLUMN_NEVER_DISPLAY ) { gameLocal->GetMainMenu()->mainMenu->SetGlobal( va("columnname%d",i), leaderboardDef->columnDefs[i].locDisplayName ); }*/ } lbCache->SetLeaderboard( leaderboardDef, lbCache->GetFilter() ); refreshLeaderboard = true; } /* ======================== idMenuScreen_Shell_Leaderboards::RefreshLeaderboard ======================== */ void idMenuScreen_Shell_Leaderboards::RefreshLeaderboard() { if ( refreshWhenMasterIsOnline ) { SetLeaderboardIndex(); refreshWhenMasterIsOnline = false; } if ( !refreshLeaderboard ) { return; } refreshLeaderboard = false; bool upArrow = false; bool downArrow = false; int focusIndex = -1; idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > lbListings; if ( !lbCache->IsLoadingNewLeaderboard() && lbCache->GetErrorCode() == LEADERBOARD_DISPLAY_ERROR_NONE ) { for ( int addIndex = 0; addIndex < MAX_STAT_LISTINGS; ++addIndex ) { idList< idStr > values; int index = lbCache->GetRowOffset() + addIndex; const idLeaderboardCallback::row_t * row = lbCache->GetLeaderboardRow( index ); // If this row is not in the cache, this will kick off a request if ( row != NULL ) { values.Append( va( "%i", (int)row->rank ) ); values.Append( row->name ); values.Append( FormatColumn( &lbCache->GetLeaderboard()->columnDefs[0], row->columns[0] ).ToString() ); } if ( lbCache->GetEntryIndex() == addIndex ) { focusIndex = addIndex; } lbListings.Append( values ); } if ( lbCache->GetRowOffset() != 0 ) { upArrow = true; } if ( ( lbCache->GetRowOffset() + MAX_STAT_LISTINGS ) < lbCache->GetNumRowsInLeaderboard() ) { downArrow = true; } } if ( lbHeading != NULL ) { lbHeading->SetText( leaderboards[ lbIndex ].name ); lbHeading->SetStrokeInfo( true, 0.75f, 1.75f ); } if ( focusIndex >= 0 ) { options->SetFocusIndex( focusIndex ); } if ( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL ) { btnPageDwn->GetSprite()->SetVisible( downArrow ); } if ( btnPageUp != NULL && btnPageUp->GetSprite() != NULL ) { btnPageUp->GetSprite()->SetVisible( upArrow ); } options->SetListData( lbListings ); Update(); const char * leaderboardErrorStrings[] = { "", "#str_online_leaderboards_error_failed", // failed "", // not online - players are just taken back to multiplayer menu "#str_online_leaderboards_error_not_ranked", // not ranked }; compile_time_assert( sizeof( leaderboardErrorStrings ) / sizeof( leaderboardErrorStrings[0] ) == LEADERBOARD_DISPLAY_ERROR_MAX ); bool isLoading = lbCache->IsLoadingNewLeaderboard(); idStr error = leaderboardErrorStrings[ lbCache->GetErrorCode() ]; if ( isLoading ) { ShowMessage( true, "#str_online_loading", true ); } else { if ( !error.IsEmpty() ) { ShowMessage( true, error, false ); } else { if ( lbCache->GetNumRowsInLeaderboard() > 0 ) { ShowMessage( false, "", false ); } else { ShowMessage( true, "#str_online_leaderboards_no_data", false ); } } } } /* ======================== idMenuScreen_Shell_Leaderboards::ShowMessage ======================== */ void idMenuScreen_Shell_Leaderboards::ShowMessage( bool show, idStr message, bool spinner ) { if ( !menuData || !menuData->GetGUI() ) { return; } idSWFSpriteInstance * pacifier = menuData->GetGUI()->GetRootObject().GetNestedSprite( "menuLeaderboards", "info", "pacifier" ); if ( !pacifier ) { return; } if ( show ) { if ( spinner && options != NULL && options->GetSprite() != NULL ) { options->GetSprite()->SetAlpha( 0.35f ); } else if ( options != NULL && options->GetSprite() != NULL ) { options->GetSprite()->SetVisible( false ); } pacifier->SetVisible( true ); idSWFTextInstance * txtMsg = pacifier->GetScriptObject()->GetNestedText( "message" ); if ( txtMsg != NULL ) { txtMsg->SetText( message ); txtMsg->SetStrokeInfo( true, 0.75f, 1.75f ); } idSWFSpriteInstance * spriteSpinner = pacifier->GetScriptObject()->GetNestedSprite( "graphic" ); if ( spriteSpinner != NULL ) { spriteSpinner->StopFrame( spinner ? 1 : 2 ); } } else { if ( options != NULL && options->GetSprite() != NULL ) { options->GetSprite()->SetVisible( true ); options->GetSprite()->SetAlpha( 1.0f ); } pacifier->SetVisible( false ); } } //************************************************************************************************************************* // LBCACHE //************************************************************************************************************************* class LBCallback : public idLeaderboardCallback { public: LBCallback() {} void Call() { gameLocal.Shell_UpdateLeaderboard( this ); } LBCallback * Clone() const { return new LBCallback( *this ); } }; /* ======================== idLBCache::Pump ======================== */ void idLBCache::Pump() { if ( loadingNewLeaderboard || requestingRows ) { return; } if ( pendingDef != NULL ) { SetLeaderboard( pendingDef, pendingFilter ); } } /* ======================== idLBCache::Reset ======================== */ void idLBCache::Reset() { for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) { rowBlocks[i].startIndex = 0; rowBlocks[i].rows.Clear(); } def = NULL; filter = DEFAULT_LEADERBOARD_FILTER; pendingDef = NULL; pendingFilter = DEFAULT_LEADERBOARD_FILTER; rowOffset = 0; requestingRows = false; numRowsInLeaderboard = 0; entryIndex = 0; loadingNewLeaderboard = false; } /* ======================== idLBCache::SetLeaderboard ======================== */ void idLBCache::SetLeaderboard( const leaderboardDefinition_t * def_, leaderboardFilterMode_t filter_ ) { // If we are busy waiting on results from a previous request, queue up this request if ( loadingNewLeaderboard || requestingRows ) { pendingDef = def_; pendingFilter = filter_; return; } //idLib::Printf( "SetLeaderboard 0x%p.\n", def_ ); // Reset all Reset(); // Set leaderboard and filter def = def_; filter = filter_; loadingNewLeaderboard = true; // This means we are waiting on the first set of results for this new leaderboard localIndex = -1; // don't know where the user is in the rows yet // Kick off initial stats request (which is initially based on the filter type) if ( filter == LEADERBOARD_FILTER_MYSCORE ) { LBCallback cb; session->LeaderboardDownload( 0, def, 0, MAX_ROWS_PER_BLOCK, cb ); } else if ( filter == LEADERBOARD_FILTER_FRIENDS ) { LBCallback cb; session->LeaderboardDownload( 0, def, -1, 100, cb ); // Request up to 100 friends } else { LBCallback cb; session->LeaderboardDownload( 0, def, rowOffset + 1, MAX_ROWS_PER_BLOCK, cb ); //session->LeaderboardDownload( 0, def, rowOffset + 1, 10, cb ); // For testing } } /* ======================== idLBCache::CycleFilter ======================== */ void idLBCache::CycleFilter() { // Set the proper filter if ( filter == LEADERBOARD_FILTER_OVERALL ) { filter = LEADERBOARD_FILTER_MYSCORE; } else if ( filter == LEADERBOARD_FILTER_MYSCORE ) { filter = LEADERBOARD_FILTER_FRIENDS; } else { filter = LEADERBOARD_FILTER_OVERALL; } // Reset the leaderboard with the new filter SetLeaderboard( def, filter ); } /* ======================== idLBCache::GetFilterStrType ======================== */ idStr idLBCache::GetFilterStrType() { if ( filter == LEADERBOARD_FILTER_FRIENDS ) { return idLocalization::GetString( "#str_swf_leaderboards_friends_heading" ); } else if ( filter == LEADERBOARD_FILTER_MYSCORE ) { return idLocalization::GetString( "#str_swf_leaderboards_global_self_heading" ); } return idLocalization::GetString( "#str_swf_leaderboards_global_heading" ); } /* ======================== idLBCache::Scroll ======================== */ bool idLBCache::Scroll( int amount ) { if ( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE ) { return false; // don't allow scrolling on errors } // Remember old offsets so we know if anything moved int oldEntryIndex = entryIndex; int oldRowOffset = rowOffset; // Move cursor index by scroll amount entryIndex += amount; // Clamp cursor index (scrolling row offset if we can) if ( entryIndex < 0 ) { rowOffset += entryIndex; entryIndex = 0; } else if ( entryIndex >= numRowsInLeaderboard ) { entryIndex = numRowsInLeaderboard - 1; rowOffset = entryIndex - ( MAX_STAT_LISTINGS - 1 ); } else if ( entryIndex >= MAX_STAT_LISTINGS ) { rowOffset += entryIndex - ( MAX_STAT_LISTINGS - 1 ); entryIndex = MAX_STAT_LISTINGS - 1; } // Clamp row offset rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset ); // Let caller know if anything actually changed return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset ); } /* ======================== idLBCache::ScrollOffset ======================== */ bool idLBCache::ScrollOffset( int amount ) { if ( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE ) { return false; // don't allow scrolling on errors } // Remember old offsets so we know if anything moved int oldEntryIndex = entryIndex; int oldRowOffset = rowOffset; rowOffset += amount; // Clamp row offset rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset ); if ( rowOffset != oldRowOffset ) { entryIndex -= amount; // adjust in opposite direction so same item stays selected entryIndex = idMath::ClampInt( 0, rowOffset + ( MAX_STAT_LISTINGS - 1 ), entryIndex ); } else { entryIndex += amount; entryIndex = idMath::ClampInt( 0, numRowsInLeaderboard - 1, entryIndex ); } // Let caller know if anything actually changed return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset ); } /* ======================== idLBCache::FindFreeRowBlock ======================== */ idLBRowBlock * idLBCache::FindFreeRowBlock() { int bestTime = 0; int bestBlockIndex = 0; for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) { if ( rowBlocks[i].rows.Num() == 0 ) { return &rowBlocks[i]; // Prefer completely empty blocks } // Search for oldest block in the mean time if ( i == 0 || rowBlocks[i].lastTime < bestTime ) { bestBlockIndex = i; bestTime = rowBlocks[i].lastTime; } } return &rowBlocks[bestBlockIndex]; } /* ======================== idLBCache::CallbackErrorToDisplayError ======================== */ leaderboardDisplayError_t idLBCache::CallbackErrorToDisplayError( leaderboardError_t errorCode ) { switch ( errorCode ) { case LEADERBOARD_ERROR_NONE: return LEADERBOARD_DISPLAY_ERROR_NONE; default: return LEADERBOARD_DISPLAY_ERROR_FAILED; } } /* ======================== idLBCache::Update ======================== */ void idLBCache::Update( const idLeaderboardCallback * callback ) { requestingRows = false; //idLib::Printf( "Stats returned.\n" ); errorCode = CallbackErrorToDisplayError( callback->GetErrorCode() ); // check if trying to view "My Score" leaderboard when you aren't ranked yet if ( loadingNewLeaderboard && filter == LEADERBOARD_FILTER_MYSCORE && callback->GetLocalIndex() == -1 && errorCode == LEADERBOARD_DISPLAY_ERROR_NONE ) { errorCode = LEADERBOARD_DISPLAY_ERROR_NOT_RANKED; } if ( errorCode != LEADERBOARD_DISPLAY_ERROR_NONE ) { numRowsInLeaderboard = 0; loadingNewLeaderboard = false; switch ( errorCode ) { case LEADERBOARD_DISPLAY_ERROR_NOT_ONLINE: /*idMenuHandler_Shell * shell = gameLocal.Shell_GetHandler(); if ( shell != NULL ) { shell->SetNextScreen( SHELL_AREA_ROOT, MENU_TRANSITION_SIMPLE ); }*/ common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, true, "", 0, true ); break; default: break; } return; } if ( callback->GetDef() != def ) { // Not the leaderboard we are looking for (This should no longer be possible) idLib::Printf( "Wrong leaderboard.\n" ); numRowsInLeaderboard = 0; loadingNewLeaderboard = false; return; } // Get total rows in this leaderboard numRowsInLeaderboard = callback->GetNumRowsInLeaderboard(); // Store the index that the master user is in, if we haven't already found the index if ( callback->GetLocalIndex() != -1 ) { localIndex = callback->GetStartIndex() + callback->GetLocalIndex(); } if ( loadingNewLeaderboard == true ) { // Default to viewing the local user (if the right filter mode is set) if ( callback->GetLocalIndex() != -1 && ( filter == LEADERBOARD_FILTER_MYSCORE || filter == LEADERBOARD_FILTER_FRIENDS ) ) { // Put their name and cursor at the top rowOffset = callback->GetLocalIndex() + callback->GetStartIndex(); entryIndex = 0; // Scroll the cursor up to center their name as much as possible Scroll( -MAX_STAT_LISTINGS / 2 ); // Set the cursor to their name entryIndex = ( callback->GetLocalIndex() + callback->GetStartIndex() ) - rowOffset; } loadingNewLeaderboard = false; } // Find a a row block to store these new rows idLBRowBlock * rowBlock = FindFreeRowBlock(); rowBlock->lastTime = Sys_Milliseconds(); // Freshen row rowBlock->startIndex = callback->GetStartIndex(); rowBlock->rows = callback->GetRows(); } /* ======================== idLBCache::GetLeaderboardRow ======================== */ const idLeaderboardCallback::row_t * idLBCache::GetLeaderboardRow( int row ) { if ( loadingNewLeaderboard ) { return NULL; // If we are refreshing (seeing this leaderboard for the first time), force NULL till we get first set of results } if ( row >= numRowsInLeaderboard ) { return NULL; } // Find it in the cache for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) { int startIndex = rowBlocks[i].startIndex; int lastIndex = startIndex + rowBlocks[i].rows.Num() - 1; if ( row >= startIndex && row <= lastIndex ) { rowBlocks[i].lastTime = Sys_Milliseconds(); // Freshen row return &rowBlocks[i].rows[row - startIndex]; } } // Not found, kick off a request to download it // (this will not allow more than one request at a time) if ( !requestingRows ) { // If we don't have this row, kick off a request to get it LBCallback cb; requestingRows = true; session->LeaderboardDownload( 0, def, row + 1, MAX_ROWS_PER_BLOCK, cb ); //session->LeaderboardDownload( 0, def, row + 1, 10, cb ); //idLib::Printf( "Stat request\n" ); } return NULL; } /* ======================== idMainMenu::SetSPLeaderboardFromMenuSettings ======================== */ void idLBCache::DisplayGamerCardUI( const idLeaderboardCallback::row_t * row ) { }