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.

352 lines
10 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 "clientmode_hl2mpnormal.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. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. static ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 );
  22. // Player entries in a death notice
  23. struct DeathNoticePlayer
  24. {
  25. char szName[MAX_PLAYER_NAME_LENGTH];
  26. int iEntIndex;
  27. };
  28. // Contents of each entry in our list of death notices
  29. struct DeathNoticeItem
  30. {
  31. DeathNoticePlayer Killer;
  32. DeathNoticePlayer Victim;
  33. CHudTexture *iconDeath;
  34. int iSuicide;
  35. float flDisplayTime;
  36. bool bHeadshot;
  37. };
  38. //-----------------------------------------------------------------------------
  39. // Purpose:
  40. //-----------------------------------------------------------------------------
  41. class CHudDeathNotice : public CHudElement, public vgui::Panel
  42. {
  43. DECLARE_CLASS_SIMPLE( CHudDeathNotice, vgui::Panel );
  44. public:
  45. CHudDeathNotice( const char *pElementName );
  46. void Init( void );
  47. void VidInit( void );
  48. virtual bool ShouldDraw( void );
  49. virtual void Paint( void );
  50. virtual void ApplySchemeSettings( vgui::IScheme *scheme );
  51. void SetColorForNoticePlayer( int iTeamNumber );
  52. void RetireExpiredDeathNotices( void );
  53. virtual void FireGameEvent( IGameEvent * event );
  54. private:
  55. CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" );
  56. CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" );
  57. CPanelAnimationVar( bool, m_bRightJustify, "RightJustify", "1" );
  58. CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" );
  59. // Texture for skull symbol
  60. CHudTexture *m_iconD_skull;
  61. CHudTexture *m_iconD_headshot;
  62. CUtlVector<DeathNoticeItem> m_DeathNotices;
  63. };
  64. using namespace vgui;
  65. DECLARE_HUDELEMENT( CHudDeathNotice );
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. CHudDeathNotice::CHudDeathNotice( const char *pElementName ) :
  70. CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" )
  71. {
  72. vgui::Panel *pParent = g_pClientMode->GetViewport();
  73. SetParent( pParent );
  74. m_iconD_headshot = NULL;
  75. m_iconD_skull = NULL;
  76. SetHiddenBits( HIDEHUD_MISCSTATUS );
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. //-----------------------------------------------------------------------------
  81. void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme )
  82. {
  83. BaseClass::ApplySchemeSettings( scheme );
  84. SetPaintBackgroundEnabled( false );
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. void CHudDeathNotice::Init( void )
  90. {
  91. ListenForGameEvent( "player_death" );
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. void CHudDeathNotice::VidInit( void )
  97. {
  98. m_iconD_skull = gHUD.GetIcon( "d_skull" );
  99. m_DeathNotices.Purge();
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose: Draw if we've got at least one death notice in the queue
  103. //-----------------------------------------------------------------------------
  104. bool CHudDeathNotice::ShouldDraw( void )
  105. {
  106. return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) );
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose:
  110. //-----------------------------------------------------------------------------
  111. void CHudDeathNotice::SetColorForNoticePlayer( int iTeamNumber )
  112. {
  113. surface()->DrawSetTextColor( GameResources()->GetTeamColor( iTeamNumber ) );
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose:
  117. //-----------------------------------------------------------------------------
  118. void CHudDeathNotice::Paint()
  119. {
  120. if ( !m_iconD_skull )
  121. return;
  122. int yStart = GetClientModeHL2MPNormal()->GetDeathMessageStartHeight();
  123. surface()->DrawSetTextFont( m_hTextFont );
  124. surface()->DrawSetTextColor( GameResources()->GetTeamColor( 0 ) );
  125. int iCount = m_DeathNotices.Count();
  126. for ( int i = 0; i < iCount; i++ )
  127. {
  128. CHudTexture *icon = m_DeathNotices[i].iconDeath;
  129. if ( !icon )
  130. continue;
  131. wchar_t victim[ 256 ];
  132. wchar_t killer[ 256 ];
  133. // Get the team numbers for the players involved
  134. int iKillerTeam = 0;
  135. int iVictimTeam = 0;
  136. if( g_PR )
  137. {
  138. iKillerTeam = g_PR->GetTeam( m_DeathNotices[i].Killer.iEntIndex );
  139. iVictimTeam = g_PR->GetTeam( m_DeathNotices[i].Victim.iEntIndex );
  140. }
  141. g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Victim.szName, victim, sizeof( victim ) );
  142. g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Killer.szName, killer, sizeof( killer ) );
  143. // Get the local position for this notice
  144. int len = UTIL_ComputeStringWidth( m_hTextFont, victim );
  145. int y = yStart + (m_flLineHeight * i);
  146. int iconWide;
  147. int iconTall;
  148. if( icon->bRenderUsingFont )
  149. {
  150. iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
  151. iconTall = surface()->GetFontTall( icon->hFont );
  152. }
  153. else
  154. {
  155. float scale = ( (float)ScreenHeight() / 480.0f ); //scale based on 640x480
  156. iconWide = (int)( scale * (float)icon->Width() );
  157. iconTall = (int)( scale * (float)icon->Height() );
  158. }
  159. int x;
  160. if ( m_bRightJustify )
  161. {
  162. x = GetWide() - len - iconWide;
  163. }
  164. else
  165. {
  166. x = 0;
  167. }
  168. // Only draw killers name if it wasn't a suicide
  169. if ( !m_DeathNotices[i].iSuicide )
  170. {
  171. if ( m_bRightJustify )
  172. {
  173. x -= UTIL_ComputeStringWidth( m_hTextFont, killer );
  174. }
  175. SetColorForNoticePlayer( iKillerTeam );
  176. // Draw killer's name
  177. surface()->DrawSetTextPos( x, y );
  178. surface()->DrawSetTextFont( m_hTextFont );
  179. surface()->DrawUnicodeString( killer );
  180. surface()->DrawGetTextPos( x, y );
  181. }
  182. Color iconColor( 255, 80, 0, 255 );
  183. // Draw death weapon
  184. //If we're using a font char, this will ignore iconTall and iconWide
  185. icon->DrawSelf( x, y, iconWide, iconTall, iconColor );
  186. x += iconWide;
  187. SetColorForNoticePlayer( iVictimTeam );
  188. // Draw victims name
  189. surface()->DrawSetTextPos( x, y );
  190. surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
  191. surface()->DrawUnicodeString( victim );
  192. }
  193. // Now retire any death notices that have expired
  194. RetireExpiredDeathNotices();
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: This message handler may be better off elsewhere
  198. //-----------------------------------------------------------------------------
  199. void CHudDeathNotice::RetireExpiredDeathNotices( void )
  200. {
  201. // Loop backwards because we might remove one
  202. int iSize = m_DeathNotices.Size();
  203. for ( int i = iSize-1; i >= 0; i-- )
  204. {
  205. if ( m_DeathNotices[i].flDisplayTime < gpGlobals->curtime )
  206. {
  207. m_DeathNotices.Remove(i);
  208. }
  209. }
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Server's told us that someone's died
  213. //-----------------------------------------------------------------------------
  214. void CHudDeathNotice::FireGameEvent( IGameEvent * event )
  215. {
  216. if (!g_PR)
  217. return;
  218. if ( hud_deathnotice_time.GetFloat() == 0 )
  219. return;
  220. // the event should be "player_death"
  221. int killer = engine->GetPlayerForUserID( event->GetInt("attacker") );
  222. int victim = engine->GetPlayerForUserID( event->GetInt("userid") );
  223. const char *killedwith = event->GetString( "weapon" );
  224. char fullkilledwith[128];
  225. if ( killedwith && *killedwith )
  226. {
  227. Q_snprintf( fullkilledwith, sizeof(fullkilledwith), "death_%s", killedwith );
  228. }
  229. else
  230. {
  231. fullkilledwith[0] = 0;
  232. }
  233. // Do we have too many death messages in the queue?
  234. if ( m_DeathNotices.Count() > 0 &&
  235. m_DeathNotices.Count() >= (int)m_flMaxDeathNotices )
  236. {
  237. // Remove the oldest one in the queue, which will always be the first
  238. m_DeathNotices.Remove(0);
  239. }
  240. // Get the names of the players
  241. const char *killer_name = g_PR->GetPlayerName( killer );
  242. const char *victim_name = g_PR->GetPlayerName( victim );
  243. if ( !killer_name )
  244. killer_name = "";
  245. if ( !victim_name )
  246. victim_name = "";
  247. // Make a new death notice
  248. DeathNoticeItem deathMsg;
  249. deathMsg.Killer.iEntIndex = killer;
  250. deathMsg.Victim.iEntIndex = victim;
  251. Q_strncpy( deathMsg.Killer.szName, killer_name, MAX_PLAYER_NAME_LENGTH );
  252. Q_strncpy( deathMsg.Victim.szName, victim_name, MAX_PLAYER_NAME_LENGTH );
  253. deathMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
  254. deathMsg.iSuicide = ( !killer || killer == victim );
  255. // Try and find the death identifier in the icon list
  256. deathMsg.iconDeath = gHUD.GetIcon( fullkilledwith );
  257. if ( !deathMsg.iconDeath || deathMsg.iSuicide )
  258. {
  259. // Can't find it, so use the default skull & crossbones icon
  260. deathMsg.iconDeath = m_iconD_skull;
  261. }
  262. // Add it to our list of death notices
  263. m_DeathNotices.AddToTail( deathMsg );
  264. char sDeathMsg[512];
  265. // Record the death notice in the console
  266. if ( deathMsg.iSuicide )
  267. {
  268. if ( !strcmp( fullkilledwith, "d_worldspawn" ) )
  269. {
  270. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.\n", deathMsg.Victim.szName );
  271. }
  272. else //d_world
  273. {
  274. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.\n", deathMsg.Victim.szName );
  275. }
  276. }
  277. else
  278. {
  279. Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", deathMsg.Killer.szName, deathMsg.Victim.szName );
  280. if ( fullkilledwith && *fullkilledwith && (*fullkilledwith > 13 ) )
  281. {
  282. Q_strncat( sDeathMsg, VarArgs( " with %s.\n", fullkilledwith+6 ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
  283. }
  284. }
  285. Msg( "%s", sDeathMsg );
  286. }