diff options
Diffstat (limited to 'game/client/econ/backpack_panel.cpp')
| -rw-r--r-- | game/client/econ/backpack_panel.cpp | 4272 |
1 files changed, 4272 insertions, 0 deletions
diff --git a/game/client/econ/backpack_panel.cpp b/game/client/econ/backpack_panel.cpp new file mode 100644 index 0000000..eb90d6b --- /dev/null +++ b/game/client/econ/backpack_panel.cpp @@ -0,0 +1,4272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "backpack_panel.h" +#include "item_confirm_delete_dialog.h" +#include "vgui/ISurface.h" +#include "gamestringpool.h" +#include "iclientmode.h" +#include "econ_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/ScalableImagePanel.h" +#include "vgui/IInput.h" +#include "econ/tool_items/tool_items.h" +#include "econ_gcmessages.h" +#include "item_style_select_dialog.h" +#include "econ_item_system.h" +#include "econ_item_tools.h" +#include "econ_ui.h" +#include "gc_clientsystem.h" +#include "econ_store.h" +#include "rtime.h" +#include "econ_item_description.h" +#include "dynamic_recipe_subpanel.h" +#include "item_slot_panel.h" +#include "crate_detail_panels.h" +#include "tf_warinfopanel.h" +#include "character_info_panel.h" +#include "trading_start_dialog.h" +#include "vgui_controls/MenuItem.h" +#include "tf_duckleaderboard.h" +#include "tf_item_inventory.h" +#include "store/store_panel.h" +#include "strange_count_transfer_panel.h" +#include "collection_crafting_panel.h" +#include "halloween_offering_panel.h" +#include "store/v2/tf_store_preview_item2.h" +#include "item_ad_panel.h" +#include "client_community_market.h" +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +#ifdef STAGING_ONLY +extern ConVar tf_use_card_tooltips; +extern ConVar tf_weapon_force_allow_inspect; +#endif // STAGING_ONLY + +ConVar tf_trade_up_use_count( "tf_trade_up_use_count", "3", FCVAR_ARCHIVE | FCVAR_HIDDEN ); + + +void UseConsumableItem( CEconItemView *pItem, vgui::Panel* pParent ); + +const ItemSortTypeData_t g_BackpackSortTypes[] = +{ + { "#Backpack_SortBy_Header", kGCItemSort_NoSort }, + { "#Backpack_SortBy_Rarity", kGCItemSort_SortByRarity }, + { "#Backpack_SortBy_Type", kGCItemSort_SortByType }, + { "#Backpack_SortBy_Class", kTFGCItemSort_SortByClass }, + { "#Backpack_SortBy_Slot", kTFGCItemSort_SortBySlot }, + { "#Backpack_SortBy_Date", kGCItemSort_SortByDate }, +}; + +// Array of borders for rarities. Three borders for each rarity: Base, Mouseover, and Selected +const char *g_szItemBorders[][5] = +{ + { "BackpackItemBorder", "BackpackItemMouseOverBorder", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder", "BackpackItemGreyedOutSelectedBorder" }, // AE_NORMAL = 0 + { "BackpackItemBorder_1", "BackpackItemMouseOverBorder_1", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_1", "BackpackItemGreyedOutSelectedBorder_1" }, // AE_RARITY1 = 1 + { "BackpackItemBorder_2", "BackpackItemMouseOverBorder_2", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_2", "BackpackItemGreyedOutSelectedBorder_2" }, // AE_RARITY2 = 2 + { "BackpackItemBorder_Vintage", "BackpackItemMouseOverBorder_Vintage", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Vintage", "BackpackItemGreyedOutSelectedBorder_Vintage" }, // AE_VINTAGE = 3 + { "BackpackItemBorder_3", "BackpackItemMouseOverBorder_3", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_3", "BackpackItemGreyedOutSelectedBorder_3" }, // AE_RARITY3 + { "BackpackItemBorder_4", "BackpackItemMouseOverBorder_4", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_4", "BackpackItemGreyedOutSelectedBorder_4" }, // AE_RARITY4 + { "BackpackItemBorder_Unique", "BackpackItemMouseOverBorder_Unique", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Unique", "BackpackItemGreyedOutSelectedBorder_Unique" }, // AE_UNIQUE + { "BackpackItemBorder_Community", "BackpackItemMouseOverBorder_Community", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Community", "BackpackItemGreyedOutSelectedBorder_Community" }, // AE_COMMUNITY + { "BackpackItemBorder_Developer", "BackpackItemMouseOverBorder_Developer", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Developer", "BackpackItemGreyedOutSelectedBorder_Developer" }, // AE_DEVELOPER + { "BackpackItemBorder_SelfMade", "BackpackItemMouseOverBorder_SelfMade", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_SelfMade", "BackpackItemGreyedOutSelectedBorder_SelfMade" }, // AE_SELFMADE + { "BackpackItemBorder_Customized", "BackpackItemMouseOverBorder_Customized", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Customized", "BackpackItemGreyedOutSelectedBorder_Customized" }, // AE_CUSTOMIZED + { "BackpackItemBorder_Strange", "BackpackItemMouseOverBorder_Strange", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Strange", "BackpackItemGreyedOutSelectedBorder_Strange" }, // AE_STRANGE + { "BackpackItemBorder_Completed", "BackpackItemMouseOverBorder_Completed", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Completed", "BackpackItemGreyedOutSelectedBorder_Completed" }, // AE_COMPLETED + { "BackpackItemBorder_Haunted", "BackpackItemMouseOverBorder_Haunted", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Haunted", "BackpackItemGreyedOutSelectedBorder_Haunted" }, // AE_HAUNTED + { "BackpackItemBorder_Collectors", "BackpackItemMouseOverBorder_Collectors", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_Collectors", "BackpackItemGreyedOutSelectedBorder_Collectors" }, // AE_COLLECTORS + + { "BackpackItemBorder_PaintkitWeapon", "BackpackItemMouseOverBorder_PaintkitWeapon", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_PaintkitWeapon", "BackpackItemGreyedOutSelectedBorder_PaintkitWeapon" }, // AE_Paintkit + { "BackpackItemBorder_RarityDefault", "BackpackItemMouseOverBorder_RarityDefault", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityDefault", "BackpackItemGreyedOutSelectedBorder_RarityDefault" }, // AE_RARITY_DEFAULT, + { "BackpackItemBorder_RarityCommon", "BackpackItemMouseOverBorder_RarityCommon", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityCommon", "BackpackItemGreyedOutSelectedBorder_RarityCommon" }, // AE_RARITY_COMMON, + { "BackpackItemBorder_RarityUncommon", "BackpackItemMouseOverBorder_RarityUncommon", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityUncommon", "BackpackItemGreyedOutSelectedBorder_RarityUncommon" }, // AE_RARITY_UNCOMMON, + { "BackpackItemBorder_RarityRare", "BackpackItemMouseOverBorder_RarityRare", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityRare", "BackpackItemGreyedOutSelectedBorder_RarityRare" }, // AE_RARITY_RARE, + { "BackpackItemBorder_RarityMythical", "BackpackItemMouseOverBorder_RarityMythical", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityMythical", "BackpackItemGreyedOutSelectedBorder_RarityMythical" }, // AE_RARITY_MYTHICAL, + { "BackpackItemBorder_RarityLegendary", "BackpackItemMouseOverBorder_RarityLegendary", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityLegendary", "BackpackItemGreyedOutSelectedBorder_RarityLegendary" }, // AE_RARITY_LEGENDARY, + { "BackpackItemBorder_RarityAncient", "BackpackItemMouseOverBorder_RarityAncient", "BackpackItemSelectedBorder", "BackpackItemGreyedOutBorder_RarityAncient", "BackpackItemGreyedOutSelectedBorder_RarityAncient" }, // AE_RARITY_ANCIENT, +}; + +COMPILE_TIME_ASSERT( ARRAYSIZE(g_szItemBorders) == AE_MAX_TYPES ); + +enum { kNoUserData = -1 }; + +static bool HasPaint ( const CEconItemView *pEconItemView, const char *, int ) +{ + static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" ); + static CSchemaAttributeDefHandle pAttrDef_PaintRGB2( "set item tint RGB 2" ); + + return pEconItemView->FindAttribute( pAttrDef_PaintRGB ) + || pEconItemView->FindAttribute( pAttrDef_PaintRGB2 ); +} + +static bool HasCustomAttribute ( const CEconItemView *pEconItemView, const char *szAttrName, int ) +{ + const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( szAttrName ); + + return pAttrDef + ? pEconItemView->FindAttribute( pAttrDef ) + : NULL; +} + +static bool HasCustomUserAttribute ( const CEconItemView *pEconItemView, const char *, int iUserData ) +{ + Assert( iUserData != kNoUserData ); + + CCountUserGeneratedAttributeIterator countIterator; + pEconItemView->IterateAttributes( &countIterator ); + + return countIterator.GetCount() > iUserData; +} + +static bool HasRemovableCustomName ( const CEconItemView *pEconItemView, const char *, int ) +{ + if ( !pEconItemView->GetItemDefinition() ) + return false; + + if ( pEconItemView->GetQuality() == AE_UNIQUE && pEconItemView->GetItemDefinition()->GetArmoryDescString() && !V_stricmp( pEconItemView->GetItemDefinition()->GetArmoryDescString(), "stockitem" ) ) + return false; + + return pEconItemView->GetSOCData() && pEconItemView->GetSOCData()->GetCustomName(); +} + +static bool HasRemovableCustomDesc ( const CEconItemView *pEconItemView, const char *, int ) +{ + if ( !pEconItemView->GetItemDefinition() ) + return false; + + if ( pEconItemView->GetQuality() == AE_UNIQUE && pEconItemView->GetItemDefinition()->GetArmoryDescString() && !V_stricmp( pEconItemView->GetItemDefinition()->GetArmoryDescString(), "stockitem" ) ) + return false; + + return pEconItemView->GetSOCData() && pEconItemView->GetSOCData()->GetCustomDesc(); +} + +enum EItemCustomizationRemoveType +{ + kCustomizationRemove_Paint, + kCustomizationRemove_Name, + kCustomizationRemove_Desc, + kCustomizationRemove_CustomTexture, + kCustomizationRemove_MakersMark, + kCustomizationRemove_UniqueCraftIndex, + kCustomizationRemove_StrangePart, + kCustomizationRemove_StrangeScores, + kCustomizationRemove_UpgradeCard, + kCustomizationRemove_KillStreak, + kCustomizationRemove_GiftedBy, + kCustomizationRemove_Festivizer, +}; + +typedef bool (* HasRefurbishablePropertyFunc_t)( const CEconItemView *pEconItemView, const char *pArg, int iUserData ); + +void GetCustomDialogToken_PaintName( const CEconItemView *pEconItemView, int iUserData, CUtlConstWideString& out_String ) +{ + extern const CEconItemDefinition *GetPaintItemDefinitionForPaintedItem( const IEconItemInterface *pEconItem ); + + Assert( iUserData == kNoUserData ); + + const CEconItemDefinition *pPaintItemDef = GetPaintItemDefinitionForPaintedItem( pEconItemView ); + if ( !pPaintItemDef ) + { + out_String = L""; + return; + } + + out_String = GLocalizationProvider()->Find( pPaintItemDef->GetItemBaseName() ); +} + +void GetCustomDialogToken_StrangePartName( const CEconItemView *pEconItemView, int iUserData, CUtlConstWideString& out_String ) +{ + extern uint32 GetScoreTypeForKillEaterAttr( const IEconItemInterface *pEconItem, const CEconItemAttributeDefinition *pAttribDef ); + extern const wchar_t *GetLocalizedStringForKillEaterTypeAttr( const CLocalizationProvider *pLocalizationProvider, uint32 unKillEaterEventType ); // return type changed from locchar_t * because the backpack panel only exists on the client + + uint32 unKillEaterBaseType = GetScoreTypeForKillEaterAttr( pEconItemView, GetKillEaterAttr_Type( iUserData ) ); + + out_String = GetLocalizedStringForKillEaterTypeAttr( GLocalizationProvider(), unKillEaterBaseType ); +} + +void GetCustomDialogToken_UserAttributeName( const CEconItemView *pEconItemView, int iUserData, CUtlConstWideString& out_String ) +{ + Assert( pEconItemView ); + + const CEconItemAttributeDefinition *pAttrDef = GetCardUpgradeForIndex( pEconItemView, iUserData ); + if ( !pAttrDef ) + { + out_String = L"unknown"; + return; + } + + attrib_value_t attrVal; + Verify( pEconItemView->FindAttribute( pAttrDef, &attrVal ) ); + CEconAttributeDescription attrDesc( GLocalizationProvider(), pAttrDef, attrVal ); + out_String = attrDesc.GetShortDescription(); +} + +typedef void (* GetCustomDialogLocalizationTokenFunc_t)( const CEconItemView *pEconItemView, int iUserData, CUtlConstWideString& out_String ); + +struct RefurbishableProperty +{ + HasRefurbishablePropertyFunc_t m_pFunc; + GetCustomDialogLocalizationTokenFunc_t m_pGetCustomDialogLocalizationTokenFunc; + const char *m_szArg; + const char *m_pszSelectionUILocalizationToken; + const char *m_szDialogTitle; + const char *m_szDialogDesc; + EItemCustomizationRemoveType m_eRemovalType; + int m_iUserData; +}; + +// TODO: Add Gifted by Tag here +static RefurbishableProperty g_RemoveableAttributes[] = +{ + { &HasRemovableCustomName, NULL, NULL, "#RefurbishItem_RemoveNameCombo", "#RefurbishItem_RemoveNameTitle", "#RefurbishItem_RemoveName", kCustomizationRemove_Name, kNoUserData }, // does this item have a custom name? + { &HasRemovableCustomDesc, NULL, NULL, "#RefurbishItem_RemoveDescCombo", "#RefurbishItem_RemoveDescTitle", "#RefurbishItem_RemoveDesc", kCustomizationRemove_Desc, kNoUserData }, // does this item have a custom description? + { &HasPaint, &GetCustomDialogToken_PaintName, "set item tint rgb", "#RefurbishItem_RemovePaintCombo", "#RefurbishItem_RemovePaintTitle", "#RefurbishItem_RemovePaint", kCustomizationRemove_Paint, kNoUserData }, // is this item painted? + { &HasCustomAttribute, NULL, "custom texture hi", "#RefurbishItem_RemoveCustomTextureCombo", "#RefurbishItem_RemoveCustomTextureTitle", "#RefurbishItem_RemoveCustomTexture", kCustomizationRemove_CustomTexture, kNoUserData }, // does this have a custom texture applied? + { &HasCustomAttribute, NULL, "makers mark id", "#RefurbishItem_RemoveMakersMarkCombo", "#RefurbishItem_RemoveMakersMarkTitle", "#RefurbishItem_RemoveMakersMark", kCustomizationRemove_MakersMark, kNoUserData }, // was this item crafted by a specific dude? + { &HasCustomAttribute, NULL, "killstreak tier", "#RefurbishItem_RemoveKillStreakCombo", "#RefurbishItem_RemoveKillStreakTitle", "#RefurbishItem_RemoveKillStreak", kCustomizationRemove_KillStreak, kNoUserData }, // Killstreak Effect + { &HasCustomAttribute, NULL, "gifter account id", "#RefurbishItem_RemoveGifterCombo", "#RefurbishItem_RemoveGifterTitle", "#RefurbishItem_RemoveGifter", kCustomizationRemove_GiftedBy, kNoUserData }, // Gifted by + { &HasCustomAttribute, NULL, "is_festivized", "#RefurbishItem_RemoveFestivizerCombo", "#RefurbishItem_RemoveFestivizerTitle", "#RefurbishItem_RemoveFestivizer", kCustomizationRemove_Festivizer, kNoUserData }, // Festivizer + + //"gifter account id", // who gifted us this item? (will also remove "event date") +}; + +//----------------------------------------------------------------------------- +// Purpose: Look over this weapon to see if it has any strange stat counters to reset optionally. +//----------------------------------------------------------------------------- +static bool HasResettableScoreAttributes ( const CEconItemView *pEconItemView, const char *, int ) +{ + if ( !pEconItemView ) + return false; + + for ( int i = 0; i < GetKillEaterAttrCount(); i++ ) + { + uint32 unScore; + if ( pEconItemView->FindAttribute( GetKillEaterAttr_Score( i ), &unScore ) && unScore > 0 ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int GetRemovableAttributesCount() +{ + return ARRAYSIZE( g_RemoveableAttributes ) + + GetKillEaterAttrCount() + + GetMaxCardUpgradesPerItem() // remove card upgrades + + 1; // strange quality item score reset +} + +RefurbishableProperty RemovableAttributes_GetAttributeDetails( int i ) +{ + Assert( i >= 0 ); + Assert( i < GetRemovableAttributesCount() ); + + if ( i < ARRAYSIZE( g_RemoveableAttributes ) ) + return g_RemoveableAttributes[i]; + + // Which attribute in particular are we looking for? + int iStrangePartIndex = i - ARRAYSIZE( g_RemoveableAttributes ); + if ( iStrangePartIndex < GetKillEaterAttrCount() ) + { + int iKillEaterAttrIndex = (GetKillEaterAttrCount() - GetKillEaterAttrCount()) + iStrangePartIndex; + + // if we're looking at strange attributes... + if ( GetKillEaterAttr_IsUserCustomizable( iKillEaterAttrIndex ) ) + { + // Common properties for all strange part attributes. + static RefurbishableProperty sStrangePartProperty = { &HasCustomAttribute, &GetCustomDialogToken_StrangePartName, NULL, "#RefurbishItem_RemoveStrangePartCombo", "#RefurbishItem_RemoveStrangePartTitle", "#RefurbishItem_RemoveStrangePart", kCustomizationRemove_StrangePart, kNoUserData }; + + RefurbishableProperty partReturnProp = sStrangePartProperty; + partReturnProp.m_szArg = GetKillEaterAttr_Score( iKillEaterAttrIndex )->GetDefinitionName(); // ...then we check for the presence of a score attribute if this slot is a strange part... + partReturnProp.m_iUserData = iKillEaterAttrIndex; + + return partReturnProp; + } + + // ...or the presence of a restriction attribute if this slot is a base slot that might have a filter + static RefurbishableProperty sStrangeFilterProperty = { &HasCustomAttribute, &GetCustomDialogToken_StrangePartName, NULL, "#RefurbishItem_RemoveStrangeFilterCombo", "#RefurbishItem_RemoveStrangeFilterTitle", "#RefurbishItem_RemoveStrangeFilter", kCustomizationRemove_StrangePart, kNoUserData }; + + RefurbishableProperty filterReturnProp = sStrangeFilterProperty; + filterReturnProp.m_szArg = GetKillEaterAttr_Restriction( iKillEaterAttrIndex )->GetDefinitionName(); + filterReturnProp.m_iUserData = iKillEaterAttrIndex; + + return filterReturnProp; + } + + // Look for any properties that were user-assigned. We allow users to remove them. + int iCardUpgradeIndex = iStrangePartIndex - GetKillEaterAttrCount(); + if ( iCardUpgradeIndex < GetMaxCardUpgradesPerItem() ) + { + // Common properties for all card upgrade attributes. + static RefurbishableProperty sCardUpgradeProperty = { &HasCustomUserAttribute, &GetCustomDialogToken_UserAttributeName, NULL, "#RefurbishItem_RemoveSpellCombo", "#RefurbishItem_RemoveSpellTitle", "#RefurbishItem_RemoveSpellUpgrade", kCustomizationRemove_UpgradeCard, kNoUserData }; + + RefurbishableProperty returnProp = sCardUpgradeProperty; + // FIX THIS FOR CARDS / SPELLS? + // returnProp.m_szArg = GetCustomDialogToken_UserAttributeName ? + returnProp.m_iUserData = iCardUpgradeIndex; + + return returnProp; + } + + // We might also be trying to reset the strange score counters. + Assert( iStrangePartIndex == GetKillEaterAttrCount() + GetMaxCardUpgradesPerItem() ); + Assert( i == GetRemovableAttributesCount() - 1 ); + + static RefurbishableProperty sStrangeScoreReset = { &HasResettableScoreAttributes, NULL, NULL, "#RefurbishItem_RemoveStrangeScoresCombo", "#RefurbishItem_RemoveStrangeScoresTitle", "#RefurbishItem_RemoveStrangeScores", kCustomizationRemove_StrangeScores, kNoUserData }; + return sStrangeScoreReset; +} + +bool RemovableAttributes_DoesAttributeApply( int i, const CEconItemView *pEconItemView ) +{ + static CSchemaAttributeDefHandle pAttr_CannotRestore( "cannot restore" ); + if ( pEconItemView->FindAttribute( pAttr_CannotRestore ) ) + return false; + + RefurbishableProperty attr = RemovableAttributes_GetAttributeDetails( i ); + + return attr.m_pFunc( pEconItemView, attr.m_szArg, attr.m_iUserData ); +} + +bool RemovableAttributes_DoAnyAttributesApply( const CEconItemView *pEconItemView ) +{ + static CSchemaAttributeDefHandle pAttr_CannotRestore( "cannot restore" ); + if ( pEconItemView->FindAttribute( pAttr_CannotRestore ) ) + return false; + + for ( int i = 0; i < GetRemovableAttributesCount(); i++ ) + { + if ( RemovableAttributes_DoesAttributeApply( i, pEconItemView ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ConVar cl_showbackpackrarities( "cl_showbackpackrarities", "0", FCVAR_ARCHIVE, "0 = Show no backpack icon border colors. 1 = Show item rarities within the backpack. 2 = Show item rarities only for Market-listable items." ); +ConVar cl_show_market_data_on_items( "cl_show_market_data_on_items", "1", FCVAR_ARCHIVE, "0 = Never. 1 = Only when showing borders for Market-listable items. 2 = Always." ); + +ConVar tf_explanations_backpackpanel( "tf_explanations_backpackpanel", "0", FCVAR_ARCHIVE, "Whether the user has seen explanations for this panel." ); + +ConVar tf_backpack_page_button_delay( "tf_backpack_page_button_delay", "0.5", FCVAR_ARCHIVE, "Amount of time the mouse cursor needs to hover over the page button to select the page." ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBackpackPanel::CBackpackPanel( vgui::Panel *parent, const char *panelName ) : CBaseLoadoutPanel( parent, panelName ) +{ + m_nQuickOpenTxn = 0; + m_pContextMenu = NULL; + m_pPageButtonKVs = NULL; + m_mapSeenItems.SetLessFunc( DefLessFunc( itemid_t ) ); + m_bInitializedSeenItems = false; + m_pNameFilterTextEntry = NULL; + m_flFilterItemTime = 0.f; + m_mapFilteringItems.SetLessFunc( DefLessFunc(int) ); + + m_pNextPageButton = NULL; + m_pPrevPageButton = NULL; + m_pShowExplanationsButton = NULL; + m_pCurPageLabel = NULL; + m_pSortByComboBox = NULL; + m_pShowRarityComboBox = NULL; + m_pShowBaseItemsCheckbox = NULL; + m_pDragToNextPageButton = NULL; + m_pDragToPrevPageButton = NULL; + m_flPreventDragPageSwitchUntil = 0; + m_flStartExplanationsAt = 0; + + m_flMouseDownTime = 0; + m_pItemDraggedFromPanel = NULL; + m_iDraggedFromPage = 0; + m_bMouseDownOnItemPanel = false; + m_bDragging = false; + m_iMouseDownX = m_iMouseDownY = 0; + + m_pMouseDragItemPanel = vgui::SETUP_PANEL( new CItemModelPanel( this, "mousedragitempanel" ) ); + m_pCancelToolButton = NULL; + m_pCraftButton = NULL; + m_bShowBaseItems = false; + m_pConfirmDeleteDialog = NULL; + m_pToolIcon = NULL; + m_eSelectionMode = StandardSelection; + m_nLastToolPage = 0; + m_pDynamicRecipePanel = NULL; + m_pItemSlotPanel = NULL; + m_pStrangeToolPanel = NULL; + + m_nNumActivePages = 0; + + m_pInspectPanel = new CTFItemInspectionPanel( this, "InspectionPanel" ); + m_pInspectCosmeticPanel = new CTFStorePreviewItemPanel2( this, "Resource/UI/econ/InspectionPanel_Cosmetic.res", "storepreviewitem", NULL ); + m_pCollectionCraftPanel = NULL; + m_pHalloweenOfferingPanel = NULL; + m_pMannCoTradePanel = NULL; + + CancelToolSelection(); + + ListenForGameEvent( "gc_connected" ); +} + +CBackpackPanel::~CBackpackPanel() +{ + if ( m_pPageButtonKVs ) + { + m_pPageButtonKVs->deleteThis(); + m_pPageButtonKVs = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + if ( !m_pSortByComboBox && UsesRarityControls() ) + { + m_pSortByComboBox = new vgui::ComboBox( this, "SortByComboBox", 5, false ); + m_pSortByComboBox->AddActionSignalTarget( this ); + } + + LoadControlSettings( GetResFile() ); + + BaseClass::ApplySchemeSettings( pScheme ); + + m_pNameFilterTextEntry = FindControl<vgui::TextEntry>( "NameFilterTextEntry" ); + if ( m_pNameFilterTextEntry ) + { + m_pNameFilterTextEntry->AddActionSignalTarget( this ); + } + + m_pCancelToolButton = dynamic_cast<CExButton*>( FindChildByName("CancelApplyToolButton") ); + m_pCraftButton = dynamic_cast<CExButton*>( FindChildByName("CraftButton") ); + m_pToolIcon = dynamic_cast<vgui::ScalableImagePanel*>( FindChildByName("tool_icon") ); + + m_pNextPageButton = dynamic_cast<CExButton*>( FindChildByName("NextPageButton") ); + m_pPrevPageButton = dynamic_cast<CExButton*>( FindChildByName("PrevPageButton") ); + m_pShowExplanationsButton = dynamic_cast<CExButton*>( FindChildByName("ShowExplanationsButton") ); + m_pDragToNextPageButton = dynamic_cast<CExButton*>( FindChildByName("DragToNextPageButton") ); + m_pDragToPrevPageButton = dynamic_cast<CExButton*>( FindChildByName("DragToPrevPageButton") ); + m_pCurPageLabel = dynamic_cast<vgui::Label*>( FindChildByName("CurPageLabel") ); + m_pShowRarityComboBox = dynamic_cast<vgui::ComboBox*>( FindChildByName( "ShowRarityComboBox" ) ); + if ( m_pShowRarityComboBox ) + { + m_pShowRarityComboBox->AddActionSignalTarget( this ); + + m_pShowRarityComboBox->AddItem( "#TF_Backpack_ShowNoBorders", NULL ); + m_pShowRarityComboBox->AddItem( "#TF_Backpack_ShowQualityBorders", NULL ); + m_pShowRarityComboBox->AddItem( "#TF_Backpack_ShowMarketableBorders", NULL ); + + m_pShowRarityComboBox->ActivateItemByRow( cl_showbackpackrarities.GetInt() ); + } + m_pShowBaseItemsCheckbox = dynamic_cast<vgui::CheckButton*>( FindChildByName( "ShowBaseItemsCheckbox" ) ); + if ( m_pShowBaseItemsCheckbox ) + { + m_pShowBaseItemsCheckbox->AddActionSignalTarget( this ); + m_pShowBaseItemsCheckbox->SetSelected( m_bShowBaseItems ); + } + + m_pMouseDragItemPanel->SetBorder( pScheme->GetBorder("BackpackItemMouseOverBorder") ); + + // Setup our combo box + if ( m_pSortByComboBox ) + { + m_pSortByComboBox->RemoveAll(); + vgui::HFont hFont = pScheme->GetFont( "HudFontSmallestBold", true ); + m_pSortByComboBox->SetFont( hFont ); + KeyValues *pKeyValues = new KeyValues( "data" ); + for ( int i = 0; i < ARRAYSIZE(g_BackpackSortTypes); i++ ) + { + pKeyValues->SetInt( "sortby", i ); + m_pSortByComboBox->AddItem( g_BackpackSortTypes[i].szSortDesc, pKeyValues ); + } + pKeyValues->deleteThis(); + m_pSortByComboBox->ActivateItemByRow( 0 ); + m_pSortByComboBox->GetMenu()->SetNumberOfVisibleItems( ARRAYSIZE(g_BackpackSortTypes) ); + } + + // Create page buttons + const int nNumMaxPages = GetNumMaxPages(); + for ( int i=m_Pages.Count(); i<nNumMaxPages; ++i ) + { + EditablePanel *pPage = vgui::SETUP_PANEL( new EditablePanel( this, CFmtStr( "page_%d", i ) ) ); + m_Pages.AddToTail( pPage ); + } + + if ( m_pInspectCosmeticPanel ) + { + // Force it to load it's scheme now, because it needs to be done before we set it's visibility below + m_pInspectCosmeticPanel->InvalidateLayout( false, true ); + m_pInspectCosmeticPanel->SetVisible( false ); + } +} + +void CBackpackPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + KeyValues *pItemKV = inResourceData->FindKey( "pagebuttons_kv" ); + if ( pItemKV ) + { + if ( m_pPageButtonKVs ) + { + m_pPageButtonKVs->deleteThis(); + } + m_pPageButtonKVs = new KeyValues("pagebuttons_kv"); + pItemKV->CopySubkeys( m_pPageButtonKVs ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::AddNewItemPanel( int iPanelIndex ) +{ + BaseClass::AddNewItemPanel( iPanelIndex ); + + // Store a position for our new panel + m_ItemModelPanelPos.AddToTail(); + m_ItemModelPanelPos[iPanelIndex].x = m_ItemModelPanelPos[iPanelIndex].y = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CItemModelPanel *CBackpackPanel::GetItemPanelAtPos( int x, int y ) +{ + if ( !m_pItemModelPanels.Count() ) + return NULL; + + int iW = m_pItemModelPanels[0]->GetWide(); + int iH = m_pItemModelPanels[0]->GetTall(); + for ( int i = 0; i < m_ItemModelPanelPos.Count(); i++ ) + { + if ( (x < m_ItemModelPanelPos[i].x) || (x > (m_ItemModelPanelPos[i].x + iW)) ) + continue; + if ( (y < m_ItemModelPanelPos[i].y) || (y > (m_ItemModelPanelPos[i].y + iH)) ) + continue; + return m_pItemModelPanels[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBackpackPanel::GetPageButtonIndexAtPos( int x, int y ) +{ + if ( !m_Pages.Count() ) + return -1; + + int iW = m_Pages[0]->GetWide(); + int iH = m_Pages[0]->GetTall(); + for ( int i = 0; i < m_PageButtonPos.Count(); i++ ) + { + if ( (x < m_PageButtonPos[i].x) || (x > (m_PageButtonPos[i].x + iW)) ) + continue; + if ( (y < m_PageButtonPos[i].y) || (y > (m_PageButtonPos[i].y + iH)) ) + continue; + return m_Pages[i]->IsVisible() ? i : -1; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Change the text color on the page buttons based on the context of the +// page they represent. +//----------------------------------------------------------------------------- +void CBackpackPanel::SetPageButtonTextColorBasedOnContents() +{ + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + + if ( m_Pages.Count() == 0 ) + return; + + if ( !pScheme ) + return; + + const Color& colorEmpty = pScheme->GetColor( "TanDarker", Color( 235, 226, 202, 255 ) ); + const Color& colorPartial = Color( 170, 161, 137, 255 ); + const Color& colorFull = pScheme->GetColor( "TanLight", Color( 235, 226, 202, 255 ) ); + const Color& colorSelected = pScheme->GetColor( "TFOrange", Color( 145, 73, 59, 255 ) ); + + CUtlVector<int> vecPageCount; + CUtlVector<int> vecNewPageCount; + vecPageCount.EnsureCount( m_Pages.Count() ); + vecNewPageCount.EnsureCount( m_Pages.Count() ); + // Initialize to 0 + FOR_EACH_VEC( vecPageCount, i ) + { + vecPageCount[i] = 0; + vecNewPageCount[i] = 0; + } + + CPlayerInventory *pInv = InventoryManager()->GetLocalInventory(); + Assert( pInv ); + // Tally up how many items are on each page + if ( pInv ) + { + for ( int i = 0 ; i < pInv->GetItemCount() ; ++i ) + { + CEconItemView *pItem = pInv->GetItem( i ); + const int nSlot = InventoryManager()->GetBackpackPositionFromBackend( pItem->GetInventoryPosition() ) - 1; + const int nPage = nSlot / GetNumSlotsPerPage(); + if ( nPage >= 0 && nPage < m_Pages.Count() ) + { + vecPageCount[ nPage ] = vecPageCount[ nPage ] + 1; + + // Unackknowledged items technically are on the 1st page, so dont count them + if ( m_mapSeenItems.Find( pItem->GetItemID() ) == m_mapSeenItems.InvalidIndex() + && IsUnacknowledged( pItem->GetInventoryPosition() ) == false && !m_bShowBaseItems && !HasNameFilter() ) + { + vecNewPageCount[ nPage ] = vecNewPageCount[ nPage ] + 1; + } + } + } + } + + // Set the color for each page button + FOR_EACH_VEC( m_Pages, i ) + { + const int nNewCount = vecNewPageCount[i]; + const int nCount = vecPageCount[i]; + CExButton* pButton = dynamic_cast<CExButton*>( m_Pages[i]->FindChildByName( "Button" ) ); + if ( pButton ) + { + Color setColor = colorEmpty; + const Color& bgColor = GetCurrentPage() == i ? colorSelected : pButton->GetButtonDefaultBgColor(); + + if ( nCount == GetNumSlotsPerPage() ) + setColor = colorFull; + else if ( nCount > 0 ) + setColor = colorPartial; + + pButton->SetSelectedColor( setColor, pButton->GetButtonSelectedBgColor() ); + pButton->SetDefaultColor( setColor, bgColor ); + pButton->SetArmedColor( setColor, pButton->GetButtonArmedBgColor() ); + pButton->SetDepressedColor( setColor, pButton->GetButtonDepressedBgColor() ); + } + + // Show our "NEW!" label if there's any unseen items on that page + CExLabel* pNew = dynamic_cast<CExLabel*>( m_Pages[i]->FindChildByName( "New" ) ); + if ( pNew ) + { + pNew->SetVisible( nNewCount > 0 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::MarkItemIDDirty( itemid_t itemID ) +{ + if ( m_vecDirtyItems.Find( itemID ) == m_vecDirtyItems.InvalidIndex() ) + { + m_vecDirtyItems.AddToTail( itemID ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::PositionItemPanel( CItemModelPanel *pPanel, int iIndex ) +{ + int iCenter = GetWide() * 0.5; + int iButtonX = (iIndex % GetNumColumns()); + int iButtonY = (iIndex / GetNumColumns()); + int iXPos = (iCenter + m_iItemBackpackOffcenterX) + (iButtonX * m_pItemModelPanels[iIndex]->GetWide()) + (m_iItemBackpackXDelta * iButtonX); + int iYPos = m_iItemYPos + (iButtonY * m_pItemModelPanels[iIndex]->GetTall() ) + (m_iItemBackpackYDelta * iButtonY); + + m_pItemModelPanels[iIndex]->SetPos( iXPos, iYPos ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::PerformLayout( void ) +{ + BaseClass::PerformLayout(); + + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + // Viewing the backpack. Layout all the buttons and hide the class image. + m_pItemModelPanels[i]->SetVisible( true ); + m_pItemModelPanels[i]->SetNoItemText( "#SelectNoItemSlot" ); + + PositionItemPanel( m_pItemModelPanels[i], i ); + + // Cache off where we put the panel + m_pItemModelPanels[i]->GetPos( m_ItemModelPanelPos[i].x, m_ItemModelPanelPos[i].y ); + + // Take into account parent's position + Panel* pParent = m_pItemModelPanels[i]->GetParent(); + if( pParent ) + { + int x = 0,y = 0; + pParent->GetPos( x, y ); + m_ItemModelPanelPos[i].x += x; + m_ItemModelPanelPos[i].y += y; + } + } + + // adjust page buttons + { + m_nNumActivePages = GetNumPages(); + + int iCenter = GetWide() * 0.5; + + int iPageBarWidth = 2 * abs( m_iItemBackpackOffcenterX ); + int iPageButtonWidth = ( iPageBarWidth - ( m_iPageButtonPerRow - 1 ) * m_iPageButtonXDelta ) / m_iPageButtonPerRow; + int iPageButtonWidthPlusDelta = iPageButtonWidth + m_iPageButtonXDelta; + int iPageButtonHeightPlusDelta = m_iPageButtonHeight + m_iPageButtonYDelta; + int iStart = iCenter + m_iItemBackpackOffcenterX; + + m_PageButtonPos.EnsureCount( m_Pages.Count() ); + for ( int i=0; i<m_Pages.Count(); ++i ) + { + EditablePanel *pPage = m_Pages[i]; + if ( pPage ) + { + // Apply control settings here + if ( m_pPageButtonKVs ) + pPage->ApplySettings( m_pPageButtonKVs ); + CExButton* pButton = dynamic_cast<CExButton*>( pPage->FindChildByName( "Button" ) ); + pPage->InvalidateLayout( true, true ); + // Make the button have the right command and send it's signals to us + if ( pButton ) + { + pButton->SetSelected( false ); + pButton->SetCommand( CFmtStr( "goto_page_%d", i ) ); + pButton->AddActionSignalTarget( this ); + } + pPage->SetDialogVariable( "page", i+1 ); + + bool bVisible = i < m_nNumActivePages; + if ( bVisible ) + { + int iRow = i /m_iPageButtonPerRow; + int iColumn = i % m_iPageButtonPerRow; + pPage->SetBounds( iStart + iColumn * iPageButtonWidthPlusDelta, m_iPageButtonYPos + iRow * iPageButtonHeightPlusDelta, iPageButtonWidth, m_iPageButtonHeight ); + pPage->GetPos( m_PageButtonPos[i].x , m_PageButtonPos[i].y ); + } + pPage->SetVisible( bVisible ); + } + } + + // Update colors and the "NEW!" labels + SetPageButtonTextColorBasedOnContents(); + } + + if ( m_pNextPageButton ) + { + m_pNextPageButton->SetVisible( true ); + } + if ( m_pPrevPageButton ) + { + m_pPrevPageButton->SetVisible( true ); + } + if ( m_pCurPageLabel ) + { + m_pCurPageLabel->SetVisible( true ); + } + + if ( m_pSortByComboBox ) + { + m_pSortByComboBox->SetVisible( !InToolSelectionMode() ); + } + if ( m_pShowRarityComboBox ) + { + m_pShowRarityComboBox->SetVisible( true ); + } + + if ( m_pNextPageButton ) + { + m_pNextPageButton->SetEnabled( GetNumPages() > 1 ); + } + if ( m_pPrevPageButton ) + { + m_pPrevPageButton->SetEnabled( GetNumPages() > 1 ); + } + + if ( !m_bDragging ) + { + if ( m_pDragToNextPageButton && m_pDragToPrevPageButton ) + { + m_pDragToNextPageButton->SetVisible( false ); + m_pDragToPrevPageButton->SetVisible( false ); + } + } + + bool bShowActions = (!m_bItemsOnly && !InToolSelectionMode()); + if ( m_pCraftButton ) + { + m_pCraftButton->SetVisible( bShowActions ); + } + if ( m_pCancelToolButton ) + { + m_pCancelToolButton->SetVisible( InToolSelectionMode() ); + } + + if ( m_pShowExplanationsButton ) + { + m_pShowExplanationsButton->SetVisible( !m_bItemsOnly ); + } + if ( m_pShowBaseItemsCheckbox ) + { + m_pShowBaseItemsCheckbox->SetVisible( !m_bItemsOnly ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::FireGameEvent( IGameEvent *event ) +{ + static CSchemaItemDefHandle pItemDef_BasePaintCan( "Paint Can" ); + const char *type = event->GetName(); + if ( Q_strcmp( "gc_connected", type ) == 0 ) + { + if ( !m_bInitializedSeenItems ) + { + CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory(); + if ( pInventory ) + { + for ( int i = 0; i < pInventory->GetItemCount(); i++ ) + { + CEconItemView *pItem = pInventory->GetItem(i); + m_mapSeenItems.Insert( pItem->GetItemID() ); + } + } + + m_bInitializedSeenItems = true; + } + + m_vecPaintCans.Purge(); + m_vecStrangeParts.Purge(); + const CEconItemSchema::ToolsItemDefinitionMap_t &toolDefs = GetItemSchema()->GetToolsItemDefinitionMap(); + + // Store all of the active paint can item defs + FOR_EACH_MAP_FAST( toolDefs, i ) + { + const CEconItemDefinition *pItemDef = toolDefs[i]; + const IEconTool *pEconTool = pItemDef->GetEconTool(); + if ( !pEconTool ) + continue; + + // Paint can list + // Ignore the stock paintcan thats only for armory purposes + if ( !V_strcmp( pEconTool->GetTypeName(), "paint_can" ) && pItemDef_BasePaintCan != pItemDef ) + { + // Paint Can + m_vecPaintCans.AddToTail( pItemDef->GetDefinitionIndex() ); + } + // Strange Parts List + else if ( !V_strcmp( pEconTool->GetTypeName(), "strange_part" ) ) + { + m_vecStrangeParts.AddToTail( pItemDef->GetDefinitionIndex() ); + } + } + } + + BaseClass::FireGameEvent( event ); +} + +void CBackpackPanel::CheckForQuickOpenKey() +{ + if ( !m_hQuickOpenCrate ) + return; + + // We only want to continue if it's the transaction we're listening for + if ( EconUI()->GetStorePanel()->GetMostRecentSuccessfulTransactionID() != m_nQuickOpenTxn ) + { + return; + } + + CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory(); + if ( pInventory ) + { + for ( int i = 0; i < pInventory->GetItemCount(); i++ ) + { + CEconItemView *pInvItem = pInventory->GetItem( i ); + + uint32 iPosition = pInvItem->GetInventoryPosition(); + if ( IsUnacknowledged( iPosition ) == false ) + continue; + + if ( InventoryManager()->GetBackpackPositionFromBackend( iPosition ) != 0 ) + continue; + + // Now make sure we haven't got a clientside saved ack for this item. + if ( InventoryManager()->HasBeenAckedByClient( pInvItem ) ) + continue; + + // If item is not a drop we want to show the notification otherwise they'll get the notification on death + int iFoundMethod = GetUnacknowledgedReason( iPosition ); + if ( iFoundMethod != UNACK_ITEM_PURCHASED ) + continue; + + if ( !pInvItem->GetStaticData()->IsTool() ) + continue; + + if( !CEconSharedToolSupport::ToolCanApplyTo( pInvItem, m_hQuickOpenCrate ) ) + continue; + + if ( !pInvItem->GetStaticData()->GetEconTool() ) + continue; + + if ( !Q_strcmp( pInvItem->GetStaticData()->GetEconTool()->GetTypeName(), "decoder_ring" ) == 0 ) + continue; + + ApplyTool( this, pInvItem, m_hQuickOpenCrate ); + CloseStoreStatusDialog(); + + m_hQuickOpenCrate = NULL; + m_nQuickOpenTxn = 0; + return; + } + } + + m_hQuickOpenCrate = NULL; + m_nQuickOpenTxn = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: When the store get's a new transaction ID, it comes here as well +//----------------------------------------------------------------------------- +void CBackpackPanel::SetCurrentTransactionID( uint64 nTxnID ) +{ + // If we've got a quick open crate st, and no quick open transaction ID, + // then we want to capture the incoming transaction ID so that we can + // compare future incoming successful transactions to see if they have + // the key we're expecting + if ( m_hQuickOpenCrate && m_nQuickOpenTxn == 0 ) + { + m_nQuickOpenTxn = nTxnID; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory ) +{ + if ( bVisible ) + { + m_pMouseDragItemPanel->SetVisible( false ); + + if( m_pDynamicRecipePanel ) + { + m_pDynamicRecipePanel->SetVisible( false ); + } + + if ( m_pItemSlotPanel ) + { + m_pItemSlotPanel->SetVisible( false ); + } + + m_bShowBaseItems = false; + if ( m_pShowBaseItemsCheckbox ) + { + m_pShowBaseItemsCheckbox->SetSelected( m_bShowBaseItems ); + } + + if ( !bReturningFromArmory ) + { + SetCurrentPage( 0 ); + CancelToolSelection(); + } + + m_nNumActivePages = 0; + +#ifdef STAGING_ONLY + // Reset pinned-state of the card + m_pMouseOverCardPanel->PinCard( false ); +#endif + } + else + { + if ( m_bDragging ) + { + StopDrag( false ); + } + } + + if ( m_pInspectPanel ) + { + m_pInspectPanel->SetVisible( false ); + } + + if ( m_pInspectCosmeticPanel ) + { + m_pInspectCosmeticPanel->SetVisible( false ); + } + + if ( m_pCollectionCraftPanel ) + { + m_pCollectionCraftPanel->SetVisible( false ); + } + + if ( m_pHalloweenOfferingPanel ) + { + m_pHalloweenOfferingPanel->SetVisible( false ); + } + + if ( m_pMannCoTradePanel ) + { + m_pMannCoTradePanel->SetVisible( false ); + } + + if ( m_pStrangeToolPanel ) + { + m_pStrangeToolPanel->MarkForDeletion(); + m_pStrangeToolPanel = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::PostShowPanel( bool bVisible ) +{ + if ( bVisible ) + { + DeSelectAllBackpackItemPanels(); + + RequestFocus(); + + // Clear out text field + ClearNameFilter( true ); + } + + // If this is the first time we've opened the loadout, start the loadout explanations + ConVar *pConVar = GetExplanationConVar(); + if ( bVisible && pConVar && !pConVar->GetBool() && ShouldShowExplanations() ) + { + m_flStartExplanationsAt = Plat_FloatTime() + 0.5; + vgui::ivgui()->AddTickSignal( GetVPanel() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBackpackPanel::GetNumPages( void ) +{ + int iMaxItems = InventoryManager()->GetLocalInventory()->GetMaxItemCount(); + return (int)(ceil((float)iMaxItems / (float)BACKPACK_SLOTS_PER_PAGE)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::AssignItemToPanel( CItemModelPanel *pPanel, int iIndex ) +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + static int iItemBackpackPos = 0; + if ( iIndex == 0 ) + { + iItemBackpackPos = 0; + } + int iPanelBackpackPos = GetBackpackPosForPanelIndex(iIndex); + + static int iLastMapItem = -1; + + pPanel->SetShowQuantity( true ); + + const wchar_t* wszFilter = GetNameFilter(); + bool bInToolSelection = InToolSelectionMode() && m_ToolSelectionItem.IsValid(); + + CEconItemView *pItemData = NULL; + CEconItemView tempItem; + if ( m_bShowBaseItems ) + { + const CEconItemDefinition* pItemDef = NULL; + + const CEconItemSchema::BaseItemDefinitionMap_t& mapItems = GetItemSchema()->GetBaseItemDefinitionMap(); + int iStart = iIndex == 0 ? mapItems.FirstInorder() : mapItems.NextInorder( iLastMapItem ); + for ( int it = iStart; it != mapItems.InvalidIndex(); it = mapItems.NextInorder( it ) ) + { + iLastMapItem = it; + + if ( mapItems[it]->IsBaseItem() && !mapItems[it]->IsHidden() ) + { + // Instead of linking to this base item definition, link to the definition of what it will become + // when we customize it. + CFmtStr fmtStrCustomizedDefName( "Upgradeable %s", mapItems[it]->GetDefinitionName() ); + pItemDef = GetItemSchema()->GetItemDefinitionByName( fmtStrCustomizedDefName.Access() ); + + // If we don't have an upgradeable version, we assume that we can't upgrade it and link to the base + // definition instead. We expect this to only happen if the item won't actually be useable for whatever + // purpose (name tags, etc.). We sanity-check this on the GC. + if ( !pItemDef ) + { + pItemDef = mapItems[it]; + } + + tempItem.Init( pItemDef->GetDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); + + // skip this item if the tool cannot be applied to it + if ( bInToolSelection && !CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, &tempItem ) ) + { + pItemDef = NULL; + continue; + } + + if ( DoesItemPassSearchFilter( tempItem.GetDescription(), wszFilter ) ) + { + break; + } + } + + pItemDef = NULL; + } + + if ( pItemDef ) + { + pItemData = &tempItem; + + ++iItemBackpackPos; + } + } + else if ( HasNameFilter() ) + { + int iStart = iIndex == 0 ? m_mapFilteringItems.FirstInorder() : m_mapFilteringItems.NextInorder( iLastMapItem ); + for ( int it = iStart; it != m_mapFilteringItems.InvalidIndex(); it = m_mapFilteringItems.NextInorder( it ) ) + { + iLastMapItem = it; + + CEconItemView *pItem = m_mapFilteringItems[it]; + + // skip this item if the tool cannot be applied to it + if ( bInToolSelection && !CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, pItem ) ) + { + continue; + } + + if ( !DoesItemPassSearchFilter( pItem->GetDescription(), wszFilter ) ) + { + continue; + } + + if ( ++iItemBackpackPos != iPanelBackpackPos ) + { + continue; + } + + pItemData = pItem; + break; + } + } + else if ( bInToolSelection ) + { + CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory(); + if ( pInventory ) + { + // Backpack positions start from 1 + Assert( iPanelBackpackPos > 0 && iPanelBackpackPos <= pInventory->GetMaxItemCount() ); + int iStart = iIndex == 0 ? 0 : iLastMapItem + 1; + for ( int i = iStart; i < pInventory->GetItemCount(); i++ ) + { + iLastMapItem = i; + + CEconItemView *pItem = pInventory->GetItem(i); + + if ( m_ToolSelectionItem.GetStaticData()->IsTool() ) + { + if ( !CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, pItem ) ) + { + continue; + } + } + else + { + if ( !pItem->GetStaticData()->IsTool() ) + { + continue; + } + + if ( !CEconSharedToolSupport::ToolCanApplyTo( pItem, &m_ToolSelectionItem ) ) + { + continue; + } + + if ( ( m_ToolSelectionItem.GetStaticData()->GetCapabilities() & ITEM_CAP_DECODABLE ) && pItem->GetStaticData()->GetEconTool() && ( Q_strcmp( pItem->GetStaticData()->GetEconTool()->GetTypeName(), "decoder_ring" ) != 0 ) ) + { + continue; + } + } + + if ( ++iItemBackpackPos != iPanelBackpackPos ) + { + continue; + } + + pItemData = pItem; + break; + } + } + } + else + { + pItemData = InventoryManager()->GetItemByBackpackPosition( iPanelBackpackPos ); + iItemBackpackPos = iPanelBackpackPos; + + if ( pItemData == NULL && pPanel->GetItem() == NULL ) + { + return; + } + + int nDirtyIndex = pItemData ? m_vecDirtyItems.Find( pItemData->GetItemID() ) : m_vecDirtyItems.InvalidIndex(); + + if ( pItemData // Want to put in an item + && pPanel->GetItem() // Panel has an item + && pItemData->GetItemID() == pPanel->GetItem()->GetItemID() // That panel has the same item that we want to put in + && nDirtyIndex == m_vecDirtyItems.InvalidIndex() ) // And that item is not dirtied. + { + // We dont do anything + return; + } + + if ( nDirtyIndex != m_vecDirtyItems.InvalidIndex() ) + { + m_vecDirtyItems.Remove( nDirtyIndex ); + } + } + + if ( iItemBackpackPos != iPanelBackpackPos ) + { + pItemData = NULL; + } + + pPanel->SetItem( pItemData ); + + bool bSeen = true; + // Have we not seen this item before? + if ( !m_bShowBaseItems && pItemData && m_mapSeenItems.Find( pItemData->GetItemID() ) == m_mapSeenItems.InvalidIndex() ) + { + bSeen = false; + } + + // Show our "NEW!" label if this item hasnt been seen + CExLabel *pNewPanel = dynamic_cast< CExLabel* >( pPanel->FindChildByName( "New" ) ); + if ( pNewPanel ) + { + pNewPanel->SetVisible( !bSeen ); + } + + pPanel->DirtyDescription(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::ClearNameFilter( bool bUpdateModelPanels ) +{ + if ( m_wNameFilter.Count() == 0 ) + return; + + m_wNameFilter.RemoveAll(); + if( m_pNameFilterTextEntry ) + { + m_pNameFilterTextEntry->SetText( "" ); + } + + if ( bUpdateModelPanels ) + { + m_flFilterItemTime = gpGlobals->curtime + 0.1f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::UpdateFilteringItems() +{ + m_mapFilteringItems.RemoveAll(); + + if ( !HasNameFilter() ) + return; + + CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory(); + if ( !pInventory ) + return; + + for ( int i = 0; i < pInventory->GetItemCount(); i++ ) + { + CEconItemView *pItem = pInventory->GetItem(i); + + if ( pItem->GetItemDefinition()->IsHidden() ) + continue; + + int iBackpackPosition = InventoryManager()->GetBackpackPositionFromBackend( pItem->GetInventoryPosition() ); + m_mapFilteringItems.Insert( iBackpackPosition, pItem ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::UpdateModelPanels( void ) +{ + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + UpdateFilteringItems(); + + // We're showing the backpack. Show all the items in our inventory + FOR_EACH_VEC( m_pItemModelPanels, i ) + { + m_pItemModelPanels[i]->SetShowEquipped( true ); + m_pItemModelPanels[i]->SetShowGreyedOutTooltip( true ); + AssignItemToPanel( m_pItemModelPanels[i], i ); + + if ( !m_pItemModelPanels[i]->HasItem() && m_pItemModelPanels[i]->IsSelected() ) + { + m_pItemModelPanels[i]->SetSelected( false ); + } + + SetBorderForItem( m_pItemModelPanels[i], false ); + } + + // Clean out. We just did all the heavy lifting. + m_vecDirtyItems.Purge(); + + if ( InToolSelectionMode() && m_ToolSelectionItem.IsValid() ) + { + wchar_t wTemp[256]; + g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find( "BackpackApplyTool" ), 1, m_ToolSelectionItem.GetItemName() ); + SetDialogVariable( "loadoutclass", wTemp ); + } + else + { + SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( "BackpackTitle" ) ); + } + + char szTmp[16]; + V_sprintf_safe( szTmp, "%d/%d", GetCurrentPage()+1, GetNumPages() ); + SetDialogVariable( "backpackpage", szTmp ); + + // Now layout again to position our item buttons + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Mark visited item model panels as seen +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemPanelEntered( vgui::Panel *panel ) +{ + if ( m_pContextMenu && m_pContextMenu->IsVisible() ) + return; + + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + if ( pItemPanel ) + { + // Hide the "NEW!" label + CExLabel *pNewPanel = dynamic_cast< CExLabel* >( pItemPanel->FindChildByName( "New" ) ); + if ( pNewPanel ) + { + pNewPanel->SetVisible( false ); + } + + // Mark this item as "seen" + CEconItemView *pItem = pItemPanel->GetItem(); + if ( pItem ) + { + if ( m_mapSeenItems.Find( pItem->GetItemID() ) == m_mapSeenItems.InvalidIndex() ) + { + m_mapSeenItems.Insert( pItem->GetItemID() ); + SetPageButtonTextColorBasedOnContents(); + } + } + } + + BaseClass::OnItemPanelEntered( panel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemPanelMousePressed( vgui::Panel *panel ) +{ + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + + if ( pItemPanel && IsVisible() && pItemPanel->IsGreyedOut() == false && AllowDragging( pItemPanel ) ) + { + m_flMouseDownTime = gpGlobals->curtime; + m_iMouseDownX = m_iMouseDownY = 0; + m_pItemDraggedFromPanel = pItemPanel; + m_iDraggedFromPage = GetCurrentPage(); + m_bDragging = false; + m_bMouseDownOnItemPanel = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle the escape key since it doesn't show up in OnKeyCodePressed +//----------------------------------------------------------------------------- +void CBackpackPanel::OnKeyCodeTyped(vgui::KeyCode code) +{ + if ( code == KEY_ESCAPE && InToolSelectionMode() ) + { + CancelToolSelection(); + } + else if ( code == KEY_ENTER ) + { + // Do nothing. This gets hit frequently when people type in the filter + // text entry and then hit 'Enter', expecting it to execute the filter. + // We automatically apply it, so let's just eat 'Enter', which was causing + // us to activate some button on the main menu. + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles key press events in the backpack +//----------------------------------------------------------------------------- +void CBackpackPanel::OnKeyCodePressed( vgui::KeyCode code ) +{ + // Ignore key events while the confirm delete dialog is up + if( m_pConfirmDeleteDialog ) + return; + + // let our parent class handle all the arrow key/dpad stuff + if( HandleItemSelectionKeyPressed( code ) ) + { + return; + } + + // Handle close here, CBasePanel parent doesn't support "DialogClosing" command + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + if ( (nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B) && InToolSelectionMode() ) + { + CancelToolSelection(); + } + else if( code == KEY_PAGEDOWN ) + { + OnCommand( "nextpage" ); + } + else if( code == KEY_PAGEUP ) + { + OnCommand( "prevpage" ); + } + else if ( ( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A ) ) + { + if( InToolSelectionMode() ) + { + HandleToolItemSelection( GetFirstSelectedItem() ); + } + else + { + OpenContextMenu(); + } + } + else if ( nButtonCode == KEY_XBUTTON_X || nButtonCode == STEAMCONTROLLER_X ) + { + if( !InToolSelectionMode() ) + { + OnCommand( "deleteitem" ); + } + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles key press events in the backpack +//----------------------------------------------------------------------------- +void CBackpackPanel::OnKeyCodeReleased( vgui::KeyCode code ) +{ + if( ! HandleItemSelectionKeyReleased( code ) ) + BaseClass::OnKeyCodeReleased( code ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnMouseCaptureLost( void ) +{ + if ( m_bDragging ) + { + StopDrag( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnMouseReleased(vgui::MouseCode code) +{ + if ( code == MOUSE_LEFT ) + { + if ( m_bDragging ) + { + // When we're dragging, we have mouse capture, so the item panels aren't getting mouse input. + // We need to find out what item panel we're over, and let it know. + if ( m_pPrevDragOverItemPanel ) + { + OnItemPanelMouseReleased( m_pPrevDragOverItemPanel ); + } + else + { + StopDrag( false ); + } + } + } + + BaseClass::OnMouseReleased( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnConfirmDelete( KeyValues *data ) +{ + // Delete all the selected item + if ( data ) + { + int iConfirmed = data->GetInt( "confirmed", 0 ); + if ( iConfirmed ) + { + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + EconUI()->Gamestats_ItemTransaction( IE_ITEM_DELETED, m_pItemModelPanels[i]->GetItem() ); + InventoryManager()->DropItem( m_pItemModelPanels[i]->GetItem()->GetItemID() ); + } + } + DeSelectAllBackpackItemPanels(); + } + } + + m_pConfirmDeleteDialog = NULL; + + // If we're embedded in the discard item panel, it needs to know we made room. Send a message to our parent that it can catch. + PostMessage( GetParent(), new KeyValues("ConfirmDlgResult", "confirmed", 2 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemPanelMouseReleased( vgui::Panel *panel ) +{ + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + + if ( pItemPanel && IsVisible() ) + { + if ( InToolSelectionMode() ) + { + // They're selecting the item they'd like to apply a tool to + HandleToolItemSelection( pItemPanel->GetItem() ); + } + else if ( !m_bDragging ) + { + // If they're not holding down ctrl, deselect all existing selections + if ( !vgui::input()->IsKeyDown(KEY_LCONTROL) && !vgui::input()->IsKeyDown(KEY_RCONTROL) ) + { + DeSelectAllBackpackItemPanels(); + } + + // Quick clicks just select the item + ToggleSelectBackpackItemPanel( pItemPanel ); + + if ( pItemPanel->IsSelected() ) + { + OpenContextMenu(); + } + } + else + { + int iPanelIndex = GetBackpackPositionForPanel( pItemPanel ); + if ( !CanDragTo(pItemPanel, iPanelIndex) ) + { + StopDrag(false); + } + else + { + StopDrag( true ); + if ( (pItemPanel != m_pItemDraggedFromPanel || m_iDraggedFromPage != GetCurrentPage() ) && m_pMouseDragItemPanel->HasItem() ) + { + HandleDragTo( pItemPanel, iPanelIndex ); + } + else if ( m_iDraggedFromPage == GetCurrentPage() ) + { + m_pItemDraggedFromPanel->SetItem( m_pMouseDragItemPanel->GetItem() ); + } + } + } + + m_pItemDraggedFromPanel = NULL; + } +} + + +bool GetDecodedByItemDefIndex( const CEconItemView *pItem, uint32 *pDecodedBy = NULL ) +{ + static CSchemaAttributeDefHandle pAttrDef_DecodedBy( "decoded by itemdefindex" ); + + if ( pDecodedBy ) + { + return pItem->FindAttribute( pAttrDef_DecodedBy, pDecodedBy ); + } + else + { + return pItem->FindAttribute( pAttrDef_DecodedBy ); + } +} + +CEconItemView* GetFirstCompatibleKeyForCrate( const CEconItemView *pItem ) +{ + // Check if we have any decoder rings that can be applied onto this + CPlayerInventory *pInv = InventoryManager()->GetLocalInventory(); + Assert( pInv ); + if ( pInv ) + { + for ( int i = 0; i < pInv->GetItemCount(); ++i ) + { + CEconItemView *pInvItem = pInv->GetItem( i ); + + if ( pInvItem->GetQuality() == AE_SELFMADE ) + continue; + + if ( pInvItem->GetStaticData()->IsTool() && CEconSharedToolSupport::ToolCanApplyTo( pInvItem, pItem ) && pInvItem->GetStaticData()->GetEconTool() && ( Q_strcmp( pInvItem->GetStaticData()->GetEconTool()->GetTypeName(), "decoder_ring" ) == 0 ) ) + { + return pInvItem; + } + } + } + + return NULL; +} + +bool CanInventoryItemsApplyTo( const CEconItemView *pItem ) +{ + // Check if we have any tools that can be applied onto this + CPlayerInventory *pInv = InventoryManager()->GetLocalInventory(); + Assert( pInv ); + if ( pInv ) + { + for ( int i = 0 ; i < pInv->GetItemCount() ; ++i ) + { + CEconItemView *pInvItem = pInv->GetItem( i ); + if ( pInvItem->GetStaticData()->IsTool() && CEconSharedToolSupport::ToolCanApplyTo( pInvItem, pItem ) ) + { + return true; + } + } + } + + return false; +} +//----------------------------------------------------------------------------- +bool CreateMarketPriceString( item_definition_index_t iDefIndex, wchar_t *pszString, int iBufferSize ) +{ + // Get Market Price + steam_market_gc_identifier_t ident; + ident.m_unDefIndex = iDefIndex; + ident.m_unQuality = AE_UNIQUE; // Get this from default item def? + + const client_market_data_t *pClientMarketData = GetClientMarketData( ident ); + if ( !pClientMarketData ) + return false; + + const ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); + + // Set that price into the button + wchar_t pszCurrencyString[kLocalizedPriceSizeInChararacters]; + MakeMoneyString( pszCurrencyString, ARRAYSIZE( pszCurrencyString ), pClientMarketData->m_unLowestPrice, eCurrency ); + + wchar_t pszConstructed[kLocalizedPriceSizeInChararacters]; + g_pVGuiLocalize->ConstructString_safe( pszConstructed, g_pVGuiLocalize->Find( "#TF_MarketPrice" ), 1, pszCurrencyString ); + + // copy result; + V_wcsncpy( pszString, pszConstructed, iBufferSize ); + return true; +} +//----------------------------------------------------------------------------- +bool CreateStorePriceString( item_definition_index_t iDefIndex, wchar_t *pszString, int iBufferSize ) +{ + // Get Market Price + steam_market_gc_identifier_t ident; + ident.m_unDefIndex = iDefIndex; + ident.m_unQuality = AE_UNIQUE; // Get this from default item def? + + // Get the price of the item + const econ_store_entry_t *pEntry = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( iDefIndex ); + if ( !pEntry ) + return false; + + const ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); + + // Set that price into the button + wchar_t pszCurrencyString[kLocalizedPriceSizeInChararacters]; + MakeMoneyString( pszCurrencyString, ARRAYSIZE( pszCurrencyString ), pEntry->GetCurrentPrice( eCurrency ), eCurrency ); + + wchar_t pszConstructed[kLocalizedPriceSizeInChararacters]; + g_pVGuiLocalize->ConstructString_safe( pszConstructed, g_pVGuiLocalize->Find( "#TF_StorePrice" ), 1, pszCurrencyString ); + + // copy result; + V_wcsncpy( pszString, pszConstructed, iBufferSize ); + return true; +} +//----------------------------------------------------------------------------- +void CBackpackPanel::AddCommerceSubmenus( Menu *pSubMenu, item_definition_index_t iItemDef, const char* pszActionFmt ) +{ + wchar_t wPriceListing[256]; + // Store + if ( CreateStorePriceString( iItemDef, wPriceListing, sizeof( wPriceListing ) ) ) + { + int nIndex = pSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "%s%s%d", "store_", pszActionFmt, iItemDef ) ), this ); + vgui::MenuItem *pMenuItem = pSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( wPriceListing ); + pMenuItem->InvalidateLayout( true, false ); + } + + // Market + if ( CreateMarketPriceString( iItemDef, wPriceListing, sizeof( wPriceListing ) ) ) + { + int nIndex = pSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "%s%s%d", "market_", pszActionFmt, iItemDef ) ), this ); + vgui::MenuItem *pMenuItem = pSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( wPriceListing ); + pMenuItem->InvalidateLayout( true, false ); + } + else + { + int nIndex = pSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "%s%s%d", "market_", pszActionFmt, iItemDef ) ), this ); + vgui::MenuItem *pMenuItem = pSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( "#TF_MarketUnavailable" ) ); + pMenuItem->InvalidateLayout( true, false ); + } + +} +//----------------------------------------------------------------------------- +void CBackpackPanel::AddPaintToContextMenu( Menu *pPaintSubMenu, item_definition_index_t iPaintDef, bool bAddCommerce ) +{ + GameItemDefinition_t * pPaintCanDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinition( iPaintDef ) ); + if ( !pPaintCanDef ) + return; + + wchar_t wBuff[256]; + char cBuff[256]; + V_swprintf_safe( wBuff, L" %ls", g_pVGuiLocalize->Find( pPaintCanDef->GetItemBaseName() ) ); + + char szItemName[256]; + g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find( pPaintCanDef->GetItemBaseName() ), szItemName, sizeof( szItemName ) ); + V_sprintf_safe( cBuff, " %s", szItemName ); + + uint32 unPaintRGB0 = 0; + uint32 unPaintRGB1 = 0; + + static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" ); + static CSchemaAttributeDefHandle pAttrDef_PaintRGB2( "set item tint RGB 2" ); + + float fRGB = 0.0f; + + if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pPaintCanDef, pAttrDef_PaintRGB, &fRGB ) && fRGB != 0.0f ) + { + unPaintRGB0 = fRGB; + + // We may or may not have a secondary paint color as well. If we don't, we just use the primary + // paint color to fill both slots. + if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pPaintCanDef, pAttrDef_PaintRGB2, &fRGB ) ) + { + unPaintRGB1 = fRGB; + } + else + { + unPaintRGB1 = unPaintRGB0; + } + } + + if ( !bAddCommerce ) + { + int nIndex = pPaintSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "paint%d", iPaintDef ) ), this ); + vgui::MenuItem *pMenuItem = pPaintSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( wBuff ); + pMenuItem->InvalidateLayout( true, false ); + + CItemMaterialCustomizationIconPanel *pCustomPanel = new CItemMaterialCustomizationIconPanel( pMenuItem, "paint" ); + pCustomPanel->SetZPos( -100 ); + pCustomPanel->SetTall( 30 ); + pCustomPanel->SetWide( 30 ); + pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( ( unPaintRGB0 & 0xFF0000 ) >> 16, 0, 255 ), clamp( ( unPaintRGB0 & 0xFF00 ) >> 8, 0, 255 ), clamp( ( unPaintRGB0 & 0xFF ), 0, 255 ), 255 ) ); + pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( ( unPaintRGB1 & 0xFF0000 ) >> 16, 0, 255 ), clamp( ( unPaintRGB1 & 0xFF00 ) >> 8, 0, 255 ), clamp( ( unPaintRGB1 & 0xFF ), 0, 255 ), 255 ) ); + } + else + { + // + const char *pszContextMenuBorder = "NotificationDefault"; + const char *pszContextMenuFont = "HudFontMediumSecondary"; + + Menu *pSubMenu = new Menu( this, "PaintSubMenu" ); + pSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + int iPos = pPaintSubMenu->AddCascadingMenuItem( cBuff, this, pSubMenu ); + + CItemMaterialCustomizationIconPanel *pCustomPanel = new CItemMaterialCustomizationIconPanel( pPaintSubMenu, "paint" ); + pCustomPanel->SetZPos( 100 ); + pCustomPanel->SetPos( 0, iPos * pPaintSubMenu->GetMenuItemHeight() ); + pCustomPanel->SetTall( 30 ); + pCustomPanel->SetWide( 30 ); + pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( ( unPaintRGB0 & 0xFF0000 ) >> 16, 0, 255 ), clamp( ( unPaintRGB0 & 0xFF00 ) >> 8, 0, 255 ), clamp( ( unPaintRGB0 & 0xFF ), 0, 255 ), 255 ) ); + pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( ( unPaintRGB1 & 0xFF0000 ) >> 16, 0, 255 ), clamp( ( unPaintRGB1 & 0xFF00 ) >> 8, 0, 255 ), clamp( ( unPaintRGB1 & 0xFF ), 0, 255 ), 255 ) ); + + AddCommerceSubmenus( pSubMenu, iPaintDef, "paint" ); + } +} +// +// Add commerce context options for an item. Adds 'Store' and 'Market' options if appropriate (and Pricing) other wise just click to use +// +void CBackpackPanel::AddCommerceToContextMenu( Menu *pMenu, const char* pszActionFmt, item_definition_index_t iItemDefIndex, bool bAddMarket, bool bAddStore ) +{ + GameItemDefinition_t * pItemDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinition( iItemDefIndex ) ); + if ( !pItemDef ) + return; + + // + if ( !bAddMarket && !bAddStore ) + { + int nIndex = pMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "%s%d", pszActionFmt, iItemDefIndex ) ), this ); + vgui::MenuItem *pMenuItem = pMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) ); + pMenuItem->InvalidateLayout( true, false ); + } + else + { + // + const char *pszContextMenuBorder = "NotificationDefault"; + const char *pszContextMenuFont = "HudFontMediumSecondary"; + + Menu *pSubMenu = new Menu( this, "CommerceSubMenu" ); + pSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + pMenu->AddCascadingMenuItem( pItemDef->GetItemBaseName(), this, pSubMenu ); + + AddCommerceSubmenus( pSubMenu, iItemDefIndex, pszActionFmt ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Opens a context menu with actions relevant for the passed in item +//----------------------------------------------------------------------------- +void CBackpackPanel::OpenContextMenu() +{ + CUtlVector<CEconItemView*> vecSelectedItems; + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->GetItem() ) + { + vecSelectedItems.AddToTail( m_pItemModelPanels[i]->GetItem() ); + } + } + + if ( m_pContextMenu ) + delete m_pContextMenu; + + m_pContextMenu = new Menu( this, "ContextMenu" ); + MenuBuilder contextMenuBuilder( m_pContextMenu, this ); + const char *pszContextMenuBorder = "NotificationDefault"; + const char *pszContextMenuFont = "HudFontMediumSecondary"; + m_pContextMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + m_pContextMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + + if ( vecSelectedItems.Count() == 1 ) + { + const CEconItemView *pItem = vecSelectedItems.Head(); + const CTFItemDefinition *pItemDef = pItem->GetStaticData(); + static CSchemaItemDefHandle DuckBadgeItemDef( "Duck Badge" ); + static CSchemaItemDefHandle StrangeCountTransferItemDef( "Strange Count Transfer Tool" ); + + // Tools of any kind can't be used if they are in escrow. + static CSchemaAttributeDefHandle pAttrib_ToolEscrowUntil( "tool escrow until date" ); + uint32 unEscrowTime; + const bool bToolIsInEscrow = pItem->FindAttribute( pAttrib_ToolEscrowUntil, &unEscrowTime ) + && unEscrowTime > CRTime::RTime32TimeCur(); + + const IEconTool *pEconTool = pItem->GetItemDefinition()->GetEconTool(); + + const bool bIsTool = pItem->GetStaticData()->IsTool() && (pEconTool != NULL); + const bool bIsGCConsumable = ( ( pItem->GetStaticData()->GetCapabilities() & ITEM_CAP_USABLE_GC ) != 0 ); + bool bSkipAddTrade = false; // Hack: We should really ask the tool if the command supplants trade. + + // Tool usage goes first. The cursor starts on this element, so double-clicks will work like how they used to. + // Strange Count Transfer + if ( StrangeCountTransferItemDef == pItem->GetItemDefinition() ) + { + contextMenuBuilder.AddMenuItem( "#ApplyOnItem", new KeyValues( "Context_OpenStrangeCountTransfer" ), "primaryaction" ); + } + else if ( pItem->GetStaticData()->IsTool() && pEconTool == NULL ) + { + // do nothing. not a real tool (basic balloons with color that we don't want to 'remove' the paint) + } + else if ( (bIsTool || bIsGCConsumable) && !bToolIsInEscrow && pEconTool->CanBeUsedNow( pItem ) ) + { + Assert( pEconTool ); + + const int nTokens = pEconTool->GetUseCommandCount( pItem ); + for ( int i = 0; i < nTokens; ++i ) + { + const char *pszToolUsageString = pEconTool->GetUseCommandLocalizationToken( pItem, i ); + + // If we didn't have a custom usage string, fall back to a sane default based on whether or + // not we're a consumable or not. + if ( !pszToolUsageString ) + { + pszToolUsageString = bIsGCConsumable ? "#ConsumeItem" : "#ApplyOnItem"; + } + + const char *pszContext = pEconTool->GetUseCommand( pItem, i ); + contextMenuBuilder.AddMenuItem( pszToolUsageString, new KeyValues( pszContext ), "primaryaction" ); + } + + // Hack: We should really ask the tool if the command supplants trade. For now, if we have two + // things, then one of them is trade, so skip it. + bSkipAddTrade = nTokens > 1; + } + else if ( pItem->GetItemDefinition()->GetCapabilities() & ITEM_CAP_DECODABLE ) + { + + static CSchemaAttributeDefHandle pAttrDef_CanShuffleCrateContents( "can shuffle crate contents" ); + + if ( pItem->FindAttribute( pAttrDef_CanShuffleCrateContents ) ) + { + contextMenuBuilder.AddMenuItem( "#ShuffleContents", new KeyValues( "Context_Shuffle" ), "primaryaction" ); + } + + if ( GetFirstCompatibleKeyForCrate( pItem ) != NULL ) + { + contextMenuBuilder.AddMenuItem( "#UseKey", new KeyValues( "Context_OpenCrateWithKey" ), "primaryaction" ); + } + + if ( GetDecodedByItemDefIndex( pItem ) ) + { + contextMenuBuilder.AddMenuItem( "#GetKey", new KeyValues( "Context_GetItemFromStore" ), "primaryaction" ); + contextMenuBuilder.AddMenuItem( "#BuyAndUseKey", new KeyValues( "Context_BuyKeyAndOpenCrate" ), "primaryaction" ); + } + } + else if ( pItem->GetItemDefinition()->GetCapabilities() & ITEM_CAP_HAS_SLOTS ) + { + // check if we have at least 1 slot criteria + static CSchemaAttributeDefHandle pAttrDef_Slot( "item slot criteria 1" ); + if ( pItem->FindAttribute( pAttrDef_Slot ) ) + { + contextMenuBuilder.AddMenuItem( "#EditSlots", new KeyValues( "Context_EditSlot" ), "primaryaction" ); + } + } + else if ( DuckBadgeItemDef == pItem->GetItemDefinition() ) + { + contextMenuBuilder.AddMenuItem( "#Duck_ViewLeaderboards", new KeyValues( "Context_OpenDuckLeaderboards" ), "primaryaction" ); + + if ( CanInventoryItemsApplyTo( pItem ) ) + { + contextMenuBuilder.AddMenuItem( "#UseDuckToken", new KeyValues( "Context_ApplyByItem" ), "primaryaction" ); + } + + if ( GetDecodedByItemDefIndex( pItem ) ) + { + contextMenuBuilder.AddMenuItem( "#GetDuckToken", new KeyValues( "Context_GetItemFromStore" ), "primaryaction" ); + } + } + + // 3D Inspect + float flInspect = 0; + static CSchemaAttributeDefHandle pAttrib_WeaponAllowInspect( "weapon_allow_inspect" ); + static CSchemaAttributeDefHandle pAttrib_CosmeticAllowInspect( "cosmetic_allow_inspect" ); + if ( pItem && pItem->IsValid() && + ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttrib_WeaponAllowInspect, &flInspect ) || FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttrib_CosmeticAllowInspect, &flInspect ) +#ifdef STAGING_ONLY + || tf_weapon_force_allow_inspect.GetBool() +#endif + ) ) + { + if ( flInspect != 0 +#ifdef STAGING_ONLY + || tf_weapon_force_allow_inspect.GetBool() +#endif + ) + { + contextMenuBuilder.AddMenuItem( "#Context_InspectModel", new KeyValues( "Context_InspectModel" ), "primaryaction" ); + } + } + + // Add equip sub menu + { + Menu *pEquipSubMenu = NULL; + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) + { + if ( !pItemDef->CanBeUsedByClass( iClass ) ) + continue; + + if ( pEquipSubMenu == NULL ) + { + pEquipSubMenu = new Menu( this, "EquipMenu" ); + pEquipSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pEquipSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + + contextMenuBuilder.AddCascadingMenuItem( "#Context_Equip", pEquipSubMenu, "primaryaction" ); + } + + const char *pszClassName = NULL; + switch ( iClass ) + { + case TF_CLASS_SCOUT: pszClassName = "#TF_Class_Name_Scout"; break; + case TF_CLASS_SNIPER: pszClassName = "#TF_Class_Name_Sniper"; break; + case TF_CLASS_SOLDIER: pszClassName = "#TF_Class_Name_Soldier"; break; + case TF_CLASS_DEMOMAN: pszClassName = "#TF_Class_Name_Demoman"; break; + case TF_CLASS_MEDIC: pszClassName = "#TF_Class_Name_Medic"; break; + case TF_CLASS_HEAVYWEAPONS: pszClassName = "#TF_Class_Name_HWGuy"; break; + case TF_CLASS_PYRO: pszClassName = "#TF_Class_Name_Pyro"; break; + case TF_CLASS_SPY: pszClassName = "#TF_Class_Name_Spy"; break; + case TF_CLASS_ENGINEER: pszClassName = "#TF_Class_Name_Engineer"; break; + } + + pEquipSubMenu->AddMenuItem( pszClassName, new KeyValues( "Command", "command", CFmtStr( "equipclass%d", iClass ) ), this ); + } + } + + // For customizable items only + if ( !pItem->IsTemporaryItem() ) + { + bool bCanCraftUp = GetCollectionCraftingInvalidReason(pItem, NULL) == NULL; + bool bCanStatClockTrade = GetCraftCommonStatClockInvalidReason(pItem, NULL) == NULL; + Menu *pMannCoTradeSubMenu = NULL; + + if ( bCanCraftUp || bCanStatClockTrade ) + { + pMannCoTradeSubMenu = new Menu(this, "MannCoTradeSubMenu"); + pMannCoTradeSubMenu->SetBorder(scheme()->GetIScheme(GetScheme())->GetBorder(pszContextMenuBorder)); + pMannCoTradeSubMenu->SetFont(scheme()->GetIScheme(GetScheme())->GetFont(pszContextMenuFont)); + contextMenuBuilder.AddCascadingMenuItem("#Context_MannCoTrade", pMannCoTradeSubMenu, "customization"); + + if ( bCanCraftUp ) + { + int nIndex = pMannCoTradeSubMenu->AddMenuItem("", new KeyValues("Command", "command", "Context_CraftUpCollection"), this); + vgui::MenuItem *pMenuItem = pMannCoTradeSubMenu->GetMenuItem(nIndex); + pMenuItem->SetText("#Context_TradeUp"); + pMenuItem->InvalidateLayout(true, false); + } + + if ( bCanStatClockTrade ) + { + int nIndex = pMannCoTradeSubMenu->AddMenuItem("", new KeyValues("Command", "command", "Context_CraftCommonStatClock"), this); + vgui::MenuItem *pMenuItem = pMannCoTradeSubMenu->GetMenuItem(nIndex); + pMenuItem->SetText("#Context_CommonStatClock"); + pMenuItem->InvalidateLayout(true, false); + } + } + + + + // Campaign coin access trades + static CSchemaAttributeDefHandle pAttrDef_IsOperationPass( "is_operation_pass" ); + if ( pItem->FindAttribute( pAttrDef_IsOperationPass ) ) + { + Menu *pMannCoCoinTradeSubMenu = new Menu( this, "MannCoTradeSubMenu" ); + pMannCoCoinTradeSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pMannCoCoinTradeSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + contextMenuBuilder.AddCascadingMenuItem( "#Context_MannCoTrade", pMannCoCoinTradeSubMenu, "customization" ); + + int nIndex = pMannCoCoinTradeSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", "Context_CraftUpCollection" ), this ); + vgui::MenuItem *pMenuItem = pMannCoCoinTradeSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( "#Context_TradeUp" ); + pMenuItem->InvalidateLayout( true, false ); + + nIndex = pMannCoCoinTradeSubMenu->AddMenuItem("", new KeyValues("Command", "command", "Context_CraftCommonStatClock"), this); + pMenuItem = pMannCoCoinTradeSubMenu->GetMenuItem(nIndex); + pMenuItem->SetText("#Context_CommonStatClock"); + pMenuItem->InvalidateLayout(true, false); + } + + // Halloween trade up offering. + // Needs two attrs + static CSchemaAttributeDefHandle pAttrDef_HalloweenOffering( "allow_halloween_offering" ); + static CSchemaAttributeDefHandle pAttrDef_DeactiveDate( "deactive date" ); + + // Check the date + uint32 unDeactiveDate = 0; + uint32 unCurrentDate = CRTime::RTime32TimeCur(); + if ( pAttrDef_HalloweenOffering && pItem->FindAttribute( pAttrDef_HalloweenOffering ) && pItem->FindAttribute( pAttrDef_DeactiveDate, &unDeactiveDate ) && unDeactiveDate > unCurrentDate ) + { + vgui::MenuItem *pMenuItem = contextMenuBuilder.AddMenuItem( "#Context_HalloweenOffering", new KeyValues( "Context_HalloweenOffering" ), "customization" ); + + ImagePanel *pNewImage = new ImagePanel( pMenuItem, "new" ); + pNewImage->SetZPos( 100 ); + pNewImage->SetWide( 40 ); + pNewImage->SetTall( 40 ); + pNewImage->SetPos( 220, -5 ); + pNewImage->SetMouseInputEnabled( false ); + pNewImage->SetShouldScaleImage( true ); + pNewImage->SetImage( "new" ); + } + + // Change name + GameItemDefinition_t * pNameTagDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinitionByName( "Name Tag" ) ); + if ( CEconSharedToolSupport::ToolCanApplyToDefinition( dynamic_cast<const GameItemDefinition_t *>( pNameTagDef ), pItemDef ) ) + { + contextMenuBuilder.AddMenuItem( "#Context_Rename", new KeyValues( "DoRename" ), "customization" ); + } + + // Change description + GameItemDefinition_t * pDescTagDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinitionByName( "Description Tag" ) ); + if ( CEconSharedToolSupport::ToolCanApplyToDefinition( dynamic_cast<const GameItemDefinition_t *>( pDescTagDef ), pItemDef ) ) + { + contextMenuBuilder.AddMenuItem( "#Context_Description", new KeyValues( "DoDescription" ), "customization" ); + } + + // Add paint options sub menu + if ( m_vecPaintCans.Count() > 0 ) + { + GameItemDefinition_t * pPaintCanDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinition( m_vecPaintCans[0] ) ); + if ( pPaintCanDef && CEconSharedToolSupport::ToolCanApplyToDefinition( dynamic_cast<const GameItemDefinition_t *>( pPaintCanDef ), pItemDef ) ) + { + Menu *pPaintSubMenu = NULL; + pPaintSubMenu = new Menu( this, "PaintSubMenu" ); + pPaintSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pPaintSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + contextMenuBuilder.AddCascadingMenuItem( "#Context_Paint", pPaintSubMenu, "customization" ); + + CUtlVector<item_definition_index_t> vecOwnedPaints; + CUtlVector<item_definition_index_t> vecStorePaints; + + // Find out if the user owns this item or not and place in the proper bucket + CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); + + FOR_EACH_VEC( m_vecPaintCans, i ) + { + if ( pLocalInv && pLocalInv->FindFirstItembyItemDef( m_vecPaintCans[i] ) ) + { + vecOwnedPaints.AddToTail( m_vecPaintCans[i] ); + } + else + { + vecStorePaints.AddToTail( m_vecPaintCans[i] ); + } + } + + if ( vecOwnedPaints.Count() > 0 ) + { + // Add Header and loop + int nIndex = pPaintSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", "" ), this ); + vgui::MenuItem *pMenuItem = pPaintSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( "#TF_Owned" ) ); + pMenuItem->InvalidateLayout( true, false ); + + FOR_EACH_VEC( vecOwnedPaints, i ) + { + AddPaintToContextMenu( pPaintSubMenu, vecOwnedPaints[i], false ); + } + } + + pPaintSubMenu->AddSeparator(); + if ( vecStorePaints.Count() > 0 ) + { + // Add Header and loop + int nIndex = pPaintSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", "" ), this ); + vgui::MenuItem *pMenuItem = pPaintSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( "#TF_Commerce" ) ); + pMenuItem->InvalidateLayout( true, false ); + + FOR_EACH_VEC( vecStorePaints, i ) + { + AddPaintToContextMenu( pPaintSubMenu, vecStorePaints[i], true ); + } + } + } + } + + // Strange Parts + if ( BIsItemStrange( pItem ) ) + { + Menu *pStrangePartsSubMenu = NULL; + CUtlVector<item_definition_index_t> vecOwnedParts; + CUtlVector<item_definition_index_t> vecStoreParts; + + // Find out if the user owns this item or not and place in the proper bucket + CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory(); + FOR_EACH_VEC( m_vecStrangeParts, i ) + { + // Determine if this can be applied + //GameItemDefinition_t *pStrangePartDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinition( m_vecStrangeParts[i] ) ); + CEconItemView partItemView; + partItemView.Init( m_vecStrangeParts[i], AE_USE_SCRIPT_VALUE, 1 ); + if ( CEconSharedToolSupport::ToolCanApplyTo( &partItemView, pItem ) ) + { + // Create menu + if ( !pStrangePartsSubMenu ) + { + pStrangePartsSubMenu = new Menu( this, "StrangePartsSubMenu" ); + pStrangePartsSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) ); + pStrangePartsSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) ); + contextMenuBuilder.AddCascadingMenuItem( "#Context_StrangeParts", pStrangePartsSubMenu, "customization" ); + } + + if ( pLocalInv && pLocalInv->FindFirstItembyItemDef( m_vecStrangeParts[i] ) ) + { + vecOwnedParts.AddToTail( m_vecStrangeParts[i] ); + } + else + { + vecStoreParts.AddToTail( m_vecStrangeParts[i] ); + } + } + } + + if ( pStrangePartsSubMenu ) + { + if ( vecOwnedParts.Count() > 0 ) + { + // Add Header and loop + int nIndex = pStrangePartsSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", "" ), this ); + vgui::MenuItem *pMenuItem = pStrangePartsSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( "#TF_Owned" ) ); + pMenuItem->InvalidateLayout( true, false ); + + FOR_EACH_VEC( vecOwnedParts, i ) + { + AddCommerceToContextMenu( pStrangePartsSubMenu, "strangepart_", vecOwnedParts[i], false, false ); + } + } + + pStrangePartsSubMenu->AddSeparator(); + if ( vecStoreParts.Count() > 0 ) + { + // Add Header and loop + int nIndex = pStrangePartsSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", "" ), this ); + vgui::MenuItem *pMenuItem = pStrangePartsSubMenu->GetMenuItem( nIndex ); + pMenuItem->SetText( g_pVGuiLocalize->Find( "#TF_Market" ) ); + pMenuItem->InvalidateLayout( true, false ); + + FOR_EACH_VEC( vecStoreParts, i ) + { + AddCommerceToContextMenu( pStrangePartsSubMenu, "strangepart_", vecStoreParts[i], true, false ); + } + } + } + } + + if ( pItem->IsMarketable() ) + { + contextMenuBuilder.AddMenuItem( "#Context_MarketPlaceSell", new KeyValues( "DoSellMarketplace" ), "economy" ); + } + + // Trade to another player + if ( pItem->IsTradable() && !bSkipAddTrade ) + { + contextMenuBuilder.AddMenuItem( "#Context_Trade", new KeyValues( "DoTradeToPlayer" ), "economy" ); + } + + if ( pItem->GetItemDefinition()->GetCapabilities() & ITEM_CAP_CAN_BE_RESTORED ) + { + if ( RemovableAttributes_DoAnyAttributesApply( pItem ) ) + { + contextMenuBuilder.AddMenuItem( "#RefurbishItem", new KeyValues( "Context_RefurbishItem" ), "destructive" ); + } + } + } + } + else + { + // Check if ALL selected items can be crafted together + bool bCanCraftUp = true; + for( int i=0; i < COLLECTION_CRAFTING_ITEM_COUNT && i < vecSelectedItems.Count(); ++i ) + { + CEconItemView* pPrevItem = ( i - 1 ) < 0 ? NULL : vecSelectedItems[ i - 1 ]; + bCanCraftUp &= GetCollectionCraftingInvalidReason( vecSelectedItems[ i ], pPrevItem ) == NULL; + } + + bool bCanStatClockTrade = true; + for (int i = 0; i < COLLECTION_CRAFTING_ITEM_COUNT && i < vecSelectedItems.Count(); ++i) + { + CEconItemView* pPrevItem = (i - 1) < 0 ? NULL : vecSelectedItems[i - 1]; + bCanStatClockTrade &= GetCraftCommonStatClockInvalidReason(vecSelectedItems[i], pPrevItem) == NULL; + } + + Menu *pMannCoTradeSubMenu = NULL; + + if ( bCanCraftUp || bCanStatClockTrade ) + { + pMannCoTradeSubMenu = new Menu(this, "MannCoTradeSubMenu"); + pMannCoTradeSubMenu->SetBorder(scheme()->GetIScheme(GetScheme())->GetBorder(pszContextMenuBorder)); + pMannCoTradeSubMenu->SetFont(scheme()->GetIScheme(GetScheme())->GetFont(pszContextMenuFont)); + contextMenuBuilder.AddCascadingMenuItem("#Context_MannCoTrade", pMannCoTradeSubMenu, "customization"); + + if (bCanCraftUp) + { + int nIndex = pMannCoTradeSubMenu->AddMenuItem("", new KeyValues("Command", "command", "Context_CraftUpCollection"), this); + vgui::MenuItem *pMenuItem = pMannCoTradeSubMenu->GetMenuItem(nIndex); + pMenuItem->SetText("#Context_TradeUp"); + pMenuItem->InvalidateLayout(true, false); + } + + if (bCanStatClockTrade) + { + int nIndex = pMannCoTradeSubMenu->AddMenuItem("", new KeyValues("Command", "command", "Context_CraftCommonStatClock"), this); + vgui::MenuItem *pMenuItem = pMannCoTradeSubMenu->GetMenuItem(nIndex); + pMenuItem->SetText("#Context_CommonStatClock"); + pMenuItem->InvalidateLayout(true, false); + } + } + } + + if ( !m_bShowBaseItems ) + { + bool bDeleteAvailable = true; + // Check that all of the selected items are deletable + for( int i=0; i < vecSelectedItems.Count() && bDeleteAvailable; ++i ) + { + static CSchemaAttributeDefHandle pAttrDef_NoDelete( "cannot delete" ); + bDeleteAvailable &= !vecSelectedItems[i]->FindAttribute( pAttrDef_NoDelete ); + } + + // Only show the delete button if every slected item is deletable + if ( bDeleteAvailable ) + { + contextMenuBuilder.AddMenuItem( "#TF_SteamWorkshop_Delete", new KeyValues( "DoDelete" ), "destructive" ); + } + } + + // Position to the cursor's position + int nX, nY; + g_pVGuiInput->GetCursorPosition( nX, nY ); + m_pContextMenu->SetPos( nX - 1, nY - 1 ); + + m_pContextMenu->SetVisible(true); + m_pContextMenu->AddActionSignalTarget(this); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemPanelMouseRightRelease( vgui::Panel *panel ) +{ +#ifdef STAGING_ONLY + if ( tf_use_card_tooltips.GetBool() ) + { + m_pMouseOverCardPanel->PinCard( true ); + } + else +#endif + { + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + if ( pItemPanel && pItemPanel->IsVisible() ) + { + // If they're not holding down ctrl, deselect all existing selections + if ( !vgui::input()->IsKeyDown(KEY_LCONTROL) && !vgui::input()->IsKeyDown(KEY_RCONTROL) ) + { + DeSelectAllBackpackItemPanels(); + ToggleSelectBackpackItemPanel( pItemPanel ); + } + else if ( AllowSelection() && !pItemPanel->IsGreyedOut() ) + { + if ( !pItemPanel->IsSelected() && pItemPanel->HasItem() ) + { + pItemPanel->SetSelected( true ); + } + SetBorderForItem( pItemPanel, false ); + } + + OpenContextMenu(); + } + } +} + +void CBackpackPanel::OnMouseMismatchedRelease( MouseCode code, Panel* pPressedPanel ) +{ + if ( pPressedPanel ) + { + OnMouseReleased( code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::StartDrag( int x, int y ) +{ + // don't allow item drag if there's a filter + if ( HasNameFilter() ) + return; + + m_bDragging = true; + HideMouseOverPanel(); + + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_pMouseDragItemPanel->SetItem( m_pItemDraggedFromPanel->GetItem() ); + m_pMouseDragItemPanel->InvalidateLayout( true ); + + m_pItemDraggedFromPanel->Dragged( true ); + + // Calculate the mouse offset from the top left of the panel we're going to drag + m_iDragOffsetX = m_pMouseDragItemPanel->GetWide() * 0.5f; + m_iDragOffsetY = m_pMouseDragItemPanel->GetTall() * 0.5f; + m_flPreventDragPageSwitchUntil = 0; + + m_pMouseDragItemPanel->SetVisible( true ); + + m_pItemDraggedFromPanel->SetItem( NULL ); + SetBorderForItem( m_pItemDraggedFromPanel, false ); + m_pPrevDragOverItemPanel = NULL; + + vgui::input()->SetMouseCapture( GetVPanel() ); + + if ( m_pDragToNextPageButton && m_pDragToPrevPageButton ) + { + m_pDragToNextPageButton->SetVisible( GetNumPages() > 1 ); + m_pDragToPrevPageButton->SetVisible( GetNumPages() > 1 ); + } + + // play pickup sound + CEconItemView *item = m_pMouseDragItemPanel->GetItem(); + if ( item ) + { + const char *soundFilename = item->GetDefinitionString( "pickup_sound", "" ); + if ( soundFilename[0] ) + { + vgui::surface()->PlaySound( soundFilename ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::StopDrag( bool bSucceeded ) +{ + if ( !m_pItemDraggedFromPanel ) + return; + + if ( !bSucceeded ) + { + if ( m_iDraggedFromPage == GetCurrentPage() ) + { + m_pItemDraggedFromPanel->SetItem( m_pMouseDragItemPanel->GetItem() ); + } + m_pItemDraggedFromPanel = NULL; + } + + m_pMouseDragItemPanel->SetVisible( false ); + m_bDragging = false; + + vgui::input()->SetMouseCapture( NULL ); + + if ( m_pDragToNextPageButton && m_pDragToPrevPageButton ) + { + m_pDragToNextPageButton->SetVisible( false ); + m_pDragToPrevPageButton->SetVisible( false ); + } + + // play drop sound + CEconItemView *item = m_pMouseDragItemPanel->GetItem(); + if ( item ) + { + const char *soundFilename = item->GetDefinitionString( "drop_sound", "ui/item_default_drop.wav" ); + vgui::surface()->PlaySound( soundFilename ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBackpackPanel::GetBackpackPositionForPanel( CItemModelPanel *pItemPanel ) +{ + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i] == pItemPanel ) + return i; + } + return -1; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::HandleDragTo( CItemModelPanel *pItemPanel, int iPanelIndex ) +{ + // Find the position based on the panel we're dragging to + if ( iPanelIndex != -1 ) + { + // If the current panel is selected, unselect it + if ( m_pItemModelPanels[iPanelIndex]->IsSelected() ) + { + ToggleSelectBackpackItemPanel( m_pItemModelPanels[iPanelIndex] ); + } + if ( m_pItemDraggedFromPanel->IsSelected() ) + { + ToggleSelectBackpackItemPanel( m_pItemDraggedFromPanel ); + } + + // We "move" the items in the backpack immediately, because when the messages come back + // from steam they'll fix the positions if the move fails for some reason. + CEconItemView *pItem = NULL; + if ( m_pItemModelPanels[iPanelIndex]->HasItem() ) + { + // We need to copy it because it's about to get stomped by the other item + pItem = new CEconItemView( *m_pItemModelPanels[iPanelIndex]->GetItem() ); + } + m_pItemModelPanels[iPanelIndex]->SetItem( m_pMouseDragItemPanel->GetItem() ); + + if ( m_iDraggedFromPage == GetCurrentPage() ) + { + m_pItemDraggedFromPanel->SetItem( pItem ); + } + + if ( pItem ) + { + delete pItem; + } + + // Tell the inventory to move the item + // Translate it to the right page + int iBackpackPosition = GetBackpackPosForPanelIndex( iPanelIndex ); + InventoryManager()->MoveItemToBackpackPosition( m_pMouseDragItemPanel->GetItem(), iBackpackPosition ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnTick( void ) +{ + BaseClass::OnTick(); + + bool bNeedsTick = false; + if ( m_flStartExplanationsAt && m_flStartExplanationsAt < Plat_FloatTime() ) + { + m_flStartExplanationsAt = 0; + + if ( ShouldShowExplanations() ) + { + ConVar *pConVar = GetExplanationConVar(); + if ( pConVar ) + { + pConVar->SetValue( 1 ); + } + + CExplanationPopup *pPopup = dynamic_cast<CExplanationPopup*>( FindChildByName("StartExplanation") ); + if ( pPopup ) + { + pPopup->Popup(); + } + } + } + else + { + bNeedsTick = true; + } + + // To handle page movement while holding the mouse still over the page buttons, + // we need to keep calling OnCursorMoved() whenever we're dragging. + if ( m_bDragging && m_pMouseDragItemPanel && m_pItemDraggedFromPanel && IsVisible() ) + { + int mx,my; + vgui::input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + OnCursorMoved( mx,my ); + + bNeedsTick = true; + } + + if ( !bNeedsTick && !NeedsDerivedTickSignal() ) + { + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnThink( void ) +{ + BaseClass::OnThink(); + + if ( m_flFilterItemTime > 0 && gpGlobals->curtime >= m_flFilterItemTime ) + { + SetCurrentPage( 0 ); + DeSelectAllBackpackItemPanels(); + UpdateModelPanels(); + + m_flFilterItemTime = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnCursorMoved( int x, int y ) +{ + if ( !m_pItemDraggedFromPanel ) + return; + + if ( m_bDragging && m_pMouseDragItemPanel ) + { + m_pMouseDragItemPanel->SetPos( x - m_iDragOffsetX, y - m_iDragOffsetY ); + + // When we're dragging, we have mouse capture, so the item panels aren't getting mouse input. + // We need to find out what item panel we're over, and let it know. + + if ( m_flPreventDragPageSwitchUntil < Plat_FloatTime() ) + { + // First, are we over the page turning areas? + bool bDragNext = false; + if ( m_pDragToNextPageButton && m_pDragToNextPageButton->IsVisible() ) + { + int iDragX, iDragY; + m_pDragToNextPageButton->GetPos( iDragX, iDragY ); + bDragNext = ( x >= iDragX && x <= (iDragX + m_pDragToNextPageButton->GetWide() ) ); + } + if ( !bDragNext && m_pNextPageButton && m_pNextPageButton->IsEnabled() ) + { + int iDragX, iDragY; + m_pNextPageButton->GetPos( iDragX, iDragY ); + bDragNext = ( x >= iDragX && x <= (iDragX + m_pNextPageButton->GetWide()) && y >= iDragY && y <= (iDragY + m_pNextPageButton->GetTall()) ); + } + if ( bDragNext ) + { + OnCommand( "nextpage" ); + m_flPreventDragPageSwitchUntil = Plat_FloatTime() + tf_backpack_page_button_delay.GetFloat(); + return; + } + + bool bDragPrev = false; + if ( m_pDragToPrevPageButton && m_pDragToPrevPageButton->IsVisible() ) + { + int iDragX, iDragY; + m_pDragToPrevPageButton->GetPos( iDragX, iDragY ); + bDragPrev = ( x >= iDragX && x <= (iDragX + m_pDragToPrevPageButton->GetWide() ) ); + } + if ( !bDragPrev && m_pPrevPageButton && m_pPrevPageButton->IsEnabled() ) + { + int iDragX, iDragY; + m_pPrevPageButton->GetPos( iDragX, iDragY ); + bDragPrev = ( x >= iDragX && x <= (iDragX + m_pPrevPageButton->GetWide()) && y >= iDragY && y <= (iDragY + m_pPrevPageButton->GetTall()) ); + } + if ( bDragPrev ) + { + OnCommand( "prevpage" ); + m_flPreventDragPageSwitchUntil = Plat_FloatTime() + tf_backpack_page_button_delay.GetFloat(); + return; + } + } + + // check if we're hovering page button + static int iPrevHoveringPage = -1; + static float flLastPageButtonEnterTime = 0.f; + int iHoveringPage = GetPageButtonIndexAtPos( x, y ); + if ( iHoveringPage != -1 ) + { + if ( iHoveringPage == GetCurrentPage() ) + { + iPrevHoveringPage = -1; + flLastPageButtonEnterTime = 0.f; + } + else if ( iPrevHoveringPage != iHoveringPage ) + { + iPrevHoveringPage = iHoveringPage; + flLastPageButtonEnterTime = Plat_FloatTime() + tf_backpack_page_button_delay.GetFloat(); + } + else if ( flLastPageButtonEnterTime > 0 && flLastPageButtonEnterTime < Plat_FloatTime() ) + { + flLastPageButtonEnterTime = 0.f; + SetCurrentPage( iHoveringPage ); + UpdateModelPanels(); + } + return; + } + else + { + // reset hovering page buttons data + iPrevHoveringPage = -1; + flLastPageButtonEnterTime = 0.f; + } + + CItemModelPanel *pOverPanel = GetItemPanelAtPos( x, y ); + if ( m_pPrevDragOverItemPanel != pOverPanel ) + { + if ( m_pPrevDragOverItemPanel ) + { + OnItemPanelExited( m_pPrevDragOverItemPanel ); + } + + m_pPrevDragOverItemPanel = pOverPanel; + + if ( m_pPrevDragOverItemPanel ) + { + OnItemPanelEntered( m_pPrevDragOverItemPanel ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemPanelCursorMoved( int x, int y ) +{ + if ( !m_pItemDraggedFromPanel ) + return; + + if ( !m_bDragging && m_pItemDraggedFromPanel->HasItem() && !InToolSelectionMode() ) + { + // Don't drag instantly, so it's easy to select + if ( (gpGlobals->curtime - m_flMouseDownTime) > 0.3 ) + { + StartDrag( x,y ); + } + else + { + if ( !m_iMouseDownX ) + { + m_iMouseDownX = x; + m_iMouseDownY = y; + } + else if ( abs(m_iMouseDownX - x) > XRES(10) || abs(m_iMouseDownY - y) > YRES(10) ) + { + StartDrag( x,y ); + } + } + } + + OnCursorMoved( x, y ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::ToggleSelectBackpackItemPanel( CItemModelPanel *pPanel ) +{ + if ( !AllowSelection() || pPanel->IsGreyedOut() ) + return; + + if ( pPanel->IsSelected() || !pPanel->HasItem() ) + { + pPanel->SetSelected( false ); + } + else + { + pPanel->SetSelected( true ); + } + SetBorderForItem( pPanel, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::DeSelectAllBackpackItemPanels( void ) +{ + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() ) + { + m_pItemModelPanels[i]->SetSelected( false ); + SetBorderForItem( m_pItemModelPanels[i], false ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever the selection changes in the item loadout panel +//----------------------------------------------------------------------------- +void CBackpackPanel::OnItemContentsChanged( CEconItemView *pEconItemView ) +{ + Assert( pEconItemView ); + + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + CEconItemView *pInternalItem = m_pItemModelPanels[i] && m_pItemModelPanels[i]->HasItem() + ? m_pItemModelPanels[i]->GetItem() + : NULL; + + if ( *pInternalItem == *pEconItemView ) + { + m_pItemModelPanels[i]->DirtyDescription(); + OnItemSelectionChanged(); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when text changes in combo box +//----------------------------------------------------------------------------- +void CBackpackPanel::OnTextChanged( KeyValues *data ) +{ + Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") ); + + vgui::TextEntry *pTextEntry = dynamic_cast<vgui::TextEntry *>( pPanel ); + if ( pTextEntry ) + { + if ( pTextEntry == m_pNameFilterTextEntry ) + { + m_wNameFilter.RemoveAll(); + if ( m_pNameFilterTextEntry->GetTextLength() ) + { + m_wNameFilter.EnsureCount( m_pNameFilterTextEntry->GetTextLength() + 1 ); + m_pNameFilterTextEntry->GetText( m_wNameFilter.Base(), m_wNameFilter.Count() * sizeof(wchar_t) ); + V_wcslower( m_wNameFilter.Base() ); + } + m_flFilterItemTime = gpGlobals->curtime + 0.5f; + return; + } + } + + vgui::ComboBox *pComboBox = dynamic_cast<vgui::ComboBox *>( pPanel ); + if ( pComboBox ) + { + if ( pComboBox == m_pSortByComboBox ) + { + // the class selection combo box changed, update class details + KeyValues *pUserData = m_pSortByComboBox->GetActiveItemUserData(); + if ( !pUserData ) + return; + + enum { kSortType_Dummy = -1 }; + int iSortTypeSelectionIndex = pUserData->GetInt( "sortby", kSortType_Dummy ); + if ( iSortTypeSelectionIndex != kSortType_Dummy ) + { + uint32 iSortType = g_BackpackSortTypes[iSortTypeSelectionIndex].iSortType; + if ( iSortType != kGCItemSort_NoSort ) + { + InventoryManager()->SortBackpackBy( iSortType ); + + // Now go back to the "Sort by" header, and move the focus to the close button. + m_pSortByComboBox->ActivateItemByRow( 0 ); + } + } + } + else if ( pComboBox == m_pShowRarityComboBox ) + { + cl_showbackpackrarities.SetValue( m_pShowRarityComboBox->GetActiveItem() ); + + // Refresh all item borders + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + SetBorderForItem( m_pItemModelPanels[i], false ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnButtonChecked( KeyValues *pData ) +{ + Panel *pPanel = reinterpret_cast<vgui::Panel *>( pData->GetPtr("panel") ); + + if ( m_bShowBaseItems != m_pShowBaseItemsCheckbox->IsSelected() && m_pShowBaseItemsCheckbox == pPanel && IsVisible() ) + { + SetShowBaseItems( m_pShowBaseItemsCheckbox->IsSelected() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnCancelSelection( void ) +{ + if ( m_pConfirmDeleteDialog ) + { + m_pConfirmDeleteDialog->MarkForDeletion(); + m_pConfirmDeleteDialog = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CBackpackPanel::GetGreyOutItemPanelReason( CItemModelPanel *pItemPanel ) +{ + if ( InToolSelectionMode() ) + { + bool bIsSelectedTool = (m_ToolSelectionItem.IsValid() && pItemPanel->HasItem()) ? (m_ToolSelectionItem == *pItemPanel->GetItem()) : false; + if ( !bIsSelectedTool ) + { + if ( m_ToolSelectionItem.GetStaticData()->IsTool() ) + { + if ( !CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, pItemPanel->GetItem() ) ) + { + return "#Econ_GreyOutReason_ToolCannotApply"; + } + } + else if ( pItemPanel->GetItem() && pItemPanel->GetItem()->GetStaticData()->IsTool() ) + { + if ( !CEconSharedToolSupport::ToolCanApplyTo( pItemPanel->GetItem(), &m_ToolSelectionItem ) ) + { + return "#Econ_GreyOutReason_ToolCannotApply"; + } + } + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver ) +{ + tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ); + + if ( !pItemPanel ) + return; + + const char *pszBorder = NULL; + + bool bIsSelectedTool = (m_ToolSelectionItem.IsValid() && pItemPanel->HasItem()) ? (m_ToolSelectionItem == *pItemPanel->GetItem()) : false; + + // Handle grey out + const char *pszGreyOutReason = GetGreyOutItemPanelReason( pItemPanel ); + const bool bGreyOut = pszGreyOutReason != NULL; + + pItemPanel->SetGreyedOut( pszGreyOutReason ); + + int iRarity = GetItemQualityForBorder( pItemPanel ); + + if ( InToolSelectionMode() && bIsSelectedTool ) + { + // We're in tool application mode, and this panel is the tool being used + pszBorder = "BackpackItemBorder_SelfMade"; + + if ( m_pToolIcon ) + { + int iX, iY; + pItemPanel->GetPos( iX, iY ); + m_pToolIcon->SetPos( iX, iY ); + m_pToolIcon->SetVisible( true ); + } + } + else if ( bGreyOut ) + { + if( pItemPanel->IsSelected() ) + { + pszBorder = g_szItemBorders[iRarity][4]; + } + else + { + pszBorder = g_szItemBorders[iRarity][3]; + } + } + else + { + + if ( pItemPanel->IsSelected() ) + { + pszBorder = g_szItemBorders[iRarity][2]; + } + else if ( bMouseOver ) + { + pszBorder = g_szItemBorders[iRarity][1]; + } + else + { + pszBorder = g_szItemBorders[iRarity][0]; + } + } + + vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) ); +} + +//----------------------------------------------------------------------------- +class CTFRemoveItemCustomizationConfirmDialog : public CTFGenericConfirmDialog +{ + DECLARE_CLASS_SIMPLE( CTFRemoveItemCustomizationConfirmDialog, CTFGenericConfirmDialog ); +public: + CTFRemoveItemCustomizationConfirmDialog( const RefurbishableProperty& prop, CEconItemView *pItem ) + : CTFGenericConfirmDialog( prop.m_szDialogTitle, // dialog title + prop.m_szDialogDesc, // dialog text + "#RefurbishItem_Yes", // confirm button text + "#RefurbishItem_No", // cancel button text + NULL, // callback + NULL ) // parent + , m_prop( prop ) + , m_Item( *pItem ) // copy in case our UI changes behind us + { + GetCustomDialogLocalizationTokenFunc_t m_pDialogCustomTokenFunc = m_prop.m_pGetCustomDialogLocalizationTokenFunc; + if ( m_pDialogCustomTokenFunc ) + { + CUtlConstWideString wsDialogCustomToken; + (*m_pDialogCustomTokenFunc)( &m_Item, m_prop.m_iUserData, wsDialogCustomToken ); + if ( !wsDialogCustomToken.IsEmpty() ) + { + AddStringToken( "confirm_dialog_token", wsDialogCustomToken.Get() ); + } + } + } + + virtual ~CTFRemoveItemCustomizationConfirmDialog() { } + + virtual void OnCommand( const char *command ); + +private: + RefurbishableProperty m_prop; + CEconItemView m_Item; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SendGCSimpleAttributeRemovalMessage( CEconItemView *pEconItemView, const char *szDesc, EGCItemMsg eItemMsg ) +{ + EconUI()->Gamestats_ItemTransaction( IE_ITEM_REMOVED_ATTRIB, pEconItemView, szDesc ); + GCSDK::CProtoBufMsg<CMsgGCRemoveCustomizationAttributeSimple> msg( eItemMsg ); + msg.Body().set_item_id( pEconItemView->GetItemID() ); + GCClientSystem()->BSendMessage( msg ); +} + +void CTFRemoveItemCustomizationConfirmDialog::OnCommand( const char *command ) +{ + BaseClass::OnCommand( command ); + + // Did the user say "yes, remove this particular attribute"? If so, notify the GC. We can't + // remove multiple attributes at a time because each removal will cause a new item to be created, + // invalidating the item reference we've got in this dialog. + if ( !Q_strnicmp( command, "confirm", 7 ) ) + { + // remove the attribute + switch( m_prop.m_eRemovalType ) + { + case kCustomizationRemove_Paint: + SendGCSimpleAttributeRemovalMessage( &m_Item, "paint", k_EMsgGCRemoveItemPaint ); + break; + + case kCustomizationRemove_Name: + { + EconUI()->Gamestats_ItemTransaction( IE_ITEM_REMOVED_ATTRIB, &m_Item, "name" ); + GCSDK::CGCMsg< MsgGCRemoveItemName_t > msg( k_EMsgGCRemoveItemName ); + msg.Body().m_unItemID = m_Item.GetItemID(); + msg.Body().m_bDescription = false; + GCClientSystem()->BSendMessage( msg ); + } + break; + + case kCustomizationRemove_Desc: + { + EconUI()->Gamestats_ItemTransaction( IE_ITEM_REMOVED_ATTRIB, &m_Item, "description" ); + GCSDK::CGCMsg< MsgGCRemoveItemName_t > msg( k_EMsgGCRemoveItemName ); + msg.Body().m_unItemID = m_Item.GetItemID(); + msg.Body().m_bDescription = true; + GCClientSystem()->BSendMessage( msg ); + } + break; + + case kCustomizationRemove_CustomTexture: + SendGCSimpleAttributeRemovalMessage( &m_Item, "custom_texture", k_EMsgGCRemoveCustomTexture ); + break; + + case kCustomizationRemove_MakersMark: + SendGCSimpleAttributeRemovalMessage( &m_Item, "makers_mark", k_EMsgGCRemoveMakersMark ); + break; + + case kCustomizationRemove_StrangePart: + { + Assert( m_prop.m_iUserData != kNoUserData ); + int iKillEaterAttrIndex = m_prop.m_iUserData; + + // What attribute did we select? + const CEconItemAttributeDefinition *pAttrDef = GetKillEaterAttr_Type( iKillEaterAttrIndex ); + Assert( pAttrDef ); + + // Make sure this item has this attribute. + float fScoreType = kKillEaterEvent_PlayerKill; + Verify( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( &m_Item, pAttrDef, &fScoreType ) || iKillEaterAttrIndex == 0 ); + + // Dispatch message. + EconUI()->Gamestats_ItemTransaction( IE_ITEM_REMOVED_ATTRIB, &m_Item, "strange_part" ); + GCSDK::CProtoBufMsg<CMsgGCRemoveStrangePart> msg( k_EMsgGCRemoveStrangePart ); + + msg.Body().set_item_id( m_Item.GetItemID() ); + msg.Body().set_strange_part_score_type( (int)fScoreType ); + GCClientSystem()->BSendMessage( msg ); + } + break; + + case kCustomizationRemove_StrangeScores: + { + Assert( m_prop.m_iUserData == kNoUserData ); + + EconUI()->Gamestats_ItemTransaction( IE_ITEM_RESET_STRANGE_COUNTERS, &m_Item ); + GCSDK::CProtoBufMsg<CMsgGCResetStrangeScores> msg( k_EMsgGCResetStrangeScores ); + msg.Body().set_item_id( m_Item.GetItemID() ); + GCClientSystem()->BSendMessage( msg ); + } + break; + + case kCustomizationRemove_UpgradeCard: + { + Assert( m_prop.m_iUserData != kNoUserData ); + + // Make sure we selected a valid attribute that this item has. + const CEconItemAttributeDefinition *pAttrDef = GetCardUpgradeForIndex( &m_Item, m_prop.m_iUserData ); + Assert( pAttrDef ); + Verify( m_Item.FindAttribute( pAttrDef ) ); + + // Dispatch message. + EconUI()->Gamestats_ItemTransaction( IE_ITEM_REMOVED_ATTRIB, &m_Item, "upgrade_card" ); + GCSDK::CProtoBufMsg<CMsgGCRemoveUpgradeCard> msg( k_EMsgGCRemoveUpgradeCard ); + + msg.Body().set_item_id( m_Item.GetItemID() ); + msg.Body().set_attribute_index( pAttrDef->GetDefinitionIndex() ); + GCClientSystem()->BSendMessage( msg ); + } + break; + + case kCustomizationRemove_KillStreak: + SendGCSimpleAttributeRemovalMessage( &m_Item, "killstreak", k_EMsgGCRemoveKillStreak ); + break; + case kCustomizationRemove_GiftedBy: + SendGCSimpleAttributeRemovalMessage( &m_Item, "giftedby", k_EMsgGCRemoveGiftedBy ); + break; + case kCustomizationRemove_Festivizer: + SendGCSimpleAttributeRemovalMessage( &m_Item, "festivizer", k_EMsgGCRemoveFestivizer ); + break; + default: + AssertMsg( false, "Unknown item customization removal type!" ); + break; + } + } + + SetVisible( false ); + MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CRefurbishItemDialog : public CComboBoxBackpackOverlayDialogBase +{ +public: + DECLARE_CLASS_SIMPLE( CRefurbishItemDialog, CComboBoxBackpackOverlayDialogBase ); + +public: + CRefurbishItemDialog( vgui::Panel *pParent, CEconItemView *m_pItem ) : CComboBoxBackpackOverlayDialogBase( pParent, m_pItem ) { } + +private: + virtual void PopulateComboBoxOptions() + { + Assert( m_pItem ); + + KeyValues *pKeyValues = new KeyValues( "data" ); + for ( int i = 0; i < GetRemovableAttributesCount(); i++ ) + { + if ( RemovableAttributes_DoesAttributeApply( i, m_pItem ) ) + { + pKeyValues->SetInt( "data", i ); + + RefurbishableProperty prop = RemovableAttributes_GetAttributeDetails( i ); + + CUtlConstWideString wsDialogCustomToken; + if ( prop.m_pGetCustomDialogLocalizationTokenFunc ) + { + (*prop.m_pGetCustomDialogLocalizationTokenFunc)( m_pItem, prop.m_iUserData, wsDialogCustomToken ); + } + CConstructLocalizedString localizedUI( GLocalizationProvider()->Find( prop.m_pszSelectionUILocalizationToken ), wsDialogCustomToken.IsEmpty() ? L"" : wsDialogCustomToken.Get() ); + GetComboBox()->AddItem( localizedUI, pKeyValues ); + } + } + pKeyValues->deleteThis(); + + Assert( GetComboBox()->GetItemCount() > 0 ); + + GetComboBox()->ActivateItemByRow( 0 ); + } + + virtual void OnComboBoxApplication() + { + if ( !m_pItem ) + return; + + KeyValues *pKVActiveUserData = GetComboBox()->GetActiveItemUserData(); + int iIndex = pKVActiveUserData ? pKVActiveUserData->GetInt( "data", -1 ) : -1; + if ( iIndex < 0 ) + return; + + const RefurbishableProperty RefurbProp = RemovableAttributes_GetAttributeDetails( iIndex ); + + CTFRemoveItemCustomizationConfirmDialog *pDialog = new CTFRemoveItemCustomizationConfirmDialog( RefurbProp, m_pItem ); + if ( pDialog ) + { + pDialog->Show(); + } + } + + virtual const char *GetTitleLabelLocalizationToken() const { return "#TF_Item_RefurbishItemHeader"; } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Handles item selection while in tool selection mode +//----------------------------------------------------------------------------- +void CBackpackPanel::HandleToolItemSelection( CEconItemView *pItem ) +{ + if ( !InToolSelectionMode() ) + { + // must be in tool selection mode + Assert( InToolSelectionMode() ); + return; + } + + if ( pItem ) + { + // Check if we should bring up the shuffle dialog instead of directly using the tool + static CSchemaAttributeDefHandle pAttrDef_CanShuffleCrateContents( "can shuffle crate contents" ); + if ( pItem->FindAttribute( pAttrDef_CanShuffleCrateContents ) ) + { + CInputStringForItemBackpackOverlayDialog *pDialog = vgui::SETUP_PANEL( new CInputStringForItemBackpackOverlayDialog( this, pItem, &m_ToolSelectionItem ) ); + if ( pDialog ) + { + pDialog->Show(); + CancelToolSelection(); + } + } + else if ( m_ToolSelectionItem.FindAttribute( pAttrDef_CanShuffleCrateContents ) ) + { + CInputStringForItemBackpackOverlayDialog *pDialog = vgui::SETUP_PANEL( new CInputStringForItemBackpackOverlayDialog( this, &m_ToolSelectionItem, pItem ) ); + if ( pDialog ) + { + pDialog->Show(); + CancelToolSelection(); + } + } + // is a tool being applied onto this item + else if ( m_ToolSelectionItem.GetStaticData()->IsTool() && ApplyTool( this, &m_ToolSelectionItem, pItem ) ) + { + CancelToolSelection(); + UpdateModelPanels(); + } + // is this item a tool that can be applied on a selected item + else if ( pItem->GetStaticData()->IsTool() && ApplyTool( this, pItem, &m_ToolSelectionItem ) ) + { + CancelToolSelection(); + UpdateModelPanels(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Tries to use the item. This might switch the panel to a tool item +// selection mode, or launch the recipe crafting panel. +//----------------------------------------------------------------------------- +void CBackpackPanel::SetupToolSelectionItem() +{ + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + m_ToolSelectionItem = *m_pItemModelPanels[i]->GetItem(); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Open up the trade dialog +//----------------------------------------------------------------------------- +void CBackpackPanel::DoTradeToPlayer() +{ + OpenTradingStartDialog( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Use the trade dialog to send a gift to a player. +//----------------------------------------------------------------------------- +void CBackpackPanel::DoGiftToPlayer() +{ + OpenTradingStartDialog( this, &m_ToolSelectionItem ); +} + +//----------------------------------------------------------------------------- +// Purpose: Open up the overlay to sell the selected item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoSellMarketplace() +{ + CUtlVector< CItemModelPanel* > m_vecSelected; + GetSelectedPanels( SELECT_FIRST, m_vecSelected ); + Assert( m_vecSelected.Count() ); + if( !m_vecSelected.Count() ) + return; + + if ( m_vecSelected.Count() && steamapicontext && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() ) + { + CEconItemView *pItem = m_vecSelected.Head()->GetItem(); + const char *pszPrefix = ""; + if ( GetUniverse() == k_EUniverseBeta ) + { + pszPrefix = "beta."; + } + uint32 nAssetContext = 2; // k_EEconContextBackpack + char szURL[512]; + V_snprintf( szURL, sizeof(szURL), "http://%ssteamcommunity.com/my/inventory/?sellOnLoad=1#%d_%d_%llu", pszPrefix, engine->GetAppID(), nAssetContext, pItem->GetItemID() ); + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Use a description tag, or offer to buy one +//----------------------------------------------------------------------------- +void CBackpackPanel::DoDescription() +{ + static CSchemaItemDefHandle pItemDef_DescTag( "Description Tag" ); + if ( !AttemptToUseItem( pItemDef_DescTag->GetDefinitionIndex() ) ) + { + AttemptToShowItemInStore( pItemDef_DescTag->GetDefinitionIndex() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Use a name tag, or offer to buy one +//----------------------------------------------------------------------------- +void CBackpackPanel::DoRename() +{ + static CSchemaItemDefHandle pItemDef_NameTag( "Name Tag" ); + if ( !AttemptToUseItem( pItemDef_NameTag->GetDefinitionIndex() ) ) + { + AttemptToShowItemInStore( pItemDef_NameTag->GetDefinitionIndex() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Delete the selected items +//----------------------------------------------------------------------------- +void CBackpackPanel::DoDelete() +{ + // Hide the mouseover panel + HideMouseOverPanel(); + + int iItemsToDelete = 0; + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + iItemsToDelete++; + } + } + + // Bring up confirm dialog + CConfirmDeleteItemDialog *pConfirm = vgui::SETUP_PANEL( new CConfirmDeleteItemDialog( this, ( iItemsToDelete > 1 ) ) ); + if ( pConfirm ) + { + pConfirm->Show(); + + m_pConfirmDeleteDialog = pConfirm; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Use the selected tool +//----------------------------------------------------------------------------- +void CBackpackPanel::DoApplyOnItem() +{ + SetupToolSelectionItem(); + + if ( m_ToolSelectionItem.IsValid() ) + { + const IEconTool *pEconTool = m_ToolSelectionItem.GetItemDefinition()->GetEconTool(); + + // Gather all quest objective attributes + CRecipeComponentMatchingIterator recipeIterator( NULL, NULL ); + if ( m_ToolSelectionItem.GetSOCData() ) + { + m_ToolSelectionItem.GetSOCData()->IterateAttributes( &recipeIterator ); + } + + if( pEconTool && recipeIterator.GetMatchingComponentInputs().Count() > 0 ) + { + // Launch new crafting window if we need to + if( m_pDynamicRecipePanel == NULL ) + { + m_pDynamicRecipePanel = vgui::SETUP_PANEL( new CDynamicRecipePanel( this, "dynamic_recipe_panel", &m_ToolSelectionItem ) ); + } + + // Set recipe item into panel + if ( m_pDynamicRecipePanel ) + { + m_pDynamicRecipePanel->SetVisible( true ); + m_pDynamicRecipePanel->SetNewRecipe( &m_ToolSelectionItem ); + } + return; + } + + // Check if we actually have any items we can use this tool on + bool bHasValidTargetItem = false; + CPlayerInventory *pInv = InventoryManager()->GetLocalInventory(); + Assert( pInv ); + if ( pInv ) + { + for ( int i = 0 ; i < pInv->GetItemCount() ; ++i ) + { + CEconItemView *pItem = pInv->GetItem( i ); + if ( CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, pItem ) ) + { + bHasValidTargetItem = true; + break; + } + } + + // If no applicable items, try stock items + if ( !bHasValidTargetItem ) + { + // this is really inefficient, maybe have a list of baseitems somewhere + CEconItemView tempItem; + const CEconItemDefinition* pItemDef = NULL; + + const CEconItemSchema::SortedItemDefinitionMap_t& mapItems = GetItemSchema()->GetSortedItemDefinitionMap(); + for ( int it = mapItems.FirstInorder(); it != mapItems.InvalidIndex(); it = mapItems.NextInorder( it ) ) + { + if ( mapItems[it]->IsBaseItem() && !mapItems[it]->IsHidden() ) + { + CFmtStr fmtStrCustomizedDefName( "Upgradeable %s", mapItems[it]->GetDefinitionName() ); + pItemDef = GetItemSchema()->GetItemDefinitionByName( fmtStrCustomizedDefName.Access() ); + if ( pItemDef ) + { + tempItem.Init( pItemDef->GetDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); + if ( CEconSharedToolSupport::ToolCanApplyTo( &m_ToolSelectionItem, &tempItem ) ) + { + bHasValidTargetItem = true; + break; + } + } + } + } + + if ( bHasValidTargetItem ) + { + // automatically switch to stock items + OnCommand( "showbaseitems" ); + } + } + } + + if ( !bHasValidTargetItem ) + { + ShowMessageBox( NULL, "#ToolNoTargetItems", "#GameUI_OK" ); + return; + } + + m_eSelectionMode = ToolSelection; + m_nLastToolPage = GetCurrentPage(); + + if ( m_pMouseOverTooltip ) + { + m_pMouseOverTooltip->HideTooltip(); + } + + ClearNameFilter( true ); + SetCurrentPage( 0 ); + UpdateModelPanels(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: use a consumable item directly from the backpack +//----------------------------------------------------------------------------- +void CBackpackPanel::DoUseConsumableItem() +{ + SetupToolSelectionItem(); + +#ifdef TF_CLIENT_DLL + UseConsumableItem( &m_ToolSelectionItem, this ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Use the tool to unwrap an item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoUnwrapItem() +{ + DoUseConsumableItem(); +} + +//----------------------------------------------------------------------------- +// Purpose: Use the tool to deliver an item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoDeliverItem() +{ + SetupToolSelectionItem(); + +#ifdef TF_CLIENT_DLL + const CEconTool_WrappedGift *pWrappedGiftTool = m_ToolSelectionItem.GetItemDefinition() + ? m_ToolSelectionItem.GetItemDefinition()->GetTypedEconTool<CEconTool_WrappedGift>() + : NULL; + + if ( pWrappedGiftTool && pWrappedGiftTool->BIsDirectGift() ) + { + DoUseConsumableItem(); + return; + } + else if ( pWrappedGiftTool && pWrappedGiftTool->BIsGlobalGift() ) + { + // If this is a global gift, we don't let the user pick a target so we're done as of now. + extern void UseUntargetedGiftConfirm( bool bConfirmed, void *pContext ); + CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#TF_DeliverGiftDialog_Title", "#TF_DeliverGiftDialog_Random_Text", + "#TF_DeliverGiftDialog_Confirm", "#TF_DeliverGiftDialog_Cancel", + &UseUntargetedGiftConfirm ); + + pDialog->SetContext( &m_ToolSelectionItem ); + return; + } + + DoGiftToPlayer(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Show +//----------------------------------------------------------------------------- +void CBackpackPanel::DoApplyByItem() +{ + SetupToolSelectionItem(); + + m_eSelectionMode = ToolSelection; + m_nLastToolPage = GetCurrentPage(); + + if ( m_pMouseOverTooltip ) + { + m_pMouseOverTooltip->HideTooltip(); + } + + ClearNameFilter( true ); + SetCurrentPage( 0 ); + UpdateModelPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: open shuffle items dialog +//----------------------------------------------------------------------------- +void CBackpackPanel::DoShuffle() +{ + SetupToolSelectionItem(); + + CInputStringForItemBackpackOverlayDialog *pDialog = vgui::SETUP_PANEL( new CInputStringForItemBackpackOverlayDialog( this, &m_ToolSelectionItem ) ); + if ( pDialog ) + { + pDialog->Show(); + } +} + +void CBackpackPanel::DoEditSlot() +{ + SetupToolSelectionItem(); + + // Launch new slot window if we need to + if( m_pItemSlotPanel == NULL ) + { + m_pItemSlotPanel = vgui::SETUP_PANEL( new CItemSlotPanel( this ) ); + } + + // Set item into panel + if ( m_pItemSlotPanel ) + { + m_pItemSlotPanel->SetVisible( true ); + m_pItemSlotPanel->SetItem( m_ToolSelectionItem.GetSOCData() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Refurbish item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoRefurbishItem() +{ + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + m_ComboBoxOverlaySelectionItem = *m_pItemModelPanels[i]->GetItem(); + break; + } + } + + CRefurbishItemDialog *pRefurbishDialog = vgui::SETUP_PANEL( new CRefurbishItemDialog( this, &m_ComboBoxOverlaySelectionItem ) ); + if ( pRefurbishDialog ) + { + pRefurbishDialog->Show(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Deode by item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoGetItemFromStore() +{ + SetupToolSelectionItem(); + + uint32 iDecoableItemDef = 0; + if ( GetDecodedByItemDefIndex( &m_ToolSelectionItem, &iDecoableItemDef ) ) + { + // casting to the proper type since our econ system is dumb + const float& value_as_float = (float&)iDecoableItemDef; + CEconItemDefinition * pDefIndex = GetItemSchema()->GetItemDefinition( (int)value_as_float ); + + EconUI()->GetStorePanel()->AddToCartAndCheckoutImmediately( pDefIndex->GetDefinitionIndex() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Open End of the Line duck leaderboards +//----------------------------------------------------------------------------- +void CBackpackPanel::DoOpenDuckLeaderboards() +{ + CCharacterInfoPanel* pCharInfo = dynamic_cast< CCharacterInfoPanel* >( EconUI() ); + CDucksLeaderboardManager *pDuckLeaderboards = vgui::SETUP_PANEL( new CDucksLeaderboardManager( pCharInfo, "DucksLeaderboardPanel" ) ); + pDuckLeaderboards->SetVisible( true ); +} + +//----------------------------------------------------------------------------- +// Strange Count Transfer Dialog +//----------------------------------------------------------------------------- +void CBackpackPanel::DoStrangeCountTransfer() +{ + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels( SELECT_FIRST, vecSelected ); + + Assert( vecSelected.Count() ); + if ( vecSelected.IsEmpty() ) + return; + + m_pStrangeToolPanel = vgui::SETUP_PANEL( new CStrangeCountTransferPanel( this, vecSelected[0]->GetItem() ) ); +} + +//----------------------------------------------------------------------------- +// Collection crafting +//----------------------------------------------------------------------------- +void CBackpackPanel::DoCraftUpCollection() +{ + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels( SELECT_ALL, vecSelected ); + + //Assert( vecSelected.Count() ); + //if ( vecSelected.IsEmpty() ) + // return; + + // Get all the items that were selected + CUtlVector< const CEconItemView* > vecSelectedItems; + FOR_EACH_VEC( vecSelected, i ) + { + if ( vecSelected[ i ]->GetItem() && GetCollectionCraftingInvalidReason( vecSelected[ i ]->GetItem(), NULL ) == NULL ) + { + vecSelectedItems.AddToTail( vecSelected[ i ]->GetItem() ); + } + } + + // For tracking how many times they've opened this menu + tf_trade_up_use_count.SetValue( tf_trade_up_use_count.GetInt() - 1 ); + + // Open it up! + GetCollectionCraftPanel()->Show( vecSelectedItems ); +} + +//----------------------------------------------------------------------------- +// Collection crafting +//----------------------------------------------------------------------------- +void CBackpackPanel::DoHalloweenOffering() +{ + // Open it up! + if ( !m_pHalloweenOfferingPanel ) + { + m_pHalloweenOfferingPanel = vgui::SETUP_PANEL( new CHalloweenOfferingPanel( this, m_pMouseOverTooltip ) ); + m_pHalloweenOfferingPanel->InvalidateLayout( true, true ); + } + // empty + CUtlVector< const CEconItemView* > vecSelectedItems; + m_pHalloweenOfferingPanel->Show( vecSelectedItems ); +} + +//----------------------------------------------------------------------------- +// Craft Common StatClock +//----------------------------------------------------------------------------- +void CBackpackPanel::DoCraftCommonStatClock() +{ + // Open it up! + if ( !m_pMannCoTradePanel ) + { + m_pMannCoTradePanel = vgui::SETUP_PANEL( new CCraftCommonStatClockPanel( this, m_pMouseOverTooltip ) ); // make this more generic + m_pMannCoTradePanel->InvalidateLayout( true, true ); + } + + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels(SELECT_ALL, vecSelected); + + CUtlVector< const CEconItemView* > vecSelectedItems; + FOR_EACH_VEC(vecSelected, i) + { + if (vecSelected[i]->GetItem() && GetCraftCommonStatClockInvalidReason(vecSelected[i]->GetItem(), NULL) == NULL) + { + vecSelectedItems.AddToTail(vecSelected[i]->GetItem()); + } + } + + m_pMannCoTradePanel->Show( vecSelectedItems ); +} + +//----------------------------------------------------------------------------- +// Purpose: Bring up the 3D inspect panel for the selected item +//----------------------------------------------------------------------------- +void CBackpackPanel::DoInspectModel() +{ + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels( SELECT_FIRST, vecSelected ); + + if ( vecSelected.IsEmpty() ) + return; + + CEconItemView *pItem = vecSelected[0]->GetItem(); + if ( pItem ) + { + float flInspect = 0; + static CSchemaAttributeDefHandle pAttrib_WeaponAllowInspect( "weapon_allow_inspect" ); + if ( ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttrib_WeaponAllowInspect, &flInspect ) && flInspect != 0.f ) +#ifdef STAGING_ONLY + || tf_weapon_force_allow_inspect.GetBool() +#endif + ) + { + m_pInspectPanel->SetVisible( true ); + m_pInspectPanel->SetItemCopy( vecSelected[0]->GetItem() ); + } + else + { + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; ++iClass ) + { + if ( pItem->GetStaticData()->CanBeUsedByClass( iClass ) ) + { + m_pInspectCosmeticPanel->PreviewItem( iClass, pItem ); + break; + } + } + m_pInspectCosmeticPanel->SetVisible( true ); + } + } +} +//----------------------------------------------------------------------------- +void CBackpackPanel::OpenInspectModelPanelAndCopyItem( CEconItemView *pItemView ) +{ + if ( !pItemView ) + return; + + EconUI()->OpenEconUI( ECONUI_BACKPACK ); + + // Figure out which preview to show + float flInspect = 0; + static CSchemaAttributeDefHandle pAttrib_WeaponAllowInspect( "weapon_allow_inspect" ); + if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItemView, pAttrib_WeaponAllowInspect, &flInspect ) && flInspect != 0.f ) + { + m_pInspectPanel->SetVisible( true ); + m_pInspectPanel->SetItemCopy( pItemView ); + } + else + { + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; ++iClass ) + { + if ( pItemView->GetStaticData()->CanBeUsedByClass( iClass ) ) + { + m_pInspectCosmeticPanel->PreviewItemCopy( iClass, pItemView ); + m_pInspectCosmeticPanel->SetVisible( true ); + break; + } + } + } +} + +CCollectionCraftingPanel* CBackpackPanel::GetCollectionCraftPanel() +{ + if ( !m_pCollectionCraftPanel ) + { + m_pCollectionCraftPanel = vgui::SETUP_PANEL( new CCollectionCraftingPanel( this, m_pMouseOverTooltip ) ); + m_pCollectionCraftPanel->InvalidateLayout( true, true ); + } + + return m_pCollectionCraftPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Buy a key, and then immediately use it on the selected crate once +// tbe store transaction completes +//----------------------------------------------------------------------------- +void CBackpackPanel::DoBuyKeyAndOpenCrate() +{ + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels( SELECT_FIRST, vecSelected ); + + if ( vecSelected.IsEmpty() ) + return; + + m_hQuickOpenCrate.SetItem( vecSelected.Head()->GetItem() ); + + uint32 iDecoableItemDef = 0; + if ( GetDecodedByItemDefIndex( m_hQuickOpenCrate, &iDecoableItemDef ) ) + { + // casting to the proper type since our econ system is dumb + const float& value_as_float = (float&)iDecoableItemDef; + CEconItemDefinition * pDefIndex = GetItemSchema()->GetItemDefinition( (int)value_as_float ); + + EconUI()->GetStorePanel()->AddToCartAndCheckoutImmediately( pDefIndex->GetDefinitionIndex() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Find the first compatible key in our inventory, and use it on the +// selected crate +//----------------------------------------------------------------------------- +void CBackpackPanel::DoOpenCrateWithKey() +{ + CUtlVector< CItemModelPanel* > vecSelected; + GetSelectedPanels( SELECT_FIRST, vecSelected ); + + if ( vecSelected.IsEmpty() ) + return; + + CEconItemView *pCrate = vecSelected.Head()->GetItem(); + + CEconItemView *pKey = GetFirstCompatibleKeyForCrate( pCrate ); + if ( !pKey ) + return; + + ApplyTool( this, pKey, pCrate ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Open up the loadout for a class +//----------------------------------------------------------------------------- +void CBackpackPanel::DoEquipForClass( int nClass ) +{ + // Negative because reasons + EconUI()->OpenEconUI( -nClass ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given a paint can index, offer to use one or buy one +//----------------------------------------------------------------------------- +void CBackpackPanel::DoPaint( int nPaintItemIndex, bool bUseStore, bool bUseMarket ) +{ + if ( !bUseStore && !bUseMarket ) + { + AttemptToUseItem( nPaintItemIndex ); + } + + if ( bUseStore ) + { + AttemptToShowItemInStore( nPaintItemIndex ); + } + else if ( bUseMarket ) + { + AttemptToShowItemInMarket( nPaintItemIndex ); + } +} +//----------------------------------------------------------------------------- +// Purpose: Given a strange part index, offer to use one or buy one (Market) +//----------------------------------------------------------------------------- +void CBackpackPanel::DoStrangePart( int nStrangePartIndex, bool bUseMarket ) +{ + if ( !bUseMarket ) + { + AttemptToUseItem( nStrangePartIndex ); + } + + if ( bUseMarket ) + { + AttemptToShowItemInMarket( nStrangePartIndex ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Try to find the first item with the passed-in item name in our inventory +// and try to use it on the selected panel's item. If we dont have an item +// matching the name passed in, go to the store and prompt the user to buy one. +//----------------------------------------------------------------------------- +bool CBackpackPanel::AttemptToUseItem( item_definition_index_t iItemDefIndex ) +{ + CUtlVector< CItemModelPanel* > m_vecSelected; + GetSelectedPanels( SELECT_FIRST, m_vecSelected ); + Assert( m_vecSelected.Count() ); + if ( !m_vecSelected.Count() ) + return false; + + CEconItemView *pSelectedItem = m_vecSelected.Head()->GetItem(); + Assert( pSelectedItem ); + if ( !pSelectedItem ) + return false; + + CEconItemView *pNameTag = CTFPlayerInventory::GetFirstItemOfItemDef( iItemDefIndex ); + if ( pNameTag ) + { + if ( ApplyTool( this, pNameTag, pSelectedItem ) ) + { + CancelToolSelection(); + UpdateModelPanels(); + } + return true; + } + return false; +} +//----------------------------------------------------------------------------- +void CBackpackPanel::AttemptToShowItemInStore( item_definition_index_t iItemDefIndex ) +{ + CUtlVector< CItemModelPanel* > m_vecSelected; + GetSelectedPanels( SELECT_FIRST, m_vecSelected ); + Assert( m_vecSelected.Count() ); + if( !m_vecSelected.Count() ) + return; + + CEconItemView *pSelectedItem = m_vecSelected.Head()->GetItem(); + Assert( pSelectedItem ); + if ( !pSelectedItem ) + return; + + EconUI()->OpenStorePanel( iItemDefIndex, false ); +} +//----------------------------------------------------------------------------- +void CBackpackPanel::AttemptToShowItemInMarket( item_definition_index_t iItemDefIndex ) +{ + CUtlVector< CItemModelPanel* > m_vecSelected; + GetSelectedPanels( SELECT_FIRST, m_vecSelected ); + Assert( m_vecSelected.Count() ); + if ( !m_vecSelected.Count() ) + return; + + CEconItemView *pSelectedItem = m_vecSelected.Head()->GetItem(); + Assert( pSelectedItem ); + if ( !pSelectedItem ) + return; + + CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iItemDefIndex ); + Assert( pItemDef ); + if ( !pItemDef ) + return; + + if ( !CBaseAdPanel::CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) ) + return; + + if ( pItemDef && steamapicontext && steamapicontext->SteamFriends() ) + { + const char *pszPrefix = ""; + if ( GetUniverse() == k_EUniverseBeta ) + { + pszPrefix = "beta."; + } + + static char pszItemName[256]; + g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ), pszItemName, sizeof( pszItemName ) ); + + char szURL[512]; + V_snprintf( szURL, sizeof( szURL ), "http://%ssteamcommunity.com/market/listings/%d/%s", pszPrefix, engine->GetAppID(), pszItemName ); + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL ); + } +} +//----------------------------------------------------------------------------- +// Purpose: Get the first, or all selected item model panels +//----------------------------------------------------------------------------- +void CBackpackPanel::GetSelectedPanels( ESelection eSelection, CUtlVector< CItemModelPanel* >& m_vecSelected ) const +{ + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + m_vecSelected.AddToTail( m_pItemModelPanels[i] ); + + if ( eSelection == SELECT_FIRST ) + { + return; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OnCommand( const char *command ) +{ + if ( V_strncasecmp( command, "goto_page_", V_strlen( "goto_page_" ) ) == 0 ) + { + int iPage = V_atoi( &command[ V_strlen( "goto_page_" ) ] ); + SetCurrentPage( iPage ); + UpdateModelPanels(); + return; + } + else if ( !Q_strnicmp( command, "nextpage", 8 ) ) + { + if ( !m_bDragging ) + { + DeSelectAllBackpackItemPanels(); + } + + SetCurrentPage( GetCurrentPage() + 1 ); + UpdateModelPanels(); + return; + } + else if ( !Q_strnicmp( command, "prevpage", 8 ) ) + { + if ( !m_bDragging ) + { + DeSelectAllBackpackItemPanels(); + } + + SetCurrentPage( GetCurrentPage() - 1 ); + UpdateModelPanels(); + return; + } + else if ( !Q_strnicmp( command, "useitem", 7 ) ) + { + AssertMsg( 0, "Everything should be going through the context menu. Fix the calling code." ); + return; + } + else if ( !Q_strnicmp( command, "showbackpackitems", 17 ) ) + { + SetShowBaseItems( false ); + if ( m_pShowBaseItemsCheckbox ) + { + m_pShowBaseItemsCheckbox->SetSelected( false ); + } + return; + } + else if ( !Q_strnicmp( command, "showbaseitems", 13 ) ) + { + SetShowBaseItems( true ); + if ( m_pShowBaseItemsCheckbox ) + { + m_pShowBaseItemsCheckbox->SetSelected( true ); + } + return; + } + else if ( !Q_strnicmp( command, "canceltool", 10 ) ) + { + CancelToolSelection(); + UpdateModelPanels(); + return; + } + else if ( !Q_stricmp( command, "show_explanations" ) ) + { + if ( !m_flStartExplanationsAt ) + { + m_flStartExplanationsAt = Plat_FloatTime(); + vgui::ivgui()->AddTickSignal( GetVPanel() ); + } + RequestFocus(); + } + else if ( !Q_stricmp( command, "showdetails" ) ) + { + for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) + { + if ( m_pItemModelPanels[i]->IsSelected() && m_pItemModelPanels[i]->HasItem() ) + { + OpenArmory( m_pItemModelPanels[i]->GetItem() ); + break; + } + } + UpdateModelPanels(); + return; + } + else if ( !V_strnicmp( command, "equipclass", 10 ) ) + { + int nClass = atoi( command + 10 ); + DoEquipForClass( nClass ); + } + else if ( !V_strnicmp( command, "paint", 5 ) ) + { + int nIndex = atoi( command + 5 ); + DoPaint( nIndex, false, false ); + } + else if ( !V_strnicmp( command, "market_paint", 12 ) ) + { + int nIndex = atoi( command + 12 ); + DoPaint( nIndex, false, true ); + } + else if ( !V_strnicmp( command, "store_paint", 11 ) ) + { + int nIndex = atoi( command + 11 ); + DoPaint( nIndex, true, false ); + } + else if ( !V_strnicmp( command, "strangepart_", 12 ) ) + { + int nIndex = atoi( command + 12 ); + DoStrangePart( nIndex, false ); + } + else if ( !V_strnicmp( command, "market_strangepart_", 19 ) ) + { + int nIndex = atoi( command + 19 ); + DoStrangePart( nIndex, true ); + } + else if ( !V_strnicmp( command, "Context_CraftUpCollection", 25 ) ) + { + DoCraftUpCollection(); + } + else if ( !V_strnicmp( command, "Context_CraftCommonStatClock", 25 ) ) + { + DoCraftCommonStatClock(); + } +#ifdef STAGING_ONLY + else if ( !V_strnicmp( command, "unpin", 5 ) ) + { + m_pMouseOverCardPanel->PinCard( false ); + } +#endif + + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::OpenArmory( CEconItemView* item ) +{ + PostMessage( GetParent()->GetParent()->GetParent(), new KeyValues("OpenArmoryDirect", "itemdef", item->GetItemDefIndex() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::CancelToolSelection( void ) +{ + if ( m_eSelectionMode == StandardSelection ) + return; + + m_eSelectionMode = StandardSelection; + + ClearNameFilter( false ); + + OnCommand( "showbackpackitems" ); + + SetCurrentPage( m_nLastToolPage ); + m_nLastToolPage = 0; + + m_ToolSelectionItem.Invalidate(); + if ( m_pToolIcon ) + { + m_pToolIcon->SetVisible( false ); + } + DeSelectAllBackpackItemPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::SetShowBaseItems( bool bShow ) +{ + bool bGoToFirstPage = m_bShowBaseItems != bShow; + m_bShowBaseItems = bShow; + if( bGoToFirstPage ) + { + SetCurrentPage( 0 ); + } + + DeSelectAllBackpackItemPanels(); + if ( m_pToolIcon ) + { + m_pToolIcon->SetVisible( false ); + } + UpdateModelPanels(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ConVar *CBackpackPanel::GetExplanationConVar( void ) +{ + return &tf_explanations_backpackpanel; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBackpackPanel::SetCurrentPage( int nNewPage ) +{ + if( m_pToolIcon ) + { + m_pToolIcon->SetVisible( false ); + } + + if ( nNewPage < 0 ) + { + nNewPage = GetNumPages() - 1; + } + else if ( nNewPage >= GetNumPages() ) + { + nNewPage = 0; + } + + // deselect old page button + if ( m_Pages.Count() > GetCurrentPage() && m_Pages[GetCurrentPage()] ) + { + CExButton* pButton = dynamic_cast<CExButton*>( m_Pages[GetCurrentPage()]->FindChildByName( "Button" ) ); + if ( pButton ) + { + pButton->SetSelected( false ); + } + } + + BaseClass::SetCurrentPage( nNewPage ); + + // mark new page button as selected + if ( m_Pages.Count() > GetCurrentPage() && m_Pages[GetCurrentPage()] ) + { + CExButton* pButton = dynamic_cast<CExButton*>( m_Pages[GetCurrentPage()]->FindChildByName( "Button" ) ); + if ( pButton ) + { + pButton->SetSelected( true ); + } + } +} + + +int CBackpackPanel::GetItemQualityForBorder( CItemModelPanel* pItemPanel ) const +{ + if ( pItemPanel->HasItem() && ( cl_showbackpackrarities.GetInt() > 0 || m_bForceShowBackpackRarities ) + && ( cl_showbackpackrarities.GetInt() < 2 || pItemPanel->GetItem()->IsMarketable() ) ) + { + uint8 nRarity = pItemPanel->GetItem()->GetItemDefinition()->GetRarity(); + if ( ( nRarity != k_unItemRarity_Any ) && ( pItemPanel->GetItem()->GetItemQuality() != AE_SELFMADE ) ) + { + // translate this quality to rarity + return nRarity + AE_RARITY_DEFAULT; + } + + return pItemPanel->GetItem()->GetItemQuality(); + } + + return 0; +} |