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.

2765 lines
104 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: CEconItem, a shared object for econ items
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "econ_item.h"
  8. #include "econ_item_schema.h"
  9. #include "rtime.h"
  10. #include "gcsdk/enumutils.h"
  11. #include "smartptr.h"
  12. #ifdef GC_DLL
  13. #include "gcsdk/sqlaccess/sqlaccess.h"
  14. #include "econ/localization_provider.h"
  15. #endif
  16. #if defined( TF_CLIENT_DLL ) || defined( TF_DLL )
  17. #include "tf_gcmessages.h"
  18. #endif
  19. using namespace GCSDK;
  20. #ifdef GC_DLL
  21. IMPLEMENT_CLASS_MEMPOOL( CEconItem, 100 * 1000, UTLMEMORYPOOL_GROW_SLOW );
  22. IMPLEMENT_CLASS_MEMPOOL( CEconItemCustomData, 50 * 1000, UTLMEMORYPOOL_GROW_SLOW );
  23. #endif
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. extern int EconWear_ToIntCategory( float flWear );
  27. /*static*/ const schema_attribute_stat_bucket_t *CSchemaAttributeStats::m_pHead;
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Utility function to convert datafile strings to ints.
  30. //-----------------------------------------------------------------------------
  31. int StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings, bool bDontAssert )
  32. {
  33. if ( !szValue || !szValue[0] )
  34. return -1;
  35. for ( int i = 0; i < iNumStrings; i++ )
  36. {
  37. if ( !Q_stricmp(szValue, pValueStrings[i]) )
  38. return i;
  39. }
  40. if ( !bDontAssert )
  41. {
  42. Assert( !"Missing value in StringFieldToInt()!" );
  43. }
  44. return -1;
  45. }
  46. //-----------------------------------------------------------------------------
  47. // Purpose: Utility function to convert datafile strings to ints.
  48. //-----------------------------------------------------------------------------
  49. int StringFieldToInt( const char *szValue, const CUtlVector<const char *>& vecValueStrings, bool bDontAssert )
  50. {
  51. return StringFieldToInt( szValue, (const char **)&vecValueStrings[0], vecValueStrings.Count(), bDontAssert );
  52. }
  53. // --------------------------------------------------------------------------
  54. // Purpose:
  55. // --------------------------------------------------------------------------
  56. CEconItem::CEconItem()
  57. : BaseClass( )
  58. , m_pCustomData( NULL )
  59. , m_ulID( INVALID_ITEM_ID )
  60. , m_unStyle( 0 )
  61. , m_pszSmallIcon( NULL )
  62. , m_pszLargeIcon( NULL )
  63. {
  64. Init();
  65. }
  66. CEconItem::CEconItem( const CEconItem& rhs )
  67. : BaseClass( )
  68. , m_pCustomData( NULL )
  69. , m_ulID( INVALID_ITEM_ID )
  70. , m_unStyle( 0 )
  71. , m_pszSmallIcon( NULL )
  72. , m_pszLargeIcon( NULL )
  73. {
  74. Init();
  75. (*this) = rhs;
  76. }
  77. void CEconItem::Init()
  78. {
  79. memset( &m_dirtyBits, 0, sizeof( m_dirtyBits ) );
  80. #ifdef GC
  81. // to set defaults
  82. CSchItem item;
  83. item.m_ulID = INVALID_ITEM_ID;
  84. DeserializeFromSchemaItem( item );
  85. COMPILE_TIME_ASSERT( sizeof( m_ulID ) == sizeof( item.m_ulID ) );
  86. COMPILE_TIME_ASSERT( sizeof( m_unAccountID ) == sizeof( item.m_unAccountID ) );
  87. COMPILE_TIME_ASSERT( sizeof( m_unDefIndex ) == sizeof( item.m_unDefIndex ) );
  88. COMPILE_TIME_ASSERT( sizeof( m_unLevel ) == sizeof( item.m_unLevel ) );
  89. COMPILE_TIME_ASSERT( sizeof( m_nQuality ) == sizeof( item.m_nQuality ) );
  90. COMPILE_TIME_ASSERT( sizeof( m_unInventory ) == sizeof( item.m_unInventory ) );
  91. COMPILE_TIME_ASSERT( sizeof( m_unFlags ) == sizeof( item.m_unFlags ) );
  92. COMPILE_TIME_ASSERT( sizeof( m_unOrigin ) == sizeof( item.m_unOrigin ) );
  93. COMPILE_TIME_ASSERT( sizeof( m_unStyle ) == sizeof( item.m_unStyle ) );
  94. // @note (Tom Bui): we need to know about any new fields
  95. // Need to add new fields to:
  96. // CEconItem::operator=
  97. // CEconItem::SerializeToSchemaItem
  98. // CEconItem::DeserializeFromSchemaItem
  99. // CEconItem::SerializeToProtoBufItem
  100. // CEconItem::DeserializeFromProtoBufItem
  101. // CEconManager::BYieldingLoadSOCache
  102. // CEconGetPlayerItemsJob::BYieldingHandleGetPlayerItemsV001
  103. COMPILE_TIME_ASSERT( CSchItem::k_iFieldMax == 13 );
  104. m_bEquippedThisGameServerSession = false;
  105. #endif
  106. }
  107. // --------------------------------------------------------------------------
  108. // Purpose:
  109. // --------------------------------------------------------------------------
  110. CEconItem::~CEconItem()
  111. {
  112. // Free up any memory we may have allocated for our singleton attribute. Any other attributes
  113. // will be cleaned up as part of freeing the custom data object itself.
  114. if ( m_dirtyBits.m_bHasAttribSingleton )
  115. {
  116. CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
  117. }
  118. // Free up any custom data we may have allocated. This will catch any attributes not
  119. // in our singleton.
  120. if ( m_pCustomData )
  121. {
  122. delete m_pCustomData;
  123. }
  124. }
  125. // --------------------------------------------------------------------------
  126. // Purpose:
  127. // --------------------------------------------------------------------------
  128. CEconItemCustomData::~CEconItemCustomData()
  129. {
  130. FOR_EACH_VEC( m_vecAttributes, i )
  131. {
  132. FreeAttributeMemory( &m_vecAttributes[i] );
  133. }
  134. if ( m_pInteriorItem )
  135. {
  136. delete m_pInteriorItem;
  137. }
  138. }
  139. // --------------------------------------------------------------------------
  140. // Purpose:
  141. // --------------------------------------------------------------------------
  142. void CEconItem::CopyAttributesFrom( const CEconItem& source )
  143. {
  144. // Copy attributes -- each new instance needs to be allocated and then copied into by somewhere
  145. // that knows what the actual type is. Rather than do anything type-specific here, we just have each
  146. // attribute serialize it's value to a bytestream and then deserialize it. This is as safe as we can
  147. // make it but sort of silly wasteful.
  148. for ( int i = 0; i < source.GetDynamicAttributeCountInternal(); i++ )
  149. {
  150. const attribute_t& attr = source.GetDynamicAttributeInternal( i );
  151. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
  152. Assert( pAttrDef );
  153. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  154. Assert( pAttrType );
  155. std::string sBytes;
  156. pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
  157. pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, sBytes );
  158. }
  159. }
  160. // --------------------------------------------------------------------------
  161. // Purpose:
  162. // --------------------------------------------------------------------------
  163. CEconItem &CEconItem::operator=( const CEconItem& rhs )
  164. {
  165. // We do destructive operations on our local object, including freeing attribute memory, as part of
  166. // the copy, so we force self-copies to be a no-op.
  167. if ( &rhs == this )
  168. return *this;
  169. m_ulID = rhs.m_ulID;
  170. SetOriginalID( rhs.GetOriginalID() );
  171. m_unAccountID = rhs.m_unAccountID;
  172. m_unDefIndex = rhs.m_unDefIndex;
  173. m_unLevel = rhs.m_unLevel;
  174. m_nQuality = rhs.m_nQuality;
  175. m_unInventory = rhs.m_unInventory;
  176. SetQuantity( rhs.GetQuantity() );
  177. m_unFlags = rhs.m_unFlags;
  178. m_unOrigin = rhs.m_unOrigin;
  179. m_unStyle = rhs.m_unStyle;
  180. m_EquipInstanceSingleton = rhs.m_EquipInstanceSingleton;
  181. // If we have memory allocated for a single attribute we free it manually.
  182. if ( m_dirtyBits.m_bHasAttribSingleton )
  183. {
  184. CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
  185. }
  186. // Copy over our dirty bits but manually reset our attribute singleton state -- if we did have one,
  187. // we just deleted it above (and might replace it below); if we didn't have one, this won't affect
  188. // anything. Either way, because we have no attribute memory allocated at this point, we need this
  189. // to be reflected in the dirty bits so that if we do copy attributes, we copy them into the correct
  190. // place (either the singleton or the custom data, to be allocated later).
  191. m_dirtyBits = rhs.m_dirtyBits;
  192. m_dirtyBits.m_bHasAttribSingleton = false;
  193. // Free any custom memory we've allocated. This will also remove any custom attributes.
  194. if ( rhs.m_pCustomData == NULL )
  195. {
  196. delete m_pCustomData;
  197. m_pCustomData = NULL;
  198. }
  199. else
  200. {
  201. // Check for and copy in the equip instances from CustomData
  202. EnsureCustomDataExists();
  203. m_pCustomData->m_vecEquipped = rhs.m_pCustomData->m_vecEquipped;
  204. }
  205. CopyAttributesFrom( rhs );
  206. // Reset our material overrides, they'll be set again on demand as needed.
  207. ResetMaterialOverrides();
  208. return *this;
  209. }
  210. // --------------------------------------------------------------------------
  211. // Purpose:
  212. // --------------------------------------------------------------------------
  213. void CEconItem::SetItemID( itemid_t ulID )
  214. {
  215. uint64 ulOldID = m_ulID;
  216. m_ulID = ulID;
  217. // only overwrite if we don't have an original id currently and we are a new item cloned off an old item
  218. if ( ulOldID != INVALID_ITEM_ID && ulOldID != ulID && ( m_pCustomData == NULL || m_pCustomData->m_ulOriginalID == INVALID_ITEM_ID ) && ulID != INVALID_ITEM_ID && ulOldID != INVALID_ITEM_ID )
  219. {
  220. SetOriginalID( ulOldID );
  221. }
  222. ResetMaterialOverrides();
  223. }
  224. // --------------------------------------------------------------------------
  225. // Purpose:
  226. // --------------------------------------------------------------------------
  227. itemid_t CEconItem::GetOriginalID() const
  228. {
  229. if ( m_pCustomData != NULL && m_pCustomData->m_ulOriginalID != INVALID_ITEM_ID )
  230. return m_pCustomData->m_ulOriginalID;
  231. return m_ulID;
  232. }
  233. // --------------------------------------------------------------------------
  234. // Purpose:
  235. // --------------------------------------------------------------------------
  236. void CEconItem::SetOriginalID( itemid_t ulOriginalID )
  237. {
  238. if ( ulOriginalID != m_ulID )
  239. {
  240. EnsureCustomDataExists();
  241. m_pCustomData->m_ulOriginalID = ulOriginalID;
  242. }
  243. }
  244. // --------------------------------------------------------------------------
  245. // Purpose:
  246. // --------------------------------------------------------------------------
  247. int CEconItem::GetQuantity() const
  248. {
  249. if ( m_pCustomData != NULL )
  250. return m_pCustomData->m_unQuantity;
  251. return 1;
  252. }
  253. // --------------------------------------------------------------------------
  254. // Purpose:
  255. // --------------------------------------------------------------------------
  256. void CEconItem::SetQuantity( uint16 unQuantity )
  257. {
  258. if ( m_pCustomData )
  259. {
  260. m_pCustomData->m_unQuantity = unQuantity;
  261. }
  262. else if ( unQuantity > 1 )
  263. {
  264. EnsureCustomDataExists();
  265. m_pCustomData->m_unQuantity = unQuantity;
  266. }
  267. }
  268. // --------------------------------------------------------------------------
  269. // Purpose:
  270. // --------------------------------------------------------------------------
  271. static const char *GetCustomNameOrAttributeDesc( const CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef )
  272. {
  273. if ( !pAttrDef )
  274. {
  275. // If we didn't specify the attribute in the schema we can't possibly have an
  276. // answer. This isn't really an error in that case.
  277. return NULL;
  278. }
  279. const char *pszStrContents;
  280. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItem, pAttrDef, &pszStrContents ) )
  281. return pszStrContents;
  282. return NULL;
  283. }
  284. // --------------------------------------------------------------------------
  285. // Purpose:
  286. // --------------------------------------------------------------------------
  287. static void SetCustomNameOrDescAttribute( CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef, const char *pszNewValue )
  288. {
  289. Assert( pItem );
  290. if ( !pAttrDef )
  291. {
  292. // If we didn't specify the attribute in the schema, that's fine if we're setting
  293. // the empty name/description string, but it isn't fine if we're trying to set
  294. // actual content.
  295. AssertMsg( !pszNewValue, "Attempt to set non-empty value for custom name/desc with no attribute present." );
  296. return;
  297. }
  298. // Removing existing value?
  299. if ( !pszNewValue || !pszNewValue[0] )
  300. {
  301. pItem->RemoveDynamicAttribute( pAttrDef );
  302. return;
  303. }
  304. CAttribute_String attrStr;
  305. attrStr.set_value( pszNewValue );
  306. pItem->SetDynamicAttributeValue( pAttrDef, attrStr );
  307. }
  308. // --------------------------------------------------------------------------
  309. // Purpose:
  310. // --------------------------------------------------------------------------
  311. const char *CEconItem::GetCustomName() const
  312. {
  313. static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
  314. return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomName );
  315. }
  316. // --------------------------------------------------------------------------
  317. // Purpose:
  318. // --------------------------------------------------------------------------
  319. void CEconItem::SetCustomName( const char *pName )
  320. {
  321. static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
  322. SetCustomNameOrDescAttribute( this, pAttrDef_CustomName, pName );
  323. }
  324. // --------------------------------------------------------------------------
  325. // Purpose:
  326. // --------------------------------------------------------------------------
  327. bool CEconItem::IsEquipped() const
  328. {
  329. for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
  330. {
  331. const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
  332. Assert( curEquipInstance.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
  333. if ( GetItemSchema()->IsValidClass( curEquipInstance.m_unEquippedClass ) )
  334. return true;
  335. }
  336. return false;
  337. }
  338. // --------------------------------------------------------------------------
  339. // Purpose:
  340. // --------------------------------------------------------------------------
  341. bool CEconItem::IsEquippedForClass( equipped_class_t unClass ) const
  342. {
  343. return NULL != FindEquippedInstanceForClass( unClass );
  344. }
  345. // --------------------------------------------------------------------------
  346. // Purpose:
  347. // --------------------------------------------------------------------------
  348. equipped_slot_t CEconItem::GetEquippedPositionForClass( equipped_class_t unClass ) const
  349. {
  350. const EquippedInstance_t *pInstance = FindEquippedInstanceForClass( unClass );
  351. if ( pInstance )
  352. return pInstance->m_unEquippedSlot;
  353. return INVALID_EQUIPPED_SLOT;
  354. }
  355. // --------------------------------------------------------------------------
  356. // Purpose:
  357. // --------------------------------------------------------------------------
  358. const CEconItem::EquippedInstance_t *CEconItem::FindEquippedInstanceForClass( equipped_class_t nClass ) const
  359. {
  360. for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
  361. {
  362. const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
  363. if ( curEquipInstance.m_unEquippedClass == nClass )
  364. return &curEquipInstance;
  365. }
  366. return NULL;
  367. }
  368. //----------------------------------------------------------------------------
  369. // Purpose:
  370. //----------------------------------------------------------------------------
  371. void CEconItem::InternalVerifyEquipInstanceIntegrity() const
  372. {
  373. if ( m_dirtyBits.m_bHasEquipSingleton )
  374. {
  375. Assert( !m_pCustomData );
  376. Assert( m_EquipInstanceSingleton.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
  377. }
  378. else if ( m_pCustomData )
  379. {
  380. FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
  381. {
  382. Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
  383. for ( int j = i + 1; j < m_pCustomData->m_vecEquipped.Count(); j++ )
  384. {
  385. Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedClass != m_pCustomData->m_vecEquipped[j].m_unEquippedClass );
  386. }
  387. }
  388. }
  389. else
  390. {
  391. Assert( GetEquippedInstanceCount() == 0 );
  392. }
  393. }
  394. //----------------------------------------------------------------------------
  395. // Purpose:
  396. //----------------------------------------------------------------------------
  397. void CEconItem::Equip( equipped_class_t unClass, equipped_slot_t unSlot )
  398. {
  399. Assert( GetItemSchema()->IsValidClass( unClass ) );
  400. Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
  401. // First, make sure we don't have this item already equipped for this class.
  402. UnequipFromClass( unClass );
  403. // If we have no instances of this item equipped, we want to shove this into the
  404. // first empty slot we can find. If we already have a custom data allocated, we
  405. // use that. If not, we want to use the singleton if we can. Otherwise, we make
  406. // a new custom data and fall back to using that.
  407. if ( m_pCustomData )
  408. {
  409. m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
  410. }
  411. else if ( !m_dirtyBits.m_bHasEquipSingleton )
  412. {
  413. m_EquipInstanceSingleton = EquippedInstance_t( unClass, unSlot );
  414. m_dirtyBits.m_bHasEquipSingleton = true;
  415. }
  416. else
  417. {
  418. EnsureCustomDataExists();
  419. m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
  420. }
  421. InternalVerifyEquipInstanceIntegrity();
  422. #ifdef GC_DLL
  423. m_bEquippedThisGameServerSession = true;
  424. #endif // GC_DLL
  425. }
  426. //----------------------------------------------------------------------------
  427. // Purpose:
  428. //----------------------------------------------------------------------------
  429. void CEconItem::Unequip()
  430. {
  431. if ( m_dirtyBits.m_bHasEquipSingleton )
  432. {
  433. Assert( !m_pCustomData );
  434. m_dirtyBits.m_bHasEquipSingleton = false;
  435. }
  436. else if ( m_pCustomData )
  437. {
  438. m_pCustomData->m_vecEquipped.Purge();
  439. }
  440. InternalVerifyEquipInstanceIntegrity();
  441. }
  442. //----------------------------------------------------------------------------
  443. // Purpose:
  444. //----------------------------------------------------------------------------
  445. void CEconItem::UnequipFromClass( equipped_class_t unClass )
  446. {
  447. Assert( GetItemSchema()->IsValidClass( unClass ) );
  448. // If we only have a single equipped class...
  449. if ( m_dirtyBits.m_bHasEquipSingleton )
  450. {
  451. // ...and that's the class we're trying to remove from...
  452. if ( m_EquipInstanceSingleton.m_unEquippedClass == unClass )
  453. {
  454. // ...we now have no equipped classes!
  455. m_dirtyBits.m_bHasEquipSingleton = false;
  456. }
  457. }
  458. else if ( m_pCustomData )
  459. {
  460. // ...otherwise, if we have multiple equipped classes...
  461. FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
  462. {
  463. // ...then look through our list to find out if we have this class...
  464. if ( m_pCustomData->m_vecEquipped[i].m_unEquippedClass == unClass )
  465. {
  466. // ...and if we do, remove it.
  467. m_pCustomData->m_vecEquipped.FastRemove( i );
  468. break;
  469. }
  470. }
  471. }
  472. InternalVerifyEquipInstanceIntegrity();
  473. }
  474. // --------------------------------------------------------------------------
  475. // Purpose:
  476. // --------------------------------------------------------------------------
  477. int CEconItem::GetEquippedInstanceCount() const
  478. {
  479. if ( m_pCustomData )
  480. return m_pCustomData->m_vecEquipped.Count();
  481. else
  482. return m_dirtyBits.m_bHasEquipSingleton ? 1 : 0;
  483. }
  484. // --------------------------------------------------------------------------
  485. // Purpose:
  486. // --------------------------------------------------------------------------
  487. const CEconItem::EquippedInstance_t &CEconItem::GetEquippedInstance( int iIdx ) const
  488. {
  489. Assert( iIdx >= 0 && iIdx < GetEquippedInstanceCount() );
  490. if ( m_pCustomData )
  491. return m_pCustomData->m_vecEquipped[iIdx];
  492. else
  493. return m_EquipInstanceSingleton;
  494. }
  495. // --------------------------------------------------------------------------
  496. // Purpose:
  497. // --------------------------------------------------------------------------
  498. const char *CEconItem::GetCustomDesc() const
  499. {
  500. static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
  501. return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomDesc );
  502. }
  503. // --------------------------------------------------------------------------
  504. // Purpose:
  505. // --------------------------------------------------------------------------
  506. void CEconItem::SetCustomDesc( const char *pDesc )
  507. {
  508. static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
  509. SetCustomNameOrDescAttribute( this, pAttrDef_CustomDesc, pDesc );
  510. }
  511. // --------------------------------------------------------------------------
  512. // Purpose:
  513. // --------------------------------------------------------------------------
  514. bool CEconItem::GetInUse() const
  515. {
  516. return ( m_dirtyBits.m_bInUse ) != 0;
  517. }
  518. // --------------------------------------------------------------------------
  519. // Purpose:
  520. // --------------------------------------------------------------------------
  521. void CEconItem::SetInUse( bool bInUse )
  522. {
  523. if ( bInUse )
  524. {
  525. m_dirtyBits.m_bInUse = 1;
  526. }
  527. else
  528. {
  529. m_dirtyBits.m_bInUse = 0;
  530. }
  531. }
  532. // --------------------------------------------------------------------------
  533. // Purpose:
  534. // --------------------------------------------------------------------------
  535. const GameItemDefinition_t *CEconItem::GetItemDefinition() const
  536. {
  537. const CEconItemDefinition *pRet = GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
  538. const GameItemDefinition_t *pTypedRet = dynamic_cast<const GameItemDefinition_t *>( pRet );
  539. AssertMsg( pRet == pTypedRet, "Item definition of inappropriate type." );
  540. return pTypedRet;
  541. }
  542. // --------------------------------------------------------------------------
  543. // Purpose:
  544. // --------------------------------------------------------------------------
  545. bool CEconItem::IsTradable() const
  546. {
  547. return !m_dirtyBits.m_bInUse
  548. && IEconItemInterface::IsTradable();
  549. }
  550. // --------------------------------------------------------------------------
  551. // Purpose:
  552. // --------------------------------------------------------------------------
  553. void CEconItem::AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept /*= 0xFFFFFFFF*/ )
  554. {
  555. if ( !pOther )
  556. return;
  557. int nOtherUntradability = pOther->GetUntradabilityFlags() & nTradabilityFlagsToAccept;
  558. RTime32 otherUntradableTime = pOther->GetTradableAfterDateTime();
  559. // Become untradable if the other item is untradable
  560. AdoptMoreRestrictedTradability( nOtherUntradability, otherUntradableTime );
  561. }
  562. // --------------------------------------------------------------------------
  563. // Purpose: Given untradability flags and a untradable time, set this item's
  564. // untradability. This does not clear existing untradabilty.
  565. // --------------------------------------------------------------------------
  566. void CEconItem::AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime )
  567. {
  568. static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
  569. static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
  570. if ( !pAttrib_CannotTrade || !pAttrib_TradableAfter )
  571. return;
  572. // We're already permanently untradable. We can't get more untradable, so we're done.
  573. if ( GetUntradabilityFlags() & k_Untradability_Permanent )
  574. return;
  575. if( nTradabilityFlags & k_Untradability_Permanent )
  576. {
  577. SetDynamicAttributeValue( pAttrib_CannotTrade, 0u );
  578. }
  579. else if ( nTradabilityFlags & k_Untradability_Temporary && nUntradableTime > GetTradableAfterDateTime() )
  580. {
  581. // Take the "tradable after date" if it's larger than ours
  582. SetDynamicAttributeValue( pAttrib_TradableAfter, nUntradableTime );
  583. }
  584. }
  585. // --------------------------------------------------------------------------
  586. // Purpose:
  587. // --------------------------------------------------------------------------
  588. bool CEconItem::IsMarketable() const
  589. {
  590. return !m_dirtyBits.m_bInUse
  591. && IEconItemInterface::IsMarketable();
  592. }
  593. // --------------------------------------------------------------------------
  594. // Purpose:
  595. // --------------------------------------------------------------------------
  596. bool CEconItem::IsCommodity() const
  597. {
  598. return !m_dirtyBits.m_bInUse
  599. && IEconItemInterface::IsCommodity();
  600. }
  601. void CEconItem::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
  602. {
  603. Assert( pIterator );
  604. // custom attributes?
  605. for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
  606. {
  607. const attribute_t &attrib = GetDynamicAttributeInternal( i );
  608. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
  609. if ( !pAttrDef )
  610. continue;
  611. if ( !pAttrDef->GetAttributeType()->OnIterateAttributeValue( pIterator, pAttrDef, attrib.m_value ) )
  612. return;
  613. }
  614. // in static attributes?
  615. const CEconItemDefinition *pItemDef = GetItemDefinition();
  616. if ( !pItemDef )
  617. return;
  618. pItemDef->IterateAttributes( pIterator );
  619. }
  620. // --------------------------------------------------------------------------
  621. // Purpose:
  622. // --------------------------------------------------------------------------
  623. style_index_t CEconItem::GetStyle() const
  624. {
  625. static CSchemaAttributeDefHandle pAttrDef_ItemStyleOverride( "item style override" );
  626. float fStyleOverride = 0.f;
  627. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_ItemStyleOverride, &fStyleOverride ) )
  628. {
  629. return fStyleOverride;
  630. }
  631. static CSchemaAttributeDefHandle pAttrDef_ItemStyleStrange( "style changes on strange level" );
  632. uint32 iMaxStyle = 0;
  633. if ( pAttrDef_ItemStyleStrange && FindAttribute( pAttrDef_ItemStyleStrange, &iMaxStyle ) )
  634. {
  635. // Use the strange prefix if the weapon has one.
  636. uint32 unScore = 0;
  637. if ( !FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
  638. return 0;
  639. // What type of event are we tracking and how does it describe itself?
  640. uint32 unKillEaterEventType = 0;
  641. // This will overwrite our default 0 value if we have a value set but leave it if not.
  642. float fKillEaterEventType;
  643. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, GetKillEaterAttr_Type( 0 ), &fKillEaterEventType ) )
  644. {
  645. unKillEaterEventType = fKillEaterEventType;
  646. }
  647. const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( unKillEaterEventType );
  648. if ( !pszLevelingDataName )
  649. {
  650. pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
  651. }
  652. const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, unScore );
  653. if ( !pLevelDef )
  654. return 0;
  655. return Min( pLevelDef->GetLevel(), iMaxStyle );
  656. }
  657. return m_unStyle;
  658. }
  659. const char* CEconItem::FindIconURL( bool bLarge ) const
  660. {
  661. const char* pszSize = bLarge ? "l" : "s";
  662. static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
  663. bool bIsFestivized = pAttrDef_IsFestivized ? FindAttribute( pAttrDef_IsFestivized ) : false;
  664. const CEconItemDefinition *pDef = GetItemDefinition();
  665. // Go through and figure out all the different decorations on
  666. // this item and construct the key to lookup the icon.
  667. // NOTE: These are not currently composable, so they return out when
  668. // a match is found. Once items are more composable, we'll want
  669. // to keep adding all the components together to get the fully
  670. // composed icon (ie. add the strange token, and the festive token, etc.)
  671. const CEconItemPaintKitDefinition* pPaintKitDef = pDef->GetCustomPainkKitDefinition();
  672. if ( pPaintKitDef )
  673. {
  674. float flWear = 0;
  675. GetCustomPaintKitWear( flWear );
  676. int iWearIndex = EconWear_ToIntCategory( flWear );
  677. const char* pszFmtStr = bIsFestivized ? "%s%sw%df" : "%s%sw%d";
  678. const char* pszValue = pDef->GetIconURL( CFmtStr( pszFmtStr, pszSize, pPaintKitDef->GetName(), iWearIndex ) );
  679. if ( pszValue )
  680. return pszValue;
  681. }
  682. const CEconStyleInfo *pStyle = pDef->GetStyleInfo( GetStyle() );
  683. if ( pStyle )
  684. {
  685. const char* pszValue = pDef->GetIconURL( CFmtStr( "%ss%d", pszSize, GetStyle() ) );
  686. if ( pszValue )
  687. return pszValue;
  688. }
  689. if ( bIsFestivized )
  690. {
  691. const char* pszValue = pDef->GetIconURL( CFmtStr( "%sf", pszSize ) );
  692. if ( pszValue )
  693. return pszValue;
  694. }
  695. return pDef->GetIconURL( CFmtStr( "%s", pszSize ) );
  696. }
  697. // --------------------------------------------------------------------------
  698. // Purpose:
  699. // --------------------------------------------------------------------------
  700. const char *CEconItem::GetIconURLSmall() const
  701. {
  702. if ( m_pszSmallIcon == NULL )
  703. {
  704. m_pszSmallIcon = FindIconURL( false );
  705. }
  706. return m_pszSmallIcon;
  707. }
  708. // --------------------------------------------------------------------------
  709. // Purpose:
  710. // --------------------------------------------------------------------------
  711. const char *CEconItem::GetIconURLLarge() const
  712. {
  713. if ( m_pszLargeIcon == NULL )
  714. {
  715. m_pszLargeIcon = FindIconURL( true );
  716. }
  717. return m_pszLargeIcon;
  718. }
  719. // --------------------------------------------------------------------------
  720. // Purpose:
  721. // --------------------------------------------------------------------------
  722. bool CEconItem::IsUsableInCrafting() const
  723. {
  724. return !m_dirtyBits.m_bInUse
  725. && IEconItemInterface::IsUsableInCrafting();
  726. }
  727. #ifdef GC_DLL
  728. // --------------------------------------------------------------------------
  729. // Purpose:
  730. // --------------------------------------------------------------------------
  731. RTime32 CEconItem::GetAssetInfoExpirationCacheExpirationTime() const
  732. {
  733. return GetTradableAfterDateTime();
  734. }
  735. #endif // GC_DLL
  736. // --------------------------------------------------------------------------
  737. // Purpose:
  738. // --------------------------------------------------------------------------
  739. int CEconItem::GetDynamicAttributeCountInternal() const
  740. {
  741. if ( m_pCustomData )
  742. return m_pCustomData->m_vecAttributes.Count();
  743. else
  744. return m_dirtyBits.m_bHasAttribSingleton ? 1 : 0;
  745. }
  746. // --------------------------------------------------------------------------
  747. // Purpose:
  748. // --------------------------------------------------------------------------
  749. CEconItem::attribute_t &CEconItem::GetMutableDynamicAttributeInternal( int iAttrIndexIntoArray )
  750. {
  751. Assert( iAttrIndexIntoArray >= 0 );
  752. Assert( iAttrIndexIntoArray < GetDynamicAttributeCountInternal() );
  753. if ( m_pCustomData )
  754. return m_pCustomData->m_vecAttributes[ iAttrIndexIntoArray ];
  755. else
  756. return m_CustomAttribSingleton;
  757. }
  758. // --------------------------------------------------------------------------
  759. // Purpose:
  760. // --------------------------------------------------------------------------
  761. CEconItem::attribute_t *CEconItem::FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef )
  762. {
  763. Assert( pAttrDef );
  764. if ( m_pCustomData )
  765. {
  766. FOR_EACH_VEC( m_pCustomData->m_vecAttributes, i )
  767. {
  768. if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
  769. return &m_pCustomData->m_vecAttributes[i];
  770. }
  771. }
  772. else if ( m_dirtyBits.m_bHasAttribSingleton )
  773. {
  774. if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
  775. return &m_CustomAttribSingleton;
  776. }
  777. return NULL;
  778. }
  779. // --------------------------------------------------------------------------
  780. // Purpose:
  781. // --------------------------------------------------------------------------
  782. CEconItem::attribute_t &CEconItem::AddDynamicAttributeInternal()
  783. {
  784. if ( 0 == GetDynamicAttributeCountInternal() && NULL == m_pCustomData )
  785. {
  786. m_dirtyBits.m_bHasAttribSingleton = true;
  787. return m_CustomAttribSingleton;
  788. }
  789. else
  790. {
  791. EnsureCustomDataExists();
  792. return m_pCustomData->m_vecAttributes[ m_pCustomData->m_vecAttributes.AddToTail() ];
  793. }
  794. }
  795. // --------------------------------------------------------------------------
  796. void CEconItem::SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime )
  797. {
  798. RTime32 rtExistingTime = 0;
  799. if ( FindAttribute( pAttrDef, &rtExistingTime ) )
  800. {
  801. //we have the attribute already, and see if the value exceeds what we are going to set
  802. if ( rtExistingTime >= rtTime )
  803. return;
  804. }
  805. //it doesn't so we need to update
  806. SetDynamicAttributeValue( pAttrDef, rtTime );
  807. }
  808. // --------------------------------------------------------------------------
  809. // Purpose:
  810. // --------------------------------------------------------------------------
  811. void CEconItem::SetTradableAfterDateTime( RTime32 rtTime )
  812. {
  813. //don't bother if the time is in the past (this also covers the 0 case)
  814. if( rtTime < CRTime::RTime32TimeCur() )
  815. return;
  816. //the attribute we are going to assign
  817. static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
  818. if( !pAttrib_TradableAfter )
  819. return;
  820. //see if we have a STATIC cannot trade attribute (ignore dynamic, because that could change and be used
  821. // to short out the trade restriction).
  822. //This is currently disabled so we can measure whether or not this is beneficial and if the savings justifies the corner case risk this exposes - JohnO 1/12/15
  823. /*
  824. const GameItemDefinition_t* pItemDef = GetItemDefinition();
  825. if( pItemDef )
  826. {
  827. static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
  828. uint32 unCannotTrade = 0;
  829. if( ::FindAttribute( pItemDef, pAttrib_CannotTrade, &unCannotTrade ) )
  830. {
  831. return;
  832. }
  833. }
  834. */
  835. //now set it to the maximum time
  836. SetDynamicMaxTimeAttributeValue( pAttrib_TradableAfter, rtTime );
  837. }
  838. // --------------------------------------------------------------------------
  839. // Purpose:
  840. // --------------------------------------------------------------------------
  841. void CEconItem::RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef )
  842. {
  843. Assert( pAttrDef );
  844. Assert( pAttrDef->GetDefinitionIndex() != INVALID_ATTRIB_DEF_INDEX );
  845. if ( m_pCustomData )
  846. {
  847. for ( int i = 0; i < m_pCustomData->m_vecAttributes.Count(); i++ )
  848. {
  849. if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
  850. {
  851. CEconItemCustomData::FreeAttributeMemory( &m_pCustomData->m_vecAttributes[i] );
  852. m_pCustomData->m_vecAttributes.FastRemove( i );
  853. return;
  854. }
  855. }
  856. }
  857. else if ( m_dirtyBits.m_bHasAttribSingleton )
  858. {
  859. if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
  860. {
  861. CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
  862. m_dirtyBits.m_bHasAttribSingleton = false;
  863. }
  864. }
  865. }
  866. // --------------------------------------------------------------------------
  867. // Purpose:
  868. // --------------------------------------------------------------------------
  869. /*static*/ void CEconItemCustomData::FreeAttributeMemory( CEconItem::attribute_t *pAttrib )
  870. {
  871. Assert( pAttrib );
  872. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( pAttrib->m_unDefinitionIndex );
  873. Assert( pAttrDef );
  874. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  875. Assert( pAttrType );
  876. pAttrType->UnloadEconAttributeValue( &pAttrib->m_value );
  877. }
  878. // --------------------------------------------------------------------------
  879. // Purpose: Frees any unused memory in the internal structures
  880. // --------------------------------------------------------------------------
  881. void CEconItem::Compact()
  882. {
  883. if ( m_pCustomData )
  884. {
  885. m_pCustomData->m_vecAttributes.Compact();
  886. m_pCustomData->m_vecEquipped.Compact();
  887. }
  888. }
  889. #ifdef GC_DLL
  890. // --------------------------------------------------------------------------
  891. // Purpose:
  892. // --------------------------------------------------------------------------
  893. static bool BInsertEquippedInstanceSQL( const CEconItem *pItem, const CEconItem::EquippedInstance_t& equipInst, CSQLAccess& sqlAccess )
  894. {
  895. Assert( pItem );
  896. const char *pszUpdateOrInsert = "MERGE EquipInstance AS tblEquipInstance "
  897. "USING (SELECT ? AS AccountID, ? AS ClassID, ? AS SlotID) AS tblEquipInstance_NewRow "
  898. "ON (tblEquipInstance.AccountID = tblEquipInstance_NewRow.AccountID AND "
  899. "tblEquipInstance.ClassID = tblEquipInstance_NewRow.ClassID AND "
  900. "tblEquipInstance.SlotID = tblEquipInstance_NewRow.SlotID) "
  901. "WHEN MATCHED THEN "
  902. "UPDATE SET tblEquipInstance.ItemID = ? "
  903. "WHEN NOT MATCHED BY TARGET THEN "
  904. "INSERT (AccountID, ClassID, SlotID, ItemID) VALUES (?, ?, ?, ?);";
  905. sqlAccess.AddBindParam( pItem->GetAccountID() ); // USING (SELECT ... AS AccountID)
  906. sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // USING (SELECT ... AS ClassID)
  907. sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // USING (SELECT ... AS SlotID)
  908. sqlAccess.AddBindParam( pItem->GetItemID() ); // UPDATE SET ...
  909. sqlAccess.AddBindParam( pItem->GetAccountID() ); // INSERT (AccountID)
  910. sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // INSERT (ClassID)
  911. sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // INSERT (SlotID)
  912. sqlAccess.AddBindParam( pItem->GetItemID() ); // INSERT (ItemID)
  913. return sqlAccess.BYieldingExecute( "BYieldingOnHandledPeriodicScoreTimePeriod", pszUpdateOrInsert );
  914. }
  915. // --------------------------------------------------------------------------
  916. // Purpose:
  917. // --------------------------------------------------------------------------
  918. static bool BInsertAllEquippedInstancesSQL( const CEconItem *pItem, CSQLAccess& sqlAccess )
  919. {
  920. Assert( pItem );
  921. for ( int i = 0; i < pItem->GetEquippedInstanceCount(); i++ )
  922. {
  923. if ( !BInsertEquippedInstanceSQL( pItem, pItem->GetEquippedInstance( i ), sqlAccess ) )
  924. return false;
  925. }
  926. return true;
  927. }
  928. // --------------------------------------------------------------------------
  929. // Purpose: Adds non-Item-table inserts to the SQL insert for this object
  930. // --------------------------------------------------------------------------
  931. bool CEconItem::BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess )
  932. {
  933. // @note Tom Bui: This could be simplified greatly, but let's see if it is an issue
  934. CSchItem item;
  935. SerializeToSchemaItem( item );
  936. if( !CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &item ) )
  937. return false;
  938. // attributes get to written to any number of joined tables based on type
  939. for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
  940. {
  941. const attribute_t &attrib = GetDynamicAttributeInternal( i );
  942. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
  943. if ( !pAttrDef )
  944. {
  945. EmitError( SPEW_GC, "CEconItem::BYieldingAddInsertToTransaction(): attempt to insert unknown attribute ID %d.\n", attrib.m_unDefinitionIndex );
  946. return false;
  947. }
  948. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  949. Assert( pAttrType );
  950. CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
  951. pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, attrib.m_value, pAttrRecord.Get() );
  952. if( !sqlAccess.BYieldingInsertRecord( pAttrRecord.Get() ) )
  953. return false;
  954. }
  955. // currently-equipped positions get written to a joined table
  956. if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
  957. return false;
  958. // add audit record for the create
  959. CEconItemDefinition *pItemDef = GEconManager()->GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
  960. if ( pItemDef )
  961. {
  962. const char* pDatabaseAuditTableName = pItemDef->GetDatabaseAuditTableName();
  963. if ( pDatabaseAuditTableName )
  964. {
  965. CFmtStr1024 sStatement;
  966. sStatement.sprintf( "INSERT INTO %s (ItemID) VALUES (%llu)", pDatabaseAuditTableName, m_ulID );
  967. uint32 nRows;
  968. bool bRet = sqlAccess.BYieldingExecute( sStatement, sStatement, &nRows );
  969. if ( bRet == false )
  970. {
  971. CSteamID steamID( m_unAccountID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
  972. EmitError( SPEW_GC, "Failed to add item audit to table %s for %s and item %llu.\n", pDatabaseAuditTableName, steamID.Render(), m_ulID );
  973. return false;
  974. }
  975. }
  976. }
  977. return true;
  978. }
  979. //----------------------------------------------------------------------------
  980. // Purpose:
  981. //----------------------------------------------------------------------------
  982. static bool BRemoveEquipInstancesFromSQL( CSQLAccess& sqlAccess, uint32 unAccountID, itemid_t unItemID )
  983. {
  984. CSchEquipInstance schEquipInstanceWhere;
  985. schEquipInstanceWhere.m_unAccountID = unAccountID;
  986. schEquipInstanceWhere.m_ulItemID = unItemID;
  987. return sqlAccess.BYieldingDeleteRecords( schEquipInstanceWhere, CSET_2_COL( CSchEquipInstance, k_iField_unAccountID, k_iField_ulItemID ) );
  988. }
  989. //----------------------------------------------------------------------------
  990. // Purpose: Writes the non-PK fields on the object to the database
  991. //----------------------------------------------------------------------------
  992. bool CEconItem::BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
  993. {
  994. Assert( sqlAccess.BInTransaction() );
  995. if( IsForeign() )
  996. {
  997. CSchForeignItem schForeignItem;
  998. schForeignItem.m_ulID = GetItemID();
  999. schForeignItem.m_unInventory = GetInventoryToken();
  1000. return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schForeignItem, CSET_1_COL( CSchForeignItem, k_iField_unInventory ) );
  1001. }
  1002. // Non-foreign items.
  1003. CSchItem item;
  1004. SerializeToSchemaItem( item );
  1005. CColumnSet csDatabaseDirty( item.GetPRecordInfo() );
  1006. GetDirtyColumnSet( fields, csDatabaseDirty );
  1007. // Write out base item properties if any are dirty. It's possible to get here with only dirty attributes,
  1008. // in which case we want to neither write to our item properties nor abort here if the write fails.
  1009. if ( !csDatabaseDirty.IsEmpty() && !CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &item, csDatabaseDirty ) )
  1010. return false;
  1011. // Write out additional attribute properties?
  1012. FOR_EACH_VEC( fields, i )
  1013. {
  1014. uint32 iFieldID = fields[i] & 0x0000ffff, // low 16 bits are the ID
  1015. iFieldIDType = fields[i] & 0xffff0000; // high 16 bits are the ID type
  1016. if ( iFieldIDType == kUpdateFieldIDType_FieldID )
  1017. {
  1018. // Most item field IDs will be updated above in SerializeToSchemaItem(). Anything that needs custom
  1019. // writing behavior should handle it here.
  1020. // If we've changed our equip state, remove all of our previous equipped instances (if any) and write
  1021. // out our new rows (if any).
  1022. if ( iFieldID == CSOEconItem::kEquippedStateFieldNumber )
  1023. {
  1024. if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetID() ) )
  1025. return false;
  1026. if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
  1027. return false;
  1028. }
  1029. }
  1030. else if ( iFieldIDType == kUpdateFieldIDType_AttributeID )
  1031. {
  1032. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( iFieldID );
  1033. Assert( pAttrDef );
  1034. // Did we dirty an attribute and then remove it entirely?
  1035. const attribute_t *pEconAttr = FindDynamicAttributeInternal( pAttrDef );
  1036. if ( !pEconAttr )
  1037. continue;
  1038. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  1039. Assert( pAttrType );
  1040. CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
  1041. pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, pEconAttr->m_value, pAttrRecord.Get() );
  1042. CColumnSet csWhere( pAttrRecord->GetPRecordInfo() );
  1043. csWhere.BAddColumn( 0 );
  1044. csWhere.BAddColumn( 1 );
  1045. CColumnSet csUpdate( CColumnSet::Inverse( csWhere ) );
  1046. Assert( !csUpdate.IsEmpty() );
  1047. if ( !sqlAccess.BYieldingUpdateRecord( *pAttrRecord.Get(), csWhere, csUpdate ) )
  1048. return false;
  1049. }
  1050. // Our field ID is something we don't recognize.
  1051. else
  1052. {
  1053. AssertMsg( false, "Unknown field ID type in CEconItem::BYieldingAddWriteToTransaction()." );
  1054. }
  1055. }
  1056. return true;
  1057. }
  1058. // --------------------------------------------------------------------------
  1059. // Purpose: "deletes" an object by setting its owner to 0.
  1060. // --------------------------------------------------------------------------
  1061. bool CEconItem::BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess )
  1062. {
  1063. Assert( sqlAccess.BInTransaction() );
  1064. // we never remove econ items in the server
  1065. sqlAccess.AddBindParam( GetItemID() );
  1066. if( !sqlAccess.BYieldingExecute( NULL, "UPDATE Item SET AccountId = 0 WHERE ID = ?" ) )
  1067. return false;
  1068. if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetItemID() ) )
  1069. return false;
  1070. return true;
  1071. }
  1072. // --------------------------------------------------------------------------
  1073. // Purpose: Load the interior item from the database.
  1074. // --------------------------------------------------------------------------
  1075. bool CEconItem::BYieldingLoadInteriorItem()
  1076. {
  1077. static CSchemaAttributeDefHandle pAttribLow( "referenced item id low" );
  1078. static CSchemaAttributeDefHandle pAttribHigh( "referenced item id high" );
  1079. // see if it's already loaded
  1080. if ( m_pCustomData && m_pCustomData->m_pInteriorItem )
  1081. return true;
  1082. // we require the low 32 bits...
  1083. uint32 unAttribValueBitsLow;
  1084. if ( !FindAttribute( pAttribLow, &unAttribValueBitsLow ) )
  1085. return false;
  1086. // ...but default the high 32 bits to 0 if not present
  1087. uint32 unAttribValueBitsHigh = 0;
  1088. FindAttribute( pAttribHigh, &unAttribValueBitsHigh ); // return value ignored
  1089. COMPILE_TIME_ASSERT( sizeof( uint64 ) == sizeof( itemid_t ) );
  1090. uint64 wrappedItemId = ( uint64( unAttribValueBitsHigh ) << 32 ) | uint64( unAttribValueBitsLow );
  1091. // allocate scratch object to try and fill with DB contents
  1092. CEconItem *pNewItem = new CEconItem;
  1093. if ( !pNewItem->BYieldingSerializeFromDatabase( wrappedItemId ) )
  1094. {
  1095. delete pNewItem;
  1096. return false;
  1097. }
  1098. // DB load succeeded -- make sure if we're treating this item ID as an interior item, it isn't
  1099. // currently owned by someone. this is a non-fatal error (it could happen if support rolls back
  1100. // transactions in weird ways) but it probably means we have something weird going on
  1101. if ( pNewItem->GetAccountID() != 0 )
  1102. {
  1103. // ...
  1104. }
  1105. // everything loaded alright so hook our reference up
  1106. EnsureCustomDataExists();
  1107. m_pCustomData->m_pInteriorItem = pNewItem;
  1108. return true;
  1109. }
  1110. // --------------------------------------------------------------------------
  1111. // Purpose: Directly set the interior item
  1112. // --------------------------------------------------------------------------
  1113. void CEconItem::SetInteriorItem( CEconItem* pInteriorItem )
  1114. {
  1115. EnsureCustomDataExists();
  1116. m_pCustomData->m_pInteriorItem = pInteriorItem;
  1117. }
  1118. // --------------------------------------------------------------------------
  1119. // Purpose:
  1120. // --------------------------------------------------------------------------
  1121. CEconItem* CEconItem::YieldingGetInteriorItem()
  1122. {
  1123. if ( !m_pCustomData || !m_pCustomData->m_pInteriorItem )
  1124. {
  1125. if ( !BYieldingLoadInteriorItem() )
  1126. {
  1127. Assert( !GetInteriorItem() );
  1128. }
  1129. }
  1130. return GetInteriorItem();
  1131. }
  1132. #endif // GC_DLL
  1133. CEconItem* CEconItem::GetInteriorItem()
  1134. {
  1135. return m_pCustomData ? m_pCustomData->m_pInteriorItem : NULL;
  1136. }
  1137. // --------------------------------------------------------------------------
  1138. // Purpose: This item has been traded. Give it an opportunity to update any internal
  1139. // properties in response.
  1140. // --------------------------------------------------------------------------
  1141. void CEconItem::OnTraded( uint32 unTradabilityDelaySeconds )
  1142. {
  1143. // if Steam wants us to impose a tradability delay on the item
  1144. if ( unTradabilityDelaySeconds != 0 )
  1145. {
  1146. RTime32 rtTradableAfter = ( ( CRTime::RTime32TimeCur() / k_nSecondsPerDay ) * k_nSecondsPerDay ) + unTradabilityDelaySeconds;
  1147. SetTradableAfterDateTime( rtTradableAfter );
  1148. }
  1149. else
  1150. {
  1151. // If we have a "tradable after date" attribute and we were just traded, remove the date
  1152. // limit as we're obviously past it.
  1153. static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
  1154. RemoveDynamicAttribute( pAttrib_TradableAfter );
  1155. }
  1156. OnTransferredOwnership();
  1157. }
  1158. // --------------------------------------------------------------------------
  1159. // Purpose: Ownership of this item has changed, so do whatever things are necessary
  1160. // --------------------------------------------------------------------------
  1161. void CEconItem::OnTransferredOwnership()
  1162. {
  1163. // Reset all our strange scores.
  1164. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  1165. {
  1166. const CEconItemAttributeDefinition *pAttrDef = GetKillEaterAttr_Score(i);
  1167. // Skip over any attributes our schema doesn't understand. We ideally wouldn't ever
  1168. // have this happen but if it does we don't want to ignore other valid attributes.
  1169. if ( !pAttrDef )
  1170. continue;
  1171. // Ignore any attributes we don't have on this item.
  1172. if ( !FindAttribute( pAttrDef ) )
  1173. continue;
  1174. // Zero out the value of this stat attribute.
  1175. SetDynamicAttributeValue( pAttrDef, 0u );
  1176. }
  1177. // Free accounts have the ability to trade any item out that they received in a trade.
  1178. SetFlag( kEconItemFlag_CanBeTradedByFreeAccounts );
  1179. }
  1180. // --------------------------------------------------------------------------
  1181. // Purpose: This item has been traded. Give it an opportunity to update any internal
  1182. // properties in response.
  1183. // --------------------------------------------------------------------------
  1184. void CEconItem::OnReceivedFromMarket( bool bFromRollback )
  1185. {
  1186. OnTransferredOwnership();
  1187. #ifdef GC_DLL
  1188. static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
  1189. if ( !bFromRollback && GEconManager()->GetTradableAfterDurationForPurchase() != 0 )
  1190. {
  1191. // Add "tradable after date" attribute for items received from the market, since the funds
  1192. // used to purchase the item may be untrusted
  1193. RTime32 rtTradableAfter = GEconManager()->GetTradableAfterDateForPurchase();
  1194. SetDynamicAttributeValue( pAttrib_TradableAfter, rtTradableAfter );
  1195. }
  1196. // Do we need to remove this attribute on rollback?
  1197. /*else
  1198. {
  1199. RemoveDynamicAttribute( pAttrib_TradableAfter );
  1200. }*/
  1201. #endif // GC_DLL
  1202. }
  1203. // --------------------------------------------------------------------------
  1204. // Purpose: Parses the bits required to create a econ item from the message.
  1205. // Overloaded to include support for attributes.
  1206. // --------------------------------------------------------------------------
  1207. bool CEconItem::BParseFromMessage( const CUtlBuffer & buffer )
  1208. {
  1209. CSOEconItem msgItem;
  1210. if( !msgItem.ParseFromArray( buffer.Base(), buffer.TellMaxPut() ) )
  1211. return false;
  1212. DeserializeFromProtoBufItem( msgItem );
  1213. return true;
  1214. }
  1215. // --------------------------------------------------------------------------
  1216. // Purpose: Parses the bits required to create a econ item from the message.
  1217. // Overloaded to include support for attributes.
  1218. // --------------------------------------------------------------------------
  1219. bool CEconItem::BParseFromMessage( const std::string &buffer )
  1220. {
  1221. CSOEconItem msgItem;
  1222. if( !msgItem.ParseFromString( buffer ) )
  1223. return false;
  1224. DeserializeFromProtoBufItem( msgItem );
  1225. return true;
  1226. }
  1227. //----------------------------------------------------------------------------
  1228. // Purpose: Overrides all the fields in msgLocal that are present in the
  1229. // network message
  1230. //----------------------------------------------------------------------------
  1231. bool CEconItem::BUpdateFromNetwork( const CSharedObject & objUpdate )
  1232. {
  1233. const CEconItem & econObjUpdate = (const CEconItem &)objUpdate;
  1234. *this = econObjUpdate;
  1235. return true;
  1236. }
  1237. #ifdef GC
  1238. //----------------------------------------------------------------------------
  1239. // Purpose: Adds the relevant bits to update this object to the message. This
  1240. // must include any relevant information about which fields are being
  1241. // updated. This is called once for all subscribers.
  1242. //----------------------------------------------------------------------------
  1243. bool CEconItem::BAddToMessage( CUtlBuffer & bufOutput ) const
  1244. {
  1245. VPROF_BUDGET( "CEconItem::BAddToMessage::CUtlBuffer", VPROF_BUDGETGROUP_STEAM );
  1246. // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
  1247. CSOEconItem msg;
  1248. SerializeToProtoBufItem( msg );
  1249. return CProtoBufSharedObjectBase::SerializeToBuffer( msg, bufOutput );
  1250. }
  1251. //----------------------------------------------------------------------------
  1252. // Purpose: Adds the relevant bits to update this object to the message. This
  1253. // must include any relevant information about which fields are being
  1254. // updated. This is called once for all subscribers.
  1255. //----------------------------------------------------------------------------
  1256. bool CEconItem::BAddToMessage( std::string *pBuffer ) const
  1257. {
  1258. VPROF_BUDGET( "CEconItem::BAddToMessage::std::string", VPROF_BUDGETGROUP_STEAM );
  1259. // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
  1260. CSOEconItem msg;
  1261. SerializeToProtoBufItem( msg );
  1262. return msg.SerializeToString( pBuffer );
  1263. }
  1264. //----------------------------------------------------------------------------
  1265. // Purpose: Adds just the item ID to the message so that the client can find
  1266. // which item to destroy
  1267. //----------------------------------------------------------------------------
  1268. bool CEconItem::BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const
  1269. {
  1270. CSOEconItem msgItem;
  1271. msgItem.set_id( GetItemID() );
  1272. CProtoBufSharedObjectBase::SerializeToBuffer( msgItem, bufDestroy );
  1273. return true;
  1274. }
  1275. //----------------------------------------------------------------------------
  1276. // Purpose: Adds just the item ID to the message so that the client can find
  1277. // which item to destroy
  1278. //----------------------------------------------------------------------------
  1279. bool CEconItem::BAddDestroyToMessage( std::string *pBuffer ) const
  1280. {
  1281. CSOEconItem msgItem;
  1282. msgItem.set_id( GetItemID() );
  1283. return msgItem.SerializeToString( pBuffer );
  1284. }
  1285. #endif //GC
  1286. //----------------------------------------------------------------------------
  1287. // Purpose: Returns true if this is less than than the object in soRHS. This
  1288. // comparison is deterministic, but it may not be pleasing to a user
  1289. // since it is just going to compare raw memory. If you need a sort
  1290. // that is user-visible you will need to do it at a higher level that
  1291. // actually knows what the data in these objects means.
  1292. //----------------------------------------------------------------------------
  1293. bool CEconItem::BIsKeyLess( const CSharedObject & soRHS ) const
  1294. {
  1295. Assert( GetTypeID() == soRHS.GetTypeID() );
  1296. const CEconItem & soSchemaRHS = (const CEconItem &)soRHS;
  1297. return m_ulID < soSchemaRHS.m_ulID;
  1298. }
  1299. //----------------------------------------------------------------------------
  1300. // Purpose: Copy the data from the specified schema shared object into this.
  1301. // Both objects must be of the same type.
  1302. //----------------------------------------------------------------------------
  1303. void CEconItem::Copy( const CSharedObject & soRHS )
  1304. {
  1305. *this = (const CEconItem &)soRHS;
  1306. }
  1307. //----------------------------------------------------------------------------
  1308. // Purpose: Dumps diagnostic information about the shared object
  1309. //----------------------------------------------------------------------------
  1310. void CEconItem::Dump() const
  1311. {
  1312. CSOEconItem msgItem;
  1313. SerializeToProtoBufItem( msgItem );
  1314. CProtoBufSharedObjectBase::Dump( msgItem );
  1315. }
  1316. //----------------------------------------------------------------------------
  1317. // Purpose: Return short, identifying string about the object
  1318. //----------------------------------------------------------------------------
  1319. CUtlString CEconItem::GetDebugString() const
  1320. {
  1321. CUtlString result;
  1322. result.Format( "[CEconItem: ID=%llu, DefIdx=%d]", GetItemID(), GetDefinitionIndex() );
  1323. return result;
  1324. }
  1325. #ifdef GC
  1326. //-----------------------------------------------------------------------------
  1327. // Purpose: Deserializes an item from a KV object
  1328. // Input: pKVItem - Pointer to the KV structure that represents an item
  1329. // schema - Econ item schema used for decoding human readable names
  1330. // pVecErrors - Pointer to a vector where human readable errors will
  1331. // be added
  1332. // Output: True if the item deserialized successfully, false otherwise
  1333. //-----------------------------------------------------------------------------
  1334. bool CEconItem::BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
  1335. {
  1336. Assert( pVecErrors );
  1337. Assert( NULL != pKVItem );
  1338. if ( NULL == pKVItem )
  1339. return false;
  1340. // The basic properties
  1341. SetItemID( pKVItem->GetUint64( "ID", INVALID_ITEM_ID ) );
  1342. SetInventoryToken( pKVItem->GetInt( "InventoryPos", GetUnacknowledgedPositionFor(UNACK_ITEM_DROPPED) ) ); // Start by assuming it's a drop
  1343. SetQuantity( pKVItem->GetInt( "Quantity", 1 ) );
  1344. // Look up the following properties based on names from the schema
  1345. const char *pchDefName = pKVItem->GetString( "DefName" );
  1346. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pchDefName );
  1347. if( !pItemDef )
  1348. {
  1349. pVecErrors->AddToTail( CUtlString( CFmtStr( "Item definition \"%s\" not found", pchDefName ) ) );
  1350. // we can't do any reasonable validation with no item def, so just stop here
  1351. return false;
  1352. }
  1353. SetDefinitionIndex( pItemDef->GetDefinitionIndex() );
  1354. const char *pchQualityName = pKVItem->GetString( "QualityName" );
  1355. if( !pchQualityName || ! *pchQualityName )
  1356. {
  1357. // set the default quality for the definition
  1358. if( pItemDef->GetQuality() == k_unItemQuality_Any )
  1359. {
  1360. pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality was not specified and this item def doesn't define one either." ) ) );
  1361. }
  1362. else
  1363. {
  1364. SetQuality( pItemDef->GetQuality() );
  1365. }
  1366. }
  1367. else if ( !GetItemSchema()->BGetItemQualityFromName( pchQualityName, &m_nQuality ) || k_unItemQuality_Any == GetQuality() )
  1368. {
  1369. pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality \"%s\" not found", pchQualityName ) ) );
  1370. }
  1371. // make sure the level is sane
  1372. SetItemLevel( pKVItem->GetInt( "Level", pItemDef->GetMinLevel() ) );
  1373. // read the flags
  1374. uint8 unFlags = GetFlags();
  1375. if( pKVItem->GetInt( "flag_cannot_trade", 0 ) )
  1376. {
  1377. unFlags |= kEconItemFlag_CannotTrade;
  1378. }
  1379. else
  1380. {
  1381. unFlags = unFlags & ~kEconItemFlag_CannotTrade;
  1382. }
  1383. if( pKVItem->GetInt( "flag_cannot_craft", 0 ) )
  1384. {
  1385. unFlags |= kEconItemFlag_CannotBeUsedInCrafting;
  1386. }
  1387. else
  1388. {
  1389. unFlags = unFlags & ~kEconItemFlag_CannotBeUsedInCrafting;
  1390. }
  1391. if( pKVItem->GetInt( "flag_non_economy", 0 ) )
  1392. {
  1393. unFlags |= kEconItemFlag_NonEconomy;
  1394. }
  1395. else
  1396. {
  1397. unFlags = unFlags & ~kEconItemFlag_NonEconomy;
  1398. }
  1399. SetFlag( unFlags );
  1400. // Deserialize the attributes
  1401. KeyValues *pKVAttributes = pKVItem->FindKey( "Attributes" );
  1402. if ( NULL != pKVAttributes )
  1403. {
  1404. FOR_EACH_SUBKEY( pKVAttributes, pKVAttr )
  1405. {
  1406. // Try to load each line into an attribute in memory. It's possible that if we fail to successfully
  1407. // load some attribute contents here we'll leak small amounts of memory, but if that happens we're
  1408. // going to fail to start up anyway so we don't really care.
  1409. static_attrib_t staticAttrib;
  1410. if ( !staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVAttr, pVecErrors ) )
  1411. continue;
  1412. const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition();
  1413. Assert( pAttrDef );
  1414. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  1415. Assert( pAttrType );
  1416. // Load the attribute contents into memory on the item.
  1417. pAttrType->LoadEconAttributeValue( this, pAttrDef, staticAttrib.m_value );
  1418. // Free up our temp loading memory.
  1419. pAttrType->UnloadEconAttributeValue( &staticAttrib.m_value );
  1420. }
  1421. }
  1422. return ( NULL == pVecErrors || 0 == pVecErrors->Count() );
  1423. }
  1424. // --------------------------------------------------------------------------
  1425. // Purpose:
  1426. // --------------------------------------------------------------------------
  1427. bool CEconItem::BYieldingSerializeFromDatabase( itemid_t ulItemID )
  1428. {
  1429. static CColumnSet csItem( CSET_FULL( CSchItem ) );
  1430. enum
  1431. {
  1432. kQueryResultSet_SingleItem_Item = 0,
  1433. kQueryResultSet_SingleItem_Attributes_Base = 1,
  1434. kQueryResultSet_SingleItemCount
  1435. };
  1436. static uint32 sExpectedResults = 0;
  1437. static CFmtStrMax sLoadQuery;
  1438. if ( 0 == sLoadQuery.Length() )
  1439. {
  1440. sLoadQuery.Append(
  1441. "DECLARE @ItemID BIGINT "
  1442. "SET @ItemID = ? " );
  1443. // Load the item itself.
  1444. TSQLCmdStr sStatement;
  1445. BuildSelectStatementText( &sStatement, csItem );
  1446. sStatement.Append( " WHERE ID = @ItemID " );
  1447. sLoadQuery.Append( sStatement );
  1448. DbgVerify( kQueryResultSet_SingleItem_Item == sExpectedResults++ );
  1449. // Load any associated attributes.
  1450. DbgVerify( kQueryResultSet_SingleItem_Attributes_Base == sExpectedResults++ );
  1451. {
  1452. const CUtlVector<attr_type_t>& vecAttributeTypes = GetItemSchema()->GetAttributeTypes();
  1453. FOR_EACH_VEC( vecAttributeTypes, i )
  1454. {
  1455. // We could actually skip loading the "ItemID" column here because we're only loading one item
  1456. // of data, but we would have to do some work because we don't actually know the CSch types here
  1457. // so it doesn't seem worth it. We'll have to update the LoadSQLAttributesToEconItem() code if
  1458. // we ever do this.
  1459. BuildSelectStatementText( &sStatement, vecAttributeTypes[i].m_pAttrType->GetFullColumnSet() );
  1460. sStatement.Append( " WHERE ItemID = @ItemID " );
  1461. sLoadQuery.Append( sStatement );
  1462. }
  1463. }
  1464. }
  1465. Assert( kQueryResultSet_SingleItemCount == sExpectedResults );
  1466. // Load item and associated attributes from SQL.
  1467. CSQLAccess sqlAccess;
  1468. sqlAccess.AddBindParam( ulItemID );
  1469. if ( !sqlAccess.BYieldingExecute( "CEconItem::BYieldingSerializeFromDatabase", sLoadQuery.Get() ) )
  1470. {
  1471. EmitError( SPEW_GC, __FUNCTION__ ": failed to execute SQL load for item ID %llu\n", ulItemID );
  1472. return false;
  1473. }
  1474. // ...
  1475. if ( sqlAccess.GetResultSetCount() < sExpectedResults )
  1476. {
  1477. EmitError( SPEW_GC, __FUNCTION__ ": unable to interior item ID %llu. Got %d results back, but expected %d!\n", ulItemID, sqlAccess.GetResultSetCount(), sExpectedResults );
  1478. return false;
  1479. }
  1480. // Make sure we only got one item back.
  1481. const uint32 nRowCount = sqlAccess.GetResultSetRowCount( kQueryResultSet_SingleItem_Item );
  1482. if ( nRowCount != 1 )
  1483. {
  1484. EmitError( SPEW_GC, __FUNCTION__ ": invalid number of rows returned from SQL loading item ID %llu: %d\n", ulItemID, nRowCount );
  1485. return false;
  1486. }
  1487. // Make sure we understand what sort of item this is. We can't make any forward progress loading if we don't know
  1488. // what definition its attached to.
  1489. CSchItem schItem;
  1490. sqlAccess.GetResultRecord( kQueryResultSet_SingleItem_Item, 0 ).BWriteToRecord( &schItem, csItem );
  1491. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( schItem.m_unDefIndex );
  1492. if ( !pItemDef )
  1493. {
  1494. EmitError( SPEW_GC, __FUNCTION__ ": unable to find item definition %d for item %llu!\n", schItem.m_unDefIndex, schItem.m_ulID );
  1495. return false;
  1496. }
  1497. // We have everything we need to complete the load here so default initialize -- free up attribute memory, current
  1498. // state, etc.
  1499. *this = CEconItem();
  1500. // Deserialize from DB schema.
  1501. DeserializeFromSchemaItem( schItem );
  1502. // Load attributes.
  1503. CEconManager::CEconItemAttributeLoader AttributeLoader;
  1504. AttributeLoader.LoadSQLAttributesToEconItem( this, sqlAccess, kQueryResultSet_SingleItem_Attributes_Base );
  1505. // Done.
  1506. Compact();
  1507. return true;
  1508. }
  1509. // --------------------------------------------------------------------------
  1510. // Purpose:
  1511. // --------------------------------------------------------------------------
  1512. void CEconItem::SerializeToSchemaItem( CSchItem &item ) const
  1513. {
  1514. VPROF_BUDGET( "CEconItem::SerializeToSchemaItem()", VPROF_BUDGETGROUP_STEAM );
  1515. item.m_ulID = m_ulID;
  1516. item.m_ulOriginalID = GetOriginalID();
  1517. item.m_unAccountID = m_unAccountID;
  1518. item.m_unDefIndex = m_unDefIndex;
  1519. item.m_unLevel = m_unLevel;
  1520. item.m_nQuality = m_nQuality;
  1521. item.m_unInventory = m_unInventory;
  1522. item.m_unQuantity = GetQuantity();
  1523. item.m_unFlags = m_unFlags;
  1524. item.m_unOrigin = m_unOrigin;
  1525. item.m_unStyle = m_unStyle;
  1526. }
  1527. // --------------------------------------------------------------------------
  1528. // Purpose:
  1529. // --------------------------------------------------------------------------
  1530. void CEconItem::DeserializeFromSchemaItem( const CSchItem &item )
  1531. {
  1532. VPROF_BUDGET( "CEconItem::DeserializeFromSchemaItem()", VPROF_BUDGETGROUP_STEAM );
  1533. m_ulID = item.m_ulID;
  1534. SetOriginalID( item.m_ulOriginalID ? item.m_ulOriginalID : item.m_ulID );
  1535. m_unAccountID = item.m_unAccountID;
  1536. m_unDefIndex = item.m_unDefIndex;
  1537. m_unLevel = item.m_unLevel;
  1538. m_nQuality = item.m_nQuality;
  1539. m_unInventory = item.m_unInventory;
  1540. SetQuantity( item.m_unQuantity );
  1541. m_unFlags = item.m_unFlags;
  1542. m_unOrigin = item.m_unOrigin;
  1543. m_unStyle = item.m_unStyle;
  1544. // set name if any, or remove if non-existent
  1545. SetCustomName( READ_VAR_CHAR_FIELD( item, m_VarCharCustomName ) );
  1546. // set desc if any, or remove if non-existent
  1547. SetCustomDesc( READ_VAR_CHAR_FIELD( item, m_VarCharCustomDesc ) );
  1548. }
  1549. #endif // GC
  1550. // --------------------------------------------------------------------------
  1551. // Purpose:
  1552. // --------------------------------------------------------------------------
  1553. void CEconItem::SerializeToProtoBufItem( CSOEconItem &msgItem ) const
  1554. {
  1555. VPROF_BUDGET( "CEconItem::SerializeToProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
  1556. msgItem.set_id( m_ulID );
  1557. if( m_ulID != GetOriginalID() )
  1558. msgItem.set_original_id( GetOriginalID() );
  1559. msgItem.set_account_id( m_unAccountID );
  1560. msgItem.set_def_index( m_unDefIndex );
  1561. msgItem.set_level( m_unLevel );
  1562. msgItem.set_quality( m_nQuality );
  1563. msgItem.set_inventory( m_unInventory );
  1564. msgItem.set_quantity( GetQuantity() );
  1565. msgItem.set_flags( m_unFlags );
  1566. msgItem.set_origin( m_unOrigin );
  1567. msgItem.set_style( m_unStyle );
  1568. msgItem.set_in_use( m_dirtyBits.m_bInUse );
  1569. for( int nAttr = 0; nAttr < GetDynamicAttributeCountInternal(); nAttr++ )
  1570. {
  1571. const attribute_t & attr = GetDynamicAttributeInternal( nAttr );
  1572. // skip over attributes we don't understand
  1573. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
  1574. if ( !pAttrDef )
  1575. continue;
  1576. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  1577. Assert( pAttrType );
  1578. CSOEconItemAttribute *pMsgAttr = msgItem.add_attribute();
  1579. pMsgAttr->set_def_index( attr.m_unDefinitionIndex );
  1580. std::string sBytes;
  1581. pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
  1582. pMsgAttr->set_value_bytes( sBytes );
  1583. }
  1584. msgItem.set_contains_equipped_state_v2( true );
  1585. for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
  1586. {
  1587. const EquippedInstance_t &instance = GetEquippedInstance( i );
  1588. CSOEconItemEquipped *pMsgEquipped = msgItem.add_equipped_state();
  1589. pMsgEquipped->set_new_class( instance.m_unEquippedClass );
  1590. pMsgEquipped->set_new_slot( instance.m_unEquippedSlot );
  1591. }
  1592. if ( m_pCustomData )
  1593. {
  1594. const char *pszCustomName = GetCustomName();
  1595. if ( pszCustomName )
  1596. {
  1597. msgItem.set_custom_name( pszCustomName );
  1598. }
  1599. const char *pszCustomDesc = GetCustomDesc();
  1600. if ( pszCustomDesc )
  1601. {
  1602. msgItem.set_custom_desc( pszCustomDesc );
  1603. }
  1604. const CEconItem *pInteriorItem = GetInteriorItem();
  1605. if ( pInteriorItem )
  1606. {
  1607. CSOEconItem *pMsgInteriorItem = msgItem.mutable_interior_item();
  1608. pInteriorItem->SerializeToProtoBufItem( *pMsgInteriorItem );
  1609. }
  1610. }
  1611. }
  1612. // --------------------------------------------------------------------------
  1613. // Purpose:
  1614. // --------------------------------------------------------------------------
  1615. void CEconItem::DeserializeFromProtoBufItem( const CSOEconItem &msgItem )
  1616. {
  1617. VPROF_BUDGET( "CEconItem::DeserializeFromProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
  1618. // Start by resetting
  1619. SAFE_DELETE( m_pCustomData );
  1620. m_dirtyBits.m_bHasAttribSingleton = false;
  1621. m_dirtyBits.m_bHasEquipSingleton = false;
  1622. // Now copy from the message
  1623. m_ulID = msgItem.id();
  1624. SetOriginalID( msgItem.has_original_id() ? msgItem.original_id() : m_ulID );
  1625. m_unAccountID = msgItem.account_id();
  1626. m_unDefIndex = msgItem.def_index();
  1627. m_unLevel = msgItem.level();
  1628. m_nQuality = msgItem.quality();
  1629. m_unInventory = msgItem.inventory();
  1630. SetQuantity( msgItem.quantity() );
  1631. m_unFlags = msgItem.flags();
  1632. m_unOrigin = msgItem.origin();
  1633. m_unStyle = msgItem.style();
  1634. m_dirtyBits.m_bInUse = msgItem.in_use() ? 1 : 0;
  1635. // set name if any
  1636. if( msgItem.has_custom_name() )
  1637. {
  1638. SetCustomName( msgItem.custom_name().c_str() );
  1639. }
  1640. // set desc if any
  1641. if( msgItem.has_custom_desc() )
  1642. {
  1643. SetCustomDesc( msgItem.custom_desc().c_str() );
  1644. }
  1645. // read the attributes
  1646. for( int nAttr = 0; nAttr < msgItem.attribute_size(); nAttr++ )
  1647. {
  1648. // skip over old-format messages
  1649. const CSOEconItemAttribute& msgAttr = msgItem.attribute( nAttr );
  1650. if ( msgAttr.has_value() || !msgAttr.has_value_bytes() )
  1651. continue;
  1652. // skip over attributes we don't understand
  1653. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( msgAttr.def_index() );
  1654. if ( !pAttrDef )
  1655. continue;
  1656. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  1657. Assert( pAttrType );
  1658. pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, msgAttr.value_bytes() );
  1659. }
  1660. // Check to see if the item has an interior object.
  1661. if ( msgItem.has_interior_item() )
  1662. {
  1663. EnsureCustomDataExists();
  1664. m_pCustomData->m_pInteriorItem = new CEconItem();
  1665. m_pCustomData->m_pInteriorItem->DeserializeFromProtoBufItem( msgItem.interior_item() );
  1666. }
  1667. // update equipped state
  1668. if ( msgItem.has_contains_equipped_state_v2() && msgItem.contains_equipped_state_v2() )
  1669. {
  1670. // unequip from everything...
  1671. Unequip();
  1672. // ...and re-equip to whatever our current state is
  1673. for ( int i = 0; i < msgItem.equipped_state_size(); i++ )
  1674. {
  1675. Equip( msgItem.equipped_state(i).new_class(), msgItem.equipped_state(i).new_slot() );
  1676. }
  1677. }
  1678. }
  1679. #ifdef GC
  1680. #include "econ/localization_provider.h"
  1681. // --------------------------------------------------------------------------
  1682. // Purpose:
  1683. // --------------------------------------------------------------------------
  1684. void CEconItem::GetDirtyColumnSet( const CUtlVector< int > &fields, CColumnSet &cs ) const
  1685. {
  1686. cs.MakeEmpty();
  1687. /* @note Tom Bui: We know only the inventory/position field and the quantity field can be dirty...
  1688. for ( int nField = 0; nField < CSchItem::k_iFieldMax; ++nField )
  1689. {
  1690. if ( bDatabase ? BIsFieldDatabaseDirty( nField ) : BIsFieldNetworkDirty( nField ) )
  1691. {
  1692. cs.BAddColumn( nField );
  1693. }
  1694. }
  1695. */
  1696. if( fields.HasElement( CSOEconItem::kInventoryFieldNumber ) )
  1697. cs.BAddColumn( CSchItem::k_iField_unInventory );
  1698. if( fields.HasElement( CSOEconItem::kQuantityFieldNumber ) )
  1699. cs.BAddColumn( CSchItem::k_iField_unQuantity );
  1700. if( fields.HasElement( CSOEconItem::kStyleFieldNumber ) )
  1701. cs.BAddColumn( CSchItem::k_iField_unStyle );
  1702. }
  1703. // --------------------------------------------------------------------------
  1704. // Purpose: Exports the item for use in another app
  1705. // --------------------------------------------------------------------------
  1706. void CEconItem::ExportToAPI( CWebAPIValues *pValues ) const
  1707. {
  1708. pValues->CreateChildObject( "id" )->SetUInt64Value( m_ulID );
  1709. pValues->CreateChildObject( "def_index" )->SetUInt32Value( m_unDefIndex );
  1710. pValues->CreateChildObject( "level" )->SetUInt32Value( m_unLevel );
  1711. pValues->CreateChildObject( "quality" )->SetInt32Value( m_nQuality );
  1712. if( GetCustomName() )
  1713. pValues->CreateChildObject( "custom_name" )->SetStringValue( GetCustomName() );
  1714. if( GetCustomDesc() )
  1715. pValues->CreateChildObject( "custom_desc" )->SetStringValue( GetCustomDesc() );
  1716. }
  1717. // --------------------------------------------------------------------------
  1718. // Purpose: Imports a view of the item from another app
  1719. // --------------------------------------------------------------------------
  1720. bool CEconItem::BImportFromAPI( CWebAPIValues *pValues )
  1721. {
  1722. m_unLevel = pValues->GetChildUInt32Value( "level", 1 );
  1723. m_nQuality = pValues->GetChildUInt32Value( "quality", GetItemSchema()->GetDefaultQuality() );
  1724. CUtlString sValue;
  1725. pValues->GetChildStringValue( sValue, "custom_name", "" );
  1726. CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
  1727. if( !sValue.IsEmpty() )
  1728. SetCustomName( sValue.Get() );
  1729. pValues->GetChildStringValue( sValue, "custom_desc", "" );
  1730. CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
  1731. if( !sValue.IsEmpty() )
  1732. SetCustomDesc( sValue.Get() );
  1733. return true;
  1734. }
  1735. #endif
  1736. // --------------------------------------------------------------------------
  1737. // Purpose:
  1738. // --------------------------------------------------------------------------
  1739. void CEconItem::EnsureCustomDataExists()
  1740. {
  1741. if ( m_pCustomData == NULL )
  1742. {
  1743. m_pCustomData = new CEconItemCustomData();
  1744. if ( m_dirtyBits.m_bHasEquipSingleton )
  1745. {
  1746. m_pCustomData->m_vecEquipped.AddToTail( m_EquipInstanceSingleton );
  1747. m_EquipInstanceSingleton = EquippedInstance_t();
  1748. m_dirtyBits.m_bHasEquipSingleton = false;
  1749. }
  1750. if ( m_dirtyBits.m_bHasAttribSingleton )
  1751. {
  1752. m_pCustomData->m_vecAttributes.AddToTail( m_CustomAttribSingleton );
  1753. m_dirtyBits.m_bHasAttribSingleton = false;
  1754. }
  1755. }
  1756. }
  1757. #ifdef GC_DLL
  1758. // --------------------------------------------------------------------------
  1759. // Purpose:
  1760. // --------------------------------------------------------------------------
  1761. static bool BYieldingAddAuditRecordImpl( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
  1762. {
  1763. CEconUserSession *pUserSession = GGCGameBase()->FindEconUserSession( CSteamID( unOwnerID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
  1764. CGCGSSession *pGSSession = pUserSession && pUserSession->GetSteamIDGS().IsValid() ? GGCEcon()->FindGSSession( pUserSession->GetSteamIDGS() ) : NULL;
  1765. // Prepare the audit record
  1766. CSchItemAudit schItemAudit;
  1767. schItemAudit.m_ulItemID = ulItemID;
  1768. schItemAudit.m_RTime32Stamp = CRTime::RTime32TimeCur();
  1769. schItemAudit.m_eAction = eAction;
  1770. schItemAudit.m_unOwnerID = unOwnerID;
  1771. schItemAudit.m_unData = unData;
  1772. schItemAudit.m_unServerIP = pGSSession ? pGSSession->GetAddr() : 0;
  1773. schItemAudit.m_usServerPort = pGSSession ? pGSSession->GetPort() : 0;
  1774. return sqlAccess->BYieldingInsertRecord( &schItemAudit );
  1775. }
  1776. // --------------------------------------------------------------------------
  1777. // Purpose:
  1778. // --------------------------------------------------------------------------
  1779. bool CEconItem::CAuditEntry::BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const
  1780. {
  1781. return BYieldingAddAuditRecordImpl( &sqlAccess, pItem->GetID(), pItem->GetAccountID(), m_eAction, m_unData );
  1782. }
  1783. // --------------------------------------------------------------------------
  1784. // Purpose: Adds an audit record to the DB for an item
  1785. // --------------------------------------------------------------------------
  1786. void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData )
  1787. {
  1788. YieldingAddAuditRecord( sqlAccess, pItem->GetItemID(), unOwnerID, eAction, unData );
  1789. }
  1790. // --------------------------------------------------------------------------
  1791. // Purpose: Adds an audit record to the DB for an item
  1792. // --------------------------------------------------------------------------
  1793. void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
  1794. {
  1795. Verify( BYieldingAddAuditRecordImpl( sqlAccess, ulItemID, unOwnerID, eAction, unData ) );
  1796. }
  1797. // --------------------------------------------------------------------------
  1798. // Purpose: Adds an item to the DB
  1799. // --------------------------------------------------------------------------
  1800. bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData )
  1801. {
  1802. CSQLAccess sqlAccess;
  1803. sqlAccess.BBeginTransaction( "YieldingAddItemToDatabase" );
  1804. pItem->SetAccountID( steamID.GetAccountID() );
  1805. if( !pItem->BYieldingAddInsertToTransaction( sqlAccess ) )
  1806. {
  1807. sqlAccess.RollbackTransaction();
  1808. return false;
  1809. }
  1810. YieldingAddAuditRecord( &sqlAccess, pItem, steamID.GetAccountID(), eAction, unData );
  1811. // commit the item and audit record
  1812. if( !sqlAccess.BCommitTransaction() )
  1813. {
  1814. EmitError( SPEW_GC, "Failed to add item for %s\n", steamID.Render() );
  1815. return false;
  1816. }
  1817. return true;
  1818. }
  1819. //----------------------------------------------------------------------------
  1820. // Purpose:
  1821. //----------------------------------------------------------------------------
  1822. static CEconItem *FindEquippedItemForClassAndSlot( CEconSharedObjectCache *pSOCache, equipped_class_t unClass, equipped_slot_t unSlot )
  1823. {
  1824. VPROF_BUDGET( "FindEquippedItemForClassAndSlot()", VPROF_BUDGETGROUP_STEAM );
  1825. Assert( pSOCache );
  1826. CGCSharedObjectTypeCache *pItemTypeCache = pSOCache->FindTypeCache( k_EEconTypeItem );
  1827. const uint32 unCount = pItemTypeCache ? pItemTypeCache->GetCount() : 0;
  1828. for ( uint32 i = 0; i < unCount; i++ )
  1829. {
  1830. CEconItem *pItem = static_cast<CEconItem *>( pItemTypeCache->GetObject( i ) );
  1831. Assert( pItem );
  1832. for ( int j = 0; j < pItem->GetEquippedInstanceCount(); j++ )
  1833. {
  1834. const CEconItem::EquippedInstance_t& equipInst = pItem->GetEquippedInstance( j );
  1835. if ( equipInst.m_unEquippedClass == unClass && equipInst.m_unEquippedSlot == unSlot )
  1836. return pItem;
  1837. }
  1838. }
  1839. return NULL;
  1840. }
  1841. //----------------------------------------------------------------------------
  1842. // Purpose:
  1843. //----------------------------------------------------------------------------
  1844. /*static*/ void CEconItemEquipInstanceHelpers::AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession /* = NULL */ )
  1845. {
  1846. VPROF_BUDGET( "CEconEquipInstance::AssignItemToSlot()", VPROF_BUDGETGROUP_STEAM );
  1847. Assert( pSOCache );
  1848. Assert( GetItemSchema()->IsValidClass( unClass ) );
  1849. Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
  1850. CEconItem *pPreviousEquippedItem = FindEquippedItemForClassAndSlot( pSOCache, unClass, unSlot );
  1851. // skip out on expensive work if we're going to equip the item that's already in that slot
  1852. if ( pItem == pPreviousEquippedItem )
  1853. return;
  1854. // do we have an item to unequip first
  1855. if ( pPreviousEquippedItem )
  1856. {
  1857. pPreviousEquippedItem->UnequipFromClass( unClass );
  1858. pSOCache->DirtyObjectField( pPreviousEquippedItem, CSOEconItem::kEquippedStateFieldNumber );
  1859. GGCBase()->AddCacheToWritebackQueue( pSOCache );
  1860. const bool bPreviousItemIsNowEquipped = pPreviousEquippedItem->IsEquipped();
  1861. if ( pOptionalSession && !bPreviousItemIsNowEquipped )
  1862. {
  1863. pOptionalSession->OnEquippedStateChange( pPreviousEquippedItem, false );
  1864. }
  1865. }
  1866. // are we equipping a new item to this slot, or were we only responsible for unequipping
  1867. // the existing item?
  1868. if ( pItem )
  1869. {
  1870. // notify the session, in case it wants to do anything; the default implementation
  1871. // will update the equipped state on the item but other types may have additional
  1872. // behavior
  1873. const bool bItemWasEquipped = pItem->IsEquipped();
  1874. pItem->Equip( unClass, unSlot );
  1875. pSOCache->DirtyObjectField( pItem, CSOEconItem::kEquippedStateFieldNumber );
  1876. GGCBase()->AddCacheToWritebackQueue( pSOCache );
  1877. const bool bItemIsEquipped = pItem->IsEquipped();
  1878. Assert( bItemIsEquipped );
  1879. // did our equipped state change? this can happen if we equip ourself for the
  1880. // first time for this account right now. Keep in mind that this can be either
  1881. // a "is newly-equipped" or a "was equipped but is now not" state transition.
  1882. if ( pOptionalSession && (bItemWasEquipped != bItemIsEquipped) )
  1883. {
  1884. pOptionalSession->OnEquippedStateChange( pItem, bItemIsEquipped );
  1885. }
  1886. }
  1887. }
  1888. #define ITEM_ACTION( x ) { x, #x }
  1889. ENUMSTRINGS_START( EItemAction )
  1890. ITEM_ACTION( k_EItemActionGSCreate ),
  1891. ITEM_ACTION( k_EItemActionUnpurchase ),
  1892. ITEM_ACTION( k_EItemActionDelete ),
  1893. ITEM_ACTION( k_EItemActionAwardAchievement ),
  1894. ITEM_ACTION( k_EItemActionBanned ),
  1895. ITEM_ACTION( k_EItemActionQuantityChanged ),
  1896. ITEM_ACTION( k_EItemActionRestored ),
  1897. ITEM_ACTION( k_EItemActionAwardTime ),
  1898. ITEM_ACTION( k_EItemActionManualCreate ),
  1899. ITEM_ACTION( k_EItemActionDrop ),
  1900. ITEM_ACTION( k_EItemActionPickUp ),
  1901. ITEM_ACTION( k_EItemActionCraftDestroy ),
  1902. ITEM_ACTION( k_EItemActionCraftCreate ),
  1903. ITEM_ACTION( k_EItemActionLimitExceeded ),
  1904. ITEM_ACTION( k_EItemActionPurchase ),
  1905. ITEM_ACTION( k_EItemActionNameChanged_Add ),
  1906. ITEM_ACTION( k_EItemActionUnlockCrate_Add ),
  1907. ITEM_ACTION( k_EItemActionPaintItem_Add ),
  1908. ITEM_ACTION( k_EItemActionAutoGrantItem ),
  1909. ITEM_ACTION( k_EItemActionCrossGameAchievement ),
  1910. ITEM_ACTION( k_EItemActionAddItemToSocket_Add ),
  1911. ITEM_ACTION( k_EItemActionAddSocketToItem_Add ),
  1912. ITEM_ACTION( k_EItemActionRemoveSocketItem_Add ),
  1913. ITEM_ACTION( k_EItemActionCustomizeItemTexture_Add ),
  1914. ITEM_ACTION( k_EItemActionItemTraded_Add ),
  1915. ITEM_ACTION( k_EItemActionUseItem ),
  1916. ITEM_ACTION( k_EItemActionAwardGift_Receiver ),
  1917. ITEM_ACTION( k_EItemActionNameChanged_Remove ),
  1918. ITEM_ACTION( k_EItemActionUnlockCrate_Remove ),
  1919. ITEM_ACTION( k_EItemActionPaintItem_Remove ),
  1920. ITEM_ACTION( k_EItemActionAddItemToSocket_Remove ),
  1921. ITEM_ACTION( k_EItemActionAddSocketToItem_Remove ),
  1922. ITEM_ACTION( k_EItemActionRemoveSocketItem_Remove ),
  1923. ITEM_ACTION( k_EItemActionCustomizeItemTexture_Remove ),
  1924. ITEM_ACTION( k_EItemActionItemTraded_Remove ),
  1925. ITEM_ACTION( k_EItemActionUnpackItemBundle ),
  1926. ITEM_ACTION( k_EItemActionCreateItemFromBundle ),
  1927. ITEM_ACTION( k_EItemActionAwardStorePromotionItem ),
  1928. ITEM_ACTION( k_EItemActionConvertItem ),
  1929. ITEM_ACTION( k_EItemActionEarnedItem ),
  1930. ITEM_ACTION( k_EItemActionAwardGift_Giver ),
  1931. ITEM_ACTION( k_EItemActionRefundedItem ),
  1932. ITEM_ACTION( k_EItemActionAwardThirdPartyPromo ),
  1933. ITEM_ACTION( k_EItemActionRemoveItemName_Remove ),
  1934. ITEM_ACTION( k_EItemActionRemoveItemName_Add ),
  1935. ITEM_ACTION( k_EItemActionRemoveItemPaint_Remove ),
  1936. ITEM_ACTION( k_EItemActionRemoveItemPaint_Add ),
  1937. ITEM_ACTION( k_EItemActionHalloweenDrop ),
  1938. ITEM_ACTION( k_EItemActionSteamWorkshopContributor ),
  1939. ITEM_ACTION( k_EItemActionManualOwnershipChange ),
  1940. ITEM_ACTION( k_EItemActionSupportDelete ),
  1941. ITEM_ACTION( k_EItemActionSupportCreatedByUndo ),
  1942. ITEM_ACTION( k_EItemActionSupportDeletedByUndo ),
  1943. ITEM_ACTION( k_EItemActionSupportQuantityChangedByUndo ),
  1944. ITEM_ACTION( k_EItemActionSupportRename_Add ),
  1945. ITEM_ACTION( k_EItemActionSupportRename_Remove ),
  1946. ITEM_ACTION( k_EItemActionSupportDescribe_Add ),
  1947. ITEM_ACTION( k_EItemActionSupportDescribe_Remove ),
  1948. ITEM_ACTION( k_EItemActionStrangePartApply_Add ),
  1949. ITEM_ACTION( k_EItemActionStrangePartApply_Remove ),
  1950. ITEM_ACTION( k_EItemActionStrangeScoreReset_Add ),
  1951. ITEM_ACTION( k_EItemActionStrangeScoreReset_Remove ),
  1952. ITEM_ACTION( k_EItemActionStrangePartRemove_Add ),
  1953. ITEM_ACTION( k_EItemActionStrangePartRemove_Remove ),
  1954. ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Add ),
  1955. ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Remove ),
  1956. ITEM_ACTION( k_EItemActionTransmogrify_Add ),
  1957. ITEM_ACTION( k_EItemActionTransmogrify_Remove ),
  1958. ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Add ),
  1959. ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Remove ),
  1960. ITEM_ACTION( k_EItemActionSupportStrangify_Add ),
  1961. ITEM_ACTION( k_EItemActionSupportStrangify_Remove ),
  1962. ITEM_ACTION( k_EItemActionUpgradeCardApply_Add ),
  1963. ITEM_ACTION( k_EItemActionUpgradeCardApply_Remove ),
  1964. ITEM_ACTION( k_EItemActionUpgradeCardRemove_Add ),
  1965. ITEM_ACTION( k_EItemActionUpgradeCardRemove_Remove ),
  1966. #ifdef STAGING_ONLY
  1967. ITEM_ACTION( k_EItemActionDev_ClientLootListRoll ),
  1968. #endif
  1969. ITEM_ACTION( k_EItemActionPeriodicScoreReward_Add ),
  1970. ITEM_ACTION( k_EItemActionPeriodicScoreReward_Remove ),
  1971. ITEM_ACTION( k_EItemActionGiftWrap_Add ),
  1972. ITEM_ACTION( k_EItemActionGiftWrap_Remove ),
  1973. ITEM_ACTION( k_EItemActionGiftDelivery_Add ),
  1974. ITEM_ACTION( k_EItemActionGiftDelivery_Remove ),
  1975. ITEM_ACTION( k_EItemActionGiftUnwrap_Add ),
  1976. ITEM_ACTION( k_EItemActionGiftUnwrap_Remove ),
  1977. ITEM_ACTION( k_EItemActionPackageItem ),
  1978. ITEM_ACTION( k_EItemActionPackageItem_Revoked ),
  1979. ITEM_ACTION( k_EItemActionHandleMapToken ),
  1980. ITEM_ACTION( k_EItemActionCafeOrSchoolItem_Remove ),
  1981. ITEM_ACTION( k_EItemActionVACBanned_Remove ),
  1982. ITEM_ACTION( k_EItemActionUpgradeThirdPartyPromo ),
  1983. ITEM_ACTION( k_EItemActionExpired ),
  1984. ITEM_ACTION( k_EItemActionTradeRollback_Add ),
  1985. ITEM_ACTION( k_EItemActionTradeRollback_Remove ),
  1986. ITEM_ACTION( k_EItemActionCDKeyGrant ),
  1987. ITEM_ACTION( k_EItemActionCDKeyRevoke ),
  1988. ITEM_ACTION( k_EItemActionWeddingRing_Add ),
  1989. ITEM_ACTION( k_EItemActionWeddingRing_Remove ),
  1990. ITEM_ACTION( k_EItemActionWeddingRing_AddPartner ),
  1991. ITEM_ACTION( k_EItemActionEconSetUnowned ),
  1992. ITEM_ACTION( k_EItemActionEconSetOwned ),
  1993. ITEM_ACTION( k_EItemActionStrangifyItem_Add ),
  1994. ITEM_ACTION( k_EItemActionStrangifyItem_Remove ),
  1995. ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Remove ),
  1996. ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Add ),
  1997. ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Remove ),
  1998. ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Add ),
  1999. ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Remove ),
  2000. ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Add ),
  2001. ITEM_ACTION( k_EItemActionCollectItem_RemoveCollection ),
  2002. ITEM_ACTION( k_EItemActionCollectItem_UpdateCollection ),
  2003. ITEM_ACTION( k_EItemActionCollectItem_CollectedItem ),
  2004. ITEM_ACTION( k_EItemActionCollectItem_RedeemCollectionReward ),
  2005. ITEM_ACTION( k_EItemActionPreviewItem_BeginPreviewPeriod ),
  2006. ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodExpired ),
  2007. ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodItemBought ),
  2008. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_RemoveTicket ),
  2009. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantBadge ),
  2010. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove ),
  2011. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add ),
  2012. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot ),
  2013. ITEM_ACTION( k_EItemActionMvM_RemoveSquadSurplusVoucher ),
  2014. ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Receiver ),
  2015. ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Giver ),
  2016. ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot ),
  2017. ITEM_ACTION( k_EItemActionMvM_AwardHelpANoobBonus_Helper ),
  2018. ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Add ),
  2019. ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove ),
  2020. ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolRemove ),
  2021. ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolAdd ),
  2022. ITEM_ACTION( k_EItemActionConsumeItem_Consume_InputRemove ),
  2023. ITEM_ACTION( k_EItemActionConsumeItem_Complete_OutputAdd ),
  2024. ITEM_ACTION( k_EItemActionConsumeItem_Complete_ToolRemove ),
  2025. ITEM_ACTION( k_EItemActionItemEaterRecharge_Add ),
  2026. ITEM_ACTION( k_EItemActionItemEaterRecharge_Remove ),
  2027. ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Remove ),
  2028. ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Add ),
  2029. ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Add ),
  2030. ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Remove ),
  2031. ITEM_ACTION( k_EItemAction_OperationPass_Add ),
  2032. ITEM_ACTION( k_EItemActionSpyVsEngyWar_JoinedWar ),
  2033. ITEM_ACTION( k_EItemActionMarket_Add ),
  2034. ITEM_ACTION( k_EItemActionMarket_Remove ),
  2035. ITEM_ACTION( k_EItemAction_QuestComplete_Reward ),
  2036. ITEM_ACTION( k_EItemAction_QuestComplete_Remove ),
  2037. ITEM_ACTION( k_EItemActionStrangeCountTransfer_Add ),
  2038. ITEM_ACTION( k_EItemActionStrangeCountTransfer_Remove ),
  2039. ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Add ),
  2040. ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Remove ),
  2041. ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Add ),
  2042. ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Remove ),
  2043. ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Remove ),
  2044. ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Add ),
  2045. ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Remove ),
  2046. ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Add ),
  2047. ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Remove ),
  2048. ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Add ),
  2049. ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Remove ),
  2050. ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Add ),
  2051. ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Remove ),
  2052. ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Add ),
  2053. ITEM_ACTION( k_EItemActionViralCompetitiveBetaPass_Drop ),
  2054. ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Remove ),
  2055. ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Add ),
  2056. ENUMSTRINGS_END( EItemAction )
  2057. const char *PchFriendlyNameFromEItemAction( EItemAction eItemAction, EItemActionMissingBehavior eMissingBehavior )
  2058. {
  2059. Assert( eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing || eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnNULLIfMissing );
  2060. switch( eItemAction )
  2061. {
  2062. case k_EItemActionGSCreate: return "Created by Gameserver";
  2063. case k_EItemActionUnpurchase: return "Unpurchase";
  2064. case k_EItemActionDelete: return "Deleted by Owner";
  2065. case k_EItemActionAwardAchievement: return "Achievement Award";
  2066. case k_EItemActionBanned: return "Banned";
  2067. case k_EItemActionQuantityChanged: return "Quantity Changed";
  2068. case k_EItemActionRestored: return "Restored";
  2069. case k_EItemActionAwardTime: return "Timed Drop";
  2070. case k_EItemActionManualCreate: return "Created by Support";
  2071. case k_EItemActionDrop: return "Dropped";
  2072. case k_EItemActionPickUp: return "Picked up";
  2073. case k_EItemActionCraftDestroy: return "Crafted";
  2074. case k_EItemActionCraftCreate: return "Crafted";
  2075. case k_EItemActionLimitExceeded: return "Destroyed by Backpack Limit";
  2076. case k_EItemActionPurchase: return "Purchase";
  2077. case k_EItemActionNameChanged_Add: return "Added by Name Change";
  2078. case k_EItemActionUnlockCrate_Add: return "Added by Crate Unlock";
  2079. case k_EItemActionPaintItem_Add: return "Added by Paint";
  2080. case k_EItemActionAutoGrantItem: return "Autogrant";
  2081. case k_EItemActionCrossGameAchievement: return "Cross-Game Achievement Award";
  2082. case k_EItemActionAddItemToSocket_Add: return "Added to Socket";
  2083. case k_EItemActionAddSocketToItem_Add: return "Added by Socket";
  2084. case k_EItemActionRemoveSocketItem_Add: return "Removed by Socket";
  2085. case k_EItemActionCustomizeItemTexture_Add: return "Added by Custom Texture";
  2086. case k_EItemActionItemTraded_Add: return "Trade";
  2087. case k_EItemActionUseItem: return "Used";
  2088. case k_EItemActionAwardGift_Receiver: return "Gift Received";
  2089. case k_EItemActionNameChanged_Remove: return "Removed by Name Change";
  2090. case k_EItemActionUnlockCrate_Remove: return "Removed by Crate Unlock";
  2091. case k_EItemActionPaintItem_Remove: return "Removed by Paint";
  2092. case k_EItemActionAddItemToSocket_Remove: return "Removed from Socket";
  2093. case k_EItemActionAddSocketToItem_Remove: return "Removed by Socket";
  2094. case k_EItemActionRemoveSocketItem_Remove: return "Removed Socket Item";
  2095. case k_EItemActionCustomizeItemTexture_Remove: return "Removed by Custom Texture";
  2096. case k_EItemActionItemTraded_Remove: return "Trade";
  2097. case k_EItemActionUnpackItemBundle: return "Unpacked Bundle";
  2098. case k_EItemActionCreateItemFromBundle: return "Added from Bundle";
  2099. case k_EItemActionAwardStorePromotionItem: return "Store Promo Item";
  2100. case k_EItemActionConvertItem: return "Converted";
  2101. case k_EItemActionEarnedItem: return "Earned";
  2102. case k_EItemActionAwardGift_Giver: return "Gift Sent";
  2103. case k_EItemActionRefundedItem: return "Item Refunded";
  2104. case k_EItemActionAwardThirdPartyPromo: return "Third-Party Promo";
  2105. case k_EItemActionRemoveItemName_Remove: return "Removed by Name Removal";
  2106. case k_EItemActionRemoveItemName_Add: return "Added by Name Removal";
  2107. case k_EItemActionRemoveItemPaint_Remove: return "Removed by Paint Removal";
  2108. case k_EItemActionRemoveItemPaint_Add: return "Added by Paint Removal";
  2109. case k_EItemActionHalloweenDrop: return "Halloween Drop";
  2110. case k_EItemActionSteamWorkshopContributor: return "Steam Workshop Contributor";
  2111. case k_EItemActionManualOwnershipChange: return "Support Manual Ownership Change";
  2112. case k_EItemActionSupportDelete: return "Deleted by Support";
  2113. case k_EItemActionSupportCreatedByUndo: return "Created by Undo";
  2114. case k_EItemActionSupportDeletedByUndo: return "Deleted by Undo";
  2115. case k_EItemActionSupportQuantityChangedByUndo: return "Quantity Changed by Undo";
  2116. case k_EItemActionSupportRename_Add: return "Added by Support Rename";
  2117. case k_EItemActionSupportRename_Remove: return "Removed by Support Rename";
  2118. case k_EItemActionSupportDescribe_Add: return "Added by Support Describe";
  2119. case k_EItemActionSupportDescribe_Remove: return "Removed by Support Describe";
  2120. case k_EItemActionStrangePartApply_Add: return "Added by Strange Part Apply";
  2121. case k_EItemActionStrangePartApply_Remove: return "Removed by Strange Part Apply";
  2122. case k_EItemActionStrangeScoreReset_Add: return "Added by Strange Score Reset";
  2123. case k_EItemActionStrangeScoreReset_Remove: return "Removed by Strange Score Reset";
  2124. case k_EItemActionStrangePartRemove_Add: return "Added by Strange Part Remove";
  2125. case k_EItemActionStrangePartRemove_Remove: return "Removed by Strange Part Remove";
  2126. case k_EItemActionStrangeRestrictionApply_Add: return "Added by Strange Restriction Apply";
  2127. case k_EItemActionStrangeRestrictionApply_Remove: return "Removed by Strange Restriction Apply";
  2128. case k_EItemActionTransmogrify_Add: return "Added by Transmogrify Apply";
  2129. case k_EItemActionTransmogrify_Remove: return "Removed by Transmogrify Apply";
  2130. case k_EItemActionHalloweenSpellPageAdd_Add: return "Added by Halloween Spell Page Apply";
  2131. case k_EItemActionHalloweenSpellPageAdd_Remove: return "Removed by Halloween Spell Page Apply";
  2132. case k_EItemActionSupportStrangify_Add: return "Added by Support Strangify";
  2133. case k_EItemActionSupportStrangify_Remove: return "Removed by Support Strangify";
  2134. case k_EItemActionUpgradeCardApply_Add: return "Added by Upgrade Card Apply";
  2135. case k_EItemActionUpgradeCardApply_Remove: return "Removed by Upgrade Card Apply";
  2136. case k_EItemActionUpgradeCardRemove_Add: return "Added by Upgrade Card Remove";
  2137. case k_EItemActionUpgradeCardRemove_Remove: return "Removed by Upgrade Card Remove";
  2138. #ifdef STAGING_ONLY
  2139. case k_EItemActionDev_ClientLootListRoll: return "Dev: Client Loot List Roll";
  2140. #endif
  2141. case k_EItemActionPeriodicScoreReward_Add: return "Periodic Score System Reward Grant Add";
  2142. case k_EItemActionPeriodicScoreReward_Remove: return "Periodic Score System Reward Remove";
  2143. case k_EItemActionGiftWrap_Add: return "Wrapped Gift - Added";
  2144. case k_EItemActionGiftWrap_Remove: return "Wrapped Gift - Remove";
  2145. case k_EItemActionGiftDelivery_Add: return "Gift Delivered - Add";
  2146. case k_EItemActionGiftDelivery_Remove: return "Gift Delivered - Remove";
  2147. case k_EItemActionGiftUnwrap_Add: return "Unwrapped Gift - Added";
  2148. case k_EItemActionGiftUnwrap_Remove: return "Unwrapped Gift - Remove";
  2149. case k_EItemActionPackageItem: return "Added by Steam Purchase";
  2150. case k_EItemActionPackageItem_Revoked: return "Revoked by Steam Purchase";
  2151. case k_EItemActionHandleMapToken: return "Map Token Applied";
  2152. case k_EItemActionCafeOrSchoolItem_Remove: return "Cafe or School Removal";
  2153. case k_EItemActionVACBanned_Remove: return "VAC Ban removal";
  2154. case k_EItemActionUpgradeThirdPartyPromo: return "Third-Party Promo Upgrade";
  2155. case k_EItemActionExpired: return "Expired";
  2156. case k_EItemActionTradeRollback_Add: return "Trade Rollback";
  2157. case k_EItemActionTradeRollback_Remove: return "Trade Rollback";
  2158. case k_EItemActionCDKeyGrant: return "CD Key Grant";
  2159. case k_EItemActionCDKeyRevoke: return "CD Key Rollback";
  2160. case k_EItemActionWeddingRing_Add: return "Wedding Ring Add";
  2161. case k_EItemActionWeddingRing_Remove: return "Wedding Ring Remove";
  2162. case k_EItemActionWeddingRing_AddPartner: return "Wedding Ring Add (Partner)";
  2163. case k_EItemActionEconSetUnowned: return "Econ SetUnowned";
  2164. case k_EItemActionEconSetOwned: return "Econ SetOwned";
  2165. case k_EItemActionStrangifyItem_Add: return "Added by Strangify Apply";
  2166. case k_EItemActionStrangifyItem_Remove: return "Removed by Strangify Apply";
  2167. case k_EItemActionRemoveItemCraftIndex_Remove: return "Removed by Craft Index Removal";
  2168. case k_EItemActionRemoveItemCraftIndex_Add: return "Added by Craft Index Removal";
  2169. case k_EItemActionRemoveItemMakersMark_Remove: return "Removed by Maker's Mark Removal";
  2170. case k_EItemActionRemoveItemMakersMark_Add: return "Added by Maker's Mark Removal";
  2171. case k_EItemActionRemoveItemKillStreak_Remove: return "Removed by KillStreak Removal";
  2172. case k_EItemActionRemoveItemKillStreak_Add: return "Added by KillStreak Removal";
  2173. case k_EItemActionCollectItem_CollectedItem: return "Item Collection: Added to a Collection";
  2174. case k_EItemActionCollectItem_RemoveCollection: return "Item Collection: Removed Old Collection Item";
  2175. case k_EItemActionCollectItem_UpdateCollection: return "Item Collection: Created New Collection Item";
  2176. case k_EItemActionCollectItem_RedeemCollectionReward: return "Item Collection: Redeemed Reward";
  2177. case k_EItemActionPreviewItem_BeginPreviewPeriod: return "Item Preview: Begin Preview Period";
  2178. case k_EItemActionPreviewItem_EndPreviewPeriodExpired: return "Item Preview: End Preview Period by Expiration";
  2179. case k_EItemActionPreviewItem_EndPreviewPeriodItemBought: return "Item Preview: End Preview Period by Item Purchase";
  2180. case k_EItemActionMvM_ChallengeCompleted_RemoveTicket: return "Removed MvM Ticket";
  2181. case k_EItemActionMvM_ChallengeCompleted_GrantBadge: return "Granted MvM Badge";
  2182. case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add: return "Added by MvM Stamp Update";
  2183. case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove: return "Removed by MvM Stamp Update";
  2184. case k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot: return "Added by MvM Mission Completion";
  2185. case k_EItemActionMvM_RemoveSquadSurplusVoucher: return "Removed MvM squad surplus voucher";
  2186. case k_EItemActionMvM_AwardSquadSurplus_Receiver: return "Received MvM squad surplus";
  2187. case k_EItemActionMvM_AwardSquadSurplus_Giver: return "Generated MvM squad surplus";
  2188. case k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot: return "Added by MvM Tour Completion";
  2189. case k_EItemActionMvM_AwardHelpANoobBonus_Helper: return "Added by MvM Help-A-Noob";
  2190. case k_EItemActionHalloween_UpdateMerasmusLootLevel_Add: return "Updating Halloween Merasmus Loot - Adding New item";
  2191. case k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove: return "Updating Halloween Merasmus Loot Level - Removing Old Item";
  2192. case k_EItemActionConsumeItem_Consume_ToolRemove: return "Dynamic Recipe: Consuming Input Item - Removing Old Recipe-Item";
  2193. case k_EItemActionConsumeItem_Consume_ToolAdd: return "Dynamic Recipe: Consuming Input Item - Adding New Recipe-Item";
  2194. case k_EItemActionConsumeItem_Consume_InputRemove: return "Dynamic Recipe: Consuming Input Item - Removing Input item";
  2195. case k_EItemActionConsumeItem_Complete_OutputAdd: return "Dynamic Recipe: Recipe Complete - Adding Output Item";
  2196. case k_EItemActionConsumeItem_Complete_ToolRemove: return "Dynamic Recipe: Recipe Complete - Removing Recipe-Item";
  2197. case k_EItemActionItemEaterRecharge_Add: return "Added by Item Eater Recharger";
  2198. case k_EItemActionItemEaterRecharge_Remove: return "Removed by Item Eater Recharger";
  2199. case k_EItemActionSupportAddOrModifyAttribute_Remove: return "Removed by Support Add/Modify Attribute";
  2200. case k_EItemActionSupportAddOrModifyAttribute_Add: return "Added by Support Add/Modify Attribute";
  2201. case k_EItemAction_UpdateDuckBadgeLevel_Add: return "Updating Duck Badge Level - Adding New item";
  2202. case k_EItemAction_UpdateDuckBadgeLevel_Remove: return "Updating Duck Badge Level - Removing Old Item";
  2203. case k_EItemActionSpyVsEngyWar_JoinedWar: return "Added by joining the Spy vs. Engy War";
  2204. case k_EItemAction_OperationPass_Add: return "Adding Operation Pass";
  2205. case k_EItemAction_QuestDrop: return "Quest Drop";
  2206. case k_EItemActionMarket_Add: return "Market - Add";
  2207. case k_EItemActionMarket_Remove: return "Market - Remove";
  2208. case k_EItemAction_QuestComplete_Reward: return "Added from completing a quest";
  2209. case k_EItemAction_QuestComplete_Remove: return "Removed from completing a quest";
  2210. case k_EItemActionStrangeCountTransfer_Add: return "Added by using a Strange Count Transfer tool";
  2211. case k_EItemActionStrangeCountTransfer_Remove: return "Removed by using a Strange Count Transfer tool";
  2212. case k_EItemActionCraftCollectionUpgrade_Add: return "Added by using Craft Collection Upgrade";
  2213. case k_EItemActionCraftCollectionUpgrade_Remove: return "Removed by using Craft Collection Upgrade";
  2214. case k_EItemActionCraftHalloweenOffering_Add: return "Added by using Craft Halloween Offering";
  2215. case k_EItemActionCraftHalloweenOffering_Remove: return "Removed by using Craft Halloween Offering";
  2216. case k_EItemActionRemoveItemGiftedBy_Remove: return "Removed by Gifted By Removal";
  2217. case k_EItemActionRemoveItemGiftedBy_Add: return "Added by Gifted By Removal";
  2218. case k_EItemActionAddParticleVerticalAttr_Remove: return "Removed by Particle Vertical Attr Add";
  2219. case k_EItemActionAddParticleVerticalAttr_Add: return "Added by Particle Vertical Attr Add";
  2220. case k_EItemActionAddParticleUseHeadOriginAttr_Remove: return "Removed by Particle Use Head Origin Attr Add";
  2221. case k_EItemActionAddParticleUseHeadOriginAttr_Add: return "Added by Particle Use Head Origin Attr Add";
  2222. case k_EItemActionRemoveItemDynamicAttr_Remove: return "Removed by Dynamic Attribute Removal";
  2223. case k_EItemActionRemoveItemDynamicAttr_Add: return "Added by Dynamic Attribute Removal";
  2224. case k_EItemActionCraftStatClockTradeUp_Add: return "Added through Stat Clock Trade-Up";
  2225. case k_EItemActionCraftStatClockTradeUp_Remove: return "Consumed through Stat Clock Trade-Up";
  2226. case k_EItemActionViralCompetitiveBetaPass_Drop: return "Added via Competitive Beta Invite Drop";
  2227. case k_EItemActionSupportDeleteAttribute_Remove: return "Removed by Support Delete Attribute";
  2228. case k_EItemActionSupportDeleteAttribute_Add: return "Added by Support Delete Attribute";
  2229. default:
  2230. return eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing
  2231. ? PchNameFromEItemAction( eItemAction )
  2232. : PchNameFromEItemActionUnsafe( eItemAction );
  2233. }
  2234. }
  2235. const char *PchLocalizedNameFromEItemAction( EItemAction eAction, CLocalizationProvider &localizationProvider )
  2236. {
  2237. const char *pchAction = PchNameFromEItemAction( eAction );
  2238. if ( !V_strncmp( pchAction, "k_EItemAction", 13 ) )
  2239. {
  2240. CFmtStr fmtLocKey( "ItemHistory_Action_%s", &pchAction[13] );
  2241. locchar_t *pchLocalizedAction = localizationProvider.Find( fmtLocKey.String() );
  2242. if ( pchLocalizedAction != NULL )
  2243. {
  2244. return pchLocalizedAction;
  2245. }
  2246. }
  2247. if ( BIsActionDestructive( eAction ) )
  2248. {
  2249. return localizationProvider.Find( "ItemHistory_Action_GenericRemove" );
  2250. }
  2251. else if ( BIsActionCreative( eAction ) )
  2252. {
  2253. return localizationProvider.Find( "ItemHistory_Action_GenericAdd" );
  2254. }
  2255. else
  2256. {
  2257. return "Unknown";
  2258. }
  2259. }
  2260. ENUMSTRINGS_START( eEconItemOrigin )
  2261. { kEconItemOrigin_Drop, "Timed Drop" },
  2262. { kEconItemOrigin_Achievement, "Achievement" },
  2263. { kEconItemOrigin_Purchased, "Purchased" },
  2264. { kEconItemOrigin_Traded, "Traded" },
  2265. { kEconItemOrigin_Crafted, "Crafted" },
  2266. { kEconItemOrigin_StorePromotion, "Store Promotion" },
  2267. { kEconItemOrigin_Gifted, "Gifted" },
  2268. { kEconItemOrigin_SupportGranted, "Support Granted" },
  2269. { kEconItemOrigin_FoundInCrate, "Found in Crate" },
  2270. { kEconItemOrigin_Earned, "Earned" },
  2271. { kEconItemOrigin_ThirdPartyPromotion, "Third-Party Promotion" },
  2272. { kEconItemOrigin_GiftWrapped, "Wrapped Gift" },
  2273. { kEconItemOrigin_HalloweenDrop, "Halloween Drop" },
  2274. { kEconItemOrigin_PackageItem, "Steam Purchase" },
  2275. { kEconItemOrigin_Foreign, "Foreign Item" },
  2276. { kEconItemOrigin_CDKey, "CD Key" },
  2277. { kEconItemOrigin_CollectionReward, "Collection Reward" },
  2278. { kEconItemOrigin_PreviewItem, "Preview Item" },
  2279. { kEconItemOrigin_SteamWorkshopContribution, "Steam Workshop Contribution" },
  2280. { kEconItemOrigin_PeriodicScoreReward, "Periodic score reward" },
  2281. { kEconItemOrigin_MvMMissionCompletionReward, "MvM Badge completion reward" },
  2282. { kEconItemOrigin_MvMSquadSurplusReward, "MvM Squad surplus reward" },
  2283. { kEconItemOrigin_RecipeOutput, "Recipe output" },
  2284. { kEconItemOrigin_QuestDrop, "Quest Drop" },
  2285. { kEconItemOrigin_QuestLoanerItem, "Quest Loaner Item" },
  2286. { kEconItemOrigin_TradeUp, "Trade-Up" },
  2287. { kEconItemOrigin_ViralCompetitiveBetaPassSpread, "Viral Competitive Beta Pass Spread" },
  2288. ENUMSTRINGS_END( eEconItemOrigin )
  2289. #endif
  2290. //-----------------------------------------------------------------------------
  2291. // Purpose:
  2292. //-----------------------------------------------------------------------------
  2293. bool CCrateLootListWrapper::BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem )
  2294. {
  2295. Assert( m_pLootList == NULL );
  2296. // Find out what series this crate belongs to.
  2297. static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" );
  2298. if ( !pAttr_CrateSeries )
  2299. return false;
  2300. int iCrateSeries;
  2301. {
  2302. float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves
  2303. if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f )
  2304. return false;
  2305. iCrateSeries = fCrateSeries;
  2306. }
  2307. // Our index is an index into the revolving-loot-lists list. From that list we'll be able to
  2308. // get a loot list name, which we'll use to look up the actual contents.
  2309. const CEconItemSchema::RevolvingLootListDefinitionMap_t& mapRevolvingLootLists = GetItemSchema()->GetRevolvingLootLists();
  2310. int idx = mapRevolvingLootLists.Find( iCrateSeries );
  2311. if ( !mapRevolvingLootLists.IsValidIndex( idx ) )
  2312. return false;
  2313. const char *pszLootList = mapRevolvingLootLists.Element( idx );
  2314. // Get the loot list.
  2315. m_pLootList = GetItemSchema()->GetLootListByName( pszLootList );
  2316. m_unAuditDetailData = iCrateSeries;
  2317. return m_pLootList != NULL;
  2318. }
  2319. //-----------------------------------------------------------------------------
  2320. // Purpose:
  2321. //-----------------------------------------------------------------------------
  2322. bool CCrateLootListWrapper::BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem )
  2323. {
  2324. Assert( m_pLootList == NULL );
  2325. // Find out what series this crate belongs to.
  2326. static CSchemaAttributeDefHandle pAttr_LootListName( "loot list name" );
  2327. if ( !pAttr_LootListName )
  2328. return false;
  2329. CAttribute_String str;
  2330. if ( !pEconItem->FindAttribute( pAttr_LootListName, &str ) )
  2331. return false;
  2332. m_pLootList = GetItemSchema()->GetLootListByName( str.value().c_str() );
  2333. return m_pLootList != NULL;
  2334. }
  2335. //-----------------------------------------------------------------------------
  2336. // Purpose:
  2337. //-----------------------------------------------------------------------------
  2338. bool CCrateLootListWrapper::BAttemptLineItemInitialization( const IEconItemInterface *pEconItem )
  2339. {
  2340. Assert( m_pLootList == NULL );
  2341. // Do we have at least one line item specified?
  2342. if ( !pEconItem->FindAttribute( CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[0] ) )
  2343. return false;
  2344. m_pLootList = new CAttributeLineItemLootList( pEconItem );
  2345. m_bIsDynamicallyAllocatedLootList = true;
  2346. return true;
  2347. }