summaryrefslogtreecommitdiff
path: root/game/client/econ/item_selection_panel.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/econ/item_selection_panel.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/econ/item_selection_panel.cpp')
-rw-r--r--game/client/econ/item_selection_panel.cpp1493
1 files changed, 1493 insertions, 0 deletions
diff --git a/game/client/econ/item_selection_panel.cpp b/game/client/econ/item_selection_panel.cpp
new file mode 100644
index 0000000..62a8bf0
--- /dev/null
+++ b/game/client/econ/item_selection_panel.cpp
@@ -0,0 +1,1493 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+#include "item_selection_panel.h"
+#include "vgui/ISurface.h"
+#include "c_tf_player.h"
+#include "gamestringpool.h"
+#include "iclientmode.h"
+#include "tf_item_inventory.h"
+#include "ienginevgui.h"
+#include <vgui/ILocalize.h>
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui/IInput.h"
+#include "item_model_panel.h"
+#include "econ_item_constants.h"
+#include "econ_item_system.h"
+#include "econ_item_description.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+ConVar tf_item_selection_panel_sort_type( "tf_item_selection_panel_sort_type", 0, FCVAR_NONE, "0 - Sort is off, 1 - Sort is Alphabet (Pub)" );
+
+const char *g_szEquipSlotHeader[] =
+{
+ "#ItemSel_PRIMARY", // LOADOUT_POSITION_PRIMARY = 0,
+ "#ItemSel_SECONDARY", // LOADOUT_POSITION_SECONDARY,
+ "#ItemSel_MELEE", // LOADOUT_POSITION_MELEE,
+ "#ItemSel_UTILITY", // LOADOUT_POSITION_UTILITY // Staging
+ "#ItemSel_PDA", // LOADOUT_POSITION_BUILDING,
+ "#ItemSel_PDA", // LOADOUT_POSITION_PDA,
+ "#ItemSel_PDA", // LOADOUT_POSITION_PDA2
+ "#ItemSel_MISC", // LOADOUT_POSITION_HEAD
+ "#ItemSel_MISC", // LOADOUT_POSITION_MISC
+ "#ItemSel_ACTION", // LOADOUT_POSITION_ACTION
+ "#ItemSel_MISC", // LOADOUT_POSITION_MISC2
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT2
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT3
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT4
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT5
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT6
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT7
+ "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT8
+#ifdef STAGING_ONLY
+ "#ItemSel_PDA_ADDON1", // LOADOUT_POSITION_PDA_ADDON1
+ "#ItemSel_PDA_ADDON2", // LOADOUT_POSITION_PDA_ADDON2
+ "", // LOADOUT_POSITION_PDA3,
+ "", // LOADOUT_POSITION_BUILDING2,
+#endif // STAGING_ONLY
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_szEquipSlotHeader ) == CLASS_LOADOUT_POSITION_COUNT );
+
+static bool ShouldItemNotStack( CEconItemView *pItemData )
+{
+ CEconItem *pSOCData = pItemData->GetSOCData();
+ if ( pSOCData && pSOCData->BHasDynamicAttributes() )
+ {
+ return true;
+ }
+
+ return false;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CItemSelectionPanel::CItemSelectionPanel(Panel *parent) : CBaseLoadoutPanel(parent, "ItemSelectionPanel")
+{
+ m_pCaller = parent;
+ m_pSelectionItemModelPanelKVs = NULL;
+ m_pDuplicateLabelKVs = NULL;
+ m_bShowingEntireBackpack = false;
+ m_iItemsInSelection = 0;
+
+ m_bShowDuplicates = false;
+ m_bForceBackpack = false;
+ m_pOnlyAllowUniqueQuality = NULL;
+ m_pShowBackpack = NULL;
+ m_pShowSelection = NULL;
+ m_pNextPageButton = NULL;
+ m_pPrevPageButton = NULL;
+ m_pCurPageLabel = NULL;
+ m_pNoItemsInSelectionLabel = NULL;
+ m_bGotMousePressed = false;
+ m_pNameFilterTextEntry = NULL;
+
+ m_DuplicateCounts.SetLessFunc( DefLessFunc( DuplicateCountsMap_t::KeyType_t ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CItemSelectionPanel::~CItemSelectionPanel()
+{
+ if ( m_pSelectionItemModelPanelKVs )
+ {
+ m_pSelectionItemModelPanelKVs->deleteThis();
+ m_pSelectionItemModelPanelKVs = NULL;
+ }
+ if ( m_pDuplicateLabelKVs )
+ {
+ m_pDuplicateLabelKVs->deleteThis();
+ m_pDuplicateLabelKVs = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ m_pNameFilterTextEntry = NULL;
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( GetSchemeFile() );
+
+ m_pNoItemsInSelectionLabel = dynamic_cast<vgui::Label*>( FindChildByName("NoItemsLabel") );
+ m_pOnlyAllowUniqueQuality = dynamic_cast<vgui::CheckButton *>( FindChildByName("OnlyAllowUniqueQuality") );
+ m_pShowBackpack = dynamic_cast<CExButton*>( FindChildByName("ShowBackpack") );
+ m_pShowSelection = dynamic_cast<CExButton*>( FindChildByName("ShowSelection") );
+ m_pNextPageButton = dynamic_cast<CExButton*>( FindChildByName("NextPageButton") );
+ m_pPrevPageButton = dynamic_cast<CExButton*>( FindChildByName("PrevPageButton") );
+ m_pCurPageLabel = dynamic_cast<vgui::Label*>( FindChildByName("CurPageLabel") );
+
+ // Give individual selection panels the ability to specify whether or not they want to show the
+ // checkbox controlling quality filtering. It really only makes sense for things like crafting,
+ // not equipment selection.
+ if ( m_pOnlyAllowUniqueQuality )
+ {
+ m_pOnlyAllowUniqueQuality->SetVisible( DisplayOnlyAllowUniqueQualityCheckbox() );
+
+ // By default, if the checkbox is visible, it's enabled. Users can disable it manually if
+ // they want to craft potentially-more-valuable items.
+ if ( m_pOnlyAllowUniqueQuality->IsVisible() )
+ {
+ m_pOnlyAllowUniqueQuality->SetSelected( true );
+ }
+ }
+
+
+ m_pNameFilterTextEntry = FindControl<vgui::TextEntry>( "NameFilterTextEntry" );
+ if ( m_pNameFilterTextEntry )
+ {
+ m_pNameFilterTextEntry->AddActionSignalTarget( this );
+ }
+
+ UpdateModelPanels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ KeyValues *pItemKV = inResourceData->FindKey( "modelpanels_selection_kv" );
+ if ( pItemKV )
+ {
+ if ( m_pSelectionItemModelPanelKVs )
+ {
+ m_pSelectionItemModelPanelKVs->deleteThis();
+ }
+ m_pSelectionItemModelPanelKVs = new KeyValues("modelpanels_selection_kv");
+ pItemKV->CopySubkeys( m_pSelectionItemModelPanelKVs );
+ }
+
+ KeyValues *pLabelKV = inResourceData->FindKey( "duplicatelabels_kv" );
+ if ( pLabelKV )
+ {
+ if ( m_pDuplicateLabelKVs )
+ {
+ m_pDuplicateLabelKVs->deleteThis();
+ }
+ m_pDuplicateLabelKVs = new KeyValues("duplicatelabels_kv");
+ pLabelKV->CopySubkeys( m_pDuplicateLabelKVs );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::ApplyKVsToItemPanels( void )
+{
+ BaseClass::ApplyKVsToItemPanels();
+
+ if ( !m_bShowingEntireBackpack )
+ {
+ if ( m_pSelectionItemModelPanelKVs )
+ {
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ m_pItemModelPanels[i]->ApplySettings( m_pSelectionItemModelPanelKVs );
+ m_pItemModelPanels[i]->UpdatePanels();
+ }
+ }
+
+ if ( m_pDuplicateLabelKVs )
+ {
+ FOR_EACH_VEC( m_pDuplicateCountLabels, i )
+ {
+ if ( !m_pDuplicateCountLabels[i]->IsVisible() )
+ continue;
+
+ m_pDuplicateCountLabels[i]->ApplySettings( m_pDuplicateLabelKVs );
+ m_pDuplicateCountLabels[i]->SetMouseInputEnabled( false );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ // In backpack mode we show empty slots. Otherwise we don't.
+ bool bVisible = m_bShowingEntireBackpack;
+ if ( !bVisible )
+ {
+ bVisible = (i < GetNumSlotsPerPage()) && ShouldItemPanelBeVisible( m_pItemModelPanels[i], i );
+ }
+ m_pItemModelPanels[i]->SetVisible( bVisible );
+
+ if ( bVisible )
+ {
+ PositionItemPanel( m_pItemModelPanels[i], i );
+ }
+
+ UpdateDuplicateCounts();
+ }
+
+ FOR_EACH_VEC( m_pDuplicateCountLabels, i )
+ {
+ if ( m_pDuplicateCountLabels[i]->IsVisible() )
+ {
+ int iXPos, iYPos;
+ m_pItemModelPanels[i]->GetPos( iXPos, iYPos );
+ m_pDuplicateCountLabels[i]->SetPos( iXPos, iYPos );
+ }
+ }
+
+ m_pShowBackpack->SetVisible( !m_bShowingEntireBackpack && !m_bForceBackpack );
+ m_pShowSelection->SetVisible( m_bShowingEntireBackpack && !m_bForceBackpack );
+
+ m_pNextPageButton->SetVisible( true );
+ m_pPrevPageButton->SetVisible( true );
+ m_pCurPageLabel->SetVisible( true );
+ m_pNextPageButton->SetEnabled( GetNumPages() > 1 );
+ m_pPrevPageButton->SetEnabled( GetNumPages() > 1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnThink( void )
+{
+ BaseClass::OnThink();
+
+ if ( m_flFilterItemTime && gpGlobals->curtime >= m_flFilterItemTime )
+ {
+ SetCurrentPage( 0 );
+ //DeSelectAllBackpackItemPanels();
+ UpdateModelPanels();
+
+ m_flFilterItemTime = 0.0f;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnCommand( const char *command )
+{
+ if ( !Q_stricmp( command, "vguicancel" ) )
+ {
+ PostMessageSelectionReturned( INVALID_ITEM_ID );
+
+ OnClose();
+ return;
+ }
+ else if ( !Q_strnicmp( command, "nextpage", 8 ) )
+ {
+ HideMouseOverPanel();
+ SetCurrentPage( GetCurrentPage() + 1 );
+ UpdateModelPanels();
+ return;
+ }
+ else if ( !Q_strnicmp( command, "prevpage", 8 ) )
+ {
+ HideMouseOverPanel();
+ SetCurrentPage( GetCurrentPage() - 1 );
+ UpdateModelPanels();
+ return;
+ }
+ else if ( !Q_strnicmp( command, "show_backpack", 8 ) )
+ {
+ m_bReapplyItemKVs = true;
+ m_bShowingEntireBackpack = true;
+ UpdateModelPanels();
+ //Repaint();
+ return;
+ }
+ else if ( !Q_strnicmp( command, "show_selection", 8 ) )
+ {
+ m_bReapplyItemKVs = true;
+ m_bShowingEntireBackpack = false;
+ UpdateModelPanels();
+ //Repaint();
+ return;
+ }
+ else
+ {
+ engine->ClientCmd( const_cast<char *>( command ) );
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnButtonChecked( KeyValues *pData )
+{
+ Assert( reinterpret_cast<vgui::Panel *>( pData->GetPtr("panel") ) == m_pOnlyAllowUniqueQuality );
+ UpdateModelPanels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the escape key because it doesn't come through as "pressed"
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnKeyCodeTyped(vgui::KeyCode code)
+{
+ if ( code == KEY_ESCAPE )
+ {
+ // 0 implies do nothing, INVALID_ITEM_ID means stock and we dont want to equip stock
+ PostMessageSelectionReturned( 0 );
+ OnClose();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles keypresses in the item selection panel
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnKeyCodePressed( vgui::KeyCode code )
+{
+ // let our parent class handle all the arrow key/dpad stuff
+ if( HandleItemSelectionKeyPressed( code ) )
+ {
+ return;
+ }
+
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A )
+ {
+ CItemModelPanel *pItemPanel = GetFirstSelectedItemModelPanel( true );
+ if( pItemPanel && !pItemPanel->IsGreyedOut() )
+ {
+ NotifySelectionReturned( pItemPanel );
+ }
+ }
+ else if( nButtonCode == KEY_XBUTTON_B )
+ {
+ PostMessageSelectionReturned( INVALID_ITEM_ID );
+ OnClose();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles key release events in the backpack
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnKeyCodeReleased( vgui::KeyCode code )
+{
+ if( ! HandleItemSelectionKeyReleased( code ) )
+ BaseClass::OnKeyCodeReleased( code );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnClose( void )
+{
+ BaseClass::OnClose();
+
+ if ( ShouldDeleteOnClose() )
+ {
+ // Delete ourself now that we're done
+ MarkForDeletion();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::SetVisible( bool bState )
+{
+ BaseClass::SetVisible( bState );
+
+ if( bState )
+ {
+ m_wNameFilter.RemoveAll();
+ if( m_pNameFilterTextEntry )
+ {
+ m_pNameFilterTextEntry->SetText( "" );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnItemPanelMousePressed( vgui::Panel *panel )
+{
+ // This is annoying. Why do I get mouse released events for releases that started with a push before I was visible?
+ m_bGotMousePressed = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnItemPanelMouseReleased( vgui::Panel *panel )
+{
+ if ( !m_bGotMousePressed )
+ return;
+ m_bGotMousePressed = false;
+
+ CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
+
+ NotifySelectionReturned( pItemPanel );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Lets the parent know what the selection was
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::NotifySelectionReturned( CItemModelPanel *pItemPanel )
+{
+ if ( pItemPanel && IsVisible() )
+ {
+ CEconItemView *pItemData = pItemPanel->GetItem();
+ if ( GetItemNotSelectableReason( pItemData ) != NULL )
+ return;
+
+ if ( DisableItemSelectionFromGrayedOutPanels() && pItemPanel->IsGreyedOut() )
+ return;
+
+ itemid_t ulItemID = INVALID_ITEM_ID;
+ if ( pItemData && pItemData->IsValid() )
+ {
+ ulItemID = pItemData->GetItemID();
+ }
+
+ PostMessageSelectionReturned( ulItemID );
+ }
+
+ OnClose();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::UpdateModelPanels( void )
+{
+ // If we're showing the whole backpack, go through the inventory like the backpack does.
+ if ( m_bShowingEntireBackpack )
+ {
+ UpdateModelPanelsForSelection();
+
+ if ( m_pNoItemsInSelectionLabel )
+ {
+ m_pNoItemsInSelectionLabel->SetVisible( false );
+ }
+ }
+ else
+ {
+ // Clear the dupe counts.
+ m_DuplicateCounts.Purge();
+ UpdateModelPanelsForSelection();
+
+ if ( m_pNoItemsInSelectionLabel )
+ {
+ m_pNoItemsInSelectionLabel->SetVisible( m_iItemsInSelection == 0 );
+ }
+ }
+
+ // Update the current backpack page
+ char szTmp[16];
+ Q_snprintf(szTmp, 16, "%d/%d", GetCurrentPage()+1, GetNumPages() );
+ SetDialogVariable( "backpackpage", szTmp );
+
+ // And we're done!
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::UpdateDuplicateCounts( void )
+{
+ bool bShow = (m_bShowDuplicates && !m_bShowingEntireBackpack);
+ if ( !bShow )
+ {
+ FOR_EACH_VEC( m_pDuplicateCountLabels, i )
+ {
+ m_pDuplicateCountLabels[i]->SetVisible( false );
+ }
+ return;
+ }
+
+ FOR_EACH_VEC( m_pItemModelPanels, i )
+ {
+ if ( i >= GetNumSlotsPerPage() )
+ break;
+
+ if ( !m_pItemModelPanels[i]->IsVisible() )
+ {
+ m_pDuplicateCountLabels[i]->SetVisible( false );
+ continue;
+ }
+
+ if ( m_pDuplicateCountLabels.Count() <= i )
+ {
+ CExLabel *pLabel = new CExLabel( this, "", "x1" );
+ m_pDuplicateCountLabels.AddToTail( pLabel );
+
+ if ( m_pDuplicateLabelKVs )
+ {
+ pLabel->ApplySettings( m_pDuplicateLabelKVs );
+ }
+ pLabel->MakeReadyForUse();
+ pLabel->InvalidateLayout( true );
+ pLabel->SetMouseInputEnabled( false );
+ }
+
+ CEconItemView *pItem = m_pItemModelPanels[i]->GetItem();
+ if ( !pItem || !pItem->IsValid() || ShouldItemNotStack( pItem ) )
+ {
+ m_pDuplicateCountLabels[i]->SetVisible( false );
+ continue;
+ }
+
+ int iIndex = m_DuplicateCounts.Find( item_stack_type_t( pItem->GetItemDefIndex(), pItem->GetQuality() ) );
+ if ( iIndex == m_DuplicateCounts.InvalidIndex() || m_DuplicateCounts[iIndex] <= 1 )
+ {
+ m_pDuplicateCountLabels[i]->SetVisible( false );
+ continue;
+ }
+
+ wchar_t wzCost[10];
+ _snwprintf( wzCost, ARRAYSIZE( wzCost ), L"x%d", m_DuplicateCounts[iIndex] );
+ m_pDuplicateCountLabels[i]->SetText( wzCost );
+ m_pDuplicateCountLabels[i]->SetVisible( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::CreateItemPanels( void )
+{
+ // Always create the maximum number of panels
+ int iNumPanels = BACKPACK_SLOTS_PER_PAGE;
+ if ( m_pItemModelPanels.Count() < iNumPanels )
+ {
+ for ( int i = m_pItemModelPanels.Count(); i < iNumPanels; i++ )
+ {
+ AddNewItemPanel(i);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CItemSelectionPanel::GetNumPages( void )
+{
+ int iNumItems = 0;
+ if ( m_bShowingEntireBackpack )
+ {
+ iNumItems = InventoryManager()->GetLocalInventory()->GetMaxItemCount();
+ }
+ else
+ {
+ iNumItems = m_iItemsInSelection;
+ }
+
+ return (int)(ceil((float)iNumItems / (float)GetNumItemPanels()));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::SetCurrentPage( int nNewPage )
+{
+ if ( nNewPage < 0 )
+ {
+ nNewPage = GetNumPages() - 1;
+ }
+ else if ( nNewPage >= GetNumPages() )
+ {
+ nNewPage = 0;
+ }
+
+ BaseClass::SetCurrentPage( nNewPage );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex )
+{
+ int iCenter = GetWide() * 0.5;
+ int iButtonX = (iIndex % GetNumColumns());
+ int iButtonY = (iIndex / GetNumColumns());
+ int iXPos = (iCenter + m_iItemBackpackOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX);
+ int iYPos = m_iItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY);
+
+ m_pItemModelPanels[iIndex]->SetPos( iXPos, iYPos );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::PostMessageSelectionReturned( itemid_t ulItemID )
+{
+ KeyValues *pKey = new KeyValues( "SelectionReturned" );
+ pKey->SetUint64( "itemindex", ulItemID );
+ PostMessage( m_pCaller, pKey );
+}
+
+//=====================================================================================================================
+// EQUIP SLOT ITEM SELECTION PANEL
+//=====================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEquipSlotItemSelectionPanel::CEquipSlotItemSelectionPanel(Panel *parent, int iClass, int iSlot) : CItemSelectionPanel( parent )
+{
+ m_iClass = iClass;
+ m_iSlot = iSlot;
+
+ m_pWeaponLabel = NULL;
+
+ m_flFilterItemTime = 0.f;
+
+ m_iCurrentItemID = INVALID_ITEM_ID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEquipSlotItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pWeaponLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
+
+ TFPlayerClassData_t *pData = GetPlayerClassData( m_iClass );
+ SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) );
+
+ if ( m_pWeaponLabel )
+ {
+ m_pWeaponLabel->SetText( g_szEquipSlotHeader[m_iSlot] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEquipSlotItemSelectionPanel::PerformLayout( void )
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEquipSlotItemSelectionPanel::ShouldItemPanelBeVisible( CItemModelPanel *pPanel, int iPanelIndex )
+{
+ // If we don't have an item, but we're the first model panel on a slot
+ // that has no base item, we still want to be visible because we're the
+ // panel that allows players to select "Empty" for the slot.
+ return ( pPanel->HasItem() || (iPanelIndex == 0 && !TFInventoryManager()->SlotContainsBaseItems( GEconItemSchema().GetEquipTypeFromClassIndex( m_iClass ), m_iSlot )) );
+}
+
+//-----------------------------------------------------------------------------
+// Helper classes/functions for CItemSelectionPanel::UpdateModelPanels().
+//-----------------------------------------------------------------------------
+// Used to sort/verify uniqueness of user-facing items.
+struct RarityEconIdKey
+{
+ int m_iQualitySort;
+ item_definition_index_t m_defIndex;
+ uint32 m_unKillEaterScore;
+
+ RarityEconIdKey ( )
+ : m_iQualitySort( -1 )
+ , m_defIndex( INVALID_ITEM_DEF_INDEX )
+ , m_unKillEaterScore( 0 )
+ {
+ //
+ }
+
+ RarityEconIdKey ( int iQuality, item_definition_index_t defIndex, uint32 unKillEaterScore )
+ : m_iQualitySort( EconQuality_GetRarityScore( (EEconItemQuality)iQuality ) )
+ , m_defIndex( defIndex )
+ , m_unKillEaterScore( unKillEaterScore )
+ {
+ //
+ }
+
+ bool operator< ( const RarityEconIdKey& rhs ) const
+ {
+ return m_defIndex < rhs.m_defIndex
+ || m_iQualitySort < rhs.m_iQualitySort
+ || m_unKillEaterScore < rhs.m_unKillEaterScore;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static int SortRarityEconIdKeysBackpack ( CEconItemView *const *a, CEconItemView *const *b )
+{
+ Assert( a );
+ Assert( *a );
+ Assert( b );
+ Assert( *b );
+
+ // Sorting by the backpack order doesn't need to check any subproperties like level because
+ // every item should have a unique backpack slot already/
+ return ExtractBackpackPositionFromBackend( (*a)->GetInventoryPosition() ) < ExtractBackpackPositionFromBackend( (*b)->GetInventoryPosition() )
+ ? -1
+ : 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static int SortRarityEconIdKeysAlphabetical_Views ( CEconItemView *const *a, CEconItemView *const *b )
+{
+ Assert( a );
+ Assert( *a );
+ Assert( b );
+ Assert( *b );
+
+ // First pass -- sort backpack items by user-visible display name.
+ // Note: locale-savvy string sorting uses wcscoll, not wcscmp
+ int iStrCmpRes = wcscoll( (*a)->GetItemName(), (*b)->GetItemName() );
+ if ( iStrCmpRes != 0 )
+ return iStrCmpRes;
+
+ // Sort by kill eater score as a last-ditch ordering attempt.
+ static CSchemaAttributeDefHandle pAttrDef_KillEaterScore( "kill eater" );
+
+ uint32 unKillEaterScoreA = 0,
+ unKillEaterScoreB = 0;
+
+ (*a)->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScoreA );
+ (*b)->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScoreB );
+
+ // Our names match so sort by quality for similarly-named items.
+ if ( EconQuality_GetRarityScore( (EEconItemQuality)(*a)->GetItemQuality() ) < EconQuality_GetRarityScore( (EEconItemQuality)(*b)->GetItemQuality() ) ||
+ unKillEaterScoreA < unKillEaterScoreB ||
+ (*a)->GetItemLevel() < (*b)->GetItemLevel() )
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static int SortRarityEconIdKeysAlphabetical ( const CEquippableItemsForSlotGenerator::CEquippableResult *a, const CEquippableItemsForSlotGenerator::CEquippableResult *b )
+{
+ return SortRarityEconIdKeysAlphabetical_Views( &a->m_pEconItemView, &b->m_pEconItemView );
+}
+
+//-----------------------------------------------------------------------------
+static int SortRarityEconIdKeysDate( const CEquippableItemsForSlotGenerator::CEquippableResult *a, const CEquippableItemsForSlotGenerator::CEquippableResult *b )
+{
+ return ( a->m_pEconItemView->GetID() > b->m_pEconItemView->GetID() ) ? -1 : ( a->m_pEconItemView->GetID() < b->m_pEconItemView->GetID() ) ? 1 : 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: figure out what items should be displayed to the user for a specific
+// loadout and what order they should appear in.
+//-----------------------------------------------------------------------------
+CEquippableItemsForSlotGenerator::CEquippableItemsForSlotGenerator( int iClass, int iSlot, equip_region_mask_t unUsedEquipRegionMask, unsigned int unFlags )
+ : m_pEquippedItemView( NULL )
+{
+ m_DuplicateCountsMap.SetLessFunc( DefLessFunc( DuplicateCountMap_t::KeyType_t ) );
+
+ // Misc and building slot items have multiple positions they can be assigned to in the loadout
+ // screen but internally they're all tagged as "misc slot" items so that's what we search for.
+ int iSearchSlot = iSlot;
+ if ( GEconItemSchema().GetAccountIndex() == iClass )
+ {
+ if ( IsQuestSlot( iSearchSlot ) )
+ {
+ iSearchSlot = ACCOUNT_LOADOUT_POSITION_ACCOUNT1;
+ }
+ }
+ else
+ {
+ if ( IsMiscSlot( iSearchSlot ) )
+ {
+ iSearchSlot = LOADOUT_POSITION_MISC;
+ }
+ else if ( IsBuildingSlot( iSearchSlot ) )
+ {
+ iSearchSlot = LOADOUT_POSITION_BUILDING;
+ }
+ else if ( IsTauntSlot( iSearchSlot ) )
+ {
+ iSearchSlot = LOADOUT_POSITION_TAUNT;
+ }
+ }
+
+
+
+ // To start with, generate a list of all potentially-useable items that we want to consider for
+ // the UI. We'll strip this down based on duplicates/gameplay restrictions and sort it at the end.
+ CUtlVector<CEconItemView*> vecItems;
+ int iNumItems = TFInventoryManager()->GetAllUsableItemsForSlot( iClass, iSearchSlot, &vecItems );
+
+ CEconItemView *pEquippedItem = NULL;
+
+ typedef CUtlMap<RarityEconIdKey, CEquippableItemsForSlotGenerator::CEquippableResult> HighestLevelMap_t;
+ HighestLevelMap_t mapHighestLevel;
+ SetDefLessFunc( mapHighestLevel );
+
+ // We also prevent items with different kill eater scores from stacking.
+ static CSchemaAttributeDefHandle pAttrDef_KillEaterScore( "kill eater" );
+
+ // Iterate over the list of all the items that we consider as potential display candidates.
+ for ( int i = 0; i < iNumItems; i++ )
+ {
+ CEconItemView *pItem = vecItems[i];
+
+ // Before doing any sort of culling, count the number of unique instances of this particular
+ // definition.
+ {
+ const item_stack_type_t stackType( pItem->GetItemDefIndex(), pItem->GetQuality() );
+ DuplicateCountMap_t::IndexType_t iIndex = m_DuplicateCountsMap.Find( stackType );
+ if ( iIndex == m_DuplicateCountsMap.InvalidIndex() )
+ {
+ m_DuplicateCountsMap.Insert( stackType, 1 );
+ }
+ else
+ {
+ m_DuplicateCountsMap[ iIndex ]++;
+ }
+ }
+
+ // Track whether this is our currently-equipped item.
+ CEconItemView *pCurItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, iSlot );
+ if ( pCurItemData && pCurItemData->GetItemID() && pCurItemData->GetItemID() == pItem->GetItemID() )
+ {
+ pEquippedItem = pItem;
+ }
+
+ // If this item conflicts with items we already have equipped, we note that so that it shows up
+ // differently.
+ CEquippableItemsForSlotGenerator::EItemDisplayType eDisplayType = kSlotDisplay_Normal;
+
+ if ( pItem->GetItemDefinition()->GetEquipRegionMask() & unUsedEquipRegionMask )
+ {
+ eDisplayType = kSlotDisplay_Disabled_EquipRegionConflict;
+ }
+
+ // If we're listing *all* items, including duplicates, we just add everything to the list at once and
+ // move on. We still do the above equipped-item specialcasing.
+ if ( unFlags & kSlotGenerator_ShowDuplicates )
+ {
+ m_vecDisplayItems.AddToTail( CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
+ continue;
+ }
+
+ // Has this item been modified by the user in some way? If so, always list.
+ if ( ShouldItemNotStack( pItem ) )
+ {
+ m_vecDisplayItems.AddToTail( CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
+ continue;
+ }
+
+ // Throw this item into the running map of the highest level item of this type we've seen so far.
+ // "Of this type" means "has a matching rarity and item definition index", so uniques will be sorted
+ // differently from unusuals, but both will show their highest-level item.
+ //
+ // This code does make the assumption that nothing in the key will be able to affect the display type.
+ // (ie., a higher-level item will never be equippable where a lower-level item is not)
+ {
+ uint32 unKillEaterScore = 0;
+ pItem->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScore );
+
+ RarityEconIdKey keyEconId( pItem->GetItemQuality(), pItem->GetItemDefIndex(), unKillEaterScore );
+ HighestLevelMap_t::IndexType_t iIndex = mapHighestLevel.Find( keyEconId );
+ if ( iIndex == mapHighestLevel.InvalidIndex() || pItem->GetItemLevel() > mapHighestLevel[iIndex].m_pEconItemView->GetItemLevel() )
+ {
+ mapHighestLevel.InsertOrReplace( keyEconId, CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
+ }
+ }
+ }
+
+ // Take the resulting list of items we've force-added and items we've taken the highest-level representative
+ // from and put them all into our unsorted display list.
+ FOR_EACH_MAP( mapHighestLevel, i )
+ {
+ m_vecDisplayItems.AddToTail( mapHighestLevel[i] );
+ }
+
+ // Always leave a base item in the list. We don't have to worry about level changes or other weird overlaps
+ // for base weapons. If we don't have an equipped item, this must be the equipped item.
+ CEconItemView *pBaseItem = TFInventoryManager()->GetBaseItemForClass( iClass, iSlot );
+ if ( pBaseItem && pBaseItem->IsValid() )
+ {
+ if ( !pEquippedItem )
+ {
+ pEquippedItem = pBaseItem;
+ }
+ m_vecDisplayItems.AddToTail( pBaseItem );
+ }
+
+ // If the equipped item is in the list, remove it if we're going to manually add it at the top of the display
+ // list later. We add it to the list above and remove it here to make sure that we don't get weird effects
+ // when the equipped item would be the highest level example of an item, etc.
+ if ( pEquippedItem )
+ {
+ if ( unFlags & kSlotGenerator_EquippedSpecialHandling )
+ {
+ m_vecDisplayItems.FindAndFastRemove( pEquippedItem );
+ m_pEquippedItemView = pEquippedItem;
+ }
+ // Special case adding our equipped item even if it doesn't meet the ordinary display criteria. It might
+ // not be the highest level or rarity, but the user equipped it somehow so make sure it shows up.
+ else if ( m_vecDisplayItems.Find( pEquippedItem ) == -1 )
+ {
+ m_vecDisplayItems.AddToTail( pEquippedItem );
+ }
+ }
+
+ // Remove duplicates and sort to put it into the order they'll be displayed to the user.
+ // Sort the items based on selection type
+ int iSortType = tf_item_selection_panel_sort_type.GetInt();
+ switch ( iSortType )
+ {
+ case 0: m_vecDisplayItems.Sort( &SortRarityEconIdKeysDate ); break;
+ case 1: m_vecDisplayItems.Sort( &SortRarityEconIdKeysAlphabetical ); break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+equip_region_mask_t GenerateEquipRegionConflictMask( int iClass, int iUpToSlot, int iIgnoreSlot )
+{
+ Assert( iUpToSlot <= CLASS_LOADOUT_POSITION_COUNT );
+
+ equip_region_mask_t unEquippedRegionMask = 0;
+ for ( int i = 0; i < iUpToSlot; i++ )
+ {
+ if ( i == iIgnoreSlot )
+ continue;
+
+ const CEconItemView *pEquippedItemView = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
+ if ( !pEquippedItemView )
+ continue;
+
+ unEquippedRegionMask |= pEquippedItemView->GetItemDefinition()->GetEquipRegionConflictMask();
+ }
+
+ return unEquippedRegionMask;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEquipSlotItemSelectionPanel::UpdateModelPanelsForSelection( void )
+{
+ Assert( !DisplayOnlyAllowUniqueQualityCheckbox() );
+
+ const bool bShowEquippedItemFirst = true;
+
+ CEquippableItemsForSlotGenerator::EquippableResultsVec_t vecDisplayItems;
+ const wchar_t* wscFilter = m_wNameFilter.Count() ? m_wNameFilter.Base() : NULL;
+
+ // Generate a mask that is "every equip region we're using except for whatever is in this current slot" and use
+ // that to generate a list of everything we could possibly equip for this current slot.
+ const equip_region_mask_t unUsedEquipRegionMask = GenerateEquipRegionConflictMask( m_iClass, CLASS_LOADOUT_POSITION_COUNT, m_iSlot );
+
+ CEquippableItemsForSlotGenerator equippableItems( m_iClass,
+ m_iSlot,
+ unUsedEquipRegionMask,
+ bShowEquippedItemFirst ? CEquippableItemsForSlotGenerator::kSlotGenerator_EquippedSpecialHandling : CEquippableItemsForSlotGenerator::kSlotGenerator_None );
+
+
+ if( m_bShowingEntireBackpack )
+ {
+ int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount();
+ for ( int i = 1; i <= iNumItems; i++ )
+ {
+ CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i);
+ if ( pItemData && pItemData->IsValid() )
+ {
+ if( !DoesItemPassSearchFilter( pItemData->GetDescription(), wscFilter ) )
+ continue;
+
+ CEquippableItemsForSlotGenerator::CEquippableResult& result = vecDisplayItems[ vecDisplayItems.AddToTail( pItemData ) ];
+ result.m_eDisplayType = GetItemNotSelectableReason( pItemData ) ? CEquippableItemsForSlotGenerator::kSlotDisplay_Invalid : CEquippableItemsForSlotGenerator::kSlotDisplay_Normal;
+ }
+ }
+ }
+ else
+ {
+ // Copy the generated data back into our local structures.
+ DeepCopyMap( equippableItems.GetDuplicateCountMap(), &m_DuplicateCounts );
+
+ const CEquippableItemsForSlotGenerator::EquippableResultsVec_t& allDisplayItems = equippableItems.GetDisplayItems();
+ for ( int i=0; i<allDisplayItems.Count(); ++i )
+ {
+ if ( DoesItemPassSearchFilter( allDisplayItems[i].m_pEconItemView->GetDescription(), wscFilter ) )
+ {
+ vecDisplayItems.AddToTail( allDisplayItems[i] );
+ }
+ }
+ }
+
+ CEconItemView *pEquippedItem = equippableItems.GetEquippedItem();
+ if ( pEquippedItem )
+ {
+ if ( bShowEquippedItemFirst && DoesItemPassSearchFilter( pEquippedItem->GetDescription(), wscFilter ) )
+ {
+ vecDisplayItems.AddToHead( pEquippedItem );
+ }
+
+ m_iCurrentItemID = pEquippedItem->GetItemID();
+ }
+
+ // If the loadout slot is one that players can have empty, put a "nothing" entry at the end of the list.
+ if ( !TFInventoryManager()->SlotContainsBaseItems( GEconItemSchema().GetEquipTypeFromClassIndex( m_iClass ), m_iSlot ) )
+ {
+ vecDisplayItems.AddToHead( NULL );
+ }
+
+ m_iItemsInSelection = vecDisplayItems.Count();
+
+ // Make sure we're not on an invalid page.
+ if ( GetCurrentPage() >= GetNumPages() )
+ {
+ SetCurrentPage( 0 );
+ }
+
+ int nOldSelection = GetFirstSelectedItemIndex( true );
+ int nPageStart = GetCurrentPage() * GetNumSlotsPerPage();
+ nOldSelection += nPageStart;
+
+ static ConVarRef joystick( "joystick" );
+ if ( joystick.IsValid() && joystick.GetBool() )
+ {
+ if( nOldSelection == -1 || nOldSelection >= vecDisplayItems.Count() )
+ nOldSelection = nPageStart;
+ }
+
+
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ int iItemIndex = i + nPageStart;
+
+ // We only show the equipped state for the equipped items, below
+ m_pItemModelPanels[i]->SetShowEquipped( false );
+ m_pItemModelPanels[i]->SetShowGreyedOutTooltip( true );
+ m_pItemModelPanels[i]->SetGreyedOut( NULL );
+ m_pItemModelPanels[i]->SetNoItemText( "#SelectNoItemSlot" );
+ bool bSelected = joystick.IsValid() && joystick.GetBool() && iItemIndex == nOldSelection;
+ m_pItemModelPanels[i]->SetSelected( bSelected );
+ m_pItemModelPanels[i]->SetShowQuantity( true );
+ m_pItemModelPanels[i]->SetForceShowEquipped( false );
+
+ bool bShowEquipped = false;
+ if ( vecDisplayItems.Count() > iItemIndex )
+ {
+ const char* pszGreyOutReason = NULL;
+ if( m_bShowingEntireBackpack )
+ {
+ pszGreyOutReason = GetItemNotSelectableReason( vecDisplayItems[iItemIndex].m_pEconItemView );
+ }
+ else
+ {
+ pszGreyOutReason = vecDisplayItems[iItemIndex].m_eDisplayType == CEquippableItemsForSlotGenerator::kSlotDisplay_Normal ? NULL : "#Econ_GreyOutReason_EquipRegionConflict";
+ }
+ // Show equipped state on base items too
+ bShowEquipped = false;
+ // Check if this item is already equipped, potentially in another slot
+ if ( vecDisplayItems[iItemIndex].m_pEconItemView )
+ {
+ bShowEquipped |= vecDisplayItems[iItemIndex].m_pEconItemView->IsEquippedForClass( m_iClass );
+ }
+
+ // Check if this item is the currently equipped item
+ if ( pEquippedItem )
+ {
+ bShowEquipped |= pEquippedItem == vecDisplayItems[iItemIndex].m_pEconItemView;
+ }
+
+ m_pItemModelPanels[i]->SetForceShowEquipped( bShowEquipped );
+ m_pItemModelPanels[i]->SetItem( vecDisplayItems[iItemIndex].m_pEconItemView );
+ m_pItemModelPanels[i]->SetGreyedOut( pszGreyOutReason );
+ }
+ else
+ {
+ m_pItemModelPanels[i]->SetItem( NULL );
+ }
+
+ SetBorderForItem( m_pItemModelPanels[i], false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEquipSlotItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
+{
+ if ( !pItem )
+ return NULL;
+
+ CTFItemDefinition *pItemData = pItem->GetStaticData();
+
+ if ( !pItemData->CanBeUsedByClass(m_iClass) )
+ return "#Econ_GreyOutReason_CannotBeUsedByThisClass";
+
+ extern bool AreSlotsConsideredIdentical( EEquipType_t eEquipType, int iBaseSlot, int iTestSlot );
+ if ( !AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItemData->GetLoadoutSlot(m_iClass), m_iSlot ) )
+ return "#Econ_GreyOutReason_CannotBeUsedInThisSlot";
+
+ // Should we gray out this item? This will happen if we're coming from the loadout and we have equip region
+ // conflicts of some kind.
+ const equip_region_mask_t unUsedEquipRegionMask = GenerateEquipRegionConflictMask( m_iClass, CLASS_LOADOUT_POSITION_COUNT, m_iSlot );
+ if ( pItemData->GetEquipRegionMask() & unUsedEquipRegionMask )
+ return "#Econ_GreyOutReason_EquipRegionConflict";
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEquipSlotItemSelectionPanel::OnBackPressed()
+{
+ Assert( m_iCurrentItemID != INVALID_ITEM_DEF_INDEX );
+ PostMessageSelectionReturned( m_iCurrentItemID );
+ OnClose();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionPanel::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
+
+ vgui::TextEntry *pTextEntry = dynamic_cast<vgui::TextEntry *>( pPanel );
+ if ( pTextEntry )
+ {
+ if ( pTextEntry == m_pNameFilterTextEntry )
+ {
+ m_wNameFilter.RemoveAll();
+ if ( m_pNameFilterTextEntry->GetTextLength() )
+ {
+ m_wNameFilter.EnsureCount( m_pNameFilterTextEntry->GetTextLength() + 1 );
+ m_pNameFilterTextEntry->GetText( m_wNameFilter.Base(), m_wNameFilter.Count() * sizeof(wchar_t) );
+ V_wcslower( m_wNameFilter.Base() );
+ }
+ m_flFilterItemTime = gpGlobals->curtime + 0.5f;
+ return;
+ }
+ }
+}
+
+
+//=====================================================================================================================
+// ITEM CRITERIA BASED SELECTION PANEL
+//=====================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CItemCriteriaSelectionPanel::CItemCriteriaSelectionPanel(Panel *parent, const CItemSelectionCriteria *pCriteria, itemid_t pExceptions[], int iNumExceptions ) : CItemSelectionPanel( parent )
+{
+ m_pCriteria = pCriteria;
+
+ UpdateExceptions( pExceptions, iNumExceptions );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemCriteriaSelectionPanel::UpdateExceptions( itemid_t pExceptions[], int iNumExceptions )
+{
+ m_Exceptions.Purge();
+
+ for ( int i = 0; i < iNumExceptions; i++ )
+ {
+ m_Exceptions.AddToTail( pExceptions[i] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemCriteriaSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ vgui::Label *pLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
+ if ( pLabel )
+ {
+ pLabel->SetVisible( false );
+ }
+ SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( "#Craft_SelectItemPanel" ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CItemCriteriaSelectionPanel::ShouldItemPanelBeVisible( CItemModelPanel *pPanel, int iPanelIndex )
+{
+ // First "Empty" panel is always visible.
+ return ( pPanel->HasItem() || iPanelIndex == 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemCriteriaSelectionPanel::UpdateModelPanelsForSelection( void )
+{
+ CUtlVector<CEconItemView*> vecDisplayItems;
+
+ CUtlVector<item_stack_type_t> vecDefsFound;
+
+ const wchar_t* wscFilter = m_wNameFilter.Count() ? m_wNameFilter.Base() : NULL;
+
+ int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount();
+ for ( int i = 1; i <= iNumItems; i++ )
+ {
+ CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i);
+ bool bAdd = false;
+ if ( pItemData && pItemData->IsValid() )
+ {
+ // If this is a valid item, we want to show this if we're showing our entire backpack
+ // or we doing the tailored list and this item matches.
+ if ( m_bShowingEntireBackpack || GetItemNotSelectableReason( pItemData ) == NULL )
+ {
+ // The item also needs to pass the text search filter as well
+ if( DoesItemPassSearchFilter( pItemData->GetDescription(), wscFilter ) )
+ {
+ bAdd = true;
+ }
+ }
+ }
+ // When showing our entire backpack we take everything so long as we arent filtering
+ else if( wscFilter == NULL && m_bShowingEntireBackpack )
+ {
+ bAdd = true;
+ }
+
+ if( bAdd )
+ {
+ // For actual items see if we should stack. Only do so if we're NOT showing
+ // the entire backpack
+ if( pItemData && !m_bShowingEntireBackpack )
+ {
+ // Has this item been modified by the user in some way? If so, always list,
+ // but don't add it to our duplicate count, or our "found indices" list.
+ item_definition_index_t iDefIndex = pItemData->GetItemDefIndex();
+ if ( ShouldItemNotStack( pItemData ) )
+ {
+ vecDisplayItems.AddToTail( pItemData );
+ continue;
+ }
+
+ item_stack_type_t stackType( iDefIndex, pItemData->GetQuality() );
+ int iIndex = m_DuplicateCounts.Find( stackType );
+ if ( iIndex == m_DuplicateCounts.InvalidIndex() )
+ {
+ m_DuplicateCounts.Insert( stackType, 1 );
+ }
+ else
+ {
+ m_DuplicateCounts[ iIndex ]++;
+ }
+
+ if ( vecDefsFound.Find( stackType ) != vecDefsFound.InvalidIndex() )
+ continue;
+
+ vecDefsFound.AddToTail( stackType );
+ }
+
+ vecDisplayItems.AddToTail( pItemData );
+ }
+ }
+
+ // Sort them alphabetically if not viewing entire backpack
+ if( !m_bShowingEntireBackpack )
+ {
+ vecDisplayItems.Sort( &SortRarityEconIdKeysAlphabetical_Views );
+ }
+
+ // Add an "Empty" item to the start
+ vecDisplayItems.AddToHead( NULL );
+
+ m_iItemsInSelection = vecDisplayItems.Count();
+
+ // Make sure we're not on an invalid page.
+ if ( GetCurrentPage() >= GetNumPages() )
+ {
+ SetCurrentPage( 0 );
+ }
+
+ for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
+ {
+ int iItemIndex = i + (GetCurrentPage() * GetNumSlotsPerPage());
+ if ( vecDisplayItems.Count() > iItemIndex )
+ {
+ m_pItemModelPanels[i]->SetItem( vecDisplayItems[iItemIndex] );
+ }
+ else
+ {
+ m_pItemModelPanels[i]->SetItem( NULL );
+ }
+
+ // We only show the equipped state for the equipped items, below
+ m_pItemModelPanels[i]->SetShowEquipped( true );
+ m_pItemModelPanels[i]->SetShowGreyedOutTooltip( true );
+ m_pItemModelPanels[i]->SetGreyedOut( GetItemNotSelectableReason( m_pItemModelPanels[i]->GetItem() ) );
+ m_pItemModelPanels[i]->SetNoItemText( "#SelectNoItemSlot" );
+
+ SetBorderForItem( m_pItemModelPanels[i], false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CItemCriteriaSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
+{
+ if ( !pItem )
+ return NULL;
+
+ // Ignore it if it's in our exceptions list
+ if ( m_Exceptions.Find( pItem->GetItemID() ) != m_Exceptions.InvalidIndex() )
+ return "";
+
+ // Matching all items?
+ if ( !m_pCriteria )
+ return NULL;
+
+ CTFItemDefinition *pItemData = pItem->GetStaticData();
+ return m_pCriteria->BEvaluate( pItemData ) ? NULL : "";
+}
+
+//=====================================================================================================================
+// CRAFTING SELECTION PANEL
+//=====================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CCraftingItemSelectionPanel::CCraftingItemSelectionPanel(Panel *parent )
+ : CItemCriteriaSelectionPanel( parent, NULL, NULL, 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCraftingItemSelectionPanel::UpdateOnShow( const CItemSelectionCriteria *pCriteria, bool bForceBackpack, itemid_t pExceptions[], int iNumExceptions )
+{
+ m_pCriteria = pCriteria;
+ UpdateExceptions( pExceptions, iNumExceptions );
+
+ if ( m_bShowingEntireBackpack != bForceBackpack )
+ {
+ SetCurrentPage( 0 );
+ m_bShowingEntireBackpack = bForceBackpack;
+ m_bReapplyItemKVs = true;
+ }
+ m_bForceBackpack = bForceBackpack;
+
+ if ( !m_bForceBackpack )
+ {
+ SetCurrentPage( 0 );
+ }
+
+ UpdateModelPanels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CCraftingItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
+{
+ if ( !pItem )
+ return NULL;
+
+ // Must not be marked no-craft
+ if ( !pItem->IsUsableInCrafting() )
+ return "#Econ_GreyOutReason_ItemNotCraftable";
+
+ // Are we filtering out items of non-unique quality that we might not want to accidentally craft?
+ if ( m_pOnlyAllowUniqueQuality && m_pOnlyAllowUniqueQuality->IsSelected() && pItem->GetQuality() != AE_UNIQUE )
+ return "#Econ_GreyOutReason_ItemSpecialQuality";
+
+ return BaseClass::GetItemNotSelectableReason( pItem );
+}
+
+
+//=====================================================================================================================
+// ACCOUNT SLOT ITEM SELECTION PANEL
+//=====================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CAccountSlotItemSelectionPanel::CAccountSlotItemSelectionPanel( Panel *pParent, int iSlot, const char *pszTitleToken )
+ : CEquipSlotItemSelectionPanel( pParent, GEconItemSchema().GetAccountIndex(), iSlot )
+ , m_pszTitleToken( pszTitleToken )
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAccountSlotItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pWeaponLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
+ if ( m_pWeaponLabel )
+ {
+ m_pWeaponLabel->SetVisible( false );
+ }
+
+ SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( m_pszTitleToken ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CAccountSlotItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
+{
+ if ( !pItem )
+ return NULL;
+
+ CTFItemDefinition *pItemData = pItem->GetStaticData();
+
+ if ( pItemData->GetEquipType() != EEquipType_t::EQUIP_TYPE_ACCOUNT )
+ return "#Econ_GreyOutReason_CannotBeUsedByThisClass";
+
+ extern bool AreSlotsConsideredIdentical( EEquipType_t eEquipType, int iBaseSlot, int iTestSlot );
+ if ( !AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItemData->GetLoadoutSlot(m_iClass), m_iSlot ) )
+ return "#Econ_GreyOutReason_CannotBeUsedInThisSlot";
+
+ return NULL;
+} \ No newline at end of file