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.

580 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "hud.h"
  9. #include "hud_macros.h"
  10. #include "c_tf_player.h"
  11. #include "iclientmode.h"
  12. #include "ienginevgui.h"
  13. #include <vgui/ILocalize.h>
  14. #include <vgui/ISurface.h>
  15. #include <vgui/IVGui.h>
  16. #include <vgui_controls/EditablePanel.h>
  17. #include <vgui_controls/Label.h>
  18. #include "tf_shareddefs.h"
  19. #include "tf_hud_notification_panel.h"
  20. #include "tf_hud_freezepanel.h"
  21. #include <filesystem.h>
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. using namespace vgui;
  25. ConVar tf_hud_notification_duration( "tf_hud_notification_duration", "3.0", 0, "How long to display hud notification panels before fading them" );
  26. ConVar tf_hud_notification_show_count_kart_controls( "tf_hud_notification_show_count_kart_controls", "0", FCVAR_ARCHIVE );
  27. ConVar tf_hud_notification_show_count_ghost_controls( "tf_hud_notification_show_count_ghost_controls", "0", FCVAR_ARCHIVE );
  28. ConVar tf_hud_notification_show_count_ghost_controls_no_respawn( "tf_hud_notification_show_count_ghost_controls_no_respawn", "0", FCVAR_ARCHIVE );
  29. DECLARE_HUDELEMENT( CHudNotificationPanel );
  30. DECLARE_HUD_MESSAGE( CHudNotificationPanel, HudNotify );
  31. DECLARE_HUD_MESSAGE( CHudNotificationPanel, HudNotifyCustom );
  32. //-----------------------------------------------------------------------------
  33. // Purpose:
  34. //-----------------------------------------------------------------------------
  35. CHudNotificationPanel::CHudNotificationPanel( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "NotificationPanel" )
  36. {
  37. Panel *pParent = g_pClientMode->GetViewport();
  38. SetParent( pParent );
  39. SetHiddenBits( HIDEHUD_MISCSTATUS );
  40. vgui::ivgui()->AddTickSignal( GetVPanel() );
  41. m_flFadeTime = 0;
  42. // listen for one version that just passes an int, for prebuilt notifications
  43. // and another that takes a res file to load
  44. m_pText = new Label( this, "Notification_Label", "" );
  45. m_pIcon = new CIconPanel( this, "Notification_Icon" );
  46. m_pBackground = new ImagePanel( this, "Notification_Background" );
  47. RegisterForRenderGroup( "mid" );
  48. RegisterForRenderGroup( "commentary" );
  49. LoadManifest();
  50. m_mapShowCounts.SetLessFunc( DefLessFunc( int ) ) ;
  51. m_mapShowCounts.Insert( HUD_NOTIFY_HOW_TO_CONTROL_GHOST, ShowCount_t( 3, 300.f, &tf_hud_notification_show_count_ghost_controls ) );
  52. m_mapShowCounts.Insert( HUD_NOTIFY_HOW_TO_CONTROL_KART, ShowCount_t( 3, 300.f, &tf_hud_notification_show_count_kart_controls ) );
  53. m_mapShowCounts.Insert( HUD_NOTIFY_HOW_TO_CONTROL_GHOST_NO_RESPAWN, ShowCount_t( 3, 300.f, &tf_hud_notification_show_count_ghost_controls_no_respawn ) );
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Purpose:
  57. //-----------------------------------------------------------------------------
  58. void CHudNotificationPanel::Init( void )
  59. {
  60. CHudElement::Init();
  61. HOOK_HUD_MESSAGE( CHudNotificationPanel, HudNotify );
  62. HOOK_HUD_MESSAGE( CHudNotificationPanel, HudNotifyCustom );
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. //-----------------------------------------------------------------------------
  67. void CHudNotificationPanel::ApplySchemeSettings( IScheme *pScheme )
  68. {
  69. // load control settings...
  70. LoadControlSettings( "resource/UI/notifications/base_notification.res" );
  71. BaseClass::ApplySchemeSettings( pScheme );
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. void CHudNotificationPanel::MsgFunc_HudNotify( bf_read &msg )
  77. {
  78. int iType = msg.ReadByte();
  79. bool bForceShow = msg.ReadByte();
  80. // Ignore notifications in minmode
  81. if ( !bForceShow )
  82. {
  83. ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
  84. if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
  85. return;
  86. }
  87. float flDuration = tf_hud_notification_duration.GetFloat();
  88. // Check if we're only supposed to show a limited number of times
  89. auto idx = m_mapShowCounts.Find( iType );
  90. if ( m_mapShowCounts.IsValidIndex( idx ) )
  91. {
  92. auto& showCount = m_mapShowCounts[ idx ];
  93. // Stop here if we've met our max show count, or it's too soon
  94. if ( showCount.m_pConVar->GetInt() >= showCount.m_nMaxShowCount
  95. || showCount.m_flNextAllowedTime > Plat_FloatTime() )
  96. {
  97. return;
  98. }
  99. // Increment show count
  100. showCount.m_pConVar->SetValue( showCount.m_pConVar->GetInt() + 1 );
  101. // Set next time we're allowed to show
  102. showCount.m_flNextAllowedTime = Plat_FloatTime() + showCount.m_flCooldown;
  103. }
  104. InvalidateLayout( true, true );
  105. LoadControlSettings( GetNotificationByType( iType, flDuration ) );
  106. // set up the fade time
  107. m_flFadeTime = gpGlobals->curtime + flDuration;
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose:
  111. //-----------------------------------------------------------------------------
  112. void CHudNotificationPanel::MsgFunc_HudNotifyCustom( bf_read &msg )
  113. {
  114. // Ignore notifications in minmode
  115. ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
  116. if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
  117. return;
  118. // Reload the base
  119. LoadControlSettings( "resource/UI/notifications/base_notification.res" );
  120. char szText[256];
  121. char szIcon[256];
  122. msg.ReadString( szText, sizeof(szText) );
  123. msg.ReadString( szIcon, sizeof(szIcon) );
  124. int iBackgroundTeam = msg.ReadByte();
  125. SetupNotifyCustom( szText, szIcon, iBackgroundTeam );
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose:
  129. //-----------------------------------------------------------------------------
  130. void CHudNotificationPanel::SetupNotifyCustom( const char *pszText, const char *pszIcon, int iBackgroundTeam )
  131. {
  132. // Reload the base
  133. LoadControlSettings( "resource/UI/notifications/base_notification.res" );
  134. m_pIcon->SetIcon( pszIcon );
  135. m_pText->SetText( pszText );
  136. if ( iBackgroundTeam == TF_TEAM_RED )
  137. {
  138. m_pBackground->SetImage( "../hud/score_panel_red_bg" );
  139. }
  140. else if ( iBackgroundTeam == TF_TEAM_BLUE )
  141. {
  142. m_pBackground->SetImage( "../hud/score_panel_blue_bg" );
  143. }
  144. else
  145. {
  146. m_pBackground->SetImage( "../hud/notification_black" );
  147. }
  148. // set up the fade time
  149. m_flFadeTime = gpGlobals->curtime + tf_hud_notification_duration.GetFloat();
  150. InvalidateLayout();
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, const char *pszIcon, int iBackgroundTeam )
  156. {
  157. // Reload the base
  158. LoadControlSettings( "resource/UI/notifications/base_notification.res" );
  159. m_pIcon->SetIcon( pszIcon );
  160. m_pText->SetText( pszText );
  161. if ( iBackgroundTeam == TF_TEAM_RED )
  162. {
  163. m_pBackground->SetImage( "../hud/score_panel_red_bg" );
  164. }
  165. else if ( iBackgroundTeam == TF_TEAM_BLUE )
  166. {
  167. m_pBackground->SetImage( "../hud/score_panel_blue_bg" );
  168. }
  169. else
  170. {
  171. m_pBackground->SetImage( "../hud/notification_black" );
  172. }
  173. // set up the fade time
  174. m_flFadeTime = gpGlobals->curtime + tf_hud_notification_duration.GetFloat();
  175. InvalidateLayout();
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose:
  179. //-----------------------------------------------------------------------------
  180. void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, HudNotification_t type, float overrideDuration )
  181. {
  182. float flDuration = tf_hud_notification_duration.GetFloat();
  183. // Reload the base
  184. LoadControlSettings( GetNotificationByType( type, flDuration ) );
  185. m_pText->SetText( pszText );
  186. // set up the fade time
  187. m_flFadeTime = gpGlobals->curtime + ( overrideDuration > 0.f ? overrideDuration : flDuration );
  188. InvalidateLayout();
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Purpose:
  192. //-----------------------------------------------------------------------------
  193. void CHudNotificationPanel::PerformLayout( void )
  194. {
  195. BaseClass::PerformLayout();
  196. // re-layout the panel manually so we fit the length of the string!
  197. // **** this is super yucky, i'm going to cry myself to sleep tonight ****
  198. int iTextWide, iTextTall;
  199. m_pText->GetContentSize( iTextWide, iTextTall );
  200. m_pText->SetSize( iTextWide, m_pText->GetTall() );
  201. float flTextWide = m_pText->GetWide();
  202. float flIconWide = m_pIcon->GetWide();
  203. float flSpacer = XRES( 5 );
  204. float flEndSpacer = XRES( 8 ) + XRES( 3 ) * ( flTextWide / 184 ); // total hackery
  205. float flTotalWidth = flEndSpacer + flIconWide + flSpacer + flTextWide + flEndSpacer;
  206. float flLeftSide = ( GetWide() - flTotalWidth ) * 0.5f;
  207. // resize and position background
  208. m_pBackground->SetPos( flLeftSide, 0 );
  209. m_pBackground->SetWide( flTotalWidth );
  210. // reposition icon
  211. int iIconXPos, iIconYPos;
  212. m_pIcon->GetPos( iIconXPos, iIconYPos );
  213. m_pIcon->SetPos( flLeftSide + flEndSpacer, iIconYPos );
  214. // reposition text
  215. int iTextXPos, iTextYPos;
  216. m_pText->GetPos( iTextXPos, iTextYPos );
  217. m_pText->SetPos( flLeftSide + flEndSpacer + flIconWide + flSpacer, iTextYPos );
  218. const unsigned short tempBufSize = 2048;
  219. wchar_t tempBufIn[tempBufSize];
  220. wchar_t tempBufOut[tempBufSize];
  221. m_pText->GetText( tempBufIn, tempBufSize );
  222. UTIL_ReplaceKeyBindings( tempBufIn, tempBufSize, tempBufOut, tempBufSize );
  223. m_pText->SetText( tempBufOut );
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. //-----------------------------------------------------------------------------
  228. bool CHudNotificationPanel::ShouldDraw( void )
  229. {
  230. // only if we have a valid message to draw
  231. if ( m_flFadeTime < gpGlobals->curtime )
  232. return false;
  233. if ( IsTakingAFreezecamScreenshot() )
  234. return false;
  235. return CHudElement::ShouldDraw();
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. //-----------------------------------------------------------------------------
  240. void CHudNotificationPanel::OnTick( void )
  241. {
  242. // set alpha based on time left
  243. // do this if we can fade the icons and the background
  244. /*
  245. float flLifeTime = m_flFadeTime - gpGlobals->curtime;
  246. if ( flLifeTime >= 1 )
  247. {
  248. SetAlpha( 255 );
  249. }
  250. else
  251. {
  252. SetAlpha( (float)( 255.0f * flLifeTime ) );
  253. }
  254. */
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. const char *CHudNotificationPanel::GetNotificationByType( int iType, float& flDuration )
  260. {
  261. bool bOnBlueTeam = false;
  262. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  263. if ( pLocalPlayer )
  264. {
  265. bOnBlueTeam = ( pLocalPlayer->GetTeamNumber() == TF_TEAM_BLUE );
  266. }
  267. const char *pszResult = "";
  268. switch ( iType )
  269. {
  270. case HUD_NOTIFY_YOUR_FLAG_TAKEN:
  271. if ( bOnBlueTeam )
  272. {
  273. pszResult = "resource/UI/notifications/notify_your_flag_taken_blue.res";
  274. }
  275. else
  276. {
  277. pszResult = "resource/UI/notifications/notify_your_flag_taken_red.res";
  278. }
  279. break;
  280. case HUD_NOTIFY_YOUR_FLAG_DROPPED:
  281. if ( bOnBlueTeam )
  282. {
  283. pszResult = "resource/UI/notifications/notify_your_flag_dropped_blue.res";
  284. }
  285. else
  286. {
  287. pszResult = "resource/UI/notifications/notify_your_flag_dropped_red.res";
  288. }
  289. break;
  290. case HUD_NOTIFY_YOUR_FLAG_RETURNED:
  291. if ( bOnBlueTeam )
  292. {
  293. pszResult = "resource/UI/notifications/notify_your_flag_returned_blue.res";
  294. }
  295. else
  296. {
  297. pszResult = "resource/UI/notifications/notify_your_flag_returned_red.res";
  298. }
  299. break;
  300. case HUD_NOTIFY_YOUR_FLAG_CAPTURED:
  301. if ( bOnBlueTeam )
  302. {
  303. pszResult = "resource/UI/notifications/notify_your_flag_captured_blue.res";
  304. }
  305. else
  306. {
  307. pszResult = "resource/UI/notifications/notify_your_flag_captured_red.res";
  308. }
  309. break;
  310. case HUD_NOTIFY_ENEMY_FLAG_TAKEN:
  311. if ( bOnBlueTeam )
  312. {
  313. pszResult = "resource/UI/notifications/notify_enemy_flag_taken_blue.res";
  314. }
  315. else
  316. {
  317. pszResult = "resource/UI/notifications/notify_enemy_flag_taken_red.res";
  318. }
  319. break;
  320. case HUD_NOTIFY_ENEMY_FLAG_DROPPED:
  321. if ( bOnBlueTeam )
  322. {
  323. pszResult = "resource/UI/notifications/notify_enemy_flag_dropped_blue.res";
  324. }
  325. else
  326. {
  327. pszResult = "resource/UI/notifications/notify_enemy_flag_dropped_red.res";
  328. }
  329. break;
  330. case HUD_NOTIFY_ENEMY_FLAG_RETURNED:
  331. if ( bOnBlueTeam )
  332. {
  333. pszResult = "resource/UI/notifications/notify_enemy_flag_returned_blue.res";
  334. }
  335. else
  336. {
  337. pszResult = "resource/UI/notifications/notify_enemy_flag_returned_red.res";
  338. }
  339. break;
  340. case HUD_NOTIFY_ENEMY_FLAG_CAPTURED:
  341. if ( bOnBlueTeam )
  342. {
  343. pszResult = "resource/UI/notifications/notify_enemy_flag_captured_blue.res";
  344. }
  345. else
  346. {
  347. pszResult = "resource/UI/notifications/notify_enemy_flag_captured_red.res";
  348. }
  349. break;
  350. case HUD_NOTIFY_TOUCHING_ENEMY_CTF_CAP:
  351. if ( bOnBlueTeam )
  352. {
  353. pszResult = "resource/UI/notifications/notify_touching_enemy_ctf_cap_blue.res";
  354. }
  355. else
  356. {
  357. pszResult = "resource/UI/notifications/notify_touching_enemy_ctf_cap_red.res";
  358. }
  359. break;
  360. case HUD_NOTIFY_NO_INVULN_WITH_FLAG:
  361. if ( bOnBlueTeam )
  362. {
  363. pszResult = "resource/UI/notifications/notify_no_invuln_with_flag_blue.res";
  364. }
  365. else
  366. {
  367. pszResult = "resource/UI/notifications/notify_no_invuln_with_flag_red.res";
  368. }
  369. break;
  370. case HUD_NOTIFY_NO_TELE_WITH_FLAG:
  371. if ( bOnBlueTeam )
  372. {
  373. pszResult = "resource/UI/notifications/notify_no_tele_with_flag_blue.res";
  374. }
  375. else
  376. {
  377. pszResult = "resource/UI/notifications/notify_no_tele_with_flag_red.res";
  378. }
  379. break;
  380. case HUD_NOTIFY_SPECIAL:
  381. pszResult = "resource/UI/notifications/notify_special.res";
  382. break;
  383. case HUD_NOTIFY_GOLDEN_WRENCH:
  384. pszResult = "resource/UI/notifications/notify_golden_wrench.res";
  385. break;
  386. case HUD_NOTIFY_RD_ROBOT_UNDER_ATTACK:
  387. if ( bOnBlueTeam )
  388. {
  389. pszResult = "resource/UI/notifications/notify_rd_robot_attacked_blue.res";
  390. }
  391. else
  392. {
  393. pszResult = "resource/UI/notifications/notify_rd_robot_attacked_red.res";
  394. }
  395. break;
  396. case HUD_NOTIFY_HOW_TO_CONTROL_GHOST:
  397. pszResult = "resource/UI/notifications/notify_how_to_control_ghost.res";
  398. flDuration = 10.f;
  399. break;
  400. case HUD_NOTIFY_HOW_TO_CONTROL_KART:
  401. pszResult = "resource/UI/notifications/notify_how_to_control_kart.res";
  402. flDuration = 10.f;
  403. break;
  404. case HUD_NOTIFY_HOW_TO_CONTROL_GHOST_NO_RESPAWN:
  405. pszResult = "resource/UI/notifications/notify_how_to_control_ghost_no_respawn.res";
  406. flDuration = 10.f;
  407. break;
  408. // Passtime
  409. case HUD_NOTIFY_PASSTIME_HOWTO:
  410. pszResult = "resource/UI/notifications/notify_passtime_howto.res";
  411. flDuration = 10.f;
  412. break;
  413. case HUD_NOTIFY_PASSTIME_NO_TELE:
  414. pszResult = "resource/UI/notifications/notify_passtime_no_tele.res";
  415. break;
  416. case HUD_NOTIFY_PASSTIME_NO_CARRY:
  417. pszResult = "resource/UI/notifications/notify_passtime_no_carry.res";
  418. break;
  419. case HUD_NOTIFY_PASSTIME_NO_INVULN:
  420. pszResult = "resource/UI/notifications/notify_passtime_no_invuln.res";
  421. break;
  422. case HUD_NOTIFY_PASSTIME_NO_DISGUISE:
  423. pszResult = "resource/UI/notifications/notify_passtime_no_disguise.res";
  424. break;
  425. case HUD_NOTIFY_PASSTIME_NO_CLOAK:
  426. pszResult = "resource/UI/notifications/notify_passtime_no_cloak.res";
  427. break;
  428. case HUD_NOTIFY_PASSTIME_NO_OOB:
  429. pszResult = "resource/UI/notifications/notify_passtime_no_oob.res";
  430. break;
  431. case HUD_NOTIFY_PASSTIME_NO_HOLSTER:
  432. pszResult = "resource/UI/notifications/notify_passtime_no_holster.res";
  433. break;
  434. case HUD_NOTIFY_PASSTIME_NO_TAUNT:
  435. pszResult = "resource/UI/notifications/notify_passtime_no_taunt.res";
  436. break;
  437. // Competitive
  438. case HUD_NOTIFY_COMPETITIVE_GC_DOWN:
  439. pszResult = "resource/UI/notifications/notify_competitive_gc_down.res";
  440. flDuration = 20.f;
  441. break;
  442. case HUD_NOTIFY_TRUCE_START:
  443. pszResult = "resource/UI/notifications/notify_truce_start.res";
  444. flDuration = 10.f;
  445. break;
  446. case HUD_NOTIFY_TRUCE_END:
  447. pszResult = "resource/UI/notifications/notify_truce_end.res";
  448. flDuration = 10.f;
  449. break;
  450. default:
  451. break;
  452. }
  453. return pszResult;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. bool CHudNotificationPanel::LoadManifest( void )
  459. {
  460. const char *pszManifestFile = "resource/UI/notifications/notification_manifest.txt";
  461. KeyValues *manifest = new KeyValues( pszManifestFile );
  462. if ( manifest->LoadFromFile( g_pFullFileSystem, pszManifestFile, "GAME" ) == false )
  463. {
  464. manifest->deleteThis();
  465. return false;
  466. }
  467. // Load each file defined in the text
  468. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  469. {
  470. if ( !Q_stricmp( sub->GetName(), "file" ) )
  471. {
  472. if ( BuildGroup::PrecacheResFile( sub->GetString() ) == false )
  473. {
  474. Warning("Failed to load notification res file '%s' specified in %s.\n", sub->GetString(), pszManifestFile );
  475. }
  476. }
  477. }
  478. manifest->deleteThis();
  479. return true;
  480. }