diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/tf_hud_match_status.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/tf_hud_match_status.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_match_status.cpp | 907 |
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 ); +} |