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.
594 lines
15 KiB
594 lines
15 KiB
//========= 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; |
|
}
|
|
|