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.

2013 lines
70 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "crafting_panel.h"
  8. #include "dynamic_recipe_subpanel.h"
  9. #include "vgui/ISurface.h"
  10. #include "vgui/ISystem.h"
  11. #include "c_tf_player.h"
  12. #include "gamestringpool.h"
  13. #include "iclientmode.h"
  14. #include "tf_item_inventory.h"
  15. #include "ienginevgui.h"
  16. #include <vgui/ILocalize.h>
  17. #include "vgui_controls/TextImage.h"
  18. #include "vgui_controls/CheckButton.h"
  19. #include "vgui_controls/ComboBox.h"
  20. #include <vgui_controls/TextEntry.h>
  21. #include "vgui/IInput.h"
  22. #include "gcsdk/gcclient.h"
  23. #include "gcsdk/gcclientjob.h"
  24. #include "character_info_panel.h"
  25. #include "charinfo_loadout_subpanel.h"
  26. #include "econ_item_system.h"
  27. #include "econ_item_constants.h"
  28. #include "tf_hud_notification_panel.h"
  29. #include "tf_hud_chat.h"
  30. #include "c_tf_gamestats.h"
  31. #include "confirm_dialog.h"
  32. #include "econ_notifications.h"
  33. #include "gc_clientsystem.h"
  34. #include "charinfo_loadout_subpanel.h"
  35. #include "item_selection_criteria.h"
  36. #include "rtime.h"
  37. #include "c_tf_freeaccount.h"
  38. #include "econ_dynamic_recipe.h"
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include <tier0/memdbgon.h>
  41. static CDynamicRecipePanel* g_DynamicRecipePanel = NULL;
  42. extern const char *g_szItemBorders[AE_MAX_TYPES][5];
  43. //-----------------------------------------------------------------------------
  44. // Purpose: Default to NULL item
  45. //-----------------------------------------------------------------------------
  46. CRecipeComponentItemModelPanel::CRecipeComponentItemModelPanel( vgui::Panel *parent, const char *name )
  47. : CItemModelPanel( parent, name )
  48. , m_nPageNumber( 0 )
  49. {
  50. SetItem( NULL );
  51. }
  52. //-----------------------------------------------------------------------------
  53. // Purpose: Add recipe to our list of recipes. Each call to this function
  54. // effectively adds an item to the next page
  55. //-----------------------------------------------------------------------------
  56. void CRecipeComponentItemModelPanel::AddRecipe( itemid_t nRecipe )
  57. {
  58. RecipeItem_t& recipeItem = m_vecRecipes[m_vecRecipes.AddToTail()];
  59. recipeItem.m_nRecipeIndex = nRecipe;
  60. UpdateRecipeItem( &recipeItem );
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Purpose: Wipe all recipes from all pages
  64. //-----------------------------------------------------------------------------
  65. void CRecipeComponentItemModelPanel::DeleteRecipes()
  66. {
  67. m_vecDefaultItems.Purge();
  68. m_vecRecipes.Purge();
  69. SetItem( NULL );
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Override to set the item to be our default item if NULL is passed in.
  73. // Also handles greying out the panels
  74. //-----------------------------------------------------------------------------
  75. void CRecipeComponentItemModelPanel::SetItem( const CEconItemView *pItem )
  76. {
  77. // Use the default item if they set NULL
  78. if( pItem == NULL )
  79. {
  80. SetBlankState();
  81. }
  82. else
  83. {
  84. BaseClass::SetItem( pItem );
  85. SetGreyedOut( NULL );
  86. }
  87. InvalidateLayout( true );
  88. }
  89. void CRecipeComponentItemModelPanel::SetBlankState()
  90. {
  91. CEconItemView* pDefaultItem = NULL;
  92. if( m_nPageNumber < m_vecDefaultItems.Count() )
  93. pDefaultItem = m_vecDefaultItems[ m_nPageNumber ];
  94. BaseClass::SetItem( pDefaultItem );
  95. SetGreyedOut( "" );
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Move a recipe item to a specific page
  99. //-----------------------------------------------------------------------------
  100. void CRecipeComponentItemModelPanel::SetRecipeItem( itemid_t nRecipeItem, int nPageNumber )
  101. {
  102. Assert( nPageNumber < m_vecRecipes.Count() );
  103. m_vecRecipes[ nPageNumber ].m_nRecipeIndex = nRecipeItem;
  104. UpdateRecipeItem( &m_vecRecipes[ nPageNumber ] );
  105. // Use the item that this item ID maps to
  106. SetItem( m_vecRecipes[ nPageNumber ].m_pRecipeItem );
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Add a default item to a page
  110. //-----------------------------------------------------------------------------
  111. void CRecipeComponentItemModelPanel::AddDefaultItem( CEconItemView *pItem )
  112. {
  113. m_vecDefaultItems[ m_vecDefaultItems.AddToTail() ] = pItem;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Get a recipe item on a given page
  117. //-----------------------------------------------------------------------------
  118. CEconItemView* CRecipeComponentItemModelPanel::GetRecipeItem( int nPageNumber ) const
  119. {
  120. if( nPageNumber < m_vecRecipes.Count() )
  121. {
  122. return m_vecRecipes[nPageNumber].m_pRecipeItem;
  123. }
  124. return NULL;
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Get the recipe index from a specific page
  128. //-----------------------------------------------------------------------------
  129. itemid_t CRecipeComponentItemModelPanel::GetRecipeIndex( int nPageNumber ) const
  130. {
  131. if( nPageNumber < m_vecRecipes.Count() )
  132. {
  133. return m_vecRecipes[nPageNumber].m_nRecipeIndex;
  134. }
  135. return INVALID_ITEM_ID;
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Purpose: Iterate through all attributes on a item and turn recipe attributes
  139. // into input and output items. Store those items in vectors for inputs
  140. // and outputs. Inputs are sorted from least common to most common
  141. // so that the later pages are filled with more repeats than the early pages
  142. //-----------------------------------------------------------------------------
  143. bool CDynamicRecipePanel::CRecipeComponentAttributeCounter::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
  144. {
  145. static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
  146. Assert( pAttrib_CannotTrade );
  147. unsigned nCount = value.num_required() - value.num_fulfilled();
  148. if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
  149. {
  150. CEconItem* pItem = m_vecTempEconItems[m_vecTempEconItems.AddToTail( new CEconItem() )];
  151. DecodeItemFromEncodedAttributeString( value, pItem );
  152. for( unsigned i=0; i < nCount; ++i )
  153. {
  154. CEconItemView& item = m_vecOutputItems[ m_vecOutputItems.AddToTail() ];
  155. item.SetItemDefIndex( pItem->GetDefinitionIndex() );
  156. item.SetItemQuality( pItem->GetQuality() );
  157. item.SetItemLevel( pItem->GetItemLevel() );
  158. item.SetItemID( pItem->GetItemID() );
  159. item.SetNonSOEconItem( pItem ); // Set the item into the econ item view.
  160. item.SetInitialized( true );
  161. item.SetItemOriginOverride( kEconItemOrigin_RecipeOutput ); // Spoof where we came from
  162. // Set the untradable flag if the attribute says so
  163. if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_UNTRADABLE )
  164. {
  165. item.GetAttributeList()->SetRuntimeAttributeValue( pAttrib_CannotTrade, 0.0f ); // value doesn't matter -- we only check for presence/absence
  166. }
  167. }
  168. }
  169. else
  170. {
  171. m_nInputCount += nCount;
  172. CEconItem* pItem = m_vecTempEconItems[m_vecTempEconItems.AddToTail( new CEconItem() )];
  173. DecodeItemFromEncodedAttributeString( value, pItem );
  174. CUtlVector<InputComponent_t>& inputSeries = m_vecInputItems[ m_vecInputItems.AddToTail() ];
  175. for( unsigned i=0; i < nCount; ++i )
  176. {
  177. InputComponent_t& item = inputSeries[ inputSeries.AddToTail() ];
  178. item.m_ItemView.SetItemDefIndex( pItem->GetDefinitionIndex() );
  179. item.m_ItemView.SetItemQuality( pItem->GetQuality() );
  180. item.m_ItemView.SetItemLevel( 0 );
  181. item.m_ItemView.SetItemID( pItem->GetItemID() );
  182. item.m_ItemView.SetNonSOEconItem( pItem ); // Set the item into the econ item view.
  183. item.m_ItemView.SetInitialized( true );
  184. item.m_ItemView.SetItemOriginOverride( kEconItemOrigin_RecipeOutput ); // Spoof where we came from
  185. item.m_pAttrib = pAttrDef;
  186. }
  187. m_vecInputItems.Sort( LeastCommonInputSortFunc );
  188. }
  189. return true;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Get the output item on a given page
  193. //-----------------------------------------------------------------------------
  194. CEconItemView* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetOutputItem( int i )
  195. {
  196. if( i >= 0 && i < m_vecOutputItems.Count() )
  197. return &m_vecOutputItems[i];
  198. return NULL;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Get the input item on a given page
  202. //-----------------------------------------------------------------------------
  203. CEconItemView* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputItem( int i )
  204. {
  205. InputComponent_t* pInputComponent = GetInputComponent( i );
  206. if( pInputComponent )
  207. {
  208. return &pInputComponent->m_ItemView;
  209. }
  210. return NULL;
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose: Get the attribute that the item in a given panel maps to
  214. //-----------------------------------------------------------------------------
  215. const CEconItemAttributeDefinition* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputAttrib( int i )
  216. {
  217. InputComponent_t* pInputComponent = GetInputComponent( i );
  218. if( pInputComponent )
  219. {
  220. return pInputComponent->m_pAttrib;
  221. }
  222. return NULL;
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Sort input vectors based on count. Fewer first.
  226. //-----------------------------------------------------------------------------
  227. int CDynamicRecipePanel::CRecipeComponentAttributeCounter::LeastCommonInputSortFunc( const CCopyableUtlVector<InputComponent_t> *p1, const CCopyableUtlVector<InputComponent_t> *p2 )
  228. {
  229. return p1->Count() > p2->Count();
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Find input component i from our 2D vector of input items
  233. //-----------------------------------------------------------------------------
  234. CDynamicRecipePanel::CRecipeComponentAttributeCounter::InputComponent_t* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputComponent( int i )
  235. {
  236. int nAccum = 0;
  237. FOR_EACH_VEC( m_vecInputItems, nIndex )
  238. {
  239. int nCount = m_vecInputItems[ nIndex ].Count();
  240. if( i < nAccum + nCount )
  241. {
  242. return &m_vecInputItems[ nIndex ][ i - nAccum ];
  243. }
  244. nAccum += nCount;
  245. }
  246. return NULL;
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Reset all data in the iterator
  250. //-----------------------------------------------------------------------------
  251. void CDynamicRecipePanel::CRecipeComponentAttributeCounter::Reset()
  252. {
  253. m_vecInputItems.Purge();
  254. m_vecOutputItems.Purge();
  255. m_vecTempEconItems.PurgeAndDeleteElements();
  256. m_nInputCount = 0;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose: Compare Check if m_pItemToMatch passes the criteria of any of the
  260. // attributes on m_pSourceItem.
  261. //-----------------------------------------------------------------------------
  262. bool CDynamicRecipePanel::CDynamicRecipeItemMatchFind::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
  263. {
  264. // Can't match ourself
  265. if( m_pSourceItem && m_pItemToMatch && m_pSourceItem->GetID() == m_pItemToMatch->GetID() )
  266. return true;
  267. if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
  268. return true;
  269. if( !DefinedItemAttribMatch( value, m_pItemToMatch ) )
  270. return true;
  271. // Must be useable in crafting. Expensive -- do this last.
  272. if( !m_pItemToMatch || !m_pItemToMatch->IsUsableInCrafting() )
  273. return true;
  274. // A match!
  275. m_bMatchesAny = true;
  276. return false;
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose: Delete all recipe data
  280. //-----------------------------------------------------------------------------
  281. void CInputPanelItemModelPanel::DeleteRecipes()
  282. {
  283. CRecipeComponentItemModelPanel::DeleteRecipes();
  284. m_vecAttrDef.Purge();
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Add component info to a new page
  288. //-----------------------------------------------------------------------------
  289. void CInputPanelItemModelPanel::AddComponentInfo( const CEconItemAttributeDefinition *pComponentAttrib )
  290. {
  291. m_vecAttrDef[ m_vecAttrDef.AddToTail() ] = pComponentAttrib;
  292. AddRecipe( NULL );
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: Check if a passed in database item matches the desired item in this
  296. // item panel. Default to the current page
  297. //-----------------------------------------------------------------------------
  298. bool CInputPanelItemModelPanel::MatchesAttribCriteria( itemid_t itemID ) const
  299. {
  300. return MatchesAttribCriteria( itemID, m_nPageNumber );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Check if a passed in database item matches the desired item in this
  304. // item panel on the specified page.
  305. //-----------------------------------------------------------------------------
  306. bool CInputPanelItemModelPanel::MatchesAttribCriteria( itemid_t itemID, int nPageNumber ) const
  307. {
  308. if( !m_pDynamicRecipeItem )
  309. return false;
  310. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  311. if ( !pLocalInv )
  312. return false;
  313. const CEconItemView* pItem = pLocalInv->GetInventoryItemByItemID( itemID );
  314. if( !pItem || !pItem->IsUsableInCrafting() )
  315. return false;
  316. const CEconItemAttributeDefinition* pAttrDef = GetAttrib( nPageNumber );
  317. CAttribute_DynamicRecipeComponent attribValue;
  318. if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) )
  319. {
  320. return DefinedItemAttribMatch( attribValue, pItem );
  321. }
  322. return false;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Get the attribute that this panel represents
  326. //-----------------------------------------------------------------------------
  327. const CEconItemAttributeDefinition* CInputPanelItemModelPanel::GetAttrib( int nPageNumber ) const
  328. {
  329. if( nPageNumber >= 0 && nPageNumber < m_vecAttrDef.Count() )
  330. return m_vecAttrDef[ nPageNumber ];
  331. return NULL;
  332. }
  333. void CInputPanelItemModelPanel::SetBlankState()
  334. {
  335. // Get the default item
  336. CEconItemView* pDefaultItem = NULL;
  337. if( m_nPageNumber < m_vecDefaultItems.Count() )
  338. pDefaultItem = m_vecDefaultItems[ m_nPageNumber ];
  339. // Grey out
  340. SetGreyedOut( "" );
  341. // Check for the "item name text override" attribute on the default item
  342. static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
  343. CAttribute_String attrItemNameTextOverride;
  344. if ( pDefaultItem )
  345. {
  346. pDefaultItem->FindAttribute( pAttrDef_ItemNameTextOverride, &attrItemNameTextOverride );
  347. if ( FStrEq( attrItemNameTextOverride.value().c_str(), "#TF_ItemName_Item" ) )
  348. {
  349. // This is a dummy item. Dont display an icon. Just the name of the item
  350. CItemModelPanel::SetItem( NULL );
  351. SetAttribOnly( true );
  352. SetTextYPos( 0 );
  353. //SetNoItemText( pDefaultItem->GetItemName(), NULL, NULL );
  354. const wchar_t *pszItemname = pDefaultItem->GetItemName();
  355. if ( V_wcscmp( pszItemname, g_pVGuiLocalize->Find( "#TF_ItemName_Item" ) ) == 0 && pDefaultItem->GetQuality() == AE_PAINTKITWEAPON )
  356. {
  357. SetNoItemText( g_pVGuiLocalize->Find( "paintkitweapon" ), NULL, NULL );
  358. }
  359. else
  360. {
  361. SetNoItemText( pDefaultItem->GetItemName(), NULL, NULL );
  362. }
  363. }
  364. else
  365. {
  366. BaseClass::SetItem( pDefaultItem );
  367. }
  368. }
  369. else
  370. {
  371. // Construct an item from the attribute that describes this input
  372. CEconItem tempItem;
  373. const CEconItemAttributeDefinition* pAttrDef = GetAttrib( m_nPageNumber );
  374. CAttribute_DynamicRecipeComponent attribValue;
  375. if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) )
  376. {
  377. DecodeItemFromEncodedAttributeString( attribValue, &tempItem );
  378. }
  379. // Shove it into an econitemview
  380. CEconItemView tempView;
  381. tempView.Init( tempItem.GetItemDefIndex(), tempItem.GetQuality(), 0 );
  382. tempView.SetNonSOEconItem( &tempItem );
  383. // Set its name as the text for this item model panel
  384. CItemModelPanel::SetItem( NULL );
  385. SetAttribOnly( true );
  386. SetTextYPos( 0 );
  387. SetNoItemText( tempView.GetItemName(), NULL, NULL );
  388. }
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose: Is this in play
  392. //-----------------------------------------------------------------------------
  393. bool CRecipeComponentItemModelPanel::IsSlotAvailable( int nPageNumber )
  394. {
  395. return nPageNumber < m_vecRecipes.Count();
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose: Sets the passed in recipe item and changes the item we show
  399. //-----------------------------------------------------------------------------
  400. void CRecipeComponentItemModelPanel::UpdateRecipeItem( RecipeItem_t* pRecipeItem )
  401. {
  402. Assert( pRecipeItem );
  403. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  404. if ( pLocalInv == NULL )
  405. return;
  406. pRecipeItem->m_pRecipeItem = pLocalInv->GetInventoryItemByItemID( pRecipeItem->m_nRecipeIndex );
  407. SetItem( pRecipeItem->m_pRecipeItem );
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose: Update the item we show for the current page
  411. //-----------------------------------------------------------------------------
  412. void CRecipeComponentItemModelPanel::UpdateDisplayItem()
  413. {
  414. if( m_nPageNumber < m_vecRecipes.Count() )
  415. {
  416. UpdateRecipeItem( &m_vecRecipes[ m_nPageNumber ] );
  417. }
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: Set the current page
  421. //-----------------------------------------------------------------------------
  422. void CRecipeComponentItemModelPanel::SetPageNumber( int nPageNumber )
  423. {
  424. Assert( nPageNumber >= 0 );
  425. m_nPageNumber = nPageNumber;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose:
  429. //-----------------------------------------------------------------------------
  430. CDynamicRecipePanel::CDynamicRecipePanel( vgui::Panel *parent, const char *panelName, CEconItemView* pRecipeItem )
  431. : CBackpackPanel( parent, panelName )
  432. , m_pDynamicRecipeItem( pRecipeItem )
  433. , m_pRecipeCraftButton( NULL )
  434. , m_nNumRecipeItems( 0 )
  435. , m_bAllRecipePanelsFilled( false )
  436. , m_bInputPanelsDirty( false )
  437. , m_nInputPage( 0 )
  438. , m_nOutputPage( 0 )
  439. , m_pCurInputPageLabel( NULL )
  440. , m_pNextInputPageButton( NULL )
  441. , m_pPrevInputPageButton( NULL )
  442. , m_flAbortCraftingAt( 0 )
  443. , m_pMouseOverItemPanel( NULL )
  444. , m_pNoMatchesLabel( NULL )
  445. , m_pUntradableOutputsLabel( NULL )
  446. , m_bShowUntradable( false )
  447. {
  448. g_DynamicRecipePanel = this;
  449. m_pRecipeContainer = new vgui::EditablePanel( this, "recipecontainer" );
  450. m_pInventoryContainer = new vgui::EditablePanel( this, "inventorycontainer" );
  451. m_pShowUntradableItemsCheckbox = new vgui::CheckButton( m_pInventoryContainer, "untradablecheckbox", "#Dynamic_Recipe_Untradable_Checkbox" );
  452. m_pShowUntradableItemsCheckbox->AddActionSignalTarget( this );
  453. m_pInputsLabel = new CExLabel( m_pRecipeContainer, "InputLabel", "#Craft_Recipe_Inputs" );
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. CDynamicRecipePanel::~CDynamicRecipePanel( void )
  459. {
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose: Get all our controls
  463. //-----------------------------------------------------------------------------
  464. void CDynamicRecipePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  465. {
  466. LoadControlSettings( GetResFile() );
  467. // This calls AddNewItemPanel
  468. BaseClass::ApplySchemeSettings( pScheme );
  469. CExButton* pCancelButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("CancelButton") );
  470. if ( pCancelButton )
  471. pCancelButton->AddActionSignalTarget( this );
  472. m_pRecipeCraftButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName("CraftButton") );
  473. if ( m_pRecipeCraftButton )
  474. m_pRecipeCraftButton->AddActionSignalTarget( this );
  475. m_pNextPageButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("NextPageButton") );
  476. if( m_pNextPageButton )
  477. m_pNextPageButton->AddActionSignalTarget( this );
  478. m_pPrevPageButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("PrevPageButton") );
  479. if( m_pPrevPageButton )
  480. m_pPrevPageButton->AddActionSignalTarget( this );
  481. m_pCurPageLabel = dynamic_cast<vgui::Label*>( m_pInventoryContainer->FindChildByName("CurPageLabel") );
  482. Assert( m_pCurPageLabel );
  483. m_pNoMatchesLabel = dynamic_cast<CExLabel*>( m_pInventoryContainer->FindChildByName( "NoMatches" ) );
  484. Assert( m_pNoMatchesLabel );
  485. m_pUntradableOutputsLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "UntradableLabel" ) );
  486. Assert( m_pUntradableOutputsLabel );
  487. m_pOutputsLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "OutputLabel" ) );
  488. Assert( m_pOutputsLabel );
  489. m_pCurInputPageLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "CurInputPageLabel" ) );
  490. m_pNextInputPageButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName( "NextInputPageButton" ) );
  491. if( m_pNextInputPageButton )
  492. m_pNextInputPageButton->AddActionSignalTarget( this );
  493. m_pPrevInputPageButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName( "PrevInputPageButton" ) );
  494. if( m_pPrevInputPageButton )
  495. m_pPrevInputPageButton->AddActionSignalTarget( this );
  496. #ifdef STAGING_ONLY
  497. m_pDevGiveInputsButton = new CExButton( m_pInventoryContainer, "dev_giveinputsbutton", "[Debug] Give Inputs", this, "dev_giveinputs" );
  498. m_pDevGiveInputsButton->SetEnabled( true );
  499. m_pDevGiveInputsButton->SetVisible( true );
  500. #endif
  501. InvalidateLayout();
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose:
  505. //-----------------------------------------------------------------------------
  506. void CDynamicRecipePanel::ApplySettings( KeyValues *inResourceData )
  507. {
  508. BaseClass::ApplySettings( inResourceData );
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: Update all the item panels and page buttons and labels
  512. //-----------------------------------------------------------------------------
  513. void CDynamicRecipePanel::PerformLayout( void )
  514. {
  515. BaseClass::PerformLayout();
  516. #ifdef STAGING_ONLY
  517. if( m_pDevGiveInputsButton )
  518. {
  519. m_pDevGiveInputsButton->SetPos( XRES(0) , YRES(290) );
  520. m_pDevGiveInputsButton->SetWide( 210 );
  521. m_pDevGiveInputsButton->SetTall( 40 );
  522. m_pDevGiveInputsButton->MoveToFront();
  523. m_pDevGiveInputsButton->SetEnabled( true );
  524. m_pDevGiveInputsButton->SetVisible( true );
  525. }
  526. #endif
  527. if( m_pSortByComboBox )
  528. {
  529. m_pSortByComboBox->SetVisible( false );
  530. }
  531. UpdateModelPanels();
  532. bool bNoMatches = m_nNumRecipeItems == 0;
  533. bool bMultiplePagesOfMatches = m_nNumRecipeItems > (unsigned)GetNumBackpackPanelsPerPage();
  534. // Some panels show and hide based on the number of matches
  535. if( m_pNoMatchesLabel)
  536. m_pNoMatchesLabel->SetVisible( bNoMatches );
  537. if( m_pNextPageButton )
  538. m_pNextPageButton->SetVisible( bMultiplePagesOfMatches );
  539. if( m_pPrevPageButton )
  540. m_pPrevPageButton->SetVisible( bMultiplePagesOfMatches );
  541. if( m_pCurPageLabel )
  542. m_pCurPageLabel->SetVisible( bMultiplePagesOfMatches );
  543. bool bMultiplePagesOfInputs = m_RecipeIterator.GetInputCount() > GetNumInputPanelsPerPage();
  544. if( m_pNextInputPageButton )
  545. m_pNextInputPageButton->SetVisible( bMultiplePagesOfInputs );
  546. if( m_pNextInputPageButton )
  547. m_pNextInputPageButton->SetEnabled( m_nInputPage < GetNumInputPages() - 1 );
  548. if( m_pCurInputPageLabel )
  549. m_pCurInputPageLabel->SetVisible( bMultiplePagesOfInputs );
  550. if( m_pPrevInputPageButton )
  551. m_pPrevInputPageButton->SetVisible( bMultiplePagesOfInputs );
  552. if( m_pPrevInputPageButton )
  553. m_pPrevInputPageButton->SetEnabled( m_nInputPage > 0 );
  554. if( m_pUntradableOutputsLabel )
  555. m_pUntradableOutputsLabel->SetVisible( m_pDynamicRecipeItem ? !m_pDynamicRecipeItem->IsTradable() : false );
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose: Handle commands
  559. //-----------------------------------------------------------------------------
  560. void CDynamicRecipePanel::OnCommand( const char *command )
  561. {
  562. if ( !Q_strnicmp( command, "back", 4 ) )
  563. {
  564. PostMessage( GetParent(), new KeyValues("CraftingClosed") );
  565. return;
  566. }
  567. else if ( !Q_strnicmp( command, "craft", 5 ) )
  568. {
  569. // Check if we should warn about partial completion
  570. if( WarnAboutPartialCompletion() )
  571. {
  572. if( CheckForUntradableItems() )
  573. {
  574. Craft();
  575. }
  576. }
  577. return;
  578. }
  579. else if ( !Q_stricmp( command, "reloadscheme" ) )
  580. {
  581. InvalidateLayout( true, true ); // deliberatly fallthrough to baseclass
  582. }
  583. else if( !Q_stricmp( command, "cancel" ) )
  584. {
  585. SetVisible( false );
  586. return;
  587. }
  588. else if ( !Q_strnicmp( command, "nextpage", 8 ) )
  589. {
  590. InvalidateLayout(); // deliberatly fallthrough to baseclass
  591. }
  592. else if ( !Q_strnicmp( command, "prevpage", 8 ) )
  593. {
  594. InvalidateLayout(); // deliberatly fallthrough to baseclass
  595. }
  596. else if( !Q_strnicmp( command, "nextinputpage", 13 ) )
  597. {
  598. if( m_nInputPage < GetNumInputPages() )
  599. m_nInputPage++;
  600. InvalidateLayout();
  601. return;
  602. }
  603. else if( !Q_strnicmp( command, "previnputpage", 13 ) )
  604. {
  605. if( m_nInputPage > 0 )
  606. m_nInputPage--;
  607. InvalidateLayout();
  608. return;
  609. }
  610. else if( !Q_strnicmp( command, "deleteitem", 10 ) ||
  611. !Q_strnicmp( command, "useitem", 7 ) )
  612. {
  613. // Gobble up these commands
  614. return;
  615. }
  616. #ifdef STAGING_ONLY
  617. else if( !Q_strnicmp( command, "dev_giveinputs", 14 ) )
  618. {
  619. Debug_GiveRequiredInputs();
  620. if( m_pDevGiveInputsButton )
  621. m_pDevGiveInputsButton->SetEnabled( false );
  622. }
  623. #endif
  624. BaseClass::OnCommand( command );
  625. }
  626. //-----------------------------------------------------------------------------
  627. // Purpose: Gobble up all keyboard input for now!
  628. //-----------------------------------------------------------------------------
  629. void CDynamicRecipePanel::OnKeyCodePressed( vgui::KeyCode /*code*/ )
  630. {
  631. return;
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. void CDynamicRecipePanel::OnButtonChecked( KeyValues *pData )
  637. {
  638. Panel *pPanel = reinterpret_cast<vgui::Panel *>( pData->GetPtr("panel") );
  639. if ( m_pShowUntradableItemsCheckbox == pPanel )
  640. {
  641. if ( m_bShowUntradable != m_pShowUntradableItemsCheckbox->IsSelected() )
  642. {
  643. m_bShowUntradable = m_pShowUntradableItemsCheckbox->IsSelected();
  644. InitItemPanels();
  645. UpdateModelPanels();
  646. }
  647. }
  648. }
  649. int CDynamicRecipePanel::GetNumItemPanels( void )
  650. {
  651. return DYNAMIC_RECIPE_INPUT_COUNT + DYNAMIC_RECIPE_OUTPUT_COUNT + DYNAMIC_RECIPE_PACKPACK_COUNT_PER_PAGE;
  652. }
  653. //-----------------------------------------------------------------------------
  654. // Purpose: Check to see that each and every input panel has a recipe item in it
  655. //-----------------------------------------------------------------------------
  656. bool CDynamicRecipePanel::AllRecipePanelsFilled( void )
  657. {
  658. // Need to recalculate
  659. if( m_bInputPanelsDirty )
  660. {
  661. // Assume all filled, and go through and try to find one that's empty
  662. m_bAllRecipePanelsFilled = true;
  663. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  664. {
  665. CInputPanelItemModelPanel* pInputPanel = m_vecRecipeInputModelPanels[i];
  666. for( int j=0; j < GetNumInputPages(); ++j )
  667. {
  668. if( pInputPanel->GetRecipeItem( j ) == NULL && pInputPanel->GetAttrib( j ) != NULL )
  669. {
  670. m_bAllRecipePanelsFilled = false;
  671. break;
  672. }
  673. }
  674. }
  675. }
  676. return m_bAllRecipePanelsFilled;
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: Callback for the confirm partial completion dialog
  680. //-----------------------------------------------------------------------------
  681. void ConfirmDestroyItems( bool bConfirmed, void* pContext )
  682. {
  683. CDynamicRecipePanel *pRecipePanel = ( CDynamicRecipePanel* )pContext;
  684. if ( pRecipePanel && bConfirmed )
  685. {
  686. if( pRecipePanel->CheckForUntradableItems() )
  687. {
  688. pRecipePanel->Craft();
  689. }
  690. }
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose: Callback for the conrim untradable dialog
  694. //-----------------------------------------------------------------------------
  695. static void ConfirmUntradableCraft( bool bConfirmed, void* pContext )
  696. {
  697. CDynamicRecipePanel *pRecipePanel = ( CDynamicRecipePanel* )pContext;
  698. if ( pRecipePanel && bConfirmed )
  699. {
  700. pRecipePanel->Craft();
  701. }
  702. }
  703. //-----------------------------------------------------------------------------
  704. // Purpose: Go through each input panel and find out if any of them contain an
  705. // item that is untradeable
  706. //-----------------------------------------------------------------------------
  707. bool CDynamicRecipePanel::CheckForUntradableItems( void )
  708. {
  709. // We dont care if the recipe itself is already not tradable
  710. if( !m_pDynamicRecipeItem->IsTradable() )
  711. {
  712. return true;
  713. }
  714. bool bHasUntradable = false;
  715. for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; ++i )
  716. {
  717. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i];
  718. for( int j = 0; j < GetNumInputPages(); ++j )
  719. {
  720. itemid_t nRecipeItemID = pPanel->GetRecipeIndex( j );
  721. if ( nRecipeItemID != 0 && nRecipeItemID != INVALID_ITEM_ID )
  722. {
  723. CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( nRecipeItemID );
  724. if ( pItemData->IsTradable() == false )
  725. {
  726. bHasUntradable = true;
  727. break;
  728. }
  729. }
  730. }
  731. }
  732. if ( bHasUntradable )
  733. {
  734. enum { kWarningLength = 512 };
  735. locchar_t wszWarning[ kWarningLength ] = LOCCHAR("");
  736. loc_scpy_safe( wszWarning,
  737. CConstructLocalizedString( GLocalizationProvider()->Find( "Dynamic_Recipe_Untradable_Text" ),
  738. m_pDynamicRecipeItem->GetItemName() ) );
  739. CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#Craft_Untradable_Title", wszWarning, "#GameUI_OK", "#Cancel", &ConfirmUntradableCraft, NULL );
  740. if ( pDialog )
  741. {
  742. pDialog->SetContext( this );
  743. pDialog->Show();
  744. }
  745. return false;
  746. }
  747. return true;
  748. }
  749. //-----------------------------------------------------------------------------
  750. // Purpose: Check if not all of the inputs have items set into them. Put up
  751. // a prompt and return false if so.
  752. //-----------------------------------------------------------------------------
  753. bool CDynamicRecipePanel::WarnAboutPartialCompletion( void )
  754. {
  755. if( !AllRecipePanelsFilled() )
  756. {
  757. enum { kWarningLength = 512 };
  758. locchar_t wszWarning[ kWarningLength ] = LOCCHAR("");
  759. loc_scpy_safe( wszWarning,
  760. CConstructLocalizedString( GLocalizationProvider()->Find( "Dynamic_Recipe_Partial_Completion_Warning" ),
  761. m_pDynamicRecipeItem->GetItemName() ) );
  762. CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#Craft_Untradable_Title", wszWarning, "#GameUI_OK", "#Cancel", &ConfirmDestroyItems, NULL );
  763. if ( pDialog )
  764. {
  765. pDialog->SetContext( this );
  766. pDialog->Show();
  767. }
  768. return false;
  769. }
  770. return true;
  771. }
  772. //-----------------------------------------------------------------------------
  773. // Purpose: They hit the craft button! Cook up a message that we're going to send
  774. // to the GC that contains all of the item_ids and attributes they are
  775. // to apply to. Open a crafting status dialog when after we send the message.
  776. //-----------------------------------------------------------------------------
  777. void CDynamicRecipePanel::Craft()
  778. {
  779. GCSDK::CProtoBufMsg<CMsgFulfillDynamicRecipeComponent> msg( k_EMsgGCFulfillDynamicRecipeComponent );
  780. msg.Body().set_tool_item_id( m_pDynamicRecipeItem->GetItemID() );
  781. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  782. {
  783. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i];
  784. for( int j=0; j < GetNumInputPages(); ++j )
  785. {
  786. itemid_t nRecipeItemID = pPanel->GetRecipeIndex( j );
  787. if( nRecipeItemID != 0 && nRecipeItemID != INVALID_ITEM_ID )
  788. {
  789. if( !pPanel->MatchesAttribCriteria( nRecipeItemID, j ) )
  790. {
  791. AssertMsg( 0, "Input panel has recipe item that does not pass its criteria" );
  792. // Something bad happened. Return this item to the backpack.
  793. ReturnRecipeItemToBackpack( nRecipeItemID, pPanel, j );
  794. continue;
  795. }
  796. // Add component
  797. CMsgRecipeComponent* pComponent = msg.Body().add_consumption_components();
  798. pComponent->set_subject_item_id( nRecipeItemID );
  799. const CEconItemAttributeDefinition* pAttribute = pPanel->GetAttrib( j );
  800. if( !pAttribute )
  801. {
  802. AssertMsg( 0, "NULL attribute in panel what attempting to craft" );
  803. // Something bad happened. Return this item to the backpack.
  804. ReturnRecipeItemToBackpack( nRecipeItemID, pPanel, j );
  805. continue;
  806. }
  807. pComponent->set_attribute_index( pAttribute->GetDefinitionIndex() );
  808. }
  809. }
  810. }
  811. EconUI()->Gamestats_ItemTransaction( IE_ITEM_USED_TOOL, m_pDynamicRecipeItem, "consumed_item" );
  812. GCClientSystem()->BSendMessage( msg );
  813. // Open a craft status window to take focus away and let the user know something is happening
  814. OpenCraftingStatusDialog( this, "#CraftUpdate_Start", true, false, false );
  815. m_flAbortCraftingAt = vgui::system()->GetCurrentTime() + 10;
  816. vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose: Think when we're waiting for a craft response
  820. //-----------------------------------------------------------------------------
  821. void CDynamicRecipePanel::OnTick( void )
  822. {
  823. BaseClass::OnTick();
  824. if( IsVisible() )
  825. {
  826. if( m_flAbortCraftingAt != 0.f )
  827. {
  828. // Timeout for crafting. Let them know we failed.
  829. if( m_flAbortCraftingAt < vgui::system()->GetCurrentTime() )
  830. {
  831. OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false );
  832. m_flAbortCraftingAt = 0;
  833. }
  834. }
  835. }
  836. }
  837. //-----------------------------------------------------------------------------
  838. // Purpose: Close the craft status window if we close
  839. //-----------------------------------------------------------------------------
  840. void CDynamicRecipePanel::OnShowPanel( bool bVisible, bool bReturningFromArmory )
  841. {
  842. if ( !bVisible )
  843. {
  844. CloseCraftingStatusDialog();
  845. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  846. }
  847. BaseClass::OnShowPanel( bVisible, bReturningFromArmory );
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose: Add the new panels into vectors for each group, and set parents to
  851. // appropriate frames
  852. //-----------------------------------------------------------------------------
  853. void CDynamicRecipePanel::AddNewItemPanel( int iPanelIndex )
  854. {
  855. if( IsInputPanel( iPanelIndex ) )
  856. {
  857. // Input item
  858. int nIndex = m_vecRecipeInputModelPanels.AddToTail();
  859. CInputPanelItemModelPanel* pPanel = vgui::SETUP_PANEL( new CInputPanelItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex), m_pDynamicRecipeItem ));
  860. m_pItemModelPanels.AddToTail( pPanel );
  861. pPanel->SetParent( m_pRecipeContainer );
  862. m_vecRecipeInputModelPanels[nIndex] = pPanel;
  863. }
  864. else if( IsOutputPanel( iPanelIndex ) )
  865. {
  866. // Output item
  867. CItemModelPanel* pPanel = vgui::SETUP_PANEL( new CItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex) ) );
  868. m_vecRecipeOutputModelPanels.AddToTail( pPanel );
  869. m_pItemModelPanels.AddToTail( pPanel );
  870. pPanel->SetParent( m_pRecipeContainer );
  871. }
  872. else
  873. {
  874. // Inventory item
  875. int nIndex = m_vecBackpackModelPanels.AddToTail();
  876. CRecipeComponentItemModelPanel* pPanel = vgui::SETUP_PANEL( new CRecipeComponentItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex) ) );
  877. m_vecBackpackModelPanels[nIndex] = pPanel;
  878. m_pItemModelPanels.AddToTail( pPanel );
  879. pPanel->SetParent( m_pInventoryContainer );
  880. }
  881. CItemModelPanel* pPanel = m_pItemModelPanels.Tail();
  882. pPanel->SetActAsButton( true, true );
  883. pPanel->SetTooltip( m_pMouseOverTooltip, "" );
  884. pPanel->SetShowGreyedOutTooltip( true );
  885. pPanel->SetShowEquipped( true );
  886. // Store a position for our new panel
  887. m_ItemModelPanelPos.AddToTail();
  888. m_ItemModelPanelPos[iPanelIndex].x = m_ItemModelPanelPos[iPanelIndex].y = 0;
  889. Assert( iPanelIndex == (m_pItemModelPanels.Count()-1) );
  890. }
  891. //-----------------------------------------------------------------------------
  892. // Purpose: Is this index an input panel
  893. //-----------------------------------------------------------------------------
  894. bool CDynamicRecipePanel::IsInputPanel( int iPanelIndex ) const
  895. {
  896. return iPanelIndex < DYNAMIC_RECIPE_INPUT_COUNT;
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Purpose: Is this index an output panel
  900. //-----------------------------------------------------------------------------
  901. bool CDynamicRecipePanel::IsOutputPanel( int iPanelIndex) const
  902. {
  903. return iPanelIndex < ( DYNAMIC_RECIPE_OUTPUT_COUNT + DYNAMIC_RECIPE_INPUT_COUNT ) && !IsInputPanel(iPanelIndex);
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Purpose: Is this index a backpack panel
  907. //-----------------------------------------------------------------------------
  908. bool CDynamicRecipePanel::IsBackpackPanel( int iPanelIndex ) const
  909. {
  910. return ( iPanelIndex >= ( DYNAMIC_RECIPE_INPUT_COUNT + DYNAMIC_RECIPE_OUTPUT_COUNT )
  911. && !IsInputPanel( iPanelIndex )
  912. && !IsOutputPanel( iPanelIndex ) );
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Purpose: Is this backpack panel on this page
  916. //-----------------------------------------------------------------------------
  917. bool CDynamicRecipePanel::IsInvPanelOnThisPage( unsigned nIndex ) const
  918. {
  919. unsigned nNumPerPage = DYNAMIC_RECIPE_BACKPACK_ROWS * DYNAMIC_RECIPE_BACKPACK_COLS;
  920. unsigned nMinIndex = nNumPerPage * GetCurrentPage();
  921. unsigned nMaxIndex = nNumPerPage * (GetCurrentPage() + 1);
  922. return nIndex >= nMinIndex && nIndex < nMaxIndex;
  923. }
  924. //-----------------------------------------------------------------------------
  925. // Purpose: Given the number of items that match the recipe, how many pages
  926. // do we need to show
  927. //-----------------------------------------------------------------------------
  928. int CDynamicRecipePanel::GetNumPages()
  929. {
  930. return ceil( float(m_nNumRecipeItems) / float(GetNumBackpackPanelsPerPage()) );
  931. }
  932. //-----------------------------------------------------------------------------
  933. // Purpose: Sets the page of the backpack panels
  934. //-----------------------------------------------------------------------------
  935. void CDynamicRecipePanel::SetCurrentPage( int nNewPage )
  936. {
  937. if ( nNewPage < 0 )
  938. {
  939. nNewPage = GetNumPages() - 1;
  940. }
  941. else if ( nNewPage >= GetNumPages() )
  942. {
  943. nNewPage = 0;
  944. }
  945. FOR_EACH_VEC( m_vecBackpackModelPanels, i )
  946. {
  947. m_vecBackpackModelPanels[i]->SetPageNumber( nNewPage );
  948. }
  949. BaseClass::SetCurrentPage( nNewPage );
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Purpose: Chance the current page for all input panels
  953. //-----------------------------------------------------------------------------
  954. void CDynamicRecipePanel::SetCurrentInputPage( int nNewPage )
  955. {
  956. m_nInputPage = nNewPage;
  957. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  958. {
  959. m_vecRecipeInputModelPanels[i]->SetPageNumber( nNewPage );
  960. }
  961. }
  962. //-----------------------------------------------------------------------------
  963. // Purpose: Given the number of input items, how many input pages do we need to show
  964. //-----------------------------------------------------------------------------
  965. int CDynamicRecipePanel::GetNumInputPages() const
  966. {
  967. return ceil( float(m_RecipeIterator.GetInputCount()) / float(GetNumInputPanelsPerPage()) );
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose: Given the number of output items, how many output pages do we need to show
  971. //-----------------------------------------------------------------------------
  972. int CDynamicRecipePanel::GetNumOutputPage() const
  973. {
  974. return ceil( float(m_RecipeIterator.GetOutputCount()) / float(GetNumOutputPanelsPerPage()) );
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Purpose: Reset all of the item panels, their pages, their info and re-evaluate
  978. // the recipe item for attributes and the backpack for matching items
  979. //-----------------------------------------------------------------------------
  980. void CDynamicRecipePanel::InitItemPanels()
  981. {
  982. // Clear out every panel's items
  983. FOR_EACH_VEC( m_pItemModelPanels, i )
  984. {
  985. m_pItemModelPanels[i]->SetItem( NULL );
  986. }
  987. // Go through and set the item to all inventory panels to NULL
  988. FOR_EACH_VEC( m_vecBackpackModelPanels, i )
  989. {
  990. m_vecBackpackModelPanels[i]->DeleteRecipes();
  991. }
  992. // Go through our backpack and repopulate our backpack and matching components
  993. FindPossibleBackpackItems();
  994. // Reset to the first page
  995. SetCurrentInputPage( 0 );
  996. SetCurrentPage( 0 );
  997. // Go through and set default items on input panels
  998. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  999. {
  1000. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i];
  1001. // Clear out any recipes we have
  1002. pPanel->DeleteRecipes();
  1003. // Clear out stale info
  1004. pPanel->SetDynamicRecipeItem( m_pDynamicRecipeItem );
  1005. }
  1006. for( int i=0; i < m_RecipeIterator.GetInputCount(); ++i )
  1007. {
  1008. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[ i % m_vecRecipeInputModelPanels.Count() ];
  1009. pPanel->AddDefaultItem( m_RecipeIterator.GetInputItem( i ) );
  1010. pPanel->AddComponentInfo( m_RecipeIterator.GetInputAttrib( i ) );
  1011. }
  1012. // Go through and set items into output panels
  1013. FOR_EACH_VEC( m_vecRecipeOutputModelPanels, i )
  1014. {
  1015. CItemModelPanel* pPanel = m_vecRecipeOutputModelPanels[i];
  1016. // Set appropriate item for output panel
  1017. if( i < m_RecipeIterator.GetOutputCount() )
  1018. {
  1019. int nOutputIndex = (m_nOutputPage * DYNAMIC_RECIPE_OUTPUT_COUNT) + i;
  1020. pPanel->SetItem( m_RecipeIterator.GetOutputItem( nOutputIndex ) );
  1021. }
  1022. }
  1023. // Go through all the recipe items and set them into inventory panels
  1024. PopulatePanelsForCurrentPage();
  1025. UpdateModelPanels();
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose: Fills in the backpack slots with the items for the current page
  1029. //-----------------------------------------------------------------------------
  1030. void CDynamicRecipePanel::PopulatePanelsForCurrentPage()
  1031. {
  1032. // Go through all the recipe items and set them into inventory panels
  1033. FOR_EACH_VEC( m_vecBackpackModelPanels, i )
  1034. {
  1035. CRecipeComponentItemModelPanel* pPanel = m_vecBackpackModelPanels[i];
  1036. // Update the item we display. Our inventory might have shifted around
  1037. pPanel->UpdateDisplayItem();
  1038. bool bIsSlotOpen = pPanel->IsSlotAvailable( GetCurrentPage() );
  1039. pPanel->SetVisible( bIsSlotOpen );
  1040. pPanel->SetEnabled( bIsSlotOpen );
  1041. pPanel->InvalidateLayout();
  1042. SetBorderForItem( pPanel, false );
  1043. }
  1044. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1045. {
  1046. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i];
  1047. pPanel->SetPageNumber( m_nInputPage );
  1048. pPanel->UpdateDisplayItem();
  1049. bool bIsSlotOpen = pPanel->IsSlotAvailable( m_nInputPage );
  1050. pPanel->SetVisible( bIsSlotOpen );
  1051. pPanel->SetEnabled( bIsSlotOpen );
  1052. pPanel->InvalidateLayout();
  1053. SetBorderForItem( pPanel, false );
  1054. }
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose: Go through the player's entire backpack and check if each of them
  1058. // passes any of the input panel's attributes criteria
  1059. //-----------------------------------------------------------------------------
  1060. void CDynamicRecipePanel::FindPossibleBackpackItems()
  1061. {
  1062. Assert( m_pDynamicRecipeItem->GetSOCData() );
  1063. // Iterate through the attributes on our recipe item
  1064. // and create all of our input and output items.
  1065. m_RecipeIterator.Reset();
  1066. CEconItem* pEconItem = m_pDynamicRecipeItem->GetSOCData() ;
  1067. pEconItem->IterateAttributes( &m_RecipeIterator );
  1068. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  1069. if ( pLocalInv == NULL )
  1070. return;
  1071. m_nNumRecipeItems = 0;
  1072. // Go through our backpack and filter items that match our input criteria
  1073. for ( int i = 0 ; i < pLocalInv->GetItemCount() ; ++i )
  1074. {
  1075. CEconItemView *pItem = pLocalInv->GetItem( i );
  1076. Assert( pItem );
  1077. // If we're not showing untradable items, and this item is untradeable
  1078. // then we just skip it
  1079. if ( !pItem->IsTradable() && !m_bShowUntradable )
  1080. continue;
  1081. CDynamicRecipeItemMatchFind matchingIterator( m_pDynamicRecipeItem, pItem );
  1082. m_pDynamicRecipeItem->IterateAttributes( &matchingIterator );
  1083. if( matchingIterator.MatchesAnyAttributes() )
  1084. {
  1085. // Set in the recipe
  1086. m_vecBackpackModelPanels[ m_nNumRecipeItems % GetNumBackpackPanelsPerPage() ]->AddRecipe( pItem->GetItemID() );
  1087. ++m_nNumRecipeItems;
  1088. }
  1089. }
  1090. InvalidateLayout();
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // Purpose: Layout the item panels
  1094. //-----------------------------------------------------------------------------
  1095. void CDynamicRecipePanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex )
  1096. {
  1097. int iCenter = 0;
  1098. int iButtonX = 0, iButtonY = 0, iXPos = 0, iYPos = 0;
  1099. // Position all of the panels
  1100. if( IsInputPanel( iIndex ) )
  1101. {
  1102. iButtonX = (iIndex % CRAFTING_SLOTS_INPUT_COLUMNS);
  1103. iButtonY = (iIndex / CRAFTING_SLOTS_INPUT_COLUMNS);
  1104. iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX);
  1105. iYPos = m_iItemYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY);
  1106. pPanel->SetPos( iXPos, iYPos );
  1107. }
  1108. else if( IsOutputPanel( iIndex ) )
  1109. {
  1110. int iButtonIndex = iIndex - DYNAMIC_RECIPE_INPUT_COUNT;
  1111. iButtonX = (iButtonIndex % CRAFTING_SLOTS_OUTPUT_COLUMNS);
  1112. iButtonY = (iButtonIndex / CRAFTING_SLOTS_OUTPUT_COLUMNS);
  1113. iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX);
  1114. iYPos = m_iOutputItemYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY);
  1115. pPanel->SetPos( iXPos, iYPos );
  1116. pPanel->SetItem( m_RecipeIterator.GetOutputItem( iButtonIndex ) );
  1117. }
  1118. else if( IsBackpackPanel( iIndex ) )
  1119. {
  1120. int iButtonIndex = iIndex - DYNAMIC_RECIPE_INPUT_COUNT - DYNAMIC_RECIPE_OUTPUT_COUNT;
  1121. iButtonX = (iButtonIndex % DYNAMIC_RECIPE_BACKPACK_COLS);
  1122. iButtonY = (iButtonIndex / DYNAMIC_RECIPE_BACKPACK_COLS);
  1123. iXPos = (iCenter + m_iInventoryXPos) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX);
  1124. iYPos = m_iInventoryYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY);
  1125. pPanel->SetPos( iXPos, iYPos );
  1126. }
  1127. }
  1128. //-----------------------------------------------------------------------------
  1129. // Purpose: Update each panel to see if it should show, what item to show, and if
  1130. // it's greyed out. Update the page labels here too.
  1131. //-----------------------------------------------------------------------------
  1132. void CDynamicRecipePanel::UpdateModelPanels( void )
  1133. {
  1134. // Check if any input panels have recipe items in them
  1135. bool bAnyInputHasRecipe = false;
  1136. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1137. {
  1138. CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i];
  1139. // Update the item we display. Our inventory might have shifted around
  1140. pPanel->UpdateDisplayItem();
  1141. SetBorderForItem( pPanel, false );
  1142. // Check for a recipe
  1143. for( int j=0; j < GetNumInputPages(); ++j )
  1144. {
  1145. CEconItemView* pRecipe = pPanel->GetRecipeItem( j );
  1146. bAnyInputHasRecipe |= pRecipe != NULL;
  1147. }
  1148. // Input panels are visible and enabled if their recipe slot is available
  1149. pPanel->SetEnabled( pPanel->GetDefaultItem() != NULL );
  1150. pPanel->SetVisible( pPanel->GetDefaultItem() != NULL );
  1151. }
  1152. static CSchemaAttributeDefHandle pAttrib_NoPartialComplete( "recipe no partial complete" );
  1153. bool bPartialCompletionAllowed = !m_pDynamicRecipeItem->FindAttribute( pAttrib_NoPartialComplete );
  1154. m_pInputsLabel->SetText( bPartialCompletionAllowed ? "#Craft_Recipe_Inputs" : "#Dynamic_Recipe_Outputs_No_Partial_Complete" );
  1155. // If any of the input panels have a recipe in them, then crafting is available
  1156. if ( m_pRecipeCraftButton )
  1157. {
  1158. bool bCraftEnabled = ( bPartialCompletionAllowed && bAnyInputHasRecipe ) || AllRecipePanelsFilled();
  1159. m_pRecipeCraftButton->SetEnabled( bCraftEnabled );
  1160. }
  1161. if ( m_pRecipeCraftButton )
  1162. {
  1163. m_pRecipeCraftButton->SetText( AllRecipePanelsFilled() ? "#CraftConfirm" : "#ToolCustomizeTextureOKButton" );
  1164. }
  1165. if ( m_pOutputsLabel )
  1166. {
  1167. m_pOutputsLabel->SetText( AllRecipePanelsFilled() ? "#Craft_Recipe_Outputs" : "#Dynamic_Recipe_Outputs_Not_Complete");
  1168. m_pOutputsLabel->SetFgColor( AllRecipePanelsFilled() ? Color( 200, 80, 60, 255 ) : Color( 117, 107, 94, 255 ) );
  1169. }
  1170. FOR_EACH_VEC( m_vecRecipeOutputModelPanels, i )
  1171. {
  1172. CItemModelPanel* pPanel = m_vecRecipeOutputModelPanels[i];
  1173. SetBorderForItem( pPanel, false );
  1174. // Output panels are visible and enabled if they have an item in them
  1175. pPanel->SetVisible( pPanel->GetItem() != NULL );
  1176. pPanel->SetEnabled( pPanel->GetItem() != NULL );
  1177. }
  1178. PopulatePanelsForCurrentPage();
  1179. // Update the current backpack page numbers
  1180. char szTmp[16];
  1181. Q_snprintf(szTmp, 16, "%d/%d", GetCurrentPage() + 1, GetNumPages() );
  1182. m_pInventoryContainer->SetDialogVariable( "backpackpage", szTmp );
  1183. // Update the current input page numbers
  1184. Q_snprintf(szTmp, 16, "%d/%d", m_nInputPage + 1, GetNumInputPages() );
  1185. m_pRecipeContainer->SetDialogVariable( "inputpage", szTmp );
  1186. DeSelectAllBackpackItemPanels();
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Purpose: If this is a backpack panel, send the item into the first accepting
  1190. // input pane, if one exists, or else do nothing. If this is a input
  1191. // panel with a backpack item in it, send the backpack item back to the
  1192. // backpack.
  1193. //-----------------------------------------------------------------------------
  1194. void CDynamicRecipePanel::OnItemPanelMouseDoublePressed( vgui::Panel *panel )
  1195. {
  1196. CRecipeComponentItemModelPanel *pSrcPanel = dynamic_cast < CRecipeComponentItemModelPanel * > ( panel );
  1197. if( !pSrcPanel )
  1198. return;
  1199. int iIndex = GetBackpackPositionForPanel( pSrcPanel );
  1200. // Send from an input panel to the first open backpack panel, even on a different page
  1201. if( IsInputPanel( iIndex ) )
  1202. {
  1203. CInputPanelItemModelPanel *pInputPanel = assert_cast<CInputPanelItemModelPanel*>( pSrcPanel );
  1204. Assert( pInputPanel );
  1205. int nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pInputPanel->GetPageNumber() );
  1206. CEconItemView* pRecipeItem = pInputPanel->GetRecipeItem( pInputPanel->GetPageNumber() );
  1207. if( pRecipeItem )
  1208. {
  1209. // Just put it in the first open slot.
  1210. ReturnRecipeItemToBackpack( nSrcRecipeIndex, pInputPanel, pInputPanel->GetPageNumber() );
  1211. }
  1212. }
  1213. else if( IsBackpackPanel( iIndex ) )
  1214. {
  1215. // Sending from the backpack panel to the first matching input panel that's on the current input page.
  1216. int nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( GetCurrentPage() );
  1217. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1218. {
  1219. CInputPanelItemModelPanel *pDstPanel = m_vecRecipeInputModelPanels[i];
  1220. if( pDstPanel->GetRecipeItem( pDstPanel->GetPageNumber() ) == NULL && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) )
  1221. {
  1222. // Next check if we just want to move the item
  1223. SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, pDstPanel->GetPageNumber() );
  1224. break;
  1225. }
  1226. }
  1227. }
  1228. m_bInputPanelsDirty = true;
  1229. // Force panels to update
  1230. UpdateModelPanels();
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Purpose: Border highlight if the panel has an item in it
  1234. //-----------------------------------------------------------------------------
  1235. void CDynamicRecipePanel::OnItemPanelEntered( vgui::Panel *panel )
  1236. {
  1237. CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
  1238. m_pMouseOverItemPanel = pItemPanel;
  1239. CEconItemView *pItem = pItemPanel->GetItem();
  1240. // Recalc the borders on item panels
  1241. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1242. {
  1243. SetBorderForItem( m_vecRecipeInputModelPanels[i], false );
  1244. }
  1245. if ( pItemPanel && IsVisible() )
  1246. {
  1247. SetBorderForItem( pItemPanel, pItem != NULL );
  1248. }
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Purpose: Turn of border highlights
  1252. //-----------------------------------------------------------------------------
  1253. void CDynamicRecipePanel::OnItemPanelExited( vgui::Panel *panel )
  1254. {
  1255. CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
  1256. m_pMouseOverItemPanel = NULL;
  1257. // Recalc the borders on item panels
  1258. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1259. {
  1260. SetBorderForItem( m_vecRecipeInputModelPanels[i], false );
  1261. }
  1262. if ( pItemPanel && !pItemPanel->IsSelected() )
  1263. {
  1264. SetBorderForItem( pItemPanel, false );
  1265. }
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // Purpose: We got a craft response! Close out this window because we're going
  1269. // to show the user their loot
  1270. //-----------------------------------------------------------------------------
  1271. void CDynamicRecipePanel::OnRecipeCompleted()
  1272. {
  1273. SetVisible( false );
  1274. m_flAbortCraftingAt = 0.f;
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. // Purpose: Set the border for the item. Input panels highlight when a dragged
  1278. // item matches its criteria. Output items highlight when all inputs
  1279. // are fulfilled.
  1280. //-----------------------------------------------------------------------------
  1281. void CDynamicRecipePanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver )
  1282. {
  1283. if ( !pItemPanel || !pItemPanel->IsVisible() )
  1284. return;
  1285. int iIndex = GetBackpackPositionForPanel( pItemPanel );
  1286. if( IsInputPanel( iIndex ) )
  1287. {
  1288. // Special case for input panels. They need to highlight when a dragged item
  1289. // matches their criteria.
  1290. CItemModelPanel* pPanel = m_bDragging ? m_pMouseDragItemPanel : m_pMouseOverItemPanel;
  1291. CEconItemView* pPanelItem = pPanel ? pPanel->GetItem() : NULL;
  1292. CInputPanelItemModelPanel* pInputPanel = dynamic_cast<CInputPanelItemModelPanel*>( pItemPanel );
  1293. Assert( pInputPanel );
  1294. // If this panel doesnt have a recipe, then we want some sort of greyed out look
  1295. if( pInputPanel->GetRecipeItem( pInputPanel->GetPageNumber() ) == NULL )
  1296. {
  1297. const char *pszBorder = NULL;
  1298. int iRarity = GetItemQualityForBorder( pItemPanel );
  1299. // We only want backpack panels and the drag panel to be highlight sources
  1300. bool bValidHighlightSourcePanel = pPanel == m_pMouseDragItemPanel
  1301. || m_vecBackpackModelPanels.Find( static_cast<CRecipeComponentItemModelPanel*>(pPanel) ) != m_vecBackpackModelPanels.InvalidIndex();
  1302. // If this panel can accept whatever the source panel is, we want to highlight a bit
  1303. if( bValidHighlightSourcePanel && pPanelItem && pInputPanel->MatchesAttribCriteria( pPanelItem->GetItemID() ) )
  1304. {
  1305. if ( pItemPanel->GetItem() )
  1306. {
  1307. pszBorder = g_szItemBorders[iRarity][3];
  1308. pItemPanel->SetGreyedOut( NULL );
  1309. }
  1310. else
  1311. {
  1312. pszBorder = g_szItemBorders[iRarity][0];
  1313. }
  1314. }
  1315. else // Look gloomy and uninviting if not
  1316. {
  1317. pszBorder = g_szItemBorders[iRarity][3];
  1318. pItemPanel->SetGreyedOut( "" );
  1319. }
  1320. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  1321. pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) );
  1322. return;
  1323. }
  1324. }
  1325. else if( IsOutputPanel( iIndex ) )
  1326. {
  1327. const char *pszBorder = NULL;
  1328. int iRarity = GetItemQualityForBorder( pItemPanel );
  1329. // The output panel greys out when any of the input panels are not fulfilled,
  1330. // and lights up when they are all fulfilled
  1331. if ( iRarity >= 0 && iRarity < ARRAYSIZE( g_szItemBorders ) )
  1332. {
  1333. if( AllRecipePanelsFilled() )
  1334. {
  1335. pszBorder = g_szItemBorders[iRarity][0];
  1336. pItemPanel->SetGreyedOut( NULL );
  1337. }
  1338. else
  1339. {
  1340. pszBorder = g_szItemBorders[iRarity][3];
  1341. pItemPanel->SetGreyedOut( NULL );
  1342. }
  1343. }
  1344. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  1345. pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) );
  1346. return;
  1347. }
  1348. // Backpack and output panels get default treatment
  1349. BaseClass::SetBorderForItem( pItemPanel, bMouseOver );
  1350. }
  1351. //-----------------------------------------------------------------------------
  1352. // Purpose: Swap recipe items in srcpanel and dstpanel
  1353. //-----------------------------------------------------------------------------
  1354. void CDynamicRecipePanel::SetRecipeComponentIntoPanel( itemid_t nSrcRecipeIndex, CRecipeComponentItemModelPanel* pSrcPanel, int nSrcPage, CRecipeComponentItemModelPanel* pDstPanel, int nDstPage )
  1355. {
  1356. Assert( nSrcRecipeIndex != 0 );
  1357. Assert( pSrcPanel );
  1358. Assert( pDstPanel );
  1359. Assert( pSrcPanel->GetRecipeItem( nSrcPage ) );
  1360. // Get the recipe from the destination panel
  1361. itemid_t pDstRecipeIndex = pDstPanel->GetRecipeIndex( nDstPage );
  1362. // Swap recipes
  1363. pSrcPanel->SetRecipeItem( pDstRecipeIndex, nSrcPage );
  1364. pDstPanel->SetRecipeItem( nSrcRecipeIndex, nDstPage );
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose: Check if a given panel is allowed to be dragged.
  1368. //-----------------------------------------------------------------------------
  1369. bool CDynamicRecipePanel::AllowDragging( CItemModelPanel *pPanel )
  1370. {
  1371. int iIndex = GetBackpackPositionForPanel( pPanel );
  1372. // If this isn't an input or backpack panel, abort
  1373. if( !IsInputPanel( iIndex ) && !IsBackpackPanel( iIndex ) )
  1374. return false;
  1375. CRecipeComponentItemModelPanel* pRecipePanel = dynamic_cast< CRecipeComponentItemModelPanel* >( pPanel );
  1376. Assert( pRecipePanel );
  1377. if( !pRecipePanel )
  1378. return false;
  1379. int nPageNumber = 0;
  1380. if( IsBackpackPanel( iIndex ) )
  1381. {
  1382. nPageNumber = GetCurrentPage();
  1383. }
  1384. else if ( IsInputPanel( iIndex ) )
  1385. {
  1386. nPageNumber = m_nInputPage;
  1387. }
  1388. // Get the recipe item out of this panel
  1389. CEconItemView* pRecipe = pRecipePanel->GetRecipeItem( nPageNumber );
  1390. // If no recipe, abort
  1391. if( !pRecipe )
  1392. return false;
  1393. return true;
  1394. }
  1395. //-----------------------------------------------------------------------------
  1396. // Purpose: Start dragging!
  1397. //-----------------------------------------------------------------------------
  1398. void CDynamicRecipePanel::StartDrag( int x, int y )
  1399. {
  1400. BaseClass::StartDrag( x, y );
  1401. // Recalc the borders on item panels
  1402. FOR_EACH_VEC( m_pItemModelPanels, i )
  1403. {
  1404. SetBorderForItem( m_pItemModelPanels[i], false );
  1405. }
  1406. }
  1407. //-----------------------------------------------------------------------------
  1408. // Purpose: Stop dragging
  1409. //-----------------------------------------------------------------------------
  1410. void CDynamicRecipePanel::StopDrag( bool bSucceeded )
  1411. {
  1412. BaseClass::StopDrag( bSucceeded );
  1413. // Recalc the borders on item panels
  1414. FOR_EACH_VEC( m_pItemModelPanels, i )
  1415. {
  1416. SetBorderForItem( m_pItemModelPanels[i], false );
  1417. }
  1418. }
  1419. //-----------------------------------------------------------------------------
  1420. // Purpose: Helper function to find out if an input panel can accept an item
  1421. //-----------------------------------------------------------------------------
  1422. bool CDynamicRecipePanel::InputPanelCanAcceptItem( CItemModelPanel* pPanel, itemid_t nItemID )
  1423. {
  1424. Assert( IsInputPanel( GetBackpackPositionForPanel( pPanel ) ) );
  1425. CInputPanelItemModelPanel* pInputPanel = dynamic_cast<CInputPanelItemModelPanel*>( pPanel );
  1426. Assert( pInputPanel );
  1427. return pInputPanel->MatchesAttribCriteria( nItemID );
  1428. }
  1429. //-----------------------------------------------------------------------------
  1430. // Purpose: Check if the dragged item can be dropped into a given panel
  1431. //-----------------------------------------------------------------------------
  1432. bool CDynamicRecipePanel::CanDragTo( CItemModelPanel *pItemPanel, int iPanelIndex )
  1433. {
  1434. // From input to backpack panel
  1435. int iDraggedFromPos = GetBackpackPositionForPanel(m_pItemDraggedFromPanel);
  1436. if( IsInputPanel( iDraggedFromPos ) && IsBackpackPanel( iPanelIndex ) )
  1437. return true;
  1438. itemid_t itemID = m_pMouseDragItemPanel->GetItem()
  1439. ? m_pMouseDragItemPanel->GetItem()->GetItemID()
  1440. : 0;
  1441. // From backpack to input panel that can accept the item
  1442. if( IsBackpackPanel( iDraggedFromPos )
  1443. && IsInputPanel( iPanelIndex )
  1444. && InputPanelCanAcceptItem( pItemPanel,itemID ) )
  1445. return true;
  1446. // From inoput to other input that can also accept the item
  1447. if( IsInputPanel( iDraggedFromPos )
  1448. && IsInputPanel( iPanelIndex )
  1449. && InputPanelCanAcceptItem( pItemPanel, itemID ) )
  1450. return true;
  1451. return false;
  1452. }
  1453. //-----------------------------------------------------------------------------
  1454. // Purpose: Handle the user releasing their drag on a given panel. Only valid
  1455. // from backpack<->backpack, input<->input, backpack<->input
  1456. //-----------------------------------------------------------------------------
  1457. void CDynamicRecipePanel::HandleDragTo( CItemModelPanel * /*pItemPanel*/, int iPanelIndex )
  1458. {
  1459. int iDraggedFromPos = GetBackpackPositionForPanel(m_pItemDraggedFromPanel);
  1460. // The destination panel needs to exist, be enabled and be visible or else we dont consider it.
  1461. CItemModelPanel* pDestinationPanel = m_pItemModelPanels[ iPanelIndex ];
  1462. if( !pDestinationPanel || !pDestinationPanel->IsEnabled() || !pDestinationPanel->IsVisible() )
  1463. {
  1464. return;
  1465. }
  1466. m_bInputPanelsDirty = true;
  1467. // If we dragged from a inventory panel onto an input panel
  1468. if( IsInputPanel( iPanelIndex ) && IsBackpackPanel( iDraggedFromPos ) )
  1469. {
  1470. CRecipeComponentItemModelPanel* pSrcPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] );
  1471. CInputPanelItemModelPanel* pDstPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iPanelIndex] );
  1472. if( pSrcPanel && pDstPanel )
  1473. {
  1474. // They dragged an item from the backpack into an input slot. Check if this is ok.
  1475. itemid_t nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( GetCurrentPage() );
  1476. Assert( nSrcRecipeIndex != 0 );
  1477. if( nSrcRecipeIndex != 0 && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) )
  1478. {
  1479. SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, GetCurrentPage(), pDstPanel, pDstPanel->GetPageNumber() );
  1480. }
  1481. }
  1482. }
  1483. else if( IsInputPanel( iDraggedFromPos ) && IsBackpackPanel( iPanelIndex ) )
  1484. {
  1485. CRecipeComponentItemModelPanel* pSrcPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] );
  1486. CRecipeComponentItemModelPanel* pDstPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iPanelIndex] );
  1487. // If we dragged from an input panel to a backpack panel, return the item
  1488. // to its original backpack slot regardless of where they dragged it to
  1489. if( pSrcPanel && pDstPanel )
  1490. {
  1491. // They dragged from an input panel to inventory panel. Check if there's something in
  1492. // the inventory slot already. If so, just move to the first open spot.
  1493. itemid_t pSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pSrcPanel->GetPageNumber() );
  1494. itemid_t pDstRecipeIndex = pDstPanel->GetRecipeIndex( GetCurrentPage() );
  1495. Assert( pSrcRecipeIndex != 0 );
  1496. if( pSrcRecipeIndex != 0 && pDstRecipeIndex != 0 )
  1497. {
  1498. // There's already a recipe item in there. Just put it in the first open slot.
  1499. ReturnRecipeItemToBackpack( pSrcRecipeIndex, pSrcPanel, 0 );
  1500. }
  1501. else if( pSrcRecipeIndex )
  1502. {
  1503. // It's open! Place in there
  1504. SetRecipeComponentIntoPanel( pSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, GetCurrentPage() );
  1505. }
  1506. }
  1507. }
  1508. else if( IsInputPanel( iDraggedFromPos ) && IsInputPanel( iPanelIndex ) )
  1509. {
  1510. CInputPanelItemModelPanel* pSrcPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] );
  1511. CInputPanelItemModelPanel* pDstPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iPanelIndex] );
  1512. // Dragged from an input panel to an input panel.
  1513. if( pSrcPanel && pDstPanel )
  1514. {
  1515. itemid_t nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pSrcPanel->GetPageNumber() );
  1516. itemid_t nDstRecipeIndex = pDstPanel->GetRecipeIndex( pDstPanel->GetPageNumber() );
  1517. // First see if we can swap our inputs
  1518. if( nSrcRecipeIndex != 0 && nDstRecipeIndex != 0 )
  1519. {
  1520. if( pSrcPanel->MatchesAttribCriteria( nDstRecipeIndex ) &&
  1521. pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) )
  1522. {
  1523. SetRecipeComponentIntoPanel( nDstRecipeIndex, pSrcPanel, 0, pDstPanel, 0 );
  1524. }
  1525. }
  1526. else if( nSrcRecipeIndex != 0 && nDstRecipeIndex == 0 && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) )
  1527. {
  1528. // Next check if we just want to move the item
  1529. SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, pDstPanel->GetPageNumber() );
  1530. }
  1531. }
  1532. }
  1533. else
  1534. {
  1535. AssertMsg( 0, "Unhandled drag case!" );
  1536. }
  1537. UpdateModelPanels();
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. // Purpose: Return a given item to the first open slot in out backpack
  1541. //-----------------------------------------------------------------------------
  1542. void CDynamicRecipePanel::ReturnRecipeItemToBackpack( itemid_t nItemID, CRecipeComponentItemModelPanel* pSrcPanel, int nSrcPage )
  1543. {
  1544. // For each page, check each recipe slot, on each panel for an opening
  1545. for( int i=0; i < GetNumPages(); ++i )
  1546. {
  1547. FOR_EACH_VEC( m_vecBackpackModelPanels, j )
  1548. {
  1549. CRecipeComponentItemModelPanel* pDstPanel = m_vecBackpackModelPanels[j];
  1550. CEconItemView* pDstRecipe = pDstPanel->GetRecipeItem( i );
  1551. bool bSlotIsOpen = pDstPanel->IsSlotAvailable( i );
  1552. if( bSlotIsOpen && pDstRecipe == NULL )
  1553. {
  1554. SetRecipeComponentIntoPanel( nItemID, pSrcPanel, nSrcPage, pDstPanel, i );
  1555. return;
  1556. }
  1557. }
  1558. }
  1559. AssertMsg( 0, "No open backpack slot found when returning item to backpack!" );
  1560. }
  1561. //-----------------------------------------------------------------------------
  1562. // Purpose: Set in a new recipe item. Update labels and reset panels.
  1563. //-----------------------------------------------------------------------------
  1564. void CDynamicRecipePanel::SetNewRecipe( CEconItemView* pNewRecipeItem )
  1565. {
  1566. m_flAbortCraftingAt = 0.f;
  1567. m_pDynamicRecipeItem = pNewRecipeItem;
  1568. // Update recipe title
  1569. m_pRecipeContainer->SetDialogVariable( "recipetitle", m_pDynamicRecipeItem->GetItemName() );
  1570. // By default, dont show untradable items
  1571. m_pShowUntradableItemsCheckbox->SetSelected( false );
  1572. // Repopulate the panels
  1573. InitItemPanels();
  1574. UpdateModelPanels();
  1575. }
  1576. class CWaitForConsumeDialog : public CGenericWaitingDialog
  1577. {
  1578. public:
  1579. CWaitForConsumeDialog( vgui::Panel *pParent ) : CGenericWaitingDialog( pParent )
  1580. {
  1581. }
  1582. protected:
  1583. virtual void OnTimeout()
  1584. {
  1585. // Play an exciting sound!
  1586. vgui::surface()->PlaySound( "misc/achievement_earned.wav" );
  1587. // Show them their loot!
  1588. InventoryManager()->ShowItemsPickedUp( true );
  1589. }
  1590. };
  1591. void CDynamicRecipePanel::OnCraftResponse( itemid_t nNewToolID, EGCMsgResponse eResponse )
  1592. {
  1593. // We got a response. We dont need to time-out
  1594. m_flAbortCraftingAt = 0;
  1595. switch( eResponse )
  1596. {
  1597. case k_EGCMsgResponseOK:
  1598. {
  1599. // If a new tool id comes back, that means we only partially completed the recipe
  1600. if( nNewToolID != 0 )
  1601. {
  1602. OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Success", false, true, false );
  1603. // Play a sound letting them know the item is gone
  1604. const char *pszSoundFilename = m_pDynamicRecipeItem->GetDefinitionString( "recipe_partial_complete_sound", "ui/chem_set_add_element.wav" );
  1605. vgui::surface()->PlaySound( pszSoundFilename );
  1606. CEconItemView* pNewRecipe = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( nNewToolID );
  1607. Assert( pNewRecipe );
  1608. // Set the new recipe into the panel. This resets all the item panels within.
  1609. SetNewRecipe( pNewRecipe );
  1610. }
  1611. else
  1612. {
  1613. CloseCraftingStatusDialog();
  1614. // No new tool, so we completed the recipe!
  1615. const char *pszSoundFilename = m_pDynamicRecipeItem->GetDefinitionString( "recipe_complete_sound", "ui/chem_set_creation.wav" );
  1616. vgui::surface()->PlaySound( pszSoundFilename );
  1617. // Show the "Completing consumption" dialog for 5 seconds
  1618. ShowWaitingDialog( new CWaitForConsumeDialog( NULL ), "#ToolConsumptionInProgress", true, false, 5.0f );
  1619. // Hide the dynamic recipe panel after 6 seconds
  1620. g_DynamicRecipePanel->PostMessage( g_DynamicRecipePanel, new KeyValues("RecipeCompleted"), 6.f );
  1621. }
  1622. }
  1623. break;
  1624. case k_EGCMsgResponseInvalid:
  1625. {
  1626. // Something was bad in the request.
  1627. OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Invalid", false, true, false );
  1628. InitItemPanels();
  1629. UpdateModelPanels();
  1630. }
  1631. break;
  1632. case k_EGCMsgResponseNoMatch:
  1633. {
  1634. // One or more of the items sent in didnt match
  1635. OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_NoMatch", false, true, false );
  1636. InitItemPanels();
  1637. UpdateModelPanels();
  1638. }
  1639. break;
  1640. default:
  1641. {
  1642. OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Default", false, true, false );
  1643. InitItemPanels();
  1644. UpdateModelPanels();
  1645. }
  1646. }
  1647. }
  1648. //-----------------------------------------------------------------------------
  1649. // Purpose: GC Msg handler to receive the dynamic recipe
  1650. //-----------------------------------------------------------------------------
  1651. class CGCCompleteDynamicRecipeResponse : public GCSDK::CGCClientJob
  1652. {
  1653. public:
  1654. CGCCompleteDynamicRecipeResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  1655. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  1656. {
  1657. GCSDK::CGCMsg<MsgGCStandardResponse_t> msg( pNetPacket );
  1658. itemid_t nNewToolID = INVALID_ITEM_ID;
  1659. if( !msg.BReadUint64Data( &nNewToolID ) )
  1660. return true;
  1661. g_DynamicRecipePanel->OnCraftResponse( nNewToolID, (EGCMsgResponse)msg.Body().m_eResponse );
  1662. return true;
  1663. }
  1664. };
  1665. GC_REG_JOB( GCSDK::CGCClient, CGCCompleteDynamicRecipeResponse, "CGCCompleteDynamicRecipeResponse", k_EMsgGCFulfillDynamicRecipeComponentResponse, GCSDK::k_EServerTypeGCClient );
  1666. #ifdef STAGING_ONLY
  1667. void CDynamicRecipePanel::Debug_GiveRequiredInputs() const
  1668. {
  1669. if ( !steamapicontext || !steamapicontext->SteamUser() )
  1670. {
  1671. Msg("Not connected to Steam.\n");
  1672. return;
  1673. }
  1674. CSteamID steamIDForPlayer = steamapicontext->SteamUser()->GetSteamID();
  1675. if ( !steamIDForPlayer.IsValid() )
  1676. {
  1677. Msg("Failed to find a valid steamID for the local player.\n");
  1678. return;
  1679. }
  1680. FOR_EACH_VEC( m_vecRecipeInputModelPanels, i )
  1681. {
  1682. CInputPanelItemModelPanel *pInputPanel = m_vecRecipeInputModelPanels[i];
  1683. int nPage = 0;
  1684. for( const CEconItemAttributeDefinition *pAttrDef = pInputPanel->GetAttrib( nPage ); pAttrDef != NULL; pAttrDef = pInputPanel->GetAttrib( ++nPage ) )
  1685. {
  1686. CAttribute_DynamicRecipeComponent attribValue;
  1687. if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) )
  1688. {
  1689. GCSDK::CProtoBufMsg<CMsgDevNewItemRequest> msg( k_EMsgGCDev_NewItemRequest );
  1690. msg.Body().set_receiver( steamIDForPlayer.ConvertToUint64() );
  1691. CItemSelectionCriteria criteria;
  1692. if( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
  1693. {
  1694. criteria.BAddCondition( "name", k_EOperator_String_EQ, GetItemSchema()->GetItemDefinition( attribValue.def_index() )->GetDefinitionName(), true );
  1695. }
  1696. if( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET )
  1697. {
  1698. criteria.SetQuality( attribValue.item_quality() );
  1699. }
  1700. criteria.SetIgnoreEnabledFlag( true );
  1701. criteria.BSerializeToMsg( *msg.Body().mutable_criteria() );
  1702. GCClientSystem()->BSendMessage( msg );
  1703. }
  1704. }
  1705. }
  1706. }
  1707. #endif