diff options
Diffstat (limited to 'game/client/tf/vgui/tf_matchmaking_dashboard.cpp')
| -rw-r--r-- | game/client/tf/vgui/tf_matchmaking_dashboard.cpp | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_matchmaking_dashboard.cpp b/game/client/tf/vgui/tf_matchmaking_dashboard.cpp new file mode 100644 index 0000000..ca10e8b --- /dev/null +++ b/game/client/tf/vgui/tf_matchmaking_dashboard.cpp @@ -0,0 +1,594 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "tf_shareddefs.h" +#include "tf_matchmaking_dashboard.h" +#include "tf_gamerules.h" +#include "ienginevgui.h" +#include "clientmode_tf.h" +#include "tf_hud_disconnect_prompt.h" +#include "tf_gc_client.h" +#include "tf_party.h" +#include "../vgui2/src/VPanel.h" + +using namespace vgui; +using namespace GCSDK; + +ConVar tf_mm_dashboard_spew_enabled( "tf_mm_dashboard_spew_enabled", "0", FCVAR_ARCHIVE ); +#define MMDashboardSpew(...) \ + do { \ + if ( tf_mm_dashboard_spew_enabled.GetBool() ) \ + { \ + ConColorMsg( Color( 187, 80, 255, 255 ), "MMDashboard:" __VA_ARGS__ ); \ + } \ + } while(false) \ + +extern ConVar tf_mm_next_map_vote_time; + +#ifdef STAGING_ONLY +ConVar tf_mm_dashboard_force_show( "tf_mm_dashboard_force_show", "0", 0, "Force the mm dashboard to show" ); +ConVar tf_mm_popup_state_override( "tf_mm_popup_state_override", "", 0, "Force state on mm dashboard popup" ); +#endif + + + +bool BInEndOfMatch() +{ + const bool bInEndOfMatch = TFGameRules() && + TFGameRules()->State_Get() == GR_STATE_GAME_OVER && + GTFGCClientSystem()->BConnectedToMatchServer( false ); + + return bInEndOfMatch; +} + +//----------------------------------------------------------------------------- +// Purpose: Pnael that lives on the viewport that is a popup that we parent +// the MM dashboard panels to +//----------------------------------------------------------------------------- +class CMatchMakingHUDPopupContainer : public Panel +{ +public: + DECLARE_CLASS_SIMPLE( CMatchMakingHUDPopupContainer, Panel ); + CMatchMakingHUDPopupContainer() + : Panel( g_pClientMode->GetViewport(), "MMDashboardPopupContainer" ) + { + SetProportional( true ); + SetBounds( 0, 0, g_pClientMode->GetViewport()->GetWide(), g_pClientMode->GetViewport()->GetTall() ); + MakePopup(); + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( false ); // This can never be true + SetVisible( false ); + ivgui()->AddTickSignal( GetVPanel(), 100 ); + } + + virtual void OnTick() + { + BaseClass::OnThink(); + + bool bChildrenVisible = false; + int nCount = GetChildCount(); + for( int i=0; i < nCount && !bChildrenVisible; ++i ) + { + CExpandablePanel* pChild = assert_cast< CExpandablePanel* >( GetChild( i ) ); + bChildrenVisible = bChildrenVisible || pChild->BIsExpanded() || ( !pChild->BIsExpanded() && pChild->GetPercentAnimated() != 1.f ); + } + + SetVisible( bChildrenVisible ); + } +}; + +CMMDashboardParentManager::CMMDashboardParentManager() + : m_bAttachedToGameUI( false ) +{ + ListenForGameEvent( "gameui_activated" ); + ListenForGameEvent( "gameui_hidden" ); + + m_pHUDPopup = new CMatchMakingHUDPopupContainer(); +} + +//----------------------------------------------------------------------------- +// Purpose: Update who we need to parent the MM dashboard panels to +//----------------------------------------------------------------------------- +void CMMDashboardParentManager::FireGameEvent( IGameEvent *event ) +{ + if ( FStrEq( event->GetName(), "gameui_activated" ) ) + { + m_bAttachedToGameUI = false; + } + else if ( FStrEq( event->GetName(), "gameui_hidden" ) ) + { + m_bAttachedToGameUI = true; + } + + UpdateParenting(); +} + +void CMMDashboardParentManager::AddPanel( CExpandablePanel* pPanel ) +{ + m_vecPanels.Insert( pPanel ); + UpdateParenting(); +} + +void CMMDashboardParentManager::RemovePanel( CExpandablePanel* pPanel ) +{ + m_vecPanels.FindAndRemove( pPanel ); + m_vecPanels.RedoSort(); + UpdateParenting(); +} + +void CMMDashboardParentManager::PushModalFullscreenPopup( vgui::Panel* pPanel ) +{ + m_vecFullscreenPopups.AddToTail( pPanel ); + UpdateParenting(); +} + +void CMMDashboardParentManager::PopModalFullscreenPopup( vgui::Panel* pPanel ) +{ + m_vecFullscreenPopups.FindAndRemove( pPanel ); + UpdateParenting(); +} + +void CMMDashboardParentManager::UpdateParenting() +{ + m_bAttachedToGameUI ? AttachToGameUI() : AttachToTopMostPopup(); +} + +//----------------------------------------------------------------------------- +// Purpose: Parent the MM dashboard panels to the right panels +//----------------------------------------------------------------------------- +void CMMDashboardParentManager::AttachToGameUI() +{ + if ( !m_pHUDPopup ) + { + return; + } + + FOR_EACH_VEC( m_vecPanels, i ) + { + CExpandablePanel *pPanel = m_vecPanels[ i ]; + bool bKBInput = pPanel->IsKeyBoardInputEnabled(); + bool bMouseInput = pPanel->IsMouseInputEnabled(); + + pPanel->SetParent( (Panel*)m_pHUDPopup ); + + // Restore mouse, KV input sensitivity because MakePopup forces both to true + pPanel->SetKeyBoardInputEnabled( bKBInput ); + pPanel->SetMouseInputEnabled( bMouseInput ); + // Don't adopt the parent's proportionalness + pPanel->SetProportional( true ); + } +} + +void CMMDashboardParentManager::AttachToTopMostPopup() +{ + // Not being used. Hide it. + if ( m_pHUDPopup ) + { + m_pHUDPopup->SetVisible( false ); + } + + FOR_EACH_VEC( m_vecPanels, i ) + { + Panel *pPanel = m_vecPanels[ i ]; + // No longer a popup + surface()->ReleasePanel( pPanel->GetVPanel() ); + ((VPanel*)pPanel->GetVPanel())->SetPopup( false ); + + if ( m_vecFullscreenPopups.Count() ) + { + pPanel->SetParent( m_vecFullscreenPopups.Tail() ); + } + else + { + pPanel->SetParent( enginevgui->GetPanel( PANEL_GAMEUIDLL ) ); + } + pPanel->MoveToFront(); + // Don't adopt the parent's proportionalness + pPanel->SetProportional( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Snag the singleton CMMDashboardParentManager +//----------------------------------------------------------------------------- +CMMDashboardParentManager* GetMMDashboardParentManager() +{ + static CMMDashboardParentManager* s_pParentManager = NULL; + if ( !s_pParentManager ) + { + s_pParentManager = new CMMDashboardParentManager(); + } + + return s_pParentManager; +} + + +void CTFMatchmakingPopup::OnEnter() +{ + MMDashboardSpew( "Entering state %s\n", GetName() ); + + Update(); + + SetCollapsed( false ); +} + +void CTFMatchmakingPopup::OnUpdate() +{ +} + +void CTFMatchmakingPopup::OnExit() +{ + MMDashboardSpew( "Exiting state %s\n", GetName() ); + SetCollapsed( true ); +} + + +void CTFMatchmakingPopup::Update() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFMatchmakingPopup::CTFMatchmakingPopup( const char* pszName, const char* pszResFile ) + : CExpandablePanel( NULL, pszName ) + , m_pszResFile( pszResFile ) + , m_bActive( false ) +{ + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme"); + SetScheme(scheme); + ivgui()->AddTickSignal( GetVPanel(), 100 ); + + GetMMDashboardParentManager()->AddPanel( this ); + SetKeyBoardInputEnabled( false ); + + SetProportional( true ); + + ListenForGameEvent( "rematch_failed_to_create" ); + ListenForGameEvent( "party_updated" ); +} + +CTFMatchmakingPopup::~CTFMatchmakingPopup() +{ + GetMMDashboardParentManager()->RemovePanel( this ); +} + +void CTFMatchmakingPopup::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetMouseInputEnabled( true ); + LoadControlSettings( m_pszResFile ); + + // This cannot ever be true or else things get weird when in-game + SetKeyBoardInputEnabled( false ); + + if ( m_bActive ) + { + OnEnter(); + } + else + { + OnExit(); + } + + GetMMDashboardParentManager()->UpdateParenting(); +} + +void CTFMatchmakingPopup::OnThink() +{ + BaseClass::OnThink(); + + // Move us to be touching the bottom of the dashboard panel + // These panels have no relation whatsoever, so we're doing this manually + Panel* pDashboard = GetMMDashboard(); + int nNewYPos = Max( pDashboard->GetYPos() + pDashboard->GetTall() - YRES(10), YRES(-5) ); + SetPos( GetXPos(), nNewYPos ); + + if ( m_bActive ) + { + OnUpdate(); + } +} + +void CTFMatchmakingPopup::OnTick() +{ + BaseClass::OnTick(); + + bool bShouldBeActive = ShouldBeActve(); + if ( bShouldBeActive != m_bActive ) + { + if ( bShouldBeActive ) + { + m_bActive = true; + OnEnter(); + } + else + { + m_bActive = false; + OnExit(); + } + } + + SetMouseInputEnabled( ShouldBeActve() ); + SetKeyBoardInputEnabled( false ); // Never +} + + +void CTFMatchmakingPopup::OnCommand( const char *command ) +{ + if ( FStrEq( "join_match", command ) ) + { + JoinMatch(); + } + else if ( FStrEq( "abandon_match", command ) ) + { + GTFGCClientSystem()->RejoinLobby( false ); + } + else if ( FStrEq( "leave_queue", command ) ) + { + // Only the leader can leave the queue + if ( GTFGCClientSystem()->BIsPartyLeader() ) + { + switch( GTFGCClientSystem()->GetSearchMode() ) + { + case TF_Matchmaking_LADDER: + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_LADDER ); + break; + + case TF_Matchmaking_CASUAL: + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_CASUAL ); + break; + + default: + // Unhandled + Assert( false ); + break; + }; + } + } + else if( FStrEq( "rematch", command ) ) + { + if ( !GTFGCClientSystem()->BIsPartyLeader() ) + return; + + engine->ClientCmd( "rematch_vote 2" ); + } + else if ( FStrEq( "new_match", command ) ) + { + if ( !GTFGCClientSystem()->BIsPartyLeader() ) + return; + + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_SEARCHING ); + engine->ClientCmd( "rematch_vote 1" ); + } + else if ( FStrEq( "leave_party", command ) ) + { + // Leave current party and create a new one! + GTFGCClientSystem()->SendExitMatchmaking( false ); + return; + } +} + + +void CTFMatchmakingPopup::FireGameEvent( IGameEvent *pEvent ) +{ + if ( FStrEq( pEvent->GetName(), "rematch_failed_to_create" ) ) + { + // If the GC failed to create our rematch, then go ahead and requeue + if ( !GTFGCClientSystem()->BIsPartyLeader() ) + return; + + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_SEARCHING ); + } + else if ( FStrEq( pEvent->GetName(), "party_updated" ) ) + { + if ( ShouldBeActve() ) + { + Update(); + } + } +} + + +#ifdef STAGING_ONLY +CON_COMMAND( reload_mm_popup, "Reload the mm popup panel. Pass any 2nd argument to recreate the panel." ) +{ + bool bRecreate = false; + if ( args.ArgC() == 2 ) + { + bRecreate = true; + } + + auto& vecPopups = CreateMMPopupPanels( bRecreate ); + + if ( !bRecreate ) + { + FOR_EACH_VEC( vecPopups, i ) + { + vecPopups[ i ]->InvalidateLayout( true, true ); + } + } +} + +CON_COMMAND( reload_mm_dashboard, "Reload the mm join panel." ) +{ + GetMMDashboard()->InvalidateLayout( true, true ); +} +#endif + + +CTFMatchmakingDashboard::CTFMatchmakingDashboard() + : CExpandablePanel( NULL, "MMDashboard" ) +{ + SetKeyBoardInputEnabled( false ); + ivgui()->AddTickSignal( GetVPanel(), 100 ); + + GetMMDashboardParentManager()->AddPanel( this ); + + CreateMMPopupPanels(); + + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme"); + SetScheme(scheme); + SetProportional( true ); +} + +CTFMatchmakingDashboard::~CTFMatchmakingDashboard() +{ + GetMMDashboardParentManager()->RemovePanel( this ); +} + +void CTFMatchmakingDashboard::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetMouseInputEnabled( true ); + LoadControlSettings( "resource/UI/MatchMakingDashboard.res" ); + + // This cannot ever be true or else things get weird when in-game + SetKeyBoardInputEnabled( false ); + + GetMMDashboardParentManager()->UpdateParenting(); +} + +void CTFMatchmakingDashboard::OnCommand( const char *command ) +{ + if ( FStrEq( command, "disconnect" ) ) + { + CTFParty* pParty = GTFGCClientSystem()->GetParty(); + bool bInPartyOfMany = pParty && pParty->GetNumMembers() > 1; + + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() ); + if ( pMatchDesc && pMatchDesc->BShouldAutomaticallyRequeueOnMatchEnd() && !bInPartyOfMany ) + { + // If this is an auto-requeue match type, then assume hitting the "Disconnect" button + // means you are done playing entirely with MM. Close out to the main menu. + bool bNoPenalty = ( GTFGCClientSystem()->GetAssignedMatchAbandonStatus() != k_EAbandonGameStatus_AbandonWithPenalty ); + if ( bNoPenalty ) + { + GTFGCClientSystem()->EndMatchmaking( true ); + } + else + { + // Prompt if this would be considered an abandon with a penalty + CTFDisconnectConfirmDialog *pDialog = BuildDisconnectConfirmDialog(); + if ( pDialog ) + { + pDialog->Show(); + } + } + } + else + { + // Go back to the MM screens. The party leader will request the right wizard state + switch( GTFGCClientSystem()->GetSearchMode() ) + { + case TF_Matchmaking_LADDER: + if ( GTFGCClientSystem()->BIsPartyLeader() ) + { + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_LADDER ); + } + engine->ClientCmd_Unrestricted( "OpenMatchmakingLobby ladder" ); + break; + + case TF_Matchmaking_CASUAL: + if ( GTFGCClientSystem()->BIsPartyLeader() ) + { + GTFGCClientSystem()->RequestSelectWizardStep( TF_Matchmaking_WizardStep_CASUAL ); + } + engine->ClientCmd_Unrestricted( "OpenMatchmakingLobby casual" ); + break; + + default: + // Unhandled + GTFGCClientSystem()->EndMatchmaking(); + Assert( false ); + break; + }; + } + + // Disconnect from the server + engine->DisconnectInternal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out if we should be visible and require mouse input +//----------------------------------------------------------------------------- +void CTFMatchmakingDashboard::OnTick() +{ + bool bInEndOfMatch = TFGameRules() && TFGameRules()->State_Get() == GR_STATE_GAME_OVER; + + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules() ? TFGameRules()->GetCurrentMatchGroup() : k_nMatchGroup_Invalid ); + bool bShouldBeVisible = GTFGCClientSystem()->BConnectedToMatchServer( false ) + && bInEndOfMatch + && pMatchDesc + && pMatchDesc->BUsesDashboard(); + +#ifdef STAGING_ONLY + bShouldBeVisible |= tf_mm_dashboard_force_show.GetBool(); +#endif + + if ( BIsExpanded() && !bShouldBeVisible ) + { + SetCollapsed( true ); + } + else if ( !BIsExpanded() && bShouldBeVisible ) + { + SetCollapsed( false ); + } + + SetKeyBoardInputEnabled( false ); + SetMouseInputEnabled( BIsExpanded() ); +} + +CUtlVector< IMMPopupFactory* > IMMPopupFactory::s_vecPopupFactories; + +//----------------------------------------------------------------------------- +// Purpose: Snag the singleton CTFMatchmakingPopup +//----------------------------------------------------------------------------- +CUtlVector< CTFMatchmakingPopup* >& CreateMMPopupPanels( bool bRecreate /*= false*/ ) +{ + static CUtlVector< CTFMatchmakingPopup* > s_vecPopups; + + if ( bRecreate && s_vecPopups.Count() ) + { + FOR_EACH_VEC( s_vecPopups, i ) + { + s_vecPopups[ i ]->MarkForDeletion(); + } + s_vecPopups.Purge(); + } + + + if ( s_vecPopups.IsEmpty() ) + { + FOR_EACH_VEC( IMMPopupFactory::s_vecPopupFactories, i ) + { + s_vecPopups.AddToTail( IMMPopupFactory::s_vecPopupFactories[i]->Create() ); + } + } + + return s_vecPopups; +} + + +//----------------------------------------------------------------------------- +// Purpose: Snag the singleton CTFMatchmakingDashboard +//----------------------------------------------------------------------------- +CTFMatchmakingDashboard* GetMMDashboard() +{ + static CTFMatchmakingDashboard* s_pDashboardPanel = NULL; + if ( !s_pDashboardPanel ) + { + s_pDashboardPanel = new CTFMatchmakingDashboard(); + } + + return s_pDashboardPanel; +} |