|
|
//========= 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; }
|