Team Fortress 2 Source Code as on 22/4/2020
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.

1492 lines
50 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "item_selection_panel.h"
  8. #include "vgui/ISurface.h"
  9. #include "c_tf_player.h"
  10. #include "gamestringpool.h"
  11. #include "iclientmode.h"
  12. #include "tf_item_inventory.h"
  13. #include "ienginevgui.h"
  14. #include <vgui/ILocalize.h>
  15. #include "vgui_controls/TextImage.h"
  16. #include "vgui_controls/CheckButton.h"
  17. #include "vgui_controls/ComboBox.h"
  18. #include "vgui/IInput.h"
  19. #include "item_model_panel.h"
  20. #include "econ_item_constants.h"
  21. #include "econ_item_system.h"
  22. #include "econ_item_description.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. 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)" );
  26. const char *g_szEquipSlotHeader[] =
  27. {
  28. "#ItemSel_PRIMARY", // LOADOUT_POSITION_PRIMARY = 0,
  29. "#ItemSel_SECONDARY", // LOADOUT_POSITION_SECONDARY,
  30. "#ItemSel_MELEE", // LOADOUT_POSITION_MELEE,
  31. "#ItemSel_UTILITY", // LOADOUT_POSITION_UTILITY // Staging
  32. "#ItemSel_PDA", // LOADOUT_POSITION_BUILDING,
  33. "#ItemSel_PDA", // LOADOUT_POSITION_PDA,
  34. "#ItemSel_PDA", // LOADOUT_POSITION_PDA2
  35. "#ItemSel_MISC", // LOADOUT_POSITION_HEAD
  36. "#ItemSel_MISC", // LOADOUT_POSITION_MISC
  37. "#ItemSel_ACTION", // LOADOUT_POSITION_ACTION
  38. "#ItemSel_MISC", // LOADOUT_POSITION_MISC2
  39. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT
  40. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT2
  41. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT3
  42. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT4
  43. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT5
  44. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT6
  45. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT7
  46. "#ItemSel_TAUNT", // LOADOUT_POSITION_TAUNT8
  47. #ifdef STAGING_ONLY
  48. "#ItemSel_PDA_ADDON1", // LOADOUT_POSITION_PDA_ADDON1
  49. "#ItemSel_PDA_ADDON2", // LOADOUT_POSITION_PDA_ADDON2
  50. "", // LOADOUT_POSITION_PDA3,
  51. "", // LOADOUT_POSITION_BUILDING2,
  52. #endif // STAGING_ONLY
  53. };
  54. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szEquipSlotHeader ) == CLASS_LOADOUT_POSITION_COUNT );
  55. static bool ShouldItemNotStack( CEconItemView *pItemData )
  56. {
  57. CEconItem *pSOCData = pItemData->GetSOCData();
  58. if ( pSOCData && pSOCData->BHasDynamicAttributes() )
  59. {
  60. return true;
  61. }
  62. return false;
  63. };
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. //-----------------------------------------------------------------------------
  67. CItemSelectionPanel::CItemSelectionPanel(Panel *parent) : CBaseLoadoutPanel(parent, "ItemSelectionPanel")
  68. {
  69. m_pCaller = parent;
  70. m_pSelectionItemModelPanelKVs = NULL;
  71. m_pDuplicateLabelKVs = NULL;
  72. m_bShowingEntireBackpack = false;
  73. m_iItemsInSelection = 0;
  74. m_bShowDuplicates = false;
  75. m_bForceBackpack = false;
  76. m_pOnlyAllowUniqueQuality = NULL;
  77. m_pShowBackpack = NULL;
  78. m_pShowSelection = NULL;
  79. m_pNextPageButton = NULL;
  80. m_pPrevPageButton = NULL;
  81. m_pCurPageLabel = NULL;
  82. m_pNoItemsInSelectionLabel = NULL;
  83. m_bGotMousePressed = false;
  84. m_pNameFilterTextEntry = NULL;
  85. m_DuplicateCounts.SetLessFunc( DefLessFunc( DuplicateCountsMap_t::KeyType_t ) );
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose:
  89. //-----------------------------------------------------------------------------
  90. CItemSelectionPanel::~CItemSelectionPanel()
  91. {
  92. if ( m_pSelectionItemModelPanelKVs )
  93. {
  94. m_pSelectionItemModelPanelKVs->deleteThis();
  95. m_pSelectionItemModelPanelKVs = NULL;
  96. }
  97. if ( m_pDuplicateLabelKVs )
  98. {
  99. m_pDuplicateLabelKVs->deleteThis();
  100. m_pDuplicateLabelKVs = NULL;
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. void CItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  107. {
  108. m_pNameFilterTextEntry = NULL;
  109. BaseClass::ApplySchemeSettings( pScheme );
  110. LoadControlSettings( GetSchemeFile() );
  111. m_pNoItemsInSelectionLabel = dynamic_cast<vgui::Label*>( FindChildByName("NoItemsLabel") );
  112. m_pOnlyAllowUniqueQuality = dynamic_cast<vgui::CheckButton *>( FindChildByName("OnlyAllowUniqueQuality") );
  113. m_pShowBackpack = dynamic_cast<CExButton*>( FindChildByName("ShowBackpack") );
  114. m_pShowSelection = dynamic_cast<CExButton*>( FindChildByName("ShowSelection") );
  115. m_pNextPageButton = dynamic_cast<CExButton*>( FindChildByName("NextPageButton") );
  116. m_pPrevPageButton = dynamic_cast<CExButton*>( FindChildByName("PrevPageButton") );
  117. m_pCurPageLabel = dynamic_cast<vgui::Label*>( FindChildByName("CurPageLabel") );
  118. // Give individual selection panels the ability to specify whether or not they want to show the
  119. // checkbox controlling quality filtering. It really only makes sense for things like crafting,
  120. // not equipment selection.
  121. if ( m_pOnlyAllowUniqueQuality )
  122. {
  123. m_pOnlyAllowUniqueQuality->SetVisible( DisplayOnlyAllowUniqueQualityCheckbox() );
  124. // By default, if the checkbox is visible, it's enabled. Users can disable it manually if
  125. // they want to craft potentially-more-valuable items.
  126. if ( m_pOnlyAllowUniqueQuality->IsVisible() )
  127. {
  128. m_pOnlyAllowUniqueQuality->SetSelected( true );
  129. }
  130. }
  131. m_pNameFilterTextEntry = FindControl<vgui::TextEntry>( "NameFilterTextEntry" );
  132. if ( m_pNameFilterTextEntry )
  133. {
  134. m_pNameFilterTextEntry->AddActionSignalTarget( this );
  135. }
  136. UpdateModelPanels();
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose:
  140. //-----------------------------------------------------------------------------
  141. void CItemSelectionPanel::ApplySettings( KeyValues *inResourceData )
  142. {
  143. BaseClass::ApplySettings( inResourceData );
  144. KeyValues *pItemKV = inResourceData->FindKey( "modelpanels_selection_kv" );
  145. if ( pItemKV )
  146. {
  147. if ( m_pSelectionItemModelPanelKVs )
  148. {
  149. m_pSelectionItemModelPanelKVs->deleteThis();
  150. }
  151. m_pSelectionItemModelPanelKVs = new KeyValues("modelpanels_selection_kv");
  152. pItemKV->CopySubkeys( m_pSelectionItemModelPanelKVs );
  153. }
  154. KeyValues *pLabelKV = inResourceData->FindKey( "duplicatelabels_kv" );
  155. if ( pLabelKV )
  156. {
  157. if ( m_pDuplicateLabelKVs )
  158. {
  159. m_pDuplicateLabelKVs->deleteThis();
  160. }
  161. m_pDuplicateLabelKVs = new KeyValues("duplicatelabels_kv");
  162. pLabelKV->CopySubkeys( m_pDuplicateLabelKVs );
  163. }
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Purpose:
  167. //-----------------------------------------------------------------------------
  168. void CItemSelectionPanel::ApplyKVsToItemPanels( void )
  169. {
  170. BaseClass::ApplyKVsToItemPanels();
  171. if ( !m_bShowingEntireBackpack )
  172. {
  173. if ( m_pSelectionItemModelPanelKVs )
  174. {
  175. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  176. {
  177. m_pItemModelPanels[i]->ApplySettings( m_pSelectionItemModelPanelKVs );
  178. m_pItemModelPanels[i]->UpdatePanels();
  179. }
  180. }
  181. if ( m_pDuplicateLabelKVs )
  182. {
  183. FOR_EACH_VEC( m_pDuplicateCountLabels, i )
  184. {
  185. if ( !m_pDuplicateCountLabels[i]->IsVisible() )
  186. continue;
  187. m_pDuplicateCountLabels[i]->ApplySettings( m_pDuplicateLabelKVs );
  188. m_pDuplicateCountLabels[i]->SetMouseInputEnabled( false );
  189. }
  190. }
  191. }
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. //-----------------------------------------------------------------------------
  196. void CItemSelectionPanel::PerformLayout( void )
  197. {
  198. BaseClass::PerformLayout();
  199. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  200. {
  201. // In backpack mode we show empty slots. Otherwise we don't.
  202. bool bVisible = m_bShowingEntireBackpack;
  203. if ( !bVisible )
  204. {
  205. bVisible = (i < GetNumSlotsPerPage()) && ShouldItemPanelBeVisible( m_pItemModelPanels[i], i );
  206. }
  207. m_pItemModelPanels[i]->SetVisible( bVisible );
  208. if ( bVisible )
  209. {
  210. PositionItemPanel( m_pItemModelPanels[i], i );
  211. }
  212. UpdateDuplicateCounts();
  213. }
  214. FOR_EACH_VEC( m_pDuplicateCountLabels, i )
  215. {
  216. if ( m_pDuplicateCountLabels[i]->IsVisible() )
  217. {
  218. int iXPos, iYPos;
  219. m_pItemModelPanels[i]->GetPos( iXPos, iYPos );
  220. m_pDuplicateCountLabels[i]->SetPos( iXPos, iYPos );
  221. }
  222. }
  223. m_pShowBackpack->SetVisible( !m_bShowingEntireBackpack && !m_bForceBackpack );
  224. m_pShowSelection->SetVisible( m_bShowingEntireBackpack && !m_bForceBackpack );
  225. m_pNextPageButton->SetVisible( true );
  226. m_pPrevPageButton->SetVisible( true );
  227. m_pCurPageLabel->SetVisible( true );
  228. m_pNextPageButton->SetEnabled( GetNumPages() > 1 );
  229. m_pPrevPageButton->SetEnabled( GetNumPages() > 1 );
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. //-----------------------------------------------------------------------------
  234. void CItemSelectionPanel::OnThink( void )
  235. {
  236. BaseClass::OnThink();
  237. if ( m_flFilterItemTime && gpGlobals->curtime >= m_flFilterItemTime )
  238. {
  239. SetCurrentPage( 0 );
  240. //DeSelectAllBackpackItemPanels();
  241. UpdateModelPanels();
  242. m_flFilterItemTime = 0.0f;
  243. }
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose:
  247. //-----------------------------------------------------------------------------
  248. void CItemSelectionPanel::OnCommand( const char *command )
  249. {
  250. if ( !Q_stricmp( command, "vguicancel" ) )
  251. {
  252. PostMessageSelectionReturned( INVALID_ITEM_ID );
  253. OnClose();
  254. return;
  255. }
  256. else if ( !Q_strnicmp( command, "nextpage", 8 ) )
  257. {
  258. HideMouseOverPanel();
  259. SetCurrentPage( GetCurrentPage() + 1 );
  260. UpdateModelPanels();
  261. return;
  262. }
  263. else if ( !Q_strnicmp( command, "prevpage", 8 ) )
  264. {
  265. HideMouseOverPanel();
  266. SetCurrentPage( GetCurrentPage() - 1 );
  267. UpdateModelPanels();
  268. return;
  269. }
  270. else if ( !Q_strnicmp( command, "show_backpack", 8 ) )
  271. {
  272. m_bReapplyItemKVs = true;
  273. m_bShowingEntireBackpack = true;
  274. UpdateModelPanels();
  275. //Repaint();
  276. return;
  277. }
  278. else if ( !Q_strnicmp( command, "show_selection", 8 ) )
  279. {
  280. m_bReapplyItemKVs = true;
  281. m_bShowingEntireBackpack = false;
  282. UpdateModelPanels();
  283. //Repaint();
  284. return;
  285. }
  286. else
  287. {
  288. engine->ClientCmd( const_cast<char *>( command ) );
  289. }
  290. BaseClass::OnCommand( command );
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose:
  294. //-----------------------------------------------------------------------------
  295. void CItemSelectionPanel::OnButtonChecked( KeyValues *pData )
  296. {
  297. Assert( reinterpret_cast<vgui::Panel *>( pData->GetPtr("panel") ) == m_pOnlyAllowUniqueQuality );
  298. UpdateModelPanels();
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Handle the escape key because it doesn't come through as "pressed"
  302. //-----------------------------------------------------------------------------
  303. void CItemSelectionPanel::OnKeyCodeTyped(vgui::KeyCode code)
  304. {
  305. if ( code == KEY_ESCAPE )
  306. {
  307. // 0 implies do nothing, INVALID_ITEM_ID means stock and we dont want to equip stock
  308. PostMessageSelectionReturned( 0 );
  309. OnClose();
  310. }
  311. else
  312. {
  313. BaseClass::OnKeyCodeTyped( code );
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Handles keypresses in the item selection panel
  318. //-----------------------------------------------------------------------------
  319. void CItemSelectionPanel::OnKeyCodePressed( vgui::KeyCode code )
  320. {
  321. // let our parent class handle all the arrow key/dpad stuff
  322. if( HandleItemSelectionKeyPressed( code ) )
  323. {
  324. return;
  325. }
  326. ButtonCode_t nButtonCode = GetBaseButtonCode( code );
  327. if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A )
  328. {
  329. CItemModelPanel *pItemPanel = GetFirstSelectedItemModelPanel( true );
  330. if( pItemPanel && !pItemPanel->IsGreyedOut() )
  331. {
  332. NotifySelectionReturned( pItemPanel );
  333. }
  334. }
  335. else if( nButtonCode == KEY_XBUTTON_B )
  336. {
  337. PostMessageSelectionReturned( INVALID_ITEM_ID );
  338. OnClose();
  339. }
  340. else
  341. {
  342. BaseClass::OnKeyCodePressed( code );
  343. }
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Purpose: Handles key release events in the backpack
  347. //-----------------------------------------------------------------------------
  348. void CItemSelectionPanel::OnKeyCodeReleased( vgui::KeyCode code )
  349. {
  350. if( ! HandleItemSelectionKeyReleased( code ) )
  351. BaseClass::OnKeyCodeReleased( code );
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Purpose:
  355. //-----------------------------------------------------------------------------
  356. void CItemSelectionPanel::OnClose( void )
  357. {
  358. BaseClass::OnClose();
  359. if ( ShouldDeleteOnClose() )
  360. {
  361. // Delete ourself now that we're done
  362. MarkForDeletion();
  363. }
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose:
  367. //-----------------------------------------------------------------------------
  368. void CItemSelectionPanel::SetVisible( bool bState )
  369. {
  370. BaseClass::SetVisible( bState );
  371. if( bState )
  372. {
  373. m_wNameFilter.RemoveAll();
  374. if( m_pNameFilterTextEntry )
  375. {
  376. m_pNameFilterTextEntry->SetText( "" );
  377. }
  378. }
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. //-----------------------------------------------------------------------------
  383. void CItemSelectionPanel::OnItemPanelMousePressed( vgui::Panel *panel )
  384. {
  385. // This is annoying. Why do I get mouse released events for releases that started with a push before I was visible?
  386. m_bGotMousePressed = true;
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose:
  390. //-----------------------------------------------------------------------------
  391. void CItemSelectionPanel::OnItemPanelMouseReleased( vgui::Panel *panel )
  392. {
  393. if ( !m_bGotMousePressed )
  394. return;
  395. m_bGotMousePressed = false;
  396. CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
  397. NotifySelectionReturned( pItemPanel );
  398. }
  399. //-----------------------------------------------------------------------------
  400. // Purpose: Lets the parent know what the selection was
  401. //-----------------------------------------------------------------------------
  402. void CItemSelectionPanel::NotifySelectionReturned( CItemModelPanel *pItemPanel )
  403. {
  404. if ( pItemPanel && IsVisible() )
  405. {
  406. CEconItemView *pItemData = pItemPanel->GetItem();
  407. if ( GetItemNotSelectableReason( pItemData ) != NULL )
  408. return;
  409. if ( DisableItemSelectionFromGrayedOutPanels() && pItemPanel->IsGreyedOut() )
  410. return;
  411. itemid_t ulItemID = INVALID_ITEM_ID;
  412. if ( pItemData && pItemData->IsValid() )
  413. {
  414. ulItemID = pItemData->GetItemID();
  415. }
  416. PostMessageSelectionReturned( ulItemID );
  417. }
  418. OnClose();
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. //-----------------------------------------------------------------------------
  423. void CItemSelectionPanel::UpdateModelPanels( void )
  424. {
  425. // If we're showing the whole backpack, go through the inventory like the backpack does.
  426. if ( m_bShowingEntireBackpack )
  427. {
  428. UpdateModelPanelsForSelection();
  429. if ( m_pNoItemsInSelectionLabel )
  430. {
  431. m_pNoItemsInSelectionLabel->SetVisible( false );
  432. }
  433. }
  434. else
  435. {
  436. // Clear the dupe counts.
  437. m_DuplicateCounts.Purge();
  438. UpdateModelPanelsForSelection();
  439. if ( m_pNoItemsInSelectionLabel )
  440. {
  441. m_pNoItemsInSelectionLabel->SetVisible( m_iItemsInSelection == 0 );
  442. }
  443. }
  444. // Update the current backpack page
  445. char szTmp[16];
  446. Q_snprintf(szTmp, 16, "%d/%d", GetCurrentPage()+1, GetNumPages() );
  447. SetDialogVariable( "backpackpage", szTmp );
  448. // And we're done!
  449. InvalidateLayout();
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. //-----------------------------------------------------------------------------
  454. void CItemSelectionPanel::UpdateDuplicateCounts( void )
  455. {
  456. bool bShow = (m_bShowDuplicates && !m_bShowingEntireBackpack);
  457. if ( !bShow )
  458. {
  459. FOR_EACH_VEC( m_pDuplicateCountLabels, i )
  460. {
  461. m_pDuplicateCountLabels[i]->SetVisible( false );
  462. }
  463. return;
  464. }
  465. FOR_EACH_VEC( m_pItemModelPanels, i )
  466. {
  467. if ( i >= GetNumSlotsPerPage() )
  468. break;
  469. if ( !m_pItemModelPanels[i]->IsVisible() )
  470. {
  471. m_pDuplicateCountLabels[i]->SetVisible( false );
  472. continue;
  473. }
  474. if ( m_pDuplicateCountLabels.Count() <= i )
  475. {
  476. CExLabel *pLabel = new CExLabel( this, "", "x1" );
  477. m_pDuplicateCountLabels.AddToTail( pLabel );
  478. if ( m_pDuplicateLabelKVs )
  479. {
  480. pLabel->ApplySettings( m_pDuplicateLabelKVs );
  481. }
  482. pLabel->MakeReadyForUse();
  483. pLabel->InvalidateLayout( true );
  484. pLabel->SetMouseInputEnabled( false );
  485. }
  486. CEconItemView *pItem = m_pItemModelPanels[i]->GetItem();
  487. if ( !pItem || !pItem->IsValid() || ShouldItemNotStack( pItem ) )
  488. {
  489. m_pDuplicateCountLabels[i]->SetVisible( false );
  490. continue;
  491. }
  492. int iIndex = m_DuplicateCounts.Find( item_stack_type_t( pItem->GetItemDefIndex(), pItem->GetQuality() ) );
  493. if ( iIndex == m_DuplicateCounts.InvalidIndex() || m_DuplicateCounts[iIndex] <= 1 )
  494. {
  495. m_pDuplicateCountLabels[i]->SetVisible( false );
  496. continue;
  497. }
  498. wchar_t wzCost[10];
  499. _snwprintf( wzCost, ARRAYSIZE( wzCost ), L"x%d", m_DuplicateCounts[iIndex] );
  500. m_pDuplicateCountLabels[i]->SetText( wzCost );
  501. m_pDuplicateCountLabels[i]->SetVisible( true );
  502. }
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Purpose:
  506. //-----------------------------------------------------------------------------
  507. void CItemSelectionPanel::CreateItemPanels( void )
  508. {
  509. // Always create the maximum number of panels
  510. int iNumPanels = BACKPACK_SLOTS_PER_PAGE;
  511. if ( m_pItemModelPanels.Count() < iNumPanels )
  512. {
  513. for ( int i = m_pItemModelPanels.Count(); i < iNumPanels; i++ )
  514. {
  515. AddNewItemPanel(i);
  516. }
  517. }
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose:
  521. //-----------------------------------------------------------------------------
  522. int CItemSelectionPanel::GetNumPages( void )
  523. {
  524. int iNumItems = 0;
  525. if ( m_bShowingEntireBackpack )
  526. {
  527. iNumItems = InventoryManager()->GetLocalInventory()->GetMaxItemCount();
  528. }
  529. else
  530. {
  531. iNumItems = m_iItemsInSelection;
  532. }
  533. return (int)(ceil((float)iNumItems / (float)GetNumItemPanels()));
  534. }
  535. //-----------------------------------------------------------------------------
  536. // Purpose:
  537. //-----------------------------------------------------------------------------
  538. void CItemSelectionPanel::SetCurrentPage( int nNewPage )
  539. {
  540. if ( nNewPage < 0 )
  541. {
  542. nNewPage = GetNumPages() - 1;
  543. }
  544. else if ( nNewPage >= GetNumPages() )
  545. {
  546. nNewPage = 0;
  547. }
  548. BaseClass::SetCurrentPage( nNewPage );
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose:
  552. //-----------------------------------------------------------------------------
  553. void CItemSelectionPanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex )
  554. {
  555. int iCenter = GetWide() * 0.5;
  556. int iButtonX = (iIndex % GetNumColumns());
  557. int iButtonY = (iIndex / GetNumColumns());
  558. int iXPos = (iCenter + m_iItemBackpackOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX);
  559. int iYPos = m_iItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY);
  560. m_pItemModelPanels[iIndex]->SetPos( iXPos, iYPos );
  561. }
  562. //-----------------------------------------------------------------------------
  563. // Purpose:
  564. //-----------------------------------------------------------------------------
  565. void CItemSelectionPanel::PostMessageSelectionReturned( itemid_t ulItemID )
  566. {
  567. KeyValues *pKey = new KeyValues( "SelectionReturned" );
  568. pKey->SetUint64( "itemindex", ulItemID );
  569. PostMessage( m_pCaller, pKey );
  570. }
  571. //=====================================================================================================================
  572. // EQUIP SLOT ITEM SELECTION PANEL
  573. //=====================================================================================================================
  574. //-----------------------------------------------------------------------------
  575. // Purpose:
  576. //-----------------------------------------------------------------------------
  577. CEquipSlotItemSelectionPanel::CEquipSlotItemSelectionPanel(Panel *parent, int iClass, int iSlot) : CItemSelectionPanel( parent )
  578. {
  579. m_iClass = iClass;
  580. m_iSlot = iSlot;
  581. m_pWeaponLabel = NULL;
  582. m_flFilterItemTime = 0.f;
  583. m_iCurrentItemID = INVALID_ITEM_ID;
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Purpose:
  587. //-----------------------------------------------------------------------------
  588. void CEquipSlotItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  589. {
  590. BaseClass::ApplySchemeSettings( pScheme );
  591. m_pWeaponLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
  592. TFPlayerClassData_t *pData = GetPlayerClassData( m_iClass );
  593. SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) );
  594. if ( m_pWeaponLabel )
  595. {
  596. m_pWeaponLabel->SetText( g_szEquipSlotHeader[m_iSlot] );
  597. }
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose:
  601. //-----------------------------------------------------------------------------
  602. void CEquipSlotItemSelectionPanel::PerformLayout( void )
  603. {
  604. BaseClass::PerformLayout();
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. //-----------------------------------------------------------------------------
  609. bool CEquipSlotItemSelectionPanel::ShouldItemPanelBeVisible( CItemModelPanel *pPanel, int iPanelIndex )
  610. {
  611. // If we don't have an item, but we're the first model panel on a slot
  612. // that has no base item, we still want to be visible because we're the
  613. // panel that allows players to select "Empty" for the slot.
  614. return ( pPanel->HasItem() || (iPanelIndex == 0 && !TFInventoryManager()->SlotContainsBaseItems( GEconItemSchema().GetEquipTypeFromClassIndex( m_iClass ), m_iSlot )) );
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Helper classes/functions for CItemSelectionPanel::UpdateModelPanels().
  618. //-----------------------------------------------------------------------------
  619. // Used to sort/verify uniqueness of user-facing items.
  620. struct RarityEconIdKey
  621. {
  622. int m_iQualitySort;
  623. item_definition_index_t m_defIndex;
  624. uint32 m_unKillEaterScore;
  625. RarityEconIdKey ( )
  626. : m_iQualitySort( -1 )
  627. , m_defIndex( INVALID_ITEM_DEF_INDEX )
  628. , m_unKillEaterScore( 0 )
  629. {
  630. //
  631. }
  632. RarityEconIdKey ( int iQuality, item_definition_index_t defIndex, uint32 unKillEaterScore )
  633. : m_iQualitySort( EconQuality_GetRarityScore( (EEconItemQuality)iQuality ) )
  634. , m_defIndex( defIndex )
  635. , m_unKillEaterScore( unKillEaterScore )
  636. {
  637. //
  638. }
  639. bool operator< ( const RarityEconIdKey& rhs ) const
  640. {
  641. return m_defIndex < rhs.m_defIndex
  642. || m_iQualitySort < rhs.m_iQualitySort
  643. || m_unKillEaterScore < rhs.m_unKillEaterScore;
  644. }
  645. };
  646. //-----------------------------------------------------------------------------
  647. // Purpose:
  648. //-----------------------------------------------------------------------------
  649. static int SortRarityEconIdKeysBackpack ( CEconItemView *const *a, CEconItemView *const *b )
  650. {
  651. Assert( a );
  652. Assert( *a );
  653. Assert( b );
  654. Assert( *b );
  655. // Sorting by the backpack order doesn't need to check any subproperties like level because
  656. // every item should have a unique backpack slot already/
  657. return ExtractBackpackPositionFromBackend( (*a)->GetInventoryPosition() ) < ExtractBackpackPositionFromBackend( (*b)->GetInventoryPosition() )
  658. ? -1
  659. : 1;
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose:
  663. //-----------------------------------------------------------------------------
  664. static int SortRarityEconIdKeysAlphabetical_Views ( CEconItemView *const *a, CEconItemView *const *b )
  665. {
  666. Assert( a );
  667. Assert( *a );
  668. Assert( b );
  669. Assert( *b );
  670. // First pass -- sort backpack items by user-visible display name.
  671. // Note: locale-savvy string sorting uses wcscoll, not wcscmp
  672. int iStrCmpRes = wcscoll( (*a)->GetItemName(), (*b)->GetItemName() );
  673. if ( iStrCmpRes != 0 )
  674. return iStrCmpRes;
  675. // Sort by kill eater score as a last-ditch ordering attempt.
  676. static CSchemaAttributeDefHandle pAttrDef_KillEaterScore( "kill eater" );
  677. uint32 unKillEaterScoreA = 0,
  678. unKillEaterScoreB = 0;
  679. (*a)->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScoreA );
  680. (*b)->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScoreB );
  681. // Our names match so sort by quality for similarly-named items.
  682. if ( EconQuality_GetRarityScore( (EEconItemQuality)(*a)->GetItemQuality() ) < EconQuality_GetRarityScore( (EEconItemQuality)(*b)->GetItemQuality() ) ||
  683. unKillEaterScoreA < unKillEaterScoreB ||
  684. (*a)->GetItemLevel() < (*b)->GetItemLevel() )
  685. {
  686. return -1;
  687. }
  688. return 1;
  689. }
  690. //-----------------------------------------------------------------------------
  691. // Purpose:
  692. //-----------------------------------------------------------------------------
  693. static int SortRarityEconIdKeysAlphabetical ( const CEquippableItemsForSlotGenerator::CEquippableResult *a, const CEquippableItemsForSlotGenerator::CEquippableResult *b )
  694. {
  695. return SortRarityEconIdKeysAlphabetical_Views( &a->m_pEconItemView, &b->m_pEconItemView );
  696. }
  697. //-----------------------------------------------------------------------------
  698. static int SortRarityEconIdKeysDate( const CEquippableItemsForSlotGenerator::CEquippableResult *a, const CEquippableItemsForSlotGenerator::CEquippableResult *b )
  699. {
  700. return ( a->m_pEconItemView->GetID() > b->m_pEconItemView->GetID() ) ? -1 : ( a->m_pEconItemView->GetID() < b->m_pEconItemView->GetID() ) ? 1 : 0;
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose: figure out what items should be displayed to the user for a specific
  704. // loadout and what order they should appear in.
  705. //-----------------------------------------------------------------------------
  706. CEquippableItemsForSlotGenerator::CEquippableItemsForSlotGenerator( int iClass, int iSlot, equip_region_mask_t unUsedEquipRegionMask, unsigned int unFlags )
  707. : m_pEquippedItemView( NULL )
  708. {
  709. m_DuplicateCountsMap.SetLessFunc( DefLessFunc( DuplicateCountMap_t::KeyType_t ) );
  710. // Misc and building slot items have multiple positions they can be assigned to in the loadout
  711. // screen but internally they're all tagged as "misc slot" items so that's what we search for.
  712. int iSearchSlot = iSlot;
  713. if ( GEconItemSchema().GetAccountIndex() == iClass )
  714. {
  715. if ( IsQuestSlot( iSearchSlot ) )
  716. {
  717. iSearchSlot = ACCOUNT_LOADOUT_POSITION_ACCOUNT1;
  718. }
  719. }
  720. else
  721. {
  722. if ( IsMiscSlot( iSearchSlot ) )
  723. {
  724. iSearchSlot = LOADOUT_POSITION_MISC;
  725. }
  726. else if ( IsBuildingSlot( iSearchSlot ) )
  727. {
  728. iSearchSlot = LOADOUT_POSITION_BUILDING;
  729. }
  730. else if ( IsTauntSlot( iSearchSlot ) )
  731. {
  732. iSearchSlot = LOADOUT_POSITION_TAUNT;
  733. }
  734. }
  735. // To start with, generate a list of all potentially-useable items that we want to consider for
  736. // the UI. We'll strip this down based on duplicates/gameplay restrictions and sort it at the end.
  737. CUtlVector<CEconItemView*> vecItems;
  738. int iNumItems = TFInventoryManager()->GetAllUsableItemsForSlot( iClass, iSearchSlot, &vecItems );
  739. CEconItemView *pEquippedItem = NULL;
  740. typedef CUtlMap<RarityEconIdKey, CEquippableItemsForSlotGenerator::CEquippableResult> HighestLevelMap_t;
  741. HighestLevelMap_t mapHighestLevel;
  742. SetDefLessFunc( mapHighestLevel );
  743. // We also prevent items with different kill eater scores from stacking.
  744. static CSchemaAttributeDefHandle pAttrDef_KillEaterScore( "kill eater" );
  745. // Iterate over the list of all the items that we consider as potential display candidates.
  746. for ( int i = 0; i < iNumItems; i++ )
  747. {
  748. CEconItemView *pItem = vecItems[i];
  749. // Before doing any sort of culling, count the number of unique instances of this particular
  750. // definition.
  751. {
  752. const item_stack_type_t stackType( pItem->GetItemDefIndex(), pItem->GetQuality() );
  753. DuplicateCountMap_t::IndexType_t iIndex = m_DuplicateCountsMap.Find( stackType );
  754. if ( iIndex == m_DuplicateCountsMap.InvalidIndex() )
  755. {
  756. m_DuplicateCountsMap.Insert( stackType, 1 );
  757. }
  758. else
  759. {
  760. m_DuplicateCountsMap[ iIndex ]++;
  761. }
  762. }
  763. // Track whether this is our currently-equipped item.
  764. CEconItemView *pCurItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, iSlot );
  765. if ( pCurItemData && pCurItemData->GetItemID() && pCurItemData->GetItemID() == pItem->GetItemID() )
  766. {
  767. pEquippedItem = pItem;
  768. }
  769. // If this item conflicts with items we already have equipped, we note that so that it shows up
  770. // differently.
  771. CEquippableItemsForSlotGenerator::EItemDisplayType eDisplayType = kSlotDisplay_Normal;
  772. if ( pItem->GetItemDefinition()->GetEquipRegionMask() & unUsedEquipRegionMask )
  773. {
  774. eDisplayType = kSlotDisplay_Disabled_EquipRegionConflict;
  775. }
  776. // If we're listing *all* items, including duplicates, we just add everything to the list at once and
  777. // move on. We still do the above equipped-item specialcasing.
  778. if ( unFlags & kSlotGenerator_ShowDuplicates )
  779. {
  780. m_vecDisplayItems.AddToTail( CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
  781. continue;
  782. }
  783. // Has this item been modified by the user in some way? If so, always list.
  784. if ( ShouldItemNotStack( pItem ) )
  785. {
  786. m_vecDisplayItems.AddToTail( CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
  787. continue;
  788. }
  789. // Throw this item into the running map of the highest level item of this type we've seen so far.
  790. // "Of this type" means "has a matching rarity and item definition index", so uniques will be sorted
  791. // differently from unusuals, but both will show their highest-level item.
  792. //
  793. // This code does make the assumption that nothing in the key will be able to affect the display type.
  794. // (ie., a higher-level item will never be equippable where a lower-level item is not)
  795. {
  796. uint32 unKillEaterScore = 0;
  797. pItem->FindAttribute( pAttrDef_KillEaterScore, &unKillEaterScore );
  798. RarityEconIdKey keyEconId( pItem->GetItemQuality(), pItem->GetItemDefIndex(), unKillEaterScore );
  799. HighestLevelMap_t::IndexType_t iIndex = mapHighestLevel.Find( keyEconId );
  800. if ( iIndex == mapHighestLevel.InvalidIndex() || pItem->GetItemLevel() > mapHighestLevel[iIndex].m_pEconItemView->GetItemLevel() )
  801. {
  802. mapHighestLevel.InsertOrReplace( keyEconId, CEquippableItemsForSlotGenerator::CEquippableResult( pItem, eDisplayType ) );
  803. }
  804. }
  805. }
  806. // Take the resulting list of items we've force-added and items we've taken the highest-level representative
  807. // from and put them all into our unsorted display list.
  808. FOR_EACH_MAP( mapHighestLevel, i )
  809. {
  810. m_vecDisplayItems.AddToTail( mapHighestLevel[i] );
  811. }
  812. // Always leave a base item in the list. We don't have to worry about level changes or other weird overlaps
  813. // for base weapons. If we don't have an equipped item, this must be the equipped item.
  814. CEconItemView *pBaseItem = TFInventoryManager()->GetBaseItemForClass( iClass, iSlot );
  815. if ( pBaseItem && pBaseItem->IsValid() )
  816. {
  817. if ( !pEquippedItem )
  818. {
  819. pEquippedItem = pBaseItem;
  820. }
  821. m_vecDisplayItems.AddToTail( pBaseItem );
  822. }
  823. // If the equipped item is in the list, remove it if we're going to manually add it at the top of the display
  824. // list later. We add it to the list above and remove it here to make sure that we don't get weird effects
  825. // when the equipped item would be the highest level example of an item, etc.
  826. if ( pEquippedItem )
  827. {
  828. if ( unFlags & kSlotGenerator_EquippedSpecialHandling )
  829. {
  830. m_vecDisplayItems.FindAndFastRemove( pEquippedItem );
  831. m_pEquippedItemView = pEquippedItem;
  832. }
  833. // Special case adding our equipped item even if it doesn't meet the ordinary display criteria. It might
  834. // not be the highest level or rarity, but the user equipped it somehow so make sure it shows up.
  835. else if ( m_vecDisplayItems.Find( pEquippedItem ) == -1 )
  836. {
  837. m_vecDisplayItems.AddToTail( pEquippedItem );
  838. }
  839. }
  840. // Remove duplicates and sort to put it into the order they'll be displayed to the user.
  841. // Sort the items based on selection type
  842. int iSortType = tf_item_selection_panel_sort_type.GetInt();
  843. switch ( iSortType )
  844. {
  845. case 0: m_vecDisplayItems.Sort( &SortRarityEconIdKeysDate ); break;
  846. case 1: m_vecDisplayItems.Sort( &SortRarityEconIdKeysAlphabetical ); break;
  847. }
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose:
  851. //-----------------------------------------------------------------------------
  852. equip_region_mask_t GenerateEquipRegionConflictMask( int iClass, int iUpToSlot, int iIgnoreSlot )
  853. {
  854. Assert( iUpToSlot <= CLASS_LOADOUT_POSITION_COUNT );
  855. equip_region_mask_t unEquippedRegionMask = 0;
  856. for ( int i = 0; i < iUpToSlot; i++ )
  857. {
  858. if ( i == iIgnoreSlot )
  859. continue;
  860. const CEconItemView *pEquippedItemView = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
  861. if ( !pEquippedItemView )
  862. continue;
  863. unEquippedRegionMask |= pEquippedItemView->GetItemDefinition()->GetEquipRegionConflictMask();
  864. }
  865. return unEquippedRegionMask;
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose:
  869. //-----------------------------------------------------------------------------
  870. void CEquipSlotItemSelectionPanel::UpdateModelPanelsForSelection( void )
  871. {
  872. Assert( !DisplayOnlyAllowUniqueQualityCheckbox() );
  873. const bool bShowEquippedItemFirst = true;
  874. CEquippableItemsForSlotGenerator::EquippableResultsVec_t vecDisplayItems;
  875. const wchar_t* wscFilter = m_wNameFilter.Count() ? m_wNameFilter.Base() : NULL;
  876. // Generate a mask that is "every equip region we're using except for whatever is in this current slot" and use
  877. // that to generate a list of everything we could possibly equip for this current slot.
  878. const equip_region_mask_t unUsedEquipRegionMask = GenerateEquipRegionConflictMask( m_iClass, CLASS_LOADOUT_POSITION_COUNT, m_iSlot );
  879. CEquippableItemsForSlotGenerator equippableItems( m_iClass,
  880. m_iSlot,
  881. unUsedEquipRegionMask,
  882. bShowEquippedItemFirst ? CEquippableItemsForSlotGenerator::kSlotGenerator_EquippedSpecialHandling : CEquippableItemsForSlotGenerator::kSlotGenerator_None );
  883. if( m_bShowingEntireBackpack )
  884. {
  885. int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount();
  886. for ( int i = 1; i <= iNumItems; i++ )
  887. {
  888. CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i);
  889. if ( pItemData && pItemData->IsValid() )
  890. {
  891. if( !DoesItemPassSearchFilter( pItemData->GetDescription(), wscFilter ) )
  892. continue;
  893. CEquippableItemsForSlotGenerator::CEquippableResult& result = vecDisplayItems[ vecDisplayItems.AddToTail( pItemData ) ];
  894. result.m_eDisplayType = GetItemNotSelectableReason( pItemData ) ? CEquippableItemsForSlotGenerator::kSlotDisplay_Invalid : CEquippableItemsForSlotGenerator::kSlotDisplay_Normal;
  895. }
  896. }
  897. }
  898. else
  899. {
  900. // Copy the generated data back into our local structures.
  901. DeepCopyMap( equippableItems.GetDuplicateCountMap(), &m_DuplicateCounts );
  902. const CEquippableItemsForSlotGenerator::EquippableResultsVec_t& allDisplayItems = equippableItems.GetDisplayItems();
  903. for ( int i=0; i<allDisplayItems.Count(); ++i )
  904. {
  905. if ( DoesItemPassSearchFilter( allDisplayItems[i].m_pEconItemView->GetDescription(), wscFilter ) )
  906. {
  907. vecDisplayItems.AddToTail( allDisplayItems[i] );
  908. }
  909. }
  910. }
  911. CEconItemView *pEquippedItem = equippableItems.GetEquippedItem();
  912. if ( pEquippedItem )
  913. {
  914. if ( bShowEquippedItemFirst && DoesItemPassSearchFilter( pEquippedItem->GetDescription(), wscFilter ) )
  915. {
  916. vecDisplayItems.AddToHead( pEquippedItem );
  917. }
  918. m_iCurrentItemID = pEquippedItem->GetItemID();
  919. }
  920. // If the loadout slot is one that players can have empty, put a "nothing" entry at the end of the list.
  921. if ( !TFInventoryManager()->SlotContainsBaseItems( GEconItemSchema().GetEquipTypeFromClassIndex( m_iClass ), m_iSlot ) )
  922. {
  923. vecDisplayItems.AddToHead( NULL );
  924. }
  925. m_iItemsInSelection = vecDisplayItems.Count();
  926. // Make sure we're not on an invalid page.
  927. if ( GetCurrentPage() >= GetNumPages() )
  928. {
  929. SetCurrentPage( 0 );
  930. }
  931. int nOldSelection = GetFirstSelectedItemIndex( true );
  932. int nPageStart = GetCurrentPage() * GetNumSlotsPerPage();
  933. nOldSelection += nPageStart;
  934. static ConVarRef joystick( "joystick" );
  935. if ( joystick.IsValid() && joystick.GetBool() )
  936. {
  937. if( nOldSelection == -1 || nOldSelection >= vecDisplayItems.Count() )
  938. nOldSelection = nPageStart;
  939. }
  940. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  941. {
  942. int iItemIndex = i + nPageStart;
  943. // We only show the equipped state for the equipped items, below
  944. m_pItemModelPanels[i]->SetShowEquipped( false );
  945. m_pItemModelPanels[i]->SetShowGreyedOutTooltip( true );
  946. m_pItemModelPanels[i]->SetGreyedOut( NULL );
  947. m_pItemModelPanels[i]->SetNoItemText( "#SelectNoItemSlot" );
  948. bool bSelected = joystick.IsValid() && joystick.GetBool() && iItemIndex == nOldSelection;
  949. m_pItemModelPanels[i]->SetSelected( bSelected );
  950. m_pItemModelPanels[i]->SetShowQuantity( true );
  951. m_pItemModelPanels[i]->SetForceShowEquipped( false );
  952. bool bShowEquipped = false;
  953. if ( vecDisplayItems.Count() > iItemIndex )
  954. {
  955. const char* pszGreyOutReason = NULL;
  956. if( m_bShowingEntireBackpack )
  957. {
  958. pszGreyOutReason = GetItemNotSelectableReason( vecDisplayItems[iItemIndex].m_pEconItemView );
  959. }
  960. else
  961. {
  962. pszGreyOutReason = vecDisplayItems[iItemIndex].m_eDisplayType == CEquippableItemsForSlotGenerator::kSlotDisplay_Normal ? NULL : "#Econ_GreyOutReason_EquipRegionConflict";
  963. }
  964. // Show equipped state on base items too
  965. bShowEquipped = false;
  966. // Check if this item is already equipped, potentially in another slot
  967. if ( vecDisplayItems[iItemIndex].m_pEconItemView )
  968. {
  969. bShowEquipped |= vecDisplayItems[iItemIndex].m_pEconItemView->IsEquippedForClass( m_iClass );
  970. }
  971. // Check if this item is the currently equipped item
  972. if ( pEquippedItem )
  973. {
  974. bShowEquipped |= pEquippedItem == vecDisplayItems[iItemIndex].m_pEconItemView;
  975. }
  976. m_pItemModelPanels[i]->SetForceShowEquipped( bShowEquipped );
  977. m_pItemModelPanels[i]->SetItem( vecDisplayItems[iItemIndex].m_pEconItemView );
  978. m_pItemModelPanels[i]->SetGreyedOut( pszGreyOutReason );
  979. }
  980. else
  981. {
  982. m_pItemModelPanels[i]->SetItem( NULL );
  983. }
  984. SetBorderForItem( m_pItemModelPanels[i], false );
  985. }
  986. }
  987. //-----------------------------------------------------------------------------
  988. // Purpose:
  989. //-----------------------------------------------------------------------------
  990. const char *CEquipSlotItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
  991. {
  992. if ( !pItem )
  993. return NULL;
  994. CTFItemDefinition *pItemData = pItem->GetStaticData();
  995. if ( !pItemData->CanBeUsedByClass(m_iClass) )
  996. return "#Econ_GreyOutReason_CannotBeUsedByThisClass";
  997. extern bool AreSlotsConsideredIdentical( EEquipType_t eEquipType, int iBaseSlot, int iTestSlot );
  998. if ( !AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItemData->GetLoadoutSlot(m_iClass), m_iSlot ) )
  999. return "#Econ_GreyOutReason_CannotBeUsedInThisSlot";
  1000. // Should we gray out this item? This will happen if we're coming from the loadout and we have equip region
  1001. // conflicts of some kind.
  1002. const equip_region_mask_t unUsedEquipRegionMask = GenerateEquipRegionConflictMask( m_iClass, CLASS_LOADOUT_POSITION_COUNT, m_iSlot );
  1003. if ( pItemData->GetEquipRegionMask() & unUsedEquipRegionMask )
  1004. return "#Econ_GreyOutReason_EquipRegionConflict";
  1005. return NULL;
  1006. }
  1007. //-----------------------------------------------------------------------------
  1008. // Purpose:
  1009. //-----------------------------------------------------------------------------
  1010. void CEquipSlotItemSelectionPanel::OnBackPressed()
  1011. {
  1012. Assert( m_iCurrentItemID != INVALID_ITEM_DEF_INDEX );
  1013. PostMessageSelectionReturned( m_iCurrentItemID );
  1014. OnClose();
  1015. }
  1016. //-----------------------------------------------------------------------------
  1017. // Purpose:
  1018. //-----------------------------------------------------------------------------
  1019. void CItemSelectionPanel::OnTextChanged( KeyValues *data )
  1020. {
  1021. Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
  1022. vgui::TextEntry *pTextEntry = dynamic_cast<vgui::TextEntry *>( pPanel );
  1023. if ( pTextEntry )
  1024. {
  1025. if ( pTextEntry == m_pNameFilterTextEntry )
  1026. {
  1027. m_wNameFilter.RemoveAll();
  1028. if ( m_pNameFilterTextEntry->GetTextLength() )
  1029. {
  1030. m_wNameFilter.EnsureCount( m_pNameFilterTextEntry->GetTextLength() + 1 );
  1031. m_pNameFilterTextEntry->GetText( m_wNameFilter.Base(), m_wNameFilter.Count() * sizeof(wchar_t) );
  1032. V_wcslower( m_wNameFilter.Base() );
  1033. }
  1034. m_flFilterItemTime = gpGlobals->curtime + 0.5f;
  1035. return;
  1036. }
  1037. }
  1038. }
  1039. //=====================================================================================================================
  1040. // ITEM CRITERIA BASED SELECTION PANEL
  1041. //=====================================================================================================================
  1042. //-----------------------------------------------------------------------------
  1043. // Purpose:
  1044. //-----------------------------------------------------------------------------
  1045. CItemCriteriaSelectionPanel::CItemCriteriaSelectionPanel(Panel *parent, const CItemSelectionCriteria *pCriteria, itemid_t pExceptions[], int iNumExceptions ) : CItemSelectionPanel( parent )
  1046. {
  1047. m_pCriteria = pCriteria;
  1048. UpdateExceptions( pExceptions, iNumExceptions );
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose:
  1052. //-----------------------------------------------------------------------------
  1053. void CItemCriteriaSelectionPanel::UpdateExceptions( itemid_t pExceptions[], int iNumExceptions )
  1054. {
  1055. m_Exceptions.Purge();
  1056. for ( int i = 0; i < iNumExceptions; i++ )
  1057. {
  1058. m_Exceptions.AddToTail( pExceptions[i] );
  1059. }
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Purpose:
  1063. //-----------------------------------------------------------------------------
  1064. void CItemCriteriaSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  1065. {
  1066. BaseClass::ApplySchemeSettings( pScheme );
  1067. vgui::Label *pLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
  1068. if ( pLabel )
  1069. {
  1070. pLabel->SetVisible( false );
  1071. }
  1072. SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( "#Craft_SelectItemPanel" ) );
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Purpose:
  1076. //-----------------------------------------------------------------------------
  1077. bool CItemCriteriaSelectionPanel::ShouldItemPanelBeVisible( CItemModelPanel *pPanel, int iPanelIndex )
  1078. {
  1079. // First "Empty" panel is always visible.
  1080. return ( pPanel->HasItem() || iPanelIndex == 0 );
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. // Purpose:
  1084. //-----------------------------------------------------------------------------
  1085. void CItemCriteriaSelectionPanel::UpdateModelPanelsForSelection( void )
  1086. {
  1087. CUtlVector<CEconItemView*> vecDisplayItems;
  1088. CUtlVector<item_stack_type_t> vecDefsFound;
  1089. const wchar_t* wscFilter = m_wNameFilter.Count() ? m_wNameFilter.Base() : NULL;
  1090. int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount();
  1091. for ( int i = 1; i <= iNumItems; i++ )
  1092. {
  1093. CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i);
  1094. bool bAdd = false;
  1095. if ( pItemData && pItemData->IsValid() )
  1096. {
  1097. // If this is a valid item, we want to show this if we're showing our entire backpack
  1098. // or we doing the tailored list and this item matches.
  1099. if ( m_bShowingEntireBackpack || GetItemNotSelectableReason( pItemData ) == NULL )
  1100. {
  1101. // The item also needs to pass the text search filter as well
  1102. if( DoesItemPassSearchFilter( pItemData->GetDescription(), wscFilter ) )
  1103. {
  1104. bAdd = true;
  1105. }
  1106. }
  1107. }
  1108. // When showing our entire backpack we take everything so long as we arent filtering
  1109. else if( wscFilter == NULL && m_bShowingEntireBackpack )
  1110. {
  1111. bAdd = true;
  1112. }
  1113. if( bAdd )
  1114. {
  1115. // For actual items see if we should stack. Only do so if we're NOT showing
  1116. // the entire backpack
  1117. if( pItemData && !m_bShowingEntireBackpack )
  1118. {
  1119. // Has this item been modified by the user in some way? If so, always list,
  1120. // but don't add it to our duplicate count, or our "found indices" list.
  1121. item_definition_index_t iDefIndex = pItemData->GetItemDefIndex();
  1122. if ( ShouldItemNotStack( pItemData ) )
  1123. {
  1124. vecDisplayItems.AddToTail( pItemData );
  1125. continue;
  1126. }
  1127. item_stack_type_t stackType( iDefIndex, pItemData->GetQuality() );
  1128. int iIndex = m_DuplicateCounts.Find( stackType );
  1129. if ( iIndex == m_DuplicateCounts.InvalidIndex() )
  1130. {
  1131. m_DuplicateCounts.Insert( stackType, 1 );
  1132. }
  1133. else
  1134. {
  1135. m_DuplicateCounts[ iIndex ]++;
  1136. }
  1137. if ( vecDefsFound.Find( stackType ) != vecDefsFound.InvalidIndex() )
  1138. continue;
  1139. vecDefsFound.AddToTail( stackType );
  1140. }
  1141. vecDisplayItems.AddToTail( pItemData );
  1142. }
  1143. }
  1144. // Sort them alphabetically if not viewing entire backpack
  1145. if( !m_bShowingEntireBackpack )
  1146. {
  1147. vecDisplayItems.Sort( &SortRarityEconIdKeysAlphabetical_Views );
  1148. }
  1149. // Add an "Empty" item to the start
  1150. vecDisplayItems.AddToHead( NULL );
  1151. m_iItemsInSelection = vecDisplayItems.Count();
  1152. // Make sure we're not on an invalid page.
  1153. if ( GetCurrentPage() >= GetNumPages() )
  1154. {
  1155. SetCurrentPage( 0 );
  1156. }
  1157. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  1158. {
  1159. int iItemIndex = i + (GetCurrentPage() * GetNumSlotsPerPage());
  1160. if ( vecDisplayItems.Count() > iItemIndex )
  1161. {
  1162. m_pItemModelPanels[i]->SetItem( vecDisplayItems[iItemIndex] );
  1163. }
  1164. else
  1165. {
  1166. m_pItemModelPanels[i]->SetItem( NULL );
  1167. }
  1168. // We only show the equipped state for the equipped items, below
  1169. m_pItemModelPanels[i]->SetShowEquipped( true );
  1170. m_pItemModelPanels[i]->SetShowGreyedOutTooltip( true );
  1171. m_pItemModelPanels[i]->SetGreyedOut( GetItemNotSelectableReason( m_pItemModelPanels[i]->GetItem() ) );
  1172. m_pItemModelPanels[i]->SetNoItemText( "#SelectNoItemSlot" );
  1173. SetBorderForItem( m_pItemModelPanels[i], false );
  1174. }
  1175. }
  1176. //-----------------------------------------------------------------------------
  1177. // Purpose:
  1178. //-----------------------------------------------------------------------------
  1179. const char *CItemCriteriaSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
  1180. {
  1181. if ( !pItem )
  1182. return NULL;
  1183. // Ignore it if it's in our exceptions list
  1184. if ( m_Exceptions.Find( pItem->GetItemID() ) != m_Exceptions.InvalidIndex() )
  1185. return "";
  1186. // Matching all items?
  1187. if ( !m_pCriteria )
  1188. return NULL;
  1189. CTFItemDefinition *pItemData = pItem->GetStaticData();
  1190. return m_pCriteria->BEvaluate( pItemData ) ? NULL : "";
  1191. }
  1192. //=====================================================================================================================
  1193. // CRAFTING SELECTION PANEL
  1194. //=====================================================================================================================
  1195. //-----------------------------------------------------------------------------
  1196. // Purpose:
  1197. //-----------------------------------------------------------------------------
  1198. CCraftingItemSelectionPanel::CCraftingItemSelectionPanel(Panel *parent )
  1199. : CItemCriteriaSelectionPanel( parent, NULL, NULL, 0 )
  1200. {
  1201. }
  1202. //-----------------------------------------------------------------------------
  1203. // Purpose:
  1204. //-----------------------------------------------------------------------------
  1205. void CCraftingItemSelectionPanel::UpdateOnShow( const CItemSelectionCriteria *pCriteria, bool bForceBackpack, itemid_t pExceptions[], int iNumExceptions )
  1206. {
  1207. m_pCriteria = pCriteria;
  1208. UpdateExceptions( pExceptions, iNumExceptions );
  1209. if ( m_bShowingEntireBackpack != bForceBackpack )
  1210. {
  1211. SetCurrentPage( 0 );
  1212. m_bShowingEntireBackpack = bForceBackpack;
  1213. m_bReapplyItemKVs = true;
  1214. }
  1215. m_bForceBackpack = bForceBackpack;
  1216. if ( !m_bForceBackpack )
  1217. {
  1218. SetCurrentPage( 0 );
  1219. }
  1220. UpdateModelPanels();
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose:
  1224. //-----------------------------------------------------------------------------
  1225. const char *CCraftingItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
  1226. {
  1227. if ( !pItem )
  1228. return NULL;
  1229. // Must not be marked no-craft
  1230. if ( !pItem->IsUsableInCrafting() )
  1231. return "#Econ_GreyOutReason_ItemNotCraftable";
  1232. // Are we filtering out items of non-unique quality that we might not want to accidentally craft?
  1233. if ( m_pOnlyAllowUniqueQuality && m_pOnlyAllowUniqueQuality->IsSelected() && pItem->GetQuality() != AE_UNIQUE )
  1234. return "#Econ_GreyOutReason_ItemSpecialQuality";
  1235. return BaseClass::GetItemNotSelectableReason( pItem );
  1236. }
  1237. //=====================================================================================================================
  1238. // ACCOUNT SLOT ITEM SELECTION PANEL
  1239. //=====================================================================================================================
  1240. //-----------------------------------------------------------------------------
  1241. // Purpose:
  1242. //-----------------------------------------------------------------------------
  1243. CAccountSlotItemSelectionPanel::CAccountSlotItemSelectionPanel( Panel *pParent, int iSlot, const char *pszTitleToken )
  1244. : CEquipSlotItemSelectionPanel( pParent, GEconItemSchema().GetAccountIndex(), iSlot )
  1245. , m_pszTitleToken( pszTitleToken )
  1246. {}
  1247. //-----------------------------------------------------------------------------
  1248. // Purpose:
  1249. //-----------------------------------------------------------------------------
  1250. void CAccountSlotItemSelectionPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  1251. {
  1252. BaseClass::BaseClass::ApplySchemeSettings( pScheme );
  1253. m_pWeaponLabel = dynamic_cast<vgui::Label*>( FindChildByName("ItemSlotLabel") );
  1254. if ( m_pWeaponLabel )
  1255. {
  1256. m_pWeaponLabel->SetVisible( false );
  1257. }
  1258. SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( m_pszTitleToken ) );
  1259. }
  1260. //-----------------------------------------------------------------------------
  1261. // Purpose:
  1262. //-----------------------------------------------------------------------------
  1263. const char *CAccountSlotItemSelectionPanel::GetItemNotSelectableReason( const CEconItemView *pItem ) const
  1264. {
  1265. if ( !pItem )
  1266. return NULL;
  1267. CTFItemDefinition *pItemData = pItem->GetStaticData();
  1268. if ( pItemData->GetEquipType() != EEquipType_t::EQUIP_TYPE_ACCOUNT )
  1269. return "#Econ_GreyOutReason_CannotBeUsedByThisClass";
  1270. extern bool AreSlotsConsideredIdentical( EEquipType_t eEquipType, int iBaseSlot, int iTestSlot );
  1271. if ( !AreSlotsConsideredIdentical( pItem->GetStaticData()->GetEquipType(), pItemData->GetLoadoutSlot(m_iClass), m_iSlot ) )
  1272. return "#Econ_GreyOutReason_CannotBeUsedInThisSlot";
  1273. return NULL;
  1274. }