Team Fortress 2 Source Code as on 22/4/2020
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.

1075 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Display a list of achievements for the current game
  4. //
  5. //=============================================================================//
  6. #include "achievementsdialog.h"
  7. #include "vgui_controls/Button.h"
  8. #include "vgui/ILocalize.h"
  9. #include "ixboxsystem.h"
  10. #include "iachievementmgr.h"
  11. #include "EngineInterface.h"
  12. #include "GameUI_Interface.h"
  13. #include "MouseMessageForwardingPanel.h"
  14. #include "filesystem.h"
  15. #include "vgui_controls/ImagePanel.h"
  16. #include "vgui_controls/ComboBox.h"
  17. #include "vgui_controls/CheckButton.h"
  18. #include "vgui_controls/ScrollBar.h"
  19. #include "BasePanel.h"
  20. #include "fmtstr.h"
  21. #include "UtlSortVector.h"
  22. using namespace vgui;
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. KeyValues *g_pPreloadedAchievementItemLayout = NULL;
  26. #define NUM_COMBO_BOX_LINES_DEFAULT 10
  27. #define NUM_COMBO_BOX_LINES_MAX 16
  28. // Shared helper functions so xbox and pc can share as much code as possible while coming from different bases.
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file.
  31. //-----------------------------------------------------------------------------
  32. bool LoadAchievementIcon( vgui::ImagePanel* pIconPanel, IAchievement *pAchievement, const char *pszExt /*= NULL*/ )
  33. {
  34. char imagePath[_MAX_PATH];
  35. Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) );
  36. Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS );
  37. if ( pszExt )
  38. {
  39. Q_strncat( imagePath, pszExt, sizeof(imagePath), COPY_ALL_CHARACTERS );
  40. }
  41. Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS );
  42. char checkFile[_MAX_PATH];
  43. Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath );
  44. if ( !g_pFullFileSystem->FileExists( checkFile ) )
  45. {
  46. Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" );
  47. }
  48. pIconPanel->SetShouldScaleImage( true );
  49. pIconPanel->SetImage( imagePath );
  50. pIconPanel->SetVisible( true );
  51. return pIconPanel->IsVisible();
  52. }
  53. //-----------------------------------------------------------------------------
  54. // The bias is to ensure the percentage bar gets plenty orange before it reaches the text,
  55. // as the white-on-grey is hard to read.
  56. //-----------------------------------------------------------------------------
  57. Color LerpColors ( Color cStart, Color cEnd, float flPercent )
  58. {
  59. float r = (float)((float)(cStart.r()) + (float)(cEnd.r() - cStart.r()) * Bias( flPercent, 0.75 ) );
  60. float g = (float)((float)(cStart.g()) + (float)(cEnd.g() - cStart.g()) * Bias( flPercent, 0.75 ) );
  61. float b = (float)((float)(cStart.b()) + (float)(cEnd.b() - cStart.b()) * Bias( flPercent, 0.75 ) );
  62. float a = (float)((float)(cStart.a()) + (float)(cEnd.a() - cStart.a()) * Bias( flPercent, 0.75 ) );
  63. return Color( r, g, b, a );
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Shares common percentage bar calculations/color settings between xbox and pc.
  67. // Not really intended for robustness or reuse across many panels.
  68. // Input : pFrame - assumed to have certain child panels (see below)
  69. // *pAchievement - source achievement to poll for progress. Non progress achievements will not show a percentage bar.
  70. //-----------------------------------------------------------------------------
  71. void UpdateProgressBar( vgui::EditablePanel* pPanel, IAchievement *pAchievement, Color clrProgressBar )
  72. {
  73. if ( pAchievement->GetGoal() > 1 )
  74. {
  75. bool bShowProgress = true;
  76. // if this achievement gets saved with game and we're not in a level and have not achieved it, then we do not have any state
  77. // for this achievement, don't show progress
  78. if ( ( pAchievement->GetFlags() & ACH_SAVE_WITH_GAME ) && !GameUI().IsInLevel() && !pAchievement->IsAchieved() )
  79. {
  80. bShowProgress = false;
  81. }
  82. float flCompletion = 0.0f;
  83. // Once achieved, we can't rely on count. If they've completed the achievement just set to 100%.
  84. int iCount = pAchievement->GetCount();
  85. if ( pAchievement->IsAchieved() )
  86. {
  87. flCompletion = 1.0f;
  88. iCount = pAchievement->GetGoal();
  89. }
  90. else if ( bShowProgress )
  91. {
  92. flCompletion = ( ((float)pAchievement->GetCount()) / ((float)pAchievement->GetGoal()) );
  93. // In rare cases count can exceed goal and not be achieved (switch local storage on X360, take saved game from different user on PC).
  94. // These will self-correct with continued play, but if we're in that state don't show more than 100% achieved.
  95. flCompletion = min( flCompletion, 1.f );
  96. }
  97. char szPercentageText[ 256 ] = "";
  98. if ( bShowProgress )
  99. {
  100. Q_snprintf( szPercentageText, 256, "%d/%d", iCount, pAchievement->GetGoal() );
  101. }
  102. pPanel->SetControlString( "PercentageText", szPercentageText );
  103. pPanel->SetControlVisible( "PercentageText", true );
  104. pPanel->SetControlVisible( "CompletionText", true );
  105. vgui::ImagePanel *pPercentageBar = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBar" );
  106. vgui::ImagePanel *pPercentageBarBkg = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBarBackground" );
  107. if ( pPercentageBar && pPercentageBarBkg )
  108. {
  109. pPercentageBar->SetFillColor( clrProgressBar );
  110. pPercentageBar->SetWide( pPercentageBarBkg->GetWide() * flCompletion );
  111. pPanel->SetControlVisible( "PercentageBarBackground", IsX360() ? bShowProgress : true );
  112. pPanel->SetControlVisible( "PercentageBar", true );
  113. }
  114. }
  115. }
  116. // TODO: revisit this once other games are rebuilt using the updated IAchievement interface
  117. bool GameSupportsAchievementTracker()
  118. {
  119. const char *pGame = Q_UnqualifiedFileName( engine->GetGameDirectory() );
  120. if ( ( Q_stricmp( pGame, "tf" ) == 0 ) || ( Q_stricmp( pGame, "tf_beta" ) == 0 ) )
  121. return true;
  122. return false;
  123. }
  124. //----------------------------------------------------------
  125. // CAchievementsDialog_XBox
  126. //----------------------------------------------------------
  127. CAchievementsDialog_XBox::CAchievementsDialog_XBox( vgui::Panel *pParent ) : BaseClass( pParent, "AchievementsDialog" )
  128. {
  129. m_nTotalAchievements = 0;
  130. m_nUnlocked = 0;
  131. m_iSelection = 0;
  132. m_iScroll = 0;
  133. m_iNumItems = 0;
  134. m_bCenterOnScreen = true;
  135. m_pProgressBg = new vgui::Panel( this, "ProgressBg" );
  136. m_pProgressBar = new vgui::Panel( this, "ProgressBar" );
  137. m_pProgressPercent = new vgui::Label( this, "ProgressPercent", "" );
  138. m_pNumbering = new vgui::Label( this, "Numbering", "" );
  139. m_pUpArrow = new vgui::Label( this, "UpArrow", "" );
  140. m_pDownArrow = new vgui::Label( this, "DownArrow", "" );
  141. SetDeleteSelfOnClose(true);
  142. SetSizeable( false );
  143. m_pFooter = new CFooterPanel( pParent, "AchievementsFooter" );
  144. }
  145. CAchievementsDialog_XBox::~CAchievementsDialog_XBox()
  146. {
  147. delete m_pProgressBg;
  148. delete m_pProgressBar;
  149. delete m_pProgressPercent;
  150. delete m_pNumbering;
  151. delete m_pUpArrow;
  152. delete m_pDownArrow;
  153. delete m_pFooter;
  154. }
  155. //----------------------------------------------------------
  156. // Position the dialogs elements
  157. //----------------------------------------------------------
  158. void CAchievementsDialog_XBox::PerformLayout( void )
  159. {
  160. BaseClass::PerformLayout();
  161. // Avoid division by zero if the achievements haven't been downloaded yet
  162. if ( m_nTotalAchievements )
  163. {
  164. int x, y, wide, tall;
  165. m_pProgressBg->GetBounds( x, y, wide, tall );
  166. int iBarX = GetWide() - wide - m_nBorderWidth;
  167. int iBarWide = wide * m_nUnlocked / m_nTotalAchievements;
  168. m_pProgressBg->SetBounds( iBarX, y, wide, tall );
  169. m_pProgressBar->SetBounds( iBarX, y, iBarWide, tall );
  170. wchar_t wszProgressText[64];
  171. #ifdef WIN32
  172. V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %s", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) );
  173. #else
  174. V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %S", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) );
  175. #endif
  176. m_pProgressPercent->SetText( wszProgressText );
  177. m_pProgressPercent->SizeToContents();
  178. m_pProgressPercent->SetPos( GetWide() - m_pProgressPercent->GetWide() - m_nBorderWidth, y + tall + 3 );
  179. int menux, menuy;
  180. m_Menu.GetPos( menux, menuy );
  181. // Do a perform layout on the menu so we get the correct height now
  182. m_Menu.InvalidateLayout( true, false );
  183. int iBottomOfTable = menuy + m_Menu.GetTall() + 3;
  184. m_pNumbering->SizeToContents();
  185. m_pNumbering->SetPos( m_nBorderWidth, iBottomOfTable );
  186. m_pUpArrow->GetPos( x, y );
  187. m_pUpArrow->SetPos( x, iBottomOfTable );
  188. m_pDownArrow->GetPos( x, y );
  189. m_pDownArrow->SetPos( x, iBottomOfTable );
  190. wchar_t wszNumbering[64];
  191. wchar_t *wzNumberingFmt = g_pVGuiLocalize->Find( "#GameUI_Achievement_Menu_Range" );
  192. wchar_t wzActiveItem[8];
  193. wchar_t wzTotal[8];
  194. V_snwprintf( wzActiveItem, ARRAYSIZE( wzActiveItem ), L"%d", m_Menu.GetActiveItemIndex()+1 );
  195. V_snwprintf( wzTotal, ARRAYSIZE( wzTotal ), L"%d", m_nTotalAchievements );
  196. g_pVGuiLocalize->ConstructString( wszNumbering, sizeof( wszNumbering ), wzNumberingFmt, 2, wzActiveItem, wzTotal );
  197. m_pNumbering->SetText( wszNumbering );
  198. m_pNumbering->SetWide( GetWide() );
  199. }
  200. if ( m_bCenterOnScreen )
  201. {
  202. MoveToCenterOfScreen();
  203. }
  204. // Set the footer
  205. m_pFooter->ClearButtons();
  206. if ( m_pFooterInfo && ( m_pFooterInfo->GetInt( "hide_regular_footer", 0 ) > 0 ) == false )
  207. {
  208. for ( KeyValues *pButton = m_pFooterInfo->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() )
  209. {
  210. if ( !Q_stricmp( pButton->GetName(), "button" ) )
  211. {
  212. // Add a button to the footer
  213. const char *pText = pButton->GetString( "text", "NULL" );
  214. const char *pIcon = pButton->GetString( "icon", "NULL" );
  215. m_pFooter->AddNewButtonLabel( pText, pIcon );
  216. }
  217. }
  218. }
  219. }
  220. //----------------------------------------------------------
  221. // Query the system interface for this game's list of achievements
  222. //----------------------------------------------------------
  223. void CAchievementsDialog_XBox::ApplySettings( KeyValues *pResourceData )
  224. {
  225. BaseClass::ApplySettings( pResourceData );
  226. m_bCenterOnScreen = pResourceData->GetInt( "center", 1 ) == 1;
  227. if ( !achievementmgr )
  228. {
  229. AssertOnce( 0 && "No achievement manager interface in GameUI.dll" );
  230. return;
  231. }
  232. achievementmgr->EnsureGlobalStateLoaded();
  233. int iAllAchievements = achievementmgr->GetAchievementCount();
  234. for ( int i = 0; i < iAllAchievements; ++i )
  235. {
  236. IAchievement* pCurAchievement = (IAchievement*)achievementmgr->GetAchievementByIndex( i );
  237. Assert ( pCurAchievement );
  238. if ( pCurAchievement->IsAchieved() )
  239. ++m_nUnlocked;
  240. // don't show hidden achievements if not achieved
  241. if ( pCurAchievement->ShouldHideUntilAchieved() && !pCurAchievement->IsAchieved() )
  242. continue;
  243. CAchievementItem *pItem = m_Menu.AddAchievementItem( ACHIEVEMENT_LOCALIZED_NAME( pCurAchievement ),
  244. ACHIEVEMENT_LOCALIZED_DESC( pCurAchievement ),
  245. pCurAchievement->GetPointValue(),
  246. pCurAchievement->IsAchieved(),
  247. pCurAchievement
  248. );
  249. SETUP_PANEL( pItem );
  250. }
  251. m_nTotalAchievements = m_Menu.GetItemCount();
  252. }
  253. //----------------------------------------------------------
  254. // Add the progress bar and update the dialog layout
  255. //----------------------------------------------------------
  256. void CAchievementsDialog_XBox::ApplySchemeSettings( vgui::IScheme *pScheme )
  257. {
  258. BaseClass::ApplySchemeSettings( pScheme );
  259. m_pProgressBg->SetBgColor( Color( 200, 184, 151, 255 ) );
  260. m_pProgressBar->SetBgColor( Color( 179, 82, 22, 255 ) );
  261. m_pProgressPercent->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
  262. m_pNumbering->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Scrolling up and down with up/down, exit to main menu with back button
  266. //-----------------------------------------------------------------------------
  267. void CAchievementsDialog_XBox::OnKeyCodePressed( vgui::KeyCode code )
  268. {
  269. // Handle close here, CBasePanel parent doesn't support "DialogClosing" command
  270. if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
  271. {
  272. OnCommand( "AchievementsDialogClosing" );
  273. SetDeleteSelfOnClose( true );
  274. }
  275. else
  276. {
  277. BaseClass::OnKeyCodePressed( code );
  278. }
  279. InvalidateLayout();
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose: catch key repeats
  283. //-----------------------------------------------------------------------------
  284. void CAchievementsDialog_XBox::HandleKeyRepeated( vgui::KeyCode code )
  285. {
  286. BaseClass::HandleKeyRepeated( code );
  287. InvalidateLayout();
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose: Fade main menu back in when this dialog closes.
  291. //-----------------------------------------------------------------------------
  292. void CAchievementsDialog_XBox::OnClose()
  293. {
  294. BasePanel()->RunCloseAnimation( "CloseAchievementsDialog_OpenMainMenu" );
  295. BaseClass::OnClose();
  296. }
  297. //////////////////////////////////////////////////////////////////////////
  298. // PC Implementation
  299. //////////////////////////////////////////////////////////////////////////
  300. //-----------------------------------------------------------------------------
  301. // Purpose: creates child panels, passes down name to pick up any settings from res files.
  302. //-----------------------------------------------------------------------------
  303. CAchievementsDialog::CAchievementsDialog(vgui::Panel *parent) : BaseClass(parent, "AchievementsDialog")
  304. {
  305. SetDeleteSelfOnClose(true);
  306. SetBounds(0, 0, 512, 384);
  307. SetMinimumSize( 256, 300 );
  308. SetSizeable( true );
  309. m_nOldScrollItem = -1;
  310. m_nScrollItem = -1;
  311. m_nUnlocked = 0;
  312. m_nTotalAchievements = 0;
  313. m_pAchievementsList = new vgui::PanelListPanel( this, "listpanel_achievements" );
  314. m_pAchievementsList->SetFirstColumnWidth( 0 );
  315. m_pListBG = new vgui::ImagePanel( this, "listpanel_background" );
  316. m_pPercentageBarBackground = SETUP_PANEL( new ImagePanel( this, "PercentageBarBackground" ) );
  317. m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) );
  318. m_pSelectionHighlight = new ImagePanel( m_pAchievementsList->FindChildByName( "PanelListEmbedded" ), "SelectionHighlight" );
  319. m_pSelectionHighlight->SetVisible( false );
  320. m_pSelectionHighlight->SetZPos( 100 );
  321. m_pAchievementPackCombo = new ComboBox(this, "achievement_pack_combo", NUM_COMBO_BOX_LINES_DEFAULT, false);
  322. m_pHideAchievedCheck = new vgui::CheckButton( this, "HideAchieved", "" );
  323. // int that holds the highest number achievement id we've found
  324. int iHighestAchievementIDSeen = -1;
  325. int iNextGroupBoundary = 1000;
  326. Q_memset( m_AchievementGroups, 0, sizeof(m_AchievementGroups) );
  327. m_iNumAchievementGroups = 0;
  328. // Base groups
  329. // CreateNewAchievementGroup( 0, 16000 );
  330. CreateNewAchievementGroup( 0, 999 );
  331. Assert ( achievementmgr );
  332. if ( achievementmgr )
  333. {
  334. int iCount = achievementmgr->GetAchievementCount();
  335. for ( int i = 0; i < iCount; ++i )
  336. {
  337. IAchievement* pCur = achievementmgr->GetAchievementByIndex( i );
  338. if ( !pCur )
  339. continue;
  340. int iAchievementID = pCur->GetAchievementID();
  341. if ( iAchievementID > iHighestAchievementIDSeen )
  342. {
  343. // if its crossed the next group boundary, create a new group
  344. if ( iAchievementID >= iNextGroupBoundary )
  345. {
  346. int iNewGroupBoundary = 100 * ( (int)( (float)iAchievementID / 100 ) );
  347. CreateNewAchievementGroup( iNewGroupBoundary, iNewGroupBoundary+99 );
  348. iNextGroupBoundary = iNewGroupBoundary + 100;
  349. }
  350. iHighestAchievementIDSeen = iAchievementID;
  351. }
  352. // don't show hidden achievements if not achieved
  353. if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() )
  354. continue;
  355. m_nTotalAchievements++;
  356. if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() )
  357. continue;
  358. bool bAchieved = pCur->IsAchieved();
  359. if ( bAchieved )
  360. {
  361. ++m_nUnlocked;
  362. }
  363. for ( int j=0;j<m_iNumAchievementGroups;j++ )
  364. {
  365. if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange &&
  366. iAchievementID <= m_AchievementGroups[j].m_iMaxRange )
  367. {
  368. if ( bAchieved )
  369. {
  370. m_AchievementGroups[j].m_iNumUnlocked++;
  371. }
  372. m_AchievementGroups[j].m_iNumAchievements++;
  373. }
  374. }
  375. }
  376. }
  377. CreateOrUpdateComboItems( true );
  378. m_pAchievementPackCombo->ActivateItemByRow( 0 );
  379. }
  380. CAchievementsDialog::~CAchievementsDialog()
  381. {
  382. if ( achievementmgr )
  383. {
  384. achievementmgr->SaveGlobalStateIfDirty( false ); // check for saving here to store achievements we want pinned to HUD
  385. }
  386. m_pAchievementsList->DeleteAllItems();
  387. delete m_pAchievementsList;
  388. delete m_pPercentageBarBackground;
  389. delete m_pPercentageBar;
  390. }
  391. void CAchievementsDialog::OnCheckButtonChecked(Panel *panel)
  392. {
  393. if ( panel == m_pHideAchievedCheck )
  394. {
  395. UpdateAchievementList();
  396. }
  397. }
  398. void CAchievementsDialog::CreateNewAchievementGroup( int iMinRange, int iMaxRange )
  399. {
  400. if ( m_iNumAchievementGroups < MAX_ACHIEVEMENT_GROUPS )
  401. {
  402. m_AchievementGroups[m_iNumAchievementGroups].m_iMinRange = iMinRange;
  403. m_AchievementGroups[m_iNumAchievementGroups].m_iMaxRange = iMaxRange;
  404. m_iNumAchievementGroups++;
  405. }
  406. else
  407. {
  408. AssertMsg( false, "CAchievementsDialog: Too many achievement groups" );
  409. }
  410. }
  411. //----------------------------------------------------------
  412. // Get the width we're going to lock at
  413. //----------------------------------------------------------
  414. void CAchievementsDialog::ApplySettings( KeyValues *pResourceData )
  415. {
  416. m_iFixedWidth = pResourceData->GetInt( "wide", 512 );
  417. BaseClass::ApplySettings( pResourceData );
  418. }
  419. //----------------------------------------------------------
  420. // Preserve our width to the one in the .res file
  421. //----------------------------------------------------------
  422. void CAchievementsDialog::OnSizeChanged(int newWide, int newTall)
  423. {
  424. // Lock the width, but allow height scaling
  425. if ( newWide != m_iFixedWidth )
  426. {
  427. SetSize( m_iFixedWidth, newTall );
  428. return;
  429. }
  430. BaseClass::OnSizeChanged(newWide, newTall);
  431. }
  432. //----------------------------------------------------------
  433. // New group was selected in the dropdown, recalc what achievements to show
  434. //----------------------------------------------------------
  435. void CAchievementsDialog::OnTextChanged(KeyValues *data)
  436. {
  437. Panel *pPanel = (Panel *)data->GetPtr( "panel", NULL );
  438. // first check which control had its text changed!
  439. if ( pPanel == m_pAchievementPackCombo )
  440. {
  441. UpdateAchievementList();
  442. }
  443. }
  444. class CAchievementsLess
  445. {
  446. public:
  447. bool Less( const IAchievement* src1, const IAchievement* src2, void *pCtx )
  448. {
  449. IAchievement* _src1 = const_cast<IAchievement*>(src1);
  450. IAchievement* _src2 = const_cast<IAchievement*>(src2);
  451. if ( _src1->IsAchieved() && !_src2->IsAchieved() )
  452. return false;
  453. if ( !_src1->IsAchieved() && _src2->IsAchieved() )
  454. return true;
  455. const char* name1 = _src1->GetName();
  456. const char* name2 = _src2->GetName();
  457. if ( wcscoll( ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name1 ), ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name2 ) ) < 0 )
  458. return true;
  459. return false;
  460. }
  461. };
  462. //----------------------------------------------------------
  463. // Re-populate the achievement list with the selected group
  464. //----------------------------------------------------------
  465. void CAchievementsDialog::UpdateAchievementList()
  466. {
  467. m_pAchievementsList->DeleteAllItems();
  468. KeyValues *pData = m_pAchievementPackCombo->GetActiveItemUserData();
  469. if ( pData )
  470. {
  471. int iMinRange = pData->GetInt( "minrange" );
  472. int iMaxRange = pData->GetInt( "maxrange" );
  473. if ( achievementmgr )
  474. {
  475. CUtlSortVector<IAchievement*, CAchievementsLess> achievements;
  476. int iCount = achievementmgr->GetAchievementCount();
  477. for ( int i = 0; i < iCount; ++i )
  478. {
  479. IAchievement* pCur = achievementmgr->GetAchievementByIndex( i );
  480. if ( !pCur )
  481. continue;
  482. int iAchievementID = pCur->GetAchievementID();
  483. if ( iAchievementID < iMinRange || iAchievementID > iMaxRange )
  484. continue;
  485. // don't show hidden achievements if not achieved
  486. if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() )
  487. continue;
  488. if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() )
  489. continue;
  490. // if we don't have a localized name, don't add it to the list
  491. if ( pCur->GetName() == NULL || ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( pCur->GetName() ) == NULL )
  492. continue;
  493. achievements.Insert( pCur );
  494. }
  495. for ( int i=0; i<achievements.Count(); i++ )
  496. {
  497. CAchievementDialogItemPanel *achievementItemPanel = new CAchievementDialogItemPanel( m_pAchievementsList, "AchievementDialogItemPanel", i );
  498. achievementItemPanel->SetAchievementInfo( achievements[i] );
  499. m_pAchievementsList->AddItem( NULL, achievementItemPanel );
  500. }
  501. }
  502. }
  503. if ( m_pAchievementsList->GetItemCount() > 0 )
  504. {
  505. m_nOldScrollItem = -1;
  506. m_nScrollItem = -1;
  507. m_pSelectionHighlight->SetVisible( false );
  508. m_pAchievementsList->ScrollToItem( 0 );
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Purpose: Loads settings from achievementsdialog.res in hl2/resource/ui/
  513. // Sets up progress bar displaying total achievement unlocking progress by the user.
  514. //-----------------------------------------------------------------------------
  515. void CAchievementsDialog::ApplySchemeSettings( IScheme *pScheme )
  516. {
  517. BaseClass::ApplySchemeSettings( pScheme );
  518. LoadControlSettings("resource/ui/AchievementsDialog.res");
  519. if ( !achievementmgr )
  520. {
  521. AssertOnce( 0 && "No achievement manager interface in GameUI.dll" );
  522. return;
  523. }
  524. // Set up total completion percentage bar
  525. float flCompletion = 0.0f;
  526. if ( achievementmgr->GetAchievementCount() > 0 )
  527. {
  528. flCompletion = (((float)m_nUnlocked) / ((float)m_nTotalAchievements));
  529. }
  530. char szPercentageText[64];
  531. V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )",
  532. m_nUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) );
  533. SetControlString( "PercentageText", szPercentageText );
  534. SetControlVisible( "PercentageText", true );
  535. SetControlVisible( "CompletionText", true );
  536. Color clrHighlight = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) );
  537. Color clrWhite(255, 255, 255, 255);
  538. Color cProgressBar = Color( static_cast<float>( clrHighlight.r() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.r() ) * flCompletion,
  539. static_cast<float>( clrHighlight.g() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.g() ) * flCompletion,
  540. static_cast<float>( clrHighlight.b() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.b() ) * flCompletion,
  541. static_cast<float>( clrHighlight.a() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.a() ) * flCompletion );
  542. m_pPercentageBar->SetFgColor( cProgressBar );
  543. m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion );
  544. SetControlVisible( "PercentageBarBackground", true );
  545. SetControlVisible( "PercentageBar", true );
  546. m_pSelectionHighlight->SetFillColor( Color( clrHighlight.r(), clrHighlight.g(), clrHighlight.b(), 32 ) );
  547. if ( m_iNumAchievementGroups <= 2 )
  548. {
  549. // we have no achievement packs. Hide the combo and bump the achievement list up a bit
  550. m_pAchievementPackCombo->SetVisible( false );
  551. // do some work to preserve the pincorner and resizing
  552. int comboX, comboY;
  553. m_pAchievementPackCombo->GetPos( comboX, comboY );
  554. int x, y, w, h;
  555. m_pAchievementsList->GetBounds( x, y, w, h );
  556. PinCorner_e corner = m_pAchievementsList->GetPinCorner();
  557. int pinX, pinY;
  558. m_pAchievementsList->GetPinOffset( pinX, pinY );
  559. int resizeOffsetX, resizeOffsetY;
  560. m_pAchievementsList->GetResizeOffset( resizeOffsetX, resizeOffsetY );
  561. m_pAchievementsList->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY );
  562. m_pAchievementsList->SetBounds( x, comboY, w, h + ( y - comboY ) );
  563. m_pListBG->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY );
  564. m_pListBG->SetBounds( x, comboY, w, h + ( y - comboY ) );
  565. }
  566. MoveToCenterOfScreen();
  567. }
  568. void CAchievementsDialog::ScrollToItem( int nDirection )
  569. {
  570. if ( m_pAchievementsList->GetItemCount() > 0 )
  571. {
  572. m_nScrollItem = clamp( m_nScrollItem + nDirection, 0, m_pAchievementsList->GetItemCount() - 1 );
  573. if ( m_nOldScrollItem != m_nScrollItem )
  574. {
  575. m_nOldScrollItem = m_nScrollItem;
  576. m_pAchievementsList->ScrollToItem( m_nScrollItem );
  577. Panel *pItem = m_pAchievementsList->GetItemPanel( m_nScrollItem );
  578. if ( pItem )
  579. {
  580. int nX, nY, nWide, nTall;
  581. pItem->GetBounds( nX, nY, nWide, nTall );
  582. m_pSelectionHighlight->SetVisible( true );
  583. m_pSelectionHighlight->SetBounds( nX, nY, nWide, nTall );
  584. }
  585. }
  586. }
  587. }
  588. void CAchievementsDialog::OnKeyCodePressed( vgui::KeyCode code )
  589. {
  590. // Handle close here, CBasePanel parent doesn't support "DialogClosing" command
  591. ButtonCode_t nButtonCode = GetBaseButtonCode( code );
  592. if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B )
  593. {
  594. OnCommand( "Close" );
  595. }
  596. else if ( nButtonCode == KEY_XBUTTON_X || nButtonCode == STEAMCONTROLLER_X )
  597. {
  598. if ( m_pHideAchievedCheck && m_pHideAchievedCheck->IsVisible() )
  599. {
  600. m_pHideAchievedCheck->SetSelected( !m_pHideAchievedCheck->IsSelected() );
  601. }
  602. }
  603. else if ( nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
  604. {
  605. if ( GameSupportsAchievementTracker() && m_nScrollItem >= 0 && m_nScrollItem < m_pAchievementsList->GetItemCount() )
  606. {
  607. CAchievementDialogItemPanel *pItem = dynamic_cast< CAchievementDialogItemPanel* >( m_pAchievementsList->GetItemPanel( m_nScrollItem) );
  608. if ( pItem && pItem->IsVisible() )
  609. {
  610. pItem->ToggleShowOnHUD();
  611. }
  612. }
  613. }
  614. else if ( nButtonCode == KEY_XBUTTON_UP ||
  615. nButtonCode == KEY_XSTICK1_UP ||
  616. nButtonCode == KEY_XSTICK2_UP ||
  617. nButtonCode == STEAMCONTROLLER_DPAD_UP ||
  618. nButtonCode == KEY_UP )
  619. {
  620. ScrollToItem( -1 );
  621. }
  622. else if ( nButtonCode == KEY_XBUTTON_DOWN ||
  623. nButtonCode == KEY_XSTICK1_DOWN ||
  624. nButtonCode == KEY_XSTICK2_DOWN ||
  625. nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
  626. nButtonCode == KEY_DOWN )
  627. {
  628. ScrollToItem( 1 );
  629. }
  630. else if ( nButtonCode == KEY_XBUTTON_LEFT ||
  631. nButtonCode == KEY_XSTICK1_LEFT ||
  632. nButtonCode == KEY_XSTICK2_LEFT ||
  633. nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
  634. nButtonCode == KEY_LEFT )
  635. {
  636. m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() - 1 );
  637. }
  638. else if ( nButtonCode == KEY_XBUTTON_RIGHT ||
  639. nButtonCode == KEY_XSTICK1_RIGHT ||
  640. nButtonCode == KEY_XSTICK2_RIGHT ||
  641. nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
  642. nButtonCode == KEY_RIGHT )
  643. {
  644. m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() + 1 );
  645. }
  646. else
  647. {
  648. BaseClass::OnKeyCodePressed( code );
  649. }
  650. InvalidateLayout();
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Purpose: Each sub-panel gets its data updated
  654. //-----------------------------------------------------------------------------
  655. void CAchievementsDialog::UpdateAchievementDialogInfo( void )
  656. {
  657. int iCount = m_pAchievementsList->GetItemCount();
  658. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  659. for ( int i = 0; i < iCount; i++ )
  660. {
  661. CAchievementDialogItemPanel *pPanel = (CAchievementDialogItemPanel*)m_pAchievementsList->GetItemPanel(i);
  662. if ( pPanel )
  663. {
  664. pPanel->UpdateAchievementInfo( pScheme );
  665. }
  666. }
  667. // update the groups and overall progress bar
  668. if ( achievementmgr )
  669. {
  670. int i;
  671. // reset group completed counts
  672. for ( i=0;i<m_iNumAchievementGroups;i++ )
  673. {
  674. m_AchievementGroups[i].m_iNumUnlocked = 0;
  675. }
  676. int iAchievementCount = achievementmgr->GetAchievementCount();
  677. // update counts for each achieved achievement
  678. for ( i=0;i<iAchievementCount;i++ )
  679. {
  680. IAchievement* pCurAchievement = achievementmgr->GetAchievementByIndex( i );
  681. if ( !pCurAchievement || !pCurAchievement->IsAchieved() )
  682. continue;
  683. int iAchievementID = pCurAchievement->GetAchievementID();
  684. for ( int j=0;j<m_iNumAchievementGroups;j++ )
  685. {
  686. if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange &&
  687. iAchievementID <= m_AchievementGroups[j].m_iMaxRange )
  688. {
  689. m_AchievementGroups[j].m_iNumUnlocked++;
  690. }
  691. }
  692. }
  693. // update the global progress bar label
  694. char szPercentageText[64];
  695. float flCompletion = (((float)m_AchievementGroups[0].m_iNumUnlocked) / ((float)iAchievementCount));
  696. V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )",
  697. m_AchievementGroups[0].m_iNumUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) );
  698. // and the progress bar
  699. m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion );
  700. SetControlString( "PercentageText", szPercentageText );
  701. }
  702. CreateOrUpdateComboItems( false ); // update them with new achieved counts
  703. m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() );
  704. }
  705. void CAchievementsDialog::CreateOrUpdateComboItems( bool bCreate )
  706. {
  707. for ( int i=0;i<m_iNumAchievementGroups;i++ )
  708. {
  709. char buf[128];
  710. Q_snprintf( buf, sizeof(buf), "#Achievement_Group_%d", m_AchievementGroups[i].m_iMinRange );
  711. const wchar_t *wzGroupName = g_pVGuiLocalize->Find( buf );
  712. if ( !wzGroupName )
  713. {
  714. wzGroupName = L"Need Title ( %s1 of %s2 )";
  715. }
  716. wchar_t wzGroupTitle[128];
  717. if ( wzGroupName )
  718. {
  719. wchar_t wzNumUnlocked[8];
  720. V_snwprintf( wzNumUnlocked, ARRAYSIZE( wzNumUnlocked ), L"%d", m_AchievementGroups[i].m_iNumUnlocked );
  721. wchar_t wzNumAchievements[8];
  722. V_snwprintf( wzNumAchievements, ARRAYSIZE( wzNumAchievements ), L"%d", m_AchievementGroups[i].m_iNumAchievements );
  723. g_pVGuiLocalize->ConstructString( wzGroupTitle, sizeof( wzGroupTitle ), wzGroupName, 2, wzNumUnlocked, wzNumAchievements );
  724. }
  725. KeyValues *pKV = new KeyValues( "grp" );
  726. pKV->SetInt( "minrange", m_AchievementGroups[i].m_iMinRange );
  727. pKV->SetInt( "maxrange", m_AchievementGroups[i].m_iMaxRange );
  728. if ( bCreate )
  729. m_AchievementGroups[i].m_iDropDownGroupID = m_pAchievementPackCombo->AddItem( wzGroupTitle, pKV );
  730. else
  731. m_pAchievementPackCombo->UpdateItem( m_AchievementGroups[i].m_iDropDownGroupID, wzGroupTitle, pKV );
  732. }
  733. if ( bCreate && ( m_iNumAchievementGroups > NUM_COMBO_BOX_LINES_DEFAULT ) )
  734. {
  735. if ( m_pAchievementPackCombo )
  736. {
  737. m_pAchievementPackCombo->SetNumberOfEditLines( ( m_iNumAchievementGroups <= NUM_COMBO_BOX_LINES_MAX ) ? m_iNumAchievementGroups : NUM_COMBO_BOX_LINES_MAX );
  738. }
  739. }
  740. }
  741. void CAchievementsDialog::OnCommand( const char *command )
  742. {
  743. if ( !Q_strcasecmp( command, "ongameuiactivated" ) )
  744. {
  745. UpdateAchievementDialogInfo();
  746. }
  747. BaseClass::OnCommand( command );
  748. }
  749. //-----------------------------------------------------------------------------
  750. // Purpose: creates child panels, passes down name to pick up any settings from res files.
  751. //-----------------------------------------------------------------------------
  752. CAchievementDialogItemPanel::CAchievementDialogItemPanel( vgui::PanelListPanel *parent, const char* name, int iListItemID ) : BaseClass( parent, name )
  753. {
  754. m_iListItemID = iListItemID;
  755. m_pParent = parent;
  756. m_pSchemeSettings = NULL;
  757. SetMouseInputEnabled( true );
  758. parent->SetMouseInputEnabled( true );
  759. //CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL);
  760. //panel->SetZPos(2);
  761. }
  762. CAchievementDialogItemPanel::~CAchievementDialogItemPanel()
  763. {
  764. /*
  765. delete m_pAchievementIcon;
  766. delete m_pAchievementNameLabel;
  767. delete m_pAchievementDescLabel;
  768. delete m_pPercentageBar;
  769. delete m_pPercentageText;
  770. delete m_pShowOnHUDCheck;
  771. */
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose: Updates displayed achievement data. In applyschemesettings, and when gameui activates.
  775. //-----------------------------------------------------------------------------
  776. void CAchievementDialogItemPanel::UpdateAchievementInfo( IScheme* pScheme )
  777. {
  778. if ( m_pSourceAchievement && m_pSchemeSettings )
  779. {
  780. // Set name, description and unlocked state text
  781. m_pAchievementNameLabel->SetText( ACHIEVEMENT_LOCALIZED_NAME( m_pSourceAchievement ) );
  782. m_pAchievementDescLabel->SetText( ACHIEVEMENT_LOCALIZED_DESC( m_pSourceAchievement ) );
  783. // Setup icon
  784. // get the vtfFilename from the path.
  785. // Display percentage completion for progressive achievements
  786. // Set up total completion percentage bar. Goal > 1 means its a progress achievement.
  787. UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar );
  788. if ( m_pSourceAchievement->IsAchieved() )
  789. {
  790. LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement );
  791. SetBgColor( pScheme->GetColor( "AchievementsLightGrey", Color(255, 0, 0, 255) ) );
  792. m_pAchievementNameLabel->SetFgColor( pScheme->GetColor( "SteamLightGreen", Color(255, 255, 255, 255) ) );
  793. Color fgColor = pScheme->GetColor( "Label.TextBrightColor", Color(255, 255, 255, 255) );
  794. m_pAchievementDescLabel->SetFgColor( fgColor );
  795. m_pPercentageText->SetFgColor( fgColor );
  796. m_pShowOnHUDCheck->SetVisible( false );
  797. m_pShowOnHUDCheck->SetSelected( false );
  798. }
  799. else
  800. {
  801. LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement, "_bw" );
  802. SetBgColor( pScheme->GetColor( "AchievementsDarkGrey", Color(255, 0, 0, 255) ) );
  803. Color fgColor = pScheme->GetColor( "AchievementsInactiveFG", Color(255, 255, 255, 255) );
  804. m_pAchievementNameLabel->SetFgColor( fgColor );
  805. m_pAchievementDescLabel->SetFgColor( fgColor );
  806. m_pPercentageText->SetFgColor( fgColor );
  807. if ( GameSupportsAchievementTracker() )
  808. {
  809. m_pShowOnHUDCheck->SetVisible( !m_pSourceAchievement->ShouldHideUntilAchieved() ); // m_pSourceAchievement->GetGoal() > 1 &&
  810. m_pShowOnHUDCheck->SetSelected( m_pSourceAchievement->ShouldShowOnHUD() );
  811. }
  812. else
  813. {
  814. m_pShowOnHUDCheck->SetVisible( false );
  815. }
  816. }
  817. }
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Purpose: Makes a local copy of a pointer to the achievement entity stored on the client.
  821. //-----------------------------------------------------------------------------
  822. void CAchievementDialogItemPanel::SetAchievementInfo( IAchievement* pAchievement )
  823. {
  824. if ( !pAchievement )
  825. {
  826. Assert( 0 );
  827. return;
  828. }
  829. m_pSourceAchievement = pAchievement;
  830. m_iSourceAchievementIndex = pAchievement->GetAchievementID();
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Purpose:
  834. //-----------------------------------------------------------------------------
  835. void CAchievementDialogItemPanel::PreloadResourceFile( void )
  836. {
  837. const char *controlResourceName = "resource/ui/AchievementItem.res";
  838. g_pPreloadedAchievementItemLayout = new KeyValues(controlResourceName);
  839. g_pPreloadedAchievementItemLayout->LoadFromFile(g_pFullFileSystem, controlResourceName);
  840. }
  841. //-----------------------------------------------------------------------------
  842. // Purpose: Loads settings from hl2/resource/ui/achievementitem.res
  843. // Sets display info for this achievement item.
  844. //-----------------------------------------------------------------------------
  845. void CAchievementDialogItemPanel::ApplySchemeSettings( IScheme* pScheme )
  846. {
  847. if ( !g_pPreloadedAchievementItemLayout )
  848. {
  849. PreloadResourceFile();
  850. }
  851. LoadControlSettings( "", NULL, g_pPreloadedAchievementItemLayout );
  852. m_pSchemeSettings = pScheme;
  853. if ( !m_pSourceAchievement )
  854. {
  855. return;
  856. }
  857. m_pAchievementIcon = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("AchievementIcon")));
  858. m_pAchievementNameLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementName"));
  859. m_pAchievementDescLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementDesc"));
  860. m_pPercentageBar = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("PercentageBar")));
  861. m_pPercentageText = dynamic_cast<vgui::Label*>(FindChildByName("PercentageText"));
  862. m_pShowOnHUDCheck = dynamic_cast<vgui::CheckButton*>(FindChildByName("ShowOnHUD"));
  863. m_pShowOnHUDCheck->SetMouseInputEnabled( true );
  864. m_pShowOnHUDCheck->SetEnabled( true );
  865. m_pShowOnHUDCheck->SetCheckButtonCheckable( true );
  866. m_pShowOnHUDCheck->AddActionSignalTarget( this );
  867. BaseClass::ApplySchemeSettings( pScheme );
  868. // m_pSchemeSettings must be set for this.
  869. UpdateAchievementInfo( pScheme );
  870. }
  871. void CAchievementDialogItemPanel::ToggleShowOnHUD( void )
  872. {
  873. m_pShowOnHUDCheck->SetSelected( !m_pShowOnHUDCheck->IsSelected() );
  874. }
  875. void CAchievementDialogItemPanel::OnCheckButtonChecked(Panel *panel)
  876. {
  877. if ( GameSupportsAchievementTracker() && panel == m_pShowOnHUDCheck && m_pSourceAchievement )
  878. {
  879. m_pSourceAchievement->SetShowOnHUD( m_pShowOnHUDCheck->IsSelected() );
  880. }
  881. }