Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

378 lines
11 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Draws TF2's death notices
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hud_macros.h"
#include "hudelement.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 <game/client/iviewport.h>
#include <KeyValues.h>
#include "c_baseplayer.h"
#include "c_team.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 );
// Player entries in a death notice
struct DeathNoticePlayer
{
char szName[MAX_PLAYER_NAME_LENGTH];
int iEntIndex;
};
// Contents of each entry in our list of death notices
struct DeathNoticeItem
{
DeathNoticePlayer Killer;
DeathNoticePlayer Victim;
DeathNoticePlayer Assist;
CHudTexture *iconDeath;
int iSuicide;
int iTeamKill;
float flDisplayTime;
};
//-----------------------------------------------------------------------------
// 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 SetColorForNoticePlayer( C_BasePlayer *pPlayer, int iTeamNumber );
void RetireExpiredDeathNotices( void );
void MsgFunc_DeathMsg( bf_read &msg );
private:
CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" );
CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" );
CPanelAnimationVar( bool, m_bRightJustify, "RightJustify", "1" );
CPanelAnimationVar( Color, m_clrFriendlyText, "FriendlyTextColor", "FriendlyTextColor" );
CPanelAnimationVar( Color, m_clrEnemyText, "EnemyTextColor", "EnemyTextColor" );
CPanelAnimationVar( Color, m_clrMyKillsText, "MyKillsTextColor", "MyKillsTextColor" );
CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" );
// Texture for skull symbol
CHudTexture *m_iconD_skull;
CUtlVector<DeathNoticeItem> m_DeathNotices;
};
using namespace vgui;
DECLARE_HUDELEMENT( CHudDeathNotice );
// DECLARE_HUD_MESSAGE( CHudDeathNotice, DeathMsg );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CHudDeathNotice::CHudDeathNotice( const char *pElementName ) :
CHudElement( pElementName ), BaseClass( NULL, "HudDeathNotice" )
{
vgui::Panel *pParent = g_pClientMode->GetViewport();
SetParent( pParent );
SetHiddenBits( HIDEHUD_MISCSTATUS );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme )
{
BaseClass::ApplySchemeSettings( scheme );
SetPaintBackgroundEnabled( false );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudDeathNotice::Init( void )
{
//FIXME!!!!
//HOOK_MESSAGE( DeathMsg );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudDeathNotice::VidInit( void )
{
m_iconD_skull = gHUD.GetIcon( "d_skull" );
m_DeathNotices.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Draw if we've got at least one death notice in the queue
//-----------------------------------------------------------------------------
bool CHudDeathNotice::ShouldDraw( void )
{
return ( CHudElement::ShouldDraw() && ( m_DeathNotices.Count() ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudDeathNotice::SetColorForNoticePlayer( C_BasePlayer *pPlayer, int iTeamNumber )
{
// Set the right color
if ( pPlayer == C_BasePlayer::GetLocalPlayer() )
{
surface()->DrawSetTextColor( m_clrMyKillsText );
}
else if ( GetLocalTeam() && GetLocalTeam()->GetTeamNumber() == iTeamNumber )
{
surface()->DrawSetTextColor( m_clrFriendlyText );
}
else
{
surface()->DrawSetTextColor( m_clrEnemyText );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudDeathNotice::Paint()
{
surface()->DrawSetTextFont( m_hTextFont );
surface()->DrawSetTextColor( m_clrFriendlyText );
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 assist[ 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].Assist.szName, assist, sizeof( assist ) );
// Get the team numbers for the players involved
C_BasePlayer *pKiller = static_cast<C_BasePlayer*>(cl_entitylist->GetEnt( m_DeathNotices[i].Killer.iEntIndex ));
int iKillerTeam = pKiller ? pKiller->GetTeamNumber() : 0;
C_BasePlayer *pAssist = NULL;
if ( m_DeathNotices[i].Assist.iEntIndex )
{
pAssist = static_cast<C_BasePlayer*>(cl_entitylist->GetEnt( m_DeathNotices[i].Assist.iEntIndex ));
}
C_BasePlayer *pVictim = static_cast<C_BasePlayer*>(cl_entitylist->GetEnt( m_DeathNotices[i].Victim.iEntIndex ));
int iVictimTeam = pVictim ? pVictim->GetTeamNumber() : 0;
// Get the local position for this notice
int len = UTIL_ComputeStringWidth( m_hTextFont, victim );
int y = (m_flLineHeight * i);
int x;
if ( m_bRightJustify )
{
x = GetWide() - len - icon->Width() - 5;
}
else
{
x = 0;
}
// Only draw killers name if it wasn't a suicide
if ( !m_DeathNotices[i].iSuicide )
{
if ( m_bRightJustify )
{
x -= UTIL_ComputeStringWidth( m_hTextFont, killer );
if ( pAssist )
{
x -= UTIL_ComputeStringWidth( m_hTextFont, L" + " );
x -= UTIL_ComputeStringWidth( m_hTextFont, killer );
}
}
SetColorForNoticePlayer( pKiller, iKillerTeam );
// Draw killer's name
surface()->DrawSetTextPos( x, y );
surface()->DrawUnicodeString( killer );
surface()->DrawGetTextPos( x, y );
// Did we have an assistant?
if ( pAssist )
{
// Draw the conjuction
const wchar_t *sAnd = L" + ";
surface()->DrawSetTextPos( x, y );
surface()->DrawUnicodeString( sAnd );
surface()->DrawUnicodeString( assist );
surface()->DrawGetTextPos( x, y );
}
}
Color iconColor( 255, 80, 0, 255 );
if ( m_DeathNotices[i].iTeamKill )
{
// display it in sickly green
iconColor = Color( 10, 240, 10, 255 );
}
// Draw death weapon
icon->DrawSelf( x, y, iconColor );
x += icon->Width();
SetColorForNoticePlayer( pVictim, iVictimTeam );
// Draw victims name
surface()->DrawSetTextPos( x, y );
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::MsgFunc_DeathMsg( bf_read &msg )
{
if (!g_PR)
return;
int killer = msg.ReadByte();
int assist = msg.ReadByte();
int victim = msg.ReadByte();
char killedwith[32];
char fullkilledwith[128];
msg.ReadString( killedwith, sizeof(killedwith) );
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 = g_PR->GetPlayerName( killer );
const char *assist_name = g_PR->GetPlayerName( assist );
const char *victim_name = g_PR->GetPlayerName( victim );
if ( !killer_name )
killer_name = "";
if ( !assist_name )
assist_name = "";
if ( !victim_name )
victim_name = "";
// Make a new death notice
DeathNoticeItem deathMsg;
deathMsg.Killer.iEntIndex = killer;
deathMsg.Assist.iEntIndex = assist;
deathMsg.Victim.iEntIndex = victim;
Q_strncpy( deathMsg.Killer.szName, killer_name, MAX_PLAYER_NAME_LENGTH );
Q_strncpy( deathMsg.Assist.szName, assist_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 = ( !killer || killer == victim );
deathMsg.iTeamKill = ( !strcmp( fullkilledwith, "d_teammate" ) );
// 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_world" ) )
{
Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s died.\n", deathMsg.Victim.szName );
}
else
{
Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s suicided.\n", deathMsg.Victim.szName );
}
}
else if ( deathMsg.iTeamKill )
{
Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s teamkilled %s", deathMsg.Killer.szName, deathMsg.Victim.szName );
}
else
{
if ( assist )
{
Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s and %s killed %s", deathMsg.Killer.szName, deathMsg.Assist.szName, deathMsg.Victim.szName );
}
else
{
Q_snprintf( sDeathMsg, sizeof( sDeathMsg ), "%s killed %s", deathMsg.Killer.szName, deathMsg.Victim.szName );
}
}
if ( fullkilledwith && *fullkilledwith && (*fullkilledwith > 13 ) && strcmp( fullkilledwith, "d_world" ) && !deathMsg.iTeamKill )
{
Q_strncat( sDeathMsg, VarArgs( " with %s.\n", fullkilledwith+2 ), sizeof( sDeathMsg ), COPY_ALL_CHARACTERS );
}
Msg( sDeathMsg );
}