//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "BasePanel.h" #include "NewGameDialog.h" #include "EngineInterface.h" #include "vgui_controls/Button.h" #include "vgui_controls/CheckButton.h" #include "KeyValues.h" #include "vgui/ISurface.h" #include "vgui/IInput.h" #include "vgui/ILocalize.h" #include #include "vgui_controls/RadioButton.h" #include "vgui_controls/ComboBox.h" #include "vgui_controls/ImagePanel.h" #include "vgui_controls/Frame.h" #include "vgui_controls/ControllerMap.h" #include "filesystem.h" #include "ModInfo.h" #include "tier1/convar.h" #include "GameUI_Interface.h" #include "tier0/icommandline.h" #include "vgui_controls/AnimationController.h" #include "CommentaryExplanationDialog.h" #include "vgui_controls/BitmapImagePanel.h" #include "BonusMapsDatabase.h" #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; static float g_ScrollSpeedSlow; static float g_ScrollSpeedFast; // sort function used in displaying chapter list struct chapter_t { char filename[32]; }; static int __cdecl ChapterSortFunc(const void *elem1, const void *elem2) { chapter_t *c1 = (chapter_t *)elem1; chapter_t *c2 = (chapter_t *)elem2; // compare chapter number first static int chapterlen = strlen("chapter"); if (atoi(c1->filename + chapterlen) > atoi(c2->filename + chapterlen)) return 1; else if (atoi(c1->filename + chapterlen) < atoi(c2->filename + chapterlen)) return -1; // compare length second (longer string show up later in the list, eg. chapter9 before chapter9a) if (strlen(c1->filename) > strlen(c2->filename)) return 1; else if (strlen(c1->filename) < strlen(c2->filename)) return -1; // compare strings third return strcmp(c1->filename, c2->filename); } //----------------------------------------------------------------------------- // Purpose: invisible panel used for selecting a chapter panel //----------------------------------------------------------------------------- class CSelectionOverlayPanel : public vgui::Panel { DECLARE_CLASS_SIMPLE( CSelectionOverlayPanel, Panel ); int m_iChapterIndex; CNewGameDialog *m_pSelectionTarget; public: CSelectionOverlayPanel( Panel *parent, CNewGameDialog *selectionTarget, int chapterIndex ) : BaseClass( parent, NULL ) { m_iChapterIndex = chapterIndex; m_pSelectionTarget = selectionTarget; SetPaintEnabled(false); SetPaintBackgroundEnabled(false); } virtual void OnMousePressed( vgui::MouseCode code ) { if (GetParent()->IsEnabled()) { m_pSelectionTarget->SetSelectedChapterIndex( m_iChapterIndex ); } } virtual void OnMouseDoublePressed( vgui::MouseCode code ) { // call the panel OnMousePressed( code ); if (GetParent()->IsEnabled()) { PostMessage( m_pSelectionTarget, new KeyValues("Command", "command", "play") ); } } }; //----------------------------------------------------------------------------- // Purpose: selectable item with screenshot for an individual chapter in the dialog //----------------------------------------------------------------------------- class CGameChapterPanel : public vgui::EditablePanel { DECLARE_CLASS_SIMPLE( CGameChapterPanel, vgui::EditablePanel ); ImagePanel *m_pLevelPicBorder; ImagePanel *m_pLevelPic; ImagePanel *m_pCommentaryIcon; Label *m_pChapterLabel; Label *m_pChapterNameLabel; Color m_TextColor; Color m_DisabledColor; Color m_SelectedColor; Color m_FillColor; char m_szConfigFile[_MAX_PATH]; char m_szChapter[32]; bool m_bTeaserChapter; bool m_bHasBonus; bool m_bCommentaryMode; bool m_bIsSelected; public: CGameChapterPanel( CNewGameDialog *parent, const char *name, const char *chapterName, int chapterIndex, const char *chapterNumber, const char *chapterConfigFile, bool bCommentary ) : BaseClass( parent, name ) { Q_strncpy( m_szConfigFile, chapterConfigFile, sizeof(m_szConfigFile) ); Q_strncpy( m_szChapter, chapterNumber, sizeof(m_szChapter) ); m_pLevelPicBorder = SETUP_PANEL( new ImagePanel( this, "LevelPicBorder" ) ); m_pLevelPic = SETUP_PANEL( new ImagePanel( this, "LevelPic" ) ); m_pCommentaryIcon = NULL; m_bCommentaryMode = bCommentary; m_bIsSelected = false; wchar_t text[32]; wchar_t num[32]; wchar_t *chapter = g_pVGuiLocalize->Find("#GameUI_Chapter"); g_pVGuiLocalize->ConvertANSIToUnicode( chapterNumber, num, sizeof(num) ); _snwprintf( text, ARRAYSIZE(text), L"%ls %ls", chapter ? chapter : L"CHAPTER", num ); if ( ModInfo().IsSinglePlayerOnly() ) { m_pChapterLabel = new Label( this, "ChapterLabel", text ); m_pChapterNameLabel = new Label( this, "ChapterNameLabel", chapterName ); } else { m_pChapterLabel = new Label( this, "ChapterLabel", chapterName ); m_pChapterNameLabel = new Label( this, "ChapterNameLabel", "#GameUI_LoadCommentary" ); } SetPaintBackgroundEnabled( false ); // the image has the same name as the config file char szMaterial[ MAX_PATH ]; Q_snprintf( szMaterial, sizeof(szMaterial), "chapters/%s", chapterConfigFile ); char *ext = strstr( szMaterial, "." ); if ( ext ) { *ext = 0; } m_pLevelPic->SetImage( szMaterial ); KeyValues *pKeys = NULL; if ( GameUI().IsConsoleUI() ) { pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameChapterPanel.res" ); } LoadControlSettings( "Resource/NewGameChapterPanel.res", NULL, pKeys ); int px, py; m_pLevelPicBorder->GetPos( px, py ); SetSize( m_pLevelPicBorder->GetWide(), py + m_pLevelPicBorder->GetTall() ); // create a selection panel the size of the page CSelectionOverlayPanel *overlay = new CSelectionOverlayPanel( this, parent, chapterIndex ); overlay->SetBounds(0, 0, GetWide(), GetTall()); overlay->MoveToFront(); // HACK: Detect new episode teasers by the "Coming Soon" text wchar_t w_szStrTemp[256]; m_pChapterNameLabel->GetText( w_szStrTemp, sizeof(w_szStrTemp) ); m_bTeaserChapter = !wcscmp(w_szStrTemp, L"Coming Soon"); m_bHasBonus = false; } virtual void ApplySchemeSettings( IScheme *pScheme ) { m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) ); m_FillColor = pScheme->GetColor( "NewGame.FillColor", Color(255, 255, 255, 255) ); m_DisabledColor = pScheme->GetColor( "NewGame.DisabledColor", Color(255, 255, 255, 255) ); m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); BaseClass::ApplySchemeSettings( pScheme ); // Hide chapter numbers for new episode teasers if ( m_bTeaserChapter ) { m_pChapterLabel->SetVisible( false ); } if ( GameUI().IsConsoleUI() ) { m_pChapterNameLabel->SetVisible( false ); } m_pCommentaryIcon = dynamic_cast( FindChildByName( "CommentaryIcon" ) ); if ( m_pCommentaryIcon ) m_pCommentaryIcon->SetVisible( m_bCommentaryMode ); } bool IsSelected( void ) const { return m_bIsSelected; } void SetSelected( bool state ) { m_bIsSelected = state; // update the text/border colors if ( !IsEnabled() ) { m_pChapterLabel->SetFgColor( m_DisabledColor ); m_pChapterNameLabel->SetFgColor( Color(0, 0, 0, 0) ); m_pLevelPicBorder->SetFillColor( m_DisabledColor ); m_pLevelPic->SetAlpha( GameUI().IsConsoleUI() ? 64 : 128 ); return; } if ( state ) { if ( !GameUI().IsConsoleUI() ) { m_pChapterLabel->SetFgColor( m_SelectedColor ); m_pChapterNameLabel->SetFgColor( m_SelectedColor ); } m_pLevelPicBorder->SetFillColor( m_SelectedColor ); } else { m_pChapterLabel->SetFgColor( m_TextColor ); m_pChapterNameLabel->SetFgColor( m_TextColor ); m_pLevelPicBorder->SetFillColor( m_FillColor ); } m_pLevelPic->SetAlpha( 255 ); } const char *GetConfigFile() { return m_szConfigFile; } const char *GetChapter() { return m_szChapter; } bool IsTeaserChapter() { return m_bTeaserChapter; } bool HasBonus() { return m_bHasBonus; } void SetCommentaryMode( bool bCommentaryMode ) { m_bCommentaryMode = bCommentaryMode; if ( m_pCommentaryIcon ) m_pCommentaryIcon->SetVisible( m_bCommentaryMode ); } }; const char *COM_GetModDirectory() { static char modDir[MAX_PATH]; if ( Q_strlen( modDir ) == 0 ) { const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) ); Q_strncpy( modDir, gamedir, sizeof(modDir) ); if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) ) { Q_StripLastDir( modDir, sizeof(modDir) ); int dirlen = Q_strlen( modDir ); Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen ); } } return modDir; } //----------------------------------------------------------------------------- // Purpose: new game chapter selection //----------------------------------------------------------------------------- CNewGameDialog::CNewGameDialog(vgui::Panel *parent, bool bCommentaryMode) : BaseClass(parent, "NewGameDialog") { SetDeleteSelfOnClose(true); SetBounds(0, 0, 372, 160); SetSizeable( false ); m_iSelectedChapter = -1; m_ActiveTitleIdx = 0; m_bCommentaryMode = bCommentaryMode; m_bMapStarting = false; m_bScrolling = false; m_ScrollCt = 0; m_ScrollSpeed = 0.f; m_ButtonPressed = SCROLL_NONE; m_ScrollDirection = SCROLL_NONE; m_pCommentaryLabel = NULL; m_iBonusSelection = 0; m_bScrollToFirstBonusMap = false; SetTitle("#GameUI_NewGame", true); m_pNextButton = new Button( this, "Next", "#gameui_next" ); m_pPrevButton = new Button( this, "Prev", "#gameui_prev" ); m_pPlayButton = new CNewGamePlayButton( this, "Play", "#GameUI_Play" ); m_pPlayButton->SetCommand( "Play" ); vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); cancel->SetCommand( "Close" ); m_pCenterBg = SETUP_PANEL( new Panel( this, "CenterBG" ) ); m_pCenterBg->SetVisible( false ); if ( GameUI().IsConsoleUI() ) { m_pNextButton->SetVisible( false ); m_pPrevButton->SetVisible( false ); m_pPlayButton->SetVisible( false ); cancel->SetVisible( false ); m_pCenterBg->SetPaintBackgroundType( 2 ); m_pCenterBg->SetVisible( true ); m_pChapterTitleLabels[0] = SETUP_PANEL( new Label( this, "ChapterTitleLabel", "" ) ); m_pChapterTitleLabels[0]->SetVisible( true ); m_pChapterTitleLabels[0]->SetFgColor( Color( 255, 255, 255, 255 ) ); m_pChapterTitleLabels[1] = SETUP_PANEL( new Label( this, "ChapterTitleLabel2", "" ) ); m_pChapterTitleLabels[1]->SetVisible( true ); m_pChapterTitleLabels[1]->SetAlpha( 0 ); m_pChapterTitleLabels[1]->SetFgColor( Color( 255, 255, 255, 255 ) ); m_pBonusSelection = SETUP_PANEL( new Label( this, "BonusSelectionLabel", "#GameUI_BonusMapsStandard" ) ); m_pBonusSelectionBorder = SETUP_PANEL( new ImagePanel( this, "BonusSelectionBorder" ) ); m_pFooter = new CFooterPanel( parent, "NewGameFooter" ); m_pFooter->AddNewButtonLabel( "#GameUI_Play", "#GameUI_Icons_A_BUTTON" ); m_pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); } else { m_pFooter = NULL; } // parse out the chapters off disk static const int MAX_CHAPTERS = 32; chapter_t chapters[MAX_CHAPTERS]; char szFullFileName[MAX_PATH]; int chapterIndex = 0; if ( IsPC() || !IsX360() ) { FileFindHandle_t findHandle = FILESYSTEM_INVALID_FIND_HANDLE; const char *fileName = "cfg/chapter*.cfg"; fileName = g_pFullFileSystem->FindFirst( fileName, &findHandle ); while ( fileName && chapterIndex < MAX_CHAPTERS ) { if ( fileName[0] ) { // Only load chapter configs from the current mod's cfg dir // or else chapters appear that we don't want! Q_snprintf( szFullFileName, sizeof(szFullFileName), "cfg/%s", fileName ); FileHandle_t f = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" ); if ( f ) { // don't load chapter files that are empty, used in the demo if ( g_pFullFileSystem->Size(f) > 0 ) { Q_strncpy(chapters[chapterIndex].filename, fileName, sizeof(chapters[chapterIndex].filename)); ++chapterIndex; } g_pFullFileSystem->Close( f ); } } fileName = g_pFullFileSystem->FindNext(findHandle); } } else if ( IsX360() ) { int ChapterStringIndex = 0; bool bExists = true; while ( bExists && chapterIndex < MAX_CHAPTERS ) { Q_snprintf( szFullFileName, sizeof( szFullFileName ), "cfg/chapter%d.cfg", ChapterStringIndex+1 ); FileHandle_t f = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" ); if ( f ) { Q_strncpy(chapters[chapterIndex].filename, szFullFileName + 4, sizeof(chapters[chapterIndex].filename)); ++chapterIndex; ++ChapterStringIndex; g_pFullFileSystem->Close( f ); } else { bExists = false; } //Hack to account for xbox360 missing chapter9a if ( ChapterStringIndex == 10 ) { Q_snprintf( szFullFileName, sizeof( szFullFileName ), "cfg/chapter9a.cfg" ); FileHandle_t fChap = g_pFullFileSystem->Open( szFullFileName, "rb", "MOD" ); if ( fChap ) { Q_strncpy(chapters[chapterIndex].filename, szFullFileName + 4, sizeof(chapters[chapterIndex].filename)); ++chapterIndex; g_pFullFileSystem->Close( fChap ); } } } } bool bBonusesUnlocked = false; if ( GameUI().IsConsoleUI() ) { if ( !m_bCommentaryMode ) { // Scan to see if the bonus maps have been unlocked bBonusesUnlocked = BonusMapsDatabase()->BonusesUnlocked(); } } // sort the chapters qsort(chapters, chapterIndex, sizeof(chapter_t), &ChapterSortFunc); // work out which chapters are unlocked ConVarRef var( "sv_unlockedchapters" ); if ( bBonusesUnlocked ) { // Bonuses are unlocked so we need to unlock all the chapters too var.SetValue( 15 ); } const char *unlockedChapter = var.IsValid() ? var.GetString() : "1"; int iUnlockedChapter = atoi(unlockedChapter); // add chapters to combobox for (int i = 0; i < chapterIndex; i++) { const char *fileName = chapters[i].filename; char chapterID[32] = { 0 }; sscanf(fileName, "chapter%s", chapterID); // strip the extension char *ext = V_stristr(chapterID, ".cfg"); if (ext) { *ext = 0; } const char *pGameDir = COM_GetModDirectory(); char chapterName[64]; Q_snprintf(chapterName, sizeof(chapterName), "#%s_Chapter%s_Title", pGameDir, chapterID); Q_snprintf( szFullFileName, sizeof( szFullFileName ), "%s", fileName ); CGameChapterPanel *chapterPanel = SETUP_PANEL( new CGameChapterPanel( this, NULL, chapterName, i, chapterID, szFullFileName, m_bCommentaryMode ) ); chapterPanel->SetVisible( false ); UpdatePanelLockedStatus( iUnlockedChapter, i + 1, chapterPanel ); if ( GameUI().IsConsoleUI() ) { if ( bBonusesUnlocked ) { // check to see if it has associated challenges for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap ) { BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap ); if ( Q_stricmp( pMap->szChapterName, szFullFileName ) == 0 && !pMap->bLocked ) { chapterPanel->m_bHasBonus = true; chapterPanel->SetControlVisible( "HasBonusLabel", true ); } } } } m_ChapterPanels.AddToTail( chapterPanel ); } KeyValues *pKeys = NULL; if ( GameUI().IsConsoleUI() ) { pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameDialog.res" ); } LoadControlSettings( "Resource/NewGameDialog.res", NULL, pKeys ); // Reset all properties for ( int i = 0; i < NUM_SLOTS; ++i ) { m_PanelIndex[i] = INVALID_INDEX; } if ( !m_ChapterPanels.Count() ) { UpdateMenuComponents( SCROLL_NONE ); return; } // Layout panel positions relative to the dialog center. int panelWidth = m_ChapterPanels[0]->GetWide() + 16; int dialogWidth = GetWide(); m_PanelXPos[2] = ( dialogWidth - panelWidth ) / 2 + 8; if (m_ChapterPanels.Count() > 1) { m_PanelXPos[1] = m_PanelXPos[2] - panelWidth; m_PanelXPos[0] = m_PanelXPos[1]; m_PanelXPos[3] = m_PanelXPos[2] + panelWidth; m_PanelXPos[4] = m_PanelXPos[3]; } else { m_PanelXPos[0] = m_PanelXPos[1] = m_PanelXPos[3] = m_PanelXPos[4] = m_PanelXPos[2]; } m_PanelAlpha[0] = 0; m_PanelAlpha[1] = 255; m_PanelAlpha[2] = 255; m_PanelAlpha[3] = 255; m_PanelAlpha[4] = 0; int panelHeight; m_ChapterPanels[0]->GetSize( panelWidth, panelHeight ); m_pCenterBg->SetWide( panelWidth + 16 ); m_pCenterBg->SetPos( m_PanelXPos[2] - 8, m_PanelYPos[2] - (m_pCenterBg->GetTall() - panelHeight) + 8 ); m_pCenterBg->SetBgColor( Color( 190, 115, 0, 255 ) ); // start the first item selected SetSelectedChapterIndex( 0 ); } CNewGameDialog::~CNewGameDialog() { delete m_pFooter; m_pFooter = NULL; } void CNewGameDialog::Activate( void ) { m_bMapStarting = false; if ( GameUI().IsConsoleUI() ) { // Stop blinking the menu item now that we've seen the unlocked stuff CBasePanel *pBasePanel = BasePanel(); if ( pBasePanel ) pBasePanel->SetMenuItemBlinkingState( "OpenNewGameDialog", false ); BonusMapsDatabase()->SetBlink( false ); } // Commentary stuff is set up on activate because in XBox the new game menu is never deleted SetTitle( ( ( m_bCommentaryMode ) ? ( "#GameUI_LoadCommentary" ) : ( "#GameUI_NewGame") ), true); if ( m_pCommentaryLabel ) m_pCommentaryLabel->SetVisible( m_bCommentaryMode ); // work out which chapters are unlocked ConVarRef var( "sv_unlockedchapters" ); const char *unlockedChapter = var.IsValid() ? var.GetString() : "1"; int iUnlockedChapter = atoi(unlockedChapter); for ( int i = 0; i < m_ChapterPanels.Count(); i++) { CGameChapterPanel *pChapterPanel = m_ChapterPanels[ i ]; if ( pChapterPanel ) { pChapterPanel->SetCommentaryMode( m_bCommentaryMode ); UpdatePanelLockedStatus( iUnlockedChapter, i + 1, pChapterPanel ); } } BaseClass::Activate(); } //----------------------------------------------------------------------------- // Purpose: Apply special properties of the menu //----------------------------------------------------------------------------- void CNewGameDialog::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); int ypos = inResourceData->GetInt( "chapterypos", 40 ); for ( int i = 0; i < NUM_SLOTS; ++i ) { m_PanelYPos[i] = ypos; } m_pCenterBg->SetTall( inResourceData->GetInt( "centerbgtall", 0 ) ); g_ScrollSpeedSlow = inResourceData->GetFloat( "scrollslow", 0.0f ); g_ScrollSpeedFast = inResourceData->GetFloat( "scrollfast", 0.0f ); SetFastScroll( false ); } void CNewGameDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); if ( m_pFooter ) { KeyValues *pFooterControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "NewGameFooter.res" ); m_pFooter->LoadControlSettings( "null", NULL, pFooterControlSettings ); } UpdateMenuComponents( SCROLL_NONE ); m_pCommentaryLabel = dynamic_cast( FindChildByName( "CommentaryUnlock" ) ); if ( m_pCommentaryLabel ) m_pCommentaryLabel->SetVisible( m_bCommentaryMode ); if ( GameUI().IsConsoleUI() ) { if ( !m_bCommentaryMode && BonusMapsDatabase()->BonusesUnlocked() && !m_ChapterPanels[ m_PanelIndex[SLOT_CENTER] ]->HasBonus() ) { // Find the first bonus ScrollSelectionPanels( SCROLL_LEFT ); m_bScrollToFirstBonusMap = true; } } } static float GetArrowAlpha( void ) { // X360TBD: Pulsing arrows return 255.f; } //----------------------------------------------------------------------------- // Purpose: sets the correct properties for visible components //----------------------------------------------------------------------------- void CNewGameDialog::UpdateMenuComponents( EScrollDirection dir ) { // This is called prior to any scrolling, // so we need to look ahead to the post-scroll state int centerIdx = SLOT_CENTER; if ( dir == SCROLL_LEFT ) { ++centerIdx; } else if ( dir == SCROLL_RIGHT ) { --centerIdx; } int leftIdx = centerIdx - 1; int rightIdx = centerIdx + 1; if ( GameUI().IsConsoleUI() ) { bool bHasBonus = false; if ( m_PanelIndex[centerIdx] != INVALID_INDEX ) { wchar_t buffer[ MAX_PATH ]; m_ChapterPanels[ m_PanelIndex[centerIdx] ]->m_pChapterNameLabel->GetText( buffer, sizeof(buffer) ); m_pChapterTitleLabels[(unsigned)m_ActiveTitleIdx]->SetText( buffer ); // If it has bonuses show the scroll up and down arrows bHasBonus = m_ChapterPanels[ m_PanelIndex[centerIdx] ]->HasBonus(); } vgui::Panel *leftArrow = this->FindChildByName( "LeftArrow" ); vgui::Panel *rightArrow = this->FindChildByName( "RightArrow" ); if ( leftArrow ) { if ( m_PanelIndex[leftIdx] != INVALID_INDEX ) { leftArrow->SetFgColor( Color( 255, 255, 255, GetArrowAlpha() ) ); } else { leftArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); } } if ( rightArrow ) { if ( m_PanelIndex[rightIdx] != INVALID_INDEX ) { rightArrow->SetFgColor( Color( 255, 255, 255, GetArrowAlpha() ) ); } else { rightArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); } } if ( bHasBonus ) { // Find the bonus description for this panel for ( int iBonus = 0; iBonus < BonusMapsDatabase()->BonusCount(); ++iBonus ) { m_pBonusMapDescription = BonusMapsDatabase()->GetBonusData( iBonus ); if ( Q_stricmp( m_pBonusMapDescription->szChapterName, m_ChapterPanels[ m_PanelIndex[centerIdx] ]->GetConfigFile() ) == 0 ) break; } } else { m_pBonusMapDescription = NULL; } vgui::Panel *upArrow = this->FindChildByName( "UpArrow" ); vgui::Panel *downArrow = this->FindChildByName( "DownArrow" ); if ( upArrow ) upArrow->SetVisible( bHasBonus ); if ( downArrow ) downArrow->SetVisible( bHasBonus ); m_pBonusSelection->SetVisible( bHasBonus ); m_pBonusSelectionBorder->SetVisible( bHasBonus ); UpdateBonusSelection(); } // No buttons in the xbox ui if ( !GameUI().IsConsoleUI() ) { if ( m_PanelIndex[leftIdx] == INVALID_INDEX || m_PanelIndex[leftIdx] == 0 ) { m_pPrevButton->SetVisible( false ); m_pPrevButton->SetEnabled( false ); } else { m_pPrevButton->SetVisible( true ); m_pPrevButton->SetEnabled( true ); } if ( m_ChapterPanels.Count() < 4 ) // if there are less than 4 chapters show the next button but disabled { m_pNextButton->SetVisible( true ); m_pNextButton->SetEnabled( false ); } else if ( m_PanelIndex[rightIdx] == INVALID_INDEX || m_PanelIndex[rightIdx] == m_ChapterPanels.Count()-1 ) { m_pNextButton->SetVisible( false ); m_pNextButton->SetEnabled( false ); } else { m_pNextButton->SetVisible( true ); m_pNextButton->SetEnabled( true ); } } } void CNewGameDialog::UpdateBonusSelection( void ) { int iNumChallenges = 0; if ( m_pBonusMapDescription ) { if ( m_pBonusMapDescription->m_pChallenges ) iNumChallenges = m_pBonusMapDescription->m_pChallenges->Count(); // Wrap challenge selection to fit number of possible selections if ( m_iBonusSelection < 0 ) m_iBonusSelection = iNumChallenges + 1; else if ( m_iBonusSelection >= iNumChallenges + 2 ) m_iBonusSelection = 0; } else { // No medals to show SetControlVisible( "ChallengeEarnedMedal", false ); SetControlVisible( "ChallengeBestLabel", false ); SetControlVisible( "ChallengeNextMedal", false ); SetControlVisible( "ChallengeNextLabel", false ); return; } if ( m_iBonusSelection == 0 ) { m_pBonusSelection->SetText( "#GameUI_BonusMapsStandard" ); SetControlVisible( "ChallengeEarnedMedal", false ); SetControlVisible( "ChallengeBestLabel", false ); SetControlVisible( "ChallengeNextMedal", false ); SetControlVisible( "ChallengeNextLabel", false ); } else if ( m_iBonusSelection == 1 ) { m_pBonusSelection->SetText( "#GameUI_BonusMapsAdvanced" ); SetControlVisible( "ChallengeEarnedMedal", false ); SetControlVisible( "ChallengeBestLabel", false ); SetControlVisible( "ChallengeNextMedal", false ); SetControlVisible( "ChallengeNextLabel", false ); char szMapAdvancedName[ 256 ] = ""; if ( m_pBonusMapDescription ) { Q_snprintf( szMapAdvancedName, sizeof( szMapAdvancedName ), "%s_advanced", m_pBonusMapDescription->szMapFileName ); } BonusMapDescription_t *pAdvancedDescription = NULL; // Find the bonus description for this panel for ( int iBonus = 0; iBonus < BonusMapsDatabase()->BonusCount(); ++iBonus ) { pAdvancedDescription = BonusMapsDatabase()->GetBonusData( iBonus ); if ( Q_stricmp( szMapAdvancedName, pAdvancedDescription->szMapFileName ) == 0 ) break; } if ( pAdvancedDescription && pAdvancedDescription->bComplete ) { CBitmapImagePanel *pBitmap = dynamic_cast( FindChildByName( "ChallengeEarnedMedal" ) ); pBitmap->SetVisible( true ); pBitmap->setTexture( "hud/icon_complete" ); } } else { int iChallenge = m_iBonusSelection - 2; ChallengeDescription_t *pChallengeDescription = &((*m_pBonusMapDescription->m_pChallenges)[ iChallenge ]); // Set the display text for the selected challenge m_pBonusSelection->SetText( pChallengeDescription->szName ); int iBest, iEarnedMedal, iNext, iNextMedal; GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal ); char szBuff[ 512 ]; // Set earned medal if ( iEarnedMedal > -1 && iBest != -1 ) { if ( iChallenge < 10 ) Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_0%i_%s", iChallenge, g_pszMedalNames[ iEarnedMedal ] ); else Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_%i_%s", iChallenge, g_pszMedalNames[ iEarnedMedal ] ); CBitmapImagePanel *pBitmap = dynamic_cast( FindChildByName( "ChallengeEarnedMedal" ) ); pBitmap->SetVisible( true ); pBitmap->setTexture( szBuff ); } else { CBitmapImagePanel *pBitmap = dynamic_cast( FindChildByName( "ChallengeEarnedMedal" ) ); pBitmap->SetVisible( false ); } // Set next medal if ( iNextMedal > 0 ) { if ( iChallenge < 10 ) Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_0%i_%s", iChallenge, g_pszMedalNames[ iNextMedal ] ); else Q_snprintf( szBuff, sizeof( szBuff ), "medals/medal_%i_%s", iChallenge, g_pszMedalNames[ iNextMedal ] ); CBitmapImagePanel *pBitmap = dynamic_cast( FindChildByName( "ChallengeNextMedal" ) ); pBitmap->SetVisible( true ); pBitmap->setTexture( szBuff ); } else { SetControlVisible( "ChallengeNextMedal", false ); } wchar_t szWideBuff[ 64 ]; wchar_t szWideBuff2[ 64 ]; // Best label if ( iBest != -1 ) { Q_snprintf( szBuff, sizeof( szBuff ), "%i", iBest ); g_pVGuiLocalize->ConvertANSIToUnicode( szBuff, szWideBuff2, sizeof( szWideBuff2 ) ); g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( "#GameUI_BonusMapsBest" ), 1, szWideBuff2 ); g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) ); SetControlString( "ChallengeBestLabel", szBuff ); SetControlVisible( "ChallengeBestLabel", true ); } else { SetControlVisible( "ChallengeBestLabel", false ); } // Next label if ( iNext != -1 ) { Q_snprintf( szBuff, sizeof( szBuff ), "%i", iNext ); g_pVGuiLocalize->ConvertANSIToUnicode( szBuff, szWideBuff2, sizeof( szWideBuff2 ) ); g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( "#GameUI_BonusMapsGoal" ), 1, szWideBuff2 ); g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) ); SetControlString( "ChallengeNextLabel", szBuff ); SetControlVisible( "ChallengeNextLabel", true ); } else { SetControlVisible( "ChallengeNextLabel", false ); } } } //----------------------------------------------------------------------------- // Purpose: sets a chapter as selected //----------------------------------------------------------------------------- void CNewGameDialog::SetSelectedChapterIndex( int index ) { m_iSelectedChapter = index; for (int i = 0; i < m_ChapterPanels.Count(); i++) { if ( i == index ) { m_ChapterPanels[i]->SetSelected( true ); } else { m_ChapterPanels[i]->SetSelected( false ); } } if ( m_pPlayButton ) { m_pPlayButton->SetEnabled( true ); } // Setup panels to the left of the selected panel int selectedSlot = GameUI().IsConsoleUI() ? SLOT_CENTER : index % 3 + 1; int currIdx = index; for ( int i = selectedSlot; i >= 0 && currIdx >= 0; --i ) { m_PanelIndex[i] = currIdx; --currIdx; InitPanelIndexForDisplay( i ); } // Setup panels to the right of the selected panel currIdx = index + 1; for ( int i = selectedSlot + 1; i < NUM_SLOTS && currIdx < m_ChapterPanels.Count(); ++i ) { m_PanelIndex[i] = currIdx; ++currIdx; InitPanelIndexForDisplay( i ); } UpdateMenuComponents( SCROLL_NONE ); } //----------------------------------------------------------------------------- // Purpose: sets a chapter as selected //----------------------------------------------------------------------------- void CNewGameDialog::SetSelectedChapter( const char *chapter ) { Assert( chapter ); for (int i = 0; i < m_ChapterPanels.Count(); i++) { if ( chapter && !Q_stricmp(m_ChapterPanels[i]->GetChapter(), chapter) ) { m_iSelectedChapter = i; m_ChapterPanels[m_iSelectedChapter]->SetSelected( true ); } else { m_ChapterPanels[i]->SetSelected( false ); } } if ( m_pPlayButton ) { m_pPlayButton->SetEnabled( true ); } } //----------------------------------------------------------------------------- // iUnlockedChapter - the value of sv_unlockedchapters, 1-based. A value of 0 // is treated as a 1, since at least one chapter must be unlocked. // // i - the 1-based index of the chapter we're considering. //----------------------------------------------------------------------------- void CNewGameDialog::UpdatePanelLockedStatus( int iUnlockedChapter, int i, CGameChapterPanel *pChapterPanel ) { if ( iUnlockedChapter <= 0 ) { iUnlockedChapter = 1; } // Commentary mode requires chapters to be finished before they can be chosen bool bLocked = false; if ( m_bCommentaryMode ) { bLocked = ( iUnlockedChapter <= i ); } else { if ( iUnlockedChapter < i ) { // Never lock the first chapter bLocked = ( i != 0 ); } } pChapterPanel->SetEnabled( !bLocked ); } //----------------------------------------------------------------------------- // Purpose: Called before a panel scroll starts. //----------------------------------------------------------------------------- void CNewGameDialog::PreScroll( EScrollDirection dir ) { int hideIdx = INVALID_INDEX; if ( dir == SCROLL_LEFT ) { hideIdx = m_PanelIndex[SLOT_LEFT]; } else if ( dir == SCROLL_RIGHT ) { hideIdx = m_PanelIndex[SLOT_RIGHT]; } if ( hideIdx != INVALID_INDEX ) { // Push back the panel that's about to be hidden // so the next panel scrolls over the top of it. m_ChapterPanels[hideIdx]->SetZPos( 0 ); } // Flip the active title label prior to the crossfade m_ActiveTitleIdx ^= 0x01; } //----------------------------------------------------------------------------- // Purpose: Called after a panel scroll finishes. //----------------------------------------------------------------------------- void CNewGameDialog::PostScroll( EScrollDirection dir ) { int index = INVALID_INDEX; if ( dir == SCROLL_LEFT ) { index = m_PanelIndex[SLOT_RIGHT]; } else if ( dir == SCROLL_RIGHT ) { index = m_PanelIndex[SLOT_LEFT]; } // Fade in the revealed panel if ( index != INVALID_INDEX ) { CGameChapterPanel *panel = m_ChapterPanels[index]; panel->SetZPos( 50 ); GetAnimationController()->RunAnimationCommand( panel, "alpha", 255, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); } if ( GameUI().IsConsoleUI() ) { if ( BonusMapsDatabase()->BonusesUnlocked() && m_bScrollToFirstBonusMap ) { if ( !m_ChapterPanels[ m_PanelIndex[SLOT_CENTER] ]->HasBonus() ) { // Find the first bonus ScrollSelectionPanels( SCROLL_LEFT ); } else { // Found a bonus, stop scrolling m_bScrollToFirstBonusMap = false; } } } } //----------------------------------------------------------------------------- // Purpose: Initiates a panel scroll and starts the animation. //----------------------------------------------------------------------------- void CNewGameDialog::ScrollSelectionPanels( EScrollDirection dir ) { // Only initiate a scroll if panels aren't currently scrolling if ( !m_bScrolling ) { // Handle any pre-scroll setup PreScroll( dir ); if ( dir == SCROLL_LEFT) { m_ScrollCt += SCROLL_LEFT; } else if ( dir == SCROLL_RIGHT && m_PanelIndex[SLOT_CENTER] != 0 ) { m_ScrollCt += SCROLL_RIGHT; } m_bScrolling = true; AnimateSelectionPanels(); // Update the arrow colors, help text, and buttons. Doing it here looks better than having // the components change after the entire scroll animation has finished. UpdateMenuComponents( m_ScrollDirection ); } } void CNewGameDialog::ScrollBonusSelection( EScrollDirection dir ) { // Don't scroll if there's no bonuses for this panel if ( !m_pBonusMapDescription ) return; m_iBonusSelection += dir; vgui::surface()->PlaySound( "UI/buttonclick.wav" ); UpdateBonusSelection(); } //----------------------------------------------------------------------------- // Purpose: Initiates the scripted scroll and fade effects of all five slotted panels //----------------------------------------------------------------------------- void CNewGameDialog::AnimateSelectionPanels( void ) { int idxOffset = 0; int startIdx = SLOT_LEFT; int endIdx = SLOT_RIGHT; // Don't scroll outside the bounds of the panel list if ( m_ScrollCt >= SCROLL_LEFT && (m_PanelIndex[SLOT_CENTER] < m_ChapterPanels.Count() - 1 || !GameUI().IsConsoleUI()) ) { idxOffset = -1; endIdx = SLOT_OFFRIGHT; m_ScrollDirection = SCROLL_LEFT; } else if ( m_ScrollCt <= SCROLL_RIGHT && (m_PanelIndex[SLOT_CENTER] > 0 || !GameUI().IsConsoleUI()) ) { idxOffset = 1; startIdx = SLOT_OFFLEFT; m_ScrollDirection = SCROLL_RIGHT; } if ( 0 == idxOffset ) { // Kill the scroll, it's outside the bounds m_ScrollCt = 0; m_bScrolling = false; m_ScrollDirection = SCROLL_NONE; vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); return; } // Should never happen if ( startIdx > endIdx ) return; for ( int i = startIdx; i <= endIdx; ++i ) { if ( m_PanelIndex[i] != INVALID_INDEX ) { int nextIdx = i + idxOffset; CGameChapterPanel *panel = m_ChapterPanels[ m_PanelIndex[i] ]; GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[nextIdx], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); } } if ( GameUI().IsConsoleUI() ) { vgui::surface()->PlaySound( "UI/buttonclick.wav" ); // Animate the center background panel GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_LINEAR ); // Crossfade the chapter title labels int inactiveTitleIdx = m_ActiveTitleIdx ^ 0x01; GetAnimationController()->RunAnimationCommand( m_pChapterTitleLabels[(unsigned)m_ActiveTitleIdx], "alpha", 255, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); GetAnimationController()->RunAnimationCommand( m_pChapterTitleLabels[inactiveTitleIdx], "alpha", 0, 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_LINEAR ); // Scrolling up through chapters, offset is negative m_iSelectedChapter -= idxOffset; } PostMessage( this, new KeyValues( "FinishScroll" ), m_ScrollSpeed ); } //----------------------------------------------------------------------------- // Purpose: After a scroll, each panel slot holds the index of a panel that has // scrolled to an adjacent slot. This function updates each slot so // it holds the index of the panel that is actually in that slot's position. //----------------------------------------------------------------------------- void CNewGameDialog::ShiftPanelIndices( int offset ) { // Shift all the elements over one slot, then calculate what the last slot's index should be. int lastSlot = NUM_SLOTS - 1; if ( offset > 0 ) { // Hide the panel that's dropping out of the slots if ( IsValidPanel( m_PanelIndex[0] ) ) { m_ChapterPanels[ m_PanelIndex[0] ]->SetVisible( false ); } // Scrolled panels to the right, so shift the indices one slot to the left Q_memmove( &m_PanelIndex[0], &m_PanelIndex[1], lastSlot * sizeof( m_PanelIndex[0] ) ); if ( m_PanelIndex[lastSlot] != INVALID_INDEX ) { int num = m_PanelIndex[ lastSlot ] + 1; if ( IsValidPanel( num ) ) { m_PanelIndex[lastSlot] = num; InitPanelIndexForDisplay( lastSlot ); } else { m_PanelIndex[lastSlot] = INVALID_INDEX; } } } else { // Hide the panel that's dropping out of the slots if ( IsValidPanel( m_PanelIndex[lastSlot] ) ) { m_ChapterPanels[ m_PanelIndex[lastSlot] ]->SetVisible( false ); } // Scrolled panels to the left, so shift the indices one slot to the right Q_memmove( &m_PanelIndex[1], &m_PanelIndex[0], lastSlot * sizeof( m_PanelIndex[0] ) ); if ( m_PanelIndex[0] != INVALID_INDEX ) { int num = m_PanelIndex[0] - 1; if ( IsValidPanel( num ) ) { m_PanelIndex[0] = num; InitPanelIndexForDisplay( 0 ); } else { m_PanelIndex[0] = INVALID_INDEX; } } } } //----------------------------------------------------------------------------- // Purpose: Validates an index into the selection panels vector //----------------------------------------------------------------------------- bool CNewGameDialog::IsValidPanel( const int idx ) { if ( idx < 0 || idx >= m_ChapterPanels.Count() ) return false; return true; } //----------------------------------------------------------------------------- // Purpose: Sets up a panel's properties before it is displayed //----------------------------------------------------------------------------- void CNewGameDialog::InitPanelIndexForDisplay( const int idx ) { CGameChapterPanel *panel = m_ChapterPanels[ m_PanelIndex[idx] ]; if ( panel ) { panel->SetPos( m_PanelXPos[idx], m_PanelYPos[idx] ); panel->SetAlpha( m_PanelAlpha[idx] ); panel->SetVisible( true ); if ( m_PanelAlpha[idx] ) { panel->SetZPos( 50 ); } } } //----------------------------------------------------------------------------- // Purpose: Sets which scroll speed should be used //----------------------------------------------------------------------------- void CNewGameDialog::SetFastScroll( bool fast ) { m_ScrollSpeed = fast ? g_ScrollSpeedFast : g_ScrollSpeedSlow; } //----------------------------------------------------------------------------- // Purpose: Checks if a button is being held down, and speeds up the scroll //----------------------------------------------------------------------------- void CNewGameDialog::ContinueScrolling( void ) { if ( !GameUI().IsConsoleUI() ) { if ( m_PanelIndex[SLOT_CENTER-1] % 3 ) { // m_ButtonPressed = m_ScrollDirection; ScrollSelectionPanels( m_ScrollDirection ); } return; } if ( m_ButtonPressed == m_ScrollDirection ) { SetFastScroll( true ); ScrollSelectionPanels( m_ScrollDirection ); } else if ( m_ButtonPressed != SCROLL_NONE ) { // The other direction has been pressed - start a slow scroll SetFastScroll( false ); ScrollSelectionPanels( (EScrollDirection)m_ButtonPressed ); } else { SetFastScroll( false ); } } //----------------------------------------------------------------------------- // Purpose: Called when a scroll distance of one slot has been completed //----------------------------------------------------------------------------- void CNewGameDialog::FinishScroll( void ) { // Fade the center bg panel back in GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_LINEAR ); ShiftPanelIndices( m_ScrollDirection ); m_bScrolling = false; m_ScrollCt = 0; // End of scroll step PostScroll( m_ScrollDirection ); // Continue scrolling if necessary ContinueScrolling(); } //----------------------------------------------------------------------------- // Purpose: starts the game at the specified skill level //----------------------------------------------------------------------------- void CNewGameDialog::StartGame( void ) { if ( m_ChapterPanels.IsValidIndex( m_iSelectedChapter ) ) { char mapcommand[512]; mapcommand[0] = 0; Q_snprintf( mapcommand, sizeof( mapcommand ), "disconnect\ndeathmatch 0\nprogress_enable\nexec %s\n", m_ChapterPanels[m_iSelectedChapter]->GetConfigFile() ); // Set commentary ConVarRef commentary( "commentary" ); commentary.SetValue( m_bCommentaryMode ); ConVarRef sv_cheats( "sv_cheats" ); sv_cheats.SetValue( m_bCommentaryMode ); if ( IsPC() ) { // If commentary is on, we go to the explanation dialog (but not for teaser trailers) if ( m_bCommentaryMode && !m_ChapterPanels[m_iSelectedChapter]->IsTeaserChapter() ) { // Check our current state and disconnect us from any multiplayer server we're connected to. // This fixes an exploit where players would click "start" on the commentary dialog to enable // sv_cheats on the client (via the code above) and then hit to get out of the explanation dialog. if ( GameUI().IsInMultiplayer() ) { engine->ExecuteClientCmd( "disconnect" ); } DHANDLE hCommentaryExplanationDialog; if ( !hCommentaryExplanationDialog.Get() ) { hCommentaryExplanationDialog = new CCommentaryExplanationDialog( BasePanel(), mapcommand ); } hCommentaryExplanationDialog->Activate(); } else { // start map BasePanel()->FadeToBlackAndRunEngineCommand( mapcommand ); } } else if ( IsX360() ) { if ( m_ChapterPanels[m_iSelectedChapter]->HasBonus() && m_iBonusSelection > 0 ) { if ( m_iBonusSelection == 1 ) { // Run the advanced chamber instead of the config file char *pLastSpace = Q_strrchr( mapcommand, '\n' ); pLastSpace[ 0 ] = '\0'; pLastSpace = Q_strrchr( mapcommand, '\n' ); Q_snprintf( pLastSpace, sizeof( mapcommand ) - Q_strlen( mapcommand ), "\nmap %s_advanced\n", m_pBonusMapDescription->szMapFileName ); } else { char sz[ 256 ]; int iChallenge = m_iBonusSelection - 1; // Set up the challenge mode Q_snprintf( sz, sizeof( sz ), "sv_bonus_challenge %i\n", iChallenge ); engine->ClientCmd_Unrestricted( sz ); ChallengeDescription_t *pChallengeDescription = &((*m_pBonusMapDescription->m_pChallenges)[ iChallenge - 1 ]); // Set up medal goals BonusMapsDatabase()->SetCurrentChallengeObjectives( pChallengeDescription->iBronze, pChallengeDescription->iSilver, pChallengeDescription->iGold ); BonusMapsDatabase()->SetCurrentChallengeNames( m_pBonusMapDescription->szFileName, m_pBonusMapDescription->szMapName, pChallengeDescription->szName ); } } m_bMapStarting = true; BasePanel()->FadeToBlackAndRunEngineCommand( mapcommand ); } OnClose(); } } void CNewGameDialog::OnClose( void ) { m_KeyRepeat.Reset(); if ( GameUI().IsConsoleUI() && !m_bMapStarting ) { BasePanel()->RunCloseAnimation( "CloseNewGameDialog_OpenMainMenu" ); BonusMapsDatabase()->WriteSaveData(); // Closing this dialog is a good time to save } BaseClass::OnClose(); } //----------------------------------------------------------------------------- // Purpose: handles button commands //----------------------------------------------------------------------------- void CNewGameDialog::OnCommand( const char *command ) { bool bReset = true; if ( !stricmp( command, "Play" ) ) { if ( m_bMapStarting ) return; if ( GameUI().IsConsoleUI() ) { if ( m_ChapterPanels[m_iSelectedChapter]->IsEnabled() ) { if ( !GameUI().HasSavedThisMenuSession() && GameUI().IsInLevel() && engine->GetMaxClients() == 1 ) { vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_NEW_GAME, this ); } else { OnCommand( "StartNewGame" ); } } else { // This chapter isn't unlocked! m_bMapStarting = false; vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); if ( m_bCommentaryMode ) { BasePanel()->ShowMessageDialog( MD_COMMENTARY_CHAPTER_UNLOCK_EXPLANATION, this ); } } } else { StartGame(); } } #ifdef _X360 else if ( !stricmp( command, "StartNewGame" ) ) { ConVarRef commentary( "commentary" ); if ( m_bCommentaryMode && !commentary.GetBool() ) { // Using the commentary menu, but not already in commentary mode, explain the rules PostMessage( (vgui::Panel*)this, new KeyValues( "command", "command", "StartNewGameWithCommentaryExplanation" ), 0.2f ); } else { if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED || !ModInfo().IsSinglePlayerOnly() ) { // Multiplayer or no storage device so don't bore them with autosave details m_bMapStarting = true; OnCommand( "StartNewGameNoCommentaryExplanation" ); } else { // Don't allow other inputs m_bMapStarting = true; // Remind them how autosaves work PostMessage( (vgui::Panel*)this, new KeyValues( "command", "command", "StartNewGameWithAutosaveExplanation" ), 0.2f ); } } } else if ( !stricmp( command, "StartNewGameWithAutosaveExplanation" ) ) { BasePanel()->ShowMessageDialog( MD_AUTOSAVE_EXPLANATION, this ); } else if ( !stricmp( command, "StartNewGameWithCommentaryExplanation" ) ) { if ( ModInfo().IsSinglePlayerOnly() ) { // Don't allow other inputs m_bMapStarting = true; BasePanel()->ShowMessageDialog( MD_COMMENTARY_EXPLANATION, this ); } else { // Don't allow other inputs m_bMapStarting = true; BasePanel()->ShowMessageDialog( MD_COMMENTARY_EXPLANATION_MULTI, this ); } } else if ( !stricmp( command, "StartNewGameNoCommentaryExplanation" ) ) { vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); BasePanel()->RunAnimationWithCallback( this, "CloseNewGameDialog", new KeyValues( "StartGame" ) ); } #endif else if ( !stricmp( command, "Next" ) ) { if ( m_bMapStarting ) return; ScrollSelectionPanels( SCROLL_LEFT ); bReset = false; } else if ( !stricmp( command, "Prev" ) ) { if ( m_bMapStarting ) return; ScrollSelectionPanels( SCROLL_RIGHT ); bReset = false; } else if ( !stricmp( command, "Mode_Next" ) ) { if ( m_bMapStarting ) return; ScrollBonusSelection( SCROLL_LEFT ); bReset = false; } else if ( !stricmp( command, "Mode_Prev" ) ) { if ( m_bMapStarting ) return; ScrollBonusSelection( SCROLL_RIGHT ); bReset = false; } else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) { vgui::surface()->RestrictPaintToSinglePanel(NULL); } else { BaseClass::OnCommand( command ); } if ( bReset ) { m_KeyRepeat.Reset(); } } void CNewGameDialog::PaintBackground() { if ( !GameUI().IsConsoleUI() ) { BaseClass::PaintBackground(); return; } int wide, tall; GetSize( wide, tall ); Color col = GetBgColor(); DrawBox( 0, 0, wide, tall, col, 1.0f ); int y = 0; if ( m_pChapterTitleLabels[0] ) { // offset by title int titleX, titleY, titleWide, titleTall; m_pChapterTitleLabels[0]->GetBounds( titleX, titleY, titleWide, titleTall ); y += titleY + titleTall; } else { y = 8; } // draw an inset Color darkColor; darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() ); vgui::surface()->DrawSetColor( darkColor ); vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 ); } void CNewGameDialog::OnKeyCodePressed( KeyCode code ) { switch ( code ) { case KEY_XBUTTON_LEFT: case KEY_XSTICK1_LEFT: case KEY_XSTICK2_LEFT: case KEY_LEFT: case STEAMCONTROLLER_DPAD_LEFT: if ( !m_bScrolling ) { for ( int i = 0; i < m_ChapterPanels.Count(); ++i ) { if ( m_ChapterPanels[ i ]->IsSelected() ) { int nNewChapter = i - 1; if ( nNewChapter >= 0 ) { if ( nNewChapter < m_PanelIndex[ SLOT_LEFT ] && m_PanelIndex[ SLOT_LEFT ] != -1 ) { ScrollSelectionPanels( SCROLL_RIGHT ); } else if ( m_ChapterPanels[ nNewChapter ]->IsEnabled() ) { SetSelectedChapterIndex( nNewChapter ); } } break; } } } return; case KEY_XBUTTON_RIGHT: case KEY_XSTICK1_RIGHT: case KEY_XSTICK2_RIGHT: case KEY_RIGHT: case STEAMCONTROLLER_DPAD_RIGHT: if ( !m_bScrolling ) { for ( int i = 0; i < m_ChapterPanels.Count(); ++i ) { if ( m_ChapterPanels[ i ]->IsSelected() ) { int nNewChapter = i + 1; if ( nNewChapter < m_ChapterPanels.Count() ) { if ( nNewChapter > m_PanelIndex[ SLOT_RIGHT ] && m_PanelIndex[ SLOT_RIGHT ] != -1 ) { ScrollSelectionPanels( SCROLL_LEFT ); } else if ( m_ChapterPanels[ nNewChapter ]->IsEnabled() ) { SetSelectedChapterIndex( nNewChapter ); } } break; } } } return; case KEY_XBUTTON_B: case STEAMCONTROLLER_B: OnCommand( "Close" ); return; case KEY_XBUTTON_A: case STEAMCONTROLLER_A: OnCommand( "Play" ); return; } m_KeyRepeat.KeyDown( code ); BaseClass::OnKeyCodePressed( code ); } void CNewGameDialog::OnKeyCodeReleased( vgui::KeyCode code ) { m_KeyRepeat.KeyUp( code ); BaseClass::OnKeyCodeReleased( code ); } void CNewGameDialog::OnThink() { vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); if ( code ) { OnKeyCodeTyped( code ); } BaseClass::OnThink(); }