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.
 
 
 
 
 
 

1703 lines
50 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_classmenu.h"
#include "IGameUIFuncs.h" // for key bindings
#include "tf_hud_notification_panel.h"
#include "character_info_panel.h"
#include "playerspawncache.h"
#include "iclientmode.h"
#include "econ_gcmessages.h"
#include "gc_clientsystem.h"
#include "vgui/IInput.h"
#include <vgui_controls/PanelListPanel.h>
#include <vgui_controls/ScrollBarSlider.h>
#include "tf_gamerules.h"
#include "engine/IEngineSound.h"
#include "inputsystem/iinputsystem.h"
#if defined( REPLAY_ENABLED )
#include "replay/iclientreplaycontext.h"
#include "replay/ireplaysystem.h"
#include "replay/ienginereplay.h"
#include "replay/shared_defs.h"
#endif
extern IGameUIFuncs *gameuifuncs; // for key binding details
using namespace vgui;
ConVar _cl_classmenuopen( "_cl_classmenuopen", "0", 0, "internal cvar used to tell server when class menu is open" );
#if defined( REPLAY_ENABLED )
ConVar replay_replaywelcomedlgcount( "replay_replaywelcomedlgcount", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE | FCVAR_HIDDEN, "The number of times the replay help dialog has displayed." );
#endif
ConVar tf_mvm_classupgradehelpcount( "tf_mvm_classupgradehelpcount", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE | FCVAR_HIDDEN, "The number of times the player upgrade help dialog has displayed." );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFClassTipsItemPanel::CTFClassTipsItemPanel( Panel *parent, const char *name, int iListItemID ) : BaseClass( parent, name )
{
m_pTipIcon = new vgui::ImagePanel( this, "TipIcon" );
m_pTipLabel = new CExLabel( this, "TipLabel", "" );
// m_pTipIcon = dynamic_cast<vgui::ImagePanel*>( FindChildByName( "TipIcon" ) );
// m_pTipLabel = dynamic_cast<CExLabel*>( FindChildByName( "TipLabel" ) );
}
CTFClassTipsItemPanel::~CTFClassTipsItemPanel()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassTipsItemPanel::SetClassTip( const wchar_t *pwszText, const char *pszIcon )
{
// Set tf_english string and .res icon path
if ( m_pTipLabel && m_pTipIcon )
{
if ( pszIcon )
{
m_pTipIcon->SetImage( pszIcon );
}
else
{
m_pTipIcon->SetVisible( false );
// int iX, iY = 0;
// m_pTipLabel->GetPos( iX, iY );
// int nIconWidth = m_pTipIcon->GetWide();
// m_pTipLabel->SetPos( ( iX - nIconWidth ), iY );
}
m_pTipLabel->SetText( pwszText );
}
InvalidateLayout( true, true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassTipsItemPanel::ApplySchemeSettings( IScheme* pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "Resource/UI/ClassTipsItem.res" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CTFClassTipsListPanel : public PanelListPanel
{
private:
DECLARE_CLASS_SIMPLE( CTFClassTipsListPanel, PanelListPanel );
public:
CTFClassTipsListPanel( Panel *parent, const char *panelName )
: PanelListPanel( parent, panelName )
{
m_pScrollBar = GetScrollbar();
m_pUpArrow = new CExImageButton( this, "UpArrow", "" );
if ( m_pUpArrow )
{
m_pUpArrow->AddActionSignalTarget( m_pScrollBar );
m_pUpArrow->SetCommand(new KeyValues("ScrollButtonPressed", "index", 0));
m_pUpArrow->GetImage()->SetShouldScaleImage( true );
m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
m_pUpArrow->SetAlpha( 255 );
m_pUpArrow->SetPaintBackgroundEnabled( false );
m_pUpArrow->SetVisible( false );
m_pUpArrow->PassMouseTicksTo( m_pScrollBar );
m_pUpArrow->SetImageDefault( "chalkboard_scroll_up" );
}
m_pDownArrow = new CExImageButton( this, "DownArrow", "" );
if ( m_pDownArrow )
{
m_pDownArrow->AddActionSignalTarget( m_pScrollBar );
m_pDownArrow->SetCommand(new KeyValues("ScrollButtonPressed", "index", 1));
m_pDownArrow->GetImage()->SetShouldScaleImage( true );
m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
m_pDownArrow->SetAlpha( 255 );
m_pDownArrow->SetPaintBackgroundEnabled( false );
m_pDownArrow->SetVisible( false );
m_pDownArrow->PassMouseTicksTo( m_pScrollBar );
m_pDownArrow->SetImageDefault( "chalkboard_scroll_down" );
}
if ( m_pScrollBar )
{
m_pScrollBar->SetOverriddenButtons( m_pUpArrow, m_pDownArrow );
m_pScrollBar->SetVisible( false );
}
m_pLine = new vgui::ImagePanel( this, "Line" );
m_pBox = new vgui::ImagePanel( this, "Box" );
if ( m_pLine )
{
m_pLine->SetImage( "chalkboard_scroll_line" );
m_pLine->SetShouldScaleImage( true );
}
if ( m_pBox )
{
m_pBox->SetImage( "chalkboard_scroll_box" );
m_pBox->SetShouldScaleImage( true );
}
SetScrollBarImagesVisible( false );
vgui::ivgui()->AddTickSignal( GetVPanel() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
~CTFClassTipsListPanel()
{
ivgui()->RemoveTickSignal( GetVPanel() );
}
private:
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int GetListHeight( void )
{
int nHeight = 0;
for ( int i = FirstItem(); i < GetItemCount(); i++ )
{
vgui::Panel *pClassTipsItemPanel = GetItemPanel( i );
if ( pClassTipsItemPanel )
{
nHeight += pClassTipsItemPanel->GetTall();
}
}
return nHeight;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SetScrollBarImagesVisible( bool visible )
{
if ( m_pDownArrow && m_pDownArrow->IsVisible() != visible )
{
m_pDownArrow->SetVisible( visible );
m_pDownArrow->SetEnabled( visible );
}
if ( m_pUpArrow && m_pUpArrow->IsVisible() != visible )
{
m_pUpArrow->SetVisible( visible );
m_pUpArrow->SetEnabled( visible );
}
if ( m_pLine && m_pLine->IsVisible() != visible )
{
m_pLine->SetVisible( visible );
}
if ( m_pBox && m_pBox->IsVisible() != visible )
{
m_pBox->SetVisible( visible );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void OnTick()
{
if ( !IsVisible() )
return;
if ( m_pDownArrow && m_pUpArrow && m_pLine && m_pBox )
{
if ( m_pScrollBar && m_pScrollBar->IsVisible() && GetListHeight() > GetTall() )
{
SetScrollBarImagesVisible ( true );
// set the alpha on the up arrow
int nMin, nMax;
m_pScrollBar->GetRange( nMin, nMax );
int nScrollPos = m_pScrollBar->GetValue();
int nRangeWindow = m_pScrollBar->GetRangeWindow();
int nBottom = nMax - nRangeWindow;
if ( nBottom < 0 )
{
nBottom = 0;
}
int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255;
m_pUpArrow->SetAlpha( nAlpha );
// set the alpha on the down arrow
nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255;
m_pDownArrow->SetAlpha( nAlpha );
ScrollBarSlider *pSlider = m_pScrollBar->GetSlider();
if ( pSlider && pSlider->GetRangeWindow() > 0 )
{
int x, y, w, t, min, max;
m_pLine->GetBounds( x, y, w, t );
pSlider->GetNobPos( min, max );
m_pBox->SetBounds( x, y + min, w, ( max - min ) );
}
}
else
{
// turn off our images
SetScrollBarImagesVisible ( false );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
if ( m_pScrollBar )
{
m_pScrollBar->SetZPos( 500 );
m_pUpArrow->SetZPos( 501 );
m_pDownArrow->SetZPos( 501 );
// turn off painting the vertical scrollbar
m_pScrollBar->SetPaintBackgroundEnabled( false );
m_pScrollBar->SetPaintBorderEnabled( false );
m_pScrollBar->SetPaintEnabled( false );
m_pScrollBar->SetScrollbarButtonsVisible( false );
m_pScrollBar->GetButton(0)->SetMouseInputEnabled( false );
m_pScrollBar->GetButton(1)->SetMouseInputEnabled( false );
if ( m_pScrollBar->IsVisible() )
{
int nMin, nMax;
m_pScrollBar->GetRange( nMin, nMax );
m_pScrollBar->SetValue( nMin );
int nScrollbarWide = m_pScrollBar->GetWide();
int wide, tall;
GetSize( wide, tall );
if ( m_pUpArrow )
{
m_pUpArrow->SetBounds( wide - nScrollbarWide, 0, nScrollbarWide, nScrollbarWide );
m_pUpArrow->GetImage()->SetSize( nScrollbarWide, nScrollbarWide );
}
if ( m_pLine )
{
m_pLine->SetBounds( wide - nScrollbarWide, nScrollbarWide, nScrollbarWide, tall - ( 2 * nScrollbarWide ) );
}
if ( m_pBox )
{
m_pBox->SetBounds( wide - nScrollbarWide, nScrollbarWide, nScrollbarWide, nScrollbarWide );
}
if ( m_pDownArrow )
{
m_pDownArrow->SetBounds( wide - nScrollbarWide, tall - nScrollbarWide, nScrollbarWide, nScrollbarWide );
m_pDownArrow->GetImage()->SetSize( nScrollbarWide, nScrollbarWide );
}
SetScrollBarImagesVisible( false );
}
}
}
vgui::ScrollBar *m_pScrollBar;
CExImageButton *m_pUpArrow;
CExImageButton *m_pDownArrow;
vgui::ImagePanel *m_pLine;
vgui::ImagePanel *m_pBox;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CTFClassTipsPanel : public EditablePanel
{
private:
DECLARE_CLASS_SIMPLE( CTFClassTipsPanel, EditablePanel );
public:
CTFClassTipsPanel( Panel *parent, const char *panelName )
: EditablePanel( parent, panelName )
{
m_pClassTipsListPanel = new CTFClassTipsListPanel( this, "ClassTipsListPanel" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
~CTFClassTipsPanel()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SetClass( int iClass )
{
const char *pPathID = IsX360() ? "MOD" : "GAME";
m_fmtResFilename.sprintf( "classes/%s.res", g_aRawPlayerClassNames[ iClass ] );
if ( !g_pFullFileSystem->FileExists( m_fmtResFilename.Access(), pPathID ) &&
g_pFullFileSystem->FileExists( "classes/default.res", pPathID ) )
{
m_fmtResFilename.sprintf( "classes/default.res" );
}
if ( m_pClassTipsListPanel )
{
m_pClassTipsListPanel->DeleteAllItems();
int nScrollToItem = 0;
// Get tip count
const wchar_t *wzTipCount = g_pVGuiLocalize->Find( CFmtStr( "ClassTips_%d_Count", iClass ) );
int nTipCount = wzTipCount ? _wtoi( wzTipCount ) : 0;
for ( int iTip = 1; iTip < nTipCount+1; ++iTip )
{
const wchar_t *pwszText = g_pVGuiLocalize->Find( CFmtStr( "#ClassTips_%d_%d", iClass, iTip ) );
const wchar_t *pwszTextMvM = g_pVGuiLocalize->Find( CFmtStr( "#ClassTips_%d_%d_MvM", iClass, iTip ) );
wchar_t *pwszIcon = g_pVGuiLocalize->Find( CFmtStr( "ClassTips_%d_%d_Icon", iClass, iTip ) );
char szIcon[MAX_PATH];
szIcon[0] = 0;
if ( pwszIcon )
{
g_pVGuiLocalize->ConvertUnicodeToANSI( pwszIcon, szIcon, sizeof( szIcon ) );
}
// Don't load MvM tips outside the mode
if ( pwszTextMvM )
{
if ( !TFGameRules()->IsMannVsMachineMode() )
continue;
// If we're MvM mode, remember first MvM tip
if ( !nScrollToItem )
nScrollToItem = iTip;
}
// Create a TipsItemPanel for each tip
if ( pwszText || pwszTextMvM )
{
CTFClassTipsItemPanel *pClassTipsItemPanel = new CTFClassTipsItemPanel( this, "ClassTipsItemPanel", iTip );
if ( pwszText )
{
pClassTipsItemPanel->SetClassTip( pwszText, szIcon );
}
else if ( pwszTextMvM )
{
pClassTipsItemPanel->SetClassTip( pwszTextMvM, szIcon );
}
m_pClassTipsListPanel->AddItem( NULL, pClassTipsItemPanel );
}
}
if ( m_pClassTipsListPanel->GetItemCount() > 0 )
{
m_pClassTipsListPanel->SetFirstColumnWidth( 0 );
m_pClassTipsListPanel->ScrollToItem( nScrollToItem );
}
}
InvalidateLayout( true, true );
}
private:
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
virtual void ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "Resource/UI/ClassTipsList.res" );
}
CTFClassTipsListPanel *m_pClassTipsListPanel;
CFmtStr m_fmtResFilename;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFClassMenu::CTFClassMenu( IViewPort *pViewPort )
: CClassMenu( pViewPort )
{
MakePopup();
m_mouseoverButtons.RemoveAll();
m_iClassMenuKey = BUTTON_CODE_INVALID;
m_iCurrentClassIndex = TF_CLASS_HEAVYWEAPONS;
#ifdef _X360
m_pFooter = new CTFFooter( this, "Footer" );
#endif
m_pTFPlayerModelPanel = NULL;
m_pSelectAClassLabel = NULL;
m_pClassTipsPanel = new CTFClassTipsPanel( this, "ClassTipsPanel" );
Q_memset( m_pClassButtons, 0, sizeof( m_pClassButtons ) );
#ifndef _X360
char tempName[MAX_PATH];
for ( int i = 0 ; i < CLASS_COUNT_IMAGES ; ++i )
{
Q_snprintf( tempName, sizeof( tempName ), "countImage%d", i );
m_ClassCountImages[i] = new CTFImagePanel( this, tempName );
}
m_pCountLabel = NULL;
m_pLocalPlayerImage = new CTFImagePanel( this, "localPlayerImage" );
m_pLocalPlayerBG = new CTFImagePanel( this, "localPlayerBG" );
m_iLocalPlayerClass = TEAM_UNASSIGNED;
m_pClassButtons[TF_CLASS_SCOUT] = new CExImageButton( this, "scout", "", this );
m_pClassButtons[TF_CLASS_SOLDIER] = new CExImageButton( this, "soldier", "", this );
m_pClassButtons[TF_CLASS_PYRO] = new CExImageButton( this, "pyro", "", this );
m_pClassButtons[TF_CLASS_DEMOMAN] = new CExImageButton( this, "demoman", "", this );
m_pClassButtons[TF_CLASS_MEDIC] = new CExImageButton( this, "medic", "", this );
m_pClassButtons[TF_CLASS_HEAVYWEAPONS] = new CExImageButton( this, "heavyweapons", "", this );
m_pClassButtons[TF_CLASS_SNIPER] = new CExImageButton( this, "sniper", "", this );
m_pClassButtons[TF_CLASS_ENGINEER] = new CExImageButton( this, "engineer", "", this );
m_pClassButtons[TF_CLASS_SPY] = new CExImageButton( this, "spy", "", this );
m_pClassButtons[TF_CLASS_RANDOM] = new CExImageButton( this, "random", "", this );
#endif
m_pEditLoadoutButton = NULL;
m_nBaseMusicGuid = -1;
ListenForGameEvent( "localplayer_changeteam" );
ListenForGameEvent( "show_match_summary" );
Q_memset( m_pMvmUpgradeImages, 0, sizeof( m_pMvmUpgradeImages ) );
m_pMvmUpgradeImages[TF_CLASS_SCOUT] = new vgui::ImagePanel( this, "MvMUpgradeImageScout" );
m_pMvmUpgradeImages[TF_CLASS_SOLDIER] = new vgui::ImagePanel( this, "MvMUpgradeImageSolider" );
m_pMvmUpgradeImages[TF_CLASS_PYRO] = new vgui::ImagePanel( this, "MvMUpgradeImagePyro" );
m_pMvmUpgradeImages[TF_CLASS_DEMOMAN] = new vgui::ImagePanel( this, "MvMUpgradeImageDemoman" );
m_pMvmUpgradeImages[TF_CLASS_MEDIC] = new vgui::ImagePanel( this, "MvMUpgradeImageMedic" );
m_pMvmUpgradeImages[TF_CLASS_HEAVYWEAPONS] = new vgui::ImagePanel( this, "MvMUpgradeImageHeavy" );
m_pMvmUpgradeImages[TF_CLASS_SNIPER] = new vgui::ImagePanel( this, "MvMUpgradeImageSniper" );
m_pMvmUpgradeImages[TF_CLASS_ENGINEER] = new vgui::ImagePanel( this, "MvMUpgradeImageEngineer" );
m_pMvmUpgradeImages[TF_CLASS_SPY] = new vgui::ImagePanel( this, "MvMUpgradeImageSpy" );
vgui::ivgui()->AddTickSignal( GetVPanel() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
{
m_pClassHintIcons[i] = nullptr;
}
// Load the .res file
if ( ::input->IsSteamControllerActive() )
{
LoadControlSettings( "Resource/UI/ClassSelection_SC.res" );
m_pCancelHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "CancelHintIcon" ) );
m_pEditLoadoutHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "EditLoadoutHintIcon" ) );
m_pClassHintIcons[TF_CLASS_SCOUT] = dynamic_cast< CSCHintIcon* >( FindChildByName( "ScoutHintIcon" ) );
m_pClassHintIcons[TF_CLASS_SOLDIER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SoldierHintIcon" ) );
m_pClassHintIcons[TF_CLASS_PYRO] = dynamic_cast< CSCHintIcon* >( FindChildByName( "PyroHintIcon" ) );
m_pClassHintIcons[TF_CLASS_DEMOMAN] = dynamic_cast< CSCHintIcon* >( FindChildByName( "DemomanHintIcon" ) );
m_pClassHintIcons[TF_CLASS_HEAVYWEAPONS] = dynamic_cast< CSCHintIcon* >( FindChildByName( "HeavyHintIcon" ) );
m_pClassHintIcons[TF_CLASS_MEDIC] = dynamic_cast< CSCHintIcon* >( FindChildByName( "MedicHintIcon" ) );
m_pClassHintIcons[TF_CLASS_SPY] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SpyHintIcon" ) );
m_pClassHintIcons[TF_CLASS_ENGINEER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "EngineerHintIcon" ) );
m_pClassHintIcons[TF_CLASS_SNIPER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SniperHintIcon" ) );
m_pClassHintIcons[TF_CLASS_RANDOM] = dynamic_cast< CSCHintIcon* >( FindChildByName( "RandomHintIcon" ) );
for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
{
if ( m_pClassHintIcons[i] )
{
m_pClassHintIcons[i]->SetVisible( false );
}
}
SetMouseInputEnabled( false );
}
else
{
LoadControlSettings( "Resource/UI/ClassSelection.res" );
m_pCancelHintIcon = m_pEditLoadoutHintIcon = nullptr;
SetMouseInputEnabled( true );
}
m_pTFPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("TFPlayerModel") );
m_pSelectAClassLabel = dynamic_cast<CExLabel*>( FindChildByName( "ClassMenuSelect" ) );
m_pEditLoadoutButton = dynamic_cast<CExButton*>( FindChildByName( "EditLoadoutButton" ) );
const char *pTeamExtension = GetTeamNumber() == TF_TEAM_BLUE ? "blu" : "red";
for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
{
if ( !m_pClassButtons[i] )
continue;
if ( !IsValidTFPlayerClass( i ) && i != TF_CLASS_RANDOM )
continue;
m_pClassButtons[i]->SetImageSelected( CFmtStr( "class_sel_sm_%s_%s", g_aRawPlayerClassNamesShort[i], pTeamExtension ).Access() );
if( i != TF_CLASS_RANDOM )
{
m_pClassButtons[i]->SetArmedSound("misc/null.wav");
}
}
}
void CTFClassMenu::PerformLayout()
{
BaseClass::PerformLayout();
#ifndef _X360
m_pCountLabel = dynamic_cast< CExLabel * >( FindChildByName( "CountLabel" ) );
if ( m_pCountLabel )
{
m_pCountLabel->SizeToContents();
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFClassMenu::GetCurrentPlayerClass()
{
int iClass = TF_CLASS_HEAVYWEAPONS;
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() != TF_CLASS_UNDEFINED )
{
iClass = pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex();
}
return iClass;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CExImageButton *CTFClassMenu::GetCurrentClassButton()
{
const int iClass = GetCurrentPlayerClass();
m_iCurrentClassIndex = iRemapIndexToClass[ iClass ];
return m_pClassButtons[iClass];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::ShowPanel( bool bShow )
{
if ( bShow )
{
// Hide the other class menu
if ( gViewPortInterface )
{
gViewPortInterface->ShowPanel( GetTeamNumber() == TF_TEAM_BLUE ? PANEL_CLASS_RED : PANEL_CLASS_BLUE, false );
}
// can't change class if you're on the losing team during the "bonus time" after a team has won the round
if ( ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN &&
C_TFPlayer::GetLocalTFPlayer() &&
C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TFGameRules()->GetWinningTeam()
&& C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TEAM_SPECTATOR
&& C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TEAM_UNASSIGNED
&& GetSpectatorMode() == OBS_MODE_NONE ) ||
TFGameRules()->State_Get() == GR_STATE_GAME_OVER ||
( TFGameRules()->IsInTraining() && C_TFPlayer::GetLocalTFPlayer() &&
( C_TFPlayer::GetLocalTFPlayer()->GetPlayerClass() == NULL || C_TFPlayer::GetLocalTFPlayer()->GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED ) ) )
{
SetVisible( false );
CHudNotificationPanel *pNotifyPanel = GET_HUDELEMENT( CHudNotificationPanel );
if ( pNotifyPanel )
{
if ( C_TFPlayer::GetLocalTFPlayer() )
{
pNotifyPanel->SetupNotifyCustom( "#TF_CantChangeClassNow", "ico_notify_flag_moving", C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() );
}
}
return;
}
engine->CheckPoint( "ClassMenu" );
// Force us to reload our scheme, in case Steam Controller stuff has changed.
InvalidateLayout( true, true );
Activate();
m_iClassMenuKey = gameuifuncs->GetButtonCodeForBind( "changeclass" );
m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" );
SelectClass( GetCurrentPlayerClass() );
}
else
{
SetVisible( false );
if ( m_pTFPlayerModelPanel )
{
m_pTFPlayerModelPanel->ClearCarriedItems();
}
}
}
const char *g_pszLegacyClassSelectVCDWeapons[TF_LAST_NORMAL_CLASS] =
{
"", // TF_CLASS_UNDEFINED = 0,
"", // TF_CLASS_SCOUT, // weapons handled individually
"", // TF_CLASS_SNIPER, // weapons handled individually
"", // TF_CLASS_SOLDIER, // weapons handled individually
"tf_weapon_grenadelauncher", // TF_CLASS_DEMOMAN,
"tf_weapon_medigun", // TF_CLASS_MEDIC,
"tf_weapon_minigun", // TF_CLASS_HEAVYWEAPONS,
"tf_weapon_flamethrower", // TF_CLASS_PYRO,
"", // TF_CLASS_SPY, // weapons handled individually
"tf_weapon_wrench", // TF_CLASS_ENGINEER,
};
int g_iLegacyClassSelectWeaponSlots[TF_LAST_NORMAL_CLASS] =
{
LOADOUT_POSITION_PRIMARY, // TF_CLASS_UNDEFINED = 0,
LOADOUT_POSITION_PRIMARY, // TF_CLASS_SCOUT, // TF_FIRST_NORMAL_CLASS
LOADOUT_POSITION_PRIMARY, // TF_CLASS_SNIPER,
LOADOUT_POSITION_PRIMARY, // TF_CLASS_SOLDIER,
LOADOUT_POSITION_PRIMARY, // TF_CLASS_DEMOMAN,
LOADOUT_POSITION_SECONDARY, // TF_CLASS_MEDIC,
LOADOUT_POSITION_PRIMARY, // TF_CLASS_HEAVYWEAPONS,
LOADOUT_POSITION_PRIMARY, // TF_CLASS_PYRO,
LOADOUT_POSITION_MELEE, // TF_CLASS_SPY,
LOADOUT_POSITION_MELEE, // TF_CLASS_ENGINEER,
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::UpdateButtonSelectionStates( int iClass )
{
// Set the correct button as selected, all other buttons as not selected
for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
{
if ( !m_pClassButtons[ i ] )
continue;
m_pClassButtons[ i ]->SetSelected( i == iClass );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::SelectClass( int iClass )
{
if ( !engine->IsInGame() )
return;
if ( !m_pTFPlayerModelPanel )
return;
if ( !m_pClassTipsPanel )
return;
if ( !m_pEditLoadoutButton )
return;
// Update select hint icon for Steam Controller
for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
{
if ( m_pClassHintIcons[i] )
{
m_pClassHintIcons[i]->SetVisible( i == iClass );
}
}
// Were we random? If so, we'll force our class to refresh later to prevent the
// model panel thinking the class hasn't changed.
const bool bClassWasRandom = m_iCurrentClassIndex == TF_CLASS_RANDOM;
// Cache current player class
m_iCurrentClassIndex = iClass;
UpdateButtonSelectionStates( iClass );
bool bRandomClass = iClass == TF_CLASS_RANDOM;
if ( !IsValidTFPlayerClass( iClass ) && !bRandomClass )
{
m_pTFPlayerModelPanel->SetVisible( false );
m_pTFPlayerModelPanel->ClearCarriedItems();
return;
}
m_pTFPlayerModelPanel->SetVisible( true );
m_pTFPlayerModelPanel->ClearCarriedItems();
if ( bRandomClass )
{
m_pEditLoadoutButton->SetVisible( false );
if ( m_pEditLoadoutHintIcon )
{
m_pEditLoadoutHintIcon->SetVisible( false );
}
MDLHandle_t hModel = mdlcache->FindMDL( "models/class_menu/random_class_icon.mdl" );
m_pTFPlayerModelPanel->SetMDL( hModel );
m_pTFPlayerModelPanel->SetSequence( ACT_IDLE );
m_pTFPlayerModelPanel->InvalidateLayout( true, true ); // Updates position
m_pTFPlayerModelPanel->SetSkin( GetTeamNumber() == TF_TEAM_RED ? 0 : 1 );
mdlcache->Release( hModel ); // counterbalance addref from within FindMDL
}
else
{
bool bIsRobot = false;
// Check for Robot
static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" );
for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
{
CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
if ( !pItemData )
continue;
if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) )
{
bIsRobot = true;
break;
}
}
/*if ( pLocalPlayer )
{
int iRobot = 0;
CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iRobot, appear_as_mvm_robot );
bIsRobot = iRobot ? true : false;
}*/
m_pTFPlayerModelPanel->SetToPlayerClass( iClass, bIsRobot, bClassWasRandom );
m_pEditLoadoutButton->SetVisible( true );
if ( m_pEditLoadoutHintIcon )
{
m_pEditLoadoutHintIcon->SetVisible( true );
}
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
int iTeam = GetTeamNumber();
m_pTFPlayerModelPanel->SetTeam( iTeam );
}
LoadItems();
}
m_pClassTipsPanel->SetClass( iClass );
enginesound->StopSoundByGuid( m_nBaseMusicGuid );
CBroadcastRecipientFilter filter;
char nClassMusicStr[64];
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
sprintf( nClassMusicStr, "music.mvm_class_menu_0%i", iClass );
}
else
{
sprintf( nClassMusicStr, "music.class_menu_0%i", iClass );
}
CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, nClassMusicStr );
m_nBaseMusicGuid = enginesound->GetGuidForLastSoundEmitted();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::LoadItems()
{
const int iClass = m_pTFPlayerModelPanel->GetPlayerClass();
m_pTFPlayerModelPanel->ClearCarriedItems();
static CSchemaAttributeDefHandle pAttrDef_DisableFancyLoadoutAnim( "disable fancy class select anim" );
bool bCanUseFancyClassSelectAnimation = true;
static CSchemaAttributeDefHandle pAttrDef_ClassSelectOverrideVCD( "class select override vcd" );
CAttribute_String attrClassSelectOverrideVCD;
const char *pszVCD = "class_select";
for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
{
CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
if ( pItemData && pItemData->IsValid() )
{
m_pTFPlayerModelPanel->AddCarriedItem( pItemData );
// Certain items have different shapes and would interfere with our class select animations.
bCanUseFancyClassSelectAnimation = bCanUseFancyClassSelectAnimation
&& !pItemData->FindAttribute( pAttrDef_DisableFancyLoadoutAnim );
// Some items want to override the class select VCD
if ( pItemData->FindAttribute( pAttrDef_ClassSelectOverrideVCD, &attrClassSelectOverrideVCD ) )
{
const char *pszClassSelectOverrideVCD = attrClassSelectOverrideVCD.value().c_str();
if ( pszClassSelectOverrideVCD && *pszClassSelectOverrideVCD )
{
pszVCD = pszClassSelectOverrideVCD;
}
}
}
}
m_pTFPlayerModelPanel->PlayVCD( bCanUseFancyClassSelectAnimation ? pszVCD : NULL, g_pszLegacyClassSelectVCDWeapons[iClass] );
m_pTFPlayerModelPanel->HoldItemInSlot( g_iLegacyClassSelectWeaponSlots[iClass] );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnKeyCodePressed( KeyCode code )
{
m_KeyRepeat.KeyDown( code );
if ( code > KEY_0 && code <= KEY_9 )
{
const int iButton = code - KEY_0;
const int iClass = iRemapIndexToClass[ iButton ];
SelectClass( iClass );
Go();
}
else if ( code > KEY_PAD_0 && code <= KEY_PAD_9 )
{
const int iButton = code - KEY_PAD_0;
const int iClass = iRemapIndexToClass[ iButton ];
SelectClass( iClass );
Go();
}
else if( code == KEY_ENTER || code == KEY_SPACE || code == KEY_XBUTTON_A || code == KEY_XBUTTON_RTRIGGER || code == STEAMCONTROLLER_A )
{
Go();
}
else if ( ( m_iClassMenuKey != BUTTON_CODE_INVALID && m_iClassMenuKey == code ) ||
code == KEY_XBUTTON_BACK ||
code == KEY_XBUTTON_B ||
code == STEAMCONTROLLER_B ||
code == KEY_0 ||
code == KEY_PAD_0 )
{
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( pLocalPlayer && ( pLocalPlayer->GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED ) )
{
ShowPanel( false );
}
}
else if( code == KEY_XBUTTON_RIGHT || code == KEY_XSTICK1_RIGHT || code == STEAMCONTROLLER_DPAD_RIGHT )
{
int loopCheck = 0;
int nCurrentClass = GetRemappedMenuIndexForClass( m_iCurrentClassIndex );
do
{
loopCheck++;
nCurrentClass++;
nCurrentClass = ( nCurrentClass % TF_CLASS_MENU_BUTTONS );
} while( ( m_pClassButtons[ iRemapIndexToClass[nCurrentClass] ] == NULL ) && ( loopCheck < TF_CLASS_MENU_BUTTONS ) );
SelectClass( iRemapIndexToClass[ nCurrentClass ] );
}
else if ( code == STEAMCONTROLLER_Y )
{
OnCommand( "openloadout" );
}
else if( code == KEY_XBUTTON_LEFT || code == KEY_XSTICK1_LEFT || code == STEAMCONTROLLER_DPAD_LEFT )
{
int loopCheck = 0;
int nCurrentClass = GetRemappedMenuIndexForClass( m_iCurrentClassIndex );
do
{
loopCheck++;
nCurrentClass--;
if( nCurrentClass <= 0 )
{
nCurrentClass = GetRemappedMenuIndexForClass( TF_CLASS_RANDOM );
}
} while( ( m_pClassButtons[ iRemapIndexToClass[nCurrentClass] ] == NULL ) && ( loopCheck < TF_CLASS_MENU_BUTTONS ) );
SelectClass( iRemapIndexToClass[ nCurrentClass ] );
}
else if( code == KEY_XBUTTON_UP || code == KEY_XSTICK1_UP || code == STEAMCONTROLLER_DPAD_UP )
{
// Scroll class info text up
if ( g_lastPanel )
{
CExRichText *pRichText = dynamic_cast< CExRichText * >( g_lastPanel->FindChildByName( "classInfo" ) );
if ( pRichText )
{
PostMessage( pRichText, new KeyValues("MoveScrollBarDirect", "delta", 1) );
}
}
}
else if( code == KEY_XBUTTON_DOWN || code == KEY_XSTICK1_DOWN || code == STEAMCONTROLLER_DPAD_DOWN )
{
// Scroll class info text up
if ( g_lastPanel )
{
CExRichText *pRichText = dynamic_cast< CExRichText * >( g_lastPanel->FindChildByName( "classInfo" ) );
if ( pRichText )
{
PostMessage( pRichText, new KeyValues("MoveScrollBarDirect", "delta", -1) );
}
}
}
else
{
BaseClass::OnKeyCodePressed( code );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnKeyCodeReleased( vgui::KeyCode code )
{
m_KeyRepeat.KeyUp( code );
BaseClass::OnKeyCodeReleased( code );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnThink()
{
vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
if ( code )
{
OnKeyCodePressed( code );
}
// Get mouse cursor position
int aCursorPos[2];
vgui::input()->GetCursorPos( aCursorPos[0], aCursorPos[1] );
// Go through all buttons - if the mouse is within one, select that class
for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
{
if ( !m_pClassButtons[ i ] )
continue;
if ( m_iCurrentClassIndex != i && m_pClassButtons[ i ]->IsWithin( aCursorPos[0], aCursorPos[1] ) )
{
SelectClass( i );
}
}
//Always hide the health... this needs to be done every frame because a message from the server keeps resetting this.
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
pLocalPlayer->m_Local.m_iHideHUD |= HIDEHUD_HEALTH;
}
BaseClass::OnThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::SetCancelButtonVisible( bool bVisible )
{
SetVisibleButton( "CancelButton", bVisible );
if ( m_pCancelHintIcon )
{
m_pCancelHintIcon->SetVisible( bVisible );
}
if ( m_pSelectAClassLabel )
{
m_pSelectAClassLabel->SetVisible( !bVisible );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::Update()
{
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
// Force them to pick a class if they haven't picked one yet.
if ( ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() != TF_CLASS_UNDEFINED ) )
{
#ifdef _X360
if ( m_pFooter )
{
m_pFooter->ShowButtonLabel( "cancel", true );
}
#else
SetCancelButtonVisible( true );
if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() )
{
SetVisibleButton( "ResetButton", true );
}
else
{
SetVisibleButton( "ResetButton", false );
}
#endif
}
else
{
#ifdef _X360
if ( m_pFooter )
{
m_pFooter->ShowButtonLabel( "cancel", false );
}
#else
SetCancelButtonVisible( false );
SetVisibleButton( "ResetButton", false );
#endif
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Panel *CTFClassMenu::CreateControlByName( const char *controlName )
{
if ( !Q_stricmp( "CIconPanel", controlName ) )
{
return new CIconPanel( this, "icon_panel" );
}
else
{
return BaseClass::CreateControlByName( controlName );
}
}
//-----------------------------------------------------------------------------
// Catch the mouseover event and set the active class
//-----------------------------------------------------------------------------
void CTFClassMenu::OnShowPage( vgui::Panel *panel, const char *pagename )
{
for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
{
if (m_pClassButtons[i] == panel )
{
// SelectClass( i );
break;
}
}
}
//-----------------------------------------------------------------------------
// Draw nothing
//-----------------------------------------------------------------------------
void CTFClassMenu::PaintBackground( void )
{
}
//-----------------------------------------------------------------------------
// Do things that should be done often, eg number of players in the
// selected class
//-----------------------------------------------------------------------------
void CTFClassMenu::OnTick( void )
{
//When a player changes teams, their class and team values don't get here
//necessarily before the command to update the class menu. This leads to the cancel button
//being visible and people cancelling before they have a class. check for class == TF_CLASS_UNDEFINED and if so
//hide the cancel button
if ( !IsVisible() )
return;
#ifndef _X360
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
// Force them to pick a class if they haven't picked one yet.
if ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() == TF_CLASS_UNDEFINED )
{
SetCancelButtonVisible( false );
SetVisibleButton( "ResetButton", false );
}
UpdateClassCounts();
#endif
BaseClass::OnTick();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnClose()
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
// Clear the HIDEHUD_HEALTH bit we hackily added. Turns out prediction
// was restoring these bits every frame. Unfortunately, prediction
// is off for karts which means the spell hud item would disappear if you
// brought up this menu and returned.
pLocalPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_HEALTH;
}
ShowPanel( false );
BaseClass::OnClose();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::SetVisible( bool state )
{
BaseClass::SetVisible( state );
m_KeyRepeat.Reset();
if ( state )
{
engine->ServerCmd( "menuopen" ); // to the server
engine->ClientCmd( "_cl_classmenuopen 1" ); // for other panels
CBroadcastRecipientFilter filter;
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.mvm_class_menu" );
}
else
{
CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.class_menu" );
}
CheckMvMUpgrades();
}
else
{
engine->ServerCmd( "menuclosed" );
engine->ClientCmd( "_cl_classmenuopen 0" );
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
CBaseEntity::StopSound( SOUND_FROM_UI_PANEL, "music.mvm_class_menu" );
}
else
{
CBaseEntity::StopSound( SOUND_FROM_UI_PANEL, "music.class_menu" );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::Go()
{
const int iClass = m_iCurrentClassIndex;
if ( iClass == TF_CLASS_UNDEFINED )
return;
// Check class limits
if ( TFGameRules() && !TFGameRules()->CanPlayerChooseClass( C_TFPlayer::GetLocalTFPlayer(), iClass ) )
return;
#if defined( REPLAY_ENABLED )
// Display replay recording message if appropriate
int &nDisplayedConnectedRecording = CPlayerSpawnCache::Instance().m_Data.m_nDisplayedConnectedRecording;
if ( g_pReplay->IsReplayEnabled() &&
!g_pEngineClientReplay->IsPlayingReplayDemo() && // FIXME: We shouldn't need this here but for some reason the engine thinks a replay is recording during demo playback, even though replay_recording has a FCVAR_DONTRECORD flag
!nDisplayedConnectedRecording &&
replay_replaywelcomedlgcount.GetInt() <= MAX_TIMES_TO_SHOW_REPLAY_WELCOME_DLG )
{
wchar_t wText[256];
wchar wKeyBind[80];
char szText[256];
const char *pSaveReplayKey = engine->Key_LookupBinding( "save_replay" );
if ( !pSaveReplayKey )
{
pSaveReplayKey = "< not bound >";
}
g_pVGuiLocalize->ConvertANSIToUnicode( pSaveReplayKey, wKeyBind, sizeof( wKeyBind ) );
g_pVGuiLocalize->ConstructString_safe( wText, g_pVGuiLocalize->Find( "#Replay_ConnectRecording" ), 1, wKeyBind );
g_pVGuiLocalize->ConvertUnicodeToANSI( wText, szText, sizeof( szText ) );
extern ConVar replay_msgduration_connectrecording;
g_pClientMode->DisplayReplayMessage( szText, replay_msgduration_connectrecording.GetFloat(), false, NULL, true );
// Don't execute this clause next time the player spawns, unless the cache has been cleared
++nDisplayedConnectedRecording;
// Increment (archives)
replay_replaywelcomedlgcount.SetValue( replay_replaywelcomedlgcount.GetInt() + 1 );
}
#endif
// This will complete any pending replay, commit if necessary, and clear - this way when the player respawns
// we will start with a fresh replay for the new life.
g_pClientReplayContext->OnPlayerClassChanged();
// Change class
BaseClass::OnCommand( CFmtStr( "joinclass %s", g_aRawPlayerClassNames[ iClass ] ).Access() );
CBroadcastRecipientFilter filter;
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
{
CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.mvm_class_select" );
}
else
{
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnCommand( const char *command )
{
if ( !V_strnicmp( command, "select", 6 ) )
{
const char *pClass = command + 6;
const int iClass = atoi( pClass );
// Avoid restarting the animation if the user selected on the same class
if ( iClass != m_iCurrentClassIndex )
{
SelectClass( iClass );
}
else
{
// Ensure selection states, in case the user clicks a button and drags away
UpdateButtonSelectionStates( iClass );
}
Go();
}
else if ( !V_strnicmp( command, "resetclass", 10 ) )
{
if ( TFGameRules() && !TFGameRules()->IsInHighlanderMode() )
return;
engine->ClientCmd( const_cast<char *>( command ) );
}
else if ( !V_strnicmp( command, "openloadout", 11 ) )
{
// Let this panel know when you've closed, so we can reload items
EconUI()->AddPanelCloseListener( this );
// Make the back button close, rather than go back to the econ root panel
EconUI()->SetClosePanel( -m_iCurrentClassIndex );
// Set team number, so the model's color will match
EconUI()->SetDefaultTeam( GetTeamNumber() );
// Go directly to the loadout for the selected class
EconUI()->OpenEconUI( -m_iCurrentClassIndex );
}
else
{
BaseClass::OnCommand( command );
}
}
//-----------------------------------------------------------------------------
// Purpose: Console command to select a class
//-----------------------------------------------------------------------------
void CTFClassMenu::Join_Class( const CCommand &args )
{
if ( args.ArgC() > 1 )
{
char cmd[256];
Q_snprintf( cmd, sizeof( cmd ), "joinclass %s", args.Arg( 1 ) );
OnCommand( cmd );
ShowPanel( false );
}
}
static const char *g_sDialogVariables[] = {
"",
"numScout",
"numSoldier",
"numPyro",
"numDemoman",
"numHeavy",
"numEngineer",
"numMedic",
"numSniper",
"numSpy",
"",
};
static const char *g_sClassImagesBlue[] = {
"",
"class_sel_sm_scout_blu",
"class_sel_sm_soldier_blu",
"class_sel_sm_pyro_blu",
"class_sel_sm_demo_blu",
"class_sel_sm_heavy_blu",
"class_sel_sm_engineer_blu",
"class_sel_sm_medic_blu",
"class_sel_sm_sniper_blu",
"class_sel_sm_spy_blu",
"class_sel_sm_scout_blu",
};
static const char *g_sClassImagesRed[] = {
"",
"class_sel_sm_scout_red",
"class_sel_sm_soldier_red",
"class_sel_sm_pyro_red",
"class_sel_sm_demo_red",
"class_sel_sm_heavy_red",
"class_sel_sm_engineer_red",
"class_sel_sm_medic_red",
"class_sel_sm_sniper_red",
"class_sel_sm_spy_red",
"class_sel_sm_scout_red",
};
int g_ClassDefinesRemap[] = {
0,
TF_CLASS_SCOUT,
TF_CLASS_SOLDIER,
TF_CLASS_PYRO,
TF_CLASS_DEMOMAN,
TF_CLASS_HEAVYWEAPONS,
TF_CLASS_ENGINEER,
TF_CLASS_MEDIC,
TF_CLASS_SNIPER,
TF_CLASS_SPY,
TF_CLASS_CIVILIAN,
};
void CTFClassMenu::UpdateNumClassLabels( int iTeam )
{
#ifndef _X360
int nTotalCount = 0;
// count how many of each class there are
if ( !g_TF_PR )
return;
if ( iTeam < FIRST_GAME_TEAM || iTeam >= TF_TEAM_COUNT ) // invalid team number
return;
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
bool bSpectator = pLocalPlayer && pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR;
int iLocalPlayerClass = TF_CLASS_UNDEFINED;
if ( pLocalPlayer )
{
iLocalPlayerClass = pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex();
}
if ( iLocalPlayerClass == TF_CLASS_UNDEFINED )
{
m_iLocalPlayerClass = iLocalPlayerClass;
if ( m_pLocalPlayerImage && m_pLocalPlayerImage->IsVisible() )
{
m_pLocalPlayerImage->SetVisible( false );
}
if ( m_pLocalPlayerBG && m_pLocalPlayerBG->IsVisible() )
{
m_pLocalPlayerBG->SetVisible( false );
}
}
for( int i = TF_FIRST_NORMAL_CLASS ; i <= TF_LAST_NORMAL_CLASS ; i++ )
{
if ( bSpectator == true )
{
SetDialogVariable( g_sDialogVariables[i], "" );
continue;
}
int classCount = g_TF_PR->GetCountForPlayerClass( iTeam, g_ClassDefinesRemap[i], false );
int iClassLimit = TFGameRules()->GetClassLimit( g_ClassDefinesRemap[i] );
if ( iClassLimit != NO_CLASS_LIMIT )
{
if ( classCount >= iClassLimit )
{
if ( classCount > 0 )
{
wchar_t wTemp[32];
wchar_t wzCount[10];
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", classCount );
g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("TF_ClassLimitHit"), 1, wzCount );
SetDialogVariable( g_sDialogVariables[i], wTemp );
}
else
{
SetDialogVariable( g_sDialogVariables[i], g_pVGuiLocalize->Find("TF_ClassLimitHit_None") );
}
}
else
{
wchar_t wTemp[32];
wchar_t wzCount[10];
_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", classCount );
wchar_t wzMax[10];
_snwprintf( wzMax, ARRAYSIZE( wzMax ), L"%d", iClassLimit );
g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("TF_ClassLimitUnder"), 2, wzCount, wzMax );
SetDialogVariable( g_sDialogVariables[i], wTemp );
}
}
else if ( classCount > 0 )
{
SetDialogVariable( g_sDialogVariables[i], classCount );
}
else
{
SetDialogVariable( g_sDialogVariables[i], "" );
}
if ( g_ClassDefinesRemap[i] == iLocalPlayerClass )
{
// take 1 off the count for the images since the local player has their own image already
if ( classCount > 0 )
{
classCount--;
}
if ( m_pLocalPlayerImage )
{
if ( !m_pLocalPlayerImage->IsVisible() )
{
m_pLocalPlayerImage->SetVisible( true );
}
if ( m_iLocalPlayerClass != iLocalPlayerClass )
{
m_iLocalPlayerClass = iLocalPlayerClass;
m_pLocalPlayerImage->SetImage( iTeam == TF_TEAM_BLUE ? g_sClassImagesBlue[i] : g_sClassImagesRed[i] );
}
}
if ( m_pLocalPlayerBG && !m_pLocalPlayerBG->IsVisible() )
{
m_pLocalPlayerBG->SetVisible( true );
}
}
if ( nTotalCount < CLASS_COUNT_IMAGES )
{
for ( int j = 0 ; j < classCount ; ++j )
{
CTFImagePanel *pImage = m_ClassCountImages[nTotalCount];
if ( pImage )
{
pImage->SetVisible( true );
pImage->SetImage( iTeam == TF_TEAM_BLUE ? g_sClassImagesBlue[i] : g_sClassImagesRed[i] );
}
nTotalCount++;
if ( nTotalCount >= CLASS_COUNT_IMAGES )
{
break;
}
}
}
}
if ( nTotalCount == 0 )
{
// no classes for our team yet
if ( m_pCountLabel && m_pCountLabel->IsVisible() )
{
m_pCountLabel->SetVisible( false );
}
}
else
{
if ( m_pCountLabel && !m_pCountLabel->IsVisible() )
{
m_pCountLabel->SetVisible( true );
}
}
// turn off any unused images
while ( nTotalCount < CLASS_COUNT_IMAGES )
{
CTFImagePanel *pImage = m_ClassCountImages[nTotalCount];
if ( pImage )
{
pImage->SetVisible( false );
}
nTotalCount++;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::FireGameEvent( IGameEvent *event )
{
const char *pszEventName = event->GetName();
// when we are changing levels
if ( FStrEq( pszEventName, "localplayer_changeteam" ) )
{
if ( IsVisible() )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
int iTeam = pLocalPlayer->GetTeamNumber();
if ( iTeam != GetTeamNumber() )
{
ShowPanel( false );
if ( iTeam == TF_TEAM_BLUE )
{
gViewPortInterface->ShowPanel( PANEL_CLASS_BLUE, true );
}
else
{
gViewPortInterface->ShowPanel( PANEL_CLASS_RED, true );
}
}
}
}
}
else if ( FStrEq( pszEventName, "show_match_summary" ) )
{
if ( IsVisible() )
{
ShowPanel( false );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnEconUIClosed()
{
// Reload items on model panel, in case anything's changed
LoadItems();
}
int g_nNumUpgradeIconsForLastHint = 0;
//-----------------------------------------------------------------------------
void CTFClassMenu::CheckMvMUpgrades()
{
// Set MvM Icons invisible
for ( int icons = 0; icons < ARRAYSIZE( m_pMvmUpgradeImages ); ++icons )
{
if ( m_pMvmUpgradeImages[icons] == NULL )
continue;
m_pMvmUpgradeImages[icons]->SetVisible( false );
}
if ( !TFGameRules() || !TFGameRules()->IsMannVsMachineMode() )
return;
CMannVsMachineStats *pStats = MannVsMachineStats_GetInstance();
if ( !pStats )
return;
CUtlVector< CUpgradeInfo > *upgrades = pStats->GetLocalPlayerUpgrades();
int nShowUpgradingHint = -1;
int nNumUpgradeIconsForHint = 0;
for ( int i = 0; i < upgrades->Count(); ++i )
{
vgui::Panel *pUpgradeImage = m_pMvmUpgradeImages[upgrades->Element(i).m_iPlayerClass];
if ( !pUpgradeImage )
continue;
if ( !pUpgradeImage->IsVisible() )
{
pUpgradeImage->SetVisible( true );
nNumUpgradeIconsForHint++;
// Only show the hint if we've shown it 3 or less times ever
if ( nShowUpgradingHint == -1 && tf_mvm_classupgradehelpcount.GetInt() < 3 )
{
int nY;
pUpgradeImage->GetPos( nShowUpgradingHint, nY );
nShowUpgradingHint += pUpgradeImage->GetWide() / 2;
}
}
}
// Only show the hint if there are more upgrade icon than the last time we openned the menu
if ( nShowUpgradingHint != -1 && g_nNumUpgradeIconsForLastHint < nNumUpgradeIconsForHint )
{
CExplanationPopup *pPopup = dynamic_cast< CExplanationPopup* >( FindChildByName("StartExplanation") );
if ( pPopup )
{
pPopup->SetCalloutInParentsX( nShowUpgradingHint );
pPopup->Popup();
g_nNumUpgradeIconsForLastHint = nNumUpgradeIconsForHint;
tf_mvm_classupgradehelpcount.SetValue( tf_mvm_classupgradehelpcount.GetInt() + 1 );
}
}
}