summaryrefslogtreecommitdiff
path: root/game/client/tf/tf_hud_match_status.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/tf_hud_match_status.cpp')
-rw-r--r--game/client/tf/tf_hud_match_status.cpp907
1 files changed, 907 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_match_status.cpp b/game/client/tf/tf_hud_match_status.cpp
new file mode 100644
index 0000000..cd41174
--- /dev/null
+++ b/game/client/tf/tf_hud_match_status.cpp
@@ -0,0 +1,907 @@
+//========= 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 );
+}