//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: baseclientstate.cpp: implementation of the CBaseClientState class. // //===========================================================================// #include "client_pch.h" #include "limits.h" #include "cl_pluginhelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vgui_baseui_interface.h" #include "vgui_askconnectpanel.h" #include "cmd.h" #include "tier1/convar.h" #include "baseclientstate.h" extern CClientState cl; //----------------------------------------------------------------------------- // Purpose: Displays the options menu //----------------------------------------------------------------------------- class CPluginMenu : public vgui::EditablePanel { private: DECLARE_CLASS_SIMPLE( CPluginMenu, vgui::EditablePanel ); public: CPluginMenu( vgui::Panel *parent ); virtual ~CPluginMenu(); void Show( KeyValues *kv ); void OnCommand(const char *command); }; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CPluginMenu::CPluginMenu( vgui::Panel *parent ) : EditablePanel(parent, "PluginMenu" ) { LoadControlSettings("Resource/UI/PluginMenu.res"); } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CPluginMenu::~CPluginMenu() { } //----------------------------------------------------------------------------- // Purpose: Show the options menu after using key values to configure it //----------------------------------------------------------------------------- void CPluginMenu::Show( KeyValues *kv ) { vgui::Label *control = dynamic_cast(FindChildByName("Text")); if (control) { control->SetText( kv->GetWString( "msg" ) ); } int i = 0; // hide all the buttons for ( i = 0; i < GetChildCount(); i++ ) { vgui::Button *button = dynamic_cast(GetChild(i)); if ( button ) { button->SetVisible( false ); } } i = 1; // now work out what buttons to display for ( KeyValues *pCur=kv->GetFirstTrueSubKey(); pCur; pCur=pCur->GetNextTrueSubKey(), i++ ) { char controlName[64]; Q_snprintf( controlName, sizeof(controlName), "option%i", i ); vgui::Button *button = dynamic_cast(FindChildByName(controlName,true)); Assert( button ); if ( button ) { button->SetText( pCur->GetWString( "msg" )); button->SetCommand( pCur->GetString( "command" )); button->SetVisible( true ); } } } //----------------------------------------------------------------------------- // Purpose: when a button is pressed send that command back to the engine //----------------------------------------------------------------------------- void CPluginMenu::OnCommand( const char *command ) { Cbuf_AddText( command ); Cbuf_AddText( "\n" ); CallParentFunction( new KeyValues( "Command", "command", "close" ) ); } //----------------------------------------------------------------------------- // Purpose: Displays the gameui portion of plugin menus //----------------------------------------------------------------------------- class CPluginGameUIDialog : public vgui::Frame { private: DECLARE_CLASS_SIMPLE( CPluginGameUIDialog, vgui::Frame ); public: CPluginGameUIDialog(); virtual ~CPluginGameUIDialog(); virtual void Show( DIALOG_TYPE type, KeyValues *kv ); protected: void OnCommand( const char *cmd ); private: CPluginMenu *m_Menu; vgui::RichText *m_RichText; vgui::Label *m_Message; vgui::TextEntry *m_Entry; vgui::Label *m_EntryLabel; vgui::Button *m_CloseButton; char m_szEntryCommand[ 255 ]; }; //----------------------------------------------------------------------------- // Purpose: constructor //----------------------------------------------------------------------------- CPluginGameUIDialog::CPluginGameUIDialog() : vgui::Frame( NULL, "Plugins" ) { // initialize dialog SetTitle( "Plugins", true ); SetAlpha( 255 ); SetScheme( "Tracker" ); m_szEntryCommand[ 0 ] = 0; m_Menu = new CPluginMenu( this ); m_RichText = new vgui::RichText( this, "Rich" ); m_Message = new vgui::Label( this, "Label", "" ); m_Entry = new vgui::TextEntry( this, "Entry" ); m_EntryLabel = new vgui::Label( this, "EntryLabel", "" ); m_CloseButton = new vgui::Button( this, "Close", ""); LoadControlSettings("Resource/UI/Plugin.res"); InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: destructor //----------------------------------------------------------------------------- CPluginGameUIDialog::~CPluginGameUIDialog() { } //----------------------------------------------------------------------------- // Purpose: called when the close button is pressed //----------------------------------------------------------------------------- void CPluginGameUIDialog::OnCommand( const char *cmd ) { if ( !Q_stricmp( cmd, "close" ) ) { if ( Q_strlen(m_szEntryCommand) > 0 ) { // Check that we can add the two execution markers if ( !Cbuf_HasRoomForExecutionMarkers( 2 ) ) { AssertMsg( false, "CPluginGameUIDialog::OnCommand called but there is no room for the execution markers. Ignoring command." ); return; } char userCMD[ 512 ]; char entryText[ 255 ]; m_Entry->GetText( entryText, sizeof(entryText) ); Q_snprintf( userCMD, sizeof(userCMD), "%s %s\n", m_szEntryCommand, entryText ); // Only let them run commands marked with FCVAR_CLIENTCMD_CAN_EXECUTE. Cbuf_AddTextWithMarkers( eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE, userCMD, eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE ); } Close(); g_PluginManager->OnPanelClosed(); } else { BaseClass::OnCommand( cmd ); } } //----------------------------------------------------------------------------- // Purpose: shows a precanned style of message in the GameUI //----------------------------------------------------------------------------- void CPluginGameUIDialog::Show( DIALOG_TYPE type, KeyValues *kv ) { m_Menu->SetVisible(false); m_RichText->SetVisible(false); m_Message->SetVisible(false); m_Entry->SetVisible(false); m_EntryLabel->SetVisible(false); m_szEntryCommand[ 0 ] = 0; SetTitle( kv->GetWString( "title" ), true ); switch ( type ) { case DIALOG_MENU: // a options menu m_Menu->Show( kv ); m_Menu->SetVisible( true ); break; case DIALOG_TEXT: // a richtext dialog m_RichText->SetText( kv->GetWString( "msg" ) ); m_RichText->SetVisible( true ); break; case DIALOG_MSG: // just a msg to the screen, don't display in the gameui SetVisible( false ); return; break; case DIALOG_ENTRY: m_Entry->SetVisible( true ); m_EntryLabel->SetVisible( true ); m_EntryLabel->SetText( kv->GetWString( "msg" ) ); Q_strncpy( m_szEntryCommand, kv->GetString( "command" ), sizeof(m_szEntryCommand) ); m_CloseButton->SetText( "#GameUI_OK" ); break; default: Msg( "Invalid menu type (%i)\n", type ); break; } Activate(); } //----------------------------------------------------------------------------- // Purpose: the individual message snippets //----------------------------------------------------------------------------- class CMessage : public vgui::Label { private: DECLARE_CLASS_SIMPLE( CMessage, vgui::Label ); public: CMessage(vgui::Panel *parent, const char *panelName, const char *text); ~CMessage(); bool HasExtraPanel() { return m_bHasExtraPanel; } protected: void ApplySchemeSettings( vgui::IScheme *pScheme ); private: bool m_bHasExtraPanel; }; CMessage::CMessage( vgui::Panel *parent, const char *panelName, const char *text ) : vgui::Label( parent, panelName, text ) { m_bHasExtraPanel = false; } CMessage::~CMessage() { } void CMessage::ApplySchemeSettings( vgui::IScheme *pScheme ) { vgui::HFont font = pScheme->GetFont( "PluginText", false ); if ( font == vgui::INVALID_FONT ) { font = pScheme->GetFont( "HudHintText", false ); } SetFont(font); BaseClass::ApplySchemeSettings( pScheme ); } //----------------------------------------------------------------------------- // Purpose: the hud plugin message panel //----------------------------------------------------------------------------- class CPluginHudMessage : public vgui::Frame { private: DECLARE_CLASS_SIMPLE( CPluginHudMessage, vgui::Frame ); public: CPluginHudMessage( vgui::VPANEL parent ); ~CPluginHudMessage(); void ShowMessage( const wchar_t *message, int time, Color clr, bool bHasExtraPanel ); void StartHiding(); void Hide(); protected: void ApplySchemeSettings( vgui::IScheme *pScheme ); void OnTick(); void OnSizeChanged( int newWide, int newTall ); private: enum { MESSAGE_X_INSET = 40, MAX_TEXT_LEN_PIXELS = 400 }; CMessage *m_Message; vgui::ImagePanel *m_pExtraPanelIcon; bool m_bHidingControl; int m_iTargetH, m_iTargetW; Color m_fgColor; vgui::AnimationController *m_pAnimationController; }; //----------------------------------------------------------------------------- // Purpose: constructor //----------------------------------------------------------------------------- CPluginHudMessage::CPluginHudMessage( vgui::VPANEL parent ) : vgui::Frame( NULL, "PluginHudMessage" ) { SetParent( parent ); SetVisible( false ); SetAlpha( 255 ); SetMinimumSize( 10 , 10 ); SetScheme( "ClientScheme" ); SetMoveable(false); SetSizeable(false); SetKeyBoardInputEnabled( false ); SetMouseInputEnabled( false ); SetTitleBarVisible( false ); m_pExtraPanelIcon = new vgui::ImagePanel( this, "ExtraPanelIcon" ); m_pExtraPanelIcon->SetVisible( false ); m_Message = new CMessage( this, "Msg", ""); m_Message->SetVisible( false ); m_pAnimationController = new vgui::AnimationController( NULL ); m_pAnimationController->SetParent( parent ); m_pAnimationController->SetScriptFile( parent, "scripts/plugin_animations.txt" ); m_pAnimationController->SetProportional( false ); vgui::ivgui()->AddTickSignal(GetVPanel()); LoadControlSettings("Resource/UI/PluginHud.res"); InvalidateLayout(); GetSize( m_iTargetW, m_iTargetH ); } //----------------------------------------------------------------------------- // Purpose: destructor //----------------------------------------------------------------------------- CPluginHudMessage::~CPluginHudMessage() { } //----------------------------------------------------------------------------- // Purpose: set the see through color and rounded corners //----------------------------------------------------------------------------- void CPluginHudMessage::ApplySchemeSettings( vgui::IScheme *pScheme ) { m_pExtraPanelIcon->SetImage( vgui::scheme()->GetImage( "plugin/message_waiting", true ) ); BaseClass::ApplySchemeSettings( pScheme ); SetBgColor( pScheme->GetColor( "Plugins.BgColor", pScheme->GetColor( "TransparentBlack", Color( 0, 0, 0, 192 ))) ); SetPaintBackgroundType( 2 ); m_Message->SetFgColor( m_fgColor ); m_pExtraPanelIcon->SetVisible( !m_bHidingControl ); } //----------------------------------------------------------------------------- // Purpose: run the anim controller and hide the message label if the anim var says to //----------------------------------------------------------------------------- void CPluginHudMessage::OnTick() { m_pAnimationController->UpdateAnimations( Sys_FloatTime() ); BaseClass::OnTick(); } //----------------------------------------------------------------------------- // Purpose: get the label size to track //----------------------------------------------------------------------------- void CPluginHudMessage::OnSizeChanged( int newWide, int newTall ) { BaseClass::OnSizeChanged( newWide, newTall ); int w, h; GetSize( w, h ); m_Message->SetBounds( MESSAGE_X_INSET, 5, w - MESSAGE_X_INSET - 10, h - 10 ); } //----------------------------------------------------------------------------- // Purpose: start the shrinking anim for the control if it should be showed, the hide anim otherwsise //----------------------------------------------------------------------------- void CPluginHudMessage::StartHiding() { if ( m_pExtraPanelIcon->IsVisible() ) { m_pAnimationController->StartAnimationSequence( "PluginMessageSmall" ); } else { Hide(); } } //----------------------------------------------------------------------------- // Purpose: starts the hide anim for the control //----------------------------------------------------------------------------- void CPluginHudMessage::Hide() { m_pAnimationController->StartAnimationSequence( "PluginMessageHide" ); m_pExtraPanelIcon->SetVisible( false ); } //----------------------------------------------------------------------------- // Purpose: shows a text message on the hud //----------------------------------------------------------------------------- void CPluginHudMessage::ShowMessage( const wchar_t *text, int time, Color clr, bool bHasExtraPanel ) { m_Message->SetVisible( true ); m_Message->SetBounds( MESSAGE_X_INSET, 5, m_iTargetW - MESSAGE_X_INSET - 10, m_iTargetH - 10 ); m_Message->SetText( text ); m_Message->SetFgColor( clr ); m_fgColor = clr; m_bHidingControl = !bHasExtraPanel; if ( bHasExtraPanel ) { m_pExtraPanelIcon->SetVisible( true ); } m_pAnimationController->StartAnimationSequence( "PluginMessageShow" ); SetVisible( true ); InvalidateLayout(); int textW, textH; m_Message->GetContentSize( textW, textH ); textW = min( textW + MESSAGE_X_INSET + 10, (int)MAX_TEXT_LEN_PIXELS ); SetSize( textW, m_iTargetH ); // the "small" animation event changes our size } //----------------------------------------------------------------------------- // Purpose: constructor //----------------------------------------------------------------------------- CPluginUIManager::CPluginUIManager() : vgui::Panel( NULL, "PluginManager" ) { m_iCurPriority = INT_MAX; m_iMessageDisplayUntil = 0; m_iHudDisplayUntil = 0; m_bShutdown = false; m_pGameUIDialog = new CPluginGameUIDialog(); Assert( m_pGameUIDialog ); m_pGameUIDialog->SetParent( EngineVGui()->GetPanel( PANEL_GAMEUIDLL ) ); m_pHudMessage = new CPluginHudMessage(EngineVGui()->GetPanel( PANEL_CLIENTDLL )); Assert( m_pHudMessage ); vgui::ivgui()->AddTickSignal(GetVPanel()); } //----------------------------------------------------------------------------- // Purpose: destructor //----------------------------------------------------------------------------- CPluginUIManager::~CPluginUIManager() { } //----------------------------------------------------------------------------- // Purpose: hides the two plugin dialogs at the appropriate times //----------------------------------------------------------------------------- void CPluginUIManager::OnTick() { if ( m_bShutdown ) { return; } if ( m_iMessageDisplayUntil != 0 && !EngineVGui()->IsGameUIVisible() && m_iMessageDisplayUntil < Sys_FloatTime() ) // check the GameUI large message { m_pGameUIDialog->SetVisible( false ); m_pHudMessage->Hide(); m_iMessageDisplayUntil = 0; m_iCurPriority = INT_MAX; } if ( m_iHudDisplayUntil != 0 && m_iHudDisplayUntil < Sys_FloatTime() ) // check the hud panel { m_pHudMessage->StartHiding(); m_iHudDisplayUntil = 0; } BaseClass::OnTick(); } //----------------------------------------------------------------------------- // Purpose: shuts down the plugin UI //----------------------------------------------------------------------------- void CPluginUIManager::Shutdown() { vgui::ivgui()->RemoveTickSignal(GetVPanel()); m_pHudMessage->Hide(); m_pGameUIDialog->SetVisible( false ); MarkForDeletion(); m_bShutdown = true; } //----------------------------------------------------------------------------- // Purpose: shows a particular ui type and queues their lifetime //----------------------------------------------------------------------------- void CPluginUIManager::Show( DIALOG_TYPE type, KeyValues *kv ) { // Check for the special DIALOG_ASKCONNECT command. if ( type == DIALOG_ASKCONNECT ) { // Don't allow this prompt on QuickPlay servers if ( cl.IsClientConnectionViaMatchMaking() ) return; // Do the askconnect dialog. float flDuration = kv->GetFloat( "time", 4.0f ); const char *pIP = kv->GetString( "title", NULL ); if ( !pIP ) { DevMsg( "Ignoring DIALOG_ASKCONNECT message. No IP specified." ); return; } ShowAskConnectPanel( pIP, flDuration ); return; } int level = kv->GetInt( "level", INT_MAX ); if ( level < m_iCurPriority ) { m_iCurPriority = level; } else { DevMsg( "Ignoring message %s, %i < %i\n", kv->GetName(), level, m_iCurPriority ); return; } if ( type != DIALOG_MSG ) { m_iMessageDisplayUntil = Sys_FloatTime() + min(max( kv->GetInt( "time", 10 ),10),200); } else { m_iMessageDisplayUntil = Sys_FloatTime() + 10; } m_iHudDisplayUntil = Sys_FloatTime() + 10; // hud messages only get 10 seconds m_pGameUIDialog->Show( type, kv ); Color clr( 255, 255, 255, 255 ); if ( !kv->IsEmpty( "color" ) ) { clr = kv->GetColor( "color" ); } m_pHudMessage->ShowMessage( kv->GetWString( "title" ), 10, clr, type != DIALOG_MSG ); } //----------------------------------------------------------------------------- // Purpose: called when the gameui panel is closed //----------------------------------------------------------------------------- void CPluginUIManager::OnPanelClosed() { m_iCurPriority = INT_MAX; m_iHudDisplayUntil = 0; m_iMessageDisplayUntil = 0; m_pGameUIDialog->SetVisible( false ); m_pHudMessage->Hide(); } void CPluginUIManager::GetHudMessagePosition( int &x, int &y, int &wide, int &tall ) { if ( m_pHudMessage ) { m_pHudMessage->GetBounds( x, y, wide, tall ); } else { x = y = wide = tall = 0; } } CPluginUIManager *g_PluginManager = NULL; //============================================================================= // // external interfaces // //============================================================================= ConVar cl_showpluginmessages ( "cl_showpluginmessages", "1", FCVAR_ARCHIVE, "Allow plugins to display messages to you" ); void PluginHelpers_Menu( SVC_Menu *msg ) { if ( !msg->m_MenuKeyValues ) { return; } if ( !cl_showpluginmessages.GetBool() ) { return; } if ( !g_PluginManager ) { g_PluginManager = new CPluginUIManager(); } g_PluginManager->Show( msg->m_Type, msg->m_MenuKeyValues ); }