diff options
Diffstat (limited to 'game/client/tf/vgui/tf_lobbypanel.cpp')
| -rw-r--r-- | game/client/tf/vgui/tf_lobbypanel.cpp | 1107 |
1 files changed, 1107 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_lobbypanel.cpp b/game/client/tf/vgui/tf_lobbypanel.cpp new file mode 100644 index 0000000..8f8a182 --- /dev/null +++ b/game/client/tf/vgui/tf_lobbypanel.cpp @@ -0,0 +1,1107 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +#include "tf_party.h" +#include "tf_item_inventory.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/SectionedListPanel.h" +#include "vgui/IInput.h" +#include <vgui_controls/ImageList.h> +#include "vgui_avatarimage.h" +#include "tf_ladder_data.h" +#include "vgui_controls/Menu.h" +#include "tf_match_description.h" +#include "tf_badge_panel.h" +#include "tf_controls.h" + +#include "tf_lobbypanel.h" +#include "tf_lobby_container_frame.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +ConVar tf_matchmaking_join_in_progress( "tf_matchmaking_join_in_progress", "0", FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Saved preference for if the player wants to join games in progress." ); + +const int k_iPopIndex_Any = -1000; +const int k_iPopIndex_OnlyNotYetCompleted = -1001; +const int k_iPopIndex_AnyNormal = -1002; +const int k_iPopIndex_AnyIntermediate = -1003; +const int k_iPopIndex_AnyAdvanced = -1004; +const int k_iPopIndex_AnyExpert = -1005; +const int k_iPopIndex_AnyHaunted = -1006; + +static void GetMvmChallengeSet( int idxChallenge, CMvMMissionSet &result ) +{ + result.Clear(); + + if ( idxChallenge >= 0 ) + { + result.SetMissionBySchemaIndex( idxChallenge, true ); + return; + } + + bool bMannUP = GTFGCClientSystem()->GetSearchPlayForBraggingRights(); +#ifdef USE_MVM_TOUR + int idxTour = GTFGCClientSystem()->GetSearchMannUpTourIndex(); + Assert( bMannUP || idxTour < 0 ); +#endif // USE_MVM_TOUR + +#ifdef USE_MVM_TOUR + uint32 nNotCompletedChallenges = ~0U; + CTFParty *pParty = GTFGCClientSystem()->GetParty(); + if ( pParty ) + { + for ( int i = 0 ; i < pParty->GetNumMembers() ; ++i ) + { + nNotCompletedChallenges &= ~pParty->Obj().members( i ).completed_missions(); + } + } + else + { + if ( idxTour >= 0 ) + { + uint32 nTours = 0, nCompletedChallenge = 0; + GTFGCClientSystem()->BGetLocalPlayerBadgeInfoForTour( idxTour, &nTours, &nCompletedChallenge ); + + nNotCompletedChallenges = ~nCompletedChallenge; + } + } +#endif // USE_MVM_TOUR + + for ( int i = 0 ; i < GetItemSchema()->GetMvmMissions().Count() ; ++i ) + { + const MvMMission_t &chal = GetItemSchema()->GetMvmMissions()[ i ]; + + // Cannot select non-MannUp missions in mann up mode +#ifdef USE_MVM_TOUR + int iBadgeSlot = (idxTour < 0) ? -1 : GetItemSchema()->GetMvmMissionBadgeSlotForTour( idxTour, i ); + if ( bMannUP && iBadgeSlot < 0 ) + continue; +#else // new mm + bool bIsChallengeInMannUp = chal.m_unMannUpPoints > 0; + if ( bMannUP && !bIsChallengeInMannUp ) + continue; +#endif // USE_MVM_TOUR + + // Does this challenge fit the search criteria? + bool bSelect = false; + switch ( idxChallenge ) + { + case k_iPopIndex_Any: + bSelect = true; + break; + case k_iPopIndex_OnlyNotYetCompleted: +#ifdef USE_MVM_TOUR + if ( iBadgeSlot >= 0 ) + { + int iChallengeBit = ( 1 << iBadgeSlot ); + if ( nNotCompletedChallenges & iChallengeBit ) + { + bSelect = true; + } + } +#endif // USE_MVM_TOUR + break; + + case k_iPopIndex_AnyNormal: + bSelect = ( chal.m_eDifficulty == k_EMvMChallengeDifficulty_Normal ); + break; + + case k_iPopIndex_AnyIntermediate: + bSelect = ( chal.m_eDifficulty == k_EMvMChallengeDifficulty_Intermediate ); + break; + + case k_iPopIndex_AnyAdvanced: + bSelect = ( chal.m_eDifficulty == k_EMvMChallengeDifficulty_Advanced ); + break; + + case k_iPopIndex_AnyExpert: + bSelect = ( chal.m_eDifficulty == k_EMvMChallengeDifficulty_Expert ); + break; + + case k_iPopIndex_AnyHaunted: + bSelect = ( chal.m_eDifficulty == k_EMvMChallengeDifficulty_Haunted ); + break; + + default: + Assert( false ); + } + result.SetMissionBySchemaIndex( i, bSelect ); + } +} + +#ifdef ENABLE_GC_MATCHMAKING + +Color s_colorBannedPlayerListItem( 250, 50, 45, 255 ); +Color s_colorPlayerListItem( 255, 255, 255, 255 ); +Color s_colorChatRemovedFromQueue( 200, 10, 10, 255 ); +Color s_colorChatAddedToQueue( 10, 200, 10, 255 ); +Color s_colorChatPlayerJoinedParty( 255, 255, 255, 255 ); +Color s_colorChatPlayerJoinedPartyName( 200, 200, 10, 255 ); +Color s_colorChatPlayerLeftParty( 255, 255, 255, 255 ); +Color s_colorChatPlayerLeftPartyName( 200, 200, 10, 255 ); +Color s_colorChatPlayerChatName( 200, 200, 10, 255 ); +Color s_colorChatPlayerChatText( 180, 180, 180, 255 ); +Color s_colorChatDefault( 180, 180, 180, 255 ); +Color s_colorChallengeForegroundEnabled( 255, 255, 255, 255 ); +Color s_colorChallengeForegroundHaunted( 135, 79, 173, 255 ); +Color s_colorChallengeForegroundDisabled( 100, 100, 100, 128 ); +Color s_colorChallengeHeader( 250, 114, 45, 255 ); + +static void GetPlayerNameForSteamID( wchar_t *wCharPlayerName, int nBufSizeBytes, const CSteamID &steamID ) +{ + const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID ); + V_UTF8ToUnicode( pszName, wCharPlayerName, nBufSizeBytes ); +} + +CBaseLobbyPanel::CBaseLobbyPanel( vgui::Panel *pParent, CBaseLobbyContainerFrame* pContainer ) + : vgui::PropertySheet( pParent, "LobbyPanel" ), m_sPersonaStateChangedCallback( this, &CBaseLobbyPanel::OnPersonaStateChanged ) + , m_pContainer( pContainer ) +{ + //ListenForGameEvent( "lobby_updated" ); + ListenForGameEvent( "party_updated" ); + ListenForGameEvent( "mm_lobby_chat" ); + ListenForGameEvent( "mm_lobby_member_join" ); + ListenForGameEvent( "mm_lobby_member_leave" ); + + m_iWritingPanel = 0; + + m_pSearchActiveGroupBox = NULL; + m_pSearchActiveTitleLabel = NULL; + m_pSearchActivePenaltyLabel = NULL; + m_pPartyHasLowPriority = NULL; + + m_pJoinLateCheckButton = NULL; + m_pJoinLateValueLabel = NULL; + + m_pInviteButton = NULL; + m_pChatLog = NULL; + //m_nFirstMapShown = -1; + m_pImageList = NULL; + + m_iImageIsBanned = -1; + m_iImageRadioButtonYes = -1; + m_iImageRadioButtonNo = -1; + m_iImageCheckBoxDisabled = -1; + m_iImageCheckBoxYes = -1; + m_iImageCheckBoxNo = -1; + m_iImageCheckBoxMixed = -1; + m_iImageNew = -1; + m_iImageNo = -1; + + m_fontPlayerListItem = 0; + + // Party + m_pPartyActiveGroupBox = new vgui::EditablePanel( this, "PartyActiveGroupBox" ); + vgui::EditablePanel *pPartyGroupPanel = new vgui::EditablePanel( m_pPartyActiveGroupBox, "PartyGroupBox" ); + m_pChatPlayerList = new vgui::SectionedListPanel( pPartyGroupPanel, "PartyPlayerList" ); + m_pChatTextEntry = new ChatTextEntry( m_pPartyActiveGroupBox, "ChatTextEntry" ); Assert( m_pChatTextEntry ); + m_pChatLog = new ChatLog( m_pPartyActiveGroupBox, "ChatLog" ); Assert( m_pChatLog ); + + m_mapAvatarsToImageList.SetLessFunc( DefLessFunc(int) ); + + m_eCurrentPartyState = CSOTFParty_State_UI; + + m_flRefreshPlayerListTime = -1.f; + + m_pToolTip = new CMainMenuToolTip( this ); + vgui::EditablePanel* pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" ); + pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false ); + pToolTipEmbeddedPanel->SetMouseInputEnabled( false ); + m_pToolTip->SetEmbeddedPanel( pToolTipEmbeddedPanel ); + m_pToolTip->SetTooltipDelay( 0 ); +} + +CBaseLobbyPanel::~CBaseLobbyPanel() +{ + delete m_pImageList; + m_pImageList = NULL; +} + +void CBaseLobbyPanel::OnClickedOnPlayer() +{ + int iSelected = m_pChatPlayerList->GetSelectedItem(); + //m_pChatPlayerList->ClearSelection(); + if ( iSelected < 0 ) + return; + CSteamID steamID = SteamIDFromDecimalString( m_pChatPlayerList->GetItemData( iSelected )->GetString( "steamid", "" ) ); + if ( !steamID.IsValid() ) + return; + + vgui::Menu *menu = new vgui::Menu(this, "ContextMenu"); + + int x, y; + vgui::input()->GetCursorPos(x, y); + menu->SetPos(x, y); + + wchar_t wszLocalized[512]; + char szLocalized[512]; + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_ScoreBoard_Context_Trade" ), 0 ); + g_pVGuiLocalize->ConvertUnicodeToANSI( wszLocalized, szLocalized, sizeof( szLocalized ) ); + + menu->AddMenuItem( szLocalized, new KeyValues( "TradeWithUser", "steamid", CFmtStr( "%llu", steamID.ConvertToUint64() ).Access() ), this ); + + menu->SetVisible(true); +} + +void CBaseLobbyPanel::SetMatchmakingModeBackground() +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + // Get the background panel. + vgui::ImagePanel* pModeBackgroundImage = FindControl< vgui::ImagePanel >( "ModeBackgroundImage", true ); + if ( !pModeBackgroundImage ) + return; + + const char* pszImageName = NULL; + const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( GetMatchGroup() ); + if ( pMatchDesc && pMatchDesc->m_pProgressionDesc ) + { + // Get the level of the local player, and pull the background image out of the level + const LevelInfo_t& level = pMatchDesc->m_pProgressionDesc->YieldingGetLevelForSteamID( steamapicontext->SteamUser()->GetSteamID() ); + pszImageName = level.m_pszLobbyBackgroundImage; + } + + // Set the image name, if we got one, into the panel + if ( pszImageName ) + { + pModeBackgroundImage->SetImage( pszImageName ); + } + + // Only show if we got one (MvM doesn't have any) + pModeBackgroundImage->SetVisible( pszImageName != NULL ); +} + +//----------------------------------------------------------------------------- +void CBaseLobbyPanel::FireGameEvent( IGameEvent *event ) +{ + if ( !IsVisible() || !m_pContainer->IsVisible() ) + return; + + const char *pszEventName = event->GetName(); + if ( !Q_stricmp( pszEventName, "party_updated" ) ) + { + + CTFParty *pParty = GTFGCClientSystem()->GetParty(); + if ( pParty ) + { + if ( m_eCurrentPartyState != pParty->GetState() ) + { + wchar_t wszLocalized[512]; + + switch ( pParty->GetState() ) + { + case CSOTFParty_State_UI: + m_pChatLog->InsertColorChange( s_colorChatRemovedFromQueue ); + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_RemovedFromQueue" ), 0 ); + m_pChatLog->InsertString( wszLocalized ); + break; + + case CSOTFParty_State_FINDING_MATCH: + m_pChatLog->InsertColorChange( s_colorChatAddedToQueue ); + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_AddedToQueue" ), 0 ); + m_pChatLog->InsertString( wszLocalized ); + break; + + default: + Assert( false ); + case CSOTFParty_State_IN_MATCH: + break; + } + m_eCurrentPartyState = pParty->GetState(); + } + } + else + { + m_eCurrentPartyState = CSOTFParty_State_UI; + } + + UpdatePlayerList(); + WriteGameSettingsControls(); + return; + } + + if ( !Q_stricmp( pszEventName, "mm_lobby_chat" ) ) + { + CSteamID steamID = SteamIDFromDecimalString( event->GetString( "steamid", "0" ) ); + + const char *pszText = event->GetString( "text", "" ); + int l = V_strlen( pszText ); + if ( l > 0 ) + { + int nBufSize = l * sizeof(wchar_t) + 4; + wchar_t *wText = (wchar_t *)stackalloc( nBufSize ); + V_UTF8ToUnicode( pszText, wText, nBufSize ); + switch ( event->GetInt( "type", CTFGCClientSystem::k_eLobbyMsg_UserChat ) ) + { + default: + Assert( !"Unknown chat message type" ); + case CTFGCClientSystem::k_eLobbyMsg_SystemMsgFromLeader: + m_pChatLog->InsertColorChange( s_colorChatDefault ); + m_pChatLog->InsertString( wText ); + m_pChatLog->InsertString("\n"); + break; + + case CTFGCClientSystem::k_eLobbyMsg_UserChat: + { + + wchar_t wCharPlayerName[ 128 ]; + GetPlayerNameForSteamID( wCharPlayerName, sizeof(wCharPlayerName), steamID ); + m_pChatLog->InsertColorChange( s_colorChatPlayerChatName ); + m_pChatLog->InsertString( wCharPlayerName ); + m_pChatLog->InsertString( ": " ); + m_pChatLog->InsertColorChange( s_colorChatPlayerChatText ); + m_pChatLog->InsertString( wText ); + m_pChatLog->InsertString("\n"); + } break; + } + } + + return; + } + + if ( !Q_stricmp( pszEventName, "mm_lobby_member_join" ) ) + { + CSteamID steamID = SteamIDFromDecimalString( event->GetString( "steamid", "0" ) ); + + bool bSolo = false; + if ( steamID == steamapicontext->SteamUser()->GetSteamID() ) + { + m_pChatLog->SetText(""); + bSolo = ( event->GetInt( "solo", 0 ) != 0 ); + } + + wchar_t wszLocalized[512]; + + // An empty lobby by ourselves? + if ( bSolo ) + { + m_pChatLog->InsertColorChange( s_colorChatDefault ); + + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_StartSearchChat" ), 0 ); + m_pChatLog->InsertString( wszLocalized ); + + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_InviteFriendsChat" ), 0 ); + m_pChatLog->InsertString( wszLocalized ); + } + else + { + wchar_t wCharPlayerName[128]; + m_pChatLog->InsertColorChange( s_colorChatPlayerJoinedPartyName ); + GetPlayerNameForSteamID( wCharPlayerName, sizeof( wCharPlayerName ), steamID ); + m_pChatLog->InsertColorChange( s_colorChatPlayerJoinedParty ); + + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_PlayerJoinedPartyChat" ), 1, wCharPlayerName ); + m_pChatLog->InsertString( wszLocalized ); + } + + UpdatePlayerList(); + + return; + } + + if ( !Q_stricmp( pszEventName, "mm_lobby_member_leave" ) ) + { + CSteamID steamID = SteamIDFromDecimalString( event->GetString( "steamid", "0" ) ); + wchar_t wCharPlayerName[ 128 ]; + GetPlayerNameForSteamID( wCharPlayerName, sizeof(wCharPlayerName), steamID ); + m_pChatLog->InsertColorChange( s_colorChatPlayerLeftParty ); + + wchar_t wszLocalized[512]; + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_PlayerLeftPartyChat" ), 1, wCharPlayerName ); + m_pChatLog->InsertString( wszLocalized ); + + UpdatePlayerList(); + WriteGameSettingsControls(); + + return; + } + + Assert( false ); +} + +void CBaseLobbyPanel::OnCommand( const char *command ) +{ + if ( FStrEq( command, "invite" ) ) + { + GTFGCClientSystem()->RequestActivateInvite(); + } + else if ( FStrEq( command, "open_charinfo" ) ) + { + engine->ClientCmd_Unrestricted( "open_econui_backpack" ); + } + else + { + // What other commands are there? + Assert( false ); + } +} + + +bool CBaseLobbyPanel::IsAnyoneBanned( RTime32 &rtimeExpire ) const +{ + bool bBanned = false; + RTime32 rtimeHighest = 0; + + // This only matters if we're searching for a mannup or ladder game + CTFParty *pParty = GTFGCClientSystem()->GetParty(); + if ( pParty && ( !pParty->GetSearchPlayForBraggingRights() && !IsLadderGroup( pParty->GetMatchGroup() ) ) ) + { + return false; + } + + for( int i=0; i<m_vecPlayers.Count(); ++i ) + { + if ( m_vecPlayers[i].m_bIsBanned ) + { + bBanned = true; + + if ( m_vecPlayers[i].m_rtimeBanExpire > rtimeHighest ) + { + rtimeHighest = m_vecPlayers[i].m_rtimeBanExpire; + } + } + } + + rtimeExpire = rtimeHighest; + return bBanned; +} + +const CBaseLobbyPanel::LobbyPlayerInfo* CBaseLobbyPanel::GetLobbyPlayerInfo( CSteamID &steamID ) const +{ + for ( int i = 0; i < m_vecPlayers.Count(); ++i ) + { + if ( m_vecPlayers[i].m_steamID == steamID ) + { + return &m_vecPlayers[i]; + } + } + + Assert( false ); + return NULL; +} + +bool CBaseLobbyPanel::IsAnyoneLowPriority( RTime32 &rtimeExpire ) const +{ + bool bLowPriority = false; + RTime32 rtimeHighest = 0; + + CTFParty *pParty = GTFGCClientSystem()->GetParty(); + if ( pParty && !pParty->GetSearchPlayForBraggingRights() && !IsLadderGroup( pParty->GetMatchGroup() ) ) + return false; + + for ( int i = 0; i < m_vecPlayers.Count(); ++i ) + { + if ( m_vecPlayers[i].m_bIsLowPriority ) + { + bLowPriority = true; + + if ( m_vecPlayers[i].m_rtimeLowPriorityExpire > rtimeHighest ) + { + rtimeHighest = m_vecPlayers[i].m_rtimeLowPriorityExpire; + } + } + } + + rtimeExpire = rtimeHighest; + return bLowPriority; +} + +void CBaseLobbyPanel::UpdateControls() +{ + WriteGameSettingsControls(); + WriteStatusControls(); + UpdatePlayerList(); +} + +void CBaseLobbyPanel::OnCheckButtonChecked( vgui::Panel *panel ) +{ + if ( m_iWritingPanel > 0 ) + return; + if ( panel == m_pJoinLateCheckButton ) + { + if ( BIsPartyLeader() && GCClientSystem()->BConnectedtoGC() ) + { + tf_matchmaking_join_in_progress.SetValue( m_pJoinLateCheckButton->IsSelected() ? 1 : 0 ); + GTFGCClientSystem()->SetSearchJoinLate( m_pJoinLateCheckButton->IsSelected() ); + } + else + { + WriteGameSettingsControls(); + } + } +} + +void CBaseLobbyPanel::OnTradeWithUser( KeyValues* params ) +{ + CSteamID steamID = SteamIDFromDecimalString( params->GetString( "steamid", "" ) ); + if ( !steamID.IsValid() ) + return; + steamapicontext->SteamFriends()->ActivateGameOverlayToUser( "jointrade", steamID ); +} + +void CBaseLobbyPanel::OnItemLeftClick( vgui::Panel* panel ) +{ + if ( m_iWritingPanel > 0 ) + return; + m_pChatTextEntry->RequestFocus(); + if ( panel == m_pChatPlayerList ) + { + OnClickedOnPlayer(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseLobbyPanel::WriteStatusControls() +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + enum EDisabledState + { + DISABLED_NONE, + DISABLED_NO_GC, + DISABLED_MATCH_IN_PROGRESS + }; + + EDisabledState eDisabled = DISABLED_NONE; + + if ( GTFGCClientSystem()->BHaveLiveMatch() ) + { + eDisabled = DISABLED_MATCH_IN_PROGRESS; + } + + if ( !GCClientSystem()->BConnectedtoGC() || ( GTFGCClientSystem()->GetParty() && GTFGCClientSystem()->GetParty()->BOffline() ) ) + { + eDisabled = DISABLED_NO_GC; + } + + SetControlVisible( "NoGCGroupBox", eDisabled == DISABLED_NO_GC, true ); + SetControlVisible( "MatchInProgressGroupBox", eDisabled == DISABLED_MATCH_IN_PROGRESS, true ); + + if ( GTFGCClientSystem()->GetWizardStep() == TF_Matchmaking_WizardStep_SEARCHING ) + { + m_pSearchActiveGroupBox->SetVisible( true ); + + const CMsgMatchmakingProgress &progress = GTFGCClientSystem()->m_msgMatchmakingProgress; + wchar_t wszCount[32]; + CUtlVector<vgui::Label *> vecNearbyFields; + vgui::Label *pNearbyColumnHead = dynamic_cast<vgui::Label *>( FindChildByName( "NearbyColumnHead", true ) ); + Assert( pNearbyColumnHead ); + vecNearbyFields.AddToTail( pNearbyColumnHead ); + + #define DO_FIELD( protobufname, labelname, bNearby ) \ + vgui::Label *p##labelname = dynamic_cast<vgui::Label *>( FindChildByName( #labelname, true ) ); \ + Assert( p##labelname ); \ + if ( p##labelname ) \ + { \ + if ( progress.has_##protobufname() ) \ + { \ + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", progress.protobufname() ); \ + p##labelname->SetText( wszCount ); \ + if ( bNearby ) bHasAnyNearbyData = true; \ + } \ + else \ + { \ + p##labelname->SetText( "#TF_Matchmaking_NoData" ); \ + } \ + if ( bNearby ) vecNearbyFields.AddToTail( p##labelname ); \ + } + + bool bHasAnyNearbyData = false; + DO_FIELD( matching_worldwide_searching_players, PlayersSearchingMatchingWorldwideValue, false ) + DO_FIELD( matching_near_you_searching_players, PlayersSearchingMatchingNearbyValue, true ) + DO_FIELD( matching_worldwide_active_players, PlayersInGameMatchingWorldwideValue, false ) + DO_FIELD( matching_near_you_active_players, PlayersInGameMatchingNearbyValue, true ) + DO_FIELD( matching_worldwide_empty_gameservers, EmptyGameserversMatchingWorldwideValue, false ) + DO_FIELD( matching_near_you_empty_gameservers, EmptyGameserversMatchingNearbyValue, true ) + DO_FIELD( total_worldwide_searching_players, PlayersSearchingTotalWorldwideValue, false ) + DO_FIELD( total_near_you_searching_players, PlayersSearchingTotalNearbyValue, true ) + DO_FIELD( total_worldwide_active_players, PlayersInGameTotalWorldwideValue, false ) + DO_FIELD( total_near_you_active_players, PlayersInGameTotalNearbyValue, true ) + + FOR_EACH_VEC( vecNearbyFields, i ) + { + vecNearbyFields[i]->SetVisible( bHasAnyNearbyData ); + } + + // Show the low priority message? This only really applies to ladder games for now. + RTime32 rtimeExpire = 0; + bool bShowTimer = IsAnyoneLowPriority( rtimeExpire ); + if ( bShowTimer ) + { + CRTime timeExpire( rtimeExpire ); + timeExpire.SetToGMT( false ); + char time_buf[k_RTimeRenderBufferSize]; + if ( m_pPartyHasLowPriority ) + { + m_pPartyHasLowPriority->SetDialogVariable( "penaltytimer", CFmtStr( "Expires: %s", ( ( rtimeExpire > 0 ) ? timeExpire.Render( time_buf ) : "" ) ) ); + } + if ( m_pSearchActivePenaltyLabel ) + { + m_pSearchActivePenaltyLabel->SetText( "#TF_Matchmaking_PartyLowPriority" ); + } + } + if ( m_pPartyHasLowPriority ) + { + m_pPartyHasLowPriority->SetVisible( bShowTimer ); + } + if ( m_pSearchActivePenaltyLabel ) + { + m_pSearchActivePenaltyLabel->SetVisible( bShowTimer ); + } + + // HOLY CHEESEBALL BUSY INDICATOR + const wchar_t *pwszEllipses = &L"....."[ 4 - ( (unsigned)Plat_FloatTime() % 5U ) ]; + wchar_t wszLocalized[512]; + g_pVGuiLocalize->ConstructString_safe( wszLocalized, g_pVGuiLocalize->Find( "#TF_Matchmaking_Searching" ), 1, pwszEllipses ); + if ( m_pSearchActiveTitleLabel ) + { + m_pSearchActiveTitleLabel->SetText( wszLocalized ); + } + } + else + { + m_pSearchActiveGroupBox->SetVisible( false ); + } +} + +void CBaseLobbyPanel::WriteGameSettingsControls() +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + // Make sure we want to be in matchmaking. (If we don't, the frame should hide us pretty quickly.) + // We might get an event or something right at the transition point occasionally when the UI should + // not be visible + if ( GTFGCClientSystem()->GetMatchmakingUIState() == eMatchmakingUIState_Inactive ) + { + return; + } + + SetMatchmakingModeBackground(); + + bool bLeader = BIsPartyLeader(); + bool bInUIState = BIsPartyInUIState(); + + m_pJoinLateCheckButton->ToggleButton::SetSelected( GTFGCClientSystem()->GetSearchJoinLate() ); // !KLUDGE! call base to avoid firing the signal + + bool bShowLateJoin = ShouldShowLateJoin(); + m_pJoinLateCheckButton->SetVisible( bShowLateJoin && bLeader ); + m_pJoinLateValueLabel->SetText( GTFGCClientSystem()->GetSearchJoinLate() ? "#TF_Matchmaking_SearchForAll" : "#TF_Matchmaking_SearchForNew" ); + m_pJoinLateValueLabel->SetVisible( bShowLateJoin && !bLeader ); + //m_pJoinLateValueLabel->SetEnabled( bInUIState ); + + m_pInviteButton->SetVisible( bInUIState && ( m_vecPlayers.Count() < k_nTFPartyMaxSize ) ); + + m_pChatTextEntry->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the player list +//----------------------------------------------------------------------------- +void CBaseLobbyPanel::UpdatePlayerList() +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + if ( !IsVisible() || !m_pContainer->IsVisible() ) + return; + + m_pChatPlayerList->ClearSelection(); + m_pChatPlayerList->RemoveAll(); + m_vecPlayers.RemoveAll(); + + bool bLadderGame = GTFGCClientSystem()->GetSearchMode() == TF_Matchmaking_LADDER && + IsLadderGroup( (EMatchGroup)GTFGCClientSystem()->GetLadderType() ); + const IMatchGroupDescription *pMatchDesc = GetMatchGroupDescription( GetMatchGroup() ); + EMMPenaltyPool ePenaltyPool = pMatchDesc ? pMatchDesc->m_params.m_ePenaltyPool : eMMPenaltyPool_Invalid; + + if ( steamapicontext == NULL || steamapicontext->SteamUser() == NULL ) + return; + + // Locate party, if we have one. + CTFParty *pParty = GTFGCClientSystem()->GetParty(); + + if ( pParty == NULL ) + { + LobbyPlayerInfo p; + p.m_steamID = steamapicontext->SteamUser()->GetSteamID(); + p.m_sName = steamapicontext->SteamFriends()->GetPersonaName(); + p.m_bHasTicket = GTFGCClientSystem()->BLocalPlayerInventoryHasMvmTicket(); + p.m_bSquadSurplus = GTFGCClientSystem()->GetLocalPlayerSquadSurplus(); +#ifdef USE_MVM_TOUR + int idxTour = GTFGCClientSystem()->GetSearchMannUpTourIndex(); + if ( idxTour < 0 || !GTFGCClientSystem()->BGetLocalPlayerBadgeInfoForTour( idxTour, &p.m_nBadgeLevel, &p.m_nCompletedChallenges ) ) + { + p.m_nBadgeLevel = 0; + p.m_nCompletedChallenges = 0; + } +#endif // USE_MVM_TOUR + p.m_pAvatarImage = NULL; + p.m_bHasCompetitiveAccess = GTFGCClientSystem()->BHasCompetitiveAccess(); + CSOTFLadderData *pData = GetLocalPlayerLadderData( (EMatchGroup)GTFGCClientSystem()->GetLadderType() ); + p.m_unLadderRank = ( pData ? pData->Obj().rank() : 1u ); + + uint32 unExperienceLevel = 1u; + const IProgressionDesc *pProgressionDesc = pMatchDesc ? pMatchDesc->m_pProgressionDesc : NULL; + if ( pData && pProgressionDesc && pMatchDesc->m_params.m_eMatchType == MATCH_TYPE_CASUAL ) + { + LevelInfo_t levelInfo = pProgressionDesc->GetLevelForExperience( pData->Obj().experience() ); + unExperienceLevel = levelInfo.m_nLevelNum; + } + p.m_unExperienceLevel = unExperienceLevel; + + CEconGameAccountClient *pGameAccountClient = NULL; + if ( InventoryManager() && TFInventoryManager()->GetLocalTFInventory() && TFInventoryManager()->GetLocalTFInventory()->GetSOC() ) + { + pGameAccountClient = TFInventoryManager()->GetLocalTFInventory()->GetSOC()->GetSingleton<CEconGameAccountClient>(); + } + + p.m_bIsBanned = false; + p.m_rtimeBanExpire = 0; + p.m_rtimeLowPriorityExpire = 0; + p.m_bIsLowPriority = false; + if ( pGameAccountClient && ePenaltyPool != eMMPenaltyPool_Invalid ) + { + switch ( ePenaltyPool ) + { + case eMMPenaltyPool_Casual: + p.m_rtimeBanExpire = pGameAccountClient->Obj().matchmaking_casual_ban_expiration(); + p.m_rtimeLowPriorityExpire = pGameAccountClient->Obj().matchmaking_casual_low_priority_expiration(); + p.m_bIsBanned = p.m_rtimeBanExpire > CRTime::RTime32TimeCur(); + p.m_bIsLowPriority = p.m_rtimeLowPriorityExpire > CRTime::RTime32TimeCur(); + break; + case eMMPenaltyPool_Ranked: + p.m_rtimeBanExpire = pGameAccountClient->Obj().matchmaking_ranked_ban_expiration(); + p.m_rtimeLowPriorityExpire = pGameAccountClient->Obj().matchmaking_ranked_low_priority_expiration(); + p.m_bIsBanned = p.m_rtimeBanExpire > CRTime::RTime32TimeCur(); + p.m_bIsLowPriority = p.m_rtimeLowPriorityExpire > CRTime::RTime32TimeCur(); + break; + default: Assert( false ); + } + } + + // !TEST! + //p.m_bIsBanned = true; + + //for (int i = 0 ; i < 6 ; ++i) // !TEST! + m_vecPlayers.AddToTail( p ); + } + else + { + for ( int i = 0 ; i < pParty->GetNumMembers() ; ++i ) + { + LobbyPlayerInfo p; + p.m_steamID = pParty->GetMember( i ); + p.m_sName = steamapicontext->SteamFriends()->GetFriendPersonaName( p.m_steamID ); + if ( p.m_sName.IsEmpty() ) + continue; + p.m_bHasTicket = pParty->Obj().members( i ).owns_ticket(); + p.m_nBadgeLevel = pParty->Obj().members( i ).badge_level(); + p.m_nCompletedChallenges = pParty->Obj().members( i ).completed_missions(); + p.m_bSquadSurplus = pParty->Obj().members( i ).squad_surplus(); + p.m_pAvatarImage = NULL; + p.m_bIsBanned = pParty->Obj().members( i ).is_banned(); + p.m_bHasCompetitiveAccess = pParty->Obj().members( i ).competitive_access(); + p.m_unLadderRank = pParty->Obj().members( i ).ladder_rank(); + p.m_rtimeBanExpire = pParty->Obj().matchmaking_ban_time(); + p.m_rtimeLowPriorityExpire = pParty->Obj().matchmaking_low_priority_time(); + p.m_bIsLowPriority = pParty->Obj().members( i ).is_low_priority(); + + uint32 unExperienceLevel = 1u; + const IProgressionDesc *pProgressionDesc = pMatchDesc ? pMatchDesc->m_pProgressionDesc : NULL; + if ( pProgressionDesc && pMatchDesc->m_params.m_eMatchType == MATCH_TYPE_CASUAL ) + { + LevelInfo_t levelInfo = pProgressionDesc->GetLevelForExperience( pParty->Obj().members( i ).experience() ); + unExperienceLevel = levelInfo.m_nLevelNum; + } + p.m_unExperienceLevel = unExperienceLevel; + + if ( p.m_steamID == pParty->GetLeader() ) + { + m_vecPlayers.AddToHead( p ); + } + else + { + m_vecPlayers.AddToTail( p ); + } + } + } + + for( int i = 0; i < m_vecPlayers.Count(); ++i ) + { + KeyValues *pKeyValues = new KeyValues( "data" ); + CUtlString sName; + if ( i == 0 && m_vecPlayers.Count() > 1 ) + { + sName.Format( "%s (leader)", m_vecPlayers[i].m_sName.String() ); // !FIXME! Localize + } + else + { + sName = m_vecPlayers[i].m_sName; + } + pKeyValues->SetString( "name", sName ); + + pKeyValues->SetString( "steamid", CFmtStr( "%llu", m_vecPlayers[i].m_steamID.ConvertToUint64() ).Access() ); + pKeyValues->SetInt( "badge_level", Max( 1U, m_vecPlayers[i].m_nBadgeLevel ) ); + + ApplyChatUserSettings( m_vecPlayers[ i ], pKeyValues ); + + pKeyValues->SetInt( "is_banned", ( m_vecPlayers[i].m_bIsBanned || m_vecPlayers[i].m_bIsLowPriority ) ? m_iImageIsBanned : 0 ); + + // See if the avatar's changed + int iAvatar = steamapicontext->SteamFriends()->GetSmallFriendAvatar( m_vecPlayers[i].m_steamID ); + int iIndex = m_mapAvatarsToImageList.Find( iAvatar ); + if ( iIndex == m_mapAvatarsToImageList.InvalidIndex() ) + { + CAvatarImage *pImage = new CAvatarImage(); + pImage->SetAvatarSteamID( m_vecPlayers[i].m_steamID ); + pImage->SetDrawFriend( false ); // you can only invite friends, this isn't that useful + pImage->SetAvatarSize( m_iAvatarWidth, m_iAvatarWidth ); + int iImageIndex = m_pImageList->AddImage( pImage ); + + iIndex = m_mapAvatarsToImageList.Insert( iAvatar, iImageIndex ); + } + pKeyValues->SetInt( "avatar", m_mapAvatarsToImageList[iIndex] ); + CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( m_mapAvatarsToImageList[iIndex] ); + pAvIm->UpdateFriendStatus(); + m_vecPlayers[i].m_pAvatarImage = pAvIm; + if ( bLadderGame ) + { + if ( !m_vecPlayers[i].m_bHasCompetitiveAccess ) + { + pKeyValues->SetInt( "has_competitive_access", m_iImageNo ); + } + } + + pKeyValues->SetInt( "ladder_rank", m_vecPlayers[i].m_unLadderRank ); + pKeyValues->SetInt( "experience_level", m_vecPlayers[i].m_unExperienceLevel ); + + int itemID = m_pChatPlayerList->AddItem( 0, pKeyValues ); + m_pChatPlayerList->SetItemFont( itemID, m_fontPlayerListItem ); + m_pChatPlayerList->SetItemFgColor( itemID, ( m_vecPlayers[i].m_bIsBanned || m_vecPlayers[i].m_bIsLowPriority ) ? s_colorBannedPlayerListItem : s_colorPlayerListItem ); + + pKeyValues->deleteThis(); + } + + // force the list to PerformLayout() now so we can update our medal images + m_pChatPlayerList->InvalidateLayout( true ); + + int iPanelCount = 0; + // This only works in 6v6 Comp and 12v12 Casual for now + if ( ( GetMatchGroup() == k_nMatchGroup_Ladder_6v6 ) || ( GetMatchGroup() == k_nMatchGroup_Casual_12v12 ) ) + { + int nColumn = m_pChatPlayerList->GetColumnIndexByName( 0, "rank" ); + + for ( int nRow = 0; nRow < m_pChatPlayerList->GetItemCount(); nRow++ ) + { + KeyValues *pKeyValues = m_pChatPlayerList->GetItemData( nRow ); + if ( !pKeyValues ) + continue; + + CSteamID steamID = SteamIDFromDecimalString( pKeyValues->GetString( "steamid", "0" ) ); + if ( !steamID.IsValid() ) + continue; + + uint32 unLevel = pKeyValues->GetInt( "ladder_rank" ); + if ( GetMatchGroup() == k_nMatchGroup_Casual_12v12 ) + { + unLevel = pKeyValues->GetInt( "experience_level" ); + } + + // Create a panel if we need one + if ( iPanelCount >= m_vecChatBadges.Count() ) + { + m_vecChatBadges.AddToTail(); + m_vecChatBadges[iPanelCount].m_pBadgeModel = vgui::SETUP_PANEL( new CTFBadgePanel( m_pChatPlayerList, "Model" ) ); + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetZPos( 9999 ); + m_vecChatBadges[iPanelCount].m_nShownLevel = 0u; + } + + // Move it into place and resize. This is terrible, but VGUI has forced my hand + int nX, nY, nWide, nTall; + m_pChatPlayerList->GetMaxCellBounds( nRow, nColumn, nX, nY, nWide, nTall ); + int nSideLength = Max( nWide, nTall ); + nX = ( nX + ( nWide / 2 ) ) - ( nSideLength / 2 ); + nY = ( nY + ( nTall / 2 ) ) - ( nSideLength / 2 ); + + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetBounds( nX, nY, nSideLength, nSideLength ); + + // Different dude in the slot or their level is different? Update the medal model + if ( m_vecChatBadges[iPanelCount].m_steamIDOwner != steamID || + m_vecChatBadges[iPanelCount].m_nShownLevel != unLevel ) + { + m_vecChatBadges[iPanelCount].m_steamIDOwner = steamID; + + const LevelInfo_t& level = pMatchDesc->m_pProgressionDesc->GetLevelByNumber( unLevel ); + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetupBadge( pMatchDesc->m_pProgressionDesc, level ); + + wchar_t wszOutString[128]; + char szLocalized[512]; + wchar_t wszCount[16]; + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", level.m_nLevelNum ); + const wchar_t *wpszFormat = g_pVGuiLocalize->Find( pMatchDesc->m_pProgressionDesc->m_pszLevelToken ); + g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 2, wszCount, g_pVGuiLocalize->Find( level.m_pszLevelTitle ) ); + g_pVGuiLocalize->ConvertUnicodeToANSI( wszOutString, szLocalized, sizeof( szLocalized ) ); + + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetTooltip( m_pToolTip, szLocalized ); + m_vecChatBadges[iPanelCount].m_nShownLevel = level.m_nLevelNum; + m_vecChatBadges[iPanelCount].m_pBadgeModel->InvalidateLayout( true, true ); + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetVisible( true ); + } + + iPanelCount++; + } + } + + for ( ; iPanelCount < m_vecChatBadges.Count(); ++iPanelCount ) + { + m_vecChatBadges[iPanelCount].m_pBadgeModel->SetVisible( false ); + m_vecChatBadges[iPanelCount].m_nShownLevel = 0u; // Will cause the badge to refresh when it gets a player + } +} + + +//----------------------------------------------------------------------------- +void CBaseLobbyPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( GetResFile() ); + + m_pSearchActiveGroupBox = dynamic_cast<vgui::EditablePanel *>(FindChildByName( "SearchActiveGroupBox", true )); Assert( m_pSearchActiveGroupBox ); + m_pSearchActiveTitleLabel = dynamic_cast<vgui::Label *>(FindChildByName( "SearchActiveTitle", true )); Assert( m_pSearchActiveTitleLabel ); + m_pSearchActivePenaltyLabel = dynamic_cast<vgui::Label *>( FindChildByName( "PartyHasLowPriorityLabel", true ) ); Assert( m_pSearchActivePenaltyLabel ); + + m_pPartyHasLowPriority = dynamic_cast<vgui::EditablePanel *>( FindChildByName( "PartyHasLowPriorityGroupBox", true ) ); Assert( m_pPartyHasLowPriority ); + + m_pJoinLateCheckButton = dynamic_cast<vgui::CheckButton *>(FindChildByName( "JoinLateCheckButton", true )); Assert( m_pJoinLateCheckButton ); + m_pJoinLateValueLabel = dynamic_cast<vgui::Label *>(FindChildByName( "JoinLateValueLabel", true )); Assert( m_pJoinLateValueLabel ); + + m_pInviteButton = dynamic_cast<vgui::Button *>(FindChildByName( "InviteButton", true )); Assert( m_pInviteButton ); + + delete m_pImageList; + m_pImageList = new vgui::ImageList( false ); + + m_iImageIsBanned = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_timeout_active", true ) ); + m_pImageList->GetImage( m_iImageIsBanned )->SetSize( m_iBannedWidth, m_iBannedWidth ); + m_iImageCheckBoxDisabled = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_check_box_disabled", true ) ); + m_pImageList->GetImage( m_iImageCheckBoxDisabled )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageCheckBoxYes = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_check_box_yes", true ) ); + m_pImageList->GetImage( m_iImageCheckBoxYes )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageCheckBoxNo = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_check_box_no", true ) ); + m_pImageList->GetImage( m_iImageCheckBoxNo )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageCheckBoxMixed = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_check_box_mixed", true ) ); + m_pImageList->GetImage( m_iImageCheckBoxMixed )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageRadioButtonYes = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_radio_button_yes", true ) ); + m_pImageList->GetImage( m_iImageRadioButtonYes )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageRadioButtonNo = m_pImageList->AddImage( vgui::scheme()->GetImage( "pve/mvm_radio_button_no", true ) ); + m_pImageList->GetImage( m_iImageRadioButtonNo )->SetSize( m_iChallengeCheckBoxWidth, m_iChallengeCheckBoxWidth * ( 3.75f / 4.0f ) ); + m_iImageNew = m_pImageList->AddImage( vgui::scheme()->GetImage( "new", true ) ); + m_pImageList->GetImage( m_iImageNew )->SetSize( m_iNewWidth, m_iNewWidth * ( 3.75f / 4.0f ) ); + + m_iImageNo = m_pImageList->AddImage( vgui::scheme()->GetImage( "hud/vote_no", true ) ); + + m_mapAvatarsToImageList.RemoveAll(); + m_pChatPlayerList->SetImageList( m_pImageList, false ); + m_pChatPlayerList->SetVisible( true ); + + + // + // Populate the challenge list + // + m_pJoinLateCheckButton->AddActionSignalTarget( this ); + m_pInviteButton->AddActionSignalTarget( this ); + m_pChatPlayerList->AddActionSignalTarget( this ); + + m_fontPlayerListItem = pScheme->GetFont( "DefaultSmall", true ); + + // + // Populate the player list + // + + m_pChatPlayerList->SetVerticalScrollbar( false ); + m_pChatPlayerList->RemoveAll(); + m_pChatPlayerList->RemoveAllSections(); + m_pChatPlayerList->AddSection( 0, "Players" ); + m_pChatPlayerList->SetSectionAlwaysVisible( 0, true ); + m_pChatPlayerList->SetSectionFgColor( 0, Color( 255, 255, 255, 255 ) ); + m_pChatPlayerList->SetBgColor( Color( 0, 0, 0, 0 ) ); + m_pChatPlayerList->SetBorder( NULL ); + m_pChatPlayerList->SetClickable( false ); + //m_pChatPlayerList->SetClickable( true ); // enable context menu to trade / kick? + + bool bPartyLeader = BIsPartyLeader() && GCClientSystem()->BConnectedtoGC(); + + if ( bPartyLeader ) + { + extern bool TF_IsHolidayActive( int eHoliday ); + bool bHalloween = TF_IsHolidayActive( kHoliday_Halloween ); + static bool bForcedOnce = false; + if ( bHalloween && !bForcedOnce ) + { + GTFGCClientSystem()->SetQuickplayGameType( kGameCategory_Event247 ); + bForcedOnce = true; + } + } + + if ( bPartyLeader ) + { + GTFGCClientSystem()->SetSearchJoinLate( tf_matchmaking_join_in_progress.GetBool() ); + } +} + +void CBaseLobbyPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + WriteGameSettingsControls(); + UpdatePlayerList(); +} + +void CBaseLobbyPanel::OnItemContextMenu( vgui::Panel* panel ) +{ + if ( m_iWritingPanel > 0 ) + return; + m_pChatTextEntry->RequestFocus(); + if ( panel == m_pChatPlayerList ) + { + OnClickedOnPlayer(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Command to launch the lobby UI, connecting to a particular lobby +//----------------------------------------------------------------------------- +static void CL_ConnectLobby( const CCommand &args ) +{ + if ( args.ArgC() < 2 ) + { + Warning( "connect_lobby missing LobbyID argument\n" ); + return; + } + + uint64 ulSteamID = 0; + sscanf( args.Arg( 1 ), "%lld", &ulSteamID ); + CSteamID steamIDLobby( ulSteamID ); + if ( !steamIDLobby.IsValid() || !steamIDLobby.IsLobby() ) + { + Warning( "connect_lobby passed invalid LobbyID '%s'\n", args.Arg( 1 ) ); + return; + } + + GTFGCClientSystem()->AcceptFriendInviteToJoinLobby( steamIDLobby ); +} + +void OnSteamGameLobbyJoinRequested( GameLobbyJoinRequested_t *pInfo ); + +static ConCommand connect_lobby_command( "connect_lobby", &CL_ConnectLobby, "<64-bit lobby ID> Accept friend invite, connecting to specified Steam lobby and joining the corresponding search party" ); + +#endif // #ifdef ENABLE_GC_MATCHMAKING |