summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/class_loadout_panel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/class_loadout_panel.cpp')
-rw-r--r--game/client/tf/vgui/class_loadout_panel.cpp1539
1 files changed, 1539 insertions, 0 deletions
diff --git a/game/client/tf/vgui/class_loadout_panel.cpp b/game/client/tf/vgui/class_loadout_panel.cpp
new file mode 100644
index 0000000..5db5fba
--- /dev/null
+++ b/game/client/tf/vgui/class_loadout_panel.cpp
@@ -0,0 +1,1539 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+#include "class_loadout_panel.h"
+#include "c_tf_player.h"
+#include "vgui_controls/CheckButton.h"
+#include "econ_gcmessages.h"
+#include "gc_clientsystem.h"
+#include "tf_item_system.h"
+#include "loadout_preset_panel.h"
+#include "econ_item_description.h"
+#include "item_style_select_dialog.h"
+#include "vgui/IInput.h"
+#include "vgui_controls/PanelListPanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+extern ConVar tf_respawn_on_loadoutchanges;
+
+ConVar tf_show_preset_explanation_in_class_loadout( "tf_show_preset_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ConVar tf_show_taunt_explanation_in_class_loadout( "tf_show_taunt_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+
+void ParticleSlider_UpdateRequest( int iLoadoutPosition, float value )
+{
+ CClassLoadoutPanel *pPanel = g_pClassLoadoutPanel;
+ if ( !pPanel )
+ return;
+
+ CEconItemView *pHat = pPanel->GetItemInSlot( iLoadoutPosition );
+ if ( !pHat )
+ return;
+
+ // does this hat even have a particle effect
+ static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
+ uint32 iHasEffect = 0;
+ if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
+ return;
+
+ // Check for use head toggle
+ static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
+ uint32 iUseHead = 0;
+ if ( !pHat->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) || iUseHead == 0 )
+ return;
+
+ // Look for the attribute and request to change it
+ static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
+ uint32 iOffSet = 0;
+ if ( !pHat->FindAttribute( pAttrDef_VerticalOffset, &iOffSet ) && value == 0 )
+ {
+ return;
+ }
+ else
+ {
+ const float& flAttrValue = (float&)iOffSet;
+ if ( value == flAttrValue )
+ return; // no change do nothing
+ }
+
+ // Send a message to the GC to request a change
+ GCSDK::CProtoBufMsg<CMsgSetItemEffectVerticalOffset> msg( k_EMsgGCSetItemEffectVerticalOffset );
+ msg.Body().set_item_id( pHat->GetItemID() );
+ msg.Body().set_offset( value );
+ GCClientSystem()->BSendMessage( msg );
+}
+
+void HatOffset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
+{
+ ConVarRef cVarRef( pConVar );
+ ParticleSlider_UpdateRequest( LOADOUT_POSITION_HEAD, cVarRef.GetFloat() );
+}
+ConVar tf_hat_effect_offset( "tf_hat_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", HatOffset_Callback );
+
+void Misc1Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
+{
+ ConVarRef cVarRef( pConVar );
+ ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC, cVarRef.GetFloat() );
+}
+ConVar tf_misc1_effect_offset( "tf_misc1_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc1Offset_Callback );
+
+void Misc2Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
+{
+ ConVarRef cVarRef( pConVar );
+ ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC2, cVarRef.GetFloat() );
+}
+ConVar tf_misc2_effect_offset( "tf_misc2_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc2Offset_Callback );
+
+
+// Hacky solution to different classes wanting different slots visible in their loadouts, and in different positions
+struct LoadoutPanelPositioningInstance
+{
+ int m_iPos[NUM_ITEM_PANELS_IN_LOADOUT];
+};
+
+bool IsTauntPanelPosition( int iButtonPos )
+{
+ return iButtonPos >= 9 && iButtonPos <= 16;
+}
+
+const LoadoutPanelPositioningInstance g_DefaultLoadoutPanelPositioning =
+{
+ {
+ 1, // LOADOUT_POSITION_PRIMARY = 0,
+ 2, // LOADOUT_POSITION_SECONDARY,
+ 3, // LOADOUT_POSITION_MELEE,
+ 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
+ 0, // LOADOUT_POSITION_BUILDING,
+ 0, // LOADOUT_POSITION_PDA,
+ 0, // LOADOUT_POSITION_PDA2,
+ 5, // LOADOUT_POSITION_HEAD,
+ 6, // LOADOUT_POSITION_MISC,
+ 8, // LOADOUT_POSITION_ACTION,
+ 7, // LOADOUT_POSITION_MISC2,
+ 9, // LOADOUT_POSITION_TAUNT,
+ 10, // LOADOUT_POSITION_TAUNT2,
+ 11, // LOADOUT_POSITION_TAUNT3,
+ 12, // LOADOUT_POSITION_TAUNT4,
+ 13, // LOADOUT_POSITION_TAUNT5,
+ 14, // LOADOUT_POSITION_TAUNT6,
+ 15, // LOADOUT_POSITION_TAUNT7,
+ 16, // LOADOUT_POSITION_TAUNT8,
+
+#ifdef STAGING_ONLY
+ 0, // LOADOUT_POSITION_PDA_ADDON1,
+ 0, // LOADOUT_POSITION_PDA_ADDON2,
+ 0, // LOADOUT_POSITION_PDA3,
+ //9, // LOADOUT_POSITION_MISC3,
+ //10, // LOADOUT_POSITION_MISC4,
+ //11, // LOADOUT_POSITION_MISC5,
+ //12, // LOADOUT_POSITION_MISC6,
+ //13, // LOADOUT_POSITION_MISC7,
+ //14, // LOADOUT_POSITION_MISC8,
+ //15, // LOADOUT_POSITION_MISC9,
+ //16, // LOADOUT_POSITION_MISC10,
+ 0, // LOADOUT_POSITION_BUILDING2,
+#endif // STAGING_ONLY
+ }
+};
+
+const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Spy =
+{
+ {
+ 0, // LOADOUT_POSITION_PRIMARY = 0,
+ 1, // LOADOUT_POSITION_SECONDARY,
+ 2, // LOADOUT_POSITION_MELEE,
+ 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
+ 4, // LOADOUT_POSITION_BUILDING, // sapper
+ 0, // LOADOUT_POSITION_PDA, // disguise kit (Hidden)
+ 3, // LOADOUT_POSITION_PDA2, // Watch
+ 5, // LOADOUT_POSITION_HEAD,
+ 6, // LOADOUT_POSITION_MISC,
+ 8, // LOADOUT_POSITION_ACTION,
+ 7, // LOADOUT_POSITION_MISC2,
+ 9, // LOADOUT_POSITION_TAUNT,
+ 10, // LOADOUT_POSITION_TAUNT2,
+ 11, // LOADOUT_POSITION_TAUNT3,
+ 12, // LOADOUT_POSITION_TAUNT4,
+ 13, // LOADOUT_POSITION_TAUNT5,
+ 14, // LOADOUT_POSITION_TAUNT6,
+ 15, // LOADOUT_POSITION_TAUNT7,
+ 16, // LOADOUT_POSITION_TAUNT8,
+#ifdef STAGING_ONLY
+ 0, // LOADOUT_POSITION_PDA_ADDON1,
+ 0, // LOADOUT_POSITION_PDA_ADDON2,
+ 0, // LOADOUT_POSITION_PDA3,
+ //9, // LOADOUT_POSITION_MISC3,
+ //10, // LOADOUT_POSITION_MISC4,
+ //11, // LOADOUT_POSITION_MISC5,
+ //12, // LOADOUT_POSITION_MISC6,
+ //13, // LOADOUT_POSITION_MISC7,
+ //14, // LOADOUT_POSITION_MISC8,
+ //15, // LOADOUT_POSITION_MISC9,
+ //16, // LOADOUT_POSITION_MISC10,
+ 0, // LOADOUT_POSITION_BUILDING2,
+#endif // STAGING_ONLY
+ }
+};
+
+const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Engineer =
+{
+ {
+ 1, // LOADOUT_POSITION_PRIMARY = 0,
+ 2, // LOADOUT_POSITION_SECONDARY,
+ 3, // LOADOUT_POSITION_MELEE,
+ 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
+ 0, // LOADOUT_POSITION_BUILDING,
+ 4, // LOADOUT_POSITION_PDA,
+ 0, // LOADOUT_POSITION_PDA2,
+ 5, // LOADOUT_POSITION_HEAD,
+ 6, // LOADOUT_POSITION_MISC,
+ 8, // LOADOUT_POSITION_ACTION,
+ 7, // LOADOUT_POSITION_MISC2,
+ 9, // LOADOUT_POSITION_TAUNT,
+ 10, // LOADOUT_POSITION_TAUNT2,
+ 11, // LOADOUT_POSITION_TAUNT3,
+ 12, // LOADOUT_POSITION_TAUNT4,
+ 13, // LOADOUT_POSITION_TAUNT5,
+ 14, // LOADOUT_POSITION_TAUNT6,
+ 15, // LOADOUT_POSITION_TAUNT7,
+ 16, // LOADOUT_POSITION_TAUNT8,
+#ifdef STAGING_ONLY
+ 17, // LOADOUT_POSITION_PDA_ADDON1,
+ 18, // LOADOUT_POSITION_PDA_ADDON2,
+ 0, // LOADOUT_POSITION_PDA3,
+ //9, // LOADOUT_POSITION_MISC3,
+ //10, // LOADOUT_POSITION_MISC4,
+ //11, // LOADOUT_POSITION_MISC5,
+ //12, // LOADOUT_POSITION_MISC6,
+ //13, // LOADOUT_POSITION_MISC7,
+ //14, // LOADOUT_POSITION_MISC8,
+ //15, // LOADOUT_POSITION_MISC9,
+ //16, // LOADOUT_POSITION_MISC10,
+ 0, // LOADOUT_POSITION_BUILDING2,
+#endif // STAGING_ONLY
+ }
+};
+
+const LoadoutPanelPositioningInstance *g_VisibleLoadoutSlotsPerClass[] =
+{
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_UNDEFINED
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SCOUT
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SNIPER
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SOLDIER
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_DEMOMAN
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_MEDIC
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_HEAVYWEAPONS
+ &g_DefaultLoadoutPanelPositioning, // TF_CLASS_PYRO
+ &g_LoadoutPanelPositioning_Spy, // TF_CLASS_SPY
+ &g_LoadoutPanelPositioning_Engineer, // TF_CLASS_ENGINEER
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_VisibleLoadoutSlotsPerClass ) == TF_LAST_NORMAL_CLASS );
+
+//-----------------------------------------------------------------------------
+// Particle Effect Slider
+//-----------------------------------------------------------------------------
+CLoadoutItemOptionsPanel::CLoadoutItemOptionsPanel( Panel *parent, const char *pName ) : vgui::EditablePanel( parent, pName )
+{
+ m_pHatParticleSlider = NULL;
+ m_pHatParticleUseHeadButton = NULL;
+
+ m_iCurrentClassIndex = -1;
+ m_eItemSlot = LOADOUT_POSITION_INVALID;
+
+ m_pListPanel = new vgui::PanelListPanel( this, "PanelListPanel" );
+ m_pListPanel->SetFirstColumnWidth( 0 );
+ m_pHatParticleSlider = new CCvarSlider( m_pListPanel, "HatParticleSlider" );
+ m_pHatParticleSlider->AddActionSignalTarget( this );
+ m_pHatParticleUseHeadButton = new vgui::CheckButton( m_pListPanel, "HatUseHeadCheckButton", "#GameUI_ParticleHatUseHead" );
+ m_pHatParticleUseHeadButton->AddActionSignalTarget( this );
+ m_pSetStyleButton = new CExButton( m_pListPanel, "SetStyleButton", "#TF_Item_SelectStyle" );
+ m_pSetStyleButton->AddActionSignalTarget( this );
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ LoadControlSettings( "resource/UI/ItemOptionsPanel.res" );
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+ m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::OnCommand( const char *command )
+{
+ if ( FStrEq( command, "particle_button_clicked" ) )
+ {
+ UpdateItemOptionsUI();
+ return;
+ }
+ else if ( FStrEq( command, "particle_use_head_clicked" ) )
+ {
+ // Grab current hat
+ CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
+ if ( !pHat )
+ return;
+
+ // does this hat even have a particle effect
+ static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
+ uint32 iHasEffect = 0;
+ if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
+ return;
+
+ // Send a message to the GC to request a change
+ GCSDK::CProtoBufMsg<CMsgSetHatEffectUseHeadOrigin> msg( k_EMsgGCSetHatEffectUseHeadOrigin );
+ msg.Body().set_item_id( pHat->GetItemID() );
+ msg.Body().set_use_head( m_pHatParticleUseHeadButton->IsSelected() );
+ GCClientSystem()->BSendMessage( msg );
+ return;
+ }
+ else if ( FStrEq( command, "set_style" ) )
+ {
+ CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
+ CStyleSelectDialog *pStyle = vgui::SETUP_PANEL( new CStyleSelectDialog( GetParent(), pHat ) );
+ if ( pStyle )
+ {
+ pStyle->Show();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
+{
+ if ( FStrEq( pParams->GetName(), "SliderDragEnd" ) )
+ {
+ m_pHatParticleSlider->ApplyChanges();
+ }
+
+ BaseClass::OnMessage( pParams, hFromPanel );
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::SetItemSlot( loadout_positions_t eItemSlot, int iClassIndex )
+{
+ m_eItemSlot = eItemSlot;
+ m_iCurrentClassIndex = iClassIndex;
+ // Init the Slider based on the slot
+ const char * pszConVarName = NULL;
+
+ switch ( eItemSlot )
+ {
+ case LOADOUT_POSITION_HEAD :
+ pszConVarName = "tf_hat_effect_offset";
+ break;
+ case LOADOUT_POSITION_MISC :
+ pszConVarName = "tf_misc1_effect_offset";
+ break;
+ case LOADOUT_POSITION_MISC2 :
+ pszConVarName = "tf_misc2_effect_offset";
+ break;
+ default:
+ break;
+ }
+
+ if ( pszConVarName )
+ {
+ m_pHatParticleSlider->SetupSlider( -8, 8, pszConVarName, false );
+ }
+
+ m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
+ m_pHatParticleSlider->SetTickCaptions( "", "" );
+
+ UpdateItemOptionsUI();
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::UpdateItemOptionsUI()
+{
+ if ( m_eItemSlot == LOADOUT_POSITION_INVALID )
+ return;
+
+ m_pListPanel->RemoveAll();
+
+ // Add controls for various item options
+ AddControlsParticleEffect();
+ AddControlsSetStyle();
+
+ // Bail if no controls added
+ if ( m_pListPanel->GetItemCount() == 0 )
+ {
+ // We should have some controls if we get to this point.
+ Assert( 0 );
+ SetVisible( false );
+ return;
+ }
+
+ // Resize the background and list panel to contain all the controls
+ int nVertPixels = m_pListPanel->ComputeVPixelsNeeded();
+ int nNewTall = Min( 200, nVertPixels );
+ m_pListPanel->SetTall( nNewTall );
+ SetTall( nNewTall );
+ InvalidateLayout( true, false );
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::AddControlsParticleEffect( void ) const
+{
+ m_pHatParticleUseHeadButton->SetVisible( false );
+ m_pHatParticleSlider->SetVisible( false );
+
+ CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
+ if ( pItem )
+ {
+ // does this hat even have a particle effect
+ static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
+ uint32 iValue = 0;
+ if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect, &iValue ) )
+ {
+ m_pHatParticleUseHeadButton->SetVisible( true );
+ m_pListPanel->AddItem( NULL, m_pHatParticleUseHeadButton );
+ m_pListPanel->AddItem( NULL, m_pHatParticleSlider );
+
+ // Check for use head toggle
+ static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
+ uint32 iUseHead = 0;
+ if ( pItem->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) && iUseHead > 0 )
+ {
+ m_pHatParticleSlider->SetVisible( true );
+
+ m_pHatParticleUseHeadButton->SetSelected( true );
+ m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
+ m_pHatParticleSlider->Repaint();
+
+ // Get offset if it exists
+ static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
+ uint32 iOffset = 0;
+ if ( pItem->FindAttribute( pAttrDef_VerticalOffset, &iOffset ) )
+ {
+ m_pHatParticleSlider->SetSliderValue( (float&)iOffset );
+ }
+ }
+ else
+ {
+ m_pHatParticleUseHeadButton->SetSelected( false );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void CLoadoutItemOptionsPanel::AddControlsSetStyle( void ) const
+{
+ m_pSetStyleButton->SetVisible( false );
+
+ CEconItemView *pItem = GetItem();
+ if ( pItem && pItem->GetStaticData()->GetNumStyles() )
+ {
+ m_pSetStyleButton->SetVisible( true );
+ m_pListPanel->AddItem( NULL, m_pSetStyleButton );
+ }
+}
+
+//-----------------------------------------------------------------------------
+CEconItemView* CLoadoutItemOptionsPanel::GetItem( void ) const
+{
+ return TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
+}
+
+CClassLoadoutPanel *g_pClassLoadoutPanel = NULL;
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CClassLoadoutPanel::CClassLoadoutPanel( vgui::Panel *parent )
+ : CBaseLoadoutPanel( parent, "class_loadout_panel" )
+ , m_pItemOptionPanelKVs( NULL )
+{
+ m_iCurrentClassIndex = TF_CLASS_UNDEFINED;
+ m_iCurrentTeamIndex = TF_TEAM_RED;
+ m_iCurrentSlotIndex = -1;
+ m_pPlayerModelPanel = NULL;
+ m_pSelectionPanel = NULL;
+ m_pTauntHintLabel = NULL;
+ m_pTauntLabel = NULL;
+ m_pTauntCaratLabel = NULL;
+ m_pPassiveAttribsLabel = NULL;
+ m_pLoadoutPresetPanel = NULL;
+ m_pPresetsExplanationPopup = NULL;
+ m_pTauntsExplanationPopup = NULL;
+ m_pBuildablesButton = NULL;
+
+ m_pCharacterLoadoutButton = NULL;
+ m_pTauntLoadoutButton = NULL;
+
+ m_bInTauntLoadoutMode = false;
+
+ g_pClassLoadoutPanel = this;
+
+ m_pItemOptionPanel = new CLoadoutItemOptionsPanel( this, "ItemOptionsPanel" );
+}
+
+CClassLoadoutPanel::~CClassLoadoutPanel()
+{
+ if ( m_pItemOptionPanelKVs )
+ {
+ m_pItemOptionPanelKVs->deleteThis();
+ m_pItemOptionPanelKVs = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ LoadControlSettings( "Resource/UI/ClassLoadoutPanel.res" );
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") );
+ m_pTauntHintLabel = dynamic_cast<vgui::Label*>( FindChildByName("TauntHintLabel") );
+ m_pTauntLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntLabel") );
+ m_pTauntCaratLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntCaratLabel") );
+ m_pBuildablesButton = dynamic_cast<CExButton*>( FindChildByName("BuildablesButton") );
+ m_pCharacterLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("CharacterLoadoutButton") );
+ m_pTauntLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("TauntLoadoutButton") );
+ m_pPassiveAttribsLabel = dynamic_cast<CExLabel*>( FindChildByName("PassiveAttribsLabel") );
+ m_pLoadoutPresetPanel = dynamic_cast<CLoadoutPresetPanel*>( FindChildByName( "loadout_preset_panel" ) );
+ m_pPresetsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "PresetsExplanation" ) );
+ m_pTauntsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "TauntsExplanation" ) );
+ m_pTopLinePanel = FindChildByName( "TopLine" );
+ if ( m_pPassiveAttribsLabel )
+ {
+ m_pPassiveAttribsLabel->SetMouseInputEnabled( false );
+ }
+
+ m_pMouseOverTooltip->SetPositioningStrategy( IPTTP_BOTTOM_SIDE );
+
+ m_aDefaultColors[LOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorFg", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[LOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorFg", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[LOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorFg", Color( 255, 255, 255, 255 ) );
+
+ m_aDefaultColors[LOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorBg", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[LOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorBg", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[LOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorBg", Color( 255, 255, 255, 255 ) );
+
+ m_aDefaultColors[NOTLOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.TextColor", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[NOTLOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedTextColor", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[NOTLOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedTextColor", Color( 255, 255, 255, 255 ) );
+
+ m_aDefaultColors[NOTLOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.BgColor", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[NOTLOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedBgColor", Color( 255, 255, 255, 255 ) );
+ m_aDefaultColors[NOTLOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedBgColor", Color( 255, 255, 255, 255 ) );
+}
+
+
+void CClassLoadoutPanel::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ KeyValues *pItemKV = inResourceData->FindKey( "itemoptionpanels_kv" );
+ if ( pItemKV )
+ {
+ if ( m_pItemOptionPanelKVs )
+ {
+ m_pItemOptionPanelKVs->deleteThis();
+ }
+ m_pItemOptionPanelKVs = new KeyValues("itemoptionpanels_kv");
+ pItemKV->CopySubkeys( m_pItemOptionPanelKVs );
+ }
+
+#ifdef STAGING_ONLY
+ // PDA Panels
+ if ( m_pItemModelPanels.Count() > LOADOUT_POSITION_PDA_ADDON2 )
+ {
+ for ( int i = LOADOUT_POSITION_PDA_ADDON1; i <= LOADOUT_POSITION_PDA_ADDON2; i++ )
+ {
+ int wide = m_pItemModelPanels[i]->GetWide();
+ int tall = m_pItemModelPanels[i]->GetTall();
+
+ m_pItemModelPanels[i]->SetSize( wide / 2, tall / 2 );
+ m_pItemModelPanels[i]->InvalidateLayout();
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ // This is disabled by default in res file. IF we turn it on again, uncomment this.
+ /*if ( m_pPassiveAttribsLabel )
+ {
+ m_pPassiveAttribsLabel->SetVisible( !m_bInTauntLoadoutMode );
+ }*/
+
+ if ( m_pTauntHintLabel )
+ {
+ m_pTauntHintLabel->SetVisible( m_bInTauntLoadoutMode );
+
+ const char *key = engine->Key_LookupBinding( "taunt" );
+ if ( !key )
+ {
+ key = "< not bound >";
+ }
+ SetDialogVariable( "taunt", key );
+ }
+
+ if ( m_pTauntLabel )
+ {
+ m_pTauntLabel->SetVisible( m_bInTauntLoadoutMode );
+ }
+ if ( m_pTauntCaratLabel )
+ {
+ m_pTauntCaratLabel->SetVisible( m_bInTauntLoadoutMode );
+ }
+ if ( m_pCharacterLoadoutButton )
+ {
+ UpdatePageButtonColor( m_pCharacterLoadoutButton, !m_bInTauntLoadoutMode );
+ }
+ if ( m_pTauntLoadoutButton )
+ {
+ UpdatePageButtonColor( m_pTauntLoadoutButton, m_bInTauntLoadoutMode );
+ }
+
+ FOR_EACH_VEC( m_vecItemOptionButtons, i )
+ {
+ m_vecItemOptionButtons[i]->SetVisible( false );
+ }
+
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ // Viewing a class loadout. Layout the buttons & the class image.
+ if ( i >= NUM_ITEM_PANELS_IN_LOADOUT )
+ {
+ m_pItemModelPanels[i]->SetVisible( false );
+ continue;
+ }
+
+ int iButtonPos = 0;
+ if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
+ {
+ iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[i];
+ }
+
+ bool bIsVisible = false;
+ if ( iButtonPos > 0 )
+ {
+ bIsVisible = m_bInTauntLoadoutMode ? IsTauntPanelPosition( iButtonPos ) : !IsTauntPanelPosition( iButtonPos );
+ }
+ m_pItemModelPanels[i]->SetVisible( bIsVisible );
+
+ if ( bIsVisible )
+ {
+ if ( m_bInTauntLoadoutMode )
+ {
+ iButtonPos -= g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[LOADOUT_POSITION_TAUNT];
+ }
+ else
+ {
+ iButtonPos--;
+ }
+
+#ifdef STAGING_ONLY
+ // Override for the PDA AddOnSlots
+ if ( i == LOADOUT_POSITION_PDA_ADDON1 )
+ {
+ int iYPos, iXPos;
+ m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetPos( iXPos, iYPos );
+ int iWide, iTall;
+ m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetSize( iWide, iTall );
+
+ m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos );
+ m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
+ continue;
+ }
+ else if ( i == LOADOUT_POSITION_PDA_ADDON2 )
+ {
+ int iYPos, iXPos;
+ m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetPos( iXPos, iYPos );
+ int iWide, iTall;
+ m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetSize( iWide, iTall );
+
+ m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos + iTall - (iTall / 2.1 ) );
+ m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
+ continue;
+ }
+#endif
+
+ m_pItemModelPanels[i]->SetNoItemText( ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[i] );
+
+ int iCenter = GetWide() * 0.5;
+ int iColumnHeight = 4;
+ int iColumn = iButtonPos / iColumnHeight;
+ int iYButtonPos = iButtonPos % iColumnHeight;
+
+ int iOffset = iColumn == 0 ? m_iItemXPosOffcenterA : m_iItemXPosOffcenterB + ((iColumn - 1) * 200);
+ int iXPos = iCenter + iOffset;
+ int iYPos = m_iItemYPos + (m_iItemYDelta * iYButtonPos);
+ m_pItemModelPanels[i]->SetPos( iXPos, iYPos );
+
+ // Update position and visibility of the item option buttons
+ if ( i < m_vecItemOptionButtons.Count() )
+ {
+ // Place the button just inside the item model panel
+ CExButton* pItemOptionsPanel = m_vecItemOptionButtons[iButtonPos];
+ int iButtonWide = m_pItemModelPanels[i]->GetWide();
+ int iMyWide = pItemOptionsPanel->GetWide();
+ int iOptionsXPos = iColumn == 0
+ ? iXPos + iButtonWide - iMyWide
+ : iXPos;
+ pItemOptionsPanel->SetPos( iOptionsXPos, iYPos );
+
+ CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
+ // Enable or disable the item options button for this item model panel
+ pItemOptionsPanel->SetVisible( !m_bInTauntLoadoutMode && AnyOptionsAvailableForItem( pItemData ) );
+ pItemOptionsPanel->SetCommand( CFmtStr( "options%d", i ) );
+ }
+ }
+
+ m_pItemModelPanels[ i ]->SetSelected( false );
+ }
+
+ if ( m_pLoadoutPresetPanel )
+ {
+ m_pLoadoutPresetPanel->SetPos( ( ScreenWidth() - m_pLoadoutPresetPanel->GetWide() ) / 2, m_iItemYPos );
+ }
+
+ LinkModelPanelControllerNavigation( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnKeyCodePressed( vgui::KeyCode code )
+{
+ // See if the preset control uses this key
+ if( m_pLoadoutPresetPanel->HandlePresetKeyPressed( code ) )
+ {
+ return;
+ }
+
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ if (nButtonCode == KEY_XBUTTON_LEFT ||
+ nButtonCode == KEY_XSTICK1_LEFT ||
+ nButtonCode == KEY_XSTICK2_LEFT ||
+ nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
+ code == KEY_LEFT ||
+ nButtonCode == KEY_XBUTTON_RIGHT ||
+ nButtonCode == KEY_XSTICK1_RIGHT ||
+ nButtonCode == KEY_XSTICK2_RIGHT ||
+ nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
+ code == KEY_RIGHT ||
+ nButtonCode == KEY_XBUTTON_UP ||
+ nButtonCode == KEY_XSTICK1_UP ||
+ nButtonCode == KEY_XSTICK2_UP ||
+ nButtonCode == STEAMCONTROLLER_DPAD_UP ||
+ code == KEY_UP ||
+ nButtonCode == KEY_XBUTTON_DOWN ||
+ nButtonCode == KEY_XSTICK1_DOWN ||
+ nButtonCode == KEY_XSTICK2_DOWN ||
+ nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
+ code == KEY_DOWN )
+ {
+ // just eat all navigation keys so we don't
+ // end up with undesirable navigation behavior bubbling from
+ // one item model panel to another
+ }
+ else if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A )
+ {
+ // show the current loadout slot
+ int nSelected = GetFirstSelectedItemIndex( true );
+ if( nSelected != -1 )
+ {
+ OnCommand( VarArgs("change%d", nSelected ) );
+ }
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnNavigateTo( const char* panelName )
+{
+ CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
+ if( !pChild )
+ return;
+
+ pChild->SetSelected( true );
+ SetBorderForItem( pChild, false );
+ pChild->RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnNavigateFrom( const char* panelName )
+{
+ CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
+ if( !pChild )
+ return;
+
+ pChild->SetSelected( false );
+ SetBorderForItem( pChild, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory )
+{
+ if ( bVisible )
+ {
+ // always start in character loadout page
+ SetLoadoutPage( CHARACTER_LOADOUT_PAGE );
+
+ if ( m_pSelectionPanel )
+ {
+ m_pSelectionPanel->SetVisible( false );
+ m_pSelectionPanel->MarkForDeletion();
+ m_pSelectionPanel = NULL;
+ }
+
+ m_iCurrentSlotIndex = TF_WPN_TYPE_PRIMARY;
+ if( m_pItemModelPanels.Count() && m_pItemModelPanels[0] )
+ {
+ m_pItemModelPanels[0]->SetSelected( true );
+ SetBorderForItem( m_pItemModelPanels[0], false );
+ }
+
+ m_bLoadoutHasChanged = false;
+
+ if ( tf_show_preset_explanation_in_class_loadout.GetBool() && m_pPresetsExplanationPopup )
+ {
+ m_pPresetsExplanationPopup->Popup();
+ tf_show_preset_explanation_in_class_loadout.SetValue( 0 );
+ }
+ else if ( tf_show_taunt_explanation_in_class_loadout.GetBool() && m_pTauntsExplanationPopup )
+ {
+ m_pTauntsExplanationPopup->Popup();
+ tf_show_taunt_explanation_in_class_loadout.SetValue( 0 );
+ }
+
+ ClearItemOptionsMenu();
+ }
+ else
+ {
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->ClearCarriedItems();
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::PostShowPanel( bool bVisible )
+{
+ if ( bVisible )
+ {
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->SetVisible( true );
+ }
+
+ if ( m_pBuildablesButton )
+ {
+ m_pBuildablesButton->SetVisible( m_iCurrentClassIndex == TF_CLASS_ENGINEER );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::SetClass( int iClass )
+{
+ m_iCurrentClassIndex = iClass;
+
+ if ( m_pLoadoutPresetPanel )
+ {
+ m_pLoadoutPresetPanel->SetClass( m_iCurrentClassIndex );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::SetTeam( int iTeam )
+{
+ Assert( IsValidTFTeam( iTeam ) );
+ m_iCurrentTeamIndex = iTeam;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CClassLoadoutPanel::GetNumRelevantSlots() const
+{
+ return m_pItemModelPanels.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView *CClassLoadoutPanel::GetItemInSlot( int iSlot )
+{
+ if( iSlot >= 0 && iSlot < m_pItemModelPanels.Count() )
+ {
+ return m_pItemModelPanels[iSlot]->GetItem();
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::FireGameEvent( IGameEvent *event )
+{
+ // If we're not visible, ignore all events
+ if ( !IsVisible() )
+ return;
+
+ BaseClass::FireGameEvent( event );
+
+ // We need to update ourselves after the base has done it, so our item models have been updated
+ const char *type = event->GetName();
+ if ( Q_strcmp( "inventory_updated", type ) == 0 )
+ {
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
+ }
+ }
+}
+
+void CClassLoadoutPanel::AddNewItemPanel( int iPanelIndex )
+{
+ BaseClass::AddNewItemPanel( iPanelIndex );
+
+ m_vecItemOptionButtons[ m_vecItemOptionButtons.AddToTail() ] = new CExButton( this,
+ CFmtStr( "item_options_button%d", iPanelIndex ),
+ "+",
+ this,
+ CFmtStr( "options%d", iPanelIndex ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::UpdateModelPanels( void )
+{
+ // Search for a Robot Costume
+ bool bIsRobot = false;
+ static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" );
+ // For now, fill them out with the local player's currently wielded items
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
+ if ( !pItemData )
+ continue;
+ if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) )
+ {
+ bIsRobot = true;
+ break;
+ }
+ }
+
+ // We're showing the loadout for a specific class.
+ TFPlayerClassData_t *pData = GetPlayerClassData( m_iCurrentClassIndex );
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->ClearCarriedItems();
+ m_pPlayerModelPanel->SetToPlayerClass( m_iCurrentClassIndex, bIsRobot );
+ m_pPlayerModelPanel->SetTeam( m_iCurrentTeamIndex );
+ }
+
+ // For now, fill them out with the local player's currently wielded items
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
+ m_pItemModelPanels[i]->SetItem( pItemData );
+ m_pItemModelPanels[i]->SetShowQuantity( true );
+ m_pItemModelPanels[i]->SetSelected( false );
+ SetBorderForItem( m_pItemModelPanels[i], false );
+
+ if ( m_pPlayerModelPanel && pItemData && pItemData->IsValid() )
+ {
+ m_pPlayerModelPanel->AddCarriedItem( pItemData );
+ }
+ }
+
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
+ }
+
+ SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) );
+
+ UpdatePassiveAttributes();
+
+ // Now layout again to position our item buttons
+ InvalidateLayout();
+
+ if ( m_pItemOptionPanel->IsVisible() )
+ {
+ m_pItemOptionPanel->UpdateItemOptionsUI();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnItemPanelMouseReleased( vgui::Panel *panel )
+{
+ CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
+
+ if ( pItemPanel && IsVisible() )
+ {
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ if ( m_pItemModelPanels[i] == pItemPanel )
+ {
+ OnCommand( VarArgs("change%d", i) );
+ return;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnSelectionReturned( KeyValues *data )
+{
+ if ( data )
+ {
+ uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID );
+
+ // ulIndex implies do nothing (escape key)
+ if ( ulIndex != 0 )
+ {
+ TFInventoryManager()->EquipItemInLoadout( m_iCurrentClassIndex, m_iCurrentSlotIndex, ulIndex );
+
+ m_bLoadoutHasChanged = true;
+
+ UpdateModelPanels();
+
+ // Send the preset panel a msg so it can save the change
+ KeyValues *pLoadoutChangedMsg = new KeyValues( "LoadoutChanged" );
+ pLoadoutChangedMsg->SetInt( "slot", m_iCurrentSlotIndex );
+ pLoadoutChangedMsg->SetUint64( "itemid", ulIndex );
+ PostMessage( m_pLoadoutPresetPanel, pLoadoutChangedMsg );
+ }
+ }
+
+ PostMessage( GetParent(), new KeyValues("SelectionEnded") );
+
+ // It'll have deleted itself, so we don't need to clean it up
+ m_pSelectionPanel = NULL;
+ OnCancelSelection();
+
+ // find the selected item and give it the focus
+ CItemModelPanel *pSelection = GetFirstSelectedItemModelPanel( true );
+ if( !pSelection )
+ {
+ m_pItemModelPanels[0]->SetSelected( true );
+ pSelection = m_pItemModelPanels[0];
+ }
+
+ pSelection->RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnCancelSelection( void )
+{
+ if ( m_pSelectionPanel )
+ {
+ m_pSelectionPanel->SetVisible( false );
+ m_pSelectionPanel->MarkForDeletion();
+ m_pSelectionPanel = NULL;
+ }
+
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->SetVisible( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::RespawnPlayer()
+{
+ if ( tf_respawn_on_loadoutchanges.GetBool() )
+ {
+ // Tell the GC to tell server that we should respawn if we're in a respawn room
+ GCSDK::CGCMsg< MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
+ GCClientSystem()->BSendMessage( msg );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply KVs to the item option buttons
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::ApplyKVsToItemPanels( void )
+{
+ BaseClass::ApplyKVsToItemPanels();
+
+ if ( m_pItemOptionPanelKVs )
+ {
+ for ( int i = 0; i < m_vecItemOptionButtons.Count(); i++ )
+ {
+ m_vecItemOptionButtons[i]->ApplySettings( m_pItemOptionPanelKVs );
+ m_vecItemOptionButtons[i]->InvalidateLayout();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnClosing( void )
+{
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->ClearCarriedItems();
+ }
+
+ if ( m_bLoadoutHasChanged )
+ {
+ RespawnPlayer();
+
+ m_bLoadoutHasChanged = false;
+ }
+}
+
+extern const char *g_szItemBorders[AE_MAX_TYPES][5];
+extern ConVar cl_showbackpackrarities;
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver )
+{
+ if ( !pItemPanel )
+ return;
+
+ const char *pszBorder = NULL;
+
+ if ( pItemPanel->IsGreyedOut() )
+ {
+ pszBorder = "EconItemBorder";
+ }
+ else
+ {
+ int iRarity = 0;
+ if ( pItemPanel->HasItem() && cl_showbackpackrarities.GetBool() )
+ {
+ iRarity = pItemPanel->GetItem()->GetItemQuality();
+
+ uint8 nRarity = pItemPanel->GetItem()->GetItemDefinition()->GetRarity();
+ if ( ( nRarity != k_unItemRarity_Any ) && ( iRarity != AE_SELFMADE ) )
+ {
+ // translate this quality to rarity
+ iRarity = nRarity + AE_RARITY_DEFAULT;
+ }
+
+ if ( iRarity > 0 )
+ {
+ if ( bMouseOver || pItemPanel->IsSelected() )
+ {
+ pszBorder = g_szItemBorders[iRarity][1];
+ }
+ else
+ {
+ pszBorder = g_szItemBorders[iRarity][0];
+ }
+ }
+ }
+
+
+ if ( iRarity == 0 )
+ {
+ if ( bMouseOver || pItemPanel->IsSelected() )
+ {
+ pszBorder = "LoadoutItemMouseOverBorder";
+ }
+ else
+ {
+ pszBorder = "EconItemBorder";
+ }
+ }
+ }
+
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clear the item options menu and reset the button that summoned it
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::ClearItemOptionsMenu( void )
+{
+ SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );
+ m_pItemOptionPanel->SetItemSlot( LOADOUT_POSITION_INVALID, m_iCurrentClassIndex );
+ m_pItemOptionPanel->SetVisible( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Safely set the text for a button
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::SetOptionsButtonText( int nIndex, const char* pszText )
+{
+ if ( nIndex >= 0 && nIndex < m_vecItemOptionButtons.Count() )
+ {
+ m_vecItemOptionButtons[ m_pItemOptionPanel->GetItemSlot() ]->SetText( pszText );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return if the passed in item has any options
+//-----------------------------------------------------------------------------
+bool CClassLoadoutPanel::AnyOptionsAvailableForItem( const CEconItemView *pItem )
+{
+ if ( !pItem )
+ return false;
+
+ // Styles!
+ if ( pItem->GetStaticData()->GetNumSelectableStyles() > 1 )
+ return true;
+
+ // Unusual particle effect! For Cosmetics only
+ static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
+ if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect ) && pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) >= LOADOUT_POSITION_HEAD )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::SetLoadoutPage( classloadoutpage_t loadoutPage )
+{
+ ClearItemOptionsMenu();
+ switch ( loadoutPage )
+ {
+ case CHARACTER_LOADOUT_PAGE:
+ {
+ m_bInTauntLoadoutMode = false;
+ }
+ break;
+ case TAUNT_LOADOUT_PAGE:
+ {
+ m_bInTauntLoadoutMode = true;
+ }
+ break;
+ default:
+ {
+ // Unhandled loadout page
+ Assert( 0 );
+ }
+ }
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnCommand( const char *command )
+{
+ if ( FStrEq( command, "characterloadout" ) )
+ {
+ SetLoadoutPage( CHARACTER_LOADOUT_PAGE );
+ return;
+ }
+ else if ( FStrEq( command, "tauntloadout" ) )
+ {
+ SetLoadoutPage( TAUNT_LOADOUT_PAGE );
+ return;
+ }
+ else if ( !V_strnicmp( command, "change", 6 ) )
+ {
+ const char *pszNum = command+6;
+ if ( pszNum && pszNum[0] )
+ {
+ int iSlot = atoi(pszNum);
+ if ( iSlot >= 0 && iSlot < CLASS_LOADOUT_POSITION_COUNT && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
+ {
+ if ( m_iCurrentSlotIndex != iSlot )
+ {
+ m_iCurrentSlotIndex = iSlot;
+ }
+
+ // Create the selection screen. It removes itself on close.
+ m_pSelectionPanel = new CEquipSlotItemSelectionPanel( this, m_iCurrentClassIndex, iSlot );
+ m_pSelectionPanel->ShowPanel( 0, true );
+
+ if ( m_pPlayerModelPanel )
+ {
+ m_pPlayerModelPanel->SetVisible( false );
+ }
+
+ ClearItemOptionsMenu();
+
+ PostMessage( GetParent(), new KeyValues("SelectionStarted") );
+ }
+ }
+
+ return;
+ }
+ else if ( !V_strnicmp( command, "options", 7 ) )
+ {
+ const char *pszNum = command + 7;
+ if( pszNum && pszNum[0] )
+ {
+ int iSlot = atoi( pszNum );
+ //iSlot = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[iSlot - 1];
+ if ( iSlot >= 0 && iSlot < m_vecItemOptionButtons.Count() && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
+ {
+ // Change the button we're coming from to be a "+"
+ SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );
+
+ // Update the current slot index for callback from the setstyle button.
+ // It will send us a message to change the item the player model is holding
+ // and we need this to be updated for that.
+ m_iCurrentSlotIndex = iSlot;
+
+
+ // Did they just toggle?
+ if ( m_pItemOptionPanel->GetItemSlot() == iSlot )
+ {
+ m_pItemOptionPanel->SetVisible( !m_pItemOptionPanel->IsVisible() );
+ }
+ else
+ {
+ // Set the options panel to have the data for this slot
+ m_pItemOptionPanel->SetItemSlot( (loadout_positions_t)iSlot, m_iCurrentClassIndex );
+ m_pItemOptionPanel->SetVisible( true );
+ // Figure out if this is on the left or right
+ int iColumnHeight = 4;
+ int iColumn = iSlot / iColumnHeight;
+ PinCorner_e myCornerToPin = iColumn == 0 ? PIN_TOPLEFT : PIN_TOPRIGHT;
+ PinCorner_e siblingCornerPinTo = iColumn == 0 ? PIN_TOPRIGHT : PIN_TOPLEFT;
+ // Pin to the appropriate side
+ int iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[ iSlot ] - 1;
+ m_pItemOptionPanel->PinToSibling( m_vecItemOptionButtons[ iButtonPos ]->GetName(), myCornerToPin, siblingCornerPinTo );
+ m_pItemOptionPanel->UpdateItemOptionsUI();
+ }
+
+ // Change the button we're going to to be "-" if we're visible, "+" if we're not
+ SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), m_pItemOptionPanel->IsVisible() ? "-" : "+" );
+ }
+ return;
+ }
+ }
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
+{
+ BaseClass::OnMessage( pParams, hFromPanel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct passive_attrib_to_print_t
+{
+ const CEconItemAttributeDefinition *m_pAttrDef;
+ attrib_value_t m_value;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAttributeIterator_AddPassiveAttribsToPassiveList : public CEconItemSpecificAttributeIterator
+{
+public:
+ CAttributeIterator_AddPassiveAttribsToPassiveList( CUtlVector<passive_attrib_to_print_t> *pList, bool bForceAdd )
+ : m_pList( pList )
+ , m_bForceAdd( bForceAdd )
+ {
+ Assert( m_pList );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+ {
+ Assert( pAttrDef );
+
+ if ( pAttrDef->IsHidden() )
+ return true;
+
+ if ( !m_bForceAdd )
+ {
+ const char *pDesc = pAttrDef->GetArmoryDescString();
+ if ( !pDesc || !pDesc[0] )
+ return true;
+
+ // If we have the "on_wearer" key, we're a passive attribute
+ if ( !Q_stristr(pDesc, "on_wearer") )
+ return true;
+ }
+
+ // Now see if we're already in the list
+ FOR_EACH_VEC( (*m_pList), i )
+ {
+ passive_attrib_to_print_t& passiveAttr = (*m_pList)[i];
+
+ Assert( passiveAttr.m_pAttrDef );
+
+ // We match if our class is the same -- this is a case-sensitive compare!
+ if ( Q_strcmp( passiveAttr.m_pAttrDef->GetAttributeClass(), pAttrDef->GetAttributeClass() ) )
+ continue;
+
+ // We've found a matching attribute. Collate our values and stomp over the earlier value.
+ passiveAttr.m_value = CollateAttributeValues( passiveAttr.m_pAttrDef, passiveAttr.m_value, pAttrDef, value );
+
+ return true;
+ }
+
+ // We didn't find it. Add it to the list.
+ passive_attrib_to_print_t newPassiveAttr = { pAttrDef, value };
+ m_pList->AddToTail( newPassiveAttr );
+
+ return true;
+ }
+
+ // Other types are ignored.
+
+private:
+ CUtlVector<passive_attrib_to_print_t> *m_pList;
+ bool m_bForceAdd;
+};
+
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::UpdatePassiveAttributes( void )
+{
+ if ( !m_pPassiveAttribsLabel )
+ return;
+
+ // We build a list of attributes & associated values by looping through all equipped items.
+ // This way we can identify & collate attributes based on the same definition index.
+ CUtlVector<passive_attrib_to_print_t> vecAttribsToPrint;
+
+ // Loop through all equipped items
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
+ if ( pItemData && pItemData->IsValid() )
+ {
+ CAttributeIterator_AddPassiveAttribsToPassiveList attrItPassives( &vecAttribsToPrint, false );
+ pItemData->IterateAttributes( &attrItPassives );
+ }
+ }
+
+ // Then add any set bonuses
+ if ( steamapicontext && steamapicontext->SteamUser() )
+ {
+ CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
+ CUtlVector<const CEconItemSetDefinition *> pActiveSets;
+ TFInventoryManager()->GetActiveSets( &pActiveSets, localSteamID, m_iCurrentClassIndex );
+
+ FOR_EACH_VEC( pActiveSets, set )
+ {
+ CAttributeIterator_AddPassiveAttribsToPassiveList attrItSetPassives( &vecAttribsToPrint, true );
+ pActiveSets[set]->IterateAttributes( &attrItSetPassives );
+ }
+ }
+
+ // Now build the text
+ wchar_t wszPassiveDesc[4096];
+ wszPassiveDesc[0] = '\0';
+ m_pPassiveAttribsLabel->GetTextImage()->ClearColorChangeStream();
+
+ wchar_t *pHeader = g_pVGuiLocalize->Find( "#TF_PassiveAttribs" );
+ if ( pHeader )
+ {
+ V_wcscpy_safe( wszPassiveDesc, pHeader );
+ V_wcscat_safe( wszPassiveDesc, L"\n" );
+ }
+
+ if ( vecAttribsToPrint.Count() )
+ {
+ FOR_EACH_VEC( vecAttribsToPrint, i )
+ {
+ CEconAttributeDescription AttrDesc( GLocalizationProvider(), vecAttribsToPrint[i].m_pAttrDef, vecAttribsToPrint[i].m_value );
+ AddAttribPassiveText( AttrDesc, wszPassiveDesc, ARRAYSIZE(wszPassiveDesc) );
+ }
+ }
+ else
+ {
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ Color col = pScheme->GetColor( GetColorNameForAttribColor( ATTRIB_COL_NEUTRAL ), Color(255,255,255,255) );
+ m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( wszPassiveDesc ) );
+
+ wchar_t *pNone = g_pVGuiLocalize->Find( "#TF_PassiveAttribs_None" );
+ if ( pNone )
+ {
+ V_wcscat_safe( wszPassiveDesc, pNone );
+ }
+ }
+
+ m_pPassiveAttribsLabel->SetText( wszPassiveDesc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::AddAttribPassiveText( const CEconAttributeDescription& AttrDesc, INOUT_Z_CAP(iNumPassiveChars) wchar_t *out_wszPassiveDesc, int iNumPassiveChars )
+{
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ Assert( pScheme );
+
+ if ( !AttrDesc.GetDescription().IsEmpty() )
+ {
+ // Insert the color change at the current position
+ Color col = pScheme->GetColor( GetColorNameForAttribColor( AttrDesc.GetDefaultColor() ), Color(255,255,255,255) );
+ m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( out_wszPassiveDesc ) );
+
+ // Now append the text of the attribute
+ V_wcsncat( out_wszPassiveDesc, AttrDesc.GetDescription().Get(), iNumPassiveChars );
+ V_wcsncat( out_wszPassiveDesc, L"\n", iNumPassiveChars );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClassLoadoutPanel::UpdatePageButtonColor( CExImageButton *pPageButton, bool bIsActive )
+{
+ if ( pPageButton )
+ {
+ int iLoaded = bIsActive ? LOADED : NOTLOADED;
+ pPageButton->SetDefaultColor( m_aDefaultColors[iLoaded][FG][DEFAULT], m_aDefaultColors[iLoaded][BG][DEFAULT] );
+ pPageButton->SetArmedColor( m_aDefaultColors[iLoaded][FG][ARMED], m_aDefaultColors[iLoaded][BG][ARMED] );
+ pPageButton->SetDepressedColor( m_aDefaultColors[iLoaded][FG][DEPRESSED], m_aDefaultColors[iLoaded][BG][DEPRESSED] );
+ }
+}