Counter Strike : Global Offensive Source Code
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.

593 lines
21 KiB

  1. //========= Copyright � 1996-2005, 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 <KeyValues.h>
  17. #include <game_controls/baseviewport.h>
  18. #include "clientmode_shared.h"
  19. #include "c_baseplayer.h"
  20. #include "c_team.h"
  21. #include "tf_shareddefs.h"
  22. #include "hud_basedeathnotice.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. static ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 );
  26. using namespace vgui;
  27. //-----------------------------------------------------------------------------
  28. // Purpose:
  29. //-----------------------------------------------------------------------------
  30. CHudBaseDeathNotice::CHudBaseDeathNotice( const char *pElementName ) :
  31. CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" )
  32. {
  33. vgui::Panel *pParent = GetClientMode()->GetViewport();
  34. SetParent( pParent );
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose:
  38. //-----------------------------------------------------------------------------
  39. void CHudBaseDeathNotice::ApplySchemeSettings( IScheme *scheme )
  40. {
  41. BaseClass::ApplySchemeSettings( scheme );
  42. SetPaintBackgroundEnabled( false );
  43. CalcRoundedCorners();
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. //-----------------------------------------------------------------------------
  48. void CHudBaseDeathNotice::Init( void )
  49. {
  50. ListenForGameEvent( "player_death" );
  51. ListenForGameEvent( "object_destroyed" );
  52. ListenForGameEvent( "teamplay_point_captured" );
  53. ListenForGameEvent( "teamplay_capture_blocked" );
  54. ListenForGameEvent( "teamplay_flag_event" );
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. //-----------------------------------------------------------------------------
  59. void CHudBaseDeathNotice::VidInit( void )
  60. {
  61. m_DeathNotices.RemoveAll();
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Draw if we've got at least one death notice in the queue
  65. //-----------------------------------------------------------------------------
  66. bool CHudBaseDeathNotice::ShouldDraw( void )
  67. {
  68. return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) );
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose:
  72. //-----------------------------------------------------------------------------
  73. Color CHudBaseDeathNotice::GetTeamColor( int iTeamNumber )
  74. {
  75. // By default, return the standard team color. Subclasses may override this.
  76. return g_PR->GetTeamColor( iTeamNumber );
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. //-----------------------------------------------------------------------------
  81. void CHudBaseDeathNotice::Paint()
  82. {
  83. // Retire any death notices that have expired
  84. RetireExpiredDeathNotices();
  85. CBaseViewport *pViewport = dynamic_cast<CBaseViewport *>( GetClientModeNormal()->GetViewport() );
  86. int yStart = pViewport->GetDeathMessageStartHeight();
  87. surface()->DrawSetTextFont( m_hTextFont );
  88. int xMargin = XRES( 10 );
  89. int xSpacing = UTIL_ComputeStringWidth( m_hTextFont, L" " );
  90. int iCount = m_DeathNotices.Count();
  91. for ( int i = 0; i < iCount; i++ )
  92. {
  93. DeathNoticeItem &msg = m_DeathNotices[i];
  94. CHudTexture *icon = msg.iconDeath;
  95. wchar_t victim[256]=L"";
  96. wchar_t killer[256]=L"";
  97. // TEMP - print the death icon name if we don't have a material for it
  98. g_pVGuiLocalize->ConvertANSIToUnicode( msg.Victim.szName, victim, sizeof( victim ) );
  99. g_pVGuiLocalize->ConvertANSIToUnicode( msg.Killer.szName, killer, sizeof( killer ) );
  100. int iVictimTextWide = UTIL_ComputeStringWidth( m_hTextFont, victim ) + xSpacing;
  101. int iDeathInfoTextWide= msg.wzInfoText[0] ? UTIL_ComputeStringWidth( m_hTextFont, msg.wzInfoText ) + xSpacing : 0;
  102. int iKillerTextWide = killer[0] ? UTIL_ComputeStringWidth( m_hTextFont, killer ) + xSpacing : 0;
  103. int iLineTall = m_flLineHeight;
  104. int iTextTall = surface()->GetFontTall( m_hTextFont );
  105. int iconWide = 0, iconTall = 0, iDeathInfoOffset = 0, iVictimTextOffset = 0, iconActualWide = 0;
  106. // Get the local position for this notice
  107. if ( icon )
  108. {
  109. iconActualWide = icon->EffectiveWidth( 1.0f );
  110. iconWide = iconActualWide + xSpacing;
  111. iconTall = icon->EffectiveHeight( 1.0f );
  112. int iconTallDesired = iLineTall-YRES(2);
  113. Assert( 0 != iconTallDesired );
  114. float flScale = (float) iconTallDesired / (float) iconTall;
  115. iconActualWide *= flScale;
  116. iconTall *= flScale;
  117. iconWide *= flScale;
  118. }
  119. int iTotalWide = iKillerTextWide + iconWide + iVictimTextWide + iDeathInfoTextWide + ( xMargin * 2 );
  120. int y = yStart + ( ( iLineTall + m_flLineSpacing ) * i );
  121. int yText = y + ( ( iLineTall - iTextTall ) / 2 );
  122. int yIcon = y + ( ( iLineTall - iconTall ) / 2 );
  123. int x=0;
  124. if ( m_bRightJustify )
  125. {
  126. x = GetWide() - iTotalWide;
  127. }
  128. // draw a background panel for the message
  129. Vertex_t vert[NUM_BACKGROUND_COORD];
  130. GetBackgroundPolygonVerts( x, y+1, x+iTotalWide, y+iLineTall-1, ARRAYSIZE( vert ), vert );
  131. surface()->DrawSetTexture( -1 );
  132. surface()->DrawSetColor( msg.bLocalPlayerInvolved ? m_clrLocalBGColor : m_clrBaseBGColor );
  133. surface()->DrawTexturedPolygon( ARRAYSIZE( vert ), vert );
  134. x += xMargin;
  135. if ( killer[0] )
  136. {
  137. // Draw killer's name
  138. DrawText( x, yText, m_hTextFont, GetTeamColor( msg.Killer.iTeam ), killer );
  139. x += iKillerTextWide;
  140. }
  141. // Draw death icon
  142. if ( icon )
  143. {
  144. icon->DrawSelf( x, yIcon, iconActualWide, iconTall, m_clrIcon );
  145. x += iconWide;
  146. }
  147. // Draw additional info text next to death icon
  148. if ( msg.wzInfoText[0] )
  149. {
  150. if ( msg.bSelfInflicted )
  151. {
  152. iDeathInfoOffset += iVictimTextWide;
  153. iVictimTextOffset -= iDeathInfoTextWide;
  154. }
  155. DrawText( x + iDeathInfoOffset, yText, m_hTextFont, Color(255,255,255,255), msg.wzInfoText );
  156. x += iDeathInfoTextWide;
  157. }
  158. // Draw victims name
  159. DrawText( x + iVictimTextOffset, yText, m_hTextFont, GetTeamColor( msg.Victim.iTeam ), victim );
  160. x += iVictimTextWide;
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: This message handler may be better off elsewhere
  165. //-----------------------------------------------------------------------------
  166. void CHudBaseDeathNotice::RetireExpiredDeathNotices()
  167. {
  168. // Remove any expired death notices. Loop backwards because we might remove one
  169. int iCount = m_DeathNotices.Count();
  170. for ( int i = iCount-1; i >= 0; i-- )
  171. {
  172. if ( gpGlobals->curtime > m_DeathNotices[i].GetExpiryTime() )
  173. {
  174. m_DeathNotices.Remove(i);
  175. }
  176. }
  177. // Do we have too many death messages in the queue?
  178. if ( m_DeathNotices.Count() > 0 &&
  179. m_DeathNotices.Count() > (int)m_flMaxDeathNotices )
  180. {
  181. // First, remove any notices not involving the local player, since they are lower priority.
  182. iCount = m_DeathNotices.Count();
  183. int iNeedToRemove = iCount - (int)m_flMaxDeathNotices;
  184. // loop condition is iCount-1 because we won't remove the most recent death notice, otherwise
  185. // new non-local-player-involved messages would not appear if the queue was full of messages involving the local player
  186. for ( int i = 0; i < iCount-1 && iNeedToRemove > 0 ; i++ )
  187. {
  188. if ( !m_DeathNotices[i].bLocalPlayerInvolved )
  189. {
  190. m_DeathNotices.Remove( i );
  191. iCount--;
  192. iNeedToRemove--;
  193. }
  194. }
  195. // Now that we've culled any non-local-player-involved messages up to the amount we needed to remove, see
  196. // if we've removed enough
  197. iCount = m_DeathNotices.Count();
  198. iNeedToRemove = iCount - (int)m_flMaxDeathNotices;
  199. if ( iNeedToRemove > 0 )
  200. {
  201. // if we still have too many messages, then just remove however many we need, oldest first
  202. for ( int i = 0; i < iNeedToRemove; i++ )
  203. {
  204. m_DeathNotices.Remove( 0 );
  205. }
  206. }
  207. }
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Server's told us that someone's died
  211. //-----------------------------------------------------------------------------
  212. void CHudBaseDeathNotice::FireGameEvent( IGameEvent *event )
  213. {
  214. if ( !g_PR )
  215. {
  216. return;
  217. }
  218. if ( hud_deathnotice_time.GetFloat() == 0 )
  219. {
  220. return;
  221. }
  222. const char *pszEventName = event->GetName();
  223. // Add a new death message. Note we always look it up by index rather than create a reference or pointer to it;
  224. // additional messages may get added during this function that cause the underlying array to get realloced, so don't
  225. // ever keep a pointer to memory here.
  226. int iMsg = AddDeathNoticeItem();
  227. int iLocalPlayerIndex = GetLocalPlayerIndex();
  228. bool bPlayerDeath = FStrEq( pszEventName, "player_death" );
  229. bool bObjectDeath = FStrEq( pszEventName, "object_destroyed" );
  230. if ( bPlayerDeath || bObjectDeath )
  231. {
  232. int victim = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
  233. int killer = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
  234. const char *killedwith = event->GetString( "weapon" );
  235. const char *killedwithweaponlog = event->GetString( "weapon_logclassname" );
  236. if ( bObjectDeath && victim == 0 )
  237. {
  238. // for now, no death notices of map placed objects
  239. m_DeathNotices.Remove( iMsg );
  240. return;
  241. }
  242. // Get the names of the players
  243. const char *killer_name = g_PR->GetPlayerName( killer );
  244. const char *victim_name = g_PR->GetPlayerName( victim );
  245. if ( !killer_name )
  246. {
  247. killer_name = "";
  248. }
  249. if ( !victim_name )
  250. {
  251. victim_name = "";
  252. }
  253. // Make a new death notice
  254. bool bLocalPlayerInvolved = false;
  255. if ( iLocalPlayerIndex == killer || iLocalPlayerIndex == victim )
  256. {
  257. bLocalPlayerInvolved = true;
  258. }
  259. m_DeathNotices[iMsg].bLocalPlayerInvolved = bLocalPlayerInvolved;
  260. m_DeathNotices[iMsg].Killer.iTeam = g_PR->GetTeam( killer );
  261. m_DeathNotices[iMsg].Victim.iTeam = g_PR->GetTeam( victim );
  262. Q_strncpy( m_DeathNotices[iMsg].Killer.szName, killer_name, ARRAYSIZE( m_DeathNotices[iMsg].Killer.szName ) );
  263. Q_strncpy( m_DeathNotices[iMsg].Victim.szName, victim_name, ARRAYSIZE( m_DeathNotices[iMsg].Victim.szName ) );
  264. if ( killedwith && *killedwith )
  265. {
  266. Q_snprintf( m_DeathNotices[iMsg].szIcon, sizeof(m_DeathNotices[iMsg].szIcon), "d_%s", killedwith );
  267. }
  268. if ( !killer || killer == victim )
  269. {
  270. m_DeathNotices[iMsg].bSelfInflicted = true;
  271. m_DeathNotices[iMsg].Killer.szName[0] = 0;
  272. if ( event->GetInt( "damagebits" ) & DMG_FALL )
  273. {
  274. // special case text for falling death
  275. V_wcsncpy( m_DeathNotices[iMsg].wzInfoText, g_pVGuiLocalize->Find( "#DeathMsg_Fall" ), sizeof( m_DeathNotices[iMsg].wzInfoText ) );
  276. }
  277. else if ( ( event->GetInt( "damagebits" ) & DMG_VEHICLE ) || ( 0 == Q_stricmp( m_DeathNotices[iMsg].szIcon, "d_tracktrain" ) ) )
  278. {
  279. // special case icon for hit-by-vehicle death
  280. Q_strncpy( m_DeathNotices[iMsg].szIcon, "d_vehicle", ARRAYSIZE( m_DeathNotices[iMsg].szIcon ) );
  281. }
  282. }
  283. char sDeathMsg[512];
  284. // Record the death notice in the console
  285. if ( m_DeathNotices[iMsg].bSelfInflicted )
  286. {
  287. if ( !strcmp( m_DeathNotices[iMsg].szIcon, "d_worldspawn" ) )
  288. {
  289. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.", m_DeathNotices[iMsg].Victim.szName );
  290. }
  291. else // d_world
  292. {
  293. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.", m_DeathNotices[iMsg].Victim.szName );
  294. }
  295. }
  296. else
  297. {
  298. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", m_DeathNotices[iMsg].Killer.szName, m_DeathNotices[iMsg].Victim.szName );
  299. if ( killedwithweaponlog && killedwithweaponlog[0] && ( killedwithweaponlog[0] > 13 ) )
  300. {
  301. Q_strncat( sDeathMsg, VarArgs( " with %s.", killedwithweaponlog ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
  302. }
  303. else if ( m_DeathNotices[iMsg].szIcon[0] && ( m_DeathNotices[iMsg].szIcon[0] > 13 ) )
  304. {
  305. Q_strncat( sDeathMsg, VarArgs( " with %s.", &m_DeathNotices[iMsg].szIcon[2] ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
  306. }
  307. }
  308. Msg( "%s\n", sDeathMsg );
  309. }
  310. else if ( FStrEq( "teamplay_point_captured", pszEventName ) )
  311. {
  312. GetLocalizedControlPointName( event, m_DeathNotices[iMsg].Victim.szName, ARRAYSIZE( m_DeathNotices[iMsg].Victim.szName ) );
  313. // Array of capper indices
  314. const char *cappers = event->GetString("cappers");
  315. char szCappers[256];
  316. szCappers[0] = '\0';
  317. int len = Q_strlen(cappers);
  318. for( int i=0;i<len;i++ )
  319. {
  320. int iPlayerIndex = (int)cappers[i];
  321. Assert( iPlayerIndex > 0 && iPlayerIndex <= gpGlobals->maxClients );
  322. const char *pPlayerName = g_PR->GetPlayerName( iPlayerIndex );
  323. if ( i == 0 )
  324. {
  325. // use first player as the team
  326. m_DeathNotices[iMsg].Killer.iTeam = g_PR->GetTeam( iPlayerIndex );
  327. m_DeathNotices[iMsg].Victim.iTeam = TEAM_UNASSIGNED;
  328. }
  329. else
  330. {
  331. Q_strncat( szCappers, ", ", sizeof(szCappers), 2 );
  332. }
  333. Q_strncat( szCappers, pPlayerName, sizeof(szCappers), COPY_ALL_CHARACTERS );
  334. if ( iLocalPlayerIndex == iPlayerIndex )
  335. m_DeathNotices[iMsg].bLocalPlayerInvolved = true;
  336. }
  337. Q_strncpy( m_DeathNotices[iMsg].Killer.szName, szCappers, sizeof(m_DeathNotices[iMsg].Killer.szName) );
  338. V_wcsncpy( m_DeathNotices[iMsg].wzInfoText, g_pVGuiLocalize->Find( "#Msg_Captured" ), sizeof( m_DeathNotices[iMsg].wzInfoText ) );
  339. // print a log message
  340. Msg( "%s captured %s for team #%d\n", m_DeathNotices[iMsg].Killer.szName, m_DeathNotices[iMsg].Victim.szName, m_DeathNotices[iMsg].Killer.iTeam );
  341. }
  342. else if ( FStrEq( "teamplay_capture_blocked", pszEventName ) )
  343. {
  344. GetLocalizedControlPointName( event, m_DeathNotices[iMsg].Victim.szName, ARRAYSIZE( m_DeathNotices[iMsg].Victim.szName ) );
  345. V_wcsncpy( m_DeathNotices[iMsg].wzInfoText, g_pVGuiLocalize->Find( "#Msg_Defended" ), sizeof( m_DeathNotices[iMsg].wzInfoText ) );
  346. int iPlayerIndex = event->GetInt( "blocker" );
  347. const char *blocker_name = g_PR->GetPlayerName( iPlayerIndex );
  348. Q_strncpy( m_DeathNotices[iMsg].Killer.szName, blocker_name, ARRAYSIZE( m_DeathNotices[iMsg].Killer.szName ) );
  349. m_DeathNotices[iMsg].Killer.iTeam = g_PR->GetTeam( iPlayerIndex );
  350. if ( iLocalPlayerIndex == iPlayerIndex )
  351. m_DeathNotices[iMsg].bLocalPlayerInvolved = true;
  352. // print a log message
  353. Msg( "%s defended %s for team #%d\n", m_DeathNotices[iMsg].Killer.szName, m_DeathNotices[iMsg].Victim.szName, m_DeathNotices[iMsg].Killer.iTeam );
  354. }
  355. else if ( FStrEq( "teamplay_flag_event", pszEventName ) )
  356. {
  357. const char *pszMsgKey = NULL;
  358. int iEventType = event->GetInt( "eventtype" );
  359. switch ( iEventType )
  360. {
  361. case TF_FLAGEVENT_PICKUP:
  362. pszMsgKey = "#Msg_PickedUpFlag";
  363. break;
  364. case TF_FLAGEVENT_CAPTURE:
  365. pszMsgKey = "#Msg_CapturedFlag";
  366. break;
  367. case TF_FLAGEVENT_DEFEND:
  368. pszMsgKey = "#Msg_DefendedFlag";
  369. break;
  370. // Add this when we can get localization for it
  371. //case TF_FLAGEVENT_DROPPED:
  372. // pszMsgKey = "#Msg_DroppedFlag";
  373. // break;
  374. default:
  375. // unsupported, don't put anything up
  376. m_DeathNotices.Remove( iMsg );
  377. return;
  378. }
  379. wchar_t *pwzEventText = g_pVGuiLocalize->Find( pszMsgKey );
  380. Assert( pwzEventText );
  381. if ( pwzEventText )
  382. {
  383. V_wcsncpy( m_DeathNotices[iMsg].wzInfoText, pwzEventText, sizeof( m_DeathNotices[iMsg].wzInfoText ) );
  384. }
  385. else
  386. {
  387. V_memset( m_DeathNotices[iMsg].wzInfoText, 0, sizeof( m_DeathNotices[iMsg].wzInfoText ) );
  388. }
  389. int iPlayerIndex = event->GetInt( "player" );
  390. const char *szPlayerName = g_PR->GetPlayerName( iPlayerIndex );
  391. Q_strncpy( m_DeathNotices[iMsg].Killer.szName, szPlayerName, ARRAYSIZE( m_DeathNotices[iMsg].Killer.szName ) );
  392. m_DeathNotices[iMsg].Killer.iTeam = g_PR->GetTeam( iPlayerIndex );
  393. if ( iLocalPlayerIndex == iPlayerIndex )
  394. m_DeathNotices[iMsg].bLocalPlayerInvolved = true;
  395. }
  396. OnGameEvent( event, m_DeathNotices[iMsg] );
  397. if ( !m_DeathNotices[iMsg].iconDeath && m_DeathNotices[iMsg].szIcon )
  398. {
  399. // Try and find the death identifier in the icon list
  400. // On consoles, we flip usage of the inverted icon to make it more visible
  401. bool bInverted = m_DeathNotices[iMsg].bLocalPlayerInvolved;
  402. if ( IsGameConsole() )
  403. {
  404. bInverted = !bInverted;
  405. }
  406. m_DeathNotices[iMsg].iconDeath = GetIcon( m_DeathNotices[iMsg].szIcon, bInverted );
  407. if ( !m_DeathNotices[iMsg].iconDeath )
  408. {
  409. // Can't find it, so use the default skull & crossbones icon
  410. m_DeathNotices[iMsg].iconDeath = GetIcon( "d_skull_tf", m_DeathNotices[iMsg].bLocalPlayerInvolved );
  411. }
  412. }
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose: Gets the localized name of the control point sent in the event
  416. //-----------------------------------------------------------------------------
  417. void CHudBaseDeathNotice::GetLocalizedControlPointName( IGameEvent *event, char *namebuf, int namelen )
  418. {
  419. // Cap point name ( MATTTODO: can't we find this from the point index ? )
  420. const char *pName = event->GetString( "cpname", "Unnamed Control Point" );
  421. const wchar_t *pLocalizedName = g_pVGuiLocalize->Find( pName );
  422. if ( pLocalizedName )
  423. {
  424. g_pVGuiLocalize->ConvertUnicodeToANSI( pLocalizedName, namebuf, namelen );
  425. }
  426. else
  427. {
  428. Q_strncpy( namebuf, pName, namelen );
  429. }
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose: Adds a new death notice to the queue
  433. //-----------------------------------------------------------------------------
  434. int CHudBaseDeathNotice::AddDeathNoticeItem()
  435. {
  436. int iMsg = m_DeathNotices.AddToTail();
  437. DeathNoticeItem &msg = m_DeathNotices[iMsg];
  438. msg.flCreationTime = gpGlobals->curtime;
  439. return iMsg;
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: draw text helper
  443. //-----------------------------------------------------------------------------
  444. void CHudBaseDeathNotice::DrawText( int x, int y, HFont hFont, Color clr, const wchar_t *szText )
  445. {
  446. surface()->DrawSetTextPos( x, y );
  447. surface()->DrawSetTextColor( clr );
  448. surface()->DrawSetTextFont( hFont ); //reset the font, draw icon can change it
  449. surface()->DrawUnicodeString( szText, FONT_DRAW_NONADDITIVE );
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose: Creates a rounded-corner polygon that fits in the specified bounds
  453. //-----------------------------------------------------------------------------
  454. void CHudBaseDeathNotice::GetBackgroundPolygonVerts( int x0, int y0, int x1, int y1, int iVerts, vgui::Vertex_t vert[] )
  455. {
  456. Assert( iVerts == NUM_BACKGROUND_COORD );
  457. // use the offsets we generated for one corner and apply those to the passed-in dimensions to create verts for the poly
  458. for ( int i = 0; i < NUM_CORNER_COORD; i++ )
  459. {
  460. int j = ( NUM_CORNER_COORD-1 ) - i;
  461. // upper left corner
  462. vert[i].Init( Vector2D( x0 + m_CornerCoord[i].x, y0 + m_CornerCoord[i].y ) );
  463. // upper right corner
  464. vert[i+NUM_CORNER_COORD].Init( Vector2D( x1 - m_CornerCoord[j].x, y0 + m_CornerCoord[j].y ) );
  465. // lower right corner
  466. vert[i+(NUM_CORNER_COORD*2)].Init( Vector2D( x1 - m_CornerCoord[i].x, y1 - m_CornerCoord[i].y ) );
  467. // lower left corner
  468. vert[i+(NUM_CORNER_COORD*3)].Init( Vector2D( x0 + m_CornerCoord[j].x, y1 - m_CornerCoord[j].y) );
  469. }
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose: Creates the offsets for rounded corners based on current screen res
  473. //-----------------------------------------------------------------------------
  474. void CHudBaseDeathNotice::CalcRoundedCorners()
  475. {
  476. // generate the offset geometry for upper left corner
  477. int iMax = ARRAYSIZE( m_CornerCoord );
  478. for ( int i = 0; i < iMax; i++ )
  479. {
  480. m_CornerCoord[i].x = m_flCornerRadius * ( 1 - cos( ( (float) i / (float) (iMax - 1 ) ) * ( M_PI / 2 ) ) );
  481. m_CornerCoord[i].y = m_flCornerRadius * ( 1 - sin( ( (float) i / (float) (iMax - 1 ) ) * ( M_PI / 2 ) ) );
  482. }
  483. }
  484. //-----------------------------------------------------------------------------
  485. // Purpose: Gets specified icon
  486. //-----------------------------------------------------------------------------
  487. CHudTexture *CHudBaseDeathNotice::GetIcon( const char *szIcon, bool bInvert )
  488. {
  489. // get the inverted version if specified
  490. if ( bInvert && 0 == V_strncmp( "d_", szIcon, 2 ) )
  491. {
  492. // change prefix from d_ to dneg_
  493. char szIconTmp[255] = "dneg_";
  494. V_strcat( szIconTmp, szIcon+2, ARRAYSIZE( szIconTmp ) );
  495. CHudTexture *pIcon = HudIcons().GetIcon( szIconTmp );
  496. // return inverted version if found
  497. if ( pIcon )
  498. return pIcon;
  499. // if we didn't find the inverted version, keep going and try the normal version
  500. }
  501. return HudIcons().GetIcon( szIcon );
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose: Gets the expiry time for this death notice item
  505. //-----------------------------------------------------------------------------
  506. float DeathNoticeItem::GetExpiryTime()
  507. {
  508. float flDuration = hud_deathnotice_time.GetFloat();
  509. if ( bLocalPlayerInvolved )
  510. {
  511. // if the local player is involved, make the message last longer
  512. flDuration *= 2;
  513. }
  514. return flCreationTime + flDuration;
  515. }