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.

331 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: EconItemFactory: Manages rolling for items requested by the game server
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. // memdbgon must be the last include file in a .cpp file!!!
  8. #include "tier0/memdbgon.h"
  9. #include "econ/econ_assetapi_context.h"
  10. using namespace GCSDK;
  11. //-----------------------------------------------------------------------------
  12. // Purpose: Constructor
  13. //-----------------------------------------------------------------------------
  14. CEconItemFactory::CEconItemFactory( )
  15. : m_ulNextObjID( 0 )
  16. , m_bIsInitialized( false )
  17. {
  18. }
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Initializes the item factory and schema. Return false if init failed
  21. //-----------------------------------------------------------------------------
  22. bool CEconItemFactory::BYieldingInit()
  23. {
  24. CUtlVector< CUtlString > vecErrors;
  25. bool bRet = m_schema.BInit( "scripts/items/unencrypted/items_master.txt", "GAME", &vecErrors );
  26. FOR_EACH_VEC( vecErrors, nError )
  27. {
  28. EmitError( SPEW_GC, "%s\n", vecErrors[nError].Get() );
  29. }
  30. static const char *pchMaxIDQuery = "SELECT MAX( ID ) FROM "
  31. "( select max(ID) AS ID FROM Item UNION SELECT MAX(ID) AS ID FROM ForeignItem ) as tbl";
  32. CSQLAccess sqlAccess;
  33. if( !sqlAccess.BYieldingExecuteSingleResult<uint64, uint64>( NULL, pchMaxIDQuery, k_EGCSQLType_int64, &m_ulNextObjID, NULL ) )
  34. {
  35. EmitError( SPEW_GC, "Failed to read max item ID" );
  36. return false;
  37. }
  38. m_ulNextObjID++; // our next ID is one past the current max ID
  39. m_bIsInitialized = bRet;
  40. return bRet;
  41. }
  42. static const CEconItemQualityDefinition *GetQualityDefinitionForItemCreation( const CItemSelectionCriteria *pOptionalCriteria, const CEconItemDefinition *pItemDef )
  43. {
  44. Assert( pItemDef );
  45. // Do we have a quality specified? If so, is it a valid quality? If not, we fall back to the
  46. // quality specified by the item definition, the schema, etc.
  47. uint8 unQuality = k_unItemQuality_Any;
  48. // Quality specified in generation request via criteria?
  49. if ( pOptionalCriteria && pOptionalCriteria->BQualitySet() )
  50. {
  51. unQuality = pOptionalCriteria->GetQuality();
  52. }
  53. // If not: quality specified in item definition?
  54. if ( unQuality == k_unItemQuality_Any )
  55. {
  56. unQuality = pItemDef->GetQuality();
  57. }
  58. // Final fallback: default quality in schema.
  59. if ( unQuality == k_unItemQuality_Any )
  60. {
  61. unQuality = GetItemSchema()->GetDefaultQuality();
  62. }
  63. AssertMsg( unQuality != k_unItemQuality_Any, "Unable to locate valid quality!" );
  64. return GetItemSchema()->GetQualityDefinition( unQuality );
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: Creates an item matching the incoming item selection criteria
  68. // Input: pItem - Pointer to the item to fill in
  69. // criteria - The criteria that the generated item must match
  70. // Output: True if a matching item could be generated, false otherwise
  71. //-----------------------------------------------------------------------------
  72. CEconItem *CEconItemFactory::CreateRandomItem( const CEconGameAccount *pGameAccount, const CItemSelectionCriteria &criteria )
  73. {
  74. // Find a matching item definition.
  75. const CEconItemDefinition *pItemDef = RollItemDefinition( criteria );
  76. if ( NULL == pItemDef )
  77. {
  78. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateRandomItem(): Item creation request with no matching definition\n" );
  79. return NULL;
  80. }
  81. const CEconItemQualityDefinition *pQualityDef = GetQualityDefinitionForItemCreation( &criteria, pItemDef );
  82. if ( NULL == pQualityDef )
  83. {
  84. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateRandomItem(): Item creation request with unknown quality\n" );
  85. return NULL;
  86. }
  87. // At this point we have everything that can fail will already have failed, so we can safely
  88. // create an item and just move properties over to it.
  89. CEconItem *pItem = new CEconItem();
  90. pItem->SetItemID( GetNextID() );
  91. pItem->SetDefinitionIndex( pItemDef->GetDefinitionIndex() );
  92. pItem->SetItemLevel( criteria.BItemLevelSet() ? criteria.GetItemLevel() : pItemDef->RollItemLevel() );
  93. pItem->SetQuality( pQualityDef->GetDBValue() );
  94. pItem->SetInventoryToken( criteria.GetInitialInventory() );
  95. pItem->SetQuantity( criteria.BInitialQuantitySet() ? criteria.GetInitialQuantity() : pItemDef->GetDefaultDropQuantity() );
  96. // don't set account ID
  97. // Add any custom attributes we need
  98. if( !BAddGCGeneratedAttributesToItem( pGameAccount, pItem ) )
  99. {
  100. delete pItem;
  101. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Failed to generate attributes\n" );
  102. return NULL;
  103. }
  104. return pItem;
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose: Creates an item based on a specific item definition index
  108. // Input: pItem - Pointer to the item to fill in
  109. // unDefinitionIndex - The definition index of the item to create
  110. // Output: True if a matching item could be generated, false otherwise
  111. //-----------------------------------------------------------------------------
  112. CEconItem *CEconItemFactory::CreateSpecificItem( const CEconGameAccount *pGameAccount, item_definition_index_t unDefinitionIndex )
  113. {
  114. // Find the matching index
  115. const CEconItemDefinition *pItemDef = m_schema.GetItemDefinition( unDefinitionIndex );
  116. if ( NULL == pItemDef )
  117. {
  118. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Item creation request with no matching definition (def index %u)\n", unDefinitionIndex );
  119. return NULL;
  120. }
  121. const CEconItemQualityDefinition *pQualityDef = GetQualityDefinitionForItemCreation( NULL, pItemDef );
  122. if ( NULL == pQualityDef )
  123. {
  124. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Item creation request with unknown quality\n" );
  125. return NULL;
  126. }
  127. CEconItem *pItem = new CEconItem();
  128. if ( pGameAccount != NULL )
  129. pItem->SetItemID( GetNextID() );
  130. pItem->SetDefinitionIndex( unDefinitionIndex );
  131. pItem->SetItemLevel( pItemDef->RollItemLevel() );
  132. pItem->SetQuality( pQualityDef->GetDBValue() );
  133. // don't set inventory token
  134. pItem->SetQuantity( MAX( 1, pItemDef->GetDefaultDropQuantity() ) );
  135. // Startup test code calls this with a null pGameAccount.
  136. if ( pGameAccount != NULL )
  137. {
  138. pItem->SetAccountID( pGameAccount->Obj().m_unAccountID );
  139. // Add any custom attributes we need
  140. if( !BAddGCGeneratedAttributesToItem( pGameAccount, pItem ) )
  141. {
  142. delete pItem;
  143. EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Failed to generate attributes\n" );
  144. return NULL;
  145. }
  146. }
  147. return pItem;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose: Randomly chooses an item definition that matches the criteria
  151. // Input: sCriteria - The criteria that the generated item must match
  152. // Output: The chosen item definition, or NULL if no item could be selected
  153. //-----------------------------------------------------------------------------
  154. const CEconItemDefinition *CEconItemFactory::RollItemDefinition( const CItemSelectionCriteria &criteria ) const
  155. {
  156. // Determine which item templates match the criteria
  157. CUtlVector<item_definition_index_t> vecMatches;
  158. const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_schema.GetItemDefinitionMap();
  159. FOR_EACH_MAP_FAST( mapDefs, i )
  160. {
  161. if ( criteria.BEvaluate( mapDefs[i] ) )
  162. {
  163. vecMatches.AddToTail( mapDefs.Key( i ) );
  164. }
  165. }
  166. if ( 0 == vecMatches.Count() )
  167. return NULL;
  168. // Choose a random match
  169. int iIndex = RandomInt( 0, vecMatches.Count() - 1 );
  170. return m_schema.GetItemDefinition( vecMatches[iIndex] );
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose: Generates attributes that the item definition insists it always has, but must be generated by the GC
  174. // Input:
  175. //-----------------------------------------------------------------------------
  176. bool CEconItemFactory::BAddGCGeneratedAttributesToItem( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const
  177. {
  178. const CEconItemDefinition *pDef = m_schema.GetItemDefinition( pItem->GetDefinitionIndex() );
  179. if ( !pDef )
  180. return false;
  181. const CUtlVector<static_attrib_t> &vecStaticAttribs = pDef->GetStaticAttributes();
  182. // Only generate attributes that force the GC to generate them (so they vary per item created)
  183. FOR_EACH_VEC( vecStaticAttribs, i )
  184. {
  185. if ( vecStaticAttribs[i].bForceGCToGenerate )
  186. {
  187. ApplyStaticAttributeToItem( pItem, vecStaticAttribs[i], pGameAccount );
  188. }
  189. }
  190. const IEconTool* pTool = pDef->GetEconTool();
  191. if( pTool )
  192. {
  193. if( !pTool->BGenerateDynamicAttributes( pItem, pGameAccount ) )
  194. return false;
  195. }
  196. if ( !pDef->BApplyPropertyGenerators( pItem ) )
  197. return false;
  198. return true;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose:
  202. //-----------------------------------------------------------------------------
  203. void CEconItemFactory::ApplyStaticAttributeToItem( CEconItem *pItem, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const
  204. {
  205. static CSchemaAttributeDefHandle pAttr_ElevateQuality( "elevate quality" );
  206. static CSchemaAttributeDefHandle pAttr_ElevateToUnusual( "elevate to unusual if applicable" );
  207. static CSchemaAttributeDefHandle pAttr_Particle( "attach particle effect" );
  208. static CSchemaAttributeDefHandle pAttr_HatUnusual( "hat only unusual effect" );
  209. static CSchemaAttributeDefHandle pAttrDef_TauntUnusual( "taunt only unusual effect" );
  210. static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
  211. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
  212. Assert( pAttrDef );
  213. // Special-case the elevate-quality attribute.
  214. if ( pAttrDef == pAttr_ElevateQuality )
  215. {
  216. //AssertMsg( CEconItem::GetTypedAttributeType<CSchemaAttributeType_Default>( pAttrDef ), "Elevate quality attribute doesn't have the right type!" );
  217. int iQuality = (int)staticAttrib.m_value.asFloat;
  218. // Do not change the quality of an item to Strange if it is not basic
  219. if ( iQuality == AE_STRANGE )
  220. {
  221. if ( pItem->GetQuality() == AE_UNIQUE || pItem->GetQuality() == AE_PAINTKITWEAPON || pItem->GetQuality() == AE_NORMAL )
  222. {
  223. pItem->SetQuality( iQuality );
  224. }
  225. // If the quality is strange, strangify this item
  226. StrangifyItemInPlace( pItem );
  227. }
  228. else
  229. {
  230. pItem->SetQuality( iQuality );
  231. }
  232. return;
  233. }
  234. // Special-case to elevate-quality only if item has particles. This 'attr' needs to be added LAST in a lootlist
  235. // Or rather after particles may have been granted
  236. else if ( pAttrDef == pAttr_ElevateToUnusual )
  237. {
  238. // Scan all attributes.
  239. if ( pItem->FindAttribute( pAttr_Particle ) || pItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
  240. {
  241. pItem->SetQuality( AE_UNUSUAL );
  242. }
  243. return;
  244. }
  245. else if ( pAttrDef == pAttr_HatUnusual )
  246. {
  247. // Ensure the target item is a hat, if it is not bail, if it is setup a particle effect attr (Whole head items are considered 'hats' for purposes of unusuals )
  248. if ( !(pItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) )
  249. && !(pItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) )
  250. ) {
  251. // does not match, bail
  252. return;
  253. }
  254. // create a new static attrib
  255. static_attrib_t unusualAttr( staticAttrib );
  256. // load the normal attach effect instead
  257. pAttr_Particle->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttr_Particle, unusualAttr, pGameAccount );
  258. return;
  259. }
  260. else if ( pAttrDef == pAttrDef_TauntUnusual )
  261. {
  262. // Ensure the target item is a taunt, if it is not bail
  263. if ( pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) != LOADOUT_POSITION_TAUNT )
  264. {
  265. // does not match, bail
  266. CFmtStr fmtStr( "Attempted to put an unusual taunt effect onto item %s, but it's not a taunt! Check which lootlists it appears in and remove it from any that are trying to unusualize it!", pItem->GetItemDefinition()->GetItemDefinitionName() );
  267. EmitError( SPEW_GC, "%s\n", fmtStr.Get() );
  268. return;
  269. }
  270. // create a new static attrib
  271. static_attrib_t unusualAttr( staticAttrib );
  272. // load the normal attach effect instead
  273. pAttrDef_TauntUnusualAttr->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttrDef_TauntUnusualAttr, unusualAttr, pGameAccount );
  274. return;
  275. }
  276. // Custom attribute initialization code?
  277. pAttrDef->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttrDef, staticAttrib, pGameAccount );
  278. }