Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

637 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: baseclientstate.cpp: implementation of the CBaseClientState class.
  4. //
  5. //===========================================================================//
  6. #include "client_pch.h"
  7. #include "limits.h"
  8. #include "cl_pluginhelpers.h"
  9. #include <inetchannel.h>
  10. #include <vgui/ISurface.h>
  11. #include <vgui/IScheme.h>
  12. #include <vgui/ILocalize.h>
  13. #include <vgui/IVGui.h>
  14. #include <vgui/IPanel.h>
  15. #include <vgui_controls/Controls.h>
  16. #include <vgui_controls/Frame.h>
  17. #include <vgui_controls/EditablePanel.h>
  18. #include <vgui_controls/Button.h>
  19. #include <vgui_controls/RichText.h>
  20. #include <vgui_controls/Label.h>
  21. #include <vgui_controls/TextEntry.h>
  22. #include <vgui_controls/ImagePanel.h>
  23. #include <vgui_controls/AnimationController.h>
  24. #include "vgui_baseui_interface.h"
  25. #include "vgui_askconnectpanel.h"
  26. #include "cmd.h"
  27. #include "tier1/convar.h"
  28. #include "baseclientstate.h"
  29. extern CClientState cl;
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Displays the options menu
  32. //-----------------------------------------------------------------------------
  33. class CPluginMenu : public vgui::EditablePanel
  34. {
  35. private:
  36. DECLARE_CLASS_SIMPLE( CPluginMenu, vgui::EditablePanel );
  37. public:
  38. CPluginMenu( vgui::Panel *parent );
  39. virtual ~CPluginMenu();
  40. void Show( KeyValues *kv );
  41. void OnCommand(const char *command);
  42. };
  43. //-----------------------------------------------------------------------------
  44. // Purpose: Constructor
  45. //-----------------------------------------------------------------------------
  46. CPluginMenu::CPluginMenu( vgui::Panel *parent ) : EditablePanel(parent, "PluginMenu" )
  47. {
  48. LoadControlSettings("Resource/UI/PluginMenu.res");
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Destructor
  52. //-----------------------------------------------------------------------------
  53. CPluginMenu::~CPluginMenu()
  54. {
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose: Show the options menu after using key values to configure it
  58. //-----------------------------------------------------------------------------
  59. void CPluginMenu::Show( KeyValues *kv )
  60. {
  61. vgui::Label *control = dynamic_cast<vgui::Label *>(FindChildByName("Text"));
  62. if (control)
  63. {
  64. control->SetText( kv->GetWString( "msg" ) );
  65. }
  66. int i = 0;
  67. // hide all the buttons
  68. for ( i = 0; i < GetChildCount(); i++ )
  69. {
  70. vgui::Button *button = dynamic_cast<vgui::Button *>(GetChild(i));
  71. if ( button )
  72. {
  73. button->SetVisible( false );
  74. }
  75. }
  76. i = 1;
  77. // now work out what buttons to display
  78. for ( KeyValues *pCur=kv->GetFirstTrueSubKey(); pCur; pCur=pCur->GetNextTrueSubKey(), i++ )
  79. {
  80. char controlName[64];
  81. Q_snprintf( controlName, sizeof(controlName), "option%i", i );
  82. vgui::Button *button = dynamic_cast<vgui::Button *>(FindChildByName(controlName,true));
  83. Assert( button );
  84. if ( button )
  85. {
  86. button->SetText( pCur->GetWString( "msg" ));
  87. button->SetCommand( pCur->GetString( "command" ));
  88. button->SetVisible( true );
  89. }
  90. }
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: when a button is pressed send that command back to the engine
  94. //-----------------------------------------------------------------------------
  95. void CPluginMenu::OnCommand( const char *command )
  96. {
  97. Cbuf_AddText( command );
  98. Cbuf_AddText( "\n" );
  99. CallParentFunction( new KeyValues( "Command", "command", "close" ) );
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose: Displays the gameui portion of plugin menus
  103. //-----------------------------------------------------------------------------
  104. class CPluginGameUIDialog : public vgui::Frame
  105. {
  106. private:
  107. DECLARE_CLASS_SIMPLE( CPluginGameUIDialog, vgui::Frame );
  108. public:
  109. CPluginGameUIDialog();
  110. virtual ~CPluginGameUIDialog();
  111. virtual void Show( DIALOG_TYPE type, KeyValues *kv );
  112. protected:
  113. void OnCommand( const char *cmd );
  114. private:
  115. CPluginMenu *m_Menu;
  116. vgui::RichText *m_RichText;
  117. vgui::Label *m_Message;
  118. vgui::TextEntry *m_Entry;
  119. vgui::Label *m_EntryLabel;
  120. vgui::Button *m_CloseButton;
  121. char m_szEntryCommand[ 255 ];
  122. };
  123. //-----------------------------------------------------------------------------
  124. // Purpose: constructor
  125. //-----------------------------------------------------------------------------
  126. CPluginGameUIDialog::CPluginGameUIDialog() : vgui::Frame( NULL, "Plugins" )
  127. {
  128. // initialize dialog
  129. SetTitle( "Plugins", true );
  130. SetAlpha( 255 );
  131. SetScheme( "Tracker" );
  132. m_szEntryCommand[ 0 ] = 0;
  133. m_Menu = new CPluginMenu( this );
  134. m_RichText = new vgui::RichText( this, "Rich" );
  135. m_Message = new vgui::Label( this, "Label", "" );
  136. m_Entry = new vgui::TextEntry( this, "Entry" );
  137. m_EntryLabel = new vgui::Label( this, "EntryLabel", "" );
  138. m_CloseButton = new vgui::Button( this, "Close", "");
  139. LoadControlSettings("Resource/UI/Plugin.res");
  140. InvalidateLayout();
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose: destructor
  144. //-----------------------------------------------------------------------------
  145. CPluginGameUIDialog::~CPluginGameUIDialog()
  146. {
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: called when the close button is pressed
  150. //-----------------------------------------------------------------------------
  151. void CPluginGameUIDialog::OnCommand( const char *cmd )
  152. {
  153. if ( !Q_stricmp( cmd, "close" ) )
  154. {
  155. if ( Q_strlen(m_szEntryCommand) > 0 )
  156. {
  157. // Check that we can add the two execution markers
  158. if ( !Cbuf_HasRoomForExecutionMarkers( 2 ) )
  159. {
  160. AssertMsg( false, "CPluginGameUIDialog::OnCommand called but there is no room for the execution markers. Ignoring command." );
  161. return;
  162. }
  163. char userCMD[ 512 ];
  164. char entryText[ 255 ];
  165. m_Entry->GetText( entryText, sizeof(entryText) );
  166. Q_snprintf( userCMD, sizeof(userCMD), "%s %s\n", m_szEntryCommand, entryText );
  167. // Only let them run commands marked with FCVAR_CLIENTCMD_CAN_EXECUTE.
  168. Cbuf_AddTextWithMarkers( eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE, userCMD, eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE );
  169. }
  170. Close();
  171. g_PluginManager->OnPanelClosed();
  172. }
  173. else
  174. {
  175. BaseClass::OnCommand( cmd );
  176. }
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: shows a precanned style of message in the GameUI
  180. //-----------------------------------------------------------------------------
  181. void CPluginGameUIDialog::Show( DIALOG_TYPE type, KeyValues *kv )
  182. {
  183. m_Menu->SetVisible(false);
  184. m_RichText->SetVisible(false);
  185. m_Message->SetVisible(false);
  186. m_Entry->SetVisible(false);
  187. m_EntryLabel->SetVisible(false);
  188. m_szEntryCommand[ 0 ] = 0;
  189. SetTitle( kv->GetWString( "title" ), true );
  190. switch ( type )
  191. {
  192. case DIALOG_MENU: // a options menu
  193. m_Menu->Show( kv );
  194. m_Menu->SetVisible( true );
  195. break;
  196. case DIALOG_TEXT: // a richtext dialog
  197. m_RichText->SetText( kv->GetWString( "msg" ) );
  198. m_RichText->SetVisible( true );
  199. break;
  200. case DIALOG_MSG: // just a msg to the screen, don't display in the gameui
  201. SetVisible( false );
  202. return;
  203. break;
  204. case DIALOG_ENTRY:
  205. m_Entry->SetVisible( true );
  206. m_EntryLabel->SetVisible( true );
  207. m_EntryLabel->SetText( kv->GetWString( "msg" ) );
  208. Q_strncpy( m_szEntryCommand, kv->GetString( "command" ), sizeof(m_szEntryCommand) );
  209. m_CloseButton->SetText( "#GameUI_OK" );
  210. break;
  211. default:
  212. Msg( "Invalid menu type (%i)\n", type );
  213. break;
  214. }
  215. Activate();
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose: the individual message snippets
  219. //-----------------------------------------------------------------------------
  220. class CMessage : public vgui::Label
  221. {
  222. private:
  223. DECLARE_CLASS_SIMPLE( CMessage, vgui::Label );
  224. public:
  225. CMessage(vgui::Panel *parent, const char *panelName, const char *text);
  226. ~CMessage();
  227. bool HasExtraPanel() { return m_bHasExtraPanel; }
  228. protected:
  229. void ApplySchemeSettings( vgui::IScheme *pScheme );
  230. private:
  231. bool m_bHasExtraPanel;
  232. };
  233. CMessage::CMessage( vgui::Panel *parent, const char *panelName, const char *text ) : vgui::Label( parent, panelName, text )
  234. {
  235. m_bHasExtraPanel = false;
  236. }
  237. CMessage::~CMessage()
  238. {
  239. }
  240. void CMessage::ApplySchemeSettings( vgui::IScheme *pScheme )
  241. {
  242. vgui::HFont font = pScheme->GetFont( "PluginText", false );
  243. if ( font == vgui::INVALID_FONT )
  244. {
  245. font = pScheme->GetFont( "HudHintText", false );
  246. }
  247. SetFont(font);
  248. BaseClass::ApplySchemeSettings( pScheme );
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose: the hud plugin message panel
  252. //-----------------------------------------------------------------------------
  253. class CPluginHudMessage : public vgui::Frame
  254. {
  255. private:
  256. DECLARE_CLASS_SIMPLE( CPluginHudMessage, vgui::Frame );
  257. public:
  258. CPluginHudMessage( vgui::VPANEL parent );
  259. ~CPluginHudMessage();
  260. void ShowMessage( const wchar_t *message, int time, Color clr, bool bHasExtraPanel );
  261. void StartHiding();
  262. void Hide();
  263. protected:
  264. void ApplySchemeSettings( vgui::IScheme *pScheme );
  265. void OnTick();
  266. void OnSizeChanged( int newWide, int newTall );
  267. private:
  268. enum { MESSAGE_X_INSET = 40, MAX_TEXT_LEN_PIXELS = 400 };
  269. CMessage *m_Message;
  270. vgui::ImagePanel *m_pExtraPanelIcon;
  271. bool m_bHidingControl;
  272. int m_iTargetH, m_iTargetW;
  273. Color m_fgColor;
  274. vgui::AnimationController *m_pAnimationController;
  275. };
  276. //-----------------------------------------------------------------------------
  277. // Purpose: constructor
  278. //-----------------------------------------------------------------------------
  279. CPluginHudMessage::CPluginHudMessage( vgui::VPANEL parent ) : vgui::Frame( NULL, "PluginHudMessage" )
  280. {
  281. SetParent( parent );
  282. SetVisible( false );
  283. SetAlpha( 255 );
  284. SetMinimumSize( 10 , 10 );
  285. SetScheme( "ClientScheme" );
  286. SetMoveable(false);
  287. SetSizeable(false);
  288. SetKeyBoardInputEnabled( false );
  289. SetMouseInputEnabled( false );
  290. SetTitleBarVisible( false );
  291. m_pExtraPanelIcon = new vgui::ImagePanel( this, "ExtraPanelIcon" );
  292. m_pExtraPanelIcon->SetVisible( false );
  293. m_Message = new CMessage( this, "Msg", "");
  294. m_Message->SetVisible( false );
  295. m_pAnimationController = new vgui::AnimationController( NULL );
  296. m_pAnimationController->SetParent( parent );
  297. m_pAnimationController->SetScriptFile( parent, "scripts/plugin_animations.txt" );
  298. m_pAnimationController->SetProportional( false );
  299. vgui::ivgui()->AddTickSignal(GetVPanel());
  300. LoadControlSettings("Resource/UI/PluginHud.res");
  301. InvalidateLayout();
  302. GetSize( m_iTargetW, m_iTargetH );
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: destructor
  306. //-----------------------------------------------------------------------------
  307. CPluginHudMessage::~CPluginHudMessage()
  308. {
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: set the see through color and rounded corners
  312. //-----------------------------------------------------------------------------
  313. void CPluginHudMessage::ApplySchemeSettings( vgui::IScheme *pScheme )
  314. {
  315. m_pExtraPanelIcon->SetImage( vgui::scheme()->GetImage( "plugin/message_waiting", true ) );
  316. BaseClass::ApplySchemeSettings( pScheme );
  317. SetBgColor( pScheme->GetColor( "Plugins.BgColor", pScheme->GetColor( "TransparentBlack", Color( 0, 0, 0, 192 ))) );
  318. SetPaintBackgroundType( 2 );
  319. m_Message->SetFgColor( m_fgColor );
  320. m_pExtraPanelIcon->SetVisible( !m_bHidingControl );
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: run the anim controller and hide the message label if the anim var says to
  324. //-----------------------------------------------------------------------------
  325. void CPluginHudMessage::OnTick()
  326. {
  327. m_pAnimationController->UpdateAnimations( Sys_FloatTime() );
  328. BaseClass::OnTick();
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose: get the label size to track
  332. //-----------------------------------------------------------------------------
  333. void CPluginHudMessage::OnSizeChanged( int newWide, int newTall )
  334. {
  335. BaseClass::OnSizeChanged( newWide, newTall );
  336. int w, h;
  337. GetSize( w, h );
  338. m_Message->SetBounds( MESSAGE_X_INSET, 5, w - MESSAGE_X_INSET - 10, h - 10 );
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose: start the shrinking anim for the control if it should be showed, the hide anim otherwsise
  342. //-----------------------------------------------------------------------------
  343. void CPluginHudMessage::StartHiding()
  344. {
  345. if ( m_pExtraPanelIcon->IsVisible() )
  346. {
  347. m_pAnimationController->StartAnimationSequence( "PluginMessageSmall" );
  348. }
  349. else
  350. {
  351. Hide();
  352. }
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose: starts the hide anim for the control
  356. //-----------------------------------------------------------------------------
  357. void CPluginHudMessage::Hide()
  358. {
  359. m_pAnimationController->StartAnimationSequence( "PluginMessageHide" );
  360. m_pExtraPanelIcon->SetVisible( false );
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: shows a text message on the hud
  364. //-----------------------------------------------------------------------------
  365. void CPluginHudMessage::ShowMessage( const wchar_t *text, int time, Color clr, bool bHasExtraPanel )
  366. {
  367. m_Message->SetVisible( true );
  368. m_Message->SetBounds( MESSAGE_X_INSET, 5, m_iTargetW - MESSAGE_X_INSET - 10, m_iTargetH - 10 );
  369. m_Message->SetText( text );
  370. m_Message->SetFgColor( clr );
  371. m_fgColor = clr;
  372. m_bHidingControl = !bHasExtraPanel;
  373. if ( bHasExtraPanel )
  374. {
  375. m_pExtraPanelIcon->SetVisible( true );
  376. }
  377. m_pAnimationController->StartAnimationSequence( "PluginMessageShow" );
  378. SetVisible( true );
  379. InvalidateLayout();
  380. int textW, textH;
  381. m_Message->GetContentSize( textW, textH );
  382. textW = min( textW + MESSAGE_X_INSET + 10, (int)MAX_TEXT_LEN_PIXELS );
  383. SetSize( textW, m_iTargetH ); // the "small" animation event changes our size
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: constructor
  387. //-----------------------------------------------------------------------------
  388. CPluginUIManager::CPluginUIManager() : vgui::Panel( NULL, "PluginManager" )
  389. {
  390. m_iCurPriority = INT_MAX;
  391. m_iMessageDisplayUntil = 0;
  392. m_iHudDisplayUntil = 0;
  393. m_bShutdown = false;
  394. m_pGameUIDialog = new CPluginGameUIDialog();
  395. Assert( m_pGameUIDialog );
  396. m_pGameUIDialog->SetParent( EngineVGui()->GetPanel( PANEL_GAMEUIDLL ) );
  397. m_pHudMessage = new CPluginHudMessage(EngineVGui()->GetPanel( PANEL_CLIENTDLL ));
  398. Assert( m_pHudMessage );
  399. vgui::ivgui()->AddTickSignal(GetVPanel());
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose: destructor
  403. //-----------------------------------------------------------------------------
  404. CPluginUIManager::~CPluginUIManager()
  405. {
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: hides the two plugin dialogs at the appropriate times
  409. //-----------------------------------------------------------------------------
  410. void CPluginUIManager::OnTick()
  411. {
  412. if ( m_bShutdown )
  413. {
  414. return;
  415. }
  416. if ( m_iMessageDisplayUntil != 0 && !EngineVGui()->IsGameUIVisible() && m_iMessageDisplayUntil < Sys_FloatTime() ) // check the GameUI large message
  417. {
  418. m_pGameUIDialog->SetVisible( false );
  419. m_pHudMessage->Hide();
  420. m_iMessageDisplayUntil = 0;
  421. m_iCurPriority = INT_MAX;
  422. }
  423. if ( m_iHudDisplayUntil != 0 && m_iHudDisplayUntil < Sys_FloatTime() ) // check the hud panel
  424. {
  425. m_pHudMessage->StartHiding();
  426. m_iHudDisplayUntil = 0;
  427. }
  428. BaseClass::OnTick();
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose: shuts down the plugin UI
  432. //-----------------------------------------------------------------------------
  433. void CPluginUIManager::Shutdown()
  434. {
  435. vgui::ivgui()->RemoveTickSignal(GetVPanel());
  436. m_pHudMessage->Hide();
  437. m_pGameUIDialog->SetVisible( false );
  438. MarkForDeletion();
  439. m_bShutdown = true;
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: shows a particular ui type and queues their lifetime
  443. //-----------------------------------------------------------------------------
  444. void CPluginUIManager::Show( DIALOG_TYPE type, KeyValues *kv )
  445. {
  446. // Check for the special DIALOG_ASKCONNECT command.
  447. if ( type == DIALOG_ASKCONNECT )
  448. {
  449. // Don't allow this prompt on QuickPlay servers
  450. if ( cl.IsClientConnectionViaMatchMaking() )
  451. return;
  452. // Do the askconnect dialog.
  453. float flDuration = kv->GetFloat( "time", 4.0f );
  454. const char *pIP = kv->GetString( "title", NULL );
  455. if ( !pIP )
  456. {
  457. DevMsg( "Ignoring DIALOG_ASKCONNECT message. No IP specified." );
  458. return;
  459. }
  460. ShowAskConnectPanel( pIP, flDuration );
  461. return;
  462. }
  463. int level = kv->GetInt( "level", INT_MAX );
  464. if ( level < m_iCurPriority )
  465. {
  466. m_iCurPriority = level;
  467. }
  468. else
  469. {
  470. DevMsg( "Ignoring message %s, %i < %i\n", kv->GetName(), level, m_iCurPriority );
  471. return;
  472. }
  473. if ( type != DIALOG_MSG )
  474. {
  475. m_iMessageDisplayUntil = Sys_FloatTime() + min(max( kv->GetInt( "time", 10 ),10),200);
  476. }
  477. else
  478. {
  479. m_iMessageDisplayUntil = Sys_FloatTime() + 10;
  480. }
  481. m_iHudDisplayUntil = Sys_FloatTime() + 10; // hud messages only get 10 seconds
  482. m_pGameUIDialog->Show( type, kv );
  483. Color clr( 255, 255, 255, 255 );
  484. if ( !kv->IsEmpty( "color" ) )
  485. {
  486. clr = kv->GetColor( "color" );
  487. }
  488. m_pHudMessage->ShowMessage( kv->GetWString( "title" ), 10, clr, type != DIALOG_MSG );
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: called when the gameui panel is closed
  492. //-----------------------------------------------------------------------------
  493. void CPluginUIManager::OnPanelClosed()
  494. {
  495. m_iCurPriority = INT_MAX;
  496. m_iHudDisplayUntil = 0;
  497. m_iMessageDisplayUntil = 0;
  498. m_pGameUIDialog->SetVisible( false );
  499. m_pHudMessage->Hide();
  500. }
  501. void CPluginUIManager::GetHudMessagePosition( int &x, int &y, int &wide, int &tall )
  502. {
  503. if ( m_pHudMessage )
  504. {
  505. m_pHudMessage->GetBounds( x, y, wide, tall );
  506. }
  507. else
  508. {
  509. x = y = wide = tall = 0;
  510. }
  511. }
  512. CPluginUIManager *g_PluginManager = NULL;
  513. //=============================================================================
  514. //
  515. // external interfaces
  516. //
  517. //=============================================================================
  518. ConVar cl_showpluginmessages ( "cl_showpluginmessages", "1", FCVAR_ARCHIVE, "Allow plugins to display messages to you" );
  519. void PluginHelpers_Menu( SVC_Menu *msg )
  520. {
  521. if ( !msg->m_MenuKeyValues )
  522. {
  523. return;
  524. }
  525. if ( !cl_showpluginmessages.GetBool() )
  526. {
  527. return;
  528. }
  529. if ( !g_PluginManager )
  530. {
  531. g_PluginManager = new CPluginUIManager();
  532. }
  533. g_PluginManager->Show( msg->m_Type, msg->m_MenuKeyValues );
  534. }