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.
 
 
 
 
 
 

907 lines
29 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "hudelement.h"
#include "iclientmode.h"
#include <vgui_controls/AnimationController.h>
#include <vgui_controls/EditablePanel.h>
#include <vgui_controls/SectionedListPanel.h>
#include <vgui_controls/ImageList.h>
#include "vgui_avatarimage.h"
#include "tf_hud_match_status.h"
#include "tf_gamerules.h"
#include "c_tf_team.h"
#include "vgui_controls/ScalableImagePanel.h"
#include "tf_time_panel.h"
#include "c_team_objectiveresource.h"
#include "game_controls/spectatorgui.h"
#include "c_tf_playerresource.h"
#include "tf_gc_client.h"
#include "tf_match_description.h"
#include "tf_hud_tournament.h"
#include "tf_classmenu.h"
extern ConVar mp_winlimit;
extern ConVar mp_tournament_stopwatch;
using namespace vgui;
void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
//-----------------------------------------------------------------------------
// Purpose: Use the new match HUD or the old? Right now, Comp is the key
//-----------------------------------------------------------------------------
bool ShouldUseMatchHUD()
{
const IMatchGroupDescription* pMatchDesc = NULL;
if ( GTFGCClientSystem()->BHaveLiveMatch() )
{
pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
}
else if ( TFGameRules() )
{
pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
}
if ( pMatchDesc )
{
return pMatchDesc->m_params.m_bUseMatchHud;
}
return false;
}
const int g_nMaxSupportedRounds = 5;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CRoundCounterPanel::CRoundCounterPanel( Panel *parent, const char *panelName )
: BaseClass( parent, panelName )
, m_pRoundIndicatorKVs( NULL )
, m_pRoundWinIndicatorRedKV( NULL )
, m_pRoundWinIndicatorBlueKV( NULL )
, m_bCountDirty( false )
{
ListenForGameEvent( "winlimit_changed" );
ListenForGameEvent( "winpanel_show_scores" );
ListenForGameEvent( "stop_watch_changed" );
ListenForGameEvent( "teamplay_round_start" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CRoundCounterPanel::~CRoundCounterPanel()
{
if ( m_pRoundIndicatorKVs )
m_pRoundIndicatorKVs->deleteThis();
if ( m_pRoundWinIndicatorRedKV )
m_pRoundWinIndicatorRedKV->deleteThis();
if ( m_pRoundWinIndicatorBlueKV )
m_pRoundWinIndicatorBlueKV->deleteThis();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRoundCounterPanel::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/UI/HudRoundCounter.res" );
}
//-----------------------------------------------------------------------------
// Purpose: Put a copy of the specified keys in block pszKeyName from pKVIn
// into pKV
//-----------------------------------------------------------------------------
void LoadKeyValues( KeyValues** pKV, KeyValues* pKVIn, const char* pszKeyName )
{
if ( (*pKV) )
(*pKV)->deleteThis();
(*pKV) = pKVIn->FindKey( pszKeyName );
if ((*pKV))
{
(*pKV) = (*pKV)->MakeCopy();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRoundCounterPanel::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
LoadKeyValues( &m_pRoundIndicatorKVs, inResourceData, "RoundIndicatorPanel_kv" );
LoadKeyValues( &m_pRoundWinIndicatorRedKV, inResourceData, "RoundWinPanelRed_kv" );
LoadKeyValues( &m_pRoundWinIndicatorBlueKV, inResourceData, "RoundWinPanelBlue_kv" );
CreateRoundPanels( m_vecBlueRoundIndicators, "RoundIndicator", m_pRoundIndicatorKVs );
CreateRoundPanels( m_vecRedRoundIndicators, "RoundIndicator", m_pRoundIndicatorKVs );
CreateRoundPanels( m_vecBlueWinIndicators, "WinIndicatorBlue", m_pRoundWinIndicatorBlueKV );
CreateRoundPanels( m_vecRedWinIndicators, "WinIndicatorRed", m_pRoundWinIndicatorRedKV );
}
//-----------------------------------------------------------------------------
// Purpose: Ensure there are the correct number of image panels. If not, create
// them and apply the passed-in settings
//-----------------------------------------------------------------------------
void CRoundCounterPanel::CreateRoundPanels( ImageVector& vecImages, const char* pszName, KeyValues* pKVSettings )
{
int nMaxRounds = g_nMaxSupportedRounds;
if ( vecImages.Count() != nMaxRounds )
{
FOR_EACH_VEC( vecImages, i )
{
vecImages[ i ]->MarkForDeletion();
}
vecImages.Purge();
if ( nMaxRounds > 0 )
{
while ( nMaxRounds-- )
{
vecImages.AddToTail(new ImagePanel(this, pszName));
}
}
}
if ( pKVSettings )
{
FOR_EACH_VEC(vecImages, i)
{
vecImages[i]->ApplySettings( pKVSettings );
}
}
}
extern ConVar tf_attack_defend_map;
//-----------------------------------------------------------------------------
// Purpose: Loop through and conditionally set visible some panels
//-----------------------------------------------------------------------------
void VisibleCondition( CRoundCounterPanel::ImageVector& vecImages, int iMax )
{
bool bInStopWatch = tf_attack_defend_map.GetBool();
FOR_EACH_VEC( vecImages, i )
{
vecImages[i]->SetVisible( i < iMax && !bInStopWatch );
}
}
//-----------------------------------------------------------------------------
// Purpose: Position all of the round panels and resize the background blue/red
//-----------------------------------------------------------------------------
void CRoundCounterPanel::PerformLayout()
{
BaseClass::PerformLayout();
if ( !TFGameRules() || !ShouldUseMatchHUD() )
return;
C_TFTeam* pTeams[ TF_TEAM_COUNT ];
pTeams[ TF_TEAM_RED ] = GetGlobalTFTeam( TF_TEAM_RED );
pTeams[ TF_TEAM_BLUE ] = GetGlobalTFTeam( TF_TEAM_BLUE );
if ( !pTeams[ TF_TEAM_RED ] || !pTeams[ TF_TEAM_BLUE ] )
return;
// Layout the round indicators
LayoutPanels( m_vecBlueRoundIndicators, EAlignment::ALIGN_WEST, (GetWide() / 2) - m_nIndicatorStartOffset, m_nIndicatorPanelStep );
VisibleCondition( m_vecBlueRoundIndicators, mp_winlimit.GetInt() );
LayoutPanels( m_vecRedRoundIndicators, EAlignment::ALIGN_EAST, (GetWide() / 2) + m_nIndicatorStartOffset, m_nIndicatorPanelStep );
VisibleCondition( m_vecRedRoundIndicators, mp_winlimit.GetInt() );
// Layout the win indicators
LayoutPanels( m_vecBlueWinIndicators, EAlignment::ALIGN_WEST, (GetWide() / 2) - m_nIndicatorStartOffset, m_nIndicatorPanelStep );
VisibleCondition( m_vecBlueWinIndicators, Min( mp_winlimit.GetInt(), pTeams[ TF_TEAM_BLUE ]->m_iScore ) );
LayoutPanels( m_vecRedWinIndicators, EAlignment::ALIGN_EAST, (GetWide() / 2) + m_nIndicatorStartOffset, m_nIndicatorPanelStep );
VisibleCondition( m_vecRedWinIndicators, Min( mp_winlimit.GetInt(), pTeams[ TF_TEAM_RED ]->m_iScore ) );
}
void CRoundCounterPanel::OnThink()
{
if ( m_bCountDirty )
{
int nNumVisible = 0;
FOR_EACH_VEC( m_vecBlueRoundIndicators, i )
{
if ( m_vecBlueRoundIndicators[i]->IsVisible() )
++nNumVisible;
}
if ( nNumVisible != mp_winlimit.GetInt() )
{
InvalidateLayout();
m_bCountDirty = false;
}
}
}
void CRoundCounterPanel::FireGameEvent(IGameEvent * event )
{
if ( FStrEq( event->GetName(), "winlimit_changed" ) ) // Resize if the win limit changes
{
m_bCountDirty = true;
}
else if ( FStrEq( event->GetName(), "winpanel_show_scores" ) // Conditionally hide the win markers
|| FStrEq( event->GetName(), "stop_watch_changed" ) // Match the timing of the win panel "Ding!" when the scores update
|| FStrEq( event->GetName(), "teamplay_round_start" ) ) // Make sure we're accurate when the round starts in case the hud event didnt happen
{
InvalidateLayout( true );
}
}
//-----------------------------------------------------------------------------
// Purpose: Layout the round panels
//-----------------------------------------------------------------------------
void CRoundCounterPanel::LayoutPanels( ImageVector& vecImages, EAlignment eAlignment, int nStartPos, int nMaxWide )
{
if ( !mp_winlimit.GetInt() )
return;
FOR_EACH_VEC( vecImages, i )
{
Panel* pPanel = vecImages[ i ];
const int nXStartPos = eAlignment == ALIGN_EAST ? nStartPos : nStartPos;
const int nStep = ( nMaxWide / mp_winlimit.GetInt() );
const int nXOffset = nStep * i;
// Step out the panels by the steph width
int nXPos = eAlignment == ALIGN_EAST ? nXStartPos + nXOffset - ( pPanel->GetWide() / 2 ) + ( nStep / 2 )
: nXStartPos - nXOffset - ( pPanel->GetWide() / 2 ) - ( nStep / 2 );
pPanel->SetPos( nXPos, pPanel->GetYPos() );
}
}
DECLARE_HUDELEMENT( CTFHudMatchStatus );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudMatchStatus::CTFHudMatchStatus(const char *pElementName)
: CHudElement(pElementName)
, BaseClass(NULL, "HudMatchStatus")
, m_pTimePanel( NULL )
, m_bUseMatchHUD( false )
, m_eMatchGroupSettings( k_nMatchGroup_Invalid )
{
Panel *pParent = g_pClientMode->GetViewport();
SetParent(pParent);
SetHiddenBits( HIDEHUD_MISCSTATUS );
m_pMatchStartModelPanel = new CModelPanel( this, "MatchDoors" );
m_pRoundCounter = new CRoundCounterPanel( this, "RoundCounter" );
m_pTimePanel = new CTFHudTimeStatus( this, "ObjectiveStatusTimePanel" );
m_pRoundSignModel = new CModelPanel( this, "RoundSignModel" );
m_pTeamStatus = new CTFTeamStatus( this, "TeamStatus" );
m_pBlueTeamPanel = new vgui::EditablePanel( this, "BlueTeamPanel" );
m_pPlayerListBlue = new vgui::SectionedListPanel( m_pBlueTeamPanel, "BluePlayerList" );
m_pBlueLeaderAvatarImage = new CAvatarImagePanel( m_pBlueTeamPanel, "BlueLeaderAvatar" );
m_pBlueLeaderAvatarBG = new EditablePanel( m_pBlueTeamPanel, "BlueLeaderAvatarBG" );
m_pBlueTeamImage = new ImagePanel( m_pBlueTeamPanel, "BlueTeamImage" );
m_pBlueTeamName = new CExLabel( m_pBlueTeamPanel, "BlueTeamLabel", "" );
m_pRedTeamPanel = new vgui::EditablePanel( this, "RedTeamPanel" );
m_pPlayerListRed = new vgui::SectionedListPanel( m_pRedTeamPanel, "RedPlayerList" );
m_pRedLeaderAvatarImage = new CAvatarImagePanel( m_pRedTeamPanel, "RedLeaderAvatar" );
m_pRedLeaderAvatarBG = new EditablePanel( m_pRedTeamPanel, "RedLeaderAvatarBG" );
m_pRedTeamImage = new ImagePanel( m_pRedTeamPanel, "RedTeamImage" );
m_pRedTeamName = new CExLabel( m_pRedTeamPanel, "RedTeamLabel", "" );
m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) );
m_mapAvatarsToImageList.RemoveAll();
ListenForGameEvent( "teamplay_round_start" );
ListenForGameEvent( "restart_timer_time" );
ListenForGameEvent( "show_match_summary" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFHudMatchStatus::~CTFHudMatchStatus()
{
if ( NULL != m_pImageList )
{
delete m_pImageList;
m_pImageList = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::Reset()
{
SetPanelsVisible();
if ( m_pTimePanel )
{
m_pTimePanel->Reset();
}
if ( m_pTeamStatus )
{
m_pTeamStatus->Reset();
}
CHudElement::Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::SetPanelsVisible()
{
m_pRoundCounter->SetVisible( ShouldUseMatchHUD() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
KeyValues *pConditions = NULL;
if ( ShouldUseMatchHUD() )
{
pConditions = new KeyValues( "conditions" );
AddSubKeyNamed( pConditions, "if_match" );
const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
if ( pMatchDesc )
{
if ( pMatchDesc->m_params.m_pmm_match_group_size->GetInt() > 12 )
{
AddSubKeyNamed( pConditions, "if_large" );
}
}
}
// load control settings...
LoadControlSettings( "resource/UI/HudMatchStatus.res", NULL, NULL, pConditions );
if ( pConditions )
{
pConditions->deleteThis();
}
if ( m_pImageList )
delete m_pImageList;
m_pImageList = new ImageList( false );
m_mapAvatarsToImageList.RemoveAll();
m_pPlayerListBlue->SetImageList( m_pImageList, false );
m_pPlayerListRed->SetImageList( m_pImageList, false );
InitPlayerList( m_pPlayerListBlue, TF_TEAM_BLUE );
InitPlayerList( m_pPlayerListRed, TF_TEAM_RED );
m_hPlayerListFont = pScheme->GetFont( "Default", true );
UpdatePlayerList();
UpdateTeamInfo();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::PerformLayout()
{
BaseClass::PerformLayout();
SetPanelsVisible();
}
bool CTFHudMatchStatus::ShouldDraw( void )
{
// Force to draw during match summary so the doors show up. This panel
// will try to hide itself if you're dead, but we want to ignore that
// behavior and force us to draw.
if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
return true;
if ( gViewPortInterface->GetActivePanel() )
return false;
return CHudElement::ShouldDraw();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::OnThink()
{
if ( !TFGameRules() )
return;
bool bReload = false;
bool bUseMatchHUD = ShouldUseMatchHUD();
if ( bUseMatchHUD != m_bUseMatchHUD )
{
m_bUseMatchHUD = bUseMatchHUD;
bReload = true;
}
EMatchGroup eCurrentGroup = TFGameRules()->GetCurrentMatchGroup();
if ( eCurrentGroup != m_eMatchGroupSettings )
{
m_eMatchGroupSettings = eCurrentGroup;
bReload = true;
}
if ( bReload )
{
InvalidateLayout( false, true );
// The KOTH timers are their own hud element
CTFHudKothTimeStatus *pKothHUD = GET_HUDELEMENT( CTFHudKothTimeStatus );
if ( pKothHUD )
{
pKothHUD->InvalidateLayout( false, true );
}
}
// check for an active timer and turn the time panel on or off if we need to
if ( m_pTimePanel )
{
// Don't draw in freezecam, or when the game's not running
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
bool bDisplayTimer = !( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM );
if ( TeamplayRoundBasedRules()->IsInTournamentMode() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
{
bDisplayTimer = false;
}
if ( bDisplayTimer )
{
// is the time panel still pointing at an active timer?
int iCurrentTimer = m_pTimePanel->GetTimerIndex();
CTeamRoundTimer *pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( iCurrentTimer ) );
if ( pTimer && !pTimer->IsDormant() && !pTimer->IsDisabled() && pTimer->ShowInHud() )
{
// the current timer is fine, make sure the panel is visible
bDisplayTimer = true;
}
else if ( ObjectiveResource() )
{
// check for a different timer
int iActiveTimer = ObjectiveResource()->GetTimerToShowInHUD();
pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( iActiveTimer ) );
bDisplayTimer = ( iActiveTimer != 0 && pTimer && !pTimer->IsDormant() );
m_pTimePanel->SetTimerIndex( iActiveTimer );
}
}
if ( bDisplayTimer && !TFGameRules()->ShowMatchSummary() )
{
if ( !TFGameRules()->IsInKothMode() )
{
if ( !m_pTimePanel->IsVisible() )
{
m_pTimePanel->SetVisible( true );
// If our spectator GUI is visible, invalidate its layout so that it moves the reinforcement label
if ( g_pSpectatorGUI )
{
g_pSpectatorGUI->InvalidateLayout();
}
}
}
else
{
bool bVisible = TeamplayRoundBasedRules()->IsInWaitingForPlayers();
if ( m_pTimePanel->IsVisible() != bVisible )
{
m_pTimePanel->SetVisible( bVisible );
// If our spectator GUI is visible, invalidate its layout so that it moves the reinforcement label
if ( g_pSpectatorGUI )
{
g_pSpectatorGUI->InvalidateLayout();
}
}
}
}
else
{
if ( m_pTimePanel->IsVisible() )
{
m_pTimePanel->SetVisible( false );
}
}
}
BaseClass::OnThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::FireGameEvent( IGameEvent * event )
{
if ( !ShouldUseMatchHUD() )
return;
if ( FStrEq("teamplay_round_start", event->GetName() ) )
{
// Drop the round sign right when the match starts on rounds > 1
if ( TFGameRules()->GetRoundsPlayed() > 0 )
{
ShowRoundSign( TFGameRules()->GetRoundsPlayed() );
}
}
else if ( FStrEq( "restart_timer_time", event->GetName() ) )
{
HandleCountdown( event->GetInt( "time" ) );
}
else if ( FStrEq( "show_match_summary", event->GetName() ) )
{
if ( m_pBlueTeamPanel )
{
m_pBlueTeamPanel->SetVisible( false );
}
if ( m_pRedTeamPanel )
{
m_pRedTeamPanel->SetVisible( false );
}
const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
bool bForceDoors = false;
#ifdef STAGING_ONLY
bForceDoors = tf_test_match_summary.GetBool();
#endif
if ( bForceDoors || ( pMatchDesc && pMatchDesc->m_params.m_bShowPostRoundDoors ) )
{
if ( TFGameRules() && TFGameRules()->MapHasMatchSummaryStage() && ( bForceDoors || pMatchDesc->m_params.m_bUseMatchSummaryStage ) )
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchWinDoors", false );
}
else
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchWinDoors_NoOpen", false );
}
}
}
}
void CTFHudMatchStatus::HandleCountdown( int nTime )
{
// Update the timer
SetDialogVariable( "countdown", nTime );
switch ( nTime )
{
case 2:
// Drop the round sign with 2 seconds to go on the 1st round
if ( TFGameRules()->GetRoundsPlayed() == 0 )
{
ShowRoundSign( TFGameRules()->GetRoundsPlayed() );
}
break;
case 10:
if ( TFGameRules()->GetRoundsPlayed() == 0 )
{
ShowMatchStartDoors();
}
else
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowCountdown", false );
}
break;
}
}
#ifdef STAGING_ONLY
ConVar tf_comp_door_skin_override( "tf_comp_door_skin_override", "-1", 0, "Skin override for the competitive doors. Set to -1 to not override calculated skin" );
ConVar tf_comp_door_bodygroup_override( "tf_comp_door_bodygroup_override", "-1", 0, "Bodygroup override for the competitive doors. Set to -1 to not override calculated skin" );
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::ShowMatchStartDoors()
{
if ( TFGameRules()->GetCurrentMatchGroup() == k_nMatchGroup_Invalid )
return;
const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
int nSkin = 0;
int nSubModel = 0;
if ( pMatchDesc->BGetRoundDoorParameters( nSkin, nSubModel ) )
{
#ifdef STAGING_ONLY
if ( tf_comp_door_skin_override.GetInt() != -1 )
{
nSkin = tf_comp_door_skin_override.GetInt();
}
if ( tf_comp_door_bodygroup_override.GetInt() != -1 )
{
nSubModel = tf_comp_door_bodygroup_override.GetInt();
}
#endif
UpdatePlayerList();
UpdateTeamInfo();
if ( m_pMatchStartModelPanel->m_hModel == NULL )
{
m_pMatchStartModelPanel->UpdateModel();
}
m_pMatchStartModelPanel->SetBodyGroup( "logos", nSubModel );
m_pMatchStartModelPanel->UpdateModel();
m_pMatchStartModelPanel->SetSkin( nSkin );
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchStartDoors", false );
// Hide the class selection panel. It sorts weird with the doors, and we dont have time to figure out why.
gViewPortInterface->ShowPanel( PANEL_CLASS_RED, false );
gViewPortInterface->ShowPanel( PANEL_CLASS_BLUE, false );
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( pLocalPlayer )
{
pLocalPlayer->EmitSound( pMatchDesc->m_params.m_pszMatchStartSound );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Show the round sign with the specified round number
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::ShowRoundSign( int nRoundNumber )
{
if ( TFGameRules()->GetCurrentMatchGroup() == k_nMatchGroup_Invalid )
return;
if ( !m_pRoundSignModel || !m_pRoundSignModel->m_pModelInfo )
return;
Assert( TFGameRules()->GetRoundsPlayed() >= 0 && TFGameRules()->GetRoundsPlayed() <= 6 );
int nSkin = 0;
int nBodyGroup = 0;
if ( GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() )->BGetRoundStartBannerParameters( nSkin, nBodyGroup ) )
{
if ( m_pRoundSignModel->m_hModel == NULL )
{
m_pRoundSignModel->UpdateModel();
}
// Change the skin and bodygroup to be correct for the mode and round
m_pRoundSignModel->SetBodyGroup( "logos", nBodyGroup );
m_pRoundSignModel->m_pModelInfo->m_nSkin = nSkin;
// Make the model actually update with the new look
m_pRoundSignModel->SetPanelDirty();
m_pRoundSignModel->UpdateModel();
// Play the sign drop anim
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence(this, "HudTournament_ShowRoundSign", false);
}
}
//-----------------------------------------------------------------------------
// Purpose: Used for sorting players
//-----------------------------------------------------------------------------
bool TFPlayerSortFunc( vgui::SectionedListPanel *list, int itemID1, int itemID2 )
{
KeyValues *it1 = list->GetItemData( itemID1 );
KeyValues *it2 = list->GetItemData( itemID2 );
Assert( it1 && it2 );
// first compare score
int v1 = it1->GetInt( "score" );
int v2 = it2->GetInt( "score" );
if ( v1 > v2 )
return true;
else if ( v1 < v2 )
return false;
// if score is the same, use player index to get deterministic sort
int iPlayerIndex1 = it1->GetInt( "playerIndex" );
int iPlayerIndex2 = it2->GetInt( "playerIndex" );
return ( iPlayerIndex1 > iPlayerIndex2 );
}
//-----------------------------------------------------------------------------
// Purpose: Inits the player list in a list panel
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::InitPlayerList( SectionedListPanel *pPlayerList, int nTeam )
{
pPlayerList->SetVerticalScrollbar( false );
pPlayerList->RemoveAll();
pPlayerList->RemoveAllSections();
pPlayerList->AddSection( 0, "Players", TFPlayerSortFunc );
pPlayerList->SetSectionAlwaysVisible( 0, true );
pPlayerList->SetSectionDrawDividerBar( 0, false );
pPlayerList->SetBorder( NULL );
pPlayerList->SetMouseInputEnabled( false );
pPlayerList->SetClickable( false );
pPlayerList->AddColumnToSection( 0, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth );
pPlayerList->AddColumnToSection( 0, "spacer", "", 0, m_iSpacerWidth );
// the player avatar is always a fixed size, so as we change resolutions we need to vary the size of the name column to adjust the total width of all the columns
int nExtraSpace = pPlayerList->GetWide() - m_iAvatarWidth - m_iSpacerWidth - m_iNameWidth - ( 2 * SectionedListPanel::COLUMN_DATA_INDENT ); // the SectionedListPanel will indent the columns on either end by SectionedListPanel::COLUMN_DATA_INDENT
pPlayerList->AddColumnToSection( 0, "name", "", 0, m_iNameWidth + nExtraSpace );
}
//-----------------------------------------------------------------------------
// Purpose: Updates the player list
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::UpdatePlayerList()
{
m_pPlayerListRed->RemoveAll();
m_pPlayerListRed->ClearAllColorOverrideForCell();
m_pPlayerListBlue->RemoveAll();
m_pPlayerListBlue->ClearAllColorOverrideForCell();
if ( !g_TF_PR )
return;
for ( int playerIndex = 1; playerIndex <= MAX_PLAYERS; playerIndex++ )
{
if ( g_PR->IsConnected( playerIndex ) )
{
SectionedListPanel *pPlayerList = NULL;
int nTeam = g_PR->GetTeam( playerIndex );
switch ( nTeam )
{
case TF_TEAM_BLUE:
pPlayerList = m_pPlayerListBlue;
break;
case TF_TEAM_RED:
pPlayerList = m_pPlayerListRed;
break;
}
if ( null == pPlayerList )
continue;
KeyValues *pKeyValues = new KeyValues( "data" );
pKeyValues->SetInt( "playerIndex", playerIndex );
pKeyValues->SetString( "name", g_TF_PR->GetPlayerName( playerIndex ) );
UpdatePlayerAvatar( playerIndex, pKeyValues );
int itemID = pPlayerList->AddItem( 0, pKeyValues );
pPlayerList->SetItemFgColor( itemID, g_PR->GetTeamColor( nTeam ) );
pPlayerList->SetItemBgColor( itemID, Color( 120, 120, 120, 80 ) );
pPlayerList->SetItemBgHorizFillInset( itemID, m_iHorizFillInset );
pPlayerList->SetItemFont( itemID, m_hPlayerListFont );
pKeyValues->deleteThis();
}
}
m_pPlayerListRed->SetSectionFgColor( 0, g_PR->GetTeamColor( TF_TEAM_RED ) );
m_pPlayerListBlue->SetSectionFgColor( 0, g_PR->GetTeamColor( TF_TEAM_BLUE ) );
}
//-----------------------------------------------------------------------------
// Purpose: Updates the player list
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::UpdatePlayerAvatar( int playerIndex, KeyValues *kv )
{
// Update their avatar
if ( kv && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() )
{
player_info_t pi;
if ( engine->GetPlayerInfo( playerIndex, &pi ) )
{
if ( pi.friendsID )
{
CSteamID steamIDForPlayer( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
// See if we already have that avatar in our list
int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer );
int iImageIndex;
if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() )
{
CAvatarImage *pImage = new CAvatarImage();
pImage->SetAvatarSteamID( steamIDForPlayer );
pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling
iImageIndex = m_pImageList->AddImage( pImage );
m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex );
}
else
{
iImageIndex = m_mapAvatarsToImageList[iMapIndex];
}
kv->SetInt( "avatar", iImageIndex );
CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex );
pAvIm->UpdateFriendStatus();
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFHudMatchStatus::UpdateTeamInfo()
{
for ( int teamIndex = TF_TEAM_RED; teamIndex <= TF_TEAM_BLUE; teamIndex++ )
{
C_TFTeam *team = GetGlobalTFTeam( teamIndex );
if ( team )
{
// choose dialog variables to set depending on team
const char *pDialogVarTeamName = "";
vgui::EditablePanel *pPanel = NULL;
switch ( teamIndex )
{
case TF_TEAM_RED:
pDialogVarTeamName = "redteamname";
pPanel = m_pRedTeamPanel;
break;
case TF_TEAM_BLUE:
pDialogVarTeamName = "blueteamname";
pPanel = m_pBlueTeamPanel;
break;
default:
Assert( false );
break;
}
// set the team name
if ( pPanel )
{
pPanel->SetDialogVariable( pDialogVarTeamName, team->Get_Localized_Name() );
}
}
}
bool bShowAvatars = g_TF_PR && g_TF_PR->HasPremadeParties();
if ( bShowAvatars )
{
m_pRedLeaderAvatarImage->SetPlayer( GetSteamIDForPlayerIndex( g_TF_PR->GetPartyLeaderRedTeamIndex() ), k_EAvatarSize64x64 );
m_pRedLeaderAvatarImage->SetShouldDrawFriendIcon( false );
m_pBlueLeaderAvatarImage->SetPlayer( GetSteamIDForPlayerIndex( g_TF_PR->GetPartyLeaderBlueTeamIndex() ), k_EAvatarSize64x64 );
m_pBlueLeaderAvatarImage->SetShouldDrawFriendIcon( false );
}
m_pRedLeaderAvatarImage->SetVisible( bShowAvatars );
m_pRedLeaderAvatarBG->SetVisible( bShowAvatars );
m_pRedTeamName->SetVisible( bShowAvatars );
m_pRedTeamImage->SetVisible( !bShowAvatars );
m_pBlueLeaderAvatarImage->SetVisible( bShowAvatars );
m_pBlueLeaderAvatarBG->SetVisible( bShowAvatars );
m_pBlueTeamName->SetVisible( bShowAvatars );
m_pBlueTeamImage->SetVisible( !bShowAvatars );
}