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.

2204 lines
58 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <filesystem.h>
  8. #include "ienginevgui.h"
  9. #include "tf_gcmessages.h"
  10. #include "tf_mouseforwardingpanel.h"
  11. #include "gc_clientsystem.h"
  12. #include "c_tf_gamestats.h"
  13. #include "tf_hud_mainmenuoverride.h"
  14. #include "tf_gamerules.h"
  15. #include "econ/confirm_dialog.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. //------------------------------------------------------------------------------------------------------
  19. #define TRAINING_DIALOG_NAME "TrainingDialog"
  20. #define TRAINING_PROGRESS_FILE "trainingprogress.txt"
  21. //------------------------------------------------------------------------------------------------------
  22. #ifdef _DEBUG
  23. #define PRINT_KEY_VALUES( kv_ ) { CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER); kv_->RecursiveSaveToFile( buf, 0 ); ConMsg( "---\n%s\n---\n", buf.String() ); }
  24. #else
  25. #define PRINT_KEY_VALUES( kv_ ) { }
  26. #endif
  27. //------------------------------------------------------------------------------------------------------
  28. enum GameMode_t // Supported game modes for offline practice
  29. {
  30. MODE_INVALID = -1,
  31. MODE_CP,
  32. MODE_KOTH,
  33. MODE_PL,
  34. NUM_GAME_MODES
  35. };
  36. static const char *gs_pGameModeTokens[ NUM_GAME_MODES ] = {
  37. "#Gametype_CP",
  38. "#Gametype_Koth",
  39. "#Gametype_Escort",
  40. };
  41. //------------------------------------------------------------------------------------------------------
  42. ConVar cl_training_completed_with_classes( "cl_training_completed_with_classes", "0", FCVAR_ARCHIVE, "Bitfield representing what classes have been used to complete training." );
  43. bool Training_TrainingProgressFileExists()
  44. {
  45. const char *pFilename = TRAINING_PROGRESS_FILE;
  46. return g_pFullFileSystem->FileExists( pFilename, NULL );
  47. }
  48. //------------------------------------------------------------------------------------------------------
  49. static int Training_GetClassProgress( int iClass ) // Returns a percent, in the range [0,100]
  50. {
  51. Assert( iClass >= TF_FIRST_NORMAL_CLASS && iClass <= TF_LAST_NORMAL_CLASS );
  52. const int nDefaultResult = iClass == TF_CLASS_SOLDIER ? 0 : -1;
  53. KeyValuesAD pTrainingProgressData( "TrainingProgress" );
  54. if ( !pTrainingProgressData )
  55. {
  56. Warning( "Failed to save training progress!\n" );
  57. AssertMsg( 0, "Failed to save training progress!\n" );
  58. return nDefaultResult;
  59. }
  60. const char *pFilename = TRAINING_PROGRESS_FILE;
  61. if ( !pTrainingProgressData->LoadFromFile( g_pFullFileSystem, pFilename, "MOD" ) )
  62. return nDefaultResult;
  63. const char *pClassName = g_aPlayerClassNames_NonLocalized[ iClass ];
  64. KeyValues *pClassSubKey = pTrainingProgressData->FindKey( pClassName );
  65. if ( !pClassSubKey )
  66. return nDefaultResult;
  67. return pClassSubKey->GetInt( "progress", nDefaultResult );
  68. }
  69. static void Training_GetProgress( int pClass[TF_CLASS_COUNT] ) // Returns a percent, in the range [0,100]
  70. {
  71. KeyValuesAD pTrainingProgressData( "TrainingProgress" );
  72. const char *pFilename = TRAINING_PROGRESS_FILE;
  73. bool bLoadedFileOk = pTrainingProgressData->LoadFromFile( g_pFullFileSystem, pFilename, "MOD" );
  74. for ( int i = 0; i < TF_CLASS_COUNT; ++i )
  75. {
  76. const int iClass = i;
  77. const int nDefaultResult = ( bLoadedFileOk && iClass == TF_CLASS_SOLDIER ) ? 0 : -1;
  78. const char *pClassName = g_aPlayerClassNames_NonLocalized[ iClass ];
  79. KeyValues *pClassSubKey = pTrainingProgressData->FindKey( pClassName );
  80. if ( !pClassSubKey )
  81. {
  82. pClass[ i ] = nDefaultResult;
  83. continue;
  84. }
  85. pClass[ i ] = pClassSubKey->GetInt( "progress", nDefaultResult );
  86. }
  87. }
  88. KeyValues *Training_LoadProgressFile()
  89. {
  90. KeyValues *pTrainingProgressData = new KeyValues( "TrainingProgress" );
  91. if ( !pTrainingProgressData )
  92. {
  93. Warning( "Failed to save training progress!\n" );
  94. AssertMsg( 0, "Failed to save training progress!\n" );
  95. return NULL;
  96. }
  97. // Attempt to load any existing progress from disk
  98. const char *pFilename = TRAINING_PROGRESS_FILE;
  99. if ( !pTrainingProgressData->LoadFromFile( g_pFullFileSystem, pFilename, "MOD" ) )
  100. {
  101. // File didn't exist - create from defaults
  102. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  103. {
  104. KeyValues *pClassSubKey = new KeyValues( g_aPlayerClassNames_NonLocalized[ i ] );
  105. if ( !pClassSubKey )
  106. continue;
  107. pClassSubKey->SetInt( "progress", -1 ); // -1 means they haven't beat anything
  108. pTrainingProgressData->AddSubKey( pClassSubKey );
  109. }
  110. }
  111. return pTrainingProgressData;
  112. }
  113. void Training_SaveProgress( KeyValues *pTrainingProgressData )
  114. {
  115. const char *pFilename = TRAINING_PROGRESS_FILE;
  116. if ( !pTrainingProgressData->SaveToFile( g_pFullFileSystem, pFilename, "MOD" ) )
  117. {
  118. Warning( "Failed to save progress!\n" );
  119. AssertMsg( 0, "Failed to save progress!" );
  120. }
  121. }
  122. KeyValues *Training_FindClassData( KeyValues *pTrainingProgressData, int iClass )
  123. {
  124. const char *pClassName = g_aPlayerClassNames_NonLocalized[ iClass ];
  125. return pTrainingProgressData->FindKey( pClassName );
  126. }
  127. void Training_SaveProgress( int pProgress[ TF_CLASS_COUNT ] )
  128. {
  129. KeyValues *pTrainingProgressData = Training_LoadProgressFile();
  130. if ( !pTrainingProgressData )
  131. return;
  132. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  133. {
  134. KeyValues *pClassSubKey = Training_FindClassData( pTrainingProgressData, i );
  135. if ( !pClassSubKey )
  136. {
  137. AssertMsg( 0, "All classes should have been created on load if they didn't exist - this should not happen!" );
  138. continue;
  139. }
  140. Assert( pProgress[ i ] >= -1 );
  141. pClassSubKey->SetInt( "progress", pProgress[ i ] );
  142. }
  143. Training_SaveProgress( pTrainingProgressData );
  144. }
  145. void Training_MarkClassComplete( int iClass, int iStage )
  146. {
  147. Assert( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_LAST_NORMAL_CLASS );
  148. Assert( iStage >= 0 );
  149. KeyValues *pTrainingProgressData = Training_LoadProgressFile();
  150. if ( !pTrainingProgressData )
  151. return;
  152. // Find the data for the corresponding class
  153. KeyValues *pClassSubKey = Training_FindClassData( pTrainingProgressData, iClass );
  154. if ( pClassSubKey )
  155. {
  156. pClassSubKey->SetInt( "progress", iStage );
  157. }
  158. else
  159. {
  160. Warning( "Failed to load data for class %s!\n", g_aPlayerClassNames_NonLocalized[ iClass ] );
  161. }
  162. // Unlock next class if necessary
  163. const int iLastTrainingClass = TF_CLASS_ENGINEER;
  164. if ( iClass != iLastTrainingClass )
  165. {
  166. const int aNextClasses[ TF_CLASS_COUNT ] = {
  167. -1, // TF_CLASS_UNDEFINED
  168. -1, // TF_CLASS_SCOUT
  169. -1, // TF_CLASS_SNIPER
  170. TF_CLASS_DEMOMAN, // TF_CLASS_SOLDIER
  171. TF_CLASS_SPY, // TF_CLASS_DEMOMAN
  172. -1, // TF_CLASS_MEDIC
  173. -1, // TF_CLASS_HEAVYWEAPONS
  174. -1, // TF_CLASS_PYRO
  175. TF_CLASS_ENGINEER, // TF_CLASS_SPY
  176. -1, // TF_CLASS_ENGINEER
  177. };
  178. const int aUnlockRequirements[ TF_CLASS_COUNT ] = {
  179. -1, // TF_CLASS_UNDEFINED
  180. -1, // TF_CLASS_SCOUT
  181. -1, // TF_CLASS_SNIPER
  182. 2, // TF_CLASS_SOLDIER - must beat 2 stages to complete soldier training
  183. 1, // TF_CLASS_DEMOMAN
  184. -1, // TF_CLASS_MEDIC
  185. -1, // TF_CLASS_HEAVYWEAPONS
  186. -1, // TF_CLASS_PYRO
  187. 1, // TF_CLASS_SPY
  188. -1, // TF_CLASS_ENGINEER
  189. };
  190. const int iNextClass = aNextClasses[ iClass ];
  191. const bool bCurrentClassCompleted = iStage >= aUnlockRequirements[ iClass ];
  192. if ( iNextClass >= TF_FIRST_NORMAL_CLASS && bCurrentClassCompleted )
  193. {
  194. // Find the data for the given class and unlock it
  195. KeyValues *pNextClassData = pTrainingProgressData->FindKey( g_aPlayerClassNames_NonLocalized[ iNextClass ] );
  196. if ( pNextClassData )
  197. {
  198. pNextClassData->SetInt( "progress", 0 );
  199. }
  200. else
  201. {
  202. AssertMsg( 0, "This class data should have been filled out above" );
  203. }
  204. }
  205. }
  206. // Attempt to save
  207. Training_SaveProgress( pTrainingProgressData );
  208. // Free
  209. pTrainingProgressData->deleteThis();
  210. }
  211. static ConVar training_map_video( "training_map_video", "", 0, "Video to show for training" );
  212. void CL_Training_LevelShutdown()
  213. {
  214. training_map_video.Revert();
  215. }
  216. int Training_GetNumCoursesForClass( int iClass )
  217. {
  218. static int s_aClassCourses[ TF_CLASS_COUNT ] = {
  219. 0, // TF_CLASS_UNDEFINED
  220. 0, // TF_CLASS_SCOUT
  221. 0, // TF_CLASS_SNIPER
  222. 2, // TF_CLASS_SOLDIER
  223. 1, // TF_CLASS_DEMOMAN
  224. 0, // TF_CLASS_MEDIC
  225. 0, // TF_CLASS_HEAVYWEAPONS
  226. 0, // TF_CLASS_PYRO
  227. 1, // TF_CLASS_SPY
  228. 1, // TF_CLASS_ENGINEER
  229. };
  230. AssertMsg( iClass >= 0 && iClass < TF_CLASS_COUNT, "Training_GetNumCoursesForClass(): Class out of range!" );
  231. return s_aClassCourses[ iClass ];
  232. }
  233. int Training_GetNumCourses()
  234. {
  235. static bool s_bComputed = false;
  236. static int s_nTotal = 0;
  237. if ( !s_bComputed )
  238. {
  239. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  240. {
  241. s_nTotal += Training_GetNumCoursesForClass( i );
  242. }
  243. s_bComputed = true;
  244. }
  245. AssertMsg( s_nTotal == 5, "Number of total courses is incorrect - should be soldier (2) + demo (1) + spy (1) + engy (1)" );
  246. return s_nTotal;
  247. }
  248. int Training_GetProgressCount()
  249. {
  250. int aProgress[ TF_CLASS_COUNT ];
  251. Training_GetProgress( aProgress );
  252. int nTotalProgress = 0;
  253. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  254. {
  255. int nClassProgress = Training_GetClassProgress( i );
  256. if ( nClassProgress > 0 )
  257. {
  258. nTotalProgress += nClassProgress;
  259. }
  260. }
  261. return nTotalProgress;
  262. }
  263. bool Training_IsComplete()
  264. {
  265. return Training_GetProgressCount() == Training_GetNumCourses();
  266. }
  267. void Training_Init()
  268. {
  269. // If the progress file already exists, early out as we only do conversation from the old system to the new here.
  270. if ( Training_TrainingProgressFileExists() )
  271. return;
  272. int aProgress[ TF_CLASS_COUNT ];
  273. int fProgressOld = cl_training_completed_with_classes.GetInt();
  274. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  275. {
  276. if ( ( fProgressOld & ( 1 << i ) ) != 0 )
  277. {
  278. aProgress[ i ] = 1;
  279. }
  280. else
  281. {
  282. aProgress[ i ] = -1;
  283. }
  284. }
  285. const int TRAINING_CLASS_ATTACK_DEFEND = 15;
  286. // Add an explicit check for attack/defend
  287. if ( ( fProgressOld & ( 1 << TRAINING_CLASS_ATTACK_DEFEND ) ) != 0 )
  288. {
  289. aProgress[ TF_CLASS_SOLDIER ] = 2;
  290. }
  291. // Soldier should always be at least 0
  292. aProgress[ TF_CLASS_SOLDIER ] = MAX( aProgress[ TF_CLASS_SOLDIER ], 0 );
  293. // Save a file with the given progress settings
  294. Training_SaveProgress( aProgress );
  295. }
  296. //------------------------------------------------------------------------------------------------------
  297. Panel *FindAncestorByName( Panel *pChild, const char *pName )
  298. {
  299. if ( !pChild )
  300. return NULL;
  301. Panel *pCurrent = pChild->GetParent();
  302. while ( pCurrent )
  303. {
  304. if ( FStrEq( pCurrent->GetName(), pName ) )
  305. return pCurrent;
  306. pCurrent = pCurrent->GetParent();
  307. }
  308. return NULL;
  309. }
  310. CExButton *SetupButtonActionSignalTarget( Panel *pParent, const char *pButtonName, const char *pCommand = NULL )
  311. {
  312. CExButton *pButton = dynamic_cast< CExButton * >( pParent->FindChildByName( pButtonName ) );
  313. EditablePanel *pTrainingDialog = static_cast< EditablePanel * >( FindAncestorByName( pParent, TRAINING_DIALOG_NAME ) ); Assert( pTrainingDialog );
  314. if ( pButton && pTrainingDialog )
  315. {
  316. if ( pCommand )
  317. {
  318. pButton->SetCommand( pCommand );
  319. }
  320. pButton->AddActionSignalTarget( pTrainingDialog );
  321. }
  322. return pButton;
  323. }
  324. //------------------------------------------------------------------------------------------------------
  325. //
  326. // Sets dialog title/subtitle and sets up cancel/back buttons
  327. //
  328. class CTrainingBasePanel : public EditablePanel
  329. {
  330. DECLARE_CLASS_SIMPLE( CTrainingBasePanel, EditablePanel );
  331. public:
  332. CTrainingBasePanel( Panel *pParent, const char *pName )
  333. : EditablePanel( pParent, pName ),
  334. m_pPrevPagePanel( NULL )
  335. {
  336. }
  337. virtual void ApplySettings( KeyValues *pInResourceData )
  338. {
  339. BaseClass::ApplySettings( pInResourceData );
  340. m_strTitleToken = pInResourceData->GetString( "TrainingTitle", NULL );
  341. m_strSubTitleToken = pInResourceData->GetString( "TrainingSubTitle", NULL );
  342. }
  343. inline bool FindCharInWideString( const wchar_t *pStr, wchar_t c )
  344. {
  345. if ( !pStr )
  346. return false;
  347. const int nLen = V_wcslen( pStr );
  348. for ( int i = 0; i < nLen; ++i )
  349. {
  350. if ( pStr[ i ] == c )
  351. return true;
  352. }
  353. return false;
  354. }
  355. virtual void ApplySchemeSettings( IScheme *pScheme )
  356. {
  357. BaseClass::ApplySchemeSettings( pScheme );
  358. EditablePanel *pTrainingDialog = static_cast< EditablePanel * >( FindAncestorByName( this, TRAINING_DIALOG_NAME ) ); Assert( pTrainingDialog );
  359. if ( pTrainingDialog )
  360. {
  361. const wchar_t *pTitleString = g_pVGuiLocalize->Find( m_strTitleToken.Get() );
  362. if ( FindCharInWideString( pTitleString, L'%' ) )
  363. {
  364. KeyValues *pTitleFormatData = GetTitleFormatData(); AssertMsg( pTitleFormatData, "Should get valid data here." );
  365. if ( pTitleFormatData )
  366. {
  367. wchar_t wszTitle[ 1024 ];
  368. g_pVGuiLocalize->ConstructString_safe( wszTitle, m_strTitleToken.Get(), pTitleFormatData );
  369. pTitleFormatData->deleteThis();
  370. pTrainingDialog->SetDialogVariable( "title", wszTitle );
  371. }
  372. }
  373. else
  374. {
  375. pTrainingDialog->SetDialogVariable( "title", g_pVGuiLocalize->Find( m_strTitleToken.Get() ) );
  376. }
  377. pTrainingDialog->SetDialogVariable( "subtitle", g_pVGuiLocalize->Find( m_strSubTitleToken ) );
  378. }
  379. }
  380. virtual void OnCommand( const char *pCommand )
  381. {
  382. if ( FStrEq( pCommand, "goprev" ) )
  383. {
  384. GoPrev();
  385. }
  386. else if ( FStrEq( pCommand, "gonext" ) )
  387. {
  388. GoNext();
  389. }
  390. else
  391. {
  392. BaseClass::OnCommand( pCommand );
  393. }
  394. }
  395. virtual void OnKeyCodePressed( KeyCode nCode )
  396. {
  397. ButtonCode_t nButtonCode = GetBaseButtonCode( nCode );
  398. if ( nCode == KEY_SPACE || nCode == KEY_ENTER || nCode == KEY_XBUTTON_A || nCode == STEAMCONTROLLER_A )
  399. {
  400. Go();
  401. }
  402. else if ( nButtonCode == KEY_XBUTTON_LEFT ||
  403. nButtonCode == KEY_XSTICK1_LEFT ||
  404. nButtonCode == KEY_XSTICK2_LEFT ||
  405. nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
  406. nButtonCode == KEY_LEFT )
  407. {
  408. GoPrev();
  409. }
  410. else if ( nButtonCode == KEY_XBUTTON_RIGHT ||
  411. nButtonCode == KEY_XSTICK1_RIGHT ||
  412. nButtonCode == KEY_XSTICK2_RIGHT ||
  413. nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
  414. nButtonCode == KEY_RIGHT )
  415. {
  416. GoNext();
  417. }
  418. else
  419. {
  420. BaseClass::OnKeyCodePressed( nCode );
  421. }
  422. }
  423. void Go()
  424. {
  425. EditablePanel *pTrainingDialog = static_cast< EditablePanel * >( FindAncestorByName( this, TRAINING_DIALOG_NAME ) ); Assert( pTrainingDialog );
  426. if ( pTrainingDialog )
  427. {
  428. const char *pGoCommand = GetGoCommand();
  429. if ( pGoCommand )
  430. {
  431. pTrainingDialog->OnCommand( pGoCommand );
  432. }
  433. }
  434. }
  435. virtual void GoPrev()
  436. {
  437. }
  438. virtual void GoNext()
  439. {
  440. }
  441. virtual void OnBackPressed()
  442. {
  443. }
  444. virtual KeyValues *GetTitleFormatData() const
  445. {
  446. return NULL;
  447. }
  448. virtual const char *GetGoCommand() const
  449. {
  450. return NULL;
  451. }
  452. void SetPrevPage( CTrainingBasePanel *pPanel )
  453. {
  454. m_pPrevPagePanel = pPanel;
  455. }
  456. CTrainingBasePanel *GetPrevPage()
  457. {
  458. return m_pPrevPagePanel;
  459. }
  460. virtual bool IsFirstPage() const
  461. {
  462. return false;
  463. }
  464. virtual bool ShouldShowGradient() const
  465. {
  466. return false;
  467. }
  468. protected:
  469. CUtlString m_strTitleToken;
  470. CUtlString m_strSubTitleToken;
  471. CTrainingBasePanel *m_pPrevPagePanel;
  472. };
  473. //------------------------------------------------------------------------------------------------------
  474. class CTrainingBaseCarouselPanel : public CTrainingBasePanel
  475. {
  476. DECLARE_CLASS_SIMPLE( CTrainingBaseCarouselPanel, CTrainingBasePanel );
  477. public:
  478. CTrainingBaseCarouselPanel( Panel *pParent, const char *pName )
  479. : CTrainingBasePanel( pParent, pName ),
  480. m_iPage( 0 )
  481. {
  482. }
  483. virtual void ApplySchemeSettings( IScheme *pScheme )
  484. {
  485. BaseClass::ApplySchemeSettings( pScheme );
  486. CFmtStr fmtCurPageLabelText( "%i/%i", m_iPage + 1, GetNumPages() );
  487. SetDialogVariable( "curpage", fmtCurPageLabelText.Access() );
  488. const int nNumPages = GetNumPages();
  489. // Set visibility on buttons and current page based on the number of pages.
  490. CExLabel *pCurPageLabel = dynamic_cast< CExLabel * >( FindChildByName( "CurPageLabel" ) );
  491. if ( pCurPageLabel )
  492. {
  493. pCurPageLabel->SetVisible( nNumPages > 1 );
  494. }
  495. const char *pNavButtonNames[2] = { "PrevButton", "NextButton" };
  496. for ( int i = 0; i < 2; ++i )
  497. {
  498. CExButton *pCurButton = dynamic_cast< CExButton * >( FindChildByName( pNavButtonNames[ i ] ) );
  499. if ( !pCurButton )
  500. continue;
  501. pCurButton->SetVisible( nNumPages > 1 );
  502. }
  503. }
  504. virtual void OnCommand( const char *pCommand )
  505. {
  506. if ( FStrEq( pCommand, "goprev" ) )
  507. {
  508. GoPrev();
  509. }
  510. else if ( FStrEq( pCommand, "gonext" ) )
  511. {
  512. GoNext();
  513. }
  514. else
  515. {
  516. BaseClass::OnCommand( pCommand );
  517. }
  518. }
  519. virtual void OnKeyCodePressed( KeyCode nCode )
  520. {
  521. ButtonCode_t nButtonCode = GetBaseButtonCode( nCode );
  522. if ( nButtonCode == KEY_XBUTTON_LEFT ||
  523. nButtonCode == KEY_XSTICK1_LEFT ||
  524. nButtonCode == KEY_XSTICK2_LEFT ||
  525. nButtonCode == KEY_LEFT )
  526. {
  527. GoPrev();
  528. }
  529. else if ( nButtonCode == KEY_XBUTTON_RIGHT ||
  530. nButtonCode == KEY_XSTICK1_RIGHT ||
  531. nButtonCode == KEY_XSTICK2_RIGHT ||
  532. nButtonCode == KEY_RIGHT )
  533. {
  534. GoNext();
  535. }
  536. else
  537. {
  538. BaseClass::OnKeyCodePressed( nCode );
  539. }
  540. }
  541. void GoPrev()
  542. {
  543. --m_iPage;
  544. if ( m_iPage < 0 )
  545. {
  546. m_iPage += GetNumPages();
  547. }
  548. InvalidateLayout( false, true );
  549. }
  550. void GoNext()
  551. {
  552. m_iPage = ( m_iPage + 1 ) % GetNumPages();
  553. InvalidateLayout( false, true );
  554. }
  555. virtual int GetNumPages() const = 0;
  556. protected:
  557. int m_iPage;
  558. };
  559. //------------------------------------------------------------------------------------------------------
  560. class CModePanel : public EditablePanel
  561. {
  562. DECLARE_CLASS_SIMPLE( CModePanel, EditablePanel );
  563. public:
  564. CModePanel( Panel *pParent, const char *pName )
  565. : EditablePanel( pParent, pName )
  566. {
  567. HScheme hScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
  568. SetScheme( hScheme );
  569. SetProportional( true );
  570. }
  571. ~CModePanel()
  572. {
  573. }
  574. virtual void ApplySettings( KeyValues *pInResourceData )
  575. {
  576. BaseClass::ApplySettings( pInResourceData );
  577. m_strModeNameToken = pInResourceData->GetString( "modename", NULL );
  578. m_strDescriptionToken = pInResourceData->GetString( "description", NULL );
  579. m_strImageToken = pInResourceData->GetString( "image", NULL );
  580. m_strStartCommand = pInResourceData->GetString( "startcommand", NULL );
  581. }
  582. virtual void ApplySchemeSettings( IScheme *pScheme )
  583. {
  584. BaseClass::ApplySchemeSettings( pScheme );
  585. LoadControlSettings( "resource/ui/training/modeselection/modepanel.res" );
  586. EditablePanel *pContainer = static_cast< EditablePanel * >( FindChildByName( "ModeInfoContainer" ) );
  587. if ( pContainer )
  588. {
  589. pContainer->SetDialogVariable( "modename", g_pVGuiLocalize->Find( m_strModeNameToken.Get() ) );
  590. pContainer->SetDialogVariable( "description", g_pVGuiLocalize->Find( m_strDescriptionToken.Get() ) );
  591. EditablePanel *pImageFrame = static_cast< EditablePanel * >( pContainer->FindChildByName( "ImageFrame" ) );
  592. if ( pImageFrame )
  593. {
  594. ImagePanel *pImage = dynamic_cast< ImagePanel * >( pContainer->FindChildByName( "Image" ) );
  595. if ( pImage )
  596. {
  597. pImage->SetImage( m_strImageToken );
  598. pImage->SetParent( pImageFrame );
  599. }
  600. }
  601. }
  602. SetupButtonActionSignalTarget( this, "StartButton", m_strStartCommand.Get() );
  603. }
  604. virtual void PerformLayout( void )
  605. {
  606. BaseClass::PerformLayout();
  607. GetParent()->NavigateTo();
  608. }
  609. private:
  610. CUtlString m_strModeNameToken;
  611. CUtlString m_strDescriptionToken;
  612. CUtlString m_strImageToken;
  613. CUtlString m_strStartCommand;
  614. };
  615. DECLARE_BUILD_FACTORY( CModePanel );
  616. //------------------------------------------------------------------------------------------------------
  617. class CModeSelectionPanel : public CTrainingBasePanel
  618. {
  619. DECLARE_CLASS_SIMPLE( CModeSelectionPanel, CTrainingBasePanel );
  620. public:
  621. CModeSelectionPanel( Panel *pParent, const char *pName )
  622. : CTrainingBasePanel( pParent, pName )
  623. {
  624. SetProportional( true );
  625. }
  626. virtual void ApplySchemeSettings( IScheme *pScheme )
  627. {
  628. BaseClass::ApplySchemeSettings( pScheme );
  629. LoadControlSettings( "resource/ui/training/modeselection/modeselection.res" );
  630. }
  631. virtual bool IsFirstPage() const
  632. {
  633. return true;
  634. }
  635. virtual void PerformLayout()
  636. {
  637. BaseClass::PerformLayout();
  638. Panel *pPanel = FindChildByName( "BasicTrainingPanel" );
  639. if ( pPanel )
  640. {
  641. pPanel->SetNavToRelay( "StartButton" );
  642. pPanel->SetNavRight( "<<OfflinePracticePanel" );
  643. pPanel->InvalidateLayout();
  644. }
  645. pPanel = FindChildByName( "OfflinePracticePanel" );
  646. if ( pPanel )
  647. {
  648. pPanel->SetNavToRelay( "StartButton" );
  649. pPanel->SetNavLeft( "<<BasicTrainingPanel" );
  650. pPanel->InvalidateLayout();
  651. }
  652. SetNavToRelay( "BasicTrainingPanel" );
  653. }
  654. };
  655. DECLARE_BUILD_FACTORY( CModeSelectionPanel );
  656. //------------------------------------------------------------------------------------------------------
  657. class CBasicTraining_ClassPanel : public EditablePanel
  658. {
  659. DECLARE_CLASS_SIMPLE( CBasicTraining_ClassPanel, EditablePanel );
  660. public:
  661. CBasicTraining_ClassPanel( Panel *pParent, const char *pName )
  662. : EditablePanel( pParent, pName ),
  663. m_pImagePanel( NULL ),
  664. m_pSelectButton( NULL )
  665. {
  666. SetProportional( true );
  667. }
  668. virtual void ApplySchemeSettings( IScheme *pScheme )
  669. {
  670. BaseClass::ApplySchemeSettings( pScheme );
  671. LoadControlSettings( "resource/ui/training/basictraining/classpanel.res" );
  672. m_pImagePanel = dynamic_cast< ImagePanel * >( FindChildByName( "Image" ) ); Assert( m_pImagePanel );
  673. m_pSelectButton = SetupButtonActionSignalTarget( this, "SelectButton" ); Assert( m_pSelectButton );
  674. if ( m_pSelectButton )
  675. {
  676. m_pSelectButton->SetDefaultBorder( pScheme->GetBorder( m_pSelectButton->IsEnabled() ? "MainMenuButtonDefault" : "MainMenuButtonDisabled" ) );
  677. }
  678. m_pProgressLabel = dynamic_cast< CExLabel * >( FindChildByName( "ProgressLabel" ) );
  679. }
  680. virtual void PerformLayout()
  681. {
  682. BaseClass::PerformLayout();
  683. if ( m_pImagePanel && m_pSelectButton )
  684. {
  685. const int nMargin = XRES( 10 );
  686. const int nButtonStartY = YRES( 215 );
  687. m_pImagePanel->SetBounds( nMargin, YRES( 20 ), GetWide() - 2 * nMargin, nButtonStartY - YRES( 40 ) );
  688. int aButtonBounds[4] = {
  689. nMargin, nButtonStartY, GetWide() - nMargin * 2, (int)YRES( 25 )
  690. };
  691. m_pSelectButton->SetBounds( aButtonBounds[0], aButtonBounds[1], aButtonBounds[2], aButtonBounds[3] );
  692. CExLabel *pProgressLabel = dynamic_cast< CExLabel * >( FindChildByName( "ProgressLabel" ) );
  693. if ( pProgressLabel )
  694. {
  695. int aPos[2];
  696. pProgressLabel->GetPos( aPos[0], aPos[1] );
  697. pProgressLabel->SetPos( aButtonBounds[0], aPos[1] );
  698. pProgressLabel->SetWide( aButtonBounds[2] );
  699. }
  700. }
  701. GetParent()->NavigateTo();
  702. }
  703. void SetClassData( int iClass, int nProgress, const char *pImageBase )
  704. {
  705. const bool bLocked = nProgress < 0;
  706. if ( m_pImagePanel )
  707. {
  708. CFmtStr fmtImagePath( "%s_%s", pImageBase, bLocked ? "off" : "on" );
  709. m_pImagePanel->SetImage( fmtImagePath.Access() );
  710. }
  711. if ( m_pSelectButton )
  712. {
  713. m_pSelectButton->SetEnabled( !bLocked );
  714. }
  715. if ( m_pProgressLabel )
  716. {
  717. const int nPercent = (int)( 100.0f * nProgress / Training_GetNumCoursesForClass( iClass ) );
  718. wchar_t wszLocalized[256];
  719. if ( nPercent > 0 )
  720. {
  721. if ( nPercent < 100 )
  722. {
  723. wchar_t wszNum[16] = L"";
  724. V_snwprintf( wszNum, ARRAYSIZE( wszNum ), L"%i", nPercent );
  725. g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TR_Progress" ), 1, wszNum );
  726. }
  727. else
  728. {
  729. V_wcsncpy( wszLocalized, g_pVGuiLocalize->Find( "#TR_ProgressDone" ), sizeof( wszLocalized ) );
  730. }
  731. m_pProgressLabel->SetText( wszLocalized );
  732. m_pProgressLabel->SetVisible( true );
  733. }
  734. else
  735. {
  736. m_pProgressLabel->SetVisible( false );
  737. }
  738. }
  739. InvalidateLayout( true, false );
  740. }
  741. void SetSelectCommand( const char *pCommand )
  742. {
  743. if ( m_pSelectButton )
  744. {
  745. m_pSelectButton->SetCommand( pCommand );
  746. }
  747. }
  748. private:
  749. ImagePanel *m_pImagePanel;
  750. CExButton *m_pSelectButton;
  751. CExLabel *m_pProgressLabel;
  752. };
  753. DECLARE_BUILD_FACTORY( CBasicTraining_ClassPanel );
  754. //------------------------------------------------------------------------------------------------------
  755. enum Consts_t
  756. {
  757. NUM_CLASS_PANELS = 4,
  758. };
  759. const char *g_pClassPanelNames[ NUM_CLASS_PANELS ] =
  760. {
  761. "SoldierPanel",
  762. "DemoPanel",
  763. "SpyPanel",
  764. "EngineerPanel"
  765. };
  766. class CBasicTraining_ClassSelectionPanel : public CTrainingBasePanel
  767. {
  768. DECLARE_CLASS_SIMPLE( CBasicTraining_ClassSelectionPanel, CTrainingBasePanel );
  769. public:
  770. CBasicTraining_ClassSelectionPanel( Panel *pParent, const char *pName )
  771. : CTrainingBasePanel( pParent, pName )
  772. {
  773. SetProportional( true );
  774. for ( int i = 0; i < NUM_CLASS_PANELS; ++i )
  775. {
  776. m_PanelInfos[ i ].m_pPanel = new CBasicTraining_ClassPanel( this, g_pClassPanelNames[ i ] );
  777. }
  778. }
  779. virtual void ApplySettings( KeyValues *pInResourceData )
  780. {
  781. BaseClass::ApplySettings( pInResourceData );
  782. for ( int i = 0; i < NUM_CLASS_PANELS; ++i )
  783. {
  784. CFmtStr fmtToken( "Class%iToken", i );
  785. m_PanelInfos[ i ].m_strSelectButtonToken = pInResourceData->GetString( fmtToken.Access(), NULL );
  786. CFmtStr fmtImage( "Class%iImage", i );
  787. m_PanelInfos[ i ].m_strClassImage = pInResourceData->GetString( fmtImage.Access(), NULL );
  788. CFmtStr fmtCommand( "Class%iCommand", i );
  789. m_PanelInfos[ i ].m_strCommand = pInResourceData->GetString( fmtCommand.Access(), NULL );
  790. }
  791. PRINT_KEY_VALUES( pInResourceData );
  792. }
  793. virtual void ApplySchemeSettings( IScheme *pScheme )
  794. {
  795. BaseClass::ApplySchemeSettings( pScheme );
  796. LoadControlSettings( "resource/ui/training/basictraining/classselection.res" );
  797. }
  798. virtual void PerformLayout()
  799. {
  800. BaseClass::PerformLayout();
  801. const int nWidth = GetWide();
  802. const int nClassPanelW = nWidth / NUM_CLASS_PANELS;
  803. const int nClassPanelH = YRES( 260 );
  804. const int aTrainingClasses[ NUM_CLASS_PANELS ] = {
  805. TF_CLASS_SOLDIER, TF_CLASS_DEMOMAN, TF_CLASS_SPY, TF_CLASS_ENGINEER
  806. };
  807. for ( int i = 0; i < NUM_CLASS_PANELS; ++i )
  808. {
  809. CBasicTraining_ClassPanel *pCurClassPanel = m_PanelInfos[ i ].m_pPanel;
  810. pCurClassPanel->SetBounds(
  811. i * nClassPanelW,
  812. 0,
  813. nClassPanelW,
  814. nClassPanelH
  815. );
  816. pCurClassPanel->SetDialogVariable( "selectbuttontext", g_pVGuiLocalize->Find( m_PanelInfos[ i ].m_strSelectButtonToken.Get() ) );
  817. const int nProgress = Training_GetClassProgress( aTrainingClasses[ i ] );
  818. pCurClassPanel->SetClassData( aTrainingClasses[ i ], nProgress, m_PanelInfos[ i ].m_strClassImage.Get() );
  819. pCurClassPanel->SetSelectCommand( m_PanelInfos[ i ].m_strCommand.Get() );
  820. pCurClassPanel->SetNavToRelay( "SelectButton" );
  821. char szName[ 64 ];
  822. if ( i > 0 )
  823. {
  824. Panel *pPrevPanel = m_PanelInfos[ i - 1 ].m_pPanel;
  825. if ( pPrevPanel )
  826. {
  827. V_snprintf( szName, sizeof( szName ), "<%s", pPrevPanel->GetName() );
  828. pCurClassPanel->SetNavLeft( szName );
  829. V_snprintf( szName, sizeof( szName ), "<%s", pCurClassPanel->GetName() );
  830. pPrevPanel->SetNavRight( szName );
  831. }
  832. }
  833. pCurClassPanel->InvalidateLayout();
  834. }
  835. SetNavToRelay( g_pClassPanelNames[ 0 ] );
  836. }
  837. virtual bool ShouldShowGradient() const
  838. {
  839. return true;
  840. }
  841. private:
  842. struct ClassPanelInfo_t
  843. {
  844. CBasicTraining_ClassPanel *m_pPanel;
  845. CUtlString m_strSelectButtonToken;
  846. CUtlString m_strClassImage;
  847. CUtlString m_strCommand;
  848. }
  849. m_PanelInfos[ NUM_CLASS_PANELS ];
  850. };
  851. DECLARE_BUILD_FACTORY( CBasicTraining_ClassSelectionPanel );
  852. //------------------------------------------------------------------------------------------------------
  853. class CBasicTraining_ClassDetailsPanel : public CTrainingBasePanel
  854. {
  855. DECLARE_CLASS_SIMPLE( CBasicTraining_ClassDetailsPanel, CTrainingBasePanel );
  856. public:
  857. CBasicTraining_ClassDetailsPanel( Panel *pParent, const char *pName )
  858. : CTrainingBasePanel( pParent, pName ),
  859. m_iClass( TF_CLASS_UNDEFINED ),
  860. m_pStartTrainingButton( NULL )
  861. {
  862. SetProportional( true );
  863. }
  864. virtual void ApplySchemeSettings( IScheme *pScheme )
  865. {
  866. BaseClass::ApplySchemeSettings( pScheme );
  867. LoadControlSettings( "resource/ui/training/basictraining/classdetails.res" );
  868. EditablePanel *pOverlayPanel = dynamic_cast< EditablePanel * >( FindChildByName( "OverlayPanel" ) );
  869. if ( pOverlayPanel && m_iClass >= TF_FIRST_NORMAL_CLASS && m_iClass < TF_LAST_NORMAL_CLASS )
  870. {
  871. pOverlayPanel->SetDialogVariable( "classname", g_pVGuiLocalize->Find( g_aPlayerClassNames[ m_iClass ] ) );
  872. CFmtStr fmtDescToken( "TR_ClassInfo_%s", m_szClassName );
  873. pOverlayPanel->SetDialogVariable( "description", g_pVGuiLocalize->Find( fmtDescToken.Access() ) );
  874. for ( int i = 0; i < 3; ++i )
  875. {
  876. CFmtStr fmtWeaponImageName( "WeaponImage%i", i );
  877. ImagePanel *pCurImage = dynamic_cast< ImagePanel * >( pOverlayPanel->FindChildByName( fmtWeaponImageName.Access() ) );
  878. if ( pCurImage )
  879. {
  880. CFmtStr fmtWeaponImagePath;
  881. GetWeaponPath( m_iClass, i, fmtWeaponImagePath );
  882. pCurImage->SetImage( fmtWeaponImagePath.Access() );
  883. }
  884. }
  885. }
  886. ImagePanel *pClassImage = dynamic_cast< ImagePanel * >( FindChildByName( "ClassImage" ) );
  887. if ( pClassImage )
  888. {
  889. CFmtStr fmtImageName( "training/class_%s_on", m_szClassName );
  890. pClassImage->SetImage( fmtImageName.Access() );
  891. }
  892. ImagePanel *pClassIconImage = dynamic_cast< ImagePanel * >( FindChildByName( "ClassIconImage" ) );
  893. if ( pClassIconImage )
  894. {
  895. CFmtStr fmtImageName( "training/class_icon_%s", m_szClassName );
  896. pClassIconImage->SetImage( fmtImageName.Access() );
  897. }
  898. m_pStartTrainingButton = SetupButtonActionSignalTarget( this, "StartTrainingButton" );
  899. }
  900. virtual void PerformLayout()
  901. {
  902. BaseClass::PerformLayout();
  903. SetNavToRelay( "StartTrainingButton" );
  904. NavigateTo();
  905. }
  906. void GetWeaponPath( int iClass, int iWeapon, CFmtStr &fmtOut ) // iWeapon is in [0,2]
  907. {
  908. static const char *s_pWeaponNames[ TF_CLASS_COUNT ][ 3 ] = {
  909. { NULL, NULL, NULL }, // TF_CLASS_UNDEFINED
  910. { NULL, NULL, NULL }, // TF_CLASS_SCOUT
  911. { NULL, NULL, NULL }, // TF_CLASS_SNIPER
  912. { "rocketlauncher", "shotgun", "shovel" }, // TF_CLASS_SOLDIER
  913. { "grenadelauncher", "stickybomb_launcher", "bottle" }, // TF_CLASS_DEMOMAN,
  914. { NULL, NULL, NULL }, // TF_CLASS_MEDIC
  915. { NULL, NULL, NULL }, // TF_CLASS_HEAVYWEAPONS
  916. { NULL, NULL, NULL }, // TF_CLASS_PYRO
  917. { "revolver", "c_spy_watch", "knife", }, // TF_CLASS_SPY,
  918. { "shotgun", "pistol", "wrench" }, // TF_CLASS_ENGINEER,
  919. };
  920. Assert( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_CLASS_COUNT );
  921. Assert( iWeapon >= 0 && iWeapon < 3 );
  922. if ( iClass == TF_CLASS_SPY && iWeapon == 1 )
  923. {
  924. fmtOut.sprintf( "../backpack/weapons/c_models/c_spy_watch/parts/c_spy_watch" );
  925. }
  926. else
  927. {
  928. fmtOut.sprintf( "../backpack/weapons/w_models/w_%s", s_pWeaponNames[ iClass ][ iWeapon ] );
  929. }
  930. }
  931. virtual bool ShouldShowGradient() const
  932. {
  933. return true;
  934. }
  935. virtual const char *GetGoCommand() const
  936. {
  937. if ( !m_pStartTrainingButton )
  938. return NULL;
  939. KeyValues *pCommand = m_pStartTrainingButton->GetCommand();
  940. if ( !pCommand )
  941. return NULL;
  942. return pCommand->GetString( "command", NULL );
  943. }
  944. void SetClass( const char *pClassName )
  945. {
  946. V_strcpy_safe( m_szClassName, pClassName );
  947. // Setup class details panel
  948. if ( FStrEq( pClassName, "soldier" ) )
  949. {
  950. m_iClass = TF_CLASS_SOLDIER;
  951. }
  952. else if ( FStrEq( pClassName, "demoman" ) )
  953. {
  954. m_iClass = TF_CLASS_DEMOMAN;
  955. }
  956. else if ( FStrEq( pClassName, "spy" ) )
  957. {
  958. m_iClass = TF_CLASS_SPY;
  959. }
  960. else if ( FStrEq( pClassName, "engineer" ) )
  961. {
  962. m_iClass = TF_CLASS_ENGINEER;
  963. }
  964. else
  965. {
  966. AssertMsg( 0, "Bad class name." );
  967. }
  968. }
  969. private:
  970. char m_szClassName[16];
  971. int m_iClass;
  972. CExButton *m_pStartTrainingButton;
  973. };
  974. DECLARE_BUILD_FACTORY( CBasicTraining_ClassDetailsPanel );
  975. //------------------------------------------------------------------------------------------------------
  976. class COfflinePractice_ModeSelectionPanel : public CTrainingBaseCarouselPanel
  977. {
  978. DECLARE_CLASS_SIMPLE( COfflinePractice_ModeSelectionPanel, CTrainingBaseCarouselPanel );
  979. public:
  980. COfflinePractice_ModeSelectionPanel( Panel *pParent, const char *pName )
  981. : CTrainingBaseCarouselPanel( pParent, pName ),
  982. m_pGameModeImagePanel( NULL )
  983. {
  984. SetProportional( true );
  985. }
  986. virtual void ApplySettings( KeyValues *pInResourceData )
  987. {
  988. BaseClass::ApplySettings( pInResourceData );
  989. for ( int i = 0; i < NUM_PRACTICE_MODES; ++i )
  990. {
  991. CFmtStr fmtModeToken( "Mode%iToken", i );
  992. m_ModeInfos[ i ].m_strModeToken = pInResourceData->GetString( fmtModeToken.Access(), NULL );
  993. CFmtStr fmtDescToken( "Desc%iToken", i );
  994. m_ModeInfos[ i ].m_strDescToken = pInResourceData->GetString( fmtDescToken.Access(), NULL );
  995. CFmtStr fmtImagePath( "Image%iPath", i );
  996. m_ModeInfos[ i ].m_strImage = pInResourceData->GetString( fmtImagePath.Access(), NULL );
  997. CFmtStr fmtModeId( "Mode%iId", i );
  998. m_ModeInfos[ i ].m_nId = ( GameMode_t )pInResourceData->GetInt( fmtModeId.Access(), MODE_INVALID ); Assert( m_ModeInfos[ i ].m_nId != MODE_INVALID );
  999. }
  1000. PRINT_KEY_VALUES( pInResourceData );
  1001. }
  1002. virtual void ApplySchemeSettings( IScheme *pScheme )
  1003. {
  1004. BaseClass::ApplySchemeSettings( pScheme );
  1005. LoadControlSettings( "resource/ui/training/offlinepractice/practicemodeselection.res" );
  1006. m_pGameModeImagePanel = dynamic_cast< ImagePanel * >( FindChildByName( "GameModeImagePanel" ) );
  1007. if ( m_pGameModeImagePanel )
  1008. {
  1009. Assert( m_iPage >= 0 && m_iPage < NUM_PRACTICE_MODES );
  1010. m_pGameModeImagePanel->SetImage( m_ModeInfos[ m_iPage ].m_strImage.Get() );
  1011. }
  1012. SetupButtonActionSignalTarget( this, "SelectCurrentGameModeButton" );
  1013. SetDialogVariable( "description", g_pVGuiLocalize->Find( m_ModeInfos[ m_iPage ].m_strDescToken.Get() ) );
  1014. SetDialogVariable( "gamemode", g_pVGuiLocalize->Find( m_ModeInfos[ m_iPage ].m_strModeToken.Get() ) );
  1015. CFmtStr fmtCurPageLabelText( "%i/%i", m_iPage + 1, NUM_PRACTICE_MODES );
  1016. SetDialogVariable( "curpage", fmtCurPageLabelText.Access() );
  1017. }
  1018. virtual void PerformLayout()
  1019. {
  1020. BaseClass::PerformLayout();
  1021. if ( m_pGameModeImagePanel )
  1022. {
  1023. // Use .res file ypos
  1024. int aPos[2];
  1025. m_pGameModeImagePanel->GetPos( aPos[0], aPos[1] );
  1026. // Center
  1027. m_pGameModeImagePanel->SetPos( ( GetWide() - m_pGameModeImagePanel->GetWide() ) / 2, aPos[1] );
  1028. }
  1029. SetNavToRelay( "SelectCurrentGameModeButton" );
  1030. NavigateTo();
  1031. }
  1032. virtual int GetNumPages() const
  1033. {
  1034. return NUM_PRACTICE_MODES;
  1035. }
  1036. GameMode_t GetMode() const
  1037. {
  1038. return m_ModeInfos[ m_iPage ].m_nId;
  1039. }
  1040. private:
  1041. enum Consts_t
  1042. {
  1043. NUM_PRACTICE_MODES = 3,
  1044. };
  1045. struct PracticeModeInfo_t
  1046. {
  1047. CUtlString m_strModeToken;
  1048. CUtlString m_strDescToken;
  1049. CUtlString m_strImage;
  1050. GameMode_t m_nId;
  1051. }
  1052. m_ModeInfos[ NUM_PRACTICE_MODES ];
  1053. ImagePanel *m_pGameModeImagePanel;
  1054. };
  1055. DECLARE_BUILD_FACTORY( COfflinePractice_ModeSelectionPanel );
  1056. const char *g_pDifficultyModes[ 4 ] = { "Easy", "Normal", "Hard", "Expert" };
  1057. //------------------------------------------------------------------------------------------------------
  1058. class COfflinePractice_MapSelectionPanel : public CTrainingBaseCarouselPanel
  1059. {
  1060. DECLARE_CLASS_SIMPLE( COfflinePractice_MapSelectionPanel, CTrainingBaseCarouselPanel );
  1061. struct MapInfo_t
  1062. {
  1063. CUtlString m_strDisplayName;
  1064. CUtlString m_strName;
  1065. int m_aPlayerRange[2];
  1066. };
  1067. public:
  1068. COfflinePractice_MapSelectionPanel( Panel *pParent, const char *pName )
  1069. : CTrainingBaseCarouselPanel( pParent, pName ),
  1070. m_pMapImagePanel( NULL ),
  1071. m_pDefaultsData( NULL ),
  1072. m_pDifficultyComboBox( NULL ),
  1073. m_pSavedData( NULL ),
  1074. m_iGameMode( MODE_INVALID )
  1075. {
  1076. SetProportional( true );
  1077. LoadMapData();
  1078. }
  1079. ~COfflinePractice_MapSelectionPanel()
  1080. {
  1081. for ( int i = 0; i < NUM_GAME_MODES; ++i )
  1082. {
  1083. m_vecMapData[i].PurgeAndDeleteElements();
  1084. }
  1085. if ( m_pDefaultsData )
  1086. {
  1087. m_pDefaultsData->deleteThis();
  1088. }
  1089. }
  1090. void SetGameMode( int iGameMode )
  1091. {
  1092. m_iGameMode = iGameMode;
  1093. m_iPage = 0;
  1094. InvalidateLayout( false, true );
  1095. }
  1096. const MapInfo_t *GetSelectedMapInfo() const
  1097. {
  1098. return m_iGameMode < 0 ? NULL : m_vecMapData[ m_iGameMode ][ m_iPage ];
  1099. }
  1100. int GetMaxPlayers() const
  1101. {
  1102. return GetSelectedMapInfo()->m_aPlayerRange[1];
  1103. }
  1104. const char *GetMapName() const
  1105. {
  1106. return GetSelectedMapInfo()->m_strName.Get();
  1107. }
  1108. virtual void ApplySchemeSettings( IScheme *pScheme )
  1109. {
  1110. LoadControlSettings( "resource/ui/training/offlinepractice/mapselection.res" );
  1111. BaseClass::ApplySchemeSettings( pScheme );
  1112. const MapInfo_t *pCurMapInfo = GetSelectedMapInfo();
  1113. if ( !pCurMapInfo )
  1114. return;
  1115. m_pMapImagePanel = dynamic_cast< ImagePanel * >( FindChildByName( "MapImagePanel" ) );
  1116. if ( m_pMapImagePanel )
  1117. {
  1118. Assert( m_iPage >= 0 && m_iPage < GetMapCount() );
  1119. CFmtStr fmtMapImageBasePath( "training/screenshots/%s.vmt", pCurMapInfo->m_strName.Get() );
  1120. m_pMapImagePanel->SetImage( fmtMapImageBasePath.Access() );
  1121. }
  1122. // Send the 'select' button's command to the actual dialog
  1123. SetupButtonActionSignalTarget( this, "SelectCurrentMapButton" );
  1124. // update recommended number of players
  1125. CExLabel *pSuggestedPlayerCountLabel = dynamic_cast< CExLabel * >( FindChildByName( "SuggestedPlayerCountLabel" ) );
  1126. if ( pSuggestedPlayerCountLabel )
  1127. {
  1128. wchar_t wszLocalized[256];
  1129. wchar_t wszNum1[16]=L"";
  1130. wchar_t wszNum2[16]=L"";
  1131. V_snwprintf( wszNum1, ARRAYSIZE( wszNum1 ), L"%i", pCurMapInfo->m_aPlayerRange[0] );
  1132. V_snwprintf( wszNum2, ARRAYSIZE( wszNum2 ), L"%i", pCurMapInfo->m_aPlayerRange[1] );
  1133. g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_OfflinePractice_NumPlayers" ), 2, wszNum1, wszNum2 );
  1134. pSuggestedPlayerCountLabel->SetText( wszLocalized );
  1135. }
  1136. m_pDifficultyComboBox = dynamic_cast< ComboBox * >( FindChildByName( "DifficultyComboBox" ) );
  1137. if ( m_pDifficultyComboBox )
  1138. {
  1139. for ( int i = 0; i < ARRAYSIZE( g_pDifficultyModes ); ++i )
  1140. {
  1141. m_pDifficultyComboBox->AddItem( g_pDifficultyModes[i], NULL );
  1142. }
  1143. }
  1144. TextEntry *pNumPlayersTextEntry = dynamic_cast< TextEntry * >( FindChildByName( "NumPlayersTextEntry" ) );
  1145. if ( pNumPlayersTextEntry )
  1146. {
  1147. pNumPlayersTextEntry->SetBorder( pScheme->GetBorder( "ComboBoxBorder" ) );
  1148. }
  1149. SetupButtonActionSignalTarget( this, "StartOfflinePracticeButton" );
  1150. SetDialogVariable( "mapname", pCurMapInfo->m_strDisplayName.Get() );
  1151. UpdateControlsFromSavedData( m_pDifficultyComboBox, pNumPlayersTextEntry );
  1152. }
  1153. virtual void PerformLayout()
  1154. {
  1155. BaseClass::PerformLayout();
  1156. if ( m_pMapImagePanel )
  1157. {
  1158. // Use .res file ypos
  1159. int aPos[2];
  1160. m_pMapImagePanel->GetPos( aPos[0], aPos[1] );
  1161. // Center
  1162. m_pMapImagePanel->SetPos( ( GetWide() - m_pMapImagePanel->GetWide() ) / 2, aPos[1] );
  1163. }
  1164. SetNavToRelay( "StartOfflinePracticeButton" );
  1165. NavigateTo();
  1166. }
  1167. virtual void OnKeyCodePressed( KeyCode nCode )
  1168. {
  1169. ButtonCode_t nButtonCode = GetBaseButtonCode( nCode );
  1170. if ( nButtonCode == KEY_XBUTTON_X )
  1171. {
  1172. if ( m_pDifficultyComboBox )
  1173. {
  1174. m_pDifficultyComboBox->SilentActivateItemByRow( ( m_pDifficultyComboBox->GetActiveItem() + 1 ) % ARRAYSIZE( g_pDifficultyModes ) );
  1175. }
  1176. }
  1177. else if ( nButtonCode == KEY_XBUTTON_UP ||
  1178. nButtonCode == KEY_XSTICK1_UP ||
  1179. nButtonCode == KEY_XSTICK2_UP ||
  1180. nButtonCode == KEY_UP )
  1181. {
  1182. SetControlInt( "NumPlayersTextEntry", clamp( GetControlInt( "NumPlayersTextEntry", 0 ) + 1, 1, 31 ) );
  1183. }
  1184. else if ( nButtonCode == KEY_XBUTTON_DOWN ||
  1185. nButtonCode == KEY_XSTICK1_DOWN ||
  1186. nButtonCode == KEY_XSTICK2_DOWN ||
  1187. nButtonCode == KEY_RIGHT )
  1188. {
  1189. SetControlInt( "NumPlayersTextEntry", clamp( GetControlInt( "NumPlayersTextEntry", 0 ) - 1, 1, 31 ) );
  1190. }
  1191. else
  1192. {
  1193. BaseClass::OnKeyCodePressed( nCode );
  1194. }
  1195. }
  1196. virtual int GetNumPages() const
  1197. {
  1198. return GetMapCount();
  1199. }
  1200. int GetMapCount() const
  1201. {
  1202. return m_vecMapData[ m_iGameMode ].Count();
  1203. }
  1204. void GetControlValues( int *pOutNumPlayers, int *pOutDiff, CUtlString *pOutMap = NULL )
  1205. {
  1206. const MapInfo_t *pSelectedMapInfo = GetSelectedMapInfo();
  1207. if ( !pSelectedMapInfo )
  1208. return;
  1209. *pOutNumPlayers = clamp( GetControlInt( "NumPlayersTextEntry", 0 ), 1, 31 );
  1210. *pOutDiff = clamp( GetBotDifficulty(), 0, 3 );
  1211. if ( pOutMap )
  1212. {
  1213. *pOutMap = pSelectedMapInfo->m_strName;
  1214. }
  1215. }
  1216. bool DoSetup()
  1217. {
  1218. // @note Tom Bui: if you add any other convars that get set, please revert them
  1219. // in CTFBotManager::RevertOfflinePracticeConvars()
  1220. const MapInfo_t *pSelectedMapInfo = GetSelectedMapInfo();
  1221. if ( !pSelectedMapInfo )
  1222. return false;
  1223. int nQuota = 1;
  1224. int iDifficulty = 0;
  1225. GetControlValues( &nQuota, &iDifficulty );
  1226. // the player count in the dialog includes the human player, so decrease the bot count by one
  1227. ConVarRef tf_bot_quota( "tf_bot_quota" );
  1228. tf_bot_quota.SetValue( nQuota - 1 );
  1229. ConVarRef tf_bot_quota_mode( "tf_bot_quota_mode" );
  1230. tf_bot_quota_mode.SetValue( "normal" );
  1231. ConVarRef tf_bot_auto_vacate( "tf_bot_auto_vacate" );
  1232. tf_bot_auto_vacate.SetValue( 0 );
  1233. ConVarRef tf_bot_difficulty( "tf_bot_difficulty" );
  1234. tf_bot_difficulty.SetValue( iDifficulty );
  1235. ConVarRef tf_bot_offline_practice( "tf_bot_offline_practice" );
  1236. tf_bot_offline_practice.SetValue( 1 );
  1237. tf_training_client_message.SetValue( TRAINING_CLIENT_MESSAGE_WATCHING_INTRO_MOVIE );
  1238. SaveSettings();
  1239. return true;
  1240. }
  1241. private:
  1242. virtual KeyValues *GetTitleFormatData() const
  1243. {
  1244. KeyValues *pResult = new KeyValues( "data" );
  1245. if ( pResult )
  1246. {
  1247. const char *pGameModeToken = ( m_iGameMode >= 0 && m_iGameMode < NUM_GAME_MODES ) ? gs_pGameModeTokens[ m_iGameMode ] : "";
  1248. pResult->SetWString( "gametype", g_pVGuiLocalize->Find( pGameModeToken ) );
  1249. }
  1250. return pResult;
  1251. }
  1252. virtual void OnBackPressed()
  1253. {
  1254. SaveSettings();
  1255. }
  1256. void SaveSettings()
  1257. {
  1258. // Save settings
  1259. if ( m_pSavedData )
  1260. {
  1261. int nNumPlayers = 1;
  1262. int iDifficulty = 0;
  1263. CUtlString strMap;
  1264. GetControlValues( &nNumPlayers, &iDifficulty, &strMap );
  1265. m_pSavedData->SetInt( "tf_bot_quota", nNumPlayers );
  1266. m_pSavedData->SetInt( "tf_bot_difficulty", iDifficulty );
  1267. m_pSavedData->SetString( "map", strMap.Get() );
  1268. }
  1269. if ( !m_pSavedData->SaveToFile( g_pFullFileSystem, "OfflinePracticeConfig.vdf", "MOD" ) )
  1270. {
  1271. Warning( "Failed to write save data to OfflinePracticeConfig.vdf!\n" );
  1272. }
  1273. }
  1274. int GetBotDifficulty() const
  1275. {
  1276. if ( m_pDifficultyComboBox )
  1277. {
  1278. return m_pDifficultyComboBox->GetActiveItem();
  1279. }
  1280. AssertMsg( 0, "Shouldn't get here." );
  1281. return 0;
  1282. }
  1283. void UpdateControlsFromSavedData( ComboBox *pDifficultyComboBox, TextEntry *pNumPlayersTextEntry )
  1284. {
  1285. if ( !pDifficultyComboBox )
  1286. return;
  1287. if ( !pNumPlayersTextEntry )
  1288. return;
  1289. int iDifficulty = -1;
  1290. int nQuota = 0;
  1291. const char *defaultMap = "";
  1292. if ( m_pSavedData )
  1293. {
  1294. m_pSavedData->deleteThis();
  1295. m_pSavedData = NULL;
  1296. }
  1297. m_pSavedData = new KeyValues( "OfflinePracticeConfig" );
  1298. // load the config data
  1299. if ( m_pSavedData )
  1300. {
  1301. // this is game-specific data, so it should live in GAME, not CONFIG
  1302. if ( m_pSavedData->LoadFromFile( g_pFullFileSystem, "OfflinePracticeConfig.vdf", "MOD" ) )
  1303. {
  1304. iDifficulty = m_pSavedData->GetInt( "tf_bot_difficulty", -1 );
  1305. nQuota = m_pSavedData->GetInt( "tf_bot_quota", 0 );
  1306. defaultMap = m_pSavedData->GetString( "map", "" );
  1307. }
  1308. }
  1309. if ( m_pDefaultsData )
  1310. {
  1311. const int nMaxPlayers = m_pDefaultsData->GetInt( "max_players" );
  1312. if ( FStrEq( defaultMap, "" ) )
  1313. {
  1314. defaultMap = m_pDefaultsData->GetString( "map", "" );
  1315. }
  1316. if ( nQuota == 0 )
  1317. {
  1318. nQuota = m_pDefaultsData->GetInt( "suggested_players", nMaxPlayers );
  1319. }
  1320. if ( iDifficulty == -1 )
  1321. {
  1322. const char *pDifficultyString = m_pDefaultsData->GetString( "difficulty" );
  1323. if ( pDifficultyString )
  1324. {
  1325. static const char* difficulties [] = { "easy", "normal", "hard", "expert" };
  1326. for ( int i = 0, n = ARRAYSIZE(difficulties); i < n; ++i )
  1327. {
  1328. if ( Q_strcmp( difficulties[i], pDifficultyString ) == 0)
  1329. {
  1330. iDifficulty = i;
  1331. break;
  1332. }
  1333. }
  1334. }
  1335. }
  1336. }
  1337. // Set values in controls
  1338. m_pDifficultyComboBox->SilentActivateItemByRow( iDifficulty );
  1339. CFmtStr fmtNumPlayers( "%i", nQuota );
  1340. pNumPlayersTextEntry->SetText( fmtNumPlayers.Access() );
  1341. }
  1342. void LoadMapData()
  1343. {
  1344. m_pOfflinePracticeData = new KeyValues( "offline_practice.res" );
  1345. const char *pFilename = "resource/offline_practice.res";
  1346. if ( !m_pOfflinePracticeData->LoadFromFile( g_pFullFileSystem, pFilename, "MOD" ) )
  1347. {
  1348. Warning( "Could not load %s!\n", pFilename );
  1349. return;
  1350. }
  1351. // Save defaults
  1352. KeyValues *pDefaultsData = m_pOfflinePracticeData->FindKey( "defaults" );
  1353. if ( pDefaultsData )
  1354. {
  1355. m_pDefaultsData = pDefaultsData->MakeCopy();
  1356. }
  1357. KeyValues *pMapData = m_pOfflinePracticeData->FindKey( "maps" );
  1358. if ( pMapData )
  1359. {
  1360. FOR_EACH_TRUE_SUBKEY( pMapData, pCurMap )
  1361. {
  1362. MapInfo_t *pMapInfo = new MapInfo_t;
  1363. pMapInfo->m_strName = pCurMap->GetName();
  1364. pMapInfo->m_strDisplayName = pCurMap->GetString( "name" );
  1365. pMapInfo->m_aPlayerRange[0] = pCurMap->GetInt( "min_players" );
  1366. pMapInfo->m_aPlayerRange[1] = pCurMap->GetInt( "max_players" );
  1367. // Figure out which bucket to add to
  1368. const GameMode_t iGameMode = GetGameModeFromMapName( pMapInfo->m_strName.Get() );
  1369. if ( iGameMode != MODE_INVALID )
  1370. {
  1371. AddMapInfo( pMapInfo, iGameMode );
  1372. }
  1373. }
  1374. }
  1375. pMapData->deleteThis();
  1376. }
  1377. GameMode_t GetGameModeFromMapName( const char *pMapName )
  1378. {
  1379. if ( !V_strnicmp( pMapName, "cp", 2 ) )
  1380. {
  1381. return MODE_CP;
  1382. }
  1383. else if ( !V_strnicmp( pMapName, "koth", 4 ) )
  1384. {
  1385. return MODE_KOTH;
  1386. }
  1387. else if ( !V_strnicmp( pMapName, "pl", 2 ) )
  1388. {
  1389. return MODE_PL;
  1390. }
  1391. AssertMsg( 0, "Should never get here!" );
  1392. return MODE_INVALID;
  1393. }
  1394. void AddMapInfo( MapInfo_t *pMapInfo, GameMode_t iGameMode )
  1395. {
  1396. m_vecMapData[ iGameMode ].AddToTail( pMapInfo );
  1397. }
  1398. int m_iGameMode;
  1399. KeyValues *m_pSavedData;
  1400. KeyValues *m_pDefaultsData;
  1401. ImagePanel *m_pMapImagePanel;
  1402. ComboBox *m_pDifficultyComboBox;
  1403. KeyValues *m_pOfflinePracticeData;
  1404. CUtlVector< MapInfo_t * > m_vecMapData[ NUM_GAME_MODES ];
  1405. };
  1406. DECLARE_BUILD_FACTORY( COfflinePractice_MapSelectionPanel );
  1407. //------------------------------------------------------------------------------------------------------
  1408. class CTrainingDialog : public EditablePanel
  1409. {
  1410. DECLARE_CLASS_SIMPLE( CTrainingDialog, EditablePanel );
  1411. public:
  1412. CTrainingDialog( Panel *parent )
  1413. : EditablePanel( parent, TRAINING_DIALOG_NAME ),
  1414. m_pBackButton( NULL ),
  1415. m_pCancelButton( NULL ),
  1416. m_pGradientBgPanel( NULL ),
  1417. m_pModeSelectionPanel( NULL ),
  1418. m_pCurrentPagePanel( NULL ),
  1419. m_pBasicTraining_ClassSelectionPanel( NULL ),
  1420. m_pBasicTraining_ClassDetailsPanel( NULL ),
  1421. m_pOfflinePractice_ModeSelectionPanel( NULL ),
  1422. m_pOfflinePractice_MapSelectionPanel( NULL ),
  1423. m_pTrainingData( NULL ),
  1424. m_bStartTraining( false ),
  1425. m_bContinue( false )
  1426. {
  1427. HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
  1428. SetScheme(scheme);
  1429. SetProportional( true );
  1430. m_pContainer = new EditablePanel( this, "Container" );
  1431. // load configuration
  1432. const char *filename = "resource/training.res";
  1433. m_pTrainingData = new KeyValues( "training.res" );
  1434. Assert( m_pTrainingData );
  1435. if ( !m_pTrainingData->LoadFromFile( g_pFullFileSystem, filename, "MOD" ) )
  1436. {
  1437. Warning( "Unable to load '%s'\n", filename );
  1438. AssertMsg( 0, "Couldn't load training data!" );
  1439. }
  1440. C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_open", "training" );
  1441. }
  1442. virtual ~CTrainingDialog()
  1443. {
  1444. C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_close", "training" );
  1445. ivgui()->RemoveTickSignal( GetVPanel() );
  1446. }
  1447. virtual void SetDialogVariable( const char *pVarName, const char *pValue )
  1448. {
  1449. m_pContainer->SetDialogVariable( pVarName, pValue );
  1450. }
  1451. virtual void SetDialogVariable( const char *pVarName, const wchar_t *pValue )
  1452. {
  1453. m_pContainer->SetDialogVariable( pVarName, pValue );
  1454. }
  1455. virtual void SetDialogVariable( const char *pVarName, int nValue )
  1456. {
  1457. m_pContainer->SetDialogVariable( pVarName, nValue );
  1458. }
  1459. virtual void SetDialogVariable( const char *pVarName, float flValue )
  1460. {
  1461. m_pContainer->SetDialogVariable( pVarName, flValue );
  1462. }
  1463. void SetupButton( const char *pPanelName, CExButton **ppOut = NULL )
  1464. {
  1465. Panel *pPanel = m_pContainer->FindChildByName( pPanelName );
  1466. if ( pPanel )
  1467. {
  1468. pPanel->AddActionSignalTarget( this );
  1469. }
  1470. if ( ppOut )
  1471. {
  1472. *ppOut = static_cast< CExButton * >( pPanel );
  1473. }
  1474. }
  1475. virtual void Show()
  1476. {
  1477. SetVisible( true );
  1478. MakePopup();
  1479. MoveToFront();
  1480. SetKeyBoardInputEnabled( true );
  1481. SetMouseInputEnabled( true );
  1482. TFModalStack()->PushModal( this );
  1483. }
  1484. virtual void OnThink()
  1485. {
  1486. BaseClass::OnThink();
  1487. }
  1488. virtual void OnCommand( const char *pCommand )
  1489. {
  1490. C_CTFGameStats::ImmediateWriteInterfaceEvent( "on_command(training)", pCommand );
  1491. if ( FStrEq( pCommand, "prevpage" ) )
  1492. {
  1493. ShowPrevPage();
  1494. }
  1495. else if ( FStrEq( pCommand, "cancel" ) )
  1496. {
  1497. Close();
  1498. }
  1499. else if ( FStrEq( pCommand, "basictrainingselected" ) )
  1500. {
  1501. BasicTraining_ShowClassSelection();
  1502. }
  1503. else if ( FStrEq( pCommand, "offlinepracticeselected" ) )
  1504. {
  1505. OfflinePractice_ShowPracticeMode();
  1506. }
  1507. else if ( FStrEq( pCommand, "startbasictraining" ) )
  1508. {
  1509. BasicTraining_Start();
  1510. }
  1511. else if ( !V_strnicmp( pCommand, "basictraining_classselection_", 29 ) )
  1512. {
  1513. BasicTraining_ShowClassDetailsPage( pCommand + 29 );
  1514. }
  1515. else if ( FStrEq( pCommand, "selectcurrentgamemode" ) )
  1516. {
  1517. OfflinePractice_ShowMapSelection();
  1518. }
  1519. else if ( FStrEq( pCommand, "startofflinepractice" ) )
  1520. {
  1521. OfflinePractice_Start();
  1522. }
  1523. else
  1524. {
  1525. BaseClass::OnCommand( pCommand );
  1526. }
  1527. }
  1528. void SetCurrentPage( CTrainingBasePanel *pPanel, bool bGoingBack = false )
  1529. {
  1530. AssertMsg( pPanel, "Setting current page to NULL!" );
  1531. pPanel->SetVisible( true );
  1532. if ( !bGoingBack )
  1533. {
  1534. pPanel->SetPrevPage( m_pCurrentPagePanel );
  1535. }
  1536. m_pCurrentPagePanel = pPanel;
  1537. m_pCurrentPagePanel->InvalidateLayout( false, true );
  1538. InvalidateLayout( true, false );
  1539. }
  1540. void ShowPrevPage()
  1541. {
  1542. CTrainingBasePanel *pPrevPagePanel = m_pCurrentPagePanel->GetPrevPage();
  1543. if ( pPrevPagePanel )
  1544. {
  1545. if ( m_pCurrentPagePanel == pPrevPagePanel )
  1546. return;
  1547. m_pCurrentPagePanel->SetVisible( false );
  1548. m_pCurrentPagePanel->OnBackPressed();
  1549. SetCurrentPage( pPrevPagePanel, true );
  1550. }
  1551. else
  1552. {
  1553. OnCommand( "cancel" );
  1554. }
  1555. }
  1556. void HideCurrentPage()
  1557. {
  1558. m_pCurrentPagePanel->SetVisible( false );
  1559. }
  1560. void BasicTraining_ShowClassSelection()
  1561. {
  1562. if ( m_pCurrentPagePanel == m_pBasicTraining_ClassSelectionPanel )
  1563. return;
  1564. HideCurrentPage();
  1565. SetCurrentPage( m_pBasicTraining_ClassSelectionPanel );
  1566. }
  1567. int GetClassFromData( KeyValues *pClassData )
  1568. {
  1569. const int iClass = pClassData->GetInt( "class", TF_CLASS_SOLDIER );
  1570. if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS )
  1571. {
  1572. return TF_CLASS_SOLDIER;
  1573. }
  1574. return iClass;
  1575. }
  1576. static void ConfirmDialogCallback( bool bConfirmed, void *pContext )
  1577. {
  1578. CTrainingDialog *pDialog = ( CTrainingDialog * )pContext;
  1579. if ( pDialog )
  1580. {
  1581. pDialog->m_bContinue = bConfirmed;
  1582. pDialog->m_bStartTraining = true;
  1583. }
  1584. }
  1585. void ConfirmContinue()
  1586. {
  1587. ShowConfirmDialog( "#TR_ContinueTitle", "#TR_ContinueMsg", "#TR_Continue", "#TR_StartOver", &ConfirmDialogCallback, NULL, this );
  1588. }
  1589. void BasicTraining_Start()
  1590. {
  1591. if ( !m_pTrainingData )
  1592. return;
  1593. KeyValues *pData = m_pTrainingData->FindKey( m_strBasicTrainingClassName.Get() );
  1594. if ( !pData )
  1595. return;
  1596. // Override for soldier - if target practice is complete, start from
  1597. const int iClass = GetClassFromData( pData );
  1598. int nProgress = Training_GetClassProgress( iClass );
  1599. if ( iClass == TF_CLASS_SOLDIER && nProgress >= 1 )
  1600. {
  1601. ConfirmContinue();
  1602. return;
  1603. }
  1604. m_bStartTraining = true;
  1605. m_bContinue = false;
  1606. }
  1607. virtual void Think()
  1608. {
  1609. if ( !m_bStartTraining )
  1610. return;
  1611. KeyValues *pData = m_pTrainingData->FindKey( m_strBasicTrainingClassName.Get() );
  1612. if ( !pData )
  1613. return;
  1614. // Override map if user has selected to continue
  1615. const char *pMapName = pData->GetString( "map", NULL );
  1616. if ( m_bContinue )
  1617. {
  1618. pMapName = "tr_dustbowl";
  1619. }
  1620. if ( pMapName )
  1621. {
  1622. const int iClass = GetClassFromData( pData );
  1623. ConVarRef training_class( "training_class" );
  1624. training_class.SetValue( iClass );
  1625. const char* pMapVideo = pData->GetString( "video", "" );
  1626. training_map_video.SetValue( pMapVideo );
  1627. // create the command to execute
  1628. CFmtStr fmtMapCommand( "disconnect\nwait\nwait\n\nprogress_enable\nmap %s\n", pMapName );
  1629. // exec
  1630. engine->ClientCmd_Unrestricted( fmtMapCommand.Access() );
  1631. }
  1632. Close();
  1633. }
  1634. void BasicTraining_ShowClassDetailsPage( const char *pClassName )
  1635. {
  1636. if ( m_pCurrentPagePanel == m_pBasicTraining_ClassDetailsPanel )
  1637. return;
  1638. HideCurrentPage();
  1639. m_pBasicTraining_ClassDetailsPanel->SetClass( pClassName );
  1640. m_pBasicTraining_ClassDetailsPanel->InvalidateLayout( true, true );
  1641. SetCurrentPage( m_pBasicTraining_ClassDetailsPanel );
  1642. // Cache class
  1643. m_strBasicTrainingClassName = pClassName;
  1644. }
  1645. void OfflinePractice_ShowPracticeMode()
  1646. {
  1647. if ( m_pCurrentPagePanel == m_pOfflinePractice_ModeSelectionPanel )
  1648. return;
  1649. HideCurrentPage();
  1650. SetCurrentPage( m_pOfflinePractice_ModeSelectionPanel );
  1651. }
  1652. void OfflinePractice_ShowMapSelection()
  1653. {
  1654. if ( m_pCurrentPagePanel == m_pOfflinePractice_MapSelectionPanel )
  1655. return;
  1656. // Pass on the game mode to the map selection panel so it will only show corresponding maps
  1657. const GameMode_t nGameMode = m_pOfflinePractice_ModeSelectionPanel->GetMode();
  1658. m_pOfflinePractice_MapSelectionPanel->SetGameMode( nGameMode );
  1659. HideCurrentPage();
  1660. SetCurrentPage( m_pOfflinePractice_MapSelectionPanel );
  1661. }
  1662. void OfflinePractice_Start()
  1663. {
  1664. // reset server enforced cvars
  1665. g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED );
  1666. // Cheats were disabled; revert all cheat cvars to their default values.
  1667. // This must be done heading into multiplayer games because people can play
  1668. // demos etc and set cheat cvars with sv_cheats 0.
  1669. g_pCVar->RevertFlaggedConVars( FCVAR_CHEAT );
  1670. DevMsg( "FCVAR_CHEAT cvars reverted to defaults.\n" );
  1671. if ( m_pOfflinePractice_MapSelectionPanel->DoSetup() )
  1672. {
  1673. // create the command to execute
  1674. CFmtStr1024 fmtMapCommand(
  1675. "disconnect\nwait\nwait\nmaxplayers %i\n\nprogress_enable\nmap %s\n",
  1676. m_pOfflinePractice_MapSelectionPanel->GetMaxPlayers(),
  1677. m_pOfflinePractice_MapSelectionPanel->GetMapName()
  1678. );
  1679. // exec
  1680. engine->ClientCmd_Unrestricted( fmtMapCommand.Access() );
  1681. }
  1682. Close();
  1683. }
  1684. virtual void ApplySchemeSettings( IScheme *pScheme )
  1685. {
  1686. BaseClass::ApplySchemeSettings( pScheme );
  1687. LoadControlSettings( "Resource/ui/training/main.res" );
  1688. SetupButton( "CancelButton", &m_pCancelButton );
  1689. SetupButton( "BackButton", &m_pBackButton );
  1690. m_pModeSelectionPanel = dynamic_cast< CModeSelectionPanel * >( m_pContainer->FindChildByName( "ModeSelectionPanel" ) ); Assert( m_pModeSelectionPanel );
  1691. m_pBasicTraining_ClassSelectionPanel = dynamic_cast< CBasicTraining_ClassSelectionPanel * >( m_pContainer->FindChildByName( "BasicTraining_ClassSelectionPanel" ) ); Assert( m_pBasicTraining_ClassSelectionPanel );
  1692. m_pBasicTraining_ClassDetailsPanel = dynamic_cast< CBasicTraining_ClassDetailsPanel * >( m_pContainer->FindChildByName( "BasicTraining_ClassDetailsPanel" ) ); Assert( m_pBasicTraining_ClassDetailsPanel );
  1693. m_pOfflinePractice_ModeSelectionPanel = dynamic_cast< COfflinePractice_ModeSelectionPanel * >( m_pContainer->FindChildByName( "OfflinePractice_ModeSelectionPanel" ) ); Assert( m_pOfflinePractice_ModeSelectionPanel );
  1694. m_pOfflinePractice_MapSelectionPanel = dynamic_cast< COfflinePractice_MapSelectionPanel * >( m_pContainer->FindChildByName( "OfflinePractice_MapSelectionPanel" ) ); Assert( m_pOfflinePractice_MapSelectionPanel );
  1695. m_pGradientBgPanel = dynamic_cast< ImagePanel * >( m_pContainer->FindChildByName( "GradientBgPanel" ) );
  1696. m_pCurrentPagePanel = m_pModeSelectionPanel;
  1697. }
  1698. virtual void PerformLayout()
  1699. {
  1700. BaseClass::PerformLayout();
  1701. if ( m_pCurrentPagePanel )
  1702. {
  1703. m_pCurrentPagePanel->SetVisible( true );
  1704. const bool bFirstPage = m_pCurrentPagePanel->IsFirstPage();
  1705. if ( m_pBackButton && m_pCancelButton )
  1706. {
  1707. m_pBackButton->SetVisible( !bFirstPage );
  1708. int w = m_pContainer->GetWide();
  1709. const int nBuffer = XRES( 5 );
  1710. int cbx, cby;
  1711. m_pCancelButton->GetPos( cbx, cby );
  1712. if ( bFirstPage )
  1713. {
  1714. m_pCancelButton->SetPos( ( w - m_pCancelButton->GetWide() ) / 2, cby );
  1715. }
  1716. else
  1717. {
  1718. m_pBackButton->SetPos( w/2 - m_pBackButton->GetWide() - nBuffer, cby );
  1719. m_pCancelButton->SetPos( w/2 + nBuffer, cby );
  1720. }
  1721. if ( m_pGradientBgPanel )
  1722. {
  1723. m_pGradientBgPanel->SetVisible( m_pCurrentPagePanel->ShouldShowGradient() );
  1724. }
  1725. }
  1726. }
  1727. }
  1728. virtual void OnKeyCodePressed( KeyCode code )
  1729. {
  1730. ButtonCode_t nButtonCode = GetBaseButtonCode( code );
  1731. if ( code == KEY_ESCAPE )
  1732. {
  1733. OnCommand( "cancel" );
  1734. }
  1735. else if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B )
  1736. {
  1737. OnCommand( "prevpage" );
  1738. }
  1739. else if ( code == KEY_ENTER || code == KEY_SPACE || nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
  1740. {
  1741. if ( m_pCurrentPagePanel )
  1742. {
  1743. m_pCurrentPagePanel->Go();
  1744. }
  1745. }
  1746. else
  1747. {
  1748. BaseClass::OnKeyCodePressed( code );
  1749. }
  1750. }
  1751. protected:
  1752. void Close()
  1753. {
  1754. SetVisible( false );
  1755. TFModalStack()->PopModal( this );
  1756. MarkForDeletion();
  1757. }
  1758. private:
  1759. EditablePanel *m_pContainer;
  1760. CModeSelectionPanel *m_pModeSelectionPanel;
  1761. CBasicTraining_ClassSelectionPanel *m_pBasicTraining_ClassSelectionPanel;
  1762. CBasicTraining_ClassDetailsPanel *m_pBasicTraining_ClassDetailsPanel;
  1763. COfflinePractice_ModeSelectionPanel *m_pOfflinePractice_ModeSelectionPanel;
  1764. COfflinePractice_MapSelectionPanel *m_pOfflinePractice_MapSelectionPanel;
  1765. CTrainingBasePanel *m_pCurrentPagePanel;
  1766. CTrainingBasePanel *m_pPrevPagePanel;
  1767. CExButton *m_pCancelButton;
  1768. CExButton *m_pBackButton;
  1769. ImagePanel *m_pGradientBgPanel;
  1770. KeyValues *m_pTrainingData;
  1771. CUtlString m_strBasicTrainingClassName;
  1772. bool m_bStartTraining;
  1773. bool m_bContinue;
  1774. };
  1775. static DHANDLE<CTrainingDialog> g_pTrainingDialog;
  1776. //------------------------------------------------------------------------------------------------------
  1777. void CL_ShowTrainingDialog( const CCommand &args )
  1778. {
  1779. if ( g_pTrainingDialog.Get() == NULL )
  1780. {
  1781. IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) );
  1782. g_pTrainingDialog = new CTrainingDialog( (CHudMainMenuOverride*)pMMOverride );
  1783. g_pTrainingDialog->InvalidateLayout( true, true );
  1784. }
  1785. g_pTrainingDialog->Show();
  1786. }
  1787. //------------------------------------------------------------------------------------------------------
  1788. CON_COMMAND( cl_training_class_unlock_all, "Unlock all training" )
  1789. {
  1790. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i )
  1791. {
  1792. Training_MarkClassComplete( i, 100 );
  1793. }
  1794. }
  1795. //------------------------------------------------------------------------------------------------------
  1796. #ifdef _DEBUG
  1797. CON_COMMAND( training_set, 0 )
  1798. {
  1799. if ( args.ArgC() != 3 )
  1800. {
  1801. Warning( "Not enough arguments\n" );
  1802. return;
  1803. }
  1804. Training_MarkClassComplete( atoi( args[1] ), atoi( args[2] ) );
  1805. }
  1806. #endif
  1807. //------------------------------------------------------------------------------------------------------
  1808. static ConCommand training_showdlg( "training_showdlg", &CL_ShowTrainingDialog, "Displays the training dialog." );