//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include "c_tf_player.h"
#include "iclientmode.h"
#include "ienginevgui.h"
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui_controls/EditablePanel.h>
#include <vgui_controls/ProgressBar.h>
#include "view_scene.h"
#include "view.h"
#include "tf_gamerules.h"
#include "tf_logic_halloween_2014.h"
#include "tf_weapon_invis.h"
#include <vgui_controls/AnimationController.h>

#include "c_tf_objective_resource.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

using namespace vgui;

extern ISoundEmitterSystemBase *soundemitterbase;

// Floating delta text items, float off the top of the frame to 
// show changes to the metal account value
typedef struct 
{
	enum eAccountDeltaType_t
	{
		ACCOUNT_DELTA_INVALID,
		ACCOUNT_DELTA_HEALING,
		ACCOUNT_DELTA_DAMAGE,
		ACCOUNT_DELTA_BONUS_POINTS,
		ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_RED,
		ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_BLUE,
	};



	// amount of delta
	int m_iAmount;
	
	bool m_bLargeFont;		// display larger font
	eAccountDeltaType_t m_eDataType;

	// die time
	float m_flDieTime;

	// position
	int m_nX;				// X Pos in screen space & world space
	int m_nXEnd;			// Ending X Pos in screen space and world space
	int m_nHStart;			// Starting Y Pos in screen space, Z pos in world space
	int m_nHEnd;			// Ending Y Pos in screen space, Z pos in world space
	int m_nY;				// Y Coord in world space, not used in screen space
	bool m_bWorldSpace;
	float m_flBatchWindow;
	int m_nSourceID;		// Can be entindex, etc
	Color m_color;
	bool m_bShadows;

	// append a bit of extra text to the end
	wchar_t m_wzText[8];

} account_delta_t;

#define NUM_ACCOUNT_DELTA_ITEMS 10

ConVar hud_combattext( "hud_combattext", "1", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
ConVar hud_combattext_healing( "hud_combattext_healing", "1", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Shows health restored per-second over heal targets." );
ConVar hud_combattext_batching( "hud_combattext_batching", "0", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "If set to 1, numbers that are too close together are merged." );
ConVar hud_combattext_batching_window( "hud_combattext_batching_window", "0.2", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Maximum delay between damage events in order to batch numbers.", true, 0.1, true, 2.0 );
ConVar hud_combattext_doesnt_block_overhead_text( "hud_combattext_doesnt_block_overhead_text", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "If set to 1, allow text like \"CRIT\" to still show over a victim's head." );

ConVar tf_dingalingaling( "tf_dingalingaling", "0", FCVAR_ARCHIVE, "If set to 1, play a sound everytime you injure an enemy. The sound can be customized by replacing the 'tf/sound/ui/hitsound.wav' file." );
ConVar tf_dingaling_volume( "tf_dingaling_volume", "0.75", FCVAR_ARCHIVE, "Desired volume of the hit sound.", true, 0.0, true, 1.0 );
ConVar tf_dingaling_pitchmindmg( "tf_dingaling_pitchmindmg", "100", FCVAR_ARCHIVE, "Desired pitch of the hit sound when a minimal damage hit (<= 10 health) is done.", true, 1, true, 255 );
ConVar tf_dingaling_pitchmaxdmg( "tf_dingaling_pitchmaxdmg", "100", FCVAR_ARCHIVE, "Desired pitch of the hit sound when a maximum damage hit (>= 150 health) is done.", true, 1, true, 255 );
ConVar tf_dingaling_pitch_override( "tf_dingaling_pitch_override", "-1", FCVAR_NONE, "If set, pitch for all hit sounds." );

ConVar tf_dingalingaling_lasthit( "tf_dingalingaling_lasthit", "0", FCVAR_ARCHIVE, "If set to 1, play a sound whenever one of your attacks kills an enemy. The sound can be customized by replacing the 'tf/sound/ui/killsound.wav' file." );
ConVar tf_dingaling_lasthit_volume( "tf_dingaling_lasthit_volume", "0.75", FCVAR_ARCHIVE, "Desired volume of the last hit sound.", true, 0.0, true, 1.0 );
ConVar tf_dingaling_lasthit_pitchmindmg( "tf_dingaling_lasthit_pitchmindmg", "100", FCVAR_ARCHIVE, "Desired pitch of the last hit sound when a minimal damage hit (<= 10 health) is done.", true, 1, true, 255 );
ConVar tf_dingaling_lasthit_pitchmaxdmg( "tf_dingaling_lasthit_pitchmaxdmg", "100", FCVAR_ARCHIVE, "Desired pitch of the last hit sound when a maximum damage hit (>= 150 health) is done.", true, 1, true, 255 );
ConVar tf_dingaling_lasthit_pitch_override( "tf_dingaling_lasthit_pitch_override", "-1", FCVAR_NONE, "If set, pitch for last hit sounds." );

ConVar tf_dingalingaling_repeat_delay( "tf_dingalingaling_repeat_delay", "0.0", FCVAR_ARCHIVE, "Desired repeat delay of the hit sound.  Set to 0 to play a sound for every instance of damage dealt.", true, 0.f, false, 0.f );

ConVar hud_damagemeter( "hud_damagemeter", "0", FCVAR_CHEAT, "Display damage-per-second information in the lower right corner of the screen." );
ConVar hud_damagemeter_period( "hud_damagemeter_period", "0", FCVAR_NONE, "When set to zero, average damage-per-second across all recent damage events, otherwise average damage across defined period (number of seconds)." );
ConVar hud_damagemeter_ooctimer( "hud_damagemeter_ooctimer", "1", FCVAR_NONE, "How many seconds after the last damage event before we consider the player out of combat." );

struct hitsound_params_t
{
	hitsound_params_t( const char * pszName, int minpitch, int maxpitch )
	{
		m_pszName = pszName;
		m_iMinPitch = minpitch;
		m_iMaxPitch = maxpitch;
	}

	float GetPitchMin( bool bLastHit ) const
	{
		return bLastHit ? tf_dingaling_lasthit_pitchmindmg.GetInt() : tf_dingaling_pitchmindmg.GetInt();
		//return RemapValClamped( tf_dingaling_pitchmindmg.GetInt(), 0, 100, m_iMinPitch, m_iMaxPitch );
	}

	float GetPitchMax( bool bLastHit ) const
	{
		return bLastHit ? tf_dingaling_lasthit_pitchmaxdmg.GetInt() : tf_dingaling_pitchmaxdmg.GetInt();
		//return RemapValClamped( tf_dingaling_pitchmaxdmg.GetInt(), 0, 100, m_iMinPitch, m_iMaxPitch );
	}

	float GetPitchFromDamage( int damage, bool bLastHit ) const
	{
		if ( bLastHit && tf_dingaling_lasthit_pitch_override.GetInt() > 0 )
		{
			return tf_dingaling_lasthit_pitch_override.GetFloat();
		}
		else if ( tf_dingaling_pitch_override.GetInt() > 0 )
		{
			return tf_dingaling_pitch_override.GetFloat();
		}

		return RemapValClamped( damage, 10, 150, GetPitchMin( bLastHit ), GetPitchMax( bLastHit ) );
	}

	const char *m_pszName;
	int m_iMinPitch;
	int m_iMaxPitch;
};

static const hitsound_params_t g_HitSounds[] =
{
	hitsound_params_t( "Player.HitSoundDefaultDing",		1,			255 ),
	hitsound_params_t( "Player.HitSoundElectro",			1,			255 ),
	hitsound_params_t( "Player.HitSoundNotes",				1,			255 ),
	hitsound_params_t( "Player.HitSoundPercussion",			1,			255 ),
	hitsound_params_t( "Player.HitSoundRetro",				1,			255 ),
	hitsound_params_t( "Player.HitSoundSpace",				1,			255 ),
	hitsound_params_t( "Player.HitSoundBeepo",				1,			255 ),
	hitsound_params_t( "Player.HitSoundVortex",				1,			255 ),
	hitsound_params_t( "Player.HitSoundSquasher",			1,			255 ),
};

static const hitsound_params_t g_LastHitSounds[] =
{
	hitsound_params_t( "Player.KillSoundDefaultDing", 1, 255 ),
	hitsound_params_t( "Player.KillSoundElectro", 1, 255 ),
	hitsound_params_t( "Player.KillSoundNotes", 1, 255 ),
	hitsound_params_t( "Player.KillSoundPercussion", 1, 255 ),
	hitsound_params_t( "Player.KillSoundRetro", 1, 255 ),
	hitsound_params_t( "Player.KillSoundSpace", 1, 255 ),
	hitsound_params_t( "Player.KillSoundBeepo", 1, 255 ),
	hitsound_params_t( "Player.KillSoundVortex", 1, 255 ),
	hitsound_params_t( "Player.KillSoundSquasher", 1, 255 ),
};

ConVar tf_dingalingaling_effect( "tf_dingalingaling_effect", "0", FCVAR_ARCHIVE, "Which Dingalingaling sound is used", true, 0, true, ARRAYSIZE( g_HitSounds )-1 );
ConVar tf_dingalingaling_last_effect( "tf_dingalingaling_last_effect", "0", FCVAR_ARCHIVE, "Which final hit sound to play when the target expires.", true, 0, true, ARRAYSIZE( g_LastHitSounds )-1 );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CAccountPanel : public EditablePanel
{
	DECLARE_CLASS_SIMPLE( CAccountPanel, EditablePanel );

public:
	CAccountPanel( Panel *parent, const char *name )
		: EditablePanel( parent, name )
	{
		m_nBGTexture = -1;
		m_bNegativeFlipDir = false;
		SetDialogVariable( "metal", 0 );
	}

	virtual void	ApplySchemeSettings( IScheme *scheme ) OVERRIDE;
	virtual void	ApplySettings( KeyValues *inResourceData ) OVERRIDE;
	virtual void	Paint( void ) OVERRIDE;

	virtual account_delta_t *OnAccountValueChanged( int iOldValue, int iNewValue, account_delta_t::eAccountDeltaType_t type );

	virtual const char *GetResFileName( void ) { return "resource/UI/HudAccountPanel.res"; }

protected:
	virtual Color GetColor( const account_delta_t::eAccountDeltaType_t& type );

	CUtlVector <account_delta_t> m_AccountDeltaItems;

	int m_nBGTexture;
	bool m_bNegativeFlipDir;

	CPanelAnimationVarAliasType( float, m_flDeltaItemStartPos, "delta_item_start_y", "100", "proportional_float" );
	CPanelAnimationVarAliasType( float, m_flDeltaItemEndPos, "delta_item_end_y", "0", "proportional_float" );

	CPanelAnimationVarAliasType( float, m_flDeltaItemX, "delta_item_x", "0", "proportional_float" );
	CPanelAnimationVarAliasType( float, m_flDeltaItemXEndPos, "delta_item_end_x", "0", "proportional_float" );

	CPanelAnimationVarAliasType( float, m_flBGImageX, "bg_image_x", "0", "proportional_float" );
	CPanelAnimationVarAliasType( float, m_flBGImageY, "bg_image_y", "0", "proportional_float" );
	CPanelAnimationVarAliasType( float, m_flBGImageWide, "bg_image_wide", "0", "proportional_float" );
	CPanelAnimationVarAliasType( float, m_flBGImageTall, "bg_image_tall", "0", "proportional_float" );

	CPanelAnimationVar( Color, m_DeltaPositiveColor, "PositiveColor", "0 255 0 255" );
	CPanelAnimationVar( Color, m_DeltaNegativeColor, "NegativeColor", "255 0 0 255" );
	CPanelAnimationVar( Color, m_DeltaEventColor, "EventColor", "255 0 255 255" );
	CPanelAnimationVar( Color, m_DeltaRedRobotScoreColor, "RedRobotScoreColor", "255 0 0 255" );
	CPanelAnimationVar( Color, m_DeltaBlueRobotScoreColor, "BlueRobotScoreColor", "0 166 255 255" );

	CPanelAnimationVar( float, m_flDeltaLifetime, "delta_lifetime", "2.0" );

	CPanelAnimationVar( vgui::HFont, m_hDeltaItemFont, "delta_item_font", "Default" );
	CPanelAnimationVar( vgui::HFont, m_hDeltaItemFontBig, "delta_item_font_big", "Default" );
};


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CHudAccountPanel : public CHudElement, public CAccountPanel
{
	DECLARE_CLASS_SIMPLE( CHudAccountPanel, CAccountPanel );

public:
	CHudAccountPanel( const char *pElementName ) 
		: CHudElement( pElementName )
		, CAccountPanel( NULL, pElementName )
	{
		Panel *pParent = g_pClientMode->GetViewport();
		SetParent( pParent );
		SetHiddenBits( HIDEHUD_MISCSTATUS );
		ListenForGameEvent( "player_account_changed" );
	}

	virtual void LevelInit( void ) OVERRIDE
	{
		CHudElement::LevelInit();
	}

	virtual void FireGameEvent( IGameEvent *event ) OVERRIDE
	{
		const char * type = event->GetName();

		if ( Q_strcmp(type, "player_account_changed") == 0 )
		{
			int iOldValue = event->GetInt( "old_account" );
			int iNewValue = event->GetInt( "new_account" );
			account_delta_t::eAccountDeltaType_t deltaType = ( iNewValue - iOldValue >= 0 ) ? account_delta_t::ACCOUNT_DELTA_HEALING
																							: account_delta_t::ACCOUNT_DELTA_DAMAGE;

			OnAccountValueChanged( iOldValue, iNewValue, deltaType );
		}
		else
		{
			CHudElement::FireGameEvent( event );
		}
	}

	virtual bool ShouldDraw( void ) OVERRIDE
	{
		C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pPlayer || !pPlayer->IsAlive() || !pPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
		{
			m_AccountDeltaItems.RemoveAll();
			return false;
		}

		CTFPlayer *pTFPlayer = CTFPlayer::GetLocalTFPlayer();
		if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
			return false;
		
		if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
			return false;

		if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
			return false;

		return CHudElement::ShouldDraw();
	}
};

DECLARE_HUDELEMENT( CHudAccountPanel );

// Derived account panels
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CHealthAccountPanel : public CHudAccountPanel
{
	DECLARE_CLASS_SIMPLE( CHealthAccountPanel, CHudAccountPanel );
public:
	CHealthAccountPanel( const char *pElementName ) : CHudAccountPanel(pElementName)
	{
		ListenForGameEvent( "player_healonhit" );
	}

	virtual const char *GetResFileName( void ) { return "resource/UI/HudHealthAccount.res"; }

	void FireGameEvent( IGameEvent *event )
	{
		const char * type = event->GetName();

		if ( Q_strcmp(type, "player_healonhit") == 0 )
		{
			int iAmount = event->GetInt( "amount" );
			int iPlayer = event->GetInt( "entindex" );
			CTFPlayer *pEventPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayer ) );
			if ( pEventPlayer && !pEventPlayer->IsDormant() )
			{
				if ( pEventPlayer == C_TFPlayer::GetLocalTFPlayer() )
				{
					OnAccountValueChanged( 0, iAmount, account_delta_t::ACCOUNT_DELTA_HEALING );
				}
				else
				{
					CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
					if ( pLocalPlayer )
					{
						bool bEnemySpy = !pLocalPlayer->InSameTeam( pEventPlayer ) && pEventPlayer->IsPlayerClass( TF_CLASS_SPY );
						bool bSneakyEnemySpy = bEnemySpy && ( pEventPlayer->m_Shared.IsStealthed() || pLocalPlayer->m_Shared.IsSpyDisguisedAsMyTeam( pEventPlayer ) );
						bool bShouldSpawnRedParticle = ( pEventPlayer->GetTeamNumber() == TF_TEAM_RED );
						if ( bSneakyEnemySpy )
						{
							bShouldSpawnRedParticle = ( GetLocalPlayerTeam() == TF_TEAM_RED );
						}

						const char *pEffectName;
						if ( iAmount < 0 )
						{
							pEffectName = bShouldSpawnRedParticle ? "healthlost_red" : "healthlost_blu";
						}
						else if ( iAmount >= 100 )
						{
							if ( pEventPlayer->IsMiniBoss() )
							{
								pEffectName = bShouldSpawnRedParticle ? "healthgained_red_giant" : "healthgained_blu_giant";

							}
							else
							{
								pEffectName = bShouldSpawnRedParticle ? "healthgained_red_large" : "healthgained_blu_large";
							}
						}
						else
						{
							pEffectName = bShouldSpawnRedParticle ? "healthgained_red" : "healthgained_blu";
						}

						pEventPlayer->ParticleProp()->Create( pEffectName, PATTACH_POINT, "head" );
					}
				}
			}
		}
		else
		{
			CHudElement::FireGameEvent( event );
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	bool ShouldDraw( void )
	{
		C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pPlayer || !pPlayer->IsAlive() )
		{
			m_AccountDeltaItems.RemoveAll();
		}

		if ( !m_AccountDeltaItems.Count() )
			return false;

		if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
			return false;

		if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
			return false;

		if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
			return false;

		return CHudElement::ShouldDraw();
	}
};
DECLARE_HUDELEMENT( CHealthAccountPanel );

class CScoreAccountPanel : public CAccountPanel, public CGameEventListener
{
	DECLARE_CLASS_SIMPLE( CScoreAccountPanel, CAccountPanel );
public:
	CScoreAccountPanel( Panel *parent, const char *name )
		: CAccountPanel( parent, name )
		, m_nTeam( TF_TEAM_COUNT )
	{}

	virtual const char *GetResFileName( void ) { return "resource/UI/HudScoreAccount.res"; }

	virtual void FireGameEvent( IGameEvent *event ) OVERRIDE
	{
		const char * pszEventName = event->GetName();

		if ( Q_strcmp(pszEventName, m_pszEventName) == 0 )
		{
			CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
			if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
				return;

			int nTeam = event->GetInt( "team" );
			if ( m_nTeam != nTeam )
				return;

			const int nPoints = event->GetInt( "points" );
			if ( !nPoints )
				return;

			account_delta_t* pNewAccount = OnAccountValueChanged( 0, nPoints, account_delta_t::ACCOUNT_DELTA_BONUS_POINTS );
			if ( pNewAccount )
			{
				pNewAccount->m_bShadows = true;
				pNewAccount->m_flBatchWindow = pNewAccount->m_flDieTime;
				pNewAccount->m_nSourceID = (( nPoints > 0 ) ? 0 : 1 ) + (nTeam * 2);

				if ( ( GetLocalPlayerTeam() == nTeam && nPoints > 0 ) || ( GetLocalPlayerTeam() != nTeam && nPoints < 0 ) )
				{
					pNewAccount->m_color = m_DeltaPositiveColor;
				}
				else
				{
					pNewAccount->m_color = m_DeltaNegativeColor;
				}
			}
		}
	}

	void ApplySettings( KeyValues *inResourceData )
	{
		BaseClass::ApplySettings( inResourceData );

		Q_strncpy( m_pszEventName, inResourceData->GetString( "event" ), sizeof( m_pszEventName ) );
		if ( m_pszEventName )
		{
			ListenForGameEvent( m_pszEventName );
		}

		const char *pszTeam = inResourceData->GetString( "team" );
		if ( Q_stricmp( pszTeam, "red" ) == 0 )
		{
			m_nTeam = TF_TEAM_RED;
		}
		else
		{
			m_nTeam = TF_TEAM_BLUE;
		}
	}

private:
	char m_pszEventName[32]; // max length of event names
	int	 m_nTeam;
};

DECLARE_BUILD_FACTORY( CScoreAccountPanel );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CDamageAccountPanel : public CHudAccountPanel
{
	DECLARE_CLASS_SIMPLE( CDamageAccountPanel, CHudAccountPanel );
public:
	CDamageAccountPanel( const char *pElementName ) : CHudAccountPanel(pElementName)
	{
		ListenForGameEvent( "player_hurt" );
		ListenForGameEvent( "npc_hurt" );
		ListenForGameEvent( "player_healed" );
		ListenForGameEvent( "player_bonuspoints" );
		ListenForGameEvent( "building_healed" );

		ResetDamageVars();
		vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
	}

	virtual void OnTick( void );
	virtual const char *GetResFileName( void ) { return "resource/UI/HudDamageAccount.res"; }
	virtual void Paint( void );


	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	bool ShouldDrawDPSMeter( void )
	{
		return hud_damagemeter.GetBool();
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void DisplayDamageFeedback( CTFPlayer *pAttacker, CBaseCombatCharacter *pVictim, int iDamage, int iHealth, bool bIsCrit )
	{
		if ( iDamage <= 0 ) // zero value (invuln?)
			return;

		CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
			return;

		if ( !pAttacker || !pVictim )
			return;

		// Show the attacker, or when healing the player that is
		if ( ( pAttacker == pLocalPlayer )  ||
			 ( pLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && ( pLocalPlayer->MedicGetHealTarget() == pAttacker ) ) )
		{
			bool bDeadRingerSpy = false;
			C_TFPlayer *pVictimPlayer = ToTFPlayer( pVictim );
			if ( pVictimPlayer )
			{
				// Player hurt self
				if ( pAttacker == pVictimPlayer )
					return;

				// Don't show damage on stealthed and/or disguised enemy spies
				if ( pVictimPlayer->IsPlayerClass( TF_CLASS_SPY ) && pVictimPlayer->GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
				{
					CTFWeaponInvis *pWpn = (CTFWeaponInvis *)pVictimPlayer->Weapon_OwnsThisID( TF_WEAPON_INVIS );
					if ( pWpn && pWpn->HasFeignDeath() )
					{
						if ( pVictimPlayer->m_Shared.IsFeignDeathReady() )
						{
							bDeadRingerSpy = true;
						}
					}

					if ( !bDeadRingerSpy )
					{
						if ( pVictimPlayer->m_Shared.GetDisguiseTeam() == pLocalPlayer->GetTeamNumber() || pVictimPlayer->m_Shared.IsStealthed() )
							return;
					}
				}
			}

			if ( pAttacker == pLocalPlayer )
			{
				g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "DamagedPlayer" );

				bool bHitEnabled = ( tf_dingalingaling.GetBool() );
				bool bLastHitEnabled = ( tf_dingalingaling_lasthit.GetBool() );
				bool bLastHit = ( iHealth <= 0 ) || bDeadRingerSpy;
				if ( bLastHitEnabled && bLastHit )
				{
					// Always allow the last hit sound
					m_flLastDingTime = 0.f;
				}
				
				// Play hitbeeps 
				if ( ( bHitEnabled || bLastHitEnabled ) && 
					 ( gpGlobals->curtime > ( m_flLastDingTime + tf_dingalingaling_repeat_delay.GetFloat() ) || tf_dingalingaling_repeat_delay.GetFloat() == 0.f ) )
				{
					m_flLastDingTime = gpGlobals->curtime;

					CSoundParameters params;
					CLocalPlayerFilter filter;
					const char *pszSound = NULL;
					const hitsound_params_t *pHitSound = NULL;

					if ( bLastHit && bLastHitEnabled )
					{
						pszSound = g_LastHitSounds[tf_dingalingaling_last_effect.GetInt()].m_pszName;
						pHitSound = &g_LastHitSounds[tf_dingalingaling_last_effect.GetInt()];
						if ( pszSound && pHitSound && CBaseEntity::GetParametersForSound( pszSound, params, NULL ) )
						{
							EmitSound_t es( params );
							es.m_nPitch = pHitSound->GetPitchFromDamage( iDamage, bLastHit );
							es.m_flVolume = tf_dingaling_lasthit_volume.GetFloat();
							pLocalPlayer->EmitSound( filter, pLocalPlayer->entindex(), es );
						}
					}
					else if ( bHitEnabled )
					{
						pszSound = g_HitSounds[tf_dingalingaling_effect.GetInt()].m_pszName;
						pHitSound = &g_HitSounds[tf_dingalingaling_effect.GetInt()];
						if ( pszSound && pHitSound && CBaseEntity::GetParametersForSound( pszSound, params, NULL ) )
						{
							EmitSound_t es( params );
							es.m_nPitch = pHitSound->GetPitchFromDamage( iDamage, false );
							es.m_flVolume = tf_dingaling_volume.GetFloat();
							pLocalPlayer->EmitSound( filter, pLocalPlayer->entindex(), es );
						}
					}
				}
			}

			if ( hud_combattext.GetBool() )
			{
				// Ignore damage events on targets that we can't see, so it's not a cheat
				trace_t	tr;
				UTIL_TraceLine( pVictim->WorldSpaceCenter(), MainViewOrigin(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
				if ( tr.fraction >= 1.f )
				{
					account_delta_t *pNewAccount = OnAccountValueChanged( 0, -iDamage, account_delta_t::ACCOUNT_DELTA_DAMAGE );
					if ( pNewAccount )
					{
						Vector vecPos = pVictim->GetAbsOrigin();
						Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
						int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
						vecPos.z += (VEC_HULL_MAX_SCALED( pVictim ).z + nHeightoffset);
						pNewAccount->m_nX = vecPos.x;
						pNewAccount->m_nXEnd = pNewAccount->m_nX;
						pNewAccount->m_nY = vecPos.y;
						pNewAccount->m_nHStart = vecPos.z;
						pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32;	// How many units to float up
						pNewAccount->m_bWorldSpace = true;
						pNewAccount->m_nSourceID = pVictim->entindex();
						pNewAccount->m_flBatchWindow = hud_combattext_batching.GetBool() ? hud_combattext_batching_window.GetFloat() : 0.f;
						pNewAccount->m_bLargeFont = bIsCrit;
						//	V_swprintf_safe( pNewAccount->m_wzText, L" (%d)", m_nQueuedDamageEvents );
					}
				}
			}

			m_flLastDamageEventTime = gpGlobals->curtime;

			// Damage meter tracking
			if ( hud_damagemeter_period.GetFloat() > 0.f )
			{
				// Store events and average across a sliding window
				DamageHistory_t damage = { (float)iDamage, m_flLastDamageEventTime };
				m_DamageHistory.AddToTail( damage );
			}
			else
			{
				// Running tally until we hit the out-of-combat timer
				if ( m_flFirstDamageEventTime == 0.f )
				{
					m_flFirstDamageEventTime = m_flLastDamageEventTime;
					m_flDamagePerSecond = 0.f;
					m_flDamageMeterTotal = 0.f;
				}
				
				m_flDamageMeterTotal += iDamage;
			}
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void FireGameEvent( IGameEvent *event )
	{
		if ( FStrEq( event->GetName(), "player_hurt" ) )
		{
			const int iDamage = event->GetInt( "damageamount" );
			const int iHealth = event->GetInt( "health" );

			const int iAttacker = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
			C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( iAttacker ) );

			const int iVictim = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
			C_TFPlayer *pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictim ) );

			DisplayDamageFeedback( pAttacker, pVictim, iDamage, iHealth, event->GetBool( "crit", 0 ) );
		}
		else if ( FStrEq( event->GetName(), "npc_hurt" ) )
		{
			const int iDamage = event->GetInt( "damageamount" );
			const int iHealth = event->GetInt( "health" );

			const int iAttacker = engine->GetPlayerForUserID( event->GetInt( "attacker_player" ) );
			C_TFPlayer *pAttacker = ToTFPlayer( UTIL_PlayerByIndex( iAttacker ) );

			C_BaseCombatCharacter *pVictim = (C_BaseCombatCharacter *)ClientEntityList().GetClientEntity( event->GetInt( "entindex" ) );
			
			DisplayDamageFeedback( pAttacker, pVictim, iDamage, iHealth, event->GetBool( "crit", 0 ) );
		}
		else if ( FStrEq( event->GetName(), "player_healed" ) )
		{
			if ( hud_combattext.GetBool() && hud_combattext_healing.GetBool() )
			{
				CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
				if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
					return;

				const int iHealer = engine->GetPlayerForUserID( event->GetInt( "healer" ) );
				CBasePlayer *pHealer = UTIL_PlayerByIndex( iHealer );
				if ( pHealer && pHealer == pLocalPlayer )
				{
					const int iPatient = engine->GetPlayerForUserID( event->GetInt( "patient" ) );
					CBasePlayer *pPatient = UTIL_PlayerByIndex( iPatient );

					if ( pPatient )
					{
						const int iHealedAmt = event->GetInt( "amount" );

						account_delta_t *pNewAccount = OnAccountValueChanged( 0, iHealedAmt, account_delta_t::ACCOUNT_DELTA_HEALING );
						if ( pNewAccount )
						{
							Vector vecPos = pPatient->GetAbsOrigin();
							Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
							int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
							vecPos.z += ( VEC_HULL_MAX_SCALED( pPatient ).z + nHeightoffset );
							pNewAccount->m_nX = vecPos.x;
							pNewAccount->m_nXEnd = pNewAccount->m_nX;
							pNewAccount->m_nY = vecPos.y;
							pNewAccount->m_nHStart = vecPos.z;
							pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32;		// Float 32 units up in worldspace
							pNewAccount->m_bWorldSpace = true;
						}
					}
				}
			}
		}
		else if ( FStrEq( event->GetName(), "player_bonuspoints" ) )
		{
			if ( hud_combattext.GetBool() )
			{
				CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
				if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
					return;

				const int nPoints = ( event->GetInt( "points" ) / 10 );
				if ( !nPoints )
					return;

				const int iPlayer = event->GetInt( "player_entindex" );
				CBasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayer );
				if ( pPlayer && pPlayer == pLocalPlayer )
				{
					const int iSource = event->GetInt( "source_entindex" );
					C_BaseEntity *pSource = ClientEntityList().GetBaseEntity( iSource );
					if ( !pSource )
						return;

					account_delta_t *pNewAccount = OnAccountValueChanged( 0, nPoints, account_delta_t::ACCOUNT_DELTA_BONUS_POINTS );
					if ( pNewAccount )
					{
						Vector vecPos = pSource->GetAbsOrigin();
						Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
						int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
						vecPos.z += ( pSource->IsPlayer() ) ? (VEC_HULL_MAX_SCALED( pSource->GetBaseAnimating() ).z + nHeightoffset) : 0;
						pNewAccount->m_nX = vecPos.x;
						pNewAccount->m_nXEnd = pNewAccount->m_nX;
						pNewAccount->m_nY = vecPos.y;
						pNewAccount->m_nHStart = vecPos.z;
						pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 16;
						pNewAccount->m_bWorldSpace = true;
					}
				}
			}
		}
		else if ( FStrEq( event->GetName(), "building_healed" ) )
		{
			if ( !hud_combattext.GetBool() || !hud_combattext_healing.GetBool() )
				return;

			CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
			if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
				return;

			CBaseEntity *pHealer = ClientEntityList().GetEnt( event->GetInt( "healer" ) );
			if ( pHealer && pHealer == pLocalPlayer )
			{
				CBaseEntity *pBuilding = ClientEntityList().GetEnt( event->GetInt( "building" ) );
				if ( !pBuilding )
					return;

				const int iHealedAmt = event->GetInt( "amount" );

				account_delta_t *pNewAccount = OnAccountValueChanged( 0, iHealedAmt, account_delta_t::ACCOUNT_DELTA_HEALING );
				if ( pNewAccount )
				{
					Vector vecPos = pBuilding->GetAbsOrigin();
					Vector vecDistance = vecPos - pLocalPlayer->GetAbsOrigin();
					int nHeightoffset = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (200.0f * 200.0f), 1, 16 );
					vecPos.z += ( 64 + nHeightoffset );
					pNewAccount->m_nX = vecPos.x;
					pNewAccount->m_nXEnd = pNewAccount->m_nX;
					pNewAccount->m_nY = vecPos.y;
					pNewAccount->m_nHStart = vecPos.z;
					pNewAccount->m_nHEnd = pNewAccount->m_nHStart + 32;		// Float 32 units up in worldspace
					pNewAccount->m_bWorldSpace = true;
				}
				
			}
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	bool ShouldDraw( void )
	{
		C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pPlayer || !pPlayer->IsAlive() )
		{
			m_AccountDeltaItems.RemoveAll();
		}

		if ( ShouldDrawDPSMeter() )
			return true;

		if ( !m_AccountDeltaItems.Count() )
			return false;

		return CHudElement::ShouldDraw();
	}

	//-----------------------------------------------------------------------------
	// Purpose: called whenever a new level is starting
	//-----------------------------------------------------------------------------
	virtual void LevelInit( void ) OVERRIDE
	{
		ResetDamageVars();

		BaseClass::LevelInit();
	}

private:

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void ResetDamageVars( void )
	{
		m_flFirstDamageEventTime = 0.f;
		m_flLastDamageEventTime = 0.f;
		m_flDamagePerSecond = 0.f;
		m_flDamageMeterTotal = 0.f;
		m_flLastDingTime = 0.f;
	}

private:

	// DamageMeter
	float				m_flFirstDamageEventTime;
	float				m_flLastDamageEventTime;
	float				m_flDamagePerSecond;
	float				m_flDamageMeterTotal;
	struct DamageHistory_t
	{
		float flDamage;
		float flDamageTime;
	};
	CUtlVector< DamageHistory_t >	m_DamageHistory;

	// Dings
	float				m_flLastDingTime;
};

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CDamageAccountPanel::OnTick( void )
{
	if ( ShouldDrawDPSMeter() )
	{
		// We're out of combat - nuke everything
		if ( m_flLastDamageEventTime < gpGlobals->curtime - hud_damagemeter_ooctimer.GetFloat() )
		{
			m_DamageHistory.RemoveAll();
			m_flFirstDamageEventTime = 0.f;
		}
		else
		{
			float flPeriod = hud_damagemeter_period.GetFloat();
			
			// Period-based calculation (averaged across a defined range)
			if ( flPeriod > 0.f )
			{
				m_flDamageMeterTotal = 0.f;

				FOR_EACH_VEC_BACK( m_DamageHistory, i )
				{
					if ( flPeriod > 0.f )
					{
						// This method averages across a fixed period, so nuke entires outside the period
						if ( gpGlobals->curtime - flPeriod > m_DamageHistory[i].flDamageTime )
						{
							m_DamageHistory.Remove( i );
							continue;
						}
					}

					// What's left is within the period (sliding window)
					m_flDamageMeterTotal += m_DamageHistory[i].flDamage;
				}
				
				m_flDamagePerSecond = m_flDamageMeterTotal / flPeriod;
			}
			// Event-based calculation (absolute dps)
			else if ( m_flFirstDamageEventTime > 0.f )
			{
				flPeriod = Max( m_flLastDamageEventTime - m_flFirstDamageEventTime, 1.f );
				m_flDamagePerSecond = m_flDamageMeterTotal / flPeriod;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CDamageAccountPanel::Paint( void )
{
	BaseClass::Paint();

	if ( ShouldDrawDPSMeter() )
	{
		int iScreenWide, iScreenTall;
		GetHudSize( iScreenWide, iScreenTall );
		int nX = iScreenWide / 1.15;
		int nY = iScreenTall / 1.20;

		int r = 255, g = 255, b = 255;
		if ( m_flLastDamageEventTime < gpGlobals->curtime - hud_damagemeter_ooctimer.GetFloat() )
		{
			r = 255, g = 0, b = 0;
		}
		Color cDPS( r, g, b, 255 );

		vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
		vgui::surface()->DrawSetTextColor( cDPS );
		vgui::surface()->DrawSetTextPos( nX, nY );

		wchar_t wDPSBuf[20];
		V_swprintf_safe( wDPSBuf, L"%d DPS", (int)m_flDamagePerSecond );
		vgui::surface()->DrawPrintText( wDPSBuf, wcslen( wDPSBuf ), FONT_DRAW_NONADDITIVE );
	}
}

DECLARE_HUDELEMENT( CDamageAccountPanel );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAccountPanel::ApplySchemeSettings( IScheme *pScheme )
{
	// load control settings...
	LoadControlSettings( GetResFileName() );

	BaseClass::ApplySchemeSettings( pScheme );
}

void CAccountPanel::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	// Backwards compatibility.  If we DONT find "delta_item_end_x" specified in the keyvalues,
	// then just take the starting x-pos as the ending x-pos.
	if ( inResourceData->FindKey( "delta_item_end_x" ) == NULL )
	{
		m_flDeltaItemXEndPos = m_flDeltaItemX;
	}

	m_bNegativeFlipDir = inResourceData->FindKey( "negative_flip_dir", false );

	const char *pszBGTextureName = inResourceData->GetString( "bg_texture", NULL );
	if ( m_nBGTexture == -1 && pszBGTextureName && pszBGTextureName[0] )
	{
		m_nBGTexture = vgui::surface()->CreateNewTextureID();
		vgui::surface()->DrawSetTextureFile( m_nBGTexture , pszBGTextureName, true, false);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
account_delta_t *CAccountPanel::OnAccountValueChanged( int iOldValue, int iNewValue, account_delta_t::eAccountDeltaType_t type )
{
	// update the account value
	SetDialogVariable( "metal", iNewValue ); 

	int iDelta = iNewValue - iOldValue;

	C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( iDelta != 0 && pPlayer && pPlayer->IsAlive() )
	{
		int index = m_AccountDeltaItems.AddToTail();
		account_delta_t *pNewDeltaItem = &m_AccountDeltaItems[index];

		pNewDeltaItem->m_flDieTime = gpGlobals->curtime + m_flDeltaLifetime;
		pNewDeltaItem->m_iAmount = iDelta;
		pNewDeltaItem->m_nX = m_flDeltaItemX;
		pNewDeltaItem->m_nXEnd = m_flDeltaItemXEndPos;
		pNewDeltaItem->m_nHStart = m_flDeltaItemStartPos;
		pNewDeltaItem->m_nHEnd = m_flDeltaItemEndPos;
		pNewDeltaItem->m_bWorldSpace = false;
		pNewDeltaItem->m_nSourceID = -1;
		pNewDeltaItem->m_flBatchWindow = 0.f;
		pNewDeltaItem->m_bLargeFont = false;
		pNewDeltaItem->m_eDataType = type;
		pNewDeltaItem->m_wzText[0] = NULL;
		pNewDeltaItem->m_color = GetColor( type ); 
		pNewDeltaItem->m_bShadows = false;
		return &m_AccountDeltaItems[index];
	}

	return NULL;
}

Color CAccountPanel::GetColor( const account_delta_t::eAccountDeltaType_t& type )
{
	if ( type == account_delta_t::ACCOUNT_DELTA_BONUS_POINTS )
	{
		return m_DeltaEventColor;
	}
	else if ( type == account_delta_t::ACCOUNT_DELTA_HEALING )
	{
		return m_DeltaPositiveColor;
	}
	else if ( type == account_delta_t::ACCOUNT_DELTA_DAMAGE )
	{
		return m_DeltaNegativeColor;
	}
	else if ( type == account_delta_t::ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_BLUE )
	{
		return m_DeltaBlueRobotScoreColor;
	}
	else if ( type == account_delta_t::ACCOUNT_DELTA_ROBOT_DESTRUCTION_POINT_RED )
	{
		return m_DeltaRedRobotScoreColor;
	}
	
	return Color( 255, 255, 255, 255 );
}

//-----------------------------------------------------------------------------
// Purpose: Paint the deltas
//-----------------------------------------------------------------------------
void CAccountPanel::Paint( void )
{
	BaseClass::Paint();

	FOR_EACH_VEC_BACK( m_AccountDeltaItems, i )
	{
		// Reduce lifetime when count grows too high
		float flTimeMod = m_AccountDeltaItems.Count() > NUM_ACCOUNT_DELTA_ITEMS ? RemapValClamped( m_AccountDeltaItems.Count(), 10.f, 15.f, 0.5f, 1.5f ) : 0.f;

		// update all the valid delta items
		if ( ( m_AccountDeltaItems[i].m_flDieTime - flTimeMod ) > gpGlobals->curtime )
		{
			// position and alpha are determined from the lifetime
			Color c = m_AccountDeltaItems[i].m_color;

			float flLifetimePercent = ( m_flDeltaLifetime - ( m_AccountDeltaItems[i].m_flDieTime - gpGlobals->curtime ) ) / m_flDeltaLifetime;
			// fade out after half our lifetime
			int nAlpha = flLifetimePercent > 0.5 ? (int)( 255.0f * ( ( 0.5f - flLifetimePercent ) / 0.5f ) ) : 255;
			c[3] = nAlpha;
			


			// Some items want to be batched together as they're super frequent (i.e. damage events from a flamethrower, or minigun)
			if ( m_AccountDeltaItems[i].m_flBatchWindow > 0.f && m_AccountDeltaItems[i].m_nSourceID != -1 && m_AccountDeltaItems.IsValidIndex( i - 1 ) )
			{
				// If next item is from the same source and too close, merge
				float flDelay = m_AccountDeltaItems[i].m_flBatchWindow;
				if ( m_AccountDeltaItems[i].m_flDieTime - m_AccountDeltaItems[i-1].m_flDieTime <= flDelay &&
					 m_AccountDeltaItems[i-1].m_nSourceID == m_AccountDeltaItems[i].m_nSourceID )
				{
					m_AccountDeltaItems[i].m_iAmount += m_AccountDeltaItems[i-1].m_iAmount;
					m_AccountDeltaItems.Remove( i - 1 );
					continue;
				}
			}

			float flHeight = m_AccountDeltaItems[i].m_nHEnd - m_AccountDeltaItems[i].m_nHStart;
			float flWidth = m_AccountDeltaItems[i].m_nXEnd - m_AccountDeltaItems[i].m_nX;

			// We can be told to go the opposite direction if we're negative
			if ( m_bNegativeFlipDir && m_AccountDeltaItems[i].m_iAmount < 0 )
			{
				flHeight = -flHeight;
				flWidth = -flWidth;
			}

			float flYPos = m_AccountDeltaItems[i].m_nHStart + ( flLifetimePercent * flHeight );
			float flXPos = m_AccountDeltaItems[i].m_nX + ( flLifetimePercent * flWidth );
			if ( m_AccountDeltaItems[i].m_bWorldSpace )
			{
				Vector vecWorld( m_AccountDeltaItems[i].m_nX, m_AccountDeltaItems[i].m_nY, flYPos );
				int iX,iY;
				if ( !GetVectorInHudSpace( vecWorld, iX, iY ) )				// Tested - NOT GetVectorInScreenSpace
					continue;

				flXPos = iX;
				flYPos = iY;
			}

			// If we have a background texture, then draw it!
			if ( m_nBGTexture != -1 )
			{
				vgui::surface()->DrawSetColor(255,255,255,nAlpha);
				vgui::surface()->DrawSetTexture(m_nBGTexture);
				vgui::surface()->DrawTexturedRect( flXPos + m_flBGImageX, flYPos + m_flBGImageY, flXPos + m_flBGImageX + m_flBGImageWide, flYPos + m_flBGImageY + m_flBGImageTall );
			}

			wchar_t wBuf[20];

			if ( m_AccountDeltaItems[i].m_iAmount > 0 )
			{
				V_swprintf_safe( wBuf, L"+%d", m_AccountDeltaItems[i].m_iAmount );
			}
			else
			{
				V_swprintf_safe( wBuf, L"%d", m_AccountDeltaItems[i].m_iAmount );
			}

			// Append?
			if ( m_AccountDeltaItems[i].m_wzText[0] )
			{
				wchar_t wAppend[8] = { 0 };
				V_swprintf_safe( wAppend, L"%ls", m_AccountDeltaItems[i].m_wzText );
				V_wcscat_safe( wBuf, wAppend );
			}


			if ( m_AccountDeltaItems[i].m_bLargeFont )
			{
				vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
			}
			else
			{
				vgui::surface()->DrawSetTextFont( m_hDeltaItemFont );
			}

			// If we're supposed to have shadows, then draw the text as black and offset a bit first.
			// Things get ugly as we approach 0 alpha, so stop drawing the shadow a bit early.
			if ( m_AccountDeltaItems[i].m_bShadows && c[3] > 10 )
			{
				vgui::surface()->DrawSetTextPos( (int)flXPos + XRES(1), (int)flYPos + YRES(1) );
				vgui::surface()->DrawSetTextColor( COLOR_BLACK );
				vgui::surface()->DrawPrintText( wBuf, wcslen(wBuf), FONT_DRAW_NONADDITIVE );
			}

			vgui::surface()->DrawSetTextPos( (int)flXPos, (int)flYPos );
			vgui::surface()->DrawSetTextColor( c );
			vgui::surface()->DrawPrintText( wBuf, wcslen(wBuf), FONT_DRAW_NONADDITIVE );
		}
		else
		{
			m_AccountDeltaItems.Remove( i );
		}
	}
}

#ifdef STAGING_ONLY
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CBountyAccountPanel : public CHudAccountPanel
{
	DECLARE_CLASS_SIMPLE( CBountyAccountPanel, CHudAccountPanel );
public:
	CBountyAccountPanel( const char *pElementName ) : CHudAccountPanel( pElementName )
	{
	}

	virtual const char *GetResFileName( void ) { return "resource/UI/HudDamageAccount.res"; }

	//-----------------------------------------------------------------------------
	// Purpose:
	//-----------------------------------------------------------------------------
	void Paint( void )
	{
		if ( TFGameRules() && ( !TFGameRules()->IsBountyMode() || TFGameRules()->IsMannVsMachineMode() ) )
			return;

		C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pPlayer )
			return;

		int iScreenWide, iScreenTall;
		GetHudSize( iScreenWide, iScreenTall );
		int nX = iScreenWide * 0.06f;
		int nY = iScreenTall * 0.97f;

		Color cDPS( 25, 255, 25, 255 );
		vgui::surface()->DrawSetTextFont( m_hDeltaItemFontBig );
		vgui::surface()->DrawSetTextColor( cDPS );
		vgui::surface()->DrawSetTextPos( nX, nY );

		m_nCurrency = pPlayer->GetCurrency();
		wchar_t wCurrency[20];
		V_swprintf_safe( wCurrency, L"$%d", m_nCurrency );
		vgui::surface()->DrawPrintText( wCurrency, wcslen( wCurrency ), FONT_DRAW_NONADDITIVE );

		if ( pPlayer->GetCurrency() != m_nCurrency )
		{
			pPlayer->EmitSound( "Credits.Updated" );
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	bool ShouldDraw( void )
	{
		C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
		if ( !pPlayer || !pPlayer->IsAlive() )
			return false;

		if ( TFGameRules() && ( !TFGameRules()->IsBountyMode() || TFGameRules()->IsMannVsMachineMode() ) )
			return false;

		return CHudElement::ShouldDraw();
	}
private:

	int m_nCurrency;
};

DECLARE_HUDELEMENT( CBountyAccountPanel );
#endif // STAGING_ONLY