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.

1634 lines
56 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Draws CSPort's death notices
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "hudelement.h"
  9. #include "hud_macros.h"
  10. #include "c_playerresource.h"
  11. #include "iclientmode.h"
  12. #include <vgui_controls/Controls.h>
  13. #include <vgui_controls/Panel.h>
  14. #include <vgui/ISurface.h>
  15. #include <vgui/ILocalize.h>
  16. #include "vgui_controls/TextImage.h"
  17. #include <KeyValues.h>
  18. #include "c_baseplayer.h"
  19. #include "c_team.h"
  20. #include "gcsdk/gcclientsdk.h"
  21. #include "tf_gcmessages.h"
  22. #include "tf_item_inventory.h"
  23. #include "hud_basedeathnotice.h"
  24. #include "tf_shareddefs.h"
  25. #include "clientmode_tf.h"
  26. #include "c_tf_player.h"
  27. #include "c_tf_playerresource.h"
  28. #include "tf_hud_freezepanel.h"
  29. #include "engine/IEngineSound.h"
  30. #include "tf_controls.h"
  31. #include "tf_gamerules.h"
  32. #include "econ_notifications.h"
  33. //#include "econ/econ_controls.h"
  34. #include "passtime_game_events.h"
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. using namespace vgui;
  38. // Must match resource/tf_objects.txt!!!
  39. const char *szLocalizedObjectNames[OBJ_LAST] =
  40. {
  41. "#TF_Object_Dispenser",
  42. "#TF_Object_Tele",
  43. "#TF_Object_Sentry",
  44. "#TF_object_Sapper"
  45. };
  46. ConVar cl_hud_killstreak_display_time( "cl_hud_killstreak_display_time", "3", FCVAR_ARCHIVE, "How long a killstreak notice stays on the screen (in seconds). Range is from 0 to 100." );
  47. ConVar cl_hud_killstreak_display_fontsize( "cl_hud_killstreak_display_fontsize", "0", FCVAR_ARCHIVE, "Adjusts font size of killstreak notices. Range is from 0 to 2 (default is 1)." );
  48. ConVar cl_hud_killstreak_display_alpha( "cl_hud_killstreak_display_alpha", "120", FCVAR_ARCHIVE, "Adjusts font alpha value of killstreak notices. Range is from 0 to 255 (default is 200)." );
  49. const int STREAK_MIN = 5;
  50. const int STREAK_MIN_MVM = 20;
  51. const int STREAK_MIN_DUCKS = 10;
  52. static int MinStreakForType( CTFPlayerShared::ETFStreak eStreakType )
  53. {
  54. bool bIsMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode();
  55. if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks )
  56. {
  57. return STREAK_MIN_DUCKS;
  58. }
  59. if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
  60. {
  61. return 1;
  62. }
  63. if ( bIsMvM )
  64. {
  65. return STREAK_MIN_MVM;
  66. }
  67. return STREAK_MIN;
  68. }
  69. //=========================================================
  70. // CTFStreakNotice
  71. //=========================================================
  72. class CTFStreakNotice : public CHudElement, public vgui::EditablePanel
  73. {
  74. DECLARE_CLASS_SIMPLE( CTFStreakNotice, vgui::EditablePanel );
  75. public:
  76. CTFStreakNotice( const char *pName );
  77. virtual bool ShouldDraw( void ) OVERRIDE;
  78. virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) OVERRIDE;
  79. virtual void Paint( void ) OVERRIDE;
  80. void StreakEnded( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iStreak );
  81. void StreakUpdated( CTFPlayerShared::ETFStreak eStreakType, int iPlayerID, int iStreak, int iStreakIncrement );
  82. bool IsCurrentStreakHigherPriority( CTFPlayerShared::ETFStreak eStreakType, int iStreak );
  83. HFont GetStreakFont( void );
  84. private:
  85. CExLabel *m_pLabel;
  86. EditablePanel *m_pBackground;
  87. float m_flLastMessageTime;
  88. int m_nCurrStreakCount;
  89. CTFPlayerShared::ETFStreak m_nCurrStreakType;
  90. int m_nLabelXPos;
  91. int m_nLabelYPos;
  92. CHudTexture *m_iconKillStreak;
  93. CHudTexture *m_iconDuckStreak;
  94. };
  95. //-----------------------------------------------------------------------------
  96. CTFStreakNotice::CTFStreakNotice( const char *pName ) : CHudElement( pName ), vgui::EditablePanel( NULL, pName )
  97. {
  98. SetParent( g_pClientMode->GetViewport() );
  99. m_pBackground = new EditablePanel( this, "Background" );
  100. m_pLabel = new CExLabel( this, "SplashLabel", "" );
  101. m_flLastMessageTime = -10.0f;
  102. m_nCurrStreakCount = 0;
  103. m_nCurrStreakType = (CTFPlayerShared::ETFStreak)0;
  104. m_iconKillStreak = gHUD.GetIcon( "leaderboard_streak" );
  105. m_iconDuckStreak = gHUD.GetIcon( "eotl_duck" );
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //-----------------------------------------------------------------------------
  110. bool CTFStreakNotice::ShouldDraw( void )
  111. {
  112. if ( !CHudElement::ShouldDraw() )
  113. return false;
  114. C_TFPlayer *pPlayer = CTFPlayer::GetLocalTFPlayer();
  115. if ( !pPlayer )
  116. return false;
  117. if ( IsTakingAFreezecamScreenshot() )
  118. return false;
  119. return m_nCurrStreakCount > 0;
  120. }
  121. //-----------------------------------------------------------------------------
  122. void CTFStreakNotice::ApplySchemeSettings( IScheme *pScheme )
  123. {
  124. BaseClass::ApplySchemeSettings( pScheme );
  125. LoadControlSettings( "resource/UI/HudKillStreakNotice.res" );
  126. m_pLabel->GetPos( m_nLabelXPos, m_nLabelYPos );
  127. SetSize( XRES(640), YRES(480) );
  128. }
  129. //-----------------------------------------------------------------------------
  130. void CTFStreakNotice::Paint( void )
  131. {
  132. int nDisplayTime = clamp( cl_hud_killstreak_display_time.GetInt(), 1, 100 );
  133. if ( m_flLastMessageTime + nDisplayTime < gpGlobals->realtime )
  134. {
  135. SetVisible( false );
  136. m_nCurrStreakCount = 0;
  137. return;
  138. }
  139. float flFadeTime = 1.5f;
  140. float flTimeRemaining = (float)nDisplayTime - ( gpGlobals->realtime - m_flLastMessageTime );
  141. SetVisible( true );
  142. if ( flTimeRemaining > flFadeTime )
  143. {
  144. SetAlpha( 255 );
  145. }
  146. else
  147. {
  148. float flAlpha = RemapValClamped( flTimeRemaining, flFadeTime, 0.f, 255.f, 0.f );
  149. SetAlpha( flAlpha );
  150. }
  151. // Move labels down when in spectator
  152. C_TFPlayer *pPlayer = CTFPlayer::GetLocalTFPlayer();
  153. CHudTexture *pIcon = ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Ducks || m_nCurrStreakType == CTFPlayerShared::kTFStreak_Duck_levelup ) ? m_iconDuckStreak : m_iconKillStreak;
  154. if ( pPlayer && pIcon )
  155. {
  156. int nYOffset = ( pPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ? YRES(40) : 0 );
  157. int iWide, iTall;
  158. m_pLabel->GetContentSize( iWide, iTall );
  159. m_pLabel->SizeToContents();
  160. m_pLabel->SetPos( XRES(320) - iWide / 2, m_nLabelYPos + nYOffset );
  161. m_pBackground->SetSize( iWide + iTall / 2, iTall ); // add in icon width
  162. m_pBackground->SetPos( XRES(315) - iWide / 2, m_nLabelYPos + nYOffset);
  163. wchar_t szTitle[256];
  164. m_pLabel->GetText( szTitle, 256 );
  165. HFont hFont = GetStreakFont();
  166. int iTextWide= UTIL_ComputeStringWidth( hFont, szTitle );
  167. pIcon->DrawSelf( XRES(320) - (iWide / 2) + iTextWide, m_nLabelYPos + nYOffset, iTall, iTall, Color(235, 226, 202, GetAlpha() ) );
  168. }
  169. BaseClass::Paint();
  170. }
  171. //-----------------------------------------------------------------------------
  172. void CTFStreakNotice::StreakEnded( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iStreak )
  173. {
  174. if ( iStreak < 10 )
  175. return;
  176. if ( IsCurrentStreakHigherPriority( eStreakType, iStreak ) )
  177. return;
  178. // Temp override all messages
  179. // Add New message
  180. m_flLastMessageTime = gpGlobals->realtime;
  181. // Generate the String
  182. const wchar_t *wzMsg = NULL;
  183. bool bSelfKill = false;
  184. if ( iKillerID == iVictimID )
  185. {
  186. wzMsg = g_pVGuiLocalize->Find( ( eStreakType == CTFPlayerShared::kTFStreak_Ducks ) ? "#Msg_DuckStreakEndSelf" : "#Msg_KillStreakEndSelf" );
  187. bSelfKill = true;
  188. }
  189. else
  190. {
  191. wzMsg = g_pVGuiLocalize->Find( ( eStreakType == CTFPlayerShared::kTFStreak_Ducks ) ? "#Msg_DuckStreakEnd" : "#Msg_KillStreakEnd" );
  192. }
  193. if ( !wzMsg )
  194. return;
  195. // m_nCurrStreakCount = iStreak;
  196. // Killer Name
  197. wchar_t wszKillerName[MAX_PLAYER_NAME_LENGTH / 2];
  198. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iKillerID ), wszKillerName, sizeof(wszKillerName) );
  199. // Victim Name
  200. wchar_t wszVictimName[MAX_PLAYER_NAME_LENGTH / 2];
  201. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iVictimID ), wszVictimName, sizeof(wszVictimName) );
  202. // Count
  203. wchar_t wzCount[10];
  204. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iStreak );
  205. wchar_t wTemp[256];
  206. if ( bSelfKill )
  207. {
  208. g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 2, wszKillerName, wzCount );
  209. }
  210. else
  211. {
  212. g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 3, wszKillerName, wszVictimName, wzCount );
  213. }
  214. HFont hFont = GetStreakFont();
  215. if ( m_pLabel->GetFont() != hFont )
  216. {
  217. m_pLabel->SetFont( hFont );
  218. }
  219. m_pLabel->SetText( wTemp );
  220. // Get player Team for color
  221. Color cKillerColor(235, 226, 202, 255);
  222. if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_RED )
  223. {
  224. cKillerColor = COLOR_RED;
  225. }
  226. else if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_BLUE )
  227. {
  228. cKillerColor = COLOR_BLUE;
  229. }
  230. Color cVictimColor(235, 226, 202, 255);
  231. if ( g_PR->GetTeam( iVictimID ) == TF_TEAM_RED )
  232. {
  233. cVictimColor = COLOR_RED;
  234. }
  235. else if ( g_PR->GetTeam( iVictimID ) == TF_TEAM_BLUE )
  236. {
  237. cVictimColor = COLOR_BLUE;
  238. }
  239. m_pLabel->GetTextImage()->ClearColorChangeStream();
  240. // We change the title's text color to match the colors of the matching model panel backgrounds
  241. wchar_t *txt = wTemp;
  242. int iWChars = 0;
  243. while ( txt && *txt )
  244. {
  245. switch ( *txt )
  246. {
  247. case 0x01: // Normal color
  248. m_pLabel->GetTextImage()->AddColorChange( Color(235, 226, 202, cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  249. break;
  250. case 0x02: // Team color
  251. m_pLabel->GetTextImage()->AddColorChange( Color( cKillerColor.r(), cKillerColor.g(), cKillerColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  252. break;
  253. case 0x03: // Item 2 color
  254. m_pLabel->GetTextImage()->AddColorChange( Color( cVictimColor.r(), cVictimColor.g(), cVictimColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  255. break;
  256. default:
  257. break;
  258. }
  259. txt++;
  260. iWChars++;
  261. }
  262. m_flLastMessageTime = gpGlobals->realtime;
  263. SetVisible( true );
  264. }
  265. //-----------------------------------------------------------------------------
  266. void CTFStreakNotice::StreakUpdated( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iStreak, int iStreakIncrement )
  267. {
  268. // Temp override all messages
  269. // Add New message
  270. bool bIsMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode();
  271. int iStreakMin = MinStreakForType( eStreakType );
  272. if ( IsCurrentStreakHigherPriority( eStreakType, iStreak ) )
  273. return;
  274. // Is this message worth responding to
  275. int iStreakTier = 0;
  276. if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks)
  277. {
  278. // Notices at 15, 30, then increments of 50. We may increment by multiple ducks per kill, so check if we passed over a milestone.
  279. if ( iStreak >= 15 && ( iStreak - iStreakIncrement < 15 ) )
  280. {
  281. iStreakTier = 1;
  282. iStreak = 15;
  283. }
  284. else if ( iStreak >= 30 && ( iStreak - iStreakIncrement <30 ) )
  285. {
  286. iStreakTier = 2;
  287. iStreak = 30;
  288. }
  289. else if ( iStreak > 50 && iStreak % 50 < iStreakIncrement )
  290. {
  291. iStreakTier = Min( 2 + ( iStreak / 50 ), 5 );
  292. iStreak -= iStreak % 50;
  293. }
  294. else
  295. {
  296. return;
  297. }
  298. }
  299. else if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
  300. {
  301. iStreakTier = 5;
  302. }
  303. else if ( bIsMvM )
  304. {
  305. if ( iStreak % iStreakMin != 0 )
  306. return;
  307. iStreakTier = iStreak / iStreakMin;
  308. }
  309. else
  310. {
  311. if ( iStreak == 5 )
  312. {
  313. iStreakTier = 1;
  314. }
  315. else if ( iStreak == 10 )
  316. {
  317. iStreakTier = 2;
  318. }
  319. else if ( iStreak == 15 )
  320. {
  321. iStreakTier = 3;
  322. }
  323. else if ( iStreak == 20 )
  324. {
  325. iStreakTier = 4;
  326. }
  327. else if ( iStreak % 10 == 0 || iStreak % 10 == 5 )
  328. {
  329. iStreakTier = 5;
  330. }
  331. else
  332. {
  333. return;
  334. }
  335. }
  336. m_nCurrStreakCount = iStreak;
  337. m_nCurrStreakType = eStreakType;
  338. const wchar_t *wzMsg = NULL;
  339. const char *pszSoundName = "Game.KillStreak";
  340. Color cCustomColor(235, 226, 202, 255);
  341. if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks )
  342. {
  343. // Duckstreak tiers
  344. switch ( iStreakTier )
  345. {
  346. case 1:
  347. // TODO duckier colors?
  348. cCustomColor = Color( 112, 176, 74, 255); // Green
  349. wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak1" );
  350. //pszSoundName = "Announcer.DuckStreak_Level1";
  351. break;
  352. case 2:
  353. cCustomColor = Color( 207, 106, 50, 255); // Orange
  354. wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak2" );
  355. //pszSoundName = "Announcer.DuckStreak_Level2";
  356. break;
  357. case 3:
  358. cCustomColor = Color( 134, 80, 172, 255); // Purple
  359. wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak3" );
  360. //pszSoundName = "Announcer.DuckStreak_Level3";
  361. break;
  362. case 4:
  363. cCustomColor = Color(255, 215, 0, 255); // Gold
  364. wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak4" );
  365. //pszSoundName = "Announcer.DuckStreak_Level4";
  366. break;
  367. default:
  368. cCustomColor = Color(255, 215, 0, 255); // Still Gold
  369. wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak5" );
  370. //pszSoundName = "Announcer.DuckStreak_Level4";
  371. break;
  372. }
  373. }
  374. else if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
  375. {
  376. cCustomColor = Color( 255, 215, 0, 255 ); // Gold
  377. switch ( RandomInt( 1, 3 ) )
  378. {
  379. case 1: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup1" ); break;
  380. case 2: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup2" ); break;
  381. default: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup3" ); break;
  382. }
  383. }
  384. else
  385. {
  386. // Killstreak tiers
  387. switch ( iStreakTier )
  388. {
  389. case 1:
  390. cCustomColor = Color( 112, 176, 74, 255); // Green
  391. wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak1" );
  392. //pszSoundName = "Announcer.KillStreak_Level1";
  393. break;
  394. case 2:
  395. cCustomColor = Color( 207, 106, 50, 255); // Orange
  396. wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak2" );
  397. //pszSoundName = "Announcer.KillStreak_Level2";
  398. break;
  399. case 3:
  400. cCustomColor = Color( 134, 80, 172, 255); // Purple
  401. wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak3" );
  402. //pszSoundName = "Announcer.KillStreak_Level3";
  403. break;
  404. case 4:
  405. cCustomColor = Color(255, 215, 0, 255); // Gold
  406. wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak4" );
  407. //pszSoundName = "Announcer.KillStreak_Level4";
  408. break;
  409. default:
  410. cCustomColor = Color(255, 215, 0, 255); // Still Gold
  411. wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak5" );
  412. //pszSoundName = "Announcer.KillStreak_Level4";
  413. break;
  414. }
  415. }
  416. if ( !wzMsg )
  417. return;
  418. // Get player Team for color
  419. Color cTeamColor(235, 226, 202, 255);
  420. if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_RED )
  421. {
  422. cTeamColor = COLOR_RED;
  423. }
  424. else if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_BLUE )
  425. {
  426. cTeamColor = COLOR_BLUE;
  427. }
  428. // Generate the String
  429. // Count
  430. wchar_t wzCount[10];
  431. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iStreak );
  432. // Name
  433. wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH / 2];
  434. g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iKillerID ), wszPlayerName, sizeof(wszPlayerName) );
  435. wchar_t wTemp[256];
  436. g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 2, wszPlayerName, wzCount );
  437. HFont hFont = GetStreakFont();
  438. if ( m_pLabel->GetFont() != hFont )
  439. {
  440. m_pLabel->SetFont( hFont );
  441. }
  442. m_pLabel->SetText( wTemp );
  443. // Now go through the string and find the escape characters telling us where the color changes are
  444. m_pLabel->GetTextImage()->ClearColorChangeStream();
  445. // We change the title's text color to match the colors of the matching model panel backgrounds
  446. wchar_t *txt = wTemp;
  447. int iWChars = 0;
  448. while ( txt && *txt )
  449. {
  450. switch ( *txt )
  451. {
  452. case 0x01: // Normal color
  453. m_pLabel->GetTextImage()->AddColorChange( Color(235,226,202,cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  454. break;
  455. case 0x02: // Team color
  456. m_pLabel->GetTextImage()->AddColorChange( Color( cTeamColor.r(), cTeamColor.g(), cTeamColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  457. break;
  458. case 0x03: // Item 2 color
  459. m_pLabel->GetTextImage()->AddColorChange( Color( cCustomColor.r(), cCustomColor.g(), cCustomColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
  460. break;
  461. default:
  462. break;
  463. }
  464. txt++;
  465. iWChars++;
  466. }
  467. // Play Local Sound
  468. int iLocalPlayerIndex = GetLocalPlayerIndex();
  469. if ( iLocalPlayerIndex == iKillerID && pszSoundName )
  470. {
  471. CLocalPlayerFilter filter;
  472. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName );
  473. }
  474. m_flLastMessageTime = gpGlobals->realtime + (float)iStreakTier / 2.0;
  475. SetVisible( true );
  476. }
  477. //-----------------------------------------------------------------------------
  478. bool CTFStreakNotice::IsCurrentStreakHigherPriority( CTFPlayerShared::ETFStreak eStreakType, int iStreak )
  479. {
  480. // duck level ups are highest priority
  481. if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
  482. return false;
  483. if ( !m_nCurrStreakCount )
  484. return false;
  485. // Ducks never override kills
  486. if ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Kills && eStreakType == CTFPlayerShared::kTFStreak_Ducks )
  487. return true;
  488. // But kills always override ducks
  489. if ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Ducks && eStreakType == CTFPlayerShared::kTFStreak_Kills )
  490. return false;
  491. // Don't stomp a higher streak with a lower, unless it's been around long enough
  492. float flElapsedTime = gpGlobals->realtime - m_flLastMessageTime;
  493. float flDisplayMinTime = Max( ( cl_hud_killstreak_display_time.GetFloat() / 3.f ), 1.f );
  494. return ( iStreak < m_nCurrStreakCount && flElapsedTime < flDisplayMinTime );
  495. }
  496. //-----------------------------------------------------------------------------
  497. HFont CTFStreakNotice::GetStreakFont( void )
  498. {
  499. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  500. const char *pszFontName = "HudFontSmallestBold";
  501. int nFontSize = cl_hud_killstreak_display_fontsize.GetInt(); // Default is 1: HudFontSmallBold
  502. if ( nFontSize == 1 )
  503. {
  504. pszFontName = "HudFontSmallBold";
  505. }
  506. else if ( nFontSize == 2 )
  507. {
  508. pszFontName = "HudFontMediumSmallBold";
  509. }
  510. return pScheme->GetFont( pszFontName, true );
  511. }
  512. DECLARE_HUDELEMENT( CTFStreakNotice );
  513. //-----------------------------------------------------------------------------
  514. // TFDeathNotice
  515. //-----------------------------------------------------------------------------
  516. class CTFHudDeathNotice : public CHudBaseDeathNotice
  517. {
  518. DECLARE_CLASS_SIMPLE( CTFHudDeathNotice, CHudBaseDeathNotice );
  519. public:
  520. CTFHudDeathNotice( const char *pElementName ) : CHudBaseDeathNotice( pElementName ) {};
  521. virtual void Init( void );
  522. virtual void ApplySchemeSettings( vgui::IScheme *scheme );
  523. virtual bool IsVisible( void );
  524. virtual bool ShouldDraw( void );
  525. virtual void FireGameEvent( IGameEvent *event );
  526. void PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType );
  527. virtual bool ShouldShowDeathNotice( IGameEvent *event );
  528. protected:
  529. virtual void OnGameEvent( IGameEvent *event, int iDeathNoticeMsg );
  530. virtual Color GetTeamColor( int iTeamNumber, bool bLocalPlayerInvolved = false );
  531. virtual Color GetInfoTextColor( int iDeathNoticeMsg );
  532. virtual Color GetBackgroundColor ( int iDeathNoticeMsg );
  533. virtual bool EventIsPlayerDeath( const char *eventName );
  534. virtual int UseExistingNotice( IGameEvent *event );
  535. private:
  536. void AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey );
  537. void AddStreakMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iKillerStreak, int iStreakIncrement, int iVictimID, int iDeathNoticeMsg );
  538. void AddStreakEndedMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iVictimStreak, int iDeathNoticeMsg );
  539. CHudTexture* GetMannPowerIcon( RuneTypes_t tRuneType, bool bRedTeam );
  540. CHudTexture *m_iconDomination;
  541. CHudTexture *m_iconKillStreak;
  542. CHudTexture *m_iconDuckStreak;
  543. CHudTexture *m_iconDuckStreakDNeg;
  544. CHudTexture *m_iconKillStreakDNeg;
  545. CPanelAnimationVar( Color, m_clrBlueText, "TeamBlue", "153 204 255 255" );
  546. CPanelAnimationVar( Color, m_clrRedText, "TeamRed", "255 64 64 255" );
  547. CPanelAnimationVar( Color, m_clrPurpleText, "PurpleText", "134 80 172 255" );
  548. CPanelAnimationVar( Color, m_clrGreenText, "GreenText", "112 176 74 255" );
  549. CPanelAnimationVar( Color, m_clrLocalPlayerText, "LocalPlayerColor", "65 65 65 255" );
  550. CTFStreakNotice *m_pStreakNotice;
  551. bool m_bShowItemOnKill;
  552. };
  553. DECLARE_HUDELEMENT( CTFHudDeathNotice );
  554. void CTFHudDeathNotice::Init()
  555. {
  556. BaseClass::Init();
  557. ListenForGameEvent( "fish_notice" );
  558. ListenForGameEvent( "fish_notice__arm" );
  559. ListenForGameEvent( "duck_xp_level_up" );
  560. //ListenForGameEvent( "throwable_hit" );
  561. m_bShowItemOnKill = true;
  562. // PASSTIME if this is called at level load or something we should check mode before this block
  563. ListenForGameEvent( PasstimeGameEvents::BallGet::s_eventName );
  564. ListenForGameEvent( PasstimeGameEvents::BallStolen::s_eventName );
  565. ListenForGameEvent( PasstimeGameEvents::Score::s_eventName );
  566. ListenForGameEvent( PasstimeGameEvents::PassCaught::s_eventName );
  567. ListenForGameEvent( PasstimeGameEvents::BallBlocked::s_eventName );
  568. }
  569. void CTFHudDeathNotice::ApplySchemeSettings( vgui::IScheme *scheme )
  570. {
  571. BaseClass::ApplySchemeSettings( scheme );
  572. m_iconDomination = gHUD.GetIcon( "leaderboard_dominated" );
  573. m_iconKillStreak = gHUD.GetIcon( "leaderboard_streak" );
  574. m_iconKillStreakDNeg = gHUD.GetIcon( "leaderboard_streak_dneg" );
  575. m_iconDuckStreak = gHUD.GetIcon( "eotl_duck" );
  576. m_iconDuckStreakDNeg = gHUD.GetIcon( "eotl_duck_dneg" );
  577. m_pStreakNotice = new CTFStreakNotice( "KillStreakNotice" );
  578. }
  579. bool CTFHudDeathNotice::IsVisible( void )
  580. {
  581. if ( IsTakingAFreezecamScreenshot() )
  582. return false;
  583. return BaseClass::IsVisible();
  584. }
  585. bool CTFHudDeathNotice::ShouldDraw( void )
  586. {
  587. return true;
  588. }
  589. bool CTFHudDeathNotice::ShouldShowDeathNotice( IGameEvent *event )
  590. {
  591. if ( event->GetBool( "silent_kill" ) )
  592. {
  593. // Don't show a kill event for the team of the silent kill victim.
  594. int iVictimID = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
  595. C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictimID ) );
  596. if ( pVictim && pVictim->GetTeamNumber() == GetLocalPlayerTeam() && iVictimID != GetLocalPlayerIndex() )
  597. {
  598. return false;
  599. }
  600. }
  601. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && ( event->GetInt( "death_flags" ) & TF_DEATH_MINIBOSS ) == 0 )
  602. {
  603. int iLocalPlayerIndex = GetLocalPlayerIndex();
  604. if ( iLocalPlayerIndex != engine->GetPlayerForUserID( event->GetInt( "attacker" ) ) &&
  605. iLocalPlayerIndex != engine->GetPlayerForUserID( event->GetInt( "assister" ) ) )
  606. {
  607. C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( engine->GetPlayerForUserID( event->GetInt( "userid" ) ) ) );
  608. if ( pVictim && pVictim->GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  609. {
  610. return false;
  611. }
  612. }
  613. }
  614. return true;
  615. }
  616. void CTFHudDeathNotice::PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType )
  617. {
  618. int iLocalPlayerIndex = GetLocalPlayerIndex();
  619. //We're not involved in this kill
  620. if ( iKillerIndex != iLocalPlayerIndex && iVictimIndex != iLocalPlayerIndex )
  621. return;
  622. const char *pszSoundName = NULL;
  623. if ( iType == TF_DEATH_DOMINATION )
  624. {
  625. if ( iKillerIndex == iLocalPlayerIndex )
  626. {
  627. pszSoundName = "Game.Domination";
  628. }
  629. else if ( iVictimIndex == iLocalPlayerIndex )
  630. {
  631. pszSoundName = "Game.Nemesis";
  632. }
  633. }
  634. else if ( iType == TF_DEATH_REVENGE )
  635. {
  636. pszSoundName = "Game.Revenge";
  637. }
  638. CLocalPlayerFilter filter;
  639. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName );
  640. }
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Server's told us that someone's died
  643. //-----------------------------------------------------------------------------
  644. void CTFHudDeathNotice::FireGameEvent( IGameEvent *event )
  645. {
  646. const char * pszEventName = event->GetName();
  647. if ( FStrEq( "duck_xp_level_up", pszEventName ) )
  648. {
  649. int level = event->GetInt( "level" );
  650. AddStreakMsg( CTFPlayerShared::kTFStreak_Duck_levelup, GetLocalPlayerIndex(), level, 1, -1, 0 );
  651. return;
  652. }
  653. BaseClass::FireGameEvent( event );
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Purpose:
  657. //-----------------------------------------------------------------------------
  658. bool CTFHudDeathNotice::EventIsPlayerDeath( const char* eventName )
  659. {
  660. return FStrEq( eventName, "fish_notice" )
  661. || FStrEq( eventName, "fish_notice__arm" )
  662. //|| FStrEq( eventName, "throwable_hit" )
  663. || BaseClass::EventIsPlayerDeath( eventName );
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Purpose: Called when a game event happens and a death notice is about to be
  667. // displayed. This method can examine the event and death notice and
  668. // make game-specific tweaks to it before it is displayed
  669. //-----------------------------------------------------------------------------
  670. void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg )
  671. {
  672. const bool bIsSillyPyroVision = IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO );
  673. const char *pszEventName = event->GetName();
  674. if ( FStrEq( pszEventName, "player_death" ) || FStrEq( pszEventName, "object_destroyed" ) )
  675. {
  676. bool bIsObjectDestroyed = FStrEq( pszEventName, "object_destroyed" );
  677. int iCustomDamage = event->GetInt( "customkill" );
  678. int iLocalPlayerIndex = GetLocalPlayerIndex();
  679. const int iKillerID = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
  680. const int iVictimID = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
  681. // if there was an assister, put both the killer's and assister's names in the death message
  682. int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
  683. EHorriblePyroVisionHack ePyroVisionHack = kHorriblePyroVisionHack_KillAssisterType_Default;
  684. CUtlConstString sAssisterNameScratch;
  685. const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
  686. // If we don't have a real assister (would have been passed in to us as a player index) and
  687. // we're in crazy pyrovision mode and we got a dummy assister, than fall back and display
  688. // that just for giggles. We use this so the Balloonicorn and friends can get the assist
  689. // credit they so rightly deserve.
  690. if ( !assister_name && bIsSillyPyroVision )
  691. {
  692. // Ignore this for self-kills.
  693. if ( bIsObjectDestroyed || (iKillerID != iVictimID) )
  694. {
  695. const char *pszMaybeFallbackAssisterName = event->GetString( "assister_fallback" );
  696. if ( pszMaybeFallbackAssisterName && pszMaybeFallbackAssisterName[0] )
  697. {
  698. // We store the type of silly assist in the first byte of the string because we
  699. // are terrible people.
  700. ePyroVisionHack = (EHorriblePyroVisionHack)pszMaybeFallbackAssisterName[0];
  701. Assert( ePyroVisionHack != kHorriblePyroVisionHack_KillAssisterType_Default );
  702. pszMaybeFallbackAssisterName = &pszMaybeFallbackAssisterName[1];
  703. // If we pass in a localization string, we need to convert it back to ANSI temporarily.
  704. // This won't localize "The" Balloonicorn because we don't have a real item with a real
  705. // quality, etc., just a single localization token.
  706. switch ( ePyroVisionHack )
  707. {
  708. case kHorriblePyroVisionHack_KillAssisterType_LocalizationString:
  709. case kHorriblePyroVisionHack_KillAssisterType_LocalizationString_First:
  710. {
  711. wchar_t *wszLocalizedItemName = GLocalizationProvider()->Find( pszMaybeFallbackAssisterName );
  712. char szANSIConvertedItemName[ MAX_PLAYER_NAME_LENGTH ];
  713. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedItemName, szANSIConvertedItemName, MAX_PLAYER_NAME_LENGTH );
  714. sAssisterNameScratch = szANSIConvertedItemName;
  715. assister_name = sAssisterNameScratch.Get();
  716. break;
  717. }
  718. case kHorriblePyroVisionHack_KillAssisterType_CustomName:
  719. case kHorriblePyroVisionHack_KillAssisterType_CustomName_First:
  720. {
  721. sAssisterNameScratch = pszMaybeFallbackAssisterName;
  722. assister_name = sAssisterNameScratch.Get();
  723. break;
  724. }
  725. default:
  726. assert( !"Unknown pyro item hack type! Something has gone horribly, horribly worse." );
  727. }
  728. }
  729. }
  730. }
  731. bool bMultipleKillers = false;
  732. if ( assister_name )
  733. {
  734. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  735. const char *pszKillerName = msg.Killer.szName;
  736. const char *pszAssisterName = assister_name;
  737. // Check to see if we're swapping the killer and the assister. We use this so the brain slug can get the kill
  738. // credit for the HUD death notices, with the player being the assister.
  739. if ( pszAssisterName && (ePyroVisionHack == kHorriblePyroVisionHack_KillAssisterType_CustomName_First ||
  740. ePyroVisionHack == kHorriblePyroVisionHack_KillAssisterType_LocalizationString_First) )
  741. {
  742. std::swap( pszKillerName, pszAssisterName );
  743. }
  744. char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
  745. Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", pszKillerName, pszAssisterName );
  746. Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
  747. if ( iLocalPlayerIndex == iAssisterID )
  748. {
  749. msg.bLocalPlayerInvolved = true;
  750. }
  751. bMultipleKillers = true;
  752. }
  753. // play an exciting sound if a sniper pulls off any sort of penetration kill
  754. const int iPlayerPenetrationCount = !event->IsEmpty( "playerpenetratecount" ) ? event->GetInt( "playerpenetratecount" ) : 0;
  755. bool bPenetrateSound = iPlayerPenetrationCount > 0;
  756. // This happens too frequently in Coop/TD
  757. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  758. {
  759. bPenetrateSound = false;
  760. }
  761. if ( bPenetrateSound )
  762. {
  763. CLocalPlayerFilter filter;
  764. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Game.PenetrationKill" );
  765. }
  766. int deathFlags = event->GetInt( "death_flags" );
  767. if ( !bIsObjectDestroyed )
  768. {
  769. // if this death involved a player dominating another player or getting revenge on another player, add an additional message
  770. // mentioning that
  771. // WARNING: AddAdditionalMsg will grow and potentially realloc the m_DeathNotices array. So be careful
  772. // using pointers to m_DeathNotices elements...
  773. if ( deathFlags & TF_DEATH_DOMINATION )
  774. {
  775. AddAdditionalMsg( iKillerID, iVictimID, bIsSillyPyroVision ? "#Msg_Dominating_What" : "#Msg_Dominating" );
  776. PlayRivalrySounds( iKillerID, iVictimID, TF_DEATH_DOMINATION );
  777. }
  778. if ( deathFlags & TF_DEATH_ASSISTER_DOMINATION && ( iAssisterID > 0 ) )
  779. {
  780. AddAdditionalMsg( iAssisterID, iVictimID, bIsSillyPyroVision ? "#Msg_Dominating_What" : "#Msg_Dominating" );
  781. PlayRivalrySounds( iAssisterID, iVictimID, TF_DEATH_DOMINATION );
  782. }
  783. if ( deathFlags & TF_DEATH_REVENGE )
  784. {
  785. AddAdditionalMsg( iKillerID, iVictimID, bIsSillyPyroVision ? "#Msg_Revenge_What" : "#Msg_Revenge" );
  786. PlayRivalrySounds( iKillerID, iVictimID, TF_DEATH_REVENGE );
  787. }
  788. if ( deathFlags & TF_DEATH_ASSISTER_REVENGE && ( iAssisterID > 0 ) )
  789. {
  790. AddAdditionalMsg( iAssisterID, iVictimID, bIsSillyPyroVision ? "#Msg_Revenge_What" : "#Msg_Revenge" );
  791. PlayRivalrySounds( iAssisterID, iVictimID, TF_DEATH_REVENGE );
  792. }
  793. }
  794. else
  795. {
  796. // if this is an object destroyed message, set the victim name to "<object type> (<owner>)"
  797. int iObjectType = event->GetInt( "objecttype" );
  798. if ( iObjectType >= 0 && iObjectType < OBJ_LAST )
  799. {
  800. // get the localized name for the object
  801. char szLocalizedObjectName[MAX_PLAYER_NAME_LENGTH];
  802. szLocalizedObjectName[ 0 ] = 0;
  803. const wchar_t *wszLocalizedObjectName = g_pVGuiLocalize->Find( szLocalizedObjectNames[iObjectType] );
  804. if ( wszLocalizedObjectName )
  805. {
  806. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedObjectName, szLocalizedObjectName, ARRAYSIZE( szLocalizedObjectName ) );
  807. }
  808. else
  809. {
  810. Warning( "Couldn't find localized object name for '%s'\n", szLocalizedObjectNames[iObjectType] );
  811. Q_strncpy( szLocalizedObjectName, szLocalizedObjectNames[iObjectType], sizeof( szLocalizedObjectName ) );
  812. }
  813. // compose the string
  814. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  815. if ( msg.Victim.szName[0] )
  816. {
  817. char szVictimBuf[MAX_PLAYER_NAME_LENGTH*2];
  818. Q_snprintf( szVictimBuf, ARRAYSIZE(szVictimBuf), "%s (%s)", szLocalizedObjectName, msg.Victim.szName );
  819. Q_strncpy( msg.Victim.szName, szVictimBuf, ARRAYSIZE( msg.Victim.szName ) );
  820. }
  821. else
  822. {
  823. Q_strncpy( msg.Victim.szName, szLocalizedObjectName, ARRAYSIZE( msg.Victim.szName ) );
  824. }
  825. }
  826. else
  827. {
  828. Assert( false ); // invalid object type
  829. }
  830. }
  831. const wchar_t *pMsg = NULL;
  832. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  833. switch ( iCustomDamage )
  834. {
  835. case TF_DMG_CUSTOM_BACKSTAB:
  836. if ( FStrEq( msg.szIcon, "d_sharp_dresser" ) )
  837. {
  838. Q_strncpy( msg.szIcon, "d_sharp_dresser_backstab", ARRAYSIZE( msg.szIcon ) );
  839. }
  840. else
  841. {
  842. Q_strncpy( msg.szIcon, "d_backstab", ARRAYSIZE( msg.szIcon ) );
  843. }
  844. break;
  845. case TF_DMG_CUSTOM_HEADSHOT_DECAPITATION:
  846. case TF_DMG_CUSTOM_HEADSHOT:
  847. {
  848. if ( FStrEq( event->GetString( "weapon" ), "ambassador" ) )
  849. {
  850. Q_strncpy( msg.szIcon, "d_ambassador_headshot", ARRAYSIZE( msg.szIcon ) );
  851. }
  852. else if ( FStrEq( event->GetString( "weapon" ), "huntsman" ) )
  853. {
  854. Q_strncpy( msg.szIcon, "d_huntsman_headshot", ARRAYSIZE( msg.szIcon ) );
  855. }
  856. else
  857. {
  858. // Did this headshot penetrate something before the kill? If so, show a fancy icon
  859. // so the player feels proud.
  860. if ( iPlayerPenetrationCount > 0 )
  861. {
  862. Q_strncpy( msg.szIcon, "d_headshot_player_penetration", ARRAYSIZE( msg.szIcon ) );
  863. }
  864. else
  865. {
  866. Q_strncpy( msg.szIcon, "d_headshot", ARRAYSIZE( msg.szIcon ) );
  867. }
  868. }
  869. break;
  870. }
  871. case TF_DMG_CUSTOM_BURNING:
  872. if ( event->GetInt( "attacker" ) == event->GetInt( "userid" ) )
  873. {
  874. // suicide by fire
  875. Q_strncpy( msg.szIcon, "d_firedeath", ARRAYSIZE( msg.szIcon ) );
  876. msg.wzInfoText[0] = 0;
  877. }
  878. break;
  879. case TF_DMG_CUSTOM_BURNING_ARROW:
  880. // special-case if the player is killed from a burning arrow after it has already landed
  881. Q_strncpy( msg.szIcon, "d_huntsman_burning", ARRAYSIZE( msg.szIcon ) );
  882. msg.wzInfoText[0] = 0;
  883. break;
  884. case TF_DMG_CUSTOM_FLYINGBURN:
  885. // special-case if the player is killed from a burning arrow as the killing blow
  886. Q_strncpy( msg.szIcon, "d_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) );
  887. msg.wzInfoText[0] = 0;
  888. break;
  889. case TF_DMG_CUSTOM_PUMPKIN_BOMB:
  890. // special-case if the player is killed by a pumpkin bomb
  891. Q_strncpy( msg.szIcon, "d_pumpkindeath", ARRAYSIZE( msg.szIcon ) );
  892. msg.wzInfoText[0] = 0;
  893. break;
  894. case TF_DMG_CUSTOM_SUICIDE:
  895. {
  896. // display a different message if this was suicide, or assisted suicide (suicide w/recent damage, kill awarded to damager)
  897. bool bAssistedSuicide = event->GetInt( "userid" ) != event->GetInt( "attacker" );
  898. pMsg = g_pVGuiLocalize->Find( ( bAssistedSuicide ) ? ( bMultipleKillers ? "#DeathMsg_AssistedSuicide_Multiple" : "#DeathMsg_AssistedSuicide" ) : ( "#DeathMsg_Suicide" ) );
  899. if ( pMsg )
  900. {
  901. V_wcsncpy( msg.wzInfoText, pMsg, sizeof( msg.wzInfoText ) );
  902. }
  903. break;
  904. }
  905. case TF_DMG_CUSTOM_EYEBALL_ROCKET:
  906. {
  907. if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
  908. {
  909. char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
  910. szLocalizedName[ 0 ] = 0;
  911. const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_EYEBALL_BOSS_DEATHCAM_NAME" );
  912. if ( wszLocalizedName )
  913. {
  914. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
  915. Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
  916. msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to purple for MONOCULUS!
  917. }
  918. }
  919. break;
  920. }
  921. case TF_DMG_CUSTOM_MERASMUS_ZAP:
  922. case TF_DMG_CUSTOM_MERASMUS_GRENADE:
  923. case TF_DMG_CUSTOM_MERASMUS_DECAPITATION:
  924. {
  925. if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
  926. {
  927. char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
  928. szLocalizedName[ 0 ] = 0;
  929. const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_MERASMUS_DEATHCAM_NAME" );
  930. if ( wszLocalizedName )
  931. {
  932. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
  933. Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
  934. msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to green for MERASMUS!
  935. }
  936. }
  937. break;
  938. }
  939. case TF_DMG_CUSTOM_SPELL_SKELETON:
  940. {
  941. if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
  942. {
  943. char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
  944. szLocalizedName[ 0 ] = 0;
  945. const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_SKELETON_DEATHCAM_NAME" );
  946. if ( wszLocalizedName )
  947. {
  948. g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
  949. Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
  950. msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to green for THE UNDEAD!
  951. }
  952. }
  953. break;
  954. }
  955. case TF_DMG_CUSTOM_KART:
  956. // special-case if the player is pushed by kart
  957. Q_strncpy( msg.szIcon, "d_bumper_kart", ARRAYSIZE( msg.szIcon ) );
  958. msg.wzInfoText[0] = 0;
  959. break;
  960. case TF_DMG_CUSTOM_GIANT_HAMMER:
  961. // special-case Giant hammer
  962. Q_strncpy( msg.szIcon, "d_necro_smasher", ARRAYSIZE( msg.szIcon ) );
  963. msg.wzInfoText[0] = 0;
  964. break;
  965. default:
  966. break;
  967. }
  968. if ( ( event->GetInt( "damagebits" ) & DMG_NERVEGAS ) )
  969. {
  970. // special case icon for hit-by-vehicle death
  971. Q_strncpy( msg.szIcon, "d_saw_kill", ARRAYSIZE( msg.szIcon ) );
  972. }
  973. int iKillStreakTotal = event->GetInt( "kill_streak_total" );
  974. int iKillStreakWep = event->GetInt( "kill_streak_wep" );
  975. int iDuckStreakTotal = event->GetInt( "duck_streak_total" );
  976. int iDucksThisKill = event->GetInt( "ducks_streaked" );
  977. // if the active weapon is kill streak
  978. C_TFPlayer* pKiller = ToTFPlayer( UTIL_PlayerByIndex( iKillerID ) );
  979. C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictimID ) );
  980. C_TFPlayer* pAssister = ToTFPlayer( UTIL_PlayerByIndex( iAssisterID ) );
  981. // Mannpower runes
  982. if ( pKiller && pKiller->m_Shared.IsCarryingRune() )
  983. {
  984. msg.iconPreKillerName = GetMannPowerIcon( pKiller->m_Shared.GetCarryingRuneType(), pKiller->GetTeamNumber() == TF_TEAM_RED );
  985. }
  986. if ( pVictim && pVictim->m_Shared.IsCarryingRune() )
  987. {
  988. msg.iconPostVictimName = GetMannPowerIcon( pVictim->m_Shared.GetCarryingRuneType(), pVictim->GetTeamNumber() == TF_TEAM_RED );
  989. }
  990. if ( iKillStreakWep > 0 )
  991. {
  992. // append kill streak count to this notification
  993. wchar_t wzCount[10];
  994. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iKillStreakWep );
  995. g_pVGuiLocalize->ConstructString_safe( msg.wzPreKillerText, g_pVGuiLocalize->Find("#Kill_Streak"), 1, wzCount );
  996. if ( msg.bLocalPlayerInvolved )
  997. {
  998. msg.iconPostKillerName = m_iconKillStreakDNeg;
  999. }
  1000. else
  1001. {
  1002. msg.iconPostKillerName = m_iconKillStreak;
  1003. }
  1004. }
  1005. else if ( iDuckStreakTotal > 0 && iDucksThisKill )
  1006. {
  1007. // Duckstreak icon (always lower priority)
  1008. wchar_t wzCount[10];
  1009. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iDuckStreakTotal );
  1010. g_pVGuiLocalize->ConstructString_safe( msg.wzPreKillerText, g_pVGuiLocalize->Find("#Duck_Streak"), 1, wzCount );
  1011. msg.iconPostKillerName = msg.bLocalPlayerInvolved ? m_iconDuckStreakDNeg : m_iconDuckStreak;
  1012. }
  1013. // Check to see if we want a extra notification
  1014. // Attempt to display these in order of descending priority
  1015. // Check Assister for Additional Messages
  1016. int iKillStreakAssist = event->GetInt( "kill_streak_assist" );
  1017. int iKillStreakVictim = event->GetInt( "kill_streak_victim" );
  1018. // Kills
  1019. AddStreakMsg( CTFPlayerShared::kTFStreak_Kills, iKillerID, iKillStreakTotal, 1, iVictimID, iDeathNoticeMsg );
  1020. if ( pAssister && iKillStreakAssist > 1 )
  1021. {
  1022. AddStreakMsg( CTFPlayerShared::kTFStreak_Kills, iAssisterID, iKillStreakAssist, 1, iVictimID, iDeathNoticeMsg );
  1023. }
  1024. if ( pVictim && iKillStreakVictim > 2 )
  1025. {
  1026. AddStreakEndedMsg( CTFPlayerShared::kTFStreak_Kills, iKillerID, iVictimID, iKillStreakVictim, iDeathNoticeMsg );
  1027. }
  1028. // Ducks
  1029. int iDuckStreakAssist = event->GetInt( "duck_streak_assist" );
  1030. int iDuckStreakVictim = event->GetInt( "duck_streak_victim" );
  1031. int iDuckStreakIncrement = event->GetInt( "ducks_streaked" );
  1032. AddStreakMsg( CTFPlayerShared::kTFStreak_Ducks, iKillerID, iDuckStreakTotal, iDuckStreakIncrement, iVictimID, iDeathNoticeMsg );
  1033. if ( pAssister && iDuckStreakAssist > 0 && iDucksThisKill )
  1034. {
  1035. AddStreakMsg( CTFPlayerShared::kTFStreak_Ducks, iAssisterID, iDuckStreakAssist, iDuckStreakIncrement, iVictimID, iDeathNoticeMsg );
  1036. }
  1037. if ( pVictim && iDuckStreakVictim > 2 )
  1038. {
  1039. AddStreakEndedMsg( CTFPlayerShared::kTFStreak_Ducks, iKillerID, iVictimID, iDuckStreakVictim, iDeathNoticeMsg );
  1040. }
  1041. // STAGING ONLY test
  1042. // If Local Player killed someone and they have an item waiting, let them know
  1043. #ifdef STAGING_ONLY
  1044. //if ( iLocalPlayerIndex == iKillerID && m_bShowItemOnKill )
  1045. //{
  1046. // if ( CEconNotification_HasNewItemsOnKill::HasUnacknowledgedItems() )
  1047. // {
  1048. // CEconNotification_HasNewItemsOnKill *pNotification = new CEconNotification_HasNewItemsOnKill( iVictimID );
  1049. // NotificationQueue_Add( pNotification );
  1050. // m_bShowItemOnKill = false;
  1051. // }
  1052. //}
  1053. //if ( iLocalPlayerIndex == iVictimID )
  1054. //{
  1055. // m_bShowItemOnKill = true;
  1056. //}
  1057. #endif
  1058. }
  1059. else if ( FStrEq( "teamplay_point_captured", pszEventName ) ||
  1060. FStrEq( "teamplay_capture_blocked", pszEventName ) ||
  1061. FStrEq( "teamplay_flag_event", pszEventName ) )
  1062. {
  1063. bool bDefense = ( FStrEq( "teamplay_capture_blocked", pszEventName ) || ( FStrEq( "teamplay_flag_event", pszEventName ) &&
  1064. TF_FLAGEVENT_DEFEND == event->GetInt( "eventtype" ) ) );
  1065. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1066. const char *szCaptureIcons[] = { "d_redcapture", "d_bluecapture" };
  1067. const char *szDefenseIcons[] = { "d_reddefend", "d_bluedefend" };
  1068. int iTeam = msg.Killer.iTeam;
  1069. Assert( iTeam >= FIRST_GAME_TEAM );
  1070. Assert( iTeam < FIRST_GAME_TEAM + TF_TEAM_COUNT );
  1071. if ( iTeam < FIRST_GAME_TEAM || iTeam >= FIRST_GAME_TEAM + TF_TEAM_COUNT )
  1072. return;
  1073. int iIndex = msg.Killer.iTeam - FIRST_GAME_TEAM;
  1074. Assert( iIndex < ARRAYSIZE( szCaptureIcons ) );
  1075. Q_strncpy( msg.szIcon, bDefense ? szDefenseIcons[iIndex] : szCaptureIcons[iIndex], ARRAYSIZE( msg.szIcon ) );
  1076. }
  1077. else if ( FStrEq( "fish_notice", pszEventName ) || FStrEq( "fish_notice__arm", pszEventName ) )
  1078. {
  1079. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1080. int deathFlags = event->GetInt( "death_flags" );
  1081. int iCustomDamage = event->GetInt( "customkill" );
  1082. if ( ( iCustomDamage == TF_DMG_CUSTOM_FISH_KILL ) || ( deathFlags & TF_DEATH_FEIGN_DEATH ) )
  1083. {
  1084. g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, FStrEq( "fish_notice", pszEventName ) ? g_pVGuiLocalize->Find("#Humiliation_Kill") : g_pVGuiLocalize->Find("#Humiliation_Kill_Arm"), 0 );
  1085. }
  1086. else
  1087. {
  1088. wchar_t wzCount[10];
  1089. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", ++msg.iCount );
  1090. g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Humiliation_Count"), 1, wzCount );
  1091. }
  1092. // if there was an assister, put both the killer's and assister's names in the death message
  1093. int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
  1094. const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
  1095. if ( assister_name )
  1096. {
  1097. char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
  1098. Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", msg.Killer.szName, assister_name );
  1099. Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
  1100. }
  1101. }
  1102. //else if ( FStrEq( "throwable_hit", pszEventName ) )
  1103. //{
  1104. // DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1105. // int deathFlags = event->GetInt( "death_flags" );
  1106. // int iCustomDamage = event->GetInt( "customkill" );
  1107. // // Make sure the icon is up to date
  1108. // m_DeathNotices[iDeathNoticeMsg].iconDeath = GetIcon( m_DeathNotices[ iDeathNoticeMsg ].szIcon, m_DeathNotices[iDeathNoticeMsg].bLocalPlayerInvolved ? kDeathNoticeIcon_Inverted : kDeathNoticeIcon_Standard );
  1109. // if ( ( iCustomDamage == TF_DMG_CUSTOM_THROWABLE_KILL ) || ( deathFlags & TF_DEATH_FEIGN_DEATH ) )
  1110. // {
  1111. // g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Throwable_Kill"), 0 );
  1112. // }
  1113. // else
  1114. // {
  1115. // wchar_t wzCount[10];
  1116. // _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", event->GetInt( "totalhits" ) );
  1117. // g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Humiliation_Count"), 1, wzCount );
  1118. // }
  1119. // // if there was an assister, put both the killer's and assister's names in the death message
  1120. // int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
  1121. // const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
  1122. // if ( assister_name )
  1123. // {
  1124. // char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
  1125. // Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", msg.Killer.szName, assister_name );
  1126. // Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
  1127. // }
  1128. //}
  1129. else if ( FStrEq( "rd_robot_killed", pszEventName ) )
  1130. {
  1131. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1132. int killer = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
  1133. const char *killedwith = event->GetString( "weapon" );
  1134. msg.Killer.iTeam = g_PR->GetTeam( killer );
  1135. Q_strncpy( msg.Killer.szName, g_PR->GetPlayerName( killer ), ARRAYSIZE( msg.Killer.szName ) );
  1136. Q_strncpy( msg.Victim.szName, g_PR->GetTeam( killer ) == TF_TEAM_RED ? "BLUE ROBOT" : "RED ROBOT", ARRAYSIZE( msg.Victim.szName ) );
  1137. msg.Victim.iTeam = g_PR->GetTeam( killer ) == TF_TEAM_RED ? TF_TEAM_BLUE : TF_TEAM_RED;
  1138. Q_snprintf( msg.szIcon, sizeof(msg.szIcon), "d_%s", killedwith );
  1139. }
  1140. else if ( FStrEq( PasstimeGameEvents::BallGet::s_eventName, pszEventName ) ) // passtime ball get
  1141. {
  1142. PasstimeGameEvents::BallGet ev( event );
  1143. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1144. // info
  1145. V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeBallGet"), sizeof( msg.wzInfoText ) );
  1146. // killer
  1147. const char *szPlayerName = g_PR->GetPlayerName( ev.ownerIndex);
  1148. Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
  1149. msg.Killer.iTeam = g_PR->GetTeam( ev.ownerIndex );
  1150. // flags
  1151. if ( GetLocalPlayerIndex() == ev.ownerIndex )
  1152. msg.bLocalPlayerInvolved = true;
  1153. // icon
  1154. const char *const icon = "d_passtime_pass";
  1155. Q_strncpy( msg.szIcon, icon, ARRAYSIZE( msg.szIcon ) );
  1156. }
  1157. else if ( FStrEq( PasstimeGameEvents::BallStolen::s_eventName, pszEventName ) ) // passtime ball stolen
  1158. {
  1159. PasstimeGameEvents::BallStolen ev( event );
  1160. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1161. int attackerTeam = g_PR->GetTeam( ev.attackerIndex );
  1162. int victimTeam = g_PR->GetTeam( ev.victimIndex );
  1163. // attacker
  1164. const char *szPlayerName = g_PR->GetPlayerName( ev.attackerIndex );
  1165. Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
  1166. msg.Killer.iTeam = attackerTeam;
  1167. // victim
  1168. szPlayerName = g_PR->GetPlayerName( ev.victimIndex );
  1169. Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
  1170. msg.Victim.iTeam = victimTeam;
  1171. V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeSteal"), sizeof( msg.wzInfoText ) );
  1172. // flags
  1173. int localPlayerIndex = GetLocalPlayerIndex();
  1174. msg.bLocalPlayerInvolved = (localPlayerIndex == ev.attackerIndex)
  1175. || (localPlayerIndex == ev.victimIndex);
  1176. // icon
  1177. Q_strncpy( msg.szIcon, "d_passtime_steal", ARRAYSIZE(msg.szIcon) );
  1178. }
  1179. else if ( FStrEq( PasstimeGameEvents::Score::s_eventName, pszEventName ) ) // passtime score
  1180. {
  1181. PasstimeGameEvents::Score ev( event );
  1182. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1183. // info
  1184. if ( ev.numPoints > 1 )
  1185. {
  1186. wchar_t wzCount[10];
  1187. _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", ev.numPoints );
  1188. g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeScoreCount"), 1, wzCount );
  1189. }
  1190. else
  1191. {
  1192. V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeScore"), sizeof( msg.wzInfoText ) );
  1193. }
  1194. // killer
  1195. const char *szPlayerName = g_PR->GetPlayerName( ev.scorerIndex );
  1196. Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
  1197. msg.Killer.iTeam = g_PR->GetTeam( ev.scorerIndex );
  1198. // flags
  1199. if ( GetLocalPlayerIndex() == ev.scorerIndex )
  1200. msg.bLocalPlayerInvolved = true;
  1201. // icon
  1202. const char *const icon = (msg.Killer.iTeam == TF_TEAM_RED)
  1203. ? "d_passtime_score_red"
  1204. : "d_passtime_score_blue";
  1205. Q_strncpy( msg.szIcon, icon, ARRAYSIZE( msg.szIcon ) );
  1206. }
  1207. else if ( FStrEq( PasstimeGameEvents::PassCaught::s_eventName, pszEventName ) ) // passtime pass
  1208. {
  1209. PasstimeGameEvents::PassCaught ev( event );
  1210. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1211. int passerTeam = g_PR->GetTeam( ev.passerIndex );
  1212. int catcherTeam = g_PR->GetTeam( ev.catcherIndex );
  1213. //
  1214. // Pass or interception?
  1215. //
  1216. int killerIndex, victimIndex, killerTeam, victimTeam;
  1217. const char *pszDesc;
  1218. if ( passerTeam == catcherTeam )
  1219. {
  1220. // pass
  1221. killerIndex = ev.passerIndex;
  1222. killerTeam = passerTeam;
  1223. victimIndex = ev.catcherIndex;
  1224. victimTeam = catcherTeam;
  1225. pszDesc = "#Msg_PasstimePassComplete";
  1226. Q_strncpy( msg.szIcon, "d_passtime_pass", ARRAYSIZE(msg.szIcon) );
  1227. }
  1228. else
  1229. {
  1230. // interception
  1231. victimIndex = ev.passerIndex;
  1232. victimTeam = passerTeam;
  1233. killerIndex = ev.catcherIndex;
  1234. killerTeam = catcherTeam;
  1235. pszDesc = "#Msg_PasstimeInterception";
  1236. Q_strncpy( msg.szIcon, "d_passtime_intercept", ARRAYSIZE(msg.szIcon) );
  1237. }
  1238. // killer
  1239. const char *szPlayerName = g_PR->GetPlayerName( killerIndex );
  1240. Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
  1241. msg.Killer.iTeam = killerTeam;
  1242. // victim
  1243. szPlayerName = g_PR->GetPlayerName( victimIndex );
  1244. Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
  1245. msg.Victim.iTeam = victimTeam;
  1246. V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find( pszDesc ), sizeof( msg.wzInfoText ) );
  1247. // flags
  1248. int localPlayerIndex = GetLocalPlayerIndex();
  1249. msg.bLocalPlayerInvolved = (localPlayerIndex == ev.catcherIndex)
  1250. || (localPlayerIndex == ev.passerIndex);
  1251. }
  1252. else if ( FStrEq( PasstimeGameEvents::BallBlocked::s_eventName, pszEventName ) ) // passtime ball stolen
  1253. {
  1254. PasstimeGameEvents::BallBlocked ev( event );
  1255. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1256. // blocker
  1257. const char *szPlayerName = g_PR->GetPlayerName( ev.blockerIndex );
  1258. Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
  1259. msg.Killer.iTeam = g_PR->GetTeam( ev.blockerIndex );
  1260. // owner
  1261. szPlayerName = g_PR->GetPlayerName( ev.ownerIndex );
  1262. Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
  1263. msg.Victim.iTeam = g_PR->GetTeam( ev.ownerIndex );
  1264. V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeBlock"), sizeof( msg.wzInfoText ) );
  1265. // flags
  1266. int localPlayerIndex = GetLocalPlayerIndex();
  1267. msg.bLocalPlayerInvolved = (localPlayerIndex == ev.blockerIndex)
  1268. || (localPlayerIndex == ev.ownerIndex);
  1269. // icon
  1270. Q_strncpy( msg.szIcon, "d_ball", ARRAYSIZE(msg.szIcon) );
  1271. }
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Purpose: Adds an additional death message
  1275. //-----------------------------------------------------------------------------
  1276. void CTFHudDeathNotice::AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey )
  1277. {
  1278. DeathNoticeItem &msg2 = m_DeathNotices[AddDeathNoticeItem()];
  1279. Q_strncpy( msg2.Killer.szName, g_PR->GetPlayerName( iKillerID ), ARRAYSIZE( msg2.Killer.szName ) );
  1280. msg2.Killer.iTeam = g_PR->GetTeam( iKillerID );
  1281. Q_strncpy( msg2.Victim.szName, g_PR->GetPlayerName( iVictimID ), ARRAYSIZE( msg2.Victim.szName ) );
  1282. msg2.Victim.iTeam = g_PR->GetTeam( iVictimID );
  1283. const wchar_t *wzMsg = g_pVGuiLocalize->Find( pMsgKey );
  1284. if ( wzMsg )
  1285. {
  1286. V_wcsncpy( msg2.wzInfoText, wzMsg, sizeof( msg2.wzInfoText ) );
  1287. }
  1288. msg2.iconDeath = m_iconDomination;
  1289. int iLocalPlayerIndex = GetLocalPlayerIndex();
  1290. if ( iLocalPlayerIndex == iVictimID || iLocalPlayerIndex == iKillerID )
  1291. {
  1292. msg2.bLocalPlayerInvolved = true;
  1293. }
  1294. }
  1295. //-----------------------------------------------------------------------------
  1296. void CTFHudDeathNotice::AddStreakMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iKillerStreak, int iStreakIncrement, int iVictimID, int iDeathNoticeMsg )
  1297. {
  1298. int nMinStreak = MinStreakForType( eStreakType );
  1299. if ( iKillerStreak < nMinStreak )
  1300. return;
  1301. if ( !m_pStreakNotice )
  1302. return;
  1303. if ( cl_hud_killstreak_display_time.GetInt() <= 0 )
  1304. return;
  1305. m_pStreakNotice->StreakUpdated( eStreakType, iKillerID, iKillerStreak, iStreakIncrement );
  1306. }
  1307. //-----------------------------------------------------------------------------
  1308. void CTFHudDeathNotice::AddStreakEndedMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iVictimStreak, int iDeathNoticeMsg )
  1309. {
  1310. int nMinStreak = MinStreakForType( eStreakType );
  1311. if ( iVictimStreak < nMinStreak )
  1312. return;
  1313. if ( !m_pStreakNotice )
  1314. return;
  1315. if ( cl_hud_killstreak_display_time.GetInt() <= 0 )
  1316. return;
  1317. m_pStreakNotice->StreakEnded( eStreakType, iKillerID, iVictimID, iVictimStreak );
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Purpose: returns the color to draw text in for this team.
  1321. //-----------------------------------------------------------------------------
  1322. Color CTFHudDeathNotice::GetTeamColor( int iTeamNumber, bool bLocalPlayerInvolved /* = false */ )
  1323. {
  1324. switch ( iTeamNumber )
  1325. {
  1326. case TF_TEAM_BLUE:
  1327. return m_clrBlueText;
  1328. break;
  1329. case TF_TEAM_RED:
  1330. return m_clrRedText;
  1331. break;
  1332. case TEAM_UNASSIGNED:
  1333. if ( bLocalPlayerInvolved )
  1334. return m_clrLocalPlayerText;
  1335. else
  1336. return Color( 255, 255, 255, 255 );
  1337. break;
  1338. case TF_TEAM_HALLOWEEN:
  1339. if ( TFGameRules() && ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) || TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) ) )
  1340. {
  1341. return m_clrGreenText;
  1342. }
  1343. else
  1344. {
  1345. return m_clrPurpleText;
  1346. }
  1347. break;
  1348. default:
  1349. AssertOnce( false ); // invalid team
  1350. return Color( 255, 255, 255, 255 );
  1351. break;
  1352. }
  1353. }
  1354. //-----------------------------------------------------------------------------
  1355. // Purpose:
  1356. //-----------------------------------------------------------------------------
  1357. Color CTFHudDeathNotice::GetInfoTextColor( int iDeathNoticeMsg )
  1358. {
  1359. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1360. if ( msg.bLocalPlayerInvolved )
  1361. return m_clrLocalPlayerText;
  1362. return Color( 255, 255, 255, 255 );
  1363. }
  1364. //-----------------------------------------------------------------------------
  1365. Color CTFHudDeathNotice::GetBackgroundColor ( int iDeathNoticeMsg )
  1366. {
  1367. DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
  1368. return msg.bLocalPlayerInvolved ? m_clrLocalBGColor : m_clrBaseBGColor;
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose:
  1372. //-----------------------------------------------------------------------------
  1373. int CTFHudDeathNotice::UseExistingNotice( IGameEvent *event )
  1374. {
  1375. // Fish Notices and Throwables
  1376. // Add check for all throwables
  1377. int iTarget = event->GetInt( "weaponid" );
  1378. if (iTarget == TF_WEAPON_BAT_FISH || iTarget == TF_WEAPON_THROWABLE || iTarget == TF_WEAPON_GRENADE_THROWABLE )
  1379. {
  1380. // Look for a matching pre-existing notice.
  1381. for ( int i=0; i<m_DeathNotices.Count(); ++i )
  1382. {
  1383. DeathNoticeItem &msg = m_DeathNotices[i];
  1384. if ( msg.iWeaponID != iTarget )
  1385. continue;
  1386. if ( msg.iKillerID != event->GetInt( "attacker" ) )
  1387. continue;
  1388. if ( msg.iVictimID != event->GetInt( "userid" ) )
  1389. continue;
  1390. return i;
  1391. }
  1392. }
  1393. return BaseClass::UseExistingNotice( event );
  1394. }
  1395. //-----------------------------------------------------------------------------
  1396. CHudTexture* CTFHudDeathNotice::GetMannPowerIcon( RuneTypes_t tRuneType, bool bIsRedTeam )
  1397. {
  1398. // Red team is normal file and blue is dNeg file
  1399. switch ( tRuneType )
  1400. {
  1401. case RUNE_STRENGTH: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_strength" ) : gHUD.GetIcon( "dneg_mannpower_strength" );
  1402. case RUNE_HASTE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_haste" ) : gHUD.GetIcon( "dneg_mannpower_haste" );
  1403. case RUNE_REGEN: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_regen" ) : gHUD.GetIcon( "dneg_mannpower_regen" );
  1404. case RUNE_RESIST: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_resist" ) : gHUD.GetIcon( "dneg_mannpower_resist" );
  1405. case RUNE_VAMPIRE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_vamp" ) : gHUD.GetIcon( "dneg_mannpower_vamp" );
  1406. case RUNE_REFLECT: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_reflect" ) : gHUD.GetIcon( "dneg_mannpower_reflect" );
  1407. case RUNE_PRECISION: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_precision" ) : gHUD.GetIcon( "dneg_mannpower_precision" );
  1408. case RUNE_AGILITY: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_agility" ) : gHUD.GetIcon( "dneg_mannpower_agility" );
  1409. case RUNE_KNOCKOUT: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_fist" ) : gHUD.GetIcon( "dneg_mannpower_fist" );
  1410. case RUNE_KING: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_king" ) : gHUD.GetIcon( "dneg_mannpower_king" );
  1411. case RUNE_PLAGUE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_plague" ) : gHUD.GetIcon( "dneg_mannpower_plague" );
  1412. case RUNE_SUPERNOVA: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_supernova" ) : gHUD.GetIcon( "dneg_mannpower_supernova" );
  1413. default: return NULL;
  1414. }
  1415. return NULL;
  1416. }