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.

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