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 /game/client/tf/tf_hud_mainmenuoverride.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/tf_hud_mainmenuoverride.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_mainmenuoverride.cpp | 3427 |
1 files changed, 3427 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_mainmenuoverride.cpp b/game/client/tf/tf_hud_mainmenuoverride.cpp new file mode 100644 index 0000000..3d52a78 --- /dev/null +++ b/game/client/tf/tf_hud_mainmenuoverride.cpp @@ -0,0 +1,3427 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "tf_hud_mainmenuoverride.h" +#include "ienginevgui.h" +#include "tf_viewport.h" +#include "clientmode_tf.h" +#include "confirm_dialog.h" +#include <vgui/ILocalize.h> +#include "tf_controls.h" +#include "tf_gamerules.h" +#include "tf_statsummary.h" +#include "rtime.h" +#include "tf_gcmessages.h" +#include "econ_notifications.h" +#include "c_tf_freeaccount.h" +#include "econ_gcmessages.h" +#include "econ_item_inventory.h" +#include "gcsdk/gcclient.h" +#include "gcsdk/gcclientjob.h" +#include "econ_item_system.h" +#include <vgui_controls/AnimationController.h> +#include "store/store_panel.h" +#include "gc_clientsystem.h" +#include <vgui_controls/ScrollBarSlider.h> +#include "filesystem.h" +#include "tf_hud_disconnect_prompt.h" +#include "tf_gc_client.h" +#include "sourcevr/isourcevirtualreality.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/materialsystem_config.h" +#include "tf_warinfopanel.h" +#include "quest_log_panel.h" +#include "tf_item_inventory.h" +#include "quest_log_panel.h" +#include "econ_quests.h" +#include "tf_streams.h" +#include "tf_matchmaking_shared.h" +#include "tf_lobby_container_frame_comp.h" +#include "tf_lobby_container_frame_mvm.h" +#include "tf_lobby_container_frame_casual.h" + +#include "replay/ireplaysystem.h" +#include "replay/ienginereplay.h" +#include "replay/vgui/replayperformanceeditor.h" +#include "materialsystem/itexture.h" +#include "imageutils.h" +#include "icommandline.h" +#include "vgui/ISystem.h" +#include "report_player_dialog.h" + +#ifdef SAXXYMAINMENU_ENABLED +#include "tf_hud_saxxycontest.h" +#endif + +#include "c_tf_gamestats.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ); + +extern const char *g_sImagesBlue[]; +extern int EconWear_ToIntCategory( float flWear ); + +void cc_tf_safemode_toggle( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + CHudMainMenuOverride *pMMOverride = (CHudMainMenuOverride*)( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) ); + if ( pMMOverride ) + { + pMMOverride->InvalidateLayout(); + } +} + +ConVar tf_recent_achievements( "tf_recent_achievements", "0", FCVAR_ARCHIVE ); +ConVar tf_training_has_prompted_for_training( "tf_training_has_prompted_for_training", "0", FCVAR_ARCHIVE, "Whether the user has been prompted for training" ); +ConVar tf_training_has_prompted_for_offline_practice( "tf_training_has_prompted_for_offline_practice", "0", FCVAR_ARCHIVE, "Whether the user has been prompted to try offline practice." ); +ConVar tf_training_has_prompted_for_forums( "tf_training_has_prompted_for_forums", "0", FCVAR_ARCHIVE, "Whether the user has been prompted to view the new user forums." ); +ConVar tf_training_has_prompted_for_options( "tf_training_has_prompted_for_options", "0", FCVAR_ARCHIVE, "Whether the user has been prompted to view the TF2 advanced options." ); +ConVar tf_training_has_prompted_for_loadout( "tf_training_has_prompted_for_loadout", "0", FCVAR_ARCHIVE, "Whether the user has been prompted to equip something in their loadout." ); +ConVar cl_ask_bigpicture_controller_opt_out( "cl_ask_bigpicture_controller_opt_out", "0", FCVAR_ARCHIVE, "Whether the user has opted out of being prompted for controller support in Big Picture." ); +ConVar cl_mainmenu_operation_motd_start( "cl_mainmenu_operation_motd_start", "0", FCVAR_ARCHIVE | FCVAR_HIDDEN ); +ConVar cl_mainmenu_operation_motd_reset( "cl_mainmenu_operation_motd_reset", "0", FCVAR_ARCHIVE | FCVAR_HIDDEN ); +ConVar cl_mainmenu_safemode( "cl_mainmenu_safemode", "0", FCVAR_NONE, "Enable safe mode", cc_tf_safemode_toggle ); +ConVar cl_mainmenu_updateglow( "cl_mainmenu_updateglow", "1", FCVAR_ARCHIVE | FCVAR_HIDDEN ); + +void cc_promotional_codes_button_changed( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) ); + if ( pMMOverride ) + { + ( (CHudMainMenuOverride*)pMMOverride )->UpdatePromotionalCodes(); + } +} +ConVar cl_promotional_codes_button_show( "cl_promotional_codes_button_show", "1", FCVAR_ARCHIVE, "Toggles the 'View Promotional Codes' button in the main menu for players that have used the 'RIFT Well Spun Hat Claim Code'.", cc_promotional_codes_button_changed ); + +extern bool Training_IsComplete(); + +//----------------------------------------------------------------------------- +// Callback to launch the lobby UI +//----------------------------------------------------------------------------- +static void CL_OpenMatchmakingLobby( const CCommand &args ) +{ + if ( GTFGCClientSystem()->GetMatchmakingUIState() != eMatchmakingUIState_InGame ) + { + const char *arg1 = ""; + if ( args.ArgC() > 1 ) + { + arg1 = args[1]; + } + + // Make sure we are connected to steam, or they are going to be disappointed + if ( steamapicontext == NULL + || steamapicontext->SteamUtils() == NULL + || steamapicontext->SteamMatchmakingServers() == NULL + || steamapicontext->SteamUser() == NULL + || !steamapicontext->SteamUser()->BLoggedOn() + ) { + Warning( "Steam not properly initialized or connected.\n" ); + ShowMessageBox( "#TF_MM_GenericFailure_Title", "#TF_MM_GenericFailure", "#GameUI_OK" ); + return; + } + + // Make sure we have a GC connection + if ( !GCClientSystem()->BConnectedtoGC() ) + { + Warning( "Not connected to GC.\n" ); + ShowMessageBox( "#TF_MM_NoGC_Title", "#TF_MM_NoGC", "#GameUI_OK" ); + return; + } + + // If we're idle, use our argument to start matchmaking. + if ( GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_Inactive ) + { + TF_MatchmakingMode mode = TF_Matchmaking_LADDER; + if ( FStrEq( args[1], "mvm" ) ) + { + mode = TF_Matchmaking_MVM; + } + else if ( FStrEq( args[1], "ladder" ) ) + { + mode = TF_Matchmaking_LADDER; + } + else if ( FStrEq( args[1], "casual" ) ) + { + mode = TF_Matchmaking_CASUAL; + } + + GTFGCClientSystem()->BeginMatchmaking( mode ); + } + } + + CHudMainMenuOverride* pMMOverride = (CHudMainMenuOverride*)( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) ); + if ( pMMOverride ) + { + switch( GTFGCClientSystem()->GetSearchMode() ) + { + case TF_Matchmaking_MVM: + pMMOverride->OpenMvMMMPanel(); + break; + + case TF_Matchmaking_LADDER: + pMMOverride->OpenCompMMPanel(); + break; + + case TF_Matchmaking_CASUAL: + pMMOverride->OpenCasualMMPanel(); + + default: + return; + } + } +} + +static ConCommand openmatchmakinglobby_command( "OpenMatchmakingLobby", &CL_OpenMatchmakingLobby, "Activates the matchmaking lobby." ); + +static void CL_ReloadMMPanels( const CCommand &args ) +{ + CHudMainMenuOverride* pMMOverride = (CHudMainMenuOverride*)( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) ); + if ( pMMOverride ) + { + pMMOverride->ReloadMMPanels(); + } +} +ConCommand reload_mm_panels( "reload_mm_panels", &CL_ReloadMMPanels ); + +//----------------------------------------------------------------------------- +// Purpose: Prompt the user and ask if they really want to start training (if they are in a game) +//----------------------------------------------------------------------------- +class CTFConfirmTrainingDialog : public CConfirmDialog +{ + DECLARE_CLASS_SIMPLE( CTFConfirmTrainingDialog, CConfirmDialog ); +public: + CTFConfirmTrainingDialog( const char *pText, const char *pTitle, vgui::Panel *parent ) : BaseClass(parent), m_pText( pText ), m_pTitle( pTitle ) {} + + virtual const wchar_t *GetText() + { + return g_pVGuiLocalize->Find( m_pText ); + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + // Set the X to be bright, and the rest dull + if ( m_pConfirmButton ) + { + m_pConfirmButton->SetText( "#TF_Training_Prompt_ConfirmButton" ); + } + if ( m_pCancelButton ) + { + m_pCancelButton->SetText( "#TF_Training_Prompt_CancelButton" ); + } + + CExLabel *pTitle = dynamic_cast< CExLabel* >( FindChildByName( "TitleLabel" ) ); + if ( pTitle ) + { + pTitle->SetText( m_pTitle ); + } + } +protected: + const char *m_pText; + const char *m_pTitle; +}; + +class CCompetitiveAccessInfoPanel : public EditablePanel, public CLocalSteamSharedObjectListener +{ + DECLARE_CLASS_SIMPLE( CCompetitiveAccessInfoPanel, EditablePanel ); +public: + CCompetitiveAccessInfoPanel( Panel* pParent, const char* pszName ) + : EditablePanel( pParent, pszName ) + { + m_pPhoneButton = NULL; + m_pPremiumButton = NULL; + m_pPhoneCheckImage = NULL; + m_pPremiumCheckImage = NULL; + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE + { + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings( "resource/ui/CompetitiveAccessInfo.res" ); + + m_pPhoneButton = FindControl< CExImageButton >( "PhoneButton", true ); + m_pPremiumButton = FindControl< CExImageButton >( "PremiumButton", true ); + m_pPhoneCheckImage = FindControl< ImagePanel >( "PhoneCheckImage", true ); + m_pPremiumCheckImage = FindControl< ImagePanel >( "PremiumCheckImage", true ); + } + + virtual void PerformLayout() OVERRIDE + { + BaseClass::PerformLayout(); + + bool bIsFreeAccount = IsFreeTrialAccount(); + if ( m_pPremiumButton ) + { + m_pPremiumButton->SetEnabled( bIsFreeAccount ); + } + if ( m_pPremiumCheckImage ) + { + m_pPremiumCheckImage->SetVisible( !bIsFreeAccount ); + } + + bool bIsPhoneVerified = GTFGCClientSystem()->BIsPhoneVerified(); + bool bIsPhoneIdentifying = GTFGCClientSystem()->BIsPhoneIdentifying(); + bool bPhoneReady = bIsPhoneVerified && bIsPhoneIdentifying; + if ( m_pPhoneButton ) + { + m_pPhoneButton->SetEnabled( !bPhoneReady ); + } + if ( m_pPhoneCheckImage ) + { + m_pPhoneCheckImage->SetVisible( bPhoneReady ); + } + } + + virtual void OnCommand( const char *command ) OVERRIDE + { + if ( FStrEq( command, "close" ) ) + { + SetVisible( false ); + return; + } + else if ( FStrEq( command, "addphone" ) ) + { + if ( steamapicontext && steamapicontext->SteamFriends() ) + { + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "https://support.steampowered.com/kb_article.php?ref=8625-WRAH-9030#addphone" ); + } + return; + } + else if ( FStrEq( command, "addpremium" ) ) + { + if ( steamapicontext && steamapicontext->SteamFriends() ) + { + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "https://steamcommunity.com/sharedfiles/filedetails/?id=143430756" ); + } + return; + } + + BaseClass::OnCommand( command ); + } + + virtual void SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE + { + if ( pObject->GetTypeID() != CEconGameAccountClient::k_nTypeID ) + return; + + if ( GTFGCClientSystem()->BHasCompetitiveAccess() ) + { + SetVisible( false ); + } + else + { + InvalidateLayout(); + } + } + + virtual void SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE + { + if ( pObject->GetTypeID() != CEconGameAccountClient::k_nTypeID ) + return; + + if ( GTFGCClientSystem()->BHasCompetitiveAccess() ) + { + SetVisible( false ); + } + else + { + InvalidateLayout(); + } + } + +private: + CExImageButton *m_pPhoneButton; + CExImageButton *m_pPremiumButton; + ImagePanel *m_pPhoneCheckImage; + ImagePanel *m_pPremiumCheckImage; +}; +DECLARE_BUILD_FACTORY( CCompetitiveAccessInfoPanel ); + +class CMainMenuPlayListEntry : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CMainMenuPlayListEntry, EditablePanel ); +public: + + enum EDisabledStates_t + { + NOT_DISABLED = 0, + DISABLED_NO_COMP_ACCESS, + DISABLED_NO_GC, + DISABLED_MATCH_RUNNING, + + NUM_DISABLED_STATES + }; + + CMainMenuPlayListEntry( Panel* pParent, const char* pszName ) + : EditablePanel( pParent, pszName ) + { + m_pToolTip = NULL; + } + + ~CMainMenuPlayListEntry() + { + if (m_pToolTip != NULL) + { + delete m_pToolTip; + m_pToolTip = NULL; + } + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE + { + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings( "resource/ui/MainMenuPlayListEntry.res" ); + + CExImageButton *pLockImage = FindControl< CExImageButton >("LockImage"); + if (pLockImage) + { + EditablePanel *pToolTipPanel = FindControl< EditablePanel >("TooltipPanel"); + if (pToolTipPanel) + { + m_pToolTip = new CTFTextToolTip(this); + m_pToolTip->SetEmbeddedPanel(pToolTipPanel); + pToolTipPanel->MakePopup(false, true); + pToolTipPanel->SetKeyBoardInputEnabled(false); + pToolTipPanel->SetMouseInputEnabled(false); + m_pToolTip->SetText("#TF_Competitive_Requirements"); + m_pToolTip->SetTooltipDelay(0); + pLockImage->SetTooltip(m_pToolTip, "#TF_Competitive_Requirements"); + } + } + + SetDisabledReason( NOT_DISABLED ); + } + + virtual void ApplySettings( KeyValues *inResourceData ) OVERRIDE + { + BaseClass::ApplySettings( inResourceData ); + + m_strImageName = inResourceData->GetString( "image_name" ); + m_strButtonCommand = inResourceData->GetString( "button_command" ); + m_strButtonToken = inResourceData->GetString( "button_token" ); + m_strDescToken = inResourceData->GetString( "desc_token" ); + } + + void SetDisabledReason( EDisabledStates_t eReason ) + { + static const DisabledStateDesc_t s_DisabledStates[] = { { NULL, NULL, NULL } // NOT_DISABLED + , { "#TF_Competitive_Requirements", "comp_access_info", "locked_icon" } // DISABLED_NO_COMP_ACCESS + , { "#TF_MM_NoGC", NULL, "gc_dc" } // DISABLED_NO_GC + , { "#TF_Competitive_MatchRunning", NULL, NULL } }; // DISABLED_MATCH_RUNNING + + COMPILE_TIME_ASSERT( ARRAYSIZE( s_DisabledStates ) == NUM_DISABLED_STATES ); + + const DisabledStateDesc_t& stateDisabled = s_DisabledStates[ eReason ]; + + SetControlEnabled( "ModeButton", stateDisabled.m_pszLocToken == NULL ); + SetControlVisible( "LockImage", stateDisabled.m_pszLocToken != NULL ); + + CExImageButton *pLockImage = FindControl< CExImageButton >("LockImage"); + if ( pLockImage ) + { + if ( stateDisabled.m_pszImageName ) + { + pLockImage->SetSubImage( stateDisabled.m_pszImageName ); + } + + // Button behavior + pLockImage->SetEnabled( stateDisabled.m_pszButtonCommand != NULL ); + pLockImage->SetCommand( stateDisabled.m_pszButtonCommand ); + pLockImage->GetImage()->SetVisible( stateDisabled.m_pszImageName != NULL ); + + m_pToolTip->SetText( stateDisabled.m_pszLocToken ); + pLockImage->SetTooltip( m_pToolTip, stateDisabled.m_pszLocToken ); + m_pToolTip->PerformLayout(); + } + } + + virtual void PerformLayout() OVERRIDE + { + BaseClass::PerformLayout(); + + ImagePanel* pModeImage = FindControl< ImagePanel >( "ModeImage" ); + if ( pModeImage ) + { + pModeImage->SetImage( m_strImageName ); + } + + Button* pButton = FindControl< Button >( "ModeButton" ); + if ( pButton ) + { + pButton->SetCommand( m_strButtonCommand ); + } + + Label* pLabel = FindControl< Label >( "ModeButton" ); + if ( pLabel ) + { + pLabel->SetText( m_strButtonToken ); + } + pLabel = FindControl< Label >( "DescLabel" ); + if ( pLabel ) + { + pLabel->SetText( m_strDescToken ); + } + pLabel = FindControl< Label >( "DescLabelShadow" ); + if ( pLabel ) + { + pLabel->SetText( m_strDescToken ); + } + } + + + +private: + + struct DisabledStateDesc_t + { + const char* m_pszLocToken; + const char* m_pszButtonCommand; + const char* m_pszImageName; + }; + + CUtlString m_strImageName; + CUtlString m_strButtonCommand; + CUtlString m_strButtonToken; + CUtlString m_strDescToken; + + CTFTextToolTip *m_pToolTip; +}; + +DECLARE_BUILD_FACTORY( CMainMenuPlayListEntry ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudMainMenuOverride::CHudMainMenuOverride( IViewPort *pViewPort ) : BaseClass( NULL, PANEL_MAINMENUOVERRIDE ) +{ + // We don't want the gameui to delete us, or things get messy + SetAutoDelete( false ); + SetVisible( true ); + + m_bPlayListExpanded = false; + m_pVRModeButton = NULL; + m_pVRModeBackground = NULL; + + m_pButtonKV = NULL; + m_pQuitButton = NULL; + m_pDisconnectButton = NULL; + m_pBackToReplaysButton = NULL; + m_pStoreHasNewItemsImage = NULL; + + m_nLastMOTDRequestAt = 0; + m_nLastMOTDRequestLanguage = k_Lang_English; + m_bReloadedAllMOTDs = false; + m_iCurrentMOTD = -1; + m_bInitMOTD = false; + + m_pMOTDPanel = NULL; + m_pMOTDShowPanel = NULL; + m_pMOTDURLButton = NULL; + m_pMOTDNextButton = NULL; + m_pMOTDPrevButton = NULL; + m_iNotiPanelWide = 0; + + m_pFeaturedItemPanel = NULL;//new CItemModelPanel( m_pStoreSpecialPanel, "FeaturedItemModelPanel" ); + m_bReapplyButtonKVs = false; + + m_pMouseOverItemPanel = vgui::SETUP_PANEL( new CItemModelPanel( this, "mouseoveritempanel" ) ); + m_pMouseOverTooltip = new CItemModelPanelToolTip( this ); + m_pMouseOverTooltip->SetupPanels( this, m_pMouseOverItemPanel ); + + m_pMOTDHeaderLabel = NULL; + m_pMOTDHeaderIcon = NULL; + m_pMOTDTitleLabel = NULL; + m_pMOTDTitleImageContainer = NULL; + m_pMOTDTitleImage = NULL; + m_hTitleLabelFont = vgui::INVALID_FONT; + + m_pQuestLogButton = new EditablePanel( this, "QuestLogButton" ); + +#ifdef STAGING_ONLY + m_bGeneratingIcons = false; + m_pIconData = NULL; +#endif + + m_bHaveNewMOTDs = false; + m_bMOTDShownAtStartup = false; + + m_pCharacterImagePanel = NULL; + m_iCharacterImageIdx = -1; + +#ifdef SAXXYMAINMENU_ENABLED + m_pSaxxyAwardsPanel = NULL; + m_pSaxxySettings = NULL; +#endif + + m_pWarLandingPage = new CWarLandingPanel( this, "WarPanel" ); + + m_flCheckTrainingAt = 0; + m_bWasInTraining = false; + m_flLastWarNagTime = 0.f; + + ScheduleItemCheck(); + + m_pToolTip = new CMainMenuToolTip( this ); + m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" ); + m_pToolTipEmbeddedPanel->MakePopup( false, true ); + m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false ); + m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false ); + m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel ); + m_pToolTip->SetTooltipDelay( 0 ); + + ListenForGameEvent( "gc_connected" ); + ListenForGameEvent( "item_schema_initialized" ); + ListenForGameEvent( "store_pricesheet_updated" ); + ListenForGameEvent( "inventory_updated" ); + ListenForGameEvent( "gameui_activated" ); + ListenForGameEvent( "party_updated" ); + + // Create our MOTD scrollable section + m_pMOTDPanel = new vgui::EditablePanel( this, "MOTD_Panel" ); + m_pMOTDPanel->SetVisible( true ); + m_pMOTDTextPanel = new vgui::EditablePanel( this, "MOTD_TextPanel" ); + m_pMOTDTextScroller = new vgui::ScrollableEditablePanel( m_pMOTDPanel, m_pMOTDTextPanel, "MOTD_TextScroller" ); + + m_pMOTDTextScroller->GetScrollbar()->SetAutohideButtons( true ); + m_pMOTDTextScroller->GetScrollbar()->SetPaintBorderEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->SetPaintBackgroundEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->GetButton(0)->SetPaintBorderEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->GetButton(0)->SetPaintBackgroundEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->GetButton(1)->SetPaintBorderEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->GetButton(1)->SetPaintBackgroundEnabled( false ); + m_pMOTDTextScroller->GetScrollbar()->SetAutoResize( PIN_TOPRIGHT, AUTORESIZE_DOWN, -24, 0, -16, 0 ); + + m_pMOTDTextLabel = NULL; + + m_pNotificationsShowPanel = NULL; + m_pNotificationsPanel = new vgui::EditablePanel( this, "Notifications_Panel" ); + m_pNotificationsControl = NotificationQueue_CreateMainMenuUIElement( m_pNotificationsPanel, "Notifications_Control" ); + m_pNotificationsScroller = new vgui::ScrollableEditablePanel( m_pNotificationsPanel, m_pNotificationsControl, "Notifications_Scroller" ); + + m_iNumNotifications = 0; + + m_pFeaturedItemMouseOverPanel = new CItemModelPanel( this, "FeaturedItemMouseOverItemPanel" ); + m_pFeaturedItemToolTip = new CSimplePanelToolTip( this ); + m_pFeaturedItemToolTip->SetControlledPanel( m_pFeaturedItemMouseOverPanel ); + + m_pBackground = new vgui::ImagePanel( this, "Background" ); + m_pEventPromoContainer = new EditablePanel( this, "EventPromo" ); + m_pSafeModeContainer = new EditablePanel( this, "SafeMode" ); + + // Cause the quest UI to be created + GetQuestLog(); + + m_bStabilizedInitialLayout = false; + + m_bBackgroundUsesCharacterImages = true; + + m_pWatchStreamsPanel = new CTFStreamListPanel( this, "StreamListPanel" ); + + vgui::ivgui()->AddTickSignal( GetVPanel(), 50 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudMainMenuOverride::~CHudMainMenuOverride( void ) +{ + C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_close", "main_menu_override" ); + + if ( GetClientModeTFNormal()->GameUI() ) + { + GetClientModeTFNormal()->GameUI()->SetMainMenuOverride( NULL ); + } + + if ( m_pButtonKV ) + { + m_pButtonKV->deleteThis(); + m_pButtonKV = NULL; + } + + // Stop Animation Sequences + if ( m_pNotificationsShowPanel ) + { + g_pClientMode->GetViewportAnimationController()->CancelAnimationsForPanel( m_pNotificationsShowPanel ); + } + + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Override painting traversal to suppress main menu painting if we're not ready to show yet +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::PaintTraverse( bool Repaint, bool allowForce ) +{ + // Ugly hack: disable painting until we're done screwing around with updating the layout during initialization. + // Use -menupaintduringinit command line parameter to reinstate old behavior + if ( m_bStabilizedInitialLayout || CommandLine()->CheckParm("-menupaintduringinit") ) + { + BaseClass::PaintTraverse( Repaint, allowForce ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::OnTick() +{ + if ( m_iNumNotifications != NotificationQueue_GetNumNotifications() ) + { + m_iNumNotifications = NotificationQueue_GetNumNotifications(); + UpdateNotifications(); + CheckForNewQuests(); + } + else if ( m_pNotificationsPanel->IsVisible() ) + { + AdjustNotificationsPanelHeight(); + } + + static bool s_bRanOnce = false; + if ( !s_bRanOnce ) + { + s_bRanOnce = true; + if ( char const *szConnectAdr = CommandLine()->ParmValue( "+connect" ) ) + { + Msg( "Executing deferred connect command: %s\n", szConnectAdr ); + engine->ExecuteClientCmd( CFmtStr( "connect %s -%s\n", szConnectAdr, "ConnectStringOnCommandline" ) ); + } + } + + // See if its time to nag about joining the war + float flTimeSinceWarNag = Plat_FloatTime() - m_flLastWarNagTime; + if ( !m_bPlayListExpanded && m_pHighlightAnims[ MMHA_WAR ] && ( flTimeSinceWarNag > 300.f || m_flLastWarNagTime == 0.f ) ) + { + // Make sure our SOCache is ready + GCSDK::CGCClientSharedObjectCache *pSOCache = NULL; + if ( steamapicontext && steamapicontext->SteamUser() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + pSOCache = GCClientSystem()->GetSOCache( steamID ); + } + + // Need to be initialized. If we're not, we'll get false positives + // when we actually go to look for our war data + if ( pSOCache && pSOCache->BIsInitialized() ) + { + m_flLastWarNagTime = Plat_FloatTime(); + + // Get war data + const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( PYRO_VS_HEAVY_WAR_DEF_INDEX ); + CWarData *pWarData = GetLocalPlayerWarData( pWarDef->GetDefIndex() ); + war_side_t nAffiliation = INVALID_WAR_SIDE; + if ( pWarData ) + { + // Get affiliation if they have one. + nAffiliation = pWarData->Obj().affiliation(); + } + + // They haven't joined the war! Nag 'em + if ( nAffiliation == INVALID_WAR_SIDE && pWarDef->IsActive() ) + { + StartHighlightAnimation( MMHA_WAR ); + } + } + } + + +#ifdef STAGING_ONLY + if ( m_bGeneratingIcons ) + { + GenerateIconsThink(); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::AttachToGameUI( void ) +{ + C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_open", "main_menu_override" ); + + if ( GetClientModeTFNormal()->GameUI() ) + { + GetClientModeTFNormal()->GameUI()->SetMainMenuOverride( GetVPanel() ); + } + + SetKeyBoardInputEnabled( true ); + SetMouseInputEnabled( true ); + SetCursor(dc_arrow); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ConVar tf_last_store_pricesheet_version( "tf_last_store_pricesheet_version", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE | FCVAR_DONTRECORD | FCVAR_HIDDEN ); + +void CHudMainMenuOverride::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( FStrEq( type, "gameui_activated" ) ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "MMenu_PlayList_Collapse_Immediate", false ); + m_bPlayListExpanded = false; + return; + } + if ( Q_strcmp( type, "gc_connected" ) == 0 ) + { + char uilanguage[ 64 ]; + uilanguage[0] = 0; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + + //V_strcpy_safe( uilanguage, "german" ); + + ELanguage nCurLang = PchLanguageToELanguage( uilanguage ); + + // If we've changed language from when we last requested, we ask for all MOTDs again. + if ( nCurLang != m_nLastMOTDRequestLanguage ) + { + m_nLastMOTDRequestAt = 0; + m_nLastMOTDRequestLanguage = nCurLang; + m_bReloadedAllMOTDs = true; + } + + // Ask the GC for the MOTD + GCSDK::CGCMsg<MsgGCMOTDRequest_t> msg( k_EMsgGCMOTDRequest ); + msg.Body().m_eLanguage = nCurLang; + msg.Body().m_nLastMOTDRequest = m_nLastMOTDRequestAt; + GCClientSystem()->BSendMessage( msg ); + + // Roll our last asked time forward here. It won't get written to our + // cache file if we don't get a response from the GC. + CRTime cTimeHack; + m_nLastMOTDRequestAt = CRTime::RTime32TimeCur(); + + // Load the store info, so we can display the current special + CStorePanel::RequestPricesheet(); + CheckForNewQuests(); + UpdatePlaylistEntries(); + } + else if ( Q_strcmp( type, "item_schema_initialized" ) == 0 ) + { + // Tell the schema to load its MOTD block from our clientside cache file + CUtlVector< CUtlString > vecErrors; + KeyValues *pEntriesKV = new KeyValues( "motd_entries"); + if ( pEntriesKV->LoadFromFile( g_pFullFileSystem, GC_MOTD_CACHE_FILE ) ) + { + // Extract our last MOTD request time + const char *pszTime = pEntriesKV->GetString( "last_request_time", NULL ); + m_nLastMOTDRequestAt = ( pszTime && pszTime[0] ) ? CRTime::RTime32FromString(pszTime) : 0; + + const char *pszLang = pEntriesKV->GetString( "last_request_language", NULL ); + m_nLastMOTDRequestLanguage = ( pszLang && pszLang[0] ) ? PchLanguageToELanguage(pszLang) : k_Lang_English; + + // Parse the entries + GetMOTDManager().BInitMOTDEntries( pEntriesKV, &vecErrors ); + GetMOTDManager().PurgeUnusedMOTDEntries( pEntriesKV ); + } + } + else if ( Q_strcmp( type, "store_pricesheet_updated" ) == 0 ) + { + // If the contents of the store have changed since the last time we went in and/or launched + // the game, change the button color so that players know there's new content available. + if ( EconUI() && + EconUI()->GetStorePanel() && + EconUI()->GetStorePanel()->GetPriceSheet() ) + { + const CEconStorePriceSheet *pPriceSheet = EconUI()->GetStorePanel()->GetPriceSheet(); + + // The cvar system can't deal with integers that lose data when represented as floating point + // numbers. We don't really care about supreme accuracy for detecting changes -- worst case if + // we change the price sheet almost exactly 18 hours apart, some subset of players won't get the + // "new!" label and that's fine. + const uint32 unPriceSheetVersion = (uint32)pPriceSheet->GetVersionStamp() & 0xffff; + + if ( unPriceSheetVersion != (uint32)tf_last_store_pricesheet_version.GetInt() ) + { + tf_last_store_pricesheet_version.SetValue( (int)unPriceSheetVersion ); + + if ( m_pStoreHasNewItemsImage ) + { + m_pStoreHasNewItemsImage->SetVisible( true ); + } + } + } + + // might as well do this here too + UpdatePromotionalCodes(); + + LoadCharacterImageFile(); + + if ( NeedsToChooseMostHelpfulFriend() ) + { + NotifyNeedsToChooseMostHelpfulFriend(); + } + } + else if ( FStrEq( "inventory_updated", type ) ) + { + CheckForNewQuests(); + } + else if ( FStrEq( "party_updated", type ) ) + { + UpdatePlaylistEntries(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + KeyValues *pItemKV = inResourceData->FindKey( "button_kv" ); + if ( pItemKV ) + { + if ( m_pButtonKV ) + { + m_pButtonKV->deleteThis(); + } + m_pButtonKV = new KeyValues("button_kv"); + pItemKV->CopySubkeys( m_pButtonKV ); + + m_bReapplyButtonKVs = true; + } + +#ifdef SAXXYMAINMENU_ENABLED + KeyValues *pSaxxySettings = inResourceData->FindKey( "SaxxySettings" ); + if ( pSaxxySettings ) + { + if ( m_pSaxxySettings ) + { + m_pSaxxySettings->deleteThis(); + } + m_pSaxxySettings = pSaxxySettings->MakeCopy(); + + if ( m_pSaxxyAwardsPanel ) + { + m_pSaxxyAwardsPanel->ApplySettings( m_pSaxxySettings ); + } + } +#endif + + m_bPlayListExpanded = false; + + UpdatePlaylistEntries(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::ApplySchemeSettings( IScheme *scheme ) +{ + // We need to re-hook ourselves up to the TF client scheme, because the GameUI will try to change us their its scheme + vgui::HScheme pScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme"); + SetScheme(pScheme); + SetProportional( true ); + + m_pFeaturedItemMouseOverPanel->InvalidateLayout( true, true ); + m_bBackgroundUsesCharacterImages = true; + + bool bHolidayActive = false; + KeyValues *pConditions = NULL; + const char *pszHoliday = UTIL_GetActiveHolidayString(); + if ( pszHoliday && pszHoliday[0] ) + { + pConditions = new KeyValues( "conditions" ); + + char szCondition[64]; + Q_snprintf( szCondition, sizeof( szCondition ), "if_%s", pszHoliday ); + AddSubKeyNamed( pConditions, szCondition ); + + if ( FStrEq( pszHoliday, "halloween" ) ) + { + // for Halloween we also want to pick a random background + int nBackground = RandomInt( 0, 4 ); + AddSubKeyNamed( pConditions, CFmtStr( "if_halloween_%d", nBackground ) ); + if ( ( nBackground == 3 ) || ( nBackground == 4 ) ) + { + m_bBackgroundUsesCharacterImages = false; + } + } + + bHolidayActive = true; + } + + if ( !bHolidayActive ) + { + FOR_EACH_MAP_FAST( GetItemSchema()->GetOperationDefinitions(), iOperation ) + { + CEconOperationDefinition *pOperation = GetItemSchema()->GetOperationDefinitions()[iOperation]; + if ( !pOperation || !pOperation->IsActive() || !pOperation->IsCampaign() ) + continue; + + if ( !pConditions ) + pConditions = new KeyValues( "conditions" ); + + AddSubKeyNamed( pConditions, "if_operation" ); + break; + } + } + + if ( !pConditions ) + { + pConditions = new KeyValues( "conditions" ); + } + + // Put in ratio condition + float aspectRatio = engine->GetScreenAspectRatio(); + AddSubKeyNamed( pConditions, aspectRatio >= 1.6 ? "if_wider" : "if_taller" ); + + RemoveAllMenuEntries(); + + LoadControlSettings( "resource/UI/MainMenuOverride.res", NULL, NULL, pConditions ); + + BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme(pScheme) ); + + if ( pConditions ) + { + pConditions->deleteThis(); + } + + m_pQuitButton = dynamic_cast<CExButton*>( FindChildByName("QuitButton") ); + m_pDisconnectButton = dynamic_cast<CExButton*>( FindChildByName("DisconnectButton") ); + m_pBackToReplaysButton = dynamic_cast<CExButton*>( FindChildByName("BackToReplaysButton") ); + m_pStoreHasNewItemsImage = dynamic_cast<ImagePanel*>( FindChildByName( "StoreHasNewItemsImage", true ) ); + + { + Panel *pButton = FindChildByName( "VRModeButton" ); + if( pButton ) + { + m_pVRModeButton = dynamic_cast< CExButton *>( pButton->GetChild( 0 ) ); + } + } + m_pVRModeBackground = FindChildByName( "VRBGPanel" ); + + bool bShowVR = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter == materials->GetCurrentAdapter(); + if ( m_pVRModeBackground ) + { + m_pVRModeBackground->SetVisible( bShowVR ); + } + + m_bIsDisconnectText = true; + + // Tell all the MOTD buttons that we want their messages + m_pMOTDPrevButton = dynamic_cast<CExImageButton*>( m_pMOTDPanel->FindChildByName("MOTD_PrevButton") ); + m_pMOTDNextButton = dynamic_cast<CExImageButton*>( m_pMOTDPanel->FindChildByName("MOTD_NextButton") ); + m_pMOTDURLButton = dynamic_cast<CExButton*>( m_pMOTDPanel->FindChildByName("MOTD_URLButton") ); + + // m_pNotificationsShowPanel shows number of unread notifications. Pressing it pops up the first notification. + m_pNotificationsShowPanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName("Notifications_ShowButtonPanel") ); + + m_iNotiPanelWide = m_pNotificationsPanel->GetWide(); + + // m_pMOTDShowPanel shows that the player has an unread MOTD. Pressing it pops up the MOTD. + m_pMOTDShowPanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName("MOTD_ShowButtonPanel") ); + + vgui::EditablePanel* pHeaderContainer = dynamic_cast<vgui::EditablePanel*>( m_pMOTDPanel->FindChildByName( "MOTD_HeaderContainer" ) ); + if ( pHeaderContainer ) + { + m_pMOTDHeaderLabel = dynamic_cast<vgui::Label*>( pHeaderContainer->FindChildByName( "MOTD_HeaderLabel" ) ); + } + + m_pMOTDHeaderIcon = dynamic_cast<vgui::ImagePanel*>( m_pMOTDPanel->FindChildByName("MOTD_HeaderIcon") ); + + m_pMOTDTitleLabel = dynamic_cast<vgui::Label*>( m_pMOTDPanel->FindChildByName("MOTD_TitleLabel") ); + if ( m_pMOTDTitleLabel ) + { + m_hTitleLabelFont = m_pMOTDTitleLabel->GetFont(); + } + + m_pMOTDTextLabel = dynamic_cast<vgui::Label*>( m_pMOTDTextPanel->FindChildByName( "MOTD_TextLabel" ) ); + + m_pMOTDTitleImageContainer = dynamic_cast<vgui::EditablePanel*>( m_pMOTDPanel->FindChildByName("MOTD_TitleImageContainer") ); + if ( m_pMOTDTitleImageContainer ) + { + m_pMOTDTitleImage = dynamic_cast<vgui::ImagePanel*>( m_pMOTDTitleImageContainer->FindChildByName("MOTD_TitleImage") ); + } + + m_pNotificationsScroller->GetScrollbar()->SetAutohideButtons( true ); + m_pNotificationsScroller->GetScrollbar()->SetPaintBorderEnabled( false ); + m_pNotificationsScroller->GetScrollbar()->SetPaintBackgroundEnabled( false ); + m_pNotificationsScroller->GetScrollbar()->GetButton(0)->SetPaintBorderEnabled( false ); + m_pNotificationsScroller->GetScrollbar()->GetButton(0)->SetPaintBackgroundEnabled( false ); + m_pNotificationsScroller->GetScrollbar()->GetButton(1)->SetPaintBorderEnabled( false ); + m_pNotificationsScroller->GetScrollbar()->GetButton(1)->SetPaintBackgroundEnabled( false ); + + // Add tooltips for various buttons + CExImageButton *pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("CommentaryButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_Commentary" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("CoachPlayersButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_Coach" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("ReportBugButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_ReportBug" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("AchievementsButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_Achievements" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("NewUserForumsButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_NewUserForum" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("ReplayButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_Replay" ); + } + pImageButton = dynamic_cast<CExImageButton *>( FindChildByName("WorkshopButton") ); + if ( pImageButton ) + { + pImageButton->SetTooltip( m_pToolTip, "#MMenu_Tooltip_Workshop" ); + } + + // Highlights + m_pHighlightAnims[ MMHA_TUTORIAL ] = FindControl< CExplanationPopup >( "TutorialHighlight" ); + m_pHighlightAnims[ MMHA_PRACTICE ] = FindControl< CExplanationPopup >( "PracticeHighlight" ); + m_pHighlightAnims[ MMHA_NEWUSERFORUM ] = FindControl< CExplanationPopup >( "NewUserForumHighlight" ); + m_pHighlightAnims[ MMHA_OPTIONS ] = FindControl< CExplanationPopup >( "OptionsHighlightPanel" ); + m_pHighlightAnims[ MMHA_LOADOUT ] = FindControl< CExplanationPopup >( "LoadoutHighlightPanel" ); + m_pHighlightAnims[ MMHA_STORE ] = FindControl< CExplanationPopup >( "StoreHighlightPanel" ); + m_pHighlightAnims[ MMHA_WAR ] = FindControl< CExplanationPopup >( "WarHighlightPanel" ); + + m_pCompetitiveAccessInfo = dynamic_cast<vgui::EditablePanel*>( FindChildByName("CompetitiveAccessInfoPanel") ); + + LoadCharacterImageFile(); + + RemoveAllMenuEntries(); + LoadMenuEntries(); + + UpdateNotifications(); + UpdatePromotionalCodes(); + + ScheduleTrainingCheck( false ); + + PerformKeyRebindings(); + CheckForNewQuests(); + + // Asking for these will create them if they dont already exist. + GetCasualLobbyPanel()->InvalidateLayout( false, true ); + GetCompLobbyPanel()->InvalidateLayout( false, true ); + GetMvMLobbyPanel()->InvalidateLayout( false, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::LoadCharacterImageFile( void ) +{ + if ( !m_bBackgroundUsesCharacterImages ) + return; + + m_pCharacterImagePanel = dynamic_cast<vgui::ImagePanel*>( FindChildByName( "TFCharacterImage" ) ); + if ( m_pCharacterImagePanel ) + { + KeyValues *pCharacterFile = new KeyValues( "CharacterBackgrounds" ); + + if ( pCharacterFile->LoadFromFile( g_pFullFileSystem, "scripts/CharacterBackgrounds.txt" ) ) + { + CUtlVector<KeyValues *> vecUseableCharacters; + + const char* pszActiveWarName = NULL; + const WarDefinitionMap_t& mapWars = GetItemSchema()->GetWarDefinitions(); + FOR_EACH_MAP_FAST( mapWars, i ) + { + const CWarDefinition* pWarDef = mapWars[i]; + if ( pWarDef->IsActive() ) + { + pszActiveWarName = pWarDef->GetDefName(); + break; + } + } + + // Count the number of possible characters. + FOR_EACH_SUBKEY( pCharacterFile, pCharacter ) + { + EHoliday eHoliday = (EHoliday)UTIL_GetHolidayForString( pCharacter->GetString( "holiday_restriction" ) ); + const char* pszAssociatedWar = pCharacter->GetString( "war_restriction" ); + + int iWeight = 1; + + // If a War is active, that's all we want to show. If not, then bias towards holidays + if ( pszActiveWarName != NULL ) + { + if ( !FStrEq( pszAssociatedWar, pszActiveWarName ) ) + { + iWeight = 0; + } + } + else if ( eHoliday != kHoliday_None ) + { + iWeight = UTIL_IsHolidayActive( eHoliday ) ? 6 : 0; + } + + for ( int i = 0; i < iWeight; i++ ) + { + vecUseableCharacters.AddToTail( pCharacter ); + } + } + + // Pick a character at random. + if ( m_iCharacterImageIdx < 0 && vecUseableCharacters.Count() > 0 ) + { + m_iCharacterImageIdx = rand() % vecUseableCharacters.Count(); + } + + // Make sure we found a character we can use. + if ( vecUseableCharacters.IsValidIndex( m_iCharacterImageIdx ) ) + { + KeyValues *pCharacter = vecUseableCharacters[m_iCharacterImageIdx]; + + if ( IsFreeTrialAccount( ) && m_pHighlightAnims[ MMHA_STORE ] && !m_bPlayListExpanded ) + { + const char* text = pCharacter->GetString( "store_text" ); + if ( text ) + { + m_pHighlightAnims[ MMHA_STORE ]->SetDialogVariable( "highlighttext", g_pVGuiLocalize->Find( text ) ); + StartHighlightAnimation( MMHA_STORE ); + } + } + + const char* image_name = pCharacter->GetString( "image" ); + m_pCharacterImagePanel->SetImage( image_name ); + } + } + + pCharacterFile->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::LoadMenuEntries( void ) +{ + KeyValues *datafile = new KeyValues("GameMenu"); + datafile->UsesEscapeSequences( true ); // VGUI uses escape sequences + bool bLoaded = datafile->LoadFromFile( g_pFullFileSystem, "Resource/GameMenu.res", "custom_mod" ); + if ( !bLoaded ) + { + bLoaded = datafile->LoadFromFile( g_pFullFileSystem, "Resource/GameMenu.res", "vgui" ); + if ( !bLoaded ) + { + // only allow to load loose files when using insecure mode + if ( CommandLine()->FindParm( "-insecure" ) ) + { + bLoaded = datafile->LoadFromFile( g_pFullFileSystem, "Resource/GameMenu.res" ); + } + } + } + + 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->GetName(); + int iStyle = dat->GetInt("style", 0 ); + + if ( !cmd || !cmd[0] ) + { + int iIdx = m_pMMButtonEntries.AddToTail(); + m_pMMButtonEntries[iIdx].pPanel = NULL; + m_pMMButtonEntries[iIdx].bOnlyInGame = dat->GetBool( "OnlyInGame" ); + m_pMMButtonEntries[iIdx].bOnlyInReplay = dat->GetBool( "OnlyInReplay" ); + m_pMMButtonEntries[iIdx].bOnlyAtMenu = dat->GetBool( "OnlyAtMenu" ); + m_pMMButtonEntries[iIdx].bOnlyVREnabled = dat->GetBool( "OnlyWhenVREnabled" ); + m_pMMButtonEntries[iIdx].iStyle = iStyle; + continue; + } + + // Create the new editable panel (first, see if we have one already) + vgui::EditablePanel *pPanel = dynamic_cast<vgui::EditablePanel *>( FindChildByName( name, true ) ); + if ( !pPanel ) + { + Assert( false ); // We don't want to do this anymore. We need an actual hierarchy so things can slide + // around when the play buttin is pressed and the play options expand + pPanel = new vgui::EditablePanel( this, name ); + } + else + { + // It already exists in our .res file. Note that it's a custom button. + iStyle = MMBS_CUSTOM; + } + + if ( pPanel ) + { + if ( m_pButtonKV && iStyle != MMBS_CUSTOM ) + { + pPanel->ApplySettings( m_pButtonKV ); + } + + int iIdx = m_pMMButtonEntries.AddToTail(); + m_pMMButtonEntries[iIdx].pPanel = pPanel; + m_pMMButtonEntries[iIdx].bOnlyInGame = dat->GetBool( "OnlyInGame" ); + m_pMMButtonEntries[iIdx].bOnlyInReplay = dat->GetBool( "OnlyInReplay" ); + m_pMMButtonEntries[iIdx].bOnlyAtMenu = dat->GetBool( "OnlyAtMenu" ); + m_pMMButtonEntries[iIdx].bOnlyVREnabled = dat->GetBool( "OnlyWhenVREnabled" ); + m_pMMButtonEntries[iIdx].iStyle = iStyle; + m_pMMButtonEntries[iIdx].pszImage = dat->GetString( "subimage" ); + m_pMMButtonEntries[iIdx].pszTooltip = dat->GetString( "tooltip", NULL ); + + // Tell the button that we'd like messages from it + CExImageButton *pButton = dynamic_cast<CExImageButton*>( pPanel->FindChildByName("SubButton") ); + if ( pButton ) + { + if ( m_pMMButtonEntries[iIdx].pszTooltip ) + { + pButton->SetTooltip( m_pToolTip, m_pMMButtonEntries[iIdx].pszTooltip ); + } + + pButton->SetText( label ); + pButton->SetCommand( cmd ); + pButton->SetMouseInputEnabled( true ); + pButton->AddActionSignalTarget( GetVPanel() ); + + if ( m_pMMButtonEntries[iIdx].pszImage && m_pMMButtonEntries[iIdx].pszImage[0] ) + { + pButton->SetSubImage( m_pMMButtonEntries[iIdx].pszImage ); + } + } + } + + OnUpdateMenu(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::RemoveAllMenuEntries( void ) +{ + FOR_EACH_VEC_BACK( m_pMMButtonEntries, i ) + { + if ( m_pMMButtonEntries[i].pPanel ) + { + // Manually remove anything that's not going to be removed automatically + if ( m_pMMButtonEntries[i].pPanel->IsBuildModeDeletable() == false ) + { + m_pMMButtonEntries[i].pPanel->MarkForDeletion(); + } + } + } + m_pMMButtonEntries.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + bool bFirstButton = true; + + int iYPos = m_iButtonY; + FOR_EACH_VEC( m_pMMButtonEntries, i ) + { + bool bIsVisible = (m_pMMButtonEntries[i].pPanel ? m_pMMButtonEntries[i].pPanel->IsVisible() : m_pMMButtonEntries[i].bIsVisible); + if ( !bIsVisible ) + continue; + + if ( bFirstButton && m_pMMButtonEntries[i].pPanel != NULL ) + { + m_pMMButtonEntries[i].pPanel->NavigateTo(); + bFirstButton = false; + } + + // Don't reposition it if it's a custom button + if ( m_pMMButtonEntries[i].iStyle == MMBS_CUSTOM ) + continue; + + // If we're a spacer, just leave a blank and move on + if ( m_pMMButtonEntries[i].pPanel == NULL ) + { + iYPos += YRES(20); + continue; + } + + m_pMMButtonEntries[i].pPanel->SetPos( (GetWide() * 0.5) + m_iButtonXOffset, iYPos ); + iYPos += m_pMMButtonEntries[i].pPanel->GetTall() + m_iButtonYDelta; + } + + if ( m_pFeaturedItemMouseOverPanel->IsVisible() ) + { + m_pFeaturedItemMouseOverPanel->SetVisible( false ); + } + + if ( m_pEventPromoContainer && m_pSafeModeContainer ) + { + m_pEventPromoContainer->SetVisible( !cl_mainmenu_safemode.GetBool() ); + m_pSafeModeContainer->SetVisible( cl_mainmenu_safemode.GetBool() ); + if ( cl_mainmenu_safemode.GetBool() ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pSafeModeContainer, "MMenu_SafeMode_Blink" ); + } + else + { + g_pClientMode->GetViewportAnimationController()->CancelAnimationsForPanel( m_pSafeModeContainer ); + } + } + + // Make the glows behind the update buttons pulse + if ( m_pEventPromoContainer && cl_mainmenu_updateglow.GetInt() ) + { + EditablePanel* pUpdateBackground = m_pEventPromoContainer->FindControl< EditablePanel >( "Background", true ); + if ( pUpdateBackground ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pUpdateBackground, "MMenu_UpdateButton_StartGlow" ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::OnUpdateMenu( void ) +{ + // The dumb gameui.dll basepanel calls this every damn frame it's visible. + // So try and do the least amount of work if nothing has changed. + + bool bSomethingChanged = false; + bool bInGame = engine->IsInGame(); +#if defined( REPLAY_ENABLED ) + bool bInReplay = g_pEngineClientReplay->IsPlayingReplayDemo(); +#else + bool bInReplay = false; +#endif + bool bIsVREnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter == materials->GetCurrentAdapter(); + + // First, reapply any KVs we have to reapply + if ( m_bReapplyButtonKVs ) + { + m_bReapplyButtonKVs = false; + + if ( m_pButtonKV ) + { + FOR_EACH_VEC( m_pMMButtonEntries, i ) + { + if ( m_pMMButtonEntries[i].iStyle != MMBS_CUSTOM && m_pMMButtonEntries[i].pPanel ) + { + m_pMMButtonEntries[i].pPanel->ApplySettings( m_pButtonKV ); + } + } + } + } + + // Hide the character if we're in game. + if ( bInGame || bInReplay ) + { + if ( m_pCharacterImagePanel && m_pCharacterImagePanel->IsVisible() ) + { + m_pCharacterImagePanel->SetVisible( false ); + } + } + else if ( !bInGame && !bInReplay ) + { + if ( m_pCharacterImagePanel && !m_pCharacterImagePanel->IsVisible() ) + { + m_pCharacterImagePanel->SetVisible( true ); + } + } + + // Position the entries + FOR_EACH_VEC( m_pMMButtonEntries, i ) + { + bool shouldBeVisible = true; + if ( m_pMMButtonEntries[i].bOnlyInGame && !bInGame ) + { + shouldBeVisible = false; + } + else if ( m_pMMButtonEntries[i].bOnlyInReplay && !bInReplay ) + { + shouldBeVisible = false; + } + else if ( m_pMMButtonEntries[i].bOnlyAtMenu && (bInGame || bInReplay) ) + { + shouldBeVisible = false; + } + else if ( m_pMMButtonEntries[i].bOnlyVREnabled && ( !bIsVREnabled || ShouldForceVRActive() ) ) + { + shouldBeVisible = false; + } + + // Set the right visibility + bool bIsVisible = (m_pMMButtonEntries[i].pPanel ? m_pMMButtonEntries[i].pPanel->IsVisible() : m_pMMButtonEntries[i].bIsVisible); + if ( bIsVisible != shouldBeVisible ) + { + m_pMMButtonEntries[i].bIsVisible = shouldBeVisible; + if ( m_pMMButtonEntries[i].pPanel ) + { + m_pMMButtonEntries[i].pPanel->SetVisible( shouldBeVisible ); + } + bSomethingChanged = true; + } + } + + if ( m_pQuitButton && m_pDisconnectButton && m_pBackToReplaysButton ) + { + bool bShowQuit = !( bInGame || bInReplay ); + bool bShowDisconnect = bInGame && !bInReplay; + + if ( m_pQuitButton->IsVisible() != bShowQuit ) + { + m_pQuitButton->SetVisible( bShowQuit ); + } + + if ( m_pBackToReplaysButton->IsVisible() != bInReplay ) + { + m_pBackToReplaysButton->SetVisible( bInReplay ); + } + + if ( m_pDisconnectButton->IsVisible() != bShowDisconnect ) + { + m_pDisconnectButton->SetVisible( bShowDisconnect ); + } + + if ( bShowDisconnect ) + { + bool bIsDisconnectText = GTFGCClientSystem()->GetCurrentServerAbandonStatus() != k_EAbandonGameStatus_AbandonWithPenalty; + if ( m_bIsDisconnectText != bIsDisconnectText ) + { + m_bIsDisconnectText = bIsDisconnectText; + m_pDisconnectButton->SetText( m_bIsDisconnectText ? "#GameUI_GameMenu_Disconnect" : "#TF_MM_Rejoin_Abandon" ); + } + } + } + + if ( m_pBackground ) + { + if ( cl_mainmenu_operation_motd_reset.GetBool() && cl_mainmenu_operation_motd_start.GetBool() ) + { + cl_mainmenu_operation_motd_start.SetValue( 0 ); + cl_mainmenu_operation_motd_reset.SetValue( 0 ); + } + + if ( !cl_mainmenu_operation_motd_start.GetInt() ) + { + char sztime[k_RTimeRenderBufferSize]; + CRTime::RTime32ToString( CRTime::RTime32TimeCur(), sztime ); + cl_mainmenu_operation_motd_start.SetValue( sztime ); + } + + bool bShouldBeVisible = bInGame == false; + if ( m_pBackground->IsVisible() != bShouldBeVisible ) + { + m_pBackground->SetVisible( bShouldBeVisible ); + + // Always show this on startup when we have a new campaign + if ( m_bStabilizedInitialLayout && bShouldBeVisible && ( m_bHaveNewMOTDs || !m_bMOTDShownAtStartup ) ) + { + RTime32 rtFirstLaunchTime = CRTime::RTime32FromString( cl_mainmenu_operation_motd_start.GetString() ); + RTime32 rtThreeDaysFromStart = CRTime::RTime32DateAdd( rtFirstLaunchTime, 7, k_ETimeUnitDay ); + if ( m_bHaveNewMOTDs || CRTime::RTime32TimeCur() < rtThreeDaysFromStart ) + { + SetMOTDVisible( true ); + m_bMOTDShownAtStartup = true; + } + } + } + } + + if ( bSomethingChanged ) + { + InvalidateLayout(); + + ScheduleItemCheck(); + } + + if ( !bInGame && m_flCheckTrainingAt && m_flCheckTrainingAt < engine->Time() ) + { + m_flCheckTrainingAt = 0; + CheckTrainingStatus(); + } + + if ( !bInGame && m_flCheckUnclaimedItems && m_flCheckUnclaimedItems < engine->Time() ) + { + m_flCheckUnclaimedItems = 0; + CheckUnclaimedItems(); + } + +#ifdef SAXXYMAINMENU_ENABLED + const bool bSaxxyShouldBeVisible = !bInGame && !bInReplay; + if ( !m_pSaxxyAwardsPanel && bSaxxyShouldBeVisible ) + { + m_pSaxxyAwardsPanel = new CSaxxyAwardsPanel( this, "SaxxyPanel" ); + + if ( m_pSaxxySettings ) + { + m_pSaxxyAwardsPanel->ApplySettings( m_pSaxxySettings ); + } + + m_pSaxxyAwardsPanel->InvalidateLayout( true, true ); + } + else if ( m_pSaxxyAwardsPanel && !bSaxxyShouldBeVisible ) + { + m_pSaxxyAwardsPanel->MarkForDeletion(); + m_pSaxxyAwardsPanel = NULL; + } +#endif + + if ( m_pVRModeButton && m_pVRModeButton->IsVisible() ) + { + if( UseVR() ) + m_pVRModeButton->SetText( "#MMenu_VRMode_Deactivate" ); + else + m_pVRModeButton->SetText( "#MMenu_VRMode_Activate" ); + } + + if ( !IsLayoutInvalid() ) + { + m_bStabilizedInitialLayout = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Check to see if we need to hound the player about unclaimed items. +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::CheckUnclaimedItems() +{ + // Only do this if we don't have a notification about unclaimed items already. + for ( int i=0; i<NotificationQueue_GetNumNotifications(); i++ ) + { + CEconNotification* pNotification = NotificationQueue_Get( i ); + if ( pNotification ) + { + if ( !Q_strcmp( pNotification->GetUnlocalizedText(), "TF_HasNewItems") ) + { + return; + } + } + } + + // Only provide a notification if there are items to pick up. + if ( TFInventoryManager()->GetNumItemPickedUpItems() == 0 ) + return; + + TFInventoryManager()->GetLocalTFInventory()->NotifyHasNewItems(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::OnConfirm( KeyValues *pParams ) +{ + if ( pParams->GetBool( "confirmed" ) ) + { + engine->ClientCmd_Unrestricted( "disconnect" ); + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine training_showdlg" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::UpdateMOTD( bool bNewMOTDs ) +{ + if ( m_bInitMOTD == false ) + { + m_pMOTDPanel->InvalidateLayout( true, true ); + m_bInitMOTD = true; + } + + if ( bNewMOTDs ) + { + m_bHaveNewMOTDs = true; + m_iCurrentMOTD = -1; + } + + int iCount = GetMOTDManager().GetNumMOTDs(); + if ( !iCount || m_iCurrentMOTD < 0 ) + { + m_iCurrentMOTD = (iCount-1); + } + + // If we don't have an MOTD selected, show the most recent one + CMOTDEntryDefinition *pMOTD = GetMOTDManager().GetMOTDByIndex( m_iCurrentMOTD ); + if ( pMOTD ) + { + char uilanguage[ 64 ]; + uilanguage[0] = 0; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + ELanguage nCurLang = PchLanguageToELanguage( uilanguage ); + + RTime32 nTime = pMOTD->GetPostTime(); + wchar_t wzDate[64]; + + char rgchDateBuf[ 128 ]; + BGetLocalFormattedDate( nTime, rgchDateBuf, sizeof( rgchDateBuf ) ); + + // Start with the day ("Aug 21") + CRTime cTime( nTime ); + g_pVGuiLocalize->ConvertANSIToUnicode( rgchDateBuf, wzDate, sizeof( wzDate ) ); + + m_pMOTDPanel->SetDialogVariable( "motddate", wzDate ); + + // Header Color and text + if ( m_pMOTDHeaderLabel ) + { + m_pMOTDHeaderLabel->SetText( pMOTD->GetHeaderTitle(nCurLang) ); + int iHeaderType = pMOTD->GetHeaderType(); + switch ( iHeaderType ) + { + case 0: + m_pMOTDHeaderLabel->SetBgColor( Color ( 183, 108, 58, 255 ) ); + break; + case 1: + m_pMOTDHeaderLabel->SetBgColor( Color ( 141, 178, 61, 255 ) ); + break; + default: + m_pMOTDHeaderLabel->SetBgColor( Color ( 183, 108, 58, 255 ) ); + break; + } + } + + if ( m_pMOTDHeaderIcon ) + { + // Header Class icon + if ( pMOTD->GetHeaderIcon() == NULL || Q_strcmp( pMOTD->GetHeaderIcon(), "" ) == 0) + { + m_pMOTDHeaderIcon->SetVisible(false); + } + else + { + m_pMOTDHeaderIcon->SetVisible(true); + m_pMOTDHeaderIcon->SetImage( pMOTD->GetHeaderIcon() ); + } + } + + // Set the Title and change font until it fits + // title + int iTitleWide = 0; + int iTitleTall = 0; + + int iLabelWide = 0; + int iLabelTall = 0; + + wchar_t wszText[512]; + g_pVGuiLocalize->ConvertANSIToUnicode( pMOTD->GetTitle(nCurLang), wszText, sizeof( wszText ) ); + + if ( m_hTitleLabelFont != vgui::INVALID_FONT ) + { + surface()->GetTextSize( m_hTitleLabelFont, wszText, iTitleWide, iTitleTall ); + } + + if ( m_pMOTDTitleLabel ) + { + m_pMOTDTitleLabel->GetSize( iLabelWide, iLabelTall ); + + if ( iTitleWide > iLabelWide ) + { + IScheme *pScheme = scheme()->GetIScheme( m_pMOTDTitleLabel->GetScheme() ); + + int hMediumBoldFont = pScheme->GetFont( "HudFontMediumBold" ); + surface()->GetTextSize( hMediumBoldFont, wszText, iTitleWide, iTitleTall ); + if ( iTitleWide > iLabelWide ) + { + m_pMOTDTitleLabel->SetFont( pScheme->GetFont( "HudFontMediumSmallBold" ) ); + } + else + { + m_pMOTDTitleLabel->SetFont( hMediumBoldFont ); + } + } + else + { + if ( m_hTitleLabelFont != vgui::INVALID_FONT ) + { + m_pMOTDTitleLabel->SetFont( m_hTitleLabelFont ); + } + } + } + m_pMOTDPanel->SetDialogVariable( "motdtitle", pMOTD->GetTitle(nCurLang) ); + + // Body Text + m_pMOTDTextPanel->SetDialogVariable( "motdtext", pMOTD->GetText(nCurLang) ); + + // Image + const char* pszImage = pMOTD->GetImage(); + + if ( m_pMOTDTitleImage ) + { + m_pMOTDTitleImage->SetShouldScaleImage( false ); + if ( pszImage == NULL || Q_strcmp( pszImage, "" ) == 0 || Q_strcmp( pszImage, "class_icons/filter_all_on") == 0 ) + { + m_pMOTDTitleImage->SetImage( "../logo/new_tf2_logo" ); + } + else + { + m_pMOTDTitleImage->SetImage( pszImage ); + } + + IImage *pImage = m_pMOTDTitleImage->GetImage(); + int iContentWide = 0; + int iContentTall = 0; + if ( m_pMOTDTitleImageContainer ) + { + m_pMOTDTitleImageContainer->GetSize( iContentWide, iContentTall ); + } + + int iImgWide; + int iImgTall; + pImage->GetSize( iImgWide, iImgTall ); + + // get the size of the content + // perform a uniform scale along the horizontal + float fImageScale = MIN( (float)iContentWide / (float)iImgWide, 1.0f ); + float fScaledTall = iImgTall * fImageScale; + float fScaledWide = iImgWide * fImageScale; + pImage->SetSize( fScaledWide, fScaledTall ); + + // reposition the image so that its centered + m_pMOTDTitleImage->SetPos( (iContentWide - fScaledWide) / 2, (iContentTall - fScaledTall) / 2 ); + } + + // We need to resize our text label to fit all the text + if ( m_pMOTDTextLabel ) + { + m_pMOTDTextLabel->InvalidateLayout( true ); + + int wide, tall; + m_pMOTDTextLabel->GetContentSize(wide, tall); + m_pMOTDTextLabel->SetSize( m_pMOTDTextPanel->GetWide(), tall ); + m_pMOTDTextPanel->SetSize( m_pMOTDTextPanel->GetWide(), m_pMOTDTextLabel->GetTall() ); + } + + if ( m_pMOTDURLButton ) + { + const char *pszURL = pMOTD->GetURL(); + m_pMOTDURLButton->SetVisible( (pszURL && pszURL[0]) ); + } + if ( m_pMOTDPrevButton ) + { + m_pMOTDPrevButton->SetEnabled( m_iCurrentMOTD > 0 ); + m_pMOTDPrevButton->SetSubImage( m_iCurrentMOTD > 0 ? "blog_back" : "blog_back_disabled" ); + } + if ( m_pMOTDNextButton ) + { + m_pMOTDNextButton->SetEnabled( m_iCurrentMOTD < (iCount-1) ); + m_pMOTDNextButton->SetSubImage( m_iCurrentMOTD < (iCount-1) ? "blog_forward" : "blog_forward_disabled" ); + } + + // Move our scrollbar to the top. + m_pMOTDTextScroller->InvalidateLayout(); + m_pMOTDTextScroller->Repaint(); + m_pMOTDTextScroller->GetScrollbar()->SetValue( 0 ); + m_pMOTDTextScroller->GetScrollbar()->SetVisible( m_pMOTDTextPanel->GetTall() > m_pMOTDTextScroller->GetScrollbar()->GetTall() ); + m_pMOTDTextScroller->GetScrollbar()->InvalidateLayout(); + m_pMOTDTextScroller->GetScrollbar()->Repaint(); + } + else + { + // Hide the MOTD, and the button to show it. + SetMOTDVisible( false ); + + if ( m_pMOTDShowPanel ) + { + m_pMOTDShowPanel->SetVisible( false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetMOTDButtonVisible( bool bVisible ) +{ + if ( bVisible && m_pMOTDPanel && m_pMOTDPanel->IsVisible() ) + return; + + if ( m_pMOTDShowPanel ) + { + // Show the notifications show panel button if we have new notifications. + m_pMOTDShowPanel->SetVisible( bVisible ); + + if ( bVisible && m_bHaveNewMOTDs ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pMOTDShowPanel, "HasMOTDBlink" ); + } + else + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pMOTDShowPanel, "HasMOTDBlinkStop" ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetMOTDVisible( bool bVisible ) +{ + m_pMOTDPanel->SetVisible( bVisible ); + + if ( bVisible ) + { + // Ensure the text is correct. + UpdateMOTD( false ); + + // Clear MOTD button. + SetMOTDButtonVisible( true ); + SetNotificationsPanelVisible( false ); + SetQuestLogVisible( false ); + SetWatchStreamVisible( false ); + //SetNotificationsButtonVisible( false ); + + // Consider new MOTDs as having been viewed. + m_bHaveNewMOTDs = false; + } + else + { + SetMOTDButtonVisible( true ); + UpdateNotifications(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetQuestLogVisible( bool bVisible ) +{ + GetQuestLog()->ShowPanel( bVisible ); + + if ( bVisible ) + { + SetMOTDVisible( false ); + SetNotificationsPanelVisible( false ); + SetWatchStreamVisible( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetWatchStreamVisible( bool bVisible ) +{ + m_pWatchStreamsPanel->SetVisible( bVisible ); + + if ( bVisible ) + { + SetMOTDVisible( false ); + SetNotificationsPanelVisible( false ); + SetQuestLogVisible( false ); + } +} + +bool CHudMainMenuOverride::CheckAndWarnForPREC( void ) +{ + enum check_state + { + INVALID, + FOUND, + NOT_FOUND, + }; + + static check_state s_state = INVALID; + if ( s_state == INVALID ) + { + s_state = NOT_FOUND; + + ICvar::Iterator iter( g_pCVar ); + for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) + { + ConCommandBase *cmd = iter.Get(); + if ( cmd ) + { + if ( !Q_strncmp( cmd->GetName(), "prec_", 5 ) ) + { + s_state = FOUND; + break; + } + } + } + } + + if ( s_state == FOUND ) + { + ShowMessageBox( "#TF_Incompatible_AddOn", "#TF_PREC_Loaded" ); + } + + return ( s_state == FOUND ); +} + +void CHudMainMenuOverride::OpenMvMMMPanel() +{ + if ( CheckAndWarnForPREC() ) + return; + + GetMvMLobbyPanel()->ShowPanel( true ); +} + +void CHudMainMenuOverride::OpenCompMMPanel() +{ + if ( CheckAndWarnForPREC() ) + return; + + GetCompLobbyPanel()->ShowPanel( true ); +} + +void CHudMainMenuOverride::OpenCasualMMPanel() +{ + if ( CheckAndWarnForPREC() ) + return; + + GetCasualLobbyPanel()->ShowPanel( true ); +} + +CLobbyContainerFrame_Comp* CHudMainMenuOverride::GetCompLobbyPanel() +{ + static CLobbyContainerFrame_Comp* pCompPanel = NULL; + if ( pCompPanel == NULL ) + { + pCompPanel = SETUP_PANEL( new CLobbyContainerFrame_Comp() ); + } + + return pCompPanel; +} + +CLobbyContainerFrame_MvM* CHudMainMenuOverride::GetMvMLobbyPanel() +{ + static CLobbyContainerFrame_MvM* pMvMPanel = NULL; + if ( pMvMPanel == NULL ) + { + pMvMPanel = SETUP_PANEL( new CLobbyContainerFrame_MvM() ); + } + + return pMvMPanel; +} + +CLobbyContainerFrame_Casual* CHudMainMenuOverride::GetCasualLobbyPanel() +{ + static CLobbyContainerFrame_Casual* pCasualPanel = NULL; + if ( pCasualPanel == NULL ) + { + pCasualPanel = SETUP_PANEL( new CLobbyContainerFrame_Casual() ); + } + + return pCasualPanel; +} + +void CHudMainMenuOverride::ReloadMMPanels() +{ + if ( GetCasualLobbyPanel()->IsVisible() ) + { + GetCasualLobbyPanel()->InvalidateLayout( true, true ); + GetCasualLobbyPanel()->ShowPanel( true ); + } + + if ( GetCompLobbyPanel()->IsVisible() ) + { + GetCompLobbyPanel()->InvalidateLayout( true, true ); + GetCompLobbyPanel()->ShowPanel( true ); + } + + if ( GetMvMLobbyPanel()->IsVisible() ) + { + GetMvMLobbyPanel()->InvalidateLayout( true, true ); + GetMvMLobbyPanel()->ShowPanel( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::UpdateNotifications() +{ + int iNumNotifications = NotificationQueue_GetNumNotifications(); + + wchar_t wszNumber[16]=L""; + V_swprintf_safe( wszNumber, L"%i", iNumNotifications ); + wchar_t wszText[1024]=L""; + g_pVGuiLocalize->ConstructString_safe( wszText, g_pVGuiLocalize->Find( "#MMenu_Notifications_Show" ), 1, wszNumber ); + + m_pNotificationsPanel->SetDialogVariable( "notititle", wszText ); + + bool bHasNotifications = iNumNotifications != 0; + + if ( m_pNotificationsShowPanel ) + { + SetNotificationsButtonVisible( bHasNotifications ); + + bool bBlinkNotifications = bHasNotifications && m_pNotificationsShowPanel->IsVisible(); + if ( bBlinkNotifications ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pNotificationsShowPanel, "HasNotificationsBlink" ); + } + else + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pNotificationsShowPanel, "HasNotificationsBlinkStop" ); + } + } + + if ( !bHasNotifications ) + { + SetNotificationsButtonVisible( false ); + SetNotificationsPanelVisible( false ); + } + + AdjustNotificationsPanelHeight(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetNotificationsButtonVisible( bool bVisible ) +{ + if ( bVisible && ( m_pNotificationsPanel && m_pNotificationsPanel->IsVisible() ) ) + return; + + if ( m_pNotificationsShowPanel ) + { + // Show the notifications show panel button if we have new notifications. + m_pNotificationsShowPanel->SetVisible( bVisible ); + + // Set the notification count variable. + if ( m_pNotificationsShowPanel ) + { + m_pNotificationsShowPanel->SetDialogVariable( "noticount", NotificationQueue_GetNumNotifications() ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::SetNotificationsPanelVisible( bool bVisible ) +{ + if ( m_pNotificationsPanel ) + { + bool bHasNotifications = NotificationQueue_GetNumNotifications() != 0; + + if ( bHasNotifications ) + { + UpdateNotifications(); + } + + m_pNotificationsPanel->SetVisible( bVisible ); + + if ( bVisible ) + { + m_pNotificationsScroller->InvalidateLayout(); + m_pNotificationsScroller->GetScrollbar()->InvalidateLayout(); + m_pNotificationsScroller->GetScrollbar()->SetValue( 0 ); + + SetMOTDVisible( false ); + SetQuestLogVisible( false ); + SetWatchStreamVisible( false ); + + m_pNotificationsShowPanel->SetVisible( false ); + + m_pNotificationsControl->OnTick(); + m_pNotificationsControl->PerformLayout(); + AdjustNotificationsPanelHeight(); + + // Faster updating while open. + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + vgui::ivgui()->AddTickSignal( GetVPanel(), 5 ); + } + else + { + // Clear all notifications. + if ( bHasNotifications ) + { + SetNotificationsButtonVisible( true ); + } + + // Slower updating while closed. + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + vgui::ivgui()->AddTickSignal( GetVPanel(), 250 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::AdjustNotificationsPanelHeight() +{ + // Fit to our contents, which may change without notifying us. + int iNotiTall = m_pNotificationsControl->GetTall(); + if ( iNotiTall > m_pNotificationsScroller->GetTall() ) + iNotiTall = m_pNotificationsScroller->GetTall(); + int iTargetTall = YRES(40) + iNotiTall; + if ( m_pNotificationsPanel->GetTall() != iTargetTall ) + m_pNotificationsPanel->SetTall( iTargetTall ); + + // Adjust visibility of the slider buttons and our width, as contents change. + if ( m_pNotificationsScroller ) + { + if ( m_pNotificationsScroller->GetScrollbar()->GetSlider() && + m_pNotificationsScroller->GetScrollbar()->GetSlider()->IsSliderVisible() ) + { + m_pNotificationsPanel->SetWide( m_iNotiPanelWide + m_pNotificationsScroller->GetScrollbar()->GetSlider()->GetWide() ); + m_pNotificationsScroller->GetScrollbar()->SetScrollbarButtonsVisible( true ); + } + else + { + m_pNotificationsPanel->SetWide( m_iNotiPanelWide ); + m_pNotificationsScroller->GetScrollbar()->SetScrollbarButtonsVisible( false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::UpdatePromotionalCodes( void ) +{ + // should we show the promo codes button? + vgui::Panel *pPromoCodesButton = FindChildByName( "ShowPromoCodesButton" ); + if ( pPromoCodesButton ) + { + bool bShouldBeVisible = false; + if ( steamapicontext && steamapicontext->SteamUser() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID ); + if ( pSOCache ) + { + GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( k_EEconTypeClaimCode ); + bShouldBeVisible = pTypeCache != NULL && pTypeCache->GetCount() != 0; + } + } + + // The promo code button collides with the VR mode button. Turn off the promo code button + // in that case since the people who deliberately enabled VR are much more likely to want that + // than to claim their Well Spun Hat in Rift. + bool bShowVR = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter == materials->GetCurrentAdapter(); + if( bShowVR ) + { + bShouldBeVisible = false; + } + + // has the player turned off this button? + if ( !cl_promotional_codes_button_show.GetBool() ) + { + bShouldBeVisible = false; + } + + if ( pPromoCodesButton->IsVisible() != bShouldBeVisible ) + { + pPromoCodesButton->SetVisible( bShouldBeVisible ); + } + + if ( m_pVRModeBackground ) + { + m_pVRModeBackground->SetVisible( bShouldBeVisible || bShowVR ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudMainMenuOverride::IsVisible( void ) +{ + /* + // Only draw whenever the main menu is visible + if ( GetClientModeTFNormal()->GameUI() && steamapicontext && steamapicontext->SteamFriends() ) + return GetClientModeTFNormal()->GameUI()->IsMainMenuVisible(); + return BaseClass::IsVisible(); + */ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::StartHighlightAnimation( mm_highlight_anims iAnim ) +{ + vgui::surface()->PlaySound( "ui/hint.wav" ); + + if ( m_pHighlightAnims[ iAnim ] ) + { + m_pHighlightAnims[ iAnim ]->Popup(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::HideHighlight( mm_highlight_anims iAnim ) +{ + if ( m_pHighlightAnims[ iAnim ] ) + { + m_pHighlightAnims[ iAnim ]->Hide( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::TogglePlayListMenu( void ) +{ + if ( m_bPlayListExpanded ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "MMenu_PlayList_Collapse", false ); + } + else + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "MMenu_PlayList_Expand", false ); + UpdatePlaylistEntries(); + } + + // These all rely on the playlist being in a specific state. If we're + // toggling, then there's no guarantees anything is where we think it is anymore + HideHighlight( MMHA_TUTORIAL ); + HideHighlight( MMHA_PRACTICE ); + HideHighlight( MMHA_LOADOUT ); + HideHighlight( MMHA_STORE ); + HideHighlight( MMHA_WAR ); + + m_bPlayListExpanded = !m_bPlayListExpanded; + + CheckTrainingStatus(); +} + +void PromptOrFireCommand( const char* pszCommand ) +{ + if ( engine->IsInGame() ) + { + CTFDisconnectConfirmDialog *pDialog = BuildDisconnectConfirmDialog(); + if ( pDialog ) + { + pDialog->Show(); + pDialog->AddConfirmCommand( pszCommand ); + } + } + else + { + engine->ClientCmd_Unrestricted( pszCommand ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Make the glows behind the update buttons stop pulsing +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::StopUpdateGlow() +{ + // Dont ever glow again + if ( cl_mainmenu_updateglow.GetInt() ) + { + cl_mainmenu_updateglow.SetValue( 0 ); + engine->ClientCmd_Unrestricted( "host_writeconfig" ); + } + + if ( m_pEventPromoContainer ) + { + EditablePanel* pUpdateBackground = m_pEventPromoContainer->FindControl< EditablePanel >( "Background", true ); + if ( pUpdateBackground ) + { + g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( pUpdateBackground, "MMenu_UpdateButton_StartGlow" ); + pUpdateBackground->SetControlVisible( "ViewDetailsGlow", false, true ); + pUpdateBackground->SetControlVisible( "ViewWarButtonGlow", false, true ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::OnCommand( const char *command ) +{ + C_CTFGameStats::ImmediateWriteInterfaceEvent( "on_command(main_menu_override)", command ); + + if ( FStrEq( "toggle_play_menu", command ) ) + { + TogglePlayListMenu(); + return; + } + else if ( FStrEq( "play_competitive", command ) ) + { + // Defaulting to 6v6 + GTFGCClientSystem()->SetLadderType( k_nMatchGroup_Ladder_6v6 ); + PromptOrFireCommand( "OpenMatchmakingLobby ladder" ); + return; + } + else if ( FStrEq( "play_casual", command ) ) + { + // Defaulting to 12v12 + GTFGCClientSystem()->SetLadderType( k_nMatchGroup_Casual_12v12 ); + PromptOrFireCommand( "OpenMatchmakingLobby casual" ); + return; + } + else if ( FStrEq( "play_mvm", command ) ) + { + PromptOrFireCommand( "OpenMatchmakingLobby mvm" ); + return; + } + else if ( FStrEq( "play_quickplay", command ) ) + { + PromptOrFireCommand( "OpenQuickplayDialog" ); + return; + } + else if ( FStrEq( "play_training", command ) ) + { + HideHighlight( MMHA_TUTORIAL ); + + if ( engine->IsInGame() ) + { + const char *pText = "#TF_Training_Prompt"; + const char *pTitle = "#TF_Training_Prompt_Title"; + if ( TFGameRules() && TFGameRules()->IsInTraining() ) + { + pTitle = "#TF_Training_Restart_Title"; + pText = "#TF_Training_Restart_Text"; + } + CTFConfirmTrainingDialog *pConfirm = vgui::SETUP_PANEL( new CTFConfirmTrainingDialog( pText, pTitle, this ) ); + if ( pConfirm ) + { + pConfirm->Show(); + } + } + else + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine training_showdlg" ); + } + } + else if ( Q_strnicmp( command, "soundentry", 10 ) == 0 ) + { + PlaySoundEntry( command + 11 ); + return; + } + else if ( !Q_stricmp( command, "motd_viewurl" ) ) + { + CMOTDEntryDefinition *pMOTD = GetMOTDManager().GetMOTDByIndex( m_iCurrentMOTD ); + if ( pMOTD ) + { + const char *pszURL = pMOTD->GetURL(); + if ( pszURL && pszURL[0] ) + { + if ( steamapicontext && steamapicontext->SteamFriends() ) + { + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( pszURL ); + } + } + } + return; + } + else if ( !Q_stricmp( command, "view_newuser_forums" ) ) + { + HideHighlight( MMHA_NEWUSERFORUM ); + + if ( steamapicontext && steamapicontext->SteamFriends() ) + { + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://forums.steampowered.com/forums/forumdisplay.php?f=906" ); + } + return; + } + else if ( !Q_stricmp( command, "opentf2options" ) ) + { + HideHighlight( MMHA_OPTIONS ); + + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine opentf2options" ); + } + else if ( !Q_stricmp( command, "motd_prev" ) ) + { + if ( m_iCurrentMOTD > 0 ) + { + m_iCurrentMOTD--; + UpdateMOTD( false ); + } + return; + } + else if ( !Q_stricmp( command, "motd_next" ) ) + { + if ( m_iCurrentMOTD < (GetMOTDManager().GetNumMOTDs()-1) ) + { + m_iCurrentMOTD++; + UpdateMOTD( false ); + } + return; + } + else if ( !Q_stricmp( command, "motd_show" ) ) + { + SetMOTDVisible( !m_pMOTDPanel->IsVisible() ); + } + else if ( !Q_stricmp( command, "motd_hide" ) ) + { + SetMOTDVisible( false ); + } + else if ( !Q_stricmp( command, "noti_show" ) ) + { + SetNotificationsPanelVisible( true ); + } + else if ( !Q_stricmp( command, "noti_hide" ) ) + { + SetNotificationsPanelVisible( false ); + } + else if ( !Q_stricmp( command, "notifications_update" ) ) + { + // force visible if + if ( NotificationQueue_GetNumNotifications() != 0 ) + { + SetNotificationsButtonVisible( true ); + } + else + { + UpdateNotifications(); + } + } + else if ( !Q_stricmp( command, "test_anim" ) ) + { + InvalidateLayout( true, true ); + + StartHighlightAnimation( MMHA_TUTORIAL ); + StartHighlightAnimation( MMHA_PRACTICE ); + StartHighlightAnimation( MMHA_NEWUSERFORUM ); + StartHighlightAnimation( MMHA_OPTIONS ); + StartHighlightAnimation( MMHA_STORE ); + StartHighlightAnimation( MMHA_LOADOUT ); + StartHighlightAnimation( MMHA_WAR ); + } + else if ( !Q_stricmp( command, "offlinepractice" ) ) + { + HideHighlight( MMHA_PRACTICE ); + + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine training_showdlg" ); + } + else if ( !Q_stricmp( command, "buyfeatured" ) ) + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( VarArgs("engine open_store %d 1", m_pFeaturedItemPanel ? m_pFeaturedItemPanel->GetItem()->GetItemDefIndex() : 0 ) ); + } + else if ( !Q_stricmp( command, "armory_open" ) ) + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine open_charinfo_armory" ); + } + else if ( !Q_stricmp( command, "engine disconnect" ) && engine->IsInGame() && TFGameRules() && ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) ) + { + // If we're playing MvM, "New Game" should take us back to MvM matchmaking + CTFDisconnectConfirmDialog *pDialog = BuildDisconnectConfirmDialog(); + if ( pDialog ) + { + pDialog->Show(); + } + return; + } + else if ( !Q_stricmp( command, "callvote" ) ) + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "engine callvote" ); + if ( GetClientModeTFNormal()->GameUI() ) + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( "ResumeGame" ); + } + return; + } + else if ( !Q_stricmp( command, "showpromocodes" ) ) + { + if ( steamapicontext && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + switch ( GetUniverse() ) + { + case k_EUniversePublic: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStr1024( "http://steamcommunity.com/profiles/%llu/promocodes/tf2", steamID.ConvertToUint64() ) ); break; + case k_EUniverseBeta: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStr1024( "http://beta.steamcommunity.com/profiles/%llu/promocodes/tf2", steamID.ConvertToUint64() ) ); break; + case k_EUniverseDev: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStr1024( "http://localhost/community/profiles/%llu/promocodes/tf2", steamID.ConvertToUint64() ) ); break; + } + } + } + else if ( !Q_stricmp( command, "exitreplayeditor" ) ) + { + #if defined( REPLAY_ENABLED ) + CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor(); + if ( !pEditor ) + return; + + pEditor->Exit_ShowDialogs(); +#endif // REPLAY_ENABLED + } + else if ( FStrEq( "showcomic", command ) ) + { + if ( m_pWarLandingPage ) + { + m_pWarLandingPage->InvalidateLayout( true, true ); + m_pWarLandingPage->SetVisible( true ); + } + } + else if ( FStrEq( "questlog", command ) ) + { + SetQuestLogVisible( !GetQuestLog()->IsVisible() ); + } + else if ( FStrEq( "watch_stream", command ) ) + { + SetWatchStreamVisible( !m_pWatchStreamsPanel->IsVisible() ); + } + else if ( FStrEq( "view_update_page", command ) ) + { + StopUpdateGlow(); + + if ( steamapicontext && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->IsOverlayEnabled() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + switch ( GetUniverse() ) + { + case k_EUniversePublic: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/meetyourmatch" ); break; + case k_EUniverseBeta: // Fall through + case k_EUniverseDev: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://csham.valvesoftware.com/tf.com/meetyourmatch" ); break; + } + } + else + { + OpenStoreStatusDialog( NULL, "#MMenu_OverlayRequired", true, false ); + } + return; + } + else if ( FStrEq( "view_update_comic", command ) ) + { + if ( steamapicontext && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->IsOverlayEnabled() ) + { + CSteamID steamID = steamapicontext->SteamUser()->GetSteamID(); + switch ( GetUniverse() ) + { + case k_EUniversePublic: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/gargoyles_and_gravel" ); break; + case k_EUniverseBeta: // Fall through + case k_EUniverseDev: steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/gargoyles_and_gravel" ); break; + } + } + else + { + OpenStoreStatusDialog( NULL, "#MMenu_OverlayRequired", true, false ); + } + return; + } + else if ( FStrEq( "view_war", command ) ) + { + HideHighlight( MMHA_WAR ); + StopUpdateGlow(); + + m_pWarLandingPage->InvalidateLayout( true, true ); + m_pWarLandingPage->SetVisible( true ); + + return; + } + else if ( FStrEq( "comp_access_info", command ) ) + { + if ( m_pCompetitiveAccessInfo ) + { + m_pCompetitiveAccessInfo->SetVisible( true ); + } + } + else if ( FStrEq( "OpenReportPlayerDialog", command ) ) + { + if ( !m_hReportPlayerDialog.Get() ) + { + m_hReportPlayerDialog = vgui::SETUP_PANEL( new CReportPlayerDialog( this ) ); + int x, y, ww, wt, wide, tall; + vgui::surface()->GetWorkspaceBounds( x, y, ww, wt ); + m_hReportPlayerDialog->GetSize(wide, tall); + + // Center it, keeping requested size + m_hReportPlayerDialog->SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2)); + } + m_hReportPlayerDialog->Activate(); + } + else + { + // Pass it on to GameUI main menu + if ( GetClientModeTFNormal()->GameUI() ) + { + GetClientModeTFNormal()->GameUI()->SendMainMenuCommand( command ); + return; + } + } + + BaseClass::OnCommand( command ); +} + +void CHudMainMenuOverride::OnKeyCodePressed( KeyCode code ) +{ + if ( code == KEY_XBUTTON_B && engine->IsInGame() ) + { + OnCommand( "ResumeGame" ); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::CheckTrainingStatus( void ) +{ + bool bNeedsTraining = tf_training_has_prompted_for_training.GetInt() <= 0; + bool bNeedsPractice = tf_training_has_prompted_for_offline_practice.GetInt() <= 0; + bool bShowForum = tf_training_has_prompted_for_forums.GetInt() <= 0; + bool bShowOptions = tf_training_has_prompted_for_options.GetInt() <= 0; + bool bWasInTraining = m_bWasInTraining; + m_bWasInTraining = false; + + bool bShowLoadout = false; + if ( tf_training_has_prompted_for_loadout.GetInt() <= 0 ) + { + // See if we have any items in our inventory. + int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetItemCount(); + if ( iNumItems > 0 ) + { + bShowLoadout = true; + } + } + + if ( bShowLoadout && !m_bPlayListExpanded ) + { + tf_training_has_prompted_for_loadout.SetValue( 1 ); + StartHighlightAnimation( MMHA_LOADOUT ); + } + else if ( bNeedsTraining && m_bPlayListExpanded ) + { + tf_training_has_prompted_for_training.SetValue( 1 ); + + if ( m_pHighlightAnims[ MMHA_TUTORIAL ] ) + { + if ( UTIL_HasLoadedAnyMap() ) + { + m_pHighlightAnims[ MMHA_TUTORIAL ]->SetDialogVariable( "highlighttext", g_pVGuiLocalize->Find( "#MMenu_TutorialHighlight_Title2" ) ); + } + else + { + m_pHighlightAnims[ MMHA_TUTORIAL ]->SetDialogVariable( "highlighttext", g_pVGuiLocalize->Find( "#MMenu_TutorialHighlight_Title" ) ); + } + } + + StartHighlightAnimation( MMHA_TUTORIAL ); + } + else if ( bWasInTraining && Training_IsComplete() == false && tf_training_has_prompted_for_training.GetInt() < 2 && m_bPlayListExpanded) + { + tf_training_has_prompted_for_training.SetValue( 2 ); + + if ( m_pHighlightAnims[ MMHA_TUTORIAL ] ) + { + m_pHighlightAnims[ MMHA_TUTORIAL ]->SetDialogVariable( "highlighttext", g_pVGuiLocalize->Find( "#MMenu_TutorialHighlight_Title3" ) ); + } + + StartHighlightAnimation( MMHA_TUTORIAL ); + } + else if ( bNeedsPractice && m_bPlayListExpanded ) + { + tf_training_has_prompted_for_offline_practice.SetValue( 1 ); + StartHighlightAnimation( MMHA_PRACTICE ); + } + else if ( bShowForum ) + { + tf_training_has_prompted_for_forums.SetValue( 1 ); + StartHighlightAnimation( MMHA_NEWUSERFORUM ); + } + else if ( bShowOptions ) + { + tf_training_has_prompted_for_options.SetValue( 1 ); + StartHighlightAnimation( MMHA_OPTIONS ); + } +} + +void CHudMainMenuOverride::CheckForNewQuests( void ) +{ + CUtlVector< CEconItemView * > questItems; + TFInventoryManager()->GetAllQuestItems( &questItems ); + + ImagePanel *pImage = m_pQuestLogButton->FindControl< ImagePanel >( "SubImage", true ); + if ( pImage ) + { + if ( questItems.Count() > 0 ) + { + pImage->SetImage( "button_quests" ); + } + else + { + pImage->SetImage( "button_quests_disabled" ); + } + } + + EditablePanel *pNotiPanel = m_pQuestLogButton->FindControl< EditablePanel >( "NotificationsContainer", true ); + if ( pNotiPanel ) + { + // how many quests are unidentified? + int iUnidentified = 0; + FOR_EACH_VEC( questItems, i ) + { + if ( IsQuestItemUnidentified( questItems[i]->GetSOCData() ) ) + { + iUnidentified++; + } + } + + pNotiPanel->SetDialogVariable( "noticount", iUnidentified ); + pNotiPanel->SetVisible( iUnidentified > 0 ); + } +} + +void CHudMainMenuOverride::UpdatePlaylistEntries( void ) +{ + CMainMenuPlayListEntry::EDisabledStates_t eDisabledState = CMainMenuPlayListEntry::NOT_DISABLED; + + CTFParty* pParty = GTFGCClientSystem()->GetParty(); + if ( ( pParty && pParty->BOffline() ) || !GTFGCClientSystem()->BConnectedtoGC() || GTFGCClientSystem()->BHasOutstandingMatchmakingPartyMessage() ) + { + eDisabledState = CMainMenuPlayListEntry::DISABLED_NO_GC; + } + + // If we have a live match, and a we're not in it, but we should be in, + // dont let the user click the MM UI buttons. GTFGCClientSystem::Update() will nag them + // to rejoin their match or abandon. + if ( pParty && pParty->GetState() == CSOTFParty_State_IN_MATCH ) + { + eDisabledState = CMainMenuPlayListEntry::DISABLED_MATCH_RUNNING; + } + + CMainMenuPlayListEntry* pEntry = FindControl< CMainMenuPlayListEntry >( "CasualEntry", true ); + if ( pEntry ) + { + pEntry->SetDisabledReason( eDisabledState ); + } + + pEntry = FindControl< CMainMenuPlayListEntry >( "MvMEntry", true ); + if ( pEntry ) + { + pEntry->SetDisabledReason( eDisabledState ); + } + + pEntry = FindControl< CMainMenuPlayListEntry >( "CompetitiveEntry", true ); + if ( pEntry ) + { + // Only check competitive access last + if ( eDisabledState == CMainMenuPlayListEntry::NOT_DISABLED ) + { + eDisabledState = !GTFGCClientSystem()->BHasCompetitiveAccess() ? CMainMenuPlayListEntry::DISABLED_NO_COMP_ACCESS : eDisabledState; + } + pEntry->SetDisabledReason( eDisabledState ); + } +} + +void CHudMainMenuOverride::SOEvent( const CSharedObject* pObject ) +{ + if ( pObject->GetTypeID() == CEconGameAccountClient::k_nTypeID ) + { + UpdatePlaylistEntries(); + } + + if ( pObject->GetTypeID() != CEconItem::k_nTypeID ) + return; + + CEconItem *pEconItem = (CEconItem *)pObject; + + // If the item is a competitive pass - update the main menu lock + // From _items_main.txt + const item_definition_index_t kCompetitivePassID = 1167; + if ( pEconItem->GetItemDefIndex() == kCompetitivePassID ) + { + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName(PANEL_MAINMENUOVERRIDE); + if (pMMPanel) + { + pMMPanel->UpdatePlaylistEntries(); + } + } + +} + + + +#define REMAP_COMMAND( oldCommand, newCommand ) \ + const char *pszKey##oldCommand = engine->Key_LookupBindingExact(#oldCommand); \ + const char *pszNewKey##oldCommand = engine->Key_LookupBindingExact(#newCommand); \ + if ( pszKey##oldCommand && !pszNewKey##oldCommand ) \ + { \ + Msg( "Rebinding key %s to new command " #newCommand ".\n", pszKey##oldCommand ); \ + engine->ClientCmd_Unrestricted( VarArgs( "bind \"%s\" \"" #newCommand "\"\n", pszKey##oldCommand ) ); \ + } + +//----------------------------------------------------------------------------- +// Purpose: Rebinds any binds for old commands to their new commands. +//----------------------------------------------------------------------------- +void CHudMainMenuOverride::PerformKeyRebindings( void ) +{ + REMAP_COMMAND( inspect, +inspect ); + REMAP_COMMAND( taunt, +taunt ); + REMAP_COMMAND( use_action_slot_item, +use_action_slot_item ); + REMAP_COMMAND( use_action_slot_item_server, +use_action_slot_item_server ); +} + +//----------------------------------------------------------------------------- +// Purpose: GC Msg handler to receive the MOTD request response +//----------------------------------------------------------------------------- +class CGCMOTDRequestResponse : public GCSDK::CGCClientJob +{ +public: + CGCMOTDRequestResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CGCMsg<MsgGCMOTDRequestResponse_t> msg( pNetPacket ); + + // No new entries? + if ( !msg.Body().m_nEntries ) + return true; + + // No main menu panel? + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ); + if ( !pMMPanel ) + return true; + + // Get our local language + char uilanguage[ 64 ]; + uilanguage[0] = 0; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + + //V_strcpy_safe( uilanguage, "german" ); + + KeyValues *pEntriesKV = new KeyValues( "motd_entries"); + + // Try and load the cache file. If we fail, we'll just create a new one. + if ( !pMMPanel->ReloadedAllMOTDs() ) + { + pEntriesKV->LoadFromFile( g_pFullFileSystem, GC_MOTD_CACHE_FILE ); + } + + bool bNewMOTDs = false; + + // Store the time & language we last checked. + char rtime_buf[k_RTimeRenderBufferSize]; + pEntriesKV->SetString( "last_request_time", CRTime::RTime32ToString( pMMPanel->GetLastMOTDRequestTime(), rtime_buf ) ); + pEntriesKV->SetString( "last_request_language", GetLanguageShortName( pMMPanel->GetLastMOTDRequestLanguage() ) ); + + // Read in the entries one by one, and insert them into our keyvalues structure. + for ( int i = 0; i < msg.Body().m_nEntries; i++ ) + { + char pchMsgString[2048]; + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + + // If there's already an entry with this index, overwrite the data + KeyValues *pNewEntry = pEntriesKV->FindKey( pchMsgString ); + if ( !pNewEntry ) + { + pNewEntry = new KeyValues( pchMsgString ); + pEntriesKV->AddSubKey( pNewEntry ); + } + pNewEntry->SetName( pchMsgString ); + + RTime32 iTime; + if ( !msg.BReadUintData( &iTime ) ) + return false; + pNewEntry->SetString( "post_time", CRTime::RTime32ToString(iTime, rtime_buf) ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( VarArgs("title_%s", uilanguage), pchMsgString ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( VarArgs("text_%s", uilanguage), pchMsgString ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( "url", pchMsgString ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( "image", pchMsgString ); + + int iHeadertype; + if ( !msg.BReadIntData( &iHeadertype ) ) + return false; + pNewEntry->SetString( "header_type", CFmtStr( "%d", iHeadertype ).Access() ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( VarArgs("header_%s", uilanguage), pchMsgString ); + + if ( !msg.BReadStr( pchMsgString, Q_ARRAYSIZE( pchMsgString ) ) ) + return false; + pNewEntry->SetString( "header_icon", pchMsgString ); + + bNewMOTDs = true; + } + + // Tell the schema to reload its MOTD block + CUtlVector< CUtlString > vecErrors; + pMMPanel->GetMOTDManager().BInitMOTDEntries( pEntriesKV, &vecErrors ); + pMMPanel->GetMOTDManager().PurgeUnusedMOTDEntries( pEntriesKV ); + + // Save out our cache + pEntriesKV->SaveToFile( g_pFullFileSystem, GC_MOTD_CACHE_FILE ); + + // And tell the main menu to refresh the MOTD. + //pMMPanel->SetMOTDVisible( bNewMOTDs ); HACK! Temporarily turn this off! + pMMPanel->UpdateMOTD( bNewMOTDs ); + return true; + } +}; + +GC_REG_JOB( GCSDK::CGCClient, CGCMOTDRequestResponse, "CGCMOTDRequestResponse", k_EMsgGCMOTDRequestResponse, GCSDK::k_EServerTypeGCClient ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainMenuToolTip::PerformLayout() +{ + if ( !ShouldLayout() ) + return; + + _isDirty = false; + + // Resize our text labels to fit. + int iW = 0; + int iH = 0; + for (int i = 0; i < m_pEmbeddedPanel->GetChildCount(); i++) + { + vgui::Label *pLabel = dynamic_cast<vgui::Label*>( m_pEmbeddedPanel->GetChild(i) ); + if ( !pLabel ) + continue; + + // Only checking to see if we have any text + char szTmp[2]; + pLabel->GetText( szTmp, sizeof(szTmp) ); + if ( !szTmp[0] ) + continue; + + pLabel->InvalidateLayout(true); + + int iX, iY; + pLabel->GetPos( iX, iY ); + iW = MAX( iW, ( pLabel->GetWide() + (iX * 2) ) ); + + if ( iH == 0 ) + { + iH += MAX( iH, pLabel->GetTall() + (iY * 2) ); + } + else + { + iH += MAX( iH, pLabel->GetTall() ); + } + } + m_pEmbeddedPanel->SetSize( iW, iH ); + + m_pEmbeddedPanel->SetVisible(true); + + PositionWindow( m_pEmbeddedPanel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainMenuToolTip::HideTooltip() +{ + if ( m_pEmbeddedPanel ) + { + m_pEmbeddedPanel->SetVisible(false); + } + + BaseTooltip::HideTooltip(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainMenuToolTip::SetText(const char *pszText) +{ + if ( m_pEmbeddedPanel ) + { + _isDirty = true; + + if ( pszText && pszText[0] == '#' ) + { + m_pEmbeddedPanel->SetDialogVariable( "tiptext", g_pVGuiLocalize->Find( pszText ) ); + } + else + { + m_pEmbeddedPanel->SetDialogVariable( "tiptext", pszText ); + } + m_pEmbeddedPanel->SetDialogVariable( "tipsubtext", "" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Reload the .res file +//----------------------------------------------------------------------------- +#if defined( STAGING_ONLY ) +ConVar tf_icon_festive( "tf_icon_festive", 0 ); +CON_COMMAND( mainmenu_refresh, "" ) +{ + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ); + if ( !pMMPanel ) + return; + + pMMPanel->InvalidateLayout( true, true ); +} + +CON_COMMAND( create_icons, "Generate 512 x 512 Paint Kit Item Icons for SteamMarket, Specify min and max itemdef ranges if desired" ) +{ + tf_icon_festive.SetValue( false ); + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ); + if ( !pMMPanel ) + return; + + int min = args.ArgC() > 1 ? atoi( args[1] ) : -1; + int max = args.ArgC() > 2 ? atoi( args[2] ) : -1; + + pMMPanel->GenerateIcons( false, min, max ); +} + +CON_COMMAND( create_icons_large, "Generate 1024 x 1024 Paint Kit Item Icons for Testing, Specify min and max itemdef ranges if desired" ) +{ + tf_icon_festive.SetValue( false ); + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ); + if ( !pMMPanel ) + return; + + int min = args.ArgC() > 1 ? atoi( args[1] ) : -1; + int max = args.ArgC() > 2 ? atoi( args[2] ) : -1;; + + pMMPanel->GenerateIcons( true, min, max ); +} + +CON_COMMAND( create_icons_festive, "" ) +{ + tf_icon_festive.SetValue( true ); + + CHudMainMenuOverride *pMMPanel = (CHudMainMenuOverride*)gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ); + if ( !pMMPanel ) + return; + + int min = args.ArgC() > 1 ? atoi( args[1] ) : -1; + int max = args.ArgC() > 2 ? atoi( args[2] ) : -1; + + pMMPanel->GenerateIcons( false, min, max ); +} + +//----------------------------------------------------------------------------- +void BuildPaintkitItemInventoryImagePath( char *pchOutfile, int nMaxPath, const CTFItemDefinition *pItemDef, int iWear, bool bLargeTestIcons ) +{ +// CUtlString strDefName( pItemDef->GetDefinitionName() ); + //strDefName = strDefName.Replace( ' ', '_' ); + //strDefName.ToLower(); +// const char *pchDefName = strDefName; + + bool bIsPaintkitItem = pItemDef->GetCustomPainkKitDefinition() != NULL; + const char *pchOutputFolder; + if ( bIsPaintkitItem ) + { + pchOutputFolder = bLargeTestIcons ? "resource/econ/generated_icons/LargeTest/" : "scripts/items/unencrypted/icons/generated_paintkit_icons/"; + } + else + { + pchOutputFolder = "scripts/items/unencrypted/icons/generated_item_icons/"; + } + +// const char *pWear = bIsPaintkitItem ? CFmtStr( "_wear%d", iWear ) : ""; + char fname[ MAX_PATH ]; + V_FileBase( pItemDef->GetInventoryImage(), fname, sizeof(fname) ); + + if ( tf_icon_festive.GetBool() == true ) + { + V_snprintf( pchOutfile, nMaxPath, "%s%s_festive.png", + pchOutputFolder, + fname + ); + } + else + { + V_snprintf( pchOutfile, nMaxPath, "%s%s.png", + pchOutputFolder, + fname + ); + } +} +//----------------------------------------------------------------------------- +bool SaveImageIconAsPng( CEconItemView *pItem, ITexture *pInputTexture, const char *pszFilePath ) +{ + bool bRet = false; + ITexture *pTexture = materials->FindTexture( "_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET ); + + if ( !pTexture ) + return bRet; + + // If this is 3 4, we're only generating the actual composite texture for SFM + ConVarRef r_texcomp_dump( "r_texcomp_dump" ); + if ( r_texcomp_dump.GetInt() == 4 ) + { + return true; + } + + if ( pTexture->GetImageFormat() == IMAGE_FORMAT_RGBA8888 || + pTexture->GetImageFormat() == IMAGE_FORMAT_ABGR8888 || + pTexture->GetImageFormat() == IMAGE_FORMAT_ARGB8888 || + pTexture->GetImageFormat() == IMAGE_FORMAT_BGRA8888 || + pTexture->GetImageFormat() == IMAGE_FORMAT_BGRX8888 ) + { + int width = Min( pInputTexture->GetActualWidth(), pTexture->GetActualWidth() ); + int height = Min( pInputTexture->GetActualHeight(), pTexture->GetActualHeight() ); + Rect_t SrcRect = { 0, 0, width, height }; + Rect_t DstRect = SrcRect; + + if ( ( width > 0 ) && ( height > 0 ) ) + { + void *pixelValue = malloc( width * height * sizeof( RGBA8888_t ) ); + + if ( pixelValue ) + { + CMatRenderContextPtr pRenderContext( materials ); + + pRenderContext->PushRenderTargetAndViewport( pTexture, 0, 0, width, height ); + pRenderContext->CopyTextureToRenderTargetEx( 0, pInputTexture, &SrcRect, &DstRect ); + + pRenderContext->ReadPixels( 0, 0, width, height, (unsigned char *)pixelValue, pInputTexture->GetImageFormat() ); + + CUtlBuffer outBuffer; + ImgUtl_WriteRGBAAsPNGToBuffer( reinterpret_cast<const unsigned char *>( pixelValue ), width, height, outBuffer ); + + FileHandle_t hFileOut = g_pFullFileSystem->Open( pszFilePath, "wb" ); + if ( hFileOut != FILESYSTEM_INVALID_HANDLE ) + { + Msg( "Saved.. %s\n", pszFilePath ); + g_pFullFileSystem->Write( outBuffer.Base(), outBuffer.TellPut(), hFileOut ); + g_pFullFileSystem->Close( hFileOut ); + bRet = true; + } + + // restore our previous state + pRenderContext->PopRenderTargetAndViewport(); + + free( pixelValue ); + } + } + } + return bRet; +} +//----------------------------------------------------------------------------- +extern ConVar tf_paint_kit_force_wear; + +ConVar tf_paint_kit_icon_generating_index( "tf_paint_kit_icon_generating_index", 0 ); + +void StartNextImage( CEconItemView *pItemData, CEmbeddedItemModelPanel *pItemModelPanel, const CUtlVector< item_definition_index_t > &vecItemDef, float flCurrentWear ) +{ + // Init the item + item_definition_index_t iDefIndex = vecItemDef[ tf_paint_kit_icon_generating_index.GetInt() ]; + pItemData->Init( iDefIndex, AE_PAINTKITWEAPON, AE_USE_SCRIPT_VALUE, true ); + pItemData->SetWeaponSkinBase( NULL ); + pItemData->SetWeaponSkinBaseCompositor( NULL ); + + bool bIsPaintkitItem = pItemData->GetCustomPainkKitDefinition() != NULL; + if ( bIsPaintkitItem ) + { + // Set up the wear + static CSchemaAttributeDefHandle pAttrDef_TextureWear( "set_item_texture_wear" ); + pItemData->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_TextureWear, flCurrentWear ); + + Msg( "Force Setting PaintKit Wear for Icon Generation : Wear Level %d", tf_paint_kit_force_wear.GetInt() ); + } + else + { + // force use_model_cache_icon + static CSchemaAttributeDefHandle pAttrDef_UseModelCacheIcon( "use_model_cache_icon" ); + uint32 unUseModelCacheIcon = 1; + pItemData->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_UseModelCacheIcon, unUseModelCacheIcon ); + } + + // Add festive attr if enabled + if ( tf_icon_festive.GetBool() == true ) + { + static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" ); + pItemData->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_IsFestivized, 1 ); + } + //int iWear = EconWear_ToIntCategory( flCurrentWear ); + + // Force set convar for wear + //tf_paint_kit_force_wear.SetValue( iWear ); + + // force image generation to use high res + pItemData->SetWeaponSkinUseHighRes( true ); + + pItemModelPanel->SetItem( pItemData ); + pItemModelPanel->InvalidateLayout( true, true ); +} + +extern ConVar tf_paint_kit_generating_icons; +void CHudMainMenuOverride::GenerateIconsThink() +{ + CEmbeddedItemModelPanel *pItemModelPanel = dynamic_cast<CEmbeddedItemModelPanel *>( FindChildByName( "icon_generator" ) ); + if ( !pItemModelPanel ) + return; + + ITexture *pIcon = pItemModelPanel->GetCachedGeneratedIcon(); + if ( !pIcon ) + return; + + Msg( "Saving.. [%d] - %s\n", m_pIconData->GetItemDefIndex(), m_pIconData->GetItemDefinition()->GetDefinitionName() ); + + // Generate filepath + char outfile[ MAX_PATH ]; + BuildPaintkitItemInventoryImagePath( outfile, sizeof(outfile), m_pIconData->GetItemDefinition(), tf_paint_kit_force_wear.GetInt(), m_bGeneratingLargeTestIcons ); + SaveImageIconAsPng( m_pIconData, pIcon, outfile ); + + // Generate next step + while ( true ) + { + int iIndex = tf_paint_kit_icon_generating_index.GetInt(); + iIndex++; + + // Increment Index, if index is greater, increment wear + if ( iIndex >= m_vecIconDefs.Count() ) + { + iIndex = 0; + if ( tf_paint_kit_force_wear.GetInt() == 5 ) + { + // reset bg color + SetBgColor( Color( 0, 0, 0, 0 ) ); + m_bGeneratingIcons = false; + Msg( "Icon Generating Completed" ); + tf_paint_kit_generating_icons.SetValue( 0 ); + tf_paint_kit_icon_generating_index.SetValue( 0 ); + return; + } + else + { + tf_paint_kit_force_wear.SetValue( tf_paint_kit_force_wear.GetInt() + 1 ); + } + } + + tf_paint_kit_icon_generating_index.SetValue( iIndex ); + + // only render non-paintkit item one time + if ( tf_paint_kit_force_wear.GetInt() > 0 ) + { + // search for the next paintkit item to draw + item_definition_index_t iDefIndex = m_vecIconDefs[ tf_paint_kit_icon_generating_index.GetInt() ]; + CEconItemView temp; + temp.Init( iDefIndex, AE_PAINTKITWEAPON, AE_USE_SCRIPT_VALUE, true ); + if ( temp.GetCustomPainkKitDefinition() ) + { + // draw this one + break; + } + } + } + + // start next item, other wise increment wear and go again + delete m_pIconData; + m_pIconData = new CEconItemView; + StartNextImage( m_pIconData, pItemModelPanel, m_vecIconDefs, 0.2f ); +} + +ConVar tf_icon_bgcolor_override( "tf_icon_bgcolor_override", "" ); +ConVar tf_icon_allow_all_items( "tf_icon_allow_all_items", "0" ); +void CHudMainMenuOverride::GenerateIcons( bool bLarge, int min /*= -1*/, int max /*= -1*/ ) +{ + CEmbeddedItemModelPanel *pItemModelPanel = dynamic_cast<CEmbeddedItemModelPanel *>( FindChildByName("icon_generator") ); + if ( !pItemModelPanel ) + return; + + const char *pszBGColor = tf_icon_bgcolor_override.GetString(); + if ( pszBGColor && *pszBGColor ) + { + color32 bgcolor; + UTIL_StringToColor32( &bgcolor, pszBGColor ); + SetBgColor( Color( bgcolor.r, bgcolor.g, bgcolor.b, 255 ) ); + } + + + const char *pchOutputFolder = bLarge ? "resource/econ/generated_icons/LargeTest/" : "scripts/items/unencrypted/icons/generated_paintkit_icons"; + g_pFullFileSystem->CreateDirHierarchy( pchOutputFolder, NULL ); + + pItemModelPanel->SetTall( 1024 ); + pItemModelPanel->SetWide( 1024 ); + //pItemModelPanel->SetZPos( 1000 ); + + pItemModelPanel->SetInventoryImageType( CEmbeddedItemModelPanel::IMAGETYPE_LARGE ); + pItemModelPanel->SetVisible( true ); + pItemModelPanel->m_bOfflineIconGeneration = true; + + int rtSize = bLarge ? 1024 : 512; + + // Create a larger render target + materials->OverrideRenderTargetAllocation( true ); + + materials->CreateNamedRenderTargetTextureEx2( + "offline_icon_generation", + rtSize, rtSize, + RT_SIZE_DEFAULT, + materials->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_SHARED, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + 0 ); + materials->OverrideRenderTargetAllocation( false ); + + // Populate list of item icons + m_vecIconDefs.RemoveAll(); + const CEconItemSchema::SortedItemDefinitionMap_t &mapItems = GetItemSchema()->GetSortedItemDefinitionMap(); + + int nPaintkitItems = 0; + int nNormalItems = 0; + + FOR_EACH_MAP( mapItems, idxItem ) + { + CEconItemDefinition *pItem = mapItems[idxItem]; + CTFItemDefinition *pTFDef = (CTFItemDefinition *)pItem; + item_definition_index_t iDefIndex = pTFDef->GetDefinitionIndex(); + + // skip numbers below min + if ( min != -1 && iDefIndex < min ) + continue; + + // skip numbers if above. do not 'break;' since mapItems may NOT be in defindex order + if ( max != -1 && iDefIndex > max ) + continue; + + if ( pTFDef->GetCustomPainkKitDefinition() ) + { + m_vecIconDefs.AddToTail( iDefIndex ); + + nPaintkitItems++; + } + else if ( tf_icon_allow_all_items.GetBool() ) + { + m_vecIconDefs.AddToTail( iDefIndex ); + + nNormalItems++; + } + } + + if ( !m_vecIconDefs.Count() ) + { + Msg( "Didn't find any valid itemdefs to generate icons for" ); + return; + } + + Msg( "Found %d Valid Item defs. %d normal items and %d paintkit items\n", m_vecIconDefs.Count(), nNormalItems, nPaintkitItems ); + + int nNumGeneratingIcons = nNormalItems + nPaintkitItems * 5; + Msg( "Generating Icons for %d Items and 5 Wear Levels for each paintkit items (%d icons total)\n", m_vecIconDefs.Count(), nNumGeneratingIcons ); + + + // Starting conditions + tf_paint_kit_force_wear.SetValue( 1 ); + tf_paint_kit_generating_icons.SetValue( 1 ); + + // Create a dummy item + m_pIconData = new CEconItemView; + StartNextImage( m_pIconData, pItemModelPanel, m_vecIconDefs, 0.2 ); + + m_bGeneratingIcons = true; + m_bGeneratingLargeTestIcons = bLarge; +} +#endif + + + + |