//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Draws DoD:S's death notices // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "hudelement.h" #include "hud_macros.h" #include "c_playerresource.h" #include "iclientmode.h" #include #include #include #include #include #include "c_baseplayer.h" #include "c_team.h" #include "dod_shareddefs.h" #include "clientmode_dod.h" #include "c_dod_player.h" #include "c_dod_playerresource.h" #include "c_dod_objective_resource.h" #include "dod_hud_freezepanel.h" #include "engine/IEngineSound.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar hud_deathnotice_time( "hud_deathnotice_time", "6", 0 ); ConVar cl_deathicon_width( "cl_deathicon_width", "57" ); ConVar cl_deathicon_height( "cl_deathicon_height", "18" ); #define MAX_DEATHNOTICE_NAME_LENGTH 128 // to hold multiple player cappers // a very useful function for getting the ideal scale factor of a sprite that's to be // scaled into a space float GetScale( int nIconWidth, int nIconHeight, int nWidth, int nHeight ); // Player entries in a death notice struct DeathNoticePlayer { char szName[MAX_DEATHNOTICE_NAME_LENGTH]; int iEntIndex; }; // Contents of each entry in our list of death notices struct DeathNoticeItem { DeathNoticeItem() { iconDeath = NULL; bSuicide = false; bCapMsg = false; bLocalPlayerInvolved = false; bDefense = false; bDominating = false; } DeathNoticePlayer Killer; DeathNoticePlayer Victim; CHudTexture *iconDeath; bool bSuicide; float flDisplayTime; // When I see a boolean like this, I know serious bullshit is afoot! bool bCapMsg; // if this is set, this is a flag cap msg. // Killer.szName is the list of players that capped // Victim.szName is the localized point name // iMaterial is the material index of the flag icon to show // iEntIndex in Killer is the capping team int iMaterial; bool bLocalPlayerInvolved; // Is the local player a capper, killer or victim in this message bool bDefense; bool bDominating; wchar_t wzInfoText[32]; // any additional text to display next to icon }; //----------------------------------------------------------------------------- // 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( int iTeamNumber ); void RetireExpiredDeathNotices( void ); void FireGameEvent( IGameEvent * event); void DrawBackgroundBox( int x, int y, int w, int h, bool bLocalPlayerInvolved ); int DrawDefenseItem( DeathNoticeItem *pItem, int xRight, int y ); int DrawDeathNoticeItem( DeathNoticeItem *pItem, int x, int y ); int DrawDominationNoticeItem( DeathNoticeItem *pItem, int xRight, int y ); virtual bool IsVisible( void ); void AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey ); void PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType ); private: CPanelAnimationVarAliasType( float, m_flLineHeight, "LineHeight", "15", "proportional_float" ); CPanelAnimationVar( float, m_flMaxDeathNotices, "MaxDeathNotices", "4" ); CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudNumbersTimer" ); CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "255 255 255 100" ); CPanelAnimationVar( Color, m_ActiveBackgroundColor, "ActiveBackgroundColor", "255 255 255 140" ); // Special death notice icons CHudTexture *m_iconD_skull; CHudTexture *m_pIconDefended; CHudTexture *m_iconDomination; CUtlVector m_DeathNotices; int m_iMaterialTexture; }; 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_skull = NULL; m_iconDomination = NULL; SetHiddenBits( HIDEHUD_MISCSTATUS ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDeathNotice::ApplySchemeSettings( IScheme *scheme ) { BaseClass::ApplySchemeSettings( scheme ); SetPaintBackgroundEnabled( false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDeathNotice::Init( void ) { ListenForGameEvent( "player_death" ); ListenForGameEvent( "dod_point_captured" ); ListenForGameEvent( "dod_capture_blocked" ); m_iMaterialTexture = vgui::surface()->CreateNewTextureID(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDeathNotice::VidInit( void ) { m_iconD_skull = gHUD.GetIcon( "d_skull_dod" ); m_pIconDefended = gHUD.GetIcon( "icon_defended" ); m_iconDomination = gHUD.GetIcon( "leaderboard_dominated" ); 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: Hide if we just took a freezecam screenshot //----------------------------------------------------------------------------- bool CHudDeathNotice::IsVisible( void ) { if ( IsTakingAFreezecamScreenshot() ) return false; return BaseClass::IsVisible(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDeathNotice::SetColorForNoticePlayer( int iTeamNumber ) { Color c = g_PR->GetTeamColor( iTeamNumber ); surface()->DrawSetTextColor( c ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDeathNotice::Paint() { int yStart = GetClientModeDODNormal()->GetDeathMessageStartHeight(); surface()->DrawSetTextFont( m_hTextFont ); int y = yStart; int x = GetWide(); int iCount = m_DeathNotices.Count(); for ( int i = 0; i < iCount; i++ ) { if ( m_DeathNotices[i].bDefense ) y += DrawDefenseItem( &m_DeathNotices[i], x, y ); else y += DrawDeathNoticeItem( &m_DeathNotices[i], x, y ); } // Now retire any death notices that have expired RetireExpiredDeathNotices(); } int CHudDeathNotice::DrawDefenseItem( DeathNoticeItem *pItem, int xRight, int y ) { // Get the team numbers for the players involved int iKillerTeam = pItem->Killer.iEntIndex; int iVictimTeam = TEAM_UNASSIGNED; wchar_t victim[ 256 ]; wchar_t killer[ 256 ]; g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) ); g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) ); // Get the local position for this notice int len = UTIL_ComputeStringWidth( m_hTextFont, victim ); int iconWide; int iconTall; float scale = ( (float)ScreenHeight() / 480.0f ) * 0.6; //scale based on 800x600 iconWide = iconTall = (int)( scale * 16.0 ); int iconDefSize = (int)( scale * 32.0 ); int spacerX = XRES(5); int x = xRight - len - spacerX - iconWide - XRES(10); x -= iconDefSize; surface()->DrawSetTextFont( m_hTextFont ); int iFontTall = vgui::surface()->GetFontTall( m_hTextFont ); int yText = y + ( iconDefSize - iFontTall ) / 2; int boxWidth = len + iconWide + spacerX; boxWidth += iconDefSize; int boxHeight = m_flLineHeight; int boxBorder = XRES(2); // Draw Defender's name int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap x -= nameWidth; boxWidth += nameWidth; DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved ); SetColorForNoticePlayer( iKillerTeam ); // Draw killer's name surface()->DrawSetTextPos( x, yText ); surface()->DrawUnicodeString( killer ); surface()->DrawGetTextPos( x, yText ); x += spacerX; Color iconColor( 255, 80, 0, 255 ); // Draw shield + cap icon m_pIconDefended->DrawSelf( x, y, iconDefSize, iconDefSize, Color(255,255,255,255) ); x += iconDefSize + spacerX; const char *szMatName = GetMaterialNameFromIndex( pItem->iMaterial ); vgui::surface()->DrawSetColor( Color(255,255,255,255) ); vgui::surface()->DrawSetTextureFile( m_iMaterialTexture, szMatName, true, false); int iconY = y + iconDefSize / 2 - iconTall / 2; vgui::surface()->DrawTexturedRect( x, iconY, x + iconWide, iconY + iconTall ); x += iconWide; SetColorForNoticePlayer( iVictimTeam ); // Draw location name surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it surface()->DrawSetTextPos( x, yText ); surface()->DrawUnicodeString( victim ); // return height of this item // base spacing on the height of the background box return boxHeight + boxBorder*2 + YRES(4); } // X is right side, do a right align! int CHudDeathNotice::DrawDeathNoticeItem( DeathNoticeItem *pItem, int xRight, int y ) { if ( pItem->bDominating ) { return DrawDominationNoticeItem( pItem, xRight, y ); } bool bCapMsg = pItem->bCapMsg; // Get the team numbers for the players involved int iKillerTeam = TEAM_UNASSIGNED; int iVictimTeam = TEAM_UNASSIGNED; if ( bCapMsg ) { iKillerTeam = pItem->Killer.iEntIndex; iVictimTeam = TEAM_UNASSIGNED; } else { if( g_PR ) { iKillerTeam = g_PR->GetTeam( pItem->Killer.iEntIndex ); iVictimTeam = g_PR->GetTeam( pItem->Victim.iEntIndex ); } } wchar_t victim[ 256 ]; wchar_t killer[ 256 ]; g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) ); g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) ); // Get the local position for this notice int len = UTIL_ComputeStringWidth( m_hTextFont, victim ); int iconWide; int iconTall; CHudTexture *icon = pItem->iconDeath; Assert( icon ); if ( bCapMsg ) { float scale = ( (float)ScreenHeight() / 480.0f ) * 0.6; //scale based on 800x600 iconWide = iconTall = (int)( scale * 32.0 ); } else { if ( !icon ) return 0; if( icon->bRenderUsingFont ) { iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont ); iconTall = surface()->GetFontTall( icon->hFont ); } else { float scale = GetScale( icon->Width(), icon->Height(), XRES(cl_deathicon_width.GetInt()), YRES(cl_deathicon_height.GetInt()) ); iconWide = (int)( scale * (float)icon->Width() ); iconTall = (int)( scale * (float)icon->Height() ); } } int spacerX = XRES(5); int x = xRight - len - spacerX - iconWide - XRES(10); if ( pItem->bDefense ) { x -= iconWide; //m_iDefendedIconSize; } surface()->DrawSetTextFont( m_hTextFont ); int iFontTall = vgui::surface()->GetFontTall( m_hTextFont ); int boxWidth = len + iconWide + spacerX; if ( pItem->bDefense ) { boxWidth += iconWide; //m_iDefendedIconSize; } int boxHeight = m_flLineHeight; //MIN( iconTall, m_flLineHeight ); int boxBorder = XRES(2); int yText = y + ( m_flLineHeight - iFontTall ) / 2; int yIcon = y + ( m_flLineHeight - iconTall ) / 2; // Only draw killers name if it wasn't a suicide if ( !pItem->bSuicide ) { int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap x -= nameWidth; boxWidth += nameWidth; DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved ); SetColorForNoticePlayer( iKillerTeam ); // Draw killer's name surface()->DrawSetTextPos( x, yText ); const wchar_t *p = killer; while ( *p ) { surface()->DrawUnicodeChar( *p++ ); } surface()->DrawGetTextPos( x, yText ); x += spacerX; } else { DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved ); } Color iconColor( 255, 80, 0, 255 ); // Draw death weapon or cap icon if ( bCapMsg ) { const char *szMatName = GetMaterialNameFromIndex( pItem->iMaterial ); vgui::surface()->DrawSetColor( Color(255,255,255,255) ); vgui::surface()->DrawSetTextureFile( m_iMaterialTexture, szMatName, true, false); vgui::surface()->DrawTexturedRect( x, yIcon, x + iconWide, yIcon + iconTall ); x += iconWide + spacerX; } else { //If we're using a font char, this will ignore iconTall and iconWide icon->DrawSelf( x, yIcon, iconWide, iconTall, iconColor ); x += iconWide + spacerX; } SetColorForNoticePlayer( iVictimTeam ); // Draw victims name surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it surface()->DrawSetTextPos( x, yText ); const wchar_t *p = victim; while ( *p ) { surface()->DrawUnicodeChar( *p++ ); } // return height of this item // base spacing on the height of the background box return boxHeight + boxBorder*2 + YRES(4); } int CHudDeathNotice::DrawDominationNoticeItem( DeathNoticeItem *pItem, int xRight, int y ) { // Get the team numbers for the players involved int iKillerTeam = TEAM_UNASSIGNED; int iVictimTeam = TEAM_UNASSIGNED; if( g_PR ) { iKillerTeam = g_PR->GetTeam( pItem->Killer.iEntIndex ); iVictimTeam = g_PR->GetTeam( pItem->Victim.iEntIndex ); } wchar_t victim[ 256 ]; wchar_t killer[ 256 ]; g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Victim.szName, victim, sizeof( victim ) ); g_pVGuiLocalize->ConvertANSIToUnicode( pItem->Killer.szName, killer, sizeof( killer ) ); // Get the local position for this notice int len = UTIL_ComputeStringWidth( m_hTextFont, victim ); int iconWide; int iconTall; Assert( pItem->iconDeath ); CHudTexture *icon = pItem->iconDeath; if ( !icon ) return 0; if( icon->bRenderUsingFont ) { iconWide = surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont ); iconTall = surface()->GetFontTall( icon->hFont ); } else { float scale = GetScale( icon->Width(), icon->Height(), XRES(cl_deathicon_width.GetInt()), YRES(cl_deathicon_height.GetInt()) ); iconWide = (int)( scale * (float)icon->Width() ); iconTall = (int)( scale * (float)icon->Height() ); } int spacerX = XRES(5); int x = xRight - len - spacerX - iconWide - XRES(10); surface()->DrawSetTextFont( m_hTextFont ); int iFontTall = vgui::surface()->GetFontTall( m_hTextFont ); int boxWidth = len + iconWide + spacerX; int iDominatingLen = UTIL_ComputeStringWidth( m_hTextFont, pItem->wzInfoText ) + XRES(2); x -= iDominatingLen; boxWidth += iDominatingLen; int boxHeight = m_flLineHeight; //MIN( iconTall, m_flLineHeight ); int boxBorder = XRES(2); int yText = y + ( m_flLineHeight - iFontTall ) / 2; int yIcon = y + ( m_flLineHeight - iconTall ) / 2; int nameWidth = UTIL_ComputeStringWidth( m_hTextFont, killer ) + spacerX; // gap x -= nameWidth; boxWidth += nameWidth; DrawBackgroundBox( x-boxBorder, y-boxBorder, boxWidth+2*boxBorder, boxHeight+2*boxBorder, pItem->bLocalPlayerInvolved ); SetColorForNoticePlayer( iKillerTeam ); // Draw killer's name surface()->DrawSetTextPos( x, yText ); const wchar_t *p = killer; while ( *p ) { surface()->DrawUnicodeChar( *p++ ); } surface()->DrawGetTextPos( x, yText ); x += spacerX; Color iconColor( 255, 80, 0, 255 ); //If we're using a font char, this will ignore iconTall and iconWide icon->DrawSelf( x, yIcon, iconWide, iconTall, iconColor ); x += iconWide + spacerX; surface()->DrawSetTextColor( Color(255,255,255,255) ); // Draw dominating string surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it surface()->DrawSetTextPos( x, yText ); p = pItem->wzInfoText; while ( *p ) { surface()->DrawUnicodeChar( *p++ ); } x += iDominatingLen; SetColorForNoticePlayer( iVictimTeam ); // Draw victims name //surface()->DrawSetTextFont( m_hTextFont ); //reset the font, draw icon can change it surface()->DrawSetTextPos( x, yText ); p = victim; while ( *p ) { surface()->DrawUnicodeChar( *p++ ); } // return height of this item // base spacing on the height of the background box return boxHeight + boxBorder*2 + YRES(4); } ConVar cl_deathicon_bg_alpha( "cl_deathicon_bg_alpha", "1.0" ); void CHudDeathNotice::DrawBackgroundBox( int x, int y, int w, int h, bool bLocalPlayerInvolved ) { Panel::DrawBox( x, y, w, h, bLocalPlayerInvolved ? m_ActiveBackgroundColor : m_BackgroundColor, cl_deathicon_bg_alpha.GetFloat() ); } //----------------------------------------------------------------------------- // 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; if ( hud_deathnotice_time.GetFloat() == 0 ) return; C_DODPlayer *pLocal = C_DODPlayer::GetLocalDODPlayer(); Assert( pLocal ); if ( !pLocal ) return; int iLocalPlayerIndex = pLocal->entindex(); const char *pEventName = event->GetName(); if ( Q_strcmp( "dod_point_captured", pEventName ) == 0 ) { // Cap point index int cp = event->GetInt( "cp", -1 ); Assert( cp >= 0 ); // Cap point name ( MATTTODO: can't we find this from the point index ? ) const char *pName = event->GetString( "cpname", "Unnamed Control Point" ); const wchar_t *pBuf = g_pVGuiLocalize->Find( pName ); // Array of capper indeces const char *cappers = event->GetString("cappers"); DeathNoticeItem capMsg; capMsg.bCapMsg = true; capMsg.bSuicide = false; capMsg.bDefense = false; capMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat(); capMsg.bLocalPlayerInvolved = false; char szCappers[256]; szCappers[0] = '\0'; int len = Q_strlen(cappers); for( int i=0;i 0 && iPlayerIndex <= gpGlobals->maxClients ); const char *pPlayerName = g_PR->GetPlayerName( iPlayerIndex ); if ( i == 0 ) { // use first player as the team capMsg.Killer.iEntIndex = g_PR->GetTeam( iPlayerIndex ); capMsg.iMaterial = g_pObjectiveResource->GetIconForTeam( cp, capMsg.Killer.iEntIndex ); if ( g_pObjectiveResource->GetBombsRequired( cp ) > 0 ) { capMsg.iMaterial = g_pObjectiveResource->GetCPBombedIcon( cp ); } } else { Q_strncat( szCappers, ", ", sizeof(szCappers), 2 ); } Q_strncat( szCappers, pPlayerName, sizeof(szCappers), COPY_ALL_CHARACTERS ); } Q_strncpy( capMsg.Killer.szName, szCappers, sizeof(capMsg.Killer.szName) ); if ( pBuf ) { g_pVGuiLocalize->ConvertUnicodeToANSI( pBuf, capMsg.Victim.szName, sizeof(capMsg.Victim.szName) ); } else { Q_strncpy( capMsg.Victim.szName, pName, sizeof(capMsg.Victim.szName) ); } // 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); } m_DeathNotices.AddToTail( capMsg ); // print a log message char szLogMsg[512]; Q_snprintf( szLogMsg, sizeof( szLogMsg ), "%s captured %s for the %s\n", capMsg.Killer.szName, capMsg.Victim.szName, capMsg.Killer.iEntIndex == TEAM_ALLIES ? "U.S. Army" : "Wermacht" ); Msg( "%s",szLogMsg ); } else if ( Q_strcmp( "dod_capture_blocked", pEventName ) == 0 ) { // Cap point index int cp = event->GetInt( "cp", -1 ); Assert( cp >= 0 ); // Cap point name const char *pName = event->GetString( "cpname", "Unnamed Control Point" ); const wchar_t *pBuf = g_pVGuiLocalize->Find( pName ); // A single blocker entindex int iBlocker = event->GetInt("blocker"); DeathNoticeItem capMsg; capMsg.bCapMsg = true; capMsg.bSuicide = false; capMsg.bDefense = true; capMsg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat(); capMsg.bLocalPlayerInvolved = false; capMsg.Killer.iEntIndex = g_PR->GetTeam( iBlocker ); capMsg.iMaterial = g_pObjectiveResource->GetIconForTeam( cp, capMsg.Killer.iEntIndex ); if ( iBlocker == iLocalPlayerIndex ) capMsg.bLocalPlayerInvolved = true; Q_strncpy( capMsg.Killer.szName, g_PR->GetPlayerName( iBlocker ), sizeof(capMsg.Killer.szName) ); char buf[128]; if ( pBuf ) { g_pVGuiLocalize->ConvertUnicodeToANSI( pBuf, buf, sizeof(buf) ); pName = buf; } Q_snprintf( capMsg.Victim.szName, sizeof(capMsg.Victim.szName), " - %s", pName ); // 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); } m_DeathNotices.AddToTail( capMsg ); } else if ( Q_strcmp( "player_death", pEventName ) == 0 ) { int killer = engine->GetPlayerForUserID( event->GetInt("attacker") ); int victim = engine->GetPlayerForUserID( event->GetInt("userid") ); const char *killedwith = event->GetString( "weapon" ); 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 = g_PR->GetPlayerName( killer ); const char *victim_name = g_PR->GetPlayerName( victim ); if ( !killer_name ) killer_name = ""; if ( !victim_name ) victim_name = ""; // Make a new death notice DeathNoticeItem deathMsg; deathMsg.Killer.iEntIndex = killer; deathMsg.Victim.iEntIndex = victim; 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.bSuicide = ( !killer || killer == victim ); deathMsg.bCapMsg = false; deathMsg.bDefense = false; deathMsg.iMaterial = -1; deathMsg.bLocalPlayerInvolved = ( killer == iLocalPlayerIndex || victim == iLocalPlayerIndex ); // 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 ); if ( event->GetInt( "dominated" ) > 0 ) { AddAdditionalMsg( killer, victim, "#Msg_Dominating" ); PlayRivalrySounds( killer, victim, DOD_DEATHFLAG_DOMINATION ); } if ( event->GetInt( "revenge" ) > 0 ) { AddAdditionalMsg( killer, victim, "#Msg_Revenge" ); PlayRivalrySounds( killer, victim, DOD_DEATHFLAG_REVENGE ); } char sDeathMsg[512]; // Record the death notice in the console if ( deathMsg.bSuicide ) { 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 ); } } //----------------------------------------------------------------------------- // Purpose: Adds an additional death message //----------------------------------------------------------------------------- void CHudDeathNotice::AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey ) { int iMsg = m_DeathNotices.AddToTail(); DeathNoticeItem &msg = m_DeathNotices[iMsg]; msg.Killer.iEntIndex = iKillerID; msg.Victim.iEntIndex = iVictimID; Q_strncpy( msg.Killer.szName, g_PR->GetPlayerName( iKillerID ), ARRAYSIZE( msg.Killer.szName ) ); Q_strncpy( msg.Victim.szName, g_PR->GetPlayerName( iVictimID ), ARRAYSIZE( msg.Victim.szName ) ); msg.flDisplayTime = gpGlobals->curtime + hud_deathnotice_time.GetFloat(); msg.bSuicide = false; msg.bCapMsg = false; msg.bDefense = false; msg.iMaterial = -1; msg.bDominating = true; const wchar_t *wzMsg = g_pVGuiLocalize->Find( pMsgKey ); if ( wzMsg ) { V_wcsncpy( msg.wzInfoText, wzMsg, sizeof( msg.wzInfoText ) ); } msg.iconDeath = m_iconDomination; int iLocalPlayerIndex = GetLocalPlayerIndex(); if ( iLocalPlayerIndex == iVictimID || iLocalPlayerIndex == iKillerID ) { msg.bLocalPlayerInvolved = true; } } ConVar dod_playrivalrysounds( "dod_playrivalrysounds", "1", FCVAR_ARCHIVE ); void CHudDeathNotice::PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType ) { if ( dod_playrivalrysounds.GetBool() == false ) return; int iLocalPlayerIndex = GetLocalPlayerIndex(); //We're not involved in this kill if ( iKillerIndex != iLocalPlayerIndex && iVictimIndex != iLocalPlayerIndex ) return; const char *pszSoundName = NULL; if ( iType == DOD_DEATHFLAG_DOMINATION ) { if ( iKillerIndex == iLocalPlayerIndex ) { pszSoundName = "Game.Domination"; } else if ( iVictimIndex == iLocalPlayerIndex ) { pszSoundName = "Game.Nemesis"; } } else if ( iType == DOD_DEATHFLAG_REVENGE ) { pszSoundName = "Game.Revenge"; } CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName ); }