MenuScreen_Shell_Leaderboards.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../../idLib/precompiled.h"
  22. #include "../Game_local.h"
  23. const static int NUM_LEADERBOARD_ITEMS = 16;
  24. const int MAX_STAT_LISTINGS = 16;
  25. static const int MAX_ROWS_PER_BLOCK = 50;
  26. idMenuScreen_Shell_Leaderboards::~idMenuScreen_Shell_Leaderboards() {
  27. if ( lbCache != NULL ) {
  28. delete lbCache;
  29. lbCache = NULL;
  30. }
  31. }
  32. // Helper functions for formatting leaderboard columns
  33. static idStr FormatTime( int64 time ) {
  34. int minutes = time / ( 1000 * 60 );
  35. int seconds = ( time - ( minutes * 1000 * 60 ) ) / 1000;
  36. int mseconds = time - ( ( minutes * 1000 * 60 ) + ( seconds * 1000 ) );
  37. return idStr( va( "%02d:%02d.%03d", minutes, seconds, mseconds ) );
  38. }
  39. static idStr FormatCash( int64 cash ) { return idStr::FormatCash( static_cast<int32>( cash ) ); }
  40. static int32 FormatNumber( int64 number ) { return static_cast<int32>( number ); }
  41. static idSWFScriptVar FormatColumn( const columnDef_t * columnDef, int64 score ) {
  42. switch( columnDef->displayType ) {
  43. case STATS_COLUMN_DISPLAY_TIME_MILLISECONDS: return idSWFScriptVar( FormatTime( score ) );
  44. case STATS_COLUMN_DISPLAY_CASH: return idSWFScriptVar( FormatCash( score ) );
  45. default: return idSWFScriptVar( FormatNumber( score ) );
  46. }
  47. }
  48. /*
  49. ========================
  50. idMenuScreen_Shell_Leaderboards::Initialize
  51. ========================
  52. */
  53. void idMenuScreen_Shell_Leaderboards::Initialize( idMenuHandler * data ) {
  54. idMenuScreen::Initialize( data );
  55. if ( data != NULL ) {
  56. menuGUI = data->GetGUI();
  57. }
  58. lbCache = new idLBCache();
  59. lbCache->Reset();
  60. SetSpritePath( "menuLeaderboards" );
  61. options = new (TAG_SWF) idMenuWidget_DynamicList();
  62. options->SetNumVisibleOptions( NUM_LEADERBOARD_ITEMS );
  63. options->SetSpritePath( GetSpritePath(), "info", "options" );
  64. options->SetWrappingAllowed( true );
  65. while ( options->GetChildren().Num() < NUM_LEADERBOARD_ITEMS ) {
  66. idMenuWidget_Button * const buttonWidget = new (TAG_SWF) idMenuWidget_Button();
  67. buttonWidget->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_PRESS_FOCUSED, options->GetChildren().Num() );
  68. buttonWidget->Initialize( data );
  69. options->AddChild( buttonWidget );
  70. }
  71. options->Initialize( data );
  72. AddChild( options );
  73. btnBack = new (TAG_SWF) idMenuWidget_Button();
  74. btnBack->Initialize( data );
  75. btnBack->SetLabel( "#str_swf_party_lobby" );
  76. btnBack->SetSpritePath( GetSpritePath(), "info", "btnBack" );
  77. btnBack->AddEventAction( WIDGET_EVENT_PRESS ).Set( WIDGET_ACTION_GO_BACK );
  78. AddChild( btnBack );
  79. btnNext = new (TAG_SWF) idMenuWidget_Button();
  80. btnNext->Initialize( data );
  81. btnNext->SetLabel( "#str_swf_next" );
  82. btnNext->SetSpritePath( GetSpritePath(), "info", "btnNext" );
  83. btnNext->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) );
  84. AddChild( btnNext );
  85. btnPrev = new (TAG_SWF) idMenuWidget_Button();
  86. btnPrev->Initialize( data );
  87. btnPrev->SetLabel( "#str_swf_prev" );
  88. btnPrev->SetSpritePath( GetSpritePath(), "info", "btnPrevious" );
  89. btnPrev->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) );
  90. AddChild( btnPrev );
  91. btnPageDwn = new (TAG_SWF) idMenuWidget_Button();
  92. btnPageDwn->Initialize( data );
  93. btnPageDwn->SetLabel( "#str_swf_next_page" );
  94. btnPageDwn->SetSpritePath( GetSpritePath(), "info", "btnPageDwn" );
  95. idSWFParmList parms;
  96. parms.Append( MAX_STAT_LISTINGS - 1 );
  97. btnPageDwn->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) );
  98. btnPageDwn->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) );
  99. AddChild( btnPageDwn );
  100. btnPageUp = new (TAG_SWF) idMenuWidget_Button();
  101. btnPageUp->Initialize( data );
  102. btnPageUp->SetLabel( "#str_swf_prev_page" );
  103. btnPageUp->SetSpritePath( GetSpritePath(), "info", "btnPageUp" );
  104. parms.Clear();
  105. parms.Append( MAX_STAT_LISTINGS - 1 );
  106. btnPageUp->AddEventAction( WIDGET_EVENT_PRESS ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) );
  107. btnPageUp->AddEventAction( WIDGET_EVENT_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) );
  108. AddChild( btnPageUp );
  109. 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 ) );
  110. 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 ) );
  111. AddEventAction( WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_LSTICK_RELEASE ) );
  112. AddEventAction( WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_LSTICK_RELEASE ) );
  113. AddEventAction( WIDGET_EVENT_SCROLL_DOWN ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_DOWN_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_DOWN ) );
  114. AddEventAction( WIDGET_EVENT_SCROLL_UP ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_UP_START_REPEATER_VARIABLE, WIDGET_EVENT_SCROLL_UP ) );
  115. AddEventAction( WIDGET_EVENT_SCROLL_DOWN_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_DOWN_RELEASE ) );
  116. AddEventAction( WIDGET_EVENT_SCROLL_UP_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_UP_RELEASE ) );
  117. AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_DOWN_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN ) );
  118. AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_SCROLL_PAGE_UP_START_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP ) );
  119. AddEventAction( WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEDWN_RELEASE ) );
  120. AddEventAction( WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_STOP_REPEATER, WIDGET_EVENT_SCROLL_PAGEUP_RELEASE ) );
  121. AddEventAction( WIDGET_EVENT_TAB_NEXT ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_NEXT, WIDGET_EVENT_TAB_NEXT ) );
  122. AddEventAction( WIDGET_EVENT_TAB_PREV ).Set( new (TAG_SWF) idWidgetActionHandler( this, WIDGET_ACTION_EVENT_TAB_PREV, WIDGET_EVENT_TAB_PREV ) );
  123. leaderboards.Clear();
  124. const idList< mpMap_t > maps = common->GetMapList();
  125. const char ** gameModes = NULL;
  126. const char ** gameModesDisplay = NULL;
  127. int numModes = game->GetMPGameModes( &gameModes, &gameModesDisplay );
  128. for ( int mapIndex = 0; mapIndex < maps.Num(); ++mapIndex ) {
  129. for ( int modeIndex = 0; modeIndex < numModes; ++modeIndex ) {
  130. // Check the supported modes on the map.
  131. if( maps[ mapIndex ].supportedModes & BIT( modeIndex ) ) {
  132. int boardID = LeaderboardLocal_GetID( mapIndex, modeIndex );
  133. const leaderboardDefinition_t * lbDef = Sys_FindLeaderboardDef( boardID );
  134. if ( lbDef != NULL ) {
  135. doomLeaderboard_t lb = doomLeaderboard_t( lbDef, lbDef->boardName );
  136. leaderboards.Append( lb );
  137. }
  138. }
  139. }
  140. }
  141. }
  142. /*
  143. ========================
  144. idMenuScreen_Shell_Leaderboards::PumpLBCache
  145. ========================
  146. */
  147. void idMenuScreen_Shell_Leaderboards::PumpLBCache() {
  148. if ( lbCache == NULL ) {
  149. return;
  150. }
  151. lbCache->Pump();
  152. }
  153. /*
  154. ========================
  155. idMenuScreen_Shell_Leaderboards::ClearLeaderboard
  156. ========================
  157. */
  158. void idMenuScreen_Shell_Leaderboards::ClearLeaderboard() {
  159. if ( lbCache == NULL ) {
  160. return;
  161. }
  162. lbCache->Reset();
  163. }
  164. /*
  165. ========================
  166. idMenuScreen_Shell_Leaderboards::Update
  167. ========================
  168. */
  169. void idMenuScreen_Shell_Leaderboards::Update() {
  170. if ( menuData != NULL ) {
  171. idMenuWidget_CommandBar * cmdBar = menuData->GetCmdBar();
  172. if ( cmdBar != NULL ) {
  173. cmdBar->ClearAllButtons();
  174. idMenuWidget_CommandBar::buttonInfo_t * buttonInfo;
  175. buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY2 );
  176. if ( menuData->GetPlatform() != 2 ) {
  177. buttonInfo->label = "#str_00395";
  178. }
  179. buttonInfo->action.Set( WIDGET_ACTION_GO_BACK );
  180. buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY3 );
  181. buttonInfo->label = "#str_online_leaderboards_toggle_filter";
  182. buttonInfo->action.Set( WIDGET_ACTION_JOY3_ON_PRESS );
  183. if ( !lbCache->IsLoadingNewLeaderboard() && !lbCache->IsRequestingRows() && options != NULL && options->GetTotalNumberOfOptions() > 0 ) {
  184. buttonInfo = cmdBar->GetButton( idMenuWidget_CommandBar::BUTTON_JOY1 );
  185. if ( menuData->GetPlatform() != 2 ) {
  186. buttonInfo->label = "#str_swf_view_profile";
  187. }
  188. buttonInfo->action.Set( WIDGET_ACTION_PRESS_FOCUSED );
  189. }
  190. }
  191. }
  192. idSWFScriptObject & root = GetSWFObject()->GetRootObject();
  193. if ( BindSprite( root ) ) {
  194. idSWFTextInstance * heading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtHeading" );
  195. if ( heading != NULL ) {
  196. heading->SetText( lbCache->GetFilterStrType() );
  197. heading->SetStrokeInfo( true, 0.75f, 1.75f );
  198. }
  199. idSWFSpriteInstance * gradient = GetSprite()->GetScriptObject()->GetNestedSprite( "info", "gradient" );
  200. if ( gradient != NULL && heading != NULL ) {
  201. gradient->SetXPos( heading->GetTextLength() );
  202. }
  203. }
  204. if ( btnBack != NULL ) {
  205. btnBack->BindSprite( root );
  206. }
  207. idMenuScreen::Update();
  208. }
  209. /*
  210. ========================
  211. idMenuScreen_Shell_Leaderboards::ShowScreen
  212. ========================
  213. */
  214. void idMenuScreen_Shell_Leaderboards::ShowScreen( const mainMenuTransition_t transitionType ) {
  215. idMenuScreen::ShowScreen( transitionType );
  216. if ( GetSprite() != NULL ) {
  217. lbHeading = GetSprite()->GetScriptObject()->GetNestedText( "info", "txtLbType" );
  218. if ( menuData != NULL && menuData->GetGUI() != NULL ) {
  219. idSWFScriptObject * const shortcutKeys = menuData->GetGUI()->GetGlobal( "shortcutKeys" ).GetObject();
  220. if ( verify( shortcutKeys != NULL ) ) {
  221. // TAB NEXT
  222. idSWFScriptObject * const btnTabNext = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnNext" );
  223. if ( btnTabNext != NULL ) {
  224. btnTabNext->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_NEXT, 0 ) );
  225. shortcutKeys->Set( "JOY6", btnTabNext );
  226. if ( btnTabNext->GetSprite() != NULL && menuData != NULL ) {
  227. btnTabNext->GetSprite()->StopFrame( menuData->GetPlatform() + 1 );
  228. }
  229. }
  230. // TAB PREV
  231. idSWFScriptObject * const btnTabPrev = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPrevious" );
  232. if ( btnTabPrev != NULL ) {
  233. btnTabPrev->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_TAB_PREV, 0 ) );
  234. shortcutKeys->Set( "JOY5", btnTabPrev );
  235. if ( btnTabPrev->GetSprite() != NULL && menuData != NULL ) {
  236. btnTabPrev->GetSprite()->StopFrame( menuData->GetPlatform() + 1 );
  237. }
  238. }
  239. // TAB NEXT
  240. idSWFScriptObject * const btnDwn = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageDwn" );
  241. if ( btnDwn != NULL ) {
  242. btnDwn->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEDWN, 0 ) );
  243. shortcutKeys->Set( "JOY_TRIGGER2", btnDwn );
  244. if ( btnDwn->GetSprite() != NULL && menuData != NULL ) {
  245. btnDwn->GetSprite()->StopFrame( menuData->GetPlatform() + 1 );
  246. }
  247. }
  248. // TAB PREV
  249. idSWFScriptObject * const btnUp = GetSprite()->GetScriptObject()->GetNestedObj( "info", "btnPageUp" );
  250. if ( btnUp != NULL ) {
  251. btnUp->Set( "onPress", new ( TAG_SWF ) WrapWidgetSWFEvent( this, WIDGET_EVENT_SCROLL_PAGEUP, 0 ) );
  252. shortcutKeys->Set( "JOY_TRIGGER1", btnUp );
  253. if ( btnUp->GetSprite() != NULL && menuData != NULL ) {
  254. btnUp->GetSprite()->StopFrame( menuData->GetPlatform() + 1 );
  255. }
  256. }
  257. }
  258. }
  259. }
  260. SetLeaderboardIndex();
  261. if ( menuData == NULL ) {
  262. return;
  263. }
  264. int platform = menuData->GetPlatform();
  265. if ( btnNext != NULL && btnNext->GetSprite() != NULL ) {
  266. idSWFSpriteInstance * btnImg = btnNext->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
  267. if ( btnImg != NULL ) {
  268. if ( platform == 2 ) {
  269. btnImg->SetVisible( false );
  270. } else {
  271. btnImg->SetVisible( true );
  272. btnImg->StopFrame( platform + 1 );
  273. }
  274. }
  275. }
  276. if ( btnPrev != NULL && btnPrev->GetSprite() != NULL ) {
  277. idSWFSpriteInstance * btnImg = btnPrev->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
  278. if ( btnImg != NULL ) {
  279. if ( platform == 2 ) {
  280. btnImg->SetVisible( false );
  281. } else {
  282. btnImg->SetVisible( true );
  283. btnImg->StopFrame( platform + 1 );
  284. }
  285. }
  286. }
  287. if ( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL ) {
  288. idSWFSpriteInstance * btnImg = btnPageDwn->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
  289. if ( btnImg != NULL ) {
  290. if ( platform == 2 ) {
  291. btnImg->SetVisible( false );
  292. } else {
  293. btnImg->SetVisible( true );
  294. btnImg->StopFrame( platform + 1 );
  295. }
  296. }
  297. }
  298. if ( btnPageUp != NULL && btnPageUp->GetSprite() != NULL ) {
  299. idSWFSpriteInstance * btnImg = btnPageUp->GetSprite()->GetScriptObject()->GetNestedSprite( "btnImg" );
  300. if ( btnImg != NULL ) {
  301. if ( platform == 2 ) {
  302. btnImg->SetVisible( false );
  303. } else {
  304. btnImg->SetVisible( true );
  305. btnImg->StopFrame( platform + 1 );
  306. }
  307. }
  308. }
  309. }
  310. /*
  311. ========================
  312. idMenuScreen_Shell_Leaderboards::HideScreen
  313. ========================
  314. */
  315. void idMenuScreen_Shell_Leaderboards::HideScreen( const mainMenuTransition_t transitionType ) {
  316. idMenuScreen::HideScreen( transitionType );
  317. }
  318. /*
  319. ========================
  320. idMenuScreen_Shell_Leaderboards::HandleAction
  321. ========================
  322. */
  323. bool idMenuScreen_Shell_Leaderboards::HandleAction( idWidgetAction & action, const idWidgetEvent & event, idMenuWidget * widget, bool forceHandled ) {
  324. if ( menuData == NULL ) {
  325. return true;
  326. }
  327. if ( menuData->ActiveScreen() != SHELL_AREA_LEADERBOARDS ) {
  328. return false;
  329. }
  330. widgetAction_t actionType = action.GetType();
  331. const idSWFParmList & parms = action.GetParms();
  332. switch ( actionType ) {
  333. case WIDGET_ACTION_GO_BACK: {
  334. menuData->SetNextScreen( SHELL_AREA_PARTY_LOBBY, MENU_TRANSITION_SIMPLE );
  335. return true;
  336. }
  337. case WIDGET_ACTION_JOY3_ON_PRESS: {
  338. lbCache->CycleFilter();
  339. refreshLeaderboard = true;
  340. return true;
  341. }
  342. case WIDGET_ACTION_PRESS_FOCUSED: {
  343. if ( options == NULL ) {
  344. return true;
  345. }
  346. int index = options->GetFocusIndex();
  347. if ( parms.Num() != 0 ) {
  348. index = parms[0].ToInteger();
  349. }
  350. if ( lbCache->GetEntryIndex() != index ) {
  351. lbCache->SetEntryIndex( index );
  352. refreshLeaderboard = true;
  353. return true;
  354. }
  355. const idLeaderboardCallback::row_t * row = lbCache->GetLeaderboardRow( lbCache->GetRowOffset() + lbCache->GetEntryIndex() );
  356. if ( row != NULL ) {
  357. lbCache->DisplayGamerCardUI( row );
  358. }
  359. return true;
  360. }
  361. case WIDGET_ACTION_SCROLL_TAB: {
  362. int delta = parms[0].ToInteger();
  363. lbIndex += delta;
  364. SetLeaderboardIndex();
  365. return true;
  366. }
  367. case WIDGET_ACTION_SCROLL_VERTICAL_VARIABLE: {
  368. if ( parms.Num() == 0 ) {
  369. return true;
  370. }
  371. if ( options == NULL ) {
  372. return true;
  373. }
  374. int dir = parms[ 0 ].ToInteger();
  375. if ( lbCache->Scroll( dir ) ) {
  376. // TODO_SPARTY: play scroll sound
  377. refreshLeaderboard = true;
  378. }
  379. return true;
  380. }
  381. case WIDGET_ACTION_SCROLL_PAGE: {
  382. if ( parms.Num() == 0 ) {
  383. return true;
  384. }
  385. if ( options == NULL ) {
  386. return true;
  387. }
  388. int dir = parms[ 0 ].ToInteger();
  389. if ( lbCache->ScrollOffset( dir ) ) {
  390. refreshLeaderboard = true;
  391. }
  392. return true;
  393. }
  394. }
  395. return idMenuWidget::HandleAction( action, event, widget, forceHandled );
  396. }
  397. /*
  398. ========================
  399. idMenuScreen_Shell_Leaderboards::UpdateLeaderboard
  400. ========================
  401. */
  402. void idMenuScreen_Shell_Leaderboards::UpdateLeaderboard( const idLeaderboardCallback * callback ) {
  403. lbCache->Update( callback );
  404. if ( callback->GetErrorCode() != LEADERBOARD_ERROR_NONE ) {
  405. if ( !session->GetSignInManager().IsMasterLocalUserOnline() ) {
  406. refreshWhenMasterIsOnline = true;
  407. }
  408. }
  409. refreshLeaderboard = true;
  410. }
  411. /*
  412. ========================
  413. idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex
  414. ========================
  415. */
  416. void idMenuScreen_Shell_Leaderboards::SetLeaderboardIndex() {
  417. if ( lbIndex >= leaderboards.Num() ) {
  418. lbIndex = 0;
  419. } else if ( lbIndex < 0 ) {
  420. lbIndex = leaderboards.Num() - 1;
  421. }
  422. const leaderboardDefinition_t * leaderboardDef = leaderboards[ lbIndex ].lb;
  423. for ( int i = 0; i < leaderboardDef->numColumns; i++ ) {
  424. /*if ( leaderboardDef->columnDefs[i].displayType != STATS_COLUMN_NEVER_DISPLAY ) {
  425. gameLocal->GetMainMenu()->mainMenu->SetGlobal( va("columnname%d",i), leaderboardDef->columnDefs[i].locDisplayName );
  426. }*/
  427. }
  428. lbCache->SetLeaderboard( leaderboardDef, lbCache->GetFilter() );
  429. refreshLeaderboard = true;
  430. }
  431. /*
  432. ========================
  433. idMenuScreen_Shell_Leaderboards::RefreshLeaderboard
  434. ========================
  435. */
  436. void idMenuScreen_Shell_Leaderboards::RefreshLeaderboard() {
  437. if ( refreshWhenMasterIsOnline ) {
  438. SetLeaderboardIndex();
  439. refreshWhenMasterIsOnline = false;
  440. }
  441. if ( !refreshLeaderboard ) {
  442. return;
  443. }
  444. refreshLeaderboard = false;
  445. bool upArrow = false;
  446. bool downArrow = false;
  447. int focusIndex = -1;
  448. idList< idList< idStr, TAG_IDLIB_LIST_MENU >, TAG_IDLIB_LIST_MENU > lbListings;
  449. if ( !lbCache->IsLoadingNewLeaderboard() && lbCache->GetErrorCode() == LEADERBOARD_DISPLAY_ERROR_NONE ) {
  450. for ( int addIndex = 0; addIndex < MAX_STAT_LISTINGS; ++addIndex ) {
  451. idList< idStr > values;
  452. int index = lbCache->GetRowOffset() + addIndex;
  453. const idLeaderboardCallback::row_t * row = lbCache->GetLeaderboardRow( index ); // If this row is not in the cache, this will kick off a request
  454. if ( row != NULL ) {
  455. values.Append( va( "%i", (int)row->rank ) );
  456. values.Append( row->name );
  457. values.Append( FormatColumn( &lbCache->GetLeaderboard()->columnDefs[0], row->columns[0] ).ToString() );
  458. }
  459. if ( lbCache->GetEntryIndex() == addIndex ) {
  460. focusIndex = addIndex;
  461. }
  462. lbListings.Append( values );
  463. }
  464. if ( lbCache->GetRowOffset() != 0 ) {
  465. upArrow = true;
  466. }
  467. if ( ( lbCache->GetRowOffset() + MAX_STAT_LISTINGS ) < lbCache->GetNumRowsInLeaderboard() ) {
  468. downArrow = true;
  469. }
  470. }
  471. if ( lbHeading != NULL ) {
  472. lbHeading->SetText( leaderboards[ lbIndex ].name );
  473. lbHeading->SetStrokeInfo( true, 0.75f, 1.75f );
  474. }
  475. if ( focusIndex >= 0 ) {
  476. options->SetFocusIndex( focusIndex );
  477. }
  478. if ( btnPageDwn != NULL && btnPageDwn->GetSprite() != NULL ) {
  479. btnPageDwn->GetSprite()->SetVisible( downArrow );
  480. }
  481. if ( btnPageUp != NULL && btnPageUp->GetSprite() != NULL ) {
  482. btnPageUp->GetSprite()->SetVisible( upArrow );
  483. }
  484. options->SetListData( lbListings );
  485. Update();
  486. const char * leaderboardErrorStrings[] = {
  487. "",
  488. "#str_online_leaderboards_error_failed", // failed
  489. "", // not online - players are just taken back to multiplayer menu
  490. "#str_online_leaderboards_error_not_ranked", // not ranked
  491. };
  492. compile_time_assert( sizeof( leaderboardErrorStrings ) / sizeof( leaderboardErrorStrings[0] ) == LEADERBOARD_DISPLAY_ERROR_MAX );
  493. bool isLoading = lbCache->IsLoadingNewLeaderboard();
  494. idStr error = leaderboardErrorStrings[ lbCache->GetErrorCode() ];
  495. if ( isLoading ) {
  496. ShowMessage( true, "#str_online_loading", true );
  497. } else {
  498. if ( !error.IsEmpty() ) {
  499. ShowMessage( true, error, false );
  500. } else {
  501. if ( lbCache->GetNumRowsInLeaderboard() > 0 ) {
  502. ShowMessage( false, "", false );
  503. } else {
  504. ShowMessage( true, "#str_online_leaderboards_no_data", false );
  505. }
  506. }
  507. }
  508. }
  509. /*
  510. ========================
  511. idMenuScreen_Shell_Leaderboards::ShowMessage
  512. ========================
  513. */
  514. void idMenuScreen_Shell_Leaderboards::ShowMessage( bool show, idStr message, bool spinner ) {
  515. if ( !menuData || !menuData->GetGUI() ) {
  516. return;
  517. }
  518. idSWFSpriteInstance * pacifier = menuData->GetGUI()->GetRootObject().GetNestedSprite( "menuLeaderboards", "info", "pacifier" );
  519. if ( !pacifier ) {
  520. return;
  521. }
  522. if ( show ) {
  523. if ( spinner && options != NULL && options->GetSprite() != NULL ) {
  524. options->GetSprite()->SetAlpha( 0.35f );
  525. } else if ( options != NULL && options->GetSprite() != NULL ) {
  526. options->GetSprite()->SetVisible( false );
  527. }
  528. pacifier->SetVisible( true );
  529. idSWFTextInstance * txtMsg = pacifier->GetScriptObject()->GetNestedText( "message" );
  530. if ( txtMsg != NULL ) {
  531. txtMsg->SetText( message );
  532. txtMsg->SetStrokeInfo( true, 0.75f, 1.75f );
  533. }
  534. idSWFSpriteInstance * spriteSpinner = pacifier->GetScriptObject()->GetNestedSprite( "graphic" );
  535. if ( spriteSpinner != NULL ) {
  536. spriteSpinner->StopFrame( spinner ? 1 : 2 );
  537. }
  538. } else {
  539. if ( options != NULL && options->GetSprite() != NULL ) {
  540. options->GetSprite()->SetVisible( true );
  541. options->GetSprite()->SetAlpha( 1.0f );
  542. }
  543. pacifier->SetVisible( false );
  544. }
  545. }
  546. //*************************************************************************************************************************
  547. // LBCACHE
  548. //*************************************************************************************************************************
  549. class LBCallback : public idLeaderboardCallback
  550. {
  551. public:
  552. LBCallback() {}
  553. void Call() {
  554. gameLocal.Shell_UpdateLeaderboard( this );
  555. }
  556. LBCallback * Clone() const {
  557. return new LBCallback( *this );
  558. }
  559. };
  560. /*
  561. ========================
  562. idLBCache::Pump
  563. ========================
  564. */
  565. void idLBCache::Pump() {
  566. if ( loadingNewLeaderboard || requestingRows ) {
  567. return;
  568. }
  569. if ( pendingDef != NULL ) {
  570. SetLeaderboard( pendingDef, pendingFilter );
  571. }
  572. }
  573. /*
  574. ========================
  575. idLBCache::Reset
  576. ========================
  577. */
  578. void idLBCache::Reset() {
  579. for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) {
  580. rowBlocks[i].startIndex = 0;
  581. rowBlocks[i].rows.Clear();
  582. }
  583. def = NULL;
  584. filter = DEFAULT_LEADERBOARD_FILTER;
  585. pendingDef = NULL;
  586. pendingFilter = DEFAULT_LEADERBOARD_FILTER;
  587. rowOffset = 0;
  588. requestingRows = false;
  589. numRowsInLeaderboard = 0;
  590. entryIndex = 0;
  591. loadingNewLeaderboard = false;
  592. }
  593. /*
  594. ========================
  595. idLBCache::SetLeaderboard
  596. ========================
  597. */
  598. void idLBCache::SetLeaderboard( const leaderboardDefinition_t * def_, leaderboardFilterMode_t filter_ ) {
  599. // If we are busy waiting on results from a previous request, queue up this request
  600. if ( loadingNewLeaderboard || requestingRows ) {
  601. pendingDef = def_;
  602. pendingFilter = filter_;
  603. return;
  604. }
  605. //idLib::Printf( "SetLeaderboard 0x%p.\n", def_ );
  606. // Reset all
  607. Reset();
  608. // Set leaderboard and filter
  609. def = def_;
  610. filter = filter_;
  611. loadingNewLeaderboard = true; // This means we are waiting on the first set of results for this new leaderboard
  612. localIndex = -1; // don't know where the user is in the rows yet
  613. // Kick off initial stats request (which is initially based on the filter type)
  614. if ( filter == LEADERBOARD_FILTER_MYSCORE ) {
  615. LBCallback cb;
  616. session->LeaderboardDownload( 0, def, 0, MAX_ROWS_PER_BLOCK, cb );
  617. } else if ( filter == LEADERBOARD_FILTER_FRIENDS ) {
  618. LBCallback cb;
  619. session->LeaderboardDownload( 0, def, -1, 100, cb ); // Request up to 100 friends
  620. } else {
  621. LBCallback cb;
  622. session->LeaderboardDownload( 0, def, rowOffset + 1, MAX_ROWS_PER_BLOCK, cb );
  623. //session->LeaderboardDownload( 0, def, rowOffset + 1, 10, cb ); // For testing
  624. }
  625. }
  626. /*
  627. ========================
  628. idLBCache::CycleFilter
  629. ========================
  630. */
  631. void idLBCache::CycleFilter() {
  632. // Set the proper filter
  633. if ( filter == LEADERBOARD_FILTER_OVERALL ) {
  634. filter = LEADERBOARD_FILTER_MYSCORE;
  635. } else if ( filter == LEADERBOARD_FILTER_MYSCORE ) {
  636. filter = LEADERBOARD_FILTER_FRIENDS;
  637. } else {
  638. filter = LEADERBOARD_FILTER_OVERALL;
  639. }
  640. // Reset the leaderboard with the new filter
  641. SetLeaderboard( def, filter );
  642. }
  643. /*
  644. ========================
  645. idLBCache::GetFilterStrType
  646. ========================
  647. */
  648. idStr idLBCache::GetFilterStrType() {
  649. if ( filter == LEADERBOARD_FILTER_FRIENDS ) {
  650. return idLocalization::GetString( "#str_swf_leaderboards_friends_heading" );
  651. } else if ( filter == LEADERBOARD_FILTER_MYSCORE ) {
  652. return idLocalization::GetString( "#str_swf_leaderboards_global_self_heading" );
  653. }
  654. return idLocalization::GetString( "#str_swf_leaderboards_global_heading" );
  655. }
  656. /*
  657. ========================
  658. idLBCache::Scroll
  659. ========================
  660. */
  661. bool idLBCache::Scroll( int amount ) {
  662. if ( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE ) {
  663. return false; // don't allow scrolling on errors
  664. }
  665. // Remember old offsets so we know if anything moved
  666. int oldEntryIndex = entryIndex;
  667. int oldRowOffset = rowOffset;
  668. // Move cursor index by scroll amount
  669. entryIndex += amount;
  670. // Clamp cursor index (scrolling row offset if we can)
  671. if ( entryIndex < 0 ) {
  672. rowOffset += entryIndex;
  673. entryIndex = 0;
  674. } else if ( entryIndex >= numRowsInLeaderboard ) {
  675. entryIndex = numRowsInLeaderboard - 1;
  676. rowOffset = entryIndex - ( MAX_STAT_LISTINGS - 1 );
  677. } else if ( entryIndex >= MAX_STAT_LISTINGS ) {
  678. rowOffset += entryIndex - ( MAX_STAT_LISTINGS - 1 );
  679. entryIndex = MAX_STAT_LISTINGS - 1;
  680. }
  681. // Clamp row offset
  682. rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset );
  683. // Let caller know if anything actually changed
  684. return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset );
  685. }
  686. /*
  687. ========================
  688. idLBCache::ScrollOffset
  689. ========================
  690. */
  691. bool idLBCache::ScrollOffset( int amount ) {
  692. if ( GetErrorCode() != LEADERBOARD_DISPLAY_ERROR_NONE ) {
  693. return false; // don't allow scrolling on errors
  694. }
  695. // Remember old offsets so we know if anything moved
  696. int oldEntryIndex = entryIndex;
  697. int oldRowOffset = rowOffset;
  698. rowOffset += amount;
  699. // Clamp row offset
  700. rowOffset = idMath::ClampInt( 0, Max( numRowsInLeaderboard - MAX_STAT_LISTINGS, 0 ), rowOffset );
  701. if ( rowOffset != oldRowOffset ) {
  702. entryIndex -= amount; // adjust in opposite direction so same item stays selected
  703. entryIndex = idMath::ClampInt( 0, rowOffset + ( MAX_STAT_LISTINGS - 1 ), entryIndex );
  704. } else {
  705. entryIndex += amount;
  706. entryIndex = idMath::ClampInt( 0, numRowsInLeaderboard - 1, entryIndex );
  707. }
  708. // Let caller know if anything actually changed
  709. return ( oldEntryIndex != entryIndex || oldRowOffset != rowOffset );
  710. }
  711. /*
  712. ========================
  713. idLBCache::FindFreeRowBlock
  714. ========================
  715. */
  716. idLBRowBlock * idLBCache::FindFreeRowBlock() {
  717. int bestTime = 0;
  718. int bestBlockIndex = 0;
  719. for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) {
  720. if ( rowBlocks[i].rows.Num() == 0 ) {
  721. return &rowBlocks[i]; // Prefer completely empty blocks
  722. }
  723. // Search for oldest block in the mean time
  724. if ( i == 0 || rowBlocks[i].lastTime < bestTime ) {
  725. bestBlockIndex = i;
  726. bestTime = rowBlocks[i].lastTime;
  727. }
  728. }
  729. return &rowBlocks[bestBlockIndex];
  730. }
  731. /*
  732. ========================
  733. idLBCache::CallbackErrorToDisplayError
  734. ========================
  735. */
  736. leaderboardDisplayError_t idLBCache::CallbackErrorToDisplayError( leaderboardError_t errorCode ) {
  737. switch ( errorCode ) {
  738. case LEADERBOARD_ERROR_NONE:
  739. return LEADERBOARD_DISPLAY_ERROR_NONE;
  740. default:
  741. return LEADERBOARD_DISPLAY_ERROR_FAILED;
  742. }
  743. }
  744. /*
  745. ========================
  746. idLBCache::Update
  747. ========================
  748. */
  749. void idLBCache::Update( const idLeaderboardCallback * callback ) {
  750. requestingRows = false;
  751. //idLib::Printf( "Stats returned.\n" );
  752. errorCode = CallbackErrorToDisplayError( callback->GetErrorCode() );
  753. // check if trying to view "My Score" leaderboard when you aren't ranked yet
  754. if ( loadingNewLeaderboard && filter == LEADERBOARD_FILTER_MYSCORE && callback->GetLocalIndex() == -1 && errorCode == LEADERBOARD_DISPLAY_ERROR_NONE ) {
  755. errorCode = LEADERBOARD_DISPLAY_ERROR_NOT_RANKED;
  756. }
  757. if ( errorCode != LEADERBOARD_DISPLAY_ERROR_NONE ) {
  758. numRowsInLeaderboard = 0;
  759. loadingNewLeaderboard = false;
  760. switch ( errorCode ) {
  761. case LEADERBOARD_DISPLAY_ERROR_NOT_ONLINE:
  762. /*idMenuHandler_Shell * shell = gameLocal.Shell_GetHandler();
  763. if ( shell != NULL ) {
  764. shell->SetNextScreen( SHELL_AREA_ROOT, MENU_TRANSITION_SIMPLE );
  765. }*/
  766. common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, true, "", 0, true );
  767. break;
  768. default:
  769. break;
  770. }
  771. return;
  772. }
  773. if ( callback->GetDef() != def ) {
  774. // Not the leaderboard we are looking for (This should no longer be possible)
  775. idLib::Printf( "Wrong leaderboard.\n" );
  776. numRowsInLeaderboard = 0;
  777. loadingNewLeaderboard = false;
  778. return;
  779. }
  780. // Get total rows in this leaderboard
  781. numRowsInLeaderboard = callback->GetNumRowsInLeaderboard();
  782. // Store the index that the master user is in, if we haven't already found the index
  783. if ( callback->GetLocalIndex() != -1 ) {
  784. localIndex = callback->GetStartIndex() + callback->GetLocalIndex();
  785. }
  786. if ( loadingNewLeaderboard == true ) {
  787. // Default to viewing the local user (if the right filter mode is set)
  788. if ( callback->GetLocalIndex() != -1 && ( filter == LEADERBOARD_FILTER_MYSCORE || filter == LEADERBOARD_FILTER_FRIENDS ) ) {
  789. // Put their name and cursor at the top
  790. rowOffset = callback->GetLocalIndex() + callback->GetStartIndex();
  791. entryIndex = 0;
  792. // Scroll the cursor up to center their name as much as possible
  793. Scroll( -MAX_STAT_LISTINGS / 2 );
  794. // Set the cursor to their name
  795. entryIndex = ( callback->GetLocalIndex() + callback->GetStartIndex() ) - rowOffset;
  796. }
  797. loadingNewLeaderboard = false;
  798. }
  799. // Find a a row block to store these new rows
  800. idLBRowBlock * rowBlock = FindFreeRowBlock();
  801. rowBlock->lastTime = Sys_Milliseconds(); // Freshen row
  802. rowBlock->startIndex = callback->GetStartIndex();
  803. rowBlock->rows = callback->GetRows();
  804. }
  805. /*
  806. ========================
  807. idLBCache::GetLeaderboardRow
  808. ========================
  809. */
  810. const idLeaderboardCallback::row_t * idLBCache::GetLeaderboardRow( int row ) {
  811. if ( loadingNewLeaderboard ) {
  812. return NULL; // If we are refreshing (seeing this leaderboard for the first time), force NULL till we get first set of results
  813. }
  814. if ( row >= numRowsInLeaderboard ) {
  815. return NULL;
  816. }
  817. // Find it in the cache
  818. for ( int i = 0; i < NUM_ROW_BLOCKS; i++ ) {
  819. int startIndex = rowBlocks[i].startIndex;
  820. int lastIndex = startIndex + rowBlocks[i].rows.Num() - 1;
  821. if ( row >= startIndex && row <= lastIndex ) {
  822. rowBlocks[i].lastTime = Sys_Milliseconds(); // Freshen row
  823. return &rowBlocks[i].rows[row - startIndex];
  824. }
  825. }
  826. // Not found, kick off a request to download it
  827. // (this will not allow more than one request at a time)
  828. if ( !requestingRows ) {
  829. // If we don't have this row, kick off a request to get it
  830. LBCallback cb;
  831. requestingRows = true;
  832. session->LeaderboardDownload( 0, def, row + 1, MAX_ROWS_PER_BLOCK, cb );
  833. //session->LeaderboardDownload( 0, def, row + 1, 10, cb );
  834. //idLib::Printf( "Stat request\n" );
  835. }
  836. return NULL;
  837. }
  838. /*
  839. ========================
  840. idMainMenu::SetSPLeaderboardFromMenuSettings
  841. ========================
  842. */
  843. void idLBCache::DisplayGamerCardUI( const idLeaderboardCallback::row_t * row ) {
  844. }