You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1493 lines
50 KiB
1493 lines
50 KiB
//========= 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; |
|
} |