diff options
Diffstat (limited to 'game/client/tf/vgui/dynamic_recipe_subpanel.cpp')
| -rw-r--r-- | game/client/tf/vgui/dynamic_recipe_subpanel.cpp | 2013 |
1 files changed, 2013 insertions, 0 deletions
diff --git a/game/client/tf/vgui/dynamic_recipe_subpanel.cpp b/game/client/tf/vgui/dynamic_recipe_subpanel.cpp new file mode 100644 index 0000000..35ad19a --- /dev/null +++ b/game/client/tf/vgui/dynamic_recipe_subpanel.cpp @@ -0,0 +1,2013 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "crafting_panel.h" +#include "dynamic_recipe_subpanel.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "c_tf_player.h" +#include "gamestringpool.h" +#include "iclientmode.h" +#include "tf_item_inventory.h" +#include "ienginevgui.h" +#include <vgui/ILocalize.h> +#include "vgui_controls/TextImage.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/ComboBox.h" +#include <vgui_controls/TextEntry.h> +#include "vgui/IInput.h" +#include "gcsdk/gcclient.h" +#include "gcsdk/gcclientjob.h" +#include "character_info_panel.h" +#include "charinfo_loadout_subpanel.h" +#include "econ_item_system.h" +#include "econ_item_constants.h" +#include "tf_hud_notification_panel.h" +#include "tf_hud_chat.h" +#include "c_tf_gamestats.h" +#include "confirm_dialog.h" +#include "econ_notifications.h" +#include "gc_clientsystem.h" +#include "charinfo_loadout_subpanel.h" +#include "item_selection_criteria.h" +#include "rtime.h" +#include "c_tf_freeaccount.h" +#include "econ_dynamic_recipe.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +static CDynamicRecipePanel* g_DynamicRecipePanel = NULL; +extern const char *g_szItemBorders[AE_MAX_TYPES][5]; + +//----------------------------------------------------------------------------- +// Purpose: Default to NULL item +//----------------------------------------------------------------------------- +CRecipeComponentItemModelPanel::CRecipeComponentItemModelPanel( vgui::Panel *parent, const char *name ) + : CItemModelPanel( parent, name ) + , m_nPageNumber( 0 ) +{ + SetItem( NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add recipe to our list of recipes. Each call to this function +// effectively adds an item to the next page +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::AddRecipe( itemid_t nRecipe ) +{ + RecipeItem_t& recipeItem = m_vecRecipes[m_vecRecipes.AddToTail()]; + recipeItem.m_nRecipeIndex = nRecipe; + UpdateRecipeItem( &recipeItem ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Wipe all recipes from all pages +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::DeleteRecipes() +{ + m_vecDefaultItems.Purge(); + m_vecRecipes.Purge(); + SetItem( NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Override to set the item to be our default item if NULL is passed in. +// Also handles greying out the panels +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::SetItem( const CEconItemView *pItem ) +{ + // Use the default item if they set NULL + if( pItem == NULL ) + { + SetBlankState(); + } + else + { + BaseClass::SetItem( pItem ); + SetGreyedOut( NULL ); + } + + InvalidateLayout( true ); +} + + +void CRecipeComponentItemModelPanel::SetBlankState() +{ + CEconItemView* pDefaultItem = NULL; + if( m_nPageNumber < m_vecDefaultItems.Count() ) + pDefaultItem = m_vecDefaultItems[ m_nPageNumber ]; + BaseClass::SetItem( pDefaultItem ); + SetGreyedOut( "" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Move a recipe item to a specific page +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::SetRecipeItem( itemid_t nRecipeItem, int nPageNumber ) +{ + Assert( nPageNumber < m_vecRecipes.Count() ); + m_vecRecipes[ nPageNumber ].m_nRecipeIndex = nRecipeItem; + + UpdateRecipeItem( &m_vecRecipes[ nPageNumber ] ); + + // Use the item that this item ID maps to + SetItem( m_vecRecipes[ nPageNumber ].m_pRecipeItem ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add a default item to a page +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::AddDefaultItem( CEconItemView *pItem ) +{ + m_vecDefaultItems[ m_vecDefaultItems.AddToTail() ] = pItem; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get a recipe item on a given page +//----------------------------------------------------------------------------- +CEconItemView* CRecipeComponentItemModelPanel::GetRecipeItem( int nPageNumber ) const +{ + if( nPageNumber < m_vecRecipes.Count() ) + { + return m_vecRecipes[nPageNumber].m_pRecipeItem; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the recipe index from a specific page +//----------------------------------------------------------------------------- +itemid_t CRecipeComponentItemModelPanel::GetRecipeIndex( int nPageNumber ) const +{ + if( nPageNumber < m_vecRecipes.Count() ) + { + return m_vecRecipes[nPageNumber].m_nRecipeIndex; + } + + return INVALID_ITEM_ID; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Iterate through all attributes on a item and turn recipe attributes +// into input and output items. Store those items in vectors for inputs +// and outputs. Inputs are sorted from least common to most common +// so that the later pages are filled with more repeats than the early pages +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::CRecipeComponentAttributeCounter::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) +{ + static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" ); + Assert( pAttrib_CannotTrade ); + + unsigned nCount = value.num_required() - value.num_fulfilled(); + + if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) + { + CEconItem* pItem = m_vecTempEconItems[m_vecTempEconItems.AddToTail( new CEconItem() )]; + DecodeItemFromEncodedAttributeString( value, pItem ); + + for( unsigned i=0; i < nCount; ++i ) + { + CEconItemView& item = m_vecOutputItems[ m_vecOutputItems.AddToTail() ]; + + item.SetItemDefIndex( pItem->GetDefinitionIndex() ); + item.SetItemQuality( pItem->GetQuality() ); + item.SetItemLevel( pItem->GetItemLevel() ); + item.SetItemID( pItem->GetItemID() ); + item.SetNonSOEconItem( pItem ); // Set the item into the econ item view. + item.SetInitialized( true ); + item.SetItemOriginOverride( kEconItemOrigin_RecipeOutput ); // Spoof where we came from + + // Set the untradable flag if the attribute says so + if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_UNTRADABLE ) + { + item.GetAttributeList()->SetRuntimeAttributeValue( pAttrib_CannotTrade, 0.0f ); // value doesn't matter -- we only check for presence/absence + } + } + } + else + { + m_nInputCount += nCount; + + CEconItem* pItem = m_vecTempEconItems[m_vecTempEconItems.AddToTail( new CEconItem() )]; + DecodeItemFromEncodedAttributeString( value, pItem ); + + CUtlVector<InputComponent_t>& inputSeries = m_vecInputItems[ m_vecInputItems.AddToTail() ]; + for( unsigned i=0; i < nCount; ++i ) + { + InputComponent_t& item = inputSeries[ inputSeries.AddToTail() ]; + item.m_ItemView.SetItemDefIndex( pItem->GetDefinitionIndex() ); + item.m_ItemView.SetItemQuality( pItem->GetQuality() ); + item.m_ItemView.SetItemLevel( 0 ); + item.m_ItemView.SetItemID( pItem->GetItemID() ); + item.m_ItemView.SetNonSOEconItem( pItem ); // Set the item into the econ item view. + item.m_ItemView.SetInitialized( true ); + item.m_ItemView.SetItemOriginOverride( kEconItemOrigin_RecipeOutput ); // Spoof where we came from + item.m_pAttrib = pAttrDef; + } + + m_vecInputItems.Sort( LeastCommonInputSortFunc ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the output item on a given page +//----------------------------------------------------------------------------- +CEconItemView* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetOutputItem( int i ) +{ + if( i >= 0 && i < m_vecOutputItems.Count() ) + return &m_vecOutputItems[i]; + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the input item on a given page +//----------------------------------------------------------------------------- +CEconItemView* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputItem( int i ) +{ + InputComponent_t* pInputComponent = GetInputComponent( i ); + if( pInputComponent ) + { + return &pInputComponent->m_ItemView; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the attribute that the item in a given panel maps to +//----------------------------------------------------------------------------- +const CEconItemAttributeDefinition* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputAttrib( int i ) +{ + InputComponent_t* pInputComponent = GetInputComponent( i ); + if( pInputComponent ) + { + return pInputComponent->m_pAttrib; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Sort input vectors based on count. Fewer first. +//----------------------------------------------------------------------------- +int CDynamicRecipePanel::CRecipeComponentAttributeCounter::LeastCommonInputSortFunc( const CCopyableUtlVector<InputComponent_t> *p1, const CCopyableUtlVector<InputComponent_t> *p2 ) +{ + return p1->Count() > p2->Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Find input component i from our 2D vector of input items +//----------------------------------------------------------------------------- +CDynamicRecipePanel::CRecipeComponentAttributeCounter::InputComponent_t* CDynamicRecipePanel::CRecipeComponentAttributeCounter::GetInputComponent( int i ) +{ + int nAccum = 0; + FOR_EACH_VEC( m_vecInputItems, nIndex ) + { + int nCount = m_vecInputItems[ nIndex ].Count(); + + if( i < nAccum + nCount ) + { + return &m_vecInputItems[ nIndex ][ i - nAccum ]; + } + + nAccum += nCount; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Reset all data in the iterator +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::CRecipeComponentAttributeCounter::Reset() +{ + m_vecInputItems.Purge(); + m_vecOutputItems.Purge(); + m_vecTempEconItems.PurgeAndDeleteElements(); + m_nInputCount = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Compare Check if m_pItemToMatch passes the criteria of any of the +// attributes on m_pSourceItem. +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::CDynamicRecipeItemMatchFind::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) +{ + // Can't match ourself + if( m_pSourceItem && m_pItemToMatch && m_pSourceItem->GetID() == m_pItemToMatch->GetID() ) + return true; + + if( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) + return true; + + if( !DefinedItemAttribMatch( value, m_pItemToMatch ) ) + return true; + + // Must be useable in crafting. Expensive -- do this last. + if( !m_pItemToMatch || !m_pItemToMatch->IsUsableInCrafting() ) + return true; + + // A match! + m_bMatchesAny = true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Delete all recipe data +//----------------------------------------------------------------------------- +void CInputPanelItemModelPanel::DeleteRecipes() +{ + CRecipeComponentItemModelPanel::DeleteRecipes(); + m_vecAttrDef.Purge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add component info to a new page +//----------------------------------------------------------------------------- +void CInputPanelItemModelPanel::AddComponentInfo( const CEconItemAttributeDefinition *pComponentAttrib ) +{ + m_vecAttrDef[ m_vecAttrDef.AddToTail() ] = pComponentAttrib; + AddRecipe( NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if a passed in database item matches the desired item in this +// item panel. Default to the current page +//----------------------------------------------------------------------------- +bool CInputPanelItemModelPanel::MatchesAttribCriteria( itemid_t itemID ) const +{ + return MatchesAttribCriteria( itemID, m_nPageNumber ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Check if a passed in database item matches the desired item in this +// item panel on the specified page. +//----------------------------------------------------------------------------- +bool CInputPanelItemModelPanel::MatchesAttribCriteria( itemid_t itemID, int nPageNumber ) const +{ + if( !m_pDynamicRecipeItem ) + return false; + + CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); + if ( !pLocalInv ) + return false; + + const CEconItemView* pItem = pLocalInv->GetInventoryItemByItemID( itemID ); + + if( !pItem || !pItem->IsUsableInCrafting() ) + return false; + + const CEconItemAttributeDefinition* pAttrDef = GetAttrib( nPageNumber ); + CAttribute_DynamicRecipeComponent attribValue; + if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) ) + { + return DefinedItemAttribMatch( attribValue, pItem ); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the attribute that this panel represents +//----------------------------------------------------------------------------- +const CEconItemAttributeDefinition* CInputPanelItemModelPanel::GetAttrib( int nPageNumber ) const +{ + if( nPageNumber >= 0 && nPageNumber < m_vecAttrDef.Count() ) + return m_vecAttrDef[ nPageNumber ]; + + return NULL; +} + + +void CInputPanelItemModelPanel::SetBlankState() +{ + // Get the default item + CEconItemView* pDefaultItem = NULL; + if( m_nPageNumber < m_vecDefaultItems.Count() ) + pDefaultItem = m_vecDefaultItems[ m_nPageNumber ]; + + // Grey out + SetGreyedOut( "" ); + + // Check for the "item name text override" attribute on the default item + static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" ); + CAttribute_String attrItemNameTextOverride; + if ( pDefaultItem ) + { + pDefaultItem->FindAttribute( pAttrDef_ItemNameTextOverride, &attrItemNameTextOverride ); + + if ( FStrEq( attrItemNameTextOverride.value().c_str(), "#TF_ItemName_Item" ) ) + { + // This is a dummy item. Dont display an icon. Just the name of the item + CItemModelPanel::SetItem( NULL ); + SetAttribOnly( true ); + SetTextYPos( 0 ); + //SetNoItemText( pDefaultItem->GetItemName(), NULL, NULL ); + const wchar_t *pszItemname = pDefaultItem->GetItemName(); + if ( V_wcscmp( pszItemname, g_pVGuiLocalize->Find( "#TF_ItemName_Item" ) ) == 0 && pDefaultItem->GetQuality() == AE_PAINTKITWEAPON ) + { + SetNoItemText( g_pVGuiLocalize->Find( "paintkitweapon" ), NULL, NULL ); + } + else + { + SetNoItemText( pDefaultItem->GetItemName(), NULL, NULL ); + } + } + else + { + BaseClass::SetItem( pDefaultItem ); + } + } + else + { + // Construct an item from the attribute that describes this input + CEconItem tempItem; + const CEconItemAttributeDefinition* pAttrDef = GetAttrib( m_nPageNumber ); + CAttribute_DynamicRecipeComponent attribValue; + if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) ) + { + DecodeItemFromEncodedAttributeString( attribValue, &tempItem ); + } + + // Shove it into an econitemview + CEconItemView tempView; + tempView.Init( tempItem.GetItemDefIndex(), tempItem.GetQuality(), 0 ); + tempView.SetNonSOEconItem( &tempItem ); + + // Set its name as the text for this item model panel + CItemModelPanel::SetItem( NULL ); + SetAttribOnly( true ); + SetTextYPos( 0 ); + SetNoItemText( tempView.GetItemName(), NULL, NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Is this in play +//----------------------------------------------------------------------------- +bool CRecipeComponentItemModelPanel::IsSlotAvailable( int nPageNumber ) +{ + return nPageNumber < m_vecRecipes.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the passed in recipe item and changes the item we show +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::UpdateRecipeItem( RecipeItem_t* pRecipeItem ) +{ + Assert( pRecipeItem ); + + CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); + if ( pLocalInv == NULL ) + return; + + pRecipeItem->m_pRecipeItem = pLocalInv->GetInventoryItemByItemID( pRecipeItem->m_nRecipeIndex ); + SetItem( pRecipeItem->m_pRecipeItem ); +} + +//----------------------------------------------------------------------------- +// Purpose: Update the item we show for the current page +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::UpdateDisplayItem() +{ + if( m_nPageNumber < m_vecRecipes.Count() ) + { + UpdateRecipeItem( &m_vecRecipes[ m_nPageNumber ] ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the current page +//----------------------------------------------------------------------------- +void CRecipeComponentItemModelPanel::SetPageNumber( int nPageNumber ) +{ + Assert( nPageNumber >= 0 ); + m_nPageNumber = nPageNumber; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CDynamicRecipePanel::CDynamicRecipePanel( vgui::Panel *parent, const char *panelName, CEconItemView* pRecipeItem ) + : CBackpackPanel( parent, panelName ) + , m_pDynamicRecipeItem( pRecipeItem ) + , m_pRecipeCraftButton( NULL ) + , m_nNumRecipeItems( 0 ) + , m_bAllRecipePanelsFilled( false ) + , m_bInputPanelsDirty( false ) + , m_nInputPage( 0 ) + , m_nOutputPage( 0 ) + , m_pCurInputPageLabel( NULL ) + , m_pNextInputPageButton( NULL ) + , m_pPrevInputPageButton( NULL ) + , m_flAbortCraftingAt( 0 ) + , m_pMouseOverItemPanel( NULL ) + , m_pNoMatchesLabel( NULL ) + , m_pUntradableOutputsLabel( NULL ) + , m_bShowUntradable( false ) + +{ + g_DynamicRecipePanel = this; + + m_pRecipeContainer = new vgui::EditablePanel( this, "recipecontainer" ); + m_pInventoryContainer = new vgui::EditablePanel( this, "inventorycontainer" ); + m_pShowUntradableItemsCheckbox = new vgui::CheckButton( m_pInventoryContainer, "untradablecheckbox", "#Dynamic_Recipe_Untradable_Checkbox" ); + m_pShowUntradableItemsCheckbox->AddActionSignalTarget( this ); + m_pInputsLabel = new CExLabel( m_pRecipeContainer, "InputLabel", "#Craft_Recipe_Inputs" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CDynamicRecipePanel::~CDynamicRecipePanel( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Get all our controls +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + LoadControlSettings( GetResFile() ); + + // This calls AddNewItemPanel + BaseClass::ApplySchemeSettings( pScheme ); + + CExButton* pCancelButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("CancelButton") ); + if ( pCancelButton ) + pCancelButton->AddActionSignalTarget( this ); + + m_pRecipeCraftButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName("CraftButton") ); + if ( m_pRecipeCraftButton ) + m_pRecipeCraftButton->AddActionSignalTarget( this ); + + m_pNextPageButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("NextPageButton") ); + if( m_pNextPageButton ) + m_pNextPageButton->AddActionSignalTarget( this ); + + m_pPrevPageButton = dynamic_cast<CExButton*>( m_pInventoryContainer->FindChildByName("PrevPageButton") ); + if( m_pPrevPageButton ) + m_pPrevPageButton->AddActionSignalTarget( this ); + + m_pCurPageLabel = dynamic_cast<vgui::Label*>( m_pInventoryContainer->FindChildByName("CurPageLabel") ); + Assert( m_pCurPageLabel ); + + m_pNoMatchesLabel = dynamic_cast<CExLabel*>( m_pInventoryContainer->FindChildByName( "NoMatches" ) ); + Assert( m_pNoMatchesLabel ); + + m_pUntradableOutputsLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "UntradableLabel" ) ); + Assert( m_pUntradableOutputsLabel ); + + m_pOutputsLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "OutputLabel" ) ); + Assert( m_pOutputsLabel ); + + m_pCurInputPageLabel = dynamic_cast<CExLabel*>( m_pRecipeContainer->FindChildByName( "CurInputPageLabel" ) ); + + m_pNextInputPageButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName( "NextInputPageButton" ) ); + if( m_pNextInputPageButton ) + m_pNextInputPageButton->AddActionSignalTarget( this ); + + m_pPrevInputPageButton = dynamic_cast<CExButton*>( m_pRecipeContainer->FindChildByName( "PrevInputPageButton" ) ); + if( m_pPrevInputPageButton ) + m_pPrevInputPageButton->AddActionSignalTarget( this ); + +#ifdef STAGING_ONLY + m_pDevGiveInputsButton = new CExButton( m_pInventoryContainer, "dev_giveinputsbutton", "[Debug] Give Inputs", this, "dev_giveinputs" ); + m_pDevGiveInputsButton->SetEnabled( true ); + m_pDevGiveInputsButton->SetVisible( true ); +#endif + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Update all the item panels and page buttons and labels +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + +#ifdef STAGING_ONLY + if( m_pDevGiveInputsButton ) + { + m_pDevGiveInputsButton->SetPos( XRES(0) , YRES(290) ); + m_pDevGiveInputsButton->SetWide( 210 ); + m_pDevGiveInputsButton->SetTall( 40 ); + m_pDevGiveInputsButton->MoveToFront(); + m_pDevGiveInputsButton->SetEnabled( true ); + m_pDevGiveInputsButton->SetVisible( true ); + } +#endif + + if( m_pSortByComboBox ) + { + m_pSortByComboBox->SetVisible( false ); + } + + UpdateModelPanels(); + + bool bNoMatches = m_nNumRecipeItems == 0; + bool bMultiplePagesOfMatches = m_nNumRecipeItems > (unsigned)GetNumBackpackPanelsPerPage(); + // Some panels show and hide based on the number of matches + if( m_pNoMatchesLabel) + m_pNoMatchesLabel->SetVisible( bNoMatches ); + if( m_pNextPageButton ) + m_pNextPageButton->SetVisible( bMultiplePagesOfMatches ); + if( m_pPrevPageButton ) + m_pPrevPageButton->SetVisible( bMultiplePagesOfMatches ); + if( m_pCurPageLabel ) + m_pCurPageLabel->SetVisible( bMultiplePagesOfMatches ); + + bool bMultiplePagesOfInputs = m_RecipeIterator.GetInputCount() > GetNumInputPanelsPerPage(); + if( m_pNextInputPageButton ) + m_pNextInputPageButton->SetVisible( bMultiplePagesOfInputs ); + if( m_pNextInputPageButton ) + m_pNextInputPageButton->SetEnabled( m_nInputPage < GetNumInputPages() - 1 ); + if( m_pCurInputPageLabel ) + m_pCurInputPageLabel->SetVisible( bMultiplePagesOfInputs ); + if( m_pPrevInputPageButton ) + m_pPrevInputPageButton->SetVisible( bMultiplePagesOfInputs ); + if( m_pPrevInputPageButton ) + m_pPrevInputPageButton->SetEnabled( m_nInputPage > 0 ); + + if( m_pUntradableOutputsLabel ) + m_pUntradableOutputsLabel->SetVisible( m_pDynamicRecipeItem ? !m_pDynamicRecipeItem->IsTradable() : false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle commands +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnCommand( const char *command ) +{ + if ( !Q_strnicmp( command, "back", 4 ) ) + { + PostMessage( GetParent(), new KeyValues("CraftingClosed") ); + return; + } + else if ( !Q_strnicmp( command, "craft", 5 ) ) + { + // Check if we should warn about partial completion + if( WarnAboutPartialCompletion() ) + { + if( CheckForUntradableItems() ) + { + Craft(); + } + } + + return; + } + else if ( !Q_stricmp( command, "reloadscheme" ) ) + { + InvalidateLayout( true, true ); // deliberatly fallthrough to baseclass + } + else if( !Q_stricmp( command, "cancel" ) ) + { + SetVisible( false ); + return; + } + else if ( !Q_strnicmp( command, "nextpage", 8 ) ) + { + InvalidateLayout(); // deliberatly fallthrough to baseclass + } + else if ( !Q_strnicmp( command, "prevpage", 8 ) ) + { + InvalidateLayout(); // deliberatly fallthrough to baseclass + } + else if( !Q_strnicmp( command, "nextinputpage", 13 ) ) + { + if( m_nInputPage < GetNumInputPages() ) + m_nInputPage++; + InvalidateLayout(); + return; + } + else if( !Q_strnicmp( command, "previnputpage", 13 ) ) + { + if( m_nInputPage > 0 ) + m_nInputPage--; + InvalidateLayout(); + return; + } + else if( !Q_strnicmp( command, "deleteitem", 10 ) || + !Q_strnicmp( command, "useitem", 7 ) ) + { + // Gobble up these commands + return; + } +#ifdef STAGING_ONLY + else if( !Q_strnicmp( command, "dev_giveinputs", 14 ) ) + { + Debug_GiveRequiredInputs(); + if( m_pDevGiveInputsButton ) + m_pDevGiveInputsButton->SetEnabled( false ); + } +#endif + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gobble up all keyboard input for now! +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnKeyCodePressed( vgui::KeyCode /*code*/ ) +{ + return; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnButtonChecked( KeyValues *pData ) +{ + Panel *pPanel = reinterpret_cast<vgui::Panel *>( pData->GetPtr("panel") ); + + if ( m_pShowUntradableItemsCheckbox == pPanel ) + { + if ( m_bShowUntradable != m_pShowUntradableItemsCheckbox->IsSelected() ) + { + m_bShowUntradable = m_pShowUntradableItemsCheckbox->IsSelected(); + InitItemPanels(); + UpdateModelPanels(); + } + } +} + + +int CDynamicRecipePanel::GetNumItemPanels( void ) +{ + return DYNAMIC_RECIPE_INPUT_COUNT + DYNAMIC_RECIPE_OUTPUT_COUNT + DYNAMIC_RECIPE_PACKPACK_COUNT_PER_PAGE; +} + +//----------------------------------------------------------------------------- +// Purpose: Check to see that each and every input panel has a recipe item in it +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::AllRecipePanelsFilled( void ) +{ + // Need to recalculate + if( m_bInputPanelsDirty ) + { + // Assume all filled, and go through and try to find one that's empty + m_bAllRecipePanelsFilled = true; + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel* pInputPanel = m_vecRecipeInputModelPanels[i]; + for( int j=0; j < GetNumInputPages(); ++j ) + { + if( pInputPanel->GetRecipeItem( j ) == NULL && pInputPanel->GetAttrib( j ) != NULL ) + { + m_bAllRecipePanelsFilled = false; + break; + } + } + } + } + + return m_bAllRecipePanelsFilled; +} + + +//----------------------------------------------------------------------------- +// Purpose: Callback for the confirm partial completion dialog +//----------------------------------------------------------------------------- +void ConfirmDestroyItems( bool bConfirmed, void* pContext ) +{ + CDynamicRecipePanel *pRecipePanel = ( CDynamicRecipePanel* )pContext; + if ( pRecipePanel && bConfirmed ) + { + if( pRecipePanel->CheckForUntradableItems() ) + { + pRecipePanel->Craft(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Callback for the conrim untradable dialog +//----------------------------------------------------------------------------- +static void ConfirmUntradableCraft( bool bConfirmed, void* pContext ) +{ + CDynamicRecipePanel *pRecipePanel = ( CDynamicRecipePanel* )pContext; + if ( pRecipePanel && bConfirmed ) + { + pRecipePanel->Craft(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Go through each input panel and find out if any of them contain an +// item that is untradeable +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::CheckForUntradableItems( void ) +{ + // We dont care if the recipe itself is already not tradable + if( !m_pDynamicRecipeItem->IsTradable() ) + { + return true; + } + + bool bHasUntradable = false; + for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; ++i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i]; + for( int j = 0; j < GetNumInputPages(); ++j ) + { + itemid_t nRecipeItemID = pPanel->GetRecipeIndex( j ); + + if ( nRecipeItemID != 0 && nRecipeItemID != INVALID_ITEM_ID ) + { + CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( nRecipeItemID ); + if ( pItemData->IsTradable() == false ) + { + bHasUntradable = true; + break; + } + } + } + } + + if ( bHasUntradable ) + { + enum { kWarningLength = 512 }; + locchar_t wszWarning[ kWarningLength ] = LOCCHAR(""); + + loc_scpy_safe( wszWarning, + CConstructLocalizedString( GLocalizationProvider()->Find( "Dynamic_Recipe_Untradable_Text" ), + m_pDynamicRecipeItem->GetItemName() ) ); + + CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#Craft_Untradable_Title", wszWarning, "#GameUI_OK", "#Cancel", &ConfirmUntradableCraft, NULL ); + + if ( pDialog ) + { + pDialog->SetContext( this ); + pDialog->Show(); + } + + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Check if not all of the inputs have items set into them. Put up +// a prompt and return false if so. +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::WarnAboutPartialCompletion( void ) +{ + if( !AllRecipePanelsFilled() ) + { + enum { kWarningLength = 512 }; + locchar_t wszWarning[ kWarningLength ] = LOCCHAR(""); + + loc_scpy_safe( wszWarning, + CConstructLocalizedString( GLocalizationProvider()->Find( "Dynamic_Recipe_Partial_Completion_Warning" ), + m_pDynamicRecipeItem->GetItemName() ) ); + + CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#Craft_Untradable_Title", wszWarning, "#GameUI_OK", "#Cancel", &ConfirmDestroyItems, NULL ); + if ( pDialog ) + { + pDialog->SetContext( this ); + pDialog->Show(); + } + + + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: They hit the craft button! Cook up a message that we're going to send +// to the GC that contains all of the item_ids and attributes they are +// to apply to. Open a crafting status dialog when after we send the message. +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::Craft() +{ + GCSDK::CProtoBufMsg<CMsgFulfillDynamicRecipeComponent> msg( k_EMsgGCFulfillDynamicRecipeComponent ); + msg.Body().set_tool_item_id( m_pDynamicRecipeItem->GetItemID() ); + + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i]; + + for( int j=0; j < GetNumInputPages(); ++j ) + { + itemid_t nRecipeItemID = pPanel->GetRecipeIndex( j ); + + if( nRecipeItemID != 0 && nRecipeItemID != INVALID_ITEM_ID ) + { + if( !pPanel->MatchesAttribCriteria( nRecipeItemID, j ) ) + { + AssertMsg( 0, "Input panel has recipe item that does not pass its criteria" ); + // Something bad happened. Return this item to the backpack. + ReturnRecipeItemToBackpack( nRecipeItemID, pPanel, j ); + continue; + } + + // Add component + CMsgRecipeComponent* pComponent = msg.Body().add_consumption_components(); + pComponent->set_subject_item_id( nRecipeItemID ); + + const CEconItemAttributeDefinition* pAttribute = pPanel->GetAttrib( j ); + if( !pAttribute ) + { + AssertMsg( 0, "NULL attribute in panel what attempting to craft" ); + // Something bad happened. Return this item to the backpack. + ReturnRecipeItemToBackpack( nRecipeItemID, pPanel, j ); + continue; + } + pComponent->set_attribute_index( pAttribute->GetDefinitionIndex() ); + } + } + } + + EconUI()->Gamestats_ItemTransaction( IE_ITEM_USED_TOOL, m_pDynamicRecipeItem, "consumed_item" ); + GCClientSystem()->BSendMessage( msg ); + + // Open a craft status window to take focus away and let the user know something is happening + OpenCraftingStatusDialog( this, "#CraftUpdate_Start", true, false, false ); + m_flAbortCraftingAt = vgui::system()->GetCurrentTime() + 10; + vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Think when we're waiting for a craft response +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnTick( void ) +{ + BaseClass::OnTick(); + + if( IsVisible() ) + { + if( m_flAbortCraftingAt != 0.f ) + { + // Timeout for crafting. Let them know we failed. + if( m_flAbortCraftingAt < vgui::system()->GetCurrentTime() ) + { + OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false ); + m_flAbortCraftingAt = 0; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Close the craft status window if we close +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnShowPanel( bool bVisible, bool bReturningFromArmory ) +{ + if ( !bVisible ) + { + CloseCraftingStatusDialog(); + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + } + + BaseClass::OnShowPanel( bVisible, bReturningFromArmory ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add the new panels into vectors for each group, and set parents to +// appropriate frames +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::AddNewItemPanel( int iPanelIndex ) +{ + if( IsInputPanel( iPanelIndex ) ) + { + // Input item + int nIndex = m_vecRecipeInputModelPanels.AddToTail(); + CInputPanelItemModelPanel* pPanel = vgui::SETUP_PANEL( new CInputPanelItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex), m_pDynamicRecipeItem )); + m_pItemModelPanels.AddToTail( pPanel ); + pPanel->SetParent( m_pRecipeContainer ); + m_vecRecipeInputModelPanels[nIndex] = pPanel; + } + else if( IsOutputPanel( iPanelIndex ) ) + { + // Output item + CItemModelPanel* pPanel = vgui::SETUP_PANEL( new CItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex) ) ); + m_vecRecipeOutputModelPanels.AddToTail( pPanel ); + m_pItemModelPanels.AddToTail( pPanel ); + pPanel->SetParent( m_pRecipeContainer ); + } + else + { + // Inventory item + int nIndex = m_vecBackpackModelPanels.AddToTail(); + CRecipeComponentItemModelPanel* pPanel = vgui::SETUP_PANEL( new CRecipeComponentItemModelPanel( this, VarArgs("modelpanel%d", iPanelIndex) ) ); + m_vecBackpackModelPanels[nIndex] = pPanel; + m_pItemModelPanels.AddToTail( pPanel ); + pPanel->SetParent( m_pInventoryContainer ); + } + + CItemModelPanel* pPanel = m_pItemModelPanels.Tail(); + pPanel->SetActAsButton( true, true ); + pPanel->SetTooltip( m_pMouseOverTooltip, "" ); + pPanel->SetShowGreyedOutTooltip( true ); + pPanel->SetShowEquipped( true ); + + // Store a position for our new panel + m_ItemModelPanelPos.AddToTail(); + m_ItemModelPanelPos[iPanelIndex].x = m_ItemModelPanelPos[iPanelIndex].y = 0; + + Assert( iPanelIndex == (m_pItemModelPanels.Count()-1) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Is this index an input panel +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::IsInputPanel( int iPanelIndex ) const +{ + return iPanelIndex < DYNAMIC_RECIPE_INPUT_COUNT; +} + +//----------------------------------------------------------------------------- +// Purpose: Is this index an output panel +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::IsOutputPanel( int iPanelIndex) const +{ + return iPanelIndex < ( DYNAMIC_RECIPE_OUTPUT_COUNT + DYNAMIC_RECIPE_INPUT_COUNT ) && !IsInputPanel(iPanelIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: Is this index a backpack panel +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::IsBackpackPanel( int iPanelIndex ) const +{ + return ( iPanelIndex >= ( DYNAMIC_RECIPE_INPUT_COUNT + DYNAMIC_RECIPE_OUTPUT_COUNT ) + && !IsInputPanel( iPanelIndex ) + && !IsOutputPanel( iPanelIndex ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Is this backpack panel on this page +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::IsInvPanelOnThisPage( unsigned nIndex ) const +{ + unsigned nNumPerPage = DYNAMIC_RECIPE_BACKPACK_ROWS * DYNAMIC_RECIPE_BACKPACK_COLS; + unsigned nMinIndex = nNumPerPage * GetCurrentPage(); + unsigned nMaxIndex = nNumPerPage * (GetCurrentPage() + 1); + + return nIndex >= nMinIndex && nIndex < nMaxIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Given the number of items that match the recipe, how many pages +// do we need to show +//----------------------------------------------------------------------------- +int CDynamicRecipePanel::GetNumPages() +{ + return ceil( float(m_nNumRecipeItems) / float(GetNumBackpackPanelsPerPage()) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the page of the backpack panels +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::SetCurrentPage( int nNewPage ) +{ + if ( nNewPage < 0 ) + { + nNewPage = GetNumPages() - 1; + } + else if ( nNewPage >= GetNumPages() ) + { + nNewPage = 0; + } + + FOR_EACH_VEC( m_vecBackpackModelPanels, i ) + { + m_vecBackpackModelPanels[i]->SetPageNumber( nNewPage ); + } + + BaseClass::SetCurrentPage( nNewPage ); +} + +//----------------------------------------------------------------------------- +// Purpose: Chance the current page for all input panels +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::SetCurrentInputPage( int nNewPage ) +{ + m_nInputPage = nNewPage; + + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + m_vecRecipeInputModelPanels[i]->SetPageNumber( nNewPage ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Given the number of input items, how many input pages do we need to show +//----------------------------------------------------------------------------- +int CDynamicRecipePanel::GetNumInputPages() const +{ + return ceil( float(m_RecipeIterator.GetInputCount()) / float(GetNumInputPanelsPerPage()) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given the number of output items, how many output pages do we need to show +//----------------------------------------------------------------------------- +int CDynamicRecipePanel::GetNumOutputPage() const +{ + return ceil( float(m_RecipeIterator.GetOutputCount()) / float(GetNumOutputPanelsPerPage()) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Reset all of the item panels, their pages, their info and re-evaluate +// the recipe item for attributes and the backpack for matching items +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::InitItemPanels() +{ + // Clear out every panel's items + FOR_EACH_VEC( m_pItemModelPanels, i ) + { + m_pItemModelPanels[i]->SetItem( NULL ); + } + + // Go through and set the item to all inventory panels to NULL + FOR_EACH_VEC( m_vecBackpackModelPanels, i ) + { + m_vecBackpackModelPanels[i]->DeleteRecipes(); + } + + // Go through our backpack and repopulate our backpack and matching components + FindPossibleBackpackItems(); + + // Reset to the first page + SetCurrentInputPage( 0 ); + SetCurrentPage( 0 ); + + // Go through and set default items on input panels + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i]; + + // Clear out any recipes we have + pPanel->DeleteRecipes(); + // Clear out stale info + pPanel->SetDynamicRecipeItem( m_pDynamicRecipeItem ); + } + + for( int i=0; i < m_RecipeIterator.GetInputCount(); ++i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[ i % m_vecRecipeInputModelPanels.Count() ]; + + pPanel->AddDefaultItem( m_RecipeIterator.GetInputItem( i ) ); + pPanel->AddComponentInfo( m_RecipeIterator.GetInputAttrib( i ) ); + } + + // Go through and set items into output panels + FOR_EACH_VEC( m_vecRecipeOutputModelPanels, i ) + { + CItemModelPanel* pPanel = m_vecRecipeOutputModelPanels[i]; + + // Set appropriate item for output panel + if( i < m_RecipeIterator.GetOutputCount() ) + { + int nOutputIndex = (m_nOutputPage * DYNAMIC_RECIPE_OUTPUT_COUNT) + i; + pPanel->SetItem( m_RecipeIterator.GetOutputItem( nOutputIndex ) ); + } + } + + // Go through all the recipe items and set them into inventory panels + PopulatePanelsForCurrentPage(); + + UpdateModelPanels(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fills in the backpack slots with the items for the current page +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::PopulatePanelsForCurrentPage() +{ + // Go through all the recipe items and set them into inventory panels + FOR_EACH_VEC( m_vecBackpackModelPanels, i ) + { + CRecipeComponentItemModelPanel* pPanel = m_vecBackpackModelPanels[i]; + // Update the item we display. Our inventory might have shifted around + pPanel->UpdateDisplayItem(); + + bool bIsSlotOpen = pPanel->IsSlotAvailable( GetCurrentPage() ); + pPanel->SetVisible( bIsSlotOpen ); + pPanel->SetEnabled( bIsSlotOpen ); + pPanel->InvalidateLayout(); + SetBorderForItem( pPanel, false ); + } + + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i]; + pPanel->SetPageNumber( m_nInputPage ); + pPanel->UpdateDisplayItem(); + + bool bIsSlotOpen = pPanel->IsSlotAvailable( m_nInputPage ); + pPanel->SetVisible( bIsSlotOpen ); + pPanel->SetEnabled( bIsSlotOpen ); + pPanel->InvalidateLayout(); + SetBorderForItem( pPanel, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Go through the player's entire backpack and check if each of them +// passes any of the input panel's attributes criteria +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::FindPossibleBackpackItems() +{ + Assert( m_pDynamicRecipeItem->GetSOCData() ); + // Iterate through the attributes on our recipe item + // and create all of our input and output items. + m_RecipeIterator.Reset(); + CEconItem* pEconItem = m_pDynamicRecipeItem->GetSOCData() ; + pEconItem->IterateAttributes( &m_RecipeIterator ); + + CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); + if ( pLocalInv == NULL ) + return; + + m_nNumRecipeItems = 0; + + // Go through our backpack and filter items that match our input criteria + for ( int i = 0 ; i < pLocalInv->GetItemCount() ; ++i ) + { + CEconItemView *pItem = pLocalInv->GetItem( i ); + Assert( pItem ); + + // If we're not showing untradable items, and this item is untradeable + // then we just skip it + if ( !pItem->IsTradable() && !m_bShowUntradable ) + continue; + + CDynamicRecipeItemMatchFind matchingIterator( m_pDynamicRecipeItem, pItem ); + m_pDynamicRecipeItem->IterateAttributes( &matchingIterator ); + if( matchingIterator.MatchesAnyAttributes() ) + { + // Set in the recipe + m_vecBackpackModelPanels[ m_nNumRecipeItems % GetNumBackpackPanelsPerPage() ]->AddRecipe( pItem->GetItemID() ); + ++m_nNumRecipeItems; + } + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the item panels +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex ) +{ + int iCenter = 0; + int iButtonX = 0, iButtonY = 0, iXPos = 0, iYPos = 0; + + // Position all of the panels + if( IsInputPanel( iIndex ) ) + { + iButtonX = (iIndex % CRAFTING_SLOTS_INPUT_COLUMNS); + iButtonY = (iIndex / CRAFTING_SLOTS_INPUT_COLUMNS); + iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX); + iYPos = m_iItemYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); + + pPanel->SetPos( iXPos, iYPos ); + } + else if( IsOutputPanel( iIndex ) ) + { + int iButtonIndex = iIndex - DYNAMIC_RECIPE_INPUT_COUNT; + iButtonX = (iButtonIndex % CRAFTING_SLOTS_OUTPUT_COLUMNS); + iButtonY = (iButtonIndex / CRAFTING_SLOTS_OUTPUT_COLUMNS); + iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX); + iYPos = m_iOutputItemYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); + + pPanel->SetPos( iXPos, iYPos ); + pPanel->SetItem( m_RecipeIterator.GetOutputItem( iButtonIndex ) ); + } + else if( IsBackpackPanel( iIndex ) ) + { + int iButtonIndex = iIndex - DYNAMIC_RECIPE_INPUT_COUNT - DYNAMIC_RECIPE_OUTPUT_COUNT; + iButtonX = (iButtonIndex % DYNAMIC_RECIPE_BACKPACK_COLS); + iButtonY = (iButtonIndex / DYNAMIC_RECIPE_BACKPACK_COLS); + iXPos = (iCenter + m_iInventoryXPos) + (iButtonX * pPanel->GetWide()) + (m_iItemBackpackXDelta * iButtonX); + iYPos = m_iInventoryYPos + (iButtonY * pPanel->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); + + pPanel->SetPos( iXPos, iYPos ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Update each panel to see if it should show, what item to show, and if +// it's greyed out. Update the page labels here too. +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::UpdateModelPanels( void ) +{ + // Check if any input panels have recipe items in them + bool bAnyInputHasRecipe = false; + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel* pPanel = m_vecRecipeInputModelPanels[i]; + + // Update the item we display. Our inventory might have shifted around + pPanel->UpdateDisplayItem(); + + SetBorderForItem( pPanel, false ); + // Check for a recipe + for( int j=0; j < GetNumInputPages(); ++j ) + { + CEconItemView* pRecipe = pPanel->GetRecipeItem( j ); + bAnyInputHasRecipe |= pRecipe != NULL; + } + // Input panels are visible and enabled if their recipe slot is available + pPanel->SetEnabled( pPanel->GetDefaultItem() != NULL ); + pPanel->SetVisible( pPanel->GetDefaultItem() != NULL ); + } + + static CSchemaAttributeDefHandle pAttrib_NoPartialComplete( "recipe no partial complete" ); + bool bPartialCompletionAllowed = !m_pDynamicRecipeItem->FindAttribute( pAttrib_NoPartialComplete ); + + m_pInputsLabel->SetText( bPartialCompletionAllowed ? "#Craft_Recipe_Inputs" : "#Dynamic_Recipe_Outputs_No_Partial_Complete" ); + + // If any of the input panels have a recipe in them, then crafting is available + if ( m_pRecipeCraftButton ) + { + bool bCraftEnabled = ( bPartialCompletionAllowed && bAnyInputHasRecipe ) || AllRecipePanelsFilled(); + m_pRecipeCraftButton->SetEnabled( bCraftEnabled ); + } + if ( m_pRecipeCraftButton ) + { + m_pRecipeCraftButton->SetText( AllRecipePanelsFilled() ? "#CraftConfirm" : "#ToolCustomizeTextureOKButton" ); + } + if ( m_pOutputsLabel ) + { + m_pOutputsLabel->SetText( AllRecipePanelsFilled() ? "#Craft_Recipe_Outputs" : "#Dynamic_Recipe_Outputs_Not_Complete"); + m_pOutputsLabel->SetFgColor( AllRecipePanelsFilled() ? Color( 200, 80, 60, 255 ) : Color( 117, 107, 94, 255 ) ); + } + + FOR_EACH_VEC( m_vecRecipeOutputModelPanels, i ) + { + CItemModelPanel* pPanel = m_vecRecipeOutputModelPanels[i]; + SetBorderForItem( pPanel, false ); + + // Output panels are visible and enabled if they have an item in them + pPanel->SetVisible( pPanel->GetItem() != NULL ); + pPanel->SetEnabled( pPanel->GetItem() != NULL ); + } + + PopulatePanelsForCurrentPage(); + + // Update the current backpack page numbers + char szTmp[16]; + Q_snprintf(szTmp, 16, "%d/%d", GetCurrentPage() + 1, GetNumPages() ); + m_pInventoryContainer->SetDialogVariable( "backpackpage", szTmp ); + + // Update the current input page numbers + Q_snprintf(szTmp, 16, "%d/%d", m_nInputPage + 1, GetNumInputPages() ); + m_pRecipeContainer->SetDialogVariable( "inputpage", szTmp ); + + DeSelectAllBackpackItemPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: If this is a backpack panel, send the item into the first accepting +// input pane, if one exists, or else do nothing. If this is a input +// panel with a backpack item in it, send the backpack item back to the +// backpack. +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnItemPanelMouseDoublePressed( vgui::Panel *panel ) +{ + CRecipeComponentItemModelPanel *pSrcPanel = dynamic_cast < CRecipeComponentItemModelPanel * > ( panel ); + if( !pSrcPanel ) + return; + + int iIndex = GetBackpackPositionForPanel( pSrcPanel ); + + // Send from an input panel to the first open backpack panel, even on a different page + if( IsInputPanel( iIndex ) ) + { + CInputPanelItemModelPanel *pInputPanel = assert_cast<CInputPanelItemModelPanel*>( pSrcPanel ); + Assert( pInputPanel ); + + int nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pInputPanel->GetPageNumber() ); + CEconItemView* pRecipeItem = pInputPanel->GetRecipeItem( pInputPanel->GetPageNumber() ); + if( pRecipeItem ) + { + // Just put it in the first open slot. + ReturnRecipeItemToBackpack( nSrcRecipeIndex, pInputPanel, pInputPanel->GetPageNumber() ); + } + } + else if( IsBackpackPanel( iIndex ) ) + { + // Sending from the backpack panel to the first matching input panel that's on the current input page. + int nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( GetCurrentPage() ); + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel *pDstPanel = m_vecRecipeInputModelPanels[i]; + if( pDstPanel->GetRecipeItem( pDstPanel->GetPageNumber() ) == NULL && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) ) + { + // Next check if we just want to move the item + SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, pDstPanel->GetPageNumber() ); + break; + } + } + } + + m_bInputPanelsDirty = true; + // Force panels to update + UpdateModelPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: Border highlight if the panel has an item in it +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnItemPanelEntered( vgui::Panel *panel ) +{ + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + m_pMouseOverItemPanel = pItemPanel; + + CEconItemView *pItem = pItemPanel->GetItem(); + + // Recalc the borders on item panels + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + SetBorderForItem( m_vecRecipeInputModelPanels[i], false ); + } + + if ( pItemPanel && IsVisible() ) + { + SetBorderForItem( pItemPanel, pItem != NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Turn of border highlights +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnItemPanelExited( vgui::Panel *panel ) +{ + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + m_pMouseOverItemPanel = NULL; + + // Recalc the borders on item panels + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + SetBorderForItem( m_vecRecipeInputModelPanels[i], false ); + } + + if ( pItemPanel && !pItemPanel->IsSelected() ) + { + SetBorderForItem( pItemPanel, false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: We got a craft response! Close out this window because we're going +// to show the user their loot +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::OnRecipeCompleted() +{ + SetVisible( false ); + m_flAbortCraftingAt = 0.f; + +} + + +//----------------------------------------------------------------------------- +// Purpose: Set the border for the item. Input panels highlight when a dragged +// item matches its criteria. Output items highlight when all inputs +// are fulfilled. +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver ) +{ + if ( !pItemPanel || !pItemPanel->IsVisible() ) + return; + + int iIndex = GetBackpackPositionForPanel( pItemPanel ); + if( IsInputPanel( iIndex ) ) + { + // Special case for input panels. They need to highlight when a dragged item + // matches their criteria. + CItemModelPanel* pPanel = m_bDragging ? m_pMouseDragItemPanel : m_pMouseOverItemPanel; + CEconItemView* pPanelItem = pPanel ? pPanel->GetItem() : NULL; + + CInputPanelItemModelPanel* pInputPanel = dynamic_cast<CInputPanelItemModelPanel*>( pItemPanel ); + Assert( pInputPanel ); + // If this panel doesnt have a recipe, then we want some sort of greyed out look + if( pInputPanel->GetRecipeItem( pInputPanel->GetPageNumber() ) == NULL ) + { + const char *pszBorder = NULL; + int iRarity = GetItemQualityForBorder( pItemPanel ); + + // We only want backpack panels and the drag panel to be highlight sources + bool bValidHighlightSourcePanel = pPanel == m_pMouseDragItemPanel + || m_vecBackpackModelPanels.Find( static_cast<CRecipeComponentItemModelPanel*>(pPanel) ) != m_vecBackpackModelPanels.InvalidIndex(); + + // If this panel can accept whatever the source panel is, we want to highlight a bit + if( bValidHighlightSourcePanel && pPanelItem && pInputPanel->MatchesAttribCriteria( pPanelItem->GetItemID() ) ) + { + if ( pItemPanel->GetItem() ) + { + pszBorder = g_szItemBorders[iRarity][3]; + pItemPanel->SetGreyedOut( NULL ); + } + else + { + pszBorder = g_szItemBorders[iRarity][0]; + } + } + else // Look gloomy and uninviting if not + { + pszBorder = g_szItemBorders[iRarity][3]; + pItemPanel->SetGreyedOut( "" ); + } + + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) ); + return; + } + + } + else if( IsOutputPanel( iIndex ) ) + { + const char *pszBorder = NULL; + int iRarity = GetItemQualityForBorder( pItemPanel ); + + // The output panel greys out when any of the input panels are not fulfilled, + // and lights up when they are all fulfilled + if ( iRarity >= 0 && iRarity < ARRAYSIZE( g_szItemBorders ) ) + { + if( AllRecipePanelsFilled() ) + { + pszBorder = g_szItemBorders[iRarity][0]; + pItemPanel->SetGreyedOut( NULL ); + } + else + { + pszBorder = g_szItemBorders[iRarity][3]; + pItemPanel->SetGreyedOut( NULL ); + } + } + + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) ); + return; + } + + // Backpack and output panels get default treatment + BaseClass::SetBorderForItem( pItemPanel, bMouseOver ); +} + +//----------------------------------------------------------------------------- +// Purpose: Swap recipe items in srcpanel and dstpanel +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::SetRecipeComponentIntoPanel( itemid_t nSrcRecipeIndex, CRecipeComponentItemModelPanel* pSrcPanel, int nSrcPage, CRecipeComponentItemModelPanel* pDstPanel, int nDstPage ) +{ + Assert( nSrcRecipeIndex != 0 ); + Assert( pSrcPanel ); + Assert( pDstPanel ); + Assert( pSrcPanel->GetRecipeItem( nSrcPage ) ); + + // Get the recipe from the destination panel + itemid_t pDstRecipeIndex = pDstPanel->GetRecipeIndex( nDstPage ); + // Swap recipes + pSrcPanel->SetRecipeItem( pDstRecipeIndex, nSrcPage ); + pDstPanel->SetRecipeItem( nSrcRecipeIndex, nDstPage ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if a given panel is allowed to be dragged. +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::AllowDragging( CItemModelPanel *pPanel ) +{ + int iIndex = GetBackpackPositionForPanel( pPanel ); + + // If this isn't an input or backpack panel, abort + if( !IsInputPanel( iIndex ) && !IsBackpackPanel( iIndex ) ) + return false; + + CRecipeComponentItemModelPanel* pRecipePanel = dynamic_cast< CRecipeComponentItemModelPanel* >( pPanel ); + Assert( pRecipePanel ); + if( !pRecipePanel ) + return false; + + int nPageNumber = 0; + if( IsBackpackPanel( iIndex ) ) + { + nPageNumber = GetCurrentPage(); + } + else if ( IsInputPanel( iIndex ) ) + { + nPageNumber = m_nInputPage; + } + // Get the recipe item out of this panel + CEconItemView* pRecipe = pRecipePanel->GetRecipeItem( nPageNumber ); + + // If no recipe, abort + if( !pRecipe ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Start dragging! +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::StartDrag( int x, int y ) +{ + BaseClass::StartDrag( x, y ); + + // Recalc the borders on item panels + FOR_EACH_VEC( m_pItemModelPanels, i ) + { + SetBorderForItem( m_pItemModelPanels[i], false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Stop dragging +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::StopDrag( bool bSucceeded ) +{ + BaseClass::StopDrag( bSucceeded ); + + // Recalc the borders on item panels + FOR_EACH_VEC( m_pItemModelPanels, i ) + { + SetBorderForItem( m_pItemModelPanels[i], false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Helper function to find out if an input panel can accept an item +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::InputPanelCanAcceptItem( CItemModelPanel* pPanel, itemid_t nItemID ) +{ + Assert( IsInputPanel( GetBackpackPositionForPanel( pPanel ) ) ); + + CInputPanelItemModelPanel* pInputPanel = dynamic_cast<CInputPanelItemModelPanel*>( pPanel ); + Assert( pInputPanel ); + + return pInputPanel->MatchesAttribCriteria( nItemID ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if the dragged item can be dropped into a given panel +//----------------------------------------------------------------------------- +bool CDynamicRecipePanel::CanDragTo( CItemModelPanel *pItemPanel, int iPanelIndex ) +{ + // From input to backpack panel + int iDraggedFromPos = GetBackpackPositionForPanel(m_pItemDraggedFromPanel); + if( IsInputPanel( iDraggedFromPos ) && IsBackpackPanel( iPanelIndex ) ) + return true; + + itemid_t itemID = m_pMouseDragItemPanel->GetItem() + ? m_pMouseDragItemPanel->GetItem()->GetItemID() + : 0; + + // From backpack to input panel that can accept the item + if( IsBackpackPanel( iDraggedFromPos ) + && IsInputPanel( iPanelIndex ) + && InputPanelCanAcceptItem( pItemPanel,itemID ) ) + return true; + + // From inoput to other input that can also accept the item + if( IsInputPanel( iDraggedFromPos ) + && IsInputPanel( iPanelIndex ) + && InputPanelCanAcceptItem( pItemPanel, itemID ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the user releasing their drag on a given panel. Only valid +// from backpack<->backpack, input<->input, backpack<->input +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::HandleDragTo( CItemModelPanel * /*pItemPanel*/, int iPanelIndex ) +{ + int iDraggedFromPos = GetBackpackPositionForPanel(m_pItemDraggedFromPanel); + + // The destination panel needs to exist, be enabled and be visible or else we dont consider it. + CItemModelPanel* pDestinationPanel = m_pItemModelPanels[ iPanelIndex ]; + if( !pDestinationPanel || !pDestinationPanel->IsEnabled() || !pDestinationPanel->IsVisible() ) + { + return; + } + + m_bInputPanelsDirty = true; + + // If we dragged from a inventory panel onto an input panel + if( IsInputPanel( iPanelIndex ) && IsBackpackPanel( iDraggedFromPos ) ) + { + CRecipeComponentItemModelPanel* pSrcPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] ); + CInputPanelItemModelPanel* pDstPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iPanelIndex] ); + + if( pSrcPanel && pDstPanel ) + { + // They dragged an item from the backpack into an input slot. Check if this is ok. + itemid_t nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( GetCurrentPage() ); + Assert( nSrcRecipeIndex != 0 ); + if( nSrcRecipeIndex != 0 && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) ) + { + SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, GetCurrentPage(), pDstPanel, pDstPanel->GetPageNumber() ); + } + } + } + else if( IsInputPanel( iDraggedFromPos ) && IsBackpackPanel( iPanelIndex ) ) + { + CRecipeComponentItemModelPanel* pSrcPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] ); + CRecipeComponentItemModelPanel* pDstPanel = dynamic_cast<CRecipeComponentItemModelPanel*>( m_pItemModelPanels[iPanelIndex] ); + + // If we dragged from an input panel to a backpack panel, return the item + // to its original backpack slot regardless of where they dragged it to + if( pSrcPanel && pDstPanel ) + { + // They dragged from an input panel to inventory panel. Check if there's something in + // the inventory slot already. If so, just move to the first open spot. + itemid_t pSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pSrcPanel->GetPageNumber() ); + itemid_t pDstRecipeIndex = pDstPanel->GetRecipeIndex( GetCurrentPage() ); + + Assert( pSrcRecipeIndex != 0 ); + if( pSrcRecipeIndex != 0 && pDstRecipeIndex != 0 ) + { + // There's already a recipe item in there. Just put it in the first open slot. + ReturnRecipeItemToBackpack( pSrcRecipeIndex, pSrcPanel, 0 ); + } + else if( pSrcRecipeIndex ) + { + // It's open! Place in there + SetRecipeComponentIntoPanel( pSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, GetCurrentPage() ); + } + } + } + else if( IsInputPanel( iDraggedFromPos ) && IsInputPanel( iPanelIndex ) ) + { + CInputPanelItemModelPanel* pSrcPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iDraggedFromPos] ); + CInputPanelItemModelPanel* pDstPanel = dynamic_cast<CInputPanelItemModelPanel*>( m_pItemModelPanels[iPanelIndex] ); + + // Dragged from an input panel to an input panel. + if( pSrcPanel && pDstPanel ) + { + itemid_t nSrcRecipeIndex = pSrcPanel->GetRecipeIndex( pSrcPanel->GetPageNumber() ); + itemid_t nDstRecipeIndex = pDstPanel->GetRecipeIndex( pDstPanel->GetPageNumber() ); + + // First see if we can swap our inputs + if( nSrcRecipeIndex != 0 && nDstRecipeIndex != 0 ) + { + if( pSrcPanel->MatchesAttribCriteria( nDstRecipeIndex ) && + pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) ) + { + SetRecipeComponentIntoPanel( nDstRecipeIndex, pSrcPanel, 0, pDstPanel, 0 ); + } + } + else if( nSrcRecipeIndex != 0 && nDstRecipeIndex == 0 && pDstPanel->MatchesAttribCriteria( nSrcRecipeIndex ) ) + { + // Next check if we just want to move the item + SetRecipeComponentIntoPanel( nSrcRecipeIndex, pSrcPanel, pSrcPanel->GetPageNumber(), pDstPanel, pDstPanel->GetPageNumber() ); + } + } + } + else + { + AssertMsg( 0, "Unhandled drag case!" ); + } + + UpdateModelPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return a given item to the first open slot in out backpack +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::ReturnRecipeItemToBackpack( itemid_t nItemID, CRecipeComponentItemModelPanel* pSrcPanel, int nSrcPage ) +{ + // For each page, check each recipe slot, on each panel for an opening + for( int i=0; i < GetNumPages(); ++i ) + { + FOR_EACH_VEC( m_vecBackpackModelPanels, j ) + { + CRecipeComponentItemModelPanel* pDstPanel = m_vecBackpackModelPanels[j]; + CEconItemView* pDstRecipe = pDstPanel->GetRecipeItem( i ); + bool bSlotIsOpen = pDstPanel->IsSlotAvailable( i ); + if( bSlotIsOpen && pDstRecipe == NULL ) + { + SetRecipeComponentIntoPanel( nItemID, pSrcPanel, nSrcPage, pDstPanel, i ); + return; + } + } + } + + AssertMsg( 0, "No open backpack slot found when returning item to backpack!" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set in a new recipe item. Update labels and reset panels. +//----------------------------------------------------------------------------- +void CDynamicRecipePanel::SetNewRecipe( CEconItemView* pNewRecipeItem ) +{ + m_flAbortCraftingAt = 0.f; + m_pDynamicRecipeItem = pNewRecipeItem; + // Update recipe title + m_pRecipeContainer->SetDialogVariable( "recipetitle", m_pDynamicRecipeItem->GetItemName() ); + // By default, dont show untradable items + m_pShowUntradableItemsCheckbox->SetSelected( false ); + // Repopulate the panels + InitItemPanels(); + UpdateModelPanels(); +} + + +class CWaitForConsumeDialog : public CGenericWaitingDialog +{ +public: + CWaitForConsumeDialog( vgui::Panel *pParent ) : CGenericWaitingDialog( pParent ) + { + } + +protected: + virtual void OnTimeout() + { + // Play an exciting sound! + vgui::surface()->PlaySound( "misc/achievement_earned.wav" ); + + // Show them their loot! + InventoryManager()->ShowItemsPickedUp( true ); + } +}; + +void CDynamicRecipePanel::OnCraftResponse( itemid_t nNewToolID, EGCMsgResponse eResponse ) +{ + // We got a response. We dont need to time-out + m_flAbortCraftingAt = 0; + + switch( eResponse ) + { + case k_EGCMsgResponseOK: + { + // If a new tool id comes back, that means we only partially completed the recipe + if( nNewToolID != 0 ) + { + OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Success", false, true, false ); + + // Play a sound letting them know the item is gone + const char *pszSoundFilename = m_pDynamicRecipeItem->GetDefinitionString( "recipe_partial_complete_sound", "ui/chem_set_add_element.wav" ); + vgui::surface()->PlaySound( pszSoundFilename ); + + CEconItemView* pNewRecipe = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( nNewToolID ); + Assert( pNewRecipe ); + // Set the new recipe into the panel. This resets all the item panels within. + SetNewRecipe( pNewRecipe ); + } + else + { + CloseCraftingStatusDialog(); + + // No new tool, so we completed the recipe! + const char *pszSoundFilename = m_pDynamicRecipeItem->GetDefinitionString( "recipe_complete_sound", "ui/chem_set_creation.wav" ); + vgui::surface()->PlaySound( pszSoundFilename ); + // Show the "Completing consumption" dialog for 5 seconds + ShowWaitingDialog( new CWaitForConsumeDialog( NULL ), "#ToolConsumptionInProgress", true, false, 5.0f ); + + // Hide the dynamic recipe panel after 6 seconds + g_DynamicRecipePanel->PostMessage( g_DynamicRecipePanel, new KeyValues("RecipeCompleted"), 6.f ); + } + } + break; + case k_EGCMsgResponseInvalid: + { + // Something was bad in the request. + OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Invalid", false, true, false ); + + InitItemPanels(); + UpdateModelPanels(); + } + break; + case k_EGCMsgResponseNoMatch: + { + // One or more of the items sent in didnt match + OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_NoMatch", false, true, false ); + + InitItemPanels(); + UpdateModelPanels(); + } + break; + default: + { + OpenCraftingStatusDialog( this, "#Dynamic_Recipe_Response_Default", false, true, false ); + + InitItemPanels(); + UpdateModelPanels(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: GC Msg handler to receive the dynamic recipe +//----------------------------------------------------------------------------- +class CGCCompleteDynamicRecipeResponse : public GCSDK::CGCClientJob +{ +public: + CGCCompleteDynamicRecipeResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CGCMsg<MsgGCStandardResponse_t> msg( pNetPacket ); + + itemid_t nNewToolID = INVALID_ITEM_ID; + if( !msg.BReadUint64Data( &nNewToolID ) ) + return true; + + g_DynamicRecipePanel->OnCraftResponse( nNewToolID, (EGCMsgResponse)msg.Body().m_eResponse ); + + return true; + } + +}; + +GC_REG_JOB( GCSDK::CGCClient, CGCCompleteDynamicRecipeResponse, "CGCCompleteDynamicRecipeResponse", k_EMsgGCFulfillDynamicRecipeComponentResponse, GCSDK::k_EServerTypeGCClient ); + +#ifdef STAGING_ONLY +void CDynamicRecipePanel::Debug_GiveRequiredInputs() const +{ + if ( !steamapicontext || !steamapicontext->SteamUser() ) + { + Msg("Not connected to Steam.\n"); + return; + } + CSteamID steamIDForPlayer = steamapicontext->SteamUser()->GetSteamID(); + if ( !steamIDForPlayer.IsValid() ) + { + Msg("Failed to find a valid steamID for the local player.\n"); + return; + } + + FOR_EACH_VEC( m_vecRecipeInputModelPanels, i ) + { + CInputPanelItemModelPanel *pInputPanel = m_vecRecipeInputModelPanels[i]; + + int nPage = 0; + for( const CEconItemAttributeDefinition *pAttrDef = pInputPanel->GetAttrib( nPage ); pAttrDef != NULL; pAttrDef = pInputPanel->GetAttrib( ++nPage ) ) + { + CAttribute_DynamicRecipeComponent attribValue; + if( m_pDynamicRecipeItem->FindAttribute<CAttribute_DynamicRecipeComponent >( pAttrDef, &attribValue ) ) + { + GCSDK::CProtoBufMsg<CMsgDevNewItemRequest> msg( k_EMsgGCDev_NewItemRequest ); + msg.Body().set_receiver( steamIDForPlayer.ConvertToUint64() ); + CItemSelectionCriteria criteria; + + if( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET ) + { + criteria.BAddCondition( "name", k_EOperator_String_EQ, GetItemSchema()->GetItemDefinition( attribValue.def_index() )->GetDefinitionName(), true ); + } + + if( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET ) + { + criteria.SetQuality( attribValue.item_quality() ); + } + + criteria.SetIgnoreEnabledFlag( true ); + criteria.BSerializeToMsg( *msg.Body().mutable_criteria() ); + GCClientSystem()->BSendMessage( msg ); + } + } + } +} +#endif |