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.

595 lines
15 KiB

5 years ago
//========= 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;
}