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.

941 lines
27 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Draws DoD:S'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 <KeyValues.h>
  17. #include "c_baseplayer.h"
  18. #include "c_team.h"
  19. #include "dod_shareddefs.h"
  20. #include "clientmode_dod.h"
  21. #include "c_dod_player.h"
  22. #include "c_dod_playerresource.h"
  23. #include "c_dod_objective_resource.h"
  24. #include "dod_hud_freezepanel.h"
  25. #include "engine/IEngineSound.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 );
  29. ConVar cl_deathicon_width( "cl_deathicon_width", "57" );
  30. ConVar cl_deathicon_height( "cl_deathicon_height", "18" );
  31. #define MAX_DEATHNOTICE_NAME_LENGTH 128 // to hold multiple player cappers
  32. // a very useful function for getting the ideal scale factor of a sprite that's to be
  33. // scaled into a space
  34. float GetScale( int nIconWidth, int nIconHeight, int nWidth, int nHeight );
  35. // Player entries in a death notice
  36. struct DeathNoticePlayer
  37. {
  38. char szName[MAX_DEATHNOTICE_NAME_LENGTH];
  39. int iEntIndex;
  40. };
  41. // Contents of each entry in our list of death notices
  42. struct DeathNoticeItem
  43. {
  44. DeathNoticeItem()
  45. {
  46. iconDeath = NULL;
  47. bSuicide = false;
  48. bCapMsg = false;
  49. bLocalPlayerInvolved = false;
  50. bDefense = false;
  51. bDominating = false;
  52. }
  53. DeathNoticePlayer Killer;
  54. DeathNoticePlayer Victim;
  55. CHudTexture *iconDeath;
  56. bool bSuicide;
  57. float flDisplayTime;
  58. // When I see a boolean like this, I know serious bullshit is afoot!
  59. bool bCapMsg; // if this is set, this is a flag cap msg.
  60. // Killer.szName is the list of players that capped
  61. // Victim.szName is the localized point name
  62. // iMaterial is the material index of the flag icon to show
  63. // iEntIndex in Killer is the capping team
  64. int iMaterial;
  65. bool bLocalPlayerInvolved; // Is the local player a capper, killer or victim in this message
  66. bool bDefense;
  67. bool bDominating;
  68. wchar_t wzInfoText[32]; // any additional text to display next to icon
  69. };
  70. //-----------------------------------------------------------------------------
  71. // Purpose:
  72. //-----------------------------------------------------------------------------
  73. class CHudDeathNotice : public CHudElement, public vgui::Panel
  74. {
  75. DECLARE_CLASS_SIMPLE( CHudDeathNotice, vgui::Panel );
  76. public:
  77. CHudDeathNotice( const char *pElementName );
  78. void Init( void );
  79. void VidInit( void );
  80. virtual bool ShouldDraw( void );
  81. virtual void Paint( void );
  82. virtual void ApplySchemeSettings( vgui::IScheme *scheme );
  83. void SetColorForNoticePlayer( int iTeamNumber );
  84. void RetireExpiredDeathNotices( void );
  85. void FireGameEvent( IGameEvent * event);
  86. void DrawBackgroundBox( int x, int y, int w, int h, bool bLocalPlayerInvolved );
  87. int DrawDefenseItem( DeathNoticeItem *pItem, int xRight, int y );
  88. int DrawDeathNoticeItem( DeathNoticeItem *pItem, int x, int y );
  89. int DrawDominationNoticeItem( DeathNoticeItem *pItem, int xRight, int y );
  90. virtual bool IsVisible( void );
  91. void AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey );
  92. void PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType );
  93. private:
  94. CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" );
  95. CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" );
  96. CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" );
  97. CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "255 255 255 100" );
  98. CPanelAnimationVar( Color, m_ActiveBackgroundColor, "ActiveBackgroundColor", "255 255 255 140" );
  99. // Special death notice icons
  100. CHudTexture *m_iconD_skull;
  101. CHudTexture *m_pIconDefended;
  102. CHudTexture *m_iconDomination;
  103. CUtlVector<DeathNoticeItem> m_DeathNotices;
  104. int m_iMaterialTexture;
  105. };
  106. using namespace vgui;
  107. DECLARE_HUDELEMENT( CHudDeathNotice );
  108. //-----------------------------------------------------------------------------
  109. // Purpose:
  110. //-----------------------------------------------------------------------------
  111. CHudDeathNotice::CHudDeathNotice( const char *pElementName ) :
  112. CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" )
  113. {
  114. vgui::Panel *pParent = g_pClientMode->GetViewport();
  115. SetParent( pParent );
  116. m_iconD_skull = NULL;
  117. m_iconDomination = NULL;
  118. SetHiddenBits( HIDEHUD_MISCSTATUS );
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme )
  124. {
  125. BaseClass::ApplySchemeSettings( scheme );
  126. SetPaintBackgroundEnabled( false );
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CHudDeathNotice::Init( void )
  132. {
  133. ListenForGameEvent( "player_death" );
  134. ListenForGameEvent( "dod_point_captured" );
  135. ListenForGameEvent( "dod_capture_blocked" );
  136. m_iMaterialTexture = vgui::surface()->CreateNewTextureID();
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose:
  140. //-----------------------------------------------------------------------------
  141. void CHudDeathNotice::VidInit( void )
  142. {
  143. m_iconD_skull = gHUD.GetIcon( "d_skull_dod" );
  144. m_pIconDefended = gHUD.GetIcon( "icon_defended" );
  145. m_iconDomination = gHUD.GetIcon( "leaderboard_dominated" );
  146. m_DeathNotices.Purge();
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: Draw if we've got at least one death notice in the queue
  150. //-----------------------------------------------------------------------------
  151. bool CHudDeathNotice::ShouldDraw( void )
  152. {
  153. return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) );
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose: Hide if we just took a freezecam screenshot
  157. //-----------------------------------------------------------------------------
  158. bool CHudDeathNotice::IsVisible( void )
  159. {
  160. if ( IsTakingAFreezecamScreenshot() )
  161. return false;
  162. return BaseClass::IsVisible();
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. //-----------------------------------------------------------------------------
  167. void CHudDeathNotice::SetColorForNoticePlayer( int iTeamNumber )
  168. {
  169. Color c = g_PR->GetTeamColor( iTeamNumber );
  170. surface()->DrawSetTextColor( c );
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose:
  174. //-----------------------------------------------------------------------------
  175. void CHudDeathNotice::Paint()
  176. {
  177. int yStart = GetClientModeDODNormal()->GetDeathMessageStartHeight();
  178. surface()->DrawSetTextFont( m_hTextFont );
  179. int y = yStart;
  180. int x = GetWide();
  181. int iCount = m_DeathNotices.Count();
  182. for ( int i = 0; i < iCount; i++ )
  183. {
  184. if ( m_DeathNotices[i].bDefense )
  185. y += DrawDefenseItem( &m_DeathNotices[i], x, y );
  186. else
  187. y += DrawDeathNoticeItem( &m_DeathNotices[i], x, y );
  188. }
  189. // Now retire any death notices that have expired
  190. RetireExpiredDeathNotices();
  191. }
  192. int CHudDeathNotice::DrawDefenseItem( DeathNoticeItem *pItem, int xRight, int y )
  193. {
  194. // Get the team numbers for the players involved
  195. int iKillerTeam = pItem->Killer.iEntIndex;
  196. int iVictimTeam = TEAM_UNASSIGNED;
  197. wchar_t victim[ 256 ];
  198. wchar_t killer[ 256 ];
  199. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) );
  200. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) );
  201. // Get the local position for this notice
  202. int len = UTIL_ComputeStringWidth( m_hTextFont, victim );
  203. int iconWide;
  204. int iconTall;
  205. float scale = ( (float)ScreenHeight() / 480.0f ) * 0.6; //scale based on 800x600
  206. iconWide = iconTall = (int)( scale * 16.0 );
  207. int iconDefSize = (int)( scale * 32.0 );
  208. int spacerX = XRES(5);
  209. int x = xRight - len - spacerX - iconWide - XRES(10);
  210. x -= iconDefSize;
  211. surface()->DrawSetTextFont( m_hTextFont );
  212. int iFontTall = vgui::surface()->GetFontTall( m_hTextFont );
  213. int yText = y + ( iconDefSize - iFontTall ) / 2;
  214. int boxWidth = len + iconWide + spacerX;
  215. boxWidth += iconDefSize;
  216. int boxHeight = m_flLineHeight;
  217. int boxBorder = XRES(2);
  218. // Draw Defender's name
  219. int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap
  220. x -= nameWidth;
  221. boxWidth += nameWidth;
  222. DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved );
  223. SetColorForNoticePlayer( iKillerTeam );
  224. // Draw killer's name
  225. surface()->DrawSetTextPos( x, yText );
  226. surface()->DrawUnicodeString( killer );
  227. surface()->DrawGetTextPos( x, yText );
  228. x += spacerX;
  229. Color iconColor( 255, 80, 0, 255 );
  230. // Draw shield + cap icon
  231. m_pIconDefended->DrawSelf( x, y, iconDefSize, iconDefSize, Color(255,255,255,255) );
  232. x += iconDefSize + spacerX;
  233. const char *szMatName = GetMaterialNameFromIndex( pItem->iMaterial );
  234. vgui::surface()->DrawSetColor( Color(255,255,255,255) );
  235. vgui::surface()->DrawSetTextureFile( m_iMaterialTexture, szMatName, true, false);
  236. int iconY = y + iconDefSize / 2 - iconTall / 2;
  237. vgui::surface()->DrawTexturedRect( x, iconY, x + iconWide, iconY + iconTall );
  238. x += iconWide;
  239. SetColorForNoticePlayer( iVictimTeam );
  240. // Draw location name
  241. surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
  242. surface()->DrawSetTextPos( x, yText );
  243. surface()->DrawUnicodeString( victim );
  244. // return height of this item
  245. // base spacing on the height of the background box
  246. return boxHeight + boxBorder*2 + YRES(4);
  247. }
  248. // X is right side, do a right align!
  249. int CHudDeathNotice::DrawDeathNoticeItem( DeathNoticeItem *pItem, int xRight, int y )
  250. {
  251. if ( pItem->bDominating )
  252. {
  253. return DrawDominationNoticeItem( pItem, xRight, y );
  254. }
  255. bool bCapMsg = pItem->bCapMsg;
  256. // Get the team numbers for the players involved
  257. int iKillerTeam = TEAM_UNASSIGNED;
  258. int iVictimTeam = TEAM_UNASSIGNED;
  259. if ( bCapMsg )
  260. {
  261. iKillerTeam = pItem->Killer.iEntIndex;
  262. iVictimTeam = TEAM_UNASSIGNED;
  263. }
  264. else
  265. {
  266. if( g_PR )
  267. {
  268. iKillerTeam = g_PR->GetTeam( pItem->Killer.iEntIndex );
  269. iVictimTeam = g_PR->GetTeam( pItem->Victim.iEntIndex );
  270. }
  271. }
  272. wchar_t victim[ 256 ];
  273. wchar_t killer[ 256 ];
  274. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) );
  275. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) );
  276. // Get the local position for this notice
  277. int len = UTIL_ComputeStringWidth( m_hTextFont, victim );
  278. int iconWide;
  279. int iconTall;
  280. CHudTexture *icon = pItem->iconDeath;
  281. Assert( icon );
  282. if ( bCapMsg )
  283. {
  284. float scale = ( (float)ScreenHeight() / 480.0f ) * 0.6; //scale based on 800x600
  285. iconWide = iconTall = (int)( scale * 32.0 );
  286. }
  287. else
  288. {
  289. if ( !icon )
  290. return 0;
  291. if( icon->bRenderUsingFont )
  292. {
  293. iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
  294. iconTall = surface()->GetFontTall( icon->hFont );
  295. }
  296. else
  297. {
  298. float scale = GetScale( icon->Width(), icon->Height(), XRES(cl_deathicon_width.GetInt()), YRES(cl_deathicon_height.GetInt()) );
  299. iconWide = (int)( scale * (float)icon->Width() );
  300. iconTall = (int)( scale * (float)icon->Height() );
  301. }
  302. }
  303. int spacerX = XRES(5);
  304. int x = xRight - len - spacerX - iconWide - XRES(10);
  305. if ( pItem->bDefense )
  306. {
  307. x -= iconWide; //m_iDefendedIconSize;
  308. }
  309. surface()->DrawSetTextFont( m_hTextFont );
  310. int iFontTall = vgui::surface()->GetFontTall( m_hTextFont );
  311. int boxWidth = len + iconWide + spacerX;
  312. if ( pItem->bDefense )
  313. {
  314. boxWidth += iconWide; //m_iDefendedIconSize;
  315. }
  316. int boxHeight = m_flLineHeight; //MIN( iconTall, m_flLineHeight );
  317. int boxBorder = XRES(2);
  318. int yText = y + ( m_flLineHeight - iFontTall ) / 2;
  319. int yIcon = y + ( m_flLineHeight - iconTall ) / 2;
  320. // Only draw killers name if it wasn't a suicide
  321. if ( !pItem->bSuicide )
  322. {
  323. int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap
  324. x -= nameWidth;
  325. boxWidth += nameWidth;
  326. DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved );
  327. SetColorForNoticePlayer( iKillerTeam );
  328. // Draw killer's name
  329. surface()->DrawSetTextPos( x, yText );
  330. const wchar_t *p = killer;
  331. while ( *p )
  332. {
  333. surface()->DrawUnicodeChar( *p++ );
  334. }
  335. surface()->DrawGetTextPos( x, yText );
  336. x += spacerX;
  337. }
  338. else
  339. {
  340. DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved );
  341. }
  342. Color iconColor( 255, 80, 0, 255 );
  343. // Draw death weapon or cap icon
  344. if ( bCapMsg )
  345. {
  346. const char *szMatName = GetMaterialNameFromIndex( pItem->iMaterial );
  347. vgui::surface()->DrawSetColor( Color(255,255,255,255) );
  348. vgui::surface()->DrawSetTextureFile( m_iMaterialTexture, szMatName, true, false);
  349. vgui::surface()->DrawTexturedRect( x, yIcon, x + iconWide, yIcon + iconTall );
  350. x += iconWide + spacerX;
  351. }
  352. else
  353. {
  354. //If we're using a font char, this will ignore iconTall and iconWide
  355. icon->DrawSelf( x, yIcon, iconWide, iconTall, iconColor );
  356. x += iconWide + spacerX;
  357. }
  358. SetColorForNoticePlayer( iVictimTeam );
  359. // Draw victims name
  360. surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
  361. surface()->DrawSetTextPos( x, yText );
  362. const wchar_t *p = victim;
  363. while ( *p )
  364. {
  365. surface()->DrawUnicodeChar( *p++ );
  366. }
  367. // return height of this item
  368. // base spacing on the height of the background box
  369. return boxHeight + boxBorder*2 + YRES(4);
  370. }
  371. int CHudDeathNotice::DrawDominationNoticeItem( DeathNoticeItem *pItem, int xRight, int y )
  372. {
  373. // Get the team numbers for the players involved
  374. int iKillerTeam = TEAM_UNASSIGNED;
  375. int iVictimTeam = TEAM_UNASSIGNED;
  376. if( g_PR )
  377. {
  378. iKillerTeam = g_PR->GetTeam( pItem->Killer.iEntIndex );
  379. iVictimTeam = g_PR->GetTeam( pItem->Victim.iEntIndex );
  380. }
  381. wchar_t victim[ 256 ];
  382. wchar_t killer[ 256 ];
  383. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) );
  384. g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) );
  385. // Get the local position for this notice
  386. int len = UTIL_ComputeStringWidth( m_hTextFont, victim );
  387. int iconWide;
  388. int iconTall;
  389. Assert( pItem->iconDeath );
  390. CHudTexture *icon = pItem->iconDeath;
  391. if ( !icon )
  392. return 0;
  393. if( icon->bRenderUsingFont )
  394. {
  395. iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
  396. iconTall = surface()->GetFontTall( icon->hFont );
  397. }
  398. else
  399. {
  400. float scale = GetScale( icon->Width(), icon->Height(), XRES(cl_deathicon_width.GetInt()), YRES(cl_deathicon_height.GetInt()) );
  401. iconWide = (int)( scale * (float)icon->Width() );
  402. iconTall = (int)( scale * (float)icon->Height() );
  403. }
  404. int spacerX = XRES(5);
  405. int x = xRight - len - spacerX - iconWide - XRES(10);
  406. surface()->DrawSetTextFont( m_hTextFont );
  407. int iFontTall = vgui::surface()->GetFontTall( m_hTextFont );
  408. int boxWidth = len + iconWide + spacerX;
  409. int iDominatingLen = UTIL_ComputeStringWidth( m_hTextFont, pItem->wzInfoText ) + XRES(2);
  410. x -= iDominatingLen;
  411. boxWidth += iDominatingLen;
  412. int boxHeight = m_flLineHeight; //MIN( iconTall, m_flLineHeight );
  413. int boxBorder = XRES(2);
  414. int yText = y + ( m_flLineHeight - iFontTall ) / 2;
  415. int yIcon = y + ( m_flLineHeight - iconTall ) / 2;
  416. int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap
  417. x -= nameWidth;
  418. boxWidth += nameWidth;
  419. DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved );
  420. SetColorForNoticePlayer( iKillerTeam );
  421. // Draw killer's name
  422. surface()->DrawSetTextPos( x, yText );
  423. const wchar_t *p = killer;
  424. while ( *p )
  425. {
  426. surface()->DrawUnicodeChar( *p++ );
  427. }
  428. surface()->DrawGetTextPos( x, yText );
  429. x += spacerX;
  430. Color iconColor( 255, 80, 0, 255 );
  431. //If we're using a font char, this will ignore iconTall and iconWide
  432. icon->DrawSelf( x, yIcon, iconWide, iconTall, iconColor );
  433. x += iconWide + spacerX;
  434. surface()->DrawSetTextColor( Color(255,255,255,255) );
  435. // Draw dominating string
  436. surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
  437. surface()->DrawSetTextPos( x, yText );
  438. p = pItem->wzInfoText;
  439. while ( *p )
  440. {
  441. surface()->DrawUnicodeChar( *p++ );
  442. }
  443. x += iDominatingLen;
  444. SetColorForNoticePlayer( iVictimTeam );
  445. // Draw victims name
  446. //surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
  447. surface()->DrawSetTextPos( x, yText );
  448. p = victim;
  449. while ( *p )
  450. {
  451. surface()->DrawUnicodeChar( *p++ );
  452. }
  453. // return height of this item
  454. // base spacing on the height of the background box
  455. return boxHeight + boxBorder*2 + YRES(4);
  456. }
  457. ConVar cl_deathicon_bg_alpha( "cl_deathicon_bg_alpha", "1.0" );
  458. void CHudDeathNotice::DrawBackgroundBox( int x, int y, int w, int h, bool bLocalPlayerInvolved )
  459. {
  460. Panel::DrawBox( x, y, w, h,
  461. bLocalPlayerInvolved ? m_ActiveBackgroundColor : m_BackgroundColor,
  462. cl_deathicon_bg_alpha.GetFloat() );
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: This message handler may be better off elsewhere
  466. //-----------------------------------------------------------------------------
  467. void CHudDeathNotice::RetireExpiredDeathNotices( void )
  468. {
  469. // Loop backwards because we might remove one
  470. int iSize = m_DeathNotices.Size();
  471. for ( int i = iSize-1; i >= 0; i-- )
  472. {
  473. if ( m_DeathNotices[i].flDisplayTime < gpGlobals->curtime )
  474. {
  475. m_DeathNotices.Remove(i);
  476. }
  477. }
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose: Server's told us that someone's died
  481. //-----------------------------------------------------------------------------
  482. void CHudDeathNotice::FireGameEvent( IGameEvent * event)
  483. {
  484. if (!g_PR)
  485. return;
  486. if ( hud_deathnotice_time.GetFloat() == 0 )
  487. return;
  488. C_DODPlayer *pLocal = C_DODPlayer::GetLocalDODPlayer();
  489. Assert( pLocal );
  490. if ( !pLocal )
  491. return;
  492. int iLocalPlayerIndex = pLocal->entindex();
  493. const char *pEventName = event->GetName();
  494. if ( Q_strcmp( "dod_point_captured", pEventName ) == 0 )
  495. {
  496. // Cap point index
  497. int cp = event->GetInt( "cp", -1 );
  498. Assert( cp >= 0 );
  499. // Cap point name ( MATTTODO: can't we find this from the point index ? )
  500. const char *pName = event->GetString( "cpname", "Unnamed Control Point" );
  501. const wchar_t *pBuf = g_pVGuiLocalize->Find( pName );
  502. // Array of capper indeces
  503. const char *cappers = event->GetString("cappers");
  504. DeathNoticeItem capMsg;
  505. capMsg.bCapMsg = true;
  506. capMsg.bSuicide = false;
  507. capMsg.bDefense = false;
  508. capMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
  509. capMsg.bLocalPlayerInvolved = false;
  510. char szCappers[256];
  511. szCappers[0] = '\0';
  512. int len = Q_strlen(cappers);
  513. for( int i=0;i<len;i++ )
  514. {
  515. int iPlayerIndex = (int)cappers[i];
  516. if ( iPlayerIndex == iLocalPlayerIndex )
  517. capMsg.bLocalPlayerInvolved = true;
  518. Assert( iPlayerIndex > 0 && iPlayerIndex <= gpGlobals->maxClients );
  519. const char *pPlayerName = g_PR->GetPlayerName( iPlayerIndex );
  520. if ( i == 0 )
  521. {
  522. // use first player as the team
  523. capMsg.Killer.iEntIndex = g_PR->GetTeam( iPlayerIndex );
  524. capMsg.iMaterial = g_pObjectiveResource->GetIconForTeam( cp, capMsg.Killer.iEntIndex );
  525. if ( g_pObjectiveResource->GetBombsRequired( cp ) > 0 )
  526. {
  527. capMsg.iMaterial = g_pObjectiveResource->GetCPBombedIcon( cp );
  528. }
  529. }
  530. else
  531. {
  532. Q_strncat( szCappers, ", ", sizeof(szCappers), 2 );
  533. }
  534. Q_strncat( szCappers, pPlayerName, sizeof(szCappers), COPY_ALL_CHARACTERS );
  535. }
  536. Q_strncpy( capMsg.Killer.szName, szCappers, sizeof(capMsg.Killer.szName) );
  537. if ( pBuf )
  538. {
  539. g_pVGuiLocalize->ConvertUnicodeToANSI( pBuf, capMsg.Victim.szName, sizeof(capMsg.Victim.szName) );
  540. }
  541. else
  542. {
  543. Q_strncpy( capMsg.Victim.szName, pName, sizeof(capMsg.Victim.szName) );
  544. }
  545. // Do we have too many death messages in the queue?
  546. if ( m_DeathNotices.Count() > 0 &&
  547. m_DeathNotices.Count() >= (int)m_flMaxDeathNotices )
  548. {
  549. // Remove the oldest one in the queue, which will always be the first
  550. m_DeathNotices.Remove(0);
  551. }
  552. m_DeathNotices.AddToTail( capMsg );
  553. // print a log message
  554. char szLogMsg[512];
  555. Q_snprintf( szLogMsg, sizeof( szLogMsg ), "%s captured %s for the %s\n",
  556. capMsg.Killer.szName,
  557. capMsg.Victim.szName,
  558. capMsg.Killer.iEntIndex == TEAM_ALLIES ? "U.S. Army" : "Wermacht" );
  559. Msg( "%s",szLogMsg );
  560. }
  561. else if ( Q_strcmp( "dod_capture_blocked", pEventName ) == 0 )
  562. {
  563. // Cap point index
  564. int cp = event->GetInt( "cp", -1 );
  565. Assert( cp >= 0 );
  566. // Cap point name
  567. const char *pName = event->GetString( "cpname", "Unnamed Control Point" );
  568. const wchar_t *pBuf = g_pVGuiLocalize->Find( pName );
  569. // A single blocker entindex
  570. int iBlocker = event->GetInt("blocker");
  571. DeathNoticeItem capMsg;
  572. capMsg.bCapMsg = true;
  573. capMsg.bSuicide = false;
  574. capMsg.bDefense = true;
  575. capMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
  576. capMsg.bLocalPlayerInvolved = false;
  577. capMsg.Killer.iEntIndex = g_PR->GetTeam( iBlocker );
  578. capMsg.iMaterial = g_pObjectiveResource->GetIconForTeam( cp, capMsg.Killer.iEntIndex );
  579. if ( iBlocker == iLocalPlayerIndex )
  580. capMsg.bLocalPlayerInvolved = true;
  581. Q_strncpy( capMsg.Killer.szName, g_PR->GetPlayerName( iBlocker ), sizeof(capMsg.Killer.szName) );
  582. char buf[128];
  583. if ( pBuf )
  584. {
  585. g_pVGuiLocalize->ConvertUnicodeToANSI( pBuf, buf, sizeof(buf) );
  586. pName = buf;
  587. }
  588. Q_snprintf( capMsg.Victim.szName, sizeof(capMsg.Victim.szName), " - %s", pName );
  589. // Do we have too many death messages in the queue?
  590. if ( m_DeathNotices.Count() > 0 &&
  591. m_DeathNotices.Count() >= (int)m_flMaxDeathNotices )
  592. {
  593. // Remove the oldest one in the queue, which will always be the first
  594. m_DeathNotices.Remove(0);
  595. }
  596. m_DeathNotices.AddToTail( capMsg );
  597. }
  598. else if ( Q_strcmp( "player_death", pEventName ) == 0 )
  599. {
  600. int killer = engine->GetPlayerForUserID( event->GetInt("attacker") );
  601. int victim = engine->GetPlayerForUserID( event->GetInt("userid") );
  602. const char *killedwith = event->GetString( "weapon" );
  603. char fullkilledwith[128];
  604. if ( killedwith && *killedwith )
  605. {
  606. Q_snprintf( fullkilledwith, sizeof(fullkilledwith), "d_%s", killedwith );
  607. }
  608. else
  609. {
  610. fullkilledwith[0] = 0;
  611. }
  612. // Do we have too many death messages in the queue?
  613. if ( m_DeathNotices.Count() > 0 &&
  614. m_DeathNotices.Count() >= (int)m_flMaxDeathNotices )
  615. {
  616. // Remove the oldest one in the queue, which will always be the first
  617. m_DeathNotices.Remove(0);
  618. }
  619. // Get the names of the players
  620. const char *killer_name = g_PR->GetPlayerName( killer );
  621. const char *victim_name = g_PR->GetPlayerName( victim );
  622. if ( !killer_name )
  623. killer_name = "";
  624. if ( !victim_name )
  625. victim_name = "";
  626. // Make a new death notice
  627. DeathNoticeItem deathMsg;
  628. deathMsg.Killer.iEntIndex = killer;
  629. deathMsg.Victim.iEntIndex = victim;
  630. Q_strncpy( deathMsg.Killer.szName, killer_name, MAX_PLAYER_NAME_LENGTH );
  631. Q_strncpy( deathMsg.Victim.szName, victim_name, MAX_PLAYER_NAME_LENGTH );
  632. deathMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
  633. deathMsg.bSuicide = ( !killer || killer == victim );
  634. deathMsg.bCapMsg = false;
  635. deathMsg.bDefense = false;
  636. deathMsg.iMaterial = -1;
  637. deathMsg.bLocalPlayerInvolved = ( killer == iLocalPlayerIndex || victim == iLocalPlayerIndex );
  638. // Try and find the death identifier in the icon list
  639. deathMsg.iconDeath = gHUD.GetIcon( fullkilledwith );
  640. if ( !deathMsg.iconDeath )
  641. {
  642. // Can't find it, so use the default skull & crossbones icon
  643. deathMsg.iconDeath = m_iconD_skull;
  644. }
  645. // Add it to our list of death notices
  646. m_DeathNotices.AddToTail( deathMsg );
  647. if ( event->GetInt( "dominated" ) > 0 )
  648. {
  649. AddAdditionalMsg( killer, victim, "#Msg_Dominating" );
  650. PlayRivalrySounds( killer, victim, DOD_DEATHFLAG_DOMINATION );
  651. }
  652. if ( event->GetInt( "revenge" ) > 0 )
  653. {
  654. AddAdditionalMsg( killer, victim, "#Msg_Revenge" );
  655. PlayRivalrySounds( killer, victim, DOD_DEATHFLAG_REVENGE );
  656. }
  657. char sDeathMsg[512];
  658. // Record the death notice in the console
  659. if ( deathMsg.bSuicide )
  660. {
  661. if ( !strcmp( fullkilledwith, "d_worldspawn" ) )
  662. {
  663. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.\n", deathMsg.Victim.szName );
  664. }
  665. else //d_world
  666. {
  667. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.\n", deathMsg.Victim.szName );
  668. }
  669. }
  670. else
  671. {
  672. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", deathMsg.Killer.szName, deathMsg.Victim.szName );
  673. if ( fullkilledwith && *fullkilledwith && (*fullkilledwith > 13 ) )
  674. {
  675. Q_strncat( sDeathMsg, VarArgs( " with %s.\n", fullkilledwith+2 ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
  676. }
  677. }
  678. Msg( "%s",sDeathMsg );
  679. }
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose: Adds an additional death message
  683. //-----------------------------------------------------------------------------
  684. void CHudDeathNotice::AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey )
  685. {
  686. int iMsg = m_DeathNotices.AddToTail();
  687. DeathNoticeItem &msg = m_DeathNotices[iMsg];
  688. msg.Killer.iEntIndex = iKillerID;
  689. msg.Victim.iEntIndex = iVictimID;
  690. Q_strncpy( msg.Killer.szName, g_PR->GetPlayerName( iKillerID ), ARRAYSIZE( msg.Killer.szName ) );
  691. Q_strncpy( msg.Victim.szName, g_PR->GetPlayerName( iVictimID ), ARRAYSIZE( msg.Victim.szName ) );
  692. msg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
  693. msg.bSuicide = false;
  694. msg.bCapMsg = false;
  695. msg.bDefense = false;
  696. msg.iMaterial = -1;
  697. msg.bDominating = true;
  698. const wchar_t *wzMsg = g_pVGuiLocalize->Find( pMsgKey );
  699. if ( wzMsg )
  700. {
  701. V_wcsncpy( msg.wzInfoText, wzMsg, sizeof( msg.wzInfoText ) );
  702. }
  703. msg.iconDeath = m_iconDomination;
  704. int iLocalPlayerIndex = GetLocalPlayerIndex();
  705. if ( iLocalPlayerIndex == iVictimID || iLocalPlayerIndex == iKillerID )
  706. {
  707. msg.bLocalPlayerInvolved = true;
  708. }
  709. }
  710. ConVar dod_playrivalrysounds( "dod_playrivalrysounds", "1", FCVAR_ARCHIVE );
  711. void CHudDeathNotice::PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType )
  712. {
  713. if ( dod_playrivalrysounds.GetBool() == false )
  714. return;
  715. int iLocalPlayerIndex = GetLocalPlayerIndex();
  716. //We're not involved in this kill
  717. if ( iKillerIndex != iLocalPlayerIndex && iVictimIndex != iLocalPlayerIndex )
  718. return;
  719. const char *pszSoundName = NULL;
  720. if ( iType == DOD_DEATHFLAG_DOMINATION )
  721. {
  722. if ( iKillerIndex == iLocalPlayerIndex )
  723. {
  724. pszSoundName = "Game.Domination";
  725. }
  726. else if ( iVictimIndex == iLocalPlayerIndex )
  727. {
  728. pszSoundName = "Game.Nemesis";
  729. }
  730. }
  731. else if ( iType == DOD_DEATHFLAG_REVENGE )
  732. {
  733. pszSoundName = "Game.Revenge";
  734. }
  735. CLocalPlayerFilter filter;
  736. C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName );
  737. }