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.

1436 lines
37 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "econ_notifications.h"
  9. #include "hudelement.h"
  10. #include "iclientmode.h"
  11. #include "ienginevgui.h"
  12. #include "vgui_avatarimage.h"
  13. #include "vgui_controls/Controls.h"
  14. #include "vgui_controls/EditablePanel.h"
  15. #include "vgui_controls/TextImage.h"
  16. #include "vgui/ILocalize.h"
  17. #include "vgui/ISurface.h"
  18. #include "vgui/IVGui.h"
  19. #include "rtime.h"
  20. #include "econ_controls.h"
  21. #include "hud_basechat.h"
  22. #include "hud_vote.h"
  23. #include "inputsystem/iinputsystem.h"
  24. #include "iinput.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include <tier0/memdbgon.h>
  27. //-----------------------------------------------------------------------------
  28. ConVar cl_notifications_show_ingame( "cl_notifications_show_ingame", "1", FCVAR_ARCHIVE, "Whether notifications should show up in-game." );
  29. ConVar cl_notifications_max_num_visible( "cl_notifications_max_num_visible", "3", FCVAR_ARCHIVE, "How many notifications are visible in-game." );
  30. ConVar cl_notifications_move_time( "cl_notifications_move_time", "0.5", FCVAR_ARCHIVE, "How long it takes for a notification to move." );
  31. // notification queue holds all the notifications
  32. class CEconNotificationQueue
  33. {
  34. public:
  35. CEconNotificationQueue();
  36. ~CEconNotificationQueue();
  37. int AddNotification( CEconNotification *pNotification );
  38. void RemoveAllNotifications();
  39. void RemoveNotification( int iID );
  40. void RemoveNotification( CEconNotification *pNotification );
  41. void RemoveNotifications( NotificationFilterFunc func );
  42. int CountNotifications( NotificationFilterFunc func );
  43. void VisitNotifications( CEconNotificationVisitor &visitor );
  44. CEconNotification *GetNotification( int iID );
  45. CEconNotification *GetNotificationByIndex( int idx );
  46. void Update();
  47. bool HasItems() { return m_vecNotifications.Count() != 0; }
  48. const CUtlVector< CEconNotification *> &GetItems() { return m_vecNotifications; }
  49. private:
  50. int m_iIDGenerator;
  51. CUtlVector< CEconNotification *> m_vecNotifications;
  52. };
  53. static CEconNotificationQueue g_notificationQueue;
  54. CEconNotificationQueue::CEconNotificationQueue()
  55. : m_iIDGenerator(0)
  56. {
  57. }
  58. CEconNotificationQueue::~CEconNotificationQueue()
  59. {
  60. }
  61. int CEconNotificationQueue::AddNotification( CEconNotification *pNotification )
  62. {
  63. int iID = ++m_iIDGenerator;
  64. pNotification->m_iID = iID;
  65. m_vecNotifications.AddToTail( pNotification );
  66. return iID;
  67. }
  68. void CEconNotificationQueue::RemoveAllNotifications()
  69. {
  70. m_vecNotifications.PurgeAndDeleteElements();
  71. }
  72. void CEconNotificationQueue::RemoveNotification( int iID )
  73. {
  74. FOR_EACH_VEC( m_vecNotifications, i )
  75. {
  76. CEconNotification *pNotification = m_vecNotifications[i];
  77. if ( pNotification->GetID() == iID )
  78. {
  79. delete pNotification;
  80. m_vecNotifications.Remove( i );
  81. return;
  82. }
  83. }
  84. }
  85. void CEconNotificationQueue::RemoveNotification( CEconNotification *pNotification )
  86. {
  87. if ( pNotification )
  88. {
  89. RemoveNotification( pNotification->GetID() );
  90. }
  91. }
  92. void CEconNotificationQueue::RemoveNotifications( NotificationFilterFunc func )
  93. {
  94. for ( int i = 0; i < m_vecNotifications.Count(); ++i)
  95. {
  96. CEconNotification *pNotification = m_vecNotifications[i];
  97. if ( func( pNotification ) )
  98. {
  99. pNotification->MarkForDeletion();
  100. }
  101. }
  102. }
  103. int CEconNotificationQueue::CountNotifications( NotificationFilterFunc func )
  104. {
  105. int nResult = 0;
  106. for ( int i = 0; i < m_vecNotifications.Count(); ++i)
  107. {
  108. CEconNotification *pNotification = m_vecNotifications[i];
  109. if ( func( pNotification ) )
  110. {
  111. ++nResult;
  112. }
  113. }
  114. return nResult;
  115. }
  116. void CEconNotificationQueue::VisitNotifications( CEconNotificationVisitor &visitor )
  117. {
  118. for ( int i = 0; i < m_vecNotifications.Count(); ++i )
  119. {
  120. CEconNotification *pNotification = m_vecNotifications[i];
  121. visitor.Visit( *pNotification );
  122. }
  123. }
  124. CEconNotification *CEconNotificationQueue::GetNotification( int iID )
  125. {
  126. FOR_EACH_VEC( m_vecNotifications, i )
  127. {
  128. CEconNotification *pNotification = m_vecNotifications[i];
  129. if ( pNotification->GetID() == iID )
  130. {
  131. return pNotification;
  132. }
  133. }
  134. return NULL;
  135. }
  136. CEconNotification *CEconNotificationQueue::GetNotificationByIndex( int idx )
  137. {
  138. if ( idx < 0 || idx >= m_vecNotifications.Count() )
  139. {
  140. Assert( !"Invalid index passed to GetNotificationByIndex" );
  141. return NULL;
  142. }
  143. return m_vecNotifications[idx];
  144. }
  145. void CEconNotificationQueue::Update()
  146. {
  147. float flNowTime = engine->Time();
  148. for ( int i = 0; i < m_vecNotifications.Count(); )
  149. {
  150. CEconNotification *pNotification = m_vecNotifications[i];
  151. if ( pNotification->GetIsInUse() == false && pNotification->GetExpireTime() >= 0 && pNotification->GetExpireTime() < flNowTime )
  152. {
  153. pNotification->Expired();
  154. delete pNotification;
  155. m_vecNotifications.Remove( i );
  156. continue;
  157. }
  158. pNotification->UpdateTick();
  159. ++i;
  160. }
  161. }
  162. //-----------------------------------------------------------------------------
  163. static void ColorizeText( CEconNotification *pNotification, CExLabel *pControl, const wchar_t* wszText )
  164. {
  165. static wchar_t wszStrippedText[2048];
  166. if ( pControl == NULL )
  167. return;
  168. pControl->GetTextImage()->ClearColorChangeStream();
  169. if ( wszText == NULL )
  170. {
  171. pControl->SetText( L"" );
  172. return;
  173. }
  174. Color newColor = pControl->GetFgColor();
  175. int endIdx = 0;
  176. int insertIdx = 0;
  177. bool bContinue = true;
  178. while ( bContinue )
  179. {
  180. bool bSetColor = false;
  181. switch ( wszText[endIdx] )
  182. {
  183. case 0:
  184. bContinue = false;
  185. break;
  186. case COLOR_NORMAL:
  187. case COLOR_USEOLDCOLORS:
  188. newColor = pControl->GetFgColor();
  189. bSetColor = true;
  190. break;
  191. case COLOR_PLAYERNAME:
  192. newColor = g_ColorYellow;
  193. bSetColor = true;
  194. break;
  195. case COLOR_LOCATION:
  196. newColor = g_ColorDarkGreen;
  197. bSetColor = true;
  198. break;
  199. case COLOR_ACHIEVEMENT:
  200. {
  201. vgui::IScheme *pSourceScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "SourceScheme" ) );
  202. if ( pSourceScheme )
  203. {
  204. newColor = pSourceScheme->GetColor( "SteamLightGreen", pControl->GetBgColor() );
  205. }
  206. else
  207. {
  208. newColor = pControl->GetFgColor();
  209. }
  210. bSetColor = true;
  211. }
  212. break;
  213. case COLOR_CUSTOM:
  214. newColor = pControl->GetFgColor();
  215. KeyValues *pKeyValues = pNotification->GetKeyValues();
  216. if ( pKeyValues )
  217. {
  218. KeyValues* pColor = pKeyValues->FindKey( "custom_color" );
  219. if ( pColor )
  220. {
  221. newColor = pColor->GetColor();
  222. }
  223. }
  224. bSetColor = true;
  225. break;
  226. }
  227. if ( bSetColor )
  228. {
  229. pControl->GetTextImage()->AddColorChange( newColor, insertIdx );
  230. }
  231. else
  232. {
  233. wszStrippedText[insertIdx++] = wszText[endIdx];
  234. }
  235. ++endIdx;
  236. }
  237. pControl->SetText( wszStrippedText );
  238. }
  239. // generic "toast" for notifications
  240. class CGenericNotificationToast : public vgui::EditablePanel
  241. {
  242. DECLARE_CLASS_SIMPLE( CGenericNotificationToast, vgui::EditablePanel );
  243. public:
  244. CGenericNotificationToast( vgui::Panel *parent, int iNotificationID, bool bMainMenu );
  245. virtual ~CGenericNotificationToast();
  246. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  247. virtual void PerformLayout();
  248. protected:
  249. int m_iNotificationID;
  250. vgui::Panel *m_pAvatarBG;
  251. CAvatarImagePanel *m_pAvatar;
  252. bool m_bMainMenu;
  253. };
  254. CGenericNotificationToast::CGenericNotificationToast( vgui::Panel *parent, int iNotificationID, bool bMainMenu )
  255. : BaseClass( parent, "GenericNotificationToast" )
  256. , m_iNotificationID( iNotificationID )
  257. , m_pAvatar( NULL )
  258. , m_pAvatarBG( NULL )
  259. , m_bMainMenu( bMainMenu )
  260. {
  261. }
  262. CGenericNotificationToast::~CGenericNotificationToast()
  263. {
  264. }
  265. void CGenericNotificationToast::ApplySchemeSettings( vgui::IScheme *pScheme )
  266. {
  267. BaseClass::ApplySchemeSettings( pScheme );
  268. CEconNotification *pNotification = NotificationQueue_Get( m_iNotificationID );
  269. bool bHighPriority = pNotification && pNotification->BHighPriority();
  270. KeyValues *pConditions = NULL;
  271. if ( bHighPriority )
  272. {
  273. pConditions = new KeyValues( "conditions" );
  274. if ( bHighPriority )
  275. {
  276. KeyValues *pSubKey = new KeyValues( "if_high_priority" );
  277. pConditions->AddSubKey( pSubKey );
  278. }
  279. }
  280. if ( m_bMainMenu )
  281. {
  282. LoadControlSettings( "Resource/UI/Econ/GenericNotificationToastMainMenu.res", NULL, NULL, pConditions );
  283. }
  284. else
  285. {
  286. LoadControlSettings( "Resource/UI/Econ/GenericNotificationToast.res", NULL, NULL, pConditions );
  287. }
  288. if ( pConditions )
  289. {
  290. pConditions->deleteThis();
  291. }
  292. m_pAvatar = dynamic_cast< CAvatarImagePanel *>( FindChildByName("AvatarImage") );
  293. m_pAvatarBG = FindChildByName("AvatarBGPanel");
  294. if ( pNotification )
  295. {
  296. if ( pNotification->GetSteamID() == CSteamID() )
  297. {
  298. ColorizeText( pNotification, dynamic_cast< CExLabel* >( FindChildByName( "TextLabel" ) ), pNotification->GetText() );
  299. }
  300. else
  301. {
  302. ColorizeText( pNotification, dynamic_cast< CExLabel* >( FindChildByName( "AvatarTextLabel" ) ), pNotification->GetText() );
  303. }
  304. }
  305. }
  306. void CGenericNotificationToast::PerformLayout()
  307. {
  308. BaseClass::PerformLayout();
  309. CSteamID steamID;
  310. CEconNotification *pNotification = NotificationQueue_Get( m_iNotificationID );
  311. if ( pNotification )
  312. {
  313. steamID = pNotification->GetSteamID();
  314. }
  315. int iMinHeight = 0;
  316. if ( m_pAvatar )
  317. {
  318. if ( steamID != CSteamID() )
  319. {
  320. m_pAvatar->SetVisible( true );
  321. m_pAvatar->SetShouldDrawFriendIcon( false );
  322. m_pAvatar->SetPlayer( steamID, k_EAvatarSize64x64 );
  323. // make sure there's a minimum height
  324. // note we use iY to ensure that there's a buffer below too
  325. int iX, iY, iWidth, iHeight;
  326. m_pAvatar->GetBounds( iX, iY, iWidth, iHeight );
  327. iMinHeight = 2 * iY + iHeight;
  328. }
  329. else
  330. {
  331. m_pAvatar->SetVisible( false );
  332. m_pAvatar->ClearAvatar();
  333. }
  334. }
  335. if ( m_pAvatarBG )
  336. {
  337. m_pAvatarBG->SetVisible( m_pAvatar != NULL && m_pAvatar->IsVisible() );
  338. }
  339. const char *pTextLabelName = steamID != CSteamID() ? "AvatarTextLabel" : "TextLabel";
  340. CExLabel* pText = dynamic_cast< CExLabel *>( FindChildByName( pTextLabelName ) );
  341. if ( pText )
  342. {
  343. pText->SetVisible( true );
  344. pText->InvalidateLayout( true, false );
  345. int iWidth, iHeight;
  346. pText->GetSize( iWidth, iHeight );
  347. int iContentWidth, iContentHeight;
  348. pText->GetContentSize( iContentWidth, iContentHeight );
  349. pText->SetSize( iWidth, iContentHeight );
  350. int iDelta = iContentHeight - iHeight;
  351. // resize ourselves to fit
  352. int iContainerWidth, iContainerHeight;
  353. GetSize( iContainerWidth, iContainerHeight );
  354. SetSize( iContainerWidth, MAX( iContainerHeight + iDelta, iMinHeight ) );
  355. }
  356. }
  357. //-----------------------------------------------------------------------------
  358. class CNotificationToastControl : public vgui::EditablePanel
  359. {
  360. DECLARE_CLASS_SIMPLE( CNotificationToastControl, vgui::EditablePanel );
  361. public:
  362. CNotificationToastControl( vgui::EditablePanel *pParent, vgui::EditablePanel *pNotificationToast, int iNotificationID, bool bAddControls )
  363. : BaseClass( pParent, bAddControls ? "NotificationToastControl" : "NotificationToastContainer" )
  364. , m_pChild( pNotificationToast )
  365. , m_iNotificationID( iNotificationID )
  366. , m_bAddControls( bAddControls )
  367. , m_pTriggerButton( NULL )
  368. , m_pAcceptButton( NULL )
  369. , m_pDeclineButton( NULL )
  370. , m_iOverrideHeight( 0 )
  371. {
  372. m_pChild->SetParent( this );
  373. }
  374. virtual ~CNotificationToastControl()
  375. {
  376. }
  377. virtual void ApplySchemeSettings( vgui::IScheme *scheme )
  378. {
  379. CEconNotification *pNotification = g_notificationQueue.GetNotification( m_iNotificationID );
  380. // It is not entirely clear why pNotification is allowed to be NULL. Weapon switching
  381. // with pyro was causing crashes because of pNotification being NULL, and there were
  382. // previously existing checks, but it's not clear why.
  383. CEconNotification::EType eNotificationType = pNotification ? pNotification->NotificationType() \
  384. : CEconNotification::eType_Basic;
  385. bool bHighPriority = pNotification && pNotification->BHighPriority();
  386. bool bCanDelete = false;
  387. bool bCanAcceptDecline = false;
  388. bool bCanTrigger = false;
  389. bool bOneButton = false;
  390. switch ( eNotificationType )
  391. {
  392. case CEconNotification::eType_AcceptDecline:
  393. bCanAcceptDecline = true;
  394. break;
  395. case CEconNotification::eType_Basic:
  396. bCanDelete = true;
  397. bOneButton = true;
  398. break;
  399. case CEconNotification::eType_MustTrigger:
  400. bCanTrigger = true;
  401. bOneButton = true;
  402. break;
  403. case CEconNotification::eType_Trigger:
  404. bCanTrigger = true;
  405. bCanDelete = true;
  406. break;
  407. default:
  408. Assert( !"Unhandled enum type" );
  409. }
  410. KeyValues *pConditions = NULL;
  411. if ( bOneButton || bHighPriority )
  412. {
  413. pConditions = new KeyValues( "conditions" );
  414. if ( bOneButton )
  415. {
  416. KeyValues *pSubKey = new KeyValues( "if_one_button" );
  417. pConditions->AddSubKey( pSubKey );
  418. }
  419. if ( bHighPriority )
  420. {
  421. KeyValues *pSubKey = new KeyValues( "if_high_priority" );
  422. pConditions->AddSubKey( pSubKey );
  423. }
  424. }
  425. if ( m_bAddControls )
  426. {
  427. LoadControlSettings( "Resource/UI/Econ/NotificationToastControl.res", NULL, NULL, pConditions );
  428. }
  429. else
  430. {
  431. LoadControlSettings( "Resource/UI/Econ/NotificationToastContainer.res", NULL, NULL, pConditions );
  432. }
  433. if ( pConditions )
  434. {
  435. pConditions->deleteThis();
  436. }
  437. BaseClass::ApplySchemeSettings( scheme );
  438. GetSize( m_iOriginalWidth, m_iOriginalHeight );
  439. CExButton *pDeleteButton = dynamic_cast< CExButton *>( FindChildByName( "DeleteButton" ) );
  440. if ( pDeleteButton && bCanDelete )
  441. {
  442. pDeleteButton->AddActionSignalTarget( this );
  443. pDeleteButton->SetVisible ( pNotification != NULL );
  444. }
  445. if ( pNotification == NULL )
  446. return;
  447. if ( bCanAcceptDecline )
  448. {
  449. m_pAcceptButton = dynamic_cast< CExButton * >( FindChildByName( "AcceptButton" ) );
  450. m_pDeclineButton = dynamic_cast< CExButton * >( FindChildByName( "DeclineButton" ) );
  451. if ( m_pAcceptButton && m_pDeclineButton )
  452. {
  453. m_pAcceptButton->AddActionSignalTarget( this );
  454. m_pDeclineButton->AddActionSignalTarget( this );
  455. m_pAcceptButton->SetVisible( true );
  456. m_pDeclineButton->SetVisible( true );
  457. int posX, posY;
  458. m_pAcceptButton->GetPos( posX, posY );
  459. m_iButtonOffsetY = GetTall() - posY;
  460. }
  461. }
  462. if ( bCanTrigger )
  463. {
  464. m_pTriggerButton = dynamic_cast< CExButton *>( FindChildByName( "TriggerButton" ) );
  465. if ( m_pTriggerButton )
  466. {
  467. m_pTriggerButton->AddActionSignalTarget( this );
  468. m_pTriggerButton->SetVisible( true );
  469. int posX, posY;
  470. m_pTriggerButton->GetPos( posX, posY );
  471. m_iButtonOffsetY = GetTall() - posY;
  472. }
  473. }
  474. }
  475. virtual void PerformLayout()
  476. {
  477. BaseClass::PerformLayout();
  478. m_pChild->PerformLayout();
  479. int iWidth, iHeight;
  480. m_pChild->GetSize( iWidth, iHeight );
  481. // position control buttons
  482. if ( iHeight + m_iButtonOffsetY > m_iOriginalHeight )
  483. {
  484. if ( m_pAcceptButton && m_pDeclineButton )
  485. {
  486. int posX, posY;
  487. m_pAcceptButton->GetPos( posX, posY );
  488. // int newPosY = iHeight;
  489. // iHeight += m_iButtonOffsetY;
  490. // m_pAcceptButton->SetPos( posX, newPosY );
  491. // m_pDeclineButton->GetPos( posX, posY );
  492. // m_pDeclineButton->SetPos( posX, newPosY );
  493. }
  494. else if ( m_pTriggerButton )
  495. {
  496. int posX, posY;
  497. m_pTriggerButton->GetPos( posX, posY );
  498. // posY = iHeight;
  499. // iHeight += m_iButtonOffsetY;
  500. // m_pTriggerButton->SetPos( posX, posY );
  501. }
  502. }
  503. // position help label
  504. CEconNotification *pNotification = g_notificationQueue.GetNotification( m_iNotificationID );
  505. CExLabel *pHelpLabel = dynamic_cast< CExLabel* >( FindChildByName( "HelpTextLabel" ) );
  506. if ( pHelpLabel )
  507. {
  508. if ( pNotification )
  509. {
  510. const wchar_t *pszText = NULL;
  511. const char *pszTextKey = pNotification->GetUnlocalizedHelpText();
  512. if ( pszTextKey )
  513. {
  514. pszText = g_pVGuiLocalize->Find( pszTextKey );
  515. }
  516. if ( pszText )
  517. {
  518. wchar_t wzFinal[512] = L"";
  519. if ( ::input->IsSteamControllerActive() )
  520. {
  521. UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ), GAME_ACTION_SET_FPSCONTROLS );
  522. }
  523. else
  524. {
  525. UTIL_ReplaceKeyBindings( pszText, 0, wzFinal, sizeof( wzFinal ) );
  526. }
  527. ColorizeText( pNotification, pHelpLabel, wzFinal );
  528. }
  529. }
  530. pHelpLabel->InvalidateLayout( true, false );
  531. int posX, posY;
  532. pHelpLabel->GetPos( posX, posY );
  533. int iContentWidth, iContentHeight;
  534. pHelpLabel->GetContentSize( iContentWidth, iContentHeight );
  535. int iLabelWidth, iLabelHeight;
  536. pHelpLabel->GetSize( iLabelWidth, iLabelHeight );
  537. int iTextInsetX, iTextInsetY;
  538. pHelpLabel->GetTextInset( &iTextInsetX, &iTextInsetY );
  539. pHelpLabel->SetSize( iLabelWidth, iContentHeight + iTextInsetY );
  540. posY = iHeight;
  541. pHelpLabel->SetPos( posX, posY - iTextInsetY );
  542. iHeight += iContentHeight + iTextInsetY;
  543. }
  544. // resize ourselves to fit the child height wise
  545. int iContainerHeight = MAX( m_iOriginalHeight, iHeight );
  546. SetSize( m_iOriginalWidth, m_iOverrideHeight != 0 ? m_iOverrideHeight : iContainerHeight );
  547. }
  548. virtual void OnCommand( const char *command )
  549. {
  550. CEconNotification *pNotification = g_notificationQueue.GetNotification( m_iNotificationID );
  551. if ( pNotification != NULL )
  552. {
  553. if ( !Q_strncmp( command, "delete", ARRAYSIZE( "delete" ) ) )
  554. {
  555. pNotification->Deleted();
  556. g_notificationQueue.RemoveNotification( m_iNotificationID );
  557. return;
  558. }
  559. else if ( !Q_strncmp( command, "trigger", ARRAYSIZE( "trigger" ) ) )
  560. {
  561. pNotification->Trigger();
  562. }
  563. else if ( !Q_strncmp( command, "accept", ARRAYSIZE( "accept" ) ) )
  564. {
  565. pNotification->Accept();
  566. }
  567. else if ( !Q_strncmp( command, "decline", ARRAYSIZE( "decline" ) ) )
  568. {
  569. pNotification->Decline();
  570. }
  571. else
  572. {
  573. BaseClass::OnCommand( command );
  574. }
  575. }
  576. else
  577. {
  578. BaseClass::OnCommand( command );
  579. }
  580. }
  581. int GetOverrideHeight() const
  582. {
  583. return m_iOverrideHeight;
  584. }
  585. void SetOverrideHeight( int iHeight )
  586. {
  587. m_iOverrideHeight = iHeight;
  588. }
  589. private:
  590. vgui::EditablePanel* m_pChild;
  591. vgui::Panel* m_pTriggerButton;
  592. vgui::Panel* m_pAcceptButton;
  593. vgui::Panel* m_pDeclineButton;
  594. int m_iNotificationID;
  595. int m_iOriginalWidth;
  596. int m_iOriginalHeight;
  597. int m_iButtonOffsetY;
  598. int m_iOverrideHeight;
  599. bool m_bAddControls;
  600. };
  601. //-----------------------------------------------------------------------------
  602. struct NotificationUIInfo_t
  603. {
  604. CNotificationToastControl *m_pPanel;
  605. int m_iStartPosX;
  606. int m_iStartPosY;
  607. };
  608. // notification queue panel that is a HUD element
  609. // this is the visualization of the notifications while in game
  610. class CNotificationQueuePanel : public CHudElement, public vgui::EditablePanel
  611. {
  612. DECLARE_CLASS_SIMPLE( CNotificationQueuePanel, vgui::EditablePanel );
  613. public:
  614. CNotificationQueuePanel( const char *pElementName )
  615. : CHudElement( pElementName )
  616. , BaseClass( NULL, "NotificationQueuePanel" )
  617. , m_mapNotificationPanels( DefLessFunc(int) )
  618. , m_flInvalidateTime( 0.0f )
  619. , m_bInvalidated( false )
  620. {
  621. vgui::Panel *pParent = g_pClientMode->GetViewport();
  622. SetParent( pParent );
  623. SetHiddenBits( HIDEHUD_MISCSTATUS );
  624. }
  625. virtual ~CNotificationQueuePanel()
  626. {
  627. }
  628. virtual bool ShouldDraw( void )
  629. {
  630. if ( !CHudElement::ShouldDraw() )
  631. {
  632. return false;
  633. }
  634. if ( engine->IsPlayingDemo() )
  635. {
  636. return false;
  637. }
  638. if ( cl_notifications_show_ingame.GetInt() == 0 )
  639. {
  640. return false;
  641. }
  642. CHudVote *pHudVote = GET_HUDELEMENT( CHudVote );
  643. if ( pHudVote && pHudVote->IsVoteUIActive() )
  644. {
  645. return false;
  646. }
  647. return m_mapNotificationPanels.Count() > 0 || g_notificationQueue.HasItems();
  648. }
  649. virtual void PerformLayout( void )
  650. {
  651. BaseClass::PerformLayout();
  652. // Get filtered list of only the notifications that show some in-game content
  653. CUtlVector< CEconNotification *> notifications;
  654. GetNotifications( notifications );
  655. const float flMoveTime = cl_notifications_move_time.GetFloat();
  656. float lerpPercentage = flMoveTime > 0 ? clamp( ( flMoveTime - m_flInvalidateTime ) / flMoveTime, 0.0f, 1.0f ) : 1.0f;
  657. float flCurrTime = engine->Time();
  658. // move the notifications around
  659. const int kMaxVisibleNotifications = cl_notifications_max_num_visible.GetInt();
  660. int iPosY = MIN( notifications.Count() - 1, kMaxVisibleNotifications - 1 ) * m_iOverlapOffset_Y;
  661. int iPosX = MIN( notifications.Count() - 1, kMaxVisibleNotifications - 1 ) * m_iOverlapOffset_X;
  662. int zpos = 100;
  663. int iPreviousHeight = 0;
  664. for ( int i = 0; i < notifications.Count(); ++i )
  665. {
  666. CEconNotification *pNotification = notifications[i];
  667. int mapIdx = m_mapNotificationPanels.Find( pNotification->GetID() );
  668. if ( m_mapNotificationPanels.IsValidIndex( mapIdx ) == false || pNotification->GetInGameLifeTime() < flCurrTime )
  669. {
  670. continue;
  671. }
  672. NotificationUIInfo_t &info = m_mapNotificationPanels[mapIdx];
  673. CNotificationToastControl *pPanel = info.m_pPanel;
  674. if ( pPanel )
  675. {
  676. if ( pPanel->IsVisible() == false )
  677. {
  678. pPanel->SetVisible( true );
  679. }
  680. if ( i == 0 && pPanel->GetOverrideHeight() != 0 )
  681. {
  682. pPanel->SetOverrideHeight( 0 );
  683. pPanel->InvalidateLayout( true, false );
  684. }
  685. int iPanelX;
  686. int iPanelY;
  687. pPanel->GetPos( iPanelX, iPanelY );
  688. if ( m_bInvalidated )
  689. {
  690. info.m_iStartPosX = iPanelX;
  691. info.m_iStartPosY = iPanelY;
  692. }
  693. int iNewPosX = iPosX;
  694. int iNewPosY = iPosY;
  695. iNewPosX = Lerp( lerpPercentage, info.m_iStartPosX, iNewPosX );
  696. iNewPosY = Lerp( lerpPercentage, info.m_iStartPosY, iNewPosY );
  697. pPanel->SetPos( iNewPosX, iNewPosY );
  698. pPanel->SetZPos( --zpos );
  699. bool bStoppedMoving = iNewPosX == iPanelX && iNewPosY == iPosY;
  700. // only show panels that are more than we want visible if they are moving
  701. if ( i > kMaxVisibleNotifications - 1 )
  702. {
  703. if ( bStoppedMoving )
  704. {
  705. pPanel->SetVisible( false );
  706. }
  707. continue;
  708. }
  709. // don't poke out underneath if we are visible and stopped moving
  710. if ( i != 0 && pPanel->GetOverrideHeight() == 0 && bStoppedMoving )
  711. {
  712. pPanel->SetOverrideHeight( MIN( pPanel->GetTall(), iPreviousHeight ) );
  713. pPanel->InvalidateLayout( true, false );
  714. }
  715. iPreviousHeight = pPanel->GetTall();
  716. iPosY = MAX( 0, iPosY - m_iOverlapOffset_Y );
  717. iPosX = MAX( 0, iPosX - m_iOverlapOffset_X );
  718. }
  719. }
  720. m_bInvalidated = false;
  721. SetTall( ScreenHeight() - m_iOriginalY );
  722. }
  723. virtual void ApplySchemeSettings( vgui::IScheme *scheme )
  724. {
  725. LoadControlSettings( "Resource/UI/Econ/NotificationQueuePanel.res" );
  726. GetBounds( m_iOriginalX, m_iOriginalY, m_iOriginalWidth, m_iOriginalHeight );
  727. BaseClass::ApplySchemeSettings( scheme );
  728. }
  729. virtual void OnThink()
  730. {
  731. BaseClass::OnThink();
  732. if ( IsVisible() == false )
  733. {
  734. return;
  735. }
  736. // Get filtered list of only the notifications that show some in-game content
  737. CUtlVector< CEconNotification *> notifications;
  738. GetNotifications( notifications );
  739. float flCurrTime = engine->Time();
  740. // check to see if we have a panel for each notification
  741. int i = 0;
  742. for ( i = 0; i < notifications.Count(); ++i )
  743. {
  744. CEconNotification *pNotification = notifications[i];
  745. int mapIdx = m_mapNotificationPanels.Find( pNotification->GetID() );
  746. if ( m_mapNotificationPanels.IsValidIndex( mapIdx ) == false || pNotification->GetInGameLifeTime() < flCurrTime )
  747. {
  748. m_bInvalidated = true;
  749. // create the panel and add it to the UI
  750. // have it slide from the bottom
  751. CNotificationToastControl *pControl = NULL;
  752. int iPosX = 0, iPosY = 0;
  753. vgui::EditablePanel *pPanel = pNotification->CreateUIElement( false );
  754. if ( pPanel )
  755. {
  756. pControl = new CNotificationToastControl( this, pPanel, pNotification->GetID(), false );
  757. pControl->GetPos( iPosX, iPosY );
  758. pControl->SetPos( iPosX, ScreenHeight() );
  759. iPosY = ScreenHeight();
  760. }
  761. NotificationUIInfo_t info = { pControl, iPosX, iPosY };
  762. m_mapNotificationPanels.Insert( pNotification->GetID(), info );
  763. }
  764. }
  765. // now check to see if we have panels and there is no matching notification
  766. i = m_mapNotificationPanels.FirstInorder();
  767. while ( m_mapNotificationPanels.IsValidIndex( i ) )
  768. {
  769. int idx = i;
  770. i = m_mapNotificationPanels.NextInorder( i );
  771. int iID = m_mapNotificationPanels.Key( idx );
  772. CEconNotification *pNotification = g_notificationQueue.GetNotification( iID );
  773. if ( pNotification == NULL || pNotification->GetInGameLifeTime() < flCurrTime )
  774. {
  775. // fade here, cause we don't really want to re-layout
  776. NotificationUIInfo_t &info = m_mapNotificationPanels[idx];
  777. vgui::EditablePanel *pPanel = info.m_pPanel;
  778. if ( pPanel )
  779. {
  780. pPanel->MarkForDeletion();
  781. }
  782. m_mapNotificationPanels.RemoveAt( idx );
  783. m_bInvalidated = true;
  784. }
  785. }
  786. if ( m_bInvalidated )
  787. {
  788. m_flInvalidateTime = MAX( cl_notifications_move_time.GetFloat(), 0.0f );
  789. }
  790. if ( m_flInvalidateTime > 0 )
  791. {
  792. m_flInvalidateTime -= gpGlobals->frametime;
  793. InvalidateLayout( true, false );
  794. }
  795. }
  796. protected:
  797. typedef CUtlMap< int, NotificationUIInfo_t > tNotificationPanels;
  798. tNotificationPanels m_mapNotificationPanels;
  799. int m_iOriginalX;
  800. int m_iOriginalY;
  801. int m_iOriginalWidth;
  802. int m_iOriginalHeight;
  803. float m_flInvalidateTime;
  804. bool m_bInvalidated;
  805. CPanelAnimationVar( int, m_iVisibleBuffer, "buffer_between_visible", "5" );
  806. CPanelAnimationVar( int, m_iOverlapOffset_X, "overlap_offset_x", "10" );
  807. CPanelAnimationVar( int, m_iOverlapOffset_Y, "overlap_offset_y", "10" );
  808. void GetNotifications(CUtlVector< CEconNotification *> &notifications )
  809. {
  810. const CUtlVector< CEconNotification *> &allNotifications = g_notificationQueue.GetItems();
  811. for (int i = 0 ; i < allNotifications.Count() ; ++i )
  812. {
  813. CEconNotification *pNotification = allNotifications[i];
  814. if ( pNotification->BShowInGameElements() )
  815. {
  816. notifications.AddToTail( pNotification );
  817. }
  818. }
  819. }
  820. };
  821. DECLARE_HUDELEMENT( CNotificationQueuePanel );
  822. //-----------------------------------------------------------------------------
  823. CEconNotification::CEconNotification()
  824. : m_pText("")
  825. , m_pSoundFilename( NULL )
  826. , m_flExpireTime( engine->Time() + 10.0f )
  827. , m_pKeyValues( NULL )
  828. , m_bInUse( false )
  829. , m_steamID()
  830. {
  831. }
  832. CEconNotification::~CEconNotification()
  833. {
  834. if ( m_pKeyValues )
  835. {
  836. m_pKeyValues->deleteThis();
  837. }
  838. }
  839. void CEconNotification::SetText( const char *pText )
  840. {
  841. m_pText = pText;
  842. }
  843. void CEconNotification::AddStringToken( const char* pToken, const wchar_t* pValue )
  844. {
  845. if ( m_pKeyValues == NULL )
  846. {
  847. m_pKeyValues = new KeyValues( "CEconNotification" );
  848. }
  849. m_pKeyValues->SetWString( pToken, pValue );
  850. }
  851. void CEconNotification::SetKeyValues( KeyValues *pKeyValues )
  852. {
  853. if ( m_pKeyValues != NULL )
  854. {
  855. m_pKeyValues->deleteThis();
  856. }
  857. m_pKeyValues = pKeyValues->MakeCopy();
  858. }
  859. KeyValues *CEconNotification::GetKeyValues() const
  860. {
  861. return m_pKeyValues;
  862. }
  863. const wchar_t *CEconNotification::GetText()
  864. {
  865. g_pVGuiLocalize->ConstructString_safe( m_wszBuffer, m_pText, m_pKeyValues );
  866. return m_wszBuffer;
  867. }
  868. int CEconNotification::GetID() const
  869. {
  870. return m_iID;
  871. }
  872. void CEconNotification::SetLifetime( float flSeconds )
  873. {
  874. m_flExpireTime = engine->Time() + flSeconds;
  875. }
  876. float CEconNotification::GetExpireTime() const
  877. {
  878. return m_flExpireTime;
  879. }
  880. float CEconNotification::GetInGameLifeTime() const
  881. {
  882. return m_flExpireTime; // default's to passed in time unless otherwise set (for derived classes)
  883. }
  884. void CEconNotification::SetIsInUse( bool bInUse)
  885. {
  886. m_bInUse = bInUse;
  887. }
  888. bool CEconNotification::GetIsInUse() const
  889. {
  890. return m_bInUse;
  891. }
  892. void CEconNotification::SetSteamID( const CSteamID &steamID )
  893. {
  894. m_steamID = steamID;
  895. }
  896. const CSteamID &CEconNotification::GetSteamID() const
  897. {
  898. return m_steamID;
  899. }
  900. void CEconNotification::MarkForDeletion()
  901. {
  902. // to be deleted ASAP
  903. m_flExpireTime = 0.0f;
  904. }
  905. CEconNotification::EType CEconNotification::NotificationType()
  906. {
  907. return eType_Basic;
  908. }
  909. bool CEconNotification::BHighPriority()
  910. {
  911. return false;
  912. }
  913. void CEconNotification::Trigger()
  914. {
  915. }
  916. void CEconNotification::Accept()
  917. {
  918. }
  919. void CEconNotification::Decline()
  920. {
  921. }
  922. void CEconNotification::Deleted()
  923. {
  924. }
  925. void CEconNotification::Expired()
  926. {
  927. }
  928. vgui::EditablePanel *CEconNotification::CreateUIElement( bool bMainMenu ) const
  929. {
  930. CGenericNotificationToast *pToast = new CGenericNotificationToast( NULL, m_iID, bMainMenu );
  931. return pToast;
  932. }
  933. const char *CEconNotification::GetUnlocalizedHelpText()
  934. {
  935. switch ( NotificationType() )
  936. {
  937. case eType_AcceptDecline:
  938. return "#Notification_AcceptOrDecline_Help";
  939. case eType_MustTrigger:
  940. case eType_Trigger:
  941. return "#Notification_CanTrigger_Help";
  942. default:
  943. Assert( !"Unhandled enum value" );
  944. // ---v
  945. case eType_Basic:
  946. return "#Notification_Remove_Help";
  947. }
  948. }
  949. //-----------------------------------------------------------------------------
  950. class CMainMenuNotificationsControl : public vgui::EditablePanel
  951. {
  952. DECLARE_CLASS_SIMPLE( CMainMenuNotificationsControl, vgui::EditablePanel );
  953. public:
  954. CMainMenuNotificationsControl( vgui::EditablePanel *pParent, const char *pElementName )
  955. : BaseClass( pParent, pElementName )
  956. , m_mapNotificationPanels( DefLessFunc(int) )
  957. , m_iNumItems( 0 )
  958. {
  959. vgui::ivgui()->AddTickSignal( GetVPanel(), 250 );
  960. }
  961. virtual ~CMainMenuNotificationsControl()
  962. {
  963. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  964. }
  965. virtual void PerformLayout( void )
  966. {
  967. BaseClass::PerformLayout();
  968. const CUtlVector< CEconNotification *> &notifications = g_notificationQueue.GetItems();
  969. // position the notifications around
  970. // grow down
  971. int iTotalHeight = 0;
  972. const int kBuffer = 5;
  973. for ( int i = 0; i < notifications.Count(); ++i )
  974. {
  975. CEconNotification *pNotification = notifications[i];
  976. int mapIdx = m_mapNotificationPanels.Find( pNotification->GetID() );
  977. if ( m_mapNotificationPanels.IsValidIndex( mapIdx ) == false )
  978. {
  979. continue;
  980. }
  981. NotificationUIInfo_t &info = m_mapNotificationPanels[mapIdx];
  982. vgui::EditablePanel *pPanel = info.m_pPanel;
  983. if ( pPanel )
  984. {
  985. int iPanelX;
  986. int iPanelY;
  987. int iWidth;
  988. int iHeight;
  989. pPanel->GetBounds( iPanelX, iPanelY, iWidth, iHeight );
  990. int iNewPosX = iPanelX;
  991. int iNewPosY = iTotalHeight;
  992. pPanel->SetPos( iNewPosX, iNewPosY );
  993. iTotalHeight += iHeight + kBuffer;
  994. }
  995. }
  996. int iWidth, iHeight;
  997. GetSize( iWidth, iHeight );
  998. SetSize( iWidth, iTotalHeight );
  999. if ( iTotalHeight != iHeight )
  1000. {
  1001. GetParent()->InvalidateLayout( false, false );
  1002. }
  1003. }
  1004. virtual void OnTick()
  1005. {
  1006. if ( m_iNumItems != g_notificationQueue.GetItems().Count() )
  1007. {
  1008. m_iNumItems = g_notificationQueue.GetItems().Count();
  1009. PostActionSignal( new KeyValues("Command", "command", "notifications_update" ) );
  1010. }
  1011. }
  1012. virtual void OnThink()
  1013. {
  1014. BaseClass::OnThink();
  1015. if ( IsVisible() == false )
  1016. {
  1017. return;
  1018. }
  1019. const CUtlVector< CEconNotification *> &notifications = g_notificationQueue.GetItems();
  1020. bool bInvalidated = false;
  1021. // check to see if we have a panel for each notification
  1022. int i = 0;
  1023. for ( i = 0; i < notifications.Count(); ++i )
  1024. {
  1025. CEconNotification *pNotification = notifications[i];
  1026. int mapIdx = m_mapNotificationPanels.Find( pNotification->GetID() );
  1027. if ( m_mapNotificationPanels.IsValidIndex( mapIdx ) == false )
  1028. {
  1029. bInvalidated = true;
  1030. CNotificationToastControl *pControl = NULL;
  1031. vgui::EditablePanel *pPanel = pNotification->CreateUIElement( true );
  1032. if ( pPanel )
  1033. {
  1034. pControl = new CNotificationToastControl( this, pPanel, pNotification->GetID(), true );
  1035. }
  1036. NotificationUIInfo_t info = { pControl, 0, 0 };
  1037. m_mapNotificationPanels.Insert( pNotification->GetID(), info );
  1038. }
  1039. }
  1040. // now check to see if we have panels and there is no matching notification
  1041. i = m_mapNotificationPanels.FirstInorder();
  1042. while ( m_mapNotificationPanels.IsValidIndex( i ) )
  1043. {
  1044. int idx = i;
  1045. i = m_mapNotificationPanels.NextInorder( i );
  1046. int iID = m_mapNotificationPanels.Key( idx );
  1047. if ( g_notificationQueue.GetNotification( iID ) == NULL )
  1048. {
  1049. NotificationUIInfo_t &info = m_mapNotificationPanels[idx];
  1050. vgui::EditablePanel *pPanel = info.m_pPanel;
  1051. if ( pPanel )
  1052. {
  1053. pPanel->MarkForDeletion();
  1054. }
  1055. m_mapNotificationPanels.RemoveAt( idx );
  1056. bInvalidated = true;
  1057. }
  1058. }
  1059. if ( bInvalidated )
  1060. {
  1061. InvalidateLayout( true, false );
  1062. }
  1063. }
  1064. protected:
  1065. typedef CUtlMap< int, NotificationUIInfo_t > tNotificationPanels;
  1066. tNotificationPanels m_mapNotificationPanels;
  1067. int m_iNumItems;
  1068. };
  1069. // Show in UI
  1070. class CNotificationsPresentPanel : public vgui::EditablePanel
  1071. {
  1072. DECLARE_CLASS_SIMPLE( CNotificationsPresentPanel, vgui::EditablePanel );
  1073. public:
  1074. CNotificationsPresentPanel( vgui::Panel *pParent, const char* pElementName ) : vgui::EditablePanel( pParent, "NotificationsPresentPanel" )
  1075. {
  1076. SetMouseInputEnabled( true );
  1077. }
  1078. virtual ~CNotificationsPresentPanel()
  1079. {
  1080. }
  1081. virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
  1082. {
  1083. BaseClass::ApplySchemeSettings( pScheme );
  1084. LoadControlSettings( "Resource/UI/Econ/NotificationsPresentPanel.res" );
  1085. for ( int i = 0; i < GetChildCount(); i++ )
  1086. {
  1087. vgui::Panel *pChild = GetChild( i );
  1088. pChild->SetMouseInputEnabled( false );
  1089. }
  1090. }
  1091. virtual void OnMousePressed(vgui::MouseCode code)
  1092. {
  1093. if ( code != MOUSE_LEFT )
  1094. return;
  1095. PostActionSignal( new KeyValues("Close") );
  1096. // audible feedback
  1097. const char *soundFilename = "ui/buttonclick.wav";
  1098. vgui::surface()->PlaySound( soundFilename );
  1099. }
  1100. };
  1101. DECLARE_BUILD_FACTORY( CNotificationsPresentPanel );
  1102. //-----------------------------------------------------------------------------
  1103. // External interface for the notification queue
  1104. int NotificationQueue_Add( CEconNotification *pNotification )
  1105. {
  1106. if ( !engine->IsInGame() || (cl_notifications_show_ingame.GetBool() && pNotification->BShowInGameElements()) )
  1107. {
  1108. vgui::surface()->PlaySound( pNotification->GetSoundFilename() );
  1109. }
  1110. return g_notificationQueue.AddNotification( pNotification );
  1111. }
  1112. CEconNotification *NotificationQueue_Get( int iID )
  1113. {
  1114. return g_notificationQueue.GetNotification( iID );
  1115. }
  1116. CEconNotification *NotificationQueue_GetByIndex( int idx )
  1117. {
  1118. return g_notificationQueue.GetNotificationByIndex( idx );
  1119. }
  1120. void NotificationQueue_RemoveAll()
  1121. {
  1122. g_notificationQueue.RemoveAllNotifications();
  1123. }
  1124. void NotificationQueue_Remove( int iID )
  1125. {
  1126. g_notificationQueue.RemoveNotification( iID );
  1127. }
  1128. void NotificationQueue_Remove( CEconNotification *pNotification )
  1129. {
  1130. g_notificationQueue.RemoveNotification( pNotification );
  1131. }
  1132. void NotificationQueue_Remove( NotificationFilterFunc func )
  1133. {
  1134. g_notificationQueue.RemoveNotifications( func );
  1135. }
  1136. int NotificationQueue_Count( NotificationFilterFunc func )
  1137. {
  1138. return g_notificationQueue.CountNotifications( func );
  1139. }
  1140. void NotificationQueue_Visit( CEconNotificationVisitor &visitor )
  1141. {
  1142. g_notificationQueue.VisitNotifications( visitor );
  1143. }
  1144. void NotificationQueue_Update()
  1145. {
  1146. g_notificationQueue.Update();
  1147. }
  1148. int NotificationQueue_GetNumNotifications()
  1149. {
  1150. return g_notificationQueue.GetItems().Count();
  1151. }
  1152. vgui::EditablePanel* NotificationQueue_CreateMainMenuUIElement( vgui::EditablePanel *pParent, const char *pElementName )
  1153. {
  1154. CMainMenuNotificationsControl *pControl = new CMainMenuNotificationsControl( pParent, pElementName );
  1155. pControl->AddActionSignalTarget( pParent );
  1156. return pControl;
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. CON_COMMAND( cl_trigger_first_notification, "Tries to accept/trigger the first notification" )
  1160. {
  1161. const CUtlVector< CEconNotification *> &notifications = g_notificationQueue.GetItems();
  1162. if ( notifications.Count() > 0 )
  1163. {
  1164. CEconNotification *pNotification = notifications[0];
  1165. switch ( pNotification->NotificationType() )
  1166. {
  1167. case CEconNotification::eType_AcceptDecline:
  1168. pNotification->Accept();
  1169. break;
  1170. case CEconNotification::eType_MustTrigger:
  1171. case CEconNotification::eType_Trigger:
  1172. pNotification->Trigger();
  1173. case CEconNotification::eType_Basic:
  1174. break;
  1175. default:
  1176. Assert( !"Unhandled enum value" );
  1177. }
  1178. }
  1179. }
  1180. CON_COMMAND( cl_decline_first_notification, "Tries to decline/remove the first notification" )
  1181. {
  1182. const CUtlVector< CEconNotification *> &notifications = g_notificationQueue.GetItems();
  1183. if ( notifications.Count() > 0 )
  1184. {
  1185. CEconNotification *pNotification = notifications[0];
  1186. switch ( pNotification->NotificationType() )
  1187. {
  1188. case CEconNotification::eType_AcceptDecline:
  1189. pNotification->Decline();
  1190. break;
  1191. case CEconNotification::eType_MustTrigger:
  1192. break; // YOU MUUUSSTTTTT
  1193. case CEconNotification::eType_Trigger:
  1194. case CEconNotification::eType_Basic:
  1195. pNotification->Deleted();
  1196. pNotification->MarkForDeletion();
  1197. break;
  1198. default:
  1199. Assert( !"Unhandled enum value" );
  1200. }
  1201. }
  1202. }
  1203. #ifdef _DEBUG
  1204. #include "confirm_dialog.h"
  1205. class CTFTestNotification : public CEconNotification
  1206. {
  1207. public:
  1208. CTFTestNotification( const char* pText, EType eType )
  1209. : CEconNotification()
  1210. , m_pText( pText )
  1211. , m_eType( eType )
  1212. {
  1213. }
  1214. virtual EType NotificationType() OVERRIDE { return m_eType; }
  1215. virtual void Trigger() OVERRIDE
  1216. {
  1217. ShowMessageBox( "", m_pText, "#GameUI_OK" );
  1218. MarkForDeletion();
  1219. }
  1220. virtual void Accept() OVERRIDE
  1221. {
  1222. ShowMessageBox( "Accept", m_pText, "#GameUI_OK" );
  1223. MarkForDeletion();
  1224. }
  1225. virtual void Decline() OVERRIDE
  1226. {
  1227. ShowMessageBox( "Decline", m_pText, "#GameUI_OK" );
  1228. MarkForDeletion();
  1229. }
  1230. private:
  1231. const char *m_pText;
  1232. EType m_eType;
  1233. };
  1234. CON_COMMAND( cl_add_notification, "Adds a notification" )
  1235. {
  1236. if ( args.ArgC() >= 2 )
  1237. {
  1238. CEconNotification::EType eType = CEconNotification::eType_Basic;
  1239. if ( args.ArgC() >= 5 )
  1240. {
  1241. eType = (CEconNotification::EType)atoi( args[4] );
  1242. }
  1243. CEconNotification *pNotification = new CTFTestNotification( args[1], eType );
  1244. pNotification->SetText( args[1] );
  1245. if ( args.ArgC() >= 3 )
  1246. {
  1247. int iLifetime = atoi( args[2] );
  1248. pNotification->SetLifetime( iLifetime );
  1249. }
  1250. if ( args.ArgC() >= 4 )
  1251. {
  1252. if ( steamapicontext && steamapicontext->SteamUser() )
  1253. {
  1254. pNotification->SetSteamID( steamapicontext->SteamUser()->GetSteamID() );
  1255. }
  1256. }
  1257. int id = NotificationQueue_Add( pNotification );
  1258. Msg( "Added notification %d\n", id);
  1259. }
  1260. }
  1261. #endif