summaryrefslogtreecommitdiff
path: root/game/client/tf/tf_hud_mainmenuoverride.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/tf_hud_mainmenuoverride.cpp')
-rw-r--r--game/client/tf/tf_hud_mainmenuoverride.cpp3427
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
+
+
+
+