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.

250 lines
8.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Functions related to dynamic recipes
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "econ_dynamic_recipe.h"
  8. #ifndef GC_DLL
  9. #include "quest_objective_manager.h"
  10. #endif
  11. // This pattern was chosen to not be:
  12. // - a valid string acceptable for user-input (ie., custom name)
  13. // - a sensical float bit pattern
  14. // - a common int bit pattern
  15. // - meaningful Unicode data
  16. const char *g_pszAttrEncodeSeparator = "|\x01\x02\x01\x03|\x01\x02\x01\x03|";
  17. CRecipeComponentMatchingIterator::CRecipeComponentMatchingIterator( const IEconItemInterface *pSourceItem,
  18. const IEconItemInterface *pTargetItem )
  19. : m_pSourceItem( pSourceItem )
  20. , m_pTargetItem( pTargetItem )
  21. , m_bIgnoreCompleted( true )
  22. , m_nInputsTotal( 0 )
  23. , m_nInputsFulfilled( 0 )
  24. , m_nOutputsTotal( 0 )
  25. {}
  26. bool CRecipeComponentMatchingIterator::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
  27. {
  28. // Don't count ourselves as a match!
  29. if ( m_pSourceItem && m_pTargetItem && m_pSourceItem->GetID() == m_pTargetItem->GetID() )
  30. return true;
  31. // If this isn't a match and the item isn't NULL, we skip. We consider NULL to mean
  32. // that we want to tally ALL attributes of this type
  33. if ( !DefinedItemAttribMatch( value, m_pTargetItem ) && m_pTargetItem != NULL )
  34. return true;
  35. // Dont let non-craftable items through
  36. if ( m_pTargetItem && !m_pTargetItem->IsUsableInCrafting() )
  37. return true;
  38. // Is this an output?
  39. if ( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
  40. {
  41. m_vecMatchingOutputs.AddToTail( pAttrDef );
  42. m_nOutputsTotal += value.num_required();
  43. }
  44. else
  45. {
  46. m_vecMatchingInputs.AddToTail( pAttrDef );
  47. m_nInputsTotal += value.num_required();
  48. m_nInputsFulfilled += value.num_fulfilled();
  49. }
  50. return true;
  51. }
  52. bool DefinedItemAttribMatch( const CAttribute_DynamicRecipeComponent& attribValue, const IEconItemInterface* pItem )
  53. {
  54. if ( !pItem )
  55. return false;
  56. // If our fulfilled count is what our item count is, then we're done. We dont want any more matches.
  57. if ( attribValue.num_fulfilled() == attribValue.num_required() )
  58. return false;
  59. // If the item_def flag is set, and the item's item_def doesnt match then not a match
  60. if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET ) &&
  61. ( attribValue.def_index() != (uint32)pItem->GetItemDefIndex() ) )
  62. return false;
  63. // If the quality flag is set, and the item's quality doesn't match, then not a match
  64. if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET ) &&
  65. ( attribValue.item_quality() != (uint32)pItem->GetQuality() ) )
  66. return false;
  67. // check if we have ALL required attributes
  68. if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL )
  69. {
  70. CUtlVector<CEconItem::attribute_t> vecAttribs;
  71. if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
  72. {
  73. AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
  74. return false;
  75. }
  76. FOR_EACH_VEC( vecAttribs, i )
  77. {
  78. const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
  79. Assert( pAttr );
  80. uint32 itemAttributeValue;
  81. if ( !pAttr || !pItem->FindAttribute( pAttr, &itemAttributeValue ) || itemAttributeValue != vecAttribs[i].m_value.asUint32 )
  82. {
  83. return false;
  84. }
  85. }
  86. }
  87. // check if we have ANY required attributes
  88. else if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY )
  89. {
  90. CUtlVector<CEconItem::attribute_t> vecAttribs;
  91. if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
  92. {
  93. AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
  94. return false;
  95. }
  96. bool bHasAnyMatchingAttributes = false;
  97. FOR_EACH_VEC( vecAttribs, i )
  98. {
  99. const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
  100. Assert( pAttr );
  101. uint32 itemAttributeValue;
  102. if ( pAttr && pItem->FindAttribute( pAttr, &itemAttributeValue ) && itemAttributeValue == vecAttribs[i].m_value.asUint32 )
  103. {
  104. bHasAnyMatchingAttributes = true;
  105. break;
  106. }
  107. }
  108. if ( !bHasAnyMatchingAttributes )
  109. {
  110. return false;
  111. }
  112. }
  113. return true;
  114. }
  115. bool DecodeAttributeStringIntoAttributes( const CAttribute_DynamicRecipeComponent& attribValue, CUtlVector<CEconItem::attribute_t>& vecAttribs )
  116. {
  117. CUtlStringList vecAttributeStrings; // Automatically free'd
  118. V_SplitString( attribValue.attributes_string().c_str(), g_pszAttrEncodeSeparator, vecAttributeStrings );
  119. if( vecAttributeStrings.Count() % 2 != 0 )
  120. {
  121. AssertMsg1( 0, "%s: Uneven count of encoded attribute strings!", __FUNCTION__ );
  122. return false;
  123. }
  124. for( int j = 0; j< vecAttributeStrings.Count(); j+=2 )
  125. {
  126. // Get the attribute definition that's stored in the string, and its type
  127. attrib_definition_index_t index = Q_atoi( vecAttributeStrings[j] );
  128. const CEconItemAttributeDefinition *pAttrDef = GEconItemSchema().GetAttributeDefinition( index );
  129. if ( !pAttrDef )
  130. {
  131. #ifdef GC
  132. EmitError( SPEW_GC, __FUNCTION__ ": Unable to find attribute definition '%s' (index %d)!\n", vecAttributeStrings[j], j );
  133. #endif
  134. return false;
  135. }
  136. CEconItem::attribute_t& attrib = vecAttribs[vecAttribs.AddToTail()];
  137. attrib.m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
  138. // Now have the attribute read in the value stored in the string
  139. const ISchemaAttributeType* pAttrType = pAttrDef->GetAttributeType();
  140. pAttrType->InitializeNewEconAttributeValue( &attrib.m_value );
  141. // Don't fail us now!
  142. const char* pszAttribValue = vecAttributeStrings[j+1];
  143. if ( !pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszAttribValue, &attrib.m_value ) )
  144. {
  145. #ifdef GC
  146. EmitError( SPEW_GC, __FUNCTION__ ": Unable to parse attribute value '%s' for attribute '%s'!\n", pszAttribValue, pAttrDef->GetDefinitionName() );
  147. #endif
  148. return false;
  149. }
  150. }
  151. return true;
  152. }
  153. bool DecodeItemFromEncodedAttributeString( const CAttribute_DynamicRecipeComponent& attribValue, CEconItem* pItem )
  154. {
  155. // If the item_def flag is set, set that item def
  156. if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
  157. {
  158. pItem->SetDefinitionIndex( attribValue.def_index() );
  159. }
  160. else
  161. {
  162. // If the flag is not set, then we want the item name to be generic. In english, we want to just call it "item".
  163. CAttribute_String attrStr;
  164. attrStr.set_value( "#TF_ItemName_Item" );
  165. static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
  166. pItem->SetDynamicAttributeValue( pAttrDef_ItemNameTextOverride, attrStr );
  167. }
  168. // If the quality flag is set, take the quality
  169. if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET )
  170. {
  171. pItem->SetQuality( attribValue.item_quality() );
  172. // If there's no item def set and the quality specified is "unique", we want to be explicit
  173. // and have the item description actually say "unique item" so there's no confusion as to what
  174. // item quality we want as an input.
  175. if ( !( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
  176. && pItem->GetQuality() == AE_UNIQUE )
  177. {
  178. CAttribute_String attrStr;
  179. attrStr.set_value( "#unique" );
  180. static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
  181. pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
  182. }
  183. }
  184. else
  185. {
  186. // If no quality was specified, we want to explicity say that we'll accept ANY quality.
  187. pItem->SetQuality( AE_UNIQUE );
  188. CAttribute_String attrStr;
  189. attrStr.set_value( "#TF_QualityText_Any" );
  190. static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
  191. pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
  192. }
  193. pItem->SetFlags( 0 );
  194. // Get all the attributes encoded into the attribute
  195. CUtlVector<CEconItem::attribute_t> vecAttribs;
  196. if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
  197. {
  198. AssertMsg1( 0, " %s : Unable to decode dynamic recipe attributes", __FUNCTION__ );
  199. return false;
  200. }
  201. // Apply the attributes to the item
  202. FOR_EACH_VEC( vecAttribs, j )
  203. {
  204. // We don't expect to get here with any missing attributes.
  205. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( vecAttribs[j].m_unDefinitionIndex );
  206. Assert( pAttrDef );
  207. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  208. pAttrType->LoadEconAttributeValue( pItem, pAttrDef, vecAttribs[j].m_value );
  209. // Free up our attribute memory now that we're done with it.
  210. pAttrType->UnloadEconAttributeValue( &vecAttribs[j].m_value );
  211. }
  212. return true;
  213. }