diff options
Diffstat (limited to 'game/client/tf/tf_hud_target_id.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_target_id.cpp | 1564 |
1 files changed, 1564 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_target_id.cpp b/game/client/tf/tf_hud_target_id.cpp new file mode 100644 index 0000000..dd41b29 --- /dev/null +++ b/game/client/tf/tf_hud_target_id.cpp @@ -0,0 +1,1564 @@ +//========= 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(); +} |