//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: HUD Target ID element
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_hud_target_id.h"
#include "c_tf_playerresource.h"
#include "iclientmode.h"
#include "vgui/ILocalize.h"
#include "c_baseobject.h"
#include "c_team.h"
#include "tf_gamerules.h"
#include "tf_hud_statpanel.h"
#if defined( REPLAY_ENABLED )
#include "replay/iclientreplaycontext.h"
#include "replay/ireplaymoviemanager.h"
#include "replay/ienginereplay.h"
#endif // REPLAY_ENABLED
#include "tf_weapon_bonesaw.h"
#include "sourcevr/isourcevirtualreality.h"
#include "tf_revive.h"
#include "tf_logic_robot_destruction.h"
#include "entity_capture_flag.h"
#include "vgui_avatarimage.h"

#include "VGuiMatSurface/IMatSystemSurface.h"
#include "renderparm.h"

#include "tf_dropped_weapon.h"
#include "econ/econ_item_description.h"

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

extern ConVar cl_hud_minmode;

DECLARE_HUDELEMENT( CMainTargetID );
DECLARE_HUDELEMENT( CSpectatorTargetID );
DECLARE_HUDELEMENT( CSecondaryTargetID );

using namespace vgui;

enum
{
	SPECTATOR_TARGET_ID_NORMAL = 0,
	SPECTATOR_TARGET_ID_BOTTOM_LEFT,
	SPECTATOR_TARGET_ID_BOTTOM_CENTER,
	SPECTATOR_TARGET_ID_BOTTOM_RIGHT,
};

void SpectatorTargetLocationCallback( IConVar *var, const char *oldString, float oldFloat )
{
	CSpectatorTargetID *pSpecTargetID = (CSpectatorTargetID *)GET_HUDELEMENT( CSpectatorTargetID );
	if ( pSpecTargetID )
	{
		pSpecTargetID->InvalidateLayout();
	}
}
ConVar tf_spectator_target_location( "tf_spectator_target_location", "0", FCVAR_ARCHIVE, "Determines the location of the spectator targetID panel.", true, 0, true, 3, SpectatorTargetLocationCallback );
ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar" );
ConVar tf_hud_target_id_alpha( "tf_hud_target_id_alpha", "100", FCVAR_ARCHIVE, "Alpha value of target id background, default 100" );
ConVar tf_hud_target_id_offset( "tf_hud_target_id_offset", "0", FCVAR_ARCHIVE, "RES file Y offset for target id" );
ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "2", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons.  1 = everyone, 2 = friends only." );

#ifdef STAGING_ONLY
ConVar tf_bountymode_showhealth( "tf_bountymode_showhealth", "0", FCVAR_ARCHIVE, "Show floating health icon over enemy players.  1 = show health, 2 = show health and level", true, 0, true, 2 );
#endif // STAGING_ONLY

bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer )
{
	if ( !pTarget || !pLocalPlayer )
		return false;

	if ( tf_hud_target_id_disable_floating_health.GetBool() )
		return false;

	if ( pTarget->IsHealthBarVisible() )
		return true;

	if ( !pTarget->IsPlayer() )
		return false;

	if ( pLocalPlayer->IsPlayerClass( TF_CLASS_SPY ) )
		return true;

	if ( pLocalPlayer->InSameTeam( pTarget ) )
		return true;

	if ( pLocalPlayer->InSameDisguisedTeam( pTarget ) )
		return true;

	

	int iSeeEnemyHealth = 0;
	CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalPlayer, iSeeEnemyHealth, see_enemy_health )
	if ( iSeeEnemyHealth )
		return true;
	
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTargetID::CTargetID( const char *pElementName ) :
	CHudElement( pElementName ), BaseClass( NULL, pElementName )
{
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );

	m_hFont = g_hFontTrebuchet24;
	m_flLastChangeTime = 0;
	m_iLastEntIndex = 0;
	m_nOriginalY = 0;
	m_bArenaPanelVisible = false;

	SetHiddenBits( HIDEHUD_MISCSTATUS );

	m_pTargetNameLabel = NULL;
	m_pTargetDataLabel = NULL;
	m_pBGPanel = NULL;
	m_pMoveableIcon = NULL;
	m_pMoveableSymbolIcon = NULL;
	m_pMoveableIconBG = NULL;
	m_pMoveableKeyLabel = NULL;
	m_pTargetHealth = new CTFSpectatorGUIHealth( this, "SpectatorGUIHealth" );
	m_pTargetAmmoIcon = NULL;
	m_pTargetKillStreakIcon = NULL;
	m_bLayoutOnUpdate = false;

	m_pFloatingHealthIcon = NULL;
	m_iLastScannedEntIndex = 0;
	m_pAvatarImage = NULL;

	RegisterForRenderGroup( "mid" );
	RegisterForRenderGroup( "commentary" );

	m_iRenderPriority = 5;

	ListenForGameEvent( "show_class_layout" );
	RegisterForRenderGroup( "arena_target_id" );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::LevelShutdown( void )
{
	if ( m_pFloatingHealthIcon )
	{
		m_pFloatingHealthIcon->MarkForDeletion();
		m_pFloatingHealthIcon = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Setup
//-----------------------------------------------------------------------------
void CTargetID::Reset( void )
{
	m_pTargetHealth->Reset();

	vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
	if ( pScheme )
	{
		m_LabelColorDefault = pScheme->GetColor( "Label.TextColor", Color( 255, 255, 255, 255 ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::FireGameEvent( IGameEvent * event )
{
	const char *eventName = event->GetName();

	if ( FStrEq( "show_class_layout", eventName ) )
	{
		if ( TFGameRules() && TFGameRules()->IsInArenaMode() && GetLocalPlayerTeam() > LAST_SHARED_TEAM )
		{
			m_bArenaPanelVisible = event->GetBool( "show", false );
		}
		else
		{
			m_bArenaPanelVisible = false;
		}

		InvalidateLayout( true );
	}
}

//-----------------------------------------------------------------------------
bool CTargetID::DrawHealthIcon() 
{
	C_BaseEntity *pEnt = cl_entitylist->GetEnt( GetTargetIndex() );
	if ( pEnt && pEnt->IsBaseObject() )
		return true;

	if ( tf_hud_target_id_disable_floating_health.GetBool() )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Find out which player to pull an avatar image from.  pTFPlayer is the player under the crosshair.
//-----------------------------------------------------------------------------
C_TFPlayer *CTargetID::GetTargetForSteamAvatar( C_TFPlayer *pTFPlayer )
{
	if ( !tf_hud_target_id_show_avatars.GetBool() )
		return NULL;

	if ( !pTFPlayer || ( g_TF_PR && g_TF_PR->IsFakePlayer( pTFPlayer->entindex() ) ) )
		return NULL;

	C_TFPlayer *pTFLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pTFLocalPlayer )
		return NULL;

	// Health icon inside the panel (too busy - figure this out later)
	if ( DrawHealthIcon() )
		return NULL;

	// Save room when healing or being healed
	if ( pTFLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && pTFLocalPlayer->MedicGetHealTarget() == pTFPlayer )
		return NULL;
	
	C_TFPlayer *pTFHealer = NULL;
	float flHealerChargeLevel = -1.f;
	pTFLocalPlayer->GetHealer( &pTFHealer, &flHealerChargeLevel );
	if ( pTFHealer && pTFHealer->entindex() == m_iTargetEntIndex )
		return NULL;

	if ( pTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
	{
		C_TFPlayer *pDisguiseTarget = ToTFPlayer( pTFPlayer->m_Shared.GetDisguiseTarget() );
		if ( pDisguiseTarget && ( pTFLocalPlayer->InSameTeam( pDisguiseTarget ) || pDisguiseTarget == pTFLocalPlayer ) )
		{
			// Bots don't (currently) have avatars.
			if ( pDisguiseTarget->IsBot() )
				return NULL;

			if ( tf_hud_target_id_show_avatars.GetInt() == 2 && !pTFLocalPlayer->IsPlayerOnSteamFriendsList( pDisguiseTarget ) )
				return NULL;

			return pDisguiseTarget;
		}
	}

	if ( pTFLocalPlayer->IsPlayerOnSteamFriendsList( pTFPlayer ) )
		return pTFPlayer;

	if ( tf_hud_target_id_show_avatars.GetInt() == 1 )
		return pTFPlayer;

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::ApplySchemeSettings( vgui::IScheme *scheme )
{
	LoadControlSettings( "resource/UI/TargetID.res" );

	BaseClass::ApplySchemeSettings( scheme );

	m_pTargetNameLabel = dynamic_cast<Label *>(FindChildByName("TargetNameLabel"));
	m_pTargetDataLabel = dynamic_cast<Label *>(FindChildByName("TargetDataLabel"));
	m_pBGPanel =  dynamic_cast<CTFImagePanel *> ( FindChildByName("TargetIDBG") );
	m_pMoveableSubPanel = dynamic_cast<vgui::EditablePanel *> ( FindChildByName("MoveableSubPanel") );
	if ( m_pMoveableSubPanel )
	{
		m_pMoveableIcon = dynamic_cast<CIconPanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableIcon") );
		m_pMoveableSymbolIcon = dynamic_cast<vgui::ImagePanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableSymbolIcon") );
		m_pMoveableIconBG = dynamic_cast<CIconPanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableIconBG") );
		m_pMoveableKeyLabel = dynamic_cast<Label *>( m_pMoveableSubPanel->FindChildByName("MoveableKeyLabel") );
	}
	m_hFont = scheme->GetFont( "TargetID", true );
	m_pTargetAmmoIcon = dynamic_cast<vgui::ImagePanel *>( FindChildByName( "AmmoIcon" ) );
	m_pTargetKillStreakIcon = dynamic_cast<vgui::ImagePanel *>( FindChildByName( "KillStreakIcon" ) );
	m_pAvatarImage = dynamic_cast< CAvatarImagePanel* >( FindChildByName( "AvatarImage" ) );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	m_iRenderPriority = inResourceData->GetInt( "priority" );

	int x;
	GetPos( x, m_nOriginalY );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTargetID::GetRenderGroupPriority( void )
{
	return m_iRenderPriority;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::UpdateFloatingHealthIconVisibility( bool bVisible )
{
	if ( m_pFloatingHealthIcon && ( m_pFloatingHealthIcon->IsVisible() != bVisible ) )
	{
		m_pFloatingHealthIcon->SetVisible( bVisible );
	}
}

//-----------------------------------------------------------------------------
// Purpose: clear out string etc between levels
//-----------------------------------------------------------------------------
void CTargetID::VidInit()
{
	CHudElement::VidInit();

	m_flLastChangeTime = 0;
	m_iLastEntIndex = 0;
}

bool CTargetID::IsValidIDTarget( int nEntIndex, float flOldTargetRetainFOV, float &flNewTargetRetainFOV )
{
	bool bReturn = false;
	flNewTargetRetainFOV = 0.0f;

	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
		return false;

#ifdef STAGING_ONLY
	if ( pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) )
		return false;
#endif // STAGING_ONLY

	if ( nEntIndex )
	{
		C_BaseEntity *pEnt = cl_entitylist->GetEnt( nEntIndex );
		if ( pEnt )
		{
			Vector vDiff = pEnt->EyePosition() - pLocalTFPlayer->EyePosition();
			float flDist;
			flDist = VectorNormalize( vDiff );

			if ( flOldTargetRetainFOV != 0.0f )
			{
				// It has a FOV that maintains previous targets
				Vector vForward;
				pLocalTFPlayer->EyeVectors( &vForward );

				float fAngle = 1.0f - vDiff.Dot( vForward );
				fAngle = RemapVal( fAngle, 0.0f, 1.0f, 0.0f, 90.0f );

				if ( fAngle > flOldTargetRetainFOV )
				{
					return false;
				}
			}

			C_TFPlayer *pPlayer = ToTFPlayer( pEnt );

			int iHideEnemyHealth = 0;
			CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalTFPlayer, iHideEnemyHealth, hide_enemy_health );

			bool bInSameTeam = pLocalTFPlayer->InSameDisguisedTeam( pEnt );	
			bool bSpy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && iHideEnemyHealth == 0;

			if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
			{
				// We don't want to show health bars to the spy in MVM because it's distracting
				bSpy = false;

				// Are we disguised as the enemy?
				if ( pLocalTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pLocalTFPlayer->m_Shared.GetDisguiseTeam() != pLocalTFPlayer->GetTeamNumber() )
				{
					// Get the target's apparent team
					int iTheirApparentTeam = pEnt->GetTeamNumber();
					if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
					{
						iTheirApparentTeam = pPlayer->m_Shared.GetDisguiseTeam();
					}

					// Are we disguised as they appear?
					if ( pLocalTFPlayer->m_Shared.GetDisguiseTeam() == iTheirApparentTeam )
					{
						// Don't show the health
						bInSameTeam = false;
					}
				}
			}

			bool bSpectator = pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR;
			int iSeeEnemyHealth = 0;
			bool bStealthed = false;
			bool bHealthBarVisible = ShouldHealthBarBeVisible( pEnt, pLocalTFPlayer );
			bool bShow = bHealthBarVisible;

			if ( pPlayer )
			{
				if ( pPlayer->m_Shared.IsStealthed() )
				{
					bStealthed = true;
					bHealthBarVisible = false;
					bShow = false;
				}

				if ( !bStealthed )
				{
					CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalTFPlayer, iSeeEnemyHealth, see_enemy_health );
				}

				bool bMaintainInFOV = !pLocalTFPlayer->InSameTeam( pEnt );

				if ( bHealthBarVisible )
				{
					bool bEnemyPlayer = pPlayer->GetTeamNumber() != pLocalTFPlayer->GetTeamNumber();
					bool bEnemyMiniBoss = pPlayer->IsMiniBoss() && bEnemyPlayer;
					bShow = bEnemyMiniBoss;
#ifdef STAGING_ONLY
					bShow |= TFGameRules() && TFGameRules()->IsBountyMode() && tf_bountymode_showhealth.GetInt() && bEnemyPlayer;
#endif // STAGING_ONLY

					if ( bShow )
					{
						bMaintainInFOV = false;

						// Minibosses keep the health indicator up within a small FOV until a different valid target is selected
						// The FOV needs to grow exponentially when a target is getting near
						if ( bEnemyMiniBoss )
						{
							bMaintainInFOV = true;
						}
					}
				}

				if ( bMaintainInFOV )
				{
					const float flMaxDist = 800.0f;
					float fInterp = RemapVal( flMaxDist - MIN( flDist, flMaxDist ), 0.0f, flMaxDist, 0.0f, 1.0f );
					fInterp *= fInterp;
					flNewTargetRetainFOV = fInterp * 13.0f + 0.75f;
				}

				bReturn = ( bSpectator || pLocalTFPlayer->InSameTeam( pEnt ) || ( ( bInSameTeam || bSpy || iSeeEnemyHealth ) && !bStealthed ) );
			}


			if ( bShow || bHealthBarVisible )
			{
				// See if we're re-targeting our previous
				if ( m_pFloatingHealthIcon )
				{
					if ( m_pFloatingHealthIcon->GetEntity() && m_pFloatingHealthIcon->GetEntity() == pEnt )
					{
						UpdateFloatingHealthIconVisibility( true );
					}
					else
					{
						// New target - clear previous
						m_pFloatingHealthIcon->MarkForDeletion();
						m_pFloatingHealthIcon = NULL;
					}
				}

				//Recreate the floating health icon if there isn't one, we're not a spectator, and 
				// we're not a spy or this was a robot from Robot Destruction-Mode
				if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && !DrawHealthIcon() )
				{
					m_pFloatingHealthIcon = CFloatingHealthIcon::AddFloatingHealthIcon( pEnt );
				}
			}
			else if ( pEnt->IsBaseObject() && ( bInSameTeam || bSpy ) )
			{
				bReturn = true;
			}
			else if ( pEnt->IsVisibleToTargetID() )
			{
				bReturn = true;
			}
			else
			{
				UpdateFloatingHealthIconVisibility( false );
			}
		}
	}

	return bReturn;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTargetID::ShouldDraw( void )
{
	if ( !CHudElement::ShouldDraw() )
	{
		UpdateFloatingHealthIconVisibility( false );
		return false;
	}

	if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
	{
		UpdateFloatingHealthIconVisibility( false );
		return false;
	}

	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
	{
		UpdateFloatingHealthIconVisibility( false );
		return false;
	}

	if ( pLocalTFPlayer->IsTaunting() )
	{
		UpdateFloatingHealthIconVisibility( false );
		return false;
	}

	// Get our target's ent index
	m_iTargetEntIndex = CalculateTargetIndex(pLocalTFPlayer);
	if ( !m_iTargetEntIndex )
	{
		if ( m_flTargetRetainFOV == 0.0f )
		{
			// Check to see if we should clear our ID
			if ( m_flLastChangeTime && ( gpGlobals->curtime > m_flLastChangeTime ) )
			{
				m_flLastChangeTime = 0;
				m_iLastEntIndex = 0;
			}
			else
			{
				// Keep re-using the old one
				m_iTargetEntIndex = m_iLastEntIndex;
			}
		}

		// If we're showing a floating health icon, and no longer have a target,
		// hide it and see if it's the same entity next time
		UpdateFloatingHealthIconVisibility( false );
	}
	else
	{
		m_flLastChangeTime = gpGlobals->curtime;

		if ( m_iTargetEntIndex != m_iLastScannedEntIndex )
		{
			// If we switched to another, valid target for a floating health icon, recreate it on the next pass
			if ( m_pFloatingHealthIcon )
			{
				m_pFloatingHealthIcon->MarkForDeletion();
				m_pFloatingHealthIcon = NULL;
			}
			m_iLastScannedEntIndex = m_iTargetEntIndex;
		}
	}

	float flTargetRetainFOV = 0.0f;
	bool bReturn = IsValidIDTarget( m_iTargetEntIndex, 0.0f, flTargetRetainFOV );

	if ( !bReturn )
	{
		m_iLastEntIndex = 0;
	}
	else
	{
		if ( !IsVisible() || (m_iTargetEntIndex != m_iLastEntIndex) )
		{
			m_iLastEntIndex = m_iTargetEntIndex;
			m_bLayoutOnUpdate = true;
			m_flTargetRetainFOV = flTargetRetainFOV;
			if ( m_pAvatarImage )
			{
				m_pAvatarImage->SetVisible( false );
			}
		}

		UpdateID();
	}

	return bReturn;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::PerformLayout( void )
{
	int iXIndent = XRES(5);
	int iXPostdent = XRES(10);
	int iWidth = iXIndent + iXPostdent;
	if ( DrawHealthIcon() )
	{
		iWidth += m_pTargetHealth->GetWide();
	}
	if ( m_pAvatarImage && m_pAvatarImage->IsVisible() )
	{
		iWidth += m_pAvatarImage->GetWide() + XRES( 2 );
	}

	int iTextW, iTextH;
	int iDataW, iDataH;

	if ( m_pTargetNameLabel && m_pTargetDataLabel )
	{
		m_pTargetNameLabel->GetContentSize( iTextW, iTextH );
		m_pTargetDataLabel->GetContentSize( iDataW, iDataH );
		iWidth += MAX(iTextW,iDataW);

		if ( m_pBGPanel )
		{
			m_pBGPanel->SetSize( iWidth, GetTall() );
		}

		int x1 = 0, y1 = 0;
		int x2 = 0, y2 = 0;
		int x3 = 0, y3 = 0;
		m_pTargetNameLabel->GetPos( x1, y1 );
		m_pTargetDataLabel->GetPos( x2, y2 );
		if ( m_pTargetKillStreakIcon )
		{
			m_pTargetKillStreakIcon->GetPos( x3, y3 );
		}
		
		int iWideExtra = 0;
		if ( DrawHealthIcon() )
		{
			iWideExtra += m_pTargetHealth->GetWide();
		}
		if ( m_pAvatarImage && m_pAvatarImage->IsVisible() )
		{
			iWideExtra += m_pAvatarImage->GetWide() + XRES( 4 );
		}

		int nBuffer = ( m_pAvatarImage && m_pAvatarImage->IsVisible() ) ? 6 : 8;
		m_pTargetNameLabel->SetPos( XRES( nBuffer ) + iWideExtra, y1 );
		m_pTargetDataLabel->SetPos( XRES( nBuffer ) + iWideExtra, y2 );

		if ( m_pTargetKillStreakIcon )
		{
			int nKSBuffer = ( cl_hud_minmode.GetBool() ) ? 6 : 9;
			m_pTargetKillStreakIcon->SetPos( XRES( nKSBuffer ) + iWideExtra, y3 );
		}
	}

	// Put the moveable icon to the right hand of our panel
	if ( m_pMoveableSubPanel && m_pMoveableSubPanel->IsVisible() )
	{
		if ( m_pMoveableKeyLabel && m_pMoveableIcon && m_pMoveableSymbolIcon && m_pMoveableIconBG )
		{
			m_pMoveableKeyLabel->SizeToContents();

			int iIndent = XRES(4);
			int iMoveWide = MAX( XRES(16) + m_pMoveableKeyLabel->GetWide() + iIndent, (m_pMoveableIcon->GetWide()) + iIndent + XRES(8) );
			m_pMoveableKeyLabel->SetWide( iMoveWide );
			m_pMoveableSubPanel->SetSize( iMoveWide, GetTall() );
			m_pMoveableSubPanel->SetPos( iWidth - iIndent, 0 );

			int x,y;
			m_pMoveableKeyLabel->GetPos( x, y );
			m_pMoveableSymbolIcon->SetPos( (iMoveWide - m_pMoveableSymbolIcon->GetWide()) * 0.5, y - m_pMoveableSymbolIcon->GetTall() );
			m_pMoveableSymbolIcon->GetPos( x, y );
			m_pMoveableIcon->SetPos( (iMoveWide - m_pMoveableIcon->GetWide()) * 0.5, y - m_pMoveableIcon->GetTall() );
			m_pMoveableIconBG->SetSize( m_pMoveableSubPanel->GetWide(), m_pMoveableSubPanel->GetTall() );
		}
	}

	if ( m_pMoveableSubPanel && m_pMoveableSubPanel->IsVisible() )
	{
		// Now add our extra width to the total size
		iWidth += m_pMoveableSubPanel->GetWide();
	}

	SetSize( iWidth, GetTall() );

	int nOffset = m_bArenaPanelVisible ? YRES (120) : 0; // HACK: move the targetID up a bit so it won't overlap the panel
	if( UseVR() )
	{
		SetPos( ScreenWidth() - iWidth - m_iXOffset,  m_nOriginalY - nOffset + YRES( tf_hud_target_id_offset.GetInt() ) );
	}
	else
	{
		SetPos( (ScreenWidth() - iWidth) * 0.5,  m_nOriginalY - nOffset + YRES( tf_hud_target_id_offset.GetInt() ) );
	}
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	CTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer ) 
{ 
	int iIndex = pLocalTFPlayer->GetIDTarget(); 

	// If our target entity is already in our secondary ID, don't show it in primary.
	CSecondaryTargetID *pSecondaryID = GET_HUDELEMENT( CSecondaryTargetID );
	if ( pSecondaryID && pSecondaryID != this && pSecondaryID->GetTargetIndex() == iIndex )
	{
		iIndex = 0;
	}

	return iIndex;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTargetID::UpdateID( void )
{
	wchar_t sIDString[ MAX_ID_STRING ] = L"";
	wchar_t sDataString[ MAX_ID_STRING ] = L"";

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

	// Default the labels' colors
	Color colorName = m_LabelColorDefault;
	Color colorData = m_LabelColorDefault;

	// Get our target's ent index
	// Is this an entindex sent by the server?
	if ( m_iTargetEntIndex )
	{
		C_BaseEntity *pEnt = cl_entitylist->GetEnt( m_iTargetEntIndex );
		if ( !pEnt )
			return;

		bool bShowHealth = false;
		float flHealth = 0;
		float flMaxHealth = 1;
		int iMaxBuffedHealth = 0;
		int iTargetTeam = pEnt->GetTeamNumber();
		const char *pszActionCommand = NULL;
		const char *pszActionIcon = NULL;

		m_pTargetHealth->SetBuilding( false );
		m_pTargetHealth->SetLevel( -1 );

		// Some entities we always want to check, cause the text may change
		// even while we're looking at it
		// Is it a player?
		if ( IsPlayerIndex( m_iTargetEntIndex ) )
		{
			const char *printFormatString = NULL;
			wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ];
			bool bDisguisedTarget = false;
			bool bDisguisedEnemy = false;

			C_TFPlayer *pPlayer = static_cast<C_TFPlayer*>( pEnt );
			if ( !pPlayer )
				return;

			C_TFPlayer *pDisguiseTarget = NULL;
			g_pVGuiLocalize->ConvertANSIToUnicode( pPlayer->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );

			// determine if the target is a disguised spy (either friendly or enemy)
			if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && // they're disguised
				//!pPlayer->m_Shared.InCond( TF_COND_DISGUISING ) && // they're not in the process of disguising
				!pPlayer->m_Shared.IsStealthed() ) // they're not cloaked
			{
				bDisguisedTarget = true;
				pDisguiseTarget = ToTFPlayer( pPlayer->m_Shared.GetDisguiseTarget() );

				if ( pLocalTFPlayer->InSameTeam( pEnt ) == false )
				{
					iTargetTeam = pPlayer->m_Shared.GetDisguiseTeam();
				}
			}

			if ( bDisguisedTarget )
			{
				// is the target a disguised enemy spy?
				if ( pPlayer->IsEnemyPlayer() )
				{
					if ( pDisguiseTarget )
					{
						bDisguisedEnemy = true;
						// change the player name
						g_pVGuiLocalize->ConvertANSIToUnicode( pDisguiseTarget->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );
						// change the team  / team color
					}
				}
			}

			bool bInSameTeam = pLocalTFPlayer->InSameDisguisedTeam( pEnt );
			bool bSpy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_SPY );
			bool bMedic = pLocalTFPlayer->IsPlayerClass( TF_CLASS_MEDIC );
			bool bHeavy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS );

			// See if the player wants to fill in the data string
			bool bIsAmmoData = false;
			bool bIsKillStreakData = false;
			pPlayer->GetTargetIDDataString( bDisguisedTarget, sDataString, sizeof(sDataString), bIsAmmoData, bIsKillStreakData );
			if ( pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR || bInSameTeam || bSpy || bDisguisedEnemy || bMedic || bHeavy )
			{
				printFormatString = "#TF_playerid_sameteam";
				bShowHealth = true;
			}
			else if ( pLocalTFPlayer->m_Shared.GetState() == TF_STATE_DYING )
			{
				// We're looking at an enemy who killed us.
				printFormatString = "#TF_playerid_diffteam";
				bShowHealth = true;
			}			

			if ( bShowHealth )
			{
				if ( g_TF_PR )
				{
					if ( bDisguisedEnemy )
					{
						flHealth = (float)pPlayer->m_Shared.GetDisguiseHealth();
						flMaxHealth = (float)pPlayer->m_Shared.GetDisguiseMaxHealth();
						iMaxBuffedHealth = pPlayer->m_Shared.GetDisguiseMaxBuffedHealth();
					}
					else
					{
						flHealth = (float)pPlayer->GetHealth();
						flMaxHealth = g_TF_PR->GetMaxHealth( m_iTargetEntIndex );
						iMaxBuffedHealth = pPlayer->m_Shared.GetMaxBuffedHealth();
					}
				}
				else
				{
					bShowHealth = false;
				}
			}

			if ( printFormatString )
			{
				const wchar_t *pszPrepend = GetPrepend();
				if ( !pszPrepend || !pszPrepend[0] )
				{
					pszPrepend = L"";
				}
				g_pVGuiLocalize->ConstructString_safe( sIDString, g_pVGuiLocalize->Find(printFormatString), 2, pszPrepend, wszPlayerName );
			}

			// Show target's clip state to attached medics
			bool bShowClipInfo = bIsAmmoData &&
								 sDataString[0] && 
								 ToTFPlayer( pLocalTFPlayer->MedicGetHealTarget() ) == pPlayer;
			if ( m_pTargetAmmoIcon && m_pTargetAmmoIcon->IsVisible() != bShowClipInfo )
			{
				m_pTargetAmmoIcon->SetVisible( bShowClipInfo );
			}

			bool bShowKillStreak = bIsKillStreakData && sDataString[0];
			if ( m_pTargetKillStreakIcon && m_pTargetKillStreakIcon->IsVisible() != bShowKillStreak )
			{
				m_pTargetKillStreakIcon->SetVisible( bShowKillStreak );
			}
		}
		else	
		{
			// see if it is an object
			if ( pEnt->IsBaseObject() )
			{
				C_BaseObject *pObj = assert_cast<C_BaseObject *>( pEnt );

				pObj->GetTargetIDString( sIDString, sizeof(sIDString), false );
				pObj->GetTargetIDDataString( sDataString, sizeof(sDataString) );
				bShowHealth = true;
				flHealth = pObj->GetHealth();
				flMaxHealth = pObj->GetMaxHealth();
				m_pTargetHealth->SetBuilding( true );
				
				if ( m_pTargetKillStreakIcon )
				{
					m_pTargetKillStreakIcon->SetVisible( false );
				}

				// Switch the icon to the right object
				if ( pObj->GetBuilder() == pLocalTFPlayer )
				{
					int iObj = pObj->GetType();

					if ( iObj >= OBJ_DISPENSER && iObj <= OBJ_SENTRYGUN )
					{
						if ( pLocalTFPlayer->CanPickupBuilding(pObj) )
						{
							pszActionCommand = "+attack2";
						}

						
						switch ( iObj )
						{
						default:
						case OBJ_DISPENSER:
							pszActionIcon = "obj_status_dispenser";
							break;
						case OBJ_TELEPORTER:
							{
								pszActionIcon = (pObj->GetObjectMode() == MODE_TELEPORTER_ENTRANCE) ? "obj_status_tele_entrance" : "obj_status_tele_exit";
							}
							break;
						case OBJ_SENTRYGUN:
							{
								int iLevel = pObj->GetUpgradeLevel();
								if ( iLevel == 3 )
								{
									pszActionIcon =  "obj_status_sentrygun_3";
								}
								else
								{
									pszActionIcon = (iLevel == 2) ? "obj_status_sentrygun_2" : "obj_status_sentrygun_1";
								}
							}
							break;
						}
					}
				}
			}
			// Generic
			else if ( pEnt->IsVisibleToTargetID() )
			{
				CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag * >( pEnt );
				if ( pFlag && pFlag->GetPointValue() > 0 )
				{
					bShowHealth = false;
					g_pVGuiLocalize->ConvertANSIToUnicode( CFmtStr("%d Points", pFlag->GetPointValue() ), sIDString, sizeof(sIDString) );
				}
				else
				{
					CTFDroppedWeapon *pDroppedWeapon = dynamic_cast< CTFDroppedWeapon * >( pEnt );
					if ( pDroppedWeapon )
					{
						CEconItemView* pDroppedEconItem = pDroppedWeapon->GetItem();
						if ( pLocalTFPlayer->GetDroppedWeaponInRange() != NULL )
						{
							pszActionIcon = "obj_weapon_pickup";
							pszActionCommand = "+use_action_slot_item";
						}
	
						if ( FStrEq( pDroppedEconItem->GetStaticData()->GetItemClass(), "tf_weapon_medigun" ) )
						{
							wchar_t wszChargeLevel[10];
							_snwprintf( wszChargeLevel, ARRAYSIZE( wszChargeLevel ) - 1, L"%.0f", pDroppedWeapon->GetChargeLevel() * 100 );
							wszChargeLevel[ARRAYSIZE( wszChargeLevel ) - 1] = '\0';

							g_pVGuiLocalize->ConstructString_safe( sIDString, L"%s1 (%s2%)", 2, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pDroppedEconItem->GetItemDefinition(), pDroppedEconItem->GetItemQuality() ).GetFullName(), wszChargeLevel );
						}
						else
						{
							g_pVGuiLocalize->ConstructString_safe( sIDString, L"%s1", 1, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pDroppedEconItem->GetItemDefinition(), pDroppedEconItem->GetItemQuality() ).GetFullName() );
						}

						locchar_t wszPlayerName [128];
						CBasePlayer *pOwner = GetPlayerByAccountID( pDroppedEconItem->GetAccountID() );
						// Bots will not work here, so don't fill this out.
						if ( pOwner )
						{
							g_pVGuiLocalize->ConvertANSIToUnicode( pOwner->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );
							g_pVGuiLocalize->ConstructString_safe( sDataString, g_pVGuiLocalize->Find( "#TF_WhoDropped" ), 1, wszPlayerName );

							// Get the rarity color
							vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
							if ( pScheme )
							{
								const char* pszColorName = GetItemSchema()->GetRarityColor( pDroppedEconItem->GetItemDefinition()->GetRarity() );
								pszColorName = pszColorName ? pszColorName : "TanLight";
								colorName = pScheme->GetColor( pszColorName, Color( 255, 255, 255, 255 ) );
							}
						}
					}
					else if ( pLocalTFPlayer->InSameTeam( pEnt ) )
					{
						bShowHealth = true;
						flHealth = pEnt->GetHealth();
						flMaxHealth = pEnt->GetMaxHealth();
						iMaxBuffedHealth = pEnt->GetMaxHealth();

						// Display respawn timer on revive markers by hacking bountymode's player level display
						if ( !pEnt->IsPlayer() )
						{
							CTFReviveMarker *pMarker = dynamic_cast< CTFReviveMarker* >( pEnt );
							if ( pMarker && pMarker->GetOwner() )
							{
								float flRespawn = TFGameRules()->GetNextRespawnWave( pMarker->GetTeamNumber(), pMarker->GetOwner() ) - gpGlobals->curtime;
								m_pTargetHealth->SetLevel( (int)flRespawn );

								g_pVGuiLocalize->ConvertANSIToUnicode( pMarker->GetOwner()->GetPlayerName(), sIDString, sizeof(sIDString) );
							}
						}
					}
				}
			}
		}

		// Setup health icon
		if ( !pEnt->IsAlive() && ( pEnt->IsPlayer() || pEnt->IsBaseObject() ) )
		{
			flHealth = 0;	// fixup for health being 1 when dead
		}

		m_pTargetHealth->SetHealth( flHealth, flMaxHealth, iMaxBuffedHealth );
		m_pTargetHealth->SetVisible( DrawHealthIcon() );
		if ( m_pMoveableSubPanel )
		{
			bool bShowActionKey = pszActionCommand != NULL;
			if ( m_pMoveableSubPanel->IsVisible() != bShowActionKey )
			{
				m_pMoveableSubPanel->SetVisible( bShowActionKey );
				m_bLayoutOnUpdate = true;
			}

			if ( m_pMoveableSubPanel->IsVisible() )
			{
				const char *pBoundKey = engine->Key_LookupBinding( pszActionCommand );
				m_pMoveableSubPanel->SetDialogVariable( "movekey", pBoundKey );
			}

			if ( m_pMoveableIcon )
			{
				if ( pszActionIcon )
				{
					m_pMoveableIcon->SetIcon( pszActionIcon );
				}
				m_pMoveableIcon->SetVisible( pszActionIcon != NULL );
			}
		}

		if ( m_pAvatarImage && pEnt->IsPlayer() )
		{
			C_BasePlayer *pTFTarget = GetTargetForSteamAvatar( ToTFPlayer( pEnt ) );

			bool bShowAvatar = ( pTFTarget ) ? true : false;
			if ( m_pAvatarImage->IsVisible() != bShowAvatar )
			{
				m_pAvatarImage->SetVisible( bShowAvatar );
				if ( bShowAvatar )
				{
					m_pAvatarImage->SetPlayer( pTFTarget );
					m_pAvatarImage->SetShouldDrawFriendIcon( false );
					m_pAvatarImage->SetAlpha( tf_hud_target_id_alpha.GetInt() );
				}
			}
		}

		if ( m_pTargetNameLabel && m_pTargetDataLabel )
		{
			int iNameW, iDataW, iIgnored;
			m_pTargetNameLabel->GetContentSize( iNameW, iIgnored );
			m_pTargetDataLabel->GetContentSize( iDataW, iIgnored );

			// Target name
			if ( sIDString[0] )
			{
				sIDString[ ARRAYSIZE(sIDString)-1 ] = '\0';
				m_pTargetNameLabel->SetVisible(true);
				m_pTargetNameLabel->SetFgColor( colorName );

				// TODO: Support	if( hud_centerid.GetInt() == 0 )
				SetDialogVariable( "targetname", sIDString );
			}
			else
			{
				m_pTargetNameLabel->SetVisible(false);
				m_pTargetNameLabel->SetText("");
			}

			// Extra target data
			if ( sDataString[0] )
			{
				sDataString[ ARRAYSIZE(sDataString)-1 ] = '\0';
				m_pTargetDataLabel->SetVisible(true);
				m_pTargetDataLabel->SetFgColor( colorData );

				SetDialogVariable( "targetdata", sDataString );
			}
			else
			{
				m_pTargetDataLabel->SetVisible(false);
				m_pTargetDataLabel->SetText("");
			}

			int iPostNameW, iPostDataW;
			m_pTargetNameLabel->GetContentSize( iPostNameW, iIgnored );
			m_pTargetDataLabel->GetContentSize( iPostDataW, iIgnored );

			if ( m_pBGPanel )
			{
				m_pBGPanel->SetBGTeam( iTargetTeam );
				m_pBGPanel->UpdateBGImage();
				m_pBGPanel->SetAlpha( tf_hud_target_id_alpha.GetInt() );
			}

			if ( m_bLayoutOnUpdate || (iPostDataW != iDataW) || (iPostNameW != iNameW) )
			{
				InvalidateLayout( true );
				m_bLayoutOnUpdate = false;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CSecondaryTargetID::CSecondaryTargetID( const char *pElementName ) : CTargetID( pElementName )
{
	m_wszPrepend[0] = '\0';

	RegisterForRenderGroup( "mid" );

	m_bWasHidingLowerElements = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CSecondaryTargetID::ShouldDraw( void )
{
	bool bDraw = BaseClass::ShouldDraw();

	if ( bDraw )
	{
		if ( !m_bWasHidingLowerElements )
		{
			HideLowerPriorityHudElementsInGroup( "mid" );
			m_bWasHidingLowerElements = true;
		}
	}
	else 
	{
		if ( m_bWasHidingLowerElements )
		{
			UnhideLowerPriorityHudElementsInGroup( "mid" );
			m_bWasHidingLowerElements = false;
		}
	}

	return bDraw;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CSecondaryTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer )
{
	// If we're a medic & we're healing someone, target him.
	CBaseEntity *pHealTarget = pLocalTFPlayer->MedicGetHealTarget();
	if ( pHealTarget )
	{
		if ( pHealTarget->entindex() != m_iTargetEntIndex )
		{
			g_pVGuiLocalize->ConstructString_safe( m_wszPrepend, g_pVGuiLocalize->Find("#TF_playerid_healtarget" ), 0 );
		}
		return pHealTarget->entindex();
	}

	// If we have a healer, target him.
	C_TFPlayer *pHealer;
	float flHealerChargeLevel;
	pLocalTFPlayer->GetHealer( &pHealer, &flHealerChargeLevel );
	if ( pHealer )
	{
		if ( pHealer->entindex() != m_iTargetEntIndex )
		{
			g_pVGuiLocalize->ConstructString_safe( m_wszPrepend, g_pVGuiLocalize->Find("#TF_playerid_healer" ), 0 );
		}
		return pHealer->entindex();
	}

	if ( m_iTargetEntIndex )
	{
		m_wszPrepend[0] = '\0';
	}
	return 0;
}

// Separately declared versions of the hud element for alive and dead so they
// can have different positions

bool CMainTargetID::ShouldDraw( void )
{
	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
		return false;

	if ( pLocalTFPlayer->GetObserverMode() > OBS_MODE_NONE )
		return false;

	return BaseClass::ShouldDraw();
}

bool CSpectatorTargetID::ShouldDraw( void )
{
	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
		return false;

	if ( pLocalTFPlayer->GetObserverMode() <= OBS_MODE_NONE ||
		 pLocalTFPlayer->GetObserverMode() == OBS_MODE_FREEZECAM )
		return false;

	if ( pLocalTFPlayer->m_bIsCoaching )
	{
		return false;
	}

	// Hide panel for freeze-cam screenshot?
	extern bool IsTakingAFreezecamScreenshot();
	extern ConVar hud_freezecamhide;
	
	if ( IsTakingAFreezecamScreenshot() && hud_freezecamhide.GetBool() )
		return false;

#if defined( REPLAY_ENABLED )
	if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
		return false;
#endif

	return BaseClass::ShouldDraw();
}

int	CSpectatorTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer ) 
{ 
	int iIndex = BaseClass::CalculateTargetIndex( pLocalTFPlayer );

#if defined( REPLAY_ENABLED )
	// Don't execute this if we're watching a replay
	if ( ( !g_pEngineClientReplay || !g_pEngineClientReplay->IsPlayingReplayDemo() ) && pLocalTFPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalTFPlayer->GetObserverTarget() )
#else
	if ( pLocalTFPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalTFPlayer->GetObserverTarget() )
#endif
	{
		iIndex = pLocalTFPlayer->GetObserverTarget()->entindex();
	}

	return iIndex;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSpectatorTargetID::ApplySchemeSettings( vgui::IScheme *scheme )
{
	BaseClass::ApplySchemeSettings( scheme );

	if ( m_pBGPanel )
	{
		m_pBGPanel->SetVisible( false );
	}

	m_pBGPanel_Spec_Blue = FindChildByName("TargetIDBG_Spec_Blue");
	m_pBGPanel_Spec_Red = FindChildByName("TargetIDBG_Spec_Red");

	if ( m_pBGPanel_Spec_Blue )
	{
		m_pBGPanel_Spec_Blue->SetVisible( true );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSpectatorTargetID::PerformLayout( void )
{
	int iXIndent = XRES(5);
	int iXPostdent = XRES(10);
	int iWidth = m_pTargetHealth->GetWide() + iXIndent + iXPostdent;

	int iTextW, iTextH;
	int iDataW, iDataH;

	if ( m_pTargetNameLabel && m_pTargetDataLabel )
	{
		m_pTargetNameLabel->GetContentSize( iTextW, iTextH );
		m_pTargetDataLabel->GetContentSize( iDataW, iDataH );
		iWidth += MAX(iTextW,iDataW);

		SetSize( iWidth, GetTall() );

		int nOffset = m_bArenaPanelVisible ? YRES (120) : 0; // HACK: move the targetID up a bit so it won't overlap the panel

		int x1 = 0, y1 = 0;
		int x2 = 0, y2 = 0;
		int x3 = 0, y3 = 0;
		m_pTargetNameLabel->GetPos( x1, y1 );
		m_pTargetDataLabel->GetPos( x2, y2 );
		if ( m_pTargetKillStreakIcon )
		{
			m_pTargetKillStreakIcon->GetPos( x3, y3 );
		}

		// Shift Labels
		{
			int nBuffer = ( m_pAvatarImage && m_pAvatarImage->IsVisible() ) ? 6 : 8;
			m_pTargetNameLabel->SetPos( XRES( nBuffer ) + m_pTargetHealth->GetWide(), y1 );
			m_pTargetDataLabel->SetPos( XRES( nBuffer ) + m_pTargetHealth->GetWide(), y2 );

			if ( m_pTargetKillStreakIcon )
			{
				m_pTargetKillStreakIcon->SetPos( XRES( 10 ) + m_pTargetHealth->GetWide(), y3 );
			}
		}

		if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_NORMAL )
		{
			SetPos( (ScreenWidth() - iWidth) * 0.5,  m_nOriginalY - nOffset );
		}
		else
		{
			int iBottomBarHeight = 0;
			if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
			{
				iBottomBarHeight = g_pSpectatorGUI->GetBottomBarHeight();
			}

			int iYPos =	ScreenHeight() - GetTall() - iBottomBarHeight - m_iYOffset;

			if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_LEFT )
			{
				SetPos( m_iXOffset, iYPos );
			}
			else if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_CENTER )
			{
				SetPos( (ScreenWidth() - iWidth) * 0.5, iYPos );
			}
			else if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_RIGHT )
			{
				SetPos( ScreenWidth() - iWidth - m_iXOffset, iYPos );
			}
		}

		if ( m_pBGPanel_Spec_Blue )
		{
			m_pBGPanel_Spec_Blue->SetSize( iWidth, GetTall() );
		}

		if ( m_pBGPanel_Spec_Red )
		{
			m_pBGPanel_Spec_Red->SetSize( iWidth, GetTall() );
		}

		if ( m_pBGPanel_Spec_Blue && m_pBGPanel_Spec_Red )
		{
			if ( m_iTargetEntIndex )
			{
				C_BaseEntity *pEnt = cl_entitylist->GetEnt( m_iTargetEntIndex );
				if ( pEnt )
				{
					bool bRed = ( pEnt->GetTeamNumber() == TF_TEAM_RED );
					m_pBGPanel_Spec_Blue->SetVisible( !bRed );
					m_pBGPanel_Spec_Red->SetVisible( bRed );			

					m_pBGPanel_Spec_Blue->SetAlpha( tf_hud_target_id_alpha.GetInt() );
					m_pBGPanel_Spec_Red->SetAlpha( tf_hud_target_id_alpha.GetInt() );
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CFloatingHealthIcon::CFloatingHealthIcon( vgui::Panel *parent, const char *name ) : EditablePanel( parent, name )
{
	m_flPrevHealth = -1.f;
	m_nPrevLevel = 0;

	SetVisible( false );
	SetBounds( 0, 0, 128, 128 );

	vgui::ivgui()->AddTickSignal( GetVPanel(), 50 );
	OnTick();

	m_pTargetHealth = new CTFSpectatorGUIHealth( this, "SpectatorGUIHealth" );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFloatingHealthIcon::Reset( void )
{
	m_pTargetHealth->Reset();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFloatingHealthIcon::SetEntity( C_BaseEntity *pEntity )
{
	m_hEntity = pEntity;

	if ( !m_pTargetHealth )
		return;

	m_pTargetHealth->SetAllowAnimations( false );
	m_pTargetHealth->HideHealthBonusImage();

	bool bBuilding = false;

	if ( m_hEntity->IsPlayer() )
	{
		C_TFPlayer *pPlayer = ToTFPlayer( m_hEntity );
		bBuilding = ( pPlayer && pPlayer->IsMiniBoss() ) ? true : false;
	}
	m_pTargetHealth->SetBuilding( bBuilding );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CFloatingHealthIcon* CFloatingHealthIcon::AddFloatingHealthIcon( C_BaseEntity *pEntity )
{
	CFloatingHealthIcon *pHealthIcon = new CFloatingHealthIcon( g_pClientMode->GetViewport(), "HealthIcon" );
	vgui::SETUP_PANEL( pHealthIcon );
	pHealthIcon->SetEntity( pEntity );

	return pHealthIcon;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFloatingHealthIcon::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	LoadControlSettings( "resource/UI/HealthIconPanel.res" );
	SetVisible( false );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFloatingHealthIcon::OnTick( void )
{
	if ( !m_pTargetHealth )
		return;

	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();

	if ( !ShouldHealthBarBeVisible( m_hEntity, pLocalTFPlayer ) )
	{
		SetVisible( false );
		return;
	}

	C_TFPlayer *pTargetPlayer = ToTFPlayer( m_hEntity );
	if ( pTargetPlayer && pTargetPlayer->m_Shared.IsStealthed() )
	{
		SetVisible( false );
		return;
	}

	// Defaults for all entities
	float flHealth = m_hEntity->GetHealth();
	float flMaxHealth = m_hEntity->GetMaxHealth();
	float iMaxBuffedHealth = m_hEntity->GetMaxHealth();

	if ( pTargetPlayer && pTargetPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTargetPlayer->IsEnemyPlayer() )
	{
		flHealth = (float)pTargetPlayer->m_Shared.GetDisguiseHealth();
		flMaxHealth = (float)pTargetPlayer->m_Shared.GetDisguiseMaxHealth();
		iMaxBuffedHealth = pTargetPlayer->m_Shared.GetDisguiseMaxBuffedHealth();
	}
		
	if ( flHealth != m_flPrevHealth )
 	{
		m_pTargetHealth->SetHealth( flHealth, flMaxHealth, iMaxBuffedHealth );
		m_flPrevHealth = flHealth;
	}

#ifdef STAGING_ONLY
	if ( TFGameRules() && TFGameRules()->IsBountyMode() && tf_bountymode_showhealth.GetInt() == 2 )
	{
		if ( m_hEntity->IsPlayer() )
		{
			if ( !pTargetPlayer || pTargetPlayer->IsMiniBoss() )
				return;

			int nPlayerLevel = pTargetPlayer->GetExperienceLevel();
			if ( nPlayerLevel != m_nPrevLevel )
			{
				m_pTargetHealth->SetLevel( nPlayerLevel );
				m_nPrevLevel = nPlayerLevel;
			}
		}
	}
#endif // STAGING_ONLY
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
ConVar tf_healthicon_height_offset( "tf_healthicon_height_offset", "10", FCVAR_ARCHIVE, "Offset of the health icon away from the top of the target." );
void CFloatingHealthIcon::Paint( void )
{
	if ( !CalculatePosition() )
		return;

	BaseClass::Paint();
}

//-----------------------------------------------------------------------------
bool CFloatingHealthIcon::CalculatePosition( )
{
	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
		return false;

	if ( !m_hEntity || m_hEntity->IsDormant() )
	{
		return false;
	}

	Vector vecTarget = m_hEntity->GetAbsOrigin();

	// Reposition based on our target's position
	Vector vecDistance = vecTarget - pLocalTFPlayer->GetAbsOrigin();
	vecTarget.z += VEC_HULL_MAX_SCALED( m_hEntity->GetBaseAnimating() ).z + tf_healthicon_height_offset.GetInt() + m_hEntity->GetHealthBarHeightOffset();

	int iX, iY;
	GetVectorInHudSpace( vecTarget, iX, iY );				// TODO: GetVectorInHudSpace or GetVectorInScreenSpace?
	SetPos( iX - ( GetWide() / 2 ), iY - GetTall() );

	return true;
}

//-----------------------------------------------------------------------------
void CFloatingHealthIcon::SetVisible( bool state )
{
	if ( state )
	{
		CalculatePosition();
	}

	BaseClass::SetVisible( state );
}


//-----------------------------------------------------------------------------
bool CFloatingHealthIcon::IsVisible( void )
{
	if ( !m_pTargetHealth )
		return false;

	C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalTFPlayer )
		return false;

	//if ( pLocalTFPlayer->GetObserverMode() == OBS_MODE_FREEZECAM )
	if ( pLocalTFPlayer->GetObserverMode() > OBS_MODE_NONE )
		return false;

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

	return BaseClass::IsVisible();
}