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.

1724 lines
49 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "BasePanel.h"
  8. #include "NewGameDialog.h"
  9. #include "EngineInterface.h"
  10. #include "vgui_controls/Button.h"
  11. #include "vgui_controls/CheckButton.h"
  12. #include "KeyValues.h"
  13. #include "vgui/ISurface.h"
  14. #include "vgui/IInput.h"
  15. #include "vgui/ILocalize.h"
  16. #include <vgui/ISystem.h>
  17. #include "vgui_controls/RadioButton.h"
  18. #include "vgui_controls/ComboBox.h"
  19. #include "vgui_controls/ImagePanel.h"
  20. #include "vgui_controls/Frame.h"
  21. #include "vgui_controls/ControllerMap.h"
  22. #include "filesystem.h"
  23. #include "ModInfo.h"
  24. #include "tier1/convar.h"
  25. #include "GameUI_Interface.h"
  26. #include "tier0/icommandline.h"
  27. #include "vgui_controls/AnimationController.h"
  28. #include "CommentaryExplanationDialog.h"
  29. #include "vgui_controls/BitmapImagePanel.h"
  30. #include "BonusMapsDatabase.h"
  31. #include <stdio.h>
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. using namespace vgui;
  35. static float g_ScrollSpeedSlow;
  36. static float g_ScrollSpeedFast;
  37. // sort function used in displaying chapter list
  38. struct chapter_t
  39. {
  40. char filename[32];
  41. };
  42. static int __cdecl ChapterSortFunc(const void *elem1, const void *elem2)
  43. {
  44. chapter_t *c1 = (chapter_t *)elem1;
  45. chapter_t *c2 = (chapter_t *)elem2;
  46. // compare chapter number first
  47. static int chapterlen = strlen("chapter");
  48. if (atoi(c1->filename + chapterlen) > atoi(c2->filename + chapterlen))
  49. return 1;
  50. else if (atoi(c1->filename + chapterlen) < atoi(c2->filename + chapterlen))
  51. return -1;
  52. // compare length second (longer string show up later in the list, eg. chapter9 before chapter9a)
  53. if (strlen(c1->filename) > strlen(c2->filename))
  54. return 1;
  55. else if (strlen(c1->filename) < strlen(c2->filename))
  56. return -1;
  57. // compare strings third
  58. return strcmp(c1->filename, c2->filename);
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose: invisible panel used for selecting a chapter panel
  62. //-----------------------------------------------------------------------------
  63. class CSelectionOverlayPanel : public vgui::Panel
  64. {
  65. DECLARE_CLASS_SIMPLE( CSelectionOverlayPanel, Panel );
  66. int m_iChapterIndex;
  67. CNewGameDialog *m_pSelectionTarget;
  68. public:
  69. CSelectionOverlayPanel( Panel *parent, CNewGameDialog *selectionTarget, int chapterIndex ) : BaseClass( parent, NULL )
  70. {
  71. m_iChapterIndex = chapterIndex;
  72. m_pSelectionTarget = selectionTarget;
  73. SetPaintEnabled(false);
  74. SetPaintBackgroundEnabled(false);
  75. }
  76. virtual void OnMousePressed( vgui::MouseCode code )
  77. {
  78. if (GetParent()->IsEnabled())
  79. {
  80. m_pSelectionTarget->SetSelectedChapterIndex( m_iChapterIndex );
  81. }
  82. }
  83. virtual void OnMouseDoublePressed( vgui::MouseCode code )
  84. {
  85. // call the panel
  86. OnMousePressed( code );
  87. if (GetParent()->IsEnabled())
  88. {
  89. PostMessage( m_pSelectionTarget, new KeyValues("Command", "command", "play") );
  90. }
  91. }
  92. };
  93. //-----------------------------------------------------------------------------
  94. // Purpose: selectable item with screenshot for an individual chapter in the dialog
  95. //-----------------------------------------------------------------------------
  96. class CGameChapterPanel : public vgui::EditablePanel
  97. {
  98. DECLARE_CLASS_SIMPLE( CGameChapterPanel, vgui::EditablePanel );
  99. ImagePanel *m_pLevelPicBorder;
  100. ImagePanel *m_pLevelPic;
  101. ImagePanel *m_pCommentaryIcon;
  102. Label *m_pChapterLabel;
  103. Label *m_pChapterNameLabel;
  104. Color m_TextColor;
  105. Color m_DisabledColor;
  106. Color m_SelectedColor;
  107. Color m_FillColor;
  108. char m_szConfigFile[_MAX_PATH];
  109. char m_szChapter[32];
  110. bool m_bTeaserChapter;
  111. bool m_bHasBonus;
  112. bool m_bCommentaryMode;
  113. bool m_bIsSelected;
  114. public:
  115. CGameChapterPanel( CNewGameDialog *parent, const char *name, const char *chapterName, int chapterIndex, const char *chapterNumber, const char *chapterConfigFile, bool bCommentary ) : BaseClass( parent, name )
  116. {
  117. Q_strncpy( m_szConfigFile, chapterConfigFile, sizeof(m_szConfigFile) );
  118. Q_strncpy( m_szChapter, chapterNumber, sizeof(m_szChapter) );
  119. m_pLevelPicBorder = SETUP_PANEL( new ImagePanel( this, "LevelPicBorder" ) );
  120. m_pLevelPic = SETUP_PANEL( new ImagePanel( this, "LevelPic" ) );
  121. m_pCommentaryIcon = NULL;
  122. m_bCommentaryMode = bCommentary;
  123. m_bIsSelected = false;
  124. wchar_t text[32];
  125. wchar_t num[32];
  126. wchar_t *chapter = g_pVGuiLocalize->Find("#GameUI_Chapter");
  127. g_pVGuiLocalize->ConvertANSIToUnicode( chapterNumber, num, sizeof(num) );
  128. _snwprintf( text, ARRAYSIZE(text), L"%ls %ls", chapter ? chapter : L"CHAPTER", num );
  129. if ( ModInfo().IsSinglePlayerOnly() )
  130. {
  131. m_pChapterLabel = new Label( this, "ChapterLabel", text );
  132. m_pChapterNameLabel = new Label( this, "ChapterNameLabel", chapterName );
  133. }
  134. else
  135. {
  136. m_pChapterLabel = new Label( this, "ChapterLabel", chapterName );
  137. m_pChapterNameLabel = new Label( this, "ChapterNameLabel", "#GameUI_LoadCommentary" );
  138. }
  139. SetPaintBackgroundEnabled( false );
  140. // the image has the same name as the config file
  141. char szMaterial[ MAX_PATH ];
  142. Q_snprintf( szMaterial, sizeof(szMaterial), "chapters/%s", chapterConfigFile );
  143. char *ext = strstr( szMaterial, "." );
  144. if ( ext )
  145. {
  146. *ext = 0;
  147. }
  148. m_pLevelPic->SetImage( szMaterial );
  149. KeyValues *pKeys = NULL;
  150. if ( GameUI().IsConsoleUI() )
  151. {
  152. pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameChapterPanel.res" );
  153. }
  154. LoadControlSettings( "Resource/NewGameChapterPanel.res", NULL, pKeys );
  155. int px, py;
  156. m_pLevelPicBorder->GetPos( px, py );
  157. SetSize( m_pLevelPicBorder->GetWide(), py + m_pLevelPicBorder->GetTall() );
  158. // create a selection panel the size of the page
  159. CSelectionOverlayPanel *overlay = new CSelectionOverlayPanel( this, parent, chapterIndex );
  160. overlay->SetBounds(0, 0, GetWide(), GetTall());
  161. overlay->MoveToFront();
  162. // HACK: Detect new episode teasers by the "Coming Soon" text
  163. wchar_t w_szStrTemp[256];
  164. m_pChapterNameLabel->GetText( w_szStrTemp, sizeof(w_szStrTemp) );
  165. m_bTeaserChapter = !wcscmp(w_szStrTemp, L"Coming Soon");
  166. m_bHasBonus = false;
  167. }
  168. virtual void ApplySchemeSettings( IScheme *pScheme )
  169. {
  170. m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) );
  171. m_FillColor = pScheme->GetColor( "NewGame.FillColor", Color(255, 255, 255, 255) );
  172. m_DisabledColor = pScheme->GetColor( "NewGame.DisabledColor", Color(255, 255, 255, 255) );
  173. m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) );
  174. BaseClass::ApplySchemeSettings( pScheme );
  175. // Hide chapter numbers for new episode teasers
  176. if ( m_bTeaserChapter )
  177. {
  178. m_pChapterLabel->SetVisible( false );
  179. }
  180. if ( GameUI().IsConsoleUI() )
  181. {
  182. m_pChapterNameLabel->SetVisible( false );
  183. }
  184. m_pCommentaryIcon = dynamic_cast<ImagePanel*>( FindChildByName( "CommentaryIcon" ) );
  185. if ( m_pCommentaryIcon )
  186. m_pCommentaryIcon->SetVisible( m_bCommentaryMode );
  187. }
  188. bool IsSelected( void ) const { return m_bIsSelected; }
  189. void SetSelected( bool state )
  190. {
  191. m_bIsSelected = state;
  192. // update the text/border colors
  193. if ( !IsEnabled() )
  194. {
  195. m_pChapterLabel->SetFgColor( m_DisabledColor );
  196. m_pChapterNameLabel->SetFgColor( Color(0, 0, 0, 0) );
  197. m_pLevelPicBorder->SetFillColor( m_DisabledColor );
  198. m_pLevelPic->SetAlpha( GameUI().IsConsoleUI() ? 64 : 128 );
  199. return;
  200. }
  201. if ( state )
  202. {
  203. if ( !GameUI().IsConsoleUI() )
  204. {
  205. m_pChapterLabel->SetFgColor( m_SelectedColor );
  206. m_pChapterNameLabel->SetFgColor( m_SelectedColor );
  207. }
  208. m_pLevelPicBorder->SetFillColor( m_SelectedColor );
  209. }
  210. else
  211. {
  212. m_pChapterLabel->SetFgColor( m_TextColor );
  213. m_pChapterNameLabel->SetFgColor( m_TextColor );
  214. m_pLevelPicBorder->SetFillColor( m_FillColor );
  215. }
  216. m_pLevelPic->SetAlpha( 255 );
  217. }
  218. const char *GetConfigFile()
  219. {
  220. return m_szConfigFile;
  221. }
  222. const char *GetChapter()
  223. {
  224. return m_szChapter;
  225. }
  226. bool IsTeaserChapter()
  227. {
  228. return m_bTeaserChapter;
  229. }
  230. bool HasBonus()
  231. {
  232. return m_bHasBonus;
  233. }
  234. void SetCommentaryMode( bool bCommentaryMode )
  235. {
  236. m_bCommentaryMode = bCommentaryMode;
  237. if ( m_pCommentaryIcon )
  238. m_pCommentaryIcon->SetVisible( m_bCommentaryMode );
  239. }
  240. };
  241. const char *COM_GetModDirectory()
  242. {
  243. static char modDir[MAX_PATH];
  244. if ( Q_strlen( modDir ) == 0 )
  245. {
  246. const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
  247. Q_strncpy( modDir, gamedir, sizeof(modDir) );
  248. if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
  249. {
  250. Q_StripLastDir( modDir, sizeof(modDir) );
  251. int dirlen = Q_strlen( modDir );
  252. Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
  253. }
  254. }
  255. return modDir;
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose: new game chapter selection
  259. //-----------------------------------------------------------------------------
  260. CNewGameDialog::CNewGameDialog(vgui::Panel *parent, bool bCommentaryMode) : BaseClass(parent, "NewGameDialog")
  261. {
  262. SetDeleteSelfOnClose(true);
  263. SetBounds(0, 0, 372, 160);
  264. SetSizeable( false );
  265. m_iSelectedChapter = -1;
  266. m_ActiveTitleIdx = 0;
  267. m_bCommentaryMode = bCommentaryMode;
  268. m_bMapStarting = false;
  269. m_bScrolling = false;
  270. m_ScrollCt = 0;
  271. m_ScrollSpeed = 0.f;
  272. m_ButtonPressed = SCROLL_NONE;
  273. m_ScrollDirection = SCROLL_NONE;
  274. m_pCommentaryLabel = NULL;
  275. m_iBonusSelection = 0;
  276. m_bScrollToFirstBonusMap = false;
  277. SetTitle("#GameUI_NewGame", true);
  278. m_pNextButton = new Button( this, "Next", "#gameui_next" );
  279. m_pPrevButton = new Button( this, "Prev", "#gameui_prev" );
  280. m_pPlayButton = new CNewGamePlayButton( this, "Play", "#GameUI_Play" );
  281. m_pPlayButton->SetCommand( "Play" );
  282. vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" );
  283. cancel->SetCommand( "Close" );
  284. m_pCenterBg = SETUP_PANEL( new Panel( this, "CenterBG" ) );
  285. m_pCenterBg->SetVisible( false );
  286. if ( GameUI().IsConsoleUI() )
  287. {
  288. m_pNextButton->SetVisible( false );
  289. m_pPrevButton->SetVisible( false );
  290. m_pPlayButton->SetVisible( false );
  291. cancel->SetVisible( false );
  292. m_pCenterBg->SetPaintBackgroundType( 2 );
  293. m_pCenterBg->SetVisible( true );
  294. m_pChapterTitleLabels[0] = SETUP_PANEL( new Label( this, "ChapterTitleLabel", "" ) );
  295. m_pChapterTitleLabels[0]->SetVisible( true );
  296. m_pChapterTitleLabels[0]->SetFgColor( Color( 255, 255, 255, 255 ) );
  297. m_pChapterTitleLabels[1] = SETUP_PANEL( new Label( this, "ChapterTitleLabel2", "" ) );
  298. m_pChapterTitleLabels[1]->SetVisible( true );
  299. m_pChapterTitleLabels[1]->SetAlpha( 0 );
  300. m_pChapterTitleLabels[1]->SetFgColor( Color( 255, 255, 255, 255 ) );
  301. m_pBonusSelection = SETUP_PANEL( new Label( this, "BonusSelectionLabel", "#GameUI_BonusMapsStandard" ) );
  302. m_pBonusSelectionBorder = SETUP_PANEL( new ImagePanel( this, "BonusSelectionBorder" ) );
  303. m_pFooter = new CFooterPanel( parent, "NewGameFooter" );
  304. m_pFooter->AddNewButtonLabel( "#GameUI_Play", "#GameUI_Icons_A_BUTTON" );
  305. m_pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" );
  306. }
  307. else
  308. {
  309. m_pFooter = NULL;
  310. }
  311. // parse out the chapters off disk
  312. static const int MAX_CHAPTERS = 32;
  313. chapter_t chapters[MAX_CHAPTERS];
  314. char szFullFileName[MAX_PATH];
  315. int chapterIndex = 0;
  316. if ( IsPC() || !IsX360() )
  317. {
  318. FileFindHandle_t findHandle = FILESYSTEM_INVALID_FIND_HANDLE;
  319. const char *fileName = "cfg/chapter*.cfg";
  320. fileName = g_pFullFileSystem->FindFirst( fileName, &findHandle );
  321. while ( fileName && chapterIndex < MAX_CHAPTERS )
  322. {
  323. if ( fileName[0] )
  324. {
  325. // Only load chapter configs from the current mod's cfg dir
  326. // or else chapters appear that we don't want!
  327. Q_snprintf( szFullFileName, sizeof(szFullFileName), "cfg/%s", fileName );
  328. FileHandle_t f = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" );
  329. if ( f )
  330. {
  331. // don't load chapter files that are empty, used in the demo
  332. if ( g_pFullFileSystem->Size(f) > 0 )
  333. {
  334. Q_strncpy(chapters[chapterIndex].filename, fileName, sizeof(chapters[chapterIndex].filename));
  335. ++chapterIndex;
  336. }
  337. g_pFullFileSystem->Close( f );
  338. }
  339. }
  340. fileName = g_pFullFileSystem->FindNext(findHandle);
  341. }
  342. }
  343. else if ( IsX360() )
  344. {
  345. int ChapterStringIndex = 0;
  346. bool bExists = true;
  347. while ( bExists && chapterIndex < MAX_CHAPTERS )
  348. {
  349. Q_snprintf( szFullFileName, sizeof( szFullFileName ), "cfg/chapter%d.cfg", ChapterStringIndex+1 );
  350. FileHandle_t f = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" );
  351. if ( f )
  352. {
  353. Q_strncpy(chapters[chapterIndex].filename, szFullFileName + 4, sizeof(chapters[chapterIndex].filename));
  354. ++chapterIndex;
  355. ++ChapterStringIndex;
  356. g_pFullFileSystem->Close( f );
  357. }
  358. else
  359. {
  360. bExists = false;
  361. }
  362. //Hack to account for xbox360 missing chapter9a
  363. if ( ChapterStringIndex == 10 )
  364. {
  365. Q_snprintf( szFullFileName, sizeof( szFullFileName ), "cfg/chapter9a.cfg" );
  366. FileHandle_t fChap = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" );
  367. if ( fChap )
  368. {
  369. Q_strncpy(chapters[chapterIndex].filename, szFullFileName + 4, sizeof(chapters[chapterIndex].filename));
  370. ++chapterIndex;
  371. g_pFullFileSystem->Close( fChap );
  372. }
  373. }
  374. }
  375. }
  376. bool bBonusesUnlocked = false;
  377. if ( GameUI().IsConsoleUI() )
  378. {
  379. if ( !m_bCommentaryMode )
  380. {
  381. // Scan to see if the bonus maps have been unlocked
  382. bBonusesUnlocked = BonusMapsDatabase()->BonusesUnlocked();
  383. }
  384. }
  385. // sort the chapters
  386. qsort(chapters, chapterIndex, sizeof(chapter_t), &ChapterSortFunc);
  387. // work out which chapters are unlocked
  388. ConVarRef var( "sv_unlockedchapters" );
  389. if ( bBonusesUnlocked )
  390. {
  391. // Bonuses are unlocked so we need to unlock all the chapters too
  392. var.SetValue( 15 );
  393. }
  394. const char *unlockedChapter = var.IsValid() ? var.GetString() : "1";
  395. int iUnlockedChapter = atoi(unlockedChapter);
  396. // add chapters to combobox
  397. for (int i = 0; i < chapterIndex; i++)
  398. {
  399. const char *fileName = chapters[i].filename;
  400. char chapterID[32] = { 0 };
  401. sscanf(fileName, "chapter%s", chapterID);
  402. // strip the extension
  403. char *ext = V_stristr(chapterID, ".cfg");
  404. if (ext)
  405. {
  406. *ext = 0;
  407. }
  408. const char *pGameDir = COM_GetModDirectory();
  409. char chapterName[64];
  410. Q_snprintf(chapterName, sizeof(chapterName), "#%s_Chapter%s_Title", pGameDir, chapterID);
  411. Q_snprintf( szFullFileName, sizeof( szFullFileName ), "%s", fileName );
  412. CGameChapterPanel *chapterPanel = SETUP_PANEL( new CGameChapterPanel( this, NULL, chapterName, i, chapterID, szFullFileName, m_bCommentaryMode ) );
  413. chapterPanel->SetVisible( false );
  414. UpdatePanelLockedStatus( iUnlockedChapter, i + 1, chapterPanel );
  415. if ( GameUI().IsConsoleUI() )
  416. {
  417. if ( bBonusesUnlocked )
  418. {
  419. // check to see if it has associated challenges
  420. for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap )
  421. {
  422. BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap );
  423. if ( Q_stricmp( pMap->szChapterName, szFullFileName ) == 0 && !pMap->bLocked )
  424. {
  425. chapterPanel->m_bHasBonus = true;
  426. chapterPanel->SetControlVisible( "HasBonusLabel", true );
  427. }
  428. }
  429. }
  430. }
  431. m_ChapterPanels.AddToTail( chapterPanel );
  432. }
  433. KeyValues *pKeys = NULL;
  434. if ( GameUI().IsConsoleUI() )
  435. {
  436. pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameDialog.res" );
  437. }
  438. LoadControlSettings( "Resource/NewGameDialog.res", NULL, pKeys );
  439. // Reset all properties
  440. for ( int i = 0; i < NUM_SLOTS; ++i )
  441. {
  442. m_PanelIndex[i] = INVALID_INDEX;
  443. }
  444. if ( !m_ChapterPanels.Count() )
  445. {
  446. UpdateMenuComponents( SCROLL_NONE );
  447. return;
  448. }
  449. // Layout panel positions relative to the dialog center.
  450. int panelWidth = m_ChapterPanels[0]->GetWide() + 16;
  451. int dialogWidth = GetWide();
  452. m_PanelXPos[2] = ( dialogWidth - panelWidth ) / 2 + 8;
  453. if (m_ChapterPanels.Count() > 1)
  454. {
  455. m_PanelXPos[1] = m_PanelXPos[2] - panelWidth;
  456. m_PanelXPos[0] = m_PanelXPos[1];
  457. m_PanelXPos[3] = m_PanelXPos[2] + panelWidth;
  458. m_PanelXPos[4] = m_PanelXPos[3];
  459. }
  460. else
  461. {
  462. m_PanelXPos[0] = m_PanelXPos[1] = m_PanelXPos[3] =
  463. m_PanelXPos[4] = m_PanelXPos[2];
  464. }
  465. m_PanelAlpha[0] = 0;
  466. m_PanelAlpha[1] = 255;
  467. m_PanelAlpha[2] = 255;
  468. m_PanelAlpha[3] = 255;
  469. m_PanelAlpha[4] = 0;
  470. int panelHeight;
  471. m_ChapterPanels[0]->GetSize( panelWidth, panelHeight );
  472. m_pCenterBg->SetWide( panelWidth + 16 );
  473. m_pCenterBg->SetPos( m_PanelXPos[2] - 8, m_PanelYPos[2] - (m_pCenterBg->GetTall() - panelHeight) + 8 );
  474. m_pCenterBg->SetBgColor( Color( 190, 115, 0, 255 ) );
  475. // start the first item selected
  476. SetSelectedChapterIndex( 0 );
  477. }
  478. CNewGameDialog::~CNewGameDialog()
  479. {
  480. delete m_pFooter;
  481. m_pFooter = NULL;
  482. }
  483. void CNewGameDialog::Activate( void )
  484. {
  485. m_bMapStarting = false;
  486. if ( GameUI().IsConsoleUI() )
  487. {
  488. // Stop blinking the menu item now that we've seen the unlocked stuff
  489. CBasePanel *pBasePanel = BasePanel();
  490. if ( pBasePanel )
  491. pBasePanel->SetMenuItemBlinkingState( "OpenNewGameDialog", false );
  492. BonusMapsDatabase()->SetBlink( false );
  493. }
  494. // Commentary stuff is set up on activate because in XBox the new game menu is never deleted
  495. SetTitle( ( ( m_bCommentaryMode ) ? ( "#GameUI_LoadCommentary" ) : ( "#GameUI_NewGame") ), true);
  496. if ( m_pCommentaryLabel )
  497. m_pCommentaryLabel->SetVisible( m_bCommentaryMode );
  498. // work out which chapters are unlocked
  499. ConVarRef var( "sv_unlockedchapters" );
  500. const char *unlockedChapter = var.IsValid() ? var.GetString() : "1";
  501. int iUnlockedChapter = atoi(unlockedChapter);
  502. for ( int i = 0; i < m_ChapterPanels.Count(); i++)
  503. {
  504. CGameChapterPanel *pChapterPanel = m_ChapterPanels[ i ];
  505. if ( pChapterPanel )
  506. {
  507. pChapterPanel->SetCommentaryMode( m_bCommentaryMode );
  508. UpdatePanelLockedStatus( iUnlockedChapter, i + 1, pChapterPanel );
  509. }
  510. }
  511. BaseClass::Activate();
  512. }
  513. //-----------------------------------------------------------------------------
  514. // Purpose: Apply special properties of the menu
  515. //-----------------------------------------------------------------------------
  516. void CNewGameDialog::ApplySettings( KeyValues *inResourceData )
  517. {
  518. BaseClass::ApplySettings( inResourceData );
  519. int ypos = inResourceData->GetInt( "chapterypos", 40 );
  520. for ( int i = 0; i < NUM_SLOTS; ++i )
  521. {
  522. m_PanelYPos[i] = ypos;
  523. }
  524. m_pCenterBg->SetTall( inResourceData->GetInt( "centerbgtall", 0 ) );
  525. g_ScrollSpeedSlow = inResourceData->GetFloat( "scrollslow", 0.0f );
  526. g_ScrollSpeedFast = inResourceData->GetFloat( "scrollfast", 0.0f );
  527. SetFastScroll( false );
  528. }
  529. void CNewGameDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
  530. {
  531. BaseClass::ApplySchemeSettings( pScheme );
  532. if ( m_pFooter )
  533. {
  534. KeyValues *pFooterControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameFooter.res" );
  535. m_pFooter->LoadControlSettings( "null", NULL, pFooterControlSettings );
  536. }
  537. UpdateMenuComponents( SCROLL_NONE );
  538. m_pCommentaryLabel = dynamic_cast<vgui::Label*>( FindChildByName( "CommentaryUnlock" ) );
  539. if ( m_pCommentaryLabel )
  540. m_pCommentaryLabel->SetVisible( m_bCommentaryMode );
  541. if ( GameUI().IsConsoleUI() )
  542. {
  543. if ( !m_bCommentaryMode && BonusMapsDatabase()->BonusesUnlocked() && !m_ChapterPanels[ m_PanelIndex[SLOT_CENTER] ]->HasBonus() )
  544. {
  545. // Find the first bonus
  546. ScrollSelectionPanels( SCROLL_LEFT );
  547. m_bScrollToFirstBonusMap = true;
  548. }
  549. }
  550. }
  551. static float GetArrowAlpha( void )
  552. {
  553. // X360TBD: Pulsing arrows
  554. return 255.f;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: sets the correct properties for visible components
  558. //-----------------------------------------------------------------------------
  559. void CNewGameDialog::UpdateMenuComponents( EScrollDirection dir )
  560. {
  561. // This is called prior to any scrolling,
  562. // so we need to look ahead to the post-scroll state
  563. int centerIdx = SLOT_CENTER;
  564. if ( dir == SCROLL_LEFT )
  565. {
  566. ++centerIdx;
  567. }
  568. else if ( dir == SCROLL_RIGHT )
  569. {
  570. --centerIdx;
  571. }
  572. int leftIdx = centerIdx - 1;
  573. int rightIdx = centerIdx + 1;
  574. if ( GameUI().IsConsoleUI() )
  575. {
  576. bool bHasBonus = false;
  577. if ( m_PanelIndex[centerIdx] != INVALID_INDEX )
  578. {
  579. wchar_t buffer[ MAX_PATH ];
  580. m_ChapterPanels[ m_PanelIndex[centerIdx] ]->m_pChapterNameLabel->GetText( buffer, sizeof(buffer) );
  581. m_pChapterTitleLabels[(unsigned)m_ActiveTitleIdx]->SetText( buffer );
  582. // If it has bonuses show the scroll up and down arrows
  583. bHasBonus = m_ChapterPanels[ m_PanelIndex[centerIdx] ]->HasBonus();
  584. }
  585. vgui::Panel *leftArrow = this->FindChildByName( "LeftArrow" );
  586. vgui::Panel *rightArrow = this->FindChildByName( "RightArrow" );
  587. if ( leftArrow )
  588. {
  589. if ( m_PanelIndex[leftIdx] != INVALID_INDEX )
  590. {
  591. leftArrow->SetFgColor( Color( 255, 255, 255, GetArrowAlpha() ) );
  592. }
  593. else
  594. {
  595. leftArrow->SetFgColor( Color( 128, 128, 128, 64 ) );
  596. }
  597. }
  598. if ( rightArrow )
  599. {
  600. if ( m_PanelIndex[rightIdx] != INVALID_INDEX )
  601. {
  602. rightArrow->SetFgColor( Color( 255, 255, 255, GetArrowAlpha() ) );
  603. }
  604. else
  605. {
  606. rightArrow->SetFgColor( Color( 128, 128, 128, 64 ) );
  607. }
  608. }
  609. if ( bHasBonus )
  610. {
  611. // Find the bonus description for this panel
  612. for ( int iBonus = 0; iBonus < BonusMapsDatabase()->BonusCount(); ++iBonus )
  613. {
  614. m_pBonusMapDescription = BonusMapsDatabase()->GetBonusData( iBonus );
  615. if ( Q_stricmp( m_pBonusMapDescription->szChapterName, m_ChapterPanels[ m_PanelIndex[centerIdx] ]->GetConfigFile() ) == 0 )
  616. break;
  617. }
  618. }
  619. else
  620. {
  621. m_pBonusMapDescription = NULL;
  622. }
  623. vgui::Panel *upArrow = this->FindChildByName( "UpArrow" );
  624. vgui::Panel *downArrow = this->FindChildByName( "DownArrow" );
  625. if ( upArrow )
  626. upArrow->SetVisible( bHasBonus );
  627. if ( downArrow )
  628. downArrow->SetVisible( bHasBonus );
  629. m_pBonusSelection->SetVisible( bHasBonus );
  630. m_pBonusSelectionBorder->SetVisible( bHasBonus );
  631. UpdateBonusSelection();
  632. }
  633. // No buttons in the xbox ui
  634. if ( !GameUI().IsConsoleUI() )
  635. {
  636. if ( m_PanelIndex[leftIdx] == INVALID_INDEX || m_PanelIndex[leftIdx] == 0 )
  637. {
  638. m_pPrevButton->SetVisible( false );
  639. m_pPrevButton->SetEnabled( false );
  640. }
  641. else
  642. {
  643. m_pPrevButton->SetVisible( true );
  644. m_pPrevButton->SetEnabled( true );
  645. }
  646. if ( m_ChapterPanels.Count() < 4 ) // if there are less than 4 chapters show the next button but disabled
  647. {
  648. m_pNextButton->SetVisible( true );
  649. m_pNextButton->SetEnabled( false );
  650. }
  651. else if ( m_PanelIndex[rightIdx] == INVALID_INDEX || m_PanelIndex[rightIdx] == m_ChapterPanels.Count()-1 )
  652. {
  653. m_pNextButton->SetVisible( false );
  654. m_pNextButton->SetEnabled( false );
  655. }
  656. else
  657. {
  658. m_pNextButton->SetVisible( true );
  659. m_pNextButton->SetEnabled( true );
  660. }
  661. }
  662. }
  663. void CNewGameDialog::UpdateBonusSelection( void )
  664. {
  665. int iNumChallenges = 0;
  666. if ( m_pBonusMapDescription )
  667. {
  668. if ( m_pBonusMapDescription->m_pChallenges )
  669. iNumChallenges = m_pBonusMapDescription->m_pChallenges->Count();
  670. // Wrap challenge selection to fit number of possible selections
  671. if ( m_iBonusSelection < 0 )
  672. m_iBonusSelection = iNumChallenges + 1;
  673. else if ( m_iBonusSelection >= iNumChallenges + 2 )
  674. m_iBonusSelection = 0;
  675. }
  676. else
  677. {
  678. // No medals to show
  679. SetControlVisible( "ChallengeEarnedMedal", false );
  680. SetControlVisible( "ChallengeBestLabel", false );
  681. SetControlVisible( "ChallengeNextMedal", false );
  682. SetControlVisible( "ChallengeNextLabel", false );
  683. return;
  684. }
  685. if ( m_iBonusSelection == 0 )
  686. {
  687. m_pBonusSelection->SetText( "#GameUI_BonusMapsStandard" );
  688. SetControlVisible( "ChallengeEarnedMedal", false );
  689. SetControlVisible( "ChallengeBestLabel", false );
  690. SetControlVisible( "ChallengeNextMedal", false );
  691. SetControlVisible( "ChallengeNextLabel", false );
  692. }
  693. else if ( m_iBonusSelection == 1 )
  694. {
  695. m_pBonusSelection->SetText( "#GameUI_BonusMapsAdvanced" );
  696. SetControlVisible( "ChallengeEarnedMedal", false );
  697. SetControlVisible( "ChallengeBestLabel", false );
  698. SetControlVisible( "ChallengeNextMedal", false );
  699. SetControlVisible( "ChallengeNextLabel", false );
  700. char szMapAdvancedName[ 256 ] = "";
  701. if ( m_pBonusMapDescription )
  702. {
  703. Q_snprintf( szMapAdvancedName, sizeof( szMapAdvancedName ), "%s_advanced", m_pBonusMapDescription->szMapFileName );
  704. }
  705. BonusMapDescription_t *pAdvancedDescription = NULL;
  706. // Find the bonus description for this panel
  707. for ( int iBonus = 0; iBonus < BonusMapsDatabase()->BonusCount(); ++iBonus )
  708. {
  709. pAdvancedDescription = BonusMapsDatabase()->GetBonusData( iBonus );
  710. if ( Q_stricmp( szMapAdvancedName, pAdvancedDescription->szMapFileName ) == 0 )
  711. break;
  712. }
  713. if ( pAdvancedDescription && pAdvancedDescription->bComplete )
  714. {
  715. CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( FindChildByName( "ChallengeEarnedMedal" ) );
  716. pBitmap->SetVisible( true );
  717. pBitmap->setTexture( "hud/icon_complete" );
  718. }
  719. }
  720. else
  721. {
  722. int iChallenge = m_iBonusSelection - 2;
  723. ChallengeDescription_t *pChallengeDescription = &((*m_pBonusMapDescription->m_pChallenges)[ iChallenge ]);
  724. // Set the display text for the selected challenge
  725. m_pBonusSelection->SetText( pChallengeDescription->szName );
  726. int iBest, iEarnedMedal, iNext, iNextMedal;
  727. GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal );
  728. char szBuff[ 512 ];
  729. // Set earned medal
  730. if ( iEarnedMedal > -1 && iBest != -1 )
  731. {
  732. if ( iChallenge < 10 )
  733. Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_0%i_%s", iChallenge, g_pszMedalNames[ iEarnedMedal ] );
  734. else
  735. Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_%i_%s", iChallenge, g_pszMedalNames[ iEarnedMedal ] );
  736. CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( FindChildByName( "ChallengeEarnedMedal" ) );
  737. pBitmap->SetVisible( true );
  738. pBitmap->setTexture( szBuff );
  739. }
  740. else
  741. {
  742. CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( FindChildByName( "ChallengeEarnedMedal" ) );
  743. pBitmap->SetVisible( false );
  744. }
  745. // Set next medal
  746. if ( iNextMedal > 0 )
  747. {
  748. if ( iChallenge < 10 )
  749. Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_0%i_%s", iChallenge, g_pszMedalNames[ iNextMedal ] );
  750. else
  751. Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_%i_%s", iChallenge, g_pszMedalNames[ iNextMedal ] );
  752. CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( FindChildByName( "ChallengeNextMedal" ) );
  753. pBitmap->SetVisible( true );
  754. pBitmap->setTexture( szBuff );
  755. }
  756. else
  757. {
  758. SetControlVisible( "ChallengeNextMedal", false );
  759. }
  760. wchar_t szWideBuff[ 64 ];
  761. wchar_t szWideBuff2[ 64 ];
  762. // Best label
  763. if ( iBest != -1 )
  764. {
  765. Q_snprintf( szBuff, sizeof( szBuff ), "%i", iBest );
  766. g_pVGuiLocalize->ConvertANSIToUnicode( szBuff, szWideBuff2, sizeof( szWideBuff2 ) );
  767. g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( "#GameUI_BonusMapsBest" ), 1, szWideBuff2 );
  768. g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );
  769. SetControlString( "ChallengeBestLabel", szBuff );
  770. SetControlVisible( "ChallengeBestLabel", true );
  771. }
  772. else
  773. {
  774. SetControlVisible( "ChallengeBestLabel", false );
  775. }
  776. // Next label
  777. if ( iNext != -1 )
  778. {
  779. Q_snprintf( szBuff, sizeof( szBuff ), "%i", iNext );
  780. g_pVGuiLocalize->ConvertANSIToUnicode( szBuff, szWideBuff2, sizeof( szWideBuff2 ) );
  781. g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( "#GameUI_BonusMapsGoal" ), 1, szWideBuff2 );
  782. g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );
  783. SetControlString( "ChallengeNextLabel", szBuff );
  784. SetControlVisible( "ChallengeNextLabel", true );
  785. }
  786. else
  787. {
  788. SetControlVisible( "ChallengeNextLabel", false );
  789. }
  790. }
  791. }
  792. //-----------------------------------------------------------------------------
  793. // Purpose: sets a chapter as selected
  794. //-----------------------------------------------------------------------------
  795. void CNewGameDialog::SetSelectedChapterIndex( int index )
  796. {
  797. m_iSelectedChapter = index;
  798. for (int i = 0; i < m_ChapterPanels.Count(); i++)
  799. {
  800. if ( i == index )
  801. {
  802. m_ChapterPanels[i]->SetSelected( true );
  803. }
  804. else
  805. {
  806. m_ChapterPanels[i]->SetSelected( false );
  807. }
  808. }
  809. if ( m_pPlayButton )
  810. {
  811. m_pPlayButton->SetEnabled( true );
  812. }
  813. // Setup panels to the left of the selected panel
  814. int selectedSlot = GameUI().IsConsoleUI() ? SLOT_CENTER : index % 3 + 1;
  815. int currIdx = index;
  816. for ( int i = selectedSlot; i >= 0 && currIdx >= 0; --i )
  817. {
  818. m_PanelIndex[i] = currIdx;
  819. --currIdx;
  820. InitPanelIndexForDisplay( i );
  821. }
  822. // Setup panels to the right of the selected panel
  823. currIdx = index + 1;
  824. for ( int i = selectedSlot + 1; i < NUM_SLOTS && currIdx < m_ChapterPanels.Count(); ++i )
  825. {
  826. m_PanelIndex[i] = currIdx;
  827. ++currIdx;
  828. InitPanelIndexForDisplay( i );
  829. }
  830. UpdateMenuComponents( SCROLL_NONE );
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Purpose: sets a chapter as selected
  834. //-----------------------------------------------------------------------------
  835. void CNewGameDialog::SetSelectedChapter( const char *chapter )
  836. {
  837. Assert( chapter );
  838. for (int i = 0; i < m_ChapterPanels.Count(); i++)
  839. {
  840. if ( chapter && !Q_stricmp(m_ChapterPanels[i]->GetChapter(), chapter) )
  841. {
  842. m_iSelectedChapter = i;
  843. m_ChapterPanels[m_iSelectedChapter]->SetSelected( true );
  844. }
  845. else
  846. {
  847. m_ChapterPanels[i]->SetSelected( false );
  848. }
  849. }
  850. if ( m_pPlayButton )
  851. {
  852. m_pPlayButton->SetEnabled( true );
  853. }
  854. }
  855. //-----------------------------------------------------------------------------
  856. // iUnlockedChapter - the value of sv_unlockedchapters, 1-based. A value of 0
  857. // is treated as a 1, since at least one chapter must be unlocked.
  858. //
  859. // i - the 1-based index of the chapter we're considering.
  860. //-----------------------------------------------------------------------------
  861. void CNewGameDialog::UpdatePanelLockedStatus( int iUnlockedChapter, int i, CGameChapterPanel *pChapterPanel )
  862. {
  863. if ( iUnlockedChapter <= 0 )
  864. {
  865. iUnlockedChapter = 1;
  866. }
  867. // Commentary mode requires chapters to be finished before they can be chosen
  868. bool bLocked = false;
  869. if ( m_bCommentaryMode )
  870. {
  871. bLocked = ( iUnlockedChapter <= i );
  872. }
  873. else
  874. {
  875. if ( iUnlockedChapter < i )
  876. {
  877. // Never lock the first chapter
  878. bLocked = ( i != 0 );
  879. }
  880. }
  881. pChapterPanel->SetEnabled( !bLocked );
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose: Called before a panel scroll starts.
  885. //-----------------------------------------------------------------------------
  886. void CNewGameDialog::PreScroll( EScrollDirection dir )
  887. {
  888. int hideIdx = INVALID_INDEX;
  889. if ( dir == SCROLL_LEFT )
  890. {
  891. hideIdx = m_PanelIndex[SLOT_LEFT];
  892. }
  893. else if ( dir == SCROLL_RIGHT )
  894. {
  895. hideIdx = m_PanelIndex[SLOT_RIGHT];
  896. }
  897. if ( hideIdx != INVALID_INDEX )
  898. {
  899. // Push back the panel that's about to be hidden
  900. // so the next panel scrolls over the top of it.
  901. m_ChapterPanels[hideIdx]->SetZPos( 0 );
  902. }
  903. // Flip the active title label prior to the crossfade
  904. m_ActiveTitleIdx ^= 0x01;
  905. }
  906. //-----------------------------------------------------------------------------
  907. // Purpose: Called after a panel scroll finishes.
  908. //-----------------------------------------------------------------------------
  909. void CNewGameDialog::PostScroll( EScrollDirection dir )
  910. {
  911. int index = INVALID_INDEX;
  912. if ( dir == SCROLL_LEFT )
  913. {
  914. index = m_PanelIndex[SLOT_RIGHT];
  915. }
  916. else if ( dir == SCROLL_RIGHT )
  917. {
  918. index = m_PanelIndex[SLOT_LEFT];
  919. }
  920. // Fade in the revealed panel
  921. if ( index != INVALID_INDEX )
  922. {
  923. CGameChapterPanel *panel = m_ChapterPanels[index];
  924. panel->SetZPos( 50 );
  925. GetAnimationController()->RunAnimationCommand( panel, "alpha", 255, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  926. }
  927. if ( GameUI().IsConsoleUI() )
  928. {
  929. if ( BonusMapsDatabase()->BonusesUnlocked() && m_bScrollToFirstBonusMap )
  930. {
  931. if ( !m_ChapterPanels[ m_PanelIndex[SLOT_CENTER] ]->HasBonus() )
  932. {
  933. // Find the first bonus
  934. ScrollSelectionPanels( SCROLL_LEFT );
  935. }
  936. else
  937. {
  938. // Found a bonus, stop scrolling
  939. m_bScrollToFirstBonusMap = false;
  940. }
  941. }
  942. }
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Purpose: Initiates a panel scroll and starts the animation.
  946. //-----------------------------------------------------------------------------
  947. void CNewGameDialog::ScrollSelectionPanels( EScrollDirection dir )
  948. {
  949. // Only initiate a scroll if panels aren't currently scrolling
  950. if ( !m_bScrolling )
  951. {
  952. // Handle any pre-scroll setup
  953. PreScroll( dir );
  954. if ( dir == SCROLL_LEFT)
  955. {
  956. m_ScrollCt += SCROLL_LEFT;
  957. }
  958. else if ( dir == SCROLL_RIGHT && m_PanelIndex[SLOT_CENTER] != 0 )
  959. {
  960. m_ScrollCt += SCROLL_RIGHT;
  961. }
  962. m_bScrolling = true;
  963. AnimateSelectionPanels();
  964. // Update the arrow colors, help text, and buttons. Doing it here looks better than having
  965. // the components change after the entire scroll animation has finished.
  966. UpdateMenuComponents( m_ScrollDirection );
  967. }
  968. }
  969. void CNewGameDialog::ScrollBonusSelection( EScrollDirection dir )
  970. {
  971. // Don't scroll if there's no bonuses for this panel
  972. if ( !m_pBonusMapDescription )
  973. return;
  974. m_iBonusSelection += dir;
  975. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  976. UpdateBonusSelection();
  977. }
  978. //-----------------------------------------------------------------------------
  979. // Purpose: Initiates the scripted scroll and fade effects of all five slotted panels
  980. //-----------------------------------------------------------------------------
  981. void CNewGameDialog::AnimateSelectionPanels( void )
  982. {
  983. int idxOffset = 0;
  984. int startIdx = SLOT_LEFT;
  985. int endIdx = SLOT_RIGHT;
  986. // Don't scroll outside the bounds of the panel list
  987. if ( m_ScrollCt >= SCROLL_LEFT && (m_PanelIndex[SLOT_CENTER] < m_ChapterPanels.Count() - 1 || !GameUI().IsConsoleUI()) )
  988. {
  989. idxOffset = -1;
  990. endIdx = SLOT_OFFRIGHT;
  991. m_ScrollDirection = SCROLL_LEFT;
  992. }
  993. else if ( m_ScrollCt <= SCROLL_RIGHT && (m_PanelIndex[SLOT_CENTER] > 0 || !GameUI().IsConsoleUI()) )
  994. {
  995. idxOffset = 1;
  996. startIdx = SLOT_OFFLEFT;
  997. m_ScrollDirection = SCROLL_RIGHT;
  998. }
  999. if ( 0 == idxOffset )
  1000. {
  1001. // Kill the scroll, it's outside the bounds
  1002. m_ScrollCt = 0;
  1003. m_bScrolling = false;
  1004. m_ScrollDirection = SCROLL_NONE;
  1005. vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
  1006. return;
  1007. }
  1008. // Should never happen
  1009. if ( startIdx > endIdx )
  1010. return;
  1011. for ( int i = startIdx; i <= endIdx; ++i )
  1012. {
  1013. if ( m_PanelIndex[i] != INVALID_INDEX )
  1014. {
  1015. int nextIdx = i + idxOffset;
  1016. CGameChapterPanel *panel = m_ChapterPanels[ m_PanelIndex[i] ];
  1017. GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1018. GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1019. GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1020. }
  1021. }
  1022. if ( GameUI().IsConsoleUI() )
  1023. {
  1024. vgui::surface()->PlaySound( "UI/buttonclick.wav" );
  1025. // Animate the center background panel
  1026. GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1027. // Crossfade the chapter title labels
  1028. int inactiveTitleIdx = m_ActiveTitleIdx ^ 0x01;
  1029. GetAnimationController()->RunAnimationCommand( m_pChapterTitleLabels[(unsigned)m_ActiveTitleIdx], "alpha", 255, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1030. GetAnimationController()->RunAnimationCommand( m_pChapterTitleLabels[inactiveTitleIdx], "alpha", 0, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1031. // Scrolling up through chapters, offset is negative
  1032. m_iSelectedChapter -= idxOffset;
  1033. }
  1034. PostMessage( this, new KeyValues( "FinishScroll" ), m_ScrollSpeed );
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // Purpose: After a scroll, each panel slot holds the index of a panel that has
  1038. // scrolled to an adjacent slot. This function updates each slot so
  1039. // it holds the index of the panel that is actually in that slot's position.
  1040. //-----------------------------------------------------------------------------
  1041. void CNewGameDialog::ShiftPanelIndices( int offset )
  1042. {
  1043. // Shift all the elements over one slot, then calculate what the last slot's index should be.
  1044. int lastSlot = NUM_SLOTS - 1;
  1045. if ( offset > 0 )
  1046. {
  1047. // Hide the panel that's dropping out of the slots
  1048. if ( IsValidPanel( m_PanelIndex[0] ) )
  1049. {
  1050. m_ChapterPanels[ m_PanelIndex[0] ]->SetVisible( false );
  1051. }
  1052. // Scrolled panels to the right, so shift the indices one slot to the left
  1053. Q_memmove( &m_PanelIndex[0], &m_PanelIndex[1], lastSlot * sizeof( m_PanelIndex[0] ) );
  1054. if ( m_PanelIndex[lastSlot] != INVALID_INDEX )
  1055. {
  1056. int num = m_PanelIndex[ lastSlot ] + 1;
  1057. if ( IsValidPanel( num ) )
  1058. {
  1059. m_PanelIndex[lastSlot] = num;
  1060. InitPanelIndexForDisplay( lastSlot );
  1061. }
  1062. else
  1063. {
  1064. m_PanelIndex[lastSlot] = INVALID_INDEX;
  1065. }
  1066. }
  1067. }
  1068. else
  1069. {
  1070. // Hide the panel that's dropping out of the slots
  1071. if ( IsValidPanel( m_PanelIndex[lastSlot] ) )
  1072. {
  1073. m_ChapterPanels[ m_PanelIndex[lastSlot] ]->SetVisible( false );
  1074. }
  1075. // Scrolled panels to the left, so shift the indices one slot to the right
  1076. Q_memmove( &m_PanelIndex[1], &m_PanelIndex[0], lastSlot * sizeof( m_PanelIndex[0] ) );
  1077. if ( m_PanelIndex[0] != INVALID_INDEX )
  1078. {
  1079. int num = m_PanelIndex[0] - 1;
  1080. if ( IsValidPanel( num ) )
  1081. {
  1082. m_PanelIndex[0] = num;
  1083. InitPanelIndexForDisplay( 0 );
  1084. }
  1085. else
  1086. {
  1087. m_PanelIndex[0] = INVALID_INDEX;
  1088. }
  1089. }
  1090. }
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // Purpose: Validates an index into the selection panels vector
  1094. //-----------------------------------------------------------------------------
  1095. bool CNewGameDialog::IsValidPanel( const int idx )
  1096. {
  1097. if ( idx < 0 || idx >= m_ChapterPanels.Count() )
  1098. return false;
  1099. return true;
  1100. }
  1101. //-----------------------------------------------------------------------------
  1102. // Purpose: Sets up a panel's properties before it is displayed
  1103. //-----------------------------------------------------------------------------
  1104. void CNewGameDialog::InitPanelIndexForDisplay( const int idx )
  1105. {
  1106. CGameChapterPanel *panel = m_ChapterPanels[ m_PanelIndex[idx] ];
  1107. if ( panel )
  1108. {
  1109. panel->SetPos( m_PanelXPos[idx], m_PanelYPos[idx] );
  1110. panel->SetAlpha( m_PanelAlpha[idx] );
  1111. panel->SetVisible( true );
  1112. if ( m_PanelAlpha[idx] )
  1113. {
  1114. panel->SetZPos( 50 );
  1115. }
  1116. }
  1117. }
  1118. //-----------------------------------------------------------------------------
  1119. // Purpose: Sets which scroll speed should be used
  1120. //-----------------------------------------------------------------------------
  1121. void CNewGameDialog::SetFastScroll( bool fast )
  1122. {
  1123. m_ScrollSpeed = fast ? g_ScrollSpeedFast : g_ScrollSpeedSlow;
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // Purpose: Checks if a button is being held down, and speeds up the scroll
  1127. //-----------------------------------------------------------------------------
  1128. void CNewGameDialog::ContinueScrolling( void )
  1129. {
  1130. if ( !GameUI().IsConsoleUI() )
  1131. {
  1132. if ( m_PanelIndex[SLOT_CENTER-1] % 3 )
  1133. {
  1134. // m_ButtonPressed = m_ScrollDirection;
  1135. ScrollSelectionPanels( m_ScrollDirection );
  1136. }
  1137. return;
  1138. }
  1139. if ( m_ButtonPressed == m_ScrollDirection )
  1140. {
  1141. SetFastScroll( true );
  1142. ScrollSelectionPanels( m_ScrollDirection );
  1143. }
  1144. else if ( m_ButtonPressed != SCROLL_NONE )
  1145. {
  1146. // The other direction has been pressed - start a slow scroll
  1147. SetFastScroll( false );
  1148. ScrollSelectionPanels( (EScrollDirection)m_ButtonPressed );
  1149. }
  1150. else
  1151. {
  1152. SetFastScroll( false );
  1153. }
  1154. }
  1155. //-----------------------------------------------------------------------------
  1156. // Purpose: Called when a scroll distance of one slot has been completed
  1157. //-----------------------------------------------------------------------------
  1158. void CNewGameDialog::FinishScroll( void )
  1159. {
  1160. // Fade the center bg panel back in
  1161. GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1162. ShiftPanelIndices( m_ScrollDirection );
  1163. m_bScrolling = false;
  1164. m_ScrollCt = 0;
  1165. // End of scroll step
  1166. PostScroll( m_ScrollDirection );
  1167. // Continue scrolling if necessary
  1168. ContinueScrolling();
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. // Purpose: starts the game at the specified skill level
  1172. //-----------------------------------------------------------------------------
  1173. void CNewGameDialog::StartGame( void )
  1174. {
  1175. if ( m_ChapterPanels.IsValidIndex( m_iSelectedChapter ) )
  1176. {
  1177. char mapcommand[512];
  1178. mapcommand[0] = 0;
  1179. Q_snprintf( mapcommand, sizeof( mapcommand ), "disconnect\ndeathmatch 0\nprogress_enable\nexec %s\n", m_ChapterPanels[m_iSelectedChapter]->GetConfigFile() );
  1180. // Set commentary
  1181. ConVarRef commentary( "commentary" );
  1182. commentary.SetValue( m_bCommentaryMode );
  1183. ConVarRef sv_cheats( "sv_cheats" );
  1184. sv_cheats.SetValue( m_bCommentaryMode );
  1185. if ( IsPC() )
  1186. {
  1187. // If commentary is on, we go to the explanation dialog (but not for teaser trailers)
  1188. if ( m_bCommentaryMode && !m_ChapterPanels[m_iSelectedChapter]->IsTeaserChapter() )
  1189. {
  1190. // Check our current state and disconnect us from any multiplayer server we're connected to.
  1191. // This fixes an exploit where players would click "start" on the commentary dialog to enable
  1192. // sv_cheats on the client (via the code above) and then hit <esc> to get out of the explanation dialog.
  1193. if ( GameUI().IsInMultiplayer() )
  1194. {
  1195. engine->ExecuteClientCmd( "disconnect" );
  1196. }
  1197. DHANDLE<CCommentaryExplanationDialog> hCommentaryExplanationDialog;
  1198. if ( !hCommentaryExplanationDialog.Get() )
  1199. {
  1200. hCommentaryExplanationDialog = new CCommentaryExplanationDialog( BasePanel(), mapcommand );
  1201. }
  1202. hCommentaryExplanationDialog->Activate();
  1203. }
  1204. else
  1205. {
  1206. // start map
  1207. BasePanel()->FadeToBlackAndRunEngineCommand( mapcommand );
  1208. }
  1209. }
  1210. else if ( IsX360() )
  1211. {
  1212. if ( m_ChapterPanels[m_iSelectedChapter]->HasBonus() && m_iBonusSelection > 0 )
  1213. {
  1214. if ( m_iBonusSelection == 1 )
  1215. {
  1216. // Run the advanced chamber instead of the config file
  1217. char *pLastSpace = Q_strrchr( mapcommand, '\n' );
  1218. pLastSpace[ 0 ] = '\0';
  1219. pLastSpace = Q_strrchr( mapcommand, '\n' );
  1220. Q_snprintf( pLastSpace, sizeof( mapcommand ) - Q_strlen( mapcommand ), "\nmap %s_advanced\n", m_pBonusMapDescription->szMapFileName );
  1221. }
  1222. else
  1223. {
  1224. char sz[ 256 ];
  1225. int iChallenge = m_iBonusSelection - 1;
  1226. // Set up the challenge mode
  1227. Q_snprintf( sz, sizeof( sz ), "sv_bonus_challenge %i\n", iChallenge );
  1228. engine->ClientCmd_Unrestricted( sz );
  1229. ChallengeDescription_t *pChallengeDescription = &((*m_pBonusMapDescription->m_pChallenges)[ iChallenge - 1 ]);
  1230. // Set up medal goals
  1231. BonusMapsDatabase()->SetCurrentChallengeObjectives( pChallengeDescription->iBronze, pChallengeDescription->iSilver, pChallengeDescription->iGold );
  1232. BonusMapsDatabase()->SetCurrentChallengeNames( m_pBonusMapDescription->szFileName, m_pBonusMapDescription->szMapName, pChallengeDescription->szName );
  1233. }
  1234. }
  1235. m_bMapStarting = true;
  1236. BasePanel()->FadeToBlackAndRunEngineCommand( mapcommand );
  1237. }
  1238. OnClose();
  1239. }
  1240. }
  1241. void CNewGameDialog::OnClose( void )
  1242. {
  1243. m_KeyRepeat.Reset();
  1244. if ( GameUI().IsConsoleUI() && !m_bMapStarting )
  1245. {
  1246. BasePanel()->RunCloseAnimation( "CloseNewGameDialog_OpenMainMenu" );
  1247. BonusMapsDatabase()->WriteSaveData(); // Closing this dialog is a good time to save
  1248. }
  1249. BaseClass::OnClose();
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. // Purpose: handles button commands
  1253. //-----------------------------------------------------------------------------
  1254. void CNewGameDialog::OnCommand( const char *command )
  1255. {
  1256. bool bReset = true;
  1257. if ( !stricmp( command, "Play" ) )
  1258. {
  1259. if ( m_bMapStarting )
  1260. return;
  1261. if ( GameUI().IsConsoleUI() )
  1262. {
  1263. if ( m_ChapterPanels[m_iSelectedChapter]->IsEnabled() )
  1264. {
  1265. if ( !GameUI().HasSavedThisMenuSession() && GameUI().IsInLevel() && engine->GetMaxClients() == 1 )
  1266. {
  1267. vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
  1268. BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_NEW_GAME, this );
  1269. }
  1270. else
  1271. {
  1272. OnCommand( "StartNewGame" );
  1273. }
  1274. }
  1275. else
  1276. {
  1277. // This chapter isn't unlocked!
  1278. m_bMapStarting = false;
  1279. vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
  1280. if ( m_bCommentaryMode )
  1281. {
  1282. BasePanel()->ShowMessageDialog( MD_COMMENTARY_CHAPTER_UNLOCK_EXPLANATION, this );
  1283. }
  1284. }
  1285. }
  1286. else
  1287. {
  1288. StartGame();
  1289. }
  1290. }
  1291. #ifdef _X360
  1292. else if ( !stricmp( command, "StartNewGame" ) )
  1293. {
  1294. ConVarRef commentary( "commentary" );
  1295. if ( m_bCommentaryMode && !commentary.GetBool() )
  1296. {
  1297. // Using the commentary menu, but not already in commentary mode, explain the rules
  1298. PostMessage( (vgui::Panel*)this, new KeyValues( "command", "command", "StartNewGameWithCommentaryExplanation" ), 0.2f );
  1299. }
  1300. else
  1301. {
  1302. if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ||
  1303. !ModInfo().IsSinglePlayerOnly() )
  1304. {
  1305. // Multiplayer or no storage device so don't bore them with autosave details
  1306. m_bMapStarting = true;
  1307. OnCommand( "StartNewGameNoCommentaryExplanation" );
  1308. }
  1309. else
  1310. {
  1311. // Don't allow other inputs
  1312. m_bMapStarting = true;
  1313. // Remind them how autosaves work
  1314. PostMessage( (vgui::Panel*)this, new KeyValues( "command", "command", "StartNewGameWithAutosaveExplanation" ), 0.2f );
  1315. }
  1316. }
  1317. }
  1318. else if ( !stricmp( command, "StartNewGameWithAutosaveExplanation" ) )
  1319. {
  1320. BasePanel()->ShowMessageDialog( MD_AUTOSAVE_EXPLANATION, this );
  1321. }
  1322. else if ( !stricmp( command, "StartNewGameWithCommentaryExplanation" ) )
  1323. {
  1324. if ( ModInfo().IsSinglePlayerOnly() )
  1325. {
  1326. // Don't allow other inputs
  1327. m_bMapStarting = true;
  1328. BasePanel()->ShowMessageDialog( MD_COMMENTARY_EXPLANATION, this );
  1329. }
  1330. else
  1331. {
  1332. // Don't allow other inputs
  1333. m_bMapStarting = true;
  1334. BasePanel()->ShowMessageDialog( MD_COMMENTARY_EXPLANATION_MULTI, this );
  1335. }
  1336. }
  1337. else if ( !stricmp( command, "StartNewGameNoCommentaryExplanation" ) )
  1338. {
  1339. vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
  1340. BasePanel()->RunAnimationWithCallback( this, "CloseNewGameDialog", new KeyValues( "StartGame" ) );
  1341. }
  1342. #endif
  1343. else if ( !stricmp( command, "Next" ) )
  1344. {
  1345. if ( m_bMapStarting )
  1346. return;
  1347. ScrollSelectionPanels( SCROLL_LEFT );
  1348. bReset = false;
  1349. }
  1350. else if ( !stricmp( command, "Prev" ) )
  1351. {
  1352. if ( m_bMapStarting )
  1353. return;
  1354. ScrollSelectionPanels( SCROLL_RIGHT );
  1355. bReset = false;
  1356. }
  1357. else if ( !stricmp( command, "Mode_Next" ) )
  1358. {
  1359. if ( m_bMapStarting )
  1360. return;
  1361. ScrollBonusSelection( SCROLL_LEFT );
  1362. bReset = false;
  1363. }
  1364. else if ( !stricmp( command, "Mode_Prev" ) )
  1365. {
  1366. if ( m_bMapStarting )
  1367. return;
  1368. ScrollBonusSelection( SCROLL_RIGHT );
  1369. bReset = false;
  1370. }
  1371. else if ( !Q_stricmp( command, "ReleaseModalWindow" ) )
  1372. {
  1373. vgui::surface()->RestrictPaintToSinglePanel(NULL);
  1374. }
  1375. else
  1376. {
  1377. BaseClass::OnCommand( command );
  1378. }
  1379. if ( bReset )
  1380. {
  1381. m_KeyRepeat.Reset();
  1382. }
  1383. }
  1384. void CNewGameDialog::PaintBackground()
  1385. {
  1386. if ( !GameUI().IsConsoleUI() )
  1387. {
  1388. BaseClass::PaintBackground();
  1389. return;
  1390. }
  1391. int wide, tall;
  1392. GetSize( wide, tall );
  1393. Color col = GetBgColor();
  1394. DrawBox( 0, 0, wide, tall, col, 1.0f );
  1395. int y = 0;
  1396. if ( m_pChapterTitleLabels[0] )
  1397. {
  1398. // offset by title
  1399. int titleX, titleY, titleWide, titleTall;
  1400. m_pChapterTitleLabels[0]->GetBounds( titleX, titleY, titleWide, titleTall );
  1401. y += titleY + titleTall;
  1402. }
  1403. else
  1404. {
  1405. y = 8;
  1406. }
  1407. // draw an inset
  1408. Color darkColor;
  1409. darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() );
  1410. vgui::surface()->DrawSetColor( darkColor );
  1411. vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 );
  1412. }
  1413. void CNewGameDialog::OnKeyCodePressed( KeyCode code )
  1414. {
  1415. switch ( code )
  1416. {
  1417. case KEY_XBUTTON_LEFT:
  1418. case KEY_XSTICK1_LEFT:
  1419. case KEY_XSTICK2_LEFT:
  1420. case KEY_LEFT:
  1421. case STEAMCONTROLLER_DPAD_LEFT:
  1422. if ( !m_bScrolling )
  1423. {
  1424. for ( int i = 0; i < m_ChapterPanels.Count(); ++i )
  1425. {
  1426. if ( m_ChapterPanels[ i ]->IsSelected() )
  1427. {
  1428. int nNewChapter = i - 1;
  1429. if ( nNewChapter >= 0 )
  1430. {
  1431. if ( nNewChapter < m_PanelIndex[ SLOT_LEFT ] && m_PanelIndex[ SLOT_LEFT ] != -1 )
  1432. {
  1433. ScrollSelectionPanels( SCROLL_RIGHT );
  1434. }
  1435. else if ( m_ChapterPanels[ nNewChapter ]->IsEnabled() )
  1436. {
  1437. SetSelectedChapterIndex( nNewChapter );
  1438. }
  1439. }
  1440. break;
  1441. }
  1442. }
  1443. }
  1444. return;
  1445. case KEY_XBUTTON_RIGHT:
  1446. case KEY_XSTICK1_RIGHT:
  1447. case KEY_XSTICK2_RIGHT:
  1448. case KEY_RIGHT:
  1449. case STEAMCONTROLLER_DPAD_RIGHT:
  1450. if ( !m_bScrolling )
  1451. {
  1452. for ( int i = 0; i < m_ChapterPanels.Count(); ++i )
  1453. {
  1454. if ( m_ChapterPanels[ i ]->IsSelected() )
  1455. {
  1456. int nNewChapter = i + 1;
  1457. if ( nNewChapter < m_ChapterPanels.Count() )
  1458. {
  1459. if ( nNewChapter > m_PanelIndex[ SLOT_RIGHT ] && m_PanelIndex[ SLOT_RIGHT ] != -1 )
  1460. {
  1461. ScrollSelectionPanels( SCROLL_LEFT );
  1462. }
  1463. else if ( m_ChapterPanels[ nNewChapter ]->IsEnabled() )
  1464. {
  1465. SetSelectedChapterIndex( nNewChapter );
  1466. }
  1467. }
  1468. break;
  1469. }
  1470. }
  1471. }
  1472. return;
  1473. case KEY_XBUTTON_B:
  1474. case STEAMCONTROLLER_B:
  1475. OnCommand( "Close" );
  1476. return;
  1477. case KEY_XBUTTON_A:
  1478. case STEAMCONTROLLER_A:
  1479. OnCommand( "Play" );
  1480. return;
  1481. }
  1482. m_KeyRepeat.KeyDown( code );
  1483. BaseClass::OnKeyCodePressed( code );
  1484. }
  1485. void CNewGameDialog::OnKeyCodeReleased( vgui::KeyCode code )
  1486. {
  1487. m_KeyRepeat.KeyUp( code );
  1488. BaseClass::OnKeyCodeReleased( code );
  1489. }
  1490. void CNewGameDialog::OnThink()
  1491. {
  1492. vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
  1493. if ( code )
  1494. {
  1495. OnKeyCodeTyped( code );
  1496. }
  1497. BaseClass::OnThink();
  1498. }