diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gameui | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'gameui')
147 files changed, 40635 insertions, 0 deletions
diff --git a/gameui/BackgroundMenuButton.cpp b/gameui/BackgroundMenuButton.cpp new file mode 100644 index 0000000..f67f487 --- /dev/null +++ b/gameui/BackgroundMenuButton.cpp @@ -0,0 +1,72 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BackgroundMenuButton.h" + +#include <KeyValues.h> +#include <vgui/IImage.h> +#include <vgui/IScheme.h> +#include <vgui_controls/Menu.h> +#include <vgui_controls/MenuItem.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBackgroundMenuButton::CBackgroundMenuButton(vgui::Panel *parent, const char *name) : BaseClass(parent, name, "") +{ + m_pImage = NULL; + m_pMouseOverImage = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBackgroundMenuButton::~CBackgroundMenuButton() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackgroundMenuButton::OnCommand(const char *command) +{ + BaseClass::OnCommand(command); +} + +//----------------------------------------------------------------------------- +// Purpose: Makes the button transparent +//----------------------------------------------------------------------------- +void CBackgroundMenuButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + // hack some colors in + SetFgColor(Color(255, 255, 255, 255)); + SetBgColor(Color(0, 0, 0, 0)); + SetDefaultColor(Color(255, 255, 255, 255), Color(0, 0, 0, 0)); + SetArmedColor(Color(255, 255, 0, 255), Color(0, 0, 0, 0)); + SetDepressedColor(Color(255, 255, 0, 255), Color(0, 0, 0, 0)); + SetContentAlignment(Label::a_west); + SetBorder(NULL); + SetDefaultBorder(NULL); + SetDepressedBorder(NULL); + SetKeyFocusBorder(NULL); + SetTextInset(0, 0); + SetAlpha(0); + + /* + // sounds disabled for this button (since it's so big now) + SetArmedSound("UI/buttonrollover.wav"); + SetDepressedSound("UI/buttonclick.wav"); + SetReleasedSound("UI/buttonclickrelease.wav"); + */ +} diff --git a/gameui/BackgroundMenuButton.h b/gameui/BackgroundMenuButton.h new file mode 100644 index 0000000..c4886a2 --- /dev/null +++ b/gameui/BackgroundMenuButton.h @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BACKGROUNDMENUBUTTON_H +#define BACKGROUNDMENUBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Button.h> + +//----------------------------------------------------------------------------- +// Purpose: Baseclass for the left and right ingame menus that lay on the background +//----------------------------------------------------------------------------- +class CBackgroundMenuButton : public vgui::Button +{ +public: + CBackgroundMenuButton(vgui::Panel *parent, const char *name); + ~CBackgroundMenuButton(); + + virtual void OnCommand(const char *command); + +protected: + vgui::Menu *RecursiveLoadGameMenu(KeyValues *datafile); + vgui::Menu *m_pMenu; + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + vgui::IImage *m_pImage, *m_pMouseOverImage; + typedef vgui::Button BaseClass; +}; + + +#endif // BACKGROUNDMENUBUTTON_H
\ No newline at end of file diff --git a/gameui/BasePanel.cpp b/gameui/BasePanel.cpp new file mode 100644 index 0000000..eaabd91 --- /dev/null +++ b/gameui/BasePanel.cpp @@ -0,0 +1,4832 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include <stdio.h> + +#include "threadtools.h" + +#include "BasePanel.h" +#include "EngineInterface.h" +#include "VGuiSystemModuleLoader.h" + +#include "vgui/IInput.h" +#include "vgui/ILocalize.h" +#include "vgui/IPanel.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" +#include "filesystem.h" +#include "GameConsole.h" +#include "GameUI_Interface.h" +#include "vgui_controls/PropertyDialog.h" +#include "vgui_controls/PropertySheet.h" +#include "materialsystem/materialsystem_config.h" +#include "materialsystem/imaterialsystem.h" +#include "sourcevr/isourcevirtualreality.h" + +using namespace vgui; + +#include "GameConsole.h" +#include "ModInfo.h" + +#include "IGameUIFuncs.h" +#include "LoadingDialog.h" +#include "BackgroundMenuButton.h" +#include "vgui_controls/AnimationController.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuItem.h" +#include "vgui_controls/PHandle.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/QueryBox.h" +#include "vgui_controls/ControllerMap.h" +#include "vgui_controls/KeyRepeat.h" +#include "tier0/icommandline.h" +#include "tier1/convar.h" +#include "NewGameDialog.h" +#include "BonusMapsDialog.h" +#include "LoadGameDialog.h" +#include "SaveGameDialog.h" +#include "OptionsDialog.h" +#include "CreateMultiplayerGameDialog.h" +#include "ChangeGameDialog.h" +#include "BackgroundMenuButton.h" +#include "PlayerListDialog.h" +#include "BenchmarkDialog.h" +#include "LoadCommentaryDialog.h" +#include "ControllerDialog.h" +#include "BonusMapsDatabase.h" +#include "engine/IEngineSound.h" +#include "bitbuf.h" +#include "tier1/fmtstr.h" +#include "inputsystem/iinputsystem.h" +#include "ixboxsystem.h" +#include "matchmaking/matchmakingbasepanel.h" +#include "matchmaking/achievementsdialog.h" +#include "iachievementmgr.h" +#include "UtlSortVector.h" + +#include "game/client/IGameClientExports.h" + +#include "OptionsSubAudio.h" +#include "hl2orange.spa.h" +#include "CustomTabExplanationDialog.h" +#if defined( _X360 ) +#include "xbox/xbox_launch.h" +#else +#include "xbox/xboxstubs.h" +#endif + +#include "../engine/imatchmaking.h" +#include "tier1/utlstring.h" +#include "steam/steam_api.h" + +#undef MessageBox // Windows helpfully #define's this to MessageBoxA, we're using vgui::MessageBox + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#define MAIN_MENU_INDENT_X360 10 + +ConVar vgui_message_dialog_modal( "vgui_message_dialog_modal", "1", FCVAR_ARCHIVE ); + +extern vgui::DHANDLE<CLoadingDialog> g_hLoadingDialog; +static CBasePanel *g_pBasePanel = NULL; +static float g_flAnimationPadding = 0.01f; + +extern const char *COM_GetModDirectory( void ); + +extern ConVar x360_audio_english; +extern bool bSteamCommunityFriendsVersion; + +static vgui::DHANDLE<vgui::PropertyDialog> g_hOptionsDialog; + +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +CBasePanel *BasePanel() +{ + return g_pBasePanel; +} + +//----------------------------------------------------------------------------- +// Purpose: hack function to give the module loader access to the main panel handle +// only used in VguiSystemModuleLoader +//----------------------------------------------------------------------------- +VPANEL GetGameUIBasePanel() +{ + return BasePanel()->GetVPanel(); +} + +CGameMenuItem::CGameMenuItem(vgui::Menu *parent, const char *name) : BaseClass(parent, name, "GameMenuItem") +{ + m_bRightAligned = false; +} + +void CGameMenuItem::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + // make fully transparent + SetFgColor(GetSchemeColor("MainMenu.TextColor", pScheme)); + SetBgColor(Color(0, 0, 0, 0)); + SetDefaultColor(GetSchemeColor("MainMenu.TextColor", pScheme), Color(0, 0, 0, 0)); + SetArmedColor(GetSchemeColor("MainMenu.ArmedTextColor", pScheme), Color(0, 0, 0, 0)); + SetDepressedColor(GetSchemeColor("MainMenu.DepressedTextColor", pScheme), Color(0, 0, 0, 0)); + SetContentAlignment(Label::a_west); + SetBorder(NULL); + SetDefaultBorder(NULL); + SetDepressedBorder(NULL); + SetKeyFocusBorder(NULL); + + vgui::HFont hMainMenuFont = pScheme->GetFont( "MainMenuFont", IsProportional() ); + + if ( hMainMenuFont ) + { + SetFont( hMainMenuFont ); + } + else + { + SetFont( pScheme->GetFont( "MenuLarge", IsProportional() ) ); + } + SetTextInset(0, 0); + SetArmedSound("UI/buttonrollover.wav"); + SetDepressedSound("UI/buttonclick.wav"); + SetReleasedSound("UI/buttonclickrelease.wav"); + SetButtonActivationType(Button::ACTIVATE_ONPRESSED); + + if ( GameUI().IsConsoleUI() ) + { + SetArmedColor(GetSchemeColor("MainMenu.ArmedTextColor", pScheme), GetSchemeColor("Button.ArmedBgColor", pScheme)); + SetTextInset( MAIN_MENU_INDENT_X360, 0 ); + } + + if (m_bRightAligned) + { + SetContentAlignment(Label::a_east); + } +} + +void CGameMenuItem::PaintBackground() +{ + if ( !GameUI().IsConsoleUI() ) + { + BaseClass::PaintBackground(); + } + else + { + if ( !IsArmed() || !IsVisible() || GetParent()->GetAlpha() < 32 ) + return; + + int wide, tall; + GetSize( wide, tall ); + + DrawBoxFade( 0, 0, wide, tall, GetButtonBgColor(), 1.0f, 255, 0, true ); + DrawBoxFade( 2, 2, wide - 4, tall - 4, Color( 0, 0, 0, 96 ), 1.0f, 255, 0, true ); + } +} + +void CGameMenuItem::SetRightAlignedText(bool state) +{ + m_bRightAligned = state; +} + +//----------------------------------------------------------------------------- +// Purpose: General purpose 1 of N menu +//----------------------------------------------------------------------------- +class CGameMenu : public vgui::Menu +{ +public: + DECLARE_CLASS_SIMPLE( CGameMenu, vgui::Menu ); + + CGameMenu(vgui::Panel *parent, const char *name) : BaseClass(parent, name) + { + if ( GameUI().IsConsoleUI() ) + { + // shows graphic button hints + m_pConsoleFooter = new CFooterPanel( parent, "MainMenuFooter" ); + + int iFixedWidth = 245; + +#ifdef _X360 + // In low def we need a smaller highlight + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + if ( !videoMode.fIsHiDef ) + { + iFixedWidth = 240; + } + else + { + iFixedWidth = 350; + } +#endif + + SetFixedWidth( iFixedWidth ); + } + else + { + m_pConsoleFooter = NULL; + } + + m_hMainMenuOverridePanel = NULL; + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + BaseClass::ApplySchemeSettings(pScheme); + + // make fully transparent + SetMenuItemHeight(atoi(pScheme->GetResourceString("MainMenu.MenuItemHeight"))); + SetBgColor(Color(0, 0, 0, 0)); + SetBorder(NULL); + } + + virtual void LayoutMenuBorder() + { + } + + void SetMainMenuOverride( vgui::VPANEL panel ) + { + m_hMainMenuOverridePanel = panel; + + if ( m_hMainMenuOverridePanel ) + { + // We've got an override panel. Nuke all our menu items. + DeleteAllItems(); + } + } + + virtual void SetVisible(bool state) + { + if ( m_hMainMenuOverridePanel ) + { + // force to be always visible + ipanel()->SetVisible( m_hMainMenuOverridePanel, true ); + + // move us to the back instead of going invisible + if ( !state ) + { + ipanel()->MoveToBack(m_hMainMenuOverridePanel); + } + } + + // force to be always visible + BaseClass::SetVisible(true); + + // move us to the back instead of going invisible + if (!state) + { + ipanel()->MoveToBack(GetVPanel()); + } + } + + virtual int AddMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, KeyValues *userData = NULL) + { + MenuItem *item = new CGameMenuItem(this, itemName); + item->AddActionSignalTarget(target); + item->SetCommand(command); + item->SetText(itemText); + item->SetUserData(userData); + return BaseClass::AddMenuItem(item); + } + + virtual int AddMenuItem(const char *itemName, const char *itemText, KeyValues *command, Panel *target, KeyValues *userData = NULL) + { + CGameMenuItem *item = new CGameMenuItem(this, itemName); + item->AddActionSignalTarget(target); + item->SetCommand(command); + item->SetText(itemText); + item->SetRightAlignedText(true); + item->SetUserData(userData); + return BaseClass::AddMenuItem(item); + } + + virtual void SetMenuItemBlinkingState( const char *itemName, bool state ) + { + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + MenuItem *menuItem = dynamic_cast<MenuItem *>(child); + if (menuItem) + { + if ( Q_strcmp( menuItem->GetCommand()->GetString("command", ""), itemName ) == 0 ) + { + menuItem->SetBlink( state ); + } + } + } + InvalidateLayout(); + } + + virtual void OnSetFocus() + { + if ( m_hMainMenuOverridePanel ) + { + Panel *pMainMenu = ipanel()->GetPanel( m_hMainMenuOverridePanel, "ClientDLL" ); + if ( pMainMenu ) + { + pMainMenu->PerformLayout(); + } + } + + BaseClass::OnSetFocus(); + } + + virtual void OnCommand(const char *command) + { + m_KeyRepeat.Reset(); + + + if (!stricmp(command, "Open")) + { + if ( m_hMainMenuOverridePanel ) + { + // force to be always visible + ipanel()->MoveToFront( m_hMainMenuOverridePanel ); + ipanel()->RequestFocus( m_hMainMenuOverridePanel ); + } + else + { + MoveToFront(); + RequestFocus(); + } + } + else + { + BaseClass::OnCommand(command); + } + } + + virtual void OnKeyCodePressed( KeyCode code ) + { + if ( IsX360() ) + { + if ( GetAlpha() != 255 ) + { + SetEnabled( false ); + // inhibit key activity during transitions + return; + } + + SetEnabled( true ); + + if ( code == KEY_XBUTTON_B || code == KEY_XBUTTON_START ) + { + if ( GameUI().IsInLevel() ) + { + GetParent()->OnCommand( "ResumeGame" ); + } + return; + } + } + + m_KeyRepeat.KeyDown( code ); + + int nDir = 0; + + switch ( code ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + case STEAMCONTROLLER_DPAD_UP: + nDir = -1; + break; + + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + nDir = 1; + break; + } + + if ( nDir != 0 ) + { + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons; + VguiPanelGetSortedChildButtonList( this, (void*)&vecSortedButtons ); + + if ( VguiPanelNavigateSortedChildButtonList( (void*)&vecSortedButtons, nDir ) != -1 ) + { + // Handled! + return; + } + } + else if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > vecSortedButtons; + VguiPanelGetSortedChildButtonList( this, (void*)&vecSortedButtons ); + + for ( int i = 0; i < vecSortedButtons.Count(); i++ ) + { + if ( vecSortedButtons[ i ].pButton->IsArmed() ) + { + vecSortedButtons[ i ].pButton->DoClick(); + return; + } + } + } + + BaseClass::OnKeyCodePressed( code ); + + // HACK: Allow F key bindings to operate even here + if ( IsPC() && code >= KEY_F1 && code <= KEY_F12 ) + { + // See if there is a binding for the FKey + const char *binding = gameuifuncs->GetBindingForButtonCode( code ); + if ( binding && binding[0] ) + { + // submit the entry as a console commmand + char szCommand[256]; + Q_strncpy( szCommand, binding, sizeof( szCommand ) ); + engine->ClientCmd_Unrestricted( szCommand ); + } + } + } + + void OnKeyCodeReleased( vgui::KeyCode code ) + { + m_KeyRepeat.KeyUp( code ); + + BaseClass::OnKeyCodeReleased( code ); + } + + void OnThink() + { + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + OnKeyCodeTyped( code ); + } + + BaseClass::OnThink(); + } + + virtual void OnKillFocus() + { + BaseClass::OnKillFocus(); + + if ( m_hMainMenuOverridePanel ) + { + // force us to the rear when we lose focus (so it looks like the menu is always on the background) + surface()->MovePopupToBack( m_hMainMenuOverridePanel ); + } + else + { + // force us to the rear when we lose focus (so it looks like the menu is always on the background) + surface()->MovePopupToBack(GetVPanel()); + } + + m_KeyRepeat.Reset(); + } + + void ShowFooter( bool bShow ) + { + if ( m_pConsoleFooter ) + { + m_pConsoleFooter->SetVisible( bShow ); + } + } + + void UpdateMenuItemState( bool isInGame, bool isMultiplayer, bool isInReplay, bool isVREnabled, bool isVRActive ) + { + bool isSteam = IsPC() && ( CommandLine()->FindParm("-steam") != 0 ); + bool bIsConsoleUI = GameUI().IsConsoleUI(); + + // disabled save button if we're not in a game + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + MenuItem *menuItem = dynamic_cast<MenuItem *>(child); + if (menuItem) + { + bool shouldBeVisible = true; + // filter the visibility + KeyValues *kv = menuItem->GetUserData(); + if (!kv) + continue; + + if (!isInGame && kv->GetInt("OnlyInGame") ) + { + shouldBeVisible = false; + } + if (!isInReplay && kv->GetInt("OnlyInReplay") ) + { + shouldBeVisible = false; + } + else if (!isVREnabled && kv->GetInt("OnlyWhenVREnabled") ) + { + shouldBeVisible = false; + } + else if ( ( !isVRActive || ShouldForceVRActive() ) && kv->GetInt( "OnlyWhenVRActive" ) ) + { + shouldBeVisible = false; + } + else if (isVRActive && kv->GetInt("OnlyWhenVRInactive") ) + { + shouldBeVisible = false; + } + else if (isMultiplayer && kv->GetInt("notmulti")) + { + shouldBeVisible = false; + } + else if (isInGame && !isMultiplayer && kv->GetInt("notsingle")) + { + shouldBeVisible = false; + } + else if (isSteam && kv->GetInt("notsteam")) + { + shouldBeVisible = false; + } + else if ( !bIsConsoleUI && kv->GetInt( "ConsoleOnly" ) ) + { + shouldBeVisible = false; + } + + // If we're playing back a replay, hide everything else + if ( isInReplay && !kv->GetInt("OnlyInReplay") ) + { + shouldBeVisible = false; + } + + menuItem->SetVisible( shouldBeVisible ); + } + } + + if ( !isInGame ) + { + // Sort them into their original order + for ( int j = 0; j < GetChildCount() - 2; j++ ) + { + MoveMenuItem( j, j + 1 ); + } + } + else + { + // Sort them into their in game order + for ( int i = 0; i < GetChildCount(); i++ ) + { + for ( int j = i; j < GetChildCount() - 2; j++ ) + { + int iID1 = GetMenuID( j ); + int iID2 = GetMenuID( j + 1 ); + + MenuItem *menuItem1 = GetMenuItem( iID1 ); + MenuItem *menuItem2 = GetMenuItem( iID2 ); + + KeyValues *kv1 = menuItem1->GetUserData(); + KeyValues *kv2 = menuItem2->GetUserData(); + + if ( kv1->GetInt("InGameOrder") > kv2->GetInt("InGameOrder") ) + MoveMenuItem( iID2, iID1 ); + } + } + } + + InvalidateLayout(); + + if ( m_pConsoleFooter ) + { + // update the console footer + const char *pHelpName; + if ( !isInGame ) + pHelpName = "MainMenu"; + else + pHelpName = "GameMenu"; + + if ( !m_pConsoleFooter->GetHelpName() || V_stricmp( pHelpName, m_pConsoleFooter->GetHelpName() ) ) + { + // game menu must re-establish its own help once it becomes re-active + m_pConsoleFooter->SetHelpNameAndReset( pHelpName ); + m_pConsoleFooter->AddNewButtonLabel( "#GameUI_Action", "#GameUI_Icons_A_BUTTON" ); + if ( isInGame ) + { + m_pConsoleFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); + } + } + } + } + + MESSAGE_FUNC_INT( OnCursorEnteredMenuItem, "CursorEnteredMenuItem", VPanel); + +private: + CFooterPanel *m_pConsoleFooter; + vgui::CKeyRepeatHandler m_KeyRepeat; + vgui::VPANEL m_hMainMenuOverridePanel; +}; + +//----------------------------------------------------------------------------- +// Purpose: Respond to cursor entering a menuItem. +//----------------------------------------------------------------------------- +void CGameMenu::OnCursorEnteredMenuItem(int VPanel) +{ + VPANEL menuItem = (VPANEL)VPanel; + MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName())); + KeyValues *pCommand = item->GetCommand(); + if ( !pCommand->GetFirstSubKey() ) + return; + const char *pszCmd = pCommand->GetFirstSubKey()->GetString(); + if ( !pszCmd || !pszCmd[0] ) + return; + + BaseClass::OnCursorEnteredMenuItem( VPanel ); +} + +static CBackgroundMenuButton* CreateMenuButton( CBasePanel *parent, const char *panelName, const wchar_t *panelText ) +{ + CBackgroundMenuButton *pButton = new CBackgroundMenuButton( parent, panelName ); + pButton->SetProportional(true); + pButton->SetCommand("OpenGameMenu"); + pButton->SetText(panelText); + + return pButton; +} + +bool g_bIsCreatingNewGameMenuForPreFetching = false; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBasePanel::CBasePanel() : Panel(NULL, "BaseGameUIPanel") +{ + g_pBasePanel = this; + m_bLevelLoading = false; + m_eBackgroundState = BACKGROUND_INITIAL; + m_flTransitionStartTime = 0.0f; + m_flTransitionEndTime = 0.0f; + m_flFrameFadeInTime = 0.5f; + m_bRenderingBackgroundTransition = false; + m_bFadingInMenus = false; + m_bEverActivated = false; + m_iGameMenuInset = 24; + m_bPlatformMenuInitialized = false; + m_bHaveDarkenedBackground = false; + m_bHaveDarkenedTitleText = true; + m_bForceTitleTextUpdate = true; + m_BackdropColor = Color(0, 0, 0, 128); + m_pConsoleAnimationController = NULL; + m_pConsoleControlSettings = NULL; + m_bCopyFrameBuffer = false; + m_bUseRenderTargetImage = false; + m_ExitingFrameCount = 0; + m_bXUIVisible = false; + m_bUseMatchmaking = false; + m_bRestartFromInvite = false; + m_bRestartSameGame = false; + m_bUserRefusedSignIn = false; + m_bUserRefusedStorageDevice = false; + m_bWaitingForUserSignIn = false; + m_bWaitingForStorageDeviceHandle = false; + m_bNeedStorageDeviceHandle = false; + m_bStorageBladeShown = false; + m_iStorageID = XBX_INVALID_STORAGE_ID; + m_pAsyncJob = NULL; + m_pStorageDeviceValidatedNotify = NULL; + + m_iRenderTargetImageID = -1; + m_iBackgroundImageID = -1; + m_iProductImageID = -1; + m_iLoadingImageID = -1; + + if ( GameUI().IsConsoleUI() ) + { + m_pConsoleAnimationController = new AnimationController( this ); + m_pConsoleAnimationController->SetScriptFile( GetVPanel(), "scripts/GameUIAnimations.txt" ); + m_pConsoleAnimationController->SetAutoReloadScript( IsDebug() ); + + m_pConsoleControlSettings = new KeyValues( "XboxDialogs.res" ); + if ( !m_pConsoleControlSettings->LoadFromFile( g_pFullFileSystem, "resource/UI/XboxDialogs.res", "GAME" ) ) + { + Error( "Failed to load UI control settings!\n" ); + } + else + { + m_pConsoleControlSettings->ProcessResolutionKeys( surface()->GetResolutionKey() ); + } + +#ifdef _X360 + x360_audio_english.SetValue( XboxLaunch()->GetForceEnglish() ); +#endif + } + + m_pGameMenuButtons.AddToTail( CreateMenuButton( this, "GameMenuButton", ModInfo().GetGameTitle() ) ); + m_pGameMenuButtons.AddToTail( CreateMenuButton( this, "GameMenuButton2", ModInfo().GetGameTitle2() ) ); +#ifdef CS_BETA + if ( !ModInfo().NoCrosshair() ) // hack to not show the BETA for HL2 or HL1Port + { + m_pGameMenuButtons.AddToTail( CreateMenuButton( this, "BetaButton", L"BETA" ) ); + } +#endif // CS_BETA + + m_pGameMenu = NULL; + m_pGameLogo = NULL; + m_hMainMenuOverridePanel = NULL; + + if ( SteamClient() ) + { + HSteamPipe steamPipe = SteamClient()->CreateSteamPipe(); + ISteamUtils *pUtils = SteamClient()->GetISteamUtils( steamPipe, "SteamUtils002" ); + if ( pUtils ) + { + bSteamCommunityFriendsVersion = true; + } + + SteamClient()->BReleaseSteamPipe( steamPipe ); + } + + CreateGameMenu(); + CreateGameLogo(); + + // Bonus maps menu blinks if something has been unlocked since the player last opened the menu + // This is saved as persistant data, and here is where we check for that + CheckBonusBlinkState(); + + // start the menus fully transparent + SetMenuAlpha( 0 ); + + if ( GameUI().IsConsoleUI() ) + { + // do any costly resource prefetching now.... + // force the new dialog to get all of its chapter pics + g_bIsCreatingNewGameMenuForPreFetching = true; + m_hNewGameDialog = new CNewGameDialog( this, false ); + m_hNewGameDialog->MarkForDeletion(); + g_bIsCreatingNewGameMenuForPreFetching = false; + + m_hOptionsDialog_Xbox = new COptionsDialogXbox( this ); + m_hOptionsDialog_Xbox->MarkForDeletion(); + + m_hControllerDialog = new CControllerDialog( this ); + m_hControllerDialog->MarkForDeletion(); + + ArmFirstMenuItem(); + m_pConsoleAnimationController->StartAnimationSequence( "InitializeUILayout" ); + } + + // Record data used for rich presence updates + if ( IsX360() ) + { + // Get our active mod directory name + const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );; + + // Set the game we're playing + m_iGameID = CONTEXT_GAME_GAME_HALF_LIFE_2; + m_bSinglePlayer = true; + if ( Q_stristr( pGameName, "episodic" ) ) + { + m_iGameID = CONTEXT_GAME_GAME_EPISODE_ONE; + } + else if ( Q_stristr( pGameName, "ep2" ) ) + { + m_iGameID = CONTEXT_GAME_GAME_EPISODE_TWO; + } + else if ( Q_stristr( pGameName, "portal" ) ) + { + m_iGameID = CONTEXT_GAME_GAME_PORTAL; + } + else if ( Q_stristr( pGameName, "tf" ) ) + { + m_iGameID = CONTEXT_GAME_GAME_TEAM_FORTRESS; + m_bSinglePlayer = false; + } + + } +} + +//----------------------------------------------------------------------------- +// Purpose: Xbox 360 - Get the console UI keyvalues to pass to LoadControlSettings() +//----------------------------------------------------------------------------- +KeyValues *CBasePanel::GetConsoleControlSettings( void ) +{ + return m_pConsoleControlSettings; +} + +//----------------------------------------------------------------------------- +// Purpose: Causes the first menu item to be armed +//----------------------------------------------------------------------------- +void CBasePanel::ArmFirstMenuItem( void ) +{ + UpdateGameMenus(); + + // Arm the first item in the menu + for ( int i = 0; i < m_pGameMenu->GetItemCount(); ++i ) + { + if ( m_pGameMenu->GetMenuItem( i )->IsVisible() ) + { + m_pGameMenu->SetCurrentlyHighlightedItem( i ); + break; + } + } +} + +CBasePanel::~CBasePanel() +{ + g_pBasePanel = NULL; + + if ( vgui::surface() ) + { + if ( m_iRenderTargetImageID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iRenderTargetImageID ); + m_iRenderTargetImageID = -1; + } + + if ( m_iBackgroundImageID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iBackgroundImageID ); + m_iBackgroundImageID = -1; + } + + if ( m_iProductImageID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iProductImageID ); + m_iProductImageID = -1; + } + + if ( m_iLoadingImageID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iLoadingImageID ); + m_iLoadingImageID = -1; + } + } +} + +static const char *g_rgValidCommands[] = +{ + "OpenGameMenu", + "OpenPlayerListDialog", + "OpenNewGameDialog", + "OpenLoadGameDialog", + "OpenSaveGameDialog", + "OpenCustomMapsDialog", + "OpenOptionsDialog", + "OpenBenchmarkDialog", + "OpenServerBrowser", + "OpenFriendsDialog", + "OpenLoadDemoDialog", + "OpenCreateMultiplayerGameDialog", + "OpenChangeGameDialog", + "OpenLoadCommentaryDialog", + "Quit", + "QuitNoConfirm", + "ResumeGame", + "Disconnect", +}; + +static void CC_GameMenuCommand( const CCommand &args ) +{ + int c = args.ArgC(); + if ( c < 2 ) + { + Msg( "Usage: gamemenucommand <commandname>\n" ); + return; + } + + if ( !g_pBasePanel ) + { + return; + } + + vgui::ivgui()->PostMessage( g_pBasePanel->GetVPanel(), new KeyValues("Command", "command", args[1] ), NULL); +} + +static int CC_GameMenuCompletionFunc( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) +{ + char const *cmdname = "gamemenucommand"; + + char *substring = (char *)partial; + if ( Q_strstr( partial, cmdname ) ) + { + substring = (char *)partial + strlen( cmdname ) + 1; + } + + int checklen = Q_strlen( substring ); + + CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc ); + + int i; + int c = ARRAYSIZE( g_rgValidCommands ); + for ( i = 0; i < c; ++i ) + { + if ( Q_strnicmp( g_rgValidCommands[ i ], substring, checklen ) ) + continue; + + CUtlString str; + str = g_rgValidCommands[ i ]; + + symbols.Insert( str ); + + // Too many + if ( symbols.Count() >= COMMAND_COMPLETION_MAXITEMS ) + break; + } + + // Now fill in the results + int slot = 0; + for ( i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder( i ) ) + { + char const *name = symbols[ i ].String(); + + char buf[ 512 ]; + Q_strncpy( buf, name, sizeof( buf ) ); + Q_strlower( buf ); + + Q_snprintf( commands[ slot++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", + cmdname, buf ); + } + + return slot; +} + +static ConCommand gamemenucommand( "gamemenucommand", CC_GameMenuCommand, "Issue game menu command.", 0, CC_GameMenuCompletionFunc ); + +//----------------------------------------------------------------------------- +// Purpose: paints the main background image +//----------------------------------------------------------------------------- +void CBasePanel::PaintBackground() +{ + if ( !GameUI().IsInLevel() || g_hLoadingDialog.Get() || m_ExitingFrameCount ) + { + // not in the game or loading dialog active or exiting, draw the ui background + DrawBackgroundImage(); + } + else if ( IsX360() ) + { + // only valid during loading from level to level + m_bUseRenderTargetImage = false; + } + + if ( m_flBackgroundFillAlpha ) + { + int swide, stall; + surface()->GetScreenSize(swide, stall); + surface()->DrawSetColor(0, 0, 0, m_flBackgroundFillAlpha); + surface()->DrawFilledRect(0, 0, swide, stall); + } +} + +//----------------------------------------------------------------------------- +// Updates which background state we should be in. +// +// NOTE: These states change at funny times and overlap. They CANNOT be +// used to demarcate exact transitions. +//----------------------------------------------------------------------------- +void CBasePanel::UpdateBackgroundState() +{ + if ( m_ExitingFrameCount ) + { + // trumps all, an exiting state must own the screen image + // cannot be stopped + SetBackgroundRenderState( BACKGROUND_EXITING ); + } + else if ( GameUI().IsInLevel() ) + { + SetBackgroundRenderState( BACKGROUND_LEVEL ); + } + else if ( GameUI().IsInBackgroundLevel() && !m_bLevelLoading ) + { + // 360 guarantees a progress bar + // level loading is truly completed when the progress bar is gone, then transition to main menu + if ( IsPC() || ( IsX360() && !g_hLoadingDialog.Get() ) ) + { + SetBackgroundRenderState( BACKGROUND_MAINMENU ); + } + } + else if ( m_bLevelLoading ) + { + SetBackgroundRenderState( BACKGROUND_LOADING ); + } + else if ( m_bEverActivated && m_bPlatformMenuInitialized ) + { + SetBackgroundRenderState( BACKGROUND_DISCONNECTED ); + } + + if ( GameUI().IsConsoleUI() ) + { + if ( !m_ExitingFrameCount && !m_bLevelLoading && !g_hLoadingDialog.Get() && GameUI().IsInLevel() ) + { + // paused + if ( m_flBackgroundFillAlpha == 0.0f ) + m_flBackgroundFillAlpha = 120.0f; + } + else + { + m_flBackgroundFillAlpha = 0; + } + + // console ui has completely different menu/dialog/fill/fading behavior + return; + } + + // don't evaluate the rest until we've initialized the menus + if ( !m_bPlatformMenuInitialized ) + return; + + // check for background fill + // fill over the top if we have any dialogs up + int i; + bool bHaveActiveDialogs = false; + bool bIsInLevel = GameUI().IsInLevel(); + for ( i = 0; i < GetChildCount(); ++i ) + { + VPANEL child = ipanel()->GetChild( GetVPanel(), i ); + if ( child + && ipanel()->IsVisible( child ) + && ipanel()->IsPopup( child ) + && child != m_pGameMenu->GetVPanel() ) + { + bHaveActiveDialogs = true; + } + } + // see if the base gameui panel has dialogs hanging off it (engine stuff, console, bug reporter) + VPANEL parent = GetVParent(); + for ( i = 0; i < ipanel()->GetChildCount( parent ); ++i ) + { + VPANEL child = ipanel()->GetChild( parent, i ); + if ( child + && ipanel()->IsVisible( child ) + && ipanel()->IsPopup( child ) + && child != GetVPanel() ) + { + bHaveActiveDialogs = true; + } + } + + // check to see if we need to fade in the background fill + bool bNeedDarkenedBackground = (bHaveActiveDialogs || bIsInLevel); + if ( m_bHaveDarkenedBackground != bNeedDarkenedBackground ) + { + // fade in faster than we fade out + float targetAlpha, duration; + if ( bNeedDarkenedBackground ) + { + // fade in background tint + targetAlpha = m_BackdropColor[3]; + duration = m_flFrameFadeInTime; + } + else + { + // fade out background tint + targetAlpha = 0.0f; + duration = 2.0f; + } + + m_bHaveDarkenedBackground = bNeedDarkenedBackground; + vgui::GetAnimationController()->RunAnimationCommand( this, "m_flBackgroundFillAlpha", targetAlpha, 0.0f, duration, AnimationController::INTERPOLATOR_LINEAR ); + } + + // check to see if the game title should be dimmed + // don't transition on level change + if ( m_bLevelLoading ) + return; + + bool bNeedDarkenedTitleText = bHaveActiveDialogs; + if (m_bHaveDarkenedTitleText != bNeedDarkenedTitleText || m_bForceTitleTextUpdate) + { + float targetTitleAlpha, duration; + if (bHaveActiveDialogs) + { + // fade out title text + duration = m_flFrameFadeInTime; + targetTitleAlpha = 32.0f; + } + else + { + // fade in title text + duration = 2.0f; + targetTitleAlpha = 255.0f; + } + + if ( m_pGameLogo ) + { + vgui::GetAnimationController()->RunAnimationCommand( m_pGameLogo, "alpha", targetTitleAlpha, 0.0f, duration, AnimationController::INTERPOLATOR_LINEAR ); + } + + // Msg( "animating title (%d => %d at time %.2f)\n", m_pGameMenuButton->GetAlpha(), (int)targetTitleAlpha, engine->Time()); + for ( i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + vgui::GetAnimationController()->RunAnimationCommand( m_pGameMenuButtons[i], "alpha", targetTitleAlpha, 0.0f, duration, AnimationController::INTERPOLATOR_LINEAR ); + } + m_bHaveDarkenedTitleText = bNeedDarkenedTitleText; + m_bForceTitleTextUpdate = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets how the game background should render +//----------------------------------------------------------------------------- +void CBasePanel::SetBackgroundRenderState(EBackgroundState state) +{ + if ( state == m_eBackgroundState ) + { + return; + } + + // apply state change transition + float frametime = engine->Time(); + + if ( m_eBackgroundState == BACKGROUND_INITIAL && ( state == BACKGROUND_DISCONNECTED || state == BACKGROUND_MAINMENU ) ) + { + ConVar* dev_loadtime_mainmenu = cvar->FindVar( "dev_loadtime_mainmenu" ); + if (dev_loadtime_mainmenu) { + dev_loadtime_mainmenu->SetValue( frametime ); + } + } + + + m_bRenderingBackgroundTransition = false; + m_bFadingInMenus = false; + + CMatchmakingBasePanel *pPanel = GetMatchmakingBasePanel(); + + if ( pPanel ) + { + if ( state == BACKGROUND_LOADING ) + { + pPanel->SetVisible( false ); + } + else + { + pPanel->SetVisible( true ); + } + } + + if ( state == BACKGROUND_EXITING ) + { + // hide the menus + m_bCopyFrameBuffer = false; + } + else if ( state == BACKGROUND_DISCONNECTED || state == BACKGROUND_MAINMENU ) + { + // menu fading + // make the menus visible + m_bFadingInMenus = true; + m_flFadeMenuStartTime = frametime; + m_flFadeMenuEndTime = frametime + 3.0f; + + if ( state == BACKGROUND_MAINMENU ) + { + // fade background into main menu + m_bRenderingBackgroundTransition = true; + m_flTransitionStartTime = frametime; + m_flTransitionEndTime = frametime + 3.0f; + } + } + else if ( state == BACKGROUND_LOADING ) + { + if ( GameUI().IsConsoleUI() ) + { + RunAnimationWithCallback( this, "InstantHideMainMenu", new KeyValues( "LoadMap" ) ); + } + + // hide the menus + SetMenuAlpha( 0 ); + } + else if ( state == BACKGROUND_LEVEL ) + { + // show the menus + SetMenuAlpha( 255 ); + } + + m_eBackgroundState = state; +} + +void CBasePanel::StartExitingProcess() +{ + // must let a non trivial number of screen swaps occur to stabilize image + // ui runs in a constrained state, while shutdown is occurring + m_flTransitionStartTime = engine->Time(); + m_flTransitionEndTime = m_flTransitionStartTime + 0.5f; + m_ExitingFrameCount = 30; + g_pInputSystem->DetachFromWindow(); + + CMatchmakingBasePanel *pPanel = GetMatchmakingBasePanel(); + if ( pPanel ) + { + pPanel->CloseAllDialogs( false ); + } + + engine->StartXboxExitingProcess(); +} + +//----------------------------------------------------------------------------- +// Purpose: Size should only change on first vgui frame after startup +//----------------------------------------------------------------------------- +void CBasePanel::OnSizeChanged( int newWide, int newTall ) +{ + // Recenter message dialogs + m_MessageDialogHandler.PositionDialogs( newWide, newTall ); + + if ( m_hMatchmakingBasePanel.Get() ) + { + m_hMatchmakingBasePanel->SetBounds( 0, 0, newWide, newTall ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: notifications +//----------------------------------------------------------------------------- +void CBasePanel::OnLevelLoadingStarted() +{ + m_bLevelLoading = true; + + m_pGameMenu->ShowFooter( false ); + + if ( m_hMatchmakingBasePanel.Get() ) + { + m_hMatchmakingBasePanel->OnCommand( "LevelLoadingStarted" ); + } + + if ( IsX360() && m_eBackgroundState == BACKGROUND_LEVEL ) + { + // already in a level going to another level + // frame buffer is about to be cleared, copy it off for ui backing purposes + m_bCopyFrameBuffer = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: notification +//----------------------------------------------------------------------------- +void CBasePanel::OnLevelLoadingFinished() +{ + m_bLevelLoading = false; + + if ( m_hMatchmakingBasePanel.Get() ) + { + m_hMatchmakingBasePanel->OnCommand( "LevelLoadingFinished" ); + } +} + +//----------------------------------------------------------------------------- +// Draws the background image. +//----------------------------------------------------------------------------- +void CBasePanel::DrawBackgroundImage() +{ + if ( IsX360() && m_bCopyFrameBuffer ) + { + // force the engine to do an image capture ONCE into this image's render target + char filename[MAX_PATH]; + surface()->DrawGetTextureFile( m_iRenderTargetImageID, filename, sizeof( filename ) ); + engine->CopyFrameBufferToMaterial( filename ); + m_bCopyFrameBuffer = false; + m_bUseRenderTargetImage = true; + } + + int wide, tall; + GetSize( wide, tall ); + + float frametime = engine->Time(); + + // a background transition has a running map underneath it, so fade image out + // otherwise, there is no map and the background image stays opaque + int alpha = 255; + if ( m_bRenderingBackgroundTransition ) + { + // goes from [255..0] + alpha = (m_flTransitionEndTime - frametime) / (m_flTransitionEndTime - m_flTransitionStartTime) * 255; + alpha = clamp( alpha, 0, 255 ); + } + + // an exiting process needs to opaquely cover everything + if ( m_ExitingFrameCount ) + { + // goes from [0..255] + alpha = (m_flTransitionEndTime - frametime) / (m_flTransitionEndTime - m_flTransitionStartTime) * 255; + alpha = 255 - clamp( alpha, 0, 255 ); + } + + int iImageID = m_iBackgroundImageID; + if ( IsX360() ) + { + if ( m_ExitingFrameCount ) + { + if ( !m_bRestartSameGame ) + { + iImageID = m_iProductImageID; + } + } + else if ( m_bUseRenderTargetImage ) + { + // the render target image must be opaque, the alpha channel contents are unknown + // it is strictly an opaque background image and never used as an overlay + iImageID = m_iRenderTargetImageID; + alpha = 255; + } + } + + surface()->DrawSetColor( 255, 255, 255, alpha ); + surface()->DrawSetTexture( iImageID ); + surface()->DrawTexturedRect( 0, 0, wide, tall ); + + if ( IsX360() && m_ExitingFrameCount ) + { + // Make invisible when going back to appchooser + m_pGameMenu->CGameMenu::BaseClass::SetVisible( false ); + + IScheme *pScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "SourceScheme" ) ); + HFont hFont = pScheme->GetFont( "ChapterTitle" ); + wchar_t *pString = g_pVGuiLocalize->Find( "#GameUI_Loading" ); + int textWide, textTall; + surface()->GetTextSize( hFont, pString, textWide, textTall ); + surface()->DrawSetTextPos( ( wide - textWide )/2, tall * 0.50f ); + surface()->DrawSetTextFont( hFont ); + surface()->DrawSetTextColor( 255, 255, 255, alpha ); + surface()->DrawPrintText( pString, wcslen( pString ) ); + } + + // 360 always use the progress bar, TCR Requirement, and never this loading plaque + if ( IsPC() && ( m_bRenderingBackgroundTransition || m_eBackgroundState == BACKGROUND_LOADING ) ) + { + // draw the loading image over the top + surface()->DrawSetColor(255, 255, 255, alpha); + surface()->DrawSetTexture(m_iLoadingImageID); + int twide, ttall; + surface()->DrawGetTextureSize(m_iLoadingImageID, twide, ttall); + surface()->DrawTexturedRect(wide - twide, tall - ttall, wide, tall); + } + + // update the menu alpha + if ( m_bFadingInMenus ) + { + if ( GameUI().IsConsoleUI() ) + { + m_pConsoleAnimationController->StartAnimationSequence( "OpenMainMenu" ); + m_bFadingInMenus = false; + } + else + { + // goes from [0..255] + alpha = (frametime - m_flFadeMenuStartTime) / (m_flFadeMenuEndTime - m_flFadeMenuStartTime) * 255; + alpha = clamp( alpha, 0, 255 ); + m_pGameMenu->SetAlpha( alpha ); + if ( alpha == 255 ) + { + m_bFadingInMenus = false; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::CreateGameMenu() +{ + // load settings from config file + KeyValues *datafile = new KeyValues("GameMenu"); + datafile->UsesEscapeSequences( true ); // VGUI uses escape sequences + if (datafile->LoadFromFile( g_pFullFileSystem, "Resource/GameMenu.res" ) ) + { + m_pGameMenu = RecursiveLoadGameMenu(datafile); + } + + if ( !m_pGameMenu ) + { + Error( "Could not load file Resource/GameMenu.res" ); + } + else + { + // start invisible + SETUP_PANEL( m_pGameMenu ); + m_pGameMenu->SetAlpha( 0 ); + } + + datafile->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::CreateGameLogo() +{ + if ( ModInfo().UseGameLogo() ) + { + m_pGameLogo = new CMainMenuGameLogo( this, "GameLogo" ); + + if ( m_pGameLogo ) + { + SETUP_PANEL( m_pGameLogo ); + m_pGameLogo->InvalidateLayout( true, true ); + + // start invisible + m_pGameLogo->SetAlpha( 0 ); + } + } + else + { + m_pGameLogo = NULL; + } +} + +void CBasePanel::CheckBonusBlinkState() +{ +#ifdef _X360 + // On 360 if we have a storage device at this point and try to read the bonus data it can't find the bonus file! + return; +#endif + + if ( BonusMapsDatabase()->GetBlink() ) + { + if ( GameUI().IsConsoleUI() ) + SetMenuItemBlinkingState( "OpenNewGameDialog", true ); // Consoles integrate bonus maps menu into the new game menu + else + SetMenuItemBlinkingState( "OpenBonusMapsDialog", true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if menu items need to be enabled/disabled +//----------------------------------------------------------------------------- +void CBasePanel::UpdateGameMenus() +{ + // check our current state + bool isInGame = GameUI().IsInLevel(); + bool isMulti = isInGame && (engine->GetMaxClients() > 1); + bool isInReplay = GameUI().IsInReplay(); + bool isVREnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter == materials->GetCurrentAdapter(); + bool isVRActive = UseVR(); + + // iterate all the menu items + m_pGameMenu->UpdateMenuItemState( isInGame, isMulti, isInReplay, isVREnabled, isVRActive ); + + if ( m_hMainMenuOverridePanel ) + { + vgui::ivgui()->PostMessage( m_hMainMenuOverridePanel, new KeyValues( "UpdateMenu" ), NULL ); + } + + // position the menu + InvalidateLayout(); + m_pGameMenu->SetVisible( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up the game menu from the keyvalues +// the game menu is hierarchial, so this is recursive +//----------------------------------------------------------------------------- +CGameMenu *CBasePanel::RecursiveLoadGameMenu(KeyValues *datafile) +{ + CGameMenu *menu = new CGameMenu(this, datafile->GetName()); + + // loop through all the data adding items to the menu + for (KeyValues *dat = datafile->GetFirstSubKey(); dat != NULL; dat = dat->GetNextKey()) + { + const char *label = dat->GetString("label", "<unknown>"); + const char *cmd = dat->GetString("command", NULL); + const char *name = dat->GetString("name", label); + + if ( cmd && !Q_stricmp( cmd, "OpenFriendsDialog" ) && bSteamCommunityFriendsVersion ) + continue; + + menu->AddMenuItem(name, label, cmd, this, dat); + } + + return menu; +} + +//----------------------------------------------------------------------------- +// Purpose: update the taskbar a frame +//----------------------------------------------------------------------------- +void CBasePanel::RunFrame() +{ + InvalidateLayout(); + vgui::GetAnimationController()->UpdateAnimations( engine->Time() ); + + if ( GameUI().IsConsoleUI() ) + { + // run the console ui animations + m_pConsoleAnimationController->UpdateAnimations( engine->Time() ); + + if ( IsX360() && m_ExitingFrameCount && engine->Time() >= m_flTransitionEndTime ) + { + if ( m_ExitingFrameCount > 1 ) + { + m_ExitingFrameCount--; + if ( m_ExitingFrameCount == 1 ) + { + // enough frames have transpired, send the single shot quit command + // If we kicked off this event from an invite, we need to properly setup the restart to account for that + if ( m_bRestartFromInvite ) + { + engine->ClientCmd_Unrestricted( "quit_x360 invite" ); + } + else if ( m_bRestartSameGame ) + { + engine->ClientCmd_Unrestricted( "quit_x360 restart" ); + } + else + { + // quits to appchooser + engine->ClientCmd_Unrestricted( "quit_x360\n" ); + } + } + } + } + } + + UpdateBackgroundState(); + + if ( !m_bPlatformMenuInitialized ) + { + // check to see if the platform is ready to load yet + if ( IsX360() || g_VModuleLoader.IsPlatformReady() ) + { + m_bPlatformMenuInitialized = true; + } + } + + // Check to see if a pending async task has already finished + if ( m_pAsyncJob && !m_pAsyncJob->m_hThreadHandle ) + { + m_pAsyncJob->Completed(); + delete m_pAsyncJob; + m_pAsyncJob = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tells XBox Live our user is in the current game's menu +//----------------------------------------------------------------------------- +void CBasePanel::UpdateRichPresenceInfo() +{ +#if defined( _X360 ) + // For all other users logged into this console (not primary), set to idle to satisfy cert + for( uint i = 0; i < XUSER_MAX_COUNT; ++i ) + { + XUSER_SIGNIN_STATE State = XUserGetSigninState( i ); + + if( State != eXUserSigninState_NotSignedIn ) + { + if ( i != XBX_GetPrimaryUserId() ) + { + // Set rich presence as 'idle' for users logged in that can't participate in orange box. + if ( !xboxsystem->UserSetContext( i, X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_IDLE, true ) ) + { + Warning( "BasePanel: UserSetContext failed.\n" ); + } + } + } + } + + if ( !GameUI().IsInLevel() ) + { + if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), CONTEXT_GAME, m_iGameID, true ) ) + { + Warning( "BasePanel: UserSetContext failed.\n" ); + } + if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_MENU, true ) ) + { + Warning( "BasePanel: UserSetContext failed.\n" ); + } + if ( m_bSinglePlayer ) + { + if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_SINGLEPLAYER, true ) ) + { + Warning( "BasePanel: UserSetContext failed.\n" ); + } + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Lays out the position of the taskbar +//----------------------------------------------------------------------------- +void CBasePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Get the screen size + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + + // Get the size of the menu + int menuWide, menuTall; + m_pGameMenu->GetSize( menuWide, menuTall ); + + int idealMenuY = m_iGameMenuPos.y; + if ( idealMenuY + menuTall + m_iGameMenuInset > tall ) + { + idealMenuY = tall - menuTall - m_iGameMenuInset; + } + + int yDiff = idealMenuY - m_iGameMenuPos.y; + + for ( int i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + // Get the size of the logo text + //int textWide, textTall; + m_pGameMenuButtons[i]->SizeToContents(); + //vgui::surface()->GetTextSize( m_pGameMenuButtons[i]->GetFont(), ModInfo().GetGameTitle(), textWide, textTall ); + + // place menu buttons above middle of screen + m_pGameMenuButtons[i]->SetPos(m_iGameTitlePos[i].x, m_iGameTitlePos[i].y + yDiff); + //m_pGameMenuButtons[i]->SetSize(textWide + 4, textTall + 4); + } + + if ( m_pGameLogo ) + { + // move the logo to sit right on top of the menu + m_pGameLogo->SetPos( m_iGameMenuPos.x + m_pGameLogo->GetOffsetX(), idealMenuY - m_pGameLogo->GetTall() + m_pGameLogo->GetOffsetY() ); + } + + // position self along middle of screen + if ( GameUI().IsConsoleUI() ) + { + int posx, posy; + m_pGameMenu->GetPos( posx, posy ); + m_iGameMenuPos.x = posx; + } + m_pGameMenu->SetPos(m_iGameMenuPos.x, idealMenuY); + + UpdateGameMenus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Loads scheme information +//----------------------------------------------------------------------------- +void CBasePanel::ApplySchemeSettings(IScheme *pScheme) +{ + int i; + BaseClass::ApplySchemeSettings(pScheme); + + m_iGameMenuInset = atoi(pScheme->GetResourceString("MainMenu.Inset")); + m_iGameMenuInset *= 2; + + IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); + CUtlVector< Color > buttonColor; + if ( pClientScheme ) + { + m_iGameTitlePos.RemoveAll(); + for ( i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetFont(pClientScheme->GetFont("ClientTitleFont", true)); + m_iGameTitlePos.AddToTail( coord() ); + m_iGameTitlePos[i].x = atoi(pClientScheme->GetResourceString( CFmtStr( "Main.Title%d.X", i+1 ) ) ); + m_iGameTitlePos[i].x = scheme()->GetProportionalScaledValue( m_iGameTitlePos[i].x ); + m_iGameTitlePos[i].y = atoi(pClientScheme->GetResourceString( CFmtStr( "Main.Title%d.Y", i+1 ) ) ); + m_iGameTitlePos[i].y = scheme()->GetProportionalScaledValue( m_iGameTitlePos[i].y ); + + if ( GameUI().IsConsoleUI() ) + m_iGameTitlePos[i].x += MAIN_MENU_INDENT_X360; + + buttonColor.AddToTail( pClientScheme->GetColor( CFmtStr( "Main.Title%d.Color", i+1 ), Color(255, 255, 255, 255)) ); + } +#ifdef CS_BETA + if ( !ModInfo().NoCrosshair() ) // hack to not show the BETA for HL2 or HL1Port + { + m_pGameMenuButtons[m_pGameMenuButtons.Count()-1]->SetFont(pClientScheme->GetFont("BetaFont", true)); + } +#endif // CS_BETA + + m_iGameMenuPos.x = atoi(pClientScheme->GetResourceString("Main.Menu.X")); + m_iGameMenuPos.x = scheme()->GetProportionalScaledValue( m_iGameMenuPos.x ); + m_iGameMenuPos.y = atoi(pClientScheme->GetResourceString("Main.Menu.Y")); + m_iGameMenuPos.y = scheme()->GetProportionalScaledValue( m_iGameMenuPos.y ); + + m_iGameMenuInset = atoi(pClientScheme->GetResourceString("Main.BottomBorder")); + m_iGameMenuInset = scheme()->GetProportionalScaledValue( m_iGameMenuInset ); + } + else + { + for ( i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetFont(pScheme->GetFont("TitleFont")); + buttonColor.AddToTail( Color( 255, 255, 255, 255 ) ); + } + } + + for ( i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetDefaultColor(buttonColor[i], Color(0, 0, 0, 0)); + m_pGameMenuButtons[i]->SetArmedColor(buttonColor[i], Color(0, 0, 0, 0)); + m_pGameMenuButtons[i]->SetDepressedColor(buttonColor[i], Color(0, 0, 0, 0)); + } + + m_flFrameFadeInTime = atof(pScheme->GetResourceString("Frame.TransitionEffectTime")); + + // work out current focus - find the topmost panel + SetBgColor(Color(0, 0, 0, 0)); + + m_BackdropColor = pScheme->GetColor("mainmenu.backdrop", Color(0, 0, 0, 128)); + + char filename[MAX_PATH]; + if ( IsX360() ) + { + // 360 uses FullFrameFB1 RT for map to map transitioning + if ( m_iRenderTargetImageID == -1 ) + { + m_iRenderTargetImageID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( m_iRenderTargetImageID, "console/rt_background", false, false ); + } + } + + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall ); + float aspectRatio = (float)screenWide/(float)screenTall; + bool bIsWidescreen = aspectRatio >= 1.5999f; + + // work out which background image to use + if ( IsPC() || !IsX360() ) + { + // pc uses blurry backgrounds based on the background level + char background[MAX_PATH]; + engine->GetMainMenuBackgroundName( background, sizeof(background) ); + Q_snprintf( filename, sizeof( filename ), "console/%s%s", background, ( bIsWidescreen ? "_widescreen" : "" ) ); + } + else + { + // 360 uses hi-res game specific backgrounds + char gameName[MAX_PATH]; + const char *pGameDir = engine->GetGameDirectory(); + V_FileBase( pGameDir, gameName, sizeof( gameName ) ); + V_snprintf( filename, sizeof( filename ), "vgui/appchooser/background_%s%s", gameName, ( bIsWidescreen ? "_widescreen" : "" ) ); + } + + if ( m_iBackgroundImageID == -1 ) + { + m_iBackgroundImageID = surface()->CreateNewTextureID(); + } + surface()->DrawSetTextureFile( m_iBackgroundImageID, filename, false, false ); + + if ( IsX360() ) + { + // 360 uses a product image during application exit + V_snprintf( filename, sizeof( filename ), "vgui/appchooser/background_orange%s", ( bIsWidescreen ? "_widescreen" : "" ) ); + + if ( m_iProductImageID == -1 ) + { + m_iProductImageID = surface()->CreateNewTextureID(); + } + surface()->DrawSetTextureFile( m_iProductImageID, filename, false, false ); + } + + if ( IsPC() ) + { + // load the loading icon + if ( m_iLoadingImageID == -1 ) + { + m_iLoadingImageID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( m_iLoadingImageID, "Console/startup_loading", false, false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: message handler for platform menu; activates the selected module +//----------------------------------------------------------------------------- +void CBasePanel::OnActivateModule(int moduleIndex) +{ + g_VModuleLoader.ActivateModule(moduleIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: Animates menus on gameUI being shown +//----------------------------------------------------------------------------- +void CBasePanel::OnGameUIActivated() +{ + // If the load failed, we're going to bail out here + if ( engine->MapLoadFailed() ) + { + // Don't display this again until it happens again + engine->SetMapLoadFailed( false ); + ShowMessageDialog( MD_LOAD_FAILED_WARNING ); + } + + + if ( !m_bEverActivated ) + { + // Layout the first time to avoid focus issues (setting menus visible will grab focus) + UpdateGameMenus(); + m_bEverActivated = true; + +#if defined( _X360 ) + + // Open all active containers if we have a valid storage device + if ( XBX_GetPrimaryUserId() != XBX_INVALID_USER_ID && XBX_GetStorageDeviceId() != XBX_INVALID_STORAGE_ID && XBX_GetStorageDeviceId() != XBX_STORAGE_DECLINED ) + { + // Open user settings and save game container here + uint nRet = engine->OnStorageDeviceAttached(); + if ( nRet != ERROR_SUCCESS ) + { + // Invalidate the device + XBX_SetStorageDeviceId( XBX_INVALID_STORAGE_ID ); + + // FIXME: We don't know which device failed! + // Pop a dialog explaining that the user's data is corrupt + BasePanel()->ShowMessageDialog( MD_STORAGE_DEVICES_CORRUPT ); + } + } + + // determine if we're starting up because of a cross-game invite + int fLaunchFlags = XboxLaunch()->GetLaunchFlags(); + if ( fLaunchFlags & LF_INVITERESTART ) + { + XNKID nSessionID; + XboxLaunch()->GetInviteSessionID( &nSessionID ); + matchmaking->JoinInviteSessionByID( nSessionID ); + } +#endif + + // Brute force check to open tf matchmaking ui. + if ( GameUI().IsConsoleUI() ) + { + const char *pGame = engine->GetGameDirectory(); + if ( !Q_stricmp( Q_UnqualifiedFileName( pGame ), "tf" ) ) + { + m_bUseMatchmaking = true; + RunMenuCommand( "OpenMatchmakingBasePanel" ); + } + } + } + + if ( GameUI().IsConsoleUI() ) + { + ArmFirstMenuItem(); + } + if ( GameUI().IsInLevel() ) + { + if ( !m_bUseMatchmaking ) + { + OnCommand( "OpenPauseMenu" ); + } + else + { + RunMenuCommand( "OpenMatchmakingBasePanel" ); + } + + if ( m_hAchievementsDialog.Get() ) + { + // Achievement dialog refreshes it's data if the player looks at the pause menu + m_hAchievementsDialog->OnCommand( "OnGameUIActivated" ); + } + } + else // not the pause menu, update presence + { + if ( IsX360() ) + { + UpdateRichPresenceInfo(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: executes a menu command +//----------------------------------------------------------------------------- +void CBasePanel::RunMenuCommand(const char *command) +{ + if ( !Q_stricmp( command, "OpenGameMenu" ) ) + { + if ( m_pGameMenu ) + { + PostMessage( m_pGameMenu, new KeyValues("Command", "command", "Open") ); + } + } + else if ( !Q_stricmp( command, "OpenPlayerListDialog" ) ) + { + OnOpenPlayerListDialog(); + } + else if ( !Q_stricmp( command, "OpenNewGameDialog" ) ) + { + OnOpenNewGameDialog(); + } + else if ( !Q_stricmp( command, "OpenLoadGameDialog" ) ) + { + if ( !GameUI().IsConsoleUI() ) + { + OnOpenLoadGameDialog(); + } + else + { + OnOpenLoadGameDialog_Xbox(); + } + } + else if ( !Q_stricmp( command, "OpenSaveGameDialog" ) ) + { + if ( !GameUI().IsConsoleUI() ) + { + OnOpenSaveGameDialog(); + } + else + { + OnOpenSaveGameDialog_Xbox(); + } + } + else if ( !Q_stricmp( command, "OpenBonusMapsDialog" ) ) + { + OnOpenBonusMapsDialog(); + } + else if ( !Q_stricmp( command, "OpenOptionsDialog" ) ) + { + if ( !GameUI().IsConsoleUI() ) + { + OnOpenOptionsDialog(); + } + else + { + OnOpenOptionsDialog_Xbox(); + } + } + else if ( !Q_stricmp( command, "OpenControllerDialog" ) ) + { + if ( GameUI().IsConsoleUI() ) + { + OnOpenControllerDialog(); + } + } + else if ( !Q_stricmp( command, "OpenBenchmarkDialog" ) ) + { + OnOpenBenchmarkDialog(); + } + else if ( !Q_stricmp( command, "OpenServerBrowser" ) ) + { + OnOpenServerBrowser(); + } + else if ( !Q_stricmp( command, "OpenFriendsDialog" ) ) + { + OnOpenFriendsDialog(); + } + else if ( !Q_stricmp( command, "OpenLoadDemoDialog" ) ) + { + OnOpenDemoDialog(); + } + else if ( !Q_stricmp( command, "OpenCreateMultiplayerGameDialog" ) ) + { + OnOpenCreateMultiplayerGameDialog(); + } + else if ( !Q_stricmp( command, "OpenChangeGameDialog" ) ) + { + OnOpenChangeGameDialog(); + } + else if ( !Q_stricmp( command, "OpenLoadCommentaryDialog" ) ) + { + OnOpenLoadCommentaryDialog(); + } + else if ( !Q_stricmp( command, "OpenLoadSingleplayerCommentaryDialog" ) ) + { + OpenLoadSingleplayerCommentaryDialog(); + } + else if ( !Q_stricmp( command, "OpenMatchmakingBasePanel" ) ) + { + OnOpenMatchmakingBasePanel(); + } + else if ( !Q_stricmp( command, "OpenAchievementsDialog" ) ) + { + if ( IsPC() ) + { + if ( !steamapicontext->SteamUser() || !steamapicontext->SteamUser()->BLoggedOn() ) + { + vgui::MessageBox *pMessageBox = new vgui::MessageBox("#GameUI_Achievements_SteamRequired_Title", "#GameUI_Achievements_SteamRequired_Message"); + pMessageBox->DoModal(); + return; + } + OnOpenAchievementsDialog(); + } + else + { + OnOpenAchievementsDialog_Xbox(); + } + } + //============================================================================= + // HPE_BEGIN: + // [dwenger] Use cs-specific achievements dialog + //============================================================================= + + else if ( !Q_stricmp( command, "OpenCSAchievementsDialog" ) ) + { + if ( IsPC() ) + { + if ( !steamapicontext->SteamUser() || !steamapicontext->SteamUser()->BLoggedOn() ) + { + vgui::MessageBox *pMessageBox = new vgui::MessageBox("#GameUI_Achievements_SteamRequired_Title", "#GameUI_Achievements_SteamRequired_Message", this ); + pMessageBox->DoModal(); + return; + } + + OnOpenCSAchievementsDialog(); + } + } + //============================================================================= + // HPE_END + //============================================================================= + + else if ( !Q_stricmp( command, "AchievementsDialogClosing" ) ) + { + if ( IsX360() ) + { + if ( m_hAchievementsDialog.Get() ) + { + m_hAchievementsDialog->Close(); + } + } + } + else if ( !Q_stricmp( command, "Quit" ) ) + { + OnOpenQuitConfirmationDialog(); + } + else if ( !Q_stricmp( command, "QuitNoConfirm" ) ) + { + if ( IsX360() ) + { + // start the shutdown process + StartExitingProcess(); + } + else + { + //============================================================================= + // HPE_BEGIN: + // [dwenger] Shut down achievements panel + //============================================================================= + + if ( GameClientExports() ) + { + GameClientExports()->ShutdownAchievementPanel(); + } + + //============================================================================= + // HPE_END + //============================================================================= + + // hide everything while we quit + SetVisible( false ); + vgui::surface()->RestrictPaintToSinglePanel( GetVPanel() ); + engine->ClientCmd_Unrestricted( "quit\n" ); + } + } + else if ( !Q_stricmp( command, "QuitRestartNoConfirm" ) ) + { + if ( IsX360() ) + { + // start the shutdown process + m_bRestartSameGame = true; + StartExitingProcess(); + } + } + else if ( !Q_stricmp( command, "ResumeGame" ) ) + { + GameUI().HideGameUI(); + } + else if ( !Q_stricmp( command, "Disconnect" ) ) + { + if ( IsX360() ) + { + OnOpenDisconnectConfirmationDialog(); + } + else + { + engine->ClientCmd_Unrestricted( "disconnect" ); + } + } + else if ( !Q_stricmp( command, "DisconnectNoConfirm" ) ) + { + ConVarRef commentary( "commentary" ); + if ( commentary.IsValid() && commentary.GetBool() ) + { + engine->ClientCmd_Unrestricted( "disconnect" ); + + CMatchmakingBasePanel *pBase = GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->CloseAllDialogs( false ); + pBase->OnCommand( "OpenWelcomeDialog" ); + } + } + else + { + // Leave our current session, if we have one + matchmaking->KickPlayerFromSession( 0 ); + } + } + else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) + { + vgui::surface()->RestrictPaintToSinglePanel(NULL); + } + else if ( Q_stristr( command, "engine " ) ) + { + const char *engineCMD = strstr( command, "engine " ) + strlen( "engine " ); + if ( strlen( engineCMD ) > 0 ) + { + engine->ClientCmd_Unrestricted( const_cast<char *>( engineCMD ) ); + } + } + else if ( !Q_stricmp( command, "ShowSigninUI" ) ) + { + m_bWaitingForUserSignIn = true; + xboxsystem->ShowSigninUI( 1, 0 ); // One user, no special flags + } + else if ( !Q_stricmp( command, "ShowDeviceSelector" ) ) + { + OnChangeStorageDevice(); + } + else if ( !Q_stricmp( command, "SignInDenied" ) ) + { + // The user doesn't care, so re-send the command they wanted and mark that we want to skip checking + m_bUserRefusedSignIn = true; + if ( m_strPostPromptCommand.IsEmpty() == false ) + { + OnCommand( m_strPostPromptCommand ); + } + } + else if ( !Q_stricmp( command, "RequiredSignInDenied" ) ) + { + m_strPostPromptCommand = ""; + } + else if ( !Q_stricmp( command, "RequiredStorageDenied" ) ) + { + m_strPostPromptCommand = ""; + } + else if ( !Q_stricmp( command, "StorageDeviceDenied" ) ) + { + // The user doesn't care, so re-send the command they wanted and mark that we want to skip checking + m_bUserRefusedStorageDevice = true; + IssuePostPromptCommand(); + + // Set us as declined + XBX_SetStorageDeviceId( XBX_STORAGE_DECLINED ); + m_iStorageID = XBX_INVALID_STORAGE_ID; + + if ( m_pStorageDeviceValidatedNotify ) + { + *m_pStorageDeviceValidatedNotify = 2; + m_pStorageDeviceValidatedNotify = NULL; + } + } + else if ( !Q_stricmp( command, "clear_storage_deviceID" ) ) + { + XBX_SetStorageDeviceId( XBX_STORAGE_DECLINED ); + } + else if ( !Q_stricmp( command, "RestartWithNewLanguage" ) ) + { + if ( !IsX360() ) + { + char szSteamURL[50]; + + // hide everything while we quit + SetVisible( false ); + vgui::surface()->RestrictPaintToSinglePanel( GetVPanel() ); + engine->ClientCmd_Unrestricted( "quit\n" ); + + // Construct Steam URL. Pattern is steam://run/<appid>/<language>. (e.g. Ep1 In French ==> steam://run/380/french) + V_snprintf( szSteamURL, sizeof(szSteamURL), "steam://run/%d/%s", engine->GetAppID(), COptionsSubAudio::GetUpdatedAudioLanguage() ); + + // Set Steam URL for re-launch in registry. Launcher will check this registry key and exec it in order to re-load the game in the proper language +#if defined( WIN32 ) && !defined( _X360 ) + HKEY hKey; + + if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source", NULL, KEY_WRITE, &hKey) == ERROR_SUCCESS ) + { + RegSetValueEx( hKey, "Relaunch URL", 0, REG_SZ, (const unsigned char *)szSteamURL, sizeof( szSteamURL ) ); + + RegCloseKey(hKey); + } +#elif defined( OSX ) || defined( LINUX ) + FILE *fp = fopen( "/tmp/hl2_relaunch", "w+" ); + if ( fp ) + { + fprintf( fp, "%s\n", szSteamURL ); + } + fclose( fp ); +#elif defined( _X360 ) +#else +#error +#endif + } + } + else + { + BaseClass::OnCommand( command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Queue a command to be run when XUI Closes +//----------------------------------------------------------------------------- +void CBasePanel::QueueCommand( const char *pCommand ) +{ + if ( m_bXUIVisible ) + { + m_CommandQueue.AddToTail( CUtlString( pCommand ) ); + } + else + { + OnCommand( pCommand ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Run all the commands in the queue +//----------------------------------------------------------------------------- +void CBasePanel::RunQueuedCommands() +{ + for ( int i = 0; i < m_CommandQueue.Count(); ++i ) + { + OnCommand( m_CommandQueue[i] ); + } + ClearQueuedCommands(); +} + +//----------------------------------------------------------------------------- +// Purpose: Clear all queued commands +//----------------------------------------------------------------------------- +void CBasePanel::ClearQueuedCommands() +{ + m_CommandQueue.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Whether this command should cause us to prompt the user if they're not signed in and do not have a storage device +//----------------------------------------------------------------------------- +bool CBasePanel::IsPromptableCommand( const char *command ) +{ + // Blech! + if ( !Q_stricmp( command, "OpenNewGameDialog" ) || + !Q_stricmp( command, "OpenLoadGameDialog" ) || + !Q_stricmp( command, "OpenSaveGameDialog" ) || + !Q_stricmp( command, "OpenBonusMapsDialog" ) || + !Q_stricmp( command, "OpenOptionsDialog" ) || + !Q_stricmp( command, "OpenControllerDialog" ) || + !Q_stricmp( command, "OpenLoadCommentaryDialog" ) || + !Q_stricmp( command, "OpenLoadSingleplayerCommentaryDialog" ) || + !Q_stricmp( command, "OpenAchievementsDialog" ) || + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Use cs-specific achievements dialog + //============================================================================= + + !Q_stricmp( command, "OpenCSAchievementsDialog" ) ) + + //============================================================================= + // HPE_END + //============================================================================= + + { + return true; + } + + return false; +} + +#ifdef _WIN32 +//------------------------- +// Purpose: Job wrapper +//------------------------- +static unsigned PanelJobWrapperFn( void *pvContext ) +{ + CBasePanel::CAsyncJobContext *pAsync = reinterpret_cast< CBasePanel::CAsyncJobContext * >( pvContext ); + + float const flTimeStart = Plat_FloatTime(); + + pAsync->ExecuteAsync(); + + float const flElapsedTime = Plat_FloatTime() - flTimeStart; + + if ( flElapsedTime < pAsync->m_flLeastExecuteTime ) + { + ThreadSleep( ( pAsync->m_flLeastExecuteTime - flElapsedTime ) * 1000 ); + } + + ReleaseThreadHandle( ( ThreadHandle_t ) pAsync->m_hThreadHandle ); + pAsync->m_hThreadHandle = NULL; + + return 0; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Enqueues a job function to be called on a separate thread +//----------------------------------------------------------------------------- +void CBasePanel::ExecuteAsync( CAsyncJobContext *pAsync ) +{ + Assert( !m_pAsyncJob ); + Assert( pAsync && !pAsync->m_hThreadHandle ); + m_pAsyncJob = pAsync; + +#ifdef _WIN32 + ThreadHandle_t hHandle = CreateSimpleThread( PanelJobWrapperFn, reinterpret_cast< void * >( pAsync ) ); + pAsync->m_hThreadHandle = hHandle; + +#ifdef _X360 + ThreadSetAffinity( hHandle, XBOX_PROCESSOR_3 ); +#endif + +#else + pAsync->ExecuteAsync(); +#endif +} + + + +//----------------------------------------------------------------------------- +// Purpose: Whether this command requires the user be signed in +//----------------------------------------------------------------------------- +bool CBasePanel::CommandRequiresSignIn( const char *command ) +{ + // Blech again! + if ( !Q_stricmp( command, "OpenAchievementsDialog" ) || + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Use cs-specific achievements dialog + //============================================================================= + + !Q_stricmp( command, "OpenCSAchievementsDialog" ) || + + //============================================================================= + // HPE_END + //============================================================================= + + !Q_stricmp( command, "OpenLoadGameDialog" ) || + !Q_stricmp( command, "OpenSaveGameDialog" ) || + !Q_stricmp( command, "OpenRankingsDialog" ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Whether the command requires the user to have a valid storage device +//----------------------------------------------------------------------------- +bool CBasePanel::CommandRequiresStorageDevice( const char *command ) +{ + // Anything which touches the storage device must prompt + if ( !Q_stricmp( command, "OpenSaveGameDialog" ) || + !Q_stricmp( command, "OpenLoadGameDialog" ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Whether the command requires the user to have a valid profile selected +//----------------------------------------------------------------------------- +bool CBasePanel::CommandRespectsSignInDenied( const char *command ) +{ + // Anything which touches the user profile must prompt + if ( !Q_stricmp( command, "OpenOptionsDialog" ) || + !Q_stricmp( command, "OpenControllerDialog" ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: A storage device has been connected, update our settings and anything else +//----------------------------------------------------------------------------- + +class CAsyncCtxOnDeviceAttached : public CBasePanel::CAsyncJobContext +{ +public: + CAsyncCtxOnDeviceAttached(); + ~CAsyncCtxOnDeviceAttached(); + virtual void ExecuteAsync(); + virtual void Completed(); + uint GetContainerOpenResult( void ) { return m_ContainerOpenResult; } + +private: + uint m_ContainerOpenResult; +}; + +CAsyncCtxOnDeviceAttached::CAsyncCtxOnDeviceAttached() : + CBasePanel::CAsyncJobContext( 3.0f ), // Storage device info for at least 3 seconds + m_ContainerOpenResult( ERROR_SUCCESS ) +{ + BasePanel()->ShowMessageDialog( MD_CHECKING_STORAGE_DEVICE ); +} + +CAsyncCtxOnDeviceAttached::~CAsyncCtxOnDeviceAttached() +{ + BasePanel()->CloseMessageDialog( 0 ); +} + +void CAsyncCtxOnDeviceAttached::ExecuteAsync() +{ + // Asynchronously do the tasks that don't interact with the command buffer + + // Open user settings and save game container here + m_ContainerOpenResult = engine->OnStorageDeviceAttached(); + if ( m_ContainerOpenResult != ERROR_SUCCESS ) + return; + + // Make the QOS system initialized for multiplayer games + if ( !ModInfo().IsSinglePlayerOnly() ) + { +#if defined( _X360 ) + ( void ) matchmaking->GetQosWithLIVE(); +#endif + } +} + +void CAsyncCtxOnDeviceAttached::Completed() +{ + BasePanel()->OnCompletedAsyncDeviceAttached( this ); +} + + +void CBasePanel::OnDeviceAttached( void ) +{ + ExecuteAsync( new CAsyncCtxOnDeviceAttached ); +} + +void CBasePanel::OnCompletedAsyncDeviceAttached( CAsyncCtxOnDeviceAttached *job ) +{ + uint nRet = job->GetContainerOpenResult(); + if ( nRet != ERROR_SUCCESS ) + { + // Invalidate the device + XBX_SetStorageDeviceId( XBX_INVALID_STORAGE_ID ); + + // FIXME: We don't know which device failed! + // Pop a dialog explaining that the user's data is corrupt + BasePanel()->ShowMessageDialog( MD_STORAGE_DEVICES_CORRUPT ); + } + + // First part of the device checking completed asynchronously, + // perform the rest of duties that require to run on main thread. + engine->ReadConfiguration(); + engine->ExecuteClientCmd( "refreshplayerstats" ); + + BonusMapsDatabase()->ReadBonusMapSaveData(); + + if ( m_hSaveGameDialog_Xbox.Get() ) + { + m_hSaveGameDialog_Xbox->OnCommand( "RefreshSaveGames" ); + } + if ( m_hLoadGameDialog_Xbox.Get() ) + { + m_hLoadGameDialog_Xbox->OnCommand( "RefreshSaveGames" ); + } + if ( m_hOptionsDialog_Xbox.Get() ) + { + m_hOptionsDialog_Xbox->OnCommand( "RefreshOptions" ); + } + if ( m_pStorageDeviceValidatedNotify ) + { + *m_pStorageDeviceValidatedNotify = 1; + m_pStorageDeviceValidatedNotify = NULL; + } + + // Finish their command + IssuePostPromptCommand(); +} + +//----------------------------------------------------------------------------- +// Purpose: FIXME: Only TF takes this path... +//----------------------------------------------------------------------------- +bool CBasePanel::ValidateStorageDevice( void ) +{ + if ( m_bUserRefusedStorageDevice == false ) + { +#if defined( _X360 ) + if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID ) + { + // Try to discover content on the user's storage devices + DWORD nFoundDevice = xboxsystem->DiscoverUserData( XBX_GetPrimaryUserId(), COM_GetModDirectory() ); + if ( nFoundDevice == XBX_INVALID_STORAGE_ID ) + { + // They don't have a device, so ask for one + ShowMessageDialog( MD_PROMPT_STORAGE_DEVICE ); + return false; + } + else + { + // Take this device + XBX_SetStorageDeviceId( nFoundDevice ); + OnDeviceAttached(); + } + // Fall through + } +#endif + } + return true; +} + +bool CBasePanel::ValidateStorageDevice( int *pStorageDeviceValidated ) +{ + if ( m_pStorageDeviceValidatedNotify ) + { + if ( pStorageDeviceValidated != m_pStorageDeviceValidatedNotify ) + { + *m_pStorageDeviceValidatedNotify = -1; + m_pStorageDeviceValidatedNotify = NULL; + } + else + { + return false; + } + } + + if ( pStorageDeviceValidated ) + { + if ( HandleStorageDeviceRequest( "" ) ) + return true; + + m_pStorageDeviceValidatedNotify = pStorageDeviceValidated; + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Monitor commands for certain necessary cases +// Input : *command - What menu command we're policing +//----------------------------------------------------------------------------- +bool CBasePanel::HandleSignInRequest( const char *command ) +{ +#ifdef _X360 + // If we have a post-prompt command, we're coming back into the call from that prompt + bool bQueuedCall = ( m_strPostPromptCommand.IsEmpty() == false ); + + XUSER_SIGNIN_INFO info; + bool bValidUser = ( XUserGetSigninInfo( XBX_GetPrimaryUserId(), 0, &info ) == ERROR_SUCCESS ); + + if ( bValidUser ) + return true; + + // Queued command means we're returning from a prompt or blade + if ( bQueuedCall ) + { + // Blade has returned with nothing + if ( m_bUserRefusedSignIn ) + return true; + + // User has not denied the storage device, so ask + ShowMessageDialog( MD_PROMPT_SIGNIN ); + m_strPostPromptCommand = command; + + // Do not run command + return false; + } + else + { + // If the user refused the sign-in and we respect that on this command, we're done + if ( m_bUserRefusedSignIn && CommandRespectsSignInDenied( command ) ) + return true; + + // If the message is required first, then do that instead + if ( CommandRequiresSignIn( command ) ) + { + ShowMessageDialog( MD_PROMPT_SIGNIN_REQUIRED ); + m_strPostPromptCommand = command; + return false; + } + + // Pop a blade out + xboxsystem->ShowSigninUI( 1, 0 ); + m_strPostPromptCommand = command; + m_bWaitingForUserSignIn = true; + m_bUserRefusedSignIn = false; + return false; + } +#endif // _X360 + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *command - +//----------------------------------------------------------------------------- +bool CBasePanel::HandleStorageDeviceRequest( const char *command ) +{ + // If we don't have a valid sign-in, then we do nothing! + if ( m_bUserRefusedSignIn ) + return true; + + // If we have a valid storage device, there's nothing to prompt for + if ( XBX_GetStorageDeviceId() != XBX_INVALID_STORAGE_ID && XBX_GetStorageDeviceId() != XBX_STORAGE_DECLINED ) + return true; + + // If we have a post-prompt command, we're coming back into the call from that prompt + bool bQueuedCall = ( m_strPostPromptCommand.IsEmpty() == false ); + + // Are we returning from a prompt? + if ( bQueuedCall && m_bStorageBladeShown ) + { + // User has declined + if ( m_bUserRefusedStorageDevice ) + return true; + + // Prompt them + ShowMessageDialog( MD_PROMPT_STORAGE_DEVICE ); + m_strPostPromptCommand = command; + + // Do not run the command + return false; + } + else + { + // If the user refused the sign-in and we respect that on this command, we're done + if ( m_bUserRefusedStorageDevice && CommandRespectsSignInDenied( command ) ) + return true; + +#if 0 // This attempts to find user data, but may not be cert-worthy even though it's a bit nicer for the user + // Attempt to automatically find a device + DWORD nFoundDevice = xboxsystem->DiscoverUserData( XBX_GetPrimaryUserId(), COM_GetModDirectory() ); + if ( nFoundDevice != XBX_INVALID_STORAGE_ID ) + { + // Take this device + XBX_SetStorageDeviceId( nFoundDevice ); + OnDeviceAttached(); + return true; + } +#endif // + + // If the message is required first, then do that instead + if ( CommandRequiresStorageDevice( command ) ) + { + ShowMessageDialog( MD_PROMPT_STORAGE_DEVICE_REQUIRED ); + m_strPostPromptCommand = command; + return false; + } + + // This is a misnomer of the first order! + OnChangeStorageDevice(); + m_strPostPromptCommand = command; + m_bStorageBladeShown = true; + m_bUserRefusedStorageDevice = false; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Clear the command we've queued once it has succeeded in being called +//----------------------------------------------------------------------------- +void CBasePanel::ClearPostPromptCommand( const char *pCompletedCommand ) +{ + if ( !Q_stricmp( m_strPostPromptCommand, pCompletedCommand ) ) + { + // All commands are executed, so stop holding this + m_strPostPromptCommand = ""; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Issue our queued command to either the base panel or the matchmaking panel +//----------------------------------------------------------------------------- +void CBasePanel::IssuePostPromptCommand( void ) +{ + // The device is valid, so launch any pending commands + if ( m_strPostPromptCommand.IsEmpty() == false ) + { + if ( m_bSinglePlayer ) + { + OnCommand( m_strPostPromptCommand ); + } + else + { + CMatchmakingBasePanel *pMatchMaker = GetMatchmakingBasePanel(); + if ( pMatchMaker ) + { + pMatchMaker->OnCommand( m_strPostPromptCommand ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: message handler for menu selections +//----------------------------------------------------------------------------- +void CBasePanel::OnCommand( const char *command ) +{ + if ( GameUI().IsConsoleUI() ) + { +#if defined( _X360 ) + + // See if this is a command we need to intercept + if ( IsPromptableCommand( command ) ) + { + // Handle the sign in case + if ( HandleSignInRequest( command ) == false ) + return; + + // Handle storage + if ( HandleStorageDeviceRequest( command ) == false ) + return; + + // If we fall through, we'll need to track this again + m_bStorageBladeShown = false; + + // Fall through + } +#endif // _X360 + + RunAnimationWithCallback( this, command, new KeyValues( "RunMenuCommand", "command", command ) ); + + // Clear our pending command if we just executed it + ClearPostPromptCommand( command ); + } + else + { + RunMenuCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs an animation sequence, then calls a message mapped function +// when the animation is complete. +//----------------------------------------------------------------------------- +void CBasePanel::RunAnimationWithCallback( vgui::Panel *parent, const char *animName, KeyValues *msgFunc ) +{ + if ( !m_pConsoleAnimationController ) + return; + + m_pConsoleAnimationController->StartAnimationSequence( animName ); + float sequenceLength = m_pConsoleAnimationController->GetAnimationSequenceLength( animName ); + if ( sequenceLength ) + { + sequenceLength += g_flAnimationPadding; + } + if ( parent && msgFunc ) + { + PostMessage( parent, msgFunc, sequenceLength ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: trinary choice query "save & quit", "quit", "cancel" +//----------------------------------------------------------------------------- +class CSaveBeforeQuitQueryDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CSaveBeforeQuitQueryDialog, vgui::Frame ); +public: + CSaveBeforeQuitQueryDialog(vgui::Panel *parent, const char *name) : BaseClass(parent, name) + { + LoadControlSettings("resource/SaveBeforeQuitDialog.res"); + SetDeleteSelfOnClose(true); + SetSizeable(false); + } + + void DoModal() + { + BaseClass::Activate(); + input()->SetAppModalSurface(GetVPanel()); + MoveToCenterOfScreen(); + vgui::surface()->RestrictPaintToSinglePanel(GetVPanel()); + + GameUI().PreventEngineHideGameUI(); + } + + void OnKeyCodeTyped(KeyCode code) + { + // ESC cancels + if ( code == KEY_ESCAPE ) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + + void OnKeyCodePressed(KeyCode code) + { + // ESC cancels + if ( code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B || code == STEAMCONTROLLER_START ) + { + Close(); + } + else if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + OnCommand( "SaveAndQuit" ); + } + else if ( code == KEY_XBUTTON_X || code == STEAMCONTROLLER_X ) + { + OnCommand( "Quit" ); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + + virtual void OnCommand(const char *command) + { + if (!Q_stricmp(command, "Quit")) + { + PostMessage(GetVParent(), new KeyValues("Command", "command", "QuitNoConfirm")); + } + else if (!Q_stricmp(command, "SaveAndQuit")) + { + // find a new name to save + char saveName[128]; + CSaveGameDialog::FindSaveSlot( saveName, sizeof(saveName) ); + if ( saveName[ 0 ] ) + { + // save the game + char sz[ 256 ]; + Q_snprintf(sz, sizeof( sz ), "save %s\n", saveName ); + engine->ClientCmd_Unrestricted( sz ); + } + + // quit + PostMessage(GetVParent(), new KeyValues("Command", "command", "QuitNoConfirm")); + } + else if (!Q_stricmp(command, "Cancel")) + { + Close(); + } + else + { + BaseClass::OnCommand(command); + } + } + + virtual void OnClose() + { + BaseClass::OnClose(); + vgui::surface()->RestrictPaintToSinglePanel(NULL); + GameUI().AllowEngineHideGameUI(); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: simple querybox that accepts escape +//----------------------------------------------------------------------------- +class CQuitQueryBox : public vgui::QueryBox +{ + DECLARE_CLASS_SIMPLE( CQuitQueryBox, vgui::QueryBox ); +public: + CQuitQueryBox(const char *title, const char *info, Panel *parent) : BaseClass( title, info, parent ) + { + } + + void DoModal( Frame* pFrameOver ) + { + BaseClass::DoModal( pFrameOver ); + vgui::surface()->RestrictPaintToSinglePanel(GetVPanel()); + GameUI().PreventEngineHideGameUI(); + } + + void OnKeyCodeTyped(KeyCode code) + { + // ESC cancels + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + + void OnKeyCodePressed(KeyCode code) + { + // ESC cancels + if (code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B) + { + Close(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + + virtual void OnClose() + { + BaseClass::OnClose(); + vgui::surface()->RestrictPaintToSinglePanel(NULL); + GameUI().AllowEngineHideGameUI(); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: asks user how they feel about quiting +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenQuitConfirmationDialog() +{ + if ( GameUI().IsConsoleUI() ) + { + if ( !GameUI().HasSavedThisMenuSession() && GameUI().IsInLevel() && engine->GetMaxClients() == 1 ) + { + // single player, progress will be lost... + ShowMessageDialog( MD_SAVE_BEFORE_QUIT ); + } + else + { + if ( m_bUseMatchmaking ) + { + ShowMessageDialog( MD_QUIT_CONFIRMATION_TF ); + } + else + { + ShowMessageDialog( MD_QUIT_CONFIRMATION ); + } + } + return; + } + + + if ( GameUI().IsInLevel() && engine->GetMaxClients() == 1 ) + { + // prompt for saving current game before quiting + CSaveBeforeQuitQueryDialog *box = new CSaveBeforeQuitQueryDialog(this, "SaveBeforeQuitQueryDialog"); + box->DoModal(); + } + else + { + // simple ok/cancel prompt + QueryBox *box = new CQuitQueryBox("#GameUI_QuitConfirmationTitle", "#GameUI_QuitConfirmationText", this); + box->SetOKButtonText("#GameUI_Quit"); + box->SetOKCommand(new KeyValues("Command", "command", "QuitNoConfirm")); + box->SetCancelCommand(new KeyValues("Command", "command", "ReleaseModalWindow")); + box->AddActionSignalTarget(this); + box->DoModal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: asks user how they feel about disconnecting +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenDisconnectConfirmationDialog() +{ + // THis is for disconnecting from a multiplayer server + Assert( m_bUseMatchmaking ); + Assert( IsX360() ); + + if ( GameUI().IsConsoleUI() && GameUI().IsInLevel() ) + { + if ( engine->GetLocalPlayer() == 1 ) + { + ShowMessageDialog( MD_DISCONNECT_CONFIRMATION_HOST ); + } + else + { + ShowMessageDialog( MD_DISCONNECT_CONFIRMATION ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenNewGameDialog(const char *chapter ) +{ + if ( !m_hNewGameDialog.Get() ) + { + m_hNewGameDialog = new CNewGameDialog(this, false); + PositionDialog( m_hNewGameDialog ); + } + + if ( chapter ) + { + ((CNewGameDialog *)m_hNewGameDialog.Get())->SetSelectedChapter(chapter); + } + + ((CNewGameDialog *)m_hNewGameDialog.Get())->SetCommentaryMode( false ); + m_hNewGameDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenBonusMapsDialog( void ) +{ + if ( !m_hBonusMapsDialog.Get() ) + { + m_hBonusMapsDialog = new CBonusMapsDialog(this); + PositionDialog( m_hBonusMapsDialog ); + } + + m_hBonusMapsDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenLoadGameDialog() +{ + if ( !m_hLoadGameDialog.Get() ) + { + m_hLoadGameDialog = new CLoadGameDialog(this); + PositionDialog( m_hLoadGameDialog ); + } + m_hLoadGameDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenLoadGameDialog_Xbox() +{ + if ( !m_hLoadGameDialog_Xbox.Get() ) + { + m_hLoadGameDialog_Xbox = new CLoadGameDialogXbox(this); + PositionDialog( m_hLoadGameDialog_Xbox ); + } + m_hLoadGameDialog_Xbox->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenSaveGameDialog() +{ + if ( !m_hSaveGameDialog.Get() ) + { + m_hSaveGameDialog = new CSaveGameDialog(this); + PositionDialog( m_hSaveGameDialog ); + } + m_hSaveGameDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenSaveGameDialog_Xbox() +{ + if ( !m_hSaveGameDialog_Xbox.Get() ) + { + m_hSaveGameDialog_Xbox = new CSaveGameDialogXbox(this); + PositionDialog( m_hSaveGameDialog_Xbox ); + } + m_hSaveGameDialog_Xbox->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenOptionsDialog() +{ + if ( !m_hOptionsDialog.Get() ) + { + m_hOptionsDialog = new COptionsDialog(this); + g_hOptionsDialog = m_hOptionsDialog; + PositionDialog( m_hOptionsDialog ); + } + + m_hOptionsDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenOptionsDialog_Xbox() +{ + if ( !m_hOptionsDialog_Xbox.Get() ) + { + m_hOptionsDialog_Xbox = new COptionsDialogXbox( this ); + PositionDialog( m_hOptionsDialog_Xbox ); + } + + m_hOptionsDialog_Xbox->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: forces any changed options dialog settings to be applied immediately, if it's open +//----------------------------------------------------------------------------- +void CBasePanel::ApplyOptionsDialogSettings() +{ + if (m_hOptionsDialog.Get()) + { + m_hOptionsDialog->ApplyChanges(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenControllerDialog() +{ + if ( !m_hControllerDialog.Get() ) + { + m_hControllerDialog = new CControllerDialog( this ); + PositionDialog( m_hControllerDialog ); + } + + m_hControllerDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenBenchmarkDialog() +{ + if (!m_hBenchmarkDialog.Get()) + { + m_hBenchmarkDialog = new CBenchmarkDialog(this, "BenchmarkDialog"); + PositionDialog( m_hBenchmarkDialog ); + } + m_hBenchmarkDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenServerBrowser() +{ + g_VModuleLoader.ActivateModule("Servers"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenFriendsDialog() +{ + g_VModuleLoader.ActivateModule("Friends"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenDemoDialog() +{ +/* if ( !m_hDemoPlayerDialog.Get() ) + { + m_hDemoPlayerDialog = new CDemoPlayerDialog(this); + PositionDialog( m_hDemoPlayerDialog ); + } + m_hDemoPlayerDialog->Activate();*/ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenCreateMultiplayerGameDialog() +{ + if (!m_hCreateMultiplayerGameDialog.Get()) + { + m_hCreateMultiplayerGameDialog = new CCreateMultiplayerGameDialog(this); + PositionDialog(m_hCreateMultiplayerGameDialog); + } + m_hCreateMultiplayerGameDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenChangeGameDialog() +{ +#ifdef POSIX + // Alfred says this is old legacy code that allowed you to walk through looking for + // gameinfos and switch and it's not needed anymore. So I'm killing this assert... +#else + if (!m_hChangeGameDialog.Get()) + { + m_hChangeGameDialog = new CChangeGameDialog(this); + PositionDialog(m_hChangeGameDialog); + } + m_hChangeGameDialog->Activate(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenPlayerListDialog() +{ + if (!m_hPlayerListDialog.Get()) + { + m_hPlayerListDialog = new CPlayerListDialog(this); + PositionDialog(m_hPlayerListDialog); + } + m_hPlayerListDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenLoadCommentaryDialog() +{ + if (!m_hPlayerListDialog.Get()) + { + m_hLoadCommentaryDialog = new CLoadCommentaryDialog(this); + PositionDialog(m_hLoadCommentaryDialog); + } + m_hLoadCommentaryDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OpenLoadSingleplayerCommentaryDialog() +{ + if ( !m_hNewGameDialog.Get() ) + { + m_hNewGameDialog = new CNewGameDialog(this,true); + PositionDialog( m_hNewGameDialog ); + } + + ((CNewGameDialog *)m_hNewGameDialog.Get())->SetCommentaryMode( true ); + m_hNewGameDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenAchievementsDialog() +{ + if (!m_hAchievementsDialog.Get()) + { + m_hAchievementsDialog = new CAchievementsDialog( this ); + PositionDialog(m_hAchievementsDialog); + } + m_hAchievementsDialog->Activate(); +} + +//============================================================================= +// HPE_BEGIN: +// [dwenger] Use cs-specific achievements dialog +//============================================================================= + +void CBasePanel::OnOpenCSAchievementsDialog() +{ + if ( GameClientExports() ) + { + int screenWide = 0; + int screenHeight = 0; + engine->GetScreenSize( screenWide, screenHeight ); + + // [smessick] For lower resolutions, open the Steam achievements instead of the CSS achievements screen. + if ( screenWide < GameClientExports()->GetAchievementsPanelMinWidth() ) + { + ISteamFriends *friends = steamapicontext->SteamFriends(); + if ( friends ) + { + friends->ActivateGameOverlay( "Achievements" ); + } + } + else + { + // Display the CSS achievements screen. + GameClientExports()->CreateAchievementsPanel( this ); + GameClientExports()->DisplayAchievementPanel(); + } + } +} + +//============================================================================= +// HPE_END +//============================================================================= + +void CBasePanel::OnOpenAchievementsDialog_Xbox() +{ + if (!m_hAchievementsDialog.Get()) + { + m_hAchievementsDialog = new CAchievementsDialog_XBox( this ); + PositionDialog(m_hAchievementsDialog); + } + m_hAchievementsDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnOpenMatchmakingBasePanel() +{ + if (!m_hMatchmakingBasePanel.Get()) + { + m_hMatchmakingBasePanel = new CMatchmakingBasePanel( this ); + int x, y, wide, tall; + GetBounds( x, y, wide, tall ); + m_hMatchmakingBasePanel->SetBounds( x, y, wide, tall ); + } + + if ( m_pGameLogo ) + { + m_pGameLogo->SetVisible( false ); + } + + // Hide the standard game menu + for ( int i = 0; i < m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetVisible( false ); + } + + // Hide the BasePanel's button footer + m_pGameMenu->ShowFooter( false ); + + m_hMatchmakingBasePanel->SetVisible( true ); + + m_hMatchmakingBasePanel->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Helper function for this common operation +//----------------------------------------------------------------------------- +CMatchmakingBasePanel *CBasePanel::GetMatchmakingBasePanel() +{ + CMatchmakingBasePanel *pBase = NULL; + if ( m_bUseMatchmaking ) + { + pBase = dynamic_cast< CMatchmakingBasePanel* >( m_hMatchmakingBasePanel.Get() ); + } + return pBase; +} + +//----------------------------------------------------------------------------- +// Purpose: moves the game menu button to the right place on the taskbar +//----------------------------------------------------------------------------- +void CBasePanel::PositionDialog(vgui::PHandle dlg) +{ + if (!dlg.Get()) + return; + + int x, y, ww, wt, wide, tall; + vgui::surface()->GetWorkspaceBounds( x, y, ww, wt ); + dlg->GetSize(wide, tall); + + // Center it, keeping requested size + dlg->SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2)); +} + +//----------------------------------------------------------------------------- +// Purpose: Add an Xbox 360 message dialog to a dialog stack +//----------------------------------------------------------------------------- +void CBasePanel::ShowMessageDialog( const uint nType, vgui::Panel *pOwner ) +{ + if ( pOwner == NULL ) + { + pOwner = this; + } + + m_MessageDialogHandler.ShowMessageDialog( nType, pOwner ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add an Xbox 360 message dialog to a dialog stack +//----------------------------------------------------------------------------- +void CBasePanel::CloseMessageDialog( const uint nType ) +{ + m_MessageDialogHandler.CloseMessageDialog( nType ); +} + +//----------------------------------------------------------------------------- +// Purpose: Matchmaking notification from engine +//----------------------------------------------------------------------------- +void CBasePanel::SessionNotification( const int notification, const int param ) +{ + // This is a job for the matchmaking panel + CMatchmakingBasePanel *pBase = GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->SessionNotification( notification, param ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: System notification from engine +//----------------------------------------------------------------------------- +void CBasePanel::SystemNotification( const int notification ) +{ + CMatchmakingBasePanel *pBase = GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->SystemNotification( notification ); + } + + if ( notification == SYSTEMNOTIFY_USER_SIGNEDIN ) + { +#if defined( _X360 ) + // See if it was the active user who signed in + uint state = XUserGetSigninState( XBX_GetPrimaryUserId() ); + if ( state != eXUserSigninState_NotSignedIn ) + { + // Reset a bunch of state + m_bUserRefusedSignIn = false; + m_bUserRefusedStorageDevice = false; + m_bStorageBladeShown = false; + } + UpdateRichPresenceInfo(); + engine->GetAchievementMgr()->DownloadUserData(); + engine->GetAchievementMgr()->EnsureGlobalStateLoaded(); +#endif + } + else if ( notification == SYSTEMNOTIFY_USER_SIGNEDOUT ) + { +#if defined( _X360 ) + // See if it was the active user who signed out + uint state = XUserGetSigninState( XBX_GetPrimaryUserId() ); + if ( state != eXUserSigninState_NotSignedIn ) + { + return; + } + + // Invalidate their storage ID + engine->OnStorageDeviceDetached(); + m_bUserRefusedStorageDevice = false; + m_bUserRefusedSignIn = false; + m_iStorageID = XBX_INVALID_STORAGE_ID; + engine->GetAchievementMgr()->InitializeAchievements(); + m_MessageDialogHandler.CloseAllMessageDialogs(); + +#endif + if ( GameUI().IsInLevel() ) + { + if ( m_pGameLogo ) + { + m_pGameLogo->SetVisible( false ); + } + + // Hide the standard game menu + for ( int i = 0; i < m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetVisible( false ); + } + + // Hide the BasePanel's button footer + m_pGameMenu->ShowFooter( false ); + + QueueCommand( "QuitNoConfirm" ); + } + else + { + CloseBaseDialogs(); + } + + OnCommand( "OpenMainMenu" ); + } + else if ( notification == SYSTEMNOTIFY_STORAGEDEVICES_CHANGED ) + { + if ( m_hSaveGameDialog_Xbox.Get() ) + m_hSaveGameDialog_Xbox->OnCommand( "RefreshSaveGames" ); + if ( m_hLoadGameDialog_Xbox.Get() ) + m_hLoadGameDialog_Xbox->OnCommand( "RefreshSaveGames" ); + + // FIXME: This code is incorrect, they do NOT need a storage device, it is only recommended that they do + if ( GameUI().IsInLevel() ) + { + // They wanted to use a storage device and are already playing! + // They need a storage device now or we're quitting the game! + m_bNeedStorageDeviceHandle = true; + ShowMessageDialog( MD_STORAGE_DEVICES_NEEDED, this ); + } + else + { + ShowMessageDialog( MD_STORAGE_DEVICES_CHANGED, this ); + } + } + else if ( notification == SYSTEMNOTIFY_XUIOPENING ) + { + m_bXUIVisible = true; + } + else if ( notification == SYSTEMNOTIFY_XUICLOSED ) + { + m_bXUIVisible = false; + + if ( m_bWaitingForStorageDeviceHandle ) + { + DWORD ret = xboxsystem->GetOverlappedResult( m_hStorageDeviceChangeHandle, NULL, true ); + if ( ret != ERROR_IO_INCOMPLETE ) + { + // Done waiting + xboxsystem->ReleaseAsyncHandle( m_hStorageDeviceChangeHandle ); + + m_bWaitingForStorageDeviceHandle = false; + + // If we selected something, validate it + if ( m_iStorageID != XBX_INVALID_STORAGE_ID ) + { + // Check to see if there is enough room on this storage device + if ( xboxsystem->DeviceCapacityAdequate( m_iStorageID, COM_GetModDirectory() ) == false ) + { + ShowMessageDialog( MD_STORAGE_DEVICES_TOO_FULL, this ); + m_bStorageBladeShown = false; // Show the blade again next time + m_strPostPromptCommand = ""; // Clear the buffer, we can't return + } + else + { + m_bNeedStorageDeviceHandle = false; + + // Set the storage device + XBX_SetStorageDeviceId( m_iStorageID ); + OnDeviceAttached(); + } + } + else + { + if ( m_pStorageDeviceValidatedNotify ) + { + *m_pStorageDeviceValidatedNotify = 2; + m_pStorageDeviceValidatedNotify = NULL; + } + else if ( m_bNeedStorageDeviceHandle ) + { + // They didn't select a storage device! + // Remind them that they must pick one or the game will shut down + ShowMessageDialog( MD_STORAGE_DEVICES_NEEDED, this ); + } + else + { + // Start off the command we queued up + IssuePostPromptCommand(); + } + } + } + } + + // If we're waiting for the user to sign in, and check if they selected a usable profile + if ( m_bWaitingForUserSignIn ) + { + // Done waiting + m_bWaitingForUserSignIn = false; + m_bUserRefusedSignIn = false; + + // The UI has closed, so go off and revalidate the state + if ( m_strPostPromptCommand.IsEmpty() == false ) + { + // Run the command again + OnCommand( m_strPostPromptCommand ); + } + } + + RunQueuedCommands(); + } + else if ( notification == SYSTEMNOTIFY_INVITE_SHUTDOWN ) + { + // Quit the current game without confirmation + m_bRestartFromInvite = true; + m_bXUIVisible = true; + OnCommand( "QuitNoConfirm" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Matchmaking notification that a player's info has changed +//----------------------------------------------------------------------------- +void CBasePanel::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ) +{ + CMatchmakingBasePanel *pBase = GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Matchmaking notification to add a session to the browser +//----------------------------------------------------------------------------- +void CBasePanel::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ) +{ + CMatchmakingBasePanel *pBase = GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->SessionSearchResult( searchIdx, pHostData, pResult, ping ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnChangeStorageDevice( void ) +{ + if ( m_bWaitingForStorageDeviceHandle == false ) + { + m_bWaitingForStorageDeviceHandle = true; + m_hStorageDeviceChangeHandle = xboxsystem->CreateAsyncHandle(); + m_iStorageID = XBX_INVALID_STORAGE_ID; + xboxsystem->ShowDeviceSelector( true, &m_iStorageID, &m_hStorageDeviceChangeHandle ); + } +} + +void CBasePanel::OnCreditsFinished( void ) +{ + if ( !IsX360() ) + { + // valid for 360 only + Assert( 0 ); + return; + } + + bool bExitToAppChooser = false; + if ( bExitToAppChooser ) + { + // unknown state from engine, force to a compliant exiting state + // causes an complete exit out of the game back to the app launcher + SetVisible( true ); + m_pGameMenu->SetAlpha( 0 ); + StartExitingProcess(); + } + else + { + // expecting to transition from the credits back to the background map + // prevent any possibility of using the last transition image + m_bUseRenderTargetImage = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::OnGameUIHidden() +{ + if ( m_hOptionsDialog.Get() ) + { + PostMessage( m_hOptionsDialog.Get(), new KeyValues( "GameUIHidden" ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the alpha of the menu panels +//----------------------------------------------------------------------------- +void CBasePanel::SetMenuAlpha(int alpha) +{ + if ( GameUI().IsConsoleUI() ) + { + // handled by animation, not code + return; + } + + m_pGameMenu->SetAlpha(alpha); + + if ( m_pGameLogo ) + { + m_pGameLogo->SetAlpha( alpha ); + } + + for ( int i=0; i<m_pGameMenuButtons.Count(); ++i ) + { + m_pGameMenuButtons[i]->SetAlpha(alpha); + } + m_bForceTitleTextUpdate = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBasePanel::GetMenuAlpha( void ) +{ + return m_pGameMenu->GetAlpha(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::SetMainMenuOverride( vgui::VPANEL panel ) +{ + m_hMainMenuOverridePanel = panel; + + if ( m_pGameMenu ) + { + m_pGameMenu->SetMainMenuOverride( panel ); + } + + if ( m_hMainMenuOverridePanel ) + { + // Parent it to this panel + ipanel()->SetParent( m_hMainMenuOverridePanel, GetVPanel() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: starts the game +//----------------------------------------------------------------------------- +void CBasePanel::FadeToBlackAndRunEngineCommand( const char *engineCommand ) +{ + KeyValues *pKV = new KeyValues( "RunEngineCommand", "command", engineCommand ); + + // execute immediately, with no delay + PostMessage( this, pKV, 0 ); +} + +void CBasePanel::SetMenuItemBlinkingState( const char *itemName, bool state ) +{ + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + CGameMenu *pGameMenu = dynamic_cast<CGameMenu *>(child); + if ( pGameMenu ) + { + pGameMenu->SetMenuItemBlinkingState( itemName, state ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: runs an engine command, used for delays +//----------------------------------------------------------------------------- +void CBasePanel::RunEngineCommand(const char *command) +{ + engine->ClientCmd_Unrestricted(command); +} + +//----------------------------------------------------------------------------- +// Purpose: runs an animation to close a dialog and cleans up after close +//----------------------------------------------------------------------------- +void CBasePanel::RunCloseAnimation( const char *animName ) +{ + RunAnimationWithCallback( this, animName, new KeyValues( "FinishDialogClose" ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: cleans up after a menu closes +//----------------------------------------------------------------------------- +void CBasePanel::FinishDialogClose( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: xbox UI panel that displays button icons and help text for all menus +//----------------------------------------------------------------------------- +CFooterPanel::CFooterPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) +{ + SetVisible( true ); + SetAlpha( 0 ); + m_pHelpName = NULL; + + m_pSizingLabel = new vgui::Label( this, "SizingLabel", "" ); + m_pSizingLabel->SetVisible( false ); + + m_nButtonGap = 32; + m_nButtonGapDefault = 32; + m_ButtonPinRight = 100; + m_FooterTall = 80; + + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if ( tall <= 480 ) + { + m_FooterTall = 60; + } + + m_ButtonOffsetFromTop = 0; + m_ButtonSeparator = 4; + m_TextAdjust = 0; + + m_bPaintBackground = false; + m_bCenterHorizontal = false; + + m_szButtonFont[0] = '\0'; + m_szTextFont[0] = '\0'; + m_szFGColor[0] = '\0'; + m_szBGColor[0] = '\0'; +} + +CFooterPanel::~CFooterPanel() +{ + SetHelpNameAndReset( NULL ); + + delete m_pSizingLabel; +} + +//----------------------------------------------------------------------------- +// Purpose: apply scheme settings +//----------------------------------------------------------------------------- +void CFooterPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_hButtonFont = pScheme->GetFont( ( m_szButtonFont[0] != '\0' ) ? m_szButtonFont : "GameUIButtons" ); + m_hTextFont = pScheme->GetFont( ( m_szTextFont[0] != '\0' ) ? m_szTextFont : "MenuLarge" ); + + SetFgColor( pScheme->GetColor( m_szFGColor, Color( 255, 255, 255, 255 ) ) ); + SetBgColor( pScheme->GetColor( m_szBGColor, Color( 0, 0, 0, 255 ) ) ); + + int x, y, w, h; + GetParent()->GetBounds( x, y, w, h ); + SetBounds( x, h - m_FooterTall, w, m_FooterTall ); +} + +//----------------------------------------------------------------------------- +// Purpose: apply settings +//----------------------------------------------------------------------------- +void CFooterPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + // gap between hints + m_nButtonGap = inResourceData->GetInt( "buttongap", 32 ); + m_nButtonGapDefault = m_nButtonGap; + m_ButtonPinRight = inResourceData->GetInt( "button_pin_right", 100 ); + m_FooterTall = inResourceData->GetInt( "tall", 80 ); + m_ButtonOffsetFromTop = inResourceData->GetInt( "buttonoffsety", 0 ); + m_ButtonSeparator = inResourceData->GetInt( "button_separator", 4 ); + m_TextAdjust = inResourceData->GetInt( "textadjust", 0 ); + + m_bCenterHorizontal = ( inResourceData->GetInt( "center", 0 ) == 1 ); + m_bPaintBackground = ( inResourceData->GetInt( "paintbackground", 0 ) == 1 ); + + // fonts for text and button + Q_strncpy( m_szTextFont, inResourceData->GetString( "fonttext", "MenuLarge" ), sizeof( m_szTextFont ) ); + Q_strncpy( m_szButtonFont, inResourceData->GetString( "fontbutton", "GameUIButtons" ), sizeof( m_szButtonFont ) ); + + // fg and bg colors + Q_strncpy( m_szFGColor, inResourceData->GetString( "fgcolor", "White" ), sizeof( m_szFGColor ) ); + Q_strncpy( m_szBGColor, inResourceData->GetString( "bgcolor", "Black" ), sizeof( m_szBGColor ) ); + + for ( KeyValues *pButton = inResourceData->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() ) + { + const char *pName = pButton->GetName(); + + if ( !Q_stricmp( pName, "button" ) ) + { + // Add a button to the footer + const char *pText = pButton->GetString( "text", "NULL" ); + const char *pIcon = pButton->GetString( "icon", "NULL" ); + AddNewButtonLabel( pText, pIcon ); + } + } + + InvalidateLayout( false, true ); // force ApplySchemeSettings to run +} + +//----------------------------------------------------------------------------- +// Purpose: adds button icons and help text to the footer panel when activating a menu +//----------------------------------------------------------------------------- +void CFooterPanel::AddButtonsFromMap( vgui::Frame *pMenu ) +{ + SetHelpNameAndReset( pMenu->GetName() ); + + CControllerMap *pMap = dynamic_cast<CControllerMap*>( pMenu->FindChildByName( "ControllerMap" ) ); + if ( pMap ) + { + int buttonCt = pMap->NumButtons(); + for ( int i = 0; i < buttonCt; ++i ) + { + const char *pText = pMap->GetBindingText( i ); + if ( pText ) + { + AddNewButtonLabel( pText, pMap->GetBindingIcon( i ) ); + } + } + } +} + +void CFooterPanel::SetStandardDialogButtons() +{ + SetHelpNameAndReset( "Dialog" ); + AddNewButtonLabel( "#GameUI_Action", "#GameUI_Icons_A_BUTTON" ); + AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Caller must tag the button layout. May support reserved names +// to provide stock help layouts trivially. +//----------------------------------------------------------------------------- +void CFooterPanel::SetHelpNameAndReset( const char *pName ) +{ + if ( m_pHelpName ) + { + free( m_pHelpName ); + m_pHelpName = NULL; + } + + if ( pName ) + { + m_pHelpName = strdup( pName ); + } + + ClearButtons(); +} + +//----------------------------------------------------------------------------- +// Purpose: Caller must tag the button layout +//----------------------------------------------------------------------------- +const char *CFooterPanel::GetHelpName() +{ + return m_pHelpName; +} + +void CFooterPanel::ClearButtons( void ) +{ + m_ButtonLabels.PurgeAndDeleteElements(); +} + +//----------------------------------------------------------------------------- +// Purpose: creates a new button label with icon and text +//----------------------------------------------------------------------------- +void CFooterPanel::AddNewButtonLabel( const char *text, const char *icon ) +{ + ButtonLabel_t *button = new ButtonLabel_t; + + Q_strncpy( button->name, text, MAX_PATH ); + button->bVisible = true; + + // Button icons are a single character + wchar_t *pIcon = g_pVGuiLocalize->Find( icon ); + if ( pIcon ) + { + button->icon[0] = pIcon[0]; + button->icon[1] = '\0'; + } + else + { + button->icon[0] = '\0'; + } + + // Set the help text + wchar_t *pText = g_pVGuiLocalize->Find( text ); + if ( pText ) + { + wcsncpy( button->text, pText, wcslen( pText ) + 1 ); + } + else + { + button->text[0] = '\0'; + } + + m_ButtonLabels.AddToTail( button ); +} + +//----------------------------------------------------------------------------- +// Purpose: Shows/Hides a button label +//----------------------------------------------------------------------------- +void CFooterPanel::ShowButtonLabel( const char *name, bool show ) +{ + for ( int i = 0; i < m_ButtonLabels.Count(); ++i ) + { + if ( !Q_stricmp( m_ButtonLabels[ i ]->name, name ) ) + { + m_ButtonLabels[ i ]->bVisible = show; + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Changes a button's text +//----------------------------------------------------------------------------- +void CFooterPanel::SetButtonText( const char *buttonName, const char *text ) +{ + for ( int i = 0; i < m_ButtonLabels.Count(); ++i ) + { + if ( !Q_stricmp( m_ButtonLabels[ i ]->name, buttonName ) ) + { + wchar_t *wtext = g_pVGuiLocalize->Find( text ); + if ( text ) + { + wcsncpy( m_ButtonLabels[ i ]->text, wtext, wcslen( wtext ) + 1 ); + } + else + { + m_ButtonLabels[ i ]->text[ 0 ] = '\0'; + } + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Footer panel background rendering +//----------------------------------------------------------------------------- +void CFooterPanel::PaintBackground( void ) +{ + if ( !m_bPaintBackground ) + return; + + BaseClass::PaintBackground(); +} + +//----------------------------------------------------------------------------- +// Purpose: Footer panel rendering +//----------------------------------------------------------------------------- +void CFooterPanel::Paint( void ) +{ + // inset from right edge + int wide = GetWide(); + int right = wide - m_ButtonPinRight; + + // center the text within the button + int buttonHeight = vgui::surface()->GetFontTall( m_hButtonFont ); + int fontHeight = vgui::surface()->GetFontTall( m_hTextFont ); + int textY = ( buttonHeight - fontHeight )/2 + m_TextAdjust; + + if ( textY < 0 ) + { + textY = 0; + } + + int y = m_ButtonOffsetFromTop; + + if ( !m_bCenterHorizontal ) + { + // draw the buttons, right to left + int x = right; + + for ( int i = 0; i < m_ButtonLabels.Count(); ++i ) + { + ButtonLabel_t *pButton = m_ButtonLabels[i]; + if ( !pButton->bVisible ) + continue; + + // Get the string length + m_pSizingLabel->SetFont( m_hTextFont ); + m_pSizingLabel->SetText( pButton->text ); + m_pSizingLabel->SizeToContents(); + + int iTextWidth = m_pSizingLabel->GetWide(); + + if ( iTextWidth == 0 ) + x += m_nButtonGap; // There's no text, so remove the gap between buttons + else + x -= iTextWidth; + + // Draw the string + vgui::surface()->DrawSetTextFont( m_hTextFont ); + vgui::surface()->DrawSetTextColor( GetFgColor() ); + vgui::surface()->DrawSetTextPos( x, y + textY ); + vgui::surface()->DrawPrintText( pButton->text, wcslen( pButton->text ) ); + + // Draw the button + // back up button width and a little extra to leave a gap between button and text + x -= ( vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] ) + m_ButtonSeparator ); + vgui::surface()->DrawSetTextFont( m_hButtonFont ); + vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 ); + vgui::surface()->DrawSetTextPos( x, y ); + vgui::surface()->DrawPrintText( pButton->icon, 1 ); + + // back up to next string + x -= m_nButtonGap; + } + } + else + { + // center the buttons (as a group) + int x = wide / 2; + int totalWidth = 0; + int i = 0; + int nButtonCount = 0; + + // need to loop through and figure out how wide our buttons and text are (with gaps between) so we can offset from the center + for ( i = 0; i < m_ButtonLabels.Count(); ++i ) + { + ButtonLabel_t *pButton = m_ButtonLabels[i]; + if ( !pButton->bVisible ) + continue; + + // Get the string length + m_pSizingLabel->SetFont( m_hTextFont ); + m_pSizingLabel->SetText( pButton->text ); + m_pSizingLabel->SizeToContents(); + + totalWidth += vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] ); + totalWidth += m_ButtonSeparator; + totalWidth += m_pSizingLabel->GetWide(); + + nButtonCount++; // keep track of how many active buttons we'll be drawing + } + + totalWidth += ( nButtonCount - 1 ) * m_nButtonGap; // add in the gaps between the buttons + x -= ( totalWidth / 2 ); + + for ( i = 0; i < m_ButtonLabels.Count(); ++i ) + { + ButtonLabel_t *pButton = m_ButtonLabels[i]; + if ( !pButton->bVisible ) + continue; + + // Get the string length + m_pSizingLabel->SetFont( m_hTextFont ); + m_pSizingLabel->SetText( pButton->text ); + m_pSizingLabel->SizeToContents(); + + int iTextWidth = m_pSizingLabel->GetWide(); + + // Draw the icon + vgui::surface()->DrawSetTextFont( m_hButtonFont ); + vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 ); + vgui::surface()->DrawSetTextPos( x, y ); + vgui::surface()->DrawPrintText( pButton->icon, 1 ); + x += vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] ) + m_ButtonSeparator; + + // Draw the string + vgui::surface()->DrawSetTextFont( m_hTextFont ); + vgui::surface()->DrawSetTextColor( GetFgColor() ); + vgui::surface()->DrawSetTextPos( x, y + textY ); + vgui::surface()->DrawPrintText( pButton->text, wcslen( pButton->text ) ); + + x += iTextWidth + m_nButtonGap; + } + } +} + +DECLARE_BUILD_FACTORY( CFooterPanel ); + +#ifdef _X360 +//----------------------------------------------------------------------------- +// Purpose: Reload the resource files on the Xbox 360 +//----------------------------------------------------------------------------- +void CBasePanel::Reload_Resources( const CCommand &args ) +{ + m_pConsoleControlSettings->Clear(); + if ( m_pConsoleControlSettings->LoadFromFile( g_pFullFileSystem, "resource/UI/XboxDialogs.res" ) ) + { + m_pConsoleControlSettings->ProcessResolutionKeys( surface()->GetResolutionKey() ); + } +} +#endif + + +// X360TBD: Move into a separate module when completed +CMessageDialogHandler::CMessageDialogHandler() +{ + m_iDialogStackTop = -1; +} +void CMessageDialogHandler::ShowMessageDialog( int nType, vgui::Panel *pOwner ) +{ + int iSimpleFrame = 0; + if ( ModInfo().IsSinglePlayerOnly() ) + { + iSimpleFrame = MD_SIMPLEFRAME; + } + + switch( nType ) + { + case MD_SEARCHING_FOR_GAMES: + CreateMessageDialog( MD_CANCEL|MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_SearchingForGames", + NULL, + "CancelOperation", + pOwner, + true ); + break; + + case MD_CREATING_GAME: + CreateMessageDialog( MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_CreatingGame", + NULL, + NULL, + pOwner, + true ); + break; + + case MD_SESSION_SEARCH_FAILED: + CreateMessageDialog( MD_YESNO|MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_NoGamesFound", + "ShowSessionOptionsDialog", + "ReturnToMainMenu", + pOwner ); + break; + + case MD_SESSION_CREATE_FAILED: + CreateMessageDialog( MD_OK, + NULL, + "#TF_Dlg_CreateFailed", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_SESSION_CONNECTING: + CreateMessageDialog( 0, + NULL, + "#TF_Dlg_Connecting", + NULL, + NULL, + pOwner, + true ); + break; + + case MD_SESSION_CONNECT_NOTAVAILABLE: + CreateMessageDialog( MD_OK, + NULL, + "#TF_Dlg_JoinRefused", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_SESSION_CONNECT_SESSIONFULL: + CreateMessageDialog( MD_OK, + NULL, + "#TF_Dlg_GameFull", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_SESSION_CONNECT_FAILED: + CreateMessageDialog( MD_OK, + NULL, + "#TF_Dlg_JoinFailed", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_LOST_HOST: + CreateMessageDialog( MD_OK|MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_LostHost", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_LOST_SERVER: + CreateMessageDialog( MD_OK|MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_LostServer", + "ReturnToMainMenu", + NULL, + pOwner ); + break; + + case MD_MODIFYING_SESSION: + CreateMessageDialog( MD_RESTRICTPAINT, + NULL, + "#TF_Dlg_ModifyingSession", + NULL, + NULL, + pOwner, + true ); + break; + + case MD_SAVE_BEFORE_QUIT: + CreateMessageDialog( MD_YESNO|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_QuitConfirmationTitle", + "#GameUI_Console_QuitWarning", + "QuitNoConfirm", + "CloseQuitDialog_OpenMainMenu", + pOwner ); + break; + + case MD_QUIT_CONFIRMATION: + CreateMessageDialog( MD_YESNO|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_QuitConfirmationTitle", + "#GameUI_QuitConfirmationText", + "QuitNoConfirm", + "CloseQuitDialog_OpenMainMenu", + pOwner ); + break; + + case MD_QUIT_CONFIRMATION_TF: + CreateMessageDialog( MD_YESNO|MD_RESTRICTPAINT, + "#GameUI_QuitConfirmationTitle", + "#GameUI_QuitConfirmationText", + "QuitNoConfirm", + "CloseQuitDialog_OpenMatchmakingMenu", + pOwner ); + break; + + case MD_DISCONNECT_CONFIRMATION: + CreateMessageDialog( MD_YESNO|MD_RESTRICTPAINT, + "", + "#GameUI_DisconnectConfirmationText", + "DisconnectNoConfirm", + "close_dialog", + pOwner ); + break; + + case MD_DISCONNECT_CONFIRMATION_HOST: + CreateMessageDialog( MD_YESNO|MD_RESTRICTPAINT, + "", + "#GameUI_DisconnectHostConfirmationText", + "DisconnectNoConfirm", + "close_dialog", + pOwner ); + break; + + case MD_KICK_CONFIRMATION: + CreateMessageDialog( MD_YESNO, + "", + "#TF_Dlg_ConfirmKick", + "KickPlayer", + "close_dialog", + pOwner ); + break; + + case MD_CLIENT_KICKED: + CreateMessageDialog( MD_OK|MD_RESTRICTPAINT, + "", + "#TF_Dlg_ClientKicked", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_EXIT_SESSION_CONFIRMATION: + CreateMessageDialog( MD_YESNO, + "", + "#TF_Dlg_ExitSessionText", + "ReturnToMainMenu", + "close_dialog", + pOwner ); + break; + + case MD_STORAGE_DEVICES_NEEDED: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_Console_StorageRemovedTitle", + "#GameUI_Console_StorageNeededBody", + "ShowDeviceSelector", + "QuitNoConfirm", + pOwner ); + break; + + case MD_STORAGE_DEVICES_CHANGED: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_Console_StorageRemovedTitle", + "#GameUI_Console_StorageRemovedBody", + "ShowDeviceSelector", + "clear_storage_deviceID", + pOwner ); + break; + + case MD_STORAGE_DEVICES_TOO_FULL: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_Console_StorageTooFullTitle", + "#GameUI_Console_StorageTooFullBody", + "ShowDeviceSelector", + "StorageDeviceDenied", + pOwner ); + break; + + case MD_PROMPT_STORAGE_DEVICE: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_Console_NoStorageDeviceSelectedTitle", + "#GameUI_Console_NoStorageDeviceSelectedBody", + "ShowDeviceSelector", + "StorageDeviceDenied", + pOwner ); + break; + + case MD_PROMPT_STORAGE_DEVICE_REQUIRED: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|MD_SIMPLEFRAME, + "#GameUI_Console_NoStorageDeviceSelectedTitle", + "#GameUI_Console_StorageDeviceRequiredBody", + "ShowDeviceSelector", + "RequiredStorageDenied", + pOwner ); + break; + + case MD_PROMPT_SIGNIN: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame, + "#GameUI_Console_NoUserProfileSelectedTitle", + "#GameUI_Console_NoUserProfileSelectedBody", + "ShowSignInUI", + "SignInDenied", + pOwner ); + break; + + case MD_PROMPT_SIGNIN_REQUIRED: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_COMMANDAFTERCLOSE|iSimpleFrame, + "#GameUI_Console_NoUserProfileSelectedTitle", + "#GameUI_Console_UserProfileRequiredBody", + "ShowSignInUI", + "RequiredSignInDenied", + pOwner ); + break; + + case MD_NOT_ONLINE_ENABLED: + CreateMessageDialog( MD_YESNO|MD_WARNING, + "", + "#TF_Dlg_NotOnlineEnabled", + "ShowSigninUI", + "close_dialog", + pOwner ); + break; + + case MD_NOT_ONLINE_SIGNEDIN: + CreateMessageDialog( MD_YESNO|MD_WARNING, + "", + "#TF_Dlg_NotOnlineSignedIn", + "ShowSigninUI", + "close_dialog", + pOwner ); + break; + + case MD_DEFAULT_CONTROLS_CONFIRM: + CreateMessageDialog( MD_YESNO|MD_WARNING|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_RestoreDefaults", + "#GameUI_ControllerSettingsText", + "DefaultControls", + "close_dialog", + pOwner ); + break; + + case MD_AUTOSAVE_EXPLANATION: + CreateMessageDialog( MD_OK|MD_WARNING|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_ConfirmNewGame_Title", + "#GameUI_AutoSave_Console_Explanation", + "StartNewGameNoCommentaryExplanation", + NULL, + pOwner ); + break; + + case MD_COMMENTARY_EXPLANATION: + CreateMessageDialog( MD_OK|MD_WARNING|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_CommentaryDialogTitle", + "#GAMEUI_Commentary_Console_Explanation", + "StartNewGameNoCommentaryExplanation", + NULL, + pOwner ); + break; + + case MD_COMMENTARY_EXPLANATION_MULTI: + CreateMessageDialog( MD_OK|MD_WARNING, + "#GameUI_CommentaryDialogTitle", + "#GAMEUI_Commentary_Console_Explanation", + "StartNewGameNoCommentaryExplanation", + NULL, + pOwner ); + break; + + case MD_COMMENTARY_CHAPTER_UNLOCK_EXPLANATION: + CreateMessageDialog( MD_OK|MD_WARNING|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_CommentaryDialogTitle", + "#GameUI_CommentaryUnlock", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_SAVE_BEFORE_LANGUAGE_CHANGE: + CreateMessageDialog( MD_YESNO|MD_WARNING|MD_SIMPLEFRAME|MD_COMMANDAFTERCLOSE|MD_RESTRICTPAINT, + "#GameUI_ChangeLanguageRestart_Title", + "#GameUI_ChangeLanguageRestart_Info", + "AcceptVocalsLanguageChange", + "CancelVocalsLanguageChange", + pOwner ); + + case MD_SAVE_BEFORE_NEW_GAME: + CreateMessageDialog( MD_OKCANCEL|MD_WARNING|iSimpleFrame|MD_COMMANDAFTERCLOSE|MD_RESTRICTPAINT, + "#GameUI_ConfirmNewGame_Title", + "#GameUI_NewGameWarning", + "StartNewGame", + "close_dialog", + pOwner ); + break; + + case MD_SAVE_BEFORE_LOAD: + CreateMessageDialog( MD_OKCANCEL|MD_WARNING|iSimpleFrame|MD_COMMANDAFTERCLOSE|MD_RESTRICTPAINT, + "#GameUI_ConfirmLoadGame_Title", + "#GameUI_LoadWarning", + "LoadGame", + "LoadGameCancelled", + pOwner ); + break; + + case MD_DELETE_SAVE_CONFIRM: + CreateMessageDialog( MD_OKCANCEL|MD_WARNING|iSimpleFrame|MD_COMMANDAFTERCLOSE, + "#GameUI_ConfirmDeleteSaveGame_Title", + "#GameUI_ConfirmDeleteSaveGame_Info", + "DeleteGame", + "DeleteGameCancelled", + pOwner ); + break; + + case MD_SAVE_OVERWRITE: + CreateMessageDialog( MD_OKCANCEL|MD_WARNING|iSimpleFrame|MD_COMMANDAFTERCLOSE, + "#GameUI_ConfirmOverwriteSaveGame_Title", + "#GameUI_ConfirmOverwriteSaveGame_Info", + "SaveGame", + "OverwriteGameCancelled", + pOwner ); + break; + + case MD_SAVING_WARNING: + CreateMessageDialog( MD_WARNING|iSimpleFrame|MD_COMMANDONFORCECLOSE, + "", + "#GameUI_SavingWarning", + "SaveSuccess", + NULL, + pOwner, + true); + break; + + case MD_SAVE_COMPLETE: + CreateMessageDialog( MD_OK|iSimpleFrame|MD_COMMANDAFTERCLOSE, + "#GameUI_ConfirmOverwriteSaveGame_Title", + "#GameUI_GameSaved", + "CloseAndSelectResume", + NULL, + pOwner ); + break; + + case MD_LOAD_FAILED_WARNING: + CreateMessageDialog( MD_OK |MD_WARNING|iSimpleFrame, + "#GameUI_LoadFailed", + "#GameUI_LoadFailed_Description", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_OPTION_CHANGE_FROM_X360_DASHBOARD: + CreateMessageDialog( MD_OK|iSimpleFrame|MD_RESTRICTPAINT, + "#GameUI_SettingChangeFromX360Dashboard_Title", + "#GameUI_SettingChangeFromX360Dashboard_Info", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_STANDARD_SAMPLE: + CreateMessageDialog( MD_OK, + "Standard Dialog", + "This is a standard dialog", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_WARNING_SAMPLE: + CreateMessageDialog( MD_OK | MD_WARNING, + "#GameUI_Dialog_Warning", + "This is a warning dialog", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_ERROR_SAMPLE: + CreateMessageDialog( MD_OK | MD_ERROR, + "Error Dialog", + "This is an error dialog", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_STORAGE_DEVICES_CORRUPT: + CreateMessageDialog( MD_OK | MD_WARNING | iSimpleFrame | MD_RESTRICTPAINT, + "", + "#GameUI_Console_FileCorrupt", + "close_dialog", + NULL, + pOwner ); + break; + + case MD_CHECKING_STORAGE_DEVICE: + CreateMessageDialog( iSimpleFrame | MD_RESTRICTPAINT, + NULL, + "#GameUI_Dlg_CheckingStorageDevice", + NULL, + NULL, + pOwner, + true ); + break; + + default: + break; + } +} + +void CMessageDialogHandler::CloseAllMessageDialogs() +{ + for ( int i = 0; i < MAX_MESSAGE_DIALOGS; ++i ) + { + CMessageDialog *pDlg = m_hMessageDialogs[i]; + if ( pDlg ) + { + vgui::surface()->RestrictPaintToSinglePanel(NULL); + if ( vgui_message_dialog_modal.GetBool() ) + { + vgui::input()->ReleaseAppModalSurface(); + } + + pDlg->Close(); + m_hMessageDialogs[i] = NULL; + } + } +} + +void CMessageDialogHandler::CloseMessageDialog( const uint nType ) +{ + int nStackIdx = 0; + if ( nType & MD_WARNING ) + { + nStackIdx = DIALOG_STACK_IDX_WARNING; + } + else if ( nType & MD_ERROR ) + { + nStackIdx = DIALOG_STACK_IDX_ERROR; + } + + CMessageDialog *pDlg = m_hMessageDialogs[nStackIdx]; + if ( pDlg ) + { + vgui::surface()->RestrictPaintToSinglePanel(NULL); + if ( vgui_message_dialog_modal.GetBool() ) + { + vgui::input()->ReleaseAppModalSurface(); + } + + pDlg->Close(); + m_hMessageDialogs[nStackIdx] = NULL; + } +} + +void CMessageDialogHandler::CreateMessageDialog( const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pCreator, bool bShowActivity /*= false*/ ) +{ + int nStackIdx = 0; + if ( nType & MD_WARNING ) + { + nStackIdx = DIALOG_STACK_IDX_WARNING; + } + else if ( nType & MD_ERROR ) + { + nStackIdx = DIALOG_STACK_IDX_ERROR; + } + + // Can only show one dialog of each type at a time + if ( m_hMessageDialogs[nStackIdx].Get() ) + { + Warning( "Tried to create two dialogs of type %d\n", nStackIdx ); + return; + } + + // Show the new dialog + m_hMessageDialogs[nStackIdx] = new CMessageDialog( BasePanel(), nType, pTitle, pMsg, pCmdA, pCmdB, pCreator, bShowActivity ); + + m_hMessageDialogs[nStackIdx]->SetControlSettingsKeys( BasePanel()->GetConsoleControlSettings()->FindKey( "MessageDialog.res" ) ); + + if ( nType & MD_RESTRICTPAINT ) + { + vgui::surface()->RestrictPaintToSinglePanel( m_hMessageDialogs[nStackIdx]->GetVPanel() ); + } + + ActivateMessageDialog( nStackIdx ); +} + +//----------------------------------------------------------------------------- +// Purpose: Activate a new message dialog +//----------------------------------------------------------------------------- +void CMessageDialogHandler::ActivateMessageDialog( int nStackIdx ) +{ + int x, y, wide, tall; + vgui::surface()->GetWorkspaceBounds( x, y, wide, tall ); + PositionDialog( m_hMessageDialogs[nStackIdx], wide, tall ); + + uint nType = m_hMessageDialogs[nStackIdx]->GetType(); + if ( nType & MD_WARNING ) + { + m_hMessageDialogs[nStackIdx]->SetZPos( 75 ); + } + else if ( nType & MD_ERROR ) + { + m_hMessageDialogs[nStackIdx]->SetZPos( 100 ); + } + + // Make sure the topmost item on the stack still has focus + int idx = MAX_MESSAGE_DIALOGS - 1; + for ( idx; idx >= nStackIdx; --idx ) + { + CMessageDialog *pDialog = m_hMessageDialogs[idx]; + if ( pDialog ) + { + pDialog->Activate(); + if ( vgui_message_dialog_modal.GetBool() ) + { + vgui::input()->SetAppModalSurface( pDialog->GetVPanel() ); + } + m_iDialogStackTop = idx; + break; + } + } +} + +void CMessageDialogHandler::PositionDialogs( int wide, int tall ) +{ + for ( int i = 0; i < MAX_MESSAGE_DIALOGS; ++i ) + { + if ( m_hMessageDialogs[i].Get() ) + { + PositionDialog( m_hMessageDialogs[i], wide, tall ); + } + } +} + +void CMessageDialogHandler::PositionDialog( vgui::PHandle dlg, int wide, int tall ) +{ + int w, t; + dlg->GetSize(w, t); + dlg->SetPos( (wide - w) / 2, (tall - t) / 2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Editable panel that can replace the GameMenuButtons in CBasePanel +//----------------------------------------------------------------------------- +CMainMenuGameLogo::CMainMenuGameLogo( vgui::Panel *parent, const char *name ) : vgui::EditablePanel( parent, name ) +{ + m_nOffsetX = 0; + m_nOffsetY = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainMenuGameLogo::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_nOffsetX = inResourceData->GetInt( "offsetX", 0 ); + m_nOffsetY = inResourceData->GetInt( "offsetY", 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainMenuGameLogo::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues *pConditions = new KeyValues( "conditions" ); + if ( pConditions ) + { + char background[MAX_PATH]; + engine->GetMainMenuBackgroundName( background, sizeof(background) ); + + KeyValues *pSubKey = new KeyValues( background ); + if ( pSubKey ) + { + pConditions->AddSubKey( pSubKey ); + } + } + + LoadControlSettings( "Resource/GameLogo.res", NULL, NULL, pConditions ); + + if ( pConditions ) + { + pConditions->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePanel::CloseBaseDialogs( void ) +{ + if ( m_hNewGameDialog.Get() ) + m_hNewGameDialog->Close(); + + if ( m_hAchievementsDialog.Get() ) + m_hAchievementsDialog->Close(); + + if ( m_hBonusMapsDialog.Get() ) + m_hBonusMapsDialog->Close(); + + if ( m_hControllerDialog.Get() ) + m_hControllerDialog->Close(); + + if ( m_hLoadGameDialog_Xbox.Get() ) + m_hLoadGameDialog_Xbox->Close(); + + if ( m_hOptionsDialog_Xbox.Get() ) + m_hOptionsDialog_Xbox->Close(); + + if ( m_hSaveGameDialog_Xbox.Get() ) + m_hSaveGameDialog_Xbox->Close(); + + if ( m_hLoadCommentaryDialog.Get() ) + m_hLoadCommentaryDialog->Close(); + + if ( m_hCreateMultiplayerGameDialog.Get() ) + m_hCreateMultiplayerGameDialog->Close(); +} + +static void CC_GameUIShowDialog( const CCommand &args ) +{ + int c = args.ArgC(); + + if ( c < 2 ) + { + Msg( "Usage: gamemenucommand <commandname>\n" ); + return; + } + + GameUI().ShowMessageDialog( atoi(args[1]) ); +} +static ConCommand gameui_show_dialog( "gameui_show_dialog", CC_GameUIShowDialog, "Show an arbitrary Dialog.", 0 ); + +static void CC_GameUIHideDialog( const CCommand &args ) +{ + int c = args.ArgC(); + if ( c < 1 ) + { + Msg( "Usage: gamemenucommand <commandname>\n" ); + return; + } + + GameUI().CloseMessageDialog( 0 ); +} +static ConCommand gameui_hide_dialog( "gameui_hide_dialog", CC_GameUIHideDialog, "asdf", 0 ); + +static void RefreshOptionsDialog( const CCommand &args ) +{ + if ( g_hOptionsDialog ) + { + CBasePanel* pBasePanel = (CBasePanel*) g_hOptionsDialog->GetParent(); + g_hOptionsDialog->Close(); + delete g_hOptionsDialog.Get(); + if ( pBasePanel ) + { + pBasePanel->OnOpenOptionsDialog(); + COptionsDialog *pOptionsDialog = dynamic_cast<COptionsDialog*>( g_hOptionsDialog.Get() ); + if ( pOptionsDialog ) + { + pOptionsDialog->GetPropertySheet()->SetActivePage( pOptionsDialog->GetOptionsSubMultiplayer() ); + } + } + } +} +static ConCommand refresh_options_dialog( "refresh_options_dialog", RefreshOptionsDialog, "Refresh the options dialog.", 0 ); diff --git a/gameui/BasePanel.h b/gameui/BasePanel.h new file mode 100644 index 0000000..bb1e9b4 --- /dev/null +++ b/gameui/BasePanel.h @@ -0,0 +1,462 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BASEPANEL_H +#define BASEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Panel.h" +#include "vgui_controls/PHandle.h" +#include "vgui_controls/MenuItem.h" +#include "vgui_controls/MessageDialog.h" +#include "KeyValues.h" +#include "utlvector.h" +#include "tier1/CommandBuffer.h" + +#include "ixboxsystem.h" + +#if !defined( _X360 ) +#include "xbox/xboxstubs.h" +#endif + +enum +{ + DIALOG_STACK_IDX_STANDARD, + DIALOG_STACK_IDX_WARNING, + DIALOG_STACK_IDX_ERROR, +}; + +class CMatchmakingBasePanel; +class CBackgroundMenuButton; +class CGameMenu; +class CAsyncCtxOnDeviceAttached; + +// X360TBD: Move into a separate module when finished +class CMessageDialogHandler +{ +public: + CMessageDialogHandler(); + void ShowMessageDialog( int nType, vgui::Panel *pOwner ); + void CloseMessageDialog( const uint nType = 0 ); + void CloseAllMessageDialogs(); + void CreateMessageDialog( const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pCreator, bool bShowActivity = false ); + void ActivateMessageDialog( int nStackIdx ); + void PositionDialogs( int wide, int tall ); + void PositionDialog( vgui::PHandle dlg, int wide, int tall ); + +private: + static const int MAX_MESSAGE_DIALOGS = 3; + vgui::DHANDLE< CMessageDialog > m_hMessageDialogs[MAX_MESSAGE_DIALOGS]; + int m_iDialogStackTop; +}; + +//----------------------------------------------------------------------------- +// Purpose: Panel that acts as background for button icons and help text in the UI +//----------------------------------------------------------------------------- +class CFooterPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CFooterPanel, vgui::EditablePanel ); + +public: + CFooterPanel( Panel *parent, const char *panelName ); + virtual ~CFooterPanel(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void Paint( void ); + virtual void PaintBackground( void ); + + // caller tags the current hint, used to assist in ownership + void SetHelpNameAndReset( const char *pName ); + const char *GetHelpName(); + + void AddButtonsFromMap( vgui::Frame *pMenu ); + void SetStandardDialogButtons(); + void AddNewButtonLabel( const char *text, const char *icon ); + void ShowButtonLabel( const char *name, bool show = true ); + void SetButtonText( const char *buttonName, const char *text ); + void ClearButtons(); + void SetButtonGap( int nButtonGap ){ m_nButtonGap = nButtonGap; } + void UseDefaultButtonGap(){ m_nButtonGap = m_nButtonGapDefault; } + +private: + struct ButtonLabel_t + { + bool bVisible; + char name[MAX_PATH]; + wchar_t text[MAX_PATH]; + wchar_t icon[2]; // icon is a single character + }; + + CUtlVector< ButtonLabel_t* > m_ButtonLabels; + + vgui::Label *m_pSizingLabel; // used to measure font sizes + + bool m_bPaintBackground; // fill the background? + bool m_bCenterHorizontal; // center buttons horizontally? + int m_ButtonPinRight; // if not centered, this is the distance from the right margin that we use to start drawing buttons (right to left) + int m_nButtonGap; // space between buttons when drawing + int m_nButtonGapDefault; // space between buttons (initial value) + int m_FooterTall; // height of the footer + int m_ButtonOffsetFromTop; // how far below the top the buttons should be drawn + int m_ButtonSeparator; // space between the button icon and text + int m_TextAdjust; // extra adjustment for the text (vertically)...text is centered on the button icon and then this value is applied + + char m_szTextFont[64]; // font for the button text + char m_szButtonFont[64]; // font for the button icon + char m_szFGColor[64]; // foreground color (text) + char m_szBGColor[64]; // background color (fill color) + + vgui::HFont m_hButtonFont; + vgui::HFont m_hTextFont; + char *m_pHelpName; +}; + +//----------------------------------------------------------------------------- +// Purpose: EditablePanel that can replace the GameMenuButtons in CBasePanel +//----------------------------------------------------------------------------- +class CMainMenuGameLogo : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CMainMenuGameLogo, vgui::EditablePanel ); +public: + CMainMenuGameLogo( vgui::Panel *parent, const char *name ); + + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + int GetOffsetX(){ return m_nOffsetX; } + int GetOffsetY(){ return m_nOffsetY; } + +private: + int m_nOffsetX; + int m_nOffsetY; +}; + +//----------------------------------------------------------------------------- +// Purpose: Transparent menu item designed to sit on the background ingame +//----------------------------------------------------------------------------- +class CGameMenuItem : public vgui::MenuItem +{ + DECLARE_CLASS_SIMPLE( CGameMenuItem, vgui::MenuItem ); +public: + CGameMenuItem(vgui::Menu *parent, const char *name); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PaintBackground( void ); + void SetRightAlignedText( bool state ); + +private: + bool m_bRightAligned; +}; + +//----------------------------------------------------------------------------- +// Purpose: This is the panel at the top of the panel hierarchy for GameUI +// It handles all the menus, background images, and loading dialogs +//----------------------------------------------------------------------------- +class CBasePanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CBasePanel, vgui::Panel ); + +public: + CBasePanel(); + virtual ~CBasePanel(); + +public: + // + // Implementation of async jobs + // An async job is enqueued by calling "ExecuteAsync" with the proper job context. + // Job's function "ExecuteAsync" is called on a separate thread. + // After the job finishes the "Completed" function is called on the + // main thread. + // + class CAsyncJobContext + { + public: + CAsyncJobContext( float flLeastExecuteTime = 0.0f ) : m_flLeastExecuteTime( flLeastExecuteTime ), m_hThreadHandle( NULL ) {} + virtual ~CAsyncJobContext() {} + + virtual void ExecuteAsync() = 0; // Executed on the secondary thread + virtual void Completed() = 0; // Executed on the main thread + + public: + void * volatile m_hThreadHandle; // Handle to an async job thread waiting for + float m_flLeastExecuteTime; // Least amount of time this job should keep executing + }; + + CAsyncJobContext *m_pAsyncJob; + void ExecuteAsync( CAsyncJobContext *pAsync ); + + +public: + // notifications + void OnLevelLoadingStarted(); + void OnLevelLoadingFinished(); + + // update the taskbar a frame + void RunFrame(); + + // fades to black then runs an engine command (usually to start a level) + void FadeToBlackAndRunEngineCommand( const char *engineCommand ); + + // sets the blinking state of a menu item + void SetMenuItemBlinkingState( const char *itemName, bool state ); + + // handles gameUI being shown + void OnGameUIActivated(); + + // game dialogs + void OnOpenNewGameDialog( const char *chapter = NULL ); + void OnOpenBonusMapsDialog(); + void OnOpenLoadGameDialog(); + void OnOpenLoadGameDialog_Xbox(); + void OnOpenSaveGameDialog(); + void OnOpenSaveGameDialog_Xbox(); + void OnOpenServerBrowser(); + void OnOpenFriendsDialog(); + void OnOpenDemoDialog(); + void OnOpenCreateMultiplayerGameDialog(); + void OnOpenQuitConfirmationDialog(); + void OnOpenDisconnectConfirmationDialog(); + void OnOpenChangeGameDialog(); + void OnOpenPlayerListDialog(); + void OnOpenBenchmarkDialog(); + void OnOpenOptionsDialog(); + void OnOpenOptionsDialog_Xbox(); + void OnOpenLoadCommentaryDialog(); + void OpenLoadSingleplayerCommentaryDialog(); + void OnOpenAchievementsDialog(); + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Specific code for CS Achievements Display + //============================================================================= + + // $TODO(HPE): Move this to a game-specific location + void OnOpenCSAchievementsDialog(); + + //============================================================================= + // HPE_END + //============================================================================= + + void OnOpenAchievementsDialog_Xbox(); + void OnOpenControllerDialog(); + + // Xbox 360 + CMatchmakingBasePanel* GetMatchmakingBasePanel(); + void OnOpenMatchmakingBasePanel(); + void SessionNotification( const int notification, const int param = 0 ); + void SystemNotification( const int notification ); + void ShowMessageDialog( const uint nType, vgui::Panel *pParent = NULL ); + void CloseMessageDialog( const uint nType ); + void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ); + void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ); + void OnChangeStorageDevice(); + bool ValidateStorageDevice(); + bool ValidateStorageDevice( int *pStorageDeviceValidated ); + void OnCreditsFinished(); + + KeyValues *GetConsoleControlSettings( void ); + + // forces any changed options dialog settings to be applied immediately, if it's open + void ApplyOptionsDialogSettings(); + + vgui::AnimationController *GetAnimationController( void ) { return m_pConsoleAnimationController; } + void RunCloseAnimation( const char *animName ); + void RunAnimationWithCallback( vgui::Panel *parent, const char *animName, KeyValues *msgFunc ); + void PositionDialog( vgui::PHandle dlg ); + + virtual void OnSizeChanged( int newWide, int newTall ); + + void ArmFirstMenuItem( void ); + + void OnGameUIHidden(); + + void CloseBaseDialogs( void ); + bool IsWaitingForConsoleUI( void ) { return m_bWaitingForStorageDeviceHandle || m_bWaitingForUserSignIn || m_bXUIVisible; } + +#if defined( _X360 ) + CON_COMMAND_MEMBER_F( CBasePanel, "gameui_reload_resources", Reload_Resources, "Reload the Xbox 360 UI res files", 0 ); +#endif + + int GetMenuAlpha( void ); + + void SetMainMenuOverride( vgui::VPANEL panel ); + + + +protected: + virtual void PaintBackground(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +public: + // FIXME: This should probably become a friend relationship between the classes + bool HandleSignInRequest( const char *command ); + bool HandleStorageDeviceRequest( const char *command ); + void ClearPostPromptCommand( const char *pCompletedCommand ); + +private: + enum EBackgroundState + { + BACKGROUND_INITIAL, + BACKGROUND_LOADING, + BACKGROUND_MAINMENU, + BACKGROUND_LEVEL, + BACKGROUND_DISCONNECTED, + BACKGROUND_EXITING, // Console has started an exiting state, cannot be stopped + }; + void SetBackgroundRenderState(EBackgroundState state); + + friend class CAsyncCtxOnDeviceAttached; + void OnDeviceAttached( void ); + void OnCompletedAsyncDeviceAttached( CAsyncCtxOnDeviceAttached *job ); + + void IssuePostPromptCommand( void ); + + void UpdateBackgroundState(); + + // sets the menu alpha [0..255] + void SetMenuAlpha(int alpha); + + // menu manipulation + void CreatePlatformMenu(); + void CreateGameMenu(); + void CreateGameLogo(); + void CheckBonusBlinkState(); + void UpdateGameMenus(); + CGameMenu *RecursiveLoadGameMenu(KeyValues *datafile); + + void StartExitingProcess(); + + bool IsPromptableCommand( const char *command ); + bool CommandRequiresSignIn( const char *command ); + bool CommandRequiresStorageDevice( const char *command ); + bool CommandRespectsSignInDenied( const char *command ); + + void QueueCommand( const char *pCommand ); + void RunQueuedCommands(); + void ClearQueuedCommands(); + + virtual void OnCommand(const char *command); + virtual void PerformLayout(); + MESSAGE_FUNC_INT( OnActivateModule, "ActivateModule", moduleIndex); + + void UpdateRichPresenceInfo(); + + // menu logo + CMainMenuGameLogo *m_pGameLogo; + + // menu buttons + CUtlVector< CBackgroundMenuButton * >m_pGameMenuButtons; + CGameMenu *m_pGameMenu; + bool m_bPlatformMenuInitialized; + int m_iGameMenuInset; + + vgui::VPANEL m_hMainMenuOverridePanel; + + struct coord { + int x; + int y; + }; + CUtlVector< coord > m_iGameTitlePos; + coord m_iGameMenuPos; + + // base dialogs + vgui::DHANDLE<vgui::Frame> m_hNewGameDialog; + vgui::DHANDLE<vgui::Frame> m_hBonusMapsDialog; + vgui::DHANDLE<vgui::Frame> m_hLoadGameDialog; + vgui::DHANDLE<vgui::Frame> m_hLoadGameDialog_Xbox; + vgui::DHANDLE<vgui::Frame> m_hSaveGameDialog; + vgui::DHANDLE<vgui::Frame> m_hSaveGameDialog_Xbox; + vgui::DHANDLE<vgui::PropertyDialog> m_hOptionsDialog; + vgui::DHANDLE<vgui::Frame> m_hOptionsDialog_Xbox; + vgui::DHANDLE<vgui::Frame> m_hCreateMultiplayerGameDialog; + //vgui::DHANDLE<vgui::Frame> m_hDemoPlayerDialog; + vgui::DHANDLE<vgui::Frame> m_hChangeGameDialog; + vgui::DHANDLE<vgui::Frame> m_hPlayerListDialog; + vgui::DHANDLE<vgui::Frame> m_hBenchmarkDialog; + vgui::DHANDLE<vgui::Frame> m_hLoadCommentaryDialog; + vgui::DHANDLE<vgui::Frame> m_hAchievementsDialog; + + // Xbox 360 + vgui::DHANDLE<vgui::Frame> m_hMatchmakingBasePanel; + vgui::DHANDLE<vgui::Frame> m_hControllerDialog; + + EBackgroundState m_eBackgroundState; + + CMessageDialogHandler m_MessageDialogHandler; + CUtlVector< CUtlString > m_CommandQueue; + + vgui::AnimationController *m_pConsoleAnimationController; + KeyValues *m_pConsoleControlSettings; + + void DrawBackgroundImage(); + int m_iBackgroundImageID; + int m_iRenderTargetImageID; + int m_iLoadingImageID; + int m_iProductImageID; + bool m_bLevelLoading; + bool m_bEverActivated; + bool m_bCopyFrameBuffer; + bool m_bUseRenderTargetImage; + int m_ExitingFrameCount; + bool m_bXUIVisible; + bool m_bUseMatchmaking; + bool m_bRestartFromInvite; + bool m_bRestartSameGame; + + // Used for internal state dealing with blades + bool m_bUserRefusedSignIn; + bool m_bUserRefusedStorageDevice; + bool m_bWaitingForUserSignIn; + bool m_bStorageBladeShown; + CUtlString m_strPostPromptCommand; + + // Storage device changing vars + bool m_bWaitingForStorageDeviceHandle; + bool m_bNeedStorageDeviceHandle; + AsyncHandle_t m_hStorageDeviceChangeHandle; + uint m_iStorageID; + int *m_pStorageDeviceValidatedNotify; + + // background transition + bool m_bFadingInMenus; + float m_flFadeMenuStartTime; + float m_flFadeMenuEndTime; + + bool m_bRenderingBackgroundTransition; + float m_flTransitionStartTime; + float m_flTransitionEndTime; + + // Used for rich presence updates on xbox360 + bool m_bSinglePlayer; + uint m_iGameID; // matches context value in hl2orange.spa.h + + // background fill transition + bool m_bHaveDarkenedBackground; + bool m_bHaveDarkenedTitleText; + bool m_bForceTitleTextUpdate; + float m_flFrameFadeInTime; + Color m_BackdropColor; + CPanelAnimationVar( float, m_flBackgroundFillAlpha, "m_flBackgroundFillAlpha", "0" ); + + // fading to game + MESSAGE_FUNC_CHARPTR( RunEngineCommand, "RunEngineCommand", command ); + MESSAGE_FUNC( FinishDialogClose, "FinishDialogClose" ); + +public: + MESSAGE_FUNC_CHARPTR( RunMenuCommand, "RunMenuCommand", command ); +}; + +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +extern CBasePanel *BasePanel(); + + +#endif // BASEPANEL_H diff --git a/gameui/BaseSaveGameDialog.cpp b/gameui/BaseSaveGameDialog.cpp new file mode 100644 index 0000000..99f7fc1 --- /dev/null +++ b/gameui/BaseSaveGameDialog.cpp @@ -0,0 +1,668 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "BaseSaveGameDialog.h" +#include "filesystem.h" +#include "savegame_version.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Button.h" +#include "tier1/utlbuffer.h" +#include <stdio.h> +#include <stdlib.h> +#include "filesystem.h" + +#include "MouseMessageForwardingPanel.h" +#include "TGAImagePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define TGA_IMAGE_PANEL_WIDTH 180 +#define TGA_IMAGE_PANEL_HEIGHT 100 + +#define MAX_LISTED_SAVE_GAMES 128 + +//----------------------------------------------------------------------------- +// Purpose: Describes the layout of a same game pic +//----------------------------------------------------------------------------- +class CSaveGamePanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CSaveGamePanel, vgui::EditablePanel ); +public: + CSaveGamePanel( PanelListPanel *parent, const char *name, int saveGameListItemID ) : BaseClass( parent, name ) + { + m_iSaveGameListItemID = saveGameListItemID; + m_pParent = parent; + m_pSaveGameImage = new CTGAImagePanel( this, "SaveGameImage" ); + m_pAutoSaveImage = new ImagePanel( this, "AutoSaveImage" ); + m_pSaveGameScreenshotBackground = new ImagePanel( this, "SaveGameScreenshotBackground" ); + m_pChapterLabel = new Label( this, "ChapterLabel", "" ); + m_pTypeLabel = new Label( this, "TypeLabel", "" ); + m_pElapsedTimeLabel = new Label( this, "ElapsedTimeLabel", "" ); + m_pFileTimeLabel = new Label( this, "FileTimeLabel", "" ); + + CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL); + panel->SetZPos(2); + + SetSize( 200, 140 ); + + LoadControlSettings( "resource/SaveGamePanel.res" ); + + m_FillColor = m_pSaveGameScreenshotBackground->GetFillColor(); + } + + void SetSaveGameInfo( SaveGameDescription_t &save ) + { + // set the bitmap to display + char tga[_MAX_PATH]; + Q_strncpy( tga, save.szFileName, sizeof(tga) ); + char *ext = strstr( tga, ".sav" ); + if ( ext ) + { + strcpy( ext, ".tga" ); + } + + // If a TGA file exists then it is a user created savegame + if ( g_pFullFileSystem->FileExists( tga ) ) + { + m_pSaveGameImage->SetTGA( tga ); + } + // If there is no TGA then it is either an autosave or the user TGA file has been deleted + else + { + m_pSaveGameImage->SetVisible( false ); + m_pAutoSaveImage->SetVisible( true ); + m_pAutoSaveImage->SetImage( "resource\\autosave" ); + } + + // set the title text + m_pChapterLabel->SetText( save.szComment ); + + // type + SetControlString( "TypeLabel", save.szType ); + SetControlString( "ElapsedTimeLabel", save.szElapsedTime ); + SetControlString( "FileTimeLabel", save.szFileTime ); + } + + MESSAGE_FUNC_INT( OnPanelSelected, "PanelSelected", state ) + { + if ( state ) + { + // set the text color to be orange, and the pic border to be orange + m_pSaveGameScreenshotBackground->SetFillColor( m_SelectedColor ); + m_pChapterLabel->SetFgColor( m_SelectedColor ); + m_pTypeLabel->SetFgColor( m_SelectedColor ); + m_pElapsedTimeLabel->SetFgColor( m_SelectedColor ); + m_pFileTimeLabel->SetFgColor( m_SelectedColor ); + } + else + { + m_pSaveGameScreenshotBackground->SetFillColor( m_FillColor ); + m_pChapterLabel->SetFgColor( m_TextColor ); + m_pTypeLabel->SetFgColor( m_TextColor ); + m_pElapsedTimeLabel->SetFgColor( m_TextColor ); + m_pFileTimeLabel->SetFgColor( m_TextColor ); + } + + PostMessage( m_pParent->GetVParent(), new KeyValues("PanelSelected") ); + } + + virtual void OnMousePressed( vgui::MouseCode code ) + { + m_pParent->SetSelectedPanel( this ); + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) ); + m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + BaseClass::ApplySchemeSettings( pScheme ); + } + + virtual void OnMouseDoublePressed( vgui::MouseCode code ) + { + // call the panel + OnMousePressed( code ); + PostMessage( m_pParent->GetParent(), new KeyValues("Command", "command", "loadsave") ); + } + + int GetSaveGameListItemID() + { + return m_iSaveGameListItemID; + } + +private: + vgui::PanelListPanel *m_pParent; + vgui::Label *m_pChapterLabel; + CTGAImagePanel *m_pSaveGameImage; + ImagePanel *m_pAutoSaveImage; + + // things to change color when the selection changes + ImagePanel *m_pSaveGameScreenshotBackground; + Label *m_pTypeLabel; + Label *m_pElapsedTimeLabel; + Label *m_pFileTimeLabel; + Color m_TextColor, m_FillColor, m_SelectedColor; + + int m_iSaveGameListItemID; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBaseSaveGameDialog::CBaseSaveGameDialog( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) +{ + CreateSavedGamesList(); + ScanSavedGames(); + + m_pLoadButton = new vgui::Button( this, "loadsave", "" ); + SetControlEnabled( "loadsave", false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates the load game display list +//----------------------------------------------------------------------------- +void CBaseSaveGameDialog::CreateSavedGamesList() +{ + m_pGameList = new vgui::PanelListPanel( this, "listpanel_loadgame" ); + m_pGameList->SetFirstColumnWidth( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the save file name of the selected item +//----------------------------------------------------------------------------- +int CBaseSaveGameDialog::GetSelectedItemSaveIndex() +{ + CSaveGamePanel *panel = dynamic_cast<CSaveGamePanel *>(m_pGameList->GetSelectedPanel()); + if ( panel ) + { + // find the panel in the list + for ( int i = 0; i < m_SaveGames.Count(); i++ ) + { + if ( i == panel->GetSaveGameListItemID() ) + { + return i; + } + } + } + return m_SaveGames.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: builds save game list from directory +//----------------------------------------------------------------------------- +void CBaseSaveGameDialog::ScanSavedGames() +{ + // populate list box with all saved games on record: + char szDirectory[_MAX_PATH]; + Q_snprintf( szDirectory, sizeof( szDirectory ), "save/*.sav" ); + + // clear the current list + m_pGameList->DeleteAllItems(); + m_SaveGames.RemoveAll(); + + // iterate the saved files + FileFindHandle_t handle; + const char *pFileName = g_pFullFileSystem->FindFirst( szDirectory, &handle ); + while (pFileName) + { + if ( !Q_strnicmp(pFileName, "HLSave", strlen( "HLSave" ) ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + char szFileName[_MAX_PATH]; + Q_snprintf(szFileName, sizeof( szFileName ), "save/%s", pFileName); + + // Only load save games from the current mod's save dir + if( !g_pFullFileSystem->FileExists( szFileName, "MOD" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + SaveGameDescription_t save; + if ( ParseSaveData( szFileName, pFileName, save ) ) + { + m_SaveGames.AddToTail( save ); + } + + pFileName = g_pFullFileSystem->FindNext( handle ); + } + + g_pFullFileSystem->FindClose( handle ); + + // notify derived classes that save games are being scanned (so they can insert their own) + OnScanningSaveGames(); + + // sort the save list + qsort( m_SaveGames.Base(), m_SaveGames.Count(), sizeof(SaveGameDescription_t), &SaveGameSortFunc ); + + // add to the list + for ( int saveIndex = 0; saveIndex < m_SaveGames.Count() && saveIndex < MAX_LISTED_SAVE_GAMES; saveIndex++ ) + { + // add the item to the panel + AddSaveGameItemToList( saveIndex ); + } + + // display a message if there are no save games + if ( !m_SaveGames.Count() ) + { + vgui::Label *pNoSavesLabel = SETUP_PANEL(new Label(m_pGameList, "NoSavesLabel", "#GameUI_NoSaveGamesToDisplay")); + pNoSavesLabel->SetTextColorState(vgui::Label::CS_DULL); + m_pGameList->AddItem( NULL, pNoSavesLabel ); + } + + SetControlEnabled( "loadsave", false ); + SetControlEnabled( "delete", false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds an item to the list +//----------------------------------------------------------------------------- +void CBaseSaveGameDialog::AddSaveGameItemToList( int saveIndex ) +{ + // create the new panel and add to the list + CSaveGamePanel *saveGamePanel = new CSaveGamePanel( m_pGameList, "SaveGamePanel", saveIndex ); + saveGamePanel->SetSaveGameInfo( m_SaveGames[saveIndex] ); + m_pGameList->AddItem( NULL, saveGamePanel ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the save game info out of the .sav file header +//----------------------------------------------------------------------------- +bool CBaseSaveGameDialog::ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t &save ) +{ + char szMapName[SAVEGAME_MAPNAME_LEN]; + char szComment[SAVEGAME_COMMENT_LEN]; + char szElapsedTime[SAVEGAME_ELAPSED_LEN]; + + if ( !pszFileName || !pszShortName ) + return false; + + Q_strncpy( save.szShortName, pszShortName, sizeof(save.szShortName) ); + Q_strncpy( save.szFileName, pszFileName, sizeof(save.szFileName) ); + + FileHandle_t fh = g_pFullFileSystem->Open( pszFileName, "rb", "MOD" ); + if (fh == FILESYSTEM_INVALID_HANDLE) + return false; + + int readok = SaveReadNameAndComment( fh, szMapName, ARRAYSIZE(szMapName), szComment, ARRAYSIZE(szComment) ); + g_pFullFileSystem->Close(fh); + + if ( !readok ) + { + return false; + } + + Q_strncpy( save.szMapName, szMapName, sizeof(save.szMapName) ); + + // Elapsed time is the last 6 characters in comment. (mmm:ss) + int i; + i = strlen( szComment ); + Q_strncpy( szElapsedTime, "??", sizeof( szElapsedTime ) ); + if (i >= 6) + { + Q_strncpy( szElapsedTime, (char *)&szComment[i - 6], 7 ); + szElapsedTime[6] = '\0'; + + // parse out + int minutes = atoi( szElapsedTime ); + int seconds = atoi( szElapsedTime + 4); + + // reformat + if ( minutes ) + { + Q_snprintf( szElapsedTime, sizeof(szElapsedTime), "%d %s %d seconds", minutes, minutes > 1 ? "minutes" : "minute", seconds ); + } + else + { + Q_snprintf( szElapsedTime, sizeof(szElapsedTime), "%d seconds", seconds ); + } + + // Chop elapsed out of comment. + int n; + + n = i - 6; + szComment[n] = '\0'; + + n--; + + // Strip back the spaces at the end. + while ((n >= 1) && + szComment[n] && + szComment[n] == ' ') + { + szComment[n--] = '\0'; + } + } + + // calculate the file name to print + const char *pszType = ""; + if (strstr(pszFileName, "quick")) + { + pszType = "#GameUI_QuickSave"; + } + else if (strstr(pszFileName, "autosave")) + { + pszType = "#GameUI_AutoSave"; + } + + Q_strncpy( save.szType, pszType, sizeof(save.szType) ); + Q_strncpy( save.szComment, szComment, sizeof(save.szComment) ); + Q_strncpy( save.szElapsedTime, szElapsedTime, sizeof(save.szElapsedTime) ); + + // Now get file time stamp. + long fileTime = g_pFullFileSystem->GetFileTime(pszFileName); + char szFileTime[32]; + g_pFullFileSystem->FileTimeToString(szFileTime, sizeof(szFileTime), fileTime); + char *newline = strstr(szFileTime, "\n"); + if (newline) + { + *newline = 0; + } + Q_strncpy( save.szFileTime, szFileTime, sizeof(save.szFileTime) ); + save.iTimestamp = fileTime; + return true; +} + +void CBaseSaveGameDialog::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_ESCAPE ) + { + OnCommand( "Close" ); + return; + } + + BaseClass::OnKeyCodeTyped( code ); +} + +void CBaseSaveGameDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_B ) + { + OnCommand( "Close" ); + return; + } + else if ( code == KEY_XSTICK1_DOWN || + code == KEY_XSTICK2_DOWN || + code == KEY_XBUTTON_DOWN || + code == KEY_DOWN ) + { + if ( m_pGameList->GetItemCount() ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( !pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) ); + m_pGameList->ScrollToItem( m_pGameList->FirstItem() ); + return; + } + else + { + int nNextPanelID = m_pGameList->FirstItem(); + while ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel ) + { + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + if ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nNextPanelID ) ); + m_pGameList->ScrollToItem( nNextPanelID ); + return; + } + + break; + } + + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + } + } + } + } + else if ( code == KEY_XSTICK1_UP || + code == KEY_XSTICK2_UP || + code == KEY_XBUTTON_UP || + code == KEY_UP ) + { + if ( m_pGameList->GetItemCount() ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( !pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) ); + m_pGameList->ScrollToItem( m_pGameList->FirstItem() ); + return; + } + else + { + int nNextPanelID = m_pGameList->FirstItem(); + if ( m_pGameList->GetItemPanel( nNextPanelID ) != pSelectedPanel ) + { + while ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + int nOldPanelID = nNextPanelID; + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + + if ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nOldPanelID ) ); + m_pGameList->ScrollToItem( nOldPanelID ); + return; + } + } + } + } + } + } + } + else if ( code == KEY_ENTER || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( pSelectedPanel ) + { + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + ConVarRef var( "joystick" ); + if ( var.IsValid() && !var.GetBool() ) + { + var.SetValue( true ); + } + + ConVarRef var2( "hud_fastswitch" ); + if ( var2.IsValid() && var2.GetInt() != 2 ) + { + var2.SetValue( 2 ); + } + } + + m_pLoadButton->DoClick(); + return; + } + } + + BaseClass::OnKeyCodePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: timestamp sort function for savegames +//----------------------------------------------------------------------------- +int CBaseSaveGameDialog::SaveGameSortFunc( const void *lhs, const void *rhs ) +{ + const SaveGameDescription_t *s1 = (const SaveGameDescription_t *)lhs; + const SaveGameDescription_t *s2 = (const SaveGameDescription_t *)rhs; + + if (s1->iTimestamp < s2->iTimestamp) + return 1; + else if (s1->iTimestamp > s2->iTimestamp) + return -1; + + // timestamps are equal, so just sort by filename + return strcmp(s1->szFileName, s2->szFileName); +} + +#define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) ) + +int SaveReadNameAndComment( FileHandle_t f, OUT_Z_CAP(nameSize) char *name, int nameSize, OUT_Z_CAP(commentSize) char *comment, int commentSize ) +{ + int i, tag, size, tokenSize, tokenCount; + char *pSaveData, *pFieldName, **pTokenList; + + name[0] = '\0'; + comment[0] = '\0'; + + g_pFullFileSystem->Read( &tag, sizeof(int), f ); + if ( tag != MAKEID('J','S','A','V') ) + { + return 0; + } + + g_pFullFileSystem->Read( &tag, sizeof(int), f ); + if ( tag != SAVEGAME_VERSION ) // Enforce version for now + { + return 0; + } + + g_pFullFileSystem->Read( &size, sizeof(int), f ); + + g_pFullFileSystem->Read( &tokenCount, sizeof(int), f ); // These two ints are the token list + g_pFullFileSystem->Read( &tokenSize, sizeof(int), f ); + size += tokenSize; + + // Sanity Check. + if ( tokenCount < 0 || tokenCount > 1024*1024*32 ) + { + return 0; + } + + if ( tokenSize < 0 || tokenSize > 1024*1024*32 ) + { + return 0; + } + + pSaveData = (char *)new char[size]; + g_pFullFileSystem->Read(pSaveData, size, f); + + int nNumberOfFields; + + char *pData; + int nFieldSize; + + pData = pSaveData; + + // Allocate a table for the strings, and parse the table + if ( tokenSize > 0 ) + { + pTokenList = new char *[tokenCount]; + + // Make sure the token strings pointed to by the pToken hashtable. + for( i=0; i<tokenCount; i++ ) + { + pTokenList[i] = *pData ? pData : NULL; // Point to each string in the pToken table + while( *pData++ ); // Find next token (after next null) + } + } + else + pTokenList = NULL; + + // short, short (size, index of field name) + nFieldSize = *(short *)pData; + pData += sizeof(short); + pFieldName = pTokenList[ *(short *)pData ]; + + if (stricmp(pFieldName, "GameHeader")) + { + delete[] pSaveData; + return 0; + }; + + // int (fieldcount) + pData += sizeof(short); + nNumberOfFields = *(int*)pData; + pData += nFieldSize; + + // Each field is a short (size), short (index of name), binary string of "size" bytes (data) + for (i = 0; i < nNumberOfFields; i++) + { + // Data order is: + // Size + // szName + // Actual Data + + nFieldSize = *(short *)pData; + pData += sizeof(short); + + pFieldName = pTokenList[ *(short *)pData ]; + pData += sizeof(short); + + if (!stricmp(pFieldName, "comment")) + { + int copySize = MAX(commentSize, nFieldSize); + Q_strncpy(comment, pData, copySize); + } + else if (!stricmp(pFieldName, "mapName")) + { + int copySize = MAX(nameSize, nFieldSize); + Q_strncpy(name, pData, copySize); + }; + + // Move to Start of next field. + pData += nFieldSize; + }; + + // Delete the string table we allocated + delete[] pTokenList; + delete[] pSaveData; + + if (strlen(name) > 0 && strlen(comment) > 0) + return 1; + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an existing save game +//----------------------------------------------------------------------------- +void CBaseSaveGameDialog::DeleteSaveGame( const char *fileName ) +{ + if ( !fileName || !fileName[0] ) + return; + + // delete the save game file + g_pFullFileSystem->RemoveFile( fileName, "MOD" ); + + // delete the associated tga + char tga[_MAX_PATH]; + Q_strncpy( tga, fileName, sizeof(tga) ); + char *ext = strstr( tga, ".sav" ); + if ( ext ) + { + strcpy( ext, ".tga" ); + } + g_pFullFileSystem->RemoveFile( tga, "MOD" ); +} + +//----------------------------------------------------------------------------- +// Purpose: One item has been selected +//----------------------------------------------------------------------------- +void CBaseSaveGameDialog::OnPanelSelected() +{ + SetControlEnabled( "loadsave", true ); + SetControlEnabled( "delete", true ); +} + + diff --git a/gameui/BaseSaveGameDialog.h b/gameui/BaseSaveGameDialog.h new file mode 100644 index 0000000..43ac6b4 --- /dev/null +++ b/gameui/BaseSaveGameDialog.h @@ -0,0 +1,81 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BASESAVEGAMEDIALOG_H +#define BASESAVEGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "vgui/MouseCode.h" +#include "KeyValues.h" +#include "utlvector.h" + + +#define SAVEGAME_MAPNAME_LEN 32 +#define SAVEGAME_COMMENT_LEN 80 +#define SAVEGAME_ELAPSED_LEN 32 + +namespace vgui +{ + class Button; +}; + + +struct SaveGameDescription_t +{ + char szShortName[64]; + char szFileName[128]; + char szMapName[SAVEGAME_MAPNAME_LEN]; + char szComment[SAVEGAME_COMMENT_LEN]; + char szType[64]; + char szElapsedTime[SAVEGAME_ELAPSED_LEN]; + char szFileTime[32]; + unsigned int iTimestamp; + unsigned int iSize; +}; + + +int SaveReadNameAndComment( FileHandle_t f, OUT_Z_CAP(nameSize) char *name, int nameSize, OUT_Z_CAP(commentSize) char *comment, int commentSize ); + + +//----------------------------------------------------------------------------- +// Purpose: Base class for save & load game dialogs +//----------------------------------------------------------------------------- +class CBaseSaveGameDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBaseSaveGameDialog, vgui::Frame ); + +public: + CBaseSaveGameDialog( vgui::Panel *parent, const char *name ); + static int __cdecl SaveGameSortFunc( const void *lhs, const void *rhs ); + +protected: + CUtlVector<SaveGameDescription_t> m_SaveGames; + vgui::PanelListPanel *m_pGameList; + + virtual void OnScanningSaveGames() {} + + void DeleteSaveGame( const char *fileName ); + void ScanSavedGames(); + void CreateSavedGamesList(); + int GetSelectedItemSaveIndex(); + void AddSaveGameItemToList( int saveIndex ); + + bool ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t &save ); + + void OnKeyCodeTyped( vgui::KeyCode code ); + void OnKeyCodePressed( vgui::KeyCode code ); + +private: + MESSAGE_FUNC( OnPanelSelected, "PanelSelected" ); + + vgui::Button *m_pLoadButton; +}; + + +#endif // BASESAVEGAMEDIALOG_H diff --git a/gameui/BenchmarkDialog.cpp b/gameui/BenchmarkDialog.cpp new file mode 100644 index 0000000..9cf0a1a --- /dev/null +++ b/gameui/BenchmarkDialog.cpp @@ -0,0 +1,130 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BenchmarkDialog.h" +#include "EngineInterface.h" +#include "BasePanel.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include "filesystem.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/CheckButton.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBenchmarkDialog::CBenchmarkDialog(vgui::Panel *parent, const char *name) : BaseClass(parent, name) +{ + Button *button = new Button(this, "RunButton", "RunButton"); + button->SetCommand(new KeyValues("RunBenchmark")); + SetSizeable(false); + SetDeleteSelfOnClose(true); + + LoadControlSettings("Resource/BenchmarkDialog.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: Launches the benchmark +//----------------------------------------------------------------------------- +void CBenchmarkDialog::RunBenchmark() +{ + // apply settings + BasePanel()->ApplyOptionsDialogSettings(); + + // launch the map + engine->ClientCmd_Unrestricted("disconnect\n"); + engine->ClientCmd_Unrestricted("wait\n"); + engine->ClientCmd_Unrestricted("wait\n"); + engine->ClientCmd_Unrestricted("maxplayers 1\n"); + engine->ClientCmd_Unrestricted("progress_enable\n"); + engine->ClientCmd_Unrestricted("map test_hardware\n"); + + // close this dialog + Close(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Displays benchmark results +//----------------------------------------------------------------------------- +class CBenchmarkResultsDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBenchmarkResultsDialog, vgui::Frame ); +public: + CBenchmarkResultsDialog( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) + { + SetTitle("#GameUI_BenchmarkResults_Title", true); + SetDeleteSelfOnClose(true); + SetSizeable(false); + + m_pUploadCheck = new CheckButton( this, "UploadCheck", "#GameUI_BenchmarkResults_UploadNow" ); + + LoadControlSettings("Resource/BenchmarkResultsDialog.res"); + + m_pUploadCheck->SetSelected( true ); + MoveToCenterOfScreen(); + } + + virtual void Activate() + { + BaseClass::Activate(); + + KeyValues *kv = new KeyValues( "Benchmark" ); + if ( kv->LoadFromFile( g_pFullFileSystem, "results/results.txt", "MOD" ) ) + { + // get the framerate + char szFrameRate[32]; + Q_snprintf( szFrameRate, sizeof(szFrameRate), "%.2f", kv->GetFloat("framerate") ); + SetDialogVariable( "framerate", szFrameRate ); + } + else + { + Close(); + } + kv->deleteThis(); + } + + void OnKeyCodePressed( KeyCode code ) + { + if ( code == KEY_XBUTTON_B ) + { + Close(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + +private: + virtual void OnClose() + { + if ( m_pUploadCheck->IsSelected() ) + { + engine->ClientCmd_Unrestricted( "bench_upload\n" ); + } + BaseClass::OnClose(); + } + + vgui::CheckButton *m_pUploadCheck; +}; + +//----------------------------------------------------------------------------- +// Purpose: Launches the stats dialog +//----------------------------------------------------------------------------- +CON_COMMAND_F( bench_showstatsdialog, "Shows a dialog displaying the most recent benchmark results.", FCVAR_CHEAT ) +{ + static vgui::DHANDLE<CBenchmarkResultsDialog> g_BenchmarkResultsDialog; + + if (!g_BenchmarkResultsDialog.Get()) + { + g_BenchmarkResultsDialog = new CBenchmarkResultsDialog( BasePanel(), "BenchmarkResultsDialog" ); + } + + g_BenchmarkResultsDialog->Activate(); +} diff --git a/gameui/BenchmarkDialog.h b/gameui/BenchmarkDialog.h new file mode 100644 index 0000000..5e323fe --- /dev/null +++ b/gameui/BenchmarkDialog.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BENCHMARKDIALOG_H +#define BENCHMARKDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +//----------------------------------------------------------------------------- +// Purpose: Benchmark launch dialog +//----------------------------------------------------------------------------- +class CBenchmarkDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBenchmarkDialog, vgui::Frame ); +public: + CBenchmarkDialog(vgui::Panel *parent, const char *name); + + void OnKeyCodePressed( vgui::KeyCode code ) + { + if ( code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B ) + { + Close(); + } + else if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + RunBenchmark(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + +private: + MESSAGE_FUNC( RunBenchmark, "RunBenchmark" ); +}; + + +#endif // BENCHMARKDIALOG_H diff --git a/gameui/BitmapImagePanel.cpp b/gameui/BitmapImagePanel.cpp new file mode 100644 index 0000000..b2b3545 --- /dev/null +++ b/gameui/BitmapImagePanel.cpp @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BitmapImagePanel.h" +#include <vgui/ISurface.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +CBitmapImagePanel::CBitmapImagePanel( Panel *parent, char const *panelName, + char const *filename /*= NULL*/ ) : Panel( parent, panelName ) +{ + m_szTexture[ 0 ] = 0; + m_bUploaded = false; + m_nTextureId = -1; + + SetBounds( 0, 0, 100, 100 ); + + if ( filename && filename[ 0 ] ) + { + Q_strncpy( m_szTexture, filename, sizeof( m_szTexture ) ); + } +} + +void CBitmapImagePanel::PaintBackground() +{ + if (!m_szTexture[0]) + return; + + if ( !m_bUploaded ) + forceUpload(); + + int w, h; + GetSize( w, h ); + surface()->DrawSetColor( Color( 255, 255, 255, 255 ) ); + surface()->DrawSetTexture( m_nTextureId ); + surface()->DrawTexturedRect( 0, 0, w, h ); +} + +void CBitmapImagePanel::setTexture( char const *filename ) +{ + Q_strncpy( m_szTexture, filename, sizeof( m_szTexture ) ); + + if ( m_bUploaded ) + { + forceReload(); + } + else + { + forceUpload(); + } +} + +void CBitmapImagePanel::forceUpload() +{ + if ( !m_szTexture[ 0 ] ) + return; + + m_bUploaded = true; + + m_nTextureId = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( m_nTextureId, m_szTexture, false, true); +} + +void CBitmapImagePanel::forceReload( void ) +{ + if ( !m_bUploaded ) + return; + + // Force texture to re-upload to video card + surface()->DrawSetTextureFile( m_nTextureId, m_szTexture, false, true); +} diff --git a/gameui/BitmapImagePanel.h b/gameui/BitmapImagePanel.h new file mode 100644 index 0000000..ce5f28f --- /dev/null +++ b/gameui/BitmapImagePanel.h @@ -0,0 +1,37 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BITMAPIMAGEPANEL_H +#define BITMAPIMAGEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> + +class CBitmapImagePanel : public vgui::Panel +{ +public: + CBitmapImagePanel( vgui::Panel *parent, char const *panelName, char const *filename = NULL ); + + virtual void PaintBackground(); + + virtual void setTexture( char const *filename ); + + virtual void forceReload( void ); + +private: + typedef vgui::Panel BaseClass; + + void forceUpload(); + + bool m_bUploaded; + int m_nTextureId; + char m_szTexture[ 128 ]; +}; + +#endif // BITMAPIMAGEPANEL_H diff --git a/gameui/BonusMapsDatabase.cpp b/gameui/BonusMapsDatabase.cpp new file mode 100644 index 0000000..7e394ed --- /dev/null +++ b/gameui/BonusMapsDatabase.cpp @@ -0,0 +1,916 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BonusMapsDatabase.h" +#include "EngineInterface.h" + +#include "tier1/convar.h" +#include "tier1/utlbuffer.h" + +#include "filesystem.h" +#include "ModInfo.h" +#include "EngineInterface.h" +#include "ixboxsystem.h" +#include "KeyValues.h" +#include "BasePanel.h" +#include "GameUI_Interface.h" +#include "BonusMapsDialog.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#define MOD_DIR ( IsXbox() ? "DEFAULT_WRITE_PATH" : "MOD" ) + + +const char g_pszMedalNames[4][8] = +{ + "none", + "bronze", + "silver", + "gold" +}; + + +const char *COM_GetModDirectory(); + + +bool WriteBonusMapSavedData( KeyValues *data ) +{ + if ( IsX360() && ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ) ) + return false; + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + data->RecursiveSaveToFile( buf, 0 ); + + char szFilename[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/bonus_maps_data.bmd" ); + else + Q_snprintf( szFilename, sizeof( szFilename ), "save/bonus_maps_data.bmd" ); + + bool bWriteSuccess = g_pFullFileSystem->WriteFile( szFilename, MOD_DIR, buf ); + + xboxsystem->FinishContainerWrites(); + + return bWriteSuccess; +} + +void GetBooleanStatus( KeyValues *pBonusFilesKey, BonusMapDescription_t &map ) +{ + KeyValues *pFileKey = NULL; + KeyValues *pBonusKey = NULL; + + for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() ) + { + if ( Q_strcmp( pFileKey->GetName(), map.szFileName ) == 0 ) + { + for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() ) + { + if ( Q_strcmp( pBonusKey->GetName(), map.szMapName ) == 0 ) + { + // Found the data + break; + } + } + break; + } + } + + if ( pBonusKey ) + { + map.bLocked = ( pBonusKey->GetInt( "lock" ) != 0 ); + map.bComplete = ( pBonusKey->GetInt( "complete" ) != 0 ); + } +} + +bool SetBooleanStatus( KeyValues *pBonusFilesKey, const char *pchName, const char *pchFileName, const char *pchMapName, bool bValue ) +{ + // Don't create entries for files that don't exist + if ( !IsX360() && ! (g_pFullFileSystem->FileExists( pchFileName, "MOD" ) || g_pFullFileSystem->IsDirectory( pchFileName, "MOD" ) ) ) + { + DevMsg( "Failed to set boolean status for file %s.", pchFileName ); + return false; + } + + bool bChanged = false; + + KeyValues *pFileKey = NULL; + KeyValues *pBonusKey = NULL; + + for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() ) + { + if ( Q_strcmp( pFileKey->GetName(), pchFileName ) == 0 ) + { + for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() ) + { + if ( Q_strcmp( pBonusKey->GetName(), pchMapName ) == 0 ) + { + // Found the data + break; + } + } + break; + } + } + + if ( !pFileKey ) + { + // Didn't find it, so create a new spot for the data + pFileKey = new KeyValues( pchFileName ); + pBonusFilesKey->AddSubKey( pFileKey ); + } + + if ( !pBonusKey ) + { + pBonusKey = new KeyValues( pchMapName, pchName, "0" ); + pFileKey->AddSubKey( pBonusKey ); + bChanged = true; + } + + if ( ( pBonusKey->GetInt( pchName ) != 0 ) != bValue ) + { + bChanged = true; + pBonusKey->SetInt( pchName, bValue ); + } + + return bChanged; +} + +float GetChallengeBests( KeyValues *pBonusFilesKey, BonusMapDescription_t &challenge ) +{ + // There's no challenges, so bail and assume 0% challenge completion + if ( challenge.m_pChallenges == NULL || challenge.m_pChallenges->Count() == 0 ) + return 0.0f; + + KeyValues *pFileKey = NULL; + KeyValues *pBonusKey = NULL; + + for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() ) + { + if ( Q_strcmp( pFileKey->GetName(), challenge.szFileName ) == 0 ) + { + for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() ) + { + if ( Q_strcmp( pBonusKey->GetName(), challenge.szMapName ) == 0 ) + { + // Found the data + break; + } + } + break; + } + } + + float fChallengePoints = 0.0f; + + for ( int iChallenge = 0; iChallenge < challenge.m_pChallenges->Count(); ++iChallenge ) + { + ChallengeDescription_t *pChallengeDescription = &((*challenge.m_pChallenges)[ iChallenge ]); + pChallengeDescription->iBest = ( ( pBonusKey ) ? ( pBonusKey->GetInt( pChallengeDescription->szName, -1 ) ) : ( -1 ) ); + + if ( pChallengeDescription->iBest >= 0 ) + { + if ( pChallengeDescription->iBest <= pChallengeDescription->iGold ) + fChallengePoints += 3.0f; + else if ( pChallengeDescription->iBest <= pChallengeDescription->iSilver ) + fChallengePoints += 2.0f; + else if ( pChallengeDescription->iBest <= pChallengeDescription->iBronze ) + fChallengePoints += 1.0f; + } + } + + return fChallengePoints / ( challenge.m_pChallenges->Count() * 3.0f ); +} + +bool UpdateChallengeBest( KeyValues *pBonusFilesKey, const BonusMapChallenge_t &challenge ) +{ + // Don't create entries for files that don't exist + if ( !IsX360() && !g_pFullFileSystem->FileExists( challenge.szFileName, "MOD" ) ) + { + DevMsg( "Failed to set challenge best for file %s.", challenge.szFileName ); + return false; + } + + bool bChanged = false; + + KeyValues *pFileKey = NULL; + KeyValues *pBonusKey = NULL; + + for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() ) + { + if ( Q_strcmp( pFileKey->GetName(), challenge.szFileName ) == 0 ) + { + for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() ) + { + if ( Q_strcmp( pBonusKey->GetName(), challenge.szMapName ) == 0 ) + { + // Found the challenge + break; + } + } + break; + } + } + + if ( !pFileKey ) + { + // Didn't find it, so create a new spot for data + pFileKey = new KeyValues( challenge.szFileName ); + pBonusFilesKey->AddSubKey( pFileKey ); + } + + if ( !pBonusKey ) + { + pBonusKey = new KeyValues( challenge.szMapName, challenge.szChallengeName, -1 ); + pFileKey->AddSubKey( pBonusKey ); + bChanged = true; + } + + int iCurrentBest = pBonusKey->GetInt( challenge.szChallengeName, -1 ); + if ( iCurrentBest == -1 || iCurrentBest > challenge.iBest ) + { + bChanged = true; + pBonusKey->SetInt( challenge.szChallengeName, challenge.iBest ); + } + + return bChanged; +} + +void GetChallengeMedals( ChallengeDescription_t *pChallengeDescription, int &iBest, int &iEarnedMedal, int &iNext, int &iNextMedal ) +{ + iBest = pChallengeDescription->iBest; + + if ( iBest == -1 ) + iEarnedMedal = 0; + else if ( iBest <= pChallengeDescription->iGold ) + iEarnedMedal = 3; + else if ( iBest <= pChallengeDescription->iSilver ) + iEarnedMedal = 2; + else if ( iBest <= pChallengeDescription->iBronze ) + iEarnedMedal = 1; + else + iEarnedMedal = 0; + + iNext = -1; + + switch ( iEarnedMedal ) + { + case 0: + iNext = pChallengeDescription->iBronze; + iNextMedal = 1; + break; + case 1: + iNext = pChallengeDescription->iSilver; + iNextMedal = 2; + break; + case 2: + iNext = pChallengeDescription->iGold; + iNextMedal = 3; + break; + case 3: + iNext = -1; + iNextMedal = -1; + break; + } +} + + +CBonusMapsDatabase *g_pBonusMapsDatabase = NULL; + +CBonusMapsDatabase *BonusMapsDatabase( void ) +{ + if ( !g_pBonusMapsDatabase ) + static CBonusMapsDatabase StaticBonusMapsDatabase; + + return g_pBonusMapsDatabase; +} + + +//----------------------------------------------------------------------------- +// Purpose:Constructor +//----------------------------------------------------------------------------- +CBonusMapsDatabase::CBonusMapsDatabase( void ) +{ + Assert( g_pBonusMapsDatabase == NULL ); // There should only be 1 bonus maps database + g_pBonusMapsDatabase = this; + + RootPath(); + + m_pBonusMapsManifest = new KeyValues( "bonus_maps_manifest" ); + m_pBonusMapsManifest->LoadFromFile( g_pFullFileSystem, "scripts/bonus_maps_manifest.txt", NULL ); + + m_iX360BonusesUnlocked = -1; // Only used on X360 + m_bHasLoadedSaveData = false; + + ReadBonusMapSaveData(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBonusMapsDatabase::~CBonusMapsDatabase() +{ + g_pBonusMapsDatabase = NULL; +} + +extern bool g_bIsCreatingNewGameMenuForPreFetching; + +bool CBonusMapsDatabase::ReadBonusMapSaveData( void ) +{ + if ( !m_pBonusMapSavedData ) + m_pBonusMapSavedData = new KeyValues( "bonus_map_saved_data" ); + + if ( g_bIsCreatingNewGameMenuForPreFetching ) + { + // Although we may have a storage device it's not going to be able to find our file at this point! BAIL! + return false; + } + +#ifdef _X360 + // Nothing to read + if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ) + return false; +#endif + + char szFilename[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/bonus_maps_data.bmd" ); + else + Q_snprintf( szFilename, sizeof( szFilename ), "save/bonus_maps_data.bmd" ); + + m_pBonusMapSavedData->LoadFromFile( g_pFullFileSystem, szFilename, NULL ); + + m_bSavedDataChanged = false; + m_bHasLoadedSaveData = true; + + return true; +} + +bool CBonusMapsDatabase::WriteSaveData( void ) +{ + bool bSuccess = false; + + if ( m_bSavedDataChanged ) + bSuccess = WriteBonusMapSavedData( m_pBonusMapSavedData ); + + if ( bSuccess ) + m_bSavedDataChanged = false; + + return bSuccess; +} + +void CBonusMapsDatabase::RootPath( void ) +{ + m_iDirDepth = 0; + V_strcpy_safe( m_szCurrentPath, "." ); +} + +void CBonusMapsDatabase::AppendPath( const char *pchAppend ) +{ + ++m_iDirDepth; + char szCurPathTmp[MAX_PATH]; + V_strcpy_safe( szCurPathTmp, m_szCurrentPath ); + Q_snprintf( m_szCurrentPath, sizeof( m_szCurrentPath ), "%s/%s", szCurPathTmp, pchAppend ); +} + +void CBonusMapsDatabase::BackPath( void ) +{ + if ( m_iDirDepth == 0 ) + return; + + if ( m_iDirDepth == 1 ) + { + RootPath(); // back to root + return; + } + + --m_iDirDepth; + Q_strrchr( m_szCurrentPath, '/' )[ 0 ] = '\0'; // remove a dir from the end +} + +void CBonusMapsDatabase::SetPath( const char *pchPath, int iDirDepth ) +{ + V_strcpy_safe( m_szCurrentPath, pchPath ); + m_iDirDepth = iDirDepth; +} + +void CBonusMapsDatabase::ClearBonusMapsList( void ) +{ + m_BonusMaps.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: builds bonus map list from directory +//----------------------------------------------------------------------------- +void CBonusMapsDatabase::ScanBonusMaps( void ) +{ + // Don't load in the bonus maps before we've properly read in the save data + if ( !m_bHasLoadedSaveData ) + { + if ( ! ReadBonusMapSaveData() ) + return; + } + + char *pCurrentPath = &(m_szCurrentPath[2]); + + // Reset completion percentage + m_iCompletableLevels = 0; + m_fCurrentCompletion = 0.0f; + + // populate list box with all bonus maps in the current path + char szDirectory[_MAX_PATH]; + + if ( Q_strcmp( m_szCurrentPath, "." ) == 0 ) + { + // We're at the root, so look at the directories in the manifest + KeyValues *pKey = NULL; + for ( pKey = m_pBonusMapsManifest->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey() ) + { + const char *pchType = pKey->GetName(); + if ( Q_strcmp( pchType, "search" ) == 0 ) + { + // Search through the directory + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s/", pKey->GetString() ); + + BuildSubdirectoryList( szDirectory, true ); + BuildBonusMapsList( szDirectory, true ); + } + else if ( Q_strcmp( pchType, "dir" ) == 0 ) + { + AddBonus( "", pKey->GetString(), true ); + } + else if ( Q_strcmp( pchType, "map" ) == 0 ) + { + AddBonus( "", pKey->GetString(), false ); + } + } + } + else + { + // Search through the current directory + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s/", pCurrentPath ); + + BuildSubdirectoryList( szDirectory, false ); + BuildBonusMapsList( szDirectory, false ); + } +} + +void CBonusMapsDatabase::RefreshMapData( void ) +{ + // Reset completion percentage + m_iCompletableLevels = 0; + m_fCurrentCompletion = 0.0f; + + for ( int iMap = 0; iMap < m_BonusMaps.Count(); ++iMap ) + { + BonusMapDescription_t *pMap = &m_BonusMaps[ iMap ]; + + float fCompletion = GetChallengeBests( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap ); + + // If all the challenges are completed set it as complete + if ( fCompletion == 1.0f ) + SetBooleanStatus( "complete", pMap->szFileName, pMap->szMapName, true ); + + GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap ); + + if ( pMap->bComplete ) + fCompletion = 1.0f; + + if ( !pMap->bIsFolder ) + { + m_fCurrentCompletion += fCompletion; + ++m_iCompletableLevels; + } + } +} + +int CBonusMapsDatabase::BonusCount( void ) +{ + if ( m_BonusMaps.Count() == 0 ) + ScanBonusMaps(); + + return m_BonusMaps.Count(); +} + +bool CBonusMapsDatabase::GetBlink( void ) +{ + KeyValues *pBlinkKey = m_pBonusMapSavedData->FindKey( "blink" ); + if ( !pBlinkKey ) + return false; + + return ( pBlinkKey->GetInt() != 0 ); +} + +void CBonusMapsDatabase::SetBlink( bool bState ) +{ + KeyValues *pBlinkKey = m_pBonusMapSavedData->FindKey( "blink" ); + if ( pBlinkKey ) + { + bool bCurrentState = ( pBlinkKey->GetInt() != 0 ); + if ( bState && !bCurrentState ) + { + pBlinkKey->SetStringValue( "1" ); + m_bSavedDataChanged = true; + } + else if ( !bState && bCurrentState ) + { + pBlinkKey->SetStringValue( "0" ); + m_bSavedDataChanged = true; + } + } +} + +// Only used on X360 +bool CBonusMapsDatabase::BonusesUnlocked( void ) +{ + if ( m_iX360BonusesUnlocked == -1 ) + { + // Never checked, so set up the proper X360 scan + BonusMapsDatabase()->ClearBonusMapsList(); // clear the current list + BonusMapsDatabase()->RootPath(); + BonusMapsDatabase()->ScanBonusMaps(); + + m_iX360BonusesUnlocked = 0; + } + + if ( m_iX360BonusesUnlocked == 0 ) + { + // Hasn't been recorded as unlocked yet + for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap ) + { + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap ); + if ( Q_strcmp( pMap->szMapName, "#Bonus_Map_AdvancedChambers" ) == 0 && !pMap->bLocked ) + { + // All bonuses unlocked, remember this and set up the proper X360 scan to get info. + m_iX360BonusesUnlocked = 1; + break; + } + } + } + + return ( m_iX360BonusesUnlocked != 0 ); +} + +void CBonusMapsDatabase::SetCurrentChallengeNames( const char *pchFileName, const char *pchMapName, const char *pchChallengeName ) +{ + V_strcpy_safe( m_CurrentChallengeNames.szFileName, pchFileName ); + V_strcpy_safe( m_CurrentChallengeNames.szMapName, pchMapName ); + V_strcpy_safe( m_CurrentChallengeNames.szChallengeName, pchChallengeName ); +} + +void CBonusMapsDatabase::GetCurrentChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName ) +{ + Q_strcpy( pchFileName, m_CurrentChallengeNames.szFileName ); + Q_strcpy( pchMapName, m_CurrentChallengeNames.szMapName ); + Q_strcpy( pchChallengeName, m_CurrentChallengeNames.szChallengeName ); +} + +void CBonusMapsDatabase::SetCurrentChallengeObjectives( int iBronze, int iSilver, int iGold ) +{ + m_CurrentChallengeObjectives.iBronze = iBronze; + m_CurrentChallengeObjectives.iSilver = iSilver; + m_CurrentChallengeObjectives.iGold = iGold; +} + +void CBonusMapsDatabase::GetCurrentChallengeObjectives( int &iBronze, int &iSilver, int &iGold ) +{ + iBronze = m_CurrentChallengeObjectives.iBronze; + iSilver = m_CurrentChallengeObjectives.iSilver; + iGold = m_CurrentChallengeObjectives.iGold; +} + +bool CBonusMapsDatabase::SetBooleanStatus( const char *pchName, const char *pchFileName, const char *pchMapName, bool bValue ) +{ + bool bChanged = ::SetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), pchName, pchFileName, pchMapName, bValue ); + if ( bChanged ) + m_bSavedDataChanged = true; + + return bChanged; +} + +bool CBonusMapsDatabase::SetBooleanStatus( const char *pchName, int iIndex, bool bValue ) +{ + BonusMapDescription_t *pMap = &(m_BonusMaps[iIndex]); + + bool bChanged = SetBooleanStatus( pchName, pMap->szFileName, pMap->szMapName, bValue ); + GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap ); + + return bChanged; +} + +bool CBonusMapsDatabase::UpdateChallengeBest( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest ) +{ + BonusMapChallenge_t challenge; + V_strcpy_safe( challenge.szFileName, pchFileName ); + V_strcpy_safe( challenge.szMapName, pchMapName ); + V_strcpy_safe( challenge.szChallengeName, pchChallengeName ); + challenge.iBest = iBest; + + bool bChanged = ::UpdateChallengeBest( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), challenge ); + if ( bChanged ) + m_bSavedDataChanged = true; + + return bChanged; +} + +float CBonusMapsDatabase::GetCompletionPercentage( void ) +{ + // Avoid divide by zero + if ( m_iCompletableLevels <= 0 ) + return 0.0f; + + return m_fCurrentCompletion / m_iCompletableLevels; +} + +int CBonusMapsDatabase::NumAdvancedComplete( void ) +{ + char szCurrentPath[_MAX_PATH]; + V_strcpy_safe( szCurrentPath, m_szCurrentPath ); + int iDirDepth = m_iDirDepth; + + BonusMapsDatabase()->ClearBonusMapsList(); + SetPath( "./scripts/advanced_chambers", 1 ); + ScanBonusMaps(); + + int iNumComplete = 0; + + // Look through all the bonus maps + for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap ) + { + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap ); + + if ( pMap && Q_strstr( pMap->szMapName, "Advanced" ) != NULL ) + { + // It's an advanced map, so check if it's complete + if ( pMap->bComplete ) + ++iNumComplete; + } + } + + BonusMapsDatabase()->ClearBonusMapsList(); + SetPath( szCurrentPath, iDirDepth ); + ScanBonusMaps(); + + return iNumComplete; +} + +void CBonusMapsDatabase::NumMedals( int piNumMedals[ 3 ] ) +{ + char szCurrentPath[_MAX_PATH]; + V_strcpy_safe( szCurrentPath, m_szCurrentPath ); + int iDirDepth = m_iDirDepth; + + BonusMapsDatabase()->ClearBonusMapsList(); + SetPath( "./scripts/challenges", 1 ); + ScanBonusMaps(); + + for ( int i = 0; i < 3; ++i ) + piNumMedals[ i ] = 0; + + // Look through all the bonus maps + for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap ) + { + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap ); + + if ( pMap && pMap->m_pChallenges ) + { + for ( int iChallenge = 0; iChallenge < pMap->m_pChallenges->Count(); ++iChallenge ) + { + ChallengeDescription_t *pChallengeDescription = &((*pMap->m_pChallenges)[ iChallenge ]); + + int iBest, iEarnedMedal, iNext, iNextMedal; + GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal ); + + // Increase the count for this medal and every medal below it + while ( iEarnedMedal > 0 ) + { + --iEarnedMedal; // Medals are 1,2&3 so subtract 1 first + piNumMedals[ iEarnedMedal ]++; + } + } + } + } + + BonusMapsDatabase()->ClearBonusMapsList(); + SetPath( szCurrentPath, iDirDepth ); + ScanBonusMaps(); +} + + +void CBonusMapsDatabase::AddBonus( const char *pCurrentPath, const char *pDirFileName, bool bIsFolder ) +{ + char szFileName[_MAX_PATH]; + Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pDirFileName ); + + // Only load bonus maps from the current mod's maps dir + if( !IsX360() && !( g_pFullFileSystem->IsDirectory( szFileName, "MOD" ) || g_pFullFileSystem->FileExists( szFileName, "MOD" ) )) + return; + + ParseBonusMapData( szFileName, pDirFileName, bIsFolder ); +} + +void CBonusMapsDatabase::BuildSubdirectoryList( const char *pCurrentPath, bool bOutOfRoot ) +{ + char szDirectory[_MAX_PATH]; + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s*", pCurrentPath ); + + FileFindHandle_t dirHandle; + const char *pDirFileName = g_pFullFileSystem->FindFirst( szDirectory, &dirHandle ); + + while (pDirFileName) + { + // Skip it if it's not a directory, is the root, is back, or is an invalid folder + if ( !g_pFullFileSystem->FindIsDirectory( dirHandle ) || + Q_strcmp( pDirFileName, "." ) == 0 || + Q_strcmp( pDirFileName, ".." ) == 0 || + Q_stricmp( pDirFileName, "soundcache" ) == 0 || + Q_stricmp( pDirFileName, "graphs" ) == 0 ) + { + pDirFileName = g_pFullFileSystem->FindNext( dirHandle ); + continue; + } + + if ( !bOutOfRoot ) + AddBonus( pCurrentPath, pDirFileName, true ); + else + { + char szFileName[_MAX_PATH]; + Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pDirFileName ); + AddBonus( "", szFileName, true ); + } + + pDirFileName = g_pFullFileSystem->FindNext( dirHandle ); + } + + g_pFullFileSystem->FindClose( dirHandle ); +} + +void CBonusMapsDatabase::BuildBonusMapsList( const char *pCurrentPath, bool bOutOfRoot ) +{ + char szDirectory[_MAX_PATH]; + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s*.bns", pCurrentPath ); + + FileFindHandle_t mapHandle; + const char *pMapFileName = g_pFullFileSystem->FindFirst( szDirectory, &mapHandle ); + + while ( pMapFileName && Q_strlen(pMapFileName)>0 ) + { + // Skip it if it's a directory or is the folder info + if ( g_pFullFileSystem->FindIsDirectory( mapHandle ) || Q_strstr( pMapFileName, "folderinfo.bns" ) ) + { + pMapFileName = g_pFullFileSystem->FindNext( mapHandle ); + continue; + } + + if ( !bOutOfRoot ) + AddBonus( pCurrentPath, pMapFileName, false ); + else + { + char szFileName[_MAX_PATH]; + Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pMapFileName ); + AddBonus( "", szFileName, false ); + } + + pMapFileName = g_pFullFileSystem->FindNext( mapHandle ); + } + + g_pFullFileSystem->FindClose( mapHandle ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the save game info out of the .sav file header +//----------------------------------------------------------------------------- +void CBonusMapsDatabase::ParseBonusMapData( char const *pszFileName, char const *pszShortName, bool bIsFolder ) +{ + if ( !pszFileName || !pszShortName ) + return; + + char szMapInfo[_MAX_PATH]; + + // if it's a directory, there's no optional info + if ( bIsFolder ) + { + // get the folder info file name + Q_snprintf( szMapInfo, sizeof(szMapInfo), "%s/folderinfo.bns", pszFileName ); + } + else + { + // get the map info file name + Q_strncpy( szMapInfo, pszFileName, sizeof(szMapInfo) ); + } + + KeyValues *kv = new KeyValues( pszShortName ); + if ( !kv->LoadFromFile( g_pFullFileSystem, szMapInfo, NULL ) ) + DevMsg( "Unable to load bonus map info file %s\n", szMapInfo ); + + while ( kv ) + { + int iMap = m_BonusMaps.AddToTail(); + + BonusMapDescription_t *pMap = &m_BonusMaps[ iMap ]; + + // set required map data + Q_strncpy( pMap->szFileName, pszFileName, sizeof(pMap->szFileName) ); + Q_strncpy( pMap->szShortName, pszShortName, sizeof(pMap->szShortName) ); + pMap->bIsFolder = bIsFolder; + + // set optional map data + V_strcpy_safe( pMap->szMapName, kv->GetName() ); + V_strcpy_safe( pMap->szMapFileName, kv->GetString( "map" ) ); + V_strcpy_safe( pMap->szChapterName, kv->GetString( "chapter" ) ); + V_strcpy_safe( pMap->szImageName, kv->GetString( "image" ) ); + V_strcpy_safe( pMap->szComment, kv->GetString( "comment" ) ); + pMap->bLocked = ( kv->GetInt( "lock", 0 ) != 0 ); + pMap->bComplete = ( kv->GetInt( "complete", 0 ) != 0 ); + + float fCompletion = 0.0f; + + KeyValues *pChallenges = kv->FindKey( "challenges" ); + + if ( pChallenges ) + { + for ( KeyValues *pChallengeKey = pChallenges->GetFirstSubKey(); pChallengeKey; pChallengeKey = pChallengeKey->GetNextKey() ) + { + if ( !pMap->m_pChallenges ) + pMap->m_pChallenges = new CUtlVector<ChallengeDescription_t>; + + int iChallenge = pMap->m_pChallenges->AddToTail(); + + ChallengeDescription_t *pChallenge = &(*pMap->m_pChallenges)[ iChallenge ]; + V_strcpy_safe( pChallenge->szName, pChallengeKey->GetName() ); + V_strcpy_safe( pChallenge->szComment, pChallengeKey->GetString( "comment" ) ); + pChallenge->iType = pChallengeKey->GetInt( "type", -1 ); + pChallenge->iBronze = pChallengeKey->GetInt( "bronze" ); + pChallenge->iSilver = pChallengeKey->GetInt( "silver" ); + pChallenge->iGold = pChallengeKey->GetInt( "gold" ); + } + + fCompletion = GetChallengeBests( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap ); + + // If all the challenges are completed set it as complete + if ( fCompletion == 1.0f ) + SetBooleanStatus( "complete", pMap->szFileName, pMap->szMapName, true ); + } + + // Get boolean status last because it can be altered if all the challenges were completed + GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap ); + + if ( pMap->bComplete ) + fCompletion = 1.0f; + + if ( !pMap->bIsFolder ) + { + m_fCurrentCompletion += fCompletion; + ++m_iCompletableLevels; + kv = kv->GetNextTrueSubKey(); + } + else + kv = NULL; + } +} + + +void CC_BonusMapUnlock( const CCommand &args ) +{ + if ( args.ArgC() < 3 ) + { + GameUI().BonusMapUnlock(); + return; + } + + GameUI().BonusMapUnlock( args[ 1 ], args[ 2 ] ); +} +static ConCommand sv_bonus_map_unlock("sv_bonus_map_unlock", CC_BonusMapUnlock, "Locks a bonus map.", FCVAR_CHEAT ); + + +void CC_BonusMapComplete( const CCommand &args ) +{ + if ( args.ArgC() < 3 ) + { + GameUI().BonusMapComplete(); + return; + } + + GameUI().BonusMapComplete( args[ 1 ], args[ 2 ] ); +} +static ConCommand sv_bonus_map_complete("sv_bonus_map_complete", CC_BonusMapComplete, "Completes a bonus map.", FCVAR_CHEAT ); + + +void CC_BonusMapChallengeUpdate( const CCommand &args ) +{ + if ( args.ArgC() < 5 ) + { + return; + } + + GameUI().BonusMapChallengeUpdate( args[ 1 ], args[ 2 ], args[ 3 ], atoi( args[ 4 ] ) ); +} +static ConCommand sv_bonus_map_challenge_update("sv_bonus_map_challenge_update", CC_BonusMapChallengeUpdate, "Updates a bonus map challenge score.", FCVAR_CHEAT ); diff --git a/gameui/BonusMapsDatabase.h b/gameui/BonusMapsDatabase.h new file mode 100644 index 0000000..a3275d3 --- /dev/null +++ b/gameui/BonusMapsDatabase.h @@ -0,0 +1,167 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BONUSMAPSDATABASE_H +#define BONUSMAPSDATABASE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "utlvector.h" + + +struct ChallengeDescription_t +{ + char szName[32]; + char szComment[256]; + + int iType; + + int iBronze; + int iSilver; + int iGold; + + int iBest; +}; + +struct BonusMapDescription_t +{ + bool bIsFolder; + + char szShortName[64]; + char szFileName[128]; + + char szMapFileName[128]; + char szChapterName[128]; + char szImageName[128]; + + char szMapName[64]; + char szComment[256]; + + bool bLocked; + bool bComplete; + + CUtlVector<ChallengeDescription_t> *m_pChallenges; + + BonusMapDescription_t( void ) + { + bIsFolder = false; + + szShortName[ 0 ] = '\0'; + szFileName[ 0 ] = '\0'; + + szMapFileName[ 0 ] = '\0'; + szChapterName[ 0 ] = '\0'; + szImageName[ 0 ] = '\0'; + + szMapName[ 0 ] = '\0'; + szComment[ 0 ] = '\0'; + + bLocked = false; + bComplete = false; + + m_pChallenges = NULL; + } +}; + +struct BonusMapChallenge_t +{ + char szFileName[128]; + char szMapName[32]; + char szChallengeName[32]; + int iBest; +}; + + +class KeyValues; + + +//----------------------------------------------------------------------------- +// Purpose: Keeps track of bonus maps on disk +//----------------------------------------------------------------------------- +class CBonusMapsDatabase +{ + +public: + CBonusMapsDatabase( void ); + ~CBonusMapsDatabase(); + + bool ReadBonusMapSaveData( void ); + bool WriteSaveData( void ); + + const char * GetPath( void ) { return m_szCurrentPath; } + void RootPath( void ); + void AppendPath( const char *pchAppend ); + void BackPath( void ); + void SetPath( const char *pchPath, int iDirDepth ); + + void ClearBonusMapsList( void ); + void ScanBonusMaps( void ); + void RefreshMapData( void ); + + int BonusCount( void ); + BonusMapDescription_t * GetBonusData( int iIndex ) { return &(m_BonusMaps[ iIndex ]); } + int InvalidIndex( void ) { return m_BonusMaps.InvalidIndex(); } + bool IsValidIndex( int iIndex ) { return m_BonusMaps.IsValidIndex( iIndex ); } + + bool GetBlink( void ); + void SetBlink( bool bState ); + + bool BonusesUnlocked( void ); + + void SetCurrentChallengeNames( const char *pchFileName, const char *pchMapName, const char *pchChallengeName ); + void GetCurrentChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName ); + void SetCurrentChallengeObjectives( int iBronze, int iSilver, int iGold ); + void GetCurrentChallengeObjectives( int &iBronze, int &iSilver, int &iGold ); + + bool SetBooleanStatus( const char *pchName, const char *pchFileName, const char *pchMapName, bool bValue ); + bool SetBooleanStatus( const char *pchName, int iIndex, bool bValue ); + bool UpdateChallengeBest( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest ); + + float GetCompletionPercentage( void ); + + int NumAdvancedComplete( void ); + void NumMedals( int piNumMedals[ 3 ] ); + +private: + + void AddBonus( const char *pCurrentPath, const char *pDirFileName, bool bIsFolder ); + void BuildSubdirectoryList( const char *pCurrentPath, bool bOutOfRoot ); + void BuildBonusMapsList( const char *pCurrentPath, bool bOutOfRoot ); + + void ParseBonusMapData( char const *pszFileName, char const *pszShortName, bool bIsFolder ); + +private: + + KeyValues *m_pBonusMapsManifest; + + CUtlVector<BonusMapDescription_t> m_BonusMaps; + + KeyValues *m_pBonusMapSavedData; + bool m_bSavedDataChanged; + + int m_iX360BonusesUnlocked; // Only used on 360 + bool m_bHasLoadedSaveData; + + int m_iDirDepth; + char m_szCurrentPath[_MAX_PATH]; + float m_fCurrentCompletion; + int m_iCompletableLevels; + + BonusMapChallenge_t m_CurrentChallengeNames; + ChallengeDescription_t m_CurrentChallengeObjectives; +}; + + +void GetChallengeMedals( ChallengeDescription_t *pChallengeDescription, int &iBest, int &iEarnedMedal, int &iNext, int &iNextMedal ); +CBonusMapsDatabase *BonusMapsDatabase( void ); + +extern const char g_pszMedalNames[4][8]; + + +#endif // BONUSMAPSDATABASE_H diff --git a/gameui/BonusMapsDialog.cpp b/gameui/BonusMapsDialog.cpp new file mode 100644 index 0000000..f5243a7 --- /dev/null +++ b/gameui/BonusMapsDialog.cpp @@ -0,0 +1,1006 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BonusMapsDialog.h" +#include "EngineInterface.h" + +#include "vgui/ISystem.h" +#include "vgui/ISurface.h" +#include "vgui/IVGui.h" +#include <vgui/ILocalize.h> +#include "filesystem.h" + +#include "vgui_controls/Button.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/QueryBox.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui_controls/FileOpenDialog.h" + +#include "TGAImagePanel.h" +#include "MouseMessageForwardingPanel.h" + +#include "BasePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +#define MAX_LISTED_BONUS_MAPS 128 + + +extern const char *COM_GetModDirectory( void ); + + +bool ConstructFullImagePath( const char *pCurrentPath, const char *pchImageName, char *pchImageFileName ) +{ + char *ext = Q_strstr( pchImageName , ".tga" ); + if ( ext ) + { + // Use the specified image + if ( pchImageName[ 0 ] != '.' ) + Q_snprintf( pchImageFileName, _MAX_PATH, "%s", pchImageName ); + else + Q_snprintf( pchImageFileName, _MAX_PATH, "%s/%s", pCurrentPath, pchImageName ); + + return true; + } + + Q_strcpy( pchImageFileName, pchImageName ); + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Describes the layout of a same game pic +//----------------------------------------------------------------------------- +class CBonusMapPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CBonusMapPanel, vgui::EditablePanel ); +public: + CBonusMapPanel( PanelListPanel *parent, const char *name, int bonusMapListItemID ) : BaseClass( parent, name ) + { + m_iBonusMapListItemID = bonusMapListItemID; + m_pParent = parent; + m_pBonusMapTGAImage = new CTGAImagePanel( this, "BonusMapTGAImage" ); + m_pBonusMapImage = SETUP_PANEL( new ImagePanel( this, "BonusMapImage" ) ); + m_pBonusMapScreenshotBackground = SETUP_PANEL( new ImagePanel( this, "BonusMapScreenshotBackground" ) ); + m_pMapNameLabel = new Label( this, "MapNameLabel", "" ); + m_pLockIcon = new ImagePanel( this, "LockIcon" ); + m_pCompleteIcon = new ImagePanel( this, "CompleteIcon" ); + + CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL); + panel->SetZPos(2); + + SetSize( 200, 140 ); + + LoadControlSettings( "resource/BonusMapPanel.res" ); + + m_FillColor = m_pBonusMapScreenshotBackground->GetFillColor(); + } + + void SetBonusMapInfo( const char *pCurrentPath, BonusMapDescription_t &map ) + { + // set the image to display + char szImageFileName[_MAX_PATH]; + + bool bIsTGA = false; + + if ( map.bIsFolder ) + { + if ( map.szImageName[ 0 ] == '\0' ) + { + // use associate bonus folder icon + Q_snprintf( szImageFileName, sizeof(szImageFileName), "%s/foldericon.tga", map.szFileName ); + bIsTGA = true; + + // use default folder icon + if( !g_pFullFileSystem->FileExists( szImageFileName, "MOD" ) ) + { + V_strcpy_safe( szImageFileName, "bonusmaps/icon_bonus_map_folder" ); + bIsTGA = false; + } + } + else + { + // Use the specified image + bIsTGA = ConstructFullImagePath( pCurrentPath, map.szImageName, szImageFileName ); + } + } + else + { + if ( map.szImageName[ 0 ] == '\0' ) + { + // Didn't specify an image name, so pair it with the name of this file + char szImpliedTgaName[_MAX_PATH]; + Q_snprintf( szImpliedTgaName, sizeof( szImpliedTgaName ), "%s.tga", map.szMapFileName ); + bIsTGA = ConstructFullImagePath( pCurrentPath, szImpliedTgaName, szImageFileName ); + + // if it doesn't exist use default bonus map icon + if( !g_pFullFileSystem->FileExists( szImageFileName, "MOD" ) ) + { + V_strcpy_safe( szImageFileName, "bonusmaps/icon_bonus_map_default" ); + bIsTGA = false; + } + } + else + { + // Use the specified image + bIsTGA = ConstructFullImagePath( pCurrentPath, map.szImageName, szImageFileName ); + } + } + + if ( bIsTGA ) + { + m_pBonusMapTGAImage->SetTGANonMod( szImageFileName ); + m_pBonusMapTGAImage->SetSize( 180, 100 ); + m_pBonusMapTGAImage->SetVisible( true ); + m_pBonusMapImage->SetVisible( false ); + } + else + { + m_pBonusMapImage->SetImage( szImageFileName ); + m_pBonusMapImage->SetSize( 180, 100 ); + m_pBonusMapImage->SetVisible( true ); + m_pBonusMapTGAImage->SetVisible( false ); + } + + m_pLockIcon->SetVisible( map.bLocked ); + m_pCompleteIcon->SetVisible( map.bComplete ); + + // set the title text + m_pMapNameLabel->SetText( map.szMapName ); + } + + MESSAGE_FUNC_INT( OnPanelSelected, "PanelSelected", state ) + { + if ( state ) + { + // set the text color to be orange, and the pic border to be orange + m_pBonusMapScreenshotBackground->SetFillColor( m_SelectedColor ); + m_pMapNameLabel->SetFgColor( Color( 0, 0, 0, 255 ) ); + } + else + { + m_pBonusMapScreenshotBackground->SetFillColor( m_FillColor ); + m_pMapNameLabel->SetFgColor( m_TextColor ); + } + + PostMessage( m_pParent->GetVParent(), new KeyValues("PanelSelected") ); + } + + virtual void OnMousePressed( vgui::MouseCode code ) + { + m_pParent->SetSelectedPanel( this ); + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) ); + m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + BaseClass::ApplySchemeSettings( pScheme ); + } + + virtual void OnMouseDoublePressed( vgui::MouseCode code ) + { + // call the panel + OnMousePressed( code ); + PostMessage( m_pParent->GetParent(), new KeyValues("Command", "command", "loadbonusmap") ); + } + + int GetBonusMapListItemID() + { + return m_iBonusMapListItemID; + } + +private: + vgui::PanelListPanel *m_pParent; + vgui::Label *m_pMapNameLabel; + ImagePanel *m_pBonusMapImage; + CTGAImagePanel *m_pBonusMapTGAImage; + + ImagePanel *m_pLockIcon; + ImagePanel *m_pCompleteIcon; + + // things to change color when the selection changes + ImagePanel *m_pBonusMapScreenshotBackground; + Color m_TextColor, m_FillColor, m_SelectedColor; + + int m_iBonusMapListItemID; +}; + +CBonusMapsDialog *g_pBonusMapsDialog = NULL; + + +//----------------------------------------------------------------------------- +// Purpose:Constructor +//----------------------------------------------------------------------------- +CBonusMapsDialog::CBonusMapsDialog(vgui::Panel *parent) : BaseClass(parent, "BonusMapsDialog") +{ + g_pBonusMapsDialog = this; + m_hImportBonusMapsDialog = NULL; + + BonusMapsDatabase()->RootPath(); + + CreateBonusMapsList(); + + BuildMapsList(); + + new vgui::Button( this, "loadbonusmap", "" ); + SetControlEnabled( "loadbonusmap", false ); + + SetDeleteSelfOnClose(true); + //SetBounds(0, 0, 512, 384); + //SetMinimumSize( 256, 300 ); + SetSizeable( false ); + + SetTitle("#GameUI_BonusMaps", true); + + vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + m_pPercentageBarBackground = SETUP_PANEL( new ImagePanel( this, "PercentageBarBackground" ) ); + m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) ); + + LoadControlSettings("resource/BonusMapsDialog.res"); + + // Stop blinking the bonus maps menu item + CBasePanel *pBasePanel = BasePanel(); + if ( pBasePanel ) + pBasePanel->SetMenuItemBlinkingState( "OpenBonusMapsDialog", false ); + + BonusMapsDatabase()->SetBlink( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBonusMapsDialog::~CBonusMapsDialog() +{ + BonusMapsDatabase()->WriteSaveData(); // Closing this dialog is a good time to save + g_pBonusMapsDialog = NULL; +} + + +bool CBonusMapsDialog::ImportZippedBonusMaps( const char *pchZippedFileName ) +{ + // Get the zip file's name with dir info + char *pchShortFileName = Q_strrchr( pchZippedFileName, CORRECT_PATH_SEPARATOR ); + + if ( !pchShortFileName ) + return false; + + // It's going to go in the maps folder + char szOutFilename[ 512 ]; + + Q_snprintf( szOutFilename, sizeof( szOutFilename ), "maps%s", pchShortFileName ); + Q_StripExtension( szOutFilename, szOutFilename, sizeof( szOutFilename ) ); + + // If there's already a folder by the same name we're going to tack a number onto the end + int iOutFilenameLength = Q_strlen( szOutFilename ); + int iSameFolderCount = 1; + + while ( g_pFullFileSystem->FileExists( szOutFilename, "MOD" ) ) + { + ++iSameFolderCount; + + if ( iSameFolderCount > 99 ) + { + return false; + } + + szOutFilename[ iOutFilenameLength ] = '\0'; + Q_snprintf( szOutFilename, sizeof( szOutFilename ), "%s%02i", szOutFilename, iSameFolderCount ); + } + + // Pull the files out of the zip + if ( g_pFullFileSystem->UnzipFile( pchZippedFileName, "MOD", szOutFilename ) ) + { + // New maps have been imported, so refresh the list + BuildMapsList(); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: builds bonus map list from directory +//----------------------------------------------------------------------------- +void CBonusMapsDialog::BuildMapsList( void ) +{ + // clear the current list + m_pGameList->DeleteAllItems(); + + BonusMapsDatabase()->ClearBonusMapsList(); + BonusMapsDatabase()->ScanBonusMaps(); + + // Enable back button if we're in a sub folder + bool bIsRoot = ( Q_strcmp( BonusMapsDatabase()->GetPath(), "." ) == 0 ); + SetControlEnabled( "Back", !bIsRoot ); + SetControlVisible( "Back", !bIsRoot ); + SetControlEnabled( "ImportBonusMaps", bIsRoot ); + SetControlVisible( "ImportBonusMaps", bIsRoot ); + + char szDisplayPath[_MAX_PATH]; + Q_snprintf( szDisplayPath, _MAX_PATH, "%s/", BonusMapsDatabase()->GetPath() ); + + SetControlString( "FileName", szDisplayPath ); + SetControlString( "CommentLabel", "" ); + + int iMapCount = BonusMapsDatabase()->BonusCount(); + + // add to the list + for ( int iMapIndex = 0; iMapIndex < iMapCount && iMapIndex < MAX_LISTED_BONUS_MAPS; ++iMapIndex ) + { + CBonusMapPanel *bonusMapPanel = new CBonusMapPanel( m_pGameList, "BonusMapPanel", iMapIndex ); + bonusMapPanel->SetBonusMapInfo( BonusMapsDatabase()->GetPath(), *(BonusMapsDatabase()->GetBonusData( iMapIndex )) ); + m_pGameList->AddItem( NULL, bonusMapPanel ); + } + + // display a message if there are no save games + if ( iMapCount <= 0 ) + { + vgui::Label *pNoSavesLabel = SETUP_PANEL(new Label(m_pGameList, "NoBonusMapsLabel", "#GameUI_NoBonusMapsToDisplay")); + pNoSavesLabel->SetTextColorState(vgui::Label::CS_DULL); + m_pGameList->AddItem( NULL, pNoSavesLabel ); + m_pGameList->SetNumColumns( 1 ); + } + else + { + m_pGameList->SetNumColumns( 3 ); + } + + RefreshCompletionPercentage(); + + // Disable load button + SetControlEnabled( "loadbonusmap", false ); + + // Make challenge selection invisible + m_pChallengeSelection->SetEnabled( false ); + m_pChallengeSelection->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates the load game display list +//----------------------------------------------------------------------------- +void CBonusMapsDialog::CreateBonusMapsList() +{ + m_pGameList = new vgui::PanelListPanel( this, "listpanel_bonusmaps" ); + m_pGameList->SetFirstColumnWidth( 0 ); + + new Label( this, "FileName", "./" ); + new Label( this, "CommentLabel", "" ); + + m_pChallengeSelection = new vgui::ComboBox( this, "ChallengeSelection", 0, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the save file name of the selected item +//----------------------------------------------------------------------------- +int CBonusMapsDialog::GetSelectedItemBonusMapIndex() +{ + CBonusMapPanel *panel = dynamic_cast<CBonusMapPanel *>(m_pGameList->GetSelectedPanel()); + if ( panel && panel->GetBonusMapListItemID() < BonusMapsDatabase()->BonusCount() ) + return panel->GetBonusMapListItemID(); + + return BonusMapsDatabase()->InvalidIndex(); +} + +void CBonusMapsDialog::SetSelectedBooleanStatus( const char *pchName, bool bValue ) +{ + CBonusMapPanel *pSelectedBonusMapPanel = (CBonusMapPanel *)m_pGameList->GetSelectedPanel(); + if ( !pSelectedBonusMapPanel ) + return; + + BonusMapsDatabase()->SetBooleanStatus( pchName, pSelectedBonusMapPanel->GetBonusMapListItemID(), bValue ); + + // Change the status in the dialog + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( pSelectedBonusMapPanel->GetBonusMapListItemID() ); + pSelectedBonusMapPanel->SetBonusMapInfo( BonusMapsDatabase()->GetPath(), *pMap ); + + RefreshCompletionPercentage(); + RefreshDialog( pMap ); +} + +void CBonusMapsDialog::RefreshData( void ) +{ + for ( int iMap = 0; iMap < m_pGameList->GetItemCount(); ++iMap ) + { + CBonusMapPanel *pBonusMapPanel = (CBonusMapPanel *)m_pGameList->GetItemPanel( iMap ); + + if ( pBonusMapPanel ) + pBonusMapPanel->SetBonusMapInfo( BonusMapsDatabase()->GetPath(), *(BonusMapsDatabase()->GetBonusData( pBonusMapPanel->GetBonusMapListItemID() ) ) ); + } + + CBonusMapPanel *pSelectedBonusMapPanel = (CBonusMapPanel *)m_pGameList->GetSelectedPanel(); + if ( !pSelectedBonusMapPanel ) + return; + + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( pSelectedBonusMapPanel->GetBonusMapListItemID() ); + + RefreshCompletionPercentage(); + RefreshDialog( pMap ); +} + +int CBonusMapsDialog::GetSelectedChallenge( void ) +{ + if ( !m_pChallengeSelection->IsEnabled() ) + return -1; + + KeyValues *pUserDataKey = m_pChallengeSelection->GetActiveItemUserData(); + return pUserDataKey->GetInt( "challenge" ); +} + +void CBonusMapsDialog::RefreshDialog( BonusMapDescription_t *pMap ) +{ + if ( !pMap || pMap->bLocked || ( m_pChallengeSelection->IsEnabled() && GetSelectedChallenge() == -1 ) ) + { + // It's locked or no challenge is selected, so disable the load button + SetControlEnabled( "loadbonusmap", false ); + } + else + { + // Enable the load button + SetControlEnabled( "loadbonusmap", true ); + } + + RefreshMedalDisplay( pMap ); + + if ( pMap ) + SetControlString( "CommentLabel", pMap->szComment ); + else + SetControlString( "CommentLabel", "" ); +} + +void CBonusMapsDialog::RefreshMedalDisplay( BonusMapDescription_t *pMap ) +{ + { + int iFirstChildIndex = FindChildIndexByName( "ChallengeMedalOverview00" ); + for ( int iMedal = 0; iMedal < 5; ++iMedal ) + { + Panel *pMedalImage = GetChild( iFirstChildIndex + iMedal ); + pMedalImage->SetVisible( false ); + } + } + if ( !pMap || !pMap->m_pChallenges ) + { + SetControlVisible( "ChallengeCommentLabel", false ); + SetControlVisible( "ChallengeEarnedMedal", false ); + SetControlVisible( "ChallengeBestLabel", false ); + SetControlVisible( "ChallengeNextMedal", false ); + SetControlVisible( "ChallengeNextLabel", false ); + + return; + } + + char szBuff[ 512 ]; + + int iChallenge = GetSelectedChallenge(); + + if ( iChallenge < 0 ) + { + SetControlVisible( "ChallengeCommentLabel", false ); + SetControlVisible( "ChallengeEarnedMedal", false ); + SetControlVisible( "ChallengeBestLabel", false ); + SetControlVisible( "ChallengeNextMedal", false ); + SetControlVisible( "ChallengeNextLabel", false ); + + int iFirstChildIndex = FindChildIndexByName( "ChallengeMedalOverview00" ); + for ( int iChallengeIndex = 0; iChallengeIndex < m_pChallengeSelection->GetItemCount(); ++iChallengeIndex ) + { + KeyValues *pUserDataKey = m_pChallengeSelection->GetItemUserData( iChallengeIndex ); + int iChallengeNum = pUserDataKey->GetInt( "challenge" ); + + if ( iChallengeNum >= 0 ) + { + ChallengeDescription_t *pChallengeDescription = NULL; + + for ( int i = 0 ; i < pMap->m_pChallenges->Count(); ++i ) + { + int iType = ((*pMap->m_pChallenges)[ i ]).iType; + + if ( iType == -1 ) + { + iType = i; + } + + if ( iType == iChallengeNum ) + pChallengeDescription = &((*pMap->m_pChallenges)[ i ]); + } + + if ( pChallengeDescription ) + { + int iBest, iEarnedMedal, iNext, iNextMedal; + GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal ); + + if ( iChallengeNum < 10 ) + Q_snprintf( szBuff, 256, "medals/medal_0%i_%s", iChallengeNum, g_pszMedalNames[ iEarnedMedal ] ); + else + Q_snprintf( szBuff, 256, "medals/medal_%i_%s", iChallengeNum, g_pszMedalNames[ iEarnedMedal ] ); + } + + CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( GetChild( iFirstChildIndex + iChallengeNum ) ); + pBitmap->SetVisible( true ); + pBitmap->setTexture( szBuff ); + } + } + + return; + } + + ChallengeDescription_t *pChallengeDescription = NULL; + + for ( int i = 0 ; i < pMap->m_pChallenges->Count(); ++i ) + { + int iType = ((*pMap->m_pChallenges)[ i ]).iType; + + if ( iType == -1 ) + { + iType = i; + } + + if ( iType == iChallenge ) + pChallengeDescription = &((*pMap->m_pChallenges)[ i ]); + } + + if ( !pChallengeDescription ) + return; + + const char *pchChallengeComment = pChallengeDescription->szComment; + + int iBest, iEarnedMedal, iNext, iNextMedal; + GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal ); + + // Set comment label + SetControlString( "ChallengeCommentLabel", pchChallengeComment ); + SetControlVisible( "ChallengeCommentLabel", true ); + + // Set earned medal + if ( iEarnedMedal > -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<CBitmapImagePanel*>( FindChildByName( "ChallengeEarnedMedal" ) ); + pBitmap->SetVisible( true ); + pBitmap->setTexture( szBuff ); + } + + // 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<CBitmapImagePanel*>( 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 ); +} + +void CBonusMapsDialog::RefreshCompletionPercentage( void ) +{ + float fPercentage = BonusMapsDatabase()->GetCompletionPercentage(); + if ( fPercentage > 0.0f ) + { + char szBuff[ 256 ]; + if ( fPercentage * 100.0f < 1.0f ) + Q_snprintf( szBuff, 256, "%.2f%%", fPercentage * 100.0f ); // Show decimal places if less than 1% + else + Q_snprintf( szBuff, 256, "%.0f%%", fPercentage * 100.0f ); + + SetControlString( "PercentageText", szBuff ); + SetControlVisible( "PercentageText", true ); + SetControlVisible( "CompletionText", true ); + + // Blend the color from backround color 0% to selected color 100% + Color cProgressBar = Color( static_cast<float>( m_PercentageBarBackgroundColor.r() ) * ( 1.0f - fPercentage ) + static_cast<float>( m_PercentageBarColor.r() ) * fPercentage, + static_cast<float>( m_PercentageBarBackgroundColor.g() ) * ( 1.0f - fPercentage ) + static_cast<float>( m_PercentageBarColor.g() ) * fPercentage, + static_cast<float>( m_PercentageBarBackgroundColor.b() ) * ( 1.0f - fPercentage ) + static_cast<float>( m_PercentageBarColor.b() ) * fPercentage, + static_cast<float>( m_PercentageBarBackgroundColor.a() ) * ( 1.0f - fPercentage ) + static_cast<float>( m_PercentageBarColor.a() ) * fPercentage ); + + m_pPercentageBar->SetFillColor( cProgressBar ); + m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * fPercentage ); + + SetControlVisible( "PercentageBarBackground", true ); + SetControlVisible( "PercentageBar", true ); + } + else + { + // 0% complete so don't display + SetControlVisible( "PercentageText", false ); + SetControlVisible( "CompletionText", false ); + SetControlVisible( "PercentageBarBackground", false ); + SetControlVisible( "PercentageBar", false ); + } +} + + +void CBonusMapsDialog::ApplySchemeSettings( IScheme *pScheme ) +{ + m_PercentageBarBackgroundColor = Color( 0, 0, 0, 64 ); + m_PercentageBarColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + BaseClass::ApplySchemeSettings( pScheme ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBonusMapsDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "loadbonusmap" ) ) + { + int mapIndex = GetSelectedItemBonusMapIndex(); + if ( BonusMapsDatabase()->IsValidIndex( mapIndex ) ) + { + BonusMapDescription_t *pBonusMap = BonusMapsDatabase()->GetBonusData( mapIndex ); + + // Don't do anything with locked items + if ( pBonusMap->bLocked || ( m_pChallengeSelection->IsEnabled() && GetSelectedChallenge() == -1 ) ) + return; + + const char *shortName = pBonusMap->szShortName; + if ( shortName && shortName[ 0 ] ) + { + if ( pBonusMap->bIsFolder ) + { + BonusMapsDatabase()->AppendPath( shortName ); + + // repopulate list with current directory + BuildMapsList(); + + m_pGameList->MoveScrollBarToTop(); + } + else + { + // Load the game, return to top and switch to engine + char sz[ 256 ]; + + // Set the challenge mode if one is selected + int iChallenge = GetSelectedChallenge() + 1; + + if ( iChallenge > 0 ) + { + Q_snprintf( sz, sizeof( sz ), "sv_bonus_challenge %i\n", iChallenge ); + engine->ClientCmd_Unrestricted( sz ); + + ChallengeDescription_t *pChallengeDescription = &((*pBonusMap->m_pChallenges)[ iChallenge - 1 ]); + + // Set up medal goals + BonusMapsDatabase()->SetCurrentChallengeObjectives( pChallengeDescription->iBronze, pChallengeDescription->iSilver, pChallengeDescription->iGold ); + BonusMapsDatabase()->SetCurrentChallengeNames( pBonusMap->szFileName, pBonusMap->szMapName, pChallengeDescription->szName ); + } + + if ( pBonusMap->szMapFileName[ 0 ] != '.' ) + { + Q_snprintf( sz, sizeof( sz ), "map %s\n", pBonusMap->szMapFileName ); + } + else + { + const char *pchSubDir = Q_strnchr( BonusMapsDatabase()->GetPath(), '/', Q_strlen( BonusMapsDatabase()->GetPath() ) ); + + if ( pchSubDir ) + { + pchSubDir = Q_strnchr( pchSubDir + 1, '/', Q_strlen( pchSubDir ) ); + + if ( pchSubDir ) + { + ++pchSubDir; + const char *pchMapFileName = pBonusMap->szMapFileName + 2; + Q_snprintf( sz, sizeof( sz ), "map %s/%s\n", pchSubDir, pchMapFileName ); + } + } + } + + engine->ClientCmd_Unrestricted( sz ); + + // Close this dialog + //OnClose(); + } + } + } + } + else if ( !stricmp( command, "back" ) ) + { + BonusMapsDatabase()->BackPath(); + + // repopulate list with current directory + BuildMapsList(); + + m_pChallengeSelection->RemoveAll(); + m_pChallengeSelection->AddItem( "<Select A Challenge>", new KeyValues( "ChallengeSelection", "challenge", -1 ) ); + + RefreshDialog( NULL ); + + m_pGameList->MoveScrollBarToTop(); + } + else if ( !stricmp( command, "ImportBonusMaps" ) ) + { + if ( m_hImportBonusMapsDialog == NULL ) + { + m_hImportBonusMapsDialog = new FileOpenDialog( NULL, "#GameUI_ImportBonusMaps", true ); + m_hImportBonusMapsDialog->AddFilter( "*.bmz", "#GameUI_BMZ_Files", true ); + m_hImportBonusMapsDialog->AddActionSignalTarget( this ); + } + m_hImportBonusMapsDialog->DoModal(false); + m_hImportBonusMapsDialog->Activate(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +void CBonusMapsDialog::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_ESCAPE ) + { + OnCommand( "Close" ); + return; + } + + BaseClass::OnKeyCodeTyped( code ); +} + +void CBonusMapsDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_B ) + { + OnCommand( "Close" ); + return; + } + else if ( code == KEY_XSTICK1_RIGHT || + code == KEY_XSTICK2_RIGHT || + code == KEY_XBUTTON_RIGHT || + code == KEY_RIGHT ) + { + if ( m_pGameList->GetItemCount() ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( !pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) ); + m_pGameList->ScrollToItem( m_pGameList->FirstItem() ); + return; + } + else + { + int nNextPanelID = m_pGameList->FirstItem(); + while ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel ) + { + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + if ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nNextPanelID ) ); + m_pGameList->ScrollToItem( nNextPanelID ); + return; + } + + break; + } + + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + } + } + } + } + else if ( code == KEY_XSTICK1_LEFT || + code == KEY_XSTICK2_LEFT || + code == KEY_XBUTTON_LEFT || + code == KEY_LEFT ) + { + if ( m_pGameList->GetItemCount() ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( !pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( m_pGameList->FirstItem() ) ); + m_pGameList->ScrollToItem( m_pGameList->FirstItem() ); + return; + } + else + { + int nNextPanelID = m_pGameList->FirstItem(); + if ( m_pGameList->GetItemPanel( nNextPanelID ) != pSelectedPanel ) + { + while ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + int nOldPanelID = nNextPanelID; + nNextPanelID = m_pGameList->NextItem( nNextPanelID ); + + if ( nNextPanelID != m_pGameList->InvalidItemID() ) + { + if ( m_pGameList->GetItemPanel( nNextPanelID ) == pSelectedPanel ) + { + m_pGameList->SetSelectedPanel( m_pGameList->GetItemPanel( nOldPanelID ) ); + m_pGameList->ScrollToItem( nOldPanelID ); + return; + } + } + } + } + } + } + } + else if ( code == KEY_XSTICK1_DOWN || + code == KEY_XSTICK2_DOWN || + code == KEY_XBUTTON_DOWN || + code == KEY_DOWN ) + { + int nOldActiveItem = m_pChallengeSelection->GetActiveItem(); + int nActiveItem = min( nOldActiveItem + 1, m_pChallengeSelection->GetItemCount() - 1 ); + + if ( nOldActiveItem != nActiveItem ) + { + m_pChallengeSelection->ActivateItem( nActiveItem ); + return; + } + } + else if ( code == KEY_XSTICK1_UP || + code == KEY_XSTICK2_UP || + code == KEY_XBUTTON_UP || + code == KEY_UP ) + { + int nOldActiveItem = m_pChallengeSelection->GetActiveItem(); + int nActiveItem = max( nOldActiveItem - 1, 0 ); + + if ( nOldActiveItem != nActiveItem ) + { + m_pChallengeSelection->ActivateItem( nActiveItem ); + return; + } + } + else if ( code == KEY_ENTER || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + Panel *pSelectedPanel = m_pGameList->GetSelectedPanel(); + if ( pSelectedPanel ) + { + OnCommand( "loadbonusmap" ); + return; + } + } + + BaseClass::OnKeyCodePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: One item has been selected +//----------------------------------------------------------------------------- +void CBonusMapsDialog::OnPanelSelected() +{ + CBonusMapPanel *pSelectedBonusMapPanel = (CBonusMapPanel *)m_pGameList->GetSelectedPanel(); + if ( !pSelectedBonusMapPanel ) + return; + + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( pSelectedBonusMapPanel->GetBonusMapListItemID() ); + if ( !pMap ) + return; + + SetControlString( "CommentLabel", pMap->szComment ); + + // Handle challenge selection box + int iNumChallenges = 0; + + if ( pMap->m_pChallenges ) + { + // Get the name of the previously selected challenge so that we can select a matching challenge (if one exists) + int iSelectedChallenge = GetSelectedChallenge(); + + m_pChallengeSelection->RemoveAll(); + + m_pChallengeSelection->AddItem( "<Select A Challenge>", new KeyValues( "ChallengeSelection", "challenge", -1 ) ); + + int iFoundSimilar = 0; + + for ( iNumChallenges; iNumChallenges < pMap->m_pChallenges->Count(); ++iNumChallenges ) + { + ChallengeDescription_t *pChallenge = &(*pMap->m_pChallenges)[ iNumChallenges ]; + int iType = iNumChallenges; + + // If the challenge type was specified then use that instead of (legacy) the order the challenges were listed + if ( pChallenge->iType != -1 ) + iType = pChallenge->iType; + + m_pChallengeSelection->AddItem( pChallenge->szName, new KeyValues( "ChallengeSelection", "challenge", iType ) ); + + if ( iSelectedChallenge == iNumChallenges ) + iFoundSimilar = iNumChallenges + 1; + } + + m_pChallengeSelection->ActivateItemByRow( iFoundSimilar ); + } + + if ( iNumChallenges > 0 ) + { + m_pChallengeSelection->SetEnabled( true ); + m_pChallengeSelection->SetVisible( true ); + m_pChallengeSelection->SetNumberOfEditLines( iNumChallenges + 1 ); + } + else + { + m_pChallengeSelection->SetEnabled( false ); + m_pChallengeSelection->SetVisible( false ); + } + + RefreshDialog( pMap ); +} + +void CBonusMapsDialog::OnControlModified() +{ + CBonusMapPanel *pSelectedBonusMapPanel = (CBonusMapPanel *)m_pGameList->GetSelectedPanel(); + if ( !pSelectedBonusMapPanel ) + return; + BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( pSelectedBonusMapPanel->GetBonusMapListItemID() ); + if ( !pMap ) + return; + RefreshDialog( pMap ); +} + +// file selected. This can only happen when someone selects an image to be imported as a spray logo. +void CBonusMapsDialog::OnFileSelected( const char *fullpath ) +{ + if ( fullpath == NULL || fullpath[ 0 ] == '\0' ) + return; + + // this can take a while, put up a waiting cursor + surface()->SetCursor(dc_hourglass); + + ImportZippedBonusMaps( fullpath ); + + // change the cursor back to normal + surface()->SetCursor(dc_user); +} diff --git a/gameui/BonusMapsDialog.h b/gameui/BonusMapsDialog.h new file mode 100644 index 0000000..cd7cbae --- /dev/null +++ b/gameui/BonusMapsDialog.h @@ -0,0 +1,75 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BONUSMAPSDIALOG_H +#define BONUSMAPSDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +#include "BonusMapsDatabase.h" + + +//----------------------------------------------------------------------------- +// Purpose: Displays and loads available bonus maps +//----------------------------------------------------------------------------- +class CBonusMapsDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBonusMapsDialog, vgui::Frame ); + +public: + CBonusMapsDialog(vgui::Panel *parent); + ~CBonusMapsDialog(); + + void SetSelectedBooleanStatus( const char *pchName, bool bValue ); + void RefreshData( void ); + + int GetSelectedChallenge( void ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + + void OnKeyCodeTyped( vgui::KeyCode code ); + void OnKeyCodePressed( vgui::KeyCode code ); + +private: + bool ImportZippedBonusMaps( const char *pchZippedFileName ); + + void BuildMapsList( void ); + + void CreateBonusMapsList(); + int GetSelectedItemBonusMapIndex(); + + void RefreshDialog( BonusMapDescription_t *pMap ); + void RefreshMedalDisplay( BonusMapDescription_t *pMap ); + void RefreshCompletionPercentage( void ); + + MESSAGE_FUNC( OnPanelSelected, "PanelSelected" ); + MESSAGE_FUNC( OnControlModified, "ControlModified" ); + MESSAGE_FUNC( OnTextChanged, "TextChanged" ) + { + OnControlModified(); + } + MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath ); + +private: + Color m_PercentageBarBackgroundColor, m_PercentageBarColor; + + vgui::FileOpenDialog *m_hImportBonusMapsDialog; + vgui::PanelListPanel *m_pGameList; + vgui::ComboBox *m_pChallengeSelection; + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; +}; + + +extern CBonusMapsDialog *g_pBonusMapsDialog; + + +#endif // BONUSMAPSDIALOG_H diff --git a/gameui/CDKeyEntryDialog.cpp b/gameui/CDKeyEntryDialog.cpp new file mode 100644 index 0000000..2ee6791 --- /dev/null +++ b/gameui/CDKeyEntryDialog.cpp @@ -0,0 +1,323 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "CDKeyEntryDialog.h" +#include "EngineInterface.h" + +#include "steamcommon.h" // VALVE_AUTH cd key checking code +#include "ValidateNewValveCDKeyClient.h" +#include "ValveCDKeyGameAndTerritoryCodes.h" + +#include "vgui/IInput.h" +#include "vgui/IPanel.h" +#include "vgui/ISystem.h" +#include "vgui/ISurface.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/TextEntry.h" +#include "vstdlib/random.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#include <ctype.h> + +using namespace vgui; + +#define FAKE_CDKEY_LEN 49 +#define FAKE_CDKEY_REGKEY "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Settings\\EncryptedCDKey" + +//----------------------------------------------------------------------------- +// Purpose: hacky class to make all input uppercase +//----------------------------------------------------------------------------- +class CUpperCaseTextEntry : public vgui::TextEntry +{ + DECLARE_CLASS_SIMPLE( CUpperCaseTextEntry, vgui::TextEntry ); +public: + CUpperCaseTextEntry(vgui::Panel *parent, const char *name) : TextEntry(parent, name) {} + + virtual void InsertChar(wchar_t unichar) + { + // only allow input of valid cdkey characters + if (unichar >= 'a' && unichar <= 'z') + { + // force to be uppercase + BaseClass::InsertChar(unichar - 'a' + 'A'); + } + else if ((unichar >= 'A' && unichar <= 'Z') + || (unichar >= '0' && unichar <= '9')) + { + BaseClass::InsertChar(unichar); + } + } +}; + + +class CloseMessageBox : public vgui::MessageBox +{ +public: + CloseMessageBox(const char *title, const char *text, Panel *parent = NULL): MessageBox( title, text, parent) {} + + virtual void OnClose() + { + engine->ClientCmd_Unrestricted("quit\n"); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCDKeyEntryDialog::CCDKeyEntryDialog(vgui::Panel *parent, bool inConnect) : BaseClass(parent, "CDKeyEntryDialog") +{ + // see what type of cdkey we have + SetDeleteSelfOnClose(true); + m_bInConnect = inConnect; + m_iErrCount = 0; + m_bEnteredValidCDKey = false; + + m_pOK = new Button(this, "OKButton", "#GameUI_OK"); + m_pQuitGame = new Button(this, "CancelButton", "#GameUI_Quit"); + m_pEntry1 = new CUpperCaseTextEntry(this, "Entry1");; + m_pEntry2 = new CUpperCaseTextEntry(this, "Entry2"); + m_pEntry3 = new CUpperCaseTextEntry(this, "Entry3"); + m_pEntry4 = new CUpperCaseTextEntry(this, "Entry4"); + m_pEntry5 = new CUpperCaseTextEntry(this, "Entry5"); + + m_pEntry1->SetMaximumCharCount(5); + m_pEntry2->SetMaximumCharCount(5); + m_pEntry3->SetMaximumCharCount(5); + m_pEntry4->SetMaximumCharCount(5); + m_pEntry5->SetMaximumCharCount(5); + + m_pEntry1->SetAutoProgressOnHittingCharLimit(true); + m_pEntry2->SetAutoProgressOnHittingCharLimit(true); + m_pEntry3->SetAutoProgressOnHittingCharLimit(true); + m_pEntry4->SetAutoProgressOnHittingCharLimit(true); + m_pEntry5->SetAutoProgressOnHittingCharLimit(false); + + SetSizeable(false); + SetSize(360, 224); + SetTitle("#GameUI_CDKey", true); + + LoadControlSettings("Resource/ValveCDKeyEntryDialog.res"); +// MoveToCenterOfScreen(); + + SetMinimizeButtonVisible(false); + SetMaximizeButtonVisible(false); + + m_pOK->SetEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCDKeyEntryDialog::~CCDKeyEntryDialog() +{ + vgui::surface()->RestrictPaintToSinglePanel(NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if there is a valid weak check key in the registry +//----------------------------------------------------------------------------- +bool CCDKeyEntryDialog::IsValidWeakCDKeyInRegistry() +{ + char fakekey[FAKE_CDKEY_LEN]; + if (vgui::system()->GetRegistryString(FAKE_CDKEY_REGKEY, fakekey, sizeof(fakekey))) + { + if (strlen(fakekey) == (FAKE_CDKEY_LEN - 1) + && fakekey[17] == 'E' + && fakekey[31] == 'z' + && fakekey[43] == 'n') + { + return true; + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCDKeyEntryDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "OK")) + { + if (IsEnteredKeyValid()) + { + m_bEnteredValidCDKey = true; + + // write out fake key and continue + char fakekey[FAKE_CDKEY_LEN]; + for (int i = 0; i < sizeof(fakekey) - 1; i++) + { + // pick some random fields for us to check later + if (i == 17) + { + fakekey[i] = 'E'; + } + else if (i == 31) + { + fakekey[i] = 'z'; + } + else if (i == 43) + { + fakekey[i] = 'n'; + } + else + { + fakekey[i] = RandomInt('0', 'z'); + } + } + fakekey[sizeof(fakekey) - 1] = 0; + vgui::system()->SetRegistryString(FAKE_CDKEY_REGKEY, fakekey); + + if ( m_bInConnect ) + { + engine->ClientCmd_Unrestricted( "retry\n" ); // retry the server connection with this new key... + } + Close(); + } + else + { + m_hErrorBox = new MessageBox("#GameUI_CDKey_Invalid_Title","#GameUI_CDKey_Invalid_Text", this); + m_hErrorBox->ShowWindow( this ); + } + } + else if (!stricmp(command, "Cancel") || !stricmp(command, "Close")) + { + Close(); + } + else + { + BaseClass::OnCommand(command); + } + + if (!m_bEnteredValidCDKey) // moved away from the dialog box to make it a little harder to crack... + { + m_iErrCount++; + if( m_iErrCount >= MAX_CDKEY_ERRORS ) + { + // too many bad entries, make em quit + CloseMessageBox *bx = new CloseMessageBox("#GameUI_CDKey_Invalid_Title","#GameUI_CDKey_TooManyTries", this); + bx->ShowWindow( this ); + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCDKeyEntryDialog::OnClose() +{ + if (!m_bEnteredValidCDKey) + { + // if we don't have a valid key we can't continue + engine->ClientCmd_Unrestricted("quit\n"); + } + + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCDKeyEntryDialog::OnThink() +{ + if (!m_bEnteredValidCDKey) + { + // force us to be the only thing to draw + VPANEL vpanel = m_hErrorBox.Get() ? m_hErrorBox->GetVPanel() : GetVPanel(); + vgui::surface()->RestrictPaintToSinglePanel(vpanel); + + // make sure we're the focus + if (!(input()->GetFocus() && ipanel()->HasParent(input()->GetFocus(), GetVPanel()))) + { + BaseClass::Activate(); + } + } + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns whether or not the key entered by the user passes the simple check +//----------------------------------------------------------------------------- +bool CCDKeyEntryDialog::IsEnteredKeyValid() +{ + // get the entered text + char cdkey[32]; + m_pEntry1->GetText(cdkey, 6); + m_pEntry2->GetText(cdkey + 6, 6); + m_pEntry3->GetText(cdkey + 12, 6); + m_pEntry4->GetText(cdkey + 18, 6); + m_pEntry5->GetText(cdkey + 24, 6); + + // add in the hyphens + cdkey[5] = '-'; + cdkey[11] = '-'; + cdkey[17] = '-'; + cdkey[23] = '-'; + + // verify the entry + for (int i = 0; i < 29; i++) + { + if (cdkey[i] != '-' && !isalnum(cdkey[i])) + return false; + } +#if !defined( NO_STEAM ) + uint gameCode, salesTerritoryCode, uniqueSerialNumber; + + // test key - this key passes they weak check, but not the strong check + // "5J5Q3-QCME2-2SMMV-SBHN9-43S2S" + if ( SteamWeakVerifyNewValveCDKey(cdkey, &gameCode, &salesTerritoryCode, &uniqueSerialNumber) == eSteamErrorNone ) + { + // require hl2 retail cdkey (ATI Bundle keys are also in this category) + if (gameCode == eHalfLife2) + return true; + } +#endif + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: handles user entering data in fields +//----------------------------------------------------------------------------- +void CCDKeyEntryDialog::OnTextChanged(Panel *entry) +{ + char cdkey[32]; + m_pEntry1->GetText(cdkey, 6); + m_pEntry2->GetText(cdkey + 6, 6); + m_pEntry3->GetText(cdkey + 12, 6); + m_pEntry4->GetText(cdkey + 18, 6); + m_pEntry5->GetText(cdkey + 24, 6); + + // add in the hyphens + cdkey[5] = '-'; + cdkey[11] = '-'; + cdkey[17] = '-'; + cdkey[23] = '-'; + + if (strlen(cdkey) == 29) + { + m_pOK->SetEnabled(true); + } + else + { + m_pOK->SetEnabled(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCDKeyEntryDialog::Activate() +{ + BaseClass::Activate(); + m_pEntry1->RequestFocus(); +} diff --git a/gameui/CDKeyEntryDialog.h b/gameui/CDKeyEntryDialog.h new file mode 100644 index 0000000..1200826 --- /dev/null +++ b/gameui/CDKeyEntryDialog.h @@ -0,0 +1,58 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CDKEYENTRYDIALOG_H +#define CDKEYENTRYDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CCDKeyEntryDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CCDKeyEntryDialog, vgui::Frame ); + +public: + CCDKeyEntryDialog(vgui::Panel *parent, bool inConnect = false); + ~CCDKeyEntryDialog(); + + virtual void Activate(); + + static bool IsValidWeakCDKeyInRegistry(); + +private: + enum { MAX_CDKEY_ERRORS = 5 }; + + virtual void OnCommand(const char *command); + virtual void OnClose(); + virtual void OnThink(); + + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + bool IsEnteredKeyValid(); + + vgui::Button *m_pOK; + vgui::Button *m_pQuitGame; + vgui::TextEntry *m_pEntry1; + vgui::TextEntry *m_pEntry2; + vgui::TextEntry *m_pEntry3; + vgui::TextEntry *m_pEntry4; + vgui::TextEntry *m_pEntry5; + + vgui::DHANDLE<vgui::MessageBox> m_hErrorBox; + + bool m_bEnteredValidCDKey; + + bool m_bInConnect; + int m_iErrCount; +}; + + +#endif // CDKEYENTRYDIALOG_H diff --git a/gameui/ChangeGameDialog.cpp b/gameui/ChangeGameDialog.cpp new file mode 100644 index 0000000..8e6101b --- /dev/null +++ b/gameui/ChangeGameDialog.cpp @@ -0,0 +1,161 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#ifdef _XBOX +#include "xbox/xbox_platform.h" +#include "xbox/xbox_win32stubs.h" +#endif + +#if !defined( _X360 ) +#include <windows.h> +#endif +#include <stdio.h> + +#include "ChangeGameDialog.h" +#include "ModInfo.h" +#include "EngineInterface.h" + +#include <vgui_controls/ListPanel.h> +#include <KeyValues.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CChangeGameDialog::CChangeGameDialog(vgui::Panel *parent) : Frame(parent, "ChangeGameDialog") +{ + SetSize(400, 340); + SetMinimumSize(400, 340); + SetTitle("#GameUI_ChangeGame", true); + + m_pModList = new ListPanel(this, "ModList"); + m_pModList->SetEmptyListText("#GameUI_NoOtherGamesAvailable"); + m_pModList->AddColumnHeader(0, "ModName", "#GameUI_Game", 128); + + LoadModList(); + LoadControlSettings("Resource/ChangeGameDialog.res"); + + // if there's a mod in the list, select the first one + if (m_pModList->GetItemCount() > 0) + { + m_pModList->SetSingleSelectedItem(m_pModList->GetItemIDFromRow(0)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CChangeGameDialog::~CChangeGameDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Fills the mod list +//----------------------------------------------------------------------------- +void CChangeGameDialog::LoadModList() +{ + // look for third party games + char szSearchPath[_MAX_PATH + 5]; + Q_strncpy(szSearchPath, "*.*", sizeof( szSearchPath ) ); + + // use local filesystem since it has to look outside path system, and will never be used under steam + WIN32_FIND_DATA wfd; + HANDLE hResult; + memset(&wfd, 0, sizeof(WIN32_FIND_DATA)); + + hResult = FindFirstFile( szSearchPath, &wfd); + if (hResult != INVALID_HANDLE_VALUE) + { + BOOL bMoreFiles; + while (1) + { + if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (Q_strnicmp(wfd.cFileName, ".", 1))) + { + // Check for dlls\*.dll + char szDllDirectory[MAX_PATH + 16]; + Q_snprintf(szDllDirectory, sizeof( szDllDirectory ), "%s\\gameinfo.txt", wfd.cFileName); + + FILE *f = fopen(szDllDirectory, "rb"); + if (f) + { + // find the description + fseek(f, 0, SEEK_END); + unsigned int size = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = (char *)malloc(size + 1); + if (fread(buf, 1, size, f) == size) + { + buf[size] = 0; + + CModInfo modInfo; + modInfo.LoadGameInfoFromBuffer(buf); + + if (strcmp(modInfo.GetGameName(), ModInfo().GetGameName())) + { + // Add the game directory. + strlwr(wfd.cFileName); + KeyValues *itemData = new KeyValues("Mod"); + itemData->SetString("ModName", modInfo.GetGameName()); + itemData->SetString("ModDir", wfd.cFileName); + m_pModList->AddItem(itemData, 0, false, false); + } + } + free(buf); + fclose(f); + } + } + bMoreFiles = FindNextFile(hResult, &wfd); + if (!bMoreFiles) + break; + } + + FindClose(hResult); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChangeGameDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "OK")) + { + if (m_pModList->GetSelectedItemsCount() > 0) + { + KeyValues *kv = m_pModList->GetItem(m_pModList->GetSelectedItem(0)); + if (kv) + { + // change the game dir and restart the engine + char szCmd[256]; + Q_snprintf(szCmd, sizeof( szCmd ), "_setgamedir %s\n", kv->GetString("ModDir")); + engine->ClientCmd_Unrestricted(szCmd); + + // Force restart of entire engine + engine->ClientCmd_Unrestricted("_restart\n"); + } + } + } + else if (!stricmp(command, "Cancel")) + { + Close(); + } + else + { + BaseClass::OnCommand(command); + } +} + + + + + + diff --git a/gameui/ChangeGameDialog.h b/gameui/ChangeGameDialog.h new file mode 100644 index 0000000..3bd4d7c --- /dev/null +++ b/gameui/ChangeGameDialog.h @@ -0,0 +1,36 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CHANGEGAMEDIALOG_H +#define CHANGEGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> + +//----------------------------------------------------------------------------- +// Purpose: Dialogs for use to change current loaded mod +//----------------------------------------------------------------------------- +class CChangeGameDialog : public vgui::Frame +{ +public: + CChangeGameDialog(vgui::Panel *parent); + ~CChangeGameDialog(); + + virtual void OnCommand( const char *command ); + +private: + void LoadModList(); + + vgui::ListPanel *m_pModList; + + typedef vgui::Frame BaseClass; +}; + + +#endif // CHANGEGAMEDIALOG_H diff --git a/gameui/CommandCheckButton.cpp b/gameui/CommandCheckButton.cpp new file mode 100644 index 0000000..1ea98ce --- /dev/null +++ b/gameui/CommandCheckButton.cpp @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "CommandCheckButton.h" +#include "EngineInterface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +CCommandCheckButton::CCommandCheckButton( Panel *parent, const char *panelName, const char *text, const char *downcmd, const char *upcmd ) + : CheckButton( parent, panelName, text ) +{ + m_pszDown = downcmd ? strdup( downcmd ) : NULL; + m_pszUp = upcmd ? strdup( upcmd ) : NULL; +} + +CCommandCheckButton::~CCommandCheckButton() +{ + free( m_pszDown ); + free( m_pszUp ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *panel - +//----------------------------------------------------------------------------- +void CCommandCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); + + if ( IsSelected() && m_pszDown ) + { + engine->ClientCmd_Unrestricted( m_pszDown ); + engine->ClientCmd_Unrestricted( "\n" ); + } + else if ( !IsSelected() && m_pszUp ) + { + engine->ClientCmd_Unrestricted( m_pszUp ); + engine->ClientCmd_Unrestricted( "\n" ); + } +} diff --git a/gameui/CommandCheckButton.h b/gameui/CommandCheckButton.h new file mode 100644 index 0000000..24dbff5 --- /dev/null +++ b/gameui/CommandCheckButton.h @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef COMMANDCHECKBUTTON_H +#define COMMANDCHECKBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/CheckButton.h> + +class CCommandCheckButton : public vgui::CheckButton +{ +public: + CCommandCheckButton( vgui::Panel *parent, const char *panelName, const char *text, const char *downcmd, const char *upcmd ); + ~CCommandCheckButton(); + +// virtual void OnCheckButtonChecked(vgui::Panel *panel); + virtual void SetSelected( bool state ); +private: + typedef vgui::CheckButton BaseClass; + + char *m_pszDown; + char *m_pszUp; +}; +#endif // COMMANDCHECKBUTTON_H diff --git a/gameui/CommentaryDialog.cpp b/gameui/CommentaryDialog.cpp new file mode 100644 index 0000000..6f3479c --- /dev/null +++ b/gameui/CommentaryDialog.cpp @@ -0,0 +1,255 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "CommentaryDialog.h" +#include "BasePanel.h" +#include "convar.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" + +#include <stdio.h> + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCommentaryDialog::CCommentaryDialog(vgui::Panel *parent) : BaseClass(parent, "CommentaryDialog") +{ + SetDeleteSelfOnClose(true); + SetSizeable( false ); + + input()->SetAppModalSurface(GetVPanel()); + vgui::surface()->RestrictPaintToSinglePanel(GetVPanel()); + GameUI().PreventEngineHideGameUI(); + + SetTitle("#GameUI_CommentaryDialogTitle", true); + + LoadControlSettings("Resource/CommentaryDialog.res"); + + MoveToCenterOfScreen(); + + bool bCommentaryOn = false; + ConVarRef commentary( "commentary" ); + if ( commentary.IsValid() ) + { + bCommentaryOn = commentary.GetBool(); + } + + // Setup the buttons & labels to reflect the current state of the commentary + if ( bCommentaryOn ) + { + SetControlString( "ModeLabel", "#GAMEUI_Commentary_LabelOn" ); + SetControlString( "TurnOnButton", "#GAMEUI_Commentary_LeaveOn" ); + SetControlString( "TurnOffButton", "#GAMEUI_Commentary_TurnOff" ); + } + else + { + SetControlString( "ModeLabel", "#GAMEUI_Commentary_LabelOff" ); + SetControlString( "TurnOnButton", "#GAMEUI_Commentary_TurnOn" ); + SetControlString( "TurnOffButton", "#GAMEUI_Commentary_LeaveOff" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCommentaryDialog::~CCommentaryDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCommentaryDialog::OnClose( void ) +{ + BaseClass::OnClose(); + + vgui::surface()->RestrictPaintToSinglePanel(NULL); + GameUI().AllowEngineHideGameUI(); + + // Bring up the post dialog + DHANDLE<CPostCommentaryDialog> hPostCommentaryDialog; + if ( !hPostCommentaryDialog.Get() ) + { + hPostCommentaryDialog = new CPostCommentaryDialog( BasePanel() ); + } + hPostCommentaryDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *command - +//----------------------------------------------------------------------------- +void CCommentaryDialog::OnCommand( const char *command ) +{ + if ( !Q_stricmp( command, "TurnOn" ) ) + { + ConVarRef commentary("commentary"); + commentary.SetValue( 1 ); + Close(); + } + else if ( !Q_stricmp( command, "TurnOff" ) ) + { + ConVarRef commentary("commentary"); + commentary.SetValue( 0 ); + Close(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCommentaryDialog::OnKeyCodePressed(KeyCode code) +{ + // Ignore escape key + if ( code == KEY_ESCAPE ) + return; + + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + OnCommand( "TurnOn" ); + return; + } + else if ( code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B ) + { + OnCommand( "TurnOff" ); + return; + } + + BaseClass::OnKeyCodePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void OpenCommentaryDialog( void ) +{ + DHANDLE<CCommentaryDialog> hCommentaryDialog; + if ( !hCommentaryDialog.Get() ) + { + hCommentaryDialog = new CCommentaryDialog( BasePanel() ); + } + + GameUI().ActivateGameUI(); + hCommentaryDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ConVar commentary_firstrun("commentary_firstrun", "0", FCVAR_ARCHIVE ); +void CC_CommentaryTestFirstRun( void ) +{ + // The enable/disable commentary box in the sound options got lost in time; + // always prompt the user for commentary mode instead on new game. + //if ( !commentary_firstrun.GetBool() ) + { + commentary_firstrun.SetValue(1); + OpenCommentaryDialog(); + } +} +static ConCommand commentary_testfirstrun("commentary_testfirstrun", CC_CommentaryTestFirstRun, 0 ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPostCommentaryDialog::CPostCommentaryDialog(vgui::Panel *parent) : BaseClass(parent, "PostCommentaryDialog") +{ + SetDeleteSelfOnClose(true); + SetSizeable( false ); + + input()->SetAppModalSurface(GetVPanel()); + vgui::surface()->RestrictPaintToSinglePanel(GetVPanel()); + m_bResetPaintRestrict = false; + + SetTitle("#GameUI_CommentaryDialogTitle", true); + + LoadControlSettings("Resource/PostCommentaryDialog.res"); + + MoveToCenterOfScreen(); + + bool bCommentaryOn = false; + ConVarRef commentary("commentary"); + if ( commentary.IsValid() ) + { + bCommentaryOn = commentary.GetBool(); + } + + // Setup the buttons & labels to reflect the current state of the commentary + if ( bCommentaryOn ) + { + SetControlString( "PostModeLabel", "#GAMEUI_PostCommentary_ModeLabelOn" ); + } + else + { + SetControlString( "PostModeLabel", "#GAMEUI_PostCommentary_ModeLabelOff" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPostCommentaryDialog::~CPostCommentaryDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPostCommentaryDialog::OnFinishedClose( void ) +{ + BaseClass::OnFinishedClose(); + + if ( !m_bResetPaintRestrict ) + { + m_bResetPaintRestrict = true; + vgui::surface()->RestrictPaintToSinglePanel(NULL); + GameUI().HideGameUI(); + } +} + +void CPostCommentaryDialog::OnKeyCodeTyped(KeyCode code) +{ + if ( code == KEY_ESCAPE ) + { + Close(); + vgui::surface()->RestrictPaintToSinglePanel(NULL); + m_bResetPaintRestrict = true; + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPostCommentaryDialog::OnKeyCodePressed(KeyCode code) +{ + if ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B ) + { + Close(); + vgui::surface()->RestrictPaintToSinglePanel(NULL); + m_bResetPaintRestrict = true; + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} diff --git a/gameui/CommentaryDialog.h b/gameui/CommentaryDialog.h new file mode 100644 index 0000000..c1acae4 --- /dev/null +++ b/gameui/CommentaryDialog.h @@ -0,0 +1,55 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef COMMENTARYDIALOG_H +#define COMMENTARYDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "utlvector.h" +#include <vgui/KeyCode.h> + +class CGameChapterPanel; +class CSkillSelectionDialog; + +//----------------------------------------------------------------------------- +// Purpose: Handles selection of commentary mode on/off +//----------------------------------------------------------------------------- +class CCommentaryDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CCommentaryDialog, vgui::Frame ); + +public: + CCommentaryDialog(vgui::Panel *parent); + ~CCommentaryDialog(); + + virtual void OnClose( void ); + virtual void OnCommand( const char *command ); + virtual void OnKeyCodePressed(vgui::KeyCode code); +}; + +//----------------------------------------------------------------------------- +// Purpose: Small dialog to remind players on method of changing commentary mode +//----------------------------------------------------------------------------- +class CPostCommentaryDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CPostCommentaryDialog, vgui::Frame ); + +public: + CPostCommentaryDialog(vgui::Panel *parent); + ~CPostCommentaryDialog(); + + virtual void OnFinishedClose( void ); + virtual void OnKeyCodeTyped(vgui::KeyCode code); + virtual void OnKeyCodePressed(vgui::KeyCode code); + +private: + bool m_bResetPaintRestrict; +}; + +#endif // COMMENTARYDIALOG_H diff --git a/gameui/CommentaryExplanationDialog.cpp b/gameui/CommentaryExplanationDialog.cpp new file mode 100644 index 0000000..e4d88eb --- /dev/null +++ b/gameui/CommentaryExplanationDialog.cpp @@ -0,0 +1,107 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "CommentaryExplanationDialog.h" +#include "BasePanel.h" +#include "convar.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" + +#include <stdio.h> + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCommentaryExplanationDialog::CCommentaryExplanationDialog(vgui::Panel *parent, char *pszFinishCommand) : BaseClass(parent, "CommentaryExplanationDialog") +{ + SetDeleteSelfOnClose(true); + SetSizeable( false ); + + input()->SetAppModalSurface(GetVPanel()); + + LoadControlSettings("Resource/CommentaryExplanationDialog.res"); + + MoveToCenterOfScreen(); + + GameUI().PreventEngineHideGameUI(); + + // Save off the finish command + Q_snprintf( m_pszFinishCommand, sizeof( m_pszFinishCommand ), "%s", pszFinishCommand ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCommentaryExplanationDialog::~CCommentaryExplanationDialog() +{ +} + +void CCommentaryExplanationDialog::OnKeyCodeTyped(KeyCode code) +{ + if ( code == KEY_ESCAPE ) + { + OnCommand( "cancel" ); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCommentaryExplanationDialog::OnKeyCodePressed(KeyCode code) +{ + if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B || GetBaseButtonCode( code ) == STEAMCONTROLLER_B ) + { + OnCommand( "cancel" ); + } + else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_A || GetBaseButtonCode( code ) == STEAMCONTROLLER_A ) + { + OnCommand( "ok" ); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void CCommentaryExplanationDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "ok" ) ) + { + Close(); + BasePanel()->FadeToBlackAndRunEngineCommand( m_pszFinishCommand ); + } + else if ( !stricmp( command, "cancel" ) || !stricmp( command, "close" ) ) + { + Close(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCommentaryExplanationDialog::OnClose( void ) +{ + BaseClass::OnClose(); + GameUI().AllowEngineHideGameUI(); +} diff --git a/gameui/CommentaryExplanationDialog.h b/gameui/CommentaryExplanationDialog.h new file mode 100644 index 0000000..8ee6310 --- /dev/null +++ b/gameui/CommentaryExplanationDialog.h @@ -0,0 +1,37 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef COMMENTARYEXPLANATIONDIALOG_H +#define COMMENTARYEXPLANATIONDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "utlvector.h" +#include <vgui/KeyCode.h> + +//----------------------------------------------------------------------------- +// Purpose: Dialog that explains the commentary mode +//----------------------------------------------------------------------------- +class CCommentaryExplanationDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CCommentaryExplanationDialog, vgui::Frame ); + +public: + CCommentaryExplanationDialog(vgui::Panel *parent, char *pszFinishCommand); + ~CCommentaryExplanationDialog(); + + virtual void OnKeyCodeTyped(vgui::KeyCode code); + virtual void OnKeyCodePressed(vgui::KeyCode code); + virtual void OnCommand( const char *command ); + virtual void OnClose( void ); + +private: + char m_pszFinishCommand[ 512 ]; +}; + +#endif // COMMENTARYEXPLANATIONDIALOG_H diff --git a/gameui/ContentControlDialog.cpp b/gameui/ContentControlDialog.cpp new file mode 100644 index 0000000..7613914 --- /dev/null +++ b/gameui/ContentControlDialog.cpp @@ -0,0 +1,406 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <stdio.h> +#include <memory.h> +#if defined( WIN32 ) && !defined( _X360 ) +#include <windows.h> +#endif + +#include "ContentControlDialog.h" +#include "checksum_md5.h" +#include "EngineInterface.h" + +#include <vgui/IInput.h> +#include <vgui/ISystem.h> +#include <vgui/ISurface.h> +#include "tier1/KeyValues.h" +#include "tier1/convar.h" + +#include <vgui_controls/Button.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/RadioButton.h> +#include <vgui_controls/TextEntry.h> +#include <tier0/vcrmode.h> + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Basic help dialog +//----------------------------------------------------------------------------- +CContentControlDialog::CContentControlDialog(vgui::Panel *parent) : vgui::Frame(parent, "ContentControlDialog") +{ + SetBounds(0, 0, 372, 160); + SetSizeable( false ); + + SetTitle( "#GameUI_ContentLock", true ); + + m_pStatus = new vgui::Label( this, "ContentStatus", "" ); + + m_pPasswordLabel = new vgui::Label( this, "PasswordPrompt", "#GameUI_PasswordPrompt" ); + m_pPassword2Label = new vgui::Label( this, "PasswordReentryPrompt", "#GameUI_PasswordReentryPrompt" ); + + m_pExplain = new vgui::Label( this, "ContentControlExplain", "" ); + + m_pPassword = new vgui::TextEntry( this, "Password" ); + m_pPassword2 = new vgui::TextEntry( this, "Password2" ); + + m_pOK = new vgui::Button( this, "Ok", "#GameUI_OK" ); + m_pOK->SetCommand( "Ok" ); + + vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Cancel" ); + + m_szGorePW[ 0 ] = 0; + ResetPassword(); + + LoadControlSettings("Resource\\ContentControlDialog.res"); + +// Explain(""); +// UpdateContentControlStatus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CContentControlDialog::~CContentControlDialog() +{ +} + +void CContentControlDialog::Activate() +{ + BaseClass::Activate(); + + m_pPassword->SetText(""); + m_pPassword->RequestFocus(); + m_pPassword2->SetText(""); + Explain(""); + UpdateContentControlStatus(); + + input()->SetAppModalSurface(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::ResetPassword() +{ + // Set initial value +#ifdef WIN32 +#ifndef _XBOX + HKEY key; + if ( ERROR_SUCCESS == VCRHook_RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Valve\\Half-Life\\Settings", 0, KEY_READ, &key)) + { + DWORD type; + DWORD bufSize = sizeof(m_szGorePW); + + VCRHook_RegQueryValueEx(key, "User Token 2", NULL, &type, (unsigned char *)m_szGorePW, &bufSize ); + VCRHook_RegCloseKey( key ); + } + else +#endif + { + m_szGorePW[ 0 ] = 0; + } +#else + vgui::system()->SetRegistryString( "Software\\Valve\\Half-Life\\Settings\\User Token 2", m_szGorePW ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::ApplyPassword() +{ + WriteToken( m_szGorePW ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::Explain( char const *fmt, ... ) +{ + if ( !m_pExplain ) + return; + + va_list argptr; + char text[1024]; + + va_start (argptr,fmt); + Q_vsnprintf (text, sizeof(text), fmt, argptr); + va_end (argptr); + + m_pExplain->SetText( text ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *command - +//----------------------------------------------------------------------------- +void CContentControlDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "Ok" ) ) + { + bool canclose = false; + + char pw1[ 256 ]; + char pw2[ 256 ]; + + m_pPassword->GetText( pw1, 256 ); + m_pPassword2->GetText( pw2, 256 ); + + // Get text and check +// bool enabled = PasswordEnabled(); //( m_szGorePW[0]!=0 ) ? true : false; +// bool pwMatch = stricmp( pw1, pw2 ) == 0 ? true : false; + + if (IsPasswordEnabledInDialog()) + { + canclose = DisablePassword(pw1); +// canclose = CheckPassword( m_szGorePW, pw1, false ); + } + else if (!strcmp(pw1, pw2)) + { + canclose = EnablePassword(pw1); +// canclose = CheckPassword( NULL, pw1, true ); + } + else + { + Explain( "#GameUI_PasswordsDontMatch" ); + } + + if ( canclose ) + { + OnClose(); + } + } + else if ( !stricmp( command, "Cancel" ) ) + { + OnClose(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::OnClose() +{ + BaseClass::OnClose(); + PostActionSignal(new KeyValues("ContentControlClose")); +// MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::WriteToken( const char *str ) +{ + // Set initial value +#ifdef WIN32 +#ifndef _XBOX + HKEY key; + if ( ERROR_SUCCESS == VCRHook_RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Valve\\Half-Life\\Settings", 0, KEY_WRITE, &key)) + { + DWORD type = REG_SZ; + DWORD bufSize = strlen( str ) + 1; + + VCRHook_RegSetValueEx(key, "User Token 2", 0, type, (const unsigned char *)str, bufSize ); + + VCRHook_RegCloseKey( key ); + } +#endif +#else + vgui::system()->SetRegistryString( "Software\\Valve\\Half-Life\\Settings\\User Token 2", m_szGorePW ); +#endif + Q_strncpy( m_szGorePW, str, sizeof( m_szGorePW ) ); + + UpdateContentControlStatus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::HashPassword(const char *newPW, char *hashBuffer, int maxlen ) +{ + // Compute the md5 hash and save it. + unsigned char md5_hash[16]; + MD5Context_t ctx; + + MD5Init( &ctx ); + MD5Update( &ctx, (unsigned char const *)newPW, strlen( newPW ) ); + MD5Final( md5_hash, &ctx ); + + char hex[ 128 ]; + Q_binarytohex( md5_hash, sizeof( md5_hash ), hex, sizeof( hex ) ); + +// char digestedPW[ 128 ]; + Q_strncpy( hashBuffer, hex, maxlen ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +/* +bool CContentControlDialog::CheckPassword( char const *oldPW, char const *newPW, bool enableContentControl ) +{ + char digestedPW[ 128 ]; + HashPassword(newPW, digestedPW, sizeof( digestedPW ) ); + + // Compute the md5 hash and save it. + unsigned char md5_hash[16]; + MD5Context_t ctx; + + MD5Init( &ctx ); + MD5Update( &ctx, (unsigned char const *)(LPCSTR)newPW, strlen( newPW ) ); + MD5Final( md5_hash, &ctx ); + + char hex[ 128 ]; + Q_binarytohex( md5_hash, sizeof( md5_hash ), hex, sizeof( hex ) ); + + Q_strncpy( digestedPW, hex, sizeof( digestedPW ) ); +*/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CContentControlDialog::EnablePassword(const char *newPW) +{ + if ( !newPW[ 0 ] ) + { + Explain( "#GameUI_MustEnterPassword" ); + return false; + } + + char digestedPW[ 128 ]; + HashPassword(newPW, digestedPW, sizeof( digestedPW ) ); + + // disable violence +/* engine->Cvar_SetValue("violence_hblood", 0.0 ); + engine->Cvar_SetValue("violence_hgibs" , 0.0 ); + engine->Cvar_SetValue("violence_ablood", 0.0 ); + engine->Cvar_SetValue("violence_agibs" , 0.0 ); + */ + + ConVarRef violence_hblood( "violence_hblood" ); + violence_hblood.SetValue(false); + + ConVarRef violence_hgibs( "violence_hgibs" ); + violence_hgibs.SetValue(false); + + ConVarRef violence_ablood( "violence_ablood" ); + violence_ablood.SetValue(false); + + ConVarRef violence_agibs( "violence_agibs" ); + violence_agibs.SetValue(false); + + // Store digest to registry +// WriteToken( digestedPW ); + Q_strncpy(m_szGorePW, digestedPW, sizeof( m_szGorePW ) ); + /* + } + else + { + if ( stricmp( oldPW, digestedPW ) ) + { + // Warn that password is invalid + Explain( "#GameUI_IncorrectPassword" ); + return false; + } + } + }*/ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CContentControlDialog::DisablePassword(const char *oldPW) +{ + if ( !oldPW[ 0 ] ) + { + Explain( "#GameUI_MustEnterPassword" ); + return false; + } + + char digestedPW[ 128 ]; + HashPassword(oldPW, digestedPW, sizeof( digestedPW ) ); + + if( stricmp( m_szGorePW, digestedPW ) ) + { + Explain( "#GameUI_IncorrectPassword" ); + return false; + } + + m_szGorePW[0] = 0; + + // set the violence cvars +/* engine->Cvar_SetValue("violence_hblood", 1.0 ); + engine->Cvar_SetValue("violence_hgibs" , 1.0 ); + engine->Cvar_SetValue("violence_ablood", 1.0 ); + engine->Cvar_SetValue("violence_agibs" , 1.0 ); + */ + ConVarRef violence_hblood( "violence_hblood" ); + violence_hblood.SetValue(true); + + ConVarRef violence_hgibs( "violence_hgibs" ); + violence_hgibs.SetValue(true); + + ConVarRef violence_ablood( "violence_ablood" ); + violence_ablood.SetValue(true); + + ConVarRef violence_agibs( "violence_agibs" ); + violence_agibs.SetValue(true); + + +// // Remove digest value +// WriteToken( "" ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CContentControlDialog::IsPasswordEnabledInDialog() +{ + return m_szGorePW[0] != 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CContentControlDialog::UpdateContentControlStatus( void ) +{ + bool enabled = IsPasswordEnabledInDialog(); //( m_szGorePW[0]!=0 ) ? true : false; + m_pStatus->SetText( enabled ? "#GameUI_ContentStatusEnabled" : "#GameUI_ContentStatusDisabled" ); + + if (enabled) + { + m_pPasswordLabel->SetText("#GameUI_PasswordDisablePrompt"); + } + else + { + m_pPasswordLabel->SetText("#GameUI_PasswordPrompt"); + } + + // hide the re-entry + m_pPassword2Label->SetVisible(!enabled); + m_pPassword2->SetVisible(!enabled); +// m_pOK->SetText( enabled ? "#GameUI_Disable" : "#GameUI_Enable" ); +} diff --git a/gameui/ContentControlDialog.h b/gameui/ContentControlDialog.h new file mode 100644 index 0000000..f4aeb4d --- /dev/null +++ b/gameui/ContentControlDialog.h @@ -0,0 +1,66 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CONTENTCONTROLDIALOG_H +#define CONTENTCONTROLDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CContentControlDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CContentControlDialog, vgui::Frame ); + +public: + CContentControlDialog(vgui::Panel *parent); + ~CContentControlDialog(); + + virtual void OnCommand( const char *command ); + virtual void OnClose(); + virtual void Activate(); + + void ResetPassword(); + void ApplyPassword(); + bool IsPasswordEnabledInDialog(); + bool IsPasswordEnabled() { return ( m_szGorePW[0] != 0 ); } + +protected: + void WriteToken( const char *str ); + bool CheckPassword( char const *oldPW, char const *newPW, bool enableContentControl ); + void UpdateContentControlStatus( void ); + + void Explain( PRINTF_FORMAT_STRING char const *fmt, ... ); + + void HashPassword(const char *newPW, char *hashBuffer, int maxlen ); + bool EnablePassword(const char *newPW); + bool DisablePassword(const char *oldPW); + + enum + { + MAX_GORE_PW = 64, + }; + + char m_szGorePW[ MAX_GORE_PW ]; + + bool m_bDefaultPassword; + vgui::Label *m_pStatus; + vgui::Button *m_pOK; + vgui::TextEntry *m_pPassword; + vgui::Label *m_pPasswordLabel; + vgui::Label *m_pPassword2Label; + vgui::TextEntry *m_pPassword2; + + vgui::Label *m_pExplain; +}; + + +#endif // CONTENTCONTROLDIALOG_H diff --git a/gameui/ControllerDialog.cpp b/gameui/ControllerDialog.cpp new file mode 100644 index 0000000..69dcf37 --- /dev/null +++ b/gameui/ControllerDialog.cpp @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BasePanel.h" +#include "ControllerDialog.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +CControllerDialog::CControllerDialog( vgui::Panel *parent ) : BaseClass( parent, true ) // TRUE second param says we want the controller options +{ +} + +void CControllerDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetControlString( "TitleLabel", "#GameUI_Controller" ); +} diff --git a/gameui/ControllerDialog.h b/gameui/ControllerDialog.h new file mode 100644 index 0000000..eff42fd --- /dev/null +++ b/gameui/ControllerDialog.h @@ -0,0 +1,27 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CONTROLLERDIALOG_H +#define CONTROLLERDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "OptionsDialog.h" + +class CControllerDialog : public COptionsDialogXbox +{ + DECLARE_CLASS_SIMPLE( CControllerDialog, COptionsDialogXbox ); + +public: + CControllerDialog(vgui::Panel *parent); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +}; + +#endif // CONTROLLERDIALOG_H + diff --git a/gameui/CreateMultiplayerGameBotPage.cpp b/gameui/CreateMultiplayerGameBotPage.cpp new file mode 100644 index 0000000..0f27128 --- /dev/null +++ b/gameui/CreateMultiplayerGameBotPage.cpp @@ -0,0 +1,228 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "CreateMultiplayerGameBotPage.h" + +using namespace vgui; + +#include <KeyValues.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/TextEntry.h> + +#include "filesystem.h" +#include "PanelListPanel.h" +#include "scriptobject.h" +#include <tier0/vcrmode.h> +#include "tier1/convar.h" +#include "EngineInterface.h" +#include "CvarToggleCheckButton.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +// for join team combo box +enum BotGUITeamType +{ + BOT_GUI_TEAM_RANDOM = 0, + BOT_GUI_TEAM_CT = 1, + BOT_GUI_TEAM_T = 2 +}; + +// these must correlate with above enum +static const char *joinTeamArg[] = { "any", "ct", "t", NULL }; + + +// for bot chatter combo box +enum BotGUIChatterType +{ + BOT_GUI_CHATTER_NORMAL = 0, + BOT_GUI_CHATTER_MINIMAL = 1, + BOT_GUI_CHATTER_RADIO = 2, + BOT_GUI_CHATTER_OFF = 3 +}; + +// these must correlate with above enum +static const char *chatterArg[] = { "normal", "minimal", "radio", "off", NULL }; + + +extern void UTIL_StripInvalidCharacters( char *pszInput ); + + +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameBotPage::SetJoinTeamCombo( const char *team ) +{ + if (team) + { + for( int i=0; joinTeamArg[i]; ++i ) + if (!stricmp( team, joinTeamArg[i] )) + { + m_joinTeamCombo->ActivateItemByRow( i ); + return; + } + } + else + { + m_joinTeamCombo->ActivateItemByRow( BOT_GUI_TEAM_RANDOM ); + } +} + +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameBotPage::SetChatterCombo( const char *chatter ) +{ + if (chatter) + { + for( int i=0; chatterArg[i]; ++i ) + if (!stricmp( chatter, chatterArg[i] )) + { + m_chatterCombo->ActivateItemByRow( i ); + return; + } + } + else + { + m_joinTeamCombo->ActivateItemByRow( BOT_GUI_CHATTER_NORMAL ); + } +} + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameBotPage::CCreateMultiplayerGameBotPage( vgui::Panel *parent, const char *name, KeyValues *botKeys ) : PropertyPage( parent, name ) +{ + m_pSavedData = botKeys; + + m_allowRogues = new CCvarToggleCheckButton( this, "BotAllowRogueCheck", "", "bot_allow_rogues" ); + m_allowPistols = new CCvarToggleCheckButton( this, "BotAllowPistolsCheck", "", "bot_allow_pistols" ); + m_allowShotguns = new CCvarToggleCheckButton( this, "BotAllowShotgunsCheck", "", "bot_allow_shotguns" ); + m_allowSubmachineGuns = new CCvarToggleCheckButton( this, "BotAllowSubmachineGunsCheck", "", "bot_allow_sub_machine_guns" ); + m_allowRifles = new CCvarToggleCheckButton( this, "BotAllowRiflesCheck", "", "bot_allow_rifles" ); + m_allowMachineGuns = new CCvarToggleCheckButton( this, "BotAllowMachineGunsCheck", "", "bot_allow_machine_guns" ); + m_allowGrenades = new CCvarToggleCheckButton( this, "BotAllowGrenadesCheck", "", "bot_allow_grenades" ); + m_allowSnipers = new CCvarToggleCheckButton( this, "BotAllowSnipersCheck", "", "bot_allow_snipers" ); +#ifdef CS_SHIELD_ENABLED + m_allowShields = new CCvarToggleCheckButton( this, "BotAllowShieldCheck", "", "bot_allow_shield" ); +#endif // CS_SHIELD_ENABLED + + m_joinAfterPlayer = new CCvarToggleCheckButton( this, "BotJoinAfterPlayerCheck", "", "bot_join_after_player" ); + + m_deferToHuman = new CCvarToggleCheckButton( this, "BotDeferToHumanCheck", "", "bot_defer_to_human" ); + + // set up team join combo box + // NOTE: If order of AddItem is changed, update the associated enum + m_joinTeamCombo = new ComboBox( this, "BotJoinTeamCombo", 3, false ); + m_joinTeamCombo->AddItem( "#Cstrike_Random", NULL ); + m_joinTeamCombo->AddItem( "#Cstrike_Team_CT", NULL ); + m_joinTeamCombo->AddItem( "#Cstrike_Team_T", NULL ); + + // set up chatter combo box + // NOTE: If order of AddItem is changed, update the associated enum + m_chatterCombo = new ComboBox( this, "BotChatterCombo", 4, false ); + m_chatterCombo->AddItem( "#Cstrike_Bot_Chatter_Normal", NULL ); + m_chatterCombo->AddItem( "#Cstrike_Bot_Chatter_Minimal", NULL ); + m_chatterCombo->AddItem( "#Cstrike_Bot_Chatter_Radio", NULL ); + m_chatterCombo->AddItem( "#Cstrike_Bot_Chatter_Off", NULL ); + + // create text entry fields for quota and prefix + m_prefixEntry = new TextEntry( this, "BotPrefixEntry" ); + + // set positions and sizes from resources file + LoadControlSettings( "Resource/CreateMultiplayerGameBotPage.res" ); + + // get initial values from bot keys + m_joinAfterPlayer->SetSelected( botKeys->GetInt( "bot_join_after_player", 1 ) ); + m_allowRogues->SetSelected( botKeys->GetInt( "bot_allow_rogues", 1 ) ); + m_allowPistols->SetSelected( botKeys->GetInt( "bot_allow_pistols", 1 ) ); + m_allowShotguns->SetSelected( botKeys->GetInt( "bot_allow_shotguns", 1 ) ); + m_allowSubmachineGuns->SetSelected( botKeys->GetInt( "bot_allow_sub_machine_guns", 1 ) ); + m_allowMachineGuns->SetSelected( botKeys->GetInt( "bot_allow_machine_guns", 1 ) ); + m_allowRifles->SetSelected( botKeys->GetInt( "bot_allow_rifles", 1 ) ); + m_allowSnipers->SetSelected( botKeys->GetInt( "bot_allow_snipers", 1 ) ); + m_allowGrenades->SetSelected( botKeys->GetInt( "bot_allow_grenades", 1 ) ); +#ifdef CS_SHIELD_ENABLED + m_allowShields->SetSelected( botKeys->GetInt( "bot_allow_shield", 1 ) ); +#endif // CS_SHIELD_ENABLED + m_deferToHuman->SetSelected( botKeys->GetInt( "bot_defer_to_human", 1 ) ); + + SetJoinTeamCombo( botKeys->GetString( "bot_join_team", "any" ) ); + SetChatterCombo( botKeys->GetString( "bot_chatter", "normal" ) ); + + // set bot_prefix + const char *prefix = botKeys->GetString( "bot_prefix" ); + if (prefix) + SetControlString( "BotPrefixEntry", prefix ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameBotPage::~CCreateMultiplayerGameBotPage() +{ + // vgui handles deletion of children automatically through the hierarchy +} + +//----------------------------------------------------------------------------- +// Reset values +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameBotPage::OnResetChanges() +{ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void UpdateValue( KeyValues *data, const char *cvarName, int value ) +{ + data->SetInt( cvarName, value ); + + ConVarRef var( cvarName ); + var.SetValue( value ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void UpdateValue( KeyValues *data, const char *cvarName, const char *value ) +{ + data->SetString( cvarName, value ); + + ConVarRef var( cvarName ); + var.SetValue( value ); +} + +//----------------------------------------------------------------------------- +// Called to get data from the page +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameBotPage::OnApplyChanges() +{ + UpdateValue( m_pSavedData, "bot_join_after_player", m_joinAfterPlayer->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_rogues", m_allowRogues->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_pistols", m_allowPistols->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_shotguns", m_allowShotguns->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_sub_machine_guns", m_allowSubmachineGuns->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_machine_guns", m_allowMachineGuns->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_rifles", m_allowRifles->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_snipers", m_allowSnipers->IsSelected() ); + UpdateValue( m_pSavedData, "bot_allow_grenades", m_allowGrenades->IsSelected() ); +#ifdef CS_SHIELD_ENABLED + UpdateValue( m_pSavedData, "bot_allow_shield", m_allowShields->IsSelected() ); +#endif // CS_SHIELD_ENABLED + UpdateValue( m_pSavedData, "bot_defer_to_human", m_deferToHuman->IsSelected() ); + + // set bot_join_team + UpdateValue( m_pSavedData, "bot_join_team", joinTeamArg[ m_joinTeamCombo->GetActiveItem() ] ); + + // set bot_chatter + UpdateValue( m_pSavedData, "bot_chatter", chatterArg[ m_chatterCombo->GetActiveItem() ] ); + + // set bot_prefix + #define BUF_LENGTH 256 + char entryBuffer[ BUF_LENGTH ]; + m_prefixEntry->GetText( entryBuffer, BUF_LENGTH ); + UpdateValue( m_pSavedData, "bot_prefix", entryBuffer ); +} + diff --git a/gameui/CreateMultiplayerGameBotPage.h b/gameui/CreateMultiplayerGameBotPage.h new file mode 100644 index 0000000..d9f698e --- /dev/null +++ b/gameui/CreateMultiplayerGameBotPage.h @@ -0,0 +1,66 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CREATEMULTIPLAYERGAMEBOTPAGE_H +#define CREATEMULTIPLAYERGAMEBOTPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> + +class CPanelListPanel; +class CDescription; +class mpcontrol_t; +class CCvarToggleCheckButton; + +//----------------------------------------------------------------------------- +// Purpose: advanced bot properties page of the create game server dialog +//----------------------------------------------------------------------------- +class CCreateMultiplayerGameBotPage : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( CCreateMultiplayerGameBotPage, vgui::PropertyPage ); + +public: + CCreateMultiplayerGameBotPage( vgui::Panel *parent, const char *name, KeyValues *botKeys ); + ~CCreateMultiplayerGameBotPage(); + +protected: + virtual void OnResetChanges(); + virtual void OnApplyChanges(); + +private: + CCvarToggleCheckButton *m_joinAfterPlayer; + + CCvarToggleCheckButton *m_allowRogues; + + CCvarToggleCheckButton *m_allowPistols; + CCvarToggleCheckButton *m_allowShotguns; + CCvarToggleCheckButton *m_allowSubmachineGuns; + CCvarToggleCheckButton *m_allowMachineGuns; + CCvarToggleCheckButton *m_allowRifles; + CCvarToggleCheckButton *m_allowGrenades; +#ifdef CS_SHIELD_ENABLED + CCvarToggleCheckButton *m_allowShields; +#endif // CS_SHIELD_ENABLED + CCvarToggleCheckButton *m_allowSnipers; + + CCvarToggleCheckButton *m_deferToHuman; + + vgui::ComboBox *m_joinTeamCombo; + void SetJoinTeamCombo( const char *team ); + + vgui::ComboBox *m_chatterCombo; + void SetChatterCombo( const char *team ); + + vgui::TextEntry *m_prefixEntry; + + KeyValues *m_pSavedData; +}; + + +#endif // CREATEMULTIPLAYERGAMEBOTPAGE_H diff --git a/gameui/CreateMultiplayerGameDialog.cpp b/gameui/CreateMultiplayerGameDialog.cpp new file mode 100644 index 0000000..a372255 --- /dev/null +++ b/gameui/CreateMultiplayerGameDialog.cpp @@ -0,0 +1,190 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "CreateMultiplayerGameDialog.h" +#include "CreateMultiplayerGameServerPage.h" +#include "CreateMultiplayerGameGameplayPage.h" +#include "CreateMultiplayerGameBotPage.h" + +#include "EngineInterface.h" +#include "ModInfo.h" +#include "GameUI_Interface.h" + +#include <stdio.h> + +using namespace vgui; + +#include "vgui_controls/ComboBox.h" +#include <vgui/ILocalize.h> + +#include "filesystem.h" +#include <KeyValues.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameDialog::CCreateMultiplayerGameDialog(vgui::Panel *parent) : PropertyDialog(parent, "CreateMultiplayerGameDialog") +{ + m_bBotsEnabled = false; + SetDeleteSelfOnClose(true); + SetSize(348, 460); + + SetTitle("#GameUI_CreateServer", true); + SetOKButtonText("#GameUI_Start"); + + if ( ModInfo().UseBots() ) + { + m_bBotsEnabled = true; + } + + m_pServerPage = new CCreateMultiplayerGameServerPage(this, "ServerPage"); + m_pGameplayPage = new CCreateMultiplayerGameGameplayPage(this, "GameplayPage"); + m_pBotPage = NULL; + + AddPage(m_pServerPage, "#GameUI_Server"); + AddPage(m_pGameplayPage, "#GameUI_Game"); + + // create KeyValues object to load/save config options + m_pSavedData = new KeyValues( "ServerConfig" ); + + // load the config data + if (m_pSavedData) + { + m_pSavedData->LoadFromFile( g_pFullFileSystem, "ServerConfig.vdf", "GAME" ); // this is game-specific data, so it should live in GAME, not CONFIG + + const char *startMap = m_pSavedData->GetString("map", ""); + if (startMap[0]) + { + m_pServerPage->SetMap(startMap); + } + } + + if ( m_bBotsEnabled ) + { + // add a page of advanced bot controls + // NOTE: These controls will use the bot keys to initialize their values + m_pBotPage = new CCreateMultiplayerGameBotPage( this, "BotPage", m_pSavedData ); + AddPage( m_pBotPage, "#GameUI_CPUPlayerOptions" ); + m_pServerPage->EnableBots( m_pSavedData ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameDialog::~CCreateMultiplayerGameDialog() +{ + if (m_pSavedData) + { + m_pSavedData->deleteThis(); + m_pSavedData = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs the server when the OK button is pressed +//----------------------------------------------------------------------------- +bool CCreateMultiplayerGameDialog::OnOK(bool applyOnly) +{ + // reset server enforced cvars + g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED ); + + // Cheats were disabled; revert all cheat cvars to their default values. + // This must be done heading into multiplayer games because people can play + // demos etc and set cheat cvars with sv_cheats 0. + g_pCVar->RevertFlaggedConVars( FCVAR_CHEAT ); + + DevMsg( "FCVAR_CHEAT cvars reverted to defaults.\n" ); + + BaseClass::OnOK(applyOnly); + + // get these values from m_pServerPage and store them temporarily + char szMapName[64], szHostName[64], szPassword[64]; + strncpy(szMapName, m_pServerPage->GetMapName(), sizeof( szMapName )); + strncpy(szHostName, m_pGameplayPage->GetHostName(), sizeof( szHostName )); + strncpy(szPassword, m_pGameplayPage->GetPassword(), sizeof( szPassword )); + + // save the config data + if (m_pSavedData) + { + if (m_pServerPage->IsRandomMapSelected()) + { + // it's set to random map, just save an + m_pSavedData->SetString("map", ""); + } + else + { + m_pSavedData->SetString("map", szMapName); + } + + // save config to a file + m_pSavedData->SaveToFile( g_pFullFileSystem, "ServerConfig.vdf", "GAME" ); + } + + char szMapCommand[1024]; + + // create the command to execute + Q_snprintf(szMapCommand, sizeof( szMapCommand ), "disconnect\nwait\nwait\nsv_lan 1\nsetmaster enable\nmaxplayers %i\nsv_password \"%s\"\nhostname \"%s\"\nprogress_enable\nmap %s\n", + m_pGameplayPage->GetMaxPlayers(), + szPassword, + szHostName, + szMapName + ); + + // exec + engine->ClientCmd_Unrestricted(szMapCommand); + + return true; +} + +void CCreateMultiplayerGameDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + // Handle close here, CBasePanel parent doesn't support "DialogClosing" command + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B ) + { + OnCommand( "Close" ); + } + else if ( nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A ) + { + OnOK( false ); + } + else if ( nButtonCode == KEY_XBUTTON_UP || + nButtonCode == KEY_XSTICK1_UP || + nButtonCode == KEY_XSTICK2_UP || + nButtonCode == STEAMCONTROLLER_DPAD_UP || + nButtonCode == KEY_UP ) + { + int nItem = m_pServerPage->GetMapList()->GetActiveItem() - 1; + if ( nItem < 0 ) + { + nItem = m_pServerPage->GetMapList()->GetItemCount() - 1; + } + m_pServerPage->GetMapList()->ActivateItem( nItem ); + } + else if ( nButtonCode == KEY_XBUTTON_DOWN || + nButtonCode == KEY_XSTICK1_DOWN || + nButtonCode == KEY_XSTICK2_DOWN || + nButtonCode == STEAMCONTROLLER_DPAD_DOWN || + nButtonCode == KEY_DOWN ) + { + int nItem = m_pServerPage->GetMapList()->GetActiveItem() + 1; + if ( nItem >= m_pServerPage->GetMapList()->GetItemCount() ) + { + nItem = 0; + } + m_pServerPage->GetMapList()->ActivateItem( nItem ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +}
\ No newline at end of file diff --git a/gameui/CreateMultiplayerGameDialog.h b/gameui/CreateMultiplayerGameDialog.h new file mode 100644 index 0000000..4507f5f --- /dev/null +++ b/gameui/CreateMultiplayerGameDialog.h @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CREATEMULTIPLAYERGAMEDIALOG_H +#define CREATEMULTIPLAYERGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyDialog.h> + +class CCreateMultiplayerGameServerPage; +class CCreateMultiplayerGameGameplayPage; +class CCreateMultiplayerGameBotPage; + +//----------------------------------------------------------------------------- +// Purpose: dialog for launching a listenserver +//----------------------------------------------------------------------------- +class CCreateMultiplayerGameDialog : public vgui::PropertyDialog +{ + DECLARE_CLASS_SIMPLE( CCreateMultiplayerGameDialog, vgui::PropertyDialog ); + +public: + CCreateMultiplayerGameDialog(vgui::Panel *parent); + ~CCreateMultiplayerGameDialog(); + +protected: + virtual bool OnOK(bool applyOnly); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + +private: + CCreateMultiplayerGameServerPage *m_pServerPage; + CCreateMultiplayerGameGameplayPage *m_pGameplayPage; + CCreateMultiplayerGameBotPage *m_pBotPage; + + bool m_bBotsEnabled; + + // for loading/saving game config + KeyValues *m_pSavedData; +}; + + +#endif // CREATEMULTIPLAYERGAMEDIALOG_H diff --git a/gameui/CreateMultiplayerGameGameplayPage.cpp b/gameui/CreateMultiplayerGameGameplayPage.cpp new file mode 100644 index 0000000..4c8e06a --- /dev/null +++ b/gameui/CreateMultiplayerGameGameplayPage.cpp @@ -0,0 +1,475 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <stdio.h> +#include <time.h> + +#include "CreateMultiplayerGameGameplayPage.h" + +using namespace vgui; + +#include <KeyValues.h> +#include <vgui/ILocalize.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/TextEntry.h> + +#include "filesystem.h" +#include "PanelListPanel.h" +#include "scriptobject.h" +#include <tier0/vcrmode.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#define OPTIONS_DIR "cfg" +#define DEFAULT_OPTIONS_FILE OPTIONS_DIR "/settings_default.scr" +#define OPTIONS_FILE OPTIONS_DIR "/settings.scr" + +//----------------------------------------------------------------------------- +// Purpose: class for loading/saving server config file +//----------------------------------------------------------------------------- +class CServerDescription : public CDescription +{ +public: + CServerDescription( void ); + + void WriteScriptHeader( FileHandle_t fp ); + void WriteFileHeader( FileHandle_t fp ); +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameGameplayPage::CCreateMultiplayerGameGameplayPage(vgui::Panel *parent, const char *name) : PropertyPage(parent, name) +{ + SetSize( 10, 10 ); // Quiet "parent not sized yet" spew + m_pOptionsList = new CPanelListPanel(this, "GameOptions"); + + m_pDescription = new CServerDescription(); + m_pDescription->InitFromFile( DEFAULT_OPTIONS_FILE ); + m_pDescription->InitFromFile( OPTIONS_FILE ); + m_pList = NULL; + + LoadControlSettings("Resource/CreateMultiplayerGameGameplayPage.res"); + + LoadGameOptionsList(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameGameplayPage::~CCreateMultiplayerGameGameplayPage() +{ + delete m_pDescription; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CCreateMultiplayerGameGameplayPage::GetMaxPlayers() +{ + return atoi(GetValue("maxplayers", "32")); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameGameplayPage::GetPassword() +{ + return GetValue("sv_password", ""); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameGameplayPage::GetHostName() +{ + return GetValue("hostname", "Half-Life"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameGameplayPage::GetValue(const char *cvarName, const char *defaultValue) +{ + for (mpcontrol_t *mp = m_pList; mp != NULL; mp = mp->next) + { + Panel *control = mp->pControl; + if (control && !stricmp(mp->GetName(), cvarName)) + { + KeyValues *data = new KeyValues("GetText"); + static char buf[128]; + if (control && control->RequestInfo(data)) + { + strncpy(buf, data->GetString("text", defaultValue), sizeof(buf) - 1); + } + else + { + // no value found, copy in default text + strncpy(buf, defaultValue, sizeof(buf) - 1); + } + + // ensure null termination of string + buf[sizeof(buf) - 1] = 0; + + // free + data->deleteThis(); + return buf; + } + + } + + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: called to get data from the page +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameGameplayPage::OnApplyChanges() +{ + // Get the values from the controls + GatherCurrentValues(); + + // Create the game.cfg file + if ( m_pDescription ) + { + FileHandle_t fp; + + // Add settings to config.cfg + m_pDescription->WriteToConfig(); + + // save out in the settings file + g_pFullFileSystem->CreateDirHierarchy( OPTIONS_DIR, "GAME" ); + fp = g_pFullFileSystem->Open( OPTIONS_FILE, "wb", "GAME" ); + if ( fp ) + { + m_pDescription->WriteToScriptFile( fp ); + g_pFullFileSystem->Close( fp ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Creates all the controls in the game options list +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameGameplayPage::LoadGameOptionsList() +{ + // destroy any existing controls + mpcontrol_t *p, *n; + + p = m_pList; + while ( p ) + { + n = p->next; + // + delete p->pControl; + delete p->pPrompt; + delete p; + p = n; + } + + m_pList = NULL; + + + // Go through desciption creating controls + CScriptObject *pObj; + + pObj = m_pDescription->pObjList; + + mpcontrol_t *pCtrl; + + CheckButton *pBox; + TextEntry *pEdit; + ComboBox *pCombo; + CScriptListItem *pListItem; + + Panel *objParent = m_pOptionsList; + + while ( pObj ) + { + if ( pObj->type == O_OBSOLETE ) + { + pObj = pObj->pNext; + continue; + } + + pCtrl = new mpcontrol_t( objParent, pObj->cvarname ); + pCtrl->type = pObj->type; + + switch ( pCtrl->type ) + { + case O_BOOL: + pBox = new CheckButton( pCtrl, "DescCheckButton", pObj->prompt ); + pBox->SetSelected( pObj->fdefValue != 0.0f ? true : false ); + + pCtrl->pControl = (Panel *)pBox; + break; + case O_STRING: + case O_NUMBER: + pEdit = new TextEntry( pCtrl, "DescEdit"); + pEdit->InsertString(pObj->defValue); + pCtrl->pControl = (Panel *)pEdit; + break; + case O_LIST: + pCombo = new ComboBox( pCtrl, "DescEdit", 5, false ); + + pListItem = pObj->pListItems; + while ( pListItem ) + { + pCombo->AddItem(pListItem->szItemText, NULL); + pListItem = pListItem->pNext; + } + + pCombo->ActivateItemByRow((int)pObj->fdefValue); + + pCtrl->pControl = (Panel *)pCombo; + break; + default: + break; + } + + if ( pCtrl->type != O_BOOL ) + { + pCtrl->pPrompt = new vgui::Label( pCtrl, "DescLabel", "" ); + pCtrl->pPrompt->SetContentAlignment( vgui::Label::a_west ); + pCtrl->pPrompt->SetTextInset( 5, 0 ); + pCtrl->pPrompt->SetText( pObj->prompt ); + } + + pCtrl->pScrObj = pObj; + pCtrl->SetSize( 100, 28 ); + //pCtrl->SetBorder( scheme()->GetBorder(1, "DepressedButtonBorder") ); + m_pOptionsList->AddItem( pCtrl ); + + // Link it in + if ( !m_pList ) + { + m_pList = pCtrl; + pCtrl->next = NULL; + } + else + { + mpcontrol_t *p; + p = m_pList; + while ( p ) + { + if ( !p->next ) + { + p->next = pCtrl; + pCtrl->next = NULL; + break; + } + p = p->next; + } + } + + pObj = pObj->pNext; + } +} + +//----------------------------------------------------------------------------- +// Purpose: applies all the values in the page +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameGameplayPage::GatherCurrentValues() +{ + if ( !m_pDescription ) + return; + + // OK + CheckButton *pBox; + TextEntry *pEdit; + ComboBox *pCombo; + + mpcontrol_t *pList; + + CScriptObject *pObj; + CScriptListItem *pItem; + + char szValue[256]; + char strValue[256]; + wchar_t w_szStrValue[256]; + + pList = m_pList; + while ( pList ) + { + pObj = pList->pScrObj; + + if ( !pList->pControl ) + { + pObj->SetCurValue( pObj->defValue ); + pList = pList->next; + continue; + } + + switch ( pObj->type ) + { + case O_BOOL: + pBox = (CheckButton *)pList->pControl; + Q_snprintf( szValue, sizeof( szValue ), "%s", pBox->IsSelected() ? "1" : "0" ); + break; + case O_NUMBER: + pEdit = ( TextEntry * )pList->pControl; + pEdit->GetText( strValue, sizeof( strValue ) ); + Q_snprintf( szValue, sizeof( szValue ), "%s", strValue ); + break; + case O_STRING: + pEdit = ( TextEntry * )pList->pControl; + pEdit->GetText( strValue, sizeof( strValue ) ); + Q_snprintf( szValue, sizeof( szValue ), "%s", strValue ); + break; + case O_LIST: + pCombo = ( ComboBox *)pList->pControl; + pCombo->GetText( w_szStrValue, sizeof( w_szStrValue ) / sizeof( wchar_t ) ); + + pItem = pObj->pListItems; + + while ( pItem ) + { + wchar_t *wLocalizedString = NULL; + wchar_t w_szStrTemp[256]; + + // Localized string? + if ( pItem->szItemText[0] == '#' ) + { + wLocalizedString = g_pVGuiLocalize->Find( pItem->szItemText ); + } + + if ( wLocalizedString ) + { + // Copy the string we found into our temp array + V_wcscpy_safe( w_szStrTemp, wLocalizedString ); + } + else + { + // Just convert what we have to Unicode + g_pVGuiLocalize->ConvertANSIToUnicode( pItem->szItemText, w_szStrTemp, sizeof( w_szStrTemp ) ); + } + + if ( _wcsicmp( w_szStrTemp, w_szStrValue ) == 0 ) + { + // Found a match! + break; + } + + pItem = pItem->pNext; + } + + if ( pItem ) + { + Q_snprintf( szValue, sizeof( szValue ), "%s", pItem->szValue ); + } + else //Couldn't find index + { + Q_snprintf( szValue, sizeof( szValue ), "%s", pObj->defValue ); + } + break; + } + + // Remove double quotes and % characters + UTIL_StripInvalidCharacters( szValue, sizeof( szValue ) ); + + Q_strncpy( strValue, szValue, sizeof( strValue ) ); + + pObj->SetCurValue( strValue ); + + pList = pList->next; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, load/save server settings object +//----------------------------------------------------------------------------- +CServerDescription::CServerDescription( void ) : CDescription() +{ + setHint( "// NOTE: THIS FILE IS AUTOMATICALLY REGENERATED, \r\n" +"//DO NOT EDIT THIS HEADER, YOUR COMMENTS WILL BE LOST IF YOU DO\r\n" +"// Multiplayer options script\r\n" +"//\r\n" +"// Format:\r\n" +"// Version [float]\r\n" +"// Options description followed by \r\n" +"// Options defaults\r\n" +"//\r\n" +"// Option description syntax:\r\n" +"//\r\n" +"// \"cvar\" { \"Prompt\" { type [ type info ] } { default } }\r\n" +"//\r\n" +"// type = \r\n" +"// BOOL (a yes/no toggle)\r\n" +"// STRING\r\n" +"// NUMBER\r\n" +"// LIST\r\n" +"//\r\n" +"// type info:\r\n" +"// BOOL no type info\r\n" +"// NUMBER min max range, use -1 -1 for no limits\r\n" +"// STRING no type info\r\n" +"// LIST "" delimited list of options value pairs\r\n" +"//\r\n" +"//\r\n" +"// default depends on type\r\n" +"// BOOL is \"0\" or \"1\"\r\n" +"// NUMBER is \"value\"\r\n" +"// STRING is \"value\"\r\n" +"// LIST is \"index\", where index \"0\" is the first element of the list\r\n\r\n\r\n" ); + + setDescription ( "SERVER_OPTIONS" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CServerDescription::WriteScriptHeader( FileHandle_t fp ) +{ + char am_pm[] = "AM"; + tm newtime; + VCRHook_LocalTime( &newtime ); + + if( newtime.tm_hour > 12 ) /* Set up extension. */ + Q_strncpy( am_pm, "PM", sizeof( am_pm ) ); + if( newtime.tm_hour > 12 ) /* Convert from 24-hour */ + newtime.tm_hour -= 12; /* to 12-hour clock. */ + if( newtime.tm_hour == 0 ) /*Set hour to 12 if midnight. */ + newtime.tm_hour = 12; + + g_pFullFileSystem->FPrintf( fp, (char *)getHint() ); + +// Write out the comment and Cvar Info: + g_pFullFileSystem->FPrintf( fp, "// Half-Life Server Configuration Layout Script (stores last settings chosen, too)\r\n" ); + g_pFullFileSystem->FPrintf( fp, "// File generated: %.19s %s\r\n", asctime( &newtime ), am_pm ); + g_pFullFileSystem->FPrintf( fp, "//\r\n//\r\n// Cvar\t-\tSetting\r\n\r\n" ); + + g_pFullFileSystem->FPrintf( fp, "VERSION %.1f\r\n\r\n", SCRIPT_VERSION ); + + g_pFullFileSystem->FPrintf( fp, "DESCRIPTION SERVER_OPTIONS\r\n{\r\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CServerDescription::WriteFileHeader( FileHandle_t fp ) +{ + char am_pm[] = "AM"; + tm newtime; + VCRHook_LocalTime( &newtime ); + + if( newtime.tm_hour > 12 ) /* Set up extension. */ + Q_strncpy( am_pm, "PM", sizeof( am_pm ) ); + if( newtime.tm_hour > 12 ) /* Convert from 24-hour */ + newtime.tm_hour -= 12; /* to 12-hour clock. */ + if( newtime.tm_hour == 0 ) /*Set hour to 12 if midnight. */ + newtime.tm_hour = 12; + + g_pFullFileSystem->FPrintf( fp, "// Half-Life Server Configuration Settings\r\n" ); + g_pFullFileSystem->FPrintf( fp, "// DO NOT EDIT, GENERATED BY HALF-LIFE\r\n" ); + g_pFullFileSystem->FPrintf( fp, "// File generated: %.19s %s\r\n", asctime( &newtime ), am_pm ); + g_pFullFileSystem->FPrintf( fp, "//\r\n//\r\n// Cvar\t-\tSetting\r\n\r\n" ); +} diff --git a/gameui/CreateMultiplayerGameGameplayPage.h b/gameui/CreateMultiplayerGameGameplayPage.h new file mode 100644 index 0000000..e0cc788 --- /dev/null +++ b/gameui/CreateMultiplayerGameGameplayPage.h @@ -0,0 +1,48 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CREATEMULTIPLAYERGAMEGAMEPLAYPAGE_H +#define CREATEMULTIPLAYERGAMEGAMEPLAYPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> + +class CPanelListPanel; +class CDescription; +class mpcontrol_t; + +//----------------------------------------------------------------------------- +// Purpose: server options page of the create game server dialog +//----------------------------------------------------------------------------- +class CCreateMultiplayerGameGameplayPage : public vgui::PropertyPage +{ +public: + CCreateMultiplayerGameGameplayPage(vgui::Panel *parent, const char *name); + ~CCreateMultiplayerGameGameplayPage(); + + // returns currently entered information about the server + int GetMaxPlayers(); + const char *GetPassword(); + const char *GetHostName(); + +protected: + virtual void OnApplyChanges(); + +private: + const char *GetValue(const char *cvarName, const char *defaultValue); + void LoadGameOptionsList(); + void GatherCurrentValues(); + + CDescription *m_pDescription; + mpcontrol_t *m_pList; + CPanelListPanel *m_pOptionsList; +}; + + +#endif // CREATEMULTIPLAYERGAMEGAMEPLAYPAGE_H diff --git a/gameui/CreateMultiplayerGameServerPage.cpp b/gameui/CreateMultiplayerGameServerPage.cpp new file mode 100644 index 0000000..58f2ad5 --- /dev/null +++ b/gameui/CreateMultiplayerGameServerPage.cpp @@ -0,0 +1,313 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "CreateMultiplayerGameServerPage.h" + +using namespace vgui; + +#include <KeyValues.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/RadioButton.h> +#include <vgui_controls/CheckButton.h> +#include "filesystem.h" +#include "tier1/convar.h" +#include "EngineInterface.h" +#include "CvarToggleCheckButton.h" + +#include "ModInfo.h" + +// for SRC +#include <vstdlib/random.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#define RANDOM_MAP "#GameUI_RandomMap" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameServerPage::CCreateMultiplayerGameServerPage(vgui::Panel *parent, const char *name) : PropertyPage(parent, name) +{ + m_pSavedData = NULL; + + // we can use this if we decide we want to put "listen server" at the end of the game name + m_pMapList = new ComboBox(this, "MapList", 12, false); + + m_pEnableBotsCheck = new CheckButton( this, "EnableBotsCheck", "" ); + m_pEnableBotsCheck->SetVisible( false ); + m_pEnableBotsCheck->SetEnabled( false ); + + LoadControlSettings("Resource/CreateMultiplayerGameServerPage.res"); + + LoadMapList(); + m_szMapName[0] = 0; + + // initialize hostname + SetControlString("ServerNameEdit", ModInfo().GetGameName());//szHostName); + + // initialize password +// SetControlString("PasswordEdit", engine->pfnGetCvarString("sv_password")); + ConVarRef var( "sv_password" ); + if ( var.IsValid() ) + { + SetControlString("PasswordEdit", var.GetString() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameServerPage::~CCreateMultiplayerGameServerPage() +{ +} + +void CCreateMultiplayerGameServerPage::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_LEFT || code == KEY_XSTICK1_LEFT || code == KEY_XSTICK2_LEFT ) + { + int nItem = m_pMapList->GetActiveItem(); + nItem -= 1; + if ( nItem < 0 ) + { + nItem = m_pMapList->GetItemCount() - 1; + } + + m_pMapList->SilentActivateItem( nItem ); + } + else if ( code == KEY_XBUTTON_RIGHT || code == KEY_XSTICK1_RIGHT || code == KEY_XSTICK2_RIGHT ) + { + int nItem = m_pMapList->GetActiveItem(); + nItem += 1; + if ( nItem >= m_pMapList->GetItemCount() ) + { + nItem = 0; + } + + m_pMapList->SilentActivateItem( nItem ); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::EnableBots( KeyValues *data ) +{ + m_pSavedData = data; + + int quota = data->GetInt( "bot_quota", 0 ); + SetControlInt( "BotQuotaCombo", quota ); + m_pEnableBotsCheck->SetSelected( (quota > 0) ); + + int difficulty = data->GetInt( "bot_difficulty", 0 ); + difficulty = max( difficulty, 0 ); + difficulty = min( 3, difficulty ); + + char buttonName[64]; + Q_snprintf( buttonName, sizeof( buttonName ), "SkillLevel%d", difficulty ); + vgui::RadioButton *button = dynamic_cast< vgui::RadioButton * >(FindChildByName( buttonName )); + if ( button ) + { + button->SetSelected( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: called to get the info from the dialog +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::OnApplyChanges() +{ + KeyValues *kv = m_pMapList->GetActiveItemUserData(); + strncpy(m_szMapName, kv->GetString("mapname", ""), DATA_STR_LENGTH); + + if ( m_pSavedData ) + { + int quota = GetControlInt( "BotQuotaCombo", 0 ); + if ( !m_pEnableBotsCheck->IsSelected() ) + { + quota = 0; + } + m_pSavedData->SetInt( "bot_quota", quota ); + ConVarRef bot_quota( "bot_quota" ); + bot_quota.SetValue( quota ); + + int difficulty = 0; + for ( int i=0; i<4; ++i ) + { + char buttonName[64]; + Q_snprintf( buttonName, sizeof( buttonName ), "SkillLevel%d", i ); + vgui::RadioButton *button = dynamic_cast< vgui::RadioButton * >(FindChildByName( buttonName )); + if ( button ) + { + if ( button->IsSelected() ) + { + difficulty = i; + break; + } + } + } + m_pSavedData->SetInt( "bot_difficulty", difficulty ); + ConVarRef bot_difficulty( "bot_difficulty" ); + bot_difficulty.SetValue( difficulty ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: loads the list of available maps into the map list +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::LoadMaps( const char *pszPathID ) +{ + FileFindHandle_t findHandle = NULL; + + KeyValues *hiddenMaps = ModInfo().GetHiddenMaps(); + + const char *pszFilename = g_pFullFileSystem->FindFirstEx( "maps/*.bsp", pszPathID, &findHandle ); + while ( pszFilename ) + { + char mapname[256]; + char *ext, *str; + + // FindFirst ignores the pszPathID, so check it here + // TODO: this doesn't find maps in fallback dirs + _snprintf( mapname, sizeof(mapname), "maps/%s", pszFilename ); + if ( !g_pFullFileSystem->FileExists( mapname, pszPathID ) ) + { + goto nextFile; + } + + // remove the text 'maps/' and '.bsp' from the file name to get the map name + + str = Q_strstr( pszFilename, "maps" ); + if ( str ) + { + strncpy( mapname, str + 5, sizeof(mapname) - 1 ); // maps + \\ = 5 + } + else + { + strncpy( mapname, pszFilename, sizeof(mapname) - 1 ); + } + ext = Q_strstr( mapname, ".bsp" ); + if ( ext ) + { + *ext = 0; + } + + //!! hack: strip out single player HL maps + // this needs to be specified in a seperate file + if ( !stricmp( ModInfo().GetGameName(), "Half-Life" ) && ( mapname[0] == 'c' || mapname[0] == 't') && mapname[2] == 'a' && mapname[1] >= '0' && mapname[1] <= '5' ) + { + goto nextFile; + } + + // strip out maps that shouldn't be displayed + if ( hiddenMaps ) + { + if ( hiddenMaps->GetInt( mapname, 0 ) ) + { + goto nextFile; + } + } + + // add to the map list + m_pMapList->AddItem( mapname, new KeyValues( "data", "mapname", mapname ) ); + + // get the next file + nextFile: + pszFilename = g_pFullFileSystem->FindNext( findHandle ); + } + g_pFullFileSystem->FindClose( findHandle ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: loads the list of available maps into the map list +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::LoadMapList() +{ + // clear the current list (if any) + m_pMapList->DeleteAllItems(); + + // add special "name" to represent loading a randomly selected map + m_pMapList->AddItem( RANDOM_MAP, new KeyValues( "data", "mapname", RANDOM_MAP ) ); + + // Load the GameDir maps + LoadMaps( "GAME" ); + + // set the first item to be selected + m_pMapList->ActivateItem( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCreateMultiplayerGameServerPage::IsRandomMapSelected() +{ + const char *mapname = m_pMapList->GetActiveItemUserData()->GetString("mapname"); + if (!stricmp( mapname, RANDOM_MAP )) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameServerPage::GetMapName() +{ + int count = m_pMapList->GetItemCount(); + + // if there is only one entry it's the special "select random map" entry + if( count <= 1 ) + return NULL; + + const char *mapname = m_pMapList->GetActiveItemUserData()->GetString("mapname"); + if (!strcmp( mapname, RANDOM_MAP )) + { + int which = RandomInt( 1, count - 1 ); + mapname = m_pMapList->GetItemUserData( which )->GetString("mapname"); + } + + return mapname; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets currently selected map in the map combobox +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::SetMap(const char *mapName) +{ + for (int i = 0; i < m_pMapList->GetItemCount(); i++) + { + if (!m_pMapList->IsItemIDValid(i)) + continue; + + if (!stricmp(m_pMapList->GetItemUserData(i)->GetString("mapname"), mapName)) + { + m_pMapList->ActivateItem(i); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::OnCheckButtonChecked() +{ + SetControlEnabled("SkillLevel0", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("SkillLevel1", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("SkillLevel2", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("SkillLevel3", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("BotQuotaCombo", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("BotQuotaLabel", m_pEnableBotsCheck->IsSelected()); + SetControlEnabled("BotDifficultyLabel", m_pEnableBotsCheck->IsSelected()); +} diff --git a/gameui/CreateMultiplayerGameServerPage.h b/gameui/CreateMultiplayerGameServerPage.h new file mode 100644 index 0000000..227177b --- /dev/null +++ b/gameui/CreateMultiplayerGameServerPage.h @@ -0,0 +1,60 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CREATEMULTIPLAYERGAMESERVERPAGE_H +#define CREATEMULTIPLAYERGAMESERVERPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> +#include "CvarToggleCheckButton.h" + +//----------------------------------------------------------------------------- +// Purpose: server options page of the create game server dialog +//----------------------------------------------------------------------------- +class CCreateMultiplayerGameServerPage : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( CCreateMultiplayerGameServerPage, vgui::PropertyPage ); + +public: + CCreateMultiplayerGameServerPage(vgui::Panel *parent, const char *name); + ~CCreateMultiplayerGameServerPage(); + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + + // returns currently entered information about the server + void SetMap(const char *name); + bool IsRandomMapSelected(); + const char *GetMapName(); + + vgui::ComboBox *GetMapList( void ) { return m_pMapList; } + + // CS Bots + void EnableBots( KeyValues *data ); + int GetBotQuota( void ); + bool GetBotsEnabled( void ); + +protected: + virtual void OnApplyChanges(); + MESSAGE_FUNC( OnCheckButtonChecked, "CheckButtonChecked" ); + +private: + void LoadMapList(); + void LoadMaps( const char *pszPathID ); + + vgui::ComboBox *m_pMapList; + vgui::CheckButton *m_pEnableBotsCheck; + CCvarToggleCheckButton *m_pEnableTutorCheck; + KeyValues *m_pSavedData; + + enum { DATA_STR_LENGTH = 64 }; + char m_szMapName[DATA_STR_LENGTH]; +}; + + +#endif // CREATEMULTIPLAYERGAMESERVERPAGE_H diff --git a/gameui/CustomTabExplanationDialog.cpp b/gameui/CustomTabExplanationDialog.cpp new file mode 100644 index 0000000..f58c1cb --- /dev/null +++ b/gameui/CustomTabExplanationDialog.cpp @@ -0,0 +1,93 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "CustomTabExplanationDialog.h" +#include "BasePanel.h" +#include "convar.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "ModInfo.h" +#include <stdio.h> + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCustomTabExplanationDialog::CCustomTabExplanationDialog(vgui::Panel *parent) : BaseClass(parent, "CustomTabExplanationDialog") +{ + SetDeleteSelfOnClose(true); + SetSizeable( false ); + + input()->SetAppModalSurface(GetVPanel()); + + LoadControlSettings("Resource/CustomTabExplanationDialog.res"); + + MoveToCenterOfScreen(); + + GameUI().PreventEngineHideGameUI(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCustomTabExplanationDialog::~CCustomTabExplanationDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCustomTabExplanationDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetDialogVariable( "game", ModInfo().GetGameName() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCustomTabExplanationDialog::OnKeyCodePressed(KeyCode code) +{ + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void CCustomTabExplanationDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "ok" ) || !stricmp( command, "cancel" ) || !stricmp( command, "close" ) ) + { + Close(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCustomTabExplanationDialog::OnClose( void ) +{ + BaseClass::OnClose(); + GameUI().AllowEngineHideGameUI(); +} diff --git a/gameui/CustomTabExplanationDialog.h b/gameui/CustomTabExplanationDialog.h new file mode 100644 index 0000000..754391b --- /dev/null +++ b/gameui/CustomTabExplanationDialog.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef CUSTOMTABEXPLANATIONDIALOG_H +#define CUSTOMTABEXPLANATIONDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "utlvector.h" +#include <vgui/KeyCode.h> +#include "vgui_controls/URLLabel.h" + +//----------------------------------------------------------------------------- +// Purpose: Dialog that explains the custom tab +//----------------------------------------------------------------------------- +class CCustomTabExplanationDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CCustomTabExplanationDialog, vgui::Frame ); + +public: + CCustomTabExplanationDialog(vgui::Panel *parent); + ~CCustomTabExplanationDialog(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnKeyCodePressed(vgui::KeyCode code); + virtual void OnCommand( const char *command ); + virtual void OnClose( void ); +}; + +#endif // CUSTOMTABEXPLANATIONDIALOG_H diff --git a/gameui/CvarNegateCheckButton.cpp b/gameui/CvarNegateCheckButton.cpp new file mode 100644 index 0000000..8f9e3cb --- /dev/null +++ b/gameui/CvarNegateCheckButton.cpp @@ -0,0 +1,136 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "CvarNegateCheckButton.h" +#include "EngineInterface.h" +#include <vgui/IVGui.h> +#include "IGameUIFuncs.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +CCvarNegateCheckButton::CCvarNegateCheckButton( Panel *parent, const char *panelName, const char *text, + const char *cvarname ) + : CheckButton( parent, panelName, text ) +{ + m_pszCvarName = cvarname ? strdup( cvarname ) : NULL; + Reset(); + AddActionSignalTarget( this ); +} + +CCvarNegateCheckButton::~CCvarNegateCheckButton() +{ + free( m_pszCvarName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCvarNegateCheckButton::Paint() +{ + if ( !m_pszCvarName ) + { + BaseClass::Paint(); + return; + } + + // Look up current value +// float value = engine->pfnGetCvarFloat( m_pszCvarName ); + ConVarRef var( m_pszCvarName ); + if ( !var.IsValid() ) + return; + + float value = var.GetFloat(); + + if ( value < 0 ) + { + if ( !m_bStartState ) + { + SetSelected( true ); + m_bStartState = true; + } + } + else + { + if ( m_bStartState ) + { + SetSelected( false ); + m_bStartState = false; + } + } + BaseClass::Paint(); +} + +void CCvarNegateCheckButton::Reset() +{ + // Look up current value +// float value = engine->pfnGetCvarFloat( m_pszCvarName ); + ConVarRef var( m_pszCvarName ); + if ( !var.IsValid() ) + return; + + float value = var.GetFloat(); + + if ( value < 0 ) + { + m_bStartState = true; + } + else + { + m_bStartState = false; + } + SetSelected(m_bStartState); +} + +bool CCvarNegateCheckButton::HasBeenModified() +{ + return IsSelected() != m_bStartState; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *panel - +//----------------------------------------------------------------------------- +void CCvarNegateCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); +} + +void CCvarNegateCheckButton::ApplyChanges() +{ + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + return; + + ConVarRef var( m_pszCvarName ); + float value = var.GetFloat(); + + value = (float)fabs( value ); + if (value < 0.00001) + { + // correct the value if it's not set + value = 0.022f; + } + + m_bStartState = IsSelected(); + value = -value; + + float ans = m_bStartState ? value : -value; + var.SetValue( ans ); +} + + +void CCvarNegateCheckButton::OnButtonChecked() +{ + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +} diff --git a/gameui/CvarNegateCheckButton.h b/gameui/CvarNegateCheckButton.h new file mode 100644 index 0000000..9eb1d94 --- /dev/null +++ b/gameui/CvarNegateCheckButton.h @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CVARNEGATECHECKBUTTON_H +#define CVARNEGATECHECKBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/CheckButton.h> + +class CCvarNegateCheckButton : public vgui::CheckButton +{ + DECLARE_CLASS_SIMPLE( CCvarNegateCheckButton, vgui::CheckButton ); + +public: + CCvarNegateCheckButton( vgui::Panel *parent, const char *panelName, const char *text, + char const *cvarname ); + ~CCvarNegateCheckButton(); + + virtual void SetSelected( bool state ); + virtual void Paint(); + + void Reset(); + void ApplyChanges(); + bool HasBeenModified(); + +private: + MESSAGE_FUNC( OnButtonChecked, "CheckButtonChecked" ); + + char *m_pszCvarName; + bool m_bStartState; +}; + +#endif // CVARNEGATECHECKBUTTON_H diff --git a/gameui/CvarTextEntry.cpp b/gameui/CvarTextEntry.cpp new file mode 100644 index 0000000..8f8e926 --- /dev/null +++ b/gameui/CvarTextEntry.cpp @@ -0,0 +1,112 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "CvarTextEntry.h" +#include "EngineInterface.h" +#include <vgui/IVGui.h> +#include "IGameUIFuncs.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +static const int MAX_CVAR_TEXT = 64; + +CCvarTextEntry::CCvarTextEntry( Panel *parent, const char *panelName, char const *cvarname ) + : TextEntry( parent, panelName) +{ + m_pszCvarName = cvarname ? strdup( cvarname ) : NULL; + m_pszStartValue[0] = 0; + + if ( m_pszCvarName ) + { + Reset(); + } + + AddActionSignalTarget( this ); +} + +CCvarTextEntry::~CCvarTextEntry() +{ + if ( m_pszCvarName ) + { + free( m_pszCvarName ); + } +} + +void CCvarTextEntry::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + if (GetMaximumCharCount() < 0 || GetMaximumCharCount() > MAX_CVAR_TEXT) + { + SetMaximumCharCount(MAX_CVAR_TEXT - 1); + } +} + +void CCvarTextEntry::ApplyChanges( bool immediate ) +{ + if ( !m_pszCvarName ) + return; + + char szText[ MAX_CVAR_TEXT ]; + GetText( szText, MAX_CVAR_TEXT ); + + if ( !szText[ 0 ] ) + return; + + if ( immediate ) + { + // set immediately - don't wait for the next frame + ConVarRef newCvar( m_pszCvarName ); + newCvar.SetValue( szText ); + } + else + { + char szCommand[ 256 ]; + sprintf( szCommand, "%s \"%s\"\n", m_pszCvarName, szText ); + engine->ClientCmd_Unrestricted( szCommand ); + } + + Q_strncpy( m_pszStartValue, szText, sizeof( m_pszStartValue ) ); +} + +void CCvarTextEntry::Reset() +{ +// char *value = engine->pfnGetCvarString( m_pszCvarName ); + ConVarRef var( m_pszCvarName ); + if ( !var.IsValid() ) + return; + const char *value = var.GetString(); + if ( value && value[ 0 ] ) + { + SetText( value ); + Q_strncpy( m_pszStartValue, value, sizeof( m_pszStartValue ) ); + } +} + +bool CCvarTextEntry::HasBeenModified() +{ + char szText[ MAX_CVAR_TEXT ]; + GetText( szText, MAX_CVAR_TEXT ); + + return stricmp( szText, m_pszStartValue ); +} + + +void CCvarTextEntry::OnTextChanged() +{ + if ( !m_pszCvarName ) + return; + + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +} diff --git a/gameui/CvarTextEntry.h b/gameui/CvarTextEntry.h new file mode 100644 index 0000000..8e3ad14 --- /dev/null +++ b/gameui/CvarTextEntry.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CVARTEXTENTRY_H +#define CVARTEXTENTRY_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/TextEntry.h> + +class CCvarTextEntry : public vgui::TextEntry +{ + DECLARE_CLASS_SIMPLE( CCvarTextEntry, vgui::TextEntry ); + +public: + CCvarTextEntry( vgui::Panel *parent, const char *panelName, char const *cvarname ); + ~CCvarTextEntry(); + + MESSAGE_FUNC( OnTextChanged, "TextChanged" ); + void ApplyChanges( bool immediate = false ); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + void Reset(); + bool HasBeenModified(); + +private: + char *m_pszCvarName; + char m_pszStartValue[64]; +}; + +#endif // CVARTEXTENTRY_H diff --git a/gameui/CvarToggleCheckButton.cpp b/gameui/CvarToggleCheckButton.cpp new file mode 100644 index 0000000..f8d3141 --- /dev/null +++ b/gameui/CvarToggleCheckButton.cpp @@ -0,0 +1,159 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "CvarToggleCheckButton.h" +#include "EngineInterface.h" +#include <vgui/IVGui.h> +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include "IGameUIFuncs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +vgui::Panel *CvarToggleCheckButton_Factory() +{ + return new CCvarToggleCheckButton( NULL, NULL, "CvarToggleCheckButton", NULL ); +} +DECLARE_BUILD_FACTORY_CUSTOM( CCvarToggleCheckButton, CvarToggleCheckButton_Factory ); + +CCvarToggleCheckButton::CCvarToggleCheckButton( Panel *parent, const char *panelName, const char *text, + char const *cvarname ) + : CheckButton( parent, panelName, text ) +{ + m_pszCvarName = cvarname ? strdup( cvarname ) : NULL; + + if (m_pszCvarName) + { + Reset(); + } + AddActionSignalTarget( this ); +} + +CCvarToggleCheckButton::~CCvarToggleCheckButton() +{ + if ( m_pszCvarName ) + { + free( m_pszCvarName ); + } +} + +void CCvarToggleCheckButton::Paint() +{ + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + { + BaseClass::Paint(); + return; + } + + // Look up current value +// bool value = engine->pfnGetCvarFloat( m_pszCvarName ) > 0.0f ? true : false; + ConVarRef var( m_pszCvarName, true ); + if ( !var.IsValid() ) + return; + bool value = var.GetBool(); + + if ( value != m_bStartValue ) + //if ( value != IsSelected() ) + { + SetSelected( value ); + m_bStartValue = value; + } + BaseClass::Paint(); +} + +void CCvarToggleCheckButton::ApplyChanges() +{ + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + return; + + m_bStartValue = IsSelected(); +// engine->Cvar_SetValue( m_pszCvarName, m_bStartValue ? 1.0f : 0.0f ); + ConVarRef var( m_pszCvarName, true ); + if ( !var.IsValid() ) + return; + var.SetValue( m_bStartValue ); +} + +void CCvarToggleCheckButton::Reset() +{ +// m_bStartValue = engine->pfnGetCvarFloat( m_pszCvarName ) > 0.0f ? true : false; + + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + return; + + ConVarRef var( m_pszCvarName, true ); + if ( !var.IsValid() ) + return; + m_bStartValue = var.GetBool(); + SetSelected(m_bStartValue); +} + +bool CCvarToggleCheckButton::HasBeenModified() +{ + return IsSelected() != m_bStartValue; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *panel - +//----------------------------------------------------------------------------- +void CCvarToggleCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); + + if ( !m_pszCvarName || !m_pszCvarName[ 0 ] ) + return; +/* + // Look up current value + bool value = state; + + engine->Cvar_SetValue( m_pszCvarName, value ? 1.0f : 0.0f );*/ +} + + +//----------------------------------------------------------------------------- +void CCvarToggleCheckButton::OnButtonChecked() +{ + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +} + +//----------------------------------------------------------------------------- +void CCvarToggleCheckButton::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + const char *cvarName = inResourceData->GetString("cvar_name", ""); + const char *cvarValue = inResourceData->GetString("cvar_value", ""); + + if( Q_stricmp( cvarName, "") == 0 ) + return;// Doesn't have cvar set up in res file, must have been constructed with it. + + if( m_pszCvarName ) + free( m_pszCvarName );// got a "", not a NULL from the create-control call + + m_pszCvarName = cvarName ? strdup( cvarName ) : NULL; + + if( Q_stricmp( cvarValue, "1") == 0 ) + m_bStartValue = true; + else + m_bStartValue = false; + + const ConVar *var = cvar->FindVar( m_pszCvarName ); + if ( var ) + { + if( var->GetBool() ) + SetSelected( true ); + else + SetSelected( false ); + } +}
\ No newline at end of file diff --git a/gameui/CvarToggleCheckButton.h b/gameui/CvarToggleCheckButton.h new file mode 100644 index 0000000..2294745 --- /dev/null +++ b/gameui/CvarToggleCheckButton.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CVARTOGGLECHECKBUTTON_H +#define CVARTOGGLECHECKBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/CheckButton.h> + +class CCvarToggleCheckButton : public vgui::CheckButton +{ + DECLARE_CLASS_SIMPLE( CCvarToggleCheckButton, vgui::CheckButton ); + +public: + CCvarToggleCheckButton( vgui::Panel *parent, const char *panelName, const char *text, + char const *cvarname ); + ~CCvarToggleCheckButton(); + + virtual void SetSelected( bool state ); + + virtual void Paint(); + + void Reset(); + void ApplyChanges(); + bool HasBeenModified(); + virtual void ApplySettings( KeyValues *inResourceData ); + +private: + MESSAGE_FUNC( OnButtonChecked, "CheckButtonChecked" ); + + char *m_pszCvarName; + bool m_bStartValue; +}; + +#endif // CVARTOGGLECHECKBUTTON_H diff --git a/gameui/EngineInterface.h b/gameui/EngineInterface.h new file mode 100644 index 0000000..c2ad78b --- /dev/null +++ b/gameui/EngineInterface.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Includes all the headers/declarations necessary to access the +// engine interface +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ENGINEINTERFACE_H +#define ENGINEINTERFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +// these stupid set of includes are required to use the cdll_int interface +#include "mathlib/vector.h" +//#include "wrect.h" +#define IN_BUTTONS_H + +// engine interface +#include "cdll_int.h" +#include "icvar.h" + +// engine interface singleton accessors +extern IVEngineClient *engine; +extern class IGameUIFuncs *gameuifuncs; +extern class IEngineSound *enginesound; +extern class IMatchmaking *matchmaking; +extern class IXboxSystem *xboxsystem; +extern class IAchievementMgr *achievementmgr; +extern class CSteamAPIContext *steamapicontext; + +#endif // ENGINEINTERFACE_H diff --git a/gameui/GameApp.cpp b/gameui/GameApp.cpp new file mode 100644 index 0000000..db4f992 --- /dev/null +++ b/gameui/GameApp.cpp @@ -0,0 +1 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// diff --git a/gameui/GameApp.h b/gameui/GameApp.h new file mode 100644 index 0000000..db4f992 --- /dev/null +++ b/gameui/GameApp.h @@ -0,0 +1 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// diff --git a/gameui/GameConsole.cpp b/gameui/GameConsole.cpp new file mode 100644 index 0000000..5f1cb0c --- /dev/null +++ b/gameui/GameConsole.cpp @@ -0,0 +1,169 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <stdio.h> + +#include "GameConsole.h" +#include "GameConsoleDialog.h" +#include "LoadingDialog.h" +#include "vgui/ISurface.h" + +#include "KeyValues.h" +#include "vgui/VGUI.h" +#include "vgui/IVGui.h" +#include "vgui_controls/Panel.h" +#include "convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static CGameConsole g_GameConsole; +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +CGameConsole &GameConsole() +{ + return g_GameConsole; +} +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameConsole, IGameConsole, GAMECONSOLE_INTERFACE_VERSION, g_GameConsole); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CGameConsole::CGameConsole() +{ + m_bInitialized = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CGameConsole::~CGameConsole() +{ + m_bInitialized = false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets up the console for use +//----------------------------------------------------------------------------- +void CGameConsole::Initialize() +{ +#ifndef _XBOX + m_pConsole = vgui::SETUP_PANEL( new CGameConsoleDialog() ); // we add text before displaying this so set it up now! + + // set the console to taking up most of the right-half of the screen + int swide, stall; + vgui::surface()->GetScreenSize(swide, stall); + int offsetx = vgui::scheme()->GetProportionalScaledValue(16); + int offsety = vgui::scheme()->GetProportionalScaledValue(64); + + m_pConsole->SetBounds( + swide / 2 - offsetx, + offsety, + swide / 2, + stall - (offsety * 2)); + + m_bInitialized = true; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: activates the console, makes it visible and brings it to the foreground +//----------------------------------------------------------------------------- +void CGameConsole::Activate() +{ +#ifndef _XBOX + if (!m_bInitialized) + return; + + vgui::surface()->RestrictPaintToSinglePanel(NULL); + m_pConsole->Activate(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: hides the console +//----------------------------------------------------------------------------- +void CGameConsole::Hide() +{ +#ifndef _XBOX + if (!m_bInitialized) + return; + + m_pConsole->Hide(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: clears the console +//----------------------------------------------------------------------------- +void CGameConsole::Clear() +{ +#ifndef _XBOX + if (!m_bInitialized) + return; + + m_pConsole->Clear(); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: returns true if the console is currently in focus +//----------------------------------------------------------------------------- +bool CGameConsole::IsConsoleVisible() +{ +#ifndef _XBOX + if (!m_bInitialized) + return false; + + return m_pConsole->IsVisible(); +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: activates the console after a delay +//----------------------------------------------------------------------------- +void CGameConsole::ActivateDelayed(float time) +{ +#ifndef _XBOX + if (!m_bInitialized) + return; + + m_pConsole->PostMessage(m_pConsole, new KeyValues("Activate"), time); +#endif +} + +void CGameConsole::SetParent( int parent ) +{ +#ifndef _XBOX + if (!m_bInitialized) + return; + + m_pConsole->SetParent( static_cast<vgui::VPANEL>( parent )); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: static command handler +//----------------------------------------------------------------------------- +void CGameConsole::OnCmdCondump() +{ +#ifndef _XBOX + g_GameConsole.m_pConsole->DumpConsoleTextToFile(); +#endif +} + +#ifndef _XBOX +CON_COMMAND( condump, "dump the text currently in the console to condumpXX.log" ) +{ + g_GameConsole.OnCmdCondump(); +} +#endif diff --git a/gameui/GameConsole.h b/gameui/GameConsole.h new file mode 100644 index 0000000..f3ed1ce --- /dev/null +++ b/gameui/GameConsole.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef GAMECONSOLE_H +#define GAMECONSOLE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "GameUI/IGameConsole.h" + +class CGameConsoleDialog; + +//----------------------------------------------------------------------------- +// Purpose: VGui implementation of the game/dev console +//----------------------------------------------------------------------------- +class CGameConsole : public IGameConsole +{ +public: + CGameConsole(); + ~CGameConsole(); + + // sets up the console for use + void Initialize(); + + // activates the console, makes it visible and brings it to the foreground + virtual void Activate(); + // hides the console + virtual void Hide(); + // clears the console + virtual void Clear(); + + // returns true if the console is currently in focus + virtual bool IsConsoleVisible(); + + // activates the console after a delay + void ActivateDelayed(float time); + + void SetParent( int parent ); + + static void OnCmdCondump(); +private: + + bool m_bInitialized; + CGameConsoleDialog *m_pConsole; +}; + +extern CGameConsole &GameConsole(); + +#endif // GAMECONSOLE_H diff --git a/gameui/GameConsoleDialog.cpp b/gameui/GameConsoleDialog.cpp new file mode 100644 index 0000000..430102c --- /dev/null +++ b/gameui/GameConsoleDialog.cpp @@ -0,0 +1,99 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "GameConsoleDialog.h" +#include "GameUI_Interface.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "vgui/KeyCode.h" +#include "LoadingDialog.h" +#include "IGameUIFuncs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CGameConsoleDialog::CGameConsoleDialog() : BaseClass( NULL, "GameConsole", false ) +{ + AddActionSignalTarget( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: generic vgui command handler +//----------------------------------------------------------------------------- +void CGameConsoleDialog::OnCommand(const char *command) +{ + if ( !Q_stricmp( command, "Close" ) ) + { + if ( GameUI().IsInBackgroundLevel() ) + { + // Tell the engine we've hid the console, so that it unpauses the game + // even though we're still sitting at the menu. + engine->ClientCmd_Unrestricted( "unpause" ); + } + } + + BaseClass::OnCommand(command); +} + + +//----------------------------------------------------------------------------- +// HACK: Allow F key bindings to operate even when typing in the text entry field +//----------------------------------------------------------------------------- +void CGameConsoleDialog::OnKeyCodeTyped(KeyCode code) +{ + BaseClass::OnKeyCodeTyped(code); + + // check for processing + if ( m_pConsolePanel->TextEntryHasFocus() ) + { + // HACK: Allow F key bindings to operate even here + if ( code >= KEY_F1 && code <= KEY_F12 ) + { + // See if there is a binding for the FKey + const char *binding = gameuifuncs->GetBindingForButtonCode( code ); + if ( binding && binding[0] ) + { + // submit the entry as a console commmand + char szCommand[256]; + Q_strncpy( szCommand, binding, sizeof( szCommand ) ); + engine->ClientCmd_Unrestricted( szCommand ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Submits a command +//----------------------------------------------------------------------------- +void CGameConsoleDialog::OnCommandSubmitted( const char *pCommand ) +{ + engine->ClientCmd_Unrestricted( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Submits a command +//----------------------------------------------------------------------------- +void CGameConsoleDialog::OnClosedByHittingTilde() +{ + if ( !LoadingDialog() ) + { + GameUI().HideGameUI(); + } + else + { + vgui::surface()->RestrictPaintToSinglePanel( LoadingDialog()->GetVPanel() ); + } +} diff --git a/gameui/GameConsoleDialog.h b/gameui/GameConsoleDialog.h new file mode 100644 index 0000000..5d57ef1 --- /dev/null +++ b/gameui/GameConsoleDialog.h @@ -0,0 +1,40 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef GAMECONSOLEDIALOG_H +#define GAMECONSOLEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/consoledialog.h" +#include <Color.h> +#include "utlvector.h" +#include "EngineInterface.h" +#include "vgui_controls/Frame.h" + + +//----------------------------------------------------------------------------- +// Purpose: Game/dev console dialog +//----------------------------------------------------------------------------- +class CGameConsoleDialog : public vgui::CConsoleDialog +{ + DECLARE_CLASS_SIMPLE( CGameConsoleDialog, vgui::CConsoleDialog ); + +public: + CGameConsoleDialog(); + +private: + MESSAGE_FUNC( OnClosedByHittingTilde, "ClosedByHittingTilde" ); + MESSAGE_FUNC_CHARPTR( OnCommandSubmitted, "CommandSubmitted", command ); + + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + virtual void OnCommand( const char *command ); +}; + + +#endif // GAMECONSOLEDIALOG_H diff --git a/gameui/GameUI.h b/gameui/GameUI.h new file mode 100644 index 0000000..8d17348 --- /dev/null +++ b/gameui/GameUI.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef GAMEUI_H +#define GAMEUI_H +#ifdef _WIN32 +#pragma once +#endif + +class IGameUI; + +//----------------------------------------------------------------------------- +// Purpose: Accessor function to get game ui interface +//----------------------------------------------------------------------------- +inline IGameUI *gameui() +{ + extern IGameUI *g_pGameUI; + return g_pGameUI; +} + +#endif // GAMEUI_H diff --git a/gameui/GameUI.vpc b/gameui/GameUI.vpc new file mode 100644 index 0000000..bf782c2 --- /dev/null +++ b/gameui/GameUI.vpc @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// GAMEUI.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$include "$SRCDIR\vpc_scripts\source_dll_base.vpc" +$Include "$SRCDIR\vpc_scripts\source_saxxyawards.vpc" +$include "$SRCDIR\vpc_scripts\source_cryptlib_include.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;.\;$SRCDIR\vgui2\include;$SRCDIR\vgui2\controls;$SRCDIR\common\GameUI" + $PreprocessorDefinitions "$BASE;GAMEUI_EXPORTS;VERSION_SAFE_STEAM_API_INTERFACES" + } + + $Linker + { + $AdditionalDependencies "$BASE Ws2_32.lib odbc32.lib odbccp32.lib Shlwapi.lib" [$WIN32] + $SystemLibraries "iconv;z" [$OSXALL] + $SystemLibraries "rt" [$LINUXALL && !$DEDICATED] + $GCC_ExtraLinkerFlags "-L/usr/lib32 -L/usr/lib" [$LINUXALL && !$DEDICATED] + } +} + +$Configuration "Debug" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE Xonlined.lib" + } +} + +$Configuration "Release" +{ + $Linker [$X360] + { + $AdditionalDependencies "$BASE Xonline.lib" + } +} + +$Project "GameUI" +{ + $Folder "Source Files" + { + $File "BackgroundMenuButton.cpp" + $File "BasePanel.cpp" + $File "GameConsole.cpp" + $File "GameUI_Interface.cpp" + $File "LogoFile.cpp" [!$POSIX] + $File "ModInfo.cpp" + $File "MouseMessageForwardingPanel.cpp" + $File "$SRCDIR\Tracker\common\msgbuffer.cpp" [!$POSIX] + $File "$SRCDIR\Tracker\common\netapi.cpp" [!$POSIX] + $File "$SRCDIR\common\GameUI\ObjectList.cpp" + $File "PanelListPanel.cpp" + $File "RunGameEngine.cpp" + $File "$SRCDIR\common\GameUI\scriptobject.cpp" + $File "$SRCDIR\Tracker\common\Socket.cpp" [!$POSIX] + $File "Sys_Utils.cpp" + $File "TextEntryBox.cpp" + $File "TGAImagePanel.cpp" + $File "$SRCDIR\public\vgui_controls\vgui_controls.cpp" + $File "VGuiSystemModuleLoader.cpp" + $File "BonusMapsDatabase.cpp" + $File "BonusMapsDatabase.h" + $File "$SRCDIR\common\language.cpp" + $File "$SRCDIR\common\imageutils.cpp" + $File "SaveGameBrowserDialog.cpp" + $File "gameui_util.cpp" + $File "gameui_util.h" + } + + $Folder "Header Files" + { + $File "BackgroundMenuButton.h" + $File "BasePanel.h" + $File "BaseSaveGameDialog.h" + $File "CDKeyEntryDialog.h" + $File "ChangeGameDialog.h" + $File "CreateMultiplayerGameBotPage.h" + $File "CreateMultiplayerGameDialog.h" + $File "CreateMultiplayerGameGameplayPage.h" + $File "CreateMultiplayerGameServerPage.h" + $File "EngineInterface.h" + $File "GameConsole.h" + $File "GameUI_Interface.h" + $File "LoadingDialog.h" + $File "$SRCDIR\vgui2\src\Memorybitmap.h" + $File "ModInfo.h" + $File "MouseMessageForwardingPanel.h" + $File "PanelListPanel.h" + $File "$SRCDIR\common\GameUI\scriptobject.h" + $File "Sys_Utils.h" + $File "TextEntryBox.h" + $File "TGAImagePanel.h" + $File "VGuiSystemModuleLoader.h" + $File "SaveGameBrowserDialog.h" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\iachievementmgr.h" + $File "$SRCDIR\public\game\client\IGameClientExports.h" + $File "$SRCDIR\common\GameUI\IGameUI.h" + $File "$SRCDIR\public\IGameUIFuncs.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\common\IObjectContainer.h" + $File "$SRCDIR\common\IRunGameEngine.h" + $File "$SRCDIR\common\IVguiModule.h" + $File "$SRCDIR\common\IVGuiModuleLoader.h" + $File "$SRCDIR\common\GameUI\ObjectList.h" + $File "$SRCDIR\public\savegame_version.h" + $File "$SRCDIR\Tracker\common\TrackerMessageFlags.h" + $File "$SRCDIR\common\ValveCDKeyGameAndTerritoryCodes.h" + $File "$SRCDIR\common\language.h" + $File "$SRCDIR\common\imageutils.h" + } + + $Folder "Controls" + { + $File "BitmapImagePanel.cpp" + $File "BitmapImagePanel.h" + $File "CommandCheckButton.cpp" + $File "CommandCheckButton.h" + $File "CvarNegateCheckButton.cpp" + $File "CvarNegateCheckButton.h" + $File "$SRCDIR\common\GameUI\cvarslider.cpp" + $File "$SRCDIR\common\GameUI\cvarslider.h" + $File "CvarTextEntry.cpp" + $File "CvarTextEntry.h" + $File "CvarToggleCheckButton.cpp" + $File "CvarToggleCheckButton.h" + $File "HapticControlBox.cpp" + $File "HapticControlBox.h" + $File "KeyToggleCheckButton.cpp" + $File "KeyToggleCheckButton.h" + $File "LabeledCommandComboBox.cpp" + $File "LabeledCommandComboBox.h" + $File "URLButton.cpp" + $File "URLButton.h" + $File "vcontrolslistpanel.cpp" + $File "vcontrolslistpanel.h" + } + + $Folder "Dialogs" + { + $File "BenchmarkDialog.cpp" + $File "BenchmarkDialog.h" + $File "BonusMapsDialog.cpp" + $File "BonusMapsDialog.h" + $File "CommentaryDialog.cpp" + $File "CommentaryDialog.h" + $File "CommentaryExplanationDialog.cpp" + $File "CommentaryExplanationDialog.h" + $File "ContentControlDialog.cpp" + $File "ContentControlDialog.h" + $File "CustomTabExplanationDialog.cpp" + $File "CustomTabExplanationDialog.h" + $File "GameConsoleDialog.cpp" + $File "GameConsoleDialog.h" + $File "LoadGameDialog_Xbox.cpp" + $File "LoadGameDialog.cpp" + $File "LoadGameDialog.h" + $File "MultiplayerAdvancedDialog.cpp" + $File "MultiplayerAdvancedDialog.h" + $File "NewGameDialog.cpp" + $File "NewGameDialog.h" + $File "PlayerListDialog.cpp" + $File "PlayerListDialog.h" + $File "SaveGameDialog_Xbox.cpp" + $File "SaveGameDialog.cpp" + $File "SaveGameDialog.h" + $File "LoadCommentaryDialog.cpp" + $File "LoadingDialog.cpp" + $File "BaseSaveGameDialog.cpp" + $File "ChangeGameDialog.cpp" [!$POSIX] + $File "CreateMultiplayerGameBotPage.cpp" + $File "CreateMultiplayerGameDialog.cpp" + $File "CreateMultiplayerGameGameplayPage.cpp" + $File "CreateMultiplayerGameServerPage.cpp" + $File "OptionsDialog_Xbox.cpp" + $File "ControllerDialog.cpp" + $File "ControllerDialog.h" + } + + $Folder "Matchmaking" + { + $File "matchmaking\achievementsdialog.cpp" + $File "matchmaking\achievementsdialog.h" + $File "matchmaking\basedialog.cpp" + $File "matchmaking\basedialog.h" + $File "matchmaking\dialogmenu.cpp" + $File "matchmaking\dialogmenu.h" + $File "matchmaking\leaderboarddialog.cpp" + $File "matchmaking\leaderboarddialog.h" + $File "matchmaking\matchmakingbasepanel.cpp" + $File "matchmaking\matchmakingbasepanel.h" + $File "matchmaking\pausedialog.cpp" + $File "matchmaking\pausedialog.h" + $File "matchmaking\sessionlobbydialog.cpp" + $File "matchmaking\sessionlobbydialog.h" + $File "matchmaking\sessionoptionsdialog.cpp" + $File "matchmaking\sessionoptionsdialog.h" + $File "matchmaking\sessionbrowserdialog.cpp" + $File "matchmaking\sessionbrowserdialog.h" + $File "matchmaking\welcomedialog.cpp" + $File "matchmaking\welcomedialog.h" + } + + $Folder "Options Dialog" + { + $File "OptionsDialog.cpp" + $File "OptionsDialog.h" + $File "OptionsSubAudio.cpp" + $File "OptionsSubAudio.h" + $File "OptionsSubDifficulty.cpp" + $File "OptionsSubDifficulty.h" + $File "OptionsSubGame.cpp" + $File "OptionsSubGame.h" + $File "OptionsSubHaptics.cpp" [$WIN32] + $File "OptionsSubHaptics.h" [$WIN32] + $File "OptionsSubKeyboard.cpp" + $File "OptionsSubKeyboard.h" + $File "OptionsSubMouse.cpp" + $File "OptionsSubMouse.h" + $File "OptionsSubMultiplayer.cpp" + $File "OptionsSubMultiplayer.h" + $File "OptionsSubPortal.cpp" + $File "OptionsSubPortal.h" + $File "OptionsSubVideo.cpp" + $File "OptionsSubVideo.h" + $File "OptionsSubVoice.cpp" + $File "OptionsSubVoice.h" + } + + $Folder "Link Libraries" + { + $Lib bitmap + $Lib mathlib + $Lib matsys_controls + $Lib tier2 + $Lib tier3 + $Lib vgui_controls + $Lib vtf + $Lib "$LIBCOMMON/libjpeg" [!$DEDICATED] + $ImpLib steam_api + $Lib libpng [!$VS2015&&!$DEDICATED] + $Lib $LIBCOMMON/libpng [$VS2015&&!$DEDICATED] + $Lib libz [!$DEDICATED] + $ImpLib SDL2 [$SDL] + } +} diff --git a/gameui/GameUIPanel.h b/gameui/GameUIPanel.h new file mode 100644 index 0000000..6b61566 --- /dev/null +++ b/gameui/GameUIPanel.h @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef GAMEUIPANEL_H +#define GAMEUIPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +// Retrieve the root panel for the GameUI subsystem +namespace vgui +{ +class Panel; +}; + +vgui::Panel *GetGameUIRootPanel( void ); + +#endif // GAMEUIPANEL_H diff --git a/gameui/GameUI_Interface.cpp b/gameui/GameUI_Interface.cpp new file mode 100644 index 0000000..01c7b39 --- /dev/null +++ b/gameui/GameUI_Interface.cpp @@ -0,0 +1,1285 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements all the functions exported by the GameUI dll +// +// $NoKeywords: $ +//===========================================================================// + +#ifdef WIN32 +#if !defined( _X360 ) +#include <windows.h> +#endif +#include <io.h> +#include <direct.h> +#elif defined( POSIX ) +#include <sys/time.h> +#else +#error +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <tier0/dbg.h> + +#ifdef SendMessage +#undef SendMessage +#endif + +#include "filesystem.h" +#include "GameUI_Interface.h" +#include "Sys_Utils.h" +#include "string.h" +#include "tier0/icommandline.h" + +// interface to engine +#include "EngineInterface.h" + +#include "replay/ienginereplay.h" +#include "replay/ireplaysystem.h" + +#include "VGuiSystemModuleLoader.h" +#include "bitmap/tgaloader.h" + +#include "GameConsole.h" +#include "LoadingDialog.h" +#include "CDKeyEntryDialog.h" +#include "ModInfo.h" +#include "game/client/IGameClientExports.h" +#include "materialsystem/imaterialsystem.h" +#include "engine/imatchmaking.h" +#include "ixboxsystem.h" +#include "iachievementmgr.h" +#include "IGameUIFuncs.h" +#include <ienginevgui.h> +#include "steam/steam_api.h" +#include "BonusMapsDatabase.h" +#include "BonusMapsDialog.h" +#include "sourcevr/isourcevirtualreality.h" + +// vgui2 interface +// note that GameUI project uses ..\vgui2\include, not ..\utils\vgui\include +#include "BasePanel.h" + +#include <vgui/Cursor.h> +#include <KeyValues.h> +#include <vgui/ILocalize.h> +#include <vgui/IPanel.h> +#include <vgui/IScheme.h> +#include <vgui/IVGui.h> +#include <vgui/ISystem.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Menu.h> +#include <vgui_controls/PHandle.h> +#include "tier3/tier3.h" +#include "tier0/vcrmode.h" +#include "matsys_controls/matsyscontrols.h" +#include "steam/steam_api.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#include "tier0/dbg.h" +#include "engine/IEngineSound.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +IGameUIFuncs *gameuifuncs = NULL; +IEngineVGui *enginevguifuncs = NULL; +IMatchmaking *matchmaking = NULL; +IXboxSystem *xboxsystem = NULL; // 360 only +vgui::ISurface *enginesurfacefuncs = NULL; +IVEngineClient *engine = NULL; +IEngineSound *enginesound = NULL; +IAchievementMgr *achievementmgr = NULL; +IEngineClientReplay *g_pEngineClientReplay = NULL; +ISourceVirtualReality *g_pSourceVR = NULL; + +static CSteamAPIContext g_SteamAPIContext; +CSteamAPIContext *steamapicontext = &g_SteamAPIContext; + +static CBasePanel *staticPanel = NULL; + +class CGameUI; +CGameUI *g_pGameUI = NULL; + +class CLoadingDialog; +vgui::DHANDLE<CLoadingDialog> g_hLoadingDialog; +vgui::VPANEL g_hLoadingBackgroundDialog = NULL; + +static CGameUI g_GameUI; +static WHANDLE g_hMutex = NULL; +static WHANDLE g_hWaitMutex = NULL; + +static IGameClientExports *g_pGameClientExports = NULL; +IGameClientExports *GameClientExports() +{ + return g_pGameClientExports; +} + +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +CGameUI &GameUI() +{ + return g_GameUI; +} + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameUI, IGameUI, GAMEUI_INTERFACE_VERSION, g_GameUI); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CGameUI::CGameUI() +{ + g_pGameUI = this; + m_bTryingToLoadFriends = false; + m_iFriendsLoadPauseFrames = 0; + m_iGameIP = 0; + m_iGameConnectionPort = 0; + m_iGameQueryPort = 0; + m_bActivatedUI = false; + m_szPreviousStatusText[0] = 0; + m_bIsConsoleUI = false; + m_bHasSavedThisMenuSession = false; + m_bOpenProgressOnStart = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CGameUI::~CGameUI() +{ + g_pGameUI = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialization +//----------------------------------------------------------------------------- +void CGameUI::Initialize( CreateInterfaceFn factory ) +{ + ConnectTier1Libraries( &factory, 1 ); + ConnectTier2Libraries( &factory, 1 ); + ConVar_Register( FCVAR_CLIENTDLL ); + ConnectTier3Libraries( &factory, 1 ); + + enginesound = (IEngineSound *)factory(IENGINESOUND_CLIENT_INTERFACE_VERSION, NULL); + engine = (IVEngineClient *)factory( VENGINE_CLIENT_INTERFACE_VERSION, NULL ); + + steamapicontext->Init(); + + ConVarRef var( "gameui_xbox" ); + m_bIsConsoleUI = var.IsValid() && var.GetBool(); + + vgui::VGui_InitInterfacesList( "GameUI", &factory, 1 ); + vgui::VGui_InitMatSysInterfacesList( "GameUI", &factory, 1 ); + + // load localization file + g_pVGuiLocalize->AddFile( "Resource/gameui_%language%.txt", "GAME", true ); + + // load mod info + ModInfo().LoadCurrentGameInfo(); + + // load localization file for kb_act.lst + g_pVGuiLocalize->AddFile( "Resource/valve_%language%.txt", "GAME", true ); + + enginevguifuncs = (IEngineVGui *)factory( VENGINE_VGUI_VERSION, NULL ); + enginesurfacefuncs = (vgui::ISurface *)factory(VGUI_SURFACE_INTERFACE_VERSION, NULL); + gameuifuncs = (IGameUIFuncs *)factory( VENGINE_GAMEUIFUNCS_VERSION, NULL ); + matchmaking = (IMatchmaking *)factory( VENGINE_MATCHMAKING_VERSION, NULL ); + xboxsystem = (IXboxSystem *)factory( XBOXSYSTEM_INTERFACE_VERSION, NULL ); + g_pEngineClientReplay = (IEngineClientReplay *)factory( ENGINE_REPLAY_CLIENT_INTERFACE_VERSION, NULL ); + + if ( ModInfo().SupportsVR() && CommandLine()->CheckParm( "-vr" ) ) + { + g_pSourceVR = (ISourceVirtualReality *)factory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL ); + } + + // NOTE: g_pEngineReplay intentionally not checked here + if ( !enginesurfacefuncs || !gameuifuncs || !enginevguifuncs || !xboxsystem || (IsX360() && !matchmaking) ) + { + Error( "CGameUI::Initialize() failed to get necessary interfaces\n" ); + } + + // setup base panel + staticPanel = new CBasePanel(); + staticPanel->SetBounds(0, 0, 400, 300 ); + staticPanel->SetPaintBorderEnabled( false ); + staticPanel->SetPaintBackgroundEnabled( true ); + staticPanel->SetPaintEnabled( false ); + staticPanel->SetVisible( true ); + staticPanel->SetMouseInputEnabled( false ); + staticPanel->SetKeyBoardInputEnabled( false ); + + vgui::VPANEL rootpanel = enginevguifuncs->GetPanel( PANEL_GAMEUIDLL ); + staticPanel->SetParent( rootpanel ); +} + +void CGameUI::PostInit() +{ + if ( IsX360() ) + { + enginesound->PrecacheSound( "UI/buttonrollover.wav", true, true ); + enginesound->PrecacheSound( "UI/buttonclick.wav", true, true ); + enginesound->PrecacheSound( "UI/buttonclickrelease.wav", true, true ); + enginesound->PrecacheSound( "player/suit_denydevice.wav", true, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the specified panel as the background panel for the loading +// dialog. If NULL, default background is used. If you set a panel, +// it should be full-screen with an opaque background, and must be a VGUI popup. +//----------------------------------------------------------------------------- +void CGameUI::SetLoadingBackgroundDialog( vgui::VPANEL panel ) +{ + g_hLoadingBackgroundDialog = panel; +} + +void CGameUI::BonusMapUnlock( const char *pchFileName, const char *pchMapName ) +{ + if ( !pchFileName || pchFileName[ 0 ] == '\0' || + !pchMapName || pchMapName[ 0 ] == '\0' ) + { + if ( !g_pBonusMapsDialog ) + return; + + g_pBonusMapsDialog->SetSelectedBooleanStatus( "lock", false ); + return; + } + + if ( BonusMapsDatabase()->SetBooleanStatus( "lock", pchFileName, pchMapName, false ) ) + { + BonusMapsDatabase()->RefreshMapData(); + + if ( !g_pBonusMapsDialog ) + { + // It unlocked without the bonus maps menu open, so flash the menu item + CBasePanel *pBasePanel = BasePanel(); + if ( pBasePanel ) + { + if ( GameUI().IsConsoleUI() ) + { + if ( Q_stricmp( pchFileName, "scripts/advanced_chambers" ) == 0 ) + { + pBasePanel->SetMenuItemBlinkingState( "OpenNewGameDialog", true ); + } + } + else + { + pBasePanel->SetMenuItemBlinkingState( "OpenBonusMapsDialog", true ); + } + } + + BonusMapsDatabase()->SetBlink( true ); + } + else + g_pBonusMapsDialog->RefreshData(); // Update the open dialog + } +} + +void CGameUI::BonusMapComplete( const char *pchFileName, const char *pchMapName ) +{ + if ( !pchFileName || pchFileName[ 0 ] == '\0' || + !pchMapName || pchMapName[ 0 ] == '\0' ) + { + if ( !g_pBonusMapsDialog ) + return; + + g_pBonusMapsDialog->SetSelectedBooleanStatus( "complete", true ); + BonusMapsDatabase()->RefreshMapData(); + g_pBonusMapsDialog->RefreshData(); + return; + } + + if ( BonusMapsDatabase()->SetBooleanStatus( "complete", pchFileName, pchMapName, true ) ) + { + BonusMapsDatabase()->RefreshMapData(); + + // Update the open dialog + if ( g_pBonusMapsDialog ) + g_pBonusMapsDialog->RefreshData(); + } +} + +void CGameUI::BonusMapChallengeUpdate( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest ) +{ + if ( !pchFileName || pchFileName[ 0 ] == '\0' || + !pchMapName || pchMapName[ 0 ] == '\0' || + !pchChallengeName || pchChallengeName[ 0 ] == '\0' ) + { + return; + } + else + { + if ( BonusMapsDatabase()->UpdateChallengeBest( pchFileName, pchMapName, pchChallengeName, iBest ) ) + { + // The challenge best changed, so write it to the file + BonusMapsDatabase()->WriteSaveData(); + BonusMapsDatabase()->RefreshMapData(); + + // Update the open dialog + if ( g_pBonusMapsDialog ) + g_pBonusMapsDialog->RefreshData(); + } + } +} + +void CGameUI::BonusMapChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName ) +{ + if ( !pchFileName || !pchMapName || !pchChallengeName ) + return; + + BonusMapsDatabase()->GetCurrentChallengeNames( pchFileName, pchMapName, pchChallengeName ); +} + +void CGameUI::BonusMapChallengeObjectives( int &iBronze, int &iSilver, int &iGold ) +{ + BonusMapsDatabase()->GetCurrentChallengeObjectives( iBronze, iSilver, iGold ); +} + +void CGameUI::BonusMapDatabaseSave( void ) +{ + BonusMapsDatabase()->WriteSaveData(); +} + +int CGameUI::BonusMapNumAdvancedCompleted( void ) +{ + return BonusMapsDatabase()->NumAdvancedComplete(); +} + +void CGameUI::BonusMapNumMedals( int piNumMedals[ 3 ] ) +{ + BonusMapsDatabase()->NumMedals( piNumMedals ); +} + +//----------------------------------------------------------------------------- +// Purpose: connects to client interfaces +//----------------------------------------------------------------------------- +void CGameUI::Connect( CreateInterfaceFn gameFactory ) +{ + g_pGameClientExports = (IGameClientExports *)gameFactory(GAMECLIENTEXPORTS_INTERFACE_VERSION, NULL); + + achievementmgr = engine->GetAchievementMgr(); + + if (!g_pGameClientExports) + { + Error("CGameUI::Initialize() failed to get necessary interfaces\n"); + } + + m_GameFactory = gameFactory; +} + +//----------------------------------------------------------------------------- +// Purpose: Callback function; sends platform Shutdown message to specified window +//----------------------------------------------------------------------------- +int __stdcall SendShutdownMsgFunc(WHANDLE hwnd, int lparam) +{ + Sys_PostMessage(hwnd, Sys_RegisterWindowMessage("ShutdownValvePlatform"), 0, 1); + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Searches for GameStartup*.mp3 files in the sound/ui folder and plays one +//----------------------------------------------------------------------------- +void CGameUI::PlayGameStartupSound() +{ + if ( IsX360() ) + return; + + if ( CommandLine()->FindParm( "-nostartupsound" ) ) + return; + + FileFindHandle_t fh; + + CUtlVector<char *> fileNames; + char path[ 512 ]; + + bool bHolidayFound = false; + + // only want to run the holiday check for TF2 + const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" ); + if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) ) + { + // check for a holiday sound file + const char *pszHoliday = NULL; + + if ( GameClientExports() ) + { + pszHoliday = GameClientExports()->GetHolidayString(); + if ( pszHoliday && pszHoliday[0] ) + { + Q_snprintf( path, sizeof( path ), "sound/ui/holiday/gamestartup_%s*.mp3", pszHoliday ); + Q_FixSlashes( path ); + + char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh ); + { + if ( fn ) + { + bHolidayFound = true; + } + } + } + } + } + + // only want to do this if we haven't found a holiday file + if ( !bHolidayFound ) + { + Q_snprintf( path, sizeof( path ), "sound/ui/gamestartup*.mp3" ); + Q_FixSlashes( path ); + } + + char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh ); + if ( fn ) + { + do + { + char ext[ 10 ]; + Q_ExtractFileExtension( fn, ext, sizeof( ext ) ); + + if ( !Q_stricmp( ext, "mp3" ) ) + { + char temp[ 512 ]; + if ( bHolidayFound ) + { + Q_snprintf( temp, sizeof( temp ), "ui/holiday/%s", fn ); + } + else + { + Q_snprintf( temp, sizeof( temp ), "ui/%s", fn ); + } + + char *found = new char[ strlen( temp ) + 1 ]; + Q_strncpy( found, temp, strlen( temp ) + 1 ); + + Q_FixSlashes( found ); + fileNames.AddToTail( found ); + } + + fn = g_pFullFileSystem->FindNext( fh ); + + } while ( fn ); + + g_pFullFileSystem->FindClose( fh ); + } + + // did we find any? + if ( fileNames.Count() > 0 ) + { +#ifdef WIN32 + SYSTEMTIME SystemTime; + GetSystemTime( &SystemTime ); + int index = SystemTime.wMilliseconds % fileNames.Count(); +#else + struct timeval tm; + gettimeofday( &tm, NULL ); + int index = tm.tv_usec/1000 % fileNames.Count(); +#endif + + if ( fileNames.IsValidIndex( index ) && fileNames[index] ) + { + // Play the Saxxy music if we're in saxxy mode. +#if defined( SAXXYMAINMENU_ENABLED ) + bool bIsTF = false; + const char *pGameDir = engine->GetGameDirectory(); + if ( pGameDir ) + { + // Is the game TF? + const int nStrLen = V_strlen( pGameDir ); + bIsTF = nStrLen + && nStrLen >= 2 && + pGameDir[nStrLen-2] == 't' && + pGameDir[nStrLen-1] == 'f'; + } + + // escape chars "*#" make it stream, and be affected by snd_musicvolume + const char *pSoundFile = bIsTF ? "ui/holiday/gamestartup_saxxy.mp3" : fileNames[index]; +#else + const char *pSoundFile = fileNames[index]; +#endif + + char found[ 512 ]; + Q_snprintf( found, sizeof( found ), "play *#%s", pSoundFile ); + + engine->ClientCmd_Unrestricted( found ); + } + + fileNames.PurgeAndDeleteElements(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called to setup the game UI +//----------------------------------------------------------------------------- +void CGameUI::Start() +{ + // determine Steam location for configuration + if ( !FindPlatformDirectory( m_szPlatformDir, sizeof( m_szPlatformDir ) ) ) + return; + + if ( IsPC() ) + { + // setup config file directory + char szConfigDir[512]; + Q_strncpy( szConfigDir, m_szPlatformDir, sizeof( szConfigDir ) ); + Q_strncat( szConfigDir, "config", sizeof( szConfigDir ), COPY_ALL_CHARACTERS ); + + Msg( "Steam config directory: %s\n", szConfigDir ); + + g_pFullFileSystem->AddSearchPath(szConfigDir, "CONFIG"); + g_pFullFileSystem->CreateDirHierarchy("", "CONFIG"); + + // user dialog configuration + vgui::system()->SetUserConfigFile("InGameDialogConfig.vdf", "CONFIG"); + + g_pFullFileSystem->AddSearchPath( "platform", "PLATFORM" ); + } + + // localization + g_pVGuiLocalize->AddFile( "Resource/platform_%language%.txt"); + g_pVGuiLocalize->AddFile( "Resource/vgui_%language%.txt"); + + Sys_SetLastError( SYS_NO_ERROR ); + + if ( IsPC() ) + { + if ( !IsPosix() ) + { + // Alfred says this is really, really old code that does some wacky crap that only + // happened in the first version of HL and it's the only game that does this and + // it was a steam testing type thing and we don't need to do it on Posix, etc. + + g_hMutex = Sys_CreateMutex( "ValvePlatformUIMutex" ); + g_hWaitMutex = Sys_CreateMutex( "ValvePlatformWaitMutex" ); + if ( g_hMutex == 0 || g_hWaitMutex == 0 || Sys_GetLastError() == SYS_ERROR_INVALID_HANDLE ) + { + // error, can't get handle to mutex + if (g_hMutex) + { + Sys_ReleaseMutex(g_hMutex); + } + if (g_hWaitMutex) + { + Sys_ReleaseMutex(g_hWaitMutex); + } + g_hMutex = NULL; + g_hWaitMutex = NULL; + Error("Steam Error: Could not access Steam, bad mutex\n"); + return; + } + unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0); + if (!(waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED)) + { + // mutex locked, need to deactivate Steam (so we have the Friends/ServerBrowser data files) + // get the wait mutex, so that Steam.exe knows that we're trying to acquire ValveTrackerMutex + waitResult = Sys_WaitForSingleObject(g_hWaitMutex, 0); +#ifdef WIN32 + if (waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED) + { + Sys_EnumWindows(SendShutdownMsgFunc, 1); + } +#endif + } + } + + // Delay playing the startup music until the first frame + m_bPlayGameStartupSound = true; + + // now we are set up to check every frame to see if we can friends/server browser + m_bTryingToLoadFriends = true; + m_iFriendsLoadPauseFrames = 1; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Validates the user has a cdkey in the registry +//----------------------------------------------------------------------------- +void CGameUI::ValidateCDKey() +{ + // this check is disabled, since we have no plans for an offline version of hl2 +#if 0 + //!! hack, write out a regkey for now so developers don't have to type it in + //!! undo this before release + vgui::system()->SetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Source\\Settings\\EncryptedCDKey", "QOgi:JXrJj<Eb8abkESf4Pg;OfofJwDzRsyH>AdjtyPnV[FB"); + + // see what's in the registry + if (!CCDKeyEntryDialog::IsValidWeakCDKeyInRegistry()) + { + m_hCDKeyEntryDialog = new CCDKeyEntryDialog(NULL, false); + m_hCDKeyEntryDialog->Activate(); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Finds which directory the platform resides in +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CGameUI::FindPlatformDirectory(char *platformDir, int bufferSize) +{ + platformDir[0] = '\0'; + + if ( platformDir[0] == '\0' ) + { + // we're not under steam, so setup using path relative to game + if ( IsPC() ) + { +#ifdef WIN32 + if ( ::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), platformDir, bufferSize ) ) + { + char *lastslash = strrchr(platformDir, '\\'); // this should be just before the filename + if ( lastslash ) + { + *lastslash = 0; + Q_strncat(platformDir, "\\platform\\", bufferSize, COPY_ALL_CHARACTERS ); + return true; + } + } +#else + if ( getcwd( platformDir, bufferSize ) ) + { + V_AppendSlash( platformDir, bufferSize ); + Q_strncat(platformDir, "platform", bufferSize, COPY_ALL_CHARACTERS ); + V_AppendSlash( platformDir, bufferSize ); + return true; + } +#endif + } + else + { + // xbox fetches the platform path from exisiting platform search path + // path to executeable is not correct for xbox remote configuration + if ( g_pFullFileSystem->GetSearchPath( "PLATFORM", false, platformDir, bufferSize ) ) + { + char *pSeperator = strchr( platformDir, ';' ); + if ( pSeperator ) + *pSeperator = '\0'; + return true; + } + } + + Error( "Unable to determine platform directory\n" ); + return false; + } + + return (platformDir[0] != 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Called to Shutdown the game UI system +//----------------------------------------------------------------------------- +void CGameUI::Shutdown() +{ + // notify all the modules of Shutdown + g_VModuleLoader.ShutdownPlatformModules(); + + // unload the modules them from memory + g_VModuleLoader.UnloadPlatformModules(); + + ModInfo().FreeModInfo(); + + // release platform mutex + // close the mutex + if (g_hMutex) + { + Sys_ReleaseMutex(g_hMutex); + } + if (g_hWaitMutex) + { + Sys_ReleaseMutex(g_hWaitMutex); + } + + BonusMapsDatabase()->WriteSaveData(); + + steamapicontext->Clear(); + + + ConVar_Unregister(); + DisconnectTier3Libraries(); + DisconnectTier2Libraries(); + DisconnectTier1Libraries(); +} + +//----------------------------------------------------------------------------- +// Purpose: just wraps an engine call to activate the gameUI +//----------------------------------------------------------------------------- +void CGameUI::ActivateGameUI() +{ + engine->ExecuteClientCmd("gameui_activate"); +} + +//----------------------------------------------------------------------------- +// Purpose: just wraps an engine call to hide the gameUI +//----------------------------------------------------------------------------- +void CGameUI::HideGameUI() +{ + engine->ExecuteClientCmd("gameui_hide"); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle allowing the engine to hide the game UI with the escape key +//----------------------------------------------------------------------------- +void CGameUI::PreventEngineHideGameUI() +{ + engine->ExecuteClientCmd("gameui_preventescape"); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle allowing the engine to hide the game UI with the escape key +//----------------------------------------------------------------------------- +void CGameUI::AllowEngineHideGameUI() +{ + engine->ExecuteClientCmd("gameui_allowescape"); +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the game UI +//----------------------------------------------------------------------------- +void CGameUI::OnGameUIActivated() +{ + m_bActivatedUI = true; + + // hide/show the main panel to Activate all game ui + staticPanel->SetVisible( true ); + + // pause the server in single player + if ( engine->GetMaxClients() <= 1 ) + { + engine->ClientCmd_Unrestricted( "setpause" ); + } + + SetSavedThisMenuSession( false ); + + // notify taskbar + BasePanel()->OnGameUIActivated(); + + if ( GameClientExports() ) + { + const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" ); + // only want to run this for TF2 + if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) ) + { + GameClientExports()->OnGameUIActivated(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hides the game ui, in whatever state it's in +//----------------------------------------------------------------------------- +void CGameUI::OnGameUIHidden() +{ + if ( GameClientExports() ) + { + const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" ); + // only want to run this for TF2 + if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) ) + { + GameClientExports()->OnGameUIHidden(); + } + } + + // unpause the game when leaving the UI + if ( engine->GetMaxClients() <= 1 ) + { + engine->ClientCmd_Unrestricted("unpause"); + } + + BasePanel()->OnGameUIHidden(); +} + +//----------------------------------------------------------------------------- +// Purpose: paints all the vgui elements +//----------------------------------------------------------------------------- +void CGameUI::RunFrame() +{ + if ( IsX360() && m_bOpenProgressOnStart ) + { + StartProgressBar(); + m_bOpenProgressOnStart = false; + } + + // resize the background panel to the screen size + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + staticPanel->SetSize(wide,tall); + + // Run frames + g_VModuleLoader.RunFrame(); + BasePanel()->RunFrame(); + + // Play the start-up music the first time we run frame + if ( IsPC() && m_bPlayGameStartupSound ) + { + PlayGameStartupSound(); + m_bPlayGameStartupSound = false; + } + + if ( IsPC() && ( ( IsPosix() && m_bTryingToLoadFriends ) || + ( m_bTryingToLoadFriends && m_iFriendsLoadPauseFrames-- < 1 && g_hMutex && g_hWaitMutex ) ) ) + { + // try and load Steam platform files + unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0); + if ( IsPosix() || ( waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED )) + { + // we got the mutex, so load Friends/Serverbrowser + // clear the loading flag + m_bTryingToLoadFriends = false; + g_VModuleLoader.LoadPlatformModules(&m_GameFactory, 1, false); + + // release the wait mutex + if ( !IsPosix() ) + Sys_ReleaseMutex(g_hWaitMutex); + + // notify the game of our game name + const char *fullGamePath = engine->GetGameDirectory(); + const char *pathSep = strrchr( fullGamePath, '/' ); + if ( !pathSep ) + { + pathSep = strrchr( fullGamePath, '\\' ); + } + if ( pathSep ) + { + KeyValues *pKV = new KeyValues("ActiveGameName" ); + pKV->SetString( "name", pathSep + 1 ); + pKV->SetInt( "appid", engine->GetAppID() ); + KeyValues *modinfo = new KeyValues("ModInfo"); + if ( modinfo->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) ) + { + pKV->SetString( "game", modinfo->GetString( "game", "" ) ); + } + modinfo->deleteThis(); + + g_VModuleLoader.PostMessageToAllModules( pKV ); + } + + // notify the ui of a game connect if we're already in a game + if (m_iGameIP) + { + SendConnectedToGameMessage(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the game connects to a server +//----------------------------------------------------------------------------- +void CGameUI::OLD_OnConnectToServer(const char *game, int IP, int port) +{ + // Nobody should use this anymore because the query port and the connection port can be different. + // Use OnConnectToServer2 instead. + Assert( false ); + OnConnectToServer2( game, IP, port, port ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the game connects to a server +//----------------------------------------------------------------------------- +void CGameUI::OnConnectToServer2(const char *game, int IP, int connectionPort, int queryPort) +{ + m_iGameIP = IP; + m_iGameConnectionPort = connectionPort; + m_iGameQueryPort = queryPort; + + SendConnectedToGameMessage(); +} + + +void CGameUI::SendConnectedToGameMessage() +{ + KeyValues *kv = new KeyValues( "ConnectedToGame" ); + kv->SetInt( "ip", m_iGameIP ); + kv->SetInt( "connectionport", m_iGameConnectionPort ); + kv->SetInt( "queryport", m_iGameQueryPort ); + + g_VModuleLoader.PostMessageToAllModules( kv ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the game disconnects from a server +//----------------------------------------------------------------------------- +void CGameUI::OnDisconnectFromServer( uint8 eSteamLoginFailure ) +{ + m_iGameIP = 0; + m_iGameConnectionPort = 0; + m_iGameQueryPort = 0; + + g_VModuleLoader.PostMessageToAllModules(new KeyValues("DisconnectedFromGame")); + + if ( eSteamLoginFailure == STEAMLOGINFAILURE_NOSTEAMLOGIN ) + { + if ( g_hLoadingDialog ) + { + g_hLoadingDialog->DisplayNoSteamConnectionError(); + } + } + else if ( eSteamLoginFailure == STEAMLOGINFAILURE_VACBANNED ) + { + if ( g_hLoadingDialog ) + { + g_hLoadingDialog->DisplayVACBannedError(); + } + } + else if ( eSteamLoginFailure == STEAMLOGINFAILURE_LOGGED_IN_ELSEWHERE ) + { + if ( g_hLoadingDialog ) + { + g_hLoadingDialog->DisplayLoggedInElsewhereError(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: activates the loading dialog on level load start +//----------------------------------------------------------------------------- +void CGameUI::OnLevelLoadingStarted( bool bShowProgressDialog ) +{ + g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingStarted" ) ); + + // notify + BasePanel()->OnLevelLoadingStarted(); + + if ( bShowProgressDialog ) + { + StartProgressBar(); + } + + // Don't play the start game sound if this happens before we get to the first frame + m_bPlayGameStartupSound = false; +} + +//----------------------------------------------------------------------------- +// Purpose: closes any level load dialog +//----------------------------------------------------------------------------- +void CGameUI::OnLevelLoadingFinished(bool bError, const char *failureReason, const char *extendedReason) +{ + StopProgressBar( bError, failureReason, extendedReason ); + + // notify all the modules + g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingFinished" ) ); + + // hide the UI + HideGameUI(); + + // notify + BasePanel()->OnLevelLoadingFinished(); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates progress bar +// Output : Returns true if screen should be redrawn +//----------------------------------------------------------------------------- +bool CGameUI::UpdateProgressBar(float progress, const char *statusText) +{ + // if either the progress bar or the status text changes, redraw the screen + bool bRedraw = false; + + if ( ContinueProgressBar( progress ) ) + { + bRedraw = true; + } + + if ( SetProgressBarStatusText( statusText ) ) + { + bRedraw = true; + } + + return bRedraw; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameUI::StartProgressBar() +{ + if ( !g_hLoadingDialog.Get() ) + { + g_hLoadingDialog = new CLoadingDialog(staticPanel); + } + + // open a loading dialog + m_szPreviousStatusText[0] = 0; + g_hLoadingDialog->SetProgressPoint(0.0f); + g_hLoadingDialog->Open(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the screen should be updated +//----------------------------------------------------------------------------- +bool CGameUI::ContinueProgressBar( float progressFraction ) +{ + if (!g_hLoadingDialog.Get()) + return false; + + g_hLoadingDialog->Activate(); + return g_hLoadingDialog->SetProgressPoint(progressFraction); +} + +//----------------------------------------------------------------------------- +// Purpose: stops progress bar, displays error if necessary +//----------------------------------------------------------------------------- +void CGameUI::StopProgressBar(bool bError, const char *failureReason, const char *extendedReason) +{ + if (!g_hLoadingDialog.Get() && bError) + { + g_hLoadingDialog = new CLoadingDialog(staticPanel); + } + + if (!g_hLoadingDialog.Get()) + return; + + if ( !IsX360() && bError ) + { + // turn the dialog to error display mode + g_hLoadingDialog->DisplayGenericError(failureReason, extendedReason); + } + else + { + // close loading dialog + g_hLoadingDialog->Close(); + g_hLoadingDialog = NULL; + } + // should update the background to be in a transition here +} + +//----------------------------------------------------------------------------- +// Purpose: sets loading info text +//----------------------------------------------------------------------------- +bool CGameUI::SetProgressBarStatusText(const char *statusText) +{ + if (!g_hLoadingDialog.Get()) + return false; + + if (!statusText) + return false; + + if (!stricmp(statusText, m_szPreviousStatusText)) + return false; + + g_hLoadingDialog->SetStatusText(statusText); + Q_strncpy(m_szPreviousStatusText, statusText, sizeof(m_szPreviousStatusText)); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameUI::SetSecondaryProgressBar(float progress /* range [0..1] */) +{ + if (!g_hLoadingDialog.Get()) + return; + + g_hLoadingDialog->SetSecondaryProgress(progress); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameUI::SetSecondaryProgressBarText(const char *statusText) +{ + if (!g_hLoadingDialog.Get()) + return; + + g_hLoadingDialog->SetSecondaryProgressText(statusText); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns prev settings +//----------------------------------------------------------------------------- +bool CGameUI::SetShowProgressText( bool show ) +{ + if (!g_hLoadingDialog.Get()) + return false; + + return g_hLoadingDialog->SetShowProgressText( show ); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're currently playing the game +//----------------------------------------------------------------------------- +bool CGameUI::IsInLevel() +{ + const char *levelName = engine->GetLevelName(); + if (levelName && levelName[0] && !engine->IsLevelMainMenuBackground()) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're at the main menu and a background level is loaded +//----------------------------------------------------------------------------- +bool CGameUI::IsInBackgroundLevel() +{ + const char *levelName = engine->GetLevelName(); + if (levelName && levelName[0] && engine->IsLevelMainMenuBackground()) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're in a multiplayer game +//----------------------------------------------------------------------------- +bool CGameUI::IsInMultiplayer() +{ + return (IsInLevel() && engine->GetMaxClients() > 1); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're playing back a replay +//----------------------------------------------------------------------------- +bool CGameUI::IsInReplay() +{ + return g_pEngineClientReplay && g_pEngineClientReplay->IsPlayingReplayDemo(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're console ui +//----------------------------------------------------------------------------- +bool CGameUI::IsConsoleUI() +{ + return m_bIsConsoleUI; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we've saved without closing the menu +//----------------------------------------------------------------------------- +bool CGameUI::HasSavedThisMenuSession() +{ + return m_bHasSavedThisMenuSession; +} + +void CGameUI::SetSavedThisMenuSession( bool bState ) +{ + m_bHasSavedThisMenuSession = bState; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameUI::ShowNewGameDialog( int chapter ) +{ + char val[32]; + Q_snprintf( val, sizeof(val), "%d", chapter); + staticPanel->OnOpenNewGameDialog(val); +} + +//----------------------------------------------------------------------------- +// Purpose: Makes the loading background dialog visible, if one has been set +//----------------------------------------------------------------------------- +void CGameUI::ShowLoadingBackgroundDialog() +{ + if ( g_hLoadingBackgroundDialog ) + { + vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, staticPanel->GetVPanel() ); + vgui::ipanel()->PerformApplySchemeSettings( g_hLoadingBackgroundDialog ); + vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, true ); + vgui::ipanel()->MoveToFront( g_hLoadingBackgroundDialog ); + vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "activate" ), staticPanel->GetVPanel() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hides the loading background dialog, if one has been set +//----------------------------------------------------------------------------- +void CGameUI::HideLoadingBackgroundDialog() +{ + if ( g_hLoadingBackgroundDialog ) + { + vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, NULL ); + vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, false ); + vgui::ipanel()->MoveToBack( g_hLoadingBackgroundDialog ); + vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "deactivate" ), staticPanel->GetVPanel() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether a loading background dialog has been set +//----------------------------------------------------------------------------- +bool CGameUI::HasLoadingBackgroundDialog() +{ + return ( 0 != g_hLoadingBackgroundDialog ); +} + +//----------------------------------------------------------------------------- +// Purpose: Xbox 360 calls from engine to GameUI +//----------------------------------------------------------------------------- +void CGameUI::SessionNotification( const int notification, const int param ) +{ + BasePanel()->SessionNotification( notification, param ); +} +void CGameUI::SystemNotification( const int notification ) +{ + BasePanel()->SystemNotification( notification ); +} +void CGameUI::ShowMessageDialog( const uint nType, vgui::Panel *pOwner ) +{ + BasePanel()->ShowMessageDialog( nType, pOwner ); +} +void CGameUI::CloseMessageDialog( const uint nType ) +{ + BasePanel()->CloseMessageDialog( nType ); +} +void CGameUI::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ) +{ + BasePanel()->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost ); +} +void CGameUI::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ) +{ + BasePanel()->SessionSearchResult( searchIdx, pHostData, pResult, ping ); +} +void CGameUI::OnCreditsFinished( void ) +{ + BasePanel()->OnCreditsFinished(); +} +bool CGameUI::ValidateStorageDevice( int *pStorageDeviceValidated ) +{ + return BasePanel()->ValidateStorageDevice( pStorageDeviceValidated ); +} + +void CGameUI::SetProgressOnStart() +{ + m_bOpenProgressOnStart = true; +} + +void CGameUI::OnConfirmQuit( void ) +{ + BasePanel()->OnOpenQuitConfirmationDialog(); +} + +bool CGameUI::IsMainMenuVisible( void ) +{ + CBasePanel *pBasePanel = BasePanel(); + if ( pBasePanel ) + return (pBasePanel->IsVisible() && pBasePanel->GetMenuAlpha() > 0 ); + return false; +} + +// Client DLL is providing us with a panel that it wants to replace the main menu with +void CGameUI::SetMainMenuOverride( vgui::VPANEL panel ) +{ + CBasePanel *pBasePanel = BasePanel(); + if ( pBasePanel ) + { + pBasePanel->SetMainMenuOverride( panel ); + } +} + +// Client DLL is telling us that a main menu command was issued, probably from its custom main menu panel +void CGameUI::SendMainMenuCommand( const char *pszCommand ) +{ + CBasePanel *pBasePanel = BasePanel(); + if ( pBasePanel ) + { + pBasePanel->RunMenuCommand( pszCommand ); + } +}
\ No newline at end of file diff --git a/gameui/GameUI_Interface.h b/gameui/GameUI_Interface.h new file mode 100644 index 0000000..cee5c77 --- /dev/null +++ b/gameui/GameUI_Interface.h @@ -0,0 +1,159 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines the interface that the GameUI dll exports +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef GAMEUI_INTERFACE_H +#define GAMEUI_INTERFACE_H +#pragma once + +#include "GameUI/IGameUI.h" + +#include <vgui_controls/Panel.h> +#include <vgui_controls/PHandle.h> + +class IGameClientExports; + +//----------------------------------------------------------------------------- +// Purpose: Implementation of GameUI's exposed interface +//----------------------------------------------------------------------------- +class CGameUI : public IGameUI +{ +public: + CGameUI(); + ~CGameUI(); + + virtual void Initialize( CreateInterfaceFn appFactory ); + virtual void Connect( CreateInterfaceFn gameFactory ); + virtual void Start(); + virtual void Shutdown(); + virtual void RunFrame(); + virtual void PostInit(); + + // plays the startup mp3 when GameUI starts + void PlayGameStartupSound(); + + // Engine wrappers for activating / hiding the gameUI + void ActivateGameUI(); + void HideGameUI(); + + // Toggle allowing the engine to hide the game UI with the escape key + void PreventEngineHideGameUI(); + void AllowEngineHideGameUI(); + + virtual void SetLoadingBackgroundDialog( vgui::VPANEL panel ); + + // Bonus maps interfaces + virtual void BonusMapUnlock( const char *pchFileName = NULL, const char *pchMapName = NULL ); + virtual void BonusMapComplete( const char *pchFileName = NULL, const char *pchMapName = NULL ); + virtual void BonusMapChallengeUpdate( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest ); + virtual void BonusMapChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName ); + virtual void BonusMapChallengeObjectives( int &iBronze, int &iSilver, int &iGold ); + virtual void BonusMapDatabaseSave( void ); + virtual int BonusMapNumAdvancedCompleted( void ); + virtual void BonusMapNumMedals( int piNumMedals[ 3 ] ); + + // notifications + virtual void OnGameUIActivated(); + virtual void OnGameUIHidden(); + virtual void OLD_OnConnectToServer( const char *game, int IP, int port ); // OLD: use OnConnectToServer2 + virtual void OnConnectToServer2( const char *game, int IP, int connectionPort, int queryPort ); + virtual void OnDisconnectFromServer( uint8 eSteamLoginFailure ); + virtual void OnLevelLoadingStarted( bool bShowProgressDialog ); + virtual void OnLevelLoadingFinished( bool bError, const char *failureReason, const char *extendedReason ); + virtual void OnDisconnectFromServer_OLD( uint8 eSteamLoginFailure, const char *username ) { OnDisconnectFromServer( eSteamLoginFailure ); } + + // progress + virtual bool UpdateProgressBar(float progress, const char *statusText); + // Shows progress desc, returns previous setting... (used with custom progress bars ) + virtual bool SetShowProgressText( bool show ); + + // brings up the new game dialog + virtual void ShowNewGameDialog( int chapter ); + + // Xbox 360 + virtual void SessionNotification( const int notification, const int param = 0 ); + virtual void SystemNotification( const int notification ); + virtual void ShowMessageDialog( const uint nType, vgui::Panel *pOwner = NULL ); + virtual void CloseMessageDialog( const uint nType = 0 ); + virtual void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ); + virtual void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ); + virtual void OnCreditsFinished( void ); + + // X360 Storage device validation: + // returns true right away if storage device has been previously selected. + // otherwise returns false and will set the variable pointed by pStorageDeviceValidated to 1 + // once the storage device is selected by user. + virtual bool ValidateStorageDevice( int *pStorageDeviceValidated ); + + virtual void SetProgressOnStart(); + + virtual void OnConfirmQuit( void ); + + virtual bool IsMainMenuVisible( void ); + + // Client DLL is providing us with a panel that it wants to replace the main menu with + virtual void SetMainMenuOverride( vgui::VPANEL panel ); + // Client DLL is telling us that a main menu command was issued, probably from its custom main menu panel + virtual void SendMainMenuCommand( const char *pszCommand ); + + // state + bool IsInLevel(); + bool IsInBackgroundLevel(); + bool IsInMultiplayer(); + bool IsInReplay(); + bool IsConsoleUI(); + bool HasSavedThisMenuSession(); + void SetSavedThisMenuSession( bool bState ); + + void ShowLoadingBackgroundDialog(); + void HideLoadingBackgroundDialog(); + bool HasLoadingBackgroundDialog(); + +private: + void SendConnectedToGameMessage(); + + virtual void StartProgressBar(); + virtual bool ContinueProgressBar(float progressFraction); + virtual void StopProgressBar(bool bError, const char *failureReason, const char *extendedReason = NULL); + virtual bool SetProgressBarStatusText(const char *statusText); + + //!! these functions currently not implemented + virtual void SetSecondaryProgressBar(float progress /* range [0..1] */); + virtual void SetSecondaryProgressBarText(const char *statusText); + + bool FindPlatformDirectory(char *platformDir, int bufferSize); + void GetUpdateVersion( char *pszProd, char *pszVer); + void ValidateCDKey(); + + CreateInterfaceFn m_GameFactory; + + bool m_bPlayGameStartupSound : 1; + bool m_bTryingToLoadFriends : 1; + bool m_bActivatedUI : 1; + bool m_bIsConsoleUI : 1; + bool m_bHasSavedThisMenuSession : 1; + bool m_bOpenProgressOnStart : 1; + + int m_iGameIP; + int m_iGameConnectionPort; + int m_iGameQueryPort; + + int m_iFriendsLoadPauseFrames; + + char m_szPreviousStatusText[128]; + char m_szPlatformDir[MAX_PATH]; + + vgui::DHANDLE<class CCDKeyEntryDialog> m_hCDKeyEntryDialog; +}; + +// Purpose: singleton accessor +extern CGameUI &GameUI(); + +// expose client interface +extern IGameClientExports *GameClientExports(); + + +#endif // GAMEUI_INTERFACE_H diff --git a/gameui/HapticControlBox.cpp b/gameui/HapticControlBox.cpp new file mode 100644 index 0000000..10a240f --- /dev/null +++ b/gameui/HapticControlBox.cpp @@ -0,0 +1,191 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#include "HapticControlBox.h" +#include <vgui_controls/Controls.h> +#include <vgui_controls/Panel.h> +#include "cvarslider.h" +#include "mathlib/vmatrix.h" +#include <vgui/ISurface.h> +#include "KeyValues.h" +#include <vgui/ISystem.h> +ControlBoxVisual::ControlBoxVisual(vgui::Panel *parent,const char *panelName, CCvarSlider *n, CCvarSlider *r, CCvarSlider *u, CCvarSlider *f, CCvarSlider *l, CCvarSlider *d) : +BaseClass(parent,panelName) +{ + m_iMouseOver = -1; + m_flTime=0; + + //disable mouse input. + SetMouseInputEnabled(false); + + SlideValues = new CCvarSliderCube(n, r, u, f, l, d); + + SlideValues->Down->AddActionSignalTarget(GetVPanel()); + SlideValues->Far->AddActionSignalTarget(GetVPanel()); + SlideValues->Up->AddActionSignalTarget(GetVPanel()); + SlideValues->Near->AddActionSignalTarget(GetVPanel()); + SlideValues->Left->AddActionSignalTarget(GetVPanel()); + SlideValues->Right->AddActionSignalTarget(GetVPanel()); + SetWide(64); + SetTall(64); +} + +void ControlBoxVisual::OnSlideEnter(KeyValues*data) +{ + vgui::VPANEL fromPanel = data->GetInt("VPANEL"); + if(fromPanel == SlideValues->Up->GetVPanel()) + m_iMouseOver = HUI_BOX_UP; + else if(fromPanel == SlideValues->Down->GetVPanel()) + m_iMouseOver = HUI_BOX_DOWN; + else if(fromPanel == SlideValues->Right->GetVPanel()) + m_iMouseOver = HUI_BOX_RIGHT; + else if(fromPanel == SlideValues->Left->GetVPanel()) + m_iMouseOver = HUI_BOX_LEFT; + else if(fromPanel == SlideValues->Far->GetVPanel()) + m_iMouseOver = HUI_BOX_FAR; + else if(fromPanel == SlideValues->Near->GetVPanel()) + m_iMouseOver = HUI_BOX_NEAR; +} +void ControlBoxVisual::OnSlideExit(KeyValues*data) +{ + //vgui::VPANEL fromPanel = data->GetInt("VPANEL"); + m_iMouseOver=-1; +} +void ControlBoxVisual::Paint() +{ + m_flTime = vgui::system()->GetFrameTime(); + BaseClass::Paint(); + vgui::surface()->DrawSetColor(0,0,0,255); + DrawCube(); + vgui::surface()->DrawSetColor(255,255,255,255); + + //first draw the cube once. + DrawCube( SlideValues->Near->GetSliderValue()*-1, + SlideValues->Right->GetSliderValue()*-1, + SlideValues->Up->GetSliderValue()*-1, + SlideValues->Far->GetSliderValue(), + SlideValues->Left->GetSliderValue(), + SlideValues->Down->GetSliderValue()); + //then check if we have something selected + if(m_iMouseOver!=-1) + { + vgui::surface()->DrawSetColor(255,0,0,255); + // if we do, draw a special cube. + DrawCube( SlideValues->Near->GetSliderValue()*-1, + SlideValues->Right->GetSliderValue()*-1, + SlideValues->Up->GetSliderValue()*-1, + SlideValues->Far->GetSliderValue(), + SlideValues->Left->GetSliderValue(), + SlideValues->Down->GetSliderValue(), + m_iMouseOver); + + + } +} + +void ControlBoxVisual::DrawCube(float n, float r, float u, float f, float l, float d, int specialside) +{ + + + l*=-1; + r*=-1;//flip + + + + Vector right[4]; + //right side + right[0]= Vector(f,r,d); + right[1]= Vector(f,r,u); + right[2]= Vector(n,r,u); + right[3]= Vector(n,r,d); + + Vector left[4]; + //left side + left[0]= Vector(f,l,d); + left[1]= Vector(f,l,u); + left[2]= Vector(n,l,u); + left[3]= Vector(n,l,d); + //yikes, this is kind of alot of typing. + switch(specialside) + { + case HUI_BOX_UP: + left[0] = left[1]; + left[3] = left[2]; + left[1] =Vector(1,-1,-1); + left[2] =Vector(-1,-1,-1); + right[0]= right[1]; + right[3]= right[2]; + right[1]=Vector(1,1,-1); + right[2]=Vector(-1,1,-1); + break; + case HUI_BOX_DOWN: + left[1] = left[0]; + left[2] = left[3]; + left[0] =Vector(1,-1,1); + left[3] =Vector(-1,-1,1); + right[1] = right[0]; + right[2] = right[3]; + right[0] =Vector(1,1,1); + right[3] =Vector(-1,1,1); + break; + case HUI_BOX_LEFT: + right[0] = left[0]; + right[1] = left[1]; + right[2] = left[2]; + right[3] = left[3]; + left[0] =Vector(1,-1,1); + left[1] =Vector(1,-1,-1); + left[2] =Vector(-1,-1,-1); + left[3] =Vector(-1,-1,1); + break; + case HUI_BOX_RIGHT: + left[0] = right[0]; + left[1] = right[1]; + left[2] = right[2]; + left[3] = right[3]; + right[0] =Vector(1,1,1); + right[1] =Vector(1,1,-1); + right[2] =Vector(-1,1,-1); + right[3] =Vector(-1,1,1); + break; + case HUI_BOX_FAR: + left[3] = left[0]; + left[2] = left[1]; + left[0] =Vector(1,-1,1); + left[1] =Vector(1,-1,-1); + right[3] = right[0]; + right[2] = right[1]; + right[0] =Vector(1,1,1); + right[1] =Vector(1,1,-1); + break; + case HUI_BOX_NEAR: + left[0] = left[3]; + left[1] = left[2]; + left[3] =Vector(-1,-1,1); + left[2] =Vector(-1,-1,-1); + right[0] = right[3]; + right[1] = right[2]; + right[3] =Vector(-1,1,1); + right[2] =Vector(-1,1,-1); + break; + default: + break; + } + + Vector pos = Vector(0,0.25 + sin(m_flTime),0.5); + VMatrix Project = SetupMatrixProjection(pos,VPlane(Vector(1,0,0),-3)); + Vector vdrawsize = Vector(1,(float)GetWide()/10.0f,(float)GetTall()/10.0f); + for(int i=0;i!=4;i++) + { + right[i] = Project.VMul3x3(right[i])*vdrawsize; + left[i] = Project.VMul3x3(left[i])*vdrawsize; + } + vdrawsize *=5; + for(int i = 0;i!=4;i++) + { + int next = i+1; + if(next>3) + next = 0; + vgui::surface()->DrawLine(vdrawsize.y+left[i].y,vdrawsize.z+left[i].z,vdrawsize.y+left[next].y,vdrawsize.z+left[next].z); + vgui::surface()->DrawLine(vdrawsize.y+right[i].y,vdrawsize.z+right[i].z,vdrawsize.y+right[next].y,vdrawsize.z+right[next].z); + vgui::surface()->DrawLine(vdrawsize.y+left[i].y,vdrawsize.z+left[i].z,vdrawsize.y+right[i].y,vdrawsize.z+right[i].z); + } +}
\ No newline at end of file diff --git a/gameui/HapticControlBox.h b/gameui/HapticControlBox.h new file mode 100644 index 0000000..37073a9 --- /dev/null +++ b/gameui/HapticControlBox.h @@ -0,0 +1,53 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifndef HAPTICCONTROLBOX_H +#define HAPTICCONTROLBOX_H + +#include <vgui_controls/Controls.h> +#include <vgui_controls/Panel.h> +#include "cvarslider.h" +class ControlBoxVisual : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE(ControlBoxVisual,vgui::Panel); +public: + ControlBoxVisual(vgui::Panel *parent, const char *panelName, CCvarSlider *near, CCvarSlider *right, CCvarSlider *up, CCvarSlider *far, CCvarSlider *left, CCvarSlider *down); + virtual void Paint(); + MESSAGE_FUNC_PARAMS(OnSlideEnter, "CursorEnteredSlider", data); + MESSAGE_FUNC_PARAMS(OnSlideExit, "CursorExitedSlider", data); +protected: + void DrawCube(float Near=-1, float Right=-1, float Up=-1, float Far=1, float Left=1, float Down=1, int specialside=-1); + enum eBoxID + { + HUI_BOX_UP =0, + HUI_BOX_RIGHT, + HUI_BOX_NEAR, + HUI_BOX_DOWN, + HUI_BOX_LEFT, + HUI_BOX_FAR, + HUI_BOX_SLIDERCOUNT, + }; + + struct CCvarSliderCube + { + CCvarSliderCube(CCvarSlider *n,CCvarSlider *r,CCvarSlider *u,CCvarSlider *f,CCvarSlider *l,CCvarSlider *d) + { + Near = n; + Right = r; + Up = u; + Far = f; + Left = l; + Down = d; + }; + CCvarSlider *Near; + CCvarSlider *Right; + CCvarSlider *Up; + CCvarSlider *Far; + CCvarSlider *Left; + CCvarSlider *Down; + }; + + CCvarSliderCube *SlideValues;// up right near down left far and spingk + int m_iMouseOver; + float m_flTime; +}; + +#endif
\ No newline at end of file diff --git a/gameui/KeyToggleCheckButton.cpp b/gameui/KeyToggleCheckButton.cpp new file mode 100644 index 0000000..de13117 --- /dev/null +++ b/gameui/KeyToggleCheckButton.cpp @@ -0,0 +1,118 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "KeyToggleCheckButton.h" +#include "EngineInterface.h" +#include <vgui/IVGui.h> +#include "IGameUIFuncs.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +CKeyToggleCheckButton::CKeyToggleCheckButton( Panel *parent, const char *panelName, const char *text, + char const *key, char const *cmdname ) + : CheckButton( parent, panelName, text ) +{ + m_pszKeyName = key ? strdup( key ) : NULL; + m_pszCmdName = cmdname ? strdup( cmdname ) : NULL; + + if (m_pszKeyName) + { + Reset(); + } + //m_bNoCommand = false; +} + +CKeyToggleCheckButton::~CKeyToggleCheckButton() +{ + free( m_pszKeyName ); + free( m_pszCmdName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyToggleCheckButton::Paint() +{ + BaseClass::Paint(); + + if ( !m_pszKeyName ) + return; + + // Fixme, look up key state + bool isdown; + if ( gameuifuncs->IsKeyDown( m_pszKeyName, isdown ) ) + { + // if someone changed the value using the consoel + if ( m_bStartValue != isdown ) + { + SetSelected( isdown ); + m_bStartValue = isdown; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *panel - +//----------------------------------------------------------------------------- +/* +void CKeyToggleCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); + + if ( !m_pszCmdName || !m_pszCmdName[ 0 ] ) + return; + + if ( m_bNoCommand ) + return; + + char szCommand[ 256 ]; + + Q_snprintf( szCommand, sizeof( szCommand ), "%c%s\n", IsSelected() ? '+' : '-', + m_pszCmdName ); + + engine->pfnClientCmd( szCommand ); +}*/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyToggleCheckButton::Reset() +{ + gameuifuncs->IsKeyDown( m_pszKeyName, m_bStartValue ); + if ( IsSelected() != m_bStartValue) + { + SetSelected( m_bStartValue ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyToggleCheckButton::ApplyChanges() +{ + if ( !m_pszCmdName || !m_pszCmdName[ 0 ] ) + return; + + char szCommand[ 256 ]; + + Q_snprintf( szCommand, sizeof( szCommand ), "%c%s\n", IsSelected() ? '+' : '-', + m_pszCmdName ); + + engine->ClientCmd_Unrestricted( szCommand ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CKeyToggleCheckButton::HasBeenModified() +{ + return IsSelected() != m_bStartValue; +}
\ No newline at end of file diff --git a/gameui/KeyToggleCheckButton.h b/gameui/KeyToggleCheckButton.h new file mode 100644 index 0000000..0160df9 --- /dev/null +++ b/gameui/KeyToggleCheckButton.h @@ -0,0 +1,38 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef KEYTOGGLECHECKBUTTON_H +#define KEYTOGGLECHECKBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/CheckButton.h> + +class CKeyToggleCheckButton : public vgui::CheckButton +{ +public: + CKeyToggleCheckButton( vgui::Panel *parent, const char *panelName, const char *text, + char const *keyname, char const *cmdname ); + ~CKeyToggleCheckButton(); + + //virtual void SetSelected( bool state ); + virtual void Paint(); + + void Reset(); + void ApplyChanges(); + bool HasBeenModified(); + +private: + typedef vgui::CheckButton BaseClass; + + char *m_pszKeyName; + char *m_pszCmdName; + + bool m_bStartValue; +}; +#endif // KEYTOGGLECHECKBUTTON_H diff --git a/gameui/LabeledCommandComboBox.cpp b/gameui/LabeledCommandComboBox.cpp new file mode 100644 index 0000000..020f6be --- /dev/null +++ b/gameui/LabeledCommandComboBox.cpp @@ -0,0 +1,131 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "LabeledCommandComboBox.h" +#include "EngineInterface.h" +#include <KeyValues.h> +#include <vgui/ILocalize.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +CLabeledCommandComboBox::CLabeledCommandComboBox( vgui::Panel *parent, const char *panelName ) : vgui::ComboBox( parent, panelName, 6, false ) +{ + AddActionSignalTarget(this); + m_iCurrentSelection = -1; + m_iStartSelection = -1; +} + +CLabeledCommandComboBox::~CLabeledCommandComboBox( void ) +{ +} + +void CLabeledCommandComboBox::DeleteAllItems() +{ + BaseClass::DeleteAllItems(); + m_Items.RemoveAll(); +} + +void CLabeledCommandComboBox::AddItem( char const *text, char const *engineCommand ) +{ + int idx = m_Items.AddToTail(); + COMMANDITEM *item = &m_Items[ idx ]; + + item->comboBoxID = BaseClass::AddItem( text, NULL ); + + Q_strncpy( item->name, text, sizeof( item->name ) ); + + if (text[0] == '#') + { + // need to localize the string + wchar_t *localized = g_pVGuiLocalize->Find(text); + if (localized) + { + g_pVGuiLocalize->ConvertUnicodeToANSI(localized, item->name, sizeof(item->name)); + } + } + + Q_strncpy( item->command, engineCommand, sizeof( item->command ) ); +} + +void CLabeledCommandComboBox::ActivateItem(int index) +{ + if ( index< m_Items.Count() ) + { + int comboBoxID = m_Items[index].comboBoxID; + BaseClass::ActivateItem(comboBoxID); + m_iCurrentSelection = index; + } +} + +void CLabeledCommandComboBox::SetInitialItem(int index) +{ + if ( index< m_Items.Count() ) + { + m_iStartSelection = index; + int comboBoxID = m_Items[index].comboBoxID; + ActivateItem(comboBoxID); + } +} + +void CLabeledCommandComboBox::OnTextChanged( char const *text ) +{ + int i; + for ( i = 0; i < m_Items.Size(); i++ ) + { + COMMANDITEM *item = &m_Items[ i ]; + if ( !stricmp( item->name, text ) ) + { + // engine->pfnClientCmd( item->command ); + m_iCurrentSelection = i; + break; + } + } + + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +// PostMessage( GetParent()->GetVPanel(), new vgui::KeyValues( "TextChanged", "text", text ) ); +} + +const char *CLabeledCommandComboBox::GetActiveItemCommand() +{ + if (m_iCurrentSelection == -1) + return NULL; + + COMMANDITEM *item = &m_Items[ m_iCurrentSelection ]; + return item->command; +} + +void CLabeledCommandComboBox::ApplyChanges() +{ + if (m_iCurrentSelection == -1) + return; + if (m_Items.Size() < 1) + return; + + Assert( m_iCurrentSelection < m_Items.Size() ); + COMMANDITEM *item = &m_Items[ m_iCurrentSelection ]; + engine->ClientCmd_Unrestricted( item->command ); + m_iStartSelection = m_iCurrentSelection; +} + +bool CLabeledCommandComboBox::HasBeenModified() +{ + return m_iStartSelection != m_iCurrentSelection; +} + +void CLabeledCommandComboBox::Reset() +{ + if (m_iStartSelection != -1) + { + ActivateItem(m_iStartSelection); + } +} diff --git a/gameui/LabeledCommandComboBox.h b/gameui/LabeledCommandComboBox.h new file mode 100644 index 0000000..104ce2e --- /dev/null +++ b/gameui/LabeledCommandComboBox.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LABELEDCOMMANDCOMBOBOX_H +#define LABELEDCOMMANDCOMBOBOX_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Panel.h> +#include "utlvector.h" + +class CLabeledCommandComboBox : public vgui::ComboBox +{ + DECLARE_CLASS_SIMPLE( CLabeledCommandComboBox, vgui::ComboBox ); + +public: + CLabeledCommandComboBox(vgui::Panel *parent, const char *panelName); + ~CLabeledCommandComboBox(); + + virtual void DeleteAllItems(); + virtual void AddItem(char const *text, char const *engineCommand); + virtual void ActivateItem(int itemIndex); + const char *GetActiveItemCommand(); + + void SetInitialItem(int itemIndex); + + void ApplyChanges(); + void Reset(); + bool HasBeenModified(); + + enum + { + MAX_NAME_LEN = 256, + MAX_COMMAND_LEN = 256 + }; + +private: + MESSAGE_FUNC_CHARPTR( OnTextChanged, "TextChanged", text ); + + struct COMMANDITEM + { + char name[ MAX_NAME_LEN ]; + char command[ MAX_COMMAND_LEN ]; + int comboBoxID; + }; + + CUtlVector< COMMANDITEM > m_Items; + int m_iCurrentSelection; + int m_iStartSelection; +}; + +#endif // LABELEDCOMMANDCOMBOBOX_H diff --git a/gameui/LoadCommentaryDialog.cpp b/gameui/LoadCommentaryDialog.cpp new file mode 100644 index 0000000..1c70b11 --- /dev/null +++ b/gameui/LoadCommentaryDialog.cpp @@ -0,0 +1,411 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "LoadCommentaryDialog.h" +#include "EngineInterface.h" +#include "IGameUIFuncs.h" + +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include "filesystem.h" + +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/QueryBox.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "BasePanel.h" + +#include "GameUI_Interface.h" + +#include "MouseMessageForwardingPanel.h" +#include "TGAImagePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +void OpenLoadCommentaryDialog( void ) +{ + DHANDLE<CLoadCommentaryDialog> hCommentaryDialog; + if ( !hCommentaryDialog.Get() ) + { + hCommentaryDialog = new CLoadCommentaryDialog( BasePanel() ); + } + + GameUI().ActivateGameUI(); + hCommentaryDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CC_LoadCommentary_Test( void ) +{ + OpenLoadCommentaryDialog(); +} +static ConCommand commentary_testfirstrun("loadcommentary", CC_LoadCommentary_Test, 0 ); + +//----------------------------------------------------------------------------- +// Purpose: Describes the layout of a commentary map list item +//----------------------------------------------------------------------------- +class CCommentaryItemPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CCommentaryItemPanel, vgui::EditablePanel ); +public: + CCommentaryItemPanel( PanelListPanel *parent, const char *name, int iListItemID ) : BaseClass( parent, name ) + { + m_iListItemID = iListItemID; + m_pParent = parent; + m_pCommentaryScreenshot = new CTGAImagePanel( this, "CommentaryMapScreenshot" ); + m_pCommentaryScreenshotBackground = new ImagePanel( this, "CommentaryScreenshotBackground" ); + + // map name + m_pMapNameLabel = new Label( this, "MapName", "" ); + + // description + m_pDescriptionLabel = new Label( this, "Description", "" ); + + CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL); + panel->SetZPos(2); + + SetSize( 200, 140 ); + + LoadControlSettings( "resource/CommentaryItem.res" ); + + m_FillColor = m_pCommentaryScreenshotBackground->GetFillColor(); + } + + void SetCommentaryInfo( CommentaryItem_t &item ) + { + // set the bitmap to display + char tga[_MAX_PATH]; + Q_strncpy( tga, item.szMapFileName, sizeof(tga) ); + char *ext = strstr( tga, ".txt" ); + if ( ext ) + { + strcpy( ext, ".tga" ); + } + m_pCommentaryScreenshot->SetTGA( tga ); + + // set the title text + m_pMapNameLabel->SetText( item.szPrintName ); + m_pDescriptionLabel->SetText( item.szDescription ); + } + + MESSAGE_FUNC_INT( OnPanelSelected, "PanelSelected", state ) + { + if ( state ) + { + // set the text color to be orange, and the pic border to be orange + m_pCommentaryScreenshotBackground->SetFillColor( m_SelectedColor ); + m_pMapNameLabel->SetFgColor( m_SelectedColor ); + m_pDescriptionLabel->SetFgColor( m_SelectedColor ); + } + else + { + m_pCommentaryScreenshotBackground->SetFillColor( m_FillColor ); + m_pMapNameLabel->SetFgColor( m_TextColor ); + m_pDescriptionLabel->SetFgColor( m_TextColor ); + } + + PostMessage( m_pParent->GetVParent(), new KeyValues("PanelSelected") ); + } + + virtual void OnMousePressed( vgui::MouseCode code ) + { + m_pParent->SetSelectedPanel( this ); + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) ); + m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + BaseClass::ApplySchemeSettings( pScheme ); + } + + virtual void OnMouseDoublePressed( vgui::MouseCode code ) + { + // call the panel + OnMousePressed( code ); + PostMessage( m_pParent->GetParent(), new KeyValues("Command", "command", "loadcommentary") ); + } + + int GetListItemID() + { + return m_iListItemID; + } + +private: + vgui::PanelListPanel *m_pParent; + + vgui::Label *m_pMapNameLabel; + vgui::Label *m_pDescriptionLabel; + CTGAImagePanel *m_pCommentaryScreenshot; + ImagePanel *m_pCommentaryScreenshotBackground; + + Color m_TextColor, m_FillColor, m_SelectedColor; + + int m_iListItemID; +}; + +//----------------------------------------------------------------------------- +// Purpose:Constructor +//----------------------------------------------------------------------------- +CLoadCommentaryDialog::CLoadCommentaryDialog(vgui::Panel *parent) : BaseClass(parent, "LoadCommentaryDialog") +{ + SetDeleteSelfOnClose(true); + SetBounds(0, 0, 512, 384); + SetMinimumSize( 256, 300 ); + SetSizeable( true ); + + SetTitle("#GameUI_LoadCommentary", true); + + vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + CreateCommentaryItemList(); + + new vgui::Button( this, "loadcommentary", "" ); + SetControlEnabled( "loadcommentary", false ); + + LoadControlSettings("resource/LoadCommentaryDialog.res"); + + // Look for commentary .txt files in the map directory + ScanCommentaryFiles(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::OnCommand( const char *command ) +{ + if ( !Q_stricmp( command, "loadcommentary" ) ) + { + int itemIndex = GetSelectedItemIndex(); + if ( m_CommentaryItems.IsValidIndex(itemIndex) ) + { + const char *mapName = m_CommentaryItems[itemIndex].szMapName; + if ( mapName && mapName[ 0 ] ) + { + // Load the game, return to top and switch to engine + char sz[ 256 ]; + Q_snprintf(sz, sizeof( sz ), "progress_enable\ncommentary 1\nmap %s\n", mapName ); + + // Close this dialog + OnClose(); + + engine->ClientCmd_Unrestricted( sz ); + } + } + } + else + { + BaseClass::OnCommand( command ); + } +} + +void CLoadCommentaryDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + OnCommand( "loadcommentary" ); + return; + } + else if ( code == KEY_XBUTTON_B ) + { + OnCommand( "Close" ); + return; + } + + BaseClass::OnKeyCodePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates the load game display list +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::CreateCommentaryItemList() +{ + m_pGameList = new vgui::PanelListPanel( this, "listpanel_commentary" ); + m_pGameList->SetFirstColumnWidth( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the save file name of the selected item +//----------------------------------------------------------------------------- +int CLoadCommentaryDialog::GetSelectedItemIndex() +{ + CCommentaryItemPanel *panel = dynamic_cast<CCommentaryItemPanel *>(m_pGameList->GetSelectedPanel()); + if ( panel ) + { + // find the panel in the list + for ( int i = 0; i < m_CommentaryItems.Count(); i++ ) + { + if ( i == panel->GetListItemID() ) + { + return i; + } + } + } + return m_CommentaryItems.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: builds save game list from directory +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::ScanCommentaryFiles() +{ + // populate list box with all saved games on record: + char szDirectory[_MAX_PATH]; + Q_snprintf( szDirectory, sizeof( szDirectory ), "maps/*commentary.txt" ); + + // clear the current list + m_pGameList->DeleteAllItems(); + m_CommentaryItems.RemoveAll(); + + // iterate the files + FileFindHandle_t handle; + const char *pFileName = g_pFullFileSystem->FindFirst( szDirectory, &handle ); + while (pFileName) + { + char szFileName[_MAX_PATH]; + Q_snprintf(szFileName, sizeof( szFileName ), "maps/%s", pFileName); + + // Only load save games from the current mod's save dir + if( !g_pFullFileSystem->FileExists( szFileName, "MOD" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + ParseCommentaryFile( szFileName, pFileName ); + + pFileName = g_pFullFileSystem->FindNext( handle ); + } + + g_pFullFileSystem->FindClose( handle ); + + // sort the save list + qsort( m_CommentaryItems.Base(), m_CommentaryItems.Count(), sizeof(CommentaryItem_t), &SaveGameSortFunc ); + + // add to the list + for ( int saveIndex = 0; saveIndex < m_CommentaryItems.Count() && saveIndex < MAX_LISTED_COMMENTARY_ITEMS; saveIndex++ ) + { + // add the item to the panel + AddCommentaryItemToList( saveIndex ); + } + + // display a message if there are no save games + if ( !m_CommentaryItems.Count() ) + { + vgui::Label *pNoCommentaryItemsLabel = SETUP_PANEL(new Label(m_pGameList, "NoCommentaryItemsLabel", "#GameUI_NoCommentaryItemsToDisplay")); + pNoCommentaryItemsLabel->SetTextColorState(vgui::Label::CS_DULL); + m_pGameList->AddItem( NULL, pNoCommentaryItemsLabel ); + } + + SetControlEnabled( "loadcommentary", false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds an item to the list +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::AddCommentaryItemToList( int itemIndex ) +{ + // create the new panel and add to the list + CCommentaryItemPanel *commentaryItemPanel = new CCommentaryItemPanel( m_pGameList, "CommentaryItemPanel", itemIndex ); + commentaryItemPanel->SetCommentaryInfo( m_CommentaryItems[itemIndex] ); + m_pGameList->AddItem( NULL, commentaryItemPanel ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the save game info out of the .sav file header +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::ParseCommentaryFile( char const *pszFileName, char const *pszShortName ) +{ + if ( !pszFileName || !pszShortName ) + return; + + // load the file as keyvalues + KeyValues *pData = new KeyValues( "commentary_data" ); + + if ( false == pData->LoadFromFile( g_pFullFileSystem, pszFileName, "MOD" ) ) + { + pData->deleteThis(); + return; + } + + // walk the platform menu loading all the interfaces + KeyValues *menuKeys = pData->FindKey("trackinfo", false); + if ( menuKeys ) + { + for (KeyValues *track = menuKeys->GetFirstSubKey(); track != NULL; track = track->GetNextKey()) + { + //Msg( "track found: %s %s\n", track->GetString("map", "?"), track->GetString( "description", "asdf" ) ); + + CommentaryItem_t item; + Q_strncpy( item.szMapFileName, pszFileName, sizeof(item.szMapFileName) ); + Q_strncpy( item.szMapName, track->GetString( "map", "" ), sizeof(item.szMapName) ); + Q_strncpy( item.szPrintName, track->GetString( "printname", "" ), sizeof(item.szPrintName) ); + Q_strncpy( item.szDescription, track->GetString( "description", "" ), sizeof(item.szDescription) ); + + //item.iChannel = track->GetInt( "channel" ); + + m_CommentaryItems.AddToTail( item ); + } + } + else + { + CommentaryItem_t item; + Q_strncpy( item.szMapFileName, pszFileName, sizeof(item.szMapFileName) ); + + char mapname[_MAX_PATH]; + Q_strncpy( mapname, pszFileName, sizeof(item.szMapName) ); + char *ext = strstr( mapname, "_commentary" ); + if ( !ext ) + return; + + *ext = '\0'; + Q_FileBase( mapname, item.szMapName, sizeof(item.szMapName) ); + + Q_strncpy( item.szPrintName, "No trackinfo found.", sizeof(item.szPrintName) ); + Q_strncpy( item.szDescription, "No trackinfo found.", sizeof(item.szDescription) ); + m_CommentaryItems.AddToTail( item ); + } + + return; +} + +//----------------------------------------------------------------------------- +// Purpose: timestamp sort function for savegames +//----------------------------------------------------------------------------- +int CLoadCommentaryDialog::SaveGameSortFunc( const void *lhs, const void *rhs ) +{ + // Sort by map name + const CommentaryItem_t *s1 = (const CommentaryItem_t *)lhs; + const CommentaryItem_t *s2 = (const CommentaryItem_t *)rhs; + + return Q_stricmp( s1->szPrintName, s2->szPrintName ); +} + +//----------------------------------------------------------------------------- +// Purpose: One item has been selected +//----------------------------------------------------------------------------- +void CLoadCommentaryDialog::OnPanelSelected() +{ + SetControlEnabled( "loadcommentary", true ); +} diff --git a/gameui/LoadCommentaryDialog.h b/gameui/LoadCommentaryDialog.h new file mode 100644 index 0000000..aa728b6 --- /dev/null +++ b/gameui/LoadCommentaryDialog.h @@ -0,0 +1,63 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef LOADCOMMENTARYDIALOG_H +#define LOADCOMMENTARYDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "filesystem.h" +#include "utlvector.h" + + +#define COMMENTARY_MAPFILENAME_MAX_LEN MAX_PATH +#define COMMENTARY_MAPNAME_MAX_LEN 32 +#define COMMENTARY_DESCRIP_MAX_LEN 512 +#define MAX_LISTED_COMMENTARY_ITEMS 32 + +struct CommentaryItem_t +{ + char szMapFileName[COMMENTARY_MAPFILENAME_MAX_LEN]; // file location of the map + char szMapName[COMMENTARY_MAPNAME_MAX_LEN]; // clean name of the map, eg "dod_kalt" + char szPrintName[COMMENTARY_MAPNAME_MAX_LEN]; // printable name "Kalt" + char szDescription[COMMENTARY_DESCRIP_MAX_LEN]; // track description "This map is fun, and people like it!" + + //int iChannel; // TODO: multiple channels within a single map, loaded into separate commentary tracks +}; + +//----------------------------------------------------------------------------- +// Purpose: Base class for save & load game dialogs +//----------------------------------------------------------------------------- +class CLoadCommentaryDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CLoadCommentaryDialog, vgui::Frame ); + +public: + CLoadCommentaryDialog( vgui::Panel *parent ); + +protected: + CUtlVector<CommentaryItem_t> m_CommentaryItems; + vgui::PanelListPanel *m_pGameList; + + virtual void OnCommand( const char *command ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + + void ScanCommentaryFiles(); + void CreateCommentaryItemList(); + int GetSelectedItemIndex(); + void AddCommentaryItemToList( int saveIndex ); + + void ParseCommentaryFile( char const *pszFileName, char const *pszShortName ); + static int __cdecl SaveGameSortFunc( const void *lhs, const void *rhs ); + +private: + MESSAGE_FUNC( OnPanelSelected, "PanelSelected" ); +}; + + +#endif // LOADCOMMENTARYDIALOG_H diff --git a/gameui/LoadGameDialog.cpp b/gameui/LoadGameDialog.cpp new file mode 100644 index 0000000..0b54cb8 --- /dev/null +++ b/gameui/LoadGameDialog.cpp @@ -0,0 +1,107 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "LoadGameDialog.h" +#include "EngineInterface.h" + +#include "vgui/ISystem.h" +#include "vgui/ISurface.h" +#include "vgui/IVGui.h" +#include "KeyValues.h" +#include "filesystem.h" + +#include "vgui_controls/Button.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/QueryBox.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose:Constructor +//----------------------------------------------------------------------------- +CLoadGameDialog::CLoadGameDialog(vgui::Panel *parent) : BaseClass(parent, "LoadGameDialog") +{ + SetDeleteSelfOnClose(true); + SetBounds(0, 0, 512, 384); + SetMinimumSize( 256, 300 ); + SetSizeable( true ); + + SetTitle("#GameUI_LoadGame", true); + + vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + LoadControlSettings("resource/LoadGameDialog.res"); + + SetControlEnabled( "delete", false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CLoadGameDialog::~CLoadGameDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadGameDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "loadsave" ) ) + { + int saveIndex = GetSelectedItemSaveIndex(); + if ( m_SaveGames.IsValidIndex(saveIndex) ) + { + const char *shortName = m_SaveGames[saveIndex].szShortName; + if ( shortName && shortName[ 0 ] ) + { + // Load the game, return to top and switch to engine + char sz[ 256 ]; + Q_snprintf(sz, sizeof( sz ), "progress_enable\nload %s\n", shortName ); + + engine->ClientCmd_Unrestricted( sz ); + + // Close this dialog + OnClose(); + } + } + } + else if ( !stricmp( command, "Delete" ) ) + { + int saveIndex = GetSelectedItemSaveIndex(); + if ( m_SaveGames.IsValidIndex(saveIndex) ) + { + // confirm the deletion + QueryBox *box = new QueryBox( "#GameUI_ConfirmDeleteSaveGame_Title", "#GameUI_ConfirmDeleteSaveGame_Info" ); + box->AddActionSignalTarget(this); + box->SetOKButtonText("#GameUI_ConfirmDeleteSaveGame_OK"); + box->SetOKCommand(new KeyValues("Command", "command", "DeleteConfirmed")); + box->DoModal(); + } + } + else if ( !stricmp( command, "DeleteConfirmed" ) ) + { + int saveIndex = GetSelectedItemSaveIndex(); + if ( m_SaveGames.IsValidIndex(saveIndex) ) + { + DeleteSaveGame( m_SaveGames[saveIndex].szFileName ); + + // reset the list + ScanSavedGames(); + m_pGameList->MoveScrollBarToTop(); + } + } + else + { + BaseClass::OnCommand( command ); + } +} + diff --git a/gameui/LoadGameDialog.h b/gameui/LoadGameDialog.h new file mode 100644 index 0000000..4e8de64 --- /dev/null +++ b/gameui/LoadGameDialog.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LOADGAMEDIALOG_H +#define LOADGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "BaseSaveGameDialog.h" +#include "SaveGameDialog.h" +#include "SaveGameBrowserDialog.h" +#include "BasePanel.h" +#include "vgui_controls/KeyRepeat.h" + +//----------------------------------------------------------------------------- +// Purpose: Displays game loading options +//----------------------------------------------------------------------------- +class CLoadGameDialog : public CBaseSaveGameDialog +{ + DECLARE_CLASS_SIMPLE( CLoadGameDialog, CBaseSaveGameDialog ); + +public: + CLoadGameDialog(vgui::Panel *parent); + ~CLoadGameDialog(); + + virtual void OnCommand( const char *command ); +}; + +// +// +// + +class CLoadGameDialogXbox : public CSaveGameBrowserDialog +{ + DECLARE_CLASS_SIMPLE( CLoadGameDialogXbox, CSaveGameBrowserDialog ); + +public: + CLoadGameDialogXbox( vgui::Panel *parent ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand(const char *command); + virtual void PerformSelectedAction( void ); + virtual void PerformDeletion( void ); + virtual void UpdateFooterOptions( void ); + +private: + void DeleteSaveGame( const SaveGameDescription_t *pSaveDesc ); +}; + +#endif // LOADGAMEDIALOG_H diff --git a/gameui/LoadGameDialog_Xbox.cpp b/gameui/LoadGameDialog_Xbox.cpp new file mode 100644 index 0000000..742a0ea --- /dev/null +++ b/gameui/LoadGameDialog_Xbox.cpp @@ -0,0 +1,219 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BasePanel.h" +#include "LoadGameDialog.h" + +#include "winlite.h" +#include "vgui/ISurface.h" + +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "ixboxsystem.h" +#include "filesystem.h" + +#include "SaveGameBrowserDialog.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +CLoadGameDialogXbox::CLoadGameDialogXbox( vgui::Panel *parent ) : BaseClass( parent ) +{ + m_bFilterAutosaves = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + vgui::Label *pTitle = (Label *) FindChildByName( "TitleLabel" ); + if ( pTitle ) + { + pTitle->SetText( "#GameUI_LoadGame" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::PerformSelectedAction( void ) +{ + BaseClass::PerformSelectedAction(); + + if ( !GetNumPanels() ) + return; + + SetControlDisabled( true ); + + // Warn the player if they're already in a map + if ( !GameUI().HasSavedThisMenuSession() && GameUI().IsInLevel() && engine->GetMaxClients() == 1 ) + { + BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_LOAD, this ); + } + else + { + // Otherwise just do it + OnCommand( "LoadGame" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::PerformDeletion( void ) +{ + // Cannot delete autosaves! + CGameSavePanel *pPanel = GetActivePanel(); + if ( pPanel == NULL || ( pPanel && pPanel->IsAutoSaveType() ) ) + return; + + BaseClass::PerformDeletion(); + + SetControlDisabled( true ); + + vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); + BasePanel()->ShowMessageDialog( MD_DELETE_SAVE_CONFIRM, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bNewSaveSelected - +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::UpdateFooterOptions( void ) +{ + CFooterPanel *pFooter = GetFooterPanel(); + + // Show available buttons + pFooter->ClearButtons(); + + // Make sure we have panels to show + if ( HasActivePanels() ) + { + pFooter->AddNewButtonLabel( "#GameUI_Load", "#GameUI_Icons_A_BUTTON" ); + + // Don't allow deletions of autosaves! + CGameSavePanel *pPanel = GetActivePanel(); + if ( pPanel && pPanel->IsAutoSaveType() == false ) + { + pFooter->AddNewButtonLabel( "#GameUI_Delete", "#GameUI_Icons_X_BUTTON" ); + } + } + + // Always allow storage devices changes and cancelling + pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); + pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *command - +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::OnCommand( const char *command ) +{ + m_KeyRepeat.Reset(); + + if ( !Q_stricmp( command, "LoadGame" ) ) + { + // Must have an active panel to perform this action + if ( GetNumPanels() == 0 ) + { + SetControlDisabled( false ); + return; + } + + const SaveGameDescription_t *pSave = GetActivePanelSaveDescription(); + + // Load the saved game + char szCmd[ 256 ]; + Q_snprintf( szCmd, sizeof( szCmd ), "xload %s", pSave->szShortName ); + engine->ClientCmd_Unrestricted( szCmd ); + + // Ignore all other input while we're open + OnClose(); + } + else if ( !Q_stricmp( command, "DeleteGame" ) ) + { + // Must have an active panel to perform this action + if ( GetNumPanels() == 0 ) + { + SetControlDisabled( false ); + return; + } + + // Delete the game they've selected + const SaveGameDescription_t *pSave = GetActivePanelSaveDescription(); + DeleteSaveGame( pSave ); + RemoveActivePanel(); + } + else if ( !Q_stricmp( command, "RefreshSaveGames" ) ) + { + // FIXME: At this point the rug has been pulled out from undereath us! + RefreshSaveGames(); + } + else if ( !Q_stricmp( command, "LoadGameCancelled" ) ) + { + SetControlDisabled( false ); + } + else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) + { + vgui::surface()->RestrictPaintToSinglePanel( NULL ); + } + else if ( !Q_stricmp( command, "DeleteGameCancelled" ) ) + { + SetControlDisabled( false ); + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an existing save game +//----------------------------------------------------------------------------- +void CLoadGameDialogXbox::DeleteSaveGame( const SaveGameDescription_t *pSaveDesc ) +{ + if ( pSaveDesc == NULL ) + { + SetControlDisabled( false ); + return; + } + + // If we're deleting our more recent save game, we need to make sure we setup the engine to properly load the last most recent + if ( Q_stristr( engine->GetMostRecentSaveGame(), pSaveDesc->szShortName ) ) + { + // We must have at least two active save games that we know about + if ( GetNumPanels() > 1 ) + { + // The panels are sorted by how recent they are, so the first element is the most recent + const SaveGameDescription_t *pDesc = GetPanelSaveDecription( 0 ); + if ( pDesc == pSaveDesc ) + { + // If we're deleting our most recent, we need to pick the next most recent + pDesc = GetPanelSaveDecription( 1 ); + } + + // Remember this filename for the next time we need to reload + if ( pDesc ) + { + engine->SetMostRecentSaveGame( pDesc->szShortName ); + } + } + } + + // Delete the save game file + g_pFullFileSystem->RemoveFile( pSaveDesc->szFileName, "MOD" ); + + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + // Return control + SetControlDisabled( false ); +} diff --git a/gameui/LoadingDialog.cpp b/gameui/LoadingDialog.cpp new file mode 100644 index 0000000..b0d6be6 --- /dev/null +++ b/gameui/LoadingDialog.cpp @@ -0,0 +1,669 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include "LoadingDialog.h" +#include "EngineInterface.h" +#include "IGameUIFuncs.h" + +#include <vgui/IInput.h> +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <vgui/IScheme.h> +#include <vgui/ISystem.h> +#include <vgui_controls/ProgressBar.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/HTML.h> +#include <vgui_controls/RichText.h> +#include "tier0/icommandline.h" + +#include "GameUI_Interface.h" +#include "ModInfo.h" +#include "BasePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CLoadingDialog::CLoadingDialog( vgui::Panel *parent ) : Frame(parent, "LoadingDialog") +{ + SetDeleteSelfOnClose(true); + + // Use console style + m_bConsoleStyle = GameUI().IsConsoleUI(); + + if ( !m_bConsoleStyle ) + { + SetSize( 416, 100 ); + SetTitle( "#GameUI_Loading", true ); + } + + // center the loading dialog, unless we have another dialog to show in the background + m_bCenter = !GameUI().HasLoadingBackgroundDialog(); + + m_bShowingSecondaryProgress = false; + m_flSecondaryProgress = 0.0f; + m_flLastSecondaryProgressUpdateTime = 0.0f; + m_flSecondaryProgressStartTime = 0.0f; + + m_pProgress = new ProgressBar( this, "Progress" ); + m_pProgress2 = new ProgressBar( this, "Progress2" ); + m_pInfoLabel = new Label( this, "InfoLabel", "" ); + m_pCancelButton = new Button( this, "CancelButton", "#GameUI_Cancel" ); + m_pTimeRemainingLabel = new Label( this, "TimeRemainingLabel", "" ); + m_pCancelButton->SetCommand( "Cancel" ); + + if ( ModInfo().IsSinglePlayerOnly() == false && m_bConsoleStyle == true ) + { + m_pLoadingBackground = new Panel( this, "LoadingDialogBG" ); + } + else + { + m_pLoadingBackground = NULL; + } + + SetMinimizeButtonVisible( false ); + SetMaximizeButtonVisible( false ); + SetCloseButtonVisible( false ); + SetSizeable( false ); + SetMoveable( false ); + + if ( m_bConsoleStyle ) + { + m_bCenter = false; + m_pProgress->SetVisible( false ); + m_pProgress2->SetVisible( false ); + m_pInfoLabel->SetVisible( false ); + m_pCancelButton->SetVisible( false ); + m_pTimeRemainingLabel->SetVisible( false ); + m_pCancelButton->SetVisible( false ); + + SetMinimumSize( 0, 0 ); + SetTitleBarVisible( false ); + + m_flProgressFraction = 0; + } + else + { + m_pInfoLabel->SetBounds(20, 32, 392, 24); + m_pProgress->SetBounds(20, 64, 300, 24); + m_pCancelButton->SetBounds(330, 64, 72, 24); + m_pProgress2->SetVisible(false); + } + + SetupControlSettings( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CLoadingDialog::~CLoadingDialog() +{ + if ( input()->GetAppModalSurface() == GetVPanel() ) + { + vgui::surface()->RestrictPaintToSinglePanel( NULL ); + } +} + +void CLoadingDialog::PaintBackground() +{ + if ( !m_bConsoleStyle ) + { + BaseClass::PaintBackground(); + return; + } + + // draw solid progress bar with curved endcaps + int panelWide, panelTall; + GetSize( panelWide, panelTall ); + int barWide, barTall; + m_pProgress->GetSize( barWide, barTall ); + int x = ( panelWide - barWide )/2; + int y = panelTall - barTall; + + if ( m_pLoadingBackground ) + { + vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); + Color color = GetSchemeColor( "TanDarker", Color(255, 255, 255, 255), vgui::scheme()->GetIScheme(scheme) ); + + m_pLoadingBackground->SetFgColor( color ); + m_pLoadingBackground->SetBgColor( color ); + + m_pLoadingBackground->SetPaintBackgroundEnabled( true ); + } + + if ( ModInfo().IsSinglePlayerOnly() ) + { + DrawBox( x, y, barWide, barTall, Color( 0, 0, 0, 255 ), 1.0f ); + } + + DrawBox( x+2, y+2, barWide-4, barTall-4, Color( 100, 100, 100, 255 ), 1.0f ); + + barWide = m_flProgressFraction * ( barWide - 4 ); + if ( barWide >= 12 ) + { + // cannot draw a curved box smaller than 12 without artifacts + DrawBox( x+2, y+2, barWide, barTall-4, Color( 200, 100, 0, 255 ), 1.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets up dialog layout +//----------------------------------------------------------------------------- +void CLoadingDialog::SetupControlSettings( bool bForceShowProgressText ) +{ + m_bShowingVACInfo = false; + + if ( GameUI().IsConsoleUI() ) + { + KeyValues *pControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "LoadingDialogNoBanner.res" ); + LoadControlSettings( "null", NULL, pControlSettings ); + return; + } + + if ( ModInfo().IsSinglePlayerOnly() && !bForceShowProgressText ) + { + LoadControlSettings("Resource/LoadingDialogNoBannerSingle.res"); + } + else if ( gameuifuncs->IsConnectedToVACSecureServer() ) + { + LoadControlSettings("Resource/LoadingDialogVAC.res"); + m_bShowingVACInfo = true; + } + else + { + LoadControlSettings("Resource/LoadingDialogNoBanner.res"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Activates the loading screen, initializing and making it visible +//----------------------------------------------------------------------------- +void CLoadingDialog::Open() +{ + if ( !m_bConsoleStyle ) + { + SetTitle( "#GameUI_Loading", true ); + } + + HideOtherDialogs( true ); + BaseClass::Activate(); + + if ( !m_bConsoleStyle ) + { + m_pProgress->SetVisible( true ); + if ( !ModInfo().IsSinglePlayerOnly() ) + { + m_pInfoLabel->SetVisible( true ); + } + m_pInfoLabel->SetText(""); + + m_pCancelButton->SetText("#GameUI_Cancel"); + m_pCancelButton->SetCommand("Cancel"); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: error display file +//----------------------------------------------------------------------------- +void CLoadingDialog::SetupControlSettingsForErrorDisplay( const char *settingsFile ) +{ + if ( m_bConsoleStyle ) + { + return; + } + + m_bCenter = true; + SetTitle("#GameUI_Disconnected", true); + m_pInfoLabel->SetText(""); + LoadControlSettings( settingsFile ); + HideOtherDialogs( true ); + + BaseClass::Activate(); + + m_pProgress->SetVisible(false); + + m_pInfoLabel->SetVisible(true); + m_pCancelButton->SetText("#GameUI_Close"); + m_pCancelButton->SetCommand("Close"); + m_pInfoLabel->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: shows or hides other top-level dialogs +//----------------------------------------------------------------------------- +void CLoadingDialog::HideOtherDialogs( bool bHide ) +{ + if ( bHide ) + { + if ( GameUI().HasLoadingBackgroundDialog() ) + { + // if we have a loading background dialog, hide any other dialogs by moving the full-screen background dialog to the + // front, then moving ourselves in front of it + GameUI().ShowLoadingBackgroundDialog(); + vgui::ipanel()->MoveToFront( GetVPanel() ); + vgui::input()->SetAppModalSurface( GetVPanel() ); + } + else + { + // if there is no loading background dialog, use VGUI paint restrictions to hide other dialogs + vgui::surface()->RestrictPaintToSinglePanel(GetVPanel()); + } + } + else + { + if ( GameUI().HasLoadingBackgroundDialog() ) + { + GameUI().HideLoadingBackgroundDialog(); + vgui::input()->SetAppModalSurface( NULL ); + } + else + { + // remove any rendering restrictions + vgui::surface()->RestrictPaintToSinglePanel(NULL); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Turns dialog into error display +//----------------------------------------------------------------------------- +void CLoadingDialog::DisplayGenericError(const char *failureReason, const char *extendedReason) +{ + if ( m_bConsoleStyle ) + { + return; + } + + // In certain race conditions, DisplayGenericError can get called AFTER OnClose() has been called. + // If that happens and we don't call Activate(), then it'll continue closing when we don't want it to. + Activate(); + + SetupControlSettingsForErrorDisplay("Resource/LoadingDialogError.res"); + + if ( extendedReason && strlen( extendedReason ) > 0 ) + { + wchar_t compositeReason[256], finalMsg[512], formatStr[256]; + if ( extendedReason[0] == '#' ) + { + wcsncpy(compositeReason, g_pVGuiLocalize->Find(extendedReason), sizeof( compositeReason ) / sizeof( wchar_t ) ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode(extendedReason, compositeReason, sizeof( compositeReason )); + } + + if ( failureReason[0] == '#' ) + { + wcsncpy(formatStr, g_pVGuiLocalize->Find(failureReason), sizeof( formatStr ) / sizeof( wchar_t ) ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode(failureReason, formatStr, sizeof( formatStr )); + } + + g_pVGuiLocalize->ConstructString(finalMsg, sizeof( finalMsg ), formatStr, 1, compositeReason); + m_pInfoLabel->SetText(finalMsg); + } + else + { + m_pInfoLabel->SetText(failureReason); + } + + int wide, tall; + int x,y; + m_pInfoLabel->GetContentSize( wide, tall ); + m_pInfoLabel->GetPos( x, y ); + SetTall( tall + y + 50 ); + + int buttonX, buttonY; + m_pCancelButton->GetPos( buttonX, buttonY ); + m_pCancelButton->SetPos( buttonX, tall + y + 6 ); + + m_pCancelButton->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: explain to the user they can't join secure servers due to a VAC ban +//----------------------------------------------------------------------------- +void CLoadingDialog::DisplayVACBannedError() +{ + if ( m_bConsoleStyle ) + { + return; + } + + SetupControlSettingsForErrorDisplay("Resource/LoadingDialogErrorVACBanned.res"); + SetTitle("#VAC_ConnectionRefusedTitle", true); +} + + +//----------------------------------------------------------------------------- +// Purpose: explain to the user they can't connect to public servers due to +// not having a valid connection to Steam +// this should only happen if they are a pirate +//----------------------------------------------------------------------------- +void CLoadingDialog::DisplayNoSteamConnectionError() +{ + if ( m_bConsoleStyle ) + { + return; + } + + SetupControlSettingsForErrorDisplay("Resource/LoadingDialogErrorNoSteamConnection.res"); +} + + +//----------------------------------------------------------------------------- +// Purpose: explain to the user they got kicked from a server due to that same account +// logging in from another location. This also triggers the refresh login dialog on OK +// being pressed. +//----------------------------------------------------------------------------- +void CLoadingDialog::DisplayLoggedInElsewhereError() +{ + if ( m_bConsoleStyle ) + { + return; + } + + SetupControlSettingsForErrorDisplay("Resource/LoadingDialogErrorLoggedInElsewhere.res"); + m_pCancelButton->SetText("#GameUI_RefreshLogin_Login"); + m_pCancelButton->SetCommand("Login"); +} + + +//----------------------------------------------------------------------------- +// Purpose: sets status info text +//----------------------------------------------------------------------------- +void CLoadingDialog::SetStatusText(const char *statusText) +{ + if ( m_bConsoleStyle ) + { + return; + } + + m_pInfoLabel->SetText(statusText); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the previous state +//----------------------------------------------------------------------------- +bool CLoadingDialog::SetShowProgressText( bool show ) +{ + if ( m_bConsoleStyle ) + { + return false; + } + + bool bret = m_pInfoLabel->IsVisible(); + if ( bret != show ) + { + SetupControlSettings( show ); + m_pInfoLabel->SetVisible( show ); + } + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: updates time remaining +//----------------------------------------------------------------------------- +void CLoadingDialog::OnThink() +{ + BaseClass::OnThink(); + + if ( !m_bConsoleStyle && m_bShowingSecondaryProgress ) + { + // calculate the time remaining string + wchar_t unicode[512]; + if (m_flSecondaryProgress >= 1.0f) + { + m_pTimeRemainingLabel->SetText("complete"); + } + else if (ProgressBar::ConstructTimeRemainingString(unicode, sizeof(unicode), m_flSecondaryProgressStartTime, (float)system()->GetFrameTime(), m_flSecondaryProgress, m_flLastSecondaryProgressUpdateTime, true)) + { + m_pTimeRemainingLabel->SetText(unicode); + } + else + { + m_pTimeRemainingLabel->SetText(""); + } + } + + SetAlpha( 255 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadingDialog::PerformLayout() +{ + if ( m_bConsoleStyle ) + { + // place in lower center + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall ); + int wide,tall; + GetSize( wide, tall ); + int x = 0; + int y = 0; + + if ( ModInfo().IsSinglePlayerOnly() ) + { + x = ( screenWide - wide ) * 0.50f; + y = ( screenTall - tall ) * 0.86f; + } + else + { + x = ( screenWide - ( wide * 1.30f ) ); + y = ( ( screenTall * 0.875f ) ); + } + + SetPos( x, y ); + } + else if ( m_bCenter ) + { + MoveToCenterOfScreen(); + } + else + { + // if we're not supposed to be centered, move ourselves to the lower right hand corner of the screen + int x, y, screenWide, screenTall; + surface()->GetWorkspaceBounds( x, y, screenWide, screenTall ); + int wide,tall; + GetSize( wide, tall ); + + if ( IsPC() ) + { + x = screenWide - ( wide + 10 ); + y = screenTall - ( tall + 10 ); + } + else + { + // Move farther in so we're title safe + x = screenWide - wide - (screenWide * 0.05); + y = screenTall - tall - (screenTall * 0.05); + } + + x -= m_iAdditionalIndentX; + y -= m_iAdditionalIndentY; + + SetPos( x, y ); + } + + BaseClass::PerformLayout(); + + vgui::ipanel()->MoveToFront( GetVPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the number of ticks has changed +//----------------------------------------------------------------------------- +bool CLoadingDialog::SetProgressPoint( float fraction ) +{ + if ( m_bConsoleStyle ) + { + if ( fraction >= 0.99f ) + { + // show the progress artifically completed to fill in 100% + fraction = 1.0f; + } + fraction = clamp( fraction, 0.0f, 1.0f ); + if ( (int)(fraction * 25) != (int)(m_flProgressFraction * 25) ) + { + m_flProgressFraction = fraction; + return true; + } + return IsX360(); + } + + if ( !m_bShowingVACInfo && gameuifuncs->IsConnectedToVACSecureServer() ) + { + SetupControlSettings( false ); + } + + int nOldDrawnSegments = m_pProgress->GetDrawnSegmentCount(); + m_pProgress->SetProgress( fraction ); + int nNewDrawSegments = m_pProgress->GetDrawnSegmentCount(); + return (nOldDrawnSegments != nNewDrawSegments) || IsX360(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets and shows the secondary progress bar +//----------------------------------------------------------------------------- +void CLoadingDialog::SetSecondaryProgress( float progress ) +{ + if ( m_bConsoleStyle ) + return; + + // don't show the progress if we've jumped right to completion + if (!m_bShowingSecondaryProgress && progress > 0.99f) + return; + + // if we haven't yet shown secondary progress then reconfigure the dialog + if (!m_bShowingSecondaryProgress) + { + LoadControlSettings("Resource/LoadingDialogDualProgress.res"); + m_bShowingSecondaryProgress = true; + m_pProgress2->SetVisible(true); + m_flSecondaryProgressStartTime = (float)system()->GetFrameTime(); + } + + // if progress has increased then update the progress counters + if (progress > m_flSecondaryProgress) + { + m_pProgress2->SetProgress(progress); + m_flSecondaryProgress = progress; + m_flLastSecondaryProgressUpdateTime = (float)system()->GetFrameTime(); + } + + // if progress has decreased then reset progress counters + if (progress < m_flSecondaryProgress) + { + m_pProgress2->SetProgress(progress); + m_flSecondaryProgress = progress; + m_flLastSecondaryProgressUpdateTime = (float)system()->GetFrameTime(); + m_flSecondaryProgressStartTime = (float)system()->GetFrameTime(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadingDialog::SetSecondaryProgressText(const char *statusText) +{ + if ( m_bConsoleStyle ) + { + return; + } + + SetControlString( "SecondaryProgressLabel", statusText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLoadingDialog::OnClose() +{ + // remove any rendering restrictions + HideOtherDialogs( false ); + + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: command handler +//----------------------------------------------------------------------------- +void CLoadingDialog::OnCommand(const char *command) +{ + if ( !stricmp(command, "Cancel") ) + { + // disconnect from the server + engine->ClientCmd_Unrestricted("disconnect\n"); + + // close + Close(); + } + else + { + BaseClass::OnCommand(command); + } +} + +void CLoadingDialog::OnKeyCodeTyped(KeyCode code) +{ + if ( m_bConsoleStyle ) + { + return; + } + + if ( code == KEY_ESCAPE ) + { + OnCommand("Cancel"); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Maps ESC to quiting loading +//----------------------------------------------------------------------------- +void CLoadingDialog::OnKeyCodePressed(KeyCode code) +{ + if ( m_bConsoleStyle ) + { + return; + } + + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A || nButtonCode == STEAMCONTROLLER_B ) + { + OnCommand("Cancel"); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Singleton accessor +//----------------------------------------------------------------------------- +extern vgui::DHANDLE<CLoadingDialog> g_hLoadingDialog; +CLoadingDialog *LoadingDialog() +{ + return g_hLoadingDialog.Get(); +} diff --git a/gameui/LoadingDialog.h b/gameui/LoadingDialog.h new file mode 100644 index 0000000..e38765c --- /dev/null +++ b/gameui/LoadingDialog.h @@ -0,0 +1,77 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LOADINGDIALOG_H +#define LOADINGDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/HTML.h> + +//----------------------------------------------------------------------------- +// Purpose: Dialog for displaying level loading status +//----------------------------------------------------------------------------- +class CLoadingDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CLoadingDialog, vgui::Frame ); +public: + CLoadingDialog( vgui::Panel *parent ); + ~CLoadingDialog(); + + void Open(); + bool SetProgressPoint(float fraction); + void SetStatusText(const char *statusText); + void SetSecondaryProgress(float progress); + void SetSecondaryProgressText(const char *statusText); + bool SetShowProgressText( bool show ); + + void DisplayGenericError(const char *failureReason, const char *extendedReason = NULL); + void DisplayVACBannedError(); + void DisplayNoSteamConnectionError(); + void DisplayLoggedInElsewhereError(); + +protected: + virtual void OnCommand(const char *command); + virtual void PerformLayout(); + virtual void OnThink(); + virtual void OnClose(); + virtual void OnKeyCodeTyped(vgui::KeyCode code); + virtual void OnKeyCodePressed(vgui::KeyCode code); + virtual void PaintBackground( void ); + +private: + void SetupControlSettings( bool bForceShowProgressText ); + void SetupControlSettingsForErrorDisplay( const char *settingsFile ); + void HideOtherDialogs( bool bHide ); + + vgui::ProgressBar *m_pProgress; + vgui::ProgressBar *m_pProgress2; + vgui::Label *m_pInfoLabel; + vgui::Label *m_pTimeRemainingLabel; + vgui::Button *m_pCancelButton; + vgui::Panel *m_pLoadingBackground; + + bool m_bShowingSecondaryProgress; + float m_flSecondaryProgress; + float m_flLastSecondaryProgressUpdateTime; + float m_flSecondaryProgressStartTime; + bool m_bShowingVACInfo; + bool m_bCenter; + bool m_bConsoleStyle; + float m_flProgressFraction; + + CPanelAnimationVar( int, m_iAdditionalIndentX, "AdditionalIndentX", "0" ); + CPanelAnimationVar( int, m_iAdditionalIndentY, "AdditionalIndentY", "0" ); +}; + +// singleton accessor +CLoadingDialog *LoadingDialog(); + + +#endif // LOADINGDIALOG_H diff --git a/gameui/LogoFile.cpp b/gameui/LogoFile.cpp new file mode 100644 index 0000000..f1327ab --- /dev/null +++ b/gameui/LogoFile.cpp @@ -0,0 +1,287 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined( _X360 ) +#include <windows.h> +#endif +#include <stdio.h> +#include "tier1/utlbuffer.h" +#include <vgui/VGUI.h> +#include <vgui_controls/Controls.h> +#include "filesystem.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define TYP_LUMPY 64 // 64 + grab command number + +typedef struct +{ + char identification[4]; // should be WAD2 or 2DAW + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int disksize; + int size; // uncompressed + char type; + char compression; + char pad1, pad2; + char name[16]; // must be null terminated +} lumpinfo_t; + +typedef struct +{ + char name[16]; + unsigned width, height; + unsigned offsets[4]; // four mip maps stored +} miptex_t; + +unsigned char pixdata[256]; + +float linearpalette[256][3]; +float d_red, d_green, d_blue; +int colors_used; +int color_used[256]; +float maxdistortion; +unsigned char palLogo[768]; + +/* +============= +AveragePixels +============= +*/ +unsigned char AveragePixels (int count) +{ + return pixdata[0]; +} + +/* +============== +GrabMip + +filename MIP x y width height +must be multiples of sixteen +============== +*/ +int GrabMip ( HANDLE hdib, unsigned char *lump_p, char *lumpname, COLORREF crf, int *width, int *height) +{ + int i,x,y,xl,yl,xh,yh,w,h; + unsigned char *screen_p, *source; + miptex_t *qtex; + int miplevel, mipstep; + int xx, yy; + int count; + int byteimagewidth, byteimageheight; + unsigned char *byteimage; + LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0) + + /* get pointer to BITMAPINFO (Win 3.0) */ + lpbmi = (LPBITMAPINFO)::GlobalLock((HGLOBAL)hdib); + unsigned char *lump_start = lump_p; + + xl = yl = 0; + w = lpbmi->bmiHeader.biWidth; + h = lpbmi->bmiHeader.biHeight; + + *width = w; + *height = h; + + byteimage = (unsigned char *)((LPSTR)lpbmi + sizeof( BITMAPINFOHEADER ) + 256 * sizeof( RGBQUAD ) ); + + if ( (w & 15) || (h & 15) ) + return 0; //Error ("line %i: miptex sizes must be multiples of 16", scriptline); + + xh = xl+w; + yh = yl+h; + + qtex = (miptex_t *)lump_p; + qtex->width = (unsigned)(w); + qtex->height = (unsigned)(h); + Q_strncpy (qtex->name, lumpname, sizeof( qtex->name) ); + + lump_p = (unsigned char *)&qtex->offsets[4]; + + byteimagewidth = w; + byteimageheight = h; + + source = (unsigned char *)lump_p; + qtex->offsets[0] = (unsigned)((unsigned char *)lump_p - (unsigned char *)qtex); + + // We're reading from a dib, so go bottom up + screen_p = byteimage + (h - 1) * w; + for (y=yl ; y<yh ; y++) + { + for (x=xl ; x<xh ; x++) + *lump_p++ = *screen_p++; + + screen_p -= 2 * w; + } + + // calculate gamma corrected linear palette + for (i = 0; i < 256; i++) + { + for (int j = 0; j < 3; j++) + { + float f = (float)(palLogo[i*3+j] / 255.0); + linearpalette[i][j] = f; //pow((double)f, 2); // assume textures are done at 2.2, we want to remap them at 1.0 + } + } + + maxdistortion = 0; + // assume palette full if it's a transparent texture + colors_used = 256; + for (i = 0; i < 256; i++) + color_used[i] = 1; + + + // + // subsample for greater mip levels + // + + for (miplevel = 1 ; miplevel<4 ; miplevel++) + { + d_red = d_green = d_blue = 0; // no distortion yet + qtex->offsets[miplevel] = (unsigned)(lump_p - (unsigned char *)qtex); + + mipstep = 1<<miplevel; + + for (y=0 ; y<h ; y+=mipstep) + { + for (x = 0 ; x<w ; x+= mipstep) + { + count = 0; + for (yy=0 ; yy<mipstep ; yy++) + { + for (xx=0 ; xx<mipstep ; xx++) + pixdata[count++] = source[(y+yy)*w + x + xx ]; + } + + *lump_p++ = AveragePixels (count); + } + } + } + + ::GlobalUnlock(lpbmi); + + // Write out palette in 16bit mode + *(unsigned short *) lump_p = 256; // palette size + lump_p += sizeof(short); + + memcpy(lump_p, &palLogo[0], 765); + lump_p += 765; + + *lump_p++ = (unsigned char)(crf & 0xFF); + + *lump_p++ = (unsigned char)((crf >> 8) & 0xFF); + + *lump_p++ = (unsigned char)((crf >> 16) & 0xFF); + + return lump_p - lump_start; +} + + +void UpdateLogoWAD( void *phdib, int r, int g, int b ) +{ + char logoname[ 32 ]; + char *pszName; + Q_strncpy( logoname, "LOGO", sizeof( logoname ) ); + pszName = &logoname[ 0 ]; + + HANDLE hdib = (HANDLE)phdib; + COLORREF crf = RGB( r, g, b ); + + if ((!pszName) || (pszName[0] == 0) || (hdib == NULL)) + return; + // Generate lump + + unsigned char *buf = (unsigned char *)_alloca( 16384 ); + + CUtlBuffer buffer( 0, 16384 ); + + int width, height; + + int length = GrabMip (hdib, buf, pszName, crf, &width, &height); + if ( length == 0 ) + { + return; + } + + bool sizevalid = false; + + if ( width == height ) + { + if ( width == 16 || + width == 32 || + width == 64 ) + { + sizevalid = true; + } + } + + if ( !sizevalid ) + return; + + while (length & 3) + length++; + + // Write Header + wadinfo_t header; + header.identification[0] = 'W'; + header.identification[1] = 'A'; + header.identification[2] = 'D'; + header.identification[3] = '3'; + header.numlumps = 1; + header.infotableofs = 0; + + buffer.Put( &header, sizeof( wadinfo_t ) ); + + // Fill Ino info table + lumpinfo_t info; + Q_memset (&info, 0, sizeof(info)); + Q_strncpy(info.name, pszName, sizeof( info.name ) ); + info.filepos = (int)sizeof(wadinfo_t); + info.size = info.disksize = length; + info.type = TYP_LUMPY; + info.compression = 0; + + // Write Lump + buffer.Put( buf, length ); + + // Write info table + buffer.Put( &info, sizeof( lumpinfo_t ) ); + + int savepos = buffer.TellPut(); + + buffer.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); + + header.infotableofs = length + sizeof(wadinfo_t); + + buffer.Put( &header, sizeof( wadinfo_t ) ); + + buffer.SeekPut( CUtlBuffer::SEEK_HEAD, savepos ); + + // Output to file + FileHandle_t file; + file = g_pFullFileSystem->Open( "pldecal.wad", "wb" ); + if ( file != FILESYSTEM_INVALID_HANDLE ) + { + g_pFullFileSystem->Write( buffer.Base(), buffer.TellPut(), file ); + g_pFullFileSystem->Close( file ); + } + +}
\ No newline at end of file diff --git a/gameui/ModInfo.cpp b/gameui/ModInfo.cpp new file mode 100644 index 0000000..d61fd13 --- /dev/null +++ b/gameui/ModInfo.cpp @@ -0,0 +1,251 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include "ModInfo.h" +#include "KeyValues.h" +#include "vgui_controls/Controls.h" +#include "filesystem.h" +#include "EngineInterface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +CModInfo &ModInfo() +{ + static CModInfo s_ModInfo; + return s_ModInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CModInfo::CModInfo() +{ + m_pModData = new KeyValues("ModData"); + m_wcsGameTitle[0] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CModInfo::~CModInfo() +{ + FreeModInfo(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModInfo::FreeModInfo() +{ + if (m_pModData) + { + m_pModData->deleteThis(); + m_pModData = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::IsMultiplayerOnly() +{ + return (stricmp(m_pModData->GetString("type", ""), "multiplayer_only") == 0); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::IsSinglePlayerOnly() +{ +#ifndef _XBOX + return (stricmp(m_pModData->GetString("type", ""), "singleplayer_only") == 0); +#else + // xboxissue - no support for disparate mounted content + return true; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *CModInfo::GetFallbackDir() +{ + return m_pModData->GetString("fallback_dir", ""); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const wchar_t *CModInfo::GetGameTitle() +{ + if (!m_wcsGameTitle[0]) + { + // for some reason, the standard ILocalize::ConvertANSIToUnicode() strips off + // the '�' character in 'HALF-LIFE�' - so just do a straight upconvert to unicode + const char *title = m_pModData->GetString("title", ""); + int i = 0; + for (; title[i] != 0; ++i) + { + m_wcsGameTitle[i] = (wchar_t)title[i]; + } + m_wcsGameTitle[i] = 0; + } + + return m_wcsGameTitle; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const wchar_t *CModInfo::GetGameTitle2() +{ + if (!m_wcsGameTitle2[0]) + { + // for some reason, the standard ILocalize::ConvertANSIToUnicode() strips off + // the '�' character in 'HALF-LIFE�' - so just do a straight upconvert to unicode + const char *title2 = m_pModData->GetString("title2", ""); + int i = 0; + for (; title2[i] != 0; ++i) + { + m_wcsGameTitle2[i] = (wchar_t)title2[i]; + } + m_wcsGameTitle2[i] = 0; + } + + return m_wcsGameTitle2; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *CModInfo::GetGameName() +{ + return m_pModData->GetString("game", ""); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +KeyValues *CModInfo::GetHiddenMaps() +{ + return m_pModData->FindKey( "hidden_maps" ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::HasPortals() +{ + return (stricmp(m_pModData->GetString("hasportals", "0"), "1") == 0); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::HasHDContent() +{ + return (stricmp(m_pModData->GetString("hashdcontent", "0"), "1") == 0); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::NoDifficulty() +{ + return (stricmp(m_pModData->GetString("nodifficulty", "0"), "1") == 0); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::NoModels() +{ + return (stricmp(m_pModData->GetString("nomodels", "0"), "1") == 0); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::NoHiModel() +{ + return (stricmp(m_pModData->GetString("nohimodel", "0"), "1") == 0); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::NoCrosshair() +{ + return (stricmp(m_pModData->GetString("nocrosshair", "1"), "1") == 0); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::AdvCrosshair() +{ + return ( m_pModData->GetInt( "advcrosshair" ) > 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int CModInfo::AdvCrosshairLevel() +{ + return m_pModData->GetInt( "advcrosshair" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModInfo::LoadCurrentGameInfo() +{ + // Load up gameinfo for the current mod + char const *filename = "gameinfo.txt"; + m_pModData->LoadFromFile( g_pFullFileSystem, filename ); +} + +//----------------------------------------------------------------------------- +// Purpose: loads file from null-terminated buffer +//----------------------------------------------------------------------------- +void CModInfo::LoadGameInfoFromBuffer( const char *buffer ) +{ + // Load up gameinfo.txt for the current mod + m_pModData->LoadFromBuffer( "", buffer ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::UseGameLogo() +{ + return ( Q_stricmp( m_pModData->GetString( "gamelogo", "0" ), "1" ) == 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::UseBots() +{ + return ( Q_stricmp( m_pModData->GetString( "bots", "0" ), "1" ) == 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CModInfo::SupportsVR() +{ + return (m_pModData->GetInt( "supportsvr" ) > 0); +} diff --git a/gameui/ModInfo.h b/gameui/ModInfo.h new file mode 100644 index 0000000..c2d71e1 --- /dev/null +++ b/gameui/ModInfo.h @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MODINFO_H +#define MODINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> + +class KeyValues; + +//----------------------------------------------------------------------------- +// Purpose: contains all the data entered about a mod in gameinfo.txt +//----------------------------------------------------------------------------- +class CModInfo +{ +public: + CModInfo(); + ~CModInfo(); + void FreeModInfo(); + + // loads mod info from gameinfo.txt + void LoadCurrentGameInfo(); + + // loads gameinfo from null-terminated string + void LoadGameInfoFromBuffer( const char *buffer ); + + // data accessors + const wchar_t *GetGameTitle(); + const wchar_t *GetGameTitle2(); + const char *GetGameName(); + + bool IsMultiplayerOnly(); + bool IsSinglePlayerOnly(); + + bool HasPortals(); + + bool NoDifficulty(); + bool NoModels(); + bool NoHiModel(); + bool NoCrosshair(); + bool AdvCrosshair(); + int AdvCrosshairLevel(); + const char *GetFallbackDir(); + bool UseGameLogo(); + bool UseBots(); + bool HasHDContent(); + bool SupportsVR(); + + KeyValues *GetHiddenMaps(); + +private: + wchar_t m_wcsGameTitle[128]; + wchar_t m_wcsGameTitle2[128]; + KeyValues *m_pModData; +}; + + +// singleton accessor +extern CModInfo &ModInfo(); + +#endif // MODINFO_H diff --git a/gameui/MouseMessageForwardingPanel.cpp b/gameui/MouseMessageForwardingPanel.cpp new file mode 100644 index 0000000..b02583d --- /dev/null +++ b/gameui/MouseMessageForwardingPanel.cpp @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "MouseMessageForwardingPanel.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +CMouseMessageForwardingPanel::CMouseMessageForwardingPanel( Panel *parent, const char *name ) : BaseClass( parent, name ) +{ + // don't draw an + SetPaintEnabled(false); + SetPaintBackgroundEnabled(false); + SetPaintBorderEnabled(false); +} + +void CMouseMessageForwardingPanel::PerformLayout() +{ + // fill out the whole area + int w, t; + GetParent()->GetSize(w, t); + SetBounds(0, 0, w, t); +} + +void CMouseMessageForwardingPanel::OnMousePressed( vgui::MouseCode code ) +{ + CallParentFunction( new KeyValues("MousePressed", "code", code) ); +} + +void CMouseMessageForwardingPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + CallParentFunction( new KeyValues("MouseDoublePressed", "code", code) ); +}
\ No newline at end of file diff --git a/gameui/MouseMessageForwardingPanel.h b/gameui/MouseMessageForwardingPanel.h new file mode 100644 index 0000000..604fe6b --- /dev/null +++ b/gameui/MouseMessageForwardingPanel.h @@ -0,0 +1,29 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MOUSEMESSAGEFORWARDINGPANEL_H +#define MOUSEMESSAGEFORWARDINGPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Panel.h" + +//----------------------------------------------------------------------------- +// Purpose: Invisible panel that forwards up mouse movement +//----------------------------------------------------------------------------- +class CMouseMessageForwardingPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CMouseMessageForwardingPanel, vgui::Panel ); +public: + CMouseMessageForwardingPanel( Panel *parent, const char *name ); + + virtual void PerformLayout( void ); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void OnMouseDoublePressed( vgui::MouseCode code ); +}; + +#endif //MOUSEMESSAGEFORWARDINGPANEL_H
\ No newline at end of file diff --git a/gameui/MultiplayerAdvancedDialog.cpp b/gameui/MultiplayerAdvancedDialog.cpp new file mode 100644 index 0000000..bbc7133 --- /dev/null +++ b/gameui/MultiplayerAdvancedDialog.cpp @@ -0,0 +1,431 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <time.h> + +#include "MultiplayerAdvancedDialog.h" + +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui_controls/ListPanel.h> +#include <KeyValues.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/MessageBox.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/TextEntry.h> +#include "cvarslider.h" +#include "PanelListPanel.h" +#include <vgui/IInput.h> +#include <steam/steam_api.h> +#include "EngineInterface.h" +#include "fmtstr.h" + +#include "filesystem.h" + +#include <tier0/vcrmode.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define OPTIONS_DIR "cfg" +#define DEFAULT_OPTIONS_FILE OPTIONS_DIR "/user_default.scr" +#define OPTIONS_FILE OPTIONS_DIR "/user.scr" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMultiplayerAdvancedDialog::CMultiplayerAdvancedDialog(vgui::Panel *parent) : BaseClass(NULL, "MultiplayerAdvancedDialog") +{ + SetBounds(0, 0, 372, 160); + SetSizeable( false ); + + SetTitle("#GameUI_MultiplayerAdvanced", true); + + Button *cancel = new Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + Button *ok = new Button( this, "OK", "#GameUI_OK" ); + ok->SetCommand( "Ok" ); + + m_pListPanel = new CPanelListPanel( this, "PanelListPanel" ); + + m_pList = NULL; + + m_pDescription = new CInfoDescription(); + m_pDescription->InitFromFile( DEFAULT_OPTIONS_FILE ); + m_pDescription->InitFromFile( OPTIONS_FILE ); + m_pDescription->TransferCurrentValues( NULL ); + + LoadControlSettings("Resource\\MultiplayerAdvancedDialog.res"); + CreateControls(); + + MoveToCenterOfScreen(); + SetSizeable( false ); + SetDeleteSelfOnClose( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMultiplayerAdvancedDialog::~CMultiplayerAdvancedDialog() +{ + delete m_pDescription; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::Activate() +{ + BaseClass::Activate(); + input()->SetAppModalSurface(GetVPanel()); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::OnClose() +{ + BaseClass::OnClose(); + MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *command - +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "Ok" ) ) + { + // OnApplyChanges(); + SaveValues(); + OnClose(); + return; + } + + BaseClass::OnCommand( command ); +} + +void CMultiplayerAdvancedDialog::OnKeyCodeTyped(KeyCode code) +{ + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::GatherCurrentValues() +{ + if ( !m_pDescription ) + return; + + // OK + CheckButton *pBox; + TextEntry *pEdit; + ComboBox *pCombo; + CCvarSlider *pSlider; + + mpcontrol_t *pList; + + CScriptObject *pObj; + CScriptListItem *pItem; + + char szValue[256]; + char strValue[ 256 ]; + + pList = m_pList; + while ( pList ) + { + pObj = pList->pScrObj; + + if ( pObj->type == O_CATEGORY ) + { + pList = pList->next; + continue; + } + + if ( !pList->pControl ) + { + pObj->SetCurValue( pObj->defValue ); + pList = pList->next; + continue; + } + + switch ( pObj->type ) + { + case O_BOOL: + pBox = (CheckButton *)pList->pControl; + sprintf( szValue, "%s", pBox->IsSelected() ? "1" : "0" ); + break; + case O_NUMBER: + pEdit = ( TextEntry * )pList->pControl; + pEdit->GetText( strValue, sizeof( strValue ) ); + sprintf( szValue, "%s", strValue ); + break; + case O_STRING: + pEdit = ( TextEntry * )pList->pControl; + pEdit->GetText( strValue, sizeof( strValue ) ); + sprintf( szValue, "%s", strValue ); + break; + case O_LIST: + { + pCombo = (ComboBox *)pList->pControl; + // pCombo->GetText( strValue, sizeof( strValue ) ); + int activeItem = pCombo->GetActiveItem(); + + pItem = pObj->pListItems; + // int n = (int)pObj->fdefValue; + + while ( pItem ) + { + if (!activeItem--) + break; + + pItem = pItem->pNext; + } + + if ( pItem ) + { + sprintf( szValue, "%s", pItem->szValue ); + } + else // Couln't find index + { + //assert(!("Couldn't find string in list, using default value")); + sprintf( szValue, "%s", pObj->defValue ); + } + break; + } + case O_SLIDER: + pSlider = ( CCvarSlider * )pList->pControl; + sprintf( szValue, "%.2f", pSlider->GetSliderValue() ); + break; + } + + // Remove double quotes and % characters + UTIL_StripInvalidCharacters( szValue, sizeof(szValue) ); + + strcpy( strValue, szValue ); + + pObj->SetCurValue( strValue ); + + pList = pList->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::CreateControls() +{ + DestroyControls(); + + // Go through desciption creating controls + CScriptObject *pObj; + + pObj = m_pDescription->pObjList; + + // Build out the clan dropdown + CScriptObject *pClanObj = m_pDescription->FindObject( "cl_clanid" ); + ISteamFriends *pFriends = steamapicontext->SteamFriends(); + if ( pFriends && pClanObj ) + { + pClanObj->RemoveAndDeleteAllItems(); + int iGroupCount = pFriends->GetClanCount(); + pClanObj->AddItem( new CScriptListItem( "#Cstrike_ClanTag_None", "0" ) ); + for ( int k = 0; k < iGroupCount; ++ k ) + { + CSteamID clanID = pFriends->GetClanByIndex( k ); + const char *pName = pFriends->GetClanName( clanID ); + const char *pTag = pFriends->GetClanTag( clanID ); + + char id[12]; + Q_snprintf( id, sizeof( id ), "%d", clanID.GetAccountID() ); + pClanObj->AddItem( new CScriptListItem( CFmtStr( "%s (%s)", pTag, pName ), id ) ); + } + } + + mpcontrol_t *pCtrl; + + CheckButton *pBox; + TextEntry *pEdit; + ComboBox *pCombo; + CCvarSlider *pSlider; + CScriptListItem *pListItem; + + Panel *objParent = m_pListPanel; + + while ( pObj ) + { + if ( pObj->type == O_OBSOLETE || pObj->type == O_CATEGORY ) + { + pObj = pObj->pNext; + continue; + } + + pCtrl = new mpcontrol_t( objParent, "mpcontrol_t" ); + pCtrl->type = pObj->type; + + switch ( pCtrl->type ) + { + case O_BOOL: + pBox = new CheckButton( pCtrl, "DescCheckButton", pObj->prompt ); + pBox->SetSelected( pObj->fdefValue != 0.0f ? true : false ); + + pCtrl->pControl = (Panel *)pBox; + break; + case O_STRING: + case O_NUMBER: + pEdit = new TextEntry( pCtrl, "DescTextEntry"); + pEdit->InsertString(pObj->defValue); + pCtrl->pControl = (Panel *)pEdit; + break; + case O_LIST: + { + pCombo = new ComboBox( pCtrl, "DescComboBox", 5, false ); + + // track which row matches the current value + int iRow = -1; + int iCount = 0; + pListItem = pObj->pListItems; + while ( pListItem ) + { + if ( iRow == -1 && !Q_stricmp( pListItem->szValue, pObj->curValue ) ) + iRow = iCount; + + pCombo->AddItem( pListItem->szItemText, NULL ); + pListItem = pListItem->pNext; + ++iCount; + } + + + pCombo->ActivateItemByRow( iRow ); + + pCtrl->pControl = (Panel *)pCombo; + } + break; + case O_SLIDER: + pSlider = new CCvarSlider( pCtrl, "DescSlider", "Test", pObj->fMin, pObj->fMax, pObj->cvarname, false ); + pCtrl->pControl = (Panel *)pSlider; + break; + default: + break; + } + + if ( pCtrl->type != O_BOOL ) + { + pCtrl->pPrompt = new vgui::Label( pCtrl, "DescLabel", "" ); + pCtrl->pPrompt->SetContentAlignment( vgui::Label::a_west ); + pCtrl->pPrompt->SetTextInset( 5, 0 ); + pCtrl->pPrompt->SetText( pObj->prompt ); + } + + pCtrl->pScrObj = pObj; + + switch ( pCtrl->type ) + { + case O_BOOL: + case O_STRING: + case O_NUMBER: + case O_LIST: + pCtrl->SetSize( 100, 28 ); + break; + case O_SLIDER: + pCtrl->SetSize( 100, 40 ); + break; + default: + break; + } + //pCtrl->SetBorder( scheme()->GetBorder(1, "DepressedButtonBorder") ); + m_pListPanel->AddItem( pCtrl ); + + // Link it in + if ( !m_pList ) + { + m_pList = pCtrl; + pCtrl->next = NULL; + } + else + { + mpcontrol_t *p; + p = m_pList; + while ( p ) + { + if ( !p->next ) + { + p->next = pCtrl; + pCtrl->next = NULL; + break; + } + p = p->next; + } + } + + pObj = pObj->pNext; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::DestroyControls() +{ + mpcontrol_t *p, *n; + + p = m_pList; + while ( p ) + { + n = p->next; + // + delete p->pControl; + delete p->pPrompt; + delete p; + p = n; + } + + m_pList = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiplayerAdvancedDialog::SaveValues() +{ + // Get the values from the controls: + GatherCurrentValues(); + + // Create the game.cfg file + if ( m_pDescription ) + { + FileHandle_t fp; + + // Add settings to config.cfg + m_pDescription->WriteToConfig(); + + g_pFullFileSystem->CreateDirHierarchy( OPTIONS_DIR ); + fp = g_pFullFileSystem->Open( OPTIONS_FILE, "wb" ); + if ( fp ) + { + m_pDescription->WriteToScriptFile( fp ); + g_pFullFileSystem->Close( fp ); + } + } +} + diff --git a/gameui/MultiplayerAdvancedDialog.h b/gameui/MultiplayerAdvancedDialog.h new file mode 100644 index 0000000..7465173 --- /dev/null +++ b/gameui/MultiplayerAdvancedDialog.h @@ -0,0 +1,52 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MULTIPLAYERADVANCEDDIALOG_H +#define MULTIPLAYERADVANCEDDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include "scriptobject.h" +#include <vgui/KeyCode.h> + +class CPanelListPanel; + +//----------------------------------------------------------------------------- +// Purpose: Displays a game-specific list of options +//----------------------------------------------------------------------------- +class CMultiplayerAdvancedDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CMultiplayerAdvancedDialog, vgui::Frame ); + +public: + CMultiplayerAdvancedDialog(vgui::Panel *parent); + ~CMultiplayerAdvancedDialog(); + + virtual void Activate(); + +private: + + void CreateControls(); + void DestroyControls(); + void GatherCurrentValues(); + void SaveValues(); + + CInfoDescription *m_pDescription; + + mpcontrol_t *m_pList; + + CPanelListPanel *m_pListPanel; + + virtual void OnCommand( const char *command ); + virtual void OnClose(); + virtual void OnKeyCodeTyped(vgui::KeyCode code); +}; + + +#endif // MULTIPLAYERADVANCEDDIALOG_H diff --git a/gameui/NewGameDialog.cpp b/gameui/NewGameDialog.cpp new file mode 100644 index 0000000..2d76861 --- /dev/null +++ b/gameui/NewGameDialog.cpp @@ -0,0 +1,1724 @@ +//========= 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 <vgui/ISystem.h> +#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 <stdio.h> + +// 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<ImagePanel*>( 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<vgui::Label*>( 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<CBitmapImagePanel*>( 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<CBitmapImagePanel*>( FindChildByName( "ChallengeEarnedMedal" ) ); + pBitmap->SetVisible( true ); + pBitmap->setTexture( szBuff ); + } + else + { + CBitmapImagePanel *pBitmap = dynamic_cast<CBitmapImagePanel*>( 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<CBitmapImagePanel*>( 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 <esc> to get out of the explanation dialog. + if ( GameUI().IsInMultiplayer() ) + { + engine->ExecuteClientCmd( "disconnect" ); + } + + DHANDLE<CCommentaryExplanationDialog> 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(); +} diff --git a/gameui/NewGameDialog.h b/gameui/NewGameDialog.h new file mode 100644 index 0000000..03aebd4 --- /dev/null +++ b/gameui/NewGameDialog.h @@ -0,0 +1,155 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef NEWGAMEDIALOG_H +#define NEWGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "vgui_controls/KeyRepeat.h" +#include "utlvector.h" + +class CGameChapterPanel; +class CSkillSelectionDialog; + +// Slot indices in new game menu +#define INVALID_INDEX -1 +#define SLOT_OFFLEFT 0 +#define SLOT_LEFT 1 +#define SLOT_CENTER 2 +#define SLOT_RIGHT 3 +#define SLOT_OFFRIGHT 4 +#define NUM_SLOTS 5 + + +class CNewGamePlayButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CNewGamePlayButton, vgui::Button ); + +public: + + CNewGamePlayButton( Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget=NULL, const char *pCmd=NULL ) + : vgui::Button( parent, panelName, text, pActionSignalTarget, pCmd ) + { + } + + void OnKeyCodePressed( vgui::KeyCode code ) + { + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + ConVarRef var( "joystick" ); + if ( var.IsValid() && !var.GetBool() ) + { + var.SetValue( true ); + } + + ConVarRef var2( "hud_fastswitch" ); + if ( var2.IsValid() && var2.GetInt() != 2 ) + { + var2.SetValue( 2 ); + } + DoClick(); + return; + } + + BaseClass::OnKeyCodePressed( code ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Handles starting a new game, skill and chapter selection +//----------------------------------------------------------------------------- +class CNewGameDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CNewGameDialog, vgui::Frame ); + +public: + MESSAGE_FUNC( FinishScroll, "FinishScroll" ); + MESSAGE_FUNC( StartGame, "StartGame" ); + + CNewGameDialog(vgui::Panel *parent, bool bCommentaryMode ); + ~CNewGameDialog(); + + virtual void Activate( void ); + + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + virtual void OnClose( void ); + virtual void PaintBackground(); + void SetSelectedChapterIndex( int index ); + void SetSelectedChapter( const char *chapter ); + void UpdatePanelLockedStatus( int iUnlockedChapter, int i, CGameChapterPanel *pChapterPanel ); + + void SetCommentaryMode( bool bCommentary ) { m_bCommentaryMode = bCommentary; } + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnKeyCodeReleased( vgui::KeyCode code ); + virtual void OnThink(); + + // Xbox: Defined values are also used to shift the slot indices + enum EScrollDirection + { + SCROLL_RIGHT = -1, + SCROLL_NONE = 0, + SCROLL_LEFT = 1 + }; + EScrollDirection m_ScrollDirection; + +private: + int m_iSelectedChapter; + + CUtlVector<CGameChapterPanel *> m_ChapterPanels; + + vgui::DHANDLE<CSkillSelectionDialog> m_hSkillSelectionDialog; + + vgui::Button *m_pPlayButton; + vgui::Button *m_pNextButton; + vgui::Button *m_pPrevButton; + vgui::Panel *m_pCenterBg; + vgui::Label *m_pChapterTitleLabels[2]; + vgui::Label *m_pBonusSelection; + vgui::ImagePanel *m_pBonusSelectionBorder; + CFooterPanel *m_pFooter; + bool m_bCommentaryMode; + vgui::Label *m_pCommentaryLabel; + + // Xbox + void ScrollSelectionPanels( EScrollDirection dir ); + void ScrollBonusSelection( EScrollDirection dir ); + void PreScroll( EScrollDirection dir ); + void PostScroll( EScrollDirection dir ); + void SetFastScroll( bool fast ); + void ContinueScrolling( void ); + void AnimateSelectionPanels( void ); + void ShiftPanelIndices( int offset ); + bool IsValidPanel( const int idx ); + void InitPanelIndexForDisplay( const int idx ); + void UpdateMenuComponents( EScrollDirection dir ); + void UpdateBonusSelection( void ); + + int m_PanelXPos[ NUM_SLOTS ]; + int m_PanelYPos[ NUM_SLOTS ]; + float m_PanelAlpha[ NUM_SLOTS ]; + int m_PanelIndex[ NUM_SLOTS ]; + float m_ScrollSpeed; + int m_ButtonPressed; + int m_ScrollCt; + bool m_bScrolling; + char m_ActiveTitleIdx; + bool m_bMapStarting; + int m_iBonusSelection; + bool m_bScrollToFirstBonusMap; + + struct BonusMapDescription_t *m_pBonusMapDescription; + + vgui::CKeyRepeatHandler m_KeyRepeat; +}; + +#endif // NEWGAMEDIALOG_H diff --git a/gameui/OptionsDialog.cpp b/gameui/OptionsDialog.cpp new file mode 100644 index 0000000..b1dedfe --- /dev/null +++ b/gameui/OptionsDialog.cpp @@ -0,0 +1,155 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BasePanel.h" +#include "OptionsDialog.h" + +#include "vgui_controls/Button.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/QueryBox.h" + +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" + +#include "KeyValues.h" +#include "OptionsSubKeyboard.h" +#include "OptionsSubMouse.h" +#include "OptionsSubAudio.h" +#include "OptionsSubVideo.h" +#include "OptionsSubVoice.h" +#include "OptionsSubMultiplayer.h" +#include "OptionsSubDifficulty.h" +#include "OptionsSubPortal.h" +#ifdef WIN32 +// NVNT haptic configuration dialog +#include "OptionsSubHaptics.h" +#endif +#include "ModInfo.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: Basic help dialog +//----------------------------------------------------------------------------- +COptionsDialog::COptionsDialog(vgui::Panel *parent) : PropertyDialog(parent, "OptionsDialog") +{ + SetDeleteSelfOnClose(true); + SetBounds(0, 0, 512, 406); + SetSizeable( false ); + + SetTitle("#GameUI_Options", true); + + // debug timing code, this function takes too long +// double s4 = system()->GetCurrentTime(); + +#if defined( WIN32 ) && !defined( _X360 ) + // NVNT START see if the user has a haptic device via convar. if so create haptics dialog. + ConVarRef checkHap("hap_HasDevice"); + checkHap.Init("hap_HasDevice",true); + if(checkHap.GetBool()) + { + AddPage(new COptionsSubHaptics(this), "#GameUI_Haptics_TabTitle"); + } + // NVNT END +#endif + if (ModInfo().IsSinglePlayerOnly() && !ModInfo().NoDifficulty()) + { + AddPage(new COptionsSubDifficulty(this), "#GameUI_Difficulty"); + } + + if (ModInfo().HasPortals()) + { + AddPage(new COptionsSubPortal(this), "#GameUI_Portal"); + } + + AddPage(new COptionsSubKeyboard(this), "#GameUI_Keyboard"); + AddPage(new COptionsSubMouse(this), "#GameUI_Mouse"); + + m_pOptionsSubAudio = new COptionsSubAudio(this); + AddPage(m_pOptionsSubAudio, "#GameUI_Audio"); + m_pOptionsSubVideo = new COptionsSubVideo(this); + AddPage(m_pOptionsSubVideo, "#GameUI_Video"); + + if ( !ModInfo().IsSinglePlayerOnly() ) + { + AddPage(new COptionsSubVoice(this), "#GameUI_Voice"); + } + + // add the multiplay page last, if we're combo single/multi or just multi + if ( (ModInfo().IsMultiplayerOnly() && !ModInfo().IsSinglePlayerOnly()) || + (!ModInfo().IsMultiplayerOnly() && !ModInfo().IsSinglePlayerOnly()) ) + { + m_pOptionsSubMultiplayer = new COptionsSubMultiplayer(this); + AddPage(m_pOptionsSubMultiplayer, "#GameUI_Multiplayer"); + } + +// double s5 = system()->GetCurrentTime(); +// Msg("COptionsDialog::COptionsDialog(): %.3fms\n", (float)(s5 - s4) * 1000.0f); + + SetApplyButtonVisible(true); + GetPropertySheet()->SetTabWidth(84); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +COptionsDialog::~COptionsDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Brings the dialog to the fore +//----------------------------------------------------------------------------- +void COptionsDialog::Activate() +{ + BaseClass::Activate(); + EnableApplyButton(false); +} + +void COptionsDialog::OnKeyCodePressed( KeyCode code ) +{ + switch ( GetBaseButtonCode( code ) ) + { + case KEY_XBUTTON_B: + OnCommand( "Cancel" ); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: Opens the dialog +//----------------------------------------------------------------------------- +void COptionsDialog::Run() +{ + SetTitle("#GameUI_Options", true); + Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the GameUI is hidden +//----------------------------------------------------------------------------- +void COptionsDialog::OnGameUIHidden() +{ + // tell our children about it + for ( int i = 0 ; i < GetChildCount() ; i++ ) + { + Panel *pChild = GetChild( i ); + if ( pChild ) + { + PostMessage( pChild, new KeyValues( "GameUIHidden" ) ); + } + } +} diff --git a/gameui/OptionsDialog.h b/gameui/OptionsDialog.h new file mode 100644 index 0000000..c21baa9 --- /dev/null +++ b/gameui/OptionsDialog.h @@ -0,0 +1,141 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PropertyDialog.h" +#include "vgui_controls/KeyRepeat.h" + +//----------------------------------------------------------------------------- +// Purpose: Holds all the game option pages +//----------------------------------------------------------------------------- +class COptionsDialog : public vgui::PropertyDialog +{ + DECLARE_CLASS_SIMPLE( COptionsDialog, vgui::PropertyDialog ); + +public: + COptionsDialog(vgui::Panel *parent); + ~COptionsDialog(); + + void Run(); + virtual void Activate(); + + void OnKeyCodePressed( vgui::KeyCode code ); + + vgui::PropertyPage* GetOptionsSubMultiplayer( void ) { return m_pOptionsSubMultiplayer; } + + MESSAGE_FUNC( OnGameUIHidden, "GameUIHidden" ); // called when the GameUI is hidden + +private: + class COptionsSubAudio *m_pOptionsSubAudio; + class COptionsSubVideo *m_pOptionsSubVideo; + vgui::PropertyPage *m_pOptionsSubMultiplayer; +}; + + +#define OPTIONS_MAX_NUM_ITEMS 15 + +struct OptionData_t; + +//----------------------------------------------------------------------------- +// Purpose: Holds all the game option pages +//----------------------------------------------------------------------------- +class COptionsDialogXbox : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsDialogXbox, vgui::Frame ); + +public: + COptionsDialogXbox( vgui::Panel *parent, bool bControllerOptions = false ); + ~COptionsDialogXbox(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void OnClose(); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnCommand(const char *command); + + virtual void OnKeyCodeReleased( vgui::KeyCode code); + virtual void OnThink(); + +private: + void HandleInactiveKeyCodePressed( vgui::KeyCode code ); + void HandleActiveKeyCodePressed( vgui::KeyCode code ); + void HandleBindKeyCodePressed( vgui::KeyCode code ); + + int GetSelectionLabel( void ) { return m_iSelection - m_iScroll; } + + void ActivateSelection( void ); + void DeactivateSelection( void ); + + void ChangeSelection( int iChange ); + void UpdateFooter( void ); + void UpdateSelection( void ); + void UpdateScroll( void ); + + void UncacheChoices( void ); + void GetChoiceFromConvar( OptionData_t *pOption ); + void ChangeValue( float fChange ); + void UnbindOption( OptionData_t *pOption, int iLabel ); + + void UpdateValue( OptionData_t *pOption, int iLabel ); + void UpdateBind( OptionData_t *pOption, int iLabel, ButtonCode_t codeIgnore = BUTTON_CODE_INVALID, ButtonCode_t codeAdd = BUTTON_CODE_INVALID ); + void UpdateAllBinds( ButtonCode_t code ); + + void FillInDefaultBindings( void ); + + bool ShouldSkipOption( KeyValues *pKey ); + void ReadOptionsFromFile( const char *pchFileName ); + void SortOptions( void ); + + void InitializeSliderDefaults( void ); + +private: + bool m_bControllerOptions; + bool m_bOptionsChanged; + bool m_bOldForceEnglishAudio; + + CFooterPanel *m_pFooter; + + CUtlVector<OptionData_t*> *m_pOptions; + + bool m_bSelectionActive; + OptionData_t *m_pSelectedOption; + + int m_iSelection; + int m_iScroll; + int m_iSelectorYStart; + int m_iOptionSpacing; + int m_iNumItems; + + int m_iXAxisState; + int m_iYAxisState; + float m_fNextChangeTime; + + vgui::Panel *m_pOptionsSelectionLeft; + vgui::Panel *m_pOptionsSelectionLeft2; + vgui::Label *m_pOptionsUpArrow; + vgui::Label *m_pOptionsDownArrow; + + vgui::Label *(m_pOptionLabels[ OPTIONS_MAX_NUM_ITEMS ]); + vgui::Label *(m_pValueLabels[ OPTIONS_MAX_NUM_ITEMS ]); + vgui::AnalogBar *(m_pValueBars[ OPTIONS_MAX_NUM_ITEMS ]); + + vgui::HFont m_hLabelFont; + vgui::HFont m_hButtonFont; + + Color m_SelectedColor; + + vgui::CKeyRepeatHandler m_KeyRepeat; + + int m_nButtonGap; +}; + +#endif // OPTIONSDIALOG_H diff --git a/gameui/OptionsDialog_Xbox.cpp b/gameui/OptionsDialog_Xbox.cpp new file mode 100644 index 0000000..9db9d0a --- /dev/null +++ b/gameui/OptionsDialog_Xbox.cpp @@ -0,0 +1,1683 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BasePanel.h" +#include "OptionsDialog.h" + +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" + +#include <vgui_controls/AnalogBar.h> + +#ifdef _X360 + #include "xbox/xbox_launch.h" +#endif +#include "IGameUIFuncs.h" +#include "GameUI_Interface.h" +#include "inputsystem/iinputsystem.h" +#include "EngineInterface.h" +#include "KeyValues.h" +#include "ModInfo.h" +#include "matchmaking/matchmakingbasepanel.h" +#include "vgui_controls/AnimationController.h" + +#include "tier1/utlbuffer.h" +#include "filesystem.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#define OPTION_STRING_LENGTH 64 + + +ConVar binds_per_command( "binds_per_command", "1", 0 ); + +ConVar x360_resolution_widescreen_mode( "x360_resolution_widescreen_mode", "0", 0, "This is only used for reference. Changing this value does nothing" ); +ConVar x360_resolution_width( "x360_resolution_width", "640", 0, "This is only used for reference. Changing this value does nothing" ); +ConVar x360_resolution_height( "x360_resolution_height", "480", 0, "This is only used for reference. Changing this value does nothing" ); +ConVar x360_resolution_interlaced( "x360_resolution_interlaced", "0", 0, "This is only used for reference. Changing this value does nothing" ); + +ConVar x360_audio_english("x360_audio_english", "0", 0, "Keeps track of whether we're forcing english in a localized language." ); + + +enum OptionType_e +{ + OPTION_TYPE_BINARY = 0, + OPTION_TYPE_SLIDER, + OPTION_TYPE_CHOICE, + OPTION_TYPE_BIND, + + OPTION_TYPE_TOTAL +}; + + +enum SliderHomeType_e +{ + SLIDER_HOME_NONE = 0, + SLIDER_HOME_PREV, + SLIDER_HOME_MIN, + SLIDER_HOME_CENTER, + SLIDER_HOME_MAX, + + SLIDER_HOME_TYPE_TOTAL +}; + + + +struct OptionChoiceData_t +{ + char szName[ OPTION_STRING_LENGTH ]; + char szValue[ OPTION_STRING_LENGTH ]; +}; + +struct OptionData_t +{ + char szName[ OPTION_STRING_LENGTH ]; + char szDisplayName[ OPTION_STRING_LENGTH ]; + + union + { + char szConvar[ OPTION_STRING_LENGTH ]; // Used by all types except binds + char szCommand[ OPTION_STRING_LENGTH ]; // Used exclusively by bind types + }; + + char szConvar2[ OPTION_STRING_LENGTH ]; // Used for choice types that use 2 convars + + char szConvarDef[ OPTION_STRING_LENGTH ]; + + int iPriority; + + OptionType_e eOptionType; + + bool bUnchangeable; + bool bVocalsLanguage; + + // Slider types exclusively use these + float fMinValue; + float fMaxValue; + float fIncValue; + float fSliderHomeValue; + + union + { + SliderHomeType_e eSliderHomeType; // Slider types exclusively use this int + int iCurrentChoice; // Choice types exclusively use this int + int iNumBinds; // Bind types exclusively use this int + }; + + CUtlVector<OptionChoiceData_t> m_Choices; +}; + + +class OptionsDataContainer +{ +public: + + ~OptionsDataContainer() + { + for ( int iOption = 0; iOption < m_pOptions.Count(); ++iOption ) + { + delete (m_pOptions[ iOption ]); + m_pOptions[ iOption ] = 0; + } + + for ( int iOption = 0; iOption < m_pControllerOptions.Count(); ++iOption ) + { + delete (m_pControllerOptions[ iOption ]); + m_pControllerOptions[ iOption ] = 0; + } + } + +public: + + CUtlVector<OptionData_t*> m_pOptions; + CUtlVector<OptionData_t*> m_pControllerOptions; + +}; + + +static OptionsDataContainer s_OptionsDataContainer; + +static CUtlVector<OptionChoiceData_t> s_DisabledOptions; + + +const char *UTIL_Parse( const char *data, char *token, int sizeofToken ); + + +bool ActionsAreTheSame( const char *pchAction1, const char *pchAction2 ) +{ + if ( Q_stricmp( pchAction1, pchAction2 ) == 0 ) + return true; + + if ( ( Q_stricmp( pchAction1, "+duck" ) == 0 || Q_stricmp( pchAction1, "toggle_duck" ) == 0 ) && + ( Q_stricmp( pchAction2, "+duck" ) == 0 || Q_stricmp( pchAction2, "toggle_duck" ) == 0 ) ) + { + // +duck and toggle_duck are interchangable + return true; + } + + if ( ( Q_stricmp( pchAction1, "+zoom" ) == 0 || Q_stricmp( pchAction1, "toggle_zoom" ) == 0 ) && + ( Q_stricmp( pchAction2, "+zoom" ) == 0 || Q_stricmp( pchAction2, "toggle_zoom" ) == 0 ) ) + { + // +zoom and toggle_zoom are interchangable + return true; + } + + return false; +} + + +COptionsDialogXbox::COptionsDialogXbox( vgui::Panel *parent, bool bControllerOptions ) : BaseClass( parent, "OptionsDialog" ) +{ +#ifdef _X360 + // Get out current resolution and stuff it into convars for later reference + XVIDEO_MODE videoMode; + XGetVideoMode( &videoMode ); + x360_resolution_widescreen_mode.SetValue( videoMode.fIsWideScreen ); + x360_resolution_width.SetValue( static_cast<int>( videoMode.dwDisplayWidth ) ); + x360_resolution_height.SetValue( static_cast<int>( videoMode.dwDisplayHeight ) ); + x360_resolution_interlaced.SetValue( videoMode.fIsInterlaced ); +#endif + + //Figure out which way duck is bound, and set the option_duck_method convar the correct way. + const char *pDuckKey = engine->Key_LookupBinding( "+duck" ); + ButtonCode_t code = g_pInputSystem->StringToButtonCode( pDuckKey ); + const char *pDuckMode = engine->Key_BindingForKey( code ); + + // NOW. If duck key is bound to +DUCK, set the convar to 0. Else, set it to 1. + ConVarRef varOption( "option_duck_method" ); + + if( pDuckMode != NULL ) + { + if( !Q_stricmp(pDuckMode,"+duck") ) + { + varOption.SetValue( 0 ); + } + else + { + varOption.SetValue( 1 ); + } + } + + SetSize( 32, 32 ); + SetDeleteSelfOnClose( true ); + SetTitleBarVisible( false ); + SetCloseButtonVisible( false ); + SetSizeable( false ); + + m_pFooter = new CFooterPanel( parent, "OptionsFooter" ); + m_pFooter->SetStandardDialogButtons(); + + m_bControllerOptions = bControllerOptions; + + m_bOptionsChanged = false; + + // Store the old vocal language setting + m_bOldForceEnglishAudio = x360_audio_english.GetBool(); + + // Get the correct options list + if ( !m_bControllerOptions ) + m_pOptions = &s_OptionsDataContainer.m_pOptions; + else + m_pOptions = &s_OptionsDataContainer.m_pControllerOptions; + + if ( m_pOptions->Count() == 0 ) + { + // Populate it if it's hasn't been filled + ReadOptionsFromFile( "scripts/mod_options.360.txt" ); + ReadOptionsFromFile( "scripts/options.360.txt" ); + SortOptions(); + } + + m_pSelectedOption = NULL; + + m_iSelection = 0; + m_iScroll = 0; + + m_iXAxisState = 0; + m_iYAxisState = 0; + m_fNextChangeTime = 0.0f; + + m_pOptionsSelectionLeft = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft" ) ); + m_pOptionsSelectionLeft2 = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft2" ) ); + m_pOptionsUpArrow = new vgui::Label( this, "UpArrow", "" ); + m_pOptionsDownArrow = new vgui::Label( this, "DownArrow", "" ); + + for ( int iLabel = 0; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel ) + { + char szLabelName[ 64 ]; + + Q_snprintf( szLabelName, sizeof( szLabelName), "OptionLabel%i", iLabel ); + m_pOptionLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" ); + + Q_snprintf( szLabelName, sizeof( szLabelName), "ValueLabel%i", iLabel ); + m_pValueLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" ); + + Q_snprintf( szLabelName, sizeof( szLabelName), "ValueBar%i", iLabel ); + m_pValueBars[ iLabel ] = new AnalogBar( this, szLabelName ); + } + + // Faster repeats for sideways + m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_LEFT, 0.08 ); + m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_RIGHT, 0.08 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::InitializeSliderDefaults( void ) +{ + for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption ) + { + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + if ( pOption->eOptionType != OPTION_TYPE_SLIDER ) + continue; + + if( pOption->eSliderHomeType != SLIDER_HOME_PREV ) + continue; + + const char *pszConvarName = pOption->szConvar; + if ( pOption->szConvarDef && pOption->szConvarDef[0] ) + { + // They've specified a different convar to use as the default + pszConvarName = pOption->szConvarDef; + } + + ConVarRef varOption( pszConvarName ); + pOption->fSliderHomeValue = varOption.GetFloat(); + } +} + +COptionsDialogXbox::~COptionsDialogXbox() +{ + if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() ) + { +#ifdef _X360 + XboxLaunch()->SetForceEnglish( x360_audio_english.GetBool() ); +#endif + PostMessage( BasePanel()->GetVPanel(), new KeyValues( "command", "command", "QuitRestartNoConfirm" ), 0.0f ); + } + + delete m_pFooter; + m_pFooter = NULL; +} + + +void COptionsDialogXbox::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_nButtonGap = inResourceData->GetInt( "footer_buttongap", -1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues *pControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsDialog.res" ); + LoadControlSettings( "null", NULL, pControlSettings ); + + if ( m_pFooter ) + { + KeyValues *pFooterControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsFooter.res" ); + m_pFooter->LoadControlSettings( "null", NULL, pFooterControlSettings ); + } + + m_SelectedColor = pScheme->GetColor( "SectionedListPanel.SelectedBgColor", Color(255, 255, 255, 255) ); + + m_pOptionsSelectionLeft->SetBgColor( m_SelectedColor ); + m_pOptionsSelectionLeft->SetAlpha( 96 ); + + m_pOptionsSelectionLeft2->SetBgColor( Color(0,0,0,96) ); + + int iX, iY; + m_pOptionsSelectionLeft->GetPos( iX, m_iSelectorYStart ); + m_iOptionSpacing = (m_pOptionLabels[ 0 ])->GetTall(); + + m_hLabelFont = pScheme->GetFont( "MenuLarge" ); + m_hButtonFont = pScheme->GetFont( "GameUIButtons" ); + + // Decide how many items will fit + int iTall = GetTall(); + m_iNumItems = ( iTall - 70 ) / m_iOptionSpacing; + + if ( m_iNumItems > m_pOptions->Count() ) + { + // There's more space in the dialog than needed, shrink it down + m_iNumItems = m_pOptions->Count(); + iTall = m_iNumItems * m_iOptionSpacing + 70; + SetTall( iTall ); + } + + MoveToCenterOfScreen(); + + // Adjust sizes for the number of items + vgui::Panel *pPanel = FindChildByName( "OptionsBackgroundLeft" ); + pPanel->SetTall( iTall - 70 ); + pPanel = FindChildByName( "OptionsBackgroundRight" ); + pPanel->SetTall( iTall - 70 ); + + m_pOptionsUpArrow->GetPos( iX, iY ); + m_pOptionsUpArrow->SetPos( iX, iTall - 32 ); + m_pOptionsDownArrow->GetPos( iX, iY ); + m_pOptionsDownArrow->SetPos( iX, iTall - 32 ); + + m_pValueBars[ 0 ]->SetFgColor( Color( 255, 255, 255, 200 ) ); + m_pValueBars[ 0 ]->SetBgColor( Color( 255, 255, 255, 32 ) ); + m_pValueBars[ 0 ]->SetHomeColor( Color( m_SelectedColor[0], m_SelectedColor[1], m_SelectedColor[2], 96 ) ); + + // Get proper setting for items for orginal in res file + vgui::Panel *(pPanelList[3]) = { m_pOptionLabels[ 0 ], m_pValueLabels[ 0 ], m_pValueBars[ 0 ] }; + + for ( int iPanelList = 0; iPanelList < 3; ++iPanelList ) + { + pPanel = pPanelList[ iPanelList ]; + + int iZ, iWide; + bool bVisible; + pPanel->GetPos( iX, iY ); + iZ = ipanel()->GetZPos( pPanel->GetVPanel() ); + iWide = pPanel->GetWide(); + iTall = pPanel->GetTall(); + bVisible = pPanel->IsVisible(); + + for ( int iLabel = 1; iLabel < m_iNumItems; ++iLabel ) + { + if ( iPanelList == 0 ) + { + pPanel = m_pOptionLabels[ iLabel ]; + m_pOptionLabels[ iLabel ]->SetFont( m_pOptionLabels[ 0 ]->GetFont() ); + } + else if ( iPanelList == 1 ) + { + pPanel = m_pValueLabels[ iLabel ]; + m_pValueLabels[ iLabel ]->SetFont( m_pValueLabels[ 0 ]->GetFont() ); + } + else if ( iPanelList == 2 ) + { + pPanel = m_pValueBars[ iLabel ]; + m_pValueBars[ iLabel ]->SetFgColor( m_pValueBars[ 0 ]->GetFgColor() ); + m_pValueBars[ iLabel ]->SetBgColor( m_pValueBars[ 0 ]->GetBgColor() ); + m_pValueBars[ iLabel ]->SetHomeColor( m_pValueBars[ 0 ]->GetHomeColor() ); + } + + pPanel->SetPos( iX, iY + iLabel * m_iOptionSpacing ); + pPanel->SetZPos( iZ ); + pPanel->SetWide( iWide ); + pPanel->SetTall( iTall ); + pPanel->SetVisible( bVisible ); + } + } + + // Make unused items invisible + for ( int iLabel = m_iNumItems; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel ) + { + m_pOptionLabels[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( false ); + m_pValueBars[ iLabel ]->SetVisible( false ); + } + + InitializeSliderDefaults(); + UpdateScroll(); + DeactivateSelection(); + UpdateFooter(); + + // Don't fade the background for options so that brightness can be easily adjusted + if ( !m_bControllerOptions ) + vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 0.01f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::OnClose( void ) +{ + CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->ShowFooter( true ); + } + + ConVarRef varOption( "option_duck_method" ); + + char szCommand[ 256 ]; + + for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode ) + { + ButtonCode_t code = static_cast<ButtonCode_t>( iCode ); + + const char *pDuckKeyName = gameuifuncs->GetBindingForButtonCode( code ); + + // Check if there's a binding for this key + if ( !pDuckKeyName || !pDuckKeyName[0] ) + continue; + + // If we use this binding, display the key in our list + if ( ActionsAreTheSame( pDuckKeyName, "+duck" ) ) + { + if( varOption.GetBool() ) + { + // Bind DUCK key to toggle_duck + Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "toggle_duck" ); + engine->ClientCmd_Unrestricted( szCommand ); + } + else + { + // Bind DUCK key to +DUCK + Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "+DUCK" ); + engine->ClientCmd_Unrestricted( szCommand ); + } + } + } + + // Save these settings! + if ( m_bOptionsChanged ) + engine->ClientCmd_Unrestricted( "host_writeconfig" ); + + BasePanel()->RunCloseAnimation( "CloseOptionsDialog_OpenMainMenu" ); + if ( !m_bControllerOptions ) + vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 120.0f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR ); + + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( IsX360() ) + { + if ( GetAlpha() != 255 ) + { + // inhibit key activity during transitions + return; + } + } + + if ( !m_bSelectionActive ) + HandleInactiveKeyCodePressed( code ); + else if ( m_pSelectedOption->eOptionType != OPTION_TYPE_BIND ) + HandleActiveKeyCodePressed( code ); + else + HandleBindKeyCodePressed( code ); +} + +void COptionsDialogXbox::OnCommand(const char *command) +{ + m_KeyRepeat.Reset(); + + if ( !Q_stricmp( command, "DefaultControls" ) ) + { + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + FillInDefaultBindings(); + } + else if ( !Q_stricmp( command, "RefreshOptions" ) ) + { + UncacheChoices(); + UpdateScroll(); + } + else if ( !Q_stricmp( command, "AcceptVocalsLanguageChange" ) ) + { + OnClose(); + } + else if ( !Q_stricmp( command, "CancelVocalsLanguageChange" ) ) + { + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + x360_audio_english.SetValue( m_bOldForceEnglishAudio ); + OnCommand( "RefreshOptions" ); + OnClose(); + } + else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) + { + vgui::surface()->RestrictPaintToSinglePanel(NULL); + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::OnKeyCodeReleased( vgui::KeyCode code ) +{ + m_KeyRepeat.KeyUp( code ); + + BaseClass::OnKeyCodeReleased( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsDialogXbox::OnThink() +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + OnKeyCodePressed( code ); + } + + BaseClass::OnThink(); +} + +void COptionsDialogXbox::HandleInactiveKeyCodePressed( vgui::KeyCode code ) +{ + m_KeyRepeat.KeyDown( code ); + + OptionType_e eOptionType = (*m_pOptions)[ m_iSelection ]->eOptionType; + switch( code ) + { + // Change the selected value + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: + if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable ) + { + // Don't allow more binds if it's already maxed out + if ( eOptionType == OPTION_TYPE_BIND && ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 ) ) + { + ActivateSelection(); + vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); + } + else if ( eOptionType == OPTION_TYPE_BINARY || eOptionType == OPTION_TYPE_CHOICE ) + { + ActivateSelection(); + ChangeValue( 1 ); + DeactivateSelection(); + } + } + else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage ) + { + BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this ); + } + break; + + // To the main menu + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() ) + { + // Pop up a dialog to confirm changing the language + vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); + BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_LANGUAGE_CHANGE, this ); + } + else + { + OnClose(); + } + break; + + // Move the selection up and down + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case STEAMCONTROLLER_DPAD_UP: + ChangeSelection( -1 ); + break; + + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + ChangeSelection( 1 ); + break; + + // Quickly change in the negative direction + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable ) + { + if ( eOptionType != OPTION_TYPE_BIND ) + { + ActivateSelection(); + ChangeValue( -1 ); + DeactivateSelection(); + } + } + else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage ) + { + BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this ); + } + break; + + // Quickly change in the positive direction + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable ) + { + if ( eOptionType != OPTION_TYPE_BIND ) + { + ActivateSelection(); + ChangeValue( 1 ); + DeactivateSelection(); + } + } + else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage ) + { + BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this ); + } + break; + + case KEY_XBUTTON_X: + case STEAMCONTROLLER_X: + if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable ) + { + if ( eOptionType == OPTION_TYPE_BIND ) + { + if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 ) + { + ActivateSelection(); + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + UnbindOption( m_pSelectedOption, GetSelectionLabel() ); + DeactivateSelection(); + } + } + } + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + if ( m_bControllerOptions ) + { + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); + BasePanel()->ShowMessageDialog( MD_DEFAULT_CONTROLS_CONFIRM, this ); + } + else + { + BasePanel()->OnChangeStorageDevice(); + } + break; + + default: + break; + } +} + +void COptionsDialogXbox::HandleActiveKeyCodePressed( vgui::KeyCode code ) +{ + // Only binds types should become active! + DeactivateSelection(); +} + +void COptionsDialogXbox::HandleBindKeyCodePressed( vgui::KeyCode code ) +{ + // Don't let stick movements be bound + if ( ( code >= KEY_XSTICK1_RIGHT && code <= KEY_XSTICK1_UP ) || + ( code >= KEY_XSTICK2_RIGHT && code <= KEY_XSTICK2_UP ) ) + return; + + if ( code == KEY_XBUTTON_START ) + { + vgui::surface()->PlaySound( "UI/buttonrollover.wav" ); + UpdateValue( m_pSelectedOption, GetSelectionLabel() ); + } + else + ChangeValue( static_cast<int>( code ) ); + + DeactivateSelection(); +} + +void COptionsDialogXbox::ActivateSelection( void ) +{ + m_bSelectionActive = true; + m_pSelectedOption = (*m_pOptions)[ m_iSelection ]; + + if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND ) + return; + + m_KeyRepeat.Reset(); + + m_pOptionsSelectionLeft->SetAlpha( 255 ); + + int iLabel = GetSelectionLabel(); + + m_pOptionLabels[ iLabel ]->SetFgColor( Color( 0, 0, 0, 255 ) ); + + m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont ); + m_pValueLabels[ iLabel ]->SetText( "#GameUI_SetNewButton" ); + + UpdateFooter(); +} + +void COptionsDialogXbox::DeactivateSelection( void ) +{ + m_bSelectionActive = false; + + if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND ) + return; + + m_pOptionsSelectionLeft->SetAlpha( 96 ); + + int iLabel = GetSelectionLabel(); + + m_pOptionLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) ); + m_pValueLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) ); + + UpdateFooter(); +} + +void COptionsDialogXbox::ChangeSelection( int iChange ) +{ + m_iSelection += iChange; + + if ( m_iSelection < 0 ) + { + m_iSelection = 0; + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } + else if ( m_iSelection >= m_pOptions->Count() ) + { + m_iSelection = m_pOptions->Count() - 1; + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } + else + { + vgui::surface()->PlaySound( "UI/buttonrollover.wav" ); + } + + // Make sure the selected item in in the window + if ( m_iSelection < m_iScroll ) + { + m_iScroll = m_iSelection; + UpdateScroll(); + } + else if ( GetSelectionLabel() >= m_iNumItems ) + { + m_iScroll = m_iSelection - m_iNumItems + 1; + UpdateScroll(); + } + + UpdateFooter(); + + UpdateSelection(); +} + +void COptionsDialogXbox::UpdateFooter( void ) +{ + m_pFooter->ClearButtons(); + + if ( !m_bSelectionActive ) + { + if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable ) + { + switch ( (*m_pOptions)[ m_iSelection ]->eOptionType ) + { + case OPTION_TYPE_BINARY: + m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" ); + break; + + case OPTION_TYPE_SLIDER: + m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" ); + break; + + case OPTION_TYPE_CHOICE: + m_pFooter->AddNewButtonLabel( ( (*m_pOptions)[ m_iSelection ]->m_Choices.Count() == 2 ) ? ( "#GameUI_Toggle" ) : ( "#GameUI_Modify" ), "#GameUI_Icons_DPAD" ); + break; + + case OPTION_TYPE_BIND: + { + if ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 ) + m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_A_BUTTON" ); + if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 ) + m_pFooter->AddNewButtonLabel( "#GameUI_ClearButton", "#GameUI_Icons_X_BUTTON" ); + break; + } + } + } + + if ( m_bControllerOptions ) + m_pFooter->AddNewButtonLabel( "#GameUI_DefaultButtons", "#GameUI_Icons_Y_BUTTON" ); + else + m_pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" ); + + m_pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); + } + else if ( m_pSelectedOption ) + { + switch ( m_pSelectedOption->eOptionType ) + { + case OPTION_TYPE_BIND: + m_pFooter->AddNewButtonLabel( "#GameUI_Cancel", "#GameUI_Icons_START" ); + break; + } + } + + if ( m_nButtonGap > 0 ) + { + m_pFooter->SetButtonGap( m_nButtonGap ); + } + else + { + m_pFooter->UseDefaultButtonGap(); + } + + CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel(); + if ( pBase ) + { + pBase->ShowFooter( false ); + } +} + +void COptionsDialogXbox::UpdateSelection( void ) +{ + int iYPos = m_iSelectorYStart + m_iOptionSpacing * ( GetSelectionLabel() ); + + int iX, iY; + m_pOptionsSelectionLeft->GetPos( iX, iY ); + m_pOptionsSelectionLeft->SetPos( iX, iYPos ); + m_pOptionsSelectionLeft2->GetPos( iX, iY ); + m_pOptionsSelectionLeft2->SetPos( iX, iYPos + 2 ); +} + +void COptionsDialogXbox::UpdateScroll( void ) +{ + for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel ) + { + int iOption = m_iScroll + iLabel; + + if ( iOption >= m_pOptions->Count() ) + break; + + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + m_pOptionLabels[ iLabel ]->SetText( pOption->szDisplayName ); + + UpdateValue( pOption, iLabel ); + + if ( pOption->bUnchangeable ) + { + m_pOptionLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) ); + m_pValueLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) ); + } + else + { + m_pOptionLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) ); + m_pValueLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) ); + } + + } + + // Draw arrows if there's more items to scroll to + m_pOptionsUpArrow->SetAlpha( ( m_iScroll > 0 ) ? ( 255 ) : ( 64 ) ); + m_pOptionsDownArrow->SetAlpha( ( m_iScroll + m_iNumItems < m_pOptions->Count() ) ? ( 255 ) : ( 64 ) ); +} + +void COptionsDialogXbox::UncacheChoices( void ) +{ + for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption ) + { + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + if ( pOption->eOptionType != OPTION_TYPE_CHOICE ) + continue; + + pOption->iCurrentChoice = -1; + } +} + +void COptionsDialogXbox::GetChoiceFromConvar( OptionData_t *pOption ) +{ + if ( pOption->iCurrentChoice < 0 ) + { + // Don't have a proper choice yet, so see if the convars value matches one of our choices + if ( pOption->szConvar2[ 0 ] == '\0' ) + { + ConVarRef varOption( pOption->szConvar ); + const char *pchValue = varOption.GetString(); + + // Only one convar to check + for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice ) + { + if ( Q_stricmp( pOption->m_Choices[ iChoice ].szValue, pchValue ) == 0 ) + { + pOption->iCurrentChoice = iChoice; + break; + } + + // We need to compare values in case we have "0" & "0.00000". + if ( (pchValue[0] >= '0' && pchValue[0] <= '9') || pchValue[0] == '-' ) + { + float flVal = atof(pchValue); + float flChoiceVal = atof(pOption->m_Choices[ iChoice ].szValue); + if ( flVal == flChoiceVal ) + { + pOption->iCurrentChoice = iChoice; + break; + } + } + } + } + else + { + // Two convars to contend with + ConVarRef varOption( pOption->szConvar ); + ConVarRef varOption2( pOption->szConvar2 ); + + const char *pchValue = varOption.GetString(); + const char *pchValue2 = varOption2.GetString(); + + for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice ) + { + char szOptionValue[ OPTION_STRING_LENGTH ]; + Q_strncpy( szOptionValue, pOption->m_Choices[ iChoice ].szValue, sizeof( szOptionValue ) ); + + char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) ); + + AssertMsg( pchOptionValue2, "Option uses 2 convars but an option doesn't have 2 ; separated values!" ); + + pchOptionValue2[ 0 ] = '\0'; // Set this as the end of the other value + ++pchOptionValue2; // Point at next char + + if ( Q_stricmp( szOptionValue, pchValue ) == 0 && Q_stricmp( pchOptionValue2, pchValue2 ) == 0 ) + { + pOption->iCurrentChoice = iChoice; + break; + } + } + } + } +} + +#if !defined(_X360) +// BUGBUG: This won't compile because it includes windows.h and this interface function is #defined to something else +// see below system()->GetCurrentTime +#undef GetCurrentTime +#endif + +void COptionsDialogXbox::ChangeValue( float fChange ) +{ + switch ( m_pSelectedOption->eOptionType ) + { + case OPTION_TYPE_BINARY: + { + ConVarRef varOption( m_pSelectedOption->szConvar ); + varOption.SetValue( !varOption.GetBool() ); + + UpdateValue( m_pSelectedOption, GetSelectionLabel() ); + + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + break; + } + + case OPTION_TYPE_SLIDER: + { + if ( system()->GetCurrentTime() > m_fNextChangeTime ) + { + ConVarRef varOption( m_pSelectedOption->szConvar ); + + float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount(); + if ( fNumSegments <= 0.0f ) + fNumSegments = 1.0f; + + float fIncValue; + + if ( m_pSelectedOption->fIncValue == 0.0f ) + fIncValue = ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments ); + else + fIncValue = m_pSelectedOption->fIncValue * ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments ); + + fIncValue *= fChange; + + float fOldValue = varOption.GetFloat(); + float fValue = clamp( fOldValue + fIncValue, m_pSelectedOption->fMinValue, m_pSelectedOption->fMaxValue ); + + if ( fOldValue != fValue ) + { + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + varOption.SetValue( fValue ); + UpdateValue( m_pSelectedOption, GetSelectionLabel() ); + } + else + { + // Play the invalid sound + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } + } + + break; + } + + case OPTION_TYPE_CHOICE: + { + GetChoiceFromConvar( m_pSelectedOption ); + + if ( m_pSelectedOption->iCurrentChoice >= 0 ) + { + m_pSelectedOption->iCurrentChoice += fChange; + + while ( m_pSelectedOption->iCurrentChoice >= m_pSelectedOption->m_Choices.Count() ) + m_pSelectedOption->iCurrentChoice -= m_pSelectedOption->m_Choices.Count(); + + while ( m_pSelectedOption->iCurrentChoice < 0 ) + m_pSelectedOption->iCurrentChoice += m_pSelectedOption->m_Choices.Count(); + + if ( m_pSelectedOption->szConvar2[ 0 ] == '\0' ) + { + // Only one convar to set + ConVarRef varOption( m_pSelectedOption->szConvar ); + varOption.SetValue( m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue ); + } + else + { + // Two convars to deal with + char szOptionValue[ OPTION_STRING_LENGTH ]; + Q_strncpy( szOptionValue, m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue, sizeof( szOptionValue ) ); + + char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) ); + + AssertMsg( pchOptionValue2, "Option uses to convars but an option doesn't have 2 ; separated values!" ); + + pchOptionValue2[ 0 ] = '\0'; // Set this as the end of the other value + ++pchOptionValue2; // Point at next char + + ConVarRef varOption( m_pSelectedOption->szConvar ); + ConVarRef varOption2( m_pSelectedOption->szConvar2 ); + + varOption.SetValue( szOptionValue ); + varOption2.SetValue( pchOptionValue2 ); + } + } + else + { + DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", m_pSelectedOption->szConvar, m_pSelectedOption->szDisplayName ); + } + + UpdateValue( m_pSelectedOption, GetSelectionLabel() ); + + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + break; + } + + case OPTION_TYPE_BIND: + { + // If we only allow one bind replace the previous + if ( binds_per_command.GetInt() == 1 ) + UnbindOption( m_pSelectedOption, GetSelectionLabel() ); + + ButtonCode_t code = static_cast<ButtonCode_t>( static_cast<int>( fChange ) ); + + char szCommand[ 256 ]; + Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), m_pSelectedOption->szCommand ); + engine->ClientCmd_Unrestricted( szCommand ); + + // After binding we need to update all bindings so they display the correct keys + UpdateAllBinds( code ); + + m_bOptionsChanged = true; + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + break; + } + } +} + +void COptionsDialogXbox::UnbindOption( OptionData_t *pOption, int iLabel ) +{ + if ( pOption->eOptionType != OPTION_TYPE_BIND ) + return; + + for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode ) + { + ButtonCode_t code = static_cast<ButtonCode_t>( iCode ); + + const char *pBinding = gameuifuncs->GetBindingForButtonCode( code ); + + // Check if there's a binding for this key + if ( !pBinding || !pBinding[0] ) + continue; + + // If we use this binding, display the key in our list + if ( ActionsAreTheSame( pBinding, pOption->szCommand ) ) + { + char szCommand[ 256 ]; + Q_snprintf( szCommand, sizeof( szCommand ), "unbind %s", g_pInputSystem->ButtonCodeToString( code ) ); + engine->ClientCmd_Unrestricted( szCommand ); + } + } + + pOption->iNumBinds = 0; + + if ( iLabel < 0 || iLabel >= m_iNumItems ) + return; + + m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont ); + m_pValueLabels[ iLabel ]->SetText( "" ); +} + +void COptionsDialogXbox::UpdateValue( OptionData_t *pOption, int iLabel ) +{ + if ( pOption->bVocalsLanguage ) + { + // Can't change vocal language while in game + pOption->bUnchangeable = GameUI().IsInLevel(); + } + + switch ( pOption->eOptionType ) + { + case OPTION_TYPE_BINARY: + { + ConVarRef varOption( pOption->szConvar ); + + m_pValueBars[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( true ); + m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont ); + m_pValueLabels[ iLabel ]->SetText( ( varOption.GetBool() ) ? ( "#GameUI_Enable" ) : ( "#GameUI_Disable" ) ); + break; + } + + case OPTION_TYPE_SLIDER: + { + ConVarRef varOption( pOption->szConvar ); + + m_pValueLabels[ iLabel ]->SetVisible( false ); + m_pValueBars[ iLabel ]->SetVisible( true ); + + // If it's very close to the home value set it as the home value + float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount(); + if ( fNumSegments <= 0.0f ) + fNumSegments = 1.0f; + + float fIncValue; + + if ( pOption->fIncValue == 0.0f ) + fIncValue = ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments ); + else + fIncValue = pOption->fIncValue * ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments ); + + float fNormalizeHome = fabsf( ( pOption->fMinValue - pOption->fSliderHomeValue ) / ( pOption->fMaxValue - pOption->fMinValue ) ); + if ( pOption->fIncValue < 0.0f ) + fNormalizeHome = 1.0f - fNormalizeHome; + m_pValueBars[ iLabel ]->SetHomeValue( fNormalizeHome ); + + float fNormalizeValue = fabsf( ( pOption->fMinValue - varOption.GetFloat() ) / ( pOption->fMaxValue - pOption->fMinValue ) ); + if ( pOption->fIncValue < 0.0f ) + fNormalizeValue = 1.0f - fNormalizeValue; + + // atof accuracy for convar isn't so good, so snap to the home value if we're near it. + if ( fabsf( fNormalizeValue - fNormalizeHome ) < fabsf( fIncValue * 0.1f ) ) + fNormalizeValue = fNormalizeHome; + + m_pValueBars[ iLabel ]->SetAnalogValue( fNormalizeValue ); + + break; + } + + case OPTION_TYPE_CHOICE: + { + GetChoiceFromConvar( pOption ); + + m_pValueBars[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( true ); + m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont ); + if ( pOption->iCurrentChoice >= 0 ) + { + m_pValueLabels[ iLabel ]->SetText( pOption->m_Choices[ pOption->iCurrentChoice ].szName ); + } + else + { + if ( pOption->bUnchangeable ) + { + if ( pOption->szConvar2[ 0 ] == '\0' ) + { + ConVarRef varOption( pOption->szConvar ); + m_pValueLabels[ iLabel ]->SetText( varOption.GetString() ); + } + else + { + // This is used for displaying the resolution settings + ConVarRef varOption( pOption->szConvar ); + ConVarRef varOption2( pOption->szConvar2 ); + char szBuff[ 256 ]; + Q_snprintf( szBuff, sizeof( szBuff ), "%sx%s%s", varOption.GetString(), varOption2.GetString(), ( x360_resolution_interlaced.GetBool() ) ? ( "i" ) : ( "p" ) ); + m_pValueLabels[ iLabel ]->SetText( szBuff ); + } + } + else + { + DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", pOption->szConvar, pOption->szDisplayName ); + m_pValueLabels[ iLabel ]->SetText( "#GameUI_NoOptionsYet" ); + } + } + + break; + } + + case OPTION_TYPE_BIND: + { + UpdateBind( pOption, iLabel ); + break; + } + } +} + +void COptionsDialogXbox::UpdateBind( OptionData_t *pOption, int iLabel, ButtonCode_t codeIgnore, ButtonCode_t codeAdd ) +{ + int iNumBinds = 0; + char szBinds[ OPTION_STRING_LENGTH ]; + + char szBuff[ 512 ]; + wchar_t szWideBuff[ 64 ]; + + for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode ) + { + ButtonCode_t code = static_cast<ButtonCode_t>( iCode ); + + // Don't show this key in our list + if ( code == codeIgnore ) + continue; + + bool bUseThisKey = ( codeAdd == code ); + + if ( !bUseThisKey ) + { + // If this binding is being replaced only allow the new binding to show + if ( binds_per_command.GetInt() == 1 && codeAdd != BUTTON_CODE_INVALID ) + continue; + + // Only check against bind name if we haven't already forced this binding to be used + const char *pBinding = gameuifuncs->GetBindingForButtonCode( code ); + + if ( !pBinding ) + continue; + + bUseThisKey = ActionsAreTheSame( pBinding, pOption->szCommand ); + } + + // Don't use this bind in out list + if ( !bUseThisKey ) + continue; + + // Turn localized string into icon character + Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", g_pInputSystem->ButtonCodeToString( static_cast<ButtonCode_t>( iCode ) ) ); + g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 ); + g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) ); + + // Add this icon to our list of keys to display + szBinds[ iNumBinds ] = szBuff[ 0 ]; + ++iNumBinds; + } + + if ( iNumBinds == 0 ) + { + // No keys for this bind + pOption->iNumBinds = 0; + m_pValueBars[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( true ); + m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont ); + m_pValueLabels[ iLabel ]->SetText( "" ); + } + else + { + // Show icons for list of keys + szBinds[ iNumBinds ] = '\0'; + pOption->iNumBinds = iNumBinds; + m_pValueBars[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( true ); + m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont ); + m_pValueLabels[ iLabel ]->SetText( szBinds ); + } +} + +void COptionsDialogXbox::UpdateAllBinds( ButtonCode_t code ) +{ + // Loop through all the items + for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel ) + { + int iOption = m_iScroll + iLabel; + + if ( iOption >= m_pOptions->Count() ) + break; + + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + if ( pOption->eOptionType == OPTION_TYPE_BIND ) + { + if ( pOption == m_pSelectedOption ) + { + // We just added a key to this binding so add it to the list + UpdateBind( pOption, iLabel, BUTTON_CODE_INVALID, code ); + } + else + { + // We just added a key so don't let it show up for any other bindings + UpdateBind( pOption, iLabel, code ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Read in defaults from game's default config file and populate list +// using those defaults +//----------------------------------------------------------------------------- +void COptionsDialogXbox::FillInDefaultBindings( void ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( "cfg/config.360.cfg", NULL, buf ) ) + return; + + // Clear out all current bindings + for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption ) + UnbindOption( (*m_pOptions)[ iOption ], iOption - m_iScroll ); + + const char *data = (const char*)buf.Base(); + + // loop through all the binding + while ( data != NULL ) + { + char cmd[64]; + data = UTIL_Parse( data, cmd, sizeof(cmd) ); + if ( strlen( cmd ) <= 0 ) + break; + + if ( !stricmp(cmd, "bind") ) + { + // Key name + char szKeyName[256]; + data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) ); + if ( strlen( szKeyName ) <= 0 ) + break; // Error + + char szBinding[256]; + data = UTIL_Parse( data, szBinding, sizeof(szBinding) ); + if ( strlen( szKeyName ) <= 0 ) + break; // Error + + // Bind it + char szCommand[ 256 ]; + Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", szKeyName, szBinding ); + engine->ClientCmd_Unrestricted( szCommand ); + + // Loop through all the items + for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel ) + { + int iOption = m_iScroll + iLabel; + + if ( iOption >= m_pOptions->Count() ) + break; + + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + // Check if this bind is for this option + if ( pOption->eOptionType == OPTION_TYPE_BIND && ActionsAreTheSame( pOption->szCommand, szBinding ) ) + { + char szBuff[ 512 ]; + wchar_t szWideBuff[ 64 ]; + char szBinds[ OPTION_STRING_LENGTH ]; + if ( pOption->iNumBinds > 0 ) + m_pValueLabels[ iLabel ]->GetText( szBinds, sizeof( szBinds ) ); + + // Turn localized string into icon character + Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", szKeyName ); + g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 ); + g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) ); + + // Add this icon to our list of keys to display + szBinds[ pOption->iNumBinds ] = szBuff[ 0 ]; + ++pOption->iNumBinds; + + // Show icons for list of keys + szBinds[ pOption->iNumBinds ] = '\0'; + m_pValueBars[ iLabel ]->SetVisible( false ); + m_pValueLabels[ iLabel ]->SetVisible( true ); + m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont ); + m_pValueLabels[ iLabel ]->SetText( szBinds ); + } + } + } + } + + // Reset any options with default convar values + for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel ) + { + int iOption = m_iScroll + iLabel; + + if ( iOption >= m_pOptions->Count() ) + break; + + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + + if ( pOption->szConvarDef && pOption->szConvarDef[0] ) + { + ConVarRef varDefault( pOption->szConvarDef ); + ConVarRef varOption( pOption->szConvar ); + varOption.SetValue( varDefault.GetFloat() ); + pOption->iCurrentChoice = -1; + UpdateValue( pOption, iLabel ); + } + } +} + +bool COptionsDialogXbox::ShouldSkipOption( KeyValues *pKey ) +{ + // Skip the option if not in developer mode and this is a developer only option + ConVarRef developer( "developer" ); + if ( !developer.GetBool() && ( pKey->GetInt( "dev", 0 ) != 0 ) ) + return true; + + // Skip the option if it doesn't match the controller/non-controller list + if ( m_bControllerOptions != ( pKey->GetInt( "control", 0 ) != 0 ) ) + return true; + + // Skip portal options if this mod doesn't have portals (defined in gameinfo.txt) + if ( !ModInfo().HasPortals() && ( pKey->GetInt( "portals", 0 ) != 0 ) ) + return true; + + // Skip multiplayer only options for single player (or combo) games + if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "multiplayer", 0 ) != 0 ) ) + return true; + + // Skip difficulty options for games that don't want them + if ( !( ModInfo().IsSinglePlayerOnly() && !ModInfo().NoDifficulty() ) && ( pKey->GetInt( "difficulty", 0 ) != 0 ) ) + return true; + + // Skip voice options for single player games + if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "voice", 0 ) != 0 ) ) + return true; + + // Skip if it's the vocal language option but we're not in german or french + if ( pKey->GetInt( "vocalslanguage", 0 ) != 0 ) + { + if ( !XBX_IsLocalized() ) + { + return true; + } + } + + // Don't create options if there's already an entry by the same name + char szName[ OPTION_STRING_LENGTH ]; + Q_strncpy( szName, pKey->GetName(), sizeof( szName ) ); + + for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption ) + { + OptionData_t *pOption = (*m_pOptions)[ iOption ]; + if ( Q_strcmp( pOption->szName, szName ) == 0 ) + return true; + } + + for ( int iOption = 0; iOption < s_DisabledOptions.Count(); ++iOption ) + { + OptionChoiceData_t *pOption = &(s_DisabledOptions[ iOption ]); + if ( Q_strcmp( pOption->szName, szName ) == 0 ) + return true; + } + + return false; +} + +void COptionsDialogXbox::ReadOptionsFromFile( const char *pchFileName ) +{ + KeyValues *pOptionKeys = new KeyValues( "options_x360" ); + pOptionKeys->LoadFromFile( g_pFullFileSystem, pchFileName, NULL ); + + KeyValues *pKey = NULL; + for ( pKey = pOptionKeys->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() ) + { + // Skip disabled options + if ( pKey->GetInt( "disable", 0 ) != 0 ) + { + // Remember disabled options so we don't create another with the same name + int iDisabledOption = s_DisabledOptions.AddToTail(); + OptionChoiceData_t *pDisabledOption = &(s_DisabledOptions[ iDisabledOption ]); + Q_strncpy( pDisabledOption->szName, pKey->GetName(), sizeof( pDisabledOption->szName ) ); + + continue; + } + + if ( ShouldSkipOption( pKey ) ) + continue; + + int iOption = m_pOptions->AddToTail(); + OptionData_t **pNewOption = &((*m_pOptions)[ iOption ]); + *pNewOption = new OptionData_t; + + // Get common values + Q_strncpy( (*pNewOption)->szName, pKey->GetName(), sizeof( (*pNewOption)->szName ) ); + Q_strncpy( (*pNewOption)->szDisplayName, pKey->GetString( "name", "" ), sizeof( (*pNewOption)->szDisplayName ) ); + + Q_strncpy( (*pNewOption)->szConvar, pKey->GetString( "convar", "" ), sizeof( (*pNewOption)->szConvar ) ); + if ( (*pNewOption)->szConvar[ 0 ] == '\0' ) + Q_strncpy( (*pNewOption)->szCommand, pKey->GetString( "command", "" ), sizeof( (*pNewOption)->szCommand ) ); + + Q_strncpy( (*pNewOption)->szConvarDef, pKey->GetString( "convar_def", "" ), sizeof( (*pNewOption)->szConvarDef ) ); + + (*pNewOption)->iPriority = pKey->GetInt( "priority", 0 ); + (*pNewOption)->bUnchangeable = ( pKey->GetInt( "unchangeable", 0 ) != 0 ); + (*pNewOption)->bVocalsLanguage = ( pKey->GetInt( "vocalslanguage", 0 ) != 0 ); + + // Determine the type + char szType[ OPTION_STRING_LENGTH ]; + Q_strncpy( szType, pKey->GetString( "type", "binary" ), sizeof( szType ) ); + + if ( Q_stricmp( szType, "binary" ) == 0 ) + (*pNewOption)->eOptionType = OPTION_TYPE_BINARY; + else if ( Q_stricmp( szType, "slider" ) == 0 ) + (*pNewOption)->eOptionType = OPTION_TYPE_SLIDER; + else if ( Q_stricmp( szType, "choice" ) == 0 ) + (*pNewOption)->eOptionType = OPTION_TYPE_CHOICE; + else if ( Q_stricmp( szType, "bind" ) == 0 ) + (*pNewOption)->eOptionType = OPTION_TYPE_BIND; + + // Get type specific values + switch ( (*pNewOption)->eOptionType ) + { + case OPTION_TYPE_SLIDER: + { + (*pNewOption)->fMinValue = pKey->GetFloat( "minvalue", 0.0f ); + (*pNewOption)->fMaxValue = pKey->GetFloat( "maxvalue", 1.0f ); + (*pNewOption)->fIncValue = pKey->GetFloat( "incvalue", 0.0f ); + + char szSliderHomeType[ OPTION_STRING_LENGTH ]; + Q_strncpy( szSliderHomeType, pKey->GetString( "sliderhome", "none" ), sizeof( szSliderHomeType ) ); + + if ( Q_stricmp( szSliderHomeType, "prev" ) == 0 ) + (*pNewOption)->eSliderHomeType = SLIDER_HOME_PREV; + else if ( Q_stricmp( szSliderHomeType, "min" ) == 0 ) + (*pNewOption)->eSliderHomeType = SLIDER_HOME_MIN; + else if ( Q_stricmp( szSliderHomeType, "center" ) == 0 ) + (*pNewOption)->eSliderHomeType = SLIDER_HOME_CENTER; + else if ( Q_stricmp( szSliderHomeType, "max" ) == 0 ) + (*pNewOption)->eSliderHomeType = SLIDER_HOME_MAX; + else + (*pNewOption)->eSliderHomeType = SLIDER_HOME_NONE; + + switch ( (*pNewOption)->eSliderHomeType ) + { + case SLIDER_HOME_MIN: + (*pNewOption)->fSliderHomeValue = (*pNewOption)->fMinValue; + break; + + case SLIDER_HOME_CENTER: + (*pNewOption)->fSliderHomeValue = ( (*pNewOption)->fMaxValue + (*pNewOption)->fMinValue ) * 0.5f; + break; + + case SLIDER_HOME_MAX: + (*pNewOption)->fSliderHomeValue = (*pNewOption)->fMaxValue; + break; + + default: + (*pNewOption)->fSliderHomeValue = 0.0f; + } + + break; + } + + case OPTION_TYPE_CHOICE: + { + Q_strncpy( (*pNewOption)->szConvar2, pKey->GetString( "convar2", "" ), sizeof( (*pNewOption)->szConvar ) ); + + if ( (*pNewOption)->bVocalsLanguage ) + { + (*pNewOption)->iCurrentChoice = -1; + int iChoice = (*pNewOption)->m_Choices.AddToTail(); + OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]); + + Q_strncpy( pNewOptionChoice->szName, "#GameUI_Language_English", sizeof( pNewOptionChoice->szName ) ); + Q_strncpy( pNewOptionChoice->szValue, "1", sizeof( pNewOptionChoice->szValue ) ); + + iChoice = (*pNewOption)->m_Choices.AddToTail(); + pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]); + + char szLanguage[ 256 ]; + Q_snprintf( szLanguage, sizeof( szLanguage ), "#GameUI_Language_%s", XBX_GetLanguageString() ); + Q_strncpy( pNewOptionChoice->szName, szLanguage, sizeof( pNewOptionChoice->szName ) ); + Q_strncpy( pNewOptionChoice->szValue, "0", sizeof( pNewOptionChoice->szValue ) ); + } + else + { + (*pNewOption)->iCurrentChoice = -1; + KeyValues *pChoicesKey = pKey->FindKey( "choices" ); + + if ( pChoicesKey ) + { + KeyValues *pSubKey = NULL; + for ( pSubKey = pChoicesKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + int iChoice = (*pNewOption)->m_Choices.AddToTail(); + OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]); + + Q_strncpy( pNewOptionChoice->szName, pSubKey->GetName(), sizeof( pNewOptionChoice->szName ) ); + Q_strncpy( pNewOptionChoice->szValue, pSubKey->GetString(), sizeof( pNewOptionChoice->szValue ) ); + } + + if ( (*pNewOption)->m_Choices.Count() < 2 ) + { + DevWarning( "Option \"%s\" is type CHOICE but has less than 2 choices!", (*pNewOption)->szDisplayName ); + } + } + } + break; + } + } + } +} + +int __cdecl SortByPriority( OptionData_t * const *pLeft, OptionData_t * const *pRight ) +{ + return (*pLeft)->iPriority - (*pRight)->iPriority; +} + +void COptionsDialogXbox::SortOptions( void ) +{ + m_pOptions->Sort( SortByPriority ); +} diff --git a/gameui/OptionsSubAudio.cpp b/gameui/OptionsSubAudio.cpp new file mode 100644 index 0000000..9a4d6ae --- /dev/null +++ b/gameui/OptionsSubAudio.cpp @@ -0,0 +1,432 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "OptionsSubAudio.h" + +#include "cvarslider.h" +#include "EngineInterface.h" +#include "ModInfo.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/QueryBox.h" +#include "CvarToggleCheckButton.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include <vgui/IInput.h> +#include <steam/steam_api.h> +#include <tier1/strtools.h> + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +// This member is static so that the updated audio language can be referenced during shutdown +char* COptionsSubAudio::m_pchUpdatedAudioLanguage = (char*)GetLanguageShortName( k_Lang_English ); + +enum SoundQuality_e +{ + SOUNDQUALITY_LOW, + SOUNDQUALITY_MEDIUM, + SOUNDQUALITY_HIGH, +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +COptionsSubAudio::COptionsSubAudio(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + m_pSFXSlider = new CCvarSlider( this, "SFXSlider", "#GameUI_SoundEffectVolume", 0.0f, 1.0f, "volume" ); + m_pMusicSlider = new CCvarSlider( this, "MusicSlider", "#GameUI_MusicVolume", 0.0f, 1.0f, "Snd_MusicVolume" ); + + m_pCloseCaptionCombo = new ComboBox( this, "CloseCaptionCheck", 6, false ); + m_pCloseCaptionCombo->AddItem( "#GameUI_NoClosedCaptions", NULL ); + m_pCloseCaptionCombo->AddItem( "#GameUI_SubtitlesAndSoundEffects", NULL ); + m_pCloseCaptionCombo->AddItem( "#GameUI_Subtitles", NULL ); + + m_pSoundQualityCombo = new ComboBox( this, "SoundQuality", 6, false ); + m_pSoundQualityCombo->AddItem( "#GameUI_High", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_HIGH) ); + m_pSoundQualityCombo->AddItem( "#GameUI_Medium", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_MEDIUM) ); + m_pSoundQualityCombo->AddItem( "#GameUI_Low", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_LOW) ); + + m_pSpeakerSetupCombo = new ComboBox( this, "SpeakerSetup", 6, false ); +#ifndef POSIX + m_pSpeakerSetupCombo->AddItem( "#GameUI_Headphones", new KeyValues("SpeakerSetup", "speakers", 0) ); +#endif + m_pSpeakerSetupCombo->AddItem( "#GameUI_2Speakers", new KeyValues("SpeakerSetup", "speakers", 2) ); +#ifndef POSIX + m_pSpeakerSetupCombo->AddItem( "#GameUI_4Speakers", new KeyValues("SpeakerSetup", "speakers", 4) ); + m_pSpeakerSetupCombo->AddItem( "#GameUI_5Speakers", new KeyValues("SpeakerSetup", "speakers", 5) ); + m_pSpeakerSetupCombo->AddItem( "#GameUI_7Speakers", new KeyValues("SpeakerSetup", "speakers", 7) ); +#endif + m_pSpokenLanguageCombo = new ComboBox (this, "AudioSpokenLanguage", 6, false ); + + m_pSoundMuteLoseFocusCheckButton = new CCvarToggleCheckButton( this, "snd_mute_losefocus", "#GameUI_SndMuteLoseFocus", "snd_mute_losefocus" ); + + LoadControlSettings("Resource\\OptionsSubAudio.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +COptionsSubAudio::~COptionsSubAudio() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Reloads data +//----------------------------------------------------------------------------- +void COptionsSubAudio::OnResetData() +{ + m_bRequireRestart = false; + m_pSFXSlider->Reset(); + m_pMusicSlider->Reset(); + + + // reset the combo boxes + + // close captions + ConVarRef closecaption("closecaption"); + ConVarRef cc_subtitles("cc_subtitles"); + if (closecaption.GetBool()) + { + if (cc_subtitles.GetBool()) + { + m_pCloseCaptionCombo->ActivateItem(2); + } + else + { + m_pCloseCaptionCombo->ActivateItem(1); + } + } + else + { + m_pCloseCaptionCombo->ActivateItem(0); + } + + // speakers + ConVarRef snd_surround_speakers("Snd_Surround_Speakers"); + int speakers = snd_surround_speakers.GetInt(); + +#ifdef POSIX + // On Posix there is no headphone option, so we upgrade to 2 speakers if Snd_Surround_Speakers == 0 + if ( speakers == 0 ) + speakers = 2; +#endif + + // if Snd_Surround_Speakers is -1, then upgrade to 2 speakers + if ( speakers < 0 ) + speakers = 2; + + {for (int itemID = 0; itemID < m_pSpeakerSetupCombo->GetItemCount(); itemID++) + { + KeyValues *kv = m_pSpeakerSetupCombo->GetItemUserData( itemID ); + if (kv && kv->GetInt( "speakers" ) == speakers) + { + m_pSpeakerSetupCombo->ActivateItem( itemID ); + break; + } + }} + + // sound quality is made up from several cvars + ConVarRef Snd_PitchQuality("Snd_PitchQuality"); + ConVarRef dsp_slow_cpu("dsp_slow_cpu"); + int quality = SOUNDQUALITY_LOW; + if (dsp_slow_cpu.GetBool() == false) + { + quality = SOUNDQUALITY_MEDIUM; + } + if (Snd_PitchQuality.GetBool()) + { + quality = SOUNDQUALITY_HIGH; + } + // find the item in the list and activate it + {for (int itemID = 0; itemID < m_pSoundQualityCombo->GetItemCount(); itemID++) + { + KeyValues *kv = m_pSoundQualityCombo->GetItemUserData(itemID); + if (kv && kv->GetInt("quality") == quality) + { + m_pSoundQualityCombo->ActivateItem(itemID); + } + }} + + // + // Audio Languages + // + char szCurrentLanguage[50]; + char szAvailableLanguages[512]; + szCurrentLanguage[0] = 0; + szAvailableLanguages[0] = 0; + + // Fallback to current engine language + engine->GetUILanguage( szCurrentLanguage, sizeof( szCurrentLanguage )); + + // In a Steam environment we get the current language +#if !defined( NO_STEAM ) + // When Steam isn't running we can't get the language info... + if ( steamapicontext->SteamApps() ) + { + Q_strncpy( szCurrentLanguage, steamapicontext->SteamApps()->GetCurrentGameLanguage(), sizeof(szCurrentLanguage) ); + Q_strncpy( szAvailableLanguages, steamapicontext->SteamApps()->GetAvailableGameLanguages(), sizeof(szAvailableLanguages) ); + } +#endif + + // Get the spoken language and store it for comparison purposes + m_nCurrentAudioLanguage = PchLanguageToELanguage( szCurrentLanguage ); + + // Check to see if we have a list of languages from Steam + if ( V_strlen( szAvailableLanguages ) ) + { + // Populate the combo box with each available language + CUtlVector<char*> languagesList; + V_SplitString( szAvailableLanguages, ",", languagesList ); + + for ( int i=0; i < languagesList.Count(); i++ ) + { + const ELanguage languageCode = PchLanguageToELanguage( languagesList[i] ); + m_pSpokenLanguageCombo->AddItem( GetLanguageVGUILocalization( languageCode ), new KeyValues ("Audio Languages", "language", languageCode) ); + } + + languagesList.PurgeAndDeleteElements(); + } + else + { + // Add the current language to the combo + m_pSpokenLanguageCombo->AddItem( GetLanguageVGUILocalization( m_nCurrentAudioLanguage ), new KeyValues ("Audio Languages", "language", m_nCurrentAudioLanguage) ); + } + + // Activate the current language in the combo + {for (int itemID = 0; itemID < m_pSpokenLanguageCombo->GetItemCount(); itemID++) + { + KeyValues *kv = m_pSpokenLanguageCombo->GetItemUserData( itemID ); + if ( kv && kv->GetInt( "language" ) == m_nCurrentAudioLanguage ) + { + m_pSpokenLanguageCombo->ActivateItem( itemID ); + break; + } + }} + + m_pSoundMuteLoseFocusCheckButton->Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies changes +//----------------------------------------------------------------------------- +void COptionsSubAudio::OnApplyChanges() +{ + m_pSFXSlider->ApplyChanges(); + m_pMusicSlider->ApplyChanges(); + + // set the cvars appropriately + // Tracker 28933: Note we can't do this because closecaption is marked + // FCVAR_USERINFO and it won't get sent to server is we direct set it, we + // need to pass it along to the engine parser!!! + // ConVar *closecaption = (ConVar *)cvar->FindVar("closecaption"); + int closecaption_value = 0; + + ConVarRef cc_subtitles( "cc_subtitles" ); + switch (m_pCloseCaptionCombo->GetActiveItem()) + { + default: + case 0: + closecaption_value = 0; + cc_subtitles.SetValue( 0 ); + break; + case 1: + closecaption_value = 1; + cc_subtitles.SetValue( 0 ); + break; + case 2: + closecaption_value = 1; + cc_subtitles.SetValue( 1 ); + break; + } + + // Stuff the close caption change to the console so that it can be + // sent to the server (FCVAR_USERINFO) so that you don't have to restart + // the level for the change to take effect. + char cmd[ 64 ]; + Q_snprintf( cmd, sizeof( cmd ), "closecaption %i\n", closecaption_value ); + engine->ClientCmd_Unrestricted( cmd ); + + ConVarRef snd_surround_speakers( "Snd_Surround_Speakers" ); + int speakers = m_pSpeakerSetupCombo->GetActiveItemUserData()->GetInt( "speakers" ); + snd_surround_speakers.SetValue( speakers ); + + // quality + ConVarRef Snd_PitchQuality( "Snd_PitchQuality" ); + ConVarRef dsp_slow_cpu( "dsp_slow_cpu" ); + int quality = m_pSoundQualityCombo->GetActiveItemUserData()->GetInt( "quality" ); + switch ( quality ) + { + case SOUNDQUALITY_LOW: + dsp_slow_cpu.SetValue(true); + Snd_PitchQuality.SetValue(false); + break; + case SOUNDQUALITY_MEDIUM: + dsp_slow_cpu.SetValue(false); + Snd_PitchQuality.SetValue(false); + break; + default: + Assert("Undefined sound quality setting."); + case SOUNDQUALITY_HIGH: + dsp_slow_cpu.SetValue(false); + Snd_PitchQuality.SetValue(true); + break; + }; + + // headphones at high quality get enhanced stereo turned on + ConVarRef dsp_enhance_stereo( "dsp_enhance_stereo" ); + if (speakers == 0 && quality == SOUNDQUALITY_HIGH) + { + dsp_enhance_stereo.SetValue( 1 ); + } + else + { + dsp_enhance_stereo.SetValue( 0 ); + } + + // Audio spoken language + KeyValues *kv = m_pSpokenLanguageCombo->GetItemUserData( m_pSpokenLanguageCombo->GetActiveItem() ); + const ELanguage nUpdatedAudioLanguage = (ELanguage)( kv ? kv->GetInt( "language" ) : k_Lang_English ); + + if ( nUpdatedAudioLanguage != m_nCurrentAudioLanguage ) + { + // Store new language in static member so that it can be accessed during shutdown when this instance is gone + m_pchUpdatedAudioLanguage = (char *) GetLanguageShortName( nUpdatedAudioLanguage ); + + // Inform user that they need to restart in order change language at this time + QueryBox *qb = new QueryBox( "#GameUI_ChangeLanguageRestart_Title", "#GameUI_ChangeLanguageRestart_Info", GetParent()->GetParent()->GetParent() ); + if (qb != NULL) + { + qb->SetOKCommand( new KeyValues( "Command", "command", "RestartWithNewLanguage" ) ); + qb->SetOKButtonText( "#GameUI_ChangeLanguageRestart_OkButton" ); + qb->SetCancelButtonText( "#GameUI_ChangeLanguageRestart_CancelButton" ); + qb->AddActionSignalTarget( GetParent()->GetParent()->GetParent() ); + qb->DoModal(); + } + } + + m_pSoundMuteLoseFocusCheckButton->ApplyChanges(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called on controls changing, enables the Apply button +//----------------------------------------------------------------------------- +void COptionsSubAudio::OnControlModified() +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the engine needs to be restarted +//----------------------------------------------------------------------------- +bool COptionsSubAudio::RequiresRestart() +{ + // nothing in audio requires a restart like now + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubAudio::OnCommand( const char *command ) +{ + if ( !stricmp( command, "TestSpeakers" ) ) + { + // ask them if they REALLY want to test the speakers if they're in a game already. + if (engine->IsConnected()) + { + QueryBox *qb = new QueryBox("#GameUI_TestSpeakersWarning_Title", "#GameUI_TestSpeakersWarning_Info" ); + if (qb != NULL) + { + qb->SetOKCommand(new KeyValues("RunTestSpeakers")); + qb->SetOKButtonText("#GameUI_TestSpeakersWarning_OkButton"); + qb->SetCancelButtonText("#GameUI_TestSpeakersWarning_CancelButton"); + qb->AddActionSignalTarget( this ); + qb->DoModal(); + } + else + { + // couldn't create the warning dialog for some reason, so just test the speakers. + RunTestSpeakers(); + } + } + else + { + // player isn't connected to a game so there's no reason to warn them about being disconnected. + // create the command to execute + RunTestSpeakers(); + } + } + else if ( !stricmp( command, "ShowThirdPartyAudioCredits" ) ) + { + OpenThirdPartySoundCreditsDialog(); + } + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: Run the test speakers map. +//----------------------------------------------------------------------------- +void COptionsSubAudio::RunTestSpeakers() +{ + engine->ClientCmd_Unrestricted( "disconnect\nwait\nwait\nsv_lan 1\nsetmaster enable\nmaxplayers 1\n\nhostname \"Speaker Test\"\nprogress_enable\nmap test_speakers\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: third-party audio credits dialog +//----------------------------------------------------------------------------- +class COptionsSubAudioThirdPartyCreditsDlg : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsSubAudioThirdPartyCreditsDlg, vgui::Frame ); +public: + COptionsSubAudioThirdPartyCreditsDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL ) + { + // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below) + + SetTitle("#GameUI_ThirdPartyAudio_Title", true); + SetSize( 500, 200 ); + LoadControlSettings( "resource/OptionsSubAudioThirdPartyDlg.res" ); + MoveToCenterOfScreen(); + SetSizeable( false ); + SetDeleteSelfOnClose( true ); + } + + virtual void Activate() + { + BaseClass::Activate(); + + input()->SetAppModalSurface(GetVPanel()); + } + + void OnKeyCodeTyped(KeyCode code) + { + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Open third party audio credits dialog +//----------------------------------------------------------------------------- +void COptionsSubAudio::OpenThirdPartySoundCreditsDialog() +{ + if (!m_OptionsSubAudioThirdPartyCreditsDlg.Get()) + { + m_OptionsSubAudioThirdPartyCreditsDlg = new COptionsSubAudioThirdPartyCreditsDlg(GetVParent()); + } + m_OptionsSubAudioThirdPartyCreditsDlg->Activate(); +} diff --git a/gameui/OptionsSubAudio.h b/gameui/OptionsSubAudio.h new file mode 100644 index 0000000..e5e5a84 --- /dev/null +++ b/gameui/OptionsSubAudio.h @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_AUDIO_H +#define OPTIONS_SUB_AUDIO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PropertyPage.h" +#include <language.h> + +class CLabeledCommandComboBox; +class CCvarSlider; +class CCvarToggleCheckButton; + +//----------------------------------------------------------------------------- +// Purpose: Audio Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubAudio : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubAudio, vgui::PropertyPage ); + +public: + COptionsSubAudio(vgui::Panel *parent); + ~COptionsSubAudio(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + virtual void OnCommand( const char *command ); + bool RequiresRestart(); + static char* GetUpdatedAudioLanguage() { return m_pchUpdatedAudioLanguage; } + +private: + MESSAGE_FUNC( OnControlModified, "ControlModified" ); + MESSAGE_FUNC( OnTextChanged, "TextChanged" ) + { + OnControlModified(); + } + + MESSAGE_FUNC( RunTestSpeakers, "RunTestSpeakers" ); + + vgui::ComboBox *m_pSpeakerSetupCombo; + vgui::ComboBox *m_pSoundQualityCombo; + CCvarSlider *m_pSFXSlider; + CCvarSlider *m_pMusicSlider; + vgui::ComboBox *m_pCloseCaptionCombo; + bool m_bRequireRestart; + + vgui::ComboBox *m_pSpokenLanguageCombo; + MESSAGE_FUNC( OpenThirdPartySoundCreditsDialog, "OpenThirdPartySoundCreditsDialog" ); + vgui::DHANDLE<class COptionsSubAudioThirdPartyCreditsDlg> m_OptionsSubAudioThirdPartyCreditsDlg; + ELanguage m_nCurrentAudioLanguage; + static char *m_pchUpdatedAudioLanguage; + + CCvarToggleCheckButton *m_pSoundMuteLoseFocusCheckButton; +}; + + + +#endif // OPTIONS_SUB_AUDIO_H diff --git a/gameui/OptionsSubDifficulty.cpp b/gameui/OptionsSubDifficulty.cpp new file mode 100644 index 0000000..2e2e47f --- /dev/null +++ b/gameui/OptionsSubDifficulty.cpp @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "OptionsSubDifficulty.h" +#include "tier1/convar.h" +#include "EngineInterface.h" +#include "tier1/KeyValues.h" + +#include "vgui_controls/RadioButton.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +COptionsSubDifficulty::COptionsSubDifficulty(vgui::Panel *parent) : BaseClass(parent, NULL) +{ + m_pEasyRadio = new RadioButton(this, "Skill1Radio", "#GameUI_SkillEasy"); + m_pNormalRadio = new RadioButton(this, "Skill2Radio", "#GameUI_SkillNormal"); + m_pHardRadio = new RadioButton(this, "Skill3Radio", "#GameUI_SkillHard"); + + LoadControlSettings("Resource/OptionsSubDifficulty.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: resets controls +//----------------------------------------------------------------------------- +void COptionsSubDifficulty::OnResetData() +{ + ConVarRef var( "skill" ); + + if (var.GetInt() == 1) + { + m_pEasyRadio->SetSelected(true); + } + else if (var.GetInt() == 3) + { + m_pHardRadio->SetSelected(true); + } + else + { + m_pNormalRadio->SetSelected(true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets data based on control settings +//----------------------------------------------------------------------------- +void COptionsSubDifficulty::OnApplyChanges() +{ + ConVarRef var( "skill" ); + + if ( m_pEasyRadio->IsSelected() ) + { + var.SetValue( 1 ); + } + else if ( m_pHardRadio->IsSelected() ) + { + var.SetValue( 3 ); + } + else + { + var.SetValue( 2 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: enables apply button on radio buttons being pressed +//----------------------------------------------------------------------------- +void COptionsSubDifficulty::OnRadioButtonChecked() +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} diff --git a/gameui/OptionsSubDifficulty.h b/gameui/OptionsSubDifficulty.h new file mode 100644 index 0000000..947a172 --- /dev/null +++ b/gameui/OptionsSubDifficulty.h @@ -0,0 +1,38 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_DIFFICULTY_H +#define OPTIONS_SUB_DIFFICULTY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PropertyPage.h" + +//----------------------------------------------------------------------------- +// Purpose: Difficulty selection options +//----------------------------------------------------------------------------- +class COptionsSubDifficulty : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubDifficulty, vgui::PropertyPage ); + +public: + COptionsSubDifficulty(vgui::Panel *parent); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + + MESSAGE_FUNC( OnRadioButtonChecked, "RadioButtonChecked" ); + +private: + vgui::RadioButton *m_pEasyRadio; + vgui::RadioButton *m_pNormalRadio; + vgui::RadioButton *m_pHardRadio; +}; + + +#endif // OPTIONS_SUB_DIFFICULTY_H
\ No newline at end of file diff --git a/gameui/OptionsSubGame.cpp b/gameui/OptionsSubGame.cpp new file mode 100644 index 0000000..3092ea1 --- /dev/null +++ b/gameui/OptionsSubGame.cpp @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "OptionsSubGame.h" +#include "BasePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +COptionsSubGame::COptionsSubGame( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) +{ + SetDeleteSelfOnClose( true ); + LoadControlSettings( "Resource/OptionsSubGame.res" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +COptionsSubGame::~COptionsSubGame() +{ +} + +void COptionsSubGame::OnClose( void ) +{ + BasePanel()->RunCloseAnimation( "CloseOptionsSubGame" ); + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubGame::OnCommand( const char *command ) +{ + BaseClass::OnCommand( command ); +} diff --git a/gameui/OptionsSubGame.h b/gameui/OptionsSubGame.h new file mode 100644 index 0000000..f3c9b63 --- /dev/null +++ b/gameui/OptionsSubGame.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_GAME_H +#define OPTIONS_SUB_GAME_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +//----------------------------------------------------------------------------- +// Purpose: Game Settings, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubGame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsSubGame, vgui::Frame ); + +public: + COptionsSubGame( vgui::Panel *parent, const char *name ); + ~COptionsSubGame( void ); + + virtual void OnCommand( const char *command ); + virtual void OnClose( void ); + +private: +}; + + + +#endif // OPTIONS_SUB_GAME_H diff --git a/gameui/OptionsSubHaptics.cpp b/gameui/OptionsSubHaptics.cpp new file mode 100644 index 0000000..a567dbd --- /dev/null +++ b/gameui/OptionsSubHaptics.cpp @@ -0,0 +1,196 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "OptionsSubHaptics.h" +//#include "CommandCheckButton.h" +#include "KeyToggleCheckButton.h" +#include "CvarNegateCheckButton.h" +#include "CvarToggleCheckButton.h" +#include "cvarslider.h" + +#include "EngineInterface.h" + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include "tier1/convar.h" +#include <stdio.h> +#include <vgui_controls/TextEntry.h> +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +bool HasFalcon() { + ConVarRef hap_hasDevice("hap_hasDevice"); + return (hap_hasDevice.IsValid() && hap_hasDevice.GetBool()); +} + +void COptionsSubHaptics::OnCommand(const char *command) +{ + if ( !stricmp( command, "DoDefaults" ) ) + { + engine->ClientCmd_Unrestricted("exec haptics_default.cfg"); + OnResetData(); + } + +} +COptionsSubHaptics::COptionsSubHaptics(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + + m_pForceMasterPreLabel = new Label(this, "ForceMasterPreLabel", "#GameUI_Haptics_ForceMasterScale" ); + m_pForceMasterSlider = new CCvarSlider( this, "ForceMasterSlider", "#GameUI_Haptics_ForceMasterScale", + 0.000001f, 3.0f, "hap_ForceMasterScale", true ); + + m_pForceRecoilPreLabel = new Label(this, "ForceRecoilPreLabel", "#GameUI_Haptics_RecoilScale" ); + m_pForceRecoilSlider = new CCvarSlider( this, "ForceRecoilSlider", "#GameUI_Haptics_RecoilScale", + 0.000001f, 3.0f, "hap_ForceRecoilScale", true ); + + m_pForceDamagePreLabel = new Label(this, "ForceDamagePreLabel", "#GameUI_Haptics_DamageScale" ); + m_pForceDamageSlider = new CCvarSlider( this, "ForceDamageSlider", "#GameUI_Haptics_DamageScale", + 0.000001f, 5.0f, "hap_ForceDamageScale" , true ); + + m_pForceMovementPreLabel = new Label(this, "ForceMovementPreLabel", "#GameUI_Haptics_MovementScale" ); + m_pForceMovementSlider = new CCvarSlider( this, "ForceMovementSlider", "#GameUI_Haptics_MovementScale", + 0.000001f, 3.0f, "hap_ForceMovementScale", true ); + + //Player scaling + m_pPlayerBoxPreLabel = new Label(this, "PlayerBoxPreLabel", "#GameUI_Haptics_PlayerBoxLabel" ); + m_pPlayerBoxScalePreLabel = new Label(this, "PlayerScalePreLabel", "#GameUI_Haptics_Scale" ); + m_pPlayerBoxScale = new CCvarSlider( this, "PlayerBoxScaleSlider", "#GameUI_Haptics_PlayerBoxScale", + 0.1f, 0.9f, "hap_PlayerBoxScale", true ); + m_pPlayerBoxVisual = new ControlBoxVisual(this, "PlayerBoxVisual", m_pPlayerBoxScale, m_pPlayerBoxScale, m_pPlayerBoxScale, m_pPlayerBoxScale, m_pPlayerBoxScale, m_pPlayerBoxScale); + m_pPlayerBoxStiffnessPreLabel = new Label(this, "PlayerStiffnessPreLabel", "#GameUI_Haptics_PlayerBoxStiffness" ); + m_pPlayerBoxStiffnessSlider = new CCvarSlider( this, "PlayerStiffnessSlider", "#GameUI_Haptics_PlayerBoxScale", + 0.0f, 3.0f, "hap_PlayerBoxStiffness", true ); + m_pPlayerBoxTurnPreLabel = new Label(this, "PlayerTurnPreLabel", "#GameUI_Haptics_Turning" ); + m_pPlayerBoxTurnSlider = new CCvarSlider( this, "PlayerTurnSlider", "#GameUI_Haptics_Turning", + 0.0f, 2.0f, "hap_PlayerTurnScale", true ); + m_pPlayerBoxAimPreLabel = new Label(this, "PlayerAimPreLabel", "#GameUI_Haptics_Aiming" ); + m_pPlayerBoxAimSlider = new CCvarSlider( this, "PlayerAimSlider", "#GameUI_Haptics_Aiming", + 0.0f, 2.0f, "hap_PlayerAimScale", true ); + + + //Vehicle Scaling + m_pVehicleBoxPreLabel = new Label(this, "VehicleBoxPreLabel", "#GameUI_Haptics_VehicleBoxLabel" ); + m_pVehicleBoxScalePreLabel = new Label(this, "VehicleScalePreLabel", "#GameUI_Haptics_Scale" ); + m_pVehicleBoxScale = new CCvarSlider( this, "VehicleBoxScaleSlider", "#GameUI_Haptics_VehicleBoxScale", + 0.1f, 0.6f, "hap_VehicleBoxScale", true ); + m_pVehicleBoxVisual = new ControlBoxVisual(this, "VehicleBoxVisual", m_pVehicleBoxScale, m_pVehicleBoxScale, m_pVehicleBoxScale, m_pVehicleBoxScale, m_pVehicleBoxScale, m_pVehicleBoxScale); + m_pVehicleBoxStiffnessPreLabel = new Label(this, "VehicleStiffnessPreLabel", "#GameUI_Haptics_VehicleBoxStiffness" ); + m_pVehicleBoxStiffnessSlider = new CCvarSlider( this, "VehicleStiffnessSlider", "#GameUI_Haptics_VehicleBoxScale", + 0.0f, 3.0f, "hap_VehicleBoxStiffness", true ); + m_pVehicleBoxTurnPreLabel = new Label(this, "VehicleTurnPreLabel", "#GameUI_Haptics_Turning" ); + m_pVehicleBoxTurnSlider = new CCvarSlider( this, "VehicleTurnSlider", "#GameUI_Haptics_Turning", + 0.0f, 2.0f, "hap_VehicleTurnScale", true ); + m_pVehicleBoxAimPreLabel = new Label(this, "VehicleAimPreLabel", "#GameUI_Haptics_Aiming" ); + m_pVehicleBoxAimSlider = new CCvarSlider( this, "VehicleAimSlider", "#GameUI_Haptics_Aiming", + 0.0f, 2.0f, "hap_VehicleAimScale", true ); + + + vgui::Button *defaults = new vgui::Button( this, "Defaults", "Use Defaults" ); + defaults->SetCommand("DoDefaults"); + + LoadControlSettings("Resource\\OptionsSubHaptics.res"); + UpdateVehicleEnabled(); + +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubHaptics::~COptionsSubHaptics() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubHaptics::OnResetData() +{ + m_pForceMasterSlider->Reset(); + m_pForceDamageSlider->Reset(); + m_pForceRecoilSlider->Reset(); + m_pForceMovementSlider->Reset(); + + m_pPlayerBoxScale->Reset(); + m_pPlayerBoxStiffnessSlider->Reset(); + m_pPlayerBoxTurnSlider->Reset(); + m_pPlayerBoxAimSlider->Reset(); + + m_pVehicleBoxScale->Reset(); + m_pVehicleBoxStiffnessSlider->Reset(); + m_pVehicleBoxTurnSlider->Reset(); + m_pVehicleBoxAimSlider->Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubHaptics::OnApplyChanges() +{ + m_pForceMasterSlider->ApplyChanges(); + m_pForceDamageSlider->ApplyChanges(); + m_pForceRecoilSlider->ApplyChanges(); + m_pForceMovementSlider->ApplyChanges(); + + m_pPlayerBoxScale->ApplyChanges(); + m_pPlayerBoxStiffnessSlider->ApplyChanges(); + m_pPlayerBoxTurnSlider->ApplyChanges(); + m_pPlayerBoxAimSlider->ApplyChanges(); + + m_pVehicleBoxScale->ApplyChanges(); + m_pVehicleBoxStiffnessSlider->ApplyChanges(); + m_pVehicleBoxTurnSlider->ApplyChanges(); + m_pVehicleBoxAimSlider->ApplyChanges(); + + + //Write out our config file + engine->ClientCmd_Unrestricted("writehapticconfig"); + engine->ClientCmd_Unrestricted("reloadhaptics"); +} + +//----------------------------------------------------------------------------- +// Purpose: sets background color & border +//----------------------------------------------------------------------------- +void COptionsSubHaptics::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubHaptics::OnControlModified(Panel *panel) +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubHaptics::OnTextChanged(Panel *panel) +{ +} + +void COptionsSubHaptics::UpdateVehicleEnabled() +{ + ConVarRef checkVehicle("hap_ui_vehicles"); + checkVehicle.Init("hap_ui_vehicles",true); + bool bEnabled = checkVehicle.GetBool(); + + m_pVehicleBoxPreLabel->SetEnabled( bEnabled ); + m_pVehicleBoxScalePreLabel->SetEnabled( bEnabled ); + m_pVehicleBoxScale->SetEnabled( bEnabled ); + m_pVehicleBoxStiffnessPreLabel->SetEnabled( bEnabled ); + m_pVehicleBoxStiffnessSlider->SetEnabled( bEnabled ); + m_pVehicleBoxTurnPreLabel->SetEnabled( bEnabled ); + m_pVehicleBoxTurnSlider->SetEnabled( bEnabled ); + m_pVehicleBoxAimPreLabel ->SetEnabled( bEnabled ); + m_pVehicleBoxAimSlider ->SetEnabled( bEnabled ); +} diff --git a/gameui/OptionsSubHaptics.h b/gameui/OptionsSubHaptics.h new file mode 100644 index 0000000..d623cec --- /dev/null +++ b/gameui/OptionsSubHaptics.h @@ -0,0 +1,100 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_HAPTICS_H +#define OPTIONS_SUB_HAPTICS_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> +#include "HapticControlBox.h" + + +class CCvarNegateCheckButton; +class CKeyToggleCheckButton; +class CCvarToggleCheckButton; +class CCvarSlider; + +namespace vgui +{ + class Label; + class Panel; +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubHaptics : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubHaptics, vgui::PropertyPage ); + +public: + COptionsSubHaptics(vgui::Panel *parent); + ~COptionsSubHaptics(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + virtual void OnCommand(const char *command); + virtual void UpdateVehicleEnabled(void); + +protected: + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + MESSAGE_FUNC_PTR( OnControlModified, "ControlModified", panel ); + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ) + { + OnControlModified( panel ); + } + + vgui::Label *m_pForceMasterPreLabel; + CCvarSlider *m_pForceMasterSlider; + + vgui::Label *m_pForceRecoilPreLabel; + CCvarSlider *m_pForceRecoilSlider; + + + vgui::Label *m_pForceDamagePreLabel; + CCvarSlider *m_pForceDamageSlider; + + vgui::Label *m_pForceMovementPreLabel; + CCvarSlider *m_pForceMovementSlider; + + + + //Player + vgui::Label *m_pPlayerBoxPreLabel; + vgui::Label *m_pPlayerBoxScalePreLabel; + CCvarSlider * m_pPlayerBoxScale; + ControlBoxVisual* m_pPlayerBoxVisual; + vgui::Label *m_pPlayerBoxStiffnessPreLabel; + CCvarSlider *m_pPlayerBoxStiffnessSlider; + vgui::Label *m_pPlayerBoxTurnPreLabel; + CCvarSlider *m_pPlayerBoxTurnSlider; + vgui::Label *m_pPlayerBoxAimPreLabel; + CCvarSlider *m_pPlayerBoxAimSlider; + + //Vehicle + vgui::Label *m_pVehicleBoxPreLabel; + vgui::Label *m_pVehicleBoxScalePreLabel; + CCvarSlider * m_pVehicleBoxScale; + ControlBoxVisual* m_pVehicleBoxVisual; + vgui::Label *m_pVehicleBoxStiffnessPreLabel; + CCvarSlider * m_pVehicleBoxStiffnessSlider; + vgui::Label *m_pVehicleBoxTurnPreLabel; + CCvarSlider *m_pVehicleBoxTurnSlider; + vgui::Label *m_pVehicleBoxAimPreLabel; + CCvarSlider *m_pVehicleBoxAimSlider; + + //Control Box Stuff +}; + + + +#endif // OPTIONS_SUB_MOUSE_H
\ No newline at end of file diff --git a/gameui/OptionsSubKeyboard.cpp b/gameui/OptionsSubKeyboard.cpp new file mode 100644 index 0000000..f545917 --- /dev/null +++ b/gameui/OptionsSubKeyboard.cpp @@ -0,0 +1,839 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + + +#include "OptionsSubKeyboard.h" +#include "EngineInterface.h" +#include "vcontrolslistpanel.h" + +#include <vgui_controls/Button.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/ListPanel.h> +#include <vgui_controls/QueryBox.h> + +#include <vgui/Cursor.h> +#include <vgui/IVGui.h> +#include <vgui/ISurface.h> +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include <vgui/KeyCode.h> +#include <vgui/MouseCode.h> +#include <vgui/ISystem.h> +#include <vgui/IInput.h> + +#include "filesystem.h" +#include "tier1/utlbuffer.h" +#include "IGameUIFuncs.h" +#include <vstdlib/IKeyValuesSystem.h> +#include "tier2/tier2.h" +#include "inputsystem/iinputsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +COptionsSubKeyboard::COptionsSubKeyboard(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + memset( m_Bindings, 0, sizeof( m_Bindings )); + + // create the key bindings list + CreateKeyBindingList(); + // Store all current key bindings + SaveCurrentBindings(); + // Parse default descriptions + ParseActionDescriptions(); + + m_pSetBindingButton = new Button(this, "ChangeKeyButton", ""); + m_pClearBindingButton = new Button(this, "ClearKeyButton", ""); + + LoadControlSettings("Resource/OptionsSubKeyboard.res"); + + m_pSetBindingButton->SetEnabled(false); + m_pClearBindingButton->SetEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +COptionsSubKeyboard::~COptionsSubKeyboard() +{ + DeleteSavedBindings(); +} + +//----------------------------------------------------------------------------- +// Purpose: reloads current keybinding +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnResetData() +{ + // Populate list based on current settings + FillInCurrentBindings(); + if ( IsVisible() ) + { + m_pKeyBindList->SetSelectedItem(0); + } +} + +//----------------------------------------------------------------------------- +// Purpose: saves out keybinding changes +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnApplyChanges() +{ + ApplyAllBindings(); +} + +//----------------------------------------------------------------------------- +// Purpose: Create key bindings list control +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::CreateKeyBindingList() +{ + // Create the control + m_pKeyBindList = new VControlsListPanel(this, "listpanel_keybindlist"); +} + +//----------------------------------------------------------------------------- +// Purpose: binds double-clicking or hitting enter in the keybind list to changing the key +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnKeyCodeTyped(vgui::KeyCode code) +{ + if (code == KEY_ENTER) + { + OnCommand("ChangeKey"); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: command handler +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnCommand( const char *command ) +{ + if ( !stricmp( command, "Defaults" ) ) + { + // open a box asking if we want to restore defaults + QueryBox *box = new QueryBox("#GameUI_KeyboardSettings", "#GameUI_KeyboardSettingsText"); + box->AddActionSignalTarget(this); + box->SetOKCommand(new KeyValues("Command", "command", "DefaultsOK")); + box->DoModal(); + } + else if ( !stricmp(command, "DefaultsOK")) + { + // Restore defaults from default keybindings file + FillInDefaultBindings(); + m_pKeyBindList->RequestFocus(); + } + else if ( !m_pKeyBindList->IsCapturing() && !stricmp( command, "ChangeKey" ) ) + { + m_pKeyBindList->StartCaptureMode(dc_blank); + } + else if ( !m_pKeyBindList->IsCapturing() && !stricmp( command, "ClearKey" ) ) + { + OnKeyCodePressed(KEY_DELETE); + m_pKeyBindList->RequestFocus(); + } + else if ( !stricmp(command, "Advanced") ) + { + OpenKeyboardAdvancedDialog(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +const char *UTIL_Parse( const char *data, char *token, int sizeofToken ) +{ + data = engine->ParseFile( data, token, sizeofToken ); + return data; +} +static char *UTIL_CopyString( const char *in ) +{ + int len = strlen( in ) + 1; + char *out = new char[ len ]; + Q_strncpy( out, in, len ); + return out; +} + +#ifndef _XBOX +char *UTIL_va(char *format, ...) +{ + va_list argptr; + static char string[4][1024]; + static int curstring = 0; + + curstring = ( curstring + 1 ) % 4; + + va_start (argptr, format); + Q_vsnprintf( string[curstring], 1024, format, argptr ); + va_end (argptr); + + return string[curstring]; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::ParseActionDescriptions( void ) +{ + char szBinding[256]; + char szDescription[256]; + + KeyValues *item; + + // Load the default keys list + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( "scripts/kb_act.lst", NULL, buf ) ) + return; + + const char *data = (const char*)buf.Base(); + + int sectionIndex = 0; + char token[512]; + while ( 1 ) + { + data = UTIL_Parse( data, token, sizeof(token) ); + // Done. + if ( strlen( token ) <= 0 ) + break; + + Q_strncpy( szBinding, token, sizeof( szBinding ) ); + + data = UTIL_Parse( data, token, sizeof(token) ); + if ( strlen(token) <= 0 ) + { + break; + } + + Q_strncpy(szDescription, token, sizeof( szDescription ) ); + + // Skip '======' rows + if ( szDescription[ 0 ] != '=' ) + { + // Flag as special header row if binding is "blank" + if (!stricmp(szBinding, "blank")) + { + // add header item + m_pKeyBindList->AddSection(++sectionIndex, szDescription); + m_pKeyBindList->AddColumnToSection(sectionIndex, "Action", szDescription, SectionedListPanel::COLUMN_BRIGHT, 286); + m_pKeyBindList->AddColumnToSection(sectionIndex, "Key", "#GameUI_KeyButton", SectionedListPanel::COLUMN_BRIGHT, 128); + } + else + { + // Create a new: blank item + item = new KeyValues( "Item" ); + + // fill in data + item->SetString("Action", szDescription); + item->SetString("Binding", szBinding); + item->SetString("Key", ""); + + // Add to list + m_pKeyBindList->AddItem(sectionIndex, item); + item->deleteThis(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Search current data set for item which has the specified binding string +// Input : *binding - string to find +// Output : KeyValues or NULL on failure +//----------------------------------------------------------------------------- +KeyValues *COptionsSubKeyboard::GetItemForBinding( const char *binding ) +{ + static int bindingSymbol = KeyValuesSystem()->GetSymbolForString("Binding"); + + // Loop through all items + for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++) + { + KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i)); + if ( !item ) + continue; + + KeyValues *bindingItem = item->FindKey(bindingSymbol); + const char *bindString = bindingItem->GetString(); + + // Check the "Binding" key + if (!stricmp(bindString, binding)) + return item; + } + // Didn't find it + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Bind the specified keyname to the specified item +// Input : *item - Item to which to add the key +// *keyname - The key to be added +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::AddBinding( KeyValues *item, const char *keyname ) +{ + // See if it's already there as a binding + if ( !stricmp( item->GetString( "Key", "" ), keyname ) ) + return; + + // Make sure it doesn't live anywhere + RemoveKeyFromBindItems( item, keyname ); + + const char *binding = item->GetString( "Binding", "" ); + + // Loop through all the key bindings and set all entries that have + // the same binding. This allows us to have multiple entries pointing + // to the same binding. + for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++) + { + KeyValues *curitem = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i)); + if ( !curitem ) + continue; + + const char *curbinding = curitem->GetString( "Binding", "" ); + + if (!stricmp(curbinding, binding)) + { + curitem->SetString( "Key", keyname ); + m_pKeyBindList->InvalidateItem(i); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Remove all keys from list +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::ClearBindItems( void ) +{ + for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++) + { + KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i)); + if ( !item ) + continue; + + // Reset keys + item->SetString( "Key", "" ); + + m_pKeyBindList->InvalidateItem(i); + } + + m_pKeyBindList->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all instances of the specified key from bindings +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::RemoveKeyFromBindItems( KeyValues *org_item, const char *key ) +{ + Assert( key && key[ 0 ] ); + if ( !key || !key[ 0 ] ) + return; + + int len = Q_strlen( key ); + char *pszKey = new char[len + 1]; + + if ( !pszKey ) + return; + + Q_memcpy( pszKey, key, len+1 ); + + for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++) + { + KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i)); + if ( !item ) + continue; + + // If it's bound to the primary: then remove it + if ( !stricmp( pszKey, item->GetString( "Key", "" ) ) ) + { + bool bClearEntry = true; + + if ( org_item ) + { + // Only clear it out if the actual binding isn't the same. This allows + // us to have the same key bound to multiple entries in the keybinding list + // if they point to the same command. + const char *org_binding = org_item->GetString( "Binding", "" ); + const char *binding = item->GetString( "Binding", "" ); + if ( !stricmp( org_binding, binding ) ) + { + bClearEntry = false; + } + } + + if ( bClearEntry ) + { + item->SetString( "Key", "" ); + m_pKeyBindList->InvalidateItem(i); + } + } + } + + delete [] pszKey; + + // Make sure the display is up to date + m_pKeyBindList->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Ask the engine for all bindings and set up the list +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::FillInCurrentBindings( void ) +{ + // reset the unbind list + // we only unbind keys used by the normal config items (not custom binds) + m_KeysToUnbind.RemoveAll(); + + // Clear any current settings + ClearBindItems(); + + bool bJoystick = false; + ConVarRef var( "joystick" ); + if ( var.IsValid() ) + { + bJoystick = var.GetBool(); + } + + // NVNT see if we have a falcon connected. + bool bFalcon = false; + ConVarRef falconVar("hap_HasDevice"); + if ( var.IsValid() ) + { + bFalcon = var.GetBool(); + } + + for ( int i = 0; i < BUTTON_CODE_LAST; i++ ) + { + // Look up binding + const char *binding = gameuifuncs->GetBindingForButtonCode( (ButtonCode_t)i ); + if ( !binding ) + continue; + + // See if there is an item for this one? + KeyValues *item = GetItemForBinding( binding ); + if ( item ) + { + // Bind it by name + const char *keyName = g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i ); + + // Already in list, means user had two keys bound to this item. We'll only note the first one we encounter + char const *currentKey = item->GetString( "Key", "" ); + if ( currentKey && currentKey[ 0 ] ) + { + ButtonCode_t currentBC = (ButtonCode_t)gameuifuncs->GetButtonCodeForBind( currentKey ); + + // If we're using a joystick, joystick bindings override keyboard ones + bool bShouldOverride = bJoystick && IsJoystickCode((ButtonCode_t)i) && !IsJoystickCode(currentBC); + // NVNT If we're not using a joystick, falcon bindings override keyboard ones + if( !bShouldOverride && bFalcon && IsNovintCode((ButtonCode_t)i) && !IsNovintCode(currentBC) ) + bShouldOverride = true; + + if ( !bShouldOverride ) + continue; + + // Remove the key we're about to override from the unbinding list + m_KeysToUnbind.FindAndRemove( currentKey ); + } + + AddBinding( item, keyName ); + + // remember to apply unbinding of this key when we apply + m_KeysToUnbind.AddToTail( keyName ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up memory used by saved bindings +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::DeleteSavedBindings( void ) +{ + for ( int i = 0; i < BUTTON_CODE_LAST; i++ ) + { + if ( m_Bindings[ i ].binding ) + { + delete[] m_Bindings[ i ].binding; + m_Bindings[ i ].binding = NULL; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Copy all bindings into save array +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::SaveCurrentBindings( void ) +{ + DeleteSavedBindings(); + for (int i = 0; i < BUTTON_CODE_LAST; i++) + { + const char *binding = gameuifuncs->GetBindingForButtonCode( (ButtonCode_t)i ); + if ( !binding || !binding[0]) + continue; + + // Copy the binding string + m_Bindings[ i ].binding = UTIL_CopyString( binding ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tells the engine to bind a key +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::BindKey( const char *key, const char *binding ) +{ +#ifndef _XBOX + engine->ClientCmd_Unrestricted( UTIL_va( "bind \"%s\" \"%s\"\n", key, binding ) ); +#else + char buff[256]; + Q_snprintf(buff, sizeof(buff), "bind \"%s\" \"%s\"\n", key, binding); + engine->ClientCmd_Unrestricted(buff); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Tells the engine to unbind a key +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::UnbindKey( const char *key ) +{ +#ifndef _XBOX + engine->ClientCmd_Unrestricted( UTIL_va( "unbind \"%s\"\n", key ) ); +#else + char buff[256]; + Q_snprintf(buff, sizeof(buff), "unbind \"%s\"\n", key); + engine->ClientCmd_Unrestricted(buff); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Go through list and bind specified keys to actions +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::ApplyAllBindings( void ) +{ + // unbind everything that the user unbound + {for (int i = 0; i < m_KeysToUnbind.Count(); i++) + { + UnbindKey(m_KeysToUnbind[i].String()); + }} + m_KeysToUnbind.RemoveAll(); + + // free binding memory + DeleteSavedBindings(); + + for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++) + { + KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i)); + if ( !item ) + continue; + + // See if it has a binding + const char *binding = item->GetString( "Binding", "" ); + if ( !binding || !binding[ 0 ] ) + continue; + + const char *keyname; + + // Check main binding + keyname = item->GetString( "Key", "" ); + if ( keyname && keyname[ 0 ] ) + { + // Tell the engine + BindKey( keyname, binding ); + ButtonCode_t code = g_pInputSystem->StringToButtonCode( keyname ); + if ( code != BUTTON_CODE_INVALID ) + { + m_Bindings[ code ].binding = UTIL_CopyString( binding ); + } + } + } + + // Now exec their custom bindings + engine->ClientCmd_Unrestricted( "exec userconfig.cfg\nhost_writeconfig\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Read in defaults from game's default config file and populate list +// using those defaults +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::FillInDefaultBindings( void ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( "cfg/config_default.cfg", NULL, buf ) ) + return; + + // Clear out all current bindings + ClearBindItems(); + + const char *data = (const char*)buf.Base(); + + // loop through all the binding + while ( data != NULL ) + { + char cmd[64]; + data = UTIL_Parse( data, cmd, sizeof(cmd) ); + if ( strlen( cmd ) <= 0 ) + break; + + if ( !stricmp(cmd, "bind") ) + { + // Key name + char szKeyName[256]; + data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) ); + if ( strlen( szKeyName ) <= 0 ) + break; // Error + + char szBinding[256]; + data = UTIL_Parse( data, szBinding, sizeof(szBinding) ); + if ( strlen( szKeyName ) <= 0 ) + break; // Error + + // Find item + KeyValues *item = GetItemForBinding( szBinding ); + if ( item ) + { + // Bind it + AddBinding( item, szKeyName ); + } + } + } + + PostActionSignal(new KeyValues("ApplyButtonEnable")); + + // Make sure console and escape key are always valid + KeyValues *item = GetItemForBinding( "toggleconsole" ); + if (item) + { + // Bind it + AddBinding( item, "`" ); + } + item = GetItemForBinding( "cancelselect" ); + if (item) + { + // Bind it + AddBinding( item, "ESCAPE" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: User clicked on item: remember where last active row/column was +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::ItemSelected(int itemID) +{ + m_pKeyBindList->SetItemOfInterest(itemID); + + if (m_pKeyBindList->IsItemIDValid(itemID)) + { + // find the details, see if we should be enabled/clear/whatever + m_pSetBindingButton->SetEnabled(true); + + KeyValues *kv = m_pKeyBindList->GetItemData(itemID); + if (kv) + { + const char *key = kv->GetString("Key", NULL); + if (key && *key) + { + m_pClearBindingButton->SetEnabled(true); + } + else + { + m_pClearBindingButton->SetEnabled(false); + } + + if (kv->GetInt("Header")) + { + m_pSetBindingButton->SetEnabled(false); + } + } + } + else + { + m_pSetBindingButton->SetEnabled(false); + m_pClearBindingButton->SetEnabled(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: called when the capture has finished +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::Finish( ButtonCode_t code ) +{ + int r = m_pKeyBindList->GetItemOfInterest(); + + // Retrieve clicked row and column + m_pKeyBindList->EndCaptureMode( dc_arrow ); + + // Find item for this row + KeyValues *item = m_pKeyBindList->GetItemData(r); + if ( item ) + { + // Handle keys: but never rebind the escape key + // Esc just exits bind mode silently + if ( code != BUTTON_CODE_NONE && code != KEY_ESCAPE && code != BUTTON_CODE_INVALID ) + { + // Bind the named key + AddBinding( item, g_pInputSystem->ButtonCodeToString( code ) ); + PostActionSignal( new KeyValues( "ApplyButtonEnable" ) ); + } + + m_pKeyBindList->InvalidateItem(r); + } + + m_pSetBindingButton->SetEnabled(true); + m_pClearBindingButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Scans for captured key presses +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnThink() +{ + BaseClass::OnThink(); + + if ( m_pKeyBindList->IsCapturing() ) + { + ButtonCode_t code = BUTTON_CODE_INVALID; + if ( engine->CheckDoneKeyTrapping( code ) ) + { + Finish( code ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check for enter key and go into keybinding mode if it was pressed +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OnKeyCodePressed(vgui::KeyCode code) +{ + // Enter key pressed and not already trapping next key/button press + if ( !m_pKeyBindList->IsCapturing() ) + { + // Grab which item was set as interesting + int r = m_pKeyBindList->GetItemOfInterest(); + + // Check that it's visible + int x, y, w, h; + bool visible = m_pKeyBindList->GetCellBounds(r, 1, x, y, w, h); + if (visible) + { + if ( KEY_DELETE == code ) + { + // find the current binding and remove it + KeyValues *kv = m_pKeyBindList->GetItemData(r); + + const char *key = kv->GetString("Key", NULL); + if (key && *key) + { + RemoveKeyFromBindItems(NULL, key); + } + + m_pClearBindingButton->SetEnabled(false); + m_pKeyBindList->InvalidateItem(r); + PostActionSignal(new KeyValues("ApplyButtonEnable")); + + // message handled, don't pass on + return; + } + } + } + + // Allow base class to process message instead + BaseClass::OnKeyCodePressed( code ); +} + + +//----------------------------------------------------------------------------- +// Purpose: advanced keyboard settings dialog +//----------------------------------------------------------------------------- +class COptionsSubKeyboardAdvancedDlg : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsSubKeyboardAdvancedDlg, vgui::Frame ); +public: + COptionsSubKeyboardAdvancedDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL ) + { + // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below) + + SetTitle("#GameUI_KeyboardAdvanced_Title", true); + SetSize( 280, 140 ); + LoadControlSettings( "resource/OptionsSubKeyboardAdvancedDlg.res" ); + MoveToCenterOfScreen(); + SetSizeable( false ); + SetDeleteSelfOnClose( true ); + } + + virtual void Activate() + { + BaseClass::Activate(); + + input()->SetAppModalSurface(GetVPanel()); + + // reset the data + ConVarRef con_enable( "con_enable" ); + if ( con_enable.IsValid() ) + { + SetControlInt("ConsoleCheck", con_enable.GetInt() ? 1 : 0); + } + + ConVarRef hud_fastswitch( "hud_fastswitch" ); + if ( hud_fastswitch.IsValid() ) + { + SetControlInt("FastSwitchCheck", hud_fastswitch.GetInt() ? 1 : 0); + } + } + + virtual void OnApplyData() + { + // apply data + ConVarRef con_enable( "con_enable" ); + con_enable.SetValue( GetControlInt( "ConsoleCheck", 0 ) ); + + ConVarRef hud_fastswitch( "hud_fastswitch" ); + hud_fastswitch.SetValue( GetControlInt( "FastSwitchCheck", 0 ) ); + } + + virtual void OnCommand( const char *command ) + { + if ( !stricmp(command, "OK") ) + { + // apply the data + OnApplyData(); + Close(); + } + else + { + BaseClass::OnCommand( command ); + } + } + + void OnKeyCodeTyped(KeyCode code) + { + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Open advanced keyboard options +//----------------------------------------------------------------------------- +void COptionsSubKeyboard::OpenKeyboardAdvancedDialog() +{ + if (!m_OptionsSubKeyboardAdvancedDlg.Get()) + { + m_OptionsSubKeyboardAdvancedDlg = new COptionsSubKeyboardAdvancedDlg(GetVParent()); + } + m_OptionsSubKeyboardAdvancedDlg->Activate(); +} diff --git a/gameui/OptionsSubKeyboard.h b/gameui/OptionsSubKeyboard.h new file mode 100644 index 0000000..92b2818 --- /dev/null +++ b/gameui/OptionsSubKeyboard.h @@ -0,0 +1,101 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef OPTIONS_SUB_KEYBOARD_H +#define OPTIONS_SUB_KEYBOARD_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" +#include "utlsymbol.h" + +#include <vgui_controls/PropertyPage.h> +class VControlsListPanel; + +//----------------------------------------------------------------------------- +// Purpose: Keyboard Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubKeyboard : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubKeyboard, vgui::PropertyPage ); + +public: + COptionsSubKeyboard(vgui::Panel *parent); + ~COptionsSubKeyboard(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnThink(); + + // Trap row selection message + MESSAGE_FUNC_INT( ItemSelected, "ItemSelected", itemID ); + +private: + void Finish( ButtonCode_t code ); + + //----------------------------------------------------------------------------- + // Purpose: Used for saving engine keybindings in case user hits cancel button + //----------------------------------------------------------------------------- + struct KeyBinding + { + char *binding; + }; + + // Create the key binding list control + void CreateKeyBindingList( void ); + + virtual void OnCommand( const char *command ); + + // Tell engine to bind/unbind a key + void BindKey( const char *key, const char *binding ); + void UnbindKey( const char *key ); + + // Save/restore/cleanup engine's current bindings ( for handling cancel button ) + void SaveCurrentBindings( void ); + void DeleteSavedBindings( void ); + + // Get column 0 action descriptions for all keys + void ParseActionDescriptions( void ); + + // Populate list of actions with current engine keybindings + void FillInCurrentBindings( void ); + // Remove all current bindings from list of bindings + void ClearBindItems( void ); + // Fill in bindings with mod-specified defaults + void FillInDefaultBindings( void ); + // Copy bindings out of list and set them in the engine + void ApplyAllBindings( void ); + + // Bind a key to the item + void AddBinding( KeyValues *item, const char *keyname ); + + // Remove all instances of a key from all bindings + void RemoveKeyFromBindItems( KeyValues *org_item, const char *key ); + + // Find item by binding name + KeyValues *GetItemForBinding( const char *binding ); + +private: + void OpenKeyboardAdvancedDialog(); + vgui::DHANDLE<class COptionsSubKeyboardAdvancedDlg> m_OptionsSubKeyboardAdvancedDlg; + virtual void OnKeyCodeTyped(vgui::KeyCode code); + + VControlsListPanel *m_pKeyBindList; + + vgui::Button *m_pSetBindingButton; + vgui::Button *m_pClearBindingButton; + + // List of saved bindings for the keys + KeyBinding m_Bindings[ BUTTON_CODE_LAST ]; + + // List of all the keys that need to have their binding removed + CUtlVector<CUtlSymbol> m_KeysToUnbind; +}; + +#endif // OPTIONS_SUB_KEYBOARD_H
\ No newline at end of file diff --git a/gameui/OptionsSubMouse.cpp b/gameui/OptionsSubMouse.cpp new file mode 100644 index 0000000..806b6fd --- /dev/null +++ b/gameui/OptionsSubMouse.cpp @@ -0,0 +1,261 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "OptionsSubMouse.h" +//#include "CommandCheckButton.h" +#include "KeyToggleCheckButton.h" +#include "CvarNegateCheckButton.h" +#include "CvarToggleCheckButton.h" +#include "cvarslider.h" + +#include "EngineInterface.h" + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include "tier1/convar.h" +#include <stdio.h> +#include <vgui_controls/TextEntry.h> +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +COptionsSubMouse::COptionsSubMouse(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + m_pReverseMouseCheckBox = new CCvarNegateCheckButton( + this, + "ReverseMouse", + "#GameUI_ReverseMouse", + "m_pitch" ); + + m_pMouseFilterCheckBox = new CCvarToggleCheckButton( + this, + "MouseFilter", + "#GameUI_MouseFilter", + "m_filter" ); + + m_pMouseRawCheckBox = new CCvarToggleCheckButton( + this, + "MouseRaw", + "#GameUI_MouseRaw", + "m_rawinput" ); + + m_pMouseAccelerationCheckBox = new CheckButton( + this, + "MouseAccelerationCheckbox", + "#GameUI_MouseCustomAccel" ); + + + m_pJoystickCheckBox = new CCvarToggleCheckButton( + this, + "Joystick", + "#GameUI_Joystick", + "joystick" ); + + m_pJoystickSouthpawCheckBox = new CCvarToggleCheckButton( + this, + "JoystickSouthpaw", + "#GameUI_JoystickSouthpaw", + "joy_movement_stick" ); + + m_pReverseJoystickCheckBox = new CCvarToggleCheckButton( + this, + "ReverseJoystick", + "#GameUI_ReverseJoystick", + "joy_inverty" ); + + m_pQuickInfoCheckBox = new CCvarToggleCheckButton( + this, + "HudQuickInfo", + "#GameUI_HudQuickInfo", + "hud_quickinfo" ); + + m_pMouseSensitivitySlider = new CCvarSlider( this, "Slider", "#GameUI_MouseSensitivity", + 0.1f, 6.0f, "sensitivity", true ); + + m_pMouseSensitivityLabel = new TextEntry(this, "SensitivityLabel"); + m_pMouseSensitivityLabel->AddActionSignalTarget(this); + + m_pMouseAccelExponentSlider = new CCvarSlider( this, "MouseAccelerationSlider", "#GameUI_MouseAcceleration", + 1.0f, 1.4f, "m_customaccel_exponent", true ); + + m_pMouseAccelExponentLabel = new TextEntry(this, "MouseAccelerationLabel"); + m_pMouseAccelExponentLabel->AddActionSignalTarget(this); + + m_pJoyYawSensitivitySlider = new CCvarSlider( this, "JoystickYawSlider", "#GameUI_JoystickYawSensitivity", + -0.5f, -7.0f, "joy_yawsensitivity", true ); + m_pJoyYawSensitivityPreLabel = new Label(this, "JoystickYawSensitivityPreLabel", "#GameUI_JoystickLookSpeedYaw" ); + + m_pJoyPitchSensitivitySlider = new CCvarSlider( this, "JoystickPitchSlider", "#GameUI_JoystickPitchSensitivity", + 0.5f, 7.0f, "joy_pitchsensitivity", true ); + m_pJoyPitchSensitivityPreLabel = new Label(this, "JoystickPitchSensitivityPreLabel", "#GameUI_JoystickLookSpeedPitch" ); + + LoadControlSettings("Resource\\OptionsSubMouse.res"); + + UpdateSensitivityLabel(); + UpdateAccelerationLabel(); + + UpdateJoystickPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubMouse::~COptionsSubMouse() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::OnResetData() +{ + m_pReverseMouseCheckBox->Reset(); + m_pMouseFilterCheckBox->Reset(); + m_pMouseRawCheckBox->Reset(); + m_pJoystickCheckBox->Reset(); + m_pJoystickSouthpawCheckBox->Reset(); + m_pMouseSensitivitySlider->Reset(); + m_pMouseAccelExponentSlider->Reset(); + m_pQuickInfoCheckBox->Reset(); + m_pReverseJoystickCheckBox->Reset(); + m_pJoyYawSensitivitySlider->Reset(); + m_pJoyPitchSensitivitySlider->Reset(); + + ConVarRef m_customacel("m_customaccel"); + if ( m_customacel.IsValid() ) + m_pMouseAccelerationCheckBox->SetSelected( m_customacel.GetBool() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::OnApplyChanges() +{ + m_pReverseMouseCheckBox->ApplyChanges(); + m_pMouseFilterCheckBox->ApplyChanges(); + m_pMouseRawCheckBox->ApplyChanges(); + m_pJoystickCheckBox->ApplyChanges(); + m_pJoystickSouthpawCheckBox->ApplyChanges(); + m_pMouseSensitivitySlider->ApplyChanges(); + m_pMouseAccelExponentSlider->ApplyChanges(); + m_pQuickInfoCheckBox->ApplyChanges(); + m_pReverseJoystickCheckBox->ApplyChanges(); + m_pJoyYawSensitivitySlider->ApplyChanges(); + m_pJoyPitchSensitivitySlider->ApplyChanges(); + + engine->ClientCmd_Unrestricted( "joyadvancedupdate" ); + + ConVarRef m_customacel("m_customaccel"); + if ( m_customacel.IsValid() ) + m_customacel.SetValue(m_pMouseAccelerationCheckBox->IsSelected() ? 3 : 0); +} + +//----------------------------------------------------------------------------- +// Purpose: sets background color & border +//----------------------------------------------------------------------------- +void COptionsSubMouse::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::OnControlModified(Panel *panel) +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); + + // the HasBeenModified() check is so that if the value is outside of the range of the + // slider, it won't use the slider to determine the display value but leave the + // real value that we determined in the constructor + if (panel == m_pMouseSensitivitySlider && m_pMouseSensitivitySlider->HasBeenModified()) + { + UpdateSensitivityLabel(); + } + else if (panel == m_pMouseAccelExponentSlider && m_pMouseAccelExponentSlider->HasBeenModified()) + { + UpdateAccelerationLabel(); + } + else if (panel == m_pJoystickCheckBox) + { + UpdateJoystickPanels(); + } + else if (panel == m_pMouseAccelerationCheckBox) + { + m_pMouseAccelExponentSlider->SetEnabled(m_pMouseAccelerationCheckBox->IsSelected()); + m_pMouseAccelExponentLabel->SetEnabled(m_pMouseAccelerationCheckBox->IsSelected()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::OnTextChanged(Panel *panel) +{ + if ( panel == m_pMouseSensitivityLabel ) + { + char buf[64]; + m_pMouseSensitivityLabel->GetText(buf, 64); + + float fValue; + int numParsed = sscanf(buf, "%f", &fValue); + if ( ( numParsed == 1 ) && ( fValue >= 0.0f ) ) + { + m_pMouseSensitivitySlider->SetSliderValue(fValue); + PostActionSignal(new KeyValues("ApplyButtonEnable")); + } + return; + } + + if ( panel == m_pMouseAccelExponentLabel ) + { + char buf[64]; + m_pMouseAccelExponentLabel->GetText(buf, 64); + + float fValue = (float) atof(buf); + if (fValue >= 1.0) + { + m_pMouseAccelExponentSlider->SetSliderValue(fValue); + PostActionSignal(new KeyValues("ApplyButtonEnable")); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::UpdateSensitivityLabel() +{ + char buf[64]; + Q_snprintf(buf, sizeof( buf ), " %.2f", m_pMouseSensitivitySlider->GetSliderValue()); + m_pMouseSensitivityLabel->SetText(buf); +} + + +void COptionsSubMouse::UpdateAccelerationLabel() +{ + char buf[64]; + Q_snprintf(buf, sizeof( buf ), " %.2f", m_pMouseAccelExponentSlider->GetSliderValue()); + m_pMouseAccelExponentLabel->SetText(buf); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMouse::UpdateJoystickPanels() +{ + bool bEnabled = m_pJoystickCheckBox->IsSelected(); + + m_pReverseJoystickCheckBox->SetEnabled( bEnabled ); + m_pJoystickSouthpawCheckBox->SetEnabled( bEnabled ); + m_pJoyYawSensitivitySlider->SetEnabled( bEnabled ); + m_pJoyYawSensitivityPreLabel->SetEnabled( bEnabled ); + m_pJoyPitchSensitivitySlider->SetEnabled( bEnabled ); + m_pJoyPitchSensitivityPreLabel->SetEnabled( bEnabled ); +}
\ No newline at end of file diff --git a/gameui/OptionsSubMouse.h b/gameui/OptionsSubMouse.h new file mode 100644 index 0000000..9ed4c80 --- /dev/null +++ b/gameui/OptionsSubMouse.h @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_MOUSE_H +#define OPTIONS_SUB_MOUSE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> + +class CCvarNegateCheckButton; +class CKeyToggleCheckButton; +class CCvarToggleCheckButton; +class CCvarSlider; + +namespace vgui +{ + class Label; + class Panel; +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubMouse : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubMouse, vgui::PropertyPage ); + +public: + COptionsSubMouse(vgui::Panel *parent); + ~COptionsSubMouse(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + +protected: + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + MESSAGE_FUNC_PTR( OnControlModified, "ControlModified", panel ); + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ) + { + OnControlModified( panel ); + } + + void UpdateSensitivityLabel(); + void UpdateAccelerationLabel(); + void UpdateJoystickPanels(); + +private: + CCvarNegateCheckButton *m_pReverseMouseCheckBox; + CCvarToggleCheckButton *m_pMouseFilterCheckBox; + CCvarToggleCheckButton *m_pMouseRawCheckBox; + vgui::CheckButton *m_pMouseAccelerationCheckBox; + + CCvarToggleCheckButton *m_pJoystickCheckBox; + CCvarToggleCheckButton *m_pJoystickSouthpawCheckBox; + CCvarToggleCheckButton *m_pQuickInfoCheckBox; + CCvarToggleCheckButton *m_pReverseJoystickCheckBox; + + CCvarSlider *m_pMouseSensitivitySlider; + vgui::TextEntry *m_pMouseSensitivityLabel; + + CCvarSlider *m_pMouseAccelExponentSlider; + vgui::TextEntry *m_pMouseAccelExponentLabel; + + CCvarSlider *m_pJoyYawSensitivitySlider; + vgui::Label *m_pJoyYawSensitivityPreLabel; + CCvarSlider *m_pJoyPitchSensitivitySlider; + vgui::Label *m_pJoyPitchSensitivityPreLabel; +}; + + + +#endif // OPTIONS_SUB_MOUSE_H
\ No newline at end of file diff --git a/gameui/OptionsSubMultiplayer.cpp b/gameui/OptionsSubMultiplayer.cpp new file mode 100644 index 0000000..3a262d0 --- /dev/null +++ b/gameui/OptionsSubMultiplayer.cpp @@ -0,0 +1,1890 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#if defined( WIN32 ) && !defined( _X360 ) +#include <windows.h> // SRC only!! +#endif + +#include "OptionsSubMultiplayer.h" +#include "MultiplayerAdvancedDialog.h" +#include <stdio.h> + +#include <vgui_controls/Button.h> +#include <vgui_controls/QueryBox.h> +#include <vgui_controls/CheckButton.h> +#include "tier1/KeyValues.h" +#include <vgui_controls/Label.h> +#include <vgui/ISystem.h> +#include <vgui/ISurface.h> +#include <vgui/Cursor.h> +#include <vgui_controls/RadioButton.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/FileOpenDialog.h> +#include <vgui_controls/MessageBox.h> +#include <vgui/IVGui.h> +#include <vgui/ILocalize.h> +#include <vgui/IPanel.h> +#include <vgui_controls/MessageBox.h> + +#include "CvarTextEntry.h" +#include "CvarToggleCheckButton.h" +#include "cvarslider.h" +#include "LabeledCommandComboBox.h" +#include "filesystem.h" +#include "EngineInterface.h" +#include "BitmapImagePanel.h" +#include "tier1/utlbuffer.h" +#include "ModInfo.h" +#include "tier1/convar.h" +#include "tier0/icommandline.h" + +#include "materialsystem/imaterial.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterialvar.h" + +// use the JPEGLIB_USE_STDIO define so that we can read in jpeg's from outside the game directory tree. For Spray Import. +#define JPEGLIB_USE_STDIO +#include "jpeglib/jpeglib.h" +#undef JPEGLIB_USE_STDIO + +#include <setjmp.h> + +#include "bitmap/tgawriter.h" +#include "ivtex.h" +#ifdef WIN32 +#include <io.h> +#endif + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + + +#define DEFAULT_SUIT_HUE 30 +#define DEFAULT_PLATE_HUE 6 + +void UpdateLogoWAD( void *hdib, int r, int g, int b ); + +struct ColorItem_t +{ + const char *name; + int r, g, b; +}; + +static ColorItem_t itemlist[]= +{ + { "#Valve_Orange", 255, 120, 24 }, + { "#Valve_Yellow", 225, 180, 24 }, + { "#Valve_Blue", 0, 60, 255 }, + { "#Valve_Ltblue", 0, 167, 255 }, + { "#Valve_Green", 0, 167, 0 }, + { "#Valve_Red", 255, 43, 0 }, + { "#Valve_Brown", 123, 73, 0 }, + { "#Valve_Ltgray", 100, 100, 100 }, + { "#Valve_Dkgray", 36, 36, 36 }, +}; + +static ColorItem_t s_crosshairColors[] = +{ + { "#Valve_Green", 50, 250, 50 }, + { "#Valve_Red", 250, 50, 50 }, + { "#Valve_Blue", 50, 50, 250 }, + { "#Valve_Yellow", 250, 250, 50 }, + { "#Valve_Ltblue", 50, 250, 250 }, +}; +static const int NumCrosshairColors = ARRAYSIZE(s_crosshairColors); + + +//----------------------------------------------------------------------------- +class CrosshairImagePanelSimple : public CrosshairImagePanelBase +{ + DECLARE_CLASS_SIMPLE( CrosshairImagePanelSimple, CrosshairImagePanelBase ); +public: + CrosshairImagePanelSimple( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ); + virtual void Paint(); + virtual void ResetData(); + virtual void ApplyChanges(); + static void DrawCrosshairRect( int x, int y, int w, int h, bool bAdditive ); + +protected: + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + + void InitCrosshairColorEntries(); + void InitCrosshairSizeList(); + void UpdateCrosshair(); + +private: + COptionsSubMultiplayer* m_pOptionsPanel; + vgui::ComboBox *m_pCrosshairColorCombo; + CLabeledCommandComboBox *m_pCrosshairSizeCombo; + CCvarToggleCheckButton *m_pCrosshairTranslucencyCheckbox; + int m_R, m_G, m_B; + int m_barSize; + int m_barGap; + int m_iCrosshairTextureID; +}; + +//----------------------------------------------------------------------------- +CrosshairImagePanelSimple::CrosshairImagePanelSimple( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ) : CrosshairImagePanelBase( parent, name ) +{ + m_pOptionsPanel = pOptionsPanel; + m_pCrosshairTranslucencyCheckbox = new CCvarToggleCheckButton(m_pOptionsPanel, "CrosshairTranslucencyCheckbox", "#GameUI_Translucent", "cl_crosshairusealpha"); + m_pCrosshairColorCombo = new ComboBox(m_pOptionsPanel, "CrosshairColorComboBox", 6, false); + m_pCrosshairSizeCombo = new CLabeledCommandComboBox(m_pOptionsPanel, "CrosshairSizeComboBox"); + + m_pCrosshairColorCombo->AddActionSignalTarget( this ); + m_pCrosshairSizeCombo->AddActionSignalTarget( this ); + + m_iCrosshairTextureID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( m_iCrosshairTextureID, "vgui/white_additive" , true, false); + + InitCrosshairSizeList(); + InitCrosshairColorEntries(); + ResetData(); +} + +//----------------------------------------------------------------------------- +// Purpose: initialize the crosshair size list. +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::InitCrosshairSizeList() +{ + // add in the auto, small, medium, and large size selections. + m_pCrosshairSizeCombo->AddItem("#GameUI_Auto", "cl_crosshairscale 0"); + m_pCrosshairSizeCombo->AddItem("#GameUI_Small", "cl_crosshairscale 1200"); + m_pCrosshairSizeCombo->AddItem("#GameUI_Medium", "cl_crosshairscale 768"); + m_pCrosshairSizeCombo->AddItem("#GameUI_Large", "cl_crosshairscale 600"); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::InitCrosshairColorEntries() +{ + if (m_pCrosshairColorCombo != NULL) + { + KeyValues *data = new KeyValues("data"); + + // add in the "Default" selection + data->Clear(); + + // add in the colors for the color list + for ( int i = 0; i < NumCrosshairColors; i++ ) + { + data->SetInt("color", i); + m_pCrosshairColorCombo->AddItem( s_crosshairColors[ i ].name, data); + } + + data->deleteThis(); + } +} + + +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::DrawCrosshairRect( int x0, int y0, int x1, int y1, bool bAdditive ) +{ + if ( bAdditive ) + vgui::surface()->DrawTexturedRect( x0, y0, x1, y1 ); + else + vgui::surface()->DrawFilledRect( x0, y0, x1, y1 ); +} + +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::Paint() +{ + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall );; + + BaseClass::Paint(); + + if ( !m_pCrosshairTranslucencyCheckbox ) + return; + + int wide, tall; + GetSize( wide, tall ); + + bool bAdditive = !m_pCrosshairTranslucencyCheckbox->IsSelected(); + + int a = 200; + ConVarRef cl_crosshairalpha( "cl_crosshairalpha", true ); + if ( !bAdditive && cl_crosshairalpha.IsValid() ) + { + a = clamp( cl_crosshairalpha.GetInt(), 0, 255 ); + } + vgui::surface()->DrawSetColor( m_R, m_G, m_B, a ); + + if ( bAdditive ) + { + vgui::surface()->DrawSetTexture( m_iCrosshairTextureID ); + } + + int centerX = wide / 2; + int centerY = tall / 2; + int iCrosshairDistance = m_barGap; + + int iBarThickness = 1; + int iBarSize = m_barSize; + + // draw horizontal crosshair lines + int iInnerLeft = centerX - iCrosshairDistance - iBarThickness / 2; + int iInnerRight = iInnerLeft + 2 * iCrosshairDistance + iBarThickness; + int iOuterLeft = iInnerLeft - iBarSize; + int iOuterRight = iInnerRight + iBarSize; + int y0 = centerY - iBarThickness / 2; + int y1 = y0 + iBarThickness; + DrawCrosshairRect( iOuterLeft, y0, iInnerLeft, y1, bAdditive ); + DrawCrosshairRect( iInnerRight, y0, iOuterRight, y1, bAdditive ); + + // draw vertical crosshair lines + int iInnerTop = centerY - iCrosshairDistance - iBarThickness / 2; + int iInnerBottom = iInnerTop + 2 * iCrosshairDistance + iBarThickness; + int iOuterTop = iInnerTop - iBarSize; + int iOuterBottom = iInnerBottom + iBarSize; + int x0 = centerX - iBarThickness / 2; + int x1 = x0 + iBarThickness; + DrawCrosshairRect( x0, iOuterTop, x1, iInnerTop, bAdditive ); + DrawCrosshairRect( x0, iInnerBottom, x1, iOuterBottom, bAdditive ); +} + + +//----------------------------------------------------------------------------- +// Purpose: takes the settings from the crosshair settings combo boxes and sliders +// and apply it to the crosshair illustrations. +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::UpdateCrosshair() +{ + // get the color selected in the combo box. + KeyValues *data = m_pCrosshairColorCombo->GetActiveItemUserData(); + int colorIndex = data->GetInt("color"); + colorIndex = clamp( colorIndex, 0, NumCrosshairColors ); + + int selectedColor = 0; + int actualVal = 0; + if (m_pCrosshairColorCombo != NULL) + { + selectedColor = m_pCrosshairColorCombo->GetActiveItem(); + } + + ConVarRef cl_crosshaircolor( "cl_crosshaircolor", true ); + if ( cl_crosshaircolor.IsValid() ) + { + actualVal = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors ); + } + + m_R = s_crosshairColors[selectedColor].r; + m_G = s_crosshairColors[selectedColor].g; + m_B = s_crosshairColors[selectedColor].b; + + if ( m_pCrosshairSizeCombo ) + { + int size = m_pCrosshairSizeCombo->GetActiveItem(); + + if ( size == 0 ) + { + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall ); + if ( screenTall <= 600 ) + { + // if the screen height is 600 or less, set the crosshair num to 3 (large) + size = 3; + } + else if ( screenTall <= 768 ) + { + // if the screen height is between 600 and 768, set the crosshair num to 2 (medium) + size = 2; + } + else + { + // if the screen height is greater than 768, set the crosshair num to 1 (small) + size = 1; + } + } + + int scaleBase; + switch( size ) + { + case 1: + scaleBase = 1200; + break; + case 2: + scaleBase = 768; + break; + case 3: + scaleBase = 600; + break; + default: + scaleBase = 1200; + break; + } + + m_barSize = (int) 9 * 1200 / scaleBase; + m_barGap = (int) 5 * 1200 / scaleBase; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever color combo changes +//----------------------------------------------------------------------------- +void CrosshairImagePanelSimple::OnTextChanged(vgui::Panel *panel) +{ + m_pOptionsPanel->OnControlModified(); + UpdateCrosshair(); +} + + +void CrosshairImagePanelSimple::ResetData() +{ + m_pCrosshairTranslucencyCheckbox->Reset(); + + // parse out the size value from the cvar and set the initial value. + int initialScale = 0; + ConVarRef cl_crosshairscale( "cl_crosshairscale", true ); + if ( cl_crosshairscale.IsValid() ) + { + initialScale = cl_crosshairscale.GetInt(); + if ( initialScale <= 0 ) + { + initialScale = 0; + } + else if ( initialScale <= 600 ) + { + initialScale = 3; + } + else if ( initialScale <= 768 ) + { + initialScale = 2; + } + else + { + initialScale = 1; + } + } + m_pCrosshairSizeCombo->SetInitialItem( initialScale ); + + // parse the string for the custom color settings and get the initial settings. + ConVarRef cl_crosshaircolor( "cl_crosshaircolor", true ); + int index = 0; + if ( cl_crosshaircolor.IsValid() ) + { + index = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors ); + } + m_pCrosshairColorCombo->ActivateItemByRow(index); + + UpdateCrosshair(); +} + +void CrosshairImagePanelSimple::ApplyChanges() +{ + m_pCrosshairTranslucencyCheckbox->ApplyChanges(); + + char cmd[256]; + cmd[0] = 0; + + if (m_pCrosshairColorCombo != NULL) + { + int val = m_pCrosshairColorCombo->GetActiveItem(); + Q_snprintf( cmd, sizeof(cmd), "cl_crosshaircolor %d\n", val ); + engine->ClientCmd_Unrestricted( cmd ); + } +} + + +//----------------------------------------------------------------------------- +class CrosshairImagePanelCS : public CrosshairImagePanelBase +{ + DECLARE_CLASS_SIMPLE( CrosshairImagePanelCS, CrosshairImagePanelBase ); + +public: + CrosshairImagePanelCS( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ); + virtual void ResetData(); + virtual void ApplyChanges(); + +protected: + MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", data ); + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC( OnCheckButtonChecked, "CheckButtonChecked" ); + + virtual void Paint(); + static void DrawCrosshairRect( int x, int y, int w, int h, bool bAdditive ); + void InitCrosshairColorEntries(); + void UpdateCrosshair(); + +private: + COptionsSubMultiplayer* m_pOptionsPanel; + vgui::ComboBox *m_pColorComboBox; + CCvarToggleCheckButton *m_pAlphaCheckbox; + CCvarToggleCheckButton *m_pDynamicCheckbox; + CCvarToggleCheckButton *m_pDotCheckbox; + CCvarSlider *m_pColorAlphaSlider; + CCvarSlider *m_pColorRSlider; + CCvarSlider *m_pColorGSlider; + CCvarSlider *m_pColorBSlider; + CCvarSlider *m_pSizeSlider; + CCvarSlider *m_pThicknessSlider; + int m_R, m_G, m_B; + float m_barSize; + float m_barThickness; + int m_iCrosshairTextureID; +}; + +//----------------------------------------------------------------------------- +CrosshairImagePanelCS::CrosshairImagePanelCS( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ) : CrosshairImagePanelBase( parent, name ) +{ + m_pOptionsPanel = pOptionsPanel; + m_pColorComboBox = new ComboBox(m_pOptionsPanel, "CrosshairColorComboBox", 6, false); + m_pAlphaCheckbox = new CCvarToggleCheckButton(m_pOptionsPanel, "CrosshairTranslucencyCheckbox", "#GameUI_Crosshair_Blend", "cl_crosshairusealpha"); + m_pDynamicCheckbox = new CCvarToggleCheckButton(m_pOptionsPanel, "CrosshairDynamicCheckbox", "#GameUI_CrosshairDynamic", "cl_dynamiccrosshair"); + m_pDotCheckbox = new CCvarToggleCheckButton(m_pOptionsPanel, "CrosshairDotCheckbox", "#GameUI_CrosshairDot", "cl_crosshairdot"); + m_pColorAlphaSlider = new CCvarSlider( m_pOptionsPanel, "Alpha Slider", "#GameUI_CrosshairColor_Alpha", + 0.0f, 255.0f, "cl_crosshairalpha" ); + m_pColorRSlider = new CCvarSlider( m_pOptionsPanel, "Red Color Slider", "#GameUI_CrosshairColor_Red", + 0.0f, 255.0f, "cl_crosshaircolor_r" ); + m_pColorGSlider = new CCvarSlider( m_pOptionsPanel, "Green Color Slider", "#GameUI_CrosshairColor_Green", + 0.0f, 255.0f, "cl_crosshaircolor_g" ); + m_pColorBSlider = new CCvarSlider( m_pOptionsPanel, "Blue Color Slider", "#GameUI_CrosshairColor_Blue", + 0.0f, 255.0f, "cl_crosshaircolor_b" ); + m_pSizeSlider = new CCvarSlider( m_pOptionsPanel, "Size Slider", "#GameUI_Crosshair_Size", + 0.0f, 12.0f, "cl_crosshairsize" ); + m_pThicknessSlider = new CCvarSlider( m_pOptionsPanel, "Thickness Slider", "#GameUI_Crosshair_Thickness", + 0.0f, 3.0f, "cl_crosshairthickness" ); + + m_pColorAlphaSlider->SetTickCaptions("", ""); + m_pColorRSlider->SetTickCaptions("", ""); + m_pColorGSlider->SetTickCaptions("", ""); + m_pColorBSlider->SetTickCaptions("", ""); + m_pSizeSlider->SetTickCaptions("", ""); + m_pThicknessSlider->SetTickCaptions("", ""); + + m_pAlphaCheckbox->AddActionSignalTarget(this); + m_pDynamicCheckbox->AddActionSignalTarget(this); + m_pDotCheckbox->AddActionSignalTarget(this); + m_pColorComboBox->AddActionSignalTarget( this ); + m_pColorAlphaSlider->AddActionSignalTarget( this ); + m_pColorRSlider->AddActionSignalTarget( this ); + m_pColorGSlider->AddActionSignalTarget( this ); + m_pColorBSlider->AddActionSignalTarget( this ); + m_pSizeSlider->AddActionSignalTarget( this ); + m_pThicknessSlider->AddActionSignalTarget( this ); + + InitCrosshairColorEntries(); + + m_iCrosshairTextureID = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( m_iCrosshairTextureID, "vgui/white_additive" , true, false); + + ResetData(); +} + +void CrosshairImagePanelCS::InitCrosshairColorEntries() +{ + if (m_pColorComboBox != NULL) + { + KeyValues *data = new KeyValues("data"); + + // add in the "Default" selection + data->Clear(); + + // add in the colors for the color list + for ( int i = 0; i < NumCrosshairColors; i++ ) + { + data->SetInt("color", i); + m_pColorComboBox->AddItem( s_crosshairColors[ i ].name, data); + } + + data->SetInt("color", NumCrosshairColors); + m_pColorComboBox->AddItem( "Custom", data); + + data->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +void CrosshairImagePanelCS::DrawCrosshairRect( int x0, int y0, int x1, int y1, bool bAdditive ) +{ + if ( bAdditive ) + vgui::surface()->DrawTexturedRect( x0, y0, x1, y1 ); + else + vgui::surface()->DrawFilledRect( x0, y0, x1, y1 ); +} + +//----------------------------------------------------------------------------- +void CrosshairImagePanelCS::Paint() +{ + int screenWide, screenTall; + surface()->GetScreenSize( screenWide, screenTall );; + + BaseClass::Paint(); + + int wide, tall; + GetSize( wide, tall ); + + bool bAdditive = !m_pAlphaCheckbox->IsSelected(); + bool bDynamic = m_pDynamicCheckbox->IsSelected(); + + int a = 255; + if ( !bAdditive ) + a = m_pColorAlphaSlider->GetSliderValue(); + + vgui::surface()->DrawSetColor( m_R, m_G, m_B, a ); + + if ( bAdditive ) + { + vgui::surface()->DrawSetTexture( m_iCrosshairTextureID ); + } + + int centerX = wide / 2; + int centerY = tall / 2; + + int iBarSize = RoundFloatToInt(m_barSize * screenTall / 480.0f); + int iBarThickness = max(1, RoundFloatToInt(m_barThickness * (float)screenTall / 480.0f)); + + float fBarGap = 4.0f; + if ( bDynamic ) + { + float curtime = system()->GetFrameTime(); + fBarGap *= (1.0f + cosf(curtime * 1.5f) * 0.5f); + } + + int iBarGap = RoundFloatToInt(fBarGap * screenTall / 480.0f); + + // draw horizontal crosshair lines + int iInnerLeft = centerX - iBarGap - iBarThickness / 2; + int iInnerRight = iInnerLeft + 2 * iBarGap + iBarThickness; + int iOuterLeft = iInnerLeft - iBarSize; + int iOuterRight = iInnerRight + iBarSize; + int y0 = centerY - iBarThickness / 2; + int y1 = y0 + iBarThickness; + DrawCrosshairRect( iOuterLeft, y0, iInnerLeft, y1, bAdditive ); + DrawCrosshairRect( iInnerRight, y0, iOuterRight, y1, bAdditive ); + + // draw vertical crosshair lines + int iInnerTop = centerY - iBarGap - iBarThickness / 2; + int iInnerBottom = iInnerTop + 2 * iBarGap + iBarThickness; + int iOuterTop = iInnerTop - iBarSize; + int iOuterBottom = iInnerBottom + iBarSize; + int x0 = centerX - iBarThickness / 2; + int x1 = x0 + iBarThickness; + DrawCrosshairRect( x0, iOuterTop, x1, iInnerTop, bAdditive ); + DrawCrosshairRect( x0, iInnerBottom, x1, iOuterBottom, bAdditive ); + + // draw dot + if ( m_pDotCheckbox->IsSelected() ) + { + x0 = centerX - iBarThickness / 2; + x1 = x0 + iBarThickness; + y0 = centerY - iBarThickness / 2; + y1 = y0 + iBarThickness; + DrawCrosshairRect( x0, y0, x1, y1, bAdditive ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: takes the settings from the crosshair settings combo boxes and sliders +// and apply it to the crosshair illustrations. +//----------------------------------------------------------------------------- +void CrosshairImagePanelCS::UpdateCrosshair() +{ + // get the color selected in the combo box. + KeyValues *data = m_pColorComboBox->GetActiveItemUserData(); + int colorIndex = data->GetInt("color"); + colorIndex = clamp( colorIndex, 0, NumCrosshairColors + 1 ); + + int actualVal = 0; + int selectedColor = m_pColorComboBox->GetActiveItem(); + + ConVarRef cl_crosshaircolor( "cl_crosshaircolor", true ); + if ( cl_crosshaircolor.IsValid() ) + { + actualVal = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors + 1 ); + } + + if ( selectedColor != NumCrosshairColors ) // not custom + { + m_R = s_crosshairColors[selectedColor].r; + m_G = s_crosshairColors[selectedColor].g; + m_B = s_crosshairColors[selectedColor].b; + m_pColorRSlider->SetSliderValue(m_R); + m_pColorGSlider->SetSliderValue(m_G); + m_pColorBSlider->SetSliderValue(m_B); + } + else + { + m_R = clamp( m_pColorRSlider->GetSliderValue(), 0, 255 ); + m_G = clamp( m_pColorGSlider->GetSliderValue(), 0, 255 ); + m_B = clamp( m_pColorBSlider->GetSliderValue(), 0, 255 ); + } + + m_barSize = m_pSizeSlider->GetSliderValue(); + m_barThickness = m_pThicknessSlider->GetSliderValue(); +} + + +void CrosshairImagePanelCS::OnSliderMoved(KeyValues *data) +{ + vgui::Panel* pPanel = static_cast<vgui::Panel*>(data->GetPtr("panel")); + + if ( pPanel == m_pColorRSlider || pPanel == m_pColorGSlider || pPanel == m_pColorBSlider ) + { + m_pColorComboBox->ActivateItem(NumCrosshairColors); + } + m_pOptionsPanel->OnControlModified(); + + UpdateCrosshair(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever color combo changes +//----------------------------------------------------------------------------- +void CrosshairImagePanelCS::OnTextChanged(vgui::Panel *panel) +{ + m_pOptionsPanel->OnControlModified(); + UpdateCrosshair(); +} + +void CrosshairImagePanelCS::OnCheckButtonChecked() +{ + m_pColorAlphaSlider->SetEnabled(m_pAlphaCheckbox->IsSelected()); + m_pOptionsPanel->OnControlModified(); + UpdateCrosshair(); +} + +void CrosshairImagePanelCS::ResetData() +{ + // parse the string for the custom color settings and get the initial settings. + ConVarRef cl_crosshaircolor( "cl_crosshaircolor", true ); + int index = 0; + if ( cl_crosshaircolor.IsValid() ) + { + index = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors + 1); + } + m_pColorComboBox->ActivateItemByRow(index); + + m_pAlphaCheckbox->Reset(); + m_pDynamicCheckbox->Reset(); + m_pDotCheckbox->Reset(); + m_pColorRSlider->Reset(); + m_pColorGSlider->Reset(); + m_pColorBSlider->Reset(); + m_pColorAlphaSlider->Reset(); + m_pSizeSlider->Reset(); + m_pThicknessSlider->Reset(); + + UpdateCrosshair(); +} + +void CrosshairImagePanelCS::ApplyChanges() +{ + m_pAlphaCheckbox->ApplyChanges(); + m_pDynamicCheckbox->ApplyChanges(); + m_pDotCheckbox->ApplyChanges(); + m_pColorRSlider->ApplyChanges(); + m_pColorGSlider->ApplyChanges(); + m_pColorBSlider->ApplyChanges(); + m_pColorAlphaSlider->ApplyChanges(); + m_pSizeSlider->ApplyChanges(); + m_pThicknessSlider->ApplyChanges(); + + char cmd[256]; + cmd[0] = 0; + + if (m_pColorComboBox != NULL) + { + int val = m_pColorComboBox->GetActiveItem(); + Q_snprintf( cmd, sizeof(cmd), "cl_crosshaircolor %d\n", val ); + engine->ClientCmd_Unrestricted( cmd ); + } +} + +//----------------------------------------------------------------------------- +class CrosshairImagePanelAdvanced : public CrosshairImagePanelBase +{ + DECLARE_CLASS_SIMPLE( CrosshairImagePanelAdvanced, CrosshairImagePanelBase ); +public: + CrosshairImagePanelAdvanced( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ); + virtual ~CrosshairImagePanelAdvanced(); + + virtual void ResetData(); + virtual void ApplyChanges(); + virtual void UpdateVisibility(); + +protected: + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", data ); + + virtual void Paint(); + static void DrawCrosshairRect( int x, int y, int w, int h, bool bAdditive ); + void InitAdvCrosshairStyleList(); + void SetCrosshairTexture( const char *crosshairname ); + void UpdateCrosshair(); + +private: + COptionsSubMultiplayer* m_pOptionsPanel; + + // --- advanced crosshair controls + CCvarSlider *m_pAdvCrosshairRedSlider; + CCvarSlider *m_pAdvCrosshairBlueSlider; + CCvarSlider *m_pAdvCrosshairGreenSlider; + CCvarSlider *m_pAdvCrosshairScaleSlider; + + CLabeledCommandComboBox *m_pAdvCrosshairStyle; + + int m_R, m_G, m_B; + float m_flScale; + + // material + int m_iCrosshairTextureID; + IVguiMatInfo *m_pAdvCrosshairMaterial; + + // animation + IVguiMatInfoVar *m_pFrameVar; + float m_flNextFrameChange; + int m_nNumFrames; + bool m_bAscending; // animating forward or in reverse? +}; + +//----------------------------------------------------------------------------- +CrosshairImagePanelAdvanced::CrosshairImagePanelAdvanced( Panel *parent, const char *name, COptionsSubMultiplayer* pOptionsPanel ) : CrosshairImagePanelBase( parent, name ) +{ + m_pOptionsPanel = pOptionsPanel; + m_pAdvCrosshairMaterial = NULL; + m_pFrameVar = NULL; + + m_pAdvCrosshairRedSlider = new CCvarSlider( pOptionsPanel, "Red Color Slider", "#GameUI_CrosshairColor_Red", + 0.0f, 255.0f, "cl_crosshair_red" ); + m_pAdvCrosshairGreenSlider = new CCvarSlider( pOptionsPanel, "Green Color Slider", "#GameUI_CrosshairColor_Green", + 0.0f, 255.0f, "cl_crosshair_green" ); + m_pAdvCrosshairBlueSlider = new CCvarSlider( pOptionsPanel, "Blue Color Slider", "#GameUI_CrosshairColor_Blue", + 0.0f, 255.0f, "cl_crosshair_blue" ); + + m_pAdvCrosshairRedSlider->SetTickCaptions("", ""); + m_pAdvCrosshairGreenSlider->SetTickCaptions("", ""); + m_pAdvCrosshairBlueSlider->SetTickCaptions("", ""); + + m_pAdvCrosshairScaleSlider = new CCvarSlider( pOptionsPanel, "Scale Slider", "#GameUI_CrosshairScale", + 16.0f, 48.0f, "cl_crosshair_scale" ); + + m_pAdvCrosshairStyle = new CLabeledCommandComboBox( pOptionsPanel, "AdvCrosshairList" ); + + m_pAdvCrosshairRedSlider->AddActionSignalTarget( this ); + m_pAdvCrosshairGreenSlider->AddActionSignalTarget( this ); + m_pAdvCrosshairBlueSlider->AddActionSignalTarget( this ); + m_pAdvCrosshairScaleSlider->AddActionSignalTarget( this ); + m_pAdvCrosshairStyle->AddActionSignalTarget(this); + + InitAdvCrosshairStyleList(); + + m_iCrosshairTextureID = vgui::surface()->CreateNewTextureID(); + SetCrosshairTexture("vgui/crosshairs/crosshair1"); + + UpdateCrosshair(); +} + +CrosshairImagePanelAdvanced::~CrosshairImagePanelAdvanced() +{ + if ( m_pFrameVar ) + { + delete m_pFrameVar; + m_pFrameVar = NULL; + } + + if ( m_pAdvCrosshairMaterial ) + { + delete m_pAdvCrosshairMaterial; + m_pAdvCrosshairMaterial = NULL; + } +} + +//----------------------------------------------------------------------------- +void CrosshairImagePanelAdvanced::SetCrosshairTexture( const char *crosshairname ) +{ + if ( !crosshairname || !crosshairname[0] ) + { + SetVisible( false ); + return; + } + + SetVisible( true ); + + vgui::surface()->DrawSetTextureFile( m_iCrosshairTextureID, crosshairname, true, false ); + + if ( m_pAdvCrosshairMaterial ) + { + delete m_pAdvCrosshairMaterial; + } + + m_pAdvCrosshairMaterial = vgui::surface()->DrawGetTextureMatInfoFactory( m_iCrosshairTextureID ); + + Assert(m_pAdvCrosshairMaterial); + + m_pFrameVar = m_pAdvCrosshairMaterial->FindVarFactory( "$frame", NULL ); + m_nNumFrames = m_pAdvCrosshairMaterial->GetNumAnimationFrames(); + + m_flNextFrameChange = system()->GetFrameTime() + 0.2; + m_bAscending = true; +} + +//----------------------------------------------------------------------------- +void CrosshairImagePanelAdvanced::Paint() +{ + BaseClass::Paint(); + + int wide, tall; + GetSize( wide, tall ); + + int iClipX0, iClipY0, iClipX1, iClipY1; + ipanel()->GetClipRect(GetVPanel(), iClipX0, iClipY0, iClipX1, iClipY1 ); + + // scroll through all frames + if ( m_pFrameVar ) + { + float curtime = system()->GetFrameTime(); + + if ( curtime >= m_flNextFrameChange ) + { + m_flNextFrameChange = curtime + 0.2; + + int frame = m_pFrameVar->GetIntValue(); + + if ( m_bAscending ) + { + frame++; + if ( frame >= m_nNumFrames ) + { + m_bAscending = !m_bAscending; + frame--; + } + } + else + { + frame--; + if ( frame < 0 ) + { + m_bAscending = !m_bAscending; + frame++; + } + } + + m_pFrameVar->SetIntValue(frame); + } + } + + float x, y; + + // assume square + float flDrawWidth = ( m_flScale/48.0 ) * (float)wide; + int flHalfWidth = (int)( flDrawWidth / 2 ); + + x = wide/2 - flHalfWidth; + y = tall/2 - flHalfWidth; + + vgui::surface()->DrawSetColor( m_R, m_G, m_B, 255 ); + vgui::surface()->DrawSetTexture( m_iCrosshairTextureID ); + vgui::surface()->DrawTexturedRect( x, y, x+flDrawWidth, y+flDrawWidth ); + vgui::surface()->DrawSetTexture(0); +} + +void CrosshairImagePanelAdvanced::UpdateCrosshair() +{ + // get the color selected in the combo box. + m_R = clamp( m_pAdvCrosshairRedSlider->GetSliderValue(), 0, 255 ); + m_G = clamp( m_pAdvCrosshairGreenSlider->GetSliderValue(), 0, 255 ); + m_B = clamp( m_pAdvCrosshairBlueSlider->GetSliderValue(), 0, 255 ); + + m_flScale = m_pAdvCrosshairScaleSlider->GetSliderValue(); + + if ( m_pAdvCrosshairStyle ) + { + char crosshairname[256]; + m_pAdvCrosshairStyle->GetText( crosshairname, sizeof(crosshairname) ); + + if ( ModInfo().AdvCrosshairLevel() == 1 && m_pAdvCrosshairStyle->GetActiveItem() == 0 ) // this is the "none" selection + { + SetCrosshairTexture(NULL); + } + else + { + char texture[ 256 ]; + Q_snprintf ( texture, sizeof( texture ), "vgui/crosshairs/%s", crosshairname ); + SetCrosshairTexture( texture ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: initialize the crosshair style list +//----------------------------------------------------------------------------- +void CrosshairImagePanelAdvanced::InitAdvCrosshairStyleList() +{ + // Find out images + FileFindHandle_t fh; + char directory[ 512 ]; + + ConVarRef cl_crosshair_file( "cl_crosshair_file", true ); + if ( !cl_crosshair_file.IsValid() ) + return; + + m_pAdvCrosshairStyle->DeleteAllItems(); + + if ( ModInfo().AdvCrosshairLevel() == 1 ) + { + m_pAdvCrosshairStyle->AddItem( "#GameUI_None", "" ); + } + + char crosshairfile[256]; + Q_snprintf( crosshairfile, sizeof(crosshairfile), "materials/vgui/crosshairs/%s.vtf", cl_crosshair_file.GetString() ); + + Q_snprintf( directory, sizeof( directory ), "materials/vgui/crosshairs/*.vtf" ); + const char *fn = g_pFullFileSystem->FindFirst( directory, &fh ); + int i = 0, initialItem = 0; + while (fn) + { + char filename[ 512 ]; + Q_snprintf( filename, sizeof(filename), "materials/vgui/crosshairs/%s", fn ); + if ( strlen( filename ) >= 4 ) + { + filename[ strlen( filename ) - 4 ] = 0; + Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS ); + if ( g_pFullFileSystem->FileExists( filename ) ) + { + // strip off the extension + Q_strncpy( filename, fn, sizeof( filename ) ); + filename[ strlen( filename ) - 4 ] = 0; + m_pAdvCrosshairStyle->AddItem( filename, "" ); + + // check to see if this is the one we have set + if ( crosshairfile[0] ) + { + Q_snprintf( filename, sizeof(filename), "materials/vgui/crosshairs/%s", fn ); + if (!stricmp(filename, crosshairfile)) + { + if ( ModInfo().AdvCrosshairLevel() == 1 ) + { + initialItem = i+1; + } + else + { + initialItem = i; + } + } + } + + ++i; + } + } + + fn = g_pFullFileSystem->FindNext( fh ); + } + + g_pFullFileSystem->FindClose( fh ); + m_pAdvCrosshairStyle->SetInitialItem(initialItem); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever style combo changes +//----------------------------------------------------------------------------- +void CrosshairImagePanelAdvanced::OnTextChanged(vgui::Panel *panel) +{ + m_pOptionsPanel->OnControlModified(); + UpdateCrosshair(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called whenever one of the color or scale sliders move +//----------------------------------------------------------------------------- +void CrosshairImagePanelAdvanced::OnSliderMoved(KeyValues *data) +{ + m_pOptionsPanel->OnControlModified(); + UpdateCrosshair(); +} + +void CrosshairImagePanelAdvanced::ResetData() +{ + m_pAdvCrosshairRedSlider->Reset(); + m_pAdvCrosshairGreenSlider->Reset(); + m_pAdvCrosshairBlueSlider->Reset(); + m_pAdvCrosshairScaleSlider->Reset(); + + // TODO: update style combo from cvar +} + +void CrosshairImagePanelAdvanced::ApplyChanges() +{ + m_pAdvCrosshairRedSlider->ApplyChanges(); + m_pAdvCrosshairGreenSlider->ApplyChanges(); + m_pAdvCrosshairBlueSlider->ApplyChanges(); + m_pAdvCrosshairScaleSlider->ApplyChanges(); + + // save the crosshair + char cmd[512]; + char crosshair[256]; + m_pAdvCrosshairStyle->GetText(crosshair, sizeof(crosshair)); + + if ( ModInfo().AdvCrosshairLevel() == 1 && m_pAdvCrosshairStyle->GetActiveItem() == 0 ) // this is the "none" selection + { + engine->ClientCmd_Unrestricted("cl_crosshair_file \"\""); + } + else + { + Q_snprintf(cmd, sizeof(cmd), "cl_crosshair_file %s\n", crosshair); + engine->ClientCmd_Unrestricted(cmd); + } +} + +void CrosshairImagePanelAdvanced::UpdateVisibility() +{ + SetVisible(true); + m_pAdvCrosshairRedSlider->SetVisible(true); + m_pAdvCrosshairBlueSlider->SetVisible(true); + m_pAdvCrosshairGreenSlider->SetVisible(true); + m_pAdvCrosshairScaleSlider->SetVisible(true); + m_pAdvCrosshairStyle->SetVisible(true); + + if ( ModInfo().NoCrosshair() ) + { + Panel *pTempPanel = NULL; + + pTempPanel = FindSiblingByName( "CrosshairImage" ); + pTempPanel->SetVisible( false ); + + pTempPanel = FindSiblingByName( "CrosshairLabel" ); + if ( pTempPanel ) + pTempPanel->SetVisible( false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Basic help dialog +//----------------------------------------------------------------------------- +COptionsSubMultiplayer::COptionsSubMultiplayer(vgui::Panel *parent) : vgui::PropertyPage(parent, "OptionsSubMultiplayer") +{ + Button *cancel = new Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + Button *ok = new Button( this, "OK", "#GameUI_OK" ); + ok->SetCommand( "Ok" ); + + Button *apply = new Button( this, "Apply", "#GameUI_Apply" ); + apply->SetCommand( "Apply" ); + + Button *advanced = new Button( this, "Advanced", "#GameUI_AdvancedEllipsis" ); + advanced->SetCommand( "Advanced" ); + + Button *importSprayImage = new Button( this, "ImportSprayImage", "#GameUI_ImportSprayEllipsis" ); + importSprayImage->SetCommand("ImportSprayImage"); + + m_hImportSprayDialog = NULL; + + m_pPrimaryColorSlider = new CCvarSlider( this, "Primary Color Slider", "#GameUI_PrimaryColor", + 0.0f, 255.0f, "topcolor" ); + + m_pSecondaryColorSlider = new CCvarSlider( this, "Secondary Color Slider", "#GameUI_SecondaryColor", + 0.0f, 255.0f, "bottomcolor" ); + + m_pHighQualityModelCheckBox = new CCvarToggleCheckButton( this, "High Quality Models", "#GameUI_HighModels", "cl_himodels" ); + + m_pModelList = new CLabeledCommandComboBox( this, "Player model" ); + m_ModelName[0] = 0; + InitModelList( m_pModelList ); + + m_pLogoList = new CLabeledCommandComboBox( this, "SpraypaintList" ); + m_LogoName[0] = 0; + InitLogoList( m_pLogoList ); + + m_pModelImage = new CBitmapImagePanel( this, "ModelImage", NULL ); + m_pModelImage->AddActionSignalTarget( this ); + + m_pLogoImage = new ImagePanel( this, "LogoImage" ); + m_pLogoImage->AddActionSignalTarget( this ); + + m_nLogoR = 255; + m_nLogoG = 255; + m_nLogoB = 255; + + m_pCrosshairImage = NULL; + switch ( ModInfo().AdvCrosshairLevel() ) + { + case 0: + if ( !ModInfo().NoCrosshair() ) + m_pCrosshairImage = new CrosshairImagePanelSimple( this, "CrosshairImage", this ); + break; + + case 1: // TF + case 2: // DOD + m_pCrosshairImage = new CrosshairImagePanelAdvanced( this, "AdvCrosshairImage", this ); + break; + + case 3: // Counter-Strike + m_pCrosshairImage = new CrosshairImagePanelCS( this, "CrosshairImage", this ); + break; + + } + + m_pLockRadarRotationCheckbox = new CCvarToggleCheckButton( this, "LockRadarRotationCheckbox", "#Cstrike_RadarLocked", "cl_radar_locked" ); + + m_pDownloadFilterCombo = new ComboBox( this, "DownloadFilterCheck", 4, false ); + m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_ALL", NULL ); + m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_NoSounds", NULL ); + m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_MapsOnly", NULL ); + m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_None", NULL ); + + //========= + + LoadControlSettings("Resource/OptionsSubMultiplayer.res"); + + // this is necessary because some of the game .res files don't have visiblity flags set up correctly for their controls + if ( m_pCrosshairImage ) + m_pCrosshairImage->UpdateVisibility(); + + // turn off model selection stuff if the mod specifies "nomodels" in the gameinfo.txt file + if ( ModInfo().NoModels() ) + { + Panel *pTempPanel = NULL; + + if ( m_pModelImage ) + { + m_pModelImage->SetVisible( false ); + } + + if ( m_pModelList ) + { + m_pModelList->SetVisible( false ); + } + + if ( m_pPrimaryColorSlider ) + { + m_pPrimaryColorSlider->SetVisible( false ); + } + + if ( m_pSecondaryColorSlider ) + { + m_pSecondaryColorSlider->SetVisible( false ); + } + + // #GameUI_PlayerModel (from "Resource/OptionsSubMultiplayer.res") + pTempPanel = FindChildByName( "Label1" ); + + if ( pTempPanel ) + { + pTempPanel->SetVisible( false ); + } + + // #GameUI_ColorSliders (from "Resource/OptionsSubMultiplayer.res") + pTempPanel = FindChildByName( "Colors" ); + + if ( pTempPanel ) + { + pTempPanel->SetVisible( false ); + } + } + + // turn off the himodel stuff if the mod specifies "nohimodel" in the gameinfo.txt file + if ( ModInfo().NoHiModel() ) + { + if ( m_pHighQualityModelCheckBox ) + { + m_pHighQualityModelCheckBox->SetVisible( false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubMultiplayer::~COptionsSubMultiplayer() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::OnCommand( const char *command ) +{ + if ( !stricmp( command, "Advanced" ) ) + { +#ifndef _XBOX + if (!m_hMultiplayerAdvancedDialog.Get()) + { + m_hMultiplayerAdvancedDialog = new CMultiplayerAdvancedDialog( this ); + } + m_hMultiplayerAdvancedDialog->Activate(); +#endif + } + else if (!stricmp( command, "ImportSprayImage" ) ) + { + if (m_hImportSprayDialog == NULL) + { + m_hImportSprayDialog = new FileOpenDialog(NULL, "#GameUI_ImportSprayImage", true); +#ifdef WIN32 + m_hImportSprayDialog->AddFilter("*.tga,*.jpg,*.bmp,*.vtf", "#GameUI_All_Images", true); +#else + m_hImportSprayDialog->AddFilter("*.tga,*.jpg,*.vtf", "#GameUI_All_ImagesNoBmp", true); +#endif + m_hImportSprayDialog->AddFilter("*.tga", "#GameUI_TGA_Images", false); + m_hImportSprayDialog->AddFilter("*.jpg", "#GameUI_JPEG_Images", false); +#ifdef WIN32 + m_hImportSprayDialog->AddFilter("*.bmp", "#GameUI_BMP_Images", false); +#endif + m_hImportSprayDialog->AddFilter("*.vtf", "#GameUI_VTF_Images", false); + m_hImportSprayDialog->AddActionSignalTarget(this); + } + m_hImportSprayDialog->DoModal(false); + m_hImportSprayDialog->Activate(); + } + + else if ( !stricmp( command, "ResetStats" ) ) + { + QueryBox *box = new QueryBox("#GameUI_ConfirmResetStatsTitle", "#GameUI_ConfirmResetStatsText", this); + box->SetOKButtonText("#GameUI_Reset"); + box->SetOKCommand(new KeyValues("Command", "command", "ResetStats_NoConfirm")); + box->SetCancelCommand(new KeyValues("Command", "command", "ReleaseModalWindow")); + box->AddActionSignalTarget(this); + box->DoModal(); + } + + else if ( !stricmp( command, "ResetStats_NoConfirm" ) ) + { + engine->ClientCmd_Unrestricted("stats_reset"); + } + + BaseClass::OnCommand( command ); +} + +void COptionsSubMultiplayer::ConversionError( ConversionErrorType nError ) +{ + const char *pErrorText = NULL; + + switch ( nError ) + { + case CE_MEMORY_ERROR: + pErrorText = "#GameUI_Spray_Import_Error_Memory"; + break; + + case CE_CANT_OPEN_SOURCE_FILE: + pErrorText = "#GameUI_Spray_Import_Error_Reading_Image"; + break; + + case CE_ERROR_PARSING_SOURCE: + pErrorText = "#GameUI_Spray_Import_Error_Image_File_Corrupt"; + break; + + case CE_SOURCE_FILE_SIZE_NOT_SUPPORTED: + pErrorText = "#GameUI_Spray_Import_Image_Wrong_Size"; + break; + + case CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED: + pErrorText = "#GameUI_Spray_Import_Image_Wrong_Size"; + break; + + case CE_SOURCE_FILE_TGA_FORMAT_NOT_SUPPORTED: + pErrorText = "#GameUI_Spray_Import_Error_TGA_Format_Not_Supported"; + break; + + case CE_SOURCE_FILE_BMP_FORMAT_NOT_SUPPORTED: + pErrorText = "#GameUI_Spray_Import_Error_BMP_Format_Not_Supported"; + break; + + case CE_ERROR_WRITING_OUTPUT_FILE: + pErrorText = "#GameUI_Spray_Import_Error_Writing_Temp_Output"; + break; + + case CE_ERROR_LOADING_DLL: + pErrorText = "#GameUI_Spray_Import_Error_Cant_Load_VTEX_DLL"; + break; + } + + if ( pErrorText ) + { + // Create the dialog + vgui::MessageBox *pErrorDlg = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", pErrorText ); Assert( pErrorDlg ); + + // Display + if ( pErrorDlg ) // Check for a NULL just to be extra cautious... + { + pErrorDlg->DoModal(); + } + } +} + +void COptionsSubMultiplayer::OnFileSelected(const char *fullpath) +{ +#ifndef _XBOX + // this can take a while, put up a waiting cursor + surface()->SetCursor(dc_hourglass); + + ConversionErrorType nErrorCode = ImgUtl_ConvertToVTFAndDumpVMT( fullpath, IsPosix() ? "/vgui/logos" : "\\vgui\\logos", 256, 256 ); + if ( nErrorCode == CE_SUCCESS ) + { + // refresh the logo list so the new spray shows up. + InitLogoList(m_pLogoList); + + // Get the filename + char szRootFilename[MAX_PATH]; + V_FileBase( fullpath, szRootFilename, sizeof( szRootFilename ) ); + + // automatically select the logo that was just imported. + SelectLogo(szRootFilename); + } + else + { + ConversionError( nErrorCode ); + } + + // change the cursor back to normal + surface()->SetCursor(dc_user); +#endif +} + +struct ValveJpegErrorHandler_t +{ + // The default manager + struct jpeg_error_mgr m_Base; + // For handling any errors + jmp_buf m_ErrorContext; +}; + +//----------------------------------------------------------------------------- +// Purpose: We'll override the default error handler so we can deal with errors without having to exit the engine +//----------------------------------------------------------------------------- +static void ValveJpegErrorHandler( j_common_ptr cinfo ) +{ + ValveJpegErrorHandler_t *pError = reinterpret_cast< ValveJpegErrorHandler_t * >( cinfo->err ); + + char buffer[ JMSG_LENGTH_MAX ]; + + /* Create the message */ + ( *cinfo->err->format_message )( cinfo, buffer ); + + Warning( "%s\n", buffer ); + + // Bail + longjmp( pError->m_ErrorContext, 1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Builds the list of logos +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::InitLogoList( CLabeledCommandComboBox *cb ) +{ + // Find out images + FileFindHandle_t fh; + char directory[ 512 ]; + + ConVarRef cl_logofile( "cl_logofile", true ); + if ( !cl_logofile.IsValid() ) + return; + + cb->DeleteAllItems(); + + const char *logofile = cl_logofile.GetString(); + Q_snprintf( directory, sizeof( directory ), "materials/vgui/logos/*.vtf" ); + const char *fn = g_pFullFileSystem->FindFirst( directory, &fh ); + int i = 0, initialItem = 0; + while (fn) + { + char filename[ 512 ]; + Q_snprintf( filename, sizeof(filename), "materials/vgui/logos/%s", fn ); + if ( strlen( filename ) >= 4 ) + { + filename[ strlen( filename ) - 4 ] = 0; + Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS ); + if ( g_pFullFileSystem->FileExists( filename ) ) + { + // strip off the extension + Q_strncpy( filename, fn, sizeof( filename ) ); + filename[ strlen( filename ) - 4 ] = 0; + cb->AddItem( filename, "" ); + + // check to see if this is the one we have set + Q_snprintf( filename, sizeof(filename), "materials/vgui/logos/%s", fn ); + if (!Q_stricmp(filename, logofile)) + { + initialItem = i; + } + + ++i; + } + } + + fn = g_pFullFileSystem->FindNext( fh ); + } + + g_pFullFileSystem->FindClose( fh ); + cb->SetInitialItem(initialItem); +} + + +//----------------------------------------------------------------------------- +// Purpose: Selects the given logo in the logo list. +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::SelectLogo(const char *logoName) +{ + int numEntries = m_pLogoList->GetItemCount(); + int index; + wchar_t itemText[MAX_PATH]; + wchar_t itemToSelectText[MAX_PATH]; + + // convert the logo filename to unicode + g_pVGuiLocalize->ConvertANSIToUnicode(logoName, itemToSelectText, sizeof(itemToSelectText)); + + // find the index of the spray we want. + for (index = 0; index < numEntries; ++index) + { + m_pLogoList->GetItemText(index, itemText, sizeof(itemText)); + if (!wcscmp(itemText, itemToSelectText)) + { + break; + } + } + + if (index < numEntries) + { + // select the logo. + m_pLogoList->ActivateItem(index); + } +} + +#define MODEL_MATERIAL_BASE_FOLDER "materials/vgui/playermodels/" + + +void StripStringOutOfString( const char *pPattern, const char *pIn, char *pOut ) +{ + int iLengthBase = strlen( pPattern ); + int iLengthString = strlen( pIn ); + + int k = 0; + + for ( int j = iLengthBase; j < iLengthString; j++ ) + { + pOut[k] = pIn[j]; + k++; + } + + pOut[k] = 0; +} + +void FindVMTFilesInFolder( const char *pFolder, const char *pFolderName, CLabeledCommandComboBox *cb, int &iCount, int &iInitialItem ) +{ + ConVarRef cl_modelfile( "cl_playermodel", true ); + if ( !cl_modelfile.IsValid() ) + return; + + char directory[ 512 ]; + Q_snprintf( directory, sizeof( directory ), "%s/*.*", pFolder ); + + FileFindHandle_t fh; + + const char *fn = g_pFullFileSystem->FindFirst( directory, &fh ); + const char *modelfile = cl_modelfile.GetString(); + + while ( fn ) + { + if ( !stricmp( fn, ".") || !stricmp( fn, "..") ) + { + fn = g_pFullFileSystem->FindNext( fh ); + continue; + } + + if ( g_pFullFileSystem->FindIsDirectory( fh ) ) + { + char folderpath[512]; + + Q_snprintf( folderpath, sizeof( folderpath ), "%s/%s", pFolder, fn ); + + FindVMTFilesInFolder( folderpath, fn, cb, iCount, iInitialItem ); + fn = g_pFullFileSystem->FindNext( fh ); + continue; + } + + if ( !strstr( fn, ".vmt" ) ) + { + fn = g_pFullFileSystem->FindNext( fh ); + continue; + } + + + char filename[ 512 ]; + Q_snprintf( filename, sizeof(filename), "%s/%s", pFolder, fn ); + if ( strlen( filename ) >= 4 ) + { + filename[ strlen( filename ) - 4 ] = 0; + Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS ); + if ( g_pFullFileSystem->FileExists( filename ) ) + { + char displayname[ 512 ]; + char texturepath[ 512 ]; + // strip off the extension + Q_strncpy( displayname, fn, sizeof( displayname ) ); + StripStringOutOfString( MODEL_MATERIAL_BASE_FOLDER, filename, texturepath ); + + displayname[ strlen( displayname ) - 4 ] = 0; + + if ( CORRECT_PATH_SEPARATOR == texturepath[0] ) + cb->AddItem( displayname, texturepath + 1 ); // ignore the initial "/" in texture path + else + cb->AddItem( displayname, texturepath ); + + + char realname[ 512 ]; + Q_FileBase( modelfile, realname, sizeof( realname ) ); + Q_FileBase( filename, filename, sizeof( filename ) ); + + if (!stricmp(filename, realname)) + { + iInitialItem = iCount; + } + + ++iCount; + } + } + + fn = g_pFullFileSystem->FindNext( fh ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Builds model list +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::InitModelList( CLabeledCommandComboBox *cb ) +{ + // Find out images + int i = 0, initialItem = 0; + + cb->DeleteAllItems(); + FindVMTFilesInFolder( MODEL_MATERIAL_BASE_FOLDER, "", cb, i, initialItem ); + cb->SetInitialItem( initialItem ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::RemapLogo() +{ + char logoname[256]; + + m_pLogoList->GetText( logoname, sizeof( logoname ) ); + if( !logoname[ 0 ] ) + return; + + char fullLogoName[512]; + + // make sure there is a version with the proper shader + g_pFullFileSystem->CreateDirHierarchy( "materials/VGUI/logos/UI", "GAME" ); + Q_snprintf( fullLogoName, sizeof( fullLogoName ), "materials/VGUI/logos/UI/%s.vmt", logoname ); + if ( !g_pFullFileSystem->FileExists( fullLogoName ) ) + { + FileHandle_t fp = g_pFullFileSystem->Open( fullLogoName, "wb" ); + if ( !fp ) + return; + + char data[1024]; + Q_snprintf( data, sizeof( data ), "\"UnlitGeneric\"\n\ +{\n\ + // Original shader: BaseTimesVertexColorAlphaBlendNoOverbright\n\ + \"$translucent\" 1\n\ + \"$basetexture\" \"VGUI/logos/%s\"\n\ + \"$vertexcolor\" 1\n\ + \"$vertexalpha\" 1\n\ + \"$no_fullbright\" 1\n\ + \"$ignorez\" 1\n\ +}\n\ +", logoname ); + + g_pFullFileSystem->Write( data, strlen( data ), fp ); + g_pFullFileSystem->Close( fp ); + } + + Q_snprintf( fullLogoName, sizeof( fullLogoName ), "logos/UI/%s", logoname ); + m_pLogoImage->SetImage( fullLogoName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::RemapModel() +{ + const char *pModelName = m_pModelList->GetActiveItemCommand(); + + if( pModelName == NULL ) + return; + + char texture[ 256 ]; + Q_snprintf ( texture, sizeof( texture ), "vgui/playermodels/%s", pModelName ); + texture[ strlen( texture ) - 4 ] = 0; + + m_pModelImage->setTexture( texture ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever model name changes +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::OnTextChanged(vgui::Panel *panel) +{ + RemapModel(); + RemapLogo(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::OnControlModified() +{ + PostMessage(GetParent(), new KeyValues("ApplyButtonEnable")); + InvalidateLayout(); +} + +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') +#define SUIT_HUE_START 192 +#define SUIT_HUE_END 223 +#define PLATE_HUE_START 160 +#define PLATE_HUE_END 191 + +#ifdef POSIX +typedef struct tagRGBQUAD { + uint8 rgbBlue; + uint8 rgbGreen; + uint8 rgbRed; + uint8 rgbReserved; +} RGBQUAD; +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void PaletteHueReplace( RGBQUAD *palSrc, int newHue, int Start, int end ) +{ + int i; + float r, b, g; + float maxcol, mincol; + float hue, val, sat; + + hue = (float)(newHue * (360.0 / 255)); + + for (i = Start; i <= end; i++) + { + b = palSrc[ i ].rgbBlue; + g = palSrc[ i ].rgbGreen; + r = palSrc[ i ].rgbRed; + + maxcol = max( max( r, g ), b ) / 255.0f; + mincol = min( min( r, g ), b ) / 255.0f; + + val = maxcol; + sat = (maxcol - mincol) / maxcol; + + mincol = val * (1.0f - sat); + + if (hue <= 120) + { + b = mincol; + if (hue < 60) + { + r = val; + g = mincol + hue * (val - mincol)/(120 - hue); + } + else + { + g = val; + r = mincol + (120 - hue)*(val-mincol)/hue; + } + } + else if (hue <= 240) + { + r = mincol; + if (hue < 180) + { + g = val; + b = mincol + (hue - 120)*(val-mincol)/(240 - hue); + } + else + { + b = val; + g = mincol + (240 - hue)*(val-mincol)/(hue - 120); + } + } + else + { + g = mincol; + if (hue < 300) + { + b = val; + r = mincol + (hue - 240)*(val-mincol)/(360 - hue); + } + else + { + r = val; + b = mincol + (360 - hue)*(val-mincol)/(hue - 240); + } + } + + palSrc[ i ].rgbBlue = (unsigned char)(b * 255); + palSrc[ i ].rgbGreen = (unsigned char)(g * 255); + palSrc[ i ].rgbRed = (unsigned char)(r * 255); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::ColorForName( char const *pszColorName, int&r, int&g, int&b ) +{ + r = g = b = 0; + + int count = sizeof( itemlist ) / sizeof( itemlist[0] ); + + for ( int i = 0; i < count; i++ ) + { + if (!Q_strnicmp(pszColorName, itemlist[ i ].name, strlen(itemlist[ i ].name))) + { + r = itemlist[ i ].r; + g = itemlist[ i ].g; + b = itemlist[ i ].b; + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::OnResetData() +{ + // reset the DownloadFilter combo box + if ( m_pDownloadFilterCombo ) + { + // cl_downloadfilter + ConVarRef cl_downloadfilter( "cl_downloadfilter"); + + if ( Q_stricmp( cl_downloadfilter.GetString(), "none" ) == 0 ) + { + m_pDownloadFilterCombo->ActivateItem( 3 ); + } + else if ( Q_stricmp( cl_downloadfilter.GetString(), "nosounds" ) == 0 ) + { + m_pDownloadFilterCombo->ActivateItem( 1 ); + } + else if ( Q_stricmp( cl_downloadfilter.GetString(), "mapsonly" ) == 0 ) + { + m_pDownloadFilterCombo->ActivateItem( 2 ); + } + else + { + m_pDownloadFilterCombo->ActivateItem( 0 ); + } + } + + if ( m_pCrosshairImage ) + m_pCrosshairImage->ResetData(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubMultiplayer::OnApplyChanges() +{ + m_pPrimaryColorSlider->ApplyChanges(); + m_pSecondaryColorSlider->ApplyChanges(); +// m_pModelList->ApplyChanges(); + m_pLogoList->ApplyChanges(); + m_pLogoList->GetText(m_LogoName, sizeof(m_LogoName)); + m_pHighQualityModelCheckBox->ApplyChanges(); + + for ( int i=0; i<m_cvarToggleCheckButtons.GetCount(); ++i ) + { + CCvarToggleCheckButton *toggleButton = m_cvarToggleCheckButtons[i]; + if( toggleButton->IsVisible() && toggleButton->IsEnabled() ) + { + toggleButton->ApplyChanges(); + } + } + + if ( m_pLockRadarRotationCheckbox != NULL ) + { + m_pLockRadarRotationCheckbox->ApplyChanges(); + } + + if ( m_pCrosshairImage != NULL ) + m_pCrosshairImage->ApplyChanges(); + + // save the logo name + char cmd[512]; + if ( m_LogoName[ 0 ] ) + { + Q_snprintf(cmd, sizeof(cmd), "cl_logofile materials/vgui/logos/%s.vtf\n", m_LogoName); + } + else + { + Q_strncpy( cmd, "cl_logofile \"\"\n", sizeof( cmd ) ); + } + engine->ClientCmd_Unrestricted(cmd); + + if ( m_pModelList && m_pModelList->IsVisible() && m_pModelList->GetActiveItemCommand() ) + { + Q_strncpy( m_ModelName, m_pModelList->GetActiveItemCommand(), sizeof( m_ModelName ) ); + Q_StripExtension( m_ModelName, m_ModelName, sizeof ( m_ModelName ) ); + + // save the player model name + Q_snprintf(cmd, sizeof(cmd), "cl_playermodel models/%s.mdl\n", m_ModelName ); + engine->ClientCmd_Unrestricted(cmd); + } + else + { + m_ModelName[0] = 0; + } + + // set the DownloadFilter cvar + if ( m_pDownloadFilterCombo ) + { + ConVarRef cl_downloadfilter( "cl_downloadfilter" ); + + switch ( m_pDownloadFilterCombo->GetActiveItem() ) + { + default: + case 0: + cl_downloadfilter.SetValue( "all" ); + break; + case 1: + cl_downloadfilter.SetValue( "nosounds" ); + break; + case 2: + cl_downloadfilter.SetValue( "mapsonly" ); + break; + case 3: + cl_downloadfilter.SetValue( "none" ); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Allow the res file to create controls on per-mod basis +//----------------------------------------------------------------------------- +Panel *COptionsSubMultiplayer::CreateControlByName( const char *controlName ) +{ + if( !Q_stricmp( "CCvarToggleCheckButton", controlName ) ) + { + CCvarToggleCheckButton *newButton = new CCvarToggleCheckButton( this, controlName, "", "" ); + m_cvarToggleCheckButtons.AddElement( newButton ); + return newButton; + } + else + { + return BaseClass::CreateControlByName( controlName ); + } +} + diff --git a/gameui/OptionsSubMultiplayer.h b/gameui/OptionsSubMultiplayer.h new file mode 100644 index 0000000..85577bb --- /dev/null +++ b/gameui/OptionsSubMultiplayer.h @@ -0,0 +1,113 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONSSUBMULTIPLAYER_H +#define OPTIONSSUBMULTIPLAYER_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> +#include <vgui_controls/ImagePanel.h> +#include "imageutils.h" + +class CLabeledCommandComboBox; +class CBitmapImagePanel; + +class CCvarToggleCheckButton; +class CCvarTextEntry; +class CCvarSlider; + +class CMultiplayerAdvancedDialog; + +class COptionsSubMultiplayer; + +class CrosshairImagePanelBase : public vgui::ImagePanel +{ + DECLARE_CLASS_SIMPLE( CrosshairImagePanelBase, vgui::ImagePanel ); +public: + CrosshairImagePanelBase( Panel *parent, const char *name ) : BaseClass(parent, name) {} + virtual void ResetData() {} + virtual void ApplyChanges() {} + virtual void UpdateVisibility() {} +}; + +//----------------------------------------------------------------------------- +// Purpose: multiplayer options property page +//----------------------------------------------------------------------------- +class COptionsSubMultiplayer : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubMultiplayer, vgui::PropertyPage ); + +public: + COptionsSubMultiplayer(vgui::Panel *parent); + ~COptionsSubMultiplayer(); + + virtual vgui::Panel *CreateControlByName(const char *controlName); + + MESSAGE_FUNC( OnControlModified, "ControlModified" ); + +protected: + // Called when page is loaded. Data should be reloaded from document into controls. + virtual void OnResetData(); + // Called when the OK / Apply button is pressed. Changed data should be written into document. + virtual void OnApplyChanges(); + + virtual void OnCommand( const char *command ); + +private: + void InitModelList(CLabeledCommandComboBox *cb); + void InitLogoList(CLabeledCommandComboBox *cb); + + void RemapModel(); + void RemapLogo(); + + void ConversionError( ConversionErrorType nError ); + + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath ); + + void ColorForName(char const *pszColorName, int &r, int &g, int &b); + + CBitmapImagePanel *m_pModelImage; + CLabeledCommandComboBox *m_pModelList; + char m_ModelName[128]; + + vgui::ImagePanel *m_pLogoImage; + CLabeledCommandComboBox *m_pLogoList; + char m_LogoName[128]; + + CCvarSlider *m_pPrimaryColorSlider; + CCvarSlider *m_pSecondaryColorSlider; + CCvarToggleCheckButton *m_pHighQualityModelCheckBox; + + // Mod specific general checkboxes + vgui::Dar< CCvarToggleCheckButton * > m_cvarToggleCheckButtons; + + CCvarToggleCheckButton *m_pLockRadarRotationCheckbox; + + CrosshairImagePanelBase *m_pCrosshairImage; + + // --- client download filter + vgui::ComboBox *m_pDownloadFilterCombo; + + // Begin Spray Import Functions + ConversionErrorType WriteSprayVMT(const char *vtfPath); + void SelectLogo(const char *logoName); + // End Spray Import Functions + + int m_nLogoR; + int m_nLogoG; + int m_nLogoB; + +#ifndef _XBOX + vgui::DHANDLE<CMultiplayerAdvancedDialog> m_hMultiplayerAdvancedDialog; +#endif + vgui::FileOpenDialog *m_hImportSprayDialog; +}; + +#endif // OPTIONSSUBMULTIPLAYER_H diff --git a/gameui/OptionsSubPortal.cpp b/gameui/OptionsSubPortal.cpp new file mode 100644 index 0000000..a6ad724 --- /dev/null +++ b/gameui/OptionsSubPortal.cpp @@ -0,0 +1,98 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "OptionsSubPortal.h" +#include "CvarToggleCheckButton.h" +#include "vgui_controls/ComboBox.h" + +#include "EngineInterface.h" + +#include <KeyValues.h> +#include <vgui/IScheme.h> +#include "tier1/convar.h" +#include <stdio.h> +#include <vgui_controls/TextEntry.h> +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +COptionsSubPortal::COptionsSubPortal(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + m_pPortalFunnelCheckBox = new CCvarToggleCheckButton( + this, + "PortalFunnel", + "#GameUI_PortalFunnel", + "sv_player_funnel_into_portals" ); + + m_pPortalDepthCombo = new ComboBox( this, "PortalDepth", 6, false ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth0", new KeyValues("PortalDepth", "depth", 0) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth1", new KeyValues("PortalDepth", "depth", 1) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth2", new KeyValues("PortalDepth", "depth", 2) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth3", new KeyValues("PortalDepth", "depth", 3) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth4", new KeyValues("PortalDepth", "depth", 4) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth5", new KeyValues("PortalDepth", "depth", 5) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth6", new KeyValues("PortalDepth", "depth", 6) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth7", new KeyValues("PortalDepth", "depth", 7) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth8", new KeyValues("PortalDepth", "depth", 8) ); + m_pPortalDepthCombo->AddItem( "#GameUI_PortalDepth9", new KeyValues("PortalDepth", "depth", 9) ); + + LoadControlSettings("Resource\\OptionsSubPortal.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubPortal::~COptionsSubPortal() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubPortal::OnResetData() +{ + m_pPortalFunnelCheckBox->Reset(); + + // Portal render depth + ConVarRef r_portal_stencil_depth("r_portal_stencil_depth"); + if ( r_portal_stencil_depth.IsValid() ) + { + m_pPortalDepthCombo->ActivateItem(r_portal_stencil_depth.GetInt()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubPortal::OnApplyChanges() +{ + m_pPortalFunnelCheckBox->ApplyChanges(); + + // Portal render depth + if ( m_pPortalDepthCombo->IsEnabled() ) + { + ConVarRef r_portal_stencil_depth( "r_portal_stencil_depth" ); + r_portal_stencil_depth.SetValue( m_pPortalDepthCombo->GetActiveItem() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets background color & border +//----------------------------------------------------------------------------- +void COptionsSubPortal::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubPortal::OnControlModified() +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} diff --git a/gameui/OptionsSubPortal.h b/gameui/OptionsSubPortal.h new file mode 100644 index 0000000..1d92280 --- /dev/null +++ b/gameui/OptionsSubPortal.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_PORTAL_H +#define OPTIONS_SUB_PORTAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> + +class CLabeledCommandComboBox; +class CCvarToggleCheckButton; + +namespace vgui +{ + class Label; + class Panel; +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubPortal : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubPortal, vgui::PropertyPage ); + +public: + COptionsSubPortal(vgui::Panel *parent); + ~COptionsSubPortal(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + +protected: + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + +private: + MESSAGE_FUNC( OnCheckButtonChecked, "CheckButtonChecked" ) + { + OnControlModified(); + } + MESSAGE_FUNC( OnControlModified, "ControlModified" ); + MESSAGE_FUNC( OnTextChanged, "TextChanged" ) + { + OnControlModified(); + } + + CCvarToggleCheckButton *m_pPortalFunnelCheckBox; + vgui::ComboBox *m_pPortalDepthCombo; +}; + + + +#endif // OPTIONS_SUB_MOUSE_H
\ No newline at end of file diff --git a/gameui/OptionsSubVideo.cpp b/gameui/OptionsSubVideo.cpp new file mode 100644 index 0000000..57db401 --- /dev/null +++ b/gameui/OptionsSubVideo.cpp @@ -0,0 +1,1763 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "OptionsSubVideo.h" +#include "cvarslider.h" +#include "EngineInterface.h" +#include "BasePanel.h" +#include "IGameUIFuncs.h" +#include "modes.h" +#include "materialsystem/materialsystem_config.h" +#include "filesystem.h" +#include "GameUI_Interface.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Frame.h" +#include "vgui_controls/QueryBox.h" +#include "CvarToggleCheckButton.h" +#include "tier1/KeyValues.h" +#include "vgui/IInput.h" +#include "vgui/ILocalize.h" +#include "vgui/ISystem.h" +#include "tier0/icommandline.h" +#include "tier1/convar.h" +#include "ModInfo.h" +#include "vgui_controls/Tooltip.h" +#include "sourcevr/isourcevirtualreality.h" + +#if defined( USE_SDL ) +#include "SDL.h" +#endif + +#include "inetchannelinfo.h" + +extern IMaterialSystem *materials; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: aspect ratio mappings (for normal/widescreen combo) +//----------------------------------------------------------------------------- +struct RatioToAspectMode_t +{ + int anamorphic; + float aspectRatio; +}; +RatioToAspectMode_t g_RatioToAspectModes[] = +{ + { 0, 4.0f / 3.0f }, + { 1, 16.0f / 9.0f }, + { 2, 16.0f / 10.0f }, + { 2, 1.0f }, +}; + +struct AAMode_t +{ + int m_nNumSamples; + int m_nQualityLevel; +}; + +//----------------------------------------------------------------------------- +// Purpose: list of valid dx levels +//----------------------------------------------------------------------------- +int g_DirectXLevels[] = +{ + 70, + 80, + 81, + 90, +#if DX_TO_GL_ABSTRACTION + 92, +#endif + 95, +}; + +//----------------------------------------------------------------------------- +// Purpose: returns the string name of a given dxlevel +//----------------------------------------------------------------------------- +void GetNameForDXLevel( int dxlevel, char *name, int bufferSize) +{ + if ( ( dxlevel >= 92 ) && ( dxlevel <= 95 ) ) + { + Q_snprintf( name, bufferSize, "DirectX v9.0+" ); + } + else + { + Q_snprintf( name, bufferSize, "DirectX v%.1f", dxlevel / 10.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the aspect ratio mode number for the given resolution +//----------------------------------------------------------------------------- +int GetScreenAspectMode( int width, int height ) +{ + float aspectRatio = (float)width / (float)height; + + // just find the closest ratio + float closestAspectRatioDist = 99999.0f; + int closestAnamorphic = 0; + for (int i = 0; i < ARRAYSIZE(g_RatioToAspectModes); i++) + { + float dist = fabs( g_RatioToAspectModes[i].aspectRatio - aspectRatio ); + if (dist < closestAspectRatioDist) + { + closestAspectRatioDist = dist; + closestAnamorphic = g_RatioToAspectModes[i].anamorphic; + } + } + + return closestAnamorphic; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the string name of the specified resolution mode +//----------------------------------------------------------------------------- +static void GetResolutionName( vmode_t *mode, char *sz, int sizeofsz, int desktopWidth, int desktopHeight ) +{ + Q_snprintf( sz, sizeofsz, "%i x %i%s", mode->width, mode->height, + ( mode->width == desktopWidth ) && ( mode->height == desktopHeight ) ? " (native)": "" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gamma-adjust dialog +//----------------------------------------------------------------------------- +class CGammaDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CGammaDialog, vgui::Frame ); +public: + CGammaDialog( vgui::VPANEL hParent ) : BaseClass( NULL, "OptionsSubVideoGammaDlg" ) + { + // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below) + SetTitle("#GameUI_AdjustGamma_Title", true); + SetSize( 400, 260 ); + SetDeleteSelfOnClose( true ); + + m_pGammaSlider = new CCvarSlider( this, "Gamma", "#GameUI_Gamma", 1.6f, 2.6f, "mat_monitorgamma" ); + m_pGammaLabel = new Label( this, "Gamma label", "#GameUI_Gamma" ); + m_pGammaEntry = new TextEntry( this, "GammaEntry" ); + + Button *ok = new Button( this, "OKButton", "#vgui_ok" ); + ok->SetCommand( new KeyValues("OK") ); + + LoadControlSettings( "resource/OptionsSubVideoGammaDlg.res" ); + MoveToCenterOfScreen(); + SetSizeable( false ); + + m_pGammaSlider->SetTickCaptions( "#GameUI_Light", "#GameUI_Dark" ); + } + + MESSAGE_FUNC_PTR( OnGammaChanged, "SliderMoved", panel ) + { + if (panel == m_pGammaSlider) + { + m_pGammaSlider->ApplyChanges(); + } + } + + virtual void Activate() + { + BaseClass::Activate(); + m_flOriginalGamma = m_pGammaSlider->GetValue(); + UpdateGammaLabel(); + } + + MESSAGE_FUNC( OnOK, "OK" ) + { + // make the gamma stick + m_flOriginalGamma = m_pGammaSlider->GetValue(); + Close(); + } + + virtual void OnClose() + { + // reset to the original gamma + m_pGammaSlider->SetValue( m_flOriginalGamma ); + m_pGammaSlider->ApplyChanges(); + BaseClass::OnClose(); + } + + void OnKeyCodeTyped(KeyCode code) + { + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + + MESSAGE_FUNC_PTR( OnControlModified, "ControlModified", panel ) + { + // the HasBeenModified() check is so that if the value is outside of the range of the + // slider, it won't use the slider to determine the display value but leave the + // real value that we determined in the constructor + if (panel == m_pGammaSlider && m_pGammaSlider->HasBeenModified()) + { + UpdateGammaLabel(); + } + } + + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ) + { + if (panel == m_pGammaEntry) + { + char buf[64]; + m_pGammaEntry->GetText(buf, 64); + + float fValue = (float) atof(buf); + if (fValue >= 1.0) + { + m_pGammaSlider->SetSliderValue(fValue); + PostActionSignal(new KeyValues("ApplyButtonEnable")); + } + } + } + + void UpdateGammaLabel() + { + char buf[64]; + Q_snprintf(buf, sizeof( buf ), " %.1f", m_pGammaSlider->GetSliderValue()); + m_pGammaEntry->SetText(buf); + } + + +private: + CCvarSlider *m_pGammaSlider; + vgui::Label *m_pGammaLabel; + vgui::TextEntry *m_pGammaEntry; + float m_flOriginalGamma; +}; + + +//----------------------------------------------------------------------------- +// Purpose: advanced keyboard settings dialog +//----------------------------------------------------------------------------- +class COptionsSubVideoAdvancedDlg : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsSubVideoAdvancedDlg, vgui::Frame ); +public: + COptionsSubVideoAdvancedDlg( vgui::Panel *parent ) : BaseClass( parent , "OptionsSubVideoAdvancedDlg" ) + { + SetTitle("#GameUI_VideoAdvanced_Title", true); + SetSize( 260, 400 ); + + m_pDXLevel = new ComboBox(this, "dxlabel", 6, false ); + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + KeyValues *pKeyValues = new KeyValues( "config" ); + materials->GetRecommendedConfigurationInfo( 0, pKeyValues ); + m_pDXLevel->DeleteAllItems(); + for (int i = 0; i < ARRAYSIZE(g_DirectXLevels); i++) + { + // don't allow choice of lower dxlevels than the default, + // unless we're already at that lower level or have it forced + if (!CommandLine()->CheckParm("-dxlevel") && + g_DirectXLevels[i] != config.dxSupportLevel && + g_DirectXLevels[i] < pKeyValues->GetInt("ConVar.mat_dxlevel")) + continue; + + KeyValues *pTempKV = new KeyValues("config"); + if (g_DirectXLevels[i] == pKeyValues->GetInt("ConVar.mat_dxlevel") + || materials->GetRecommendedConfigurationInfo( g_DirectXLevels[i], pTempKV )) + { + // add the configuration in the combo + char szDXLevelName[64]; + GetNameForDXLevel( g_DirectXLevels[i], szDXLevelName, sizeof(szDXLevelName) ); + m_pDXLevel->AddItem( szDXLevelName, new KeyValues("dxlevel", "dxlevel", g_DirectXLevels[i]) ); + } + + pTempKV->deleteThis(); + } + pKeyValues->deleteThis(); + + m_pModelDetail = new ComboBox( this, "ModelDetail", 6, false ); + m_pModelDetail->AddItem("#gameui_low", NULL); + m_pModelDetail->AddItem("#gameui_medium", NULL); + m_pModelDetail->AddItem("#gameui_high", NULL); + + m_pTextureDetail = new ComboBox( this, "TextureDetail", 6, false ); + m_pTextureDetail->AddItem("#gameui_low", NULL); + m_pTextureDetail->AddItem("#gameui_medium", NULL); + m_pTextureDetail->AddItem("#gameui_high", NULL); + m_pTextureDetail->AddItem("#gameui_ultra", NULL); + + // Build list of MSAA and CSAA modes, based upon those which are supported by the device + // + // The modes that we've seen in the wild to date are as follows (in perf order, fastest to slowest) + // + // 2x 4x 6x 8x 16x 8x 16xQ + // Texture/Shader Samples 1 1 1 1 1 1 1 + // Stored Color/Z Samples 2 4 6 4 4 8 8 + // Coverage Samples 2 4 6 8 16 8 16 + // MSAA or CSAA M M M C C M C + // + // The CSAA modes are nVidia only (added in the G80 generation of GPUs) + // + m_nNumAAModes = 0; + m_pAntialiasingMode = new ComboBox( this, "AntialiasingMode", 10, false ); + m_pAntialiasingMode->AddItem("#GameUI_None", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 1; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0; + m_nNumAAModes++; + + if ( materials->SupportsMSAAMode(2) ) + { + m_pAntialiasingMode->AddItem("#GameUI_2X", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 2; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0; + m_nNumAAModes++; + } + + if ( materials->SupportsMSAAMode(4) ) + { + m_pAntialiasingMode->AddItem("#GameUI_4X", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 4; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0; + m_nNumAAModes++; + } + + if ( materials->SupportsMSAAMode(6) ) + { + m_pAntialiasingMode->AddItem("#GameUI_6X", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 6; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0; + m_nNumAAModes++; + } + + if ( materials->SupportsCSAAMode(4, 2) ) // nVidia CSAA "8x" + { + m_pAntialiasingMode->AddItem("#GameUI_8X_CSAA", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 4; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 2; + m_nNumAAModes++; + } + + if ( materials->SupportsCSAAMode(4, 4) ) // nVidia CSAA "16x" + { + m_pAntialiasingMode->AddItem("#GameUI_16X_CSAA", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 4; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 4; + m_nNumAAModes++; + } + + if ( materials->SupportsMSAAMode(8) ) + { + m_pAntialiasingMode->AddItem("#GameUI_8X", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 8; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0; + m_nNumAAModes++; + } + + if ( materials->SupportsCSAAMode(8, 2) ) // nVidia CSAA "16xQ" + { + m_pAntialiasingMode->AddItem("#GameUI_16XQ_CSAA", NULL); + m_nAAModes[m_nNumAAModes].m_nNumSamples = 8; + m_nAAModes[m_nNumAAModes].m_nQualityLevel = 2; + m_nNumAAModes++; + } + + m_pFilteringMode = new ComboBox( this, "FilteringMode", 6, false ); + m_pFilteringMode->AddItem("#GameUI_Bilinear", NULL); + m_pFilteringMode->AddItem("#GameUI_Trilinear", NULL); + m_pFilteringMode->AddItem("#GameUI_Anisotropic2X", NULL); + m_pFilteringMode->AddItem("#GameUI_Anisotropic4X", NULL); + m_pFilteringMode->AddItem("#GameUI_Anisotropic8X", NULL); + m_pFilteringMode->AddItem("#GameUI_Anisotropic16X", NULL); + + m_pShadowDetail = new ComboBox( this, "ShadowDetail", 6, false ); + m_pShadowDetail->AddItem("#gameui_low", NULL); + m_pShadowDetail->AddItem("#gameui_medium", NULL); + if ( materials->SupportsShadowDepthTextures() ) + { + m_pShadowDetail->AddItem("#gameui_high", NULL); + } + + ConVarRef mat_dxlevel( "mat_dxlevel" ); + + m_pHDR = new ComboBox( this, "HDR", 6, false ); + m_pHDR->AddItem("#GameUI_hdr_level0", NULL); + m_pHDR->AddItem("#GameUI_hdr_level1", NULL); + + if ( materials->SupportsHDRMode( HDR_TYPE_INTEGER ) ) + { + m_pHDR->AddItem("#GameUI_hdr_level2", NULL); + } +#if 0 + if ( materials->SupportsHDRMode( HDR_TYPE_FLOAT ) ) + { + m_pHDR->AddItem("#GameUI_hdr_level3", NULL); + } +#endif + + m_pHDR->SetEnabled( mat_dxlevel.GetInt() >= 80 ); + + m_pWaterDetail = new ComboBox( this, "WaterDetail", 6, false ); + m_pWaterDetail->AddItem("#gameui_noreflections", NULL); + m_pWaterDetail->AddItem("#gameui_reflectonlyworld", NULL); + m_pWaterDetail->AddItem("#gameui_reflectall", NULL); + + m_pVSync = new ComboBox( this, "VSync", 2, false ); + m_pVSync->AddItem("#gameui_disabled", NULL); + m_pVSync->AddItem("#gameui_enabled", NULL); + + m_pMulticore = new ComboBox( this, "Multicore", 2, false ); + m_pMulticore->AddItem("#gameui_disabled", NULL); + m_pMulticore->AddItem("#gameui_enabled", NULL); + + m_pShaderDetail = new ComboBox( this, "ShaderDetail", 6, false ); + m_pShaderDetail->AddItem("#gameui_low", NULL); + m_pShaderDetail->AddItem("#gameui_high", NULL); + + m_pColorCorrection = new ComboBox( this, "ColorCorrection", 2, false ); + m_pColorCorrection->AddItem("#gameui_disabled", NULL); + m_pColorCorrection->AddItem("#gameui_enabled", NULL); + + m_pMotionBlur = new ComboBox( this, "MotionBlur", 2, false ); + m_pMotionBlur->AddItem("#gameui_disabled", NULL); + m_pMotionBlur->AddItem("#gameui_enabled", NULL); + + LoadControlSettings( "resource/OptionsSubVideoAdvancedDlg.res" ); + MoveToCenterOfScreen(); + SetSizeable( false ); + + m_pDXLevel->SetEnabled(false); + + m_pColorCorrection->SetEnabled( mat_dxlevel.GetInt() >= 90 ); + m_pMotionBlur->SetEnabled( mat_dxlevel.GetInt() >= 90 ); + + if ( g_pCVar->FindVar( "fov_desired" ) == NULL ) + { + Panel *pFOV = FindChildByName( "FovSlider" ); + if ( pFOV ) + { + pFOV->SetVisible( false ); + } + + pFOV = FindChildByName( "FovLabel" ); + if ( pFOV ) + { + pFOV->SetVisible( false ); + } + } + + MarkDefaultSettingsAsRecommended(); + + m_bUseChanges = false; + } + + virtual void Activate() + { + BaseClass::Activate(); + + input()->SetAppModalSurface(GetVPanel()); + + if (!m_bUseChanges) + { + // reset the data + OnResetData(); + } + } + + void SetComboItemAsRecommended( vgui::ComboBox *combo, int iItem ) + { + // get the item text + wchar_t text[512]; + combo->GetItemText(iItem, text, sizeof(text)); + + // append the recommended flag + wchar_t newText[512]; + _snwprintf( newText, sizeof(newText) / sizeof(wchar_t), L"%ls *", text ); + + // reset + combo->UpdateItem(iItem, newText, NULL); + } + + int FindMSAAMode( int nAASamples, int nAAQuality ) + { + // Run through the AA Modes supported by the device + for ( int nAAMode = 0; nAAMode < m_nNumAAModes; nAAMode++ ) + { + // If we found the mode that matches what we're looking for, return the index + if ( ( m_nAAModes[nAAMode].m_nNumSamples == nAASamples) && ( m_nAAModes[nAAMode].m_nQualityLevel == nAAQuality) ) + { + return nAAMode; + } + } + + return 0; // Didn't find what we're looking for, so no AA + } + + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ) + { + if ( panel == m_pDXLevel && RequiresRestart() ) + { + // notify the user that this will require a disconnect + QueryBox *box = new QueryBox("#GameUI_SettingRequiresDisconnect_Title", "#GameUI_SettingRequiresDisconnect_Info"); + box->AddActionSignalTarget( this ); + box->SetCancelCommand(new KeyValues("ResetDXLevelCombo")); + box->DoModal(); + } + } + + MESSAGE_FUNC( OnGameUIHidden, "GameUIHidden" ) // called when the GameUI is hidden + { + Close(); + } + + MESSAGE_FUNC( ResetDXLevelCombo, "ResetDXLevelCombo" ) + { + ConVarRef mat_dxlevel( "mat_dxlevel" ); + for (int i = 0; i < m_pDXLevel->GetItemCount(); i++) + { + KeyValues *kv = m_pDXLevel->GetItemUserData(i); + if ( kv->GetInt("dxlevel") == mat_dxlevel.GetInt( ) ) + { + m_pDXLevel->ActivateItem( i ); + break; + } + } + + // Reset HDR too + if ( m_pHDR->IsEnabled() ) + { + ConVarRef mat_hdr_level("mat_hdr_level"); + Assert( mat_hdr_level.IsValid() ); + m_pHDR->ActivateItem( clamp( mat_hdr_level.GetInt(), 0, 2 ) ); + } + } + + MESSAGE_FUNC( OK_Confirmed, "OK_Confirmed" ) + { + m_bUseChanges = true; + Close(); + } + + void MarkDefaultSettingsAsRecommended() + { + // Pull in data from dxsupport.cfg database (includes fine-grained per-vendor/per-device config data) + KeyValues *pKeyValues = new KeyValues( "config" ); + materials->GetRecommendedConfigurationInfo( 0, pKeyValues ); + + // Read individual values from keyvalues which came from dxsupport.cfg database + int nSkipLevels = pKeyValues->GetInt( "ConVar.mat_picmip", 0 ); + int nAnisotropicLevel = pKeyValues->GetInt( "ConVar.mat_forceaniso", 1 ); + int nForceTrilinear = pKeyValues->GetInt( "ConVar.mat_trilinear", 0 ); + int nAASamples = pKeyValues->GetInt( "ConVar.mat_antialias", 0 ); + int nAAQuality = pKeyValues->GetInt( "ConVar.mat_aaquality", 0 ); + int nRenderToTextureShadows = pKeyValues->GetInt( "ConVar.r_shadowrendertotexture", 0 ); + int nShadowDepthTextureShadows = pKeyValues->GetInt( "ConVar.r_flashlightdepthtexture", 0 ); +#ifndef _X360 + int nWaterUseRealtimeReflection = pKeyValues->GetInt( "ConVar.r_waterforceexpensive", 0 ); +#endif + int nWaterUseEntityReflection = pKeyValues->GetInt( "ConVar.r_waterforcereflectentities", 0 ); + int nMatVSync = pKeyValues->GetInt( "ConVar.mat_vsync", 1 ); + int nRootLOD = pKeyValues->GetInt( "ConVar.r_rootlod", 0 ); + int nReduceFillRate = pKeyValues->GetInt( "ConVar.mat_reducefillrate", 0 ); + int nDXLevel = pKeyValues->GetInt( "ConVar.mat_dxlevel", 0 ); + int nColorCorrection = pKeyValues->GetInt( "ConVar.mat_colorcorrection", 0 ); + int nMotionBlur = pKeyValues->GetInt( "ConVar.mat_motion_blur_enabled", 0 ); + // It doesn't make sense to retrieve this convar from dxsupport, because we'll then have materialsystem setting this config at loadtime. (Also, it only has very minimal support for CPU related configuration.) + //int nMulticore = pKeyValues->GetInt( "ConVar.mat_queue_mode", 0 ); + int nMulticore = GetCPUInformation()->m_nPhysicalProcessors >= 2; + + // Only recommend a dxlevel if there is more than one available + if ( m_pDXLevel->GetItemCount() > 1 ) + { + for (int i = 0; i < m_pDXLevel->GetItemCount(); i++) + { + KeyValues *kv = m_pDXLevel->GetItemUserData(i); + if (kv->GetInt("dxlevel") == pKeyValues->GetInt("ConVar.mat_dxlevel")) + { + SetComboItemAsRecommended( m_pDXLevel, i ); + break; + } + } + } + + SetComboItemAsRecommended( m_pModelDetail, 2 - nRootLOD ); + SetComboItemAsRecommended( m_pTextureDetail, 2 - nSkipLevels ); + + switch ( nAnisotropicLevel ) + { + case 2: + SetComboItemAsRecommended( m_pFilteringMode, 2 ); + break; + case 4: + SetComboItemAsRecommended( m_pFilteringMode, 3 ); + break; + case 8: + SetComboItemAsRecommended( m_pFilteringMode, 4 ); + break; + case 16: + SetComboItemAsRecommended( m_pFilteringMode, 5 ); + break; + case 0: + default: + if ( nForceTrilinear != 0 ) + { + SetComboItemAsRecommended( m_pFilteringMode, 1 ); + } + else + { + SetComboItemAsRecommended( m_pFilteringMode, 0 ); + } + break; + } + + // Map desired mode to list item number + int nMSAAMode = FindMSAAMode( nAASamples, nAAQuality ); + SetComboItemAsRecommended( m_pAntialiasingMode, nMSAAMode ); + + if ( nShadowDepthTextureShadows ) + SetComboItemAsRecommended( m_pShadowDetail, 2 ); // Shadow depth mapping (in addition to RTT shadows) + else if ( nRenderToTextureShadows ) + SetComboItemAsRecommended( m_pShadowDetail, 1 ); // RTT shadows + else + SetComboItemAsRecommended( m_pShadowDetail, 0 ); // Blobbies + + SetComboItemAsRecommended( m_pShaderDetail, nReduceFillRate ? 0 : 1 ); + +#ifndef _X360 + if ( nWaterUseRealtimeReflection ) +#endif + { + if ( nWaterUseEntityReflection ) + { + SetComboItemAsRecommended( m_pWaterDetail, 2 ); + } + else + { + SetComboItemAsRecommended( m_pWaterDetail, 1 ); + } + } +#ifndef _X360 + else + { + SetComboItemAsRecommended( m_pWaterDetail, 0 ); + } +#endif + + SetComboItemAsRecommended( m_pVSync, nMatVSync != 0 ); + + SetComboItemAsRecommended( m_pMulticore, nMulticore != 0 ); + + SetComboItemAsRecommended( m_pHDR, nDXLevel >= 90 ? 2 : 0 ); + + SetComboItemAsRecommended( m_pColorCorrection, nColorCorrection ); + + SetComboItemAsRecommended( m_pMotionBlur, nMotionBlur ); + + pKeyValues->deleteThis(); + } + + void ApplyChangesToConVar( const char *pConVarName, int value ) + { + Assert( cvar->FindVar( pConVarName ) ); + char szCmd[256]; + Q_snprintf( szCmd, sizeof(szCmd), "%s %d\n", pConVarName, value ); + engine->ClientCmd_Unrestricted( szCmd ); + } + + virtual void ApplyChanges() + { + if ( !m_bUseChanges ) + return; + + KeyValues *pActiveItem = m_pDXLevel->GetActiveItemUserData(); + if ( pActiveItem ) + { + ApplyChangesToConVar( "mat_dxlevel", pActiveItem->GetInt( "dxlevel" ) ); + } + + ApplyChangesToConVar( "r_rootlod", 2 - m_pModelDetail->GetActiveItem()); + ApplyChangesToConVar( "mat_picmip", 2 - m_pTextureDetail->GetActiveItem()); + + // reset everything tied to the filtering mode, then the switch sets the appropriate one + ApplyChangesToConVar( "mat_trilinear", false ); + ApplyChangesToConVar( "mat_forceaniso", 1 ); + switch ( m_pFilteringMode->GetActiveItem() ) + { + case 0: + break; + case 1: + ApplyChangesToConVar( "mat_trilinear", true ); + break; + case 2: + ApplyChangesToConVar( "mat_forceaniso", 2 ); + break; + case 3: + ApplyChangesToConVar( "mat_forceaniso", 4 ); + break; + case 4: + ApplyChangesToConVar( "mat_forceaniso", 8 ); + break; + case 5: + ApplyChangesToConVar( "mat_forceaniso", 16 ); + break; + default: + // Trilinear. + ApplyChangesToConVar( "mat_forceaniso", 1 ); + break; + } + + // Set the AA convars according to the menu item chosen + int nActiveAAItem = m_pAntialiasingMode->GetActiveItem(); + ApplyChangesToConVar( "mat_antialias", m_nAAModes[nActiveAAItem].m_nNumSamples ); + ApplyChangesToConVar( "mat_aaquality", m_nAAModes[nActiveAAItem].m_nQualityLevel ); + + if( m_pHDR->IsEnabled() ) + { + ConVarRef mat_hdr_level("mat_hdr_level"); + Assert( mat_hdr_level.IsValid() ); + mat_hdr_level.SetValue(m_pHDR->GetActiveItem()); + } + + if ( m_pShadowDetail->GetActiveItem() == 0 ) // Blobby shadows + { + ApplyChangesToConVar( "r_shadowrendertotexture", 0 ); // Turn off RTT shadows + ApplyChangesToConVar( "r_flashlightdepthtexture", 0 ); // Turn off shadow depth textures + } + else if ( m_pShadowDetail->GetActiveItem() == 1 ) // RTT shadows only + { + ApplyChangesToConVar( "r_shadowrendertotexture", 1 ); // Turn on RTT shadows + ApplyChangesToConVar( "r_flashlightdepthtexture", 0 ); // Turn off shadow depth textures + } + else if ( m_pShadowDetail->GetActiveItem() == 2 ) // Shadow depth textures + { + ApplyChangesToConVar( "r_shadowrendertotexture", 1 ); // Turn on RTT shadows + ApplyChangesToConVar( "r_flashlightdepthtexture", 1 ); // Turn on shadow depth textures + } + + ApplyChangesToConVar( "mat_reducefillrate", ( m_pShaderDetail->GetActiveItem() > 0 ) ? 0 : 1 ); + + switch ( m_pWaterDetail->GetActiveItem() ) + { + default: + case 0: +#ifndef _X360 + ApplyChangesToConVar( "r_waterforceexpensive", false ); +#endif + ApplyChangesToConVar( "r_waterforcereflectentities", false ); + break; + case 1: +#ifndef _X360 + ApplyChangesToConVar( "r_waterforceexpensive", true ); +#endif + ApplyChangesToConVar( "r_waterforcereflectentities", false ); + break; + case 2: +#ifndef _X360 + ApplyChangesToConVar( "r_waterforceexpensive", true ); +#endif + ApplyChangesToConVar( "r_waterforcereflectentities", true ); + break; + } + + ApplyChangesToConVar( "mat_vsync", m_pVSync->GetActiveItem() ); + + int iMC = m_pMulticore->GetActiveItem(); + ApplyChangesToConVar( "mat_queue_mode", (iMC == 0) ? 0 : -1 ); + + ApplyChangesToConVar( "mat_colorcorrection", m_pColorCorrection->GetActiveItem() ); + + ApplyChangesToConVar( "mat_motion_blur_enabled", m_pMotionBlur->GetActiveItem() ); + + CCvarSlider *pFOV = (CCvarSlider *)FindChildByName( "FOVSlider" ); + if ( pFOV ) + { + pFOV->ApplyChanges(); + } + } + + virtual void OnResetData() + { + ConVarRef mat_dxlevel( "mat_dxlevel" ); + ConVarRef r_rootlod( "r_rootlod" ); + ConVarRef mat_picmip( "mat_picmip" ); + ConVarRef mat_trilinear( "mat_trilinear" ); + ConVarRef mat_forceaniso( "mat_forceaniso" ); + ConVarRef mat_antialias( "mat_antialias" ); + ConVarRef mat_aaquality( "mat_aaquality" ); + ConVarRef mat_vsync( "mat_vsync" ); + ConVarRef mat_queue_mode( "mat_queue_mode" ); + ConVarRef r_flashlightdepthtexture( "r_flashlightdepthtexture" ); +#ifndef _X360 + ConVarRef r_waterforceexpensive( "r_waterforceexpensive" ); +#endif + ConVarRef r_waterforcereflectentities( "r_waterforcereflectentities" ); + ConVarRef mat_reducefillrate("mat_reducefillrate" ); + ConVarRef mat_hdr_level( "mat_hdr_level" ); + ConVarRef mat_colorcorrection( "mat_colorcorrection" ); + ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" ); + ConVarRef r_shadowrendertotexture( "r_shadowrendertotexture" ); + + ResetDXLevelCombo(); + + m_pModelDetail->ActivateItem( 2 - clamp(r_rootlod.GetInt(), 0, 2) ); + m_pTextureDetail->ActivateItem( 2 - clamp(mat_picmip.GetInt(), -1, 2) ); + + if ( r_flashlightdepthtexture.GetBool() ) // If we're doing flashlight shadow depth texturing... + { + r_shadowrendertotexture.SetValue( 1 ); // ...be sure render to texture shadows are also on + m_pShadowDetail->ActivateItem( 2 ); + } + else if ( r_shadowrendertotexture.GetBool() ) // RTT shadows, but not shadow depth texturing + { + m_pShadowDetail->ActivateItem( 1 ); + } + else // Lowest shadow quality + { + m_pShadowDetail->ActivateItem( 0 ); + } + + m_pShaderDetail->ActivateItem( mat_reducefillrate.GetBool() ? 0 : 1 ); + m_pHDR->ActivateItem(clamp(mat_hdr_level.GetInt(), 0, 2)); + + switch (mat_forceaniso.GetInt()) + { + case 2: + m_pFilteringMode->ActivateItem( 2 ); + break; + case 4: + m_pFilteringMode->ActivateItem( 3 ); + break; + case 8: + m_pFilteringMode->ActivateItem( 4 ); + break; + case 16: + m_pFilteringMode->ActivateItem( 5 ); + break; + case 0: + default: + if (mat_trilinear.GetBool()) + { + m_pFilteringMode->ActivateItem( 1 ); + } + else + { + m_pFilteringMode->ActivateItem( 0 ); + } + break; + } + + // Map convar to item on AA drop-down + int nAASamples = mat_antialias.GetInt(); + int nAAQuality = mat_aaquality.GetInt(); + int nMSAAMode = FindMSAAMode( nAASamples, nAAQuality ); + m_pAntialiasingMode->ActivateItem( nMSAAMode ); + + m_pAntialiasingMode->SetEnabled( m_nNumAAModes > 1 ); + +#ifndef _X360 + if ( r_waterforceexpensive.GetBool() ) +#endif + { + if ( r_waterforcereflectentities.GetBool() ) + { + m_pWaterDetail->ActivateItem( 2 ); + } + else + { + m_pWaterDetail->ActivateItem( 1 ); + } + } +#ifndef _X360 + else + { + m_pWaterDetail->ActivateItem( 0 ); + } +#endif + + m_pVSync->ActivateItem( mat_vsync.GetInt() ); + + int iMC = mat_queue_mode.GetInt(); + + // We (Rick!) have now switched -2 to mean enabled. So this comment has been rendered obsolete: + // -- For testing, we have -2, the legacy default setting as meaning multicore is disabled. + // -- After that, we'll switch -2 to mean it's enabled. + m_pMulticore->ActivateItem( (iMC == 0) ? 0 : 1 ); + + m_pColorCorrection->ActivateItem( mat_colorcorrection.GetInt() ); + + m_pMotionBlur->ActivateItem( mat_motion_blur_enabled.GetInt() ); + + // get current hardware dx support level + char dxVer[64]; + GetNameForDXLevel( mat_dxlevel.GetInt(), dxVer, sizeof( dxVer ) ); + SetControlString("dxlabel", dxVer); + + // get installed version + char szVersion[64]; + szVersion[0] = 0; + system()->GetRegistryString( "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\DirectX\\Version", szVersion, sizeof(szVersion) ); + int os = 0, majorVersion = 0, minorVersion = 0, subVersion = 0; + sscanf(szVersion, "%d.%d.%d.%d", &os, &majorVersion, &minorVersion, &subVersion); + Q_snprintf(dxVer, sizeof(dxVer), "DirectX v%d.%d", majorVersion, minorVersion); + SetControlString("dxinstalledlabel", dxVer); + } + + virtual void OnCommand( const char *command ) + { + if ( !stricmp(command, "OK") ) + { + if ( RequiresRestart() ) + { + // Bring up the confirmation dialog + QueryBox *box = new QueryBox("#GameUI_SettingRequiresDisconnect_Title", "#GameUI_SettingRequiresDisconnect_Info"); + box->AddActionSignalTarget( this ); + box->SetOKCommand(new KeyValues("OK_Confirmed")); + box->SetCancelCommand(new KeyValues("ResetDXLevelCombo")); + box->DoModal(); + box->MoveToFront(); + return; + } + + m_bUseChanges = true; + Close(); + } + else + { + BaseClass::OnCommand( command ); + } + } + + void OnKeyCodeTyped(KeyCode code) + { + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + + bool RequiresRestart() + { + if ( GameUI().IsInLevel() ) + { + if ( GameUI().IsInBackgroundLevel() ) + return false; + if ( !GameUI().IsInMultiplayer() ) + return false; + + ConVarRef mat_dxlevel( "mat_dxlevel" ); + KeyValues *pUserData = m_pDXLevel->GetActiveItemUserData(); + Assert( pUserData ); + if ( pUserData && mat_dxlevel.GetInt() != pUserData->GetInt("dxlevel") ) + { + return true; + } + + // HDR changed? + if ( m_pHDR->IsEnabled() ) + { + ConVarRef mat_hdr_level("mat_hdr_level"); + Assert( mat_hdr_level.IsValid() ); + if ( mat_hdr_level.GetInt() != m_pHDR->GetActiveItem() ) + return true; + } + } + return false; + } + +private: + bool m_bUseChanges; + vgui::ComboBox *m_pModelDetail, *m_pTextureDetail, *m_pAntialiasingMode, *m_pFilteringMode; + vgui::ComboBox *m_pShadowDetail, *m_pHDR, *m_pWaterDetail, *m_pVSync, *m_pMulticore, *m_pShaderDetail; + vgui::ComboBox *m_pColorCorrection; + vgui::ComboBox *m_pMotionBlur; + vgui::ComboBox *m_pDXLevel; + + int m_nNumAAModes; + AAMode_t m_nAAModes[16]; +}; + +#if defined( USE_SDL ) + +//----------------------------------------------------------------------------- +// Purpose: Get display index we will go fullscreen on. +//----------------------------------------------------------------------------- +static int getSDLDisplayIndex() +{ + static ConVarRef sdl_displayindex( "sdl_displayindex" ); + + Assert( sdl_displayindex.IsValid() ); + return sdl_displayindex.IsValid() ? sdl_displayindex.GetInt() : 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Get display index we are currently fullscreen on. (or -1 if none). +//----------------------------------------------------------------------------- +static int getSDLDisplayIndexFullscreen() +{ + static ConVarRef sdl_displayindex_fullscreen( "sdl_displayindex_fullscreen" ); + + Assert( sdl_displayindex_fullscreen.IsValid() ); + return sdl_displayindex_fullscreen.IsValid() ? sdl_displayindex_fullscreen.GetInt() : -1; +} + +#endif // USE_SDL + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubVideo::COptionsSubVideo(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ + m_bRequireRestart = false; + + m_bDisplayedVRModeMessage = false; + + m_pGammaButton = new Button( this, "GammaButton", "#GameUI_AdjustGamma" ); + m_pGammaButton->SetCommand(new KeyValues("OpenGammaDialog")); + m_pMode = new ComboBox(this, "Resolution", 8, false); + m_pAspectRatio = new ComboBox( this, "AspectRatio", 6, false ); + m_pVRMode = new ComboBox( this, "VRMode", 2, false ); + m_pAdvanced = new Button( this, "AdvancedButton", "#GameUI_AdvancedEllipsis" ); + m_pAdvanced->SetCommand(new KeyValues("OpenAdvanced")); + m_pBenchmark = new Button( this, "BenchmarkButton", "#GameUI_LaunchBenchmark" ); + m_pBenchmark->SetCommand(new KeyValues("LaunchBenchmark")); + m_pThirdPartyCredits = new URLButton(this, "ThirdPartyVideoCredits", "#GameUI_ThirdPartyTechCredits"); + m_pThirdPartyCredits->SetCommand(new KeyValues("OpenThirdPartyVideoCreditsDialog")); + m_pHDContent = new CheckButton( this, "HDContentButton", "#GameUI_HDContent" ); + + char pszAspectName[3][64]; + const wchar_t *unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectNormal"); + g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[0], 32); + unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectWide16x9"); + g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[1], 32); + unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectWide16x10"); + g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[2], 32); + + int iNormalItemID = m_pAspectRatio->AddItem( pszAspectName[0], NULL ); + int i16x9ItemID = m_pAspectRatio->AddItem( pszAspectName[1], NULL ); + int i16x10ItemID = m_pAspectRatio->AddItem( pszAspectName[2], NULL ); + + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + int iAspectMode = GetScreenAspectMode( config.m_VideoMode.m_Width, config.m_VideoMode.m_Height ); + switch ( iAspectMode ) + { + default: + case 0: + m_pAspectRatio->ActivateItem( iNormalItemID ); + break; + case 1: + m_pAspectRatio->ActivateItem( i16x9ItemID ); + break; + case 2: + m_pAspectRatio->ActivateItem( i16x10ItemID ); + break; + } + + char pszVRModeName[2][64]; + unicodeText = g_pVGuiLocalize->Find("#GameUI_Disabled"); + g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszVRModeName[0], 32); + unicodeText = g_pVGuiLocalize->Find("#GameUI_Enabled"); + g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszVRModeName[1], 32); + + m_pVRMode->AddItem( pszVRModeName[0], NULL ); + m_pVRMode->AddItem( pszVRModeName[1], NULL ); + + // Multimonitor under Direct3D requires you to destroy and recreate the device, + // which is an operation we don't support as it currently stands. The user can + // pass -adapter N to use a different device. +#if defined( USE_SDL ) && defined( DX_TO_GL_ABSTRACTION ) + int numVideoDisplays = SDL_GetNumVideoDisplays(); + + m_pWindowed = new vgui::ComboBox( this, "DisplayModeCombo", 5 + numVideoDisplays, false ); + + if ( numVideoDisplays <= 1 ) + { + m_pWindowed->AddItem( "#GameUI_Fullscreen", NULL ); + m_pWindowed->AddItem( "#GameUI_Windowed", NULL ); + } + else + { + // Add something like this: + // Full Screen (0) + // Full Screen (1) + // Windowed + wchar_t *fullscreenText = g_pVGuiLocalize->Find( "#GameUI_Fullscreen" ); + + for ( int i = 0; i < numVideoDisplays; i++ ) + { + wchar_t ItemText[ 256 ]; + + V_swprintf_safe( ItemText, L"%ls (%d)", fullscreenText, i ); + m_pWindowed->AddItem( ItemText, NULL ); + } + + m_pWindowed->AddItem( "#GameUI_Windowed", NULL ); + } + +#else + m_pWindowed = new vgui::ComboBox( this, "DisplayModeCombo", 6, false ); + + m_pWindowed->AddItem( "#GameUI_Fullscreen", NULL ); + m_pWindowed->AddItem( "#GameUI_Windowed", NULL ); +#endif + + LoadControlSettings("Resource\\OptionsSubVideo.res"); + + // Moved down here so we can set the Drop down's + // menu state after the default (disabled) value is loaded + PrepareResolutionList(); + + // only show the benchmark button if they have the benchmark map + if ( !g_pFullFileSystem->FileExists("maps/test_hardware.bsp") ) + { + m_pBenchmark->SetVisible( false ); + } + + if ( ModInfo().HasHDContent() ) + { + m_pHDContent->SetVisible( true ); + } + + // if VR mode isn't available, disable the dropdown + if( !g_pSourceVR ) + { + // if sourcevr.dll is missing entirely that means VR mode is not + // supported in this game. Hide the mode dropdown and its label + m_pVRMode->SetVisible( false ); + + Panel *label = FindChildByName( "VRModeLabel" ); + if( label ) + label->SetVisible( false ); + } + else if( !g_pSourceVR->IsHmdConnected() ) + { + m_pVRMode->ActivateItem( 0 ); + m_pVRMode->SetEnabled( false ); + m_pVRMode->GetTooltip()->SetText( "#GameUI_NoVRTooltip" ); + EnableOrDisableWindowedForVR(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Generates resolution list +//----------------------------------------------------------------------------- +void COptionsSubVideo::PrepareResolutionList() +{ + // get the currently selected resolution + char sz[256]; + m_pMode->GetText(sz, 256); + int currentWidth = 0, currentHeight = 0; + sscanf( sz, "%i x %i", ¤tWidth, ¤tHeight ); + + // Clean up before filling the info again. + m_pMode->DeleteAllItems(); + m_pAspectRatio->SetItemEnabled(1, false); + m_pAspectRatio->SetItemEnabled(2, false); + + // get full video mode list + vmode_t *plist = NULL; + int count = 0; + gameuifuncs->GetVideoModes( &plist, &count ); + + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + // Windowed is the last item in the combobox. + bool bWindowed = ( m_pWindowed->GetActiveItem() >= ( m_pWindowed->GetItemCount() - 1 ) ); + int desktopWidth, desktopHeight; + gameuifuncs->GetDesktopResolution( desktopWidth, desktopHeight ); + +#if defined( USE_SDL ) + bool bFullScreenWithMultipleDisplays = ( !bWindowed && ( SDL_GetNumVideoDisplays() > 1 ) ); + if ( bFullScreenWithMultipleDisplays ) + { + SDL_Rect rect; +#if defined( DX_TO_GL_ABSTRACTION ) + int displayIndex = m_pWindowed->GetActiveItem(); +#else + int displayIndex = materials->GetCurrentAdapter(); +#endif + + if ( !SDL_GetDisplayBounds( displayIndex, &rect ) ) + { + desktopWidth = rect.w; + desktopHeight = rect.h; + } + } + + // If we are switching to fullscreen, and this isn't the mode we're currently in, then + // fake things out so the native fullscreen resolution is selected. Stuck this in + // because I assume most people will go fullscreen at native resolution, and it's sometimes + // difficult to find the native resolution with all the aspect ratio options. + bool bNewFullscreenDisplay = ( !bWindowed && ( getSDLDisplayIndexFullscreen() != m_pWindowed->GetActiveItem() ) ); + if ( bNewFullscreenDisplay ) + { + currentWidth = desktopWidth; + currentHeight = desktopHeight; + } +#endif + + // iterate all the video modes adding them to the dropdown + bool bFoundWidescreen = false; + int selectedItemID = -1; + for (int i = 0; i < count; i++, plist++) + { +#if !defined( USE_SDL ) + // don't show modes bigger than the desktop for windowed mode + if ( bWindowed ) +#endif + { + if ( plist->width > desktopWidth || plist->height > desktopHeight ) + { + // Filter out sizes larger than our desktop. + continue; + } + } + + GetResolutionName( plist, sz, sizeof( sz ), desktopWidth, desktopHeight ); + + int itemID = -1; + int iAspectMode = GetScreenAspectMode( plist->width, plist->height ); + if ( iAspectMode > 0 ) + { + m_pAspectRatio->SetItemEnabled( iAspectMode, true ); + bFoundWidescreen = true; + } + + // filter the list for those matching the current aspect + if ( iAspectMode == m_pAspectRatio->GetActiveItem() ) + { + itemID = m_pMode->AddItem( sz, NULL); + } + + // try and find the best match for the resolution to be selected + if ( plist->width == currentWidth && plist->height == currentHeight ) + { + selectedItemID = itemID; + } + else if ( selectedItemID == -1 && plist->width == config.m_VideoMode.m_Width && plist->height == config.m_VideoMode.m_Height ) + { + selectedItemID = itemID; + } + } + + // disable ratio selection if we can't display widescreen. + m_pAspectRatio->SetEnabled( bFoundWidescreen ); + + m_nSelectedMode = selectedItemID; + + if ( selectedItemID != -1 ) + { + m_pMode->ActivateItem( selectedItemID ); + } + else + { + int Width = config.m_VideoMode.m_Width; + int Height = config.m_VideoMode.m_Height; + +#if defined( USE_SDL ) + // If we are switching to a new display, or the size is greater than the desktop, then + // display the desktop width and height. + if ( bNewFullscreenDisplay || ( Width > desktopWidth ) || ( Height > desktopHeight ) ) + { + Width = desktopWidth; + Height = desktopHeight; + } +#endif + + Q_snprintf( sz, ARRAYSIZE( sz ), "%d x %d", Width, Height ); + m_pMode->SetText( sz ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubVideo::~COptionsSubVideo() +{ + if (m_hOptionsSubVideoAdvancedDlg.Get()) + { + m_hOptionsSubVideoAdvancedDlg->MarkForDeletion(); + } +} + + +FILE *FOpenGameHDFile( const char *pchMode ) +{ + const char *pGameDir = engine->GetGameDirectory(); + char szModSteamInfPath[ 1024 ]; + V_ComposeFileName( pGameDir, "game_hd.txt", szModSteamInfPath, sizeof( szModSteamInfPath ) ); + + FILE *fp = fopen( szModSteamInfPath, pchMode ); + return fp; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool COptionsSubVideo::BUseHDContent() +{ + FILE *fp = FOpenGameHDFile( "rb" ); + if ( fp ) + { + fclose(fp); + return true; + } + return false; + +} + + +//----------------------------------------------------------------------------- +// Purpose: hint the engine to load HD content if possible, logic must match with engine/common.cpp BLoadHDContent +//----------------------------------------------------------------------------- +void COptionsSubVideo::SetUseHDContent( bool bUse ) +{ + if ( bUse ) + { + FILE *fp = FOpenGameHDFile( "wb+" ); + if ( fp ) + { + fprintf( fp, "If this file exists on disk HD content will be loaded.\n" ); + fclose( fp ); + } + } + else + { + const char *pGameDir = engine->GetGameDirectory(); + char szModSteamInfPath[ 1024 ]; + V_ComposeFileName( pGameDir, "game_hd.txt", szModSteamInfPath, sizeof( szModSteamInfPath ) ); + _unlink( szModSteamInfPath ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVideo::OnResetData() +{ + m_bRequireRestart = false; + + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + // reset UI elements +#if defined( USE_SDL ) && defined( DX_TO_GL_ABSTRACTION ) + int ItemIndex; + + if ( config.Windowed() ) + { + // Last item in the combobox is Windowed. + ItemIndex = ( m_pWindowed->GetItemCount() - 1 ); + } + else + { + // Check which fullscreen displayindex is currently selected, and pick it. + ItemIndex = getSDLDisplayIndex(); + + if ( ( ItemIndex < 0 ) || ItemIndex >= ( m_pWindowed->GetItemCount() - 1 ) ) + { + Assert( 0 ); + ItemIndex = 0; + } + } + + m_pWindowed->ActivateItem( ItemIndex ); +#else + m_pWindowed->ActivateItem( config.Windowed() ? 1 : 0 ); +#endif + + // reset gamma control + m_pGammaButton->SetEnabled( !config.Windowed() ); + + m_pHDContent->SetSelected( BUseHDContent() ); + + SetCurrentResolutionComboItem(); + + bool bVREnabled = config.m_nVRModeAdapter != -1; + m_pVRMode->ActivateItem( bVREnabled ? 1 : 0 ); + EnableOrDisableWindowedForVR(); + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVideo::SetCurrentResolutionComboItem() +{ + vmode_t *plist = NULL; + int count = 0; + gameuifuncs->GetVideoModes( &plist, &count ); + + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + int resolution = -1; + for ( int i = 0; i < count; i++, plist++ ) + { + if ( plist->width == config.m_VideoMode.m_Width && + plist->height == config.m_VideoMode.m_Height ) + { + resolution = i; + break; + } + } + + if (resolution != -1) + { + char sz[256]; + int desktopWidth, desktopHeight; + + gameuifuncs->GetDesktopResolution( desktopWidth, desktopHeight ); + +#if defined( USE_SDL ) + SDL_Rect rect; +#if defined( DX_TO_GL_ABSTRACTION ) + int displayIndex = getSDLDisplayIndex(); +#else + int displayIndex = materials->GetCurrentAdapter(); +#endif + + if ( !SDL_GetDisplayBounds( displayIndex, &rect ) ) + { + desktopWidth = rect.w; + desktopHeight = rect.h; + } +#endif + + GetResolutionName( plist, sz, sizeof(sz), desktopWidth, desktopHeight ); + m_pMode->SetText(sz); + } +} + +//----------------------------------------------------------------------------- +// Purpose: restarts the game +//----------------------------------------------------------------------------- +void COptionsSubVideo::OnApplyChanges() +{ + if ( RequiresRestart() ) + { + INetChannelInfo *nci = engine->GetNetChannelInfo(); + if ( nci ) + { + // Only retry if we're not running the server + const char *pAddr = nci->GetAddress(); + if ( pAddr ) + { + if ( Q_strncmp(pAddr,"127.0.0.1",9) && Q_strncmp(pAddr,"localhost",9) ) + { + engine->ClientCmd_Unrestricted( "retry\n" ); + } + else + { + engine->ClientCmd_Unrestricted( "disconnect\n" ); + } + } + } + } + + // apply advanced options + if (m_hOptionsSubVideoAdvancedDlg.Get()) + { + m_hOptionsSubVideoAdvancedDlg->ApplyChanges(); + } + + // resolution + char sz[256]; + if ( m_nSelectedMode == -1 ) + { + m_pMode->GetText( sz, 256 ); + } + else + { + m_pMode->GetItemText( m_nSelectedMode, sz, 256 ); + } + + int width = 0, height = 0; + sscanf( sz, "%i x %i", &width, &height ); + + // windowed + bool bConfigChanged = false; + bool windowed = ( m_pWindowed->GetActiveItem() == ( m_pWindowed->GetItemCount() - 1 ) ) ? true : false; + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + bool bVRMode = m_pVRMode->GetActiveItem() != 0; + if( ( -1 != config.m_nVRModeAdapter ) != bVRMode ) + { + // let engine fill in mat_vrmode_adapter + char szCmd[256]; + Q_snprintf( szCmd, sizeof(szCmd), "mat_enable_vrmode %d\n", bVRMode ? 1 : 0 ); + engine->ClientCmd_Unrestricted( szCmd ); + + // force windowed. VR mode ignores this flag and desktop mode needs to be in a window always + windowed = bVRMode; + } + + + // make sure there is a change + if ( config.m_VideoMode.m_Width != width + || config.m_VideoMode.m_Height != height + || config.Windowed() != windowed ) + { + bConfigChanged = true; + } + +#if defined( USE_SDL ) + if ( !windowed ) + { + SDL_Rect rect; + int displayIndexTarget = m_pWindowed->GetActiveItem(); + int displayIndexCurrent = getSDLDisplayIndexFullscreen(); + + // Handle going fullscreen from display X to display Y. + if ( displayIndexCurrent != displayIndexTarget ) + { + static ConVarRef sdl_displayindex( "sdl_displayindex" ); + + if ( sdl_displayindex.IsValid() ) + { + // Set the displayindex we want to go fullscreen on now. + sdl_displayindex.SetValue( displayIndexTarget ); + bConfigChanged = true; + } + } + + if ( !SDL_GetDisplayBounds( displayIndexTarget, &rect ) ) + { + // If we are going non-native fullscreen, tweak the resolution to have the same aspect ratio as the display. + if ( ( width != rect.w ) || ( height != rect.h ) ) + { + // TODO: We may want a convar to allow folks to mess with their aspect ratio? + height = ( width * rect.h ) / rect.w; + bConfigChanged = true; + } + } + } +#endif // USE_SDL + + if ( bConfigChanged ) + { + // set mode + char szCmd[ 256 ]; + Q_snprintf( szCmd, sizeof( szCmd ), "mat_setvideomode %i %i %i\n", width, height, windowed ? 1 : 0 ); + engine->ClientCmd_Unrestricted( szCmd ); + } + + if ( ModInfo().HasHDContent() ) + { + if ( BUseHDContent() != m_pHDContent->IsSelected() ) + { + SetUseHDContent( m_pHDContent->IsSelected() ); + // Bring up the confirmation dialog + MessageBox *box = new MessageBox("#GameUI_OptionsRestartRequired_Title", "#GameUI_HDRestartRequired_Info"); + box->DoModal(); + box->MoveToFront(); + } + } + + // apply changes + engine->ClientCmd_Unrestricted( "mat_savechanges\n" ); + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVideo::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_pGammaButton ) + { + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + m_pGammaButton->SetEnabled( !config.Windowed() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: enables apply button on data changing +//----------------------------------------------------------------------------- +void COptionsSubVideo::OnTextChanged(Panel *pPanel, const char *pszText) +{ + if (pPanel == m_pMode) + { + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + + m_nSelectedMode = m_pMode->GetActiveItem(); + + int w = 0, h = 0; + sscanf(pszText, "%i x %i", &w, &h); + if ( config.m_VideoMode.m_Width != w || config.m_VideoMode.m_Height != h ) + { + OnDataChanged(); + } + } + else if (pPanel == m_pAspectRatio) + { + PrepareResolutionList(); + } + else if (pPanel == m_pWindowed) + { + PrepareResolutionList(); + OnDataChanged(); + } + else if ( pPanel == m_pVRMode ) + { + if ( !m_bDisplayedVRModeMessage ) + { + bool bVRNowEnabled = m_pVRMode->GetActiveItem() == 1; + bool bVRWasEnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter != -1; + if( bVRWasEnabled != bVRNowEnabled ) + { + m_bDisplayedVRModeMessage = true; + MessageBox *box = new MessageBox( "#GameUI_VRMode", "#GameUI_VRModeRelaunchMsg", this ); + box->MoveToFront(); + box->DoModal(); + } + } + + EnableOrDisableWindowedForVR(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: enables windowed combo box +//----------------------------------------------------------------------------- +void COptionsSubVideo::EnableOrDisableWindowedForVR() +{ + bool bCanBeEnabled = g_pSourceVR && g_pSourceVR->IsHmdConnected(); + bool bVRNowEnabled = m_pVRMode->GetActiveItem() == 1; + bool bVRWasEnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter != -1; + if( bCanBeEnabled && ( bVRNowEnabled || bVRWasEnabled ) ) + { + m_pWindowed->SetEnabled( false ); + m_pWindowed->ActivateItem( m_pWindowed->GetItemCount() - 1 ); + m_pWindowed->GetTooltip()->SetText( "#GameUI_WindowedTooltip" ); + } + else + { + m_pWindowed->SetEnabled( true ); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: enables apply button +//----------------------------------------------------------------------------- +void COptionsSubVideo::OnDataChanged() +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); + +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if the changes requires a restart to take effect +//----------------------------------------------------------------------------- +bool COptionsSubVideo::RequiresRestart() +{ + if ( m_hOptionsSubVideoAdvancedDlg.Get() + && m_hOptionsSubVideoAdvancedDlg->RequiresRestart() ) + { + return true; + } + + // make sure there is a change + return m_bRequireRestart; +} + +//----------------------------------------------------------------------------- +// Purpose: Opens advanced video mode options dialog +//----------------------------------------------------------------------------- +void COptionsSubVideo::OpenAdvanced() +{ + if ( !m_hOptionsSubVideoAdvancedDlg.Get() ) + { + m_hOptionsSubVideoAdvancedDlg = new COptionsSubVideoAdvancedDlg( BasePanel()->FindChildByName( "OptionsDialog" ) ); // we'll parent this to the OptionsDialog directly + } + + m_hOptionsSubVideoAdvancedDlg->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Opens gamma-adjusting dialog +//----------------------------------------------------------------------------- +void COptionsSubVideo::OpenGammaDialog() +{ + if ( !m_hGammaDialog.Get() ) + { + m_hGammaDialog = new CGammaDialog( GetVParent() ); + } + + m_hGammaDialog->Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Opens benchmark dialog +//----------------------------------------------------------------------------- +void COptionsSubVideo::LaunchBenchmark() +{ + BasePanel()->OnOpenBenchmarkDialog(); +} + +//----------------------------------------------------------------------------- +// Purpose: third-party audio credits dialog +//----------------------------------------------------------------------------- +class COptionsSubVideoThirdPartyCreditsDlg : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COptionsSubVideoThirdPartyCreditsDlg, vgui::Frame ); +public: + COptionsSubVideoThirdPartyCreditsDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL ) + { + // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below) + + SetTitle("#GameUI_ThirdPartyVideo_Title", true); + SetSize( 500, 200 ); + LoadControlSettings( "resource/OptionsSubVideoThirdPartyDlg.res" ); + MoveToCenterOfScreen(); + SetSizeable( false ); + SetDeleteSelfOnClose( true ); + } + + virtual void Activate() + { + BaseClass::Activate(); + + input()->SetAppModalSurface(GetVPanel()); + } + + void OnKeyCodeTyped(KeyCode code) + { + // force ourselves to be closed if the escape key it pressed + if (code == KEY_ESCAPE) + { + Close(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Open third party audio credits dialog +//----------------------------------------------------------------------------- +void COptionsSubVideo::OpenThirdPartyVideoCreditsDialog() +{ + if (!m_OptionsSubVideoThirdPartyCreditsDlg.Get()) + { + m_OptionsSubVideoThirdPartyCreditsDlg = new COptionsSubVideoThirdPartyCreditsDlg(GetVParent()); + } + m_OptionsSubVideoThirdPartyCreditsDlg->Activate(); +} diff --git a/gameui/OptionsSubVideo.h b/gameui/OptionsSubVideo.h new file mode 100644 index 0000000..e832d27 --- /dev/null +++ b/gameui/OptionsSubVideo.h @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_VIDEO_H +#define OPTIONS_SUB_VIDEO_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/PropertyPage.h> +#include "EngineInterface.h" +#include "IGameUIFuncs.h" +#include "URLButton.h" + +class CCvarSlider; + +//----------------------------------------------------------------------------- +// Purpose: Video Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubVideo : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubVideo, vgui::PropertyPage ); + +public: + COptionsSubVideo(vgui::Panel *parent); + ~COptionsSubVideo(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + virtual void PerformLayout(); + + virtual bool RequiresRestart(); + +private: + void SetCurrentResolutionComboItem(); + void EnableOrDisableWindowedForVR(); + + MESSAGE_FUNC( OnDataChanged, "ControlModified" ); + MESSAGE_FUNC_PTR_CHARPTR( OnTextChanged, "TextChanged", panel, text ); + MESSAGE_FUNC( OpenAdvanced, "OpenAdvanced" ); + MESSAGE_FUNC( LaunchBenchmark, "LaunchBenchmark" ); + MESSAGE_FUNC( OpenGammaDialog, "OpenGammaDialog" ); + + + void PrepareResolutionList(); + + bool BUseHDContent(); + void SetUseHDContent( bool bUse ); + + int m_nSelectedMode; // -1 if we are running in a nonstandard mode + + bool m_bDisplayedVRModeMessage; + + vgui::ComboBox *m_pMode; + vgui::ComboBox *m_pWindowed; + vgui::ComboBox *m_pAspectRatio; + vgui::ComboBox *m_pVRMode; + vgui::Button *m_pGammaButton; + vgui::Button *m_pAdvanced; + vgui::Button *m_pBenchmark; + vgui::CheckButton *m_pHDContent; + + vgui::DHANDLE<class COptionsSubVideoAdvancedDlg> m_hOptionsSubVideoAdvancedDlg; + vgui::DHANDLE<class CGammaDialog> m_hGammaDialog; + + bool m_bRequireRestart; + MESSAGE_FUNC( OpenThirdPartyVideoCreditsDialog, "OpenThirdPartyVideoCreditsDialog" ); + vgui::URLButton *m_pThirdPartyCredits; + vgui::DHANDLE<class COptionsSubVideoThirdPartyCreditsDlg> m_OptionsSubVideoThirdPartyCreditsDlg; +}; + + + +#endif // OPTIONS_SUB_VIDEO_H
\ No newline at end of file diff --git a/gameui/OptionsSubVoice.cpp b/gameui/OptionsSubVoice.cpp new file mode 100644 index 0000000..6e08c0a --- /dev/null +++ b/gameui/OptionsSubVoice.cpp @@ -0,0 +1,334 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "OptionsSubVoice.h" +#include "cvarslider.h" +#include <vgui/IVGui.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/CheckButton.h> +#include <vgui_controls/Slider.h> +#include "EngineInterface.h" +#include "ivoicetweak.h" +#include "CvarToggleCheckButton.h" +#include "tier1/KeyValues.h" +#include "tier1/convar.h" +#include <steam/steam_api.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubVoice::COptionsSubVoice(vgui::Panel *parent) : PropertyPage(parent, NULL) +{ +#if !defined( NO_VOICE ) //#ifndef _XBOX + m_pVoiceTweak = engine->GetVoiceTweakAPI(); +#endif + m_pMicMeter = new ImagePanel(this, "MicMeter"); + m_pMicMeter2 = new ImagePanel(this, "MicMeter2"); + + m_pReceiveSliderLabel = new Label(this, "ReceiveLabel", "#GameUI_VoiceReceiveVolume"); + m_pReceiveVolume = new CCvarSlider( this, "VoiceReceive", "#GameUI_ReceiveVolume", + 0.0f, 1.0f, "voice_scale" ); + + m_pMicrophoneSliderLabel = new Label(this, "MicrophoneLabel", "#GameUI_VoiceTransmitVolume"); + m_pMicrophoneVolume = new Slider( this, "#GameUI_MicrophoneVolume" ); + m_pMicrophoneVolume->SetRange( 0, 100 ); + m_pMicrophoneVolume->AddActionSignalTarget( this ); + + m_pVoiceEnableCheckButton = new CCvarToggleCheckButton( this, "voice_modenable", "#GameUI_EnableVoice", "voice_modenable" ); + + m_pMicBoost = new CheckButton(this, "MicBoost", "#GameUI_BoostMicrophone" ); + m_pMicBoost->AddActionSignalTarget( this ); + + m_pTestMicrophoneButton = new Button(this, "TestMicrophone", "#GameUI_TestMicrophone"); + + LoadControlSettings("Resource\\OptionsSubVoice.res"); + + m_bVoiceOn = false; + m_pMicMeter2->SetVisible(false); + // no voice tweak - then disable all buttons + if (!m_pVoiceTweak) + { + m_pReceiveVolume->SetEnabled(false); + m_pMicrophoneVolume->SetEnabled(false); + m_pVoiceEnableCheckButton->SetEnabled(false); + m_pMicBoost->SetEnabled(false); + m_pTestMicrophoneButton->SetEnabled(false); + } + else + { + OnResetData(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +COptionsSubVoice::~COptionsSubVoice() +{ + // turn off voice if it was on, since we're leaving this page + if (m_bVoiceOn) + { + EndTestMicrophone(); + } + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnResetData() +{ + if (!m_pVoiceTweak) + return; + + float micVolume = m_pVoiceTweak->GetControlFloat( MicrophoneVolume ); + m_pMicrophoneVolume->SetValue( (int)( 100.0f * micVolume ) ); + m_nMicVolumeValue = m_pMicrophoneVolume->GetValue(); + + float fMicBoost = m_pVoiceTweak->GetControlFloat( MicBoost ); + m_pMicBoost->SetSelected( fMicBoost != 0.0f ); + m_bMicBoostSelected = m_pMicBoost->IsSelected(); + + m_pReceiveVolume->Reset(); + m_fReceiveVolume = m_pReceiveVolume->GetSliderValue(); + + m_pVoiceEnableCheckButton->Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnSliderMoved( int position ) +{ + if (m_pVoiceTweak) + { + if (m_pMicrophoneVolume->GetValue() != m_nMicVolumeValue) + { + PostActionSignal(new KeyValues("ApplyButtonEnable")); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnCheckButtonChecked( int state ) +{ + if (m_pVoiceTweak) + { + // if our state is different + if ( m_pMicBoost->IsSelected() != m_bMicBoostSelected) + { + PostActionSignal(new KeyValues("ApplyButtonEnable")); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnApplyChanges() +{ + if (!m_pVoiceTweak) + return; + + m_nMicVolumeValue = m_pMicrophoneVolume->GetValue(); + float fMicVolume = (float) m_nMicVolumeValue / 100.0f; + m_pVoiceTweak->SetControlFloat( MicrophoneVolume, fMicVolume ); + + m_bMicBoostSelected = m_pMicBoost->IsSelected(); + m_pVoiceTweak->SetControlFloat( MicBoost, m_bMicBoostSelected ? 1.0f : 0.0f ); + + m_pReceiveVolume->ApplyChanges(); + m_fReceiveVolume = m_pReceiveVolume->GetSliderValue(); + + m_pVoiceEnableCheckButton->ApplyChanges(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::StartTestMicrophone() +{ + if (!m_pVoiceTweak || m_bVoiceOn) + return; + + m_bVoiceOn = true; + + UseCurrentVoiceParameters(); + + if (m_pVoiceTweak->StartVoiceTweakMode()) + { + m_pTestMicrophoneButton->SetText("#GameUI_StopTestMicrophone"); + + m_pReceiveVolume->SetEnabled(false); + m_pMicrophoneVolume->SetEnabled(false); + m_pVoiceEnableCheckButton->SetEnabled(false); + m_pMicBoost->SetEnabled(false); + m_pMicrophoneSliderLabel->SetEnabled(false); + m_pReceiveSliderLabel->SetEnabled(false); + + m_pMicMeter2->SetVisible(true); + } + else + { + ResetVoiceParameters(); + + // we couldn't start it + m_bVoiceOn = false; + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::UseCurrentVoiceParameters() +{ + int nVal = m_pMicrophoneVolume->GetValue(); + float val = (float) nVal / 100.0f; + m_pVoiceTweak->SetControlFloat( MicrophoneVolume, val ); + + bool bSelected = m_pMicBoost->IsSelected(); + val = bSelected ? 1.0f : 0.0f; + m_pVoiceTweak->SetControlFloat( MicBoost, val ); + + // get where the current slider is + m_nReceiveSliderValue = m_pReceiveVolume->GetValue(); + m_pReceiveVolume->ApplyChanges(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::ResetVoiceParameters() +{ + float fMicVolume = (float) m_nMicVolumeValue / 100.0f; + m_pVoiceTweak->SetControlFloat( MicrophoneVolume, fMicVolume ); + m_pVoiceTweak->SetControlFloat( MicBoost, m_bMicBoostSelected ? 1.0f : 0.0f ); + + // restore the old value + ConVarRef voice_scale( "voice_scale" ); + voice_scale.SetValue( m_fReceiveVolume ); + + m_pReceiveVolume->Reset(); + // set the slider to 'new' value, but we've reset the 'start' value where it was + m_pReceiveVolume->SetValue(m_nReceiveSliderValue); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::EndTestMicrophone() +{ + if (!m_pVoiceTweak || !m_bVoiceOn) + return; + + if ( m_pVoiceTweak->IsStillTweaking() ) + { + m_pVoiceTweak->EndVoiceTweakMode(); + } + ResetVoiceParameters(); + m_pTestMicrophoneButton->SetText("#GameUI_TestMicrophone"); + m_bVoiceOn = false; + + m_pReceiveVolume->SetEnabled(true); + m_pMicrophoneVolume->SetEnabled(true); + m_pVoiceEnableCheckButton->SetEnabled(true); + m_pMicBoost->SetEnabled(true); + m_pMicrophoneSliderLabel->SetEnabled(true); + m_pReceiveSliderLabel->SetEnabled(true); + m_pMicMeter2->SetVisible(false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnCommand( const char *command) +{ + if (!stricmp(command, "TestMicrophone")) + { + if (!m_bVoiceOn) + { + StartTestMicrophone(); + } + else + { + EndTestMicrophone(); + } + } + else if (!stricmp(command, "SteamVoiceSettings")) + { + if ( steamapicontext && steamapicontext->SteamFriends() ) + { + steamapicontext->SteamFriends()->ActivateGameOverlay( "voicesettings" ); + } + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnPageHide() +{ + // turn off voice if it was on, since we're leaving this page + if (m_bVoiceOn) + { + EndTestMicrophone(); + } + BaseClass::OnPageHide(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnControlModified() +{ + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} + +#define BAR_WIDTH 160 +#define BAR_INCREMENT 8 + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void COptionsSubVoice::OnThink() +{ + BaseClass::OnThink(); + if (m_bVoiceOn) + { + if ( !m_pVoiceTweak->IsStillTweaking() ) + { + DevMsg( 1, "Lost Voice Tweak channels, resetting\n" ); + EndTestMicrophone(); + } + else + { + float val = m_pVoiceTweak->GetControlFloat( SpeakingVolume ); + int nValue = static_cast<int>( val*32768.0f + 0.5f ); + + int width = (BAR_WIDTH * nValue) / 32768; + width = ((width + (BAR_INCREMENT-1)) / BAR_INCREMENT) * BAR_INCREMENT; // round to nearest BAR_INCREMENT + + int wide, tall; + m_pMicMeter2->GetSize(wide, tall); + m_pMicMeter2->SetSize(width, tall); + m_pMicMeter2->Repaint(); + } + } +} diff --git a/gameui/OptionsSubVoice.h b/gameui/OptionsSubVoice.h new file mode 100644 index 0000000..945d9a6 --- /dev/null +++ b/gameui/OptionsSubVoice.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIONS_SUB_VOICE_H +#define OPTIONS_SUB_VOICE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/PropertyPage.h> + +typedef struct IVoiceTweak_s IVoiceTweak; + +class CCvarSlider; +class CCvarToggleCheckButton; +//----------------------------------------------------------------------------- +// Purpose: Voice Details, Part of OptionsDialog +//----------------------------------------------------------------------------- +class COptionsSubVoice : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( COptionsSubVoice, vgui::PropertyPage ); + +public: + COptionsSubVoice(vgui::Panel *parent); + ~COptionsSubVoice(); + + virtual void OnResetData(); + virtual void OnApplyChanges(); + +protected: + virtual void OnThink(); // called every frame before painting, but only if panel is visible + +private: + virtual void OnCommand( const char *command ); + + MESSAGE_FUNC( OnPageHide, "PageHide" ); + MESSAGE_FUNC_INT( OnSliderMoved, "SliderMoved", position ); + MESSAGE_FUNC_INT( OnCheckButtonChecked, "CheckButtonChecked", state ); + MESSAGE_FUNC( OnControlModified, "ControlModified" ); + + void StartTestMicrophone(); + void EndTestMicrophone(); + + void UseCurrentVoiceParameters(); + void ResetVoiceParameters(); + + IVoiceTweak *m_pVoiceTweak; // Engine voice tweak API. + vgui::CheckButton *m_pMicBoost; + + vgui::ImagePanel *m_pMicMeter; + vgui::ImagePanel *m_pMicMeter2; + vgui::Button *m_pTestMicrophoneButton; + vgui::Label *m_pMicrophoneSliderLabel; + vgui::Slider *m_pMicrophoneVolume; + vgui::Label *m_pReceiveSliderLabel; + CCvarSlider *m_pReceiveVolume; + CCvarToggleCheckButton *m_pVoiceEnableCheckButton; + + int m_nMicVolumeValue; + bool m_bMicBoostSelected; + float m_fReceiveVolume; + int m_nReceiveSliderValue; + + bool m_bVoiceOn; +}; + + + +#endif // OPTIONS_SUB_VOICE_H
\ No newline at end of file diff --git a/gameui/PanelListPanel.cpp b/gameui/PanelListPanel.cpp new file mode 100644 index 0000000..8ea327f --- /dev/null +++ b/gameui/PanelListPanel.cpp @@ -0,0 +1,306 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include <assert.h> +#include <vgui_controls/ScrollBar.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> + +#include <KeyValues.h> +#include <vgui/MouseCode.h> +#include <vgui/KeyCode.h> +#include <vgui/IInput.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include "PanelListPanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +class VScrollBarReversedButtons : public ScrollBar +{ +public: + VScrollBarReversedButtons( Panel *parent, const char *panelName, bool vertical ); + virtual void ApplySchemeSettings( IScheme *pScheme ); +}; + +VScrollBarReversedButtons::VScrollBarReversedButtons( Panel *parent, const char *panelName, bool vertical ) : ScrollBar( parent, panelName, vertical ) +{ +} + +void VScrollBarReversedButtons::ApplySchemeSettings( IScheme *pScheme ) +{ + ScrollBar::ApplySchemeSettings( pScheme ); + + Button *pButton; + pButton = GetButton( 0 ); + pButton->SetArmedColor( pButton->GetSchemeColor("DimBaseText", pScheme), pButton->GetBgColor()); + pButton->SetDepressedColor( pButton->GetSchemeColor("DimBaseText", pScheme), pButton->GetBgColor()); + pButton->SetDefaultColor( pButton->GetFgColor(), pButton->GetBgColor()); + + pButton = GetButton( 1 ); + pButton->SetArmedColor( pButton->GetSchemeColor("DimBaseText", pScheme), pButton->GetBgColor()); + pButton->SetDepressedColor( pButton->GetSchemeColor("DimBaseText", pScheme), pButton->GetBgColor()); + pButton->SetDefaultColor( pButton->GetFgColor(), pButton->GetBgColor()); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : x - +// y - +// wide - +// tall - +// Output : +//----------------------------------------------------------------------------- +CPanelListPanel::CPanelListPanel( vgui::Panel *parent, char const *panelName, bool inverseButtons ) : Panel( parent, panelName ) +{ + SetBounds( 0, 0, 100, 100 ); + _sliderYOffset = 0; + + if (inverseButtons) + { + _vbar = new VScrollBarReversedButtons(this, "CPanelListPanelVScroll", true ); + } + else + { + _vbar = new ScrollBar(this, "CPanelListPanelVScroll", true ); + } + _vbar->SetBounds( 0, 0, 20, 20 ); + _vbar->SetVisible(false); + _vbar->AddActionSignalTarget( this ); + + _embedded = new Panel( this, "PanelListEmbedded" ); + _embedded->SetBounds( 0, 0, 20, 20 ); + _embedded->SetPaintBackgroundEnabled( false ); + _embedded->SetPaintBorderEnabled( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPanelListPanel::~CPanelListPanel() +{ + // free data from table + DeleteAllItems(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CPanelListPanel::computeVPixelsNeeded( void ) +{ + int pixels =0; + DATAITEM *item; + Panel *panel; + for ( int i = 0; i < _dataItems.GetCount(); i++ ) + { + item = _dataItems[ i ]; + if ( !item ) + continue; + + panel = item->panel; + if ( !panel ) + continue; + + int w, h; + panel->GetSize( w, h ); + + pixels += h; + } + pixels+=5; // add a buffer after the last item + + return pixels; + +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the panel to use to render a cell +// Input : column - +// row - +// Output : Panel +//----------------------------------------------------------------------------- +Panel *CPanelListPanel::GetCellRenderer( int row ) +{ + DATAITEM *item = _dataItems[ row ]; + if ( item ) + { + Panel *panel = item->panel; + return panel; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: adds an item to the view +// data->GetName() is used to uniquely identify an item +// data sub items are matched against column header name to be used in the table +// Input : *item - +//----------------------------------------------------------------------------- +int CPanelListPanel::AddItem( Panel *panel ) +{ + InvalidateLayout(); + + DATAITEM *newitem = new DATAITEM; + newitem->panel = panel; + panel->SetParent( _embedded ); + return _dataItems.PutElement( newitem ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +int CPanelListPanel::GetItemCount( void ) +{ + return _dataItems.GetCount(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns pointer to data the row holds +// Input : itemIndex - +// Output : KeyValues +//----------------------------------------------------------------------------- +Panel *CPanelListPanel::GetItem(int itemIndex) +{ + if ( itemIndex < 0 || itemIndex >= _dataItems.GetCount() ) + return NULL; + + return _dataItems[itemIndex]->panel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : itemIndex - +// Output : DATAITEM +//----------------------------------------------------------------------------- +CPanelListPanel::DATAITEM *CPanelListPanel::GetDataItem( int itemIndex ) +{ + if ( itemIndex < 0 || itemIndex >= _dataItems.GetCount() ) + return NULL; + + return _dataItems[ itemIndex ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +//----------------------------------------------------------------------------- +void CPanelListPanel::RemoveItem(int itemIndex) +{ + DATAITEM *item = _dataItems[ itemIndex ]; + delete item->panel; + delete item; + _dataItems.RemoveElementAt(itemIndex); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: clears and deletes all the memory used by the data items +//----------------------------------------------------------------------------- +void CPanelListPanel::DeleteAllItems() +{ + for (int i = 0; i < _dataItems.GetCount(); i++) + { + if ( _dataItems[i] ) + { + delete _dataItems[i]->panel; + } + delete _dataItems[i]; + } + _dataItems.RemoveAll(); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPanelListPanel::OnMouseWheeled(int delta) +{ + int val = _vbar->GetValue(); + val -= (delta * 3 * 5); + _vbar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: relayouts out the panel after any internal changes +//----------------------------------------------------------------------------- +void CPanelListPanel::PerformLayout() +{ + int wide, tall; + GetSize( wide, tall ); + + int vpixels = computeVPixelsNeeded(); + + //!! need to make it recalculate scroll positions + _vbar->SetVisible(true); + _vbar->SetEnabled(false); + _vbar->SetRange( 0, vpixels - tall + 24); + _vbar->SetRangeWindow( 24 /*vpixels / 10*/ ); + _vbar->SetButtonPressedScrollValue( 24 ); + _vbar->SetPos(wide - 20, _sliderYOffset); + _vbar->SetSize(18, tall - 2 - _sliderYOffset); + _vbar->InvalidateLayout(); + + int top = _vbar->GetValue(); + + _embedded->SetPos( 0, -top ); + _embedded->SetSize( wide-20, vpixels ); + + // Now lay out the controls on the embedded panel + int y = 0; + int h = 0; + for ( int i = 0; i < _dataItems.GetCount(); i++, y += h ) + { + DATAITEM *item = _dataItems[ i ]; + if ( !item || !item->panel ) + continue; + + h = item->panel->GetTall(); + item->panel->SetBounds( 8, y, wide-36, h ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPanelListPanel::PaintBackground() +{ + Panel::PaintBackground(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *inResourceData - +//----------------------------------------------------------------------------- +void CPanelListPanel::ApplySchemeSettings(IScheme *pScheme) +{ + Panel::ApplySchemeSettings(pScheme); + + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + SetBgColor(GetSchemeColor("Label.BgColor", GetBgColor(), pScheme)); + + +// _labelFgColor = GetSchemeColor("WindowFgColor"); +// _selectionFgColor = GetSchemeColor("ListSelectionFgColor", _labelFgColor); +} + +void CPanelListPanel::OnSliderMoved( int position ) +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CPanelListPanel::SetSliderYOffset( int pixels ) +{ + _sliderYOffset = pixels; +} diff --git a/gameui/PanelListPanel.h b/gameui/PanelListPanel.h new file mode 100644 index 0000000..9baeccf --- /dev/null +++ b/gameui/PanelListPanel.h @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( PANELLISTPANEL_H ) +#define PANELLISTPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui_controls/Panel.h> + +class KeyValues; + + +//----------------------------------------------------------------------------- +// Purpose: A list of variable height child panels +//----------------------------------------------------------------------------- +class CPanelListPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CPanelListPanel, vgui::Panel ); + +public: + typedef struct dataitem_s + { + // Always store a panel pointer + vgui::Panel *panel; + } DATAITEM; + + CPanelListPanel( vgui::Panel *parent, char const *panelName, bool inverseButtons = false ); + ~CPanelListPanel(); + + // DATA & ROW HANDLING + // The list now owns the panel + virtual int computeVPixelsNeeded( void ); + virtual int AddItem( vgui::Panel *panel ); + virtual int GetItemCount( void ); + virtual vgui::Panel *GetItem(int itemIndex); // returns pointer to data the row holds + virtual void RemoveItem(int itemIndex); // removes an item from the table (changing the indices of all following items) + virtual void DeleteAllItems(); // clears and deletes all the memory used by the data items + + // career-mode UI wants to nudge sub-controls around + void SetSliderYOffset(int pixels); + + // PAINTING + virtual vgui::Panel *GetCellRenderer( int row ); + + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + vgui::Panel *GetEmbedded() + { + return _embedded; + } + +protected: + + DATAITEM *GetDataItem( int itemIndex ); + + virtual void PerformLayout(); + virtual void PaintBackground(); + virtual void OnMouseWheeled(int delta); + +private: + // list of the column headers + vgui::Dar<DATAITEM *> _dataItems; + vgui::ScrollBar *_vbar; + vgui::Panel *_embedded; + + int _tableStartX; + int _tableStartY; + int _sliderYOffset; +}; + +#endif // PANELLISTPANEL_H
\ No newline at end of file diff --git a/gameui/PlayerListDialog.cpp b/gameui/PlayerListDialog.cpp new file mode 100644 index 0000000..85b1267 --- /dev/null +++ b/gameui/PlayerListDialog.cpp @@ -0,0 +1,250 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "PlayerListDialog.h" + +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui_controls/ListPanel.h> +#include <KeyValues.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/MessageBox.h> +#include <vgui_controls/ComboBox.h> + +#include "EngineInterface.h" +#include "game/client/IGameClientExports.h" +#include "GameUI_Interface.h" +#include "steam/steam_api.h" +#include "fmtstr.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CPlayerListDialog::CPlayerListDialog(vgui::Panel *parent) : BaseClass(parent, "PlayerListDialog") +{ + SetSize(320, 240); + SetTitle("#GameUI_CurrentPlayers", true); + + m_pMuteButton = new Button(this, "MuteButton", ""); + + m_pPlayerList = new ListPanel(this, "PlayerList"); + m_pPlayerList->AddColumnHeader(0, "Name", "#GameUI_PlayerName", 180); + m_pPlayerList->AddColumnHeader(1, "Properties", "#GameUI_Properties", 80); + + m_pPlayerList->SetEmptyListText("#GameUI_NoOtherPlayersInGame"); + + LoadControlSettings("Resource/PlayerListDialog.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CPlayerListDialog::~CPlayerListDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerListDialog::Activate() +{ + BaseClass::Activate(); + + // refresh player list + m_pPlayerList->DeleteAllItems(); + int maxClients = engine->GetMaxClients(); + for (int i = 1; i <= maxClients; i++) + { + // get the player info from the engine + player_info_t pi; + + if ( !engine->GetPlayerInfo(i, &pi) ) + continue; + + char szPlayerIndex[32]; + Q_snprintf(szPlayerIndex, sizeof( szPlayerIndex ), "%d", i); + + // collate user data then add it to the table + KeyValues *data = new KeyValues(szPlayerIndex); + + data->SetString("Name", pi.name ); + data->SetInt("index", i); + + // add to the list + m_pPlayerList->AddItem(data, 0, false, false); + } + + // refresh player properties info + RefreshPlayerProperties(); + + // select the first item by default + m_pPlayerList->SetSingleSelectedItem( m_pPlayerList->GetItemIDFromRow(0) ); + + // toggle button states + OnItemSelected(); +} + +//----------------------------------------------------------------------------- +// Purpose: walks the players and sets their info display in the list +//----------------------------------------------------------------------------- +void CPlayerListDialog::RefreshPlayerProperties() +{ + for (int i = 0; i <= m_pPlayerList->GetItemCount(); i++) + { + KeyValues *data = m_pPlayerList->GetItem(i); + if (!data) + continue; + + // assemble properties + int playerIndex = data->GetInt("index"); + player_info_t pi; + + if ( !engine->GetPlayerInfo( playerIndex, &pi) ) + { + // disconnected + data->SetString("properties", "Disconnected"); + continue; + } + + data->SetString( "name", pi.name ); + + bool muted = false, friends = false, bot = false; + + if ( GameClientExports() && GameClientExports()->IsPlayerGameVoiceMuted(playerIndex) ) + { + muted = true; + } + if ( pi.fakeplayer ) + { + bot = true; + } + + if (bot) + { + data->SetString("properties", "CPU Player"); + } + else if (muted && friends) + { + data->SetString("properties", "Friend; Muted"); + } + else if (muted) + { + data->SetString("properties", "Muted"); + } + else if (friends) + { + data->SetString("properties", "Friend"); + } + else + { + data->SetString("properties", ""); + } + } + m_pPlayerList->RereadAllItems(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles the AddFriend command +//----------------------------------------------------------------------------- +void CPlayerListDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "Mute")) + { + ToggleMuteStateOfSelectedUser(); + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: toggles whether a user is muted or not +//----------------------------------------------------------------------------- +void CPlayerListDialog::ToggleMuteStateOfSelectedUser() +{ + if (!GameClientExports()) + return; + + for ( int iSelectedItem = 0; iSelectedItem < m_pPlayerList->GetSelectedItemsCount(); iSelectedItem++ ) + { + KeyValues *data = m_pPlayerList->GetItem( m_pPlayerList->GetSelectedItem( iSelectedItem ) ); + if (!data) + return; + int playerIndex = data->GetInt("index"); + Assert(playerIndex); + + if (GameClientExports()->IsPlayerGameVoiceMuted(playerIndex)) + { + GameClientExports()->UnmutePlayerGameVoice(playerIndex); + } + else + { + GameClientExports()->MutePlayerGameVoice(playerIndex); + } + } + + RefreshPlayerProperties(); + OnItemSelected(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerListDialog::OnItemSelected() +{ + // make sure the data is up-to-date + RefreshPlayerProperties(); + + // set the button state based on the selected item + bool bMuteButtonEnabled = false; + if (m_pPlayerList->GetSelectedItemsCount() > 0) + { + KeyValues *data = m_pPlayerList->GetItem(m_pPlayerList->GetSelectedItem(0)); + + player_info_t pi; + + int iLocalPlayer = engine->GetLocalPlayer(); + + int iPlayerIndex = data->GetInt("index"); + bool isValidPlayer = engine->GetPlayerInfo( iPlayerIndex, &pi ); + + // make sure the player is not a bot, or the user + // Matt - changed this check to see if player indeces match, instead of using friends ID + if ( pi.fakeplayer || iPlayerIndex == iLocalPlayer ) // || pi.friendsID == g_pFriendsUser->GetFriendsID() ) + { + // invalid player, + isValidPlayer = false; + } + + if (data && isValidPlayer && GameClientExports() && GameClientExports()->IsPlayerGameVoiceMuted(data->GetInt("index"))) + { + m_pMuteButton->SetText("#GameUI_UnmuteIngameVoice"); + } + else + { + m_pMuteButton->SetText("#GameUI_MuteIngameVoice"); + } + + if (GameClientExports() && isValidPlayer) + { + bMuteButtonEnabled = true; + } + } + else + { + m_pMuteButton->SetText("#GameUI_MuteIngameVoice"); + } + + m_pMuteButton->SetEnabled( bMuteButtonEnabled ); +} diff --git a/gameui/PlayerListDialog.h b/gameui/PlayerListDialog.h new file mode 100644 index 0000000..638a4e7 --- /dev/null +++ b/gameui/PlayerListDialog.h @@ -0,0 +1,53 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PLAYERLISTDIALOG_H +#define PLAYERLISTDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include "steam/steamclientpublic.h" + +//----------------------------------------------------------------------------- +// Purpose: List of players, their ingame-name and their friends-name +//----------------------------------------------------------------------------- +class CPlayerListDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CPlayerListDialog, vgui::Frame ); + +public: + CPlayerListDialog(vgui::Panel *parent); + ~CPlayerListDialog(); + + virtual void Activate(); + +private: + MESSAGE_FUNC( OnItemSelected, "ItemSelected" ); + virtual void OnCommand(const char *command); + + void ToggleMuteStateOfSelectedUser(); + void RefreshPlayerProperties(); + + void OnKeyCodePressed( vgui::KeyCode code ) + { + if ( code == KEY_XBUTTON_B ) + { + Close(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + } + + vgui::ListPanel *m_pPlayerList; + vgui::Button *m_pMuteButton; +}; + +#endif // PLAYERLISTDIALOG_H diff --git a/gameui/RunGameEngine.cpp b/gameui/RunGameEngine.cpp new file mode 100644 index 0000000..b0ecd0f --- /dev/null +++ b/gameui/RunGameEngine.cpp @@ -0,0 +1,205 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + + +#include "IRunGameEngine.h" +#include "EngineInterface.h" +#include "tier1/strtools.h" +#include "IGameUIFuncs.h" +#include "tier1/convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: Interface to running the engine from the UI dlls +//----------------------------------------------------------------------------- +class CRunGameEngine : public IRunGameEngine +{ +public: + // Returns true if the engine is running, false otherwise. + virtual bool IsRunning() + { + return true; + } + + // Adds text to the engine command buffer. Only works if IsRunning() + // returns true on success, false on failure + virtual bool AddTextCommand(const char *text) + { + engine->ClientCmd_Unrestricted((char *)text); + return true; + } + + // runs the engine with the specified command line parameters. Only works if !IsRunning() + // returns true on success, false on failure + virtual bool RunEngine(const char *gameName, const char *commandLineParams) + { + return false; + } + + virtual bool RunEngine2(const char *gameDir, const char *commandLineParams, bool isSourceGame) + { + return false; + } + + virtual ERunResult RunEngine( int iAppID, const char *gameDir, const char *commandLineParams ) + { + return k_ERunResultOkay; + } + + // returns true if the player is currently connected to a game server + virtual bool IsInGame() + { + return engine->GetLevelName() && strlen(engine->GetLevelName()) > 0; + } + + // gets information about the server the engine is currently connected to + // returns true on success, false on failure + virtual bool GetGameInfo(char *infoBuffer, int bufferSize) + { + //!! need to implement + return false; + } + + virtual void SetTrackerUserID(int trackerID, const char *trackerName) + { + gameuifuncs->SetFriendsID(trackerID, trackerName); + + // update the player's name if necessary + ConVarRef name( "name" ); + if ( name.IsValid() && trackerName && *trackerName && !Q_strcmp( name.GetString(), "unnamed" ) ) + { + name.SetValue(trackerName); + } + } + + // iterates users + // returns the number of user + virtual int GetPlayerCount() + { + return engine->GetMaxClients(); + } + + // returns a playerID for a player + // playerIndex is in the range [0, GetPlayerCount) + virtual unsigned int GetPlayerFriendsID(int playerIndex) + { + player_info_t pi; + + if ( engine->GetPlayerInfo(playerIndex, &pi ) ) + return pi.friendsID; + + return 0; + } + + // gets the in-game name of another user, returns NULL if that user doesn't exists + virtual const char *GetPlayerName(int trackerID, char *name, int namelen) + { + // find the player by their friendsID + player_info_t pi; + for (int i = 0; i < engine->GetMaxClients(); i++) + { + if (engine->GetPlayerInfo(i, &pi )) + { + if (pi.friendsID == (uint)trackerID) + { + Q_strncpy( name, pi.name, namelen ); + return name; + } + } + } + + return NULL; + } + + virtual const char *GetPlayerFriendsName(int trackerID, char *name, int namelen) + { + // find the player by their friendsID + player_info_t pi; + for (int i = 0; i < engine->GetMaxClients(); i++) + { + if (engine->GetPlayerInfo(i, &pi )) + { + if (pi.friendsID == (uint)trackerID) + { + Q_strncpy( name, pi.friendsName, namelen ); + return name; + } + } + } + + return NULL; + } + + // return the build number of the engine + virtual unsigned int GetEngineBuildNumber() + { + return engine->GetEngineBuildNumber(); + } + + // return the product version of the mod being played (comes from steam.inf) + virtual const char *GetProductVersionString() + { + return engine->GetProductVersionString(); + } +}; + +EXPOSE_SINGLE_INTERFACE(CRunGameEngine, IRunGameEngine, RUNGAMEENGINE_INTERFACE_VERSION); + +//namespace +//{ +////----------------------------------------------------------------------------- +//// Purpose: Interface to running the game engine +////----------------------------------------------------------------------------- +//abstract_class IRunGameEngine_Old : public IBaseInterface +//{ +//public: +// // Returns true if the engine is running, false otherwise. +// virtual bool IsRunning() = 0; +// +// // Adds text to the engine command buffer. Only works if IsRunning() +// // returns true on success, false on failure +// virtual bool AddTextCommand(const char *text) = 0; +// +// // runs the engine with the specified command line parameters. Only works if !IsRunning() +// // returns true on success, false on failure +// virtual bool RunEngine(const char *gameDir, const char *commandLineParams) = 0; +// +// // returns true if the player is currently connected to a game server +// virtual bool IsInGame() = 0; +// +// // gets information about the server the engine is currently ctrue on success, false on failure +// virtual bool GetGameInfo(char *infoBuffer, int bufferSize) = 0; +// +// // tells the engine our userID +// virtual void SetTrackerUserID(int trackerID, const char *trackerName) = 0; +// +// // this next section could probably moved to another interface +// // iterates users +// // returns the number of user +// virtual int GetPlayerCount() = 0; +// +// // returns a playerID for a player +// // playerIndex is in the range [0, GetPlayerCount) +// virtual unsigned int GetPlayerFriendsID(int playerIndex) = 0; +// +// // gets the in-game name of another user, returns NULL if that user doesn't exists +// virtual const char *GetPlayerName(int friendsID) = 0; +// +// // gets the friends name of a player +// virtual const char *GetPlayerFriendsName(int friendsID) = 0; +//}; +// +//#define RUNGAMEENGINE_INTERFACE_VERSION_OLD "RunGameEngine004" +// +//#ifndef _XBOX +//EXPOSE_SINGLE_INTERFACE(CRunGameEngine, IRunGameEngine_Old, RUNGAMEENGINE_INTERFACE_VERSION_OLD); +//#endif +//} + diff --git a/gameui/RunGameEngine.h b/gameui/RunGameEngine.h new file mode 100644 index 0000000..db4f992 --- /dev/null +++ b/gameui/RunGameEngine.h @@ -0,0 +1 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// diff --git a/gameui/SaveGameBrowserDialog.cpp b/gameui/SaveGameBrowserDialog.cpp new file mode 100644 index 0000000..ac894ff --- /dev/null +++ b/gameui/SaveGameBrowserDialog.cpp @@ -0,0 +1,1423 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BasePanel.h" +#include "SaveGameDialog.h" + +#include "winlite.h" // FILETIME +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" + +#include "vgui_controls/AnimationController.h" +#include "vgui_controls/ImagePanel.h" +#include "filesystem.h" +#include "KeyValues.h" +#include "ModInfo.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "vstdlib/random.h" + +#include "SaveGameBrowserDialog.h" + +extern const char *COM_GetModDirectory( void ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameSavePanel::CGameSavePanel( CSaveGameBrowserDialog *parent, SaveGameDescription_t *pSaveDesc, bool bCommandPanel ) +: BaseClass( parent, "SaveGamePanel" ) +{ + // Store our save description internally for reference later by our parent + m_SaveInfo = (*pSaveDesc); + m_bNewSavePanel = bCommandPanel; + + // Setup our main graphical elements + m_pLevelPicBorder = SETUP_PANEL( new ImagePanel( this, "LevelPicBorder" ) ); + m_pLevelPic = SETUP_PANEL( new ImagePanel( this, "LevelPic" ) ); + + // Setup our various labels + m_pChapterTitle = new Label( this, "ChapterLabel", m_SaveInfo.szComment ); + m_pTime = new Label( this, "TimeLabel", m_SaveInfo.szFileTime ); + m_pElapsedTime = new Label( this, "ElapsedLabel", m_SaveInfo.szElapsedTime ); + m_pType = new Label( this, "TypeLabel", m_SaveInfo.szType ); + + // Make sure we have a chapter description + char *pchChapterName = Q_stristr( m_SaveInfo.szComment, "chapter" ); + if ( pchChapterName ) + { + char szChapterImage[ 256 ]; + Q_snprintf( szChapterImage, sizeof(szChapterImage), "chapters/%s", Q_strlower( pchChapterName ) ); + char *ext = Q_strrchr( szChapterImage, '_' ); + if ( ext ) + { + *ext = '\0'; + } + m_pLevelPic->SetImage( szChapterImage ); + } + else + { + m_pLevelPic->SetImage( "ui_logo" ); + } + + // Setup our basic settings + KeyValues *pKeys = NULL; + if ( GameUI().IsConsoleUI() ) + { + pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SaveGamePanel.res" ); + } + LoadControlSettings( "Resource/SaveGamePanel.res", NULL, pKeys ); + + int px, py; + m_pLevelPicBorder->GetPos( px, py ); + SetSize( m_pLevelPicBorder->GetWide(), py + m_pLevelPicBorder->GetTall() + ( m_pType->GetTall() + 16 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameSavePanel::~CGameSavePanel( void ) +{ + if ( m_pLevelPicBorder ) + delete m_pLevelPicBorder; + + if ( m_pLevelPic ) + delete m_pLevelPic; + + if ( m_pChapterTitle ) + delete m_pChapterTitle; + + if ( m_pTime ) + delete m_pTime; + + if ( m_pElapsedTime ) + delete m_pElapsedTime; + + if ( m_pType ) + delete m_pType; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameSavePanel::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, 4) ); + m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + // Turn various labels off if we're the "stubbed" panel + if ( m_bNewSavePanel ) + { + m_pTime->SetVisible( false ); + m_pElapsedTime->SetVisible( false ); + m_pType->SetVisible( false ); + } + + // Setup our initial state + m_pChapterTitle->SetFgColor( m_TextColor ); + m_pTime->SetFgColor( m_TextColor ); + m_pElapsedTime->SetFgColor( m_TextColor ); + + m_pLevelPic->SetFillColor( Color( 0, 0, 0, 255 ) ); + m_pLevelPicBorder->SetFillColor( Color( 0, 0, 0, 255 ) ); + + if ( m_bNewSavePanel ) + { + float flScaleAmount = m_pLevelPic->GetScaleAmount(); + if ( flScaleAmount <= 0.0f ) + flScaleAmount = 1.0f; + + // TBD: Draw the game logo here! + int picWide = 64.0f * flScaleAmount; + int picTall = 64.0f * flScaleAmount; + int borderWide = m_pLevelPicBorder->GetWide(); + int borderTall = m_pLevelPicBorder->GetTall(); + int borderX, borderY; + m_pLevelPicBorder->GetPos( borderX, borderY ); + m_pLevelPic->SetPos( borderX + ( ( borderWide - picWide ) / 2 ), borderY + ( ( borderTall - picTall ) / 2 ) ); + m_pLevelPic->SetFillColor( Color( 0, 0, 0, 0 ) ); + } + + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Overwrite the level description +// Input : *pDesc - Description to use +//----------------------------------------------------------------------------- +void CGameSavePanel::SetDescription( SaveGameDescription_t *pDesc ) +{ + // Store our save description internally for reference later by our parent + m_SaveInfo = (*pDesc); + + // Setup our main graphical elements + m_pChapterTitle->SetText( m_SaveInfo.szComment ); + m_pTime->SetText( m_SaveInfo.szFileTime ); + m_pElapsedTime->SetText( m_SaveInfo.szElapsedTime ); + m_pType->SetText( m_SaveInfo.szType ); + + // Make sure we have a chapter description + char *pchChapterName = Q_stristr( m_SaveInfo.szComment, "chapter" ); + if ( pchChapterName ) + { + char szChapterImage[ 256 ]; + Q_snprintf( szChapterImage, sizeof(szChapterImage), "chapters/%s", Q_strlower( pchChapterName ) ); + char *ext = Q_strrchr( szChapterImage, '_' ); + if ( ext ) + { + *ext = '\0'; + } + m_pLevelPic->SetImage( szChapterImage ); + } +} + +// +// +// +// +// + +//----------------------------------------------------------------------------- +// Purpose: new game chapter selection +//----------------------------------------------------------------------------- +CSaveGameBrowserDialog::CSaveGameBrowserDialog( vgui::Panel *parent ) +: BaseClass( parent, "SaveGameDialog" ), + m_bFilterAutosaves( false ), + m_iSelectedSave( -1 ), + m_bScrolling( false ), + m_ScrollCt( 0 ), + m_ScrollSpeed( 0.0f ), + m_ButtonPressed( SCROLL_NONE ), + m_ScrollDirection( SCROLL_NONE ), + m_nDeletedPanel( INVALID_INDEX ), + m_nAddedPanel( INVALID_INDEX ), + m_nUsedStorageSpace( 0 ), + m_bControlDisabled( false ) +{ + // Setup basic attributes + SetDeleteSelfOnClose( true ); + SetSizeable( false ); + + // Create the backer that highlights the currently selected save + m_pCenterBg = SETUP_PANEL( new Panel( this, "CenterBG" ) ); + m_pCenterBg->SetPaintBackgroundType( 2 ); + m_pCenterBg->SetVisible( true ); + + // Create our button footer + m_pFooter = new CFooterPanel( parent, "SaveGameFooter" ); + + // Load our res files from the keyvalue we're holding + KeyValues *pKeys = NULL; + if ( GameUI().IsConsoleUI() ) + { + pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SaveGameDialog.res" ); + } + + LoadControlSettings( "Resource/SaveGameDialog.res", NULL, pKeys ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CSaveGameBrowserDialog::~CSaveGameBrowserDialog( void ) +{ + // Release all elements + m_SavePanels.PurgeAndDeleteElements(); + + // Kill the footer + if ( m_pFooter ) + { + delete m_pFooter; + m_pFooter = NULL; + } + + if ( m_pCenterBg ) + { + delete m_pCenterBg; + m_pCenterBg = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Show the "No save games to display" indication label and hide all browsing UI +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ShowNoSaveGameUI( void ) +{ + // Show the "no save games" text + vgui::Label *pNoSavesLabel = dynamic_cast<vgui::Label *>(FindChildByName( "NoSavesLabel" )); + if ( pNoSavesLabel ) + { + if ( m_bSaveGameIsCorrupt ) + { + pNoSavesLabel->SetText("#GameUI_SaveGame_CorruptFile"); + } + else + { + pNoSavesLabel->SetText("#GameUI_NoSaveGamesToDisplay"); + } + pNoSavesLabel->SetVisible( true ); + } + + if ( m_pCenterBg ) + m_pCenterBg->SetVisible( false ); + + vgui::Panel *pLeftArrow = FindChildByName( "LeftArrow" ); + if ( pLeftArrow ) + pLeftArrow->SetVisible( false ); + + vgui::Panel *pRightArrow = FindChildByName( "RightArrow" ); + if ( pRightArrow ) + pRightArrow->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Hide all "No save games" UI +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::HideNoSaveGameUI( void ) +{ + // Show the "no save games" text + vgui::Panel *pNoSavesLabel = FindChildByName( "NoSavesLabel" ); + if ( pNoSavesLabel ) + pNoSavesLabel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::LayoutPanels( void ) +{ + // Setup our panels depending on the mode we're in + if ( HasActivePanels() ) + { + // Hide any indicators about no save games + HideNoSaveGameUI(); + + // Layout panel positions relative to the dialog center. + int panelWidth = m_SavePanels[0]->GetWide() + 16; + int dialogWidth = GetWide(); + m_PanelXPos[2] = ( dialogWidth - panelWidth ) / 2 + 8; + 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]; + + m_PanelAlpha[0] = 0; + m_PanelAlpha[1] = 64; + m_PanelAlpha[2] = 255; + m_PanelAlpha[3] = 64; + m_PanelAlpha[4] = 0; + + int panelHeight; + m_SavePanels[0]->GetSize( panelWidth, panelHeight ); + m_pCenterBg->SetVisible( true ); + m_pCenterBg->SetWide( panelWidth + 16 ); + m_pCenterBg->SetPos( m_PanelXPos[2] - 8, m_PanelYPos[2] - (panelHeight - m_nCenterBgTallDefault) + 8 ); + m_pCenterBg->SetBgColor( Color( 190, 115, 0, 255 ) ); + } + else + { + // Hide anything to do with browsing the saves + ShowNoSaveGameUI(); + + } + + // Do internal cleanup to make sure we present a correct state to the user + UpdateMenuComponents( SCROLL_NONE ); + UpdateFooterOptions(); +} + +//----------------------------------------------------------------------------- +// Purpose: Do a fancy slide-out when we're first displayed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateDialogStart( void ) +{ + const float flAnimInTime = 0.5f; + const float flOffset = 0.1f; + + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] == INVALID_INDEX ) + continue; + + // Start us at the "opening" position + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[i] ]; + if ( panel ) + { + panel->SetPos( m_PanelXPos[0], m_PanelYPos[0] ); + panel->SetAlpha( m_PanelAlpha[0] ); + panel->SetVisible( true ); + panel->SetEnabled( true ); + panel->SetZPos( NUM_SLOTS - i ); + } + + // Now make them slide out where they're going + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + // Move and fade the back label + m_pCenterBg->SetAlpha( 0 ); + int nX, nY; + m_pCenterBg->GetPos( nX, nY ); + m_pCenterBg->SetPos( nX-m_pCenterBg->GetWide(), nY ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "xpos", nX, 0, flAnimInTime + (flOffset*2), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, (flAnimInTime+ (flOffset*2))*2.0f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + CGameSavePanel *selectedPanel = GetActivePanel(); + if ( selectedPanel && selectedPanel->IsAutoSaveType() ) + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault + 20 ); + } + else + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Do our initial layout +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::Activate( void ) +{ + // Start scanning for saved games + ScanSavedGames( m_bFilterAutosaves ); + + // Finish our layout depending on what the result of the scan was + LayoutPanels(); + + // Animate the opening animation + AnimateDialogStart(); + + BaseClass::Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply special properties of the menu +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + int ypos = inResourceData->GetInt( "chapterypos", 20 ); + for ( int i = 0; i < NUM_SLOTS; ++i ) + { + m_PanelYPos[i] = ypos; + } + + m_nCenterBgTallDefault = inResourceData->GetInt( "centerbgtall", 0 ); + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + + m_ScrollSpeedSlow = inResourceData->GetFloat( "scrollslow", 0.0f ); + m_ScrollSpeedFast = inResourceData->GetFloat( "scrollfast", 0.0f ); + SetFastScroll( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply scheme settings +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + UpdateMenuComponents( SCROLL_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the correct properties for visible components +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::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; + + // Scroll given our direction of travel + if ( dir == SCROLL_LEFT ) + { + ++centerIdx; + } + else if ( dir == SCROLL_RIGHT ) + { + --centerIdx; + } + + int leftIdx = centerIdx - 1; + int rightIdx = centerIdx + 1; + + // Update the state of the side arrows depending on whether or not we can scroll that direction + vgui::Panel *leftArrow = this->FindChildByName( "LeftArrow" ); + vgui::Panel *rightArrow = this->FindChildByName( "RightArrow" ); + if ( leftArrow ) + { + leftArrow->SetVisible( true ); + if ( m_PanelIndex[leftIdx] != INVALID_INDEX ) + { + leftArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + } + else + { + leftArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); + } + } + if ( rightArrow ) + { + rightArrow->SetVisible( true ); + if ( m_PanelIndex[rightIdx] != INVALID_INDEX ) + { + rightArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + } + else + { + rightArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets a chapter as selected +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SetSelectedSaveIndex( int index ) +{ + m_iSelectedSave = index; + + // If we have no panels, there's nothing to update + if ( HasActivePanels() == false ) + return; + + // Setup panels to the left of the selected panel + int currIdx = index; + for ( int i = SLOT_CENTER; 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 = SLOT_CENTER + 1; i < NUM_SLOTS && currIdx < m_SavePanels.Count(); ++i ) + { + m_PanelIndex[i] = currIdx; + ++currIdx; + InitPanelIndexForDisplay( i ); + } + + UpdateMenuComponents( SCROLL_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove the currently selected animation from the list with proper animations +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::RemoveActivePanel( void ) +{ + // Kill the current panel + m_nDeletedPanel = m_PanelIndex[SLOT_CENTER]; + + // Start our current panel fading + CGameSavePanel *pPanel = m_SavePanels[ m_nDeletedPanel ]; + GetAnimationController()->RunAnimationCommand( pPanel, "alpha", 0, 0, m_ScrollSpeedFast, vgui::AnimationController::INTERPOLATOR_ACCEL ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, m_ScrollSpeedFast, vgui::AnimationController::INTERPOLATOR_ACCEL ); + PostMessage( this, new KeyValues( "FinishDelete" ), m_ScrollSpeed ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::CloseAfterSave( void ) +{ + OnCommand( "CloseAndSelectResume" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishInsert( void ) +{ + CGameSavePanel *panel = m_SavePanels[ m_nAddedPanel ]; + + const float flScrollSpeed = 0.75f; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[SLOT_RIGHT], 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[SLOT_RIGHT], 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", 255, 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "CloseAfterSave" ), flScrollSpeed*2.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a new panel at the desired location +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateInsertNewPanel( const SaveGameDescription_t *pDesc ) +{ + // This is the panel that's going to move + CGameSavePanel *pNewPanel = SETUP_PANEL( new CGameSavePanel( this, (SaveGameDescription_t *) pDesc ) ); + pNewPanel->SetVisible( false ); + + // Tack this onto the list + m_nAddedPanel = m_SavePanels.InsertAfter( 0, pNewPanel ); + + // Set it up but turn it off immediately + pNewPanel->SetPos( m_PanelXPos[SLOT_CENTER], m_PanelYPos[SLOT_CENTER] ); + pNewPanel->SetVisible( true ); + pNewPanel->SetEnabled( true ); + pNewPanel->SetZPos( 0 ); + pNewPanel->SetAlpha( 0.0f ); + + // Increment our indices to reflect the change + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] == INVALID_INDEX ) + continue; + + if ( m_PanelIndex[i] > 0 ) + { + m_PanelIndex[i]++; + } + } + + // Fade the right panel away + if ( IsValidPanel( m_PanelIndex[ SLOT_RIGHT ] ) ) + { + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[ SLOT_RIGHT ] ]; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + PostMessage( this, new KeyValues( "FinishInsert" ), m_ScrollSpeed ); + } + else + { + PostMessage( this, new KeyValues( "FinishInsert" ), 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pop in the new description +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishOverwriteFadeDown( void ) +{ + const float flFadeInTime = 0.25f; + + // Fade the right panel away + CGameSavePanel *pActivePanel = GetActivePanel(); + if ( pActivePanel ) + { + pActivePanel->SetDescription( &m_NewSaveGameDesc ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( pActivePanel, "alpha", m_PanelAlpha[SLOT_CENTER], 0, flFadeInTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, flFadeInTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "CloseAfterSave" ), flFadeInTime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Animate an overwrite event by fading out the old panel and bringing it back with a new description +// Input : *pNewDesc - The new description to display +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateOverwriteActivePanel( const SaveGameDescription_t *pNewDesc ) +{ + // Save a copy of this description + m_NewSaveGameDesc = (*pNewDesc); + + // Fade the right panel away + CGameSavePanel *pActivePanel = GetActivePanel(); + if ( pActivePanel ) + { + // Panel alpha + GetAnimationController()->RunAnimationCommand( pActivePanel, "alpha", 0, 0, 0.5f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, 0.5f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "FinishOverwriteFadeDown" ), 0.75f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called before a panel scroll starts. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PreScroll( EScrollDirection dir ) +{ + int hideIdx = INVALID_INDEX; + if ( m_nDeletedPanel != INVALID_INDEX ) + { + hideIdx = m_nDeletedPanel; + } + else 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_SavePanels[hideIdx]->SetZPos( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called after a panel scroll finishes. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PostScroll( EScrollDirection dir ) +{ + // FIXME: Nothing to do here... +} + +//----------------------------------------------------------------------------- +// Purpose: Initiates a panel scroll and starts the animation. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::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 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Do all slide animation work here +// Input : nPanelIndex - Panel we're currently operating on +// nNextPanelIndex - Panel we're going to be moving over +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformSlideAction( int nPanelIndex, int nNextPanelIndex ) +{ + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[ nPanelIndex ] ]; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Initiates the scripted scroll and fade effects of all five slotted panels +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::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_SavePanels.Count() - 1 ) + { + if ( m_nDeletedPanel != INVALID_INDEX ) + { + startIdx = SLOT_RIGHT; + } + + idxOffset = -1; + endIdx = SLOT_OFFRIGHT; + m_ScrollDirection = SCROLL_LEFT; + } + else if ( m_ScrollCt <= SCROLL_RIGHT && m_PanelIndex[SLOT_CENTER] > 0 ) + { + 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 ) + { + // Don't animate the special panel, just skip it + if ( m_PanelIndex[i] == m_nDeletedPanel ) + continue; + + int nNextIdx = i+idxOffset; + if ( m_PanelIndex[i] != INVALID_INDEX ) + { + PerformSlideAction( i, nNextIdx ); + } + } + + 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_SIMPLESPLINE ); + + // Scrolling up through chapters, offset is negative + m_iSelectedSave -= idxOffset; + + UpdateFooterOptions(); + + 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 CSaveGameBrowserDialog::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; + + // Handle the deletion case + if ( m_nDeletedPanel != INVALID_INDEX ) + { + // Scroll panels in from the right + Q_memmove( &m_PanelIndex[SLOT_CENTER], &m_PanelIndex[SLOT_RIGHT], 2* sizeof( m_PanelIndex[SLOT_CENTER] ) ); + + 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 if ( offset > 0 ) + { + // Hide the panel that's dropping out of the slots + if ( IsValidPanel( m_PanelIndex[0] ) ) + { + m_SavePanels[ 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_SavePanels[ 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 CSaveGameBrowserDialog::IsValidPanel( const int idx ) +{ + if ( idx < 0 || idx >= m_SavePanels.Count() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets up a panel's properties before it is displayed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::InitPanelIndexForDisplay( const int idx ) +{ + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[idx] ]; + if ( panel ) + { + panel->SetPos( m_PanelXPos[idx], m_PanelYPos[idx] ); + panel->SetAlpha( m_PanelAlpha[idx] ); + panel->SetVisible( true ); + panel->SetEnabled( true ); + if ( m_PanelAlpha[idx] ) + { + panel->SetZPos( NUM_SLOTS ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets which scroll speed should be used +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SetFastScroll( bool fast ) +{ + m_ScrollSpeed = fast ? m_ScrollSpeedFast : m_ScrollSpeedSlow; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if a button is being held down, and speeds up the scroll +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ContinueScrolling( void ) +{ + if ( !GameUI().IsConsoleUI() ) + { + if ( m_PanelIndex[SLOT_CENTER-1] % 3 ) + { + 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: Fade animation has finished, now slide or be done +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishDelete( void ) +{ + // Catch the case where all saves are now gone! + if ( m_SavePanels.Count() == 1 ) + { + m_nDeletedPanel = INVALID_INDEX; + m_SavePanels.PurgeAndDeleteElements(); + + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + m_PanelIndex[i] = INVALID_INDEX; + } + + LayoutPanels(); + return; + } + + EScrollDirection nDirection = ( IsValidPanel( m_nDeletedPanel + 1 ) ) ? SCROLL_LEFT : SCROLL_RIGHT; + ScrollSelectionPanels( nDirection ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a scroll distance of one slot has been completed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::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 ); + + if ( m_nDeletedPanel != INVALID_INDEX ) + { + // Find where we're going next + int newSave = m_nDeletedPanel; + if ( m_SavePanels.IsValidIndex( m_nDeletedPanel + 1 ) == false ) + { + newSave = m_nDeletedPanel - 1; + } + + // Remove it from our list + CGameSavePanel *pPanel = m_SavePanels[ m_nDeletedPanel ]; + m_SavePanels.Remove( m_nDeletedPanel ); + delete pPanel; + + // Decrement all the indices to reflect the change + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] > m_nDeletedPanel ) + m_PanelIndex[i]--; + } + + // Clear the spot and be done with it + SetSelectedSaveIndex( newSave ); + m_nDeletedPanel = INVALID_INDEX; + UpdateMenuComponents( SCROLL_NONE ); + } + + // Size the "autosave" blade if need-be + CGameSavePanel *selectedPanel = GetActivePanel(); + if ( selectedPanel && selectedPanel->IsAutoSaveType() ) + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault + 20 ); + } + else + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + } + + // Continue scrolling if necessary + ContinueScrolling(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnClose( void ) +{ + SetControlDisabled( true ); + + m_KeyRepeat.Reset(); + BasePanel()->RunCloseAnimation( "CloseNewGameDialog_OpenMainMenu" ); + + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: Our save games have changed, so layout our panel again +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::RefreshSaveGames( void ) +{ + // Close any pending messages + BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); + + // Don't leave us in a locked state + SetControlDisabled( false ); + + // Re-scan the saved games + ScanSavedGames( m_bFilterAutosaves ); + + // Re-layout the panels + LayoutPanels(); + + // Run our animation again + AnimateDialogStart(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformSelectedAction( void ) +{ + // By default, do nothing + m_KeyRepeat.Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformDeletion( void ) +{ + // By default, do nothing + m_KeyRepeat.Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Release our key repeater +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnKeyCodeReleased( vgui::KeyCode code ) +{ + m_KeyRepeat.KeyUp( code ); + + BaseClass::OnKeyCodeReleased( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: Update our keypress repeater +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnThink( void ) +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + OnKeyCodePressed( code ); + } + + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + // If the console has UI up, then ignore it + if ( BasePanel()->IsWaitingForConsoleUI() ) + return; + + // Inhibit key activity during transitions + if ( GetAlpha() != 255 || m_bControlDisabled ) + return; + + m_KeyRepeat.KeyDown( code ); + + switch( code ) + { + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: + PerformSelectedAction(); + break; + + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + OnClose(); + break; + + case KEY_XBUTTON_X: + case STEAMCONTROLLER_X: + PerformDeletion(); + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + BasePanel()->OnChangeStorageDevice(); + break; + + // Move the selection up and down + case KEY_XSTICK1_LEFT: + case KEY_XBUTTON_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + ScrollSelectionPanels( SCROLL_RIGHT ); + break; + + case KEY_XSTICK1_RIGHT: + case KEY_XBUTTON_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + ScrollSelectionPanels( SCROLL_LEFT ); + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PaintBackground( void ) +{ + int wide, tall; + GetSize( wide, tall ); + + Color col = GetBgColor(); + DrawBox( 0, 0, wide, tall, col, 1.0f ); + + int y = 32; + + // 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 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the save game info out of the .sav file header +//----------------------------------------------------------------------------- +bool CSaveGameBrowserDialog::ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t *save ) +{ + char szMapName[SAVEGAME_MAPNAME_LEN]; + char szComment[SAVEGAME_COMMENT_LEN]; + char szElapsedTime[SAVEGAME_ELAPSED_LEN]; + + if ( !pszFileName || !pszShortName ) + return false; + + Q_strncpy( save->szShortName, pszShortName, sizeof(save->szShortName) ); + Q_strncpy( save->szFileName, pszFileName, sizeof(save->szFileName) ); + + FileHandle_t fh = g_pFullFileSystem->Open( pszFileName, "rb", "MOD" ); + if (fh == FILESYSTEM_INVALID_HANDLE) + return false; + + save->iSize = g_pFullFileSystem->Size( fh ); + + int readok = SaveReadNameAndComment( fh, szMapName, sizeof(szMapName), szComment, sizeof(szComment) ); + g_pFullFileSystem->Close(fh); + + if ( !readok ) + { + return false; + } + + Q_strncpy( save->szMapName, szMapName, sizeof(save->szMapName) ); + + // Elapsed time is the last 6 characters in comment. (mmm:ss) + int i; + i = strlen( szComment ); + Q_strncpy( szElapsedTime, "??", sizeof( szElapsedTime ) ); + if (i >= 6) + { + Q_strncpy( szElapsedTime, (char *)&szComment[i - 6], 7 ); + szElapsedTime[6] = '\0'; + + // parse out + int minutes = atoi( szElapsedTime ); + int seconds = atoi( szElapsedTime + 4); + int hours = minutes / 60; + minutes %= 60; + + wchar_t wzHours[6]; + wchar_t wzMins[4]; + wchar_t wzSecs[4]; + + _snwprintf( wzHours, ARRAYSIZE(wzHours), L"%d", hours ); + _snwprintf( wzMins, ARRAYSIZE(wzMins), L"%d", minutes ); + _snwprintf( wzSecs, ARRAYSIZE(wzSecs), L"%d", seconds ); + + wchar_t buf[20]; + + // reformat + if ( hours ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Hr_Min" ), 2, wzHours, wzMins ); + } + else if ( minutes ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Min_Sec" ), 2, wzMins, wzSecs ); + } + else + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Sec" ), 1, wzSecs ); + } + + g_pVGuiLocalize->ConvertUnicodeToANSI( buf, szElapsedTime, sizeof(szElapsedTime) ); + + // Chop elapsed out of comment. + char *pChop = Q_stristr( szComment, " " ); + if ( pChop != NULL ) + { + (*pChop) = '\0'; + } + } + + // calculate the file name to print + const char *pszType = ""; + if (strstr(pszFileName, "quick")) + { + pszType = "#GameUI_QuickSave"; + } + else if (strstr(pszFileName, "autosave")) + { + pszType = "#GameUI_AutoSave"; + } + + Q_strncpy( save->szType, pszType, sizeof(save->szType) ); + Q_strncpy( save->szComment, szComment, sizeof(save->szComment) ); + Q_strncpy( save->szElapsedTime, szElapsedTime, sizeof(save->szElapsedTime) ); + + // Now get file time stamp. + long fileTime = g_pFullFileSystem->GetFileTime(pszFileName); + char szFileTime[32]; + g_pFullFileSystem->FileTimeToString(szFileTime, sizeof(szFileTime), fileTime); + char *newline = strstr(szFileTime, "\n"); + if (newline) + { + *newline = 0; + } + Q_strncpy( save->szFileTime, szFileTime, sizeof(save->szFileTime) ); + save->iTimestamp = fileTime; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update our footer options depending on what we've selected +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::UpdateFooterOptions( void ) +{ + // Do nothing +} + +//----------------------------------------------------------------------------- +// Purpose: Sort our games by time +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SortSaveGames( SaveGameDescription_t *pSaves, unsigned int nNumSaves ) +{ + qsort( pSaves, nNumSaves, sizeof(SaveGameDescription_t), &CBaseSaveGameDialog::SaveGameSortFunc ); +} + +//----------------------------------------------------------------------------- +// Purpose: builds save game list from directory +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ScanSavedGames( bool bIgnoreAutosave ) +{ + // Start with a clean slate + m_nUsedStorageSpace = 0; + m_bSaveGameIsCorrupt = false; + + // Clear all known panels I'm holding now + m_SavePanels.PurgeAndDeleteElements(); + + // Reset all indices + for ( int i = 0; i < NUM_SLOTS; ++i ) + { + m_PanelIndex[i] = INVALID_INDEX; + } + + // Clear our list + CUtlVector<SaveGameDescription_t> saveGames; + + // Get the search path + char szDirectory[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s:/*", COM_GetModDirectory() ); + else + Q_snprintf( szDirectory, sizeof( szDirectory ), "save/*" ); + + Q_DefaultExtension( szDirectory, IsX360() ? ".360.sav" : ".sav", sizeof( szDirectory ) ); + Q_FixSlashes( szDirectory ); + + // iterate the saved files + FileFindHandle_t handle; + const char *pFileName = g_pFullFileSystem->FindFirst( szDirectory, &handle ); + while (pFileName) + { + if ( !Q_strnicmp(pFileName, "HLSave", strlen( "HLSave" ) ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + char szFileName[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf(szFileName, sizeof( szFileName ), "%s:/%s", COM_GetModDirectory(), pFileName ); + else + Q_snprintf(szFileName, sizeof( szFileName ), "save/%s", pFileName); + + Q_FixSlashes( szFileName ); + + // Only load save games from the current mod's save dir + if( !g_pFullFileSystem->FileExists( szFileName, "MOD" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + SaveGameDescription_t save; + if ( ParseSaveData( szFileName, pFileName, &save ) ) + { + // Add on this file's size to the count + m_nUsedStorageSpace += save.iSize; + + // Always ignore autosave dangerous (they're not considered safe until committed) + if ( Q_stristr( save.szShortName, "dangerous" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + // If we're ignoring autosaves, skip it here + if ( bIgnoreAutosave ) + { + if ( !Q_stricmp( save.szType, "#GameUI_Autosave" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + } + + saveGames.AddToTail( save ); + } + + pFileName = g_pFullFileSystem->FindNext( handle ); + } + + g_pFullFileSystem->FindClose( handle ); + + // Sort the save list + SortSaveGames( saveGames.Base(), saveGames.Count() ); + + // Now add them in order + for ( int i = 0; i < saveGames.Count(); i++ ) + { + CGameSavePanel *savePanel = SETUP_PANEL( new CGameSavePanel( this, &saveGames[i] ) ); + + savePanel->SetVisible( false ); + m_SavePanels.AddToTail( savePanel ); + } + + // Notify derived classes that save games are done being scanned + OnDoneScanningSaveGames(); + + // Always start with the first panel (as we're sorted in a specific order) + SetSelectedSaveIndex( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the currently selected panel +//----------------------------------------------------------------------------- +CGameSavePanel *CSaveGameBrowserDialog::GetActivePanel( void ) +{ + if ( IsValidPanel( m_iSelectedSave ) == false ) + return NULL; + + return m_SavePanels[ m_iSelectedSave ]; +} diff --git a/gameui/SaveGameBrowserDialog.h b/gameui/SaveGameBrowserDialog.h new file mode 100644 index 0000000..1e66f26 --- /dev/null +++ b/gameui/SaveGameBrowserDialog.h @@ -0,0 +1,193 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SAVEGAMEBROWSERDIALOG_H +#define SAVEGAMEBROWSERDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/IScheme.h" +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" + +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/KeyRepeat.h" + +#include "BasePanel.h" + +class CSaveGameBrowserDialog; + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: selectable item with screenshot for an individual chapter in the dialog +//----------------------------------------------------------------------------- +class CGameSavePanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CGameSavePanel, vgui::EditablePanel ); + +public: + + CGameSavePanel( CSaveGameBrowserDialog *parent, SaveGameDescription_t *pSaveDesc, bool bCommandPanel = false ); + ~CGameSavePanel( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + bool IsAutoSaveType( void ) { return ( Q_stristr( m_SaveInfo.szType, "autosave" ) != 0 ); } + + const SaveGameDescription_t *GetSaveInfo( void ) { return ( const SaveGameDescription_t * ) &m_SaveInfo; } + void SetDescription( SaveGameDescription_t *pDesc ); + +protected: + + SaveGameDescription_t m_SaveInfo; // Stored internally for easy access + + ImagePanel *m_pLevelPicBorder; + ImagePanel *m_pLevelPic; + ImagePanel *m_pCommentaryIcon; + Label *m_pChapterTitle; + Label *m_pTime; + Label *m_pElapsedTime; + Label *m_pType; + + Color m_TextColor; + Color m_DisabledColor; + Color m_SelectedColor; + Color m_FillColor; + + bool m_bNewSavePanel; +}; + +// Slot indices in new game menu +#define INVALID_INDEX -1 +#define SLOT_OFFLEFT 0 +#define SLOT_LEFT 1 +#define SLOT_CENTER 2 +#define SLOT_RIGHT 3 +#define SLOT_OFFRIGHT 4 +#define NUM_SLOTS 5 + +//----------------------------------------------------------------------------- +// Purpose: Handles starting a new game, skill and chapter selection +//----------------------------------------------------------------------------- +class CSaveGameBrowserDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CSaveGameBrowserDialog, vgui::Frame ); + +public: + MESSAGE_FUNC( FinishScroll, "FinishScroll" ); + MESSAGE_FUNC( FinishDelete, "FinishDelete" ); + MESSAGE_FUNC( FinishInsert, "FinishInsert" ); + MESSAGE_FUNC( FinishOverwriteFadeDown, "FinishOverwriteFadeDown" ); + MESSAGE_FUNC( CloseAfterSave, "CloseAfterSave" ); + + CSaveGameBrowserDialog(vgui::Panel *parent ); + ~CSaveGameBrowserDialog(); + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + + virtual void Activate( void ); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnClose( void ); + virtual void PaintBackground(); + virtual void PerformSelectedAction( void ); + virtual void PerformDeletion( void ); + virtual void UpdateFooterOptions( void ); + virtual void SortSaveGames( SaveGameDescription_t *pSaves, unsigned int nNumSaves ); + virtual void OnThink( void ); + virtual void OnKeyCodeReleased( vgui::KeyCode code ); + virtual void OnDoneScanningSaveGames( void ) {} + virtual void RefreshSaveGames( void ); + + unsigned int GetNumPanels( void ) { return m_SavePanels.Count(); } + bool HasActivePanels( void ) { return ( m_SavePanels.Count() != 0 ); } + CGameSavePanel *GetActivePanel( void ); + int GetActivePanelIndex( void ) { return m_iSelectedSave; } + CFooterPanel *GetFooterPanel( void ) { return m_pFooter; } + const SaveGameDescription_t *GetPanelSaveDecription( int idx ) { return ( IsValidPanel(idx) ? m_SavePanels[idx]->GetSaveInfo() : NULL ); } + const SaveGameDescription_t *GetActivePanelSaveDescription( void ) { return GetPanelSaveDecription( m_iSelectedSave ); } + + uint GetStorageSpaceUsed( void ) { return m_nUsedStorageSpace; } + + void SetSelectedSaveIndex( int index ); + void SetSelectedSave( const char *chapter ); + void AddPanel( CGameSavePanel *pPanel ) { m_SavePanels.AddToHead( pPanel ); } + + void RemoveActivePanel( void ); + void AnimateInsertNewPanel( const SaveGameDescription_t *pDesc ); + void AnimateOverwriteActivePanel( const SaveGameDescription_t *pNewDesc ); + + void SetControlDisabled( bool bState ) { m_bControlDisabled = bState; } + + // Xbox: Defined values are also used to shift the slot indices + enum EScrollDirection + { + SCROLL_RIGHT = -1, + SCROLL_NONE = 0, + SCROLL_LEFT = 1 + }; + EScrollDirection m_ScrollDirection; + +protected: + + bool m_bFilterAutosaves; + CKeyRepeatHandler m_KeyRepeat; + + bool ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t *save ); + +private: + + CUtlVector<CGameSavePanel *> m_SavePanels; + int m_iSelectedSave; + float m_ScrollSpeedSlow; + float m_ScrollSpeedFast; + int m_nDeletedPanel; // Panel being subtracted + int m_nAddedPanel; // Panel being added + SaveGameDescription_t m_NewSaveGameDesc; // Held for panel animations + uint m_nUsedStorageSpace; // Amount of disk space used by save games + + vgui::Panel *m_pCenterBg; + CFooterPanel *m_pFooter; + + // Xbox + void ScrollSelectionPanels( EScrollDirection dir ); + void PreScroll( EScrollDirection dir ); + void PostScroll( EScrollDirection dir ); + void SetFastScroll( bool fast ); + void ContinueScrolling( void ); + void AnimateSelectionPanels( void ); + void ShiftPanelIndices( int offset ); + bool IsValidPanel( const int idx ); + void InitPanelIndexForDisplay( const int idx ); + void UpdateMenuComponents( EScrollDirection dir ); + void ScanSavedGames( bool bIgnoreAutosave ); + void LayoutPanels( void ); + + // "No Save Games" label + void ShowNoSaveGameUI( void ); + void HideNoSaveGameUI( void ); + void PerformSlideAction( int nPanelIndex, int nNextPanelIndex ); + void AnimateDialogStart( void ); + + int m_nCenterBgTallDefault; + int m_PanelXPos[ NUM_SLOTS ]; + int m_PanelYPos[ NUM_SLOTS ]; + float m_PanelAlpha[ NUM_SLOTS ]; + int m_PanelIndex[ NUM_SLOTS ]; + float m_ScrollSpeed; + int m_ButtonPressed; + int m_ScrollCt; + bool m_bScrolling : 1; + bool m_bSaveGameIsCorrupt : 1; + bool m_bControlDisabled : 1; +}; + + +#endif // SAVEGAMEBROWSERDIALOG_H
\ No newline at end of file diff --git a/gameui/SaveGameDialog.cpp b/gameui/SaveGameDialog.cpp new file mode 100644 index 0000000..7a572b4 --- /dev/null +++ b/gameui/SaveGameDialog.cpp @@ -0,0 +1,163 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "SaveGameDialog.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" + +#include "vgui/ISystem.h" +#include "vgui/ISurface.h" +#include "vgui/IVGui.h" + +#include "vgui_controls/Button.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/QueryBox.h" + +#include "KeyValues.h" +#include "filesystem.h" + +#include <stdio.h> +#include <stdlib.h> + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define NEW_SAVE_GAME_TIMESTAMP 0xFFFFFFFF + +//----------------------------------------------------------------------------- +// Purpose:Constructor +//----------------------------------------------------------------------------- +CSaveGameDialog::CSaveGameDialog(vgui::Panel *parent) : BaseClass(parent, "SaveGameDialog") +{ + SetDeleteSelfOnClose(true); + SetBounds(0, 0, 512, 384); + SetSizeable( true ); + + SetTitle("#GameUI_SaveGame", true); + + vgui::Button *cancel = new vgui::Button( this, "Cancel", "#GameUI_Cancel" ); + cancel->SetCommand( "Close" ); + + LoadControlSettings("Resource\\SaveGameDialog.res"); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSaveGameDialog::~CSaveGameDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Saves game +//----------------------------------------------------------------------------- +void CSaveGameDialog::OnCommand( const char *command ) +{ + if ( !stricmp( command, "loadsave" ) ) + { + int saveIndex = GetSelectedItemSaveIndex(); + if ( m_SaveGames.IsValidIndex(saveIndex) ) + { + // see if we're overwriting + if ( m_SaveGames[saveIndex].iTimestamp == NEW_SAVE_GAME_TIMESTAMP ) + { + // new save game, proceed + OnCommand( "SaveOverwriteConfirmed" ); + } + else + { + // open confirmation dialog + QueryBox *box = new QueryBox( "#GameUI_ConfirmOverwriteSaveGame_Title", "#GameUI_ConfirmOverwriteSaveGame_Info" ); + box->AddActionSignalTarget(this); + box->SetOKButtonText("#GameUI_ConfirmOverwriteSaveGame_OK"); + box->SetOKCommand(new KeyValues("Command", "command", "SaveOverwriteConfirmed")); + box->DoModal(); + } + } + } + else if ( !stricmp( command, "SaveOverwriteConfirmed" ) ) + { + int saveIndex = GetSelectedItemSaveIndex(); + if ( m_SaveGames.IsValidIndex(saveIndex) ) + { + // delete any existing save + DeleteSaveGame( m_SaveGames[saveIndex].szFileName ); + + // save to a new name + char saveName[128]; + FindSaveSlot( saveName, sizeof(saveName) ); + if ( saveName[ 0 ] ) + { + // Load the game, return to top and switch to engine + char sz[ 256 ]; + Q_snprintf(sz, sizeof( sz ), "save %s\n", saveName ); + + engine->ClientCmd_Unrestricted( sz ); + + // Close this dialog + Close(); + + // hide the UI + GameUI().HideGameUI(); + } + } + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Opens the dialog and rebuilds the save list +//----------------------------------------------------------------------------- +void CSaveGameDialog::Activate() +{ + BaseClass::Activate(); + ScanSavedGames(); +} + +//----------------------------------------------------------------------------- +// Purpose: scans for save games +//----------------------------------------------------------------------------- +void CSaveGameDialog::OnScanningSaveGames() +{ + // create dummy item for current saved game + SaveGameDescription_t save = { "NewSavedGame", "", "", "#GameUI_NewSaveGame", "", "", "Current", NEW_SAVE_GAME_TIMESTAMP }; + m_SaveGames.AddToTail(save); +} + +//----------------------------------------------------------------------------- +// Purpose: generates a new save game name +//----------------------------------------------------------------------------- +void CSaveGameDialog::FindSaveSlot( char *buffer, int bufsize ) +{ + buffer[0] = 0; + char szFileName[512]; + for (int i = 0; i < 1000; i++) + { + Q_snprintf(szFileName, sizeof( szFileName ), "save/half-life-%03i.sav", i ); + + FileHandle_t fp = g_pFullFileSystem->Open( szFileName, "rb" ); + if (!fp) + { + // clean up name + Q_strncpy( buffer, szFileName + 5, bufsize ); + char *ext = strstr( buffer, ".sav" ); + if ( ext ) + { + *ext = 0; + } + return; + } + g_pFullFileSystem->Close(fp); + } + + Assert(!("Could not generate new save game file")); +}
\ No newline at end of file diff --git a/gameui/SaveGameDialog.h b/gameui/SaveGameDialog.h new file mode 100644 index 0000000..9557d42 --- /dev/null +++ b/gameui/SaveGameDialog.h @@ -0,0 +1,66 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SAVEGAMEDIALOG_H +#define SAVEGAMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "BaseSaveGameDialog.h" +#include "SaveGameBrowserDialog.h" +#include "vgui_controls/KeyRepeat.h" + +//----------------------------------------------------------------------------- +// Purpose: Save game dialog +//----------------------------------------------------------------------------- +class CSaveGameDialog : public CBaseSaveGameDialog +{ + DECLARE_CLASS_SIMPLE( CSaveGameDialog, CBaseSaveGameDialog ); + +public: + CSaveGameDialog( vgui::Panel *parent ); + ~CSaveGameDialog(); + + virtual void Activate(); + static void FindSaveSlot( char *buffer, int bufsize ); + +protected: + virtual void OnCommand( const char *command ); + virtual void OnScanningSaveGames(); +}; + +#define SAVE_NUM_ITEMS 4 + +// +// +// +class CAsyncCtxSaveGame; + +class CSaveGameDialogXbox : public CSaveGameBrowserDialog +{ + DECLARE_CLASS_SIMPLE( CSaveGameDialogXbox, CSaveGameBrowserDialog ); + +public: + CSaveGameDialogXbox( vgui::Panel *parent ); + virtual void PerformSelectedAction( void ); + virtual void UpdateFooterOptions( void ); + virtual void OnCommand( const char *command ); + virtual void OnDoneScanningSaveGames( void ); + +private: + friend class CAsyncCtxSaveGame; + void InitiateSaving(); + void SaveCompleted( CAsyncCtxSaveGame *pCtx ); + +private: + bool m_bGameSaving; + bool m_bNewSaveAvailable; + SaveGameDescription_t m_NewSaveDesc; +}; + +#endif // SAVEGAMEDIALOG_H diff --git a/gameui/SaveGameDialog_Xbox.cpp b/gameui/SaveGameDialog_Xbox.cpp new file mode 100644 index 0000000..610c0a0 --- /dev/null +++ b/gameui/SaveGameDialog_Xbox.cpp @@ -0,0 +1,295 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "BasePanel.h" +#include "SaveGameDialog.h" + +#include "winlite.h" // FILETIME +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" + +#include "vgui_controls/AnimationController.h" +#include "vgui_controls/ImagePanel.h" +#include "filesystem.h" +#include "KeyValues.h" +#include "ModInfo.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "vstdlib/random.h" + +#include "BaseSaveGameDialog.h" +#include "SaveGameBrowserDialog.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#include "vgui_controls/Frame.h" +#include "utlvector.h" + +extern const char *COM_GetModDirectory(); + +using namespace vgui; + +CSaveGameDialogXbox::CSaveGameDialogXbox( vgui::Panel *parent ) +: BaseClass( parent ), + m_bGameSaving ( false ), + m_bNewSaveAvailable( false ) +{ + m_bFilterAutosaves = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::PerformSelectedAction( void ) +{ + BaseClass::PerformSelectedAction(); + + // If there are no panels, don't allow this + if ( GetNumPanels() == 0 ) + return; + + SetControlDisabled( true ); + + // Decide if this is an overwrite or a new save game + bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; + if ( bNewSave ) + { + OnCommand( "SaveGame" ); + } + else + { + BasePanel()->ShowMessageDialog( MD_SAVE_OVERWRITE, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bNewSaveSelected - +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::UpdateFooterOptions( void ) +{ + CFooterPanel *pFooter = GetFooterPanel(); + + // Show available buttons + pFooter->ClearButtons(); + + bool bSavePanelsActive = ( GetNumPanels() != 0 ); + if ( bSavePanelsActive ) + { + bool bNewSaveSelected = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; + if ( bNewSaveSelected ) + { + pFooter->AddNewButtonLabel( "#GameUI_SaveGame_NewSave", "#GameUI_Icons_A_BUTTON" ); + } + else + { + pFooter->AddNewButtonLabel( "#GameUI_SaveGame_Overwrite", "#GameUI_Icons_A_BUTTON" ); + } + } + + // Always available + pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); + pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" ); +} + +//----------------------------------------------------------------------------- +// Purpose: perfrom the save on a separate thread +//----------------------------------------------------------------------------- +class CAsyncCtxSaveGame : public CBasePanel::CAsyncJobContext +{ +public: + explicit CAsyncCtxSaveGame( CSaveGameDialogXbox *pDlg ); + ~CAsyncCtxSaveGame() {} + +public: + virtual void ExecuteAsync(); + virtual void Completed(); + +public: + char m_szFilename[MAX_PATH]; + CSaveGameDialogXbox *m_pSaveGameDlg; +}; + +CAsyncCtxSaveGame::CAsyncCtxSaveGame( CSaveGameDialogXbox *pDlg ) : + CBasePanel::CAsyncJobContext( 3.0f ), // Storage device info for at least 3 seconds + m_pSaveGameDlg( pDlg ) +{ + NULL; +} + +void CAsyncCtxSaveGame::ExecuteAsync() +{ + // Sit and wait for the async save to finish + for ( ; ; ) + { + if ( !engine->IsSaveInProgress() ) + // Save operation is no longer in progress + break; + else + ThreadSleep( 50 ); + } +} + +void CAsyncCtxSaveGame::Completed() +{ + m_pSaveGameDlg->SaveCompleted( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: kicks off the async save (called on the main thread) +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::InitiateSaving() +{ + // Determine whether this is a new save or overwrite + bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; + + // Allocate the async context for saving + CAsyncCtxSaveGame *pAsyncCtx = new CAsyncCtxSaveGame( this ); + + // If this is an overwrite then there was an overwrite warning displayed + if ( !bNewSave ) + BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); + // Now display the saving warning + BasePanel()->ShowMessageDialog( MD_SAVING_WARNING, this ); + + // Kick off saving + char *szFilename = pAsyncCtx->m_szFilename; + const int maxFilenameLen = sizeof( pAsyncCtx->m_szFilename ); + char szCmd[MAX_PATH]; + + // See if this is the "new save game" slot + if ( bNewSave ) + { + // Create a new save game (name is created from the current time, which should be pretty unique) +#ifdef WIN32 + FILETIME currentTime; + GetSystemTimeAsFileTime( ¤tTime ); + Q_snprintf( szFilename, maxFilenameLen, "%s_%u", COM_GetModDirectory(), currentTime.dwLowDateTime ); +#else + time_t currentTime = time( NULL ); + Q_snprintf( szFilename, maxFilenameLen, "%s_%u", COM_GetModDirectory(), (unsigned)currentTime ); +#endif + Q_snprintf( szCmd, sizeof( szCmd ), "xsave %s", szFilename ); + engine->ExecuteClientCmd( szCmd ); + Q_strncat( szFilename, ".360.sav", maxFilenameLen ); + } + else + { + const SaveGameDescription_t *pDesc = GetActivePanelSaveDescription(); + Q_strncpy( szFilename, pDesc->szShortName, maxFilenameLen ); + Q_snprintf( szCmd, sizeof( szCmd ), "xsave %s", szFilename ); + engine->ExecuteClientCmd( szCmd ); + } + + // Enqueue waiting + BasePanel()->ExecuteAsync( pAsyncCtx ); +} + +//----------------------------------------------------------------------------- +// Purpose: handles the end of async save (called on the main thread) +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::SaveCompleted( CAsyncCtxSaveGame *pCtx ) +{ + char const *szFilename = pCtx->m_szFilename; + + // We should now be saved so get the new desciption back from the file + char szDirectory[MAX_PATH]; + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s:/%s", COM_GetModDirectory(), szFilename ); + + ParseSaveData( szDirectory, szFilename, &m_NewSaveDesc ); + + // Close the progress dialog + BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); + + bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; + if ( bNewSave ) + { + AnimateInsertNewPanel( &m_NewSaveDesc ); + } + else + { + AnimateOverwriteActivePanel( &m_NewSaveDesc ); + } + + m_bGameSaving = false; +} + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::OnCommand( const char *command ) +{ + m_KeyRepeat.Reset(); + + if ( !Q_stricmp( command, "SaveGame" ) ) + { + if ( m_bGameSaving ) + return; + m_bGameSaving = true; + + SetControlDisabled( true ); + + // Initiate the saving operation + InitiateSaving(); + } + else if ( !Q_stricmp( command, "SaveSuccess" ) ) + { + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + GameUI().SetSavedThisMenuSession( true ); + } + else if ( !Q_stricmp( command, "CloseAndSelectResume" ) ) + { + BasePanel()->ArmFirstMenuItem(); + OnCommand( "Close" ); + } + else if ( !Q_stricmp( command, "OverwriteGameCancelled" ) ) + { + SetControlDisabled( false ); + } + else if ( !Q_stricmp( command, "RefreshSaveGames" ) ) + { + RefreshSaveGames(); + } + else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) + { + vgui::surface()->RestrictPaintToSinglePanel( NULL ); + } + else if ( !m_bGameSaving ) + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: On completion of scanning, prepend a utility slot on the stack +//----------------------------------------------------------------------------- +void CSaveGameDialogXbox::OnDoneScanningSaveGames( void ) +{ + ConVarRef save_history_count("save_history_count" ); + + m_bNewSaveAvailable = false; +#ifdef _X360 + if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ) + return; + + // We only allow 10 save games minus the number of autosaves, autosavedangerous, and autosave0?'s at once + if ( GetNumPanels() >= 10 - ( 2 + (unsigned)save_history_count.GetInt() ) ) + return; + + if ( GetStorageSpaceUsed() + XBX_SAVEGAME_BYTES > XBX_PERSISTENT_BYTES_NEEDED ) + return; + + m_bNewSaveAvailable = true; + SaveGameDescription_t bogusDesc = { "#GameUI_SaveGame_NewSavedGame", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", 0, 0 }; + CGameSavePanel *newSavePanel = SETUP_PANEL( new CGameSavePanel( this, &bogusDesc, true ) ); + AddPanel( newSavePanel ); +#endif // _X360 +} diff --git a/gameui/Sys_Utils.cpp b/gameui/Sys_Utils.cpp new file mode 100644 index 0000000..44f77fe --- /dev/null +++ b/gameui/Sys_Utils.cpp @@ -0,0 +1,180 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "winlite.h" +#include "Sys_Utils.h" +#include "EngineInterface.h" +#include "tier0/vcrmode.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#ifdef WIN32 +const unsigned int SYS_NO_ERROR = NO_ERROR; +const unsigned int SYS_ERROR_INVALID_HANDLE = ERROR_INVALID_HANDLE; + +void Sys_SetLastError(unsigned long error) +{ + ::SetLastError(error); +} + +unsigned long Sys_GetLastError() +{ + return ::GetLastError(); +} + + +WHANDLE Sys_CreateMutex(const char *mutexName) +{ + return (WHANDLE)::CreateMutex(NULL, FALSE, TEXT(mutexName)); +} + +void Sys_ReleaseMutex(WHANDLE mutexHandle) +{ + ::ReleaseMutex((HANDLE)mutexHandle); +} + + +const unsigned int SYS_WAIT_OBJECT_0 = WAIT_OBJECT_0; +const unsigned int SYS_WAIT_ABANDONED = WAIT_ABANDONED; + +unsigned int Sys_WaitForSingleObject(WHANDLE mutexHandle, int milliseconds) +{ + return VCRHook_WaitForSingleObject((HANDLE)mutexHandle, milliseconds); +} + +unsigned int Sys_RegisterWindowMessage(const char *msgName) +{ + return ::RegisterWindowMessage(msgName); +} + +WHANDLE Sys_FindWindow(const char *className, const char *windowName) +{ + return (WHANDLE)::FindWindow(className, windowName); +} + +void Sys_EnumWindows(void *callbackFunction, int lparam) +{ + ::EnumWindows((WNDENUMPROC)callbackFunction, lparam); +} + +#ifndef _XBOX +void Sys_GetWindowText(WHANDLE wnd, char *buffer, int bufferSize) +{ + ::GetWindowText((HWND)wnd, buffer, bufferSize - 1); +} +#endif + +void Sys_PostMessage(WHANDLE wnd, unsigned int msg, unsigned int wParam, unsigned int lParam) +{ + ::PostMessageA((HWND)wnd, msg, wParam, lParam); +} + +#ifndef _XBOX +void Sys_SetCursorPos(int x, int y) +{ + ::SetCursorPos(x, y); +// engine->SetCursorPos(x,y); // SRC version +} +#endif + +#ifndef _XBOX +static ATOM staticWndclassAtom = 0; +static WNDCLASS staticWndclass = { NULL }; +#endif + +static LRESULT CALLBACK staticProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + return DefWindowProc(hwnd,msg,wparam,lparam); +} + +WHANDLE Sys_CreateWindowEx(const char *windowName) +{ + /* + if (!staticWndclassAtom) + { + memset( &staticWndclass,0,sizeof(staticWndclass) ); + staticWndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + staticWndclass.lpfnWndProc = staticProc; + staticWndclass.hInstance = GetModuleHandle(NULL); + staticWndclass.hIcon = 0; + staticWndclass.lpszClassName = windowName; + staticWndclassAtom = ::RegisterClass( &staticWndclass ); + + DWORD error = ::GetLastError(); + } + + return (WHANDLE)::CreateWindow(windowName, windowName, 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(NULL), 0); + */ + return (WHANDLE)1; +} + +void Sys_DestroyWindow(WHANDLE wnd) +{ + //::DestroyWindow((HWND)wnd); +} + +#elif defined( POSIX ) +const unsigned int SYS_NO_ERROR = 0; +const unsigned int SYS_ERROR_INVALID_HANDLE = 0; +const unsigned int SYS_WAIT_OBJECT_0 = 0; +const unsigned int SYS_WAIT_ABANDONED = 0; + + +void Sys_SetLastError(unsigned long error) +{ + errno = error; +} + +unsigned long Sys_GetLastError() +{ + return errno; +} + + +WHANDLE Sys_CreateMutex(const char *mutexName) +{ + Assert( !"Implement me" ); + return 0; +} + +void Sys_ReleaseMutex(WHANDLE mutexHandle) +{ + Assert( !"Implement me" ); +} + +void Sys_PostMessage(WHANDLE wnd, unsigned int msg, unsigned int wParam, unsigned int lParam) +{ + Assert( !"Implement me" ); +} + +unsigned int Sys_RegisterWindowMessage(const char *msgName) +{ + Assert( !"Implement me" ); + return 0; +} + +unsigned int Sys_WaitForSingleObject(WHANDLE mutexHandle, int milliseconds) +{ + return SYS_WAIT_ABANDONED; +} + +void Sys_EnumWindows(void *callbackFunction, int lparam) +{ + Assert( !"Implement me" ); +} + + +#else +#error +#endif + diff --git a/gameui/Sys_Utils.h b/gameui/Sys_Utils.h new file mode 100644 index 0000000..5880f73 --- /dev/null +++ b/gameui/Sys_Utils.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SYS_UTILS_H +#define SYS_UTILS_H +#ifdef _WIN32 +#pragma once +#endif + +typedef int WHANDLE; + +// ERROR HANDLING +extern const unsigned int SYS_NO_ERROR; +extern const unsigned int SYS_ERROR_INVALID_HANDLE; + +void Sys_SetLastError(unsigned long error); +unsigned long Sys_GetLastError(); + +// MUTEX HANDLING +WHANDLE Sys_CreateMutex(const char *mutexName); +void Sys_ReleaseMutex(WHANDLE mutexHandle); + +// MUTEX SYNCHRONIZATION +extern const unsigned int SYS_WAIT_OBJECT_0; +extern const unsigned int SYS_WAIT_ABANDONED; +unsigned int Sys_WaitForSingleObject(WHANDLE mutexHandle, int milliseconds); + +// window handling +unsigned int Sys_RegisterWindowMessage(const char *msgName); +WHANDLE Sys_FindWindow(const char *className, const char *windowName); +void Sys_EnumWindows(void *callbackFunction, int lparam); +void Sys_GetWindowText(WHANDLE wnd, char *buffer, int bufferSize); +void Sys_PostMessage(WHANDLE wnd, unsigned int msg, unsigned int wParam, unsigned int lParam); +WHANDLE Sys_CreateWindowEx(const char *windowName); +void Sys_DestroyWindow(WHANDLE wnd); + +// mouse +void Sys_SetCursorPos(int x, int y); + + + + + + +#endif // SYS_UTILS_H diff --git a/gameui/TGAImagePanel.cpp b/gameui/TGAImagePanel.cpp new file mode 100644 index 0000000..927b401 --- /dev/null +++ b/gameui/TGAImagePanel.cpp @@ -0,0 +1,92 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "TGAImagePanel.h" +#include "bitmap/tgaloader.h" +#include "vgui/ISurface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +CTGAImagePanel::CTGAImagePanel( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) +{ + m_iTextureID = -1; + m_bHasValidTexture = false; + m_bLoadedTexture = false; + m_szTGAName[0] = 0; + + SetPaintBackgroundEnabled( false ); +} + +CTGAImagePanel::~CTGAImagePanel() +{ + // release the texture memory + if ( vgui::surface() && m_iTextureID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iTextureID ); + m_iTextureID = -1; + } +} + +void CTGAImagePanel::SetTGA( const char *filename ) +{ + Q_snprintf( m_szTGAName, sizeof(m_szTGAName), "//MOD/%s", filename ); +} + +void CTGAImagePanel::SetTGANonMod( const char *filename ) +{ + V_strcpy_safe( m_szTGAName, filename ); +} + +void CTGAImagePanel::Paint() +{ + if ( !m_bLoadedTexture ) + { + m_bLoadedTexture = true; + // get a texture id, if we haven't already + if ( m_iTextureID == -1 ) + { + m_iTextureID = vgui::surface()->CreateNewTextureID( true ); + SetSize( 180, 100 ); + } + + // load the file + CUtlMemory<unsigned char> tga; +#ifndef _XBOX + if ( TGALoader::LoadRGBA8888( m_szTGAName, tga, m_iImageWidth, m_iImageHeight ) ) + { + // set the textureID + surface()->DrawSetTextureRGBA( m_iTextureID, tga.Base(), m_iImageWidth, m_iImageHeight, false, true ); + m_bHasValidTexture = true; + // set our size to be the size of the tga + SetSize( m_iImageWidth, m_iImageHeight ); + } + else +#endif + { + m_bHasValidTexture = false; + } + } + + // draw the image + int wide, tall; + if ( m_bHasValidTexture ) + { + surface()->DrawGetTextureSize( m_iTextureID, wide, tall ); + surface()->DrawSetTexture( m_iTextureID ); + surface()->DrawSetColor( 255, 255, 255, 255 ); + surface()->DrawTexturedRect( 0, 0, wide, tall ); + } + else + { + // draw a black fill instead + wide = 180, tall = 100; + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, wide, tall ); + } +} diff --git a/gameui/TGAImagePanel.h b/gameui/TGAImagePanel.h new file mode 100644 index 0000000..1c8d36e --- /dev/null +++ b/gameui/TGAImagePanel.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef TGAIMAGEPANEL_H +#define TGAIMAGEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Panel.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Displays a tga image +//----------------------------------------------------------------------------- +class CTGAImagePanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CTGAImagePanel, vgui::Panel ); + +public: + CTGAImagePanel( vgui::Panel *parent, const char *name ); + + ~CTGAImagePanel(); + + void SetTGA( const char *filename ); + void SetTGANonMod( const char *filename ); + + virtual void Paint( void ); + +private: + int m_iTextureID; + int m_iImageWidth, m_iImageHeight; + bool m_bHasValidTexture, m_bLoadedTexture; + char m_szTGAName[256]; +}; + +#endif //TGAIMAGEPANEL_H diff --git a/gameui/TextEntryBox.cpp b/gameui/TextEntryBox.cpp new file mode 100644 index 0000000..a78b48c --- /dev/null +++ b/gameui/TextEntryBox.cpp @@ -0,0 +1,120 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Author: Matthew D. Campbell ([email protected]), 2003 + +#include <vgui/KeyCode.h> + +#include "CvarTextEntry.h" +#include "TextEntryBox.h" +#include <vgui_controls/TextEntry.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +using namespace vgui; + +//-------------------------------------------------------------------------------------------------------------- +CTextEntryBox::CTextEntryBox(const char *title, const char *queryText, const char *entryText, bool isCvar, vgui::Panel *parent) : QueryBox(title, queryText,parent) +{ + if (isCvar) + { + m_pEntry = m_pCvarEntry = new CCvarTextEntry( this, "TextEntry", entryText ); + } + else + { + m_pEntry = new TextEntry( this, "TextEntry" ); + m_pCvarEntry = NULL; + } + m_pEntry->SetTabPosition(3); + m_pEntry->RequestFocus(); + m_pEntry->GotoTextEnd(); +} + +//-------------------------------------------------------------------------------------------------------------- +CTextEntryBox::~CTextEntryBox() +{ + delete m_pEntry; +} + +//-------------------------------------------------------------------------------------------------------------- +void CTextEntryBox::ShowWindow(Frame *pFrameOver) +{ + BaseClass::ShowWindow( pFrameOver ); + + m_pEntry->RequestFocus(); + + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------------------------------- +void CTextEntryBox::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + wide += x; + tall += y; + + const int borderW = 10; + + int labelW, labelH; + int entryW, entryH; + m_pMessageLabel->GetSize( labelW, labelH ); + + entryW = max(120, wide - borderW - borderW - borderW - labelW); + entryH = max(24, labelH); + m_pEntry->SetSize( entryW, entryH ); + + int boxWidth, boxTall; + GetSize(boxWidth, boxTall); + if (boxWidth < labelW + entryW + borderW*3) + SetSize( labelW + entryW + borderW*3, boxTall ); + + m_pMessageLabel->GetPos( x, y ); + m_pMessageLabel->SetPos( borderW, y - (entryH - labelH)/2 ); + + m_pEntry->SetPos( borderW + m_pMessageLabel->GetWide() + borderW, y - (entryH - labelH) ); +} + +//-------------------------------------------------------------------------------------------------------------- +void CTextEntryBox::OnCommand(const char *command) +{ + if (!stricmp(command, "Ok")) + { + if (m_pCvarEntry) + { + m_pCvarEntry->ApplyChanges( true ); + } + } + + BaseClass::OnCommand(command); + +} + +//-------------------------------------------------------------------------------------------------------------- +void CTextEntryBox::OnKeyCodeTyped(KeyCode code) +{ + if (code == KEY_ESCAPE) + { + OnCommand("Cancel"); + } + if (code == KEY_ENTER) + { + OnCommand("Ok"); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } +} + diff --git a/gameui/TextEntryBox.h b/gameui/TextEntryBox.h new file mode 100644 index 0000000..71626e0 --- /dev/null +++ b/gameui/TextEntryBox.h @@ -0,0 +1,46 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Author: Matthew D. Campbell ([email protected]), 2003 + +#ifndef TEXTENTRYBOX_H +#define TEXTENTRYBOX_H +#ifdef _WIN32 +#pragma once +#endif + +#include "KeyValues.h" +#include <vgui_controls/QueryBox.h> + +class CCvarTextEntry; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Popup dialog with a text entry, extending the QueryBox, which extends the MessageBox + */ +class CTextEntryBox : public vgui::QueryBox +{ +public: + CTextEntryBox(const char *title, const char *labelText, const char *entryText, bool isCvar, vgui::Panel *parent = NULL); + + virtual ~CTextEntryBox(); + + virtual void PerformLayout(); ///< Layout override to position the label and text entry + virtual void ShowWindow(vgui::Frame *pFrameOver); ///< Show window override to give focus to text entry + +private: + typedef vgui::QueryBox BaseClass; + +protected: + CCvarTextEntry *m_pCvarEntry; + vgui::TextEntry *m_pEntry; + + virtual void OnKeyCodeTyped(vgui::KeyCode code); + void OnCommand( const char *command); ///< Handle button presses +}; + +#endif // CVARTEXTENTRYBOX_H diff --git a/gameui/URLButton.cpp b/gameui/URLButton.cpp new file mode 100644 index 0000000..7ef031f --- /dev/null +++ b/gameui/URLButton.cpp @@ -0,0 +1,622 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Basic button control +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdio.h> +#include <utlsymbol.h> + +#include <vgui/IBorder.h> +#include <vgui/IInput.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui/IVGui.h> +#include <vgui/MouseCode.h> +#include <vgui/KeyCode.h> +#include <KeyValues.h> + +#include "URLButton.h" +#include <vgui_controls/FocusNavGroup.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( URLButton, URLButton ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +URLButton::URLButton(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, text) +{ + Init(); + if ( pActionSignalTarget && pCmd ) + { + AddActionSignalTarget( pActionSignalTarget ); + SetCommand( pCmd ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +URLButton::URLButton(Panel *parent, const char *panelName, const wchar_t *wszText, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, wszText) +{ + Init(); + if ( pActionSignalTarget && pCmd ) + { + AddActionSignalTarget( pActionSignalTarget ); + SetCommand( pCmd ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::Init() +{ + _buttonFlags.SetFlag( USE_CAPTURE_MOUSE | BUTTON_BORDER_ENABLED ); + + _mouseClickMask = 0; + _actionMessage = NULL; + m_bSelectionStateSaved = false; + SetTextInset(0, 0); + SetMouseClickEnabled( MOUSE_LEFT, true ); + SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED); + + // labels have this off by default, but we need it on + SetPaintBackgroundEnabled( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +URLButton::~URLButton() +{ + if (_actionMessage) + { + _actionMessage->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::SetButtonActivationType(ActivationType_t activationType) +{ + _activationType = activationType; +} + +//----------------------------------------------------------------------------- +// Purpose: Set button border attribute enabled. +//----------------------------------------------------------------------------- +void URLButton::SetButtonBorderEnabled( bool state ) +{ + if ( state != _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) ) + { + _buttonFlags.SetFlag( BUTTON_BORDER_ENABLED, state ); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button selected state. +//----------------------------------------------------------------------------- +void URLButton::SetSelected( bool state ) +{ + if ( _buttonFlags.IsFlagSet( SELECTED ) != state ) + { + _buttonFlags.SetFlag( SELECTED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Set button force depressed state. +//----------------------------------------------------------------------------- +void URLButton::ForceDepressed(bool state) +{ + if ( _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) != state ) + { + _buttonFlags.SetFlag( FORCE_DEPRESSED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button depressed state with respect to the force depressed state. +//----------------------------------------------------------------------------- +void URLButton::RecalculateDepressedState( void ) +{ + bool newState; + if (!IsEnabled()) + { + newState = false; + } + else + { + newState = _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ? true : (_buttonFlags.IsFlagSet(ARMED) && _buttonFlags.IsFlagSet( SELECTED ) ); + } + + _buttonFlags.SetFlag( DEPRESSED, newState ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether or not the button captures all mouse input when depressed +// Defaults to true +// Should be set to false for things like menu items where there is a higher-level mouse capture +//----------------------------------------------------------------------------- +void URLButton::SetUseCaptureMouse( bool state ) +{ + _buttonFlags.SetFlag( USE_CAPTURE_MOUSE, state ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if mouse capture is enabled. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool URLButton::IsUseCaptureMouseEnabled( void ) +{ + return _buttonFlags.IsFlagSet( USE_CAPTURE_MOUSE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set armed state. +//----------------------------------------------------------------------------- +void URLButton::SetArmed(bool state) +{ + if ( _buttonFlags.IsFlagSet( ARMED ) != state ) + { + _buttonFlags.SetFlag( ARMED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check armed state +//----------------------------------------------------------------------------- +bool URLButton::IsArmed() +{ + return _buttonFlags.IsFlagSet( ARMED ); +} + + +KeyValues *URLButton::GetActionMessage() +{ + return _actionMessage->MakeCopy(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Activate a button click. +//----------------------------------------------------------------------------- +void URLButton::DoClick() +{ + SetSelected(true); + FireActionSignal(); + SetSelected(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Check selected state +//----------------------------------------------------------------------------- +bool URLButton::IsSelected() +{ + return _buttonFlags.IsFlagSet( SELECTED ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check depressed state +//----------------------------------------------------------------------------- +bool URLButton::IsDepressed() +{ + return _buttonFlags.IsFlagSet( DEPRESSED ); +} + + +//----------------------------------------------------------------------------- +// Drawing focus box? +//----------------------------------------------------------------------------- +bool URLButton::IsDrawingFocusBox() +{ + return _buttonFlags.IsFlagSet( DRAW_FOCUS_BOX ); +} + +void URLButton::DrawFocusBox( bool bEnable ) +{ + _buttonFlags.SetFlag( DRAW_FOCUS_BOX, bEnable ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Paint button on screen +//----------------------------------------------------------------------------- +void URLButton::Paint(void) +{ + BaseClass::Paint(); + + int x, y; + int controlWidth, controlHeight, textWidth, textHeight; + GetSize(controlWidth, controlHeight); + GetContentSize(textWidth, textHeight); + x = textWidth; + y = controlHeight - 4; + + surface()->DrawSetColor(GetButtonFgColor()); + surface()->DrawLine(0, y, x, y); +} + +//----------------------------------------------------------------------------- +// Purpose: Perform graphical layout of button. +//----------------------------------------------------------------------------- +void URLButton::PerformLayout() +{ + // set our color + SetFgColor(GetButtonFgColor()); + SetBgColor(GetButtonBgColor()); + + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get button foreground color +// Output : Color +//----------------------------------------------------------------------------- +Color URLButton::GetButtonFgColor() +{ + return _defaultFgColor; +} + +//----------------------------------------------------------------------------- +// Purpose: Get button background color +//----------------------------------------------------------------------------- +Color URLButton::GetButtonBgColor() +{ + return _defaultBgColor; +} + +//----------------------------------------------------------------------------- +// Purpose: Called when key focus is received +//----------------------------------------------------------------------------- +void URLButton::OnSetFocus() +{ + InvalidateLayout(false); + BaseClass::OnSetFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Respond when focus is killed +//----------------------------------------------------------------------------- +void URLButton::OnKillFocus() +{ + InvalidateLayout(false); + BaseClass::OnKillFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + _defaultFgColor = GetSchemeColor("Button.TextColor", Color(255, 255, 255, 255), pScheme); + _defaultBgColor = GetSchemeColor("Button.BgColor", Color(0, 0, 0, 255), pScheme); + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Set button to be mouse clickable or not. +//----------------------------------------------------------------------------- +void URLButton::SetMouseClickEnabled(MouseCode code,bool state) +{ + if(state) + { + //set bit to 1 + _mouseClickMask|=1<<((int)(code+1)); + } + else + { + //set bit to 0 + _mouseClickMask&=~(1<<((int)(code+1))); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the command to send when the button is pressed +//----------------------------------------------------------------------------- +void URLButton::SetCommand( const char *command ) +{ + SetCommand(new KeyValues("Command", "command", command)); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the message to send when the button is pressed +//----------------------------------------------------------------------------- +void URLButton::SetCommand( KeyValues *message ) +{ + // delete the old message + if (_actionMessage) + { + _actionMessage->deleteThis(); + } + + _actionMessage = message; +} + +//----------------------------------------------------------------------------- +// Purpose: Peeks at the message to send when button is pressed +// Input : - +// Output : KeyValues +//----------------------------------------------------------------------------- +KeyValues *URLButton::GetCommand() +{ + return _actionMessage; +} + +//----------------------------------------------------------------------------- +// Purpose: Message targets that the button has been pressed +//----------------------------------------------------------------------------- +void URLButton::FireActionSignal() +{ + // message-based action signal + if (_actionMessage) + { + // see if it's a url + if (!stricmp(_actionMessage->GetName(), "command") + && !strnicmp(_actionMessage->GetString("command", ""), "url ", strlen("url ")) + && strstr(_actionMessage->GetString("command", ""), "://")) + { + // it's a command to launch a url, run it + system()->ShellExecute("open", _actionMessage->GetString("command", " ") + 4); + } + PostActionSignal(_actionMessage->MakeCopy()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets info about the button +//----------------------------------------------------------------------------- +bool URLButton::RequestInfo(KeyValues *outputData) +{ + if (!stricmp(outputData->GetName(), "GetState")) + { + outputData->SetInt("state", IsSelected()); + return true; + } + else if ( !stricmp( outputData->GetName(), "GetCommand" )) + { + if ( _actionMessage ) + { + outputData->SetString( "command", _actionMessage->GetString( "command", "" ) ); + } + else + { + outputData->SetString( "command", "" ); + } + return true; + } + + + return BaseClass::RequestInfo(outputData); +} + +//----------------------------------------------------------------------------- +// Purpose: Get control settings for editing +//----------------------------------------------------------------------------- +void URLButton::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings(outResourceData); + + if (_actionMessage) + { + outResourceData->SetString("command", _actionMessage->GetString("command", "")); + } + outResourceData->SetInt("default", _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) ); + if ( m_bSelectionStateSaved ) + { + outResourceData->SetInt( "selected", IsSelected() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings(inResourceData); + + const char *cmd = inResourceData->GetString("command", ""); + if (*cmd) + { + // add in the command + SetCommand(cmd); + } + + // saved selection state + int iSelected = inResourceData->GetInt( "selected", -1 ); + if ( iSelected != -1 ) + { + SetSelected( iSelected != 0 ); + m_bSelectionStateSaved = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Describes editing details +//----------------------------------------------------------------------------- +const char *URLButton::GetDescription( void ) +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string command, int default", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnSetState(int state) +{ + SetSelected((bool)state); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnCursorEntered() +{ + if (IsEnabled()) + { + SetArmed(true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnCursorExited() +{ + if ( !_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) + { + SetArmed(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnMousePressed(MouseCode code) +{ + if (!IsEnabled()) + return; + + if (_activationType == ACTIVATE_ONPRESSED) + { + if ( IsKeyBoardInputEnabled() ) + { + RequestFocus(); + } + DoClick(); + return; + } + + + if (IsUseCaptureMouseEnabled() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) + { + { + if ( IsKeyBoardInputEnabled() ) + { + RequestFocus(); + } + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnMouseDoublePressed(MouseCode code) +{ + OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnMouseReleased(MouseCode code) +{ + // ensure mouse capture gets released + if (IsUseCaptureMouseEnabled()) + { + input()->SetMouseCapture(NULL); + } + + if (_activationType == ACTIVATE_ONPRESSED) + return; + + + if (!IsSelected() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) + return; + + // it has to be both enabled and (mouse over the button or using a key) to fire + if ( IsEnabled() && ( GetVPanel() == input()->GetMouseOver() || _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) ) + { + DoClick(); + } + else + { + SetSelected(false); + } + + // make sure the button gets unselected + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnKeyCodePressed(KeyCode code) +{ + if (code == KEY_SPACE || code == KEY_ENTER) + { + SetArmed(true); + _buttonFlags.SetFlag( BUTTON_KEY_DOWN ); + OnMousePressed(MOUSE_LEFT); + if (IsUseCaptureMouseEnabled()) // undo the mouse capture since its a fake mouse click! + { + input()->SetMouseCapture(NULL); + } + } + else + { + _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void URLButton::OnKeyCodeReleased(KeyCode code) +{ + if (_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && (code == KEY_SPACE || code == KEY_ENTER)) + { + SetArmed(true); + OnMouseReleased(MOUSE_LEFT); + } + else + { + BaseClass::OnKeyCodeReleased(code); + } + _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); + SetArmed(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Size the object to its button and text. - only works from in ApplySchemeSettings or PerformLayout() +//----------------------------------------------------------------------------- +void URLButton::SizeToContents() +{ + int wide, tall; + GetContentSize(wide, tall); + SetSize(wide + Label::Content, tall + Label::Content); +} + diff --git a/gameui/URLButton.h b/gameui/URLButton.h new file mode 100644 index 0000000..32f88a1 --- /dev/null +++ b/gameui/URLButton.h @@ -0,0 +1,171 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef URLBUTTON_H +#define URLBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui/Dar.h> +#include <Color.h> +#include <vgui_controls/Label.h> +#include "vgui/MouseCode.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: A control that looks like a hyperlink, but behaves like a button. +//----------------------------------------------------------------------------- +class URLButton : public Label +{ + DECLARE_CLASS_SIMPLE( URLButton, Label ); + +public: + // You can optionally pass in the panel to send the click message to and the name of the command to send to that panel. + URLButton(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget=NULL, const char *pCmd=NULL); + URLButton(Panel *parent, const char *panelName, const wchar_t *text, Panel *pActionSignalTarget=NULL, const char *pCmd=NULL); + ~URLButton(); +private: + void Init(); +public: + // Set armed state. + virtual void SetArmed(bool state); + // Check armed state + virtual bool IsArmed( void ); + + // Check depressed state + virtual bool IsDepressed(); + // Set button force depressed state. + virtual void ForceDepressed(bool state); + // Set button depressed state with respect to the force depressed state. + virtual void RecalculateDepressedState( void ); + + // Set button selected state. + virtual void SetSelected(bool state); + // Check selected state + virtual bool IsSelected( void ); + + //Set whether or not the button captures all mouse input when depressed. + virtual void SetUseCaptureMouse( bool state ); + // Check if mouse capture is enabled. + virtual bool IsUseCaptureMouseEnabled( void ); + + // Activate a button click. + MESSAGE_FUNC( DoClick, "PressButton" ); + MESSAGE_FUNC( OnHotkey, "Hotkey" ) + { + DoClick(); + } + + // Set button to be mouse clickable or not. + virtual void SetMouseClickEnabled( MouseCode code, bool state ); + + // sets the how this button activates + enum ActivationType_t + { + ACTIVATE_ONPRESSEDANDRELEASED, // normal button behaviour + ACTIVATE_ONPRESSED, // menu buttons, toggle buttons + ACTIVATE_ONRELEASED, // menu items + }; + virtual void SetButtonActivationType(ActivationType_t activationType); + + // Message targets that the button has been pressed + virtual void FireActionSignal( void ); + // Perform graphical layout of button + virtual void PerformLayout(); + + virtual bool RequestInfo(KeyValues *data); + + // Respond when key focus is received + virtual void OnSetFocus(); + // Respond when focus is killed + virtual void OnKillFocus(); + + // Set button border attribute enabled, controls display of button. + virtual void SetButtonBorderEnabled( bool state ); + + // Get button foreground color + virtual Color GetButtonFgColor(); + // Get button background color + virtual Color GetButtonBgColor(); + + // Set the command to send when the button is pressed + // Set the panel to send the command to with AddActionSignalTarget() + virtual void SetCommand( const char *command ); + // Set the message to send when the button is pressed + virtual void SetCommand( KeyValues *message ); + + /* CUSTOM MESSAGE HANDLING + "PressButton" - makes the button act as if it had just been pressed by the user (just like DoClick()) + input: none + */ + + virtual void OnCursorEntered(); + virtual void OnCursorExited(); + virtual void SizeToContents(); + + virtual KeyValues *GetCommand(); + + bool IsDrawingFocusBox(); + void DrawFocusBox( bool bEnable ); + +protected: + + // Paint button on screen + virtual void Paint(void); + // Get button border attributes. + + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_INT( OnSetState, "SetState", state ); + + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnKeyCodePressed(KeyCode code); + virtual void OnKeyCodeReleased(KeyCode code); + + // Get control settings for editing + virtual void GetSettings( KeyValues *outResourceData ); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual const char *GetDescription( void ); + + KeyValues *GetActionMessage(); + +private: + enum ButtonFlags_t + { + ARMED = 0x0001, + DEPRESSED = 0x0002, + FORCE_DEPRESSED = 0x0004, + BUTTON_BORDER_ENABLED = 0x0008, + USE_CAPTURE_MOUSE = 0x0010, + BUTTON_KEY_DOWN = 0x0020, + DEFAULT_BUTTON = 0x0040, + SELECTED = 0x0080, + DRAW_FOCUS_BOX = 0x0100, + BLINK = 0x0200, + ALL_FLAGS = 0xFFFF, + }; + + CUtlFlags< unsigned short > _buttonFlags; // see ButtonFlags_t + int _mouseClickMask; + KeyValues *_actionMessage; + ActivationType_t _activationType; + + + Color _defaultFgColor, _defaultBgColor; + + bool m_bSelectionStateSaved; +}; + +} // namespace vgui + +#endif // URLBUTTON_H diff --git a/gameui/VGuiSystemModuleLoader.cpp b/gameui/VGuiSystemModuleLoader.cpp new file mode 100644 index 0000000..8078100 --- /dev/null +++ b/gameui/VGuiSystemModuleLoader.cpp @@ -0,0 +1,384 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include <stdio.h> + +#include "VGuiSystemModuleLoader.h" +#include "Sys_Utils.h" +#include "IVguiModule.h" +#include "ServerBrowser/IServerBrowser.h" + +#include <vgui/IPanel.h> +#include <vgui/ISystem.h> +#include <vgui/IVGui.h> +#include <vgui/ILocalize.h> +#include <KeyValues.h> + +#include <vgui_controls/Controls.h> +#include <vgui_controls/Panel.h> + +#include "filesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// instance of class +CVGuiSystemModuleLoader g_VModuleLoader; + +#ifdef GAMEUI_EXPORTS +extern vgui::VPANEL GetGameUIBasePanel(); +#else +#include "../SteamUI/PlatformMainPanel.h" +extern CPlatformMainPanel *g_pMainPanel; +#endif + +bool bSteamCommunityFriendsVersion = false; + +#include <tier0/dbg.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CVGuiSystemModuleLoader, IVGuiModuleLoader, VGUIMODULELOADER_INTERFACE_VERSION, g_VModuleLoader); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CVGuiSystemModuleLoader::CVGuiSystemModuleLoader() +{ + m_bModulesInitialized = false; + m_bPlatformShouldRestartAfterExit = false; + m_pPlatformModuleData = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CVGuiSystemModuleLoader::~CVGuiSystemModuleLoader() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the module loader has acquired the platform mutex and loaded the modules +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::IsPlatformReady() +{ + return m_bModulesInitialized; +} + +//----------------------------------------------------------------------------- +// Purpose: sets up all the modules for use +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::InitializeAllModules(CreateInterfaceFn *factorylist, int factorycount) +{ + if ( IsX360() ) + { + // not valid for 360 + return false; + } + + bool bSuccess = true; + + // Init vgui in the modules + int i; + for ( i = 0; i < m_Modules.Count(); i++ ) + { + if (!m_Modules[i].moduleInterface->Initialize(factorylist, factorycount)) + { + bSuccess = false; + Error("Platform Error: module failed to initialize\n"); + } + } + + // create a table of all the loaded modules + CreateInterfaceFn *moduleFactories = (CreateInterfaceFn *)_alloca(sizeof(CreateInterfaceFn) * m_Modules.Count()); + for ( i = 0; i < m_Modules.Count(); i++ ) + { + moduleFactories[i] = Sys_GetFactory(m_Modules[i].module); + } + + // give the modules a chance to link themselves together + for (i = 0; i < m_Modules.Count(); i++) + { + if (!m_Modules[i].moduleInterface->PostInitialize(moduleFactories, m_Modules.Count())) + { + bSuccess = false; + Error("Platform Error: module failed to initialize\n"); + } + +#ifdef GAMEUI_EXPORTS + m_Modules[i].moduleInterface->SetParent(GetGameUIBasePanel()); +#else + m_Modules[i].moduleInterface->SetParent(g_pMainPanel->GetVPanel()); +#endif + } + + m_bModulesInitialized = true; + return bSuccess; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads and initializes all the modules specified in the platform file +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::LoadPlatformModules(CreateInterfaceFn *factorylist, int factorycount, bool useSteamModules) +{ + if ( IsX360() ) + { + // not valid for 360 + return false; + } + + bool bSuccess = true; + + // load platform menu + KeyValues *kv = new KeyValues("Platform"); + if (!kv->LoadFromFile(g_pFullFileSystem, "steam/games/PlatformMenu.vdf", "PLATFORM")) + { + kv->deleteThis(); + return false; + } + + // walk the platform menu loading all the interfaces + KeyValues *menuKeys = kv->FindKey("Menu", true); + for (KeyValues *it = menuKeys->GetFirstSubKey(); it != NULL; it = it->GetNextKey()) + { + // see if we should skip steam modules + if (!useSteamModules && it->GetInt("SteamApp")) + continue; + + const char *pchInterface = it->GetString("interface"); + + // don't load friends if we are using Steam Community + if ( !Q_stricmp( pchInterface, "VGuiModuleTracker001" ) && bSteamCommunityFriendsVersion ) + continue; + + // get copy out of steam cache + const char *dllPath = NULL; + if ( IsOSX() ) + { + dllPath = it->GetString("dll_osx"); + } + else if ( IsLinux() ) + { + dllPath = it->GetString("dll_linux"); + } + else + { + dllPath = it->GetString("dll"); + } + + + // load the module (LoadModule calls GetLocalCopy() under steam) + CSysModule *mod = g_pFullFileSystem->LoadModule(dllPath, "EXECUTABLE_PATH"); + if (!mod) + { + Error("Platform Error: bad module '%s', not loading\n", it->GetString("dll")); + bSuccess = false; + continue; + } + + // make sure we get the right version + IVGuiModule *moduleInterface = (IVGuiModule *)Sys_GetFactory(mod)(pchInterface, NULL); + if (!moduleInterface) + { + Warning("Platform Error: module version ('%s, %s) invalid, not loading\n", it->GetString("dll"), it->GetString("interface")); + bSuccess = false; + continue; + } + + // store off the module + int newIndex = m_Modules.AddToTail(); + m_Modules[newIndex].module = mod; + m_Modules[newIndex].moduleInterface = moduleInterface; + m_Modules[newIndex].data = it; + } + + m_pPlatformModuleData = kv; + return InitializeAllModules(factorylist, factorycount) && bSuccess; +} + +//----------------------------------------------------------------------------- +// Purpose: gives all platform modules a chance to Shutdown gracefully +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::ShutdownPlatformModules() +{ + if ( IsX360() ) + { + // not valid for 360 + return; + } + + // static include guard to prevent recursive calls + static bool runningFunction = false; + if (runningFunction) + return; + + runningFunction = true; + + // deactivate all the modules first + DeactivatePlatformModules(); + + // give all the modules notice of quit + int i; + for ( i = 0; i < m_Modules.Count(); i++ ) + { + vgui::ivgui()->PostMessage(m_Modules[i].moduleInterface->GetPanel(), new KeyValues("Command", "command", "Quit"), NULL); + } + + for ( i = 0; i < m_Modules.Count(); i++ ) + { + m_Modules[i].moduleInterface->Shutdown(); + } + + runningFunction = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Deactivates all the modules (puts them into in inactive but recoverable state) +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::DeactivatePlatformModules() +{ + for (int i = 0; i < m_Modules.Count(); i++) + { + m_Modules[i].moduleInterface->Deactivate(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Reenables all the deactivated platform modules +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::ReactivatePlatformModules() +{ + for (int i = 0; i < m_Modules.Count(); i++) + { + m_Modules[i].moduleInterface->Reactivate(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Disables and unloads platform +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::UnloadPlatformModules() +{ + for (int i = 0; i < m_Modules.Count(); i++) + { + g_pFullFileSystem->UnloadModule(m_Modules[i].module); + } + + m_Modules.RemoveAll(); + + if (m_pPlatformModuleData) + { + m_pPlatformModuleData->deleteThis(); + m_pPlatformModuleData = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::RunFrame() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: returns number of modules loaded +//----------------------------------------------------------------------------- +int CVGuiSystemModuleLoader::GetModuleCount() +{ + return m_Modules.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the string menu name (unlocalized) of a module +// moduleIndex is of the range [0, GetModuleCount()) +//----------------------------------------------------------------------------- +const char *CVGuiSystemModuleLoader::GetModuleLabel(int moduleIndex) +{ + return m_Modules[moduleIndex].data->GetString("MenuName", "< unknown >"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::IsModuleVisible(int moduleIndex) +{ + return vgui::ipanel()->IsVisible( m_Modules[moduleIndex].moduleInterface->GetPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: brings the specified module to the foreground +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::IsModuleHidden(int moduleIndex) +{ + return m_Modules[moduleIndex].data->GetInt("Hidden", 0); +} + +//----------------------------------------------------------------------------- +// Purpose: brings the specified module to the foreground +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::ActivateModule(int moduleIndex) +{ + if (!m_Modules.IsValidIndex(moduleIndex)) + return false; + + m_Modules[moduleIndex].moduleInterface->Activate(); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: activates a module by name +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::ActivateModule(const char *moduleName) +{ + for (int i = 0; i < GetModuleCount(); i++) + { + if (!stricmp(GetModuleLabel(i), moduleName) || !stricmp(m_Modules[i].data->GetName(), moduleName)) + { + ActivateModule(i); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a modules interface factory +//----------------------------------------------------------------------------- +CreateInterfaceFn CVGuiSystemModuleLoader::GetModuleFactory(int moduleIndex) +{ + return Sys_GetFactory(m_Modules[moduleIndex].module); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::PostMessageToAllModules(KeyValues *message) +{ + for (int i = 0; i < m_Modules.Count(); i++) + { + vgui::ivgui()->PostMessage(m_Modules[i].moduleInterface->GetPanel(), message->MakeCopy(), NULL); + } + message->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the the platform should update and restart when it quits +//----------------------------------------------------------------------------- +void CVGuiSystemModuleLoader::SetPlatformToRestart() +{ + m_bPlatformShouldRestartAfterExit = true; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CVGuiSystemModuleLoader::ShouldPlatformRestart() +{ + return m_bPlatformShouldRestartAfterExit; +} diff --git a/gameui/VGuiSystemModuleLoader.h b/gameui/VGuiSystemModuleLoader.h new file mode 100644 index 0000000..76493db --- /dev/null +++ b/gameui/VGuiSystemModuleLoader.h @@ -0,0 +1,97 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Handles loading/unloading of different vgui modules into a shared context +// +//=============================================================================// + +#ifndef VGUISYSTEMMODULELOADER_H +#define VGUISYSTEMMODULELOADER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/PHandle.h" +#include "utlvector.h" +#include "IVGuiModuleLoader.h" + +class IVGuiModule; + +class KeyValues; + +//----------------------------------------------------------------------------- +// Purpose: Handles loading/unloading of different vgui modules into a shared context +//----------------------------------------------------------------------------- +class CVGuiSystemModuleLoader : public IVGuiModuleLoader +{ +public: + CVGuiSystemModuleLoader(); + ~CVGuiSystemModuleLoader(); + + // loads all the modules in the platform + bool LoadPlatformModules(CreateInterfaceFn *factorylist, int factorycount, bool useSteamModules); + + // returns true if the module loader has loaded the modules + bool IsPlatformReady(); + + // needs to be called every frame - updates all the modules states + void RunFrame(); + + // returns number of modules loaded + int GetModuleCount(); + + // returns the string menu name (unlocalized) of a module + // moduleIndex is of the range [0, GetModuleCount()) + const char *GetModuleLabel(int moduleIndex); + + bool IsModuleHidden(int moduleIndex); + bool IsModuleVisible(int moduleIndex); + + // returns a modules interface factory + CreateInterfaceFn GetModuleFactory(int moduleIndex); + + // brings the specified module to the foreground + bool ActivateModule(int moduleIndex); + bool ActivateModule(const char *moduleName); + + // Deactivates all the modules (puts them into in inactive but recoverable state) + void DeactivatePlatformModules(); + + // Reenables all the deactivated platform modules + void ReactivatePlatformModules(); + + // shuts down all the platform modules + void ShutdownPlatformModules(); + + // unload all active platform modules/dlls from memory + void UnloadPlatformModules(); + + // posts a message to all active modules + void PostMessageToAllModules(KeyValues *message); + + // sets the the platform should update and restart when it quits + void SetPlatformToRestart(); + + // returns true if the platform should restart after exit + bool ShouldPlatformRestart(); + +private: + // sets up all the modules for use + bool InitializeAllModules(CreateInterfaceFn *factorylist, int factorycount); + + bool m_bModulesInitialized; + bool m_bPlatformShouldRestartAfterExit; + + struct module_t + { + CSysModule *module; + IVGuiModule *moduleInterface; + KeyValues *data; + }; + + CUtlVector<module_t> m_Modules; + KeyValues *m_pPlatformModuleData; +}; + +extern CVGuiSystemModuleLoader g_VModuleLoader; + +#endif // VGUISYSTEMMODULELOADER_H diff --git a/gameui/gameui_util.cpp b/gameui/gameui_util.cpp new file mode 100644 index 0000000..0339460 --- /dev/null +++ b/gameui/gameui_util.cpp @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// + +#include <stdarg.h> +#include "gameui_util.h" +#include "strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Performs a var args printf into a static return buffer +// Input : *format - +// ... - +// Output : char +//----------------------------------------------------------------------------- +char *VarArgs( const char *format, ... ) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); + Q_vsnprintf (string, sizeof( string ), format,argptr); + va_end (argptr); + + return string; +} diff --git a/gameui/gameui_util.h b/gameui/gameui_util.h new file mode 100644 index 0000000..482d3fc --- /dev/null +++ b/gameui/gameui_util.h @@ -0,0 +1,15 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef GAMEUI_UTIL_H +#define GAMEUI_UTIL_H +#ifdef _WIN32 +#pragma once +#endif + +char *VarArgs( char *format, ... ); + +#endif // GAMEUI_UTIL_H diff --git a/gameui/matchmaking/achievementsdialog.cpp b/gameui/matchmaking/achievementsdialog.cpp new file mode 100644 index 0000000..82931b2 --- /dev/null +++ b/gameui/matchmaking/achievementsdialog.cpp @@ -0,0 +1,1075 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Display a list of achievements for the current game +// +//=============================================================================// + +#include "achievementsdialog.h" +#include "vgui_controls/Button.h" +#include "vgui/ILocalize.h" +#include "ixboxsystem.h" +#include "iachievementmgr.h" +#include "EngineInterface.h" +#include "GameUI_Interface.h" +#include "MouseMessageForwardingPanel.h" +#include "filesystem.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/ScrollBar.h" +#include "BasePanel.h" +#include "fmtstr.h" +#include "UtlSortVector.h" + +using namespace vgui; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +KeyValues *g_pPreloadedAchievementItemLayout = NULL; + +#define NUM_COMBO_BOX_LINES_DEFAULT 10 +#define NUM_COMBO_BOX_LINES_MAX 16 + +// Shared helper functions so xbox and pc can share as much code as possible while coming from different bases. + +//----------------------------------------------------------------------------- +// Purpose: Sets the parameter pIconPanel to display the specified achievement's icon file. +//----------------------------------------------------------------------------- +bool LoadAchievementIcon( vgui::ImagePanel* pIconPanel, IAchievement *pAchievement, const char *pszExt /*= NULL*/ ) +{ + char imagePath[_MAX_PATH]; + Q_strncpy( imagePath, "achievements\\", sizeof(imagePath) ); + Q_strncat( imagePath, pAchievement->GetName(), sizeof(imagePath), COPY_ALL_CHARACTERS ); + if ( pszExt ) + { + Q_strncat( imagePath, pszExt, sizeof(imagePath), COPY_ALL_CHARACTERS ); + } + Q_strncat( imagePath, ".vtf", sizeof(imagePath), COPY_ALL_CHARACTERS ); + + char checkFile[_MAX_PATH]; + Q_snprintf( checkFile, sizeof(checkFile), "materials\\vgui\\%s", imagePath ); + if ( !g_pFullFileSystem->FileExists( checkFile ) ) + { + Q_snprintf( imagePath, sizeof(imagePath), "hud\\icon_locked.vtf" ); + } + + pIconPanel->SetShouldScaleImage( true ); + pIconPanel->SetImage( imagePath ); + pIconPanel->SetVisible( true ); + + return pIconPanel->IsVisible(); +} + +//----------------------------------------------------------------------------- +// The bias is to ensure the percentage bar gets plenty orange before it reaches the text, +// as the white-on-grey is hard to read. +//----------------------------------------------------------------------------- +Color LerpColors ( Color cStart, Color cEnd, float flPercent ) +{ + float r = (float)((float)(cStart.r()) + (float)(cEnd.r() - cStart.r()) * Bias( flPercent, 0.75 ) ); + float g = (float)((float)(cStart.g()) + (float)(cEnd.g() - cStart.g()) * Bias( flPercent, 0.75 ) ); + float b = (float)((float)(cStart.b()) + (float)(cEnd.b() - cStart.b()) * Bias( flPercent, 0.75 ) ); + float a = (float)((float)(cStart.a()) + (float)(cEnd.a() - cStart.a()) * Bias( flPercent, 0.75 ) ); + return Color( r, g, b, a ); +} + +//----------------------------------------------------------------------------- +// Purpose: Shares common percentage bar calculations/color settings between xbox and pc. +// Not really intended for robustness or reuse across many panels. +// Input : pFrame - assumed to have certain child panels (see below) +// *pAchievement - source achievement to poll for progress. Non progress achievements will not show a percentage bar. +//----------------------------------------------------------------------------- +void UpdateProgressBar( vgui::EditablePanel* pPanel, IAchievement *pAchievement, Color clrProgressBar ) +{ + if ( pAchievement->GetGoal() > 1 ) + { + bool bShowProgress = true; + + // if this achievement gets saved with game and we're not in a level and have not achieved it, then we do not have any state + // for this achievement, don't show progress + if ( ( pAchievement->GetFlags() & ACH_SAVE_WITH_GAME ) && !GameUI().IsInLevel() && !pAchievement->IsAchieved() ) + { + bShowProgress = false; + } + + float flCompletion = 0.0f; + + // Once achieved, we can't rely on count. If they've completed the achievement just set to 100%. + int iCount = pAchievement->GetCount(); + if ( pAchievement->IsAchieved() ) + { + flCompletion = 1.0f; + iCount = pAchievement->GetGoal(); + } + else if ( bShowProgress ) + { + flCompletion = ( ((float)pAchievement->GetCount()) / ((float)pAchievement->GetGoal()) ); + // In rare cases count can exceed goal and not be achieved (switch local storage on X360, take saved game from different user on PC). + // These will self-correct with continued play, but if we're in that state don't show more than 100% achieved. + flCompletion = min( flCompletion, 1.f ); + } + + char szPercentageText[ 256 ] = ""; + if ( bShowProgress ) + { + Q_snprintf( szPercentageText, 256, "%d/%d", iCount, pAchievement->GetGoal() ); + } + + pPanel->SetControlString( "PercentageText", szPercentageText ); + pPanel->SetControlVisible( "PercentageText", true ); + pPanel->SetControlVisible( "CompletionText", true ); + + vgui::ImagePanel *pPercentageBar = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBar" ); + vgui::ImagePanel *pPercentageBarBkg = (vgui::ImagePanel*)pPanel->FindChildByName( "PercentageBarBackground" ); + + if ( pPercentageBar && pPercentageBarBkg ) + { + pPercentageBar->SetFillColor( clrProgressBar ); + pPercentageBar->SetWide( pPercentageBarBkg->GetWide() * flCompletion ); + + pPanel->SetControlVisible( "PercentageBarBackground", IsX360() ? bShowProgress : true ); + pPanel->SetControlVisible( "PercentageBar", true ); + } + } +} + +// TODO: revisit this once other games are rebuilt using the updated IAchievement interface +bool GameSupportsAchievementTracker() +{ + const char *pGame = Q_UnqualifiedFileName( engine->GetGameDirectory() ); + if ( ( Q_stricmp( pGame, "tf" ) == 0 ) || ( Q_stricmp( pGame, "tf_beta" ) == 0 ) ) + return true; + + return false; +} + + +//---------------------------------------------------------- +// CAchievementsDialog_XBox +//---------------------------------------------------------- + +CAchievementsDialog_XBox::CAchievementsDialog_XBox( vgui::Panel *pParent ) : BaseClass( pParent, "AchievementsDialog" ) +{ + m_nTotalAchievements = 0; + m_nUnlocked = 0; + m_iSelection = 0; + m_iScroll = 0; + m_iNumItems = 0; + m_bCenterOnScreen = true; + + m_pProgressBg = new vgui::Panel( this, "ProgressBg" ); + m_pProgressBar = new vgui::Panel( this, "ProgressBar" ); + m_pProgressPercent = new vgui::Label( this, "ProgressPercent", "" ); + m_pNumbering = new vgui::Label( this, "Numbering", "" ); + m_pUpArrow = new vgui::Label( this, "UpArrow", "" ); + m_pDownArrow = new vgui::Label( this, "DownArrow", "" ); + + SetDeleteSelfOnClose(true); + SetSizeable( false ); + + m_pFooter = new CFooterPanel( pParent, "AchievementsFooter" ); +} + +CAchievementsDialog_XBox::~CAchievementsDialog_XBox() +{ + delete m_pProgressBg; + delete m_pProgressBar; + delete m_pProgressPercent; + delete m_pNumbering; + delete m_pUpArrow; + delete m_pDownArrow; + + delete m_pFooter; +} + +//---------------------------------------------------------- +// Position the dialogs elements +//---------------------------------------------------------- +void CAchievementsDialog_XBox::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + // Avoid division by zero if the achievements haven't been downloaded yet + if ( m_nTotalAchievements ) + { + int x, y, wide, tall; + m_pProgressBg->GetBounds( x, y, wide, tall ); + int iBarX = GetWide() - wide - m_nBorderWidth; + int iBarWide = wide * m_nUnlocked / m_nTotalAchievements; + m_pProgressBg->SetBounds( iBarX, y, wide, tall ); + m_pProgressBar->SetBounds( iBarX, y, iBarWide, tall ); + + wchar_t wszProgressText[64]; +#ifdef WIN32 + V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %s", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) ); +#else + V_snwprintf( wszProgressText, ARRAYSIZE(wszProgressText), L"%d%% %S", (m_nUnlocked * 100) / m_nTotalAchievements, g_pVGuiLocalize->Find( "#GameUI_Achievement_Unlocked" ) ); +#endif + m_pProgressPercent->SetText( wszProgressText ); + m_pProgressPercent->SizeToContents(); + m_pProgressPercent->SetPos( GetWide() - m_pProgressPercent->GetWide() - m_nBorderWidth, y + tall + 3 ); + + int menux, menuy; + m_Menu.GetPos( menux, menuy ); + + // Do a perform layout on the menu so we get the correct height now + m_Menu.InvalidateLayout( true, false ); + + int iBottomOfTable = menuy + m_Menu.GetTall() + 3; + + m_pNumbering->SizeToContents(); + m_pNumbering->SetPos( m_nBorderWidth, iBottomOfTable ); + + m_pUpArrow->GetPos( x, y ); + m_pUpArrow->SetPos( x, iBottomOfTable ); + m_pDownArrow->GetPos( x, y ); + m_pDownArrow->SetPos( x, iBottomOfTable ); + + wchar_t wszNumbering[64]; + wchar_t *wzNumberingFmt = g_pVGuiLocalize->Find( "#GameUI_Achievement_Menu_Range" ); + wchar_t wzActiveItem[8]; + wchar_t wzTotal[8]; + V_snwprintf( wzActiveItem, ARRAYSIZE( wzActiveItem ), L"%d", m_Menu.GetActiveItemIndex()+1 ); + V_snwprintf( wzTotal, ARRAYSIZE( wzTotal ), L"%d", m_nTotalAchievements ); + g_pVGuiLocalize->ConstructString( wszNumbering, sizeof( wszNumbering ), wzNumberingFmt, 2, wzActiveItem, wzTotal ); + m_pNumbering->SetText( wszNumbering ); + m_pNumbering->SetWide( GetWide() ); + } + + if ( m_bCenterOnScreen ) + { + MoveToCenterOfScreen(); + } + + // Set the footer + m_pFooter->ClearButtons(); + + if ( m_pFooterInfo && ( m_pFooterInfo->GetInt( "hide_regular_footer", 0 ) > 0 ) == false ) + { + for ( KeyValues *pButton = m_pFooterInfo->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() ) + { + if ( !Q_stricmp( pButton->GetName(), "button" ) ) + { + // Add a button to the footer + const char *pText = pButton->GetString( "text", "NULL" ); + const char *pIcon = pButton->GetString( "icon", "NULL" ); + m_pFooter->AddNewButtonLabel( pText, pIcon ); + } + } + } +} + +//---------------------------------------------------------- +// Query the system interface for this game's list of achievements +//---------------------------------------------------------- +void CAchievementsDialog_XBox::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + m_bCenterOnScreen = pResourceData->GetInt( "center", 1 ) == 1; + + if ( !achievementmgr ) + { + AssertOnce( 0 && "No achievement manager interface in GameUI.dll" ); + return; + } + + achievementmgr->EnsureGlobalStateLoaded(); + + int iAllAchievements = achievementmgr->GetAchievementCount(); + + for ( int i = 0; i < iAllAchievements; ++i ) + { + IAchievement* pCurAchievement = (IAchievement*)achievementmgr->GetAchievementByIndex( i ); + Assert ( pCurAchievement ); + + if ( pCurAchievement->IsAchieved() ) + ++m_nUnlocked; + + // don't show hidden achievements if not achieved + if ( pCurAchievement->ShouldHideUntilAchieved() && !pCurAchievement->IsAchieved() ) + continue; + + CAchievementItem *pItem = m_Menu.AddAchievementItem( ACHIEVEMENT_LOCALIZED_NAME( pCurAchievement ), + ACHIEVEMENT_LOCALIZED_DESC( pCurAchievement ), + pCurAchievement->GetPointValue(), + pCurAchievement->IsAchieved(), + pCurAchievement + ); + + SETUP_PANEL( pItem ); + } + + m_nTotalAchievements = m_Menu.GetItemCount(); +} + +//---------------------------------------------------------- +// Add the progress bar and update the dialog layout +//---------------------------------------------------------- +void CAchievementsDialog_XBox::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pProgressBg->SetBgColor( Color( 200, 184, 151, 255 ) ); + m_pProgressBar->SetBgColor( Color( 179, 82, 22, 255 ) ); + m_pProgressPercent->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) ); + m_pNumbering->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Scrolling up and down with up/down, exit to main menu with back button +//----------------------------------------------------------------------------- +void CAchievementsDialog_XBox::OnKeyCodePressed( vgui::KeyCode code ) +{ + // Handle close here, CBasePanel parent doesn't support "DialogClosing" command + if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B ) + { + OnCommand( "AchievementsDialogClosing" ); + SetDeleteSelfOnClose( true ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: catch key repeats +//----------------------------------------------------------------------------- +void CAchievementsDialog_XBox::HandleKeyRepeated( vgui::KeyCode code ) +{ + BaseClass::HandleKeyRepeated( code ); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Fade main menu back in when this dialog closes. +//----------------------------------------------------------------------------- +void CAchievementsDialog_XBox::OnClose() +{ + BasePanel()->RunCloseAnimation( "CloseAchievementsDialog_OpenMainMenu" ); + BaseClass::OnClose(); +} + +////////////////////////////////////////////////////////////////////////// +// PC Implementation +////////////////////////////////////////////////////////////////////////// + + + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CAchievementsDialog::CAchievementsDialog(vgui::Panel *parent) : BaseClass(parent, "AchievementsDialog") +{ + SetDeleteSelfOnClose(true); + SetBounds(0, 0, 512, 384); + SetMinimumSize( 256, 300 ); + SetSizeable( true ); + + m_nOldScrollItem = -1; + m_nScrollItem = -1; + + m_nUnlocked = 0; + m_nTotalAchievements = 0; + + m_pAchievementsList = new vgui::PanelListPanel( this, "listpanel_achievements" ); + m_pAchievementsList->SetFirstColumnWidth( 0 ); + + m_pListBG = new vgui::ImagePanel( this, "listpanel_background" ); + + m_pPercentageBarBackground = SETUP_PANEL( new ImagePanel( this, "PercentageBarBackground" ) ); + m_pPercentageBar = SETUP_PANEL( new ImagePanel( this, "PercentageBar" ) ); + + m_pSelectionHighlight = new ImagePanel( m_pAchievementsList->FindChildByName( "PanelListEmbedded" ), "SelectionHighlight" ); + m_pSelectionHighlight->SetVisible( false ); + m_pSelectionHighlight->SetZPos( 100 ); + + m_pAchievementPackCombo = new ComboBox(this, "achievement_pack_combo", NUM_COMBO_BOX_LINES_DEFAULT, false); + m_pHideAchievedCheck = new vgui::CheckButton( this, "HideAchieved", "" ); + + // int that holds the highest number achievement id we've found + int iHighestAchievementIDSeen = -1; + int iNextGroupBoundary = 1000; + + Q_memset( m_AchievementGroups, 0, sizeof(m_AchievementGroups) ); + m_iNumAchievementGroups = 0; + + // Base groups +// CreateNewAchievementGroup( 0, 16000 ); + CreateNewAchievementGroup( 0, 999 ); + + Assert ( achievementmgr ); + if ( achievementmgr ) + { + int iCount = achievementmgr->GetAchievementCount(); + for ( int i = 0; i < iCount; ++i ) + { + IAchievement* pCur = achievementmgr->GetAchievementByIndex( i ); + + if ( !pCur ) + continue; + + int iAchievementID = pCur->GetAchievementID(); + + if ( iAchievementID > iHighestAchievementIDSeen ) + { + // if its crossed the next group boundary, create a new group + if ( iAchievementID >= iNextGroupBoundary ) + { + int iNewGroupBoundary = 100 * ( (int)( (float)iAchievementID / 100 ) ); + CreateNewAchievementGroup( iNewGroupBoundary, iNewGroupBoundary+99 ); + + iNextGroupBoundary = iNewGroupBoundary + 100; + } + + iHighestAchievementIDSeen = iAchievementID; + } + + // don't show hidden achievements if not achieved + if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() ) + continue; + + m_nTotalAchievements++; + + if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() ) + continue; + + bool bAchieved = pCur->IsAchieved(); + + if ( bAchieved ) + { + ++m_nUnlocked; + } + + for ( int j=0;j<m_iNumAchievementGroups;j++ ) + { + if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange && + iAchievementID <= m_AchievementGroups[j].m_iMaxRange ) + { + if ( bAchieved ) + { + m_AchievementGroups[j].m_iNumUnlocked++; + } + + m_AchievementGroups[j].m_iNumAchievements++; + } + } + } + } + + CreateOrUpdateComboItems( true ); + + m_pAchievementPackCombo->ActivateItemByRow( 0 ); +} + +CAchievementsDialog::~CAchievementsDialog() +{ + if ( achievementmgr ) + { + achievementmgr->SaveGlobalStateIfDirty( false ); // check for saving here to store achievements we want pinned to HUD + } + + m_pAchievementsList->DeleteAllItems(); + delete m_pAchievementsList; + delete m_pPercentageBarBackground; + delete m_pPercentageBar; +} + +void CAchievementsDialog::OnCheckButtonChecked(Panel *panel) +{ + if ( panel == m_pHideAchievedCheck ) + { + UpdateAchievementList(); + } +} + +void CAchievementsDialog::CreateNewAchievementGroup( int iMinRange, int iMaxRange ) +{ + if ( m_iNumAchievementGroups < MAX_ACHIEVEMENT_GROUPS ) + { + m_AchievementGroups[m_iNumAchievementGroups].m_iMinRange = iMinRange; + m_AchievementGroups[m_iNumAchievementGroups].m_iMaxRange = iMaxRange; + m_iNumAchievementGroups++; + } + else + { + AssertMsg( false, "CAchievementsDialog: Too many achievement groups" ); + } +} + +//---------------------------------------------------------- +// Get the width we're going to lock at +//---------------------------------------------------------- +void CAchievementsDialog::ApplySettings( KeyValues *pResourceData ) +{ + m_iFixedWidth = pResourceData->GetInt( "wide", 512 ); + + BaseClass::ApplySettings( pResourceData ); +} + +//---------------------------------------------------------- +// Preserve our width to the one in the .res file +//---------------------------------------------------------- +void CAchievementsDialog::OnSizeChanged(int newWide, int newTall) +{ + // Lock the width, but allow height scaling + if ( newWide != m_iFixedWidth ) + { + SetSize( m_iFixedWidth, newTall ); + return; + } + + BaseClass::OnSizeChanged(newWide, newTall); +} + +//---------------------------------------------------------- +// New group was selected in the dropdown, recalc what achievements to show +//---------------------------------------------------------- +void CAchievementsDialog::OnTextChanged(KeyValues *data) +{ + Panel *pPanel = (Panel *)data->GetPtr( "panel", NULL ); + + // first check which control had its text changed! + if ( pPanel == m_pAchievementPackCombo ) + { + UpdateAchievementList(); + } +} + +class CAchievementsLess +{ +public: + bool Less( const IAchievement* src1, const IAchievement* src2, void *pCtx ) + { + IAchievement* _src1 = const_cast<IAchievement*>(src1); + IAchievement* _src2 = const_cast<IAchievement*>(src2); + + if ( _src1->IsAchieved() && !_src2->IsAchieved() ) + return false; + if ( !_src1->IsAchieved() && _src2->IsAchieved() ) + return true; + + const char* name1 = _src1->GetName(); + const char* name2 = _src2->GetName(); + if ( wcscoll( ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name1 ), ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name2 ) ) < 0 ) + return true; + return false; + } +}; + +//---------------------------------------------------------- +// Re-populate the achievement list with the selected group +//---------------------------------------------------------- +void CAchievementsDialog::UpdateAchievementList() +{ + m_pAchievementsList->DeleteAllItems(); + + KeyValues *pData = m_pAchievementPackCombo->GetActiveItemUserData(); + + if ( pData ) + { + int iMinRange = pData->GetInt( "minrange" ); + int iMaxRange = pData->GetInt( "maxrange" ); + + if ( achievementmgr ) + { + CUtlSortVector<IAchievement*, CAchievementsLess> achievements; + + int iCount = achievementmgr->GetAchievementCount(); + for ( int i = 0; i < iCount; ++i ) + { + IAchievement* pCur = achievementmgr->GetAchievementByIndex( i ); + + if ( !pCur ) + continue; + + int iAchievementID = pCur->GetAchievementID(); + + if ( iAchievementID < iMinRange || iAchievementID > iMaxRange ) + continue; + + // don't show hidden achievements if not achieved + if ( pCur->ShouldHideUntilAchieved() && !pCur->IsAchieved() ) + continue; + + if ( m_pHideAchievedCheck->IsSelected() && pCur->IsAchieved() ) + continue; + + // if we don't have a localized name, don't add it to the list + if ( pCur->GetName() == NULL || ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( pCur->GetName() ) == NULL ) + continue; + + achievements.Insert( pCur ); + } + + for ( int i=0; i<achievements.Count(); i++ ) + { + CAchievementDialogItemPanel *achievementItemPanel = new CAchievementDialogItemPanel( m_pAchievementsList, "AchievementDialogItemPanel", i ); + achievementItemPanel->SetAchievementInfo( achievements[i] ); + m_pAchievementsList->AddItem( NULL, achievementItemPanel ); + } + } + } + + if ( m_pAchievementsList->GetItemCount() > 0 ) + { + m_nOldScrollItem = -1; + m_nScrollItem = -1; + m_pSelectionHighlight->SetVisible( false ); + m_pAchievementsList->ScrollToItem( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from achievementsdialog.res in hl2/resource/ui/ +// Sets up progress bar displaying total achievement unlocking progress by the user. +//----------------------------------------------------------------------------- +void CAchievementsDialog::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings("resource/ui/AchievementsDialog.res"); + + if ( !achievementmgr ) + { + AssertOnce( 0 && "No achievement manager interface in GameUI.dll" ); + return; + } + + // Set up total completion percentage bar + float flCompletion = 0.0f; + if ( achievementmgr->GetAchievementCount() > 0 ) + { + flCompletion = (((float)m_nUnlocked) / ((float)m_nTotalAchievements)); + } + + char szPercentageText[64]; + V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )", + m_nUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) ); + + SetControlString( "PercentageText", szPercentageText ); + SetControlVisible( "PercentageText", true ); + SetControlVisible( "CompletionText", true ); + + Color clrHighlight = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + Color clrWhite(255, 255, 255, 255); + + Color cProgressBar = Color( static_cast<float>( clrHighlight.r() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.r() ) * flCompletion, + static_cast<float>( clrHighlight.g() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.g() ) * flCompletion, + static_cast<float>( clrHighlight.b() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.b() ) * flCompletion, + static_cast<float>( clrHighlight.a() ) * ( 1.0f - flCompletion ) + static_cast<float>( clrWhite.a() ) * flCompletion ); + + m_pPercentageBar->SetFgColor( cProgressBar ); + m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion ); + + SetControlVisible( "PercentageBarBackground", true ); + SetControlVisible( "PercentageBar", true ); + + m_pSelectionHighlight->SetFillColor( Color( clrHighlight.r(), clrHighlight.g(), clrHighlight.b(), 32 ) ); + + if ( m_iNumAchievementGroups <= 2 ) + { + // we have no achievement packs. Hide the combo and bump the achievement list up a bit + m_pAchievementPackCombo->SetVisible( false ); + + // do some work to preserve the pincorner and resizing + + int comboX, comboY; + m_pAchievementPackCombo->GetPos( comboX, comboY ); + + int x, y, w, h; + m_pAchievementsList->GetBounds( x, y, w, h ); + + PinCorner_e corner = m_pAchievementsList->GetPinCorner(); + int pinX, pinY; + m_pAchievementsList->GetPinOffset( pinX, pinY ); + + int resizeOffsetX, resizeOffsetY; + m_pAchievementsList->GetResizeOffset( resizeOffsetX, resizeOffsetY ); + + m_pAchievementsList->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY ); + + m_pAchievementsList->SetBounds( x, comboY, w, h + ( y - comboY ) ); + + m_pListBG->SetAutoResize( corner, AUTORESIZE_DOWN, pinX, comboY, resizeOffsetX, resizeOffsetY ); + m_pListBG->SetBounds( x, comboY, w, h + ( y - comboY ) ); + } + + MoveToCenterOfScreen(); +} + +void CAchievementsDialog::ScrollToItem( int nDirection ) +{ + if ( m_pAchievementsList->GetItemCount() > 0 ) + { + m_nScrollItem = clamp( m_nScrollItem + nDirection, 0, m_pAchievementsList->GetItemCount() - 1 ); + + if ( m_nOldScrollItem != m_nScrollItem ) + { + m_nOldScrollItem = m_nScrollItem; + m_pAchievementsList->ScrollToItem( m_nScrollItem ); + + Panel *pItem = m_pAchievementsList->GetItemPanel( m_nScrollItem ); + if ( pItem ) + { + int nX, nY, nWide, nTall; + pItem->GetBounds( nX, nY, nWide, nTall ); + + m_pSelectionHighlight->SetVisible( true ); + m_pSelectionHighlight->SetBounds( nX, nY, nWide, nTall ); + } + } + } +} + +void CAchievementsDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + // Handle close here, CBasePanel parent doesn't support "DialogClosing" command + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B ) + { + OnCommand( "Close" ); + } + else if ( nButtonCode == KEY_XBUTTON_X || nButtonCode == STEAMCONTROLLER_X ) + { + if ( m_pHideAchievedCheck && m_pHideAchievedCheck->IsVisible() ) + { + m_pHideAchievedCheck->SetSelected( !m_pHideAchievedCheck->IsSelected() ); + } + } + else if ( nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A ) + { + if ( GameSupportsAchievementTracker() && m_nScrollItem >= 0 && m_nScrollItem < m_pAchievementsList->GetItemCount() ) + { + CAchievementDialogItemPanel *pItem = dynamic_cast< CAchievementDialogItemPanel* >( m_pAchievementsList->GetItemPanel( m_nScrollItem) ); + if ( pItem && pItem->IsVisible() ) + { + pItem->ToggleShowOnHUD(); + } + } + } + else if ( nButtonCode == KEY_XBUTTON_UP || + nButtonCode == KEY_XSTICK1_UP || + nButtonCode == KEY_XSTICK2_UP || + nButtonCode == STEAMCONTROLLER_DPAD_UP || + nButtonCode == KEY_UP ) + { + ScrollToItem( -1 ); + } + else if ( nButtonCode == KEY_XBUTTON_DOWN || + nButtonCode == KEY_XSTICK1_DOWN || + nButtonCode == KEY_XSTICK2_DOWN || + nButtonCode == STEAMCONTROLLER_DPAD_DOWN || + nButtonCode == KEY_DOWN ) + { + ScrollToItem( 1 ); + } + else if ( nButtonCode == KEY_XBUTTON_LEFT || + nButtonCode == KEY_XSTICK1_LEFT || + nButtonCode == KEY_XSTICK2_LEFT || + nButtonCode == STEAMCONTROLLER_DPAD_LEFT || + nButtonCode == KEY_LEFT ) + { + m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() - 1 ); + } + else if ( nButtonCode == KEY_XBUTTON_RIGHT || + nButtonCode == KEY_XSTICK1_RIGHT || + nButtonCode == KEY_XSTICK2_RIGHT || + nButtonCode == STEAMCONTROLLER_DPAD_RIGHT || + nButtonCode == KEY_RIGHT ) + { + m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() + 1 ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Each sub-panel gets its data updated +//----------------------------------------------------------------------------- +void CAchievementsDialog::UpdateAchievementDialogInfo( void ) +{ + int iCount = m_pAchievementsList->GetItemCount(); + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + for ( int i = 0; i < iCount; i++ ) + { + CAchievementDialogItemPanel *pPanel = (CAchievementDialogItemPanel*)m_pAchievementsList->GetItemPanel(i); + if ( pPanel ) + { + pPanel->UpdateAchievementInfo( pScheme ); + } + } + + // update the groups and overall progress bar + if ( achievementmgr ) + { + int i; + + // reset group completed counts + for ( i=0;i<m_iNumAchievementGroups;i++ ) + { + m_AchievementGroups[i].m_iNumUnlocked = 0; + } + + int iAchievementCount = achievementmgr->GetAchievementCount(); + + // update counts for each achieved achievement + for ( i=0;i<iAchievementCount;i++ ) + { + IAchievement* pCurAchievement = achievementmgr->GetAchievementByIndex( i ); + + if ( !pCurAchievement || !pCurAchievement->IsAchieved() ) + continue; + + int iAchievementID = pCurAchievement->GetAchievementID(); + + for ( int j=0;j<m_iNumAchievementGroups;j++ ) + { + if ( iAchievementID >= m_AchievementGroups[j].m_iMinRange && + iAchievementID <= m_AchievementGroups[j].m_iMaxRange ) + { + m_AchievementGroups[j].m_iNumUnlocked++; + } + } + } + + // update the global progress bar label + char szPercentageText[64]; + float flCompletion = (((float)m_AchievementGroups[0].m_iNumUnlocked) / ((float)iAchievementCount)); + V_sprintf_safe( szPercentageText, "%d / %d ( %d%% )", + m_AchievementGroups[0].m_iNumUnlocked, m_nTotalAchievements, (int)( flCompletion * 100.0f ) ); + + // and the progress bar + m_pPercentageBar->SetWide( m_pPercentageBarBackground->GetWide() * flCompletion ); + + SetControlString( "PercentageText", szPercentageText ); + } + + CreateOrUpdateComboItems( false ); // update them with new achieved counts + + m_pAchievementPackCombo->ActivateItemByRow( m_pAchievementPackCombo->GetActiveItem() ); +} + +void CAchievementsDialog::CreateOrUpdateComboItems( bool bCreate ) +{ + for ( int i=0;i<m_iNumAchievementGroups;i++ ) + { + char buf[128]; + + Q_snprintf( buf, sizeof(buf), "#Achievement_Group_%d", m_AchievementGroups[i].m_iMinRange ); + + const wchar_t *wzGroupName = g_pVGuiLocalize->Find( buf ); + + if ( !wzGroupName ) + { + wzGroupName = L"Need Title ( %s1 of %s2 )"; + } + + wchar_t wzGroupTitle[128]; + + if ( wzGroupName ) + { + wchar_t wzNumUnlocked[8]; + V_snwprintf( wzNumUnlocked, ARRAYSIZE( wzNumUnlocked ), L"%d", m_AchievementGroups[i].m_iNumUnlocked ); + + wchar_t wzNumAchievements[8]; + V_snwprintf( wzNumAchievements, ARRAYSIZE( wzNumAchievements ), L"%d", m_AchievementGroups[i].m_iNumAchievements ); + + g_pVGuiLocalize->ConstructString( wzGroupTitle, sizeof( wzGroupTitle ), wzGroupName, 2, wzNumUnlocked, wzNumAchievements ); + } + + KeyValues *pKV = new KeyValues( "grp" ); + pKV->SetInt( "minrange", m_AchievementGroups[i].m_iMinRange ); + pKV->SetInt( "maxrange", m_AchievementGroups[i].m_iMaxRange ); + + if ( bCreate ) + m_AchievementGroups[i].m_iDropDownGroupID = m_pAchievementPackCombo->AddItem( wzGroupTitle, pKV ); + else + m_pAchievementPackCombo->UpdateItem( m_AchievementGroups[i].m_iDropDownGroupID, wzGroupTitle, pKV ); + } + + if ( bCreate && ( m_iNumAchievementGroups > NUM_COMBO_BOX_LINES_DEFAULT ) ) + { + if ( m_pAchievementPackCombo ) + { + m_pAchievementPackCombo->SetNumberOfEditLines( ( m_iNumAchievementGroups <= NUM_COMBO_BOX_LINES_MAX ) ? m_iNumAchievementGroups : NUM_COMBO_BOX_LINES_MAX ); + } + } +} + +void CAchievementsDialog::OnCommand( const char *command ) +{ + if ( !Q_strcasecmp( command, "ongameuiactivated" ) ) + { + UpdateAchievementDialogInfo(); + } + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: creates child panels, passes down name to pick up any settings from res files. +//----------------------------------------------------------------------------- +CAchievementDialogItemPanel::CAchievementDialogItemPanel( vgui::PanelListPanel *parent, const char* name, int iListItemID ) : BaseClass( parent, name ) +{ + m_iListItemID = iListItemID; + m_pParent = parent; + m_pSchemeSettings = NULL; + + SetMouseInputEnabled( true ); + parent->SetMouseInputEnabled( true ); + + //CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL); + //panel->SetZPos(2); +} + +CAchievementDialogItemPanel::~CAchievementDialogItemPanel() +{ + /* + delete m_pAchievementIcon; + delete m_pAchievementNameLabel; + delete m_pAchievementDescLabel; + delete m_pPercentageBar; + delete m_pPercentageText; + delete m_pShowOnHUDCheck; + */ +} + +//----------------------------------------------------------------------------- +// Purpose: Updates displayed achievement data. In applyschemesettings, and when gameui activates. +//----------------------------------------------------------------------------- +void CAchievementDialogItemPanel::UpdateAchievementInfo( IScheme* pScheme ) +{ + if ( m_pSourceAchievement && m_pSchemeSettings ) + { + // Set name, description and unlocked state text + m_pAchievementNameLabel->SetText( ACHIEVEMENT_LOCALIZED_NAME( m_pSourceAchievement ) ); + m_pAchievementDescLabel->SetText( ACHIEVEMENT_LOCALIZED_DESC( m_pSourceAchievement ) ); + + // Setup icon + // get the vtfFilename from the path. + + // Display percentage completion for progressive achievements + // Set up total completion percentage bar. Goal > 1 means its a progress achievement. + UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar ); + + if ( m_pSourceAchievement->IsAchieved() ) + { + LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement ); + + SetBgColor( pScheme->GetColor( "AchievementsLightGrey", Color(255, 0, 0, 255) ) ); + + m_pAchievementNameLabel->SetFgColor( pScheme->GetColor( "SteamLightGreen", Color(255, 255, 255, 255) ) ); + + Color fgColor = pScheme->GetColor( "Label.TextBrightColor", Color(255, 255, 255, 255) ); + m_pAchievementDescLabel->SetFgColor( fgColor ); + m_pPercentageText->SetFgColor( fgColor ); + m_pShowOnHUDCheck->SetVisible( false ); + m_pShowOnHUDCheck->SetSelected( false ); + } + else + { + LoadAchievementIcon( m_pAchievementIcon, m_pSourceAchievement, "_bw" ); + + SetBgColor( pScheme->GetColor( "AchievementsDarkGrey", Color(255, 0, 0, 255) ) ); + + Color fgColor = pScheme->GetColor( "AchievementsInactiveFG", Color(255, 255, 255, 255) ); + m_pAchievementNameLabel->SetFgColor( fgColor ); + m_pAchievementDescLabel->SetFgColor( fgColor ); + m_pPercentageText->SetFgColor( fgColor ); + if ( GameSupportsAchievementTracker() ) + { + m_pShowOnHUDCheck->SetVisible( !m_pSourceAchievement->ShouldHideUntilAchieved() ); // m_pSourceAchievement->GetGoal() > 1 && + m_pShowOnHUDCheck->SetSelected( m_pSourceAchievement->ShouldShowOnHUD() ); + } + else + { + m_pShowOnHUDCheck->SetVisible( false ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Makes a local copy of a pointer to the achievement entity stored on the client. +//----------------------------------------------------------------------------- +void CAchievementDialogItemPanel::SetAchievementInfo( IAchievement* pAchievement ) +{ + if ( !pAchievement ) + { + Assert( 0 ); + return; + } + + m_pSourceAchievement = pAchievement; + m_iSourceAchievementIndex = pAchievement->GetAchievementID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAchievementDialogItemPanel::PreloadResourceFile( void ) +{ + const char *controlResourceName = "resource/ui/AchievementItem.res"; + + g_pPreloadedAchievementItemLayout = new KeyValues(controlResourceName); + g_pPreloadedAchievementItemLayout->LoadFromFile(g_pFullFileSystem, controlResourceName); +} + +//----------------------------------------------------------------------------- +// Purpose: Loads settings from hl2/resource/ui/achievementitem.res +// Sets display info for this achievement item. +//----------------------------------------------------------------------------- +void CAchievementDialogItemPanel::ApplySchemeSettings( IScheme* pScheme ) +{ + if ( !g_pPreloadedAchievementItemLayout ) + { + PreloadResourceFile(); + } + + LoadControlSettings( "", NULL, g_pPreloadedAchievementItemLayout ); + + m_pSchemeSettings = pScheme; + + if ( !m_pSourceAchievement ) + { + return; + } + + m_pAchievementIcon = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("AchievementIcon"))); + m_pAchievementNameLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementName")); + m_pAchievementDescLabel = dynamic_cast<vgui::Label*>(FindChildByName("AchievementDesc")); + m_pPercentageBar = SETUP_PANEL(dynamic_cast<vgui::ImagePanel*>(FindChildByName("PercentageBar"))); + m_pPercentageText = dynamic_cast<vgui::Label*>(FindChildByName("PercentageText")); + m_pShowOnHUDCheck = dynamic_cast<vgui::CheckButton*>(FindChildByName("ShowOnHUD")); + m_pShowOnHUDCheck->SetMouseInputEnabled( true ); + m_pShowOnHUDCheck->SetEnabled( true ); + m_pShowOnHUDCheck->SetCheckButtonCheckable( true ); + m_pShowOnHUDCheck->AddActionSignalTarget( this ); + + BaseClass::ApplySchemeSettings( pScheme ); + + // m_pSchemeSettings must be set for this. + UpdateAchievementInfo( pScheme ); +} + +void CAchievementDialogItemPanel::ToggleShowOnHUD( void ) +{ + m_pShowOnHUDCheck->SetSelected( !m_pShowOnHUDCheck->IsSelected() ); +} + +void CAchievementDialogItemPanel::OnCheckButtonChecked(Panel *panel) +{ + if ( GameSupportsAchievementTracker() && panel == m_pShowOnHUDCheck && m_pSourceAchievement ) + { + m_pSourceAchievement->SetShowOnHUD( m_pShowOnHUDCheck->IsSelected() ); + } +} diff --git a/gameui/matchmaking/achievementsdialog.h b/gameui/matchmaking/achievementsdialog.h new file mode 100644 index 0000000..b16d876 --- /dev/null +++ b/gameui/matchmaking/achievementsdialog.h @@ -0,0 +1,182 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ACHIEVEMENTSDIALOG_H +#define ACHIEVEMENTSDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/Label.h" +#include "tier1/KeyValues.h" +#include "TGAImagePanel.h" + +#define MAX_ACHIEVEMENT_GROUPS 25 + +class IAchievement; + +#define ACHIEVED_ICON_PATH "hud/icon_check.vtf" +#define LOCK_ICON_PATH "hud/icon_locked.vtf" + +// Loads an achievement's icon into a specified image panel, or turns the panel off if no achievement icon was found. +bool LoadAchievementIcon( vgui::ImagePanel* pIconPanel, IAchievement *pAchievement, const char *pszExt = NULL ); + +// Updates a listed achievement item's progress bar. +void UpdateProgressBar( vgui::EditablePanel* pPanel, IAchievement *pAchievement, Color clrProgressBar ); + +//----------------------------------------------------------------------------- +// Purpose: Simple menu to choose a matchmaking session type +//----------------------------------------------------------------------------- +class CAchievementsDialog_XBox : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CAchievementsDialog_XBox, CBaseDialog ); + +public: + CAchievementsDialog_XBox(vgui::Panel *parent); + ~CAchievementsDialog_XBox(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void PerformLayout(); + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void HandleKeyRepeated( vgui::KeyCode code ); + + virtual void OnClose(); + + +private: + + vgui::Panel *m_pProgressBg; + + vgui::Panel *m_pProgressBar; + vgui::Label *m_pProgressPercent; + vgui::Label *m_pNumbering; + vgui::Label *m_pUpArrow; + vgui::Label *m_pDownArrow; + + KeyValues* m_pResourceData; + + CFooterPanel *m_pFooter; + + bool m_bCenterOnScreen; + int m_iNumItems; + int m_nTotalAchievements; // Total achievements for this title + int m_nUnlocked; + int m_iSelection; + int m_iScroll; +}; + + +//////////////////////////////////////////////////////////////////////////// +// PC version +////////////////////////////////////////////////////////////////////////// +class CAchievementsDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE ( CAchievementsDialog, vgui::Frame ); + +public: + CAchievementsDialog( vgui::Panel *parent ); + ~CAchievementsDialog(); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + void ScrollToItem( int nDirection ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void UpdateAchievementDialogInfo( void ); + virtual void OnCommand( const char* command ); + + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void OnSizeChanged( int newWide, int newTall ); + + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ); + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data ); + + void CreateNewAchievementGroup( int iMinRange, int iMaxRange ); + void CreateOrUpdateComboItems( bool bCreate ); + void UpdateAchievementList(); + + vgui::PanelListPanel *m_pAchievementsList; + vgui::ImagePanel *m_pListBG; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + + vgui::ImagePanel *m_pSelectionHighlight; + + vgui::ComboBox *m_pAchievementPackCombo; + vgui::CheckButton *m_pHideAchievedCheck; + + int m_nUnlocked; + int m_nTotalAchievements; + + int m_iFixedWidth; + + typedef struct + { + int m_iMinRange; + int m_iMaxRange; + int m_iNumAchievements; + int m_iNumUnlocked; + int m_iDropDownGroupID; + } achievement_group_t; + + int m_iNumAchievementGroups; + + achievement_group_t m_AchievementGroups[ MAX_ACHIEVEMENT_GROUPS ]; + + int m_nScrollItem; + int m_nOldScrollItem; +}; + +////////////////////////////////////////////////////////////////////////// +// Individual item panel, displaying stats for one achievement +class CAchievementDialogItemPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CAchievementDialogItemPanel, vgui::EditablePanel ); + +public: + CAchievementDialogItemPanel( vgui::PanelListPanel *parent, const char* name, int iListItemID ); + ~CAchievementDialogItemPanel(); + + void SetAchievementInfo ( IAchievement* pAchievement ); + IAchievement* GetAchievementInfo( void ) { return m_pSourceAchievement; } + void UpdateAchievementInfo( IScheme *pScheme ); + virtual void ApplySchemeSettings( IScheme *pScheme ); + void ToggleShowOnHUD( void ); + + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ); + +private: + void PreloadResourceFile( void ); + + IAchievement* m_pSourceAchievement; + int m_iSourceAchievementIndex; + + vgui::PanelListPanel *m_pParent; + + vgui::Label *m_pAchievementNameLabel; + vgui::Label *m_pAchievementDescLabel; + vgui::Label *m_pPercentageText; + + vgui::ImagePanel *m_pLockedIcon; + vgui::ImagePanel *m_pAchievementIcon; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + + vgui::CheckButton *m_pShowOnHUDCheck; + + vgui::IScheme *m_pSchemeSettings; + + CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" ); + + int m_iListItemID; +}; + +#endif // ACHIEVEMENTSDIALOG_H diff --git a/gameui/matchmaking/basedialog.cpp b/gameui/matchmaking/basedialog.cpp new file mode 100644 index 0000000..be65718 --- /dev/null +++ b/gameui/matchmaking/basedialog.cpp @@ -0,0 +1,295 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: All matchmaking dialogs inherit from this +// +//=============================================================================// + +#include "vgui_controls/Label.h" +#include "GameUI_Interface.h" +#include "KeyValues.h" +#include "basedialog.h" +#include "BasePanel.h" +#include "matchmakingbasepanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//--------------------------------------------------------- +// CBaseDialog +//--------------------------------------------------------- +CBaseDialog::CBaseDialog( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + SetTitleBarVisible( false ); + SetCloseButtonVisible( false ); + SetSizeable( false ); + + m_pParent = pParent; + m_Menu.SetParent( this ); + + m_pTitle = new vgui::Label( this, "DialogTitle", "" ); + m_pFooterInfo = NULL; + + m_nBorderWidth = 0; + m_nButtonGap = -1; +} + +CBaseDialog::~CBaseDialog() +{ + delete m_pTitle; + + if ( m_pFooterInfo ) + { + m_pFooterInfo->deleteThis(); + } +} + +//--------------------------------------------------------- +// Purpose: Activate the dialog +//--------------------------------------------------------- +void CBaseDialog::Activate( void ) +{ + BaseClass::Activate(); + + InvalidateLayout( false, false ); +} + +//--------------------------------------------------------- +// Purpose: Set the title and menu positions +//--------------------------------------------------------- +void CBaseDialog::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + m_pTitle->SizeToContents(); + + int menux, menuy; + m_Menu.GetPos( menux, menuy ); + + int autoWide = m_Menu.GetWide() + m_nBorderWidth * 2; + int autoTall = menuy + m_Menu.GetTall() + m_nBorderWidth; + autoWide = max( autoWide, GetWide() ); + autoTall = max( autoTall, GetTall() ); + + SetSize( autoWide, autoTall ); + + if ( m_pFooterInfo && m_pParent ) + { + CMatchmakingBasePanel *pBasePanel = dynamic_cast< CMatchmakingBasePanel* >( m_pParent ); + if ( pBasePanel ) + { + // the base panel is our parent + pBasePanel->SetFooterButtons( this, m_pFooterInfo, m_nButtonGap ); + } + } + + if ( m_Menu.GetActiveItemIndex() == -1 ) + { + m_Menu.SetFocus( 0 ); + } +} + +//--------------------------------------------------------- +// Purpose: Setup sizes and positions +//--------------------------------------------------------- +void CBaseDialog::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_nBorderWidth = inResourceData->GetInt( "borderwidth", 0 ); + + KeyValues *pFooter = inResourceData->FindKey( "Footer" ); + if ( pFooter ) + { + m_pFooterInfo = pFooter->MakeCopy(); + } + + m_nButtonGap = inResourceData->GetInt( "footer_buttongap", -1 ); +} + +//--------------------------------------------------------- +// Purpose: Setup colors and fonts +//--------------------------------------------------------- +void CBaseDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetPaintBackgroundType( 2 ); + + m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 200, 184, 151, 255 ) ) ); + + char szResourceName[MAX_PATH]; + Q_snprintf( szResourceName, sizeof( szResourceName ), "%s.res", GetName() ); + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( szResourceName ); + LoadControlSettings( "NULL", NULL, pKeys ); +} + +//----------------------------------------------------------------- +// Purpose: Set the resource file to load this dialog's settings +//----------------------------------------------------------------- +void CBaseDialog::OnClose() +{ + // Hide the rather ugly fade out + SetAlpha( 0 ); + BaseClass::OnClose(); +} + +//----------------------------------------------------------------- +// Purpose: Change properties of a menu item +//----------------------------------------------------------------- +void CBaseDialog::OverrideMenuItem( KeyValues *pKeys ) +{ + // Do nothing +} + +//----------------------------------------------------------------- +// Purpose: Swap the order of two menu items +//----------------------------------------------------------------- +void CBaseDialog::SwapMenuItems( int iOne, int iTwo ) +{ + // Do nothing +} + +//----------------------------------------------------------------- +// Purpose: Send key presses to the dialog's menu +//----------------------------------------------------------------- +void CBaseDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_START ) + { + m_KeyRepeat.Reset(); + if ( GameUI().IsInLevel() ) + { + m_pParent->OnCommand( "ResumeGame" ); + } + return; + } + + m_KeyRepeat.KeyDown( code ); + + // Send down to the menu + if ( !m_Menu.HandleKeyCode( code ) ) + { + if ( code == KEY_XBUTTON_B ) + { + OnCommand( "DialogClosing" ); + SetDeleteSelfOnClose( true ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseDialog::OnKeyCodeReleased( vgui::KeyCode code ) +{ + m_KeyRepeat.KeyUp( code ); + + BaseClass::OnKeyCodeReleased( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseDialog::HandleKeyRepeated( vgui::KeyCode code ) +{ + m_Menu.HandleKeyCode( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseDialog::OnThink() +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + if ( HasFocus() ) + { + HandleKeyRepeated( code ); + } + else + { + // This can happen because of the slight delay after selecting a + // menu option and the resulting action. The selection caused the + // key repeater to be reset, but the player can press a movement + // key before the action occurs, leaving us with a key repeating + // on this dialog even though it no longer has focus. + m_KeyRepeat.Reset(); + } + } + + BaseClass::OnThink(); +} + +//----------------------------------------------------------------- +// Purpose: Forward commands to the matchmaking base panel +//----------------------------------------------------------------- +void CBaseDialog::OnCommand( const char *pCommand ) +{ + m_KeyRepeat.Reset(); + m_pParent->OnCommand( pCommand ); +} + + +//--------------------------------------------------------------------- +// Helper object to display the map picture and descriptive text +//--------------------------------------------------------------------- +CScenarioInfoPanel::CScenarioInfoPanel( vgui::Panel *parent, const char *pName ) : BaseClass( parent, pName ) +{ + m_pMapImage = new vgui::ImagePanel( this, "MapImage" ); + m_pTitle = new CPropertyLabel( this, "Title", "" ); + m_pSubtitle = new CPropertyLabel( this, "Subtitle", "" ); + + m_pDescOne = new CPropertyLabel( this, "DescOne", "" ); + m_pDescTwo = new CPropertyLabel( this, "DescTwo", "" ); + m_pDescThree = new CPropertyLabel( this, "DescThree", "" ); + + m_pValueTwo = new CPropertyLabel( this, "ValueTwo", "" ); + m_pValueThree = new CPropertyLabel( this, "ValueThree", "" ); +} +CScenarioInfoPanel::~CScenarioInfoPanel() +{ + delete m_pMapImage; + delete m_pTitle; + delete m_pSubtitle; + delete m_pDescOne; + delete m_pDescTwo; + delete m_pDescThree; + delete m_pValueTwo; + delete m_pValueThree; +} + +void CScenarioInfoPanel::PerformLayout( void ) +{ + BaseClass::PerformLayout(); +} + +void CScenarioInfoPanel::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); +} + +void CScenarioInfoPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + Color fontColor = pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 0, 0, 0, 255 ) ); + m_pTitle->SetFgColor( fontColor ); + m_pSubtitle->SetFgColor( fontColor ); + m_pDescOne->SetFgColor( fontColor ); + m_pDescTwo->SetFgColor( fontColor ); + m_pDescThree->SetFgColor( fontColor ); + m_pValueTwo->SetFgColor( fontColor ); + m_pValueThree->SetFgColor( fontColor ); + + SetPaintBackgroundType( 2 ); + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "ScenarioInfoPanel.res" ); + ApplySettings( pKeys ); +} + +DECLARE_BUILD_FACTORY( CScenarioInfoPanel ); diff --git a/gameui/matchmaking/basedialog.h b/gameui/matchmaking/basedialog.h new file mode 100644 index 0000000..1501e1f --- /dev/null +++ b/gameui/matchmaking/basedialog.h @@ -0,0 +1,120 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: All matchmaking dialogs inherit from this +// +//=============================================================================// + +#ifndef BASEDIALOG_H +#define BASEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "dialogmenu.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/KeyRepeat.h" +#include "KeyValues.h" +#include "BasePanel.h" + +#if !defined( _X360 ) +#include "xbox/xboxstubs.h" +#endif + +class CFooterPanel; + +//----------------------------------------------------------------------------- +// Purpose: A Label with an extra string to hold a session property lookup key +//----------------------------------------------------------------------------- +class CPropertyLabel : public vgui::Label +{ + DECLARE_CLASS_SIMPLE( CPropertyLabel, vgui::Label ); + +public: + CPropertyLabel( Panel *parent, const char *panelName, const char *text ) : BaseClass( parent, panelName, text ) + { + } + + virtual void ApplySettings( KeyValues *pResourceData ) + { + BaseClass::ApplySettings( pResourceData ); + + m_szPropertyString[0] = 0; + const char *pString = pResourceData->GetString( "PropertyString", NULL ); + if ( pString ) + { + Q_strncpy( m_szPropertyString, pString, sizeof( m_szPropertyString ) ); + } + } + + char m_szPropertyString[ MAX_PATH ]; +}; + +//-------------------------------- +// CBaseDialog +//-------------------------------- +class CBaseDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBaseDialog, vgui::Frame ); + +public: + CBaseDialog( vgui::Panel *parent, const char *pName ); + ~CBaseDialog(); + + // IPanel interface + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void PerformLayout(); + virtual void Activate(); + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnKeyCodeReleased( vgui::KeyCode code); + virtual void OnCommand( const char *pCommand ); + virtual void OnClose(); + virtual void OnThink(); + + virtual void OverrideMenuItem( KeyValues *pKeys ); + virtual void SwapMenuItems( int iOne, int iTwo ); + + virtual void HandleKeyRepeated( vgui::KeyCode code ); + +protected: + int m_nBorderWidth; + int m_nMinWide; + + CDialogMenu m_Menu; + vgui::Label *m_pTitle; + vgui::Panel *m_pParent; + + KeyValues *m_pFooterInfo; + int m_nButtonGap; + + vgui::CKeyRepeatHandler m_KeyRepeat; +}; + + +//--------------------------------------------------------------------- +// Helper object to display the map picture and descriptive text +//--------------------------------------------------------------------- +class CScenarioInfoPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CScenarioInfoPanel, vgui::EditablePanel ); + +public: + CScenarioInfoPanel( vgui::Panel *parent, const char *pName ); + ~CScenarioInfoPanel(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + vgui::ImagePanel *m_pMapImage; + CPropertyLabel *m_pTitle; + CPropertyLabel *m_pSubtitle; + CPropertyLabel *m_pDescOne; + CPropertyLabel *m_pDescTwo; + CPropertyLabel *m_pDescThree; + CPropertyLabel *m_pValueTwo; + CPropertyLabel *m_pValueThree; +}; + +#endif // BASEDIALOG_H
\ No newline at end of file diff --git a/gameui/matchmaking/dialogmenu.cpp b/gameui/matchmaking/dialogmenu.cpp new file mode 100644 index 0000000..e46888b --- /dev/null +++ b/gameui/matchmaking/dialogmenu.cpp @@ -0,0 +1,1524 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Multi-purpose menu for matchmaking dialogs, navigable with the xbox controller. +// +//=============================================================================// + +#include "engine/imatchmaking.h" +#include "GameUI_Interface.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "KeyValues.h" +#include "dialogmenu.h" +#include "BasePanel.h" +#include "vgui_controls/ImagePanel.h" +#include "iachievementmgr.h" // for iachievement abstract class in CAchievementItem +#include "achievementsdialog.h" // for helper functions used by both pc and xbox achievements + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------- +// Base class representing a generic menu item. Supports two text labels, +// where the first label is the "action" text and the second is an optional +// description of the action. +//----------------------------------------------------------------------- +CMenuItem::CMenuItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription ) + : BaseClass( pParent, "MenuItem" ) +{ + // Quiet "parent not sized yet" spew + SetSize( 10, 10 ); + + m_pParent = pParent; + + m_bEnabled = true; + m_nDisabledAlpha = 30; + + m_pTitle = new vgui::Label( this, "MenuItemText", pTitle ); + m_pDescription = NULL; + if ( pDescription ) + { + m_pDescription = new vgui::Label( this, "MenuItemDesc", pDescription ); + } +} + +CMenuItem::~CMenuItem() +{ + delete m_pTitle; + delete m_pDescription; +} + +//----------------------------------------------------------------------- +// Update colors according to enabled/disabled state +//----------------------------------------------------------------------- +void CMenuItem::PerformLayout() +{ + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------- +// Setup margins and calculate the total menu item size +//----------------------------------------------------------------------- +void CMenuItem::ApplySettings( KeyValues *pSettings ) +{ + BaseClass::ApplySettings( pSettings ); + + m_nBottomMargin = pSettings->GetInt( "bottommargin", 0 ); + m_nRightMargin = pSettings->GetInt( "rightmargin", 0 ); + + int x, y; + m_pTitle->GetPos( x, y ); + m_pTitle->SizeToContents(); + + int bgTall = y + m_pTitle->GetTall() + m_nBottomMargin; + int textWide = m_pTitle->GetWide(); + + if ( m_pDescription ) + { + m_pDescription->SizeToContents(); + m_pDescription->GetPos( x, y ); + bgTall = y + m_pDescription->GetTall() + m_nBottomMargin; + textWide = max( textWide, m_pDescription->GetWide() ); + } + + int bgWide = x + textWide + m_nRightMargin; + + SetSize( bgWide, bgTall ); +} + +//----------------------------------------------------------------------- +// Setup colors and fonts +//----------------------------------------------------------------------- +void CMenuItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetPaintBackgroundType( 2 ); + + m_BgColor = pScheme->GetColor( "MatchmakingMenuItemBackground", Color( 46, 43, 42, 255 ) ); + m_BgColorActive = pScheme->GetColor( "MatchmakingMenuItemBackgroundActive", Color( 150, 71, 0, 255 ) ); + + m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 0, 0, 0, 255 ) ) ); + + if ( m_pDescription ) + { + m_pDescription->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 0, 0, 0, 255 ) ) ); + } + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "MenuItem.res" ); + ApplySettings( pKeys ); +} + +//----------------------------------------------------------------------- +// Set an item as having input focus +//----------------------------------------------------------------------- +void CMenuItem::SetFocus( const bool bActive ) +{ + if ( bActive ) + { + SetBgColor( m_BgColorActive ); + } + else + { + SetBgColor( m_BgColor ); + } +} + +//----------------------------------------------------------------------- +// Set an item as having input focus +//----------------------------------------------------------------------- +void CMenuItem::SetEnabled( bool bEnabled ) +{ + if ( bEnabled ) + { + SetAlpha( 255 ); + } + else + { + SetAlpha( m_nDisabledAlpha ); + } + m_bEnabled = bEnabled; +} + +//----------------------------------------------------------------------- +// Set a column as having focus +//----------------------------------------------------------------------- +void CMenuItem::SetActiveColumn( int col ) +{ + // do nothing +} + +//----------------------------------------------------------------------- +// Set an item as having input focus +//----------------------------------------------------------------------- +bool CMenuItem::IsEnabled() +{ + return m_bEnabled; +} + +//----------------------------------------------------------------------- +// Perform any special actions when an item is "clicked" +//----------------------------------------------------------------------- +void CMenuItem::OnClick() +{ + // do nothing - derived classes implement this +} + + +//----------------------------------------------------------------------- +// CCommandItem +// +// Menu item that issues a command when clicked. +//----------------------------------------------------------------------- +CCommandItem::CCommandItem( CDialogMenu *pParent, const char *pTitleLabel, const char *pDescLabel, const char *pCommand ) + : BaseClass( pParent, pTitleLabel, pDescLabel ) +{ + Q_strncpy( m_szCommand, pCommand, MAX_COMMAND_LEN ); +} + +CCommandItem::~CCommandItem() +{ + // do nothing +} + +void CCommandItem::OnClick() +{ + GetParent()->OnCommand( m_szCommand ); + + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); +} + +void CCommandItem::SetFocus(const bool bActive ) +{ + BaseClass::SetFocus( bActive ); + + if ( bActive == true && m_bHasFocus == false ) + { + vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" ); + } + + m_bHasFocus = bActive; +} + + +//----------------------------------------------------------------------- +// CPlayerItem +// +// Menu item to display a player in the lobby. +//----------------------------------------------------------------------- +CPlayerItem::CPlayerItem( CDialogMenu *pParent, const char *pTitleLabel, int64 nId, byte bVoice, bool bReady ) +: BaseClass( pParent, pTitleLabel, NULL, "ShowGamerCard" ) +{ + m_pVoiceIcon = new vgui::Label( this, "voiceicon", "" ); + m_pReadyIcon = new vgui::Label( this, "readyicon", "" ); + + m_nId = nId; + m_bVoice = bVoice; + m_bReady = bReady; +} + +CPlayerItem::~CPlayerItem() +{ + delete m_pVoiceIcon; + delete m_pReadyIcon; +} + +void CPlayerItem::PerformLayout() +{ + BaseClass::PerformLayout(); + + const char *pVoice = ""; + + if ( m_bVoice == 2 ) + { + pVoice = "#TF_Icon_Voice"; + } + else if ( m_bVoice == 1 ) + { + pVoice = "#TF_Icon_Voice_Idle"; + } + + m_pVoiceIcon->SetText( pVoice ); + m_pReadyIcon->SetText( m_bReady ? "#TF_Icon_Ready" : "#TF_Icon_NotReady" ); + + int x, y; + m_pReadyIcon->GetPos( x, y ); + m_pReadyIcon->SetPos( GetWide() - m_pReadyIcon->GetWide() - m_nRightMargin, y ); +} + +void CPlayerItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "PlayerItem.res" ); + ApplySettings( pKeys ); +} + +void CPlayerItem::OnClick() +{ + BaseClass::OnClick(); +} + +//----------------------------------------------------------------------- +// CBrowserItem +// +// Menu item used to display session search results. +//----------------------------------------------------------------------- +CBrowserItem::CBrowserItem( CDialogMenu *pParent, const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing ) + : BaseClass( pParent, pHost, NULL, "SelectSession" ) +{ + m_pPlayers = new vgui::Label( this, "players", pPlayers ); + m_pScenario = new vgui::Label( this, "scenario", pScenario ); + m_pPing = new vgui::Label( this, "ping", pPing ); +} + +CBrowserItem::~CBrowserItem() +{ + delete m_pPlayers; + delete m_pScenario; + delete m_pPing; +} + +void CBrowserItem::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, wide, tall; + m_pPing->GetBounds( x, y, wide, tall ); + + m_pScenario->SizeToContents(); + int sx, sy; + m_pScenario->GetPos( sx, sy ); + m_pScenario->SetPos( x - m_pScenario->GetWide() - m_nRightMargin, sy ); + + SetSize( x + wide, GetTall() ); +} + +void CBrowserItem::ApplySettings( KeyValues *pSettings ) +{ + BaseClass::ApplySettings( pSettings ); +} + +void CBrowserItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + Color fgcolor = pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ); + m_pPlayers->SetFgColor( fgcolor ); + m_pScenario->SetFgColor( fgcolor ); + + m_pPing->SetContentAlignment( vgui::Label::a_center ); + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "BrowserItem.res" ); + ApplySettings( pKeys ); + + SetFocus( false ); +} + +//----------------------------------------------------------------------- +// COptionsItem +// +// Menu item used to present a list of options for the player to select +// from, such as "choose a map" or "number of rounds". +//----------------------------------------------------------------------- +COptionsItem::COptionsItem( CDialogMenu *pParent, const char *pLabel ) + : BaseClass( pParent, pLabel, NULL ) +{ + m_nActiveOption = m_Options.InvalidIndex(); + m_nOptionsXPos = 0; + m_nMaxOptionWidth = 0; + + m_szOptionsFont[0] = '\0'; + m_hOptionsFont = vgui::INVALID_FONT; + + m_pLeftArrow = new vgui::Label( this, "LeftArrow", "" ); + m_pRightArrow = new vgui::Label( this, "RightArrow", "" ); +} + +COptionsItem::~COptionsItem() +{ + m_OptionLabels.PurgeAndDeleteElements(); + + delete m_pLeftArrow; + delete m_pRightArrow; +} + +void COptionsItem::PerformLayout() +{ + BaseClass::PerformLayout(); + + int optionWide = max( m_nOptionsMinWide, GetWide() - m_nOptionsXPos - m_pRightArrow->GetWide() - m_nOptionsLeftMargin ); + int optionTall = GetTall(); + + for ( int i = 0; i < m_OptionLabels.Count(); ++i ) + { + vgui::Label *pOption = m_OptionLabels[i]; + + pOption->SetBounds( m_nOptionsXPos, 0, optionWide, optionTall ); + } + + int lx, ly; + m_pLeftArrow->GetPos( lx, ly ); + m_pLeftArrow->SetPos( m_nOptionsXPos - m_nArrowGap - m_pLeftArrow->GetWide(), ly ); + + int rx, ry; + m_pRightArrow->GetPos( rx, ry ); + m_pRightArrow->SetPos( m_nOptionsXPos + optionWide + m_nArrowGap, ry ); + + m_pLeftArrow->SetAlpha( 255 ); + m_pRightArrow->SetAlpha( 255 ); + + if ( m_nActiveOption == 0 ) + { + m_pLeftArrow->SetAlpha( 32 ); + } + else if ( m_nActiveOption == m_OptionLabels.Count() - 1 ) + { + m_pRightArrow->SetAlpha( 32 ); + } +} + +void COptionsItem::ApplySettings( KeyValues *pSettings ) +{ + BaseClass::ApplySettings( pSettings ); + + m_nOptionsXPos = pSettings->GetInt( "optionsxpos", 0 ); + m_nOptionsMinWide = pSettings->GetInt( "optionsminwide", 0 ); + m_nOptionsLeftMargin = pSettings->GetInt( "optionsleftmargin", 0 ); + m_nArrowGap = pSettings->GetInt( "arrowgap", 0 ); + + Q_strncpy( m_szOptionsFont, pSettings->GetString( "optionsfont", "Default" ), sizeof( m_szOptionsFont ) ); +} + +void COptionsItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetPaintBackgroundEnabled( false ); + + m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 200, 184, 151, 255 ) ) ); + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsItem.res" ); + ApplySettings( pKeys ); + + m_hOptionsFont = pScheme->GetFont( m_szOptionsFont ); +} + +void COptionsItem::SetFocus( const bool bActive ) +{ + if ( bActive ) + { + for ( int i = 0; i < m_OptionLabels.Count(); ++i ) + { + m_OptionLabels[i]->SetBgColor( m_BgColorActive ); + } + } + else + { + for ( int i = 0; i < m_OptionLabels.Count(); ++i ) + { + m_OptionLabels[i]->SetBgColor( m_BgColor ); + } + } +} + +void COptionsItem::AddOption( const char *pLabelText, const sessionProperty_t& option ) +{ + // Add a new option to this item's list of options + m_Options.AddToTail( option ); + + int idx = m_OptionLabels.AddToTail( new vgui::Label( this, "Option Value", pLabelText ) ); + vgui::Label *pOption = m_OptionLabels[idx]; + + // Check for a format string + if ( Q_stristr( pLabelText, "Fmt" ) ) + { + wchar_t wszString[64]; + wchar_t wzNumber[8]; + wchar_t *wzFmt = g_pVGuiLocalize->Find( pLabelText ); + g_pVGuiLocalize->ConvertANSIToUnicode( option.szValue, wzNumber, sizeof( wzNumber ) ); + g_pVGuiLocalize->ConstructString( wszString, sizeof( wszString ), wzFmt, 1, wzNumber ); + pOption->SetText( wszString ); + } + + SETUP_PANEL( pOption ); + pOption->SetPaintBackgroundType( 2 ); + + pOption->SetFont( m_hOptionsFont ); + pOption->SetBgColor( Color( 46, 43, 42, 255 ) ); + pOption->SetFgColor( m_pTitle ? m_pTitle->GetFgColor() : Color( 200, 184, 151, 255 ) ); + pOption->SetTextInset( m_nOptionsLeftMargin, 0 ); + pOption->SetContentAlignment( vgui::Label::a_southwest ); + pOption->SizeToContents(); + + int wide = max( m_nOptionsMinWide, pOption->GetWide() ); + pOption->SetBounds( m_nOptionsXPos, 0, wide, GetTall() ); + m_nMaxOptionWidth = max( wide, m_nMaxOptionWidth ); + + SetWide( m_nOptionsXPos + m_nMaxOptionWidth + m_nOptionsLeftMargin * 2 + m_nArrowGap * 2 + m_pRightArrow->GetWide() ); +} + +//----------------------------------------------------------------------- +// Return the session property associated with the current active option +//----------------------------------------------------------------------- +const sessionProperty_t &COptionsItem::GetActiveOption() +{ + return m_Options[m_nActiveOption]; +} + +//----------------------------------------------------------------------- +// Return the index of the current active option +//----------------------------------------------------------------------- +int COptionsItem::GetActiveOptionIndex() +{ + return m_nActiveOption; +} + +//----------------------------------------------------------------------- +// Sets which option currently has focus +//----------------------------------------------------------------------- +void COptionsItem::SetOptionFocus( unsigned int idx ) +{ + unsigned int itemCt = (unsigned int)m_OptionLabels.Count(); + if ( idx > itemCt ) + return; + + m_nActiveOption = idx; + + for ( unsigned int i = 0; i < itemCt; ++i ) + { + vgui::Label *pLabel = m_OptionLabels[i]; + + const bool bVisible = ( i == idx ); + pLabel->SetVisible( bVisible ); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------- +// Move focus to the next option - does not wrap +//----------------------------------------------------------------------- +void COptionsItem::SetOptionFocusNext() +{ + if ( m_nActiveOption + 1 < m_OptionLabels.Count() ) + { + SetOptionFocus( m_nActiveOption + 1 ); + } + else + { + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } +} + +//----------------------------------------------------------------------- +// Move focus to the previous option - does not wrap +//----------------------------------------------------------------------- +void COptionsItem::SetOptionFocusPrev() +{ + if ( m_nActiveOption > 0 ) + { + SetOptionFocus( m_nActiveOption - 1 ); + } + else + { + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } +} + + +//----------------------------------------------------------------------- +// CAchievementItem +// +// Menu item used to present an achievement - including image, title, +// description, points and unlock date. Clicking the item opens another +// dialog with additional information about the achievement. +//----------------------------------------------------------------------- +CAchievementItem::CAchievementItem( CDialogMenu *pParent, const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement ) + : BaseClass( pParent, "", "" ) +{ + // Title and description were returned as results of a system query, + // and are therefore already localized. + m_pTitle->SetText( pName ); + + if ( IsX360() ) + { + wchar_t buf[120]; + + // Get the screen size + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + + unsigned int iWrapLen; + + if ( tall <= 480 ) + { + iWrapLen = 50; + } + else + { + iWrapLen = 65; + } + + // let's do some wrapping on this label + wcsncpy( buf, pDesc, sizeof(buf) / sizeof( wchar_t ) ); + + if ( wcslen(buf) > iWrapLen ) + { + int iPos = iWrapLen; + + while ( iPos > 0 && buf[iPos] != L' ' ) + { + iPos--; + } + + if ( iPos > 0 && buf[iPos] == L' ' ) + { + buf[iPos] = L'\n'; + } + } + + m_pDescription->SetText( buf ); + } + else + { + m_pDescription->SetText( pDesc ); + } + + m_pSourceAchievement = pSourceAchievement; + + m_pPercentageBarBackground = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBarBackground" ) ); + m_pPercentageBar = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBar" ) ); + m_pPercentageText = SETUP_PANEL( new vgui::Label( this, "PercentageText", "" ) ); + + // Set the status icons + m_pLockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "lockedicon" ) ); + m_pUnlockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "unlockedicon" ) ); + + // Gamerscore number + if ( IsX360() ) + { + wchar_t *wzFormat = g_pVGuiLocalize->Find( "#GameUI_Achievement_Points" ); // "%s1G" + wchar_t wzPoints[10]; + V_snwprintf( wzPoints, ARRAYSIZE( wzPoints ), L"%d", points ); + wchar_t wzPointsLayout[10]; + g_pVGuiLocalize->ConstructString( wzPointsLayout, sizeof( wzPointsLayout ), wzFormat, 1, wzPoints ); + m_pPoints = new vgui::Label( this, "Points", wzPointsLayout ); + } + + // Achievement image + m_pImage = new vgui::ImagePanel( this, "icon" ); +} + +CAchievementItem::~CAchievementItem() +{ + delete m_pImage; + delete m_pPoints; + delete m_pLockedIcon; + delete m_pUnlockedIcon; + delete m_pPercentageBarBackground; + delete m_pPercentageBar; + delete m_pPercentageText; +} + +void CAchievementItem::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y; + + m_pPoints->SizeToContents(); + m_pPoints->GetPos( x, y ); + x = GetWide() - m_pPoints->GetWide() - m_nRightMargin; + m_pPoints->SetPos( x, y ); + +} + +void CAchievementItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues*pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "AchievementItem.res" ); + ApplySettings( pKeys ); + + m_pImage->SetBgColor( Color( 32, 32, 32, 255 ) ); + m_pImage->SetFgColor( Color( 32, 32, 32, 255 ) ); + m_pImage->SetPaintBackgroundEnabled( true ); + + m_pPoints->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) ); + + // Set icon image + LoadAchievementIcon( m_pImage, m_pSourceAchievement ); + + // Percentage completion bar (for progressive achievements) + UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar ); + + if ( m_pSourceAchievement && m_pSourceAchievement->IsAchieved() ) + { + m_pLockedIcon->SetVisible( false ); + m_pUnlockedIcon->SetVisible ( true ); + m_pImage->SetVisible( true ); + } + else + { + m_pLockedIcon->SetVisible( true ); + m_pUnlockedIcon->SetVisible( false ); + m_pImage->SetVisible( false ); + } +} + +//----------------------------------------------------------------------- +// CSectionedItem +// +// Menu item used to display some number of data entries, which are arranged +// into columns. Supports scrolling through columns horizontally with the +// ability to "lock" columns so they don't scroll +//----------------------------------------------------------------------- +CSectionedItem::CSectionedItem( CDialogMenu *pParent, const char **ppEntries, int ct ) + : BaseClass( pParent, "", NULL, "SelectSession" ) +{ + m_bHeader = false; + for ( int i = 0; i < ct; ++i ) + { + AddSection( ppEntries[i], m_pParent->GetColumnAlignment( i ) ); + } +} + +CSectionedItem::~CSectionedItem() +{ + ClearSections(); +} + +void CSectionedItem::ClearSections() +{ + for ( int i = 0; i < m_Sections.Count(); ++i ) + { + section_s &sec = m_Sections[i]; + delete sec.pLabel; + } +} + +void CSectionedItem::PerformLayout() +{ + BaseClass::PerformLayout(); + + int tall = GetTall(); + for ( int i = 0; i < m_Sections.Count(); ++i ) + { + vgui::Label *pLabel = m_Sections[i].pLabel; + if ( !m_bHeader ) + { + pLabel->SetFont( m_pParent->GetColumnFont(i) ); + pLabel->SetFgColor( m_pParent->GetColumnColor(i) ); + } + pLabel->SetBounds( m_pParent->GetColumnXPos(i), 0, m_pParent->GetColumnWide(i), tall ); + pLabel->SetTextInset( 10, m_bHeader ? 5 : m_pParent->GetColumnYPos(i) ); // only use ypos for the y-inset if we're not a header + } +} + +void CSectionedItem::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); +} + +void CSectionedItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SectionedItem.res" ); + ApplySettings( pKeys ); + + int iLast = m_Sections.Count() -1; + SetWide( m_pParent->GetColumnXPos(iLast) + m_pParent->GetColumnWide(iLast) ); +} + +void CSectionedItem::AddSection( const char *pText, int align ) +{ + section_s sec; + sec.pLabel = new vgui::Label( this, "Section", pText ); + SETUP_PANEL( sec.pLabel ); + sec.pLabel->SetContentAlignment( (vgui::Label::Alignment)align ); + sec.pLabel->SetTextInset( 10, 0 ); + sec.pLabel->SetBgColor( Color( 209, 112, 52, 128 ) ); + m_Sections.AddToTail( sec ); +} + +void CSectionedItem::SetActiveColumn( int col ) +{ + for ( int i = 0; i < m_Sections.Count(); ++i ) + { + m_Sections[i].pLabel->SetPaintBackgroundEnabled( i == col ); + } +} + +//-------------------------------------------------------------------------------------- +// Generic menu for Xbox 360 matchmaking dialogs. Contains a list of CMenuItems arranged +// vertically. The user can navigate the list using the controller and click on any +// item. A clicked item may send a command to the dialog and the dialog responds accordingly. +//-------------------------------------------------------------------------------------- +CDialogMenu::CDialogMenu() : BaseClass( NULL, "DialogMenu" ) +{ + // Quiet "parent not sized yet" spew + SetSize( 100, 100 ); + + m_pParent = NULL; + m_pHeader = NULL; + m_bUseFilter = false; + m_bHasHeader = false; + m_nItemSpacing = 0; + m_nMinWide = 0; + m_nActive = -1; + m_nActiveColumn = -1; + m_nBaseRowIdx = 0; + m_nBaseColumnIdx = 0; + m_iUnlocked = 0; + m_nMaxVisibleItems = 1000; // arbitrarily large + m_nMaxVisibleColumns = 1000;// arbitrarily large +} + +CDialogMenu::~CDialogMenu() +{ + m_MenuItems.PurgeAndDeleteElements(); + delete m_pHeader; +} + +void CDialogMenu::SetParent( CBaseDialog *pParent ) +{ + BaseClass::SetParent( pParent ); + m_pParent = pParent; +} + +//-------------------------------------------------------------------------------------- +// Set a filter to use when reading in menu item keyvalues +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetFilter( const char *pFilter ) +{ + if ( pFilter ) + { + Q_strncpy( m_szFilter, pFilter, sizeof( m_szFilter ) ); + m_bUseFilter = true; + } + else + { + m_bUseFilter = false; + } +} + +//-------------------------------------------------------------------------------------- +// Add a new menu item to the item array +//-------------------------------------------------------------------------------------- +CMenuItem *CDialogMenu::AddItemInternal( CMenuItem *pItem ) +{ + int idx = m_MenuItems.AddToTail( pItem ); + + SETUP_PANEL( pItem ); + + return m_MenuItems[idx]; +} + +//-------------------------------------------------------------------------------------- +// Add a new menu item of some type that derives from CMenuItem +//-------------------------------------------------------------------------------------- +CCommandItem *CDialogMenu::AddCommandItem( const char *pTitleLabel, const char *pDescLabel, const char *pCommand ) +{ + return (CCommandItem*)AddItemInternal( new CCommandItem( this, pTitleLabel, pDescLabel, pCommand ) ); +} + +CBrowserItem *CDialogMenu::AddBrowserItem( const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing ) +{ + // Results are added to the menu at runtime, so the layout needs to be updated after each addition. + CBrowserItem *pItem = (CBrowserItem*)AddItemInternal( new CBrowserItem( this, pHost, pPlayers, pScenario, pPing ) ); + PerformLayout(); + return pItem; +} + +COptionsItem *CDialogMenu::AddOptionsItem( const char *pLabel ) +{ + return (COptionsItem*)AddItemInternal( new COptionsItem( this, pLabel ) ); +} + +CAchievementItem *CDialogMenu::AddAchievementItem( const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement ) +{ + return (CAchievementItem*)AddItemInternal( new CAchievementItem( this, pName, pDesc, points, bUnlocked, pSourceAchievement ) ); +} + +CSectionedItem *CDialogMenu::AddSectionedItem( const char **ppEntries, int ct ) +{ + CSectionedItem *pItem = (CSectionedItem*)AddItemInternal( new CSectionedItem( this, ppEntries, ct ) ); + PerformLayout(); + return pItem; +} + +CPlayerItem *CDialogMenu::AddPlayerItem( const char *pTitleLabel, int64 nId, byte bVoice, bool bReady ) +{ + // Players are added to the lobby at runtime, so the layout needs to be updated after each addition. + CPlayerItem *pItem = (CPlayerItem*)AddItemInternal( new CPlayerItem( this, pTitleLabel, nId, bVoice, bReady ) ); + PerformLayout(); + return pItem; +} + +void CDialogMenu::RemovePlayerItem( int idx ) +{ + delete m_MenuItems[idx]; + m_MenuItems.Remove( idx ); + PerformLayout(); +} + +void CDialogMenu::ClearItems() +{ + m_MenuItems.PurgeAndDeleteElements(); + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------- +// Set the size an position of all the menu items +//-------------------------------------------------------------------------------------- +void CDialogMenu::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Position the menu items and set their width + int yPos = 0; + int wide = GetWide(); + + if ( m_bHasHeader ) + { + yPos = 40; + m_pHeader->SetPos( 0, 0 ); + m_pHeader->SetWide( wide ); + m_pHeader->PerformLayout(); + } + + for ( int i = 0; i < m_MenuItems.Count(); ++i ) + { + CMenuItem *pItem = m_MenuItems[i]; + + pItem->SetPos( 0, yPos ); + pItem->SetWide( wide ); + pItem->SetActiveColumn( m_nActiveColumn ); + pItem->PerformLayout(); + + if ( i < m_nBaseRowIdx || i > m_nBaseRowIdx + m_nMaxVisibleItems - 1 ) + { + pItem->SetVisible( false ); + } + else + { + pItem->SetVisible( true ); + yPos += pItem->GetTall() + m_nItemSpacing; + } + } + + // Reset the focus to update background colors of all menu items + SetFocus( m_nActive ); + + + SetTall( yPos ); +} + +//-------------------------------------------------------------------------------------- +// Parse the res file for menu items to build out the dialog menu. +//-------------------------------------------------------------------------------------- +void CDialogMenu::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + m_nItemSpacing = pResourceData->GetInt( "itemspacing", 2 ); + m_nMinWide = pResourceData->GetInt( "minwide", 0 ); + m_nActiveColumn = pResourceData->GetInt( "activecolumn", -1 ); + m_nMaxVisibleItems = pResourceData->GetInt( "maxvisibleitems", 1000 ); // arbitrarily large + m_nMaxVisibleColumns = pResourceData->GetInt( "maxvisiblecolumns", 1000 ); // arbitrarily large + + KeyValues *pColumnData = pResourceData->FindKey( "Columns" ); + if ( pColumnData ) + { + int xPos = 0; + int idx = 0; + const char *ppHeader[MAX_COLUMNS]; + for ( KeyValues *pColumn = pColumnData->GetFirstSubKey(); pColumn != NULL; pColumn = pColumn->GetNextKey() ) + { + if ( !Q_stricmp( pColumn->GetName(), "Column" ) ) + { + columninfo_s col; + col.bSortDown = true; + col.xpos = pColumn->GetInt( "xpos", xPos ); + col.ypos = pColumn->GetInt( "ypos", 0 ); + col.wide = pColumn->GetInt( "wide", 0 ); + col.align = pColumn->GetInt( "align", 3 ); // west by default + col.bLocked = pColumn->GetInt( "locked", 0 ); + col.hFont = m_pScheme->GetFont( pColumn->GetString( "font", "default" ) ); + col.color = m_pScheme->GetColor( pColumn->GetString( "fgcolor" ), Color( 0, 0, 0, 255 ) ); + + ppHeader[idx++] = pColumn->GetString( "header", "" ); + + xPos = col.xpos + col.wide; + m_Columns.AddToTail( col ); + + if ( col.bLocked ) + { + m_nBaseColumnIdx = idx; + m_iUnlocked = idx; + } + } + } + m_bHasHeader = true; + m_pHeader = new CSectionedItem( this, ppHeader, idx ); + m_pHeader->m_bHeader = true; + SETUP_PANEL( m_pHeader ); + + m_pHeader->SetPaintBackgroundEnabled( false ); + vgui::HFont headerFont = m_pScheme->GetFont( pColumnData->GetString( "headerfont", "default" ) ); + Color headerColor = m_pScheme->GetColor( pColumnData->GetString( "headerfgcolor" ), Color( 0, 0, 0, 255 ) ); + for ( int i = 0; i < idx; ++i ) + { + vgui::Label *pLabel = m_pHeader->m_Sections[i].pLabel; + pLabel->SetFont( headerFont ); + pLabel->SetFgColor( headerColor ); + pLabel->SetPaintBackgroundEnabled( false ); + } + } + + for ( KeyValues *pMenuData = pResourceData->GetFirstSubKey(); pMenuData != NULL; pMenuData = pMenuData->GetNextKey() ) + { + // See if we should skip over this block + if ( m_bUseFilter ) + { + if ( pMenuData->GetInt( m_szFilter, 0 ) == 0 ) + continue; + } + + // Give our parent a chance to change the properties of this item + m_pParent->OverrideMenuItem( pMenuData ); + + if ( !Q_stricmp( pMenuData->GetName(), "CommandItem" ) ) + { + // New Command Item + const char *label = pMenuData->GetString( "label", "<unknown>" ); + const char *description = pMenuData->GetString( "description", NULL ); + const char *command = pMenuData->GetString( "command", "<unknown>" ); + + AddCommandItem( label, description, command ); + } + else if ( !Q_stricmp( pMenuData->GetName(), "OptionsItem" ) ) + { + // New Options Item + COptionsItem *pItem = AddOptionsItem( pMenuData->GetString( "label", "<unknown>" ) ); + + // ID and ValueType and the same for all option values + const char *pID = pMenuData->GetString( "id", "NULL" ); + const char *pValueType = pMenuData->GetString( "valuetype", NULL ); + + // Add all the options + for ( KeyValues *pValue = pMenuData->GetFirstSubKey(); pValue != NULL; pValue = pValue->GetNextKey() ) + { + if ( !Q_stricmp( pValue->GetName(), "Option" ) ) + { + sessionProperty_t prop; + prop.nType = SESSION_CONTEXT; + Q_strncpy( prop.szID, pID, sizeof( prop.szID ) ); + Q_strncpy( prop.szValue, pValue->GetString( "value", "NULL" ), sizeof( prop.szValue ) ); + + if ( pValueType ) + { + // Only session properties have a type + prop.nType = SESSION_PROPERTY; + Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) ); + } + + const char *pLabel = pValue->GetString( "label", "<unknown>" ); + pItem->AddOption( pLabel, prop ); + } + } + + // Add range items after the specified items + if ( pMenuData->GetInt( "userange" ) ) + { + // Options are an implicit range of integers + int nStart = pMenuData->GetInt( "rangelow" ); + int nEnd = pMenuData->GetInt( "rangehigh" ); + int nInterval = pMenuData->GetInt( "interval", 1 ); + + // Prevent total destruction from a bad resource file + if ( nEnd < nStart ) + { + nEnd = nStart; + } + + for ( int i = nStart; i <= nEnd; i += nInterval ) + { + sessionProperty_t prop; + prop.nType = SESSION_PROPERTY; + Q_strncpy( prop.szID, pID, sizeof( prop.szID ) ); + Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) ); + Q_snprintf( prop.szValue, sizeof(prop.szValue), "%d", i ); + + pItem->AddOption( prop.szValue, prop ); + } + } + + // Set the default active option + int active = pMenuData->GetInt( "activeoption", 0 ); + pItem->SetOptionFocus( active ); + + // Notify our parent that each option has been set to its current setting + KeyValues *kv = new KeyValues( "MenuItemChanged", "item", GetItemCount() - 1 ); + PostActionSignal( kv ); + } + } + + // Calculate the final menu size according to the widest menu item + int wide = m_nMinWide; + for ( int i = 0; i < m_MenuItems.Count(); ++i ) + { + wide = max( wide, m_MenuItems[i]->GetWide() ); + } + SetWide( wide ); +} + +//-------------------------------------------------------------------------------------- +// Cache off the scheme +//-------------------------------------------------------------------------------------- +void CDialogMenu::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pScheme = pScheme; +} + +//-------------------------------------------------------------------------------------- +// Give focus (highlights) a particular menu item by index +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetFocus( int idx ) +{ + int itemCt = (unsigned int)m_MenuItems.Count(); + if ( idx >= itemCt ) + return; + + for ( int i = 0; i < itemCt; ++i ) + { + m_MenuItems[i]->SetFocus( i == idx ); + } + m_nActive = idx; + + if ( m_nActive >= 0 && m_nActive < m_nBaseRowIdx ) + { + m_nBaseRowIdx = m_nActive; + } + else if ( m_nActive > m_nBaseRowIdx + m_nMaxVisibleItems - 1 ) + { + m_nBaseRowIdx = m_nActive - ( m_nMaxVisibleItems - 1 ); + } +} + +//-------------------------------------------------------------------------------------- +// Sort the menu items according to the selected column +//-------------------------------------------------------------------------------------- +void CDialogMenu::SortMenuItems() +{ + if ( !m_bHasHeader ) + return; + + // Simple bubble sort + char szBufferOne[32]; + char szBufferTwo[32]; + bool bSortDown = GetColumnSortType( m_nActiveColumn ); + for ( int i = 1; i <= m_MenuItems.Count(); ++i ) + { + for ( int j = 0; j < m_MenuItems.Count() - i; ++j ) + { + ((CSectionedItem*)m_MenuItems[j])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferOne, sizeof( szBufferOne ) ); + ((CSectionedItem*)m_MenuItems[j+1])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferTwo, sizeof( szBufferTwo ) ); + + int diff = Q_stricmp( szBufferOne, szBufferTwo ); + bool bSwap = bSortDown ? diff > 0 : diff < 0; + if ( bSwap ) + { + CMenuItem *pTemp = m_MenuItems[j+1]; + m_MenuItems[j+1] = m_MenuItems[j]; + m_MenuItems[j] = pTemp; + + m_pParent->SwapMenuItems( j, j+1 ); + } + } + } + InvalidateLayout(); +} + +//-------------------------------------------------------------------------------------- +// Move item focus to the next item in the menu - supports wrapping +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetFocusNext() +{ + if ( m_MenuItems.Count() ) + { + int iNewIndex = ( m_nActive + 1 ) % m_MenuItems.Count(); + + int i = 0; + bool bSet = false; + while ( i < m_MenuItems.Count() ) + { + if ( m_MenuItems[iNewIndex]->IsEnabled() ) + { + SetFocus( iNewIndex ); + bSet = true; + break; + } + + iNewIndex = ( iNewIndex + 1 ) % m_MenuItems.Count(); + i++; + } + + InvalidateLayout(); + } +} + +//-------------------------------------------------------------------------------------- +// Move item focus to the previous item in the menu - supports wrapping +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetFocusPrev() +{ + if ( m_MenuItems.Count() ) + { + int iNewIndex = m_nActive - 1; + if ( iNewIndex < 0 ) + iNewIndex = m_MenuItems.Count() - 1; + + int i = 0; + bool bSet = false; + while ( i < m_MenuItems.Count() ) + { + if ( m_MenuItems[iNewIndex]->IsEnabled() ) + { + SetFocus( iNewIndex ); + bSet = true; + break; + } + + if ( --iNewIndex < 0 ) + iNewIndex = m_MenuItems.Count() - 1; + + i++; + } + + InvalidateLayout(); + } +} + +//-------------------------------------------------------------------------------------- +// For OptionsItems: Move focus to the next option in the menu item - does not wrap +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetOptionFocusNext() +{ + COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) ); + if ( pItem ) + { + pItem->SetOptionFocusNext(); + + KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive ); + PostActionSignal( kv ); + } +} + +//-------------------------------------------------------------------------------------- +// For OptionsItems: Move focus to the previous option in the menu item - does not wrap +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetOptionFocusPrev() +{ + COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) ); + if ( pItem ) + { + pItem->SetOptionFocusPrev(); + + KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive ); + PostActionSignal( kv ); + } +} + +//-------------------------------------------------------------------------------------- +// For OptionsItems: Update the base index for the columns +//-------------------------------------------------------------------------------------- +void CDialogMenu::UpdateBaseColumnIndex() +{ + if ( m_iUnlocked + m_nActiveColumn - m_nBaseColumnIdx >= m_nMaxVisibleColumns ) + { + m_nBaseColumnIdx = m_iUnlocked + m_nActiveColumn - m_nMaxVisibleColumns + 1; + } + else if ( m_nActiveColumn - m_nBaseColumnIdx < 0 ) + { + m_nBaseColumnIdx = m_nActiveColumn; + } +} + +//-------------------------------------------------------------------------------------- +// For menus with sectioned columns - move focus to the next column +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetColumnFocusNext() +{ + if ( m_nActiveColumn == -1 ) + return; + + if ( m_Columns.Count() ) + { + int iNewColumn = m_nActiveColumn + 1; + if ( iNewColumn >= m_Columns.Count() ) + return; + + m_nActiveColumn = iNewColumn; + UpdateBaseColumnIndex(); + InvalidateLayout(); + } +} + +//-------------------------------------------------------------------------------------- +// For menus with sectioned columns - move focus to the next column +//-------------------------------------------------------------------------------------- +void CDialogMenu::SetColumnFocusPrev() +{ + if ( m_nActiveColumn == -1 ) + return; + + if ( m_Columns.Count() ) + { + int iNewColumn = m_nActiveColumn - 1; + if ( iNewColumn < 0 || m_Columns[iNewColumn].bLocked ) + return; + + m_nActiveColumn = iNewColumn; + UpdateBaseColumnIndex(); + InvalidateLayout(); + } +} + +//-------------------------------------------------------------------------------------- +// For OptionsItems: Lets the dialog find out which option is currently selected +//-------------------------------------------------------------------------------------- +int CDialogMenu::GetActiveOptionIndex( int nMenuItemIdx ) +{ + int retval = -1; + COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( nMenuItemIdx ) ); + if ( pItem ) + { + retval = pItem->GetActiveOptionIndex(); + } + return retval; +} + +//----------------------------------------------------------------------- +// Return the index of the current active menu item +//----------------------------------------------------------------------- +int CDialogMenu::GetActiveItemIndex() +{ + return m_nActive; +} + +//----------------------------------------------------------------------- +// Return the index of the current active menu column +//----------------------------------------------------------------------- +int CDialogMenu::GetActiveColumnIndex() +{ + return m_nActiveColumn; +} + +//----------------------------------------------------------------------- +// Return the number of menu items +//----------------------------------------------------------------------- +int CDialogMenu::GetItemCount() +{ + return m_MenuItems.Count(); +} + +//----------------------------------------------------------------------- +// Return the number of visible menu items +//----------------------------------------------------------------------- +int CDialogMenu::GetVisibleItemCount() +{ + return min( GetItemCount(), m_nMaxVisibleItems ); +} + +//----------------------------------------------------------------------- +// Return the number of visible menu columns +//----------------------------------------------------------------------- +int CDialogMenu::GetVisibleColumnCount() +{ + return m_nMaxVisibleColumns; +} + +//----------------------------------------------------------------------- +// Return the index of the first unlocked column +//----------------------------------------------------------------------- +int CDialogMenu::GetFirstUnlockedColumnIndex() +{ + return m_iUnlocked; +} + +//----------------------------------------------------------------------- +// Return the first visible index in the menu +//----------------------------------------------------------------------- +int CDialogMenu::GetBaseRowIndex() +{ + return m_nBaseRowIdx; +} + +//----------------------------------------------------------------------- +// Set the first visible index in the menu +//----------------------------------------------------------------------- +void CDialogMenu::SetBaseRowIndex( int idx ) +{ + m_nBaseRowIdx = idx; +} + +//----------------------------------------------------------------------- +// Return the specified menu item +//----------------------------------------------------------------------- +CMenuItem *CDialogMenu::GetItem( int idx ) +{ + if ( m_MenuItems.IsValidIndex( idx ) ) + { + return m_MenuItems[idx]; + } + return NULL; +} + +//----------------------------------------------------------------------- +// Return the specified column xpos +//----------------------------------------------------------------------- +int CDialogMenu::GetColumnXPos( int idx ) +{ + // Compensate for scrolling offsets + columninfo_s &col = m_Columns[idx]; + + int xpos; + if ( col.bLocked ) + { + xpos = m_Columns[idx].xpos; + } + else + { + int trueIdx = m_iUnlocked + idx - m_nBaseColumnIdx; + if ( trueIdx < m_iUnlocked ) + { + // Put it offscreen + xpos = -100 - col.wide; + } + else + { + xpos = m_Columns[trueIdx].xpos; + } + } + return xpos; +} + +//----------------------------------------------------------------------- +// Return the specified column ypos +//----------------------------------------------------------------------- +int CDialogMenu::GetColumnYPos( int idx ) +{ + return m_Columns[idx].ypos; +} + +//----------------------------------------------------------------------- +// Return the specified column width +//----------------------------------------------------------------------- +int CDialogMenu::GetColumnWide( int idx ) +{ + return m_Columns[idx].wide; +} + +//----------------------------------------------------------------------- +// Return the specified column alignment +//----------------------------------------------------------------------- +int CDialogMenu::GetColumnAlignment( int idx ) +{ + return m_Columns[idx].align; +} + +//----------------------------------------------------------------------- +// Return the specified column font +//----------------------------------------------------------------------- +HFont CDialogMenu::GetColumnFont( int idx ) +{ + return m_Columns[idx].hFont; +} + +//----------------------------------------------------------------------- +// Return the specified column fgcolor +//----------------------------------------------------------------------- +Color CDialogMenu::GetColumnColor( int idx ) +{ + return m_Columns[idx].color; +} + +//----------------------------------------------------------------------- +// Return the specified column fgcolor +//----------------------------------------------------------------------- +bool CDialogMenu::GetColumnSortType( int idx ) +{ + bool bSortDown = m_Columns[idx].bSortDown; + m_Columns[idx].bSortDown = !bSortDown; + return bSortDown; +} + +//-------------------------------------------------------------------------------------- +// Receive the command from a clicked menu item and forwards it to the parent dialog +//-------------------------------------------------------------------------------------- +void CDialogMenu::OnCommand( const char *pCommand ) +{ + GetParent()->OnCommand( pCommand ); +} + +//-------------------------------------------------------------------------------------- +// Update the menu state according to controller input. +// Returns whether or not the keycode was handled by the menu. +//-------------------------------------------------------------------------------------- +bool CDialogMenu::HandleKeyCode( vgui::KeyCode code ) +{ + switch( code ) + { + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + SetFocusNext(); + break; + + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case STEAMCONTROLLER_DPAD_UP: + SetFocusPrev(); + break; + + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + SetOptionFocusPrev(); + SetColumnFocusPrev(); + break; + + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + SetOptionFocusNext(); + SetColumnFocusNext(); + break; + + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: + if ( m_MenuItems.Count() && m_nActive >= 0 ) + { + m_MenuItems[m_nActive]->OnClick(); + } + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + SortMenuItems(); + break; + + default: + // Not handled + return false; + } + + return true; +} + diff --git a/gameui/matchmaking/dialogmenu.h b/gameui/matchmaking/dialogmenu.h new file mode 100644 index 0000000..83940d9 --- /dev/null +++ b/gameui/matchmaking/dialogmenu.h @@ -0,0 +1,367 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DIALOGMENU_H +#define DIALOGMENU_H +#ifdef _WIN32 +#pragma once +#endif + +#if defined(_WIN32) && !defined(_X360) +#include "winlite.h" // FILETIME +#endif + +#include "vgui_controls/Panel.h" +#include "vgui_controls/Frame.h" + +class IAchievement; + +#define MAX_COMMAND_LEN 256 +#define MAX_COLUMNS 32 + +class CDialogMenu; +class CBaseDialog; + +struct sessionProperty_t +{ + static const int MAX_KEY_LEN = 64; + byte nType; + char szID[MAX_KEY_LEN]; + char szValue[MAX_KEY_LEN]; + char szValueType[MAX_KEY_LEN]; +}; + +//----------------------------------------------------------------------- +// Base class representing a generic menu item. Supports two text labels, +// where the first label is the "action" text and the second is an optional +// description of the action. +//----------------------------------------------------------------------- +class CMenuItem : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CMenuItem, vgui::EditablePanel ); + +public: + CMenuItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription ); + virtual ~CMenuItem(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pSettings ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void SetFocus( const bool bActive ); + virtual void SetEnabled( bool bEnabled ); + virtual void SetActiveColumn( int col ); + virtual bool IsEnabled(); + virtual void OnClick(); + +protected: + CDialogMenu *m_pParent; + + vgui::Label *m_pTitle; + vgui::Label *m_pDescription; + + Color m_BgColor; + Color m_BgColorActive; + + int m_nDisabledAlpha; + int m_nBottomMargin; + int m_nRightMargin; + + bool m_bEnabled; +}; + +//----------------------------------------------------------------------- +// CCommandItem +// +// Menu item that issues a command when clicked. +//----------------------------------------------------------------------- +class CCommandItem : public CMenuItem +{ + DECLARE_CLASS_SIMPLE( CCommandItem, CMenuItem ); + +public: + CCommandItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription, const char *pCommand ); + virtual ~CCommandItem(); + + virtual void OnClick(); + virtual void SetFocus( const bool bActive ); + + bool m_bHasFocus; + + char m_szCommand[MAX_PATH]; +}; + +//----------------------------------------------------------------------- +// CPlayerItem +// +// Menu item to display a player in the lobby. +//----------------------------------------------------------------------- +class CPlayerItem : public CCommandItem +{ + DECLARE_CLASS_SIMPLE( CMenuItem, CCommandItem ); + +public: + CPlayerItem( CDialogMenu *pParent, const char *pTitle, int64 nId, byte bVoice, bool bReady ); + virtual ~CPlayerItem(); + + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual void OnClick(); + + vgui::Label *m_pVoiceIcon; + vgui::Label *m_pReadyIcon; + + byte m_bVoice; + bool m_bReady; + uint64 m_nId; +}; + +//----------------------------------------------------------------------- +// CBrowserItem +// +// Menu item used to display session search results, etc. +//----------------------------------------------------------------------- +class CBrowserItem : public CCommandItem +{ + DECLARE_CLASS_SIMPLE( CBrowserItem, CCommandItem ); + +public: + CBrowserItem( CDialogMenu *pParent, const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing ); + virtual ~CBrowserItem(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pSettings ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +private: + vgui::Label *m_pPlayers; + vgui::Label *m_pScenario; + vgui::Label *m_pPing; +}; + +//----------------------------------------------------------------------- +// COptionsItem +// +// Menu item used to present a list of options for the player to select +// from, such as "choose a map" or "number of rounds". +//----------------------------------------------------------------------- +class COptionsItem : public CMenuItem +{ + DECLARE_CLASS_SIMPLE( COptionsItem, CMenuItem ); + +public: + COptionsItem( CDialogMenu *pParent, const char *pLabel ); + virtual ~COptionsItem(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pSettings ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void SetFocus( const bool bActive ); + + void SetOptionFocus( unsigned int idx ); + void SetOptionFocusNext(); + void SetOptionFocusPrev(); + + void AddOption( const char *pLabelText, const sessionProperty_t &option ); + int GetActiveOptionIndex(); + const sessionProperty_t &GetActiveOption(); + + void DeleteAllOptions() + { + m_Options.RemoveAll(); + m_OptionLabels.PurgeAndDeleteElements(); + m_nActiveOption = m_Options.InvalidIndex(); + } +private: + int m_nActiveOption; + int m_nOptionsXPos; + int m_nOptionsMinWide; + int m_nOptionsLeftMargin; + int m_nMaxOptionWidth; + int m_nArrowGap; + + CUtlVector< vgui::Label* > m_OptionLabels; + CUtlVector< sessionProperty_t > m_Options; + + char m_szOptionsFont[64]; + vgui::HFont m_hOptionsFont; + + vgui::Label *m_pLeftArrow; + vgui::Label *m_pRightArrow; +}; + +//----------------------------------------------------------------------- +// CAchievementItem +// +// Menu item used to present an achievement - including image, title, +// description, points and unlock date. Clicking the item opens another +// dialog with additional information about the achievement. +//----------------------------------------------------------------------- +class CAchievementItem : public CMenuItem +{ + DECLARE_CLASS_SIMPLE( CAchievementItem, CMenuItem ); + +public: + CAchievementItem( CDialogMenu *pParent, const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement ); + virtual ~CAchievementItem(); + + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +private: + vgui::Label *m_pPoints; + vgui::ImagePanel *m_pLockedIcon; + vgui::ImagePanel *m_pUnlockedIcon; + vgui::ImagePanel *m_pImage; + + vgui::ImagePanel *m_pPercentageBarBackground; + vgui::ImagePanel *m_pPercentageBar; + vgui::Label *m_pPercentageText; + + IAchievement *m_pSourceAchievement; + + Color m_AchievedBGColor; + Color m_UnachievedBGColor; + + CPanelAnimationVar( Color, m_clrProgressBar, "ProgressBarColor", "140 140 140 255" ); +}; + +//----------------------------------------------------------------------- +// CSectionedItem +// +// Menu item used to display some number of data entries, which are arranged +// into columns. Supports scrolling through columns horizontally with the +// ability to "lock" columns so they don't scroll +//----------------------------------------------------------------------- +class CSectionedItem : public CCommandItem +{ + DECLARE_CLASS_SIMPLE( CSectionedItem, CCommandItem ); + +public: + CSectionedItem( CDialogMenu *pParent, const char **ppEntries, int ct ); + virtual ~CSectionedItem(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *pSettings ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual void SetActiveColumn( int col ); + + void ClearSections(); + void AddSection( const char *pText, int wide ); + + struct section_s + { + int wide; + vgui::Label *pLabel; + }; + CUtlVector< section_s >m_Sections; + + bool m_bHeader; +}; + +//-------------------------------------------------------------------------------------- +// Generic menu for Xbox 360 matchmaking dialogs. Contains a list of CMenuItems arranged +// vertically. The user can navigate the list using the controller and click on any +// item. A clicked item may send a command to the dialog and the dialog responds accordingly. +//-------------------------------------------------------------------------------------- +class CDialogMenu : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CDialogMenu, vgui::Panel ); + +public: + CDialogMenu(); + ~CDialogMenu(); + + virtual void OnCommand( const char *pCommand ); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PerformLayout(); + void SetFilter( const char *pFilter ); + virtual bool HandleKeyCode( vgui::KeyCode code ); + void SetMaxVisibleItems( uint nMaxVisibleItems ); + void SetParent( CBaseDialog *pParent ); + + // Menu items + CCommandItem *AddCommandItem( const char *pTitleLabel, const char *pDescLabel, const char *pCommand ); + CPlayerItem *AddPlayerItem( const char *pTitleLabel, int64 nId, byte bVoice, bool bReady ); + CBrowserItem *AddBrowserItem( const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing ); + COptionsItem *AddOptionsItem( const char *pLabel ); + CSectionedItem *AddSectionedItem( const char **ppEntries, int ct ); + CAchievementItem *AddAchievementItem( const wchar_t *pName, const wchar_t *pDesc, uint cred, bool bUnlocked, IAchievement* pSourceAchievement ); + CMenuItem *AddItemInternal( CMenuItem *pItem ); + + void RemovePlayerItem( int idx ); + void SortMenuItems(); + void ClearItems(); + + // Navigation + void SetFocus( int idx ); + void SetFocusNext(); + void SetFocusPrev(); + void SetOptionFocusNext(); + void SetOptionFocusPrev(); + void SetColumnFocusNext(); + void SetColumnFocusPrev(); + void UpdateBaseColumnIndex(); + + // Accessors + CMenuItem *GetItem( int idx); + int GetItemCount(); + int GetActiveItemIndex(); + int GetActiveColumnIndex(); + int GetActiveOptionIndex( int idx ); + int GetVisibleItemCount(); + int GetVisibleColumnCount(); + int GetFirstUnlockedColumnIndex(); + int GetBaseRowIndex(); + void SetBaseRowIndex( int idx ); + int GetColumnXPos( int idx ); + int GetColumnYPos( int idx ); + int GetColumnWide( int idx ); + int GetColumnAlignment( int idx ); + vgui::HFont GetColumnFont( int idx ); + Color GetColumnColor( int idx ); + bool GetColumnSortType( int idx ); + +private: + struct columninfo_s + { + int xpos; + int ypos; + int wide; + int align; + bool bLocked; + Color color; + vgui::HFont hFont; + bool bSortDown; + }; + CUtlVector< columninfo_s >m_Columns; + CUtlVector< CMenuItem* > m_MenuItems; + + CBaseDialog *m_pParent; + CSectionedItem *m_pHeader; + vgui::IScheme *m_pScheme; + + char m_szFilter[MAX_COMMAND_LEN]; // string to use as a keyvalues filter when reading in menu items + + int m_nItemSpacing; // gap between menu items + int m_nMinWide; // minimum width - final menu width will always be >= m_nMinWide + + bool m_bInitialized; + bool m_bUseFilter; + bool m_bHasHeader; + int m_nMaxVisibleItems; // max number of items to display in the menu + int m_nMaxVisibleColumns; // max number of columns to display in the menu + int m_nActiveColumn; // index of the current active column + int m_nBaseColumnIdx; // array index of the first non-static column + int m_nBaseRowIdx; // array index of the first visible row + int m_nActive; // index of the current active item + int m_iUnlocked; // first unlocked column in the menu +}; + +#endif // DIALOGMENU_H diff --git a/gameui/matchmaking/leaderboarddialog.cpp b/gameui/matchmaking/leaderboarddialog.cpp new file mode 100644 index 0000000..93885db --- /dev/null +++ b/gameui/matchmaking/leaderboarddialog.cpp @@ -0,0 +1,563 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Displays a leaderboard +// +//=============================================================================// + +#include "leaderboarddialog.h" +#include "vgui_controls/Label.h" +#include "vgui/ILocalize.h" +#include "hl2orange.spa.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define NUM_ROWS_PER_QUERY 100 + +CLeaderboardDialog *g_pLeaderboardDialog; + +//---------------------------------------------------------- +// CLeaderboardDialog +//---------------------------------------------------------- +CLeaderboardDialog::CLeaderboardDialog( vgui::Panel *pParent ) : BaseClass( pParent, "LeaderboardDialog" ) +{ + g_pLeaderboardDialog = this; + m_iBaseRank = 0; + m_iActiveRank = 0; + m_iMaxRank = 0; + m_cColumns = 0; + m_iRangeBase = 0; +#if defined( _X360 ) + m_pStats = NULL; +#endif + + m_pProgressBg = new vgui::Panel( this, "ProgressBg" ); + m_pProgressBar = new vgui::Panel( this, "ProgressBar" ); + m_pProgressPercent = new vgui::Label( this, "ProgressPercent", "" ); + m_pNumbering = new vgui::Label( this, "Numbering", "" ); + m_pUpArrow = new vgui::Label( this, "UpArrow", "" ); + m_pDownArrow = new vgui::Label( this, "DownArrow", "" ); + m_pBestMoments = new vgui::Label( this, "BestMoments", "" ); +} + +CLeaderboardDialog::~CLeaderboardDialog() +{ + CleanupStats(); + + delete m_pProgressBg; + delete m_pProgressBar; + delete m_pProgressPercent; + delete m_pNumbering; + delete m_pUpArrow; + delete m_pDownArrow; +} + +//---------------------------------------------------------- +// Clean up the stats array +//---------------------------------------------------------- +void CLeaderboardDialog::CleanupStats() +{ +#if defined( _X360 ) + if ( m_pStats ) + { + delete [] m_pStats; + m_pStats = NULL; + } +#endif +} + +//---------------------------------------------------------- +// Position the dialogs elements +//---------------------------------------------------------- +void CLeaderboardDialog::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + if ( m_cColumns ) + { + int x, y, wide, tall; + m_pProgressBg->GetBounds( x, y, wide, tall ); + + int columnWide = wide / m_cColumns; + int lockedColumns = m_Menu.GetFirstUnlockedColumnIndex(); + int visibleColumns = m_Menu.GetVisibleColumnCount() - lockedColumns; + int iColumn = m_Menu.GetActiveColumnIndex() - lockedColumns; + + if ( iColumn < 0 ) + { + iColumn = 0; + } + else if ( iColumn < m_iRangeBase ) + { + m_iRangeBase = iColumn; + } + else if ( iColumn >= m_iRangeBase + visibleColumns ) + { + m_iRangeBase = iColumn - visibleColumns + 1; + } + + m_pProgressBg->SetBounds( x, y, columnWide * m_cColumns, tall ); + m_pProgressBar->SetBounds( x + columnWide * m_iRangeBase, y, columnWide * visibleColumns, tall ); + } + else + { + m_pProgressBg->SetVisible( false ); + m_pProgressBar->SetVisible( false ); + } + + int menux, menuy; + m_Menu.GetPos( menux, menuy ); + + // Do a perform layout on the menu so we get the correct height now + m_Menu.InvalidateLayout( true, false ); + + m_pNumbering->SizeToContents(); + + wchar_t wszNumbering[64]; + wchar_t *wzNumberingFmt = g_pVGuiLocalize->Find( "#GameUI_Achievement_Menu_Range" ); + wchar_t wzActiveItem[8]; + wchar_t wzTotal[8]; + + int iActive = m_iBaseRank + m_Menu.GetActiveItemIndex(); + if ( iActive < 0 ) + { + iActive = 0; + } + V_snwprintf( wzActiveItem, ARRAYSIZE( wzActiveItem ), L"%d", iActive ); + V_snwprintf( wzTotal, ARRAYSIZE( wzTotal ), L"%d", m_iMaxRank ); + g_pVGuiLocalize->ConstructString( wszNumbering, sizeof( wszNumbering ), wzNumberingFmt, 2, wzActiveItem, wzTotal ); + m_pNumbering->SetText( wszNumbering ); + m_pNumbering->SetWide( GetWide() ); + + MoveToCenterOfScreen(); +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +void CLeaderboardDialog::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_DOWN, 0.05f ); + m_KeyRepeat.SetKeyRepeatTime( KEY_XSTICK1_DOWN, 0.05f ); + m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_UP, 0.05f ); + m_KeyRepeat.SetKeyRepeatTime( KEY_XSTICK1_UP, 0.05f ); +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +void CLeaderboardDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pProgressBg->SetBgColor( Color( 200, 184, 151, 255 ) ); + m_pProgressBar->SetBgColor( Color( 179, 82, 22, 255 ) ); + m_pNumbering->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) ); + m_pBestMoments->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) ); +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +void CLeaderboardDialog::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "CenterOnPlayer" ) ) + { + if ( GetPlayerStats( -1 ) == 0 ) + { + // Player isn't on the board, just start at rank 1 + GetPlayerStats( 1 ); + } + } + else if ( !Q_stricmp( pCommand, "Friends" ) ) + { + GetPlayerStats( -1, true ); + } + + BaseClass::OnCommand( pCommand ); +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +void CLeaderboardDialog::AddLeaderboardEntry( const char **ppEntries, int ct ) +{ + m_Menu.AddSectionedItem( ppEntries, ct ); +} + +//---------------------------------------------------------- +// Get some portion of the leaderboard. This should ideally live +// in the client, since it's very mod-specific +//---------------------------------------------------------- +bool CLeaderboardDialog::GetPlayerStats( int rank, bool bFriends ) +{ +#if defined _X360 + HANDLE handle; + + // Retrieve the necessary buffer size + DWORD cbResults = 0; + + bool bRanked = false; + const char *pName = GetName(); + if ( !Q_stricmp( pName, "LeaderboardDialog_Ranked" ) ) + { + bRanked = true; + } + + XUSER_STATS_SPEC spec; + if ( !bRanked ) + { + spec.dwViewId = STATS_VIEW_PLAYER_MAX_UNRANKED; + spec.dwNumColumnIds = 15; + spec.rgwColumnIds[0] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINTS_SCORED; + spec.rgwColumnIds[1] = STATS_COLUMN_PLAYER_MAX_UNRANKED_KILLS; + spec.rgwColumnIds[2] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINTS_CAPPED; + spec.rgwColumnIds[3] = STATS_COLUMN_PLAYER_MAX_UNRANKED_POINT_DEFENSES; + spec.rgwColumnIds[4] = STATS_COLUMN_PLAYER_MAX_UNRANKED_DOMINATIONS; + spec.rgwColumnIds[5] = STATS_COLUMN_PLAYER_MAX_UNRANKED_REVENGE; + spec.rgwColumnIds[6] = STATS_COLUMN_PLAYER_MAX_UNRANKED_BUILDINGS_DESTROYED; + spec.rgwColumnIds[7] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEADSHOTS; + spec.rgwColumnIds[8] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEALTH_POINTS_HEALED; + spec.rgwColumnIds[9] = STATS_COLUMN_PLAYER_MAX_UNRANKED_INVULNS; + spec.rgwColumnIds[10] = STATS_COLUMN_PLAYER_MAX_UNRANKED_KILL_ASSISTS; + spec.rgwColumnIds[11] = STATS_COLUMN_PLAYER_MAX_UNRANKED_BACKSTABS; + spec.rgwColumnIds[12] = STATS_COLUMN_PLAYER_MAX_UNRANKED_HEALTH_POINTS_LEACHED; + spec.rgwColumnIds[13] = STATS_COLUMN_PLAYER_MAX_UNRANKED_SENTRY_KILLS; + spec.rgwColumnIds[14] = STATS_COLUMN_PLAYER_MAX_UNRANKED_TELEPORTS; + m_cColumns = 15; + } + else + { + spec.dwViewId = STATS_VIEW_PLAYER_MAX_RANKED; + spec.dwNumColumnIds = 1; + spec.rgwColumnIds[ 0 ] = STATS_COLUMN_PLAYER_MAX_RANKED_POINTS_SCORED; + + // set to zero to hide the progress bar + m_cColumns = 0; + } + + DWORD ret; + XUID xuid = 0u; + XUID xuidFriends[NUM_ROWS_PER_QUERY]; + int xuidCount = 1; + + if ( !bFriends ) + { + if ( rank == -1 ) + { + // Center on the player's xuid + XUserGetXUID( XBX_GetPrimaryUserId(), &xuid ); + + ret = XUserCreateStatsEnumeratorByXuid( + 0, + xuid, + NUM_ROWS_PER_QUERY, + 1, + &spec, + &cbResults, + &handle ); + } + else + { + // Start at the requested rank + ret = XUserCreateStatsEnumeratorByRank( + 0, + rank, + NUM_ROWS_PER_QUERY, + 1, + &spec, + &cbResults, + &handle ); + } + + if( ret != ERROR_SUCCESS ) + { + Warning( "Error getting stats\n" ); + return false; + } + + // Allocate the buffer + CleanupStats(); + m_pStats = ( XUSER_STATS_READ_RESULTS* ) new char[cbResults]; + + DWORD cpReturned; + ret = XEnumerate( handle, m_pStats, cbResults, &cpReturned, NULL ); + } + else + { + // Get Friends leaderboard + int id = XBX_GetPrimaryUserId(); + ret = XFriendsCreateEnumerator( id, 0, 5, &cbResults, &handle ); + + if ( ret != ERROR_SUCCESS ) + { + Warning( "Error getting friends list\n" ); + return false; + } + + // Allocate the buffer + XONLINE_FRIEND *pFriends = ( XONLINE_FRIEND* ) new char[cbResults]; + + DWORD cpReturned; + ret = XEnumerate( handle, pFriends, cbResults, &cpReturned, NULL ); + if( ret != ERROR_SUCCESS ) + { + delete pFriends; + return false; + } + + for ( uint i = 0; i < cpReturned; ++i ) + { + xuidFriends[i] = pFriends[i].xuid; + } + + // Allocate the buffer + CleanupStats(); + m_pStats = ( XUSER_STATS_READ_RESULTS* ) new char[cbResults]; + + ret = XUserReadStats( 0, xuidCount, xuidFriends, 1, &spec, &cbResults, m_pStats, NULL ); + } + + if( ret == ERROR_SUCCESS ) + { + const char *pEntries[32]; + char pRowBuffer[MAX_PATH]; + char pBuffers[32][MAX_PATH]; + + m_Menu.ClearItems(); + m_iMaxRank = m_pStats->pViews[0].dwTotalViewRows; + + // Did this search return any rows? + if ( m_pStats->pViews[0].dwNumRows == 0 ) + return false; + + for ( uint i = 0; i < m_pStats->pViews[0].dwNumRows; ++i ) + { + XUSER_STATS_ROW &row = m_pStats->pViews[0].pRows[i]; + + // Save off the first rank in this set of entries + if ( i == 0 && m_iBaseRank == 0 ) + { + m_iBaseRank = row.dwRank; + } + + pEntries[0] = itoa( row.dwRank, pRowBuffer, 10 ); + pEntries[1] = row.szGamertag; + for ( uint j = 0; j < row.dwNumColumns; ++j ) + { + XUSER_STATS_COLUMN &col = m_pStats->pViews[0].pRows[i].pColumns[j]; + pEntries[j+2] = itoa( col.Value.nData, pBuffers[j], 10 ); + } + + AddLeaderboardEntry( pEntries, row.dwNumColumns + 2 ); + + if ( rank == -1 && row.xuid == xuid ) + { + m_Menu.SetFocus( i ); + m_iActiveRank = row.dwRank; + } + } + } + else + { + Warning( "Error getting leaderboard stats\n" ); + return false; + } + + CloseHandle( handle ); + + return true; +#endif + + return false; +} + +//---------------------------------------------------------- +// Determine if a new set of stats needs to be downloaded +// Return true if the update has been handled, false otherwise +//---------------------------------------------------------- +void CLeaderboardDialog::UpdateLeaderboard( int iNewRank ) +{ + // Clamp the input + if ( iNewRank < 1 ) + { + iNewRank = 1; + } + else if ( iNewRank > m_iMaxRank ) + { + iNewRank = m_iMaxRank; + } + + // No action necessary? + if ( iNewRank == m_iActiveRank ) + return; + + int nInterval = iNewRank - m_iActiveRank; + int iNewActiveItemIndex = m_Menu.GetActiveItemIndex() + nInterval; + + // Set these "new" values to the current values - they will be conditionally updated. + int iNewBaseRank = m_iBaseRank; + int iNewBaseItemIndex = m_Menu.GetBaseRowIndex(); + int nVisibleItems = m_Menu.GetVisibleItemCount(); + int nHiddenItems = NUM_ROWS_PER_QUERY - nVisibleItems; + + // Are we outside the visible range of the menu? + if ( iNewActiveItemIndex < iNewBaseItemIndex ) + { + // Do we need to grab another set of columns? + if ( iNewRank < m_iBaseRank ) + { + iNewBaseRank = iNewRank - nHiddenItems; + if ( iNewBaseRank < 1 ) + { + iNewBaseRank = 1; + } + + if ( !GetPlayerStats( iNewBaseRank ) ) + { + // Failed to load player stats, don't change the current index + return; + } + + m_iBaseRank = iNewBaseRank; + } + + int nBaseToActiveInterval = iNewRank - m_iBaseRank; + + // Since we shifted the menu down, both base and active item are at the first visible menu item + iNewActiveItemIndex = nBaseToActiveInterval; + iNewBaseItemIndex = nBaseToActiveInterval; + } + else if ( iNewActiveItemIndex >= m_Menu.GetBaseRowIndex() + nVisibleItems ) + { + int nHiddenItems = NUM_ROWS_PER_QUERY - nVisibleItems; + int iTopRank = iNewRank + nHiddenItems; + if ( iTopRank > m_iMaxRank ) + { + iTopRank = m_iMaxRank; + } + + + // Do we need to grab another set of columns? + if ( iNewRank >= m_iBaseRank + NUM_ROWS_PER_QUERY ) + { + iNewBaseRank = iTopRank - NUM_ROWS_PER_QUERY + 1; + if ( !GetPlayerStats( iNewBaseRank ) ) + { + // Failed to load player stats, don't change the current index + return; + } + m_iBaseRank = iNewBaseRank; + } + + int nBaseToActiveInterval = iNewRank - m_iBaseRank; + + iNewActiveItemIndex = nBaseToActiveInterval; + iNewBaseItemIndex = iNewActiveItemIndex - nVisibleItems + 1; + } + + // Set all the new variables - must set base index before active index. + m_iActiveRank = iNewRank; + m_Menu.SetBaseRowIndex( iNewBaseItemIndex ); + m_Menu.SetFocus( iNewActiveItemIndex ); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLeaderboardDialog::HandleKeyRepeated( vgui::KeyCode code ) +{ + OnKeyCodePressed( code ); +} + +//----------------------------------------------------------------- +// Purpose: Send key presses to the dialog's menu +//----------------------------------------------------------------- +void CLeaderboardDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + + switch( code ) + { + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: +#ifdef _X360 + { + int idx = m_Menu.GetActiveItemIndex(); + if ( m_pStats && idx < (int)m_pStats->pViews[0].dwNumRows ) + { + XUSER_STATS_ROW &row = m_pStats->pViews[0].pRows[idx]; + XShowGamerCardUI( XBX_GetPrimaryUserId(), row.xuid ); + } + } +#endif + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + break; + + case KEY_XSTICK1_DOWN: + case KEY_XBUTTON_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + m_KeyRepeat.KeyDown( code ); + UpdateLeaderboard( m_iActiveRank + 1 ); + break; + + case KEY_XSTICK1_UP: + case KEY_XBUTTON_UP: + case STEAMCONTROLLER_DPAD_UP: + m_KeyRepeat.KeyDown( code ); + UpdateLeaderboard( m_iActiveRank - 1 ); + break; + + case KEY_XBUTTON_LEFT_SHOULDER: + UpdateLeaderboard( 1 ); + break; + + case KEY_XBUTTON_RIGHT_SHOULDER: + OnCommand( "CenterOnPlayer" ); + break; + + // Disabled until friends enumeration works +// case KEY_XBUTTON_RIGHT_SHOULDER: +// OnCommand( "Friends" ); +// break; + + default: + m_KeyRepeat.KeyDown( code ); + BaseClass::OnKeyCodePressed( code ); + break; + } + + // Invalidate layout when scrolling through columns + switch( code ) + { + case KEY_XSTICK1_LEFT: + case KEY_XBUTTON_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + case KEY_XSTICK1_RIGHT: + case KEY_XBUTTON_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + InvalidateLayout(); + break; + } +} + + +CON_COMMAND( mm_add_item, "Add a stats item" ) +{ + if ( args.ArgC() > 1 ) + { + int ct = atoi( args[1] ); + const char *pEntries[32]; + for ( int i = 0; i < ct; ++i ) + { + pEntries[i] = args[i+2]; + } + g_pLeaderboardDialog->AddLeaderboardEntry( pEntries, ct ); + } +} diff --git a/gameui/matchmaking/leaderboarddialog.h b/gameui/matchmaking/leaderboarddialog.h new file mode 100644 index 0000000..6fd646f --- /dev/null +++ b/gameui/matchmaking/leaderboarddialog.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Displays a leaderboard +// +//=============================================================================// + +#ifndef LEADERBOARDDIALOG_H +#define LEADERBOARDDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +//----------------------------------------------------------------------------- +// Purpose: Display player leaderboards +//----------------------------------------------------------------------------- +class CLeaderboardDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CLeaderboardDialog, CBaseDialog ); + +public: + CLeaderboardDialog(vgui::Panel *parent); + ~CLeaderboardDialog(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void PerformLayout( void ); + virtual void OnCommand( const char *pCommand ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void HandleKeyRepeated( vgui::KeyCode code ); + + bool GetPlayerStats( int rank, bool bFriends = false ); + void UpdateLeaderboard( int iNewRank ); + void AddLeaderboardEntry( const char **pEntries, int ct ); + void CleanupStats(); + +private: + vgui::Panel *m_pProgressBg; + vgui::Panel *m_pProgressBar; + vgui::Label *m_pProgressPercent; + vgui::Label *m_pNumbering; + vgui::Label *m_pUpArrow; + vgui::Label *m_pDownArrow; + vgui::Label *m_pBestMoments; + + int m_iBaseRank; + int m_iActiveRank; + int m_iMaxRank; + int m_cColumns; + int m_iRangeBase; + +#if defined( _X360 ) + XUSER_STATS_READ_RESULTS *m_pStats; +#endif +}; + + +#endif // LEADERBOARDDIALOG_H diff --git a/gameui/matchmaking/matchmakingbasepanel.cpp b/gameui/matchmaking/matchmakingbasepanel.cpp new file mode 100644 index 0000000..403e476 --- /dev/null +++ b/gameui/matchmaking/matchmakingbasepanel.cpp @@ -0,0 +1,999 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Serves as the base panel for the entire matchmaking UI +// +//=============================================================================// + +#include "matchmakingbasepanel.h" +#include "welcomedialog.h" +#include "pausedialog.h" +#include "leaderboarddialog.h" +#include "achievementsdialog.h" +#include "sessionoptionsdialog.h" +#include "sessionlobbydialog.h" +#include "sessionbrowserdialog.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/MessageDialog.h" +#include "vgui/ISurface.h" +#include "EngineInterface.h" +#include "game/client/IGameClientExports.h" +#include "GameUI_Interface.h" +#include "engine/imatchmaking.h" +#include "KeyValues.h" +#include "vstdlib/jobthread.h" +#include "BasePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//-------------------------------- +// CMatchmakingBasePanel +//-------------------------------- +CMatchmakingBasePanel::CMatchmakingBasePanel( vgui::Panel *pParent ) : BaseClass( pParent, "MatchmakingBasePanel" ) +{ + SetDeleteSelfOnClose( true ); + SetPaintBackgroundEnabled( false ); + + vgui::scheme()->LoadSchemeFromFile( "Resource/ClientScheme.res", "ClientScheme" ); + SetScheme( "ClientScheme" ); + + m_pFooter = new CFooterPanel( this, "MatchmakingFooterPanel" ); + + m_nGameType = GAMETYPE_STANDARD_MATCH; +} + +CMatchmakingBasePanel::~CMatchmakingBasePanel() +{ + if ( m_pFooter ) + { + delete m_pFooter; + m_pFooter = NULL; + } +} + +void CMatchmakingBasePanel::SetFooterButtons( CBaseDialog *pOwner, KeyValues *pKeyValues, int nButtonGap /* = -1 */ ) +{ + // Don't lay out the buttons if the dialog is not at the top of the stack + if ( m_DialogStack.Count() ) + { + CBaseDialog *pDlg = m_DialogStack.Top(); + if ( pDlg != pOwner ) + return; + } + + if ( m_pFooter ) + { + m_pFooter->ClearButtons(); + + if ( pKeyValues ) + { + for ( KeyValues *pButton = pKeyValues->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() ) + { + if ( !Q_stricmp( pButton->GetName(), "button" ) ) + { + // Add a button to the footer + const char *pText = pButton->GetString( "text", NULL ); + const char *pIcon = pButton->GetString( "icon", NULL ); + + if ( pText && pIcon ) + { + m_pFooter->AddNewButtonLabel( pText, pIcon ); + } + } + } + } + else + { + // no data was passed so just setup the standard footer buttons + m_pFooter->SetStandardDialogButtons(); + } + + if ( nButtonGap > 0 ) + { + m_pFooter->SetButtonGap( nButtonGap ); + } + else + { + m_pFooter->UseDefaultButtonGap(); + } + } +} + +void CMatchmakingBasePanel::ShowFooter( bool bShown ) +{ + m_pFooter->SetVisible( bShown ); +} + +void CMatchmakingBasePanel::SetFooterButtonVisible( const char *pszText, bool bVisible ) +{ + if ( m_pFooter ) + { + m_pFooter->ShowButtonLabel( pszText, bVisible ); + } +} + +void CMatchmakingBasePanel::Activate( void ) +{ + BaseClass::Activate(); + + // Close animation may have set this to zero + SetAlpha( 255 ); + + if ( !GameUI().IsInLevel() ) + { + OnOpenWelcomeDialog(); + } + else + { + OnOpenPauseDialog(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle commands from all matchmaking dialogs +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( "OpenWelcomeDialog", pCommand ) ) + { + OnOpenWelcomeDialog(); + } + if ( !Q_stricmp( "OpenPauseDialog", pCommand ) ) + { + OnOpenPauseDialog(); + } + if ( !Q_stricmp( "OpenRankingsDialog", pCommand ) ) + { + OnOpenRankingsDialog(); + } + else if ( !Q_stricmp( "OpenSystemLinkDialog", pCommand ) ) + { + OnOpenSystemLinkDialog(); + } + else if ( !Q_stricmp( "OpenPlayerMatchDialog", pCommand ) ) + { + OnOpenPlayerMatchDialog(); + } + else if ( !Q_stricmp( "OpenRankedMatchDialog", pCommand ) ) + { + OnOpenRankedMatchDialog(); + } + else if ( !Q_stricmp( "OpenAchievementsDialog", pCommand ) ) + { + OnOpenAchievementsDialog(); + } + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Specific code for CS Achievements Display + //============================================================================= + + else if ( !Q_stricmp( "OpenCSAchievementsDialog", pCommand ) ) + { + OnOpenCSAchievementsDialog(); + } + + //============================================================================= + // HPE_END + //============================================================================= + + else if ( !Q_stricmp( "LevelLoadingStarted", pCommand ) ) + { + OnLevelLoadingStarted(); + } + else if ( !Q_stricmp( "LevelLoadingFinished", pCommand ) ) + { + OnLevelLoadingFinished(); + } + else if ( !Q_stricmp( "SessionOptions_Modify", pCommand ) ) + { + OnOpenSessionOptionsDialog( pCommand ); + } + else if ( !Q_stricmp( "ModifySession", pCommand ) ) + { + matchmaking->ModifySession(); + } + else if ( !Q_stricmp( "ChangeClass", pCommand ) ) + { + engine->ClientCmd_Unrestricted( "changeclass" ); + OnCommand( "ResumeGame" ); + } + else if ( !Q_stricmp( "ChangeTeam", pCommand ) ) + { + engine->ClientCmd_Unrestricted( "changeteam" ); + OnCommand( "ResumeGame" ); + } + else if ( !Q_stricmp( "ShowMapInfo", pCommand ) ) + { + engine->ClientCmd_Unrestricted( "showmapinfo" ); + OnCommand( "ResumeGame" ); + } + else if ( !Q_stricmp( "StartHost", pCommand ) ) + { + // Show progress dialog + GameUI().ShowMessageDialog( MD_CREATING_GAME, this ); + + // Send the host start command + matchmaking->StartHost(); + } + else if ( !Q_stricmp( "StartSystemLinkHost", pCommand ) ) + { + // Show progress dialog + GameUI().ShowMessageDialog( MD_CREATING_GAME, this ); + + m_nGameType = GAMETYPE_SYSTEMLINK_MATCH; + matchmaking->StartHost( true ); + } + else if ( !Q_stricmp( "StartClient", pCommand ) ) + { + // Show progress dialog + GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this ); + + // Tell matchmaking to start a client and search for games + matchmaking->StartClient( false ); + } + else if ( !Q_stricmp( "StartSystemLinkClient", pCommand ) ) + { + // Show progress dialog + GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this ); + + // Set the system link flag + matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_SYSTEMLINK", NULL, NULL ); + + // Tell matchmaking to start a client and search for games + m_nGameType = GAMETYPE_SYSTEMLINK_MATCH; + matchmaking->StartClient( true ); + } + else if ( Q_stristr( pCommand, "StartQuickMatchClient_" ) ) + { + // Show progress dialog + GameUI().ShowMessageDialog( MD_SEARCHING_FOR_GAMES, this ); + + if ( Q_stristr( pCommand, "_Ranked" ) ) + { + // Set the basic flags + matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_MODE", "CONTEXT_GAME_MODE_MULTIPLAYER", NULL ); + matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_TYPE", "CONTEXT_GAME_TYPE_RANKED", NULL ); + matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_LIVE_MULTIPLAYER_RANKED", NULL, NULL ); + m_nGameType = GAMETYPE_RANKED_MATCH; + } + else + { + // Set the standard match flag + matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_MODE", "CONTEXT_GAME_MODE_MULTIPLAYER", NULL ); + matchmaking->AddSessionProperty( SESSION_CONTEXT, "CONTEXT_GAME_TYPE", "CONTEXT_GAME_TYPE_STANDARD", NULL ); + matchmaking->AddSessionProperty( SESSION_FLAG, "SESSION_CREATE_LIVE_MULTIPLAYER_STANDARD", NULL, NULL ); + m_nGameType = GAMETYPE_STANDARD_MATCH; + } + + // Tell matchmaking to start a client and search for games + matchmaking->StartClient( false ); + } + else if ( !Q_stricmp( "StartGame", pCommand ) ) + { + // Tell matchmaking the host wants to start the game + matchmaking->StartGame(); + } + else if ( Q_stristr( pCommand, "LeaderboardDialog_" ) ) + { + // This covers LeaderboardDialog_[Ranked|Stats] + OnOpenLeaderboardDialog( pCommand ); + } + else if ( Q_stristr( pCommand, "SessionOptions_" ) ) + { + // This covers six command strings: *_Host[Standard|Ranked|Systemlink], *_Client[Standard|Ranked|Systemlink] + // Each command has a unique options menu - the command string is used as the name of the .res file. + OnOpenSessionOptionsDialog( pCommand ); + } + else if ( !Q_stricmp( pCommand, "DialogClosing" ) ) + { + PopDialog(); + } + else if ( !Q_stricmp( pCommand, "AchievementsDialogClosing" ) ) + { + PopDialog(); + } + else if ( !Q_stricmp( pCommand, "show_achievements_dialog" ) ) + { + OnOpenAchievementsDialog(); + } + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Specific code for CS Achievements Display + //============================================================================= + + else if ( !Q_stricmp( pCommand, "show_csachievements_dialog" ) ) + { + OnOpenCSAchievementsDialog(); + } + + //============================================================================= + // HPE_END + //============================================================================= + + else if ( !Q_stricmp( pCommand, "ShowSessionOptionsDialog" ) ) + { + // Need to close the client options dialog and open the host options equivalent + PopDialog(); + + switch( m_nGameType ) + { + case GAMETYPE_STANDARD_MATCH: + OnOpenSessionOptionsDialog( "SessionOptions_HostStandard" ); + break; + + case GAMETYPE_RANKED_MATCH: + OnOpenSessionOptionsDialog( "SessionOptions_HostRanked" ); + break; + + case GAMETYPE_SYSTEMLINK_MATCH: + OnOpenSessionOptionsDialog( "SessionOptions_SystemLink" ); + break; + } + } + else if ( !Q_stricmp( pCommand, "ReturnToMainMenu" ) ) + { + CloseAllDialogs(); + Activate(); + } + else if ( !Q_stricmp( pCommand, "CancelOperation" ) ) + { + GameUI().CloseMessageDialog(); + PopDialog(); + matchmaking->CancelCurrentOperation(); + } + else if ( !Q_stricmp( pCommand, "StorageDeviceDenied" ) ) + { + // Set us as declined + XBX_SetStorageDeviceId( XBX_STORAGE_DECLINED ); + } + else + { + if ( !Q_stricmp( "ResumeGame", pCommand ) ) + { + CloseAllDialogs(); + } + + CallParentFunction( new KeyValues( "Command", "command", pCommand ) ); + } + + // We should handle the case when user launched the game via invite, + // was prompted for a storage device and cancelled the picker. + // In this case whenever any command gets selected from the main menu + // we should cancel the wait for storage device selection. + BasePanel()->ValidateStorageDevice( NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle notifications from matchmaking in the engine. +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::SessionNotification( const int notification, const int param ) +{ + switch( notification ) + { + case SESSION_NOTIFY_FAIL_SEARCH: + GameUI().CloseMessageDialog(); + GameUI().ShowMessageDialog( MD_SESSION_SEARCH_FAILED, this ); + break; + + case SESSION_NOTIFY_CONNECT_NOTAVAILABLE: + CloseAllDialogs(); + GameUI().ShowMessageDialog( MD_SESSION_CONNECT_NOTAVAILABLE, this ); + break; + + case SESSION_NOTIFY_CONNECT_SESSIONFULL: + CloseAllDialogs(); + GameUI().ShowMessageDialog( MD_SESSION_CONNECT_SESSIONFULL, this ); + break; + + case SESSION_NOTIFY_CONNECT_FAILED: + CloseAllDialogs(); + GameUI().ShowMessageDialog( MD_SESSION_CONNECT_FAILED, this ); + break; + + case SESSION_NOTIFY_FAIL_CREATE: + CloseAllDialogs(); + GameUI().ShowMessageDialog( MD_SESSION_CREATE_FAILED, this ); + break; + + case SESSION_NOTIFY_CLIENT_KICKED: + CloseAllDialogs(); + GameUI().ShowMessageDialog( MD_CLIENT_KICKED, this ); + break; + + case SESSION_NOTIFY_LOST_HOST: + CloseBaseDialogs(); + GameUI().ShowMessageDialog( MD_LOST_HOST, this ); + break; + + case SESSION_NOTIFY_LOST_SERVER: + CloseBaseDialogs(); + GameUI().ShowMessageDialog( MD_LOST_SERVER, this ); + break; + + case SESSION_NOFIFY_MODIFYING_SESSION: + GameUI().ShowMessageDialog( MD_MODIFYING_SESSION, this ); + break; + + case SESSION_NOTIFY_SEARCH_COMPLETED: + GameUI().CloseMessageDialog(); + + LoadSessionProperties(); + + // Switch to the session browser + switch( m_nGameType ) + { + case GAMETYPE_STANDARD_MATCH: + case GAMETYPE_RANKED_MATCH: + OnOpenSessionBrowserDialog( "SessionBrowser_Live" ); + break; + + case GAMETYPE_SYSTEMLINK_MATCH: + OnOpenSessionBrowserDialog( "SessionBrowser_SystemLink" ); + break; + } + break; + + case SESSION_NOTIFY_CREATED_HOST: + case SESSION_NOTIFY_MODIFYING_COMPLETED_HOST: + GameUI().CloseMessageDialog(); + + LoadSessionProperties(); + + // Switch to the Lobby + switch( m_nGameType ) + { + case GAMETYPE_STANDARD_MATCH: + case GAMETYPE_RANKED_MATCH: + case GAMETYPE_SYSTEMLINK_MATCH: + OnOpenSessionLobbyDialog( "SessionLobby_Host" ); + break; + } + break; + + case SESSION_NOTIFY_CREATED_CLIENT: + GameUI().ShowMessageDialog( MD_SESSION_CONNECTING, this ); + break; + + case SESSION_NOTIFY_CONNECTED_TOSESSION: + case SESSION_NOTIFY_MODIFYING_COMPLETED_CLIENT: + GameUI().CloseMessageDialog(); + + LoadSessionProperties(); + + // Switch to the Lobby + switch( m_nGameType ) + { + case GAMETYPE_STANDARD_MATCH: + case GAMETYPE_RANKED_MATCH: + case GAMETYPE_SYSTEMLINK_MATCH: + OnOpenSessionLobbyDialog( "SessionLobby_Client" ); + break; + } + break; + + case SESSION_NOTIFY_CONNECTED_TOSERVER: + CloseAllDialogs( false ); + break; + + case SESSION_NOTIFY_ENDGAME_RANKED: + // Return to the main menu + CloseAllDialogs(); + break; + + case SESSION_NOTIFY_ENDGAME_HOST: + CloseBaseDialogs(); + OnOpenSessionLobbyDialog( "SessionLobby_Host" ); + break; + + case SESSION_NOTIFY_ENDGAME_CLIENT: + CloseBaseDialogs(); + OnOpenSessionLobbyDialog( "SessionLobby_Client" ); + break; + + case SESSION_NOTIFY_COUNTDOWN: + { + CSessionLobbyDialog *pDlg = (CSessionLobbyDialog*)m_hSessionLobbyDialog.Get(); + if ( pDlg ) + { + pDlg->UpdateCountdown( param ); + } + + if ( param == 0 ) + { + BasePanel()->RunAnimationWithCallback( this, "CloseMatchmakingUI", new KeyValues( "LoadMap" ) ); + } + } + break; + + case SESSION_NOTIFY_DUMPSTATS: + Msg( "[MM] %d open dialogs\n", m_DialogStack.Count() ); + for ( int i = 0; i < m_DialogStack.Count(); ++i ) + { + const char *pString = "NULL"; + bool bVisible = false; + float fAlpha = 0.f; + CBaseDialog *pDlg = m_DialogStack[i]; + if ( pDlg ) + { + pString = pDlg->GetName(); + bVisible = pDlg->IsVisible(); + fAlpha = pDlg->GetAlpha(); + } + const char *pVisible = bVisible ? "YES" : "NO"; + Msg( "[MM] Dialog %d: %s, Visible %s, Alpha %f\n", i, pString, pVisible, fAlpha ); + } + break; + + case SESSION_NOTIFY_WELCOME: + CloseGameDialogs( false ); + Activate(); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: System Notification +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::SystemNotification( const int notification ) +{ + switch( notification ) + { + case SYSTEMNOTIFY_USER_SIGNEDOUT: + // See if this was us +#if defined( _X360 ) + uint state = XUserGetSigninState( XBX_GetPrimaryUserId() ); + if ( state == eXUserSigninState_NotSignedIn ) + { + matchmaking->KickPlayerFromSession( 0 ); + CloseAllDialogs(); + } + else if ( state != eXUserSigninState_SignedInToLive ) + { + // User was signed out of live + if ( m_bPlayingOnline ) + { + matchmaking->KickPlayerFromSession( 0 ); + CloseAllDialogs(); + } + } +#endif + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check whether a player meets the signin requirements for a multiplayer game +//----------------------------------------------------------------------------- +bool CMatchmakingBasePanel::ValidateSigninAndStorage( bool bOnlineRequired, const char *pIssuingCommand ) +{ + // Check the signin state of the primary user + bool bSignedIn = false; + bool bOnlineEnabled = false; + bool bOnlineSignedIn = false; + +#if defined( _X360 ) + int userIdx = XBX_GetPrimaryUserId(); + if ( userIdx != INVALID_USER_ID ) + { + XUSER_SIGNIN_INFO info; + uint ret = XUserGetSigninInfo( userIdx, 0, &info ); + if ( ret == ERROR_SUCCESS ) + { + bSignedIn = true; + if ( info.dwInfoFlags & XUSER_INFO_FLAG_LIVE_ENABLED ) + { + bOnlineEnabled = true; + uint state = XUserGetSigninState( XBX_GetPrimaryUserId() ); + if ( state == eXUserSigninState_SignedInToLive ) + { + bOnlineSignedIn = true; + + // Check privileges + BOOL bPrivCheck = false; + DWORD dwPrivCheck = XUserCheckPrivilege( userIdx, XPRIVILEGE_MULTIPLAYER_SESSIONS, &bPrivCheck ); + if ( ERROR_SUCCESS != dwPrivCheck || + !bPrivCheck ) + { + bOnlineEnabled = false; + } + } + } + } + } +#endif + + if ( bOnlineRequired && !bOnlineEnabled ) + { + // Player must sign in an online account + GameUI().ShowMessageDialog( MD_NOT_ONLINE_ENABLED ); + return false; + } + else if ( bOnlineRequired && !bOnlineSignedIn ) + { + // Player's live account isn't signed in to live + GameUI().ShowMessageDialog( MD_NOT_ONLINE_SIGNEDIN ); + return false; + } + else if ( !bSignedIn ) + { + // Eat the input and make the user sign in + xboxsystem->ShowSigninUI( 1, 0 ); // One user, no special flags + return false; + } + + // Handle the storage device selection + if ( !BasePanel()->HandleStorageDeviceRequest( pIssuingCommand ) ) + return false; + + // If we succeeded, clear the command out + BasePanel()->ClearPostPromptCommand( pIssuingCommand ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update player information in the lobby +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ) +{ + CSessionLobbyDialog *pLobby = dynamic_cast< CSessionLobbyDialog* >( m_hSessionLobbyDialog.Get() ); + if ( pLobby ) + { + pLobby->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Add a search result to the browser dialog +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ) +{ + CSessionBrowserDialog *pBrowser = dynamic_cast< CSessionBrowserDialog* >( m_hSessionBrowserDialog.Get() ); + if ( pBrowser ) + { + pBrowser->SessionSearchResult( searchIdx, pHostData, pResult, ping ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pre level load ops +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::OnLevelLoadingStarted() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Post level load ops +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::OnLevelLoadingFinished() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Hide the current dialog, add a new one to the stack and activate it. +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::PushDialog( vgui::DHANDLE< CBaseDialog > &hDialog ) +{ + if ( m_DialogStack.Count() ) + { + if ( m_DialogStack.Top() ) + { + m_DialogStack.Top()->Close(); + } + else + { + m_DialogStack.Pop(); + } + } + hDialog->Activate(); + m_DialogStack.Push( hDialog ); +} + +//----------------------------------------------------------------------------- +// Purpose: Close the current dialog, pop it from the top of the stack, and activate the next one. +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::PopDialog( bool bActivateNext ) +{ + if ( m_DialogStack.Count() > 1 ) + { + if ( m_DialogStack.Top() ) + { + m_DialogStack.Top()->SetDeleteSelfOnClose( true ); + m_DialogStack.Top()->Close(); + m_DialogStack.Pop(); + } + + // Drop down to the next available dialog + while ( m_DialogStack.Count() && !m_DialogStack.Top() ) + { + m_DialogStack.Pop(); + } + + if ( bActivateNext && m_DialogStack.Count() && m_DialogStack.Top() ) + { + m_DialogStack.Top()->Activate(); + } + } + + if ( m_DialogStack.Count() <= 1 ) + { + // Back at the welcome menu + m_bPlayingOnline = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Close all open dialogs down to the main menu +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::CloseGameDialogs( bool bActivateNext ) +{ + CloseBaseDialogs(); + while ( m_DialogStack.Count() > 1 ) + { + PopDialog( bActivateNext ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Close all open dialogs down to the main menu +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::CloseAllDialogs( bool bActivateNext ) +{ + GameUI().CloseMessageDialog(); + CloseGameDialogs( bActivateNext ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::CloseBaseDialogs( void ) +{ + if ( BasePanel() ) + { + BasePanel()->CloseBaseDialogs(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get session property keyvalues from base panel and matchmaking +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::LoadSessionProperties() +{ + // Grab the session property keys from XboxDialogs.res and from matchmaking + m_pSessionKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "PropertyDisplayKeys" ); + if ( m_pSessionKeys ) + { + m_pSessionKeys->ChainKeyValue( matchmaking->GetSessionProperties() ); + } + + // Cache off the map name + const char *pDiskName = NULL; + KeyValues *pName = m_pSessionKeys->FindKey( "MapDiskNames" ); + if ( pName ) + { + KeyValues *pScenario = m_pSessionKeys->FindKey( "CONTEXT_SCENARIO" ); + if ( pScenario ) + { + pDiskName = pName->GetString( pScenario->GetString( "displaystring" ), NULL ); + } + } + + if ( pDiskName ) + { + Q_strncpy( m_szMapLoadName, pDiskName, sizeof( m_szMapLoadName ) ); + Msg( "Storing mapname %s\n", m_szMapLoadName ); + if ( Q_strlen( m_szMapLoadName ) < 5 ) + { + Warning( "Bad map name!\n" ); + } + } + else + { + // X360TBD: Generate a create error + } +} + +//----------------------------------------------------------------------------- +// Purpose: Open dialog functions. +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::OnOpenWelcomeDialog() +{ + if ( !m_hWelcomeDialog.Get() ) + { + m_hWelcomeDialog = new CWelcomeDialog( this ); + m_DialogStack.Push( m_hWelcomeDialog ); + } + + m_hWelcomeDialog->Activate(); + m_bPlayingOnline = false; +} + +void CMatchmakingBasePanel::OnOpenPauseDialog() +{ + if ( !m_hPauseDialog.Get() ) + { + m_hPauseDialog = new CPauseDialog( this ); + } + PushDialog( m_hPauseDialog ); +} + +void CMatchmakingBasePanel::OnOpenRankingsDialog() +{ + if ( !ValidateSigninAndStorage( true, "OpenRankingDialog" ) ) + return; + + if ( !m_hRankingsDialog.Get() ) + { + m_hRankingsDialog = new CBaseDialog( this, "RankingsDialog" ); + } + PushDialog( m_hRankingsDialog ); +} + +void CMatchmakingBasePanel::OnOpenSystemLinkDialog() +{ + if ( !ValidateSigninAndStorage( false, "OpenSystemLinkDialog" ) ) + return; + + if ( !m_hSystemLinkDialog.Get() ) + { + m_hSystemLinkDialog = new CBaseDialog( this, "SystemLinkDialog" ); + } + PushDialog( m_hSystemLinkDialog ); +} + +void CMatchmakingBasePanel::OnOpenPlayerMatchDialog() +{ + if ( !ValidateSigninAndStorage( true, "OpenPlayerMatchDialog" ) ) + return; + + if ( !m_hPlayerMatchDialog.Get() ) + { + m_hPlayerMatchDialog = new CBaseDialog( this, "PlayerMatchDialog" ); + } + PushDialog( m_hPlayerMatchDialog ); + m_bPlayingOnline = true; +} + +void CMatchmakingBasePanel::OnOpenRankedMatchDialog() +{ + if ( !ValidateSigninAndStorage( true, "OpenRankedMatchDialog" ) ) + return; + + if ( !m_hRankedMatchDialog.Get() ) + { + m_hRankedMatchDialog = new CBaseDialog( this, "RankedMatchDialog" ); + } + PushDialog( m_hRankedMatchDialog ); + m_bPlayingOnline = true; +} + +void CMatchmakingBasePanel::OnOpenAchievementsDialog() +{ + if ( !ValidateSigninAndStorage( false, "OpenAchievementsDialog" ) ) + return; + + if ( !m_hAchievementsDialog.Get() ) + { + m_hAchievementsDialog = new CAchievementsDialog_XBox( this ); + } + PushDialog( m_hAchievementsDialog ); +} + +//============================================================================= +// HPE_BEGIN: +// [dwenger] Specific code for CS Achievements Display +//============================================================================= + +void CMatchmakingBasePanel::OnOpenCSAchievementsDialog() +{ + if ( !ValidateSigninAndStorage( false, "OpenCSAchievementsDialog" ) ) + return; + + if ( !m_hAchievementsDialog.Get() ) + { + // $TODO(HPE): m_hAchievementsDialog = new CAchievementsDialog_XBox( this ); + } + PushDialog( m_hAchievementsDialog ); +} + +//============================================================================= +// HPE_END +//============================================================================= + +void CMatchmakingBasePanel::OnOpenSessionOptionsDialog( const char *pResourceName ) +{ + if ( !m_hSessionOptionsDialog.Get() ) + { + m_hSessionOptionsDialog = new CSessionOptionsDialog( this ); + } + + if ( Q_stristr( pResourceName, "Ranked" ) ) + { + m_nGameType = GAMETYPE_RANKED_MATCH; + } + else if ( Q_stristr( pResourceName, "Standard" ) ) + { + m_nGameType = GAMETYPE_STANDARD_MATCH; + } + else if ( Q_stristr( pResourceName, "SystemLink" ) ) + { + m_nGameType = GAMETYPE_SYSTEMLINK_MATCH; + } + + LoadSessionProperties(); + + CSessionOptionsDialog* pDlg = ((CSessionOptionsDialog*)m_hSessionOptionsDialog.Get()); + pDlg->SetGameType( pResourceName ); + pDlg->SetDialogKeys( m_pSessionKeys ); + + PushDialog( m_hSessionOptionsDialog ); +} + +void CMatchmakingBasePanel::OnOpenSessionLobbyDialog( const char *pResourceName ) +{ + if ( !m_hSessionLobbyDialog.Get() ) + { + m_hSessionLobbyDialog = new CSessionLobbyDialog( this ); + } + CSessionLobbyDialog *pDlg = (CSessionLobbyDialog*)m_hSessionLobbyDialog.Get(); + pDlg->SetDialogKeys( m_pSessionKeys ); + + m_hSessionLobbyDialog->SetName( pResourceName ); + PushDialog( m_hSessionLobbyDialog ); +} + +void CMatchmakingBasePanel::OnOpenSessionBrowserDialog( const char *pResourceName ) +{ + if ( !m_hSessionBrowserDialog.Get() ) + { + m_hSessionBrowserDialog = new CSessionBrowserDialog( this, m_pSessionKeys ); + m_hSessionBrowserDialog->SetName( pResourceName ); + + // Matchmaking will start adding results immediately, so prepare the dialog + SETUP_PANEL( m_hSessionBrowserDialog.Get() ); + } + PushDialog( m_hSessionBrowserDialog ); +} + +void CMatchmakingBasePanel::OnOpenLeaderboardDialog( const char *pResourceName ) +{ + if ( !m_hLeaderboardDialog.Get() ) + { + m_hLeaderboardDialog = new CLeaderboardDialog( this ); + m_hLeaderboardDialog->SetName( pResourceName ); + SETUP_PANEL( m_hLeaderboardDialog.Get() ); + } + PushDialog( m_hLeaderboardDialog ); + m_hLeaderboardDialog->OnCommand( "CenterOnPlayer" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Callback function to start map load after ui fades out. +//----------------------------------------------------------------------------- +void CMatchmakingBasePanel::LoadMap( const char *mapname ) +{ + CloseAllDialogs( false ); + + char cmd[MAX_PATH]; + Q_snprintf( cmd, sizeof( cmd ), "map %s", m_szMapLoadName ); + BasePanel()->FadeToBlackAndRunEngineCommand( cmd ); +} + +//------------------------------------------------------- +// Keyboard input +//------------------------------------------------------- +void CMatchmakingBasePanel::OnKeyCodePressed( vgui::KeyCode code ) +{ + switch( code ) + { + case KEY_XBUTTON_B: + // Can't close the matchmaking base panel + break; + + default: + BaseClass::OnKeyCodePressed( code ); + break; + } +} diff --git a/gameui/matchmaking/matchmakingbasepanel.h b/gameui/matchmaking/matchmakingbasepanel.h new file mode 100644 index 0000000..0be40ed --- /dev/null +++ b/gameui/matchmaking/matchmakingbasepanel.h @@ -0,0 +1,114 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Serves as the base panel for the entire matchmaking UI +// +//=============================================================================// + +#ifndef MATCHMAKINGBASEPANEL_H +#define MATCHMAKINGBASEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" +#include "utlstack.h" +#include "const.h" + +enum EGameType +{ + GAMETYPE_RANKED_MATCH, + GAMETYPE_STANDARD_MATCH, + GAMETYPE_SYSTEMLINK_MATCH, +}; + +//---------------------------- +// CMatchmakingBasePanel +//---------------------------- +class CMatchmakingBasePanel : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CMatchmakingBasePanel, CBaseDialog ); + +public: + CMatchmakingBasePanel(vgui::Panel *parent); + ~CMatchmakingBasePanel(); + + virtual void OnCommand( const char *pCommand ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void Activate(); + + void SessionNotification( const int notification, const int param = 0 ); + void SystemNotification( const int notification ); + void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ); + void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ); + + void OnLevelLoadingStarted(); + void OnLevelLoadingFinished(); + void CloseGameDialogs( bool bActivateNext = true ); + void CloseAllDialogs( bool bActivateNext = true ); + void CloseBaseDialogs( void ); + + void SetFooterButtons( CBaseDialog *pOwner, KeyValues *pData, int nButtonGap = -1 ); + void ShowFooter( bool bShown ); + void SetFooterButtonVisible( const char *pszText, bool bVisible ); + + uint GetGameType( void ) { return m_nGameType; } + + MESSAGE_FUNC_CHARPTR( LoadMap, "LoadMap", mapname ); +private: + void OnOpenWelcomeDialog(); + void OnOpenPauseDialog(); + void OnOpenRankingsDialog(); + void OnOpenSystemLinkDialog(); + void OnOpenPlayerMatchDialog(); + void OnOpenRankedMatchDialog(); + void OnOpenAchievementsDialog(); + + //============================================================================= + // HPE_BEGIN: + // [dwenger] Specific code for CS Achievements Display + //============================================================================= + + // $TODO(HPE): Move this to a game-specific location + void OnOpenCSAchievementsDialog(); + + //============================================================================= + // HPE_END + //============================================================================= + + void OnOpenLeaderboardDialog( const char *pResourceName ); + void OnOpenSessionOptionsDialog( const char *pResourceName ); + void OnOpenSessionLobbyDialog( const char *pResourceName ); + void OnOpenSessionBrowserDialog( const char *pResourceName ); + + void LoadSessionProperties(); + bool ValidateSigninAndStorage( bool bOnlineRequired, const char *pIssuingCommand ); + void CenterDialog( vgui::PHandle dlg ); + void PushDialog( vgui::DHANDLE< CBaseDialog > &hDialog ); + void PopDialog( bool bActivateNext = true ); + + vgui::DHANDLE< CBaseDialog > m_hWelcomeDialog; + vgui::DHANDLE< CBaseDialog > m_hPauseDialog; + vgui::DHANDLE< CBaseDialog > m_hStatsDialog; + vgui::DHANDLE< CBaseDialog > m_hRankingsDialog; + vgui::DHANDLE< CBaseDialog > m_hLeaderboardDialog; + vgui::DHANDLE< CBaseDialog > m_hSystemLinkDialog; + vgui::DHANDLE< CBaseDialog > m_hPlayerMatchDialog; + vgui::DHANDLE< CBaseDialog > m_hRankedMatchDialog; + vgui::DHANDLE< CBaseDialog > m_hAchievementsDialog; + vgui::DHANDLE< CBaseDialog > m_hSessionOptionsDialog; + vgui::DHANDLE< CBaseDialog > m_hSessionLobbyDialog; + vgui::DHANDLE< CBaseDialog > m_hSessionBrowserDialog; + + CUtlStack< vgui::DHANDLE< CBaseDialog > > m_DialogStack; + + uint m_nSessionType; + uint m_nGameType; + bool m_bPlayingOnline; + char m_szMapLoadName[MAX_MAP_NAME]; + KeyValues *m_pSessionKeys; + + CFooterPanel *m_pFooter; +}; + + +#endif // MATCHMAKINGBASEPANEL_H diff --git a/gameui/matchmaking/pausedialog.cpp b/gameui/matchmaking/pausedialog.cpp new file mode 100644 index 0000000..f158583 --- /dev/null +++ b/gameui/matchmaking/pausedialog.cpp @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Multiplayer pause menu +// +//=============================================================================// + +#include "pausedialog.h" +#include "GameUI_Interface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//-------------------------------- +// CPauseDialog +//-------------------------------- +CPauseDialog::CPauseDialog( vgui::Panel *pParent ) : BaseClass( pParent, "PauseDialog" ) +{ + // do nothing +} + +void CPauseDialog::Activate( void ) +{ + BaseClass::Activate(); + + SetDeleteSelfOnClose( false ); + m_Menu.SetFocus( 0 ); +} + +//------------------------------------------------------- +// Keyboard input +//------------------------------------------------------- +void CPauseDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + switch( code ) + { + case KEY_XBUTTON_B: + if ( GameUI().IsInLevel() ) + { + m_pParent->OnCommand( "ResumeGame" ); + } + break; + + default: + BaseClass::OnKeyCodePressed( code ); + break; + } +}
\ No newline at end of file diff --git a/gameui/matchmaking/pausedialog.h b/gameui/matchmaking/pausedialog.h new file mode 100644 index 0000000..3d580d8 --- /dev/null +++ b/gameui/matchmaking/pausedialog.h @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Multiplayer pause menu +// +//=============================================================================// + +#ifndef PAUSEDIALOG_H +#define PAUSEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +//----------------------------------------------------------------------------- +// Purpose: Multiplayer pause menu +//----------------------------------------------------------------------------- +class CPauseDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CPauseDialog, CBaseDialog ); + +public: + CPauseDialog( vgui::Panel *parent ); + + virtual void Activate( void ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); +}; + + +#endif // PAUSEDIALOG_H diff --git a/gameui/matchmaking/sessionbrowserdialog.cpp b/gameui/matchmaking/sessionbrowserdialog.cpp new file mode 100644 index 0000000..f29b177 --- /dev/null +++ b/gameui/matchmaking/sessionbrowserdialog.cpp @@ -0,0 +1,323 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Present a list of sessions from which the player can choose a game to join. +// +//=============================================================================// + +#include "sessionbrowserdialog.h" +#include "engine/imatchmaking.h" +#include "EngineInterface.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Label.h" +#include "KeyValues.h" +#include "vgui/ISurface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CSessionBrowserDialog *g_pBrowserDialog; + +//-------------------------------- +// CSessionBrowserDialog +//-------------------------------- +CSessionBrowserDialog::CSessionBrowserDialog( vgui::Panel *pParent, KeyValues *pDialogKeys ) : BaseClass( pParent, "" ) +{ + g_pBrowserDialog = this; + m_pDialogKeys = pDialogKeys; + + SetDeleteSelfOnClose( true ); +} + +CSessionBrowserDialog::~CSessionBrowserDialog() +{ + m_pScenarioInfos.PurgeAndDeleteElements(); +} + +//--------------------------------------------------------------------- +// Purpose: Center the dialog on the screen +//--------------------------------------------------------------------- +void CSessionBrowserDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + MoveToCenterOfScreen(); + UpdateScenarioDisplay(); +} + +//--------------------------------------------------------------------- +// Purpose: Parse session properties and contexts from the resource file +//--------------------------------------------------------------------- +void CSessionBrowserDialog::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + KeyValues *pScenarios = pResourceData->FindKey( "ScenarioInfoPanels" ); + if ( pScenarios ) + { + for ( KeyValues *pScenario = pScenarios->GetFirstSubKey(); pScenario != NULL; pScenario = pScenario->GetNextKey() ) + { + CScenarioInfoPanel *pScenarioInfo = new CScenarioInfoPanel( this, "ScenarioInfoPanel" ); + SETUP_PANEL( pScenarioInfo ); + pScenarioInfo->m_pTitle->SetText( pScenario->GetString( "title" ) ); + pScenarioInfo->m_pSubtitle->SetText( pScenario->GetString( "subtitle" ) ); + pScenarioInfo->m_pMapImage->SetImage( pScenario->GetString( "image" ) ); + + int nTall = pScenario->GetInt( "tall", -1 ); + if ( nTall > 0 ) + { + pScenarioInfo->SetTall( nTall ); + } + + int nXPos = pScenario->GetInt( "xpos", -1 ); + if ( nXPos >= 0 ) + { + int x, y; + pScenarioInfo->GetPos( x, y ); + pScenarioInfo->SetPos( nXPos, y ); + } + + int nDescOneYpos = pScenario->GetInt( "descOneY", -1 ); + if ( nDescOneYpos > 0 ) + { + int x, y; + pScenarioInfo->m_pDescOne->GetPos( x, y ); + pScenarioInfo->m_pDescOne->SetPos( x, nDescOneYpos ); + } + + int nDescTwoYpos = pScenario->GetInt( "descTwoY", -1 ); + if ( nDescTwoYpos > 0 ) + { + int x, y; + pScenarioInfo->m_pDescTwo->GetPos( x, y ); + pScenarioInfo->m_pDescTwo->SetPos( x, nDescTwoYpos ); + } + + m_pScenarioInfos.AddToTail( pScenarioInfo ); + } + } +} + +//--------------------------------------------------------------------- +// Purpose: Set up colors and other such stuff +//--------------------------------------------------------------------- +void CSessionBrowserDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + for ( int i = 0; i < m_pScenarioInfos.Count(); ++i ) + { + m_pScenarioInfos[i]->SetBgColor( pScheme->GetColor( "TanDark", Color( 0, 0, 0, 255 ) ) ); + } +} + +//--------------------------------------------------------------------- +// Purpose: Info about a session, sent from matchmaking +//--------------------------------------------------------------------- +void CSessionBrowserDialog::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ) +{ + char *pPing; + switch ( ping ) + { + case 0: + pPing = "#TF_Icon_Ping_Green"; + break; + case 1: + pPing = "#TF_Icon_Ping_Yellow"; + break; + case 2: + default: + pPing = "#TF_Icon_Ping_Red"; + break; + } + + int nScenarioId = 0; + uint nContextId = m_pDialogKeys->GetInt( "scenario", -1 ); + for ( uint i = 0; i < pResult->cContexts; ++i ) + { + if ( pResult->pContexts[i].dwContextId == nContextId ) + { + nScenarioId = pResult->pContexts[i].dwValue; + break; + } + } + + hostData_s *pData = (hostData_s*)pHostData; + + int filledSlots = pResult->dwFilledPublicSlots + pResult->dwFilledPrivateSlots; + int totalSlots = filledSlots + pResult->dwOpenPublicSlots + pResult->dwOpenPrivateSlots; + + m_GameStates.AddToTail( pData->gameState ); + m_GameTimes.AddToTail( pData->gameTime ); + m_XUIDs.AddToTail( pData->xuid ); + + char szSlots[16] = {0}; + Q_snprintf( szSlots, sizeof( szSlots ), "%d/%d", filledSlots, totalSlots ); + + int ct = 0; + const char *ppStrings[4]; + ppStrings[ct++] = pData->hostName; + ppStrings[ct++] = szSlots; + ppStrings[ct++] = pData->scenario; + if ( ping != -1 ) + { + ppStrings[ct++] = pPing; + } + m_Menu.AddSectionedItem( ppStrings, ct ); + + m_ScenarioIndices.AddToTail( nScenarioId ); + m_SearchIndices.AddToTail( searchIdx ); + + if ( m_Menu.GetItemCount() == 1 ) + { + m_Menu.SetFocus( 0 ); + } + + UpdateScenarioDisplay(); +} + +//----------------------------------------------------------------- +// Purpose: Show the correct scenario image and text +//----------------------------------------------------------------- +void CSessionBrowserDialog::UpdateScenarioDisplay( void ) +{ + if ( !m_ScenarioIndices.Count() ) + return; + + // Check if the selected map has changed (first menu item) + int idx = m_Menu.GetActiveItemIndex(); + for ( int i = 0; i < m_pScenarioInfos.Count(); ++i ) + { + m_pScenarioInfos[i]->SetVisible( i == m_ScenarioIndices[idx] ); + } + + // Get the screen size + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + bool bLodef = ( tall <= 480 ); + + const char *pState = ""; + switch( m_GameStates[idx] ) + { + case 0: + if ( bLodef ) + { + pState = "#TF_GameState_InLobby_lodef"; + } + else + { + pState = "#TF_GameState_InLobby"; + } + break; + + case 1: + if ( bLodef ) + { + pState = "#TF_GameState_GameInProgress_lodef"; + } + else + { + pState = "#TF_GameState_GameInProgress"; + } + break; + } + + char szTime[32] = {0}; + if ( m_GameTimes[idx] >= NO_TIME_LIMIT ) + { + Q_strncpy( szTime, "#TF_NoTimeLimit", sizeof( szTime) ); + } + else + { + Q_snprintf( szTime, sizeof( szTime), "%d:00", m_GameTimes[idx] ); + } + + if ( !m_pScenarioInfos.IsValidIndex( m_ScenarioIndices[idx] ) ) + return; + + CScenarioInfoPanel *pPanel = m_pScenarioInfos[ m_ScenarioIndices[idx] ]; + pPanel->m_pDescOne->SetText( pState ); + pPanel->m_pDescTwo->SetText( szTime ); +} + + +//----------------------------------------------------------------- +// helper to swap two ints in a utlvector +//----------------------------------------------------------------- +static void Swap( CUtlVector< int > &vec, int iOne, int iTwo ) +{ + int temp = vec[iOne]; + vec[iOne] = vec[iTwo]; + vec[iTwo] = temp; +} + +//----------------------------------------------------------------- +// Purpose: Swap the order of two menu items +//----------------------------------------------------------------- +void CSessionBrowserDialog::SwapMenuItems( int iOne, int iTwo ) +{ + Swap( m_ScenarioIndices, iOne, iTwo ); + Swap( m_SearchIndices, iOne, iTwo ); + Swap( m_GameStates, iOne, iTwo ); + Swap( m_GameTimes, iOne, iTwo ); + + // swap the XUIDs, too + XUID temp = m_XUIDs[iOne]; + m_XUIDs[iOne] = m_XUIDs[iTwo]; + m_XUIDs[iTwo] = temp; +} + +//----------------------------------------------------------------- +// Purpose: Handle commands from the dialog menu +//----------------------------------------------------------------- +void CSessionBrowserDialog::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "SelectSession" ) ) + { + int idx = m_SearchIndices[ m_Menu.GetActiveItemIndex() ]; + matchmaking->SelectSession( idx ); + } + + m_pParent->OnCommand( pCommand ); +} + +//----------------------------------------------------------------- +// Purpose: Send key presses to the dialog's menu +//----------------------------------------------------------------- +void CSessionBrowserDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + switch( code ) + { + case KEY_XBUTTON_B: + matchmaking->KickPlayerFromSession( 0 ); + break; + case KEY_XBUTTON_X: +#ifdef _X360 + int idx = m_Menu.GetActiveItemIndex(); + if ( m_XUIDs.IsValidIndex( idx ) ) + { + XShowGamerCardUI( XBX_GetPrimaryUserId(), m_XUIDs[idx] ); + } +#endif + break; + } + + BaseClass::OnKeyCodePressed( code ); + + // Selected session may have been updated + UpdateScenarioDisplay(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSessionBrowserDialog::OnThink() +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + m_Menu.HandleKeyCode( code ); + UpdateScenarioDisplay(); + } + + BaseClass::OnThink(); +} diff --git a/gameui/matchmaking/sessionbrowserdialog.h b/gameui/matchmaking/sessionbrowserdialog.h new file mode 100644 index 0000000..ea77b9b --- /dev/null +++ b/gameui/matchmaking/sessionbrowserdialog.h @@ -0,0 +1,46 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Present a list of sessions from which the player can choose a game to join. +// +//=====================================================================================// + +#ifndef SESSIONBROWSERDIALOG_H +#define SESSIONBROWSERDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +class CSessionBrowserDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CSessionBrowserDialog, CBaseDialog ); + +public: + CSessionBrowserDialog( vgui::Panel *parent, KeyValues *pDialogKeys ); + ~CSessionBrowserDialog(); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnCommand( const char *pCommand ); + virtual void OnThink(); + + virtual void SwapMenuItems( int iOne, int iTwo ); + + void UpdateScenarioDisplay( void ); + void SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping ); + + KeyValues *m_pDialogKeys; + + CUtlVector< CScenarioInfoPanel* > m_pScenarioInfos; + CUtlVector< int > m_ScenarioIndices; + CUtlVector< int > m_SearchIndices; + CUtlVector< int > m_GameStates; + CUtlVector< int > m_GameTimes; + CUtlVector< XUID > m_XUIDs; +}; + + +#endif // SESSIONBROWSERDIALOG_H diff --git a/gameui/matchmaking/sessionlobbydialog.cpp b/gameui/matchmaking/sessionlobbydialog.cpp new file mode 100644 index 0000000..6f4e808 --- /dev/null +++ b/gameui/matchmaking/sessionlobbydialog.cpp @@ -0,0 +1,739 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "sessionlobbydialog.h" +#include "engine/imatchmaking.h" +#include "GameUI_Interface.h" +#include "EngineInterface.h" +#include "vgui/ISurface.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui/ILocalize.h" +#include "BasePanel.h" +#include "matchmakingbasepanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CSessionLobbyDialog *g_pLobbyDialog; + +//-------------------------------- +// CSessionLobbyDialog +//-------------------------------- +CSessionLobbyDialog::CSessionLobbyDialog( vgui::Panel *pParent ) : BaseClass( pParent, "SessionLobbyDialog" ) +{ + m_Menus[0].SetParent( this ); + m_Menus[0].SetName( "BluePlayers" ); + m_Menus[1].SetParent( this ); + m_Menus[1].SetName( "RedPlayers" ); + + m_iLocalTeam = -1; + m_iActiveMenu = -1; + m_nHostId = 0; + m_bHostLobby = false; + m_bCenterOnScreen = true; + + m_pLobbyStateBg = new vgui::Panel( this, "LobbyStateBg" ); + m_pLobbyStateLabel = new CPropertyLabel( this, "LobbyStateLabel", "" ); + m_pLobbyStateIcon = new CPropertyLabel( this, "LobbyStateIcon", "" ); + m_pHostLabel = new CPropertyLabel( this, "HostLabel", "" ); + m_pHostOptionsPanel = new vgui::EditablePanel( this, "HostOptions" ); + + m_pScenarioInfo = new CScenarioInfoPanel( this, "GameScenario" ); + m_pTeamInfos[BLUE_TEAM_LOBBY] = new CScenarioInfoPanel( this, "BlueTeamDescription" ); + m_pTeamInfos[RED_TEAM_LOBBY] = new CScenarioInfoPanel( this, "RedTeamDescription" ); + + m_pDialogKeys = NULL; + + g_pLobbyDialog = this; + + m_bStartingGame = false; + m_nLastPlayersNeeded = 0; + + m_nMinInfoHeight[BLUE_TEAM_LOBBY] = 0; + m_nMinInfoHeight[RED_TEAM_LOBBY] = 0; +} + +CSessionLobbyDialog::~CSessionLobbyDialog() +{ + delete m_pLobbyStateBg; + delete m_pLobbyStateLabel; + delete m_pLobbyStateIcon; + delete m_pHostLabel; + delete m_pHostOptionsPanel; + + delete m_pScenarioInfo; + for ( int i = 0; i < TOTAL_LOBBY_TEAMS; ++i ) + { + delete m_pTeamInfos[i]; + } +} + +//--------------------------------------------------------------------- +// Purpose: Dialog keys contain session contexts and properties +//--------------------------------------------------------------------- +void CSessionLobbyDialog::SetDialogKeys( KeyValues *pKeys ) +{ + m_pDialogKeys = pKeys; + InvalidateLayout(); +} + +//--------------------------------------------------------------------- +// Purpose: Helper to set label text from keyvalues +//--------------------------------------------------------------------- +void CSessionLobbyDialog::SetTextFromKeyvalues( CPropertyLabel *pLabel ) +{ + KeyValues *pKey = m_pDialogKeys->FindKey( pLabel->m_szPropertyString ); + if ( pKey ) + { + const char *pString = pKey->GetString( "displaystring", NULL ); + if ( pString ) + { + pLabel->SetText( pString ); + } + } +} + +//--------------------------------------------------------------------- +// Purpose: Center the dialog on the screen +//--------------------------------------------------------------------- +void CSessionLobbyDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( !m_pDialogKeys ) + return; + + // Set the label strings according to the keyvalues passed in + SetTextFromKeyvalues( m_pScenarioInfo->m_pTitle ); + SetTextFromKeyvalues( m_pScenarioInfo->m_pDescOne ); + SetTextFromKeyvalues( m_pScenarioInfo->m_pDescTwo ); + SetTextFromKeyvalues( m_pScenarioInfo->m_pDescThree ); + SetTextFromKeyvalues( m_pScenarioInfo->m_pValueTwo ); + SetTextFromKeyvalues( m_pScenarioInfo->m_pValueThree ); + + const char *pDiskName = "unknown"; + KeyValues *pName = m_pDialogKeys->FindKey( "MapDiskNames" ); + if ( pName ) + { + KeyValues *pScenario = m_pDialogKeys->FindKey( "CONTEXT_SCENARIO" ); + if ( pScenario ) + { + pDiskName = pName->GetString( pScenario->GetString( "displaystring" ), "unknown" ); + } + } + + // find the scenario type + KeyValues *pType = m_pDialogKeys->FindKey( "ScenarioTypes" ); + if ( pType ) + { + const char *pString = pType->GetString( pDiskName, NULL ); + if ( pString ) + { + m_pScenarioInfo->m_pSubtitle->SetText( pString ); + } + } + + // Set the team goals + KeyValues *pGoals = m_pDialogKeys->FindKey( "TeamGoals" ); + if ( pGoals ) + { + KeyValues *pTeam = pGoals->FindKey( "Blue" ); + if ( pTeam ) + { + m_pTeamInfos[BLUE_TEAM_LOBBY]->m_pDescOne->SetText( pTeam->GetString( pDiskName, "" ) ); + } + pTeam = pGoals->FindKey( "Red" ); + if ( pTeam ) + { + m_pTeamInfos[RED_TEAM_LOBBY]->m_pDescOne->SetText( pTeam->GetString( pDiskName, "" ) ); + } + } + + for ( int i = 0; i < TOTAL_LOBBY_TEAMS; ++i ) + { + UpdatePlayerCountDisplay( i ); + } + + if ( m_bCenterOnScreen ) + { + MoveToCenterOfScreen(); + } + + // Don't allow player reviews in system link games + CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent ); + if ( pBase ) + { + pBase->SetFooterButtonVisible( "#GameUI_PlayerReview", pBase->GetGameType() != GAMETYPE_SYSTEMLINK_MATCH ); + + // hide the settings changing if we're in a ranked game + if ( m_pHostOptionsPanel ) + { + bool bVisible = pBase->GetGameType() != GAMETYPE_RANKED_MATCH; + vgui::Label *pSettingsLabel = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("ChangeSettingsButton",true); + if ( pSettingsLabel ) + { + pSettingsLabel->SetVisible( bVisible ); + } + pSettingsLabel = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("ChangeSettingsText",true); + if ( pSettingsLabel ) + { + pSettingsLabel->SetVisible( bVisible ); + } + } + } +} + +//--------------------------------------------------------------------- +// Purpose: Parse session properties and contexts from the resource file +//--------------------------------------------------------------------- +void CSessionLobbyDialog::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + m_nImageBorderWidth = pResourceData->GetInt( "imageborderwidth", 15 ); + m_nTeamspacing = pResourceData->GetInt( "teamspacing", 0 ); + m_bHostLobby = pResourceData->GetInt( "hostlobby", 0 ) != 0; + m_bCenterOnScreen = pResourceData->GetInt( "center", 1 ) == 1; + + Q_strncpy( m_szCommand, pResourceData->GetString( "commandstring", "NULL" ), sizeof( m_szCommand ) ); +} + +//--------------------------------------------------------------------- +// Purpose: Set up colors and other such stuff +//--------------------------------------------------------------------- +void CSessionLobbyDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + Color cLabelColor = pScheme->GetColor( "MatchmakingDialogTitleColor", Color( 0, 0, 0, 255 ) ); + + m_pLobbyStateLabel->SetFgColor( cLabelColor ); + m_pHostLabel->SetFgColor( cLabelColor ); + + m_pLobbyStateBg->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) ); + m_pLobbyStateBg->SetPaintBackgroundType( 2 ); + + m_pHostOptionsPanel->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) ); + m_pHostOptionsPanel->SetPaintBackgroundType( 2 ); + + m_pScenarioInfo->SetBgColor( pScheme->GetColor( "TanDarker", Color( 0, 0, 0, 255 ) ) ); + m_pTeamInfos[BLUE_TEAM_LOBBY]->SetBgColor( pScheme->GetColor( "HudBlueTeam", Color( 0, 0, 0, 255 ) ) ); + m_pTeamInfos[RED_TEAM_LOBBY]->SetBgColor( pScheme->GetColor( "HudRedTeam", Color( 0, 0, 0, 255 ) ) ); + + // Cache of these heights so we never go below them when resizing + m_nMinInfoHeight[BLUE_TEAM_LOBBY] = m_pTeamInfos[BLUE_TEAM_LOBBY]->GetTall(); + m_nMinInfoHeight[RED_TEAM_LOBBY] = m_pTeamInfos[RED_TEAM_LOBBY]->GetTall(); + + //Lets set all the labels this panel owns to be the right fgcolor. hooray vgui! + int iChildren = m_pHostOptionsPanel->GetChildCount(); + + for ( int i=0;i<iChildren;i++ ) + { + vgui::Label *pLabel = dynamic_cast< vgui::Label * >( m_pHostOptionsPanel->GetChild(i) ); + if ( pLabel ) + { + SETUP_PANEL( pLabel ); + pLabel->SetFgColor( cLabelColor ); + } + } + + vgui::Label *pPlayerReviewLabel = (vgui::Label *)FindChildByName("PlayerReviewLabel" ); + if ( pPlayerReviewLabel ) + { + SETUP_PANEL( pPlayerReviewLabel ); + pPlayerReviewLabel->SetFgColor( cLabelColor ); + } + + SetLobbyReadyState( m_nLastPlayersNeeded ); +} + +void CSessionLobbyDialog::PositionTeamInfos() +{ + // Line up the team info panels and menus + int x, y; + int menux, menuy; + m_pTeamInfos[0]->GetPos( x, y ); + m_Menus[0].GetPos( menux, menuy ); + + for ( int i = 1; i < TOTAL_LOBBY_TEAMS; ++i ) + { + y += m_pTeamInfos[i - 1]->GetTall() + m_nTeamspacing; + m_pTeamInfos[i]->SetPos( x, y ); + m_Menus[i].SetPos( menux, y ); + } +} + +void CSessionLobbyDialog::ActivateNextMenu() +{ + int startIndex = m_iActiveMenu; + m_Menus[m_iActiveMenu].SetFocus( -1 ); + do + { + m_iActiveMenu = (m_iActiveMenu + 1) % TOTAL_LOBBY_TEAMS; + } while ( m_Menus[m_iActiveMenu].GetItemCount() == 0 && m_iActiveMenu != startIndex ); + + m_Menus[m_iActiveMenu].SetFocus( 0 ); +} + +void CSessionLobbyDialog::ActivatePreviousMenu() +{ + int startIndex = m_iActiveMenu; + m_Menus[m_iActiveMenu].SetFocus( -1 ); + do + { + m_iActiveMenu = m_iActiveMenu ? m_iActiveMenu - 1 : TOTAL_LOBBY_TEAMS - 1; + } while ( m_Menus[m_iActiveMenu].GetItemCount() == 0 && m_iActiveMenu != startIndex ); + + m_Menus[m_iActiveMenu].SetFocus( m_Menus[m_iActiveMenu].GetItemCount() - 1 ); +} + +void CSessionLobbyDialog::UpdatePlayerCountDisplay( int iTeam ) +{ + int ct = m_Menus[iTeam].GetItemCount(); + wchar_t wszString[32]; + wchar_t *wzPlayersFmt = g_pVGuiLocalize->Find( ct != 1 ? "#TF_ScoreBoard_Players" : "#TF_ScoreBoard_Player" ); + wchar_t wzPlayerCt[8]; + V_snwprintf( wzPlayerCt, ARRAYSIZE( wzPlayerCt ), L"%d", ct ); + g_pVGuiLocalize->ConstructString( wszString, sizeof( wszString ), wzPlayersFmt, 1, wzPlayerCt ); + + m_pTeamInfos[iTeam]->m_pSubtitle->SetText( wszString ); + + if ( m_nMinInfoHeight[iTeam] == 0 ) + { + m_nMinInfoHeight[iTeam] = m_pTeamInfos[iTeam]->GetTall(); + } + + int height = max( m_nMinInfoHeight[iTeam], m_Menus[iTeam].GetTall() ); + m_pTeamInfos[iTeam]->SetTall( height ); + + PositionTeamInfos(); +} + +void CSessionLobbyDialog::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int iTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ) +{ + if ( m_iLocalTeam == -1 ) + { + m_iLocalTeam = iTeam; + } + + bool bReady = ( nPlayersNeeded == 0 ); + + // Look for the player + int iFoundTeam = -1; + int iFoundItem = -1; + CPlayerItem *pFound = NULL; + for ( int iMenu = 0; iMenu < TOTAL_LOBBY_TEAMS && !pFound; ++iMenu ) + { + CDialogMenu &menu = m_Menus[iMenu]; + + if ( menu.GetItemCount() == 0 ) + continue; + + for ( int idx = 0; idx < menu.GetItemCount(); ++idx ) + { + CPlayerItem *pPlayerItem = dynamic_cast< CPlayerItem* >( menu.GetItem( idx ) ); + if ( pPlayerItem && pPlayerItem->m_nId == nPlayerId ) + { + pFound = pPlayerItem; + iFoundTeam = iMenu; + iFoundItem = idx; + break; + } + } + } + + // Update menu and item focus if the player changed teams + if ( iFoundTeam != iTeam ) + { + if ( pFound ) + { + // Remove the player from the current team + m_Menus[iFoundTeam].RemovePlayerItem( iFoundItem ); + UpdatePlayerCountDisplay( iFoundTeam ); + } + + if ( 0 <= iTeam && iTeam < TOTAL_LOBBY_TEAMS ) + { + // Add the player to the new team + m_Menus[iTeam].AddPlayerItem( pName, nPlayerId, cVoiceState, bReady ); + UpdatePlayerCountDisplay( iTeam ); + } + + // if the player joined an empty lobby, set the active team + if ( m_iActiveMenu == -1 ) + { + m_iActiveMenu = iTeam; + m_Menus[m_iActiveMenu].SetFocus( 0 ); + } + + // update the highlight position + if ( iFoundTeam == m_iActiveMenu ) + { + CDialogMenu &activeMenu = m_Menus[m_iActiveMenu]; + int iActive = activeMenu.GetActiveItemIndex(); + + if ( iActive == iFoundItem ) + { + // The changed player was also the highlighted player + if ( iTeam >= 0 ) + { + // Move the highlight to the player's new position + activeMenu.SetFocus( -1 ); + m_iActiveMenu = iTeam; + m_Menus[m_iActiveMenu].SetFocus( m_Menus[m_iActiveMenu].GetItemCount() - 1 ); + } + else + { + // player left the game, move the highlight to the next filled slot + if ( iActive >= activeMenu.GetItemCount() ) + { + ActivateNextMenu(); + } + } + } + else if ( iActive > iFoundItem ) + { + // Need to drop the highlighted index one slot + m_Menus[m_iActiveMenu].SetFocus( iActive - 1 ); + } + } + } + else + { + if ( pFound ) + { + if ( pFound->m_bVoice != cVoiceState ) + { + pFound->m_bVoice = cVoiceState; + pFound->InvalidateLayout(); + } + } + } + + if ( bHost ) + { + wchar_t wszString[MAX_PATH]; + wchar_t wszHostname[MAX_PATH]; + wchar_t *wzHostFmt = g_pVGuiLocalize->Find( "#TF_Lobby_Host" ); + g_pVGuiLocalize->ConvertANSIToUnicode( pName, wszHostname, sizeof( wszHostname ) ); + + V_snwprintf( wszString, ARRAYSIZE(wszString), L"%s\n%s", wzHostFmt, wszHostname ); + + m_pHostLabel->SetText( wszString ); + m_nHostId = nPlayerId; + } + + int iPlayersNeeded = ( bReady ) ? 0 : nPlayersNeeded; + SetLobbyReadyState( iPlayersNeeded ); + m_nLastPlayersNeeded = iPlayersNeeded; + + InvalidateLayout( true, false ); +} + +void CSessionLobbyDialog::SetLobbyReadyState( int nPlayersNeeded ) +{ + // check if the host is allowed to start the game + if ( nPlayersNeeded <= 0 ) + { + if ( m_bHostLobby ) + { + m_pLobbyStateLabel->SetText( "#TF_PressStart" ); + m_pLobbyStateIcon->SetText( "#GameUI_Icons_START" ); + } + else + { + m_pLobbyStateLabel->SetText( "#TF_WaitingForHost" ); + m_pLobbyStateIcon->SetText( "#TF_Icon_Alert" ); + + m_bStartingGame = false; // client guesses that they can change teams + } + } + else + { + wchar_t wszWaiting[64]; + wchar_t *wzWaitingFmt; + if ( nPlayersNeeded == 1 ) + { + wzWaitingFmt = g_pVGuiLocalize->Find( "#TF_WaitingForPlayerFmt" ); + } + else + { + wzWaitingFmt = g_pVGuiLocalize->Find( "#TF_WaitingForPlayersFmt" ); + } + wchar_t wzPlayers[8]; + V_snwprintf( wzPlayers, ARRAYSIZE( wzPlayers ), L"%d", nPlayersNeeded ); + g_pVGuiLocalize->ConstructString( wszWaiting, sizeof( wszWaiting ), wzWaitingFmt, 1, wzPlayers ); + m_pLobbyStateLabel->SetText( wszWaiting ); + m_pLobbyStateIcon->SetText( "#TF_Icon_Alert" ); + + // If we were starting and dropped below min players, cancel + SetStartGame( false ); + } +} + +void CSessionLobbyDialog::UpdateCountdown( int seconds ) +{ + if ( seconds == -1 ) + { + // countdown was canceled + SetLobbyReadyState( 0 ); + return; + } + + // Set the text in the countdown label + wchar_t wszCountdown[MAX_PATH]; + wchar_t wszSeconds[MAX_PATH]; + wchar_t *wzCountdownFmt; + if ( seconds != 1 ) + { + wzCountdownFmt = g_pVGuiLocalize->Find( "#TF_StartingInSecs" ); + } + else + { + wzCountdownFmt = g_pVGuiLocalize->Find( "#TF_StartingInSec" ); + } + V_snwprintf( wszSeconds, ARRAYSIZE( wszSeconds ), L"%d", seconds ); + g_pVGuiLocalize->ConstructString( wszCountdown, sizeof( wszCountdown ), wzCountdownFmt, 1, wszSeconds ); + + m_pLobbyStateLabel->SetText( wszCountdown ); + + if ( !m_bHostLobby ) + { + m_bStartingGame = true; // client guesses that they can't change teams + } +} + +//----------------------------------------------------------------- +// Purpose: Send key presses to the dialog's menu +//----------------------------------------------------------------- +void CSessionLobbyDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + CDialogMenu *pMenu = &m_Menus[m_iActiveMenu]; + if ( !pMenu ) + return; + + int idx = pMenu->GetActiveItemIndex(); + int itemCt = pMenu->GetItemCount(); + + CPlayerItem *pItem = dynamic_cast< CPlayerItem* >( pMenu->GetItem( idx ) ); + if ( !pItem ) + return; + + SetDeleteSelfOnClose( true ); + + switch( code ) + { + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + if ( idx >= itemCt - 1 ) + { + ActivateNextMenu(); + } + else + { + pMenu->HandleKeyCode( code ); + } + break; + + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case STEAMCONTROLLER_DPAD_UP: + if ( idx <= 0 ) + { + ActivatePreviousMenu(); + } + else + { + pMenu->HandleKeyCode( code ); + } + break; + + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: +#ifdef _X360 + XShowGamerCardUI( XBX_GetPrimaryUserId(), pItem->m_nId ); +#endif + break; + + case KEY_XBUTTON_RIGHT_SHOULDER: +#ifdef _X360 + { + // Don't allow player reviews in system link games + CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent ); + if ( pBase && pBase->GetGameType() == GAMETYPE_SYSTEMLINK_MATCH ) + break; + } + + XShowPlayerReviewUI( XBX_GetPrimaryUserId(), pItem->m_nId ); +#endif + break; + + case KEY_XBUTTON_LEFT_SHOULDER: + { + // Don't kick ourselves + if ( m_bHostLobby ) + { + if ( pItem && ((CPlayerItem*)pItem)->m_nId != m_nHostId ) + { + GameUI().ShowMessageDialog( MD_KICK_CONFIRMATION, this ); + } + else + { + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } + } + } + break; + + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + GameUI().ShowMessageDialog( MD_EXIT_SESSION_CONFIRMATION, this ); + + if ( m_bHostLobby ) + { + SetStartGame( false ); + } + break; + + case KEY_XBUTTON_X: + case STEAMCONTROLLER_X: + if ( m_bStartingGame ) + { + // We think we're loading the game, so play deny sound + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + } + else + { + matchmaking->ChangeTeam( NULL ); + } + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + if ( m_bHostLobby ) + { + // Don't allow settings changes in ranked games + CMatchmakingBasePanel *pBase = dynamic_cast< CMatchmakingBasePanel* >( m_pParent ); + if ( pBase && pBase->GetGameType() == GAMETYPE_RANKED_MATCH ) + break; + + SetDeleteSelfOnClose( false ); + OnCommand( "SessionOptions_Modify" ); + SetStartGame( false ); + } + break; + + case KEY_XBUTTON_START: + SetStartGame( !m_bStartingGame ); + break; + + default: + pMenu->HandleKeyCode( code ); + break; + } +} + +//--------------------------------------------------------------------- +// Purpose: start and stop the countdown +//--------------------------------------------------------------------- +void CSessionLobbyDialog::SetStartGame( bool bStartGame ) +{ + if ( !m_bHostLobby ) + return; + + bool bCanStart = true; + if ( m_bStartingGame != bStartGame ) + { + if ( bStartGame ) + { + m_bStartingGame = matchmaking->StartGame(); + bCanStart = m_bStartingGame; + } + else + { + if ( matchmaking->CancelStartGame() ) + { + m_bStartingGame = false; + } + } + + // If we can start the game but haven't started yet, show the "Start Game" label + bool bShowStartGame = bCanStart && !m_bStartingGame; + + // show/hide the "start game" and countdown hint label based on state + vgui::Label *pStartGame = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("StartGameText" ); + if ( pStartGame ) + { + pStartGame->SetVisible( m_bStartingGame == false ); + } + + vgui::Label *pCancelCountdown = (vgui::Label *)m_pHostOptionsPanel->FindChildByName("CancelGameText" ); + if ( pCancelCountdown ) + { + pCancelCountdown->SetVisible( m_bStartingGame == true ); + } + + if ( bShowStartGame ) + { + m_pLobbyStateLabel->SetText( "#TF_PressStart" ); + m_pLobbyStateIcon->SetText( "#GameUI_Icons_START" ); + } + } +} + +//--------------------------------------------------------------------- +// Purpose: Handle menu commands +//--------------------------------------------------------------------- +void CSessionLobbyDialog::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "ReturnToMainMenu" ) ) + { + matchmaking->KickPlayerFromSession( 0 ); + } + else if ( !Q_stricmp( pCommand, "KickPlayer" ) ) + { + CDialogMenu *pMenu = &m_Menus[m_iActiveMenu]; + CPlayerItem *pItem = (CPlayerItem*)pMenu->GetItem( pMenu->GetActiveItemIndex() ); + if ( pItem ) + { + matchmaking->KickPlayerFromSession( pItem->m_nId ); + } + } + + GetParent()->OnCommand( pCommand ); +} + +static int pnum = 1; + +CON_COMMAND( mm_add_player, "Add a player" ) +{ + if ( args.ArgC() >= 5 ) + { + int team = atoi( args[1] ); + const char *pName = args[2]; + uint32 id = atoi( args[3] ); + byte cVoiceState = atoi( args[4] ) != 0; + int nPlayersNeeded = atoi( args[5] ); + g_pLobbyDialog->UpdatePlayerInfo( id, pName, team, cVoiceState, nPlayersNeeded, false ); + } + else if ( args.ArgC() >= 1 ) + { + char name[ 32 ]; + int team = pnum & 0x1; + int id = pnum++; + if ( args.ArgC() >= 2 ) + { + team = atoi( args[ 1 ] ); + if ( args.ArgC() >= 3 ) + { + id = atoi( args[2] ); + } + } + Q_snprintf( name, sizeof( name ), "Player%d", pnum ); + g_pLobbyDialog->UpdatePlayerInfo( id, name, team, true, 0, false ); + } +}
\ No newline at end of file diff --git a/gameui/matchmaking/sessionlobbydialog.h b/gameui/matchmaking/sessionlobbydialog.h new file mode 100644 index 0000000..9049333 --- /dev/null +++ b/gameui/matchmaking/sessionlobbydialog.h @@ -0,0 +1,85 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SESSIONLOBBYDIALOG_H +#define SESSIONLOBBYDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +//----------------------------------------------------------------------------- +// Purpose: Simple menu to choose a matchmaking session type +//----------------------------------------------------------------------------- +class CSessionLobbyDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CSessionLobbyDialog, CBaseDialog ); + +public: + CSessionLobbyDialog( vgui::Panel *parent ); + ~CSessionLobbyDialog(); + + virtual void OnCommand( const char *pCommand ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + + virtual void PerformLayout(); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void SetDialogKeys( KeyValues *pKeys ); + void UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost ); + void UpdateCountdown( int seconds ); + +private: + void ActivateNextMenu(); + void ActivatePreviousMenu(); + void UpdatePlayerCountDisplay( int iTeam ); + void SetLobbyReadyState( int nPlayersNeeded ); + void PositionTeamInfos(); + + void SetTextFromKeyvalues( CPropertyLabel *pLabel ); + + void SetStartGame( bool bStartGame ); + + enum + { + BLUE_TEAM_LOBBY, + RED_TEAM_LOBBY, + TOTAL_LOBBY_TEAMS, + }; + + CDialogMenu m_Menus[TOTAL_LOBBY_TEAMS]; + + vgui::Panel *m_pLobbyStateBg; + CPropertyLabel *m_pLobbyStateLabel; + CPropertyLabel *m_pLobbyStateIcon; + CPropertyLabel *m_pHostLabel; + vgui::EditablePanel *m_pHostOptionsPanel; + + KeyValues *m_pDialogKeys; + + CScenarioInfoPanel *m_pScenarioInfo; + CScenarioInfoPanel *m_pTeamInfos[TOTAL_LOBBY_TEAMS]; + + int m_nMinInfoHeight[TOTAL_LOBBY_TEAMS]; + + uint64 m_nHostId; + bool m_bReady; + bool m_bHostLobby; + bool m_bCenterOnScreen; + int m_iLocalTeam; + int m_iActiveMenu; + int m_nImageBorderWidth; + int m_nTeamspacing; + char m_szCommand[MAX_COMMAND_LEN]; + + bool m_bStartingGame; + int m_nLastPlayersNeeded; +}; + + +#endif // SESSIONLOBBYDIALOG_H diff --git a/gameui/matchmaking/sessionoptionsdialog.cpp b/gameui/matchmaking/sessionoptionsdialog.cpp new file mode 100644 index 0000000..fba64d8 --- /dev/null +++ b/gameui/matchmaking/sessionoptionsdialog.cpp @@ -0,0 +1,420 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Dialog for setting customizable game options +// +//=============================================================================// + +#include "sessionoptionsdialog.h" +#include "engine/imatchmaking.h" +#include "EngineInterface.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Label.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//--------------------------------------------------------------------- +// CSessionOptionsDialog +//--------------------------------------------------------------------- +CSessionOptionsDialog::CSessionOptionsDialog( vgui::Panel *pParent ) : BaseClass( pParent, "SessionOptions" ) +{ + SetDeleteSelfOnClose( true ); + + m_pDialogKeys = NULL; + m_bModifySession = false; +} + +CSessionOptionsDialog::~CSessionOptionsDialog() +{ + m_pScenarioInfos.PurgeAndDeleteElements(); +} + +//--------------------------------------------------------------------- +// Purpose: Dialog keys contain session contexts and properties +//--------------------------------------------------------------------- +void CSessionOptionsDialog::SetDialogKeys( KeyValues *pKeys ) +{ + m_pDialogKeys = pKeys; +} + + +//--------------------------------------------------------------------- +// Purpose: Strip off the game type from the resource name +//--------------------------------------------------------------------- +void CSessionOptionsDialog::SetGameType( const char *pString ) +{ + // Get the full gametype from the resource name + const char *pGametype = Q_stristr( pString, "_" ); + if ( !pGametype ) + return; + + Q_strncpy( m_szGametype, pGametype + 1, sizeof( m_szGametype ) ); + + // set the menu filter + m_Menu.SetFilter( m_szGametype ); +} + +//--------------------------------------------------------------------- +// Purpose: Center the dialog on the screen +//--------------------------------------------------------------------- +void CSessionOptionsDialog::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + UpdateScenarioDisplay(); + + MoveToCenterOfScreen(); + + if ( m_pRecommendedLabel ) + { + bool bHosting = ( Q_stristr( m_szGametype, "Host" ) ); + m_pRecommendedLabel->SetVisible( bHosting ); + } +} + +//--------------------------------------------------------------------- +// Purpose: Apply common properties and contexts +//--------------------------------------------------------------------- +void CSessionOptionsDialog::ApplyCommonProperties( KeyValues *pKeys ) +{ + for ( KeyValues *pProperty = pKeys->GetFirstSubKey(); pProperty != NULL; pProperty = pProperty->GetNextKey() ) + { + const char *pName = pProperty->GetName(); + + if ( !Q_stricmp( pName, "SessionContext" ) ) + { + // Create a new session context + sessionProperty_t ctx; + ctx.nType = SESSION_CONTEXT; + Q_strncpy( ctx.szID, pProperty->GetString( "id", "NULL" ), sizeof( ctx.szID ) ); + Q_strncpy( ctx.szValue, pProperty->GetString( "value", "NULL" ), sizeof( ctx.szValue ) ); + // ctx.szValueType not used + + m_SessionProperties.AddToTail( ctx ); + } + else if ( !Q_stricmp( pName, "SessionProperty" ) ) + { + // Create a new session property + sessionProperty_t prop; + prop.nType = SESSION_PROPERTY; + Q_strncpy( prop.szID, pProperty->GetString( "id", "NULL" ), sizeof( prop.szID ) ); + Q_strncpy( prop.szValue, pProperty->GetString( "value", "NULL" ), sizeof( prop.szValue ) ); + Q_strncpy( prop.szValueType, pProperty->GetString( "valuetype", "NULL" ), sizeof( prop.szValueType ) ); + + m_SessionProperties.AddToTail( prop ); + } + else if ( !Q_stricmp( pName, "SessionFlag" ) ) + { + sessionProperty_t flag; + flag.nType = SESSION_FLAG; + Q_strncpy( flag.szID, pProperty->GetString(), sizeof( flag.szID ) ); + + m_SessionProperties.AddToTail( flag ); + } + } +} + +//--------------------------------------------------------------------- +// Purpose: Parse session properties and contexts from the resource file +//--------------------------------------------------------------------- +void CSessionOptionsDialog::ApplySettings( KeyValues *pResourceData ) +{ + BaseClass::ApplySettings( pResourceData ); + + // Apply settings common to all game types + ApplyCommonProperties( pResourceData ); + + // Apply settings specific to this game type + KeyValues *pSettings = pResourceData->FindKey( m_szGametype ); + if ( pSettings ) + { + Q_strncpy( m_szCommand, pSettings->GetString( "commandstring", "NULL" ), sizeof( m_szCommand ) ); + m_pTitle->SetText( pSettings->GetString( "title", "Unknown" ) ); + + ApplyCommonProperties( pSettings ); + } + + KeyValues *pScenarios = pResourceData->FindKey( "ScenarioInfoPanels" ); + if ( pScenarios ) + { + for ( KeyValues *pScenario = pScenarios->GetFirstSubKey(); pScenario != NULL; pScenario = pScenario->GetNextKey() ) + { + CScenarioInfoPanel *pScenarioInfo = new CScenarioInfoPanel( this, "ScenarioInfoPanel" ); + SETUP_PANEL( pScenarioInfo ); + pScenarioInfo->m_pTitle->SetText( pScenario->GetString( "title" ) ); + pScenarioInfo->m_pSubtitle->SetText( pScenario->GetString( "subtitle" ) ); + pScenarioInfo->m_pMapImage->SetImage( pScenario->GetString( "image" ) ); + + int nTall = pScenario->GetInt( "tall", -1 ); + if ( nTall > 0 ) + { + pScenarioInfo->SetTall( nTall ); + } + + m_pScenarioInfos.AddToTail( pScenarioInfo ); + } + } + + if ( Q_stristr( m_szGametype, "Modify" ) ) + { + m_bModifySession = true; + } +} + +int CSessionOptionsDialog::GetMaxPlayersRecommendedOption( void ) +{ + MM_QOS_t qos = matchmaking->GetQosWithLIVE(); + + // Conservatively assume that every player needs ~ 7 kBytes/s + // plus one for the hosting player. + int numPlayersCanService = 1 + int( qos.flBwUpKbs / 7.0f ); + + // Determine the option that suits our B/W bests + int options[] = { 8, 12, 16 }; + + for ( int k = 1; k < ARRAYSIZE( options ); ++ k ) + { + if ( options[k] > numPlayersCanService ) + { + Msg( "[SessionOptionsDialog] Defaulting number of players to %d (upstream b/w = %.1f kB/s ~ %d players).\n", + options[k - 1], qos.flBwUpKbs, numPlayersCanService ); + return k - 1; + } + } + + return ARRAYSIZE( options ) - 1; +} + +//--------------------------------------------------------------------- +// Purpose: Set up colors and other such stuff +//--------------------------------------------------------------------- +void CSessionOptionsDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + for ( int i = 0; i < m_pScenarioInfos.Count(); ++i ) + { + m_pScenarioInfos[i]->SetBgColor( pScheme->GetColor( "TanDark", Color( 0, 0, 0, 255 ) ) ); + } + + m_pRecommendedLabel = dynamic_cast<vgui::Label *>(FindChildByName( "RecommendedLabel" )); +} + +//--------------------------------------------------------------------- +// Purpose: Send all properties and contexts to the matchmaking session +//--------------------------------------------------------------------- +void CSessionOptionsDialog::SetupSession( void ) +{ + KeyValues *pKeys = new KeyValues( "SessionKeys" ); + + // Send user-selected properties and contexts + for ( int i = 0; i < m_Menu.GetItemCount(); ++i ) + { + COptionsItem *pItem = dynamic_cast< COptionsItem* >( m_Menu.GetItem( i ) ); + if ( !pItem ) + { + continue; + } + + const sessionProperty_t &prop = pItem->GetActiveOption(); + + KeyValues *pProperty = pKeys->CreateNewKey(); + pProperty->SetName( prop.szID ); + pProperty->SetInt( "type", prop.nType ); + pProperty->SetString( "valuestring", prop.szValue ); + pProperty->SetString( "valuetype", prop.szValueType ); + pProperty->SetInt( "optionindex", pItem->GetActiveOptionIndex() ); + } + + // Send contexts and properties parsed from the resource file + for ( int i = 0; i < m_SessionProperties.Count(); ++i ) + { + const sessionProperty_t &prop = m_SessionProperties[i]; + + KeyValues *pProperty = pKeys->CreateNewKey(); + pProperty->SetName( prop.szID ); + pProperty->SetInt( "type", prop.nType ); + pProperty->SetString( "valuestring", prop.szValue ); + pProperty->SetString( "valuetype", prop.szValueType ); + } + + // Matchmaking will make a copy of these keys + matchmaking->SetSessionProperties( pKeys ); + pKeys->deleteThis(); +} + +//----------------------------------------------------------------- +// Purpose: Show the correct scenario image and text +//----------------------------------------------------------------- +void CSessionOptionsDialog::UpdateScenarioDisplay( void ) +{ + // Check if the selected map has changed (first menu item) + int idx = m_Menu.GetActiveOptionIndex( 0 ); + for ( int i = 0; i < m_pScenarioInfos.Count(); ++i ) + { + m_pScenarioInfos[i]->SetVisible( i == idx ); + } +} + +//----------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------- +void CSessionOptionsDialog::OnMenuItemChanged( KeyValues *pData ) +{ + // which item changed + int iItem = pData->GetInt( "item", -1 ); + + if ( iItem >= 0 && iItem < m_Menu.GetItemCount() ) + { + COptionsItem *pActiveOption = dynamic_cast< COptionsItem* >( m_Menu.GetItem( iItem ) ); + if ( pActiveOption ) + { + const sessionProperty_t &activeOption = pActiveOption->GetActiveOption(); + + if ( !Q_strncmp( activeOption.szID, "PROPERTY_GAME_SIZE", sessionProperty_t::MAX_KEY_LEN ) ) + { + // make sure the private slots is less than prop.szValue + + int iMaxPlayers = atoi(activeOption.szValue); + bool bShouldWarnMaxPlayers = ( pActiveOption->GetActiveOptionIndex() > GetMaxPlayersRecommendedOption() ); + m_pRecommendedLabel->SetVisible( bShouldWarnMaxPlayers ); + + // find the private slots option and repopulate it + for ( int iMenu = 0; iMenu < m_Menu.GetItemCount(); ++iMenu ) + { + COptionsItem *pItem = dynamic_cast< COptionsItem* >( m_Menu.GetItem( iMenu ) ); + if ( !pItem ) + { + continue; + } + + const sessionProperty_t &prop = pItem->GetActiveOption(); + + if ( !Q_strncmp( prop.szID, "PROPERTY_PRIVATE_SLOTS", sessionProperty_t::MAX_KEY_LEN ) ) + { + const sessionProperty_t baseProp = pItem->GetActiveOption(); + + // preserve the selection + int iActiveItem = pItem->GetActiveOptionIndex(); + + // clear all options + pItem->DeleteAllOptions(); + + // re-add the items 0 - maxplayers + int nStart = 0; + int nEnd = iMaxPlayers; + int nInterval = 1; + + for ( int i = nStart; i <= nEnd; i += nInterval ) + { + sessionProperty_t propNew; + propNew.nType = SESSION_PROPERTY; + Q_strncpy( propNew.szID, baseProp.szID, sizeof( propNew.szID ) ); + Q_strncpy( propNew.szValueType, baseProp.szValueType, sizeof( propNew.szValueType ) ); + Q_snprintf( propNew.szValue, sizeof( propNew.szValue), "%d", i ); + pItem->AddOption( propNew.szValue, propNew ); + } + + // re-set the focus + pItem->SetOptionFocus( min( iActiveItem, iMaxPlayers ) ); + + // fixup the option sizes + m_Menu.InvalidateLayout(); + } + } + } + } + } +} + +//----------------------------------------------------------------- +// Purpose: Change properties of a menu item +//----------------------------------------------------------------- +void CSessionOptionsDialog::OverrideMenuItem( KeyValues *pItemKeys ) +{ + if ( m_bModifySession && m_pDialogKeys ) + { + if ( !Q_stricmp( pItemKeys->GetName(), "OptionsItem" ) ) + { + const char *pID = pItemKeys->GetString( "id", "NULL" ); + + KeyValues *pKey = m_pDialogKeys->FindKey( pID ); + if ( pKey ) + { + pItemKeys->SetInt( "activeoption", pKey->GetInt( "optionindex" ) ); + } + } + } + + // + // When hosting a new session on LIVE: + // - restrict max number of players to bandwidth allowed + // + if ( !m_bModifySession && + ( !Q_stricmp( m_szGametype, "hoststandard" ) || !Q_stricmp( m_szGametype, "hostranked" ) ) + ) + { + if ( !Q_stricmp( pItemKeys->GetName(), "OptionsItem" ) ) + { + const char *pID = pItemKeys->GetString( "id", "NULL" ); + if ( !Q_stricmp( pID, "PROPERTY_GAME_SIZE" ) ) + { + pItemKeys->SetInt( "activeoption", GetMaxPlayersRecommendedOption() ); + } + } + } +} + +//----------------------------------------------------------------- +// Purpose: Send key presses to the dialog's menu +//----------------------------------------------------------------- +void CSessionOptionsDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A ) + { + OnCommand( m_szCommand ); + } + else if ( (code == KEY_XBUTTON_B || code == STEAMCONTROLLER_B) && m_bModifySession ) + { + // Return to the session lobby without making any changes + OnCommand( "DialogClosing" ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } + + UpdateScenarioDisplay(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSessionOptionsDialog::OnThink() +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + m_Menu.HandleKeyCode( code ); + UpdateScenarioDisplay(); + } + + BaseClass::OnThink(); +} + +//--------------------------------------------------------------------- +// Purpose: Handle menu commands +//--------------------------------------------------------------------- +void CSessionOptionsDialog::OnCommand( const char *pCommand ) +{ + // Don't set up the session if the dialog is just closing + if ( Q_stricmp( pCommand, "DialogClosing" ) ) + { + SetupSession(); + OnClose(); + } + + GetParent()->OnCommand( pCommand ); +} + diff --git a/gameui/matchmaking/sessionoptionsdialog.h b/gameui/matchmaking/sessionoptionsdialog.h new file mode 100644 index 0000000..69de2b3 --- /dev/null +++ b/gameui/matchmaking/sessionoptionsdialog.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Dialog for setting customizable game options +// +//=============================================================================// + +#ifndef SESSIONOPTIONSDIALOG_H +#define SESSIONOPTIONSDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +//----------------------------------------------------------------------------- +// Purpose: Simple menu to choose a matchmaking session type +//----------------------------------------------------------------------------- +class CSessionOptionsDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CSessionOptionsDialog, CBaseDialog ); + +public: + CSessionOptionsDialog( vgui::Panel *parent ); + ~CSessionOptionsDialog(); + + virtual void OnCommand( const char *pCommand ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void OnThink(); + + virtual void PerformLayout( void ); + virtual void ApplySettings( KeyValues *pResourceData ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual void OverrideMenuItem( KeyValues *pKeys ); + + void SetGameType( const char *pGametype ); + void SetDialogKeys( KeyValues *pKeys ); + void ApplyCommonProperties( KeyValues *pKeys ); + + MESSAGE_FUNC_PARAMS( OnMenuItemChanged, "MenuItemChanged", data ); + +private: + void SetupSession( void ); + void UpdateScenarioDisplay( void ); + + int GetMaxPlayersRecommendedOption( void ); + + char m_szCommand[MAX_COMMAND_LEN]; + char m_szGametype[MAX_COMMAND_LEN]; + bool m_bModifySession; + + KeyValues *m_pDialogKeys; + + CUtlVector< sessionProperty_t > m_SessionProperties; + CUtlVector< CScenarioInfoPanel* > m_pScenarioInfos; + + vgui::Label *m_pRecommendedLabel; +}; + + +#endif // SESSIONOPTIONSDIALOG_H diff --git a/gameui/matchmaking/welcomedialog.cpp b/gameui/matchmaking/welcomedialog.cpp new file mode 100644 index 0000000..7249c70 --- /dev/null +++ b/gameui/matchmaking/welcomedialog.cpp @@ -0,0 +1,58 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Matchmaking's "main menu" +// +//=============================================================================// + +#include "welcomedialog.h" +#include "GameUI_Interface.h" +#include "vgui_controls/MessageDialog.h" +#include "ixboxsystem.h" +#include "EngineInterface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//-------------------------------- +// CPlayerMatchDialog +//-------------------------------- +CWelcomeDialog::CWelcomeDialog( vgui::Panel *pParent ) : BaseClass( pParent, "WelcomeDialog" ) +{ + // do nothing +} + +//--------------------------------------------------------- +// Purpose: Set the title and menu positions +//--------------------------------------------------------- +void CWelcomeDialog::PerformLayout( void ) +{ + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------- +// Purpose: Forward commands to the matchmaking base panel +//----------------------------------------------------------------- +void CWelcomeDialog::OnCommand( const char *pCommand ) +{ + BaseClass::OnCommand( pCommand ); +} + +//------------------------------------------------------- +// Keyboard input +//------------------------------------------------------- +void CWelcomeDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + switch( code ) + { + case KEY_XBUTTON_B: + if ( GameUI().IsInLevel() ) + { + m_pParent->OnCommand( "ResumeGame" ); + } + break; + + default: + BaseClass::OnKeyCodePressed( code ); + break; + } +}
\ No newline at end of file diff --git a/gameui/matchmaking/welcomedialog.h b/gameui/matchmaking/welcomedialog.h new file mode 100644 index 0000000..dbef44d --- /dev/null +++ b/gameui/matchmaking/welcomedialog.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Matchmaking's "main menu" +// +//=============================================================================// + +#ifndef WELCOMEDIALOG_H +#define WELCOMEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basedialog.h" + +//----------------------------------------------------------------------------- +// Purpose: Main matchmaking menu +//----------------------------------------------------------------------------- +class CWelcomeDialog : public CBaseDialog +{ + DECLARE_CLASS_SIMPLE( CWelcomeDialog, CBaseDialog ); + +public: + CWelcomeDialog(vgui::Panel *parent); + + virtual void PerformLayout( void ); + virtual void OnCommand( const char *pCommand ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + +private: + bool m_bOnlineEnabled; +}; + + +#endif // WELCOMEDIALOG_H diff --git a/gameui/vcontrolslistpanel.cpp b/gameui/vcontrolslistpanel.cpp new file mode 100644 index 0000000..f644d3e --- /dev/null +++ b/gameui/vcontrolslistpanel.cpp @@ -0,0 +1,207 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vcontrolslistpanel.h" +#include "GameUI_Interface.h" +#include "EngineInterface.h" + +#include <vgui/IInput.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/Cursor.h> +#include <KeyValues.h> + +// NVNT including for input system access +#include "tier2/tier2.h" +#include "inputsystem/iinputsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: panel used for inline editing of key bindings +//----------------------------------------------------------------------------- +class CInlineEditPanel : public vgui::Panel +{ +public: + CInlineEditPanel() : vgui::Panel(NULL, "InlineEditPanel") + { + } + + virtual void Paint() + { + int x = 0, y = 0, wide, tall; + GetSize(wide, tall); + + // Draw a white rectangle around that cell + vgui::surface()->DrawSetColor( 255, 165, 0, 255 ); + vgui::surface()->DrawFilledRect( x, y, x + wide, y + tall ); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + // forward up + if (GetParent()) + { + GetParent()->OnKeyCodeTyped(code); + } + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Panel::ApplySchemeSettings(pScheme); + SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + } + + void OnMousePressed(vgui::MouseCode code) + { + // forward up mouse pressed messages to be handled by the key options + if (GetParent()) + { + GetParent()->OnMousePressed(code); + } + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Construction +//----------------------------------------------------------------------------- +VControlsListPanel::VControlsListPanel( vgui::Panel *parent, const char *listName ) : vgui::SectionedListPanel( parent, listName ) +{ + m_bCaptureMode = false; + m_nClickRow = 0; + m_pInlineEditPanel = new CInlineEditPanel(); + m_hFont = INVALID_FONT; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +VControlsListPanel::~VControlsListPanel() +{ + m_pInlineEditPanel->MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VControlsListPanel::ApplySchemeSettings(IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + m_hFont = pScheme->GetFont("Default", IsProportional() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Start capture prompt display +//----------------------------------------------------------------------------- +void VControlsListPanel::StartCaptureMode( HCursor hCursor ) +{ + m_bCaptureMode = true; + EnterEditMode(m_nClickRow, 1, m_pInlineEditPanel); + input()->SetMouseFocus(m_pInlineEditPanel->GetVPanel()); + input()->SetMouseCapture(m_pInlineEditPanel->GetVPanel()); + // NVNT tell the input system that novint devices + // should be set unable to do menu mouse emulation. + g_pInputSystem->SetNovintPure(true); + + engine->StartKeyTrapMode(); + + if (hCursor) + { + m_pInlineEditPanel->SetCursor(hCursor); + + // save off the cursor position so we can restore it + vgui::input()->GetCursorPos( m_iMouseX, m_iMouseY ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Finish capture prompt display +//----------------------------------------------------------------------------- +void VControlsListPanel::EndCaptureMode( HCursor hCursor ) +{ + m_bCaptureMode = false; + input()->SetMouseCapture(NULL); + LeaveEditMode(); + RequestFocus(); + input()->SetMouseFocus(GetVPanel()); + // NVNT tell the input system that novint devices + // should be allowed to do menu mouse emulation. + g_pInputSystem->SetNovintPure(false); + if (hCursor) + { + m_pInlineEditPanel->SetCursor(hCursor); + surface()->SetCursor(hCursor); + if ( hCursor != dc_none ) + { + vgui::input()->SetCursorPos ( m_iMouseX, m_iMouseY ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set active row column +//----------------------------------------------------------------------------- +void VControlsListPanel::SetItemOfInterest(int itemID) +{ + m_nClickRow = itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: Retrieve row, column of interest +//----------------------------------------------------------------------------- +int VControlsListPanel::GetItemOfInterest() +{ + return m_nClickRow; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're currently waiting to capture a key +//----------------------------------------------------------------------------- +bool VControlsListPanel::IsCapturing( void ) +{ + return m_bCaptureMode; +} + +//----------------------------------------------------------------------------- +// Purpose: Forwards mouse pressed message up to keyboard page when in capture +//----------------------------------------------------------------------------- +void VControlsListPanel::OnMousePressed(vgui::MouseCode code) +{ + if (IsCapturing()) + { + // forward up mouse pressed messages to be handled by the key options + if (GetParent()) + { + GetParent()->OnMousePressed(code); + } + } + else + { + BaseClass::OnMousePressed(code); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: input handler +//----------------------------------------------------------------------------- +void VControlsListPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + if (IsItemIDValid(GetSelectedItem())) + { + // enter capture mode + OnKeyCodePressed(KEY_ENTER); + } + else + { + BaseClass::OnMouseDoublePressed(code); + } +} diff --git a/gameui/vcontrolslistpanel.h b/gameui/vcontrolslistpanel.h new file mode 100644 index 0000000..d468fa4 --- /dev/null +++ b/gameui/vcontrolslistpanel.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( VCONTROLSLISTPANEL_H ) +#define VCONTROLSLISTPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/SectionedListPanel.h> + +//----------------------------------------------------------------------------- +// Purpose: Special list subclass to handle drawing of trap mode prompt on top of +// lists client area +//----------------------------------------------------------------------------- +class VControlsListPanel : public vgui::SectionedListPanel +{ +public: + // Construction + VControlsListPanel( vgui::Panel *parent, const char *listName ); + virtual ~VControlsListPanel(); + + // Start/end capturing + virtual void StartCaptureMode(vgui::HCursor hCursor = NULL); + virtual void EndCaptureMode(vgui::HCursor hCursor = NULL); + virtual bool IsCapturing(); + + // Set which item should be associated with the prompt + virtual void SetItemOfInterest(int itemID); + virtual int GetItemOfInterest(); + + virtual void OnMousePressed(vgui::MouseCode code); + virtual void OnMouseDoublePressed(vgui::MouseCode code); + +private: + void ApplySchemeSettings(vgui::IScheme *pScheme ); + + // Are we showing the prompt? + bool m_bCaptureMode; + // If so, where? + int m_nClickRow; + // Font to use for showing the prompt + vgui::HFont m_hFont; + // panel used to edit + class CInlineEditPanel *m_pInlineEditPanel; + int m_iMouseX, m_iMouseY; + + typedef vgui::SectionedListPanel BaseClass; +}; + +#endif // VCONTROLSLISTPANEL_H
\ No newline at end of file diff --git a/gameui/xbox/xbox.def b/gameui/xbox/xbox.def new file mode 100644 index 0000000..444ddb1 --- /dev/null +++ b/gameui/xbox/xbox.def @@ -0,0 +1,3 @@ +LIBRARY gameui_360.dll +EXPORTS + CreateInterface @1 |