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.

1683 lines
49 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "BasePanel.h"
  7. #include "OptionsDialog.h"
  8. #include "vgui/ILocalize.h"
  9. #include "vgui/ISurface.h"
  10. #include "vgui/ISystem.h"
  11. #include "vgui/IVGui.h"
  12. #include <vgui_controls/AnalogBar.h>
  13. #ifdef _X360
  14. #include "xbox/xbox_launch.h"
  15. #endif
  16. #include "IGameUIFuncs.h"
  17. #include "GameUI_Interface.h"
  18. #include "inputsystem/iinputsystem.h"
  19. #include "EngineInterface.h"
  20. #include "KeyValues.h"
  21. #include "ModInfo.h"
  22. #include "matchmaking/matchmakingbasepanel.h"
  23. #include "vgui_controls/AnimationController.h"
  24. #include "tier1/utlbuffer.h"
  25. #include "filesystem.h"
  26. using namespace vgui;
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include <tier0/memdbgon.h>
  29. #define OPTION_STRING_LENGTH 64
  30. ConVar binds_per_command( "binds_per_command", "1", 0 );
  31. ConVar x360_resolution_widescreen_mode( "x360_resolution_widescreen_mode", "0", 0, "This is only used for reference. Changing this value does nothing" );
  32. ConVar x360_resolution_width( "x360_resolution_width", "640", 0, "This is only used for reference. Changing this value does nothing" );
  33. ConVar x360_resolution_height( "x360_resolution_height", "480", 0, "This is only used for reference. Changing this value does nothing" );
  34. ConVar x360_resolution_interlaced( "x360_resolution_interlaced", "0", 0, "This is only used for reference. Changing this value does nothing" );
  35. ConVar x360_audio_english("x360_audio_english", "0", 0, "Keeps track of whether we're forcing english in a localized language." );
  36. enum OptionType_e
  37. {
  38. OPTION_TYPE_BINARY = 0,
  39. OPTION_TYPE_SLIDER,
  40. OPTION_TYPE_CHOICE,
  41. OPTION_TYPE_BIND,
  42. OPTION_TYPE_TOTAL
  43. };
  44. enum SliderHomeType_e
  45. {
  46. SLIDER_HOME_NONE = 0,
  47. SLIDER_HOME_PREV,
  48. SLIDER_HOME_MIN,
  49. SLIDER_HOME_CENTER,
  50. SLIDER_HOME_MAX,
  51. SLIDER_HOME_TYPE_TOTAL
  52. };
  53. struct OptionChoiceData_t
  54. {
  55. char szName[ OPTION_STRING_LENGTH ];
  56. char szValue[ OPTION_STRING_LENGTH ];
  57. };
  58. struct OptionData_t
  59. {
  60. char szName[ OPTION_STRING_LENGTH ];
  61. char szDisplayName[ OPTION_STRING_LENGTH ];
  62. union
  63. {
  64. char szConvar[ OPTION_STRING_LENGTH ]; // Used by all types except binds
  65. char szCommand[ OPTION_STRING_LENGTH ]; // Used exclusively by bind types
  66. };
  67. char szConvar2[ OPTION_STRING_LENGTH ]; // Used for choice types that use 2 convars
  68. char szConvarDef[ OPTION_STRING_LENGTH ];
  69. int iPriority;
  70. OptionType_e eOptionType;
  71. bool bUnchangeable;
  72. bool bVocalsLanguage;
  73. // Slider types exclusively use these
  74. float fMinValue;
  75. float fMaxValue;
  76. float fIncValue;
  77. float fSliderHomeValue;
  78. union
  79. {
  80. SliderHomeType_e eSliderHomeType; // Slider types exclusively use this int
  81. int iCurrentChoice; // Choice types exclusively use this int
  82. int iNumBinds; // Bind types exclusively use this int
  83. };
  84. CUtlVector<OptionChoiceData_t> m_Choices;
  85. };
  86. class OptionsDataContainer
  87. {
  88. public:
  89. ~OptionsDataContainer()
  90. {
  91. for ( int iOption = 0; iOption < m_pOptions.Count(); ++iOption )
  92. {
  93. delete (m_pOptions[ iOption ]);
  94. m_pOptions[ iOption ] = 0;
  95. }
  96. for ( int iOption = 0; iOption < m_pControllerOptions.Count(); ++iOption )
  97. {
  98. delete (m_pControllerOptions[ iOption ]);
  99. m_pControllerOptions[ iOption ] = 0;
  100. }
  101. }
  102. public:
  103. CUtlVector<OptionData_t*> m_pOptions;
  104. CUtlVector<OptionData_t*> m_pControllerOptions;
  105. };
  106. static OptionsDataContainer s_OptionsDataContainer;
  107. static CUtlVector<OptionChoiceData_t> s_DisabledOptions;
  108. const char *UTIL_Parse( const char *data, char *token, int sizeofToken );
  109. bool ActionsAreTheSame( const char *pchAction1, const char *pchAction2 )
  110. {
  111. if ( Q_stricmp( pchAction1, pchAction2 ) == 0 )
  112. return true;
  113. if ( ( Q_stricmp( pchAction1, "+duck" ) == 0 || Q_stricmp( pchAction1, "toggle_duck" ) == 0 ) &&
  114. ( Q_stricmp( pchAction2, "+duck" ) == 0 || Q_stricmp( pchAction2, "toggle_duck" ) == 0 ) )
  115. {
  116. // +duck and toggle_duck are interchangable
  117. return true;
  118. }
  119. if ( ( Q_stricmp( pchAction1, "+zoom" ) == 0 || Q_stricmp( pchAction1, "toggle_zoom" ) == 0 ) &&
  120. ( Q_stricmp( pchAction2, "+zoom" ) == 0 || Q_stricmp( pchAction2, "toggle_zoom" ) == 0 ) )
  121. {
  122. // +zoom and toggle_zoom are interchangable
  123. return true;
  124. }
  125. return false;
  126. }
  127. COptionsDialogXbox::COptionsDialogXbox( vgui::Panel *parent, bool bControllerOptions ) : BaseClass( parent, "OptionsDialog" )
  128. {
  129. #ifdef _X360
  130. // Get out current resolution and stuff it into convars for later reference
  131. XVIDEO_MODE videoMode;
  132. XGetVideoMode( &videoMode );
  133. x360_resolution_widescreen_mode.SetValue( videoMode.fIsWideScreen );
  134. x360_resolution_width.SetValue( static_cast<int>( videoMode.dwDisplayWidth ) );
  135. x360_resolution_height.SetValue( static_cast<int>( videoMode.dwDisplayHeight ) );
  136. x360_resolution_interlaced.SetValue( videoMode.fIsInterlaced );
  137. #endif
  138. //Figure out which way duck is bound, and set the option_duck_method convar the correct way.
  139. const char *pDuckKey = engine->Key_LookupBinding( "+duck" );
  140. ButtonCode_t code = g_pInputSystem->StringToButtonCode( pDuckKey );
  141. const char *pDuckMode = engine->Key_BindingForKey( code );
  142. // NOW. If duck key is bound to +DUCK, set the convar to 0. Else, set it to 1.
  143. ConVarRef varOption( "option_duck_method" );
  144. if( pDuckMode != NULL )
  145. {
  146. if( !Q_stricmp(pDuckMode,"+duck") )
  147. {
  148. varOption.SetValue( 0 );
  149. }
  150. else
  151. {
  152. varOption.SetValue( 1 );
  153. }
  154. }
  155. SetSize( 32, 32 );
  156. SetDeleteSelfOnClose( true );
  157. SetTitleBarVisible( false );
  158. SetCloseButtonVisible( false );
  159. SetSizeable( false );
  160. m_pFooter = new CFooterPanel( parent, "OptionsFooter" );
  161. m_pFooter->SetStandardDialogButtons();
  162. m_bControllerOptions = bControllerOptions;
  163. m_bOptionsChanged = false;
  164. // Store the old vocal language setting
  165. m_bOldForceEnglishAudio = x360_audio_english.GetBool();
  166. // Get the correct options list
  167. if ( !m_bControllerOptions )
  168. m_pOptions = &s_OptionsDataContainer.m_pOptions;
  169. else
  170. m_pOptions = &s_OptionsDataContainer.m_pControllerOptions;
  171. if ( m_pOptions->Count() == 0 )
  172. {
  173. // Populate it if it's hasn't been filled
  174. ReadOptionsFromFile( "scripts/mod_options.360.txt" );
  175. ReadOptionsFromFile( "scripts/options.360.txt" );
  176. SortOptions();
  177. }
  178. m_pSelectedOption = NULL;
  179. m_iSelection = 0;
  180. m_iScroll = 0;
  181. m_iXAxisState = 0;
  182. m_iYAxisState = 0;
  183. m_fNextChangeTime = 0.0f;
  184. m_pOptionsSelectionLeft = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft" ) );
  185. m_pOptionsSelectionLeft2 = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft2" ) );
  186. m_pOptionsUpArrow = new vgui::Label( this, "UpArrow", "" );
  187. m_pOptionsDownArrow = new vgui::Label( this, "DownArrow", "" );
  188. for ( int iLabel = 0; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel )
  189. {
  190. char szLabelName[ 64 ];
  191. Q_snprintf( szLabelName, sizeof( szLabelName), "OptionLabel%i", iLabel );
  192. m_pOptionLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" );
  193. Q_snprintf( szLabelName, sizeof( szLabelName), "ValueLabel%i", iLabel );
  194. m_pValueLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" );
  195. Q_snprintf( szLabelName, sizeof( szLabelName), "ValueBar%i", iLabel );
  196. m_pValueBars[ iLabel ] = new AnalogBar( this, szLabelName );
  197. }
  198. // Faster repeats for sideways
  199. m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_LEFT, 0.08 );
  200. m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_RIGHT, 0.08 );
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. //-----------------------------------------------------------------------------
  205. void COptionsDialogXbox::InitializeSliderDefaults( void )
  206. {
  207. for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
  208. {
  209. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  210. if ( pOption->eOptionType != OPTION_TYPE_SLIDER )
  211. continue;
  212. if( pOption->eSliderHomeType != SLIDER_HOME_PREV )
  213. continue;
  214. const char *pszConvarName = pOption->szConvar;
  215. if ( pOption->szConvarDef && pOption->szConvarDef[0] )
  216. {
  217. // They've specified a different convar to use as the default
  218. pszConvarName = pOption->szConvarDef;
  219. }
  220. ConVarRef varOption( pszConvarName );
  221. pOption->fSliderHomeValue = varOption.GetFloat();
  222. }
  223. }
  224. COptionsDialogXbox::~COptionsDialogXbox()
  225. {
  226. if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() )
  227. {
  228. #ifdef _X360
  229. XboxLaunch()->SetForceEnglish( x360_audio_english.GetBool() );
  230. #endif
  231. PostMessage( BasePanel()->GetVPanel(), new KeyValues( "command", "command", "QuitRestartNoConfirm" ), 0.0f );
  232. }
  233. delete m_pFooter;
  234. m_pFooter = NULL;
  235. }
  236. void COptionsDialogXbox::ApplySettings( KeyValues *inResourceData )
  237. {
  238. BaseClass::ApplySettings( inResourceData );
  239. m_nButtonGap = inResourceData->GetInt( "footer_buttongap", -1 );
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. //-----------------------------------------------------------------------------
  244. void COptionsDialogXbox::ApplySchemeSettings( vgui::IScheme *pScheme )
  245. {
  246. BaseClass::ApplySchemeSettings( pScheme );
  247. KeyValues *pControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsDialog.res" );
  248. LoadControlSettings( "null", NULL, pControlSettings );
  249. if ( m_pFooter )
  250. {
  251. KeyValues *pFooterControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsFooter.res" );
  252. m_pFooter->LoadControlSettings( "null", NULL, pFooterControlSettings );
  253. }
  254. m_SelectedColor = pScheme->GetColor( "SectionedListPanel.SelectedBgColor", Color(255, 255, 255, 255) );
  255. m_pOptionsSelectionLeft->SetBgColor( m_SelectedColor );
  256. m_pOptionsSelectionLeft->SetAlpha( 96 );
  257. m_pOptionsSelectionLeft2->SetBgColor( Color(0,0,0,96) );
  258. int iX, iY;
  259. m_pOptionsSelectionLeft->GetPos( iX, m_iSelectorYStart );
  260. m_iOptionSpacing = (m_pOptionLabels[ 0 ])->GetTall();
  261. m_hLabelFont = pScheme->GetFont( "MenuLarge" );
  262. m_hButtonFont = pScheme->GetFont( "GameUIButtons" );
  263. // Decide how many items will fit
  264. int iTall = GetTall();
  265. m_iNumItems = ( iTall - 70 ) / m_iOptionSpacing;
  266. if ( m_iNumItems > m_pOptions->Count() )
  267. {
  268. // There's more space in the dialog than needed, shrink it down
  269. m_iNumItems = m_pOptions->Count();
  270. iTall = m_iNumItems * m_iOptionSpacing + 70;
  271. SetTall( iTall );
  272. }
  273. MoveToCenterOfScreen();
  274. // Adjust sizes for the number of items
  275. vgui::Panel *pPanel = FindChildByName( "OptionsBackgroundLeft" );
  276. pPanel->SetTall( iTall - 70 );
  277. pPanel = FindChildByName( "OptionsBackgroundRight" );
  278. pPanel->SetTall( iTall - 70 );
  279. m_pOptionsUpArrow->GetPos( iX, iY );
  280. m_pOptionsUpArrow->SetPos( iX, iTall - 32 );
  281. m_pOptionsDownArrow->GetPos( iX, iY );
  282. m_pOptionsDownArrow->SetPos( iX, iTall - 32 );
  283. m_pValueBars[ 0 ]->SetFgColor( Color( 255, 255, 255, 200 ) );
  284. m_pValueBars[ 0 ]->SetBgColor( Color( 255, 255, 255, 32 ) );
  285. m_pValueBars[ 0 ]->SetHomeColor( Color( m_SelectedColor[0], m_SelectedColor[1], m_SelectedColor[2], 96 ) );
  286. // Get proper setting for items for orginal in res file
  287. vgui::Panel *(pPanelList[3]) = { m_pOptionLabels[ 0 ], m_pValueLabels[ 0 ], m_pValueBars[ 0 ] };
  288. for ( int iPanelList = 0; iPanelList < 3; ++iPanelList )
  289. {
  290. pPanel = pPanelList[ iPanelList ];
  291. int iZ, iWide;
  292. bool bVisible;
  293. pPanel->GetPos( iX, iY );
  294. iZ = ipanel()->GetZPos( pPanel->GetVPanel() );
  295. iWide = pPanel->GetWide();
  296. iTall = pPanel->GetTall();
  297. bVisible = pPanel->IsVisible();
  298. for ( int iLabel = 1; iLabel < m_iNumItems; ++iLabel )
  299. {
  300. if ( iPanelList == 0 )
  301. {
  302. pPanel = m_pOptionLabels[ iLabel ];
  303. m_pOptionLabels[ iLabel ]->SetFont( m_pOptionLabels[ 0 ]->GetFont() );
  304. }
  305. else if ( iPanelList == 1 )
  306. {
  307. pPanel = m_pValueLabels[ iLabel ];
  308. m_pValueLabels[ iLabel ]->SetFont( m_pValueLabels[ 0 ]->GetFont() );
  309. }
  310. else if ( iPanelList == 2 )
  311. {
  312. pPanel = m_pValueBars[ iLabel ];
  313. m_pValueBars[ iLabel ]->SetFgColor( m_pValueBars[ 0 ]->GetFgColor() );
  314. m_pValueBars[ iLabel ]->SetBgColor( m_pValueBars[ 0 ]->GetBgColor() );
  315. m_pValueBars[ iLabel ]->SetHomeColor( m_pValueBars[ 0 ]->GetHomeColor() );
  316. }
  317. pPanel->SetPos( iX, iY + iLabel * m_iOptionSpacing );
  318. pPanel->SetZPos( iZ );
  319. pPanel->SetWide( iWide );
  320. pPanel->SetTall( iTall );
  321. pPanel->SetVisible( bVisible );
  322. }
  323. }
  324. // Make unused items invisible
  325. for ( int iLabel = m_iNumItems; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel )
  326. {
  327. m_pOptionLabels[ iLabel ]->SetVisible( false );
  328. m_pValueLabels[ iLabel ]->SetVisible( false );
  329. m_pValueBars[ iLabel ]->SetVisible( false );
  330. }
  331. InitializeSliderDefaults();
  332. UpdateScroll();
  333. DeactivateSelection();
  334. UpdateFooter();
  335. // Don't fade the background for options so that brightness can be easily adjusted
  336. if ( !m_bControllerOptions )
  337. vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 0.01f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR );
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose:
  341. //-----------------------------------------------------------------------------
  342. void COptionsDialogXbox::OnClose( void )
  343. {
  344. CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel();
  345. if ( pBase )
  346. {
  347. pBase->ShowFooter( true );
  348. }
  349. ConVarRef varOption( "option_duck_method" );
  350. char szCommand[ 256 ];
  351. for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
  352. {
  353. ButtonCode_t code = static_cast<ButtonCode_t>( iCode );
  354. const char *pDuckKeyName = gameuifuncs->GetBindingForButtonCode( code );
  355. // Check if there's a binding for this key
  356. if ( !pDuckKeyName || !pDuckKeyName[0] )
  357. continue;
  358. // If we use this binding, display the key in our list
  359. if ( ActionsAreTheSame( pDuckKeyName, "+duck" ) )
  360. {
  361. if( varOption.GetBool() )
  362. {
  363. // Bind DUCK key to toggle_duck
  364. Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "toggle_duck" );
  365. engine->ClientCmd_Unrestricted( szCommand );
  366. }
  367. else
  368. {
  369. // Bind DUCK key to +DUCK
  370. Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "+DUCK" );
  371. engine->ClientCmd_Unrestricted( szCommand );
  372. }
  373. }
  374. }
  375. // Save these settings!
  376. if ( m_bOptionsChanged )
  377. engine->ClientCmd_Unrestricted( "host_writeconfig" );
  378. BasePanel()->RunCloseAnimation( "CloseOptionsDialog_OpenMainMenu" );
  379. if ( !m_bControllerOptions )
  380. vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 120.0f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR );
  381. BaseClass::OnClose();
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void COptionsDialogXbox::OnKeyCodePressed( vgui::KeyCode code )
  387. {
  388. if ( IsX360() )
  389. {
  390. if ( GetAlpha() != 255 )
  391. {
  392. // inhibit key activity during transitions
  393. return;
  394. }
  395. }
  396. if ( !m_bSelectionActive )
  397. HandleInactiveKeyCodePressed( code );
  398. else if ( m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
  399. HandleActiveKeyCodePressed( code );
  400. else
  401. HandleBindKeyCodePressed( code );
  402. }
  403. void COptionsDialogXbox::OnCommand(const char *command)
  404. {
  405. m_KeyRepeat.Reset();
  406. if ( !Q_stricmp( command, "DefaultControls" ) )
  407. {
  408. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  409. FillInDefaultBindings();
  410. }
  411. else if ( !Q_stricmp( command, "RefreshOptions" ) )
  412. {
  413. UncacheChoices();
  414. UpdateScroll();
  415. }
  416. else if ( !Q_stricmp( command, "AcceptVocalsLanguageChange" ) )
  417. {
  418. OnClose();
  419. }
  420. else if ( !Q_stricmp( command, "CancelVocalsLanguageChange" ) )
  421. {
  422. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  423. x360_audio_english.SetValue( m_bOldForceEnglishAudio );
  424. OnCommand( "RefreshOptions" );
  425. OnClose();
  426. }
  427. else if ( !Q_stricmp( command, "ReleaseModalWindow" ) )
  428. {
  429. vgui::surface()->RestrictPaintToSinglePanel(NULL);
  430. }
  431. else
  432. {
  433. BaseClass::OnCommand(command);
  434. }
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void COptionsDialogXbox::OnKeyCodeReleased( vgui::KeyCode code )
  440. {
  441. m_KeyRepeat.KeyUp( code );
  442. BaseClass::OnKeyCodeReleased( code );
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. //-----------------------------------------------------------------------------
  447. void COptionsDialogXbox::OnThink()
  448. {
  449. vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
  450. if ( code )
  451. {
  452. OnKeyCodePressed( code );
  453. }
  454. BaseClass::OnThink();
  455. }
  456. void COptionsDialogXbox::HandleInactiveKeyCodePressed( vgui::KeyCode code )
  457. {
  458. m_KeyRepeat.KeyDown( code );
  459. OptionType_e eOptionType = (*m_pOptions)[ m_iSelection ]->eOptionType;
  460. switch( code )
  461. {
  462. // Change the selected value
  463. case KEY_XBUTTON_A:
  464. case STEAMCONTROLLER_A:
  465. if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
  466. {
  467. // Don't allow more binds if it's already maxed out
  468. if ( eOptionType == OPTION_TYPE_BIND && ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 ) )
  469. {
  470. ActivateSelection();
  471. vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
  472. }
  473. else if ( eOptionType == OPTION_TYPE_BINARY || eOptionType == OPTION_TYPE_CHOICE )
  474. {
  475. ActivateSelection();
  476. ChangeValue( 1 );
  477. DeactivateSelection();
  478. }
  479. }
  480. else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
  481. {
  482. BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
  483. }
  484. break;
  485. // To the main menu
  486. case KEY_XBUTTON_B:
  487. case STEAMCONTROLLER_B:
  488. if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() )
  489. {
  490. // Pop up a dialog to confirm changing the language
  491. vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
  492. BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_LANGUAGE_CHANGE, this );
  493. }
  494. else
  495. {
  496. OnClose();
  497. }
  498. break;
  499. // Move the selection up and down
  500. case KEY_XBUTTON_UP:
  501. case KEY_XSTICK1_UP:
  502. case STEAMCONTROLLER_DPAD_UP:
  503. ChangeSelection( -1 );
  504. break;
  505. case KEY_XBUTTON_DOWN:
  506. case KEY_XSTICK1_DOWN:
  507. case STEAMCONTROLLER_DPAD_DOWN:
  508. ChangeSelection( 1 );
  509. break;
  510. // Quickly change in the negative direction
  511. case KEY_XBUTTON_LEFT:
  512. case KEY_XSTICK1_LEFT:
  513. case STEAMCONTROLLER_DPAD_LEFT:
  514. if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
  515. {
  516. if ( eOptionType != OPTION_TYPE_BIND )
  517. {
  518. ActivateSelection();
  519. ChangeValue( -1 );
  520. DeactivateSelection();
  521. }
  522. }
  523. else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
  524. {
  525. BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
  526. }
  527. break;
  528. // Quickly change in the positive direction
  529. case KEY_XBUTTON_RIGHT:
  530. case KEY_XSTICK1_RIGHT:
  531. case STEAMCONTROLLER_DPAD_RIGHT:
  532. if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
  533. {
  534. if ( eOptionType != OPTION_TYPE_BIND )
  535. {
  536. ActivateSelection();
  537. ChangeValue( 1 );
  538. DeactivateSelection();
  539. }
  540. }
  541. else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
  542. {
  543. BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
  544. }
  545. break;
  546. case KEY_XBUTTON_X:
  547. case STEAMCONTROLLER_X:
  548. if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
  549. {
  550. if ( eOptionType == OPTION_TYPE_BIND )
  551. {
  552. if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 )
  553. {
  554. ActivateSelection();
  555. m_bOptionsChanged = true;
  556. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  557. UnbindOption( m_pSelectedOption, GetSelectionLabel() );
  558. DeactivateSelection();
  559. }
  560. }
  561. }
  562. break;
  563. case KEY_XBUTTON_Y:
  564. case STEAMCONTROLLER_Y:
  565. if ( m_bControllerOptions )
  566. {
  567. m_bOptionsChanged = true;
  568. vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
  569. BasePanel()->ShowMessageDialog( MD_DEFAULT_CONTROLS_CONFIRM, this );
  570. }
  571. else
  572. {
  573. BasePanel()->OnChangeStorageDevice();
  574. }
  575. break;
  576. default:
  577. break;
  578. }
  579. }
  580. void COptionsDialogXbox::HandleActiveKeyCodePressed( vgui::KeyCode code )
  581. {
  582. // Only binds types should become active!
  583. DeactivateSelection();
  584. }
  585. void COptionsDialogXbox::HandleBindKeyCodePressed( vgui::KeyCode code )
  586. {
  587. // Don't let stick movements be bound
  588. if ( ( code >= KEY_XSTICK1_RIGHT && code <= KEY_XSTICK1_UP ) ||
  589. ( code >= KEY_XSTICK2_RIGHT && code <= KEY_XSTICK2_UP ) )
  590. return;
  591. if ( code == KEY_XBUTTON_START )
  592. {
  593. vgui::surface()->PlaySound( "UI/buttonrollover.wav" );
  594. UpdateValue( m_pSelectedOption, GetSelectionLabel() );
  595. }
  596. else
  597. ChangeValue( static_cast<int>( code ) );
  598. DeactivateSelection();
  599. }
  600. void COptionsDialogXbox::ActivateSelection( void )
  601. {
  602. m_bSelectionActive = true;
  603. m_pSelectedOption = (*m_pOptions)[ m_iSelection ];
  604. if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
  605. return;
  606. m_KeyRepeat.Reset();
  607. m_pOptionsSelectionLeft->SetAlpha( 255 );
  608. int iLabel = GetSelectionLabel();
  609. m_pOptionLabels[ iLabel ]->SetFgColor( Color( 0, 0, 0, 255 ) );
  610. m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
  611. m_pValueLabels[ iLabel ]->SetText( "#GameUI_SetNewButton" );
  612. UpdateFooter();
  613. }
  614. void COptionsDialogXbox::DeactivateSelection( void )
  615. {
  616. m_bSelectionActive = false;
  617. if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
  618. return;
  619. m_pOptionsSelectionLeft->SetAlpha( 96 );
  620. int iLabel = GetSelectionLabel();
  621. m_pOptionLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) );
  622. m_pValueLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) );
  623. UpdateFooter();
  624. }
  625. void COptionsDialogXbox::ChangeSelection( int iChange )
  626. {
  627. m_iSelection += iChange;
  628. if ( m_iSelection < 0 )
  629. {
  630. m_iSelection = 0;
  631. vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
  632. }
  633. else if ( m_iSelection >= m_pOptions->Count() )
  634. {
  635. m_iSelection = m_pOptions->Count() - 1;
  636. vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
  637. }
  638. else
  639. {
  640. vgui::surface()->PlaySound( "UI/buttonrollover.wav" );
  641. }
  642. // Make sure the selected item in in the window
  643. if ( m_iSelection < m_iScroll )
  644. {
  645. m_iScroll = m_iSelection;
  646. UpdateScroll();
  647. }
  648. else if ( GetSelectionLabel() >= m_iNumItems )
  649. {
  650. m_iScroll = m_iSelection - m_iNumItems + 1;
  651. UpdateScroll();
  652. }
  653. UpdateFooter();
  654. UpdateSelection();
  655. }
  656. void COptionsDialogXbox::UpdateFooter( void )
  657. {
  658. m_pFooter->ClearButtons();
  659. if ( !m_bSelectionActive )
  660. {
  661. if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
  662. {
  663. switch ( (*m_pOptions)[ m_iSelection ]->eOptionType )
  664. {
  665. case OPTION_TYPE_BINARY:
  666. m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" );
  667. break;
  668. case OPTION_TYPE_SLIDER:
  669. m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" );
  670. break;
  671. case OPTION_TYPE_CHOICE:
  672. m_pFooter->AddNewButtonLabel( ( (*m_pOptions)[ m_iSelection ]->m_Choices.Count() == 2 ) ? ( "#GameUI_Toggle" ) : ( "#GameUI_Modify" ), "#GameUI_Icons_DPAD" );
  673. break;
  674. case OPTION_TYPE_BIND:
  675. {
  676. if ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 )
  677. m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_A_BUTTON" );
  678. if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 )
  679. m_pFooter->AddNewButtonLabel( "#GameUI_ClearButton", "#GameUI_Icons_X_BUTTON" );
  680. break;
  681. }
  682. }
  683. }
  684. if ( m_bControllerOptions )
  685. m_pFooter->AddNewButtonLabel( "#GameUI_DefaultButtons", "#GameUI_Icons_Y_BUTTON" );
  686. else
  687. m_pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" );
  688. m_pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" );
  689. }
  690. else if ( m_pSelectedOption )
  691. {
  692. switch ( m_pSelectedOption->eOptionType )
  693. {
  694. case OPTION_TYPE_BIND:
  695. m_pFooter->AddNewButtonLabel( "#GameUI_Cancel", "#GameUI_Icons_START" );
  696. break;
  697. }
  698. }
  699. if ( m_nButtonGap > 0 )
  700. {
  701. m_pFooter->SetButtonGap( m_nButtonGap );
  702. }
  703. else
  704. {
  705. m_pFooter->UseDefaultButtonGap();
  706. }
  707. CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel();
  708. if ( pBase )
  709. {
  710. pBase->ShowFooter( false );
  711. }
  712. }
  713. void COptionsDialogXbox::UpdateSelection( void )
  714. {
  715. int iYPos = m_iSelectorYStart + m_iOptionSpacing * ( GetSelectionLabel() );
  716. int iX, iY;
  717. m_pOptionsSelectionLeft->GetPos( iX, iY );
  718. m_pOptionsSelectionLeft->SetPos( iX, iYPos );
  719. m_pOptionsSelectionLeft2->GetPos( iX, iY );
  720. m_pOptionsSelectionLeft2->SetPos( iX, iYPos + 2 );
  721. }
  722. void COptionsDialogXbox::UpdateScroll( void )
  723. {
  724. for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
  725. {
  726. int iOption = m_iScroll + iLabel;
  727. if ( iOption >= m_pOptions->Count() )
  728. break;
  729. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  730. m_pOptionLabels[ iLabel ]->SetText( pOption->szDisplayName );
  731. UpdateValue( pOption, iLabel );
  732. if ( pOption->bUnchangeable )
  733. {
  734. m_pOptionLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) );
  735. m_pValueLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) );
  736. }
  737. else
  738. {
  739. m_pOptionLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) );
  740. m_pValueLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) );
  741. }
  742. }
  743. // Draw arrows if there's more items to scroll to
  744. m_pOptionsUpArrow->SetAlpha( ( m_iScroll > 0 ) ? ( 255 ) : ( 64 ) );
  745. m_pOptionsDownArrow->SetAlpha( ( m_iScroll + m_iNumItems < m_pOptions->Count() ) ? ( 255 ) : ( 64 ) );
  746. }
  747. void COptionsDialogXbox::UncacheChoices( void )
  748. {
  749. for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
  750. {
  751. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  752. if ( pOption->eOptionType != OPTION_TYPE_CHOICE )
  753. continue;
  754. pOption->iCurrentChoice = -1;
  755. }
  756. }
  757. void COptionsDialogXbox::GetChoiceFromConvar( OptionData_t *pOption )
  758. {
  759. if ( pOption->iCurrentChoice < 0 )
  760. {
  761. // Don't have a proper choice yet, so see if the convars value matches one of our choices
  762. if ( pOption->szConvar2[ 0 ] == '\0' )
  763. {
  764. ConVarRef varOption( pOption->szConvar );
  765. const char *pchValue = varOption.GetString();
  766. // Only one convar to check
  767. for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice )
  768. {
  769. if ( Q_stricmp( pOption->m_Choices[ iChoice ].szValue, pchValue ) == 0 )
  770. {
  771. pOption->iCurrentChoice = iChoice;
  772. break;
  773. }
  774. // We need to compare values in case we have "0" & "0.00000".
  775. if ( (pchValue[0] >= '0' && pchValue[0] <= '9') || pchValue[0] == '-' )
  776. {
  777. float flVal = atof(pchValue);
  778. float flChoiceVal = atof(pOption->m_Choices[ iChoice ].szValue);
  779. if ( flVal == flChoiceVal )
  780. {
  781. pOption->iCurrentChoice = iChoice;
  782. break;
  783. }
  784. }
  785. }
  786. }
  787. else
  788. {
  789. // Two convars to contend with
  790. ConVarRef varOption( pOption->szConvar );
  791. ConVarRef varOption2( pOption->szConvar2 );
  792. const char *pchValue = varOption.GetString();
  793. const char *pchValue2 = varOption2.GetString();
  794. for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice )
  795. {
  796. char szOptionValue[ OPTION_STRING_LENGTH ];
  797. Q_strncpy( szOptionValue, pOption->m_Choices[ iChoice ].szValue, sizeof( szOptionValue ) );
  798. char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) );
  799. AssertMsg( pchOptionValue2, "Option uses 2 convars but an option doesn't have 2 ; separated values!" );
  800. pchOptionValue2[ 0 ] = '\0'; // Set this as the end of the other value
  801. ++pchOptionValue2; // Point at next char
  802. if ( Q_stricmp( szOptionValue, pchValue ) == 0 && Q_stricmp( pchOptionValue2, pchValue2 ) == 0 )
  803. {
  804. pOption->iCurrentChoice = iChoice;
  805. break;
  806. }
  807. }
  808. }
  809. }
  810. }
  811. #if !defined(_X360)
  812. // BUGBUG: This won't compile because it includes windows.h and this interface function is #defined to something else
  813. // see below system()->GetCurrentTime
  814. #undef GetCurrentTime
  815. #endif
  816. void COptionsDialogXbox::ChangeValue( float fChange )
  817. {
  818. switch ( m_pSelectedOption->eOptionType )
  819. {
  820. case OPTION_TYPE_BINARY:
  821. {
  822. ConVarRef varOption( m_pSelectedOption->szConvar );
  823. varOption.SetValue( !varOption.GetBool() );
  824. UpdateValue( m_pSelectedOption, GetSelectionLabel() );
  825. m_bOptionsChanged = true;
  826. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  827. break;
  828. }
  829. case OPTION_TYPE_SLIDER:
  830. {
  831. if ( system()->GetCurrentTime() > m_fNextChangeTime )
  832. {
  833. ConVarRef varOption( m_pSelectedOption->szConvar );
  834. float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount();
  835. if ( fNumSegments <= 0.0f )
  836. fNumSegments = 1.0f;
  837. float fIncValue;
  838. if ( m_pSelectedOption->fIncValue == 0.0f )
  839. fIncValue = ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments );
  840. else
  841. fIncValue = m_pSelectedOption->fIncValue * ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments );
  842. fIncValue *= fChange;
  843. float fOldValue = varOption.GetFloat();
  844. float fValue = clamp( fOldValue + fIncValue, m_pSelectedOption->fMinValue, m_pSelectedOption->fMaxValue );
  845. if ( fOldValue != fValue )
  846. {
  847. m_bOptionsChanged = true;
  848. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  849. varOption.SetValue( fValue );
  850. UpdateValue( m_pSelectedOption, GetSelectionLabel() );
  851. }
  852. else
  853. {
  854. // Play the invalid sound
  855. vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
  856. }
  857. }
  858. break;
  859. }
  860. case OPTION_TYPE_CHOICE:
  861. {
  862. GetChoiceFromConvar( m_pSelectedOption );
  863. if ( m_pSelectedOption->iCurrentChoice >= 0 )
  864. {
  865. m_pSelectedOption->iCurrentChoice += fChange;
  866. while ( m_pSelectedOption->iCurrentChoice >= m_pSelectedOption->m_Choices.Count() )
  867. m_pSelectedOption->iCurrentChoice -= m_pSelectedOption->m_Choices.Count();
  868. while ( m_pSelectedOption->iCurrentChoice < 0 )
  869. m_pSelectedOption->iCurrentChoice += m_pSelectedOption->m_Choices.Count();
  870. if ( m_pSelectedOption->szConvar2[ 0 ] == '\0' )
  871. {
  872. // Only one convar to set
  873. ConVarRef varOption( m_pSelectedOption->szConvar );
  874. varOption.SetValue( m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue );
  875. }
  876. else
  877. {
  878. // Two convars to deal with
  879. char szOptionValue[ OPTION_STRING_LENGTH ];
  880. Q_strncpy( szOptionValue, m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue, sizeof( szOptionValue ) );
  881. char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) );
  882. AssertMsg( pchOptionValue2, "Option uses to convars but an option doesn't have 2 ; separated values!" );
  883. pchOptionValue2[ 0 ] = '\0'; // Set this as the end of the other value
  884. ++pchOptionValue2; // Point at next char
  885. ConVarRef varOption( m_pSelectedOption->szConvar );
  886. ConVarRef varOption2( m_pSelectedOption->szConvar2 );
  887. varOption.SetValue( szOptionValue );
  888. varOption2.SetValue( pchOptionValue2 );
  889. }
  890. }
  891. else
  892. {
  893. DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", m_pSelectedOption->szConvar, m_pSelectedOption->szDisplayName );
  894. }
  895. UpdateValue( m_pSelectedOption, GetSelectionLabel() );
  896. m_bOptionsChanged = true;
  897. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  898. break;
  899. }
  900. case OPTION_TYPE_BIND:
  901. {
  902. // If we only allow one bind replace the previous
  903. if ( binds_per_command.GetInt() == 1 )
  904. UnbindOption( m_pSelectedOption, GetSelectionLabel() );
  905. ButtonCode_t code = static_cast<ButtonCode_t>( static_cast<int>( fChange ) );
  906. char szCommand[ 256 ];
  907. Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), m_pSelectedOption->szCommand );
  908. engine->ClientCmd_Unrestricted( szCommand );
  909. // After binding we need to update all bindings so they display the correct keys
  910. UpdateAllBinds( code );
  911. m_bOptionsChanged = true;
  912. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  913. break;
  914. }
  915. }
  916. }
  917. void COptionsDialogXbox::UnbindOption( OptionData_t *pOption, int iLabel )
  918. {
  919. if ( pOption->eOptionType != OPTION_TYPE_BIND )
  920. return;
  921. for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
  922. {
  923. ButtonCode_t code = static_cast<ButtonCode_t>( iCode );
  924. const char *pBinding = gameuifuncs->GetBindingForButtonCode( code );
  925. // Check if there's a binding for this key
  926. if ( !pBinding || !pBinding[0] )
  927. continue;
  928. // If we use this binding, display the key in our list
  929. if ( ActionsAreTheSame( pBinding, pOption->szCommand ) )
  930. {
  931. char szCommand[ 256 ];
  932. Q_snprintf( szCommand, sizeof( szCommand ), "unbind %s", g_pInputSystem->ButtonCodeToString( code ) );
  933. engine->ClientCmd_Unrestricted( szCommand );
  934. }
  935. }
  936. pOption->iNumBinds = 0;
  937. if ( iLabel < 0 || iLabel >= m_iNumItems )
  938. return;
  939. m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
  940. m_pValueLabels[ iLabel ]->SetText( "" );
  941. }
  942. void COptionsDialogXbox::UpdateValue( OptionData_t *pOption, int iLabel )
  943. {
  944. if ( pOption->bVocalsLanguage )
  945. {
  946. // Can't change vocal language while in game
  947. pOption->bUnchangeable = GameUI().IsInLevel();
  948. }
  949. switch ( pOption->eOptionType )
  950. {
  951. case OPTION_TYPE_BINARY:
  952. {
  953. ConVarRef varOption( pOption->szConvar );
  954. m_pValueBars[ iLabel ]->SetVisible( false );
  955. m_pValueLabels[ iLabel ]->SetVisible( true );
  956. m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
  957. m_pValueLabels[ iLabel ]->SetText( ( varOption.GetBool() ) ? ( "#GameUI_Enable" ) : ( "#GameUI_Disable" ) );
  958. break;
  959. }
  960. case OPTION_TYPE_SLIDER:
  961. {
  962. ConVarRef varOption( pOption->szConvar );
  963. m_pValueLabels[ iLabel ]->SetVisible( false );
  964. m_pValueBars[ iLabel ]->SetVisible( true );
  965. // If it's very close to the home value set it as the home value
  966. float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount();
  967. if ( fNumSegments <= 0.0f )
  968. fNumSegments = 1.0f;
  969. float fIncValue;
  970. if ( pOption->fIncValue == 0.0f )
  971. fIncValue = ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments );
  972. else
  973. fIncValue = pOption->fIncValue * ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments );
  974. float fNormalizeHome = fabsf( ( pOption->fMinValue - pOption->fSliderHomeValue ) / ( pOption->fMaxValue - pOption->fMinValue ) );
  975. if ( pOption->fIncValue < 0.0f )
  976. fNormalizeHome = 1.0f - fNormalizeHome;
  977. m_pValueBars[ iLabel ]->SetHomeValue( fNormalizeHome );
  978. float fNormalizeValue = fabsf( ( pOption->fMinValue - varOption.GetFloat() ) / ( pOption->fMaxValue - pOption->fMinValue ) );
  979. if ( pOption->fIncValue < 0.0f )
  980. fNormalizeValue = 1.0f - fNormalizeValue;
  981. // atof accuracy for convar isn't so good, so snap to the home value if we're near it.
  982. if ( fabsf( fNormalizeValue - fNormalizeHome ) < fabsf( fIncValue * 0.1f ) )
  983. fNormalizeValue = fNormalizeHome;
  984. m_pValueBars[ iLabel ]->SetAnalogValue( fNormalizeValue );
  985. break;
  986. }
  987. case OPTION_TYPE_CHOICE:
  988. {
  989. GetChoiceFromConvar( pOption );
  990. m_pValueBars[ iLabel ]->SetVisible( false );
  991. m_pValueLabels[ iLabel ]->SetVisible( true );
  992. m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
  993. if ( pOption->iCurrentChoice >= 0 )
  994. {
  995. m_pValueLabels[ iLabel ]->SetText( pOption->m_Choices[ pOption->iCurrentChoice ].szName );
  996. }
  997. else
  998. {
  999. if ( pOption->bUnchangeable )
  1000. {
  1001. if ( pOption->szConvar2[ 0 ] == '\0' )
  1002. {
  1003. ConVarRef varOption( pOption->szConvar );
  1004. m_pValueLabels[ iLabel ]->SetText( varOption.GetString() );
  1005. }
  1006. else
  1007. {
  1008. // This is used for displaying the resolution settings
  1009. ConVarRef varOption( pOption->szConvar );
  1010. ConVarRef varOption2( pOption->szConvar2 );
  1011. char szBuff[ 256 ];
  1012. Q_snprintf( szBuff, sizeof( szBuff ), "%sx%s%s", varOption.GetString(), varOption2.GetString(), ( x360_resolution_interlaced.GetBool() ) ? ( "i" ) : ( "p" ) );
  1013. m_pValueLabels[ iLabel ]->SetText( szBuff );
  1014. }
  1015. }
  1016. else
  1017. {
  1018. DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", pOption->szConvar, pOption->szDisplayName );
  1019. m_pValueLabels[ iLabel ]->SetText( "#GameUI_NoOptionsYet" );
  1020. }
  1021. }
  1022. break;
  1023. }
  1024. case OPTION_TYPE_BIND:
  1025. {
  1026. UpdateBind( pOption, iLabel );
  1027. break;
  1028. }
  1029. }
  1030. }
  1031. void COptionsDialogXbox::UpdateBind( OptionData_t *pOption, int iLabel, ButtonCode_t codeIgnore, ButtonCode_t codeAdd )
  1032. {
  1033. int iNumBinds = 0;
  1034. char szBinds[ OPTION_STRING_LENGTH ];
  1035. char szBuff[ 512 ];
  1036. wchar_t szWideBuff[ 64 ];
  1037. for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
  1038. {
  1039. ButtonCode_t code = static_cast<ButtonCode_t>( iCode );
  1040. // Don't show this key in our list
  1041. if ( code == codeIgnore )
  1042. continue;
  1043. bool bUseThisKey = ( codeAdd == code );
  1044. if ( !bUseThisKey )
  1045. {
  1046. // If this binding is being replaced only allow the new binding to show
  1047. if ( binds_per_command.GetInt() == 1 && codeAdd != BUTTON_CODE_INVALID )
  1048. continue;
  1049. // Only check against bind name if we haven't already forced this binding to be used
  1050. const char *pBinding = gameuifuncs->GetBindingForButtonCode( code );
  1051. if ( !pBinding )
  1052. continue;
  1053. bUseThisKey = ActionsAreTheSame( pBinding, pOption->szCommand );
  1054. }
  1055. // Don't use this bind in out list
  1056. if ( !bUseThisKey )
  1057. continue;
  1058. // Turn localized string into icon character
  1059. Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", g_pInputSystem->ButtonCodeToString( static_cast<ButtonCode_t>( iCode ) ) );
  1060. g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 );
  1061. g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );
  1062. // Add this icon to our list of keys to display
  1063. szBinds[ iNumBinds ] = szBuff[ 0 ];
  1064. ++iNumBinds;
  1065. }
  1066. if ( iNumBinds == 0 )
  1067. {
  1068. // No keys for this bind
  1069. pOption->iNumBinds = 0;
  1070. m_pValueBars[ iLabel ]->SetVisible( false );
  1071. m_pValueLabels[ iLabel ]->SetVisible( true );
  1072. m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
  1073. m_pValueLabels[ iLabel ]->SetText( "" );
  1074. }
  1075. else
  1076. {
  1077. // Show icons for list of keys
  1078. szBinds[ iNumBinds ] = '\0';
  1079. pOption->iNumBinds = iNumBinds;
  1080. m_pValueBars[ iLabel ]->SetVisible( false );
  1081. m_pValueLabels[ iLabel ]->SetVisible( true );
  1082. m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont );
  1083. m_pValueLabels[ iLabel ]->SetText( szBinds );
  1084. }
  1085. }
  1086. void COptionsDialogXbox::UpdateAllBinds( ButtonCode_t code )
  1087. {
  1088. // Loop through all the items
  1089. for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
  1090. {
  1091. int iOption = m_iScroll + iLabel;
  1092. if ( iOption >= m_pOptions->Count() )
  1093. break;
  1094. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  1095. if ( pOption->eOptionType == OPTION_TYPE_BIND )
  1096. {
  1097. if ( pOption == m_pSelectedOption )
  1098. {
  1099. // We just added a key to this binding so add it to the list
  1100. UpdateBind( pOption, iLabel, BUTTON_CODE_INVALID, code );
  1101. }
  1102. else
  1103. {
  1104. // We just added a key so don't let it show up for any other bindings
  1105. UpdateBind( pOption, iLabel, code );
  1106. }
  1107. }
  1108. }
  1109. }
  1110. //-----------------------------------------------------------------------------
  1111. // Purpose: Read in defaults from game's default config file and populate list
  1112. // using those defaults
  1113. //-----------------------------------------------------------------------------
  1114. void COptionsDialogXbox::FillInDefaultBindings( void )
  1115. {
  1116. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1117. if ( !g_pFullFileSystem->ReadFile( "cfg/config.360.cfg", NULL, buf ) )
  1118. return;
  1119. // Clear out all current bindings
  1120. for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
  1121. UnbindOption( (*m_pOptions)[ iOption ], iOption - m_iScroll );
  1122. const char *data = (const char*)buf.Base();
  1123. // loop through all the binding
  1124. while ( data != NULL )
  1125. {
  1126. char cmd[64];
  1127. data = UTIL_Parse( data, cmd, sizeof(cmd) );
  1128. if ( strlen( cmd ) <= 0 )
  1129. break;
  1130. if ( !stricmp(cmd, "bind") )
  1131. {
  1132. // Key name
  1133. char szKeyName[256];
  1134. data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) );
  1135. if ( strlen( szKeyName ) <= 0 )
  1136. break; // Error
  1137. char szBinding[256];
  1138. data = UTIL_Parse( data, szBinding, sizeof(szBinding) );
  1139. if ( strlen( szKeyName ) <= 0 )
  1140. break; // Error
  1141. // Bind it
  1142. char szCommand[ 256 ];
  1143. Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", szKeyName, szBinding );
  1144. engine->ClientCmd_Unrestricted( szCommand );
  1145. // Loop through all the items
  1146. for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
  1147. {
  1148. int iOption = m_iScroll + iLabel;
  1149. if ( iOption >= m_pOptions->Count() )
  1150. break;
  1151. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  1152. // Check if this bind is for this option
  1153. if ( pOption->eOptionType == OPTION_TYPE_BIND && ActionsAreTheSame( pOption->szCommand, szBinding ) )
  1154. {
  1155. char szBuff[ 512 ];
  1156. wchar_t szWideBuff[ 64 ];
  1157. char szBinds[ OPTION_STRING_LENGTH ];
  1158. if ( pOption->iNumBinds > 0 )
  1159. m_pValueLabels[ iLabel ]->GetText( szBinds, sizeof( szBinds ) );
  1160. // Turn localized string into icon character
  1161. Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", szKeyName );
  1162. g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 );
  1163. g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );
  1164. // Add this icon to our list of keys to display
  1165. szBinds[ pOption->iNumBinds ] = szBuff[ 0 ];
  1166. ++pOption->iNumBinds;
  1167. // Show icons for list of keys
  1168. szBinds[ pOption->iNumBinds ] = '\0';
  1169. m_pValueBars[ iLabel ]->SetVisible( false );
  1170. m_pValueLabels[ iLabel ]->SetVisible( true );
  1171. m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont );
  1172. m_pValueLabels[ iLabel ]->SetText( szBinds );
  1173. }
  1174. }
  1175. }
  1176. }
  1177. // Reset any options with default convar values
  1178. for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
  1179. {
  1180. int iOption = m_iScroll + iLabel;
  1181. if ( iOption >= m_pOptions->Count() )
  1182. break;
  1183. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  1184. if ( pOption->szConvarDef && pOption->szConvarDef[0] )
  1185. {
  1186. ConVarRef varDefault( pOption->szConvarDef );
  1187. ConVarRef varOption( pOption->szConvar );
  1188. varOption.SetValue( varDefault.GetFloat() );
  1189. pOption->iCurrentChoice = -1;
  1190. UpdateValue( pOption, iLabel );
  1191. }
  1192. }
  1193. }
  1194. bool COptionsDialogXbox::ShouldSkipOption( KeyValues *pKey )
  1195. {
  1196. // Skip the option if not in developer mode and this is a developer only option
  1197. ConVarRef developer( "developer" );
  1198. if ( !developer.GetBool() && ( pKey->GetInt( "dev", 0 ) != 0 ) )
  1199. return true;
  1200. // Skip the option if it doesn't match the controller/non-controller list
  1201. if ( m_bControllerOptions != ( pKey->GetInt( "control", 0 ) != 0 ) )
  1202. return true;
  1203. // Skip portal options if this mod doesn't have portals (defined in gameinfo.txt)
  1204. if ( !ModInfo().HasPortals() && ( pKey->GetInt( "portals", 0 ) != 0 ) )
  1205. return true;
  1206. // Skip multiplayer only options for single player (or combo) games
  1207. if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "multiplayer", 0 ) != 0 ) )
  1208. return true;
  1209. // Skip difficulty options for games that don't want them
  1210. if ( !( ModInfo().IsSinglePlayerOnly() && !ModInfo().NoDifficulty() ) && ( pKey->GetInt( "difficulty", 0 ) != 0 ) )
  1211. return true;
  1212. // Skip voice options for single player games
  1213. if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "voice", 0 ) != 0 ) )
  1214. return true;
  1215. // Skip if it's the vocal language option but we're not in german or french
  1216. if ( pKey->GetInt( "vocalslanguage", 0 ) != 0 )
  1217. {
  1218. if ( !XBX_IsLocalized() )
  1219. {
  1220. return true;
  1221. }
  1222. }
  1223. // Don't create options if there's already an entry by the same name
  1224. char szName[ OPTION_STRING_LENGTH ];
  1225. Q_strncpy( szName, pKey->GetName(), sizeof( szName ) );
  1226. for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
  1227. {
  1228. OptionData_t *pOption = (*m_pOptions)[ iOption ];
  1229. if ( Q_strcmp( pOption->szName, szName ) == 0 )
  1230. return true;
  1231. }
  1232. for ( int iOption = 0; iOption < s_DisabledOptions.Count(); ++iOption )
  1233. {
  1234. OptionChoiceData_t *pOption = &(s_DisabledOptions[ iOption ]);
  1235. if ( Q_strcmp( pOption->szName, szName ) == 0 )
  1236. return true;
  1237. }
  1238. return false;
  1239. }
  1240. void COptionsDialogXbox::ReadOptionsFromFile( const char *pchFileName )
  1241. {
  1242. KeyValues *pOptionKeys = new KeyValues( "options_x360" );
  1243. pOptionKeys->LoadFromFile( g_pFullFileSystem, pchFileName, NULL );
  1244. KeyValues *pKey = NULL;
  1245. for ( pKey = pOptionKeys->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
  1246. {
  1247. // Skip disabled options
  1248. if ( pKey->GetInt( "disable", 0 ) != 0 )
  1249. {
  1250. // Remember disabled options so we don't create another with the same name
  1251. int iDisabledOption = s_DisabledOptions.AddToTail();
  1252. OptionChoiceData_t *pDisabledOption = &(s_DisabledOptions[ iDisabledOption ]);
  1253. Q_strncpy( pDisabledOption->szName, pKey->GetName(), sizeof( pDisabledOption->szName ) );
  1254. continue;
  1255. }
  1256. if ( ShouldSkipOption( pKey ) )
  1257. continue;
  1258. int iOption = m_pOptions->AddToTail();
  1259. OptionData_t **pNewOption = &((*m_pOptions)[ iOption ]);
  1260. *pNewOption = new OptionData_t;
  1261. // Get common values
  1262. Q_strncpy( (*pNewOption)->szName, pKey->GetName(), sizeof( (*pNewOption)->szName ) );
  1263. Q_strncpy( (*pNewOption)->szDisplayName, pKey->GetString( "name", "" ), sizeof( (*pNewOption)->szDisplayName ) );
  1264. Q_strncpy( (*pNewOption)->szConvar, pKey->GetString( "convar", "" ), sizeof( (*pNewOption)->szConvar ) );
  1265. if ( (*pNewOption)->szConvar[ 0 ] == '\0' )
  1266. Q_strncpy( (*pNewOption)->szCommand, pKey->GetString( "command", "" ), sizeof( (*pNewOption)->szCommand ) );
  1267. Q_strncpy( (*pNewOption)->szConvarDef, pKey->GetString( "convar_def", "" ), sizeof( (*pNewOption)->szConvarDef ) );
  1268. (*pNewOption)->iPriority = pKey->GetInt( "priority", 0 );
  1269. (*pNewOption)->bUnchangeable = ( pKey->GetInt( "unchangeable", 0 ) != 0 );
  1270. (*pNewOption)->bVocalsLanguage = ( pKey->GetInt( "vocalslanguage", 0 ) != 0 );
  1271. // Determine the type
  1272. char szType[ OPTION_STRING_LENGTH ];
  1273. Q_strncpy( szType, pKey->GetString( "type", "binary" ), sizeof( szType ) );
  1274. if ( Q_stricmp( szType, "binary" ) == 0 )
  1275. (*pNewOption)->eOptionType = OPTION_TYPE_BINARY;
  1276. else if ( Q_stricmp( szType, "slider" ) == 0 )
  1277. (*pNewOption)->eOptionType = OPTION_TYPE_SLIDER;
  1278. else if ( Q_stricmp( szType, "choice" ) == 0 )
  1279. (*pNewOption)->eOptionType = OPTION_TYPE_CHOICE;
  1280. else if ( Q_stricmp( szType, "bind" ) == 0 )
  1281. (*pNewOption)->eOptionType = OPTION_TYPE_BIND;
  1282. // Get type specific values
  1283. switch ( (*pNewOption)->eOptionType )
  1284. {
  1285. case OPTION_TYPE_SLIDER:
  1286. {
  1287. (*pNewOption)->fMinValue = pKey->GetFloat( "minvalue", 0.0f );
  1288. (*pNewOption)->fMaxValue = pKey->GetFloat( "maxvalue", 1.0f );
  1289. (*pNewOption)->fIncValue = pKey->GetFloat( "incvalue", 0.0f );
  1290. char szSliderHomeType[ OPTION_STRING_LENGTH ];
  1291. Q_strncpy( szSliderHomeType, pKey->GetString( "sliderhome", "none" ), sizeof( szSliderHomeType ) );
  1292. if ( Q_stricmp( szSliderHomeType, "prev" ) == 0 )
  1293. (*pNewOption)->eSliderHomeType = SLIDER_HOME_PREV;
  1294. else if ( Q_stricmp( szSliderHomeType, "min" ) == 0 )
  1295. (*pNewOption)->eSliderHomeType = SLIDER_HOME_MIN;
  1296. else if ( Q_stricmp( szSliderHomeType, "center" ) == 0 )
  1297. (*pNewOption)->eSliderHomeType = SLIDER_HOME_CENTER;
  1298. else if ( Q_stricmp( szSliderHomeType, "max" ) == 0 )
  1299. (*pNewOption)->eSliderHomeType = SLIDER_HOME_MAX;
  1300. else
  1301. (*pNewOption)->eSliderHomeType = SLIDER_HOME_NONE;
  1302. switch ( (*pNewOption)->eSliderHomeType )
  1303. {
  1304. case SLIDER_HOME_MIN:
  1305. (*pNewOption)->fSliderHomeValue = (*pNewOption)->fMinValue;
  1306. break;
  1307. case SLIDER_HOME_CENTER:
  1308. (*pNewOption)->fSliderHomeValue = ( (*pNewOption)->fMaxValue + (*pNewOption)->fMinValue ) * 0.5f;
  1309. break;
  1310. case SLIDER_HOME_MAX:
  1311. (*pNewOption)->fSliderHomeValue = (*pNewOption)->fMaxValue;
  1312. break;
  1313. default:
  1314. (*pNewOption)->fSliderHomeValue = 0.0f;
  1315. }
  1316. break;
  1317. }
  1318. case OPTION_TYPE_CHOICE:
  1319. {
  1320. Q_strncpy( (*pNewOption)->szConvar2, pKey->GetString( "convar2", "" ), sizeof( (*pNewOption)->szConvar ) );
  1321. if ( (*pNewOption)->bVocalsLanguage )
  1322. {
  1323. (*pNewOption)->iCurrentChoice = -1;
  1324. int iChoice = (*pNewOption)->m_Choices.AddToTail();
  1325. OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);
  1326. Q_strncpy( pNewOptionChoice->szName, "#GameUI_Language_English", sizeof( pNewOptionChoice->szName ) );
  1327. Q_strncpy( pNewOptionChoice->szValue, "1", sizeof( pNewOptionChoice->szValue ) );
  1328. iChoice = (*pNewOption)->m_Choices.AddToTail();
  1329. pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);
  1330. char szLanguage[ 256 ];
  1331. Q_snprintf( szLanguage, sizeof( szLanguage ), "#GameUI_Language_%s", XBX_GetLanguageString() );
  1332. Q_strncpy( pNewOptionChoice->szName, szLanguage, sizeof( pNewOptionChoice->szName ) );
  1333. Q_strncpy( pNewOptionChoice->szValue, "0", sizeof( pNewOptionChoice->szValue ) );
  1334. }
  1335. else
  1336. {
  1337. (*pNewOption)->iCurrentChoice = -1;
  1338. KeyValues *pChoicesKey = pKey->FindKey( "choices" );
  1339. if ( pChoicesKey )
  1340. {
  1341. KeyValues *pSubKey = NULL;
  1342. for ( pSubKey = pChoicesKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() )
  1343. {
  1344. int iChoice = (*pNewOption)->m_Choices.AddToTail();
  1345. OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);
  1346. Q_strncpy( pNewOptionChoice->szName, pSubKey->GetName(), sizeof( pNewOptionChoice->szName ) );
  1347. Q_strncpy( pNewOptionChoice->szValue, pSubKey->GetString(), sizeof( pNewOptionChoice->szValue ) );
  1348. }
  1349. if ( (*pNewOption)->m_Choices.Count() < 2 )
  1350. {
  1351. DevWarning( "Option \"%s\" is type CHOICE but has less than 2 choices!", (*pNewOption)->szDisplayName );
  1352. }
  1353. }
  1354. }
  1355. break;
  1356. }
  1357. }
  1358. }
  1359. }
  1360. int __cdecl SortByPriority( OptionData_t * const *pLeft, OptionData_t * const *pRight )
  1361. {
  1362. return (*pLeft)->iPriority - (*pRight)->iPriority;
  1363. }
  1364. void COptionsDialogXbox::SortOptions( void )
  1365. {
  1366. m_pOptions->Sort( SortByPriority );
  1367. }