summaryrefslogtreecommitdiff
path: root/game/client/cstrike/hud_deathnotice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/cstrike/hud_deathnotice.cpp')
-rw-r--r--game/client/cstrike/hud_deathnotice.cpp485
1 files changed, 485 insertions, 0 deletions
diff --git a/game/client/cstrike/hud_deathnotice.cpp b/game/client/cstrike/hud_deathnotice.cpp
new file mode 100644
index 0000000..3ca5d4a
--- /dev/null
+++ b/game/client/cstrike/hud_deathnotice.cpp
@@ -0,0 +1,485 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Draws CSPort's death notices
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "hudelement.h"
+#include "hud_macros.h"
+#include "c_playerresource.h"
+#include "iclientmode.h"
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/Panel.h>
+#include <vgui/ISurface.h>
+#include <vgui/ILocalize.h>
+#include <KeyValues.h>
+#include "c_baseplayer.h"
+#include "c_team.h"
+
+#include "cs_shareddefs.h"
+#include "clientmode_csnormal.h"
+#include "c_cs_player.h"
+#include "c_cs_playerresource.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+const int DOMINATION_DRAW_HEIGHT = 20;
+const int DOMINATION_DRAW_WIDTH = 20;
+
+static ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 );
+
+// Player entries in a death notice
+struct DeathNoticePlayer
+{
+ char szName[MAX_PLAYER_NAME_LENGTH];
+ char szClan[MAX_CLAN_TAG_LENGTH];
+ int iEntIndex;
+ Color color;
+};
+
+// Contents of each entry in our list of death notices
+struct DeathNoticeItem
+{
+ DeathNoticePlayer Killer;
+ DeathNoticePlayer Victim;
+ CHudTexture *iconDeath;
+ int iSuicide;
+ float flDisplayTime;
+ bool bHeadshot;
+ int iDominationImageId;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CHudDeathNotice : public CHudElement, public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CHudDeathNotice, vgui::Panel );
+public:
+ CHudDeathNotice( const char *pElementName );
+
+ void Init( void );
+ void VidInit( void );
+ virtual bool ShouldDraw( void );
+ virtual void Paint( void );
+ virtual void ApplySchemeSettings( vgui::IScheme *scheme );
+
+ void RetireExpiredDeathNotices( void );
+
+ void FireGameEvent( IGameEvent *event );
+
+protected:
+ int SetupHudImageId( const char* fname );
+
+private:
+
+ CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" );
+
+ CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" );
+
+ CPanelAnimationVar( bool, m_bRightJustify, "RightJustify", "1" );
+
+ CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" );
+
+ CPanelAnimationVar( Color, m_clrCTText, "CTTextColor", "CTTextColor" );
+ CPanelAnimationVar( Color, m_clrTerroristText, "TerroristTextColor", "TerroristTextColor" );
+
+ // Texture for skull symbol
+ CHudTexture *m_iconD_skull;
+ CHudTexture *m_iconD_headshot;
+
+ int m_iNemesisImageId;
+ int m_iDominatedImageId;
+ int m_iRevengeImageId;
+
+ Color m_teamColors[TEAM_MAXCOUNT];
+
+ CUtlVector<DeathNoticeItem> m_DeathNotices;
+};
+
+using namespace vgui;
+
+DECLARE_HUDELEMENT( CHudDeathNotice );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHudDeathNotice::CHudDeathNotice( const char *pElementName ) :
+ CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" )
+{
+ vgui::Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+
+ m_iconD_headshot = NULL;
+ m_iconD_skull = NULL;
+
+ SetHiddenBits( HIDEHUD_MISCSTATUS );
+
+ m_iNemesisImageId = SetupHudImageId("hud/freeze_nemesis");
+ m_iDominatedImageId = SetupHudImageId("hud/freeze_dominated");
+ m_iRevengeImageId = SetupHudImageId("hud/freeze_revenge");
+}
+
+
+/**
+ * Helper function to get an image id and set
+ */
+int CHudDeathNotice::SetupHudImageId( const char* fname )
+{
+ int imageId = surface()->CreateNewTextureID();
+ surface()->DrawSetTextureFile( imageId, fname, true, false );
+ return imageId;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme )
+{
+ BaseClass::ApplySchemeSettings( scheme );
+ SetPaintBackgroundEnabled( false );
+
+ // make team color lookups easier
+ memset(m_teamColors, 0, sizeof(m_teamColors));
+ m_teamColors[TEAM_CT] = m_clrCTText;
+ m_teamColors[TEAM_TERRORIST] = m_clrTerroristText;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::Init( void )
+{
+ ListenForGameEvent( "player_death" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::VidInit( void )
+{
+ m_iconD_skull = gHUD.GetIcon( "d_skull_cs" );
+ m_iconD_headshot = gHUD.GetIcon( "d_headshot" );
+ m_DeathNotices.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw if we've got at least one death notice in the queue
+//-----------------------------------------------------------------------------
+bool CHudDeathNotice::ShouldDraw( void )
+{
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+
+ if ( !pPlayer )
+ return false;
+
+ // don't show death notices when flashed
+ if ( pPlayer->IsAlive() && pPlayer->m_flFlashBangTime >= gpGlobals->curtime )
+ {
+ float flAlpha = pPlayer->m_flFlashMaxAlpha * (pPlayer->m_flFlashBangTime - gpGlobals->curtime) / pPlayer->m_flFlashDuration;
+ if ( flAlpha > 75.0f ) // 0..255
+ {
+ return false;
+ }
+ }
+
+ return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::Paint()
+{
+ if ( !m_iconD_headshot || !m_iconD_skull )
+ return;
+
+ int yStart = GetClientModeCSNormal()->GetDeathMessageStartHeight();
+
+ surface()->DrawSetTextFont( m_hTextFont );
+ surface()->DrawSetTextColor( m_clrCTText );
+
+ int dominationDrawWidth = scheme()->GetProportionalScaledValueEx( GetScheme(), DOMINATION_DRAW_WIDTH );
+ int dominationDrawHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DOMINATION_DRAW_HEIGHT );
+
+ int iconHeadshotWide;
+ int iconHeadshotTall;
+
+ if( m_iconD_headshot->bRenderUsingFont )
+ {
+ iconHeadshotWide = surface()->GetCharacterWidth( m_iconD_headshot->hFont, m_iconD_headshot->cCharacterInFont );
+ iconHeadshotTall = surface()->GetFontTall( m_iconD_headshot->hFont );
+ }
+ else
+ {
+ float scale = ( (float)ScreenHeight() / 480.0f ); //scale based on 640x480
+ iconHeadshotWide = (int)( scale * (float)m_iconD_headshot->Width() );
+ iconHeadshotTall = (int)( scale * (float)m_iconD_headshot->Height() );
+ }
+
+ int iCount = m_DeathNotices.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CHudTexture *icon = m_DeathNotices[i].iconDeath;
+ if ( !icon )
+ continue;
+
+ wchar_t victim[ 256 ];
+ wchar_t killer[ 256 ];
+ wchar_t victimclan[ 256 ];
+ wchar_t killerclan[ 256 ];
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Victim.szName, victim, sizeof( victim ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Killer.szName, killer, sizeof( killer ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Victim.szClan, victimclan, sizeof( victimclan ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( m_DeathNotices[i].Killer.szClan, killerclan, sizeof( killerclan ) );
+
+ // Get the local position for this notice
+ int victimNameLen = UTIL_ComputeStringWidth( m_hTextFont, victim );
+ int victimClanLen = UTIL_ComputeStringWidth( m_hTextFont, victimclan );
+ int y = yStart + (m_flLineHeight * i);
+
+ int iconWide;
+ int iconTall;
+
+ if( icon->bRenderUsingFont )
+ {
+ iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
+ iconTall = surface()->GetFontTall( icon->hFont );
+ }
+ else
+ {
+ float scale = ( (float)ScreenHeight() / 480.0f ); //scale based on 640x480
+ iconWide = (int)( scale * (float)icon->Width() );
+ iconTall = (int)( scale * (float)icon->Height() );
+ }
+
+ int x = 0;
+ if ( m_bRightJustify )
+ {
+ x = GetWide();
+ x -= victimNameLen;
+ x -= victimClanLen;
+ x -= iconWide;
+
+ if ( m_DeathNotices[i].bHeadshot )
+ x -= iconHeadshotWide;
+
+ if ( !m_DeathNotices[i].iSuicide )
+ {
+ x -= UTIL_ComputeStringWidth( m_hTextFont, killer );
+ x -= UTIL_ComputeStringWidth( m_hTextFont, killerclan );
+ }
+
+ if (m_DeathNotices[i].iDominationImageId >= 0)
+ {
+ x -= dominationDrawWidth;
+ }
+ }
+
+ if (m_DeathNotices[i].iDominationImageId >= 0)
+ {
+ surface()->DrawSetTexture(m_DeathNotices[i].iDominationImageId);
+ surface()->DrawSetColor(m_DeathNotices[i].Killer.color);
+ surface()->DrawTexturedRect( x, y, x + dominationDrawWidth, y + dominationDrawHeight );
+ x += dominationDrawWidth;
+ }
+
+ // Only draw killers name if it wasn't a suicide
+ if ( !m_DeathNotices[i].iSuicide )
+ {
+ // Draw killer's clan
+ surface()->DrawSetTextColor( m_DeathNotices[i].Killer.color );
+ surface()->DrawSetTextPos( x, y );
+ surface()->DrawSetTextFont( m_hTextFont );
+ surface()->DrawUnicodeString( killerclan );
+ surface()->DrawGetTextPos( x, y );
+
+ // Draw killer's name
+ surface()->DrawSetTextColor( m_DeathNotices[i].Killer.color );
+ surface()->DrawSetTextPos( x, y );
+ surface()->DrawSetTextFont( m_hTextFont );
+ surface()->DrawUnicodeString( killer );
+ surface()->DrawGetTextPos( x, y );
+ }
+
+
+ // Draw death weapon
+ //If we're using a font char, this will ignore iconTall and iconWide
+ Color iconColor( 255, 80, 0, 255 );
+ icon->DrawSelf( x, y, iconWide, iconTall, iconColor );
+ x += iconWide;
+
+ if( m_DeathNotices[i].bHeadshot )
+ {
+ m_iconD_headshot->DrawSelf( x, y, iconHeadshotWide, iconHeadshotTall, iconColor );
+ x += iconHeadshotWide;
+ }
+
+ // Draw victims clan
+ surface()->DrawSetTextColor( m_DeathNotices[i].Victim.color );
+ surface()->DrawSetTextPos( x, y );
+ surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
+ surface()->DrawUnicodeString( victimclan );
+ surface()->DrawGetTextPos( x, y );
+
+ // Draw victims name
+ surface()->DrawSetTextColor( m_DeathNotices[i].Victim.color );
+ surface()->DrawSetTextPos( x, y );
+ surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it
+ surface()->DrawUnicodeString( victim );
+ }
+
+ // Now retire any death notices that have expired
+ RetireExpiredDeathNotices();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This message handler may be better off elsewhere
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::RetireExpiredDeathNotices( void )
+{
+ // Loop backwards because we might remove one
+ int iSize = m_DeathNotices.Size();
+ for ( int i = iSize-1; i >= 0; i-- )
+ {
+ if ( m_DeathNotices[i].flDisplayTime < gpGlobals->curtime )
+ {
+ m_DeathNotices.Remove(i);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Server's told us that someone's died
+//-----------------------------------------------------------------------------
+void CHudDeathNotice::FireGameEvent( IGameEvent *event )
+{
+ if (!g_PR)
+ return;
+
+ C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
+ if ( !cs_PR )
+ return;
+
+ if ( hud_deathnotice_time.GetFloat() == 0 )
+ return;
+
+ // the event should be "player_death"
+
+ int iKiller = engine->GetPlayerForUserID( event->GetInt("attacker") );
+ int iVictim = engine->GetPlayerForUserID( event->GetInt("userid") );
+ const char *killedwith = event->GetString( "weapon" );
+ bool headshot = event->GetInt( "headshot" ) > 0;
+
+ char fullkilledwith[128];
+ if ( killedwith && *killedwith )
+ {
+ Q_snprintf( fullkilledwith, sizeof(fullkilledwith), "d_%s", killedwith );
+ }
+ else
+ {
+ fullkilledwith[0] = 0;
+ }
+
+ // Do we have too many death messages in the queue?
+ if ( m_DeathNotices.Count() > 0 &&
+ m_DeathNotices.Count() >= (int)m_flMaxDeathNotices )
+ {
+ // Remove the oldest one in the queue, which will always be the first
+ m_DeathNotices.Remove(0);
+ }
+
+ // Get the names of the players
+ const char *killer_name = iKiller > 0 ? g_PR->GetPlayerName( iKiller ) : NULL;
+ const char *victim_name = iVictim > 0 ? g_PR->GetPlayerName( iVictim ) : NULL;
+
+ if ( !killer_name )
+ killer_name = "";
+ if ( !victim_name )
+ victim_name = "";
+
+ // Get the clan tags of the players
+ const char *killer_clan = iKiller > 0 ? cs_PR->GetClanTag( iKiller ) : NULL;
+ const char *victim_clan = iVictim > 0 ? cs_PR->GetClanTag( iVictim ) : NULL;
+
+ if ( !killer_clan )
+ killer_clan = "";
+ if ( !victim_clan )
+ victim_clan = "";
+
+ // Make a new death notice
+ DeathNoticeItem deathMsg;
+ deathMsg.Killer.iEntIndex = iKiller;
+ deathMsg.Victim.iEntIndex = iVictim;
+ deathMsg.Killer.color = iKiller > 0 ? m_teamColors[g_PR->GetTeam(iKiller)] : COLOR_WHITE;
+ deathMsg.Victim.color = iVictim > 0 ? m_teamColors[g_PR->GetTeam(iVictim)] : COLOR_WHITE;
+ Q_snprintf( deathMsg.Killer.szClan, sizeof( deathMsg.Killer.szClan ), "%s ", killer_clan );
+ Q_snprintf( deathMsg.Victim.szClan, sizeof( deathMsg.Victim.szClan ), "%s ", victim_clan );
+ Q_strncpy( deathMsg.Killer.szName, killer_name, MAX_PLAYER_NAME_LENGTH );
+ Q_strncpy( deathMsg.Victim.szName, victim_name, MAX_PLAYER_NAME_LENGTH );
+ deathMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat();
+ deathMsg.iSuicide = ( !iKiller || iKiller == iVictim );
+ deathMsg.bHeadshot = headshot;
+ deathMsg.iDominationImageId = -1;
+
+ CCSPlayer* pKiller = ToCSPlayer(ClientEntityList().GetBaseEntity(iKiller));
+
+ // the local player is dead, see if this is a new nemesis or a revenge
+ if ( event->GetInt( "dominated" ) > 0 || (pKiller != NULL && pKiller->IsPlayerDominated(iVictim)) )
+ {
+ deathMsg.iDominationImageId = m_iDominatedImageId;
+ }
+ else if ( event->GetInt( "revenge" ) > 0 )
+ {
+ deathMsg.iDominationImageId = m_iRevengeImageId;
+ }
+
+ // Try and find the death identifier in the icon list
+ deathMsg.iconDeath = gHUD.GetIcon( fullkilledwith );
+
+ if ( !deathMsg.iconDeath )
+ {
+ // Can't find it, so use the default skull & crossbones icon
+ deathMsg.iconDeath = m_iconD_skull;
+ }
+
+ // Add it to our list of death notices
+ m_DeathNotices.AddToTail( deathMsg );
+
+ char sDeathMsg[512];
+
+ // Record the death notice in the console
+ if ( deathMsg.iSuicide )
+ {
+ if ( !strcmp( fullkilledwith, "d_planted_c4" ) )
+ {
+ Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s was a bit too close to the c4.\n", deathMsg.Victim.szName );
+ }
+ else if ( !strcmp( fullkilledwith, "d_worldspawn" ) )
+ {
+ Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.\n", deathMsg.Victim.szName );
+ }
+ else //d_world
+ {
+ Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.\n", deathMsg.Victim.szName );
+ }
+ }
+ else
+ {
+ Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", deathMsg.Killer.szName, deathMsg.Victim.szName );
+
+ if ( fullkilledwith && *fullkilledwith && (*fullkilledwith > 13 ) )
+ {
+ Q_strncat( sDeathMsg, VarArgs( " with %s.\n", fullkilledwith+2 ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
+ }
+ }
+
+ Msg( "%s", sDeathMsg );
+}
+
+
+