You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
13 KiB
418 lines
13 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "hud.h" |
|
#include "c_team.h" |
|
#include "tf_shareddefs.h" |
|
#include "tf_gamerules.h" |
|
#include "iclientmode.h" |
|
#include "c_playerresource.h" |
|
#include "c_tf_playerresource.h" |
|
#include "tf_hud_target_id.h" |
|
#include "c_baseobject.h" |
|
#include "tf_hud_spectator_extras.h" |
|
|
|
#include <vgui/ILocalize.h> |
|
#include <vgui/ISurface.h> |
|
|
|
using namespace vgui; |
|
|
|
ConVar tf_spec_xray_disable( "tf_spec_xray_disable", "0", FCVAR_ARCHIVE, "Disable the spectator xray mode." ); |
|
ConVar tf_enable_glows_after_respawn( "tf_enable_glows_after_respawn", "1", FCVAR_ARCHIVE, "Enable teammate glow effects after respawn." ); |
|
|
|
DECLARE_HUDELEMENT( CTFHudSpectatorExtras ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFHudSpectatorExtras::CTFHudSpectatorExtras( const char *pszElementName ) : CHudElement( pszElementName ), EditablePanel( NULL, "HudSpectatorExtras" ) |
|
{ |
|
vgui::Panel *pParent = g_pClientMode->GetViewport(); |
|
SetParent( pParent ); |
|
|
|
SetHiddenBits( 0 ); |
|
|
|
vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFHudSpectatorExtras::ShouldDraw( void ) |
|
{ |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudSpectatorExtras::Reset( void ) |
|
{ |
|
if ( !g_PR ) |
|
return; |
|
|
|
FOR_EACH_VEC( m_vecEntitiesToDraw, i ) |
|
{ |
|
int nEntIndex = m_vecEntitiesToDraw[i].m_nEntIndex; |
|
if ( IsPlayerIndex( nEntIndex ) && !g_PR->IsConnected( nEntIndex ) ) |
|
continue; |
|
|
|
CBaseCombatCharacter *pEnt = dynamic_cast< CBaseCombatCharacter* >( cl_entitylist->GetEnt( nEntIndex ) ); |
|
if ( !pEnt ) |
|
continue; |
|
|
|
if ( pEnt->IsClientSideGlowEnabled() ) |
|
{ |
|
pEnt->SetClientSideGlowEnabled( false ); |
|
} |
|
} |
|
|
|
m_vecEntitiesToDraw.Purge(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudSpectatorExtras::RemoveEntity( int nRemove ) |
|
{ |
|
FOR_EACH_VEC( m_vecEntitiesToDraw, i ) |
|
{ |
|
if ( m_vecEntitiesToDraw[i].m_nEntIndex == nRemove ) |
|
{ |
|
m_vecEntitiesToDraw.Remove( i ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudSpectatorExtras::OnTick() |
|
{ |
|
BaseClass::OnTick(); |
|
|
|
if ( !g_PR ) |
|
return; |
|
|
|
if ( TFGameRules() && TFGameRules()->ShowMatchSummary() ) |
|
{ |
|
Reset(); |
|
return; |
|
} |
|
|
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( !pLocalPlayer ) |
|
return; |
|
|
|
int nLocalPlayerTeam = pLocalPlayer->GetTeamNumber(); |
|
bool bIsHLTV = engine->IsHLTV(); |
|
|
|
if ( tf_spec_xray_disable.GetBool() || ( !bIsHLTV && ( nLocalPlayerTeam < TEAM_SPECTATOR ) ) ) |
|
{ |
|
Reset(); |
|
return; |
|
} |
|
|
|
if ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) |
|
{ |
|
if ( pLocalPlayer->IsAlive() && !pLocalPlayer->m_Shared.InCond( TF_COND_TEAM_GLOWS ) ) |
|
{ |
|
if ( m_vecEntitiesToDraw.Count() > 0 ) |
|
{ |
|
Reset(); |
|
} |
|
return; |
|
} |
|
} |
|
|
|
if ( bIsHLTV || |
|
( tf_spec_xray.GetBool() && ( ( nLocalPlayerTeam == TEAM_SPECTATOR ) || ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) || ( pLocalPlayer->m_Shared.InCond( TF_COND_TEAM_GLOWS ) && tf_enable_glows_after_respawn.GetBool() ) ) ) ) |
|
{ |
|
bool bShowEveryone = ( bIsHLTV || |
|
( ( nLocalPlayerTeam == TEAM_SPECTATOR ) && tf_spec_xray.GetBool() ) || |
|
( ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) && ( tf_spec_xray.GetInt() > 1 ) ) ); |
|
|
|
// loop through the players |
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
if ( !g_PR->IsConnected( i ) ) |
|
{ |
|
RemoveEntity( i ); |
|
continue; |
|
} |
|
|
|
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); |
|
if ( !pPlayer || ( pPlayer == pLocalPlayer ) ) |
|
{ |
|
RemoveEntity( i ); |
|
continue; |
|
} |
|
|
|
int nPlayerTeamNumber = pPlayer->GetTeamNumber(); |
|
|
|
// remove the entities we don't want to draw anymore |
|
if ( pPlayer->IsDormant() || |
|
( nPlayerTeamNumber < FIRST_GAME_TEAM ) || |
|
( !pPlayer->IsAlive() ) || |
|
( pPlayer->m_Shared.IsStealthed() && ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) || |
|
( !bShowEveryone && !pPlayer->IsPlayerClass( TF_CLASS_SPY ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) || |
|
( !bShowEveryone && pPlayer->IsPlayerClass( TF_CLASS_SPY ) && !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) || |
|
( !bShowEveryone && pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) && ( pPlayer->m_Shared.GetDisguiseTeam() != nLocalPlayerTeam ) ) ) |
|
{ |
|
if ( pPlayer->IsClientSideGlowEnabled() ) |
|
{ |
|
pPlayer->SetClientSideGlowEnabled( false ); |
|
} |
|
RemoveEntity( i ); |
|
continue; |
|
} |
|
|
|
// passed all of the tests, so make sure they're in the list |
|
int nVecIndex = -1; |
|
FOR_EACH_VEC( m_vecEntitiesToDraw, nTemp ) |
|
{ |
|
if ( m_vecEntitiesToDraw[nTemp].m_nEntIndex == i ) |
|
{ |
|
nVecIndex = nTemp; |
|
break; |
|
} |
|
} |
|
if ( nVecIndex == -1 ) |
|
{ |
|
nVecIndex = m_vecEntitiesToDraw.AddToTail(); |
|
} |
|
|
|
// set the player index |
|
m_vecEntitiesToDraw[nVecIndex].m_nEntIndex = i; |
|
|
|
// don't draw their name if we're currently spectating them, but we still want them to glow |
|
m_vecEntitiesToDraw[nVecIndex].m_bDrawName = true; |
|
if ( !pLocalPlayer->IsAlive() ) |
|
{ |
|
CSpectatorTargetID *pSpecTargetID = (CSpectatorTargetID *)GET_HUDELEMENT( CSpectatorTargetID ); |
|
if ( ( pLocalPlayer->GetObserverTarget() == pPlayer ) || ( pSpecTargetID && pSpecTargetID->GetTargetIndex() == i ) ) |
|
{ |
|
m_vecEntitiesToDraw[nVecIndex].m_bDrawName = false; |
|
|
|
// if we're in chase mode, just remove them entirely |
|
if ( pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE ) |
|
{ |
|
if ( pPlayer->IsClientSideGlowEnabled() ) |
|
{ |
|
pPlayer->SetClientSideGlowEnabled( false ); |
|
} |
|
RemoveEntity( i ); |
|
continue; |
|
} |
|
} |
|
} |
|
|
|
// disguised Spy? |
|
C_TFPlayer *pDisguiseTarget = NULL; |
|
if ( !bIsHLTV && ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) ) |
|
{ |
|
if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) |
|
{ |
|
pDisguiseTarget = ToTFPlayer( pPlayer->m_Shared.GetDisguiseTarget() ); |
|
} |
|
} |
|
|
|
// use actual name or disguised name? |
|
int nNameIndex = pDisguiseTarget ? pDisguiseTarget->entindex() : i; |
|
g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( nNameIndex ), m_vecEntitiesToDraw[nVecIndex].m_wszName, sizeof( m_vecEntitiesToDraw[nVecIndex].m_wszName ) ); |
|
m_vecEntitiesToDraw[nVecIndex].m_nNameWidth = UTIL_ComputeStringWidth( m_hNameFont, m_vecEntitiesToDraw[nVecIndex].m_wszName ); |
|
|
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = ( VEC_HULL_MAX_SCALED( pPlayer ).z ); |
|
|
|
// use actual health or disguised health? |
|
float flHealth = 1.0f; |
|
|
|
if ( pDisguiseTarget ) |
|
{ |
|
flHealth = (float)( pPlayer->m_Shared.GetDisguiseHealth() ) / (float)( pPlayer->m_Shared.GetDisguiseMaxHealth() ); |
|
} |
|
else |
|
{ |
|
flHealth = (float)( pPlayer->GetHealth() ) / (float)( pPlayer->GetMaxHealth() ); |
|
} |
|
// don't show buffed health for this simple bar |
|
if ( flHealth > 1.0f ) |
|
{ |
|
flHealth = 1.0f; |
|
} |
|
m_vecEntitiesToDraw[nVecIndex].m_flHealth = flHealth; |
|
|
|
// what color should we use? |
|
float r, g, b; |
|
pPlayer->GetGlowEffectColor( &r, &g, &b ); |
|
m_vecEntitiesToDraw[nVecIndex].m_clrGlowColor = Color( r * 255, g * 255, b * 255, 255 ); |
|
|
|
if ( !pPlayer->IsClientSideGlowEnabled() ) |
|
{ |
|
pPlayer->SetClientSideGlowEnabled( true ); |
|
} |
|
} |
|
|
|
// loop through the buildings |
|
for ( int nCount = 0; nCount < IBaseObjectAutoList::AutoList().Count(); nCount++ ) |
|
{ |
|
bool bDraw = false; |
|
C_BaseObject *pObject = static_cast<C_BaseObject*>( IBaseObjectAutoList::AutoList()[nCount] ); |
|
if ( !pObject->IsDormant() && !pObject->IsMapPlaced() && !pObject->IsEffectActive( EF_NODRAW ) ) |
|
{ |
|
if ( bShowEveryone || ( ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( nLocalPlayerTeam == pObject->GetTeamNumber() ) ) ) |
|
{ |
|
bDraw = true; |
|
} |
|
} |
|
|
|
if ( bDraw ) |
|
{ |
|
int nVecIndex = -1; |
|
FOR_EACH_VEC( m_vecEntitiesToDraw, nTemp ) |
|
{ |
|
if ( m_vecEntitiesToDraw[nTemp].m_nEntIndex == pObject->entindex() ) |
|
{ |
|
nVecIndex = nTemp; |
|
break; |
|
} |
|
} |
|
if ( nVecIndex == -1 ) |
|
{ |
|
nVecIndex = m_vecEntitiesToDraw.AddToTail(); |
|
} |
|
|
|
// set the player index |
|
m_vecEntitiesToDraw[nVecIndex].m_nEntIndex = pObject->entindex(); |
|
|
|
// don't draw the name if we're currently spectating this building, but we still want it to glow |
|
m_vecEntitiesToDraw[nVecIndex].m_bDrawName = true; |
|
if ( pLocalPlayer->GetObserverTarget() == pObject ) |
|
{ |
|
m_vecEntitiesToDraw[nVecIndex].m_bDrawName = false; |
|
} |
|
|
|
if ( pObject->GetType() == OBJ_TELEPORTER ) |
|
{ |
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = 30; |
|
} |
|
else if ( pObject->GetType() == OBJ_DISPENSER ) |
|
{ |
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = 70; |
|
} |
|
else |
|
{ |
|
switch ( pObject->GetUpgradeLevel() ) |
|
{ |
|
case 1: |
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = 50; |
|
break; |
|
case 2: |
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = 65; |
|
break; |
|
case 3: |
|
default: |
|
m_vecEntitiesToDraw[nVecIndex].m_nOffset = 80; |
|
break; |
|
} |
|
} |
|
|
|
pObject->GetTargetIDString( m_vecEntitiesToDraw[nVecIndex].m_wszName, sizeof( m_vecEntitiesToDraw[nVecIndex].m_wszName ), true ); |
|
m_vecEntitiesToDraw[nVecIndex].m_nNameWidth = UTIL_ComputeStringWidth( m_hNameFont, m_vecEntitiesToDraw[nVecIndex].m_wszName ); |
|
|
|
float flHealth = 1.0f; |
|
flHealth = (float)( pObject->GetHealth() ) / (float)( pObject->GetMaxHealth() ); |
|
if ( flHealth > 1.0f ) |
|
{ |
|
flHealth = 1.0f; |
|
} |
|
m_vecEntitiesToDraw[nVecIndex].m_flHealth = flHealth; |
|
|
|
// what color should we use? |
|
float r, g, b; |
|
pObject->GetGlowEffectColor( &r, &g, &b ); |
|
m_vecEntitiesToDraw[nVecIndex].m_clrGlowColor = Color( r * 255, g * 255, b * 255, 255 ); |
|
|
|
if ( !pObject->IsClientSideGlowEnabled() ) |
|
{ |
|
pObject->SetClientSideGlowEnabled( true ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( pObject->IsClientSideGlowEnabled() ) |
|
{ |
|
pObject->SetClientSideGlowEnabled( false ); |
|
} |
|
RemoveEntity( pObject->entindex() ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
Reset(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFHudSpectatorExtras::Paint() |
|
{ |
|
BaseClass::Paint(); |
|
|
|
if ( !g_PR ) |
|
return; |
|
|
|
if ( tf_spec_xray_disable.GetBool() ) |
|
return; |
|
|
|
int nNameOffset = 35; |
|
int nHealthWidth = 70; |
|
int nHealthHeight = 6; |
|
|
|
FOR_EACH_VEC( m_vecEntitiesToDraw, i ) |
|
{ |
|
if ( !m_vecEntitiesToDraw[i].m_bDrawName ) |
|
continue; |
|
|
|
int nEntIndex = m_vecEntitiesToDraw[i].m_nEntIndex; |
|
if ( IsPlayerIndex( nEntIndex ) && !g_PR->IsConnected( nEntIndex ) ) |
|
continue; |
|
|
|
C_BaseEntity *pEnt = cl_entitylist->GetEnt( nEntIndex ); |
|
if ( !pEnt ) |
|
continue; |
|
|
|
Vector vecPos = pEnt->GetAbsOrigin(); |
|
vecPos.z += m_vecEntitiesToDraw[i].m_nOffset; |
|
|
|
int iX, iY; |
|
Vector vecWorld( vecPos.x, vecPos.y, vecPos.z ); |
|
if ( GetVectorInHudSpace( vecWorld, iX, iY ) ) |
|
{ |
|
// draw the name |
|
vgui::surface()->DrawSetTextFont( m_hNameFont ); |
|
vgui::surface()->DrawSetTextPos( iX - ( m_vecEntitiesToDraw[i].m_nNameWidth / 2 ), iY - nNameOffset ); |
|
vgui::surface()->DrawSetTextColor( m_vecEntitiesToDraw[i].m_clrGlowColor ); |
|
vgui::surface()->DrawPrintText( m_vecEntitiesToDraw[i].m_wszName, wcslen( m_vecEntitiesToDraw[i].m_wszName ), vgui::FONT_DRAW_NONADDITIVE ); |
|
|
|
int xHealthPos = iX - 35; |
|
int yHealthPos = iY - 10; |
|
|
|
// draw the health bar background |
|
vgui::surface()->DrawSetColor( Color( 127, 127, 127, 255 ) ); |
|
vgui::surface()->DrawFilledRect( xHealthPos, yHealthPos, xHealthPos + nHealthWidth, yHealthPos + nHealthHeight ); |
|
|
|
// draw the health bar |
|
vgui::surface()->DrawSetColor( m_vecEntitiesToDraw[i].m_clrGlowColor ); |
|
vgui::surface()->DrawFilledRect( xHealthPos, yHealthPos, xHealthPos + ( nHealthWidth * m_vecEntitiesToDraw[i].m_flHealth ), yHealthPos + nHealthHeight ); |
|
} |
|
} |
|
}
|
|
|