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.

621 lines
16 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "iclientmode.h"
#include <vgui_controls/AnimationController.h>
#include <vgui_controls/EditablePanel.h>
#include <vgui_controls/ProgressBar.h>
#include "tf_gamerules.h"
#include "tf_logic_halloween_2014.h"
#include "c_tf_playerresource.h"
#include "tf_playerpanel.h"
#include "tf_teamstatus.h"
#include "tf_matchmaking_shared.h"
#include "tf_match_description.h"
using namespace vgui;
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CTFTeamStatusPlayerPanel::CTFTeamStatusPlayerPanel( vgui::Panel *parent, const char *name ) : CTFPlayerPanel( parent, name )
{
m_pHealthBar = new vgui::ContinuousProgressBar( this, "healthbar" );
m_pOverhealBar = new vgui::ContinuousProgressBar( this, "overhealbar" );
m_pClassImageBG = new vgui::Panel( this, "classimagebg" );
m_pDeathFlag = new vgui::ImagePanel( this, "DeathPanel" );
m_iTeam = TEAM_UNASSIGNED;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatusPlayerPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatusPlayerPanel::UpdateBorder( void )
{
// do nothing
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFTeamStatusPlayerPanel::Update( void )
{
if ( !g_TF_PR || !TFGameRules() )
return false;
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pLocalPlayer || ( pLocalPlayer->GetTeamNumber() < FIRST_GAME_TEAM ) )
return false;
bool bChanged = false;
bool bVisible = GetTeam() >= FIRST_GAME_TEAM;
int iRespawnWait = -1;
if ( IsVisible() != bVisible )
{
SetVisible( bVisible );
bChanged = true;
}
if ( bVisible && g_TF_PR )
{
bool bSameTeamAsLocalPlayer = ( GetTeam() == g_TF_PR->GetTeam( pLocalPlayer->entindex() ) );
// are we connected with a class?
Assert( TF_CLASS_UNDEFINED == 0 );
int iClass = -1;
bool bAlive = false;
int iHealth = -1;
bool bIsLocalPlayer = false;
if ( m_iPlayerIndex > 0 )
{
bIsLocalPlayer = pLocalPlayer->entindex() == m_iPlayerIndex;
iClass = g_TF_PR->GetPlayerClass( m_iPlayerIndex );
if ( iClass != TF_CLASS_UNDEFINED )
{
bAlive = g_TF_PR->IsAlive( m_iPlayerIndex );
}
if ( bAlive )
{
iHealth = g_TF_PR->GetHealth( m_iPlayerIndex );
}
// calc respawn time remaining
if ( !bAlive && ( iClass != TF_CLASS_UNDEFINED ) )
{
float flRespawnAt = g_TF_PR->GetNextRespawnTime( m_iPlayerIndex );
iRespawnWait = ( flRespawnAt - gpGlobals->curtime );
if ( iRespawnWait <= 0 )
iRespawnWait = -1;
}
// hide class info from the other team?
if ( !bSameTeamAsLocalPlayer )
{
iClass = TF_CLASS_UNDEFINED;
}
}
if ( m_iTeam != GetTeam() )
{
m_iTeam = GetTeam();
bChanged = true;
}
if ( m_pClassImageBG )
{
if ( m_iTeam == TF_TEAM_RED )
{
Color bgColor( m_ColorPortraitBGRedLocalPlayer );
if ( !bIsLocalPlayer )
{
bgColor = bAlive ? m_ColorPortraitBGRed : m_ColorPortraitBGRedDead;
}
m_pClassImageBG->SetBgColor( bgColor );
}
else
{
Color bgColor( m_ColorPortraitBGBlueLocalPlayer );
if ( !bIsLocalPlayer )
{
bgColor = bAlive ? m_ColorPortraitBGBlue : m_ColorPortraitBGBlueDead;
}
m_pClassImageBG->SetBgColor( bgColor );
}
}
// update live state
if ( m_pClassImage )
{
if ( m_bPrevAlive != bAlive )
{
bChanged = true;
m_bPrevAlive = bAlive;
if ( !bAlive )
{
m_pClassImage->SetDrawColor( ( m_iTeam == TF_TEAM_RED ) ? m_ColorPortraitBlendDeadRed : m_ColorPortraitBlendDeadBlue );
}
m_pDeathFlag->SetImage( ( m_iTeam == TF_TEAM_RED ) ? "../HUD/comp_player_status" : "../HUD/comp_player_status_blue" );
if ( bAlive )
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerAlive", false );
}
else
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerDead", false );
}
}
// update class image
if ( ( m_iPrevClass != iClass ) || bChanged )
{
bChanged = true;
m_iPrevClass = iClass;
if ( iClass < 0 )
{
m_pClassImage->SetImage( "hud_connecting" );
}
else if ( iClass == TF_CLASS_UNDEFINED )
{
int iDeadClass = g_TF_PR->GetPlayerClassWhenKilled( m_iPlayerIndex );
if ( !bAlive && !bSameTeamAsLocalPlayer && ( m_iTeam >= FIRST_GAME_TEAM ) && ( iDeadClass > TF_CLASS_UNDEFINED ) )
{
m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iDeadClass + 9] : g_pszItemClassImagesBlue[iDeadClass + 9] ) );
}
else
{
m_pClassImage->SetImage( "class_portraits/silhouette_alpha" );
}
}
else
{
if ( bAlive )
{
m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass] : g_pszItemClassImagesBlue[iClass] ) );
}
else
{
m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass + 9] : g_pszItemClassImagesBlue[iClass + 9] ) );
}
}
}
}
// update health indicator
if ( m_pHealthBar && m_pOverhealBar )
{
if ( iHealth != m_iPrevHealth )
{
m_iPrevHealth = iHealth;
if ( ( iHealth > 0 ) && bSameTeamAsLocalPlayer )
{
float flMaxHealth = g_TF_PR->GetMaxHealth( m_iPlayerIndex );
float flProgress = iHealth / flMaxHealth;
// health bar
if ( !m_pHealthBar->IsVisible() )
{
m_pHealthBar->SetVisible( true );
}
m_pHealthBar->SetProgress( ( flProgress >= 1.0f ) ? 1.0f : flProgress );
if ( flProgress > m_flPercentageHealthMed )
{
m_pHealthBar->SetFgColor( m_ColorBarHealthHigh );
}
else if ( flProgress > m_flPercentageHealthLow )
{
m_pHealthBar->SetFgColor( m_ColorBarHealthMed );
}
else
{
m_pHealthBar->SetFgColor( m_ColorBarHealthLow );
}
// overheal bar
if ( flProgress > 1.0f )
{
if ( !m_pOverhealBar->IsVisible() )
{
m_pOverhealBar->SetVisible( true );
}
m_pOverhealBar->SetProgress( flProgress - 1.0f );
}
else
{
if ( m_pOverhealBar->IsVisible() )
{
m_pOverhealBar->SetVisible( false );
}
}
}
else
{
if ( m_pHealthBar->IsVisible() )
{
m_pHealthBar->SetVisible( false );
}
if ( m_pOverhealBar->IsVisible() )
{
m_pOverhealBar->SetVisible( false );
}
}
bChanged = true;
}
}
// update respawn time
if ( iRespawnWait != m_iPrevRespawnWait )
{
m_iPrevRespawnWait = iRespawnWait;
if ( ( iRespawnWait < 0 ) || !bSameTeamAsLocalPlayer )
{
SetDialogVariable( "respawntime", "" );
}
else
{
SetDialogVariable( "respawntime", VarArgs( "%d", iRespawnWait ) );
}
bChanged = true;
}
// gamerules state
if ( TFGameRules()->State_Get() != m_iPrevState )
{
m_iPrevState = TFGameRules()->State_Get();
bChanged = true;
}
}
return bChanged;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTFTeamStatus::CTFTeamStatus( Panel *parent, const char *panelName ) : BaseClass( parent, panelName )
{
m_pPlayerPanelKVs = NULL;
m_bReapplyPlayerPanelKVs = false;
Reset();
ivgui()->AddTickSignal( GetVPanel(), 100 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFTeamStatus::~CTFTeamStatus()
{
if ( m_pPlayerPanelKVs )
{
m_pPlayerPanelKVs->deleteThis();
m_pPlayerPanelKVs = NULL;
}
}
void SetGrowDir( CTFTeamStatus::EGrowDir* pGrowDir, const char* pszString )
{
if ( FStrEq( pszString, "east" ) )
{
(*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_EAST;
}
else if ( FStrEq( pszString, "west" ) )
{
(*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_WEST;
}
else
{
Assert( !"Invalid direction string for team status team grow direction" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
SetGrowDir( &m_eTeam1GrowDir, inResourceData->GetString( "team1_grow_dir" ) );
SetGrowDir( &m_eTeam2GrowDir, inResourceData->GetString( "team2_grow_dir" ) );
KeyValues *pItemKV = inResourceData->FindKey( "playerpanels_kv" );
if ( pItemKV )
{
if ( m_pPlayerPanelKVs )
{
m_pPlayerPanelKVs->deleteThis();
}
m_pPlayerPanelKVs = new KeyValues( "playerpanels_kv" );
pItemKV->CopySubkeys( m_pPlayerPanelKVs );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_bReapplyPlayerPanelKVs = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::PerformLayout( void )
{
BaseClass::PerformLayout();
int iTeam1Count = 0;
int iTeam2Count = 0;
// Tally up how many are on each team so we can scale them appropriately down below
for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
{
if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_BLUE )
{
++iTeam1Count;
}
else if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_RED )
{
++iTeam2Count;
}
}
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
// Snag local player info
int nLocalPlayerTeam = TF_TEAM_COUNT;
int nLocalPlayerIndex = -1;
if ( pLocalPlayer )
{
nLocalPlayerIndex = pLocalPlayer->entindex();
nLocalPlayerTeam = pLocalPlayer->GetTeamNumber();
}
int iTeam1Processed = 0;
int iTeam2Processed = 0;
for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
{
if ( m_PlayerPanels[i]->GetPlayerIndex() <= 0 )
{
m_PlayerPanels[i]->SetVisible( false );
continue;
}
bool bIsLocalPlayerPanel = nLocalPlayerIndex == m_PlayerPanels[i]->GetPlayerIndex();
int iTeam = m_PlayerPanels[i]->GetTeam();
int iXPos = 0;
// Setup vars
EGrowDir& eGrowDir = iTeam == TF_TEAM_BLUE ? m_eTeam1GrowDir : m_eTeam2GrowDir;
int& iTeamCount = iTeam == TF_TEAM_BLUE ? iTeam1Count : iTeam2Count;
int& iBaseX = iTeam == TF_TEAM_BLUE ? m_iTeam1BaseX : m_iTeam2BaseX;
int& iProcessed = iTeam == TF_TEAM_BLUE ? iTeam1Processed : iTeam2Processed;
int& iMaxExpand = iTeam == TF_TEAM_BLUE ? m_iTeam1MaxExpand : m_iTeam2MaxExpand;
const int iGap = RemapValClamped( iTeamCount, 6, 12, m_i6v6Gap, m_i12v12Gap );
// Local player is always the innermost panel
int nTeamPanelIndex = bIsLocalPlayerPanel ? 0
: iTeam == nLocalPlayerTeam ? iProcessed + 1
: iProcessed;
// Setup X-position and widths
// Use the max width if less than 6 (to fill out the space)
int nNewWide = iTeamCount <= 6 ? m_iMaxSize : ( iMaxExpand - ( iGap * ( iTeamCount - 1 ) ) ) / iTeamCount;
// How far each panel is apart from each other
const int iXStep = iGap + nNewWide;
// How many steps out this panel should be
const int iXOffset = iXStep * nTeamPanelIndex;
// Step out the panels by the step
iXPos = eGrowDir == GROW_EAST ? iBaseX + iXOffset // Align the left edge of the left-most panel on the specified point
: eGrowDir == GROW_WEST ? iBaseX - iXOffset - nNewWide // Align the right edge of the right-most panel on the specified point
: 0;
// This is expensive, so only do it if we need to
if ( nNewWide != m_PlayerPanels[i]->GetWide() )
{
// We change the width in the KV's then reapply settings so the children will do their
// magical proportionaltoparent layout.
if ( m_pPlayerPanelKVs )
{
m_pPlayerPanelKVs->SetInt( "wide", scheme()->GetProportionalNormalizedValueEx( GetScheme(), nNewWide ) );
m_PlayerPanels[i]->ApplySettings( m_pPlayerPanelKVs );
m_PlayerPanels[i]->InvalidateLayout( false, true );
m_PlayerPanels[i]->Update();
}
}
if ( !bIsLocalPlayerPanel )
{
++iProcessed;
}
m_PlayerPanels[i]->SetPos( iXPos, m_PlayerPanels[i]->GetYPos() );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::Reset()
{
for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
{
m_PlayerPanels[i]->Reset();
}
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFTeamStatus::ShouldDraw( void )
{
// Get the player and active weapon.
C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( !pPlayer )
return false;
int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() );
if ( iLocalTeam < FIRST_GAME_TEAM )
return false;
if ( TFGameRules() )
{
const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
if ( !pMatchDesc || !pMatchDesc->m_params.m_bUseMatchHud )
return false;
if ( TFGameRules()->ShowMatchSummary() )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::OnTick()
{
if ( IsVisible() != ShouldDraw() )
{
SetVisible( ShouldDraw() );
}
if ( IsVisible() )
{
RecalculatePlayerPanels();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFTeamStatusPlayerPanel *CTFTeamStatus::GetOrAddPanel( int iPanelIndex )
{
if ( iPanelIndex < m_PlayerPanels.Count() )
{
return m_PlayerPanels[iPanelIndex];
}
Assert( iPanelIndex == m_PlayerPanels.Count() );
CTFTeamStatusPlayerPanel *pPanel = new CTFTeamStatusPlayerPanel( this, VarArgs( "playerpanel%d", iPanelIndex ) );
if ( m_pPlayerPanelKVs )
{
pPanel->ApplySettings( m_pPlayerPanelKVs );
pPanel->InvalidateLayout( false, true );
InvalidateLayout();
}
m_PlayerPanels.AddToTail( pPanel );
return pPanel;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::RecalculatePlayerPanels( void )
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pPlayer || !g_TF_PR )
return;
int iPanel = 0;
bool bNeedsLayout = false;
int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() );
if ( iLocalTeam >= FIRST_GAME_TEAM )
{
for ( int i = 1; i <= MAX_PLAYERS; i++ )
{
if ( !g_TF_PR->IsConnected( i ) )
continue;
int iTeam = g_TF_PR->GetTeam( i );
if ( iTeam < FIRST_GAME_TEAM )
continue;
// Add an entry
CTFTeamStatusPlayerPanel *pPanel = GetOrAddPanel( iPanel );
if ( pPanel->GetPlayerIndex() != i )
{
bNeedsLayout = true;
}
pPanel->SetPlayerIndex( i );
if ( pPanel->GetPreviousTeam() != pPanel->GetTeam() )
{
bNeedsLayout = true;
}
++iPanel;
}
}
// Clear out any extra panels
for ( int i = iPanel; i < m_PlayerPanels.Count(); i++ )
{
if ( m_PlayerPanels[i]->GetPlayerIndex() != 0 )
{
bNeedsLayout = true;
}
m_PlayerPanels[i]->SetPlayerIndex( 0 );
}
UpdatePlayerPanels();
if ( bNeedsLayout )
{
InvalidateLayout();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFTeamStatus::UpdatePlayerPanels( void )
{
if ( !g_TF_PR )
return;
for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
{
m_PlayerPanels[i]->Update();
}
}