diff options
Diffstat (limited to 'game/client/econ/item_selection_panel.cpp')
| -rw-r--r-- | game/client/econ/item_selection_panel.cpp | 1493 |
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 |