Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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

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