Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1777 lines
64 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "cbase.h"
  3. #include "game_item_schema.h"
  4. #include "econ_item_interface.h"
  5. #include "econ_item_tools.h"
  6. #include "econ_item_constants.h"
  7. #include "econ_dynamic_recipe.h"
  8. #include "schemainitutils.h"
  9. bool BStringsEqual( const char *pszA, const char *pszB )
  10. {
  11. if ( pszA == NULL )
  12. return pszB == NULL;
  13. if ( pszB == NULL )
  14. return false;
  15. return !V_strcmp( pszA, pszB );
  16. }
  17. const unsigned int g_CapabilityApplicationMap[] =
  18. {
  19. // if we have a tool that has this capability...
  20. // ...then we check to see if the tool target has
  21. // this capability
  22. ITEM_CAP_PAINTABLE, // ITEM_CAP_PAINTABLE
  23. ITEM_CAP_NAMEABLE, // ITEM_CAP_NAMEABLE
  24. ITEM_CAP_DECODABLE, // ITEM_CAP_DECODABLE
  25. 0, // ITEM_CAP_UNUSED; was ITEM_CAP_CAN_MOD_SOCKET
  26. ITEM_CAP_CAN_CUSTOMIZE_TEXTURE, // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
  27. 0, // ITEM_CAP_USABLE
  28. 0, // ITEM_CAP_USABLE_GC
  29. ITEM_CAP_CAN_GIFT_WRAP, // ITEM_CAP_CAN_GIFT_WRAP
  30. 0, // ITEM_CAP_USABLE_OUT_OF_GAME
  31. ITEM_CAP_CAN_COLLECT, // ITEM_CAP_CAN_COLLECT
  32. 0, // ITEM_CAP_CAN_CRAFT_COUNT
  33. 0, // ITEM_CAP_CAN_CRAFT_MARK
  34. ITEM_CAP_PAINTABLE_TEAM_COLORS | ITEM_CAP_PAINTABLE, // ITEM_CAP_PAINTABLE_TEAM_COLORS
  35. 0, // ITEM_CAP_CAN_BE_RESTORED
  36. ITEM_CAP_CAN_USE_STRANGE_PARTS, // ITEM_CAP_CAN_USE_STRANGE_PARTS
  37. ITEM_CAP_CAN_CARD_UPGRADE, // ITEM_CAP_CAN_CARD_UPGRADE
  38. ITEM_CAP_CAN_STRANGIFY, // ITEM_CAP_CAN_STRANGIFY
  39. ITEM_CAP_CAN_KILLSTREAKIFY, // ITEM_CAP_CAN_KILLSTREAKIFY
  40. ITEM_CAP_CAN_CONSUME, // ITEM_CAP_CAN_CONSUME
  41. ITEM_CAP_CAN_SPELLBOOK_PAGE, // ITEM_CAP_CAN_SPELLBOOK_PAGE
  42. ITEM_CAP_HAS_SLOTS, // ITEM_CAP_HAS_SLOTS
  43. ITEM_CAP_DUCK_UPGRADABLE, // ITEM_CAP_DUCK_UPGRADABLE
  44. ITEM_CAP_CAN_UNUSUALIFY, // ITEM_CAP_CAN_UNUSUALIFY
  45. };
  46. COMPILE_TIME_ASSERT( ARRAYSIZE( g_CapabilityApplicationMap ) == NUM_ITEM_CAPS );
  47. //---------------------------------------------------------------------------------------
  48. // Purpose:
  49. //---------------------------------------------------------------------------------------
  50. bool IEconTool::ShouldDisplayQuantity( const IEconItemInterface *pTool ) const
  51. {
  52. Assert( pTool );
  53. const GameItemDefinition_t *pItemDef = pTool->GetItemDefinition();
  54. if ( !pItemDef )
  55. return false;
  56. static CSchemaAttributeDefHandle pAttrDef_UnlimitedQuantity( "unlimited quantity" );
  57. if ( pTool->FindAttribute( pAttrDef_UnlimitedQuantity ) )
  58. return false;
  59. if ( pTool->GetQuantity() >= 0 )
  60. {
  61. if ( (pItemDef->GetCapabilities() & ITEM_CAP_USABLE_GC) != 0 )
  62. return true;
  63. if ( pItemDef->IsTool() )
  64. return true;
  65. }
  66. return false;
  67. }
  68. //---------------------------------------------------------------------------------------
  69. // Purpose:
  70. //---------------------------------------------------------------------------------------
  71. CEconTool_WrappedGift::CEconTool_WrappedGift( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  72. : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
  73. , m_pszDeliveredGiftItemDefName( NULL )
  74. , m_pDeliveredGiftItemDef( NULL )
  75. , m_bIsGlobalGift( false )
  76. , m_bIsDirectGift( false )
  77. {
  78. if ( pUsageKV )
  79. {
  80. m_bIsGlobalGift = pUsageKV->GetBool( "target_type_global", false );
  81. m_pszDeliveredGiftItemDefName = pUsageKV->GetString( "delivered_gift_item_def", NULL );
  82. m_bIsDirectGift = pUsageKV->GetBool( "is_direct_gift", false );
  83. }
  84. }
  85. //---------------------------------------------------------------------------------------
  86. // Purpose:
  87. //---------------------------------------------------------------------------------------
  88. bool CEconTool_WrappedGift::BFinishInitialization()
  89. {
  90. // Now that we've finished parsing our definitions, look for a match.
  91. if ( m_pszDeliveredGiftItemDefName )
  92. {
  93. m_pDeliveredGiftItemDef = GetItemSchema()->GetItemDefinitionByName( m_pszDeliveredGiftItemDefName );
  94. }
  95. // We're done with this value.
  96. m_pszDeliveredGiftItemDefName = NULL;
  97. return IEconTool::BFinishInitialization();
  98. }
  99. //---------------------------------------------------------------------------------------
  100. // Purpose:
  101. //---------------------------------------------------------------------------------------
  102. CEconTool_GiftWrap::CEconTool_GiftWrap( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  103. : IEconTool( pszTypeName, NULL, NULL, unCapabilities )
  104. , m_pszWrappedGiftItemDefName( NULL )
  105. , m_pWrappedGiftItemDef( NULL )
  106. {
  107. if ( pUsageKV )
  108. {
  109. m_pszWrappedGiftItemDefName = pUsageKV->GetString( "wrapped_gift_item_def", NULL );
  110. }
  111. }
  112. //---------------------------------------------------------------------------------------
  113. // Purpose:
  114. //---------------------------------------------------------------------------------------
  115. bool CEconTool_GiftWrap::BFinishInitialization()
  116. {
  117. // Now that we've finished parsing our definitions, look for a match.
  118. if ( m_pszWrappedGiftItemDefName )
  119. {
  120. m_pWrappedGiftItemDef = GetItemSchema()->GetItemDefinitionByName( m_pszWrappedGiftItemDefName );
  121. }
  122. // We're done with this value.
  123. m_pszWrappedGiftItemDefName = NULL;
  124. return m_pWrappedGiftItemDef != NULL
  125. && IEconTool::BFinishInitialization();
  126. }
  127. //---------------------------------------------------------------------------------------
  128. // Purpose:
  129. //---------------------------------------------------------------------------------------
  130. bool CEconTool_GiftWrap::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  131. {
  132. Assert( pTool );
  133. Assert( pToolSubject );
  134. if ( pToolSubject->GetQuality() == AE_SELFMADE ||
  135. pToolSubject->GetQuality() == AE_COMMUNITY ||
  136. pToolSubject->GetQuality() == AE_CUSTOMIZED ||
  137. pToolSubject->GetQuality() == AE_NORMAL )
  138. {
  139. return false;
  140. }
  141. // If an item is currently trade restricted, other flags don't matter (I think).
  142. if ( ( pToolSubject->GetUntradabilityFlags() & k_Untradability_Temporary ) != 0 )
  143. return false;
  144. // One item still has the gift wrap cap, which is the engagement ring ( Something Special For Someone Special (Tool) ).
  145. // However, the can_gift_wrap cap shouldn't overcome temporary trade restrictions.
  146. static CSchemaAttributeDefHandle pAttrDef_ToolNeedsGiftwrap( "tool needs giftwrap" );
  147. const bool cbSubjectNeedsGiftWrap = pToolSubject->FindAttribute( pAttrDef_ToolNeedsGiftwrap );
  148. const bool cbSubjectCanTrade = pToolSubject->IsTradable();
  149. const bool cbSubjectCanProceed = cbSubjectNeedsGiftWrap || cbSubjectCanTrade;
  150. if ( !cbSubjectCanProceed )
  151. return false;
  152. static CSchemaAttributeDefHandle pAttrDef_CannotGiftwrap( "cannot giftwrap" );
  153. if ( pToolSubject->FindAttribute( pAttrDef_CannotGiftwrap ) )
  154. return false;
  155. return IEconTool::CanApplyTo( pTool, pToolSubject );
  156. }
  157. CEconTool_StrangeCountTransfer::CEconTool_StrangeCountTransfer( const char *pszTypeName, item_capabilities_t unCapabilities )
  158. : IEconTool( pszTypeName, NULL, NULL, unCapabilities )
  159. {
  160. #ifdef CLIENT_DLL
  161. m_pItemSrc = NULL;
  162. m_pItemDest = NULL;
  163. #endif // CLIENT_DLL
  164. }
  165. bool CEconTool_StrangeCountTransfer::AreItemsEligibleForStrangeCountTransfer( const IEconItemInterface *pItem1, const IEconItemInterface *pItem2 )
  166. {
  167. if ( !pItem1 || !pItem2 )
  168. return false;
  169. const char *pItem1Xifier = pItem1->GetItemDefinition()->GetXifierRemapClass();
  170. const char *pItem2Xifier = pItem2->GetItemDefinition()->GetXifierRemapClass();
  171. // if no xifier class, check if the item defs are the same
  172. if ( !pItem1Xifier || !pItem2Xifier )
  173. {
  174. if ( pItem1->GetItemDefinition() != pItem2->GetItemDefinition() )
  175. {
  176. return false;
  177. }
  178. }
  179. else if ( V_stricmp( pItem1Xifier, pItem2Xifier ) != 0 )
  180. {
  181. return false;
  182. }
  183. // Item defs are compatabible, are there attributes? Check strange
  184. // Check if they are both strange (have kill eater). Quality is less important
  185. if ( !BIsItemStrange( pItem1) || !BIsItemStrange( pItem2 ) )
  186. return false;
  187. return true;
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose:
  191. //-----------------------------------------------------------------------------
  192. bool CEconTool_StrangePart::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  193. {
  194. Assert( pTool );
  195. Assert( pToolSubject );
  196. // Abort if for some reason we don't know what type of attribute we're trying to track.
  197. static CSchemaAttributeDefHandle pAttrDef_StrangePartCounterID( "strange part new counter ID" );
  198. float flNewScoreType;
  199. if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pTool, pAttrDef_StrangePartCounterID, &flNewScoreType ) )
  200. return false;
  201. // Make sure we're "strange" in that we have at least one attribute tracking scores. We can't
  202. // explicitly test for quality here because now we can have strange vintages, etc.
  203. if ( GetKillEaterAttrCount() <= 0 )
  204. return false;
  205. if ( !pToolSubject->FindAttribute( GetKillEaterAttr_Score( 0 ) ) )
  206. return false;
  207. // Make sure the target item doesn't already have the property this tool is trying to
  208. // apply, unless that counter is restricted, in which case we allow a second stat to be
  209. // added with the same value.
  210. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  211. {
  212. float flScoreType;
  213. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pToolSubject, GetKillEaterAttr_Type( i ), &flScoreType ) && // if we have a counter in this slot...
  214. RoundFloatToInt( flScoreType ) == RoundFloatToInt( flNewScoreType ) && // ...and it's counting the same thing...
  215. !pToolSubject->FindAttribute( GetKillEaterAttr_Restriction( i ) ) ) // ...and that counter isn't restricted
  216. {
  217. return false;
  218. }
  219. }
  220. // Make sure we have at least one empty customizable attribute slot.
  221. bool bFoundEmptyAttributeSlot = false;
  222. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  223. {
  224. // Ignore non-user-customizable attributes.
  225. if ( !GetKillEaterAttr_IsUserCustomizable( i ) )
  226. continue;
  227. // We expect to have both or neither of a user-customizable attribute. If this isn't the
  228. // case the later logic will be wrong.
  229. const bool bFoundTypeAttribute = pToolSubject->FindAttribute( GetKillEaterAttr_Type( i ) );
  230. Assert( bFoundTypeAttribute == pToolSubject->FindAttribute( GetKillEaterAttr_Score( i ) ) );
  231. if ( !bFoundTypeAttribute )
  232. {
  233. bFoundEmptyAttributeSlot = true;
  234. break;
  235. }
  236. }
  237. if ( !bFoundEmptyAttributeSlot )
  238. return false;
  239. // Check to make sure the definition of the item we're trying to apply to has the tags we need
  240. // to match and doesn't have any tags that we need to avoid. We use these to avoid tracking
  241. // damage done for a medigun, etc.
  242. const GameItemDefinition_t *pSubjectItemDef = pToolSubject->GetItemDefinition();
  243. if ( !pSubjectItemDef )
  244. return false;
  245. #if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
  246. // Strange Cosmetics can take on any part
  247. if ( pSubjectItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_MISC )
  248. {
  249. return true;
  250. }
  251. #endif
  252. FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
  253. {
  254. if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
  255. return false;
  256. }
  257. FOR_EACH_VEC( m_RequiredMissingTags.GetTagsList(), i )
  258. {
  259. if ( pSubjectItemDef->HasEconTag( m_RequiredMissingTags.GetTagsList()[i] ) )
  260. return false;
  261. }
  262. return IEconTool::CanApplyTo( pTool, pToolSubject );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. static const char *s_pszStrangeRestrictionTypes[] =
  268. {
  269. "", // kStrangeEventRestriction_None
  270. "victim_account_id", // kStrangeEventRestriction_VictimSteamAccount
  271. #if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
  272. "map", // kStrangeEventRestriction_Map
  273. "competitive", // kStrangeEventRestriction_Competitive
  274. #endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
  275. };
  276. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszStrangeRestrictionTypes ) == kStrangeEventRestrictionCount );
  277. CEconTool_StrangePartRestriction::CEconTool_StrangePartRestriction( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  278. : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
  279. , m_eRestrictionType( kStrangeEventRestriction_None ) // default-initialize to failure
  280. , m_unRestrictionValue( (unsigned int)-1 )
  281. {
  282. Assert( pUsageKV != NULL );
  283. // Parse our restriction type from a string. Anything invalid here will be handled below in the IsValid()
  284. // check.
  285. const int iRestrictionType = StringFieldToInt( pUsageKV->GetString( "restriction_type", "" ), &s_pszStrangeRestrictionTypes[0], ARRAYSIZE( s_pszStrangeRestrictionTypes ) );
  286. if ( iRestrictionType > 0 )
  287. {
  288. m_eRestrictionType = (strange_event_restriction_t)iRestrictionType;
  289. }
  290. // We'll use this value later from inside BFinishInitialization(). We may have to look at other
  291. // values from parts of the schema that haven't been parsed yet, like map or item names.
  292. m_pszRestrictionValue = pUsageKV->GetString( "restriction_value", NULL );
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose:
  296. //-----------------------------------------------------------------------------
  297. bool CEconTool_StrangePartRestriction::BFinishInitialization()
  298. {
  299. // Run different parsers based on our restriction type.
  300. switch ( m_eRestrictionType )
  301. {
  302. // We don't expect anything for our sub-value when dealing with Steam accounts. Asserting based on
  303. // bad data is dumb but we don't really have any way of sending an error all the way up the tree.
  304. case kStrangeEventRestriction_None:
  305. case kStrangeEventRestriction_VictimSteamAccount:
  306. if ( m_pszRestrictionValue )
  307. return false;
  308. m_unRestrictionValue = 0;
  309. break;
  310. #if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
  311. case kStrangeEventRestriction_Map:
  312. {
  313. if ( !m_pszRestrictionValue )
  314. return false;
  315. const MapDef_t *pSchemaMap = GetItemSchema()->GetMasterMapDefByName( m_pszRestrictionValue );
  316. if ( !pSchemaMap )
  317. return false;
  318. m_unRestrictionValue = pSchemaMap->m_nDefIndex;
  319. }
  320. break;
  321. case kStrangeEventRestriction_Competitive:
  322. {
  323. if (!m_pszRestrictionValue)
  324. return false;
  325. // Season string to int
  326. m_unRestrictionValue = V_atoi(m_pszRestrictionValue);
  327. }
  328. break;
  329. #endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
  330. default:
  331. AssertMsg1( false, "CEconTool_StrangePartRestriction() doesn't understand how to parse restriction type %i.", m_eRestrictionType );
  332. return false;
  333. }
  334. // We're done with this value now.
  335. m_pszRestrictionValue = NULL;
  336. return m_eRestrictionType != kStrangeEventRestriction_None
  337. && m_unRestrictionValue != (unsigned int)-1
  338. && IEconTool::BFinishInitialization();
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose:
  342. //-----------------------------------------------------------------------------
  343. bool CEconTool_StrangePartRestriction::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  344. {
  345. Assert( pTool );
  346. Assert( pTool->GetItemDefinition() );
  347. Assert( pToolSubject );
  348. if ( !IEconTool::CanApplyTo( pTool, pToolSubject ) )
  349. return false;
  350. // Abort if for some reason we don't know what type of restriction we're trying to apply.
  351. if ( m_eRestrictionType == kStrangeEventRestriction_None )
  352. return false;
  353. // Abort if we don't have our tool information. We'll need this below to avoid duplicating
  354. // scores/restrictions.
  355. const CEconTool_StrangePartRestriction *pToolRestriction = pTool->GetItemDefinition()->GetTypedEconTool<CEconTool_StrangePartRestriction>();
  356. if ( !pToolRestriction )
  357. return false;
  358. // Make sure we're "strange" in that we have at least one attribute tracking scores. We can't
  359. // explicitly test for quality here because now we can have strange vintages, etc.
  360. if ( GetKillEaterAttrCount() <= 0 )
  361. return false;
  362. if ( !pToolSubject->FindAttribute( GetKillEaterAttr_Score( 0 ) ) )
  363. return false;
  364. // Look through all of the strange attributes on this item. We're looking for a slot that
  365. // has a score that we're tracking where that score doesn't already have a restriction.
  366. // If we find one, we can restrict that particular slot as long as doing so wouldn't generate
  367. // a duplicate score (ie., "soldier kills on Hightower"). We don't need to enumerate them
  368. // here, just find existence/nonexistence of at least one valid target.
  369. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  370. {
  371. if ( GetItemSchema()->BCanStrangeFilterApplyToStrangeSlotInItem( pToolRestriction->GetRestrictionType(), pToolRestriction->GetRestrictionValue(), pToolSubject, i, NULL ) )
  372. return true;
  373. }
  374. return false;
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. //-----------------------------------------------------------------------------
  379. CEconTool_ItemDynamicRecipe::CEconTool_ItemDynamicRecipe( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  380. : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
  381. {
  382. COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( uint32 ) );
  383. COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( float ) );
  384. if ( pUsageKV )
  385. {
  386. BInitFromKV( pUsageKV, &m_vecErrors );
  387. }
  388. }
  389. const char* CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::m_pszUseParentNameIdentifier = "use_parents_item_def";
  390. //-----------------------------------------------------------------------------
  391. // Purpose: Make sure each of our components are fully formed. Return false
  392. // if any components fail BFinishInitialization_Internal()
  393. //-----------------------------------------------------------------------------
  394. bool CEconTool_ItemDynamicRecipe::BFinishInitialization()
  395. {
  396. CBaseRecipeComponent::ComponentAttribVector_t attribVec;
  397. FOR_EACH_VEC( m_vecComponents, i )
  398. {
  399. m_vecComponents[i]->BFinishInitialization_Internal( &m_vecErrors, &attribVec );
  400. }
  401. // Emit any errors we've accumulated during initialization
  402. FOR_EACH_VEC( m_vecErrors, i )
  403. {
  404. #ifdef GC_DLL
  405. EmitError( SPEW_GC, "%s\n", m_vecErrors[i].Get() );
  406. #else
  407. AssertMsg1( 0, "%s\n", m_vecErrors[i].Get() );
  408. #endif
  409. }
  410. if( m_vecErrors.Count() > 0 )
  411. return false;
  412. // Make sure we have at least one item required
  413. return IEconTool::BFinishInitialization();
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: Iterate through attributes on pTool to see if any can accept pToolSubject.
  417. // Return true if any attributes match.
  418. //-----------------------------------------------------------------------------
  419. bool CEconTool_ItemDynamicRecipe::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  420. {
  421. Assert( pTool );
  422. Assert( pToolSubject );
  423. // Iterate through all the attributes on the tool and see if the subject item matches
  424. // any of the recipe component attributes
  425. CRecipeComponentMatchingIterator matchingIterator( pTool, pToolSubject );
  426. pTool->IterateAttributes( &matchingIterator );
  427. const CUtlVector< const CEconItemAttributeDefinition* >& vecMatchingAttribs = matchingIterator.GetMatchingComponentInputs();
  428. // No matches, can't apply!
  429. if( vecMatchingAttribs.Count() == 0 )
  430. {
  431. return false;
  432. }
  433. return true;
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose:
  437. //-----------------------------------------------------------------------------
  438. CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::CBaseRecipeComponent( bool bIsOutput, const CBaseRecipeComponent* pParent )
  439. : m_bIsOutput( bIsOutput )
  440. , m_flChanceOfApplying( 1.f )
  441. , m_pParent( pParent )
  442. , m_flTotalWeights( 0.f )
  443. , m_eQuality( AE_UNDEFINED )
  444. , m_attributesMatchingType( ATTRIBUTES_MATCH_NONE )
  445. {}
  446. //-----------------------------------------------------------------------------
  447. // Purpose:
  448. //-----------------------------------------------------------------------------
  449. CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::~CBaseRecipeComponent()
  450. {
  451. m_vecAdditionalComponents.PurgeAndDeleteElements();
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose: Roll chance of component applying to the tool
  455. //-----------------------------------------------------------------------------
  456. #ifdef GC_DLL
  457. int CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::RollCount() const
  458. {
  459. if( m_vecCountChances.Count() == 0 )
  460. return 1;
  461. float flRand = RandomFloat( 0.f, 1.f ) * m_flTotalWeights;
  462. float flAccum = 0.f;
  463. // Go through and see which counts gets rolled
  464. FOR_EACH_VEC( m_vecCountChances, i )
  465. {
  466. const CountChance_t& countChance = m_vecCountChances[i];
  467. flAccum += countChance.m_flChance;
  468. if ( flRand <= flAccum )
  469. {
  470. // Winner! Roll within its range
  471. return RandomInt( countChance.m_nMinCount, countChance.m_nMaxCount );
  472. }
  473. }
  474. AssertMsg( 0, "Failed to generate a count for recipe component. Defaulting to 1" );
  475. return 1;
  476. }
  477. bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::RollChanceOfApplying() const
  478. {
  479. // Guaranteed!
  480. if( m_flChanceOfApplying == 1.f )
  481. return true;
  482. return RandomFloat() < m_flChanceOfApplying;
  483. }
  484. #endif
  485. //-----------------------------------------------------------------------------
  486. // Purpose: Set chance for attributes to apply.
  487. //-----------------------------------------------------------------------------
  488. void CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::SetChanceOfApplying( float flChance )
  489. {
  490. Assert( flChance >= 0.f && flChance <= 1.f );
  491. clamp( flChance, 0.f, 1.f );
  492. m_flChanceOfApplying = flChance;
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose:
  496. //-----------------------------------------------------------------------------
  497. bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
  498. {
  499. FOR_EACH_VEC( m_vecAdditionalComponents, i )
  500. {
  501. SCHEMA_INIT_SUBSTEP( m_vecAdditionalComponents[i]->BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
  502. }
  503. return SCHEMA_INIT_SUCCESS();
  504. }
  505. void CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::GetIsGuaranteed( int &nFlags ) const
  506. {
  507. // If we've got a 100% chance of applying, or we're the root (no parent) then we
  508. // can mark ourselves as guaranteed and continue to check our children to see if
  509. // any of them are guaranteed as well.
  510. if ( m_flChanceOfApplying == 1.f || !m_pParent )
  511. {
  512. nFlags |= GetIsOutput() ? GUARANTEED_OUTPUT : GUARANTEED_INPUT;
  513. FOR_EACH_VEC( m_vecAdditionalComponents, i )
  514. {
  515. m_vecAdditionalComponents[i]->GetIsGuaranteed( nFlags );
  516. }
  517. }
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose:
  521. //-----------------------------------------------------------------------------
  522. CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::CDynamicRecipeComponentDefinedItem( bool bIsOutput, const CBaseRecipeComponent* pParent )
  523. : CBaseRecipeComponent( bIsOutput, pParent )
  524. {}
  525. //-----------------------------------------------------------------------------
  526. // Purpose:
  527. //-----------------------------------------------------------------------------
  528. CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::~CDynamicRecipeComponentDefinedItem()
  529. {}
  530. //-----------------------------------------------------------------------------
  531. // Purpose: Make sure m_strName refers to an actual item def. Return false
  532. // if it doesn not. Child components are allowed to have "use_parents_item_def"
  533. // as their item name.
  534. //-----------------------------------------------------------------------------
  535. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
  536. {
  537. // Go through and make sure we have a bit of information that let's us describe an item
  538. bool bAnyDataSet = false;
  539. // Item def?
  540. if ( m_pParent == NULL && GetItemSchema()->GetItemDefinitionByName( m_strName ) )
  541. {
  542. bAnyDataSet = true;
  543. }
  544. else if ( BStringsEqual( m_strName, m_pszUseParentNameIdentifier ) || GetItemSchema()->GetItemDefinitionByName( m_strName ) )
  545. {
  546. bAnyDataSet = true;
  547. }
  548. // Quality?
  549. if ( m_eQuality != AE_UNDEFINED )
  550. {
  551. bAnyDataSet = true;
  552. }
  553. // Attributes?
  554. if ( m_vecDynamicAttributes.Count() )
  555. {
  556. bAnyDataSet = true;
  557. }
  558. // We better have one of the above
  559. SCHEMA_INIT_CHECK( bAnyDataSet, "Not enough data to describe component" );
  560. #ifdef GC_DLL
  561. // Get next available attrib def for defining the item
  562. CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), pAttribVec );
  563. SCHEMA_INIT_CHECK( pAttribDef, "Too many potential components" );
  564. if( pAttribDef )
  565. {
  566. pAttribVec->AddToTail( pAttribDef );
  567. }
  568. #endif
  569. SCHEMA_INIT_SUBSTEP( BaseClass::BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
  570. return SCHEMA_INIT_SUCCESS();
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose: Parse out our item definition name and quality. Return false if
  574. // either does not exist
  575. //-----------------------------------------------------------------------------
  576. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  577. {
  578. bool bNoItemDef = !!pKV->FindKey( "no_item_def" );
  579. bool bItemName = !!pKV->FindKey( "item_name" );
  580. // Make sure only one of the above is set
  581. SCHEMA_INIT_CHECK( bNoItemDef != bItemName,
  582. "Both \"no_item_def\" and \"item_name\" specified in component." );
  583. // Make sure at least one of the above is set
  584. SCHEMA_INIT_CHECK( bNoItemDef || bItemName,
  585. "Neither \"no_item_def\" or \"item_name\" specified in component." );
  586. // If they specified an item name, then we need to grab it
  587. if ( bItemName )
  588. {
  589. m_strName = pKV->GetString( "item_name", NULL );
  590. }
  591. return BaseClass::ParseKV( pKV, pVecErrors );
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Purpose: Convert ourselves into an attribute. Return false if our encoded
  595. // attributes exceed the allocated space for attributes
  596. //-----------------------------------------------------------------------------
  597. #ifdef GC_DLL
  598. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const
  599. {
  600. // Check if we should even apply
  601. if( !RollChanceOfApplying() )
  602. return true;
  603. // Gather up all the current attributes on the item
  604. ComponentAttribVector_t attribVec;
  605. CRecipeComponentMatchingIterator matchingIterator( pItem, NULL );
  606. pItem->IterateAttributes( &matchingIterator );
  607. attribVec.AddVectorToTail( matchingIterator.GetMatchingComponentInputs() );
  608. attribVec.AddVectorToTail( matchingIterator.GetMatchingComponentOutputs() );
  609. // Get next available attrib def for defining the item
  610. const CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), &attribVec );
  611. if( !pAttribDef )
  612. return false;
  613. uint32 nFlags = 0;
  614. CAttribute_DynamicRecipeComponent typedValue;
  615. // Check if our item name is specified to use our parent's
  616. const char* pszItemDefName = m_strName;
  617. if ( m_strName && m_strName[0] )
  618. {
  619. if( BStringsEqual( m_pszUseParentNameIdentifier, pszItemDefName ) && m_pParent )
  620. {
  621. // It's only possible to have another CDynamicRecipeComponentDefinedItem as a parent
  622. const CDynamicRecipeComponentDefinedItem* pParentDefinedItemComponent = dynamic_cast< const CDynamicRecipeComponentDefinedItem* >( m_pParent );
  623. AssertMsg( pParentDefinedItemComponent, "Parent attribute passed into defined item component is not a defined item component" );
  624. if( !pParentDefinedItemComponent )
  625. {
  626. return false;
  627. }
  628. // Adopt our parent's item name
  629. pszItemDefName = pParentDefinedItemComponent->m_strName;
  630. }
  631. // Make sure this item def exists
  632. CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinitionByName( pszItemDefName );
  633. AssertMsg1( pItemDef, "No item def named %s found when applying defined item component", pszItemDefName );
  634. if( !pItemDef )
  635. {
  636. return false;
  637. }
  638. // Set the item def
  639. typedValue.set_def_index( (uint32)pItemDef->GetDefinitionIndex() );
  640. nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET;
  641. }
  642. // Set the quality, if we're supposed to
  643. if ( m_eQuality != AE_UNDEFINED )
  644. {
  645. typedValue.set_item_quality( (uint32)m_eQuality );
  646. nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET;
  647. }
  648. nFlags |= m_bIsOutput ? DYNAMIC_RECIPE_FLAG_IS_OUTPUT : 0;
  649. if ( m_attributesMatchingType == ATTRIBUTES_MATCH_ALL )
  650. {
  651. nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL;
  652. }
  653. else if ( m_attributesMatchingType == ATTRIBUTES_MATCH_ANY )
  654. {
  655. nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY;
  656. }
  657. // Make sure any of the flags (besides the output flag) is set
  658. if ( ( nFlags & ~DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) == 0 )
  659. {
  660. AssertMsg(0, "Created component without any data flags set!" );
  661. return false;
  662. }
  663. typedValue.set_component_flags( nFlags );
  664. typedValue.set_num_required( RollCount() );
  665. typedValue.set_num_fulfilled( 0 );
  666. // Write out attribute all attribute indexes and values, separated by what we expect to be an invalid character sequence
  667. CUtlString strAttribs;
  668. FOR_EACH_VEC( m_vecDynamicAttributes, i )
  669. {
  670. if( i != 0 )
  671. {
  672. strAttribs.Append( g_pszAttrEncodeSeparator );
  673. }
  674. // Convert all of our attributes into a string.
  675. strAttribs.Append( CFmtStr( "%d", m_vecDynamicAttributes[i].m_AttrIndex ) );
  676. strAttribs.Append( g_pszAttrEncodeSeparator );
  677. strAttribs.Append( m_vecDynamicAttributes[i].m_strAttrData.Get() );
  678. }
  679. // Make sure we're not too long
  680. if( strAttribs.Length() >= 1024 )
  681. {
  682. AssertMsg1( 0, "String-encoded attributes exceeds 1024 characters, when encoding component %s", m_strName.Get() );
  683. return false;
  684. }
  685. // Set it in there!
  686. typedValue.set_attributes_string( strAttribs.Get() );
  687. // Check to see if we're about to create a duplicate. There's no need to spend another
  688. // attribute to describe the same item. Let's instead just increase the count on the
  689. // already-existing attribute.
  690. FOR_EACH_VEC( attribVec, i )
  691. {
  692. CAttribute_DynamicRecipeComponent existingValue;
  693. if( pItem->FindAttribute( attribVec[i], &existingValue ) )
  694. {
  695. if( typedValue.def_index() == existingValue.def_index() &&
  696. typedValue.item_quality() == existingValue.item_quality() &&
  697. typedValue.component_flags() == existingValue.component_flags() &&
  698. typedValue.attributes_string() == existingValue.attributes_string() )
  699. {
  700. pAttribDef = attribVec[i];
  701. existingValue.set_num_required( existingValue.num_required() + typedValue.num_required() );
  702. typedValue = existingValue;
  703. break;
  704. }
  705. }
  706. }
  707. pItem->SetDynamicAttributeValue( pAttribDef, typedValue );
  708. // Go through and add any additional components that depend on this component
  709. FOR_EACH_VEC( m_vecAdditionalComponents, i )
  710. {
  711. CBaseRecipeComponent* pAdditionalComponent = m_vecAdditionalComponents[i];
  712. pAdditionalComponent->AddRecipeComponentAsAttribute( pItem, pGameAccount );
  713. }
  714. return true;
  715. }
  716. #endif
  717. //-----------------------------------------------------------------------------
  718. // Purpose:
  719. //-----------------------------------------------------------------------------
  720. CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::CDynamicRecipeComponentLootList( bool bIsOutput, const CBaseRecipeComponent* pParent )
  721. : CEconTool_ItemDynamicRecipe::CBaseRecipeComponent( bIsOutput, pParent )
  722. #ifdef GC_DLL
  723. , m_eUniqueness( UNIQUE_AMONG_NOTHING )
  724. #endif
  725. {}
  726. CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::~CDynamicRecipeComponentLootList()
  727. {}
  728. //-----------------------------------------------------------------------------
  729. // Purpose: Make sure m_strName actually refers to a lootlist
  730. //-----------------------------------------------------------------------------
  731. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
  732. {
  733. // The game doesn't have all of the lootlists
  734. #ifdef GC_DLL
  735. SCHEMA_INIT_CHECK( GEconItemSchema().GetLootListByName( m_strName ),
  736. "CDynamicRecipeComponentLootList has invalid loot list: %s", m_strName.Get() );
  737. // Get next available attrib def for defining the item
  738. CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), pAttribVec );
  739. SCHEMA_INIT_CHECK( pAttribDef, "Too many potential recipe components!" );
  740. if( pAttribDef )
  741. {
  742. pAttribVec->AddToTail( pAttribDef );
  743. }
  744. // Inputs are not allowed to be marked UNIQUE_AMONG_OUTPUTS. Rather, mark the outputs UNIQUE_AMONG_INPUTS
  745. SCHEMA_INIT_CHECK( !(m_eUniqueness == UNIQUE_AMONG_OUTPUTS && !GetIsOutput() ), "Input component marked to be unique among inputs. Not supported!" );
  746. #endif
  747. // Skip defined item
  748. SCHEMA_INIT_SUBSTEP( BaseClass::BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
  749. return SCHEMA_INIT_SUCCESS();
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose: Parse in our lootlist name and quality. Return false if either doesn't exist
  753. //-----------------------------------------------------------------------------
  754. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  755. {
  756. const char* pszLootListName = pKV->GetString( "lootlist_name" );
  757. SCHEMA_INIT_CHECK( pszLootListName != NULL,
  758. "Missing lootlist name in lootlist recipe component" );
  759. m_strName = pszLootListName;
  760. bool bBaseResult = BaseClass::ParseKV( pKV, pVecErrors );
  761. #ifdef GC_DLL
  762. // By default, outputs try to avoid rolling as input, and inputs dont try to avoid anything
  763. m_eUniqueness = GetIsOutput() ? UNIQUE_AMONG_INPUTS : UNIQUE_AMONG_NOTHING;
  764. const char* pszUniqueness = pKV->GetString( "uniqueness" );
  765. if ( !V_stricmp( "unique_among_inputs", pszUniqueness ) )
  766. {
  767. m_eUniqueness = UNIQUE_AMONG_INPUTS;
  768. }
  769. else if ( !V_stricmp( "unique_among_outputs", pszUniqueness ) )
  770. {
  771. m_eUniqueness = UNIQUE_AMONG_OUTPUTS;
  772. }
  773. else if ( !V_stricmp( "unique_among_everything", pszUniqueness ) )
  774. {
  775. m_eUniqueness = UNIQUE_AMONG_EVERYTHING;
  776. }
  777. #endif
  778. return bBaseResult;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Purpose: Roll our item definition, then call our base to convert ourselves into an attribute
  782. //-----------------------------------------------------------------------------
  783. #ifdef GC_DLL
  784. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const
  785. {
  786. // Check if we should even apply
  787. if( !RollChanceOfApplying() )
  788. return true;
  789. // See if there's any item defs we should try to avoid
  790. CRecipeComponentInputDefIndexIterator inputIterator( m_eUniqueness );
  791. pItem->IterateAttributes( &inputIterator );
  792. const char* pszItemDefName = NULL;
  793. // Roll the item and any additional attributes
  794. CUtlVector< StringEncodedAttribute_t > vecLootlistGeneratedAttributes;
  795. if (!RollLootlistItemAndAttributes( vecLootlistGeneratedAttributes, &pszItemDefName, &inputIterator.GetMatchingComponentInputs(), pGameAccount ) )
  796. {
  797. return false;
  798. }
  799. vecLootlistGeneratedAttributes.AddVectorToTail( m_vecDynamicAttributes );
  800. // Create a temporary defined item component based on the lootlist roll
  801. CDynamicRecipeComponentDefinedItem definedItem( m_bIsOutput, this );
  802. definedItem.m_eQuality = m_eQuality;
  803. definedItem.m_strName = pszItemDefName;
  804. definedItem.m_attributesMatchingType = m_attributesMatchingType;
  805. definedItem.m_vecDynamicAttributes = vecLootlistGeneratedAttributes;
  806. definedItem.m_vecCountChances = m_vecCountChances;
  807. definedItem.m_flTotalWeights = m_flTotalWeights;
  808. // Write out this defined item
  809. definedItem.AddRecipeComponentAsAttribute( pItem, pGameAccount );
  810. // Create any additional components that depend on this component existing
  811. FOR_EACH_VEC( m_vecAdditionalComponents, i )
  812. {
  813. CBaseRecipeComponent* pAdditionalComponent = m_vecAdditionalComponents[i];
  814. // Update the parent to be the item we generated
  815. pAdditionalComponent->SetParent( &definedItem );
  816. pAdditionalComponent->AddRecipeComponentAsAttribute( pItem, pGameAccount );
  817. }
  818. return true;
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Purpose: Roll our item definition, then call our base to convert ourselves into an attribute
  822. //-----------------------------------------------------------------------------
  823. class CAttributeToStringIterator : public IEconItemUntypedAttributeIterator
  824. {
  825. public:
  826. CAttributeToStringIterator( CUtlVector< CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::StringEncodedAttribute_t >& vecAdditionalAttribs, CEconItem* pItem )
  827. : m_vecAdditionalAttribs( vecAdditionalAttribs )
  828. , m_pItem( pItem )
  829. {}
  830. virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef )
  831. {
  832. const CEconItem::attribute_t *pAttrInternalData = m_pItem->FindDynamicAttributeInternal( pAttrDef );
  833. // Only export attributes that we have dynamic data for.
  834. if ( pAttrInternalData )
  835. {
  836. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  837. Assert( pAttrType );
  838. // Set the definition index
  839. auto& attrib = m_vecAdditionalAttribs[ m_vecAdditionalAttribs.AddToTail() ];
  840. attrib.m_AttrIndex = pAttrDef->GetDefinitionIndex();
  841. // Convert the value to a string
  842. std::string sAttrValue;
  843. pAttrType->ConvertEconAttributeValueToString( pAttrDef, pAttrInternalData->m_value, &sAttrValue );
  844. Assert( sAttrValue.length() > 0 );
  845. attrib.m_strAttrData.Set( sAttrValue.c_str() );
  846. }
  847. return true;
  848. }
  849. private:
  850. CUtlVector< CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::StringEncodedAttribute_t >& m_vecAdditionalAttribs;
  851. CEconItem* m_pItem;
  852. };
  853. //-----------------------------------------------------------------------------
  854. // Purpose: Roll our item definition. This will give us a list of item definition. From this list
  855. // we take the first one and set our item def name and quality to be its. We then take any
  856. // attributes that it may have rolled and add them to our attributes as well. Return false
  857. // if any of these steps fail.
  858. //-----------------------------------------------------------------------------
  859. bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::RollLootlistItemAndAttributes( CUtlVector< StringEncodedAttribute_t >& vecAdditionalAttribs
  860. , const char** pszDefName
  861. , const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs
  862. , const CEconGameAccount *pGameAccount ) const
  863. {
  864. const CEconLootListDefinition* pLootList = GEconItemSchema().GetLootListByName( m_strName );
  865. if( !pLootList )
  866. {
  867. AssertMsg1( 0, "Lootlist %s not found when adding dynamic attribute", m_strName.Get() );
  868. return false;
  869. }
  870. CUtlVector<CEconItem *> vecRolledItems;
  871. // Roll our items
  872. CDefaultUniformRandomStream RandomStream;
  873. if ( !pLootList->BGenerateSingleRollRandomItems( pGameAccount, false, &vecRolledItems ) )
  874. {
  875. AssertMsg1( 0, "Error generating item defs from lootlist \"%s\"", m_strName.Get() );
  876. return false;
  877. }
  878. // We're just going to use the first one
  879. CEconItem* pGeneratedItem = vecRolledItems.Head();
  880. // Set our name and quality
  881. (*pszDefName) = pGeneratedItem->GetItemDefinition()->GetDefinitionName();
  882. const_cast<CDynamicRecipeComponentLootList*>(this)->m_eQuality = (EEconItemQuality)pGeneratedItem->GetQuality();
  883. // Sniff and encode the attributes
  884. CAttributeToStringIterator attrToString( vecAdditionalAttribs, pGeneratedItem );
  885. pGeneratedItem->IterateAttributes( &attrToString );
  886. // Cleanup
  887. for( auto pItem : vecRolledItems )
  888. {
  889. delete pItem;
  890. }
  891. return true;
  892. }
  893. #endif
  894. //-----------------------------------------------------------------------------
  895. // Purpose: Delete all the things
  896. //-----------------------------------------------------------------------------
  897. CEconTool_ItemDynamicRecipe::~CEconTool_ItemDynamicRecipe()
  898. {
  899. m_vecComponents.PurgeAndDeleteElements();
  900. }
  901. CEconTool_ItemDynamicRecipe::CRecipeComponentInputDefIndexIterator::CRecipeComponentInputDefIndexIterator( EItemDefUniqueness_t eUniqueness )
  902. : m_eUniqueness( eUniqueness )
  903. {}
  904. bool CEconTool_ItemDynamicRecipe::CRecipeComponentInputDefIndexIterator::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
  905. {
  906. // We dont care
  907. if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_NOTHING )
  908. return true;
  909. // Only check against outputs
  910. if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_OUTPUTS && !( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) )
  911. return true;
  912. // Only check against inputs
  913. if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_INPUTS && value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
  914. return true;
  915. // Add this item def if we haven't seen it
  916. if ( m_vecInputItemDefs.Find( value.def_index() ) == m_vecInputItemDefs.InvalidIndex() )
  917. {
  918. m_vecInputItemDefs.AddToTail( value.def_index() );
  919. }
  920. return true;
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose: Parse all of our inputs then outputs
  924. //-----------------------------------------------------------------------------
  925. bool CEconTool_ItemDynamicRecipe::BInitFromKV( KeyValues *pKVDef, CUtlVector<CUtlString> *pVecErrors )
  926. {
  927. // Parse the components blocks
  928. SCHEMA_INIT_SUBSTEP( CBaseRecipeComponent::ParseComponentsBlock( pKVDef, m_vecComponents, pVecErrors, NULL ) );
  929. // Sort the component vector such that inputs go first
  930. struct RecipeComponentSorter
  931. {
  932. static int SortRecipeComponentVector( CEconTool_ItemDynamicRecipe::CBaseRecipeComponent* const *pComponent1, CEconTool_ItemDynamicRecipe::CBaseRecipeComponent* const *pComponent2 )
  933. {
  934. return (*pComponent1)->GetIsOutput() && !(*pComponent2)->GetIsOutput();
  935. }
  936. };
  937. m_vecComponents.Sort( &RecipeComponentSorter::SortRecipeComponentVector );
  938. #ifdef GC_DLL
  939. int nFlags = 0;
  940. FOR_EACH_VEC( m_vecComponents, i )
  941. {
  942. m_vecComponents[i]->GetIsGuaranteed( nFlags );
  943. }
  944. SCHEMA_INIT_CHECK( nFlags & GUARANTEED_OUTPUT, "No guaranteed outputs for dynamic recipe" );
  945. SCHEMA_INIT_CHECK( nFlags & GUARANTEED_INPUT, "No guaranteed inputs for dynamic recipe" );
  946. #endif
  947. return SCHEMA_INIT_SUCCESS();
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: Parse the component blocks, determining if this component is an input or output
  951. //-----------------------------------------------------------------------------
  952. bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseComponentsBlock( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent )
  953. {
  954. // The components block doesn't exist on the client
  955. KeyValues *pKVComponents = pKV->FindKey( "components" );
  956. #ifdef GC_DLL
  957. SCHEMA_INIT_CHECK( pKVComponents || pParent, "Failed to parse components block in dynamic recipe" );
  958. #endif
  959. if ( pKVComponents )
  960. {
  961. // There's duplicate code when we read in like this, but it makes the
  962. // item defs much easier to read
  963. // Parse all the inputs
  964. KeyValues *pKVParameters = pKVComponents->FindKey( "input" );
  965. if( pKVParameters )
  966. {
  967. ParseComponents( pKVParameters, vecComponents, false, pVecErrors, pParent );
  968. }
  969. #ifdef GC_DLL
  970. // Parse all the outputs
  971. pKVParameters = pKVComponents->FindKey( "output" );
  972. if( pKVParameters )
  973. {
  974. ParseComponents( pKVParameters, vecComponents, true, pVecErrors, pParent );
  975. }
  976. #endif
  977. }
  978. return SCHEMA_INIT_SUCCESS();
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose: Create the appropriate component types and have them parse themselves.
  982. // Returns true if at least one of the components parsed has a 100% chance
  983. // of applying, and we don't have a parent
  984. //-----------------------------------------------------------------------------
  985. bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseComponents( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, bool bIsOutput, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent )
  986. {
  987. // Go through each entry in the tool
  988. KeyValues *pEntry = pKV->GetFirstSubKey();
  989. while( pEntry )
  990. {
  991. // Find which type is being specified.
  992. CBaseRecipeComponent *pComponent = NULL;
  993. if( pEntry->FindKey( "lootlist_name" ) )
  994. {
  995. pComponent = vecComponents[vecComponents.AddToTail( new CDynamicRecipeComponentLootList( bIsOutput, pParent ) )];
  996. }
  997. else if ( pEntry->FindKey( "item_name" ) || pEntry->FindKey( "no_item_def" ) )
  998. {
  999. pComponent = vecComponents[vecComponents.AddToTail( new CDynamicRecipeComponentDefinedItem( bIsOutput, pParent ) )];
  1000. }
  1001. else
  1002. {
  1003. SCHEMA_INIT_CHECK( false, "Unrecognized recipe component type!" );
  1004. }
  1005. // Now that we've got the right type, parse!
  1006. if( pComponent )
  1007. {
  1008. pComponent->ParseKV( pEntry, pVecErrors );
  1009. }
  1010. pEntry = pEntry->GetNextKey();
  1011. }
  1012. return SCHEMA_INIT_SUCCESS();
  1013. }
  1014. //-----------------------------------------------------------------------------
  1015. // Purpose: Parse in all of the attributes for this component. Attributes have
  1016. // already been parsed in, so we can immediately check if the attribute exists.
  1017. //-----------------------------------------------------------------------------
  1018. bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  1019. {
  1020. // Get the quality string
  1021. const char* pszQuality = pKV->GetString( "quality", NULL );
  1022. if ( pszQuality )
  1023. {
  1024. // Convert the quality string to a item quality
  1025. m_eQuality = EconQuality_GetQualityFromString( pszQuality );
  1026. SCHEMA_INIT_CHECK( m_eQuality != AE_UNDEFINED, "Invalid item quality \"%s\" specified for component \"%s\"", pszQuality, m_strName.Get() );
  1027. }
  1028. const char* pszAttributesMatchingType = pKV->GetString( "attributes_matching_type", NULL );
  1029. if ( pszAttributesMatchingType )
  1030. {
  1031. if ( !V_stricmp( pszAttributesMatchingType, "all" ) )
  1032. {
  1033. m_attributesMatchingType = ATTRIBUTES_MATCH_ALL;
  1034. }
  1035. else if ( !V_stricmp( pszAttributesMatchingType, "any" ) )
  1036. {
  1037. m_attributesMatchingType = ATTRIBUTES_MATCH_ANY;
  1038. }
  1039. else
  1040. {
  1041. SCHEMA_INIT_CHECK( 0, "Invalid attributes_matching_type \"%s\"", pszAttributesMatchingType );
  1042. }
  1043. }
  1044. // Parse all of our attributes
  1045. KeyValues* pKVAttribs = pKV->FindKey( "attributes" );
  1046. if( pKVAttribs )
  1047. {
  1048. FOR_EACH_SUBKEY( pKVAttribs, pKVAttribute )
  1049. {
  1050. static_attrib_t staticAttrib;
  1051. SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( "attributes", pKVAttribute, pVecErrors ) );
  1052. const CEconItemAttributeDefinition * pAttrDef = staticAttrib.GetAttributeDefinition();
  1053. SCHEMA_INIT_CHECK( pAttrDef != NULL, "Attribute index %i not found when specifying dynamic recipe", staticAttrib.iDefIndex );
  1054. StringEncodedAttribute_t& attrib = m_vecDynamicAttributes[ m_vecDynamicAttributes.AddToTail() ];
  1055. attrib.m_AttrIndex = pAttrDef->GetDefinitionIndex();
  1056. attrib.m_strAttrData = pKVAttribs->GetString( pKVAttribute->GetName(), "" );
  1057. Assert( !attrib.m_strAttrData.IsEmpty() );
  1058. }
  1059. }
  1060. // Get our chance of applying. Default to 100%
  1061. float flChance = pKV->GetFloat( "chance", 1.f );
  1062. // Make sure it's in the range (0,1]
  1063. SCHEMA_INIT_CHECK( flChance > 0.f && flChance <= 1.f, "Recipe component chance to apply out of bounds: %f", flChance );
  1064. SetChanceOfApplying( flChance );
  1065. // Read in the "counts" block if it exists
  1066. KeyValues *pKVCounts = pKV->FindKey( "counts" );
  1067. if( pKVCounts )
  1068. {
  1069. // Read check count entry
  1070. FOR_EACH_SUBKEY( pKVCounts, pKVEntry )
  1071. {
  1072. // Split out count range of the format "#-#", or just "#"
  1073. CUtlStringList vecCount;
  1074. const char* pszCount = pKVEntry->GetName();
  1075. V_SplitString( pszCount, "-", vecCount );
  1076. SCHEMA_INIT_CHECK( vecCount.Count() == 1 || vecCount.Count() == 2, "Malformed count value: %s", pszCount );
  1077. CountChance_t& countChance = m_vecCountChances[ m_vecCountChances.AddToTail() ];
  1078. // Add up the chances
  1079. countChance.m_flChance = (float)pKVEntry->GetInt();
  1080. m_flTotalWeights += countChance.m_flChance;
  1081. // Set the min and the max. If no max is specifid, then max is the min
  1082. countChance.m_nMinCount = V_atoi( vecCount[0] );
  1083. countChance.m_nMaxCount = vecCount.Count() > 1 ? V_atoi( vecCount[1] ) : countChance.m_nMinCount;
  1084. // Make sure max >= min and min > 0
  1085. SCHEMA_INIT_CHECK( countChance.m_nMaxCount >= countChance.m_nMinCount, "Recipe component count max is less than the min: %s", pszCount );
  1086. SCHEMA_INIT_CHECK( countChance.m_nMinCount > 0, "Recipe component count min less than 0: %s", pszCount );
  1087. }
  1088. }
  1089. // Init any components we may have in us
  1090. SCHEMA_INIT_CHECK( ParseComponentsBlock( pKV, m_vecAdditionalComponents, pVecErrors, this ), "Failed to parse nested components block in dynamic recipe" );
  1091. return SCHEMA_INIT_SUCCESS();
  1092. }
  1093. #ifdef GC_DLL
  1094. //-----------------------------------------------------------------------------
  1095. // Purpose: This tool will apply recipe components as attributes. Go through each
  1096. // of our attributes, roll to see if it applies, and convert it to an attribute
  1097. // and add it to the item if we do. Return false if we ever fail.
  1098. //-----------------------------------------------------------------------------
  1099. bool CEconTool_ItemDynamicRecipe::BGenerateDynamicAttributes( CEconItem* pItem, const CEconGameAccount *pGameAccount ) const
  1100. {
  1101. // Go through our inputs and write them out into attributes
  1102. FOR_EACH_VEC( m_vecComponents, i )
  1103. {
  1104. if( !m_vecComponents[i]->AddRecipeComponentAsAttribute( pItem, pGameAccount ) )
  1105. return false;
  1106. }
  1107. return true;
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Purpose: Scan the item for numbered attributes with the passed in base name.
  1111. // Return the attribute of the next available index or NULL if there are
  1112. // none available
  1113. //-----------------------------------------------------------------------------
  1114. CEconItemAttributeDefinition* CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::GetNextAvailableAttributeWithBaseName( const char* pszBaseAttribName, ComponentAttribVector_t* pAttribVec )
  1115. {
  1116. Assert( pAttribVec );
  1117. if( !pAttribVec )
  1118. return NULL;
  1119. CEconItemAttributeDefinition *pAttribDef = NULL;
  1120. int i=1;
  1121. while( pAttribDef == NULL )
  1122. {
  1123. const char* pszAttribName = CFmtStr( "%s %d", pszBaseAttribName, i++ );
  1124. CEconItemAttributeDefinition *pTempAttribDef = GEconItemSchema().GetAttributeDefinitionByName( pszAttribName );
  1125. // Check to see if this attribute we're talking about even exists
  1126. if( !pTempAttribDef )
  1127. {
  1128. return NULL;
  1129. }
  1130. // Check if the vector doesn't have this attribute. If not, it's available
  1131. if( pAttribVec->Find( pTempAttribDef ) == pAttribVec->InvalidIndex() )
  1132. {
  1133. pAttribDef = pTempAttribDef;
  1134. }
  1135. }
  1136. return pAttribDef;
  1137. }
  1138. #endif
  1139. //-----------------------------------------------------------------------------
  1140. bool CEconTool_Xifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1141. {
  1142. Assert( pTool );
  1143. Assert( pToolSubject );
  1144. const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
  1145. if ( !pSubjectItemDef )
  1146. return false;
  1147. // Check to make sure the definition of the item we're trying to apply to has the tags we need to match.
  1148. FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
  1149. {
  1150. if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
  1151. return false;
  1152. }
  1153. // If this xifier has target restrictions, ensure our subject is one of them
  1154. if ( m_ItemDefTargetRestrictions.Count() > 0 )
  1155. {
  1156. bool bPassed = false;
  1157. FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
  1158. {
  1159. const CEconItemDefinition *pTargetItemDef = GetItemSchema()->GetItemDefinition( m_ItemDefTargetRestrictions[i] );
  1160. if ( ItemDefMatch( pSubjectItemDef, pTargetItemDef ) )
  1161. {
  1162. bPassed = true;
  1163. break;
  1164. }
  1165. }
  1166. if ( bPassed == false )
  1167. return false;
  1168. }
  1169. // if rarity restriction, target needs rarity of this or lower
  1170. if ( m_ItemRarityRestriction != k_unItemRarity_Any )
  1171. {
  1172. uint8 unSubjectRarity = pToolSubject->GetItemDefinition()->GetRarity();
  1173. if ( unSubjectRarity == k_unItemRarity_Any || unSubjectRarity == 0 || unSubjectRarity > m_ItemRarityRestriction )
  1174. return false;
  1175. // needs to be equippable
  1176. static CSchemaAttributeDefHandle pAttribDef_StatModule( "weapon_uses_stattrak_module" );
  1177. if ( !pToolSubject->FindAttribute( pAttribDef_StatModule ) )
  1178. return false;
  1179. }
  1180. // Check if we have a restriction as an attribute
  1181. static CSchemaAttributeDefHandle pAttribDef_ToolTargetItem( "tool target item" );
  1182. float value;
  1183. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pTool, pAttribDef_ToolTargetItem, &value ) )
  1184. {
  1185. const CEconItemDefinition *pTargetDef = GetItemSchema()->GetItemDefinition( value );
  1186. // Check for a match (might have NULL here for target definition but that's safe to pass in)
  1187. if ( !ItemDefMatch( pSubjectItemDef, pTargetDef ) )
  1188. {
  1189. return false;
  1190. }
  1191. }
  1192. return IEconTool::CanApplyTo( pTool, pToolSubject );
  1193. }
  1194. //-----------------------------------------------------------------------------
  1195. bool CEconTool_Xifier::ItemDefMatch( const CEconItemDefinition* pTargetItemDef, const CEconItemDefinition* pSubjectItemDef ) const
  1196. {
  1197. if ( pTargetItemDef && pSubjectItemDef )
  1198. {
  1199. // Item def match counts
  1200. if ( pTargetItemDef == pSubjectItemDef )
  1201. return true;
  1202. // If these item defs have the same XifierRemapClass then they are allowed to match as well
  1203. if ( pTargetItemDef->GetXifierRemapClass() && *pTargetItemDef->GetXifierRemapClass()
  1204. && pSubjectItemDef->GetXifierRemapClass() && *pSubjectItemDef->GetXifierRemapClass() )
  1205. {
  1206. if (BStringsEqual(pSubjectItemDef->GetXifierRemapClass(), pTargetItemDef->GetXifierRemapClass()))
  1207. return true;
  1208. }
  1209. }
  1210. return false;
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. bool CEconTool_Strangifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1214. {
  1215. Assert( pTool );
  1216. Assert( pToolSubject );
  1217. // Do not allow for already strange items
  1218. if ( pToolSubject->GetQuality() == AE_STRANGE )
  1219. return false;
  1220. // Go over the attributes of the item, if it has any strange attributes the item is strange and don't apply
  1221. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  1222. {
  1223. if ( pToolSubject->FindAttribute( GetKillEaterAttr_Score( i ) ) )
  1224. {
  1225. return false;
  1226. }
  1227. }
  1228. // Default rules
  1229. return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
  1230. }
  1231. //-----------------------------------------------------------------------------
  1232. bool CEconTool_KillStreakifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1233. {
  1234. Assert( pTool );
  1235. Assert( pToolSubject );
  1236. // Make sure the item doesn't already have an effect
  1237. static CSchemaAttributeDefHandle pAttribDef_KillStreakEffect( "killstreak tier" );
  1238. float flEffectIndex = 0.0;
  1239. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pToolSubject, pAttribDef_KillStreakEffect, &flEffectIndex ) )
  1240. return false;
  1241. // Default rules
  1242. return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
  1243. }
  1244. //-----------------------------------------------------------------------------
  1245. bool CEconTool_Festivizer::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1246. {
  1247. Assert( pTool );
  1248. Assert( pToolSubject );
  1249. const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
  1250. if ( !pSubjectItemDef )
  1251. return false;
  1252. // Make sure the item doesn't already have an effect
  1253. static CSchemaAttributeDefHandle pAttribDef_Festivizer( "is_festivized" );
  1254. if ( FindAttribute( pToolSubject, pAttribDef_Festivizer ) )
  1255. return false;
  1256. // Default rules
  1257. return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
  1258. }
  1259. bool CEconTool_Unusualifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1260. {
  1261. Assert( pTool );
  1262. Assert( pToolSubject );
  1263. // don't stomp item that's already unusual
  1264. if ( pToolSubject->GetQuality() == AE_UNUSUAL )
  1265. return false;
  1266. // Default rules
  1267. return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
  1268. }
  1269. //-----------------------------------------------------------------------------
  1270. bool CEconTool_ItemEaterRecharger::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1271. {
  1272. Assert( pTool );
  1273. Assert( pToolSubject );
  1274. const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
  1275. if ( !pSubjectItemDef )
  1276. return false;
  1277. // Check to make sure the definition of the item we're trying to apply to has the tags we need to match.
  1278. FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
  1279. {
  1280. if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
  1281. return false;
  1282. }
  1283. // If this eater has target restrictions, ensure our subject is one of them
  1284. if ( m_ItemDefTargetRestrictions.Count() > 0 )
  1285. {
  1286. bool bPassed = false;
  1287. item_definition_index_t iSubject = pSubjectItemDef->GetDefinitionIndex();
  1288. FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
  1289. {
  1290. if ( m_ItemDefTargetRestrictions[i] == iSubject )
  1291. {
  1292. bPassed = true;
  1293. break;
  1294. }
  1295. }
  1296. if ( bPassed == false )
  1297. return false;
  1298. }
  1299. return IEconTool::CanApplyTo( pTool, pToolSubject );
  1300. }
  1301. //-----------------------------------------------------------------------------
  1302. int CEconTool_ItemEaterRecharger::GetChargesForItemDefId( item_definition_index_t defIndex ) const
  1303. {
  1304. FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
  1305. {
  1306. if ( m_ItemDefTargetRestrictions[i] == defIndex )
  1307. {
  1308. return m_ItemDefTargetChargeValues[i];
  1309. }
  1310. }
  1311. return 0;
  1312. }
  1313. //-----------------------------------------------------------------------------
  1314. // Purpose:
  1315. //-----------------------------------------------------------------------------
  1316. bool CEconTool_UpgradeCard::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1317. {
  1318. Assert( pTool );
  1319. Assert( pToolSubject );
  1320. const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
  1321. if ( !pSubjectItemDef )
  1322. return false;
  1323. // Abort if we're trying to apply to a base item.
  1324. if ( pSubjectItemDef->IsBaseItem() )
  1325. return false;
  1326. // Abort if for some reason we don't know what type of attribute we would attach.
  1327. if ( m_vecAttributes.Count() <= 0 )
  1328. return false;
  1329. // Make sure that none of the attributes we're going to try to apply already exist on the item. We don't
  1330. // allow double-stacking the same attribute partially for balance purposes, but also because the database
  1331. // back-end doesn't support two attributes of the same type on the same item.
  1332. FOR_EACH_VEC( m_vecAttributes, i )
  1333. {
  1334. if ( pToolSubject->FindAttribute( m_vecAttributes[i].m_pAttrDef ) )
  1335. return false;
  1336. }
  1337. // Make sure the item that we're thinking of applying to has enough room to have another card's
  1338. // worth of items attached. We do this in sort of a roundabout way, by having the attributes themselves
  1339. // know whether they came from a card or not.
  1340. CCountUserGeneratedAttributeIterator countIterator;
  1341. pToolSubject->IterateAttributes( &countIterator );
  1342. if ( countIterator.GetCount() >= GetMaxCardUpgradesPerItem() )
  1343. return false;
  1344. // Check to make sure the definition of the item we're trying to apply to has the tags we need
  1345. // to match.
  1346. FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
  1347. {
  1348. if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
  1349. return false;
  1350. }
  1351. return IEconTool::CanApplyTo( pTool, pToolSubject );
  1352. }
  1353. //-----------------------------------------------------------------------------
  1354. // Purpose:
  1355. //-----------------------------------------------------------------------------
  1356. bool CEconTool_ClassTransmogrifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1357. {
  1358. Assert( pTool );
  1359. Assert( pToolSubject );
  1360. const GameItemDefinition_t *pSubjectItemDef = pToolSubject->GetItemDefinition();
  1361. if ( !pSubjectItemDef )
  1362. return false;
  1363. // Abort if we're trying to apply to a base item.
  1364. if ( pSubjectItemDef->IsBaseItem() )
  1365. return false;
  1366. // Abort if we're trying to apply to a Self-Made or Community item
  1367. if ( pToolSubject->GetQuality() == AE_SELFMADE ||
  1368. pToolSubject->GetQuality() == AE_COMMUNITY )
  1369. {
  1370. return false;
  1371. }
  1372. // Abort if we somehow got here before we know what class we were trying to produce items for.
  1373. if ( m_iClass <= 0 || m_iClass >= LOADOUT_COUNT )
  1374. return false;
  1375. // Check to make sure the definition of the item we're trying to apply to has the tags we need
  1376. // to match.
  1377. FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
  1378. {
  1379. if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
  1380. return false;
  1381. }
  1382. return IEconTool::CanApplyTo( pTool, pToolSubject );
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. bool CEconTool_DuckToken::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
  1386. {
  1387. Assert( pTool );
  1388. Assert( pToolSubject );
  1389. static CSchemaAttributeDefHandle pAttrDef_DuckBadgeLevel( "duck badge level" );
  1390. uint32 unOldBadgeLevel = 0;
  1391. if ( !FindAttribute( pToolSubject, pAttrDef_DuckBadgeLevel, &unOldBadgeLevel ) )
  1392. return false;
  1393. if ( unOldBadgeLevel >= 5 )
  1394. return false;
  1395. // Default rules
  1396. return IEconTool::CanApplyTo( pTool, pToolSubject );
  1397. }
  1398. //---------------------------------------------------------------------------------------
  1399. // Purpose: given a tool and an item to apply the tool's effects upon, return true if the
  1400. // tool is allowed to affect the subject. This is used on the client for UI and
  1401. // on the GC for actual application validity testing.
  1402. //---------------------------------------------------------------------------------------
  1403. /* static */ bool CEconSharedToolSupport::ToolCanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject )
  1404. {
  1405. if ( pTool == NULL || pToolSubject == NULL )
  1406. return false;
  1407. const GameItemDefinition_t *pToolDef = pTool->GetItemDefinition();
  1408. if ( pToolDef == NULL )
  1409. return false;
  1410. // If we have a tool that's in escrow it can't be used on anything.
  1411. static CSchemaAttributeDefHandle pAttrib_ToolEscrowUntil( "tool escrow until date" );
  1412. if ( pTool->FindAttribute( pAttrib_ToolEscrowUntil ) )
  1413. return false;
  1414. if ( pToolSubject->IsTemporaryItem() )
  1415. return false;
  1416. // Cannot modify preview items. Should be caught by temporary-item check above.
  1417. Assert( pToolSubject->GetOrigin() != kEconItemOrigin_PreviewItem );
  1418. const GameItemDefinition_t *pToolSubjectDef = pToolSubject->GetItemDefinition();
  1419. if ( pToolSubjectDef == NULL )
  1420. return false;
  1421. if ( !ToolCanApplyToDefinition( pToolDef, pToolSubjectDef ) )
  1422. return false;
  1423. // If we can apply to the definition then we should be known to have valid tool data.
  1424. // If our tool has no tool metadata then we don't allow it to be applied to anything.
  1425. const IEconTool *pEconTool = pToolDef->GetEconTool();
  1426. Assert( pEconTool );
  1427. return pEconTool->CanApplyTo( pTool, pToolSubject );
  1428. }
  1429. /* static */ bool CEconSharedToolSupport::ToolCanApplyToDefinition( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef )
  1430. {
  1431. if ( !pToolDef || !pToolSubjectDef || !pToolDef->IsTool() )
  1432. {
  1433. // not a tool
  1434. return false;
  1435. }
  1436. // If our tool has no tool metadata then we don't allow it to be applied to anything.
  1437. const IEconTool *pEconTool = pToolDef->GetEconTool();
  1438. if ( !pEconTool )
  1439. return false;
  1440. unsigned int unToolUsageCaps = 0;
  1441. if ( pEconTool->GetCapabilities() )
  1442. {
  1443. for ( unsigned int i = 0; i < NUM_ITEM_CAPS; i++ )
  1444. {
  1445. if ( pEconTool->GetCapabilities() & (1 << i) )
  1446. {
  1447. unToolUsageCaps |= g_CapabilityApplicationMap[i];
  1448. }
  1449. }
  1450. }
  1451. // Check for base applicability of this tool to this object.
  1452. if ( (unToolUsageCaps & pToolSubjectDef->GetCapabilities()) == 0 )
  1453. return false;
  1454. // check to see if either the tool or the tool target have usage restriction
  1455. const IEconTool *pSubjectEconTool = pToolSubjectDef->GetEconTool();
  1456. if ( pSubjectEconTool )
  1457. {
  1458. // If this tool can apply to anything then we don't care about the checks below
  1459. // making sure restrictions match.
  1460. const char *pszToolRestriction = BStringsEqual( pEconTool->GetUsageRestriction(), "any" )
  1461. ? pSubjectEconTool->GetUsageRestriction()
  1462. : pEconTool->GetUsageRestriction();
  1463. if ( !BStringsEqual( pszToolRestriction, pSubjectEconTool->GetUsageRestriction() ) )
  1464. return false;
  1465. }
  1466. return true;
  1467. }
  1468. // WARNING
  1469. // DO NOT USE THIS CODE IF YOUR TOOL HAS Attribute restrictions like "tool_target_item" or similar restriction attributes
  1470. /* static */ bool CEconSharedToolSupport::ToolCanApplyToBaseItem( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef )
  1471. {
  1472. if ( !pToolSubjectDef )
  1473. return false;
  1474. // We are targetting the "Upgradeable" version of a base item and not a base item itself
  1475. if ( pToolSubjectDef->IsBaseItem() || pToolSubjectDef->IsHidden() || pToolSubjectDef->GetQuality() == AE_NORMAL || Q_strnicmp( pToolSubjectDef->GetDefinitionName(), "Upgradeable ", 12 ) )
  1476. return false;
  1477. return ToolCanApplyToDefinition( pToolDef, pToolSubjectDef );
  1478. }