//========= 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 #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 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( FindChildByName("NoItemsLabel") ); m_pOnlyAllowUniqueQuality = dynamic_cast( FindChildByName("OnlyAllowUniqueQuality") ); m_pShowBackpack = dynamic_cast( FindChildByName("ShowBackpack") ); m_pShowSelection = dynamic_cast( FindChildByName("ShowSelection") ); m_pNextPageButton = dynamic_cast( FindChildByName("NextPageButton") ); m_pPrevPageButton = dynamic_cast( FindChildByName("PrevPageButton") ); m_pCurPageLabel = dynamic_cast( 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( "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( command ) ); } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemSelectionPanel::OnButtonChecked( KeyValues *pData ) { Assert( reinterpret_cast( 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( 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 vecItems; int iNumItems = TFInventoryManager()->GetAllUsableItemsForSlot( iClass, iSearchSlot, &vecItems ); CEconItemView *pEquippedItem = NULL; typedef CUtlMap 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; iGetDescription(), 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( data->GetPtr("panel") ); vgui::TextEntry *pTextEntry = dynamic_cast( 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( 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 vecDisplayItems; CUtlVector 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( 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; }