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.
1635 lines
56 KiB
1635 lines
56 KiB
![]()
4 years ago
|
//========= 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 "vgui_controls/TextImage.h"
|
||
|
#include <KeyValues.h>
|
||
|
#include "c_baseplayer.h"
|
||
|
#include "c_team.h"
|
||
|
#include "gcsdk/gcclientsdk.h"
|
||
|
#include "tf_gcmessages.h"
|
||
|
#include "tf_item_inventory.h"
|
||
|
|
||
|
#include "hud_basedeathnotice.h"
|
||
|
|
||
|
#include "tf_shareddefs.h"
|
||
|
#include "clientmode_tf.h"
|
||
|
#include "c_tf_player.h"
|
||
|
#include "c_tf_playerresource.h"
|
||
|
#include "tf_hud_freezepanel.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "tf_controls.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "econ_notifications.h"
|
||
|
//#include "econ/econ_controls.h"
|
||
|
#include "passtime_game_events.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
|
||
|
using namespace vgui;
|
||
|
|
||
|
// Must match resource/tf_objects.txt!!!
|
||
|
const char *szLocalizedObjectNames[OBJ_LAST] =
|
||
|
{
|
||
|
"#TF_Object_Dispenser",
|
||
|
"#TF_Object_Tele",
|
||
|
"#TF_Object_Sentry",
|
||
|
"#TF_object_Sapper"
|
||
|
};
|
||
|
|
||
|
ConVar cl_hud_killstreak_display_time( "cl_hud_killstreak_display_time", "3", FCVAR_ARCHIVE, "How long a killstreak notice stays on the screen (in seconds). Range is from 0 to 100." );
|
||
|
ConVar cl_hud_killstreak_display_fontsize( "cl_hud_killstreak_display_fontsize", "0", FCVAR_ARCHIVE, "Adjusts font size of killstreak notices. Range is from 0 to 2 (default is 1)." );
|
||
|
ConVar cl_hud_killstreak_display_alpha( "cl_hud_killstreak_display_alpha", "120", FCVAR_ARCHIVE, "Adjusts font alpha value of killstreak notices. Range is from 0 to 255 (default is 200)." );
|
||
|
|
||
|
const int STREAK_MIN = 5;
|
||
|
const int STREAK_MIN_MVM = 20;
|
||
|
const int STREAK_MIN_DUCKS = 10;
|
||
|
|
||
|
static int MinStreakForType( CTFPlayerShared::ETFStreak eStreakType )
|
||
|
{
|
||
|
bool bIsMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode();
|
||
|
if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks )
|
||
|
{
|
||
|
return STREAK_MIN_DUCKS;
|
||
|
}
|
||
|
if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
if ( bIsMvM )
|
||
|
{
|
||
|
return STREAK_MIN_MVM;
|
||
|
}
|
||
|
return STREAK_MIN;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// CTFStreakNotice
|
||
|
//=========================================================
|
||
|
class CTFStreakNotice : public CHudElement, public vgui::EditablePanel
|
||
|
{
|
||
|
DECLARE_CLASS_SIMPLE( CTFStreakNotice, vgui::EditablePanel );
|
||
|
public:
|
||
|
CTFStreakNotice( const char *pName );
|
||
|
|
||
|
virtual bool ShouldDraw( void ) OVERRIDE;
|
||
|
virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) OVERRIDE;
|
||
|
virtual void Paint( void ) OVERRIDE;
|
||
|
|
||
|
void StreakEnded( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iStreak );
|
||
|
void StreakUpdated( CTFPlayerShared::ETFStreak eStreakType, int iPlayerID, int iStreak, int iStreakIncrement );
|
||
|
|
||
|
bool IsCurrentStreakHigherPriority( CTFPlayerShared::ETFStreak eStreakType, int iStreak );
|
||
|
HFont GetStreakFont( void );
|
||
|
|
||
|
private:
|
||
|
CExLabel *m_pLabel;
|
||
|
EditablePanel *m_pBackground;
|
||
|
|
||
|
float m_flLastMessageTime;
|
||
|
int m_nCurrStreakCount;
|
||
|
CTFPlayerShared::ETFStreak m_nCurrStreakType;
|
||
|
|
||
|
int m_nLabelXPos;
|
||
|
int m_nLabelYPos;
|
||
|
|
||
|
CHudTexture *m_iconKillStreak;
|
||
|
CHudTexture *m_iconDuckStreak;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFStreakNotice::CTFStreakNotice( const char *pName ) : CHudElement( pName ), vgui::EditablePanel( NULL, pName )
|
||
|
{
|
||
|
SetParent( g_pClientMode->GetViewport() );
|
||
|
|
||
|
m_pBackground = new EditablePanel( this, "Background" );
|
||
|
m_pLabel = new CExLabel( this, "SplashLabel", "" );
|
||
|
|
||
|
m_flLastMessageTime = -10.0f;
|
||
|
m_nCurrStreakCount = 0;
|
||
|
m_nCurrStreakType = (CTFPlayerShared::ETFStreak)0;
|
||
|
|
||
|
m_iconKillStreak = gHUD.GetIcon( "leaderboard_streak" );
|
||
|
m_iconDuckStreak = gHUD.GetIcon( "eotl_duck" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFStreakNotice::ShouldDraw( void )
|
||
|
{
|
||
|
if ( !CHudElement::ShouldDraw() )
|
||
|
return false;
|
||
|
|
||
|
C_TFPlayer *pPlayer = CTFPlayer::GetLocalTFPlayer();
|
||
|
if ( !pPlayer )
|
||
|
return false;
|
||
|
|
||
|
if ( IsTakingAFreezecamScreenshot() )
|
||
|
return false;
|
||
|
|
||
|
return m_nCurrStreakCount > 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFStreakNotice::ApplySchemeSettings( IScheme *pScheme )
|
||
|
{
|
||
|
BaseClass::ApplySchemeSettings( pScheme );
|
||
|
LoadControlSettings( "resource/UI/HudKillStreakNotice.res" );
|
||
|
|
||
|
m_pLabel->GetPos( m_nLabelXPos, m_nLabelYPos );
|
||
|
|
||
|
SetSize( XRES(640), YRES(480) );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFStreakNotice::Paint( void )
|
||
|
{
|
||
|
int nDisplayTime = clamp( cl_hud_killstreak_display_time.GetInt(), 1, 100 );
|
||
|
if ( m_flLastMessageTime + nDisplayTime < gpGlobals->realtime )
|
||
|
{
|
||
|
SetVisible( false );
|
||
|
m_nCurrStreakCount = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float flFadeTime = 1.5f;
|
||
|
float flTimeRemaining = (float)nDisplayTime - ( gpGlobals->realtime - m_flLastMessageTime );
|
||
|
|
||
|
SetVisible( true );
|
||
|
if ( flTimeRemaining > flFadeTime )
|
||
|
{
|
||
|
SetAlpha( 255 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
float flAlpha = RemapValClamped( flTimeRemaining, flFadeTime, 0.f, 255.f, 0.f );
|
||
|
SetAlpha( flAlpha );
|
||
|
}
|
||
|
|
||
|
// Move labels down when in spectator
|
||
|
C_TFPlayer *pPlayer = CTFPlayer::GetLocalTFPlayer();
|
||
|
CHudTexture *pIcon = ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Ducks || m_nCurrStreakType == CTFPlayerShared::kTFStreak_Duck_levelup ) ? m_iconDuckStreak : m_iconKillStreak;
|
||
|
if ( pPlayer && pIcon )
|
||
|
{
|
||
|
int nYOffset = ( pPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ? YRES(40) : 0 );
|
||
|
|
||
|
int iWide, iTall;
|
||
|
m_pLabel->GetContentSize( iWide, iTall );
|
||
|
m_pLabel->SizeToContents();
|
||
|
m_pLabel->SetPos( XRES(320) - iWide / 2, m_nLabelYPos + nYOffset );
|
||
|
|
||
|
m_pBackground->SetSize( iWide + iTall / 2, iTall ); // add in icon width
|
||
|
m_pBackground->SetPos( XRES(315) - iWide / 2, m_nLabelYPos + nYOffset);
|
||
|
|
||
|
wchar_t szTitle[256];
|
||
|
m_pLabel->GetText( szTitle, 256 );
|
||
|
HFont hFont = GetStreakFont();
|
||
|
int iTextWide= UTIL_ComputeStringWidth( hFont, szTitle );
|
||
|
pIcon->DrawSelf( XRES(320) - (iWide / 2) + iTextWide, m_nLabelYPos + nYOffset, iTall, iTall, Color(235, 226, 202, GetAlpha() ) );
|
||
|
}
|
||
|
|
||
|
BaseClass::Paint();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFStreakNotice::StreakEnded( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iStreak )
|
||
|
{
|
||
|
if ( iStreak < 10 )
|
||
|
return;
|
||
|
|
||
|
if ( IsCurrentStreakHigherPriority( eStreakType, iStreak ) )
|
||
|
return;
|
||
|
|
||
|
// Temp override all messages
|
||
|
// Add New message
|
||
|
m_flLastMessageTime = gpGlobals->realtime;
|
||
|
|
||
|
// Generate the String
|
||
|
const wchar_t *wzMsg = NULL;
|
||
|
bool bSelfKill = false;
|
||
|
if ( iKillerID == iVictimID )
|
||
|
{
|
||
|
wzMsg = g_pVGuiLocalize->Find( ( eStreakType == CTFPlayerShared::kTFStreak_Ducks ) ? "#Msg_DuckStreakEndSelf" : "#Msg_KillStreakEndSelf" );
|
||
|
bSelfKill = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wzMsg = g_pVGuiLocalize->Find( ( eStreakType == CTFPlayerShared::kTFStreak_Ducks ) ? "#Msg_DuckStreakEnd" : "#Msg_KillStreakEnd" );
|
||
|
}
|
||
|
|
||
|
if ( !wzMsg )
|
||
|
return;
|
||
|
|
||
|
// m_nCurrStreakCount = iStreak;
|
||
|
|
||
|
// Killer Name
|
||
|
wchar_t wszKillerName[MAX_PLAYER_NAME_LENGTH / 2];
|
||
|
g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iKillerID ), wszKillerName, sizeof(wszKillerName) );
|
||
|
|
||
|
// Victim Name
|
||
|
wchar_t wszVictimName[MAX_PLAYER_NAME_LENGTH / 2];
|
||
|
g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iVictimID ), wszVictimName, sizeof(wszVictimName) );
|
||
|
|
||
|
// Count
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iStreak );
|
||
|
|
||
|
wchar_t wTemp[256];
|
||
|
if ( bSelfKill )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 2, wszKillerName, wzCount );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 3, wszKillerName, wszVictimName, wzCount );
|
||
|
}
|
||
|
|
||
|
HFont hFont = GetStreakFont();
|
||
|
if ( m_pLabel->GetFont() != hFont )
|
||
|
{
|
||
|
m_pLabel->SetFont( hFont );
|
||
|
}
|
||
|
m_pLabel->SetText( wTemp );
|
||
|
|
||
|
// Get player Team for color
|
||
|
Color cKillerColor(235, 226, 202, 255);
|
||
|
if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_RED )
|
||
|
{
|
||
|
cKillerColor = COLOR_RED;
|
||
|
}
|
||
|
else if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_BLUE )
|
||
|
{
|
||
|
cKillerColor = COLOR_BLUE;
|
||
|
}
|
||
|
|
||
|
Color cVictimColor(235, 226, 202, 255);
|
||
|
if ( g_PR->GetTeam( iVictimID ) == TF_TEAM_RED )
|
||
|
{
|
||
|
cVictimColor = COLOR_RED;
|
||
|
}
|
||
|
else if ( g_PR->GetTeam( iVictimID ) == TF_TEAM_BLUE )
|
||
|
{
|
||
|
cVictimColor = COLOR_BLUE;
|
||
|
}
|
||
|
|
||
|
m_pLabel->GetTextImage()->ClearColorChangeStream();
|
||
|
|
||
|
// We change the title's text color to match the colors of the matching model panel backgrounds
|
||
|
wchar_t *txt = wTemp;
|
||
|
int iWChars = 0;
|
||
|
while ( txt && *txt )
|
||
|
{
|
||
|
switch ( *txt )
|
||
|
{
|
||
|
case 0x01: // Normal color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color(235, 226, 202, cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
case 0x02: // Team color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color( cKillerColor.r(), cKillerColor.g(), cKillerColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
case 0x03: // Item 2 color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color( cVictimColor.r(), cVictimColor.g(), cVictimColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
txt++;
|
||
|
iWChars++;
|
||
|
}
|
||
|
|
||
|
m_flLastMessageTime = gpGlobals->realtime;
|
||
|
SetVisible( true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFStreakNotice::StreakUpdated( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iStreak, int iStreakIncrement )
|
||
|
{
|
||
|
// Temp override all messages
|
||
|
// Add New message
|
||
|
|
||
|
bool bIsMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode();
|
||
|
int iStreakMin = MinStreakForType( eStreakType );
|
||
|
|
||
|
if ( IsCurrentStreakHigherPriority( eStreakType, iStreak ) )
|
||
|
return;
|
||
|
|
||
|
// Is this message worth responding to
|
||
|
int iStreakTier = 0;
|
||
|
if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks)
|
||
|
{
|
||
|
// Notices at 15, 30, then increments of 50. We may increment by multiple ducks per kill, so check if we passed over a milestone.
|
||
|
if ( iStreak >= 15 && ( iStreak - iStreakIncrement < 15 ) )
|
||
|
{
|
||
|
iStreakTier = 1;
|
||
|
iStreak = 15;
|
||
|
}
|
||
|
else if ( iStreak >= 30 && ( iStreak - iStreakIncrement <30 ) )
|
||
|
{
|
||
|
iStreakTier = 2;
|
||
|
iStreak = 30;
|
||
|
}
|
||
|
else if ( iStreak > 50 && iStreak % 50 < iStreakIncrement )
|
||
|
{
|
||
|
iStreakTier = Min( 2 + ( iStreak / 50 ), 5 );
|
||
|
iStreak -= iStreak % 50;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
|
||
|
{
|
||
|
iStreakTier = 5;
|
||
|
}
|
||
|
else if ( bIsMvM )
|
||
|
{
|
||
|
if ( iStreak % iStreakMin != 0 )
|
||
|
return;
|
||
|
|
||
|
iStreakTier = iStreak / iStreakMin;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( iStreak == 5 )
|
||
|
{
|
||
|
iStreakTier = 1;
|
||
|
}
|
||
|
else if ( iStreak == 10 )
|
||
|
{
|
||
|
iStreakTier = 2;
|
||
|
}
|
||
|
else if ( iStreak == 15 )
|
||
|
{
|
||
|
iStreakTier = 3;
|
||
|
}
|
||
|
else if ( iStreak == 20 )
|
||
|
{
|
||
|
iStreakTier = 4;
|
||
|
}
|
||
|
else if ( iStreak % 10 == 0 || iStreak % 10 == 5 )
|
||
|
{
|
||
|
iStreakTier = 5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_nCurrStreakCount = iStreak;
|
||
|
m_nCurrStreakType = eStreakType;
|
||
|
|
||
|
const wchar_t *wzMsg = NULL;
|
||
|
const char *pszSoundName = "Game.KillStreak";
|
||
|
Color cCustomColor(235, 226, 202, 255);
|
||
|
if ( eStreakType == CTFPlayerShared::kTFStreak_Ducks )
|
||
|
{
|
||
|
// Duckstreak tiers
|
||
|
switch ( iStreakTier )
|
||
|
{
|
||
|
case 1:
|
||
|
// TODO duckier colors?
|
||
|
cCustomColor = Color( 112, 176, 74, 255); // Green
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak1" );
|
||
|
//pszSoundName = "Announcer.DuckStreak_Level1";
|
||
|
break;
|
||
|
case 2:
|
||
|
cCustomColor = Color( 207, 106, 50, 255); // Orange
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak2" );
|
||
|
//pszSoundName = "Announcer.DuckStreak_Level2";
|
||
|
break;
|
||
|
case 3:
|
||
|
cCustomColor = Color( 134, 80, 172, 255); // Purple
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak3" );
|
||
|
//pszSoundName = "Announcer.DuckStreak_Level3";
|
||
|
break;
|
||
|
case 4:
|
||
|
cCustomColor = Color(255, 215, 0, 255); // Gold
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak4" );
|
||
|
//pszSoundName = "Announcer.DuckStreak_Level4";
|
||
|
break;
|
||
|
default:
|
||
|
cCustomColor = Color(255, 215, 0, 255); // Still Gold
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckStreak5" );
|
||
|
//pszSoundName = "Announcer.DuckStreak_Level4";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
|
||
|
{
|
||
|
cCustomColor = Color( 255, 215, 0, 255 ); // Gold
|
||
|
switch ( RandomInt( 1, 3 ) )
|
||
|
{
|
||
|
case 1: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup1" ); break;
|
||
|
case 2: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup2" ); break;
|
||
|
default: wzMsg = g_pVGuiLocalize->Find( "#Msg_DuckLevelup3" ); break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Killstreak tiers
|
||
|
switch ( iStreakTier )
|
||
|
{
|
||
|
case 1:
|
||
|
cCustomColor = Color( 112, 176, 74, 255); // Green
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak1" );
|
||
|
//pszSoundName = "Announcer.KillStreak_Level1";
|
||
|
break;
|
||
|
case 2:
|
||
|
cCustomColor = Color( 207, 106, 50, 255); // Orange
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak2" );
|
||
|
//pszSoundName = "Announcer.KillStreak_Level2";
|
||
|
break;
|
||
|
case 3:
|
||
|
cCustomColor = Color( 134, 80, 172, 255); // Purple
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak3" );
|
||
|
//pszSoundName = "Announcer.KillStreak_Level3";
|
||
|
break;
|
||
|
case 4:
|
||
|
cCustomColor = Color(255, 215, 0, 255); // Gold
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak4" );
|
||
|
//pszSoundName = "Announcer.KillStreak_Level4";
|
||
|
break;
|
||
|
default:
|
||
|
cCustomColor = Color(255, 215, 0, 255); // Still Gold
|
||
|
wzMsg = g_pVGuiLocalize->Find( "#Msg_KillStreak5" );
|
||
|
//pszSoundName = "Announcer.KillStreak_Level4";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !wzMsg )
|
||
|
return;
|
||
|
|
||
|
// Get player Team for color
|
||
|
Color cTeamColor(235, 226, 202, 255);
|
||
|
if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_RED )
|
||
|
{
|
||
|
cTeamColor = COLOR_RED;
|
||
|
}
|
||
|
else if ( g_PR->GetTeam( iKillerID ) == TF_TEAM_BLUE )
|
||
|
{
|
||
|
cTeamColor = COLOR_BLUE;
|
||
|
}
|
||
|
|
||
|
// Generate the String
|
||
|
// Count
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iStreak );
|
||
|
|
||
|
// Name
|
||
|
wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH / 2];
|
||
|
g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( iKillerID ), wszPlayerName, sizeof(wszPlayerName) );
|
||
|
|
||
|
wchar_t wTemp[256];
|
||
|
g_pVGuiLocalize->ConstructString_safe( wTemp, wzMsg, 2, wszPlayerName, wzCount );
|
||
|
|
||
|
HFont hFont = GetStreakFont();
|
||
|
if ( m_pLabel->GetFont() != hFont )
|
||
|
{
|
||
|
m_pLabel->SetFont( hFont );
|
||
|
}
|
||
|
m_pLabel->SetText( wTemp );
|
||
|
|
||
|
// Now go through the string and find the escape characters telling us where the color changes are
|
||
|
m_pLabel->GetTextImage()->ClearColorChangeStream();
|
||
|
|
||
|
// We change the title's text color to match the colors of the matching model panel backgrounds
|
||
|
wchar_t *txt = wTemp;
|
||
|
int iWChars = 0;
|
||
|
while ( txt && *txt )
|
||
|
{
|
||
|
switch ( *txt )
|
||
|
{
|
||
|
case 0x01: // Normal color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color(235,226,202,cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
case 0x02: // Team color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color( cTeamColor.r(), cTeamColor.g(), cTeamColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
case 0x03: // Item 2 color
|
||
|
m_pLabel->GetTextImage()->AddColorChange( Color( cCustomColor.r(), cCustomColor.g(), cCustomColor.b(), cl_hud_killstreak_display_alpha.GetInt() ), iWChars );
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
txt++;
|
||
|
iWChars++;
|
||
|
}
|
||
|
|
||
|
// Play Local Sound
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
if ( iLocalPlayerIndex == iKillerID && pszSoundName )
|
||
|
{
|
||
|
CLocalPlayerFilter filter;
|
||
|
C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName );
|
||
|
}
|
||
|
|
||
|
m_flLastMessageTime = gpGlobals->realtime + (float)iStreakTier / 2.0;
|
||
|
SetVisible( true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFStreakNotice::IsCurrentStreakHigherPriority( CTFPlayerShared::ETFStreak eStreakType, int iStreak )
|
||
|
{
|
||
|
// duck level ups are highest priority
|
||
|
if ( eStreakType == CTFPlayerShared::kTFStreak_Duck_levelup )
|
||
|
return false;
|
||
|
|
||
|
if ( !m_nCurrStreakCount )
|
||
|
return false;
|
||
|
|
||
|
// Ducks never override kills
|
||
|
if ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Kills && eStreakType == CTFPlayerShared::kTFStreak_Ducks )
|
||
|
return true;
|
||
|
|
||
|
// But kills always override ducks
|
||
|
if ( m_nCurrStreakType == CTFPlayerShared::kTFStreak_Ducks && eStreakType == CTFPlayerShared::kTFStreak_Kills )
|
||
|
return false;
|
||
|
|
||
|
// Don't stomp a higher streak with a lower, unless it's been around long enough
|
||
|
float flElapsedTime = gpGlobals->realtime - m_flLastMessageTime;
|
||
|
float flDisplayMinTime = Max( ( cl_hud_killstreak_display_time.GetFloat() / 3.f ), 1.f );
|
||
|
return ( iStreak < m_nCurrStreakCount && flElapsedTime < flDisplayMinTime );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
HFont CTFStreakNotice::GetStreakFont( void )
|
||
|
{
|
||
|
vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
|
||
|
|
||
|
const char *pszFontName = "HudFontSmallestBold";
|
||
|
int nFontSize = cl_hud_killstreak_display_fontsize.GetInt(); // Default is 1: HudFontSmallBold
|
||
|
if ( nFontSize == 1 )
|
||
|
{
|
||
|
pszFontName = "HudFontSmallBold";
|
||
|
}
|
||
|
else if ( nFontSize == 2 )
|
||
|
{
|
||
|
pszFontName = "HudFontMediumSmallBold";
|
||
|
}
|
||
|
|
||
|
return pScheme->GetFont( pszFontName, true );
|
||
|
}
|
||
|
|
||
|
DECLARE_HUDELEMENT( CTFStreakNotice );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// TFDeathNotice
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CTFHudDeathNotice : public CHudBaseDeathNotice
|
||
|
{
|
||
|
DECLARE_CLASS_SIMPLE( CTFHudDeathNotice, CHudBaseDeathNotice );
|
||
|
public:
|
||
|
CTFHudDeathNotice( const char *pElementName ) : CHudBaseDeathNotice( pElementName ) {};
|
||
|
virtual void Init( void );
|
||
|
virtual void ApplySchemeSettings( vgui::IScheme *scheme );
|
||
|
virtual bool IsVisible( void );
|
||
|
virtual bool ShouldDraw( void );
|
||
|
|
||
|
virtual void FireGameEvent( IGameEvent *event );
|
||
|
void PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType );
|
||
|
virtual bool ShouldShowDeathNotice( IGameEvent *event );
|
||
|
|
||
|
protected:
|
||
|
virtual void OnGameEvent( IGameEvent *event, int iDeathNoticeMsg );
|
||
|
virtual Color GetTeamColor( int iTeamNumber, bool bLocalPlayerInvolved = false );
|
||
|
virtual Color GetInfoTextColor( int iDeathNoticeMsg );
|
||
|
virtual Color GetBackgroundColor ( int iDeathNoticeMsg );
|
||
|
virtual bool EventIsPlayerDeath( const char *eventName );
|
||
|
|
||
|
virtual int UseExistingNotice( IGameEvent *event );
|
||
|
|
||
|
private:
|
||
|
void AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey );
|
||
|
void AddStreakMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iKillerStreak, int iStreakIncrement, int iVictimID, int iDeathNoticeMsg );
|
||
|
void AddStreakEndedMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iVictimStreak, int iDeathNoticeMsg );
|
||
|
|
||
|
CHudTexture* GetMannPowerIcon( RuneTypes_t tRuneType, bool bRedTeam );
|
||
|
|
||
|
CHudTexture *m_iconDomination;
|
||
|
CHudTexture *m_iconKillStreak;
|
||
|
CHudTexture *m_iconDuckStreak;
|
||
|
CHudTexture *m_iconDuckStreakDNeg;
|
||
|
CHudTexture *m_iconKillStreakDNeg;
|
||
|
|
||
|
CPanelAnimationVar( Color, m_clrBlueText, "TeamBlue", "153 204 255 255" );
|
||
|
CPanelAnimationVar( Color, m_clrRedText, "TeamRed", "255 64 64 255" );
|
||
|
CPanelAnimationVar( Color, m_clrPurpleText, "PurpleText", "134 80 172 255" );
|
||
|
CPanelAnimationVar( Color, m_clrGreenText, "GreenText", "112 176 74 255" );
|
||
|
CPanelAnimationVar( Color, m_clrLocalPlayerText, "LocalPlayerColor", "65 65 65 255" );
|
||
|
|
||
|
CTFStreakNotice *m_pStreakNotice;
|
||
|
|
||
|
bool m_bShowItemOnKill;
|
||
|
};
|
||
|
|
||
|
DECLARE_HUDELEMENT( CTFHudDeathNotice );
|
||
|
|
||
|
void CTFHudDeathNotice::Init()
|
||
|
{
|
||
|
BaseClass::Init();
|
||
|
|
||
|
ListenForGameEvent( "fish_notice" );
|
||
|
ListenForGameEvent( "fish_notice__arm" );
|
||
|
ListenForGameEvent( "duck_xp_level_up" );
|
||
|
//ListenForGameEvent( "throwable_hit" );
|
||
|
|
||
|
m_bShowItemOnKill = true;
|
||
|
|
||
|
// PASSTIME if this is called at level load or something we should check mode before this block
|
||
|
ListenForGameEvent( PasstimeGameEvents::BallGet::s_eventName );
|
||
|
ListenForGameEvent( PasstimeGameEvents::BallStolen::s_eventName );
|
||
|
ListenForGameEvent( PasstimeGameEvents::Score::s_eventName );
|
||
|
ListenForGameEvent( PasstimeGameEvents::PassCaught::s_eventName );
|
||
|
ListenForGameEvent( PasstimeGameEvents::BallBlocked::s_eventName );
|
||
|
}
|
||
|
|
||
|
void CTFHudDeathNotice::ApplySchemeSettings( vgui::IScheme *scheme )
|
||
|
{
|
||
|
BaseClass::ApplySchemeSettings( scheme );
|
||
|
|
||
|
m_iconDomination = gHUD.GetIcon( "leaderboard_dominated" );
|
||
|
|
||
|
m_iconKillStreak = gHUD.GetIcon( "leaderboard_streak" );
|
||
|
m_iconKillStreakDNeg = gHUD.GetIcon( "leaderboard_streak_dneg" );
|
||
|
m_iconDuckStreak = gHUD.GetIcon( "eotl_duck" );
|
||
|
m_iconDuckStreakDNeg = gHUD.GetIcon( "eotl_duck_dneg" );
|
||
|
m_pStreakNotice = new CTFStreakNotice( "KillStreakNotice" );
|
||
|
}
|
||
|
|
||
|
bool CTFHudDeathNotice::IsVisible( void )
|
||
|
{
|
||
|
if ( IsTakingAFreezecamScreenshot() )
|
||
|
return false;
|
||
|
|
||
|
return BaseClass::IsVisible();
|
||
|
}
|
||
|
|
||
|
bool CTFHudDeathNotice::ShouldDraw( void )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CTFHudDeathNotice::ShouldShowDeathNotice( IGameEvent *event )
|
||
|
{
|
||
|
if ( event->GetBool( "silent_kill" ) )
|
||
|
{
|
||
|
// Don't show a kill event for the team of the silent kill victim.
|
||
|
int iVictimID = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
|
||
|
C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictimID ) );
|
||
|
if ( pVictim && pVictim->GetTeamNumber() == GetLocalPlayerTeam() && iVictimID != GetLocalPlayerIndex() )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && ( event->GetInt( "death_flags" ) & TF_DEATH_MINIBOSS ) == 0 )
|
||
|
{
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
|
||
|
if ( iLocalPlayerIndex != engine->GetPlayerForUserID( event->GetInt( "attacker" ) ) &&
|
||
|
iLocalPlayerIndex != engine->GetPlayerForUserID( event->GetInt( "assister" ) ) )
|
||
|
{
|
||
|
C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( engine->GetPlayerForUserID( event->GetInt( "userid" ) ) ) );
|
||
|
if ( pVictim && pVictim->GetTeamNumber() == TF_TEAM_PVE_INVADERS )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CTFHudDeathNotice::PlayRivalrySounds( int iKillerIndex, int iVictimIndex, int iType )
|
||
|
{
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
|
||
|
//We're not involved in this kill
|
||
|
if ( iKillerIndex != iLocalPlayerIndex && iVictimIndex != iLocalPlayerIndex )
|
||
|
return;
|
||
|
|
||
|
const char *pszSoundName = NULL;
|
||
|
|
||
|
if ( iType == TF_DEATH_DOMINATION )
|
||
|
{
|
||
|
if ( iKillerIndex == iLocalPlayerIndex )
|
||
|
{
|
||
|
pszSoundName = "Game.Domination";
|
||
|
}
|
||
|
else if ( iVictimIndex == iLocalPlayerIndex )
|
||
|
{
|
||
|
pszSoundName = "Game.Nemesis";
|
||
|
}
|
||
|
}
|
||
|
else if ( iType == TF_DEATH_REVENGE )
|
||
|
{
|
||
|
pszSoundName = "Game.Revenge";
|
||
|
}
|
||
|
|
||
|
CLocalPlayerFilter filter;
|
||
|
C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, pszSoundName );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Server's told us that someone's died
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudDeathNotice::FireGameEvent( IGameEvent *event )
|
||
|
{
|
||
|
const char * pszEventName = event->GetName();
|
||
|
if ( FStrEq( "duck_xp_level_up", pszEventName ) )
|
||
|
{
|
||
|
int level = event->GetInt( "level" );
|
||
|
AddStreakMsg( CTFPlayerShared::kTFStreak_Duck_levelup, GetLocalPlayerIndex(), level, 1, -1, 0 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BaseClass::FireGameEvent( event );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFHudDeathNotice::EventIsPlayerDeath( const char* eventName )
|
||
|
{
|
||
|
return FStrEq( eventName, "fish_notice" )
|
||
|
|| FStrEq( eventName, "fish_notice__arm" )
|
||
|
//|| FStrEq( eventName, "throwable_hit" )
|
||
|
|| BaseClass::EventIsPlayerDeath( eventName );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called when a game event happens and a death notice is about to be
|
||
|
// displayed. This method can examine the event and death notice and
|
||
|
// make game-specific tweaks to it before it is displayed
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg )
|
||
|
{
|
||
|
const bool bIsSillyPyroVision = IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO );
|
||
|
|
||
|
const char *pszEventName = event->GetName();
|
||
|
|
||
|
if ( FStrEq( pszEventName, "player_death" ) || FStrEq( pszEventName, "object_destroyed" ) )
|
||
|
{
|
||
|
bool bIsObjectDestroyed = FStrEq( pszEventName, "object_destroyed" );
|
||
|
int iCustomDamage = event->GetInt( "customkill" );
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
|
||
|
const int iKillerID = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
|
||
|
const int iVictimID = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
|
||
|
// if there was an assister, put both the killer's and assister's names in the death message
|
||
|
int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
|
||
|
|
||
|
EHorriblePyroVisionHack ePyroVisionHack = kHorriblePyroVisionHack_KillAssisterType_Default;
|
||
|
CUtlConstString sAssisterNameScratch;
|
||
|
const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
|
||
|
|
||
|
// If we don't have a real assister (would have been passed in to us as a player index) and
|
||
|
// we're in crazy pyrovision mode and we got a dummy assister, than fall back and display
|
||
|
// that just for giggles. We use this so the Balloonicorn and friends can get the assist
|
||
|
// credit they so rightly deserve.
|
||
|
if ( !assister_name && bIsSillyPyroVision )
|
||
|
{
|
||
|
// Ignore this for self-kills.
|
||
|
if ( bIsObjectDestroyed || (iKillerID != iVictimID) )
|
||
|
{
|
||
|
const char *pszMaybeFallbackAssisterName = event->GetString( "assister_fallback" );
|
||
|
if ( pszMaybeFallbackAssisterName && pszMaybeFallbackAssisterName[0] )
|
||
|
{
|
||
|
// We store the type of silly assist in the first byte of the string because we
|
||
|
// are terrible people.
|
||
|
ePyroVisionHack = (EHorriblePyroVisionHack)pszMaybeFallbackAssisterName[0];
|
||
|
Assert( ePyroVisionHack != kHorriblePyroVisionHack_KillAssisterType_Default );
|
||
|
pszMaybeFallbackAssisterName = &pszMaybeFallbackAssisterName[1];
|
||
|
|
||
|
// If we pass in a localization string, we need to convert it back to ANSI temporarily.
|
||
|
// This won't localize "The" Balloonicorn because we don't have a real item with a real
|
||
|
// quality, etc., just a single localization token.
|
||
|
switch ( ePyroVisionHack )
|
||
|
{
|
||
|
case kHorriblePyroVisionHack_KillAssisterType_LocalizationString:
|
||
|
case kHorriblePyroVisionHack_KillAssisterType_LocalizationString_First:
|
||
|
{
|
||
|
wchar_t *wszLocalizedItemName = GLocalizationProvider()->Find( pszMaybeFallbackAssisterName );
|
||
|
char szANSIConvertedItemName[ MAX_PLAYER_NAME_LENGTH ];
|
||
|
g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedItemName, szANSIConvertedItemName, MAX_PLAYER_NAME_LENGTH );
|
||
|
sAssisterNameScratch = szANSIConvertedItemName;
|
||
|
assister_name = sAssisterNameScratch.Get();
|
||
|
break;
|
||
|
}
|
||
|
case kHorriblePyroVisionHack_KillAssisterType_CustomName:
|
||
|
case kHorriblePyroVisionHack_KillAssisterType_CustomName_First:
|
||
|
{
|
||
|
sAssisterNameScratch = pszMaybeFallbackAssisterName;
|
||
|
assister_name = sAssisterNameScratch.Get();
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
assert( !"Unknown pyro item hack type! Something has gone horribly, horribly worse." );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool bMultipleKillers = false;
|
||
|
|
||
|
if ( assister_name )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
const char *pszKillerName = msg.Killer.szName;
|
||
|
const char *pszAssisterName = assister_name;
|
||
|
|
||
|
// Check to see if we're swapping the killer and the assister. We use this so the brain slug can get the kill
|
||
|
// credit for the HUD death notices, with the player being the assister.
|
||
|
if ( pszAssisterName && (ePyroVisionHack == kHorriblePyroVisionHack_KillAssisterType_CustomName_First ||
|
||
|
ePyroVisionHack == kHorriblePyroVisionHack_KillAssisterType_LocalizationString_First) )
|
||
|
{
|
||
|
std::swap( pszKillerName, pszAssisterName );
|
||
|
}
|
||
|
|
||
|
char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
|
||
|
Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", pszKillerName, pszAssisterName );
|
||
|
Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
if ( iLocalPlayerIndex == iAssisterID )
|
||
|
{
|
||
|
msg.bLocalPlayerInvolved = true;
|
||
|
}
|
||
|
|
||
|
bMultipleKillers = true;
|
||
|
}
|
||
|
|
||
|
// play an exciting sound if a sniper pulls off any sort of penetration kill
|
||
|
const int iPlayerPenetrationCount = !event->IsEmpty( "playerpenetratecount" ) ? event->GetInt( "playerpenetratecount" ) : 0;
|
||
|
|
||
|
bool bPenetrateSound = iPlayerPenetrationCount > 0;
|
||
|
|
||
|
// This happens too frequently in Coop/TD
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
bPenetrateSound = false;
|
||
|
}
|
||
|
|
||
|
if ( bPenetrateSound )
|
||
|
{
|
||
|
CLocalPlayerFilter filter;
|
||
|
C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "Game.PenetrationKill" );
|
||
|
}
|
||
|
|
||
|
|
||
|
int deathFlags = event->GetInt( "death_flags" );
|
||
|
|
||
|
if ( !bIsObjectDestroyed )
|
||
|
{
|
||
|
// if this death involved a player dominating another player or getting revenge on another player, add an additional message
|
||
|
// mentioning that
|
||
|
|
||
|
// WARNING: AddAdditionalMsg will grow and potentially realloc the m_DeathNotices array. So be careful
|
||
|
// using pointers to m_DeathNotices elements...
|
||
|
|
||
|
if ( deathFlags & TF_DEATH_DOMINATION )
|
||
|
{
|
||
|
AddAdditionalMsg( iKillerID, iVictimID, bIsSillyPyroVision ? "#Msg_Dominating_What" : "#Msg_Dominating" );
|
||
|
PlayRivalrySounds( iKillerID, iVictimID, TF_DEATH_DOMINATION );
|
||
|
}
|
||
|
if ( deathFlags & TF_DEATH_ASSISTER_DOMINATION && ( iAssisterID > 0 ) )
|
||
|
{
|
||
|
AddAdditionalMsg( iAssisterID, iVictimID, bIsSillyPyroVision ? "#Msg_Dominating_What" : "#Msg_Dominating" );
|
||
|
PlayRivalrySounds( iAssisterID, iVictimID, TF_DEATH_DOMINATION );
|
||
|
}
|
||
|
if ( deathFlags & TF_DEATH_REVENGE )
|
||
|
{
|
||
|
AddAdditionalMsg( iKillerID, iVictimID, bIsSillyPyroVision ? "#Msg_Revenge_What" : "#Msg_Revenge" );
|
||
|
PlayRivalrySounds( iKillerID, iVictimID, TF_DEATH_REVENGE );
|
||
|
}
|
||
|
if ( deathFlags & TF_DEATH_ASSISTER_REVENGE && ( iAssisterID > 0 ) )
|
||
|
{
|
||
|
AddAdditionalMsg( iAssisterID, iVictimID, bIsSillyPyroVision ? "#Msg_Revenge_What" : "#Msg_Revenge" );
|
||
|
PlayRivalrySounds( iAssisterID, iVictimID, TF_DEATH_REVENGE );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// if this is an object destroyed message, set the victim name to "<object type> (<owner>)"
|
||
|
int iObjectType = event->GetInt( "objecttype" );
|
||
|
if ( iObjectType >= 0 && iObjectType < OBJ_LAST )
|
||
|
{
|
||
|
// get the localized name for the object
|
||
|
char szLocalizedObjectName[MAX_PLAYER_NAME_LENGTH];
|
||
|
szLocalizedObjectName[ 0 ] = 0;
|
||
|
const wchar_t *wszLocalizedObjectName = g_pVGuiLocalize->Find( szLocalizedObjectNames[iObjectType] );
|
||
|
if ( wszLocalizedObjectName )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedObjectName, szLocalizedObjectName, ARRAYSIZE( szLocalizedObjectName ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Warning( "Couldn't find localized object name for '%s'\n", szLocalizedObjectNames[iObjectType] );
|
||
|
Q_strncpy( szLocalizedObjectName, szLocalizedObjectNames[iObjectType], sizeof( szLocalizedObjectName ) );
|
||
|
}
|
||
|
|
||
|
// compose the string
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
if ( msg.Victim.szName[0] )
|
||
|
{
|
||
|
char szVictimBuf[MAX_PLAYER_NAME_LENGTH*2];
|
||
|
Q_snprintf( szVictimBuf, ARRAYSIZE(szVictimBuf), "%s (%s)", szLocalizedObjectName, msg.Victim.szName );
|
||
|
Q_strncpy( msg.Victim.szName, szVictimBuf, ARRAYSIZE( msg.Victim.szName ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_strncpy( msg.Victim.szName, szLocalizedObjectName, ARRAYSIZE( msg.Victim.szName ) );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert( false ); // invalid object type
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const wchar_t *pMsg = NULL;
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
switch ( iCustomDamage )
|
||
|
{
|
||
|
case TF_DMG_CUSTOM_BACKSTAB:
|
||
|
if ( FStrEq( msg.szIcon, "d_sharp_dresser" ) )
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_sharp_dresser_backstab", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_backstab", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
break;
|
||
|
case TF_DMG_CUSTOM_HEADSHOT_DECAPITATION:
|
||
|
case TF_DMG_CUSTOM_HEADSHOT:
|
||
|
{
|
||
|
if ( FStrEq( event->GetString( "weapon" ), "ambassador" ) )
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_ambassador_headshot", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else if ( FStrEq( event->GetString( "weapon" ), "huntsman" ) )
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_huntsman_headshot", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Did this headshot penetrate something before the kill? If so, show a fancy icon
|
||
|
// so the player feels proud.
|
||
|
if ( iPlayerPenetrationCount > 0 )
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_headshot_player_penetration", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_strncpy( msg.szIcon, "d_headshot", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case TF_DMG_CUSTOM_BURNING:
|
||
|
if ( event->GetInt( "attacker" ) == event->GetInt( "userid" ) )
|
||
|
{
|
||
|
// suicide by fire
|
||
|
Q_strncpy( msg.szIcon, "d_firedeath", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TF_DMG_CUSTOM_BURNING_ARROW:
|
||
|
// special-case if the player is killed from a burning arrow after it has already landed
|
||
|
Q_strncpy( msg.szIcon, "d_huntsman_burning", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
break;
|
||
|
|
||
|
case TF_DMG_CUSTOM_FLYINGBURN:
|
||
|
// special-case if the player is killed from a burning arrow as the killing blow
|
||
|
Q_strncpy( msg.szIcon, "d_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
break;
|
||
|
|
||
|
case TF_DMG_CUSTOM_PUMPKIN_BOMB:
|
||
|
// special-case if the player is killed by a pumpkin bomb
|
||
|
Q_strncpy( msg.szIcon, "d_pumpkindeath", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
break;
|
||
|
|
||
|
case TF_DMG_CUSTOM_SUICIDE:
|
||
|
{
|
||
|
// display a different message if this was suicide, or assisted suicide (suicide w/recent damage, kill awarded to damager)
|
||
|
bool bAssistedSuicide = event->GetInt( "userid" ) != event->GetInt( "attacker" );
|
||
|
pMsg = g_pVGuiLocalize->Find( ( bAssistedSuicide ) ? ( bMultipleKillers ? "#DeathMsg_AssistedSuicide_Multiple" : "#DeathMsg_AssistedSuicide" ) : ( "#DeathMsg_Suicide" ) );
|
||
|
if ( pMsg )
|
||
|
{
|
||
|
V_wcsncpy( msg.wzInfoText, pMsg, sizeof( msg.wzInfoText ) );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TF_DMG_CUSTOM_EYEBALL_ROCKET:
|
||
|
{
|
||
|
if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
|
||
|
{
|
||
|
char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
|
||
|
szLocalizedName[ 0 ] = 0;
|
||
|
const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_EYEBALL_BOSS_DEATHCAM_NAME" );
|
||
|
if ( wszLocalizedName )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
|
||
|
Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to purple for MONOCULUS!
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TF_DMG_CUSTOM_MERASMUS_ZAP:
|
||
|
case TF_DMG_CUSTOM_MERASMUS_GRENADE:
|
||
|
case TF_DMG_CUSTOM_MERASMUS_DECAPITATION:
|
||
|
{
|
||
|
if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
|
||
|
{
|
||
|
char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
|
||
|
szLocalizedName[ 0 ] = 0;
|
||
|
const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_MERASMUS_DEATHCAM_NAME" );
|
||
|
if ( wszLocalizedName )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
|
||
|
Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to green for MERASMUS!
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case TF_DMG_CUSTOM_SPELL_SKELETON:
|
||
|
{
|
||
|
if ( msg.Killer.iTeam == TEAM_UNASSIGNED )
|
||
|
{
|
||
|
char szLocalizedName[MAX_PLAYER_NAME_LENGTH];
|
||
|
szLocalizedName[ 0 ] = 0;
|
||
|
const wchar_t *wszLocalizedName = g_pVGuiLocalize->Find( "#TF_HALLOWEEN_SKELETON_DEATHCAM_NAME" );
|
||
|
if ( wszLocalizedName )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalizedName, szLocalizedName, ARRAYSIZE( szLocalizedName ) );
|
||
|
Q_strncpy( msg.Killer.szName, szLocalizedName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = TF_TEAM_HALLOWEEN; // This will set the name to green for THE UNDEAD!
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case TF_DMG_CUSTOM_KART:
|
||
|
// special-case if the player is pushed by kart
|
||
|
Q_strncpy( msg.szIcon, "d_bumper_kart", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
break;
|
||
|
case TF_DMG_CUSTOM_GIANT_HAMMER:
|
||
|
// special-case Giant hammer
|
||
|
Q_strncpy( msg.szIcon, "d_necro_smasher", ARRAYSIZE( msg.szIcon ) );
|
||
|
msg.wzInfoText[0] = 0;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( ( event->GetInt( "damagebits" ) & DMG_NERVEGAS ) )
|
||
|
{
|
||
|
// special case icon for hit-by-vehicle death
|
||
|
Q_strncpy( msg.szIcon, "d_saw_kill", ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
|
||
|
int iKillStreakTotal = event->GetInt( "kill_streak_total" );
|
||
|
int iKillStreakWep = event->GetInt( "kill_streak_wep" );
|
||
|
int iDuckStreakTotal = event->GetInt( "duck_streak_total" );
|
||
|
int iDucksThisKill = event->GetInt( "ducks_streaked" );
|
||
|
|
||
|
// if the active weapon is kill streak
|
||
|
C_TFPlayer* pKiller = ToTFPlayer( UTIL_PlayerByIndex( iKillerID ) );
|
||
|
C_TFPlayer* pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictimID ) );
|
||
|
C_TFPlayer* pAssister = ToTFPlayer( UTIL_PlayerByIndex( iAssisterID ) );
|
||
|
|
||
|
// Mannpower runes
|
||
|
if ( pKiller && pKiller->m_Shared.IsCarryingRune() )
|
||
|
{
|
||
|
msg.iconPreKillerName = GetMannPowerIcon( pKiller->m_Shared.GetCarryingRuneType(), pKiller->GetTeamNumber() == TF_TEAM_RED );
|
||
|
}
|
||
|
|
||
|
if ( pVictim && pVictim->m_Shared.IsCarryingRune() )
|
||
|
{
|
||
|
msg.iconPostVictimName = GetMannPowerIcon( pVictim->m_Shared.GetCarryingRuneType(), pVictim->GetTeamNumber() == TF_TEAM_RED );
|
||
|
}
|
||
|
|
||
|
if ( iKillStreakWep > 0 )
|
||
|
{
|
||
|
// append kill streak count to this notification
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iKillStreakWep );
|
||
|
g_pVGuiLocalize->ConstructString_safe( msg.wzPreKillerText, g_pVGuiLocalize->Find("#Kill_Streak"), 1, wzCount );
|
||
|
if ( msg.bLocalPlayerInvolved )
|
||
|
{
|
||
|
msg.iconPostKillerName = m_iconKillStreakDNeg;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
msg.iconPostKillerName = m_iconKillStreak;
|
||
|
}
|
||
|
}
|
||
|
else if ( iDuckStreakTotal > 0 && iDucksThisKill )
|
||
|
{
|
||
|
// Duckstreak icon (always lower priority)
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iDuckStreakTotal );
|
||
|
g_pVGuiLocalize->ConstructString_safe( msg.wzPreKillerText, g_pVGuiLocalize->Find("#Duck_Streak"), 1, wzCount );
|
||
|
msg.iconPostKillerName = msg.bLocalPlayerInvolved ? m_iconDuckStreakDNeg : m_iconDuckStreak;
|
||
|
}
|
||
|
|
||
|
// Check to see if we want a extra notification
|
||
|
// Attempt to display these in order of descending priority
|
||
|
|
||
|
// Check Assister for Additional Messages
|
||
|
int iKillStreakAssist = event->GetInt( "kill_streak_assist" );
|
||
|
int iKillStreakVictim = event->GetInt( "kill_streak_victim" );
|
||
|
|
||
|
// Kills
|
||
|
AddStreakMsg( CTFPlayerShared::kTFStreak_Kills, iKillerID, iKillStreakTotal, 1, iVictimID, iDeathNoticeMsg );
|
||
|
if ( pAssister && iKillStreakAssist > 1 )
|
||
|
{
|
||
|
AddStreakMsg( CTFPlayerShared::kTFStreak_Kills, iAssisterID, iKillStreakAssist, 1, iVictimID, iDeathNoticeMsg );
|
||
|
}
|
||
|
|
||
|
if ( pVictim && iKillStreakVictim > 2 )
|
||
|
{
|
||
|
AddStreakEndedMsg( CTFPlayerShared::kTFStreak_Kills, iKillerID, iVictimID, iKillStreakVictim, iDeathNoticeMsg );
|
||
|
}
|
||
|
|
||
|
// Ducks
|
||
|
int iDuckStreakAssist = event->GetInt( "duck_streak_assist" );
|
||
|
int iDuckStreakVictim = event->GetInt( "duck_streak_victim" );
|
||
|
int iDuckStreakIncrement = event->GetInt( "ducks_streaked" );
|
||
|
|
||
|
AddStreakMsg( CTFPlayerShared::kTFStreak_Ducks, iKillerID, iDuckStreakTotal, iDuckStreakIncrement, iVictimID, iDeathNoticeMsg );
|
||
|
if ( pAssister && iDuckStreakAssist > 0 && iDucksThisKill )
|
||
|
{
|
||
|
AddStreakMsg( CTFPlayerShared::kTFStreak_Ducks, iAssisterID, iDuckStreakAssist, iDuckStreakIncrement, iVictimID, iDeathNoticeMsg );
|
||
|
}
|
||
|
|
||
|
if ( pVictim && iDuckStreakVictim > 2 )
|
||
|
{
|
||
|
AddStreakEndedMsg( CTFPlayerShared::kTFStreak_Ducks, iKillerID, iVictimID, iDuckStreakVictim, iDeathNoticeMsg );
|
||
|
}
|
||
|
|
||
|
// STAGING ONLY test
|
||
|
// If Local Player killed someone and they have an item waiting, let them know
|
||
|
#ifdef STAGING_ONLY
|
||
|
//if ( iLocalPlayerIndex == iKillerID && m_bShowItemOnKill )
|
||
|
//{
|
||
|
// if ( CEconNotification_HasNewItemsOnKill::HasUnacknowledgedItems() )
|
||
|
// {
|
||
|
// CEconNotification_HasNewItemsOnKill *pNotification = new CEconNotification_HasNewItemsOnKill( iVictimID );
|
||
|
// NotificationQueue_Add( pNotification );
|
||
|
// m_bShowItemOnKill = false;
|
||
|
// }
|
||
|
//}
|
||
|
//if ( iLocalPlayerIndex == iVictimID )
|
||
|
//{
|
||
|
// m_bShowItemOnKill = true;
|
||
|
//}
|
||
|
#endif
|
||
|
}
|
||
|
else if ( FStrEq( "teamplay_point_captured", pszEventName ) ||
|
||
|
FStrEq( "teamplay_capture_blocked", pszEventName ) ||
|
||
|
FStrEq( "teamplay_flag_event", pszEventName ) )
|
||
|
{
|
||
|
bool bDefense = ( FStrEq( "teamplay_capture_blocked", pszEventName ) || ( FStrEq( "teamplay_flag_event", pszEventName ) &&
|
||
|
TF_FLAGEVENT_DEFEND == event->GetInt( "eventtype" ) ) );
|
||
|
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
const char *szCaptureIcons[] = { "d_redcapture", "d_bluecapture" };
|
||
|
const char *szDefenseIcons[] = { "d_reddefend", "d_bluedefend" };
|
||
|
|
||
|
int iTeam = msg.Killer.iTeam;
|
||
|
Assert( iTeam >= FIRST_GAME_TEAM );
|
||
|
Assert( iTeam < FIRST_GAME_TEAM + TF_TEAM_COUNT );
|
||
|
if ( iTeam < FIRST_GAME_TEAM || iTeam >= FIRST_GAME_TEAM + TF_TEAM_COUNT )
|
||
|
return;
|
||
|
|
||
|
int iIndex = msg.Killer.iTeam - FIRST_GAME_TEAM;
|
||
|
Assert( iIndex < ARRAYSIZE( szCaptureIcons ) );
|
||
|
|
||
|
Q_strncpy( msg.szIcon, bDefense ? szDefenseIcons[iIndex] : szCaptureIcons[iIndex], ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else if ( FStrEq( "fish_notice", pszEventName ) || FStrEq( "fish_notice__arm", pszEventName ) )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
int deathFlags = event->GetInt( "death_flags" );
|
||
|
int iCustomDamage = event->GetInt( "customkill" );
|
||
|
|
||
|
if ( ( iCustomDamage == TF_DMG_CUSTOM_FISH_KILL ) || ( deathFlags & TF_DEATH_FEIGN_DEATH ) )
|
||
|
{
|
||
|
g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, FStrEq( "fish_notice", pszEventName ) ? g_pVGuiLocalize->Find("#Humiliation_Kill") : g_pVGuiLocalize->Find("#Humiliation_Kill_Arm"), 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", ++msg.iCount );
|
||
|
g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Humiliation_Count"), 1, wzCount );
|
||
|
}
|
||
|
|
||
|
// if there was an assister, put both the killer's and assister's names in the death message
|
||
|
int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
|
||
|
const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
|
||
|
if ( assister_name )
|
||
|
{
|
||
|
char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
|
||
|
Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", msg.Killer.szName, assister_name );
|
||
|
Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
}
|
||
|
}
|
||
|
//else if ( FStrEq( "throwable_hit", pszEventName ) )
|
||
|
//{
|
||
|
// DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
// int deathFlags = event->GetInt( "death_flags" );
|
||
|
// int iCustomDamage = event->GetInt( "customkill" );
|
||
|
|
||
|
// // Make sure the icon is up to date
|
||
|
// m_DeathNotices[iDeathNoticeMsg].iconDeath = GetIcon( m_DeathNotices[ iDeathNoticeMsg ].szIcon, m_DeathNotices[iDeathNoticeMsg].bLocalPlayerInvolved ? kDeathNoticeIcon_Inverted : kDeathNoticeIcon_Standard );
|
||
|
|
||
|
// if ( ( iCustomDamage == TF_DMG_CUSTOM_THROWABLE_KILL ) || ( deathFlags & TF_DEATH_FEIGN_DEATH ) )
|
||
|
// {
|
||
|
// g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Throwable_Kill"), 0 );
|
||
|
// }
|
||
|
// else
|
||
|
// {
|
||
|
// wchar_t wzCount[10];
|
||
|
// _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", event->GetInt( "totalhits" ) );
|
||
|
// g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Humiliation_Count"), 1, wzCount );
|
||
|
// }
|
||
|
|
||
|
// // if there was an assister, put both the killer's and assister's names in the death message
|
||
|
// int iAssisterID = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
|
||
|
// const char *assister_name = ( iAssisterID > 0 ? g_PR->GetPlayerName( iAssisterID ) : NULL );
|
||
|
// if ( assister_name )
|
||
|
// {
|
||
|
// char szKillerBuf[MAX_PLAYER_NAME_LENGTH*2];
|
||
|
// Q_snprintf( szKillerBuf, ARRAYSIZE(szKillerBuf), "%s + %s", msg.Killer.szName, assister_name );
|
||
|
// Q_strncpy( msg.Killer.szName, szKillerBuf, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
// }
|
||
|
//}
|
||
|
else if ( FStrEq( "rd_robot_killed", pszEventName ) )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
int killer = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
|
||
|
const char *killedwith = event->GetString( "weapon" );
|
||
|
|
||
|
msg.Killer.iTeam = g_PR->GetTeam( killer );
|
||
|
Q_strncpy( msg.Killer.szName, g_PR->GetPlayerName( killer ), ARRAYSIZE( msg.Killer.szName ) );
|
||
|
|
||
|
Q_strncpy( msg.Victim.szName, g_PR->GetTeam( killer ) == TF_TEAM_RED ? "BLUE ROBOT" : "RED ROBOT", ARRAYSIZE( msg.Victim.szName ) );
|
||
|
msg.Victim.iTeam = g_PR->GetTeam( killer ) == TF_TEAM_RED ? TF_TEAM_BLUE : TF_TEAM_RED;
|
||
|
|
||
|
Q_snprintf( msg.szIcon, sizeof(msg.szIcon), "d_%s", killedwith );
|
||
|
}
|
||
|
else if ( FStrEq( PasstimeGameEvents::BallGet::s_eventName, pszEventName ) ) // passtime ball get
|
||
|
{
|
||
|
PasstimeGameEvents::BallGet ev( event );
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
// info
|
||
|
V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeBallGet"), sizeof( msg.wzInfoText ) );
|
||
|
|
||
|
// killer
|
||
|
const char *szPlayerName = g_PR->GetPlayerName( ev.ownerIndex);
|
||
|
Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = g_PR->GetTeam( ev.ownerIndex );
|
||
|
|
||
|
// flags
|
||
|
if ( GetLocalPlayerIndex() == ev.ownerIndex )
|
||
|
msg.bLocalPlayerInvolved = true;
|
||
|
|
||
|
// icon
|
||
|
const char *const icon = "d_passtime_pass";
|
||
|
Q_strncpy( msg.szIcon, icon, ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else if ( FStrEq( PasstimeGameEvents::BallStolen::s_eventName, pszEventName ) ) // passtime ball stolen
|
||
|
{
|
||
|
PasstimeGameEvents::BallStolen ev( event );
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
int attackerTeam = g_PR->GetTeam( ev.attackerIndex );
|
||
|
int victimTeam = g_PR->GetTeam( ev.victimIndex );
|
||
|
|
||
|
// attacker
|
||
|
const char *szPlayerName = g_PR->GetPlayerName( ev.attackerIndex );
|
||
|
Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = attackerTeam;
|
||
|
|
||
|
// victim
|
||
|
szPlayerName = g_PR->GetPlayerName( ev.victimIndex );
|
||
|
Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
|
||
|
msg.Victim.iTeam = victimTeam;
|
||
|
|
||
|
V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeSteal"), sizeof( msg.wzInfoText ) );
|
||
|
|
||
|
// flags
|
||
|
int localPlayerIndex = GetLocalPlayerIndex();
|
||
|
msg.bLocalPlayerInvolved = (localPlayerIndex == ev.attackerIndex)
|
||
|
|| (localPlayerIndex == ev.victimIndex);
|
||
|
|
||
|
// icon
|
||
|
Q_strncpy( msg.szIcon, "d_passtime_steal", ARRAYSIZE(msg.szIcon) );
|
||
|
}
|
||
|
else if ( FStrEq( PasstimeGameEvents::Score::s_eventName, pszEventName ) ) // passtime score
|
||
|
{
|
||
|
PasstimeGameEvents::Score ev( event );
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
// info
|
||
|
if ( ev.numPoints > 1 )
|
||
|
{
|
||
|
wchar_t wzCount[10];
|
||
|
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", ev.numPoints );
|
||
|
g_pVGuiLocalize->ConstructString_safe( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeScoreCount"), 1, wzCount );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeScore"), sizeof( msg.wzInfoText ) );
|
||
|
}
|
||
|
|
||
|
// killer
|
||
|
const char *szPlayerName = g_PR->GetPlayerName( ev.scorerIndex );
|
||
|
Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = g_PR->GetTeam( ev.scorerIndex );
|
||
|
|
||
|
// flags
|
||
|
if ( GetLocalPlayerIndex() == ev.scorerIndex )
|
||
|
msg.bLocalPlayerInvolved = true;
|
||
|
|
||
|
// icon
|
||
|
const char *const icon = (msg.Killer.iTeam == TF_TEAM_RED)
|
||
|
? "d_passtime_score_red"
|
||
|
: "d_passtime_score_blue";
|
||
|
Q_strncpy( msg.szIcon, icon, ARRAYSIZE( msg.szIcon ) );
|
||
|
}
|
||
|
else if ( FStrEq( PasstimeGameEvents::PassCaught::s_eventName, pszEventName ) ) // passtime pass
|
||
|
{
|
||
|
PasstimeGameEvents::PassCaught ev( event );
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
int passerTeam = g_PR->GetTeam( ev.passerIndex );
|
||
|
int catcherTeam = g_PR->GetTeam( ev.catcherIndex );
|
||
|
|
||
|
//
|
||
|
// Pass or interception?
|
||
|
//
|
||
|
int killerIndex, victimIndex, killerTeam, victimTeam;
|
||
|
const char *pszDesc;
|
||
|
if ( passerTeam == catcherTeam )
|
||
|
{
|
||
|
// pass
|
||
|
killerIndex = ev.passerIndex;
|
||
|
killerTeam = passerTeam;
|
||
|
victimIndex = ev.catcherIndex;
|
||
|
victimTeam = catcherTeam;
|
||
|
pszDesc = "#Msg_PasstimePassComplete";
|
||
|
Q_strncpy( msg.szIcon, "d_passtime_pass", ARRAYSIZE(msg.szIcon) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// interception
|
||
|
victimIndex = ev.passerIndex;
|
||
|
victimTeam = passerTeam;
|
||
|
killerIndex = ev.catcherIndex;
|
||
|
killerTeam = catcherTeam;
|
||
|
pszDesc = "#Msg_PasstimeInterception";
|
||
|
Q_strncpy( msg.szIcon, "d_passtime_intercept", ARRAYSIZE(msg.szIcon) );
|
||
|
}
|
||
|
|
||
|
// killer
|
||
|
const char *szPlayerName = g_PR->GetPlayerName( killerIndex );
|
||
|
Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = killerTeam;
|
||
|
|
||
|
// victim
|
||
|
szPlayerName = g_PR->GetPlayerName( victimIndex );
|
||
|
Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
|
||
|
msg.Victim.iTeam = victimTeam;
|
||
|
|
||
|
V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find( pszDesc ), sizeof( msg.wzInfoText ) );
|
||
|
|
||
|
// flags
|
||
|
int localPlayerIndex = GetLocalPlayerIndex();
|
||
|
msg.bLocalPlayerInvolved = (localPlayerIndex == ev.catcherIndex)
|
||
|
|| (localPlayerIndex == ev.passerIndex);
|
||
|
}
|
||
|
else if ( FStrEq( PasstimeGameEvents::BallBlocked::s_eventName, pszEventName ) ) // passtime ball stolen
|
||
|
{
|
||
|
PasstimeGameEvents::BallBlocked ev( event );
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
// blocker
|
||
|
const char *szPlayerName = g_PR->GetPlayerName( ev.blockerIndex );
|
||
|
Q_strncpy( msg.Killer.szName, szPlayerName, ARRAYSIZE( msg.Killer.szName ) );
|
||
|
msg.Killer.iTeam = g_PR->GetTeam( ev.blockerIndex );
|
||
|
|
||
|
// owner
|
||
|
szPlayerName = g_PR->GetPlayerName( ev.ownerIndex );
|
||
|
Q_strncpy( msg.Victim.szName, szPlayerName, ARRAYSIZE( msg.Victim.szName ) );
|
||
|
msg.Victim.iTeam = g_PR->GetTeam( ev.ownerIndex );
|
||
|
|
||
|
V_wcsncpy( msg.wzInfoText, g_pVGuiLocalize->Find("#Msg_PasstimeBlock"), sizeof( msg.wzInfoText ) );
|
||
|
|
||
|
// flags
|
||
|
int localPlayerIndex = GetLocalPlayerIndex();
|
||
|
msg.bLocalPlayerInvolved = (localPlayerIndex == ev.blockerIndex)
|
||
|
|| (localPlayerIndex == ev.ownerIndex);
|
||
|
|
||
|
// icon
|
||
|
Q_strncpy( msg.szIcon, "d_ball", ARRAYSIZE(msg.szIcon) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Adds an additional death message
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudDeathNotice::AddAdditionalMsg( int iKillerID, int iVictimID, const char *pMsgKey )
|
||
|
{
|
||
|
DeathNoticeItem &msg2 = m_DeathNotices[AddDeathNoticeItem()];
|
||
|
Q_strncpy( msg2.Killer.szName, g_PR->GetPlayerName( iKillerID ), ARRAYSIZE( msg2.Killer.szName ) );
|
||
|
msg2.Killer.iTeam = g_PR->GetTeam( iKillerID );
|
||
|
Q_strncpy( msg2.Victim.szName, g_PR->GetPlayerName( iVictimID ), ARRAYSIZE( msg2.Victim.szName ) );
|
||
|
msg2.Victim.iTeam = g_PR->GetTeam( iVictimID );
|
||
|
const wchar_t *wzMsg = g_pVGuiLocalize->Find( pMsgKey );
|
||
|
if ( wzMsg )
|
||
|
{
|
||
|
V_wcsncpy( msg2.wzInfoText, wzMsg, sizeof( msg2.wzInfoText ) );
|
||
|
}
|
||
|
msg2.iconDeath = m_iconDomination;
|
||
|
int iLocalPlayerIndex = GetLocalPlayerIndex();
|
||
|
if ( iLocalPlayerIndex == iVictimID || iLocalPlayerIndex == iKillerID )
|
||
|
{
|
||
|
msg2.bLocalPlayerInvolved = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudDeathNotice::AddStreakMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iKillerStreak, int iStreakIncrement, int iVictimID, int iDeathNoticeMsg )
|
||
|
{
|
||
|
int nMinStreak = MinStreakForType( eStreakType );
|
||
|
if ( iKillerStreak < nMinStreak )
|
||
|
return;
|
||
|
|
||
|
if ( !m_pStreakNotice )
|
||
|
return;
|
||
|
|
||
|
if ( cl_hud_killstreak_display_time.GetInt() <= 0 )
|
||
|
return;
|
||
|
|
||
|
m_pStreakNotice->StreakUpdated( eStreakType, iKillerID, iKillerStreak, iStreakIncrement );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudDeathNotice::AddStreakEndedMsg( CTFPlayerShared::ETFStreak eStreakType, int iKillerID, int iVictimID, int iVictimStreak, int iDeathNoticeMsg )
|
||
|
{
|
||
|
int nMinStreak = MinStreakForType( eStreakType );
|
||
|
if ( iVictimStreak < nMinStreak )
|
||
|
return;
|
||
|
|
||
|
if ( !m_pStreakNotice )
|
||
|
return;
|
||
|
|
||
|
if ( cl_hud_killstreak_display_time.GetInt() <= 0 )
|
||
|
return;
|
||
|
|
||
|
m_pStreakNotice->StreakEnded( eStreakType, iKillerID, iVictimID, iVictimStreak );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: returns the color to draw text in for this team.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
Color CTFHudDeathNotice::GetTeamColor( int iTeamNumber, bool bLocalPlayerInvolved /* = false */ )
|
||
|
{
|
||
|
switch ( iTeamNumber )
|
||
|
{
|
||
|
case TF_TEAM_BLUE:
|
||
|
return m_clrBlueText;
|
||
|
break;
|
||
|
case TF_TEAM_RED:
|
||
|
return m_clrRedText;
|
||
|
break;
|
||
|
case TEAM_UNASSIGNED:
|
||
|
if ( bLocalPlayerInvolved )
|
||
|
return m_clrLocalPlayerText;
|
||
|
else
|
||
|
return Color( 255, 255, 255, 255 );
|
||
|
break;
|
||
|
case TF_TEAM_HALLOWEEN:
|
||
|
if ( TFGameRules() && ( TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) || TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) ) )
|
||
|
{
|
||
|
return m_clrGreenText;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return m_clrPurpleText;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
AssertOnce( false ); // invalid team
|
||
|
return Color( 255, 255, 255, 255 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
Color CTFHudDeathNotice::GetInfoTextColor( int iDeathNoticeMsg )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
if ( msg.bLocalPlayerInvolved )
|
||
|
return m_clrLocalPlayerText;
|
||
|
|
||
|
return Color( 255, 255, 255, 255 );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
Color CTFHudDeathNotice::GetBackgroundColor ( int iDeathNoticeMsg )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[ iDeathNoticeMsg ];
|
||
|
|
||
|
return msg.bLocalPlayerInvolved ? m_clrLocalBGColor : m_clrBaseBGColor;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CTFHudDeathNotice::UseExistingNotice( IGameEvent *event )
|
||
|
{
|
||
|
// Fish Notices and Throwables
|
||
|
// Add check for all throwables
|
||
|
int iTarget = event->GetInt( "weaponid" );
|
||
|
if (iTarget == TF_WEAPON_BAT_FISH || iTarget == TF_WEAPON_THROWABLE || iTarget == TF_WEAPON_GRENADE_THROWABLE )
|
||
|
{
|
||
|
// Look for a matching pre-existing notice.
|
||
|
for ( int i=0; i<m_DeathNotices.Count(); ++i )
|
||
|
{
|
||
|
DeathNoticeItem &msg = m_DeathNotices[i];
|
||
|
|
||
|
if ( msg.iWeaponID != iTarget )
|
||
|
continue;
|
||
|
|
||
|
if ( msg.iKillerID != event->GetInt( "attacker" ) )
|
||
|
continue;
|
||
|
|
||
|
if ( msg.iVictimID != event->GetInt( "userid" ) )
|
||
|
continue;
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::UseExistingNotice( event );
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CHudTexture* CTFHudDeathNotice::GetMannPowerIcon( RuneTypes_t tRuneType, bool bIsRedTeam )
|
||
|
{
|
||
|
// Red team is normal file and blue is dNeg file
|
||
|
switch ( tRuneType )
|
||
|
{
|
||
|
case RUNE_STRENGTH: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_strength" ) : gHUD.GetIcon( "dneg_mannpower_strength" );
|
||
|
case RUNE_HASTE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_haste" ) : gHUD.GetIcon( "dneg_mannpower_haste" );
|
||
|
case RUNE_REGEN: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_regen" ) : gHUD.GetIcon( "dneg_mannpower_regen" );
|
||
|
case RUNE_RESIST: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_resist" ) : gHUD.GetIcon( "dneg_mannpower_resist" );
|
||
|
case RUNE_VAMPIRE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_vamp" ) : gHUD.GetIcon( "dneg_mannpower_vamp" );
|
||
|
case RUNE_REFLECT: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_reflect" ) : gHUD.GetIcon( "dneg_mannpower_reflect" );
|
||
|
case RUNE_PRECISION: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_precision" ) : gHUD.GetIcon( "dneg_mannpower_precision" );
|
||
|
case RUNE_AGILITY: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_agility" ) : gHUD.GetIcon( "dneg_mannpower_agility" );
|
||
|
case RUNE_KNOCKOUT: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_fist" ) : gHUD.GetIcon( "dneg_mannpower_fist" );
|
||
|
case RUNE_KING: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_king" ) : gHUD.GetIcon( "dneg_mannpower_king" );
|
||
|
case RUNE_PLAGUE: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_plague" ) : gHUD.GetIcon( "dneg_mannpower_plague" );
|
||
|
case RUNE_SUPERNOVA: return bIsRedTeam ? gHUD.GetIcon( "d_mannpower_supernova" ) : gHUD.GetIcon( "dneg_mannpower_supernova" );
|
||
|
default: return NULL;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|