//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "crafting_panel.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 #include "vgui_controls/TextImage.h" #include "vgui_controls/CheckButton.h" #include "vgui_controls/ComboBox.h" #include #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" // memdbgon must be the last include file in a .cpp file!!! #include ConVar tf_explanations_craftingpanel( "tf_explanations_craftingpanel", "0", FCVAR_ARCHIVE, "Whether the user has seen explanations for this panel." ); struct recipefilter_data_t { const char *pszTooltipString; const char *pszButtonImage; const char *pszButtonImageMouseover; }; recipefilter_data_t g_RecipeFilters[NUM_RECIPE_CATEGORIES] = { { "#RecipeFilter_Crafting", "crafticon_crafting_items", "crafticon_crafting_items_over" }, // RECIPE_CATEGORY_CRAFTINGITEMS, { "#RecipeFilter_CommonItems", "crafticon_common_items", "crafticon_common_items_over" }, // RECIPE_CATEGORY_COMMONITEMS, { "#RecipeFilter_RareItems", "crafticon_rare_items", "crafticon_rare_items_over" }, // RECIPE_CATEGORY_RAREITEMS, { "#RecipeFilter_Special", "crafticon_special_blueprints", "crafticon_special_blueprints_over" } // RECIPE_CATEGORY_SPECIAL, }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- wchar_t *LocalizeRecipeStringPiece( const char *pszString, wchar_t *pszConverted, int nConvertedSizeInBytes ) { if ( !pszString ) return L""; if ( pszString[0] == '#' ) return g_pVGuiLocalize->Find( pszString ); g_pVGuiLocalize->ConvertANSIToUnicode( pszString, pszConverted, nConvertedSizeInBytes ); return pszConverted; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SetItemPanelToRecipe( CItemModelPanel *pPanel, const CEconCraftingRecipeDefinition *pRecipeDef, bool bShowName ) { wchar_t wcTmpName[512]; wchar_t wcTmpDesc[512]; int iNegAttribsBegin = 0; if ( !pRecipeDef ) { Q_wcsncpy( wcTmpName, g_pVGuiLocalize->Find( "#Craft_Recipe_Custom" ), sizeof( wcTmpName ) ); Q_wcsncpy( wcTmpDesc, g_pVGuiLocalize->Find( "#Craft_Recipe_CustomDesc" ), sizeof( wcTmpDesc ) ); iNegAttribsBegin = Q_wcslen( wcTmpDesc ); } else { if ( bShowName ) { wchar_t *pName_A = g_pVGuiLocalize->Find( pRecipeDef->GetName_A() ); g_pVGuiLocalize->ConstructString_safe( wcTmpName, g_pVGuiLocalize->Find( pRecipeDef->GetName() ), 1, pName_A ); } else { wcTmpName[0] = '\0'; } wchar_t wcTmpA[32]; wchar_t wcTmpB[32]; wchar_t wcTmpC[32]; wchar_t wcTmp[512]; // Build the input string wchar_t *pInp_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_A(), wcTmpA, sizeof( wcTmpA ) ); wchar_t *pInp_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_B(), wcTmpB, sizeof( wcTmpB ) ); wchar_t *pInp_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_C(), wcTmpC, sizeof( wcTmpC ) ); g_pVGuiLocalize->ConstructString_safe( wcTmpDesc, g_pVGuiLocalize->Find( pRecipeDef->GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C ); iNegAttribsBegin = Q_wcslen(wcTmpDesc); // Build the output string wchar_t *pOut_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_A(), wcTmpA, sizeof( wcTmpA ) ); wchar_t *pOut_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_B(), wcTmpB, sizeof( wcTmpB ) ); wchar_t *pOut_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_C(), wcTmpC, sizeof( wcTmpC ) ); g_pVGuiLocalize->ConstructString_safe( wcTmp, g_pVGuiLocalize->Find( pRecipeDef->GetDescOutputs() ), 3, pOut_A, pOut_B, pOut_C ); // Concatenate, and mark the text changes V_wcscat_safe( wcTmpDesc, L"\n" ); V_wcscat_safe( wcTmpDesc, wcTmp ); } pPanel->SetAttribOnly( !bShowName ); pPanel->SetTextYPos( 0 ); pPanel->SetItem( NULL ); pPanel->SetNoItemText( wcTmpName, wcTmpDesc, iNegAttribsBegin ); pPanel->InvalidateLayout(true); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void PositionMouseOverPanelForRecipe( vgui::Panel *pScissorPanel, vgui::Panel *pRecipePanel, vgui::ScrollableEditablePanel *pRecipeScroller, CItemModelPanel *pMouseOverItemPanel ) { int x,y; vgui::ipanel()->GetAbsPos( pRecipePanel->GetVPanel(), x, y ); int xs,ys; vgui::ipanel()->GetAbsPos( pMouseOverItemPanel->GetParent()->GetVPanel(), xs, ys ); x -= xs; y -= ys; int iXPos = (x + (pRecipePanel->GetWide() * 0.5)) - (pMouseOverItemPanel->GetWide() * 0.5); int iYPos = (y + pRecipePanel->GetTall()); // Make sure the popup stays onscreen. if ( iXPos < 0 ) { iXPos = 0; } else if ( (iXPos + pMouseOverItemPanel->GetWide()) > pMouseOverItemPanel->GetParent()->GetWide() ) { iXPos = pMouseOverItemPanel->GetParent()->GetWide() - pMouseOverItemPanel->GetWide(); } if ( iYPos < 0 ) { iYPos = 0; } else if ( (iYPos + pMouseOverItemPanel->GetTall() + YRES(32)) > pMouseOverItemPanel->GetParent()->GetTall() ) { // Move it up above our item iYPos = y - pMouseOverItemPanel->GetTall() - YRES(4); } pMouseOverItemPanel->SetPos( iXPos, iYPos ); pMouseOverItemPanel->SetVisible( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCraftingPanel::CCraftingPanel( vgui::Panel *parent, const char *panelName ) : CBaseLoadoutPanel( parent, panelName ) { m_pRecipeListContainer = new vgui::EditablePanel( this, "recipecontainer" ); m_pRecipeListContainerScroller = new vgui::ScrollableEditablePanel( this, m_pRecipeListContainer, "recipecontainerscroller" ); m_pSelectedRecipeContainer = new vgui::EditablePanel( this, "selectedrecipecontainer" ); m_pRecipeButtonsKV = NULL; m_pRecipeFilterButtonsKV = NULL; m_bEventLogging = false; m_iCraftingAttempts = 0; m_iRecipeCategoryFilter = RECIPE_CATEGORY_CRAFTINGITEMS; m_iCurrentlySelectedRecipe = -1; CleanupPostCraft( true ); m_pToolTip = new CTFTextToolTip( this ); m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" ); m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false ); m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false ); m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel ); m_pToolTip->SetTooltipDelay( 0 ); m_pSelectionPanel = NULL; m_iSelectingForSlot = 0; m_pCraftButton = NULL; m_pUpgradeButton = NULL; m_pFreeAccountLabel = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCraftingPanel::~CCraftingPanel( void ) { if ( m_pRecipeButtonsKV ) { m_pRecipeButtonsKV->deleteThis(); m_pRecipeButtonsKV = NULL; } if ( m_pRecipeFilterButtonsKV ) { m_pRecipeFilterButtonsKV->deleteThis(); m_pRecipeFilterButtonsKV = NULL; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { LoadControlSettings( GetResFile() ); BaseClass::ApplySchemeSettings( pScheme ); m_pRecipeListContainerScroller->GetScrollbar()->SetAutohideButtons( true ); m_pCraftButton = dynamic_cast( m_pSelectedRecipeContainer->FindChildByName("CraftButton") ); if ( m_pCraftButton ) { m_pCraftButton->AddActionSignalTarget( this ); } m_pUpgradeButton = dynamic_cast( m_pSelectedRecipeContainer->FindChildByName("UpgradeButton") ); if ( m_pUpgradeButton ) { m_pUpgradeButton->AddActionSignalTarget( this ); } m_pFreeAccountLabel = dynamic_cast( m_pSelectedRecipeContainer->FindChildByName("FreeAccountLabel") ); CreateRecipeFilterButtons(); UpdateRecipeFilter(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); KeyValues *pItemKV = inResourceData->FindKey( "recipebuttons_kv" ); if ( pItemKV ) { if ( m_pRecipeButtonsKV ) { m_pRecipeButtonsKV->deleteThis(); } m_pRecipeButtonsKV = new KeyValues("recipebuttons_kv"); pItemKV->CopySubkeys( m_pRecipeButtonsKV ); } KeyValues *pButtonKV = inResourceData->FindKey( "recipefilterbuttons_kv" ); if ( pButtonKV ) { if ( m_pRecipeFilterButtonsKV ) { m_pRecipeFilterButtonsKV->deleteThis(); } m_pRecipeFilterButtonsKV = new KeyValues("recipefilterbuttons_kv"); pButtonKV->CopySubkeys( m_pRecipeFilterButtonsKV ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::PerformLayout( void ) { BaseClass::PerformLayout(); // Need to lay these out before we start making item panels inside them m_pRecipeListContainer->InvalidateLayout( true ); m_pRecipeListContainerScroller->InvalidateLayout( true ); // Position the recipe filters FOR_EACH_VEC( m_pRecipeFilterButtons, i ) { if ( m_pRecipeFilterButtonsKV ) { m_pRecipeFilterButtons[i]->ApplySettings( m_pRecipeFilterButtonsKV ); m_pRecipeFilterButtons[i]->InvalidateLayout(); } int iButtonW, iButtonH; m_pRecipeFilterButtons[i]->GetSize( iButtonW, iButtonH ); int iXPos = (GetWide() * 0.5) + m_iFilterOffcenterX + ((iButtonW + m_iFilterDeltaX) * i); int iYPos = m_iFilterYPos;// + ((iButtonH + m_iFilterDeltaY) * i); m_pRecipeFilterButtons[i]->SetPos( iXPos, iYPos ); } // Position the recipe buttons for ( int i = 0; i < m_pRecipeButtons.Count(); i++ ) { if ( m_pRecipeButtonsKV ) { m_pRecipeButtons[i]->ApplySettings( m_pRecipeButtonsKV ); m_pRecipeButtons[i]->InvalidateLayout(); } int iYDelta = m_pRecipeButtons[0]->GetTall() + YRES(2); // Once we've setup our first item, we know how large to make the container if ( i == 0 ) { m_pRecipeListContainer->SetSize( m_pRecipeListContainer->GetWide(), iYDelta * m_pRecipeButtons.Count() ); } int x,y; m_pRecipeButtons[i]->GetPos( x,y ); m_pRecipeButtons[i]->SetPos( x, (iYDelta * i) ); } // Now that the container has been sized, tell the scroller to re-evaluate m_pRecipeListContainerScroller->InvalidateLayout(); m_pRecipeListContainerScroller->GetScrollbar()->InvalidateLayout(); // Then position all our item panels for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { PositionItemPanel( m_pItemModelPanels[i], i ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::CreateRecipeFilterButtons( void ) { for ( int i = 0; i < NUM_RECIPE_CATEGORIES; i++ ) { if ( m_pRecipeFilterButtons.Count() <= i ) { CImageButton *pNewButton = new CImageButton( this, g_RecipeFilters[i].pszTooltipString ); m_pRecipeFilterButtons.AddToTail( pNewButton ); } m_pRecipeFilterButtons[i]->SetInactiveImage( g_RecipeFilters[i].pszButtonImage ); m_pRecipeFilterButtons[i]->SetActiveImage( g_RecipeFilters[i].pszButtonImageMouseover ); m_pRecipeFilterButtons[i]->SetTooltip( m_pToolTip, g_RecipeFilters[i].pszTooltipString ); const char *pszCommand = VarArgs("selectfilter%d", i ); m_pRecipeFilterButtons[i]->SetCommand( pszCommand ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::UpdateRecipeFilter( void ) { int iMatchingRecipes = 0; m_iCurrentlySelectedRecipe = -1; m_iCurrentRecipeTotalInputs = 0; m_iCurrentRecipeTotalOutputs = 0; FOR_EACH_VEC( m_pRecipeFilterButtons, i ) { bool bForceDepressed = ( i == m_iRecipeCategoryFilter ); m_pRecipeFilterButtons[i]->ForceDepressed( bForceDepressed ); } // Loop through the known recipes, and see which ones match our category filter for ( int i = 0; i < TFInventoryManager()->GetLocalTFInventory()->GetRecipeCount(); i++ ) { const CEconCraftingRecipeDefinition *pRecipeDef = TFInventoryManager()->GetLocalTFInventory()->GetRecipeDef(i); if ( !pRecipeDef ) continue; if ( pRecipeDef->IsDisabled() ) continue; if ( pRecipeDef->GetCategory() != m_iRecipeCategoryFilter ) continue; wchar_t wTemp[256]; wchar_t *pName_A = g_pVGuiLocalize->Find( pRecipeDef->GetName_A() ); g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find( pRecipeDef->GetName() ), 1, pName_A ); SetButtonToRecipe( iMatchingRecipes, pRecipeDef->GetDefinitionIndex(), wTemp ); iMatchingRecipes++; } // Add a "Custom" option to the bottom of the Special recipe list if ( m_iRecipeCategoryFilter == RECIPE_CATEGORY_SPECIAL ) { SetButtonToRecipe( iMatchingRecipes, RECIPE_CUSTOM, g_pVGuiLocalize->Find("#Craft_Recipe_Custom") ); iMatchingRecipes++; } // Delete excess buttons for ( int i = m_pRecipeButtons.Count() - 1; i >= iMatchingRecipes; i-- ) { m_pRecipeButtons[i]->MarkForDeletion(); m_pRecipeButtons.Remove( i ); } // Move the scrollbar to the top m_pRecipeListContainerScroller->GetScrollbar()->SetValue( 0 ); UpdateSelectedRecipe( true ); InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnCancelSelection( void ) { if ( m_pSelectionPanel ) { m_pSelectionPanel->SetVisible( false ); } CloseCraftingStatusDialog(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnSelectionReturned( KeyValues *data ) { if ( data ) { uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID ); if ( ulIndex == INVALID_ITEM_ID ) { // should this be INVALID_ITEM_ID? m_InputItems[m_iSelectingForSlot] = 0; } else { m_InputItems[m_iSelectingForSlot] = ulIndex; } UpdateModelPanels(); UpdateCraftButton(); } // It'll have deleted itself, so we don't need to clean it up OnCancelSelection(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory ) { if ( bVisible ) { if ( m_pSelectionPanel ) { m_pSelectionPanel->SetVisible( false ); } memset( m_InputItems, 0, sizeof(m_InputItems) ); memset( m_ItemPanelCriteria, 0, sizeof(m_ItemPanelCriteria) ); m_iCurrentlySelectedRecipe = -1; m_iCurrentRecipeTotalInputs = 0; m_iCurrentRecipeTotalOutputs = 0; UpdateRecipeFilter(); if ( !m_bEventLogging ) { m_bEventLogging = true; C_CTF_GameStats.Event_Crafting( IE_CRAFTING_ENTERED ); } } else { CloseCraftingStatusDialog(); vgui::ivgui()->RemoveTickSignal( GetVPanel() ); } BaseClass::OnShowPanel( bVisible, bReturningFromArmory ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnClosing() { if ( m_bEventLogging ) { C_CTF_GameStats.Event_Crafting( IE_CRAFTING_EXITED ); m_bEventLogging = false; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex ) { int iCenter = 0; int iButtonX, iButtonY, iXPos, iYPos; if ( IsInputItemPanel(iIndex) ) { iButtonX = (iIndex % CRAFTING_SLOTS_INPUT_COLUMNS); iButtonY = (iIndex / CRAFTING_SLOTS_INPUT_COLUMNS); iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX); iYPos = m_iItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); } else { int iButtonIndex = iIndex - CRAFTING_SLOTS_INPUTPANELS; iButtonX = (iButtonIndex % CRAFTING_SLOTS_OUTPUT_COLUMNS); iButtonY = (iButtonIndex / CRAFTING_SLOTS_OUTPUT_COLUMNS); iXPos = (iCenter + m_iItemCraftingOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX); iYPos = m_iOutputItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); } m_pItemModelPanels[iIndex]->SetPos( iXPos, iYPos ); return; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::UpdateRecipeItems( bool bClearInputItems ) { if ( bClearInputItems ) { memset( m_InputItems, 0, sizeof(m_InputItems) ); } memset( m_ItemPanelCriteria, 0, sizeof(m_ItemPanelCriteria) ); m_iCurrentRecipeTotalInputs = 0; m_iCurrentRecipeTotalOutputs = 0; if ( m_iCurrentlySelectedRecipe == -1 ) return; /* // Build lists of items divided by class & loadout slot, so recipes can quickly test themselves CUtlVector vecAllItems; CUtlVector vecItemsByClass[ LOADOUT_COUNT ]; CUtlVector vecItemsBySlot[ LOADOUT_POSITION_COUNT ]; for ( int i = 1; i <= TFInventoryManager()->GetLocalTFInventory()->GetMaxItemCount(); i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetItemByBackpackPosition(i); if ( pItemData && pItemData->IsValid() ) { CEconItem *pSOCData = pItemData->GetSOCData(); vecAllItems.AddToTail( pSOCData ); CTFItemDefinition *pItemDef = pItemData->GetStaticData(); // Put it in class lists for any class that can use it. Use the zeroth list as all-class items. if ( pItemDef->CanBeUsedByAllClasses() ) { vecItemsByClass[0].AddToTail( pSOCData ); } for (int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) { if ( pItemDef->CanBeUsedByClass(iClass) ) { vecItemsByClass[iClass].AddToTail( pSOCData ); } } // Put it in the slot lists for any slot that it can be equipped in for (int iSlot = 0; iSlot < LOADOUT_POSITION_COUNT; iSlot++ ) { if ( pItemDef->CanBePlacedInSlot( iSlot ) ) { vecItemsBySlot[iSlot].AddToTail( pSOCData ); } } } } */ // Find the items needed for the specified recipe if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) { // Custom recipe. Show all open buttons, and let them put anything in there. m_iCurrentRecipeTotalInputs = CRAFTING_SLOTS_INPUTPANELS; m_iCurrentRecipeTotalOutputs = 0; FOR_EACH_VEC( m_pItemModelPanels, i ) { m_pItemModelPanels[i]->SetNoItemText( "" ); } } else { const CTFCraftingRecipeDefinition *pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); if ( pRecipeDef ) { m_iCurrentRecipeTotalInputs = pRecipeDef->GetTotalInputItemsRequired(); m_iCurrentRecipeTotalOutputs = pRecipeDef->GetTotalOutputItems(); CUtlVector vecItemsUsed; // Set the text in each of the item panels const CUtlVector *vecInputCriteria; vecInputCriteria = pRecipeDef->GetInputItems(); CUtlVector vecInputDupes; vecInputDupes = pRecipeDef->GetInputItemDupeCounts(); int iModelPanel = 0; FOR_EACH_VEC( *vecInputCriteria, i ) { const char *pszNoItemText = GetItemTextForCriteria( &(*vecInputCriteria)[i] ); int iNumPanels = vecInputDupes[i] ? vecInputDupes[i] : 1; for ( int iPanel = 0; iPanel < iNumPanels; iPanel++ ) { m_ItemPanelCriteria[iModelPanel] = &(*vecInputCriteria)[i]; if ( m_pItemModelPanels[iModelPanel] ) { m_pItemModelPanels[iModelPanel]->SetNoItemText( pszNoItemText ); } iModelPanel++; } } // Set the output items as well CUtlVector vecOutputCriteria; vecOutputCriteria = pRecipeDef->GetOutputItems(); FOR_EACH_VEC( vecOutputCriteria, i ) { int iOutputPanel = CRAFTING_SLOTS_INPUTPANELS + i; CEconItemDefinition *pDef = GetItemDefFromCriteria( &vecOutputCriteria[i] ); if ( pDef ) { //m_pItemModelPanels[iOutputPanel]->SetNoItemText( pszNoItemText ); CEconItemView *pItemData = new CEconItemView(); pItemData->Init( pDef->GetDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); if ( m_pItemModelPanels[iOutputPanel] ) { m_pItemModelPanels[iOutputPanel]->SetItem( pItemData ); } delete pItemData; continue; } // If we didn't manage to extract an output, just use the recipe output string wchar_t wcTmpA[32]; wchar_t wcTmpB[32]; wchar_t wcTmpC[32]; wchar_t wcTmp[512]; wchar_t *pOut_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_A(), wcTmpA, sizeof( wcTmpA ) ); wchar_t *pOut_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_B(), wcTmpB, sizeof( wcTmpB ) ); wcTmp[0] = '\0'; V_wcscat_safe( wcTmp, pOut_A ); V_wcscat_safe( wcTmp, L" " ); V_wcscat_safe( wcTmp, pOut_B ); if ( Q_strnicmp( pRecipeDef->GetDescOutputs(), "#RDO_ABC", 8 ) == 0 ) { wchar_t *pOut_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescO_C(), wcTmpC, sizeof( wcTmpC ) ); V_wcscat_safe( wcTmp, L" " ); V_wcscat_safe( wcTmp, pOut_C ); } if ( m_pItemModelPanels[iOutputPanel] ) { m_pItemModelPanels[iOutputPanel]->SetItem( NULL ); m_pItemModelPanels[iOutputPanel]->SetNoItemText( wcTmp ); } } } } // Now check to see if they've got the right items in there UpdateCraftButton(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::UpdateCraftButton( void ) { if ( m_iCurrentlySelectedRecipe == -1 ) return; bool bAllowedToUse = true; const CEconCraftingRecipeDefinition *pRecipeDef = NULL; if ( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ) { pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); if ( !pRecipeDef ) return; bAllowedToUse = ( !IsFreeTrialAccount() || !pRecipeDef->IsPremiumAccountOnly() ); } if ( m_pCraftButton ) { m_pCraftButton->SetVisible( bAllowedToUse ); } if ( m_pUpgradeButton ) { m_pUpgradeButton->SetVisible( !bAllowedToUse ); } if ( m_pFreeAccountLabel ) { m_pFreeAccountLabel->SetVisible( !bAllowedToUse ); } if ( !bAllowedToUse ) return; bool bCraftButtonActive = false; if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) { // Need at least one item in a slot for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); if ( pItemData ) { bCraftButtonActive = true; break; } } } else { CUtlVector vecAllItems; for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); if ( pItemData ) { vecAllItems.AddToTail( pItemData->GetSOCData() ); } } bCraftButtonActive = pRecipeDef->ItemListMatchesInputs( &vecAllItems, NULL, false, NULL ); } if ( m_pCraftButton ) { m_pCraftButton->SetEnabled( bCraftButtonActive ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CCraftingPanel::GetItemTextForCriteria( const CItemSelectionCriteria *pCriteria ) { // Otherwise, look at the first condition, and see if we can determine what the item is const char *pszVal = pCriteria->GetValueForFirstConditionOfType( k_EOperator_String_EQ ); if ( pszVal && pszVal[0] ) { // Is it a loadout slot? int iSlot = StringFieldToInt( pszVal, ItemSystem()->GetItemSchema()->GetLoadoutStrings( EEquipType_t::EQUIP_TYPE_CLASS ), true ); if ( iSlot != -1 ) return ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[iSlot]; // Is it a craft material type? if ( V_stricmp( pszVal, "weapon" ) == 0 ) { return "#RI_W"; } else if ( V_stricmp( pszVal, "hat" ) == 0 ) { return "#RI_Hg"; } else if ( V_stricmp( pszVal, "craft_token" ) == 0 ) { return "#RI_T"; } else if ( V_stricmp( pszVal, "class_token" ) == 0 ) { return "#CI_T_C"; } else if ( V_stricmp( pszVal, "slot_token" ) == 0 ) { return "#CI_T_S"; } // Is it an item name? CEconItemDefinition *pDef = ItemSystem()->GetItemSchema()->GetItemDefinitionByName(pszVal); if ( pDef ) return pDef->GetItemBaseName(); } return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEconItemDefinition *CCraftingPanel::GetItemDefFromCriteria( const CItemSelectionCriteria *pCriteria ) { // Otherwise, look at the first condition, and see if we can determine what the item is const char *pszVal = pCriteria->GetValueForFirstConditionOfType( k_EOperator_String_EQ ); if ( pszVal && pszVal[0] ) return ItemSystem()->GetItemSchema()->GetItemDefinitionByName(pszVal); return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::AddNewItemPanel( int iPanelIndex ) { BaseClass::AddNewItemPanel( iPanelIndex ); // Move the model panels to our selected recipe container m_pItemModelPanels[iPanelIndex]->SetParent( m_pSelectedRecipeContainer ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::UpdateModelPanels( void ) { BaseClass::UpdateModelPanels(); for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { if ( IsInputItemPanel(i) ) { if ( m_InputItems[i] != 0 ) { CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); m_pItemModelPanels[i]->SetItem( pItemData ); m_pItemModelPanels[i]->SetVisible( true ); m_pItemModelPanels[i]->SetShowEquipped( true ); SetBorderForItem( m_pItemModelPanels[i], false ); } else { m_pItemModelPanels[i]->SetItem( NULL ); // Always show the number of slots that the recipe uses bool bVisible = (m_iCurrentRecipeTotalInputs > i); m_pItemModelPanels[i]->SetVisible( bVisible ); } } else { bool bVisible = ((m_iCurrentRecipeTotalOutputs + CRAFTING_SLOTS_INPUTPANELS) > i); m_pItemModelPanels[i]->SetVisible( bVisible ); } } vgui::Panel *pLabel = m_pSelectedRecipeContainer->FindChildByName("OutputLabel"); if ( pLabel ) { pLabel->SetVisible( m_iCurrentRecipeTotalOutputs > 0 ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::SetButtonToRecipe( int iButton, int iDefIndex, wchar_t *pszText ) { // Re-use existing buttons, or make new ones if we need more CRecipeButton *pRecipeButton = NULL; if ( iButton < m_pRecipeButtons.Count() ) { pRecipeButton = m_pRecipeButtons[iButton]; } else { pRecipeButton = new CRecipeButton( m_pRecipeListContainer, "selectrecipe", "", this, "selectrecipe" ); if ( m_pRecipeButtonsKV ) { pRecipeButton->ApplySettings( m_pRecipeButtonsKV ); } pRecipeButton->MakeReadyForUse(); m_pRecipeButtons.AddToTail( pRecipeButton ); } const char *pszCommand = VarArgs("selectrecipe%d", iDefIndex ); pRecipeButton->SetCommand( pszCommand ); pRecipeButton->SetText( pszText ); pRecipeButton->SetDefIndex( iDefIndex ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::UpdateSelectedRecipe( bool bClearInputItems ) { for ( int i = 0; i < m_pRecipeButtons.Count(); i++ ) { bool bSelected = m_pRecipeButtons[i]->m_iRecipeDefIndex == m_iCurrentlySelectedRecipe; m_pRecipeButtons[i]->ForceDepressed( bSelected ); m_pRecipeButtons[i]->RecalculateDepressedState(); if ( bSelected ) { wchar_t wszText[1024]; m_pRecipeButtons[i]->GetText( wszText, ARRAYSIZE( wszText ) ); m_pSelectedRecipeContainer->SetDialogVariable( "recipetitle", wszText ); if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) { m_pSelectedRecipeContainer->SetDialogVariable( "recipeinputstring", g_pVGuiLocalize->Find("#Craft_Recipe_CustomDesc") ); } else { const CTFCraftingRecipeDefinition *pRecipeDef = (CTFCraftingRecipeDefinition*)TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( m_iCurrentlySelectedRecipe ); if ( pRecipeDef ) { // Build the input string wchar_t wcTmpA[32]; wchar_t wcTmpB[32]; wchar_t wcTmpC[32]; wchar_t wcTmpDesc[512]; wchar_t *pInp_A = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_A(), wcTmpA, sizeof( wcTmpA ) ); wchar_t *pInp_B = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_B(), wcTmpB, sizeof( wcTmpB ) ); wchar_t *pInp_C = LocalizeRecipeStringPiece( pRecipeDef->GetDescI_C(), wcTmpC, sizeof( wcTmpC ) ); g_pVGuiLocalize->ConstructString_safe( wcTmpDesc, g_pVGuiLocalize->Find( pRecipeDef->GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C ); m_pSelectedRecipeContainer->SetDialogVariable( "recipeinputstring", wcTmpDesc ); } } } } m_pSelectedRecipeContainer->SetVisible( m_iCurrentlySelectedRecipe != -1 ); UpdateRecipeItems( bClearInputItems ); UpdateModelPanels(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnCommand( const char *command ) { if ( !Q_strnicmp( command, "selectrecipe", 12 ) ) { const char *pszNum = command+12; if ( pszNum && pszNum[0] ) { m_iCurrentlySelectedRecipe = atoi(pszNum); UpdateSelectedRecipe( true ); } return; } if ( !Q_strnicmp( command, "selectfilter", 12 ) ) { const char *pszNum = command+12; if ( pszNum && pszNum[0] ) { m_iRecipeCategoryFilter = (recipecategories_t)atoi(pszNum); UpdateRecipeFilter(); } return; } else if ( !Q_strnicmp( command, "back", 4 ) ) { PostMessage( GetParent(), new KeyValues("CraftingClosed") ); return; } else if ( !Q_strnicmp( command, "craft", 5 ) ) { if ( CheckForUntradableItems() ) { Craft(); } return; } else if ( !Q_stricmp( command, "upgrade" ) ) { EconUI()->CloseEconUI(); EconUI()->OpenStorePanel( STOREPANEL_SHOW_UPGRADESTEPS, false ); return; } else if ( !Q_stricmp( command, "reloadscheme" ) ) { InvalidateLayout( true, true ); } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnRecipePanelEntered( vgui::Panel *panel ) { CRecipeButton *pRecipePanel = dynamic_cast < CRecipeButton * > ( panel ); if ( pRecipePanel && IsVisible() && !IsIgnoringItemPanelEnters() ) { const CEconCraftingRecipeDefinition *pRecipeDef = NULL; if ( pRecipePanel->m_iRecipeDefIndex != RECIPE_CUSTOM ) { pRecipeDef = TFInventoryManager()->GetLocalTFInventory()->GetRecipeDefByDefIndex( pRecipePanel->m_iRecipeDefIndex ); } SetItemPanelToRecipe( GetMouseOverPanel(), pRecipeDef, false ); PositionMouseOverPanelForRecipe( this, pRecipePanel, m_pRecipeListContainerScroller, GetMouseOverPanel() ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnRecipePanelExited( vgui::Panel *panel ) { GetMouseOverPanel()->SetAttribOnly( false ); GetMouseOverPanel()->SetTextYPos( YRES(20) ); GetMouseOverPanel()->SetVisible( false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CCraftingPanel::GetItemPanelIndex( CItemModelPanel *pItemPanel ) { for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { if ( m_pItemModelPanels[i] == pItemPanel ) return i; } return -1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnItemPanelMousePressed( vgui::Panel *panel ) { CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); if ( pItemPanel && IsVisible() && !pItemPanel->IsGreyedOut() ) { int iPos = GetItemPanelIndex(pItemPanel); if ( IsInputItemPanel(iPos) ) { m_iSelectingForSlot = iPos; // Create it the first time around if ( !m_pSelectionPanel ) { m_pSelectionPanel = new CCraftingItemSelectionPanel( this ); } if ( m_iCurrentlySelectedRecipe == RECIPE_CUSTOM ) { m_pSelectionPanel->UpdateOnShow( NULL, true, m_InputItems, ARRAYSIZE(m_InputItems) ); } else { // Clicked on an item in the crafting area. Open up the selection panel. m_pSelectionPanel->UpdateOnShow( m_ItemPanelCriteria[iPos], false, m_InputItems, ARRAYSIZE(m_InputItems) ); } m_pSelectionPanel->ShowDuplicateCounts( true ); m_pSelectionPanel->ShowPanel( 0, true ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- static void ConfirmCraft( bool bConfirmed, void* pContext ) { CCraftingPanel *pCraftingPanel = ( CCraftingPanel* )pContext; if ( bConfirmed ) { pCraftingPanel->Craft(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCraftingPanel::CheckForUntradableItems( void ) { bool bHasUntradable = false; for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) { if ( m_InputItems[i] != 0 ) { CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); if ( pItemData->IsTradable() == false ) { bHasUntradable = true; break; } } } if ( bHasUntradable ) { CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#Craft_Untradable_Title", "#Craft_Untradable_Text", "#GameUI_OK", "#Cancel", &ConfirmCraft ); pDialog->SetContext( this ); return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::Craft( void ) { // Build our list of items that we're trying to craft ++m_iCraftingAttempts; CUtlVector vecCraftingItems; for ( int i = 0; i < CRAFTING_SLOTS_INPUTPANELS; i++ ) { if ( m_InputItems[i] != 0 ) { CEconItemView *pItemData = TFInventoryManager()->GetLocalTFInventory()->GetInventoryItemByItemID( m_InputItems[i] ); C_CTF_GameStats.Event_Crafting( IE_CRAFTING_ATTEMPT, pItemData, m_iCraftingAttempts ); vecCraftingItems.AddToTail( m_InputItems[i] ); } } if ( !vecCraftingItems.Count() ) return; GCSDK::CGCMsg msg( k_EMsgGCCraft ); msg.Body().m_nRecipeDefIndex = m_iCurrentlySelectedRecipe; msg.Body().m_nItemCount = vecCraftingItems.Count(); for ( int i = 0; i < vecCraftingItems.Count(); i++ ) { msg.AddUint64Data( vecCraftingItems[i] ); } GCClientSystem()->BSendMessage( msg ); OpenCraftingStatusDialog( this, "#CraftUpdate_Start", true, false, false ); // Start ticking so we can give up waiting if we don't get a response from the GC // We use the VGUI time, because we may not be in a game at all. m_flAbortCraftingAt = vgui::system()->GetCurrentTime() + 10; m_bWaitingForCraftItems = false; m_iRecipeIndexTried = m_iCurrentlySelectedRecipe; vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnCraftResponse( EGCMsgResponse eResponse, CUtlVector *vecCraftedIndices, int iRecipeUsed ) { switch ( eResponse ) { case k_EGCMsgResponseNoMatch: { C_CTF_GameStats.Event_Crafting( IE_CRAFTING_NO_RECIPE_MATCH, NULL, m_iCraftingAttempts ); CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); OpenCraftingStatusDialog( this, "#CraftUpdate_NoMatch", false, true, false ); } break; case k_EGCMsgResponseDenied: { // Craft denied. C_CTF_GameStats.Event_Crafting( IE_CRAFTING_FAILURE, NULL, m_iCraftingAttempts ); CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); OpenCraftingStatusDialog( this, "#CraftUpdate_Denied", false, true, false ); } break; // We've got the list of items crafted. We save off the item list until our item cache has all the items. case k_EGCMsgResponseOK: { // Start ticking, and wait until the cache contains all the items in the list. m_bWaitingForCraftItems = true; m_vecNewlyCraftedItems = *vecCraftedIndices; if ( iRecipeUsed != m_iRecipeIndexTried && iRecipeUsed != -1 ) { m_iNewRecipeIndex = iRecipeUsed; } } break; default: { // Craft failed in some way. C_CTF_GameStats.Event_Crafting( IE_CRAFTING_FAILURE, NULL, m_iCraftingAttempts ); OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false ); CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); } break; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::ShowCraftFinish( void ) { TFInventoryManager()->ShowItemsCrafted( &m_vecNewlyCraftedItems ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::OnTick( void ) { BaseClass::OnTick(); if ( IsVisible() ) { if ( m_flAbortCraftingAt ) { if ( m_flAbortCraftingAt < vgui::system()->GetCurrentTime() ) { C_CTF_GameStats.Event_Crafting( IE_CRAFTING_TIMEOUT, NULL, m_iCraftingAttempts ); CleanupPostCraft( m_iCurrentlySelectedRecipe != RECIPE_CUSTOM ); OpenCraftingStatusDialog( this, "#CraftUpdate_Failed", false, true, false ); return; } } if ( m_bWaitingForCraftItems ) { // If all the items in our newly crafted list are in the cache, we can show the pickup. FOR_EACH_VEC_BACK( m_vecNewlyCraftedItems, i ) { CEconItemView* pNewItem = InventoryManager()->GetLocalInventory()->GetInventoryItemByItemID( m_vecNewlyCraftedItems[i] ); if ( pNewItem == NULL ) return; C_CTF_GameStats.Event_Crafting( IE_CRAFTING_SUCCESS, pNewItem, m_iCraftingAttempts ); } m_bWaitingForCraftItems = false; // We have all the new items, show the pickup OpenCraftingStatusDialog( this, "#CraftUpdate_Success", false, true, true ); CleanupPostCraft( true ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingPanel::CleanupPostCraft( bool bClearInputItems ) { m_flAbortCraftingAt = 0; m_bWaitingForCraftItems = false; UpdateSelectedRecipe( bClearInputItems ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- ConVar *CCraftingPanel::GetExplanationConVar( void ) { return &tf_explanations_craftingpanel; } //================================================================================================================================ // NOT CONNECTED TO STEAM WARNING DIALOG //================================================================================================================================ static vgui::DHANDLE g_CraftingStatusPanel; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCraftingStatusDialog::CCraftingStatusDialog( vgui::Panel *pParent, const char *pElementName ) : BaseClass( pParent, "CraftingStatusDialog" ) { m_pRecipePanel = vgui::SETUP_PANEL( new CItemModelPanel( this, "RecipeItemModelPanel" ) ); m_bShowNewRecipe = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingStatusDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); if ( m_bShowNewRecipe ) { LoadControlSettings( "resource/UI/NewRecipeFoundDialog.res" ); } else { LoadControlSettings( "resource/UI/CraftingStatusDialog.res" ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingStatusDialog::OnCommand( const char *command ) { bool bClose = false; if ( !Q_stricmp( command, "close" ) ) { // If we were a success, show the player their new crafted items if ( m_bShowOnExit ) { if ( EconUI()->GetCraftingPanel() ) { EconUI()->GetCraftingPanel()->ShowCraftFinish(); } m_bShowOnExit = false; } bClose = true; } else if ( !Q_stricmp( command, "forceclose" ) ) { bClose = true; } if ( bClose ) { m_bShowOnExit = false; TFModalStack()->PopModal( this ); SetVisible( false ); MarkForDeletion(); EconUI()->SetPreventClosure( false ); return; } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingStatusDialog::OnTick( void ) { if ( !m_bAnimateEllipses || !IsVisible() ) { vgui::ivgui()->RemoveTickSignal( GetVPanel() ); } else { m_iNumEllipses = ((m_iNumEllipses+1) % 4); } switch ( m_iNumEllipses ) { case 3: SetDialogVariable( "ellipses", L"..." ); break; case 2: SetDialogVariable( "ellipses", L".." ); break; case 1: SetDialogVariable( "ellipses", L"." ); break; default: SetDialogVariable( "ellipses", L"" ); break; } BaseClass::OnTick(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingStatusDialog::UpdateSchemeForVersion( bool bRecipe ) { m_bShowNewRecipe = bRecipe; InvalidateLayout( false, true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraftingStatusDialog::ShowStatusUpdate( bool bAnimateEllipses, bool bAllowClose, bool bShowOnExit ) { m_bShowNewRecipe = false; CExButton *pButton = dynamic_cast( FindChildByName("CloseButton") ); if ( pButton ) { pButton->SetVisible( bAllowClose ); pButton->SetEnabled( bAllowClose ); } m_bAnimateEllipses = bAnimateEllipses; if ( m_bAnimateEllipses ) { vgui::ivgui()->AddTickSignal( GetVPanel(), 500 ); SetDialogVariable( "ellipses", L"" ); m_iNumEllipses = 0; } else { vgui::ivgui()->RemoveTickSignal( GetVPanel() ); SetDialogVariable( "ellipses", L"" ); } m_bShowOnExit = bShowOnExit; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SetupCraftingStatusDialog( vgui::Panel *pParent ) { if (!g_CraftingStatusPanel.Get()) { g_CraftingStatusPanel = vgui::SETUP_PANEL( new CCraftingStatusDialog( pParent, NULL ) ); } g_CraftingStatusPanel->SetVisible( true ); g_CraftingStatusPanel->MakePopup(); g_CraftingStatusPanel->MoveToFront(); g_CraftingStatusPanel->SetKeyBoardInputEnabled(true); g_CraftingStatusPanel->SetMouseInputEnabled(true); TFModalStack()->PushModal( g_CraftingStatusPanel ); EconUI()->SetPreventClosure( true ); } CCraftingStatusDialog *OpenCraftingStatusDialog( vgui::Panel *pParent, const char *pszText, bool bAnimateEllipses, bool bAllowClose, bool bShowOnExit ) { SetupCraftingStatusDialog( pParent ); g_CraftingStatusPanel->UpdateSchemeForVersion( false ); g_CraftingStatusPanel->SetDialogVariable( "updatetext", g_pVGuiLocalize->Find( pszText ) ); g_CraftingStatusPanel->ShowStatusUpdate( bAnimateEllipses, bAllowClose, bShowOnExit ); return g_CraftingStatusPanel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CloseCraftingStatusDialog( void ) { if ( g_CraftingStatusPanel ) { g_CraftingStatusPanel->OnCommand( "forceclose" ); } } //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive the craft response //----------------------------------------------------------------------------- class CGCCraftResponse : public GCSDK::CGCClientJob { public: CGCCraftResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CGCMsg msg( pNetPacket ); CUtlVector vecCraftedIndices; uint16 iItems = 0; if ( !msg.BReadUint16Data( &iItems ) ) return true; vecCraftedIndices.SetSize( iItems ); for ( int i = 0; i < iItems; i++ ) { if( !msg.BReadUint64Data( &vecCraftedIndices[i] ) ) return true; } if ( EconUI()->GetCraftingPanel() ) { EconUI()->GetCraftingPanel()->OnCraftResponse( (EGCMsgResponse)msg.Body().m_eResponse, &vecCraftedIndices, msg.Body().m_nResponseIndex ); } //Msg("RECEIVED CGCCraftResponse: %d\n", msg.Body().m_eResponse ); return true; } }; GC_REG_JOB( GCSDK::CGCClient, CGCCraftResponse, "CGCCraftResponse", k_EMsgGCCraftResponse, GCSDK::k_EServerTypeGCClient ); //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive the Golden Wrench broadcast message //----------------------------------------------------------------------------- class CGCGoldenWrenchBroadcast : public GCSDK::CGCClientJob { public: CGCGoldenWrenchBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); // @todo Tom Bui: should we display this in some other manner? This gets covered up by the crafting panel. CHudNotificationPanel *pNotifyPanel = GET_HUDELEMENT( CHudNotificationPanel ); if ( pNotifyPanel ) { bool bDeleted = msg.Body().deleted(); wchar_t szPlayerName[1024]; g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().user_name().c_str(), szPlayerName, sizeof(szPlayerName) ); wchar_t szWrenchNumber[16]=L""; _snwprintf( szWrenchNumber, ARRAYSIZE( szWrenchNumber ), L"%i", msg.Body().wrench_number() ); wchar_t szNotification[1024]=L""; g_pVGuiLocalize->ConstructString_safe( szNotification, g_pVGuiLocalize->Find( bDeleted ? "#TF_HUD_Event_GoldenWrench_D": "#TF_HUD_Event_GoldenWrench_C" ), 2, szPlayerName, szWrenchNumber ); pNotifyPanel->SetupNotifyCustom( szNotification, HUD_NOTIFY_GOLDEN_WRENCH, 10.0f ); // echo to chat CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); if ( pHUDChat ) { char szAnsi[1024]; g_pVGuiLocalize->ConvertUnicodeToANSI( szNotification, szAnsi, sizeof(szAnsi) ); pHUDChat->Printf( CHAT_FILTER_NONE, "%s", szAnsi ); } // play a sound vgui::surface()->PlaySound( bDeleted ? "vo/announcer_failure.mp3" : "vo/announcer_success.mp3" ); } //Msg("RECEIVED CGCCraftResponse: %d\n", msg.Body().m_eResponse ); return true; } }; GC_REG_JOB( GCSDK::CGCClient, CGCGoldenWrenchBroadcast, "CGCGoldenWrenchBroadcast", k_EMsgGCGoldenWrenchBroadcast, GCSDK::k_EServerTypeGCClient ); //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive the Saxxy broadcast message //----------------------------------------------------------------------------- class CGSaxxyBroadcast : public GCSDK::CGCClientJob { public: CGSaxxyBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); CEconNotification *pNotification = new CEconNotification(); pNotification->SetText( "#TF_Event_Saxxy_Deleted" ); pNotification->SetLifetime( 30.0f ); { // Who deleted this? wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ]; g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().has_user_name() ? msg.Body().user_name().c_str() : NULL, wszPlayerName, sizeof( wszPlayerName ) ); pNotification->AddStringToken( "owner", wszPlayerName ); // What category was the Saxxy for? char szCategory[MAX_ATTRIBUTE_DESCRIPTION_LENGTH]; Q_snprintf( szCategory, sizeof( szCategory ), "Replay_Contest_Category%d", msg.Body().category_number() ); pNotification->AddStringToken( "category", g_pVGuiLocalize->Find( szCategory ) ); } NotificationQueue_Add( pNotification ); return true; } }; GC_REG_JOB( GCSDK::CGCClient, CGSaxxyBroadcast, "CGSaxxyBroadcast", k_EMsgGCSaxxyBroadcast, GCSDK::k_EServerTypeGCClient ); //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive any generic item deletion notification //----------------------------------------------------------------------------- class CClientItemBroadcastNotificationJob : public GCSDK::CGCClientJob { public: CClientItemBroadcastNotificationJob( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); CEconNotification *pNotification = new CEconNotification(); pNotification->SetText( msg.Body().was_destruction() ? "#TF_Event_Item_Deleted" : "#TF_Event_Item_Created" ); pNotification->SetLifetime( 30.0f ); // Who deleted this? wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ]; g_pVGuiLocalize->ConvertANSIToUnicode( msg.Body().has_user_name() ? msg.Body().user_name().c_str() : NULL, wszPlayerName, sizeof( wszPlayerName ) ); pNotification->AddStringToken( "owner", wszPlayerName ); // What type of item was this? const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( msg.Body().item_def_index() ); if ( pItemDef ) { pNotification->AddStringToken( "item_name", g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) ); NotificationQueue_Add( pNotification ); } return true; } }; GC_REG_JOB( GCSDK::CGCClient, CClientItemBroadcastNotificationJob, "CClientItemBroadcastNotificationJob", k_EMsgGCTFSpecificItemBroadcast, GCSDK::k_EServerTypeGCClient ); //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive the Saxxy Awarded broadcast message //----------------------------------------------------------------------------- class CGSaxxyAwardedBroadcast : public GCSDK::CGCClientJob { private: // embedded notification for custom trigger class CSaxxyAwardedNotification : public CEconNotification { public: CSaxxyAwardedNotification() { SetSoundFilename( "vo/announcer_success.mp3" ); } virtual EType NotificationType() { return eType_Trigger; } virtual void Trigger() { if ( steamapicontext && steamapicontext->SteamFriends() ) { steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/saxxyawards/winners.php" ); } MarkForDeletion(); } }; public: CGSaxxyAwardedBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg< CMsgSaxxyAwarded > msg( pNetPacket ); CEconNotification *pNotification = new CSaxxyAwardedNotification(); pNotification->SetText( "#TF_Event_Saxxy_Awarded" ); pNotification->SetLifetime( 30.0f ); { // Winners CFmtStr1024 strWinners; for ( int i = 0; i < msg.Body().winner_names_size(); ++i ) { strWinners.Append( msg.Body().winner_names( i ).c_str() ); if ( i + 1 < msg.Body().winner_names_size() ) { strWinners.Append( "\n" ); } } wchar_t wszPlayerNames[ 1024 ]; g_pVGuiLocalize->ConvertANSIToUnicode( strWinners.Access(), wszPlayerNames, sizeof( wszPlayerNames ) ); pNotification->AddStringToken( "winners", wszPlayerNames ); // year CRTime cTime; cTime.SetToCurrentTime(); cTime.SetToGMT( false ); locchar_t wszYear[10]; loc_sprintf_safe( wszYear, LOCCHAR( "%04u" ), cTime.GetYear() ); pNotification->AddStringToken( "year", wszYear ); // What category was the Saxxy for? char szCategory[MAX_ATTRIBUTE_DESCRIPTION_LENGTH]; Q_snprintf( szCategory, sizeof( szCategory ), "Replay_Contest_Category%d", msg.Body().category() ); pNotification->AddStringToken( "category", g_pVGuiLocalize->Find( szCategory ) ); } NotificationQueue_Add( pNotification ); return true; } }; GC_REG_JOB( GCSDK::CGCClient, CGSaxxyAwardedBroadcast, "CGSaxxyAwardedBroadcast", k_EMsgGCSaxxy_Awarded, GCSDK::k_EServerTypeGCClient ); //----------------------------------------------------------------------------- // Purpose: GC Msg handler to receive a generic system broadcast message //----------------------------------------------------------------------------- class CGCSystemMessageBroadcast : public GCSDK::CGCClientJob { public: CGCSystemMessageBroadcast( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) { CBaseHudChat *pHUDChat = (CBaseHudChat *)GET_HUDELEMENT( CHudChat ); if ( !pHUDChat ) return false; GCSDK::CProtoBufMsg msg( pNetPacket ); // retrieve the text const char *pchMessage = msg.Body().message().c_str(); wchar_t *pwMessage = g_pVGuiLocalize->Find( pchMessage ); wchar_t wszConvertedText[2048] = L""; if ( pwMessage == NULL ) { g_pVGuiLocalize->ConvertANSIToUnicode( pchMessage, wszConvertedText, sizeof( wszConvertedText ) ); pwMessage = wszConvertedText; } Color color( 0xff, 0xcc, 0x33, 255 ); KeyValuesAD keyValues( "System Message" ); keyValues->SetWString( "message", pwMessage ); keyValues->SetColor( "custom_color", color ); // print to chat log wchar_t wszLocalizedString[2048] = L""; g_pVGuiLocalize->ConstructString_safe( wszLocalizedString, "#Notification_System_Message", keyValues ); pHUDChat->SetCustomColor( color ); pHUDChat->Printf( CHAT_FILTER_NONE, "%ls", wszLocalizedString ); // send to notification CEconNotification* pNotification = new CEconNotification(); pNotification->SetText( "#Notification_System_Message" ); pNotification->SetKeyValues( keyValues ); pNotification->SetLifetime( 30.0f ); pNotification->SetSoundFilename( "ui/system_message_alert.wav" ); NotificationQueue_Add( pNotification ); return true; } }; GC_REG_JOB( GCSDK::CGCClient, CGCSystemMessageBroadcast, "CGCSystemMessageBroadcast", k_EMsgGCSystemMessage, GCSDK::k_EServerTypeGCClient );